@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,546 @@
1
+ import React, { useState } from 'react';
2
+ import { useFormContext, useFieldArray, useWatch } from 'react-hook-form';
3
+ import { getFieldError } from '../utils/fieldErrors';
4
+ import { FieldProps, RHFValidationRules } from '../types';
5
+ import { FieldRenderer } from '../FieldRenderer';
6
+ import { HintDisplay } from './utils/HintDisplay';
7
+ import { ViewFieldWrapper } from './utils/ViewFieldWrapper';
8
+ import { PillButton } from './ui/PillButton';
9
+ import { translate } from '../i18n/activeLocale';
10
+
11
+ export function RepeaterField(props: FieldProps) {
12
+ const {
13
+ name,
14
+ label,
15
+ helperText,
16
+ hint,
17
+ hintIcon,
18
+ hintColor,
19
+ schema = [],
20
+ defaultItems = 0,
21
+ minItems,
22
+ maxItems,
23
+ addable = true,
24
+ deletable = true,
25
+ reorderable = true,
26
+ itemLabel,
27
+ disabled = false,
28
+ collapsible = false,
29
+ mode,
30
+ value,
31
+ operation,
32
+ } = props;
33
+
34
+ // Threaded down so nested fields (e.g. relation selects, file uploads) can reach the API.
35
+ const apiBaseUrl = (props as any).apiBaseUrl as string | undefined;
36
+ const resource = (props as any).resource as string | undefined;
37
+
38
+ // Determine if repeater should be treated as required (for minItems fallback)
39
+ let isRequired = props.required === true;
40
+ const validationRules = (props.validation as { rules?: Array<string | { rule?: string }> } | undefined)?.rules;
41
+ if (validationRules && Array.isArray(validationRules)) {
42
+ if (
43
+ validationRules.some(
44
+ rule => rule === 'required' || (typeof rule === 'object' && (rule as any).rule === 'required'),
45
+ )
46
+ ) {
47
+ isRequired = true;
48
+ }
49
+ }
50
+
51
+ // Effective minItems: honor explicit minItems, otherwise infer from required
52
+ const effectiveMinItems = minItems !== undefined ? minItems : isRequired ? 1 : undefined;
53
+
54
+ // View mode: render formatted display
55
+ if (mode === 'view') {
56
+ const items = Array.isArray(value) ? value : [];
57
+ if (items.length === 0) {
58
+ return <ViewFieldWrapper label={label}>-</ViewFieldWrapper>;
59
+ }
60
+
61
+ return (
62
+ <ViewFieldWrapper label={label}>
63
+ <div className="space-y-4">
64
+ {items.map((item: any, index: number) => (
65
+ <div key={index} className="border border-border rounded-lg p-4 bg-surface">
66
+ {itemLabel && (
67
+ <div className="font-medium text-fg-secondary mb-3">
68
+ {itemLabel} {index + 1}
69
+ </div>
70
+ )}
71
+ <div className="space-y-3">
72
+ {schema.map((field: any, fieldIndex: number) => {
73
+ const fieldValue = field.name ? item[field.name] : undefined;
74
+ return (
75
+ <FieldRenderer
76
+ key={fieldIndex}
77
+ field={{ ...field, mode: 'view' }}
78
+ mode="view"
79
+ value={fieldValue}
80
+ operation={operation ?? 'view'}
81
+ apiBaseUrl={apiBaseUrl}
82
+ resource={resource}
83
+ />
84
+ );
85
+ })}
86
+ </div>
87
+ </div>
88
+ ))}
89
+ </div>
90
+ </ViewFieldWrapper>
91
+ );
92
+ }
93
+
94
+ const formContext = useFormContext();
95
+ const { control, register } = formContext;
96
+ const errors = formContext.formState?.errors || {};
97
+
98
+ const { fields, append, remove, move } = useFieldArray({
99
+ control,
100
+ name,
101
+ });
102
+
103
+ // Track active tab (for non-collapsible mode)
104
+ const [activeTab, setActiveTab] = useState<number>(0);
105
+
106
+ // Track collapsed state for each item (for collapsible mode)
107
+ const [collapsedItems, setCollapsedItems] = useState<Set<number>>(new Set());
108
+
109
+ // Default items are handled by FormRenderer's extractDefaultValues.
110
+ // We do not auto-add placeholders for minItems here, to avoid off-by-one
111
+ // issues and keep the UI strictly in sync with the actual form state.
112
+
113
+ // Ensure active tab is valid
114
+ React.useEffect(() => {
115
+ if (activeTab >= fields.length && fields.length > 0) {
116
+ setActiveTab(fields.length - 1);
117
+ }
118
+ }, [fields.length, activeTab]);
119
+
120
+ const canAdd = addable && !disabled && (maxItems === undefined || fields.length < maxItems);
121
+ const canDelete = (index: number) =>
122
+ deletable && !disabled && (effectiveMinItems === undefined || fields.length > effectiveMinItems);
123
+ const canMoveUp = (index: number) => reorderable && !disabled && index > 0;
124
+ const canMoveDown = (index: number) => reorderable && !disabled && index < fields.length - 1;
125
+
126
+ const handleDelete = (index: number) => {
127
+ if (canDelete(index)) {
128
+ remove(index);
129
+ // Switch to previous tab if deleting active tab
130
+ if (activeTab >= index && activeTab > 0) {
131
+ setActiveTab(activeTab - 1);
132
+ }
133
+ }
134
+ };
135
+
136
+ const handleMoveUp = (index: number) => {
137
+ if (canMoveUp(index)) {
138
+ move(index, index - 1);
139
+ // Update active tab if moving the active item
140
+ if (activeTab === index) {
141
+ setActiveTab(index - 1);
142
+ } else if (activeTab === index - 1) {
143
+ setActiveTab(index);
144
+ }
145
+ }
146
+ };
147
+
148
+ const handleMoveDown = (index: number) => {
149
+ if (canMoveDown(index)) {
150
+ move(index, index + 1);
151
+ // Update active tab if moving the active item
152
+ if (activeTab === index) {
153
+ setActiveTab(index + 1);
154
+ } else if (activeTab === index + 1) {
155
+ setActiveTab(index);
156
+ }
157
+ }
158
+ };
159
+
160
+ const handleAdd = () => {
161
+ if (canAdd) {
162
+ append({});
163
+ if (!collapsible) {
164
+ setActiveTab(fields.length); // Switch to the newly added tab
165
+ }
166
+ }
167
+ };
168
+
169
+ const toggleCollapse = (index: number) => {
170
+ setCollapsedItems(prev => {
171
+ const newSet = new Set(prev);
172
+ if (newSet.has(index)) {
173
+ newSet.delete(index);
174
+ } else {
175
+ newSet.add(index);
176
+ }
177
+ return newSet;
178
+ });
179
+ };
180
+
181
+ // Watch all repeater items for dynamic labels
182
+ const watchedItems = useWatch({
183
+ control,
184
+ name: name,
185
+ defaultValue: [],
186
+ });
187
+
188
+ // Helper function to get dynamic label for an item
189
+ const getItemLabel = (index: number): string => {
190
+ // Default label format
191
+ const baseLabel = itemLabel || translate('core:common.item');
192
+
193
+ // Check if itemLabel has a field name specified (e.g., "Product: {name}")
194
+ if (typeof itemLabel === 'string' && itemLabel.includes('{') && itemLabel.includes('}')) {
195
+ const match = itemLabel.match(/\{([^}]+)\}/);
196
+ if (match) {
197
+ const fieldName = match[1];
198
+ const template = itemLabel;
199
+
200
+ // Get the field value from watched items
201
+ const item = watchedItems?.[index];
202
+ const fieldValue = item?.[fieldName];
203
+
204
+ // Replace {fieldName} with actual value
205
+ if (fieldValue) {
206
+ return template.replace(/\{[^}]+\}/, fieldValue);
207
+ }
208
+ // If no value, show placeholder
209
+ return template.replace(/\{[^}]+\}/, `#${index + 1}`);
210
+ }
211
+ }
212
+
213
+ // Fallback to numbered format
214
+ return `${baseLabel} ${index + 1}`;
215
+ };
216
+
217
+ // Validation for repeater as a whole (min items / required)
218
+ const repeaterValidation: RHFValidationRules = React.useMemo(() => {
219
+ const rules: RHFValidationRules = {};
220
+ if (effectiveMinItems && effectiveMinItems > 0) {
221
+ rules.validate = {
222
+ minItems: (value: any) => {
223
+ const length = Array.isArray(value) ? value.length : 0;
224
+ return length >= effectiveMinItems || `At least ${effectiveMinItems} item(s) are required`;
225
+ },
226
+ };
227
+ }
228
+ return rules;
229
+ }, [effectiveMinItems]);
230
+
231
+ const repeaterError = getFieldError(errors, name);
232
+
233
+ return (
234
+ <div className="space-y-4">
235
+ {/* Hidden field to attach validation for minItems / required */}
236
+ <input type="hidden" {...register(name, repeaterValidation)} />
237
+ {/* Label and Description */}
238
+ {label && (
239
+ <div className="mb-4">
240
+ <label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{label}</label>
241
+ {helperText && <p className="text-sm text-gray-500 dark:text-gray-400">{helperText}</p>}
242
+ <HintDisplay hint={hint} hintIcon={hintIcon} hintColor={hintColor} />
243
+ </div>
244
+ )}
245
+
246
+ {fields.length === 0 ? (
247
+ /* Empty State */
248
+ <div className="text-center py-12 text-fg-secondary text-sm border-2 border-dashed border-border rounded-lg bg-muted">
249
+ <svg
250
+ className="w-12 h-12 mx-auto mb-3 text-gray-400 dark:text-gray-500"
251
+ fill="none"
252
+ stroke="currentColor"
253
+ viewBox="0 0 24 24">
254
+ <path
255
+ strokeLinecap="round"
256
+ strokeLinejoin="round"
257
+ strokeWidth={2}
258
+ d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
259
+ />
260
+ </svg>
261
+ <p className="mb-4">{translate('core:repeater.empty')}</p>
262
+ </div>
263
+ ) : collapsible ? (
264
+ /* Collapsible/Accordion Mode */
265
+ <div className="space-y-3">
266
+ {fields.map((field, index) => {
267
+ const displayLabel = getItemLabel(index);
268
+ const isCollapsed = collapsedItems.has(index);
269
+
270
+ return (
271
+ <div
272
+ key={field.id}
273
+ className="border border-border rounded-lg bg-surface shadow-sm overflow-hidden">
274
+ {/* Collapsible Header */}
275
+ <div className="flex items-center bg-muted border-b border-border">
276
+ {/* Collapse/Expand Button */}
277
+ <button
278
+ type="button"
279
+ onClick={() => toggleCollapse(index)}
280
+ className="flex items-center flex-1 px-4 py-3 text-sm font-medium text-fg hover:bg-hover transition-colors text-left">
281
+ <svg
282
+ className={`w-5 h-5 mr-2 transition-transform ${isCollapsed ? '' : 'rotate-90'}`}
283
+ fill="none"
284
+ stroke="currentColor"
285
+ viewBox="0 0 24 24">
286
+ <path
287
+ strokeLinecap="round"
288
+ strokeLinejoin="round"
289
+ strokeWidth={2}
290
+ d="M9 5l7 7-7 7"
291
+ />
292
+ </svg>
293
+ <span>{displayLabel}</span>
294
+ </button>
295
+
296
+ {/* Action Buttons */}
297
+ <div className="flex items-center gap-1 px-2 py-2 border-l border-border">
298
+ {/* Move Up Button */}
299
+ {reorderable && (
300
+ <button
301
+ type="button"
302
+ onClick={() => handleMoveUp(index)}
303
+ disabled={!canMoveUp(index)}
304
+ className="p-2 text-fg-secondary hover:text-fg hover:bg-hover rounded disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
305
+ aria-label={translate('core:common.move_up')}
306
+ title={translate('core:common.move_up')}>
307
+ <svg
308
+ className="w-4 h-4"
309
+ fill="none"
310
+ stroke="currentColor"
311
+ viewBox="0 0 24 24">
312
+ <path
313
+ strokeLinecap="round"
314
+ strokeLinejoin="round"
315
+ strokeWidth={2}
316
+ d="M5 15l7-7 7 7"
317
+ />
318
+ </svg>
319
+ </button>
320
+ )}
321
+
322
+ {/* Move Down Button */}
323
+ {reorderable && (
324
+ <button
325
+ type="button"
326
+ onClick={() => handleMoveDown(index)}
327
+ disabled={!canMoveDown(index)}
328
+ className="p-2 text-fg-secondary hover:text-fg hover:bg-hover rounded disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
329
+ aria-label={translate('core:common.move_down')}
330
+ title={translate('core:common.move_down')}>
331
+ <svg
332
+ className="w-4 h-4"
333
+ fill="none"
334
+ stroke="currentColor"
335
+ viewBox="0 0 24 24">
336
+ <path
337
+ strokeLinecap="round"
338
+ strokeLinejoin="round"
339
+ strokeWidth={2}
340
+ d="M19 9l-7 7-7-7"
341
+ />
342
+ </svg>
343
+ </button>
344
+ )}
345
+
346
+ {/* Delete Button */}
347
+ <button
348
+ type="button"
349
+ onClick={() => handleDelete(index)}
350
+ disabled={!canDelete(index)}
351
+ className="p-2 text-red-500 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/30 rounded disabled:opacity-30 disabled:cursor-not-allowed transition-colors"
352
+ aria-label={translate('core:common.delete')}
353
+ title={translate('core:common.delete')}>
354
+ <svg
355
+ className="w-4 h-4"
356
+ fill="none"
357
+ stroke="currentColor"
358
+ viewBox="0 0 24 24">
359
+ <path
360
+ strokeLinecap="round"
361
+ strokeLinejoin="round"
362
+ strokeWidth={2}
363
+ 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"
364
+ />
365
+ </svg>
366
+ </button>
367
+ </div>
368
+ </div>
369
+
370
+ {/* Collapsible Content */}
371
+ {!isCollapsed && (
372
+ <div className="p-6">
373
+ <div className="space-y-4">
374
+ {schema.map((fieldSchema: any) => {
375
+ const nestedField = {
376
+ ...fieldSchema,
377
+ name: `${name}.${index}.${fieldSchema.name}`,
378
+ };
379
+ return (
380
+ <FieldRenderer
381
+ key={fieldSchema.name}
382
+ field={nestedField}
383
+ operation={operation}
384
+ apiBaseUrl={apiBaseUrl}
385
+ resource={resource}
386
+ />
387
+ );
388
+ })}
389
+ </div>
390
+ </div>
391
+ )}
392
+ </div>
393
+ );
394
+ })}
395
+ </div>
396
+ ) : (
397
+ /* Tab Mode (Original) */
398
+ <div className="border border-border rounded-lg bg-surface shadow-sm overflow-hidden">
399
+ {/* Tab Headers */}
400
+ <div className="flex items-center bg-muted border-b border-border overflow-x-auto">
401
+ <div className="flex flex-1 min-w-0">
402
+ {fields.map((field, index) => {
403
+ const displayLabel = getItemLabel(index);
404
+ const isActive = activeTab === index;
405
+
406
+ return (
407
+ <button
408
+ key={field.id}
409
+ type="button"
410
+ onClick={() => setActiveTab(index)}
411
+ className={`
412
+ flex items-center px-4 py-3 text-sm font-medium border-r border-border whitespace-nowrap transition-colors
413
+ ${
414
+ isActive
415
+ ? 'bg-surface text-accent border-b-2 border-b-accent -mb-px'
416
+ : 'text-fg-secondary hover:text-fg hover:bg-hover'
417
+ }
418
+ `}>
419
+ <span className="mr-2">{displayLabel}</span>
420
+ {isActive && (
421
+ <span
422
+ className="w-2 h-2 bg-accent rounded-full"
423
+ aria-label={translate('core:repeater.active_tab')}></span>
424
+ )}
425
+ </button>
426
+ );
427
+ })}
428
+ </div>
429
+
430
+ {/* Action Buttons in Header */}
431
+ <div className="flex items-center gap-1 px-2 py-2 border-l border-border bg-muted">
432
+ {/* Move Left Button */}
433
+ {reorderable && (
434
+ <button
435
+ type="button"
436
+ onClick={() => handleMoveUp(activeTab)}
437
+ disabled={!canMoveUp(activeTab)}
438
+ className="p-2 text-fg-secondary hover:text-fg hover:bg-hover rounded disabled:opacity-30 disabled:cursor-not-allowed focus:outline-none transition-colors"
439
+ aria-label={translate('core:common.move_left')}
440
+ title={translate('core:common.move_left')}>
441
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
442
+ <path
443
+ strokeLinecap="round"
444
+ strokeLinejoin="round"
445
+ strokeWidth={2}
446
+ d="M15 19l-7-7 7-7"
447
+ />
448
+ </svg>
449
+ </button>
450
+ )}
451
+
452
+ {/* Move Right Button */}
453
+ {reorderable && (
454
+ <button
455
+ type="button"
456
+ onClick={() => handleMoveDown(activeTab)}
457
+ disabled={!canMoveDown(activeTab)}
458
+ className="p-2 text-fg-secondary hover:text-fg hover:bg-hover rounded disabled:opacity-30 disabled:cursor-not-allowed focus:outline-none transition-colors"
459
+ aria-label={translate('core:common.move_right')}
460
+ title={translate('core:common.move_right')}>
461
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
462
+ <path
463
+ strokeLinecap="round"
464
+ strokeLinejoin="round"
465
+ strokeWidth={2}
466
+ d="M9 5l7 7-7 7"
467
+ />
468
+ </svg>
469
+ </button>
470
+ )}
471
+
472
+ {/* Delete Button */}
473
+ <button
474
+ type="button"
475
+ onClick={() => handleDelete(activeTab)}
476
+ disabled={!canDelete(activeTab)}
477
+ className="p-2 text-red-500 dark:text-red-400 hover:text-red-700 dark:hover:text-red-300 hover:bg-red-50 dark:hover:bg-red-900/30 rounded disabled:opacity-30 disabled:cursor-not-allowed focus:outline-none transition-colors"
478
+ aria-label={translate('core:common.delete')}
479
+ title={translate('core:common.delete')}>
480
+ <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
481
+ <path
482
+ strokeLinecap="round"
483
+ strokeLinejoin="round"
484
+ strokeWidth={2}
485
+ 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"
486
+ />
487
+ </svg>
488
+ </button>
489
+ </div>
490
+ </div>
491
+
492
+ {/* Tab Content */}
493
+ <div className="p-6">
494
+ {fields.map((field, index) => (
495
+ <div key={field.id} className={activeTab === index ? 'block' : 'hidden'}>
496
+ <div className="space-y-4">
497
+ {schema.map((fieldSchema: any) => {
498
+ // Create a namespaced field for the nested schema
499
+ const nestedField = {
500
+ ...fieldSchema,
501
+ name: `${name}.${index}.${fieldSchema.name}`,
502
+ disabled: disabled || fieldSchema.disabled, // Propagate disabled state
503
+ };
504
+ return (
505
+ <FieldRenderer
506
+ key={fieldSchema.name}
507
+ field={nestedField}
508
+ operation={operation}
509
+ apiBaseUrl={apiBaseUrl}
510
+ resource={resource}
511
+ />
512
+ );
513
+ })}
514
+ </div>
515
+ </div>
516
+ ))}
517
+ </div>
518
+ </div>
519
+ )}
520
+
521
+ {/* Add Button */}
522
+ <PillButton
523
+ type="button"
524
+ onClick={handleAdd}
525
+ disabled={!canAdd}
526
+ className="w-full border-accent/30 bg-accent-soft text-accent hover:bg-accent-soft">
527
+ <span className="flex items-center justify-center">
528
+ <svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
529
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
530
+ </svg>
531
+ {translate('core:common.add')}
532
+ {maxItems !== undefined && ` (${fields.length}/${maxItems})`}
533
+ </span>
534
+ </PillButton>
535
+
536
+ {/* Min/Max Info */}
537
+ {(minItems !== undefined || maxItems !== undefined) && (
538
+ <p className="text-xs text-gray-500 dark:text-gray-400 text-center">
539
+ {minItems !== undefined && `Minimum: ${minItems}`}
540
+ {minItems !== undefined && maxItems !== undefined && ' • '}
541
+ {maxItems !== undefined && `Maximum: ${maxItems}`}
542
+ </p>
543
+ )}
544
+ </div>
545
+ );
546
+ }
@@ -0,0 +1,144 @@
1
+ import React, { useState, useEffect, memo } from 'react';
2
+ import { ViewModal } from './ViewModal';
3
+ import { ResourceFormModal } from './modals/ResourceFormModal';
4
+ import { authenticatedFetch } from '../api/authenticatedFetch';
5
+ import { SerializedForm } from '@maxal_studio/kratosjs';
6
+ import { ModalState } from '../contexts/ResourceModalContext';
7
+ import { useTableRefresh } from '../contexts/TableRefreshContext';
8
+
9
+ interface ResourceModalRendererProps {
10
+ modal: ModalState;
11
+ apiBaseUrl: string;
12
+ onClose: () => void;
13
+ onCloseAll: () => void;
14
+ }
15
+
16
+ /**
17
+ * Renders a single modal from the modal stack
18
+ * Handles fetching form schema and rendering the appropriate modal component
19
+ * Memoized to prevent unnecessary re-renders
20
+ */
21
+ const ResourceModalRendererComponent = ({ modal, apiBaseUrl, onClose, onCloseAll }: ResourceModalRendererProps) => {
22
+ const [formSchema, setFormSchema] = useState<SerializedForm | null>(null);
23
+ const [resourceName, setResourceName] = useState<string>('');
24
+ const [loading, setLoading] = useState(false);
25
+ const { refreshTable } = useTableRefresh();
26
+
27
+ // Fetch form schema when modal opens
28
+ useEffect(() => {
29
+ const fetchFormSchema = async () => {
30
+ setLoading(true);
31
+ try {
32
+ const response = await authenticatedFetch(
33
+ `${apiBaseUrl}/${modal.resource}/schema/form`,
34
+ {
35
+ method: 'GET',
36
+ headers: {
37
+ 'Content-Type': 'application/json',
38
+ },
39
+ },
40
+ apiBaseUrl,
41
+ );
42
+
43
+ if (!response.ok) {
44
+ throw new Error(`Failed to fetch form schema: ${response.status}`);
45
+ }
46
+
47
+ const data = await response.json();
48
+ // API returns 'schema' not 'formSchema'
49
+ setFormSchema({
50
+ ...data.schema,
51
+ canCreate: data.canCreate,
52
+ canEdit: data.canEdit,
53
+ canDelete: data.canDelete,
54
+ canView: data.canView,
55
+ });
56
+ setResourceName(data.resource || data.resourceName || modal.resource);
57
+ } catch (error) {
58
+ console.error('Error fetching form schema:', error);
59
+ onClose(); // Close modal on error
60
+ } finally {
61
+ setLoading(false);
62
+ }
63
+ };
64
+
65
+ fetchFormSchema();
66
+ }, [modal.resource, modal.recordId, modal.mode, apiBaseUrl, onClose]);
67
+
68
+ // Don't render until schema is loaded
69
+ if (loading || !formSchema) {
70
+ return null;
71
+ }
72
+
73
+ const handleSuccess = (createdRecord?: any) => {
74
+ refreshTable(modal.resource);
75
+ onClose(); // Close this modal
76
+ };
77
+
78
+ // Render appropriate modal based on mode
79
+ if (modal.mode === 'view') {
80
+ return (
81
+ <ViewModal
82
+ isOpen={true}
83
+ onClose={onClose}
84
+ recordId={modal.recordId!}
85
+ formSchema={formSchema}
86
+ apiBaseUrl={apiBaseUrl}
87
+ resourceSlug={modal.resource}
88
+ resourceName={resourceName}
89
+ depth={modal.depth}
90
+ onCloseAll={onCloseAll}
91
+ />
92
+ );
93
+ }
94
+
95
+ if (modal.mode === 'edit') {
96
+ return (
97
+ <ResourceFormModal
98
+ isOpen={true}
99
+ onClose={onClose}
100
+ mode="edit"
101
+ recordId={modal.recordId!}
102
+ formSchema={formSchema}
103
+ apiBaseUrl={apiBaseUrl}
104
+ resourceSlug={modal.resource}
105
+ resourceName={resourceName}
106
+ onSuccess={handleSuccess}
107
+ depth={modal.depth}
108
+ onCloseAll={onCloseAll}
109
+ copyUrlPath={`/${modal.resource}/${modal.recordId}/edit`}
110
+ />
111
+ );
112
+ }
113
+
114
+ if (modal.mode === 'create') {
115
+ return (
116
+ <ResourceFormModal
117
+ isOpen={true}
118
+ onClose={onClose}
119
+ mode="create"
120
+ formSchema={formSchema}
121
+ apiBaseUrl={apiBaseUrl}
122
+ resourceSlug={modal.resource}
123
+ resourceName={resourceName}
124
+ onSuccess={handleSuccess}
125
+ depth={modal.depth}
126
+ onCloseAll={onCloseAll}
127
+ />
128
+ );
129
+ }
130
+
131
+ return null;
132
+ };
133
+
134
+ // Memoize to prevent unnecessary re-renders when other modals in stack change
135
+ export const ResourceModalRenderer = memo(ResourceModalRendererComponent, (prevProps, nextProps) => {
136
+ // Only re-render if the modal itself changes
137
+ return (
138
+ prevProps.modal.resource === nextProps.modal.resource &&
139
+ prevProps.modal.recordId === nextProps.modal.recordId &&
140
+ prevProps.modal.mode === nextProps.modal.mode &&
141
+ prevProps.modal.depth === nextProps.modal.depth &&
142
+ prevProps.apiBaseUrl === nextProps.apiBaseUrl
143
+ );
144
+ });