@nextelco/common-ui 2.1.97 → 2.1.99

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 (356) hide show
  1. package/dist/esm/index.js +222 -0
  2. package/dist/esm/index.js.map +1 -0
  3. package/dist/esm/legacy/assets/svgs/proef-footer-logo.svg.js +5 -0
  4. package/dist/esm/legacy/assets/svgs/proef-footer-logo.svg.js.map +1 -0
  5. package/dist/esm/legacy/components/atoms/Acronym/Acronym.css +43 -0
  6. package/dist/esm/legacy/components/atoms/Acronym/Acronym.js +31 -0
  7. package/dist/esm/legacy/components/atoms/Acronym/Acronym.js.map +1 -0
  8. package/dist/esm/legacy/components/atoms/BreadCrumb/BreadCrumb.css +36 -0
  9. package/dist/esm/legacy/components/atoms/BreadCrumb/BreadCrumb.js +57 -0
  10. package/dist/esm/legacy/components/atoms/BreadCrumb/BreadCrumb.js.map +1 -0
  11. package/dist/esm/legacy/components/atoms/Button/variants/ActionButton.css +218 -0
  12. package/dist/esm/legacy/components/atoms/Button/variants/ActionButton.js +50 -0
  13. package/dist/esm/legacy/components/atoms/Button/variants/ActionButton.js.map +1 -0
  14. package/dist/esm/legacy/components/atoms/Button/variants/FilterButton.css +56 -0
  15. package/dist/esm/legacy/components/atoms/Button/variants/FilterButton.js +34 -0
  16. package/dist/esm/legacy/components/atoms/Button/variants/FilterButton.js.map +1 -0
  17. package/dist/esm/legacy/components/atoms/Button/variants/IconButton.css +74 -0
  18. package/dist/esm/legacy/components/atoms/Button/variants/IconButton.js +29 -0
  19. package/dist/esm/legacy/components/atoms/Button/variants/IconButton.js.map +1 -0
  20. package/dist/esm/legacy/components/atoms/Button/variants/MainButton.css +99 -0
  21. package/dist/esm/legacy/components/atoms/Button/variants/MainButton.js +36 -0
  22. package/dist/esm/legacy/components/atoms/Button/variants/MainButton.js.map +1 -0
  23. package/dist/esm/legacy/components/atoms/Button/variants/TabButton.css +63 -0
  24. package/dist/esm/legacy/components/atoms/Button/variants/TabButton.js +21 -0
  25. package/dist/esm/legacy/components/atoms/Button/variants/TabButton.js.map +1 -0
  26. package/dist/esm/legacy/components/atoms/Field/Field.css +171 -0
  27. package/dist/esm/legacy/components/atoms/Field/Field.js +70 -0
  28. package/dist/esm/legacy/components/atoms/Field/Field.js.map +1 -0
  29. package/dist/esm/legacy/components/atoms/IconText/IconText.css +10 -0
  30. package/dist/esm/legacy/components/atoms/IconText/IconText.js +25 -0
  31. package/dist/esm/legacy/components/atoms/IconText/IconText.js.map +1 -0
  32. package/dist/esm/legacy/components/atoms/Icone/Icone.js +24 -0
  33. package/dist/esm/legacy/components/atoms/Icone/Icone.js.map +1 -0
  34. package/dist/esm/legacy/components/atoms/Input/Input.css +12 -0
  35. package/dist/esm/legacy/components/atoms/Input/Input.js +28 -0
  36. package/dist/esm/legacy/components/atoms/Input/Input.js.map +1 -0
  37. package/dist/esm/legacy/components/atoms/InputData/InputData.js +36 -0
  38. package/dist/esm/legacy/components/atoms/InputData/InputData.js.map +1 -0
  39. package/dist/esm/legacy/components/atoms/Label/Label.css +192 -0
  40. package/dist/esm/legacy/components/atoms/Label/Label.js +13 -0
  41. package/dist/esm/legacy/components/atoms/Label/Label.js.map +1 -0
  42. package/dist/esm/legacy/components/atoms/MultiTagPickerTag/MultiTagPickerTag.css +40 -0
  43. package/dist/esm/legacy/components/atoms/MultiTagPickerTag/MultiTagPickerTag.js +21 -0
  44. package/dist/esm/legacy/components/atoms/MultiTagPickerTag/MultiTagPickerTag.js.map +1 -0
  45. package/dist/esm/legacy/components/atoms/Select/Select.css +16 -0
  46. package/dist/esm/legacy/components/atoms/Select/Select.js +27 -0
  47. package/dist/esm/legacy/components/atoms/Select/Select.js.map +1 -0
  48. package/dist/esm/legacy/components/atoms/Stack/Stack.js +20 -0
  49. package/dist/esm/legacy/components/atoms/Stack/Stack.js.map +1 -0
  50. package/dist/esm/legacy/components/atoms/Switch/CustomSwitch.css +36 -0
  51. package/dist/esm/legacy/components/atoms/Switch/CustomSwitch.js +29 -0
  52. package/dist/esm/legacy/components/atoms/Switch/CustomSwitch.js.map +1 -0
  53. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.css +17 -0
  54. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.js +41 -0
  55. package/dist/esm/legacy/components/atoms/Tooltip/Tooltip.js.map +1 -0
  56. package/dist/esm/legacy/components/charts/Charts.js +259 -0
  57. package/dist/esm/legacy/components/charts/Charts.js.map +1 -0
  58. package/dist/esm/legacy/components/layout/AppLauncher/AppLauncher.js +100 -0
  59. package/dist/esm/legacy/components/layout/AppLauncher/AppLauncher.js.map +1 -0
  60. package/dist/esm/legacy/components/layout/Error/Error.css +20 -0
  61. package/dist/esm/legacy/components/layout/Error/Error.js +12 -0
  62. package/dist/esm/legacy/components/layout/Error/Error.js.map +1 -0
  63. package/dist/esm/legacy/components/layout/Footer/Footer.css +42 -0
  64. package/dist/esm/legacy/components/layout/Footer/Footer.js +23 -0
  65. package/dist/esm/legacy/components/layout/Footer/Footer.js.map +1 -0
  66. package/dist/esm/legacy/components/layout/Header/Header.css +66 -0
  67. package/dist/esm/legacy/components/layout/Header/Header.js +37 -0
  68. package/dist/esm/legacy/components/layout/Header/Header.js.map +1 -0
  69. package/dist/esm/legacy/components/layout/Layout/Layout.js +92 -0
  70. package/dist/esm/legacy/components/layout/Layout/Layout.js.map +1 -0
  71. package/dist/esm/legacy/components/layout/Sidebar/Sidebar.js +227 -0
  72. package/dist/esm/legacy/components/layout/Sidebar/Sidebar.js.map +1 -0
  73. package/dist/esm/legacy/components/layout/Spinner/Spinner.css +10 -0
  74. package/dist/esm/legacy/components/layout/Spinner/Spinner.js +19 -0
  75. package/dist/esm/legacy/components/layout/Spinner/Spinner.js.map +1 -0
  76. package/dist/esm/legacy/components/layout/TopBar/TopBar.js +34 -0
  77. package/dist/esm/legacy/components/layout/TopBar/TopBar.js.map +1 -0
  78. package/dist/esm/legacy/components/layout/TopNavLayout/TopNavLayout.js +82 -0
  79. package/dist/esm/legacy/components/layout/TopNavLayout/TopNavLayout.js.map +1 -0
  80. package/dist/esm/legacy/components/molecules/BarraValor/BarraValor.js +25 -0
  81. package/dist/esm/legacy/components/molecules/BarraValor/BarraValor.js.map +1 -0
  82. package/dist/esm/legacy/components/molecules/ColumnPicker/ColumnPicker.js +179 -0
  83. package/dist/esm/legacy/components/molecules/ColumnPicker/ColumnPicker.js.map +1 -0
  84. package/dist/esm/legacy/components/molecules/ConfirmModal/ConfirmModal.css +3 -0
  85. package/dist/esm/legacy/components/molecules/ConfirmModal/ConfirmModal.js +33 -0
  86. package/dist/esm/legacy/components/molecules/ConfirmModal/ConfirmModal.js.map +1 -0
  87. package/dist/esm/legacy/components/molecules/DataTable/DataTable.js +1145 -0
  88. package/dist/esm/legacy/components/molecules/DataTable/DataTable.js.map +1 -0
  89. package/dist/esm/legacy/components/molecules/FileDropzone/FileDropzone.js +68 -0
  90. package/dist/esm/legacy/components/molecules/FileDropzone/FileDropzone.js.map +1 -0
  91. package/dist/esm/legacy/components/molecules/LanguageSwitcher/LanguageSwitcher.js +96 -0
  92. package/dist/esm/legacy/components/molecules/LanguageSwitcher/LanguageSwitcher.js.map +1 -0
  93. package/dist/esm/legacy/components/molecules/Modal/Modal.css +54 -0
  94. package/dist/esm/legacy/components/molecules/Modal/Modal.js +64 -0
  95. package/dist/esm/legacy/components/molecules/Modal/Modal.js.map +1 -0
  96. package/dist/esm/legacy/components/molecules/MultiTagPicker/MultiTagPicker.css +97 -0
  97. package/dist/esm/legacy/components/molecules/MultiTagPicker/MultiTagPicker.js +113 -0
  98. package/dist/esm/legacy/components/molecules/MultiTagPicker/MultiTagPicker.js.map +1 -0
  99. package/dist/esm/legacy/components/molecules/PhotoAvatar/PhotoAvatar.js +107 -0
  100. package/dist/esm/legacy/components/molecules/PhotoAvatar/PhotoAvatar.js.map +1 -0
  101. package/dist/esm/legacy/components/molecules/Search/Search.css +18 -0
  102. package/dist/esm/legacy/components/molecules/Search/Search.js +30 -0
  103. package/dist/esm/legacy/components/molecules/Search/Search.js.map +1 -0
  104. package/dist/esm/legacy/components/molecules/SeletorPesquisa/SeletorPesquisa.js +116 -0
  105. package/dist/esm/legacy/components/molecules/SeletorPesquisa/SeletorPesquisa.js.map +1 -0
  106. package/dist/esm/legacy/components/molecules/Sidebar/Sidebar.css +69 -0
  107. package/dist/esm/legacy/components/molecules/Sidebar/Sidebar.js +62 -0
  108. package/dist/esm/legacy/components/molecules/Sidebar/Sidebar.js.map +1 -0
  109. package/dist/esm/legacy/components/molecules/UserAvatar/UserAvatar.js +105 -0
  110. package/dist/esm/legacy/components/molecules/UserAvatar/UserAvatar.js.map +1 -0
  111. package/dist/esm/legacy/components/organisms/GanttChart/GanttChart.js +162 -0
  112. package/dist/esm/legacy/components/organisms/GanttChart/GanttChart.js.map +1 -0
  113. package/dist/esm/legacy/components/organisms/Kanban/Kanban.js +88 -0
  114. package/dist/esm/legacy/components/organisms/Kanban/Kanban.js.map +1 -0
  115. package/dist/esm/legacy/components/organisms/LazyLoader/LazyLoader.js +87 -0
  116. package/dist/esm/legacy/components/organisms/LazyLoader/LazyLoader.js.map +1 -0
  117. package/dist/esm/legacy/components/organisms/MapaPontos/MapaPontos.js +427 -0
  118. package/dist/esm/legacy/components/organisms/MapaPontos/MapaPontos.js.map +1 -0
  119. package/dist/esm/legacy/components/organisms/PainelFiltros/PainelFiltros.js +206 -0
  120. package/dist/esm/legacy/components/organisms/PainelFiltros/PainelFiltros.js.map +1 -0
  121. package/dist/esm/legacy/components/organisms/PesquisaRapida/PesquisaRapida.js +147 -0
  122. package/dist/esm/legacy/components/organisms/PesquisaRapida/PesquisaRapida.js.map +1 -0
  123. package/dist/esm/legacy/components/organisms/Profile/Profile.css +26 -0
  124. package/dist/esm/legacy/components/organisms/Profile/Profile.js +26 -0
  125. package/dist/esm/legacy/components/organisms/Profile/Profile.js.map +1 -0
  126. package/dist/esm/legacy/components/organisms/RoleGuard/RoleGuard.js +68 -0
  127. package/dist/esm/legacy/components/organisms/RoleGuard/RoleGuard.js.map +1 -0
  128. package/dist/esm/legacy/components/organisms/Selector/Selector.css +87 -0
  129. package/dist/esm/legacy/components/organisms/Selector/Selector.js +181 -0
  130. package/dist/esm/legacy/components/organisms/Selector/Selector.js.map +1 -0
  131. package/dist/esm/legacy/components/organisms/StatusMatrix/StatusMatrix.js +115 -0
  132. package/dist/esm/legacy/components/organisms/StatusMatrix/StatusMatrix.js.map +1 -0
  133. package/dist/esm/legacy/components/ui/Avisos.js +86 -0
  134. package/dist/esm/legacy/components/ui/Avisos.js.map +1 -0
  135. package/dist/esm/legacy/components/ui/BarraAcoes.js +19 -0
  136. package/dist/esm/legacy/components/ui/BarraAcoes.js.map +1 -0
  137. package/dist/esm/legacy/components/ui/CabecalhoPagina.js +32 -0
  138. package/dist/esm/legacy/components/ui/CabecalhoPagina.js.map +1 -0
  139. package/dist/esm/legacy/components/ui/CaixaPesquisa.js +57 -0
  140. package/dist/esm/legacy/components/ui/CaixaPesquisa.js.map +1 -0
  141. package/dist/esm/legacy/components/ui/Confirmacao.js +52 -0
  142. package/dist/esm/legacy/components/ui/Confirmacao.js.map +1 -0
  143. package/dist/esm/legacy/components/ui/Escolha.js +157 -0
  144. package/dist/esm/legacy/components/ui/Escolha.js.map +1 -0
  145. package/dist/esm/legacy/components/ui/FaixaAviso.js +17 -0
  146. package/dist/esm/legacy/components/ui/FaixaAviso.js.map +1 -0
  147. package/dist/esm/legacy/components/ui/MenuAcoes.js +116 -0
  148. package/dist/esm/legacy/components/ui/MenuAcoes.js.map +1 -0
  149. package/dist/esm/legacy/components/ui/Passos.js +32 -0
  150. package/dist/esm/legacy/components/ui/Passos.js.map +1 -0
  151. package/dist/esm/legacy/components/ui/Separadores.js +52 -0
  152. package/dist/esm/legacy/components/ui/Separadores.js.map +1 -0
  153. package/dist/esm/legacy/components/ui/TextoAuto.js +33 -0
  154. package/dist/esm/legacy/components/ui/TextoAuto.js.map +1 -0
  155. package/dist/esm/legacy/components/ui/Ui.js +360 -0
  156. package/dist/esm/legacy/components/ui/Ui.js.map +1 -0
  157. package/dist/esm/legacy/components/ui/painelFlutuante.js +52 -0
  158. package/dist/esm/legacy/components/ui/painelFlutuante.js.map +1 -0
  159. package/dist/esm/legacy/constants/Colors.js +33 -0
  160. package/dist/esm/legacy/constants/Colors.js.map +1 -0
  161. package/dist/esm/legacy/constants/FontWeight.js +35 -0
  162. package/dist/esm/legacy/constants/FontWeight.js.map +1 -0
  163. package/dist/esm/legacy/contexts/apiContext.js +25 -0
  164. package/dist/esm/legacy/contexts/apiContext.js.map +1 -0
  165. package/dist/esm/legacy/contexts/authContext.js +85 -0
  166. package/dist/esm/legacy/contexts/authContext.js.map +1 -0
  167. package/dist/esm/legacy/contexts/socketContext.js +34 -0
  168. package/dist/esm/legacy/contexts/socketContext.js.map +1 -0
  169. package/dist/esm/legacy/hooks/useApi.js +62 -0
  170. package/dist/esm/legacy/hooks/useApi.js.map +1 -0
  171. package/dist/esm/legacy/hooks/useTheme.js +39 -0
  172. package/dist/esm/legacy/hooks/useTheme.js.map +1 -0
  173. package/dist/esm/legacy/pages/Error/ErrorPage.css +18 -0
  174. package/dist/esm/legacy/pages/Error/ErrorPage.js +50 -0
  175. package/dist/esm/legacy/pages/Error/ErrorPage.js.map +1 -0
  176. package/dist/esm/legacy/pages/Loading/Loading.css +46 -0
  177. package/dist/esm/legacy/pages/Loading/Loading.js +9 -0
  178. package/dist/esm/legacy/pages/Loading/Loading.js.map +1 -0
  179. package/dist/esm/legacy/pages/Login/LoginScreen.js +46 -0
  180. package/dist/esm/legacy/pages/Login/LoginScreen.js.map +1 -0
  181. package/dist/esm/legacy/pages/NotFound/NotFound.css +21 -0
  182. package/dist/esm/legacy/pages/NotFound/NotFound.js +24 -0
  183. package/dist/esm/legacy/pages/NotFound/NotFound.js.map +1 -0
  184. package/dist/esm/legacy/styles/themes/ButtonThemes.js +28 -0
  185. package/dist/esm/legacy/styles/themes/ButtonThemes.js.map +1 -0
  186. package/dist/esm/legacy/styles/utils/utils.js +7 -0
  187. package/dist/esm/legacy/styles/utils/utils.js.map +1 -0
  188. package/dist/esm/legacy/utils/stringUtils.js +35 -0
  189. package/dist/esm/legacy/utils/stringUtils.js.map +1 -0
  190. package/dist/esm/v1/capabilities/documents/QRScanning/scanQRCodes.js +157 -0
  191. package/dist/esm/v1/capabilities/documents/QRScanning/scanQRCodes.js.map +1 -0
  192. package/dist/esm/v1/components/documents/DocumentDropzone/DocumentDropzone.js +307 -0
  193. package/dist/esm/v1/components/documents/DocumentDropzone/DocumentDropzone.js.map +1 -0
  194. package/dist/esm/v1/components/forms/FormField/FormField.js +59 -0
  195. package/dist/esm/v1/components/forms/FormField/FormField.js.map +1 -0
  196. package/dist/esm/v1/components/forms/FormGrid/FormGrid.js +20 -0
  197. package/dist/esm/v1/components/forms/FormGrid/FormGrid.js.map +1 -0
  198. package/dist/esm/v1/components/overlays/Dialog/Dialog.js +87 -0
  199. package/dist/esm/v1/components/overlays/Dialog/Dialog.js.map +1 -0
  200. package/dist/esm/v1/foundations/icons/icons.js +76 -0
  201. package/dist/esm/v1/foundations/icons/icons.js.map +1 -0
  202. package/dist/esm/v1/primitives/buttons/Button/Button.js +27 -0
  203. package/dist/esm/v1/primitives/buttons/Button/Button.js.map +1 -0
  204. package/dist/esm/v1/primitives/documents/PdfPreview/PdfPreview.js +137 -0
  205. package/dist/esm/v1/primitives/documents/PdfPreview/PdfPreview.js.map +1 -0
  206. package/dist/esm/v1/primitives/documents/PdfPreview/pdfjs.js +26 -0
  207. package/dist/esm/v1/primitives/documents/PdfPreview/pdfjs.js.map +1 -0
  208. package/dist/esm/v1/primitives/forms/LookupInput/LookupInput.js +37 -0
  209. package/dist/esm/v1/primitives/forms/LookupInput/LookupInput.js.map +1 -0
  210. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js +29 -0
  211. package/dist/esm/v1/primitives/forms/SelectInput/SelectInput.js.map +1 -0
  212. package/dist/esm/v1/primitives/forms/TextArea/TextArea.js +75 -0
  213. package/dist/esm/v1/primitives/forms/TextArea/TextArea.js.map +1 -0
  214. package/dist/esm/v1/primitives/forms/TextInput/TextInput.js +21 -0
  215. package/dist/esm/v1/primitives/forms/TextInput/TextInput.js.map +1 -0
  216. package/dist/esm/v1/primitives/forms/field.js +12 -0
  217. package/dist/esm/v1/primitives/forms/field.js.map +1 -0
  218. package/dist/style.css +1 -1
  219. package/dist/style.layered.css +1 -1
  220. package/dist/types/index.d.ts +14 -13
  221. package/dist/types/{components → legacy/components}/atoms/Acronym/Acronym.d.ts +1 -0
  222. package/dist/types/{components → legacy/components}/atoms/BreadCrumb/BreadCrumb.d.ts +1 -0
  223. package/dist/types/{components → legacy/components}/atoms/Button/variants/ActionButton.d.ts +1 -0
  224. package/dist/types/{components → legacy/components}/atoms/Button/variants/FilterButton.d.ts +1 -0
  225. package/dist/types/{components → legacy/components}/atoms/Button/variants/IconButton.d.ts +1 -0
  226. package/dist/types/{components → legacy/components}/atoms/Button/variants/MainButton.d.ts +1 -0
  227. package/dist/types/{components → legacy/components}/atoms/Button/variants/TabButton.d.ts +1 -0
  228. package/dist/types/{components → legacy/components}/atoms/Field/Field.d.ts +1 -0
  229. package/dist/types/{components → legacy/components}/atoms/IconText/IconText.d.ts +1 -0
  230. package/dist/types/{components → legacy/components}/atoms/Icone/Icone.d.ts +2 -0
  231. package/dist/types/{components → legacy/components}/atoms/Input/Input.d.ts +1 -0
  232. package/dist/types/{components → legacy/components}/atoms/InputData/InputData.d.ts +4 -0
  233. package/dist/types/{components → legacy/components}/atoms/Label/Label.d.ts +1 -0
  234. package/dist/types/{components → legacy/components}/atoms/Select/Select.d.ts +1 -0
  235. package/dist/types/{components → legacy/components}/atoms/Stack/Stack.d.ts +1 -0
  236. package/dist/types/{components → legacy/components}/atoms/Switch/CustomSwitch.d.ts +1 -0
  237. package/dist/types/{components → legacy/components}/atoms/Tooltip/Tooltip.d.ts +1 -0
  238. package/dist/types/{components → legacy/components}/charts/Charts.d.ts +29 -6
  239. package/dist/types/{components → legacy/components}/layout/AppLauncher/AppLauncher.d.ts +3 -0
  240. package/dist/types/{components → legacy/components}/layout/Error/Error.d.ts +1 -0
  241. package/dist/types/{components → legacy/components}/layout/Footer/Footer.d.ts +1 -0
  242. package/dist/types/{components → legacy/components}/layout/Header/Header.d.ts +1 -0
  243. package/dist/types/{components → legacy/components}/layout/Layout/Layout.d.ts +2 -0
  244. package/dist/types/{components → legacy/components}/layout/Sidebar/Sidebar.d.ts +5 -0
  245. package/dist/types/{components → legacy/components}/layout/Spinner/Spinner.d.ts +1 -0
  246. package/dist/types/{components → legacy/components}/layout/TopBar/TopBar.d.ts +2 -0
  247. package/dist/types/{components → legacy/components}/layout/TopNavLayout/TopNavLayout.d.ts +3 -0
  248. package/dist/types/{components → legacy/components}/molecules/BarraValor/BarraValor.d.ts +3 -0
  249. package/dist/types/{components → legacy/components}/molecules/ColumnPicker/ColumnPicker.d.ts +8 -1
  250. package/dist/types/{components → legacy/components}/molecules/ConfirmModal/ConfirmModal.d.ts +2 -0
  251. package/dist/types/{components → legacy/components}/molecules/DataTable/DataTable.d.ts +6 -0
  252. package/dist/types/{components → legacy/components}/molecules/FileDropzone/FileDropzone.d.ts +3 -0
  253. package/dist/types/{components → legacy/components}/molecules/LanguageSwitcher/LanguageSwitcher.d.ts +3 -0
  254. package/dist/types/{components → legacy/components}/molecules/Modal/Modal.d.ts +2 -0
  255. package/dist/types/{components → legacy/components}/molecules/MultiTagPicker/MultiTagPicker.d.ts +2 -0
  256. package/dist/types/{components → legacy/components}/molecules/PhotoAvatar/PhotoAvatar.d.ts +1 -0
  257. package/dist/types/{components → legacy/components}/molecules/SeletorPesquisa/SeletorPesquisa.d.ts +3 -0
  258. package/dist/types/{components → legacy/components}/molecules/Sidebar/Sidebar.d.ts +1 -0
  259. package/dist/types/{components → legacy/components}/molecules/UserAvatar/UserAvatar.d.ts +1 -0
  260. package/dist/types/{components → legacy/components}/organisms/GanttChart/GanttChart.d.ts +8 -1
  261. package/dist/types/{components → legacy/components}/organisms/Kanban/Kanban.d.ts +3 -0
  262. package/dist/types/{components → legacy/components}/organisms/LazyLoader/LazyLoader.d.ts +1 -0
  263. package/dist/types/{components → legacy/components}/organisms/MapaPontos/MapaPontos.d.ts +12 -2
  264. package/dist/types/{components → legacy/components}/organisms/PainelFiltros/PainelFiltros.d.ts +7 -0
  265. package/dist/types/{components → legacy/components}/organisms/PesquisaRapida/PesquisaRapida.d.ts +5 -0
  266. package/dist/types/{components → legacy/components}/organisms/Profile/Profile.d.ts +1 -0
  267. package/dist/types/{components → legacy/components}/organisms/RoleGuard/RoleGuard.d.ts +6 -1
  268. package/dist/types/{components → legacy/components}/organisms/Selector/Selector.d.ts +1 -0
  269. package/dist/types/{components → legacy/components}/organisms/StatusMatrix/StatusMatrix.d.ts +6 -0
  270. package/dist/types/{components → legacy/components}/ui/Avisos.d.ts +9 -1
  271. package/dist/types/{components → legacy/components}/ui/BarraAcoes.d.ts +2 -0
  272. package/dist/types/{components → legacy/components}/ui/CabecalhoPagina.d.ts +6 -1
  273. package/dist/types/{components → legacy/components}/ui/Confirmacao.d.ts +2 -0
  274. package/dist/types/{components → legacy/components}/ui/Escolha.d.ts +14 -2
  275. package/dist/types/{components → legacy/components}/ui/FaixaAviso.d.ts +3 -0
  276. package/dist/types/{components → legacy/components}/ui/Passos.d.ts +3 -0
  277. package/dist/types/{components → legacy/components}/ui/Separadores.d.ts +3 -0
  278. package/dist/types/{components → legacy/components}/ui/TextoAuto.d.ts +2 -0
  279. package/dist/types/{components → legacy/components}/ui/Ui.d.ts +41 -7
  280. package/dist/types/{constants → legacy/constants}/FontWeight.d.ts +1 -0
  281. package/dist/types/{contexts → legacy/contexts}/apiContext.d.ts +1 -0
  282. package/dist/types/{contexts → legacy/contexts}/authContext.d.ts +2 -0
  283. package/dist/types/{contexts → legacy/contexts}/socketContext.d.ts +2 -0
  284. package/dist/types/{exports → legacy/exports}/hooks.d.ts +1 -1
  285. package/dist/types/{exports → legacy/exports}/molecules.d.ts +2 -1
  286. package/dist/types/legacy/exports/types.d.ts +7 -0
  287. package/dist/types/{hooks → legacy/hooks}/useApi.d.ts +2 -0
  288. package/dist/types/{hooks → legacy/hooks}/useTheme.d.ts +2 -0
  289. package/dist/types/{pages → legacy/pages}/Error/ErrorPage.d.ts +1 -0
  290. package/dist/types/{pages → legacy/pages}/Loading/Loading.d.ts +1 -0
  291. package/dist/types/{pages → legacy/pages}/Login/LoginScreen.d.ts +2 -0
  292. package/dist/types/{pages → legacy/pages}/NotFound/NotFound.d.ts +1 -0
  293. package/dist/types/legacy/styles/utils/utils.d.ts +2 -0
  294. package/dist/types/{types → legacy/types}/Fetcher.d.ts +1 -0
  295. package/dist/types/{types → legacy/types}/LoggedUser.d.ts +1 -0
  296. package/dist/types/{types → legacy/types}/SidebarMenu.d.ts +2 -0
  297. package/dist/types/legacy/types/themes/AcronymThemes.d.ts +4 -0
  298. package/dist/types/{types → legacy/types}/themes/ButtonThemes.d.ts +3 -0
  299. package/dist/types/legacy/types/themes/CustomSwitchThemes.d.ts +4 -0
  300. package/dist/types/{types → legacy/types}/themes/IconTextThemes.d.ts +1 -0
  301. package/dist/types/legacy/utils/stringUtils.d.ts +6 -0
  302. package/dist/types/v1/capabilities/documents/QRScanning/scanQRCodes.d.ts +25 -0
  303. package/dist/types/v1/components/documents/DocumentDropzone/DocumentDropzone.d.ts +60 -0
  304. package/dist/types/v1/components/forms/FormField/FormField.d.ts +44 -0
  305. package/dist/types/v1/components/forms/FormGrid/FormGrid.d.ts +21 -0
  306. package/dist/types/v1/components/overlays/Dialog/Dialog.d.ts +55 -0
  307. package/dist/types/v1/foundations/icons/icons.d.ts +24 -0
  308. package/dist/types/v1/index.d.ts +25 -0
  309. package/dist/types/v1/primitives/buttons/Button/Button.d.ts +18 -0
  310. package/dist/types/v1/primitives/documents/PdfPreview/PdfPreview.d.ts +17 -0
  311. package/dist/types/v1/primitives/documents/PdfPreview/pdfjs.d.ts +11 -0
  312. package/dist/types/v1/primitives/forms/LookupInput/LookupInput.d.ts +25 -0
  313. package/dist/types/v1/primitives/forms/SelectInput/SelectInput.d.ts +19 -0
  314. package/dist/types/v1/primitives/forms/TextArea/TextArea.d.ts +37 -0
  315. package/dist/types/v1/primitives/forms/TextInput/TextInput.d.ts +16 -0
  316. package/dist/types/v1/primitives/forms/field.d.ts +20 -0
  317. package/package.json +38 -33
  318. package/dist/bundle.js +0 -2
  319. package/dist/bundle.js.LICENSE.txt +0 -80
  320. package/dist/types/components/atoms/Icone/Icone.test.d.ts +0 -1
  321. package/dist/types/components/layout/Sidebar/Sidebar.test.d.ts +0 -1
  322. package/dist/types/components/molecules/BarraValor/BarraValor.test.d.ts +0 -1
  323. package/dist/types/components/molecules/DataTable/DataTable.test.d.ts +0 -1
  324. package/dist/types/components/molecules/SeletorPesquisa/SeletorPesquisa.test.d.ts +0 -1
  325. package/dist/types/components/organisms/Kanban/Kanban.test.d.ts +0 -1
  326. package/dist/types/components/organisms/RoleGuard/RoleGuard.test.d.ts +0 -1
  327. package/dist/types/components/ui/CaixaPesquisa.test.d.ts +0 -1
  328. package/dist/types/components/ui/Escolha.test.d.ts +0 -1
  329. package/dist/types/components/ui/Paginas.test.d.ts +0 -1
  330. package/dist/types/exports/types.d.ts +0 -7
  331. package/dist/types/styles/utils/utils.d.ts +0 -1
  332. package/dist/types/test/setup.d.ts +0 -1
  333. package/dist/types/types/themes/AcronymThemes.d.ts +0 -3
  334. package/dist/types/types/themes/CustomSwitchThemes.d.ts +0 -3
  335. package/dist/types/utils/stringUtils.d.ts +0 -3
  336. /package/dist/types/{components → legacy/components}/atoms/MultiTagPickerTag/MultiTagPickerTag.d.ts +0 -0
  337. /package/dist/types/{components → legacy/components}/atoms/SearchSelect/SearchSelect.d.ts +0 -0
  338. /package/dist/types/{components → legacy/components}/molecules/Search/Search.d.ts +0 -0
  339. /package/dist/types/{components → legacy/components}/ui/CaixaPesquisa.d.ts +0 -0
  340. /package/dist/types/{components → legacy/components}/ui/MenuAcoes.d.ts +0 -0
  341. /package/dist/types/{components → legacy/components}/ui/painelFlutuante.d.ts +0 -0
  342. /package/dist/types/{constants → legacy/constants}/Colors.d.ts +0 -0
  343. /package/dist/types/{exports → legacy/exports}/assets.d.ts +0 -0
  344. /package/dist/types/{exports → legacy/exports}/atoms.d.ts +0 -0
  345. /package/dist/types/{exports → legacy/exports}/charts.d.ts +0 -0
  346. /package/dist/types/{exports → legacy/exports}/constants.d.ts +0 -0
  347. /package/dist/types/{exports → legacy/exports}/contexts.d.ts +0 -0
  348. /package/dist/types/{exports → legacy/exports}/layout.d.ts +0 -0
  349. /package/dist/types/{exports → legacy/exports}/organisms.d.ts +0 -0
  350. /package/dist/types/{exports → legacy/exports}/pages.d.ts +0 -0
  351. /package/dist/types/{exports → legacy/exports}/styles.d.ts +0 -0
  352. /package/dist/types/{exports → legacy/exports}/ui.d.ts +0 -0
  353. /package/dist/types/{exports → legacy/exports}/utils.d.ts +0 -0
  354. /package/dist/types/{styles → legacy/styles}/themes/ButtonThemes.d.ts +0 -0
  355. /package/dist/types/{types → legacy/types}/SelectableElement.d.ts +0 -0
  356. /package/dist/types/{utils → legacy/utils}/keycloak.d.ts +0 -0
@@ -0,0 +1,206 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useMemo } from "react";
3
+ const LABELS = {
4
+ titulo: "Filtros",
5
+ limpar: "Limpar",
6
+ pesquisar: "Filtrar opções…",
7
+ semOpcoes: "Sem opções",
8
+ ocultar: "Ocultar filtros",
9
+ mostrar: "Mostrar filtros",
10
+ verTodas: "Ver todas ({n})",
11
+ verMenos: "Ver menos"
12
+ };
13
+ const VISIVEIS = 8;
14
+ function PainelFiltros({
15
+ seccoes,
16
+ valores,
17
+ onChange,
18
+ onLimpar,
19
+ labels,
20
+ className
21
+ }) {
22
+ const txt = { ...LABELS, ...labels };
23
+ const [fechado, setFechado] = useState(false);
24
+ const [abertas, setAbertas] = useState(
25
+ () => Object.fromEntries(seccoes.map((s) => [s.chave, s.aberta ?? true]))
26
+ );
27
+ const activos = useMemo(
28
+ () => Object.values(valores).reduce((n, v) => n + (v?.length ?? 0), 0),
29
+ [valores]
30
+ );
31
+ if (fechado) {
32
+ return /* @__PURE__ */ jsx(
33
+ "button",
34
+ {
35
+ onClick: () => setFechado(false),
36
+ title: txt.mostrar,
37
+ className: "h-full px-2 border-r border-slate-200 bg-white text-slate-500 hover:text-proef-navy dark:bg-slate-900 dark:border-slate-700 dark:text-slate-400 dark:hover:text-white",
38
+ children: /* @__PURE__ */ jsxs("span", { className: "[writing-mode:vertical-rl] text-xs font-semibold tracking-wide", children: [
39
+ txt.titulo,
40
+ activos ? ` · ${activos}` : ""
41
+ ] })
42
+ }
43
+ );
44
+ }
45
+ return /* @__PURE__ */ jsxs("div", { className: `bg-white border-r border-slate-200 dark:bg-slate-900 dark:border-slate-700 flex flex-col ${className ?? ""}`, children: [
46
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 border-b border-slate-200 dark:border-slate-700", children: [
47
+ /* @__PURE__ */ jsx("h2", { className: "text-sm font-black uppercase tracking-tight text-proef-navy dark:text-slate-100", children: txt.titulo }),
48
+ !!activos && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold bg-proef-navy text-white rounded-full px-1.5 py-0.5", children: activos }),
49
+ /* @__PURE__ */ jsxs("div", { className: "ml-auto flex items-center gap-1", children: [
50
+ /* @__PURE__ */ jsx(
51
+ "button",
52
+ {
53
+ onClick: onLimpar,
54
+ disabled: !activos,
55
+ className: "text-xs text-slate-500 hover:text-proef-navy dark:text-slate-400 dark:hover:text-white disabled:opacity-40",
56
+ children: txt.limpar
57
+ }
58
+ ),
59
+ /* @__PURE__ */ jsx(
60
+ "button",
61
+ {
62
+ onClick: () => setFechado(true),
63
+ title: txt.ocultar,
64
+ className: "text-slate-400 hover:text-proef-navy dark:hover:text-white px-1",
65
+ children: "‹"
66
+ }
67
+ )
68
+ ] })
69
+ ] }),
70
+ /* @__PURE__ */ jsx("div", { className: "overflow-auto flex-1 divide-y divide-slate-100", children: seccoes.map((seccao) => {
71
+ const nesta = seccao.campos.reduce((n, c) => n + (valores[c.chave]?.length ?? 0), 0);
72
+ const aberta = abertas[seccao.chave] ?? true;
73
+ return /* @__PURE__ */ jsxs("div", { children: [
74
+ /* @__PURE__ */ jsxs(
75
+ "button",
76
+ {
77
+ onClick: () => setAbertas((a) => ({ ...a, [seccao.chave]: !aberta })),
78
+ className: "w-full flex items-center gap-2 px-3 py-2 text-left text-xs font-bold uppercase tracking-wide text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800",
79
+ children: [
80
+ /* @__PURE__ */ jsx("span", { className: "text-slate-400 w-3", children: aberta ? "−" : "+" }),
81
+ seccao.titulo,
82
+ !!nesta && /* @__PURE__ */ jsx("span", { className: "text-[10px] font-bold bg-proef-navy text-white rounded-full px-1.5", children: nesta })
83
+ ]
84
+ }
85
+ ),
86
+ aberta && /* @__PURE__ */ jsx("div", { className: "px-3 pb-3 space-y-3", children: seccao.campos.map((campo) => /* @__PURE__ */ jsx(
87
+ Campo,
88
+ {
89
+ campo,
90
+ escolhidos: valores[campo.chave] ?? [],
91
+ onChange: (v) => onChange(campo.chave, v),
92
+ txt
93
+ },
94
+ campo.chave
95
+ )) })
96
+ ] }, seccao.chave);
97
+ }) })
98
+ ] });
99
+ }
100
+ function Campo({
101
+ campo,
102
+ escolhidos,
103
+ onChange,
104
+ txt
105
+ }) {
106
+ const [pesquisa, setPesquisa] = useState("");
107
+ const [todas, setTodas] = useState(false);
108
+ if (campo.tipo === "livre") {
109
+ return /* @__PURE__ */ jsxs("div", { children: [
110
+ /* @__PURE__ */ jsx("div", { className: "text-[11px] font-semibold text-slate-500 dark:text-slate-400 mb-1", children: campo.rotulo }),
111
+ campo.conteudo
112
+ ] });
113
+ }
114
+ const opcoes = campo.opcoes ?? [];
115
+ const limiar = campo.pesquisarACimaDe ?? 12;
116
+ const filtradas = pesquisa ? opcoes.filter((o) => semAcentos(o.rotulo).includes(semAcentos(pesquisa))) : opcoes;
117
+ const visiveis = todas ? filtradas : [
118
+ ...filtradas.slice(0, VISIVEIS),
119
+ ...filtradas.slice(VISIVEIS).filter((o) => escolhidos.includes(o.valor))
120
+ ];
121
+ const alternar = (valor) => {
122
+ if (campo.unico) {
123
+ onChange(escolhidos[0] === valor ? [] : [valor]);
124
+ return;
125
+ }
126
+ onChange(
127
+ escolhidos.includes(valor) ? escolhidos.filter((v) => v !== valor) : [...escolhidos, valor]
128
+ );
129
+ };
130
+ if (campo.tipo === "atalhos") {
131
+ return /* @__PURE__ */ jsxs("div", { children: [
132
+ /* @__PURE__ */ jsx("div", { className: "text-[11px] font-semibold text-slate-500 dark:text-slate-400 mb-1", children: campo.rotulo }),
133
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-1", children: opcoes.map((o) => {
134
+ const activo = escolhidos.includes(o.valor);
135
+ return /* @__PURE__ */ jsxs(
136
+ "button",
137
+ {
138
+ onClick: () => alternar(o.valor),
139
+ className: `flex items-center justify-between gap-1 px-2 py-1 rounded border text-[11px] ${activo ? "border-proef-navy bg-sky-50 text-proef-navy font-semibold dark:border-sky-400 dark:bg-sky-500/15 dark:text-sky-300" : "border-slate-200 text-slate-600 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800"}`,
140
+ children: [
141
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: o.rotulo }),
142
+ o.total != null && /* @__PURE__ */ jsx("b", { className: "tabular-nums", children: o.total.toLocaleString("pt-PT") })
143
+ ]
144
+ },
145
+ o.valor
146
+ );
147
+ }) })
148
+ ] });
149
+ }
150
+ return /* @__PURE__ */ jsxs("div", { children: [
151
+ /* @__PURE__ */ jsx("div", { className: "text-[11px] font-semibold text-slate-500 dark:text-slate-400 mb-1", children: campo.rotulo }),
152
+ opcoes.length > limiar && /* @__PURE__ */ jsx(
153
+ "input",
154
+ {
155
+ type: "search",
156
+ value: pesquisa,
157
+ onChange: (e) => setPesquisa(e.target.value),
158
+ placeholder: txt.pesquisar,
159
+ className: "w-full mb-1 border border-slate-300 rounded px-2 py-1 text-xs bg-white dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100"
160
+ }
161
+ ),
162
+ /* @__PURE__ */ jsxs("div", { className: "space-y-0.5", children: [
163
+ !visiveis.length && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-400", children: txt.semOpcoes }),
164
+ visiveis.map((o) => {
165
+ const activo = escolhidos.includes(o.valor);
166
+ return /* @__PURE__ */ jsxs(
167
+ "label",
168
+ {
169
+ className: `flex items-center gap-2 text-xs cursor-pointer px-1 py-0.5 rounded hover:bg-slate-50 dark:hover:bg-slate-800 ${o.total === 0 ? "opacity-45" : ""}`,
170
+ children: [
171
+ /* @__PURE__ */ jsx(
172
+ "input",
173
+ {
174
+ type: campo.unico ? "radio" : "checkbox",
175
+ checked: activo,
176
+ onClick: () => alternar(o.valor),
177
+ onChange: () => {
178
+ },
179
+ className: "accent-proef-navy"
180
+ }
181
+ ),
182
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", title: o.rotulo, children: o.rotulo }),
183
+ o.total != null && /* @__PURE__ */ jsx("span", { className: "tabular-nums text-slate-400", children: o.total.toLocaleString("pt-PT") })
184
+ ]
185
+ },
186
+ o.valor
187
+ );
188
+ })
189
+ ] }),
190
+ filtradas.length > VISIVEIS && /* @__PURE__ */ jsx(
191
+ "button",
192
+ {
193
+ onClick: () => setTodas((v) => !v),
194
+ className: "mt-1 text-[11px] text-proef-navy dark:text-sky-300 hover:underline",
195
+ children: todas ? txt.verMenos : txt.verTodas.replace("{n}", String(filtradas.length))
196
+ }
197
+ )
198
+ ] });
199
+ }
200
+ function semAcentos(texto) {
201
+ return texto.normalize("NFD").replace(/[̀-ͯ]/g, "").toLowerCase();
202
+ }
203
+ export {
204
+ PainelFiltros as default
205
+ };
206
+ //# sourceMappingURL=PainelFiltros.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PainelFiltros.js","sources":["../../../../../../src/legacy/components/organisms/PainelFiltros/PainelFiltros.tsx"],"sourcesContent":["import React, { useMemo, useState } from 'react'\r\n\r\n/**\r\n * Painel lateral de filtros facetados.\r\n *\r\n * O que o torna útil é a CONTAGEM ao lado de cada opção: diz quantos registos\r\n * ficariam se a opção fosse escolhida, com os restantes filtros mantidos. Quem\r\n * monta o painel é que calcula essas contagens — o componente não sabe o que\r\n * está a contar, e tem de ser assim: a conta certa exige tirar a própria\r\n * dimensão do filtro, e isso só o servidor sabe fazer.\r\n *\r\n * Genérico de propósito: recebe secções e opções, devolve escolhas.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\n\r\nexport interface OpcaoFiltro {\r\n valor: string\r\n rotulo: string\r\n /** Quantos registos esta opção daria. `undefined` não desenha contagem. */\r\n total?: number\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport type TipoCampoFiltro = 'opcoes' | 'atalhos' | 'livre'\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface CampoFiltro {\r\n /** Identifica o campo nos valores devolvidos. */\r\n chave: string\r\n rotulo: string\r\n tipo?: TipoCampoFiltro\r\n opcoes?: OpcaoFiltro[]\r\n /**\r\n * Escolha única. Por omissão o campo é múltiplo — é o que a maioria das\r\n * dimensões quer, e forçar única obrigaria a limpar antes de trocar.\r\n */\r\n unico?: boolean\r\n /** A partir de quantas opções aparece a caixa de pesquisa. */\r\n pesquisarACimaDe?: number\r\n /** Conteúdo próprio (datas, interruptores…), quando `tipo` é `livre`. */\r\n conteudo?: React.ReactNode\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface SeccaoFiltros {\r\n chave: string\r\n titulo: string\r\n campos: CampoFiltro[]\r\n /** Aberta na primeira vez. Depois manda o que o utilizador fizer. */\r\n aberta?: boolean\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface PainelFiltrosLabels {\r\n titulo: string\r\n limpar: string\r\n pesquisar: string\r\n semOpcoes: string\r\n ocultar: string\r\n mostrar: string\r\n verTodas: string\r\n verMenos: string\r\n}\r\n\r\nconst LABELS: PainelFiltrosLabels = {\r\n titulo: 'Filtros',\r\n limpar: 'Limpar',\r\n pesquisar: 'Filtrar opções…',\r\n semOpcoes: 'Sem opções',\r\n ocultar: 'Ocultar filtros',\r\n mostrar: 'Mostrar filtros',\r\n verTodas: 'Ver todas ({n})',\r\n verMenos: 'Ver menos',\r\n}\r\n\r\n/** Quantas opções se mostram antes do \"ver todas\". */\r\nconst VISIVEIS = 8\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface PainelFiltrosProps {\r\n seccoes: SeccaoFiltros[]\r\n /** Valores actuais: chave do campo → valores escolhidos. */\r\n valores: Record<string, string[]>\r\n onChange: (chave: string, valores: string[]) => void\r\n onLimpar?: () => void\r\n labels?: Partial<PainelFiltrosLabels>\r\n className?: string\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport default function PainelFiltros({\r\n seccoes,\r\n valores,\r\n onChange,\r\n onLimpar,\r\n labels,\r\n className,\r\n}: PainelFiltrosProps) {\r\n const txt = { ...LABELS, ...labels }\r\n const [fechado, setFechado] = useState(false)\r\n const [abertas, setAbertas] = useState<Record<string, boolean>>(() =>\r\n Object.fromEntries(seccoes.map((s) => [s.chave, s.aberta ?? true]))\r\n )\r\n\r\n const activos = useMemo(\r\n () => Object.values(valores).reduce((n, v) => n + (v?.length ?? 0), 0),\r\n [valores]\r\n )\r\n\r\n if (fechado) {\r\n return (\r\n <button\r\n onClick={() => setFechado(false)}\r\n title={txt.mostrar}\r\n className=\"h-full px-2 border-r border-slate-200 bg-white text-slate-500 hover:text-proef-navy dark:bg-slate-900 dark:border-slate-700 dark:text-slate-400 dark:hover:text-white\"\r\n >\r\n <span className=\"[writing-mode:vertical-rl] text-xs font-semibold tracking-wide\">\r\n {txt.titulo}\r\n {activos ? ` · ${activos}` : ''}\r\n </span>\r\n </button>\r\n )\r\n }\r\n\r\n return (\r\n <div className={`bg-white border-r border-slate-200 dark:bg-slate-900 dark:border-slate-700 flex flex-col ${className ?? ''}`}>\r\n <div className=\"flex items-center gap-2 px-3 py-2 border-b border-slate-200 dark:border-slate-700\">\r\n <h2 className=\"text-sm font-black uppercase tracking-tight text-proef-navy dark:text-slate-100\">{txt.titulo}</h2>\r\n {!!activos && (\r\n <span className=\"text-[10px] font-bold bg-proef-navy text-white rounded-full px-1.5 py-0.5\">\r\n {activos}\r\n </span>\r\n )}\r\n <div className=\"ml-auto flex items-center gap-1\">\r\n <button\r\n onClick={onLimpar}\r\n disabled={!activos}\r\n className=\"text-xs text-slate-500 hover:text-proef-navy dark:text-slate-400 dark:hover:text-white disabled:opacity-40\"\r\n >\r\n {txt.limpar}\r\n </button>\r\n <button\r\n onClick={() => setFechado(true)}\r\n title={txt.ocultar}\r\n className=\"text-slate-400 hover:text-proef-navy dark:hover:text-white px-1\"\r\n >\r\n ‹\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <div className=\"overflow-auto flex-1 divide-y divide-slate-100\">\r\n {seccoes.map((seccao) => {\r\n const nesta = seccao.campos.reduce((n, c) => n + (valores[c.chave]?.length ?? 0), 0)\r\n const aberta = abertas[seccao.chave] ?? true\r\n return (\r\n <div key={seccao.chave}>\r\n <button\r\n onClick={() => setAbertas((a) => ({ ...a, [seccao.chave]: !aberta }))}\r\n className=\"w-full flex items-center gap-2 px-3 py-2 text-left text-xs font-bold uppercase tracking-wide text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800\"\r\n >\r\n <span className=\"text-slate-400 w-3\">{aberta ? '−' : '+'}</span>\r\n {seccao.titulo}\r\n {!!nesta && (\r\n <span className=\"text-[10px] font-bold bg-proef-navy text-white rounded-full px-1.5\">\r\n {nesta}\r\n </span>\r\n )}\r\n </button>\r\n {aberta && (\r\n <div className=\"px-3 pb-3 space-y-3\">\r\n {seccao.campos.map((campo) => (\r\n <Campo\r\n key={campo.chave}\r\n campo={campo}\r\n escolhidos={valores[campo.chave] ?? []}\r\n onChange={(v) => onChange(campo.chave, v)}\r\n txt={txt}\r\n />\r\n ))}\r\n </div>\r\n )}\r\n </div>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nfunction Campo({\r\n campo,\r\n escolhidos,\r\n onChange,\r\n txt,\r\n}: {\r\n campo: CampoFiltro\r\n escolhidos: string[]\r\n onChange: (valores: string[]) => void\r\n txt: PainelFiltrosLabels\r\n}) {\r\n const [pesquisa, setPesquisa] = useState('')\r\n const [todas, setTodas] = useState(false)\r\n\r\n if (campo.tipo === 'livre') {\r\n return (\r\n <div>\r\n <div className=\"text-[11px] font-semibold text-slate-500 dark:text-slate-400 mb-1\">{campo.rotulo}</div>\r\n {campo.conteudo}\r\n </div>\r\n )\r\n }\r\n\r\n const opcoes = campo.opcoes ?? []\r\n const limiar = campo.pesquisarACimaDe ?? 12\r\n const filtradas = pesquisa\r\n ? opcoes.filter((o) => semAcentos(o.rotulo).includes(semAcentos(pesquisa)))\r\n : opcoes\r\n // As escolhidas ficam sempre visíveis, mesmo quando a lista está cortada:\r\n // esconder um filtro activo é a maneira mais rápida de alguém não perceber\r\n // porque é que a lista está pequena.\r\n const visiveis = todas\r\n ? filtradas\r\n : [\r\n ...filtradas.slice(0, VISIVEIS),\r\n ...filtradas.slice(VISIVEIS).filter((o) => escolhidos.includes(o.valor)),\r\n ]\r\n\r\n const alternar = (valor: string): void => {\r\n if (campo.unico) {\r\n onChange(escolhidos[0] === valor ? [] : [valor])\r\n return\r\n }\r\n onChange(\r\n escolhidos.includes(valor) ? escolhidos.filter((v) => v !== valor) : [...escolhidos, valor]\r\n )\r\n }\r\n\r\n if (campo.tipo === 'atalhos') {\r\n return (\r\n <div>\r\n <div className=\"text-[11px] font-semibold text-slate-500 dark:text-slate-400 mb-1\">{campo.rotulo}</div>\r\n <div className=\"grid grid-cols-2 gap-1\">\r\n {opcoes.map((o) => {\r\n const activo = escolhidos.includes(o.valor)\r\n return (\r\n <button\r\n key={o.valor}\r\n onClick={() => alternar(o.valor)}\r\n className={`flex items-center justify-between gap-1 px-2 py-1 rounded border text-[11px] ${\r\n activo\r\n ? 'border-proef-navy bg-sky-50 text-proef-navy font-semibold dark:border-sky-400 dark:bg-sky-500/15 dark:text-sky-300'\r\n : 'border-slate-200 text-slate-600 hover:bg-slate-50 dark:border-slate-700 dark:text-slate-300 dark:hover:bg-slate-800'\r\n }`}\r\n >\r\n <span className=\"truncate\">{o.rotulo}</span>\r\n {o.total != null && <b className=\"tabular-nums\">{o.total.toLocaleString('pt-PT')}</b>}\r\n </button>\r\n )\r\n })}\r\n </div>\r\n </div>\r\n )\r\n }\r\n\r\n return (\r\n <div>\r\n <div className=\"text-[11px] font-semibold text-slate-500 dark:text-slate-400 mb-1\">{campo.rotulo}</div>\r\n {opcoes.length > limiar && (\r\n <input\r\n type=\"search\"\r\n value={pesquisa}\r\n onChange={(e) => setPesquisa(e.target.value)}\r\n placeholder={txt.pesquisar}\r\n className=\"w-full mb-1 border border-slate-300 rounded px-2 py-1 text-xs bg-white dark:bg-slate-800 dark:border-slate-600 dark:text-slate-100\"\r\n />\r\n )}\r\n <div className=\"space-y-0.5\">\r\n {!visiveis.length && <div className=\"text-xs text-slate-400\">{txt.semOpcoes}</div>}\r\n {visiveis.map((o) => {\r\n const activo = escolhidos.includes(o.valor)\r\n return (\r\n <label\r\n key={o.valor}\r\n className={`flex items-center gap-2 text-xs cursor-pointer px-1 py-0.5 rounded hover:bg-slate-50 dark:hover:bg-slate-800 ${\r\n o.total === 0 ? 'opacity-45' : ''\r\n }`}\r\n >\r\n {/*\r\n `onClick` e não `onChange`.\r\n\r\n Um radio já marcado não dispara `onChange` ao ser clicado outra\r\n vez — o estado não muda, logo não há evento — e a lógica de\r\n desmarcar que o `alternar` tem nunca chegava a correr. Só se\r\n notava nos campos de escolha única: escolhia-se uma prioridade\r\n e a única saída era limpar o painel todo.\r\n\r\n `onClick` dispara sempre. O `onChange` vazio existe porque o\r\n React avisa quando um input é `checked` sem ele.\r\n */}\r\n <input\r\n type={campo.unico ? 'radio' : 'checkbox'}\r\n checked={activo}\r\n onClick={() => alternar(o.valor)}\r\n onChange={() => { /* o clique é tratado em onClick */ }}\r\n className=\"accent-proef-navy\"\r\n />\r\n <span className=\"flex-1 truncate\" title={o.rotulo}>{o.rotulo}</span>\r\n {o.total != null && (\r\n <span className=\"tabular-nums text-slate-400\">{o.total.toLocaleString('pt-PT')}</span>\r\n )}\r\n </label>\r\n )\r\n })}\r\n </div>\r\n {filtradas.length > VISIVEIS && (\r\n <button\r\n onClick={() => setTodas((v) => !v)}\r\n className=\"mt-1 text-[11px] text-proef-navy dark:text-sky-300 hover:underline\"\r\n >\r\n {todas ? txt.verMenos : txt.verTodas.replace('{n}', String(filtradas.length))}\r\n </button>\r\n )}\r\n </div>\r\n )\r\n}\r\n\r\n/** Comparação sem acentos nem maiúsculas: \"Guimarães\" tem de encontrar-se por \"guimaraes\". */\r\nfunction semAcentos(texto: string): string {\r\n return texto.normalize('NFD').replace(/[̀-ͯ]/g, '').toLowerCase()\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,147 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect } from "react";
3
+ const LABELS = {
4
+ titulo: "Pesquisa",
5
+ placeholder: "Pesquisar…",
6
+ semResultados: "Sem resultados.",
7
+ aEscrever: "Escreva para procurar.",
8
+ dica: "↑↓ para navegar · Enter para abrir · Esc para fechar"
9
+ };
10
+ const ESPERA_MS = 250;
11
+ function PesquisaRapida({
12
+ aberta,
13
+ onFechar,
14
+ procurar,
15
+ onEscolher,
16
+ minimo = 2,
17
+ labels
18
+ }) {
19
+ const txt = { ...LABELS, ...labels };
20
+ const [termo, setTermo] = useState("");
21
+ const [resultados, setResultados] = useState([]);
22
+ const [activo, setActivo] = useState(0);
23
+ const [aProcurar, setAProcurar] = useState(false);
24
+ const campoRef = useRef(null);
25
+ useEffect(() => {
26
+ if (aberta) {
27
+ setTermo("");
28
+ setResultados([]);
29
+ setActivo(0);
30
+ setTimeout(() => campoRef.current?.focus(), 0);
31
+ }
32
+ }, [aberta]);
33
+ useEffect(() => {
34
+ if (!aberta || termo.trim().length < minimo) {
35
+ setResultados([]);
36
+ return;
37
+ }
38
+ let cancelado = false;
39
+ setAProcurar(true);
40
+ const id = setTimeout(async () => {
41
+ try {
42
+ const r = await procurar(termo.trim());
43
+ if (!cancelado) {
44
+ setResultados(r);
45
+ setActivo(0);
46
+ }
47
+ } finally {
48
+ if (!cancelado) setAProcurar(false);
49
+ }
50
+ }, ESPERA_MS);
51
+ return () => {
52
+ cancelado = true;
53
+ clearTimeout(id);
54
+ };
55
+ }, [termo, aberta, minimo, procurar]);
56
+ if (!aberta) return null;
57
+ const aoTeclar = (e) => {
58
+ if (e.key === "Escape") return onFechar();
59
+ if (e.key === "ArrowDown") {
60
+ e.preventDefault();
61
+ setActivo((i) => Math.min(i + 1, resultados.length - 1));
62
+ }
63
+ if (e.key === "ArrowUp") {
64
+ e.preventDefault();
65
+ setActivo((i) => Math.max(i - 1, 0));
66
+ }
67
+ if (e.key === "Enter" && resultados[activo]) {
68
+ onEscolher(resultados[activo]);
69
+ onFechar();
70
+ }
71
+ };
72
+ let grupoAnterior;
73
+ return /* @__PURE__ */ jsx(
74
+ "div",
75
+ {
76
+ className: "fixed inset-0 z-[2000] bg-slate-900/40 flex items-start justify-center pt-24 px-4",
77
+ onClick: onFechar,
78
+ children: /* @__PURE__ */ jsxs(
79
+ "div",
80
+ {
81
+ className: "bg-white rounded-lg shadow-2xl w-full max-w-xl overflow-hidden",
82
+ onClick: (e) => e.stopPropagation(),
83
+ children: [
84
+ /* @__PURE__ */ jsx(
85
+ "input",
86
+ {
87
+ ref: campoRef,
88
+ value: termo,
89
+ onChange: (e) => setTermo(e.target.value),
90
+ onKeyDown: aoTeclar,
91
+ placeholder: txt.placeholder,
92
+ className: "w-full px-4 py-3 text-sm border-b border-slate-200 outline-none",
93
+ "aria-label": txt.titulo
94
+ }
95
+ ),
96
+ /* @__PURE__ */ jsxs("div", { className: "max-h-80 overflow-auto", children: [
97
+ termo.trim().length < minimo && /* @__PURE__ */ jsx("p", { className: "px-4 py-6 text-xs text-slate-400 text-center", children: txt.aEscrever }),
98
+ termo.trim().length >= minimo && !aProcurar && !resultados.length && /* @__PURE__ */ jsx("p", { className: "px-4 py-6 text-xs text-slate-400 text-center", children: txt.semResultados }),
99
+ resultados.map((r, i) => {
100
+ const cabecalho = r.grupo && r.grupo !== grupoAnterior ? r.grupo : null;
101
+ grupoAnterior = r.grupo;
102
+ return /* @__PURE__ */ jsxs("div", { children: [
103
+ cabecalho && /* @__PURE__ */ jsx("div", { className: "px-4 pt-2 pb-1 text-[10px] font-bold uppercase tracking-wide text-slate-400", children: cabecalho }),
104
+ /* @__PURE__ */ jsxs(
105
+ "button",
106
+ {
107
+ onMouseEnter: () => setActivo(i),
108
+ onClick: () => {
109
+ onEscolher(r);
110
+ onFechar();
111
+ },
112
+ className: `w-full text-left px-4 py-2 ${i === activo ? "bg-sky-50" : "hover:bg-slate-50"}`,
113
+ children: [
114
+ /* @__PURE__ */ jsx("div", { className: "text-sm text-proef-navy font-medium", children: r.titulo }),
115
+ r.detalhe && /* @__PURE__ */ jsx("div", { className: "text-xs text-slate-500 truncate", children: r.detalhe })
116
+ ]
117
+ }
118
+ )
119
+ ] }, r.id);
120
+ })
121
+ ] }),
122
+ /* @__PURE__ */ jsx("div", { className: "px-4 py-1.5 border-t border-slate-100 text-[10px] text-slate-400", children: txt.dica })
123
+ ]
124
+ }
125
+ )
126
+ }
127
+ );
128
+ }
129
+ function usarAtalhoPesquisa() {
130
+ const [aberta, setAberta] = useState(false);
131
+ useEffect(() => {
132
+ const aoTeclar = (e) => {
133
+ if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === "k") {
134
+ e.preventDefault();
135
+ setAberta((v) => !v);
136
+ }
137
+ };
138
+ window.addEventListener("keydown", aoTeclar);
139
+ return () => window.removeEventListener("keydown", aoTeclar);
140
+ }, []);
141
+ return { aberta, abrir: () => setAberta(true), fechar: () => setAberta(false) };
142
+ }
143
+ export {
144
+ PesquisaRapida as default,
145
+ usarAtalhoPesquisa
146
+ };
147
+ //# sourceMappingURL=PesquisaRapida.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PesquisaRapida.js","sources":["../../../../../../src/legacy/components/organisms/PesquisaRapida/PesquisaRapida.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from 'react'\r\n\r\n/**\r\n * Pesquisa rápida — a paleta que abre com Ctrl/Cmd + K.\r\n *\r\n * Genérica: recebe uma função de procura e devolve o que foi escolhido. Não\r\n * sabe o que procura nem onde — quem a monta é que sabe.\r\n *\r\n * O atalho é registado em `window` e não num campo: a utilidade disto é poder\r\n * ser chamada de qualquer sítio da aplicação sem ter de lá chegar com o rato.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\n\r\nexport interface ResultadoPesquisa {\r\n id: string\r\n titulo: string\r\n /** Segunda linha: o que distingue dois resultados com o mesmo título. */\r\n detalhe?: string\r\n /** Agrupador opcional, mostrado como cabeçalho. */\r\n grupo?: string\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface PesquisaRapidaLabels {\r\n titulo: string\r\n placeholder: string\r\n semResultados: string\r\n aEscrever: string\r\n dica: string\r\n}\r\n\r\nconst LABELS: PesquisaRapidaLabels = {\r\n titulo: 'Pesquisa',\r\n placeholder: 'Pesquisar…',\r\n semResultados: 'Sem resultados.',\r\n aEscrever: 'Escreva para procurar.',\r\n dica: '↑↓ para navegar · Enter para abrir · Esc para fechar',\r\n}\r\n\r\n/** Espera antes de procurar. Procurar a cada tecla seria um pedido por letra. */\r\nconst ESPERA_MS = 250\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport interface PesquisaRapidaProps {\r\n aberta: boolean\r\n onFechar: () => void\r\n /** Procura. Pode devolver poucos resultados — a lista não é paginada. */\r\n procurar: (termo: string) => Promise<ResultadoPesquisa[]>\r\n onEscolher: (resultado: ResultadoPesquisa) => void\r\n /** Mínimo de caracteres antes de procurar. */\r\n minimo?: number\r\n labels?: Partial<PesquisaRapidaLabels>\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport default function PesquisaRapida({\r\n aberta,\r\n onFechar,\r\n procurar,\r\n onEscolher,\r\n minimo = 2,\r\n labels,\r\n}: PesquisaRapidaProps) {\r\n const txt = { ...LABELS, ...labels }\r\n const [termo, setTermo] = useState('')\r\n const [resultados, setResultados] = useState<ResultadoPesquisa[]>([])\r\n const [activo, setActivo] = useState(0)\r\n const [aProcurar, setAProcurar] = useState(false)\r\n const campoRef = useRef<HTMLInputElement>(null)\r\n\r\n useEffect(() => {\r\n if (aberta) {\r\n setTermo('')\r\n setResultados([])\r\n setActivo(0)\r\n // O foco tem de esperar pela pintura: o campo ainda não está no DOM.\r\n setTimeout(() => campoRef.current?.focus(), 0)\r\n }\r\n }, [aberta])\r\n\r\n useEffect(() => {\r\n if (!aberta || termo.trim().length < minimo) {\r\n setResultados([])\r\n return\r\n }\r\n let cancelado = false\r\n setAProcurar(true)\r\n const id = setTimeout(async () => {\r\n try {\r\n const r = await procurar(termo.trim())\r\n // A resposta de uma pesquisa anterior pode chegar depois desta: sem o\r\n // cancelamento, a lista mostraria o resultado do termo errado.\r\n if (!cancelado) {\r\n setResultados(r)\r\n setActivo(0)\r\n }\r\n } finally {\r\n if (!cancelado) setAProcurar(false)\r\n }\r\n }, ESPERA_MS)\r\n return () => {\r\n cancelado = true\r\n clearTimeout(id)\r\n }\r\n }, [termo, aberta, minimo, procurar])\r\n\r\n if (!aberta) return null\r\n\r\n const aoTeclar = (e: React.KeyboardEvent): void => {\r\n if (e.key === 'Escape') return onFechar()\r\n if (e.key === 'ArrowDown') {\r\n e.preventDefault()\r\n setActivo((i) => Math.min(i + 1, resultados.length - 1))\r\n }\r\n if (e.key === 'ArrowUp') {\r\n e.preventDefault()\r\n setActivo((i) => Math.max(i - 1, 0))\r\n }\r\n if (e.key === 'Enter' && resultados[activo]) {\r\n onEscolher(resultados[activo])\r\n onFechar()\r\n }\r\n }\r\n\r\n let grupoAnterior: string | undefined\r\n\r\n return (\r\n <div\r\n className=\"fixed inset-0 z-[2000] bg-slate-900/40 flex items-start justify-center pt-24 px-4\"\r\n onClick={onFechar}\r\n >\r\n <div\r\n className=\"bg-white rounded-lg shadow-2xl w-full max-w-xl overflow-hidden\"\r\n onClick={(e) => e.stopPropagation()}\r\n >\r\n <input\r\n ref={campoRef}\r\n value={termo}\r\n onChange={(e) => setTermo(e.target.value)}\r\n onKeyDown={aoTeclar}\r\n placeholder={txt.placeholder}\r\n className=\"w-full px-4 py-3 text-sm border-b border-slate-200 outline-none\"\r\n aria-label={txt.titulo}\r\n />\r\n\r\n <div className=\"max-h-80 overflow-auto\">\r\n {termo.trim().length < minimo && (\r\n <p className=\"px-4 py-6 text-xs text-slate-400 text-center\">{txt.aEscrever}</p>\r\n )}\r\n {termo.trim().length >= minimo && !aProcurar && !resultados.length && (\r\n <p className=\"px-4 py-6 text-xs text-slate-400 text-center\">{txt.semResultados}</p>\r\n )}\r\n {resultados.map((r, i) => {\r\n const cabecalho = r.grupo && r.grupo !== grupoAnterior ? r.grupo : null\r\n grupoAnterior = r.grupo\r\n return (\r\n <div key={r.id}>\r\n {cabecalho && (\r\n <div className=\"px-4 pt-2 pb-1 text-[10px] font-bold uppercase tracking-wide text-slate-400\">\r\n {cabecalho}\r\n </div>\r\n )}\r\n <button\r\n onMouseEnter={() => setActivo(i)}\r\n onClick={() => {\r\n onEscolher(r)\r\n onFechar()\r\n }}\r\n className={`w-full text-left px-4 py-2 ${i === activo ? 'bg-sky-50' : 'hover:bg-slate-50'}`}\r\n >\r\n <div className=\"text-sm text-proef-navy font-medium\">{r.titulo}</div>\r\n {r.detalhe && <div className=\"text-xs text-slate-500 truncate\">{r.detalhe}</div>}\r\n </button>\r\n </div>\r\n )\r\n })}\r\n </div>\r\n\r\n <div className=\"px-4 py-1.5 border-t border-slate-100 text-[10px] text-slate-400\">{txt.dica}</div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\n/**\r\n * Regista Ctrl/Cmd + K e devolve o estado de abertura.\r\n *\r\n * Em hook para quem usa a paleta não ter de repetir a escuta do teclado — e\r\n * porque o atalho tem de ser removido ao desmontar, que é o passo que se\r\n * esquece.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function usarAtalhoPesquisa(): { aberta: boolean; abrir: () => void; fechar: () => void } {\r\n const [aberta, setAberta] = useState(false)\r\n\r\n useEffect(() => {\r\n const aoTeclar = (e: KeyboardEvent): void => {\r\n if ((e.ctrlKey || e.metaKey) && e.key.toLowerCase() === 'k') {\r\n e.preventDefault()\r\n setAberta((v) => !v)\r\n }\r\n }\r\n window.addEventListener('keydown', aoTeclar)\r\n return () => window.removeEventListener('keydown', aoTeclar)\r\n }, [])\r\n\r\n return { aberta, abrir: () => setAberta(true), fechar: () => setAberta(false) }\r\n}\r\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ @charset "UTF-8";
2
+ /* Intencionalmente VAZIO.
3
+ *
4
+ * As atoms antigas faziam `@import url('.../styles/index.scss')`, e este ficheiro
5
+ * injetava no CONSUMIDOR (via style-loader do bundle) um reset global agressivo
6
+ * (`* { margin:0; padding:0 }`), `@font-face` Urbanist (.ttf → 404 sob subpaths),
7
+ * `canvas { height:auto }` (partia o sizing dos charts) e `body { font-family;
8
+ * background }`. Uma LIB não pode impor nada disto — nukeava o spacing/fonte/fundo
9
+ * de qualquer app que importasse o common-ui (visível sobretudo em apps com
10
+ * utilities layered, que perdem ao `*` injetado).
11
+ *
12
+ * Cada app define o seu próprio reset/fonte/fundo. Mantido como no-op p/ os
13
+ * imports existentes não partirem. */
14
+ .cui-profile__container {
15
+ display: flex;
16
+ gap: 1rem;
17
+ }
18
+ .cui-profile__infos {
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: flex-end;
22
+ }
23
+ .cui-profile__options {
24
+ display: flex;
25
+ align-items: center;
26
+ }
@@ -0,0 +1,26 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import Acronym from "../../atoms/Acronym/Acronym.js";
3
+ import { faSignOut } from "@fortawesome/free-solid-svg-icons";
4
+ import './Profile.css';/* empty css */
5
+ import ActionButton from "../../atoms/Button/variants/ActionButton.js";
6
+ import { ActionButtonThemeStyled } from "../../../styles/themes/ButtonThemes.js";
7
+ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
8
+ const Profile = ({ user, handleLogout, notifications, logoutLabel = "Terminar Sessão" }) => {
9
+ return /* @__PURE__ */ jsx("div", { className: "cui-profile", children: /* @__PURE__ */ jsxs("div", { className: "cui-profile__container", children: [
10
+ /* @__PURE__ */ jsx("div", { className: "cui-profile__infos", children: /* @__PURE__ */ jsx(Acronym, { name: user.name, theme: { mode: "dark" } }) }),
11
+ /* @__PURE__ */ jsx("div", { className: "cui-profile__notifications", children: notifications }),
12
+ /* @__PURE__ */ jsx("div", { className: "cui-profile__options", children: /* @__PURE__ */ jsx(
13
+ ActionButton,
14
+ {
15
+ icon: /* @__PURE__ */ jsx(FontAwesomeIcon, { icon: faSignOut }),
16
+ onClick: handleLogout,
17
+ title: logoutLabel,
18
+ theme: { ...ActionButtonThemeStyled, size: "compact" }
19
+ }
20
+ ) })
21
+ ] }) });
22
+ };
23
+ export {
24
+ Profile as default
25
+ };
26
+ //# sourceMappingURL=Profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Profile.js","sources":["../../../../../../src/legacy/components/organisms/Profile/Profile.tsx"],"sourcesContent":["import React, { ReactNode } from 'react'\r\nimport { LoggedUser } from '../../../types/LoggedUser'\r\nimport Acronym from '../../atoms/Acronym/Acronym'\r\nimport { faSignOut } from '@fortawesome/free-solid-svg-icons'\r\n\r\nimport './Profile.scss'\r\nimport ActionButton from '../../atoms/Button/variants/ActionButton'\r\nimport { ActionButtonThemeStyled } from '../../../styles/themes/ButtonThemes'\r\nimport { FontAwesomeIcon } from '@fortawesome/react-fontawesome'\r\n\r\ntype Props = {\r\n user: LoggedUser\r\n handleLogout: () => void\r\n notifications?: ReactNode\r\n logoutLabel?: string\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nconst Profile = ({ user, handleLogout, notifications, logoutLabel = 'Terminar Sessão' }: Props) => {\r\n return (\r\n <div className=\"cui-profile\">\r\n <div className=\"cui-profile__container\">\r\n <div className=\"cui-profile__infos\">\r\n <Acronym name={user.name} theme={{ mode: 'dark' }} />\r\n </div>\r\n <div className=\"cui-profile__notifications\">{notifications}</div>\r\n <div className=\"cui-profile__options\">\r\n <ActionButton\r\n icon={<FontAwesomeIcon icon={faSignOut} />}\r\n onClick={handleLogout}\r\n title={logoutLabel}\r\n theme={{ ...ActionButtonThemeStyled, size: 'compact' }}\r\n />\r\n </div>\r\n </div>\r\n </div>\r\n )\r\n}\r\n\r\nexport default Profile\r\n"],"names":[],"mappings":";;;;;;;AAkBA,MAAM,UAAU,CAAC,EAAE,MAAM,cAAc,eAAe,cAAc,wBAA+B;AACjG,6BACG,OAAA,EAAI,WAAU,eACb,UAAA,qBAAC,OAAA,EAAI,WAAU,0BACb,UAAA;AAAA,IAAA,oBAAC,OAAA,EAAI,WAAU,sBACb,UAAA,oBAAC,SAAA,EAAQ,MAAM,KAAK,MAAM,OAAO,EAAE,MAAM,OAAA,GAAU,GACrD;AAAA,IACA,oBAAC,OAAA,EAAI,WAAU,8BAA8B,UAAA,eAAc;AAAA,IAC3D,oBAAC,OAAA,EAAI,WAAU,wBACb,UAAA;AAAA,MAAC;AAAA,MAAA;AAAA,QACC,MAAM,oBAAC,iBAAA,EAAgB,MAAM,UAAA,CAAW;AAAA,QACxC,SAAS;AAAA,QACT,OAAO;AAAA,QACP,OAAO,EAAE,GAAG,yBAAyB,MAAM,UAAA;AAAA,MAAU;AAAA,IAAA,EACvD,CACF;AAAA,EAAA,EAAA,CACF,EAAA,CACF;AAEJ;"}
@@ -0,0 +1,68 @@
1
+ import { jsx, Fragment } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect } from "react";
3
+ function lerRolesDoToken(accessToken) {
4
+ try {
5
+ const corpo = accessToken.split(".")[1];
6
+ if (!corpo) return [];
7
+ const json = atob(corpo.replace(/-/g, "+").replace(/_/g, "/"));
8
+ const payload = JSON.parse(json);
9
+ return payload.roles ?? [];
10
+ } catch {
11
+ return [];
12
+ }
13
+ }
14
+ function RoleGuard({
15
+ rolesPermitidas,
16
+ temSessao,
17
+ aAutenticar = false,
18
+ obterToken,
19
+ onPrecisaInteracao,
20
+ semSessao,
21
+ aCarregar,
22
+ semPermissao,
23
+ children
24
+ }) {
25
+ const [decisao, setDecisao] = useState("aResolver");
26
+ const jaCorreu = useRef(false);
27
+ const refRoles = useRef(rolesPermitidas);
28
+ const refObterToken = useRef(obterToken);
29
+ const refPrecisaInteracao = useRef(onPrecisaInteracao);
30
+ refRoles.current = rolesPermitidas;
31
+ refObterToken.current = obterToken;
32
+ refPrecisaInteracao.current = onPrecisaInteracao;
33
+ useEffect(() => {
34
+ if (!temSessao || aAutenticar || jaCorreu.current) return;
35
+ jaCorreu.current = true;
36
+ let cancelado = false;
37
+ void (async () => {
38
+ if (!refRoles.current.length) {
39
+ if (!cancelado) setDecisao("permitido");
40
+ return;
41
+ }
42
+ try {
43
+ const temAlguma = (t) => lerRolesDoToken(t).some((r) => refRoles.current.includes(r));
44
+ let ok = temAlguma(await refObterToken.current());
45
+ if (!ok) ok = temAlguma(await refObterToken.current(true));
46
+ if (!cancelado) setDecisao(ok ? "permitido" : "negado");
47
+ } catch {
48
+ if (refPrecisaInteracao.current) {
49
+ refPrecisaInteracao.current();
50
+ return;
51
+ }
52
+ if (!cancelado) setDecisao("negado");
53
+ }
54
+ })();
55
+ return () => {
56
+ cancelado = true;
57
+ };
58
+ }, [temSessao, aAutenticar]);
59
+ if (!temSessao) return /* @__PURE__ */ jsx(Fragment, { children: semSessao });
60
+ if (decisao === "aResolver") return /* @__PURE__ */ jsx(Fragment, { children: aCarregar });
61
+ if (decisao === "negado") return /* @__PURE__ */ jsx(Fragment, { children: semPermissao });
62
+ return /* @__PURE__ */ jsx(Fragment, { children });
63
+ }
64
+ export {
65
+ RoleGuard as default,
66
+ lerRolesDoToken
67
+ };
68
+ //# sourceMappingURL=RoleGuard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RoleGuard.js","sources":["../../../../../../src/legacy/components/organisms/RoleGuard/RoleGuard.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState, type ReactNode } from 'react'\r\n\r\n/**\r\n * Restringe o acesso ao portal a um conjunto de app roles.\r\n *\r\n * Não conhece o MSAL da aplicação: recebe funções para obter o token. É\r\n * deliberado — quando o bundler duplica o `@azure/msal-react`, um `useMsal`\r\n * dentro da biblioteca lê um contexto diferente do da app, a conta vem a null e\r\n * o acesso é negado a quem tem a role certa. Passando o token por fora, isso\r\n * não pode acontecer.\r\n *\r\n * As roles vêm no ACCESS token da API, não no id token — é o erro mais comum\r\n * nesta integração, e a razão de `obterToken` dever pedir o scope da API.\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\n\r\nexport interface RoleGuardProps {\r\n /** Roles com acesso. Lista vazia = qualquer autenticado entra. */\r\n rolesPermitidas: string[]\r\n /** Há sessão iniciada? Enquanto false, mostra-se `semSessao`. */\r\n temSessao: boolean\r\n /** Ainda a resolver a autenticação — evita decidir cedo de mais. */\r\n aAutenticar?: boolean\r\n /** Token de acesso da API. `forcarRenovacao` ignora a cache. */\r\n obterToken: (forcarRenovacao?: boolean) => Promise<string>\r\n /** Chamado quando falta consentimento e é preciso um redirect interactivo. */\r\n onPrecisaInteracao?: () => void\r\n\r\n semSessao: ReactNode\r\n aCarregar: ReactNode\r\n semPermissao: ReactNode\r\n children: ReactNode\r\n}\r\n\r\ntype Decisao = 'aResolver' | 'permitido' | 'negado'\r\n\r\n/**\r\n * Lê a claim `roles` de um JWT, sem validar a assinatura (quem valida é a API).\r\n * @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy.\r\n */\r\nexport function lerRolesDoToken(accessToken: string): string[] {\r\n try {\r\n const corpo = accessToken.split('.')[1]\r\n if (!corpo) return []\r\n const json = atob(corpo.replace(/-/g, '+').replace(/_/g, '/'))\r\n const payload = JSON.parse(json) as { roles?: string[] }\r\n return payload.roles ?? []\r\n } catch {\r\n return []\r\n }\r\n}\r\n\r\n/** @deprecated Legacy — não usar em código novo. Ver Storybook › Legacy. */\r\nexport default function RoleGuard({\r\n rolesPermitidas,\r\n temSessao,\r\n aAutenticar = false,\r\n obterToken,\r\n onPrecisaInteracao,\r\n semSessao,\r\n aCarregar,\r\n semPermissao,\r\n children,\r\n}: RoleGuardProps) {\r\n const [decisao, setDecisao] = useState<Decisao>('aResolver')\r\n\r\n /**\r\n * Decidir uma só vez. Em `useState` isto era um bug: a marca entrava nas\r\n * dependências do efeito, o efeito voltava a correr, a limpeza cancelava a\r\n * tentativa em curso e a segunda passagem saía logo — a decisão nunca chegava\r\n * e o portal ficava eternamente em `aCarregar`. Só não dava quando o token\r\n * estava em cache (um F5), porque aí resolve antes da limpeza.\r\n */\r\n const jaCorreu = useRef(false)\r\n\r\n /**\r\n * As três funções/arrays abaixo chegam quase sempre inline de quem usa o\r\n * componente, ou seja com identidade nova a cada render. Em dependências,\r\n * faziam o efeito re-correr e cancelar o pedido em voo. Ficam em refs: o\r\n * efeito usa sempre a última versão sem reagir a ela.\r\n */\r\n const refRoles = useRef(rolesPermitidas)\r\n const refObterToken = useRef(obterToken)\r\n const refPrecisaInteracao = useRef(onPrecisaInteracao)\r\n refRoles.current = rolesPermitidas\r\n refObterToken.current = obterToken\r\n refPrecisaInteracao.current = onPrecisaInteracao\r\n\r\n useEffect(() => {\r\n if (!temSessao || aAutenticar || jaCorreu.current) return\r\n jaCorreu.current = true\r\n\r\n let cancelado = false\r\n void (async () => {\r\n if (!refRoles.current.length) {\r\n if (!cancelado) setDecisao('permitido')\r\n return\r\n }\r\n try {\r\n const temAlguma = (t: string): boolean =>\r\n lerRolesDoToken(t).some((r) => refRoles.current.includes(r))\r\n\r\n let ok = temAlguma(await refObterToken.current())\r\n\r\n // Sem correspondência, uma segunda tentativa sem cache: uma role\r\n // atribuída há pouco ainda não está no token guardado, e negar o acesso\r\n // a quem acabou de a receber é o pior desfecho possível.\r\n if (!ok) ok = temAlguma(await refObterToken.current(true))\r\n\r\n if (!cancelado) setDecisao(ok ? 'permitido' : 'negado')\r\n } catch {\r\n // Falta de consentimento resolve-se com um redirect, uma única vez.\r\n if (refPrecisaInteracao.current) {\r\n refPrecisaInteracao.current()\r\n return\r\n }\r\n if (!cancelado) setDecisao('negado')\r\n }\r\n })()\r\n\r\n return () => {\r\n cancelado = true\r\n }\r\n }, [temSessao, aAutenticar])\r\n\r\n if (!temSessao) return <>{semSessao}</>\r\n if (decisao === 'aResolver') return <>{aCarregar}</>\r\n if (decisao === 'negado') return <>{semPermissao}</>\r\n return <>{children}</>\r\n}\r\n"],"names":[],"mappings":";;AAwCO,SAAS,gBAAgB,aAA+B;AAC7D,MAAI;AACF,UAAM,QAAQ,YAAY,MAAM,GAAG,EAAE,CAAC;AACtC,QAAI,CAAC,MAAO,QAAO,CAAA;AACnB,UAAM,OAAO,KAAK,MAAM,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG,CAAC;AAC7D,UAAM,UAAU,KAAK,MAAM,IAAI;AAC/B,WAAO,QAAQ,SAAS,CAAA;AAAA,EAC1B,QAAQ;AACN,WAAO,CAAA;AAAA,EACT;AACF;AAGA,SAAwB,UAAU;AAAA,EAChC;AAAA,EACA;AAAA,EACA,cAAc;AAAA,EACd;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAmB;AACjB,QAAM,CAAC,SAAS,UAAU,IAAI,SAAkB,WAAW;AAS3D,QAAM,WAAW,OAAO,KAAK;AAQ7B,QAAM,WAAW,OAAO,eAAe;AACvC,QAAM,gBAAgB,OAAO,UAAU;AACvC,QAAM,sBAAsB,OAAO,kBAAkB;AACrD,WAAS,UAAU;AACnB,gBAAc,UAAU;AACxB,sBAAoB,UAAU;AAE9B,YAAU,MAAM;AACd,QAAI,CAAC,aAAa,eAAe,SAAS,QAAS;AACnD,aAAS,UAAU;AAEnB,QAAI,YAAY;AAChB,UAAM,YAAY;AAChB,UAAI,CAAC,SAAS,QAAQ,QAAQ;AAC5B,YAAI,CAAC,UAAW,YAAW,WAAW;AACtC;AAAA,MACF;AACA,UAAI;AACF,cAAM,YAAY,CAAC,MACjB,gBAAgB,CAAC,EAAE,KAAK,CAAC,MAAM,SAAS,QAAQ,SAAS,CAAC,CAAC;AAE7D,YAAI,KAAK,UAAU,MAAM,cAAc,SAAS;AAKhD,YAAI,CAAC,GAAI,MAAK,UAAU,MAAM,cAAc,QAAQ,IAAI,CAAC;AAEzD,YAAI,CAAC,UAAW,YAAW,KAAK,cAAc,QAAQ;AAAA,MACxD,QAAQ;AAEN,YAAI,oBAAoB,SAAS;AAC/B,8BAAoB,QAAA;AACpB;AAAA,QACF;AACA,YAAI,CAAC,UAAW,YAAW,QAAQ;AAAA,MACrC;AAAA,IACF,GAAA;AAEA,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EACF,GAAG,CAAC,WAAW,WAAW,CAAC;AAE3B,MAAI,CAAC,UAAW,QAAO,oBAAA,UAAA,EAAG,UAAA,WAAU;AACpC,MAAI,YAAY,YAAa,QAAO,oBAAA,UAAA,EAAG,UAAA,WAAU;AACjD,MAAI,YAAY,SAAU,QAAO,oBAAA,UAAA,EAAG,UAAA,cAAa;AACjD,yCAAU,UAAS;AACrB;"}
@@ -0,0 +1,87 @@
1
+ .cui-selector {
2
+ max-width: 45vw;
3
+ }
4
+ .cui-selector__search {
5
+ border-radius: 25px;
6
+ padding: 10px;
7
+ }
8
+ .cui-selector__selected {
9
+ margin: 4vh 0;
10
+ border: 2px solid var(--dark-blue);
11
+ border-radius: 25px;
12
+ }
13
+ .cui-selector__selected-list {
14
+ padding: 10px;
15
+ display: flex;
16
+ justify-content: left;
17
+ align-items: center;
18
+ flex-wrap: wrap;
19
+ gap: 10px;
20
+ max-height: 17vh;
21
+ overflow-y: auto;
22
+ }
23
+ .cui-selector__selected-list-item {
24
+ font-size: 13px;
25
+ display: flex;
26
+ padding: 10px;
27
+ background: var(--dark-blue);
28
+ color: white;
29
+ border-radius: 25px;
30
+ }
31
+ .cui-selector__selected-list-info {
32
+ display: flex;
33
+ align-items: center;
34
+ }
35
+ .cui-selector__selected-list-option {
36
+ margin: 0 0 0 7px;
37
+ }
38
+ .cui-selector__elements {
39
+ margin: 5vh 0;
40
+ height: 30vh;
41
+ overflow: auto;
42
+ }
43
+ .cui-selector__elements-list {
44
+ display: flex;
45
+ flex-direction: column;
46
+ gap: 10px;
47
+ padding-right: 10px;
48
+ cursor: pointer;
49
+ }
50
+ .cui-selector__elements-list-item {
51
+ display: flex;
52
+ justify-content: space-between;
53
+ padding: 10px;
54
+ border-radius: 25px;
55
+ }
56
+ .cui-selector__elements-list-item-selected {
57
+ color: white;
58
+ background: var(--dark-blue);
59
+ }
60
+ .cui-selector__elements-list-item-not-selected {
61
+ background: var(--light-gray-01);
62
+ color: var(--dark-blue);
63
+ }
64
+ .cui-selector__elements-list-item-description {
65
+ display: flex;
66
+ justify-content: left;
67
+ align-items: center;
68
+ }
69
+ .cui-selector__elements-list-item-option {
70
+ display: flex;
71
+ justify-content: right;
72
+ align-items: center;
73
+ }
74
+ .cui-selector__elements-list-item-option label {
75
+ margin: 0 10px;
76
+ }
77
+ .cui-selector__elements-empty {
78
+ padding: 1rem;
79
+ }
80
+ .cui-selector__selected-empty {
81
+ padding: 1rem;
82
+ }
83
+ .cui-selector__confirmation {
84
+ display: flex;
85
+ justify-content: center;
86
+ margin: 1vh 0;
87
+ }