@goplusvn/core 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (369) hide show
  1. package/package.json +2 -1
  2. package/src/assets/erp_wallpaper.png +0 -0
  3. package/src/assets/goeat_logo.png +0 -0
  4. package/src/audit/audit-manager.ts +139 -0
  5. package/src/audit/index.ts +11 -0
  6. package/src/audit/memory-audit-logger.ts +86 -0
  7. package/src/audit/types.ts +50 -0
  8. package/src/auth/auth-service.ts +97 -0
  9. package/src/auth/index.ts +266 -0
  10. package/src/code-generation/index.ts +69 -0
  11. package/src/configs/auth-routes.ts +17 -0
  12. package/src/configs/crud.ts +136 -0
  13. package/src/configs/data/navigations.ts +781 -0
  14. package/src/configs/data/oauth-links.ts +10 -0
  15. package/src/configs/entities/material-categories.config.ts +125 -0
  16. package/src/configs/i18n.ts +12 -0
  17. package/src/configs/index.ts +26 -0
  18. package/src/configs/status.ts +25 -0
  19. package/src/configs/themes.ts +100 -0
  20. package/src/crud/components/crud-bulk-actions.tsx +91 -0
  21. package/src/crud/components/crud-card-view.tsx +241 -0
  22. package/src/crud/components/crud-context.tsx +122 -0
  23. package/src/crud/components/crud-delete-dialog.tsx +145 -0
  24. package/src/crud/components/crud-dialog.tsx +406 -0
  25. package/src/crud/components/crud-empty-state.tsx +104 -0
  26. package/src/crud/components/crud-export-button.tsx +170 -0
  27. package/src/crud/components/crud-field-renderer.tsx +653 -0
  28. package/src/crud/components/crud-filter-chips.tsx +102 -0
  29. package/src/crud/components/crud-filters/checkbox-filter.tsx +97 -0
  30. package/src/crud/components/crud-filters/datetime-filter.tsx +83 -0
  31. package/src/crud/components/crud-filters/filter-builder.tsx +66 -0
  32. package/src/crud/components/crud-filters/index.tsx +76 -0
  33. package/src/crud/components/crud-filters/radio-filter.tsx +86 -0
  34. package/src/crud/components/crud-filters/select-filter.tsx +141 -0
  35. package/src/crud/components/crud-filters/text-filter.tsx +86 -0
  36. package/src/crud/components/crud-form.tsx +642 -0
  37. package/src/crud/components/crud-import-dialog.tsx +440 -0
  38. package/src/crud/components/crud-infinite-scroll.tsx +116 -0
  39. package/src/crud/components/crud-page.tsx +1017 -0
  40. package/src/crud/components/crud-provider.tsx +277 -0
  41. package/src/crud/components/crud-row-actions.tsx +189 -0
  42. package/src/crud/components/crud-search.tsx +82 -0
  43. package/src/crud/components/crud-sheet.tsx +336 -0
  44. package/src/crud/components/crud-table-skeleton.tsx +26 -0
  45. package/src/crud/components/crud-table-toolbar.tsx +91 -0
  46. package/src/crud/components/crud-table.tsx +352 -0
  47. package/src/crud/components/crud-virtual-table.tsx +55 -0
  48. package/src/crud/components/index.tsx +20 -0
  49. package/src/crud/crud-filters/checkbox-filter.tsx +87 -0
  50. package/src/crud/crud-filters/datetime-filter.tsx +82 -0
  51. package/src/crud/crud-filters/filter-builder.tsx +64 -0
  52. package/src/crud/crud-filters/index.tsx +78 -0
  53. package/src/crud/crud-filters/radio-filter.tsx +79 -0
  54. package/src/crud/crud-filters/select-filter.tsx +148 -0
  55. package/src/crud/crud-filters/text-filter.tsx +81 -0
  56. package/src/crud/index.ts +43 -0
  57. package/src/crud/lib/crud-service.test.ts +334 -0
  58. package/src/crud/lib/crud-service.ts +358 -0
  59. package/src/crud/lib/crud-utils.test.ts +354 -0
  60. package/src/crud/lib/crud-utils.ts +299 -0
  61. package/src/crud/lib/crud-validator.ts +247 -0
  62. package/src/crud/lib/data-loader.ts +234 -0
  63. package/src/crud/lib/field-calculator.ts +241 -0
  64. package/src/crud/lib/field-formatter.ts +240 -0
  65. package/src/crud/lib/import-export-service.test.ts +290 -0
  66. package/src/crud/lib/import-export-service.ts +352 -0
  67. package/src/crud/lib/import-server-utils.ts +109 -0
  68. package/src/crud/lib/lazy-loader.ts +241 -0
  69. package/src/crud/lib/parse-filters.ts +85 -0
  70. package/src/crud/lib/permissions.ts +52 -0
  71. package/src/crud/lib/serialize-config.ts +60 -0
  72. package/src/crud/lib/stream-loader.ts +145 -0
  73. package/src/crud/lib/translate-config.ts +335 -0
  74. package/src/crud/lib/types.ts +11 -0
  75. package/src/crud/pages/entity-crud-page.tsx +144 -0
  76. package/src/crud/server.ts +8 -0
  77. package/src/home/constants.tsx +142 -0
  78. package/src/home/feature-showcase.tsx +171 -0
  79. package/src/home/home-page.tsx +191 -0
  80. package/src/home/hooks/index.ts +1 -0
  81. package/src/home/hooks/useWidgetPreferences.ts +167 -0
  82. package/src/home/index.ts +33 -0
  83. package/src/home/quick-access-dialog.tsx +271 -0
  84. package/src/home/quick-access-menu.tsx +267 -0
  85. package/src/home/types.ts +140 -0
  86. package/src/home/welcome-card.tsx +92 -0
  87. package/src/home/widget-container.tsx +258 -0
  88. package/src/home/widgets/base-widget.tsx +200 -0
  89. package/src/home/widgets/customers-widget.tsx +74 -0
  90. package/src/home/widgets/index.ts +6 -0
  91. package/src/home/widgets/orders-widget.tsx +87 -0
  92. package/src/home/widgets/revenue-widget.tsx +71 -0
  93. package/src/home/widgets/stock-widget.tsx +109 -0
  94. package/src/hooks/index.tsx +598 -0
  95. package/src/hooks/use-tenant.test.tsx +30 -0
  96. package/src/hooks/use-tenant.ts +5 -0
  97. package/src/index.ts +17 -0
  98. package/src/infrastructure/__tests__/architecture-verification.spec.ts +103 -0
  99. package/src/infrastructure/api-service.ts +317 -0
  100. package/src/infrastructure/cache/cache-manager.ts +107 -0
  101. package/src/infrastructure/cache/cache.ts +120 -0
  102. package/src/infrastructure/cache/index.ts +8 -0
  103. package/src/infrastructure/cache/types.ts +48 -0
  104. package/src/infrastructure/cron/cron-manager.ts +239 -0
  105. package/src/infrastructure/cron/index.ts +6 -0
  106. package/src/infrastructure/cron/types.ts +41 -0
  107. package/src/infrastructure/event-bus/event-bus.ts +145 -0
  108. package/src/infrastructure/event-bus/index.ts +2 -0
  109. package/src/infrastructure/event-bus/types.ts +22 -0
  110. package/src/infrastructure/index.ts +32 -0
  111. package/src/infrastructure/lock/decorators.ts +67 -0
  112. package/src/infrastructure/lock/index.ts +2 -0
  113. package/src/infrastructure/lock/lock-manager.ts +33 -0
  114. package/src/infrastructure/logger/index.ts +2 -0
  115. package/src/infrastructure/logger/logger.ts +96 -0
  116. package/src/infrastructure/logger/types.ts +25 -0
  117. package/src/layout/index.tsx +185 -0
  118. package/src/navigation/index.ts +91 -0
  119. package/src/notification/index.ts +14 -0
  120. package/src/notification/notification-service.ts +120 -0
  121. package/src/notification/storage/in-memory.ts +56 -0
  122. package/src/notification/storage/index.ts +1 -0
  123. package/src/notification/types.ts +51 -0
  124. package/src/organization/branch-service.ts +299 -0
  125. package/src/organization/branches.config.ts +154 -0
  126. package/src/organization/index.ts +5 -0
  127. package/src/plugin/apps-registry.ts +97 -0
  128. package/src/plugin/index.ts +5 -0
  129. package/src/plugin/types.ts +41 -0
  130. package/src/providers/index.tsx +109 -0
  131. package/src/providers/tenant-provider.tsx +45 -0
  132. package/src/rbac/components/roles/role-card.tsx +158 -0
  133. package/src/rbac/components/roles/role-stats-cards.tsx +29 -0
  134. package/src/rbac/components/roles/role-toolbar.tsx +123 -0
  135. package/src/rbac/hooks/use-role-operations.ts +159 -0
  136. package/src/rbac/hooks/use-roles-data.ts +59 -0
  137. package/src/rbac/index.ts +297 -0
  138. package/src/rbac/lib/permission-helpers.ts +63 -0
  139. package/src/rbac/pages/action-list-page.tsx +25 -0
  140. package/src/rbac/pages/resource-list-page.tsx +25 -0
  141. package/src/rbac/pages/role-list-page.tsx +378 -0
  142. package/src/rbac/permission-service.ts +140 -0
  143. package/src/rbac/permissions.ts +135 -0
  144. package/src/rbac/resource-service.ts +115 -0
  145. package/src/rbac/resource-validator.ts +119 -0
  146. package/src/rbac/role-service.ts +165 -0
  147. package/src/rbac/server.ts +16 -0
  148. package/src/rbac/types.ts +38 -0
  149. package/src/schemas/action.schema.ts +66 -0
  150. package/src/schemas/branch.schema.ts +52 -0
  151. package/src/schemas/coming-soon-schema.ts +9 -0
  152. package/src/schemas/company.schema.ts +44 -0
  153. package/src/schemas/forgot-passward-schema.ts +9 -0
  154. package/src/schemas/index.ts +30 -0
  155. package/src/schemas/material-category.schema.ts +43 -0
  156. package/src/schemas/material-pricing.schema.ts +74 -0
  157. package/src/schemas/material.schema.ts +76 -0
  158. package/src/schemas/materials.ts +52 -0
  159. package/src/schemas/new-passward-schema.ts +15 -0
  160. package/src/schemas/partner-company.schema.ts +149 -0
  161. package/src/schemas/register-schema.ts +36 -0
  162. package/src/schemas/resource.schema.ts +133 -0
  163. package/src/schemas/role.schema.ts +11 -0
  164. package/src/schemas/sign-in-schema.ts +24 -0
  165. package/src/schemas/supplier-pricing.schema.ts +15 -0
  166. package/src/schemas/supplier.schema.ts +120 -0
  167. package/src/schemas/system-category-group.schema.ts +67 -0
  168. package/src/schemas/system-category.schema.ts +77 -0
  169. package/src/schemas/system-config.schema.ts +118 -0
  170. package/src/schemas/uom.schema.ts +75 -0
  171. package/src/schemas/user-supplier.schema.ts +179 -0
  172. package/src/schemas/user.schema.ts +18 -0
  173. package/src/schemas/verify-email-schema.ts +9 -0
  174. package/src/schemas/warehouse.schema.ts +49 -0
  175. package/src/system/components/categories/category-list.tsx +529 -0
  176. package/src/system/components/categories/category-manager.tsx +89 -0
  177. package/src/system/components/categories/group-sidebar.tsx +308 -0
  178. package/src/system/components/settings/setting-dialogs.tsx +197 -0
  179. package/src/system/components/settings/setting-field.tsx +291 -0
  180. package/src/system/components/settings/setting-form-dialog.tsx +308 -0
  181. package/src/system/components/settings/settings-groups.ts +80 -0
  182. package/src/system/components/settings/settings-search.tsx +71 -0
  183. package/src/system/components/settings/settings-section.tsx +74 -0
  184. package/src/system/components/settings/settings-sidebar.tsx +81 -0
  185. package/src/system/constants.ts +3 -0
  186. package/src/system/index.ts +150 -0
  187. package/src/system/job-manager.ts +176 -0
  188. package/src/system/pages/components/categories/category-list.tsx +537 -0
  189. package/src/system/pages/components/categories/category-manager.tsx +90 -0
  190. package/src/system/pages/components/categories/group-sidebar.tsx +311 -0
  191. package/src/system/pages/components/settings/sales-rules-settings.tsx +222 -0
  192. package/src/system/pages/components/settings/setting-dialogs.tsx +197 -0
  193. package/src/system/pages/components/settings/setting-field.tsx +292 -0
  194. package/src/system/pages/components/settings/setting-form-dialog.tsx +308 -0
  195. package/src/system/pages/components/settings/settings-groups.ts +87 -0
  196. package/src/system/pages/components/settings/settings-page.tsx +372 -0
  197. package/src/system/pages/components/settings/settings-search.tsx +71 -0
  198. package/src/system/pages/components/settings/settings-section.tsx +74 -0
  199. package/src/system/pages/components/settings/settings-sidebar.tsx +81 -0
  200. package/src/system/pages/components/settings/system-settings.tsx +244 -0
  201. package/src/system/pages/system-category-page.tsx +15 -0
  202. package/src/system/pages/system-settings-page.tsx +380 -0
  203. package/src/system/schemas/system-category-group.schema.ts +46 -0
  204. package/src/system/schemas/system-category.schema.ts +56 -0
  205. package/src/system/services/settings-service.ts +127 -0
  206. package/src/system/services/system-category-service.ts +63 -0
  207. package/src/system/types.ts +45 -0
  208. package/src/types/index.ts +703 -0
  209. package/src/ui/auth/auth-layout.tsx +135 -0
  210. package/src/ui/auth/forgot-password-form.tsx +98 -0
  211. package/src/ui/auth/index.tsx +7 -0
  212. package/src/ui/auth/new-password-form.tsx +107 -0
  213. package/src/ui/auth/oauth-links.tsx +30 -0
  214. package/src/ui/auth/register-form.tsx +202 -0
  215. package/src/ui/auth/sign-in-form.tsx +238 -0
  216. package/src/ui/auth/verify-email-form.tsx +104 -0
  217. package/src/ui/crud/index.tsx +10 -0
  218. package/src/ui/data-display/accordion.tsx +65 -0
  219. package/src/ui/data-display/aspect-ratio.tsx +11 -0
  220. package/src/ui/data-display/avatar.tsx +163 -0
  221. package/src/ui/data-display/bento-grid.tsx +77 -0
  222. package/src/ui/data-display/carousel.tsx +249 -0
  223. package/src/ui/data-display/chart.tsx +363 -0
  224. package/src/ui/data-display/code-block-highlight.tsx +54 -0
  225. package/src/ui/data-display/collapsible.tsx +42 -0
  226. package/src/ui/data-display/compact-stat-bar.tsx +149 -0
  227. package/src/ui/data-display/data-table/data-table-context.tsx +255 -0
  228. package/src/ui/data-display/data-table/data-table-empty-state.tsx +133 -0
  229. package/src/ui/data-display/data-table/data-table-skeleton.tsx +145 -0
  230. package/src/ui/data-display/data-table/data-table-toolbar.tsx +353 -0
  231. package/src/ui/data-display/data-table/data-table.tsx +597 -0
  232. package/src/ui/data-display/data-table/index.ts +44 -0
  233. package/src/ui/data-display/data-table-column-header.tsx +75 -0
  234. package/src/ui/data-display/data-table-pagination.tsx +130 -0
  235. package/src/ui/data-display/data-table-view-options.tsx +59 -0
  236. package/src/ui/data-display/formatted-number-input.tsx +210 -0
  237. package/src/ui/data-display/highlight.tsx +20 -0
  238. package/src/ui/data-display/hover-card.tsx +48 -0
  239. package/src/ui/data-display/index.tsx +50 -0
  240. package/src/ui/data-display/iphone-15-pro.tsx +114 -0
  241. package/src/ui/data-display/kanban/index.ts +4 -0
  242. package/src/ui/data-display/kanban/kanban-board.tsx +192 -0
  243. package/src/ui/data-display/kanban/kanban-column.tsx +74 -0
  244. package/src/ui/data-display/kanban/kanban-item.tsx +50 -0
  245. package/src/ui/data-display/kanban/kanban-types.ts +21 -0
  246. package/src/ui/data-display/kpi-card.tsx +68 -0
  247. package/src/ui/data-display/media-grid.tsx +110 -0
  248. package/src/ui/data-display/safari.tsx +175 -0
  249. package/src/ui/data-display/show-more-text.tsx +55 -0
  250. package/src/ui/data-display/tabs.tsx +68 -0
  251. package/src/ui/data-display/timeline.tsx +256 -0
  252. package/src/ui/feedback/alert.tsx +60 -0
  253. package/src/ui/feedback/context-menu.tsx +245 -0
  254. package/src/ui/feedback/drawer.tsx +132 -0
  255. package/src/ui/feedback/error-dialog.tsx +273 -0
  256. package/src/ui/feedback/index.tsx +183 -0
  257. package/src/ui/feedback/progress.tsx +32 -0
  258. package/src/ui/feedback/sheet.tsx +148 -0
  259. package/src/ui/feedback/sonner.tsx +36 -0
  260. package/src/ui/forms/command.tsx +157 -0
  261. package/src/ui/forms/date-picker.tsx +73 -0
  262. package/src/ui/forms/date-range-picker.tsx +76 -0
  263. package/src/ui/forms/date-time-picker.tsx +109 -0
  264. package/src/ui/forms/editor/editor-menu-bar.tsx +394 -0
  265. package/src/ui/forms/editor/index.tsx +130 -0
  266. package/src/ui/forms/editor/multi-select-example.tsx +1234 -0
  267. package/src/ui/forms/emoji-picker.tsx +109 -0
  268. package/src/ui/forms/file-dropzone.tsx +169 -0
  269. package/src/ui/forms/file-thumbnail.tsx +29 -0
  270. package/src/ui/forms/index.tsx +201 -0
  271. package/src/ui/forms/input-file.tsx +99 -0
  272. package/src/ui/forms/input-group.tsx +46 -0
  273. package/src/ui/forms/input-otp.tsx +81 -0
  274. package/src/ui/forms/input-phone.tsx +172 -0
  275. package/src/ui/forms/input-spin.tsx +116 -0
  276. package/src/ui/forms/input-tags.tsx +219 -0
  277. package/src/ui/forms/input-time.tsx +42 -0
  278. package/src/ui/forms/multi-select.tsx +629 -0
  279. package/src/ui/forms/multiple-date-picker.tsx +74 -0
  280. package/src/ui/forms/radio-group.tsx +42 -0
  281. package/src/ui/forms/rating.tsx +158 -0
  282. package/src/ui/forms/time-picker.tsx +57 -0
  283. package/src/ui/index.tsx +17 -0
  284. package/src/ui/layout/animated-list.tsx +77 -0
  285. package/src/ui/layout/animated-sidebar.tsx +294 -0
  286. package/src/ui/layout/command-menu.tsx +355 -0
  287. package/src/ui/layout/customizer.tsx +324 -0
  288. package/src/ui/layout/footer.tsx +43 -0
  289. package/src/ui/layout/full-screen-toggle.tsx +52 -0
  290. package/src/ui/layout/header-breadcrumb.tsx +77 -0
  291. package/src/ui/layout/horizontal-layout-header.tsx +83 -0
  292. package/src/ui/layout/horizontal-layout.tsx +50 -0
  293. package/src/ui/layout/index.tsx +25 -0
  294. package/src/ui/layout/language-dropdown.tsx +103 -0
  295. package/src/ui/layout/logo.tsx +63 -0
  296. package/src/ui/layout/main-layout.tsx +57 -0
  297. package/src/ui/layout/mode-dropdown.tsx +58 -0
  298. package/src/ui/layout/notification-dropdown.tsx +127 -0
  299. package/src/ui/layout/page-tabs.tsx +306 -0
  300. package/src/ui/layout/route-cache.tsx +214 -0
  301. package/src/ui/layout/sidebar-group-icon-menu.tsx +195 -0
  302. package/src/ui/layout/sidebar.tsx +279 -0
  303. package/src/ui/layout/tab-content-cache.tsx +201 -0
  304. package/src/ui/layout/tab-navigation-provider.tsx +536 -0
  305. package/src/ui/layout/toggle-mobile-sidebar.tsx +33 -0
  306. package/src/ui/layout/top-bar-header-menubar.tsx +412 -0
  307. package/src/ui/layout/user-dropdown.tsx +188 -0
  308. package/src/ui/layout/vertical-layout-header.tsx +65 -0
  309. package/src/ui/layout/vertical-layout.tsx +47 -0
  310. package/src/ui/management/audit-log-page.tsx +209 -0
  311. package/src/ui/management/cache-management.tsx +349 -0
  312. package/src/ui/management/index.ts +3 -0
  313. package/src/ui/management/job-management.tsx +308 -0
  314. package/src/ui/pages/not-found.tsx +30 -0
  315. package/src/ui/primitives/badge.tsx +66 -0
  316. package/src/ui/primitives/breadcrumb.tsx +103 -0
  317. package/src/ui/primitives/button.tsx +129 -0
  318. package/src/ui/primitives/calendar.tsx +74 -0
  319. package/src/ui/primitives/card.tsx +86 -0
  320. package/src/ui/primitives/checkbox.tsx +31 -0
  321. package/src/ui/primitives/client.ts +30 -0
  322. package/src/ui/primitives/combobox.tsx +290 -0
  323. package/src/ui/primitives/dialog.tsx +121 -0
  324. package/src/ui/primitives/dropdown-menu.tsx +239 -0
  325. package/src/ui/primitives/dynamic-icon.tsx +24 -0
  326. package/src/ui/primitives/index.tsx +134 -0
  327. package/src/ui/primitives/input-number.tsx +131 -0
  328. package/src/ui/primitives/input.tsx +22 -0
  329. package/src/ui/primitives/keyboard.tsx +23 -0
  330. package/src/ui/primitives/label.tsx +24 -0
  331. package/src/ui/primitives/menubar.tsx +262 -0
  332. package/src/ui/primitives/navigation-menu.tsx +157 -0
  333. package/src/ui/primitives/pagination.tsx +118 -0
  334. package/src/ui/primitives/popover.tsx +56 -0
  335. package/src/ui/primitives/prefetch-link.tsx +60 -0
  336. package/src/ui/primitives/resizable.tsx +59 -0
  337. package/src/ui/primitives/scroll-area.tsx +63 -0
  338. package/src/ui/primitives/select.tsx +172 -0
  339. package/src/ui/primitives/separator.tsx +51 -0
  340. package/src/ui/primitives/sidebar.tsx +844 -0
  341. package/src/ui/primitives/slider.tsx +27 -0
  342. package/src/ui/primitives/status-badge.tsx +47 -0
  343. package/src/ui/primitives/sticky-layout.tsx +50 -0
  344. package/src/ui/primitives/switch.tsx +29 -0
  345. package/src/ui/primitives/table.tsx +116 -0
  346. package/src/ui/primitives/tabs.tsx +55 -0
  347. package/src/ui/primitives/toggle-group.tsx +70 -0
  348. package/src/ui/primitives/toggle.tsx +47 -0
  349. package/src/ui/primitives/tooltip.tsx +59 -0
  350. package/src/user/components/dangerous-zone.tsx +34 -0
  351. package/src/user/components/delete-account-form.tsx +40 -0
  352. package/src/user/components/index.ts +4 -0
  353. package/src/user/components/profile-info-form.tsx +390 -0
  354. package/src/user/components/profile-info.tsx +32 -0
  355. package/src/user/components/unified-profile-dialog.tsx +1019 -0
  356. package/src/user/components/user-stats.tsx +27 -0
  357. package/src/user/components/user-toolbar.tsx +137 -0
  358. package/src/user/components/users-card-view.tsx +253 -0
  359. package/src/user/index.ts +11 -0
  360. package/src/user/pages/user-list-page.tsx +234 -0
  361. package/src/user/pages/users-client-page.tsx +385 -0
  362. package/src/user/profile-page.tsx +19 -0
  363. package/src/user/schemas.ts +68 -0
  364. package/src/user/types.ts +34 -0
  365. package/src/user/user-service.ts +538 -0
  366. package/src/utils/index.ts +906 -0
  367. package/src/workflow/activity-timeline.tsx +412 -0
  368. package/src/workflow/approval-workflow.tsx +31 -0
  369. package/src/workflow/index.ts +2 -0
@@ -0,0 +1,385 @@
1
+ "use client";
2
+
3
+ import { useState, useCallback } from "react";
4
+ import { useRouter, usePathname, useSearchParams } from "next/navigation";
5
+ import { useViewMode } from "../../hooks";
6
+ import { toast } from "sonner";
7
+ import {
8
+ Table,
9
+ TableBody,
10
+ TableCell,
11
+ TableHead,
12
+ TableHeader,
13
+ TableRow,
14
+ Badge,
15
+ Button,
16
+ } from "../../ui";
17
+ import { Edit2 } from "lucide-react";
18
+ import type { EntityConfig, CrudPermissions } from "../../types";
19
+
20
+ import { UserToolbar } from "../components/user-toolbar";
21
+ import { UserStats } from "../components/user-stats";
22
+ import { UsersCardView } from "../components/users-card-view";
23
+ import { UnifiedProfileDialog } from "../components/unified-profile-dialog";
24
+
25
+ interface UserStats {
26
+ totalUsers: number;
27
+ activeUsers: number;
28
+ employees: number;
29
+ customers: number;
30
+ suppliers: number;
31
+ }
32
+
33
+ interface UsersClientPageProps {
34
+ initialData: any[];
35
+ config: EntityConfig;
36
+ permissions: CrudPermissions;
37
+ dictionary: any;
38
+ roles: any[];
39
+ stats: UserStats;
40
+ onAssignRoles?: (userId: string, roleCodes: string[]) => Promise<any>;
41
+ onResetPassword?: (userId: string, password: string) => Promise<any>;
42
+ }
43
+
44
+ export function UsersClientPage({
45
+ initialData,
46
+ config,
47
+ permissions,
48
+ dictionary,
49
+ roles,
50
+ stats,
51
+ onAssignRoles,
52
+ onResetPassword,
53
+ }: UsersClientPageProps) {
54
+ const router = useRouter();
55
+ const pathname = usePathname();
56
+
57
+ const searchParams = useSearchParams();
58
+ // View Mode
59
+ const [viewModeRaw, handleViewModeChangeRaw] = useViewMode(
60
+ "users-view-mode",
61
+ "grid",
62
+ );
63
+ const viewMode =
64
+ viewModeRaw === "table" || viewModeRaw === "grid"
65
+ ? (viewModeRaw as "table" | "grid")
66
+ : "grid";
67
+ const handleViewModeChange = (mode: "table" | "grid") =>
68
+ handleViewModeChangeRaw(mode);
69
+
70
+ // Selection for Quick View
71
+ const [selectedUser, setSelectedUser] = useState<any>(null);
72
+ const [quickViewOpen, setQuickViewOpen] = useState(false);
73
+
74
+ // Dialogs State
75
+ const [userFormOpen, setUserFormOpen] = useState(false);
76
+ const [userFormMode, setUserFormMode] = useState<"create" | "edit">("create");
77
+ const [userFormData, setUserFormData] = useState<any>(null);
78
+
79
+ // Handlers
80
+ const handleSearch = useCallback(
81
+ (value: string) => {
82
+ const params = new URLSearchParams(searchParams.toString());
83
+ if (value) params.set("search", value);
84
+ else params.delete("search");
85
+ router.push(`${pathname}?${params.toString()}`);
86
+ },
87
+ [pathname, router, searchParams],
88
+ );
89
+
90
+ const handleRoleFilter = useCallback(
91
+ (value: string) => {
92
+ const params = new URLSearchParams(searchParams.toString());
93
+ if (value && value !== "all") params.set("role", value);
94
+ else params.delete("role");
95
+ router.push(`${pathname}?${params.toString()}`);
96
+ },
97
+ [pathname, router, searchParams],
98
+ );
99
+
100
+ const handleUserTypeFilter = useCallback(
101
+ (value: string) => {
102
+ const params = new URLSearchParams(searchParams.toString());
103
+ if (value && value !== "all") params.set("type", value);
104
+ else params.delete("type");
105
+ router.push(`${pathname}?${params.toString()}`);
106
+ },
107
+ [pathname, router, searchParams],
108
+ );
109
+
110
+ const handleSelectUser = (user: any) => {
111
+ setSelectedUser(user);
112
+ setQuickViewOpen(true);
113
+ };
114
+
115
+ const handleCreateNew = () => {
116
+ setUserFormMode("create");
117
+ setUserFormData(null);
118
+ setUserFormOpen(true);
119
+ };
120
+
121
+ const handleEditUser = (user?: any) => {
122
+ const targetUser = user || selectedUser;
123
+ if (!targetUser) return;
124
+ setSelectedUser(targetUser);
125
+ setQuickViewOpen(true);
126
+ };
127
+
128
+ // Role Assignment
129
+ const handleAssignRoles = async (userId: string, roleCodes: string[]) => {
130
+ try {
131
+ if (onAssignRoles) {
132
+ await onAssignRoles(userId, roleCodes);
133
+ }
134
+ toast.success("Thành công", {
135
+ description: "Đã cập nhật vai trò người dùng",
136
+ });
137
+ router.refresh();
138
+ } catch (error) {
139
+ toast.error("Lỗi", {
140
+ description: "Không thể cập nhật vai trò",
141
+ });
142
+ }
143
+ };
144
+
145
+ // Reset Password
146
+ const handleResetPassword = async (userId: string, password: string) => {
147
+ try {
148
+ if (onResetPassword) {
149
+ await onResetPassword(userId, password);
150
+ }
151
+ toast.success("Thành công", {
152
+ description: "Đã đặt lại mật khẩu",
153
+ });
154
+ } catch (error) {
155
+ toast.error("Lỗi", {
156
+ description: "Không thể đặt lại mật khẩu",
157
+ });
158
+ }
159
+ };
160
+
161
+ // User Form Submit (Create/Edit)
162
+ const handleUserSubmit = async (data: any, id?: string) => {
163
+ const url = id ? `/api/users/${id}` : "/api/users";
164
+ const method = id ? "PUT" : "POST";
165
+
166
+ const res = await fetch(url, {
167
+ method,
168
+ headers: { "Content-Type": "application/json" },
169
+ body: JSON.stringify(data),
170
+ });
171
+
172
+ if (!res.ok) {
173
+ const resData = await res.json();
174
+ throw new Error(resData.error);
175
+ }
176
+
177
+ toast.success("Thành công", {
178
+ description: id ? "Đã cập nhật người dùng" : "Đã tạo người dùng mới",
179
+ });
180
+
181
+ router.refresh();
182
+ setUserFormOpen(false);
183
+ };
184
+
185
+ return (
186
+ <div className="flex flex-col gap-4 relative">
187
+ <UserStats stats={stats} />
188
+
189
+ <UserToolbar
190
+ viewMode={viewMode}
191
+ onViewModeChange={handleViewModeChange}
192
+ onCreateNew={handleCreateNew}
193
+ onSearchChange={handleSearch}
194
+ roles={roles}
195
+ onRoleChange={handleRoleFilter}
196
+ userType={searchParams.get("type") || "all"}
197
+ onUserTypeChange={handleUserTypeFilter}
198
+ permissions={permissions}
199
+ />
200
+
201
+ <div className="min-h-[400px]">
202
+ {viewMode === "table" ? (
203
+ <div className="border rounded-md">
204
+ <BasicUserTable
205
+ data={initialData}
206
+ onEdit={(u) => handleEditUser(u)}
207
+ />
208
+ </div>
209
+ ) : (
210
+ <UsersCardView data={initialData} onSelect={handleSelectUser} />
211
+ )}
212
+ </div>
213
+
214
+ {/* Create User Dialog */}
215
+ <UnifiedProfileDialog
216
+ open={userFormOpen}
217
+ onOpenChange={setUserFormOpen}
218
+ mode="create"
219
+ viewMode="admin"
220
+ roles={roles}
221
+ onSaveProfile={(data) => handleUserSubmit(data)}
222
+ />
223
+
224
+ {/* Quick View / Edit Dialog */}
225
+ {selectedUser && (
226
+ <UnifiedProfileDialog
227
+ open={quickViewOpen}
228
+ onOpenChange={setQuickViewOpen}
229
+ data={selectedUser}
230
+ mode="edit"
231
+ viewMode="admin"
232
+ roles={roles}
233
+ onSaveProfile={handleUserSubmit}
234
+ onSaveRoles={handleAssignRoles}
235
+ onSavePassword={handleResetPassword}
236
+ />
237
+ )}
238
+ </div>
239
+ );
240
+ }
241
+
242
+ // Simple Basic Table Component
243
+ function BasicUserTable({
244
+ data,
245
+ onEdit,
246
+ }: {
247
+ data: any[];
248
+ onEdit: (u: any) => void;
249
+ }) {
250
+ const [currentPage, setCurrentPage] = useState(1);
251
+ const pageSize = 10;
252
+
253
+ if (!data.length) return <div className="p-4 text-center">No data</div>;
254
+
255
+ const totalPages = Math.ceil(data.length / pageSize);
256
+ const paginatedData = data.slice((currentPage - 1) * pageSize, currentPage * pageSize);
257
+
258
+ return (
259
+ <div className="flex flex-col gap-4">
260
+ <div className="bg-background">
261
+ <Table>
262
+ <TableHeader>
263
+ <TableRow className="text-sm border-b">
264
+ <TableHead className="w-[50px] font-semibold text-foreground text-center h-10">STT</TableHead>
265
+ <TableHead className="w-[20%] min-w-[150px] font-semibold text-foreground h-10">Người dùng</TableHead>
266
+ <TableHead className="w-[20%] min-w-[150px] font-semibold text-foreground h-10">Liên hệ</TableHead>
267
+ <TableHead className="w-[15%] min-w-[120px] font-semibold text-foreground h-10">Trạng thái</TableHead>
268
+ <TableHead className="w-[15%] min-w-[120px] font-semibold text-foreground h-10">Phòng ban</TableHead>
269
+ <TableHead className="w-[20%] min-w-[150px] font-semibold text-foreground h-10">Vai trò</TableHead>
270
+ <TableHead className="w-[50px] text-center h-10"></TableHead>
271
+ </TableRow>
272
+ </TableHeader>
273
+ <TableBody>
274
+ {paginatedData.map((user, index) => (
275
+ <TableRow key={user.id} className="border-b group">
276
+ {/* STT */}
277
+ <TableCell className="py-3 text-center text-muted-foreground font-medium text-xs">
278
+ {(currentPage - 1) * pageSize + index + 1}
279
+ </TableCell>
280
+
281
+ {/* User Info */}
282
+ <TableCell className="py-3">
283
+ <div className="flex flex-col">
284
+ <div className="flex items-center gap-2">
285
+ <span className="font-semibold text-sm text-foreground">{user.name}</span>
286
+ {user.userType === "customer" && <span className="text-xs text-foreground bg-muted border px-2.5 py-0.5 rounded-full font-semibold whitespace-nowrap">Khách hàng</span>}
287
+ {user.userType === "supplier" && <span className="text-xs text-background bg-foreground px-2.5 py-0.5 rounded-full font-semibold whitespace-nowrap">Nhà cung cấp</span>}
288
+ {(!user.userType || user.userType === "employee") && <span className="text-xs text-foreground bg-muted border px-2.5 py-0.5 rounded-full font-semibold whitespace-nowrap">Nhân viên</span>}
289
+ </div>
290
+ </div>
291
+ </TableCell>
292
+
293
+ {/* Contact */}
294
+ <TableCell className="py-3">
295
+ <div className="flex flex-col">
296
+ <span className="text-sm text-foreground">{user.email}</span>
297
+ {user.phone && <span className="text-sm text-muted-foreground">{user.phone}</span>}
298
+ </div>
299
+ </TableCell>
300
+
301
+ {/* Status */}
302
+ <TableCell className="py-3">
303
+ <div className="flex items-center gap-1.5">
304
+ <span className={`inline-flex items-center text-xs font-semibold px-2.5 py-1 rounded-full whitespace-nowrap ${
305
+ user.isActive || user.status === "active"
306
+ ? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400"
307
+ : "bg-muted text-muted-foreground"
308
+ }`}>
309
+ {user.isActive || user.status === "active" ? "Hoạt động" : "Đã khóa"}
310
+ </span>
311
+ </div>
312
+ </TableCell>
313
+
314
+ {/* Department */}
315
+ <TableCell className="py-3 text-sm text-foreground">
316
+ {user.departmentName || "—"}
317
+ </TableCell>
318
+
319
+ {/* Roles */}
320
+ <TableCell className="py-3">
321
+ <div className="flex flex-wrap gap-1.5">
322
+ {user.roleNames?.map((r: string, i: number) => (
323
+ <span
324
+ key={i}
325
+ className="inline-flex items-center bg-muted text-foreground border font-semibold text-xs px-2.5 py-1 rounded-full whitespace-nowrap"
326
+ >
327
+ {r}
328
+ </span>
329
+ ))}
330
+ </div>
331
+ </TableCell>
332
+
333
+ {/* Actions */}
334
+ <TableCell className="py-2.5 text-right">
335
+ <Button
336
+ variant="ghost"
337
+ size="icon"
338
+ onClick={(e: React.MouseEvent) => {
339
+ e.stopPropagation();
340
+ onEdit(user);
341
+ }}
342
+ >
343
+ <Edit2 className="h-4 w-4" />
344
+ </Button>
345
+ </TableCell>
346
+ </TableRow>
347
+ ))}
348
+ </TableBody>
349
+ </Table>
350
+ </div>
351
+
352
+ {/* Pagination */}
353
+ {totalPages > 1 && (
354
+ <div className="flex items-center justify-between px-2 pt-2">
355
+ <p className="text-sm text-muted-foreground">
356
+ Hiển thị từ {(currentPage - 1) * pageSize + 1} đến {Math.min(currentPage * pageSize, data.length)} trong tổng số {data.length}
357
+ </p>
358
+ <div className="flex items-center gap-2">
359
+ <Button
360
+ variant="outline"
361
+ size="sm"
362
+ onClick={() => setCurrentPage((p) => Math.max(1, p - 1))}
363
+ disabled={currentPage === 1}
364
+ className="h-8 rounded-full"
365
+ >
366
+ Trang trước
367
+ </Button>
368
+ <div className="text-sm font-semibold text-foreground px-2">
369
+ Trang {currentPage} / {totalPages}
370
+ </div>
371
+ <Button
372
+ variant="outline"
373
+ size="sm"
374
+ onClick={() => setCurrentPage((p) => Math.min(totalPages, p + 1))}
375
+ disabled={currentPage === totalPages}
376
+ className="h-8 rounded-full"
377
+ >
378
+ Trang sau
379
+ </Button>
380
+ </div>
381
+ </div>
382
+ )}
383
+ </div>
384
+ );
385
+ }
@@ -0,0 +1,19 @@
1
+ import type { UserType } from "./types";
2
+ import type { DictionaryType } from "../hooks";
3
+
4
+ import { DangerousZone } from "./components/dangerous-zone";
5
+ import { ProfileInfo } from "./components/profile-info";
6
+
7
+ export interface ProfilePageProps {
8
+ user: UserType;
9
+ dictionary?: DictionaryType;
10
+ }
11
+
12
+ export function ProfilePage({ user, dictionary }: ProfilePageProps) {
13
+ return (
14
+ <div className="grid gap-4">
15
+ <ProfileInfo user={user} dictionary={dictionary} />
16
+ <DangerousZone user={user} dictionary={dictionary} />
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,68 @@
1
+ import { z } from "zod";
2
+
3
+ import { formatFileSize } from "../utils";
4
+
5
+ export const MAX_AVATAR_SIZE = 50000000; // 50 MB
6
+ export const fomratedAvatarSize = formatFileSize(MAX_AVATAR_SIZE);
7
+
8
+ export const ProfileInfoSchema = z.object({
9
+ firstName: z
10
+ .string()
11
+ .trim()
12
+ .min(2, { message: "First Name must contain at least 2 characters." })
13
+ .max(50, { message: "First Name must contain at most 50 characters." }),
14
+ lastName: z
15
+ .string()
16
+ .trim()
17
+ .min(2, { message: "Last Name must contain at least 2 characters." })
18
+ .max(50, { message: "Last Name must contain at most 50 characters." }),
19
+ username: z
20
+ .string()
21
+ .toLowerCase()
22
+ .trim()
23
+ .min(3, { message: "Username must contain at least 3 characters." })
24
+ .max(50, { message: "Username must contain at most 50 characters." }),
25
+ email: z
26
+ .string()
27
+ .email({ message: "Invalid email address" })
28
+ .toLowerCase()
29
+ .trim(),
30
+ phoneNumber: z
31
+ .string()
32
+ .min(10, { message: "Phone Number must contain at least 10 digits." })
33
+ .max(15, { message: "Username must contain at most 15 digits." }),
34
+ state: z
35
+ .string()
36
+ .trim()
37
+ .min(2, { message: "State must contain at least 2 characters." })
38
+ .max(50, { message: "State must contain at most 50 characters." }),
39
+ country: z
40
+ .string()
41
+ .trim()
42
+ .min(2, { message: "Country must contain at least 2 characters." })
43
+ .max(56, { message: "Country must contain at most 56 characters." }),
44
+ address: z
45
+ .string()
46
+ .trim()
47
+ .min(2, { message: "Address must contain at least 2 characters." })
48
+ .max(100, { message: "Address must contain at most 100 characters." }),
49
+ zipCode: z
50
+ .string()
51
+ .min(5, { message: "Zip Code must contain at least 5 digits." })
52
+ .max(10, { message: "Zip Code must contain at most 10 characters." }),
53
+ language: z.string(),
54
+ timeZone: z.string(),
55
+ currency: z.string(),
56
+ organization: z.string().trim().optional(),
57
+ avatar: z
58
+ .instanceof(File)
59
+ .refine((avatar) => avatar.size <= MAX_AVATAR_SIZE, {
60
+ message: `Avatar must be ${fomratedAvatarSize} or less.`,
61
+ })
62
+ // .refine((avatar) => avatar.type.startsWith("image/"), {
63
+ // message: "The file must be an image",
64
+ // })
65
+ .optional(),
66
+ });
67
+
68
+ export const DeleteAccountSchema = z.object({});
@@ -0,0 +1,34 @@
1
+ import type { z } from "zod";
2
+ import type { DeleteAccountSchema, ProfileInfoSchema } from "./schemas";
3
+
4
+
5
+ export interface UserType {
6
+ id: string
7
+ firstName: string
8
+ lastName: string
9
+ name: string
10
+ username: string
11
+ role: string
12
+ avatar: string
13
+ background: string
14
+ status: string
15
+ phoneNumber: string
16
+ email: string
17
+ state: string
18
+ country: string
19
+ address: string
20
+ zipCode: string
21
+ language: string
22
+ timeZone: string
23
+ currency: string
24
+ organization: string
25
+ twoFactorAuth: boolean
26
+ loginAlerts: boolean
27
+ accountReoveryOption?: "email" | "sms" | "codes"
28
+ connections: number
29
+ followers: number
30
+ }
31
+
32
+ export type DeleteAccountFormType = z.infer<typeof DeleteAccountSchema>
33
+
34
+ export type ProfileInfoFormType = z.infer<typeof ProfileInfoSchema>