@nextelco/common-ui 2.1.106 → 2.1.107

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 (177) hide show
  1. package/dist/esm/index.js +201 -145
  2. package/dist/esm/index.js.map +1 -1
  3. package/dist/esm/legacy/components/atoms/InputData/InputData.js +2 -13
  4. package/dist/esm/legacy/components/atoms/InputData/InputData.js.map +1 -1
  5. package/dist/esm/legacy/components/charts/Charts.js +147 -57
  6. package/dist/esm/legacy/components/charts/Charts.js.map +1 -1
  7. package/dist/esm/legacy/components/layout/Layout/Layout.js +21 -82
  8. package/dist/esm/legacy/components/layout/Layout/Layout.js.map +1 -1
  9. package/dist/esm/legacy/components/layout/Sidebar/Sidebar.js +29 -218
  10. package/dist/esm/legacy/components/layout/Sidebar/Sidebar.js.map +1 -1
  11. package/dist/esm/legacy/components/molecules/ColumnPicker/ColumnPicker.js +77 -10
  12. package/dist/esm/legacy/components/molecules/ColumnPicker/ColumnPicker.js.map +1 -1
  13. package/dist/esm/legacy/components/molecules/DataTable/DataTable.js +445 -337
  14. package/dist/esm/legacy/components/molecules/DataTable/DataTable.js.map +1 -1
  15. package/dist/esm/legacy/components/molecules/UserAvatar/UserAvatar.js +15 -97
  16. package/dist/esm/legacy/components/molecules/UserAvatar/UserAvatar.js.map +1 -1
  17. package/dist/esm/legacy/components/organisms/MapaPontos/MapaPontos.js +3 -3
  18. package/dist/esm/legacy/components/organisms/MapaPontos/MapaPontos.js.map +1 -1
  19. package/dist/esm/legacy/components/organisms/PainelFiltros/PainelFiltros.js +15 -3
  20. package/dist/esm/legacy/components/organisms/PainelFiltros/PainelFiltros.js.map +1 -1
  21. package/dist/esm/legacy/components/organisms/Selector/Selector.js +4 -4
  22. package/dist/esm/legacy/components/organisms/Selector/Selector.js.map +1 -1
  23. package/dist/esm/legacy/components/ui/Avisos.js +23 -76
  24. package/dist/esm/legacy/components/ui/Avisos.js.map +1 -1
  25. package/dist/esm/legacy/components/ui/CabecalhoPagina.js +14 -27
  26. package/dist/esm/legacy/components/ui/CabecalhoPagina.js.map +1 -1
  27. package/dist/esm/legacy/components/ui/CaixaPesquisa.js +3 -51
  28. package/dist/esm/legacy/components/ui/CaixaPesquisa.js.map +1 -1
  29. package/dist/esm/legacy/components/ui/Confirmacao.js +13 -31
  30. package/dist/esm/legacy/components/ui/Confirmacao.js.map +1 -1
  31. package/dist/esm/legacy/components/ui/Escolha.js +21 -137
  32. package/dist/esm/legacy/components/ui/Escolha.js.map +1 -1
  33. package/dist/esm/legacy/components/ui/FaixaAviso.js +13 -11
  34. package/dist/esm/legacy/components/ui/FaixaAviso.js.map +1 -1
  35. package/dist/esm/legacy/components/ui/Separadores.js +11 -46
  36. package/dist/esm/legacy/components/ui/Separadores.js.map +1 -1
  37. package/dist/esm/legacy/components/ui/Ui.js +18 -115
  38. package/dist/esm/legacy/components/ui/Ui.js.map +1 -1
  39. package/dist/esm/legacy/components/ui/painelFlutuante.js +5 -44
  40. package/dist/esm/legacy/components/ui/painelFlutuante.js.map +1 -1
  41. package/dist/esm/v1/capabilities/overlay/useFloatingPanel.js +52 -0
  42. package/dist/esm/v1/capabilities/overlay/useFloatingPanel.js.map +1 -0
  43. package/dist/esm/v1/capabilities/theme/useIsDark.js +15 -0
  44. package/dist/esm/v1/capabilities/theme/useIsDark.js.map +1 -0
  45. package/dist/esm/v1/capabilities/theme/useTheme.js +66 -0
  46. package/dist/esm/v1/capabilities/theme/useTheme.js.map +1 -0
  47. package/dist/esm/v1/components/data/Pagination/Pagination.js +66 -0
  48. package/dist/esm/v1/components/data/Pagination/Pagination.js.map +1 -0
  49. package/dist/esm/v1/components/feedback/Toast/Toast.js +105 -0
  50. package/dist/esm/v1/components/feedback/Toast/Toast.js.map +1 -0
  51. package/dist/esm/v1/components/layout/AppShell/AppShell.js +89 -0
  52. package/dist/esm/v1/components/layout/AppShell/AppShell.js.map +1 -0
  53. package/dist/esm/v1/components/layout/Page/Page.js +16 -0
  54. package/dist/esm/v1/components/layout/Page/Page.js.map +1 -0
  55. package/dist/esm/v1/components/layout/PageHeader/PageHeader.js +26 -0
  56. package/dist/esm/v1/components/layout/PageHeader/PageHeader.js.map +1 -0
  57. package/dist/esm/v1/components/layout/SideNav/SideNav.js +277 -0
  58. package/dist/esm/v1/components/layout/SideNav/SideNav.js.map +1 -0
  59. package/dist/esm/v1/components/layout/navTrail.js +19 -0
  60. package/dist/esm/v1/components/layout/navTrail.js.map +1 -0
  61. package/dist/esm/v1/components/overlays/ConfirmDialog/ConfirmDialog.js +95 -0
  62. package/dist/esm/v1/components/overlays/ConfirmDialog/ConfirmDialog.js.map +1 -0
  63. package/dist/esm/v1/components/overlays/Dialog/Dialog.js.map +1 -1
  64. package/dist/esm/v1/components/overlays/Tooltip/Tooltip.js +98 -0
  65. package/dist/esm/v1/components/overlays/Tooltip/Tooltip.js.map +1 -0
  66. package/dist/esm/v1/components/overlays/Tooltip/TooltipLayer.js +84 -0
  67. package/dist/esm/v1/components/overlays/Tooltip/TooltipLayer.js.map +1 -0
  68. package/dist/esm/v1/components/overlays/Tooltip/TooltipPanel.js +33 -0
  69. package/dist/esm/v1/components/overlays/Tooltip/TooltipPanel.js.map +1 -0
  70. package/dist/esm/v1/components/overlays/topLayer.js +28 -0
  71. package/dist/esm/v1/components/overlays/topLayer.js.map +1 -0
  72. package/dist/esm/v1/foundations/icons/icons.js +61 -2
  73. package/dist/esm/v1/foundations/icons/icons.js.map +1 -1
  74. package/dist/esm/v1/primitives/buttons/Button/Button.js +18 -4
  75. package/dist/esm/v1/primitives/buttons/Button/Button.js.map +1 -1
  76. package/dist/esm/v1/primitives/buttons/SegmentedControl/SegmentedControl.js +65 -0
  77. package/dist/esm/v1/primitives/buttons/SegmentedControl/SegmentedControl.js.map +1 -0
  78. package/dist/esm/v1/primitives/buttons/SegmentedControl/segmentedStyles.js +10 -0
  79. package/dist/esm/v1/primitives/buttons/SegmentedControl/segmentedStyles.js.map +1 -0
  80. package/dist/esm/v1/primitives/display/Avatar/Avatar.js +120 -0
  81. package/dist/esm/v1/primitives/display/Avatar/Avatar.js.map +1 -0
  82. package/dist/esm/v1/primitives/display/Badge/Badge.js +23 -0
  83. package/dist/esm/v1/primitives/display/Badge/Badge.js.map +1 -0
  84. package/dist/esm/v1/primitives/display/Card/Card.js +22 -0
  85. package/dist/esm/v1/primitives/display/Card/Card.js.map +1 -0
  86. package/dist/esm/v1/primitives/display/FilterChips/FilterChips.js +45 -0
  87. package/dist/esm/v1/primitives/display/FilterChips/FilterChips.js.map +1 -0
  88. package/dist/esm/v1/primitives/display/Kpi/Kpi.js +103 -0
  89. package/dist/esm/v1/primitives/display/Kpi/Kpi.js.map +1 -0
  90. package/dist/esm/v1/primitives/display/Notice/Notice.js +71 -0
  91. package/dist/esm/v1/primitives/display/Notice/Notice.js.map +1 -0
  92. package/dist/esm/v1/primitives/display/Person/Person.js +14 -0
  93. package/dist/esm/v1/primitives/display/Person/Person.js.map +1 -0
  94. package/dist/esm/v1/primitives/forms/Checkbox/Checkbox.js +1 -1
  95. package/dist/esm/v1/primitives/forms/Checkbox/Checkbox.js.map +1 -1
  96. package/dist/esm/v1/primitives/forms/DateInput/DateInput.js +150 -0
  97. package/dist/esm/v1/primitives/forms/DateInput/DateInput.js.map +1 -0
  98. package/dist/esm/v1/primitives/forms/SearchInput/SearchInput.js +43 -0
  99. package/dist/esm/v1/primitives/forms/SearchInput/SearchInput.js.map +1 -0
  100. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js +190 -12
  101. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js.map +1 -1
  102. package/dist/esm/v1/primitives/forms/field.js +4 -2
  103. package/dist/esm/v1/primitives/forms/field.js.map +1 -1
  104. package/dist/esm/v1/primitives/navigation/Tabs/Tabs.js +96 -0
  105. package/dist/esm/v1/primitives/navigation/Tabs/Tabs.js.map +1 -0
  106. package/dist/style.components.css +1 -1
  107. package/dist/style.css +1 -1
  108. package/dist/style.layered.css +1 -1
  109. package/dist/tailwind.css +24 -0
  110. package/dist/theme-boot.js +1 -0
  111. package/dist/theme.css +10 -3
  112. package/dist/tokens.css +119 -0
  113. package/dist/types/legacy/components/atoms/InputData/InputData.d.ts +4 -1
  114. package/dist/types/legacy/components/charts/Charts.d.ts +58 -5
  115. package/dist/types/legacy/components/layout/Layout/Layout.d.ts +7 -20
  116. package/dist/types/legacy/components/layout/Sidebar/Sidebar.d.ts +13 -26
  117. package/dist/types/legacy/components/molecules/ColumnPicker/ColumnPicker.d.ts +56 -3
  118. package/dist/types/legacy/components/molecules/DataTable/DataTable.d.ts +72 -7
  119. package/dist/types/legacy/components/molecules/UserAvatar/UserAvatar.d.ts +4 -5
  120. package/dist/types/legacy/components/ui/Avisos.d.ts +6 -16
  121. package/dist/types/legacy/components/ui/CabecalhoPagina.d.ts +6 -20
  122. package/dist/types/legacy/components/ui/CaixaPesquisa.d.ts +2 -4
  123. package/dist/types/legacy/components/ui/Confirmacao.d.ts +3 -10
  124. package/dist/types/legacy/components/ui/Escolha.d.ts +5 -15
  125. package/dist/types/legacy/components/ui/FaixaAviso.d.ts +2 -6
  126. package/dist/types/legacy/components/ui/Separadores.d.ts +3 -10
  127. package/dist/types/legacy/components/ui/Ui.d.ts +12 -54
  128. package/dist/types/legacy/components/ui/painelFlutuante.d.ts +6 -10
  129. package/dist/types/legacy/exports/atoms.d.ts +1 -1
  130. package/dist/types/legacy/exports/charts.d.ts +2 -2
  131. package/dist/types/legacy/exports/hooks.d.ts +2 -2
  132. package/dist/types/legacy/exports/molecules.d.ts +2 -2
  133. package/dist/types/v1/capabilities/overlay/useFloatingPanel.d.ts +20 -0
  134. package/dist/types/v1/capabilities/theme/useIsDark.d.ts +10 -0
  135. package/dist/types/v1/capabilities/theme/useTheme.d.ts +29 -0
  136. package/dist/types/v1/components/data/Pagination/Pagination.d.ts +33 -0
  137. package/dist/types/v1/components/feedback/Toast/Toast.d.ts +51 -0
  138. package/dist/types/v1/components/layout/AppShell/AppShell.d.ts +35 -0
  139. package/dist/types/v1/components/layout/Page/Page.d.ts +23 -0
  140. package/dist/types/v1/components/layout/PageHeader/PageHeader.d.ts +35 -0
  141. package/dist/types/v1/components/layout/SideNav/SideNav.d.ts +92 -0
  142. package/dist/types/v1/components/layout/navTrail.d.ts +15 -0
  143. package/dist/types/v1/components/overlays/ConfirmDialog/ConfirmDialog.d.ts +59 -0
  144. package/dist/types/v1/components/overlays/Dialog/Dialog.d.ts +3 -2
  145. package/dist/types/v1/components/overlays/Tooltip/Tooltip.d.ts +34 -0
  146. package/dist/types/v1/components/overlays/Tooltip/TooltipLayer.d.ts +18 -0
  147. package/dist/types/v1/components/overlays/Tooltip/TooltipPanel.d.ts +23 -0
  148. package/dist/types/v1/components/overlays/topLayer.d.ts +23 -0
  149. package/dist/types/v1/foundations/icons/icons.d.ts +20 -0
  150. package/dist/types/v1/index.d.ts +43 -1
  151. package/dist/types/v1/primitives/buttons/Button/Button.d.ts +20 -5
  152. package/dist/types/v1/primitives/buttons/SegmentedControl/SegmentedControl.d.ts +28 -0
  153. package/dist/types/v1/primitives/buttons/SegmentedControl/segmentedStyles.d.ts +16 -0
  154. package/dist/types/v1/primitives/display/Avatar/Avatar.d.ts +30 -0
  155. package/dist/types/v1/primitives/display/Badge/Badge.d.ts +25 -0
  156. package/dist/types/v1/primitives/display/Card/Card.d.ts +22 -0
  157. package/dist/types/v1/primitives/display/FilterChips/FilterChips.d.ts +33 -0
  158. package/dist/types/v1/primitives/display/Kpi/Kpi.d.ts +68 -0
  159. package/dist/types/v1/primitives/display/Notice/Notice.d.ts +29 -0
  160. package/dist/types/v1/primitives/display/Person/Person.d.ts +15 -0
  161. package/dist/types/v1/primitives/forms/DateInput/DateInput.d.ts +43 -0
  162. package/dist/types/v1/primitives/forms/SearchInput/SearchInput.d.ts +23 -0
  163. package/dist/types/v1/primitives/forms/SelectInput/SelectInput.d.ts +60 -12
  164. package/dist/types/v1/primitives/forms/field.d.ts +2 -0
  165. package/dist/types/v1/primitives/navigation/Tabs/Tabs.d.ts +58 -0
  166. package/package.json +3 -3
  167. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.css +0 -17
  168. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.js +0 -41
  169. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.js.map +0 -1
  170. package/dist/esm/legacy/components/molecules/Search/Search.css +0 -18
  171. package/dist/esm/legacy/components/molecules/Search/Search.js +0 -30
  172. package/dist/esm/legacy/components/molecules/Search/Search.js.map +0 -1
  173. package/dist/esm/legacy/hooks/useTheme.js +0 -39
  174. package/dist/esm/legacy/hooks/useTheme.js.map +0 -1
  175. package/dist/types/legacy/components/atoms/Tooltip/Tooltip.d.ts +0 -13
  176. package/dist/types/legacy/components/molecules/Search/Search.d.ts +0 -7
  177. package/dist/types/legacy/hooks/useTheme.d.ts +0 -17
@@ -2,6 +2,7 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { Chart, registerables } from "chart.js";
3
3
  import ChartDataLabels from "chartjs-plugin-datalabels";
4
4
  import { Doughnut, Bar, Bubble, Line } from "react-chartjs-2";
5
+ import { useIsDark } from "../../../v1/capabilities/theme/useIsDark.js";
5
6
  function euros(v) {
6
7
  if (v == null) return "—";
7
8
  const negativo = v < 0;
@@ -36,63 +37,122 @@ const PALETA = [
36
37
  function cor(i) {
37
38
  return PALETA[i % PALETA.length];
38
39
  }
39
- const legendaBaixo = {
40
- position: "bottom",
41
- labels: { boxWidth: 10, font: { size: 11 }, color: "#182238", usePointStyle: true, pointStyle: "circle" }
42
- };
43
- function BarrasEmpilhadas({ serie, altura = 280 }) {
44
- return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
40
+ function useTema() {
41
+ const escuro = useIsDark();
42
+ return escuro ? { texto: "#CBD5E1", forte: "#E2E8F0", grelha: "#1E293B" } : { texto: "#5B6577", forte: "#182238", grelha: "#EEF1F5" };
43
+ }
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
+ function Barras({
65
+ labels: todas,
66
+ series: todasSeries,
67
+ horizontal = false,
68
+ empilhado = false,
69
+ altura,
70
+ limite,
71
+ cores,
72
+ formatar = (v) => String(v),
73
+ valores = true,
74
+ percentagem = false,
75
+ legenda,
76
+ formatarEtiqueta = labelBucket,
77
+ onClick
78
+ }) {
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(
45
89
  Bar,
46
90
  {
47
91
  data: {
48
- labels: serie.labels.map(labelBucket),
49
- datasets: serie.series.map((s, i) => ({
92
+ labels: labels.map(formatarEtiqueta),
93
+ datasets: series.map((s, i) => ({
50
94
  label: s.nome,
51
95
  data: s.valores,
52
- backgroundColor: cor(i),
53
- borderRadius: 3
96
+ backgroundColor: uma && cores ? labels.map((_, j) => cores[j % cores.length]) : s.cor ?? cor(i),
97
+ borderRadius: 4,
98
+ maxBarThickness: 40
54
99
  }))
55
100
  },
56
101
  options: {
102
+ indexAxis: horizontal ? "y" : "x",
57
103
  maintainAspectRatio: false,
58
104
  responsive: true,
59
- plugins: { legend: legendaBaixo },
60
- scales: {
61
- x: { stacked: true, grid: { display: false } },
62
- y: { stacked: true, beginAtZero: true, grid: { color: "#EEF1F5" } }
63
- }
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 } }
64
120
  }
65
121
  }
66
122
  ) });
67
123
  }
68
- function BarrasHorizontais({ dados, altura = 280, limite = 12 }) {
69
- const labels = dados.labels.slice(0, limite);
70
- const valores = dados.valores.slice(0, limite);
71
- return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
72
- Bar,
124
+ function BarrasEmpilhadas({ serie, altura = 280, onClick }) {
125
+ return /* @__PURE__ */ jsx(Barras, { labels: serie.labels, series: serie.series, empilhado: true, altura, onClick, legenda: true });
126
+ }
127
+ function BarrasHorizontais({
128
+ dados,
129
+ altura = 280,
130
+ limite = 12,
131
+ onClick
132
+ }) {
133
+ const n = Math.min(limite, dados.labels.length);
134
+ return /* @__PURE__ */ jsx(
135
+ Barras,
73
136
  {
74
- data: {
75
- labels,
76
- datasets: [{ label: "", data: valores, backgroundColor: labels.map((_, i) => cor(i)), borderRadius: 3 }]
77
- },
78
- options: {
79
- indexAxis: "y",
80
- maintainAspectRatio: false,
81
- responsive: true,
82
- plugins: {
83
- legend: { display: false },
84
- datalabels: { display: (c) => c.dataset.data[c.dataIndex] != null, anchor: "end", align: "end", color: "#182238", font: { size: 11, weight: 600 } }
85
- },
86
- scales: {
87
- x: { beginAtZero: true, grid: { color: "#EEF1F5" } },
88
- y: { grid: { display: false } }
89
- },
90
- layout: { padding: { right: 28 } }
91
- }
137
+ horizontal: true,
138
+ labels: dados.labels,
139
+ series: [{ nome: "", valores: dados.valores }],
140
+ cores: Array.from({ length: n }, (_, i) => cor(i)),
141
+ limite,
142
+ altura,
143
+ onClick
92
144
  }
93
- ) });
145
+ );
94
146
  }
95
- function Anel({ dados, altura = 260, cores }) {
147
+ function Anel({
148
+ dados,
149
+ altura = 260,
150
+ cores,
151
+ formatar = (v) => String(v),
152
+ onClick
153
+ }) {
154
+ const tema = useTema();
155
+ const total = dados.valores.reduce((a, b) => a + (b || 0), 0);
96
156
  return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
97
157
  Doughnut,
98
158
  {
@@ -110,19 +170,36 @@ function Anel({ dados, altura = 260, cores }) {
110
170
  maintainAspectRatio: false,
111
171
  responsive: true,
112
172
  cutout: "58%",
173
+ ...clique(dados.labels, void 0, onClick),
113
174
  plugins: {
114
- legend: legendaBaixo,
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
+ },
115
184
  datalabels: {
116
- display: (c) => Number(c.dataset.data[c.dataIndex]) > 0,
117
- color: "#182238",
118
- font: { size: 11, weight: 600 }
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)
119
190
  }
120
191
  }
121
192
  }
122
193
  }
123
194
  ) });
124
195
  }
125
- function Linha({ dados, altura = 260, euros: emEuros = false }) {
196
+ function Linha({
197
+ dados,
198
+ altura = 260,
199
+ euros: emEuros = false,
200
+ onClick
201
+ }) {
202
+ const tema = useTema();
126
203
  return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
127
204
  Line,
128
205
  {
@@ -143,6 +220,7 @@ function Linha({ dados, altura = 260, euros: emEuros = false }) {
143
220
  options: {
144
221
  maintainAspectRatio: false,
145
222
  responsive: true,
223
+ ...clique(dados.labels, void 0, onClick),
146
224
  plugins: {
147
225
  legend: { display: false },
148
226
  tooltip: {
@@ -152,11 +230,11 @@ function Linha({ dados, altura = 260, euros: emEuros = false }) {
152
230
  }
153
231
  },
154
232
  scales: {
155
- x: { grid: { display: false } },
233
+ x: { grid: { display: false }, ticks: { color: tema.texto } },
156
234
  y: {
157
235
  beginAtZero: true,
158
- grid: { color: "#EEF1F5" },
159
- ticks: { callback: (v) => emEuros ? euros(Number(v)) : String(v) }
236
+ grid: { color: tema.grelha },
237
+ ticks: { color: tema.texto, callback: (v) => emEuros ? euros(Number(v)) : String(v) }
160
238
  }
161
239
  }
162
240
  }
@@ -166,18 +244,24 @@ function Linha({ dados, altura = 260, euros: emEuros = false }) {
166
244
  function Linhas({
167
245
  serie,
168
246
  altura = 280,
169
- area = false
247
+ area = false,
248
+ cores,
249
+ formatar = (v) => String(v),
250
+ formatarEtiqueta = labelBucket,
251
+ onClick
170
252
  }) {
253
+ const tema = useTema();
254
+ const corDe = (i) => cores?.[i] ?? cor(i);
171
255
  return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
172
256
  Line,
173
257
  {
174
258
  data: {
175
- labels: serie.labels.map(labelBucket),
259
+ labels: serie.labels.map(formatarEtiqueta),
176
260
  datasets: serie.series.map((s, i) => ({
177
261
  label: s.nome,
178
262
  data: s.valores,
179
- borderColor: cor(i),
180
- backgroundColor: area ? `${cor(i)}22` : cor(i),
263
+ borderColor: corDe(i),
264
+ backgroundColor: area ? `${corDe(i)}22` : corDe(i),
181
265
  fill: area,
182
266
  tension: 0.25,
183
267
  pointRadius: serie.labels.length > 24 ? 0 : 3,
@@ -188,10 +272,14 @@ function Linhas({
188
272
  maintainAspectRatio: false,
189
273
  responsive: true,
190
274
  interaction: { mode: "index", intersect: false },
191
- plugins: { legend: legendaBaixo },
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
+ },
192
280
  scales: {
193
- x: { grid: { display: false } },
194
- y: { beginAtZero: true, grid: { color: "#EEF1F5" } }
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)) } }
195
283
  }
196
284
  }
197
285
  }
@@ -205,6 +293,7 @@ function Bolhas({
205
293
  yTitle = "% overdue",
206
294
  tooltipLabel = (p) => `${p.responsavel}: ${p.otsAtivas} active, ${p.percentagemForaPrazo}% overdue, ${euros(p.valorPenalizado)}`
207
295
  }) {
296
+ const tema = useTema();
208
297
  const maxValor = Math.max(1, ...pontos.map((p) => p.valorPenalizado));
209
298
  return /* @__PURE__ */ jsx("div", { style: { height: altura }, children: /* @__PURE__ */ jsx(
210
299
  Bubble,
@@ -239,8 +328,8 @@ function Bolhas({
239
328
  }
240
329
  },
241
330
  scales: {
242
- x: { beginAtZero: true, title: { display: true, text: xTitle }, grid: { color: "#EEF1F5" } },
243
- y: { beginAtZero: true, title: { display: true, text: yTitle }, grid: { color: "#EEF1F5" } }
331
+ x: { beginAtZero: true, title: { display: true, text: xTitle, color: tema.texto }, grid: { color: tema.grelha }, ticks: { color: tema.texto } },
332
+ y: { beginAtZero: true, title: { display: true, text: yTitle, color: tema.texto }, grid: { color: tema.grelha }, ticks: { color: tema.texto } }
244
333
  }
245
334
  }
246
335
  }
@@ -248,6 +337,7 @@ function Bolhas({
248
337
  }
249
338
  export {
250
339
  Anel,
340
+ Barras,
251
341
  BarrasEmpilhadas,
252
342
  BarrasHorizontais,
253
343
  Bolhas,
@@ -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 } from 'chart.js'\r\nimport ChartDataLabels from 'chartjs-plugin-datalabels'\r\nimport { Bar, Doughnut, Line, Bubble } from 'react-chartjs-2'\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// 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\nconst legendaBaixo = {\r\n position: 'bottom' as const,\r\n labels: { boxWidth: 10, font: { size: 11 }, color: '#182238', usePointStyle: true, pointStyle: 'circle' as const },\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 }: { serie: SerieEmpilhada; altura?: number }) {\r\n return (\r\n <div style={{ height: altura }}>\r\n <Bar\r\n data={{\r\n labels: serie.labels.map(labelBucket),\r\n datasets: serie.series.map((s, i) => ({\r\n label: s.nome,\r\n data: s.valores,\r\n backgroundColor: cor(i),\r\n borderRadius: 3,\r\n })),\r\n }}\r\n options={{\r\n maintainAspectRatio: false,\r\n responsive: true,\r\n plugins: { legend: legendaBaixo },\r\n scales: {\r\n x: { stacked: true, grid: { display: false } },\r\n y: { stacked: true, beginAtZero: true, grid: { color: '#EEF1F5' } },\r\n },\r\n }}\r\n />\r\n </div>\r\n )\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({ dados, altura = 280, limite = 12 }: { dados: Distribuicao; altura?: number; limite?: number }) {\r\n const labels = dados.labels.slice(0, limite)\r\n const valores = dados.valores.slice(0, limite)\r\n return (\r\n <div style={{ height: altura }}>\r\n <Bar\r\n data={{\r\n labels,\r\n datasets: [{ label: '', data: valores, backgroundColor: labels.map((_, i) => cor(i)), borderRadius: 3 }],\r\n }}\r\n options={{\r\n indexAxis: 'y',\r\n maintainAspectRatio: false,\r\n responsive: true,\r\n plugins: {\r\n legend: { display: false },\r\n datalabels: { display: (c) => c.dataset.data[c.dataIndex] != null, anchor: 'end', align: 'end', color: '#182238', font: { size: 11, weight: 600 } },\r\n },\r\n scales: {\r\n x: { beginAtZero: true, grid: { color: '#EEF1F5' } },\r\n y: { grid: { display: false } },\r\n },\r\n layout: { padding: { right: 28 } },\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 function Anel({ dados, altura = 260, cores }: { dados: Distribuicao; altura?: number; cores?: string[] }) {\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 plugins: {\r\n legend: legendaBaixo,\r\n datalabels: {\r\n display: (c) => Number(c.dataset.data[c.dataIndex]) > 0,\r\n color: '#182238',\r\n font: { size: 11, weight: 600 },\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({ dados, altura = 260, euros: emEuros = false }: { dados: Distribuicao; altura?: number; euros?: boolean }) {\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 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 } },\r\n y: {\r\n beginAtZero: true,\r\n grid: { color: '#EEF1F5' },\r\n ticks: { 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}: {\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}) {\r\n return (\r\n <div style={{ height: altura }}>\r\n <Line\r\n data={{\r\n labels: serie.labels.map(labelBucket),\r\n datasets: serie.series.map((s, i) => ({\r\n label: s.nome,\r\n data: s.valores,\r\n borderColor: cor(i),\r\n backgroundColor: area ? `${cor(i)}22` : cor(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 plugins: { legend: legendaBaixo },\r\n scales: {\r\n x: { grid: { display: false } },\r\n y: { beginAtZero: true, grid: { color: '#EEF1F5' } },\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 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 }, grid: { color: '#EEF1F5' } },\r\n y: { beginAtZero: true, title: { display: true, text: yTitle }, grid: { color: '#EEF1F5' } },\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, 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,91 +1,30 @@
1
- import { jsxs, jsx } from "react/jsx-runtime";
2
- import { useState, useEffect } from "react";
3
- import Sidebar from "../Sidebar/Sidebar.js";
4
- const Menu = ({ size = 24, className }) => /* @__PURE__ */ jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", className, children: [
5
- /* @__PURE__ */ jsx("line", { x1: "4", x2: "20", y1: "12", y2: "12" }),
6
- /* @__PURE__ */ jsx("line", { x1: "4", x2: "20", y1: "6", y2: "6" }),
7
- /* @__PURE__ */ jsx("line", { x1: "4", x2: "20", y1: "18", y2: "18" })
8
- ] });
1
+ import { jsx } from "react/jsx-runtime";
2
+ import AppShell from "../../../../v1/components/layout/AppShell/AppShell.js";
3
+ import { labelsV1, menuV1 } from "../Sidebar/Sidebar.js";
9
4
  const Layout = ({
10
5
  children,
11
6
  menus,
12
- groupLabels,
13
- appTitle,
14
- topBar,
15
- avatar,
16
- onLogout,
17
- logoutLabel,
18
- languages,
19
7
  currentLang,
20
- onLanguageChange,
21
- theme,
22
- onThemeChange,
8
+ logoutLabel,
23
9
  themeLabels,
24
- footer,
25
- deployedAt,
26
10
  deployedAtLabel,
27
- sidebarOpen: sidebarOpenProp,
28
- onSidebarOpenChange
29
- }) => {
30
- const [sidebarOpen, setSidebarOpenState] = useState(sidebarOpenProp ?? window.innerWidth > 640);
31
- const [isMobile, setIsMobile] = useState(window.innerWidth <= 640);
32
- const setSidebarOpen = (open) => {
33
- setSidebarOpenState(open);
34
- onSidebarOpenChange?.(open);
35
- };
36
- useEffect(() => {
37
- const handleResize = () => {
38
- setIsMobile(window.innerWidth <= 640);
39
- setSidebarOpenState(window.innerWidth > 640);
40
- };
41
- window.addEventListener("resize", handleResize);
42
- return () => window.removeEventListener("resize", handleResize);
43
- }, []);
44
- const contentMarginLeft = isMobile ? 0 : sidebarOpen ? 256 : 80;
45
- return /* @__PURE__ */ jsxs("div", { className: "min-h-screen bg-slate-50/30 dark:bg-slate-950 flex", children: [
46
- /* @__PURE__ */ jsx(
47
- Sidebar,
48
- {
49
- isOpen: sidebarOpen,
50
- setIsOpen: setSidebarOpen,
51
- menus,
52
- groupLabels,
53
- appTitle,
54
- avatar,
55
- onLogout,
56
- logoutLabel,
57
- languages,
58
- currentLang,
59
- onLanguageChange,
60
- theme,
61
- onThemeChange,
62
- themeLabels,
63
- footer,
64
- deployedAt,
65
- deployedAtLabel
66
- }
67
- ),
68
- /* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0 transition-all duration-300", children: [
69
- !sidebarOpen && /* @__PURE__ */ jsx(
70
- "button",
71
- {
72
- onClick: () => setSidebarOpen(true),
73
- className: "fixed top-6 left-6 z-50 p-2.5 bg-white shadow-md border border-slate-200 rounded-lg text-slate-500 hover:text-proef-blue dark:bg-slate-900 dark:border-slate-700 dark:text-slate-400 transition-colors sm:hidden",
74
- children: /* @__PURE__ */ jsx(Menu, { size: 20 })
75
- }
76
- ),
77
- topBar && /* @__PURE__ */ jsx("div", { style: { marginLeft: contentMarginLeft }, className: "sticky top-0 z-30 transition-all duration-300", children: topBar }),
78
- /* @__PURE__ */ jsx(
79
- "main",
80
- {
81
- style: { marginLeft: contentMarginLeft },
82
- className: `pb-16 px-4 sm:px-12 transition-all duration-300 ${topBar ? "pt-6 sm:pt-6" : "pt-24 sm:pt-8"}`,
83
- children: /* @__PURE__ */ jsx("div", { className: "mx-auto animate-in fade-in slide-in-from-bottom-2 duration-700", children })
84
- }
85
- )
86
- ] })
87
- ] });
88
- };
11
+ footer,
12
+ sidebarOpen,
13
+ onSidebarOpenChange,
14
+ ...rest
15
+ }) => /* @__PURE__ */ jsx(
16
+ AppShell,
17
+ {
18
+ ...rest,
19
+ menu: menuV1(menus),
20
+ currentLanguage: currentLang,
21
+ labels: labelsV1({ logoutLabel, themeLabels, deployedAtLabel }),
22
+ sidebarFooter: footer,
23
+ defaultOpen: sidebarOpen,
24
+ onOpenChange: onSidebarOpenChange,
25
+ children
26
+ }
27
+ );
89
28
  export {
90
29
  Layout as default
91
30
  };
@@ -1 +1 @@
1
- {"version":3,"file":"Layout.js","sources":["../../../../../../src/legacy/components/layout/Layout/Layout.tsx"],"sourcesContent":["import React, { useState, useEffect } from 'react'\r\nimport Sidebar, { SidebarMenuItem, SidebarLanguage, SidebarTheme } from '../Sidebar/Sidebar'\r\n\r\n// ── Inline icon (no lucide-react dependency in common-ui) ─────────────────\r\ntype IconProps = { size?: number; className?: string }\r\n\r\nconst Menu = ({ size = 24, className }: IconProps) => (\r\n <svg xmlns=\"http://www.w3.org/2000/svg\" width={size} height={size} viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" className={className}>\r\n <line x1=\"4\" x2=\"20\" y1=\"12\" y2=\"12\" />\r\n <line x1=\"4\" x2=\"20\" y1=\"6\" y2=\"6\" />\r\n <line x1=\"4\" x2=\"20\" y1=\"18\" y2=\"18\" />\r\n </svg>\r\n)\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface LayoutProps {\r\n children: React.ReactNode\r\n menus: SidebarMenuItem[]\r\n groupLabels?: Record<string, string>\r\n appTitle?: string\r\n /** Barra superior opcional (título/alertas). Fica sticky acima do conteúdo,\r\n * alinhada à direita da sidebar. Traz o seu próprio fundo/padding. */\r\n topBar?: React.ReactNode\r\n avatar?: React.ReactNode\r\n onLogout?: () => void\r\n logoutLabel?: string\r\n languages?: SidebarLanguage[]\r\n currentLang?: string\r\n onLanguageChange?: (code: string) => void\r\n theme?: SidebarTheme\r\n onThemeChange?: (theme: SidebarTheme) => void\r\n themeLabels?: { label?: string; light?: string; dark?: string; system?: string }\r\n /** Slot extra no rodapé da sidebar (ex: filtro global). Ver Sidebar.footer. */\r\n footer?: (isExpanded: boolean) => React.ReactNode\r\n /** Quando este build foi feito, por baixo do logout. Ver Sidebar.deployedAt. */\r\n deployedAt?: string | number | Date\r\n deployedAtLabel?: string\r\n /**\r\n * Estado inicial da sidebar (aberta/fechada). Sem isto mantém-se o comportamento de\r\n * sempre: aberta no desktop, fechada em mobile. Passar isto serve para a app repor o\r\n * que a pessoa tinha antes de recarregar a página (ex.: lido do seu próprio\r\n * localStorage) — o Layout não persiste nada sozinho, tal como já acontece com `theme`.\r\n */\r\n sidebarOpen?: boolean\r\n /**\r\n * Chamado sempre que a sidebar abre/fecha por uma ação da pessoa (o atalho da própria\r\n * Sidebar, ou o botão de expandir em mobile) — nunca pelo ajuste automático ao\r\n * redimensionar a janela, que não é uma escolha dela. Mesmo padrão de\r\n * `onThemeChange`: o Layout só avisa, quem usa decide se e como guardar.\r\n */\r\n onSidebarOpenChange?: (open: boolean) => void\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nconst Layout = ({\r\n children,\r\n menus,\r\n groupLabels,\r\n appTitle,\r\n topBar,\r\n avatar,\r\n onLogout,\r\n logoutLabel,\r\n languages,\r\n currentLang,\r\n onLanguageChange,\r\n theme,\r\n onThemeChange,\r\n themeLabels,\r\n footer,\r\n deployedAt,\r\n deployedAtLabel,\r\n sidebarOpen: sidebarOpenProp,\r\n onSidebarOpenChange,\r\n}: LayoutProps) => {\r\n // Expandida por defeito no desktop; colapsa em mobile (< 640) — a não ser que a app\r\n // passe `sidebarOpen` (ex.: o que tinha guardado antes de recarregar a página).\r\n const [sidebarOpen, setSidebarOpenState] = useState(sidebarOpenProp ?? window.innerWidth > 640)\r\n const [isMobile, setIsMobile] = useState(window.innerWidth <= 640)\r\n\r\n // Toggle deliberado (Sidebar passa isto como o seu `setIsOpen`, e o botão de expandir\r\n // em mobile também) — avisa a app para poder persistir a escolha.\r\n const setSidebarOpen = (open: boolean) => {\r\n setSidebarOpenState(open)\r\n onSidebarOpenChange?.(open)\r\n }\r\n\r\n useEffect(() => {\r\n const handleResize = () => {\r\n setIsMobile(window.innerWidth <= 640)\r\n // Direto ao estado, não ao wrapper: isto é o viewport a mudar, não a pessoa a\r\n // escolher — não faz sentido a app gravar isto como preferência.\r\n setSidebarOpenState(window.innerWidth > 640)\r\n }\r\n window.addEventListener('resize', handleResize)\r\n return () => window.removeEventListener('resize', handleResize)\r\n }, [])\r\n\r\n // Margem do conteúdo = largura da sidebar fixa (px, inline). NÃO usar classes\r\n // sm:ml-* aqui: o Tailwind do consumidor scaneia o bundle minificado do\r\n // common-ui e nem sempre gera as responsive utilities → conteúdo ficava\r\n // por baixo do rail. Inline style é imune a isso.\r\n // Mobile (< 640): sidebar esconde (translate) → sem margem.\r\n const contentMarginLeft = isMobile ? 0 : sidebarOpen ? 256 : 80\r\n\r\n return (\r\n <div className=\"min-h-screen bg-slate-50/30 dark:bg-slate-950 flex\">\r\n <Sidebar\r\n isOpen={sidebarOpen}\r\n setIsOpen={setSidebarOpen}\r\n menus={menus}\r\n groupLabels={groupLabels}\r\n appTitle={appTitle}\r\n avatar={avatar}\r\n onLogout={onLogout}\r\n logoutLabel={logoutLabel}\r\n languages={languages}\r\n currentLang={currentLang}\r\n onLanguageChange={onLanguageChange}\r\n theme={theme}\r\n onThemeChange={onThemeChange}\r\n themeLabels={themeLabels}\r\n footer={footer}\r\n deployedAt={deployedAt}\r\n deployedAtLabel={deployedAtLabel}\r\n />\r\n <div className=\"flex-1 flex flex-col min-w-0 transition-all duration-300\">\r\n {!sidebarOpen && (\r\n <button\r\n onClick={() => setSidebarOpen(true)}\r\n className=\"fixed top-6 left-6 z-50 p-2.5 bg-white shadow-md border border-slate-200 rounded-lg text-slate-500 hover:text-proef-blue dark:bg-slate-900 dark:border-slate-700 dark:text-slate-400 transition-colors sm:hidden\"\r\n >\r\n <Menu size={20} />\r\n </button>\r\n )}\r\n {topBar && (\r\n <div style={{ marginLeft: contentMarginLeft }} className=\"sticky top-0 z-30 transition-all duration-300\">\r\n {topBar}\r\n </div>\r\n )}\r\n <main\r\n style={{ marginLeft: contentMarginLeft }}\r\n className={`pb-16 px-4 sm:px-12 transition-all duration-300 ${topBar ? 'pt-6 sm:pt-6' : 'pt-24 sm:pt-8'}`}\r\n >\r\n <div className=\"mx-auto animate-in fade-in slide-in-from-bottom-2 duration-700\">\r\n {children}\r\n </div>\r\n </main>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport default Layout\r\n"],"names":[],"mappings":";;;AAMA,MAAM,OAAO,CAAC,EAAE,OAAO,IAAI,UAAA,MACvB,qBAAC,OAAA,EAAI,OAAM,8BAA6B,OAAO,MAAM,QAAQ,MAAM,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,WAClL,UAAA;AAAA,EAAA,oBAAC,QAAA,EAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,KAAA,CAAK;AAAA,EACrC,oBAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,KAAI,IAAG,IAAA,CAAI;AAAA,EACnC,oBAAC,UAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAK,IAAG,KAAA,CAAK;AAAA,GACzC;AA2CJ,MAAM,SAAS,CAAC;AAAA,EACZ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,aAAa;AAAA,EACb;AACJ,MAAmB;AAGf,QAAM,CAAC,aAAa,mBAAmB,IAAI,SAAS,mBAAmB,OAAO,aAAa,GAAG;AAC9F,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,OAAO,cAAc,GAAG;AAIjE,QAAM,iBAAiB,CAAC,SAAkB;AACtC,wBAAoB,IAAI;AACxB,0BAAsB,IAAI;AAAA,EAC9B;AAEA,YAAU,MAAM;AACZ,UAAM,eAAe,MAAM;AACvB,kBAAY,OAAO,cAAc,GAAG;AAGpC,0BAAoB,OAAO,aAAa,GAAG;AAAA,IAC/C;AACA,WAAO,iBAAiB,UAAU,YAAY;AAC9C,WAAO,MAAM,OAAO,oBAAoB,UAAU,YAAY;AAAA,EAClE,GAAG,CAAA,CAAE;AAOL,QAAM,oBAAoB,WAAW,IAAI,cAAc,MAAM;AAE7D,SACI,qBAAC,OAAA,EAAI,WAAU,sDACX,UAAA;AAAA,IAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACG,QAAQ;AAAA,QACR,WAAW;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,IAAA;AAAA,IAEJ,qBAAC,OAAA,EAAI,WAAU,4DACV,UAAA;AAAA,MAAA,CAAC,eACE;AAAA,QAAC;AAAA,QAAA;AAAA,UACG,SAAS,MAAM,eAAe,IAAI;AAAA,UAClC,WAAU;AAAA,UAEV,UAAA,oBAAC,MAAA,EAAK,MAAM,GAAA,CAAI;AAAA,QAAA;AAAA,MAAA;AAAA,MAGvB,UACG,oBAAC,OAAA,EAAI,OAAO,EAAE,YAAY,kBAAA,GAAqB,WAAU,iDACpD,UAAA,OAAA,CACL;AAAA,MAEJ;AAAA,QAAC;AAAA,QAAA;AAAA,UACG,OAAO,EAAE,YAAY,kBAAA;AAAA,UACrB,WAAW,mDAAmD,SAAS,iBAAiB,eAAe;AAAA,UAEvG,UAAA,oBAAC,OAAA,EAAI,WAAU,kEACV,SAAA,CACL;AAAA,QAAA;AAAA,MAAA;AAAA,IACJ,EAAA,CACJ;AAAA,EAAA,GACJ;AAER;"}
1
+ {"version":3,"file":"Layout.js","sources":["../../../../../../src/legacy/components/layout/Layout/Layout.tsx"],"sourcesContent":["import React from 'react'\r\nimport AppShell from '../../../../v1/components/layout/AppShell/AppShell'\r\nimport { labelsV1, menuV1, type SidebarMenuItem, type SidebarLanguage, type SidebarTheme } from '../Sidebar/Sidebar'\r\n\r\n/*\r\n * Migrado para o V1: a moldura da app é o `AppShell` do V1. Este ficheiro só\r\n * mantém a API antiga, e os seus textos por omissão, para as apps que ainda a\r\n * usam.\r\n */\r\n\r\n/** @deprecated Legacy — usar `AppShellProps` do V1. */\r\nexport interface LayoutProps {\r\n children: React.ReactNode\r\n menus: SidebarMenuItem[]\r\n groupLabels?: Record<string, string>\r\n appTitle?: string\r\n /** Barra superior opcional (título/alertas), sticky acima do conteúdo. */\r\n topBar?: React.ReactNode\r\n avatar?: React.ReactNode\r\n onLogout?: () => void\r\n logoutLabel?: string\r\n languages?: SidebarLanguage[]\r\n currentLang?: string\r\n onLanguageChange?: (code: string) => void\r\n theme?: SidebarTheme\r\n onThemeChange?: (theme: SidebarTheme) => void\r\n themeLabels?: { label?: string; light?: string; dark?: string; system?: string }\r\n footer?: (isExpanded: boolean) => React.ReactNode\r\n deployedAt?: string | number | Date\r\n deployedAtLabel?: string\r\n /** Estado inicial da sidebar (aberta/fechada). */\r\n sidebarOpen?: boolean\r\n /** Chamado quando a pessoa abre/fecha a sidebar. */\r\n onSidebarOpenChange?: (open: boolean) => void\r\n}\r\n\r\n/** @deprecated Legacy — usar `AppShell` do V1. */\r\nconst Layout = ({\r\n children,\r\n menus,\r\n currentLang,\r\n logoutLabel,\r\n themeLabels,\r\n deployedAtLabel,\r\n footer,\r\n sidebarOpen,\r\n onSidebarOpenChange,\r\n ...rest\r\n}: LayoutProps) => (\r\n <AppShell\r\n {...rest}\r\n menu={menuV1(menus)}\r\n currentLanguage={currentLang}\r\n labels={labelsV1({ logoutLabel, themeLabels, deployedAtLabel })}\r\n sidebarFooter={footer}\r\n defaultOpen={sidebarOpen}\r\n onOpenChange={onSidebarOpenChange}\r\n >\r\n {children}\r\n </AppShell>\r\n)\r\n\r\nexport default Layout\r\n"],"names":[],"mappings":";;;AAqCA,MAAM,SAAS,CAAC;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC;AAAA,EAAA;AAAA,IACE,GAAG;AAAA,IACJ,MAAM,OAAO,KAAK;AAAA,IAClB,iBAAiB;AAAA,IACjB,QAAQ,SAAS,EAAE,aAAa,aAAa,iBAAiB;AAAA,IAC9D,eAAe;AAAA,IACf,aAAa;AAAA,IACb,cAAc;AAAA,IAEb;AAAA,EAAA;AACH;"}