InvoiceParser/wwwroot/index.html
2026-09-16 13:09:19 -07:00

324 lines
15 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Invoice Parser</title>
<link rel="stylesheet" href="css/style.css">
<script src="js/jquery-3.5.1.min.js"></script>
<script src="js/app.js"></script>
</head>
<body>
<h1>
Invoice Parser <span id="pageMode" style="color:#aaa;font-size:12px;font-weight:normal"></span>
<a href="index.html">+ New Invoice</a>
<a href="pending.html">Pending</a>
<a href="archive.html">Archive</a>
<button id="btnViewParsed" type="button" title="Compare what the parser read against the products it matched">Parsed vs matched</button>
<button id="btnTogglePreview" type="button" title="Hide the invoice preview to give the form the full window">&laquo; Hide preview</button>
</h1>
<!-- ============ Review layout (?pending=<id>) ============ -->
<div id="layout">
<div id="leftPanel">
<div id="uploadPrompt" style="display:none"></div>
<div id="previewToolbar">
<button id="btnZoomOut" type="button" title="Zoom out">&minus;</button>
<span id="zoomLevel">100%</span>
<button id="btnZoomIn" type="button" title="Zoom in">+</button>
<button id="btnZoomFit" type="button" title="Fit the page to the panel width">Fit</button>
<span class="field-note">Ctrl + scroll to zoom</span>
</div>
<div id="canvasWrapper"></div>
<div id="scanSection" style="display:none;align-items:center;gap:10px;padding:4px 0">
<button id="btnScan">Scan Invoice</button>
</div>
</div>
<div id="splitter" title="Drag to resize the preview"></div>
<div id="rightPanel">
<div id="statusBar"></div>
<div id="reviewSection">
<div class="form-section header-section">
<h2>Invoice Header</h2>
<div class="hdr-grid">
<div class="hdr-field">
<label for="vendorSelect">Vendor</label>
<div class="hdr-ctl">
<select id="vendorSelect" name="vendor_id" style="width:210px">
<option value="">-- Select Vendor --</option>
</select>
<span id="vendorBadge" class="match-badge" style="display:none"></span>
</div>
<small class="parsed-hint" id="hintVendor" style="display:none"></small>
</div>
<div class="hdr-field">
<label for="invoiceNumber">Invoice #</label>
<div class="hdr-ctl">
<input id="invoiceNumber" name="invoice_number" style="width:130px" maxlength="14">
</div>
<small class="parsed-hint" id="hintInvoiceNumber" style="display:none"></small>
</div>
<div class="hdr-field">
<label for="invoiceDate">Invoice Date</label>
<div class="hdr-ctl">
<input id="invoiceDate" name="invoice_date" type="date" style="width:135px">
</div>
<small class="parsed-hint" id="hintInvoiceDate" style="display:none"></small>
</div>
<div class="hdr-field">
<label for="poSelect">PO Number</label>
<div class="hdr-ctl">
<select id="poSelect" style="width:230px">
<option value="">No PO (auto-assign)</option>
</select>
</div>
</div>
<div class="hdr-field">
<label for="invoiceTotal">Total</label>
<div class="hdr-ctl">
<input id="invoiceTotal" name="vendor_total" type="number" step="0.01" style="width:100px">
</div>
<small class="parsed-hint" id="hintInvoiceTotal" style="display:none"></small>
</div>
</div>
</div>
<div class="form-section items-section">
<h2>Line Items <span id="lineSummary" class="po-summary"></span><span id="poCompareSummary" class="po-summary"></span>
<span class="h2-actions">
<button id="btnAddLine" type="button" title="Add a line item the parser missed">+ Add Line</button>
<button id="btnAcceptAll" type="button">Accept All</button>
</span>
</h2>
<div class="table-scroll">
<table id="lineItemsTable">
<colgroup>
<col class="c-upc">
<col class="c-desc">
<col class="c-cert">
<col class="c-po">
<col class="c-po">
<col class="c-pocost">
<col class="c-qty">
<col class="c-cost">
<col class="c-pack">
<col class="c-unitcost">
<col class="c-retail">
<col class="c-gm">
<col class="c-total">
<col class="c-size">
<col class="c-uom">
<col class="c-match">
<col class="c-del">
</colgroup>
<thead>
<tr>
<th>UPC</th>
<th>Description</th>
<th>Cert Code</th>
<th title="PO cases">PO Cs</th>
<th title="PO pieces">PO Pc</th>
<th title="PO cost">PO $</th>
<th title="Invoice quantity">Qty</th>
<th title="Cost of one case as billed on the invoice">Case Cost</th>
<th title="Units per case from the product file, or from the invoice PACK/SIZE column when the product has none - edit it to fix the margin">Pack</th>
<th title="Case cost divided by pack">Unit Cost</th>
<th title="Shelf retail from the product file">Retail</th>
<th title="Gross margin: (retail - unit cost) / retail">GM %</th>
<th>Total</th>
<th>Size</th>
<th>UOM</th>
<th>Match</th>
<th></th>
</tr>
</thead>
<tbody id="lineItemsBody"></tbody>
</table>
</div>
</div>
<div class="form-section actions-section">
<span class="field-note">Draft auto-saves to Pending</span>
<span id="saveBlockers" class="save-blockers" style="display:none"></span>
<span class="actions-right">
<button id="btnRestoreDefaults" type="button">Restore Defaults</button>
<button id="btnDeleteDraft" type="button">Delete Draft</button>
<button id="btnSave">Save Invoice</button>
</span>
</div>
</div><!-- /reviewSection -->
</div><!-- /rightPanel -->
</div><!-- /layout -->
<!-- ============ Landing dashboard ============ -->
<div id="dashboard">
<div class="dash-hero">
<div id="uploadZone" onclick="document.getElementById('fileInput').click()">
<input type="file" id="fileInput" accept=".pdf,.jpg,.jpeg,.png" multiple>
<span class="up-icon">&#128228;</span>
<label>Scan Invoices</label>
<div id="uploadPromptDash" style="color:#aaa;margin-top:10px;font-size:13px">Drag and drop or click - PDF or image (JPG, PNG). Select multiple to batch upload. Files parse automatically in the background; review them under Pending.</div>
</div>
<div id="dashStatusBar" style="display:none;padding:7px 12px;border-radius:4px;font-size:12px;margin-top:14px"></div>
</div>
<div class="dash-actions">
<a class="dash-btn primary" href="pending.html">Go to Pending Queue</a>
<button class="dash-btn" id="btnParseQueued" type="button">Parse queued now</button>
<a class="dash-btn" href="archive.html">View Archive</a>
</div>
<div class="stats-grid">
<a class="stat-card sc-ready" href="pending.html" title="Parsed and waiting for your review">
<div class="stat-num" id="statReady">-</div>
<div class="stat-label">Ready to review</div>
</a>
<a class="stat-card sc-queued" href="pending.html" title="Uploaded, waiting for the parser">
<div class="stat-num" id="statQueued">-</div>
<div class="stat-label">Queued</div>
<div class="stat-sub" id="statProcessingSub"></div>
</a>
<a class="stat-card sc-progress" href="pending.html" title="Drafts you have started editing">
<div class="stat-num" id="statProgress">-</div>
<div class="stat-label">In progress</div>
</a>
<a class="stat-card sc-error" href="pending.html" title="Background parsing failed - retry from Pending">
<div class="stat-num" id="statError">-</div>
<div class="stat-label">Errors</div>
</a>
<div class="stat-card" title="Uploaded today">
<div class="stat-num" id="statToday">-</div>
<div class="stat-label">Uploaded today</div>
</div>
<a class="stat-card sc-archived" href="archive.html" title="Completed and archived">
<div class="stat-num" id="statArchived">-</div>
<div class="stat-label">Archived</div>
</a>
<a class="stat-card sc-total" href="pending.html" title="All invoices in the pending queue">
<div class="stat-num" id="statTotal">-</div>
<div class="stat-label">Total pending</div>
</a>
<div class="stat-card sc-value" title="Sum of invoice totals across all pending drafts">
<div class="stat-num" id="statValue">-</div>
<div class="stat-label">Pending value</div>
</div>
<div class="stat-card sc-lines" title="Line items across all pending drafts">
<div class="stat-num" id="statLines">-</div>
<div class="stat-label">Pending line items</div>
</div>
</div>
<div class="dash-foot" id="dashUpdated"></div>
</div><!-- /dashboard -->
<!-- Parsed data viewer -->
<div id="parsedModal">
<div class="modal-box parsed-box">
<h2>
Parsed vs matched
<span id="ocrMethod"></span>
<label class="diff-only"><input type="checkbox" id="chkDiffOnly"> differences only</label>
<button id="btnCloseParsed" type="button">Close</button>
</h2>
<div class="parsed-tabs">
<button class="parsed-tab active" type="button" data-pane="paneCompare">Line items</button>
<button class="parsed-tab" type="button" data-pane="paneText">Extracted text</button>
</div>
<div class="parsed-pane" id="paneCompare" style="display:flex">
<div class="field-note">Left is what the parser read off the invoice; right is the product it was matched to. Differences are highlighted. Click a row to jump to it.</div>
<div id="compareScroll">
<table id="compareHeader">
<thead>
<tr><th>Header field</th><th>Parsed</th><th>In use</th></tr>
</thead>
<tbody id="compareHeaderBody"></tbody>
</table>
<table id="compareTable">
<thead>
<tr><th>#</th><th>Parsed from invoice</th><th>Matched product</th><th>Status</th></tr>
</thead>
<tbody id="compareBody"></tbody>
</table>
</div>
</div>
<div class="parsed-pane" id="paneText" style="display:none">
<div class="field-note">Text pulled off the invoice. Edits are kept with the draft and used if you re-scan.</div>
<textarea id="ocrText"></textarea>
</div>
</div>
</div>
<!-- New Product Modal -->
<div id="newProductModal">
<div class="modal-box">
<h2>Create New Product</h2>
<div class="field-row">
<label>UPC <span style="color:#ff8888">*</span></label>
<input id="np_upc" style="width:180px" maxlength="13" placeholder="e.g. 012345678901">
</div>
<div class="field-row">
<label>Description <span style="color:#ff8888">*</span></label>
<input id="np_desc" style="width:220px" maxlength="30">
</div>
<div class="field-row">
<label>Price <span style="color:#ff8888">*</span></label>
<input id="np_price" type="text" style="width:120px" placeholder="2.99 or 2/5.00">
<small class="field-note">plain price, or n/total for group pricing</small>
</div>
<div class="field-row">
<label>Department <span style="color:#ff8888">*</span></label>
<select id="np_dept" class="np-select" style="width:200px">
<option value="">-- Select Department --</option>
</select>
</div>
<div class="field-row">
<label>Tax</label>
<select id="np_tax" class="np-select" style="width:110px">
<option value="0">0 - No</option>
<option value="1">1 - Yes</option>
</select>
<small class="field-note" id="np_flagsNote">from department</small>
</div>
<div class="field-row">
<label>Food Stamp</label>
<select id="np_fs" class="np-select" style="width:110px">
<option value="0">0 - No</option>
<option value="1">1 - Yes</option>
</select>
</div>
<div class="field-row">
<label>Cost</label>
<input id="np_cost" type="number" step="0.01" min="0" style="width:100px">
</div>
<div class="field-row">
<label>Pack</label>
<input id="np_pack" type="number" step="1" min="0" style="width:100px">
</div>
<div class="field-row">
<label>Size</label>
<input id="np_size" style="width:100px" maxlength="9">
</div>
<div class="field-row">
<label>Item Code</label>
<input id="np_code" style="width:140px" maxlength="15" placeholder="cert / item code">
</div>
<div style="margin-top:14px;display:flex;gap:8px">
<button id="btnCreateProduct" style="background:#6699CC;color:#111;border:none;padding:7px 18px;border-radius:4px;font-size:13px;font-weight:bold;cursor:pointer">Create Product</button>
<button id="btnCloseModal" style="background:#555;color:#eee;border:none;padding:7px 16px;border-radius:4px;font-size:13px;cursor:pointer">Cancel</button>
</div>
<div id="newProductStatus" style="display:none;margin-top:10px;font-size:11px"></div>
</div>
</div>
</body>
</html>