@fayz-ai/plugin-reports 0.11.1 → 0.12.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/chunk-4QIHTUXD.js +179 -0
- 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/components/ReportPagination.d.ts.map +1 -1
- package/dist/{excel-KIJTC7OJ.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.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +990 -68
- 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/lib/preferences.d.ts +90 -0
- package/dist/lib/preferences.d.ts.map +1 -0
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/{pdf-7QQ3U5JQ.js → pdf-DH4CJMV5.js} +3 -3
- package/dist/{pdf-7QQ3U5JQ.js.map → pdf-DH4CJMV5.js.map} +1 -1
- package/dist/types.d.ts +9 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/views/AnalyticsAssistant.d.ts.map +1 -1
- package/dist/views/AnalyticsModule.d.ts +1 -1
- package/dist/views/AnalyticsModule.d.ts.map +1 -1
- package/dist/views/AnalyticsReportViewer.d.ts.map +1 -1
- package/dist/views/AnalyticsSettingsPage.d.ts +16 -0
- package/dist/views/AnalyticsSettingsPage.d.ts.map +1 -0
- package/dist/views/AnalyticsSurface.d.ts +22 -3
- package/dist/views/AnalyticsSurface.d.ts.map +1 -1
- package/package.json +6 -5
- package/dist/chunk-4SPBNVQA.js +0 -101
- package/dist/chunk-4SPBNVQA.js.map +0 -1
- package/dist/excel-KIJTC7OJ.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { reportColumnsToTanstack, formatValuePlain } from './chunk-
|
|
2
|
-
import * as
|
|
3
|
-
import
|
|
4
|
-
import { usePluginRuntime, getAnalyticsCards, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, hashRouterAdapter,
|
|
5
|
-
import { useOrganizationStore, useShellRailAvailable, useContextualNav, AssistantInvite } from '@fayz-ai/admin';
|
|
6
|
-
import { AnalyticsContextProvider, useModuleLayout, ModulePage, collectFilters, AnalyticsControls, AnalyticsGrid, Card, FilterChip, DataTable, Button, SubpageHeader, useAnalyticsCard, useAnalyticsContext, AnalyticsCard, Dropdown, DropdownTrigger, DropdownContent, DropdownItem } from '@fayz-ai/ui';
|
|
1
|
+
import { expandBagColumns, reportColumnsToTanstack, formatValuePlain } from './chunk-4QIHTUXD.js';
|
|
2
|
+
import * as React10 from 'react';
|
|
3
|
+
import React10__default, { useState, useEffect } from 'react';
|
|
4
|
+
import { usePluginRuntime, getAnalyticsCards, useTranslation, registerTranslations, createSafeDataProvider, getSupabaseClientOptional, hashRouterAdapter, exportCSV } from '@fayz-ai/core';
|
|
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, 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
|
-
import { BarChart3, User, Search, ArrowLeft, Printer, Construction, Download, Radio, ChevronLeft, ChevronRight } from 'lucide-react';
|
|
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
|
|
|
10
10
|
// src/data/aggregate.ts
|
|
11
11
|
function num(value) {
|
|
@@ -496,6 +496,68 @@ var en = {
|
|
|
496
496
|
"reports.assistant.suggestion.0": "How is my business doing this month?",
|
|
497
497
|
"reports.assistant.suggestion.1": "What's growing and what's falling?",
|
|
498
498
|
"reports.assistant.suggestion.2": "Where am I losing money?",
|
|
499
|
+
"reports.print": "Print",
|
|
500
|
+
"reports.pagination.range": "{{from}}\u2013{{to}} of {{total}}",
|
|
501
|
+
"reports.pagination.empty": "No results",
|
|
502
|
+
"reports.pagination.perPage": "{{size}} / page",
|
|
503
|
+
"reports.settings.nav": "Configure reports",
|
|
504
|
+
"reports.settings.title": "Configure reports",
|
|
505
|
+
"reports.settings.subtitle": "Choose what appears on the overview, in what order, and what each report shows. Saved in this browser.",
|
|
506
|
+
"reports.settings.new": "New report",
|
|
507
|
+
"reports.settings.newHint": "A new report asks a different question of a source the app already exposes.",
|
|
508
|
+
"reports.settings.newWithAIHint": "Describe what you want to see and I build it.",
|
|
509
|
+
"reports.settings.newByHand": "Create by hand",
|
|
510
|
+
"reports.settings.newByHandHint": "Pick the source, the grouping and the measures.",
|
|
511
|
+
"reports.settings.askPersona": "Report analyst",
|
|
512
|
+
"reports.settings.askPitch": "Tell me the report you want.",
|
|
513
|
+
"reports.settings.askDescription": "I know this app's data sources and the fields on each. Describe what you want to see \u2014 I pick the grouping, the measures and the format, build it and open it for you to check.",
|
|
514
|
+
"reports.settings.askSuggestion.0": "Revenue by table, highest first",
|
|
515
|
+
"reports.settings.askSuggestion.1": "How much each station produces per service",
|
|
516
|
+
"reports.settings.askSuggestion.2": "Average ticket by channel, month by month",
|
|
517
|
+
"reports.settings.askPlaceholder": "Describe the report\u2026",
|
|
518
|
+
"reports.settings.orByHand": "or build it by hand",
|
|
519
|
+
"reports.settings.reset": "Reset to defaults",
|
|
520
|
+
"reports.settings.onOverview": "On the overview",
|
|
521
|
+
"reports.settings.onOverviewHint": "Drag to reorder. Width is in grid columns.",
|
|
522
|
+
"reports.settings.offOverview": "Off the overview",
|
|
523
|
+
"reports.settings.offOverviewHint": "Still in the list and still open full size \u2014 they just do not claim a tile.",
|
|
524
|
+
"reports.settings.hidden": "Removed from the catalogue",
|
|
525
|
+
"reports.settings.empty": "No reports on the overview.",
|
|
526
|
+
"reports.settings.custom": "Yours",
|
|
527
|
+
"reports.settings.pin": "Add to the overview",
|
|
528
|
+
"reports.settings.unpin": "Remove from the overview",
|
|
529
|
+
"reports.settings.up": "Move up",
|
|
530
|
+
"reports.settings.down": "Move down",
|
|
531
|
+
"reports.settings.edit": "Adjust",
|
|
532
|
+
"reports.settings.remove": "Remove from the catalogue",
|
|
533
|
+
"reports.settings.restore": "Restore",
|
|
534
|
+
"reports.settings.delete": "Delete",
|
|
535
|
+
"reports.settings.width": "Width",
|
|
536
|
+
"reports.settings.width.1": "Quarter",
|
|
537
|
+
"reports.settings.width.2": "Half",
|
|
538
|
+
"reports.settings.width.3": "Three quarters",
|
|
539
|
+
"reports.settings.width.4": "Full",
|
|
540
|
+
"reports.settings.name": "Name",
|
|
541
|
+
"reports.settings.category": "Category",
|
|
542
|
+
"reports.settings.source": "Data source",
|
|
543
|
+
"reports.settings.sourceHint": "Where the rows come from \u2014 it decides which fields you can use.",
|
|
544
|
+
"reports.settings.viz": "Visualization",
|
|
545
|
+
"reports.settings.dimensions": "Group by",
|
|
546
|
+
"reports.settings.measures": "Measures",
|
|
547
|
+
"reports.settings.resetCard": "Reset this report",
|
|
548
|
+
"reports.settings.needName": "Give the report a name.",
|
|
549
|
+
"reports.settings.needMeasure": "Pick at least one measure.",
|
|
550
|
+
"reports.settings.save": "Create",
|
|
551
|
+
"reports.settings.cancel": "Cancel",
|
|
552
|
+
"reports.settings.done": "Done",
|
|
553
|
+
"reports.viz.kpi": "Number",
|
|
554
|
+
"reports.viz.line": "Line",
|
|
555
|
+
"reports.viz.bar": "Bars",
|
|
556
|
+
"reports.viz.area": "Area",
|
|
557
|
+
"reports.viz.donut": "Donut",
|
|
558
|
+
"reports.viz.breakdown": "Ranking",
|
|
559
|
+
"reports.viz.table": "Table",
|
|
560
|
+
"reports.viz.funnel": "Funnel",
|
|
499
561
|
"reports.unavailableTitle": "Coming soon",
|
|
500
562
|
"reports.unavailableDescription": "This report is not available yet. It will be enabled in a future update."
|
|
501
563
|
};
|
|
@@ -535,6 +597,68 @@ var ptBR = {
|
|
|
535
597
|
"reports.assistant.suggestion.0": "Como est\xE1 meu neg\xF3cio neste m\xEAs?",
|
|
536
598
|
"reports.assistant.suggestion.1": "O que est\xE1 crescendo e o que est\xE1 caindo?",
|
|
537
599
|
"reports.assistant.suggestion.2": "Onde estou perdendo dinheiro?",
|
|
600
|
+
"reports.print": "Imprimir",
|
|
601
|
+
"reports.pagination.range": "{{from}}\u2013{{to}} de {{total}}",
|
|
602
|
+
"reports.pagination.empty": "Nenhum resultado",
|
|
603
|
+
"reports.pagination.perPage": "{{size}} / p\xE1gina",
|
|
604
|
+
"reports.settings.nav": "Configurar relat\xF3rios",
|
|
605
|
+
"reports.settings.title": "Configurar relat\xF3rios",
|
|
606
|
+
"reports.settings.subtitle": "Escolha o que aparece no painel, em que ordem e o que cada relat\xF3rio mostra. As escolhas ficam neste navegador.",
|
|
607
|
+
"reports.settings.new": "Novo relat\xF3rio",
|
|
608
|
+
"reports.settings.newHint": "Um relat\xF3rio novo pergunta outra coisa a uma fonte que o app j\xE1 exp\xF5e.",
|
|
609
|
+
"reports.settings.newWithAIHint": "Descreva o que quer ver e eu monto.",
|
|
610
|
+
"reports.settings.newByHand": "Criar manualmente",
|
|
611
|
+
"reports.settings.newByHandHint": "Escolher fonte, agrupamento e medidas.",
|
|
612
|
+
"reports.settings.askPersona": "Analista de relat\xF3rios",
|
|
613
|
+
"reports.settings.askPitch": "Diga o relat\xF3rio que voc\xEA quer.",
|
|
614
|
+
"reports.settings.askDescription": "Conhe\xE7o as fontes deste app e os campos de cada uma. Descreva o que quer ver \u2014 eu escolho o agrupamento, as medidas e o formato, monto e abro para voc\xEA conferir.",
|
|
615
|
+
"reports.settings.askSuggestion.0": "Receita por mesa, do maior para o menor",
|
|
616
|
+
"reports.settings.askSuggestion.1": "Quanto cada pra\xE7a produz por turno",
|
|
617
|
+
"reports.settings.askSuggestion.2": "Ticket m\xE9dio por canal, m\xEAs a m\xEAs",
|
|
618
|
+
"reports.settings.askPlaceholder": "Descreva o relat\xF3rio\u2026",
|
|
619
|
+
"reports.settings.orByHand": "ou monte \xE0 m\xE3o",
|
|
620
|
+
"reports.settings.reset": "Restaurar padr\xF5es",
|
|
621
|
+
"reports.settings.onOverview": "No painel",
|
|
622
|
+
"reports.settings.onOverviewHint": "Arraste para reordenar. A largura \xE9 em colunas da grade.",
|
|
623
|
+
"reports.settings.offOverview": "Fora do painel",
|
|
624
|
+
"reports.settings.offOverviewHint": "Continuam na lista e abrem inteiros; s\xF3 n\xE3o ocupam um bloco do painel.",
|
|
625
|
+
"reports.settings.hidden": "Removidos do cat\xE1logo",
|
|
626
|
+
"reports.settings.empty": "Nenhum relat\xF3rio no painel.",
|
|
627
|
+
"reports.settings.custom": "Seu",
|
|
628
|
+
"reports.settings.pin": "P\xF4r no painel",
|
|
629
|
+
"reports.settings.unpin": "Tirar do painel",
|
|
630
|
+
"reports.settings.up": "Subir",
|
|
631
|
+
"reports.settings.down": "Descer",
|
|
632
|
+
"reports.settings.edit": "Ajustar",
|
|
633
|
+
"reports.settings.remove": "Remover do cat\xE1logo",
|
|
634
|
+
"reports.settings.restore": "Devolver",
|
|
635
|
+
"reports.settings.delete": "Excluir",
|
|
636
|
+
"reports.settings.width": "Largura",
|
|
637
|
+
"reports.settings.width.1": "Um quarto",
|
|
638
|
+
"reports.settings.width.2": "Metade",
|
|
639
|
+
"reports.settings.width.3": "Tr\xEAs quartos",
|
|
640
|
+
"reports.settings.width.4": "Inteiro",
|
|
641
|
+
"reports.settings.name": "Nome",
|
|
642
|
+
"reports.settings.category": "Categoria",
|
|
643
|
+
"reports.settings.source": "Fonte de dados",
|
|
644
|
+
"reports.settings.sourceHint": "De onde v\xEAm as linhas \u2014 \xE9 ela que decide quais campos voc\xEA pode usar.",
|
|
645
|
+
"reports.settings.viz": "Visualiza\xE7\xE3o",
|
|
646
|
+
"reports.settings.dimensions": "Agrupar por",
|
|
647
|
+
"reports.settings.measures": "Medidas",
|
|
648
|
+
"reports.settings.resetCard": "Restaurar este relat\xF3rio",
|
|
649
|
+
"reports.settings.needName": "D\xEA um nome ao relat\xF3rio.",
|
|
650
|
+
"reports.settings.needMeasure": "Escolha ao menos uma medida.",
|
|
651
|
+
"reports.settings.save": "Criar",
|
|
652
|
+
"reports.settings.cancel": "Cancelar",
|
|
653
|
+
"reports.settings.done": "Pronto",
|
|
654
|
+
"reports.viz.kpi": "N\xFAmero",
|
|
655
|
+
"reports.viz.line": "Linha",
|
|
656
|
+
"reports.viz.bar": "Barras",
|
|
657
|
+
"reports.viz.area": "\xC1rea",
|
|
658
|
+
"reports.viz.donut": "Rosca",
|
|
659
|
+
"reports.viz.breakdown": "Ranking",
|
|
660
|
+
"reports.viz.table": "Tabela",
|
|
661
|
+
"reports.viz.funnel": "Funil",
|
|
538
662
|
"reports.unavailableTitle": "Em breve",
|
|
539
663
|
"reports.unavailableDescription": "Este relat\xF3rio ainda n\xE3o est\xE1 dispon\xEDvel. Ser\xE1 habilitado em uma atualiza\xE7\xE3o futura."
|
|
540
664
|
};
|
|
@@ -625,8 +749,8 @@ function markViewed(cardId) {
|
|
|
625
749
|
for (const listener of listeners) listener(next);
|
|
626
750
|
}
|
|
627
751
|
function useLastViewed() {
|
|
628
|
-
const [map, setMap] =
|
|
629
|
-
|
|
752
|
+
const [map, setMap] = React10.useState(() => read());
|
|
753
|
+
React10.useEffect(() => {
|
|
630
754
|
listeners.add(setMap);
|
|
631
755
|
return () => {
|
|
632
756
|
listeners.delete(setMap);
|
|
@@ -634,27 +758,221 @@ function useLastViewed() {
|
|
|
634
758
|
}, []);
|
|
635
759
|
return map;
|
|
636
760
|
}
|
|
761
|
+
var KEY2 = "fayz.analytics.prefs";
|
|
762
|
+
var EMPTY = { version: 1, cards: {}, custom: [] };
|
|
763
|
+
function read2() {
|
|
764
|
+
if (typeof window === "undefined") return EMPTY;
|
|
765
|
+
try {
|
|
766
|
+
const raw = window.localStorage.getItem(KEY2);
|
|
767
|
+
if (!raw) return EMPTY;
|
|
768
|
+
const parsed = JSON.parse(raw);
|
|
769
|
+
return {
|
|
770
|
+
version: 1,
|
|
771
|
+
cards: parsed.cards && typeof parsed.cards === "object" ? parsed.cards : {},
|
|
772
|
+
custom: Array.isArray(parsed.custom) ? parsed.custom : []
|
|
773
|
+
};
|
|
774
|
+
} catch {
|
|
775
|
+
return EMPTY;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
function write2(prefs) {
|
|
779
|
+
if (typeof window === "undefined") return;
|
|
780
|
+
try {
|
|
781
|
+
const empty = Object.keys(prefs.cards).length === 0 && prefs.custom.length === 0;
|
|
782
|
+
if (empty) window.localStorage.removeItem(KEY2);
|
|
783
|
+
else window.localStorage.setItem(KEY2, JSON.stringify(prefs));
|
|
784
|
+
} catch {
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
788
|
+
function publish(next) {
|
|
789
|
+
write2(next);
|
|
790
|
+
for (const listener of listeners2) listener(next);
|
|
791
|
+
}
|
|
792
|
+
function useAnalyticsPrefs() {
|
|
793
|
+
const [prefs, setPrefs] = React10.useState(() => read2());
|
|
794
|
+
React10.useEffect(() => {
|
|
795
|
+
listeners2.add(setPrefs);
|
|
796
|
+
return () => {
|
|
797
|
+
listeners2.delete(setPrefs);
|
|
798
|
+
};
|
|
799
|
+
}, []);
|
|
800
|
+
return prefs;
|
|
801
|
+
}
|
|
802
|
+
function setCardPref(cardId, patch) {
|
|
803
|
+
const current = read2();
|
|
804
|
+
const merged = { ...current.cards[cardId], ...patch };
|
|
805
|
+
const meaningful = Object.entries(merged).filter(([, v]) => v !== void 0 && v !== null);
|
|
806
|
+
const cards = { ...current.cards };
|
|
807
|
+
if (meaningful.length === 0) delete cards[cardId];
|
|
808
|
+
else cards[cardId] = Object.fromEntries(meaningful);
|
|
809
|
+
publish({ ...current, cards });
|
|
810
|
+
}
|
|
811
|
+
function clearCardPref(cardId) {
|
|
812
|
+
const current = read2();
|
|
813
|
+
const cards = { ...current.cards };
|
|
814
|
+
delete cards[cardId];
|
|
815
|
+
publish({ ...current, cards });
|
|
816
|
+
}
|
|
817
|
+
function setOverviewOrder(ids) {
|
|
818
|
+
const current = read2();
|
|
819
|
+
const cards = { ...current.cards };
|
|
820
|
+
ids.forEach((id, index) => {
|
|
821
|
+
cards[id] = { ...cards[id], order: index, pinned: cards[id]?.pinned ?? true };
|
|
822
|
+
});
|
|
823
|
+
publish({ ...current, cards });
|
|
824
|
+
}
|
|
825
|
+
function saveCustomReport(report) {
|
|
826
|
+
const current = read2();
|
|
827
|
+
const custom = current.custom.some((r) => r.id === report.id) ? current.custom.map((r) => r.id === report.id ? report : r) : [...current.custom, report];
|
|
828
|
+
publish({ ...current, custom });
|
|
829
|
+
}
|
|
830
|
+
function deleteCustomReport(id) {
|
|
831
|
+
const current = read2();
|
|
832
|
+
const cards = { ...current.cards };
|
|
833
|
+
delete cards[id];
|
|
834
|
+
publish({ ...current, cards, custom: current.custom.filter((r) => r.id !== id) });
|
|
835
|
+
}
|
|
836
|
+
function resetAnalyticsPrefs() {
|
|
837
|
+
publish({ ...EMPTY });
|
|
838
|
+
}
|
|
839
|
+
function sourceKey(card) {
|
|
840
|
+
const source = card.query.source;
|
|
841
|
+
if (source.kind === "fn") return card.grain ? `fn:${card.grain}` : `fn:${card.id}`;
|
|
842
|
+
return `${source.kind}:${source.name ?? card.id}`;
|
|
843
|
+
}
|
|
844
|
+
function analyticsSources(pool) {
|
|
845
|
+
const groups = /* @__PURE__ */ new Map();
|
|
846
|
+
for (const card of pool) {
|
|
847
|
+
const key = sourceKey(card);
|
|
848
|
+
const list = groups.get(key);
|
|
849
|
+
if (list) list.push(card);
|
|
850
|
+
else groups.set(key, [card]);
|
|
851
|
+
}
|
|
852
|
+
const out = [];
|
|
853
|
+
for (const [key, cards] of groups) {
|
|
854
|
+
const base = cards.find((c) => (c.query.measures?.length ?? 0) > 0);
|
|
855
|
+
if (!base) continue;
|
|
856
|
+
const palette = fieldPalette(pool, base);
|
|
857
|
+
if (palette.measures.length === 0) continue;
|
|
858
|
+
out.push({ key, base, label: base.name, ...palette });
|
|
859
|
+
}
|
|
860
|
+
return out;
|
|
861
|
+
}
|
|
862
|
+
function fieldPalette(pool, base) {
|
|
863
|
+
const key = sourceKey(base);
|
|
864
|
+
const dimensions = /* @__PURE__ */ new Map();
|
|
865
|
+
const measures = /* @__PURE__ */ new Map();
|
|
866
|
+
for (const card of pool) {
|
|
867
|
+
if (sourceKey(card) !== key) continue;
|
|
868
|
+
for (const d of card.query.dimensions ?? []) {
|
|
869
|
+
if (!d.hidden && !dimensions.has(d.key)) dimensions.set(d.key, d);
|
|
870
|
+
}
|
|
871
|
+
for (const m of card.query.measures ?? []) {
|
|
872
|
+
if (!measures.has(m.key)) measures.set(m.key, m);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
return { dimensions: [...dimensions.values()], measures: [...measures.values()] };
|
|
876
|
+
}
|
|
877
|
+
function withRatioOperands(kept, all) {
|
|
878
|
+
const set = new Set(kept);
|
|
879
|
+
for (const key of kept) {
|
|
880
|
+
const measure = all.find((m) => m.key === key);
|
|
881
|
+
for (const operand of measure?.ratioOf ?? []) set.add(operand);
|
|
882
|
+
}
|
|
883
|
+
return set;
|
|
884
|
+
}
|
|
885
|
+
function applyPref(card, pref) {
|
|
886
|
+
if (!pref) return card;
|
|
887
|
+
const next = { ...card, query: { ...card.query }, viz: { ...card.viz } };
|
|
888
|
+
if (pref.name) next.name = pref.name;
|
|
889
|
+
if (pref.viz) next.viz.type = pref.viz;
|
|
890
|
+
if (pref.measures?.length) {
|
|
891
|
+
const all = card.query.measures ?? [];
|
|
892
|
+
const keep = withRatioOperands(pref.measures, all);
|
|
893
|
+
next.query.measures = all.filter((m) => keep.has(m.key));
|
|
894
|
+
if (card.viz.measures?.length) {
|
|
895
|
+
next.viz.measures = card.viz.measures.filter((k) => keep.has(k));
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
if (pref.dimensions?.length) {
|
|
899
|
+
const keep = new Set(pref.dimensions);
|
|
900
|
+
next.query.dimensions = (card.query.dimensions ?? []).filter((d) => d.hidden || keep.has(d.key));
|
|
901
|
+
}
|
|
902
|
+
const placed = pref.pinned ?? Boolean(card.dashboard && card.dashboard.defaultVisible !== false);
|
|
903
|
+
if (!placed) {
|
|
904
|
+
next.dashboard = void 0;
|
|
905
|
+
} else {
|
|
906
|
+
next.dashboard = {
|
|
907
|
+
...card.dashboard,
|
|
908
|
+
defaultVisible: true,
|
|
909
|
+
order: pref.order ?? card.dashboard?.order ?? 999,
|
|
910
|
+
span: pref.span ?? card.dashboard?.span
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
return next;
|
|
914
|
+
}
|
|
915
|
+
function resolveCustomReport(report, pool) {
|
|
916
|
+
const base = pool.find((c) => c.id === report.baseId);
|
|
917
|
+
if (!base) return null;
|
|
918
|
+
const palette = fieldPalette(pool, base);
|
|
919
|
+
const dimensions = report.dimensions.map((key) => palette.dimensions.find((d) => d.key === key)).filter((d) => Boolean(d));
|
|
920
|
+
const keep = withRatioOperands(report.measures, palette.measures);
|
|
921
|
+
const measures = palette.measures.filter((m) => keep.has(m.key));
|
|
922
|
+
return {
|
|
923
|
+
id: report.id,
|
|
924
|
+
name: report.name,
|
|
925
|
+
description: report.description,
|
|
926
|
+
icon: report.icon ?? "FileText",
|
|
927
|
+
category: report.category,
|
|
928
|
+
createdBy: report.createdBy,
|
|
929
|
+
grain: base.grain,
|
|
930
|
+
available: base.available,
|
|
931
|
+
compare: base.compare,
|
|
932
|
+
permission: base.permission,
|
|
933
|
+
query: {
|
|
934
|
+
source: base.query.source,
|
|
935
|
+
dateColumn: base.query.dateColumn,
|
|
936
|
+
dimensions,
|
|
937
|
+
measures,
|
|
938
|
+
filters: base.query.filters,
|
|
939
|
+
defaultSort: report.measures[0] ?? dimensions[0]?.key,
|
|
940
|
+
defaultSortDir: "desc"
|
|
941
|
+
},
|
|
942
|
+
viz: { type: report.viz }
|
|
943
|
+
};
|
|
944
|
+
}
|
|
945
|
+
function resolveCards(catalogue, prefs) {
|
|
946
|
+
const custom = prefs.custom.map((report) => resolveCustomReport(report, catalogue)).filter((c) => Boolean(c));
|
|
947
|
+
return [...catalogue, ...custom].filter((card) => !prefs.cards[card.id]?.hidden).map((card) => applyPref(card, prefs.cards[card.id]));
|
|
948
|
+
}
|
|
637
949
|
var ANALYTICS_BASE = "/analytics";
|
|
638
950
|
var REPORTS_BASE = `${ANALYTICS_BASE}/reports`;
|
|
639
|
-
var
|
|
951
|
+
var SETTINGS_ROUTE = `${ANALYTICS_BASE}/settings`;
|
|
952
|
+
var ConfigContext = React10__default.createContext(null);
|
|
640
953
|
function useAnalyticsConfig() {
|
|
641
|
-
const ctx =
|
|
954
|
+
const ctx = React10__default.useContext(ConfigContext);
|
|
642
955
|
if (!ctx) throw new Error("useAnalyticsConfig must be used inside <AnalyticsSurface>");
|
|
643
956
|
return ctx;
|
|
644
957
|
}
|
|
645
|
-
function
|
|
958
|
+
function useAnalyticsCatalogue() {
|
|
646
959
|
const runtime = usePluginRuntime();
|
|
647
960
|
const { cards, createdBy } = useAnalyticsConfig();
|
|
648
|
-
return
|
|
961
|
+
return React10__default.useMemo(() => {
|
|
649
962
|
const byId = /* @__PURE__ */ new Map();
|
|
650
963
|
for (const card of getAnalyticsCards(runtime)) byId.set(card.id, card);
|
|
651
964
|
for (const card of cards) byId.set(card.id, card);
|
|
652
965
|
return [...byId.values()].map((c) => c.createdBy ? c : { ...c, createdBy });
|
|
653
966
|
}, [runtime, cards, createdBy]);
|
|
654
967
|
}
|
|
968
|
+
function useAnalyticsCards() {
|
|
969
|
+
const catalogue = useAnalyticsCatalogue();
|
|
970
|
+
const prefs = useAnalyticsPrefs();
|
|
971
|
+
return React10__default.useMemo(() => resolveCards(catalogue, prefs), [catalogue, prefs]);
|
|
972
|
+
}
|
|
655
973
|
function useOverviewCards() {
|
|
656
974
|
const all = useAnalyticsCards();
|
|
657
|
-
return
|
|
975
|
+
return React10__default.useMemo(
|
|
658
976
|
() => all.filter((c) => c.dashboard && c.dashboard.defaultVisible !== false).sort((a, b) => (a.dashboard?.order ?? 0) - (b.dashboard?.order ?? 0)),
|
|
659
977
|
[all]
|
|
660
978
|
);
|
|
@@ -685,7 +1003,7 @@ function navigateAnalytics(path) {
|
|
|
685
1003
|
hashRouterAdapter().navigate(path);
|
|
686
1004
|
}
|
|
687
1005
|
function AnalyticsSurface({ config, provider, children }) {
|
|
688
|
-
const openCard =
|
|
1006
|
+
const openCard = React10__default.useCallback((card) => {
|
|
689
1007
|
markViewed(card.id);
|
|
690
1008
|
navigateAnalytics(`${REPORTS_BASE}/${encodeURIComponent(card.id)}`);
|
|
691
1009
|
}, []);
|
|
@@ -706,7 +1024,7 @@ function AnalyticsSurface({ config, provider, children }) {
|
|
|
706
1024
|
function AnalyticsAssistant() {
|
|
707
1025
|
const { assistant } = useAnalyticsConfig();
|
|
708
1026
|
const t = useTranslation();
|
|
709
|
-
const suggestions =
|
|
1027
|
+
const suggestions = React10__default.useMemo(
|
|
710
1028
|
() => assistant?.suggestions ?? [0, 1, 2].map((i) => ({ label: t(`reports.assistant.suggestion.${i}`) })),
|
|
711
1029
|
[assistant?.suggestions, t]
|
|
712
1030
|
);
|
|
@@ -716,6 +1034,7 @@ function AnalyticsAssistant() {
|
|
|
716
1034
|
{
|
|
717
1035
|
id: "analytics",
|
|
718
1036
|
auto: assistant.auto ?? false,
|
|
1037
|
+
role: assistant.role ?? "growth",
|
|
719
1038
|
persona: assistant.persona ?? t("reports.assistant.persona"),
|
|
720
1039
|
headline: assistant.headline ?? t("reports.assistant.headline"),
|
|
721
1040
|
pitch: assistant.pitch ?? t("reports.assistant.pitch"),
|
|
@@ -739,7 +1058,7 @@ function useAnalyticsTree() {
|
|
|
739
1058
|
const t = useTranslation();
|
|
740
1059
|
const { labels, showLiveView } = useAnalyticsConfig();
|
|
741
1060
|
const cards = useAnalyticsCards();
|
|
742
|
-
return
|
|
1061
|
+
return React10__default.useMemo(() => ({
|
|
743
1062
|
title: labels.analytics,
|
|
744
1063
|
// Only the overview. The searchable catalogue (/analytics/reports) is still
|
|
745
1064
|
// routed — bookmarks and the legacy /reports link resolve — but it is not
|
|
@@ -749,7 +1068,16 @@ function useAnalyticsTree() {
|
|
|
749
1068
|
{ id: "overview", label: labels.overview ?? t("reports.overview"), icon: "BarChart3", route: ANALYTICS_BASE, view: "overview" }
|
|
750
1069
|
],
|
|
751
1070
|
groups: groupCards(cards),
|
|
752
|
-
live: showLiveView ? { id: "live", label: labels.liveView, icon: "Radio", route: `${ANALYTICS_BASE}/live`, view: "live" } : null
|
|
1071
|
+
live: showLiveView ? { id: "live", label: labels.liveView, icon: "Radio", route: `${ANALYTICS_BASE}/live`, view: "live" } : null,
|
|
1072
|
+
// Depois dos relatórios, não antes: é a tela que fala SOBRE a lista, e uma
|
|
1073
|
+
// engrenagem no topo empurra para baixo o que a pessoa veio ver.
|
|
1074
|
+
settings: {
|
|
1075
|
+
id: "settings",
|
|
1076
|
+
label: labels.settings ?? t("reports.settings.nav"),
|
|
1077
|
+
icon: "SlidersHorizontal",
|
|
1078
|
+
route: SETTINGS_ROUTE,
|
|
1079
|
+
view: "settings"
|
|
1080
|
+
}
|
|
753
1081
|
}), [labels, showLiveView, cards, t]);
|
|
754
1082
|
}
|
|
755
1083
|
function AnalyticsModule({ view, cardId, children }) {
|
|
@@ -758,7 +1086,7 @@ function AnalyticsModule({ view, cardId, children }) {
|
|
|
758
1086
|
const activeId = cardId ? decodeURIComponent(cardId) : void 0;
|
|
759
1087
|
const shellRail = useShellRailAvailable();
|
|
760
1088
|
const claimsRail = shellRail ?? variant !== "rail";
|
|
761
|
-
const entries =
|
|
1089
|
+
const entries = React10__default.useMemo(() => [
|
|
762
1090
|
...tree.fixed.map((f) => ({
|
|
763
1091
|
id: `analytics:${f.id}`,
|
|
764
1092
|
label: f.label,
|
|
@@ -782,10 +1110,17 @@ function AnalyticsModule({ view, cardId, children }) {
|
|
|
782
1110
|
icon: tree.live.icon,
|
|
783
1111
|
route: tree.live.route,
|
|
784
1112
|
section: "main"
|
|
785
|
-
}] : []
|
|
1113
|
+
}] : [],
|
|
1114
|
+
{
|
|
1115
|
+
id: "analytics:settings",
|
|
1116
|
+
label: tree.settings.label,
|
|
1117
|
+
icon: tree.settings.icon,
|
|
1118
|
+
route: tree.settings.route,
|
|
1119
|
+
section: "main"
|
|
1120
|
+
}
|
|
786
1121
|
], [tree]);
|
|
787
1122
|
useContextualNav(claimsRail ? { base: ANALYTICS_BASE, entries, label: tree.title } : null);
|
|
788
|
-
const nav =
|
|
1123
|
+
const nav = React10__default.useMemo(() => {
|
|
789
1124
|
if (claimsRail) return [];
|
|
790
1125
|
return [
|
|
791
1126
|
...tree.fixed.map((f) => ({
|
|
@@ -811,7 +1146,14 @@ function AnalyticsModule({ view, cardId, children }) {
|
|
|
811
1146
|
icon: tree.live.icon,
|
|
812
1147
|
active: view === "live",
|
|
813
1148
|
onClick: () => navigateAnalytics(tree.live.route)
|
|
814
|
-
}] : []
|
|
1149
|
+
}] : [],
|
|
1150
|
+
{
|
|
1151
|
+
id: tree.settings.id,
|
|
1152
|
+
label: tree.settings.label,
|
|
1153
|
+
icon: tree.settings.icon,
|
|
1154
|
+
active: view === "settings",
|
|
1155
|
+
onClick: () => navigateAnalytics(tree.settings.route)
|
|
1156
|
+
}
|
|
815
1157
|
];
|
|
816
1158
|
}, [tree, view, activeId, claimsRail]);
|
|
817
1159
|
return /* @__PURE__ */ jsxs(
|
|
@@ -832,7 +1174,7 @@ function AnalyticsModule({ view, cardId, children }) {
|
|
|
832
1174
|
function AnalyticsOverview() {
|
|
833
1175
|
const { labels } = useAnalyticsConfig();
|
|
834
1176
|
const cards = useOverviewCards();
|
|
835
|
-
const filters =
|
|
1177
|
+
const filters = React10__default.useMemo(() => collectFilters(cards), [cards]);
|
|
836
1178
|
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
837
1179
|
/* @__PURE__ */ jsx(AnalyticsControls, { filters }),
|
|
838
1180
|
cards.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border border-dashed py-16 text-center", children: [
|
|
@@ -852,18 +1194,18 @@ function ReportsListPage() {
|
|
|
852
1194
|
const { labels, currency, showHeader } = useAnalyticsConfig();
|
|
853
1195
|
const cards = useAnalyticsCards();
|
|
854
1196
|
const lastViewed = useLastViewed();
|
|
855
|
-
const [search, setSearch] =
|
|
856
|
-
const [category, setCategory] =
|
|
857
|
-
const [author, setAuthor] =
|
|
858
|
-
const categoryOptions =
|
|
1197
|
+
const [search, setSearch] = React10__default.useState("");
|
|
1198
|
+
const [category, setCategory] = React10__default.useState();
|
|
1199
|
+
const [author, setAuthor] = React10__default.useState();
|
|
1200
|
+
const categoryOptions = React10__default.useMemo(
|
|
859
1201
|
() => [...new Set(cards.map((c) => c.category))].sort((a, b) => a.localeCompare(b)).map((c) => ({ value: c, label: c })),
|
|
860
1202
|
[cards]
|
|
861
1203
|
);
|
|
862
|
-
const authorOptions =
|
|
1204
|
+
const authorOptions = React10__default.useMemo(
|
|
863
1205
|
() => [...new Set(cards.map((c) => c.createdBy ?? "\u2014"))].sort((a, b) => a.localeCompare(b)).map((a) => ({ value: a, label: a })),
|
|
864
1206
|
[cards]
|
|
865
1207
|
);
|
|
866
|
-
const rows =
|
|
1208
|
+
const rows = React10__default.useMemo(() => {
|
|
867
1209
|
const q = search.trim().toLowerCase();
|
|
868
1210
|
return cards.filter((card) => {
|
|
869
1211
|
if (category && card.category !== category) return false;
|
|
@@ -872,7 +1214,7 @@ function ReportsListPage() {
|
|
|
872
1214
|
return card.name.toLowerCase().includes(q) || card.description?.toLowerCase().includes(q) || card.category.toLowerCase().includes(q);
|
|
873
1215
|
});
|
|
874
1216
|
}, [cards, search, category, author]);
|
|
875
|
-
const columns =
|
|
1217
|
+
const columns = React10__default.useMemo(() => [
|
|
876
1218
|
{
|
|
877
1219
|
id: "name",
|
|
878
1220
|
accessorFn: (card) => card.name,
|
|
@@ -979,38 +1321,47 @@ function ReportsListPage() {
|
|
|
979
1321
|
] });
|
|
980
1322
|
}
|
|
981
1323
|
function exportToCsv(columns, data, filename, currency) {
|
|
982
|
-
const
|
|
1324
|
+
const { columns: cols, rows } = expandBagColumns(columns, data);
|
|
1325
|
+
const visibleCols = cols.filter((c) => c.visible !== false);
|
|
983
1326
|
const csvColumns = visibleCols.map((col) => ({
|
|
984
1327
|
key: col.key,
|
|
985
1328
|
label: col.label,
|
|
986
1329
|
format: (value) => formatValuePlain(value, col, currency)
|
|
987
1330
|
}));
|
|
988
|
-
exportCSV(csvColumns,
|
|
1331
|
+
exportCSV(csvColumns, rows, filename);
|
|
989
1332
|
}
|
|
990
|
-
function ExportDropdown({ columns, data, filename, currency, title }) {
|
|
1333
|
+
function ExportDropdown({ columns, data, filename, currency, title, fetchAll }) {
|
|
991
1334
|
const t = useTranslation();
|
|
992
1335
|
const [excelAvailable, setExcelAvailable] = useState(false);
|
|
993
1336
|
const [pdfAvailable, setPdfAvailable] = useState(false);
|
|
1337
|
+
const [busy, setBusy] = useState(false);
|
|
994
1338
|
useEffect(() => {
|
|
995
|
-
import('./excel-
|
|
1339
|
+
import('./excel-DSORXL4O.js').then((m) => m.isExcelAvailable()).then(setExcelAvailable).catch(() => {
|
|
996
1340
|
});
|
|
997
|
-
import('./pdf-
|
|
1341
|
+
import('./pdf-DH4CJMV5.js').then((m) => m.isPdfAvailable()).then(setPdfAvailable).catch(() => {
|
|
998
1342
|
});
|
|
999
1343
|
}, []);
|
|
1000
|
-
const
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
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
|
+
}
|
|
1008
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
|
+
});
|
|
1009
1363
|
return /* @__PURE__ */ jsxs(Dropdown, { children: [
|
|
1010
|
-
/* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */
|
|
1011
|
-
/* @__PURE__ */ jsx(Download, { className: "h-4 w-4 mr-1.5" }),
|
|
1012
|
-
t("reports.export")
|
|
1013
|
-
] }) }),
|
|
1364
|
+
/* @__PURE__ */ jsx(DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsx(ToolbarAction, { icon: Download, caret: true, disabled: busy, children: t("reports.export") }) }),
|
|
1014
1365
|
/* @__PURE__ */ jsxs(DropdownContent, { align: "end", children: [
|
|
1015
1366
|
/* @__PURE__ */ jsx(DropdownItem, { onClick: handleCsv, children: t("reports.exportCsv") }),
|
|
1016
1367
|
excelAvailable && /* @__PURE__ */ jsx(DropdownItem, { onClick: handleExcel, children: t("reports.exportExcel") }),
|
|
@@ -1019,11 +1370,12 @@ function ExportDropdown({ columns, data, filename, currency, title }) {
|
|
|
1019
1370
|
] });
|
|
1020
1371
|
}
|
|
1021
1372
|
function ReportPagination({ page: page2, pageSize, total, onPageChange, onPageSizeChange }) {
|
|
1373
|
+
const t = useTranslation();
|
|
1022
1374
|
const totalPages = Math.max(1, Math.ceil(total / pageSize));
|
|
1023
1375
|
const from = (page2 - 1) * pageSize + 1;
|
|
1024
1376
|
const to = Math.min(page2 * pageSize, total);
|
|
1025
1377
|
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t px-4 py-3", children: [
|
|
1026
|
-
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: total > 0 ?
|
|
1378
|
+
/* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: total > 0 ? t("reports.pagination.range", { from, to, total }) : t("reports.pagination.empty") }),
|
|
1027
1379
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1028
1380
|
/* @__PURE__ */ jsx(
|
|
1029
1381
|
"select",
|
|
@@ -1031,10 +1383,7 @@ function ReportPagination({ page: page2, pageSize, total, onPageChange, onPageSi
|
|
|
1031
1383
|
value: pageSize,
|
|
1032
1384
|
onChange: (e) => onPageSizeChange(Number(e.target.value)),
|
|
1033
1385
|
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",
|
|
1034
|
-
children: [25, 50, 100].map((size) => /* @__PURE__ */
|
|
1035
|
-
size,
|
|
1036
|
-
" / page"
|
|
1037
|
-
] }, size))
|
|
1386
|
+
children: [25, 50, 100].map((size) => /* @__PURE__ */ jsx("option", { value: size, children: t("reports.pagination.perPage", { size }) }, size))
|
|
1038
1387
|
}
|
|
1039
1388
|
),
|
|
1040
1389
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
@@ -1071,12 +1420,16 @@ function ViewerBody({ card }) {
|
|
|
1071
1420
|
const { labels, currency } = useAnalyticsConfig();
|
|
1072
1421
|
const { state, setPage, setPageSize } = useAnalyticsContext();
|
|
1073
1422
|
const { result, loading, error, engineMissing } = useAnalyticsCard(card);
|
|
1074
|
-
const columns =
|
|
1075
|
-
const
|
|
1076
|
-
() =>
|
|
1077
|
-
[columns,
|
|
1423
|
+
const columns = React10__default.useMemo(() => cardColumns(card), [card]);
|
|
1424
|
+
const expanded = React10__default.useMemo(
|
|
1425
|
+
() => expandBagColumns(columns, result?.rows ?? []),
|
|
1426
|
+
[columns, result?.rows]
|
|
1078
1427
|
);
|
|
1079
|
-
const
|
|
1428
|
+
const tanstackColumns = React10__default.useMemo(
|
|
1429
|
+
() => reportColumnsToTanstack(expanded.columns, currency.code),
|
|
1430
|
+
[expanded.columns, currency.code]
|
|
1431
|
+
);
|
|
1432
|
+
const rows = expanded.rows;
|
|
1080
1433
|
if (card.available === false) {
|
|
1081
1434
|
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border border-dashed py-16 text-center", children: [
|
|
1082
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" }) }),
|
|
@@ -1129,7 +1482,7 @@ function AnalyticsReportViewer({ cardId }) {
|
|
|
1129
1482
|
const { labels } = useAnalyticsConfig();
|
|
1130
1483
|
const cards = useAnalyticsCards();
|
|
1131
1484
|
const card = cards.find((c) => c.id === decodeURIComponent(cardId ?? ""));
|
|
1132
|
-
|
|
1485
|
+
React10__default.useEffect(() => {
|
|
1133
1486
|
if (card) markViewed(card.id);
|
|
1134
1487
|
}, [card?.id]);
|
|
1135
1488
|
if (!card) {
|
|
@@ -1164,9 +1517,10 @@ function AnalyticsReportViewer({ cardId }) {
|
|
|
1164
1517
|
] });
|
|
1165
1518
|
}
|
|
1166
1519
|
function ViewerActions({ card }) {
|
|
1167
|
-
const { currency } = useAnalyticsConfig();
|
|
1520
|
+
const { currency, labels } = useAnalyticsConfig();
|
|
1168
1521
|
const { result } = useAnalyticsCard(card);
|
|
1169
|
-
const columns =
|
|
1522
|
+
const columns = React10__default.useMemo(() => cardColumns(card), [card]);
|
|
1523
|
+
const fetchAll = useAnalyticsCardRows(card);
|
|
1170
1524
|
if (!result?.rows?.length) return null;
|
|
1171
1525
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1172
1526
|
/* @__PURE__ */ jsx(
|
|
@@ -1174,15 +1528,13 @@ function ViewerActions({ card }) {
|
|
|
1174
1528
|
{
|
|
1175
1529
|
columns,
|
|
1176
1530
|
data: result.rows,
|
|
1531
|
+
fetchAll,
|
|
1177
1532
|
filename: card.id,
|
|
1178
1533
|
currency: currency.code,
|
|
1179
1534
|
title: card.name
|
|
1180
1535
|
}
|
|
1181
1536
|
),
|
|
1182
|
-
/* @__PURE__ */
|
|
1183
|
-
/* @__PURE__ */ jsx(Printer, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1184
|
-
"Print"
|
|
1185
|
-
] })
|
|
1537
|
+
/* @__PURE__ */ jsx(ToolbarAction, { icon: Printer, onClick: () => window.print(), children: labels.print })
|
|
1186
1538
|
] });
|
|
1187
1539
|
}
|
|
1188
1540
|
function LiveViewPage() {
|
|
@@ -1193,6 +1545,571 @@ function LiveViewPage() {
|
|
|
1193
1545
|
/* @__PURE__ */ jsx("p", { className: "mt-1 max-w-sm text-sm text-muted-foreground", children: labels.liveViewSoon })
|
|
1194
1546
|
] });
|
|
1195
1547
|
}
|
|
1548
|
+
var VIZ_TYPES = ["kpi", "line", "bar", "area", "donut", "breakdown", "table"];
|
|
1549
|
+
function vizOptionsFor(card) {
|
|
1550
|
+
const hasDimension = (card.query.dimensions ?? []).some((d) => !d.hidden);
|
|
1551
|
+
return hasDimension ? VIZ_TYPES : ["kpi", "table"];
|
|
1552
|
+
}
|
|
1553
|
+
function CardIcon({ name, className }) {
|
|
1554
|
+
const Icon = getLucideIcon(name ?? "FileText") ?? getLucideIcon("FileText");
|
|
1555
|
+
return Icon ? /* @__PURE__ */ jsx(Icon, { className }) : null;
|
|
1556
|
+
}
|
|
1557
|
+
function ReportRow({ card, custom, onEdit, children, drag }) {
|
|
1558
|
+
const t = useTranslation();
|
|
1559
|
+
return /* @__PURE__ */ jsxs(
|
|
1560
|
+
"li",
|
|
1561
|
+
{
|
|
1562
|
+
draggable: Boolean(drag),
|
|
1563
|
+
onDragStart: drag?.onDragStart,
|
|
1564
|
+
onDragOver: drag?.onDragOver,
|
|
1565
|
+
onDragEnd: drag?.onDragEnd,
|
|
1566
|
+
className: [
|
|
1567
|
+
"flex items-center gap-2 border-b border-border/60 px-3 py-2 last:border-0",
|
|
1568
|
+
drag ? "cursor-grab active:cursor-grabbing" : "",
|
|
1569
|
+
drag?.dragging ? "opacity-40" : ""
|
|
1570
|
+
].join(" "),
|
|
1571
|
+
children: [
|
|
1572
|
+
drag ? /* @__PURE__ */ jsx(GripVertical, { className: "h-4 w-4 shrink-0 text-muted-foreground/50", "aria-hidden": true }) : /* @__PURE__ */ jsx("span", { className: "w-4 shrink-0", "aria-hidden": true }),
|
|
1573
|
+
/* @__PURE__ */ jsx(CardIcon, { name: card.icon, className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
1574
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
1575
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
1576
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-sm font-medium", children: card.name }),
|
|
1577
|
+
custom ? /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: t("reports.settings.custom") }) : null
|
|
1578
|
+
] }),
|
|
1579
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-xs text-muted-foreground", children: card.category })
|
|
1580
|
+
] }),
|
|
1581
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [
|
|
1582
|
+
children,
|
|
1583
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onEdit, "aria-label": t("reports.settings.edit"), title: t("reports.settings.edit"), children: /* @__PURE__ */ jsx(SlidersHorizontal, { className: "h-3.5 w-3.5" }) })
|
|
1584
|
+
] })
|
|
1585
|
+
]
|
|
1586
|
+
}
|
|
1587
|
+
);
|
|
1588
|
+
}
|
|
1589
|
+
function CardEditor({ card, onClose }) {
|
|
1590
|
+
const t = useTranslation();
|
|
1591
|
+
const prefs = useAnalyticsPrefs();
|
|
1592
|
+
const pref = prefs.cards[card.id] ?? {};
|
|
1593
|
+
const isCustom = prefs.custom.some((r) => r.id === card.id);
|
|
1594
|
+
const [name, setName] = React10__default.useState(card.name);
|
|
1595
|
+
const measures = card.query.measures ?? [];
|
|
1596
|
+
const dimensions = (card.query.dimensions ?? []).filter((d) => !d.hidden);
|
|
1597
|
+
const keptMeasures = new Set(pref.measures ?? measures.map((m) => m.key));
|
|
1598
|
+
const keptDimensions = new Set(pref.dimensions ?? dimensions.map((d) => d.key));
|
|
1599
|
+
const toggle = (set, key, field, all) => {
|
|
1600
|
+
const next = new Set(set);
|
|
1601
|
+
if (next.has(key)) next.delete(key);
|
|
1602
|
+
else next.add(key);
|
|
1603
|
+
setCardPref(card.id, { [field]: next.size === all.length ? void 0 : [...next] });
|
|
1604
|
+
};
|
|
1605
|
+
return (
|
|
1606
|
+
// A coluna da direita, não uma gaveta só desta tela: ajustar um relatório é
|
|
1607
|
+
// olhar a lista enquanto se mexe nele, e uma gaveta cobre exatamente a
|
|
1608
|
+
// lista. É a mesma coluna do assistente — RightRailPage cai numa Sheet
|
|
1609
|
+
// sozinha quando a shell não tem coluna nenhuma.
|
|
1610
|
+
/* @__PURE__ */ jsxs(RightRailPage, { title: card.name, open: true, onClose, width: "max-w-md", children: [
|
|
1611
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
1612
|
+
card.description ? /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: card.description }) : null,
|
|
1613
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1614
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.name") }),
|
|
1615
|
+
/* @__PURE__ */ jsx(
|
|
1616
|
+
Input,
|
|
1617
|
+
{
|
|
1618
|
+
value: name,
|
|
1619
|
+
onChange: (e) => setName(e.target.value),
|
|
1620
|
+
onBlur: () => setCardPref(card.id, { name: name.trim() && name !== card.name ? name.trim() : void 0 })
|
|
1621
|
+
}
|
|
1622
|
+
)
|
|
1623
|
+
] }),
|
|
1624
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1625
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.viz") }),
|
|
1626
|
+
/* @__PURE__ */ jsxs(
|
|
1627
|
+
Select,
|
|
1628
|
+
{
|
|
1629
|
+
value: card.viz.type,
|
|
1630
|
+
onValueChange: (value) => setCardPref(card.id, { viz: value }),
|
|
1631
|
+
children: [
|
|
1632
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1633
|
+
/* @__PURE__ */ jsx(SelectContent, { children: vizOptionsFor(card).map((viz) => /* @__PURE__ */ jsx(SelectItem, { value: viz, children: t(`reports.viz.${viz}`) }, viz)) })
|
|
1634
|
+
]
|
|
1635
|
+
}
|
|
1636
|
+
)
|
|
1637
|
+
] }),
|
|
1638
|
+
dimensions.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1639
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.dimensions") }),
|
|
1640
|
+
dimensions.map((d) => /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm", children: [
|
|
1641
|
+
/* @__PURE__ */ jsx(
|
|
1642
|
+
Checkbox,
|
|
1643
|
+
{
|
|
1644
|
+
checked: keptDimensions.has(d.key),
|
|
1645
|
+
onChange: () => toggle(keptDimensions, d.key, "dimensions", dimensions.map((x) => x.key))
|
|
1646
|
+
}
|
|
1647
|
+
),
|
|
1648
|
+
d.label
|
|
1649
|
+
] }, d.key))
|
|
1650
|
+
] }) : null,
|
|
1651
|
+
measures.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1652
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.measures") }),
|
|
1653
|
+
measures.map((m) => /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm", children: [
|
|
1654
|
+
/* @__PURE__ */ jsx(
|
|
1655
|
+
Checkbox,
|
|
1656
|
+
{
|
|
1657
|
+
checked: keptMeasures.has(m.key),
|
|
1658
|
+
onChange: () => toggle(keptMeasures, m.key, "measures", measures.map((x) => x.key))
|
|
1659
|
+
}
|
|
1660
|
+
),
|
|
1661
|
+
m.label
|
|
1662
|
+
] }, m.key))
|
|
1663
|
+
] }) : null
|
|
1664
|
+
] }) }),
|
|
1665
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t border-border p-3", children: [
|
|
1666
|
+
isCustom ? /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
1667
|
+
deleteCustomReport(card.id);
|
|
1668
|
+
onClose();
|
|
1669
|
+
}, children: [
|
|
1670
|
+
/* @__PURE__ */ jsx(Trash2, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1671
|
+
t("reports.settings.delete")
|
|
1672
|
+
] }) : /* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
1673
|
+
clearCardPref(card.id);
|
|
1674
|
+
onClose();
|
|
1675
|
+
}, children: [
|
|
1676
|
+
/* @__PURE__ */ jsx(RotateCcw, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1677
|
+
t("reports.settings.resetCard")
|
|
1678
|
+
] }),
|
|
1679
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: onClose, children: t("reports.settings.done") })
|
|
1680
|
+
] })
|
|
1681
|
+
] })
|
|
1682
|
+
);
|
|
1683
|
+
}
|
|
1684
|
+
function slugify(value) {
|
|
1685
|
+
return value.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "report";
|
|
1686
|
+
}
|
|
1687
|
+
function buildCustomReport(input) {
|
|
1688
|
+
const dimensionKeys = new Set(input.source.dimensions.map((d) => d.key));
|
|
1689
|
+
const measureKeys = new Set(input.source.measures.map((m) => m.key));
|
|
1690
|
+
return {
|
|
1691
|
+
id: `custom.${slugify(input.name)}-${Date.now().toString(36)}`,
|
|
1692
|
+
name: input.name.trim(),
|
|
1693
|
+
category: input.category.trim() || "Geral",
|
|
1694
|
+
icon: "FileText",
|
|
1695
|
+
createdBy: input.createdBy,
|
|
1696
|
+
baseId: input.source.base.id,
|
|
1697
|
+
// Filtered, not trusted: a field the source does not have would resolve to
|
|
1698
|
+
// nothing and the report would open empty with no explanation.
|
|
1699
|
+
dimensions: input.dimensions.filter((k) => dimensionKeys.has(k)),
|
|
1700
|
+
measures: input.measures.filter((k) => measureKeys.has(k)),
|
|
1701
|
+
viz: input.viz,
|
|
1702
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1703
|
+
};
|
|
1704
|
+
}
|
|
1705
|
+
function NewReportPanel({
|
|
1706
|
+
sources,
|
|
1707
|
+
categories,
|
|
1708
|
+
onClose
|
|
1709
|
+
}) {
|
|
1710
|
+
const t = useTranslation();
|
|
1711
|
+
const { createdBy } = useAnalyticsConfig();
|
|
1712
|
+
const [name, setName] = React10__default.useState("");
|
|
1713
|
+
const [category, setCategory] = React10__default.useState(categories[0] ?? "Geral");
|
|
1714
|
+
const [sourceKeyState, setSourceKey] = React10__default.useState(sources[0]?.key ?? "");
|
|
1715
|
+
const [dimensions, setDimensions] = React10__default.useState([]);
|
|
1716
|
+
const [measures, setMeasures] = React10__default.useState([]);
|
|
1717
|
+
const [viz, setViz] = React10__default.useState("table");
|
|
1718
|
+
const source = sources.find((s) => s.key === sourceKeyState);
|
|
1719
|
+
React10__default.useEffect(() => {
|
|
1720
|
+
setDimensions([]);
|
|
1721
|
+
setMeasures([]);
|
|
1722
|
+
}, [sourceKeyState]);
|
|
1723
|
+
const toggle = (list, set, key) => {
|
|
1724
|
+
set(list.includes(key) ? list.filter((k) => k !== key) : [...list, key]);
|
|
1725
|
+
};
|
|
1726
|
+
const error = !name.trim() ? t("reports.settings.needName") : measures.length === 0 ? t("reports.settings.needMeasure") : null;
|
|
1727
|
+
const submit = () => {
|
|
1728
|
+
if (error || !source) return;
|
|
1729
|
+
saveCustomReport(buildCustomReport({
|
|
1730
|
+
name,
|
|
1731
|
+
category,
|
|
1732
|
+
createdBy,
|
|
1733
|
+
source,
|
|
1734
|
+
dimensions,
|
|
1735
|
+
measures,
|
|
1736
|
+
viz
|
|
1737
|
+
}));
|
|
1738
|
+
onClose();
|
|
1739
|
+
};
|
|
1740
|
+
return /* @__PURE__ */ jsxs(RightRailPage, { title: t("reports.settings.new"), open: true, onClose, width: "max-w-md", children: [
|
|
1741
|
+
/* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children: /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
1742
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1743
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.name") }),
|
|
1744
|
+
/* @__PURE__ */ jsx(Input, { value: name, onChange: (e) => setName(e.target.value) })
|
|
1745
|
+
] }),
|
|
1746
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1747
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.category") }),
|
|
1748
|
+
/* @__PURE__ */ jsx(Input, { value: category, onChange: (e) => setCategory(e.target.value), list: "fayz-analytics-categories" }),
|
|
1749
|
+
/* @__PURE__ */ jsx("datalist", { id: "fayz-analytics-categories", children: categories.map((c) => /* @__PURE__ */ jsx("option", { value: c }, c)) })
|
|
1750
|
+
] }),
|
|
1751
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1752
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.source") }),
|
|
1753
|
+
/* @__PURE__ */ jsxs(Select, { value: sourceKeyState, onValueChange: setSourceKey, children: [
|
|
1754
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1755
|
+
/* @__PURE__ */ jsx(SelectContent, { children: sources.map((s) => /* @__PURE__ */ jsx(SelectItem, { value: s.key, children: s.label }, s.key)) })
|
|
1756
|
+
] }),
|
|
1757
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("reports.settings.sourceHint") })
|
|
1758
|
+
] }),
|
|
1759
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1760
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.dimensions") }),
|
|
1761
|
+
(source?.dimensions ?? []).length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "\u2014" }) : source.dimensions.map((d) => /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm", children: [
|
|
1762
|
+
/* @__PURE__ */ jsx(Checkbox, { checked: dimensions.includes(d.key), onChange: () => toggle(dimensions, setDimensions, d.key) }),
|
|
1763
|
+
d.label
|
|
1764
|
+
] }, d.key))
|
|
1765
|
+
] }),
|
|
1766
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
1767
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.measures") }),
|
|
1768
|
+
(source?.measures ?? []).map((m) => /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm", children: [
|
|
1769
|
+
/* @__PURE__ */ jsx(Checkbox, { checked: measures.includes(m.key), onChange: () => toggle(measures, setMeasures, m.key) }),
|
|
1770
|
+
m.label
|
|
1771
|
+
] }, m.key))
|
|
1772
|
+
] }),
|
|
1773
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
1774
|
+
/* @__PURE__ */ jsx("label", { className: "text-xs font-medium text-muted-foreground", children: t("reports.settings.viz") }),
|
|
1775
|
+
/* @__PURE__ */ jsxs(Select, { value: viz, onValueChange: (value) => setViz(value), children: [
|
|
1776
|
+
/* @__PURE__ */ jsx(SelectTrigger, { children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
1777
|
+
/* @__PURE__ */ jsx(SelectContent, { children: (dimensions.length > 0 ? VIZ_TYPES : ["kpi", "table"]).map((v) => /* @__PURE__ */ jsx(SelectItem, { value: v, children: t(`reports.viz.${v}`) }, v)) })
|
|
1778
|
+
] })
|
|
1779
|
+
] })
|
|
1780
|
+
] }) }),
|
|
1781
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t border-border p-3", children: [
|
|
1782
|
+
/* @__PURE__ */ jsx("span", { className: "truncate text-xs text-destructive", children: error }),
|
|
1783
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-2", children: [
|
|
1784
|
+
/* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClose, children: t("reports.settings.cancel") }),
|
|
1785
|
+
/* @__PURE__ */ jsx(Button, { size: "sm", onClick: submit, disabled: Boolean(error), children: t("reports.settings.save") })
|
|
1786
|
+
] })
|
|
1787
|
+
] })
|
|
1788
|
+
] });
|
|
1789
|
+
}
|
|
1790
|
+
var CREATE_REPORT_TOOL = {
|
|
1791
|
+
id: "reports.createAnalyticsReport",
|
|
1792
|
+
name: "createAnalyticsReport",
|
|
1793
|
+
description: "Creates a custom analytics report for this user. Pick ONE source from the `sources` list in the surface state, then group_by and measures USING ONLY that source's field keys \u2014 a key from another source resolves to nothing and the report opens empty. Understand the intent and propose the whole report in one call instead of asking field by field. Rules of thumb: one time dimension + one measure reads best as a line; a category dimension with one measure as bar or breakdown; three or more measures as a table; no dimension at all as a kpi.",
|
|
1794
|
+
icon: "FileText",
|
|
1795
|
+
mode: "persist",
|
|
1796
|
+
execution: { plane: "client" },
|
|
1797
|
+
category: "Analytics",
|
|
1798
|
+
suggestions: [
|
|
1799
|
+
{ label: "Um relat\xF3rio de receita por mesa" },
|
|
1800
|
+
{ label: "Quanto cada pra\xE7a produz por turno" }
|
|
1801
|
+
],
|
|
1802
|
+
parameters: {
|
|
1803
|
+
type: "object",
|
|
1804
|
+
properties: {
|
|
1805
|
+
name: { type: "string", description: "Report name, in the user's language." },
|
|
1806
|
+
source: { type: "string", description: "A `key` from the surface state's `sources`." },
|
|
1807
|
+
group_by: {
|
|
1808
|
+
type: "array",
|
|
1809
|
+
description: "Dimension keys from that source. Empty means a single headline number.",
|
|
1810
|
+
items: { type: "string" }
|
|
1811
|
+
},
|
|
1812
|
+
measures: {
|
|
1813
|
+
type: "array",
|
|
1814
|
+
description: "Measure keys from that source. At least one.",
|
|
1815
|
+
items: { type: "string" }
|
|
1816
|
+
},
|
|
1817
|
+
viz: {
|
|
1818
|
+
type: "string",
|
|
1819
|
+
enum: ["kpi", "line", "bar", "area", "donut", "breakdown", "table"],
|
|
1820
|
+
description: "How to draw it. Defaults to table."
|
|
1821
|
+
},
|
|
1822
|
+
category: { type: "string", description: "Group it sits under in the rail. Reuse an existing one when it fits." },
|
|
1823
|
+
pin: { type: "boolean", description: "Also place it on the overview grid." }
|
|
1824
|
+
},
|
|
1825
|
+
required: ["name", "source", "measures"]
|
|
1826
|
+
}
|
|
1827
|
+
};
|
|
1828
|
+
var AGENT_INSTRUCTIONS = "The user is on the report configuration screen. `sources` in the surface state lists every data source with the exact dimension and measure keys askable of it \u2014 use those keys verbatim. When they describe a report, call createAnalyticsReport once with the full shape; do not interview them field by field. Name the report the way they said it. After creating, say in one line what it groups by and what it measures.";
|
|
1829
|
+
function AnalyticsSettingsPage() {
|
|
1830
|
+
const t = useTranslation();
|
|
1831
|
+
const { labels } = useAnalyticsConfig();
|
|
1832
|
+
const catalogue = useAnalyticsCatalogue();
|
|
1833
|
+
const cards = useAnalyticsCards();
|
|
1834
|
+
const prefs = useAnalyticsPrefs();
|
|
1835
|
+
const { createdBy } = useAnalyticsConfig();
|
|
1836
|
+
const [editing, setEditing] = React10__default.useState(null);
|
|
1837
|
+
const [creating, setCreating] = React10__default.useState(false);
|
|
1838
|
+
const [dragId, setDragId] = React10__default.useState(null);
|
|
1839
|
+
const askIntro = React10__default.useMemo(() => ({
|
|
1840
|
+
role: "setup",
|
|
1841
|
+
persona: t("reports.settings.askPersona"),
|
|
1842
|
+
pitch: t("reports.settings.askPitch"),
|
|
1843
|
+
description: t("reports.settings.askDescription"),
|
|
1844
|
+
suggestions: [0, 1, 2].map((i) => ({ label: t(`reports.settings.askSuggestion.${i}`) }))
|
|
1845
|
+
}), [t]);
|
|
1846
|
+
const createActions = React10__default.useMemo(() => [
|
|
1847
|
+
{
|
|
1848
|
+
id: "with-ai",
|
|
1849
|
+
label: t("assistant.askAI"),
|
|
1850
|
+
description: t("reports.settings.newWithAIHint"),
|
|
1851
|
+
icon: "WandSparkles",
|
|
1852
|
+
highlight: true,
|
|
1853
|
+
action: () => askAI({ hint: t("reports.settings.askPlaceholder"), intro: askIntro })
|
|
1854
|
+
},
|
|
1855
|
+
{
|
|
1856
|
+
id: "by-hand",
|
|
1857
|
+
label: t("reports.settings.newByHand"),
|
|
1858
|
+
description: t("reports.settings.newByHandHint"),
|
|
1859
|
+
icon: "SlidersHorizontal",
|
|
1860
|
+
action: () => setCreating(true)
|
|
1861
|
+
}
|
|
1862
|
+
], [t, askIntro]);
|
|
1863
|
+
const sources = React10__default.useMemo(() => analyticsSources(cards), [cards]);
|
|
1864
|
+
const categories = React10__default.useMemo(
|
|
1865
|
+
() => [...new Set(cards.map((c) => c.category))].sort((a, b) => a.localeCompare(b)),
|
|
1866
|
+
[cards]
|
|
1867
|
+
);
|
|
1868
|
+
const pinned = React10__default.useMemo(
|
|
1869
|
+
() => cards.filter((c) => c.dashboard).sort((a, b) => (a.dashboard?.order ?? 0) - (b.dashboard?.order ?? 0)),
|
|
1870
|
+
[cards]
|
|
1871
|
+
);
|
|
1872
|
+
const unpinned = React10__default.useMemo(
|
|
1873
|
+
() => cards.filter((c) => !c.dashboard).sort((a, b) => a.category.localeCompare(b.category) || a.name.localeCompare(b.name)),
|
|
1874
|
+
[cards]
|
|
1875
|
+
);
|
|
1876
|
+
const removed = React10__default.useMemo(
|
|
1877
|
+
() => catalogue.filter((c) => prefs.cards[c.id]?.hidden),
|
|
1878
|
+
[catalogue, prefs]
|
|
1879
|
+
);
|
|
1880
|
+
const [order, setOrder] = React10__default.useState(null);
|
|
1881
|
+
const pinnedIds = order ?? pinned.map((c) => c.id);
|
|
1882
|
+
const pinnedInOrder = pinnedIds.map((id) => pinned.find((c) => c.id === id)).filter((c) => Boolean(c));
|
|
1883
|
+
const move = (id, delta) => {
|
|
1884
|
+
const ids = pinned.map((c) => c.id);
|
|
1885
|
+
const from = ids.indexOf(id);
|
|
1886
|
+
const to = from + delta;
|
|
1887
|
+
if (from < 0 || to < 0 || to >= ids.length) return;
|
|
1888
|
+
ids.splice(to, 0, ids.splice(from, 1)[0]);
|
|
1889
|
+
setOverviewOrder(ids);
|
|
1890
|
+
};
|
|
1891
|
+
const onDragOver = (targetId) => (event) => {
|
|
1892
|
+
event.preventDefault();
|
|
1893
|
+
if (!dragId || dragId === targetId) return;
|
|
1894
|
+
const ids = [...pinnedIds];
|
|
1895
|
+
const from = ids.indexOf(dragId);
|
|
1896
|
+
const to = ids.indexOf(targetId);
|
|
1897
|
+
if (from < 0 || to < 0) return;
|
|
1898
|
+
ids.splice(to, 0, ids.splice(from, 1)[0]);
|
|
1899
|
+
setOrder(ids);
|
|
1900
|
+
};
|
|
1901
|
+
const commitDrag = () => {
|
|
1902
|
+
if (order) setOverviewOrder(order);
|
|
1903
|
+
setOrder(null);
|
|
1904
|
+
setDragId(null);
|
|
1905
|
+
};
|
|
1906
|
+
const editingCard = cards.find((c) => c.id === editing);
|
|
1907
|
+
const isCustom = (id) => prefs.custom.some((r) => r.id === id);
|
|
1908
|
+
const surfaceRef = React10__default.useRef({ sources, categories, cards, prefs, createdBy });
|
|
1909
|
+
surfaceRef.current = { sources, categories, cards, prefs, createdBy };
|
|
1910
|
+
const agentSurface = React10__default.useMemo(() => ({
|
|
1911
|
+
id: "analytics.reports",
|
|
1912
|
+
pluginId: "reports",
|
|
1913
|
+
instructions: AGENT_INSTRUCTIONS,
|
|
1914
|
+
describe: () => {
|
|
1915
|
+
const o = surfaceRef.current;
|
|
1916
|
+
return {
|
|
1917
|
+
title: "Configura\xE7\xE3o de relat\xF3rios",
|
|
1918
|
+
state: {
|
|
1919
|
+
sources: o.sources.map((source) => ({
|
|
1920
|
+
key: source.key,
|
|
1921
|
+
label: source.label,
|
|
1922
|
+
grain: source.base.grain ?? null,
|
|
1923
|
+
group_by: source.dimensions.map((d) => ({ key: d.key, label: d.label })),
|
|
1924
|
+
measures: source.measures.map((m) => ({ key: m.key, label: m.label, format: m.format ?? "number" }))
|
|
1925
|
+
})),
|
|
1926
|
+
categories: o.categories,
|
|
1927
|
+
onOverview: o.cards.filter((c) => c.dashboard).map((c) => c.name),
|
|
1928
|
+
yourReports: o.prefs.custom.map((r) => r.name)
|
|
1929
|
+
}
|
|
1930
|
+
};
|
|
1931
|
+
},
|
|
1932
|
+
tools: [{
|
|
1933
|
+
tool: CREATE_REPORT_TOOL,
|
|
1934
|
+
execute: (args) => {
|
|
1935
|
+
const o = surfaceRef.current;
|
|
1936
|
+
const source = o.sources.find((s) => s.key === args.source) ?? o.sources.find((s) => s.label === args.source);
|
|
1937
|
+
if (!source) {
|
|
1938
|
+
return { ok: false, error: "unknown_source", sources: o.sources.map((s) => s.key) };
|
|
1939
|
+
}
|
|
1940
|
+
const report = buildCustomReport({
|
|
1941
|
+
name: String(args.name ?? "").trim() || "Relat\xF3rio",
|
|
1942
|
+
category: String(args.category ?? o.categories[0] ?? "Geral"),
|
|
1943
|
+
createdBy: o.createdBy,
|
|
1944
|
+
source,
|
|
1945
|
+
dimensions: Array.isArray(args.group_by) ? args.group_by : [],
|
|
1946
|
+
measures: Array.isArray(args.measures) ? args.measures : [],
|
|
1947
|
+
viz: args.viz ?? "table"
|
|
1948
|
+
});
|
|
1949
|
+
if (report.measures.length === 0) {
|
|
1950
|
+
return {
|
|
1951
|
+
ok: false,
|
|
1952
|
+
error: "no_valid_measure",
|
|
1953
|
+
available: source.measures.map((m) => m.key)
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
saveCustomReport(report);
|
|
1957
|
+
if (args.pin) setCardPref(report.id, { pinned: true, order: 999 });
|
|
1958
|
+
navigateAnalytics(`${REPORTS_BASE}/${encodeURIComponent(report.id)}`);
|
|
1959
|
+
return {
|
|
1960
|
+
ok: true,
|
|
1961
|
+
id: report.id,
|
|
1962
|
+
name: report.name,
|
|
1963
|
+
group_by: report.dimensions,
|
|
1964
|
+
measures: report.measures,
|
|
1965
|
+
viz: report.viz
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
}]
|
|
1969
|
+
}), []);
|
|
1970
|
+
useAgentSurface(agentSurface);
|
|
1971
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
1972
|
+
/* @__PURE__ */ jsx(
|
|
1973
|
+
SubpageHeader,
|
|
1974
|
+
{
|
|
1975
|
+
title: t("reports.settings.title"),
|
|
1976
|
+
parentLabel: labels.analytics,
|
|
1977
|
+
onBack: () => navigateAnalytics(ANALYTICS_BASE)
|
|
1978
|
+
}
|
|
1979
|
+
),
|
|
1980
|
+
/* @__PURE__ */ jsxs(PageHeaderActions, { children: [
|
|
1981
|
+
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => resetAnalyticsPrefs(), children: [
|
|
1982
|
+
/* @__PURE__ */ jsx(RotateCcw, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
1983
|
+
t("reports.settings.reset")
|
|
1984
|
+
] }),
|
|
1985
|
+
/* @__PURE__ */ jsx(QuickActionsButton, { label: t("reports.settings.new"), variant: "default", actions: createActions })
|
|
1986
|
+
] }),
|
|
1987
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-2xl text-sm text-muted-foreground", children: t("reports.settings.subtitle") }),
|
|
1988
|
+
/* @__PURE__ */ jsxs(Card, { className: "overflow-hidden", children: [
|
|
1989
|
+
/* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
|
|
1990
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t("reports.settings.onOverview") }),
|
|
1991
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("reports.settings.onOverviewHint") })
|
|
1992
|
+
] }),
|
|
1993
|
+
pinnedInOrder.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-3 py-6 text-center text-sm text-muted-foreground", children: t("reports.settings.empty") }) : /* @__PURE__ */ jsx("ul", { children: pinnedInOrder.map((card, index) => /* @__PURE__ */ jsxs(
|
|
1994
|
+
ReportRow,
|
|
1995
|
+
{
|
|
1996
|
+
card,
|
|
1997
|
+
custom: isCustom(card.id),
|
|
1998
|
+
onEdit: () => setEditing(card.id),
|
|
1999
|
+
drag: {
|
|
2000
|
+
dragging: dragId === card.id,
|
|
2001
|
+
onDragStart: () => setDragId(card.id),
|
|
2002
|
+
onDragOver: onDragOver(card.id),
|
|
2003
|
+
onDragEnd: commitDrag
|
|
2004
|
+
},
|
|
2005
|
+
children: [
|
|
2006
|
+
/* @__PURE__ */ jsxs(
|
|
2007
|
+
Select,
|
|
2008
|
+
{
|
|
2009
|
+
value: String(card.dashboard?.span ?? ""),
|
|
2010
|
+
onValueChange: (value) => setCardPref(card.id, { span: Number(value) }),
|
|
2011
|
+
children: [
|
|
2012
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-8 w-[150px]", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: t("reports.settings.width") }) }),
|
|
2013
|
+
/* @__PURE__ */ jsx(SelectContent, { children: [1, 2, 3, 4].map((span) => /* @__PURE__ */ jsx(SelectItem, { value: String(span), children: t(`reports.settings.width.${span}`) }, span)) })
|
|
2014
|
+
]
|
|
2015
|
+
}
|
|
2016
|
+
),
|
|
2017
|
+
/* @__PURE__ */ jsx(
|
|
2018
|
+
Button,
|
|
2019
|
+
{
|
|
2020
|
+
variant: "ghost",
|
|
2021
|
+
size: "sm",
|
|
2022
|
+
disabled: index === 0,
|
|
2023
|
+
onClick: () => move(card.id, -1),
|
|
2024
|
+
"aria-label": t("reports.settings.up"),
|
|
2025
|
+
title: t("reports.settings.up"),
|
|
2026
|
+
children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-3.5 w-3.5" })
|
|
2027
|
+
}
|
|
2028
|
+
),
|
|
2029
|
+
/* @__PURE__ */ jsx(
|
|
2030
|
+
Button,
|
|
2031
|
+
{
|
|
2032
|
+
variant: "ghost",
|
|
2033
|
+
size: "sm",
|
|
2034
|
+
disabled: index === pinnedInOrder.length - 1,
|
|
2035
|
+
onClick: () => move(card.id, 1),
|
|
2036
|
+
"aria-label": t("reports.settings.down"),
|
|
2037
|
+
title: t("reports.settings.down"),
|
|
2038
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-3.5 w-3.5" })
|
|
2039
|
+
}
|
|
2040
|
+
),
|
|
2041
|
+
/* @__PURE__ */ jsx(
|
|
2042
|
+
Button,
|
|
2043
|
+
{
|
|
2044
|
+
variant: "ghost",
|
|
2045
|
+
size: "sm",
|
|
2046
|
+
onClick: () => setCardPref(card.id, { pinned: false }),
|
|
2047
|
+
"aria-label": t("reports.settings.unpin"),
|
|
2048
|
+
title: t("reports.settings.unpin"),
|
|
2049
|
+
children: /* @__PURE__ */ jsx(EyeOff, { className: "h-3.5 w-3.5" })
|
|
2050
|
+
}
|
|
2051
|
+
)
|
|
2052
|
+
]
|
|
2053
|
+
},
|
|
2054
|
+
card.id
|
|
2055
|
+
)) })
|
|
2056
|
+
] }),
|
|
2057
|
+
/* @__PURE__ */ jsxs(Card, { className: "overflow-hidden", children: [
|
|
2058
|
+
/* @__PURE__ */ jsxs("div", { className: "border-b border-border px-3 py-2", children: [
|
|
2059
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t("reports.settings.offOverview") }),
|
|
2060
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t("reports.settings.offOverviewHint") })
|
|
2061
|
+
] }),
|
|
2062
|
+
/* @__PURE__ */ jsx("ul", { children: unpinned.map((card) => /* @__PURE__ */ jsxs(ReportRow, { card, custom: isCustom(card.id), onEdit: () => setEditing(card.id), children: [
|
|
2063
|
+
/* @__PURE__ */ jsx(
|
|
2064
|
+
Button,
|
|
2065
|
+
{
|
|
2066
|
+
variant: "ghost",
|
|
2067
|
+
size: "sm",
|
|
2068
|
+
onClick: () => setCardPref(card.id, { pinned: true, order: pinned.length }),
|
|
2069
|
+
"aria-label": t("reports.settings.pin"),
|
|
2070
|
+
title: t("reports.settings.pin"),
|
|
2071
|
+
children: /* @__PURE__ */ jsx(Eye, { className: "h-3.5 w-3.5" })
|
|
2072
|
+
}
|
|
2073
|
+
),
|
|
2074
|
+
isCustom(card.id) ? /* @__PURE__ */ jsx(
|
|
2075
|
+
Button,
|
|
2076
|
+
{
|
|
2077
|
+
variant: "ghost",
|
|
2078
|
+
size: "sm",
|
|
2079
|
+
onClick: () => deleteCustomReport(card.id),
|
|
2080
|
+
"aria-label": t("reports.settings.delete"),
|
|
2081
|
+
title: t("reports.settings.delete"),
|
|
2082
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
2083
|
+
}
|
|
2084
|
+
) : /* @__PURE__ */ jsx(
|
|
2085
|
+
Button,
|
|
2086
|
+
{
|
|
2087
|
+
variant: "ghost",
|
|
2088
|
+
size: "sm",
|
|
2089
|
+
onClick: () => setCardPref(card.id, { hidden: true }),
|
|
2090
|
+
"aria-label": t("reports.settings.remove"),
|
|
2091
|
+
title: t("reports.settings.remove"),
|
|
2092
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
|
|
2093
|
+
}
|
|
2094
|
+
)
|
|
2095
|
+
] }, card.id)) })
|
|
2096
|
+
] }),
|
|
2097
|
+
removed.length > 0 ? /* @__PURE__ */ jsxs(Card, { className: "overflow-hidden", children: [
|
|
2098
|
+
/* @__PURE__ */ jsx("div", { className: "border-b border-border px-3 py-2", children: /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: t("reports.settings.hidden") }) }),
|
|
2099
|
+
/* @__PURE__ */ jsx("ul", { children: removed.map((card) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2 border-b border-border/60 px-3 py-2 last:border-0", children: [
|
|
2100
|
+
/* @__PURE__ */ jsx("span", { className: "w-4 shrink-0", "aria-hidden": true }),
|
|
2101
|
+
/* @__PURE__ */ jsx(CardIcon, { name: card.icon, className: "h-4 w-4 shrink-0 text-muted-foreground" }),
|
|
2102
|
+
/* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx("span", { className: "truncate text-sm text-muted-foreground", children: card.name }) }),
|
|
2103
|
+
/* @__PURE__ */ jsxs(Button, { variant: "ghost", size: "sm", onClick: () => setCardPref(card.id, { hidden: void 0 }), children: [
|
|
2104
|
+
/* @__PURE__ */ jsx(RotateCcw, { className: "mr-1.5 h-3.5 w-3.5" }),
|
|
2105
|
+
t("reports.settings.restore")
|
|
2106
|
+
] })
|
|
2107
|
+
] }, card.id)) })
|
|
2108
|
+
] }) : null,
|
|
2109
|
+
editingCard ? /* @__PURE__ */ jsx(CardEditor, { card: editingCard, onClose: () => setEditing(null) }) : null,
|
|
2110
|
+
creating ? /* @__PURE__ */ jsx(NewReportPanel, { sources, categories, onClose: () => setCreating(false) }) : null
|
|
2111
|
+
] });
|
|
2112
|
+
}
|
|
1196
2113
|
function camelToSnake2(str) {
|
|
1197
2114
|
return str.replace(/[A-Z]/g, (c) => "_" + c.toLowerCase());
|
|
1198
2115
|
}
|
|
@@ -1369,13 +2286,13 @@ function createAnalyticsPlugin(options = {}) {
|
|
|
1369
2286
|
() => createSupabaseAnalyticsProvider({ tenantId: () => getReportsTenantId() }),
|
|
1370
2287
|
() => createMockAnalyticsProvider()
|
|
1371
2288
|
);
|
|
1372
|
-
const page2 = (Body, view) => (props) =>
|
|
2289
|
+
const page2 = (Body, view) => (props) => React10__default.createElement(
|
|
1373
2290
|
AnalyticsSurface,
|
|
1374
2291
|
{ config, provider },
|
|
1375
|
-
|
|
2292
|
+
React10__default.createElement(
|
|
1376
2293
|
AnalyticsModule,
|
|
1377
2294
|
{ view, cardId: props?.cardId },
|
|
1378
|
-
|
|
2295
|
+
React10__default.createElement(Body, props)
|
|
1379
2296
|
)
|
|
1380
2297
|
);
|
|
1381
2298
|
const READ = { feature: "reports", action: "read" };
|
|
@@ -1393,6 +2310,7 @@ function createAnalyticsPlugin(options = {}) {
|
|
|
1393
2310
|
];
|
|
1394
2311
|
return {
|
|
1395
2312
|
id: "reports",
|
|
2313
|
+
defaultAgentRole: "growth",
|
|
1396
2314
|
name: config.labels.analytics,
|
|
1397
2315
|
icon: "BarChart3",
|
|
1398
2316
|
version: "2.0.0",
|
|
@@ -1416,6 +2334,10 @@ function createAnalyticsPlugin(options = {}) {
|
|
|
1416
2334
|
// thing, one of them generic.
|
|
1417
2335
|
{ path: `${REPORTS_BASE}/:cardId`, component: page2(AnalyticsReportViewer, "report"), permission: READ },
|
|
1418
2336
|
{ path: `${ANALYTICS_BASE}/live`, component: page2(LiveViewPage, "live"), permission: READ },
|
|
2337
|
+
// Read permission on purpose: the screen writes a per-user preference,
|
|
2338
|
+
// not tenant data — gating it behind a write scope would mean a viewer
|
|
2339
|
+
// cannot choose which of the reports they can see are on their overview.
|
|
2340
|
+
{ path: SETTINGS_ROUTE, component: page2(AnalyticsSettingsPage, "settings"), permission: READ },
|
|
1419
2341
|
// The old top-level route kept as a redirect target: bookmarks, saved
|
|
1420
2342
|
// links and the five apps still pointing at /reports must not 404.
|
|
1421
2343
|
{ path: "/reports", component: page2(ReportsListPage, "reports"), permission: READ }
|
|
@@ -1445,6 +2367,6 @@ function createReportsPlugin(options) {
|
|
|
1445
2367
|
});
|
|
1446
2368
|
}
|
|
1447
2369
|
|
|
1448
|
-
export { ANALYTICS_BASE, AnalyticsModule, AnalyticsOverview, AnalyticsReportViewer, AnalyticsSurface, REPORTS_BASE, ReportsListPage, aggregateRows, cardColumns, createAnalyticsPlugin, createMockAnalyticsProvider, createMockReportProvider, createReportsPlugin, createSupabaseAnalyticsProvider, createSupabaseReportProvider, getReportsTenantId, markViewed, reportDefToCard, reportDefsToCards, setReportsTenantId, useAnalyticsCards, useAnalyticsConfig, useLastViewed, useOverviewCards };
|
|
2370
|
+
export { ANALYTICS_BASE, AnalyticsModule, AnalyticsOverview, AnalyticsReportViewer, AnalyticsSettingsPage, AnalyticsSurface, REPORTS_BASE, ReportsListPage, SETTINGS_ROUTE, aggregateRows, cardColumns, clearCardPref, createAnalyticsPlugin, createMockAnalyticsProvider, createMockReportProvider, createReportsPlugin, createSupabaseAnalyticsProvider, createSupabaseReportProvider, deleteCustomReport, fieldPalette, getReportsTenantId, markViewed, reportDefToCard, reportDefsToCards, resetAnalyticsPrefs, resolveCards, resolveCustomReport, saveCustomReport, setCardPref, setOverviewOrder, setReportsTenantId, sourceKey, useAnalyticsCards, useAnalyticsCatalogue, useAnalyticsConfig, useAnalyticsPrefs, useLastViewed, useOverviewCards };
|
|
1449
2371
|
//# sourceMappingURL=index.js.map
|
|
1450
2372
|
//# sourceMappingURL=index.js.map
|