From dbbe0bae66203b366a246b746da7603e6674efd4 Mon Sep 17 00:00:00 2001 From: AnamarijaV Date: Tue, 17 Mar 2026 12:49:08 -0400 Subject: [PATCH] Initial scaffold: docs, data manifest, and static dashboard --- README.md | 30 +++++++++++ app/app.js | 107 ++++++++++++++++++++++++++++++++++++++++ app/index.html | 42 ++++++++++++++++ app/styles.css | 74 +++++++++++++++++++++++++++ data/manifest.json | 3 ++ docs/data-structure.md | 13 +++++ docs/kpi-definitions.md | 8 +++ docs/migration-plan.md | 22 +++++++++ 8 files changed, 299 insertions(+) create mode 100644 README.md create mode 100644 app/app.js create mode 100644 app/index.html create mode 100644 app/styles.css create mode 100644 data/manifest.json create mode 100644 docs/data-structure.md create mode 100644 docs/kpi-definitions.md create mode 100644 docs/migration-plan.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..02a4ffa --- /dev/null +++ b/README.md @@ -0,0 +1,30 @@ +# Power BI Dashboard Migration + +Goal: Recreate the existing Power BI report as a custom dashboard app using the provided CSV/DAX exports as the source of truth. + +## Project Goals +1. Recreate report visuals and KPIs exactly (no approximation). +2. Match all KPI values against source data. +3. Build a dashboard UI with filters, charts, and tables. +4. Commit after each step. + +## Repo Structure +``` +app/ # Simple dashboard app (static for now) +data/ # Local CSVs + manifest +/docs/ # Migration docs +``` + +## Getting Started (Local) +Open `app/index.html` in a local static server (CSV fetch requires HTTP): + +```bash +python -m http.server 8080 +``` + +Then visit: http://localhost:8080/app/ + +## Next Steps +- Add CSV exports to `/data` and update `data/manifest.json`. +- Define KPI formulas in `docs/kpi-definitions.md`. +- Implement calculations in `app/app.js`. diff --git a/app/app.js b/app/app.js new file mode 100644 index 0000000..35acee1 --- /dev/null +++ b/app/app.js @@ -0,0 +1,107 @@ +async function loadManifest() { + const res = await fetch('../data/manifest.json'); + if (!res.ok) throw new Error('Failed to load manifest.json'); + return res.json(); +} + +function parseCSV(text) { + const lines = text.trim().split(/\r?\n/); + if (!lines.length) return { headers: [], rows: [] }; + const headers = lines[0].split(',').map(h => h.trim()); + const rows = lines.slice(1).map(line => { + const cols = []; + let current = ''; + let inQuotes = false; + for (let i = 0; i < line.length; i++) { + const ch = line[i]; + if (ch === '"') { + if (inQuotes && line[i + 1] === '"') { + current += '"'; + i++; + } else { + inQuotes = !inQuotes; + } + } else if (ch === ',' && !inQuotes) { + cols.push(current); + current = ''; + } else { + current += ch; + } + } + cols.push(current); + return cols; + }); + return { headers, rows }; +} + +function renderCSVList(files) { + const list = document.getElementById('csv-list'); + list.innerHTML = ''; + if (!files.length) { + list.textContent = 'No CSV files listed in manifest.json yet.'; + return; + } + files.forEach(f => { + const pill = document.createElement('div'); + pill.className = 'csv-pill'; + pill.textContent = f; + list.appendChild(pill); + }); +} + +function renderPreview(headers, rows) { + const preview = document.getElementById('preview'); + if (!headers.length) { + preview.textContent = 'No data loaded yet.'; + return; + } + const table = document.createElement('table'); + table.className = 'table'; + const thead = document.createElement('thead'); + const trh = document.createElement('tr'); + headers.forEach(h => { + const th = document.createElement('th'); + th.textContent = h; + trh.appendChild(th); + }); + thead.appendChild(trh); + table.appendChild(thead); + + const tbody = document.createElement('tbody'); + rows.slice(0, 10).forEach(r => { + const tr = document.createElement('tr'); + r.forEach(cell => { + const td = document.createElement('td'); + td.textContent = cell; + tr.appendChild(td); + }); + tbody.appendChild(tr); + }); + table.appendChild(tbody); + + preview.innerHTML = ''; + preview.appendChild(table); +} + +async function loadFirstCSV(files) { + if (!files.length) return; + const res = await fetch(`../data/${files[0]}`); + if (!res.ok) throw new Error(`Failed to load ${files[0]}`); + const text = await res.text(); + const { headers, rows } = parseCSV(text); + renderPreview(headers, rows); +} + +async function init() { + try { + const manifest = await loadManifest(); + const files = manifest.csvFiles || []; + renderCSVList(files); + await loadFirstCSV(files); + } catch (err) { + document.getElementById('csv-list').textContent = err.message; + console.error(err); + } +} + +init(); diff --git a/app/index.html b/app/index.html new file mode 100644 index 0000000..73f803f --- /dev/null +++ b/app/index.html @@ -0,0 +1,42 @@ + + + + + + Power BI Dashboard Migration + + + +
+

Power BI Dashboard Migration

+

Loading local CSVs (no database yet)

+
+ +
+
+
KPI 1
+
+
+
+
KPI 2
+
+
+
+
KPI 3
+
+
+
+ +
+

Loaded CSVs

+
Loading…
+
+ +
+

Preview (first 10 rows)

+
No data loaded yet.
+
+ + + + diff --git a/app/styles.css b/app/styles.css new file mode 100644 index 0000000..c232781 --- /dev/null +++ b/app/styles.css @@ -0,0 +1,74 @@ +:root { + --bg: #0b0f17; + --panel: #141a26; + --text: #e6ecf2; + --muted: #9aa7b2; + --accent: #4f8cff; +} + +* { box-sizing: border-box; } + +body { + margin: 0; + font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; + background: var(--bg); + color: var(--text); + padding: 24px; +} + +header { + margin-bottom: 24px; +} + +h1 { margin: 0 0 6px 0; } + +.sub { color: var(--muted); margin: 0; } + +.kpi-grid { + display: grid; + gap: 16px; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + margin-bottom: 24px; +} + +.kpi-card { + background: var(--panel); + border: 1px solid #1f2a3a; + border-radius: 12px; + padding: 16px; +} + +.kpi-label { color: var(--muted); font-size: 13px; } +.kpi-value { font-size: 22px; margin-top: 6px; } + +.panel { + background: var(--panel); + border: 1px solid #1f2a3a; + border-radius: 12px; + padding: 16px; + margin-bottom: 24px; +} + +.csv-list { display: flex; flex-wrap: wrap; gap: 8px; } +.csv-pill { + background: #1f2a3a; + padding: 6px 10px; + border-radius: 999px; + font-size: 12px; +} + +.table-wrap { overflow-x: auto; } + +.table { + width: 100%; + border-collapse: collapse; + font-size: 12px; +} + +.table th, .table td { + border-bottom: 1px solid #1f2a3a; + padding: 6px 8px; + text-align: left; +} + +.table th { color: var(--muted); } diff --git a/data/manifest.json b/data/manifest.json new file mode 100644 index 0000000..09d4fb9 --- /dev/null +++ b/data/manifest.json @@ -0,0 +1,3 @@ +{ + "csvFiles": [] +} diff --git a/docs/data-structure.md b/docs/data-structure.md new file mode 100644 index 0000000..5fd3c4e --- /dev/null +++ b/docs/data-structure.md @@ -0,0 +1,13 @@ +# Data Structure + +## Data Sources +- CSV exports (source of truth) +- DAX exports (measure definitions) + +## Storage +- `/data/*.csv` — raw CSV exports +- `/data/manifest.json` — list of CSV files loaded by the app + +## Notes +- No database yet; app loads CSVs directly via fetch. +- Column types and relationships will be documented here once data lands. diff --git a/docs/kpi-definitions.md b/docs/kpi-definitions.md new file mode 100644 index 0000000..e80d19d --- /dev/null +++ b/docs/kpi-definitions.md @@ -0,0 +1,8 @@ +# KPI Definitions + +> This file will map each KPI to its exact formula and validation notes. + +## Template +| KPI Name | Definition | Source Tables | Filters | Notes | +|---|---|---|---|---| +| Example KPI | SUM(Table[Amount]) | Table | DateRange | Match Power BI card | diff --git a/docs/migration-plan.md b/docs/migration-plan.md new file mode 100644 index 0000000..f425dda --- /dev/null +++ b/docs/migration-plan.md @@ -0,0 +1,22 @@ +# Migration Plan + +## Phase 1 — Intake & Inventory +- [ ] Collect CSV/DAX exports and source report screenshots +- [ ] Document data model + relationships +- [ ] Identify all KPIs and filters + +## Phase 2 — KPI Definition +- [ ] Translate each DAX measure into deterministic formulas +- [ ] Validate calculations against source data + +## Phase 3 — Backend Logic (Local) +- [ ] Implement KPI calculations in JS (no database yet) +- [ ] Build reusable aggregation utilities + +## Phase 4 — UI Buildout +- [ ] Recreate filters, charts, and tables +- [ ] Match layout/visual hierarchy + +## Phase 5 — Validation +- [ ] Compare KPI outputs to Power BI values +- [ ] Iterate until exact match