@fayz-ai/plugin-reports 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/README.md +4 -4
  2. package/dist/{chunk-LODXZ2SC.js → chunk-2ZFN6ECM.js} +3 -3
  3. package/dist/chunk-2ZFN6ECM.js.map +1 -0
  4. package/dist/{ReportsContext.d.ts → context.d.ts} +1 -1
  5. package/dist/context.d.ts.map +1 -0
  6. package/dist/data/aggregate.d.ts +14 -0
  7. package/dist/data/aggregate.d.ts.map +1 -0
  8. package/dist/data/analytics-mock.d.ts +3 -0
  9. package/dist/data/analytics-mock.d.ts.map +1 -0
  10. package/dist/data/analytics-supabase.d.ts +7 -0
  11. package/dist/data/analytics-supabase.d.ts.map +1 -0
  12. package/dist/{excel-QHUSM6IH.js → excel-WE73YE2V.js} +3 -3
  13. package/dist/{excel-QHUSM6IH.js.map → excel-WE73YE2V.js.map} +1 -1
  14. package/dist/index.d.ts +63 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +972 -650
  17. package/dist/index.js.map +1 -1
  18. package/dist/lib/lastViewed.d.ts +5 -0
  19. package/dist/lib/lastViewed.d.ts.map +1 -0
  20. package/dist/lib/legacy.d.ts +5 -0
  21. package/dist/lib/legacy.d.ts.map +1 -0
  22. package/dist/{pdf-HETKN26Y.js → pdf-E6DXHFXU.js} +3 -3
  23. package/dist/{pdf-HETKN26Y.js.map → pdf-E6DXHFXU.js.map} +1 -1
  24. package/dist/views/AnalyticsOverview.d.ts +3 -0
  25. package/dist/views/AnalyticsOverview.d.ts.map +1 -0
  26. package/dist/views/AnalyticsReportViewer.d.ts +5 -0
  27. package/dist/views/AnalyticsReportViewer.d.ts.map +1 -0
  28. package/dist/views/AnalyticsSurface.d.ts +75 -0
  29. package/dist/views/AnalyticsSurface.d.ts.map +1 -0
  30. package/dist/views/LiveViewPage.d.ts +3 -0
  31. package/dist/views/LiveViewPage.d.ts.map +1 -0
  32. package/dist/views/ReportsListPage.d.ts +3 -0
  33. package/dist/views/ReportsListPage.d.ts.map +1 -0
  34. package/dist/{ReportsPage.d.ts → views/ReportsPage.d.ts} +2 -2
  35. package/dist/views/ReportsPage.d.ts.map +1 -0
  36. package/package.json +5 -7
  37. package/dist/ReportsContext.d.ts.map +0 -1
  38. package/dist/ReportsPage.d.ts.map +0 -1
  39. package/dist/chunk-LODXZ2SC.js.map +0 -1
  40. package/src/ReportsContext.tsx +0 -33
  41. package/src/ReportsPage.tsx +0 -62
  42. package/src/components/ExportDropdown.tsx +0 -63
  43. package/src/components/ReportCard.tsx +0 -38
  44. package/src/components/ReportFilterBar.tsx +0 -132
  45. package/src/components/ReportPagination.tsx +0 -59
  46. package/src/components/ReportSummaryRow.tsx +0 -68
  47. package/src/data/index.ts +0 -4
  48. package/src/data/mock.ts +0 -9
  49. package/src/data/supabase.ts +0 -200
  50. package/src/data/types.ts +0 -10
  51. package/src/export/csv.ts +0 -21
  52. package/src/export/excel.ts +0 -42
  53. package/src/export/pdf.ts +0 -47
  54. package/src/hooks/useReportData.ts +0 -123
  55. package/src/index.ts +0 -142
  56. package/src/lib/columns.ts +0 -130
  57. package/src/lib/tenant.ts +0 -3
  58. package/src/locales/en.ts +0 -28
  59. package/src/locales/index.ts +0 -7
  60. package/src/locales/pt-BR.ts +0 -28
  61. package/src/types.ts +0 -171
  62. package/src/views/ReportHub.tsx +0 -97
  63. package/src/views/ReportViewer.tsx +0 -182
@@ -0,0 +1,75 @@
1
+ import React from 'react';
2
+ import { type AnalyticsCardDef, type AnalyticsProvider as AnalyticsProviderType, type AnalyticsColumnDef, type AnalyticsCompare } from '@fayz-ai/core';
3
+ import { type AnalyticsRangePreset } from '@fayz-ai/ui';
4
+ export declare const ANALYTICS_BASE = "/analytics";
5
+ export declare const REPORTS_BASE = "/analytics/reports";
6
+ export interface AnalyticsLabels {
7
+ analytics: string;
8
+ reports: string;
9
+ liveView: string;
10
+ overviewSubtitle: string;
11
+ reportsSubtitle: string;
12
+ searchReports: string;
13
+ category: string;
14
+ createdBy: string;
15
+ /** Cleared state of each facet chip: "All categories" / "All authors". */
16
+ allCategories: string;
17
+ allCreatedBy: string;
18
+ lastViewed: string;
19
+ name: string;
20
+ noResults: string;
21
+ never: string;
22
+ back: string;
23
+ export: string;
24
+ print: string;
25
+ unavailableTitle: string;
26
+ unavailableDescription: string;
27
+ emptyOverviewTitle: string;
28
+ emptyOverviewDescription: string;
29
+ liveViewSoon: string;
30
+ results: string;
31
+ }
32
+ export interface ResolvedAnalyticsConfig {
33
+ /** Cards declared by the host app (merged with the plugin registry at render). */
34
+ cards: AnalyticsCardDef[];
35
+ labels: AnalyticsLabels;
36
+ currency: {
37
+ code: string;
38
+ locale: string;
39
+ symbol: string;
40
+ };
41
+ /** Attribution for pre-built cards — the product name, e.g. "FayzShop". */
42
+ createdBy: string;
43
+ defaultPageSize: number;
44
+ showHeader: boolean;
45
+ /** Range the surface opens on. A salon reads 30 days; a school reads a term. */
46
+ defaultRange: AnalyticsRangePreset;
47
+ defaultCompare: AnalyticsCompare;
48
+ }
49
+ export declare function useAnalyticsConfig(): ResolvedAnalyticsConfig;
50
+ /**
51
+ * Every card visible on this surface, registry + app, de-duplicated by id.
52
+ *
53
+ * The app's own definition wins a collision so a host can retune a card the
54
+ * plugin shipped (relabel it, move it off the overview) without forking the
55
+ * plugin — the alternative is apps carrying patched copies of plugin cards.
56
+ */
57
+ export declare function useAnalyticsCards(): AnalyticsCardDef[];
58
+ /** Cards a pack placed on the overview grid, in their declared order. */
59
+ export declare function useOverviewCards(): AnalyticsCardDef[];
60
+ /**
61
+ * Columns for a card's tabular view.
62
+ *
63
+ * An aggregate card has no `columns` of its own — its table IS its dimensions
64
+ * and measures, so deriving them keeps one definition instead of asking every
65
+ * card author to restate the same fields twice and let the two drift.
66
+ */
67
+ export declare function cardColumns(card: AnalyticsCardDef): AnalyticsColumnDef[];
68
+ export declare function navigateAnalytics(path: string): void;
69
+ export interface AnalyticsSurfaceProps {
70
+ config: ResolvedAnalyticsConfig;
71
+ provider: AnalyticsProviderType;
72
+ children?: React.ReactNode;
73
+ }
74
+ export declare function AnalyticsSurface({ config, provider, children }: AnalyticsSurfaceProps): React.JSX.Element;
75
+ //# sourceMappingURL=AnalyticsSurface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AnalyticsSurface.d.ts","sourceRoot":"","sources":["../../src/views/AnalyticsSurface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAEL,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,IAAI,qBAAqB,EAAE,KAAK,kBAAkB,EAC/F,KAAK,gBAAgB,EACtB,MAAM,eAAe,CAAA;AACtB,OAAO,EAA4B,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAYjF,eAAO,MAAM,cAAc,eAAe,CAAA;AAC1C,eAAO,MAAM,YAAY,uBAA8B,CAAA;AAEvD,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,eAAe,EAAE,MAAM,CAAA;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,0EAA0E;IAC1E,aAAa,EAAE,MAAM,CAAA;IACrB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,gBAAgB,EAAE,MAAM,CAAA;IACxB,sBAAsB,EAAE,MAAM,CAAA;IAC9B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,wBAAwB,EAAE,MAAM,CAAA;IAChC,YAAY,EAAE,MAAM,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,kFAAkF;IAClF,KAAK,EAAE,gBAAgB,EAAE,CAAA;IACzB,MAAM,EAAE,eAAe,CAAA;IACvB,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC1D,2EAA2E;IAC3E,SAAS,EAAE,MAAM,CAAA;IACjB,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,YAAY,EAAE,oBAAoB,CAAA;IAClC,cAAc,EAAE,gBAAgB,CAAA;CACjC;AAID,wBAAgB,kBAAkB,IAAI,uBAAuB,CAI5D;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,IAAI,gBAAgB,EAAE,CAStD;AAED,yEAAyE;AACzE,wBAAgB,gBAAgB,IAAI,gBAAgB,EAAE,CAQrD;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,gBAAgB,GAAG,kBAAkB,EAAE,CAiBxE;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEpD;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,uBAAuB,CAAA;IAC/B,QAAQ,EAAE,qBAAqB,CAAA;IAC/B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,qBAAqB,qBAqBrF"}
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function LiveViewPage(): React.JSX.Element;
3
+ //# sourceMappingURL=LiveViewPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LiveViewPage.d.ts","sourceRoot":"","sources":["../../src/views/LiveViewPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAazB,wBAAgB,YAAY,sBAW3B"}
@@ -0,0 +1,3 @@
1
+ import React from 'react';
2
+ export declare function ReportsListPage(): React.JSX.Element;
3
+ //# sourceMappingURL=ReportsListPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReportsListPage.d.ts","sourceRoot":"","sources":["../../src/views/ReportsListPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAiCzB,wBAAgB,eAAe,sBAmK9B"}
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
- import type { ResolvedReportsConfig } from './types';
3
- import type { ReportDataProvider } from './data/types';
2
+ import type { ResolvedReportsConfig } from '../types';
3
+ import type { ReportDataProvider } from '../data/types';
4
4
  export declare function ReportsPage({ config, provider, }: {
5
5
  config: ResolvedReportsConfig;
6
6
  provider: ReportDataProvider;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReportsPage.d.ts","sourceRoot":"","sources":["../../src/views/ReportsPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAI/D,OAAO,KAAK,EAAE,qBAAqB,EAAa,MAAM,UAAU,CAAA;AAChE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAA;AAYvD,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,kBAAkB,CAAA;CAC7B,qBAsCA"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "fayz": {
4
4
  "status": "preview"
5
5
  },
6
- "version": "0.9.0",
6
+ "version": "0.9.1",
7
7
  "description": "[experimental] Fayz SDK — plugin-reports plugin",
8
8
  "type": "module",
9
9
  "sideEffects": false,
@@ -12,14 +12,12 @@
12
12
  "types": "./dist/index.d.ts",
13
13
  "exports": {
14
14
  ".": {
15
- "source": "./src/index.ts",
16
15
  "types": "./dist/index.d.ts",
17
16
  "import": "./dist/index.js"
18
17
  }
19
18
  },
20
19
  "files": [
21
- "dist",
22
- "src"
20
+ "dist"
23
21
  ],
24
22
  "peerDependencies": {
25
23
  "react": "^18.0.0 || ^19.0.0",
@@ -29,9 +27,9 @@
29
27
  },
30
28
  "dependencies": {
31
29
  "@tanstack/react-table": "^8.20.0",
32
- "@fayz-ai/core": "^0.9.0",
33
- "@fayz-ai/ui": "^0.9.0",
34
- "@fayz-ai/saas": "^0.9.0"
30
+ "@fayz-ai/core": "^0.10.0",
31
+ "@fayz-ai/ui": "^0.10.0",
32
+ "@fayz-ai/admin": "^0.10.0"
35
33
  },
36
34
  "devDependencies": {
37
35
  "@types/react": "^18.3.0",
@@ -1 +0,0 @@
1
- {"version":3,"file":"ReportsContext.d.ts","sourceRoot":"","sources":["../src/ReportsContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAA;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAEtD,UAAU,mBAAmB;IAC3B,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,OAAO,EAAE,SAAS,EAAE,CAAA;CACrB;AAID,wBAAgB,iBAAiB,IAAI,mBAAmB,CAIvD;AAED,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,kBAAkB,CAAA;IAC5B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,qBAMA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"ReportsPage.d.ts","sourceRoot":"","sources":["../src/ReportsPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA2C,MAAM,OAAO,CAAA;AAI/D,OAAO,KAAK,EAAE,qBAAqB,EAAa,MAAM,SAAS,CAAA;AAC/D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAA;AAYtD,wBAAgB,WAAW,CAAC,EAC1B,MAAM,EACN,QAAQ,GACT,EAAE;IACD,MAAM,EAAE,qBAAqB,CAAA;IAC7B,QAAQ,EAAE,kBAAkB,CAAA;CAC7B,qBAsCA"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/columns.ts"],"names":["formatCurrencyLocale","formatDateLocale","formatDateTimeLocale"],"mappings":"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","file":"chunk-LODXZ2SC.js","sourcesContent":["import React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport type { ReportColumnDef } from '../types'\nimport { PersonLink } from '@fayz-ai/saas'\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 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,33 +0,0 @@
1
- import React, { createContext, useContext } from 'react'
2
- import type { ResolvedReportsConfig, ReportDef } from './types'
3
- import type { ReportDataProvider } from './data/types'
4
-
5
- interface ReportsContextValue {
6
- config: ResolvedReportsConfig
7
- provider: ReportDataProvider
8
- reports: ReportDef[]
9
- }
10
-
11
- const ReportsContext = createContext<ReportsContextValue | null>(null)
12
-
13
- export function useReportsContext(): ReportsContextValue {
14
- const ctx = useContext(ReportsContext)
15
- if (!ctx) throw new Error('useReportsContext must be used within ReportsContextProvider')
16
- return ctx
17
- }
18
-
19
- export function ReportsContextProvider({
20
- config,
21
- provider,
22
- children,
23
- }: {
24
- config: ResolvedReportsConfig
25
- provider: ReportDataProvider
26
- children: React.ReactNode
27
- }) {
28
- return (
29
- <ReportsContext.Provider value={{ config, provider, reports: config.reports }}>
30
- {children}
31
- </ReportsContext.Provider>
32
- )
33
- }
@@ -1,62 +0,0 @@
1
- import React, { useState, useEffect, useCallback } from 'react'
2
- import { ReportsContextProvider } from './ReportsContext'
3
- import { ReportHub } from './views/ReportHub'
4
- import { ReportViewer } from './views/ReportViewer'
5
- import type { ResolvedReportsConfig, ReportDef } from './types'
6
- import type { ReportDataProvider } from './data/types'
7
-
8
- const HASH_BASE = '/reports'
9
-
10
- function getReportIdFromHash(): string | null {
11
- const hash = (window.location.hash.slice(1) || '/').split('?')[0]
12
- if (hash.startsWith(HASH_BASE + '/') && hash.length > HASH_BASE.length + 1) {
13
- return hash.slice(HASH_BASE.length + 1)
14
- }
15
- return null
16
- }
17
-
18
- export function ReportsPage({
19
- config,
20
- provider,
21
- }: {
22
- config: ResolvedReportsConfig
23
- provider: ReportDataProvider
24
- }) {
25
- const [activeReportId, setActiveReportId] = useState<string | null>(getReportIdFromHash)
26
-
27
- useEffect(() => {
28
- function handler() {
29
- setActiveReportId(getReportIdFromHash())
30
- }
31
- window.addEventListener('hashchange', handler)
32
- return () => window.removeEventListener('hashchange', handler)
33
- }, [])
34
-
35
- const handleSelect = useCallback((report: ReportDef) => {
36
- window.location.hash = `${HASH_BASE}/${report.id}`
37
- }, [])
38
-
39
- const handleBack = useCallback(() => {
40
- window.location.hash = HASH_BASE
41
- }, [])
42
-
43
- const activeReport = activeReportId
44
- ? config.reports.find((r) => r.id === activeReportId) ?? null
45
- : null
46
-
47
- return (
48
- <ReportsContextProvider config={config} provider={provider}>
49
- <div className="p-1">
50
- {activeReport ? (
51
- <ReportViewer
52
- key={activeReport.id}
53
- report={activeReport}
54
- onBack={handleBack}
55
- />
56
- ) : (
57
- <ReportHub onSelect={handleSelect} />
58
- )}
59
- </div>
60
- </ReportsContextProvider>
61
- )
62
- }
@@ -1,63 +0,0 @@
1
- import React, { useEffect, useState } from 'react'
2
- import { Download } from 'lucide-react'
3
- import { Button } from '@fayz-ai/ui'
4
- import {
5
- Dropdown,
6
- DropdownTrigger,
7
- DropdownContent,
8
- DropdownItem,
9
- } from '@fayz-ai/ui'
10
- import { useTranslation } from '@fayz-ai/core'
11
- import type { ReportColumnDef } from '../types'
12
- import { exportToCsv } from '../export/csv'
13
-
14
- interface ExportDropdownProps {
15
- columns: ReportColumnDef[]
16
- data: Record<string, any>[]
17
- filename: string
18
- currency?: string
19
- title?: string
20
- }
21
-
22
- export function ExportDropdown({ columns, data, filename, currency, title }: ExportDropdownProps) {
23
- const t = useTranslation()
24
- const [excelAvailable, setExcelAvailable] = useState(false)
25
- const [pdfAvailable, setPdfAvailable] = useState(false)
26
-
27
- useEffect(() => {
28
- import('../export/excel').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {})
29
- import('../export/pdf').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {})
30
- }, [])
31
-
32
- const handleCsv = () => exportToCsv(columns, data, filename, currency)
33
-
34
- const handleExcel = async () => {
35
- const { exportToExcel } = await import('../export/excel')
36
- await exportToExcel(columns, data, filename, currency)
37
- }
38
-
39
- const handlePdf = async () => {
40
- const { exportToPdf } = await import('../export/pdf')
41
- await exportToPdf(columns, data, filename, { title, currency })
42
- }
43
-
44
- return (
45
- <Dropdown>
46
- <DropdownTrigger asChild>
47
- <Button variant="outline" size="sm">
48
- <Download className="h-4 w-4 mr-1.5" />
49
- {t('reports.export')}
50
- </Button>
51
- </DropdownTrigger>
52
- <DropdownContent align="end">
53
- <DropdownItem onClick={handleCsv}>{t('reports.exportCsv')}</DropdownItem>
54
- {excelAvailable && (
55
- <DropdownItem onClick={handleExcel}>{t('reports.exportExcel')}</DropdownItem>
56
- )}
57
- {pdfAvailable && (
58
- <DropdownItem onClick={handlePdf}>{t('reports.exportPdf')}</DropdownItem>
59
- )}
60
- </DropdownContent>
61
- </Dropdown>
62
- )
63
- }
@@ -1,38 +0,0 @@
1
- import React from 'react'
2
- import { FileText } from 'lucide-react'
3
- import { Badge, getLucideIcon } from '@fayz-ai/ui'
4
- import type { ReportDef } from '../types'
5
-
6
- interface ReportCardProps {
7
- report: ReportDef
8
- onClick: (report: ReportDef) => void
9
- }
10
-
11
- export function ReportCard({ report, onClick }: ReportCardProps) {
12
- const IconComp = getLucideIcon(report.icon) ?? FileText
13
-
14
- return (
15
- <button
16
- type="button"
17
- onClick={() => onClick(report)}
18
- className="flex flex-col items-start gap-2 rounded-xl border bg-card p-4 text-left transition-all hover:border-primary/30 hover:shadow-md"
19
- >
20
- <div className="flex w-full items-start justify-between">
21
- <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary">
22
- <IconComp className="h-5 w-5" />
23
- </div>
24
- {report.badge && (
25
- <Badge variant="secondary" className="text-xs capitalize">
26
- {report.badge}
27
- </Badge>
28
- )}
29
- </div>
30
- <div>
31
- <h4 className="font-medium text-sm">{report.name}</h4>
32
- {report.description && (
33
- <p className="text-xs text-muted-foreground mt-0.5 line-clamp-2">{report.description}</p>
34
- )}
35
- </div>
36
- </button>
37
- )
38
- }
@@ -1,132 +0,0 @@
1
- import React, { useEffect, useState } from 'react'
2
- import { DatePicker } from '@fayz-ai/ui'
3
- import { Select, SelectTrigger, SelectContent, SelectItem, SelectValue } from '@fayz-ai/ui'
4
- import { Input } from '@fayz-ai/ui'
5
- import { useTranslation } from '@fayz-ai/core'
6
- import type { ReportFilterDef, ReportDateRange, ReportFilterOption } from '../types'
7
-
8
- interface ReportFilterBarProps {
9
- dateRange: ReportDateRange
10
- onDateRangeChange: (range: ReportDateRange) => void
11
- filters: ReportFilterDef[]
12
- filterValues: Record<string, any>
13
- onFilterChange: (key: string, value: any) => void
14
- }
15
-
16
- export function ReportFilterBar({
17
- dateRange,
18
- onDateRangeChange,
19
- filters,
20
- filterValues,
21
- onFilterChange,
22
- }: ReportFilterBarProps) {
23
- const t = useTranslation()
24
-
25
- return (
26
- <div className="flex flex-wrap items-end gap-3">
27
- {/* Date range — always present */}
28
- <div className="flex items-end gap-2">
29
- <div className="space-y-1">
30
- <label className="text-xs font-medium text-muted-foreground">{t('reports.from')}</label>
31
- <DatePicker
32
- value={dateRange.from}
33
- onChange={(v) => onDateRangeChange({ ...dateRange, from: v })}
34
- />
35
- </div>
36
- <div className="space-y-1">
37
- <label className="text-xs font-medium text-muted-foreground">{t('reports.to')}</label>
38
- <DatePicker
39
- value={dateRange.to}
40
- onChange={(v) => onDateRangeChange({ ...dateRange, to: v })}
41
- />
42
- </div>
43
- </div>
44
-
45
- {/* Custom filters */}
46
- {filters.map((filter) => (
47
- <FilterControl
48
- key={filter.key}
49
- filter={filter}
50
- value={filterValues[filter.key]}
51
- onChange={(v) => onFilterChange(filter.key, v)}
52
- />
53
- ))}
54
- </div>
55
- )
56
- }
57
-
58
- // ---------------------------------------------------------------------------
59
- // Individual filter control
60
- // ---------------------------------------------------------------------------
61
-
62
- function FilterControl({
63
- filter,
64
- value,
65
- onChange,
66
- }: {
67
- filter: ReportFilterDef
68
- value: any
69
- onChange: (value: any) => void
70
- }) {
71
- const [options, setOptions] = useState<ReportFilterOption[]>(
72
- Array.isArray(filter.options) ? filter.options : [],
73
- )
74
-
75
- useEffect(() => {
76
- if (typeof filter.options === 'function') {
77
- filter.options().then(setOptions)
78
- }
79
- }, [filter.options])
80
-
81
- if (filter.type === 'select') {
82
- return (
83
- <div className="space-y-1">
84
- <label className="text-xs font-medium text-muted-foreground">{filter.label}</label>
85
- <Select value={value ?? ''} onValueChange={(v) => onChange(v === '__all__' ? '' : v)}>
86
- <SelectTrigger className="w-[180px]">
87
- <SelectValue placeholder={filter.placeholder ?? filter.label} />
88
- </SelectTrigger>
89
- <SelectContent>
90
- <SelectItem value="__all__">All</SelectItem>
91
- {options.map((opt) => (
92
- <SelectItem key={opt.value} value={opt.value}>
93
- {opt.label}
94
- </SelectItem>
95
- ))}
96
- </SelectContent>
97
- </Select>
98
- </div>
99
- )
100
- }
101
-
102
- if (filter.type === 'text') {
103
- return (
104
- <div className="space-y-1">
105
- <label className="text-xs font-medium text-muted-foreground">{filter.label}</label>
106
- <Input
107
- value={value ?? ''}
108
- onChange={(e) => onChange(e.target.value)}
109
- placeholder={filter.placeholder ?? filter.label}
110
- className="w-[180px]"
111
- />
112
- </div>
113
- )
114
- }
115
-
116
- if (filter.type === 'number') {
117
- return (
118
- <div className="space-y-1">
119
- <label className="text-xs font-medium text-muted-foreground">{filter.label}</label>
120
- <Input
121
- type="number"
122
- value={value ?? ''}
123
- onChange={(e) => onChange(e.target.value ? Number(e.target.value) : undefined)}
124
- placeholder={filter.placeholder ?? filter.label}
125
- className="w-[120px]"
126
- />
127
- </div>
128
- )
129
- }
130
-
131
- return null
132
- }
@@ -1,59 +0,0 @@
1
- import React from 'react'
2
- import { ChevronLeft, ChevronRight } from 'lucide-react'
3
- import { Button } from '@fayz-ai/ui'
4
-
5
- interface ReportPaginationProps {
6
- page: number
7
- pageSize: number
8
- total: number
9
- onPageChange: (page: number) => void
10
- onPageSizeChange: (size: number) => void
11
- }
12
-
13
- export function ReportPagination({ page, pageSize, total, onPageChange, onPageSizeChange }: ReportPaginationProps) {
14
- const totalPages = Math.max(1, Math.ceil(total / pageSize))
15
- const from = (page - 1) * pageSize + 1
16
- const to = Math.min(page * pageSize, total)
17
-
18
- return (
19
- <div className="flex items-center justify-between border-t px-4 py-3">
20
- <div className="text-sm text-muted-foreground">
21
- {total > 0 ? `${from}–${to} of ${total}` : 'No results'}
22
- </div>
23
-
24
- <div className="flex items-center gap-3">
25
- <select
26
- value={pageSize}
27
- onChange={(e) => onPageSizeChange(Number(e.target.value))}
28
- className="h-8 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2 text-sm"
29
- >
30
- {[25, 50, 100].map((size) => (
31
- <option key={size} value={size}>{size} / page</option>
32
- ))}
33
- </select>
34
-
35
- <div className="flex items-center gap-1">
36
- <Button
37
- variant="outline"
38
- size="sm"
39
- disabled={page <= 1}
40
- onClick={() => onPageChange(page - 1)}
41
- >
42
- <ChevronLeft className="h-4 w-4" />
43
- </Button>
44
- <span className="text-sm px-2">
45
- {page} / {totalPages}
46
- </span>
47
- <Button
48
- variant="outline"
49
- size="sm"
50
- disabled={page >= totalPages}
51
- onClick={() => onPageChange(page + 1)}
52
- >
53
- <ChevronRight className="h-4 w-4" />
54
- </Button>
55
- </div>
56
- </div>
57
- </div>
58
- )
59
- }
@@ -1,68 +0,0 @@
1
- import React from 'react'
2
- import type { ReportColumnDef } from '../types'
3
- import { formatValuePlain } from '../lib/columns'
4
-
5
- interface ReportSummaryRowProps {
6
- columns: ReportColumnDef[]
7
- data: Record<string, any>[]
8
- currency?: string
9
- }
10
-
11
- export function ReportSummaryRow({ columns, data, currency }: ReportSummaryRowProps) {
12
- const visibleCols = columns.filter((c) => c.visible !== false)
13
- const hasAggregates = visibleCols.some((c) => c.aggregate)
14
- if (!hasAggregates || data.length === 0) return null
15
-
16
- return (
17
- <div className="rounded-lg border bg-muted/30 overflow-hidden">
18
- <table className="w-full text-xs">
19
- <tbody>
20
- <tr className="font-semibold">
21
- {visibleCols.map((col) => {
22
- const value = computeAggregate(col, data)
23
- const alignRight = col.align === 'right' || col.type === 'currency' || col.type === 'number'
24
- return (
25
- <td
26
- key={col.key}
27
- className={`px-3 py-1.5 ${alignRight ? 'text-right' : ''}`}
28
- >
29
- {value != null
30
- ? formatValuePlain(value, col, currency)
31
- : col.aggregate
32
- ? '—'
33
- : ''}
34
- </td>
35
- )
36
- })}
37
- </tr>
38
- </tbody>
39
- </table>
40
- </div>
41
- )
42
- }
43
-
44
- function computeAggregate(col: ReportColumnDef, data: Record<string, any>[]): number | null {
45
- if (!col.aggregate) return null
46
-
47
- const values = data
48
- .map((row) => row[col.key])
49
- .filter((v) => v != null && !isNaN(Number(v)))
50
- .map(Number)
51
-
52
- if (values.length === 0) return null
53
-
54
- switch (col.aggregate) {
55
- case 'sum':
56
- return values.reduce((a, b) => a + b, 0)
57
- case 'avg':
58
- return values.reduce((a, b) => a + b, 0) / values.length
59
- case 'count':
60
- return values.length
61
- case 'min':
62
- return Math.min(...values)
63
- case 'max':
64
- return Math.max(...values)
65
- default:
66
- return null
67
- }
68
- }
package/src/data/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export type { ReportDataProvider, ReportResult } from './types'
2
- export { createSupabaseReportProvider } from './supabase'
3
- export type { SupabaseReportProviderConfig } from './supabase'
4
- export { createMockReportProvider } from './mock'
package/src/data/mock.ts DELETED
@@ -1,9 +0,0 @@
1
- import type { ReportDataProvider } from './types'
2
-
3
- export function createMockReportProvider(): ReportDataProvider {
4
- return {
5
- async fetch() {
6
- return { data: [], total: 0 }
7
- },
8
- }
9
- }