@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,311 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+ import { MoreVertical, Plus, Pencil, Trash2, FolderOpen } from "lucide-react";
5
+ import { toast } from "sonner";
6
+ import type { SystemCategoryGroup } from "../../../types";
7
+ import {
8
+ Button,
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuTrigger,
13
+ ScrollArea,
14
+ } from "@goerp/core/ui";
15
+ import { cn } from "../../../../utils";
16
+ // Assuming we'll implement these dialogs or use generic ones
17
+ // For now, I'll assume we might use a generic form or specific dialogs
18
+ // I'll leave placeholders for the logic or implementation
19
+ import { useForm } from "react-hook-form";
20
+ import type { SubmitHandler } from "react-hook-form";
21
+ import { zodResolver } from "@hookform/resolvers/zod";
22
+ import type * as z from "zod";
23
+ import {
24
+ Dialog,
25
+ DialogContent,
26
+ DialogHeader,
27
+ DialogTitle,
28
+ DialogTrigger,
29
+ Form,
30
+ FormControl,
31
+ FormField,
32
+ FormItem,
33
+ FormLabel,
34
+ FormMessage,
35
+ Input as FormInput,
36
+ Textarea,
37
+ } from "@goerp/core/ui";
38
+ import { systemCategoryGroupSchema } from "../../../schemas/system-category-group.schema";
39
+
40
+ interface GroupSidebarProps {
41
+ groups: SystemCategoryGroup[];
42
+ selectedGroup: SystemCategoryGroup | null;
43
+ onSelectGroup: (group: SystemCategoryGroup) => void;
44
+ onUpdate: (group: SystemCategoryGroup) => void;
45
+ onCreate: (group: SystemCategoryGroup) => void;
46
+ onDelete: (groupId: string) => void;
47
+ }
48
+
49
+ export function GroupSidebar({
50
+ groups,
51
+ selectedGroup,
52
+ onSelectGroup,
53
+ onUpdate,
54
+ onCreate,
55
+ onDelete,
56
+ }: GroupSidebarProps) {
57
+ const [isCreateOpen, setIsCreateOpen] = useState(false);
58
+ const [editingGroup, setEditingGroup] = useState<SystemCategoryGroup | null>(
59
+ null,
60
+ );
61
+
62
+ const handleDelete = async (group: SystemCategoryGroup) => {
63
+ if (!confirm("Bạn có chắc chắn muốn xóa nhóm này không?")) return;
64
+
65
+ try {
66
+ const res = await fetch(`/api/system-category-groups?id=${group.id}`, {
67
+ method: "DELETE",
68
+ });
69
+
70
+ if (!res.ok) throw new Error("Failed to delete group");
71
+
72
+ onDelete(group.id);
73
+ toast.success("Đã xóa nhóm danh mục thành công");
74
+ } catch (error) {
75
+ toast.error("Xóa nhóm danh mục thất bại");
76
+ console.error(error);
77
+ }
78
+ };
79
+
80
+ return (
81
+ <>
82
+ <ScrollArea className="h-full flex-1">
83
+ <div className="flex flex-col gap-1 pr-4">
84
+ {groups.map((group) => (
85
+ <div
86
+ key={group.id}
87
+ className={cn(
88
+ "group flex cursor-pointer items-center justify-between rounded-md px-3 py-2 text-sm transition-colors hover:bg-muted/50",
89
+ selectedGroup?.id === group.id
90
+ ? "bg-primary/10 font-medium text-primary hover:bg-primary/20"
91
+ : "text-muted-foreground",
92
+ )}
93
+ onClick={() => onSelectGroup(group)}
94
+ >
95
+ <div className="flex items-center gap-2 overflow-hidden">
96
+ <FolderOpen className="h-4 w-4 shrink-0" />
97
+ <span className="truncate">{group.name}</span>
98
+ </div>
99
+
100
+ <DropdownMenu>
101
+ <DropdownMenuTrigger asChild>
102
+ <Button
103
+ variant="ghost"
104
+ size="icon"
105
+ className="h-6 w-6 opacity-0 transition-opacity group-hover:opacity-100"
106
+ onClick={(e) => e.stopPropagation()}
107
+ >
108
+ <MoreVertical className="h-3 w-3" />
109
+ </Button>
110
+ </DropdownMenuTrigger>
111
+ <DropdownMenuContent align="end">
112
+ <DropdownMenuItem
113
+ onClick={(e) => {
114
+ e.stopPropagation();
115
+ setEditingGroup(group);
116
+ }}
117
+ >
118
+ <Pencil className="mr-2 h-4 w-4" />
119
+ Chỉnh sửa
120
+ </DropdownMenuItem>
121
+ <DropdownMenuItem
122
+ className="text-destructive focus:text-destructive"
123
+ onClick={(e) => {
124
+ e.stopPropagation();
125
+ handleDelete(group);
126
+ }}
127
+ >
128
+ <Trash2 className="mr-2 h-4 w-4" />
129
+ Xóa
130
+ </DropdownMenuItem>
131
+ </DropdownMenuContent>
132
+ </DropdownMenu>
133
+ </div>
134
+ ))}
135
+
136
+ {groups.length === 0 && (
137
+ <div className="py-4 text-center text-sm text-muted-foreground">
138
+ Không tìm thấy nhóm nào
139
+ </div>
140
+ )}
141
+ </div>
142
+ </ScrollArea>
143
+
144
+ <div className="pt-4">
145
+ <Button className="w-full" onClick={() => setIsCreateOpen(true)}>
146
+ <Plus className="mr-2 h-4 w-4" />
147
+ Thêm nhóm mới
148
+ </Button>
149
+ </div>
150
+
151
+ <GroupDialog
152
+ mode="create"
153
+ open={isCreateOpen}
154
+ onOpenChange={setIsCreateOpen}
155
+ onSuccess={(group) => {
156
+ onCreate(group);
157
+ setIsCreateOpen(false);
158
+ }}
159
+ />
160
+
161
+ {editingGroup && (
162
+ <GroupDialog
163
+ mode="edit"
164
+ open={!!editingGroup}
165
+ onOpenChange={(open) => !open && setEditingGroup(null)}
166
+ group={editingGroup}
167
+ onSuccess={(group) => {
168
+ onUpdate(group);
169
+ setEditingGroup(null);
170
+ }}
171
+ />
172
+ )}
173
+ </>
174
+ );
175
+ }
176
+
177
+ // Internal Dialog Component for Create/Edit
178
+ interface GroupDialogProps {
179
+ mode: "create" | "edit";
180
+ open: boolean;
181
+ onOpenChange: (open: boolean) => void;
182
+ group?: SystemCategoryGroup;
183
+ onSuccess: (group: SystemCategoryGroup) => void;
184
+ }
185
+
186
+ function GroupDialog({
187
+ mode,
188
+ open,
189
+ onOpenChange,
190
+ group,
191
+ onSuccess,
192
+ }: GroupDialogProps) {
193
+ const form = useForm<z.infer<typeof systemCategoryGroupSchema>>({
194
+ resolver: zodResolver(systemCategoryGroupSchema),
195
+ defaultValues: {
196
+ code: group?.code || "",
197
+ name: group?.name || "",
198
+ description: group?.description || "",
199
+ status: group?.status === "inactive" ? "inactive" : "active",
200
+ },
201
+ });
202
+
203
+ const onSubmit: SubmitHandler<
204
+ z.infer<typeof systemCategoryGroupSchema>
205
+ > = async (values) => {
206
+ try {
207
+ const url =
208
+ mode === "create"
209
+ ? "/api/system-category-groups"
210
+ : `/api/system-category-groups?id=${group?.id}`;
211
+ const method = mode === "create" ? "POST" : "PUT"; // Or PATCH depending on API
212
+
213
+ // Adjust payload if needed (e.g. wrapper or direct)
214
+ const payload = mode === "edit" ? { ...values, id: group?.id } : values;
215
+
216
+ const res = await fetch(url, {
217
+ method,
218
+ headers: { "Content-Type": "application/json" },
219
+ body: JSON.stringify(payload),
220
+ });
221
+
222
+ if (!res.ok) {
223
+ const errorData = await res.json();
224
+ throw new Error(errorData.message || "Action failed");
225
+ }
226
+
227
+ const data = await res.json();
228
+ toast.success(
229
+ mode === "create"
230
+ ? "Tạo nhóm danh mục thành công"
231
+ : "Cập nhật nhóm danh mục thành công",
232
+ );
233
+ onSuccess(data); // Check if API returns the full object
234
+ form.reset();
235
+ } catch (error) {
236
+ toast.error(error instanceof Error ? error.message : "Có lỗi xảy ra");
237
+ }
238
+ };
239
+
240
+ return (
241
+ <Dialog open={open} onOpenChange={onOpenChange}>
242
+ <DialogContent>
243
+ <DialogHeader>
244
+ <DialogTitle>
245
+ {mode === "create"
246
+ ? "Thêm nhóm danh mục"
247
+ : "Chỉnh sửa nhóm danh mục"}
248
+ </DialogTitle>
249
+ </DialogHeader>
250
+
251
+ <Form {...form}>
252
+ <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
253
+ <FormField
254
+ control={form.control}
255
+ name="code"
256
+ render={({ field }) => (
257
+ <FormItem>
258
+ <FormLabel>Mã nhóm</FormLabel>
259
+ <FormControl>
260
+ <FormInput
261
+ placeholder="Nhập mã nhóm (ví dụ: USER_TYPE)"
262
+ {...field}
263
+ disabled={mode === "edit"}
264
+ />
265
+ </FormControl>
266
+ <FormMessage />
267
+ </FormItem>
268
+ )}
269
+ />
270
+ <FormField
271
+ control={form.control}
272
+ name="name"
273
+ render={({ field }) => (
274
+ <FormItem>
275
+ <FormLabel>Tên nhóm</FormLabel>
276
+ <FormControl>
277
+ <FormInput placeholder="Nhập tên nhóm" {...field} />
278
+ </FormControl>
279
+ <FormMessage />
280
+ </FormItem>
281
+ )}
282
+ />
283
+ <FormField
284
+ control={form.control}
285
+ name="description"
286
+ render={({ field }) => (
287
+ <FormItem>
288
+ <FormLabel>Mô tả</FormLabel>
289
+ <FormControl>
290
+ <Textarea placeholder="Mô tả chi tiết" {...field} />
291
+ </FormControl>
292
+ <FormMessage />
293
+ </FormItem>
294
+ )}
295
+ />
296
+ <div className="flex justify-end gap-2">
297
+ <Button
298
+ type="button"
299
+ variant="outline"
300
+ onClick={() => onOpenChange(false)}
301
+ >
302
+ Hủy
303
+ </Button>
304
+ <Button type="submit">Lưu</Button>
305
+ </div>
306
+ </form>
307
+ </Form>
308
+ </DialogContent>
309
+ </Dialog>
310
+ );
311
+ }
@@ -0,0 +1,222 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect } from "react";
4
+ import { toast } from "sonner";
5
+ import {
6
+ Card,
7
+ CardContent,
8
+ CardDescription,
9
+ CardHeader,
10
+ CardTitle,
11
+ Label,
12
+ Input,
13
+ Switch,
14
+ Button,
15
+ Badge,
16
+ Alert,
17
+ AlertDescription,
18
+ } from "../../../../ui";
19
+ import { globalError } from "../../../../ui/feedback/error-dialog";
20
+ import { Settings, DollarSign, Save, RefreshCw } from "lucide-react";
21
+ import type { SalesRulesSettings } from "../../../services/settings-service";
22
+
23
+ interface SalesRulesSettingsProps {
24
+ initialSettings?: SalesRulesSettings;
25
+ }
26
+
27
+ const DEFAULT_SETTINGS: SalesRulesSettings = {
28
+ paymentRequiredThreshold: 10000000,
29
+ paymentThresholdEnabled: true,
30
+ requiredCustomerFields: ["name", "phone", "idNumber"],
31
+ };
32
+
33
+ export function SalesRulesSettingsComponent({
34
+ initialSettings,
35
+ }: SalesRulesSettingsProps) {
36
+ const [settings, setSettings] = useState<SalesRulesSettings>(
37
+ initialSettings || DEFAULT_SETTINGS,
38
+ );
39
+ const [loading, setLoading] = useState(false);
40
+ const [dirty, setDirty] = useState(false);
41
+
42
+ useEffect(() => {
43
+ loadSettings();
44
+ }, []);
45
+
46
+ const loadSettings = async () => {
47
+ try {
48
+ const res = await fetch("/api/settings/sales-rules");
49
+ if (res.ok) {
50
+ const data = await res.json();
51
+ setSettings(data);
52
+ }
53
+ } catch (e) {
54
+ console.error("Error loading settings:", e);
55
+ }
56
+ };
57
+
58
+ const handleSave = async () => {
59
+ setLoading(true);
60
+ try {
61
+ const res = await fetch("/api/settings/sales-rules", {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" },
64
+ body: JSON.stringify(settings),
65
+ });
66
+
67
+ if (!res.ok) throw new Error("Lỗi lưu cấu hình");
68
+
69
+ toast.success("Đã lưu cấu hình thành công");
70
+ setDirty(false);
71
+ } catch (e: unknown) {
72
+ const error = e instanceof Error ? e : new Error(String(e));
73
+ globalError.emit({
74
+ title: "Lỗi lưu cấu hình",
75
+ description: error.message || "Đã xảy ra lỗi không xác định",
76
+ trace: error.stack,
77
+ });
78
+ } finally {
79
+ setLoading(false);
80
+ }
81
+ };
82
+
83
+ const updateSetting = <K extends keyof SalesRulesSettings>(
84
+ key: K,
85
+ value: SalesRulesSettings[K],
86
+ ) => {
87
+ setSettings((prev) => ({ ...prev, [key]: value }));
88
+ setDirty(true);
89
+ };
90
+
91
+ const formatCurrency = (value: number) => {
92
+ return new Intl.NumberFormat("vi-VN").format(value);
93
+ };
94
+
95
+ return (
96
+ <div className="space-y-6">
97
+ {/* Payment Threshold Rule */}
98
+ <Card>
99
+ <CardHeader>
100
+ <div className="flex items-center justify-between">
101
+ <div>
102
+ <CardTitle className="flex items-center gap-2">
103
+ <DollarSign className="h-5 w-5 text-green-600" />
104
+ Quy tắc thanh toán trước
105
+ </CardTitle>
106
+ <CardDescription>
107
+ Yêu cầu thanh toán trước khi tạo đơn đặt hàng cho đơn có giá trị
108
+ dưới ngưỡng quy định
109
+ </CardDescription>
110
+ </div>
111
+ <Switch
112
+ checked={settings.paymentThresholdEnabled}
113
+ onCheckedChange={(checked) =>
114
+ updateSetting("paymentThresholdEnabled", checked)
115
+ }
116
+ />
117
+ </div>
118
+ </CardHeader>
119
+ <CardContent className="space-y-4">
120
+ <div className="space-y-2">
121
+ <Label htmlFor="threshold">Ngưỡng giá trị đơn hàng (VND)</Label>
122
+ <div className="flex items-center gap-4">
123
+ <Input
124
+ id="threshold"
125
+ type="number"
126
+ step={1000000}
127
+ min={0}
128
+ value={settings.paymentRequiredThreshold}
129
+ onChange={(e) =>
130
+ updateSetting(
131
+ "paymentRequiredThreshold",
132
+ Number(e.target.value),
133
+ )
134
+ }
135
+ disabled={!settings.paymentThresholdEnabled}
136
+ className="max-w-xs"
137
+ />
138
+ <span className="text-sm text-muted-foreground">
139
+ = {formatCurrency(settings.paymentRequiredThreshold)} đ
140
+ </span>
141
+ </div>
142
+ <p className="text-sm text-muted-foreground">
143
+ Đơn hàng dưới {formatCurrency(settings.paymentRequiredThreshold)}{" "}
144
+ VND sẽ yêu cầu thanh toán trước khi tạo đơn đặt hàng.
145
+ </p>
146
+ </div>
147
+
148
+ {settings.paymentThresholdEnabled && (
149
+ <Alert>
150
+ <Settings className="h-4 w-4" />
151
+ <AlertDescription>
152
+ Người dùng có quyền{" "}
153
+ <code className="bg-muted px-1 rounded">
154
+ sales-orders:create-without-payment
155
+ </code>{" "}
156
+ có thể bỏ qua quy tắc này.
157
+ </AlertDescription>
158
+ </Alert>
159
+ )}
160
+ </CardContent>
161
+ </Card>
162
+
163
+ {/* Required Customer Fields */}
164
+ <Card>
165
+ <CardHeader>
166
+ <CardTitle className="flex items-center gap-2">
167
+ <Settings className="h-5 w-5 text-blue-600" />
168
+ Thông tin khách hàng bắt buộc
169
+ </CardTitle>
170
+ <CardDescription>
171
+ Các trường thông tin khách hàng cần có để tạo hóa đơn và hợp đồng
172
+ </CardDescription>
173
+ </CardHeader>
174
+ <CardContent>
175
+ <div className="flex flex-wrap gap-2">
176
+ {["name", "phone", "idNumber", "email", "address"].map((field) => {
177
+ const isRequired =
178
+ settings.requiredCustomerFields.includes(field);
179
+ const fieldLabels: Record<string, string> = {
180
+ name: "Họ tên",
181
+ phone: "Số điện thoại",
182
+ idNumber: "CCCD",
183
+ email: "Email",
184
+ address: "Địa chỉ",
185
+ };
186
+ return (
187
+ <Badge
188
+ key={field}
189
+ variant={isRequired ? "default" : "outline"}
190
+ className="cursor-pointer"
191
+ onClick={() => {
192
+ const newFields = isRequired
193
+ ? settings.requiredCustomerFields.filter(
194
+ (f) => f !== field,
195
+ )
196
+ : [...settings.requiredCustomerFields, field];
197
+ updateSetting("requiredCustomerFields", newFields);
198
+ }}
199
+ >
200
+ {fieldLabels[field]}
201
+ {isRequired ? " ✓" : ""}
202
+ </Badge>
203
+ );
204
+ })}
205
+ </div>
206
+ </CardContent>
207
+ </Card>
208
+
209
+ {/* Save Button */}
210
+ <div className="flex items-center justify-end gap-2">
211
+ <Button variant="outline" onClick={loadSettings} disabled={loading}>
212
+ <RefreshCw className="mr-2 h-4 w-4" />
213
+ Làm mới
214
+ </Button>
215
+ <Button onClick={handleSave} disabled={loading || !dirty}>
216
+ <Save className="mr-2 h-4 w-4" />
217
+ {loading ? "Đang lưu..." : "Lưu cấu hình"}
218
+ </Button>
219
+ </div>
220
+ </div>
221
+ );
222
+ }