pf_app/public/index.html
Paul Trowbridge 1df37a5ff1 Refactor sources UI, rename pf_ system cols, replace filter builder with raw SQL
- Sources page: left column with stacked DB tables + registered sources panels,
  right column as full-height column mapping workbench
- Add compact table search, column search, table preview button, delete source button
- Rename fc_table system columns to pf_ prefix (pf_id, pf_iter, pf_logid,
  pf_created_at) to avoid collisions with source table columns like 'id'
- Remove 'filter' col_meta role — any non-ignore column usable in baseline filters
- Replace structured filter row builder with free-form SQL WHERE clause textarea
  and clickable column chips for insertion; fully flexible AND/OR logic
- Baseline segment cards now display raw WHERE clause text + offset

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-03 00:47:57 -04:00

290 lines
15 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="baseline">Baseline</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="sources-layout">
<!-- Left column: two stacked panels -->
<div class="sources-left-col">
<div class="panel sources-tables-panel">
<div class="panel-header">
<span>Database Tables</span>
<div class="header-actions">
<button id="btn-preview" class="btn btn-sm hidden">Preview</button>
<button id="btn-register" class="btn btn-primary btn-sm hidden">Register</button>
</div>
</div>
<div class="tables-search-wrap">
<input type="text" id="tables-search" placeholder="Search…" />
</div>
<div id="tables-grid" class="ag-theme-alpine tables-grid"></div>
</div>
<div class="panel sources-list-panel">
<div class="panel-header">
<span>Registered Sources</span>
<button id="btn-delete-source" class="btn btn-danger btn-sm hidden">Delete</button>
</div>
<div id="sources-list-grid" class="ag-theme-alpine grid-fill"></div>
</div>
</div>
<!-- Right column: column mapping workbench -->
<div class="panel sources-mapping-panel">
<div class="panel-header">
<span id="right-panel-title">Select a source to map columns</span>
<div class="header-actions">
<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 class="tables-search-wrap">
<input type="text" id="cols-search" placeholder="Search columns…" />
</div>
<div id="col-meta-grid" class="ag-theme-alpine grid-fill"></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>
<!-- ===== BASELINE WORKBENCH VIEW ===== -->
<div id="view-baseline" class="view hidden">
<div class="workbench-toolbar">
<span id="baseline-label">No version selected</span>
<button id="btn-clear-baseline" class="btn btn-danger">Clear Baseline</button>
</div>
<div class="baseline-layout">
<div class="baseline-form-panel">
<div class="panel-section-title">Add Segment</div>
<div class="baseline-form">
<label class="baseline-field-label">Description
<input type="text" id="seg-description" placeholder="e.g. All orders FY2024" />
</label>
<div class="offset-row">
<label class="baseline-field-label">Offset Type
<select id="seg-offset-type">
<option value="">— none —</option>
<option value="year">Year</option>
<option value="month">Month</option>
<option value="week">Week</option>
<option value="day">Day</option>
</select>
</label>
<label class="baseline-field-label">Offset Value
<input type="number" id="seg-offset-value" min="0" value="0" />
</label>
</div>
<div class="where-section">
<div class="filter-section-label">WHERE clause</div>
<div id="seg-col-chips" class="col-chips"></div>
<textarea id="seg-where" class="where-textarea" rows="4" placeholder="e.g. order_date BETWEEN '2024-01-01' AND '2024-12-31' AND region = 'East'"></textarea>
</div>
<div id="seg-timeline" class="timeline-preview hidden"></div>
<button id="btn-load-segment" class="btn btn-primary">Load Segment</button>
</div>
</div>
<div class="baseline-segments-panel">
<div class="panel-section-title">Loaded Segments</div>
<div id="baseline-segments-list">
<div class="segments-empty">No segments loaded yet.</div>
</div>
</div>
</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>