Dashboard & Pricing Redesign
The current dashboard dedicates ~75% of the viewport to a single action queue list. Financial visibility — renewal values, status breakdown — is absent. The office manager and CEO need at-a-glance financial health alongside staff triage.
The redesign restructures the dashboard as a card grid: KPI strip at top for quick reads, workflow-specific cards below, and a more prominent renewals chart showing status and value. A companion Pricing Management screen provides staff with control over the membership price schedule, modifiers, and per-member special rates.
Goals
- Add renewal value as a KPI visible to office manager and CEO
- Make the renewals chart more prominent with status breakdown and clickable bars
- Separate action types into workflow-specific cards (renewals, signups, data issues)
- Make the layout flexible for future dashboard cards
- Activity feed fills available space with pagination
- New pricing management screen for price bands, modifiers, and special rates
- Special rate toggle on member records for individually negotiated pricing
1 Layout — Card Grid
Three-column layout with a full-width KPI strip across the top. Each dashboard widget is a card placed into the grid. New cards can be added without changing the existing layout.
Technical notes
- CSS grid:
grid-template-columns: 1fr 1fr 320px - Chart card:
grid-column: 1 / 3 - Activity feed:
grid-column: 3; grid-row: 1 / 4 - New cards slot into the grid by adding a grid item — no layout restructuring needed
2 KPI Strip
Four metric tiles plus a quick-find search, spanning the full width above the card grid.
- Large numeral with small uppercase label — same pattern as current dashboard
- Active, Pending and Renewals counts use existing data already shown on the current dashboard
- June Value is a new capability — see Renewal Value
- Quick Find searches by organisation name or account number
Technical notes
- Active, Pending, Renewals: existing
useKpiCountshook - Quick Find: existing
useQuickFindhook —ilikeon name and account_number, limit 6 - June Value: new
useRenewalValuehook — sumsprice_excl_vat_gbp + premium_supplement_excl_vat_gbpfor current month (see Renewal Value)
3 Renewal Value New
A new KPI tile showing the total monetary value of the current month's renewals. This is the key new financial metric requested by the office manager and CEO.
Pricing data comes from a new Pricing Management screen where price bands, modifiers, and special rates are maintained. The per-member price is fixed at renewal time based on the current schedule — see the Pricing Management sections below for detail.
Technical notes
- Value computed from
price_excl_vat_gbp + premium_supplement_excl_vat_gbpfor each org renewing in the current month - New hook:
useRenewalValue— aggregates billing totals for orgs whererenewal_month = currentMonth
4 Renewals Chart
A much larger renewals chart spanning two columns of the dashboard. The current month breaks down into status segments; every bar is clickable and shows the monetary value.
Renewals by Month
2026 · showing count and valueFeatures
- Much larger than the current sidebar chart — takes a prominent position on the dashboard
- Current month stacked by status: renewed/paid, pending/due, overdue
- Clickable bars — navigates to Members list filtered by that month
- Per-month £ value shown below each bar
- Past months in neutral grey; future months as outlined placeholders
Technical notes
- Spans
grid-column: 1 / 3— two of three grid columns - Current month stacked via CSS flex with
column-reversedirection - Bar click navigates to
/members?renewal_month=N - Renewal counts: existing
useRenewalCountshook, extended with status breakdown - Value data: shares
useRenewalValuehook with KPI strip — sumsprice_excl_vat_gbp + premium_supplement_excl_vat_gbpper month
5 Action Cards
Three separate cards, one per workflow: renewal management, signup review, and data cleanup. Each card shows top items with overflow linking to the Members list.
Renewal Actions
11 membersFeatures
- One card per workflow: renewal management, signup review, data cleanup
- Renewal Actions has three collapsible tiers: overdue, due this month, upcoming
- Whole item row is clickable — navigates to org record
- Overflow links to Members list with the relevant filter pre-applied
- Signups show sector, region and size band metadata for quick context
- Data issues identify the specific problem (missing email vs missing address)
Technical notes
- Renewal Actions: existing
useActionQueuehook — overdue, due, upcoming tiers - New Signups:
membership_status = 'new'from same hook - Data Issues: orgs missing primary email or contact address, from same hook
- Overflow links require URL-driven filters on Members page (see Navigation Pattern)
6 Activity Feed
A timeline of recent membership activity shown in plain language. This is what staff see — a friendly narrative of what's happening, separate from the technical event log.
Recent Activity
Features
- Fills available space in the right column — shows as many events as will fit
- Same event types as the current dashboard, displayed in a friendlier format
- Load more appends the next page of events below — the feed grows, button moves down
- Coloured dots by event type; connector lines between entries
Technical notes
- Existing
useRecentEventshook, current limit of 10 removed - Pagination: offset-based, fetches next page on "Load more" click
- Grid placement:
grid-column: 3; grid-row: 1 / 4— fills all rows - Not the admin
/eventspage — same event types, friendlier display
8 URL-Driven Member Filters New
The Members page needs to support opening with filters already applied from a link. Today, filters reset every time you visit the page — there's no way to link someone to a filtered view.
Links to the Members list — from the dashboard, from emails, from bookmarks — will open with the right filters already applied. For example, a colleague can share a link to "all overdue renewals" and it just works.
Technical notes
This is a prerequisite for the dashboard overflow links and chart bar clicks. Without it, those links would land on an unfiltered Members page.
- Replace local
useStatefilter state withuseSearchParams - Seed filters from URL on mount; update URL when filters change
- Param mapping:
renewal_status=due|upcoming|overdue,renewal_month=N,issue=missing_data,status=active|new - Existing filter UI unchanged — just wired to the URL
9 Clickable Items
Action items are clickable as a whole row — the entire item navigates to the member record, not just the explicit Open/Review button.
The button remains as a visible affordance, but the click target is the full item. This is a change from the current dashboard where only the button is clickable.
Change from current behaviour. Try clicking any item in the mockups above — the whole row responds, not just the button.
10 Price Bands New
A new settings screen for managing the membership price schedule. Price bands are keyed to employee size — the same size_band values already stored on each member record.
Prices are applied at next renewal. Typically updated on 1 April. Already-processed renewals are not affected — the per-member price is fixed in place at renewal time.
Price Bands
Excl. VAT| Size Band | Annual Price | |
|---|---|---|
| Sole Trader | £120.00 | |
| 1–5 employees | £180.00 | |
| 6–20 employees | £280.00 | |
| 21–50 employees | £420.00 | |
| 51+ employees | £580.00 |
Features
- One row per
size_band— matches the existing enum on the organisation table - Inline edit: click Edit to change a price, Save to persist, Cancel to discard
- Prices are excl. VAT — consistent with the existing
price_excl_vat_gbpfield - Changes take effect at next renewal — no retrospective impact on existing members
Technical notes
- New table:
membership_price_band—size_band(PK, references the enum),annual_price_excl_vat_gbp(numeric(10,2)) - Seeded via migration with initial values matching the current spreadsheet
- CRUD via new
/settings/pricingroute in admin UI - At renewal time: look up band price by org's
size_band, apply modifiers, stamp ontoprice_excl_vat_gbp
11 Modifiers New
Two modifiers adjust the band price at renewal: a flat premium supplement and a percentage charity discount. Both are system-wide values managed alongside the price bands.
How modifiers are applied
- Premium supplement — a flat amount added to the band price when
is_premium = true. Example: 6–20 band (£280) + premium (£150) = £430 - Charity discount — a percentage applied after band + supplement when
is_charity = true. Example: sole trader (£120) at 50% = £60 - Both flags already exist on the organisation record and are toggled via the member's billing section
- Modifiers are applied at renewal time only — changes don't affect already-processed renewals
Technical notes
- New table:
membership_modifier—key(text PK:'premium_supplement'|'charity_discount'),value_numeric(numeric(10,2)),value_type('flat'|'percent') - Renewal price calculation:
band_price + (is_premium ? supplement : 0), then× (is_charity ? (1 - discount/100) : 1) - Result stamped onto
price_excl_vat_gbpandpremium_supplement_excl_vat_gbp - Existing
is_premium/is_charitybooleans on the org row are unchanged
12 Special Rates New
Some members have individually negotiated pricing — a fixed rate that doesn't follow the band schedule at renewal. The pricing management screen shows a read-only summary of all members with special rates.
Special Rates
Managed on each member’s record| Member | Override Price | Note | |
|---|---|---|---|
| Fergusons Transport | £350.00 | 3-year fixed rate, agreed 2025 | |
| SSE Transmissions | £500.00 | Corporate agreement |
Features
- Read-only on the pricing page — special rates are set up on the individual member's record
- Shows the override price and the reason/note explaining the arrangement
- "View" links navigate directly to the member record where the rate can be edited
- At renewal, members with a special rate keep their override price — the band schedule is not applied
Technical notes
- Derived from orgs where
has_special_rate = true— see Special Pricing on a Member - Query:
select name, price_excl_vat_gbp, special_rate_note from organisation where has_special_rate = true - No inline editing — View link navigates to
/orgs/{id}
13 Special Pricing on a Member New
The billing section on a member's record gains a "Special Rate" toggle. When enabled, the member's price is manually set and won't be overwritten by the band schedule at renewal.
This is how staff set up negotiated rates, corporate agreements, or any other pricing that doesn't follow the standard bands.
Try it below. Click the Special Rate toggle to see how the billing section changes between standard and override pricing.
Billing
Ben Nevis Outdoor Gear · WHC-00094How it works
- The Special Rate toggle appears in the billing section of every member record
- When off (default): the member gets their price from the band schedule at next renewal
- When on: an override price and reason field appear. This price is fixed and the band schedule is skipped at renewal
- The standard band price is shown for reference so staff can see what the member would pay
- The reason/note is displayed on the Special Rates summary on the pricing page
What happens when a special rate is set
- The override price and reason are saved to the member's billing record
- A note is automatically added to the organisation's notes recording the special rate, the reason, and who set it — providing a visible audit trail on the member record itself
- An event is created and appears in the activity feed on the dashboard, e.g. "Special rate set for Fergusons Transport — £350.00 (3-year fixed rate, agreed 2025)"
- The member appears on the Special Rates summary on the pricing management page
Technical notes
- New columns on
organisation:has_special_rate(boolean, default false),special_rate_note(text, nullable) - When
has_special_rate = true: renewal processing skips band lookup, keeps existingprice_excl_vat_gbp - When
has_special_rate = false: renewal processing applies band price + modifiers and stamps ontoprice_excl_vat_gbp - On toggle-on: append a timestamped note to
organisation.notesand insert aspecial_rate_setevent into the events table - On toggle-off: append a note recording the removal and insert a
special_rate_removedevent - UI: toggle added to the existing Billing section in
OrgForm.tsx, field-level save pattern consistent with other billing fields - Affiliate constraint unchanged — affiliates inherit billing from parent and cannot have their own special rate