d70081159d
- SVG canvas showing all layout fields scaled to check dimensions - Click or dropdown to select a field; drag to reposition - Sidebar shows X/Y coordinates in decimal inches with fraction equivalents (¼", ½", ¹⁄₁₆", etc.) - End X/Y inputs appear for Line and Graph fields - Nudge buttons move selected field by ¹⁄₁₆" per click - Auto-saves on drag end; debounced save on input/nudge changes - Visible toggle hides fields from PDF without deleting them - Admin-only Reset to Default wipes and re-seeds the layout - Accessible to editor+ role via ⊞ button in account header
819 lines
37 KiB
HTML
819 lines
37 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>
|
||
<!-- Login overlay -->
|
||
<div id="login-overlay" class="login-overlay">
|
||
<div class="login-card" id="login-card">
|
||
<div class="login-logo">ezcheck</div>
|
||
<!-- First-run: create admin -->
|
||
<div id="login-setup-section" hidden>
|
||
<h2>Create Admin Account</h2>
|
||
<p class="login-sub">No users exist yet. Set up the first admin account.</p>
|
||
<div class="form-group">
|
||
<label for="setup-username">Username</label>
|
||
<input type="text" id="setup-username" autocomplete="username" autocapitalize="none">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="setup-password">Password <span class="field-hint">(min 10 characters, include a digit or symbol)</span></label>
|
||
<input type="password" id="setup-password" autocomplete="new-password">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="setup-password2">Confirm Password</label>
|
||
<input type="password" id="setup-password2" autocomplete="new-password">
|
||
</div>
|
||
<div id="setup-error" class="wizard-error" hidden></div>
|
||
<button id="btn-setup-submit" class="btn-primary" style="width:100%;margin-top:8px">Create Admin & Sign In</button>
|
||
</div>
|
||
<!-- Normal login -->
|
||
<div id="login-form-section" hidden>
|
||
<h2>Sign In</h2>
|
||
<div class="form-group">
|
||
<label for="login-username">Username</label>
|
||
<input type="text" id="login-username" autocomplete="username" autocapitalize="none">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="login-password">Password</label>
|
||
<input type="password" id="login-password" autocomplete="current-password">
|
||
</div>
|
||
<div id="login-error" class="wizard-error" hidden></div>
|
||
<button id="btn-login-submit" class="btn-primary" style="width:100%;margin-top:8px">Sign In</button>
|
||
<div style="text-align:center;margin-top:8px">
|
||
<a href="#" id="link-forgot-password" style="font-size:12px;color:var(--text-muted)">Forgot password?</a>
|
||
</div>
|
||
</div>
|
||
<!-- Forgot password -->
|
||
<div id="login-forgot-section" hidden>
|
||
<h2>Reset Password</h2>
|
||
<p class="login-sub">Enter your email and we'll send a reset link.</p>
|
||
<div class="form-group">
|
||
<label for="forgot-email">Email</label>
|
||
<input type="email" id="forgot-email" autocomplete="email">
|
||
</div>
|
||
<div id="forgot-error" class="wizard-error" hidden></div>
|
||
<div id="forgot-success" class="import-result" hidden></div>
|
||
<button id="btn-forgot-submit" class="btn-primary" style="width:100%;margin-top:8px">Send Reset Link</button>
|
||
<div style="text-align:center;margin-top:8px">
|
||
<a href="#" id="link-back-to-login" style="font-size:12px;color:var(--text-muted)">Back to Sign In</a>
|
||
</div>
|
||
</div>
|
||
<!-- Reset password (arrived via emailed link) -->
|
||
<div id="login-reset-section" hidden>
|
||
<h2>Set New Password</h2>
|
||
<div class="form-group">
|
||
<label for="reset-password">New Password <span class="field-hint">(min 10 chars, include a digit or symbol)</span></label>
|
||
<input type="password" id="reset-password" autocomplete="new-password">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="reset-password2">Confirm Password</label>
|
||
<input type="password" id="reset-password2" autocomplete="new-password">
|
||
</div>
|
||
<div id="reset-error" class="wizard-error" hidden></div>
|
||
<div id="reset-success" class="import-result" hidden></div>
|
||
<button id="btn-reset-submit" class="btn-primary" style="width:100%;margin-top:8px">Set Password</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<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" data-admin-only>⚙</button>
|
||
<button id="btn-layout-editor" class="btn-header-icon" title="Edit check layout" data-editor-only>⊞</button>
|
||
<button id="btn-add-account" class="btn-header-icon" title="Add checking account" data-admin-only>+</button>
|
||
</div>
|
||
<div class="header-right">
|
||
<span class="header-info">Next check: <strong id="current-check-no">—</strong><button id="btn-set-check-no" class="btn-header-inline" title="Set next check number" data-admin-only>✎</button></span>
|
||
<button id="btn-users" class="btn-header-icon" title="Manage users" data-admin-only hidden>👥</button>
|
||
<span id="header-username" class="header-username"></span>
|
||
<button id="btn-logout" class="btn-header-icon" title="Sign out">↩</button>
|
||
</div>
|
||
</header>
|
||
|
||
<!-- View nav tabs -->
|
||
<nav class="view-nav">
|
||
<button class="view-tab active" data-view="checks">Checks</button>
|
||
<button class="view-tab" data-view="deposits">Deposits</button>
|
||
</nav>
|
||
|
||
<!-- Checks view -->
|
||
<div id="view-checks" class="view-pane">
|
||
<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" data-editor-only>+ New Check</button>
|
||
<button id="btn-import" class="btn-secondary" data-editor-only>Import .mdb</button>
|
||
<button class="btn-secondary" data-editor-only data-open-qbo="checks">Import QBO</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div id="checks-summary" class="checks-summary"></div>
|
||
|
||
<div class="table-wrap">
|
||
<table id="checks-table">
|
||
<thead>
|
||
<tr>
|
||
<th class="col-select"><input type="checkbox" id="select-all-checks" title="Select all"></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>
|
||
</div><!-- /view-checks -->
|
||
|
||
<!-- Deposits view -->
|
||
<div id="view-deposits" class="view-pane" hidden>
|
||
<div class="toolbar">
|
||
<div class="toolbar-left">
|
||
<input type="date" id="dep-filter-from" title="From date">
|
||
<span style="color:var(--text-muted)">–</span>
|
||
<input type="date" id="dep-filter-to" title="To date">
|
||
<select id="dep-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-new-deposit" class="btn-secondary" data-editor-only>+ New Deposit</button>
|
||
<button class="btn-secondary" data-editor-only data-open-qbo="deposits">Import QBO</button>
|
||
</div>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table id="deposits-table">
|
||
<thead>
|
||
<tr>
|
||
<th class="col-date">Date</th>
|
||
<th class="col-amount" style="text-align:right">Checks Total</th>
|
||
<th class="col-amount" style="text-align:right">Cash</th>
|
||
<th class="col-amount" style="text-align:right">Cash Back</th>
|
||
<th class="col-amount" style="text-align:right">Deposit Total</th>
|
||
<th style="width:50px;text-align:center">Items</th>
|
||
<th class="col-status">Status</th>
|
||
<th class="col-actions"></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="deposits-tbody">
|
||
<tr class="loading-row"><td colspan="8">Loading…</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div><!-- /view-deposits -->
|
||
|
||
<!-- 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>
|
||
|
||
<div class="delete-account-row">
|
||
<button type="button" id="btn-delete-account" class="btn-danger">Delete Account</button>
|
||
</div>
|
||
|
||
<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">Signature</p>
|
||
<div class="form-group form-group-check">
|
||
<label><input type="checkbox" id="as-second-sig" name="second_signature"> Add second signature line (0.25" above primary)</label>
|
||
</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>
|
||
|
||
<!-- Set next check number modal -->
|
||
<div id="set-check-no-overlay" class="modal-overlay"></div>
|
||
<div id="set-check-no-modal" class="modal" role="dialog" aria-labelledby="set-check-no-title">
|
||
<div class="modal-header">
|
||
<h2 id="set-check-no-title">Set Next Check Number</h2>
|
||
<button id="btn-close-set-check-no" class="btn-icon" title="Close">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="warning-box">
|
||
<strong>Warning:</strong> Changing the next check number can create duplicate check numbers or gaps in your sequence. Only do this if checks were printed outside this system or your book was restarted.
|
||
</div>
|
||
<div class="form-group" style="margin-top:12px">
|
||
<label for="set-check-no-input">Next check number</label>
|
||
<input type="number" id="set-check-no-input" min="1" step="1" style="width:120px">
|
||
</div>
|
||
<div id="set-check-no-error" class="wizard-error" hidden></div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button id="btn-confirm-set-check-no" class="btn-primary">Set Number</button>
|
||
<button id="btn-cancel-set-check-no" class="btn-ghost">Cancel</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Delete account confirmation modal -->
|
||
<div id="delete-account-overlay" class="modal-overlay"></div>
|
||
<div id="delete-account-modal" class="modal" role="dialog" aria-labelledby="delete-account-title">
|
||
<div class="modal-header">
|
||
<h2 id="delete-account-title">Delete Account</h2>
|
||
<button id="btn-close-delete-account" class="btn-icon" title="Close">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="warning-box" style="background:#fef2f2;border-color:#f87171;color:#7f1d1d">
|
||
<strong>This cannot be undone.</strong> Deleting this account will permanently remove all checks, deposits, and account settings. There is no recovery.
|
||
</div>
|
||
<p style="margin-top:14px;font-size:13px">Are you sure you want to delete <strong id="delete-account-name"></strong>?</p>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button id="btn-confirm-delete-account" class="btn-danger">Yes, Delete Account</button>
|
||
<button id="btn-cancel-delete-account" class="btn-ghost">Cancel</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- QBO Import modal -->
|
||
<div id="qbo-import-overlay" class="modal-overlay"></div>
|
||
<div id="qbo-import-modal" class="modal modal-wide" role="dialog" aria-labelledby="qbo-import-title">
|
||
<div class="modal-header">
|
||
<h2 id="qbo-import-title">Import from QuickBooks Online</h2>
|
||
<button id="btn-close-qbo-import" class="btn-icon" title="Close">×</button>
|
||
</div>
|
||
<div class="qbo-tabs">
|
||
<button class="qbo-tab active" data-tab="checks">Checks to Print</button>
|
||
<button class="qbo-tab" data-tab="deposits">Deposits</button>
|
||
</div>
|
||
<!-- Checks pane -->
|
||
<div class="qbo-pane" id="qbo-pane-checks">
|
||
<div class="modal-body">
|
||
<p class="modal-desc">Export a <strong>Transaction List</strong> or <strong>Check Detail</strong> report from QBO (filtered to checks) as CSV, then upload it here. Checks will be imported as unprinted.</p>
|
||
<div class="form-group">
|
||
<label for="qbo-checks-file">QBO CSV export</label>
|
||
<input type="file" id="qbo-checks-file" accept=".csv,.txt">
|
||
</div>
|
||
<div id="qbo-checks-preview" hidden></div>
|
||
<div id="qbo-checks-result" class="import-result" hidden></div>
|
||
<div id="qbo-checks-error" class="wizard-error" hidden></div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button id="btn-qbo-checks-parse" class="btn-secondary">Preview</button>
|
||
<button id="btn-qbo-checks-import" class="btn-primary" hidden disabled>Import</button>
|
||
<button id="btn-qbo-checks-cancel" class="btn-ghost">Cancel</button>
|
||
</div>
|
||
</div>
|
||
<!-- Deposits pane -->
|
||
<div class="qbo-pane" id="qbo-pane-deposits" hidden>
|
||
<div class="modal-body">
|
||
<p class="modal-desc">Export a <strong>Deposit Detail</strong> report from QBO as CSV. Deposits are grouped by date — one deposit record per day.</p>
|
||
<div class="form-group">
|
||
<label for="qbo-deposits-file">QBO CSV export</label>
|
||
<input type="file" id="qbo-deposits-file" accept=".csv,.txt">
|
||
</div>
|
||
<div id="qbo-deposits-preview" hidden></div>
|
||
<div id="qbo-deposits-result" class="import-result" hidden></div>
|
||
<div id="qbo-deposits-error" class="wizard-error" hidden></div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button id="btn-qbo-deposits-parse" class="btn-secondary">Preview</button>
|
||
<button id="btn-qbo-deposits-import" class="btn-primary" hidden disabled>Import</button>
|
||
<button id="btn-qbo-deposits-cancel" class="btn-ghost">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Deposit slide-in panel -->
|
||
<div id="dep-panel-overlay"></div>
|
||
<aside id="deposit-panel">
|
||
<div class="panel-header">
|
||
<h2 id="dep-panel-title">New Deposit</h2>
|
||
<button id="btn-close-dep-panel" class="btn-icon" title="Close">×</button>
|
||
</div>
|
||
<div id="deposit-panel-body">
|
||
<!-- Top section: date + cash fields -->
|
||
<div class="dep-summary">
|
||
<div class="form-row">
|
||
<div class="form-group required">
|
||
<label for="dep-date">Deposit Date</label>
|
||
<input type="date" id="dep-date">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="dep-currency">Currency ($)</label>
|
||
<input type="number" id="dep-currency" min="0" step="0.01" placeholder="0.00">
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="dep-coin">Coin ($)</label>
|
||
<input type="number" id="dep-coin" min="0" step="0.01" placeholder="0.00">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="dep-cashback">Cash Back ($)</label>
|
||
<input type="number" id="dep-cashback" min="0" step="0.01" placeholder="0.00">
|
||
</div>
|
||
</div>
|
||
<div class="dep-totals">
|
||
<div class="dep-total-row"><span>Cash Total</span><span id="dep-cash-total">$0.00</span></div>
|
||
<div class="dep-total-row"><span>Checks Total</span><span id="dep-checks-total">$0.00</span></div>
|
||
<div class="dep-total-row"><span>Subtotal</span><span id="dep-subtotal">$0.00</span></div>
|
||
<div class="dep-total-row"><span>Cash Back</span><span id="dep-cashback-display">$0.00</span></div>
|
||
<div class="dep-total-row dep-total-grand"><span>Deposit Total</span><span id="dep-grand-total">$0.00</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Check items grid -->
|
||
<div class="dep-checks-section">
|
||
<div class="dep-checks-header">
|
||
<span>Checks</span>
|
||
<button type="button" id="btn-add-dep-item" class="btn-sm btn-secondary">+ Add Row</button>
|
||
</div>
|
||
<table class="dep-items-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Check #</th>
|
||
<th>Payee</th>
|
||
<th>Memo</th>
|
||
<th style="text-align:right">Amount</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="dep-items-tbody"></tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<div id="dep-panel-error" class="wizard-error" hidden></div>
|
||
|
||
<div class="form-actions dep-form-actions">
|
||
<div class="dep-pdf-btns">
|
||
<button type="button" id="btn-dep-slip" class="btn-secondary" disabled>Deposit Slip</button>
|
||
<button type="button" id="btn-dep-report" class="btn-secondary" disabled>Report</button>
|
||
</div>
|
||
<div>
|
||
<button type="button" id="btn-save-deposit" class="btn-primary">Save Deposit</button>
|
||
<button type="button" id="btn-cancel-deposit" class="btn-ghost">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
<!-- User management modal (admin only) -->
|
||
<div id="users-overlay" class="modal-overlay"></div>
|
||
<div id="users-modal" class="modal modal-wide" role="dialog" aria-labelledby="users-title">
|
||
<div class="modal-header">
|
||
<h2 id="users-title">Manage Users</h2>
|
||
<button id="btn-close-users" class="btn-icon" title="Close">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div id="users-list"></div>
|
||
<div style="margin-top:16px;border-top:1px solid var(--border);padding-top:16px">
|
||
<h3 style="font-size:13px;font-weight:600;margin-bottom:10px" id="user-form-title">Add User</h3>
|
||
<div class="form-row">
|
||
<div class="form-group required">
|
||
<label for="uf-username">Username</label>
|
||
<input type="text" id="uf-username" autocapitalize="none">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="uf-email">Email <span class="field-hint">(for password reset)</span></label>
|
||
<input type="email" id="uf-email" autocomplete="email">
|
||
</div>
|
||
<div class="form-group required">
|
||
<label for="uf-password">Password <span class="field-hint" id="uf-password-hint">(min 10 chars, include a digit or symbol)</span></label>
|
||
<input type="password" id="uf-password" autocomplete="new-password">
|
||
</div>
|
||
<div class="form-group required">
|
||
<label for="uf-role">Role</label>
|
||
<select id="uf-role">
|
||
<option value="viewer">Viewer</option>
|
||
<option value="editor">Editor</option>
|
||
<option value="admin">Admin</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-group" id="uf-accounts-group">
|
||
<label>Account Access <span class="field-hint">(admins see all — no selection needed)</span></label>
|
||
<div id="uf-accounts-checkboxes" class="account-checkboxes"></div>
|
||
</div>
|
||
<div id="user-form-error" class="wizard-error" hidden></div>
|
||
<div style="display:flex;gap:8px;margin-top:8px">
|
||
<button id="btn-save-user" class="btn-primary">Add User</button>
|
||
<button id="btn-cancel-user-edit" class="btn-ghost" hidden>Cancel</button>
|
||
</div>
|
||
</div>
|
||
<!-- SMTP settings (admin only) -->
|
||
<div id="smtp-settings-section" style="margin-top:16px;border-top:1px solid var(--border);padding-top:16px">
|
||
<h3 style="font-size:13px;font-weight:600;margin-bottom:10px">Email Settings (SMTP)</h3>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="smtp-host">SMTP Host</label>
|
||
<input type="text" id="smtp-host" placeholder="smtp.example.com">
|
||
</div>
|
||
<div class="form-group" style="max-width:90px">
|
||
<label for="smtp-port">Port</label>
|
||
<input type="number" id="smtp-port" value="587" min="1" max="65535">
|
||
</div>
|
||
<div class="form-group" style="max-width:140px">
|
||
<label for="smtp-secure">Encryption</label>
|
||
<select id="smtp-secure">
|
||
<option value="0">STARTTLS (587)</option>
|
||
<option value="1">SSL/TLS (465)</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="smtp-user">Username</label>
|
||
<input type="text" id="smtp-user" autocomplete="off">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="smtp-pass">Password <span class="field-hint" id="smtp-pass-hint"></span></label>
|
||
<input type="password" id="smtp-pass" autocomplete="new-password">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="smtp-from">From Address</label>
|
||
<input type="email" id="smtp-from" placeholder="ezcheck@example.com">
|
||
</div>
|
||
</div>
|
||
<div id="smtp-error" class="wizard-error" hidden></div>
|
||
<div id="smtp-success" class="import-result" hidden></div>
|
||
<button id="btn-save-smtp" class="btn-secondary" style="margin-top:8px">Save Email Settings</button>
|
||
</div>
|
||
<!-- Change own password -->
|
||
<div style="margin-top:16px;border-top:1px solid var(--border);padding-top:16px">
|
||
<h3 style="font-size:13px;font-weight:600;margin-bottom:10px">Change My Password</h3>
|
||
<div class="form-row">
|
||
<div class="form-group">
|
||
<label for="cp-current">Current Password</label>
|
||
<input type="password" id="cp-current" autocomplete="current-password">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="cp-new">New Password <span class="field-hint">(min 10 chars, include a digit or symbol)</span></label>
|
||
<input type="password" id="cp-new" autocomplete="new-password">
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="cp-confirm">Confirm New</label>
|
||
<input type="password" id="cp-confirm" autocomplete="new-password">
|
||
</div>
|
||
</div>
|
||
<div id="cp-error" class="wizard-error" hidden></div>
|
||
<div id="cp-success" class="import-result" hidden>Password changed.</div>
|
||
<button id="btn-change-password" class="btn-secondary" style="margin-top:8px">Change Password</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Layout Editor Modal -->
|
||
<div id="layout-editor-overlay" class="modal-overlay"></div>
|
||
<div id="layout-editor-modal" class="modal modal-layout-editor" role="dialog" aria-labelledby="layout-editor-title">
|
||
<div class="modal-header">
|
||
<h2 id="layout-editor-title">Layout Editor</h2>
|
||
<button id="btn-close-layout-editor" class="btn-icon" title="Close">×</button>
|
||
</div>
|
||
<div class="modal-body layout-editor-body">
|
||
<div id="layout-canvas-container"><!-- SVG rendered by JS --></div>
|
||
<div id="layout-sidebar">
|
||
<div class="form-group">
|
||
<label for="layout-field-select">Field</label>
|
||
<select id="layout-field-select" style="font-size:12px"></select>
|
||
</div>
|
||
<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer">
|
||
<input type="checkbox" id="layout-field-visible"> Visible
|
||
</label>
|
||
<div class="form-group">
|
||
<label for="layout-field-x">X Position</label>
|
||
<div style="display:flex;align-items:center;gap:6px">
|
||
<input type="number" id="layout-field-x" step="0.0625" min="0" max="8.5" style="width:72px">
|
||
<span id="layout-field-x-frac" style="font-size:11px;color:var(--text-muted)"></span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="layout-field-y">Y Position</label>
|
||
<div style="display:flex;align-items:center;gap:6px">
|
||
<input type="number" id="layout-field-y" step="0.0625" min="0" max="3.5" style="width:72px">
|
||
<span id="layout-field-y-frac" style="font-size:11px;color:var(--text-muted)"></span>
|
||
</div>
|
||
</div>
|
||
<div id="layout-end-pos-group" hidden>
|
||
<div class="form-group">
|
||
<label for="layout-field-x2">End X</label>
|
||
<div style="display:flex;align-items:center;gap:6px">
|
||
<input type="number" id="layout-field-x2" step="0.0625" min="0" max="8.5" style="width:72px">
|
||
<span id="layout-field-x2-frac" style="font-size:11px;color:var(--text-muted)"></span>
|
||
</div>
|
||
</div>
|
||
<div class="form-group">
|
||
<label for="layout-field-y2">End Y</label>
|
||
<div style="display:flex;align-items:center;gap:6px">
|
||
<input type="number" id="layout-field-y2" step="0.0625" min="0" max="3.5" style="width:72px">
|
||
<span id="layout-field-y2-frac" style="font-size:11px;color:var(--text-muted)"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<div style="font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--text-muted);margin-bottom:6px">Nudge ¹⁄₁₆"</div>
|
||
<div style="display:grid;grid-template-columns:repeat(3,28px);grid-template-rows:repeat(3,28px);gap:3px;justify-content:center">
|
||
<span></span>
|
||
<button id="nudge-up" class="btn-sm btn-secondary" style="padding:0;text-align:center">↑</button>
|
||
<span></span>
|
||
<button id="nudge-left" class="btn-sm btn-secondary" style="padding:0;text-align:center">←</button>
|
||
<span></span>
|
||
<button id="nudge-right" class="btn-sm btn-secondary" style="padding:0;text-align:center">→</button>
|
||
<span></span>
|
||
<button id="nudge-down" class="btn-sm btn-secondary" style="padding:0;text-align:center">↓</button>
|
||
<span></span>
|
||
</div>
|
||
</div>
|
||
<div id="layout-save-status" style="font-size:11px;color:var(--text-muted);text-align:center;min-height:14px"></div>
|
||
<div style="margin-top:auto;padding-top:8px;border-top:1px solid var(--border)">
|
||
<button id="btn-layout-reset" class="btn-secondary btn-sm" style="width:100%;font-size:11px" data-admin-only>↺ Reset to Default</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="/js/app.js"></script>
|
||
</body>
|
||
</html>
|