@maxal_studio/kratosjs-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. package/README.md +44 -0
  2. package/dist/FieldRenderer.d.ts +13 -0
  3. package/dist/FieldRenderer.js +62 -0
  4. package/dist/FormRenderer.d.ts +7 -0
  5. package/dist/FormRenderer.js +78 -0
  6. package/dist/TableRenderer.d.ts +2 -0
  7. package/dist/TableRenderer.js +1 -0
  8. package/dist/api/actionsApi.d.ts +23 -0
  9. package/dist/api/actionsApi.js +46 -0
  10. package/dist/api/authenticatedFetch.d.ts +8 -0
  11. package/dist/api/authenticatedFetch.js +31 -0
  12. package/dist/api/exportApi.d.ts +18 -0
  13. package/dist/api/exportApi.js +50 -0
  14. package/dist/api/http.d.ts +24 -0
  15. package/dist/api/http.js +52 -0
  16. package/dist/api/resourceApi.d.ts +37 -0
  17. package/dist/api/resourceApi.js +52 -0
  18. package/dist/api/tableApi.d.ts +83 -0
  19. package/dist/api/tableApi.js +51 -0
  20. package/dist/api/urls.d.ts +19 -0
  21. package/dist/api/urls.js +46 -0
  22. package/dist/app.d.ts +101 -0
  23. package/dist/app.js +89 -0
  24. package/dist/auth/AuthContext.d.ts +22 -0
  25. package/dist/auth/AuthContext.js +147 -0
  26. package/dist/auth/LoginPage.d.ts +10 -0
  27. package/dist/auth/LoginPage.js +179 -0
  28. package/dist/auth/ProtectedRoute.d.ts +12 -0
  29. package/dist/auth/ProtectedRoute.js +22 -0
  30. package/dist/auth/authApiClient.d.ts +24 -0
  31. package/dist/auth/authApiClient.js +95 -0
  32. package/dist/auth/types.d.ts +103 -0
  33. package/dist/auth/types.js +1 -0
  34. package/dist/components/ActionFormModal.d.ts +22 -0
  35. package/dist/components/ActionFormModal.js +8 -0
  36. package/dist/components/AdminPanel.d.ts +11 -0
  37. package/dist/components/AdminPanel.js +194 -0
  38. package/dist/components/Checkbox.d.ts +10 -0
  39. package/dist/components/Checkbox.js +8 -0
  40. package/dist/components/CheckboxField.d.ts +7 -0
  41. package/dist/components/CheckboxField.js +26 -0
  42. package/dist/components/ColorPickerField.d.ts +7 -0
  43. package/dist/components/ColorPickerField.js +26 -0
  44. package/dist/components/DateTimePickerField.d.ts +7 -0
  45. package/dist/components/DateTimePickerField.js +64 -0
  46. package/dist/components/FileUploadField.d.ts +9 -0
  47. package/dist/components/FileUploadField.js +478 -0
  48. package/dist/components/GlobalSearch.d.ts +22 -0
  49. package/dist/components/GlobalSearch.js +181 -0
  50. package/dist/components/GroupField.d.ts +7 -0
  51. package/dist/components/GroupField.js +23 -0
  52. package/dist/components/HiddenField.d.ts +3 -0
  53. package/dist/components/HiddenField.js +10 -0
  54. package/dist/components/ModalBreadcrumb.d.ts +5 -0
  55. package/dist/components/ModalBreadcrumb.js +33 -0
  56. package/dist/components/ModalDrawer.d.ts +15 -0
  57. package/dist/components/ModalDrawer.js +40 -0
  58. package/dist/components/RadioField.d.ts +7 -0
  59. package/dist/components/RadioField.js +26 -0
  60. package/dist/components/RepeaterField.d.ts +3 -0
  61. package/dist/components/RepeaterField.js +191 -0
  62. package/dist/components/ResourceModalRenderer.d.ts +10 -0
  63. package/dist/components/ResourceModalRenderer.js +80 -0
  64. package/dist/components/RichEditorField.d.ts +3 -0
  65. package/dist/components/RichEditorField.js +655 -0
  66. package/dist/components/SectionField.d.ts +9 -0
  67. package/dist/components/SectionField.js +111 -0
  68. package/dist/components/SelectField.d.ts +8 -0
  69. package/dist/components/SelectField.js +523 -0
  70. package/dist/components/TabsField.d.ts +10 -0
  71. package/dist/components/TabsField.js +214 -0
  72. package/dist/components/TagsInputField.d.ts +7 -0
  73. package/dist/components/TagsInputField.js +172 -0
  74. package/dist/components/TextInputField.d.ts +7 -0
  75. package/dist/components/TextInputField.js +44 -0
  76. package/dist/components/TextareaField.d.ts +7 -0
  77. package/dist/components/TextareaField.js +31 -0
  78. package/dist/components/ToggleField.d.ts +7 -0
  79. package/dist/components/ToggleField.js +57 -0
  80. package/dist/components/ViewModal.d.ts +25 -0
  81. package/dist/components/ViewModal.js +159 -0
  82. package/dist/components/blocks/BlockRenderer.d.ts +7 -0
  83. package/dist/components/blocks/BlockRenderer.js +36 -0
  84. package/dist/components/blocks/FormBlockRenderer.d.ts +6 -0
  85. package/dist/components/blocks/FormBlockRenderer.js +110 -0
  86. package/dist/components/blocks/TableBlockRenderer.d.ts +6 -0
  87. package/dist/components/blocks/TableBlockRenderer.js +12 -0
  88. package/dist/components/blocks/TabsBlockRenderer.d.ts +7 -0
  89. package/dist/components/blocks/TabsBlockRenderer.js +11 -0
  90. package/dist/components/blocks/WidgetBlockRenderer.d.ts +6 -0
  91. package/dist/components/blocks/WidgetBlockRenderer.js +11 -0
  92. package/dist/components/columns/CheckboxColumnComponent.d.ts +6 -0
  93. package/dist/components/columns/CheckboxColumnComponent.js +21 -0
  94. package/dist/components/columns/ColorColumnComponent.d.ts +3 -0
  95. package/dist/components/columns/ColorColumnComponent.js +11 -0
  96. package/dist/components/columns/DeeplinkWrapper.d.ts +15 -0
  97. package/dist/components/columns/DeeplinkWrapper.js +85 -0
  98. package/dist/components/columns/IconColumnComponent.d.ts +3 -0
  99. package/dist/components/columns/IconColumnComponent.js +52 -0
  100. package/dist/components/columns/ImageColumnComponent.d.ts +3 -0
  101. package/dist/components/columns/ImageColumnComponent.js +98 -0
  102. package/dist/components/columns/MediaColumnComponent.d.ts +3 -0
  103. package/dist/components/columns/MediaColumnComponent.js +160 -0
  104. package/dist/components/columns/SelectColumnComponent.d.ts +6 -0
  105. package/dist/components/columns/SelectColumnComponent.js +26 -0
  106. package/dist/components/columns/TagsColumnComponent.d.ts +3 -0
  107. package/dist/components/columns/TagsColumnComponent.js +18 -0
  108. package/dist/components/columns/TextColumnComponent.d.ts +11 -0
  109. package/dist/components/columns/TextColumnComponent.js +107 -0
  110. package/dist/components/columns/TextInputColumnComponent.d.ts +6 -0
  111. package/dist/components/columns/TextInputColumnComponent.js +18 -0
  112. package/dist/components/columns/ToggleColumnComponent.d.ts +6 -0
  113. package/dist/components/columns/ToggleColumnComponent.js +25 -0
  114. package/dist/components/columns/VideoColumnComponent.d.ts +3 -0
  115. package/dist/components/columns/VideoColumnComponent.js +125 -0
  116. package/dist/components/columns/ViewColumnComponent.d.ts +3 -0
  117. package/dist/components/columns/ViewColumnComponent.js +7 -0
  118. package/dist/components/errors/ErrorBoundary.d.ts +23 -0
  119. package/dist/components/errors/ErrorBoundary.js +33 -0
  120. package/dist/components/filters/CustomFilterComponent.d.ts +10 -0
  121. package/dist/components/filters/CustomFilterComponent.js +33 -0
  122. package/dist/components/filters/DateFilterComponent.d.ts +15 -0
  123. package/dist/components/filters/DateFilterComponent.js +132 -0
  124. package/dist/components/filters/QueryBuilderFilterComponent.d.ts +11 -0
  125. package/dist/components/filters/QueryBuilderFilterComponent.js +200 -0
  126. package/dist/components/layout/Header.d.ts +10 -0
  127. package/dist/components/layout/Header.js +70 -0
  128. package/dist/components/layout/PanelBrandMark.d.ts +8 -0
  129. package/dist/components/layout/PanelBrandMark.js +28 -0
  130. package/dist/components/layout/Sidebar.d.ts +35 -0
  131. package/dist/components/layout/Sidebar.js +125 -0
  132. package/dist/components/modals/RelationCreateModal.d.ts +19 -0
  133. package/dist/components/modals/RelationCreateModal.js +57 -0
  134. package/dist/components/modals/ResourceFormModal.d.ts +37 -0
  135. package/dist/components/modals/ResourceFormModal.js +44 -0
  136. package/dist/components/modals/useResourceForm.d.ts +40 -0
  137. package/dist/components/modals/useResourceForm.js +138 -0
  138. package/dist/components/modals/view/RecordActions.d.ts +17 -0
  139. package/dist/components/modals/view/RecordActions.js +16 -0
  140. package/dist/components/modals/view/RecordDetails.d.ts +13 -0
  141. package/dist/components/modals/view/RecordDetails.js +29 -0
  142. package/dist/components/modals/view/RelationPanel.d.ts +18 -0
  143. package/dist/components/modals/view/RelationPanel.js +16 -0
  144. package/dist/components/modals/view/RelationTabs.d.ts +32 -0
  145. package/dist/components/modals/view/RelationTabs.js +42 -0
  146. package/dist/components/modals/view/useRecordView.d.ts +18 -0
  147. package/dist/components/modals/view/useRecordView.js +114 -0
  148. package/dist/components/pages/PageRenderer.d.ts +6 -0
  149. package/dist/components/pages/PageRenderer.js +107 -0
  150. package/dist/components/table/ColumnTogglePopup.d.ts +11 -0
  151. package/dist/components/table/ColumnTogglePopup.js +16 -0
  152. package/dist/components/table/GridCard.d.ts +21 -0
  153. package/dist/components/table/GridCard.js +30 -0
  154. package/dist/components/table/GridView.d.ts +23 -0
  155. package/dist/components/table/GridView.js +49 -0
  156. package/dist/components/table/LayoutToggle.d.ts +7 -0
  157. package/dist/components/table/LayoutToggle.js +9 -0
  158. package/dist/components/table/TableActionsDropdown.d.ts +13 -0
  159. package/dist/components/table/TableActionsDropdown.js +46 -0
  160. package/dist/components/table/TableBulkActions.d.ts +11 -0
  161. package/dist/components/table/TableBulkActions.js +21 -0
  162. package/dist/components/table/TableHeader.d.ts +14 -0
  163. package/dist/components/table/TableHeader.js +23 -0
  164. package/dist/components/table/TablePagination.d.ts +13 -0
  165. package/dist/components/table/TablePagination.js +55 -0
  166. package/dist/components/table/TableRow.d.ts +21 -0
  167. package/dist/components/table/TableRow.js +32 -0
  168. package/dist/components/table/TableSearchBar.d.ts +11 -0
  169. package/dist/components/table/TableSearchBar.js +12 -0
  170. package/dist/components/table/TableTabs.d.ts +14 -0
  171. package/dist/components/table/TableTabs.js +8 -0
  172. package/dist/components/ui/Badge.d.ts +6 -0
  173. package/dist/components/ui/Badge.js +12 -0
  174. package/dist/components/ui/Button.d.ts +22 -0
  175. package/dist/components/ui/Button.js +22 -0
  176. package/dist/components/ui/Card.d.ts +7 -0
  177. package/dist/components/ui/Card.js +5 -0
  178. package/dist/components/ui/ConfirmDialog.d.ts +19 -0
  179. package/dist/components/ui/ConfirmDialog.js +45 -0
  180. package/dist/components/ui/EmptyState.d.ts +9 -0
  181. package/dist/components/ui/EmptyState.js +6 -0
  182. package/dist/components/ui/ErrorAlert.d.ts +7 -0
  183. package/dist/components/ui/ErrorAlert.js +9 -0
  184. package/dist/components/ui/Input.d.ts +11 -0
  185. package/dist/components/ui/Input.js +10 -0
  186. package/dist/components/ui/Label.d.ts +5 -0
  187. package/dist/components/ui/Label.js +5 -0
  188. package/dist/components/ui/PillButton.d.ts +14 -0
  189. package/dist/components/ui/PillButton.js +19 -0
  190. package/dist/components/ui/Select.d.ts +7 -0
  191. package/dist/components/ui/Select.js +7 -0
  192. package/dist/components/ui/Spinner.d.ts +8 -0
  193. package/dist/components/ui/Spinner.js +14 -0
  194. package/dist/components/ui/Toast.d.ts +21 -0
  195. package/dist/components/ui/Toast.js +47 -0
  196. package/dist/components/ui/index.d.ts +24 -0
  197. package/dist/components/ui/index.js +12 -0
  198. package/dist/components/utils/HintDisplay.d.ts +11 -0
  199. package/dist/components/utils/HintDisplay.js +12 -0
  200. package/dist/components/utils/Icon.d.ts +22 -0
  201. package/dist/components/utils/Icon.js +22 -0
  202. package/dist/components/utils/MediaPreviewModal.d.ts +14 -0
  203. package/dist/components/utils/MediaPreviewModal.js +32 -0
  204. package/dist/components/utils/ViewFieldWrapper.d.ts +11 -0
  205. package/dist/components/utils/ViewFieldWrapper.js +9 -0
  206. package/dist/components/utils/layoutHelpers.d.ts +19 -0
  207. package/dist/components/utils/layoutHelpers.js +257 -0
  208. package/dist/components/widgets/ChartWidget.d.ts +16 -0
  209. package/dist/components/widgets/ChartWidget.js +192 -0
  210. package/dist/components/widgets/StatsWidget.d.ts +16 -0
  211. package/dist/components/widgets/StatsWidget.js +39 -0
  212. package/dist/components/widgets/WidgetRenderer.d.ts +10 -0
  213. package/dist/components/widgets/WidgetRenderer.js +50 -0
  214. package/dist/components/widgets/WidgetShell.d.ts +9 -0
  215. package/dist/components/widgets/WidgetShell.js +7 -0
  216. package/dist/contexts/AuthChallengeRegistryContext.d.ts +15 -0
  217. package/dist/contexts/AuthChallengeRegistryContext.js +15 -0
  218. package/dist/contexts/BlockRegistryContext.d.ts +18 -0
  219. package/dist/contexts/BlockRegistryContext.js +8 -0
  220. package/dist/contexts/ColumnRegistryContext.d.ts +8 -0
  221. package/dist/contexts/ColumnRegistryContext.js +30 -0
  222. package/dist/contexts/FieldRegistryContext.d.ts +13 -0
  223. package/dist/contexts/FieldRegistryContext.js +46 -0
  224. package/dist/contexts/PanelMetadataContext.d.ts +26 -0
  225. package/dist/contexts/PanelMetadataContext.js +26 -0
  226. package/dist/contexts/PanelProviders.d.ts +27 -0
  227. package/dist/contexts/PanelProviders.js +24 -0
  228. package/dist/contexts/ResourceModalContext.d.ts +26 -0
  229. package/dist/contexts/ResourceModalContext.js +76 -0
  230. package/dist/contexts/SlotRegistryContext.d.ts +19 -0
  231. package/dist/contexts/SlotRegistryContext.js +24 -0
  232. package/dist/contexts/TableRefreshContext.d.ts +10 -0
  233. package/dist/contexts/TableRefreshContext.js +30 -0
  234. package/dist/contexts/WidgetRegistryContext.d.ts +17 -0
  235. package/dist/contexts/WidgetRegistryContext.js +14 -0
  236. package/dist/contexts/createRegistryContext.d.ts +19 -0
  237. package/dist/contexts/createRegistryContext.js +20 -0
  238. package/dist/hooks/useAfterStateUpdated.d.ts +6 -0
  239. package/dist/hooks/useAfterStateUpdated.js +62 -0
  240. package/dist/hooks/useValidation.d.ts +26 -0
  241. package/dist/hooks/useValidation.js +76 -0
  242. package/dist/i18n/I18nProvider.d.ts +27 -0
  243. package/dist/i18n/I18nProvider.js +101 -0
  244. package/dist/i18n/LocaleSwitcher.d.ts +10 -0
  245. package/dist/i18n/LocaleSwitcher.js +30 -0
  246. package/dist/i18n/activeLocale.d.ts +11 -0
  247. package/dist/i18n/activeLocale.js +34 -0
  248. package/dist/i18n/buildClientI18n.d.ts +28 -0
  249. package/dist/i18n/buildClientI18n.js +67 -0
  250. package/dist/i18n/index.d.ts +11 -0
  251. package/dist/i18n/index.js +9 -0
  252. package/dist/i18n/locales/core/en.d.ts +225 -0
  253. package/dist/i18n/locales/core/en.js +252 -0
  254. package/dist/i18n/locales/core/index.d.ts +2 -0
  255. package/dist/i18n/locales/core/index.js +4 -0
  256. package/dist/i18n/locales/core/sq.d.ts +253 -0
  257. package/dist/i18n/locales/core/sq.js +255 -0
  258. package/dist/i18n/useFormatter.d.ts +18 -0
  259. package/dist/i18n/useFormatter.js +37 -0
  260. package/dist/i18n/useLocale.d.ts +11 -0
  261. package/dist/i18n/useLocale.js +11 -0
  262. package/dist/i18n/useTranslation.d.ts +12 -0
  263. package/dist/i18n/useTranslation.js +12 -0
  264. package/dist/index.d.ts +106 -0
  265. package/dist/index.js +101 -0
  266. package/dist/pages/ResourceListPage.d.ts +8 -0
  267. package/dist/pages/ResourceListPage.js +139 -0
  268. package/dist/plugin.d.ts +79 -0
  269. package/dist/plugin.js +34 -0
  270. package/dist/runtime/conditions.d.ts +35 -0
  271. package/dist/runtime/conditions.js +97 -0
  272. package/dist/runtime/formTraversal.d.ts +25 -0
  273. package/dist/runtime/formTraversal.js +37 -0
  274. package/dist/runtime/serializedFunctions.d.ts +41 -0
  275. package/dist/runtime/serializedFunctions.js +264 -0
  276. package/dist/slots/Slot.d.ts +24 -0
  277. package/dist/slots/Slot.js +29 -0
  278. package/dist/slots/SlotCluster.d.ts +22 -0
  279. package/dist/slots/SlotCluster.js +49 -0
  280. package/dist/slots/index.d.ts +7 -0
  281. package/dist/slots/index.js +4 -0
  282. package/dist/slots/mergeSlots.d.ts +18 -0
  283. package/dist/slots/mergeSlots.js +35 -0
  284. package/dist/slots/types.d.ts +87 -0
  285. package/dist/slots/types.js +30 -0
  286. package/dist/styles.css +1 -0
  287. package/dist/table/TableContext.d.ts +36 -0
  288. package/dist/table/TableContext.js +13 -0
  289. package/dist/table/TableRenderer.d.ts +29 -0
  290. package/dist/table/TableRenderer.js +159 -0
  291. package/dist/table/components/FiltersPanel.d.ts +11 -0
  292. package/dist/table/components/FiltersPanel.js +52 -0
  293. package/dist/table/components/TableToolbar.d.ts +28 -0
  294. package/dist/table/components/TableToolbar.js +27 -0
  295. package/dist/table/components/TableToolbarButton.d.ts +6 -0
  296. package/dist/table/components/TableToolbarButton.js +9 -0
  297. package/dist/table/components/TableView.d.ts +12 -0
  298. package/dist/table/components/TableView.js +21 -0
  299. package/dist/table/defaultRowActions.d.ts +21 -0
  300. package/dist/table/defaultRowActions.js +37 -0
  301. package/dist/table/hooks/useColumnVisibility.d.ts +13 -0
  302. package/dist/table/hooks/useColumnVisibility.js +59 -0
  303. package/dist/table/hooks/useEditableRows.d.ts +22 -0
  304. package/dist/table/hooks/useEditableRows.js +63 -0
  305. package/dist/table/hooks/useTableActions.d.ts +54 -0
  306. package/dist/table/hooks/useTableActions.js +313 -0
  307. package/dist/table/hooks/useTableData.d.ts +28 -0
  308. package/dist/table/hooks/useTableData.js +63 -0
  309. package/dist/table/hooks/useTableLayout.d.ts +12 -0
  310. package/dist/table/hooks/useTableLayout.js +31 -0
  311. package/dist/table/hooks/useTableQuery.d.ts +29 -0
  312. package/dist/table/hooks/useTableQuery.js +135 -0
  313. package/dist/types/index.d.ts +224 -0
  314. package/dist/types/index.js +6 -0
  315. package/dist/utils/classNames.d.ts +7 -0
  316. package/dist/utils/classNames.js +9 -0
  317. package/dist/utils/columnMediaDimensions.d.ts +13 -0
  318. package/dist/utils/columnMediaDimensions.js +29 -0
  319. package/dist/utils/columnVisibilityStorage.d.ts +22 -0
  320. package/dist/utils/columnVisibilityStorage.js +56 -0
  321. package/dist/utils/fieldErrors.d.ts +13 -0
  322. package/dist/utils/fieldErrors.js +25 -0
  323. package/dist/utils/formatValue.d.ts +28 -0
  324. package/dist/utils/formatValue.js +109 -0
  325. package/dist/utils/layoutStorage.d.ts +23 -0
  326. package/dist/utils/layoutStorage.js +53 -0
  327. package/dist/utils/redirectHandler.d.ts +7 -0
  328. package/dist/utils/redirectHandler.js +25 -0
  329. package/dist/utils/tableFormatters.d.ts +14 -0
  330. package/dist/utils/tableFormatters.js +93 -0
  331. package/dist/utils/widgetVisibilityStorage.d.ts +11 -0
  332. package/dist/utils/widgetVisibilityStorage.js +39 -0
  333. package/package.json +101 -0
  334. package/src/FieldRenderer.test.tsx +44 -0
  335. package/src/FieldRenderer.tsx +104 -0
  336. package/src/FormRenderer.containers.test.tsx +121 -0
  337. package/src/FormRenderer.test.tsx +174 -0
  338. package/src/FormRenderer.tsx +140 -0
  339. package/src/TableRenderer.tsx +2 -0
  340. package/src/api/actionsApi.ts +76 -0
  341. package/src/api/authenticatedFetch.ts +40 -0
  342. package/src/api/exportApi.ts +66 -0
  343. package/src/api/http.test.ts +58 -0
  344. package/src/api/http.ts +68 -0
  345. package/src/api/resourceApi.ts +88 -0
  346. package/src/api/tableApi.test.ts +108 -0
  347. package/src/api/tableApi.ts +107 -0
  348. package/src/api/urls.ts +50 -0
  349. package/src/app.test.tsx +67 -0
  350. package/src/app.tsx +181 -0
  351. package/src/auth/AuthContext.tsx +188 -0
  352. package/src/auth/LoginPage.tsx +380 -0
  353. package/src/auth/ProtectedRoute.tsx +39 -0
  354. package/src/auth/authApiClient.ts +109 -0
  355. package/src/auth/authFlow.test.tsx +168 -0
  356. package/src/auth/types.ts +104 -0
  357. package/src/components/ActionFormModal.tsx +45 -0
  358. package/src/components/AdminPanel.tsx +368 -0
  359. package/src/components/Checkbox.tsx +59 -0
  360. package/src/components/CheckboxField.tsx +88 -0
  361. package/src/components/ColorPickerField.tsx +93 -0
  362. package/src/components/DateTimePickerField.tsx +112 -0
  363. package/src/components/FileUploadField.tsx +841 -0
  364. package/src/components/GlobalSearch.tsx +436 -0
  365. package/src/components/GroupField.tsx +85 -0
  366. package/src/components/HiddenField.tsx +14 -0
  367. package/src/components/ModalBreadcrumb.tsx +74 -0
  368. package/src/components/ModalDrawer.tsx +137 -0
  369. package/src/components/RadioField.tsx +80 -0
  370. package/src/components/RepeaterField.tsx +546 -0
  371. package/src/components/ResourceModalRenderer.tsx +144 -0
  372. package/src/components/RichEditorField.tsx +942 -0
  373. package/src/components/SectionField.tsx +242 -0
  374. package/src/components/SelectField.tsx +843 -0
  375. package/src/components/TabsField.test.tsx +151 -0
  376. package/src/components/TabsField.tsx +386 -0
  377. package/src/components/TagsInputField.tsx +411 -0
  378. package/src/components/TextInputField.tsx +91 -0
  379. package/src/components/TextareaField.tsx +110 -0
  380. package/src/components/ToggleField.tsx +126 -0
  381. package/src/components/ViewModal.tsx +353 -0
  382. package/src/components/blocks/BlockRenderer.tsx +56 -0
  383. package/src/components/blocks/FormBlockRenderer.tsx +160 -0
  384. package/src/components/blocks/TableBlockRenderer.tsx +33 -0
  385. package/src/components/blocks/TabsBlockRenderer.tsx +49 -0
  386. package/src/components/blocks/WidgetBlockRenderer.tsx +19 -0
  387. package/src/components/columns/CheckboxColumnComponent.tsx +38 -0
  388. package/src/components/columns/ColorColumnComponent.tsx +23 -0
  389. package/src/components/columns/CustomColumn.test.tsx +55 -0
  390. package/src/components/columns/DeeplinkWrapper.tsx +103 -0
  391. package/src/components/columns/IconColumnComponent.tsx +55 -0
  392. package/src/components/columns/ImageColumnComponent.tsx +220 -0
  393. package/src/components/columns/MediaColumnComponent.tsx +294 -0
  394. package/src/components/columns/SelectColumnComponent.tsx +49 -0
  395. package/src/components/columns/TagsColumnComponent.tsx +46 -0
  396. package/src/components/columns/TextColumnComponent.tsx +191 -0
  397. package/src/components/columns/TextInputColumnComponent.tsx +35 -0
  398. package/src/components/columns/ToggleColumnComponent.tsx +56 -0
  399. package/src/components/columns/VideoColumnComponent.tsx +236 -0
  400. package/src/components/columns/ViewColumnComponent.tsx +9 -0
  401. package/src/components/errors/ErrorBoundary.tsx +58 -0
  402. package/src/components/filters/CustomFilterComponent.tsx +130 -0
  403. package/src/components/filters/DateFilterComponent.tsx +272 -0
  404. package/src/components/filters/QueryBuilderFilterComponent.tsx +502 -0
  405. package/src/components/layout/Header.tsx +212 -0
  406. package/src/components/layout/PanelBrandMark.tsx +61 -0
  407. package/src/components/layout/Sidebar.tsx +283 -0
  408. package/src/components/modals/RelationCreateModal.tsx +107 -0
  409. package/src/components/modals/ResourceFormModal.test.tsx +119 -0
  410. package/src/components/modals/ResourceFormModal.tsx +128 -0
  411. package/src/components/modals/useResourceForm.ts +207 -0
  412. package/src/components/modals/view/RecordActions.tsx +69 -0
  413. package/src/components/modals/view/RecordDetails.tsx +60 -0
  414. package/src/components/modals/view/RelationPanel.tsx +76 -0
  415. package/src/components/modals/view/RelationTabs.tsx +145 -0
  416. package/src/components/modals/view/useRecordView.ts +134 -0
  417. package/src/components/pages/PageRenderer.tsx +173 -0
  418. package/src/components/table/ColumnTogglePopup.tsx +85 -0
  419. package/src/components/table/GridCard.tsx +155 -0
  420. package/src/components/table/GridView.tsx +138 -0
  421. package/src/components/table/LayoutToggle.tsx +24 -0
  422. package/src/components/table/TableActionsDropdown.tsx +114 -0
  423. package/src/components/table/TableBulkActions.tsx +65 -0
  424. package/src/components/table/TableHeader.tsx +96 -0
  425. package/src/components/table/TablePagination.tsx +169 -0
  426. package/src/components/table/TableRow.tsx +155 -0
  427. package/src/components/table/TableSearchBar.tsx +66 -0
  428. package/src/components/table/TableTabs.tsx +49 -0
  429. package/src/components/ui/Badge.tsx +30 -0
  430. package/src/components/ui/Button.test.tsx +78 -0
  431. package/src/components/ui/Button.tsx +102 -0
  432. package/src/components/ui/Card.tsx +23 -0
  433. package/src/components/ui/ConfirmDialog.tsx +112 -0
  434. package/src/components/ui/EmptyState.tsx +24 -0
  435. package/src/components/ui/ErrorAlert.tsx +37 -0
  436. package/src/components/ui/Input.tsx +48 -0
  437. package/src/components/ui/Label.tsx +15 -0
  438. package/src/components/ui/PillButton.tsx +72 -0
  439. package/src/components/ui/Select.tsx +33 -0
  440. package/src/components/ui/Spinner.tsx +39 -0
  441. package/src/components/ui/Toast.tsx +105 -0
  442. package/src/components/ui/index.ts +24 -0
  443. package/src/components/utils/HintDisplay.tsx +26 -0
  444. package/src/components/utils/Icon.tsx +36 -0
  445. package/src/components/utils/MediaPreviewModal.tsx +114 -0
  446. package/src/components/utils/ViewFieldWrapper.tsx +23 -0
  447. package/src/components/utils/layoutHelpers.ts +267 -0
  448. package/src/components/widgets/ChartWidget.tsx +247 -0
  449. package/src/components/widgets/StatsWidget.tsx +72 -0
  450. package/src/components/widgets/WidgetRenderer.tsx +108 -0
  451. package/src/components/widgets/WidgetShell.tsx +37 -0
  452. package/src/contexts/AuthChallengeRegistryContext.tsx +29 -0
  453. package/src/contexts/BlockRegistryContext.tsx +28 -0
  454. package/src/contexts/ColumnRegistryContext.tsx +38 -0
  455. package/src/contexts/FieldRegistryContext.tsx +56 -0
  456. package/src/contexts/PanelMetadataContext.tsx +60 -0
  457. package/src/contexts/PanelProviders.tsx +85 -0
  458. package/src/contexts/ResourceModalContext.tsx +137 -0
  459. package/src/contexts/SlotRegistryContext.tsx +35 -0
  460. package/src/contexts/TableRefreshContext.tsx +44 -0
  461. package/src/contexts/WidgetRegistryContext.tsx +34 -0
  462. package/src/contexts/createRegistryContext.tsx +29 -0
  463. package/src/hooks/useAfterStateUpdated.ts +70 -0
  464. package/src/hooks/useValidation.test.ts +59 -0
  465. package/src/hooks/useValidation.ts +95 -0
  466. package/src/i18n/I18nProvider.tsx +128 -0
  467. package/src/i18n/LocaleSwitcher.tsx +50 -0
  468. package/src/i18n/activeLocale.ts +39 -0
  469. package/src/i18n/buildClientI18n.ts +101 -0
  470. package/src/i18n/i18n.test.tsx +140 -0
  471. package/src/i18n/index.ts +12 -0
  472. package/src/i18n/locales/core/en.ts +274 -0
  473. package/src/i18n/locales/core/index.ts +5 -0
  474. package/src/i18n/locales/core/sq.ts +275 -0
  475. package/src/i18n/useFormatter.ts +42 -0
  476. package/src/i18n/useLocale.ts +16 -0
  477. package/src/i18n/useTranslation.ts +17 -0
  478. package/src/index.ts +244 -0
  479. package/src/pages/ResourceListPage.tsx +205 -0
  480. package/src/plugin.ts +110 -0
  481. package/src/runtime/conditions.test.ts +99 -0
  482. package/src/runtime/conditions.ts +148 -0
  483. package/src/runtime/formTraversal.ts +41 -0
  484. package/src/runtime/serializedFunctions.test.ts +59 -0
  485. package/src/runtime/serializedFunctions.ts +284 -0
  486. package/src/slots/Slot.test.tsx +89 -0
  487. package/src/slots/Slot.tsx +47 -0
  488. package/src/slots/SlotCluster.test.tsx +95 -0
  489. package/src/slots/SlotCluster.tsx +107 -0
  490. package/src/slots/index.ts +15 -0
  491. package/src/slots/mergeSlots.test.ts +71 -0
  492. package/src/slots/mergeSlots.ts +40 -0
  493. package/src/slots/slotNames.test.ts +21 -0
  494. package/src/slots/types.ts +119 -0
  495. package/src/styles.css +437 -0
  496. package/src/table/TableContext.tsx +41 -0
  497. package/src/table/TableRenderer.test.tsx +197 -0
  498. package/src/table/TableRenderer.tsx +390 -0
  499. package/src/table/components/FiltersPanel.tsx +193 -0
  500. package/src/table/components/TableToolbar.tsx +153 -0
  501. package/src/table/components/TableToolbarButton.tsx +14 -0
  502. package/src/table/components/TableView.tsx +106 -0
  503. package/src/table/defaultRowActions.ts +43 -0
  504. package/src/table/hooks/useColumnVisibility.test.ts +51 -0
  505. package/src/table/hooks/useColumnVisibility.ts +71 -0
  506. package/src/table/hooks/useEditableRows.test.ts +69 -0
  507. package/src/table/hooks/useEditableRows.ts +89 -0
  508. package/src/table/hooks/useTableActions.ts +393 -0
  509. package/src/table/hooks/useTableData.ts +89 -0
  510. package/src/table/hooks/useTableLayout.ts +45 -0
  511. package/src/table/hooks/useTableQuery.test.ts +116 -0
  512. package/src/table/hooks/useTableQuery.ts +172 -0
  513. package/src/test/mockFetch.ts +67 -0
  514. package/src/test/setup.ts +25 -0
  515. package/src/types/index.ts +228 -0
  516. package/src/utils/classNames.ts +10 -0
  517. package/src/utils/columnMediaDimensions.ts +45 -0
  518. package/src/utils/columnVisibilityStorage.ts +55 -0
  519. package/src/utils/fieldErrors.test.ts +35 -0
  520. package/src/utils/fieldErrors.ts +27 -0
  521. package/src/utils/formatValue.test.tsx +65 -0
  522. package/src/utils/formatValue.tsx +117 -0
  523. package/src/utils/layoutStorage.ts +52 -0
  524. package/src/utils/redirectHandler.ts +29 -0
  525. package/src/utils/tableFormatters.test.ts +54 -0
  526. package/src/utils/tableFormatters.ts +104 -0
  527. package/src/utils/widgetVisibilityStorage.ts +38 -0
  528. package/tailwind.config.js +9 -0
  529. package/vite.config.ts +17 -0
@@ -0,0 +1,7 @@
1
+ import { jsxs as _jsxs } from "react/jsx-runtime";
2
+ export function ViewColumnComponent({ column, record }) {
3
+ // ViewColumn is for custom rendering - in a real implementation,
4
+ // this would load a custom view/template
5
+ // For now, just display a placeholder
6
+ return _jsxs("span", { className: "text-fg-secondary italic", children: ["Custom view: ", column.view] });
7
+ }
@@ -0,0 +1,23 @@
1
+ import React from 'react';
2
+ export interface ErrorBoundaryProps {
3
+ children: React.ReactNode;
4
+ /** Custom fallback; receives the error and a reset callback */
5
+ fallback?: (error: Error, reset: () => void) => React.ReactNode;
6
+ /** Short label for the failed area, shown in the default fallback */
7
+ label?: string;
8
+ }
9
+ interface ErrorBoundaryState {
10
+ error: Error | null;
11
+ }
12
+ /**
13
+ * Generic error boundary so a crash in one route, modal or widget
14
+ * doesn't take down the whole admin panel.
15
+ */
16
+ export declare class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
17
+ state: ErrorBoundaryState;
18
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState;
19
+ componentDidCatch(error: Error, info: React.ErrorInfo): void;
20
+ reset: () => void;
21
+ render(): string | number | bigint | boolean | Iterable<React.ReactNode> | Promise<string | number | bigint | boolean | React.ReactPortal | React.ReactElement<unknown, string | React.JSXElementConstructor<any>> | Iterable<React.ReactNode>> | React.JSX.Element;
22
+ }
23
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from 'react';
3
+ import { PillButton } from '../ui/PillButton';
4
+ import { translate } from '../../i18n/activeLocale';
5
+ /**
6
+ * Generic error boundary so a crash in one route, modal or widget
7
+ * doesn't take down the whole admin panel.
8
+ */
9
+ export class ErrorBoundary extends React.Component {
10
+ constructor() {
11
+ super(...arguments);
12
+ this.state = { error: null };
13
+ this.reset = () => {
14
+ this.setState({ error: null });
15
+ };
16
+ }
17
+ static getDerivedStateFromError(error) {
18
+ return { error };
19
+ }
20
+ componentDidCatch(error, info) {
21
+ console.error('KratosJS error boundary caught:', error, info.componentStack);
22
+ }
23
+ render() {
24
+ const { error } = this.state;
25
+ if (!error) {
26
+ return this.props.children;
27
+ }
28
+ if (this.props.fallback) {
29
+ return this.props.fallback(error, this.reset);
30
+ }
31
+ return (_jsxs("div", { className: "m-4 rounded-lg border border-danger/30 bg-danger-soft p-4", children: [_jsx("p", { className: "text-sm font-semibold text-danger", children: this.props.label ? `Something went wrong in ${this.props.label}` : 'Something went wrong' }), _jsx("p", { className: "mt-1 text-sm k-text-secondary break-words", children: error.message }), _jsx(PillButton, { type: "button", variant: "default", onClick: this.reset, className: "mt-3", children: translate('core:common.try_again') })] }));
32
+ }
33
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import { SerializedFilter } from '@maxal_studio/kratosjs';
3
+ interface CustomFilterProps {
4
+ filter: SerializedFilter;
5
+ value?: string[];
6
+ onChange: (value: string[] | undefined) => void;
7
+ embedded?: boolean;
8
+ }
9
+ export declare function CustomFilterComponent({ filter, value, onChange, embedded }: CustomFilterProps): React.JSX.Element;
10
+ export {};
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { cn } from '../../utils/classNames';
4
+ import { PillButton } from '../ui/PillButton';
5
+ import { translate } from '../../i18n/activeLocale';
6
+ export function CustomFilterComponent({ filter, value, onChange, embedded = false }) {
7
+ const [isOpen, setIsOpen] = useState(false);
8
+ const [localSelectedValues, setLocalSelectedValues] = useState([]);
9
+ const appliedValues = Array.isArray(value) ? value : value ? [value] : [];
10
+ const options = filter.componentProps?.options || [];
11
+ const toggleOption = (option) => {
12
+ const newValues = localSelectedValues.includes(option)
13
+ ? localSelectedValues.filter(v => v !== option)
14
+ : [...localSelectedValues, option];
15
+ setLocalSelectedValues(newValues);
16
+ };
17
+ const clearAll = () => {
18
+ setLocalSelectedValues([]);
19
+ onChange(undefined);
20
+ setIsOpen(false);
21
+ };
22
+ const applyChanges = () => {
23
+ onChange(localSelectedValues.length > 0 ? localSelectedValues : undefined);
24
+ setIsOpen(false);
25
+ };
26
+ const handleOpen = () => {
27
+ setLocalSelectedValues(appliedValues);
28
+ setIsOpen(embedded ? !isOpen : true);
29
+ };
30
+ return (_jsxs("div", { className: "relative", children: [_jsx("button", { onClick: handleOpen, type: "button", className: cn('w-full h-10 px-3 pr-10 text-sm text-left rounded-lg border transition-colors', 'bg-input text-fg border-border', 'focus:outline-none focus:ring-2 focus:ring-ring focus:border-accent', appliedValues.length > 0 && 'ring-2 ring-ring border-accent'), children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "truncate", children: appliedValues.length > 0 ? appliedValues.map(v => v).join(', ') : 'Select...' }), _jsxs("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1", children: [appliedValues.length > 0 && (_jsx("span", { className: "inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 text-xs font-bold text-white bg-accent rounded-full", children: appliedValues.length })), _jsx("svg", { className: "w-4 h-4 text-fg-secondary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] })] }) }), isOpen && (_jsxs(_Fragment, { children: [!embedded && _jsx("div", { className: "fixed inset-0 z-[60]", onClick: () => setIsOpen(false) }), _jsxs("div", { className: cn('flex max-h-[400px] flex-col overflow-hidden border border-border bg-surface', embedded
31
+ ? 'mt-2 rounded-xl'
32
+ : 'absolute left-0 right-0 top-full z-[70] mt-2 rounded-lg shadow-xl'), children: [_jsxs("div", { className: "p-2 overflow-y-auto flex-1", children: [options.map((option) => (_jsxs("label", { className: cn('flex items-center gap-3 px-3 py-2 rounded-md cursor-pointer transition-colors', 'hover:bg-hover', localSelectedValues.includes(option) && 'bg-muted'), children: [_jsx("input", { type: "checkbox", checked: localSelectedValues.includes(option), onChange: () => toggleOption(option), className: "w-4 h-4 rounded border-gray-300 dark:border-gray-600 text-accent focus:ring-ring" }), _jsx("span", { className: "text-sm text-fg capitalize", children: option })] }, option))), options.length === 0 && (_jsx("div", { className: "px-3 py-4 text-center text-sm text-fg-secondary", children: translate('core:filters.no_options') }))] }), _jsxs("div", { className: "flex items-center justify-between p-2 border-t border-border flex-shrink-0", children: [_jsx("button", { onClick: clearAll, type: "button", className: "text-xs font-medium text-fg-secondary hover:text-fg", disabled: localSelectedValues.length === 0, children: translate('core:common.clear') }), _jsx(PillButton, { type: "button", variant: "primary", onClick: applyChanges, children: translate('core:common.apply') })] })] })] }))] }));
33
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import { SerializedFilter } from '@maxal_studio/kratosjs';
3
+ /** Date filter value sent to API: always from/to (YYYY-MM-DD) */
4
+ export type DateFilterValue = undefined | {
5
+ from?: string;
6
+ to?: string;
7
+ };
8
+ interface DateFilterProps {
9
+ filter: SerializedFilter;
10
+ value?: DateFilterValue;
11
+ onChange: (value: DateFilterValue) => void;
12
+ embedded?: boolean;
13
+ }
14
+ export declare function DateFilterComponent({ filter, value, onChange, embedded }: DateFilterProps): React.JSX.Element;
15
+ export {};
@@ -0,0 +1,132 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { cn } from '../../utils/classNames';
4
+ import { PillButton } from '../ui/PillButton';
5
+ import { translate } from '../../i18n/activeLocale';
6
+ // Labels are catalog keys (not literals): translating here at module load would
7
+ // freeze them to the default locale, so they're resolved at render time instead.
8
+ const PRESET_OPTIONS = [
9
+ { value: 'today', labelKey: 'core:filters.preset_today' },
10
+ { value: 'yesterday', labelKey: 'core:filters.preset_yesterday' },
11
+ { value: 'this_week', labelKey: 'core:filters.preset_this_week' },
12
+ { value: 'this_month', labelKey: 'core:filters.preset_this_month' },
13
+ { value: 'this_year', labelKey: 'core:filters.preset_this_year' },
14
+ { value: 'custom', labelKey: 'core:filters.preset_custom' },
15
+ ];
16
+ const pad = (n) => String(n).padStart(2, '0');
17
+ const toDateStr = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
18
+ /**
19
+ * Compute from/to (YYYY-MM-DD) for a preset using the user's local time.
20
+ */
21
+ function getDateRangeForPreset(preset) {
22
+ const now = new Date();
23
+ switch (preset) {
24
+ case 'today': {
25
+ const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
26
+ const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
27
+ return { from: toDateStr(start), to: toDateStr(end) };
28
+ }
29
+ case 'yesterday': {
30
+ const y = new Date(now);
31
+ y.setDate(y.getDate() - 1);
32
+ const start = new Date(y.getFullYear(), y.getMonth(), y.getDate());
33
+ const end = new Date(y.getFullYear(), y.getMonth(), y.getDate(), 23, 59, 59, 999);
34
+ return { from: toDateStr(start), to: toDateStr(end) };
35
+ }
36
+ case 'this_week': {
37
+ const day = now.getDay();
38
+ const diff = day === 0 ? -6 : 1 - day;
39
+ const monday = new Date(now);
40
+ monday.setDate(monday.getDate() + diff);
41
+ const start = new Date(monday.getFullYear(), monday.getMonth(), monday.getDate());
42
+ const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
43
+ return { from: toDateStr(start), to: toDateStr(end) };
44
+ }
45
+ case 'this_month': {
46
+ const start = new Date(now.getFullYear(), now.getMonth(), 1);
47
+ const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
48
+ return { from: toDateStr(start), to: toDateStr(end) };
49
+ }
50
+ case 'this_year': {
51
+ const start = new Date(now.getFullYear(), 0, 1);
52
+ const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
53
+ return { from: toDateStr(start), to: toDateStr(end) };
54
+ }
55
+ default:
56
+ return null;
57
+ }
58
+ }
59
+ /** Infer which preset matches the current from/to (for display and reopening popup). */
60
+ function inferPresetFromValue(value) {
61
+ if (!value || (typeof value === 'object' && !value.from && !value.to))
62
+ return 'custom';
63
+ const from = value.from;
64
+ const to = value.to;
65
+ if (!from && !to)
66
+ return 'custom';
67
+ for (const preset of ['today', 'yesterday', 'this_week', 'this_month', 'this_year']) {
68
+ const range = getDateRangeForPreset(preset);
69
+ if (range && range.from === from && range.to === to)
70
+ return preset;
71
+ }
72
+ return 'custom';
73
+ }
74
+ function getPresetLabel(preset) {
75
+ const opt = PRESET_OPTIONS.find(o => o.value === preset);
76
+ return opt ? translate(opt.labelKey) : preset;
77
+ }
78
+ export function DateFilterComponent({ filter, value, onChange, embedded = false }) {
79
+ const [isOpen, setIsOpen] = useState(false);
80
+ const inferredPreset = value && (value.from || value.to) ? inferPresetFromValue(value) : 'custom';
81
+ const customFrom = (value && value.from) || '';
82
+ const customTo = (value && value.to) || '';
83
+ const [localPreset, setLocalPreset] = useState(inferredPreset);
84
+ const [localFromDate, setLocalFromDate] = useState(customFrom);
85
+ const [localToDate, setLocalToDate] = useState(customTo);
86
+ const hasValue = value !== undefined && !!(value.from || value.to);
87
+ const formatDisplay = () => {
88
+ if (!value || typeof value !== 'object')
89
+ return '';
90
+ const from = value.from;
91
+ const to = value.to;
92
+ if (from && to)
93
+ return `${from} – ${to}`;
94
+ if (from)
95
+ return translate('core:filters.from_value', { date: from });
96
+ if (to)
97
+ return translate('core:filters.until_value', { date: to });
98
+ return '';
99
+ };
100
+ const handleOpen = () => {
101
+ setLocalPreset(inferredPreset);
102
+ setLocalFromDate(customFrom);
103
+ setLocalToDate(customTo);
104
+ setIsOpen(embedded ? !isOpen : true);
105
+ };
106
+ const clearFilter = () => {
107
+ setLocalPreset('custom');
108
+ setLocalFromDate('');
109
+ setLocalToDate('');
110
+ onChange(undefined);
111
+ setIsOpen(false);
112
+ };
113
+ const applyChanges = () => {
114
+ if (localPreset !== 'custom') {
115
+ const range = getDateRangeForPreset(localPreset);
116
+ onChange(range ? { from: range.from, to: range.to } : undefined);
117
+ }
118
+ else {
119
+ const from = localFromDate || undefined;
120
+ const to = localToDate || undefined;
121
+ onChange(from || to ? { from, to } : undefined);
122
+ }
123
+ setIsOpen(false);
124
+ };
125
+ return (_jsxs("div", { className: "space-y-1.5", children: [_jsx("label", { className: "block text-sm font-medium text-fg", children: filter.label || filter.name }), _jsxs("div", { className: "relative", children: [_jsx("button", { onClick: handleOpen, type: "button", className: cn('w-full h-10 px-3 pr-10 text-sm text-left rounded-lg border transition-colors', 'bg-input text-fg border-border', 'focus:outline-none focus:ring-2 focus:ring-ring focus:border-accent', hasValue && 'ring-2 ring-ring border-accent'), children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "truncate", children: hasValue
126
+ ? inferredPreset !== 'custom'
127
+ ? getPresetLabel(inferredPreset)
128
+ : formatDisplay()
129
+ : filter.placeholder || translate('core:filters.select_range') }), _jsxs("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1", children: [hasValue && (_jsx("span", { className: "inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 text-xs font-bold text-white bg-accent rounded-full", children: "1" })), _jsx("svg", { className: "w-4 h-4 text-fg-secondary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] })] }) }), isOpen && (_jsxs(_Fragment, { children: [!embedded && _jsx("div", { className: "fixed inset-0 z-[60]", onClick: () => setIsOpen(false) }), _jsxs("div", { className: cn('flex flex-col overflow-hidden border border-border bg-surface', embedded
130
+ ? 'mt-2 rounded-xl'
131
+ : 'fixed inset-x-4 top-1/2 z-[70] mx-auto max-w-sm -translate-y-1/2 sm:absolute sm:inset-x-0 sm:top-full sm:mt-2 sm:max-w-none sm:translate-y-0 rounded-lg shadow-xl'), children: [_jsxs("div", { className: "p-4 space-y-3", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-xs font-medium text-fg-secondary mb-1.5", children: translate('core:filters.range') }), _jsx("select", { value: localPreset, onChange: e => setLocalPreset(e.target.value), className: cn('w-full h-10 px-3 text-sm rounded-lg border transition-colors', 'bg-input text-fg border-border', 'focus:outline-none focus:ring-2 focus:ring-ring focus:border-accent'), children: PRESET_OPTIONS.map(opt => (_jsx("option", { value: opt.value, children: translate(opt.labelKey) }, opt.value))) })] }), localPreset === 'custom' && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative", children: [_jsx("label", { className: "block text-xs font-medium text-fg-secondary mb-1.5", children: translate('core:filters.label_from') }), _jsx("input", { type: "date", value: localFromDate, onChange: e => setLocalFromDate(e.target.value), className: cn('w-full h-10 px-3 text-sm rounded-lg border transition-colors', 'bg-input text-fg border-border', 'focus:outline-none focus:ring-2 focus:ring-ring focus:border-accent') })] }), _jsxs("div", { className: "relative", children: [_jsx("label", { className: "block text-xs font-medium text-fg-secondary mb-1.5", children: translate('core:filters.label_to') }), _jsx("input", { type: "date", value: localToDate, onChange: e => setLocalToDate(e.target.value), className: cn('w-full h-10 px-3 text-sm rounded-lg border transition-colors', 'bg-input text-fg border-border', 'focus:outline-none focus:ring-2 focus:ring-ring focus:border-accent') })] })] }))] }), _jsxs("div", { className: "flex items-center justify-between p-4 border-t border-border shrink-0", children: [_jsx("button", { onClick: clearFilter, type: "button", className: "text-sm font-medium text-fg-secondary hover:text-fg", children: translate('core:common.clear') }), _jsx(PillButton, { type: "button", variant: "primary", onClick: applyChanges, children: translate('core:common.apply') })] })] })] }))] })] }));
132
+ }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import { SerializedFilter } from '@maxal_studio/kratosjs';
3
+ interface QueryBuilderFilterProps {
4
+ filter: SerializedFilter;
5
+ value?: any[];
6
+ onChange: (value: any[] | undefined) => void;
7
+ /** Expand inline instead of a nested popover (for filters dropdown panel). */
8
+ embedded?: boolean;
9
+ }
10
+ export declare function QueryBuilderFilterComponent({ filter, value, onChange, embedded }: QueryBuilderFilterProps): React.JSX.Element;
11
+ export {};
@@ -0,0 +1,200 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect } from 'react';
3
+ import { cn } from '../../utils/classNames';
4
+ import { PillButton } from '../ui/PillButton';
5
+ import { translate } from '../../i18n/activeLocale';
6
+ export function QueryBuilderFilterComponent({ filter, value, onChange, embedded = false }) {
7
+ const [isOpen, setIsOpen] = useState(false);
8
+ const [localRules, setLocalRules] = useState(value || []);
9
+ const appliedRules = value || [];
10
+ // Sync local rules when value prop changes (e.g., when defaults are applied)
11
+ // Only sync when dropdown is closed to avoid interrupting user edits
12
+ useEffect(() => {
13
+ if (!isOpen) {
14
+ setLocalRules(value || []);
15
+ }
16
+ }, [value, isOpen]);
17
+ const addRule = () => {
18
+ const firstConstraint = filter.constraints?.[0];
19
+ const newRule = {
20
+ type: firstConstraint?.name || '',
21
+ dataType: firstConstraint?.dataType || 'text',
22
+ data: {
23
+ operator: firstConstraint?.operators?.[0]?.name || '',
24
+ settings: {
25
+ field: firstConstraint?.name || '',
26
+ value: '',
27
+ },
28
+ },
29
+ };
30
+ setLocalRules([...localRules, newRule]);
31
+ };
32
+ const updateRule = (index, updates) => {
33
+ const newRules = localRules.map((rule, i) => (i === index ? { ...rule, ...updates } : rule));
34
+ setLocalRules(newRules);
35
+ };
36
+ const removeRule = (index) => {
37
+ setLocalRules(localRules.filter((_, i) => i !== index));
38
+ };
39
+ const clearAll = () => {
40
+ setLocalRules([]);
41
+ onChange(undefined);
42
+ setIsOpen(false);
43
+ };
44
+ const applyChanges = () => {
45
+ onChange(localRules.length > 0 ? localRules : undefined);
46
+ setIsOpen(false);
47
+ };
48
+ const handleOpen = () => {
49
+ setLocalRules(appliedRules);
50
+ setIsOpen(embedded ? !isOpen : true);
51
+ };
52
+ const getConstraint = (name) => {
53
+ return filter.constraints?.find(c => c.name === name);
54
+ };
55
+ return (_jsxs("div", { className: "relative", children: [_jsx("button", { onClick: handleOpen, type: "button", className: cn('w-full h-10 px-3 pr-10 text-sm text-left rounded-lg border transition-colors', 'bg-input text-fg border-border', 'focus:outline-none focus:ring-2 focus:ring-ring focus:border-accent', appliedRules.length > 0 && 'ring-2 ring-ring border-accent'), children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { children: appliedRules.length > 0 ? `${appliedRules.length} rule(s) active` : 'Add rules...' }), _jsxs("div", { className: "absolute right-3 top-1/2 -translate-y-1/2 flex items-center gap-1", children: [appliedRules.length > 0 && (_jsx("span", { className: "inline-flex items-center justify-center min-w-[20px] h-5 px-1.5 text-xs font-bold text-white bg-accent rounded-full", children: appliedRules.length })), _jsx("svg", { className: "w-4 h-4 text-fg-secondary", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] })] }) }), isOpen && (_jsxs(_Fragment, { children: [!embedded && _jsx("div", { className: "fixed inset-0 z-[60]", onClick: () => setIsOpen(false) }), _jsxs("div", { className: cn('flex flex-col overflow-hidden border border-border bg-surface', embedded
56
+ ? 'mt-2 rounded-xl'
57
+ : 'absolute left-0 right-0 top-full z-[70] mt-2 max-h-[500px] rounded-lg shadow-xl'), children: [_jsxs("div", { className: "flex items-center justify-between p-4 border-b border-border shrink-0", children: [_jsx("h3", { className: "text-base font-semibold text-fg", children: translate('core:filters.query_builder') }), _jsx("button", { onClick: () => setIsOpen(false), type: "button", className: "p-1 rounded-md text-fg-secondary hover:text-fg hover:bg-hover", children: _jsx("svg", { className: "w-5 h-5", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }) }) })] }), _jsxs("div", { className: "p-4 space-y-3 overflow-y-auto flex-1", children: [localRules.map((rule, index) => {
58
+ const constraint = getConstraint(rule.type);
59
+ const operators = constraint?.operators || [];
60
+ return (_jsxs("div", { className: "p-3 rounded-lg border border-border bg-muted space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-xs font-medium text-fg-secondary uppercase tracking-wide", children: ["Rule ", index + 1] }), _jsx("button", { onClick: () => removeRule(index), type: "button", className: "p-1 text-red-500 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-900/20 rounded", children: _jsx("svg", { className: "w-4 h-4", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" }) }) })] }), (() => {
61
+ const isBetweenOperator = rule.data?.operator === 'between' ||
62
+ rule.data?.operator === 'notBetween';
63
+ const isDateType = rule.dataType === 'date';
64
+ const isNumberType = rule.dataType === 'number';
65
+ return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2", children: [_jsxs("div", { className: "relative", children: [_jsx("select", { value: rule.type, onChange: e => {
66
+ const newConstraint = getConstraint(e.target.value);
67
+ updateRule(index, {
68
+ type: e.target.value,
69
+ dataType: newConstraint?.dataType || 'text',
70
+ data: {
71
+ ...rule.data,
72
+ operator: newConstraint?.operators?.[0]?.name ||
73
+ '',
74
+ settings: {
75
+ ...rule.data.settings,
76
+ field: e.target.value,
77
+ },
78
+ },
79
+ });
80
+ }, className: "w-full h-9 px-2 pr-8 text-sm rounded-md border appearance-none bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring", children: filter.constraints?.map(c => (_jsx("option", { value: c.name, children: c.label || c.name }, c.name))) }), _jsx("svg", { className: "absolute right-2 top-1/2 -translate-y-1/2 w-3 h-3 text-fg-secondary pointer-events-none", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] }), _jsxs("div", { className: "relative", children: [_jsx("select", { value: rule.data?.operator || '', onChange: e => updateRule(index, {
81
+ data: {
82
+ ...rule.data,
83
+ operator: e.target.value,
84
+ },
85
+ }), className: "w-full h-9 px-2 pr-8 text-sm rounded-md border appearance-none bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring", children: operators.map((op) => (_jsx("option", { value: op.name, children: op.label || op.name }, op.name))) }), _jsx("svg", { className: "absolute right-2 top-1/2 -translate-y-1/2 w-3 h-3 text-fg-secondary pointer-events-none", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }) })] })] }), isBetweenOperator && isDateType ? (
86
+ // For "between" operator with date type, show two date inputs
87
+ _jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2", children: (() => {
88
+ const fromValue = Array.isArray(rule.data?.settings?.value)
89
+ ? rule.data.settings.value[0]
90
+ : typeof rule.data?.settings?.value === 'object' &&
91
+ rule.data?.settings?.value?.from
92
+ ? rule.data.settings.value.from
93
+ : '';
94
+ const toValue = Array.isArray(rule.data?.settings?.value)
95
+ ? rule.data.settings.value[1]
96
+ : typeof rule.data?.settings?.value === 'object' &&
97
+ rule.data?.settings?.value?.to
98
+ ? rule.data.settings.value.to
99
+ : '';
100
+ return (_jsxs(_Fragment, { children: [_jsx("input", { type: "date", value: fromValue, onChange: e => {
101
+ const newValue = {
102
+ from: e.target.value,
103
+ to: toValue,
104
+ };
105
+ updateRule(index, {
106
+ data: {
107
+ ...rule.data,
108
+ settings: {
109
+ ...rule.data.settings,
110
+ value: newValue,
111
+ },
112
+ },
113
+ });
114
+ }, placeholder: translate('core:filters.from_date'), className: "w-full h-9 px-2 text-sm rounded-md border bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring" }), _jsx("input", { type: "date", value: toValue, onChange: e => {
115
+ const newValue = {
116
+ from: fromValue,
117
+ to: e.target.value,
118
+ };
119
+ updateRule(index, {
120
+ data: {
121
+ ...rule.data,
122
+ settings: {
123
+ ...rule.data.settings,
124
+ value: newValue,
125
+ },
126
+ },
127
+ });
128
+ }, placeholder: translate('core:filters.to_date'), className: "w-full h-9 px-2 text-sm rounded-md border bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring" })] }));
129
+ })() })) : isBetweenOperator && isNumberType ? (
130
+ // For "between" operator with number type, show two number inputs
131
+ _jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2", children: (() => {
132
+ const fromValue = Array.isArray(rule.data?.settings?.value)
133
+ ? rule.data.settings.value[0]
134
+ : '';
135
+ const toValue = Array.isArray(rule.data?.settings?.value)
136
+ ? rule.data.settings.value[1]
137
+ : '';
138
+ return (_jsxs(_Fragment, { children: [_jsx("input", { type: "number", value: fromValue, onChange: e => {
139
+ const newValue = [
140
+ e.target.value || '',
141
+ toValue,
142
+ ];
143
+ updateRule(index, {
144
+ data: {
145
+ ...rule.data,
146
+ settings: {
147
+ ...rule.data.settings,
148
+ value: newValue,
149
+ },
150
+ },
151
+ });
152
+ }, placeholder: translate('core:filters.from'), className: "w-full h-9 px-2 text-sm rounded-md border bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring" }), _jsx("input", { type: "number", value: toValue, onChange: e => {
153
+ const newValue = [
154
+ fromValue,
155
+ e.target.value || '',
156
+ ];
157
+ updateRule(index, {
158
+ data: {
159
+ ...rule.data,
160
+ settings: {
161
+ ...rule.data.settings,
162
+ value: newValue,
163
+ },
164
+ },
165
+ });
166
+ }, placeholder: translate('core:filters.to'), className: "w-full h-9 px-2 text-sm rounded-md border bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring" })] }));
167
+ })() })) : isDateType ? (
168
+ // Single date input for date type
169
+ _jsx("input", { type: "date", value: rule.data?.settings?.value || '', onChange: e => updateRule(index, {
170
+ data: {
171
+ ...rule.data,
172
+ settings: {
173
+ ...rule.data.settings,
174
+ value: e.target.value,
175
+ },
176
+ },
177
+ }), placeholder: translate('core:filters.date'), className: "w-full h-9 px-2 text-sm rounded-md border bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring" })) : rule.dataType === 'number' ? (
178
+ // Number input for number type
179
+ _jsx("input", { type: "number", value: rule.data?.settings?.value || '', onChange: e => updateRule(index, {
180
+ data: {
181
+ ...rule.data,
182
+ settings: {
183
+ ...rule.data.settings,
184
+ value: e.target.value,
185
+ },
186
+ },
187
+ }), placeholder: translate('core:filters.value'), className: "w-full h-9 px-2 text-sm rounded-md border bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring" })) : (
188
+ // Regular text input for text, select, and other types
189
+ _jsx("input", { type: "text", value: rule.data?.settings?.value || '', onChange: e => updateRule(index, {
190
+ data: {
191
+ ...rule.data,
192
+ settings: {
193
+ ...rule.data.settings,
194
+ value: e.target.value,
195
+ },
196
+ },
197
+ }), placeholder: translate('core:filters.value'), className: "w-full h-9 px-2 text-sm rounded-md border bg-input text-fg border-border focus:outline-none focus:ring-2 focus:ring-ring" }))] }));
198
+ })()] }, index));
199
+ }), localRules.length === 0 && (_jsxs("div", { className: "text-center py-8 text-fg-secondary", children: [_jsx("svg", { className: "w-12 h-12 mx-auto mb-3 opacity-50", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" }) }), _jsx("p", { className: "text-sm mb-1", children: translate('core:filters.no_rules') }), _jsx("p", { className: "text-xs", children: translate('core:filters.add_rule_hint') })] })), _jsx(PillButton, { type: "button", onClick: addRule, className: "w-full border-dashed border-accent/40 bg-transparent text-accent hover:bg-accent-soft", children: "+ Add Rule" })] }), _jsxs("div", { className: "flex items-center justify-between p-4 border-t border-border shrink-0", children: [_jsx("button", { onClick: clearAll, type: "button", className: "text-sm font-medium text-fg-secondary hover:text-fg", disabled: localRules.length === 0, children: translate('core:common.clear_all') }), _jsxs("div", { className: "flex gap-2", children: [_jsx(PillButton, { type: "button", onClick: () => setIsOpen(false), children: translate('core:common.cancel') }), _jsx(PillButton, { type: "button", variant: "primary", onClick: applyChanges, children: translate('core:common.apply') })] })] })] })] }))] }));
200
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ export interface HeaderProps {
3
+ panelId?: string;
4
+ darkMode: boolean;
5
+ onDarkModeToggle: () => void;
6
+ onMobileMenuToggle?: () => void;
7
+ apiBaseUrl?: string;
8
+ globalSearchAvailable?: boolean;
9
+ }
10
+ export declare function Header({ darkMode, onDarkModeToggle, onMobileMenuToggle, apiBaseUrl, globalSearchAvailable, }: HeaderProps): React.JSX.Element;
@@ -0,0 +1,70 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useMemo } from 'react';
3
+ import { useLocation } from 'react-router-dom';
4
+ import { Moon, Sun, Menu, User, LogOut, ChevronDown } from 'lucide-react';
5
+ import { cn } from '../../utils/classNames';
6
+ import { GlobalSearch } from '../GlobalSearch';
7
+ import { useAuth } from '../../auth/AuthContext';
8
+ import { usePanelMetadata } from '../../contexts/PanelMetadataContext';
9
+ import { IconButton } from '../ui';
10
+ import { useTranslation } from '../../i18n/useTranslation';
11
+ import { LocaleSwitcher } from '../../i18n/LocaleSwitcher';
12
+ import { useLocale } from '../../i18n/useLocale';
13
+ import { Slot } from '../../slots/Slot';
14
+ import { SlotCluster } from '../../slots/SlotCluster';
15
+ /** Language row in the account menu — hidden when the panel has a single locale. */
16
+ function LocaleSwitcherRow() {
17
+ const { t } = useTranslation();
18
+ const { locales } = useLocale();
19
+ if (locales.length <= 1)
20
+ return null;
21
+ return (_jsxs("div", { className: "flex items-center justify-between gap-2 border-b border-border px-3 py-2", children: [_jsx("span", { className: "text-xs font-medium text-fg-muted", children: t('core:panel.language') }), _jsx(LocaleSwitcher, {})] }));
22
+ }
23
+ function useCurrentPageTitle() {
24
+ const location = useLocation();
25
+ const { resources, pages } = usePanelMetadata();
26
+ return useMemo(() => {
27
+ if (location.pathname.startsWith('/page/')) {
28
+ const slug = location.pathname.replace('/page/', '').split('/')[0];
29
+ return pages.find(page => page.slug === slug)?.label;
30
+ }
31
+ const slug = location.pathname.split('/').filter(Boolean)[0];
32
+ if (!slug)
33
+ return undefined;
34
+ const resource = resources.find(item => item.slug === slug);
35
+ return resource?.pluralLabel ?? resource?.label;
36
+ }, [location.pathname, resources, pages]);
37
+ }
38
+ export function Header({ darkMode, onDarkModeToggle, onMobileMenuToggle, apiBaseUrl, globalSearchAvailable = false, }) {
39
+ const { user, logout } = useAuth();
40
+ const { t } = useTranslation();
41
+ const pageTitle = useCurrentPageTitle();
42
+ const [userMenuOpen, setUserMenuOpen] = useState(false);
43
+ const userMenuRef = useRef(null);
44
+ useEffect(() => {
45
+ const handleClickOutside = (event) => {
46
+ if (userMenuRef.current && !userMenuRef.current.contains(event.target)) {
47
+ setUserMenuOpen(false);
48
+ }
49
+ };
50
+ const handleEscape = (event) => {
51
+ if (event.key === 'Escape') {
52
+ setUserMenuOpen(false);
53
+ }
54
+ };
55
+ if (userMenuOpen) {
56
+ document.addEventListener('mousedown', handleClickOutside);
57
+ document.addEventListener('keydown', handleEscape);
58
+ }
59
+ return () => {
60
+ document.removeEventListener('mousedown', handleClickOutside);
61
+ document.removeEventListener('keydown', handleEscape);
62
+ };
63
+ }, [userMenuOpen]);
64
+ const handleLogout = async () => {
65
+ await logout();
66
+ setUserMenuOpen(false);
67
+ };
68
+ const userInitial = (user?.name || user?.email || '?').charAt(0).toUpperCase();
69
+ return (_jsxs("header", { className: "sticky top-0 z-40 flex h-14 shrink-0 items-center gap-3 border-b border-border bg-base px-4 sm:px-6", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-3", children: [onMobileMenuToggle && (_jsx(IconButton, { variant: "ghost", size: "sm", "aria-label": t('core:panel.open_menu'), onClick: onMobileMenuToggle, className: "lg:hidden", children: _jsx(Menu, { className: "h-5 w-5" }) })), pageTitle && (_jsx("h1", { className: "truncate text-lg font-semibold tracking-tight text-fg sm:text-xl", children: pageTitle })), _jsx(Slot, { name: "header.left", context: { user }, as: "div", className: "flex min-w-0 items-center gap-2" })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1 sm:gap-2", children: [_jsx(SlotCluster, { name: "header.right", context: { user } }), apiBaseUrl && globalSearchAvailable && _jsx(GlobalSearch, { apiBaseUrl: apiBaseUrl }), _jsx(IconButton, { variant: "ghost", size: "sm", "aria-label": darkMode ? t('core:panel.toggle_light') : t('core:panel.toggle_dark'), title: darkMode ? t('core:panel.toggle_light') : t('core:panel.toggle_dark'), onClick: onDarkModeToggle, className: "h-9 w-9 shrink-0", children: _jsx("span", { className: "inline-flex h-4 w-4 items-center justify-center", children: darkMode ? (_jsx(Sun, { className: "h-4 w-4 text-warning" })) : (_jsx(Moon, { className: "h-4 w-4 text-fg-secondary" })) }) }), user && (_jsxs("div", { className: "relative", ref: userMenuRef, children: [_jsxs("button", { type: "button", onClick: () => setUserMenuOpen(open => !open), "aria-expanded": userMenuOpen, "aria-haspopup": "menu", className: cn('flex items-center gap-2 rounded-full border border-transparent py-1 pl-1 pr-2 transition-colors', 'hover:border-border hover:bg-hover', 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring', userMenuOpen && 'border-border bg-hover'), children: [user.avatarUrl ? (_jsx("img", { src: user.avatarUrl, alt: "", className: "h-8 w-8 rounded-full object-cover ring-1 ring-border" })) : (_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-raised text-xs font-semibold text-fg ring-1 ring-border", children: userInitial })), _jsx("div", { className: "hidden min-w-0 text-left md:block", children: _jsx("div", { className: "max-w-[140px] truncate text-sm font-medium text-fg", children: user.name || user.email }) }), _jsx(ChevronDown, { className: cn('hidden h-4 w-4 text-fg-muted transition-transform md:block', userMenuOpen && 'rotate-180') })] }), userMenuOpen && (_jsxs("div", { role: "menu", className: "absolute right-0 z-50 mt-2 w-60 overflow-hidden rounded-xl border border-border bg-raised shadow-soft-lg", children: [_jsxs("div", { className: "border-b border-border px-4 py-3", children: [_jsx("div", { className: "truncate text-sm font-medium text-fg", children: user.name || user.email }), user.email && user.name && (_jsx("div", { className: "mt-0.5 truncate text-xs text-fg-muted", children: user.email }))] }), _jsx(LocaleSwitcherRow, {}), _jsx(Slot, { name: "header.userMenu", context: { user }, as: "div", className: "p-1" }), _jsx("div", { className: "p-1", children: _jsxs("button", { type: "button", role: "menuitem", onClick: handleLogout, className: "flex w-full items-center gap-2 rounded-lg px-3 py-2 text-left text-sm text-fg transition-colors hover:bg-hover", children: [_jsx(LogOut, { className: "h-4 w-4 text-fg-secondary" }), t('core:panel.sign_out')] }) })] }))] })), !user && (_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-muted", children: _jsx(User, { className: "h-4 w-4 text-fg-muted" }) }))] })] }));
70
+ }
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ export interface PanelBrandMarkProps {
3
+ icon?: string;
4
+ favicon?: string;
5
+ size?: 'sm' | 'md' | 'lg';
6
+ className?: string;
7
+ }
8
+ export declare function PanelBrandMark({ icon, favicon, size, className }: PanelBrandMarkProps): React.JSX.Element;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { LayoutDashboard } from 'lucide-react';
3
+ import { Icon } from '../utils/Icon';
4
+ import { cn } from '../../utils/classNames';
5
+ const SIZE_CLASSES = {
6
+ sm: {
7
+ box: 'h-8 w-8 rounded-lg',
8
+ lucide: 'h-4 w-4',
9
+ image: 'h-5 w-5',
10
+ },
11
+ md: {
12
+ box: 'h-10 w-10 rounded-xl',
13
+ lucide: 'h-5 w-5',
14
+ image: 'h-6 w-6',
15
+ },
16
+ lg: {
17
+ box: 'h-12 w-12 rounded-xl',
18
+ lucide: 'h-6 w-6',
19
+ image: 'h-7 w-7',
20
+ },
21
+ };
22
+ export function PanelBrandMark({ icon, favicon, size = 'md', className }) {
23
+ const sizes = SIZE_CLASSES[size];
24
+ if (favicon) {
25
+ return (_jsx("div", { className: cn('flex shrink-0 items-center justify-center overflow-hidden bg-surface ring-1 ring-border', sizes.box, className), children: _jsx("img", { src: favicon, alt: "", className: cn('object-contain', sizes.image) }) }));
26
+ }
27
+ return (_jsx("div", { className: cn('flex shrink-0 items-center justify-center bg-accent-soft ring-1 ring-accent/10', sizes.box, className), children: icon ? (_jsx(Icon, { name: icon, className: cn('text-accent', sizes.lucide) })) : (_jsx(LayoutDashboard, { className: cn('text-accent', sizes.lucide) })) }));
28
+ }