@happyvertical/smrt-svelte 0.55.23 → 0.55.24

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.
Files changed (85) hide show
  1. package/AGENTS.md +2 -2
  2. package/README.md +2 -2
  3. package/agents/overview-surfaces.md +108 -4
  4. package/agents/screens.md +171 -5
  5. package/dist/components/overview/index.d.ts +4 -1
  6. package/dist/components/overview/index.d.ts.map +1 -1
  7. package/dist/components/overview/index.js +3 -0
  8. package/dist/components/overview/record.d.ts +51 -0
  9. package/dist/components/overview/record.d.ts.map +1 -0
  10. package/dist/components/overview/record.js +146 -0
  11. package/dist/components/overview/server.d.ts +4 -0
  12. package/dist/components/overview/server.d.ts.map +1 -1
  13. package/dist/components/overview/server.js +4 -0
  14. package/dist/components/overview/types.d.ts +12 -0
  15. package/dist/components/overview/types.d.ts.map +1 -1
  16. package/dist/components/overview/widgets/LineItemsWidget.svelte +144 -0
  17. package/dist/components/overview/widgets/LineItemsWidget.svelte.d.ts +6 -0
  18. package/dist/components/overview/widgets/LineItemsWidget.svelte.d.ts.map +1 -0
  19. package/dist/components/overview/widgets/RecordListWidget.svelte +67 -3
  20. package/dist/components/overview/widgets/RecordListWidget.svelte.d.ts.map +1 -1
  21. package/dist/components/overview/widgets/RecordTable.svelte +167 -0
  22. package/dist/components/overview/widgets/RecordTable.svelte.d.ts +21 -0
  23. package/dist/components/overview/widgets/RecordTable.svelte.d.ts.map +1 -0
  24. package/dist/components/overview/widgets/RelatedWidget.svelte +117 -0
  25. package/dist/components/overview/widgets/RelatedWidget.svelte.d.ts +6 -0
  26. package/dist/components/overview/widgets/RelatedWidget.svelte.d.ts.map +1 -0
  27. package/dist/components/overview/widgets/core.d.ts +13 -2
  28. package/dist/components/overview/widgets/core.d.ts.map +1 -1
  29. package/dist/components/overview/widgets/core.js +43 -2
  30. package/dist/components/overview/widgets/data.d.ts +97 -8
  31. package/dist/components/overview/widgets/data.d.ts.map +1 -1
  32. package/dist/components/overview/widgets/line-totals.d.ts +48 -0
  33. package/dist/components/overview/widgets/line-totals.d.ts.map +1 -0
  34. package/dist/components/overview/widgets/line-totals.js +91 -0
  35. package/dist/components/overview/widgets/record-loaders.d.ts +63 -0
  36. package/dist/components/overview/widgets/record-loaders.d.ts.map +1 -0
  37. package/dist/components/overview/widgets/record-loaders.js +150 -0
  38. package/dist/components/overview/widgets/record-table.d.ts +45 -0
  39. package/dist/components/overview/widgets/record-table.d.ts.map +1 -0
  40. package/dist/components/overview/widgets/record-table.js +201 -0
  41. package/dist/components/overview/widgets/record-widgets.d.ts +27 -0
  42. package/dist/components/overview/widgets/record-widgets.d.ts.map +1 -0
  43. package/dist/components/overview/widgets/record-widgets.js +100 -0
  44. package/dist/components/screens/DetailScreen.svelte +105 -6
  45. package/dist/components/screens/DetailScreen.svelte.d.ts.map +1 -1
  46. package/dist/components/screens/EditForm.svelte +39 -3
  47. package/dist/components/screens/EditForm.svelte.d.ts.map +1 -1
  48. package/dist/components/screens/RecipeScreens.svelte +18 -18
  49. package/dist/components/screens/RecipeScreens.svelte.d.ts.map +1 -1
  50. package/dist/components/screens/RecordHeader.svelte +238 -0
  51. package/dist/components/screens/RecordHeader.svelte.d.ts +26 -0
  52. package/dist/components/screens/RecordHeader.svelte.d.ts.map +1 -0
  53. package/dist/components/screens/RecordScreen.svelte +511 -0
  54. package/dist/components/screens/RecordScreen.svelte.d.ts +5 -0
  55. package/dist/components/screens/RecordScreen.svelte.d.ts.map +1 -0
  56. package/dist/components/screens/columns.d.ts +29 -0
  57. package/dist/components/screens/columns.d.ts.map +1 -0
  58. package/dist/components/screens/columns.js +115 -0
  59. package/dist/components/screens/fields.d.ts +12 -2
  60. package/dist/components/screens/fields.d.ts.map +1 -1
  61. package/dist/components/screens/fields.js +37 -5
  62. package/dist/components/screens/index.d.ts +7 -2
  63. package/dist/components/screens/index.d.ts.map +1 -1
  64. package/dist/components/screens/index.js +6 -1
  65. package/dist/components/screens/record-header.d.ts +51 -0
  66. package/dist/components/screens/record-header.d.ts.map +1 -0
  67. package/dist/components/screens/record-header.js +118 -0
  68. package/dist/components/screens/relations.d.ts +49 -0
  69. package/dist/components/screens/relations.d.ts.map +1 -0
  70. package/dist/components/screens/relations.js +98 -0
  71. package/dist/components/screens/roles.d.ts +56 -0
  72. package/dist/components/screens/roles.d.ts.map +1 -0
  73. package/dist/components/screens/roles.js +174 -0
  74. package/dist/components/screens/types.d.ts +182 -1
  75. package/dist/components/screens/types.d.ts.map +1 -1
  76. package/dist/components/screens/values.d.ts +23 -1
  77. package/dist/components/screens/values.d.ts.map +1 -1
  78. package/dist/components/screens/values.js +80 -4
  79. package/dist/i18n/strings.overview.d.ts +25 -0
  80. package/dist/i18n/strings.overview.d.ts.map +1 -1
  81. package/dist/i18n/strings.overview.js +25 -0
  82. package/dist/i18n/strings.screens.d.ts +8 -0
  83. package/dist/i18n/strings.screens.d.ts.map +1 -1
  84. package/dist/i18n/strings.screens.js +8 -0
  85. package/package.json +16 -16
@@ -0,0 +1,167 @@
1
+ <script lang="ts">
2
+ /**
3
+ * RecordTable (internal): the compact records table shared by the `records`
4
+ * widget (with columns), each tab of `related`, and `lineItems`. Columns
5
+ * render by role (identifier monospace, status badge, date, money and
6
+ * quantity right-aligned); the first column links to the row. The footer is
7
+ * smrt-ui `DataTable`'s `summaries`: the count in the first column and the
8
+ * sums of the money and quantity columns. A sum covers EVERY matching record
9
+ * only when the host sent `summaryValues` or every record is loaded;
10
+ * otherwise that cell stays empty rather than adding up a partial page.
11
+ */
12
+
13
+ import type {
14
+ DataTableColumn,
15
+ DataTableSummary,
16
+ } from '@happyvertical/smrt-ui/data';
17
+ import { DataTable } from '@happyvertical/smrt-ui/data';
18
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
19
+ import { Badge } from '@happyvertical/smrt-ui/ui';
20
+ import { M } from '../../../i18n/strings.overview.js';
21
+ import { safeHref } from '../markdown.js';
22
+ import type {
23
+ RecordListColumn,
24
+ RecordListRow,
25
+ RecordStatusTone,
26
+ } from './data.js';
27
+ import {
28
+ formatRecordCell,
29
+ isSummedColumn,
30
+ isSummedRole,
31
+ statusTone,
32
+ } from './record-table.js';
33
+
34
+ interface Props {
35
+ rows: readonly RecordListRow[];
36
+ columns: readonly RecordListColumn[];
37
+ locale: string;
38
+ currency?: string;
39
+ timeZone?: string;
40
+ /** Render the footer (count and sums). */
41
+ summaries?: boolean;
42
+ /** Footer values over every matching record, keyed by column (money in minor units). */
43
+ summaryValues?: Readonly<Record<string, number>>;
44
+ /** Every matching record; more than `rows.length` means the rows are a page. */
45
+ total?: number | null;
46
+ statusTones?: Readonly<Record<string, RecordStatusTone>>;
47
+ /** Accessible name of the table. */
48
+ label?: string;
49
+ }
50
+
51
+ let {
52
+ rows,
53
+ columns,
54
+ locale,
55
+ currency,
56
+ timeZone,
57
+ summaries = false,
58
+ summaryValues,
59
+ total = null,
60
+ statusTones,
61
+ label,
62
+ }: Props = $props();
63
+
64
+ const { t } = useI18n();
65
+
66
+ interface TableRow {
67
+ id: string;
68
+ title: string;
69
+ href: string | null;
70
+ cells: Record<string, unknown>;
71
+ }
72
+
73
+ const tableRows = $derived(
74
+ rows.map(
75
+ (row): TableRow => ({
76
+ id: row.id,
77
+ title: row.title,
78
+ href: typeof row.href === 'string' ? safeHref(row.href) : null,
79
+ cells: row.cells ?? {},
80
+ }),
81
+ ),
82
+ );
83
+
84
+ const byKey = $derived(new Map(columns.map((c) => [c.key, c])));
85
+ const firstKey = $derived(columns[0]?.key);
86
+ const complete = $derived(total === null || total <= rows.length);
87
+ const context = $derived({ currency, locale, timeZone });
88
+
89
+ const tableColumns = $derived(
90
+ columns.map(
91
+ (column): DataTableColumn<TableRow> => ({
92
+ id: column.key,
93
+ label: column.label,
94
+ accessor: `cells.${column.key}`,
95
+ ...(isSummedRole(column.role) ? { align: 'right' as const } : {}),
96
+ summaryFormat: (value: number) =>
97
+ formatRecordCell(column, value, context),
98
+ }),
99
+ ),
100
+ );
101
+
102
+ const tableSummaries = $derived.by(() => {
103
+ if (!summaries || columns.length === 0) return undefined;
104
+ const out: Record<string, DataTableSummary<TableRow>> = {};
105
+ const count = total ?? rows.length;
106
+ if (firstKey && !isSummedRole(byKey.get(firstKey)?.role)) {
107
+ out[firstKey] = () =>
108
+ t(M['ui.overview.records.total'], {
109
+ count: new Intl.NumberFormat(locale).format(count),
110
+ });
111
+ }
112
+ for (const column of columns) {
113
+ if (!isSummedColumn(column)) continue;
114
+ const hosted = summaryValues?.[column.key];
115
+ if (typeof hosted === 'number' && Number.isFinite(hosted)) {
116
+ out[column.key] = () => formatRecordCell(column, hosted, context);
117
+ } else if (complete) {
118
+ out[column.key] = 'sum';
119
+ } else {
120
+ out[column.key] = () => '';
121
+ }
122
+ }
123
+ return out;
124
+ });
125
+ </script>
126
+
127
+ {#snippet cell({ column, row, value }: { column: DataTableColumn<TableRow>; row: TableRow; value: unknown })}
128
+ {@const meta = byKey.get(column.id)}
129
+ {@const text = meta ? formatRecordCell(meta, value, context) : String(value ?? '')}
130
+ {@const shown = text === '' && column.id === firstKey ? row.title : text}
131
+ {#if column.id === firstKey && row.href && shown !== ''}
132
+ <a class="smrt-record-table__link" href={row.href}>
133
+ {#if meta?.role === 'identifier'}<code>{shown}</code>{:else}{shown}{/if}
134
+ </a>
135
+ {:else if meta?.role === 'status' && shown !== ''}
136
+ <Badge size="sm" variant={statusTone(value, statusTones)}>{shown}</Badge>
137
+ {:else if meta?.role === 'identifier' && shown !== ''}
138
+ <code>{shown}</code>
139
+ {:else}
140
+ {shown}
141
+ {/if}
142
+ {/snippet}
143
+
144
+ <div class="smrt-record-table" data-smrt-component="RecordTable" role="group" aria-label={label}>
145
+ <DataTable
146
+ data={tableRows}
147
+ columns={tableColumns}
148
+ rowKey="id"
149
+ {cell}
150
+ dense
151
+ size="sm"
152
+ hoverable={false}
153
+ phoneLayout="cards"
154
+ summaries={tableSummaries}
155
+ summaryLabel={t(M['ui.overview.table.total'])}
156
+ />
157
+ </div>
158
+
159
+ <style>
160
+ @layer smrt.reset, smrt.structure, smrt.theme;
161
+ @layer smrt.theme {
162
+ .smrt-record-table { min-inline-size: 0; font-variant-numeric: tabular-nums; }
163
+ .smrt-record-table code { font-family: var(--smrt-font-family-mono); }
164
+ .smrt-record-table__link { color: var(--smrt-color-primary); text-decoration: none; }
165
+ .smrt-record-table__link:hover { text-decoration: underline; }
166
+ }
167
+ </style>
@@ -0,0 +1,21 @@
1
+ import type { RecordListColumn, RecordListRow, RecordStatusTone } from './data.js';
2
+ interface Props {
3
+ rows: readonly RecordListRow[];
4
+ columns: readonly RecordListColumn[];
5
+ locale: string;
6
+ currency?: string;
7
+ timeZone?: string;
8
+ /** Render the footer (count and sums). */
9
+ summaries?: boolean;
10
+ /** Footer values over every matching record, keyed by column (money in minor units). */
11
+ summaryValues?: Readonly<Record<string, number>>;
12
+ /** Every matching record; more than `rows.length` means the rows are a page. */
13
+ total?: number | null;
14
+ statusTones?: Readonly<Record<string, RecordStatusTone>>;
15
+ /** Accessible name of the table. */
16
+ label?: string;
17
+ }
18
+ declare const RecordTable: import("svelte").Component<Props, {}, "">;
19
+ type RecordTable = ReturnType<typeof RecordTable>;
20
+ export default RecordTable;
21
+ //# sourceMappingURL=RecordTable.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecordTable.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/RecordTable.svelte.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,gBAAgB,EACjB,MAAM,WAAW,CAAC;AASnB,UAAU,KAAK;IACb,IAAI,EAAE,SAAS,aAAa,EAAE,CAAC;IAC/B,OAAO,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,wFAAwF;IACxF,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACjD,gFAAgF;IAChF,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACzD,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAoHD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
@@ -0,0 +1,117 @@
1
+ <script lang="ts">
2
+ /**
3
+ * Related widget (#3718 record page): one tab per `children` relation, the
4
+ * tab label carrying the count ("Orders 6"), each tab a records table with
5
+ * role-chosen columns and a totals footer (`RecordTable`), and that tab's
6
+ * one "View all". Relations with no records are hidden; when every one is
7
+ * empty the widget says so once.
8
+ */
9
+ import { Tabs } from '@happyvertical/smrt-ui';
10
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
11
+ import { M } from '../../../i18n/strings.overview.js';
12
+ import { safeHref } from '../markdown.js';
13
+ import type { WidgetComponentProps } from '../types.js';
14
+ import { isRecord, type RelatedWidgetData } from './data.js';
15
+ import RecordTable from './RecordTable.svelte';
16
+ import { readRecordRows, resolveRecordColumns } from './record-table.js';
17
+
18
+ let { data, locale, title }: WidgetComponentProps<RelatedWidgetData> = $props();
19
+
20
+ const { t } = useI18n();
21
+
22
+ const tabs = $derived.by(() => {
23
+ if (!isRecord(data) || !Array.isArray(data.tabs)) return [];
24
+ const out = [];
25
+ for (const tab of data.tabs) {
26
+ if (!isRecord(tab) || typeof tab.key !== 'string') continue;
27
+ const rows = readRecordRows(tab);
28
+ const total =
29
+ typeof tab.total === 'number' && Number.isFinite(tab.total)
30
+ ? tab.total
31
+ : rows.length;
32
+ if (rows.length === 0 || total === 0) continue;
33
+ const columns = resolveRecordColumns([], tab.columns);
34
+ const summaries: Record<string, number> = {};
35
+ if (isRecord(tab.summaries)) {
36
+ for (const [key, value] of Object.entries(tab.summaries)) {
37
+ if (typeof value === 'number' && Number.isFinite(value))
38
+ summaries[key] = value;
39
+ }
40
+ }
41
+ out.push({
42
+ key: tab.key,
43
+ label: typeof tab.label === 'string' ? tab.label : tab.key,
44
+ rows,
45
+ total,
46
+ columns,
47
+ href: typeof tab.href === 'string' ? safeHref(tab.href) : null,
48
+ currency: typeof tab.currency === 'string' ? tab.currency : undefined,
49
+ timeZone: typeof tab.timeZone === 'string' ? tab.timeZone : undefined,
50
+ summaries,
51
+ });
52
+ }
53
+ return out;
54
+ });
55
+
56
+ let selected = $state<string | null>(null);
57
+ const current = $derived(tabs.find((tab) => tab.key === selected) ?? tabs[0]);
58
+ </script>
59
+
60
+ {#if !current}
61
+ <p class="smrt-related__empty" data-smrt-component="RelatedWidget">{t(M['ui.overview.related.empty'])}</p>
62
+ {:else}
63
+ <div class="smrt-related" data-smrt-component="RelatedWidget">
64
+ <Tabs
65
+ tabs={tabs.map((tab) => ({ id: tab.key, label: tab.label, count: tab.total }))}
66
+ active={current.key}
67
+ onchange={(id) => (selected = id)}
68
+ size="sm"
69
+ variant="secondary"
70
+ aria-label={title}
71
+ >
72
+ {#key current.key}
73
+ {#if current.columns.length > 0}
74
+ <RecordTable
75
+ rows={current.rows}
76
+ columns={current.columns}
77
+ {locale}
78
+ label={current.label}
79
+ currency={current.currency}
80
+ timeZone={current.timeZone}
81
+ summaries
82
+ summaryValues={current.summaries}
83
+ total={current.total}
84
+ />
85
+ {:else}
86
+ <ul class="smrt-related__list">
87
+ {#each current.rows as row (row.id)}
88
+ <li>
89
+ {#if row.href && safeHref(row.href)}
90
+ <a href={safeHref(row.href)}>{row.title}</a>
91
+ {:else}
92
+ {row.title}
93
+ {/if}
94
+ </li>
95
+ {/each}
96
+ </ul>
97
+ {/if}
98
+ {#if current.href}
99
+ <p class="smrt-related__foot">
100
+ <a href={current.href}>{t(M['ui.overview.records.view_all'])}</a>
101
+ </p>
102
+ {/if}
103
+ {/key}
104
+ </Tabs>
105
+ </div>
106
+ {/if}
107
+
108
+ <style>
109
+ @layer smrt.reset, smrt.structure, smrt.theme;
110
+ @layer smrt.theme {
111
+ .smrt-related { min-inline-size: 0; }
112
+ .smrt-related__list { display: grid; gap: var(--smrt-spacing-2); margin: var(--smrt-spacing-2) 0 0; padding: 0; list-style: none; }
113
+ .smrt-related__foot { display: flex; justify-content: flex-end; margin: var(--smrt-spacing-2) 0 0; font-size: var(--smrt-typography-body-medium-size, 0.875rem); }
114
+ .smrt-related__foot a { color: var(--smrt-color-primary); }
115
+ .smrt-related__empty { margin: 0; color: var(--smrt-color-on-surface-variant); }
116
+ }
117
+ </style>
@@ -0,0 +1,6 @@
1
+ import type { WidgetComponentProps } from '../types.js';
2
+ import { type RelatedWidgetData } from './data.js';
3
+ declare const RelatedWidget: import("svelte").Component<WidgetComponentProps<RelatedWidgetData>, {}, "">;
4
+ type RelatedWidget = ReturnType<typeof RelatedWidget>;
5
+ export default RelatedWidget;
6
+ //# sourceMappingURL=RelatedWidget.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelatedWidget.svelte.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/RelatedWidget.svelte.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAY,KAAK,iBAAiB,EAAE,MAAM,WAAW,CAAC;AA4F7D,QAAA,MAAM,aAAa,6EAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -14,7 +14,12 @@
14
14
  import { type WidgetRegistry } from '../registry.js';
15
15
  import type { WidgetDefinition, WidgetLoad } from '../types.js';
16
16
  import type { ChartWidgetData, MetricWidgetData, RecordListWidgetData, ShortcutsWidgetData } from './data.js';
17
- /** Metric: one number from a model. Options: model, measure, field, filter. */
17
+ /**
18
+ * Metric: one number from a model. Options: model, measure, field, filter,
19
+ * parentField. Under `filter: 'parent'` the FK to the record is
20
+ * `parentField` when given (and `field` is then the measured field, a `sum`
21
+ * of `totalAmount`), else `field` (a count).
22
+ */
18
23
  export declare function createMetricWidget(load: WidgetLoad<MetricWidgetData>): WidgetDefinition<MetricWidgetData>;
19
24
  /** Chart option-schema version: 2 added the `table`, `pie` and `stat` styles. */
20
25
  export declare const CHART_OPTIONS_VERSION = 2;
@@ -26,7 +31,13 @@ export declare function migrateChartOptions(options: Record<string, unknown>, fr
26
31
  * the renderer half of the runtime-report chart hints (#3878).
27
32
  */
28
33
  export declare function createChartWidget(load: WidgetLoad<ChartWidgetData>): WidgetDefinition<ChartWidgetData>;
29
- /** Record list: the latest or top records of a model. */
34
+ /**
35
+ * Record list: the latest or top records of a model. `field` names the FK on
36
+ * the model for `filter: 'parent'` (a record view's related-records panel).
37
+ * `columns` (comma-separated field names) renders the rows as a table of
38
+ * their `cells` with a totals footer (`summaries`); the loader receives the
39
+ * option and fills `cells` for those keys.
40
+ */
30
41
  export declare function createRecordListWidget(load: WidgetLoad<RecordListWidgetData>): WidgetDefinition<RecordListWidgetData>;
31
42
  /**
32
43
  * Shortcuts: the section-overview cards. The default widget of a section
@@ -1 +1 @@
1
- {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EAEX,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,WAAW,CAAC;AA2CnB,+EAA+E;AAC/E,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,CAAC,gBAAgB,CAAC,GACjC,gBAAgB,CAAC,gBAAgB,CAAC,CAgBpC;AAED,iFAAiF;AACjF,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,mFAAmF;AACnF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAIhC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,CAAC,eAAe,CAAC,GAChC,gBAAgB,CAAC,eAAe,CAAC,CAiDnC;AAED,yDAAyD;AACzD,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,CAAC,oBAAoB,CAAC,GACrC,gBAAgB,CAAC,oBAAoB,CAAC,CAkCxC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,CAAC,mBAAmB,CAAC,GACpC,gBAAgB,CAAC,mBAAmB,CAAC,CAmBvC;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,IAAI,gBAAgB,CAoBnD;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,GAAE,cAAsC,EAChD,OAAO,GAAE,iBAAsB,EAC/B,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,MAAM,IAAI,CAyBZ"}
1
+ {"version":3,"file":"core.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/core.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAC5E,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EAEX,MAAM,aAAa,CAAC;AAErB,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,mBAAmB,EACpB,MAAM,WAAW,CAAC;AAqEnB;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,UAAU,CAAC,gBAAgB,CAAC,GACjC,gBAAgB,CAAC,gBAAgB,CAAC,CAiBpC;AAED,iFAAiF;AACjF,eAAO,MAAM,qBAAqB,IAAI,CAAC;AAEvC,mFAAmF;AACnF,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAChC,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAIhC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,UAAU,CAAC,eAAe,CAAC,GAChC,gBAAgB,CAAC,eAAe,CAAC,CAiDnC;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,UAAU,CAAC,oBAAoB,CAAC,GACrC,gBAAgB,CAAC,oBAAoB,CAAC,CAqCxC;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,UAAU,CAAC,mBAAmB,CAAC,GACpC,gBAAgB,CAAC,mBAAmB,CAAC,CAmBvC;AAED,sDAAsD;AACtD,wBAAgB,gBAAgB,IAAI,gBAAgB,CAoBnD;AAED,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,MAAM,CAAC,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC;IACtC,KAAK,CAAC,EAAE,UAAU,CAAC,eAAe,CAAC,CAAC;IACpC,OAAO,CAAC,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,GAAE,cAAsC,EAChD,OAAO,GAAE,iBAAsB,EAC/B,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAClC,MAAM,IAAI,CAyBZ"}
@@ -43,6 +43,32 @@ const fieldField = {
43
43
  label: 'ui.overview.opt.field',
44
44
  help: 'ui.overview.opt.field_help',
45
45
  };
46
+ /** The FK to the record under `filter: 'parent'`, when `field` is the measured field. */
47
+ const parentFieldField = {
48
+ key: 'parentField',
49
+ type: 'identifier',
50
+ label: 'ui.overview.opt.parent_field',
51
+ help: 'ui.overview.opt.parent_field_help',
52
+ };
53
+ /**
54
+ * Table columns: field names, comma-separated (`number,status,issuedAt`).
55
+ * Options are flat JSON primitives, so the list travels as text;
56
+ * `parseColumnsOption` reads it.
57
+ */
58
+ const columnsField = {
59
+ key: 'columns',
60
+ type: 'text',
61
+ label: 'ui.overview.opt.columns',
62
+ help: 'ui.overview.opt.columns_help',
63
+ maxLength: 400,
64
+ };
65
+ /** Totals row; unset means on when a money or quantity column exists. */
66
+ const summariesField = {
67
+ key: 'summaries',
68
+ type: 'boolean',
69
+ label: 'ui.overview.opt.summaries',
70
+ help: 'ui.overview.opt.summaries_help',
71
+ };
46
72
  const measureField = (choices) => ({
47
73
  key: 'measure',
48
74
  type: 'enum',
@@ -53,7 +79,12 @@ const measureField = (choices) => ({
53
79
  label: `ui.overview.opt.measure_${value}`,
54
80
  })),
55
81
  });
56
- /** Metric: one number from a model. Options: model, measure, field, filter. */
82
+ /**
83
+ * Metric: one number from a model. Options: model, measure, field, filter,
84
+ * parentField. Under `filter: 'parent'` the FK to the record is
85
+ * `parentField` when given (and `field` is then the measured field, a `sum`
86
+ * of `totalAmount`), else `field` (a count).
87
+ */
57
88
  export function createMetricWidget(load) {
58
89
  return {
59
90
  type: 'metric',
@@ -66,6 +97,7 @@ export function createMetricWidget(load) {
66
97
  measureField(['count', 'sum', 'avg', 'min', 'max']),
67
98
  fieldField,
68
99
  filterField,
100
+ parentFieldField,
69
101
  ],
70
102
  component: MetricWidget,
71
103
  load,
@@ -134,7 +166,13 @@ export function createChartWidget(load) {
134
166
  defaultSpan: 2,
135
167
  };
136
168
  }
137
- /** Record list: the latest or top records of a model. */
169
+ /**
170
+ * Record list: the latest or top records of a model. `field` names the FK on
171
+ * the model for `filter: 'parent'` (a record view's related-records panel).
172
+ * `columns` (comma-separated field names) renders the rows as a table of
173
+ * their `cells` with a totals footer (`summaries`); the loader receives the
174
+ * option and fills `cells` for those keys.
175
+ */
138
176
  export function createRecordListWidget(load) {
139
177
  return {
140
178
  type: 'records',
@@ -145,6 +183,7 @@ export function createRecordListWidget(load) {
145
183
  titleField,
146
184
  modelField,
147
185
  { key: 'sort', type: 'identifier', label: 'ui.overview.opt.sort' },
186
+ fieldField,
148
187
  {
149
188
  key: 'direction',
150
189
  type: 'enum',
@@ -164,6 +203,8 @@ export function createRecordListWidget(load) {
164
203
  max: 20,
165
204
  },
166
205
  filterField,
206
+ columnsField,
207
+ summariesField,
167
208
  ],
168
209
  component: RecordListWidget,
169
210
  load,
@@ -4,6 +4,7 @@
4
4
  * backed by runtime reports, a collection query or the planner's in-browser
5
5
  * source without this package depending on any of them.
6
6
  */
7
+ import type { FieldRole } from '../../screens/roles.js';
7
8
  import type { WidgetValueFormat } from '../format.js';
8
9
  /** `metric`: one number. `money` values are integer minor units. */
9
10
  export interface MetricWidgetData {
@@ -33,19 +34,107 @@ export interface ChartWidgetData {
33
34
  */
34
35
  delta?: number | null;
35
36
  }
36
- /** `records`: the rows to list. The loader chooses the title field. */
37
+ /** Badge tone of a status value (`statusTone`). */
38
+ export type RecordStatusTone = 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info';
39
+ /**
40
+ * One column of a records table: the cell key (a field name of the listed
41
+ * model) and how it renders, by role: `identifier` monospace, `status` a
42
+ * badge, `date` a date without a time, `money` integer minor units as
43
+ * currency, `quantity` a number; anything else as text. `money` and
44
+ * `quantity` columns are right-aligned and summed in the footer.
45
+ */
46
+ export interface RecordListColumn {
47
+ key: string;
48
+ label: string;
49
+ role?: FieldRole;
50
+ /**
51
+ * Sum this column in the footer. Defaults to true for `money` and
52
+ * `quantity`; a unit price sets `false` (a sum of unit prices means
53
+ * nothing).
54
+ */
55
+ summarize?: boolean;
56
+ }
57
+ /** One listed record. `cells` carries the column values, keyed by column key. */
58
+ export interface RecordListRow {
59
+ id: string;
60
+ title: string;
61
+ subtitle?: string;
62
+ meta?: string;
63
+ href?: string;
64
+ /**
65
+ * Column values (`columns`), raw: money in integer minor units, dates as
66
+ * ISO strings (or preformatted text), a party as its display label.
67
+ */
68
+ cells?: Record<string, unknown>;
69
+ }
70
+ /**
71
+ * `records`: the rows to list. The loader chooses the title field. With
72
+ * columns (the widget's `columns` option or `columns` here) the rows render
73
+ * as a table of `cells`; without, as the title list.
74
+ */
37
75
  export interface RecordListWidgetData {
38
- rows: {
39
- id: string;
40
- title: string;
41
- subtitle?: string;
42
- meta?: string;
43
- href?: string;
44
- }[];
76
+ rows: RecordListRow[];
45
77
  /** Total matching records, when more than `rows`. */
46
78
  total?: number;
47
79
  /** Link to the full list. */
48
80
  href?: string;
81
+ /** Column labels and roles; keys not listed here get a humanized label. */
82
+ columns?: RecordListColumn[];
83
+ /** ISO 4217 code of the money cells (default USD). */
84
+ currency?: string;
85
+ /** Time zone a date cell is shown in (default UTC, so SSR and the browser agree). */
86
+ timeZone?: string;
87
+ /**
88
+ * Footer values over EVERY matching record, keyed by column key (money in
89
+ * minor units). Without them the footer sums the loaded rows, and only
90
+ * when they are all the matching records (`total` absent or not more).
91
+ */
92
+ summaries?: Record<string, number>;
93
+ }
94
+ /** One tab of the `related` widget: one relation's records table. */
95
+ export interface RelatedWidgetTab {
96
+ /** The relation's key (`relationKey`, `Order-customerId`). */
97
+ key: string;
98
+ /** Tab label; the widget appends the count ("Orders 6"). */
99
+ label: string;
100
+ rows: RecordListRow[];
101
+ /** Every matching record; the tab count. Defaults to `rows.length`. */
102
+ total?: number;
103
+ columns: RecordListColumn[];
104
+ /** The tab's one "View all" link. */
105
+ href?: string;
106
+ currency?: string;
107
+ timeZone?: string;
108
+ /** Footer values over every matching record (money in minor units). */
109
+ summaries?: Record<string, number>;
110
+ }
111
+ /** `related`: one tab per `children` relation; empty relations are hidden. */
112
+ export interface RelatedWidgetData {
113
+ tabs: RelatedWidgetTab[];
114
+ }
115
+ /**
116
+ * `line-items`: a document's lines (item, quantity, unit price, amount)
117
+ * with a footer, then the totals block from the parent record's own
118
+ * subtotal / discount / tax / total (`totals`), computed from the lines
119
+ * where the record has none, and flagged where they disagree.
120
+ */
121
+ export interface LineItemsWidgetData {
122
+ rows: RecordListRow[];
123
+ columns: RecordListColumn[];
124
+ /** Every line; more than `rows` means the rows are a page. */
125
+ total?: number;
126
+ /** The parent record's own totals, integer minor units. */
127
+ totals?: {
128
+ subtotal?: number | null;
129
+ discount?: number | null;
130
+ tax?: number | null;
131
+ total?: number | null;
132
+ };
133
+ currency?: string;
134
+ /** Footer values over every line (money in minor units). */
135
+ summaries?: Record<string, number>;
136
+ /** Link to every line. */
137
+ href?: string;
49
138
  }
50
139
  /** One page a shortcut links to (#3888). `count` is the record count, when known. */
51
140
  export interface ShortcutEntry {
@@ -1 +1 @@
1
- {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/data.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtD,oEAAoE;AACpE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qFAAqF;AACrF,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,uEAAuE;AACvE,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;IACJ,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6BAA6B;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qFAAqF;AACrF,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,CAAC,aAAa,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,CAAC;CAC3C;AAED,sEAAsE;AACtE,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS;QACd,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,IAAI,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,WAAW,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;KAC7D,EAAE,CAAC;CACL;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,SAAS,kBAAkB,EAAE,EACvC,SAAS,CAAC,EAAE,MAAM,GACjB,mBAAmB,CAoBrB;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzE"}
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/data.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The totals block of a document's line items (#3718 record page), pure and
3
+ * Node-tested. The record's own subtotal / discount / tax / total are
4
+ * authoritative and shown as they are; a missing one is computed from the
5
+ * lines (subtotal = sum of line amounts, total = subtotal - discount + tax).
6
+ * Lines that add up to the recorded total are tax-inclusive and agree.
7
+ * When the record's figures disagree with the lines, the block flags it
8
+ * ("Lines add up to $X") instead of silently overriding either side.
9
+ *
10
+ * Money is integer minor units throughout.
11
+ */
12
+ /** The record's own totals fields (`subtotal` / `discount` / `tax` / `total` roles). */
13
+ export interface LineItemsTotals {
14
+ subtotal?: number | null;
15
+ discount?: number | null;
16
+ tax?: number | null;
17
+ total?: number | null;
18
+ }
19
+ export type LineTotalKey = 'subtotal' | 'discount' | 'tax' | 'total';
20
+ export interface LineTotalLine {
21
+ key: LineTotalKey;
22
+ value: number;
23
+ /** Computed here (the record has no such field or value), not read from the record. */
24
+ computed: boolean;
25
+ }
26
+ export interface LineTotalsMismatch {
27
+ /** The record figure the lines disagree with. */
28
+ field: 'subtotal' | 'total';
29
+ /** The record's value. */
30
+ recorded: number;
31
+ /** What the lines add up to for that figure. */
32
+ lines: number;
33
+ }
34
+ export interface LineTotalsResult {
35
+ /** Subtotal, then discount and tax when present and non-zero, then total. */
36
+ lines: LineTotalLine[];
37
+ /** Sum of the line amounts, `null` when not every line is known. */
38
+ lineSum: number | null;
39
+ mismatch: LineTotalsMismatch | null;
40
+ }
41
+ /** Sum of integer minor-unit amounts; `null` on a non-integer or past the safe range. */
42
+ export declare function sumLineAmounts(amounts: readonly unknown[]): number | null;
43
+ /**
44
+ * The totals block for `record` totals over lines summing to `lineSum`
45
+ * (`null` when the lines are a partial page and the host sent no sum).
46
+ */
47
+ export declare function lineItemTotals(record: LineItemsTotals | undefined, lineSum: number | null): LineTotalsResult;
48
+ //# sourceMappingURL=line-totals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"line-totals.d.ts","sourceRoot":"","sources":["../../../../src/components/overview/widgets/line-totals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wFAAwF;AACxF,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,UAAU,GAAG,KAAK,GAAG,OAAO,CAAC;AAErE,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,YAAY,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,uFAAuF;IACvF,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,iDAAiD;IACjD,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC;IAC5B,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,6EAA6E;IAC7E,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,oEAAoE;IACpE,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,QAAQ,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACrC;AAQD,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,MAAM,GAAG,IAAI,CAUzE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,eAAe,GAAG,SAAS,EACnC,OAAO,EAAE,MAAM,GAAG,IAAI,GACrB,gBAAgB,CA2DlB"}