@fayz-ai/plugin-reports 0.10.0 → 0.11.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.
@@ -61,6 +61,7 @@ function reportColumnsToTanstack(columns, currency) {
61
61
  content = React.createElement(PersonLink, {
62
62
  personId: col.idKey ? row[col.idKey] : null,
63
63
  name: String(value),
64
+ kind: col.personKind,
64
65
  size: "sm",
65
66
  className: "text-xs"
66
67
  });
@@ -96,5 +97,5 @@ function reportColumnsToTanstack(columns, currency) {
96
97
  }
97
98
 
98
99
  export { formatValuePlain, reportColumnsToTanstack };
99
- //# sourceMappingURL=chunk-2ZFN6ECM.js.map
100
- //# sourceMappingURL=chunk-2ZFN6ECM.js.map
100
+ //# sourceMappingURL=chunk-4SPBNVQA.js.map
101
+ //# sourceMappingURL=chunk-4SPBNVQA.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/columns.ts"],"names":["formatCurrencyLocale","formatDateLocale","formatDateTimeLocale"],"mappings":"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","file":"chunk-4SPBNVQA.js","sourcesContent":["import React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport type { ReportColumnDef } from '../types'\nimport { PersonLink } from '@fayz-ai/admin'\nimport {\n formatCurrency as formatCurrencyLocale,\n formatDate as formatDateLocale,\n formatDateTime as formatDateTimeLocale,\n} from '@fayz-ai/core'\n\n// ---------------------------------------------------------------------------\n// Formatters (mirrors fieldToColumn.tsx logic)\n// ---------------------------------------------------------------------------\n\nfunction formatCurrency(value: any, currency?: string): string {\n const num = typeof value === 'number' ? value : parseFloat(value)\n if (isNaN(num)) return '—'\n return formatCurrencyLocale(num, currency)\n}\n\nfunction formatDate(value: any): string {\n if (!value) return '—'\n return formatDateLocale(value)\n}\n\nfunction formatDateTime(value: any): string {\n if (!value) return '—'\n return formatDateTimeLocale(value)\n}\n\nfunction formatNumber(value: any): string {\n if (value == null) return '—'\n return typeof value === 'number' ? value.toLocaleString() : String(value)\n}\n\n// ---------------------------------------------------------------------------\n// Plain-text formatter for exports\n// ---------------------------------------------------------------------------\n\nexport function formatValuePlain(value: any, col: ReportColumnDef, currency?: string): string {\n if (value == null || value === '') return ''\n switch (col.type) {\n case 'currency':\n return formatCurrency(value, col.currency ?? currency)\n case 'date':\n return formatDate(value)\n case 'datetime':\n return formatDateTime(value)\n case 'number':\n return formatNumber(value)\n case 'boolean':\n return value ? 'Yes' : 'No'\n default:\n return String(value)\n }\n}\n\n// ---------------------------------------------------------------------------\n// ReportColumnDef[] → TanStack ColumnDef[]\n// ---------------------------------------------------------------------------\n\nexport function reportColumnsToTanstack(\n columns: ReportColumnDef[],\n currency?: string,\n): ColumnDef<Record<string, any>>[] {\n return columns\n .filter((c) => c.visible !== false)\n .map((col): ColumnDef<Record<string, any>> => {\n const sortable = col.sortable ?? ['text', 'number', 'currency', 'date', 'datetime', 'email'].includes(col.type)\n const alignRight = col.align === 'right' || (!col.align && (col.type === 'currency' || col.type === 'number'))\n const alignCenter = col.align === 'center'\n\n return {\n accessorKey: col.key,\n header: col.label,\n enableSorting: sortable,\n cell: (ctx) => {\n const value = ctx.getValue()\n const row = ctx.row.original\n\n if (col.renderCell) {\n return col.renderCell(value, row)\n }\n\n if (value == null || value === '') {\n return React.createElement('span', { className: 'text-muted-foreground' }, '—')\n }\n\n let content: React.ReactNode\n switch (col.type) {\n case 'person':\n content = React.createElement(PersonLink, {\n personId: col.idKey ? row[col.idKey] : null,\n name: String(value),\n kind: col.personKind,\n size: 'sm',\n className: 'text-xs',\n })\n break\n case 'currency':\n content = formatCurrency(value, col.currency ?? currency)\n break\n case 'date':\n content = formatDate(value)\n break\n case 'datetime':\n content = formatDateTime(value)\n break\n case 'boolean':\n content = value\n ? React.createElement('svg', { className: 'h-4 w-4 text-success', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2' },\n React.createElement('polyline', { points: '20 6 9 17 4 12' }),\n )\n : React.createElement('span', { className: 'text-muted-foreground' }, '—')\n break\n case 'number':\n content = formatNumber(value)\n break\n default:\n content = String(value)\n }\n\n const cls = alignRight ? 'text-right' : alignCenter ? 'text-center' : undefined\n return cls\n ? React.createElement('div', { className: cls }, content)\n : React.createElement(React.Fragment, null, content)\n },\n meta: { align: alignRight ? 'right' : alignCenter ? 'center' : 'left' },\n }\n })\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"analytics-supabase.d.ts","sourceRoot":"","sources":["../../src/data/analytics-supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAC+B,iBAAiB,EAE3D,MAAM,eAAe,CAAA;AAsLtB,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAA;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,+BAA+B,GAAG,iBAAiB,CAkJ1G"}
1
+ {"version":3,"file":"analytics-supabase.d.ts","sourceRoot":"","sources":["../../src/data/analytics-supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAC+B,iBAAiB,EAE3D,MAAM,eAAe,CAAA;AA6MtB,MAAM,WAAW,+BAA+B;IAC9C,QAAQ,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAA;IAC7C,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,+BAA+B,GAAG,iBAAiB,CAkJ1G"}
@@ -1,4 +1,4 @@
1
- import { formatValuePlain } from './chunk-2ZFN6ECM.js';
1
+ import { formatValuePlain } from './chunk-4SPBNVQA.js';
2
2
 
3
3
  // src/export/excel.ts
4
4
  var load = (m) => new Function("m", "return import(m)")(m);
@@ -31,5 +31,5 @@ async function isExcelAvailable() {
31
31
  }
32
32
 
33
33
  export { exportToExcel, isExcelAvailable };
34
- //# sourceMappingURL=excel-WE73YE2V.js.map
35
- //# sourceMappingURL=excel-WE73YE2V.js.map
34
+ //# sourceMappingURL=excel-KIJTC7OJ.js.map
35
+ //# sourceMappingURL=excel-KIJTC7OJ.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/export/excel.ts"],"names":[],"mappings":";;;AAKA,IAAM,IAAA,GAAO,CAAC,CAAA,KAAc,IAAI,SAAS,GAAA,EAAK,kBAAkB,EAAE,CAAC,CAAA;AAEnE,eAAsB,aAAA,CACpB,OAAA,EACA,IAAA,EACA,QAAA,EACA,QAAA,EACe;AACf,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAM,CAAA;AAC9B,EAAA,MAAM,cAAc,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,YAAY,KAAK,CAAA;AAE7D,EAAA,MAAM,SAAS,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,KAAK,CAAA;AAC7C,EAAA,MAAM,OAAO,IAAA,CAAK,GAAA;AAAA,IAAI,CAAC,GAAA,KACrB,WAAA,CAAY,GAAA,CAAI,CAAC,GAAA,KAAQ;AACvB,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AACzB,MAAA,IAAI,GAAA,CAAI,IAAA,KAAS,QAAA,IAAY,GAAA,CAAI,SAAS,UAAA,EAAY;AACpD,QAAA,MAAM,MAAM,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,WAAW,KAAK,CAAA;AAChE,QAAA,OAAO,KAAA,CAAM,GAAG,CAAA,GAAI,EAAA,GAAK,GAAA;AAAA,MAC3B;AACA,MAAA,OAAO,gBAAA,CAAiB,KAAA,EAAO,GAAA,EAAK,QAAQ,CAAA;AAAA,IAC9C,CAAC;AAAA,GACH;AAEA,EAAA,MAAM,EAAA,GAAK,KAAK,KAAA,CAAM,YAAA,CAAa,CAAC,MAAA,EAAQ,GAAG,IAAI,CAAC,CAAA;AACpD,EAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,QAAA,EAAS;AAC/B,EAAA,IAAA,CAAK,KAAA,CAAM,iBAAA,CAAkB,EAAA,EAAI,EAAA,EAAI,QAAQ,CAAA;AAC7C,EAAA,IAAA,CAAK,SAAA,CAAU,EAAA,EAAI,CAAA,EAAG,QAAQ,CAAA,KAAA,CAAO,CAAA;AACvC;AAEA,eAAsB,gBAAA,GAAqC;AACzD,EAAA,IAAI;AACF,IAAA,MAAM,KAAK,MAAM,CAAA;AACjB,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF","file":"excel-WE73YE2V.js","sourcesContent":["import type { ReportColumnDef } from '../types'\nimport { formatValuePlain } from '../lib/columns'\n\n// Opaque dynamic import — hidden from Vite's static analysis so it won't\n// fail when xlsx is not installed (it's an optional peer dependency).\nconst load = (m: string) => new Function('m', 'return import(m)')(m) as Promise<any>\n\nexport async function exportToExcel(\n columns: ReportColumnDef[],\n data: Record<string, any>[],\n filename: string,\n currency?: string,\n): Promise<void> {\n const XLSX = await load('xlsx')\n const visibleCols = columns.filter((c) => c.visible !== false)\n\n const header = visibleCols.map((c) => c.label)\n const rows = data.map((row) =>\n visibleCols.map((col) => {\n const value = row[col.key]\n if (col.type === 'number' || col.type === 'currency') {\n const num = typeof value === 'number' ? value : parseFloat(value)\n return isNaN(num) ? '' : num\n }\n return formatValuePlain(value, col, currency)\n }),\n )\n\n const ws = XLSX.utils.aoa_to_sheet([header, ...rows])\n const wb = XLSX.utils.book_new()\n XLSX.utils.book_append_sheet(wb, ws, 'Report')\n XLSX.writeFile(wb, `${filename}.xlsx`)\n}\n\nexport async function isExcelAvailable(): Promise<boolean> {\n try {\n await load('xlsx')\n return true\n } catch {\n return false\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/export/excel.ts"],"names":[],"mappings":";;;AAKA,IAAM,IAAA,GAAO,CAAC,CAAA,KAAc,IAAI,SAAS,GAAA,EAAK,kBAAkB,EAAE,CAAC,CAAA;AAEnE,eAAsB,aAAA,CACpB,OAAA,EACA,IAAA,EACA,QAAA,EACA,QAAA,EACe;AACf,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAM,CAAA;AAC9B,EAAA,MAAM,cAAc,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,YAAY,KAAK,CAAA;AAE7D,EAAA,MAAM,SAAS,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,KAAK,CAAA;AAC7C,EAAA,MAAM,OAAO,IAAA,CAAK,GAAA;AAAA,IAAI,CAAC,GAAA,KACrB,WAAA,CAAY,GAAA,CAAI,CAAC,GAAA,KAAQ;AACvB,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AACzB,MAAA,IAAI,GAAA,CAAI,IAAA,KAAS,QAAA,IAAY,GAAA,CAAI,SAAS,UAAA,EAAY;AACpD,QAAA,MAAM,MAAM,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,WAAW,KAAK,CAAA;AAChE,QAAA,OAAO,KAAA,CAAM,GAAG,CAAA,GAAI,EAAA,GAAK,GAAA;AAAA,MAC3B;AACA,MAAA,OAAO,gBAAA,CAAiB,KAAA,EAAO,GAAA,EAAK,QAAQ,CAAA;AAAA,IAC9C,CAAC;AAAA,GACH;AAEA,EAAA,MAAM,EAAA,GAAK,KAAK,KAAA,CAAM,YAAA,CAAa,CAAC,MAAA,EAAQ,GAAG,IAAI,CAAC,CAAA;AACpD,EAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,QAAA,EAAS;AAC/B,EAAA,IAAA,CAAK,KAAA,CAAM,iBAAA,CAAkB,EAAA,EAAI,EAAA,EAAI,QAAQ,CAAA;AAC7C,EAAA,IAAA,CAAK,SAAA,CAAU,EAAA,EAAI,CAAA,EAAG,QAAQ,CAAA,KAAA,CAAO,CAAA;AACvC;AAEA,eAAsB,gBAAA,GAAqC;AACzD,EAAA,IAAI;AACF,IAAA,MAAM,KAAK,MAAM,CAAA;AACjB,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF","file":"excel-KIJTC7OJ.js","sourcesContent":["import type { ReportColumnDef } from '../types'\nimport { formatValuePlain } from '../lib/columns'\n\n// Opaque dynamic import — hidden from Vite's static analysis so it won't\n// fail when xlsx is not installed (it's an optional peer dependency).\nconst load = (m: string) => new Function('m', 'return import(m)')(m) as Promise<any>\n\nexport async function exportToExcel(\n columns: ReportColumnDef[],\n data: Record<string, any>[],\n filename: string,\n currency?: string,\n): Promise<void> {\n const XLSX = await load('xlsx')\n const visibleCols = columns.filter((c) => c.visible !== false)\n\n const header = visibleCols.map((c) => c.label)\n const rows = data.map((row) =>\n visibleCols.map((col) => {\n const value = row[col.key]\n if (col.type === 'number' || col.type === 'currency') {\n const num = typeof value === 'number' ? value : parseFloat(value)\n return isNaN(num) ? '' : num\n }\n return formatValuePlain(value, col, currency)\n }),\n )\n\n const ws = XLSX.utils.aoa_to_sheet([header, ...rows])\n const wb = XLSX.utils.book_new()\n XLSX.utils.book_append_sheet(wb, ws, 'Report')\n XLSX.writeFile(wb, `${filename}.xlsx`)\n}\n\nexport async function isExcelAvailable(): Promise<boolean> {\n try {\n await load('xlsx')\n return true\n } catch {\n return false\n }\n}\n"]}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { reportColumnsToTanstack, formatValuePlain } from './chunk-2ZFN6ECM.js';
2
- import * as React5 from 'react';
3
- import React5__default, { useState, useEffect } from 'react';
4
- import { usePluginRuntime, getAnalyticsCards, useTranslation, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, hashRouterAdapter, exportCSV } from '@fayz-ai/core';
1
+ import { reportColumnsToTanstack, formatValuePlain } from './chunk-4SPBNVQA.js';
2
+ import * as React6 from 'react';
3
+ import React6__default, { useState, useEffect } from 'react';
4
+ import { usePluginRuntime, getAnalyticsCards, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, hashRouterAdapter, useTranslation, exportCSV } from '@fayz-ai/core';
5
5
  import { useOrganizationStore, useShellRailAvailable, useContextualNav, AssistantInvite } from '@fayz-ai/admin';
6
6
  import { AnalyticsContextProvider, useModuleLayout, ModulePage, collectFilters, AnalyticsControls, AnalyticsGrid, Card, FilterChip, DataTable, Button, SubpageHeader, useAnalyticsCard, useAnalyticsContext, AnalyticsCard, Dropdown, DropdownTrigger, DropdownContent, DropdownItem } from '@fayz-ai/ui';
7
7
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
@@ -164,7 +164,7 @@ function reservedKeys(card) {
164
164
  ]);
165
165
  }
166
166
  function searchColumns(card) {
167
- 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);
168
168
  return cols.length ? cols : void 0;
169
169
  }
170
170
  function camelToSnake(s) {
@@ -205,6 +205,19 @@ async function selectFromView(card, state, db, tenantId, reserved) {
205
205
  total: count ?? 0
206
206
  };
207
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
+ }
208
221
  function isAggregateCard(card) {
209
222
  const { source } = card.query;
210
223
  if (source.kind !== "view" || !source.name) return false;
@@ -327,7 +340,7 @@ function createSupabaseAnalyticsProvider(config) {
327
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) => {
328
341
  out[i] = r;
329
342
  }).catch((e) => {
330
- out[i] = e instanceof Error ? e : new Error(String(e));
343
+ out[i] = toError(e);
331
344
  }));
332
345
  if (batchable.length > 0) {
333
346
  const tenantId = resolveTenantId();
@@ -340,7 +353,7 @@ function createSupabaseAnalyticsProvider(config) {
340
353
  ...batchable.map((i) => this.run(requests[i].card, requests[i].state).then((r) => {
341
354
  out[i] = r;
342
355
  }).catch((e) => {
343
- out[i] = e instanceof Error ? e : new Error(String(e));
356
+ out[i] = toError(e);
344
357
  }))
345
358
  ]);
346
359
  return out;
@@ -350,7 +363,7 @@ function createSupabaseAnalyticsProvider(config) {
350
363
  batchable.forEach((cardIndex, slot) => {
351
364
  const entry = results[slot];
352
365
  const { card } = requests[cardIndex];
353
- out[cardIndex] = entry?.error ? Object.assign(new Error(String(entry.error)), { code: entry.code }) : toResult(entry, card, reservedKeys(card));
366
+ out[cardIndex] = entry?.error ? Object.assign(toError(entry.error), { code: entry.code }) : toResult(entry, card, reservedKeys(card));
354
367
  });
355
368
  }
356
369
  await Promise.all(singles);
@@ -577,6 +590,7 @@ function reportDefToCard(report, createdBy) {
577
590
  sortable: c.sortable,
578
591
  currency: c.currency,
579
592
  idKey: c.idKey,
593
+ personKind: c.personKind,
580
594
  renderCell: c.renderCell,
581
595
  visible: c.visible,
582
596
  align: c.align,
@@ -611,8 +625,8 @@ function markViewed(cardId) {
611
625
  for (const listener of listeners) listener(next);
612
626
  }
613
627
  function useLastViewed() {
614
- const [map, setMap] = React5.useState(() => read());
615
- React5.useEffect(() => {
628
+ const [map, setMap] = React6.useState(() => read());
629
+ React6.useEffect(() => {
616
630
  listeners.add(setMap);
617
631
  return () => {
618
632
  listeners.delete(setMap);
@@ -622,16 +636,16 @@ function useLastViewed() {
622
636
  }
623
637
  var ANALYTICS_BASE = "/analytics";
624
638
  var REPORTS_BASE = `${ANALYTICS_BASE}/reports`;
625
- var ConfigContext = React5__default.createContext(null);
639
+ var ConfigContext = React6__default.createContext(null);
626
640
  function useAnalyticsConfig() {
627
- const ctx = React5__default.useContext(ConfigContext);
641
+ const ctx = React6__default.useContext(ConfigContext);
628
642
  if (!ctx) throw new Error("useAnalyticsConfig must be used inside <AnalyticsSurface>");
629
643
  return ctx;
630
644
  }
631
645
  function useAnalyticsCards() {
632
646
  const runtime = usePluginRuntime();
633
647
  const { cards, createdBy } = useAnalyticsConfig();
634
- return React5__default.useMemo(() => {
648
+ return React6__default.useMemo(() => {
635
649
  const byId = /* @__PURE__ */ new Map();
636
650
  for (const card of getAnalyticsCards(runtime)) byId.set(card.id, card);
637
651
  for (const card of cards) byId.set(card.id, card);
@@ -640,7 +654,7 @@ function useAnalyticsCards() {
640
654
  }
641
655
  function useOverviewCards() {
642
656
  const all = useAnalyticsCards();
643
- return React5__default.useMemo(
657
+ return React6__default.useMemo(
644
658
  () => all.filter((c) => c.dashboard && c.dashboard.defaultVisible !== false).sort((a, b) => (a.dashboard?.order ?? 0) - (b.dashboard?.order ?? 0)),
645
659
  [all]
646
660
  );
@@ -648,10 +662,14 @@ function useOverviewCards() {
648
662
  function cardColumns(card) {
649
663
  if (card.columns?.length) return card.columns;
650
664
  return [
651
- ...(card.query.dimensions ?? []).map((d) => ({
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) => ({
652
668
  key: d.key,
653
669
  label: d.label,
654
- 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,
655
673
  align: "left"
656
674
  })),
657
675
  ...(card.query.measures ?? []).map((m) => ({
@@ -667,7 +685,7 @@ function navigateAnalytics(path) {
667
685
  hashRouterAdapter().navigate(path);
668
686
  }
669
687
  function AnalyticsSurface({ config, provider, children }) {
670
- const openCard = React5__default.useCallback((card) => {
688
+ const openCard = React6__default.useCallback((card) => {
671
689
  markViewed(card.id);
672
690
  navigateAnalytics(`${REPORTS_BASE}/${encodeURIComponent(card.id)}`);
673
691
  }, []);
@@ -685,6 +703,28 @@ function AnalyticsSurface({ config, provider, children }) {
685
703
  }
686
704
  ) });
687
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
+ }
688
728
  var reportRoute = (cardId) => `${REPORTS_BASE}/${encodeURIComponent(cardId)}`;
689
729
  function groupCards(cards) {
690
730
  const groups = /* @__PURE__ */ new Map();
@@ -699,7 +739,7 @@ function useAnalyticsTree() {
699
739
  const t = useTranslation();
700
740
  const { labels, showLiveView } = useAnalyticsConfig();
701
741
  const cards = useAnalyticsCards();
702
- return React5__default.useMemo(() => ({
742
+ return React6__default.useMemo(() => ({
703
743
  title: labels.analytics,
704
744
  // Only the overview. The searchable catalogue (/analytics/reports) is still
705
745
  // routed — bookmarks and the legacy /reports link resolve — but it is not
@@ -718,7 +758,7 @@ function AnalyticsModule({ view, cardId, children }) {
718
758
  const activeId = cardId ? decodeURIComponent(cardId) : void 0;
719
759
  const shellRail = useShellRailAvailable();
720
760
  const claimsRail = shellRail ?? variant !== "rail";
721
- const entries = React5__default.useMemo(() => [
761
+ const entries = React6__default.useMemo(() => [
722
762
  ...tree.fixed.map((f) => ({
723
763
  id: `analytics:${f.id}`,
724
764
  label: f.label,
@@ -745,7 +785,7 @@ function AnalyticsModule({ view, cardId, children }) {
745
785
  }] : []
746
786
  ], [tree]);
747
787
  useContextualNav(claimsRail ? { base: ANALYTICS_BASE, entries, label: tree.title } : null);
748
- const nav = React5__default.useMemo(() => {
788
+ const nav = React6__default.useMemo(() => {
749
789
  if (claimsRail) return [];
750
790
  return [
751
791
  ...tree.fixed.map((f) => ({
@@ -774,7 +814,7 @@ function AnalyticsModule({ view, cardId, children }) {
774
814
  }] : []
775
815
  ];
776
816
  }, [tree, view, activeId, claimsRail]);
777
- return /* @__PURE__ */ jsx(
817
+ return /* @__PURE__ */ jsxs(
778
818
  ModulePage,
779
819
  {
780
820
  title: tree.title,
@@ -782,45 +822,25 @@ function AnalyticsModule({ view, cardId, children }) {
782
822
  navVariant: claimsRail ? "sidebar" : "rail",
783
823
  showHeader: false,
784
824
  viewKey: view === "report" ? `report:${activeId}` : view,
785
- children
825
+ children: [
826
+ /* @__PURE__ */ jsx(AnalyticsAssistant, {}),
827
+ /* @__PURE__ */ jsx("div", { className: "md:pb-16", children })
828
+ ]
786
829
  }
787
830
  );
788
831
  }
789
832
  function AnalyticsOverview() {
790
- const { labels, showHeader, assistant } = useAnalyticsConfig();
833
+ const { labels } = useAnalyticsConfig();
791
834
  const cards = useOverviewCards();
792
- const filters = React5__default.useMemo(() => collectFilters(cards), [cards]);
793
- const t = useTranslation();
794
- const suggestions = React5__default.useMemo(
795
- () => assistant?.suggestions ?? [0, 1, 2].map((i) => ({ label: t(`reports.assistant.suggestion.${i}`) })),
796
- [assistant?.suggestions, t]
797
- );
798
- return /* @__PURE__ */ jsxs(Fragment, { children: [
799
- assistant && /* @__PURE__ */ jsx(
800
- AssistantInvite,
801
- {
802
- id: "analytics",
803
- persona: assistant.persona ?? t("reports.assistant.persona"),
804
- headline: assistant.headline ?? t("reports.assistant.headline"),
805
- pitch: assistant.pitch ?? t("reports.assistant.pitch"),
806
- description: assistant.description ?? t("reports.assistant.description"),
807
- placeholder: assistant.placeholder ?? t("reports.assistant.placeholder"),
808
- suggestions
809
- }
810
- ),
811
- /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
812
- showHeader ? /* @__PURE__ */ jsxs("div", { children: [
813
- /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: labels.analytics }),
814
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: labels.overviewSubtitle })
815
- ] }) : null,
816
- /* @__PURE__ */ jsx(AnalyticsControls, { filters, className: "sticky top-3 z-20" }),
817
- cards.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border border-dashed py-16 text-center", children: [
818
- /* @__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" }) }),
819
- /* @__PURE__ */ jsx("p", { className: "font-medium", children: labels.emptyOverviewTitle }),
820
- /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: labels.emptyOverviewDescription })
821
- ] }) : /* @__PURE__ */ jsx(AnalyticsGrid, { cards })
822
- ] })
823
- ] });
835
+ const filters = React6__default.useMemo(() => collectFilters(cards), [cards]);
836
+ return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
837
+ /* @__PURE__ */ jsx(AnalyticsControls, { filters }),
838
+ cards.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border border-dashed py-16 text-center", children: [
839
+ /* @__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" }) }),
840
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: labels.emptyOverviewTitle }),
841
+ /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: labels.emptyOverviewDescription })
842
+ ] }) : /* @__PURE__ */ jsx(AnalyticsGrid, { cards })
843
+ ] }) });
824
844
  }
825
845
  function formatViewed(iso, locale, never) {
826
846
  if (!iso) return never;
@@ -832,18 +852,18 @@ function ReportsListPage() {
832
852
  const { labels, currency, showHeader } = useAnalyticsConfig();
833
853
  const cards = useAnalyticsCards();
834
854
  const lastViewed = useLastViewed();
835
- const [search, setSearch] = React5__default.useState("");
836
- const [category, setCategory] = React5__default.useState();
837
- const [author, setAuthor] = React5__default.useState();
838
- const categoryOptions = React5__default.useMemo(
855
+ const [search, setSearch] = React6__default.useState("");
856
+ const [category, setCategory] = React6__default.useState();
857
+ const [author, setAuthor] = React6__default.useState();
858
+ const categoryOptions = React6__default.useMemo(
839
859
  () => [...new Set(cards.map((c) => c.category))].sort((a, b) => a.localeCompare(b)).map((c) => ({ value: c, label: c })),
840
860
  [cards]
841
861
  );
842
- const authorOptions = React5__default.useMemo(
862
+ const authorOptions = React6__default.useMemo(
843
863
  () => [...new Set(cards.map((c) => c.createdBy ?? "\u2014"))].sort((a, b) => a.localeCompare(b)).map((a) => ({ value: a, label: a })),
844
864
  [cards]
845
865
  );
846
- const rows = React5__default.useMemo(() => {
866
+ const rows = React6__default.useMemo(() => {
847
867
  const q = search.trim().toLowerCase();
848
868
  return cards.filter((card) => {
849
869
  if (category && card.category !== category) return false;
@@ -852,7 +872,7 @@ function ReportsListPage() {
852
872
  return card.name.toLowerCase().includes(q) || card.description?.toLowerCase().includes(q) || card.category.toLowerCase().includes(q);
853
873
  });
854
874
  }, [cards, search, category, author]);
855
- const columns = React5__default.useMemo(() => [
875
+ const columns = React6__default.useMemo(() => [
856
876
  {
857
877
  id: "name",
858
878
  accessorFn: (card) => card.name,
@@ -972,18 +992,18 @@ function ExportDropdown({ columns, data, filename, currency, title }) {
972
992
  const [excelAvailable, setExcelAvailable] = useState(false);
973
993
  const [pdfAvailable, setPdfAvailable] = useState(false);
974
994
  useEffect(() => {
975
- import('./excel-WE73YE2V.js').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {
995
+ import('./excel-KIJTC7OJ.js').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {
976
996
  });
977
- import('./pdf-E6DXHFXU.js').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {
997
+ import('./pdf-7QQ3U5JQ.js').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {
978
998
  });
979
999
  }, []);
980
1000
  const handleCsv = () => exportToCsv(columns, data, filename, currency);
981
1001
  const handleExcel = async () => {
982
- const { exportToExcel } = await import('./excel-WE73YE2V.js');
1002
+ const { exportToExcel } = await import('./excel-KIJTC7OJ.js');
983
1003
  await exportToExcel(columns, data, filename, currency);
984
1004
  };
985
1005
  const handlePdf = async () => {
986
- const { exportToPdf } = await import('./pdf-E6DXHFXU.js');
1006
+ const { exportToPdf } = await import('./pdf-7QQ3U5JQ.js');
987
1007
  await exportToPdf(columns, data, filename, { title, currency });
988
1008
  };
989
1009
  return /* @__PURE__ */ jsxs(Dropdown, { children: [
@@ -1051,8 +1071,8 @@ function ViewerBody({ card }) {
1051
1071
  const { labels, currency } = useAnalyticsConfig();
1052
1072
  const { state, setPage, setPageSize } = useAnalyticsContext();
1053
1073
  const { result, loading, error, engineMissing } = useAnalyticsCard(card);
1054
- const columns = React5__default.useMemo(() => cardColumns(card), [card]);
1055
- const tanstackColumns = React5__default.useMemo(
1074
+ const columns = React6__default.useMemo(() => cardColumns(card), [card]);
1075
+ const tanstackColumns = React6__default.useMemo(
1056
1076
  () => reportColumnsToTanstack(columns, currency.code),
1057
1077
  [columns, currency.code]
1058
1078
  );
@@ -1109,7 +1129,7 @@ function AnalyticsReportViewer({ cardId }) {
1109
1129
  const { labels } = useAnalyticsConfig();
1110
1130
  const cards = useAnalyticsCards();
1111
1131
  const card = cards.find((c) => c.id === decodeURIComponent(cardId ?? ""));
1112
- React5__default.useEffect(() => {
1132
+ React6__default.useEffect(() => {
1113
1133
  if (card) markViewed(card.id);
1114
1134
  }, [card?.id]);
1115
1135
  if (!card) {
@@ -1146,7 +1166,7 @@ function AnalyticsReportViewer({ cardId }) {
1146
1166
  function ViewerActions({ card }) {
1147
1167
  const { currency } = useAnalyticsConfig();
1148
1168
  const { result } = useAnalyticsCard(card);
1149
- const columns = React5__default.useMemo(() => cardColumns(card), [card]);
1169
+ const columns = React6__default.useMemo(() => cardColumns(card), [card]);
1150
1170
  if (!result?.rows?.length) return null;
1151
1171
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1152
1172
  /* @__PURE__ */ jsx(
@@ -1234,7 +1254,7 @@ async function fetchViaView(reportDef, query, getClient, resolveTenantId, tenant
1234
1254
  }
1235
1255
  }
1236
1256
  if (query.search) {
1237
- const searchableCols = reportDef.columns.filter((c) => c.type === "text" || c.type === "email").map((c) => camelToSnake2(c.key));
1257
+ const searchableCols = reportDef.columns.filter((c) => c.type === "text" || c.type === "email" || c.type === "person").map((c) => camelToSnake2(c.key));
1238
1258
  if (searchableCols.length > 0) {
1239
1259
  const term = `%${query.search}%`;
1240
1260
  const orClause = searchableCols.map((col) => `${col}.ilike.${term}`).join(",");
@@ -1349,13 +1369,13 @@ function createAnalyticsPlugin(options = {}) {
1349
1369
  () => createSupabaseAnalyticsProvider({ tenantId: () => getReportsTenantId() }),
1350
1370
  () => createMockAnalyticsProvider()
1351
1371
  );
1352
- const page2 = (Body, view) => (props) => React5__default.createElement(
1372
+ const page2 = (Body, view) => (props) => React6__default.createElement(
1353
1373
  AnalyticsSurface,
1354
1374
  { config, provider },
1355
- React5__default.createElement(
1375
+ React6__default.createElement(
1356
1376
  AnalyticsModule,
1357
1377
  { view, cardId: props?.cardId },
1358
- React5__default.createElement(Body, props)
1378
+ React6__default.createElement(Body, props)
1359
1379
  )
1360
1380
  );
1361
1381
  const READ = { feature: "reports", action: "read" };