diff --git a/index.html b/index.html index cf05a8d..f2d42d8 100644 --- a/index.html +++ b/index.html @@ -288,7 +288,7 @@ summary{font-size:12.5px;color:var(--sea2);cursor:pointer;padding:4px 0}
Momentaufnahme deiner Getränkepaket-Abrechnung sichern.
@@ -610,50 +610,91 @@ $("#resetAll").addEventListener("click",()=>{if(confirm("Wirklich alles zurücks try{localStorage.removeItem(KEY);}catch(e){} S={pkg:"fun",priceByPkg:{fun:159,allin:279,kfun:97,kallin:125},persons:2,days:7,curday:1,byday:{},kid:{},priceOv:{},editPrices:false,custom:[]}; $("#editPrices").checked=false;$("#priceResetBar").style.display="none";initForm();renderDrinks();renderKid();compute();}}); -$("#expPdf").addEventListener("click",()=>window.print()); +$("#expPdf").addEventListener("click",exportPdf); $("#expImg").addEventListener("click",exportImage); -/* ---- JPG-Export (Canvas, offline) ---- */ -function exportImage(){ +/* ---- Abrechnung als Canvas rendern (offline) ---- */ +function buildReceipt(){ const L=LAST,W=760,pad=44,dpr=2; + const y0=142, step=40, chartTop=356, ch=120; + const barTop=chartTop+18, labelY=barTop+ch+15, footY=labelY+34, H=footY+18; const cv=document.createElement("canvas"); - const rowsTop=250, chartH=150, foot=70; - const H=rowsTop+chartH+foot+40; cv.width=W*dpr;cv.height=H*dpr;const g=cv.getContext("2d");g.scale(dpr,dpr); - g.fillStyle="#edf2f5";g.fillRect(0,0,W,H); - // header - g.fillStyle="#0b3a54";g.fillRect(0,0,W,92); - g.fillStyle="#e2673d";g.beginPath();g.moveTo(pad,30);g.lineTo(pad+30,30);g.lineTo(pad+15,48);g.lineTo(pad+15,58);g.lineTo(pad+22,58);g.lineTo(pad+22,63);g.lineTo(pad+8,63);g.lineTo(pad+8,58);g.lineTo(pad+15,58);g.closePath();g.fill(); - g.fillStyle="#fff";g.font="700 22px "+FF();g.fillText("Getränkepaket-Abrechnung",pad+46,40); - g.fillStyle="#cfe0ea";g.font="13px "+FF(); - g.fillText(PKGNAME[L.pkg]+" · "+L.persons+"× · Tag "+L.curday+" von "+L.days+" · "+new Date().toLocaleDateString("de-DE"),pad+46,64); - // rows - let y=132;g.textBaseline="alphabetic"; - const line=(lab,val,col)=>{g.fillStyle="#5f7480";g.font="14px "+FF();g.fillText(lab,pad,y); - g.fillStyle=col||"#151b1f";g.font="700 18px "+MF();g.textAlign="right";g.fillText(val,W-pad,y);g.textAlign="left";y+=38;}; - line("Paketkosten gesamt",eur(L.pkgTotal)); - line("Angerechneter Wert (im Paket)",eur(L.consumed),"#12556f"); + g.textBaseline="alphabetic"; + g.fillStyle="#eef3f6";g.fillRect(0,0,W,H); + // header bar + g.fillStyle="#0b3a54";g.fillRect(0,0,W,96); + // sauberes Martini-Logo + const lx=pad,ly=30; + g.fillStyle="#e2673d"; + g.beginPath();g.moveTo(lx,ly);g.lineTo(lx+30,ly);g.lineTo(lx+15,ly+18);g.closePath();g.fill(); // Kelch + g.fillRect(lx+13.5,ly+18,3,15); // Stiel + g.fillRect(lx+5,ly+33,20,3); // Fuß + g.beginPath();g.arc(lx+24,ly+3,2.6,0,7);g.fillStyle="#9fd0e6";g.fill(); // Olive + // Titel + g.fillStyle="#ffffff";g.font="700 23px "+FF();g.fillText("Getränkepaket-Abrechnung",lx+48,42); + g.fillStyle="#bcd4e2";g.font="13px "+FF(); + g.fillText(PKGNAME[L.pkg]+" · "+L.persons+"× · Tag "+L.curday+" von "+L.days+" · "+new Date().toLocaleDateString("de-DE"),lx+48,67); + // Zeilen + let y=y0; + const line=(lab,val,col,sub)=>{ + g.fillStyle="#33454f";g.font="15px "+FF();g.fillText(lab,pad,y); + g.fillStyle=col||"#151b1f";g.font="700 19px "+MF();g.textAlign="right";g.fillText(val,W-pad,y);g.textAlign="left"; + if(sub){g.fillStyle="#8a9aa4";g.font="12px "+FF();g.fillText(sub,pad,y+16);} + g.strokeStyle="#dbe5eb";g.beginPath();g.moveTo(pad,y+ (sub?24:11));g.lineTo(W-pad,y+(sub?24:11));g.stroke(); + y+=step+(sub?12:0);}; const done=L.remaining<=0.001; - line(done?"Break-even – Plus":"Noch bis Break-even",done?"+"+eur(-L.remaining):eur(L.remaining),done?"#1f9257":"#c2352a"); + line("Paketkosten gesamt",eur(L.pkgTotal),"#0b3a54"); + line("Angerechneter Wert (im Paket)",eur(L.consumed),"#12556f"); + line(done?"Break-even erreicht – Plus":"Noch bis Break-even",done?"+"+eur(-L.remaining):eur(L.remaining),done?"#1f9257":"#c2352a","Break-even pro Tag: "+eur(L.perDay)); line("Zusatzkosten (nicht im Paket)",eur(L.extra),"#c9542e"); line("Gratis-Bonus Kind unter 4",eur(L.kidVal),"#1f9257"); - // chart - const cx=pad, cy=rowsTop+18, cw=W-2*pad, ch=chartH-30; - g.fillStyle="#5f7480";g.font="12px "+FF();g.fillText("Angerechneter Wert je Tag (Ziel "+eur(L.perDay)+")",cx,cy-8); + // Chart + const cx=pad,cw=W-2*pad; + g.fillStyle="#5f7480";g.font="12px "+FF();g.fillText("Angerechneter Wert je Tag · Ziel "+eur(L.perDay)+"/Tag",cx,chartTop+4); const scale=Math.max(L.perDay*1.25,Math.max.apply(null,L.vals.concat([1]))*1.1,1); - const n=L.vals.length,bw=Math.min(30,(cw/n)-4); - // target line - const ty=cy+ch-(L.perDay/scale*ch);g.strokeStyle="#e2673d";g.setLineDash([5,4]);g.beginPath();g.moveTo(cx,ty);g.lineTo(cx+cw,ty);g.stroke();g.setLineDash([]); - for(let i=0;i