@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,45 @@
1
+ import type { ReactNode } from "react";
2
+ import React, { createContext, useContext } from "react";
3
+
4
+ export interface TenantBranding {
5
+ logo?: string;
6
+ companyName: string;
7
+ primaryColor?: string;
8
+ favicon?: string;
9
+ }
10
+
11
+ export interface TenantConfig {
12
+ id: string;
13
+ name: string;
14
+ branding: TenantBranding;
15
+ // Add more tenant-specific configs here (e.g., date formats, currency)
16
+ dateFormat?: string;
17
+ currency?: string;
18
+ }
19
+
20
+ export interface TenantContextValue {
21
+ tenant: TenantConfig;
22
+ }
23
+
24
+ const TenantContext = createContext<TenantContextValue | undefined>(undefined);
25
+
26
+ interface TenantProviderProps {
27
+ children: ReactNode;
28
+ tenant: TenantConfig;
29
+ }
30
+
31
+ export function TenantProvider({ children, tenant }: TenantProviderProps) {
32
+ return (
33
+ <TenantContext.Provider value={{ tenant }}>
34
+ {children}
35
+ </TenantContext.Provider>
36
+ );
37
+ }
38
+
39
+ export function useTenantContext() {
40
+ const context = useContext(TenantContext);
41
+ if (context === undefined) {
42
+ throw new Error("useTenantContext must be used within a TenantProvider");
43
+ }
44
+ return context;
45
+ }
@@ -0,0 +1,158 @@
1
+ "use client";
2
+
3
+ import { memo, useCallback } from "react";
4
+ import { format } from "date-fns";
5
+ import { useRouter } from "next/navigation";
6
+ import {
7
+ Card,
8
+ CardContent,
9
+ CardDescription,
10
+ CardHeader,
11
+ CardTitle,
12
+ Button,
13
+ Badge,
14
+ } from "../../../ui";
15
+ import { Edit, Copy, Trash2 } from "lucide-react";
16
+ import type { Role } from "../../types";
17
+
18
+ interface RoleCardProps {
19
+ role: Role;
20
+ lang: string;
21
+ canUpdate?: boolean;
22
+ canCreate?: boolean;
23
+ canDelete?: boolean;
24
+ onDuplicate: (role: Role) => void;
25
+ onDelete: (role: Role) => void;
26
+ }
27
+
28
+ const roleColors: Record<string, string> = {
29
+ admin: "bg-red-500",
30
+ finance_manager: "bg-blue-500",
31
+ purchasing_staff: "bg-green-500",
32
+ kitchen_manager: "bg-orange-500",
33
+ warehouse_staff: "bg-purple-500",
34
+ supplier: "bg-gray-500",
35
+ };
36
+
37
+ export const RoleCard = memo(function RoleCard({
38
+ role,
39
+ lang,
40
+ canUpdate,
41
+ canCreate,
42
+ canDelete,
43
+ onDuplicate,
44
+ onDelete,
45
+ }: RoleCardProps) {
46
+ const router = useRouter();
47
+ const permissionsCount = role.permissions.length;
48
+ const roleColor = roleColors[role.code] || "bg-indigo-500";
49
+
50
+ const handleEdit = useCallback(() => {
51
+ router.push(`/${lang}/roles/${role.id}/edit`);
52
+ }, [router, lang, role.id]);
53
+
54
+ const handleDuplicate = useCallback(() => {
55
+ onDuplicate(role);
56
+ }, [onDuplicate, role]);
57
+
58
+ const handleDelete = useCallback(() => {
59
+ onDelete(role);
60
+ }, [onDelete, role]);
61
+
62
+ return (
63
+ <Card className="group relative transition-all duration-300 border border-slate-200 dark:border-slate-800 bg-white dark:bg-slate-900 rounded-[12px] shadow-sm hover:shadow-md hover:border-primary/40 overflow-hidden">
64
+ <CardHeader className="p-4 pb-2">
65
+ <div className="flex items-start justify-between">
66
+ <div className="flex items-center space-x-3">
67
+ <div className="relative flex items-center justify-center">
68
+ <div className={`w-3 h-3 rounded-full ${roleColor} ring-4 ring-slate-50 dark:ring-slate-800/50`} />
69
+ </div>
70
+ <div>
71
+ <CardTitle className="text-[15px] font-bold text-slate-900 dark:text-slate-100 leading-tight">
72
+ {role.name}
73
+ </CardTitle>
74
+ <CardDescription className="text-[11px] font-mono text-slate-500 mt-0.5">
75
+ {role.code}
76
+ </CardDescription>
77
+ </div>
78
+ </div>
79
+ {role.status === "active" ? (
80
+ <Badge
81
+ variant="outline"
82
+ className="bg-emerald-50 text-emerald-600 border-emerald-200 dark:bg-emerald-500/10 dark:text-emerald-400 dark:border-emerald-500/20 text-[10px] px-2 py-0 h-5 font-semibold"
83
+ >
84
+ Hoạt động
85
+ </Badge>
86
+ ) : (
87
+ <Badge
88
+ variant="outline"
89
+ className="bg-slate-100 text-slate-500 border-slate-200 dark:bg-slate-800 dark:text-slate-400 dark:border-slate-700 text-[10px] px-2 py-0 h-5 font-semibold"
90
+ >
91
+ Khóa
92
+ </Badge>
93
+ )}
94
+ </div>
95
+ </CardHeader>
96
+ <CardContent className="p-4 pt-0 space-y-3">
97
+ <p className="text-[13px] text-slate-500 dark:text-slate-400 line-clamp-2 min-h-[2.5rem] leading-relaxed">
98
+ {role.description || "Chưa có mô tả chi tiết cho vai trò này."}
99
+ </p>
100
+
101
+ <div className="flex items-center justify-between bg-slate-50 dark:bg-slate-800/50 rounded-lg p-2.5 border border-slate-100 dark:border-slate-800">
102
+ <div className="flex flex-col items-center flex-1 border-r border-slate-200 dark:border-slate-700">
103
+ <span className="text-[16px] font-black text-primary leading-none mb-1">
104
+ {permissionsCount}
105
+ </span>
106
+ <span className="text-[9px] text-slate-500 font-bold uppercase tracking-wider">
107
+ Quyền hạn
108
+ </span>
109
+ </div>
110
+ <div className="flex flex-col items-center flex-1">
111
+ <span className="text-[16px] font-black text-emerald-600 dark:text-emerald-500 leading-none mb-1">
112
+ {role.usersCount}
113
+ </span>
114
+ <span className="text-[9px] text-slate-500 font-bold uppercase tracking-wider">
115
+ Người dùng
116
+ </span>
117
+ </div>
118
+ </div>
119
+
120
+ <div className="flex gap-2 pt-2">
121
+ {canUpdate && (
122
+ <Button
123
+ variant="outline"
124
+ size="sm"
125
+ className="flex-1 h-8 text-[12px] font-medium bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800 hover:text-slate-900 dark:hover:text-slate-100 transition-colors rounded-[8px] shadow-sm"
126
+ onClick={handleEdit}
127
+ >
128
+ <Edit className="mr-1.5 h-3.5 w-3.5 text-slate-400" />
129
+ Sửa
130
+ </Button>
131
+ )}
132
+ {canCreate && (
133
+ <Button
134
+ variant="outline"
135
+ size="sm"
136
+ className="flex-1 h-8 text-[12px] font-medium bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800 hover:bg-slate-50 dark:hover:bg-slate-800 hover:text-slate-900 dark:hover:text-slate-100 transition-colors rounded-[8px] shadow-sm"
137
+ onClick={handleDuplicate}
138
+ >
139
+ <Copy className="mr-1.5 h-3.5 w-3.5 text-slate-400" />
140
+ Chép
141
+ </Button>
142
+ )}
143
+ {canDelete && (
144
+ <Button
145
+ variant="outline"
146
+ size="sm"
147
+ className="flex-1 h-8 text-[12px] font-medium border-slate-200 dark:border-slate-800 text-red-600 dark:text-red-500 hover:bg-red-50 dark:hover:bg-red-500/10 hover:text-red-700 dark:hover:text-red-400 transition-colors bg-white dark:bg-slate-900 rounded-[8px] shadow-sm"
148
+ onClick={handleDelete}
149
+ >
150
+ <Trash2 className="mr-1.5 h-3.5 w-3.5" />
151
+ Xóa
152
+ </Button>
153
+ )}
154
+ </div>
155
+ </CardContent>
156
+ </Card>
157
+ );
158
+ });
@@ -0,0 +1,29 @@
1
+ "use client";
2
+
3
+ import { memo } from "react";
4
+ import { Shield, ShieldCheck, Key, Users } from "lucide-react";
5
+
6
+ import { CompactStatBar, CompactStatItem } from "../../../ui/data-display/compact-stat-bar";
7
+
8
+ interface RoleStatsCardsProps {
9
+ totalRoles: number;
10
+ activeRoles: number;
11
+ totalPermissions: number;
12
+ totalUsers: number;
13
+ }
14
+
15
+ export const RoleStatsCards = memo(function RoleStatsCards({
16
+ totalRoles,
17
+ activeRoles,
18
+ totalPermissions,
19
+ totalUsers,
20
+ }: RoleStatsCardsProps) {
21
+ const items: CompactStatItem[] = [
22
+ { id: "total", label: "Tổng vai trò", value: totalRoles || 0, icon: Shield, colorTheme: "dark" },
23
+ { id: "active", label: "Hoạt động", value: activeRoles || 0, icon: ShieldCheck, colorTheme: "green" },
24
+ { id: "perms", label: "Tổng quyền hạn", value: totalPermissions || 0, icon: Key, colorTheme: "purple" },
25
+ { id: "users", label: "Tổng người dùng", value: totalUsers || 0, icon: Users, colorTheme: "orange" },
26
+ ];
27
+
28
+ return <CompactStatBar items={items} />;
29
+ });
@@ -0,0 +1,123 @@
1
+ "use client";
2
+
3
+ import { memo, useState, useEffect } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import {
6
+ Button,
7
+ Input,
8
+ Select,
9
+ SelectContent,
10
+ SelectItem,
11
+ SelectTrigger,
12
+ SelectValue,
13
+ } from "../../../ui";
14
+ import { Plus, Search, Download, Upload, X } from "lucide-react";
15
+
16
+ interface RoleToolbarProps {
17
+ dictionary: any;
18
+ totalRows: number;
19
+ searchTerm: string;
20
+ statusFilter: string;
21
+ onSearchChange: (value: string) => void;
22
+ onStatusFilterChange: (value: string) => void;
23
+ lang: string;
24
+ canCreate?: boolean;
25
+ canImport?: boolean;
26
+ canExport?: boolean;
27
+ }
28
+
29
+ export const RoleToolbar = memo(function RoleToolbar({
30
+ dictionary,
31
+ totalRows,
32
+ searchTerm,
33
+ statusFilter,
34
+ onSearchChange,
35
+ onStatusFilterChange,
36
+ lang,
37
+ canCreate,
38
+ canImport,
39
+ canExport,
40
+ }: RoleToolbarProps) {
41
+ const router = useRouter();
42
+ const [localSearchTerm, setLocalSearchTerm] = useState(searchTerm);
43
+
44
+ // Sync local state when prop changes (e.g. clear filter from parent)
45
+ useEffect(() => {
46
+ setLocalSearchTerm(searchTerm);
47
+ }, [searchTerm]);
48
+
49
+ // Debounce search change
50
+ useEffect(() => {
51
+ const handler = setTimeout(() => {
52
+ if (localSearchTerm !== searchTerm) {
53
+ onSearchChange(localSearchTerm);
54
+ }
55
+ }, 500);
56
+
57
+ return () => {
58
+ clearTimeout(handler);
59
+ };
60
+ }, [localSearchTerm, onSearchChange, searchTerm]);
61
+
62
+ return (
63
+ <div className="flex items-center gap-2 flex-wrap">
64
+ <div className="relative">
65
+ <Search className="absolute left-3 top-2.5 h-4 w-4 text-muted-foreground" />
66
+ <Input
67
+ placeholder="Tìm kiếm vai trò..."
68
+ value={localSearchTerm}
69
+ onChange={(e) => setLocalSearchTerm(e.target.value)}
70
+ className="h-9 w-[220px] lg:w-[280px] pl-9 text-[13px] bg-[#ffffff] dark:bg-[#181d26] border-[#dddddd] dark:border-[#333840] hover:bg-slate-50 transition-colors shadow-sm rounded-[6px] focus-visible:ring-1 focus-visible:ring-primary"
71
+ />
72
+ {localSearchTerm && (
73
+ <Button
74
+ variant="ghost"
75
+ size="sm"
76
+ className="absolute right-1 top-1/2 -translate-y-1/2 h-7 w-7 p-0 rounded-md hover:bg-muted"
77
+ onClick={() => setLocalSearchTerm("")}
78
+ >
79
+ <X className="h-3.5 w-3.5" />
80
+ </Button>
81
+ )}
82
+ </div>
83
+
84
+ <Select value={statusFilter} onValueChange={onStatusFilterChange}>
85
+ <SelectTrigger className="h-9 w-[160px] text-[13px] bg-[#ffffff] dark:bg-[#181d26] border-[#dddddd] dark:border-[#333840] hover:bg-slate-50 transition-colors shadow-sm rounded-[6px] focus-visible:ring-primary">
86
+ <SelectValue placeholder="Trạng thái" />
87
+ </SelectTrigger>
88
+ <SelectContent className="rounded-lg">
89
+ <SelectItem value="all">Tất cả</SelectItem>
90
+ <SelectItem value="active">Hoạt động</SelectItem>
91
+ <SelectItem value="inactive">Không hoạt động</SelectItem>
92
+ </SelectContent>
93
+ </Select>
94
+
95
+ <div className="ml-auto flex items-center gap-2">
96
+ {canImport && (
97
+ <Button variant="outline" size="sm" className="h-9 px-3 rounded-[6px] shadow-sm bg-[#ffffff] dark:bg-[#181d26] border-[#dddddd] dark:border-[#333840] hover:bg-slate-50">
98
+ <Upload className="h-4 w-4 lg:mr-2" />
99
+ <span className="hidden lg:inline text-[13px]">Nhập</span>
100
+ </Button>
101
+ )}
102
+
103
+ {canExport && (
104
+ <Button variant="outline" size="sm" className="h-9 px-3 rounded-[6px] shadow-sm bg-[#ffffff] dark:bg-[#181d26] border-[#dddddd] dark:border-[#333840] hover:bg-slate-50">
105
+ <Download className="h-4 w-4 lg:mr-2" />
106
+ <span className="hidden lg:inline text-[13px]">Xuất</span>
107
+ </Button>
108
+ )}
109
+
110
+ {canCreate && (
111
+ <Button
112
+ onClick={() => router.push(`/${lang}/roles/new`)}
113
+ size="sm"
114
+ className="h-9 px-4 text-[13px] bg-primary hover:bg-primary/90 text-primary-foreground shadow-sm rounded-[6px] font-medium border-0"
115
+ >
116
+ <Plus className="mr-1.5 h-4 w-4" />
117
+ <span>Thêm mới</span>
118
+ </Button>
119
+ )}
120
+ </div>
121
+ </div>
122
+ );
123
+ });
@@ -0,0 +1,159 @@
1
+ import { useCallback, useState } from "react";
2
+ import { useRouter } from "next/navigation";
3
+ import { useSession } from "next-auth/react";
4
+ import { toast } from "sonner";
5
+
6
+ import { useTabContentCache } from "../../ui/layout/tab-content-cache";
7
+ import { useTabNavigation } from "../../ui/layout/tab-navigation-provider";
8
+ import type { Role } from "../types";
9
+
10
+ // Safe wrapper to prevent crashing when useSession is called outside of SessionProvider
11
+ // This often happens during Error Boundary or Hydration failures.
12
+ function useSafeSession() {
13
+ try {
14
+ return useSession();
15
+ } catch (error) {
16
+ console.warn("[useSafeSession] Context error caught. Fallback to unauthenticated state.");
17
+ return { data: null, status: "unauthenticated" as const, update: async () => null };
18
+ }
19
+ }
20
+
21
+ export function useRoleOperations(mutate: () => void) {
22
+ const router = useRouter();
23
+ const { update } = useSafeSession();
24
+ const { clearAllCache } = useTabContentCache();
25
+ const { clearTabs } = useTabNavigation();
26
+
27
+ const afterRbacChange = useCallback(async () => {
28
+ // Clear persisted tab caches/state so UI doesn't keep stale permissions/pages
29
+ try {
30
+ clearAllCache();
31
+ clearTabs();
32
+
33
+ if (typeof window !== "undefined") {
34
+ sessionStorage.removeItem("tab-content-cache");
35
+ sessionStorage.removeItem("tab-navigation-state");
36
+ }
37
+ } catch {
38
+ // best-effort only
39
+ }
40
+
41
+ // Refresh NextAuth session (trigger "update") so roles/permissions reload immediately
42
+ try {
43
+ await update();
44
+ } catch {
45
+ // best-effort only
46
+ }
47
+
48
+ router.refresh();
49
+ }, [router, update, clearAllCache, clearTabs]);
50
+
51
+ const handleCreateRole = useCallback(
52
+ async (roleData: any) => {
53
+ try {
54
+ const res = await fetch("/api/roles", {
55
+ method: "POST",
56
+ headers: { "Content-Type": "application/json" },
57
+ body: JSON.stringify(roleData),
58
+ });
59
+
60
+ if (!res.ok) {
61
+ const errorData = await res.json();
62
+ throw new Error(errorData.error || "Failed to create role");
63
+ }
64
+
65
+ toast.success("Thành công", {
66
+ description: "Đã tạo vai trò mới thành công",
67
+ });
68
+ mutate();
69
+ await afterRbacChange();
70
+ return true;
71
+ } catch (error: any) {
72
+ toast.error("Lỗi", {
73
+ description: error.message,
74
+ });
75
+ return false;
76
+ }
77
+ },
78
+ [mutate, afterRbacChange],
79
+ );
80
+
81
+ const handleEditRole = useCallback(
82
+ async (roleId: string, roleData: any) => {
83
+ try {
84
+ const res = await fetch(`/api/roles/${roleId}`, {
85
+ method: "PUT",
86
+ headers: { "Content-Type": "application/json" },
87
+ body: JSON.stringify(roleData),
88
+ });
89
+
90
+ if (!res.ok) {
91
+ const errorData = await res.json();
92
+ throw new Error(errorData.error || "Failed to update role");
93
+ }
94
+
95
+ toast.success("Thành công", {
96
+ description: "Đã cập nhật vai trò thành công",
97
+ });
98
+ mutate();
99
+ await afterRbacChange();
100
+ return true;
101
+ } catch (error: any) {
102
+ toast.error("Lỗi", {
103
+ description: error.message,
104
+ });
105
+ return false;
106
+ }
107
+ },
108
+ [mutate, afterRbacChange],
109
+ );
110
+
111
+ const handleDeleteRole = useCallback(
112
+ async (roleId: string) => {
113
+ try {
114
+ const res = await fetch(`/api/roles/${roleId}`, {
115
+ method: "DELETE",
116
+ });
117
+
118
+ if (!res.ok) {
119
+ const errorData = await res.json();
120
+ throw new Error(errorData.error || "Failed to delete role");
121
+ }
122
+
123
+ toast.success("Thành công", {
124
+ description: "Đã xóa vai trò thành công",
125
+ });
126
+ mutate();
127
+ await afterRbacChange();
128
+ return true;
129
+ } catch (error: any) {
130
+ toast.error("Lỗi", {
131
+ description: error.message,
132
+ });
133
+ return false;
134
+ }
135
+ },
136
+ [mutate, afterRbacChange],
137
+ );
138
+
139
+ const handleDuplicateRole = useCallback(
140
+ async (role: Role) => {
141
+ const duplicatedRoleData = {
142
+ name: `${role.name} (Copy)`,
143
+ code: `${role.code}_copy_${Date.now()}`,
144
+ description: role.description,
145
+ status: role.status,
146
+ permissions: role.permissions,
147
+ };
148
+ return handleCreateRole(duplicatedRoleData);
149
+ },
150
+ [handleCreateRole],
151
+ );
152
+
153
+ return {
154
+ handleCreateRole,
155
+ handleEditRole,
156
+ handleDeleteRole,
157
+ handleDuplicateRole,
158
+ };
159
+ }
@@ -0,0 +1,59 @@
1
+ import { useState, useMemo } from "react";
2
+ import useSWR from "swr";
3
+ import type { RolesResponse, Role } from "../types";
4
+
5
+ const fetcher = (url: string) => fetch(url).then((res) => res.json());
6
+
7
+ export function useRolesData(initialData: RolesResponse) {
8
+ const [page, setPage] = useState(1);
9
+ const [pageSize, setPageSize] = useState(20);
10
+ const [searchTerm, setSearchTerm] = useState("");
11
+ const [statusFilter, setStatusFilter] = useState("all");
12
+
13
+ const queryParams = useMemo(() => {
14
+ const params = new URLSearchParams({
15
+ page: page.toString(),
16
+ pageSize: pageSize.toString(),
17
+ });
18
+ if (searchTerm) params.set("search", searchTerm);
19
+ if (statusFilter !== "all") params.set("status", statusFilter);
20
+ return params.toString();
21
+ }, [page, pageSize, searchTerm, statusFilter]);
22
+
23
+ const {
24
+ data: response,
25
+ error,
26
+ isLoading,
27
+ mutate,
28
+ } = useSWR(`/api/roles?${queryParams}`, fetcher, {
29
+ fallbackData: initialData,
30
+ revalidateOnFocus: false,
31
+ revalidateOnReconnect: false,
32
+ keepPreviousData: true,
33
+ });
34
+
35
+ const roles: Role[] = response?.items || [];
36
+ const totalRows = response?.total || 0;
37
+
38
+ const activeRolesCount = useMemo(
39
+ () => roles.filter((r) => r.status === "active").length,
40
+ [roles],
41
+ );
42
+
43
+ return {
44
+ roles,
45
+ totalRows,
46
+ activeRolesCount,
47
+ page,
48
+ pageSize,
49
+ searchTerm,
50
+ statusFilter,
51
+ setPage,
52
+ setPageSize,
53
+ setSearchTerm,
54
+ setStatusFilter,
55
+ error,
56
+ isLoading,
57
+ mutate,
58
+ };
59
+ }