@happyvertical/smrt-reports 0.57.5 → 0.57.7
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/AGENTS.md +10 -0
- package/dist/chunks/runtime-time-D7JZ1e4J.js +1052 -0
- package/dist/chunks/runtime-time-D7JZ1e4J.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +58 -591
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +28 -4
- package/dist/playground.js +1 -1
- package/dist/playground.js.map +1 -1
- package/dist/recipes.d.ts +21 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/runtime-compiler.d.ts +7 -40
- package/dist/runtime-compiler.d.ts.map +1 -1
- package/dist/runtime-dimensions.d.ts +43 -0
- package/dist/runtime-dimensions.d.ts.map +1 -0
- package/dist/runtime-memory.d.ts +81 -0
- package/dist/runtime-memory.d.ts.map +1 -0
- package/dist/runtime-plan.d.ts +121 -0
- package/dist/runtime-plan.d.ts.map +1 -0
- package/dist/runtime-report.d.ts.map +1 -1
- package/dist/runtime-server.d.ts.map +1 -1
- package/dist/runtime-spec-hash.d.ts +11 -0
- package/dist/runtime-spec-hash.d.ts.map +1 -0
- package/dist/runtime-spec.d.ts +16 -4
- package/dist/runtime-spec.d.ts.map +1 -1
- package/dist/runtime-time.d.ts +41 -0
- package/dist/runtime-time.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +37 -12
- package/dist/spec.d.ts +25 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +2 -0
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/dist/svelte/ReportConfirmation.svelte +97 -24
- package/dist/svelte/ReportConfirmation.svelte.d.ts +20 -0
- package/dist/svelte/ReportConfirmation.svelte.d.ts.map +1 -1
- package/dist/svelte/ReportDetail.svelte +263 -12
- package/dist/svelte/ReportDetail.svelte.d.ts +10 -1
- package/dist/svelte/ReportDetail.svelte.d.ts.map +1 -1
- package/dist/svelte/ReportPicker.svelte +47 -14
- package/dist/svelte/ReportPicker.svelte.d.ts +3 -0
- package/dist/svelte/ReportPicker.svelte.d.ts.map +1 -1
- package/dist/svelte/ReportResult.svelte +216 -35
- package/dist/svelte/ReportResult.svelte.d.ts +8 -2
- package/dist/svelte/ReportResult.svelte.d.ts.map +1 -1
- package/dist/svelte/RuntimeReportWidgetOptions.svelte +9 -1
- package/dist/svelte/RuntimeReportWidgetOptions.svelte.d.ts.map +1 -1
- package/dist/svelte/SavedReportCard.svelte +242 -0
- package/dist/svelte/SavedReportCard.svelte.d.ts +30 -0
- package/dist/svelte/SavedReportCard.svelte.d.ts.map +1 -0
- package/dist/svelte/SavedReports.svelte +206 -33
- package/dist/svelte/SavedReports.svelte.d.ts +13 -0
- package/dist/svelte/SavedReports.svelte.d.ts.map +1 -1
- package/dist/svelte/SavedReportsWidget.svelte +59 -0
- package/dist/svelte/SavedReportsWidget.svelte.d.ts +19 -0
- package/dist/svelte/SavedReportsWidget.svelte.d.ts.map +1 -0
- package/dist/svelte/client.d.ts.map +1 -1
- package/dist/svelte/client.js +3 -0
- package/dist/svelte/describe.d.ts +94 -0
- package/dist/svelte/describe.d.ts.map +1 -0
- package/dist/svelte/describe.js +249 -0
- package/dist/svelte/index.d.ts +4 -1
- package/dist/svelte/index.d.ts.map +1 -1
- package/dist/svelte/index.js +4 -1
- package/dist/svelte/messages.d.ts +48 -0
- package/dist/svelte/messages.d.ts.map +1 -1
- package/dist/svelte/messages.js +59 -11
- package/dist/svelte/playground.d.ts +2 -2
- package/dist/svelte/playground.d.ts.map +1 -1
- package/dist/svelte/playground.js +1 -1
- package/dist/svelte/types.d.ts +2 -0
- package/dist/svelte/types.d.ts.map +1 -1
- package/package.json +13 -9
|
@@ -1,39 +1,104 @@
|
|
|
1
1
|
<script lang="ts">
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Saved reports as a grid of cards, each run for its headline figure. `page`
|
|
4
|
+
* is the Saved reports page (its heading, "New report", whose and archived
|
|
5
|
+
* filters); `section` is the Reports section page's widget (a hint, "New
|
|
6
|
+
* report", "View all"). "New report" asks the page's assistant (smrt-ui
|
|
7
|
+
* `requestAssistant`); with none on the page it says where to ask.
|
|
8
|
+
*/
|
|
9
|
+
import { requestAssistant } from '@happyvertical/smrt-ui/chat';
|
|
10
|
+
import { ConfirmDialog } from '@happyvertical/smrt-ui/feedback';
|
|
11
|
+
import { SegmentedControl, Switch } from '@happyvertical/smrt-ui/forms';
|
|
3
12
|
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
4
|
-
import {
|
|
13
|
+
import { EmptyState } from '@happyvertical/smrt-ui/layout';
|
|
14
|
+
import { Button, Skeleton } from '@happyvertical/smrt-ui/ui';
|
|
5
15
|
import { untrack } from 'svelte';
|
|
16
|
+
import type { ReportSourceInfo } from './describe.js';
|
|
6
17
|
import { M } from './messages.js';
|
|
7
|
-
import { reportError } from './result.js';
|
|
8
|
-
import
|
|
18
|
+
import { reportError, validReportResult } from './result.js';
|
|
19
|
+
import SavedReportCard from './SavedReportCard.svelte';
|
|
20
|
+
import type {
|
|
21
|
+
RuntimeReportResult,
|
|
22
|
+
RuntimeReportsClient,
|
|
23
|
+
SavedRuntimeReport,
|
|
24
|
+
} from './types.js';
|
|
9
25
|
|
|
10
26
|
export interface Props {
|
|
11
27
|
client?: RuntimeReportsClient;
|
|
12
28
|
reportHref?: (id: string) => string;
|
|
13
29
|
/** Host-owned identity projection; report transport never expands users. */
|
|
14
30
|
authorLabel?: (userId: string) => string | undefined;
|
|
31
|
+
/** The report sources by label and fields (`reports.runtime.sources`). */
|
|
32
|
+
sources?: readonly ReportSourceInfo[];
|
|
33
|
+
/** `page` (default) or `section`, the Reports section page's widget. */
|
|
34
|
+
variant?: 'page' | 'section';
|
|
35
|
+
/** Most cards shown (and run); the section shows its first few. */
|
|
36
|
+
limit?: number;
|
|
37
|
+
/** The Saved reports page, for the section's "View all". */
|
|
38
|
+
listHref?: string;
|
|
39
|
+
/** Replace "New report" asking the page's assistant. */
|
|
40
|
+
onnewreport?: () => void;
|
|
41
|
+
locale?: string;
|
|
42
|
+
currency?: string;
|
|
15
43
|
}
|
|
16
44
|
let {
|
|
17
45
|
client,
|
|
18
46
|
reportHref = (id) => `/reports/${encodeURIComponent(id)}`,
|
|
19
|
-
authorLabel = (
|
|
47
|
+
authorLabel = () => undefined,
|
|
48
|
+
sources = [],
|
|
49
|
+
variant = 'page',
|
|
50
|
+
limit = variant === 'section' ? 6 : 48,
|
|
51
|
+
listHref,
|
|
52
|
+
onnewreport,
|
|
53
|
+
locale = 'en',
|
|
54
|
+
currency = 'USD',
|
|
20
55
|
}: Props = $props();
|
|
21
56
|
const { t } = useI18n();
|
|
22
|
-
let
|
|
57
|
+
let whose = $state<'all' | 'mine'>('all');
|
|
23
58
|
let includeArchived = $state(false);
|
|
24
59
|
let rows = $state<SavedRuntimeReport[]>([]);
|
|
60
|
+
let total = $state(0);
|
|
61
|
+
let results = $state<Record<string, RuntimeReportResult | null>>({});
|
|
25
62
|
let loading = $state(false);
|
|
26
63
|
let error = $state<ReturnType<typeof reportError> | null>(null);
|
|
27
64
|
let refresh = $state(0);
|
|
28
65
|
let pending = $state<string | null>(null);
|
|
66
|
+
let confirming = $state<SavedRuntimeReport | null>(null);
|
|
67
|
+
let askNote = $state(false);
|
|
29
68
|
let epoch = 0;
|
|
30
69
|
|
|
70
|
+
/** Run the shown reports a few at a time, for their cards' figures. */
|
|
71
|
+
async function runAll(
|
|
72
|
+
transport: RuntimeReportsClient,
|
|
73
|
+
reports: SavedRuntimeReport[],
|
|
74
|
+
current: number,
|
|
75
|
+
) {
|
|
76
|
+
const queue = reports.filter((r) => r.status === 'active');
|
|
77
|
+
const worker = async () => {
|
|
78
|
+
for (let next = queue.shift(); next; next = queue.shift()) {
|
|
79
|
+
const id = next.id;
|
|
80
|
+
let value: RuntimeReportResult | null = null;
|
|
81
|
+
try {
|
|
82
|
+
const ran = await transport.run(id);
|
|
83
|
+
value = validReportResult(ran) ? ran : null;
|
|
84
|
+
} catch {
|
|
85
|
+
value = null;
|
|
86
|
+
}
|
|
87
|
+
if (current !== epoch) return;
|
|
88
|
+
results = { ...results, [id]: value };
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
await Promise.all([worker(), worker(), worker()]);
|
|
92
|
+
}
|
|
93
|
+
|
|
31
94
|
$effect(() => {
|
|
32
95
|
const transport = client;
|
|
33
|
-
const options = { onlyMine, includeArchived };
|
|
96
|
+
const options = { onlyMine: whose === 'mine', includeArchived };
|
|
97
|
+
const max = limit;
|
|
34
98
|
refresh;
|
|
35
99
|
const current = ++epoch;
|
|
36
100
|
rows = [];
|
|
101
|
+
results = {};
|
|
37
102
|
error = null;
|
|
38
103
|
pending = null;
|
|
39
104
|
loading = !!transport;
|
|
@@ -41,7 +106,10 @@ $effect(() => {
|
|
|
41
106
|
void transport
|
|
42
107
|
.list(options)
|
|
43
108
|
.then((value) => {
|
|
44
|
-
if (current
|
|
109
|
+
if (current !== epoch) return;
|
|
110
|
+
total = value.length;
|
|
111
|
+
rows = value.slice(0, max);
|
|
112
|
+
void runAll(transport, rows, current);
|
|
45
113
|
})
|
|
46
114
|
.catch((e) => {
|
|
47
115
|
if (current === epoch) error = reportError(e);
|
|
@@ -55,60 +123,165 @@ $effect(() => {
|
|
|
55
123
|
};
|
|
56
124
|
});
|
|
57
125
|
|
|
58
|
-
async function
|
|
126
|
+
async function setArchived(report: SavedRuntimeReport, archived: boolean) {
|
|
59
127
|
if (!client || pending || !report.canArchive) return;
|
|
60
128
|
const current = epoch;
|
|
61
129
|
pending = report.id;
|
|
62
130
|
error = null;
|
|
63
131
|
try {
|
|
64
|
-
await client.archive(report.id,
|
|
132
|
+
await client.archive(report.id, archived);
|
|
65
133
|
if (current === epoch) refresh = untrack(() => refresh) + 1;
|
|
66
134
|
} catch (e) {
|
|
67
135
|
if (current === epoch) error = reportError(e);
|
|
68
136
|
} finally {
|
|
69
137
|
if (current === epoch) pending = null;
|
|
138
|
+
confirming = null;
|
|
70
139
|
}
|
|
71
140
|
}
|
|
141
|
+
|
|
142
|
+
function actionsFor(report: SavedRuntimeReport) {
|
|
143
|
+
if (!report.canArchive) return [];
|
|
144
|
+
return report.status === 'archived'
|
|
145
|
+
? [
|
|
146
|
+
{
|
|
147
|
+
id: 'restore',
|
|
148
|
+
label: t(M['reports.restore']),
|
|
149
|
+
disabled: pending !== null,
|
|
150
|
+
onselect: () => void setArchived(report, false),
|
|
151
|
+
},
|
|
152
|
+
]
|
|
153
|
+
: [
|
|
154
|
+
{
|
|
155
|
+
id: 'archive',
|
|
156
|
+
label: t(M['reports.archive']),
|
|
157
|
+
disabled: pending !== null,
|
|
158
|
+
onselect: () => {
|
|
159
|
+
confirming = report;
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
let root = $state<HTMLElement | null>(null);
|
|
166
|
+
function newReport() {
|
|
167
|
+
if (onnewreport) {
|
|
168
|
+
onnewreport();
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
askNote = !(
|
|
172
|
+
root && requestAssistant(root, { prompt: t(M['reports.new_prompt']) })
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
const sourceOf = (id: string) => sources.find((s) => s.id === id);
|
|
176
|
+
const headingLevel = $derived(variant === 'page' ? 2 : 3);
|
|
72
177
|
</script>
|
|
73
178
|
|
|
74
|
-
<section
|
|
75
|
-
|
|
179
|
+
<section
|
|
180
|
+
bind:this={root}
|
|
181
|
+
class="saved-reports"
|
|
182
|
+
data-smrt-component="SavedReports"
|
|
183
|
+
data-variant={variant}
|
|
184
|
+
aria-label={t(M['reports.title'])}
|
|
185
|
+
>
|
|
186
|
+
{#if variant === 'page'}
|
|
187
|
+
<header class="saved-reports__head">
|
|
188
|
+
<h1 class="saved-reports__title">{t(M['reports.title'])}</h1>
|
|
189
|
+
{#if client}<Button variant="primary" onclick={newReport}>{t(M['reports.new'])}</Button>{/if}
|
|
190
|
+
</header>
|
|
191
|
+
{/if}
|
|
76
192
|
{#if !client}
|
|
77
|
-
<p>{t(M['reports.unavailable'])}</p>
|
|
193
|
+
<p class="saved-reports__note">{t(M['reports.unavailable'])}</p>
|
|
78
194
|
{:else}
|
|
79
|
-
|
|
80
|
-
<
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
195
|
+
{#if variant === 'page'}
|
|
196
|
+
<div class="saved-reports__toolbar">
|
|
197
|
+
<SegmentedControl
|
|
198
|
+
label={t(M['reports.filter_label'])}
|
|
199
|
+
options={[
|
|
200
|
+
{ value: 'all', label: t(M['reports.filter_all']) },
|
|
201
|
+
{ value: 'mine', label: t(M['reports.filter_mine']) },
|
|
202
|
+
]}
|
|
203
|
+
value={whose}
|
|
204
|
+
onvaluechange={(value) => (whose = value === 'mine' ? 'mine' : 'all')}
|
|
205
|
+
/>
|
|
206
|
+
<Switch bind:checked={includeArchived} label={t(M['reports.archived'])} size="sm" />
|
|
207
|
+
</div>
|
|
208
|
+
{:else if rows.length > 0}
|
|
209
|
+
<div class="saved-reports__toolbar">
|
|
210
|
+
<p class="saved-reports__hint">{t(M['reports.hint'])}</p>
|
|
211
|
+
<div class="saved-reports__actions">
|
|
212
|
+
{#if listHref && total > rows.length}<Button variant="ghost" size="sm" href={listHref}>{t(M['reports.view_all'])}</Button>{/if}
|
|
213
|
+
<Button variant="primary" size="sm" onclick={newReport}>{t(M['reports.new'])}</Button>
|
|
214
|
+
</div>
|
|
215
|
+
</div>
|
|
216
|
+
{/if}
|
|
217
|
+
{#if askNote}<p class="saved-reports__note" role="status">{t(M['reports.ask_where'])}</p>{/if}
|
|
218
|
+
{#if error}
|
|
219
|
+
<div class="saved-reports__error">
|
|
220
|
+
<p role="alert">{t(M[error])}</p>
|
|
221
|
+
<Button variant="secondary" size="sm" onclick={() => refresh++}>{t(M['reports.retry'])}</Button>
|
|
222
|
+
</div>
|
|
223
|
+
{/if}
|
|
84
224
|
{#if loading}
|
|
85
|
-
<
|
|
225
|
+
<ul class="saved-reports__grid" aria-busy="true" aria-label={t(M['reports.loading'])}>
|
|
226
|
+
{#each [0, 1, 2] as index (index)}
|
|
227
|
+
<li class="saved-reports__placeholder"><Skeleton variant="text" lines={4} /></li>
|
|
228
|
+
{/each}
|
|
229
|
+
</ul>
|
|
86
230
|
{:else if rows.length === 0 && !error}
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
231
|
+
<EmptyState
|
|
232
|
+
title={t(M['reports.empty_title'])}
|
|
233
|
+
description={t(M['reports.empty'])}
|
|
234
|
+
icon="document"
|
|
235
|
+
actionLabel={t(M['reports.new'])}
|
|
236
|
+
onaction={newReport}
|
|
237
|
+
size={variant === 'page' ? 'md' : 'sm'}
|
|
238
|
+
/>
|
|
239
|
+
{:else if rows.length > 0}
|
|
240
|
+
<ul class="saved-reports__grid">
|
|
90
241
|
{#each rows as report (report.id)}
|
|
91
242
|
<li>
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
243
|
+
<SavedReportCard
|
|
244
|
+
{report}
|
|
245
|
+
href={reportHref(report.id)}
|
|
246
|
+
result={report.status === 'archived' ? null : results[report.id]}
|
|
247
|
+
source={sourceOf(report.sourceId)}
|
|
248
|
+
actions={actionsFor(report)}
|
|
249
|
+
author={authorLabel(report.createdByUserId)}
|
|
250
|
+
{headingLevel}
|
|
251
|
+
{locale}
|
|
252
|
+
{currency}
|
|
253
|
+
/>
|
|
99
254
|
</li>
|
|
100
255
|
{/each}
|
|
101
256
|
</ul>
|
|
102
257
|
{/if}
|
|
103
258
|
{/if}
|
|
259
|
+
<ConfirmDialog
|
|
260
|
+
open={confirming !== null}
|
|
261
|
+
title={t(M['reports.archive_confirm_title'], { title: confirming?.title ?? '' })}
|
|
262
|
+
message={t(M['reports.archive_confirm'])}
|
|
263
|
+
confirmLabel={t(M['reports.archive'])}
|
|
264
|
+
cancelLabel={t(M['reports.cancel'])}
|
|
265
|
+
loading={pending !== null}
|
|
266
|
+
onconfirm={() => confirming && void setArchived(confirming, true)}
|
|
267
|
+
oncancel={() => (confirming = null)}
|
|
268
|
+
/>
|
|
104
269
|
</section>
|
|
105
270
|
|
|
106
271
|
<style>
|
|
107
272
|
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
108
273
|
@layer smrt.theme {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
274
|
+
.saved-reports { display: grid; gap: var(--smrt-spacing-4); min-inline-size: 0; container-type: inline-size; }
|
|
275
|
+
.saved-reports[data-variant='section'] { gap: var(--smrt-spacing-3); }
|
|
276
|
+
.saved-reports__head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--smrt-spacing-3); }
|
|
277
|
+
.saved-reports__title { margin: 0; font: var(--smrt-typography-headline-medium-font); }
|
|
278
|
+
.saved-reports__toolbar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--smrt-spacing-2) var(--smrt-spacing-4); }
|
|
279
|
+
.saved-reports__actions { display: flex; flex-wrap: wrap; gap: var(--smrt-spacing-2); }
|
|
280
|
+
.saved-reports__hint, .saved-reports__note { margin: 0; color: var(--smrt-color-on-surface-variant); font-size: var(--smrt-typography-body-medium-size); }
|
|
281
|
+
.saved-reports__error { display: flex; flex-wrap: wrap; align-items: center; gap: var(--smrt-spacing-3); color: var(--smrt-color-error); }
|
|
282
|
+
.saved-reports__error p { margin: 0; }
|
|
283
|
+
.saved-reports__grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); gap: var(--smrt-spacing-3); margin: 0; padding: 0; list-style: none; }
|
|
284
|
+
.saved-reports__grid > li { display: grid; min-inline-size: 0; }
|
|
285
|
+
.saved-reports__placeholder { padding: var(--smrt-spacing-4); border: var(--smrt-hairline); border-radius: var(--smrt-radius-large); }
|
|
113
286
|
}
|
|
114
287
|
</style>
|
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
import type { ReportSourceInfo } from './describe.js';
|
|
1
2
|
import type { RuntimeReportsClient } from './types.js';
|
|
2
3
|
export interface Props {
|
|
3
4
|
client?: RuntimeReportsClient;
|
|
4
5
|
reportHref?: (id: string) => string;
|
|
5
6
|
/** Host-owned identity projection; report transport never expands users. */
|
|
6
7
|
authorLabel?: (userId: string) => string | undefined;
|
|
8
|
+
/** The report sources by label and fields (`reports.runtime.sources`). */
|
|
9
|
+
sources?: readonly ReportSourceInfo[];
|
|
10
|
+
/** `page` (default) or `section`, the Reports section page's widget. */
|
|
11
|
+
variant?: 'page' | 'section';
|
|
12
|
+
/** Most cards shown (and run); the section shows its first few. */
|
|
13
|
+
limit?: number;
|
|
14
|
+
/** The Saved reports page, for the section's "View all". */
|
|
15
|
+
listHref?: string;
|
|
16
|
+
/** Replace "New report" asking the page's assistant. */
|
|
17
|
+
onnewreport?: () => void;
|
|
18
|
+
locale?: string;
|
|
19
|
+
currency?: string;
|
|
7
20
|
}
|
|
8
21
|
declare const SavedReports: import("svelte").Component<Props, {}, "">;
|
|
9
22
|
type SavedReports = ReturnType<typeof SavedReports>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SavedReports.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/SavedReports.svelte.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SavedReports.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/SavedReports.svelte.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAItD,OAAO,KAAK,EAEV,oBAAoB,EAErB,MAAM,YAAY,CAAC;AAGpB,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,4EAA4E;IAC5E,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACrD,0EAA0E;IAC1E,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,wEAAwE;IACxE,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,mEAAmE;IACnE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkND,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* The `saved-reports` overview widget: the Reports section page's default
|
|
4
|
+
* (`sectionDefault`), the team's saved reports as cards with their headline
|
|
5
|
+
* figures, "New report" (asks the page's assistant) and a hint. It reads
|
|
6
|
+
* through the shared browser client of the app's runtime-report routes; a
|
|
7
|
+
* host with its own transport (the planner's sample data) passes `client`.
|
|
8
|
+
*/
|
|
9
|
+
import { onMount } from 'svelte';
|
|
10
|
+
import { getRuntimeReportsBrowserClient } from './browser-client.js';
|
|
11
|
+
import type { ReportSourceInfo } from './describe.js';
|
|
12
|
+
import SavedReports from './SavedReports.svelte';
|
|
13
|
+
import type { RuntimeReportsClient } from './types.js';
|
|
14
|
+
|
|
15
|
+
export interface Props {
|
|
16
|
+
options?: Record<string, string | number | boolean | null>;
|
|
17
|
+
title?: string;
|
|
18
|
+
locale?: string;
|
|
19
|
+
/** A host-owned transport; omitted, the app's shared browser client. */
|
|
20
|
+
client?: RuntimeReportsClient;
|
|
21
|
+
reportHref?: (id: string) => string;
|
|
22
|
+
/** The Saved reports page ("View all"); default `/reports`. */
|
|
23
|
+
listHref?: string;
|
|
24
|
+
sources?: readonly ReportSourceInfo[];
|
|
25
|
+
authorLabel?: (userId: string) => string | undefined;
|
|
26
|
+
currency?: string;
|
|
27
|
+
}
|
|
28
|
+
let {
|
|
29
|
+
options = {},
|
|
30
|
+
locale = 'en',
|
|
31
|
+
client,
|
|
32
|
+
reportHref,
|
|
33
|
+
listHref = '/reports',
|
|
34
|
+
sources,
|
|
35
|
+
authorLabel,
|
|
36
|
+
currency,
|
|
37
|
+
}: Props = $props();
|
|
38
|
+
let shared = $state<RuntimeReportsClient | undefined>();
|
|
39
|
+
onMount(() => {
|
|
40
|
+
if (!client) shared = getRuntimeReportsBrowserClient();
|
|
41
|
+
});
|
|
42
|
+
const limit = $derived(
|
|
43
|
+
typeof options.limit === 'number' && options.limit > 0 ? options.limit : 6,
|
|
44
|
+
);
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<div data-smrt-component="SavedReportsWidget">
|
|
48
|
+
<SavedReports
|
|
49
|
+
variant="section"
|
|
50
|
+
client={client ?? shared}
|
|
51
|
+
{limit}
|
|
52
|
+
{reportHref}
|
|
53
|
+
{listHref}
|
|
54
|
+
{sources}
|
|
55
|
+
{authorLabel}
|
|
56
|
+
{locale}
|
|
57
|
+
{currency}
|
|
58
|
+
/>
|
|
59
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ReportSourceInfo } from './describe.js';
|
|
2
|
+
import type { RuntimeReportsClient } from './types.js';
|
|
3
|
+
export interface Props {
|
|
4
|
+
options?: Record<string, string | number | boolean | null>;
|
|
5
|
+
title?: string;
|
|
6
|
+
locale?: string;
|
|
7
|
+
/** A host-owned transport; omitted, the app's shared browser client. */
|
|
8
|
+
client?: RuntimeReportsClient;
|
|
9
|
+
reportHref?: (id: string) => string;
|
|
10
|
+
/** The Saved reports page ("View all"); default `/reports`. */
|
|
11
|
+
listHref?: string;
|
|
12
|
+
sources?: readonly ReportSourceInfo[];
|
|
13
|
+
authorLabel?: (userId: string) => string | undefined;
|
|
14
|
+
currency?: string;
|
|
15
|
+
}
|
|
16
|
+
declare const SavedReportsWidget: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type SavedReportsWidget = ReturnType<typeof SavedReportsWidget>;
|
|
18
|
+
export default SavedReportsWidget;
|
|
19
|
+
//# sourceMappingURL=SavedReportsWidget.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SavedReportsWidget.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/SavedReportsWidget.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGvD,MAAM,WAAW,KAAK;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAkCD,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/svelte/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,+BAA+B;IAC9C,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,UAAU,CAAC,EACP,OAAO,GACP,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,CAAC,KAAK,OAAO,CAAC,CAAC;CACnE;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;gBADb,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA;CAKzB;AA0CD,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE,+BAAoC,GAC5C,oBAAoB,
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/svelte/client.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,+BAA+B;IAC9C,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iEAAiE;IACjE,UAAU,CAAC,EACP,OAAO,GACP,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,kBAAkB,EAAE,YAAY,CAAC,KAAK,OAAO,CAAC,CAAC;CACnE;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAE9C,QAAQ,CAAC,MAAM,EAAE,MAAM;IACvB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;gBADb,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,MAAM,YAAA;CAKzB;AA0CD,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE,+BAAoC,GAC5C,oBAAoB,CAuKtB"}
|
package/dist/svelte/client.js
CHANGED
|
@@ -73,6 +73,9 @@ export function createRuntimeReportsHttpClient(options = {}) {
|
|
|
73
73
|
createdByUserId: row.createdByUserId,
|
|
74
74
|
status: row.status,
|
|
75
75
|
spec,
|
|
76
|
+
...(typeof row.createdAt === 'string' && row.createdAt
|
|
77
|
+
? { savedAt: row.createdAt }
|
|
78
|
+
: {}),
|
|
76
79
|
...(lastRuns.has(row.id) ? { lastRunAt: lastRuns.get(row.id) } : {}),
|
|
77
80
|
};
|
|
78
81
|
const canArchive = typeof options.canArchive === 'function'
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { RuntimeReportResult, RuntimeReportSpec } from './types.js';
|
|
2
|
+
/** A source field as a report screen names and formats it (its descriptor's `fields`). */
|
|
3
|
+
export interface ReportFieldInfo {
|
|
4
|
+
name: string;
|
|
5
|
+
label: string;
|
|
6
|
+
/** `datetime`, `integer`, `decimal`, `text`... */
|
|
7
|
+
type?: string;
|
|
8
|
+
/** `money`, `date`... */
|
|
9
|
+
format?: string;
|
|
10
|
+
}
|
|
11
|
+
/** A source by label, with its fields (a `reports.runtime.sources` descriptor). */
|
|
12
|
+
export interface ReportSourceInfo {
|
|
13
|
+
id: string;
|
|
14
|
+
label: string;
|
|
15
|
+
fields?: readonly ReportFieldInfo[];
|
|
16
|
+
}
|
|
17
|
+
type Translate = (key: string, values?: Record<string, unknown>) => string;
|
|
18
|
+
type Column = RuntimeReportResult['columns'][number];
|
|
19
|
+
export declare function humanizeName(name: string): string;
|
|
20
|
+
export interface ReportPeriod {
|
|
21
|
+
/** The field the period is on ("Issue date"). */
|
|
22
|
+
field: string;
|
|
23
|
+
/** The dates it covers ("1 Dec 2025 – 31 Dec 2025"), in UTC. */
|
|
24
|
+
range: string;
|
|
25
|
+
/** The indexes of the filters it consumed. */
|
|
26
|
+
filters: number[];
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* The spec's period: a lower (`gte`/`gt`) and/or upper (`lt`/`lte`) bound on
|
|
30
|
+
* one date field. An exclusive upper bound at midnight reads as the day
|
|
31
|
+
* before, so "last month" reads 1–31 December.
|
|
32
|
+
*/
|
|
33
|
+
export declare function reportPeriod(spec: RuntimeReportSpec, options: {
|
|
34
|
+
locale: string;
|
|
35
|
+
label: (field: string) => string;
|
|
36
|
+
t: Translate;
|
|
37
|
+
}): ReportPeriod | null;
|
|
38
|
+
export interface ReportDescription {
|
|
39
|
+
source: string;
|
|
40
|
+
/** "Issue date by month", or "" for an ungrouped report. */
|
|
41
|
+
groupings: string;
|
|
42
|
+
period: ReportPeriod | null;
|
|
43
|
+
measures: string[];
|
|
44
|
+
/** Conditions other than the period. */
|
|
45
|
+
filters: string[];
|
|
46
|
+
having: string[];
|
|
47
|
+
/** One line for a card: source · groupings · period. */
|
|
48
|
+
line: string;
|
|
49
|
+
}
|
|
50
|
+
/** Describe a spec in the source's own words. */
|
|
51
|
+
export declare function describeReport(spec: RuntimeReportSpec, options: {
|
|
52
|
+
t: Translate;
|
|
53
|
+
locale: string;
|
|
54
|
+
currency: string;
|
|
55
|
+
source?: ReportSourceInfo;
|
|
56
|
+
/** A result's columns, for measure labels ("Total amount"). */
|
|
57
|
+
columns?: readonly Column[];
|
|
58
|
+
}): ReportDescription;
|
|
59
|
+
/** Does the result's x axis run over time buckets? */
|
|
60
|
+
export declare function timeAxis(result: RuntimeReportResult): Column | undefined;
|
|
61
|
+
/** The measure columns a result charts, in order. */
|
|
62
|
+
/** The measure columns, the charted ones (`chart.y`) first. */
|
|
63
|
+
export declare function measureColumns(result: RuntimeReportResult): Column[];
|
|
64
|
+
/** A measure's chart format. */
|
|
65
|
+
export declare function measureFormat(column: Column): 'money' | 'integer' | 'decimal';
|
|
66
|
+
export interface ReportFigure {
|
|
67
|
+
/** What the figure is ("Total", "Average per month", "Peak"). */
|
|
68
|
+
label: string;
|
|
69
|
+
value: string;
|
|
70
|
+
/** Where the peak was ("Sep 2025"). */
|
|
71
|
+
detail?: string;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Headline figures for one measure: the total (only for an additive measure:
|
|
75
|
+
* a sum or a row count), the average per group, and the peak with its group.
|
|
76
|
+
* An ungrouped report has one value, its own figure.
|
|
77
|
+
*/
|
|
78
|
+
export declare function reportFigures(result: RuntimeReportResult, measure: Column, options: {
|
|
79
|
+
t: Translate;
|
|
80
|
+
locale: string;
|
|
81
|
+
currency: string;
|
|
82
|
+
/** How a group reads ("Sep 2025" for a time bucket). */
|
|
83
|
+
groupLabel: (row: RuntimeReportResult['rows'][number]) => string;
|
|
84
|
+
/** The measure's function, when the spec is known. */
|
|
85
|
+
fn?: string;
|
|
86
|
+
}): ReportFigure[];
|
|
87
|
+
/** "3 days ago", "in 2 hours", "just now": a time relative to `now`. */
|
|
88
|
+
export declare function relativeTime(iso: string, locale: string, now?: Date,
|
|
89
|
+
/** What under a minute reads as ("just now"); default the locale's "now". */
|
|
90
|
+
justNow?: string): string;
|
|
91
|
+
/** The result as CSV with the screen's own formatting (labels, buckets, money). */
|
|
92
|
+
export declare function reportCsv(result: RuntimeReportResult, cell: (value: unknown, column: Column) => string): string;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=describe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"describe.d.ts","sourceRoot":"","sources":["../../src/svelte/describe.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEzE,0FAA0F;AAC1F,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CACrC;AAED,KAAK,SAAS,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,MAAM,CAAC;AAC3E,KAAK,MAAM,GAAG,mBAAmB,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAErD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOjD;AAYD,MAAM,WAAW,YAAY;IAC3B,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,8CAA8C;IAC9C,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE;IACP,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,CAAC,EAAE,SAAS,CAAC;CACd,GACA,YAAY,GAAG,IAAI,CA+CrB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,YAAY,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,wCAAwC;IACxC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,iDAAiD;AACjD,wBAAgB,cAAc,CAC5B,IAAI,EAAE,iBAAiB,EACvB,OAAO,EAAE;IACP,CAAC,EAAE,SAAS,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,+DAA+D;IAC/D,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7B,GACA,iBAAiB,CAsDnB;AAYD,sDAAsD;AACtD,wBAAgB,QAAQ,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAKxE;AAED,qDAAqD;AACrD,+DAA+D;AAC/D,wBAAgB,cAAc,CAAC,MAAM,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAOpE;AAED,gCAAgC;AAChC,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,CAM7E;AAED,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,mBAAmB,EAC3B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IACP,CAAC,EAAE,SAAS,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,UAAU,EAAE,CAAC,GAAG,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,MAAM,CAAC;IACjE,sDAAsD;IACtD,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,GACA,YAAY,EAAE,CA+ChB;AAED,wEAAwE;AACxE,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,IAAiB;AACtB,6EAA6E;AAC7E,OAAO,CAAC,EAAE,MAAM,GACf,MAAM,CAyBR;AAWD,mFAAmF;AACnF,wBAAgB,SAAS,CACvB,MAAM,EAAE,mBAAmB,EAC3B,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,GAC/C,MAAM,CAgBR"}
|