@terreno/mcp 57.7.0 → 57.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/docs/resources/ui.md +5 -4
- package/dist/docs/upgrades/57.8.0.md +61 -0
- package/dist/docs/upgrades/57.9.0.md +28 -0
- package/dist/docs/versioned/next/explanation/charts.md +12 -0
- package/dist/docs/versioned/next/explanation/example-coverage.md +2 -1
- package/dist/docs/versioned/next/explanation/model-router-actions.md +1 -1
- package/dist/docs/versioned/next/explanation/roadmap-seed-issues.md +34 -0
- package/dist/docs/versioned/next/how-to/README.md +1 -1
- package/dist/docs/versioned/next/how-to/admin-import-prebuilt.md +1 -1
- package/dist/docs/versioned/next/how-to/charts-and-dashboards.md +5 -0
- package/dist/docs/versioned/next/how-to/circleci.md +30 -14
- package/dist/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +8 -0
- package/dist/docs/versioned/next/how-to/install-agent-skills.md +1 -1
- package/dist/docs/versioned/next/how-to/preview-demo.md +19 -0
- package/dist/docs/versioned/next/how-to/product-announcements.md +3 -0
- package/dist/docs/versioned/next/implementationPlans/ai-observability.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
- package/dist/docs/versioned/next/implementationPlans/charts-and-dashboards.md +3 -2
- package/dist/docs/versioned/next/implementationPlans/dashboard-chart-parity.md +275 -0
- package/dist/docs/versioned/next/implementationPlans/demo-workshop.md +219 -0
- package/dist/docs/versioned/next/reference/ai.md +41 -10
- package/dist/docs/versioned/next/reference/announcement-release-packs.md +146 -0
- package/dist/docs/versioned/next/reference/announcements.md +7 -3
- package/dist/docs/versioned/next/reference/api.md +1 -0
- package/dist/docs/versioned/next/reference/environment-variables.md +2 -1
- package/dist/docs/versioned/next/reference/feature-flags.md +1 -1
- package/dist/docs/versioned/next/reference/ui.md +108 -17
- package/dist/docs/versioned/next/tasks/dashboard-chart-parity.md +145 -0
- package/dist/docs/versioned/next/tasks/demo-workshop.md +150 -0
- package/package.json +2 -2
- package/src/docs/resources/ui.md +5 -4
- package/src/docs/upgrades/57.8.0.md +61 -0
- package/src/docs/upgrades/57.9.0.md +28 -0
- package/src/docs/versioned/next/explanation/charts.md +12 -0
- package/src/docs/versioned/next/explanation/example-coverage.md +2 -1
- package/src/docs/versioned/next/explanation/model-router-actions.md +1 -1
- package/src/docs/versioned/next/explanation/roadmap-seed-issues.md +34 -0
- package/src/docs/versioned/next/how-to/README.md +1 -1
- package/src/docs/versioned/next/how-to/admin-import-prebuilt.md +1 -1
- package/src/docs/versioned/next/how-to/charts-and-dashboards.md +5 -0
- package/src/docs/versioned/next/how-to/circleci.md +30 -14
- package/src/docs/versioned/next/how-to/compare-chart-rendered-snapshots.md +8 -0
- package/src/docs/versioned/next/how-to/install-agent-skills.md +1 -1
- package/src/docs/versioned/next/how-to/preview-demo.md +19 -0
- package/src/docs/versioned/next/how-to/product-announcements.md +3 -0
- package/src/docs/versioned/next/implementationPlans/ai-observability.md +1 -1
- package/src/docs/versioned/next/implementationPlans/b2b-platform-program.md +1 -1
- package/src/docs/versioned/next/implementationPlans/charts-and-dashboards.md +3 -2
- package/src/docs/versioned/next/implementationPlans/dashboard-chart-parity.md +275 -0
- package/src/docs/versioned/next/implementationPlans/demo-workshop.md +219 -0
- package/src/docs/versioned/next/reference/ai.md +41 -10
- package/src/docs/versioned/next/reference/announcement-release-packs.md +146 -0
- package/src/docs/versioned/next/reference/announcements.md +7 -3
- package/src/docs/versioned/next/reference/api.md +1 -0
- package/src/docs/versioned/next/reference/environment-variables.md +2 -1
- package/src/docs/versioned/next/reference/feature-flags.md +1 -1
- package/src/docs/versioned/next/reference/ui.md +108 -17
- package/src/docs/versioned/next/tasks/dashboard-chart-parity.md +145 -0
- package/src/docs/versioned/next/tasks/demo-workshop.md +150 -0
- package/dist/docs/ui-types-documentation.json +0 -33
- package/src/docs/ui-types-documentation.json +0 -33
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# Announcement release packs
|
|
2
|
+
|
|
3
|
+
An announcement release pack is a reviewable directory containing one manifest and one Markdown file per announcement. Import the pack through `POST /announcements/import-release`; imports are idempotent by product, release version, channel, and announcement slug.
|
|
4
|
+
|
|
5
|
+
## Directory format
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
announcements/releases/1.14.0/
|
|
9
|
+
pack.yaml
|
|
10
|
+
staff-required.md
|
|
11
|
+
patient-banner.md
|
|
12
|
+
changelog.md
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
`pack.yaml` carries release metadata, shared defaults, and the ordered file list:
|
|
16
|
+
|
|
17
|
+
```yaml
|
|
18
|
+
schema: terreno.announcement-pack/v1
|
|
19
|
+
release:
|
|
20
|
+
product: example
|
|
21
|
+
version: "1.14.0"
|
|
22
|
+
buildNumber: 1842
|
|
23
|
+
channel: production
|
|
24
|
+
defaults:
|
|
25
|
+
platforms: [ios, android, web]
|
|
26
|
+
audienceType: all
|
|
27
|
+
displayMode: feed
|
|
28
|
+
acknowledgementPolicy: dismiss-only
|
|
29
|
+
priority: 0
|
|
30
|
+
announcements:
|
|
31
|
+
- staff-required.md
|
|
32
|
+
- patient-banner.md
|
|
33
|
+
- changelog.md
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Each announcement is Markdown with YAML frontmatter:
|
|
37
|
+
|
|
38
|
+
```markdown
|
|
39
|
+
---
|
|
40
|
+
slug: staff-1-14-required
|
|
41
|
+
title: "Staff: version 1.14.0 is live"
|
|
42
|
+
displayMode: modal
|
|
43
|
+
audienceType: staff
|
|
44
|
+
acknowledgementPolicy: required
|
|
45
|
+
priority: 20
|
|
46
|
+
primaryAction:
|
|
47
|
+
label: Read the release notes
|
|
48
|
+
url: https://example.com/releases/1.14.0
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## What changed in 1.14.0
|
|
52
|
+
|
|
53
|
+
The updated workflow is now available.
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Frontmatter overrides manifest defaults. Supported fields are:
|
|
57
|
+
|
|
58
|
+
| Field | Required | Description |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `slug` | Yes | Lowercase, hyphenated stable ID within the release. Re-importing the same slug updates instead of duplicating. |
|
|
61
|
+
| `title` | Yes | Surface title. Include the user-facing release version. |
|
|
62
|
+
| `displayMode` | No | `modal`, `banner`, or `feed`. |
|
|
63
|
+
| `audienceType` | No | `all`, `staff`, or `patient`. |
|
|
64
|
+
| `acknowledgementPolicy` | No | `required` or `dismiss-only`. |
|
|
65
|
+
| `platforms` | No | Any non-empty subset of `ios`, `android`, and `web`. |
|
|
66
|
+
| `priority` | No | Higher values appear first. |
|
|
67
|
+
| `minBuildNumber` | No | Per-item override for the release build number. |
|
|
68
|
+
| `publishAt`, `expiresAt` | No | ISO 8601 timestamps. |
|
|
69
|
+
| `audience` | No | Consumer-defined targeting object. |
|
|
70
|
+
| `primaryAction` | No | Object containing `label` and absolute `url`. |
|
|
71
|
+
|
|
72
|
+
## Versioning
|
|
73
|
+
|
|
74
|
+
The format has three separate versions:
|
|
75
|
+
|
|
76
|
+
1. `schema` versions the pack contract. The current value is `terreno.announcement-pack/v1`.
|
|
77
|
+
2. `release.version` is the user-facing product version and is stored with each imported announcement.
|
|
78
|
+
3. `release.buildNumber` is the client build and becomes `minBuildNumber` unless an announcement overrides it.
|
|
79
|
+
|
|
80
|
+
The announcement model's numeric `version` is server-managed content revision metadata. Do not put it in a pack. Editing the title or body of an already-published imported announcement increments it and re-shows required content where applicable.
|
|
81
|
+
|
|
82
|
+
## Example pack
|
|
83
|
+
|
|
84
|
+
`example-backend/announcements/releases/1.14.0/` is a complete pack with a required staff modal, a patient banner, and a changelog feed item. The example backend ships a loader that parses the pack, validates it with `announcementReleaseImportSchema`, and prints or uploads the JSON body:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
cd example-backend
|
|
88
|
+
bun run announcements:pack announcements/releases/1.14.0 # print JSON
|
|
89
|
+
bun run announcements:pack announcements/releases/1.14.0 --upload http://localhost:4000
|
|
90
|
+
bun run announcements:pack announcements/releases/1.14.0 --publish --upload <api-url>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Uploads use `ANNOUNCEMENTS_UPLOAD_TOKEN`, falling back to the example default `terreno-example-announcement-upload`. Uploads are drafts unless `--publish` is passed.
|
|
94
|
+
|
|
95
|
+
## Import API
|
|
96
|
+
|
|
97
|
+
Enable automation with a dedicated secret:
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
new AnnouncementsApp({
|
|
101
|
+
uploadToken: process.env.ANNOUNCEMENTS_UPLOAD_TOKEN,
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The example Cloud Run backend seeds `ANNOUNCEMENTS_UPLOAD_TOKEN` from Terraform (`announcements_upload_token`, default `terreno-example-announcement-upload`). Use that bearer token against the example preview or production URL. Preview deploys use the same default until the secret exists.
|
|
106
|
+
|
|
107
|
+
Send the parsed manifest and Markdown files as JSON:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
curl --fail-with-body \
|
|
111
|
+
-H "Authorization: Bearer $ANNOUNCEMENTS_UPLOAD_TOKEN" \
|
|
112
|
+
-H "Content-Type: application/json" \
|
|
113
|
+
--data @announcement-release.json \
|
|
114
|
+
https://api.example.com/announcements/import-release
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The request shape is:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"release": {
|
|
122
|
+
"product": "example",
|
|
123
|
+
"version": "1.14.0",
|
|
124
|
+
"buildNumber": 1842,
|
|
125
|
+
"channel": "production"
|
|
126
|
+
},
|
|
127
|
+
"defaults": {
|
|
128
|
+
"platforms": ["ios", "android", "web"],
|
|
129
|
+
"displayMode": "feed"
|
|
130
|
+
},
|
|
131
|
+
"announcements": [
|
|
132
|
+
{
|
|
133
|
+
"slug": "changelog",
|
|
134
|
+
"title": "Version 1.14.0",
|
|
135
|
+
"body": "## What changed\n\nRelease details."
|
|
136
|
+
}
|
|
137
|
+
],
|
|
138
|
+
"publish": false
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`publish` defaults to `false`, so new rows are drafts. Pass `"publish": true` only for an intentional live import. Draft re-imports do not unpublish an existing live announcement. An authenticated admin may also call the endpoint without the dedicated upload token.
|
|
143
|
+
|
|
144
|
+
The response reports `created`, `updated`, `unchanged`, and `published` counts plus the ID, slug, status, and content version of each row.
|
|
145
|
+
|
|
146
|
+
The body is validated with the exported Zod schema `announcementReleaseImportSchema` before any row is written. Unknown keys, malformed slugs, empty titles or bodies, invalid enum values, missing `release.product` / `release.version`, and duplicate slugs return **400** `Validation failed` with per-field messages in `meta.fields`. `importAnnouncementRelease` runs the same schema when called directly from server code.
|
|
@@ -27,6 +27,7 @@ new TerrenoApp({ userModel: User })
|
|
|
27
27
|
| `help.enabled` | `boolean` | `false` | Registers help search/detail routes for MCP and in-app help |
|
|
28
28
|
| `matchAudience` | `(user, announcement) => boolean` | always `true` | Opaque audience JSON filter composed with `audienceType` via `matchAudienceByType` |
|
|
29
29
|
| `isStaff` | `(user) => boolean` | `user.admin === true` | Staff check used by `matchAudienceByType` for `audienceType` |
|
|
30
|
+
| `uploadToken` | `string` | unset | Dedicated bearer token for idempotent `POST /announcements/import-release` automation |
|
|
30
31
|
| `permissions` | partial CRUD overrides | admin-only | Overrides default `IsAdmin` permissions on announcement CRUD |
|
|
31
32
|
|
|
32
33
|
## Acknowledgement policy
|
|
@@ -52,7 +53,7 @@ Legacy MongoDB documents that still store `requiresAcknowledgement: true` map to
|
|
|
52
53
|
|
|
53
54
|
## Models
|
|
54
55
|
|
|
55
|
-
- **Announcement** — `title`, `body` (markdown), `status` (`draft` | `published` | `archived`), `version`, `priority`, `displayMode` (`modal` | `banner` | `feed`, default `modal`), `audienceType` (`staff` | `patient` | `all`, default `all`), `acknowledgementPolicy`, optional `minBuildNumber`, `audience` (Mixed), `publishAt`, `expiresAt`, `platforms`, `primaryAction`
|
|
56
|
+
- **Announcement** — `title`, `body` (markdown), `status` (`draft` | `published` | `archived`), `version`, `priority`, `displayMode` (`modal` | `banner` | `feed`, default `modal`), `audienceType` (`staff` | `patient` | `all`, default `all`), `acknowledgementPolicy`, optional `minBuildNumber`, `audience` (Mixed), `publishAt`, `expiresAt`, `platforms`, `primaryAction`, and optional imported `release` metadata plus `releaseSlug`
|
|
56
57
|
- **AnnouncementAcknowledgement** — per-user acknowledgement at a specific `version`
|
|
57
58
|
- **AnnouncementImpression** — per-view analytics row
|
|
58
59
|
- **AnnouncementClickEvent** — per-click analytics row (`action: "primaryAction"`, `version` at click time, optional `platform`)
|
|
@@ -83,7 +84,7 @@ The plugin applies `matchAudienceByType` before `matchAudience`. When `isStaff`
|
|
|
83
84
|
| GET | `/announcements/pending` | Current interrupt (`modal` or `banner` only) + `remainingCount` (`platform` query: `ios` \| `android` \| `web`; optional `version` build number) |
|
|
84
85
|
| GET | `/announcements/feed` | Paginated published changelog — all display modes (`platform` and optional `version` as above) |
|
|
85
86
|
| POST | `/announcements/:id/acknowledge` | Record acknowledgement (idempotent per version). Same visibility as pending/feed; **404** when not visible. |
|
|
86
|
-
| POST | `/announcements/:id/impression` | Record a view. Same visibility as pending/feed; **404** when not visible. |
|
|
87
|
+
| POST | `/announcements/:id/impression` | Record a view (`{ platform? }`). Same visibility as pending/feed; **404** when not visible. **400** for unknown keys or an invalid `platform`. |
|
|
87
88
|
| POST | `/announcements/:id/click` | Record a primary-action click (`{ action: "primaryAction", platform? }`; optional `?version=` for min-build visibility). Returns **404** when not visible (checked before action/CTA validation). **400** when `action` is invalid, `primaryAction` is absent, or an explicit `platform` value is invalid. |
|
|
88
89
|
|
|
89
90
|
`current` and feed items include resolved `requiresAcknowledgement` (boolean) and `displayMode` derived from policy resolution and stored fields above.
|
|
@@ -96,7 +97,7 @@ Optional integer build number on `GET /pending`, `GET /feed`, and help routes. W
|
|
|
96
97
|
- `?version=10` (or higher) shows it
|
|
97
98
|
- omitting `version` does **not** hide gated items
|
|
98
99
|
|
|
99
|
-
`POST /announcements/:id/acknowledge`, `POST /announcements/:id/impression`, and `POST /announcements/:id/click` use the same visibility rules as pending/feed (schedule, expiry, platform, min build, `audienceType`, and `matchAudience`). All three return **404** when the announcement is not visible to the caller, so targeted IDs cannot be confirmed by writing event rows. Click checks visibility before action
|
|
100
|
+
`POST /announcements/:id/acknowledge`, `POST /announcements/:id/impression`, and `POST /announcements/:id/click` use the same visibility rules as pending/feed (schedule, expiry, platform, min build, `audienceType`, and `matchAudience`). All three return **404** when the announcement is not visible to the caller, so targeted IDs cannot be confirmed by writing event rows. Impression and click bodies are validated with strict Zod schemas (`{ platform? }` and `{ action: "primaryAction", platform? }`); unknown keys or invalid values return **400** `Validation failed` with per-field messages in `meta.fields`. Click validates `platform` first, then checks visibility before validating `action` and the CTA. Returns **400** when `action` is not `"primaryAction"`, the announcement has no `primaryAction`, or an explicit body `platform` is not `ios` / `android` / `web` (omit `platform` to use query/user-agent resolution). Each click inserts a new row (not idempotent). Acknowledge is idempotent per `(userId, announcementId, version)`.
|
|
100
101
|
|
|
101
102
|
## Admin routes
|
|
102
103
|
|
|
@@ -104,9 +105,12 @@ Admin CRUD is on `/announcements` via `modelRouter`. Custom actions:
|
|
|
104
105
|
|
|
105
106
|
- `GET /announcements/config` — returns `{ data: { defaultAcknowledgementPolicy } }` from the plugin constructor (admin only; defaults to `"dismiss-only"` when omitted). Used to pre-fill the admin editor acknowledgement policy field.
|
|
106
107
|
- `GET /announcements/overview` — paginated admin dashboard with per-announcement metrics and aggregate totals. Access uses `adminOverviewPermissions` (defaults to `IsAdmin`). Query: `page` (default `1`), `limit` (default `20`, max `100`).
|
|
108
|
+
- `POST /announcements/import-release` — idempotently imports up to 100 announcements keyed by release product/version/channel plus slug. Admin auth or the configured `uploadToken` bearer token is required. New items default to draft; request `publish: true` to publish them. The example deployment's token is `terreno-example-announcement-upload`, seeded by Terraform.
|
|
107
109
|
- `POST /announcements/:id/publish` — draft → published
|
|
108
110
|
- `POST /announcements/:id/archive` — published → archived
|
|
109
111
|
|
|
112
|
+
See [Announcement release packs](announcement-release-packs.md) for the Markdown/frontmatter format, JSON request shape, version rules, and draft/live behavior.
|
|
113
|
+
|
|
110
114
|
### `GET /announcements/overview`
|
|
111
115
|
|
|
112
116
|
Admin-only list for launch dashboards. Includes **all** lifecycle statuses (`draft`, `published`, `archived`). Soft-deleted announcements and event rows are excluded.
|
|
@@ -222,6 +222,7 @@ export const todoRouter = modelRouter("/todos", Todo, {
|
|
|
222
222
|
```
|
|
223
223
|
|
|
224
224
|
Do not add `endpoints: (router) => { router.get(...) }` when an action fits.
|
|
225
|
+
Action config supports `allowAnonymous: true` as a per-action override; it does not change authentication for CRUD or sibling actions.
|
|
225
226
|
|
|
226
227
|
## modelRouter array operations
|
|
227
228
|
|
|
@@ -120,7 +120,7 @@ Resolution order for API base URL (`rtk/src/constants.ts`):
|
|
|
120
120
|
| `GEMINI_API_KEY` | `@terreno/ai` | ❌ | — | Yes | server |
|
|
121
121
|
| `OPENAI_API_KEY` | `@terreno/ai` | ❌ | — | Yes | server |
|
|
122
122
|
| `GOOGLE_VERTEX_PROJECT` | `@terreno/ai` | ❌ | — | No | server |
|
|
123
|
-
| `GOOGLE_VERTEX_LOCATION` | `@terreno/ai` | ❌ |
|
|
123
|
+
| `GOOGLE_VERTEX_LOCATION` | `@terreno/ai` | ❌ | `global` | No | server |
|
|
124
124
|
| `GOOGLE_VERTEX_ALLOWED_MODELS` | `@terreno/ai` | ❌ | all | No | server |
|
|
125
125
|
| `CLAUDE_KEY` | tooling | ❌ | — | Yes | tooling |
|
|
126
126
|
| `LANGFUSE_SECRET_KEY` | `@terreno/ai` | ❌ | — | Yes | server |
|
|
@@ -239,6 +239,7 @@ secrets and `PUBLIC_API_URL` / `COMMS_WEBHOOK_PUBLIC_URL` as above. See
|
|
|
239
239
|
| Variable | Read by | Required | Default | Secret | Scope |
|
|
240
240
|
|----------|---------|----------|---------|--------|-------|
|
|
241
241
|
| `ADMIN_SPA_ENABLED` | example-backend | ❌ | — | No | server |
|
|
242
|
+
| `ANNOUNCEMENTS_UPLOAD_TOKEN` | example-backend `AnnouncementsApp` | ❌ | example deploy: `terreno-example-announcement-upload` | Yes outside the example app | server |
|
|
242
243
|
| `ADMIN_SPA_DIST_DIR` | example-backend Dockerfile | ❌ | — | No | server |
|
|
243
244
|
| `ADMIN_SPA_DEV_PROXY` | admin-spa plugin | ❌ | — | No | server |
|
|
244
245
|
| `ADMIN_SPA_BACKEND_URL` | admin-spa tests | ❌ | — | No | tooling |
|
|
@@ -63,7 +63,7 @@ New code can also use OpenFeature React hooks (`useBooleanFlagValue`, `useString
|
|
|
63
63
|
|
|
64
64
|
## OpenFeature integration
|
|
65
65
|
|
|
66
|
-
`FeatureFlagsApp` registers an OpenFeature **server** provider (`MongoFeatureFlagProvider`) on a dedicated domain (default `"feature-flags"`) so the global default provider is unchanged. The authenticated bulk endpoint **`GET {basePath}/flagConfiguration`** returns a `FlagConfiguration`-compatible map (suitable for `TypedInMemoryProvider` on the client). The legacy **`GET {basePath}/evaluate`** response shape is unchanged but is **deprecated**: responses include `Deprecation: true` and a `Sunset` HTTP-date header (~90 days). Prefer `useTerrenoFeatureFlags` / `useFeatureFlags` from `@terreno/rtk`, or migrate direct HTTP callers to `/flagConfiguration`.
|
|
66
|
+
`FeatureFlagsApp` registers an OpenFeature **server** provider (`MongoFeatureFlagProvider`) on a dedicated domain (default `"feature-flags"`) so the global default provider is unchanged. The authenticated bulk endpoint **`GET {basePath}/flagConfiguration`** returns a `FlagConfiguration`-compatible map (suitable for `TypedInMemoryProvider` on the client). Disabled **boolean** flags are included and resolve to `off`, so a client can tell a turned-off flag from one that was never created. Disabled variant flags and archived flags are omitted. The legacy **`GET {basePath}/evaluate`** response shape is unchanged but is **deprecated**: responses include `Deprecation: true` and a `Sunset` HTTP-date header (~90 days). Prefer `useTerrenoFeatureFlags` / `useFeatureFlags` from `@terreno/rtk`, or migrate direct HTTP callers to `/flagConfiguration`.
|
|
67
67
|
|
|
68
68
|
### `defaultVariant` on `FeatureFlag`
|
|
69
69
|
|
|
@@ -4,9 +4,9 @@ React Native UI component library (a large component library). Layout (Box, Page
|
|
|
4
4
|
|
|
5
5
|
## Key exports
|
|
6
6
|
|
|
7
|
-
- Layout: `Box`, `Page`, `SplitPage`, `Card`, `DashboardGrid`
|
|
7
|
+
- Layout: `Box`, `Page`, `SplitPage`, `Card`, `ChartCard`, `DashboardGrid`, `DashboardGridItem`
|
|
8
8
|
- Forms: `TextField`, `SelectField`, `DateTimeField`, `CheckBox`
|
|
9
|
-
- Display: `Text`, `Heading`, `Badge`, `DataTable`, `LineChart`, `BarChart`, `AreaChart`, `DonutChart`
|
|
9
|
+
- Display: `Text`, `Heading`, `Badge`, `DataTable`, `Scorecard`, `SparklineChart`, `LineChart`, `BarChart`, `AreaChart`, `DonutChart`
|
|
10
10
|
- Actions: `Button`, `IconButton`, `Link`
|
|
11
11
|
- Feedback: `Spinner`, `Modal`, `Toast`
|
|
12
12
|
- Notifications: `NotificationBell`, `NotificationInbox`, `NotificationPreferences`
|
|
@@ -32,11 +32,11 @@ supported and is convenient when startup cost is not material:
|
|
|
32
32
|
import {Box, DataTable, Icon} from "@terreno/ui";
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
-
Heavy optional widgets (`GPTChat`, `EmojiSelector`, `MarkdownEditor`, consent flows, `LineChart`, `BarChart`, `AreaChart`, `DonutChart`, and related admin tools) are
|
|
35
|
+
Heavy optional widgets (`GPTChat`, `EmojiSelector`, `MarkdownEditor`, consent flows, `ChartCard`, `Scorecard`, `SparklineChart`, `LineChart`, `BarChart`, `AreaChart`, `DonutChart`, and related admin tools) are
|
|
36
36
|
re-exported from the root entry through lazy boundaries. Importing them from `@terreno/ui` stays type-compatible, but
|
|
37
37
|
their implementation modules load on first render instead of during the initial root import. `DashboardGrid` stays eager.
|
|
38
|
-
|
|
39
|
-
`
|
|
38
|
+
`MarkdownView` and `DataTable` header info defer `react-native-markdown-display`;
|
|
39
|
+
`EmojiSelector` defers `emoji-datasource` until open.
|
|
40
40
|
|
|
41
41
|
For the smallest cold-start graph, keep using subpaths for screens that only need a few primitives (for example
|
|
42
42
|
`import {Button} from "@terreno/ui/Button"`).
|
|
@@ -60,21 +60,72 @@ const customStyle: StyleProp<ViewStyle> = {
|
|
|
60
60
|
- Ensures type compatibility when passing styles to @terreno/ui components
|
|
61
61
|
- Simplifies imports (one package instead of two)
|
|
62
62
|
|
|
63
|
-
### DashboardGrid
|
|
63
|
+
### DashboardGrid and DashboardGridItem
|
|
64
64
|
|
|
65
65
|
Eager layout-only wrapping grid. Default columns `{sm: 1, md: 2, lg: 3}`. Children stay caller-supplied `Card`s. Cell width is `(rowWidth - gap × (columns - 1)) / columns` so flex `gap` does not wrap extra columns.
|
|
66
|
+
Wrap a child in `DashboardGridItem` when it must span more than one responsive column.
|
|
67
|
+
Plain children remain one column.
|
|
66
68
|
|
|
67
69
|
```tsx
|
|
68
|
-
<DashboardGrid>
|
|
70
|
+
<DashboardGrid columns={{sm: 1, md: 2, lg: 4}}>
|
|
71
|
+
<DashboardGridItem span={{sm: 1, md: 2, lg: 2}}>
|
|
72
|
+
<Card>
|
|
73
|
+
<LineChart data={points} legendLabel="Signups" />
|
|
74
|
+
</Card>
|
|
75
|
+
</DashboardGridItem>
|
|
69
76
|
<Card>
|
|
70
77
|
<LineChart data={points} legendLabel="Signups" />
|
|
71
78
|
</Card>
|
|
72
79
|
</DashboardGrid>
|
|
73
80
|
```
|
|
74
81
|
|
|
82
|
+
### ChartCard
|
|
83
|
+
|
|
84
|
+
Card chrome for a chart or table: title, optional filter summary, and an optional period
|
|
85
|
+
badge. Pass `onPeriodPress` to make the badge a button; omit it for display-only copy.
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
<ChartCard title="Cost by Device" periodLabel="Last 30 days">
|
|
89
|
+
<DonutChart data={points} />
|
|
90
|
+
</ChartCard>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Scorecard
|
|
94
|
+
|
|
95
|
+
Compact KPI tile with a title, formatted value, and optional current/comparison
|
|
96
|
+
sparkline. Numeric values use `formatValue`; string values render unchanged.
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
<Scorecard
|
|
100
|
+
title="Cost"
|
|
101
|
+
value={569}
|
|
102
|
+
formatValue={(value) => `$${value}`}
|
|
103
|
+
sparklineData={current}
|
|
104
|
+
comparisonData={previous}
|
|
105
|
+
/>
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### SparklineChart
|
|
109
|
+
|
|
110
|
+
Plot-only line chart for compact metrics. `data` draws the current solid line;
|
|
111
|
+
`comparisonData` draws a dotted previous-period line on the same scale. It has no axes,
|
|
112
|
+
grid, legend, tooltip row, or empty-state copy.
|
|
113
|
+
|
|
114
|
+
```tsx
|
|
115
|
+
<SparklineChart
|
|
116
|
+
data={[{label: "Mon", value: 3}, {label: "Tue", value: 5}]}
|
|
117
|
+
comparisonData={[{label: "Mon", value: 4}, {label: "Tue", value: 4}]}
|
|
118
|
+
/>
|
|
119
|
+
```
|
|
120
|
+
|
|
75
121
|
### LineChart
|
|
76
122
|
|
|
77
|
-
|
|
123
|
+
Line chart drawn with `react-native-svg`. Keep the simple `data` + `legendLabel` path for
|
|
124
|
+
one series, or pass named `series` for multiple lines and legend items.
|
|
125
|
+
`comparisonData` adds a dotted previous-period overlay on the shared scale. Empty data
|
|
126
|
+
shows `emptyText` (default `"No data"`). `loading` shows a `Spinner`.
|
|
127
|
+
Set `xTickPolicy` to `auto` (default), `rotate`, or `truncate`. `title`,
|
|
128
|
+
`periodLabel`, and `onPeriodPress` use the same header chrome as `ChartCard`.
|
|
78
129
|
|
|
79
130
|
```tsx
|
|
80
131
|
<LineChart
|
|
@@ -88,7 +139,10 @@ Hit targets use `Box` `onClick`, so testIDs are `{testID}.point.{index}-clickabl
|
|
|
88
139
|
|
|
89
140
|
### BarChart
|
|
90
141
|
|
|
91
|
-
|
|
142
|
+
Bar chart on the same owned-SVG contract as `LineChart` (empty, loading, legend,
|
|
143
|
+
tooltip, tick policy, and header shortcuts). `comparisonData` paints lighter bars behind
|
|
144
|
+
the current period. When `series` is passed in this facade, its first entry supplies the
|
|
145
|
+
bars and legend; grouped bars remain future work.
|
|
92
146
|
|
|
93
147
|
```tsx
|
|
94
148
|
<BarChart
|
|
@@ -101,6 +155,8 @@ Single-series bar chart on the same owned-SVG contract as `LineChart` (empty, lo
|
|
|
101
155
|
### AreaChart
|
|
102
156
|
|
|
103
157
|
Filled area plus line on the same owned-SVG contract as `LineChart`.
|
|
158
|
+
Named `series` render one fill/line pair and legend item each; `comparisonData` stays a
|
|
159
|
+
dotted line rather than a filled area.
|
|
104
160
|
|
|
105
161
|
```tsx
|
|
106
162
|
<AreaChart
|
|
@@ -112,11 +168,16 @@ Filled area plus line on the same owned-SVG contract as `LineChart`.
|
|
|
112
168
|
|
|
113
169
|
### DonutChart
|
|
114
170
|
|
|
115
|
-
One slice per `{label, value}` point. Per-slice `color` overrides the theme paint.
|
|
171
|
+
One slice per `{label, value}` point. Per-slice `color` overrides the theme paint.
|
|
172
|
+
Legend rows show label + percent by default; customize with `formatShare`.
|
|
173
|
+
`centerValue` and `centerTitle` put an aggregate in the donut hole. `title` /
|
|
174
|
+
`periodLabel` use `ChartCard` chrome (`legendLabel` remains ignored).
|
|
116
175
|
|
|
117
176
|
```tsx
|
|
118
177
|
<DonutChart
|
|
119
178
|
data={[{label: "Open", value: 3}, {color: "#112233", label: "Closed", value: 5}]}
|
|
179
|
+
centerValue="8"
|
|
180
|
+
centerTitle="Total"
|
|
120
181
|
testID="status-donut"
|
|
121
182
|
/>
|
|
122
183
|
```
|
|
@@ -189,6 +250,14 @@ Buttons automatically size to their content unless `fullWidth` is specified:
|
|
|
189
250
|
|
|
190
251
|
Internally, Button sets `alignSelf: 'flex-start'` when `fullWidth={false}` to prevent stretching in column layouts.
|
|
191
252
|
|
|
253
|
+
### TextField grow and maxHeight
|
|
254
|
+
|
|
255
|
+
`grow` makes a multiline field expand with its content. Pair it with `maxHeight` (pixels) to cap the expansion; past the cap the content scrolls.
|
|
256
|
+
|
|
257
|
+
```tsx
|
|
258
|
+
<TextArea grow maxHeight={200} onChange={setNotes} value={notes} />
|
|
259
|
+
```
|
|
260
|
+
|
|
192
261
|
### TextField password visibility
|
|
193
262
|
|
|
194
263
|
`type="password"` masks the value and renders a show/hide eye control at the end of the field.
|
|
@@ -295,7 +364,15 @@ Streaming chat surface for `@terreno/ai`. Histories, messages, submit, and optio
|
|
|
295
364
|
|
|
296
365
|
Pass `mascot` when the app owns a character. Terreno ships none. The node renders only while `currentMessages` is empty, centered in the chat panel above the suggested prompts. The empty hero uses the message viewport height as a minimum so short content stays centered, while taller mascots still scroll. Streaming feedback shares that centered hero instead of creating a second pane. Omit the prop for the default empty chat.
|
|
297
366
|
|
|
298
|
-
The composer row (attachment picker, tools, input, Send) is vertically centered, so controls stay aligned with the input as it grows. The attachment cell is omitted when `onAttachFiles` is not provided.
|
|
367
|
+
The composer row (attachment picker, tools, input, Send) is vertically centered, so controls stay aligned with the input as it grows. The composer input grows with its text up to 200px, then scrolls. The attachment cell is omitted when `onAttachFiles` is not provided.
|
|
368
|
+
|
|
369
|
+
Each history row keeps rename and delete in a three-dot overflow menu (`gpt-history-menu-{id}`), anchored with `DropdownPanel`. Rename uses an outlined pencil in the dark secondary text color. Titles truncate before the menu trigger, so the trigger stays aligned at any sidebar width.
|
|
370
|
+
|
|
371
|
+
"Scroll to bottom" appears only when content sits more than 100px below the viewport. It never shows in an empty chat.
|
|
372
|
+
|
|
373
|
+
Image content parts render copy-image and (web) download actions. The message copy button on an image-only reply copies the image, not text. On web the image is written to the clipboard as PNG; on native it uses `expo-clipboard` `setImageAsync` for `data:` URLs.
|
|
374
|
+
|
|
375
|
+
The attach control (`FilePickerButton`) opens an anchored dropdown with **Photo Library** and **Document**. On web both open a browser file input from the press itself and return `data:` URLs, so attachments survive a reload; on native they use `expo-image-picker` and `expo-document-picker`.
|
|
299
376
|
|
|
300
377
|
```tsx
|
|
301
378
|
<GPTChat
|
|
@@ -320,9 +397,17 @@ images once per mount.
|
|
|
320
397
|
### SplitPage
|
|
321
398
|
|
|
322
399
|
Master-detail layout. Pass `listViewData` plus `renderListViewItem` for the list, and
|
|
323
|
-
`renderContent` for the detail pane. On large screens both panes stay visible. On small
|
|
400
|
+
`renderContent` or children for the detail pane. On large screens both panes stay visible. On small
|
|
324
401
|
screens the detail replaces the list until the user goes back.
|
|
325
402
|
|
|
403
|
+
`desktopChildrenMinWidth` opts into a minimum pixel width for each desktop child when there
|
|
404
|
+
are one or two children. `narrowViewportChildLabels` opts into a labeled full-width pager on
|
|
405
|
+
the narrow viewport. `narrowBelowWidth` uses that viewport when the window is at or below the
|
|
406
|
+
given pixel width; when omitted, the narrow viewport follows `isNarrowViewport()`. These props
|
|
407
|
+
are web only; the native `SplitPage` ignores them. See `SplitPageProps` for when each prop
|
|
408
|
+
applies and what is ignored.
|
|
409
|
+
`IconButton`'s `backgroundOpacity` tints only that button's background.
|
|
410
|
+
|
|
326
411
|
```typescript
|
|
327
412
|
import {SplitPage, Text} from "@terreno/ui";
|
|
328
413
|
|
|
@@ -354,6 +439,12 @@ Buttons use a scale animation by default. Set `pressAnimation="opacity"` for an
|
|
|
354
439
|
|
|
355
440
|
Disabled and loading buttons use a non-interactive pressable regardless of the selected animation.
|
|
356
441
|
|
|
442
|
+
### Toast
|
|
443
|
+
|
|
444
|
+
`TerrenoProvider` mounts the toast container. Call `useToast()` for `success`, `info`, `warn`, `error`, `show`, `hide`, and `catch`.
|
|
445
|
+
|
|
446
|
+
On web, that container is portaled to `document.body` (`position: fixed`, `z-index: 999999`, `pointerEvents: "box-none"`), so a toast stays above an open `Modal` and its backdrop. The web container is sized with `100%` rather than the measured window, so statically exported pages (where `Dimensions` reports 0×0 at build time) still center the toast on screen. Native iOS and Android keep the in-tree absolute container sized to the window.
|
|
447
|
+
|
|
357
448
|
## Authentication Components
|
|
358
449
|
|
|
359
450
|
### SocialLoginButton
|
|
@@ -627,7 +718,7 @@ import {
|
|
|
627
718
|
mediaQuery,
|
|
628
719
|
mediaQueryLargerThan,
|
|
629
720
|
mediaQuerySmallerThan,
|
|
630
|
-
|
|
721
|
+
isNarrowViewport,
|
|
631
722
|
} from "@terreno/ui";
|
|
632
723
|
|
|
633
724
|
// Read the current breakpoint
|
|
@@ -645,9 +736,9 @@ if (mediaQuerySmallerThan("lg")) {
|
|
|
645
736
|
console.log("Smaller than large");
|
|
646
737
|
}
|
|
647
738
|
|
|
648
|
-
//
|
|
649
|
-
if (
|
|
650
|
-
console.info("
|
|
739
|
+
// Current window is below the desktop breakpoint
|
|
740
|
+
if (isNarrowViewport()) {
|
|
741
|
+
console.info("Narrow viewport");
|
|
651
742
|
}
|
|
652
743
|
``````
|
|
653
744
|
|
|
@@ -673,7 +764,7 @@ Web (desktop staff):
|
|
|
673
764
|
|
|
674
765
|
On web, `sm` (320) and `md` (375) still classify widths below 1024 so layouts can remain accessible.
|
|
675
766
|
|
|
676
|
-
`
|
|
767
|
+
`isNarrowViewport()` is true below the supported desktop floor: native width < 1024 (`xl`), web width < 1024 (`lg`). `isMobileDevice()` is the same check and is deprecated in favor of `isNarrowViewport`.
|
|
677
768
|
|
|
678
769
|
Responsive `Box` direction props update automatically when the window resizes or a device rotates:
|
|
679
770
|
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Tasks: Dashboard chart parity
|
|
2
|
+
|
|
3
|
+
IP: [dashboard-chart-parity.md](../implementationPlans/dashboard-chart-parity.md)
|
|
4
|
+
**Follows:** https://github.com/TerrenoLabs/terreno/issues/1175
|
|
5
|
+
**Roadmap issue:** set after IP approval
|
|
6
|
+
|
|
7
|
+
**Feature profile:** false (full IP)
|
|
8
|
+
|
|
9
|
+
## Phase 1 — Tracer (scorecard)
|
|
10
|
+
|
|
11
|
+
- [x] **Task 1.1**: `SparklineChart` + dotted `comparisonData`
|
|
12
|
+
- Delivers: public plot-only sparkline; solid current series; dotted comparison aligned on `label`; no axes/grid/legend/tooltip row; empty input does not throw
|
|
13
|
+
- Files: `ui/src/SparklineChart.tsx`, `ui/src/SparklineChart.test.tsx`, `ui/src/charts/paths.ts` (reuse line path), `ui/src/Common.ts` (TypeDoc props), `ui/src/index.tsx` / `heavyOptionalExports.tsx`
|
|
14
|
+
- Blocked by: none
|
|
15
|
+
- Skills: `terreno-ui`, `update-docs`
|
|
16
|
+
- Docs: stub `SparklineChart` on `docs/reference/ui.md` Display list
|
|
17
|
+
- Acceptance: bun tests — N points → path present; comparison path present when `comparisonData` set; empty data shows nothing / emptyText if provided; `ui` compile green
|
|
18
|
+
|
|
19
|
+
- [x] **Task 1.2**: `Scorecard` + `ChartCard`
|
|
20
|
+
- Delivers: KPI tile with title, formatted value, optional sparkline; `ChartCard` with title, period badge, optional `onPeriodPress`, optional `filterSummary`
|
|
21
|
+
- Files: `ui/src/Scorecard.tsx`, `ui/src/Scorecard.test.tsx`, `ui/src/ChartCard.tsx`, `ui/src/ChartCard.test.tsx`, lazy exports
|
|
22
|
+
- Blocked by: 1.1
|
|
23
|
+
- Skills: `terreno-ui`, `verify-ui-changes`, `update-docs`
|
|
24
|
+
- Docs: stub both on `docs/reference/ui.md`
|
|
25
|
+
- Acceptance: bun tests — value + title render; sparkline mounts when `sparklineData` set; badge press fires; period badge has no press handler when `onPeriodPress` omitted
|
|
26
|
+
|
|
27
|
+
- [x] **Task 1.3**: Scorecard demo + first golden
|
|
28
|
+
- Delivers: demo story; gallery fixture `scorecard-sparkline-comparison` (five KPI tiles, static data); PNG golden
|
|
29
|
+
- Files: `demo/stories/Scorecard.stories.tsx`, `demo/stories/ChartCard.stories.tsx`, `demo/demoConfig.tsx`, `demo/chartVisual/fixtureCatalog.ts`, `demo/chartVisual/fixtures.tsx`, `demo/rendered-snapshots/scorecard-sparkline-comparison.png`
|
|
30
|
+
- Blocked by: 1.2
|
|
31
|
+
- Skills: `terreno-ui`, `review-chart-visuals`, `verify-ui-changes`
|
|
32
|
+
- Docs: `docs/how-to/compare-chart-rendered-snapshots.md` lists the new id
|
|
33
|
+
- Acceptance: fixture catalog test includes the id; `bun run ui:charts:compare` green for it; story registered
|
|
34
|
+
|
|
35
|
+
## Phase 2 — Cartesian additive facades
|
|
36
|
+
|
|
37
|
+
- [x] **Task 2.1**: `series` + `comparisonData` on `LineChart` / `AreaChart`
|
|
38
|
+
- Delivers: multi-series lines from `series`; single `data` still works; dotted comparison overlay; per-series legend; markers stay
|
|
39
|
+
- Files: `ui/src/LineChart.tsx`, `ui/src/AreaChart.tsx`, matching tests, `ui/src/charts/scales.ts` (shared x domain across series)
|
|
40
|
+
- Blocked by: 1.2
|
|
41
|
+
- Skills: `terreno-ui`, `update-docs`
|
|
42
|
+
- Docs: `docs/reference/ui.md` Line/Area props
|
|
43
|
+
- Acceptance: bun tests — three `series` → three paths + three legend labels; `{label,value}[]` only still draws one series; comparison path exists when set
|
|
44
|
+
|
|
45
|
+
- [x] **Task 2.2**: `xTickPolicy` + bar comparison + header shortcuts
|
|
46
|
+
- Delivers: `"auto"` rotates when labels would truncate, stays horizontal for ≤7 categories; `BarChart` accepts `series` (grouped later if >1 — this slice: first series only unless a second is comparison) and `comparisonData`; `title` / `periodLabel` / `onPeriodPress` on cartesian facades
|
|
47
|
+
- Files: `ui/src/BarChart.tsx`, `ui/src/LineChart.tsx`, `ui/src/charts/layout.ts`, `ui/src/BarChart.test.tsx`, `ui/src/LineChart.test.tsx`
|
|
48
|
+
- Blocked by: 2.1
|
|
49
|
+
- Skills: `terreno-ui`
|
|
50
|
+
- Docs: tick policy in `docs/explanation/charts.md`
|
|
51
|
+
- Acceptance: bun tests — 14 long date labels with `auto` → rotated tick style; 7 weekday labels stay horizontal; facade title renders when set and `ChartCard` is not wrapping (documented exclusive use)
|
|
52
|
+
|
|
53
|
+
- [x] **Task 2.3**: Cartesian goldens
|
|
54
|
+
- Delivers: fixtures `bar-time-rotated-ticks`, `line-three-series`, `bar-day-of-week`
|
|
55
|
+
- Files: `demo/chartVisual/**`, `demo/rendered-snapshots/*.png`
|
|
56
|
+
- Blocked by: 2.2
|
|
57
|
+
- Skills: `review-chart-visuals`, `verify-ui-changes`
|
|
58
|
+
- Docs: compare-how-to fixture table
|
|
59
|
+
- Acceptance: `bun run ui:charts:compare` green for the three ids
|
|
60
|
+
|
|
61
|
+
## Phase 3 — Donut center and share legend
|
|
62
|
+
|
|
63
|
+
- [x] **Task 3.1**: Donut hole + percent legend
|
|
64
|
+
- Delivers: `centerTitle`, `centerValue`; legend `swatch + label + formatShare` (default percent of total); 100% single-slice still draws a ring + center
|
|
65
|
+
- Files: `ui/src/DonutChart.tsx`, `ui/src/DonutChart.test.tsx`
|
|
66
|
+
- Blocked by: 1.2
|
|
67
|
+
- Skills: `terreno-ui`, `update-docs`
|
|
68
|
+
- Docs: `docs/reference/ui.md` Donut props
|
|
69
|
+
- Acceptance: bun tests — center copy present; legend includes `%`; one-slice data still has one path; existing slice color override still works
|
|
70
|
+
|
|
71
|
+
- [x] **Task 3.2**: Donut goldens
|
|
72
|
+
- Delivers: `donut-center-and-share`, `donut-single-slice`
|
|
73
|
+
- Files: `demo/chartVisual/**`, goldens
|
|
74
|
+
- Blocked by: 3.1
|
|
75
|
+
- Skills: `review-chart-visuals`
|
|
76
|
+
- Docs: compare-how-to
|
|
77
|
+
- Acceptance: compare green for both ids
|
|
78
|
+
|
|
79
|
+
## Phase 4 — Spanning grid
|
|
80
|
+
|
|
81
|
+
- [x] **Task 4.1**: `DashboardGridItem` spans
|
|
82
|
+
- Delivers: eager `DashboardGridItem` with `span` per breakpoint; default span 1; children without a wrapper keep current equal-column behavior
|
|
83
|
+
- Files: `ui/src/DashboardGrid.tsx` and/or `ui/src/DashboardGridItem.tsx`, `ui/src/DashboardGrid.test.tsx`, `ui/src/index.tsx` (eager)
|
|
84
|
+
- Blocked by: 1.2
|
|
85
|
+
- Skills: `terreno-ui`, `update-docs`
|
|
86
|
+
- Docs: `docs/reference/ui.md` Layout
|
|
87
|
+
- Acceptance: bun tests — span 2 in a 4-column row yields ~2× cell width minus gap; unwrapped children still fill 1 column
|
|
88
|
+
|
|
89
|
+
- [ ] **Task 4.2**: `hows-it-going-dashboard` chart composition
|
|
90
|
+
- Delivers: hard fixture matching the reference layout (5 scorecards, cost/conv bar, 3-line imp share, DOW bar, two donuts). Table slot may be a `ChartCard` placeholder until 5.2
|
|
91
|
+
- Files: `demo/chartVisual/fixtures.tsx`, `fixtureCatalog.ts`, golden
|
|
92
|
+
- Blocked by: 2.3, 3.2, 4.1
|
|
93
|
+
- Skills: `review-chart-visuals`, `verify-ui-changes`
|
|
94
|
+
- Docs: how-to example rewritten to this composition
|
|
95
|
+
- Acceptance: compare green; fixture uses `Scorecard` / `ChartCard` / `DashboardGridItem`, not ad-hoc `Box` widths only
|
|
96
|
+
|
|
97
|
+
## Phase 5 — Table chrome
|
|
98
|
+
|
|
99
|
+
- [ ] **Task 5.1**: Header groups, footer rows, range status, wrapLines
|
|
100
|
+
- Delivers: `headerGroups` second header row; `footerRows` sticky totals; range copy `start–end of total` with existing pagination; `wrapLines={2}` wraps cell text inside `rowHeight` (virtualization unchanged)
|
|
101
|
+
- Files: `ui/src/DataTable.tsx`, `ui/src/DataTable.test.tsx`, `ui/src/Pagination.tsx` (if range lives there), `ui/src/Common.ts`
|
|
102
|
+
- Blocked by: 4.1
|
|
103
|
+
- Skills: `terreno-ui`, `update-docs`
|
|
104
|
+
- Docs: `docs/reference/ui.md` DataTable; pointer from charts how-to
|
|
105
|
+
- Acceptance: bun tests — grouped header text visible; footer row present; range text `1–50 of 452` with page 1 size 50 total 452; pinned column scroll still syncs with groups/footer; wrapLines 2 does not change `rowHeight`
|
|
106
|
+
|
|
107
|
+
- [ ] **Task 5.2**: Table card in the hard fixture
|
|
108
|
+
- Delivers: one `ChartCard` + `DataTable` in `hows-it-going-dashboard` (Search Terms or Ad Group Check-in static rows, totals, period headers)
|
|
109
|
+
- Files: `demo/chartVisual/fixtures.tsx`, update golden
|
|
110
|
+
- Blocked by: 5.1, 4.2
|
|
111
|
+
- Skills: `review-chart-visuals`, `verify-ui-changes`
|
|
112
|
+
- Docs: how-to shows table in the same grid
|
|
113
|
+
- Acceptance: compare green after intentional golden update; wrapped first-column names visible in the PNG
|
|
114
|
+
|
|
115
|
+
## Phase 6 — Packaging and docs
|
|
116
|
+
|
|
117
|
+
- [ ] **Task 6.1**: Lazy root + import regression
|
|
118
|
+
- Delivers: `Scorecard`, `SparklineChart`, `ChartCard`, chart facades stay on `heavyOptionalExports`; `DashboardGrid` / `DashboardGridItem` eager; root import does not evaluate chart files
|
|
119
|
+
- Files: `ui/src/lazyBoundaries/heavyOptionalExports.tsx`, `ui/src/index.tsx`, `ui/src/RootImportRegression.test.tsx`, `docs/reference/ui-performance.md`
|
|
120
|
+
- Blocked by: 4.1, 5.1, 2.1, 3.1
|
|
121
|
+
- Skills: `terreno-ui`, `update-docs`
|
|
122
|
+
- Docs: performance page lists new lazy exports
|
|
123
|
+
- Acceptance: `RootImportRegression` fails if new chart files load with the root import; subpath imports work
|
|
124
|
+
|
|
125
|
+
- [ ] **Task 6.2**: Diátaxis + changelog + rules
|
|
126
|
+
- Delivers: how-to rewritten for easy-build composition; explanation covers scorecard, comparison stroke, additive facades vs grammar; reference complete; changelog fragment; ui rule export list; TypeDoc
|
|
127
|
+
- Files: `docs/how-to/charts-and-dashboards.md`, `docs/explanation/charts.md`, `docs/reference/ui.md`, `docs/how-to/README.md`, `.rulesync/rules/ui/00-ui.mdc`, `changelog/unreleased/dashboard-chart-parity.md`; `bun run website:generate` if needed
|
|
128
|
+
- Blocked by: 6.1, 5.2
|
|
129
|
+
- Skills: `update-docs`, `docs-audit`, `terreno-ui`
|
|
130
|
+
- Docs: the files listed above
|
|
131
|
+
- Acceptance: a stranger can copy the how-to into a screen that resembles the reference; explanation states grammar is not required; changelog `category: Added`; existing single-series how-to steps still valid as a subset
|
|
132
|
+
|
|
133
|
+
## Verification mapping
|
|
134
|
+
|
|
135
|
+
| Criterion | Method |
|
|
136
|
+
|-----------|--------|
|
|
137
|
+
| Scorecard + sparkline + comparison | bun tests + `scorecard-sparkline-comparison` PNG |
|
|
138
|
+
| Multi-series line | bun tests + `line-three-series` PNG |
|
|
139
|
+
| Rotated vs horizontal ticks | bun tests + `bar-time-rotated-ticks` / `bar-day-of-week` |
|
|
140
|
+
| Donut center + share | bun tests + donut goldens |
|
|
141
|
+
| Grid spans + reference layout | `hows-it-going-dashboard` PNG + grid unit tests |
|
|
142
|
+
| Table chrome | bun tests + table region of hard golden |
|
|
143
|
+
| Facade compatibility | existing `*Chart.test.tsx` stay green |
|
|
144
|
+
| Lazy charts | `RootImportRegression` |
|
|
145
|
+
| Frontend proof | `verify-ui-changes` on demo gallery; artifacts on the implementation PR |
|