Files
steve 0b21f4ea3c feat(layout): add preview PDF button to layout editor
Generates a 3-up PDF with dummy check data (no real checks touched) so the
layout can be proofed without printing live checks.

Closes #12
2026-05-02 17:21:41 -06:00

900 lines
42 KiB
HTML
Raw Permalink 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>
<!-- 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 &amp; 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 id="oidc-login-section" hidden>
<div class="login-divider"><span>or</span></div>
<a id="btn-oidc-login" href="/api/auth/oidc/authorize" class="btn-oidc">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"/><polyline points="10 17 15 12 10 7"/><line x1="15" y1="12" x2="3" y2="12"/></svg>
<span id="btn-oidc-login-label">Sign in with SSO</span>
</a>
</div>
<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>
<!-- Settings page (full-page, hidden by default) -->
<div id="settings-page" class="settings-page" hidden>
<header class="settings-header">
<div class="settings-header-left">
<a href="#" id="settings-back-link" class="settings-back-link">&larr; Back</a>
</div>
<div class="settings-header-right">
<span id="settings-username" class="header-username"></span>
<button id="btn-settings-logout" class="btn-header-icon" title="Sign out"></button>
</div>
</header>
<div class="settings-layout">
<nav class="settings-sidebar">
<div class="settings-sidebar-title">Settings</div>
<div class="settings-nav-group" data-admin-only>
<div class="settings-nav-label">Administration</div>
<a href="#settings/users" class="settings-nav-item" data-settings-tab="users">Users</a>
<a href="#settings/smtp" class="settings-nav-item" data-settings-tab="smtp">Email (SMTP)</a>
</div>
<div class="settings-nav-group">
<div class="settings-nav-label">Account</div>
<a href="#settings/password" class="settings-nav-item" data-settings-tab="password">Password</a>
<a href="#settings/sso" class="settings-nav-item" data-settings-tab="sso">Single Sign-On</a>
</div>
</nav>
<main class="settings-content">
<!-- Users panel (admin only) -->
<div id="settings-panel-users" class="settings-panel" hidden>
<h2>Users</h2>
<p class="settings-desc">Manage user accounts and access permissions.</p>
<div class="settings-section">
<div id="users-list"></div>
</div>
<div class="settings-section" id="user-form-section">
<h3 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="uf-oidc-group" class="uf-oidc-section" hidden>
<h4>Single Sign-On Identity</h4>
<div class="form-row">
<div class="form-group">
<label for="uf-oidc-sub">OIDC Subject <span class="field-hint">(sub claim from provider)</span></label>
<input type="text" id="uf-oidc-sub" autocomplete="off">
</div>
<div class="form-group">
<label for="uf-oidc-issuer">OIDC Issuer <span class="field-hint">(provider URL)</span></label>
<input type="text" id="uf-oidc-issuer" autocomplete="off">
</div>
</div>
</div>
<div id="user-form-error" class="wizard-error" hidden></div>
<div style="display:flex;gap:8px;margin-top:12px">
<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>
</div>
<!-- SMTP panel (admin only) -->
<div id="settings-panel-smtp" class="settings-panel" hidden>
<h2>Email Settings</h2>
<p class="settings-desc">Configure SMTP for password reset emails.</p>
<div class="settings-section" id="smtp-settings-section">
<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:100px">
<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:160px">
<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:12px">Save Email Settings</button>
</div>
</div>
<!-- Password panel (all users) -->
<div id="settings-panel-password" class="settings-panel" hidden>
<h2>Change Password</h2>
<p class="settings-desc">Update your login password.</p>
<div class="settings-section">
<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:12px">Change Password</button>
</div>
</div>
<!-- SSO panel (all users, shown when OIDC enabled) -->
<div id="settings-panel-sso" class="settings-panel" hidden>
<h2>Single Sign-On</h2>
<p class="settings-desc">Link your account to an external identity provider.</p>
<div class="settings-section" id="oidc-link-section">
<p id="oidc-link-status" class="settings-desc" style="margin-bottom:12px"></p>
<a id="btn-oidc-link" href="/api/auth/oidc/link" class="btn-secondary" style="display:inline-block;text-decoration:none">Link My Account</a>
<button id="btn-oidc-unlink" class="btn-ghost" style="margin-left:8px" hidden>Unlink</button>
</div>
</div>
</main>
</div>
</div>
<div id="main-app">
<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" style="cursor:pointer" title="Account settings"></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 &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>
<!-- 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&quot; 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>
<p class="settings-section-label">Check Position</p>
<div class="form-group">
<label for="as-check-position">Print checks in</label>
<select id="as-check-position" name="check_position">
<option value="3-per-page">All 3 slots (3 per page)</option>
<option value="top">Top slot only</option>
<option value="middle">Middle slot only</option>
<option value="bottom">Bottom slot only</option>
</select>
<span class="field-hint">Choose which slot(s) on the page to print checks in.</span>
</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>
</div><!-- /main-app -->
<!-- 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">
<!-- Canvas with rulers -->
<div class="layout-canvas-area">
<div class="layout-ruler-corner"></div>
<div id="layout-ruler-top"></div>
<div id="layout-ruler-left"></div>
<div id="layout-canvas-container"><!-- SVG rendered by JS --></div>
</div>
<!-- Controls strip -->
<div class="layout-controls">
<div class="layout-coord" style="gap:6px">
<label for="layout-field-select" style="font-size:11px;font-weight:600;text-transform:uppercase;color:var(--text-muted)">Field</label>
<select id="layout-field-select" style="font-size:12px;max-width:200px"></select>
</div>
<label style="display:flex;align-items:center;gap:5px;font-size:12px;cursor:pointer;white-space:nowrap">
<input type="checkbox" id="layout-field-visible"> Visible
</label>
<div style="width:1px;height:24px;background:var(--border)"></div>
<div class="layout-coord">
<label for="layout-field-x">X</label>
<input type="number" id="layout-field-x" step="0.0625" min="0" max="8.5">
<span class="frac" id="layout-field-x-frac"></span>
</div>
<div class="layout-coord">
<label for="layout-field-y">Y</label>
<input type="number" id="layout-field-y" step="0.0625" min="0" max="3.5">
<span class="frac" id="layout-field-y-frac"></span>
</div>
<div id="layout-end-pos-group" style="display:contents" hidden>
<div class="layout-coord">
<label for="layout-field-x2">End X</label>
<input type="number" id="layout-field-x2" step="0.0625" min="0" max="8.5">
<span class="frac" id="layout-field-x2-frac"></span>
</div>
<div class="layout-coord">
<label for="layout-field-y2">End Y</label>
<input type="number" id="layout-field-y2" step="0.0625" min="0" max="3.5">
<span class="frac" id="layout-field-y2-frac"></span>
</div>
</div>
<div style="width:1px;height:24px;background:var(--border)"></div>
<!-- Nudge cross -->
<div style="display:flex;flex-direction:column;align-items:center;gap:2px">
<div style="font-size:10px;text-transform:uppercase;color:var(--text-muted);letter-spacing:.04em">¹⁄₁₆"</div>
<div style="display:grid;grid-template-columns:repeat(3,22px);grid-template-rows:repeat(3,22px);gap:2px">
<span></span><button id="nudge-up" class="btn-sm btn-secondary" style="padding:0;display:flex;align-items:center;justify-content:center"></button><span></span>
<button id="nudge-left" class="btn-sm btn-secondary" style="padding:0;display:flex;align-items:center;justify-content:center"></button>
<span></span>
<button id="nudge-right" class="btn-sm btn-secondary" style="padding:0;display:flex;align-items:center;justify-content:center"></button>
<span></span><button id="nudge-down" class="btn-sm btn-secondary" style="padding:0;display:flex;align-items:center;justify-content:center"></button><span></span>
</div>
</div>
<div id="layout-save-status" style="font-size:11px;color:var(--text-muted);min-width:56px"></div>
<div style="margin-left:auto;display:flex;gap:8px;align-items:center">
<button id="btn-layout-preview" class="btn-secondary btn-sm">⎙ Preview PDF</button>
<button id="btn-layout-reset" class="btn-secondary btn-sm" data-admin-only>↺ Reset to Default</button>
</div>
</div>
</div>
</div>
<script src="/js/app.js"></script>
</body>
</html>