@fayz-ai/plugin-reports 0.1.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 (89) hide show
  1. package/dist/ReportsContext.d.ts +16 -0
  2. package/dist/ReportsContext.d.ts.map +1 -0
  3. package/dist/ReportsPage.d.ts +8 -0
  4. package/dist/ReportsPage.d.ts.map +1 -0
  5. package/dist/chunk-LODXZ2SC.js +100 -0
  6. package/dist/chunk-LODXZ2SC.js.map +1 -0
  7. package/dist/chunk-ZSELW4DA.cjs +107 -0
  8. package/dist/chunk-ZSELW4DA.cjs.map +1 -0
  9. package/dist/components/ExportDropdown.d.ts +12 -0
  10. package/dist/components/ExportDropdown.d.ts.map +1 -0
  11. package/dist/components/ReportCard.d.ts +9 -0
  12. package/dist/components/ReportCard.d.ts.map +1 -0
  13. package/dist/components/ReportFilterBar.d.ts +12 -0
  14. package/dist/components/ReportFilterBar.d.ts.map +1 -0
  15. package/dist/components/ReportPagination.d.ts +11 -0
  16. package/dist/components/ReportPagination.d.ts.map +1 -0
  17. package/dist/components/ReportSummaryRow.d.ts +10 -0
  18. package/dist/components/ReportSummaryRow.d.ts.map +1 -0
  19. package/dist/data/index.d.ts +5 -0
  20. package/dist/data/index.d.ts.map +1 -0
  21. package/dist/data/mock.d.ts +3 -0
  22. package/dist/data/mock.d.ts.map +1 -0
  23. package/dist/data/supabase.d.ts +8 -0
  24. package/dist/data/supabase.d.ts.map +1 -0
  25. package/dist/data/types.d.ts +9 -0
  26. package/dist/data/types.d.ts.map +1 -0
  27. package/dist/excel-2PDUZ55J.cjs +38 -0
  28. package/dist/excel-2PDUZ55J.cjs.map +1 -0
  29. package/dist/excel-QHUSM6IH.js +35 -0
  30. package/dist/excel-QHUSM6IH.js.map +1 -0
  31. package/dist/export/csv.d.ts +3 -0
  32. package/dist/export/csv.d.ts.map +1 -0
  33. package/dist/export/excel.d.ts +4 -0
  34. package/dist/export/excel.d.ts.map +1 -0
  35. package/dist/export/pdf.d.ts +7 -0
  36. package/dist/export/pdf.d.ts.map +1 -0
  37. package/dist/hooks/useReportData.d.ts +19 -0
  38. package/dist/hooks/useReportData.d.ts.map +1 -0
  39. package/dist/index.cjs +904 -0
  40. package/dist/index.cjs.map +1 -0
  41. package/dist/index.d.ts +9 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +878 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/lib/columns.d.ts +5 -0
  46. package/dist/lib/columns.d.ts.map +1 -0
  47. package/dist/lib/tenant.d.ts +3 -0
  48. package/dist/lib/tenant.d.ts.map +1 -0
  49. package/dist/locales/en.d.ts +2 -0
  50. package/dist/locales/en.d.ts.map +1 -0
  51. package/dist/locales/index.d.ts +2 -0
  52. package/dist/locales/index.d.ts.map +1 -0
  53. package/dist/locales/pt-BR.d.ts +2 -0
  54. package/dist/locales/pt-BR.d.ts.map +1 -0
  55. package/dist/pdf-GB4JYUYH.cjs +40 -0
  56. package/dist/pdf-GB4JYUYH.cjs.map +1 -0
  57. package/dist/pdf-HETKN26Y.js +37 -0
  58. package/dist/pdf-HETKN26Y.js.map +1 -0
  59. package/dist/types.d.ts +139 -0
  60. package/dist/types.d.ts.map +1 -0
  61. package/dist/views/ReportHub.d.ts +8 -0
  62. package/dist/views/ReportHub.d.ts.map +1 -0
  63. package/dist/views/ReportViewer.d.ts +9 -0
  64. package/dist/views/ReportViewer.d.ts.map +1 -0
  65. package/package.json +55 -0
  66. package/src/ReportsContext.tsx +33 -0
  67. package/src/ReportsPage.tsx +62 -0
  68. package/src/components/ExportDropdown.tsx +63 -0
  69. package/src/components/ReportCard.tsx +38 -0
  70. package/src/components/ReportFilterBar.tsx +132 -0
  71. package/src/components/ReportPagination.tsx +59 -0
  72. package/src/components/ReportSummaryRow.tsx +68 -0
  73. package/src/data/index.ts +4 -0
  74. package/src/data/mock.ts +9 -0
  75. package/src/data/supabase.ts +200 -0
  76. package/src/data/types.ts +10 -0
  77. package/src/export/csv.ts +21 -0
  78. package/src/export/excel.ts +42 -0
  79. package/src/export/pdf.ts +47 -0
  80. package/src/hooks/useReportData.ts +123 -0
  81. package/src/index.ts +140 -0
  82. package/src/lib/columns.ts +130 -0
  83. package/src/lib/tenant.ts +3 -0
  84. package/src/locales/en.ts +28 -0
  85. package/src/locales/index.ts +7 -0
  86. package/src/locales/pt-BR.ts +28 -0
  87. package/src/types.ts +157 -0
  88. package/src/views/ReportHub.tsx +97 -0
  89. package/src/views/ReportViewer.tsx +183 -0
@@ -0,0 +1,42 @@
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
+ }
@@ -0,0 +1,47 @@
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
+ }
@@ -0,0 +1,123 @@
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 ADDED
@@ -0,0 +1,140 @@
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
+ ReportFilterOption,
132
+ ReportsPluginOptions,
133
+ ReportsPluginLabels,
134
+ ResolvedReportsConfig,
135
+ } from './types'
136
+
137
+ export type { ReportDataProvider, ReportResult } from './data/types'
138
+ export { createSupabaseReportProvider } from './data/supabase'
139
+ export type { SupabaseReportProviderConfig } from './data/supabase'
140
+ export { createMockReportProvider } from './data/mock'
@@ -0,0 +1,130 @@
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
+ }
@@ -0,0 +1,3 @@
1
+ let t: string | undefined
2
+ export function setReportsTenantId(id: string | undefined){ t = id }
3
+ export function getReportsTenantId(): string | undefined { return t }
@@ -0,0 +1,28 @@
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
+ }
@@ -0,0 +1,7 @@
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
+ }
@@ -0,0 +1,28 @@
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
+ }