@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,245 @@
1
+ "use client";
2
+
3
+ import * as ContextMenuPrimitive from "@radix-ui/react-context-menu";
4
+ import { Check, ChevronRight, Dot } from "lucide-react";
5
+
6
+ import type { ComponentProps } from "react";
7
+
8
+ import { cn } from "../../utils";
9
+
10
+ export function ContextMenu({
11
+ ...props
12
+ }: ComponentProps<typeof ContextMenuPrimitive.Root>) {
13
+ return <ContextMenuPrimitive.Root data-slot="context-menu" {...props} />;
14
+ }
15
+
16
+ export function ContextMenuTrigger({
17
+ ...props
18
+ }: ComponentProps<typeof ContextMenuPrimitive.Trigger>) {
19
+ return (
20
+ <ContextMenuPrimitive.Trigger data-slot="context-menu-trigger" {...props} />
21
+ );
22
+ }
23
+
24
+ export function ContextMenuGroup({
25
+ ...props
26
+ }: ComponentProps<typeof ContextMenuPrimitive.Group>) {
27
+ return (
28
+ <ContextMenuPrimitive.Group data-slot="context-menu-group" {...props} />
29
+ );
30
+ }
31
+
32
+ export function ContextMenuPortal({
33
+ ...props
34
+ }: ComponentProps<typeof ContextMenuPrimitive.Portal>) {
35
+ return (
36
+ <ContextMenuPrimitive.Portal data-slot="context-menu-portal" {...props} />
37
+ );
38
+ }
39
+
40
+ export function ContextMenuSub({
41
+ ...props
42
+ }: ComponentProps<typeof ContextMenuPrimitive.Sub>) {
43
+ return <ContextMenuPrimitive.Sub data-slot="context-menu-sub" {...props} />;
44
+ }
45
+
46
+ export function ContextMenuRadioGroup({
47
+ ...props
48
+ }: ComponentProps<typeof ContextMenuPrimitive.RadioGroup>) {
49
+ return (
50
+ <ContextMenuPrimitive.RadioGroup
51
+ data-slot="context-menu-radio-group"
52
+ {...props}
53
+ />
54
+ );
55
+ }
56
+
57
+ type ContextMenuSubTriggerProps = ComponentProps<
58
+ typeof ContextMenuPrimitive.SubTrigger
59
+ > & {
60
+ inset?: boolean;
61
+ };
62
+
63
+ export function ContextMenuSubTrigger({
64
+ className,
65
+ inset,
66
+ children,
67
+ ...props
68
+ }: ContextMenuSubTriggerProps) {
69
+ return (
70
+ <ContextMenuPrimitive.SubTrigger
71
+ data-slot="context-menu-sub-trigger"
72
+ className={cn(
73
+ "flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
74
+ inset && "ps-8",
75
+ className,
76
+ )}
77
+ {...props}
78
+ >
79
+ {children}
80
+ <ChevronRight className="ml-auto h-4 w-4" />
81
+ </ContextMenuPrimitive.SubTrigger>
82
+ );
83
+ }
84
+
85
+ export function ContextMenuSubContent({
86
+ className,
87
+ ...props
88
+ }: ComponentProps<typeof ContextMenuPrimitive.SubContent>) {
89
+ return (
90
+ <ContextMenuPrimitive.SubContent
91
+ data-slot="context-menu-sub-content"
92
+ className={cn(
93
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
94
+ className,
95
+ )}
96
+ {...props}
97
+ />
98
+ );
99
+ }
100
+
101
+ export function ContextMenuContent({
102
+ className,
103
+ ...props
104
+ }: ComponentProps<typeof ContextMenuPrimitive.Content>) {
105
+ return (
106
+ <ContextMenuPrimitive.Portal>
107
+ <ContextMenuPrimitive.Content
108
+ data-slot="context-menu-content"
109
+ className={cn(
110
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
111
+ className,
112
+ )}
113
+ {...props}
114
+ />
115
+ </ContextMenuPrimitive.Portal>
116
+ );
117
+ }
118
+
119
+ type ContextMenuItemProps = ComponentProps<typeof ContextMenuPrimitive.Item> & {
120
+ inset?: boolean;
121
+ variant?: "default" | "destructive";
122
+ };
123
+
124
+ export function ContextMenuItem({
125
+ className,
126
+ inset,
127
+ variant = "default",
128
+ ...props
129
+ }: ContextMenuItemProps) {
130
+ return (
131
+ <ContextMenuPrimitive.Item
132
+ data-slot="context-menu-item"
133
+ data-inset={inset}
134
+ data-variant={variant}
135
+ className={cn(
136
+ "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground",
137
+ className,
138
+ )}
139
+ {...props}
140
+ />
141
+ );
142
+ }
143
+
144
+ export function ContextMenuCheckboxItem({
145
+ className,
146
+ children,
147
+ checked,
148
+ ...props
149
+ }: ComponentProps<typeof ContextMenuPrimitive.CheckboxItem>) {
150
+ return (
151
+ <ContextMenuPrimitive.CheckboxItem
152
+ data-slot="context-menu-checkbox-item"
153
+ className={cn(
154
+ "relative flex cursor-pointer select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
155
+ className,
156
+ )}
157
+ checked={checked}
158
+ {...props}
159
+ >
160
+ <span className="absolute start-2 flex h-3.5 w-3.5 items-center justify-center">
161
+ <ContextMenuPrimitive.ItemIndicator>
162
+ <Check className="h-4 w-4" />
163
+ </ContextMenuPrimitive.ItemIndicator>
164
+ </span>
165
+ {children}
166
+ </ContextMenuPrimitive.CheckboxItem>
167
+ );
168
+ }
169
+
170
+ export function ContextMenuRadioItem({
171
+ className,
172
+ children,
173
+ ...props
174
+ }: ComponentProps<typeof ContextMenuPrimitive.RadioItem>) {
175
+ return (
176
+ <ContextMenuPrimitive.RadioItem
177
+ data-slot="context-menu-radio-item"
178
+ className={cn(
179
+ "relative flex cursor-pointer select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
180
+ className,
181
+ )}
182
+ {...props}
183
+ >
184
+ <span className="absolute start-2 flex h-3.5 w-3.5 items-center justify-center">
185
+ <ContextMenuPrimitive.ItemIndicator>
186
+ <Dot className="h-4 w-4 fill-current" />
187
+ </ContextMenuPrimitive.ItemIndicator>
188
+ </span>
189
+ {children}
190
+ </ContextMenuPrimitive.RadioItem>
191
+ );
192
+ }
193
+
194
+ type ContextMenuLabelProps = ComponentProps<
195
+ typeof ContextMenuPrimitive.Label
196
+ > & {
197
+ inset?: boolean;
198
+ };
199
+
200
+ export function ContextMenuLabel({
201
+ className,
202
+ inset,
203
+ ...props
204
+ }: ContextMenuLabelProps) {
205
+ return (
206
+ <ContextMenuPrimitive.Label
207
+ data-slot="context-menu-label"
208
+ className={cn(
209
+ "px-2 py-1.5 text-sm font-semibold text-foreground",
210
+ inset && "ps-8",
211
+ className,
212
+ )}
213
+ {...props}
214
+ />
215
+ );
216
+ }
217
+
218
+ export function ContextMenuSeparator({
219
+ className,
220
+ ...props
221
+ }: ComponentProps<typeof ContextMenuPrimitive.Separator>) {
222
+ return (
223
+ <ContextMenuPrimitive.Separator
224
+ data-slot="context-menu-separator"
225
+ className={cn("-mx-1 my-1 h-px bg-border", className)}
226
+ {...props}
227
+ />
228
+ );
229
+ }
230
+
231
+ export function ContextMenuShortcut({
232
+ className,
233
+ ...props
234
+ }: ComponentProps<"span">) {
235
+ return (
236
+ <span
237
+ data-slot="context-menu-shortcut"
238
+ className={cn(
239
+ "ms-auto text-sm tracking-widest text-muted-foreground",
240
+ className,
241
+ )}
242
+ {...props}
243
+ />
244
+ );
245
+ }
@@ -0,0 +1,132 @@
1
+ "use client";
2
+
3
+ import { Drawer as DrawerPrimitive } from "vaul";
4
+
5
+ import type { ComponentProps } from "react";
6
+
7
+ import { cn } from "../../utils";
8
+
9
+ export function Drawer({
10
+ shouldScaleBackground = true,
11
+ ...props
12
+ }: ComponentProps<typeof DrawerPrimitive.Root>) {
13
+ return (
14
+ <DrawerPrimitive.Root
15
+ data-slot="drawer"
16
+ shouldScaleBackground={shouldScaleBackground}
17
+ {...props}
18
+ />
19
+ );
20
+ }
21
+
22
+ export function DrawerTrigger({
23
+ className,
24
+ ...props
25
+ }: ComponentProps<typeof DrawerPrimitive.Trigger>) {
26
+ return (
27
+ <DrawerPrimitive.Trigger
28
+ data-slot="drawer-trigger"
29
+ className={cn("cursor-pointer", className)}
30
+ {...props}
31
+ />
32
+ );
33
+ }
34
+
35
+ export function DrawerPortal({
36
+ children,
37
+ ...props
38
+ }: Omit<ComponentProps<typeof DrawerPrimitive.Portal>, "children"> & { children?: React.ReactNode }) {
39
+ const Portal = DrawerPrimitive.Portal as any;
40
+ return <Portal data-slot="drawer-portal" {...props}>{children as any}</Portal>;
41
+ }
42
+
43
+ export function DrawerClose({
44
+ ...props
45
+ }: ComponentProps<typeof DrawerPrimitive.Close>) {
46
+ return <DrawerPrimitive.Close data-slot="drawer-close" {...props} />;
47
+ }
48
+
49
+ export function DrawerOverlay({
50
+ className,
51
+ ...props
52
+ }: ComponentProps<typeof DrawerPrimitive.Overlay>) {
53
+ return (
54
+ <DrawerPrimitive.Overlay
55
+ data-slot="drawer-overlay"
56
+ className={cn("fixed inset-0 z-50 bg-black/80", className)}
57
+ {...props}
58
+ />
59
+ );
60
+ }
61
+
62
+ export function DrawerContent({
63
+ className,
64
+ children,
65
+ ...props
66
+ }: ComponentProps<typeof DrawerPrimitive.Content>) {
67
+ return (
68
+ <DrawerPortal data-slot="drawer-portal">
69
+ <DrawerOverlay />
70
+ <DrawerPrimitive.Content
71
+ data-slot="drawer-content"
72
+ className={cn(
73
+ "fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border bg-background",
74
+ className,
75
+ )}
76
+ {...props}
77
+ >
78
+ <div className="mx-auto mt-4 h-2 w-[100px] rounded-full bg-muted" />
79
+ {children}
80
+ </DrawerPrimitive.Content>
81
+ </DrawerPortal>
82
+ );
83
+ }
84
+
85
+ export function DrawerHeader({ className, ...props }: ComponentProps<"div">) {
86
+ return (
87
+ <div
88
+ data-slot="drawer-header"
89
+ className={cn("grid gap-1.5 p-4 text-center sm:text-start", className)}
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ export function DrawerFooter({ className, ...props }: ComponentProps<"div">) {
96
+ return (
97
+ <div
98
+ data-slot="drawer-footer"
99
+ className={cn("mt-auto flex flex-col gap-2 p-4", className)}
100
+ {...props}
101
+ />
102
+ );
103
+ }
104
+
105
+ export function DrawerTitle({
106
+ className,
107
+ ...props
108
+ }: ComponentProps<typeof DrawerPrimitive.Title>) {
109
+ return (
110
+ <DrawerPrimitive.Title
111
+ data-slot="drawer-title"
112
+ className={cn(
113
+ "text-lg font-semibold leading-none tracking-tight",
114
+ className,
115
+ )}
116
+ {...props}
117
+ />
118
+ );
119
+ }
120
+
121
+ export function DrawerDescription({
122
+ className,
123
+ ...props
124
+ }: ComponentProps<typeof DrawerPrimitive.Description>) {
125
+ return (
126
+ <DrawerPrimitive.Description
127
+ data-slot="drawer-description"
128
+ className={cn("text-sm text-muted-foreground", className)}
129
+ {...props}
130
+ />
131
+ );
132
+ }
@@ -0,0 +1,273 @@
1
+ "use client";
2
+
3
+ import {
4
+ createContext,
5
+ useContext,
6
+ useState,
7
+ useCallback,
8
+ useEffect,
9
+ } from "react";
10
+ import type { ReactNode } from "react";
11
+ import {
12
+ Dialog,
13
+ DialogContent,
14
+ DialogHeader,
15
+ DialogTitle,
16
+ DialogDescription,
17
+ DialogFooter,
18
+ Button,
19
+ ScrollArea,
20
+ } from "../primitives";
21
+ import { AlertTriangle, Check, ChevronDown, ChevronRight, ClipboardCopy, Copy } from "lucide-react";
22
+
23
+ // ============================================================================
24
+ // Types
25
+ // ============================================================================
26
+
27
+ export interface ErrorDialogData {
28
+ title?: string;
29
+ description?: ReactNode;
30
+ trace?: string;
31
+ code?: string;
32
+ }
33
+
34
+ interface ErrorDialogContextType {
35
+ showError: (data: ErrorDialogData | string | Error) => void;
36
+ dismiss: () => void;
37
+ }
38
+
39
+ // ============================================================================
40
+ // Event Emitter for Global Access
41
+ // ============================================================================
42
+
43
+ type ErrorEventListener = (data: ErrorDialogData) => void;
44
+
45
+ class GlobalErrorEmitter {
46
+ private listeners: ErrorEventListener[] = [];
47
+
48
+ subscribe(listener: ErrorEventListener) {
49
+ this.listeners.push(listener);
50
+ return () => {
51
+ this.listeners = this.listeners.filter((l) => l !== listener);
52
+ };
53
+ }
54
+
55
+ emit(data: ErrorDialogData) {
56
+ this.listeners.forEach((listener) => listener(data));
57
+ }
58
+ }
59
+
60
+ export const globalError = new GlobalErrorEmitter();
61
+
62
+ // ============================================================================
63
+ // Context
64
+ // ============================================================================
65
+
66
+ const ErrorDialogContext = createContext<ErrorDialogContextType | null>(null);
67
+
68
+ export function useErrorDialog() {
69
+ const context = useContext(ErrorDialogContext);
70
+ if (!context) {
71
+ throw new Error("useErrorDialog must be used within ErrorDialogProvider");
72
+ }
73
+ return context;
74
+ }
75
+
76
+ // ============================================================================
77
+ // Component
78
+ // ============================================================================
79
+
80
+ export function ErrorDialogProvider({ children }: { children: ReactNode }) {
81
+ const [open, setOpen] = useState(false);
82
+ const [data, setData] = useState<ErrorDialogData>({});
83
+ const [expanded, setExpanded] = useState(false);
84
+ const [copied, setCopied] = useState(false);
85
+
86
+ // Subscribe to global events
87
+ useEffect(() => {
88
+ const unsubscribe = globalError.subscribe((errorData) => {
89
+ setData(errorData);
90
+ setOpen(true);
91
+ setExpanded(false);
92
+ setCopied(false);
93
+ });
94
+ return unsubscribe;
95
+ }, []);
96
+
97
+ const showError = useCallback((input: ErrorDialogData | string | Error) => {
98
+ let errorData: ErrorDialogData = {};
99
+
100
+ if (input instanceof Error) {
101
+ errorData = {
102
+ title: input.name || "Error",
103
+ description: input.message,
104
+ trace: input.stack,
105
+ };
106
+ } else if (typeof input === "string") {
107
+ errorData = {
108
+ title: "Error",
109
+ description: input,
110
+ };
111
+ } else {
112
+ errorData = input;
113
+ }
114
+
115
+ setData(errorData);
116
+ setOpen(true);
117
+ setExpanded(false);
118
+ setCopied(false);
119
+ }, []);
120
+
121
+ const dismiss = useCallback(() => {
122
+ setOpen(false);
123
+ }, []);
124
+
125
+ /** Build full diagnostic text for copy */
126
+ const buildFullDiagnostic = useCallback(() => {
127
+ // If trace already contains formatted diagnostic, use it directly
128
+ if (data.trace && data.trace.startsWith("=== Thông tin lỗi")) {
129
+ return data.trace;
130
+ }
131
+
132
+ // Otherwise build from available fields
133
+ const parts: string[] = [
134
+ `=== Thông tin lỗi ===`,
135
+ ];
136
+
137
+ if (data.code) parts.push(`Mã lỗi: ${data.code}`);
138
+
139
+ parts.push(`Thời gian: ${new Date().toLocaleString("vi-VN", {
140
+ timeZone: "Asia/Ho_Chi_Minh",
141
+ day: "2-digit", month: "2-digit", year: "numeric",
142
+ hour: "2-digit", minute: "2-digit", second: "2-digit",
143
+ })}`);
144
+
145
+ if (data.title) parts.push(`Lỗi: ${data.title}`);
146
+
147
+ if (data.description && typeof data.description === "string") {
148
+ parts.push(`Mô tả: ${data.description}`);
149
+ }
150
+
151
+ if (data.trace) {
152
+ parts.push(`\nChi tiết:\n${data.trace}`);
153
+ }
154
+
155
+ return parts.join("\n");
156
+ }, [data]);
157
+
158
+ const copyAll = useCallback(async () => {
159
+ const text = buildFullDiagnostic();
160
+ try {
161
+ await navigator.clipboard.writeText(text);
162
+ setCopied(true);
163
+ setTimeout(() => setCopied(false), 2000);
164
+ } catch {
165
+ // Fallback for older browsers
166
+ const textarea = document.createElement("textarea");
167
+ textarea.value = text;
168
+ textarea.style.position = "fixed";
169
+ textarea.style.opacity = "0";
170
+ document.body.appendChild(textarea);
171
+ textarea.select();
172
+ document.execCommand("copy");
173
+ document.body.removeChild(textarea);
174
+ setCopied(true);
175
+ setTimeout(() => setCopied(false), 2000);
176
+ }
177
+ }, [buildFullDiagnostic]);
178
+
179
+ const copyTrace = () => {
180
+ if (data.trace) {
181
+ navigator.clipboard.writeText(data.trace);
182
+ }
183
+ };
184
+
185
+ return (
186
+ <ErrorDialogContext.Provider value={{ showError, dismiss }}>
187
+ {children}
188
+ <Dialog open={open} onOpenChange={setOpen}>
189
+ <DialogContent className="max-w-md md:max-w-xl sm:max-w-lg border-destructive/20">
190
+ <DialogHeader>
191
+ <div className="flex items-center gap-2 text-destructive">
192
+ <div className="p-2 bg-destructive/10 rounded-full">
193
+ <AlertTriangle className="h-6 w-6" />
194
+ </div>
195
+ <DialogTitle className="text-xl">
196
+ {data.title || "Đã xảy ra lỗi"}
197
+ </DialogTitle>
198
+ </div>
199
+ <DialogDescription className="text-base pt-2 text-foreground font-medium">
200
+ {data.description}
201
+ </DialogDescription>
202
+ {data.code && (
203
+ <p className="text-xs text-muted-foreground font-mono mt-1">
204
+ Mã lỗi: {data.code}
205
+ </p>
206
+ )}
207
+ </DialogHeader>
208
+
209
+ {data.trace && (
210
+ <div className="mt-2 border rounded-md bg-muted/30 overflow-hidden">
211
+ <button
212
+ onClick={() => setExpanded(!expanded)}
213
+ className="w-full flex items-center justify-between p-2 text-sm font-medium hover:bg-muted/50 transition-colors"
214
+ >
215
+ <div className="flex items-center gap-1">
216
+ {expanded ? (
217
+ <ChevronDown className="h-4 w-4" />
218
+ ) : (
219
+ <ChevronRight className="h-4 w-4" />
220
+ )}
221
+ <span>Chi tiết lỗi & Trace Log</span>
222
+ </div>
223
+ </button>
224
+
225
+ {expanded && (
226
+ <div className="relative border-t bg-muted/50">
227
+ <ScrollArea className="h-[200px] w-full p-4">
228
+ <pre className="text-xs font-mono whitespace-pre-wrap break-all text-muted-foreground">
229
+ {data.trace}
230
+ </pre>
231
+ </ScrollArea>
232
+ <Button
233
+ variant="ghost"
234
+ size="icon"
235
+ className="absolute top-2 right-2 h-6 w-6"
236
+ onClick={copyTrace}
237
+ title="Copy Trace Log"
238
+ >
239
+ <Copy className="h-3 w-3" />
240
+ </Button>
241
+ </div>
242
+ )}
243
+ </div>
244
+ )}
245
+
246
+ <DialogFooter className="flex-row gap-2 sm:justify-between">
247
+ <Button
248
+ variant="outline"
249
+ size="sm"
250
+ onClick={copyAll}
251
+ className="gap-1.5 text-xs"
252
+ >
253
+ {copied ? (
254
+ <>
255
+ <Check className="h-3.5 w-3.5 text-green-600" />
256
+ Đã sao chép
257
+ </>
258
+ ) : (
259
+ <>
260
+ <ClipboardCopy className="h-3.5 w-3.5" />
261
+ Sao chép thông tin lỗi
262
+ </>
263
+ )}
264
+ </Button>
265
+ <Button variant="outline" onClick={dismiss}>
266
+ Đóng
267
+ </Button>
268
+ </DialogFooter>
269
+ </DialogContent>
270
+ </Dialog>
271
+ </ErrorDialogContext.Provider>
272
+ );
273
+ }