@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.
- package/dist/ReportsContext.d.ts +16 -0
- package/dist/ReportsContext.d.ts.map +1 -0
- package/dist/ReportsPage.d.ts +8 -0
- package/dist/ReportsPage.d.ts.map +1 -0
- package/dist/chunk-LODXZ2SC.js +100 -0
- package/dist/chunk-LODXZ2SC.js.map +1 -0
- package/dist/chunk-ZSELW4DA.cjs +107 -0
- package/dist/chunk-ZSELW4DA.cjs.map +1 -0
- package/dist/components/ExportDropdown.d.ts +12 -0
- package/dist/components/ExportDropdown.d.ts.map +1 -0
- package/dist/components/ReportCard.d.ts +9 -0
- package/dist/components/ReportCard.d.ts.map +1 -0
- package/dist/components/ReportFilterBar.d.ts +12 -0
- package/dist/components/ReportFilterBar.d.ts.map +1 -0
- package/dist/components/ReportPagination.d.ts +11 -0
- package/dist/components/ReportPagination.d.ts.map +1 -0
- package/dist/components/ReportSummaryRow.d.ts +10 -0
- package/dist/components/ReportSummaryRow.d.ts.map +1 -0
- package/dist/data/index.d.ts +5 -0
- package/dist/data/index.d.ts.map +1 -0
- package/dist/data/mock.d.ts +3 -0
- package/dist/data/mock.d.ts.map +1 -0
- package/dist/data/supabase.d.ts +8 -0
- package/dist/data/supabase.d.ts.map +1 -0
- package/dist/data/types.d.ts +9 -0
- package/dist/data/types.d.ts.map +1 -0
- package/dist/excel-2PDUZ55J.cjs +38 -0
- package/dist/excel-2PDUZ55J.cjs.map +1 -0
- package/dist/excel-QHUSM6IH.js +35 -0
- package/dist/excel-QHUSM6IH.js.map +1 -0
- package/dist/export/csv.d.ts +3 -0
- package/dist/export/csv.d.ts.map +1 -0
- package/dist/export/excel.d.ts +4 -0
- package/dist/export/excel.d.ts.map +1 -0
- package/dist/export/pdf.d.ts +7 -0
- package/dist/export/pdf.d.ts.map +1 -0
- package/dist/hooks/useReportData.d.ts +19 -0
- package/dist/hooks/useReportData.d.ts.map +1 -0
- package/dist/index.cjs +904 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +878 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/columns.d.ts +5 -0
- package/dist/lib/columns.d.ts.map +1 -0
- package/dist/lib/tenant.d.ts +3 -0
- package/dist/lib/tenant.d.ts.map +1 -0
- package/dist/locales/en.d.ts +2 -0
- package/dist/locales/en.d.ts.map +1 -0
- package/dist/locales/index.d.ts +2 -0
- package/dist/locales/index.d.ts.map +1 -0
- package/dist/locales/pt-BR.d.ts +2 -0
- package/dist/locales/pt-BR.d.ts.map +1 -0
- package/dist/pdf-GB4JYUYH.cjs +40 -0
- package/dist/pdf-GB4JYUYH.cjs.map +1 -0
- package/dist/pdf-HETKN26Y.js +37 -0
- package/dist/pdf-HETKN26Y.js.map +1 -0
- package/dist/types.d.ts +139 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/views/ReportHub.d.ts +8 -0
- package/dist/views/ReportHub.d.ts.map +1 -0
- package/dist/views/ReportViewer.d.ts +9 -0
- package/dist/views/ReportViewer.d.ts.map +1 -0
- package/package.json +55 -0
- package/src/ReportsContext.tsx +33 -0
- package/src/ReportsPage.tsx +62 -0
- package/src/components/ExportDropdown.tsx +63 -0
- package/src/components/ReportCard.tsx +38 -0
- package/src/components/ReportFilterBar.tsx +132 -0
- package/src/components/ReportPagination.tsx +59 -0
- package/src/components/ReportSummaryRow.tsx +68 -0
- package/src/data/index.ts +4 -0
- package/src/data/mock.ts +9 -0
- package/src/data/supabase.ts +200 -0
- package/src/data/types.ts +10 -0
- package/src/export/csv.ts +21 -0
- package/src/export/excel.ts +42 -0
- package/src/export/pdf.ts +47 -0
- package/src/hooks/useReportData.ts +123 -0
- package/src/index.ts +140 -0
- package/src/lib/columns.ts +130 -0
- package/src/lib/tenant.ts +3 -0
- package/src/locales/en.ts +28 -0
- package/src/locales/index.ts +7 -0
- package/src/locales/pt-BR.ts +28 -0
- package/src/types.ts +157 -0
- package/src/views/ReportHub.tsx +97 -0
- package/src/views/ReportViewer.tsx +183 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,878 @@
|
|
|
1
|
+
import { reportColumnsToTanstack, formatValuePlain } from './chunk-LODXZ2SC.js';
|
|
2
|
+
import React7, { createContext, useState, useEffect, useCallback, useMemo, useContext, useRef } from 'react';
|
|
3
|
+
import { registerTranslations, createSafeDataProvider, getSupabaseClientOptional, useTranslation, exportCSV } from '@fayz-ai/core';
|
|
4
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
5
|
+
import * as LucideIcons from 'lucide-react';
|
|
6
|
+
import { ArrowLeft, Loader2, Play, Printer, SlidersHorizontal, Construction, Search, Download, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
7
|
+
import { Button, DataTable, Input, DatePicker, Dropdown, DropdownTrigger, DropdownContent, DropdownItem, Badge, Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@fayz-ai/ui';
|
|
8
|
+
import { dedup } from '@fayz-ai/saas';
|
|
9
|
+
|
|
10
|
+
var ReportsContext = createContext(null);
|
|
11
|
+
function useReportsContext() {
|
|
12
|
+
const ctx = useContext(ReportsContext);
|
|
13
|
+
if (!ctx) throw new Error("useReportsContext must be used within ReportsContextProvider");
|
|
14
|
+
return ctx;
|
|
15
|
+
}
|
|
16
|
+
function ReportsContextProvider({
|
|
17
|
+
config,
|
|
18
|
+
provider,
|
|
19
|
+
children
|
|
20
|
+
}) {
|
|
21
|
+
return /* @__PURE__ */ jsx(ReportsContext.Provider, { value: { config, provider, reports: config.reports }, children });
|
|
22
|
+
}
|
|
23
|
+
function ReportCard({ report, onClick }) {
|
|
24
|
+
const IconComp = LucideIcons[report.icon] ?? LucideIcons.FileText;
|
|
25
|
+
return /* @__PURE__ */ jsxs(
|
|
26
|
+
"button",
|
|
27
|
+
{
|
|
28
|
+
type: "button",
|
|
29
|
+
onClick: () => onClick(report),
|
|
30
|
+
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",
|
|
31
|
+
children: [
|
|
32
|
+
/* @__PURE__ */ jsxs("div", { className: "flex w-full items-start justify-between", children: [
|
|
33
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-lg bg-primary/10 text-primary", children: /* @__PURE__ */ jsx(IconComp, { className: "h-5 w-5" }) }),
|
|
34
|
+
report.badge && /* @__PURE__ */ jsx(Badge, { variant: "secondary", className: "text-xs capitalize", children: report.badge })
|
|
35
|
+
] }),
|
|
36
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
37
|
+
/* @__PURE__ */ jsx("h4", { className: "font-medium text-sm", children: report.name }),
|
|
38
|
+
report.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-2", children: report.description })
|
|
39
|
+
] })
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
function ReportHub({ onSelect }) {
|
|
45
|
+
const t2 = useTranslation();
|
|
46
|
+
const { config } = useReportsContext();
|
|
47
|
+
const [search, setSearch] = useState("");
|
|
48
|
+
const categories = useMemo(() => {
|
|
49
|
+
const filtered = search ? config.reports.filter(
|
|
50
|
+
(r) => r.name.toLowerCase().includes(search.toLowerCase()) || r.description?.toLowerCase().includes(search.toLowerCase())
|
|
51
|
+
) : config.reports;
|
|
52
|
+
const map = /* @__PURE__ */ new Map();
|
|
53
|
+
for (const report of filtered) {
|
|
54
|
+
const list = map.get(report.category) ?? [];
|
|
55
|
+
list.push(report);
|
|
56
|
+
map.set(report.category, list);
|
|
57
|
+
}
|
|
58
|
+
const groups = [];
|
|
59
|
+
for (const [name, reports] of map) {
|
|
60
|
+
groups.push({ name, reports });
|
|
61
|
+
}
|
|
62
|
+
return groups;
|
|
63
|
+
}, [config.reports, search]);
|
|
64
|
+
const totalReports = config.reports.length;
|
|
65
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
|
|
66
|
+
config.showHeader && /* @__PURE__ */ jsxs("div", { children: [
|
|
67
|
+
/* @__PURE__ */ jsx("h1", { className: "text-2xl font-bold", children: t2("reports.pageTitle") }),
|
|
68
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground mt-1", children: t2("reports.pageSubtitle") })
|
|
69
|
+
] }),
|
|
70
|
+
totalReports > 6 && /* @__PURE__ */ jsxs("div", { className: "relative max-w-md", children: [
|
|
71
|
+
/* @__PURE__ */ jsx(Search, { className: "absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" }),
|
|
72
|
+
/* @__PURE__ */ jsx(
|
|
73
|
+
Input,
|
|
74
|
+
{
|
|
75
|
+
value: search,
|
|
76
|
+
onChange: (e) => setSearch(e.target.value),
|
|
77
|
+
placeholder: t2("reports.search"),
|
|
78
|
+
className: "pl-9"
|
|
79
|
+
}
|
|
80
|
+
)
|
|
81
|
+
] }),
|
|
82
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-8", children: categories.map((category) => /* @__PURE__ */ jsxs("div", { className: "rounded-xl border bg-card shadow-sm p-5", children: [
|
|
83
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between mb-4", children: [
|
|
84
|
+
/* @__PURE__ */ jsx("h2", { className: "font-semibold", children: category.name }),
|
|
85
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
86
|
+
category.reports.length,
|
|
87
|
+
" ",
|
|
88
|
+
t2("reports.reports")
|
|
89
|
+
] })
|
|
90
|
+
] }),
|
|
91
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3", children: category.reports.map((report) => /* @__PURE__ */ jsx(ReportCard, { report, onClick: onSelect }, report.id)) })
|
|
92
|
+
] }, category.name)) }),
|
|
93
|
+
categories.length === 0 && search && /* @__PURE__ */ jsx("div", { className: "text-center py-12 text-muted-foreground", children: t2("reports.noResults") })
|
|
94
|
+
] });
|
|
95
|
+
}
|
|
96
|
+
function defaultDateRange() {
|
|
97
|
+
const now = /* @__PURE__ */ new Date();
|
|
98
|
+
const from = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-01`;
|
|
99
|
+
const to = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
100
|
+
return { from, to };
|
|
101
|
+
}
|
|
102
|
+
function buildDefaultFilters(report) {
|
|
103
|
+
const filters = {};
|
|
104
|
+
for (const f of report.filters ?? []) {
|
|
105
|
+
if (f.defaultValue !== void 0) filters[f.key] = f.defaultValue;
|
|
106
|
+
}
|
|
107
|
+
return filters;
|
|
108
|
+
}
|
|
109
|
+
function useReportData(report, provider, defaultPageSize = 50) {
|
|
110
|
+
const [query, setQuery] = useState({
|
|
111
|
+
dateRange: defaultDateRange(),
|
|
112
|
+
filters: buildDefaultFilters(report),
|
|
113
|
+
sortBy: report.dataSource.defaultSort,
|
|
114
|
+
sortDir: report.dataSource.defaultSortDir ?? "desc",
|
|
115
|
+
page: 1,
|
|
116
|
+
pageSize: defaultPageSize
|
|
117
|
+
});
|
|
118
|
+
const [result, setResult] = useState({ data: [], total: 0 });
|
|
119
|
+
const [loading, setLoading] = useState(false);
|
|
120
|
+
const [generated, setGenerated] = useState(false);
|
|
121
|
+
const [error, setError] = useState(null);
|
|
122
|
+
const queryRef = useRef(query);
|
|
123
|
+
queryRef.current = query;
|
|
124
|
+
const fetchData = useCallback(async (q) => {
|
|
125
|
+
setLoading(true);
|
|
126
|
+
setError(null);
|
|
127
|
+
try {
|
|
128
|
+
const res = await dedup(
|
|
129
|
+
`report:${report.id}:${JSON.stringify(q)}`,
|
|
130
|
+
() => provider.fetch(report, q)
|
|
131
|
+
);
|
|
132
|
+
setResult(res);
|
|
133
|
+
setGenerated(true);
|
|
134
|
+
} catch (err) {
|
|
135
|
+
setError(err?.message ?? "Failed to load report");
|
|
136
|
+
setResult({ data: [], total: 0 });
|
|
137
|
+
} finally {
|
|
138
|
+
setLoading(false);
|
|
139
|
+
}
|
|
140
|
+
}, [report, provider]);
|
|
141
|
+
const generate = useCallback(() => {
|
|
142
|
+
fetchData(queryRef.current);
|
|
143
|
+
}, [fetchData]);
|
|
144
|
+
const setDateRange = useCallback((dateRange) => {
|
|
145
|
+
setQuery((q) => ({ ...q, dateRange, page: 1 }));
|
|
146
|
+
}, []);
|
|
147
|
+
const setFilter = useCallback((key, value) => {
|
|
148
|
+
setQuery((q) => ({ ...q, filters: { ...q.filters, [key]: value }, page: 1 }));
|
|
149
|
+
}, []);
|
|
150
|
+
const setSort = useCallback((sortBy) => {
|
|
151
|
+
setQuery((q) => ({
|
|
152
|
+
...q,
|
|
153
|
+
sortBy,
|
|
154
|
+
sortDir: q.sortBy === sortBy && q.sortDir === "asc" ? "desc" : "asc",
|
|
155
|
+
page: 1
|
|
156
|
+
}));
|
|
157
|
+
}, []);
|
|
158
|
+
const setPage = useCallback((page) => {
|
|
159
|
+
setQuery((q) => ({ ...q, page }));
|
|
160
|
+
}, []);
|
|
161
|
+
const setPageSize = useCallback((pageSize) => {
|
|
162
|
+
setQuery((q) => ({ ...q, pageSize, page: 1 }));
|
|
163
|
+
}, []);
|
|
164
|
+
const setSearch = useCallback((search) => {
|
|
165
|
+
setQuery((q) => ({ ...q, search, page: 1 }));
|
|
166
|
+
}, []);
|
|
167
|
+
return {
|
|
168
|
+
data: result.data,
|
|
169
|
+
total: result.total,
|
|
170
|
+
loading,
|
|
171
|
+
generated,
|
|
172
|
+
error,
|
|
173
|
+
query,
|
|
174
|
+
generate,
|
|
175
|
+
setDateRange,
|
|
176
|
+
setFilter,
|
|
177
|
+
setSort,
|
|
178
|
+
setPage,
|
|
179
|
+
setPageSize,
|
|
180
|
+
setSearch
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function ReportFilterBar({
|
|
184
|
+
dateRange,
|
|
185
|
+
onDateRangeChange,
|
|
186
|
+
filters,
|
|
187
|
+
filterValues,
|
|
188
|
+
onFilterChange
|
|
189
|
+
}) {
|
|
190
|
+
const t2 = useTranslation();
|
|
191
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [
|
|
192
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-end gap-2", children: [
|
|
193
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
194
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t2("reports.from") }),
|
|
195
|
+
/* @__PURE__ */ jsx(
|
|
196
|
+
DatePicker,
|
|
197
|
+
{
|
|
198
|
+
value: dateRange.from,
|
|
199
|
+
onChange: (v) => onDateRangeChange({ ...dateRange, from: v })
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
] }),
|
|
203
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
204
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t2("reports.to") }),
|
|
205
|
+
/* @__PURE__ */ jsx(
|
|
206
|
+
DatePicker,
|
|
207
|
+
{
|
|
208
|
+
value: dateRange.to,
|
|
209
|
+
onChange: (v) => onDateRangeChange({ ...dateRange, to: v })
|
|
210
|
+
}
|
|
211
|
+
)
|
|
212
|
+
] })
|
|
213
|
+
] }),
|
|
214
|
+
filters.map((filter) => /* @__PURE__ */ jsx(
|
|
215
|
+
FilterControl,
|
|
216
|
+
{
|
|
217
|
+
filter,
|
|
218
|
+
value: filterValues[filter.key],
|
|
219
|
+
onChange: (v) => onFilterChange(filter.key, v)
|
|
220
|
+
},
|
|
221
|
+
filter.key
|
|
222
|
+
))
|
|
223
|
+
] });
|
|
224
|
+
}
|
|
225
|
+
function FilterControl({
|
|
226
|
+
filter,
|
|
227
|
+
value,
|
|
228
|
+
onChange
|
|
229
|
+
}) {
|
|
230
|
+
const [options, setOptions] = useState(
|
|
231
|
+
Array.isArray(filter.options) ? filter.options : []
|
|
232
|
+
);
|
|
233
|
+
useEffect(() => {
|
|
234
|
+
if (typeof filter.options === "function") {
|
|
235
|
+
filter.options().then(setOptions);
|
|
236
|
+
}
|
|
237
|
+
}, [filter.options]);
|
|
238
|
+
if (filter.type === "select") {
|
|
239
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
240
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: filter.label }),
|
|
241
|
+
/* @__PURE__ */ jsxs(Select, { value: value ?? "", onValueChange: (v) => onChange(v === "__all__" ? "" : v), children: [
|
|
242
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "w-[180px]", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: filter.placeholder ?? filter.label }) }),
|
|
243
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
244
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "__all__", children: "All" }),
|
|
245
|
+
options.map((opt) => /* @__PURE__ */ jsx(SelectItem, { value: opt.value, children: opt.label }, opt.value))
|
|
246
|
+
] })
|
|
247
|
+
] })
|
|
248
|
+
] });
|
|
249
|
+
}
|
|
250
|
+
if (filter.type === "text") {
|
|
251
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
252
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: filter.label }),
|
|
253
|
+
/* @__PURE__ */ jsx(
|
|
254
|
+
Input,
|
|
255
|
+
{
|
|
256
|
+
value: value ?? "",
|
|
257
|
+
onChange: (e) => onChange(e.target.value),
|
|
258
|
+
placeholder: filter.placeholder ?? filter.label,
|
|
259
|
+
className: "w-[180px]"
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
] });
|
|
263
|
+
}
|
|
264
|
+
if (filter.type === "number") {
|
|
265
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
266
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: filter.label }),
|
|
267
|
+
/* @__PURE__ */ jsx(
|
|
268
|
+
Input,
|
|
269
|
+
{
|
|
270
|
+
type: "number",
|
|
271
|
+
value: value ?? "",
|
|
272
|
+
onChange: (e) => onChange(e.target.value ? Number(e.target.value) : void 0),
|
|
273
|
+
placeholder: filter.placeholder ?? filter.label,
|
|
274
|
+
className: "w-[120px]"
|
|
275
|
+
}
|
|
276
|
+
)
|
|
277
|
+
] });
|
|
278
|
+
}
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
function ReportPagination({ page, pageSize, total, onPageChange, onPageSizeChange }) {
|
|
282
|
+
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
283
|
+
const from = (page - 1) * pageSize + 1;
|
|
284
|
+
const to = Math.min(page * pageSize, total);
|
|
285
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
|
|
286
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: total > 0 ? `${from}\u2013${to} of ${total}` : "No results" }),
|
|
287
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
288
|
+
/* @__PURE__ */ jsx(
|
|
289
|
+
"select",
|
|
290
|
+
{
|
|
291
|
+
value: pageSize,
|
|
292
|
+
onChange: (e) => onPageSizeChange(Number(e.target.value)),
|
|
293
|
+
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",
|
|
294
|
+
children: [25, 50, 100].map((size) => /* @__PURE__ */ jsxs("option", { value: size, children: [
|
|
295
|
+
size,
|
|
296
|
+
" / page"
|
|
297
|
+
] }, size))
|
|
298
|
+
}
|
|
299
|
+
),
|
|
300
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
301
|
+
/* @__PURE__ */ jsx(
|
|
302
|
+
Button,
|
|
303
|
+
{
|
|
304
|
+
variant: "outline",
|
|
305
|
+
size: "sm",
|
|
306
|
+
disabled: page <= 1,
|
|
307
|
+
onClick: () => onPageChange(page - 1),
|
|
308
|
+
children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" })
|
|
309
|
+
}
|
|
310
|
+
),
|
|
311
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm px-2", children: [
|
|
312
|
+
page,
|
|
313
|
+
" / ",
|
|
314
|
+
totalPages
|
|
315
|
+
] }),
|
|
316
|
+
/* @__PURE__ */ jsx(
|
|
317
|
+
Button,
|
|
318
|
+
{
|
|
319
|
+
variant: "outline",
|
|
320
|
+
size: "sm",
|
|
321
|
+
disabled: page >= totalPages,
|
|
322
|
+
onClick: () => onPageChange(page + 1),
|
|
323
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" })
|
|
324
|
+
}
|
|
325
|
+
)
|
|
326
|
+
] })
|
|
327
|
+
] })
|
|
328
|
+
] });
|
|
329
|
+
}
|
|
330
|
+
function ReportSummaryRow({ columns, data, currency }) {
|
|
331
|
+
const visibleCols = columns.filter((c) => c.visible !== false);
|
|
332
|
+
const hasAggregates = visibleCols.some((c) => c.aggregate);
|
|
333
|
+
if (!hasAggregates || data.length === 0) return null;
|
|
334
|
+
return /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-muted/30 overflow-hidden", children: /* @__PURE__ */ jsx("table", { className: "w-full text-xs", children: /* @__PURE__ */ jsx("tbody", { children: /* @__PURE__ */ jsx("tr", { className: "font-semibold", children: visibleCols.map((col) => {
|
|
335
|
+
const value = computeAggregate(col, data);
|
|
336
|
+
const alignRight = col.align === "right" || col.type === "currency" || col.type === "number";
|
|
337
|
+
return /* @__PURE__ */ jsx(
|
|
338
|
+
"td",
|
|
339
|
+
{
|
|
340
|
+
className: `px-3 py-1.5 ${alignRight ? "text-right" : ""}`,
|
|
341
|
+
children: value != null ? formatValuePlain(value, col, currency) : col.aggregate ? "\u2014" : ""
|
|
342
|
+
},
|
|
343
|
+
col.key
|
|
344
|
+
);
|
|
345
|
+
}) }) }) }) });
|
|
346
|
+
}
|
|
347
|
+
function computeAggregate(col, data) {
|
|
348
|
+
if (!col.aggregate) return null;
|
|
349
|
+
const values = data.map((row) => row[col.key]).filter((v) => v != null && !isNaN(Number(v))).map(Number);
|
|
350
|
+
if (values.length === 0) return null;
|
|
351
|
+
switch (col.aggregate) {
|
|
352
|
+
case "sum":
|
|
353
|
+
return values.reduce((a, b) => a + b, 0);
|
|
354
|
+
case "avg":
|
|
355
|
+
return values.reduce((a, b) => a + b, 0) / values.length;
|
|
356
|
+
case "count":
|
|
357
|
+
return values.length;
|
|
358
|
+
case "min":
|
|
359
|
+
return Math.min(...values);
|
|
360
|
+
case "max":
|
|
361
|
+
return Math.max(...values);
|
|
362
|
+
default:
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function exportToCsv(columns, data, filename, currency) {
|
|
367
|
+
const visibleCols = columns.filter((c) => c.visible !== false);
|
|
368
|
+
const csvColumns = visibleCols.map((col) => ({
|
|
369
|
+
key: col.key,
|
|
370
|
+
label: col.label,
|
|
371
|
+
format: (value) => formatValuePlain(value, col, currency)
|
|
372
|
+
}));
|
|
373
|
+
exportCSV(csvColumns, data, filename);
|
|
374
|
+
}
|
|
375
|
+
function ExportDropdown({ columns, data, filename, currency, title }) {
|
|
376
|
+
const t2 = useTranslation();
|
|
377
|
+
const [excelAvailable, setExcelAvailable] = useState(false);
|
|
378
|
+
const [pdfAvailable, setPdfAvailable] = useState(false);
|
|
379
|
+
useEffect(() => {
|
|
380
|
+
import('./excel-QHUSM6IH.js').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {
|
|
381
|
+
});
|
|
382
|
+
import('./pdf-HETKN26Y.js').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {
|
|
383
|
+
});
|
|
384
|
+
}, []);
|
|
385
|
+
const handleCsv = () => exportToCsv(columns, data, filename, currency);
|
|
386
|
+
const handleExcel = async () => {
|
|
387
|
+
const { exportToExcel } = await import('./excel-QHUSM6IH.js');
|
|
388
|
+
await exportToExcel(columns, data, filename, currency);
|
|
389
|
+
};
|
|
390
|
+
const handlePdf = async () => {
|
|
391
|
+
const { exportToPdf } = await import('./pdf-HETKN26Y.js');
|
|
392
|
+
await exportToPdf(columns, data, filename, { title, currency });
|
|
393
|
+
};
|
|
394
|
+
return /* @__PURE__ */ jsxs(Dropdown, { children: [
|
|
395
|
+
/* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", children: [
|
|
396
|
+
/* @__PURE__ */ jsx(Download, { className: "h-4 w-4 mr-1.5" }),
|
|
397
|
+
t2("reports.export")
|
|
398
|
+
] }) }),
|
|
399
|
+
/* @__PURE__ */ jsxs(DropdownContent, { align: "end", children: [
|
|
400
|
+
/* @__PURE__ */ jsx(DropdownItem, { onClick: handleCsv, children: t2("reports.exportCsv") }),
|
|
401
|
+
excelAvailable && /* @__PURE__ */ jsx(DropdownItem, { onClick: handleExcel, children: t2("reports.exportExcel") }),
|
|
402
|
+
pdfAvailable && /* @__PURE__ */ jsx(DropdownItem, { onClick: handlePdf, children: t2("reports.exportPdf") })
|
|
403
|
+
] })
|
|
404
|
+
] });
|
|
405
|
+
}
|
|
406
|
+
function ReportViewer({ report, onBack }) {
|
|
407
|
+
const t2 = useTranslation();
|
|
408
|
+
const { config, provider } = useReportsContext();
|
|
409
|
+
const {
|
|
410
|
+
data,
|
|
411
|
+
total,
|
|
412
|
+
loading,
|
|
413
|
+
generated,
|
|
414
|
+
error,
|
|
415
|
+
query,
|
|
416
|
+
generate,
|
|
417
|
+
setDateRange,
|
|
418
|
+
setFilter,
|
|
419
|
+
setPage,
|
|
420
|
+
setPageSize
|
|
421
|
+
} = useReportData(report, provider, config.defaultPageSize);
|
|
422
|
+
const isAvailable = report.available !== false;
|
|
423
|
+
const tanstackColumns = useMemo(
|
|
424
|
+
() => reportColumnsToTanstack(report.columns, config.currency.code),
|
|
425
|
+
[report.columns, config.currency.code]
|
|
426
|
+
);
|
|
427
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
428
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-sm no-print", children: [
|
|
429
|
+
/* @__PURE__ */ jsxs(
|
|
430
|
+
"button",
|
|
431
|
+
{
|
|
432
|
+
onClick: onBack,
|
|
433
|
+
className: "flex items-center gap-1 text-muted-foreground hover:text-foreground transition-colors",
|
|
434
|
+
children: [
|
|
435
|
+
/* @__PURE__ */ jsx(ArrowLeft, { className: "h-3.5 w-3.5" }),
|
|
436
|
+
t2("reports.pageTitle")
|
|
437
|
+
]
|
|
438
|
+
}
|
|
439
|
+
),
|
|
440
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "/" }),
|
|
441
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: report.name })
|
|
442
|
+
] }),
|
|
443
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
444
|
+
/* @__PURE__ */ jsx("h2", { className: "text-lg font-bold", children: report.name }),
|
|
445
|
+
report.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground mt-0.5", children: report.description })
|
|
446
|
+
] }),
|
|
447
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-end gap-3 no-print", children: [
|
|
448
|
+
/* @__PURE__ */ jsx(
|
|
449
|
+
ReportFilterBar,
|
|
450
|
+
{
|
|
451
|
+
dateRange: query.dateRange,
|
|
452
|
+
onDateRangeChange: setDateRange,
|
|
453
|
+
filters: report.filters ?? [],
|
|
454
|
+
filterValues: query.filters,
|
|
455
|
+
onFilterChange: setFilter
|
|
456
|
+
}
|
|
457
|
+
),
|
|
458
|
+
/* @__PURE__ */ jsxs(Button, { onClick: generate, disabled: loading || !isAvailable, size: "sm", className: "h-9", children: [
|
|
459
|
+
loading ? /* @__PURE__ */ jsx(Loader2, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }) : /* @__PURE__ */ jsx(Play, { className: "h-3.5 w-3.5 mr-1.5" }),
|
|
460
|
+
t2("reports.generate")
|
|
461
|
+
] }),
|
|
462
|
+
generated && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
463
|
+
/* @__PURE__ */ jsx(
|
|
464
|
+
ExportDropdown,
|
|
465
|
+
{
|
|
466
|
+
columns: report.columns,
|
|
467
|
+
data,
|
|
468
|
+
filename: report.id,
|
|
469
|
+
currency: config.currency.code,
|
|
470
|
+
title: report.name
|
|
471
|
+
}
|
|
472
|
+
),
|
|
473
|
+
/* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "h-9", onClick: () => window.print(), children: [
|
|
474
|
+
/* @__PURE__ */ jsx(Printer, { className: "h-3.5 w-3.5 mr-1.5" }),
|
|
475
|
+
t2("reports.print")
|
|
476
|
+
] })
|
|
477
|
+
] })
|
|
478
|
+
] }),
|
|
479
|
+
generated && /* @__PURE__ */ jsx("div", { className: "print-only hidden text-[10px] border-b pb-2 mb-2", children: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-4 gap-y-0.5", children: [
|
|
480
|
+
query.dateRange.from && /* @__PURE__ */ jsxs("span", { children: [
|
|
481
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
482
|
+
t2("reports.filterFrom"),
|
|
483
|
+
":"
|
|
484
|
+
] }),
|
|
485
|
+
" ",
|
|
486
|
+
(/* @__PURE__ */ new Date(query.dateRange.from + "T12:00")).toLocaleDateString()
|
|
487
|
+
] }),
|
|
488
|
+
query.dateRange.to && /* @__PURE__ */ jsxs("span", { children: [
|
|
489
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
490
|
+
t2("reports.filterTo"),
|
|
491
|
+
":"
|
|
492
|
+
] }),
|
|
493
|
+
" ",
|
|
494
|
+
(/* @__PURE__ */ new Date(query.dateRange.to + "T12:00")).toLocaleDateString()
|
|
495
|
+
] }),
|
|
496
|
+
Object.entries(query.filters).map(
|
|
497
|
+
([key, value]) => value ? /* @__PURE__ */ jsxs("span", { children: [
|
|
498
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
499
|
+
key,
|
|
500
|
+
":"
|
|
501
|
+
] }),
|
|
502
|
+
" ",
|
|
503
|
+
String(value)
|
|
504
|
+
] }, key) : null
|
|
505
|
+
),
|
|
506
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
507
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
508
|
+
t2("reports.filterResults"),
|
|
509
|
+
":"
|
|
510
|
+
] }),
|
|
511
|
+
" ",
|
|
512
|
+
total
|
|
513
|
+
] })
|
|
514
|
+
] }) }),
|
|
515
|
+
error && /* @__PURE__ */ jsx("div", { className: "rounded-lg border border-destructive/50 bg-destructive/10 p-3 text-sm text-destructive", children: error }),
|
|
516
|
+
!generated && !loading && /* @__PURE__ */ jsx("div", { className: "flex flex-col items-center justify-center rounded-xl border border-dashed py-16 text-center", children: isAvailable ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
517
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-muted mb-4", children: /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-5 w-5 text-muted-foreground" }) }),
|
|
518
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: t2("reports.emptyStateTitle") }),
|
|
519
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1 max-w-sm", children: t2("reports.emptyStateDescription") })
|
|
520
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
521
|
+
/* @__PURE__ */ jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-muted mb-4", children: /* @__PURE__ */ jsx(Construction, { className: "h-5 w-5 text-muted-foreground" }) }),
|
|
522
|
+
/* @__PURE__ */ jsx("p", { className: "font-medium", children: t2("reports.unavailableTitle") }),
|
|
523
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground mt-1 max-w-sm", children: t2("reports.unavailableDescription") })
|
|
524
|
+
] }) }),
|
|
525
|
+
(generated || loading) && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
526
|
+
/* @__PURE__ */ jsx(
|
|
527
|
+
DataTable,
|
|
528
|
+
{
|
|
529
|
+
columns: tanstackColumns,
|
|
530
|
+
data,
|
|
531
|
+
loading,
|
|
532
|
+
skeletonRows: 5,
|
|
533
|
+
emptyMessage: t2("reports.noResults"),
|
|
534
|
+
variant: "card",
|
|
535
|
+
compact: true
|
|
536
|
+
}
|
|
537
|
+
),
|
|
538
|
+
report.showSummary && !loading && /* @__PURE__ */ jsx(
|
|
539
|
+
ReportSummaryRow,
|
|
540
|
+
{
|
|
541
|
+
columns: report.columns,
|
|
542
|
+
data,
|
|
543
|
+
currency: config.currency.code
|
|
544
|
+
}
|
|
545
|
+
),
|
|
546
|
+
!loading && /* @__PURE__ */ jsx("div", { className: "no-print", children: /* @__PURE__ */ jsx(
|
|
547
|
+
ReportPagination,
|
|
548
|
+
{
|
|
549
|
+
page: query.page,
|
|
550
|
+
pageSize: query.pageSize,
|
|
551
|
+
total,
|
|
552
|
+
onPageChange: setPage,
|
|
553
|
+
onPageSizeChange: setPageSize
|
|
554
|
+
}
|
|
555
|
+
) })
|
|
556
|
+
] })
|
|
557
|
+
] });
|
|
558
|
+
}
|
|
559
|
+
var HASH_BASE = "/reports";
|
|
560
|
+
function getReportIdFromHash() {
|
|
561
|
+
const hash = (window.location.hash.slice(1) || "/").split("?")[0];
|
|
562
|
+
if (hash.startsWith(HASH_BASE + "/") && hash.length > HASH_BASE.length + 1) {
|
|
563
|
+
return hash.slice(HASH_BASE.length + 1);
|
|
564
|
+
}
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
function ReportsPage({
|
|
568
|
+
config,
|
|
569
|
+
provider
|
|
570
|
+
}) {
|
|
571
|
+
const [activeReportId, setActiveReportId] = useState(getReportIdFromHash);
|
|
572
|
+
useEffect(() => {
|
|
573
|
+
function handler() {
|
|
574
|
+
setActiveReportId(getReportIdFromHash());
|
|
575
|
+
}
|
|
576
|
+
window.addEventListener("hashchange", handler);
|
|
577
|
+
return () => window.removeEventListener("hashchange", handler);
|
|
578
|
+
}, []);
|
|
579
|
+
const handleSelect = useCallback((report) => {
|
|
580
|
+
window.location.hash = `${HASH_BASE}/${report.id}`;
|
|
581
|
+
}, []);
|
|
582
|
+
const handleBack = useCallback(() => {
|
|
583
|
+
window.location.hash = HASH_BASE;
|
|
584
|
+
}, []);
|
|
585
|
+
const activeReport = activeReportId ? config.reports.find((r) => r.id === activeReportId) ?? null : null;
|
|
586
|
+
return /* @__PURE__ */ jsx(ReportsContextProvider, { config, provider, children: /* @__PURE__ */ jsx("div", { className: "p-1", children: activeReport ? /* @__PURE__ */ jsx(
|
|
587
|
+
ReportViewer,
|
|
588
|
+
{
|
|
589
|
+
report: activeReport,
|
|
590
|
+
onBack: handleBack
|
|
591
|
+
},
|
|
592
|
+
activeReport.id
|
|
593
|
+
) : /* @__PURE__ */ jsx(ReportHub, { onSelect: handleSelect }) }) });
|
|
594
|
+
}
|
|
595
|
+
function camelToSnake(str) {
|
|
596
|
+
return str.replace(/[A-Z]/g, (c) => "_" + c.toLowerCase());
|
|
597
|
+
}
|
|
598
|
+
function snakeToCamel(str) {
|
|
599
|
+
return str.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
|
|
600
|
+
}
|
|
601
|
+
function mapRow(row, columnMap) {
|
|
602
|
+
const result = {};
|
|
603
|
+
for (const [key, value] of Object.entries(row)) {
|
|
604
|
+
const camelKey = columnMap?.[key] ?? snakeToCamel(key);
|
|
605
|
+
result[camelKey] = value;
|
|
606
|
+
}
|
|
607
|
+
return result;
|
|
608
|
+
}
|
|
609
|
+
function createSupabaseReportProvider(config) {
|
|
610
|
+
const schema = config.schema ?? "public";
|
|
611
|
+
const tenantIdCol = config.tenantIdColumn ?? "tenant_id";
|
|
612
|
+
function resolveTenantId() {
|
|
613
|
+
return typeof config.tenantId === "function" ? config.tenantId() : config.tenantId;
|
|
614
|
+
}
|
|
615
|
+
function getClient() {
|
|
616
|
+
const supabase = getSupabaseClientOptional();
|
|
617
|
+
if (!supabase) throw new Error("Supabase client not available");
|
|
618
|
+
return schema === "public" ? supabase : supabase.schema(schema);
|
|
619
|
+
}
|
|
620
|
+
return {
|
|
621
|
+
async fetch(reportDef, query) {
|
|
622
|
+
const { dataSource } = reportDef;
|
|
623
|
+
const columnMap = dataSource.columnMap;
|
|
624
|
+
if (dataSource.kind === "rpc") {
|
|
625
|
+
return fetchViaRpc(reportDef, query, getClient, resolveTenantId, columnMap);
|
|
626
|
+
}
|
|
627
|
+
return fetchViaView(reportDef, query, getClient, resolveTenantId, tenantIdCol, columnMap);
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
async function fetchViaView(reportDef, query, getClient, resolveTenantId, tenantIdCol, columnMap) {
|
|
632
|
+
const { dataSource } = reportDef;
|
|
633
|
+
let q = getClient().from(dataSource.name).select("*", { count: "exact" });
|
|
634
|
+
const tenantId = resolveTenantId();
|
|
635
|
+
if (tenantId) {
|
|
636
|
+
q = q.eq(tenantIdCol, tenantId);
|
|
637
|
+
}
|
|
638
|
+
const dateCol = dataSource.dateColumn ?? "created_at";
|
|
639
|
+
if (query.dateRange.from) {
|
|
640
|
+
q = q.gte(dateCol, query.dateRange.from);
|
|
641
|
+
}
|
|
642
|
+
if (query.dateRange.to) {
|
|
643
|
+
const toValue = dateCol === "created_at" || dateCol.endsWith("_at") ? query.dateRange.to + "T23:59:59" : query.dateRange.to;
|
|
644
|
+
q = q.lte(dateCol, toValue);
|
|
645
|
+
}
|
|
646
|
+
for (const [key, value] of Object.entries(query.filters)) {
|
|
647
|
+
if (value == null || value === "") continue;
|
|
648
|
+
const col = camelToSnake(key);
|
|
649
|
+
if (Array.isArray(value)) {
|
|
650
|
+
q = q.in(col, value);
|
|
651
|
+
} else {
|
|
652
|
+
q = q.eq(col, value);
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
if (query.search) {
|
|
656
|
+
const searchableCols = reportDef.columns.filter((c) => c.type === "text" || c.type === "email").map((c) => camelToSnake(c.key));
|
|
657
|
+
if (searchableCols.length > 0) {
|
|
658
|
+
const term = `%${query.search}%`;
|
|
659
|
+
const orClause = searchableCols.map((col) => `${col}.ilike.${term}`).join(",");
|
|
660
|
+
q = q.or(orClause);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
const sortCol = query.sortBy ? camelToSnake(query.sortBy) : dataSource.defaultSort ? camelToSnake(dataSource.defaultSort) : dateCol;
|
|
664
|
+
const ascending = query.sortDir ? query.sortDir === "asc" : dataSource.defaultSortDir ? dataSource.defaultSortDir === "asc" : false;
|
|
665
|
+
q = q.order(sortCol, { ascending });
|
|
666
|
+
const from = (query.page - 1) * query.pageSize;
|
|
667
|
+
const to = from + query.pageSize - 1;
|
|
668
|
+
q = q.range(from, to);
|
|
669
|
+
const { data, error, count } = await q;
|
|
670
|
+
if (error) throw error;
|
|
671
|
+
return {
|
|
672
|
+
data: (data ?? []).map((row) => mapRow(row, columnMap)),
|
|
673
|
+
total: count ?? 0
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
async function fetchViaRpc(reportDef, query, getClient, resolveTenantId, columnMap) {
|
|
677
|
+
const { dataSource } = reportDef;
|
|
678
|
+
const tenantId = resolveTenantId();
|
|
679
|
+
const params = {
|
|
680
|
+
p_tenant_id: tenantId,
|
|
681
|
+
p_from: query.dateRange.from,
|
|
682
|
+
p_to: query.dateRange.to,
|
|
683
|
+
p_page: query.page,
|
|
684
|
+
p_page_size: query.pageSize
|
|
685
|
+
};
|
|
686
|
+
for (const [key, value] of Object.entries(query.filters)) {
|
|
687
|
+
if (value == null || value === "") continue;
|
|
688
|
+
params[`p_${camelToSnake(key)}`] = value;
|
|
689
|
+
}
|
|
690
|
+
if (query.sortBy) {
|
|
691
|
+
params.p_sort_by = camelToSnake(query.sortBy);
|
|
692
|
+
params.p_sort_dir = query.sortDir ?? "desc";
|
|
693
|
+
}
|
|
694
|
+
const { data, error } = await getClient().rpc(dataSource.name, params);
|
|
695
|
+
if (error) throw error;
|
|
696
|
+
if (data && typeof data === "object" && "data" in data) {
|
|
697
|
+
return {
|
|
698
|
+
data: (data.data ?? []).map((row) => mapRow(row, columnMap)),
|
|
699
|
+
total: data.total ?? 0
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
const rows = Array.isArray(data) ? data : [];
|
|
703
|
+
return {
|
|
704
|
+
data: rows.map((row) => mapRow(row, columnMap)),
|
|
705
|
+
total: rows.length
|
|
706
|
+
};
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
// src/data/mock.ts
|
|
710
|
+
function createMockReportProvider() {
|
|
711
|
+
return {
|
|
712
|
+
async fetch() {
|
|
713
|
+
return { data: [], total: 0 };
|
|
714
|
+
}
|
|
715
|
+
};
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
// src/lib/tenant.ts
|
|
719
|
+
var t;
|
|
720
|
+
function getReportsTenantId() {
|
|
721
|
+
return t;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
// src/locales/en.ts
|
|
725
|
+
var en = {
|
|
726
|
+
"reports.pageTitle": "Reports",
|
|
727
|
+
"reports.pageSubtitle": "Access complete reports for analysis and decision making",
|
|
728
|
+
"reports.allReports": "All Reports",
|
|
729
|
+
"reports.dateRange": "Date Range",
|
|
730
|
+
"reports.from": "From",
|
|
731
|
+
"reports.to": "To",
|
|
732
|
+
"reports.export": "Export",
|
|
733
|
+
"reports.exportCsv": "Export CSV",
|
|
734
|
+
"reports.exportExcel": "Export Excel",
|
|
735
|
+
"reports.exportPdf": "Export PDF",
|
|
736
|
+
"reports.noResults": "No results found.",
|
|
737
|
+
"reports.backToReports": "Back",
|
|
738
|
+
"reports.reports": "reports",
|
|
739
|
+
"reports.search": "Search reports...",
|
|
740
|
+
"reports.today": "Today",
|
|
741
|
+
"reports.yesterday": "Yesterday",
|
|
742
|
+
"reports.last7Days": "Last 7 days",
|
|
743
|
+
"reports.thisMonth": "This month",
|
|
744
|
+
"reports.lastMonth": "Last month",
|
|
745
|
+
"reports.thisQuarter": "This quarter",
|
|
746
|
+
"reports.custom": "Custom",
|
|
747
|
+
"reports.generate": "Generate",
|
|
748
|
+
"reports.emptyStateTitle": "Ready to generate",
|
|
749
|
+
"reports.emptyStateDescription": "Select a date range and filters, then click Generate to view the report.",
|
|
750
|
+
"reports.unavailableTitle": "Coming soon",
|
|
751
|
+
"reports.unavailableDescription": "This report is not available yet. It will be enabled in a future update."
|
|
752
|
+
};
|
|
753
|
+
|
|
754
|
+
// src/locales/pt-BR.ts
|
|
755
|
+
var ptBR = {
|
|
756
|
+
"reports.pageTitle": "Relat\xF3rios",
|
|
757
|
+
"reports.pageSubtitle": "Acesse relat\xF3rios completos para an\xE1lise e tomada de decis\xE3o",
|
|
758
|
+
"reports.allReports": "Todos os Relat\xF3rios",
|
|
759
|
+
"reports.dateRange": "Per\xEDodo",
|
|
760
|
+
"reports.from": "De",
|
|
761
|
+
"reports.to": "At\xE9",
|
|
762
|
+
"reports.export": "Exportar",
|
|
763
|
+
"reports.exportCsv": "Exportar CSV",
|
|
764
|
+
"reports.exportExcel": "Exportar Excel",
|
|
765
|
+
"reports.exportPdf": "Exportar PDF",
|
|
766
|
+
"reports.noResults": "Nenhum resultado encontrado.",
|
|
767
|
+
"reports.backToReports": "Voltar",
|
|
768
|
+
"reports.reports": "relat\xF3rios",
|
|
769
|
+
"reports.search": "Buscar relat\xF3rios...",
|
|
770
|
+
"reports.today": "Hoje",
|
|
771
|
+
"reports.yesterday": "Ontem",
|
|
772
|
+
"reports.last7Days": "\xDAltimos 7 dias",
|
|
773
|
+
"reports.thisMonth": "Este m\xEAs",
|
|
774
|
+
"reports.lastMonth": "M\xEAs passado",
|
|
775
|
+
"reports.thisQuarter": "Este trimestre",
|
|
776
|
+
"reports.custom": "Personalizado",
|
|
777
|
+
"reports.generate": "Gerar",
|
|
778
|
+
"reports.emptyStateTitle": "Pronto para gerar",
|
|
779
|
+
"reports.emptyStateDescription": "Selecione o per\xEDodo e os filtros desejados, depois clique em Gerar para visualizar o relat\xF3rio.",
|
|
780
|
+
"reports.unavailableTitle": "Em breve",
|
|
781
|
+
"reports.unavailableDescription": "Este relat\xF3rio ainda n\xE3o est\xE1 dispon\xEDvel. Ser\xE1 habilitado em uma atualiza\xE7\xE3o futura."
|
|
782
|
+
};
|
|
783
|
+
|
|
784
|
+
// src/locales/index.ts
|
|
785
|
+
var reportsLocales = {
|
|
786
|
+
en,
|
|
787
|
+
"pt-BR": ptBR
|
|
788
|
+
};
|
|
789
|
+
|
|
790
|
+
// src/index.ts
|
|
791
|
+
var DEFAULT_LABELS = {
|
|
792
|
+
pageTitle: "Reports",
|
|
793
|
+
pageSubtitle: "Access complete reports for analysis and decision making",
|
|
794
|
+
allReports: "All Reports",
|
|
795
|
+
dateRange: "Date Range",
|
|
796
|
+
from: "From",
|
|
797
|
+
to: "To",
|
|
798
|
+
export: "Export",
|
|
799
|
+
exportCsv: "Export CSV",
|
|
800
|
+
exportExcel: "Export Excel",
|
|
801
|
+
exportPdf: "Export PDF",
|
|
802
|
+
noResults: "No results found.",
|
|
803
|
+
backToReports: "Back",
|
|
804
|
+
reports: "reports",
|
|
805
|
+
search: "Search reports...",
|
|
806
|
+
today: "Today",
|
|
807
|
+
yesterday: "Yesterday",
|
|
808
|
+
last7Days: "Last 7 days",
|
|
809
|
+
thisMonth: "This month",
|
|
810
|
+
lastMonth: "Last month",
|
|
811
|
+
thisQuarter: "This quarter",
|
|
812
|
+
custom: "Custom",
|
|
813
|
+
generate: "Generate",
|
|
814
|
+
print: "Print",
|
|
815
|
+
filterFrom: "From",
|
|
816
|
+
filterTo: "To",
|
|
817
|
+
filterResults: "Results",
|
|
818
|
+
emptyStateTitle: "Ready to generate",
|
|
819
|
+
emptyStateDescription: "Select a date range and filters, then click Generate to view the report.",
|
|
820
|
+
unavailableTitle: "Coming soon",
|
|
821
|
+
unavailableDescription: "This report is not available yet. It will be enabled in a future update."
|
|
822
|
+
};
|
|
823
|
+
var DEFAULT_CURRENCY = { code: "BRL", locale: "pt-BR", symbol: "R$" };
|
|
824
|
+
function resolveConfig(options) {
|
|
825
|
+
return {
|
|
826
|
+
labels: { ...DEFAULT_LABELS, ...options.labels },
|
|
827
|
+
currency: { ...DEFAULT_CURRENCY, ...options.currency },
|
|
828
|
+
reports: options.reports,
|
|
829
|
+
defaultPageSize: options.defaultPageSize ?? 50,
|
|
830
|
+
showHeader: options.showHeader !== false
|
|
831
|
+
};
|
|
832
|
+
}
|
|
833
|
+
function createReportsPlugin(options) {
|
|
834
|
+
const config = resolveConfig(options);
|
|
835
|
+
registerTranslations(reportsLocales);
|
|
836
|
+
const provider = options.dataProvider ?? createSafeDataProvider(
|
|
837
|
+
() => createSupabaseReportProvider({ tenantId: () => getReportsTenantId() }),
|
|
838
|
+
() => createMockReportProvider()
|
|
839
|
+
);
|
|
840
|
+
const PageComponent = () => React7.createElement(ReportsPage, { config, provider });
|
|
841
|
+
return {
|
|
842
|
+
id: "reports",
|
|
843
|
+
name: config.labels.pageTitle,
|
|
844
|
+
icon: "BarChart3",
|
|
845
|
+
version: "1.0.0",
|
|
846
|
+
scope: options.scope ?? "universal",
|
|
847
|
+
verticalId: options.verticalId,
|
|
848
|
+
defaultEnabled: true,
|
|
849
|
+
dependencies: [],
|
|
850
|
+
declaredFeatures: [
|
|
851
|
+
{ id: "reports", label: config.labels.pageTitle, group: config.labels.pageTitle }
|
|
852
|
+
],
|
|
853
|
+
navigation: [
|
|
854
|
+
{
|
|
855
|
+
section: options.navSection ?? "main",
|
|
856
|
+
position: options.navPosition ?? 9,
|
|
857
|
+
label: config.labels.pageTitle,
|
|
858
|
+
route: "/reports",
|
|
859
|
+
icon: "BarChart3",
|
|
860
|
+
permission: { feature: "reports", action: "read" }
|
|
861
|
+
}
|
|
862
|
+
],
|
|
863
|
+
routes: [
|
|
864
|
+
{
|
|
865
|
+
path: "/reports",
|
|
866
|
+
component: PageComponent,
|
|
867
|
+
permission: { feature: "reports", action: "read" }
|
|
868
|
+
}
|
|
869
|
+
],
|
|
870
|
+
widgets: [],
|
|
871
|
+
registries: [],
|
|
872
|
+
locales: reportsLocales
|
|
873
|
+
};
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
export { createMockReportProvider, createReportsPlugin, createSupabaseReportProvider };
|
|
877
|
+
//# sourceMappingURL=index.js.map
|
|
878
|
+
//# sourceMappingURL=index.js.map
|