@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,244 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import {
5
+ Card,
6
+ CardContent,
7
+ CardDescription,
8
+ CardHeader,
9
+ CardTitle,
10
+ } from "../../../../ui/primitives/card";
11
+ import {
12
+ Tabs,
13
+ TabsContent,
14
+ TabsList,
15
+ TabsTrigger,
16
+ } from "../../../../ui/primitives/tabs";
17
+ import { Button } from "../../../../ui/primitives/button";
18
+ import { Input } from "../../../../ui/primitives/input";
19
+ import { Label } from "../../../../ui/primitives/label";
20
+ import { Switch } from "../../../../ui/primitives/switch";
21
+ import { toast } from "sonner";
22
+ import { Loader2 } from "lucide-react";
23
+
24
+ interface AuditSettings {
25
+ retentionDays: number;
26
+ enabledResources: string[];
27
+ }
28
+
29
+ interface NotificationSettings {
30
+ retentionDays: number;
31
+ channels: {
32
+ email: boolean;
33
+ inApp: boolean;
34
+ telegram: boolean;
35
+ };
36
+ }
37
+
38
+ export function SystemSettings() {
39
+ const [loading, setLoading] = useState(true);
40
+ const [saving, setSaving] = useState(false);
41
+ const [auditSettings, setAuditSettings] = useState<AuditSettings | null>(
42
+ null,
43
+ );
44
+ const [notifySettings, setNotifySettings] =
45
+ useState<NotificationSettings | null>(null);
46
+
47
+ useEffect(() => {
48
+ fetch("/api/admin/system/settings")
49
+ .then((res) => res.json())
50
+ .then((data) => {
51
+ if (data.data) {
52
+ setAuditSettings(data.data.audit);
53
+ setNotifySettings(data.data.notification);
54
+ }
55
+ })
56
+ .catch(() => toast.error("Failed to load settings"))
57
+ .finally(() => setLoading(false));
58
+ }, []);
59
+
60
+ const saveAuditSettings = async () => {
61
+ if (!auditSettings) return;
62
+ setSaving(true);
63
+ try {
64
+ const res = await fetch("/api/admin/system/settings", {
65
+ method: "POST",
66
+ body: JSON.stringify({ type: "audit", settings: auditSettings }),
67
+ });
68
+ if (res.ok) toast.success("Audit settings saved");
69
+ else throw new Error();
70
+ } catch {
71
+ toast.error("Failed to save audit settings");
72
+ } finally {
73
+ setSaving(false);
74
+ }
75
+ };
76
+
77
+ const saveNotifySettings = async () => {
78
+ if (!notifySettings) return;
79
+ setSaving(true);
80
+ try {
81
+ const res = await fetch("/api/admin/system/settings", {
82
+ method: "POST",
83
+ body: JSON.stringify({
84
+ type: "notification",
85
+ settings: notifySettings,
86
+ }),
87
+ });
88
+ if (res.ok) toast.success("Notification settings saved");
89
+ else throw new Error();
90
+ } catch {
91
+ toast.error("Failed to save notification settings");
92
+ } finally {
93
+ setSaving(false);
94
+ }
95
+ };
96
+
97
+ if (loading)
98
+ return (
99
+ <div className="flex justify-center p-8">
100
+ <Loader2 className="animate-spin h-8 w-8 text-muted-foreground" />
101
+ </div>
102
+ );
103
+
104
+ return (
105
+ <Tabs defaultValue="audit" className="w-full">
106
+ <TabsList>
107
+ <TabsTrigger value="audit">Audit Logging</TabsTrigger>
108
+ <TabsTrigger value="notification">Notifications</TabsTrigger>
109
+ </TabsList>
110
+
111
+ <TabsContent value="audit" className="space-y-4">
112
+ <Card>
113
+ <CardHeader>
114
+ <CardTitle>Audit Log Configuration</CardTitle>
115
+ <CardDescription>
116
+ Control how long audit logs are kept and what events are recorded.
117
+ </CardDescription>
118
+ </CardHeader>
119
+ <CardContent className="space-y-4">
120
+ <div className="grid gap-2">
121
+ <Label htmlFor="retention">Retention Period (Days)</Label>
122
+ <Input
123
+ id="retention"
124
+ type="number"
125
+ value={auditSettings?.retentionDays || 90}
126
+ onChange={(e) =>
127
+ setAuditSettings((prev) =>
128
+ prev
129
+ ? { ...prev, retentionDays: parseInt(e.target.value) }
130
+ : null,
131
+ )
132
+ }
133
+ />
134
+ </div>
135
+
136
+ <div className="flex items-center space-x-2">
137
+ {/* Placeholder for resource selection - for now just informative */}
138
+ <span className="text-sm text-muted-foreground">
139
+ Currently logging all resources ('*'). Granular resource
140
+ selection coming soon.
141
+ </span>
142
+ </div>
143
+
144
+ <Button onClick={saveAuditSettings} disabled={saving}>
145
+ {saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
146
+ Save Changes
147
+ </Button>
148
+ </CardContent>
149
+ </Card>
150
+ </TabsContent>
151
+
152
+ <TabsContent value="notification" className="space-y-4">
153
+ <Card>
154
+ <CardHeader>
155
+ <CardTitle>Notification System</CardTitle>
156
+ <CardDescription>
157
+ Configure notification channels and cleanup policies.
158
+ </CardDescription>
159
+ </CardHeader>
160
+ <CardContent className="space-y-4">
161
+ <div className="grid gap-2">
162
+ <Label htmlFor="notify-retention">Retention Period (Days)</Label>
163
+ <Input
164
+ id="notify-retention"
165
+ type="number"
166
+ value={notifySettings?.retentionDays || 30}
167
+ onChange={(e) =>
168
+ setNotifySettings((prev) =>
169
+ prev
170
+ ? { ...prev, retentionDays: parseInt(e.target.value) }
171
+ : null,
172
+ )
173
+ }
174
+ />
175
+ </div>
176
+
177
+ <div className="space-y-4 border rounded p-4">
178
+ <h4 className="font-medium text-sm">Active Channels</h4>
179
+
180
+ <div className="flex items-center justify-between">
181
+ <Label htmlFor="channel-inapp">In-App Notifications</Label>
182
+ <Switch
183
+ id="channel-inapp"
184
+ checked={notifySettings?.channels.inApp}
185
+ onCheckedChange={(checked) =>
186
+ setNotifySettings((prev) =>
187
+ prev
188
+ ? {
189
+ ...prev,
190
+ channels: { ...prev.channels, inApp: checked },
191
+ }
192
+ : null,
193
+ )
194
+ }
195
+ />
196
+ </div>
197
+
198
+ <div className="flex items-center justify-between">
199
+ <Label htmlFor="channel-email">Email Notifications</Label>
200
+ <Switch
201
+ id="channel-email"
202
+ checked={notifySettings?.channels.email}
203
+ onCheckedChange={(checked) =>
204
+ setNotifySettings((prev) =>
205
+ prev
206
+ ? {
207
+ ...prev,
208
+ channels: { ...prev.channels, email: checked },
209
+ }
210
+ : null,
211
+ )
212
+ }
213
+ />
214
+ </div>
215
+
216
+ <div className="flex items-center justify-between">
217
+ <Label htmlFor="channel-telegram">Telegram Notifications</Label>
218
+ <Switch
219
+ id="channel-telegram"
220
+ checked={notifySettings?.channels.telegram}
221
+ onCheckedChange={(checked) =>
222
+ setNotifySettings((prev) =>
223
+ prev
224
+ ? {
225
+ ...prev,
226
+ channels: { ...prev.channels, telegram: checked },
227
+ }
228
+ : null,
229
+ )
230
+ }
231
+ />
232
+ </div>
233
+ </div>
234
+
235
+ <Button onClick={saveNotifySettings} disabled={saving}>
236
+ {saving && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
237
+ Save Changes
238
+ </Button>
239
+ </CardContent>
240
+ </Card>
241
+ </TabsContent>
242
+ </Tabs>
243
+ );
244
+ }
@@ -0,0 +1,15 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { CategoryManager } from "./components/categories/category-manager";
5
+ import type { SystemCategoryGroup } from "../types";
6
+
7
+ interface SystemCategoryPageProps {
8
+ initialGroups: SystemCategoryGroup[];
9
+ }
10
+
11
+ export function SystemCategoryPage({
12
+ initialGroups,
13
+ }: SystemCategoryPageProps) {
14
+ return <CategoryManager initialGroups={initialGroups} />;
15
+ }
@@ -0,0 +1,380 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { Loader2, RefreshCw, Plus } from "lucide-react";
6
+ import { toast } from "sonner";
7
+ import { cn } from "../../utils";
8
+ import { Button } from "../../ui";
9
+
10
+ import { SETTINGS_GROUPS } from "./components/settings/settings-groups";
11
+ import { SettingsSidebar } from "./components/settings/settings-sidebar";
12
+ import { SettingsSearch } from "./components/settings/settings-search";
13
+ import { SettingsSection } from "./components/settings/settings-section";
14
+ import { SettingField } from "./components/settings/setting-field";
15
+ import type { SettingConfig } from "../types";
16
+ import { SettingFormDialog } from "./components/settings/setting-form-dialog";
17
+ import {
18
+ DeleteConfirmDialog,
19
+ SuspendConfirmDialog,
20
+ } from "./components/settings/setting-dialogs";
21
+
22
+ interface SystemSettingsPageProps {
23
+ lang: string;
24
+ }
25
+
26
+ interface SettingsData {
27
+ items: SettingConfig[];
28
+ groupedItems: Record<string, SettingConfig[]>;
29
+ }
30
+
31
+ export function SystemSettingsPage({ lang }: SystemSettingsPageProps) {
32
+ const router = useRouter();
33
+ const [loading, setLoading] = React.useState(true);
34
+ const [settings, setSettings] = React.useState<SettingsData | null>(null);
35
+ const [activeGroup, setActiveGroup] = React.useState(
36
+ SETTINGS_GROUPS[0]?.id || "general",
37
+ );
38
+ const [searchQuery, setSearchQuery] = React.useState("");
39
+ const [highlightedKey, setHighlightedKey] = React.useState<string | null>(
40
+ null,
41
+ );
42
+
43
+ // Dialog states
44
+ const [formDialogOpen, setFormDialogOpen] = React.useState(false);
45
+ const [deleteDialogOpen, setDeleteDialogOpen] = React.useState(false);
46
+ const [suspendDialogOpen, setSuspendDialogOpen] = React.useState(false);
47
+ const [selectedSetting, setSelectedSetting] =
48
+ React.useState<SettingConfig | null>(null);
49
+
50
+ // Fetch settings
51
+ const fetchSettings = React.useCallback(async () => {
52
+ setLoading(true);
53
+ try {
54
+ const res = await fetch("/api/admin/system/settings/all");
55
+ if (!res.ok) throw new Error("Failed to fetch settings");
56
+ const data = await res.json();
57
+
58
+ // Group items by category
59
+ const items: SettingConfig[] = data.data || [];
60
+ const groupedItems: Record<string, SettingConfig[]> = {};
61
+
62
+ for (const item of items) {
63
+ const category = item.category || "general";
64
+ if (!groupedItems[category]) {
65
+ groupedItems[category] = [];
66
+ }
67
+ groupedItems[category].push(item);
68
+ }
69
+
70
+ setSettings({ items, groupedItems });
71
+ } catch (error) {
72
+ console.error("Error fetching settings:", error);
73
+ toast.error("Không thể tải cấu hình hệ thống");
74
+ } finally {
75
+ setLoading(false);
76
+ }
77
+ }, []);
78
+
79
+ React.useEffect(() => {
80
+ fetchSettings();
81
+ }, [fetchSettings]);
82
+
83
+ // Handle setting update
84
+ const handleUpdate = React.useCallback(async (key: string, value: string) => {
85
+ try {
86
+ const res = await fetch("/api/admin/system/settings/update", {
87
+ method: "POST",
88
+ headers: { "Content-Type": "application/json" },
89
+ body: JSON.stringify({ key, value }),
90
+ });
91
+
92
+ if (!res.ok) throw new Error("Failed to update setting");
93
+
94
+ // Update local state
95
+ setSettings((prev) => {
96
+ if (!prev) return prev;
97
+ const items = prev.items.map((item) =>
98
+ item.key === key ? { ...item, value } : item,
99
+ );
100
+ const groupedItems: Record<string, SettingConfig[]> = {};
101
+ for (const item of items) {
102
+ const category = item.category || "general";
103
+ if (!groupedItems[category]) {
104
+ groupedItems[category] = [];
105
+ }
106
+ groupedItems[category].push(item);
107
+ }
108
+ return { items, groupedItems };
109
+ });
110
+ } catch (error) {
111
+ console.error("Error updating setting:", error);
112
+ throw error;
113
+ }
114
+ }, []);
115
+
116
+ // Action handlers
117
+ const handleDelete = React.useCallback((setting: SettingConfig) => {
118
+ setSelectedSetting(setting);
119
+ setDeleteDialogOpen(true);
120
+ }, []);
121
+
122
+ const handleToggleStatus = React.useCallback((setting: SettingConfig) => {
123
+ setSelectedSetting(setting);
124
+ setSuspendDialogOpen(true);
125
+ }, []);
126
+
127
+ const handleAddNew = React.useCallback(() => {
128
+ setSelectedSetting(null);
129
+ setFormDialogOpen(true);
130
+ }, []);
131
+
132
+ const handleDialogSuccess = React.useCallback(() => {
133
+ fetchSettings();
134
+ }, [fetchSettings]);
135
+
136
+ // Filter settings based on search query and active group
137
+ const filteredSettings = React.useMemo(() => {
138
+ if (!settings) return [];
139
+
140
+ let items = settings.items;
141
+
142
+ // Filter by active group
143
+ const group = SETTINGS_GROUPS.find((g) => g.id === activeGroup);
144
+ if (group) {
145
+ items = items.filter((item) => {
146
+ // Special case: phu_quy_* settings should be included in "integrations" group
147
+ const effectiveCategory = item.key.startsWith("phu_quy_") ? "phu_quy" : item.category;
148
+ return group.categories.includes(item.category) || group.categories.includes(effectiveCategory);
149
+ });
150
+ }
151
+
152
+ // Filter by search query
153
+ if (searchQuery.trim()) {
154
+ const query = searchQuery.toLowerCase();
155
+ items = items.filter(
156
+ (item) =>
157
+ item.key.toLowerCase().includes(query) ||
158
+ item.description?.toLowerCase().includes(query) ||
159
+ item.category.toLowerCase().includes(query),
160
+ );
161
+ }
162
+
163
+ return items;
164
+ }, [settings, activeGroup, searchQuery]);
165
+
166
+ // Group filtered settings by category
167
+ // Special handling: Group settings with key starting with "phu_quy_" into "phu_quy" category
168
+ const groupedFilteredSettings = React.useMemo(() => {
169
+ const grouped: Record<string, SettingConfig[]> = {};
170
+ for (const item of filteredSettings) {
171
+ // Special case: Group phu_quy_* settings together
172
+ let category = item.category || "general";
173
+ if (item.key.startsWith("phu_quy_")) {
174
+ category = "phu_quy";
175
+ }
176
+
177
+ if (!grouped[category]) {
178
+ grouped[category] = [];
179
+ }
180
+ grouped[category].push(item);
181
+ }
182
+ return grouped;
183
+ }, [filteredSettings]);
184
+
185
+ // Handle search keyboard navigation
186
+ const handleSearchKeyDown = React.useCallback(
187
+ (e: React.KeyboardEvent) => {
188
+ if (e.key === "Enter" && filteredSettings.length > 0) {
189
+ const firstItem = filteredSettings[0];
190
+ setHighlightedKey(firstItem.key);
191
+ document.getElementById(`setting-${firstItem.key}`)?.scrollIntoView({
192
+ behavior: "smooth",
193
+ block: "center",
194
+ });
195
+ setTimeout(() => setHighlightedKey(null), 3000);
196
+ }
197
+ },
198
+ [filteredSettings],
199
+ );
200
+
201
+ if (loading) {
202
+ return (
203
+ <div className="flex items-center justify-center min-h-[400px]">
204
+ <Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
205
+ </div>
206
+ );
207
+ }
208
+
209
+ return (
210
+ <div className="h-screen flex flex-col overflow-hidden bg-background">
211
+ {/* Header */}
212
+ <div className="border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60 shrink-0">
213
+ <div className="container flex flex-col md:flex-row items-start md:items-center justify-between py-4 md:h-16 px-4 gap-4 md:gap-0">
214
+ <div>
215
+ <h1 className="text-xl font-semibold">Cài đặt hệ thống</h1>
216
+ <p className="text-sm text-muted-foreground">
217
+ Quản lý cấu hình và tùy chỉnh hệ thống
218
+ </p>
219
+ </div>
220
+ <div className="flex items-center gap-2 self-end md:self-auto">
221
+ <Button
222
+ variant="outline"
223
+ size="sm"
224
+ onClick={fetchSettings}
225
+ disabled={loading}
226
+ className="hidden sm:flex"
227
+ >
228
+ <RefreshCw
229
+ className={cn("h-4 w-4 mr-2", loading && "animate-spin")}
230
+ />
231
+ Làm mới
232
+ </Button>
233
+ <Button
234
+ variant="outline"
235
+ size="icon"
236
+ onClick={fetchSettings}
237
+ disabled={loading}
238
+ className="sm:hidden"
239
+ >
240
+ <RefreshCw
241
+ className={cn("h-4 w-4", loading && "animate-spin")}
242
+ />
243
+ </Button>
244
+ <Button size="sm" onClick={handleAddNew}>
245
+ <Plus className="h-4 w-4 md:mr-2" />
246
+ <span className="hidden md:inline">Thêm cấu hình</span>
247
+ </Button>
248
+ </div>
249
+ </div>
250
+ </div>
251
+
252
+ {/* Main Content */}
253
+ <div className="flex-1 min-h-0 container px-2 md:px-4 py-4 md:py-6 flex flex-col">
254
+ <div className="flex flex-col md:flex-row gap-4 md:gap-8 h-full min-h-0">
255
+ {/* Sidebar */}
256
+ <SettingsSidebar
257
+ groups={SETTINGS_GROUPS}
258
+ activeGroup={activeGroup}
259
+ onGroupChange={setActiveGroup}
260
+ className="w-full md:w-64 shrink-0 md:overflow-y-auto"
261
+ />
262
+
263
+ {/* Content */}
264
+ <div className="flex-1 min-w-0 flex flex-col h-full min-h-0">
265
+ {/* Search */}
266
+ <SettingsSearch
267
+ value={searchQuery}
268
+ onChange={setSearchQuery}
269
+ onKeyDown={handleSearchKeyDown}
270
+ resultCount={filteredSettings.length}
271
+ className="mb-6 shrink-0"
272
+ />
273
+
274
+ {/* Settings Groups */}
275
+ <div className="flex-1 overflow-y-auto pr-2 space-y-8">
276
+ {Object.entries(groupedFilteredSettings).length === 0 ? (
277
+ <div className="text-center py-12">
278
+ <p className="text-muted-foreground">
279
+ {searchQuery
280
+ ? "Không tìm thấy cấu hình phù hợp"
281
+ : "Chưa có cấu hình nào trong nhóm này"}
282
+ </p>
283
+ <Button
284
+ variant="outline"
285
+ size="sm"
286
+ className="mt-4"
287
+ onClick={handleAddNew}
288
+ >
289
+ <Plus className="h-4 w-4 mr-2" />
290
+ Thêm cấu hình mới
291
+ </Button>
292
+ </div>
293
+ ) : (
294
+ Object.entries(groupedFilteredSettings).map(
295
+ ([category, items]) => (
296
+ <SettingsSection
297
+ key={category}
298
+ id={category}
299
+ title={formatCategoryName(category)}
300
+ count={items.length}
301
+ >
302
+ {items.map((item) => (
303
+ <SettingField
304
+ key={item.id}
305
+ setting={item}
306
+ onUpdate={handleUpdate}
307
+ onDelete={handleDelete}
308
+ onToggleStatus={handleToggleStatus}
309
+ isHighlighted={highlightedKey === item.key}
310
+ />
311
+ ))}
312
+ </SettingsSection>
313
+ ),
314
+ )
315
+ )}
316
+ </div>
317
+ </div>
318
+ </div>
319
+ </div>
320
+
321
+ {/* Dialogs */}
322
+ <SettingFormDialog
323
+ open={formDialogOpen}
324
+ onOpenChange={setFormDialogOpen}
325
+ setting={selectedSetting}
326
+ onSuccess={handleDialogSuccess}
327
+ />
328
+ <DeleteConfirmDialog
329
+ open={deleteDialogOpen}
330
+ onOpenChange={setDeleteDialogOpen}
331
+ setting={selectedSetting}
332
+ onSuccess={handleDialogSuccess}
333
+ />
334
+ <SuspendConfirmDialog
335
+ open={suspendDialogOpen}
336
+ onOpenChange={setSuspendDialogOpen}
337
+ setting={selectedSetting}
338
+ onSuccess={handleDialogSuccess}
339
+ />
340
+ </div>
341
+ );
342
+ }
343
+
344
+ // Helper function to format category names
345
+ function formatCategoryName(category: string): string {
346
+ const names: Record<string, string> = {
347
+ general: "Cấu hình chung",
348
+ app: "Ứng dụng",
349
+ system: "Hệ thống",
350
+ pricing: "Giá cả",
351
+ sales: "Bán hàng",
352
+ purchase: "Mua hàng",
353
+ inventory: "Kho hàng",
354
+ zalo: "Zalo",
355
+ telegram: "Telegram",
356
+ email: "Email",
357
+ sms: "SMS",
358
+ webhook: "Webhook",
359
+ notifications: "Thông báo",
360
+ alerts: "Cảnh báo",
361
+ security: "Bảo mật",
362
+ auth: "Xác thực",
363
+ audit: "Nhật ký",
364
+ automation: "Tự động hóa",
365
+ cron: "Lịch trình",
366
+ jobs: "Công việc",
367
+ scheduler: "Lập lịch",
368
+ theme: "Giao diện",
369
+ ui: "Hiển thị",
370
+ display: "Màn hình",
371
+ debug: "Debug",
372
+ cache: "Cache",
373
+ performance: "Hiệu năng",
374
+ logs: "Logs",
375
+ phu_quy: "Phú Quý",
376
+ };
377
+ return (
378
+ names[category] || category.charAt(0).toUpperCase() + category.slice(1)
379
+ );
380
+ }
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+ import { STATUS_ACTIVE, STATUS_INACTIVE, STATUS_VALUES } from "../constants";
3
+
4
+ /**
5
+ * System Category Group Validation Schema
6
+ */
7
+ export const systemCategoryGroupSchema = z.object({
8
+ code: z
9
+ .string()
10
+ .min(1, "Mã nhóm danh mục là bắt buộc")
11
+ .max(50, "Mã nhóm danh mục tối đa 50 ký tự")
12
+ .trim(),
13
+
14
+ name: z
15
+ .string()
16
+ .min(1, "Tên nhóm danh mục là bắt buộc")
17
+ .max(200, "Tên nhóm danh mục tối đa 200 ký tự")
18
+ .trim(),
19
+
20
+ description: z
21
+ .string()
22
+ .max(1000, "Mô tả tối đa 1000 ký tự")
23
+ .optional()
24
+ .or(z.literal(""))
25
+ .transform((val) => val || ""),
26
+
27
+ status: z.preprocess((val) => {
28
+ if (typeof val === "boolean") {
29
+ return val ? STATUS_ACTIVE : STATUS_INACTIVE;
30
+ }
31
+ if (val === "Active" || val === "Inactive") {
32
+ return val === "Active" ? STATUS_ACTIVE : STATUS_INACTIVE;
33
+ }
34
+ return val;
35
+ }, z.enum(STATUS_VALUES).default(STATUS_ACTIVE)),
36
+ });
37
+
38
+ export type SystemCategoryGroupInput = z.infer<
39
+ typeof systemCategoryGroupSchema
40
+ >;
41
+ export const systemCategoryGroupUpdateSchema =
42
+ systemCategoryGroupSchema.partial();
43
+ export const systemCategoryGroupBulkSchema = z.object({
44
+ ids: z.array(z.string()).min(1, "Phải chọn ít nhất 1 nhóm danh mục"),
45
+ action: z.enum(["delete", "activate", "deactivate"]),
46
+ });