diff --git a/app/index.html b/app/index.html index 79cb9ae..2b0f525 100644 --- a/app/index.html +++ b/app/index.html @@ -1391,7 +1391,7 @@ async function generatePDF() { lbl('Staff', M.left, labY); lbl('Period', M.left + W * 0.45, labY); pg.drawText(state.staff, { x:M.left, y:valY, size:sz, font:fontBold, color:clrText }); - mono(`${state.periodFrom} to ${state.periodTo}`, M.left + W * 0.45, valY); + val(`${state.periodFrom} to ${state.periodTo}`, M.left + W * 0.45, valY); y -= stripH + 12; } @@ -1432,8 +1432,8 @@ async function generatePDF() { const nowD = new Date(); const MONTHS = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; const claimDate = `Claim · ${nowD.getDate()} ${MONTHS[nowD.getMonth()]} ${nowD.getFullYear()}`; - const claimDateW = fontMono.widthOfTextAtSize(claimDate, szSm); - pg.drawText(claimDate, { x: M.left + W - claimDateW, y: hdrTopY - 15 - szLg - 4, size: szSm, font: fontMono, color: clrMuted }); + const claimDateW = fontBody.widthOfTextAtSize(claimDate, szSm); + pg.drawText(claimDate, { x: M.left + W - claimDateW, y: hdrTopY - 15 - szLg - 4, size: szSm, font: fontBody, color: clrMuted }); y = hdrTopY - boxSize - 10; @@ -1463,10 +1463,10 @@ async function generatePDF() { lbl('Currency', M.left + iC3, iLabY); pg.drawText(state.staff, { x: M.left, y: iValY, size: sz, font: fontBold, color: clrText }); - mono(`${state.periodFrom} to ${state.periodTo}`, M.left + iC2, iValY); + val(`${state.periodFrom} to ${state.periodTo}`, M.left + iC2, iValY); pg.drawText(baseCur, { x: M.left + iC3, y: iValY, size: sz, font: fontBold, color: clrAccent }); - y -= infoH + 14; + y -= infoH + 20; // ── Items ───────────────────────────────────────────────────────────────── state.items.forEach(item => { @@ -1476,21 +1476,21 @@ async function generatePDF() { accentStripe(y); pg.drawText(item.name || '(no name)', { x: M.left + 8, y, size: sz, font: fontBold, color: clrTextSoft }); const subStr = `${baseCur} ${fmtAmt(item._subtotal)}`; - const subW = fontMono.widthOfTextAtSize(subStr, sz); - pg.drawText(subStr, { x: M.left + W - subW, y, size: sz, font: fontMono, color: clrAccent }); + const subW = fontBold.widthOfTextAtSize(subStr, sz); + pg.drawText(subStr, { x: M.left + W - subW, y, size: sz, font: fontBold, color: clrAccent }); y -= lh + 2; // Hairline below section header pg.drawLine({ start:{x:M.left, y}, end:{x:M.left+W, y}, thickness:0.5, color:clrBorder }); - y -= 8; + y -= 14; // Lines item.lines.forEach((ln, li) => { needSpace(lh * 9); if (li > 0 && !justBroke) { - y -= 4; - pg.drawLine({ start:{x:M.left, y}, end:{x:M.left+W, y}, thickness:0.3, color:clrBorder }); y -= 8; + pg.drawLine({ start:{x:M.left, y}, end:{x:M.left+W, y}, thickness:0.3, color:clrBorder }); + y -= 12; } const c1=0, c2=W*0.22, c3=W*0.64; @@ -1502,17 +1502,17 @@ async function generatePDF() { const fxLblStr = 'FX RATE'; pg.drawText(fxLblStr, { x: M.left + W - fontBold.widthOfTextAtSize(fxLblStr, szXs), y, size: szXs, font: fontBold, color: clrMuted }); - y -= szXs + 4; + y -= szXs + 6; const dateInPeriod = isDateInPeriod(ln.date); - mono((ln.date || '–') + (dateInPeriod ? '' : ' (!)'), M.left + c1, y, + val((ln.date || '–') + (dateInPeriod ? '' : ' (!)'), M.left + c1, y, { color: dateInPeriod ? clrText : clrWarn }); val(truncate(ln.vendor, fontBody, sz, (c3 - c2) - 8), M.left + c2, y); val(ln.currency || '–', M.left + c3, y); const fxStr = ln.currency === baseCur ? '–' : parseFloat(ln.fxRate).toFixed(5); - const fxW = fontMono.widthOfTextAtSize(fxStr, sz); - mono(fxStr, M.left + W - fxW, y); - y -= lh + 4; + const fxW = fontBody.widthOfTextAtSize(fxStr, sz); + val(fxStr, M.left + W - fxW, y); + y -= lh + 10; // Row 2 — Description | Receipt | Amount lbl('Description', M.left, y); @@ -1520,19 +1520,19 @@ async function generatePDF() { const amtLblStr = 'AMOUNT'; pg.drawText(amtLblStr, { x: M.left + W - fontBold.widthOfTextAtSize(amtLblStr, szXs), y, size: szXs, font: fontBold, color: clrMuted }); - y -= szXs + 4; + y -= szXs + 6; val(truncate(ln.description, fontBody, sz, c3 - 8), M.left, y); val(ln.hasReceipt ? 'Yes' : 'No', M.left + c3, y); const amtStr = `${ln.currency} ${fmtAmt(ln.amount)}`; - const amtW = fontMono.widthOfTextAtSize(amtStr, sz); - mono(amtStr, M.left + W - amtW, y); - y -= lh + 4; + const amtW = fontBody.widthOfTextAtSize(amtStr, sz); + val(amtStr, M.left + W - amtW, y); + y -= lh + 10; // Row 3 — Account | Program lbl('Account', M.left, y); lbl('Program', M.left + W * 0.5, y); - y -= szXs + 4; + y -= szXs + 6; val(truncate(ln.account || '–', fontBody, sz, W * 0.5 - 8), M.left, y); const progs = ln.programs || []; @@ -1540,7 +1540,7 @@ async function generatePDF() { const pe = progs[0] || {}; const progStr = pe.program === 'Other' ? `Other: ${pe.programOther}` : (pe.program || '–'); val(truncate(progStr, fontBody, sz, W * 0.5 - 8), M.left + W * 0.5, y); - y -= lh; + y -= lh + 4; } else { const lineBaseAmt = (() => { const a=parseFloat(ln.amount)||0, r=parseFloat(ln.fxRate)||1; return r>0?a/r:0; })(); progs.forEach((pe, pi) => { @@ -1550,8 +1550,8 @@ async function generatePDF() { const progAmt = lineBaseAmt * pct / 100; const suffix = `${pct.toFixed(2)}% · ${baseCur} ${fmtAmt(progAmt)}`; val(truncate(progStr, fontBody, sz, W * 0.42 - 8), M.left + W * 0.5, y); - const sfxW = fontMono.widthOfTextAtSize(suffix, szSm); - pg.drawText(suffix, { x: M.left + W - sfxW, y, size: szSm, font: fontMono, color: clrMuted }); + const sfxW = fontBody.widthOfTextAtSize(suffix, szSm); + pg.drawText(suffix, { x: M.left + W - sfxW, y, size: szSm, font: fontBody, color: clrMuted }); y -= lh; }); } @@ -1574,7 +1574,7 @@ async function generatePDF() { y -= 6; }); - y -= 10; + y -= 16; }); // Grand total @@ -1663,7 +1663,8 @@ async function generatePDF() { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; - a.download = `reimbursement_${state.staff.replace(/\s+/g,'_')}_${state.periodFrom}_${state.periodTo}.pdf`; + const docDate = new Date().toISOString().slice(0,10); + a.download = `${state.staff.replace(/\s+/g,'_')}_${docDate}_Reimbursement.pdf`; a.click(); URL.revokeObjectURL(url); }