@fayz-ai/plugin-reports 0.12.0 → 0.12.2
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/{chunk-5SO5HWV2.js → chunk-4QIHTUXD.js} +60 -5
- package/dist/chunk-4QIHTUXD.js.map +1 -0
- package/dist/components/ExportDropdown.d.ts +7 -1
- package/dist/components/ExportDropdown.d.ts.map +1 -1
- package/dist/{excel-LDM5H5IW.js → excel-DSORXL4O.js} +6 -5
- package/dist/excel-DSORXL4O.js.map +1 -0
- package/dist/export/csv.d.ts.map +1 -1
- package/dist/export/excel.d.ts.map +1 -1
- package/dist/index.js +37 -19
- package/dist/index.js.map +1 -1
- package/dist/lib/columns.d.ts +27 -0
- package/dist/lib/columns.d.ts.map +1 -1
- package/dist/{pdf-XA2AR6B4.js → pdf-DH4CJMV5.js} +3 -3
- package/dist/{pdf-XA2AR6B4.js.map → pdf-DH4CJMV5.js.map} +1 -1
- package/dist/types.d.ts +5 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/AnalyticsReportViewer.d.ts.map +1 -1
- package/package.json +6 -5
- package/dist/chunk-5SO5HWV2.js.map +0 -1
- package/dist/excel-LDM5H5IW.js.map +0 -1
|
@@ -33,8 +33,59 @@ function formatDuration(value) {
|
|
|
33
33
|
const m = minutes % 60;
|
|
34
34
|
return m ? `${h}h ${m}min` : `${h}h`;
|
|
35
35
|
}
|
|
36
|
+
function plainValue(value) {
|
|
37
|
+
if (value == null || value === "") return "";
|
|
38
|
+
if (Array.isArray(value)) return value.map(plainValue).filter(Boolean).join(", ");
|
|
39
|
+
if (typeof value === "boolean") return value ? "Sim" : "N\xE3o";
|
|
40
|
+
if (typeof value === "object") {
|
|
41
|
+
return Object.entries(value).filter(([, v]) => v !== null && v !== void 0 && v !== "").map(([k, v]) => `${humanizeKey(k)}: ${plainValue(v)}`).join(" \xB7 ");
|
|
42
|
+
}
|
|
43
|
+
return String(value);
|
|
44
|
+
}
|
|
45
|
+
function humanizeKey(key) {
|
|
46
|
+
const spaced = key.replace(/[_-]+/g, " ").replace(/([a-z\d])([A-Z])/g, "$1 $2");
|
|
47
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1);
|
|
48
|
+
}
|
|
49
|
+
function expandBagColumns(columns, data) {
|
|
50
|
+
const bags = columns.filter((c) => c.expand);
|
|
51
|
+
if (!bags.length) return { columns, rows: data };
|
|
52
|
+
const keysByBag = /* @__PURE__ */ new Map();
|
|
53
|
+
for (const bag of bags) {
|
|
54
|
+
const keys = [];
|
|
55
|
+
for (const row of data) {
|
|
56
|
+
const value = row?.[bag.key];
|
|
57
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
58
|
+
for (const key of Object.keys(value)) if (!keys.includes(key)) keys.push(key);
|
|
59
|
+
}
|
|
60
|
+
keysByBag.set(bag.key, keys);
|
|
61
|
+
}
|
|
62
|
+
const expanded = columns.flatMap((col) => {
|
|
63
|
+
if (!col.expand) return [col];
|
|
64
|
+
return (keysByBag.get(col.key) ?? []).map((key) => ({
|
|
65
|
+
key: `${col.key}.${key}`,
|
|
66
|
+
label: humanizeKey(key),
|
|
67
|
+
type: "text",
|
|
68
|
+
// The key exists only on the flattened row, never on the read model, so
|
|
69
|
+
// a server-side ORDER BY on it would 42703. Client sort only.
|
|
70
|
+
sortable: false
|
|
71
|
+
}));
|
|
72
|
+
});
|
|
73
|
+
const rows = data.map((row) => {
|
|
74
|
+
const next = { ...row };
|
|
75
|
+
for (const [bagKey, keys] of keysByBag) {
|
|
76
|
+
const bag = row?.[bagKey];
|
|
77
|
+
for (const key of keys) {
|
|
78
|
+
next[`${bagKey}.${key}`] = bag && typeof bag === "object" ? plainValue(bag[key]) : "";
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return next;
|
|
82
|
+
});
|
|
83
|
+
return { columns: expanded, rows };
|
|
84
|
+
}
|
|
36
85
|
function formatValuePlain(value, col, currency) {
|
|
37
86
|
if (value == null || value === "") return "";
|
|
87
|
+
if (col.valueMap) return col.valueMap[String(value)] ?? String(value);
|
|
88
|
+
if (typeof value === "object") return plainValue(value);
|
|
38
89
|
switch (col.type) {
|
|
39
90
|
case "currency":
|
|
40
91
|
return formatCurrency(value, col.currency ?? currency);
|
|
@@ -69,9 +120,13 @@ function reportColumnsToTanstack(columns, currency) {
|
|
|
69
120
|
if (col.renderCell) {
|
|
70
121
|
return col.renderCell(value, row);
|
|
71
122
|
}
|
|
72
|
-
if (value == null || value === "") {
|
|
123
|
+
if (value == null || value === "" || typeof value === "object" && !plainValue(value)) {
|
|
73
124
|
return React.createElement("span", { className: "text-muted-foreground" }, "\u2014");
|
|
74
125
|
}
|
|
126
|
+
if (col.valueMap) {
|
|
127
|
+
const label = col.valueMap[String(value)] ?? String(value);
|
|
128
|
+
return React.createElement("span", { className: "text-sm" }, label);
|
|
129
|
+
}
|
|
75
130
|
let content;
|
|
76
131
|
switch (col.type) {
|
|
77
132
|
case "person":
|
|
@@ -109,7 +164,7 @@ function reportColumnsToTanstack(columns, currency) {
|
|
|
109
164
|
content = formatDuration(value);
|
|
110
165
|
break;
|
|
111
166
|
default:
|
|
112
|
-
content =
|
|
167
|
+
content = plainValue(value);
|
|
113
168
|
}
|
|
114
169
|
const cls = alignRight ? "text-right" : alignCenter ? "text-center" : void 0;
|
|
115
170
|
return cls ? React.createElement("div", { className: cls }, content) : React.createElement(React.Fragment, null, content);
|
|
@@ -119,6 +174,6 @@ function reportColumnsToTanstack(columns, currency) {
|
|
|
119
174
|
});
|
|
120
175
|
}
|
|
121
176
|
|
|
122
|
-
export { formatValuePlain, reportColumnsToTanstack };
|
|
123
|
-
//# sourceMappingURL=chunk-
|
|
124
|
-
//# sourceMappingURL=chunk-
|
|
177
|
+
export { expandBagColumns, formatValuePlain, reportColumnsToTanstack };
|
|
178
|
+
//# sourceMappingURL=chunk-4QIHTUXD.js.map
|
|
179
|
+
//# sourceMappingURL=chunk-4QIHTUXD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/columns.ts"],"names":["formatCurrencyLocale","formatDateLocale","formatDateTimeLocale"],"mappings":"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","file":"chunk-4QIHTUXD.js","sourcesContent":["import React from 'react'\nimport type { ColumnDef } from '@tanstack/react-table'\nimport type { ReportColumnDef } from '../types'\nimport { PersonLink } from '@fayz-ai/admin'\nimport {\n formatCurrency as formatCurrencyLocale,\n formatDate as formatDateLocale,\n formatDateTime as formatDateTimeLocale,\n} from '@fayz-ai/core'\n\n// ---------------------------------------------------------------------------\n// Formatters (mirrors fieldToColumn.tsx logic)\n// ---------------------------------------------------------------------------\n\nfunction formatCurrency(value: any, currency?: string): string {\n const num = typeof value === 'number' ? value : parseFloat(value)\n if (isNaN(num)) return '—'\n return formatCurrencyLocale(num, currency)\n}\n\nfunction formatDate(value: any): string {\n if (!value) return '—'\n return formatDateLocale(value)\n}\n\nfunction formatDateTime(value: any): string {\n if (!value) return '—'\n return formatDateTimeLocale(value)\n}\n\nfunction formatNumber(value: any): string {\n if (value == null) return '—'\n return typeof value === 'number' ? value.toLocaleString() : String(value)\n}\n\nfunction formatPercent(value: any): string {\n const num = typeof value === 'number' ? value : parseFloat(value)\n return isNaN(num) ? '—' : `${num.toLocaleString(undefined, { maximumFractionDigits: 2 })}%`\n}\n\n/** Durations are stored in HOURS, matching the analytics card renderer. */\nfunction formatDuration(value: any): string {\n const hours = typeof value === 'number' ? value : parseFloat(value)\n if (isNaN(hours) || hours <= 0) return '—'\n const minutes = Math.round(hours * 60)\n if (minutes < 60) return `${minutes}min`\n const h = Math.floor(minutes / 60)\n const m = minutes % 60\n return m ? `${h}h ${m}min` : `${h}h`\n}\n\n// ---------------------------------------------------------------------------\n// Plain-text formatter for exports\n// ---------------------------------------------------------------------------\n\n/** A single cell value as text, whatever shape it arrived in. Arrays join, an\n * open bag prints as \"Chave: valor · Chave: valor\" — never \"[object Object]\",\n * which is what a JSON column looked like in a spreadsheet. */\nexport function plainValue(value: any): string {\n if (value == null || value === '') return ''\n if (Array.isArray(value)) return value.map(plainValue).filter(Boolean).join(', ')\n if (typeof value === 'boolean') return value ? 'Sim' : 'Não'\n if (typeof value === 'object') {\n return Object.entries(value as Record<string, unknown>)\n .filter(([, v]) => v !== null && v !== undefined && v !== '')\n .map(([k, v]) => `${humanizeKey(k)}: ${plainValue(v)}`)\n .join(' · ')\n }\n return String(value)\n}\n\n/** `hair_remaining` / `hairRemaining` → \"Hair remaining\". A key that arrives\n * already humanized (\"Estilo\") comes back unchanged. */\nexport function humanizeKey(key: string): string {\n const spaced = key.replace(/[_-]+/g, ' ').replace(/([a-z\\d])([A-Z])/g, '$1 $2')\n return spaced.charAt(0).toUpperCase() + spaced.slice(1)\n}\n\n/**\n * Expand every `expand` column into one column PER KEY found in the rows, and\n * flatten those keys onto the rows so no consumer needs a special case.\n *\n * The keys cannot be declared on the card — each landing page asks something\n * different, and typing them here would make the NEXT form invisible. So they\n * are discovered from the rows themselves, in first-seen order, and the bag\n * column steps aside. \"Estilo\" as a real column can be read down and sorted;\n * \"Estilo: House · Perfil: Outros\" crammed into one cell cannot, and a raw JSON\n * blob is worse still.\n *\n * Used by the report table AND by the writers, so the screen and the file show\n * the same columns.\n *\n * Columns that are not `expand` pass through untouched, order included.\n */\nexport function expandBagColumns(\n columns: ReportColumnDef[],\n data: Record<string, any>[],\n): { columns: ReportColumnDef[]; rows: Record<string, any>[] } {\n const bags = columns.filter((c) => c.expand)\n if (!bags.length) return { columns, rows: data }\n\n // First-seen order, so the spreadsheet's columns follow the form's own order\n // rather than an alphabetical one nobody asked for.\n const keysByBag = new Map<string, string[]>()\n for (const bag of bags) {\n const keys: string[] = []\n for (const row of data) {\n const value = row?.[bag.key]\n if (!value || typeof value !== 'object' || Array.isArray(value)) continue\n for (const key of Object.keys(value)) if (!keys.includes(key)) keys.push(key)\n }\n keysByBag.set(bag.key, keys)\n }\n\n const expanded = columns.flatMap((col): ReportColumnDef[] => {\n if (!col.expand) return [col]\n return (keysByBag.get(col.key) ?? []).map((key) => ({\n key: `${col.key}.${key}`,\n label: humanizeKey(key),\n type: 'text' as const,\n // The key exists only on the flattened row, never on the read model, so\n // a server-side ORDER BY on it would 42703. Client sort only.\n sortable: false,\n }))\n })\n\n const rows = data.map((row) => {\n const next = { ...row }\n for (const [bagKey, keys] of keysByBag) {\n const bag = row?.[bagKey]\n for (const key of keys) {\n next[`${bagKey}.${key}`] = bag && typeof bag === 'object' ? plainValue(bag[key]) : ''\n }\n }\n return next\n })\n\n return { columns: expanded, rows }\n}\n\nexport function formatValuePlain(value: any, col: ReportColumnDef, currency?: string): string {\n if (value == null || value === '') return ''\n // A stored code is not a word: 'new' in a Situação column is the database\n // talking to itself. Mapped here, so the table and the file agree.\n if (col.valueMap) return col.valueMap[String(value)] ?? String(value)\n if (typeof value === 'object') return plainValue(value)\n switch (col.type) {\n case 'currency':\n return formatCurrency(value, col.currency ?? currency)\n case 'date':\n return formatDate(value)\n case 'datetime':\n return formatDateTime(value)\n case 'number':\n return formatNumber(value)\n case 'percent':\n return formatPercent(value)\n case 'duration':\n return formatDuration(value)\n case 'boolean':\n return value ? 'Yes' : 'No'\n default:\n return String(value)\n }\n}\n\n// ---------------------------------------------------------------------------\n// ReportColumnDef[] → TanStack ColumnDef[]\n// ---------------------------------------------------------------------------\n\nexport function reportColumnsToTanstack(\n columns: ReportColumnDef[],\n currency?: string,\n): ColumnDef<Record<string, any>>[] {\n return columns\n .filter((c) => c.visible !== false)\n .map((col): ColumnDef<Record<string, any>> => {\n const sortable = col.sortable\n ?? ['text', 'number', 'percent', 'duration', 'currency', 'date', 'datetime', 'email'].includes(col.type)\n const alignRight = col.align === 'right'\n || (!col.align && ['currency', 'number', 'percent', 'duration'].includes(col.type))\n const alignCenter = col.align === 'center'\n\n return {\n accessorKey: col.key,\n header: col.label,\n enableSorting: sortable,\n cell: (ctx) => {\n const value = ctx.getValue()\n const row = ctx.row.original\n\n if (col.renderCell) {\n return col.renderCell(value, row)\n }\n\n // An open bag ({} from a lead that answered nothing) is as empty as\n // null — it just does not look like it to a `== null` test.\n if (value == null || value === '' || (typeof value === 'object' && !plainValue(value))) {\n return React.createElement('span', { className: 'text-muted-foreground' }, '—')\n }\n\n if (col.valueMap) {\n const label = col.valueMap[String(value)] ?? String(value)\n return React.createElement('span', { className: 'text-sm' }, label)\n }\n\n let content: React.ReactNode\n switch (col.type) {\n case 'person':\n content = React.createElement(PersonLink, {\n personId: col.idKey ? row[col.idKey] : null,\n name: String(value),\n kind: col.personKind,\n size: 'sm',\n className: 'text-xs',\n })\n break\n case 'currency':\n content = formatCurrency(value, col.currency ?? currency)\n break\n case 'date':\n content = formatDate(value)\n break\n case 'datetime':\n content = formatDateTime(value)\n break\n case 'boolean':\n content = value\n ? React.createElement('svg', { className: 'h-4 w-4 text-success', viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: '2' },\n React.createElement('polyline', { points: '20 6 9 17 4 12' }),\n )\n : React.createElement('span', { className: 'text-muted-foreground' }, '—')\n break\n case 'number':\n content = formatNumber(value)\n break\n case 'percent':\n content = formatPercent(value)\n break\n case 'duration':\n content = formatDuration(value)\n break\n default:\n // plainValue, not String(): a jsonb column (form answers, custom\n // fields) rendered as the literal text \"[object Object]\".\n content = plainValue(value)\n }\n\n const cls = alignRight ? 'text-right' : alignCenter ? 'text-center' : undefined\n return cls\n ? React.createElement('div', { className: cls }, content)\n : React.createElement(React.Fragment, null, content)\n },\n meta: { align: alignRight ? 'right' : alignCenter ? 'center' : 'left' },\n }\n })\n}\n"]}
|
|
@@ -6,7 +6,13 @@ interface ExportDropdownProps {
|
|
|
6
6
|
filename: string;
|
|
7
7
|
currency?: string;
|
|
8
8
|
title?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Fetch the WHOLE result set. The table on screen is one page of 50, and a
|
|
11
|
+
* file holding 50 of 4,000 rows says nothing about being a fragment — so the
|
|
12
|
+
* export asks for everything before it writes. Absent ⇒ the page is exported.
|
|
13
|
+
*/
|
|
14
|
+
fetchAll?: () => Promise<Record<string, any>[]>;
|
|
9
15
|
}
|
|
10
|
-
export declare function ExportDropdown({ columns, data, filename, currency, title }: ExportDropdownProps): React.JSX.Element;
|
|
16
|
+
export declare function ExportDropdown({ columns, data, filename, currency, title, fetchAll }: ExportDropdownProps): React.JSX.Element;
|
|
11
17
|
export {};
|
|
12
18
|
//# sourceMappingURL=ExportDropdown.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ExportDropdown.d.ts","sourceRoot":"","sources":["../../src/components/ExportDropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAUlD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAG/C,UAAU,mBAAmB;IAC3B,OAAO,EAAE,eAAe,EAAE,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"ExportDropdown.d.ts","sourceRoot":"","sources":["../../src/components/ExportDropdown.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAA;AAUlD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAG/C,UAAU,mBAAmB;IAC3B,OAAO,EAAE,eAAe,EAAE,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAA;IAC3B,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAAC,CAAA;CAChD;AAED,wBAAgB,cAAc,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,mBAAmB,qBAuDzG"}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { formatValuePlain } from './chunk-
|
|
1
|
+
import { expandBagColumns, formatValuePlain } from './chunk-4QIHTUXD.js';
|
|
2
2
|
|
|
3
3
|
// src/export/excel.ts
|
|
4
4
|
var load = (m) => new Function("m", "return import(m)")(m);
|
|
5
5
|
async function exportToExcel(columns, data, filename, currency) {
|
|
6
6
|
const XLSX = await load("xlsx");
|
|
7
|
-
const
|
|
7
|
+
const { columns: cols, rows: expanded } = expandBagColumns(columns, data);
|
|
8
|
+
const visibleCols = cols.filter((c) => c.visible !== false);
|
|
8
9
|
const header = visibleCols.map((c) => c.label);
|
|
9
|
-
const rows =
|
|
10
|
+
const rows = expanded.map(
|
|
10
11
|
(row) => visibleCols.map((col) => {
|
|
11
12
|
const value = row[col.key];
|
|
12
13
|
if (col.type === "number" || col.type === "currency") {
|
|
@@ -31,5 +32,5 @@ async function isExcelAvailable() {
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export { exportToExcel, isExcelAvailable };
|
|
34
|
-
//# sourceMappingURL=excel-
|
|
35
|
-
//# sourceMappingURL=excel-
|
|
35
|
+
//# sourceMappingURL=excel-DSORXL4O.js.map
|
|
36
|
+
//# sourceMappingURL=excel-DSORXL4O.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/export/excel.ts"],"names":[],"mappings":";;;AAKA,IAAM,IAAA,GAAO,CAAC,CAAA,KAAc,IAAI,SAAS,GAAA,EAAK,kBAAkB,EAAE,CAAC,CAAA;AAEnE,eAAsB,aAAA,CACpB,OAAA,EACA,IAAA,EACA,QAAA,EACA,QAAA,EACe;AACf,EAAA,MAAM,IAAA,GAAO,MAAM,IAAA,CAAK,MAAM,CAAA;AAE9B,EAAA,MAAM,EAAE,SAAS,IAAA,EAAM,IAAA,EAAM,UAAS,GAAI,gBAAA,CAAiB,SAAS,IAAI,CAAA;AACxE,EAAA,MAAM,cAAc,IAAA,CAAK,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,YAAY,KAAK,CAAA;AAE1D,EAAA,MAAM,SAAS,WAAA,CAAY,GAAA,CAAI,CAAC,CAAA,KAAM,EAAE,KAAK,CAAA;AAC7C,EAAA,MAAM,OAAO,QAAA,CAAS,GAAA;AAAA,IAAI,CAAC,GAAA,KACzB,WAAA,CAAY,GAAA,CAAI,CAAC,GAAA,KAAQ;AACvB,MAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,GAAA,CAAI,GAAG,CAAA;AACzB,MAAA,IAAI,GAAA,CAAI,IAAA,KAAS,QAAA,IAAY,GAAA,CAAI,SAAS,UAAA,EAAY;AACpD,QAAA,MAAM,MAAM,OAAO,KAAA,KAAU,QAAA,GAAW,KAAA,GAAQ,WAAW,KAAK,CAAA;AAChE,QAAA,OAAO,KAAA,CAAM,GAAG,CAAA,GAAI,EAAA,GAAK,GAAA;AAAA,MAC3B;AACA,MAAA,OAAO,gBAAA,CAAiB,KAAA,EAAO,GAAA,EAAK,QAAQ,CAAA;AAAA,IAC9C,CAAC;AAAA,GACH;AAEA,EAAA,MAAM,EAAA,GAAK,KAAK,KAAA,CAAM,YAAA,CAAa,CAAC,MAAA,EAAQ,GAAG,IAAI,CAAC,CAAA;AACpD,EAAA,MAAM,EAAA,GAAK,IAAA,CAAK,KAAA,CAAM,QAAA,EAAS;AAC/B,EAAA,IAAA,CAAK,KAAA,CAAM,iBAAA,CAAkB,EAAA,EAAI,EAAA,EAAI,QAAQ,CAAA;AAC7C,EAAA,IAAA,CAAK,SAAA,CAAU,EAAA,EAAI,CAAA,EAAG,QAAQ,CAAA,KAAA,CAAO,CAAA;AACvC;AAEA,eAAsB,gBAAA,GAAqC;AACzD,EAAA,IAAI;AACF,IAAA,MAAM,KAAK,MAAM,CAAA;AACjB,IAAA,OAAO,IAAA;AAAA,EACT,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,KAAA;AAAA,EACT;AACF","file":"excel-DSORXL4O.js","sourcesContent":["import type { ReportColumnDef } from '../types'\nimport { expandBagColumns, formatValuePlain } from '../lib/columns'\n\n// Opaque dynamic import — hidden from Vite's static analysis so it won't\n// fail when xlsx is not installed (it's an optional peer dependency).\nconst load = (m: string) => new Function('m', 'return import(m)')(m) as Promise<any>\n\nexport async function exportToExcel(\n columns: ReportColumnDef[],\n data: Record<string, any>[],\n filename: string,\n currency?: string,\n): Promise<void> {\n const XLSX = await load('xlsx')\n // Mesma expansão do CSV: uma coluna por resposta do formulário.\n const { columns: cols, rows: expanded } = expandBagColumns(columns, data)\n const visibleCols = cols.filter((c) => c.visible !== false)\n\n const header = visibleCols.map((c) => c.label)\n const rows = expanded.map((row) =>\n visibleCols.map((col) => {\n const value = row[col.key]\n if (col.type === 'number' || col.type === 'currency') {\n const num = typeof value === 'number' ? value : parseFloat(value)\n return isNaN(num) ? '' : num\n }\n return formatValuePlain(value, col, currency)\n }),\n )\n\n const ws = XLSX.utils.aoa_to_sheet([header, ...rows])\n const wb = XLSX.utils.book_new()\n XLSX.utils.book_append_sheet(wb, ws, 'Report')\n XLSX.writeFile(wb, `${filename}.xlsx`)\n}\n\nexport async function isExcelAvailable(): Promise<boolean> {\n try {\n await load('xlsx')\n return true\n } catch {\n return false\n }\n}\n"]}
|
package/dist/export/csv.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/export/csv.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAG/C,wBAAgB,WAAW,CACzB,OAAO,EAAE,eAAe,EAAE,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,
|
|
1
|
+
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../../src/export/csv.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAG/C,wBAAgB,WAAW,CACzB,OAAO,EAAE,eAAe,EAAE,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,CAaN"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"excel.d.ts","sourceRoot":"","sources":["../../src/export/excel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAO/C,wBAAsB,aAAa,CACjC,OAAO,EAAE,eAAe,EAAE,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"excel.d.ts","sourceRoot":"","sources":["../../src/export/excel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAO/C,wBAAsB,aAAa,CACjC,OAAO,EAAE,eAAe,EAAE,EAC1B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,EAC3B,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAsBf;AAED,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAOzD"}
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { reportColumnsToTanstack, formatValuePlain } from './chunk-
|
|
1
|
+
import { expandBagColumns, reportColumnsToTanstack, formatValuePlain } from './chunk-4QIHTUXD.js';
|
|
2
2
|
import * as React10 from 'react';
|
|
3
3
|
import React10__default, { useState, useEffect } from 'react';
|
|
4
4
|
import { usePluginRuntime, getAnalyticsCards, useTranslation, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, hashRouterAdapter, exportCSV } from '@fayz-ai/core';
|
|
5
5
|
import { useOrganizationStore, useShellRailAvailable, useContextualNav, askAI, useAgentSurface, QuickActionsButton, AssistantInvite, RightRailPage } from '@fayz-ai/admin';
|
|
6
|
-
import { AnalyticsContextProvider, useModuleLayout, ModulePage, collectFilters, AnalyticsControls, AnalyticsGrid, Card, FilterChip, DataTable, Button, SubpageHeader, useAnalyticsCard, ToolbarAction, PageHeaderActions, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, useAnalyticsContext, AnalyticsCard, Dropdown, DropdownTrigger, DropdownContent, DropdownItem, Badge, getLucideIcon, Input, Checkbox } from '@fayz-ai/ui';
|
|
6
|
+
import { AnalyticsContextProvider, useModuleLayout, ModulePage, collectFilters, AnalyticsControls, AnalyticsGrid, Card, FilterChip, DataTable, Button, SubpageHeader, useAnalyticsCard, useAnalyticsCardRows, ToolbarAction, PageHeaderActions, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, useAnalyticsContext, AnalyticsCard, Dropdown, DropdownTrigger, DropdownContent, DropdownItem, Badge, getLucideIcon, Input, Checkbox } from '@fayz-ai/ui';
|
|
7
7
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
8
|
import { BarChart3, User, Search, ArrowLeft, Printer, RotateCcw, ChevronUp, ChevronDown, EyeOff, Eye, Trash2, Construction, Download, GripVertical, SlidersHorizontal, Radio, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
9
9
|
|
|
@@ -1321,35 +1321,47 @@ function ReportsListPage() {
|
|
|
1321
1321
|
] });
|
|
1322
1322
|
}
|
|
1323
1323
|
function exportToCsv(columns, data, filename, currency) {
|
|
1324
|
-
const
|
|
1324
|
+
const { columns: cols, rows } = expandBagColumns(columns, data);
|
|
1325
|
+
const visibleCols = cols.filter((c) => c.visible !== false);
|
|
1325
1326
|
const csvColumns = visibleCols.map((col) => ({
|
|
1326
1327
|
key: col.key,
|
|
1327
1328
|
label: col.label,
|
|
1328
1329
|
format: (value) => formatValuePlain(value, col, currency)
|
|
1329
1330
|
}));
|
|
1330
|
-
exportCSV(csvColumns,
|
|
1331
|
+
exportCSV(csvColumns, rows, filename);
|
|
1331
1332
|
}
|
|
1332
|
-
function ExportDropdown({ columns, data, filename, currency, title }) {
|
|
1333
|
+
function ExportDropdown({ columns, data, filename, currency, title, fetchAll }) {
|
|
1333
1334
|
const t = useTranslation();
|
|
1334
1335
|
const [excelAvailable, setExcelAvailable] = useState(false);
|
|
1335
1336
|
const [pdfAvailable, setPdfAvailable] = useState(false);
|
|
1337
|
+
const [busy, setBusy] = useState(false);
|
|
1336
1338
|
useEffect(() => {
|
|
1337
|
-
import('./excel-
|
|
1339
|
+
import('./excel-DSORXL4O.js').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {
|
|
1338
1340
|
});
|
|
1339
|
-
import('./pdf-
|
|
1341
|
+
import('./pdf-DH4CJMV5.js').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {
|
|
1340
1342
|
});
|
|
1341
1343
|
}, []);
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1344
|
+
const withRows = async (write3) => {
|
|
1345
|
+
if (busy) return;
|
|
1346
|
+
setBusy(true);
|
|
1347
|
+
try {
|
|
1348
|
+
const rows = fetchAll ? await fetchAll().catch(() => data) : data;
|
|
1349
|
+
await write3(rows.length ? rows : data);
|
|
1350
|
+
} finally {
|
|
1351
|
+
setBusy(false);
|
|
1352
|
+
}
|
|
1350
1353
|
};
|
|
1354
|
+
const handleCsv = () => withRows((rows) => exportToCsv(columns, rows, filename, currency));
|
|
1355
|
+
const handleExcel = () => withRows(async (rows) => {
|
|
1356
|
+
const { exportToExcel } = await import('./excel-DSORXL4O.js');
|
|
1357
|
+
await exportToExcel(columns, rows, filename, currency);
|
|
1358
|
+
});
|
|
1359
|
+
const handlePdf = () => withRows(async (rows) => {
|
|
1360
|
+
const { exportToPdf } = await import('./pdf-DH4CJMV5.js');
|
|
1361
|
+
await exportToPdf(columns, rows, filename, { title, currency });
|
|
1362
|
+
});
|
|
1351
1363
|
return /* @__PURE__ */ jsxs(Dropdown, { children: [
|
|
1352
|
-
/* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsx(ToolbarAction, { icon: Download, caret: true, children: t("reports.export") }) }),
|
|
1364
|
+
/* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsx(ToolbarAction, { icon: Download, caret: true, disabled: busy, children: t("reports.export") }) }),
|
|
1353
1365
|
/* @__PURE__ */ jsxs(DropdownContent, { align: "end", children: [
|
|
1354
1366
|
/* @__PURE__ */ jsx(DropdownItem, { onClick: handleCsv, children: t("reports.exportCsv") }),
|
|
1355
1367
|
excelAvailable && /* @__PURE__ */ jsx(DropdownItem, { onClick: handleExcel, children: t("reports.exportExcel") }),
|
|
@@ -1409,11 +1421,15 @@ function ViewerBody({ card }) {
|
|
|
1409
1421
|
const { state, setPage, setPageSize } = useAnalyticsContext();
|
|
1410
1422
|
const { result, loading, error, engineMissing } = useAnalyticsCard(card);
|
|
1411
1423
|
const columns = React10__default.useMemo(() => cardColumns(card), [card]);
|
|
1424
|
+
const expanded = React10__default.useMemo(
|
|
1425
|
+
() => expandBagColumns(columns, result?.rows ?? []),
|
|
1426
|
+
[columns, result?.rows]
|
|
1427
|
+
);
|
|
1412
1428
|
const tanstackColumns = React10__default.useMemo(
|
|
1413
|
-
() => reportColumnsToTanstack(columns, currency.code),
|
|
1414
|
-
[columns, currency.code]
|
|
1429
|
+
() => reportColumnsToTanstack(expanded.columns, currency.code),
|
|
1430
|
+
[expanded.columns, currency.code]
|
|
1415
1431
|
);
|
|
1416
|
-
const rows =
|
|
1432
|
+
const rows = expanded.rows;
|
|
1417
1433
|
if (card.available === false) {
|
|
1418
1434
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border border-dashed py-16 text-center", children: [
|
|
1419
1435
|
/* @__PURE__ */ jsx("div", { className: "mb-4 flex h-12 w-12 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsx(Construction, { className: "h-5 w-5 text-muted-foreground" }) }),
|
|
@@ -1504,6 +1520,7 @@ function ViewerActions({ card }) {
|
|
|
1504
1520
|
const { currency, labels } = useAnalyticsConfig();
|
|
1505
1521
|
const { result } = useAnalyticsCard(card);
|
|
1506
1522
|
const columns = React10__default.useMemo(() => cardColumns(card), [card]);
|
|
1523
|
+
const fetchAll = useAnalyticsCardRows(card);
|
|
1507
1524
|
if (!result?.rows?.length) return null;
|
|
1508
1525
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1509
1526
|
/* @__PURE__ */ jsx(
|
|
@@ -1511,6 +1528,7 @@ function ViewerActions({ card }) {
|
|
|
1511
1528
|
{
|
|
1512
1529
|
columns,
|
|
1513
1530
|
data: result.rows,
|
|
1531
|
+
fetchAll,
|
|
1514
1532
|
filename: card.id,
|
|
1515
1533
|
currency: currency.code,
|
|
1516
1534
|
title: card.name
|