@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
@@ -1,200 +0,0 @@
1
- import { getSupabaseClientOptional } from '@fayz-ai/core'
2
- import type { ReportDef, ReportQueryState } from '../types'
3
- import type { ReportDataProvider, ReportResult } from './types'
4
-
5
- // ---------------------------------------------------------------------------
6
- // Case conversion
7
- // ---------------------------------------------------------------------------
8
-
9
- function camelToSnake(str: string): string {
10
- return str.replace(/[A-Z]/g, (c) => '_' + c.toLowerCase())
11
- }
12
-
13
- function snakeToCamel(str: string): string {
14
- return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase())
15
- }
16
-
17
- function mapRow(row: Record<string, unknown>, columnMap?: Record<string, string>): Record<string, any> {
18
- const result: Record<string, unknown> = {}
19
- for (const [key, value] of Object.entries(row)) {
20
- const camelKey = columnMap?.[key] ?? snakeToCamel(key)
21
- result[camelKey] = value
22
- }
23
- return result
24
- }
25
-
26
- // ---------------------------------------------------------------------------
27
- // Provider factory
28
- // ---------------------------------------------------------------------------
29
-
30
- export interface SupabaseReportProviderConfig {
31
- tenantId: string | (() => string | undefined)
32
- tenantIdColumn?: string
33
- schema?: string
34
- }
35
-
36
- export function createSupabaseReportProvider(config: SupabaseReportProviderConfig): ReportDataProvider {
37
- const schema = config.schema ?? 'public'
38
- const tenantIdCol = config.tenantIdColumn ?? 'tenant_id'
39
-
40
- function resolveTenantId(): string | undefined {
41
- return typeof config.tenantId === 'function' ? config.tenantId() : config.tenantId
42
- }
43
-
44
- function getClient() {
45
- const supabase = getSupabaseClientOptional() as any
46
- if (!supabase) throw new Error('Supabase client not available')
47
- return schema === 'public' ? supabase : supabase.schema(schema)
48
- }
49
-
50
- return {
51
- async fetch(reportDef: ReportDef, query: ReportQueryState): Promise<ReportResult> {
52
- const { dataSource } = reportDef
53
- const columnMap = dataSource.columnMap
54
-
55
- if (dataSource.kind === 'rpc') {
56
- return fetchViaRpc(reportDef, query, getClient, resolveTenantId, columnMap)
57
- }
58
-
59
- return fetchViaView(reportDef, query, getClient, resolveTenantId, tenantIdCol, columnMap)
60
- },
61
- }
62
- }
63
-
64
- // ---------------------------------------------------------------------------
65
- // View-based fetch
66
- // ---------------------------------------------------------------------------
67
-
68
- async function fetchViaView(
69
- reportDef: ReportDef,
70
- query: ReportQueryState,
71
- getClient: () => any,
72
- resolveTenantId: () => string | undefined,
73
- tenantIdCol: string,
74
- columnMap?: Record<string, string>,
75
- ): Promise<ReportResult> {
76
- const { dataSource } = reportDef
77
- let q = getClient().from(dataSource.name).select('*', { count: 'exact' })
78
-
79
- // Tenant scoping
80
- const tenantId = resolveTenantId()
81
- if (tenantId) {
82
- q = q.eq(tenantIdCol, tenantId)
83
- }
84
-
85
- // Date range
86
- const dateCol = dataSource.dateColumn ?? 'created_at'
87
- if (query.dateRange.from) {
88
- q = q.gte(dateCol, query.dateRange.from)
89
- }
90
- if (query.dateRange.to) {
91
- // For date-type columns, plain date string is sufficient.
92
- // For timestamptz columns, extend to end of day.
93
- const toValue = dateCol === 'created_at' || dateCol.endsWith('_at')
94
- ? query.dateRange.to + 'T23:59:59'
95
- : query.dateRange.to
96
- q = q.lte(dateCol, toValue)
97
- }
98
-
99
- // Custom filters
100
- for (const [key, value] of Object.entries(query.filters)) {
101
- if (value == null || value === '') continue
102
- const col = camelToSnake(key)
103
- if (Array.isArray(value)) {
104
- q = q.in(col, value)
105
- } else {
106
- q = q.eq(col, value)
107
- }
108
- }
109
-
110
- // Search
111
- if (query.search) {
112
- const searchableCols = reportDef.columns
113
- .filter((c) => c.type === 'text' || c.type === 'email')
114
- .map((c: any) => camelToSnake(c.key))
115
- if (searchableCols.length > 0) {
116
- const term = `%${query.search}%`
117
- const orClause = searchableCols.map((col) => `${col}.ilike.${term}`).join(',')
118
- q = q.or(orClause)
119
- }
120
- }
121
-
122
- // Sort
123
- const sortCol = query.sortBy
124
- ? camelToSnake(query.sortBy)
125
- : dataSource.defaultSort
126
- ? camelToSnake(dataSource.defaultSort)
127
- : dateCol
128
- const ascending = query.sortDir
129
- ? query.sortDir === 'asc'
130
- : dataSource.defaultSortDir
131
- ? dataSource.defaultSortDir === 'asc'
132
- : false
133
- q = q.order(sortCol, { ascending })
134
-
135
- // Pagination
136
- const from = (query.page - 1) * query.pageSize
137
- const to = from + query.pageSize - 1
138
- q = q.range(from, to)
139
-
140
- const { data, error, count } = await q
141
-
142
- if (error) throw error
143
-
144
- return {
145
- data: (data ?? []).map((row: any) => mapRow(row, columnMap)),
146
- total: count ?? 0,
147
- }
148
- }
149
-
150
- // ---------------------------------------------------------------------------
151
- // RPC-based fetch
152
- // ---------------------------------------------------------------------------
153
-
154
- async function fetchViaRpc(
155
- reportDef: ReportDef,
156
- query: ReportQueryState,
157
- getClient: () => any,
158
- resolveTenantId: () => string | undefined,
159
- columnMap?: Record<string, string>,
160
- ): Promise<ReportResult> {
161
- const { dataSource } = reportDef
162
- const tenantId = resolveTenantId()
163
-
164
- const params: Record<string, any> = {
165
- p_tenant_id: tenantId,
166
- p_from: query.dateRange.from,
167
- p_to: query.dateRange.to,
168
- p_page: query.page,
169
- p_page_size: query.pageSize,
170
- }
171
-
172
- // Custom filters
173
- for (const [key, value] of Object.entries(query.filters)) {
174
- if (value == null || value === '') continue
175
- params[`p_${camelToSnake(key)}`] = value
176
- }
177
-
178
- if (query.sortBy) {
179
- params.p_sort_by = camelToSnake(query.sortBy)
180
- params.p_sort_dir = query.sortDir ?? 'desc'
181
- }
182
-
183
- const { data, error } = await getClient().rpc(dataSource.name, params)
184
- if (error) throw error
185
-
186
- // RPC expected to return { data: [...], total: number }
187
- if (data && typeof data === 'object' && 'data' in data) {
188
- return {
189
- data: (data.data ?? []).map((row: any) => mapRow(row, columnMap)),
190
- total: data.total ?? 0,
191
- }
192
- }
193
-
194
- // Fallback: RPC returns a plain array
195
- const rows = Array.isArray(data) ? data : []
196
- return {
197
- data: rows.map((row: any) => mapRow(row, columnMap)),
198
- total: rows.length,
199
- }
200
- }
package/src/data/types.ts DELETED
@@ -1,10 +0,0 @@
1
- import type { ReportDef, ReportQueryState } from '../types'
2
-
3
- export interface ReportResult {
4
- data: Record<string, any>[]
5
- total: number
6
- }
7
-
8
- export interface ReportDataProvider {
9
- fetch(reportDef: ReportDef, query: ReportQueryState): Promise<ReportResult>
10
- }
package/src/export/csv.ts DELETED
@@ -1,21 +0,0 @@
1
- import { exportCSV } from '@fayz-ai/core'
2
- import type { CSVColumn } from '@fayz-ai/core'
3
- import type { ReportColumnDef } from '../types'
4
- import { formatValuePlain } from '../lib/columns'
5
-
6
- export function exportToCsv(
7
- columns: ReportColumnDef[],
8
- data: Record<string, any>[],
9
- filename: string,
10
- currency?: string,
11
- ): void {
12
- const visibleCols = columns.filter((c) => c.visible !== false)
13
-
14
- const csvColumns: CSVColumn[] = visibleCols.map((col) => ({
15
- key: col.key,
16
- label: col.label,
17
- format: (value: any) => formatValuePlain(value, col, currency),
18
- }))
19
-
20
- exportCSV(csvColumns, data, filename)
21
- }
@@ -1,42 +0,0 @@
1
- import type { ReportColumnDef } from '../types'
2
- import { formatValuePlain } from '../lib/columns'
3
-
4
- // Opaque dynamic import — hidden from Vite's static analysis so it won't
5
- // fail when xlsx is not installed (it's an optional peer dependency).
6
- const load = (m: string) => new Function('m', 'return import(m)')(m) as Promise<any>
7
-
8
- export async function exportToExcel(
9
- columns: ReportColumnDef[],
10
- data: Record<string, any>[],
11
- filename: string,
12
- currency?: string,
13
- ): Promise<void> {
14
- const XLSX = await load('xlsx')
15
- const visibleCols = columns.filter((c) => c.visible !== false)
16
-
17
- const header = visibleCols.map((c) => c.label)
18
- const rows = data.map((row) =>
19
- visibleCols.map((col) => {
20
- const value = row[col.key]
21
- if (col.type === 'number' || col.type === 'currency') {
22
- const num = typeof value === 'number' ? value : parseFloat(value)
23
- return isNaN(num) ? '' : num
24
- }
25
- return formatValuePlain(value, col, currency)
26
- }),
27
- )
28
-
29
- const ws = XLSX.utils.aoa_to_sheet([header, ...rows])
30
- const wb = XLSX.utils.book_new()
31
- XLSX.utils.book_append_sheet(wb, ws, 'Report')
32
- XLSX.writeFile(wb, `${filename}.xlsx`)
33
- }
34
-
35
- export async function isExcelAvailable(): Promise<boolean> {
36
- try {
37
- await load('xlsx')
38
- return true
39
- } catch {
40
- return false
41
- }
42
- }
package/src/export/pdf.ts DELETED
@@ -1,47 +0,0 @@
1
- import type { ReportColumnDef } from '../types'
2
- import { formatValuePlain } from '../lib/columns'
3
-
4
- // Opaque dynamic import — hidden from Vite's static analysis so it won't
5
- // fail when jspdf is not installed (it's an optional peer dependency).
6
- const load = (m: string) => new Function('m', 'return import(m)')(m) as Promise<any>
7
-
8
- export async function exportToPdf(
9
- columns: ReportColumnDef[],
10
- data: Record<string, any>[],
11
- filename: string,
12
- options?: { title?: string; currency?: string },
13
- ): Promise<void> {
14
- const { default: jsPDF } = await load('jspdf')
15
- await load('jspdf-autotable')
16
-
17
- const visibleCols = columns.filter((c) => c.visible !== false)
18
-
19
- const doc = new jsPDF({ orientation: visibleCols.length > 6 ? 'landscape' : 'portrait' })
20
-
21
- if (options?.title) {
22
- doc.setFontSize(14)
23
- doc.text(options.title, 14, 20)
24
- }
25
-
26
- ;(doc as any).autoTable({
27
- startY: options?.title ? 30 : 14,
28
- head: [visibleCols.map((c) => c.label)],
29
- body: data.map((row) =>
30
- visibleCols.map((col) => formatValuePlain(row[col.key], col, options?.currency)),
31
- ),
32
- styles: { fontSize: 8 },
33
- headStyles: { fillColor: [100, 100, 100] },
34
- })
35
-
36
- doc.save(`${filename}.pdf`)
37
- }
38
-
39
- export async function isPdfAvailable(): Promise<boolean> {
40
- try {
41
- await load('jspdf')
42
- await load('jspdf-autotable')
43
- return true
44
- } catch {
45
- return false
46
- }
47
- }
@@ -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'