From 8ed56a97d5274d1453a3f33a1e7ad75483ba585c Mon Sep 17 00:00:00 2001 From: Thomas Joise Date: Sun, 19 Jul 2026 03:14:30 +0530 Subject: [PATCH] feat(d30): Home as landing page + more sidebar counts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Make the Home hub the landing: `/` now renders Home and the Dashboard moves to `/dashboard` (nav item, routes, and the hub's Dashboard card all updated). Unknown routes still fall back to `/` (Home). - Add sidebar count badges for Clients (total book size), Documents (drafts still to finish) and Modules (count) — neutral, best-effort, alongside the existing Reminders/Pipeline/Tickets/Renewals/SMS counts. Typecheck clean; web build green. Verified live: / → Home, /dashboard → Dashboard, and the Modules count badge renders from real data. Co-Authored-By: Claude Opus 4.8 (1M context) --- apps/hq-web/src/Layout.tsx | 16 ++++++++++++---- apps/hq-web/src/main.tsx | 4 ++-- apps/hq-web/src/nav.ts | 4 ++-- apps/hq-web/src/pages/Home.tsx | 2 +- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/apps/hq-web/src/Layout.tsx b/apps/hq-web/src/Layout.tsx index 9380438..f2fec9a 100644 --- a/apps/hq-web/src/Layout.tsx +++ b/apps/hq-web/src/Layout.tsx @@ -5,8 +5,8 @@ import { avatarColors, Badge, CommandPalette, initials, ModeToggle, type PaletteItem, } from '@sims/ui' import { - clearSession, displayName, getEmailStatus, getPipeline, getReminders, getRenewals, - getSearch, getSmsBalances, getTickets, hasSession, role, + clearSession, displayName, getClients, getDocuments, getEmailStatus, getModules, getPipeline, + getReminders, getRenewals, getSearch, getSmsBalances, getTickets, hasSession, role, } from './api' import { NAV_GROUPS } from './nav' @@ -24,8 +24,10 @@ export function Layout() { return !v }) const [queueCounts, setQueueCounts] = useState({ queued: 0, failed: 0 }) - // Section-badge counts (Pipeline / Tickets / Renewals / SMS) — fetched once on mount. - const [sectionCounts, setSectionCounts] = useState({ pipeline: 0, tickets: 0, renewals: 0, smsLow: 0 }) + // Section-badge counts — fetched once on mount (best-effort, stay 0 on failure). + const [sectionCounts, setSectionCounts] = useState({ + pipeline: 0, tickets: 0, renewals: 0, smsLow: 0, clients: 0, docDrafts: 0, modules: 0, + }) const [healthy, setHealthy] = useState() const [menuOpen, setMenuOpen] = useState(false) const [userOpen, setUserOpen] = useState(false) @@ -60,6 +62,9 @@ export function Layout() { getTickets({ status: 'open' }).then((r) => setSectionCounts((c) => ({ ...c, tickets: r.total }))).catch(() => { /* best-effort */ }) getRenewals().then((rows) => setSectionCounts((c) => ({ ...c, renewals: rows.length }))).catch(() => { /* best-effort */ }) getSmsBalances().then((r) => setSectionCounts((c) => ({ ...c, smsLow: r.lowCount }))).catch(() => { /* best-effort */ }) + getClients().then((cs) => setSectionCounts((c) => ({ ...c, clients: cs.length }))).catch(() => { /* best-effort */ }) + getDocuments({ status: 'draft', pageSize: 1 }).then((r) => setSectionCounts((c) => ({ ...c, docDrafts: r.total }))).catch(() => { /* best-effort */ }) + getModules().then((ms) => setSectionCounts((c) => ({ ...c, modules: ms.length }))).catch(() => { /* best-effort */ }) }, []) // Close the mobile drawer and the user menu when the route changes. @@ -165,6 +170,9 @@ export function Layout() { if (sectionCounts.tickets > 0) counts['/tickets'] = { n: sectionCounts.tickets, title: `${sectionCounts.tickets} open` } if (sectionCounts.renewals > 0) counts['/renewals'] = { n: sectionCounts.renewals, title: `${sectionCounts.renewals} upcoming` } if (sectionCounts.smsLow > 0) counts['/sms-balances'] = { n: sectionCounts.smsLow, tone: 'warn', title: `${sectionCounts.smsLow} low on balance` } + if (sectionCounts.clients > 0) counts['/clients'] = { n: sectionCounts.clients, title: `${sectionCounts.clients} client${sectionCounts.clients === 1 ? '' : 's'}` } + if (sectionCounts.docDrafts > 0) counts['/documents'] = { n: sectionCounts.docDrafts, title: `${sectionCounts.docDrafts} draft${sectionCounts.docDrafts === 1 ? '' : 's'} to finish` } + if (sectionCounts.modules > 0) counts['/modules'] = { n: sectionCounts.modules, title: `${sectionCounts.modules} module${sectionCounts.modules === 1 ? '' : 's'}` } return (
diff --git a/apps/hq-web/src/main.tsx b/apps/hq-web/src/main.tsx index f5f25a6..f48608f 100644 --- a/apps/hq-web/src/main.tsx +++ b/apps/hq-web/src/main.tsx @@ -38,8 +38,8 @@ function App() { } /> }> - } /> - } /> + } /> + } /> } /> } /> } /> diff --git a/apps/hq-web/src/nav.ts b/apps/hq-web/src/nav.ts index e45787c..21cdd74 100644 --- a/apps/hq-web/src/nav.ts +++ b/apps/hq-web/src/nav.ts @@ -20,8 +20,8 @@ export const NAV_GROUPS: NavGroup[] = [ { label: 'Overview', items: [ - { to: '/home', label: 'Home', icon: LayoutGrid }, - { to: '/', label: 'Dashboard', icon: LayoutDashboard }, + { to: '/', label: 'Home', icon: LayoutGrid }, + { to: '/dashboard', label: 'Dashboard', icon: LayoutDashboard }, ], }, { diff --git a/apps/hq-web/src/pages/Home.tsx b/apps/hq-web/src/pages/Home.tsx index 37daf93..49e2ab4 100644 --- a/apps/hq-web/src/pages/Home.tsx +++ b/apps/hq-web/src/pages/Home.tsx @@ -37,7 +37,7 @@ export function Home() { // Main, look-at-it-all-the-time sections only — the rest live in the sidebar. const cards: HubCard[] = [ - { to: '/', label: 'Dashboard', desc: 'Today’s money — dues, renewals, follow-ups, the send queue.', icon: LayoutDashboard, stat: { text: 'My Day' } }, + { to: '/dashboard', label: 'Dashboard', desc: 'Today’s money — dues, renewals, follow-ups, the send queue.', icon: LayoutDashboard, stat: { text: 'My Day' } }, { to: '/clients', label: 'Clients', desc: 'The client book — profiles, modules, ledger, support.', icon: Users }, { to: '/documents', label: 'Documents', desc: 'Quotations, proformas, invoices, credit notes.', icon: Files }, { to: '/reminders', label: 'Reminders', desc: 'The email send queue — preview, send, dismiss.', icon: BellRing, ...(remStat !== undefined ? { stat: remStat } : {}) },