From 5a00b9c096724ed9ed63ee4e9f49af6a0e05395d Mon Sep 17 00:00:00 2001 From: Viktor Barzin Date: Sat, 2 May 2026 08:42:21 +0000 Subject: [PATCH] monitoring(wealth): milestone annotations on every timeseries chart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Inspired by the user's "Journey to £1M" reference — adds vertical dashed lines on every timeseries panel at the date net worth first crossed each round threshold (£100k, £250k, £500k, £750k, £1M). Implementation: a dashboard-level annotation source ("Milestones", purple) backed by a PG query that finds the MIN(valuation_date) where SUM(total_value) >= each threshold. The query returns (time, text) pairs, e.g. "2026-04-18 → £1M 🎉". Annotations attach to all timeseries panels automatically; auto-extends as future thresholds are crossed. Verified against current data: £100k → 2021-11-01 £250k → 2023-07-18 £500k → 2024-09-19 £750k → 2025-08-26 £1M → 2026-04-18 🎉 Future work (per user request): add a "Journey" stat-card row at the top mirroring the reference (date achieved + months from previous). --- .../modules/monitoring/dashboards/wealth.json | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/stacks/monitoring/modules/monitoring/dashboards/wealth.json b/stacks/monitoring/modules/monitoring/dashboards/wealth.json index f9a22e19..8c165b88 100644 --- a/stacks/monitoring/modules/monitoring/dashboards/wealth.json +++ b/stacks/monitoring/modules/monitoring/dashboards/wealth.json @@ -9,6 +9,20 @@ "iconColor": "rgba(0, 211, 255, 1)", "name": "Annotations & Alerts", "type": "dashboard" + }, + { + "datasource": {"type": "grafana-postgresql-datasource", "uid": "wealth-pg"}, + "enable": true, + "hide": false, + "iconColor": "purple", + "name": "Milestones", + "target": { + "rawQuery": true, + "editorMode": "code", + "format": "table", + "refId": "Anno", + "rawSql": "WITH daily AS (SELECT d.valuation_date, SUM(d.total_value) AS nw FROM daily_account_valuation d JOIN accounts a ON a.id = d.account_id GROUP BY d.valuation_date), crossings AS (SELECT t, (SELECT MIN(valuation_date) FROM daily WHERE nw >= t::numeric) AS d FROM unnest(ARRAY[100000, 250000, 500000, 750000, 1000000]) AS t) SELECT d::timestamp AS \"time\", '£' || CASE WHEN t >= 1000000 THEN (t/1000000)::int::text || 'M 🎉' ELSE (t/1000)::int::text || 'k' END AS text FROM crossings WHERE d IS NOT NULL ORDER BY d" + } } ] },