Block Entity Detail Dashboard
Block Entity Detail Dashboard
Available from v0.12.4. The Block Entity Detail Dashboard is a dedicated page for a single residential building record. It aggregates all child entity data — units, management accounts, client money, and ownership history — into one view.
Navigation
From Dashboard → Blocks, click any block in the list to open its detail dashboard at:
/dashboard/blocks/[blockId]
Use the Back button in the page header to return to the blocks list.
Page Sections
Header
Displays the block name, the assigned owner (with an individual or company icon), and two action buttons: Edit and Delete.
- If no owner is assigned, an amber warning banner is shown directly below the header.
- The Management Account Banner is also surfaced here if the block does not yet have a management account configured.
Quick Stats Grid
Four stat cards provide an at-a-glance summary:
| Card | What it shows |
|---|---|
| Units | Total registered units; subtext shows how many have a leaseholder assigned |
| Account Sources | Total management account sources; subtext shows active count |
| Client Money | Total client money accounts; subtext shows active and ring-fenced counts |
| Transfers | Number of recorded ownership transfers for this block |
The Units and Client Money cards link directly to the relevant list pages, pre-filtered for the block.
Block Details Card
A structured key–value list showing:
- Block name
- Full address (line 1, line 2, city, postcode)
- Expected units (the
totalUnitsvalue set when the block was created) - Registered units (actual count from the units table)
- Created date
- Last updated date
Ownership Card
Shows the assigned owner's name and type (individual or company). If multiple ownership transfers have occurred, the transfer count is displayed as an audit trail indicator.
If no owner is assigned, the card shows a prompt to navigate to the Owners page to assign one. Per platform rules, every block must belong to exactly one owner.
Unit Status Breakdown
Visible only when the block has at least one registered unit. Shows a colour-coded pill for each unit status:
| Status | Colour |
|---|---|
| Occupied | Green |
| Vacant | Amber |
| Sublet | Blue |
| Sold Pending Completion | Violet |
| No Leaseholder | Red |
A View all units link navigates to the units list filtered by this block.
Compliance Readiness Checklist
Four checks are displayed with pass (✅) or pending (🕐) indicators:
| Check | Passes when |
|---|---|
| Owner Assigned | The block has an ownerId |
| Management Account Configured | At least one active management account source exists |
| Units Registered | At least one unit exists for the block |
| Client Money Account | At least one active client money account exists |
Editing a Block
Click Edit in the header to open an inline edit form. You can update:
- Block name (required)
- Total units
- Address line 1
- Address line 2
- City
- Postcode
Click Save Changes to submit or Cancel to discard. On success, the summary data refreshes automatically.
Deleting a Block
Click Delete in the header to open a confirmation prompt. The prompt warns that management account sources associated with the block will also be removed. Units and client money accounts should be handled separately before deletion.
On confirmation, the block is deleted and the browser redirects to /dashboard/blocks.
Loading and Error States
- Loading: A skeleton layout matching the page structure is shown while data is fetching.
- Error (render): An error boundary catches unexpected failures and shows a "Try Again" button alongside a "Back to Blocks" link.
- Error (data): If the block is not found or does not belong to the organisation, an inline error message is shown with a back link.
block.summary API
The detail dashboard is powered by the block.summary tRPC query.
Procedure type: query
Auth: Organisation-scoped (orgProcedure) — all results are filtered by orgId
Input
{ id: string } // blockId
Output
{
block: {
id: string;
orgId: string;
ownerId: string | null;
name: string;
addressLine1: string | null;
addressLine2: string | null;
city: string | null;
postcode: string | null;
totalUnits: number | null;
createdAt: Date;
updatedAt: Date;
ownerName: string | null; // joined from owners table
ownerType: string | null; // "individual" | "company"
};
units: {
total: number;
occupied: number;
vacant: number;
sublet: number;
soldPending: number;
withLeaseholder: number;
withoutLeaseholder: number;
};
managementAccounts: {
total: number;
active: number;
hasActiveSource: boolean;
};
clientMoney: {
total: number;
active: number;
pending: number;
ringFenced: number;
};
ownershipTransfers: number;
}
Errors
| Code | Condition |
|---|---|
NOT_FOUND | Block does not exist or does not belong to the requesting organisation |
Implementation notes
- Uses five separate SQL queries; unit, source, and client money counts use PostgreSQL
count(*) filter (where ...)aggregation. - All queries are scoped by both
blockIdandorgIdto enforce multi-tenant data isolation. - The response shape is designed for the detail dashboard and is not a general-purpose block fetch — use
block.getByIdfor simpler lookups.
Specs Enforced
| Spec | Where enforced |
|---|---|
| Block belongs to exactly one owner | Ownership Card, no-owner warning banner, Compliance Readiness |
| Owner must exist before block assignment | Warning banner and prompt to Owners page |
| Block requires management account before financial reporting | Management Account Banner, Compliance Readiness |
| Calmony ring-fenced client money per block | Client Money stat card and Compliance Readiness |
| Multi-tenant strict data isolation per agent | All block.summary queries scoped by orgId |