Files
check-printing/public/index.html
T
steve e81a4386d2 Add multi-account support
- Schema: account_id FK on checks and layout_fields; UNIQUE per-account on check_no and field_name
- DB: runtime migration recreates both tables to add account_id (assigns existing rows to account 1)
- Routes: GET /api/accounts lists all; GET /api/account/:id replaces hardcoded id=1; POST /api/account/setup always creates a new account and returns accountId
- checks.js: all queries scoped by account_id; POST requires account_id in body
- pdf.js: resolves account from check's account_id instead of id=1; layout fields fetched per-account
- import-mdb.js: always INSERTs a new account (never deletes existing); all records tagged with new accountId
- Frontend: account switcher in header; activeAccountId persisted to localStorage; all API calls pass account_id; switching accounts reloads checks; wizard and import auto-switch to newly created account
2026-03-12 22:13:52 -06:00

244 lines
9.7 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ezcheck</title>
<link rel="stylesheet" href="/css/style.css">
</head>
<body>
<header>
<div class="header-left">
<span class="header-brand" id="company-name">ezcheck</span>
<select id="account-switcher" class="account-switcher" title="Switch account"></select>
</div>
<span class="header-info">Next check: <strong id="current-check-no"></strong></span>
</header>
<div class="toolbar">
<div class="toolbar-left">
<input type="search" id="filter-payee" placeholder="Search payee…" style="width:160px">
<input type="date" id="filter-date-from" title="From date">
<span style="color:var(--text-muted)"></span>
<input type="date" id="filter-date-to" title="To date">
<select id="filter-status">
<option value="" selected>All</option>
<option value="0">Unprinted</option>
<option value="1">Printed</option>
</select>
</div>
<div class="toolbar-right">
<button id="btn-generate-pdf" class="btn-primary" disabled>
Generate PDF <span id="selected-count" class="badge">0</span>
</button>
<button id="btn-new-check" class="btn-secondary">+ New Check</button>
<button id="btn-import" class="btn-secondary">Import .mdb</button>
</div>
</div>
<div class="table-wrap">
<table id="checks-table">
<thead>
<tr>
<th class="col-select"></th>
<th class="col-no sortable" data-col="check_no"># <span class="sort-indicator"></span></th>
<th class="col-date sortable" data-col="check_date">Date <span class="sort-indicator"></span></th>
<th class="col-payee sortable" data-col="payee">Payee <span class="sort-indicator"></span></th>
<th class="col-amount sortable" data-col="amount">Amount <span class="sort-indicator"></span></th>
<th class="col-memo">Memo</th>
<th class="col-status">Status</th>
<th class="col-actions"></th>
</tr>
</thead>
<tbody id="checks-tbody">
<tr class="loading-row"><td colspan="8">Loading…</td></tr>
</tbody>
</table>
</div>
<!-- Slide-in panel -->
<div id="panel-overlay"></div>
<aside id="check-panel">
<div class="panel-header">
<h2 id="panel-title">New Check</h2>
<button id="btn-close-panel" class="btn-icon" title="Close">×</button>
</div>
<form id="check-form" novalidate>
<div class="form-group required">
<label for="f-payee">Payee</label>
<input type="text" id="f-payee" name="payee" required autocomplete="off">
</div>
<div class="form-row">
<div class="form-group required">
<label for="f-amount">Amount ($)</label>
<input type="number" id="f-amount" name="amount" required min="0.01" step="0.01" placeholder="0.00">
</div>
<div class="form-group required">
<label for="f-date">Date</label>
<input type="date" id="f-date" name="check_date" required>
</div>
</div>
<div class="form-group">
<label for="f-memo">Memo</label>
<input type="text" id="f-memo" name="memo">
</div>
<div class="form-row">
<div class="form-group">
<label for="f-note1">Note 1</label>
<input type="text" id="f-note1" name="note1">
</div>
<div class="form-group">
<label for="f-note2">Note 2</label>
<input type="text" id="f-note2" name="note2">
</div>
</div>
<details class="address-section">
<summary>Payee Address</summary>
<div class="address-fields">
<div class="form-group">
<label for="f-addr1">Line 1</label>
<input type="text" id="f-addr1" name="payee_address1">
</div>
<div class="form-group">
<label for="f-addr2">Line 2</label>
<input type="text" id="f-addr2" name="payee_address2">
</div>
<div class="form-group">
<label for="f-addr3">Line 3</label>
<input type="text" id="f-addr3" name="payee_address3">
</div>
<div class="form-group">
<label for="f-addr4">Line 4</label>
<input type="text" id="f-addr4" name="payee_address4">
</div>
</div>
</details>
<div class="form-actions">
<button type="submit" class="btn-primary" id="btn-save">Save Check</button>
<button type="button" class="btn-ghost" id="btn-cancel">Cancel</button>
</div>
</form>
</aside>
<!-- Setup wizard -->
<div id="wizard-overlay" class="modal-overlay"></div>
<div id="wizard-modal" class="modal wizard-modal" role="dialog" aria-labelledby="wizard-title">
<div class="modal-header">
<h2 id="wizard-title">Account Setup</h2>
</div>
<div class="wizard-steps-indicator">
<div class="wizard-step-dot active" data-step="1"><span>1</span><label>Checkwriter</label></div>
<div class="wizard-step-line"></div>
<div class="wizard-step-dot" data-step="2"><span>2</span><label>Bank</label></div>
<div class="wizard-step-line"></div>
<div class="wizard-step-dot" data-step="3"><span>3</span><label>Account</label></div>
</div>
<div class="modal-body wizard-body">
<!-- Step 1: Checkwriter -->
<div class="wizard-step" id="wizard-step-1">
<div class="form-group required">
<label for="w-company1">Organization Name</label>
<input type="text" id="w-company1" autocomplete="organization">
</div>
<div class="form-group">
<label for="w-addr1">Address</label>
<input type="text" id="w-addr1" autocomplete="street-address">
</div>
<div class="form-row form-row-3">
<div class="form-group">
<label for="w-city">City</label>
<input type="text" id="w-city" autocomplete="address-level2">
</div>
<div class="form-group form-group-sm">
<label for="w-state">State</label>
<input type="text" id="w-state" maxlength="2" autocomplete="address-level1" placeholder="MT">
</div>
<div class="form-group form-group-sm">
<label for="w-zip">ZIP</label>
<input type="text" id="w-zip" maxlength="10" autocomplete="postal-code">
</div>
</div>
<div class="form-group">
<label for="w-contact">Phone / Website / Email</label>
<input type="text" id="w-contact" placeholder="e.g. 406-555-0100">
</div>
</div>
<!-- Step 2: Bank -->
<div class="wizard-step" id="wizard-step-2" hidden>
<div class="form-group required">
<label for="w-bank-name">Bank Name</label>
<input type="text" id="w-bank-name">
</div>
<div class="form-group">
<label for="w-bank-addr">Bank Address</label>
<input type="text" id="w-bank-addr">
</div>
<div class="form-group">
<label for="w-transit">Transit Code</label>
<input type="text" id="w-transit" placeholder="e.g. 092900383">
</div>
<div class="form-group">
<label for="w-bank-contact">Phone / Website</label>
<input type="text" id="w-bank-contact">
</div>
</div>
<!-- Step 3: Account -->
<div class="wizard-step" id="wizard-step-3" hidden>
<div class="form-group required">
<label for="w-routing">Routing Number</label>
<input type="text" id="w-routing" inputmode="numeric" maxlength="9">
</div>
<div class="form-group required">
<label for="w-account">Account Number</label>
<input type="text" id="w-account" inputmode="numeric">
</div>
<div class="form-group required">
<label for="w-start-check">Starting Check Number</label>
<input type="number" id="w-start-check" min="1" step="1" value="1001">
</div>
<div class="form-group">
<label for="w-logo">Logo (optional)</label>
<input type="file" id="w-logo" accept="image/*">
<span class="field-hint">Printed top-left of each check. PNG or GIF recommended.</span>
</div>
</div>
<div id="wizard-error" class="wizard-error" hidden></div>
</div>
<div class="modal-footer wizard-footer">
<button id="btn-wizard-prev" class="btn-secondary" hidden>← Back</button>
<button id="btn-wizard-next" class="btn-primary">Next →</button>
<button id="btn-wizard-finish" class="btn-primary" hidden>Save &amp; Start</button>
<button id="btn-wizard-skip" class="btn-ghost">Use Import instead</button>
</div>
</div>
<!-- Import modal -->
<div id="import-modal-overlay" class="modal-overlay"></div>
<div id="import-modal" class="modal" role="dialog" aria-labelledby="import-modal-title">
<div class="modal-header">
<h2 id="import-modal-title">Import from .mdb</h2>
<button id="btn-close-import" class="btn-icon" title="Close">×</button>
</div>
<div class="modal-body">
<p class="modal-desc">Select an ezCheckPrinting <code>.mdb</code> file to import account settings, check layout, and check history. This will replace existing data.</p>
<div class="form-group">
<label for="import-file">File</label>
<input type="file" id="import-file" accept=".mdb">
</div>
<div id="import-log" class="import-log" hidden></div>
</div>
<div class="modal-footer">
<button id="btn-run-import" class="btn-primary">Import</button>
<button id="btn-cancel-import" class="btn-ghost">Cancel</button>
</div>
</div>
<script src="/js/app.js"></script>
</body>
</html>