@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,88 @@
1
+ import { apiGet, apiPost } from './http';
2
+
3
+ /**
4
+ * Typed endpoints for the panel's resource API.
5
+ * All functions take the panel apiBaseUrl (e.g. http://host/kratosjs/api) and a resource slug.
6
+ */
7
+
8
+ export interface RecordEnvelope<T = any> {
9
+ data: T;
10
+ metadata?: { recordTitle?: string; refreshBadges?: boolean; [key: string]: any };
11
+ }
12
+
13
+ /** Normalize bare-record responses into the `{ data, metadata }` envelope. */
14
+ function toEnvelope<T = any>(result: any): RecordEnvelope<T> {
15
+ if (result && typeof result === 'object' && 'data' in result) {
16
+ return result as RecordEnvelope<T>;
17
+ }
18
+ return { data: result as T };
19
+ }
20
+
21
+ /** GET /:slug/:id */
22
+ export async function getRecord(apiBaseUrl: string, slug: string, id: string | number): Promise<RecordEnvelope> {
23
+ return toEnvelope(await apiGet(`${apiBaseUrl}/${slug}/${id}`, apiBaseUrl));
24
+ }
25
+
26
+ /** POST /:slug */
27
+ export async function createRecord(apiBaseUrl: string, slug: string, payload: any): Promise<RecordEnvelope> {
28
+ return toEnvelope(await apiPost(`${apiBaseUrl}/${slug}`, payload, apiBaseUrl));
29
+ }
30
+
31
+ /** POST /:slug/update/:id */
32
+ export async function updateRecord(
33
+ apiBaseUrl: string,
34
+ slug: string,
35
+ id: string | number,
36
+ payload: any,
37
+ ): Promise<RecordEnvelope> {
38
+ return toEnvelope(await apiPost(`${apiBaseUrl}/${slug}/update/${id}`, payload, apiBaseUrl));
39
+ }
40
+
41
+ /** POST /:slug/bulk-delete */
42
+ export function bulkDelete(
43
+ apiBaseUrl: string,
44
+ slug: string,
45
+ ids: any[],
46
+ ): Promise<{ deleted?: any[]; metadata?: { refreshBadges?: boolean }; [key: string]: any }> {
47
+ return apiPost(`${apiBaseUrl}/${slug}/bulk-delete`, { ids }, apiBaseUrl);
48
+ }
49
+
50
+ /** GET /:slug/schema/form — returns the serialized form schema */
51
+ export async function getFormSchema(apiBaseUrl: string, slug: string): Promise<any> {
52
+ const result = await apiGet<any>(`${apiBaseUrl}/${slug}/schema/form`, apiBaseUrl);
53
+ return result?.schema || result;
54
+ }
55
+
56
+ /**
57
+ * GET /:slug/schema/table — returns the serialized table schema with
58
+ * the resource capability flags merged in (as TableRenderer expects).
59
+ */
60
+ export async function getTableSchema(apiBaseUrl: string, slug: string): Promise<any> {
61
+ const result = await apiGet<any>(`${apiBaseUrl}/${slug}/schema/table`, apiBaseUrl);
62
+ return {
63
+ ...(result?.schema || result),
64
+ canCreate: result?.canCreate,
65
+ canEdit: result?.canEdit,
66
+ canDelete: result?.canDelete,
67
+ canView: result?.canView,
68
+ };
69
+ }
70
+
71
+ /** GET /:slug/relations — relation metadata for the view modal */
72
+ export async function getRelations(apiBaseUrl: string, slug: string): Promise<any[]> {
73
+ const result = await apiGet<any>(`${apiBaseUrl}/${slug}/relations`, apiBaseUrl);
74
+ return result?.relations || [];
75
+ }
76
+
77
+ /** POST /:parentSlug/:parentId/relations/:relationName */
78
+ export async function createRelationRecord(
79
+ apiBaseUrl: string,
80
+ parentSlug: string,
81
+ parentId: string | number,
82
+ relationName: string,
83
+ payload: any,
84
+ ): Promise<RecordEnvelope> {
85
+ return toEnvelope(
86
+ await apiPost(`${apiBaseUrl}/${parentSlug}/${parentId}/relations/${relationName}`, payload, apiBaseUrl),
87
+ );
88
+ }
@@ -0,0 +1,108 @@
1
+ import { afterEach, describe, expect, it, vi } from 'vitest';
2
+ import { TableApiClient } from './tableApi';
3
+ import { mockFetch, restoreFetch } from '../test/mockFetch';
4
+
5
+ const API = 'http://api.test/admin';
6
+ const LIST_URL = `${API}/users`;
7
+
8
+ describe('TableApiClient', () => {
9
+ afterEach(() => {
10
+ restoreFetch();
11
+ });
12
+
13
+ it('posts query params to baseUrl + fetchPath and returns the result', async () => {
14
+ const result = {
15
+ data: [{ id: 1 }],
16
+ pagination: { page: 1, limit: 25, total: 1, pages: 1, hasNext: false, hasPrev: false },
17
+ };
18
+ const requests = mockFetch([{ match: '/users/list', body: result }]);
19
+
20
+ const client = new TableApiClient(API, LIST_URL, '/list');
21
+ const data = await client.fetchData({ page: 1, perPage: 25, search: 'jane' });
22
+
23
+ expect(data).toEqual(result);
24
+ expect(requests[0].method).toBe('POST');
25
+ expect(requests[0].body).toEqual({ page: 1, perPage: 25, search: 'jane' });
26
+ });
27
+
28
+ it('sends credentials:include and no Authorization header on requests', async () => {
29
+ mockFetch([{ match: '/users/list', body: { data: [], pagination: {} } }]);
30
+
31
+ const client = new TableApiClient(API, LIST_URL, '/list');
32
+ await client.fetchData({});
33
+
34
+ const fetchMock = globalThis.fetch as unknown as { mock: { calls: [string, RequestInit][] } };
35
+ const init = fetchMock.mock.calls[0][1];
36
+ expect(init?.credentials).toBe('include');
37
+ expect(new Headers(init?.headers).get('Authorization')).toBeNull();
38
+ });
39
+
40
+ it('throws an unauthorized error on 401 when refresh is not possible', async () => {
41
+ mockFetch([{ match: '/users/list', status: 401, body: { message: 'nope' } }]);
42
+
43
+ const client = new TableApiClient(API, LIST_URL, '/list');
44
+ await expect(client.fetchData({})).rejects.toThrow(/Unauthorized/);
45
+ });
46
+
47
+ it('calls the refresh endpoint and retries on 401', async () => {
48
+ const calls: { url: string; init?: RequestInit }[] = [];
49
+ let listCallCount = 0;
50
+
51
+ vi.stubGlobal('fetch', (async (input: RequestInfo | URL, init?: RequestInit) => {
52
+ const url = String(input);
53
+ calls.push({ url, init });
54
+
55
+ if (url.includes('/auth/refresh')) {
56
+ return new Response(JSON.stringify({ tokens: { expiresIn: 900 } }), {
57
+ status: 200,
58
+ headers: { 'Content-Type': 'application/json' },
59
+ });
60
+ }
61
+
62
+ listCallCount++;
63
+ if (listCallCount === 1) {
64
+ return new Response(JSON.stringify({ message: 'expired' }), {
65
+ status: 401,
66
+ headers: { 'Content-Type': 'application/json' },
67
+ });
68
+ }
69
+ return new Response(JSON.stringify({ data: [{ id: 7 }], pagination: {} }), {
70
+ status: 200,
71
+ headers: { 'Content-Type': 'application/json' },
72
+ });
73
+ }) as typeof fetch);
74
+
75
+ const client = new TableApiClient(API, LIST_URL, '/list');
76
+ const result = await client.fetchData({});
77
+
78
+ expect(result.data).toEqual([{ id: 7 }]);
79
+ expect(calls.some(c => c.url.includes('/auth/refresh'))).toBe(true);
80
+ // Retry must also use credentials:include — no localStorage involved
81
+ const retryCall = calls.find(c => c.url.includes('/users/list') && calls.indexOf(c) > 0);
82
+ expect(retryCall?.init?.credentials).toBe('include');
83
+ });
84
+
85
+ it('unwraps { data } from updateRecord responses', async () => {
86
+ mockFetch([{ match: '/users/update/5', body: { data: { id: 5, name: 'x' }, metadata: {} } }]);
87
+
88
+ const client = new TableApiClient(API, LIST_URL, '/list');
89
+ const updated = await client.updateRecord(5, { name: 'x' });
90
+ expect(updated).toEqual({ id: 5, name: 'x' });
91
+ });
92
+
93
+ it('returns the full body from deleteRecords', async () => {
94
+ const body = { deleted: [1, 2], metadata: { refreshBadges: true } };
95
+ const requests = mockFetch([{ match: '/users/bulk-delete', body }]);
96
+
97
+ const client = new TableApiClient(API, LIST_URL, '/list');
98
+ const result = await client.deleteRecords([1, 2]);
99
+
100
+ expect(result).toEqual(body);
101
+ expect(requests[0].body).toEqual({ ids: [1, 2] });
102
+ });
103
+
104
+ it('strips trailing slashes from the base url', () => {
105
+ const client = new TableApiClient(API, `${LIST_URL}/`, '/list');
106
+ expect(client.getBaseUrl()).toBe(LIST_URL);
107
+ });
108
+ });
@@ -0,0 +1,107 @@
1
+ import { apiPost } from './http';
2
+
3
+ /**
4
+ * Query parameters for table API requests
5
+ */
6
+ export interface QueryParams {
7
+ page?: number;
8
+ perPage?: number;
9
+ search?: string;
10
+ sort?: string;
11
+ sortDirection?: 'asc' | 'desc';
12
+ filters?: Record<string, any>;
13
+ queryBuilders?: Record<string, any[]>;
14
+ }
15
+
16
+ /**
17
+ * Standard pagination metadata
18
+ */
19
+ export interface PaginationMeta {
20
+ /** Current page number (1-indexed) */
21
+ page: number;
22
+ /** Number of items per page */
23
+ limit: number;
24
+ /** Total number of items across all pages */
25
+ total: number;
26
+ /** Total number of pages */
27
+ pages: number;
28
+ /** Whether there's a next page */
29
+ hasNext: boolean;
30
+ /** Whether there's a previous page */
31
+ hasPrev: boolean;
32
+ }
33
+
34
+ /**
35
+ * Result structure for table API responses
36
+ */
37
+ export interface QueryResult {
38
+ data: any[];
39
+ pagination: PaginationMeta;
40
+ widgets?: Record<string, any>;
41
+ }
42
+
43
+ /**
44
+ * API client for communicating with the table backend
45
+ */
46
+ export class TableApiClient {
47
+ private baseUrl: string;
48
+ private apiBaseUrl: string;
49
+ private fetchPath: string;
50
+
51
+ /**
52
+ * @param apiBaseUrl - Base URL for the panel API (used for token refresh)
53
+ * @param baseUrl - Base URL of the resource endpoint (e.g. 'http://localhost:3001/api/users')
54
+ * @param fetchPath - Path appended for list queries ('/list' for resources, '' for custom endpoints)
55
+ */
56
+ constructor(apiBaseUrl: string, baseUrl: string, fetchPath: string = '') {
57
+ this.baseUrl = baseUrl.replace(/\/$/, ''); // Remove trailing slash
58
+ this.fetchPath = fetchPath;
59
+ this.apiBaseUrl = apiBaseUrl;
60
+ }
61
+
62
+ /**
63
+ * Fetch table data with filtering, sorting, and pagination
64
+ */
65
+ async fetchData(params: QueryParams): Promise<QueryResult> {
66
+ return apiPost<QueryResult>(this.baseUrl + this.fetchPath, params, this.apiBaseUrl);
67
+ }
68
+
69
+ /**
70
+ * Update a single record. Unwraps the `{ data, metadata }` envelope.
71
+ */
72
+ async updateRecord(id: any, data: any): Promise<any> {
73
+ const result = await apiPost<any>(`${this.baseUrl}/update/${id}`, data, this.apiBaseUrl);
74
+ return result?.data || result;
75
+ }
76
+
77
+ /**
78
+ * Delete records (bulk or single).
79
+ * Returns the full response body (including metadata e.g. refreshBadges) so callers can react to it.
80
+ */
81
+ async deleteRecords(
82
+ ids: any[],
83
+ ): Promise<{ deleted?: any[]; metadata?: { refreshBadges?: boolean }; [key: string]: any }> {
84
+ return apiPost(`${this.baseUrl}/bulk-delete`, { ids }, this.apiBaseUrl);
85
+ }
86
+
87
+ /**
88
+ * Bulk delete records (alias for deleteRecords)
89
+ */
90
+ async bulkDelete(
91
+ ids: any[],
92
+ ): Promise<{ deleted?: any[]; metadata?: { refreshBadges?: boolean }; [key: string]: any }> {
93
+ return this.deleteRecords(ids);
94
+ }
95
+
96
+ getBaseUrl(): string {
97
+ return this.baseUrl;
98
+ }
99
+
100
+ getFetchPath(): string {
101
+ return this.fetchPath;
102
+ }
103
+
104
+ getApiBaseUrl(): string {
105
+ return this.apiBaseUrl;
106
+ }
107
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * URL derivation helpers shared by the table, modals and API clients.
3
+ */
4
+
5
+ /**
6
+ * Derive the panel API base URL from a resource API URL.
7
+ *
8
+ * - `.../api/users` -> `.../api`
9
+ * - `.../api/users/1/relations/posts` -> `.../api`
10
+ */
11
+ export function deriveApiBaseUrl(apiUrl: string): string {
12
+ if (apiUrl.includes('/relations/')) {
13
+ const baseUrl = apiUrl.substring(0, apiUrl.indexOf('/relations/'));
14
+ // Remove the record id segment to get back to the api base
15
+ return baseUrl.substring(0, baseUrl.lastIndexOf('/'));
16
+ }
17
+ return apiUrl.substring(0, apiUrl.lastIndexOf('/'));
18
+ }
19
+
20
+ /** Last path segment of a resource API URL (the resource slug). */
21
+ export function resourceSlugFromUrl(apiUrl: string): string {
22
+ return apiUrl.split('/').filter(Boolean).pop() || '';
23
+ }
24
+
25
+ /** Deterministic, short, filesystem/localStorage-safe hash of a string. */
26
+ export function hashString(str: string): string {
27
+ let hash = 0;
28
+ for (let i = 0; i < str.length; i++) {
29
+ const char = str.charCodeAt(i);
30
+ hash = (hash << 5) - hash + char;
31
+ hash = hash & hash; // Convert to 32-bit integer
32
+ }
33
+ return Math.abs(hash).toString(36);
34
+ }
35
+
36
+ /**
37
+ * Stable key for per-resource localStorage entries (column visibility, layout).
38
+ * Non-resource tables hash the URL; relation tables use the relation slug.
39
+ */
40
+ export function resourceStorageKey(apiUrl: string, isResource: boolean, relatedResourceSlug?: string): string {
41
+ if (!isResource) {
42
+ return hashString(apiUrl);
43
+ }
44
+ if (relatedResourceSlug) {
45
+ return relatedResourceSlug.replace(/[^a-zA-Z0-9_]/g, '_');
46
+ }
47
+ const segments = apiUrl.split('/').filter(Boolean);
48
+ const lastSegment = segments[segments.length - 1] || segments[segments.length - 2] || 'resource';
49
+ return lastSegment.replace(/[^a-zA-Z0-9_]/g, '_');
50
+ }
@@ -0,0 +1,67 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { resolveRegistries } from './app';
3
+ import { definePluginClient } from './plugin';
4
+
5
+ // Minimal stand-in components — resolveRegistries only moves references around.
6
+ const AppField = () => null;
7
+ const PluginField = () => null;
8
+ const AppColumn = () => null;
9
+ const AppWidget = () => null;
10
+ const AppBlock = () => null;
11
+
12
+ describe('resolveRegistries (mountAdminPanel direct registration)', () => {
13
+ it('exposes app-level fields/columns/widgets/blocks without a plugin', () => {
14
+ const registries = resolveRegistries({
15
+ fields: { 'star-rating': AppField },
16
+ columns: { 'star-rating': AppColumn },
17
+ widgets: { card: AppWidget },
18
+ blocks: { 'lives-page': AppBlock },
19
+ });
20
+
21
+ expect(registries.fields['star-rating']).toBe(AppField);
22
+ expect(registries.columns['star-rating']).toBe(AppColumn);
23
+ expect(registries.widgets.card).toBe(AppWidget);
24
+ expect(registries.blocks['lives-page']).toBe(AppBlock);
25
+ });
26
+
27
+ it('lets app registrations win over a plugin on the same key', () => {
28
+ const plugin = definePluginClient({
29
+ name: 'star-rating',
30
+ fields: { 'star-rating': PluginField },
31
+ });
32
+
33
+ const registries = resolveRegistries({
34
+ plugins: [plugin],
35
+ fields: { 'star-rating': AppField },
36
+ });
37
+
38
+ expect(registries.fields['star-rating']).toBe(AppField);
39
+ });
40
+
41
+ it('keeps plugin entries that the app does not override', () => {
42
+ const plugin = definePluginClient({ fields: { 'plugin-only': PluginField } });
43
+
44
+ const registries = resolveRegistries({
45
+ plugins: [plugin],
46
+ fields: { 'star-rating': AppField },
47
+ });
48
+
49
+ expect(registries.fields['plugin-only']).toBe(PluginField);
50
+ expect(registries.fields['star-rating']).toBe(AppField);
51
+ });
52
+
53
+ it('flows app-level validation rules into the merged rule map', () => {
54
+ const phoneRule = { name: 'phone', validate: () => true } as any;
55
+ const registries = resolveRegistries({ rules: { phone: phoneRule } });
56
+ expect(registries.rules.phone).toBe(phoneRule);
57
+ });
58
+
59
+ it('returns empty registries when given no options', () => {
60
+ const registries = resolveRegistries();
61
+ expect(registries.fields).toEqual({});
62
+ expect(registries.columns).toEqual({});
63
+ expect(registries.widgets).toEqual({});
64
+ expect(registries.blocks).toEqual({});
65
+ expect(registries.rules).toEqual({});
66
+ });
67
+ });
package/src/app.tsx ADDED
@@ -0,0 +1,181 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import { AdminPanel } from './components/AdminPanel';
4
+ import { mergePluginClients } from './plugin';
5
+ import type { KratosPluginClient, MergedPluginRegistries } from './plugin';
6
+ import type { FieldRegistry, WidgetRegistry, BlockRegistry } from './types';
7
+ import type { ColumnRegistry } from './contexts/ColumnRegistryContext';
8
+ import type { AuthChallengeRegistry } from './contexts/AuthChallengeRegistryContext';
9
+ import type { RuleDefinition } from '@maxal_studio/kratosjs';
10
+ import { ValidationEngine } from '@maxal_studio/kratosjs/dist/validation';
11
+ import type { ClientI18nConfig } from './i18n/buildClientI18n';
12
+ import type { SlotMap } from './slots/types';
13
+
14
+ export interface MountAdminPanelOptions {
15
+ /** Client manifests of the plugins used by this panel */
16
+ plugins?: KratosPluginClient[];
17
+ /**
18
+ * Custom field components keyed by field type (e.g. `'star-rating'`).
19
+ * Registered directly by the app — no plugin required. The key must match the
20
+ * `componentType` emitted by the backend builder. App entries override plugins.
21
+ */
22
+ fields?: FieldRegistry;
23
+ /** Custom table column components keyed by column type. App entries override plugins. */
24
+ columns?: ColumnRegistry;
25
+ /** Custom widget components keyed by widget type. App entries override plugins. */
26
+ widgets?: WidgetRegistry;
27
+ /** Custom page block components keyed by block type. App entries override plugins. */
28
+ blocks?: BlockRegistry;
29
+ /** Challenge UI components keyed by challenge type. App entries override plugins. */
30
+ authChallenges?: AuthChallengeRegistry;
31
+ /**
32
+ * Custom validation rules keyed by rule name. Authored once and shared with the
33
+ * server (via `panel.registerValidationRule`) so they validate identically on
34
+ * both sides. App entries override plugins.
35
+ */
36
+ rules?: Record<string, RuleDefinition>;
37
+ /**
38
+ * UI slot contributions keyed by slot name (e.g. `'header.right'`). Slots are
39
+ * 1:many — app contributions stack alongside any from plugins. A value is a
40
+ * single `SlotContribution` or an array.
41
+ *
42
+ * @example
43
+ * mountAdminPanel({
44
+ * slots: { 'header.right': { id: 'help', render: () => <HelpButton /> } },
45
+ * });
46
+ */
47
+ slots?: SlotMap;
48
+ /**
49
+ * API base URL. Defaults to the base path injected by the KratosJs server
50
+ * (window.__VALAJS_API_BASE_PATH__) on the current origin.
51
+ */
52
+ apiBaseUrl?: string;
53
+ /**
54
+ * Optional i18n override. Locales and every app/plugin catalog are authored once
55
+ * on the server and injected into the page (`window.__VALAJS_I18N__`) — you rarely
56
+ * need this. Use it only to override built-in `core:` chrome or add React-only
57
+ * strings; `translations` keys may target any namespace via a `ns:` prefix
58
+ * (e.g. `core:common.save`), unprefixed keys → `app`. It layers over the injected config.
59
+ *
60
+ * @example
61
+ * mountAdminPanel({ i18n: { translations: { en: { 'core:common.save': 'Store' } } } });
62
+ */
63
+ i18n?: ClientI18nConfig;
64
+ /** Panel id when serving multiple panels */
65
+ panelId?: string;
66
+ /** DOM element id to mount into (default: 'root') */
67
+ rootElementId?: string;
68
+ /**
69
+ * Accent color override (any CSS color). Sets the --kratos-accent tokens
70
+ * on the document root; hover/soft shades are derived automatically.
71
+ */
72
+ accentColor?: string;
73
+ }
74
+
75
+ /**
76
+ * Resolve the merged component/rule registries from mount options.
77
+ *
78
+ * Plugin manifests are merged first, then the app's own direct registrations
79
+ * (`fields`/`columns`/`widgets`/`blocks`/`rules`) are applied last so an app
80
+ * always wins over a plugin on a key collision.
81
+ */
82
+ export function resolveRegistries(options: MountAdminPanelOptions = {}): MergedPluginRegistries {
83
+ const appManifest: KratosPluginClient = {
84
+ name: 'app',
85
+ fields: options.fields,
86
+ columns: options.columns,
87
+ widgets: options.widgets,
88
+ blocks: options.blocks,
89
+ authChallenges: options.authChallenges,
90
+ rules: options.rules,
91
+ slots: options.slots,
92
+ };
93
+ return mergePluginClients([...(options.plugins ?? []), appManifest]);
94
+ }
95
+
96
+ function applyAccentColor(accentColor: string): void {
97
+ const root = document.documentElement;
98
+ root.style.setProperty('--kratos-accent', accentColor);
99
+ root.style.setProperty('--kratos-accent-hover', `color-mix(in srgb, ${accentColor} 85%, black)`);
100
+ root.style.setProperty('--kratos-accent-soft', `color-mix(in srgb, ${accentColor} 15%, transparent)`);
101
+ }
102
+
103
+ function resolveApiBaseUrl(): string {
104
+ const basePath = (window as any).__VALAJS_API_BASE_PATH__ || '/api';
105
+ return `${window.location.origin}${basePath}`;
106
+ }
107
+
108
+ /**
109
+ * The i18n config the KratosJs server injects into the admin HTML
110
+ * (`window.__VALAJS_I18N__`). This is the source of truth for locales and every
111
+ * app/plugin catalog — authored once on the server.
112
+ */
113
+ function resolveInjectedI18n(): ClientI18nConfig {
114
+ return ((window as any).__VALAJS_I18N__ as ClientI18nConfig | undefined) ?? {};
115
+ }
116
+
117
+ /**
118
+ * Mount the KratosJs admin panel into the DOM.
119
+ *
120
+ * This is the entry point apps use in their admin client bundle:
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * // src/admin/main.tsx — register custom components directly, no plugin needed
125
+ * import { mountAdminPanel } from '@maxal_studio/kratosjs-react';
126
+ * import '@maxal_studio/kratosjs-react/styles.css';
127
+ * import StarRatingField from './components/StarRatingField';
128
+ *
129
+ * mountAdminPanel({
130
+ * fields: { 'star-rating': StarRatingField },
131
+ * });
132
+ * ```
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * // Packaged plugins still work, and can be combined with app registrations:
137
+ * import starRating from '@maxal_studio/kratosjs-plugin-star-rating/client';
138
+ *
139
+ * mountAdminPanel({ plugins: [starRating] });
140
+ * ```
141
+ */
142
+ export function mountAdminPanel(options: MountAdminPanelOptions = {}): void {
143
+ const rootElement = document.getElementById(options.rootElementId ?? 'root');
144
+ if (!rootElement) {
145
+ throw new Error(`Root element "#${options.rootElementId ?? 'root'}" not found`);
146
+ }
147
+
148
+ const registries = resolveRegistries(options);
149
+
150
+ // Register plugin-provided validation rules into the shared engine so the
151
+ // client validates them exactly as the server does.
152
+ for (const definition of Object.values(registries.rules)) {
153
+ ValidationEngine.register(definition);
154
+ }
155
+
156
+ const apiBaseUrl = options.apiBaseUrl ?? resolveApiBaseUrl();
157
+
158
+ // The server-injected config (locales + app/plugin catalogs) is the base; any
159
+ // explicit `options.i18n` from the mount call layers on top as an override.
160
+ const i18nConfig: ClientI18nConfig = { ...resolveInjectedI18n(), ...options.i18n };
161
+
162
+ if (options.accentColor) {
163
+ applyAccentColor(options.accentColor);
164
+ }
165
+
166
+ ReactDOM.createRoot(rootElement).render(
167
+ <React.StrictMode>
168
+ <AdminPanel
169
+ apiBaseUrl={apiBaseUrl}
170
+ panelId={options.panelId}
171
+ customFields={registries.fields}
172
+ customColumns={registries.columns}
173
+ customWidgets={registries.widgets}
174
+ customBlocks={registries.blocks}
175
+ customAuthChallenges={registries.authChallenges}
176
+ customSlots={registries.slots}
177
+ i18nConfig={i18nConfig}
178
+ />
179
+ </React.StrictMode>,
180
+ );
181
+ }