@validation-os/dashboard 0.4.0 → 0.5.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/index.d.ts +209 -28
- package/dist/index.js +730 -357
- package/dist/index.js.map +1 -1
- package/dist/styles.css +971 -0
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -3,6 +3,142 @@ import { ReactNode } from 'react';
|
|
|
3
3
|
import { Collection, AnyRecord, Relation } from '@validation-os/core';
|
|
4
4
|
import { Progress, MoverKind, TrajectoryPoint } from '@validation-os/core/derivation';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Everything the instance passes — config only, never secrets. The API base
|
|
8
|
+
* path points at where the Clerk-gated `@validation-os/api` routes are mounted;
|
|
9
|
+
* the backend label (from the connector config) shows in the topbar; branding
|
|
10
|
+
* and the signed-in user are optional cosmetics. Auth is the instance's Clerk
|
|
11
|
+
* provider that this renders within — no credential reaches the package.
|
|
12
|
+
*/
|
|
13
|
+
interface DashboardConfig {
|
|
14
|
+
/** Where the API is mounted (default `/api`). */
|
|
15
|
+
basePath?: string;
|
|
16
|
+
/** Topbar backend indicator, e.g. "Firestore · doshi-crm". */
|
|
17
|
+
backendLabel?: string;
|
|
18
|
+
/** Optional product branding for the sidebar. */
|
|
19
|
+
branding?: {
|
|
20
|
+
/** Product name shown next to the mark (default "Validation-OS"). */
|
|
21
|
+
name?: string;
|
|
22
|
+
/** One or two letters for the square mark (default "V"). */
|
|
23
|
+
initials?: string;
|
|
24
|
+
/** A logo image for the square mark; overrides `initials` when set. */
|
|
25
|
+
logoUrl?: string;
|
|
26
|
+
};
|
|
27
|
+
/** Optional agent label shown in the topbar, e.g. "Claude Code". */
|
|
28
|
+
agentLabel?: string;
|
|
29
|
+
/** The signed-in user, for the topbar avatar + name. */
|
|
30
|
+
user?: {
|
|
31
|
+
name?: string;
|
|
32
|
+
caption?: string;
|
|
33
|
+
};
|
|
34
|
+
/** Restrict/reorder the registers shown; defaults to all, in order. */
|
|
35
|
+
registers?: Collection[];
|
|
36
|
+
}
|
|
37
|
+
interface ValidationOSDashboardProps {
|
|
38
|
+
config?: DashboardConfig;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The entire styled dashboard as one mountable app (spec OPS-1280): the frame —
|
|
42
|
+
* sidebar composing register nav + live counts, topbar with the backend
|
|
43
|
+
* indicator and user — and the register views (browse → drawer → understanding)
|
|
44
|
+
* it composes from the package's own bricks. Navigation is owned here, not the
|
|
45
|
+
* host router: the active register lives in client state (synced to the URL
|
|
46
|
+
* hash), so the instance mounts this at one route and wires no routing. Styled
|
|
47
|
+
* by the package's own token sheet — the instance imports `styles.css` once and
|
|
48
|
+
* builds no UI.
|
|
49
|
+
*/
|
|
50
|
+
declare function ValidationOSDashboard({ config }: ValidationOSDashboardProps): react.JSX.Element;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Pure presentation logic for the dashboard's visual primitives — status→tone,
|
|
54
|
+
* risk→fraction/level, the sparkline path, and count/number formatting. Kept
|
|
55
|
+
* free of React and the DOM so the mapping that drives every pill, bar and
|
|
56
|
+
* sparkline is unit-tested at this seam (spec story 13), and the components stay
|
|
57
|
+
* thin wrappers over it.
|
|
58
|
+
*/
|
|
59
|
+
/** A pill/fill tone — maps onto the `--vos-good/warn/crit/accent` tokens. */
|
|
60
|
+
type Tone = "good" | "warn" | "crit" | "accent" | "neutral";
|
|
61
|
+
/**
|
|
62
|
+
* A record's Status → a pill tone. Live/concluded read good, testing/running
|
|
63
|
+
* read warn, invalidated/rejected read crit; everything else (Proposed, and any
|
|
64
|
+
* status we don't recognise) stays neutral rather than guessing a colour. Case-
|
|
65
|
+
* and whitespace-insensitive.
|
|
66
|
+
*/
|
|
67
|
+
declare function statusTone(status: string | null | undefined): Tone;
|
|
68
|
+
/** Risk thresholds — a belief is critical at ≥60, watch at ≥30 (prototype). */
|
|
69
|
+
declare const RISK_CRIT = 60;
|
|
70
|
+
declare const RISK_WARN = 30;
|
|
71
|
+
/** Risk (0–100) → a tone for the bar fill and the number. */
|
|
72
|
+
declare function riskLevel(risk: number): Tone;
|
|
73
|
+
/** Risk (0–100) → the bar's fill fraction (0–1), clamped. */
|
|
74
|
+
declare function riskFraction(risk: number): number;
|
|
75
|
+
/** Confidence (signed) → the tone for its number: negative reads crit. */
|
|
76
|
+
declare function confidenceTone(confidence: number): Tone;
|
|
77
|
+
/** A signed number rendered with an explicit sign: `+6`, `-3`, `0`. */
|
|
78
|
+
declare function formatSigned(n: number): string;
|
|
79
|
+
/** A count for the nav/tiles, thousands-separated. */
|
|
80
|
+
declare function formatCount(n: number): string;
|
|
81
|
+
/**
|
|
82
|
+
* An SVG polyline `d` for a sparkline over `values`, fit to a `width`×`height`
|
|
83
|
+
* box with a 2px inset. The vertical domain defaults to the data's own range
|
|
84
|
+
* (with 0 always included, so a signed series keeps its baseline meaningful);
|
|
85
|
+
* pass `min`/`max` to pin it — e.g. −100…100 for Confidence. Returns "" for
|
|
86
|
+
* fewer than two points (nothing to draw).
|
|
87
|
+
*/
|
|
88
|
+
declare function sparklinePath(values: number[], width: number, height: number, min?: number, max?: number): string;
|
|
89
|
+
|
|
90
|
+
/** A colored status pill — tone from the status, the label shown verbatim. */
|
|
91
|
+
declare function StatusPill({ status }: {
|
|
92
|
+
status: string | null | undefined;
|
|
93
|
+
}): react.JSX.Element;
|
|
94
|
+
/**
|
|
95
|
+
* A risk bar (0–100) plus its number, both toned by threshold: a longer, redder
|
|
96
|
+
* bar means a riskier belief the eye should land on first (spec story 5).
|
|
97
|
+
*/
|
|
98
|
+
declare function RiskBar({ risk }: {
|
|
99
|
+
risk: number;
|
|
100
|
+
}): react.JSX.Element;
|
|
101
|
+
/**
|
|
102
|
+
* A signed confidence reading: a tiny sparkline of its trajectory when a
|
|
103
|
+
* history is available, always the +/- number (negative reads crit) — so a row
|
|
104
|
+
* shows both where a belief stands and, when known, which way it is trending
|
|
105
|
+
* (spec story 6). With no history the number stands alone; the list endpoint
|
|
106
|
+
* carries no per-row series, so the in-row sparkline appears wherever a caller
|
|
107
|
+
* does have one (e.g. the drawer trajectory uses `Sparkline` directly).
|
|
108
|
+
*/
|
|
109
|
+
declare function ConfidenceCell({ confidence, history, }: {
|
|
110
|
+
confidence: number;
|
|
111
|
+
history?: number[];
|
|
112
|
+
}): react.JSX.Element;
|
|
113
|
+
/**
|
|
114
|
+
* A signed sparkline over `values`. A zero baseline is drawn whenever the domain
|
|
115
|
+
* spans it, so a line dipping below zero reads as a belief losing ground. The
|
|
116
|
+
* last point gets a dot. `fill` shades the area under the line. Pure geometry
|
|
117
|
+
* comes from `sparklinePath`/`sparklineY`.
|
|
118
|
+
*/
|
|
119
|
+
declare function Sparkline({ values, width, height, min, max, tone, fill, ariaLabel, }: {
|
|
120
|
+
values: number[];
|
|
121
|
+
width?: number;
|
|
122
|
+
height?: number;
|
|
123
|
+
min?: number;
|
|
124
|
+
max?: number;
|
|
125
|
+
tone?: Tone;
|
|
126
|
+
fill?: boolean;
|
|
127
|
+
ariaLabel?: string;
|
|
128
|
+
}): react.JSX.Element | null;
|
|
129
|
+
/**
|
|
130
|
+
* A glanceable stat tile — a label, a big number, and an optional sub-caption.
|
|
131
|
+
* Becomes a button when `onClick` is given (counts that double as nav). Pure
|
|
132
|
+
* presentation; the caller supplies the value.
|
|
133
|
+
*/
|
|
134
|
+
declare function StatTile({ label, value, sub, onClick, active, }: {
|
|
135
|
+
label: string;
|
|
136
|
+
value: number;
|
|
137
|
+
sub?: ReactNode;
|
|
138
|
+
onClick?: () => void;
|
|
139
|
+
active?: boolean;
|
|
140
|
+
}): react.JSX.Element;
|
|
141
|
+
|
|
6
142
|
type Counts = Partial<Record<Collection, number>>;
|
|
7
143
|
interface UseCountsResult {
|
|
8
144
|
counts: Counts | null;
|
|
@@ -17,24 +153,43 @@ interface UseCountsResult {
|
|
|
17
153
|
*/
|
|
18
154
|
declare function useCounts(basePath?: string): UseCountsResult;
|
|
19
155
|
|
|
156
|
+
interface RegisterNavProps {
|
|
157
|
+
/** The active register, highlighted with a clear active state (spec story 7). */
|
|
158
|
+
active: Collection;
|
|
159
|
+
/** Called when a register is chosen. */
|
|
160
|
+
onSelect: (register: Collection) => void;
|
|
161
|
+
/** Live per-register counts; a middle dot shows until they load. */
|
|
162
|
+
counts?: Counts | null;
|
|
163
|
+
/** Restrict/reorder the registers shown; defaults to all, grouped. */
|
|
164
|
+
registers?: Collection[];
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* The sidebar register nav — every register with its live count and an active
|
|
168
|
+
* state, grouped into the register set and reference data (spec story 7). A
|
|
169
|
+
* presentational brick: the caller owns which register is active and supplies
|
|
170
|
+
* the counts. The assembled `<ValidationOSDashboard />` composes this; it's also
|
|
171
|
+
* exported so anyone building their own surface can reuse it (the second level
|
|
172
|
+
* of entry). Styled with the package's own token sheet — no host Tailwind.
|
|
173
|
+
*/
|
|
174
|
+
declare function RegisterNav({ active, onSelect, counts, registers, }: RegisterNavProps): react.JSX.Element;
|
|
175
|
+
|
|
20
176
|
interface RegisterCountsProps {
|
|
21
177
|
counts: Counts;
|
|
22
178
|
/** Optional caption under the tiles (e.g. the backend the numbers came from). */
|
|
23
179
|
caption?: string;
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
*/
|
|
29
|
-
hrefFor?: (register: Collection) => string;
|
|
180
|
+
/** Optional click handler per register — makes each tile a nav button. */
|
|
181
|
+
onSelect?: (register: Collection) => void;
|
|
182
|
+
/** The active register, highlighted when tiles are nav. */
|
|
183
|
+
active?: Collection | null;
|
|
30
184
|
}
|
|
31
185
|
/**
|
|
32
186
|
* A glanceable grid of stat tiles — one per register, the register's row count
|
|
33
|
-
* as the hero number.
|
|
34
|
-
*
|
|
35
|
-
* the
|
|
187
|
+
* as the hero number. A brick kept for a counts landing; the assembled app
|
|
188
|
+
* surfaces the same counts in the sidebar nav. Presentational: the caller reads
|
|
189
|
+
* the counts (server-side, through the adapter) and hands them in. Rendered in
|
|
190
|
+
* the package's own token sheet — no host Tailwind.
|
|
36
191
|
*/
|
|
37
|
-
declare function RegisterCounts({ counts, caption,
|
|
192
|
+
declare function RegisterCounts({ counts, caption, onSelect, active, }: RegisterCountsProps): react.JSX.Element;
|
|
38
193
|
|
|
39
194
|
interface RegisterTableProps {
|
|
40
195
|
register: Collection;
|
|
@@ -46,9 +201,11 @@ interface RegisterTableProps {
|
|
|
46
201
|
}
|
|
47
202
|
/**
|
|
48
203
|
* A list table for one register — a row per record, the register's key fields
|
|
49
|
-
* as columns
|
|
50
|
-
*
|
|
51
|
-
*
|
|
204
|
+
* as columns. Assumptions read their state at a glance: a colored Status pill, a
|
|
205
|
+
* signed Confidence, and a threshold-toned Risk bar (spec stories 4–6). Which
|
|
206
|
+
* cells render as pills/bars/sparklines is declared on the column (`kind`), so
|
|
207
|
+
* the treatment stays testable at the columns seam and this component stays a
|
|
208
|
+
* dumb renderer. Presentational: the caller supplies the rows.
|
|
52
209
|
*/
|
|
53
210
|
declare function RegisterTable({ register, records, onRowClick, selectedId, }: RegisterTableProps): react.JSX.Element;
|
|
54
211
|
|
|
@@ -71,14 +228,15 @@ interface RecordDrawerProps {
|
|
|
71
228
|
children?: ReactNode;
|
|
72
229
|
}
|
|
73
230
|
/**
|
|
74
|
-
* A record drawer that reads and edits, and wires relations.
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
* `DrawerShell
|
|
231
|
+
* A record drawer that reads and edits, and wires relations. The derived
|
|
232
|
+
* numbers lead as a visually distinct "computed — not editable" hero (spec
|
|
233
|
+
* stories 4/10): a bordered box, each number big and mono, Confidence toned by
|
|
234
|
+
* sign and Risk by threshold, with a "Why?" reveal opening the understanding
|
|
235
|
+
* layer in the same accent/pill language (story 11). Editing recomputes those
|
|
236
|
+
* numbers server-side on save; a concurrent edit surfaces as a gentle, jargon-
|
|
237
|
+
* free prompt with a re-fetch path (story 12). In read mode the drawer hosts
|
|
238
|
+
* the relation editor (`children`). Chrome is shared via `DrawerShell`; styled
|
|
239
|
+
* with the package's own token sheet, no host Tailwind.
|
|
82
240
|
*/
|
|
83
241
|
declare function RecordDrawer({ register, record, loading, error, open, onClose, basePath, onChanged, children, }: RecordDrawerProps): react.JSX.Element;
|
|
84
242
|
|
|
@@ -118,11 +276,12 @@ declare function RelationEditor({ register, recordId, basePath, onLinked, }: Rel
|
|
|
118
276
|
/**
|
|
119
277
|
* The understanding layer behind the Confidence "Why?" (OPS-1276): which
|
|
120
278
|
* experiments move the number (ranked by push) and how close each running
|
|
121
|
-
* experiment is to concluding, the goal/direct evidence that also moves it,
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* shared derivation module.
|
|
125
|
-
*
|
|
279
|
+
* experiment is to concluding, the goal/direct evidence that also moves it, and
|
|
280
|
+
* Confidence over time. It lazy-loads the readings + experiments registers (it
|
|
281
|
+
* only mounts when the Reveal is open), then derives everything through the
|
|
282
|
+
* shared derivation module. Restyled into the drawer's accent/pill language
|
|
283
|
+
* (spec story 11): signed push tracks and a signed trajectory sparkline, in the
|
|
284
|
+
* package's own token sheet — no host Tailwind.
|
|
126
285
|
*/
|
|
127
286
|
declare function UnderstandingPanel({ assumption, basePath, }: {
|
|
128
287
|
assumption: AnyRecord;
|
|
@@ -184,6 +343,8 @@ interface RegisterBrowserProps {
|
|
|
184
343
|
register: Collection;
|
|
185
344
|
/** API base path (default `/api`). */
|
|
186
345
|
basePath?: string;
|
|
346
|
+
/** A one-line description under the register title (spec story 7/9). */
|
|
347
|
+
subtitle?: string;
|
|
187
348
|
}
|
|
188
349
|
/**
|
|
189
350
|
* The browse-create-edit surface for one register: a list table that opens a
|
|
@@ -195,7 +356,7 @@ interface RegisterBrowserProps {
|
|
|
195
356
|
* everywhere. The thin host app renders this with a `register` — that's the
|
|
196
357
|
* whole page.
|
|
197
358
|
*/
|
|
198
|
-
declare function RegisterBrowser({ register, basePath }: RegisterBrowserProps): react.JSX.Element;
|
|
359
|
+
declare function RegisterBrowser({ register, basePath, subtitle, }: RegisterBrowserProps): react.JSX.Element;
|
|
199
360
|
|
|
200
361
|
interface UseListResult {
|
|
201
362
|
records: AnyRecord[] | null;
|
|
@@ -333,6 +494,14 @@ declare function hasEdits(register: Collection, original: AnyRecord, draft: Draf
|
|
|
333
494
|
* headers are plain language and derived numbers are shown, never hidden.
|
|
334
495
|
*/
|
|
335
496
|
|
|
497
|
+
/**
|
|
498
|
+
* How a cell renders. `text` is plain formatted text; `status` is a colored
|
|
499
|
+
* pill; `risk` is a threshold-toned bar + number; `confidence` is a signed
|
|
500
|
+
* number (with a sparkline when a trajectory is available). Keeping this on the
|
|
501
|
+
* column — not branching by key in the table — is what makes the visual
|
|
502
|
+
* treatment declarative and testable at this seam (spec story 13).
|
|
503
|
+
*/
|
|
504
|
+
type CellKind = "text" | "status" | "risk" | "confidence";
|
|
336
505
|
interface ColumnDef {
|
|
337
506
|
/** Stable key; also the default field read from the record. */
|
|
338
507
|
key: string;
|
|
@@ -344,6 +513,8 @@ interface ColumnDef {
|
|
|
344
513
|
align?: "left" | "right";
|
|
345
514
|
/** Marks a column whose value is computed, never hand-typed (spec story 4). */
|
|
346
515
|
derived?: boolean;
|
|
516
|
+
/** How the cell renders; defaults to `text`. */
|
|
517
|
+
kind?: CellKind;
|
|
347
518
|
}
|
|
348
519
|
/** The columns to render for a register. */
|
|
349
520
|
declare function columnsFor(register: Collection): ColumnDef[];
|
|
@@ -415,5 +586,15 @@ declare const REGISTER_LABEL: Record<Collection, string>;
|
|
|
415
586
|
declare const REGISTER_SINGULAR: Record<Collection, string>;
|
|
416
587
|
/** The order tiles read left-to-right, top-to-bottom. */
|
|
417
588
|
declare const REGISTER_ORDER: Collection[];
|
|
589
|
+
/** A one-line description shown under each register's title (spec story 9). */
|
|
590
|
+
declare const REGISTER_SUBTITLE: Record<Collection, string>;
|
|
591
|
+
/** A single-glyph icon per register for the sidebar nav (matches the prototype
|
|
592
|
+
* feel; a glyph, not an icon font, so nothing external is pulled in). */
|
|
593
|
+
declare const REGISTER_ICON: Record<Collection, string>;
|
|
594
|
+
/** The sidebar groups the registers into the register set and reference data. */
|
|
595
|
+
declare const REGISTER_GROUPS: {
|
|
596
|
+
label: string;
|
|
597
|
+
registers: Collection[];
|
|
598
|
+
}[];
|
|
418
599
|
|
|
419
|
-
export { CONFLICT_MESSAGE, type ColumnDef, type Counts, type Draft, type ExperimentView, type FieldEditor, type FieldKind, type FormField, type LinkArgs, type LinkChoice, type OtherMover, REGISTER_LABEL, REGISTER_ORDER, REGISTER_SINGULAR, RecordDrawer, type RecordDrawerProps, RecordForm, type RecordFormProps, RegisterBrowser, type RegisterBrowserProps, RegisterCounts, type RegisterCountsProps, RegisterTable, type RegisterTableProps, RelationEditor, type RelationEditorProps, type SaveResult, type Understanding, UnderstandingPanel, type UseCountsResult, type UseCreateResult, type UseLinkResult, type UseListResult, type UseRecordResult, type UseUpdateResult, buildPatch, buildUnderstanding, cellValue, columnsFor, draftFrom, editableFields, emptyDraft, formFieldsFor, formatValue, hasEdits, interpretSave, linkChoicesFrom, missingRequired, primaryLabel, toCreatePayload, useCounts, useCreate, useLink, useList, useRecord, useUpdate };
|
|
600
|
+
export { CONFLICT_MESSAGE, type CellKind, type ColumnDef, ConfidenceCell, type Counts, type DashboardConfig, type Draft, type ExperimentView, type FieldEditor, type FieldKind, type FormField, type LinkArgs, type LinkChoice, type OtherMover, REGISTER_GROUPS, REGISTER_ICON, REGISTER_LABEL, REGISTER_ORDER, REGISTER_SINGULAR, REGISTER_SUBTITLE, RISK_CRIT, RISK_WARN, RecordDrawer, type RecordDrawerProps, RecordForm, type RecordFormProps, RegisterBrowser, type RegisterBrowserProps, RegisterCounts, type RegisterCountsProps, RegisterNav, type RegisterNavProps, RegisterTable, type RegisterTableProps, RelationEditor, type RelationEditorProps, RiskBar, type SaveResult, Sparkline, StatTile, StatusPill, type Tone, type Understanding, UnderstandingPanel, type UseCountsResult, type UseCreateResult, type UseLinkResult, type UseListResult, type UseRecordResult, type UseUpdateResult, ValidationOSDashboard, type ValidationOSDashboardProps, buildPatch, buildUnderstanding, cellValue, columnsFor, confidenceTone, draftFrom, editableFields, emptyDraft, formFieldsFor, formatCount, formatSigned, formatValue, hasEdits, interpretSave, linkChoicesFrom, missingRequired, primaryLabel, riskFraction, riskLevel, sparklinePath, statusTone, toCreatePayload, useCounts, useCreate, useLink, useList, useRecord, useUpdate };
|