@proef/common-ui 2.2.6 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/dist/esm/index.js +6 -0
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/esm/legacy/components/charts/Charts.js +52 -192
  4. package/dist/esm/legacy/components/charts/Charts.js.map +1 -1
  5. package/dist/esm/legacy/components/molecules/DataTable/DataTable.js +252 -115
  6. package/dist/esm/legacy/components/molecules/DataTable/DataTable.js.map +1 -1
  7. package/dist/esm/legacy/components/organisms/GanttChart/GanttChart.js +7 -4
  8. package/dist/esm/legacy/components/organisms/GanttChart/GanttChart.js.map +1 -1
  9. package/dist/esm/v1/capabilities/overlay/useFloatingPanel.js +5 -2
  10. package/dist/esm/v1/capabilities/overlay/useFloatingPanel.js.map +1 -1
  11. package/dist/esm/v1/components/data/Charts/Charts.js +301 -0
  12. package/dist/esm/v1/components/data/Charts/Charts.js.map +1 -0
  13. package/dist/esm/v1/components/data/Pagination/Pagination.js +30 -26
  14. package/dist/esm/v1/components/data/Pagination/Pagination.js.map +1 -1
  15. package/dist/esm/v1/components/forms/FormField/FormField.js +3 -2
  16. package/dist/esm/v1/components/forms/FormField/FormField.js.map +1 -1
  17. package/dist/esm/v1/components/layout/AppShell/AppShell.js +8 -3
  18. package/dist/esm/v1/components/layout/AppShell/AppShell.js.map +1 -1
  19. package/dist/esm/v1/components/layout/Page/Page.js +1 -1
  20. package/dist/esm/v1/components/layout/Page/Page.js.map +1 -1
  21. package/dist/esm/v1/components/layout/PageHeader/PageHeader.js +21 -18
  22. package/dist/esm/v1/components/layout/PageHeader/PageHeader.js.map +1 -1
  23. package/dist/esm/v1/components/lists/ListFinder/ListFinder.js +16 -16
  24. package/dist/esm/v1/components/lists/ListFinder/ListFinder.js.map +1 -1
  25. package/dist/esm/v1/components/overlays/Dialog/Dialog.js +5 -2
  26. package/dist/esm/v1/components/overlays/Dialog/Dialog.js.map +1 -1
  27. package/dist/esm/v1/components/tables/Table/ColumnFilter.js +3 -1
  28. package/dist/esm/v1/components/tables/Table/ColumnFilter.js.map +1 -1
  29. package/dist/esm/v1/primitives/buttons/SegmentedControl/SegmentedControl.js +3 -3
  30. package/dist/esm/v1/primitives/buttons/SegmentedControl/SegmentedControl.js.map +1 -1
  31. package/dist/esm/v1/primitives/display/Kpi/Kpi.js +6 -3
  32. package/dist/esm/v1/primitives/display/Kpi/Kpi.js.map +1 -1
  33. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js +31 -9
  34. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js.map +1 -1
  35. package/dist/esm/v1/primitives/lists/ListItem/ListItem.js +5 -5
  36. package/dist/esm/v1/primitives/lists/ListItem/ListItem.js.map +1 -1
  37. package/dist/esm/v1/primitives/navigation/Tabs/Tabs.js +97 -36
  38. package/dist/esm/v1/primitives/navigation/Tabs/Tabs.js.map +1 -1
  39. package/dist/style.components.css +1 -1
  40. package/dist/style.css +1 -1
  41. package/dist/style.layered.css +1 -1
  42. package/dist/theme.css +28 -0
  43. package/dist/types/legacy/components/charts/Charts.d.ts +14 -11
  44. package/dist/types/legacy/components/molecules/DataTable/DataTable.d.ts +12 -1
  45. package/dist/types/legacy/components/organisms/GanttChart/GanttChart.d.ts +15 -1
  46. package/dist/types/legacy/exports/organisms.d.ts +1 -1
  47. package/dist/types/v1/components/data/Charts/Charts.d.ts +103 -0
  48. package/dist/types/v1/index.d.ts +2 -0
  49. package/dist/types/v1/primitives/forms/SelectInput/SelectInput.d.ts +12 -0
  50. package/dist/types/v1/primitives/lists/ListItem/ListItem.d.ts +1 -1
  51. package/package.json +1 -1
package/dist/esm/index.js CHANGED
@@ -94,6 +94,7 @@ import { AvatarStack } from "./v1/primitives/display/AvatarStack/AvatarStack.js"
94
94
  import { AvisosProvider, useAvisos } from "./legacy/components/ui/Avisos.js";
95
95
  import { Badge } from "./v1/primitives/display/Badge/Badge.js";
96
96
  import { BadgeSla, BarraFiltros, Btn, BtnGroup, Campo, CampoInterruptor, CampoPesquisa, CampoSelect, ChipEstadoInterno, Estado, LinhaAlerta, ListaCampos, Modal, SectionGroup } from "./legacy/components/ui/Ui.js";
97
+ import { BarChart, DonutChart, LineChart, shortDateLabel, useChartPalette } from "./v1/components/data/Charts/Charts.js";
97
98
  import { BoldIcon, CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, ChevronsUpDownIcon, CloseIcon, ErrorIcon, ExternalLinkIcon, FilterIcon, GlobeIcon, ImageIcon, InfoIcon, ItalicIcon, LayersIcon, ListBulletIcon, ListIcon, ListNumberedIcon, LockIcon, LogOutIcon, MaximizeIcon, MenuIcon, MinimizeIcon, MonitorIcon, MoonIcon, PanelOpenIcon, PrefilledIcon, QrCodeIcon, QuoteIcon, SearchIcon, SlidersIcon, SparklesIcon, StrikethroughIcon, SunIcon, UnderlineIcon, WarningIcon } from "./v1/foundations/icons/icons.js";
98
99
  import { Card } from "./v1/primitives/display/Card/Card.js";
99
100
  import { DEFAULT_LIST_FINDER_LABELS } from "./v1/components/lists/ListFinder/labels.js";
@@ -130,6 +131,7 @@ export {
130
131
  AvisosProvider,
131
132
  Badge,
132
133
  BadgeSla,
134
+ BarChart,
133
135
  default7 as BarraAcoes,
134
136
  BarraFiltros,
135
137
  default8 as BarraValor,
@@ -170,6 +172,7 @@ export {
170
172
  DateInput,
171
173
  default20 as Dialog,
172
174
  default21 as DocumentDropzone,
175
+ DonutChart,
173
176
  default22 as Error,
174
177
  ErrorIcon,
175
178
  default23 as ErrorPage,
@@ -208,6 +211,7 @@ export {
208
211
  LayersIcon,
209
212
  default41 as Layout,
210
213
  LazyLoader,
214
+ LineChart,
211
215
  Linha,
212
216
  LinhaAlerta,
213
217
  Linhas,
@@ -311,6 +315,7 @@ export {
311
315
  richTextToPlain,
312
316
  sanitizeRichText,
313
317
  scanQRCodes,
318
+ shortDateLabel,
314
319
  termosDaPesquisa,
315
320
  textToRichText,
316
321
  themeBootScript,
@@ -319,6 +324,7 @@ export {
319
324
  useApi,
320
325
  useAuth,
321
326
  useAvisos,
327
+ useChartPalette,
322
328
  useColumnLayout,
323
329
  useConfirm,
324
330
  useDialogContainer,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,8 +1,9 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
2
  import { Chart, registerables } from "chart.js";
3
3
  import ChartDataLabels from "chartjs-plugin-datalabels";
4
- import { Doughnut, Bar, Bubble, Line } from "react-chartjs-2";
4
+ import { Bubble } from "react-chartjs-2";
5
5
  import { useIsDark } from "../../../v1/capabilities/theme/useIsDark.js";
6
+ import { DonutChart, BarChart, LineChart } from "../../../v1/components/data/Charts/Charts.js";
6
7
  function euros(v) {
7
8
  if (v == null) return "—";
8
9
  const negativo = v < 0;
@@ -10,12 +11,6 @@ function euros(v) {
10
11
  const comMilhares = inteiro.replace(/\B(?=(\d{3})+(?!\d))/g, ".");
11
12
  return `${negativo ? "-" : ""}${comMilhares},${decimal} €`;
12
13
  }
13
- function labelBucket(b) {
14
- if (/^\d{4}-S\d{2}$/.test(b)) return `S${b.slice(6)}/${b.slice(2, 4)}`;
15
- if (/^\d{4}-\d{2}$/.test(b)) return `${b.slice(5)}/${b.slice(0, 4)}`;
16
- if (/^\d{4}-\d{2}-\d{2}$/.test(b)) return `${b.slice(8)}/${b.slice(5, 7)}`;
17
- return b;
18
- }
19
14
  Chart.register(...registerables, ChartDataLabels);
20
15
  Chart.defaults.set("plugins.datalabels", { display: false });
21
16
  Chart.defaults.font.family = "'Montserrat', sans-serif";
@@ -41,85 +36,39 @@ function useTema() {
41
36
  const escuro = useIsDark();
42
37
  return escuro ? { texto: "#CBD5E1", forte: "#E2E8F0", grelha: "#1E293B" } : { texto: "#5B6577", forte: "#182238", grelha: "#EEF1F5" };
43
38
  }
44
- function legendaBaixo(tema) {
45
- return {
46
- position: "bottom",
47
- labels: { boxWidth: 10, font: { size: 11 }, color: tema.forte, usePointStyle: true, pointStyle: "circle" }
48
- };
49
- }
50
- function clique(labels, series, onClick) {
51
- if (!onClick) return {};
52
- return {
53
- onClick: (_e, els) => {
54
- const el = els[0];
55
- if (!el) return;
56
- onClick(labels[el.index], el.index, series?.[el.datasetIndex]?.nome);
57
- },
58
- onHover: (e, els) => {
59
- const alvo = e.native?.target;
60
- if (alvo) alvo.style.cursor = els.length ? "pointer" : "default";
61
- }
62
- };
63
- }
64
39
  function Barras({
65
- labels: todas,
66
- series: todasSeries,
67
- horizontal = false,
68
- empilhado = false,
40
+ labels,
41
+ series,
42
+ horizontal,
43
+ empilhado,
69
44
  altura,
70
45
  limite,
71
46
  cores,
72
- formatar = (v) => String(v),
73
- valores = true,
74
- percentagem = false,
47
+ formatar,
48
+ valores,
49
+ percentagem,
75
50
  legenda,
76
- formatarEtiqueta = labelBucket,
51
+ formatarEtiqueta,
77
52
  onClick
78
53
  }) {
79
- const tema = useTema();
80
- const labels = limite ? todas.slice(0, limite) : todas;
81
- const series = todasSeries.map((s) => ({ ...s, valores: limite ? s.valores.slice(0, limite) : s.valores }));
82
- const uma = series.length === 1;
83
- const total = uma ? todasSeries[0].valores.reduce((a, b) => a + (b || 0), 0) : 0;
84
- const etiqueta = (v) => percentagem && uma && total > 0 ? `${formatar(v)} (${Math.round(v / total * 100)}%)` : formatar(v);
85
- const alturaFinal = altura ?? (horizontal ? Math.max(200, labels.length * 30 + 40) : 260);
86
- const eixoValor = { beginAtZero: true, stacked: empilhado, grid: { color: tema.grelha }, ticks: { color: tema.texto, callback: (v) => formatar(Number(v)) } };
87
- const eixoEtiqueta = { stacked: empilhado, grid: { display: false }, ticks: { color: tema.texto } };
88
- return /* @__PURE__ */ jsx("div", { style: { height: alturaFinal }, children: /* @__PURE__ */ jsx(
89
- Bar,
54
+ return /* @__PURE__ */ jsx(
55
+ BarChart,
90
56
  {
91
- data: {
92
- labels: labels.map(formatarEtiqueta),
93
- datasets: series.map((s, i) => ({
94
- label: s.nome,
95
- data: s.valores,
96
- backgroundColor: uma && cores ? labels.map((_, j) => cores[j % cores.length]) : s.cor ?? cor(i),
97
- borderRadius: 4,
98
- maxBarThickness: 40
99
- }))
100
- },
101
- options: {
102
- indexAxis: horizontal ? "y" : "x",
103
- maintainAspectRatio: false,
104
- responsive: true,
105
- ...clique(labels, series, onClick),
106
- plugins: {
107
- legend: legenda ?? !uma ? legendaBaixo(tema) : { display: false },
108
- tooltip: { callbacks: { label: (c) => `${c.dataset.label ? `${c.dataset.label}: ` : ""}${etiqueta(Number(c.parsed[horizontal ? "x" : "y"]))}` } },
109
- datalabels: {
110
- display: (c) => valores && !empilhado && Number(c.dataset.data[c.dataIndex]) > 0,
111
- anchor: "end",
112
- align: "end",
113
- color: tema.forte,
114
- font: { size: 10, weight: 600 },
115
- formatter: (v) => etiqueta(v)
116
- }
117
- },
118
- scales: horizontal ? { x: eixoValor, y: eixoEtiqueta } : { x: eixoEtiqueta, y: eixoValor },
119
- layout: { padding: horizontal ? { right: valores ? percentagem ? 56 : 32 : 8 } : { top: valores ? 18 : 4 } }
120
- }
57
+ labels,
58
+ series: series.map((s) => ({ name: s.nome, values: s.valores, color: s.cor })),
59
+ horizontal,
60
+ stacked: empilhado,
61
+ height: altura,
62
+ limit: limite,
63
+ colors: cores,
64
+ format: formatar,
65
+ showValues: valores,
66
+ showPercent: percentagem,
67
+ legend: legenda,
68
+ formatLabel: formatarEtiqueta,
69
+ onClick
121
70
  }
122
- ) });
71
+ );
123
72
  }
124
73
  function BarrasEmpilhadas({ serie, altura = 280, onClick }) {
125
74
  return /* @__PURE__ */ jsx(Barras, { labels: serie.labels, series: serie.series, empilhado: true, altura, onClick, legenda: true });
@@ -148,50 +97,10 @@ function Anel({
148
97
  dados,
149
98
  altura = 260,
150
99
  cores,
151
- formatar = (v) => String(v),
100
+ formatar,
152
101
  onClick
153
102
  }) {
154
- const tema = useTema();
155
- const total = dados.valores.reduce((a, b) => a + (b || 0), 0);
156
- return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
157
- Doughnut,
158
- {
159
- data: {
160
- labels: dados.labels,
161
- datasets: [
162
- {
163
- data: dados.valores,
164
- backgroundColor: cores ?? dados.labels.map((_, i) => cor(i)),
165
- borderWidth: 0
166
- }
167
- ]
168
- },
169
- options: {
170
- maintainAspectRatio: false,
171
- responsive: true,
172
- cutout: "58%",
173
- ...clique(dados.labels, void 0, onClick),
174
- plugins: {
175
- legend: legendaBaixo(tema),
176
- tooltip: {
177
- callbacks: {
178
- label: (c) => {
179
- const v = Number(c.parsed);
180
- return ` ${c.label}: ${formatar(v)}${total > 0 ? ` (${Math.round(v / total * 100)}%)` : ""}`;
181
- }
182
- }
183
- },
184
- datalabels: {
185
- // Só nas fatias que têm espaço para o número.
186
- display: (c) => total > 0 && Number(c.dataset.data[c.dataIndex]) / total >= 0.05,
187
- color: "#FFFFFF",
188
- font: { size: 11, weight: 700 },
189
- formatter: (v) => formatar(v)
190
- }
191
- }
192
- }
193
- }
194
- ) });
103
+ return /* @__PURE__ */ jsx(DonutChart, { labels: dados.labels, values: dados.valores, height: altura, colors: cores, format: formatar, onClick });
195
104
  }
196
105
  function Linha({
197
106
  dados,
@@ -199,91 +108,42 @@ function Linha({
199
108
  euros: emEuros = false,
200
109
  onClick
201
110
  }) {
202
- const tema = useTema();
203
- return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
204
- Line,
111
+ return /* @__PURE__ */ jsx(
112
+ LineChart,
205
113
  {
206
- data: {
207
- labels: dados.labels.map(labelBucket),
208
- datasets: [
209
- {
210
- label: "",
211
- data: dados.valores,
212
- borderColor: "#C23B34",
213
- backgroundColor: "rgba(194,59,52,0.12)",
214
- fill: true,
215
- tension: 0.25,
216
- pointRadius: 3
217
- }
218
- ]
219
- },
220
- options: {
221
- maintainAspectRatio: false,
222
- responsive: true,
223
- ...clique(dados.labels, void 0, onClick),
224
- plugins: {
225
- legend: { display: false },
226
- tooltip: {
227
- callbacks: {
228
- label: (c) => emEuros ? euros(Number(c.parsed.y)) : String(c.parsed.y)
229
- }
230
- }
231
- },
232
- scales: {
233
- x: { grid: { display: false }, ticks: { color: tema.texto } },
234
- y: {
235
- beginAtZero: true,
236
- grid: { color: tema.grelha },
237
- ticks: { color: tema.texto, callback: (v) => emEuros ? euros(Number(v)) : String(v) }
238
- }
239
- }
240
- }
114
+ labels: dados.labels,
115
+ series: [{ name: "", values: dados.valores, color: "#C23B34" }],
116
+ area: true,
117
+ height: altura,
118
+ format: emEuros ? (v) => euros(v) : void 0,
119
+ legend: false,
120
+ onClick
241
121
  }
242
- ) });
122
+ );
243
123
  }
244
124
  function Linhas({
245
125
  serie,
246
126
  altura = 280,
247
127
  area = false,
248
128
  cores,
249
- formatar = (v) => String(v),
250
- formatarEtiqueta = labelBucket,
129
+ formatar,
130
+ formatarEtiqueta,
251
131
  onClick
252
132
  }) {
253
- const tema = useTema();
254
- const corDe = (i) => cores?.[i] ?? cor(i);
255
- return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
256
- Line,
133
+ return /* @__PURE__ */ jsx(
134
+ LineChart,
257
135
  {
258
- data: {
259
- labels: serie.labels.map(formatarEtiqueta),
260
- datasets: serie.series.map((s, i) => ({
261
- label: s.nome,
262
- data: s.valores,
263
- borderColor: corDe(i),
264
- backgroundColor: area ? `${corDe(i)}22` : corDe(i),
265
- fill: area,
266
- tension: 0.25,
267
- pointRadius: serie.labels.length > 24 ? 0 : 3,
268
- pointHoverRadius: 4
269
- }))
270
- },
271
- options: {
272
- maintainAspectRatio: false,
273
- responsive: true,
274
- interaction: { mode: "index", intersect: false },
275
- ...clique(serie.labels, serie.series, onClick),
276
- plugins: {
277
- legend: legendaBaixo(tema),
278
- tooltip: { callbacks: { label: (c) => `${c.dataset.label}: ${formatar(Number(c.parsed.y))}` } }
279
- },
280
- scales: {
281
- x: { grid: { display: false }, ticks: { color: tema.texto, autoSkip: true, maxRotation: 0 } },
282
- y: { beginAtZero: true, grid: { color: tema.grelha }, ticks: { color: tema.texto, callback: (v) => formatar(Number(v)) } }
283
- }
284
- }
136
+ labels: serie.labels,
137
+ series: serie.series.map((s) => ({ name: s.nome, values: s.valores })),
138
+ area,
139
+ colors: cores,
140
+ format: formatar,
141
+ formatLabel: formatarEtiqueta,
142
+ height: altura,
143
+ legend: true,
144
+ onClick
285
145
  }
286
- ) });
146
+ );
287
147
  }
288
148
  function Bolhas({
289
149
  pontos,
@@ -1 +1 @@
1
- {"version":3,"file":"Charts.js","sources":["../../../../../src/legacy/components/charts/Charts.tsx"],"sourcesContent":["import React from 'react'\r\nimport { Chart as ChartJS, registerables, type ActiveElement, type ChartEvent } from 'chart.js'\r\nimport ChartDataLabels from 'chartjs-plugin-datalabels'\r\nimport { Bar, Doughnut, Line, Bubble } from 'react-chartjs-2'\r\nimport { useIsDark } from '../../../v1/capabilities/theme/useIsDark'\r\n\r\n// ---------------------------------------------------------------------------\r\n// Local types (inlined — originally imported from CRE `../types`).\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Stacked series over a set of shared labels (e.g. a dimension over time).\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport interface SerieEmpilhada {\r\n labels: string[]\r\n series: { nome: string; valores: number[] }[]\r\n}\r\n\r\n/**\r\n * A simple distribution: parallel arrays of labels and values.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport interface Distribuicao {\r\n labels: string[]\r\n valores: number[]\r\n}\r\n\r\n/**\r\n * Clique num elemento do gráfico: a etiqueta (ex: \"Incidente\"), a posição dela\r\n * e o nome da série (quando há várias). Serve para filtrar ao clicar.\r\n */\r\nexport type CliqueGrafico = (label: string, indice: number, serie?: string) => void\r\n\r\n// ---------------------------------------------------------------------------\r\n// Local formatting helpers (inlined — originally imported from CRE `../lib/format`).\r\n// Behavior kept close to the originals; the library stays self-contained.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Currency formatter. Thousands separated by '.', decimals by ',', suffixed ' €'.\r\n * Independent of Intl/ICU so the thousands separator is stable across browsers.\r\n */\r\nfunction euros(v: number | null | undefined): string {\r\n if (v == null) return '—'\r\n const negativo = v < 0\r\n const [inteiro, decimal] = Math.abs(v).toFixed(2).split('.')\r\n const comMilhares = inteiro.replace(/\\B(?=(\\d{3})+(?!\\d))/g, '.')\r\n return `${negativo ? '-' : ''}${comMilhares},${decimal} €`\r\n}\r\n\r\n/** 'YYYY-Sxx' / 'YYYY-MM' / 'YYYY-MM-DD' → short axis label. */\r\nfunction labelBucket(b: string): string {\r\n if (/^\\d{4}-S\\d{2}$/.test(b)) return `S${b.slice(6)}/${b.slice(2, 4)}`\r\n if (/^\\d{4}-\\d{2}$/.test(b)) return `${b.slice(5)}/${b.slice(0, 4)}`\r\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(b)) return `${b.slice(8)}/${b.slice(5, 7)}`\r\n return b\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Chart.js registration (once, at module load).\r\n// ---------------------------------------------------------------------------\r\n\r\n// Regista TUDO (registerables inclui Filler, controllers, scales) + o plugin de\r\n// datalabels. O registo parcial degenerava a geometria (doughnut colapsado) e o\r\n// aviso \"fill sem Filler\". registerables = equivalente a chart.js/auto.\r\nChartJS.register(...registerables, ChartDataLabels)\r\n// Datalabels off by default — enabled only where it improves readability.\r\nChartJS.defaults.set('plugins.datalabels', { display: false })\r\nChartJS.defaults.font.family = \"'Montserrat', sans-serif\"\r\nChartJS.defaults.color = '#5B6577'\r\n\r\n/**\r\n * Categorical palette. Stable order so the same dimension always maps to the same color.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport const PALETA = [\r\n '#1868B0',\r\n '#1F8A55',\r\n '#B96E00',\r\n '#C23B34',\r\n '#7C3AED',\r\n '#0891B2',\r\n '#DB2777',\r\n '#65A30D',\r\n '#4B5563',\r\n '#D97706',\r\n '#2563EB',\r\n '#059669',\r\n]\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function cor(i: number): string {\r\n return PALETA[i % PALETA.length]!\r\n}\r\n\r\n/**\r\n * Cores do texto e da grelha conforme o tema: as do claro sobre o fundo escuro\r\n * deixavam os eixos e os valores ilegíveis.\r\n */\r\nfunction useTema() {\r\n const escuro = useIsDark()\r\n return escuro\r\n ? { texto: '#CBD5E1', forte: '#E2E8F0', grelha: '#1E293B' }\r\n : { texto: '#5B6577', forte: '#182238', grelha: '#EEF1F5' }\r\n}\r\n\r\nfunction legendaBaixo(tema: ReturnType<typeof useTema>) {\r\n return {\r\n position: 'bottom' as const,\r\n labels: { boxWidth: 10, font: { size: 11 }, color: tema.forte, usePointStyle: true, pointStyle: 'circle' as const },\r\n }\r\n}\r\n\r\n/** Liga o clique do Chart.js ao `onClick` da biblioteca, com a mão por cima dos elementos. */\r\nfunction clique(labels: string[], series: { nome: string }[] | undefined, onClick?: CliqueGrafico) {\r\n if (!onClick) return {}\r\n return {\r\n onClick: (_e: ChartEvent, els: ActiveElement[]) => {\r\n const el = els[0]\r\n if (!el) return\r\n onClick(labels[el.index]!, el.index, series?.[el.datasetIndex]?.nome)\r\n },\r\n onHover: (e: ChartEvent, els: ActiveElement[]) => {\r\n const alvo = e.native?.target as HTMLElement | undefined\r\n if (alvo) alvo.style.cursor = els.length ? 'pointer' : 'default'\r\n },\r\n }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Barras — o componente geral; BarrasEmpilhadas e BarrasHorizontais são atalhos.\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface BarrasProps {\r\n labels: string[]\r\n /** Uma ou várias séries (ex: \"Atribuídos\" e \"Resolvidos\"), lado a lado ou empilhadas. */\r\n series: { nome: string; valores: number[]; cor?: string }[]\r\n /** Barras deitadas, com as etiquetas à esquerda (bom para nomes compridos e rankings). */\r\n horizontal?: boolean\r\n /** Séries empilhadas em vez de lado a lado. */\r\n empilhado?: boolean\r\n /** Altura em px. Nas horizontais, por omissão cresce com o número de barras. */\r\n altura?: number\r\n /** Só as primeiras N (ex: top 10). */\r\n limite?: number\r\n /** Cor de cada barra, quando há uma só série (ex: a cor de cada estado). */\r\n cores?: string[]\r\n /** Formato dos valores no tooltip, nas etiquetas e no eixo (ex: minutos → \"7h30\"). */\r\n formatar?: (v: number) => string\r\n /** Mostra o valor na ponta de cada barra. */\r\n valores?: boolean\r\n /** Junta a percentagem do total ao valor (ex: \"12 (34%)\"); só com uma série. */\r\n percentagem?: boolean\r\n /** Legenda das séries. Por omissão, só quando há mais do que uma. */\r\n legenda?: boolean\r\n /** Formato das etiquetas do eixo (ex: datas). Por omissão, datas ISO ficam curtas. */\r\n formatarEtiqueta?: (label: string) => string\r\n /** Clique numa barra: devolve a etiqueta, a posição e a série. */\r\n onClick?: CliqueGrafico\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function Barras({\r\n labels: todas,\r\n series: todasSeries,\r\n horizontal = false,\r\n empilhado = false,\r\n altura,\r\n limite,\r\n cores,\r\n formatar = (v) => String(v),\r\n valores = true,\r\n percentagem = false,\r\n legenda,\r\n formatarEtiqueta = labelBucket,\r\n onClick,\r\n}: BarrasProps) {\r\n const tema = useTema()\r\n const labels = limite ? todas.slice(0, limite) : todas\r\n const series = todasSeries.map((s) => ({ ...s, valores: limite ? s.valores.slice(0, limite) : s.valores }))\r\n const uma = series.length === 1\r\n // A percentagem é do total, não só das barras mostradas (com `limite` seria das top N).\r\n const total = uma ? todasSeries[0]!.valores.reduce((a, b) => a + (b || 0), 0) : 0\r\n const etiqueta = (v: number) => (percentagem && uma && total > 0 ? `${formatar(v)} (${Math.round((v / total) * 100)}%)` : formatar(v))\r\n const alturaFinal = altura ?? (horizontal ? Math.max(200, labels.length * 30 + 40) : 260)\r\n const eixoValor = { beginAtZero: true, stacked: empilhado, grid: { color: tema.grelha }, ticks: { color: tema.texto, callback: (v: string | number) => formatar(Number(v)) } }\r\n const eixoEtiqueta = { stacked: empilhado, grid: { display: false }, ticks: { color: tema.texto } }\r\n\r\n return (\r\n <div style={{ height: alturaFinal }}>\r\n <Bar\r\n data={{\r\n labels: labels.map(formatarEtiqueta),\r\n datasets: series.map((s, i) => ({\r\n label: s.nome,\r\n data: s.valores,\r\n backgroundColor: uma && cores ? labels.map((_, j) => cores[j % cores.length]!) : (s.cor ?? cor(i)),\r\n borderRadius: 4,\r\n maxBarThickness: 40,\r\n })),\r\n }}\r\n options={{\r\n indexAxis: horizontal ? 'y' : 'x',\r\n maintainAspectRatio: false,\r\n responsive: true,\r\n ...clique(labels, series, onClick),\r\n plugins: {\r\n legend: legenda ?? !uma ? legendaBaixo(tema) : { display: false },\r\n tooltip: { callbacks: { label: (c) => `${c.dataset.label ? `${c.dataset.label}: ` : ''}${etiqueta(Number(c.parsed[horizontal ? 'x' : 'y']))}` } },\r\n datalabels: {\r\n display: (c) => valores && !empilhado && Number(c.dataset.data[c.dataIndex]) > 0,\r\n anchor: 'end',\r\n align: 'end',\r\n color: tema.forte,\r\n font: { size: 10, weight: 600 },\r\n formatter: (v: number) => etiqueta(v),\r\n },\r\n },\r\n scales: horizontal ? { x: eixoValor, y: eixoEtiqueta } : { x: eixoEtiqueta, y: eixoValor },\r\n layout: { padding: horizontal ? { right: valores ? (percentagem ? 56 : 32) : 8 } : { top: valores ? 18 : 4 } },\r\n }}\r\n />\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Stacked bars for a dimension over time.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function BarrasEmpilhadas({ serie, altura = 280, onClick }: { serie: SerieEmpilhada; altura?: number; onClick?: CliqueGrafico }) {\r\n return <Barras labels={serie.labels} series={serie.series} empilhado altura={altura} onClick={onClick} legenda />\r\n}\r\n\r\n/**\r\n * Horizontal bars of a distribution (top N).\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function BarrasHorizontais({\r\n dados,\r\n altura = 280,\r\n limite = 12,\r\n onClick,\r\n}: {\r\n dados: Distribuicao\r\n altura?: number\r\n limite?: number\r\n onClick?: CliqueGrafico\r\n}) {\r\n const n = Math.min(limite, dados.labels.length)\r\n return (\r\n <Barras\r\n horizontal\r\n labels={dados.labels}\r\n series={[{ nome: '', valores: dados.valores }]}\r\n cores={Array.from({ length: n }, (_, i) => cor(i))}\r\n limite={limite}\r\n altura={altura}\r\n onClick={onClick}\r\n />\r\n )\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function Anel({\r\n dados,\r\n altura = 260,\r\n cores,\r\n formatar = (v) => String(v),\r\n onClick,\r\n}: {\r\n dados: Distribuicao\r\n altura?: number\r\n cores?: string[]\r\n /** Formato dos valores no tooltip. */\r\n formatar?: (v: number) => string\r\n /** Clique numa fatia: devolve a etiqueta e a posição. */\r\n onClick?: CliqueGrafico\r\n}) {\r\n const tema = useTema()\r\n const total = dados.valores.reduce((a, b) => a + (b || 0), 0)\r\n return (\r\n <div style={{ height: altura }}>\r\n <Doughnut\r\n data={{\r\n labels: dados.labels,\r\n datasets: [\r\n {\r\n data: dados.valores,\r\n backgroundColor: cores ?? dados.labels.map((_, i) => cor(i)),\r\n borderWidth: 0,\r\n },\r\n ],\r\n }}\r\n options={{\r\n maintainAspectRatio: false,\r\n responsive: true,\r\n cutout: '58%',\r\n ...clique(dados.labels, undefined, onClick),\r\n plugins: {\r\n legend: legendaBaixo(tema),\r\n tooltip: {\r\n callbacks: {\r\n label: (c) => {\r\n const v = Number(c.parsed)\r\n return ` ${c.label}: ${formatar(v)}${total > 0 ? ` (${Math.round((v / total) * 100)}%)` : ''}`\r\n },\r\n },\r\n },\r\n datalabels: {\r\n // Só nas fatias que têm espaço para o número.\r\n display: (c) => total > 0 && Number(c.dataset.data[c.dataIndex]) / total >= 0.05,\r\n color: '#FFFFFF',\r\n font: { size: 11, weight: 700 },\r\n formatter: (v: number) => formatar(v),\r\n },\r\n },\r\n }}\r\n />\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Simple line chart — optionally formats values as currency.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function Linha({\r\n dados,\r\n altura = 260,\r\n euros: emEuros = false,\r\n onClick,\r\n}: {\r\n dados: Distribuicao\r\n altura?: number\r\n euros?: boolean\r\n onClick?: CliqueGrafico\r\n}) {\r\n const tema = useTema()\r\n return (\r\n <div style={{ height: altura }}>\r\n <Line\r\n data={{\r\n labels: dados.labels.map(labelBucket),\r\n datasets: [\r\n {\r\n label: '',\r\n data: dados.valores,\r\n borderColor: '#C23B34',\r\n backgroundColor: 'rgba(194,59,52,0.12)',\r\n fill: true,\r\n tension: 0.25,\r\n pointRadius: 3,\r\n },\r\n ],\r\n }}\r\n options={{\r\n maintainAspectRatio: false,\r\n responsive: true,\r\n ...clique(dados.labels, undefined, onClick),\r\n plugins: {\r\n legend: { display: false },\r\n tooltip: {\r\n callbacks: {\r\n label: (c) => (emEuros ? euros(Number(c.parsed.y)) : String(c.parsed.y)),\r\n },\r\n },\r\n },\r\n scales: {\r\n x: { grid: { display: false }, ticks: { color: tema.texto } },\r\n y: {\r\n beginAtZero: true,\r\n grid: { color: tema.grelha },\r\n ticks: { color: tema.texto, callback: (v) => (emEuros ? euros(Number(v)) : String(v)) },\r\n },\r\n },\r\n }}\r\n />\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Several lines over the same labels — NOT stacked.\r\n *\r\n * Separate from `BarrasEmpilhadas` on purpose: two series that measure the same\r\n * thing at two moments (created vs closed) must be read against each other, and\r\n * stacking them would draw their sum, which means nothing.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function Linhas({\r\n serie,\r\n altura = 280,\r\n area = false,\r\n cores,\r\n formatar = (v) => String(v),\r\n formatarEtiqueta = labelBucket,\r\n onClick,\r\n}: {\r\n serie: SerieEmpilhada\r\n altura?: number\r\n /// Fill under each line. Only legible with one or two series.\r\n area?: boolean\r\n /** Cor de cada série, pela ordem. */\r\n cores?: string[]\r\n /** Formato dos valores no tooltip e no eixo. */\r\n formatar?: (v: number) => string\r\n /** Formato das etiquetas do eixo (ex: datas). */\r\n formatarEtiqueta?: (label: string) => string\r\n /** Clique num ponto: devolve a etiqueta (ex: o dia) e a série. */\r\n onClick?: CliqueGrafico\r\n}) {\r\n const tema = useTema()\r\n const corDe = (i: number) => cores?.[i] ?? cor(i)\r\n return (\r\n <div style={{ height: altura }}>\r\n <Line\r\n data={{\r\n labels: serie.labels.map(formatarEtiqueta),\r\n datasets: serie.series.map((s, i) => ({\r\n label: s.nome,\r\n data: s.valores,\r\n borderColor: corDe(i),\r\n backgroundColor: area ? `${corDe(i)}22` : corDe(i),\r\n fill: area,\r\n tension: 0.25,\r\n pointRadius: serie.labels.length > 24 ? 0 : 3,\r\n pointHoverRadius: 4,\r\n })),\r\n }}\r\n options={{\r\n maintainAspectRatio: false,\r\n responsive: true,\r\n interaction: { mode: 'index', intersect: false },\r\n ...clique(serie.labels, serie.series, onClick),\r\n plugins: {\r\n legend: legendaBaixo(tema),\r\n tooltip: { callbacks: { label: (c) => `${c.dataset.label}: ${formatar(Number(c.parsed.y))}` } },\r\n },\r\n scales: {\r\n x: { grid: { display: false }, ticks: { color: tema.texto, autoSkip: true, maxRotation: 0 } },\r\n y: { beginAtZero: true, grid: { color: tema.grelha }, ticks: { color: tema.texto, callback: (v) => formatar(Number(v)) } },\r\n },\r\n }}\r\n />\r\n </div>\r\n )\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface PontoRisco {\r\n responsavel: string\r\n otsAtivas: number\r\n percentagemForaPrazo: number\r\n valorPenalizado: number\r\n}\r\n\r\n/**\r\n * Risk-map bubbles: x = active OTs, y = % overdue, r = penalized value.\r\n * The radius is normalized against the max so bubbles stay readable at any scale.\r\n *\r\n * Axis titles and the tooltip formatter are exposed as optional props with\r\n * neutral English defaults (previously hardcoded Portuguese) so the component\r\n * carries no locale of its own.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function Bolhas({\r\n pontos,\r\n altura = 280,\r\n onClick,\r\n xTitle = 'Active OTs',\r\n yTitle = '% overdue',\r\n tooltipLabel = (p) => `${p.responsavel}: ${p.otsAtivas} active, ${p.percentagemForaPrazo}% overdue, ${euros(p.valorPenalizado)}`,\r\n}: {\r\n pontos: PontoRisco[]\r\n altura?: number\r\n onClick?: (p: PontoRisco) => void\r\n xTitle?: string\r\n yTitle?: string\r\n tooltipLabel?: (p: PontoRisco) => string\r\n}) {\r\n const tema = useTema()\r\n const maxValor = Math.max(1, ...pontos.map((p) => p.valorPenalizado))\r\n return (\r\n <div style={{ height: altura }}>\r\n <Bubble\r\n data={{\r\n datasets: pontos.map((p, i) => ({\r\n label: p.responsavel,\r\n data: [\r\n {\r\n x: p.otsAtivas,\r\n y: p.percentagemForaPrazo,\r\n r: 6 + (p.valorPenalizado / maxValor) * 18,\r\n },\r\n ],\r\n backgroundColor: `${cor(i)}CC`,\r\n borderColor: cor(i),\r\n })),\r\n }}\r\n options={{\r\n maintainAspectRatio: false,\r\n responsive: true,\r\n onClick: (_e, elements) => {\r\n const idx = elements[0]?.datasetIndex\r\n if (idx != null && onClick) onClick(pontos[idx]!)\r\n },\r\n plugins: {\r\n legend: { display: false },\r\n tooltip: {\r\n callbacks: {\r\n label: (c) => tooltipLabel(pontos[c.datasetIndex]!),\r\n },\r\n },\r\n },\r\n scales: {\r\n x: { beginAtZero: true, title: { display: true, text: xTitle, color: tema.texto }, grid: { color: tema.grelha }, ticks: { color: tema.texto } },\r\n y: { beginAtZero: true, title: { display: true, text: yTitle, color: tema.texto }, grid: { color: tema.grelha }, ticks: { color: tema.texto } },\r\n },\r\n }}\r\n />\r\n </div>\r\n )\r\n}\r\n"],"names":["ChartJS"],"mappings":"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"}
1
+ {"version":3,"file":"Charts.js","sources":["../../../../../src/legacy/components/charts/Charts.tsx"],"sourcesContent":["import React from 'react'\r\nimport { Chart as ChartJS, registerables } from 'chart.js'\r\nimport ChartDataLabels from 'chartjs-plugin-datalabels'\r\nimport { Bubble } from 'react-chartjs-2'\r\nimport { useIsDark } from '../../../v1/capabilities/theme/useIsDark'\r\nimport { BarChart, DonutChart, LineChart } from '../../../v1/components/data/Charts/Charts'\r\n\r\n// ---------------------------------------------------------------------------\r\n// Local types (inlined — originally imported from CRE `../types`).\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Stacked series over a set of shared labels (e.g. a dimension over time).\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport interface SerieEmpilhada {\r\n labels: string[]\r\n series: { nome: string; valores: number[] }[]\r\n}\r\n\r\n/**\r\n * A simple distribution: parallel arrays of labels and values.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport interface Distribuicao {\r\n labels: string[]\r\n valores: number[]\r\n}\r\n\r\n/**\r\n * Clique num elemento do gráfico: a etiqueta (ex: \"Incidente\"), a posição dela\r\n * e o nome da série (quando há várias). Serve para filtrar ao clicar.\r\n */\r\nexport type CliqueGrafico = (label: string, indice: number, serie?: string) => void\r\n\r\n// ---------------------------------------------------------------------------\r\n// Local formatting helpers (inlined — originally imported from CRE `../lib/format`).\r\n// Behavior kept close to the originals; the library stays self-contained.\r\n// ---------------------------------------------------------------------------\r\n\r\n/**\r\n * Currency formatter. Thousands separated by '.', decimals by ',', suffixed ' €'.\r\n * Independent of Intl/ICU so the thousands separator is stable across browsers.\r\n */\r\nfunction euros(v: number | null | undefined): string {\r\n if (v == null) return '—'\r\n const negativo = v < 0\r\n const [inteiro, decimal] = Math.abs(v).toFixed(2).split('.')\r\n const comMilhares = inteiro.replace(/\\B(?=(\\d{3})+(?!\\d))/g, '.')\r\n return `${negativo ? '-' : ''}${comMilhares},${decimal} €`\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Chart.js registration (once, at module load).\r\n// ---------------------------------------------------------------------------\r\n\r\n// Regista TUDO (registerables inclui Filler, controllers, scales) + o plugin de\r\n// datalabels. O registo parcial degenerava a geometria (doughnut colapsado) e o\r\n// aviso \"fill sem Filler\". registerables = equivalente a chart.js/auto.\r\nChartJS.register(...registerables, ChartDataLabels)\r\n// Datalabels off by default — enabled only where it improves readability.\r\nChartJS.defaults.set('plugins.datalabels', { display: false })\r\nChartJS.defaults.font.family = \"'Montserrat', sans-serif\"\r\nChartJS.defaults.color = '#5B6577'\r\n\r\n/**\r\n * Categorical palette. Stable order so the same dimension always maps to the same color.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport const PALETA = [\r\n '#1868B0',\r\n '#1F8A55',\r\n '#B96E00',\r\n '#C23B34',\r\n '#7C3AED',\r\n '#0891B2',\r\n '#DB2777',\r\n '#65A30D',\r\n '#4B5563',\r\n '#D97706',\r\n '#2563EB',\r\n '#059669',\r\n]\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport function cor(i: number): string {\r\n return PALETA[i % PALETA.length]!\r\n}\r\n\r\n/**\r\n * Cores do texto e da grelha conforme o tema: as do claro sobre o fundo escuro\r\n * deixavam os eixos e os valores ilegíveis.\r\n */\r\nfunction useTema() {\r\n const escuro = useIsDark()\r\n return escuro\r\n ? { texto: '#CBD5E1', forte: '#E2E8F0', grelha: '#1E293B' }\r\n : { texto: '#5B6577', forte: '#182238', grelha: '#EEF1F5' }\r\n}\r\n\r\n// ---------------------------------------------------------------------------\r\n// Barras, Anel e Linhas: adaptadores dos gráficos V1 (BarChart, DonutChart, LineChart),\r\n// com as props de sempre. Só as Bolhas continuam desenhadas aqui.\r\n// ---------------------------------------------------------------------------\r\n\r\nexport interface BarrasProps {\r\n labels: string[]\r\n /** Uma ou várias séries (ex: \"Atribuídos\" e \"Resolvidos\"), lado a lado ou empilhadas. */\r\n series: { nome: string; valores: number[]; cor?: string }[]\r\n /** Barras deitadas, com as etiquetas à esquerda (bom para nomes compridos e rankings). */\r\n horizontal?: boolean\r\n /** Séries empilhadas em vez de lado a lado. */\r\n empilhado?: boolean\r\n /** Altura em px. Nas horizontais, por omissão cresce com o número de barras. */\r\n altura?: number\r\n /** Só as primeiras N (ex: top 10). */\r\n limite?: number\r\n /** Cor de cada barra, quando há uma só série (ex: a cor de cada estado). */\r\n cores?: string[]\r\n /** Formato dos valores no tooltip, nas etiquetas e no eixo (ex: minutos → \"7h30\"). */\r\n formatar?: (v: number) => string\r\n /** Mostra o valor na ponta de cada barra. */\r\n valores?: boolean\r\n /** Junta a percentagem do total ao valor (ex: \"12 (34%)\"); só com uma série. */\r\n percentagem?: boolean\r\n /** Legenda das séries. Por omissão, só quando há mais do que uma. */\r\n legenda?: boolean\r\n /** Formato das etiquetas do eixo (ex: datas). Por omissão, datas ISO ficam curtas. */\r\n formatarEtiqueta?: (label: string) => string\r\n /** Clique numa barra: devolve a etiqueta, a posição e a série. */\r\n onClick?: CliqueGrafico\r\n}\r\n\r\n/**\r\n * Migrado para o V1: adaptador do `BarChart` (mesmas props de sempre).\r\n * @deprecated Legacy — usar o `BarChart` do V1. Ver Storybook › Legacy.\r\n */\r\nexport function Barras({\r\n labels,\r\n series,\r\n horizontal,\r\n empilhado,\r\n altura,\r\n limite,\r\n cores,\r\n formatar,\r\n valores,\r\n percentagem,\r\n legenda,\r\n formatarEtiqueta,\r\n onClick,\r\n}: BarrasProps) {\r\n return (\r\n <BarChart\r\n labels={labels}\r\n series={series.map((s) => ({ name: s.nome, values: s.valores, color: s.cor }))}\r\n horizontal={horizontal}\r\n stacked={empilhado}\r\n height={altura}\r\n limit={limite}\r\n colors={cores}\r\n format={formatar}\r\n showValues={valores}\r\n showPercent={percentagem}\r\n legend={legenda}\r\n formatLabel={formatarEtiqueta}\r\n onClick={onClick}\r\n />\r\n )\r\n}\r\n\r\n/**\r\n * Stacked bars for a dimension over time.\r\n * @deprecated Legacy — usar o `BarChart` do V1 com `stacked`. Ver Storybook › Legacy.\r\n */\r\nexport function BarrasEmpilhadas({ serie, altura = 280, onClick }: { serie: SerieEmpilhada; altura?: number; onClick?: CliqueGrafico }) {\r\n return <Barras labels={serie.labels} series={serie.series} empilhado altura={altura} onClick={onClick} legenda />\r\n}\r\n\r\n/**\r\n * Horizontal bars of a distribution (top N).\r\n * @deprecated Legacy — usar o `BarChart` do V1 com `horizontal`. Ver Storybook › Legacy.\r\n */\r\nexport function BarrasHorizontais({\r\n dados,\r\n altura = 280,\r\n limite = 12,\r\n onClick,\r\n}: {\r\n dados: Distribuicao\r\n altura?: number\r\n limite?: number\r\n onClick?: CliqueGrafico\r\n}) {\r\n const n = Math.min(limite, dados.labels.length)\r\n return (\r\n <Barras\r\n horizontal\r\n labels={dados.labels}\r\n series={[{ nome: '', valores: dados.valores }]}\r\n cores={Array.from({ length: n }, (_, i) => cor(i))}\r\n limite={limite}\r\n altura={altura}\r\n onClick={onClick}\r\n />\r\n )\r\n}\r\n\r\n/**\r\n * Migrado para o V1: adaptador do `DonutChart`.\r\n * @deprecated Legacy — usar o `DonutChart` do V1. Ver Storybook › Legacy.\r\n */\r\nexport function Anel({\r\n dados,\r\n altura = 260,\r\n cores,\r\n formatar,\r\n onClick,\r\n}: {\r\n dados: Distribuicao\r\n altura?: number\r\n cores?: string[]\r\n /** Formato dos valores no tooltip. */\r\n formatar?: (v: number) => string\r\n /** Clique numa fatia: devolve a etiqueta e a posição. */\r\n onClick?: CliqueGrafico\r\n}) {\r\n return <DonutChart labels={dados.labels} values={dados.valores} height={altura} colors={cores} format={formatar} onClick={onClick} />\r\n}\r\n\r\n/**\r\n * Simple line chart — optionally formats values as currency.\r\n * @deprecated Legacy — usar o `LineChart` do V1. Ver Storybook › Legacy.\r\n */\r\nexport function Linha({\r\n dados,\r\n altura = 260,\r\n euros: emEuros = false,\r\n onClick,\r\n}: {\r\n dados: Distribuicao\r\n altura?: number\r\n euros?: boolean\r\n onClick?: CliqueGrafico\r\n}) {\r\n return (\r\n <LineChart\r\n labels={dados.labels}\r\n series={[{ name: '', values: dados.valores, color: '#C23B34' }]}\r\n area\r\n height={altura}\r\n format={emEuros ? (v) => euros(v) : undefined}\r\n legend={false}\r\n onClick={onClick}\r\n />\r\n )\r\n}\r\n\r\n/**\r\n * Several lines over the same labels — NOT stacked.\r\n * Migrado para o V1: adaptador do `LineChart` (com a legenda sempre, como antes).\r\n * @deprecated Legacy — usar o `LineChart` do V1. Ver Storybook › Legacy.\r\n */\r\nexport function Linhas({\r\n serie,\r\n altura = 280,\r\n area = false,\r\n cores,\r\n formatar,\r\n formatarEtiqueta,\r\n onClick,\r\n}: {\r\n serie: SerieEmpilhada\r\n altura?: number\r\n /// Fill under each line. Only legible with one or two series.\r\n area?: boolean\r\n /** Cor de cada série, pela ordem. */\r\n cores?: string[]\r\n /** Formato dos valores no tooltip e no eixo. */\r\n formatar?: (v: number) => string\r\n /** Formato das etiquetas do eixo (ex: datas). */\r\n formatarEtiqueta?: (label: string) => string\r\n /** Clique num ponto: devolve a etiqueta (ex: o dia) e a série. */\r\n onClick?: CliqueGrafico\r\n}) {\r\n return (\r\n <LineChart\r\n labels={serie.labels}\r\n series={serie.series.map((s) => ({ name: s.nome, values: s.valores }))}\r\n area={area}\r\n colors={cores}\r\n format={formatar}\r\n formatLabel={formatarEtiqueta}\r\n height={altura}\r\n legend\r\n onClick={onClick}\r\n />\r\n )\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface PontoRisco {\r\n responsavel: string\r\n otsAtivas: number\r\n percentagemForaPrazo: number\r\n valorPenalizado: number\r\n}\r\n\r\n/**\r\n * Risk-map bubbles: x = active OTs, y = % overdue, r = penalized value.\r\n * The radius is normalized against the max so bubbles stay readable at any scale.\r\n *\r\n * Axis titles and the tooltip formatter are exposed as optional props with\r\n * neutral English defaults (previously hardcoded Portuguese) so the component\r\n * carries no locale of its own.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function Bolhas({\r\n pontos,\r\n altura = 280,\r\n onClick,\r\n xTitle = 'Active OTs',\r\n yTitle = '% overdue',\r\n tooltipLabel = (p) => `${p.responsavel}: ${p.otsAtivas} active, ${p.percentagemForaPrazo}% overdue, ${euros(p.valorPenalizado)}`,\r\n}: {\r\n pontos: PontoRisco[]\r\n altura?: number\r\n onClick?: (p: PontoRisco) => void\r\n xTitle?: string\r\n yTitle?: string\r\n tooltipLabel?: (p: PontoRisco) => string\r\n}) {\r\n const tema = useTema()\r\n const maxValor = Math.max(1, ...pontos.map((p) => p.valorPenalizado))\r\n return (\r\n <div style={{ height: altura }}>\r\n <Bubble\r\n data={{\r\n datasets: pontos.map((p, i) => ({\r\n label: p.responsavel,\r\n data: [\r\n {\r\n x: p.otsAtivas,\r\n y: p.percentagemForaPrazo,\r\n r: 6 + (p.valorPenalizado / maxValor) * 18,\r\n },\r\n ],\r\n backgroundColor: `${cor(i)}CC`,\r\n borderColor: cor(i),\r\n })),\r\n }}\r\n options={{\r\n maintainAspectRatio: false,\r\n responsive: true,\r\n onClick: (_e, elements) => {\r\n const idx = elements[0]?.datasetIndex\r\n if (idx != null && onClick) onClick(pontos[idx]!)\r\n },\r\n plugins: {\r\n legend: { display: false },\r\n tooltip: {\r\n callbacks: {\r\n label: (c) => tooltipLabel(pontos[c.datasetIndex]!),\r\n },\r\n },\r\n },\r\n scales: {\r\n x: { beginAtZero: true, title: { display: true, text: xTitle, color: tema.texto }, grid: { color: tema.grelha }, ticks: { color: tema.texto } },\r\n y: { beginAtZero: true, title: { display: true, text: yTitle, color: tema.texto }, grid: { color: tema.grelha }, ticks: { color: tema.texto } },\r\n },\r\n }}\r\n />\r\n </div>\r\n )\r\n}\r\n"],"names":["ChartJS"],"mappings":"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"}