@dashadmin/dash-reports 1.3.72 → 1.3.74-srcfree.1

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 (75) hide show
  1. package/dist/ReportFilters.d.ts +9 -0
  2. package/dist/ReportFiltersDrawer.d.ts +12 -0
  3. package/dist/ReportPage.d.ts +9 -0
  4. package/dist/ReportsDashboard.d.ts +7 -0
  5. package/dist/ReportsIndex.d.ts +7 -0
  6. package/dist/ReportsModule.d.ts +7 -0
  7. package/dist/TenantDashboard.d.ts +12 -0
  8. package/dist/adapters/mui/BarWidget.d.ts +3 -0
  9. package/dist/adapters/mui/DoughnutWidget.d.ts +3 -0
  10. package/dist/adapters/mui/FunnelWidget.d.ts +3 -0
  11. package/dist/adapters/mui/KpiWidget.d.ts +3 -0
  12. package/dist/adapters/mui/LineWidget.d.ts +3 -0
  13. package/dist/adapters/mui/PieWidget.d.ts +3 -0
  14. package/dist/adapters/mui/PreviewWidget.d.ts +10 -0
  15. package/dist/adapters/mui/TableWidget.d.ts +16 -0
  16. package/dist/adapters/mui/chartSetup.d.ts +2 -0
  17. package/dist/adapters/mui/chartTheme.d.ts +71 -0
  18. package/dist/adapters/mui/index.d.ts +12 -0
  19. package/dist/adapters/mui/palette.d.ts +9 -0
  20. package/dist/adapters/mui/useChartData.d.ts +14 -0
  21. package/dist/core/formatBucket.d.ts +4 -0
  22. package/dist/core/label.d.ts +3 -0
  23. package/dist/core/queryParams.d.ts +3 -0
  24. package/dist/core/types.d.ts +94 -0
  25. package/dist/core/useDashboard.d.ts +50 -0
  26. package/dist/core/useReportExport.d.ts +7 -0
  27. package/dist/core/useReportList.d.ts +7 -0
  28. package/dist/core/useReportPreview.d.ts +7 -0
  29. package/dist/core/useReportSeries.d.ts +8 -0
  30. package/dist/core/useReportSpec.d.ts +9 -0
  31. package/dist/core/useReportTable.d.ts +10 -0
  32. package/dist/core/visualizationLayout.d.ts +13 -0
  33. package/dist/core/visualizationRegistry.d.ts +14 -0
  34. package/dist/createReportsResource.d.ts +40 -0
  35. package/dist/i18n/en.d.ts +83 -0
  36. package/dist/i18n/es.d.ts +83 -0
  37. package/{src/index.tsx → dist/index.d.ts} +3 -31
  38. package/package.json +13 -10
  39. package/src/ReportFilters.tsx +0 -145
  40. package/src/ReportFiltersDrawer.tsx +0 -73
  41. package/src/ReportPage.tsx +0 -280
  42. package/src/ReportsDashboard.tsx +0 -61
  43. package/src/ReportsIndex.tsx +0 -113
  44. package/src/ReportsModule.tsx +0 -42
  45. package/src/TenantDashboard.tsx +0 -290
  46. package/src/adapters/mui/BarWidget.tsx +0 -66
  47. package/src/adapters/mui/DoughnutWidget.tsx +0 -112
  48. package/src/adapters/mui/FunnelWidget.tsx +0 -108
  49. package/src/adapters/mui/KpiWidget.tsx +0 -123
  50. package/src/adapters/mui/LineWidget.tsx +0 -70
  51. package/src/adapters/mui/PieWidget.tsx +0 -16
  52. package/src/adapters/mui/PreviewWidget.tsx +0 -123
  53. package/src/adapters/mui/TableWidget.tsx +0 -141
  54. package/src/adapters/mui/chartSetup.ts +0 -53
  55. package/src/adapters/mui/chartTheme.ts +0 -84
  56. package/src/adapters/mui/index.ts +0 -29
  57. package/src/adapters/mui/palette.ts +0 -85
  58. package/src/adapters/mui/useChartData.ts +0 -62
  59. package/src/core/formatBucket.ts +0 -73
  60. package/src/core/label.ts +0 -28
  61. package/src/core/queryParams.ts +0 -27
  62. package/src/core/types.ts +0 -157
  63. package/src/core/useDashboard.ts +0 -161
  64. package/src/core/useReportExport.ts +0 -68
  65. package/src/core/useReportList.ts +0 -51
  66. package/src/core/useReportPreview.ts +0 -68
  67. package/src/core/useReportSeries.ts +0 -59
  68. package/src/core/useReportSpec.ts +0 -79
  69. package/src/core/useReportTable.ts +0 -71
  70. package/src/core/visualizationLayout.ts +0 -34
  71. package/src/core/visualizationRegistry.ts +0 -39
  72. package/src/createReportsResource.tsx +0 -81
  73. package/src/i18n/en.ts +0 -95
  74. package/src/i18n/es.ts +0 -84
  75. /package/{src/i18n/index.ts → dist/i18n/index.d.ts} +0 -0
@@ -1,84 +0,0 @@
1
- import type { Theme } from '@mui/material';
2
-
3
- /**
4
- * Chart chrome, taken from the app's MUI theme.
5
- *
6
- * Grid and axis lines are deliberately recessive — a chart's ink belongs to its
7
- * data, and heavy or dashed rules read as content. Text uses the theme's own
8
- * ink tokens, never a series colour: a coloured mark beside a label carries the
9
- * identity, so colouring the text as well adds nothing and costs contrast.
10
- */
11
- export interface ChartChrome {
12
- dark: boolean;
13
- grid: string;
14
- axisText: string;
15
- tooltipBg: string;
16
- tooltipText: string;
17
- surface: string;
18
- }
19
-
20
- export function chartChrome(theme: Theme): ChartChrome {
21
- const dark = theme.palette.mode === 'dark';
22
-
23
- return {
24
- dark,
25
- // ~8% ink: present enough to read a value against, quiet enough to ignore.
26
- grid: dark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.08)',
27
- axisText: theme.palette.text.secondary,
28
- tooltipBg: dark ? 'rgba(32,32,30,0.96)' : 'rgba(255,255,255,0.96)',
29
- tooltipText: theme.palette.text.primary,
30
- surface: theme.palette.background.paper,
31
- };
32
- }
33
-
34
- /** Shared scale/plugin options so every widget reads the same. */
35
- export function baseChartOptions(chrome: ChartChrome, showLegend: boolean) {
36
- return {
37
- responsive: true,
38
- maintainAspectRatio: false,
39
- interaction: {
40
- // Whole-bucket hover: the hit target is the column, not the mark, so a
41
- // thin bar or a small point does not demand pixel-accurate aim.
42
- mode: 'index' as const,
43
- intersect: false,
44
- },
45
- plugins: {
46
- legend: {
47
- display: showLegend,
48
- position: 'bottom' as const,
49
- labels: {
50
- color: chrome.axisText,
51
- usePointStyle: true,
52
- pointStyle: 'circle' as const,
53
- boxWidth: 8,
54
- boxHeight: 8,
55
- padding: 16,
56
- },
57
- },
58
- tooltip: {
59
- backgroundColor: chrome.tooltipBg,
60
- titleColor: chrome.tooltipText,
61
- bodyColor: chrome.tooltipText,
62
- borderColor: chrome.grid,
63
- borderWidth: 1,
64
- padding: 10,
65
- cornerRadius: 6,
66
- displayColors: true,
67
- usePointStyle: true,
68
- },
69
- },
70
- scales: {
71
- x: {
72
- grid: { display: false },
73
- border: { color: chrome.grid },
74
- ticks: { color: chrome.axisText, maxRotation: 0, autoSkipPadding: 12 },
75
- },
76
- y: {
77
- beginAtZero: true,
78
- grid: { color: chrome.grid },
79
- border: { display: false },
80
- ticks: { color: chrome.axisText, precision: 0 as unknown as number },
81
- },
82
- },
83
- };
84
- }
@@ -1,29 +0,0 @@
1
- import { registerVisualization } from '../../core/visualizationRegistry';
2
- import { BarWidget } from './BarWidget';
3
- import { LineWidget } from './LineWidget';
4
- import { DoughnutWidget } from './DoughnutWidget';
5
- import { PieWidget } from './PieWidget';
6
- import { KpiWidget } from './KpiWidget';
7
- import { FunnelWidget } from './FunnelWidget';
8
- import { SeriesTableWidget } from './TableWidget';
9
-
10
- /**
11
- * Register the MUI/Chart.js renderers.
12
- *
13
- * Importing this module is what wires the adapter up; `core/` never imports a
14
- * chart library, so a different adapter can register its own set against the
15
- * same registry without this one being loaded.
16
- */
17
- registerVisualization('Bar', BarWidget);
18
- registerVisualization('Line', LineWidget);
19
- registerVisualization('Doughnut', DoughnutWidget);
20
- registerVisualization('Pie', PieWidget);
21
- registerVisualization('Kpi', KpiWidget);
22
- registerVisualization('Funnel', FunnelWidget);
23
- registerVisualization('Table', SeriesTableWidget);
24
-
25
- export { BarWidget, LineWidget, DoughnutWidget, PieWidget, KpiWidget, FunnelWidget, SeriesTableWidget };
26
- export { DetailTable } from './TableWidget';
27
- export { PreviewWidget } from './PreviewWidget';
28
- export * from './palette';
29
- export { chartChrome, baseChartOptions } from './chartTheme';
@@ -1,85 +0,0 @@
1
- import type { ReportSpec } from '../../core/types';
2
-
3
- /**
4
- * The categorical series palette.
5
- *
6
- * Eight hues, in a fixed order, validated for colour-vision deficiency against
7
- * both surfaces (worst adjacent CVD ΔE 9.1 light / 8.4 dark on the OKLab×100
8
- * scale; worst adjacent normal-vision ΔE 19.6 / 19.3). The dark column is the
9
- * same eight hues re-stepped for a dark surface — not an automatic flip of the
10
- * light values, which would drop several below the 3:1 contrast floor.
11
- *
12
- * Three light-mode hues (aqua, yellow, magenta) sit under 3:1 against the light
13
- * surface. That is permitted only with "relief": a legend is always rendered
14
- * for two or more series and every report also offers a Table visualisation,
15
- * so no value is reachable by colour alone.
16
- */
17
- export const SERIES_LIGHT = [
18
- '#2a78d6', // blue
19
- '#eb6834', // orange
20
- '#1baf7a', // aqua
21
- '#eda100', // yellow
22
- '#e87ba4', // magenta
23
- '#008300', // green
24
- '#4a3aa7', // violet
25
- '#e34948', // red
26
- ] as const;
27
-
28
- export const SERIES_DARK = [
29
- '#3987e5',
30
- '#d95926',
31
- '#199e70',
32
- '#c98500',
33
- '#d55181',
34
- '#008300',
35
- '#9085e9',
36
- '#e66767',
37
- ] as const;
38
-
39
- /** Anything past the eighth slot folds into this rather than inventing a hue. */
40
- export const OTHER_LIGHT = '#8a8a80';
41
- export const OTHER_DARK = '#6f6f66';
42
-
43
- export const MAX_SERIES = SERIES_LIGHT.length;
44
-
45
- export function seriesPalette(dark: boolean): readonly string[] {
46
- return dark ? SERIES_DARK : SERIES_LIGHT;
47
- }
48
-
49
- /**
50
- * A stable slot for every series key.
51
- *
52
- * Keyed on the ENTITY, never on its position in the current result. Assigning
53
- * by index would repaint every surviving series the moment a filter removed one
54
- * above it — the reader sees "delivered" change from aqua to orange and reads it
55
- * as a different thing. The spec's declared dimension values give a fixed order
56
- * that survives filtering; metric order does the same when there is no pivot.
57
- * Keys the spec does not declare (a free-form pivot like product name) fall back
58
- * to first-seen order, which is at least stable within one result.
59
- */
60
- export function buildSlotMap(spec: ReportSpec | null, pivotKey: string | undefined, seriesKeys: string[]): Map<string, number> {
61
- const ordered: string[] = [];
62
-
63
- const pivotDimension = pivotKey ? spec?.dimensions?.find((d) => d.key === pivotKey) : undefined;
64
-
65
- if (pivotDimension?.values?.length) {
66
- pivotDimension.values.forEach((v) => ordered.push(String(v.value)));
67
- } else if (!pivotKey && spec?.metrics?.length) {
68
- spec.metrics.forEach((m) => ordered.push(m.key));
69
- }
70
-
71
- seriesKeys.forEach((k) => {
72
- if (!ordered.includes(k)) ordered.push(k);
73
- });
74
-
75
- const map = new Map<string, number>();
76
- ordered.forEach((key, index) => map.set(key, index));
77
-
78
- return map;
79
- }
80
-
81
- export function colorForSlot(slot: number, dark: boolean): string {
82
- if (slot >= MAX_SERIES) return dark ? OTHER_DARK : OTHER_LIGHT;
83
-
84
- return seriesPalette(dark)[slot];
85
- }
@@ -1,62 +0,0 @@
1
- import { useMemo } from 'react';
2
- import { useTheme } from '@mui/material';
3
- import { formatBucket, formatBucketLong } from '../../core/formatBucket';
4
- import { buildSlotMap, colorForSlot, MAX_SERIES } from './palette';
5
- import { chartChrome } from './chartTheme';
6
- import { label as renderLabel } from '../../core/label';
7
- import type { VisualizationProps } from '../../core/visualizationRegistry';
8
-
9
- /**
10
- * Everything the cartesian widgets share: stable colours, folded overflow,
11
- * formatted labels and the resolved chrome.
12
- *
13
- * Series past the eighth are folded into a single "Other" series rather than
14
- * given a generated hue — nine-plus categorical colours stop being
15
- * distinguishable, and a reader cannot hold that many identities at once. The
16
- * fold is additive, so the total still reconciles with the table.
17
- */
18
- export function useChartData({ result, spec, translate }: VisualizationProps) {
19
- const theme = useTheme();
20
-
21
- return useMemo(() => {
22
- const chrome = chartChrome(theme);
23
- const pivotKey = result.meta?.pivot ?? undefined;
24
-
25
- const slots = buildSlotMap(spec, pivotKey ?? undefined, result.series.map((s) => s.key));
26
-
27
- const kept = result.series.filter((s) => (slots.get(s.key) ?? 0) < MAX_SERIES);
28
- const overflow = result.series.filter((s) => (slots.get(s.key) ?? 0) >= MAX_SERIES);
29
-
30
- const datasets = kept.map((series) => ({
31
- key: series.key,
32
- label: renderLabel(translate, series.label, series.labelText),
33
- data: series.data,
34
- color: colorForSlot(slots.get(series.key) ?? 0, chrome.dark),
35
- }));
36
-
37
- if (overflow.length > 0) {
38
- // Sum the tail position-wise so the folded series is still a real total.
39
- const summed = result.buckets.map((_, index) =>
40
- overflow.reduce<number | null>((acc, series) => {
41
- const value = series.data[index];
42
- if (value === null || value === undefined) return acc;
43
- return (acc ?? 0) + value;
44
- }, null),
45
- );
46
-
47
- datasets.push({
48
- key: '__other__',
49
- label: translate('dash_reports.other', { _: 'Other', count: overflow.length }),
50
- data: summed,
51
- color: colorForSlot(MAX_SERIES, chrome.dark),
52
- });
53
- }
54
-
55
- const labels = result.buckets.map((b) => formatBucket(b, result.granularity, translate));
56
- const longLabels = result.buckets.map((b) => formatBucketLong(b, result.granularity, translate));
57
-
58
- return { chrome, datasets, labels, longLabels, foldedCount: overflow.length };
59
- }, [result, spec, translate, theme]);
60
- }
61
-
62
- export default useChartData;
@@ -1,73 +0,0 @@
1
- import type { Granularity, TranslateFn } from './types';
2
-
3
- /**
4
- * Turn a bucket key into an axis label.
5
- *
6
- * Series grains arrive as 'YYYY-MM-DD HH:mm:ss' in the REPORTING timezone —
7
- * already local, already truncated. They are formatted by string slicing rather
8
- * than by `new Date(...)`, deliberately: parsing them into a Date would apply
9
- * the browser's timezone to a value the server already converted, shifting
10
- * every label by the offset between the viewer and the venue. A restaurant in
11
- * Santiago read from a laptop in Madrid must still show Santiago's days.
12
- */
13
- export function formatBucket(
14
- bucket: string | number,
15
- granularity: Granularity,
16
- translate: TranslateFn,
17
- ): string {
18
- // A total has exactly one bucket covering the whole window, so it has no
19
- // meaningful axis label — the surrounding card already states the period.
20
- if (granularity === 'total') {
21
- return translate('dash_reports.granularity.total', { _: 'Total' });
22
- }
23
-
24
- if (granularity === 'hourOfDay') {
25
- return `${String(bucket).padStart(2, '0')}:00`;
26
- }
27
-
28
- if (granularity === 'dayOfWeek') {
29
- // ISO day: 1 = Monday .. 7 = Sunday.
30
- const key = ['monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday', 'sunday'][Number(bucket) - 1];
31
- return translate(`dash_reports.weekdays.${key}`, { _: key ?? String(bucket) });
32
- }
33
-
34
- const raw = String(bucket);
35
- const date = raw.slice(0, 10); // YYYY-MM-DD
36
- const [year, month, day] = date.split('-');
37
-
38
- switch (granularity) {
39
- case 'yearly':
40
- return year;
41
- case 'monthly':
42
- return `${month}/${year}`;
43
- case 'weekly':
44
- return `${day}/${month}`;
45
- case 'daily':
46
- default:
47
- return `${day}/${month}`;
48
- }
49
- }
50
-
51
- /** A longer form for tooltips, where there is room to disambiguate. */
52
- export function formatBucketLong(
53
- bucket: string | number,
54
- granularity: Granularity,
55
- translate: TranslateFn,
56
- ): string {
57
- if (granularity === 'total' || granularity === 'hourOfDay' || granularity === 'dayOfWeek') {
58
- return formatBucket(bucket, granularity, translate);
59
- }
60
-
61
- const raw = String(bucket);
62
- const [year, month, day] = raw.slice(0, 10).split('-');
63
-
64
- if (granularity === 'yearly') return year;
65
- if (granularity === 'monthly') return `${month}/${year}`;
66
- if (granularity === 'weekly') {
67
- return translate('dash_reports.week_of', { _: `Week of ${day}/${month}/${year}`, date: `${day}/${month}/${year}` });
68
- }
69
-
70
- return `${day}/${month}/${year}`;
71
- }
72
-
73
- export default formatBucket;
package/src/core/label.ts DELETED
@@ -1,28 +0,0 @@
1
- import type { TranslateFn } from './types';
2
-
3
- /**
4
- * Render a backend label.
5
- *
6
- * The API sends a translation KEY plus the text the backend resolved for it.
7
- * This prefers the app's own translation of the key and falls back to the
8
- * backend's text — polyglot's `_` option does exactly that, returning the
9
- * default whenever the key is missing.
10
- *
11
- * Why both: sending only the key means every app must redeclare every report's
12
- * vocabulary, and a newly registered report renders as `reports.x.title` until
13
- * someone ships a frontend change. Sending only text would take the override
14
- * away and make responses locale-bound. This way a new report is readable with
15
- * no frontend change, and an app that wants different wording still wins by
16
- * defining the key.
17
- */
18
- export function label(
19
- translate: TranslateFn,
20
- key: string | undefined,
21
- text?: string,
22
- ): string {
23
- if (!key) return text ?? '';
24
-
25
- return translate(key, { _: text ?? key });
26
- }
27
-
28
- export default label;
@@ -1,27 +0,0 @@
1
- import type { ReportFilterState } from './types';
2
-
3
- /**
4
- * Flatten filter state into the query the backend expects.
5
- *
6
- * Dimension selections are sent under their own key (`?status[]=CREATED`),
7
- * which is what ReportQuery::resolveFilters reads. Empty selections are
8
- * omitted entirely rather than sent as an empty array — an empty `whereIn`
9
- * matches nothing, so sending one would turn "no filter" into "no rows".
10
- */
11
- export function toQueryParams(filters: ReportFilterState): Record<string, unknown> {
12
- const params: Record<string, unknown> = {
13
- start: filters.start,
14
- end: filters.end,
15
- granularity: filters.granularity,
16
- };
17
-
18
- if (filters.pivot) params.pivot = filters.pivot;
19
-
20
- Object.entries(filters.dimensions ?? {}).forEach(([key, values]) => {
21
- if (Array.isArray(values) && values.length > 0) params[key] = values;
22
- });
23
-
24
- return params;
25
- }
26
-
27
- export default toQueryParams;
package/src/core/types.ts DELETED
@@ -1,157 +0,0 @@
1
- /**
2
- * The wire contract with app/ReportCore.
3
- *
4
- * A ReportSpec is the single description the backend serves for a report, and
5
- * everything this package renders — filters, chart shells, export controls —
6
- * is derived from it. Nothing here is KitchnTabs-specific: a domain adds a
7
- * report by registering a definition server-side, and this package renders it
8
- * without a frontend change.
9
- *
10
- * Deliberately NOT called a "manifest": `ResourceManifest` already means a lazy
11
- * resource-module map in dash-app-common and dash-admin, and reusing the word
12
- * for a second, unrelated concept would be a genuine ambiguity in a codebase
13
- * that already has both.
14
- *
15
- * Every `label` is a translation KEY, never display text. The previous
16
- * generation of this feature returned Spanish strings from PHP, which is what
17
- * made it unusable for a second domain.
18
- */
19
-
20
- export type Granularity =
21
- /** One bucket for the whole window — a period total, not a timeline. */
22
- | 'total'
23
- | 'daily'
24
- | 'weekly'
25
- | 'monthly'
26
- | 'yearly'
27
- | 'hourOfDay'
28
- | 'dayOfWeek';
29
-
30
- export type VisualizationType =
31
- | 'Bar'
32
- | 'Line'
33
- | 'Pie'
34
- | 'Doughnut'
35
- | 'Kpi'
36
- | 'Funnel'
37
- | 'Table';
38
-
39
- export interface DimensionValue {
40
- value: string | number | boolean;
41
- /** Translation key. */
42
- label: string;
43
- /** Backend-resolved text, used when the app has no translation for `label`. */
44
- labelText?: string;
45
- icon?: string;
46
- }
47
-
48
- export interface ReportDimension {
49
- key: string;
50
- label: string;
51
- labelText?: string;
52
- type: 'column' | 'jsonpath' | 'relation';
53
- multiple?: boolean;
54
- source?: string;
55
- visibleTo?: string[];
56
- values?: DimensionValue[];
57
- }
58
-
59
- export interface ReportMetric {
60
- key: string;
61
- label: string;
62
- labelText?: string;
63
- kind: 'count' | 'countNotNull' | 'countDistinct' | 'sum' | 'avg' | 'min' | 'max';
64
- /** Render as money/decimal rather than an integer. */
65
- decimal: boolean;
66
- /** 'minutes' for a duration metric; the UI appends the unit. */
67
- unit?: string | null;
68
- }
69
-
70
- export interface ReportVisualization {
71
- type: VisualizationType;
72
- options?: Record<string, unknown>;
73
- }
74
-
75
- export interface ReportSpec {
76
- key: string;
77
- title: string;
78
- titleText?: string;
79
- granularities: Granularity[];
80
- defaultGranularity: Granularity;
81
- timezone: string;
82
- dimensions: ReportDimension[];
83
- metrics: ReportMetric[];
84
- visualizations: ReportVisualization[];
85
- /** How this report is best read; the tab that opens first. */
86
- defaultVisualization: VisualizationType;
87
- previewGranularity: Granularity;
88
- filters: Record<string, { type: string }>;
89
- exporters: string[];
90
- defaultRangeDays: number;
91
- }
92
-
93
- export interface ReportSeries {
94
- key: string;
95
- /** Translation key (or literal text for a value like a tenant name). */
96
- label: string;
97
- /** Backend-resolved text; used when the app has no translation for `label`. */
98
- labelText?: string;
99
- metric: string;
100
- /** Exactly one point per bucket; null means "no data", 0 means "none". */
101
- data: Array<number | null>;
102
- }
103
-
104
- export interface ReportResult {
105
- key: string;
106
- granularity: Granularity;
107
- timezone: string;
108
- /** Timestamps for series grains, small ints for cyclic ones. */
109
- buckets: Array<string | number>;
110
- series: ReportSeries[];
111
- totals: Record<string, number | null>;
112
- meta?: {
113
- start?: string;
114
- end?: string;
115
- pivot?: string | null;
116
- scopedTenantIds?: string[] | null;
117
- };
118
- }
119
-
120
- export interface ReportTablePage {
121
- data: Array<Record<string, unknown>>;
122
- total: number;
123
- page: number;
124
- perPage: number;
125
- lastPage: number;
126
- }
127
-
128
- export interface ReportListEntry {
129
- key: string;
130
- title: string;
131
- titleText?: string;
132
- metrics?: ReportMetric[];
133
- visualizations: ReportVisualization[];
134
- /** How this report is best read — drives the list preview and the first tab. */
135
- defaultVisualization: VisualizationType;
136
- /** Coarser grain for the preview: a card is too small for a daily series. */
137
- previewGranularity: Granularity;
138
- defaultRangeDays: number;
139
- }
140
-
141
- /** The user-driven part of a report request. */
142
- export interface ReportFilterState {
143
- start: string;
144
- end: string;
145
- granularity: Granularity;
146
- pivot?: string;
147
- /** Dimension key -> selected values. */
148
- dimensions: Record<string, Array<string | number | boolean>>;
149
- }
150
-
151
- /**
152
- * Injected rather than imported from react-admin, so this package stays usable
153
- * in a public/light app that has no react-admin i18n provider at all. The `_`
154
- * option is polyglot's default-value convention, so a missing key renders
155
- * sensible text instead of the raw key.
156
- */
157
- export type TranslateFn = (key: string, options?: Record<string, unknown>) => string;