@fayz-ai/plugin-reports 0.10.0 → 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.
@@ -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,29 @@ 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, showHeader } = 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
+ showHeader ? /* @__PURE__ */ jsxs("div", { children: [
838
+ /* @__PURE__ */ jsx("h1", { className: "text-2xl font-semibold tracking-tight", children: labels.analytics }),
839
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: labels.overviewSubtitle })
840
+ ] }) : null,
841
+ /* @__PURE__ */ jsx(AnalyticsControls, { filters, className: "sticky top-3 z-20" }),
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: [
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" }) }),
844
+ /* @__PURE__ */ jsx("p", { className: "font-medium", children: labels.emptyOverviewTitle }),
845
+ /* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: labels.emptyOverviewDescription })
846
+ ] }) : /* @__PURE__ */ jsx(AnalyticsGrid, { cards })
847
+ ] }) });
824
848
  }
825
849
  function formatViewed(iso, locale, never) {
826
850
  if (!iso) return never;
@@ -832,18 +856,18 @@ function ReportsListPage() {
832
856
  const { labels, currency, showHeader } = useAnalyticsConfig();
833
857
  const cards = useAnalyticsCards();
834
858
  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(
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(
839
863
  () => [...new Set(cards.map((c) => c.category))].sort((a, b) => a.localeCompare(b)).map((c) => ({ value: c, label: c })),
840
864
  [cards]
841
865
  );
842
- const authorOptions = React5__default.useMemo(
866
+ const authorOptions = React6__default.useMemo(
843
867
  () => [...new Set(cards.map((c) => c.createdBy ?? "\u2014"))].sort((a, b) => a.localeCompare(b)).map((a) => ({ value: a, label: a })),
844
868
  [cards]
845
869
  );
846
- const rows = React5__default.useMemo(() => {
870
+ const rows = React6__default.useMemo(() => {
847
871
  const q = search.trim().toLowerCase();
848
872
  return cards.filter((card) => {
849
873
  if (category && card.category !== category) return false;
@@ -852,7 +876,7 @@ function ReportsListPage() {
852
876
  return card.name.toLowerCase().includes(q) || card.description?.toLowerCase().includes(q) || card.category.toLowerCase().includes(q);
853
877
  });
854
878
  }, [cards, search, category, author]);
855
- const columns = React5__default.useMemo(() => [
879
+ const columns = React6__default.useMemo(() => [
856
880
  {
857
881
  id: "name",
858
882
  accessorFn: (card) => card.name,
@@ -972,18 +996,18 @@ function ExportDropdown({ columns, data, filename, currency, title }) {
972
996
  const [excelAvailable, setExcelAvailable] = useState(false);
973
997
  const [pdfAvailable, setPdfAvailable] = useState(false);
974
998
  useEffect(() => {
975
- import('./excel-WE73YE2V.js').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {
999
+ import('./excel-KIJTC7OJ.js').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {
976
1000
  });
977
- import('./pdf-E6DXHFXU.js').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {
1001
+ import('./pdf-7QQ3U5JQ.js').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {
978
1002
  });
979
1003
  }, []);
980
1004
  const handleCsv = () => exportToCsv(columns, data, filename, currency);
981
1005
  const handleExcel = async () => {
982
- const { exportToExcel } = await import('./excel-WE73YE2V.js');
1006
+ const { exportToExcel } = await import('./excel-KIJTC7OJ.js');
983
1007
  await exportToExcel(columns, data, filename, currency);
984
1008
  };
985
1009
  const handlePdf = async () => {
986
- const { exportToPdf } = await import('./pdf-E6DXHFXU.js');
1010
+ const { exportToPdf } = await import('./pdf-7QQ3U5JQ.js');
987
1011
  await exportToPdf(columns, data, filename, { title, currency });
988
1012
  };
989
1013
  return /* @__PURE__ */ jsxs(Dropdown, { children: [
@@ -1051,8 +1075,8 @@ function ViewerBody({ card }) {
1051
1075
  const { labels, currency } = useAnalyticsConfig();
1052
1076
  const { state, setPage, setPageSize } = useAnalyticsContext();
1053
1077
  const { result, loading, error, engineMissing } = useAnalyticsCard(card);
1054
- const columns = React5__default.useMemo(() => cardColumns(card), [card]);
1055
- const tanstackColumns = React5__default.useMemo(
1078
+ const columns = React6__default.useMemo(() => cardColumns(card), [card]);
1079
+ const tanstackColumns = React6__default.useMemo(
1056
1080
  () => reportColumnsToTanstack(columns, currency.code),
1057
1081
  [columns, currency.code]
1058
1082
  );
@@ -1109,7 +1133,7 @@ function AnalyticsReportViewer({ cardId }) {
1109
1133
  const { labels } = useAnalyticsConfig();
1110
1134
  const cards = useAnalyticsCards();
1111
1135
  const card = cards.find((c) => c.id === decodeURIComponent(cardId ?? ""));
1112
- React5__default.useEffect(() => {
1136
+ React6__default.useEffect(() => {
1113
1137
  if (card) markViewed(card.id);
1114
1138
  }, [card?.id]);
1115
1139
  if (!card) {
@@ -1146,7 +1170,7 @@ function AnalyticsReportViewer({ cardId }) {
1146
1170
  function ViewerActions({ card }) {
1147
1171
  const { currency } = useAnalyticsConfig();
1148
1172
  const { result } = useAnalyticsCard(card);
1149
- const columns = React5__default.useMemo(() => cardColumns(card), [card]);
1173
+ const columns = React6__default.useMemo(() => cardColumns(card), [card]);
1150
1174
  if (!result?.rows?.length) return null;
1151
1175
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1152
1176
  /* @__PURE__ */ jsx(
@@ -1234,7 +1258,7 @@ async function fetchViaView(reportDef, query, getClient, resolveTenantId, tenant
1234
1258
  }
1235
1259
  }
1236
1260
  if (query.search) {
1237
- 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));
1238
1262
  if (searchableCols.length > 0) {
1239
1263
  const term = `%${query.search}%`;
1240
1264
  const orClause = searchableCols.map((col) => `${col}.ilike.${term}`).join(",");
@@ -1349,13 +1373,13 @@ function createAnalyticsPlugin(options = {}) {
1349
1373
  () => createSupabaseAnalyticsProvider({ tenantId: () => getReportsTenantId() }),
1350
1374
  () => createMockAnalyticsProvider()
1351
1375
  );
1352
- const page2 = (Body, view) => (props) => React5__default.createElement(
1376
+ const page2 = (Body, view) => (props) => React6__default.createElement(
1353
1377
  AnalyticsSurface,
1354
1378
  { config, provider },
1355
- React5__default.createElement(
1379
+ React6__default.createElement(
1356
1380
  AnalyticsModule,
1357
1381
  { view, cardId: props?.cardId },
1358
- React5__default.createElement(Body, props)
1382
+ React6__default.createElement(Body, props)
1359
1383
  )
1360
1384
  );
1361
1385
  const READ = { feature: "reports", action: "read" };