> ## 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

> CanvasView stores a user-customised investor-report canvas layout for a workspace

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       | — <sub>(not a records root)</sub> |
| REST base                       | `/v1/canvas-views`                |
| Entity class                    | `CanvasView`                      |

<Note>
  **Internal object.** Not currently exposed on the public REST API. The operations below describe the intended contract.
</Note>

## 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

```json theme={null}
{
  "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" } }
    }
  }
}
```

<sub>Source: `apps/api/src/database/entities/CanvasView.ts` · domain: workspace · tier: Infrastructure</sub>
