Why this mattered
The first version of the Palladium AI dashboard shipped with static placeholder values in the metric cards and a bar chart populated with hardcoded monthly totals. The layout and visual design were solid, but the numbers told a story that had nothing to do with the actual data in the database. A fund admin viewing their workspace would see the same portfolio balance, member count, and chart bars regardless of what their cooperative had actually recorded.
This update closes that gap entirely. Every value on the overview page now reflects the live state of the selected team's data, updates in real time as records change, and correctly isolates between funds — switching to a different team immediately reloads all metrics for that team.
Overview metric cards
The four summary cards at the top of the dashboard were each addressed in turn.
Portfolio Balance
Previously hardcoded as K45,231.89. Now computed as the sum of loanAmount across all loans with status === "approved" for the current team. The subtitle shows the count of approved loans that make up the balance.
Active Members
Previously hardcoded as +2350. Now sourced from memberships.listByTeam, counting the actual number of membership records for the selected team. The subtitle names the fund to make it clear the count is fund-scoped, not platform-wide.
Savings Deposits
Previously hardcoded as +12,234. Now sourced from deposits.listByTeam, showing the running total of all deposit amounts recorded for the team. The subtitle reports the number of individual deposit records.
Loan Activity
Already live in the previous release. Counts total loan records and pending applications for the team. Included here for completeness — all four cards now share the same reactive loading pattern with skeleton placeholders while data is in-flight.
Because all three queries — loans.listByTeam, memberships.listByTeam, and deposits.listByTeam — run in parallel via Convex's reactive query engine, the cards hydrate together in a single round-trip. A shared isLoading flag ensures the skeleton state covers all four cards uniformly.
Monthly capital flow chart
The bar chart below the metric cards was previously rendered from a static array of twelve month/total pairs. It has been replaced with a fully reactive grouped bar chart powered by a new Convex query: stats.monthlyCapitalFlow.
How the query works
- Fetches all loans and deposits for the team in two parallel index scans.
- Initialises twelve buckets — one per calendar month — each with a
loansand adepositsaccumulator. - Walks each approved loan and adds its
loanAmountto the bucket corresponding to the month ofcreatedAt, filtering to the current calendar year. - Walks each deposit and adds its
amountto the bucket corresponding to the month ofdateDeposited, again filtering to the current year. - Returns the twelve-bucket array.
On the frontend the chart renders two bars per month — Approved Loans in the primary colour and Savings Deposits in a muted variant — with a legend and a formatted tooltip showing the exact dollar amounts. A loading skeleton fills the chart area while data is fetching.
Because the query reads teamId from the URL params, navigating between team workspaces automatically re-subscribes the chart to the new team's data without any manual refresh.
Member holdings: savings and loans side by side
The Members page previously showed a static list of five placeholder names and amounts. Two components were updated in this release.
Member avatar strip
Replaced hardcoded GitHub avatars with a dynamic strip that renders one avatar per actual team member. Each avatar displays the member's initials (derived from their name or email) and a role badge indicating whether they are an admin or member. Skeleton circles appear while the query is loading.
Member holdings table
Now shows two columns alongside each member's name and email: their cumulative savings (sum of all deposits attributed to that member) and their cumulative loans (sum of all loan amounts linked to that member). Values are formatted as dollar amounts and update reactively.
To support per-member loan attribution, the loans table schema was extended with an optional memberId field (type id("users")) and a corresponding by_member index. The loans.apply mutation now stores the authenticated user's ID in this field so that all platform-originated loans are linked to the applicant. Legacy-imported loans that predate this field show K0.00 in the loans column until re-attributed.
Summary of schema changes
| Table | Change |
|---|---|
| loans | Added memberId: v.optional(v.id("users")) and a new by_member index. |
| stats | New monthlyCapitalFlow query returning per-month loan and deposit buckets for the current year. |
What's coming next
With every current dashboard metric now live-bound to the database, the next phase focuses on richer per-member financial data:
- Repayment schedules — structured instalment plans linked to approved loans, with payment recording and outstanding balance tracking per member.
- Share capital — a dedicated share-purchase record type so member holdings show savings, loans, and share capital in one view.
- Year-over-year comparison — extending the capital flow chart with a prior-year overlay to surface growth trends at a glance.
- Export and reporting — downloadable CSV/PDF summaries aligned to SACCO regulator templates.