@fayz-ai/plugin-reports 0.9.0 → 0.10.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.
Files changed (79) 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/data/aggregate.d.ts +14 -0
  5. package/dist/data/aggregate.d.ts.map +1 -0
  6. package/dist/data/analytics-mock.d.ts +3 -0
  7. package/dist/data/analytics-mock.d.ts.map +1 -0
  8. package/dist/data/analytics-supabase.d.ts +7 -0
  9. package/dist/data/analytics-supabase.d.ts.map +1 -0
  10. package/dist/{excel-QHUSM6IH.js → excel-WE73YE2V.js} +3 -3
  11. package/dist/{excel-QHUSM6IH.js.map → excel-WE73YE2V.js.map} +1 -1
  12. package/dist/index.d.ts +73 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1207 -654
  15. package/dist/index.js.map +1 -1
  16. package/dist/lib/lastViewed.d.ts +5 -0
  17. package/dist/lib/lastViewed.d.ts.map +1 -0
  18. package/dist/lib/legacy.d.ts +5 -0
  19. package/dist/lib/legacy.d.ts.map +1 -0
  20. package/dist/lib/tenant.d.ts +1 -0
  21. package/dist/lib/tenant.d.ts.map +1 -1
  22. package/dist/locales/en.d.ts.map +1 -1
  23. package/dist/locales/pt-BR.d.ts.map +1 -1
  24. package/dist/{pdf-HETKN26Y.js → pdf-E6DXHFXU.js} +3 -3
  25. package/dist/{pdf-HETKN26Y.js.map → pdf-E6DXHFXU.js.map} +1 -1
  26. package/dist/views/AnalyticsModule.d.ts +10 -0
  27. package/dist/views/AnalyticsModule.d.ts.map +1 -0
  28. package/dist/views/AnalyticsOverview.d.ts +3 -0
  29. package/dist/views/AnalyticsOverview.d.ts.map +1 -0
  30. package/dist/views/AnalyticsReportViewer.d.ts +5 -0
  31. package/dist/views/AnalyticsReportViewer.d.ts.map +1 -0
  32. package/dist/views/AnalyticsSurface.d.ts +97 -0
  33. package/dist/views/AnalyticsSurface.d.ts.map +1 -0
  34. package/dist/views/LiveViewPage.d.ts +3 -0
  35. package/dist/views/LiveViewPage.d.ts.map +1 -0
  36. package/dist/views/ReportsListPage.d.ts +3 -0
  37. package/dist/views/ReportsListPage.d.ts.map +1 -0
  38. package/package.json +5 -7
  39. package/dist/ReportsContext.d.ts +0 -16
  40. package/dist/ReportsContext.d.ts.map +0 -1
  41. package/dist/ReportsPage.d.ts +0 -8
  42. package/dist/ReportsPage.d.ts.map +0 -1
  43. package/dist/chunk-LODXZ2SC.js.map +0 -1
  44. package/dist/components/ReportCard.d.ts +0 -9
  45. package/dist/components/ReportCard.d.ts.map +0 -1
  46. package/dist/components/ReportFilterBar.d.ts +0 -12
  47. package/dist/components/ReportFilterBar.d.ts.map +0 -1
  48. package/dist/components/ReportSummaryRow.d.ts +0 -10
  49. package/dist/components/ReportSummaryRow.d.ts.map +0 -1
  50. package/dist/hooks/useReportData.d.ts +0 -19
  51. package/dist/hooks/useReportData.d.ts.map +0 -1
  52. package/dist/views/ReportHub.d.ts +0 -8
  53. package/dist/views/ReportHub.d.ts.map +0 -1
  54. package/dist/views/ReportViewer.d.ts +0 -9
  55. package/dist/views/ReportViewer.d.ts.map +0 -1
  56. package/src/ReportsContext.tsx +0 -33
  57. package/src/ReportsPage.tsx +0 -62
  58. package/src/components/ExportDropdown.tsx +0 -63
  59. package/src/components/ReportCard.tsx +0 -38
  60. package/src/components/ReportFilterBar.tsx +0 -132
  61. package/src/components/ReportPagination.tsx +0 -59
  62. package/src/components/ReportSummaryRow.tsx +0 -68
  63. package/src/data/index.ts +0 -4
  64. package/src/data/mock.ts +0 -9
  65. package/src/data/supabase.ts +0 -200
  66. package/src/data/types.ts +0 -10
  67. package/src/export/csv.ts +0 -21
  68. package/src/export/excel.ts +0 -42
  69. package/src/export/pdf.ts +0 -47
  70. package/src/hooks/useReportData.ts +0 -123
  71. package/src/index.ts +0 -142
  72. package/src/lib/columns.ts +0 -130
  73. package/src/lib/tenant.ts +0 -3
  74. package/src/locales/en.ts +0 -28
  75. package/src/locales/index.ts +0 -7
  76. package/src/locales/pt-BR.ts +0 -28
  77. package/src/types.ts +0 -171
  78. package/src/views/ReportHub.tsx +0 -97
  79. package/src/views/ReportViewer.tsx +0 -182
@@ -1,123 +0,0 @@
1
- import { useState, useCallback, useRef } from 'react'
2
- import type { ReportDef, ReportQueryState, ReportDateRange } from '../types'
3
- import type { ReportDataProvider, ReportResult } from '../data/types'
4
- import { dedup } from '@fayz-ai/saas'
5
-
6
- function defaultDateRange(): ReportDateRange {
7
- const now = new Date()
8
- const from = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-01`
9
- const to = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`
10
- return { from, to }
11
- }
12
-
13
- function buildDefaultFilters(report: ReportDef): Record<string, any> {
14
- const filters: Record<string, any> = {}
15
- for (const f of report.filters ?? []) {
16
- if (f.defaultValue !== undefined) filters[f.key] = f.defaultValue
17
- }
18
- return filters
19
- }
20
-
21
- export interface UseReportDataReturn {
22
- data: Record<string, any>[]
23
- total: number
24
- loading: boolean
25
- generated: boolean
26
- error: string | null
27
- query: ReportQueryState
28
- generate: () => void
29
- setDateRange: (range: ReportDateRange) => void
30
- setFilter: (key: string, value: any) => void
31
- setSort: (sortBy: string) => void
32
- setPage: (page: number) => void
33
- setPageSize: (size: number) => void
34
- setSearch: (search: string) => void
35
- }
36
-
37
- export function useReportData(
38
- report: ReportDef,
39
- provider: ReportDataProvider,
40
- defaultPageSize: number = 50,
41
- ): UseReportDataReturn {
42
- const [query, setQuery] = useState<ReportQueryState>({
43
- dateRange: defaultDateRange(),
44
- filters: buildDefaultFilters(report),
45
- sortBy: report.dataSource.defaultSort,
46
- sortDir: report.dataSource.defaultSortDir ?? 'desc',
47
- page: 1,
48
- pageSize: defaultPageSize,
49
- })
50
-
51
- const [result, setResult] = useState<ReportResult>({ data: [], total: 0 })
52
- const [loading, setLoading] = useState(false)
53
- const [generated, setGenerated] = useState(false)
54
- const [error, setError] = useState<string | null>(null)
55
- const queryRef = useRef(query)
56
- queryRef.current = query
57
-
58
- const fetchData = useCallback(async (q: ReportQueryState) => {
59
- setLoading(true)
60
- setError(null)
61
- try {
62
- const res = await dedup(`report:${report.id}:${JSON.stringify(q)}`, () =>
63
- provider.fetch(report, q),
64
- )
65
- setResult(res)
66
- setGenerated(true)
67
- } catch (err: any) {
68
- setError(err?.message ?? 'Failed to load report')
69
- setResult({ data: [], total: 0 })
70
- } finally {
71
- setLoading(false)
72
- }
73
- }, [report, provider])
74
-
75
- const generate = useCallback(() => {
76
- fetchData(queryRef.current)
77
- }, [fetchData])
78
-
79
- const setDateRange = useCallback((dateRange: ReportDateRange) => {
80
- setQuery((q) => ({ ...q, dateRange, page: 1 }))
81
- }, [])
82
-
83
- const setFilter = useCallback((key: string, value: any) => {
84
- setQuery((q) => ({ ...q, filters: { ...q.filters, [key]: value }, page: 1 }))
85
- }, [])
86
-
87
- const setSort = useCallback((sortBy: string) => {
88
- setQuery((q) => ({
89
- ...q,
90
- sortBy,
91
- sortDir: q.sortBy === sortBy && q.sortDir === 'asc' ? 'desc' : 'asc',
92
- page: 1,
93
- }))
94
- }, [])
95
-
96
- const setPage = useCallback((page: number) => {
97
- setQuery((q) => ({ ...q, page }))
98
- }, [])
99
-
100
- const setPageSize = useCallback((pageSize: number) => {
101
- setQuery((q) => ({ ...q, pageSize, page: 1 }))
102
- }, [])
103
-
104
- const setSearch = useCallback((search: string) => {
105
- setQuery((q) => ({ ...q, search, page: 1 }))
106
- }, [])
107
-
108
- return {
109
- data: result.data,
110
- total: result.total,
111
- loading,
112
- generated,
113
- error,
114
- query,
115
- generate,
116
- setDateRange,
117
- setFilter,
118
- setSort,
119
- setPage,
120
- setPageSize,
121
- setSearch,
122
- }
123
- }
package/src/index.ts DELETED
@@ -1,142 +0,0 @@
1
- import React from 'react'
2
- import type { PluginManifest, PluginScope, VerticalId } from '@fayz-ai/core'
3
- import { createSafeDataProvider, registerTranslations } from '@fayz-ai/core'
4
- import { ReportsPage } from './ReportsPage'
5
- import type { ReportDataProvider } from './data/types'
6
- import { createSupabaseReportProvider } from './data/supabase'
7
- import { createMockReportProvider } from './data/mock'
8
- import { getReportsTenantId } from './lib/tenant'
9
- import { reportsLocales } from './locales'
10
- import type {
11
- ReportsPluginOptions,
12
- ReportsPluginLabels,
13
- ResolvedReportsConfig,
14
- } from './types'
15
-
16
- // ---------------------------------------------------------------------------
17
- // Defaults
18
- // ---------------------------------------------------------------------------
19
-
20
- const DEFAULT_LABELS: ReportsPluginLabels = {
21
- pageTitle: 'Reports',
22
- pageSubtitle: 'Access complete reports for analysis and decision making',
23
- allReports: 'All Reports',
24
- dateRange: 'Date Range',
25
- from: 'From',
26
- to: 'To',
27
- export: 'Export',
28
- exportCsv: 'Export CSV',
29
- exportExcel: 'Export Excel',
30
- exportPdf: 'Export PDF',
31
- noResults: 'No results found.',
32
- backToReports: 'Back',
33
- reports: 'reports',
34
- search: 'Search reports...',
35
- today: 'Today',
36
- yesterday: 'Yesterday',
37
- last7Days: 'Last 7 days',
38
- thisMonth: 'This month',
39
- lastMonth: 'Last month',
40
- thisQuarter: 'This quarter',
41
- custom: 'Custom',
42
- generate: 'Generate',
43
- print: 'Print',
44
- filterFrom: 'From',
45
- filterTo: 'To',
46
- filterResults: 'Results',
47
- emptyStateTitle: 'Ready to generate',
48
- emptyStateDescription: 'Select a date range and filters, then click Generate to view the report.',
49
- unavailableTitle: 'Coming soon',
50
- unavailableDescription: 'This report is not available yet. It will be enabled in a future update.',
51
- }
52
-
53
- const DEFAULT_CURRENCY = { code: 'BRL', locale: 'pt-BR', symbol: 'R$' }
54
-
55
- // ---------------------------------------------------------------------------
56
- // Config resolver
57
- // ---------------------------------------------------------------------------
58
-
59
- function resolveConfig(options: ReportsPluginOptions): ResolvedReportsConfig {
60
- return {
61
- labels: { ...DEFAULT_LABELS, ...options.labels },
62
- currency: { ...DEFAULT_CURRENCY, ...options.currency },
63
- reports: options.reports,
64
- defaultPageSize: options.defaultPageSize ?? 50,
65
- showHeader: options.showHeader !== false,
66
- }
67
- }
68
-
69
- // ---------------------------------------------------------------------------
70
- // Factory
71
- // ---------------------------------------------------------------------------
72
-
73
- export function createReportsPlugin(options: ReportsPluginOptions): PluginManifest {
74
- const config = resolveConfig(options)
75
- registerTranslations(reportsLocales)
76
- const provider: ReportDataProvider = options.dataProvider ?? createSafeDataProvider(
77
- () => createSupabaseReportProvider({ tenantId: () => getReportsTenantId() }),
78
- () => createMockReportProvider(),
79
- )
80
-
81
- const PageComponent: React.FC<any> = () =>
82
- React.createElement(ReportsPage, { config, provider })
83
-
84
- return {
85
- id: 'reports',
86
- name: config.labels.pageTitle,
87
- icon: 'BarChart3',
88
- version: '1.0.0',
89
- scope: options.scope ?? 'universal',
90
- verticalId: options.verticalId,
91
- defaultEnabled: true,
92
- dependencies: [],
93
- declaredFeatures: [
94
- { id: 'reports', label: config.labels.pageTitle, group: config.labels.pageTitle },
95
- ],
96
- navigation: [
97
- {
98
- section: options.navSection ?? 'main',
99
- position: options.navPosition ?? 9,
100
- label: config.labels.pageTitle,
101
- route: '/reports',
102
- icon: 'BarChart3',
103
- permission: { feature: 'reports', action: 'read' as const },
104
- },
105
- ],
106
- routes: [
107
- {
108
- path: '/reports',
109
- component: PageComponent,
110
- permission: { feature: 'reports', action: 'read' as const },
111
- },
112
- ],
113
- widgets: [],
114
- registries: [],
115
- locales: reportsLocales,
116
- }
117
- }
118
-
119
- // ---------------------------------------------------------------------------
120
- // Re-exports
121
- // ---------------------------------------------------------------------------
122
-
123
- export type {
124
- ReportDef,
125
- ReportColumnDef,
126
- ReportFilterDef,
127
- ReportDateRange,
128
- ReportQueryState,
129
- ReportBadge,
130
- ReportDataSource,
131
- ReportGrain,
132
- ReportAllowedAction,
133
- ReportFilterOption,
134
- ReportsPluginOptions,
135
- ReportsPluginLabels,
136
- ResolvedReportsConfig,
137
- } from './types'
138
-
139
- export type { ReportDataProvider, ReportResult } from './data/types'
140
- export { createSupabaseReportProvider } from './data/supabase'
141
- export type { SupabaseReportProviderConfig } from './data/supabase'
142
- export { createMockReportProvider } from './data/mock'
@@ -1,130 +0,0 @@
1
- import React from 'react'
2
- import type { ColumnDef } from '@tanstack/react-table'
3
- import type { ReportColumnDef } from '../types'
4
- import { PersonLink } from '@fayz-ai/saas'
5
- import {
6
- formatCurrency as formatCurrencyLocale,
7
- formatDate as formatDateLocale,
8
- formatDateTime as formatDateTimeLocale,
9
- } from '@fayz-ai/core'
10
-
11
- // ---------------------------------------------------------------------------
12
- // Formatters (mirrors fieldToColumn.tsx logic)
13
- // ---------------------------------------------------------------------------
14
-
15
- function formatCurrency(value: any, currency?: string): string {
16
- const num = typeof value === 'number' ? value : parseFloat(value)
17
- if (isNaN(num)) return '—'
18
- return formatCurrencyLocale(num, currency)
19
- }
20
-
21
- function formatDate(value: any): string {
22
- if (!value) return '—'
23
- return formatDateLocale(value)
24
- }
25
-
26
- function formatDateTime(value: any): string {
27
- if (!value) return '—'
28
- return formatDateTimeLocale(value)
29
- }
30
-
31
- function formatNumber(value: any): string {
32
- if (value == null) return '—'
33
- return typeof value === 'number' ? value.toLocaleString() : String(value)
34
- }
35
-
36
- // ---------------------------------------------------------------------------
37
- // Plain-text formatter for exports
38
- // ---------------------------------------------------------------------------
39
-
40
- export function formatValuePlain(value: any, col: ReportColumnDef, currency?: string): string {
41
- if (value == null || value === '') return ''
42
- switch (col.type) {
43
- case 'currency':
44
- return formatCurrency(value, col.currency ?? currency)
45
- case 'date':
46
- return formatDate(value)
47
- case 'datetime':
48
- return formatDateTime(value)
49
- case 'number':
50
- return formatNumber(value)
51
- case 'boolean':
52
- return value ? 'Yes' : 'No'
53
- default:
54
- return String(value)
55
- }
56
- }
57
-
58
- // ---------------------------------------------------------------------------
59
- // ReportColumnDef[] → TanStack ColumnDef[]
60
- // ---------------------------------------------------------------------------
61
-
62
- export function reportColumnsToTanstack(
63
- columns: ReportColumnDef[],
64
- currency?: string,
65
- ): ColumnDef<Record<string, any>>[] {
66
- return columns
67
- .filter((c) => c.visible !== false)
68
- .map((col): ColumnDef<Record<string, any>> => {
69
- const sortable = col.sortable ?? ['text', 'number', 'currency', 'date', 'datetime', 'email'].includes(col.type)
70
- const alignRight = col.align === 'right' || (!col.align && (col.type === 'currency' || col.type === 'number'))
71
- const alignCenter = col.align === 'center'
72
-
73
- return {
74
- accessorKey: col.key,
75
- header: col.label,
76
- enableSorting: sortable,
77
- cell: (ctx) => {
78
- const value = ctx.getValue()
79
- const row = ctx.row.original
80
-
81
- if (col.renderCell) {
82
- return col.renderCell(value, row)
83
- }
84
-
85
- if (value == null || value === '') {
86
- return React.createElement('span', { className: 'text-muted-foreground' }, '—')
87
- }
88
-
89
- let content: React.ReactNode
90
- switch (col.type) {
91
- case 'person':
92
- content = React.createElement(PersonLink, {
93
- personId: col.idKey ? row[col.idKey] : null,
94
- name: String(value),
95
- size: 'sm',
96
- className: 'text-xs',
97
- })
98
- break
99
- case 'currency':
100
- content = formatCurrency(value, col.currency ?? currency)
101
- break
102
- case 'date':
103
- content = formatDate(value)
104
- break
105
- case 'datetime':
106
- content = formatDateTime(value)
107
- break
108
- case 'boolean':
109
- content = value
110
- ? React.createElement('svg', { className: 'h-4 w-4 text-success', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2' },
111
- React.createElement('polyline', { points: '20 6 9 17 4 12' }),
112
- )
113
- : React.createElement('span', { className: 'text-muted-foreground' }, '—')
114
- break
115
- case 'number':
116
- content = formatNumber(value)
117
- break
118
- default:
119
- content = String(value)
120
- }
121
-
122
- const cls = alignRight ? 'text-right' : alignCenter ? 'text-center' : undefined
123
- return cls
124
- ? React.createElement('div', { className: cls }, content)
125
- : React.createElement(React.Fragment, null, content)
126
- },
127
- meta: { align: alignRight ? 'right' : alignCenter ? 'center' : 'left' },
128
- }
129
- })
130
- }
package/src/lib/tenant.ts DELETED
@@ -1,3 +0,0 @@
1
- let t: string | undefined
2
- export function setReportsTenantId(id: string | undefined){ t = id }
3
- export function getReportsTenantId(): string | undefined { return t }
package/src/locales/en.ts DELETED
@@ -1,28 +0,0 @@
1
- export const en: Record<string, string> = {
2
- 'reports.pageTitle': 'Reports',
3
- 'reports.pageSubtitle': 'Access complete reports for analysis and decision making',
4
- 'reports.allReports': 'All Reports',
5
- 'reports.dateRange': 'Date Range',
6
- 'reports.from': 'From',
7
- 'reports.to': 'To',
8
- 'reports.export': 'Export',
9
- 'reports.exportCsv': 'Export CSV',
10
- 'reports.exportExcel': 'Export Excel',
11
- 'reports.exportPdf': 'Export PDF',
12
- 'reports.noResults': 'No results found.',
13
- 'reports.backToReports': 'Back',
14
- 'reports.reports': 'reports',
15
- 'reports.search': 'Search reports...',
16
- 'reports.today': 'Today',
17
- 'reports.yesterday': 'Yesterday',
18
- 'reports.last7Days': 'Last 7 days',
19
- 'reports.thisMonth': 'This month',
20
- 'reports.lastMonth': 'Last month',
21
- 'reports.thisQuarter': 'This quarter',
22
- 'reports.custom': 'Custom',
23
- 'reports.generate': 'Generate',
24
- 'reports.emptyStateTitle': 'Ready to generate',
25
- 'reports.emptyStateDescription': 'Select a date range and filters, then click Generate to view the report.',
26
- 'reports.unavailableTitle': 'Coming soon',
27
- 'reports.unavailableDescription': 'This report is not available yet. It will be enabled in a future update.',
28
- }
@@ -1,7 +0,0 @@
1
- import { en } from './en'
2
- import { ptBR } from './pt-BR'
3
-
4
- export const reportsLocales: Record<string, Record<string, string>> = {
5
- en,
6
- 'pt-BR': ptBR,
7
- }
@@ -1,28 +0,0 @@
1
- export const ptBR: Record<string, string> = {
2
- 'reports.pageTitle': 'Relatórios',
3
- 'reports.pageSubtitle': 'Acesse relatórios completos para análise e tomada de decisão',
4
- 'reports.allReports': 'Todos os Relatórios',
5
- 'reports.dateRange': 'Período',
6
- 'reports.from': 'De',
7
- 'reports.to': 'Até',
8
- 'reports.export': 'Exportar',
9
- 'reports.exportCsv': 'Exportar CSV',
10
- 'reports.exportExcel': 'Exportar Excel',
11
- 'reports.exportPdf': 'Exportar PDF',
12
- 'reports.noResults': 'Nenhum resultado encontrado.',
13
- 'reports.backToReports': 'Voltar',
14
- 'reports.reports': 'relatórios',
15
- 'reports.search': 'Buscar relatórios...',
16
- 'reports.today': 'Hoje',
17
- 'reports.yesterday': 'Ontem',
18
- 'reports.last7Days': 'Últimos 7 dias',
19
- 'reports.thisMonth': 'Este mês',
20
- 'reports.lastMonth': 'Mês passado',
21
- 'reports.thisQuarter': 'Este trimestre',
22
- 'reports.custom': 'Personalizado',
23
- 'reports.generate': 'Gerar',
24
- 'reports.emptyStateTitle': 'Pronto para gerar',
25
- 'reports.emptyStateDescription': 'Selecione o período e os filtros desejados, depois clique em Gerar para visualizar o relatório.',
26
- 'reports.unavailableTitle': 'Em breve',
27
- 'reports.unavailableDescription': 'Este relatório ainda não está disponível. Será habilitado em uma atualização futura.',
28
- }
package/src/types.ts DELETED
@@ -1,171 +0,0 @@
1
- import type React from 'react'
2
- import type { FieldType } from '@fayz-ai/core'
3
-
4
- // ---------------------------------------------------------------------------
5
- // Report column
6
- // ---------------------------------------------------------------------------
7
-
8
- export type ReportFieldType = FieldType | 'person'
9
-
10
- export interface ReportColumnDef {
11
- key: string
12
- label: string
13
- type: ReportFieldType
14
- sortable?: boolean
15
- currency?: string
16
- /** For type 'person': the row key containing the person UUID */
17
- idKey?: string
18
- renderCell?: (value: any, row: any) => React.ReactNode
19
- /** Include in export but hide in table. Default: true */
20
- visible?: boolean
21
- /** Text alignment. Currency/number default to 'right' */
22
- align?: 'left' | 'center' | 'right'
23
- /** Aggregate for optional summary row */
24
- aggregate?: 'sum' | 'avg' | 'count' | 'min' | 'max'
25
- }
26
-
27
- // ---------------------------------------------------------------------------
28
- // Report filter
29
- // ---------------------------------------------------------------------------
30
-
31
- export type ReportFilterOption = { label: string; value: string }
32
-
33
- export interface ReportFilterDef {
34
- key: string
35
- label: string
36
- type: 'select' | 'multiselect' | 'text' | 'boolean' | 'number'
37
- options?: ReportFilterOption[] | (() => Promise<ReportFilterOption[]>)
38
- defaultValue?: any
39
- placeholder?: string
40
- }
41
-
42
- // ---------------------------------------------------------------------------
43
- // Report definition
44
- // ---------------------------------------------------------------------------
45
-
46
- export type ReportBadge = 'popular' | 'essential' | 'new' | (string & {})
47
-
48
- export type ReportDataSourceKind = 'view' | 'rpc'
49
-
50
- export interface ReportDataSource {
51
- kind: ReportDataSourceKind
52
- /** View name (e.g. 'rep_appointments_by_period') or RPC function name */
53
- name: string
54
- /** Column for date range filtering. Default: 'created_at' */
55
- dateColumn?: string
56
- defaultSort?: string
57
- defaultSortDir?: 'asc' | 'desc'
58
- /** snake_case DB → camelCase JS column mapping */
59
- columnMap?: Record<string, string>
60
- }
61
-
62
- export type ReportGrain = 'order' | 'order_item' | 'booking' | 'ledger_movement' | 'event'
63
-
64
- export type ReportAllowedAction = 'read'
65
-
66
- export interface ReportDef {
67
- id: string
68
- name: string
69
- description?: string
70
- icon: string
71
- category: string
72
- badge?: ReportBadge
73
- columns: ReportColumnDef[]
74
- filters?: ReportFilterDef[]
75
- dataSource: ReportDataSource
76
- /** Canonical read model/view used by the report. Defaults to dataSource.name. */
77
- sourceView?: string
78
- /** Business grain of each report row, used to prevent double counting. */
79
- grain?: ReportGrain
80
- /** Plugin that owns the report UI/definition. */
81
- ownerPlugin?: string
82
- /** Canonical data owner/spine for this report's facts. */
83
- canonicalOwner?: string
84
- /** Reports are normally read-only; explicit actions make that contract visible. */
85
- allowedActions?: ReportAllowedAction[]
86
- permission?: { feature: string; action: 'read' }
87
- /** Show aggregate summary row */
88
- showSummary?: boolean
89
- /** Whether the backing view/RPC exists. Default: true */
90
- available?: boolean
91
- }
92
-
93
- // ---------------------------------------------------------------------------
94
- // Query state
95
- // ---------------------------------------------------------------------------
96
-
97
- export interface ReportDateRange {
98
- from: string // YYYY-MM-DD
99
- to: string // YYYY-MM-DD
100
- }
101
-
102
- export interface ReportQueryState {
103
- dateRange: ReportDateRange
104
- filters: Record<string, any>
105
- sortBy?: string
106
- sortDir?: 'asc' | 'desc'
107
- page: number
108
- pageSize: number
109
- search?: string
110
- }
111
-
112
- // ---------------------------------------------------------------------------
113
- // Plugin options
114
- // ---------------------------------------------------------------------------
115
-
116
- export interface ReportsPluginLabels {
117
- pageTitle: string
118
- pageSubtitle: string
119
- allReports: string
120
- dateRange: string
121
- from: string
122
- to: string
123
- export: string
124
- exportCsv: string
125
- exportExcel: string
126
- exportPdf: string
127
- noResults: string
128
- backToReports: string
129
- reports: string
130
- search: string
131
- today: string
132
- yesterday: string
133
- last7Days: string
134
- thisMonth: string
135
- lastMonth: string
136
- thisQuarter: string
137
- custom: string
138
- generate: string
139
- print: string
140
- filterFrom: string
141
- filterTo: string
142
- filterResults: string
143
- emptyStateTitle: string
144
- emptyStateDescription: string
145
- unavailableTitle: string
146
- unavailableDescription: string
147
- }
148
-
149
- export interface ReportsPluginOptions {
150
- reports: ReportDef[]
151
- labels?: Partial<ReportsPluginLabels>
152
- currency?: { code?: string; locale?: string; symbol?: string }
153
- dataProvider?: import('./data/types').ReportDataProvider
154
- navPosition?: number
155
- navSection?: 'main' | 'secondary'
156
- scope?: import('@fayz-ai/core').PluginScope
157
- verticalId?: import('@fayz-ai/core').VerticalId
158
- /** Default page size. Default: 50 */
159
- defaultPageSize?: number
160
- /** Render the in-content title/subtitle. Set false when the app shell owns the
161
- * page title (sidebar/GHL-style layouts). Default: true. */
162
- showHeader?: boolean
163
- }
164
-
165
- export interface ResolvedReportsConfig {
166
- labels: ReportsPluginLabels
167
- currency: { code: string; locale: string; symbol: string }
168
- reports: ReportDef[]
169
- defaultPageSize: number
170
- showHeader: boolean
171
- }