a89db179cd
- Add gear button in header to open account settings modal - Modal covers Organization, Bank, Account, Logo upload, and Printer Offset fields - PUT /api/account/:id backend endpoint with full field validation - Logo file reader with inline preview; only updates logo if a new file is chosen - CSS for btn-header-icon, settings-section-label, logo-preview, form-row-4 - Add GnuMICR.otf (tracked alongside existing GnuMICR.ttf)
340 lines
14 KiB
HTML
340 lines
14 KiB
HTML
<!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>
|
||
<button id="btn-account-settings" class="btn-header-icon" title="Account settings">⚙</button>
|
||
</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 & 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>
|
||
|
||
<!-- Account settings modal -->
|
||
<div id="acct-settings-overlay" class="modal-overlay"></div>
|
||
<div id="acct-settings-modal" class="modal" role="dialog" aria-labelledby="acct-settings-title">
|
||
<div class="modal-header">
|
||
<h2 id="acct-settings-title">Account Settings</h2>
|
||
<button id="btn-close-acct-settings" class="btn-icon" title="Close">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<form id="acct-settings-form" novalidate>
|
||
|
||
<p class="settings-section-label">Organization</p>
|
||
<div class="form-group required">
|
||
<label for="as-company1">Name</label>
|
||
<input type="text" id="as-company1" name="company1">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-company2">Address</label>
|
||
<input type="text" id="as-company2" name="company2">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-company3">City / State / ZIP</label>
|
||
<input type="text" id="as-company3" name="company3">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-company4">Phone / Web / Email</label>
|
||
<input type="text" id="as-company4" name="company4">
|
||
</div>
|
||
|
||
<p class="settings-section-label">Bank</p>
|
||
<div class="form-group">
|
||
<label for="as-bank-name">Bank Name</label>
|
||
<input type="text" id="as-bank-name" name="bank_name">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-bank-info1">Bank Address</label>
|
||
<input type="text" id="as-bank-info1" name="bank_info1">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-bank-info2">Bank Phone / Web</label>
|
||
<input type="text" id="as-bank-info2" name="bank_info2">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-transit">Transit Code</label>
|
||
<input type="text" id="as-transit" name="transit_code">
|
||
</div>
|
||
|
||
<p class="settings-section-label">Account</p>
|
||
<div class="form-row">
|
||
<div class="form-group required">
|
||
<label for="as-routing">Routing Number</label>
|
||
<input type="text" id="as-routing" name="routing_number" inputmode="numeric" maxlength="9">
|
||
</div>
|
||
<div class="form-group required">
|
||
<label for="as-account">Account Number</label>
|
||
<input type="text" id="as-account" name="account_number" inputmode="numeric">
|
||
</div>
|
||
</div>
|
||
|
||
<p class="settings-section-label">Logo</p>
|
||
<div class="form-group">
|
||
<label for="as-logo">Upload new logo</label>
|
||
<input type="file" id="as-logo" accept="image/*">
|
||
<span class="field-hint">Replaces existing logo. PNG or GIF recommended.</span>
|
||
</div>
|
||
<div id="as-logo-preview" class="logo-preview" hidden></div>
|
||
|
||
<p class="settings-section-label">Printer Offset <span class="field-hint">(inches — adjust if checks print misaligned)</span></p>
|
||
<div class="form-row form-row-4">
|
||
<div class="form-group">
|
||
<label for="as-off-left">Left</label>
|
||
<input type="number" id="as-off-left" name="offset_left" step="0.01" placeholder="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-off-right">Right</label>
|
||
<input type="number" id="as-off-right" name="offset_right" step="0.01" placeholder="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-off-up">Up</label>
|
||
<input type="number" id="as-off-up" name="offset_up" step="0.01" placeholder="0">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="as-off-down">Down</label>
|
||
<input type="number" id="as-off-down" name="offset_down" step="0.01" placeholder="0">
|
||
</div>
|
||
</div>
|
||
|
||
<div id="acct-settings-error" class="wizard-error" hidden></div>
|
||
</form>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button id="btn-save-acct-settings" class="btn-primary">Save Changes</button>
|
||
<button id="btn-cancel-acct-settings" class="btn-ghost">Cancel</button>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/js/app.js"></script>
|
||
</body>
|
||
</html>
|