/** HTML rendering for email checklist and web confirmation page. */ import type { AcceptanceCriterion } from "./backlog"; import { checklistConfig } from "./checklist-config"; const SUCCESS_COLOR = "#22c55e"; function esc(str: string): string { return str.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } const styles = ` body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;background:#f8fafc;color:#1e293b;margin:0;padding:20px} .card{max-width:600px;margin:40px auto;background:#fff;border-radius:12px;box-shadow:0 1px 3px rgba(0,0,0,.1);padding:32px} h1{font-size:20px;margin:0 0 4px;color:#0f172a} .sub{font-size:14px;color:#64748b;margin-bottom:24px} .row{display:flex;align-items:flex-start;gap:12px;padding:10px 0;border-bottom:1px solid #f1f5f9} .row:last-child{border-bottom:none} .cb{width:22px;height:22px;border-radius:6px;display:inline-flex;align-items:center;justify-content:center;font-size:14px;text-decoration:none;flex-shrink:0;margin-top:1px} .uc{border:2px solid #cbd5e1;color:#cbd5e1;background:#fff} .uc:hover{border-color:#6366f1;color:#6366f1} .ck{border:2px solid ${SUCCESS_COLOR};background:${SUCCESS_COLOR};color:#fff} .txt{font-size:15px;line-height:1.5} .done{color:#94a3b8;text-decoration:line-through} .ft{text-align:center;font-size:12px;color:#94a3b8;margin-top:24px} .pb{height:6px;background:#e2e8f0;border-radius:3px;overflow:hidden;margin:16px 0 8px} .pf{height:100%;background:${SUCCESS_COLOR};border-radius:3px} .pt{font-size:13px;color:#64748b;margin-bottom:24px} .hi{background:#f0fdf4;border-radius:8px;padding:2px 0} `; function progress(criteria: AcceptanceCriterion[]): string { const total = criteria.length; const done = criteria.filter((c) => c.checked).length; const pct = total > 0 ? Math.round((done / total) * 100) : 0; return `
${done} of ${total} complete (${pct}%)
`; } function checklist(criteria: AcceptanceCriterion[], tokens: Map, justChecked?: number): string { return criteria .map((ac) => { const hi = ac.index === justChecked ? " hi" : ""; if (ac.checked) { return `
#${ac.index} ${esc(ac.text)}
`; } const token = tokens.get(ac.index); const url = token ? `${checklistConfig.baseUrl}/rtasks/check/${token}` : "#"; return `
 #${ac.index} ${esc(ac.text)}
`; }) .join("\n"); } export function renderEmailHTML( title: string, taskId: string, criteria: AcceptanceCriterion[], tokens: Map, ): string { return `

${esc(title)}

${esc(taskId)} · Click a checkbox to mark it done
${progress(criteria)} ${checklist(criteria, tokens)}
Sent by rTasks · Links expire in ${checklistConfig.tokenExpiryDays} days
`; } export function renderWebPage( title: string, taskId: string, criteria: AcceptanceCriterion[], tokens: Map, justChecked?: number, ): string { const justAC = justChecked !== undefined ? criteria.find((c) => c.index === justChecked) : undefined; const banner = justAC ? `
✓ Checked off: #${justAC.index} ${esc(justAC.text)}
` : ""; const allDone = criteria.every((c) => c.checked); const doneMsg = allDone ? `
🎉 All items complete!
` : ""; return `${esc(title)} - rTasks
${banner}

${esc(title)}

${esc(taskId)}
${progress(criteria)} ${checklist(criteria, tokens, justChecked)} ${doneMsg}
rTasks · rspace.online
`; } export function renderError(title: string, message: string): string { return `Error - rTasks

${esc(title)}

${esc(message)}

rTasks · rspace.online
`; }