Documentation Index
Fetch the complete documentation index at: https://docs.wellapp.ai/llms.txt
Use this file to discover all available pages before exploring further.
CanvasView stores a user-customised investor-report canvas layout for a workspace. One active row exists per (workspace, template_id) pair, enforced by a partial unique index on deleted_at IS NULL. The entity is REST-only and is not tracked in Hasura; the financial-overview page fetches one row by template via PUT /v1/workspaces/:id/canvas-views/:template_id (upsert). It belongs to a Workspace and optionally records the People who created it.
| Naming | Value |
|---|
| Object | CanvasView |
Resource type (JSON:API type) | canvas_view |
| Collection / records root | — (not a records root) |
| REST base | /v1/canvas-views |
| Entity class | CanvasView |
Internal object. Not currently exposed on the public REST API. The operations below describe the intended contract.
API operations
| Operation | Method & path | Status |
|---|
| List | GET /v1/canvas-views | 🟡 Planned |
| Retrieve | GET /v1/canvas-views/{id} | 🟡 Planned |
| Create | POST /v1/canvas-views | 🟡 Planned |
| Update | PATCH /v1/canvas-views/{id} | 🟡 Planned |
| Delete | DELETE /v1/canvas-views/{id} | 🟡 Planned |
Data model
Attributes
| Field | Type | Required | Constraints | Allowed values | Description |
|---|
| canvas_view_id | string (UUID) — 🔒 system | ✅ Yes | UNIQUE; DEFAULT gen_random_uuid() | — | Public-facing UUID for JSON:API addressing. System-assigned at creation; never client-provided. |
| template_id | string (varchar 50) | ✅ Yes | max 50 chars; partial unique with workspace on deleted_at IS NULL (idx_canvas_views_workspace_template_active); validated at service layer against CANVAS_TEMPLATES | Service-layer validated against CANVAS_TEMPLATES enum from @wellapp/shared (e.g. ‘investor-report-v1’). Stored as varchar — no DB enum. | Identifies which canvas template this layout belongs to. Used as the natural write key; the FE addresses the row as PUT …/canvas-views/:template_id. |
| name | string (varchar 255) | null | ⚪ No | max 255 chars; nullable | — | Optional human label for the layout. Reserved for future multi-named-layout support per workspace/template pair. Null = the implicit default layout. |
| blocks | CanvasBlockConfig[] (jsonb) | ✅ Yes | NOT NULL jsonb; service enforces: 1-7 entries, unique slot_id values, valid width enum (‘one_third’|‘two_thirds’), positions form a permutation of [0, length) | Array of { slot_id: ‘header’|‘kpi-1’|‘kpi-2’|‘kpi-3’|‘body-left’|‘body-right’|‘footer’, width: ‘one_third’|‘two_thirds’, position: number } | Ordered list of selected blocks defining the canvas layout. The read formatter drops unknown slot_id values (forward-compat); the write path strictly validates against CANVAS_SLOT_IDS. |
| created_at | datetime — 🔒 system | ✅ Yes | NOT NULL timestamptz; set once via onCreate hook | — | Timestamp when the canvas view was first created. Set automatically by the MikroORM onCreate lifecycle hook. |
| updated_at | datetime — 🔒 system | ⚪ No | nullable timestamptz; set by onCreate and onUpdate hooks | — | Timestamp of the last upsert. Updated automatically via the MikroORM onUpdate lifecycle hook on every write. |
| deleted_at | datetime | null — 🔒 system | ⚪ No | nullable timestamptz; null = active row | — | Soft-delete timestamp. Null for active rows. The partial unique index exempts soft-deleted rows so delete + re-create preserves history. Set by the service on logical deletion. |
Relationships
| Name | Type | Required | Description |
|---|
| workspace | to-one (ManyToOne) | Yes — NOT NULL FK with ON DELETE CASCADE | The workspace that owns this canvas layout. Workspace deletion cascades and removes all associated canvas_views. All queries are workspace-scoped via this FK. |
| created_by | to-one (ManyToOne) | No — nullable FK with ON DELETE SET NULL | The People (user) who created this canvas view. Nullable; set to null if the creating user is deleted. References the peoples table. |
System-computed
- canvas_view_id: auto-assigned via gen_random_uuid() default at row creation — never client-provided
- created_at: set once by MikroORM onCreate hook (new Date())
- updated_at: set by onCreate and onUpdate hooks on every write
- deleted_at: set by the service layer on soft deletion; null for active rows
- pk: internal auto-increment serial primary key — never exposed in the API
- Partial unique index idx_canvas_views_workspace_template_active enforces at most one active row per (workspace_pk, template_id) while allowing soft-deleted history rows to coexist
- The write path is an upsert keyed on (workspace, template_id) — the service resolves or creates the row rather than requiring the caller to supply canvas_view_id
- blocks validation is fully service-layer enforced (1-7 entries, unique slot_ids, valid widths, position permutation) — no DB-level CHECK constraint on the JSONB column
- The read formatter (canvas-view.formatter.ts) drops unknown slot_id entries rather than throwing — forward-compat for future slot registry expansions
Example
{
"data": {
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"type": "canvas_view",
"attributes": {
"template_id": "investor-report-v1",
"name": null,
"blocks": [
{ "slot_id": "kpi-1", "width": "one_third", "position": 0 },
{ "slot_id": "kpi-2", "width": "one_third", "position": 1 },
{ "slot_id": "kpi-3", "width": "one_third", "position": 2 },
{ "slot_id": "body-left", "width": "one_third", "position": 3 },
{ "slot_id": "body-right", "width": "two_thirds", "position": 4 }
],
"created_at": "2026-05-16T17:00:00.000Z",
"updated_at": "2026-05-20T09:30:00.000Z",
"deleted_at": null
},
"relationships": {
"workspace": { "data": { "id": "wsp_uuid_here", "type": "workspace" } },
"created_by": { "data": { "id": "ppl_uuid_here", "type": "people" } }
}
}
}
Source: apps/api/src/database/entities/CanvasView.ts · domain: workspace · tier: Infrastructure