@happyvertical/smrt-reports 0.55.23 → 0.56.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/AGENTS.md +26 -3
- package/README.md +9 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +471 -112
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +174 -5
- package/dist/playground.d.ts +2 -0
- package/dist/playground.d.ts.map +1 -0
- package/dist/playground.js +154 -0
- package/dist/playground.js.map +1 -0
- package/dist/recipes.d.ts +55 -0
- package/dist/recipes.d.ts.map +1 -1
- package/dist/runtime-report.d.ts +9 -0
- package/dist/runtime-report.d.ts.map +1 -1
- package/dist/runtime-server.d.ts +77 -0
- package/dist/runtime-server.d.ts.map +1 -0
- package/dist/runtime-spec.d.ts +2 -1
- package/dist/runtime-spec.d.ts.map +1 -1
- package/dist/smrt-knowledge.json +203 -11
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/dist/svelte/ReportConfirmation.svelte +99 -0
- package/dist/svelte/ReportConfirmation.svelte.d.ts +15 -0
- package/dist/svelte/ReportConfirmation.svelte.d.ts.map +1 -0
- package/dist/svelte/ReportDetail.svelte +69 -0
- package/dist/svelte/ReportDetail.svelte.d.ts +11 -0
- package/dist/svelte/ReportDetail.svelte.d.ts.map +1 -0
- package/dist/svelte/ReportPicker.svelte +73 -0
- package/dist/svelte/ReportPicker.svelte.d.ts +16 -0
- package/dist/svelte/ReportPicker.svelte.d.ts.map +1 -0
- package/dist/svelte/ReportResult.svelte +69 -0
- package/dist/svelte/ReportResult.svelte.d.ts +10 -0
- package/dist/svelte/ReportResult.svelte.d.ts.map +1 -0
- package/dist/svelte/RuntimeReportChartWidget.svelte +20 -0
- package/dist/svelte/RuntimeReportChartWidget.svelte.d.ts +11 -0
- package/dist/svelte/RuntimeReportChartWidget.svelte.d.ts.map +1 -0
- package/dist/svelte/RuntimeReportDetailRoute.svelte +67 -0
- package/dist/svelte/RuntimeReportDetailRoute.svelte.d.ts +21 -0
- package/dist/svelte/RuntimeReportDetailRoute.svelte.d.ts.map +1 -0
- package/dist/svelte/RuntimeReportToolResult.svelte +26 -0
- package/dist/svelte/RuntimeReportToolResult.svelte.d.ts +8 -0
- package/dist/svelte/RuntimeReportToolResult.svelte.d.ts.map +1 -0
- package/dist/svelte/RuntimeReportWidgetOptions.svelte +58 -0
- package/dist/svelte/RuntimeReportWidgetOptions.svelte.d.ts +14 -0
- package/dist/svelte/RuntimeReportWidgetOptions.svelte.d.ts.map +1 -0
- package/dist/svelte/RuntimeReportsRoute.svelte +67 -0
- package/dist/svelte/RuntimeReportsRoute.svelte.d.ts +19 -0
- package/dist/svelte/RuntimeReportsRoute.svelte.d.ts.map +1 -0
- package/dist/svelte/SavedReports.svelte +114 -0
- package/dist/svelte/SavedReports.svelte.d.ts +11 -0
- package/dist/svelte/SavedReports.svelte.d.ts.map +1 -0
- package/dist/svelte/browser-client.d.ts +10 -0
- package/dist/svelte/browser-client.d.ts.map +1 -0
- package/dist/svelte/browser-client.js +16 -0
- package/dist/svelte/client.d.ts +15 -0
- package/dist/svelte/client.d.ts.map +1 -0
- package/dist/svelte/client.js +179 -0
- package/dist/svelte/index.d.ts +15 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +13 -0
- package/dist/svelte/messages.d.ts +72 -0
- package/dist/svelte/messages.d.ts.map +1 -0
- package/dist/svelte/messages.js +72 -0
- package/dist/svelte/playground.d.ts +132 -0
- package/dist/svelte/playground.d.ts.map +1 -0
- package/dist/svelte/playground.js +62 -0
- package/dist/svelte/preview-data.d.ts +7 -0
- package/dist/svelte/preview-data.d.ts.map +1 -0
- package/dist/svelte/preview-data.js +59 -0
- package/dist/svelte/result.d.ts +8 -0
- package/dist/svelte/result.d.ts.map +1 -0
- package/dist/svelte/result.js +99 -0
- package/dist/svelte/spec.d.ts +5 -0
- package/dist/svelte/spec.d.ts.map +1 -0
- package/dist/svelte/spec.js +282 -0
- package/dist/svelte/tool-result.d.ts +11 -0
- package/dist/svelte/tool-result.d.ts.map +1 -0
- package/dist/svelte/tool-result.js +19 -0
- package/dist/svelte/types.d.ts +38 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +1 -0
- package/package.json +36 -12
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
3
|
+
import { onMount } from 'svelte';
|
|
4
|
+
import { getRuntimeReportsBrowserClient } from './browser-client.js';
|
|
5
|
+
import { M } from './messages.js';
|
|
6
|
+
import SavedReports from './SavedReports.svelte';
|
|
7
|
+
import type { RuntimeReportsClient } from './types.js';
|
|
8
|
+
|
|
9
|
+
export interface Props {
|
|
10
|
+
client?: RuntimeReportsClient;
|
|
11
|
+
serverAvailable?: boolean;
|
|
12
|
+
/** Authenticated user id projected by the server route host. */
|
|
13
|
+
currentUserId?: string;
|
|
14
|
+
/** Opaque active tenant/session key used only to isolate browser hints. */
|
|
15
|
+
clientScope?: string;
|
|
16
|
+
/** Display label for the authenticated user; other ids stay opaque. */
|
|
17
|
+
currentUserLabel?: string;
|
|
18
|
+
/** Server-derived capability hint. Every mutation is still reauthorized. */
|
|
19
|
+
canArchive?: boolean;
|
|
20
|
+
reportHref?: (id: string) => string;
|
|
21
|
+
authorLabel?: (userId: string) => string | undefined;
|
|
22
|
+
}
|
|
23
|
+
let {
|
|
24
|
+
client,
|
|
25
|
+
serverAvailable = true,
|
|
26
|
+
currentUserId,
|
|
27
|
+
clientScope,
|
|
28
|
+
currentUserLabel,
|
|
29
|
+
canArchive = false,
|
|
30
|
+
reportHref,
|
|
31
|
+
authorLabel,
|
|
32
|
+
}: Props = $props();
|
|
33
|
+
let browserClient = $state<RuntimeReportsClient | undefined>();
|
|
34
|
+
let mounted = $state(false);
|
|
35
|
+
const { t } = useI18n();
|
|
36
|
+
$effect(() => {
|
|
37
|
+
browserClient = client;
|
|
38
|
+
if (!client && mounted && serverAvailable) {
|
|
39
|
+
browserClient = getRuntimeReportsBrowserClient({
|
|
40
|
+
scopeKey: clientScope,
|
|
41
|
+
currentUserId,
|
|
42
|
+
canArchive,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
onMount(() => {
|
|
47
|
+
mounted = true;
|
|
48
|
+
return () => {
|
|
49
|
+
mounted = false;
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
const resolveAuthor = (userId: string): string | undefined =>
|
|
53
|
+
authorLabel?.(userId) ??
|
|
54
|
+
(userId === currentUserId ? currentUserLabel : undefined);
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div data-smrt-component="RuntimeReportsRoute">
|
|
58
|
+
{#if serverAvailable && !browserClient}
|
|
59
|
+
<p role="status">{t(M['reports.loading'])}</p>
|
|
60
|
+
{:else}
|
|
61
|
+
<SavedReports
|
|
62
|
+
client={serverAvailable ? browserClient : undefined}
|
|
63
|
+
{reportHref}
|
|
64
|
+
authorLabel={resolveAuthor}
|
|
65
|
+
/>
|
|
66
|
+
{/if}
|
|
67
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RuntimeReportsClient } from './types.js';
|
|
2
|
+
export interface Props {
|
|
3
|
+
client?: RuntimeReportsClient;
|
|
4
|
+
serverAvailable?: boolean;
|
|
5
|
+
/** Authenticated user id projected by the server route host. */
|
|
6
|
+
currentUserId?: string;
|
|
7
|
+
/** Opaque active tenant/session key used only to isolate browser hints. */
|
|
8
|
+
clientScope?: string;
|
|
9
|
+
/** Display label for the authenticated user; other ids stay opaque. */
|
|
10
|
+
currentUserLabel?: string;
|
|
11
|
+
/** Server-derived capability hint. Every mutation is still reauthorized. */
|
|
12
|
+
canArchive?: boolean;
|
|
13
|
+
reportHref?: (id: string) => string;
|
|
14
|
+
authorLabel?: (userId: string) => string | undefined;
|
|
15
|
+
}
|
|
16
|
+
declare const RuntimeReportsRoute: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type RuntimeReportsRoute = ReturnType<typeof RuntimeReportsRoute>;
|
|
18
|
+
export default RuntimeReportsRoute;
|
|
19
|
+
//# sourceMappingURL=RuntimeReportsRoute.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RuntimeReportsRoute.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/RuntimeReportsRoute.svelte.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAGvD,MAAM,WAAW,KAAK;IACpB,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uEAAuE;IACvE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,4EAA4E;IAC5E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CACtD;AAqDD,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Checkbox } from '@happyvertical/smrt-ui/forms';
|
|
3
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
4
|
+
import { Button } from '@happyvertical/smrt-ui/ui';
|
|
5
|
+
import { untrack } from 'svelte';
|
|
6
|
+
import { M } from './messages.js';
|
|
7
|
+
import { reportError } from './result.js';
|
|
8
|
+
import type { RuntimeReportsClient, SavedRuntimeReport } from './types.js';
|
|
9
|
+
|
|
10
|
+
export interface Props {
|
|
11
|
+
client?: RuntimeReportsClient;
|
|
12
|
+
reportHref?: (id: string) => string;
|
|
13
|
+
/** Host-owned identity projection; report transport never expands users. */
|
|
14
|
+
authorLabel?: (userId: string) => string | undefined;
|
|
15
|
+
}
|
|
16
|
+
let {
|
|
17
|
+
client,
|
|
18
|
+
reportHref = (id) => `/reports/${encodeURIComponent(id)}`,
|
|
19
|
+
authorLabel = (id) => id,
|
|
20
|
+
}: Props = $props();
|
|
21
|
+
const { t } = useI18n();
|
|
22
|
+
let onlyMine = $state(false);
|
|
23
|
+
let includeArchived = $state(false);
|
|
24
|
+
let rows = $state<SavedRuntimeReport[]>([]);
|
|
25
|
+
let loading = $state(false);
|
|
26
|
+
let error = $state<ReturnType<typeof reportError> | null>(null);
|
|
27
|
+
let refresh = $state(0);
|
|
28
|
+
let pending = $state<string | null>(null);
|
|
29
|
+
let epoch = 0;
|
|
30
|
+
|
|
31
|
+
$effect(() => {
|
|
32
|
+
const transport = client;
|
|
33
|
+
const options = { onlyMine, includeArchived };
|
|
34
|
+
refresh;
|
|
35
|
+
const current = ++epoch;
|
|
36
|
+
rows = [];
|
|
37
|
+
error = null;
|
|
38
|
+
pending = null;
|
|
39
|
+
loading = !!transport;
|
|
40
|
+
if (transport) {
|
|
41
|
+
void transport
|
|
42
|
+
.list(options)
|
|
43
|
+
.then((value) => {
|
|
44
|
+
if (current === epoch) rows = value;
|
|
45
|
+
})
|
|
46
|
+
.catch((e) => {
|
|
47
|
+
if (current === epoch) error = reportError(e);
|
|
48
|
+
})
|
|
49
|
+
.finally(() => {
|
|
50
|
+
if (current === epoch) loading = false;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return () => {
|
|
54
|
+
++epoch;
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
async function archive(report: SavedRuntimeReport) {
|
|
59
|
+
if (!client || pending || !report.canArchive) return;
|
|
60
|
+
const current = epoch;
|
|
61
|
+
pending = report.id;
|
|
62
|
+
error = null;
|
|
63
|
+
try {
|
|
64
|
+
await client.archive(report.id, report.status !== 'archived');
|
|
65
|
+
if (current === epoch) refresh = untrack(() => refresh) + 1;
|
|
66
|
+
} catch (e) {
|
|
67
|
+
if (current === epoch) error = reportError(e);
|
|
68
|
+
} finally {
|
|
69
|
+
if (current === epoch) pending = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<section data-smrt-component="SavedReports" aria-label={t(M['reports.title'])}>
|
|
75
|
+
<h1>{t(M['reports.title'])}</h1>
|
|
76
|
+
{#if !client}
|
|
77
|
+
<p>{t(M['reports.unavailable'])}</p>
|
|
78
|
+
{:else}
|
|
79
|
+
<div data-smrt-part="filters">
|
|
80
|
+
<Checkbox bind:checked={onlyMine} label={t(M['reports.only_mine'])} />
|
|
81
|
+
<Checkbox bind:checked={includeArchived} label={t(M['reports.archived'])} />
|
|
82
|
+
</div>
|
|
83
|
+
{#if error}<p role="alert">{t(M[error])}</p><Button onclick={() => refresh++}>{t(M['reports.retry'])}</Button>{/if}
|
|
84
|
+
{#if loading}
|
|
85
|
+
<p role="status">{t(M['reports.loading'])}</p>
|
|
86
|
+
{:else if rows.length === 0 && !error}
|
|
87
|
+
<p>{t(M['reports.empty'])}</p>
|
|
88
|
+
{:else}
|
|
89
|
+
<ul>
|
|
90
|
+
{#each rows as report (report.id)}
|
|
91
|
+
<li>
|
|
92
|
+
<h2><a href={reportHref(report.id)}>{report.title}</a></h2>
|
|
93
|
+
<dl>
|
|
94
|
+
<dt>{t(M['reports.source'])}</dt><dd>{report.sourceId}</dd>
|
|
95
|
+
<dt>{t(M['reports.author'])}</dt><dd>{authorLabel(report.createdByUserId) ?? report.createdByUserId}</dd>
|
|
96
|
+
</dl>
|
|
97
|
+
<p>{report.lastRunAt ? t(M['reports.last_run'], { time: report.lastRunAt }) : t(M['reports.never_run'])}</p>
|
|
98
|
+
{#if report.canArchive}<Button disabled={pending !== null} onclick={() => archive(report)}>{t(M[report.status === 'archived' ? 'reports.unarchive' : 'reports.archive'])}</Button>{/if}
|
|
99
|
+
</li>
|
|
100
|
+
{/each}
|
|
101
|
+
</ul>
|
|
102
|
+
{/if}
|
|
103
|
+
{/if}
|
|
104
|
+
</section>
|
|
105
|
+
|
|
106
|
+
<style>
|
|
107
|
+
@layer smrt.reset, smrt.structure, smrt.theme;
|
|
108
|
+
@layer smrt.theme {
|
|
109
|
+
[data-smrt-part='filters'] { display: flex; flex-wrap: wrap; gap: var(--smrt-spacing-3); }
|
|
110
|
+
ul { display: grid; gap: var(--smrt-spacing-4); padding: 0; list-style: none; }
|
|
111
|
+
li { padding: var(--smrt-spacing-4); border: var(--smrt-hairline); border-radius: var(--smrt-radius-medium); }
|
|
112
|
+
dl { display: grid; grid-template-columns: auto 1fr; gap: var(--smrt-spacing-2); }
|
|
113
|
+
}
|
|
114
|
+
</style>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RuntimeReportsClient } from './types.js';
|
|
2
|
+
export interface Props {
|
|
3
|
+
client?: RuntimeReportsClient;
|
|
4
|
+
reportHref?: (id: string) => string;
|
|
5
|
+
/** Host-owned identity projection; report transport never expands users. */
|
|
6
|
+
authorLabel?: (userId: string) => string | undefined;
|
|
7
|
+
}
|
|
8
|
+
declare const SavedReports: import("svelte").Component<Props, {}, "">;
|
|
9
|
+
type SavedReports = ReturnType<typeof SavedReports>;
|
|
10
|
+
export default SavedReports;
|
|
11
|
+
//# sourceMappingURL=SavedReports.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SavedReports.svelte.d.ts","sourceRoot":"","sources":["../../src/svelte/SavedReports.svelte.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,oBAAoB,EAAsB,MAAM,YAAY,CAAC;AAG3E,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;CACtD;AAwGD,QAAA,MAAM,YAAY,2CAAwC,CAAC;AAC3D,KAAK,YAAY,GAAG,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC;AACpD,eAAe,YAAY,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { RuntimeReportsClient } from './types.js';
|
|
2
|
+
export interface RuntimeReportsBrowserClientOptions {
|
|
3
|
+
/** Opaque host key for the active tenant/session; carries no request authority. */
|
|
4
|
+
scopeKey?: string;
|
|
5
|
+
currentUserId?: string;
|
|
6
|
+
canArchive?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/** One client per browser session so non-persistent last-run hints survive navigation. */
|
|
9
|
+
export declare function getRuntimeReportsBrowserClient(options?: RuntimeReportsBrowserClientOptions): RuntimeReportsClient;
|
|
10
|
+
//# sourceMappingURL=browser-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-client.d.ts","sourceRoot":"","sources":["../../src/svelte/browser-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAKvD,MAAM,WAAW,kCAAkC;IACjD,mFAAmF;IACnF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,0FAA0F;AAC1F,wBAAgB,8BAA8B,CAC5C,OAAO,GAAE,kCAAuC,GAC/C,oBAAoB,CAatB"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createRuntimeReportsHttpClient } from './client.js';
|
|
2
|
+
let client;
|
|
3
|
+
let identityKey = '';
|
|
4
|
+
/** One client per browser session so non-persistent last-run hints survive navigation. */
|
|
5
|
+
export function getRuntimeReportsBrowserClient(options = {}) {
|
|
6
|
+
if (typeof window === 'undefined')
|
|
7
|
+
throw new Error('The runtime reports browser client is unavailable during SSR');
|
|
8
|
+
// Route hosts pass server-derived identity and capability hints. Recreate the
|
|
9
|
+
// session client when that principal changes so hints never cross sessions.
|
|
10
|
+
const nextIdentityKey = `${options.scopeKey ?? ''}:${options.currentUserId ?? ''}:${options.canArchive === true}`;
|
|
11
|
+
if (!client || identityKey !== nextIdentityKey) {
|
|
12
|
+
client = createRuntimeReportsHttpClient(options);
|
|
13
|
+
identityKey = nextIdentityKey;
|
|
14
|
+
}
|
|
15
|
+
return client;
|
|
16
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { RuntimeReportsClient, SavedRuntimeReport } from './types.js';
|
|
2
|
+
export interface RuntimeReportsHttpClientOptions {
|
|
3
|
+
fetch?: typeof globalThis.fetch;
|
|
4
|
+
basePath?: string;
|
|
5
|
+
currentUserId?: string;
|
|
6
|
+
/** UI capability hint only. The server remains authoritative. */
|
|
7
|
+
canArchive?: boolean | ((report: Omit<SavedRuntimeReport, 'canArchive'>) => boolean);
|
|
8
|
+
}
|
|
9
|
+
export declare class RuntimeReportsHttpError extends Error {
|
|
10
|
+
readonly status: number;
|
|
11
|
+
readonly code?: string | undefined;
|
|
12
|
+
constructor(status: number, code?: string | undefined);
|
|
13
|
+
}
|
|
14
|
+
export declare function createRuntimeReportsHttpClient(options?: RuntimeReportsHttpClientOptions): RuntimeReportsClient;
|
|
15
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +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,CAoKtB"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { validReportResult } from './result.js';
|
|
2
|
+
import { runtimeReportSpecFromWire } from './spec.js';
|
|
3
|
+
export class RuntimeReportsHttpError extends Error {
|
|
4
|
+
status;
|
|
5
|
+
code;
|
|
6
|
+
constructor(status, code) {
|
|
7
|
+
super('Runtime report request failed');
|
|
8
|
+
this.status = status;
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.name = 'RuntimeReportsHttpError';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
const REPORT_LIST_PAGE_LIMIT = 1_000;
|
|
14
|
+
const MAX_REPORT_LIST_PAGES = 1_000;
|
|
15
|
+
function record(value) {
|
|
16
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
|
17
|
+
? value
|
|
18
|
+
: null;
|
|
19
|
+
}
|
|
20
|
+
async function responseJson(response) {
|
|
21
|
+
let body;
|
|
22
|
+
try {
|
|
23
|
+
body = await response.json();
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
throw new RuntimeReportsHttpError(response.status);
|
|
27
|
+
}
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
const data = record(body);
|
|
30
|
+
throw new RuntimeReportsHttpError(response.status, typeof data?.code === 'string' ? data.code : undefined);
|
|
31
|
+
}
|
|
32
|
+
return body;
|
|
33
|
+
}
|
|
34
|
+
function safeId(value) {
|
|
35
|
+
return encodeURIComponent(value);
|
|
36
|
+
}
|
|
37
|
+
function confirmUrl(path) {
|
|
38
|
+
if (!path.startsWith('/') || path.startsWith('//'))
|
|
39
|
+
throw new RuntimeReportsHttpError(400, 'invalid_confirm_path');
|
|
40
|
+
if (typeof location === 'undefined')
|
|
41
|
+
return path;
|
|
42
|
+
const url = new URL(path, location.href);
|
|
43
|
+
if (url.origin !== location.origin)
|
|
44
|
+
throw new RuntimeReportsHttpError(400, 'invalid_confirm_path');
|
|
45
|
+
return `${url.pathname}${url.search}`;
|
|
46
|
+
}
|
|
47
|
+
export function createRuntimeReportsHttpClient(options = {}) {
|
|
48
|
+
const request = options.fetch ?? globalThis.fetch;
|
|
49
|
+
if (typeof request !== 'function')
|
|
50
|
+
throw new Error('Runtime reports require a fetch implementation');
|
|
51
|
+
const basePath = (options.basePath ?? '/api/runtimereports').replace(/\/$/, '');
|
|
52
|
+
const lastRuns = new Map();
|
|
53
|
+
const project = (value) => {
|
|
54
|
+
const row = record(value);
|
|
55
|
+
if (!row)
|
|
56
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
57
|
+
const spec = runtimeReportSpecFromWire(row.spec);
|
|
58
|
+
if (!spec ||
|
|
59
|
+
typeof row.id !== 'string' ||
|
|
60
|
+
typeof row.title !== 'string' ||
|
|
61
|
+
typeof row.sourceId !== 'string' ||
|
|
62
|
+
typeof row.createdByUserId !== 'string' ||
|
|
63
|
+
(row.status !== 'active' && row.status !== 'archived')) {
|
|
64
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
65
|
+
}
|
|
66
|
+
const projected = {
|
|
67
|
+
id: row.id,
|
|
68
|
+
title: row.title,
|
|
69
|
+
...(typeof row.description === 'string' && row.description
|
|
70
|
+
? { description: row.description }
|
|
71
|
+
: {}),
|
|
72
|
+
sourceId: row.sourceId,
|
|
73
|
+
createdByUserId: row.createdByUserId,
|
|
74
|
+
status: row.status,
|
|
75
|
+
spec,
|
|
76
|
+
...(lastRuns.has(row.id) ? { lastRunAt: lastRuns.get(row.id) } : {}),
|
|
77
|
+
};
|
|
78
|
+
const canArchive = typeof options.canArchive === 'function'
|
|
79
|
+
? options.canArchive(projected)
|
|
80
|
+
: options.canArchive === true;
|
|
81
|
+
return { ...projected, canArchive };
|
|
82
|
+
};
|
|
83
|
+
const get = async (id) => project(await responseJson(await request(`${basePath}/${safeId(id)}`, {
|
|
84
|
+
headers: { accept: 'application/json' },
|
|
85
|
+
})));
|
|
86
|
+
return {
|
|
87
|
+
async list({ onlyMine, includeArchived }) {
|
|
88
|
+
const filters = new URLSearchParams();
|
|
89
|
+
if (!includeArchived)
|
|
90
|
+
filters.set('status', 'active');
|
|
91
|
+
if (onlyMine) {
|
|
92
|
+
if (!options.currentUserId)
|
|
93
|
+
throw new RuntimeReportsHttpError(400, 'missing_current_user');
|
|
94
|
+
filters.set('createdByUserId', options.currentUserId);
|
|
95
|
+
}
|
|
96
|
+
const reports = [];
|
|
97
|
+
const seenReportIds = new Set();
|
|
98
|
+
let offset = 0;
|
|
99
|
+
for (let page = 0; page < MAX_REPORT_LIST_PAGES; page++) {
|
|
100
|
+
const query = new URLSearchParams(filters);
|
|
101
|
+
query.set('limit', String(REPORT_LIST_PAGE_LIMIT));
|
|
102
|
+
query.set('offset', String(offset));
|
|
103
|
+
const body = record(await responseJson(await request(`${basePath}?${query}`, {
|
|
104
|
+
headers: { accept: 'application/json' },
|
|
105
|
+
})));
|
|
106
|
+
if (!body ||
|
|
107
|
+
!Array.isArray(body.items) ||
|
|
108
|
+
typeof body.count !== 'number' ||
|
|
109
|
+
!Number.isSafeInteger(body.count) ||
|
|
110
|
+
body.count < 0 ||
|
|
111
|
+
typeof body.limit !== 'number' ||
|
|
112
|
+
!Number.isSafeInteger(body.limit) ||
|
|
113
|
+
body.limit < 1 ||
|
|
114
|
+
body.limit > REPORT_LIST_PAGE_LIMIT ||
|
|
115
|
+
typeof body.offset !== 'number' ||
|
|
116
|
+
!Number.isSafeInteger(body.offset) ||
|
|
117
|
+
body.offset !== offset ||
|
|
118
|
+
body.items.length > body.limit) {
|
|
119
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
120
|
+
}
|
|
121
|
+
const pageReports = body.items.map(project);
|
|
122
|
+
for (const report of pageReports) {
|
|
123
|
+
if (seenReportIds.has(report.id))
|
|
124
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
125
|
+
seenReportIds.add(report.id);
|
|
126
|
+
}
|
|
127
|
+
reports.push(...pageReports);
|
|
128
|
+
offset += body.items.length;
|
|
129
|
+
if (offset >= body.count)
|
|
130
|
+
return reports;
|
|
131
|
+
if (body.items.length === 0)
|
|
132
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
133
|
+
}
|
|
134
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
135
|
+
},
|
|
136
|
+
get,
|
|
137
|
+
async run(id) {
|
|
138
|
+
const body = record(await responseJson(await request(`${basePath}/${safeId(id)}/run`, {
|
|
139
|
+
method: 'POST',
|
|
140
|
+
headers: { accept: 'application/json' },
|
|
141
|
+
})));
|
|
142
|
+
if (body?.action !== 'run' || !validReportResult(body.result))
|
|
143
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
144
|
+
lastRuns.set(id, body.result.ranAt);
|
|
145
|
+
return body.result;
|
|
146
|
+
},
|
|
147
|
+
async archive(id, archived) {
|
|
148
|
+
const body = record(await responseJson(await request(`${basePath}/${safeId(id)}/archive`, {
|
|
149
|
+
method: 'POST',
|
|
150
|
+
headers: {
|
|
151
|
+
'content-type': 'application/json',
|
|
152
|
+
accept: 'application/json',
|
|
153
|
+
},
|
|
154
|
+
body: JSON.stringify({ archived }),
|
|
155
|
+
})));
|
|
156
|
+
const result = record(body?.result);
|
|
157
|
+
if (body?.action !== 'archive' ||
|
|
158
|
+
result?.id !== id ||
|
|
159
|
+
result.status !== (archived ? 'archived' : 'active'))
|
|
160
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
161
|
+
},
|
|
162
|
+
async confirm(proposal) {
|
|
163
|
+
const body = record(await responseJson(await request(confirmUrl(proposal.confirmPath), {
|
|
164
|
+
method: 'POST',
|
|
165
|
+
headers: {
|
|
166
|
+
'content-type': 'application/json',
|
|
167
|
+
accept: 'application/json',
|
|
168
|
+
},
|
|
169
|
+
body: JSON.stringify({
|
|
170
|
+
spec: proposal.spec,
|
|
171
|
+
specHash: proposal.specHash,
|
|
172
|
+
}),
|
|
173
|
+
})));
|
|
174
|
+
if (typeof body?.id !== 'string' || body.specHash !== proposal.specHash)
|
|
175
|
+
throw new RuntimeReportsHttpError(502, 'invalid_result');
|
|
176
|
+
return get(body.id);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { getRuntimeReportsBrowserClient, type RuntimeReportsBrowserClientOptions, } from './browser-client.js';
|
|
2
|
+
export { createRuntimeReportsHttpClient, type RuntimeReportsHttpClientOptions, RuntimeReportsHttpError, } from './client.js';
|
|
3
|
+
export { default as ReportConfirmation } from './ReportConfirmation.svelte';
|
|
4
|
+
export { default as ReportDetail } from './ReportDetail.svelte';
|
|
5
|
+
export { default as ReportPicker } from './ReportPicker.svelte';
|
|
6
|
+
export { default as ReportResult } from './ReportResult.svelte';
|
|
7
|
+
export { default as RuntimeReportChartWidget } from './RuntimeReportChartWidget.svelte';
|
|
8
|
+
export { default as RuntimeReportDetailRoute } from './RuntimeReportDetailRoute.svelte';
|
|
9
|
+
export { default as RuntimeReportsRoute } from './RuntimeReportsRoute.svelte';
|
|
10
|
+
export { default as RuntimeReportToolResult } from './RuntimeReportToolResult.svelte';
|
|
11
|
+
export { default as RuntimeReportWidgetOptions } from './RuntimeReportWidgetOptions.svelte';
|
|
12
|
+
export { default as SavedReports } from './SavedReports.svelte';
|
|
13
|
+
export { runtimeReportProposalFromMessage, runtimeReportToolResultComponent, type ToolResultMessage, } from './tool-result.js';
|
|
14
|
+
export type { RuntimeReportProposal, RuntimeReportResult, RuntimeReportSpec, RuntimeReportsClient, SavedRuntimeReport, } from './types.js';
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,8BAA8B,EAC9B,KAAK,kCAAkC,GACxC,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,8BAA8B,EAC9B,KAAK,+BAA+B,EACpC,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,mCAAmC,CAAC;AACxF,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,8BAA8B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,OAAO,IAAI,0BAA0B,EAAE,MAAM,qCAAqC,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EACL,gCAAgC,EAChC,gCAAgC,EAChC,KAAK,iBAAiB,GACvB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,qBAAqB,EACrB,mBAAmB,EACnB,iBAAiB,EACjB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { getRuntimeReportsBrowserClient, } from './browser-client.js';
|
|
2
|
+
export { createRuntimeReportsHttpClient, RuntimeReportsHttpError, } from './client.js';
|
|
3
|
+
export { default as ReportConfirmation } from './ReportConfirmation.svelte';
|
|
4
|
+
export { default as ReportDetail } from './ReportDetail.svelte';
|
|
5
|
+
export { default as ReportPicker } from './ReportPicker.svelte';
|
|
6
|
+
export { default as ReportResult } from './ReportResult.svelte';
|
|
7
|
+
export { default as RuntimeReportChartWidget } from './RuntimeReportChartWidget.svelte';
|
|
8
|
+
export { default as RuntimeReportDetailRoute } from './RuntimeReportDetailRoute.svelte';
|
|
9
|
+
export { default as RuntimeReportsRoute } from './RuntimeReportsRoute.svelte';
|
|
10
|
+
export { default as RuntimeReportToolResult } from './RuntimeReportToolResult.svelte';
|
|
11
|
+
export { default as RuntimeReportWidgetOptions } from './RuntimeReportWidgetOptions.svelte';
|
|
12
|
+
export { default as SavedReports } from './SavedReports.svelte';
|
|
13
|
+
export { runtimeReportProposalFromMessage, runtimeReportToolResultComponent, } from './tool-result.js';
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export declare const M: {
|
|
2
|
+
readonly 'reports.title': "reports.title";
|
|
3
|
+
readonly 'reports.only_mine': "reports.only_mine";
|
|
4
|
+
readonly 'reports.archived': "reports.archived";
|
|
5
|
+
readonly 'reports.archive': "reports.archive";
|
|
6
|
+
readonly 'reports.unarchive': "reports.unarchive";
|
|
7
|
+
readonly 'reports.loading': "reports.loading";
|
|
8
|
+
readonly 'reports.running': "reports.running";
|
|
9
|
+
readonly 'reports.empty': "reports.empty";
|
|
10
|
+
readonly 'reports.no_rows': "reports.no_rows";
|
|
11
|
+
readonly 'reports.unavailable': "reports.unavailable";
|
|
12
|
+
readonly 'reports.error': "reports.error";
|
|
13
|
+
readonly 'reports.denied': "reports.denied";
|
|
14
|
+
readonly 'reports.invalid': "reports.invalid";
|
|
15
|
+
readonly 'reports.retry': "reports.retry";
|
|
16
|
+
readonly 'reports.rerun': "reports.rerun";
|
|
17
|
+
readonly 'reports.table': "reports.table";
|
|
18
|
+
readonly 'reports.chart': "reports.chart";
|
|
19
|
+
readonly 'reports.source': "reports.source";
|
|
20
|
+
readonly 'reports.author': "reports.author";
|
|
21
|
+
readonly 'reports.last_run': "reports.last_run";
|
|
22
|
+
readonly 'reports.never_run': "reports.never_run";
|
|
23
|
+
readonly 'reports.truncated': "reports.truncated";
|
|
24
|
+
readonly 'reports.yes': "reports.yes";
|
|
25
|
+
readonly 'reports.no': "reports.no";
|
|
26
|
+
readonly 'reports.select_report': "reports.select_report";
|
|
27
|
+
readonly 'reports.select_measure': "reports.select_measure";
|
|
28
|
+
readonly 'reports.choose_report': "reports.choose_report";
|
|
29
|
+
readonly 'reports.choose_measure': "reports.choose_measure";
|
|
30
|
+
readonly 'reports.select_dimension': "reports.select_dimension";
|
|
31
|
+
readonly 'reports.choose_dimension': "reports.choose_dimension";
|
|
32
|
+
readonly 'reports.options_invalid': "reports.options_invalid";
|
|
33
|
+
readonly 'reports.cancel': "reports.cancel";
|
|
34
|
+
readonly 'reports.save': "reports.save";
|
|
35
|
+
readonly 'reports.confirm_title': "reports.confirm_title";
|
|
36
|
+
readonly 'reports.saved': "reports.saved";
|
|
37
|
+
readonly 'reports.declined': "reports.declined";
|
|
38
|
+
readonly 'reports.groupings': "reports.groupings";
|
|
39
|
+
readonly 'reports.measures': "reports.measures";
|
|
40
|
+
readonly 'reports.filters': "reports.filters";
|
|
41
|
+
readonly 'reports.having': "reports.having";
|
|
42
|
+
readonly 'reports.none': "reports.none";
|
|
43
|
+
readonly 'reports.rows': "reports.rows";
|
|
44
|
+
readonly 'reports.saving': "reports.saving";
|
|
45
|
+
readonly 'reports.approve': "reports.approve";
|
|
46
|
+
readonly 'reports.decline': "reports.decline";
|
|
47
|
+
readonly 'reports.aggregate.count': "reports.aggregate.count";
|
|
48
|
+
readonly 'reports.aggregate.countDistinct': "reports.aggregate.countDistinct";
|
|
49
|
+
readonly 'reports.aggregate.sum': "reports.aggregate.sum";
|
|
50
|
+
readonly 'reports.aggregate.avg': "reports.aggregate.avg";
|
|
51
|
+
readonly 'reports.aggregate.min': "reports.aggregate.min";
|
|
52
|
+
readonly 'reports.aggregate.max': "reports.aggregate.max";
|
|
53
|
+
readonly 'reports.operator.eq': "reports.operator.eq";
|
|
54
|
+
readonly 'reports.operator.ne': "reports.operator.ne";
|
|
55
|
+
readonly 'reports.operator.gt': "reports.operator.gt";
|
|
56
|
+
readonly 'reports.operator.gte': "reports.operator.gte";
|
|
57
|
+
readonly 'reports.operator.lt': "reports.operator.lt";
|
|
58
|
+
readonly 'reports.operator.lte': "reports.operator.lte";
|
|
59
|
+
readonly 'reports.operator.in': "reports.operator.in";
|
|
60
|
+
readonly 'reports.operator.notIn': "reports.operator.notIn";
|
|
61
|
+
readonly 'reports.operator.contains': "reports.operator.contains";
|
|
62
|
+
readonly 'reports.operator.isNull': "reports.operator.isNull";
|
|
63
|
+
readonly 'reports.operator.isNotNull': "reports.operator.isNotNull";
|
|
64
|
+
readonly 'reports.bucket.minute': "reports.bucket.minute";
|
|
65
|
+
readonly 'reports.bucket.hour': "reports.bucket.hour";
|
|
66
|
+
readonly 'reports.bucket.day': "reports.bucket.day";
|
|
67
|
+
readonly 'reports.bucket.week': "reports.bucket.week";
|
|
68
|
+
readonly 'reports.bucket.month': "reports.bucket.month";
|
|
69
|
+
readonly 'reports.bucket.quarter': "reports.bucket.quarter";
|
|
70
|
+
readonly 'reports.bucket.year': "reports.bucket.year";
|
|
71
|
+
};
|
|
72
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/svelte/messages.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyEZ,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { defineMessages } from '@happyvertical/smrt-ui/i18n';
|
|
2
|
+
export const M = defineMessages({
|
|
3
|
+
'reports.title': 'Reports',
|
|
4
|
+
'reports.only_mine': 'Only mine',
|
|
5
|
+
'reports.archived': 'Show archived reports',
|
|
6
|
+
'reports.archive': 'Archive',
|
|
7
|
+
'reports.unarchive': 'Unarchive',
|
|
8
|
+
'reports.loading': 'Loading reports…',
|
|
9
|
+
'reports.running': 'Running report…',
|
|
10
|
+
'reports.empty': 'No saved reports. Ask the assistant to create a report, then approve its proposal.',
|
|
11
|
+
'reports.no_rows': 'No results match this report.',
|
|
12
|
+
'reports.unavailable': 'Reports are unavailable because this host has no server.',
|
|
13
|
+
'reports.error': 'The report request failed. Please try again.',
|
|
14
|
+
'reports.denied': 'You do not have permission to read this report or one of its fields.',
|
|
15
|
+
'reports.invalid': 'The report returned an invalid result.',
|
|
16
|
+
'reports.retry': 'Try again',
|
|
17
|
+
'reports.rerun': 'Re-run',
|
|
18
|
+
'reports.table': 'Table view',
|
|
19
|
+
'reports.chart': 'Chart view',
|
|
20
|
+
'reports.source': 'Source',
|
|
21
|
+
'reports.author': 'Author',
|
|
22
|
+
'reports.last_run': 'Last run: {time}',
|
|
23
|
+
'reports.never_run': 'No recent run',
|
|
24
|
+
'reports.truncated': 'Showing a bounded sample; more rows are available.',
|
|
25
|
+
'reports.yes': 'Yes',
|
|
26
|
+
'reports.no': 'No',
|
|
27
|
+
'reports.select_report': 'Report',
|
|
28
|
+
'reports.select_measure': 'Measure',
|
|
29
|
+
'reports.choose_report': 'Choose a saved report',
|
|
30
|
+
'reports.choose_measure': 'Choose a measure',
|
|
31
|
+
'reports.select_dimension': 'Dimension',
|
|
32
|
+
'reports.choose_dimension': 'Choose a dimension',
|
|
33
|
+
'reports.options_invalid': 'Choose a report and measure before saving.',
|
|
34
|
+
'reports.cancel': 'Cancel',
|
|
35
|
+
'reports.save': 'Save',
|
|
36
|
+
'reports.confirm_title': 'Review report proposal',
|
|
37
|
+
'reports.saved': 'Report saved.',
|
|
38
|
+
'reports.declined': 'Report declined. Nothing was saved.',
|
|
39
|
+
'reports.groupings': 'Group by',
|
|
40
|
+
'reports.measures': 'Measures',
|
|
41
|
+
'reports.filters': 'Filters',
|
|
42
|
+
'reports.having': 'Measure filters',
|
|
43
|
+
'reports.none': 'None',
|
|
44
|
+
'reports.rows': 'rows',
|
|
45
|
+
'reports.saving': 'Saving…',
|
|
46
|
+
'reports.approve': 'Approve',
|
|
47
|
+
'reports.decline': 'Decline',
|
|
48
|
+
'reports.aggregate.count': 'Count of {field}',
|
|
49
|
+
'reports.aggregate.countDistinct': 'Distinct count of {field}',
|
|
50
|
+
'reports.aggregate.sum': 'Sum of {field}',
|
|
51
|
+
'reports.aggregate.avg': 'Average of {field}',
|
|
52
|
+
'reports.aggregate.min': 'Minimum of {field}',
|
|
53
|
+
'reports.aggregate.max': 'Maximum of {field}',
|
|
54
|
+
'reports.operator.eq': 'equals',
|
|
55
|
+
'reports.operator.ne': 'does not equal',
|
|
56
|
+
'reports.operator.gt': 'is greater than',
|
|
57
|
+
'reports.operator.gte': 'is at least',
|
|
58
|
+
'reports.operator.lt': 'is less than',
|
|
59
|
+
'reports.operator.lte': 'is at most',
|
|
60
|
+
'reports.operator.in': 'is one of',
|
|
61
|
+
'reports.operator.notIn': 'is not one of',
|
|
62
|
+
'reports.operator.contains': 'contains',
|
|
63
|
+
'reports.operator.isNull': 'is empty',
|
|
64
|
+
'reports.operator.isNotNull': 'is not empty',
|
|
65
|
+
'reports.bucket.minute': 'Minute',
|
|
66
|
+
'reports.bucket.hour': 'Hour',
|
|
67
|
+
'reports.bucket.day': 'Day',
|
|
68
|
+
'reports.bucket.week': 'Week',
|
|
69
|
+
'reports.bucket.month': 'Month',
|
|
70
|
+
'reports.bucket.quarter': 'Quarter',
|
|
71
|
+
'reports.bucket.year': 'Year',
|
|
72
|
+
});
|