/* CheckPrinterPlus.tsx React + TypeScript + Tailwind Includes UAE banks with template mapping for check sizes. */ import React, { useEffect, useState, useRef } from "react"; function uid(): string { return Math.random().toString(36).slice(2, 9); } const STORAGE_KEY = "checkprinterplus_v3"; interface Check { id: string; payee: string; amount: number; date: string; bank: string; email: string; status: "pending" | "printed"; createdAt: string; printed: boolean; _remindedAt?: string; } interface LogEntry { id: string; ts: string; text: string; } interface State { checks: Check[]; logs: LogEntry[]; } interface FieldPos { x: number; y: number; } interface BankTemplate { width: number; // mm height: number; // mm fields: { payee: FieldPos; date: FieldPos; amount: FieldPos; amountWords: FieldPos; }; } function loadState(): State { try { const raw = localStorage.getItem(STORAGE_KEY); if (!raw) return { checks: [], logs: [] }; return JSON.parse(raw) as State; } catch { return { checks: [], logs: [] }; } } function saveState(state: State) { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } function toWords(n: number): string { return n.toFixed(2); // placeholder for number-to-words } /* UAE Bank Templates (placeholder coordinates in mm) */ const bankTemplates: Record = { "First Abu Dhabi Bank (FAB)": { width: 210, height: 99, fields: { payee: { x: 40, y: 50 }, date: { x: 160, y: 20 }, amount: { x: 160, y: 40 }, amountWords: { x: 40, y: 70 }, }, }, "Emirates NBD": { width: 210, height: 90, fields: { payee: { x: 35, y: 48 }, date: { x: 150, y: 18 }, amount: { x: 150, y: 38 }, amountWords: { x: 35, y: 68 }, }, }, "Dubai Islamic Bank": { width: 210, height: 95, fields: { payee: { x: 38, y: 52 }, date: { x: 155, y: 22 }, amount: { x: 155, y: 42 }, amountWords: { x: 38, y: 72 }, }, }, // Add all UAE banks with placeholders... }; const UAE_BANKS = Object.keys(bankTemplates); export default function CheckPrinterPlus() { const [state, setState] = useState(() => loadState()); const [form, setForm] = useState({ payee: "", amount: "", date: "", bank: "", email: "", }); const [filter, setFilter] = useState<"all" | "pending" | "printed">("all"); const [reminderDays, setReminderDays] = useState(3); const schedulerRef = useRef(null); const [selected, setSelected] = useState(null); useEffect(() => { saveState(state); }, [state]); useEffect(() => { if (schedulerRef.current) clearInterval(schedulerRef.current); schedulerRef.current = setInterval(runScheduler, 60 * 1000); runScheduler(); return () => { if (schedulerRef.current) clearInterval(schedulerRef.current); }; }, [state, reminderDays]); function addLog(text: string) { const logs = [ { id: uid(), ts: new Date().toISOString(), text }, ...state.logs, ].slice(0, 200); setState((prev) => ({ ...prev, logs })); } function addCheck(e?: React.FormEvent) { e && e.preventDefault(); if (!form.payee || !form.amount || !form.date) { addLog("Missing required fields"); return; } const check: Check = { id: uid(), payee: form.payee, amount: parseFloat(form.amount), date: form.date, bank: form.bank, email: form.email, status: "pending", createdAt: new Date().toISOString(), printed: false, }; setState((prev) => ({ checks: [check, ...prev.checks], logs: prev.logs })); setForm({ payee: "", amount: "", date: "", bank: "", email: "" }); addLog("Check added"); } function updateCheck(id: string, patch: Partial) { setState((prev) => ({ ...prev, checks: prev.checks.map((c) => (c.id === id ? { ...c, ...patch } : c)), })); } function deleteCheck(id: string) { setState((prev) => ({ ...prev, checks: prev.checks.filter((c) => c.id !== id), })); addLog("Check deleted"); } async function sendEmail(check: Check) { if (!check.email) { addLog(`No email for ${check.payee}`); return false; } await new Promise((r) => setTimeout(r, 400)); addLog(`Email reminder sent to ${check.email} for check ${check.id}`); return true; } async function runScheduler() { const now = new Date(); const upcoming = state.checks .filter((c) => c.status === "pending") .filter((c) => { const d = new Date(c.date + "T00:00:00"); const diffDays = Math.ceil( (d.getTime() - now.getTime()) / (1000 * 60 * 60 * 24) ); return diffDays >= 0 && diffDays <= reminderDays; }); for (const c of upcoming) { if (c._remindedAt) continue; const ok = await sendEmail(c); if (ok) updateCheck(c.id, { _remindedAt: new Date().toISOString() }); } } function markPrinted(id: string) { updateCheck(id, { printed: true, status: "printed" }); addLog(`Check ${id} marked printed`); } const counts = { all: state.checks.length, pending: state.checks.filter((c) => c.status === "pending").length, printed: state.checks.filter((c) => c.printed).length, }; const visible = state.checks.filter((c) => filter === "all" ? true : filter === "pending" ? c.status === "pending" : c.printed ); return (

CheckPrinterPlus

{/* Add Form */}
setForm({ ...form, payee: e.target.value })} placeholder="Payee" className="border p-2 rounded" /> setForm({ ...form, amount: e.target.value })} type="number" placeholder="Amount" className="border p-2 rounded" /> setForm({ ...form, date: e.target.value })} type="date" className="border p-2 rounded" /> setForm({ ...form, email: e.target.value })} type="email" placeholder="Reminder Email" className="border p-2 rounded md:col-span-2" />
{/* Filters */}
{/* Table */} {visible.map((c) => ( ))}
Payee Date Amount Bank Email Status
{c.payee} {c.date} {c.amount.toFixed(2)} {c.bank} {c.email} {c.printed ? "Printed" : "Pending"}
{/* Print Preview */} {selected && (

Print Preview

{(() => { const tpl = bankTemplates[selected.bank]; if (!tpl) return
No template for this bank
; return (
{selected.payee}
{selected.date}
{selected.amount.toFixed(2)}
{toWords(selected.amount)}
); })()}
)}
); }