← Back to Dashboard
Build Log · Internal
How the Dashboard Works
A running log of what's been built, how data flows in, what each piece of logic actually does, and what we still need to wire up. Update this as we go.
What the Dashboard Is
The Client Health Dashboard is a live snapshot of every HelloSciCom client we currently work with. Each card shows hours used vs. retainer this month, the hourly rate (with retainer discount), a link to the contract, the creative lead, and the status of next month's retainer. Cards are clickable for a detail/drill-down view.
Goal: at a glance, everyone on the team can answer "are we on track with this client, and is the client looped in?"
Where the Data Comes From
- Hours used (this month): Pulled live from Avaza via the Avaza REST API. A Personal Access Token (
AVAZA_API_KEY) stored in ~/.openclaw/openclaw.json authenticates a script at ~/.openclaw/scripts/refresh-dashboard-from-avaza.py. That script aggregates entries by customer + project and PUTs to the dashboard data API. Scheduled by macOS LaunchAgent com.helloscicom.avaza-dashboard-refresh — runs every hour 9 AM–7 PM ET Mon–Fri, plus noon ET Sat/Sun.
- Retainer (this month + next month): Stored in KV. Updated by the creative lead asking Potoo, or directly via the dashboard's signoff toggle.
- Hourly rates, contract URLs, creative leads, goals: Stored in KV. Seeded from a default object in
functions/api/dashboard-data.js, then overridden by anything saved to KV.
- Signoff state ("client informed"): Stored per-client in KV under
currentMonthSignoff. Toggled directly from the dashboard.
- Under-trigger dismissal: Stored per-client in KV under
currentMonthUnderDismissed. Silences the "running low" warning for the rest of the current month.
Storage: Cloudflare KV namespace CRM_DATA, key dashboard:v1. Reads via GET /api/dashboard-data, writes via PUT with header X-Potoo-Secret.
The Two Pieces of Logic That Drive Color
1. Countdown to the 15th (next month's retainer)
Each month, by the 15th, creative leads need to confirm next month's retainer with their clients. The countdown box on each card is a gentle nudge to make that happen.
- Hidden 1st–7th of the month (too early to nag).
- Visible 8th–15th, ticking down with a "days left" counter. Last 3 days highlight in amber.
- After the 15th: if still unconfirmed, the box goes red and reads OVERDUE.
- Once a client is marked confirmed for next month, the countdown is replaced with a calm "Confirmed" line until the next cycle.
- Potoo emails creative leads on the 10th with a suggested email to send to each client. (Cron job pending — see Roadmap.)
2. Health bar color (hours used vs. retainer)
The bar fills with the percentage of retainer hours used. The colors mean:
- Purple (default): Healthy. Anything under 80% used and not in either trigger condition.
- Amber: 80–100% used, no triggers fired. Heads up but not urgent.
- Red — Over: Hours have exceeded the retainer. The "Client informed?" pill appears. Bar stays red until the creative lead marks the client as informed, then it returns to green/healthy.
- Red — Running Low: Within the last 5 days of the month, and less than 60% of retainer hours have been used, and the retainer is at least 10 hours. A "What's going on?" pill appears with a dismiss option. Designed to under-trigger — most creative leads plan hours their own way and we don't want to nag.
Why the under-trigger is conservative: Sarah's note in the build meeting — err on the side of not triggering. Creative leads plan hours their own way and we don't know what's "normal" for each client. The 60% / 5-days / 10-hrs floor combo means this fires rarely, and there's a one-click dismiss for the rest of the month if it's a false alarm. Tunable in dashboard.html → computeUnderTrigger().
The "Client informed of over/under?" toggle
Only appears when a trigger has fired (over or under). The most important thing is always communication to the client — going over or under is fine as long as they know. Once toggled on:
- The bar returns to a healthy (green/purple) state.
- The pill flips from "client not informed" → "acknowledged".
- State is saved per-client per-month in KV.
On the 1st of each month, signoff and dismissal state reset automatically (handled by the data layer).
Files & Endpoints
- Main page:
frontend/dashboard.html
- This page:
frontend/dashboard-notes.html
- Drill-down detail page:
frontend/dashboard-detail.html
- Drill-down router:
frontend/functions/dashboard/[[catchall]].js
- Data API:
frontend/functions/api/dashboard-data.js · GET for reads, PUT with X-Potoo-Secret: potoo-dashboard-2026 for writes
- KV store: namespace
CRM_DATA, key dashboard:v1
What I Need from Willa / Sarah
Data still to fill in
- Hourly rates for clients without one set yet (JLS, talkSTEM, RWJF).
- Contract URLs (Google Drive / Docs links) for Cobblestone, JLS, talkSTEM, RWJF.
- Creative lead assignments for JLS, talkSTEM, RWJF.
- Creative lead email addresses for each client — so I can email them on the 10th about next month's retainer, and when an over/under trigger fires.
- Primary client contact (name + email) for each client — so I can draft language the creative lead can forward.
- Tone notes per client — one-line vibe ("Michael at Poley = casual, technical, short"; "Kate at RWJF = formal") so suggested drafts sound right.
- Next month's retainer numbers as they get confirmed — tell me and I'll lock them on the cards.
Changelog
2026-05-14 · later same day
Build Log truth pass
Caught and corrected stale language on this page and on the dashboard: hours are not email-parsed — they sync live from Avaza via the existing REST API integration (LaunchAgent + Python script described above). Roadmap item "build the Avaza integration" reframed as "add a verifier pass to the existing sync."
2026-05-14
v2 — Smarter color logic, countdown windowing, build-log page
Countdown box now hides outside the 8th→15th window (and shows "OVERDUE" after the 15th if unconfirmed). The "Client informed?" pill no longer appears by default — it only surfaces when an over-trigger or under-trigger fires. Added a new under-trigger (≤5 days left + <60% used + retainer ≥10 hrs) with a one-click dismiss for the rest of the month. Created this page and moved all build notes here from the bottom of the dashboard.
earlier
v1 — Live Avaza hours
Hours pull directly from Avaza (current month, all entries). Sub-client roll-ups working for Poley and JLS. Retainer-tier rate discounts (-$20 at 10 hrs, -$30 at 20, -$40 at 40, -$50 at 60) applied to displayed rate. Sticky header + drill-down detail pages live.
v0
v0 — Draft / Internal Tool
Initial cards, seed data, KV-backed signoff toggle.
Roadmap
- Cron: 10th-of-the-month email to creative leads — auto-generated draft per client for the lead to forward, asking the client to confirm next month's retainer. Requires the lead emails + per-client tone notes above.
- Cron: daily over/under check — when a trigger fires, email the creative lead with suggested client-facing language and a link to mark "informed".
- Avaza sync verifier (double-up) — the live Avaza sync is already running. Next: add a second independent pass via
/api/TimesheetSummary grouped by project, compare totals to the entry-level sum, and refuse the dashboard update if they disagree by more than ±0.05 hrs. Log anomalies; never silently publish drifted numbers.
- Potoo health monitor / "check engine" light — surface processing-speed or job-failure issues on the dashboard.
- Sub-creative-lead emails — for Poley sub-clients (Cloudflare, Twilio, Google), route the 10th-of-month email to the right sub-lead instead of the parent CL.
This page is internal to HelloSciCom. Last meaningful update: 2026-05-14.