@goplusvn/core 0.1.0 → 0.1.1

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 (369) hide show
  1. package/package.json +2 -1
  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
@@ -0,0 +1,336 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef, useState } from "react";
4
+ import { CheckCircle2, Loader2, X } from "lucide-react";
5
+
6
+ import type { EntityConfig } from "../../types";
7
+ import type { DynamicIconNameType } from "../../types";
8
+
9
+ import { Button } from "../../ui";
10
+ import {
11
+ Sheet,
12
+ SheetContent,
13
+ SheetDescription,
14
+ SheetHeader,
15
+ SheetTitle,
16
+ } from "../../ui";
17
+ import { DynamicIcon } from "../../ui";
18
+ import { CrudForm } from "./crud-form";
19
+ import { useCrudConfig } from "./crud-context";
20
+
21
+ interface CrudSheetProps {
22
+ open: boolean;
23
+ onOpenChange: (open: boolean) => void;
24
+ config: EntityConfig;
25
+ initialData?: Record<string, unknown>;
26
+ mode?: "create" | "edit";
27
+ onSubmit: (data: Record<string, unknown>) => Promise<void> | void;
28
+ side?: "top" | "right" | "bottom" | "left";
29
+ }
30
+
31
+ export function CrudSheet({
32
+ open,
33
+ onOpenChange,
34
+ config,
35
+ initialData,
36
+ mode = "create",
37
+ onSubmit,
38
+ side = "right",
39
+ }: CrudSheetProps) {
40
+ const { translations } = useCrudConfig();
41
+ const [showSuccess, setShowSuccess] = useState(false);
42
+ const [isSubmitting, setIsSubmitting] = useState(false);
43
+ const [formProgress, setFormProgress] = useState<number | null>(null);
44
+ const formRef = useRef<HTMLFormElement>(null);
45
+ const progressSubscriptionRef = useRef<(() => void) | null>(null);
46
+ const successTimeoutRef = useRef<NodeJS.Timeout | null>(null);
47
+ const focusTimeoutRef = useRef<NodeJS.Timeout | null>(null);
48
+
49
+ const handleSubmit = async (data: Record<string, unknown>) => {
50
+ try {
51
+ setIsSubmitting(true);
52
+ await onSubmit(data);
53
+ setIsSubmitting(false);
54
+ setShowSuccess(true);
55
+
56
+ // Clear any existing timeout
57
+ if (successTimeoutRef.current) {
58
+ clearTimeout(successTimeoutRef.current);
59
+ }
60
+
61
+ successTimeoutRef.current = setTimeout(() => {
62
+ setShowSuccess(false);
63
+ onOpenChange(false);
64
+ successTimeoutRef.current = null;
65
+ }, 1500);
66
+ } catch (error) {
67
+ setIsSubmitting(false);
68
+ throw error;
69
+ }
70
+ };
71
+
72
+ const handleCancel = () => {
73
+ if (!isSubmitting) {
74
+ onOpenChange(false);
75
+ }
76
+ };
77
+
78
+ // Focus management and cleanup
79
+ useEffect(() => {
80
+ if (open && formRef.current) {
81
+ // Clear any existing timeout
82
+ if (focusTimeoutRef.current) {
83
+ clearTimeout(focusTimeoutRef.current);
84
+ }
85
+
86
+ focusTimeoutRef.current = setTimeout(() => {
87
+ const firstInput = formRef.current?.querySelector<HTMLInputElement>(
88
+ "input:not([disabled]), textarea:not([disabled]), select:not([disabled])",
89
+ );
90
+ if (firstInput) {
91
+ firstInput.focus();
92
+ }
93
+ focusTimeoutRef.current = null;
94
+ }, 100);
95
+ } else {
96
+ // Reset states when sheet closes
97
+ setShowSuccess(false);
98
+ setIsSubmitting(false);
99
+ setFormProgress(null);
100
+ // Cleanup progress subscription
101
+ if (
102
+ progressSubscriptionRef.current &&
103
+ typeof progressSubscriptionRef.current === "function"
104
+ ) {
105
+ try {
106
+ progressSubscriptionRef.current();
107
+ } catch (error) {
108
+ console.error("Error cleaning up progress subscription:", error);
109
+ }
110
+ progressSubscriptionRef.current = null;
111
+ }
112
+ }
113
+
114
+ // Cleanup timeouts when component unmounts or sheet closes
115
+ return () => {
116
+ if (successTimeoutRef.current) {
117
+ clearTimeout(successTimeoutRef.current);
118
+ successTimeoutRef.current = null;
119
+ }
120
+ if (focusTimeoutRef.current) {
121
+ clearTimeout(focusTimeoutRef.current);
122
+ focusTimeoutRef.current = null;
123
+ }
124
+ // Cleanup progress subscription on unmount
125
+ if (
126
+ progressSubscriptionRef.current &&
127
+ typeof progressSubscriptionRef.current === "function"
128
+ ) {
129
+ try {
130
+ progressSubscriptionRef.current();
131
+ } catch (error) {
132
+ console.error(
133
+ "Error cleaning up progress subscription on unmount:",
134
+ error,
135
+ );
136
+ }
137
+ progressSubscriptionRef.current = null;
138
+ }
139
+ };
140
+ }, [open]);
141
+
142
+ // Keyboard shortcuts
143
+ useEffect(() => {
144
+ if (!open) return;
145
+
146
+ const handleKeyDown = (e: KeyboardEvent) => {
147
+ // Ignore if typing in input/textarea
148
+ const target = e.target as HTMLElement;
149
+ if (
150
+ target.tagName === "INPUT" ||
151
+ target.tagName === "TEXTAREA" ||
152
+ target.isContentEditable
153
+ ) {
154
+ return;
155
+ }
156
+
157
+ // Esc to close
158
+ if (e.key === "Escape" && !isSubmitting) {
159
+ onOpenChange(false);
160
+ }
161
+
162
+ // Ctrl/Cmd + Enter to submit
163
+ if ((e.ctrlKey || e.metaKey) && e.key === "Enter" && !isSubmitting) {
164
+ formRef.current?.requestSubmit();
165
+ }
166
+ };
167
+
168
+ window.addEventListener("keydown", handleKeyDown);
169
+ return () => window.removeEventListener("keydown", handleKeyDown);
170
+ }, [open, isSubmitting, onOpenChange]);
171
+
172
+ return (
173
+ <Sheet open={open} onOpenChange={onOpenChange}>
174
+ <SheetContent
175
+ side={side}
176
+ className="flex flex-col p-0 w-full sm:max-w-2xl overflow-hidden"
177
+ >
178
+ {/* Success overlay */}
179
+ {showSuccess && (
180
+ <div className="absolute inset-0 z-50 flex items-center justify-center bg-background/95 backdrop-blur-sm">
181
+ <div className="flex flex-col items-center gap-4">
182
+ <CheckCircle2 className="h-16 w-16 text-green-500 animate-in zoom-in-50" />
183
+ <p className="text-lg font-semibold">
184
+ {translations?.savedSuccessfully ||
185
+ (mode === "create"
186
+ ? "Created successfully!"
187
+ : "Updated successfully!")}
188
+ </p>
189
+ </div>
190
+ </div>
191
+ )}
192
+
193
+ {/* Loading overlay */}
194
+ {isSubmitting && (
195
+ <div className="absolute inset-0 z-50 flex items-center justify-center bg-background/95 backdrop-blur-sm">
196
+ <div className="flex flex-col items-center gap-4">
197
+ <Loader2 className="h-8 w-8 animate-spin text-primary" />
198
+ <p className="text-sm text-muted-foreground">
199
+ {mode === "create"
200
+ ? translations?.creating || "Creating..."
201
+ : translations?.updating || "Updating..."}
202
+ </p>
203
+ </div>
204
+ </div>
205
+ )}
206
+
207
+ {/* Header */}
208
+ <SheetHeader className="sticky top-0 z-10 bg-background border-b px-6 py-4 flex-shrink-0">
209
+ <div className="flex items-center justify-between">
210
+ <div className="flex items-center gap-3">
211
+ {config.iconName && (
212
+ <DynamicIcon
213
+ name={config.iconName as DynamicIconNameType}
214
+ className="h-5 w-5"
215
+ />
216
+ )}
217
+ <div className="flex-1">
218
+ <SheetTitle className="text-lg font-semibold">
219
+ {mode === "create"
220
+ ? translations?.add
221
+ ? `${translations.add} ${config.label}`
222
+ : `Add ${config.label}`
223
+ : translations?.edit
224
+ ? `${translations.edit} ${config.label}`
225
+ : `Edit ${config.label}`}
226
+ </SheetTitle>
227
+ <SheetDescription className="mt-1 text-xs text-muted-foreground">
228
+ {mode === "create"
229
+ ? translations?.addNew
230
+ ? `${translations.addNew} ${config.label.toLowerCase()}`
231
+ : `Add a new ${config.label.toLowerCase()}`
232
+ : translations?.update
233
+ ? `${translations.update} ${config.label.toLowerCase()}`
234
+ : `Update ${config.label.toLowerCase()} information`}
235
+ </SheetDescription>
236
+ </div>
237
+ </div>
238
+ {formProgress !== null && config.fields.length > 5 && (
239
+ <div className="flex items-center gap-2 text-xs text-muted-foreground">
240
+ <span>{formProgress}%</span>
241
+ <div className="h-1.5 w-16 bg-muted rounded-full overflow-hidden">
242
+ <div
243
+ className="h-full bg-primary transition-all duration-300"
244
+ style={{ width: `${formProgress}%` }}
245
+ />
246
+ </div>
247
+ </div>
248
+ )}
249
+ </div>
250
+ </SheetHeader>
251
+
252
+ {/* Form Content */}
253
+ <div className="flex-1 overflow-y-auto px-6 py-4">
254
+ <CrudForm
255
+ key={`${config.name}-${mode}-${initialData?.id || "new"}`}
256
+ ref={formRef}
257
+ config={config}
258
+ initialData={initialData}
259
+ mode={mode}
260
+ onSubmit={handleSubmit}
261
+ onCancel={handleCancel}
262
+ submitLabel={
263
+ mode === "create"
264
+ ? translations?.create || "Create"
265
+ : translations?.update || "Update"
266
+ }
267
+ submittingLabel={
268
+ mode === "create"
269
+ ? translations?.creating || "Creating..."
270
+ : translations?.updating || "Updating..."
271
+ }
272
+ cancelLabel={translations?.cancel || "Cancel"}
273
+ isSubmitting={isSubmitting}
274
+ enableAutoSave={mode === "create"}
275
+ onFormReady={(form) => {
276
+ // Clean up previous subscription
277
+ if (
278
+ progressSubscriptionRef.current &&
279
+ typeof progressSubscriptionRef.current === "function"
280
+ ) {
281
+ try {
282
+ progressSubscriptionRef.current();
283
+ } catch (error) {
284
+ console.error(
285
+ "Error cleaning up previous subscription:",
286
+ error,
287
+ );
288
+ }
289
+ progressSubscriptionRef.current = null;
290
+ }
291
+
292
+ // Subscribe to form value changes for progress
293
+ if (config.fields.length > 5) {
294
+ // Filter out: hidden fields and display-only fields (DTO fields)
295
+ const visibleFields = config.fields.filter(
296
+ (f) => !f.hideInForm && !f.isDisplayOnly,
297
+ );
298
+ // Watch only visible fields instead of all fields for better performance
299
+ const visibleFieldNames = visibleFields.map((f) => f.name);
300
+ try {
301
+ // form.watch() returns an unsubscribe function directly, not an object with unsubscribe()
302
+ const unsubscribe = form.watch(
303
+ visibleFieldNames,
304
+ (values: Record<string, unknown>) => {
305
+ const filledFields = visibleFields.filter((field) => {
306
+ const value = values[field.name];
307
+ return (
308
+ value !== undefined && value !== null && value !== ""
309
+ );
310
+ });
311
+ const progress = Math.round(
312
+ (filledFields.length / visibleFields.length) * 100,
313
+ );
314
+ setFormProgress(progress);
315
+ },
316
+ );
317
+ // Store unsubscribe function directly, but only if it's actually a function
318
+ if (typeof unsubscribe === "function") {
319
+ progressSubscriptionRef.current = unsubscribe;
320
+ } else {
321
+ progressSubscriptionRef.current = null;
322
+ }
323
+ } catch (error) {
324
+ console.error("Error setting up form watch:", error);
325
+ progressSubscriptionRef.current = null;
326
+ }
327
+ } else {
328
+ progressSubscriptionRef.current = null;
329
+ }
330
+ }}
331
+ />
332
+ </div>
333
+ </SheetContent>
334
+ </Sheet>
335
+ );
336
+ }
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ // Re-export DataTableSkeleton as CrudTableSkeleton for backward compatibility
4
+ // This ensures existing code using CrudTableSkeleton continues to work
5
+ import { DataTableSkeleton } from "../../ui/data-display/data-table/data-table-skeleton";
6
+ import type { DataTableSkeletonProps } from "../../ui/data-display/data-table/data-table-skeleton";
7
+
8
+ export { DataTableSkeleton as CrudTableSkeleton };
9
+ export type { DataTableSkeletonProps as CrudTableSkeletonProps };
10
+
11
+ // Legacy interface for backward compatibility
12
+ export interface LegacyCrudTableSkeletonProps {
13
+ columns: number;
14
+ rows?: number;
15
+ }
16
+
17
+ /**
18
+ * @deprecated Use DataTableSkeleton from @goerp/core/ui instead
19
+ * This is kept for backward compatibility
20
+ */
21
+ export function CrudTableSkeletonLegacy({
22
+ columns,
23
+ rows = 5,
24
+ }: LegacyCrudTableSkeletonProps) {
25
+ return <DataTableSkeleton columns={columns} rows={rows} />;
26
+ }
@@ -0,0 +1,91 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import type { Table } from "@tanstack/react-table";
5
+
6
+ import type { CrudPermissions, EntityConfig } from "../../types";
7
+
8
+ import { CrudFilterChips } from "./crud-filter-chips";
9
+ import { FilterBuilder } from "./crud-filters/filter-builder";
10
+ import { useCrudState } from "./crud-context";
11
+ import { DataTableToolbar } from "../../ui/data-display/data-table/data-table-toolbar";
12
+ import type { FilterConfig } from "../../ui/data-display/data-table/data-table-toolbar";
13
+
14
+ interface CrudTableToolbarProps<TData> {
15
+ table?: Table<TData>;
16
+ config: EntityConfig;
17
+ permissions: CrudPermissions;
18
+ viewMode?: "table" | "card";
19
+ onViewModeChange?: (mode: "table" | "card") => void;
20
+ }
21
+
22
+ export function CrudTableToolbar<TData>({
23
+ table,
24
+ config,
25
+ permissions,
26
+ viewMode = "table",
27
+ onViewModeChange,
28
+ }: CrudTableToolbarProps<TData>) {
29
+ const { filters, clearFilters, addFilter, removeFilter, search, setSearch } =
30
+ useCrudState();
31
+
32
+ // Convert Crud filters to DataTable filters
33
+ // Note: This is an approximation as CrudFilters use a more complex builder
34
+ // We'll pass the custom builder via filterBuilder slot
35
+ const dataTableFilters: FilterConfig[] = useMemo(() => {
36
+ return (config.filters || []).map((f) => ({
37
+ name: f.name,
38
+ label: f.label,
39
+ type: "custom" as const, // We use custom builder
40
+ field: f.field,
41
+ }));
42
+ }, [config.filters]);
43
+
44
+ const activeFilters = useMemo(() => {
45
+ return filters.map((f) => ({
46
+ name: f.name,
47
+ value: f.value,
48
+ operator: f.operator,
49
+ }));
50
+ }, [filters]);
51
+
52
+ return (
53
+ <div className="space-y-2">
54
+ <DataTableToolbar
55
+ table={table}
56
+ // Search
57
+ searchEnabled={config.features?.search}
58
+ searchValue={search}
59
+ onSearchChange={setSearch}
60
+ searchPlaceholder={`Search ${config.pluralLabel.toLowerCase()}...`}
61
+ // Filters
62
+ filters={dataTableFilters}
63
+ activeFilters={activeFilters}
64
+ // Custom filter builder overrides the default filter rendering
65
+ filterBuilder={<FilterBuilder filters={config.filters!} />}
66
+ // Filter callbacks
67
+ onFiltersChange={(newFilters) => {
68
+ if (newFilters.length === 0) {
69
+ clearFilters();
70
+ } else {
71
+ // Complex syncing is handled by FilterBuilder/CrudFilterChips
72
+ // This callback mainly handles the "Clear all" action
73
+ clearFilters();
74
+ }
75
+ }}
76
+ // View Options
77
+ viewMode={viewMode}
78
+ onViewModeChange={onViewModeChange}
79
+ enableViewModeToggle={!!onViewModeChange}
80
+ // Reset
81
+ onReset={() => {
82
+ setSearch("");
83
+ clearFilters();
84
+ }}
85
+ />
86
+
87
+ {/* Second row: Filter chips (specific to CRUD complex filters) */}
88
+ <CrudFilterChips config={config} />
89
+ </div>
90
+ );
91
+ }