@fayz-ai/plugin-reports 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-2ZFN6ECM.js → chunk-4SPBNVQA.js} +3 -2
- package/dist/chunk-4SPBNVQA.js.map +1 -0
- package/dist/data/analytics-mock.d.ts.map +1 -1
- package/dist/data/analytics-supabase.d.ts.map +1 -1
- package/dist/{excel-WE73YE2V.js → excel-KIJTC7OJ.js} +3 -3
- package/dist/{excel-WE73YE2V.js.map → excel-KIJTC7OJ.js.map} +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +367 -112
- package/dist/index.js.map +1 -1
- package/dist/lib/columns.d.ts.map +1 -1
- package/dist/lib/legacy.d.ts.map +1 -1
- package/dist/lib/tenant.d.ts +1 -0
- package/dist/lib/tenant.d.ts.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/{pdf-E6DXHFXU.js → pdf-7QQ3U5JQ.js} +3 -3
- package/dist/{pdf-E6DXHFXU.js.map → pdf-7QQ3U5JQ.js.map} +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/AnalyticsAssistant.d.ts +3 -0
- package/dist/views/AnalyticsAssistant.d.ts.map +1 -0
- package/dist/views/AnalyticsModule.d.ts +10 -0
- package/dist/views/AnalyticsModule.d.ts.map +1 -0
- package/dist/views/AnalyticsOverview.d.ts.map +1 -1
- package/dist/views/AnalyticsReportViewer.d.ts.map +1 -1
- package/dist/views/AnalyticsSurface.d.ts +27 -0
- package/dist/views/AnalyticsSurface.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/chunk-2ZFN6ECM.js.map +0 -1
- package/dist/components/ReportCard.d.ts +0 -9
- package/dist/components/ReportCard.d.ts.map +0 -1
- package/dist/components/ReportFilterBar.d.ts +0 -12
- package/dist/components/ReportFilterBar.d.ts.map +0 -1
- package/dist/components/ReportSummaryRow.d.ts +0 -10
- package/dist/components/ReportSummaryRow.d.ts.map +0 -1
- package/dist/context.d.ts +0 -16
- package/dist/context.d.ts.map +0 -1
- package/dist/hooks/useReportData.d.ts +0 -19
- package/dist/hooks/useReportData.d.ts.map +0 -1
- package/dist/views/ReportHub.d.ts +0 -8
- package/dist/views/ReportHub.d.ts.map +0 -1
- package/dist/views/ReportViewer.d.ts +0 -9
- package/dist/views/ReportViewer.d.ts.map +0 -1
- package/dist/views/ReportsPage.d.ts +0 -8
- package/dist/views/ReportsPage.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { reportColumnsToTanstack, formatValuePlain } from './chunk-
|
|
2
|
-
import * as
|
|
3
|
-
import
|
|
1
|
+
import { reportColumnsToTanstack, formatValuePlain } from './chunk-4SPBNVQA.js';
|
|
2
|
+
import * as React6 from 'react';
|
|
3
|
+
import React6__default, { useState, useEffect } from 'react';
|
|
4
4
|
import { usePluginRuntime, getAnalyticsCards, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, hashRouterAdapter, useTranslation, exportCSV } from '@fayz-ai/core';
|
|
5
|
-
import {
|
|
5
|
+
import { useOrganizationStore, useShellRailAvailable, useContextualNav, AssistantInvite } from '@fayz-ai/admin';
|
|
6
|
+
import { AnalyticsContextProvider, useModuleLayout, ModulePage, collectFilters, AnalyticsControls, AnalyticsGrid, Card, FilterChip, DataTable, Button, SubpageHeader, useAnalyticsCard, useAnalyticsContext, AnalyticsCard, Dropdown, DropdownTrigger, DropdownContent, DropdownItem } from '@fayz-ai/ui';
|
|
6
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
8
|
import { BarChart3, User, Search, ArrowLeft, Printer, Construction, Download, Radio, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
8
9
|
|
|
@@ -59,10 +60,10 @@ function matchesFilters(row, filters) {
|
|
|
59
60
|
function inRange(row, dateColumn, from, to) {
|
|
60
61
|
const raw = row[dateColumn];
|
|
61
62
|
if (raw == null) return true;
|
|
62
|
-
const
|
|
63
|
-
if (Number.isNaN(
|
|
64
|
-
if (from &&
|
|
65
|
-
if (to &&
|
|
63
|
+
const t = new Date(String(raw)).getTime();
|
|
64
|
+
if (Number.isNaN(t)) return true;
|
|
65
|
+
if (from && t < (/* @__PURE__ */ new Date(`${from}T00:00:00`)).getTime()) return false;
|
|
66
|
+
if (to && t > (/* @__PURE__ */ new Date(`${to}T23:59:59.999`)).getTime()) return false;
|
|
66
67
|
return true;
|
|
67
68
|
}
|
|
68
69
|
function applyMeasure(rows, agg, column) {
|
|
@@ -163,7 +164,7 @@ function reservedKeys(card) {
|
|
|
163
164
|
]);
|
|
164
165
|
}
|
|
165
166
|
function searchColumns(card) {
|
|
166
|
-
const cols = (card.columns ?? []).filter((c) => c.type === "text" || c.type === "email").map((c) => c.key);
|
|
167
|
+
const cols = (card.columns ?? []).filter((c) => c.type === "text" || c.type === "email" || c.type === "person").map((c) => c.key);
|
|
167
168
|
return cols.length ? cols : void 0;
|
|
168
169
|
}
|
|
169
170
|
function camelToSnake(s) {
|
|
@@ -204,6 +205,61 @@ async function selectFromView(card, state, db, tenantId, reserved) {
|
|
|
204
205
|
total: count ?? 0
|
|
205
206
|
};
|
|
206
207
|
}
|
|
208
|
+
function toError(err) {
|
|
209
|
+
if (err instanceof Error) return err;
|
|
210
|
+
if (err && typeof err === "object") {
|
|
211
|
+
const e = err;
|
|
212
|
+
const message = [...new Set(
|
|
213
|
+
[e.message, e.details, e.hint].filter(
|
|
214
|
+
(part) => typeof part === "string" && part.trim().length > 0
|
|
215
|
+
)
|
|
216
|
+
)].join(" \u2014 ");
|
|
217
|
+
if (message) return Object.assign(new Error(message), e.code ? { code: e.code } : {});
|
|
218
|
+
}
|
|
219
|
+
return new Error(String(err));
|
|
220
|
+
}
|
|
221
|
+
function isAggregateCard(card) {
|
|
222
|
+
const { source } = card.query;
|
|
223
|
+
if (source.kind !== "view" || !source.name) return false;
|
|
224
|
+
return (card.query.dimensions?.length ?? 0) > 0 || (card.query.measures?.length ?? 0) > 0;
|
|
225
|
+
}
|
|
226
|
+
function runArgs(card, state, tenantId) {
|
|
227
|
+
const size = state.pageSize || 50;
|
|
228
|
+
const snapshot = card.query.dateColumn === null;
|
|
229
|
+
return {
|
|
230
|
+
source: card.query.source.name,
|
|
231
|
+
tenant_id: tenantId ?? null,
|
|
232
|
+
from: !snapshot && state.dateRange.from ? `${state.dateRange.from}T00:00:00` : null,
|
|
233
|
+
// Inclusive end-of-day: the range picker speaks in days, the column is a
|
|
234
|
+
// timestamp, and closing at midnight loses everything sold that day.
|
|
235
|
+
to: !snapshot && state.dateRange.to ? `${state.dateRange.to}T23:59:59.999` : null,
|
|
236
|
+
dimensions: (card.query.dimensions ?? []).map((d) => ({
|
|
237
|
+
key: d.key,
|
|
238
|
+
...d.grain && d.grain !== "auto" ? { grain: d.grain } : {}
|
|
239
|
+
})),
|
|
240
|
+
measures: (card.query.measures ?? []).filter((m) => m.agg !== "ratio").map((m) => ({ key: m.key, agg: m.agg, ...m.column ? { column: m.column } : {} })),
|
|
241
|
+
filters: state.filters ?? {},
|
|
242
|
+
search: state.search ?? null,
|
|
243
|
+
search_columns: searchColumns(card) ?? null,
|
|
244
|
+
sort: state.sortBy ?? card.query.defaultSort ?? null,
|
|
245
|
+
dir: state.sortDir ?? card.query.defaultSortDir ?? "desc",
|
|
246
|
+
limit: card.query.limit ?? size,
|
|
247
|
+
offset: card.query.limit ? 0 : ((state.page || 1) - 1) * size
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
function toResult(data, card, reserved) {
|
|
251
|
+
const rows = data?.rows ?? [];
|
|
252
|
+
return {
|
|
253
|
+
rows: rows.map((r) => mapRow(r, reserved, card.query.source.columnMap)),
|
|
254
|
+
total: data?.total ?? rows.length,
|
|
255
|
+
summary: data?.summary ?? void 0
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function batchUnavailable(err) {
|
|
259
|
+
const code = typeof err?.code === "string" ? err.code : "";
|
|
260
|
+
if (["42883", "PGRST202"].includes(code)) return true;
|
|
261
|
+
return typeof err?.message === "string" && err.message.includes("analytics_run_batch");
|
|
262
|
+
}
|
|
207
263
|
function createSupabaseAnalyticsProvider(config) {
|
|
208
264
|
const resolveTenantId = () => (typeof config.tenantId === "function" ? config.tenantId() : config.tenantId) || void 0;
|
|
209
265
|
function client() {
|
|
@@ -211,18 +267,19 @@ function createSupabaseAnalyticsProvider(config) {
|
|
|
211
267
|
if (!supabase) throw new Error("Supabase client not available");
|
|
212
268
|
return config.schema && config.schema !== "public" ? supabase.schema(config.schema) : supabase;
|
|
213
269
|
}
|
|
270
|
+
let batchSupported = true;
|
|
214
271
|
return {
|
|
215
272
|
async run(card, state) {
|
|
216
273
|
const { source } = card.query;
|
|
217
274
|
const reserved = reservedKeys(card);
|
|
218
275
|
if (source.kind === "fn") {
|
|
219
276
|
if (!source.run) throw new Error(`analytics: card ${card.id} declares kind 'fn' with no run()`);
|
|
220
|
-
const
|
|
277
|
+
const rows = await source.run({
|
|
221
278
|
tenantId: resolveTenantId(),
|
|
222
279
|
dateRange: state.dateRange,
|
|
223
280
|
filters: state.filters
|
|
224
281
|
});
|
|
225
|
-
return aggregateRows(
|
|
282
|
+
return aggregateRows(rows, card, state);
|
|
226
283
|
}
|
|
227
284
|
if (!source.name) throw new Error(`analytics: card ${card.id} has no source name`);
|
|
228
285
|
const isDetail = (card.query.dimensions?.length ?? 0) === 0 && (card.query.measures?.length ?? 0) === 0;
|
|
@@ -237,42 +294,80 @@ function createSupabaseAnalyticsProvider(config) {
|
|
|
237
294
|
...Object.fromEntries(Object.entries(state.filters ?? {}).map(([k, v]) => [`p_${k}`, v]))
|
|
238
295
|
});
|
|
239
296
|
if (error2) throw error2;
|
|
240
|
-
const
|
|
297
|
+
const rows = Array.isArray(data2) ? data2 : data2?.rows ?? [];
|
|
241
298
|
return {
|
|
242
|
-
rows:
|
|
243
|
-
total: Array.isArray(data2) ?
|
|
299
|
+
rows: rows.map((r) => mapRow(r, reserved, source.columnMap)),
|
|
300
|
+
total: Array.isArray(data2) ? rows.length : data2?.total ?? rows.length,
|
|
244
301
|
summary: Array.isArray(data2) ? void 0 : data2?.summary
|
|
245
302
|
};
|
|
246
303
|
}
|
|
247
|
-
const
|
|
248
|
-
const snapshot = card.query.dateColumn === null;
|
|
304
|
+
const args = runArgs(card, state, resolveTenantId());
|
|
249
305
|
const { data, error } = await client().rpc("analytics_run", {
|
|
250
|
-
p_source: source
|
|
251
|
-
p_tenant_id:
|
|
252
|
-
p_from:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
p_search_columns: searchColumns(card) ?? null,
|
|
264
|
-
p_sort: state.sortBy ?? card.query.defaultSort ?? null,
|
|
265
|
-
p_dir: state.sortDir ?? card.query.defaultSortDir ?? "desc",
|
|
266
|
-
p_limit: card.query.limit ?? size,
|
|
267
|
-
p_offset: card.query.limit ? 0 : ((state.page || 1) - 1) * size
|
|
306
|
+
p_source: args.source,
|
|
307
|
+
p_tenant_id: args.tenant_id,
|
|
308
|
+
p_from: args.from,
|
|
309
|
+
p_to: args.to,
|
|
310
|
+
p_dimensions: args.dimensions,
|
|
311
|
+
p_measures: args.measures,
|
|
312
|
+
p_filters: args.filters,
|
|
313
|
+
p_search: args.search,
|
|
314
|
+
p_search_columns: args.search_columns,
|
|
315
|
+
p_sort: args.sort,
|
|
316
|
+
p_dir: args.dir,
|
|
317
|
+
p_limit: args.limit,
|
|
318
|
+
p_offset: args.offset
|
|
268
319
|
});
|
|
269
320
|
if (error) throw error;
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
321
|
+
return toResult(data, card, reserved);
|
|
322
|
+
},
|
|
323
|
+
// -----------------------------------------------------------------------
|
|
324
|
+
// The batched path.
|
|
325
|
+
//
|
|
326
|
+
// Every aggregate card in the batch becomes ONE analytics_run_batch call;
|
|
327
|
+
// detail listings and 'fn' cards, which the batch function cannot express,
|
|
328
|
+
// still go one by one — but a dashboard is all aggregates, so opening
|
|
329
|
+
// /analytics went from forty-one requests to one.
|
|
330
|
+
//
|
|
331
|
+
// Results keep their INDEX. The caller matched a promise to a position, and
|
|
332
|
+
// returning a compacted array would silently hand card 7 card 9's numbers.
|
|
333
|
+
// -----------------------------------------------------------------------
|
|
334
|
+
async runMany(requests) {
|
|
335
|
+
const out = new Array(requests.length);
|
|
336
|
+
const batchable = [];
|
|
337
|
+
for (let i = 0; i < requests.length; i++) {
|
|
338
|
+
if (batchSupported && isAggregateCard(requests[i].card)) batchable.push(i);
|
|
339
|
+
}
|
|
340
|
+
const singles = requests.map((req, i) => ({ req, i })).filter(({ i }) => !batchable.includes(i)).map(({ req, i }) => this.run(req.card, req.state).then((r) => {
|
|
341
|
+
out[i] = r;
|
|
342
|
+
}).catch((e) => {
|
|
343
|
+
out[i] = toError(e);
|
|
344
|
+
}));
|
|
345
|
+
if (batchable.length > 0) {
|
|
346
|
+
const tenantId = resolveTenantId();
|
|
347
|
+
const payload = batchable.map((i) => runArgs(requests[i].card, requests[i].state, tenantId));
|
|
348
|
+
const { data, error } = await client().rpc("analytics_run_batch", { p_requests: payload });
|
|
349
|
+
if (error && batchUnavailable(error)) {
|
|
350
|
+
batchSupported = false;
|
|
351
|
+
await Promise.all([
|
|
352
|
+
...singles,
|
|
353
|
+
...batchable.map((i) => this.run(requests[i].card, requests[i].state).then((r) => {
|
|
354
|
+
out[i] = r;
|
|
355
|
+
}).catch((e) => {
|
|
356
|
+
out[i] = toError(e);
|
|
357
|
+
}))
|
|
358
|
+
]);
|
|
359
|
+
return out;
|
|
360
|
+
}
|
|
361
|
+
if (error) throw error;
|
|
362
|
+
const results = Array.isArray(data) ? data : [];
|
|
363
|
+
batchable.forEach((cardIndex, slot) => {
|
|
364
|
+
const entry = results[slot];
|
|
365
|
+
const { card } = requests[cardIndex];
|
|
366
|
+
out[cardIndex] = entry?.error ? Object.assign(toError(entry.error), { code: entry.code }) : toResult(entry, card, reservedKeys(card));
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
await Promise.all(singles);
|
|
370
|
+
return out;
|
|
276
371
|
}
|
|
277
372
|
};
|
|
278
373
|
}
|
|
@@ -286,10 +381,10 @@ function seeded(seed) {
|
|
|
286
381
|
}
|
|
287
382
|
return () => {
|
|
288
383
|
h += 1831565813;
|
|
289
|
-
let
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
return ((
|
|
384
|
+
let t = h;
|
|
385
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
386
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
387
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
293
388
|
};
|
|
294
389
|
}
|
|
295
390
|
var SAMPLE_LABELS = {
|
|
@@ -338,21 +433,37 @@ function createMockAnalyticsProvider() {
|
|
|
338
433
|
return row;
|
|
339
434
|
});
|
|
340
435
|
return aggregateRows(rows, card, state);
|
|
436
|
+
},
|
|
437
|
+
// There is no transport to save a round trip on, so this is just the loop.
|
|
438
|
+
// It exists because the mock is reached through createSafeDataProvider,
|
|
439
|
+
// whose Proxy answers EVERY property with a function — `provider.runMany`
|
|
440
|
+
// is truthy even for a provider that never declared it, and the caller's
|
|
441
|
+
// feature check therefore cannot detect its absence. Implementing it is how
|
|
442
|
+
// the mock stops being a landmine behind that Proxy.
|
|
443
|
+
async runMany(requests) {
|
|
444
|
+
return Promise.all(requests.map((r) => this.run(r.card, r.state).catch((e) => e instanceof Error ? e : new Error(String(e)))));
|
|
341
445
|
}
|
|
342
446
|
};
|
|
343
447
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
448
|
+
var override;
|
|
449
|
+
function setReportsTenantId(id) {
|
|
450
|
+
override = id;
|
|
451
|
+
}
|
|
347
452
|
function getReportsTenantId() {
|
|
348
|
-
return
|
|
453
|
+
if (override) return override;
|
|
454
|
+
try {
|
|
455
|
+
return useOrganizationStore.getState().currentOrg?.id;
|
|
456
|
+
} catch {
|
|
457
|
+
return void 0;
|
|
458
|
+
}
|
|
349
459
|
}
|
|
350
460
|
|
|
351
461
|
// src/locales/en.ts
|
|
352
462
|
var en = {
|
|
353
463
|
"reports.pageTitle": "Reports",
|
|
354
464
|
"reports.pageSubtitle": "Access complete reports for analysis and decision making",
|
|
355
|
-
"reports.
|
|
465
|
+
"reports.overview": "Overview",
|
|
466
|
+
"reports.allReports": "All reports",
|
|
356
467
|
"reports.dateRange": "Date Range",
|
|
357
468
|
"reports.from": "From",
|
|
358
469
|
"reports.to": "To",
|
|
@@ -374,6 +485,17 @@ var en = {
|
|
|
374
485
|
"reports.generate": "Generate",
|
|
375
486
|
"reports.emptyStateTitle": "Ready to generate",
|
|
376
487
|
"reports.emptyStateDescription": "Select a date range and filters, then click Generate to view the report.",
|
|
488
|
+
// The assistant's invitation on /analytics. It opens on a question, not on a
|
|
489
|
+
// grid: the numbers are already downstairs, and what the owner actually wants
|
|
490
|
+
// is to know what they mean.
|
|
491
|
+
"reports.assistant.persona": "Growth Analyst",
|
|
492
|
+
"reports.assistant.headline": "Hi {{name}}, what would you like to learn today?",
|
|
493
|
+
"reports.assistant.pitch": "Let's find the opportunities hiding in your business",
|
|
494
|
+
"reports.assistant.description": "Tell me your goals and I'll help build a plan to hit them. I read the same data these reports do, and I'll point at what changed and what to do about it.",
|
|
495
|
+
"reports.assistant.placeholder": "Build a custom report...",
|
|
496
|
+
"reports.assistant.suggestion.0": "How is my business doing this month?",
|
|
497
|
+
"reports.assistant.suggestion.1": "What's growing and what's falling?",
|
|
498
|
+
"reports.assistant.suggestion.2": "Where am I losing money?",
|
|
377
499
|
"reports.unavailableTitle": "Coming soon",
|
|
378
500
|
"reports.unavailableDescription": "This report is not available yet. It will be enabled in a future update."
|
|
379
501
|
};
|
|
@@ -382,7 +504,8 @@ var en = {
|
|
|
382
504
|
var ptBR = {
|
|
383
505
|
"reports.pageTitle": "Relat\xF3rios",
|
|
384
506
|
"reports.pageSubtitle": "Acesse relat\xF3rios completos para an\xE1lise e tomada de decis\xE3o",
|
|
385
|
-
"reports.
|
|
507
|
+
"reports.overview": "Vis\xE3o geral",
|
|
508
|
+
"reports.allReports": "Todos os relat\xF3rios",
|
|
386
509
|
"reports.dateRange": "Per\xEDodo",
|
|
387
510
|
"reports.from": "De",
|
|
388
511
|
"reports.to": "At\xE9",
|
|
@@ -404,6 +527,14 @@ var ptBR = {
|
|
|
404
527
|
"reports.generate": "Gerar",
|
|
405
528
|
"reports.emptyStateTitle": "Pronto para gerar",
|
|
406
529
|
"reports.emptyStateDescription": "Selecione o per\xEDodo e os filtros desejados, depois clique em Gerar para visualizar o relat\xF3rio.",
|
|
530
|
+
"reports.assistant.persona": "Analista de Crescimento",
|
|
531
|
+
"reports.assistant.headline": "Oi {{name}}, o que voc\xEA quer entender hoje?",
|
|
532
|
+
"reports.assistant.pitch": "Vamos achar as oportunidades escondidas no seu neg\xF3cio",
|
|
533
|
+
"reports.assistant.description": "Me conte sua meta e eu ajudo a montar o plano para bater. Leio os mesmos dados destes relat\xF3rios e aponto o que mudou e o que fazer a respeito.",
|
|
534
|
+
"reports.assistant.placeholder": "Pe\xE7a um relat\xF3rio do seu jeito...",
|
|
535
|
+
"reports.assistant.suggestion.0": "Como est\xE1 meu neg\xF3cio neste m\xEAs?",
|
|
536
|
+
"reports.assistant.suggestion.1": "O que est\xE1 crescendo e o que est\xE1 caindo?",
|
|
537
|
+
"reports.assistant.suggestion.2": "Onde estou perdendo dinheiro?",
|
|
407
538
|
"reports.unavailableTitle": "Em breve",
|
|
408
539
|
"reports.unavailableDescription": "Este relat\xF3rio ainda n\xE3o est\xE1 dispon\xEDvel. Ser\xE1 habilitado em uma atualiza\xE7\xE3o futura."
|
|
409
540
|
};
|
|
@@ -459,6 +590,7 @@ function reportDefToCard(report, createdBy) {
|
|
|
459
590
|
sortable: c.sortable,
|
|
460
591
|
currency: c.currency,
|
|
461
592
|
idKey: c.idKey,
|
|
593
|
+
personKind: c.personKind,
|
|
462
594
|
renderCell: c.renderCell,
|
|
463
595
|
visible: c.visible,
|
|
464
596
|
align: c.align,
|
|
@@ -493,8 +625,8 @@ function markViewed(cardId) {
|
|
|
493
625
|
for (const listener of listeners) listener(next);
|
|
494
626
|
}
|
|
495
627
|
function useLastViewed() {
|
|
496
|
-
const [map, setMap] =
|
|
497
|
-
|
|
628
|
+
const [map, setMap] = React6.useState(() => read());
|
|
629
|
+
React6.useEffect(() => {
|
|
498
630
|
listeners.add(setMap);
|
|
499
631
|
return () => {
|
|
500
632
|
listeners.delete(setMap);
|
|
@@ -504,16 +636,16 @@ function useLastViewed() {
|
|
|
504
636
|
}
|
|
505
637
|
var ANALYTICS_BASE = "/analytics";
|
|
506
638
|
var REPORTS_BASE = `${ANALYTICS_BASE}/reports`;
|
|
507
|
-
var ConfigContext =
|
|
639
|
+
var ConfigContext = React6__default.createContext(null);
|
|
508
640
|
function useAnalyticsConfig() {
|
|
509
|
-
const ctx =
|
|
641
|
+
const ctx = React6__default.useContext(ConfigContext);
|
|
510
642
|
if (!ctx) throw new Error("useAnalyticsConfig must be used inside <AnalyticsSurface>");
|
|
511
643
|
return ctx;
|
|
512
644
|
}
|
|
513
645
|
function useAnalyticsCards() {
|
|
514
646
|
const runtime = usePluginRuntime();
|
|
515
647
|
const { cards, createdBy } = useAnalyticsConfig();
|
|
516
|
-
return
|
|
648
|
+
return React6__default.useMemo(() => {
|
|
517
649
|
const byId = /* @__PURE__ */ new Map();
|
|
518
650
|
for (const card of getAnalyticsCards(runtime)) byId.set(card.id, card);
|
|
519
651
|
for (const card of cards) byId.set(card.id, card);
|
|
@@ -522,7 +654,7 @@ function useAnalyticsCards() {
|
|
|
522
654
|
}
|
|
523
655
|
function useOverviewCards() {
|
|
524
656
|
const all = useAnalyticsCards();
|
|
525
|
-
return
|
|
657
|
+
return React6__default.useMemo(
|
|
526
658
|
() => all.filter((c) => c.dashboard && c.dashboard.defaultVisible !== false).sort((a, b) => (a.dashboard?.order ?? 0) - (b.dashboard?.order ?? 0)),
|
|
527
659
|
[all]
|
|
528
660
|
);
|
|
@@ -530,10 +662,14 @@ function useOverviewCards() {
|
|
|
530
662
|
function cardColumns(card) {
|
|
531
663
|
if (card.columns?.length) return card.columns;
|
|
532
664
|
return [
|
|
533
|
-
|
|
665
|
+
// A hidden dimension is group-by only — the id a person column reads from,
|
|
666
|
+
// never a column of UUIDs next to the name it belongs to.
|
|
667
|
+
...(card.query.dimensions ?? []).filter((d) => !d.hidden).map((d) => ({
|
|
534
668
|
key: d.key,
|
|
535
669
|
label: d.label,
|
|
536
|
-
type: d.format ?? (d.grain ? "date" : "text"),
|
|
670
|
+
type: d.person ? "person" : d.format ?? (d.grain ? "date" : "text"),
|
|
671
|
+
idKey: d.person?.idKey,
|
|
672
|
+
personKind: d.person?.kind,
|
|
537
673
|
align: "left"
|
|
538
674
|
})),
|
|
539
675
|
...(card.query.measures ?? []).map((m) => ({
|
|
@@ -549,7 +685,7 @@ function navigateAnalytics(path) {
|
|
|
549
685
|
hashRouterAdapter().navigate(path);
|
|
550
686
|
}
|
|
551
687
|
function AnalyticsSurface({ config, provider, children }) {
|
|
552
|
-
const openCard =
|
|
688
|
+
const openCard = React6__default.useCallback((card) => {
|
|
553
689
|
markViewed(card.id);
|
|
554
690
|
navigateAnalytics(`${REPORTS_BASE}/${encodeURIComponent(card.id)}`);
|
|
555
691
|
}, []);
|
|
@@ -567,22 +703,148 @@ function AnalyticsSurface({ config, provider, children }) {
|
|
|
567
703
|
}
|
|
568
704
|
) });
|
|
569
705
|
}
|
|
706
|
+
function AnalyticsAssistant() {
|
|
707
|
+
const { assistant } = useAnalyticsConfig();
|
|
708
|
+
const t = useTranslation();
|
|
709
|
+
const suggestions = React6__default.useMemo(
|
|
710
|
+
() => assistant?.suggestions ?? [0, 1, 2].map((i) => ({ label: t(`reports.assistant.suggestion.${i}`) })),
|
|
711
|
+
[assistant?.suggestions, t]
|
|
712
|
+
);
|
|
713
|
+
if (!assistant) return null;
|
|
714
|
+
return /* @__PURE__ */ jsx(
|
|
715
|
+
AssistantInvite,
|
|
716
|
+
{
|
|
717
|
+
id: "analytics",
|
|
718
|
+
auto: assistant.auto ?? false,
|
|
719
|
+
persona: assistant.persona ?? t("reports.assistant.persona"),
|
|
720
|
+
headline: assistant.headline ?? t("reports.assistant.headline"),
|
|
721
|
+
pitch: assistant.pitch ?? t("reports.assistant.pitch"),
|
|
722
|
+
description: assistant.description ?? t("reports.assistant.description"),
|
|
723
|
+
placeholder: assistant.placeholder ?? t("reports.assistant.placeholder"),
|
|
724
|
+
suggestions
|
|
725
|
+
}
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
var reportRoute = (cardId) => `${REPORTS_BASE}/${encodeURIComponent(cardId)}`;
|
|
729
|
+
function groupCards(cards) {
|
|
730
|
+
const groups = /* @__PURE__ */ new Map();
|
|
731
|
+
for (const card of cards) {
|
|
732
|
+
const list = groups.get(card.category);
|
|
733
|
+
if (list) list.push(card);
|
|
734
|
+
else groups.set(card.category, [card]);
|
|
735
|
+
}
|
|
736
|
+
return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([category, list]) => [category, [...list].sort((a, b) => a.name.localeCompare(b.name))]);
|
|
737
|
+
}
|
|
738
|
+
function useAnalyticsTree() {
|
|
739
|
+
const t = useTranslation();
|
|
740
|
+
const { labels, showLiveView } = useAnalyticsConfig();
|
|
741
|
+
const cards = useAnalyticsCards();
|
|
742
|
+
return React6__default.useMemo(() => ({
|
|
743
|
+
title: labels.analytics,
|
|
744
|
+
// Only the overview. The searchable catalogue (/analytics/reports) is still
|
|
745
|
+
// routed — bookmarks and the legacy /reports link resolve — but it is not
|
|
746
|
+
// listed: the tree below already IS the catalogue, and a link named "all
|
|
747
|
+
// reports" sitting above the reports reads as a different, fuller list.
|
|
748
|
+
fixed: [
|
|
749
|
+
{ id: "overview", label: labels.overview ?? t("reports.overview"), icon: "BarChart3", route: ANALYTICS_BASE, view: "overview" }
|
|
750
|
+
],
|
|
751
|
+
groups: groupCards(cards),
|
|
752
|
+
live: showLiveView ? { id: "live", label: labels.liveView, icon: "Radio", route: `${ANALYTICS_BASE}/live`, view: "live" } : null
|
|
753
|
+
}), [labels, showLiveView, cards, t]);
|
|
754
|
+
}
|
|
755
|
+
function AnalyticsModule({ view, cardId, children }) {
|
|
756
|
+
const variant = useModuleLayout();
|
|
757
|
+
const tree = useAnalyticsTree();
|
|
758
|
+
const activeId = cardId ? decodeURIComponent(cardId) : void 0;
|
|
759
|
+
const shellRail = useShellRailAvailable();
|
|
760
|
+
const claimsRail = shellRail ?? variant !== "rail";
|
|
761
|
+
const entries = React6__default.useMemo(() => [
|
|
762
|
+
...tree.fixed.map((f) => ({
|
|
763
|
+
id: `analytics:${f.id}`,
|
|
764
|
+
label: f.label,
|
|
765
|
+
icon: f.icon,
|
|
766
|
+
route: f.route,
|
|
767
|
+
section: "main"
|
|
768
|
+
})),
|
|
769
|
+
...tree.groups.flatMap(([category, list]) => list.map((card) => ({
|
|
770
|
+
id: `analytics:${card.id}`,
|
|
771
|
+
label: card.name,
|
|
772
|
+
icon: card.icon ?? "FileText",
|
|
773
|
+
route: reportRoute(card.id),
|
|
774
|
+
section: "main",
|
|
775
|
+
// Consecutive items sharing a group render under one heading — the same
|
|
776
|
+
// mechanism that heads the settings rail's plugin runs.
|
|
777
|
+
group: category
|
|
778
|
+
}))),
|
|
779
|
+
...tree.live ? [{
|
|
780
|
+
id: "analytics:live",
|
|
781
|
+
label: tree.live.label,
|
|
782
|
+
icon: tree.live.icon,
|
|
783
|
+
route: tree.live.route,
|
|
784
|
+
section: "main"
|
|
785
|
+
}] : []
|
|
786
|
+
], [tree]);
|
|
787
|
+
useContextualNav(claimsRail ? { base: ANALYTICS_BASE, entries, label: tree.title } : null);
|
|
788
|
+
const nav = React6__default.useMemo(() => {
|
|
789
|
+
if (claimsRail) return [];
|
|
790
|
+
return [
|
|
791
|
+
...tree.fixed.map((f) => ({
|
|
792
|
+
id: f.id,
|
|
793
|
+
label: f.label,
|
|
794
|
+
icon: f.icon,
|
|
795
|
+
active: view === f.view,
|
|
796
|
+
onClick: () => navigateAnalytics(f.route)
|
|
797
|
+
})),
|
|
798
|
+
...tree.groups.map(([category, list]) => ({
|
|
799
|
+
id: `group:${category}`,
|
|
800
|
+
label: category,
|
|
801
|
+
children: list.map((card) => ({
|
|
802
|
+
id: card.id,
|
|
803
|
+
label: card.name,
|
|
804
|
+
active: view === "report" && card.id === activeId,
|
|
805
|
+
onClick: () => navigateAnalytics(reportRoute(card.id))
|
|
806
|
+
}))
|
|
807
|
+
})),
|
|
808
|
+
...tree.live ? [{
|
|
809
|
+
id: tree.live.id,
|
|
810
|
+
label: tree.live.label,
|
|
811
|
+
icon: tree.live.icon,
|
|
812
|
+
active: view === "live",
|
|
813
|
+
onClick: () => navigateAnalytics(tree.live.route)
|
|
814
|
+
}] : []
|
|
815
|
+
];
|
|
816
|
+
}, [tree, view, activeId, claimsRail]);
|
|
817
|
+
return /* @__PURE__ */ jsxs(
|
|
818
|
+
ModulePage,
|
|
819
|
+
{
|
|
820
|
+
title: tree.title,
|
|
821
|
+
nav,
|
|
822
|
+
navVariant: claimsRail ? "sidebar" : "rail",
|
|
823
|
+
showHeader: false,
|
|
824
|
+
viewKey: view === "report" ? `report:${activeId}` : view,
|
|
825
|
+
children: [
|
|
826
|
+
/* @__PURE__ */ jsx(AnalyticsAssistant, {}),
|
|
827
|
+
/* @__PURE__ */ jsx("div", { className: "md:pb-16", children })
|
|
828
|
+
]
|
|
829
|
+
}
|
|
830
|
+
);
|
|
831
|
+
}
|
|
570
832
|
function AnalyticsOverview() {
|
|
571
833
|
const { labels, showHeader } = useAnalyticsConfig();
|
|
572
834
|
const cards = useOverviewCards();
|
|
573
|
-
const filters =
|
|
574
|
-
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
835
|
+
const filters = React6__default.useMemo(() => collectFilters(cards), [cards]);
|
|
836
|
+
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
575
837
|
showHeader ? /* @__PURE__ */ jsxs("div", { children: [
|
|
576
838
|
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: labels.analytics }),
|
|
577
839
|
/* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: labels.overviewSubtitle })
|
|
578
840
|
] }) : null,
|
|
579
|
-
/* @__PURE__ */ jsx(AnalyticsControls, { filters, className: "sticky top-
|
|
841
|
+
/* @__PURE__ */ jsx(AnalyticsControls, { filters, className: "sticky top-3 z-20" }),
|
|
580
842
|
cards.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border border-dashed py-16 text-center", children: [
|
|
581
843
|
/* @__PURE__ */ jsx("div", { className: "mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsx(BarChart3, { className: "h-5 w-5 text-muted-foreground" }) }),
|
|
582
844
|
/* @__PURE__ */ jsx("p", { className: "font-medium", children: labels.emptyOverviewTitle }),
|
|
583
845
|
/* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: labels.emptyOverviewDescription })
|
|
584
846
|
] }) : /* @__PURE__ */ jsx(AnalyticsGrid, { cards })
|
|
585
|
-
] });
|
|
847
|
+
] }) });
|
|
586
848
|
}
|
|
587
849
|
function formatViewed(iso, locale, never) {
|
|
588
850
|
if (!iso) return never;
|
|
@@ -594,18 +856,18 @@ function ReportsListPage() {
|
|
|
594
856
|
const { labels, currency, showHeader } = useAnalyticsConfig();
|
|
595
857
|
const cards = useAnalyticsCards();
|
|
596
858
|
const lastViewed = useLastViewed();
|
|
597
|
-
const [search, setSearch] =
|
|
598
|
-
const [category, setCategory] =
|
|
599
|
-
const [author, setAuthor] =
|
|
600
|
-
const categoryOptions =
|
|
859
|
+
const [search, setSearch] = React6__default.useState("");
|
|
860
|
+
const [category, setCategory] = React6__default.useState();
|
|
861
|
+
const [author, setAuthor] = React6__default.useState();
|
|
862
|
+
const categoryOptions = React6__default.useMemo(
|
|
601
863
|
() => [...new Set(cards.map((c) => c.category))].sort((a, b) => a.localeCompare(b)).map((c) => ({ value: c, label: c })),
|
|
602
864
|
[cards]
|
|
603
865
|
);
|
|
604
|
-
const authorOptions =
|
|
866
|
+
const authorOptions = React6__default.useMemo(
|
|
605
867
|
() => [...new Set(cards.map((c) => c.createdBy ?? "\u2014"))].sort((a, b) => a.localeCompare(b)).map((a) => ({ value: a, label: a })),
|
|
606
868
|
[cards]
|
|
607
869
|
);
|
|
608
|
-
const rows =
|
|
870
|
+
const rows = React6__default.useMemo(() => {
|
|
609
871
|
const q = search.trim().toLowerCase();
|
|
610
872
|
return cards.filter((card) => {
|
|
611
873
|
if (category && card.category !== category) return false;
|
|
@@ -614,7 +876,7 @@ function ReportsListPage() {
|
|
|
614
876
|
return card.name.toLowerCase().includes(q) || card.description?.toLowerCase().includes(q) || card.category.toLowerCase().includes(q);
|
|
615
877
|
});
|
|
616
878
|
}, [cards, search, category, author]);
|
|
617
|
-
const columns =
|
|
879
|
+
const columns = React6__default.useMemo(() => [
|
|
618
880
|
{
|
|
619
881
|
id: "name",
|
|
620
882
|
accessorFn: (card) => card.name,
|
|
@@ -730,33 +992,33 @@ function exportToCsv(columns, data, filename, currency) {
|
|
|
730
992
|
exportCSV(csvColumns, data, filename);
|
|
731
993
|
}
|
|
732
994
|
function ExportDropdown({ columns, data, filename, currency, title }) {
|
|
733
|
-
const
|
|
995
|
+
const t = useTranslation();
|
|
734
996
|
const [excelAvailable, setExcelAvailable] = useState(false);
|
|
735
997
|
const [pdfAvailable, setPdfAvailable] = useState(false);
|
|
736
998
|
useEffect(() => {
|
|
737
|
-
import('./excel-
|
|
999
|
+
import('./excel-KIJTC7OJ.js').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {
|
|
738
1000
|
});
|
|
739
|
-
import('./pdf-
|
|
1001
|
+
import('./pdf-7QQ3U5JQ.js').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {
|
|
740
1002
|
});
|
|
741
1003
|
}, []);
|
|
742
1004
|
const handleCsv = () => exportToCsv(columns, data, filename, currency);
|
|
743
1005
|
const handleExcel = async () => {
|
|
744
|
-
const { exportToExcel } = await import('./excel-
|
|
1006
|
+
const { exportToExcel } = await import('./excel-KIJTC7OJ.js');
|
|
745
1007
|
await exportToExcel(columns, data, filename, currency);
|
|
746
1008
|
};
|
|
747
1009
|
const handlePdf = async () => {
|
|
748
|
-
const { exportToPdf } = await import('./pdf-
|
|
1010
|
+
const { exportToPdf } = await import('./pdf-7QQ3U5JQ.js');
|
|
749
1011
|
await exportToPdf(columns, data, filename, { title, currency });
|
|
750
1012
|
};
|
|
751
1013
|
return /* @__PURE__ */ jsxs(Dropdown, { children: [
|
|
752
1014
|
/* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", children: [
|
|
753
1015
|
/* @__PURE__ */ jsx(Download, { className: "h-4 w-4 mr-1.5" }),
|
|
754
|
-
|
|
1016
|
+
t("reports.export")
|
|
755
1017
|
] }) }),
|
|
756
1018
|
/* @__PURE__ */ jsxs(DropdownContent, { align: "end", children: [
|
|
757
|
-
/* @__PURE__ */ jsx(DropdownItem, { onClick: handleCsv, children:
|
|
758
|
-
excelAvailable && /* @__PURE__ */ jsx(DropdownItem, { onClick: handleExcel, children:
|
|
759
|
-
pdfAvailable && /* @__PURE__ */ jsx(DropdownItem, { onClick: handlePdf, children:
|
|
1019
|
+
/* @__PURE__ */ jsx(DropdownItem, { onClick: handleCsv, children: t("reports.exportCsv") }),
|
|
1020
|
+
excelAvailable && /* @__PURE__ */ jsx(DropdownItem, { onClick: handleExcel, children: t("reports.exportExcel") }),
|
|
1021
|
+
pdfAvailable && /* @__PURE__ */ jsx(DropdownItem, { onClick: handlePdf, children: t("reports.exportPdf") })
|
|
760
1022
|
] })
|
|
761
1023
|
] });
|
|
762
1024
|
}
|
|
@@ -813,8 +1075,8 @@ function ViewerBody({ card }) {
|
|
|
813
1075
|
const { labels, currency } = useAnalyticsConfig();
|
|
814
1076
|
const { state, setPage, setPageSize } = useAnalyticsContext();
|
|
815
1077
|
const { result, loading, error, engineMissing } = useAnalyticsCard(card);
|
|
816
|
-
const columns =
|
|
817
|
-
const tanstackColumns =
|
|
1078
|
+
const columns = React6__default.useMemo(() => cardColumns(card), [card]);
|
|
1079
|
+
const tanstackColumns = React6__default.useMemo(
|
|
818
1080
|
() => reportColumnsToTanstack(columns, currency.code),
|
|
819
1081
|
[columns, currency.code]
|
|
820
1082
|
);
|
|
@@ -871,7 +1133,7 @@ function AnalyticsReportViewer({ cardId }) {
|
|
|
871
1133
|
const { labels } = useAnalyticsConfig();
|
|
872
1134
|
const cards = useAnalyticsCards();
|
|
873
1135
|
const card = cards.find((c) => c.id === decodeURIComponent(cardId ?? ""));
|
|
874
|
-
|
|
1136
|
+
React6__default.useEffect(() => {
|
|
875
1137
|
if (card) markViewed(card.id);
|
|
876
1138
|
}, [card?.id]);
|
|
877
1139
|
if (!card) {
|
|
@@ -888,8 +1150,8 @@ function AnalyticsReportViewer({ cardId }) {
|
|
|
888
1150
|
SubpageHeader,
|
|
889
1151
|
{
|
|
890
1152
|
title: card.name,
|
|
891
|
-
parentLabel: labels.
|
|
892
|
-
onBack: () => navigateAnalytics(
|
|
1153
|
+
parentLabel: labels.analytics,
|
|
1154
|
+
onBack: () => navigateAnalytics(ANALYTICS_BASE)
|
|
893
1155
|
}
|
|
894
1156
|
),
|
|
895
1157
|
card.description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: card.description }) : null,
|
|
@@ -908,7 +1170,7 @@ function AnalyticsReportViewer({ cardId }) {
|
|
|
908
1170
|
function ViewerActions({ card }) {
|
|
909
1171
|
const { currency } = useAnalyticsConfig();
|
|
910
1172
|
const { result } = useAnalyticsCard(card);
|
|
911
|
-
const columns =
|
|
1173
|
+
const columns = React6__default.useMemo(() => cardColumns(card), [card]);
|
|
912
1174
|
if (!result?.rows?.length) return null;
|
|
913
1175
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
914
1176
|
/* @__PURE__ */ jsx(
|
|
@@ -996,7 +1258,7 @@ async function fetchViaView(reportDef, query, getClient, resolveTenantId, tenant
|
|
|
996
1258
|
}
|
|
997
1259
|
}
|
|
998
1260
|
if (query.search) {
|
|
999
|
-
const searchableCols = reportDef.columns.filter((c) => c.type === "text" || c.type === "email").map((c) => camelToSnake2(c.key));
|
|
1261
|
+
const searchableCols = reportDef.columns.filter((c) => c.type === "text" || c.type === "email" || c.type === "person").map((c) => camelToSnake2(c.key));
|
|
1000
1262
|
if (searchableCols.length > 0) {
|
|
1001
1263
|
const term = `%${query.search}%`;
|
|
1002
1264
|
const orClause = searchableCols.map((col) => `${col}.ilike.${term}`).join(",");
|
|
@@ -1099,7 +1361,9 @@ function resolveConfig(options) {
|
|
|
1099
1361
|
defaultPageSize: options.defaultPageSize ?? 50,
|
|
1100
1362
|
showHeader: options.showHeader === true,
|
|
1101
1363
|
defaultRange: options.defaultRange ?? "last_30_days",
|
|
1102
|
-
defaultCompare: options.defaultCompare ?? "previous_period"
|
|
1364
|
+
defaultCompare: options.defaultCompare ?? "previous_period",
|
|
1365
|
+
showLiveView: options.showLiveView === true,
|
|
1366
|
+
assistant: options.assistant === false ? null : options.assistant ?? {}
|
|
1103
1367
|
};
|
|
1104
1368
|
}
|
|
1105
1369
|
function createAnalyticsPlugin(options = {}) {
|
|
@@ -1109,10 +1373,14 @@ function createAnalyticsPlugin(options = {}) {
|
|
|
1109
1373
|
() => createSupabaseAnalyticsProvider({ tenantId: () => getReportsTenantId() }),
|
|
1110
1374
|
() => createMockAnalyticsProvider()
|
|
1111
1375
|
);
|
|
1112
|
-
const page2 = (Body) => (props) =>
|
|
1376
|
+
const page2 = (Body, view) => (props) => React6__default.createElement(
|
|
1113
1377
|
AnalyticsSurface,
|
|
1114
1378
|
{ config, provider },
|
|
1115
|
-
|
|
1379
|
+
React6__default.createElement(
|
|
1380
|
+
AnalyticsModule,
|
|
1381
|
+
{ view, cardId: props?.cardId },
|
|
1382
|
+
React6__default.createElement(Body, props)
|
|
1383
|
+
)
|
|
1116
1384
|
);
|
|
1117
1385
|
const READ = { feature: "reports", action: "read" };
|
|
1118
1386
|
const position = options.navPosition ?? 9;
|
|
@@ -1125,23 +1393,7 @@ function createAnalyticsPlugin(options = {}) {
|
|
|
1125
1393
|
route: ANALYTICS_BASE,
|
|
1126
1394
|
icon: "BarChart3",
|
|
1127
1395
|
permission: READ
|
|
1128
|
-
}
|
|
1129
|
-
{
|
|
1130
|
-
section,
|
|
1131
|
-
position: position + 0.1,
|
|
1132
|
-
label: config.labels.reports,
|
|
1133
|
-
route: REPORTS_BASE,
|
|
1134
|
-
icon: "FileText",
|
|
1135
|
-
permission: READ
|
|
1136
|
-
},
|
|
1137
|
-
...options.showLiveView ? [{
|
|
1138
|
-
section,
|
|
1139
|
-
position: position + 0.2,
|
|
1140
|
-
label: config.labels.liveView,
|
|
1141
|
-
route: `${ANALYTICS_BASE}/live`,
|
|
1142
|
-
icon: "Radio",
|
|
1143
|
-
permission: READ
|
|
1144
|
-
}] : []
|
|
1396
|
+
}
|
|
1145
1397
|
];
|
|
1146
1398
|
return {
|
|
1147
1399
|
id: "reports",
|
|
@@ -1157,8 +1409,8 @@ function createAnalyticsPlugin(options = {}) {
|
|
|
1157
1409
|
],
|
|
1158
1410
|
navigation: nav,
|
|
1159
1411
|
routes: [
|
|
1160
|
-
{ path: ANALYTICS_BASE, component: page2(AnalyticsOverview), permission: READ },
|
|
1161
|
-
{ path: REPORTS_BASE, component: page2(ReportsListPage), permission: READ },
|
|
1412
|
+
{ path: ANALYTICS_BASE, component: page2(AnalyticsOverview, "overview"), permission: READ },
|
|
1413
|
+
{ path: REPORTS_BASE, component: page2(ReportsListPage, "reports"), permission: READ },
|
|
1162
1414
|
// Registered before the list would ever shadow it: matchRoute scores
|
|
1163
1415
|
// static segments above params, so /analytics/reports still wins its own
|
|
1164
1416
|
// exact path and only /analytics/reports/<id> lands here.
|
|
@@ -1166,14 +1418,17 @@ function createAnalyticsPlugin(options = {}) {
|
|
|
1166
1418
|
// (the card's name), so it claims the shell bar itself via PageHeader.
|
|
1167
1419
|
// Declaring both gave the page two stacked back-crumbs saying the same
|
|
1168
1420
|
// thing, one of them generic.
|
|
1169
|
-
{ path: `${REPORTS_BASE}/:cardId`, component: page2(AnalyticsReportViewer), permission: READ },
|
|
1170
|
-
{ path: `${ANALYTICS_BASE}/live`, component: page2(LiveViewPage), permission: READ },
|
|
1421
|
+
{ path: `${REPORTS_BASE}/:cardId`, component: page2(AnalyticsReportViewer, "report"), permission: READ },
|
|
1422
|
+
{ path: `${ANALYTICS_BASE}/live`, component: page2(LiveViewPage, "live"), permission: READ },
|
|
1171
1423
|
// The old top-level route kept as a redirect target: bookmarks, saved
|
|
1172
1424
|
// links and the five apps still pointing at /reports must not 404.
|
|
1173
|
-
{ path: "/reports", component: page2(ReportsListPage), permission: READ }
|
|
1425
|
+
{ path: "/reports", component: page2(ReportsListPage, "reports"), permission: READ }
|
|
1174
1426
|
],
|
|
1175
1427
|
widgets: [],
|
|
1176
1428
|
registries: [],
|
|
1429
|
+
// Measurement, not acquisition — what the Fayz plugin page lists for
|
|
1430
|
+
// Analytics.
|
|
1431
|
+
integrations: ["google-analytics", "hotjar"],
|
|
1177
1432
|
locales: reportsLocales
|
|
1178
1433
|
};
|
|
1179
1434
|
}
|
|
@@ -1194,6 +1449,6 @@ function createReportsPlugin(options) {
|
|
|
1194
1449
|
});
|
|
1195
1450
|
}
|
|
1196
1451
|
|
|
1197
|
-
export { ANALYTICS_BASE, AnalyticsOverview, AnalyticsReportViewer, AnalyticsSurface, REPORTS_BASE, ReportsListPage, aggregateRows, cardColumns, createAnalyticsPlugin, createMockAnalyticsProvider, createMockReportProvider, createReportsPlugin, createSupabaseAnalyticsProvider, createSupabaseReportProvider, markViewed, reportDefToCard, reportDefsToCards, useAnalyticsCards, useAnalyticsConfig, useLastViewed, useOverviewCards };
|
|
1452
|
+
export { ANALYTICS_BASE, AnalyticsModule, AnalyticsOverview, AnalyticsReportViewer, AnalyticsSurface, REPORTS_BASE, ReportsListPage, aggregateRows, cardColumns, createAnalyticsPlugin, createMockAnalyticsProvider, createMockReportProvider, createReportsPlugin, createSupabaseAnalyticsProvider, createSupabaseReportProvider, getReportsTenantId, markViewed, reportDefToCard, reportDefsToCards, setReportsTenantId, useAnalyticsCards, useAnalyticsConfig, useLastViewed, useOverviewCards };
|
|
1198
1453
|
//# sourceMappingURL=index.js.map
|
|
1199
1454
|
//# sourceMappingURL=index.js.map
|