@goplusvn/core 0.1.0 → 0.1.2

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 (591) hide show
  1. package/package.json +31 -175
  2. package/src/assets/erp_wallpaper.png +0 -0
  3. package/src/assets/goeat_logo.png +0 -0
  4. package/src/audit/audit-manager.ts +139 -0
  5. package/src/audit/index.ts +11 -0
  6. package/src/audit/memory-audit-logger.ts +86 -0
  7. package/src/audit/types.ts +50 -0
  8. package/src/auth/auth-service.ts +97 -0
  9. package/src/auth/index.ts +266 -0
  10. package/src/code-generation/index.ts +69 -0
  11. package/src/configs/auth-routes.ts +17 -0
  12. package/src/configs/crud.ts +136 -0
  13. package/src/configs/data/navigations.ts +781 -0
  14. package/src/configs/data/oauth-links.ts +10 -0
  15. package/src/configs/entities/material-categories.config.ts +125 -0
  16. package/src/configs/i18n.ts +12 -0
  17. package/src/configs/index.ts +26 -0
  18. package/src/configs/status.ts +25 -0
  19. package/src/configs/themes.ts +100 -0
  20. package/src/crud/components/crud-bulk-actions.tsx +91 -0
  21. package/src/crud/components/crud-card-view.tsx +241 -0
  22. package/src/crud/components/crud-context.tsx +122 -0
  23. package/src/crud/components/crud-delete-dialog.tsx +145 -0
  24. package/src/crud/components/crud-dialog.tsx +406 -0
  25. package/src/crud/components/crud-empty-state.tsx +104 -0
  26. package/src/crud/components/crud-export-button.tsx +170 -0
  27. package/src/crud/components/crud-field-renderer.tsx +653 -0
  28. package/src/crud/components/crud-filter-chips.tsx +102 -0
  29. package/src/crud/components/crud-filters/checkbox-filter.tsx +97 -0
  30. package/src/crud/components/crud-filters/datetime-filter.tsx +83 -0
  31. package/src/crud/components/crud-filters/filter-builder.tsx +66 -0
  32. package/src/crud/components/crud-filters/index.tsx +76 -0
  33. package/src/crud/components/crud-filters/radio-filter.tsx +86 -0
  34. package/src/crud/components/crud-filters/select-filter.tsx +141 -0
  35. package/src/crud/components/crud-filters/text-filter.tsx +86 -0
  36. package/src/crud/components/crud-form.tsx +642 -0
  37. package/src/crud/components/crud-import-dialog.tsx +440 -0
  38. package/src/crud/components/crud-infinite-scroll.tsx +116 -0
  39. package/src/crud/components/crud-page.tsx +1017 -0
  40. package/src/crud/components/crud-provider.tsx +277 -0
  41. package/src/crud/components/crud-row-actions.tsx +189 -0
  42. package/src/crud/components/crud-search.tsx +82 -0
  43. package/src/crud/components/crud-sheet.tsx +336 -0
  44. package/src/crud/components/crud-table-skeleton.tsx +26 -0
  45. package/src/crud/components/crud-table-toolbar.tsx +91 -0
  46. package/src/crud/components/crud-table.tsx +352 -0
  47. package/src/crud/components/crud-virtual-table.tsx +55 -0
  48. package/src/crud/components/index.tsx +20 -0
  49. package/src/crud/crud-filters/checkbox-filter.tsx +87 -0
  50. package/src/crud/crud-filters/datetime-filter.tsx +82 -0
  51. package/src/crud/crud-filters/filter-builder.tsx +64 -0
  52. package/src/crud/crud-filters/index.tsx +78 -0
  53. package/src/crud/crud-filters/radio-filter.tsx +79 -0
  54. package/src/crud/crud-filters/select-filter.tsx +148 -0
  55. package/src/crud/crud-filters/text-filter.tsx +81 -0
  56. package/src/crud/index.ts +43 -0
  57. package/src/crud/lib/crud-service.test.ts +334 -0
  58. package/src/crud/lib/crud-service.ts +358 -0
  59. package/src/crud/lib/crud-utils.test.ts +354 -0
  60. package/src/crud/lib/crud-utils.ts +299 -0
  61. package/src/crud/lib/crud-validator.ts +247 -0
  62. package/src/crud/lib/data-loader.ts +234 -0
  63. package/src/crud/lib/field-calculator.ts +241 -0
  64. package/src/crud/lib/field-formatter.ts +240 -0
  65. package/src/crud/lib/import-export-service.test.ts +290 -0
  66. package/src/crud/lib/import-export-service.ts +352 -0
  67. package/src/crud/lib/import-server-utils.ts +109 -0
  68. package/src/crud/lib/lazy-loader.ts +241 -0
  69. package/src/crud/lib/parse-filters.ts +85 -0
  70. package/src/crud/lib/permissions.ts +52 -0
  71. package/src/crud/lib/serialize-config.ts +60 -0
  72. package/src/crud/lib/stream-loader.ts +145 -0
  73. package/src/crud/lib/translate-config.ts +335 -0
  74. package/src/crud/lib/types.ts +11 -0
  75. package/src/crud/pages/entity-crud-page.tsx +144 -0
  76. package/src/crud/server.ts +8 -0
  77. package/src/home/constants.tsx +142 -0
  78. package/src/home/feature-showcase.tsx +171 -0
  79. package/src/home/home-page.tsx +191 -0
  80. package/src/home/hooks/index.ts +1 -0
  81. package/src/home/hooks/useWidgetPreferences.ts +167 -0
  82. package/src/home/index.ts +33 -0
  83. package/src/home/quick-access-dialog.tsx +271 -0
  84. package/src/home/quick-access-menu.tsx +267 -0
  85. package/src/home/types.ts +140 -0
  86. package/src/home/welcome-card.tsx +92 -0
  87. package/src/home/widget-container.tsx +258 -0
  88. package/src/home/widgets/base-widget.tsx +200 -0
  89. package/src/home/widgets/customers-widget.tsx +74 -0
  90. package/src/home/widgets/index.ts +6 -0
  91. package/src/home/widgets/orders-widget.tsx +87 -0
  92. package/src/home/widgets/revenue-widget.tsx +71 -0
  93. package/src/home/widgets/stock-widget.tsx +109 -0
  94. package/src/hooks/index.tsx +598 -0
  95. package/src/hooks/use-tenant.test.tsx +30 -0
  96. package/src/hooks/use-tenant.ts +5 -0
  97. package/src/index.ts +17 -0
  98. package/src/infrastructure/__tests__/architecture-verification.spec.ts +103 -0
  99. package/src/infrastructure/api-service.ts +317 -0
  100. package/src/infrastructure/cache/cache-manager.ts +107 -0
  101. package/src/infrastructure/cache/cache.ts +120 -0
  102. package/src/infrastructure/cache/index.ts +8 -0
  103. package/src/infrastructure/cache/types.ts +48 -0
  104. package/src/infrastructure/cron/cron-manager.ts +239 -0
  105. package/src/infrastructure/cron/index.ts +6 -0
  106. package/src/infrastructure/cron/types.ts +41 -0
  107. package/src/infrastructure/event-bus/event-bus.ts +145 -0
  108. package/src/infrastructure/event-bus/index.ts +2 -0
  109. package/src/infrastructure/event-bus/types.ts +22 -0
  110. package/src/infrastructure/index.ts +32 -0
  111. package/src/infrastructure/lock/decorators.ts +67 -0
  112. package/src/infrastructure/lock/index.ts +2 -0
  113. package/src/infrastructure/lock/lock-manager.ts +33 -0
  114. package/src/infrastructure/logger/index.ts +2 -0
  115. package/src/infrastructure/logger/logger.ts +96 -0
  116. package/src/infrastructure/logger/types.ts +25 -0
  117. package/src/layout/index.tsx +185 -0
  118. package/src/navigation/index.ts +91 -0
  119. package/src/notification/index.ts +14 -0
  120. package/src/notification/notification-service.ts +120 -0
  121. package/src/notification/storage/in-memory.ts +56 -0
  122. package/src/notification/storage/index.ts +1 -0
  123. package/src/notification/types.ts +51 -0
  124. package/src/organization/branch-service.ts +299 -0
  125. package/src/organization/branches.config.ts +154 -0
  126. package/src/organization/index.ts +5 -0
  127. package/src/plugin/apps-registry.ts +97 -0
  128. package/src/plugin/index.ts +5 -0
  129. package/src/plugin/types.ts +41 -0
  130. package/src/providers/index.tsx +109 -0
  131. package/src/providers/tenant-provider.tsx +45 -0
  132. package/src/rbac/components/roles/role-card.tsx +158 -0
  133. package/src/rbac/components/roles/role-stats-cards.tsx +29 -0
  134. package/src/rbac/components/roles/role-toolbar.tsx +123 -0
  135. package/src/rbac/hooks/use-role-operations.ts +159 -0
  136. package/src/rbac/hooks/use-roles-data.ts +59 -0
  137. package/src/rbac/index.ts +297 -0
  138. package/src/rbac/lib/permission-helpers.ts +63 -0
  139. package/src/rbac/pages/action-list-page.tsx +25 -0
  140. package/src/rbac/pages/resource-list-page.tsx +25 -0
  141. package/src/rbac/pages/role-list-page.tsx +378 -0
  142. package/src/rbac/permission-service.ts +140 -0
  143. package/src/rbac/permissions.ts +135 -0
  144. package/src/rbac/resource-service.ts +115 -0
  145. package/src/rbac/resource-validator.ts +119 -0
  146. package/src/rbac/role-service.ts +165 -0
  147. package/src/rbac/server.ts +16 -0
  148. package/src/rbac/types.ts +38 -0
  149. package/src/schemas/action.schema.ts +66 -0
  150. package/src/schemas/branch.schema.ts +52 -0
  151. package/src/schemas/coming-soon-schema.ts +9 -0
  152. package/src/schemas/company.schema.ts +44 -0
  153. package/src/schemas/forgot-passward-schema.ts +9 -0
  154. package/src/schemas/index.ts +30 -0
  155. package/src/schemas/material-category.schema.ts +43 -0
  156. package/src/schemas/material-pricing.schema.ts +74 -0
  157. package/src/schemas/material.schema.ts +76 -0
  158. package/src/schemas/materials.ts +52 -0
  159. package/src/schemas/new-passward-schema.ts +15 -0
  160. package/src/schemas/partner-company.schema.ts +149 -0
  161. package/src/schemas/register-schema.ts +36 -0
  162. package/src/schemas/resource.schema.ts +133 -0
  163. package/src/schemas/role.schema.ts +11 -0
  164. package/src/schemas/sign-in-schema.ts +24 -0
  165. package/src/schemas/supplier-pricing.schema.ts +15 -0
  166. package/src/schemas/supplier.schema.ts +120 -0
  167. package/src/schemas/system-category-group.schema.ts +67 -0
  168. package/src/schemas/system-category.schema.ts +77 -0
  169. package/src/schemas/system-config.schema.ts +118 -0
  170. package/src/schemas/uom.schema.ts +75 -0
  171. package/src/schemas/user-supplier.schema.ts +179 -0
  172. package/src/schemas/user.schema.ts +18 -0
  173. package/src/schemas/verify-email-schema.ts +9 -0
  174. package/src/schemas/warehouse.schema.ts +49 -0
  175. package/src/system/components/categories/category-list.tsx +529 -0
  176. package/src/system/components/categories/category-manager.tsx +89 -0
  177. package/src/system/components/categories/group-sidebar.tsx +308 -0
  178. package/src/system/components/settings/setting-dialogs.tsx +197 -0
  179. package/src/system/components/settings/setting-field.tsx +291 -0
  180. package/src/system/components/settings/setting-form-dialog.tsx +308 -0
  181. package/src/system/components/settings/settings-groups.ts +80 -0
  182. package/src/system/components/settings/settings-search.tsx +71 -0
  183. package/src/system/components/settings/settings-section.tsx +74 -0
  184. package/src/system/components/settings/settings-sidebar.tsx +81 -0
  185. package/src/system/constants.ts +3 -0
  186. package/src/system/index.ts +150 -0
  187. package/src/system/job-manager.ts +176 -0
  188. package/src/system/pages/components/categories/category-list.tsx +537 -0
  189. package/src/system/pages/components/categories/category-manager.tsx +90 -0
  190. package/src/system/pages/components/categories/group-sidebar.tsx +311 -0
  191. package/src/system/pages/components/settings/sales-rules-settings.tsx +222 -0
  192. package/src/system/pages/components/settings/setting-dialogs.tsx +197 -0
  193. package/src/system/pages/components/settings/setting-field.tsx +292 -0
  194. package/src/system/pages/components/settings/setting-form-dialog.tsx +308 -0
  195. package/src/system/pages/components/settings/settings-groups.ts +87 -0
  196. package/src/system/pages/components/settings/settings-page.tsx +372 -0
  197. package/src/system/pages/components/settings/settings-search.tsx +71 -0
  198. package/src/system/pages/components/settings/settings-section.tsx +74 -0
  199. package/src/system/pages/components/settings/settings-sidebar.tsx +81 -0
  200. package/src/system/pages/components/settings/system-settings.tsx +244 -0
  201. package/src/system/pages/system-category-page.tsx +15 -0
  202. package/src/system/pages/system-settings-page.tsx +380 -0
  203. package/src/system/schemas/system-category-group.schema.ts +46 -0
  204. package/src/system/schemas/system-category.schema.ts +56 -0
  205. package/src/system/services/settings-service.ts +127 -0
  206. package/src/system/services/system-category-service.ts +63 -0
  207. package/src/system/types.ts +45 -0
  208. package/src/types/index.ts +703 -0
  209. package/src/ui/auth/auth-layout.tsx +135 -0
  210. package/src/ui/auth/forgot-password-form.tsx +98 -0
  211. package/src/ui/auth/index.tsx +7 -0
  212. package/src/ui/auth/new-password-form.tsx +107 -0
  213. package/src/ui/auth/oauth-links.tsx +30 -0
  214. package/src/ui/auth/register-form.tsx +202 -0
  215. package/src/ui/auth/sign-in-form.tsx +238 -0
  216. package/src/ui/auth/verify-email-form.tsx +104 -0
  217. package/src/ui/crud/index.tsx +10 -0
  218. package/src/ui/data-display/accordion.tsx +65 -0
  219. package/src/ui/data-display/aspect-ratio.tsx +11 -0
  220. package/src/ui/data-display/avatar.tsx +163 -0
  221. package/src/ui/data-display/bento-grid.tsx +77 -0
  222. package/src/ui/data-display/carousel.tsx +249 -0
  223. package/src/ui/data-display/chart.tsx +363 -0
  224. package/src/ui/data-display/code-block-highlight.tsx +54 -0
  225. package/src/ui/data-display/collapsible.tsx +42 -0
  226. package/src/ui/data-display/compact-stat-bar.tsx +149 -0
  227. package/src/ui/data-display/data-table/data-table-context.tsx +255 -0
  228. package/src/ui/data-display/data-table/data-table-empty-state.tsx +133 -0
  229. package/src/ui/data-display/data-table/data-table-skeleton.tsx +145 -0
  230. package/src/ui/data-display/data-table/data-table-toolbar.tsx +353 -0
  231. package/src/ui/data-display/data-table/data-table.tsx +597 -0
  232. package/src/ui/data-display/data-table/index.ts +44 -0
  233. package/src/ui/data-display/data-table-column-header.tsx +75 -0
  234. package/src/ui/data-display/data-table-pagination.tsx +130 -0
  235. package/src/ui/data-display/data-table-view-options.tsx +59 -0
  236. package/src/ui/data-display/formatted-number-input.tsx +210 -0
  237. package/src/ui/data-display/highlight.tsx +20 -0
  238. package/src/ui/data-display/hover-card.tsx +48 -0
  239. package/src/ui/data-display/index.tsx +50 -0
  240. package/src/ui/data-display/iphone-15-pro.tsx +114 -0
  241. package/src/ui/data-display/kanban/index.ts +4 -0
  242. package/src/ui/data-display/kanban/kanban-board.tsx +192 -0
  243. package/src/ui/data-display/kanban/kanban-column.tsx +74 -0
  244. package/src/ui/data-display/kanban/kanban-item.tsx +50 -0
  245. package/src/ui/data-display/kanban/kanban-types.ts +21 -0
  246. package/src/ui/data-display/kpi-card.tsx +68 -0
  247. package/src/ui/data-display/media-grid.tsx +110 -0
  248. package/src/ui/data-display/safari.tsx +175 -0
  249. package/src/ui/data-display/show-more-text.tsx +55 -0
  250. package/src/ui/data-display/tabs.tsx +68 -0
  251. package/src/ui/data-display/timeline.tsx +256 -0
  252. package/src/ui/feedback/alert.tsx +60 -0
  253. package/src/ui/feedback/context-menu.tsx +245 -0
  254. package/src/ui/feedback/drawer.tsx +132 -0
  255. package/src/ui/feedback/error-dialog.tsx +273 -0
  256. package/src/ui/feedback/index.tsx +183 -0
  257. package/src/ui/feedback/progress.tsx +32 -0
  258. package/src/ui/feedback/sheet.tsx +148 -0
  259. package/src/ui/feedback/sonner.tsx +36 -0
  260. package/src/ui/forms/command.tsx +157 -0
  261. package/src/ui/forms/date-picker.tsx +73 -0
  262. package/src/ui/forms/date-range-picker.tsx +76 -0
  263. package/src/ui/forms/date-time-picker.tsx +109 -0
  264. package/src/ui/forms/editor/editor-menu-bar.tsx +394 -0
  265. package/src/ui/forms/editor/index.tsx +130 -0
  266. package/src/ui/forms/editor/multi-select-example.tsx +1234 -0
  267. package/src/ui/forms/emoji-picker.tsx +109 -0
  268. package/src/ui/forms/file-dropzone.tsx +169 -0
  269. package/src/ui/forms/file-thumbnail.tsx +29 -0
  270. package/src/ui/forms/index.tsx +201 -0
  271. package/src/ui/forms/input-file.tsx +99 -0
  272. package/src/ui/forms/input-group.tsx +46 -0
  273. package/src/ui/forms/input-otp.tsx +81 -0
  274. package/src/ui/forms/input-phone.tsx +172 -0
  275. package/src/ui/forms/input-spin.tsx +116 -0
  276. package/src/ui/forms/input-tags.tsx +219 -0
  277. package/src/ui/forms/input-time.tsx +42 -0
  278. package/src/ui/forms/multi-select.tsx +629 -0
  279. package/src/ui/forms/multiple-date-picker.tsx +74 -0
  280. package/src/ui/forms/radio-group.tsx +42 -0
  281. package/src/ui/forms/rating.tsx +158 -0
  282. package/src/ui/forms/time-picker.tsx +57 -0
  283. package/src/ui/index.tsx +17 -0
  284. package/src/ui/layout/animated-list.tsx +77 -0
  285. package/src/ui/layout/animated-sidebar.tsx +294 -0
  286. package/src/ui/layout/command-menu.tsx +355 -0
  287. package/src/ui/layout/customizer.tsx +324 -0
  288. package/src/ui/layout/footer.tsx +43 -0
  289. package/src/ui/layout/full-screen-toggle.tsx +52 -0
  290. package/src/ui/layout/header-breadcrumb.tsx +77 -0
  291. package/src/ui/layout/horizontal-layout-header.tsx +83 -0
  292. package/src/ui/layout/horizontal-layout.tsx +50 -0
  293. package/src/ui/layout/index.tsx +25 -0
  294. package/src/ui/layout/language-dropdown.tsx +103 -0
  295. package/src/ui/layout/logo.tsx +63 -0
  296. package/src/ui/layout/main-layout.tsx +57 -0
  297. package/src/ui/layout/mode-dropdown.tsx +58 -0
  298. package/src/ui/layout/notification-dropdown.tsx +127 -0
  299. package/src/ui/layout/page-tabs.tsx +306 -0
  300. package/src/ui/layout/route-cache.tsx +214 -0
  301. package/src/ui/layout/sidebar-group-icon-menu.tsx +195 -0
  302. package/src/ui/layout/sidebar.tsx +279 -0
  303. package/src/ui/layout/tab-content-cache.tsx +201 -0
  304. package/src/ui/layout/tab-navigation-provider.tsx +536 -0
  305. package/src/ui/layout/toggle-mobile-sidebar.tsx +33 -0
  306. package/src/ui/layout/top-bar-header-menubar.tsx +412 -0
  307. package/src/ui/layout/user-dropdown.tsx +188 -0
  308. package/src/ui/layout/vertical-layout-header.tsx +65 -0
  309. package/src/ui/layout/vertical-layout.tsx +47 -0
  310. package/src/ui/management/audit-log-page.tsx +209 -0
  311. package/src/ui/management/cache-management.tsx +349 -0
  312. package/src/ui/management/index.ts +3 -0
  313. package/src/ui/management/job-management.tsx +308 -0
  314. package/src/ui/pages/not-found.tsx +30 -0
  315. package/src/ui/primitives/badge.tsx +66 -0
  316. package/src/ui/primitives/breadcrumb.tsx +103 -0
  317. package/src/ui/primitives/button.tsx +129 -0
  318. package/src/ui/primitives/calendar.tsx +74 -0
  319. package/src/ui/primitives/card.tsx +86 -0
  320. package/src/ui/primitives/checkbox.tsx +31 -0
  321. package/src/ui/primitives/client.ts +30 -0
  322. package/src/ui/primitives/combobox.tsx +290 -0
  323. package/src/ui/primitives/dialog.tsx +121 -0
  324. package/src/ui/primitives/dropdown-menu.tsx +239 -0
  325. package/src/ui/primitives/dynamic-icon.tsx +24 -0
  326. package/src/ui/primitives/index.tsx +134 -0
  327. package/src/ui/primitives/input-number.tsx +131 -0
  328. package/src/ui/primitives/input.tsx +22 -0
  329. package/src/ui/primitives/keyboard.tsx +23 -0
  330. package/src/ui/primitives/label.tsx +24 -0
  331. package/src/ui/primitives/menubar.tsx +262 -0
  332. package/src/ui/primitives/navigation-menu.tsx +157 -0
  333. package/src/ui/primitives/pagination.tsx +118 -0
  334. package/src/ui/primitives/popover.tsx +56 -0
  335. package/src/ui/primitives/prefetch-link.tsx +60 -0
  336. package/src/ui/primitives/resizable.tsx +59 -0
  337. package/src/ui/primitives/scroll-area.tsx +63 -0
  338. package/src/ui/primitives/select.tsx +172 -0
  339. package/src/ui/primitives/separator.tsx +51 -0
  340. package/src/ui/primitives/sidebar.tsx +844 -0
  341. package/src/ui/primitives/slider.tsx +27 -0
  342. package/src/ui/primitives/status-badge.tsx +47 -0
  343. package/src/ui/primitives/sticky-layout.tsx +50 -0
  344. package/src/ui/primitives/switch.tsx +29 -0
  345. package/src/ui/primitives/table.tsx +116 -0
  346. package/src/ui/primitives/tabs.tsx +55 -0
  347. package/src/ui/primitives/toggle-group.tsx +70 -0
  348. package/src/ui/primitives/toggle.tsx +47 -0
  349. package/src/ui/primitives/tooltip.tsx +59 -0
  350. package/src/user/components/dangerous-zone.tsx +34 -0
  351. package/src/user/components/delete-account-form.tsx +40 -0
  352. package/src/user/components/index.ts +4 -0
  353. package/src/user/components/profile-info-form.tsx +390 -0
  354. package/src/user/components/profile-info.tsx +32 -0
  355. package/src/user/components/unified-profile-dialog.tsx +1019 -0
  356. package/src/user/components/user-stats.tsx +27 -0
  357. package/src/user/components/user-toolbar.tsx +137 -0
  358. package/src/user/components/users-card-view.tsx +253 -0
  359. package/src/user/index.ts +11 -0
  360. package/src/user/pages/user-list-page.tsx +234 -0
  361. package/src/user/pages/users-client-page.tsx +385 -0
  362. package/src/user/profile-page.tsx +19 -0
  363. package/src/user/schemas.ts +68 -0
  364. package/src/user/types.ts +34 -0
  365. package/src/user/user-service.ts +538 -0
  366. package/src/utils/index.ts +906 -0
  367. package/src/workflow/activity-timeline.tsx +412 -0
  368. package/src/workflow/approval-workflow.tsx +31 -0
  369. package/src/workflow/index.ts +2 -0
  370. package/dist/audit/index.d.mts +0 -115
  371. package/dist/audit/index.d.ts +0 -115
  372. package/dist/audit/index.js +0 -204
  373. package/dist/audit/index.js.map +0 -1
  374. package/dist/audit/index.mjs +0 -200
  375. package/dist/audit/index.mjs.map +0 -1
  376. package/dist/auth/index.d.mts +0 -86
  377. package/dist/auth/index.d.ts +0 -86
  378. package/dist/auth/index.js +0 -210
  379. package/dist/auth/index.js.map +0 -1
  380. package/dist/auth/index.mjs +0 -198
  381. package/dist/auth/index.mjs.map +0 -1
  382. package/dist/button-1dWvP9Ib.d.mts +0 -30
  383. package/dist/button-1dWvP9Ib.d.ts +0 -30
  384. package/dist/calendar-2QzdEo1z.d.mts +0 -20
  385. package/dist/calendar-2QzdEo1z.d.ts +0 -20
  386. package/dist/code-generation/index.d.mts +0 -30
  387. package/dist/code-generation/index.d.ts +0 -30
  388. package/dist/code-generation/index.js +0 -31
  389. package/dist/code-generation/index.js.map +0 -1
  390. package/dist/code-generation/index.mjs +0 -28
  391. package/dist/code-generation/index.mjs.map +0 -1
  392. package/dist/configs/index.d.mts +0 -175
  393. package/dist/configs/index.d.ts +0 -175
  394. package/dist/configs/index.js +0 -254
  395. package/dist/configs/index.js.map +0 -1
  396. package/dist/configs/index.mjs +0 -233
  397. package/dist/configs/index.mjs.map +0 -1
  398. package/dist/crud/index.d.mts +0 -646
  399. package/dist/crud/index.d.ts +0 -646
  400. package/dist/crud/index.js +0 -11772
  401. package/dist/crud/index.js.map +0 -1
  402. package/dist/crud/index.mjs +0 -11665
  403. package/dist/crud/index.mjs.map +0 -1
  404. package/dist/crud/server.d.mts +0 -20
  405. package/dist/crud/server.d.ts +0 -20
  406. package/dist/crud/server.js +0 -123
  407. package/dist/crud/server.js.map +0 -1
  408. package/dist/crud/server.mjs +0 -120
  409. package/dist/crud/server.mjs.map +0 -1
  410. package/dist/data-table-skeleton-12NA8Mjx.d.mts +0 -39
  411. package/dist/data-table-skeleton-12NA8Mjx.d.ts +0 -39
  412. package/dist/dialog-bKfjZMTd.d.mts +0 -22
  413. package/dist/dialog-bKfjZMTd.d.ts +0 -22
  414. package/dist/dynamic-icon-DrGIiu2N.d.mts +0 -10
  415. package/dist/dynamic-icon-DrGIiu2N.d.ts +0 -10
  416. package/dist/home/index.d.mts +0 -269
  417. package/dist/home/index.d.ts +0 -269
  418. package/dist/home/index.js +0 -1678
  419. package/dist/home/index.js.map +0 -1
  420. package/dist/home/index.mjs +0 -1635
  421. package/dist/home/index.mjs.map +0 -1
  422. package/dist/hooks/index.d.mts +0 -7
  423. package/dist/hooks/index.d.ts +0 -7
  424. package/dist/hooks/index.js +0 -8316
  425. package/dist/hooks/index.js.map +0 -1
  426. package/dist/hooks/index.mjs +0 -8255
  427. package/dist/hooks/index.mjs.map +0 -1
  428. package/dist/index-50hpiPrV.d.ts +0 -116
  429. package/dist/index-B9zQVEVi.d.mts +0 -116
  430. package/dist/index.d.mts +0 -5
  431. package/dist/index.d.ts +0 -5
  432. package/dist/index.js +0 -123
  433. package/dist/index.js.map +0 -1
  434. package/dist/index.mjs +0 -118
  435. package/dist/index.mjs.map +0 -1
  436. package/dist/infrastructure/index.d.mts +0 -423
  437. package/dist/infrastructure/index.d.ts +0 -423
  438. package/dist/infrastructure/index.js +0 -633
  439. package/dist/infrastructure/index.js.map +0 -1
  440. package/dist/infrastructure/index.mjs +0 -619
  441. package/dist/infrastructure/index.mjs.map +0 -1
  442. package/dist/label-DWTEkNPo.d.ts +0 -226
  443. package/dist/label-LPpdcoBx.d.mts +0 -226
  444. package/dist/layout/index.d.mts +0 -48
  445. package/dist/layout/index.d.ts +0 -48
  446. package/dist/layout/index.js +0 -117
  447. package/dist/layout/index.js.map +0 -1
  448. package/dist/layout/index.mjs +0 -90
  449. package/dist/layout/index.mjs.map +0 -1
  450. package/dist/navigation/index.d.mts +0 -16
  451. package/dist/navigation/index.d.ts +0 -16
  452. package/dist/navigation/index.js +0 -53
  453. package/dist/navigation/index.js.map +0 -1
  454. package/dist/navigation/index.mjs +0 -50
  455. package/dist/navigation/index.mjs.map +0 -1
  456. package/dist/notification/index.d.mts +0 -105
  457. package/dist/notification/index.d.ts +0 -105
  458. package/dist/notification/index.js +0 -278
  459. package/dist/notification/index.js.map +0 -1
  460. package/dist/notification/index.mjs +0 -274
  461. package/dist/notification/index.mjs.map +0 -1
  462. package/dist/organization/index.d.mts +0 -99
  463. package/dist/organization/index.d.ts +0 -99
  464. package/dist/organization/index.js +0 -360
  465. package/dist/organization/index.js.map +0 -1
  466. package/dist/organization/index.mjs +0 -352
  467. package/dist/organization/index.mjs.map +0 -1
  468. package/dist/plugin/index.d.mts +0 -83
  469. package/dist/plugin/index.d.ts +0 -83
  470. package/dist/plugin/index.js +0 -86
  471. package/dist/plugin/index.js.map +0 -1
  472. package/dist/plugin/index.mjs +0 -84
  473. package/dist/plugin/index.mjs.map +0 -1
  474. package/dist/providers/index.d.mts +0 -25
  475. package/dist/providers/index.d.ts +0 -25
  476. package/dist/providers/index.js +0 -84
  477. package/dist/providers/index.js.map +0 -1
  478. package/dist/providers/index.mjs +0 -77
  479. package/dist/providers/index.mjs.map +0 -1
  480. package/dist/rbac/index.d.mts +0 -226
  481. package/dist/rbac/index.d.ts +0 -226
  482. package/dist/rbac/index.js +0 -4784
  483. package/dist/rbac/index.js.map +0 -1
  484. package/dist/rbac/index.mjs +0 -4722
  485. package/dist/rbac/index.mjs.map +0 -1
  486. package/dist/rbac/permissions.d.mts +0 -26
  487. package/dist/rbac/permissions.d.ts +0 -26
  488. package/dist/rbac/permissions.js +0 -94
  489. package/dist/rbac/permissions.js.map +0 -1
  490. package/dist/rbac/permissions.mjs +0 -90
  491. package/dist/rbac/permissions.mjs.map +0 -1
  492. package/dist/rbac/server.d.mts +0 -1
  493. package/dist/rbac/server.d.ts +0 -1
  494. package/dist/rbac/server.js +0 -128
  495. package/dist/rbac/server.js.map +0 -1
  496. package/dist/rbac/server.mjs +0 -124
  497. package/dist/rbac/server.mjs.map +0 -1
  498. package/dist/schemas/index.d.mts +0 -1257
  499. package/dist/schemas/index.d.ts +0 -1257
  500. package/dist/schemas/index.js +0 -572
  501. package/dist/schemas/index.js.map +0 -1
  502. package/dist/schemas/index.mjs +0 -523
  503. package/dist/schemas/index.mjs.map +0 -1
  504. package/dist/server-QuYCTa89.d.mts +0 -83
  505. package/dist/server-QuYCTa89.d.ts +0 -83
  506. package/dist/sonner-C74GlRDQ.d.mts +0 -71
  507. package/dist/sonner-C74GlRDQ.d.ts +0 -71
  508. package/dist/status-BOXZgIqX.d.mts +0 -12
  509. package/dist/status-BOXZgIqX.d.ts +0 -12
  510. package/dist/system/index.d.mts +0 -77
  511. package/dist/system/index.d.ts +0 -77
  512. package/dist/system/index.js +0 -102
  513. package/dist/system/index.js.map +0 -1
  514. package/dist/system/index.mjs +0 -100
  515. package/dist/system/index.mjs.map +0 -1
  516. package/dist/tabs-C6FfBwPY.d.mts +0 -18
  517. package/dist/tabs-C6FfBwPY.d.ts +0 -18
  518. package/dist/tenant-provider-B8eC_Wpb.d.mts +0 -27
  519. package/dist/tenant-provider-B8eC_Wpb.d.ts +0 -27
  520. package/dist/types/index.d.mts +0 -469
  521. package/dist/types/index.d.ts +0 -469
  522. package/dist/types/index.js +0 -25
  523. package/dist/types/index.js.map +0 -1
  524. package/dist/types/index.mjs +0 -21
  525. package/dist/types/index.mjs.map +0 -1
  526. package/dist/ui/auth.d.mts +0 -39
  527. package/dist/ui/auth.d.ts +0 -39
  528. package/dist/ui/auth.js +0 -4941
  529. package/dist/ui/auth.js.map +0 -1
  530. package/dist/ui/auth.mjs +0 -4896
  531. package/dist/ui/auth.mjs.map +0 -1
  532. package/dist/ui/crud.d.mts +0 -2
  533. package/dist/ui/crud.d.ts +0 -2
  534. package/dist/ui/crud.js +0 -4
  535. package/dist/ui/crud.js.map +0 -1
  536. package/dist/ui/crud.mjs +0 -3
  537. package/dist/ui/crud.mjs.map +0 -1
  538. package/dist/ui/data-display.d.mts +0 -596
  539. package/dist/ui/data-display.d.ts +0 -596
  540. package/dist/ui/data-display.js +0 -5307
  541. package/dist/ui/data-display.js.map +0 -1
  542. package/dist/ui/data-display.mjs +0 -5212
  543. package/dist/ui/data-display.mjs.map +0 -1
  544. package/dist/ui/feedback.d.mts +0 -55
  545. package/dist/ui/feedback.d.ts +0 -55
  546. package/dist/ui/feedback.js +0 -2608
  547. package/dist/ui/feedback.js.map +0 -1
  548. package/dist/ui/feedback.mjs +0 -2526
  549. package/dist/ui/feedback.mjs.map +0 -1
  550. package/dist/ui/forms.d.mts +0 -309
  551. package/dist/ui/forms.d.ts +0 -309
  552. package/dist/ui/forms.js +0 -4656
  553. package/dist/ui/forms.js.map +0 -1
  554. package/dist/ui/forms.mjs +0 -4571
  555. package/dist/ui/forms.mjs.map +0 -1
  556. package/dist/ui/index.d.mts +0 -331
  557. package/dist/ui/index.d.ts +0 -331
  558. package/dist/ui/index.js +0 -16953
  559. package/dist/ui/index.js.map +0 -1
  560. package/dist/ui/index.mjs +0 -16598
  561. package/dist/ui/index.mjs.map +0 -1
  562. package/dist/ui/primitives/client.d.mts +0 -61
  563. package/dist/ui/primitives/client.d.ts +0 -61
  564. package/dist/ui/primitives/client.js +0 -3408
  565. package/dist/ui/primitives/client.js.map +0 -1
  566. package/dist/ui/primitives/client.mjs +0 -3256
  567. package/dist/ui/primitives/client.mjs.map +0 -1
  568. package/dist/ui/primitives.d.mts +0 -113
  569. package/dist/ui/primitives.d.ts +0 -113
  570. package/dist/ui/primitives.js +0 -3356
  571. package/dist/ui/primitives.js.map +0 -1
  572. package/dist/ui/primitives.mjs +0 -3227
  573. package/dist/ui/primitives.mjs.map +0 -1
  574. package/dist/user/index.d.mts +0 -228
  575. package/dist/user/index.d.ts +0 -228
  576. package/dist/user/index.js +0 -4306
  577. package/dist/user/index.js.map +0 -1
  578. package/dist/user/index.mjs +0 -4260
  579. package/dist/user/index.mjs.map +0 -1
  580. package/dist/utils/index.d.mts +0 -205
  581. package/dist/utils/index.d.ts +0 -205
  582. package/dist/utils/index.js +0 -574
  583. package/dist/utils/index.js.map +0 -1
  584. package/dist/utils/index.mjs +0 -514
  585. package/dist/utils/index.mjs.map +0 -1
  586. package/dist/workflow/index.d.mts +0 -40
  587. package/dist/workflow/index.d.ts +0 -40
  588. package/dist/workflow/index.js +0 -3710
  589. package/dist/workflow/index.js.map +0 -1
  590. package/dist/workflow/index.mjs +0 -3677
  591. package/dist/workflow/index.mjs.map +0 -1
@@ -0,0 +1,642 @@
1
+ "use client";
2
+
3
+ import {
4
+ forwardRef,
5
+ useCallback,
6
+ useEffect,
7
+ useMemo,
8
+ useRef,
9
+ useState,
10
+ } from "react";
11
+ import { zodResolver } from "@hookform/resolvers/zod";
12
+ import { useForm } from "react-hook-form";
13
+ import { AlertCircle, Loader2, Save } from "lucide-react";
14
+
15
+ import type { EntityConfig, FieldConfig } from "../../types";
16
+
17
+ import { filterDisplayOnlyFields, sortFieldsByOrder } from "../lib/crud-utils";
18
+ import {
19
+ buildSchemaFromConfig,
20
+ getDefaultValuesFromConfig,
21
+ } from "../lib/crud-validator";
22
+ import {
23
+ calculateFieldValue,
24
+ getDependentFields,
25
+ } from "../lib/field-calculator";
26
+
27
+ import {
28
+ Accordion,
29
+ AccordionContent,
30
+ AccordionItem,
31
+ AccordionTrigger,
32
+ } from "../../ui";
33
+ import { Alert, AlertDescription, AlertTitle } from "../../ui";
34
+ import { Badge } from "../../ui";
35
+ import { Button } from "../../ui";
36
+ import {
37
+ Card,
38
+ CardContent,
39
+ CardDescription,
40
+ CardHeader,
41
+ CardTitle,
42
+ } from "../../ui";
43
+ import { Form } from "../../ui";
44
+ import { Progress } from "../../ui";
45
+ import { CrudFieldRenderer } from "./crud-field-renderer";
46
+
47
+ interface CrudFormProps {
48
+ config: EntityConfig;
49
+ initialData?: Record<string, unknown>;
50
+ onSubmit: (data: Record<string, unknown>) => Promise<void> | void;
51
+ onCancel?: () => void;
52
+ submitLabel?: string;
53
+ cancelLabel?: string;
54
+ submittingLabel?: string;
55
+ isSubmitting?: boolean;
56
+ mode?: "create" | "edit";
57
+ enableAutoSave?: boolean;
58
+ onFormReady?: (form: ReturnType<typeof useForm>) => void;
59
+ }
60
+
61
+ const CrudFormComponent = forwardRef<HTMLFormElement, CrudFormProps>(
62
+ function CrudForm(
63
+ {
64
+ config,
65
+ initialData,
66
+ onSubmit,
67
+ onCancel,
68
+ submitLabel = "Submit",
69
+ cancelLabel = "Cancel",
70
+ submittingLabel = "Submitting...",
71
+ isSubmitting: externalIsSubmitting,
72
+ mode = "create",
73
+ enableAutoSave = true,
74
+ onFormReady,
75
+ },
76
+ ref,
77
+ ) {
78
+ const computedFields = useMemo(() => {
79
+ return config.fields.map((field) => {
80
+ const disableForMode =
81
+ (mode === "create" && field.disableOnCreate) ||
82
+ (mode === "edit" && field.disableOnEdit);
83
+
84
+ const shouldDisable = field.disabled || Boolean(disableForMode);
85
+
86
+ if (shouldDisable === field.disabled) {
87
+ return field;
88
+ }
89
+
90
+ const modifiedField = {
91
+ ...field,
92
+ disabled: shouldDisable,
93
+ };
94
+
95
+ return modifiedField;
96
+ });
97
+ }, [config.fields, mode]);
98
+
99
+ const schema = buildSchemaFromConfig(computedFields);
100
+ const defaultValues = initialData || getDefaultValuesFromConfig(config);
101
+ const draftKey = `draft-${config.name}-${mode}`;
102
+ const saveTimeoutRef = useRef<NodeJS.Timeout | null>(null);
103
+ const [hasDraft, setHasDraft] = useState(false);
104
+ const [lastSaved, setLastSaved] = useState<Date | null>(null);
105
+
106
+ const form = useForm({
107
+ resolver: zodResolver(schema),
108
+ defaultValues,
109
+ });
110
+
111
+ // Notify parent when form is ready
112
+ useEffect(() => {
113
+ if (onFormReady) {
114
+ onFormReady(form);
115
+ }
116
+ }, [form, onFormReady]);
117
+
118
+ // Load draft from localStorage when form initializes (only for create mode)
119
+ useEffect(() => {
120
+ if (
121
+ mode === "create" &&
122
+ enableAutoSave &&
123
+ typeof window !== "undefined" &&
124
+ !initialData
125
+ ) {
126
+ try {
127
+ const draft = localStorage.getItem(draftKey);
128
+ if (draft) {
129
+ const draftData = JSON.parse(draft);
130
+ // Only load draft if it's not empty (has at least one non-empty field)
131
+ const hasData = Object.values(draftData).some(
132
+ (value) => value !== undefined && value !== null && value !== "",
133
+ );
134
+ if (hasData) {
135
+ form.reset(draftData);
136
+ setHasDraft(true);
137
+ }
138
+ }
139
+ } catch (error) {
140
+ console.error("Error loading draft:", error);
141
+ }
142
+ }
143
+ }, [mode, enableAutoSave, draftKey, initialData, form]);
144
+
145
+ // Auto-save draft to localStorage
146
+ useEffect(() => {
147
+ if (!enableAutoSave || mode !== "create") return;
148
+
149
+ let unsubscribe: (() => void) | { unsubscribe: () => void } | undefined;
150
+
151
+ try {
152
+ // form.watch() may return an unsubscribe function or a Subscription object
153
+ unsubscribe = form.watch((data: Record<string, unknown>) => {
154
+ // Clear existing timeout
155
+ if (saveTimeoutRef.current) {
156
+ clearTimeout(saveTimeoutRef.current);
157
+ }
158
+
159
+ // Debounce save to localStorage (1 second delay)
160
+ saveTimeoutRef.current = setTimeout(() => {
161
+ try {
162
+ if (typeof window !== "undefined") {
163
+ // Only save if there's actual data
164
+ const hasData = Object.values(data).some(
165
+ (value) =>
166
+ value !== undefined && value !== null && value !== "",
167
+ );
168
+ if (hasData) {
169
+ localStorage.setItem(draftKey, JSON.stringify(data));
170
+ setHasDraft(true);
171
+ setLastSaved(new Date());
172
+ } else {
173
+ // Remove draft if form is empty
174
+ localStorage.removeItem(draftKey);
175
+ setHasDraft(false);
176
+ setLastSaved(null);
177
+ }
178
+ }
179
+ } catch (error) {
180
+ console.error("Error saving draft:", error);
181
+ }
182
+ }, 1000);
183
+ });
184
+ } catch (error) {
185
+ console.error("Error setting up form watch:", error);
186
+ }
187
+
188
+ return () => {
189
+ // Handle unsubscribe - can be a function or a Subscription object
190
+ if (unsubscribe) {
191
+ try {
192
+ if (typeof unsubscribe === "function") {
193
+ unsubscribe();
194
+ } else if (
195
+ unsubscribe &&
196
+ typeof unsubscribe === "object" &&
197
+ "unsubscribe" in unsubscribe
198
+ ) {
199
+ unsubscribe.unsubscribe();
200
+ }
201
+ } catch (error) {
202
+ console.error("Error unsubscribing from form watch:", error);
203
+ }
204
+ }
205
+ if (saveTimeoutRef.current) {
206
+ clearTimeout(saveTimeoutRef.current);
207
+ }
208
+ };
209
+ }, [form, enableAutoSave, mode, draftKey]);
210
+
211
+ // Clear draft after successful submit
212
+ const handleSubmit = async (data: Record<string, unknown>) => {
213
+ try {
214
+ // Filter out display-only fields before submitting to API
215
+ const filteredData = filterDisplayOnlyFields(data, config);
216
+
217
+ // Debug log for user-supplier form
218
+ if (config.name === "user-supplier") {
219
+ console.log("Form submit data:", {
220
+ userId: filteredData.userId,
221
+ supplierId: filteredData.supplierId,
222
+ userIdType: typeof filteredData.userId,
223
+ supplierIdType: typeof filteredData.supplierId,
224
+ fullData: filteredData,
225
+ });
226
+ }
227
+
228
+ await onSubmit(filteredData);
229
+ // Clear draft after successful submit
230
+ if (
231
+ mode === "create" &&
232
+ enableAutoSave &&
233
+ typeof window !== "undefined"
234
+ ) {
235
+ localStorage.removeItem(draftKey);
236
+ setHasDraft(false);
237
+ setLastSaved(null);
238
+ }
239
+ } catch (error) {
240
+ console.error("Form submission error:", error);
241
+ }
242
+ };
243
+
244
+ // Clear draft when canceling
245
+ const handleCancel = () => {
246
+ if (
247
+ mode === "create" &&
248
+ enableAutoSave &&
249
+ typeof window !== "undefined"
250
+ ) {
251
+ // Optionally clear draft on cancel (or keep it for next time)
252
+ // localStorage.removeItem(draftKey)
253
+ }
254
+ onCancel?.();
255
+ };
256
+
257
+ const { isSubmitting: formIsSubmitting, errors } = form.formState;
258
+ const isSubmitting = externalIsSubmitting ?? formIsSubmitting;
259
+
260
+ // ✅ Get all fields that need to be watched for calculations and showWhen
261
+ const fieldsToWatch = useMemo(() => {
262
+ const watchFields = new Set<string>();
263
+ computedFields.forEach((field) => {
264
+ // Watch fields for calculations
265
+ if (field.calculate?.dependsOn) {
266
+ field.calculate.dependsOn.forEach((depField) => {
267
+ watchFields.add(depField);
268
+ });
269
+ }
270
+ // Watch fields for cascading
271
+ if (field.cascade?.triggerField) {
272
+ watchFields.add(field.cascade.triggerField);
273
+ }
274
+ // Watch fields for showWhen conditions
275
+ if (field.showWhen?.field) {
276
+ watchFields.add(field.showWhen.field);
277
+ }
278
+ });
279
+ return Array.from(watchFields);
280
+ }, [computedFields]);
281
+
282
+ // ✅ Watch form values - watch() without args returns all values as an object
283
+ const formValues = form.watch();
284
+
285
+ // Handle field calculations
286
+ useEffect(() => {
287
+ computedFields.forEach((field) => {
288
+ if (field.calculate) {
289
+ const { dependsOn, mode = "onChange" } = field.calculate;
290
+
291
+ // Check if any dependent field has changed
292
+ const hasDependentValues = dependsOn.every(
293
+ (depField) =>
294
+ formValues[depField] !== undefined &&
295
+ formValues[depField] !== null &&
296
+ formValues[depField] !== "",
297
+ );
298
+
299
+ if (hasDependentValues) {
300
+ const calculatedValue = calculateFieldValue(field, formValues);
301
+ const currentValue = form.getValues(field.name);
302
+
303
+ // Only update if value changed
304
+ if (
305
+ calculatedValue !== currentValue &&
306
+ calculatedValue !== undefined
307
+ ) {
308
+ form.setValue(field.name, calculatedValue, {
309
+ shouldValidate: false,
310
+ shouldDirty: false,
311
+ });
312
+ }
313
+ }
314
+ }
315
+ });
316
+ }, [form, formValues, computedFields]);
317
+
318
+ // Handle field cascading
319
+ useEffect(() => {
320
+ computedFields.forEach((field) => {
321
+ if (field.cascade) {
322
+ const { triggerField, action } = field.cascade;
323
+ const triggerValue = formValues[triggerField];
324
+ const previousTriggerValue = form.getValues(
325
+ `_previous_${triggerField}`,
326
+ );
327
+
328
+ // Check if trigger field changed
329
+ if (triggerValue !== previousTriggerValue) {
330
+ if (action === "clear") {
331
+ form.setValue(field.name, undefined, {
332
+ shouldValidate: false,
333
+ shouldDirty: false,
334
+ });
335
+ } else if (action === "reset") {
336
+ const defaultValue = field.defaultValue;
337
+ form.setValue(field.name, defaultValue, {
338
+ shouldValidate: false,
339
+ shouldDirty: false,
340
+ });
341
+ }
342
+
343
+ // Store current trigger value for next comparison
344
+ form.setValue(`_previous_${triggerField}`, triggerValue, {
345
+ shouldValidate: false,
346
+ shouldDirty: false,
347
+ });
348
+ }
349
+ }
350
+ });
351
+ }, [form, formValues, computedFields]);
352
+
353
+ // Get field labels for error summary
354
+ const getFieldLabel = (fieldName: string) => {
355
+ const field = computedFields.find((f) => f.name === fieldName);
356
+ return field?.label || fieldName;
357
+ };
358
+
359
+ // Check if field should be visible based on showWhen condition
360
+ const isFieldVisible = useMemo(() => {
361
+ return (field: FieldConfig): boolean => {
362
+ if (!field.showWhen) return true;
363
+
364
+ const {
365
+ field: watchField,
366
+ value,
367
+ operator = "equals",
368
+ } = field.showWhen;
369
+ const watchValue = formValues[watchField];
370
+
371
+ switch (operator) {
372
+ case "equals":
373
+ return watchValue === value;
374
+ case "notEquals":
375
+ return watchValue !== value;
376
+ case "contains":
377
+ return String(watchValue).includes(String(value));
378
+ case "greaterThan":
379
+ return Number(watchValue) > Number(value);
380
+ case "lessThan":
381
+ return Number(watchValue) < Number(value);
382
+ case "isEmpty":
383
+ return (
384
+ watchValue === undefined ||
385
+ watchValue === null ||
386
+ watchValue === ""
387
+ );
388
+ case "isNotEmpty":
389
+ return (
390
+ watchValue !== undefined &&
391
+ watchValue !== null &&
392
+ watchValue !== ""
393
+ );
394
+ default:
395
+ return watchValue === value;
396
+ }
397
+ };
398
+ }, [formValues]);
399
+
400
+ // Error summary
401
+ const errorEntries = Object.entries(errors);
402
+ const hasErrors = errorEntries.length > 0;
403
+
404
+ // Group fields by section
405
+ const groupedFields = useMemo(() => {
406
+ // Sort fields by order first, then filter
407
+ const sortedFields = sortFieldsByOrder(computedFields);
408
+ // Filter out: hidden fields, display-only fields (DTO fields), and fields that don't meet visibility conditions
409
+ const visibleFields = sortedFields.filter(
410
+ (field) =>
411
+ !field.hideInForm && !field.isDisplayOnly && isFieldVisible(field),
412
+ );
413
+
414
+ // If formSections is defined, use it
415
+ if (config.formSections && config.formSections.length > 0) {
416
+ return config.formSections.map((section) => ({
417
+ ...section,
418
+ fields: visibleFields.filter((field) =>
419
+ section.fields.includes(field.name),
420
+ ),
421
+ }));
422
+ }
423
+
424
+ // Otherwise, group by field.section property
425
+ const sectionsMap = new Map<string, FieldConfig[]>();
426
+ const ungroupedFields: FieldConfig[] = [];
427
+
428
+ visibleFields.forEach((field) => {
429
+ if (field.section) {
430
+ if (!sectionsMap.has(field.section)) {
431
+ sectionsMap.set(field.section, []);
432
+ }
433
+ sectionsMap.get(field.section)!.push(field);
434
+ } else {
435
+ ungroupedFields.push(field);
436
+ }
437
+ });
438
+
439
+ const sections: Array<{
440
+ title: string;
441
+ description?: string;
442
+ fields: FieldConfig[];
443
+ }> = [];
444
+
445
+ // Add grouped sections
446
+ sectionsMap.forEach((fields, title) => {
447
+ sections.push({ title, fields });
448
+ });
449
+
450
+ // Add ungrouped fields as a default section if there are any
451
+ if (ungroupedFields.length > 0) {
452
+ sections.push({ title: "", fields: ungroupedFields });
453
+ }
454
+
455
+ return sections.length > 0
456
+ ? sections
457
+ : [{ title: "", fields: visibleFields }];
458
+ }, [computedFields, config.formSections, isFieldVisible]);
459
+
460
+ // ✅ Progress calculation - use form.getValues() to get all values, not just watched ones
461
+ const progress = useMemo(() => {
462
+ const allFormValues = form.getValues(); // Get all form values for progress calculation
463
+ // Filter out: hidden fields, display-only fields (DTO fields), and fields that don't meet visibility conditions
464
+ const visibleFields = computedFields.filter(
465
+ (field) =>
466
+ !field.hideInForm && !field.isDisplayOnly && isFieldVisible(field),
467
+ );
468
+ const totalFields = visibleFields.length;
469
+ if (totalFields === 0) return 100;
470
+
471
+ const filledFields = visibleFields.filter((field) => {
472
+ const value = allFormValues[field.name];
473
+ return (
474
+ value !== undefined &&
475
+ value !== null &&
476
+ value !== "" &&
477
+ !(Array.isArray(value) && value.length === 0)
478
+ );
479
+ }).length;
480
+
481
+ return Math.round((filledFields / totalFields) * 100);
482
+ }, [form, computedFields, isFieldVisible]);
483
+
484
+ // Render field with two-column layout
485
+ const renderField = (field: FieldConfig) => {
486
+ const fullWidth =
487
+ field.fullWidth ||
488
+ field.type === "textarea" ||
489
+ field.type === "multiselect" ||
490
+ (field.type === "text" && field.rows && field.rows > 3);
491
+
492
+ return (
493
+ <div key={field.name} className={fullWidth ? "md:col-span-2" : ""}>
494
+ <CrudFieldRenderer field={field} mode={mode} />
495
+ </div>
496
+ );
497
+ };
498
+
499
+ // Check if we should use sections (if more than 1 section or sections have titles)
500
+ const useSections =
501
+ groupedFields.length > 1 || groupedFields.some((s) => s.title);
502
+
503
+ return (
504
+ <Form {...form}>
505
+ <form
506
+ ref={ref}
507
+ onSubmit={form.handleSubmit(handleSubmit)}
508
+ className="space-y-6"
509
+ >
510
+ {/* Draft Indicator */}
511
+ {hasDraft && mode === "create" && enableAutoSave && (
512
+ <Alert className="mb-4 border-blue-200 bg-blue-50/50 dark:bg-blue-950/20">
513
+ <Save className="h-4 w-4 text-blue-600" />
514
+ <AlertTitle className="text-blue-900 dark:text-blue-100">
515
+ Draft saved
516
+ </AlertTitle>
517
+ <AlertDescription className="text-blue-800 dark:text-blue-200">
518
+ {lastSaved
519
+ ? `Your changes were saved automatically at ${lastSaved.toLocaleTimeString()}.`
520
+ : "Your changes are being saved automatically."}
521
+ </AlertDescription>
522
+ </Alert>
523
+ )}
524
+
525
+ {/* Error Summary */}
526
+ {hasErrors && (
527
+ <Alert variant="destructive" className="mb-4">
528
+ <AlertCircle className="h-4 w-4" />
529
+ <AlertTitle>Please fix the following errors:</AlertTitle>
530
+ <AlertDescription>
531
+ <ul className="list-disc list-inside space-y-1 mt-2 text-sm">
532
+ {errorEntries.map(([fieldName, error]) => (
533
+ <li key={fieldName}>
534
+ <strong>{getFieldLabel(fieldName)}</strong>:{" "}
535
+ {error?.message as string}
536
+ </li>
537
+ ))}
538
+ </ul>
539
+ </AlertDescription>
540
+ </Alert>
541
+ )}
542
+
543
+ {/* Form Fields with Sections */}
544
+ {useSections ? (
545
+ <Accordion
546
+ type="multiple"
547
+ defaultValue={groupedFields
548
+ .map((_, index) => `section-${index}`)
549
+ .filter(
550
+ (_, index) =>
551
+ config.formSections?.[index]?.defaultOpen !== false,
552
+ )}
553
+ className="space-y-4"
554
+ >
555
+ {groupedFields.map((section, sectionIndex) => {
556
+ if (section.fields.length === 0) return null;
557
+
558
+ return (
559
+ <AccordionItem
560
+ key={`section-${sectionIndex}`}
561
+ value={`section-${sectionIndex}`}
562
+ className="border rounded-lg px-4"
563
+ >
564
+ {section.title && (
565
+ <AccordionTrigger className="hover:no-underline">
566
+ <div className="flex flex-col items-start text-left">
567
+ <CardTitle className="text-base font-semibold">
568
+ {section.title}
569
+ </CardTitle>
570
+ {section.description && (
571
+ <CardDescription className="text-xs mt-0.5">
572
+ {section.description}
573
+ </CardDescription>
574
+ )}
575
+ </div>
576
+ </AccordionTrigger>
577
+ )}
578
+ <AccordionContent>
579
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6 pt-2">
580
+ {section.fields.map(renderField)}
581
+ </div>
582
+ </AccordionContent>
583
+ </AccordionItem>
584
+ );
585
+ })}
586
+ </Accordion>
587
+ ) : (
588
+ /* Simple two-column layout without sections */
589
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
590
+ {groupedFields[0]?.fields.map(renderField)}
591
+ </div>
592
+ )}
593
+
594
+ {/* Form Actions */}
595
+ <div className="flex items-center justify-between gap-2 pt-4 border-t sticky bottom-0 bg-background -mx-4 sm:-mx-6 px-4 sm:px-6 pb-0">
596
+ {/* Draft indicator on left */}
597
+ {hasDraft && mode === "create" && enableAutoSave && (
598
+ <Badge variant="secondary" className="text-xs">
599
+ <Save className="mr-1 h-3 w-3" />
600
+ Draft saved
601
+ </Badge>
602
+ )}
603
+
604
+ {/* Action buttons on right */}
605
+ <div className="flex gap-2 ml-auto">
606
+ {onCancel && (
607
+ <Button
608
+ type="button"
609
+ variant="outline"
610
+ onClick={handleCancel}
611
+ disabled={isSubmitting}
612
+ className="min-w-[80px]"
613
+ >
614
+ {cancelLabel}
615
+ </Button>
616
+ )}
617
+ <Button
618
+ type="submit"
619
+ disabled={isSubmitting}
620
+ className="min-w-[100px]"
621
+ >
622
+ {isSubmitting ? (
623
+ <>
624
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
625
+ <span className="hidden sm:inline">{submittingLabel}</span>
626
+ <span className="sm:hidden">...</span>
627
+ </>
628
+ ) : (
629
+ submitLabel
630
+ )}
631
+ </Button>
632
+ </div>
633
+ </div>
634
+ </form>
635
+ </Form>
636
+ );
637
+ },
638
+ );
639
+
640
+ CrudFormComponent.displayName = "CrudForm";
641
+
642
+ export const CrudForm = CrudFormComponent;