pf_app/public/index.html
Paul Trowbridge 6d8b052eb6 Add date offset to baseline — project actuals into forecast period
The baseline operation now accepts a date_offset interval (e.g. "1 year",
"6 months") and applies it to every date when inserting rows, shifting
historical actuals into the target forecast period.

SQL: {date_col} + '{{date_offset}}'::interval)::date at insert time.
Route: defaults to '0 days' if omitted so existing calls are unaffected.
UI: year/month spinners with a live before→after month chip preview so
the projected landing period is visible before submitting.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 12:48:28 -04:00

228 lines
11 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>Pivot Forecast</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community@31.0.0/styles/ag-grid.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ag-grid-community@31.0.0/styles/ag-theme-alpine.css">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div id="app">
<!-- Sidebar -->
<nav id="sidebar">
<div class="sidebar-brand">Pivot Forecast</div>
<ul class="nav-links">
<li data-view="sources" class="active">Sources</li>
<li data-view="versions">Versions</li>
<li data-view="forecast">Forecast</li>
<li data-view="log">Log</li>
</ul>
<div class="sidebar-context">
<div id="ctx-source" class="ctx-item hidden">
<span class="ctx-label">Source</span>
<span id="ctx-source-name"></span>
</div>
<div id="ctx-version" class="ctx-item hidden">
<span class="ctx-label">Version</span>
<span id="ctx-version-name"></span>
<span id="ctx-version-status" class="status-badge"></span>
</div>
</div>
<div class="sidebar-user">
<label>User</label>
<input type="text" id="input-pf-user" placeholder="your name" />
</div>
</nav>
<!-- Main content -->
<main id="content">
<div id="status-bar" class="hidden"></div>
<!-- ===== SOURCES VIEW ===== -->
<div id="view-sources" class="view active">
<div class="two-col-layout">
<div class="panel">
<div class="panel-header">
<span>Database Tables</span>
<div class="header-actions">
<button id="btn-register" class="btn btn-primary hidden">Register Table</button>
</div>
</div>
<div id="tables-grid" class="ag-theme-alpine grid-fill"></div>
</div>
<div class="panel">
<div class="panel-header">
<span id="right-panel-title">Registered Sources</span>
<div class="header-actions">
<button id="btn-back-sources" class="btn hidden">← Sources</button>
<button id="btn-save-cols" class="btn hidden">Save Columns</button>
<button id="btn-generate-sql" class="btn btn-primary hidden">Generate SQL</button>
</div>
</div>
<div id="sources-list-grid" class="ag-theme-alpine grid-fill"></div>
<div id="col-meta-grid" class="ag-theme-alpine grid-fill hidden"></div>
</div>
</div>
</div>
<!-- ===== VERSIONS VIEW ===== -->
<div id="view-versions" class="view hidden">
<div class="view-toolbar">
<span id="versions-source-label"></span>
<button id="btn-new-version" class="btn btn-primary">New Version</button>
</div>
<div id="new-version-form" class="inline-form hidden">
<h3>Create Version</h3>
<div class="form-row">
<label>Name<input type="text" id="ver-name" placeholder="e.g. FY2025 v1" /></label>
<label>Description<input type="text" id="ver-desc" placeholder="optional" /></label>
</div>
<div class="form-actions">
<button id="btn-create-version" class="btn btn-primary">Create</button>
<button id="btn-cancel-version" class="btn">Cancel</button>
</div>
</div>
<div id="versions-grid" class="ag-theme-alpine"></div>
<div id="version-actions" class="version-actions hidden">
<span id="version-actions-label"></span>
<button class="btn btn-primary" id="vbtn-forecast">Open Forecast</button>
<button class="btn" id="vbtn-baseline">Load Baseline</button>
<button class="btn" id="vbtn-reference">Load Reference</button>
<button class="btn" id="vbtn-toggle">Close Version</button>
<button class="btn btn-danger" id="vbtn-delete">Delete</button>
</div>
</div>
<!-- ===== FORECAST VIEW ===== -->
<div id="view-forecast" class="view hidden">
<div class="forecast-toolbar">
<span id="forecast-label">No version selected</span>
<button id="btn-forecast-refresh" class="btn">Refresh</button>
<button id="btn-expand-all" class="btn">Expand All</button>
<button id="btn-collapse-all" class="btn">Collapse All</button>
</div>
<div class="forecast-layout">
<div id="pivot-panel">
<div id="pivot-grid" class="ag-theme-alpine"></div>
</div>
<div id="operation-panel">
<div class="op-section">
<div class="op-title">Slice</div>
<div id="slice-display">
<span class="op-hint">Click a row to select a slice</span>
</div>
<button id="btn-clear-slice" class="btn btn-sm hidden">Clear</button>
</div>
<div id="op-forms-area" class="hidden">
<div class="op-tabs">
<button class="op-tab active" data-op="scale">Scale</button>
<button class="op-tab" data-op="recode">Recode</button>
<button class="op-tab" data-op="clone">Clone</button>
</div>
<!-- Scale -->
<div id="op-scale" class="op-form">
<label>Value Δ<input type="number" id="scale-value-incr" step="any" placeholder="0" /></label>
<label>Units Δ<input type="number" id="scale-units-incr" step="any" placeholder="0" /></label>
<label class="label-inline"><input type="checkbox" id="scale-pct" /> Treat as %</label>
<label>Note<input type="text" id="scale-note" placeholder="optional" /></label>
<button id="btn-submit-scale" class="btn btn-primary">Apply Scale</button>
</div>
<!-- Recode -->
<div id="op-recode" class="op-form hidden">
<div class="op-hint">Enter new values for dimensions to replace:</div>
<div id="recode-fields"></div>
<label>Note<input type="text" id="recode-note" placeholder="optional" /></label>
<button id="btn-submit-recode" class="btn btn-primary">Apply Recode</button>
</div>
<!-- Clone -->
<div id="op-clone" class="op-form hidden">
<div class="op-hint">Override dimension values on cloned rows:</div>
<div id="clone-fields"></div>
<label>Scale Factor<input type="number" id="clone-scale" step="any" value="1" /></label>
<label>Note<input type="text" id="clone-note" placeholder="optional" /></label>
<button id="btn-submit-clone" class="btn btn-primary">Apply Clone</button>
</div>
</div>
</div>
</div>
</div>
<!-- ===== LOG VIEW ===== -->
<div id="view-log" class="view hidden">
<div id="log-grid" class="ag-theme-alpine grid-fill"></div>
</div>
</main>
</div>
<!-- Load baseline / reference modal -->
<div id="load-data-modal" class="modal-overlay hidden">
<div class="modal load-data-modal">
<div class="modal-header">
<span id="load-data-title">Load Baseline</span>
<button id="btn-load-close" class="btn-icon">×</button>
</div>
<div id="load-data-body">
<div class="load-form-fields">
<label>Date From<input type="date" id="load-date-from" /></label>
<label>Date To<input type="date" id="load-date-to" /></label>
<div id="load-offset-fields">
<div class="load-offset-row">
<label>Offset Years<input type="number" id="load-offset-years" min="0" value="0" /></label>
<label>Offset Months<input type="number" id="load-offset-months" min="0" value="0" /></label>
</div>
</div>
<label>Note<input type="text" id="load-note" placeholder="optional" /></label>
</div>
<div id="load-date-preview" class="load-date-preview hidden">
<!-- reference: single chip list -->
<div id="load-preview-simple">
<div class="load-preview-label"></div>
<div id="load-date-chips" class="date-chips"></div>
</div>
<!-- baseline: before → after -->
<div id="load-preview-offset" class="hidden">
<div class="load-preview-columns">
<div class="load-preview-col">
<div class="load-preview-label">Source</div>
<div id="load-chips-source" class="date-chips"></div>
</div>
<div class="load-preview-arrow"></div>
<div class="load-preview-col">
<div class="load-preview-label">Projected</div>
<div id="load-chips-projected" class="date-chips"></div>
</div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button id="btn-load-submit" class="btn btn-primary">Load</button>
<button id="btn-load-cancel" class="btn">Cancel</button>
</div>
</div>
</div>
<!-- Table preview modal -->
<div id="modal-overlay" class="modal-overlay hidden">
<div class="modal">
<div class="modal-header">
<span id="modal-title">Preview</span>
<button id="modal-close" class="btn-icon">×</button>
</div>
<div id="modal-body"></div>
<div class="modal-footer">
<button id="btn-modal-register" class="btn btn-primary">Register This Table</button>
<button id="btn-modal-close" class="btn">Close</button>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/ag-grid-enterprise@31.0.0/dist/ag-grid-enterprise.min.js"></script>
<script src="app.js"></script>
</body>
</html>