π¬
Neue Antwort!
Der Entwickler hat auf dein Feedback geantwortet
SchlieΓen
Antworten senden π¬
b.classList.toggle('on',(i===0&&t==='login')||(i===1&&t==='reg')));
aerr('');
}
function aerr(msg){const e=g('aerr');e.textContent=msg;e.style.display=msg?'block':'none';}
async function doRegister(){
var uname=val('r-user'),pw=val('r-pw');
var email=g('r-email')?g('r-email').value.trim():'';
if(!uname||!email||!pw)return aerr('Alle Felder ausfΓΌllen.');
if(pw.length<8)return aerr('Passwort mind. 8 Zeichen.');
dis('r-btn',true);
var ux=await sb.from('profiles').select('id').eq('username',uname).maybeSingle();
if(ux.data){dis('r-btn',false);return aerr('Name bereits vergeben.');}
var sr=await sb.auth.signUp({email:email,password:pw,options:{data:{username:uname,role:'tester'}}});
dis('r-btn',false);
if(sr.error)return aerr(sr.error.message);
toast('Registriert! Bitte anmelden.','ok');
authTab('login');
}
async function doLogin(){
var uname=val('l-user'),pw=val('l-pw');
if(!uname||!pw)return aerr('Bitte alle Felder ausfΓΌllen.');
dis('l-btn',true);
if(uname.toLowerCase()==='admin'){
adminLoggingIn=true;
var ar=await sb.auth.signInWithPassword({email:ADMIN_INTERNAL_EMAIL,password:pw});
dis('l-btn',false);
if(ar.error){adminLoggingIn=false;return aerr('Passwort falsch.');}
return;
}
var prof=await sb.from('profiles').select('email').eq('username',uname).maybeSingle();
if(!prof.data||!prof.data.email){dis('l-btn',false);return aerr('Name nicht gefunden.');}
var lr=await sb.auth.signInWithPassword({email:prof.data.email,password:pw});
dis('l-btn',false);
if(lr.error)return aerr('Name oder Passwort falsch.');
}
async function doLogout(){
if((curProfile&&curProfile.role)==='admin'){
localStorage.removeItem('admin_device_verified');
localStorage.removeItem('admin_name');
localStorage.removeItem('admin_verified_version');
} else {
await sb.auth.signOut();
}
curUser=curProfile=null;todayEntries=[];
showScreen('auth');
}
// ββ ADMIN LOGIN ββββββββββββββββββββββββββββ
function initAdminScreen(){
if(g('al-step1'))g('al-step1').classList.remove('hidden');
if(g('al-step2'))g('al-step2').classList.add('hidden');
if(g('al-code'))g('al-code').value='';
showAlErr('');
}
function showAlErr(msg){var e=g('al-err');if(e){e.textContent=msg;e.style.display=msg?'block':'none';}}
function doAdminCheckCode(){
var code=g('al-code').value.trim().toUpperCase().replace(/\s/g,'');
if(!code){showAlErr('Bitte Code eingeben.');return;}
if(code!==ADMIN_CODE_LOCAL){showAlErr('UngΓΌltiger Admin-Code.');return;}
showAlErr('');
g('al-step1').classList.add('hidden');
g('al-step2').classList.remove('hidden');
if(g('al-username'))g('al-username').focus();
}
async function doAdminLogin(){
var username=g('al-username').value.trim();
if(!username){showAlErr('Bitte Name eingeben.');return;}
dis('al-btn-login',true);showAlErr('');
var res=await sb.auth.signInWithPassword({email:ADMIN_INTERNAL_EMAIL,password:ADMIN_INTERNAL_PW});
if(res.error){
// Versuche Account zu erstellen
var res2=await sb.auth.signUp({email:ADMIN_INTERNAL_EMAIL,password:ADMIN_INTERNAL_PW,options:{data:{username:username,role:'admin'}}});
if(res2.error){
dis('al-btn-login',false);
showAlErr('Fehler: Admin-Konto konnte nicht erstellt werden. Bitte prΓΌfe Supabase.');
return;
}
// Nochmal einloggen nach Erstellung
var res3=await sb.auth.signInWithPassword({email:ADMIN_INTERNAL_EMAIL,password:ADMIN_INTERNAL_PW});
if(res3.error){dis('al-btn-login',false);showAlErr('Fehler beim Einloggen.');return;}
}
// Anzeigename setzen
if(res.data&&res.data.user){
await sb.from('profiles').upsert({id:res.data.user.id,username:username,role:'admin',channel:'admin'});
}
dis('al-btn-login',false);
}
function doAdminFirstLogin(){doAdminCheckCode();}
async function doAdminReturnLogin(){doAdminLogin();}
async function adminSignIn(name){return true;}
function adminForgetDevice(){}
function applyProfile(){
if(!curProfile)return;
if(g('party-quick-btn'))g('party-quick-btn').style.display='block';
const b=g('ubadge');
if(b){
b.textContent='βοΈ Einstellungen';
b.className='ubadge'+(curProfile.role==='admin'?' adm':'');
}
if(g('hallo-name'))g('hallo-name').textContent=curProfile.username;
if(g('n-admin'))g('n-admin').classList.toggle('hidden',curProfile.role!=='admin');
const fb=g('feedback-card');
if(fb)fb.style.display=curProfile.role==='admin'?'none':'';
// Show changelog on update
const lastSeen=localStorage.getItem('schlueckchen_last_version');
if(lastSeen && lastSeen!==APP_VERSION)setTimeout(()=>openChangelog(),800);
localStorage.setItem('schlueckchen_last_version',APP_VERSION);
}
// ββ BOOT βββββββββββββββββββββββββββββββββββ
async function bootApp(user){
if(curUser&&curUser.id===user.id)return;
curUser=user;
var isAdmin=user.email===ADMIN_INTERNAL_EMAIL;
var res=await sb.from('profiles').select('*').eq('id',user.id).single();
if(!res.data){
var uname=user.user_metadata&&user.user_metadata.username||(isAdmin?'Admin':'Nutzer');
var role=isAdmin?'admin':(user.user_metadata&&user.user_metadata.role||'tester');
await sb.from('profiles').upsert({id:user.id,username:uname,role:role,channel:isAdmin?'admin':'stable'});
curProfile={id:user.id,username:uname,role:role,channel:isAdmin?'admin':'stable'};
} else {
curProfile=res.data;
if(isAdmin&&curProfile.role!=='admin'){
curProfile.role='admin';
await sb.from('profiles').update({role:'admin',channel:'admin'}).eq('id',user.id);
}
}
applyProfile();
await loadToday();
await loadHistory();
if(curProfile.role!=='admin')await loadMyFeedback();
setTimeout(function(){checkForNewReplies();},1500);
showScreen('app');
}
// ββ DATA βββββββββββββββββββββββββββββββββββ
function todayKey(){return new Date().toISOString().slice(0,10);}
function clearDay(){
if(!todayEntries.length){toast('Keine EintrΓ€ge vorhanden');return;}
// Custom confirm dialog (confirm() blocked in some mobile browsers)
const ov=document.createElement('div');
ov.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:9999;display:flex;align-items:center;justify-content:center;padding:24px';
ov.innerHTML='
'
+'
ποΈ
'
+'
LΓΆschen?
'
+'
Alle heutigen EintrΓ€ge werden gelΓΆscht.
'
+'
'
+'Abbrechen '
+'LΓΆschen '
+'
';
document.body.appendChild(ov);
ov.querySelector('#cn').onclick=()=>ov.remove();
ov.querySelector('#cy').onclick=async()=>{
ov.remove();
const ids=todayEntries.map(e=>e.id);
for(const id of ids) await sb.from('feeding_entries').delete().eq('id',id);
todayEntries=[];
renderSum();renderLog();toast('ποΈ Alle gelΓΆscht');
};
}
// ββ RENDER βββββββββββββββββββββββββββββββββ
function renderDate(){
const d=new Date();
g('today-str').textContent=DAYS[d.getDay()]+', '+d.getDate()+'. '+MO[d.getMonth()]+' '+d.getFullYear();
}
function renderSum(){
const milk=todayEntries.filter(e=>e.type==='milk').reduce((s,e)=>s+Number(e.amount),0);
const brei=todayEntries.filter(e=>e.type==='brei').reduce((s,e)=>s+Number(e.amount),0);
g('s-milk').textContent=milk;g('s-brei').textContent=brei;g('s-cnt').textContent=todayEntries.length;
}
function renderLog(){
var empty=g('logempty'),list=g('loglist');
if(!todayEntries.length){empty.classList.remove('hidden');list.classList.add('hidden');return;}
empty.classList.add('hidden');list.classList.remove('hidden');
var sp={small:'Klein',medium:'Mittel',large:'GroΓ'};
while(list.firstChild)list.removeChild(list.firstChild);
todayEntries.forEach(function(e){
var typeLabel=e.type==='medi'?'Medikament':e.type==='milk'?'Milch':'Brei';
var dotType=e.type==='medi'?'milk':e.type;
var icon=e.type==='milk'?'πΌ':e.type==='medi'?'π':'π₯£';
var timeStr=(e.entry_time||'').slice(0,5);
var row=document.createElement('div');
row.className='litem';
var dot=document.createElement('div');
dot.className='ldot '+dotType;
dot.textContent=icon;
row.appendChild(dot);
var meta=document.createElement('div');
meta.className='lmeta';
var ltype=document.createElement('div');
ltype.className='ltype';
var byStr=(e.added_by&&e.added_by!==curUser.id)?' Β· von Gast':'';
ltype.textContent=typeLabel+(e.spoon_size?' Β· '+sp[e.spoon_size]+' LΓΆffel':'')+byStr;
meta.appendChild(ltype);
var amtRow=document.createElement('div');
amtRow.style.display='flex';
amtRow.style.alignItems='baseline';
amtRow.style.flexWrap='wrap';
amtRow.style.gap='4px';
var amt=document.createElement('span');
amt.className='lamount '+e.type;
amt.textContent=e.amount;
amtRow.appendChild(amt);
var unit=document.createElement('span');
unit.className='lunit';
unit.textContent=' '+e.unit;
amtRow.appendChild(unit);
if(e.note&&e.type!=='medi'){
// Split note by ' Β· ' to show note and medi separately
var parts=e.note.split(' Β· π ');
var noteText=parts[0];
var mediText=parts[1]?parts.slice(1).join(' Β· '):null;
if(noteText&&!noteText.startsWith('π')){
var noteSpan=document.createElement('span');
noteSpan.style.cssText='font-size:11px;color:var(--text-l);font-weight:700';
noteSpan.textContent='Β· π '+noteText;
amtRow.appendChild(noteSpan);
}
if(mediText){
var mediSpan=document.createElement('span');
mediSpan.style.cssText='font-size:11px;color:var(--purple);font-weight:700';
mediSpan.textContent='Β· π '+mediText;
amtRow.appendChild(mediSpan);
} else if(noteText&¬eText.startsWith('π')){
var mediSpan2=document.createElement('span');
mediSpan2.style.cssText='font-size:11px;color:var(--purple);font-weight:700';
mediSpan2.textContent='Β· '+noteText;
amtRow.appendChild(mediSpan2);
}
}
meta.appendChild(amtRow);
row.appendChild(meta);
var time=document.createElement('div');
time.className='ltime';
time.textContent=timeStr+' Uhr';
row.appendChild(time);
var btn=document.createElement('button');
btn.className='ldel';
btn.textContent='β';
(function(id){btn.addEventListener('click',function(){delEntry(id);});})(e.id);
row.appendChild(btn);
list.appendChild(row);
});
}
function renderHist(history){
const card=g('histcard');
if(!(history&&history.length)){card.classList.add('hidden');return;}
card.classList.remove('hidden');
g('histlist').innerHTML=history.map(h=>{
const[,m,d]=h.summary_date.split('-');
return`
${parseInt(d)}. ${MO[parseInt(m)-1]}
πΌ ${h.milk_total_ml}ml π₯£ ${h.brei_total_g}g βΊ
`;
}).join('');
}
function renderMyFeedback(data){
const list=g('my-feedback-list');
if(!(data&&data.length)){list.innerHTML='';return;}
list.innerHTML=data.map(f=>`
${{general:'π¬',bug:'π',feature:'β¨',praise:'π'}[f.category]||'π¬'} ${f.category}
${new Date(f.created_at).toLocaleDateString('de')}
${f.status==='resolved'?'β Beantwortet ':''}
${f.message}
${(f.feedback_replies&&f.feedback_replies.length)?`
${f.feedback_replies.map(r=>`
π ${r.message}
`).join('')}
`:''}
`).join('');
}
function renderAmtGrid(){
var p=selType==='milk'?MILK_P:BREI_P;
var u=selType==='milk'?'ml':'g';
var html='';
for(var i=0;i
'+v+''+u+'
';
}
g('agrid').innerHTML=html;
}
// ββ CONTROLS βββββββββββββββββββββββββββββββ
function pickType(t){
try{
var _gstored=localStorage.getItem('schlk_guest_party');
if(_gstored){
var _gperm='eingeschraenkt';
try{_gperm=JSON.parse(_gstored).perm||'eingeschraenkt';}catch(e){}
if(t==='medi')return;
if(t==='brei'&&_gperm==='eingeschraenkt')return;
}
selType=t;selAmt=null;selSpoon=null;selMedi=null;
if(g('tb-milk'))g('tb-milk').className='tbtn milk'+(t==='milk'?' on':'');
if(g('tb-brei'))g('tb-brei').className='tbtn brei'+(t==='brei'?' on':'');
if(g('tb-medi'))g('tb-medi').className='tbtn'+(t==='medi'?' on':'');
if(g('spoonrow'))g('spoonrow').classList.toggle('show',t==='brei');
if(g('agrid'))g('agrid').style.display=t==='medi'?'none':'grid';
if(g('medi-section'))g('medi-section').style.display=t==='medi'?'block':'none';
var freerow=document.querySelector('.freerow');if(freerow)freerow.style.display=t==='medi'?'none':'flex';
if(t==='medi'){
if(g('addbtn')){g('addbtn').className='addbtn';g('addbtn').style.background='linear-gradient(135deg,var(--purple),#6A4FB0)';g('addbtn').textContent='π Medikament eintragen';}
renderMediQuick();
} else {
if(g('addbtn')){g('addbtn').style.background='';g('addbtn').className='addbtn '+t;g('addbtn').textContent=t==='milk'?'πΌ Milch eintragen':'π₯£ Brei eintragen';}
}
if(t!=='medi'&&g('ubadge2')){g('ubadge2').textContent=t==='milk'?'ml':'g';g('ubadge2').className='ubadge2 '+t;}
if(g('cval'))g('cval').value='';
clearSpoonSel();
renderAmtGrid();
}catch(ex){toast('β pickType FEHLER: '+ex.message,'err');}
}
function pickAmt(v){
selAmt=Number(v);
selSpoon=null;
clearSpoonSel();
if(g('cval'))g('cval').value='';
renderAmtGrid();
}
function pickSpoon(size,gr){
selSpoon={size,gr};selAmt=gr;
['s','m','l'].forEach(x=>g('sp-'+x).className='spbtn');
g('sp-'+size[0]).className='spbtn on';renderAmtGrid();
}
function clearSpoonSel(){['s','m','l'].forEach(x=>g('sp-'+x).className='spbtn');}
function onCustom(){const v=parseInt(g('cval').value);if(v>0){selAmt=v;selSpoon=null;clearSpoonSel();renderAmtGrid();}else selAmt=null;}
function setNow(){const n=new Date();if(!g('etime'))return;g('etime').value=String(n.getHours()).padStart(2,'0')+':'+String(n.getMinutes()).padStart(2,'0');}
function toggleHist(){const l=g('histlist'),t=g('histtog'),h=l.classList.contains('hidden');l.classList.toggle('hidden',!h);t.classList.toggle('open',h);}
// ββ POPUP ββββββββββββββββββββββββββββββββββ
function openAddPopup(){
setNow();
selAmt=null;selSpoon=null;selMedi=null;
pickType(selType);
// Show quick-select buttons for saved medis
var mediList=getMediList();
var quick=g('emedi-quick');
if(quick){
quick.innerHTML='';
mediList.forEach(function(m){
var btn=document.createElement('button');
btn.style.cssText='border:2px solid #EEE;border-radius:20px;padding:4px 10px;font-size:11px;font-weight:800;cursor:pointer;background:var(--cream);color:var(--text-l);transition:all .2s;font-family:Nunito,sans-serif';
btn.textContent='π '+m.name+(m.dose?' '+m.dose+' '+m.unit:'');
(function(med){
btn.onclick=function(){
if(g('emedi'))g('emedi').value=med.name;
if(g('emedi-dose'))g('emedi-dose').value=med.dose||'';
if(g('emedi-unit'))g('emedi-unit').value=med.unit||'Tropfen';
currentMedi=med.name;currentMediDose=med.dose||'';currentMediUnit=med.unit||'Tropfen';
quick.querySelectorAll('button').forEach(function(b){b.style.background='var(--cream)';b.style.borderColor='#EEE';b.style.color='var(--text-l)';});
btn.style.background='var(--lav)';btn.style.borderColor='var(--purple)';btn.style.color='var(--purple)';
};
})(m);
quick.appendChild(btn);
});
}
currentMedi='';currentMediDose='';currentMediUnit='Tropfen';
if(g('emedi'))g('emedi').value='';
if(g('emedi-dose'))g('emedi-dose').value='';
if(g('emedi-unit'))g('emedi-unit').value='Tropfen';
g('add-popup').classList.remove('hidden');
document.body.style.overflow='hidden';
if(guestPartyOwnerId){
if(g('tb-medi'))g('tb-medi').style.display='none';
if(g('medi-section'))g('medi-section').style.display='none';
var stored=localStorage.getItem('schlk_guest_party');
if(stored){
try{var gp=JSON.parse(stored);if(gp.perm==='eingeschraenkt'){if(g('tb-brei'))g('tb-brei').style.display='none';if(g('enote'))g('enote').style.display='none';if(g('spoonrow'))g('spoonrow').style.display='none';}}catch(e){}
}
}
}
function closeAddPopup(){g('add-popup').classList.add('hidden');document.body.style.overflow='';currentNote='';currentMedi='';currentMediDose='';currentMediUnit='Tropfen';if(g('enote'))g('enote').value='';if(g('emedi'))g('emedi').value='';if(g('emedi-dose'))g('emedi-dose').value='';if(g('emedi-unit'))g('emedi-unit').value='Tropfen';}
function closeAddPopupOutside(e){if(e.target===g('add-popup'))closeAddPopup();}
// ββ DAY MODAL ββββββββββββββββββββββββββββββ
async function openDayModal(date,label,milk,brei){
g('modal-date-title').textContent=label;
g('modal-milk').textContent=milk+'ml';
g('modal-brei').textContent=brei+'g';
g('day-modal').classList.remove('hidden');
document.body.style.overflow='hidden';
g('modal-entries').innerHTML='Ladeβ¦
';
const sp={small:'Klein',medium:'Mittel',large:'GroΓ'};
let entries=[];
{
const{data}=await sb.from('feeding_entries').select('*').eq('user_id',curUser.id).eq('entry_date',date).order('entry_time',{ascending:true});
entries=data||[];
}
if(!entries.length){g('modal-entries').innerHTML='Keine EintrΓ€ge
';return;}
g('modal-entries').innerHTML=entries.map(e=>`
${e.type==='milk'?'πΌ':'π₯£'}
${e.type==='milk'?'Milch':'Brei'}${e.spoon_size?' Β· '+sp[e.spoon_size]+' LΓΆffel':''}
${e.amount} ${e.unit}
${(e.entry_time||'').slice(0,5)} Uhr
`).join('');
}
function closeDayModal(event){
if(event&&event.target!==g('day-modal'))return;
g('day-modal').classList.add('hidden');document.body.style.overflow='';
}
// ββ FEEDBACK βββββββββββββββββββββββββββββββ
function pickCat(c){selCat=c;['general','bug','feature','praise'].forEach(x=>g('cat-'+x).classList.toggle('on',x===c));}
// ββ STATS ββββββββββββββββββββββββββββββββββ
function setPeriod(p){
statPeriod=p;['week','month','all'].forEach(x=>g('pt-'+x).classList.toggle('on',x===p));renderStats();
}
function renderStats(){
let data=[...allStats];
const today=new Date();
if(statPeriod==='week'){const c=new Date(today);c.setDate(today.getDate()-7);data=data.filter(d=>new Date(d.summary_date)>=c);}
else if(statPeriod==='month'){const c=new Date(today);c.setDate(today.getDate()-30);data=data.filter(d=>new Date(d.summary_date)>=c);}
if(!data.length){
g('avg-milk').textContent='β';g('avg-brei').textContent='β';g('avg-days').textContent='0';
g('chart-bars').innerHTML='Noch keine Daten
';
g('stat-detail-list').innerHTML='Noch keine Daten
';
return;
}
const avgMilk=Math.round(data.reduce((s,d)=>s+Number(d.milk_total_ml),0)/data.length);
const avgBrei=Math.round(data.reduce((s,d)=>s+Number(d.brei_total_g),0)/data.length);
g('avg-milk').textContent=avgMilk+'ml';g('avg-brei').textContent=avgBrei+'g';g('avg-days').textContent=data.length;
const chartData=[...data].reverse().slice(-14);
const maxVal=Math.max(...chartData.map(d=>Math.max(Number(d.milk_total_ml),Number(d.brei_total_g))),1);
g('chart-bars').innerHTML=chartData.map(d=>{
const mh=Math.max(Math.round(Number(d.milk_total_ml)/maxVal*100),d.milk_total_ml>0?4:0);
const bh=Math.max(Math.round(Number(d.brei_total_g)/maxVal*100),d.brei_total_g>0?4:0);
const[,m,day]=d.summary_date.split('-');
return`${mh>0?`
`:''} ${bh>0?`
`:''}
${parseInt(day)}.${MO[parseInt(m)-1].slice(0,3)}
`;
}).join('');
g('stat-detail-list').innerHTML=[...data].map(d=>{
const[,m,day]=d.summary_date.split('-');
return`
${parseInt(day)}. ${MO[parseInt(m)-1]}
πΌ ${d.milk_total_ml}ml π₯£ ${d.brei_total_g}g βΊ
`;
}).join('');
}
// ββ ADMIN ββββββββββββββββββββββββββββββββββ
// ββ SETTINGS βββββββββββββββββββββββββββββββ
function initSettings(){
if(curProfile)g('set-username-display').textContent=curProfile.username;
g('set-version').textContent=APP_VERSION;loadNahrung();loadMedi();
}
async function changeUsername(){
const n=g('set-new-username').value.trim();if(!n)return toast('Bitte Namen eingeben.');
const{error:une}=await sb.from('profiles').update({username:n}).eq('id',curUser.id);if(une)return toast('Fehler','err');
curProfile.username=n;g('set-username-display').textContent=n;
g('ubadge').textContent=curProfile.role==='admin'?n+' Β· '+APP_VERSION:'π€ '+n;
g('set-new-username').value='';toast('β
Name geΓ€ndert!','ok');
}
async function changePassword(){
const pw=g('set-new-pw').value;if(!pw||pw.length<8)return toast('Mind. 8 Zeichen!');
const{error:pwe}=await sb.auth.updateUser({password:pw});if(pwe)return toast('Fehler','err');
g('set-new-pw').value='';toast('β
Passwort geΓ€ndert!','ok');
}
// CHANGELOG
function openChangelog(){ g('changelog-overlay').classList.remove('hidden'); document.body.style.overflow='hidden'; }
function closeChangelog(e){ if(e&&e.target!==g('changelog-overlay'))return; g('changelog-overlay').classList.add('hidden'); document.body.style.overflow=''; }
// MEDIKAMENTE
function getMediList(){return JSON.parse(localStorage.getItem('schlueckchen_medis')||'[]');}
function saveMediList(l){localStorage.setItem('schlueckchen_medis',JSON.stringify(l));}
function loadMedi(){
const list=getMediList(),c=g('medi-list');
if(!list.length){c.innerHTML='Noch keine Medikamente hinzugefΓΌgt
';return;}
c.innerHTML=list.map((m,i)=>`π
${m.name}
${m.dose} ${m.unit}
β `).join('');
}
function addMedi(){
const name=g('medi-name').value.trim(),dose=g('medi-dose').value.trim(),unit=g('medi-unit').value;
if(!name)return toast('Bitte Namen eingeben.');
const l=getMediList();l.push({name,dose,unit});saveMediList(l);
g('medi-name').value='';g('medi-dose').value='';
loadMedi();renderMediQuick();toast('β
HinzugefΓΌgt!','ok');
}
function delMedi(i){const l=getMediList();l.splice(i,1);saveMediList(l);loadMedi();renderMediQuick();}
function renderMediQuick(){
const list=getMediList(),c=g('medi-quick-list');
if(!c)return;
if(!list.length){c.innerHTML='Keine Medikamente gespeichert β zuerst in Einstellungen hinzufΓΌgen
';return;}
c.innerHTML=list.map((m,i)=>`${m.name}${m.dose} ${m.unit} `).join('');
}
function selectMediQuick(i){
selMedi=getMediList()[i];
g('medi-quick-list').querySelectorAll('button').forEach((b,j)=>{
b.style.background=j===i?'var(--lav)':'var(--cream)';
b.style.borderColor=j===i?'var(--purple)':'#EEE';
b.style.color=j===i?'var(--purple)':'var(--text-l)';
});
g('medi-free-name').value=selMedi.name;
g('medi-free-dose').value=selMedi.dose;
g('medi-free-unit').value=selMedi.unit;
}
function getNahrungList(){return JSON.parse(localStorage.getItem('schlueckchen_nahrung')||'[]');}
function saveNahrungList(l){localStorage.setItem('schlueckchen_nahrung',JSON.stringify(l));}
function loadNahrung(){
const list=getNahrungList(),c=g('nahrung-list');
if(!list.length){c.innerHTML='Noch keine Nahrung hinzugefΓΌgt
';return;}
c.innerHTML=list.map((n,i)=>`${n.type==='milk'?'πΌ':'π₯£'}
${n.name}
${n.grams}g Packung Β· ${n.type==='milk'?'Milch':'Brei'}
β `).join('');
}
function addNahrung(){
const name=g('nahrung-name').value.trim(),type=g('nahrung-type').value,grams=parseInt(g('nahrung-grams').value);
if(!name)return toast('Bitte Namen eingeben.');if(!grams||grams<=0)return toast('Bitte PackungsgrΓΆΓe eingeben.');
const l=getNahrungList();l.push({name,type,grams});saveNahrungList(l);
g('nahrung-name').value='';g('nahrung-grams').value='';loadNahrung();toast('β
HinzugefΓΌgt!','ok');
}
function delNahrung(i){const l=getNahrungList();l.splice(i,1);saveNahrungList(l);loadNahrung();}
function showDirectPwSet(){
g('pw-set-overlay').classList.remove('hidden');
document.body.style.overflow='hidden';
}
function closePwSet(e){
if(e&&e.target!==g('pw-set-overlay'))return;
g('pw-set-overlay').classList.add('hidden');
document.body.style.overflow='';
}
async function doDirectPwSet(){
const email=g('pw-set-email').value.trim();
const current=g('pw-set-current').value;
const newPw=g('pw-set-new').value;
const confirm=g('pw-set-confirm').value;
const errEl=g('pw-set-err');
errEl.style.display='none';
if(!email||!current||!newPw||!confirm){errEl.textContent='Alle Felder ausfΓΌllen.';errEl.style.display='block';return;}
if(newPw.length<8){errEl.textContent='Mind. 8 Zeichen.';errEl.style.display='block';return;}
if(newPw!==confirm){errEl.textContent='PasswΓΆrter stimmen nicht ΓΌberein.';errEl.style.display='block';return;}
g('pw-set-btn').disabled=true;
const{error:le}=await sb.auth.signInWithPassword({email,password:current});
if(le){g('pw-set-btn').disabled=false;errEl.textContent='Aktuelles Passwort falsch.';errEl.style.display='block';return;}
const{error:ue}=await sb.auth.updateUser({password:newPw});
g('pw-set-btn').disabled=false;
if(ue){errEl.textContent='Fehler: '+ue.message;errEl.style.display='block';return;}
closePwSet();toast('Passwort geΓ€ndert!','ok');
}
function showDeleteAccount(){
const ov=document.createElement('div');
ov.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:9999;display:flex;align-items:center;justify-content:center;padding:24px';
ov.innerHTML='β οΈ
Account lΓΆschen?
Alle Daten werden permanent gelΓΆscht.
Abbrechen LΓΆschen
';
document.body.appendChild(ov);
ov.querySelector('#del-no').onclick=()=>ov.remove();
ov.querySelector('#del-yes').onclick=async()=>{ov.remove();await deleteAccount();};
}
async function deleteAccount(){
if(!curUser)return;
try{
await sb.from('feeding_entries').delete().eq('user_id',curUser.id);
await sb.from('daily_summaries').delete().eq('user_id',curUser.id);
await sb.from('feedback').delete().eq('user_id',curUser.id);
await sb.from('profiles').delete().eq('id',curUser.id);
await sb.auth.signOut();
curUser=curProfile=null;todayEntries=[];
showScreen('auth');toast('Account gelΓΆscht.','ok');
}catch(e){toast('Fehler: '+e.message,'err');}
}
// ββ PULL TO REFRESH (Admin) ββββββββββββββββ
// ββ SCREENS ββββββββββββββββββββββββββββββββ
function showScreen(name){
var scr=['auth','app','admin','stats','adminlogin','settings'];
for(var _i=0;_iel.classList.remove('show'),2800);}
function applyUpdate(){window.location.reload();}
// ββ BOOT βββββββββββββββββββββββββββββββββββ
// PRODUCTION DB FUNCTIONS
async function loadToday(){
if(!curUser)return;
var res=await sb.from('feeding_entries').select('*').eq('user_id',curUser.id).eq('entry_date',todayKey()).order('entry_time',{ascending:false});
if(res.error){console.error('loadToday:',res.error.message);return;}
todayEntries=res.data||[];renderSum();renderLog();
}
async function loadHistory(){
if(!curUser)return;
var res=await sb.from('daily_summaries').select('*').eq('user_id',curUser.id).neq('summary_date',todayKey()).order('summary_date',{ascending:false}).limit(30);
renderHist(res.data||[]);
}
async function loadMyFeedback(){
if(!curUser)return;
var res=await sb.from('feedback').select('*,feedback_replies(id,message,created_at)').eq('user_id',curUser.id).order('created_at',{ascending:false}).limit(10);
renderMyFeedback(res.data||[]);
}
async function loadStats(){
if(!curUser)return;
var res=await sb.from('daily_summaries').select('*').eq('user_id',curUser.id).order('summary_date',{ascending:false}).limit(90);
allStats=res.data||[];renderStats();
}
async function loadAdminData(){
var btn=g('admin-refresh-btn');
if(btn){btn.textContent='β³';btn.disabled=true;}
try{
var ur=await sb.from('profiles').select('*').order('created_at',{ascending:false});
var er=await sb.from('feeding_entries').select('id',{count:'exact',head:true});
var fr=await sb.from('feedback').select('id,username,category,message,status,created_at,feedback_replies(id,message,created_at)').order('created_at',{ascending:false});
if(ur.error)console.error('profiles:',ur.error.message);
if(fr.error)console.error('feedback:',fr.error.message);
var fbData=fr.data||[];
if(g('a-users'))g('a-users').textContent=(ur.data||[]).length;
if(g('a-entries'))g('a-entries').textContent=er.count||0;
if(g('a-feedback-new'))g('a-feedback-new').textContent=fbData.filter(function(f){return f.status==='new';}).length;
if(g('a-feedback-total'))g('a-feedback-total').textContent=fbData.length;
var catIcon={general:'π¬',bug:'π',feature:'β¨',praise:'π'};
if(g('a-fblist')){
if(!fbData.length){g('a-fblist').innerHTML='Kein Feedback vorhanden
';}
else{
var html='';
fbData.forEach(function(f){
html+='';
html+='
@'+(f.username||'?')+' ';
html+=''+(catIcon[f.category]||'π¬')+' '+f.category+' ';
html+=''+f.status+'
';
html+='
'+f.message+'
';
if(f.feedback_replies&&f.feedback_replies.length){
html+='
';
f.feedback_replies.forEach(function(r){html+='
π '+r.message+'
';});
html+='
';
}
html+='
';
html+=' ';
html+='Senden ';
html+='ποΈ ';
html+='
';
});
g('a-fblist').innerHTML=html;
}
}
if(g('a-ulist')){
var uhtml='Name Rolle Seit ';
(ur.data||[]).forEach(function(u){uhtml+=''+(u.username||'?')+' '+u.role+' '+new Date(u.created_at).toLocaleDateString('de')+' ';});
uhtml+='
';
g('a-ulist').innerHTML=uhtml;
}
}catch(err){console.error('loadAdminData:',err);toast('Fehler beim Laden','err');}
if(btn){btn.textContent='π';btn.disabled=false;}
}
function _ar(el){adminReply(el.dataset.fid);}
function _ad(el){adminDelFb(el.dataset.fid);}
function addEntry(){
if(!curUser){toast('β Nicht eingeloggt!','err');return;}
var etimeEl=g('etime');
var time=etimeEl?etimeEl.value:'';
if(!time){
var n=new Date();
time=String(n.getHours()).padStart(2,'0')+':'+String(n.getMinutes()).padStart(2,'0');
}
var note=(currentNote||'').trim()||(g('enote')?g('enote').value.trim():'');
var medi=(currentMedi||'').trim()||(g('emedi')?g('emedi').value.trim():'');
var mediDose=currentMediDose||(g('emedi-dose')?g('emedi-dose').value:'');
var mediUnit=currentMediUnit||'Tropfen';
var mediLabel=medi?(medi+(mediDose?' Β· '+mediDose+' '+mediUnit:'')):null;
var fullNote=[note,mediLabel?'π '+mediLabel:null].filter(Boolean).join(' Β· ')||null;
if(selType==='medi'){
var mname=(g('medi-free-name')?g('medi-free-name').value.trim():'')||(selMedi?selMedi.name:'');
var mdose=(g('medi-free-dose')?g('medi-free-dose').value.trim():'')||'1';
var munit=(g('medi-free-unit')?g('medi-free-unit').value:'')||'Tropfen';
if(!mname)return toast('β οΈ Bitte Medikament wΓ€hlen!');
var tId=guestPartyOwnerId||curUser.id;
sb.from('feeding_entries').insert({user_id:tId,added_by:curUser.id,type:'medi',amount:parseFloat(mdose)||1,unit:munit,entry_time:time+':00',entry_date:todayKey(),note:mname+(note?' Β· '+note:'')}).then(function(res){
if(res.error)return toast('β '+res.error.message,'err');
loadToday();closeAddPopup();toast('π '+mname+' eingetragen!','ok');
});
return;
}
var amt=selAmt||(g('cval')&&g('cval').value?Number(g('cval').value):0);
if(!amt||amt<=0)return toast('β οΈ Bitte Menge wΓ€hlen!');
var tId=guestPartyOwnerId||curUser.id;
sb.from('feeding_entries').insert({user_id:tId,added_by:curUser.id,type:selType,amount:amt,unit:selType==='milk'?'ml':'g',entry_time:time+':00',entry_date:todayKey(),spoon_size:selSpoon?selSpoon.size:null,note:fullNote}).then(function(res){
if(res.error)return toast('β '+res.error.message,'err');
selAmt=null;selSpoon=null;clearSpoonSel();
if(g('cval'))g('cval').value='';
if(g('enote'))g('enote').value='';
currentNote='';currentMedi='';currentMediDose='';currentMediUnit='Tropfen';
closeAddPopup();
toast('β
'+amt+(selType==='milk'?'ml':'g')+' '+(selType==='milk'?'Milch':'Brei')+' gespeichert!','ok');
loadToday();
});
}
async function delEntry(id){
await sb.from('feeding_entries').delete().eq('id',id);
todayEntries=todayEntries.filter(function(e){return e.id!==id;});
renderSum();renderLog();
}
function sendFeedback(){
var msg=g('fbtext').value.trim();
if(!msg)return toast('Bitte Nachricht eingeben.');
sb.from('feedback').insert({user_id:curUser.id,username:curProfile.username,message:msg,category:selCat,status:'new'}).then(function(res){
if(res.error)return toast('Fehler','err');
g('fbtext').value='';toast('π¬ Danke!','ok');loadMyFeedback();
});
}
async function adminReply(fbId){
var input=g('fri-'+fbId);
if(!input||!input.value.trim())return toast('Bitte Antwort eingeben.');
var msg=input.value.trim();
var res=await sb.from('feedback_replies').insert({feedback_id:fbId,admin_id:curUser.id,message:msg});
if(res.error)return toast('Fehler: '+res.error.message,'err');
await sb.from('feedback').update({status:'resolved'}).eq('id',fbId);
input.value='';toast('β
Antwort gesendet!','ok');loadAdminData();
}
async function adminDelFb(fbId){
var card=g('fbc-'+fbId);if(!card)return;
var ov=document.createElement('div');
ov.style.cssText='position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:9999;display:flex;align-items:center;justify-content:center;padding:24px';
ov.innerHTML='ποΈ
Feedback lΓΆschen?
Nein LΓΆschen
';
document.body.appendChild(ov);
ov.querySelector('#dn').onclick=function(){ov.remove();};
ov.querySelector('#dy').onclick=async function(){ov.remove();await sb.from('feedback').delete().eq('id',fbId);card.remove();toast('ποΈ GelΓΆscht');};
}
async function checkForNewReplies(){
if(!curUser||curProfile.role==='admin')return;
try{
var lastSeen=localStorage.getItem('last_reply_'+curUser.id)||'1970-01-01';
var res=await sb.from('feedback_replies').select('message,created_at,feedback!inner(message,user_id)').eq('feedback.user_id',curUser.id).gt('created_at',lastSeen).order('created_at',{ascending:false}).limit(5);
if(res.data&&res.data.length){
showReplyNotif(res.data.map(function(r){return {original:r.feedback&&r.feedback.message||'Dein Feedback',reply:r.message,date:new Date(r.created_at).toLocaleDateString('de')};}));
}
}catch(e){console.log('Reply check:',e);}
}
setNow();renderDate();renderAmtGrid();
checkPartyUrl();
var cachedV=localStorage.getItem('schlk_cache_v');
if(cachedV&&cachedV!==APP_VERSION){localStorage.setItem('schlk_cache_v',APP_VERSION);window.location.reload(true);}
else{localStorage.setItem('schlk_cache_v',APP_VERSION);}
var adminLoggingIn=false;
var guestPartyOwnerId=null;
sb.auth.getSession().then(function(res){
var s=(res.data&&res.data.session)?res.data.session:null;
if(s&&s.user.email===ADMIN_INTERNAL_EMAIL&&!adminLoggingIn){
sb.auth.signOut();
showScreen('auth');
return;
}
if(s){bootApp(s.user);}else{showScreen('auth');}
});
sb.auth.onAuthStateChange(function(_,session){
if(!session){curUser=null;curProfile=null;showScreen('auth');return;}
bootApp(session.user);
});
var partyPerm='eingeschraenkt';
var partyDur=120;
var activeParty=null;
var partyCodeInterval=null;
var partyMembersInterval=null;
function openPartyQuick(){g('party-quick-overlay').classList.remove('hidden');}
function closePartyQuick(){g('party-quick-overlay').classList.add('hidden');}
function openActiveParty(){if(activeParty){g('party-active-overlay').classList.remove('hidden');}else{openPartyQuick();}}
function openPartyCreate(){g('party-create-overlay').classList.remove('hidden');}
function closePartyCreate(){g('party-create-overlay').classList.add('hidden');}
function openPartyJoin(){g('party-join-overlay').classList.remove('hidden');if(g('party-join-code'))g('party-join-code').value='';if(g('party-join-err'))g('party-join-err').style.display='none';}
function closePartyJoin(){g('party-join-overlay').classList.add('hidden');}
function selectPerm(p){partyPerm=p;var ps=['eingeschraenkt','teilweise','voll'];for(var i=0;i0?pLeft+'min':'';
if(partyExp<=now)endParty();
}
async function renewPartyCode(){
if(!activeParty)return;
var code=genPartyCode();var codeExp=new Date(Date.now()+15*60*1000);
var r=await sb.from('parties').update({code:code,code_expires_at:codeExp.toISOString()}).eq('id',activeParty.id);
if(!r.error){activeParty.code=code;activeParty.code_expires_at=codeExp.toISOString();}
}
async function refreshPartyMembers(){
if(!activeParty)return;
var r=await sb.from('party_members').select('user_id').eq('party_id',activeParty.id);
var el=g('party-members-list');if(!el)return;
if(r.error){el.innerHTML='Fehler: '+r.error.message+'
';return;}
if(!r.data||r.data.length===0){el.innerHTML='Noch niemand beigetreten
';return;}
var html='';
for(var i=0;i'+name+'Raus ';
}
el.innerHTML=html;
}
async function kickMember(userId){
if(!activeParty)return;
await sb.from('party_members').delete().eq('party_id',activeParty.id).eq('user_id',userId);
toast('Gast entfernt','ok');
setTimeout(refreshPartyMembers,500);
}
async function endParty(){
if(!activeParty)return;
await sb.from('parties').update({active:false}).eq('id',activeParty.id);
if(partyCodeInterval)clearInterval(partyCodeInterval);
if(partyMembersInterval)clearInterval(partyMembersInterval);
partyCodeInterval=null;partyMembersInterval=null;
activeParty=null;
g('party-active-overlay').classList.add('hidden');
if(g('party-timer-mini'))g('party-timer-mini').textContent='';
if(g('party-quick-btn'))g('party-quick-btn').style.display='block';
toast('Party beendet','ok');
}
async function joinParty(){
if(!curUser)return;
var code=g('party-join-code').value.trim();
if(!code||code.length!==4){var e=g('party-join-err');if(e){e.textContent='Bitte 4-stelligen Code eingeben.';e.style.display='block';}return;}
var r=await sb.from('parties').select('*').eq('code',code).eq('active',true).single();
if(r.error||!r.data){var e=g('party-join-err');if(e){e.textContent='Code ungueltig.';e.style.display='block';}return;}
var party=r.data;
if(new Date(party.expires_at)<=new Date()){var e=g('party-join-err');if(e){e.textContent='Party abgelaufen.';e.style.display='block';}return;}
var r2=await sb.from('party_members').insert({party_id:party.id,user_id:curUser.id});
if(r2.error&&r2.error.code!=='23505'){var e=g('party-join-err');if(e){e.textContent='Fehler: '+r2.error.message;e.style.display='block';}return;}
guestPartyOwnerId=party.owner_id;
localStorage.setItem('schlk_guest_party',JSON.stringify({id:party.id,perm:party.permission,owner:party.owner_id}));
closePartyJoin();applyGuestMode(party.permission);toast('Party beigetreten!','ok');
}
function applyGuestMode(perm){
var existing=g('guest-banner');if(existing)existing.remove();
var banner=document.createElement('div');
banner.className='guest-banner';banner.id='guest-banner';
banner.textContent='Party aktiv - '+(perm==='eingeschraenkt'?'Nur Milch':perm==='teilweise'?'Milch und Brei':'Alles erlaubt');
var leaveBtn=document.createElement('button');
leaveBtn.textContent='Verlassen';
leaveBtn.ontouchstart=function(){};
leaveBtn.onclick=leaveParty;
leaveBtn.style.cssText='border:none;background:rgba(255,255,255,0.3);color:white;border-radius:8px;padding:3px 10px;font-size:11px;font-weight:800;cursor:pointer;margin-left:8px';
banner.appendChild(leaveBtn);
var app=g('s-app');if(app)app.insertBefore(banner,app.firstChild);
if(perm==='eingeschraenkt'){
if(g('tb-brei'))g('tb-brei').style.display='none';
if(g('tb-medi'))g('tb-medi').style.display='none';
if(g('medi-section'))g('medi-section').style.display='none';
if(g('enote'))g('enote').style.display='none';
if(g('spoonrow'))g('spoonrow').style.display='none';
} else if(perm==='teilweise'){
if(g('tb-medi'))g('tb-medi').style.display='none';
if(g('medi-section'))g('medi-section').style.display='none';
}
if(g('n-stats'))g('n-stats').style.display='none';
if(g('party-quick-btn'))g('party-quick-btn').style.display='none';
}
async function leaveParty(){
var stored=localStorage.getItem('schlk_guest_party');
if(stored){
try{
var p=JSON.parse(stored);
if(p&&p.id&&curUser){await sb.from('party_members').delete().eq('party_id',p.id).eq('user_id',curUser.id);}
}catch(e){}
}
localStorage.removeItem('schlk_guest_party');
guestPartyOwnerId=null;
var banner=g('guest-banner');if(banner)banner.remove();
if(g('tb-brei'))g('tb-brei').style.display='';
if(g('tb-medi'))g('tb-medi').style.display='';
if(g('medi-section'))g('medi-section').style.display='';
if(g('enote'))g('enote').style.display='';
if(g('spoonrow'))g('spoonrow').style.display='';
if(g('n-stats'))g('n-stats').style.display='';
if(g('party-quick-btn'))g('party-quick-btn').style.display='block';
toast('Party verlassen','ok');
}
function checkGuestParty(){
var stored=localStorage.getItem('schlk_guest_party');if(!stored)return;
try{
var p=JSON.parse(stored);
if(p&&p.id){
if(p.owner)guestPartyOwnerId=p.owner;
sb.from('parties').select('active,permission,expires_at,owner_id').eq('id',p.id).single().then(function(r){
if(r.data&&r.data.active&&new Date(r.data.expires_at)>new Date()){
guestPartyOwnerId=r.data.owner_id;applyGuestMode(r.data.permission);
} else {
localStorage.removeItem('schlk_guest_party');guestPartyOwnerId=null;
}
});
}
}catch(e){}
}
function checkPartyUrl(){
var params=new URLSearchParams(window.location.search);
var code=params.get('party');
if(!code)return;
localStorage.setItem('schlk_pending_party',code);
if(curUser&&g('party-join-code')){
g('party-join-code').value=code;
setTimeout(openPartyJoin,500);
}
}
function checkPendingParty(){
var code=localStorage.getItem('schlk_pending_party');if(!code||!curUser)return;
localStorage.removeItem('schlk_pending_party');
if(g('party-join-code')){g('party-join-code').value=code;openPartyJoin();}
}
setInterval(function(){
if(curUser)loadToday();
if(activeParty)refreshPartyMembers();
},15000);schlk_pending_party');
if(g('party-join-code')){g('party-join-code').value=code;openPartyJoin();}
}
setInterval(function(){
if(curUser)loadToday();
if(activeParty)refreshPartyMembers();
},15000);