@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,239 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import type { ComponentProps } from "react";
5
+ import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
6
+ import { Check, ChevronRight, Dot } from "lucide-react";
7
+ import { cn } from "../../utils";
8
+
9
+ export function DropdownMenu({
10
+ ...props
11
+ }: ComponentProps<typeof DropdownMenuPrimitive.Root>) {
12
+ return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
13
+ }
14
+
15
+ export function DropdownMenuPortal({
16
+ ...props
17
+ }: ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
18
+ return (
19
+ <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
20
+ );
21
+ }
22
+
23
+ export function DropdownMenuTrigger({
24
+ className,
25
+ ...props
26
+ }: ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
27
+ return (
28
+ <DropdownMenuPrimitive.Trigger
29
+ data-slot="dropdown-menu-trigger"
30
+ className={cn("cursor-pointer", className)}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ export function DropdownMenuGroup({
37
+ ...props
38
+ }: ComponentProps<typeof DropdownMenuPrimitive.Group>) {
39
+ return (
40
+ <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
41
+ );
42
+ }
43
+
44
+ export function DropdownMenuRadioGroup({
45
+ ...props
46
+ }: ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
47
+ return (
48
+ <DropdownMenuPrimitive.RadioGroup
49
+ data-slot="dropdown-menu-radio-group"
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ export function DropdownMenuSub({
56
+ ...props
57
+ }: ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
58
+ return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
59
+ }
60
+
61
+ type DropdownMenuSubTriggerProps = ComponentProps<
62
+ typeof DropdownMenuPrimitive.SubTrigger
63
+ > & {
64
+ inset?: boolean;
65
+ };
66
+
67
+ export function DropdownMenuSubTrigger({
68
+ className,
69
+ inset,
70
+ children,
71
+ ...props
72
+ }: DropdownMenuSubTriggerProps) {
73
+ return (
74
+ <DropdownMenuPrimitive.SubTrigger
75
+ data-slot="dropdown-menu-sub-trigger"
76
+ data-inset={inset}
77
+ className={cn(
78
+ "cursor-pointer flex items-center rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:ps-8 focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
79
+ className,
80
+ )}
81
+ {...props}
82
+ >
83
+ {children}
84
+ <ChevronRight className="ms-auto h-4 w-4 rtl:-scale-100" />
85
+ </DropdownMenuPrimitive.SubTrigger>
86
+ );
87
+ }
88
+
89
+ export function DropdownMenuSubContent({
90
+ className,
91
+ ...props
92
+ }: ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
93
+ return (
94
+ <DropdownMenuPrimitive.SubContent
95
+ data-slot="dropdown-menu-sub-content"
96
+ className={cn(
97
+ "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",
98
+ className,
99
+ )}
100
+ {...props}
101
+ />
102
+ );
103
+ }
104
+
105
+ export function DropdownMenuContent({
106
+ className,
107
+ sideOffset = 4,
108
+ ...props
109
+ }: ComponentProps<typeof DropdownMenuPrimitive.Content>) {
110
+ return (
111
+ <DropdownMenuPrimitive.Portal>
112
+ <DropdownMenuPrimitive.Content
113
+ data-slot="dropdown-menu-content"
114
+ sideOffset={sideOffset}
115
+ className={cn(
116
+ "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",
117
+ className,
118
+ )}
119
+ {...props}
120
+ />
121
+ </DropdownMenuPrimitive.Portal>
122
+ );
123
+ }
124
+
125
+ export function DropdownMenuItem({
126
+ className,
127
+ inset,
128
+ ...props
129
+ }: ComponentProps<typeof DropdownMenuPrimitive.Item> & {
130
+ inset?: boolean;
131
+ }) {
132
+ return (
133
+ <DropdownMenuPrimitive.Item
134
+ data-slot="dropdown-menu-item"
135
+ data-inset={inset}
136
+ className={cn(
137
+ "relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 data-[inset]:ps-8",
138
+ className,
139
+ )}
140
+ {...props}
141
+ />
142
+ );
143
+ }
144
+
145
+ export function DropdownMenuCheckboxItem({
146
+ className,
147
+ children,
148
+ checked,
149
+ ...props
150
+ }: ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
151
+ return (
152
+ <DropdownMenuPrimitive.CheckboxItem
153
+ data-slot="dropdown-menu-checkbox-item"
154
+ className={cn(
155
+ "relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
156
+ className,
157
+ )}
158
+ checked={checked}
159
+ {...props}
160
+ >
161
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
162
+ <DropdownMenuPrimitive.ItemIndicator>
163
+ <Check className="h-4 w-4" />
164
+ </DropdownMenuPrimitive.ItemIndicator>
165
+ </span>
166
+ {children}
167
+ </DropdownMenuPrimitive.CheckboxItem>
168
+ );
169
+ }
170
+
171
+ export function DropdownMenuRadioItem({
172
+ className,
173
+ children,
174
+ ...props
175
+ }: ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
176
+ return (
177
+ <DropdownMenuPrimitive.RadioItem
178
+ data-slot="dropdown-menu-radio-item"
179
+ className={cn(
180
+ "relative flex cursor-pointer select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-hidden transition-colors focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
181
+ className,
182
+ )}
183
+ {...props}
184
+ >
185
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
186
+ <DropdownMenuPrimitive.ItemIndicator>
187
+ <Dot className="h-4 w-4 fill-current" />
188
+ </DropdownMenuPrimitive.ItemIndicator>
189
+ </span>
190
+ {children}
191
+ </DropdownMenuPrimitive.RadioItem>
192
+ );
193
+ }
194
+
195
+ export function DropdownMenuLabel({
196
+ className,
197
+ inset,
198
+ ...props
199
+ }: ComponentProps<typeof DropdownMenuPrimitive.Label> & {
200
+ inset?: boolean;
201
+ }) {
202
+ return (
203
+ <DropdownMenuPrimitive.Label
204
+ data-slot="dropdown-menu-label"
205
+ data-inset={inset}
206
+ className={cn(
207
+ "px-2 py-1.5 text-sm font-semibold data-[inset]:ps-8",
208
+ className,
209
+ )}
210
+ {...props}
211
+ />
212
+ );
213
+ }
214
+
215
+ export function DropdownMenuSeparator({
216
+ className,
217
+ ...props
218
+ }: ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
219
+ return (
220
+ <DropdownMenuPrimitive.Separator
221
+ data-slot="dropdown-menu-separator"
222
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
223
+ {...props}
224
+ />
225
+ );
226
+ }
227
+
228
+ export function DropdownMenuShortcut({
229
+ className,
230
+ ...props
231
+ }: ComponentProps<"span">) {
232
+ return (
233
+ <span
234
+ data-slot="dropdown-menu-shortcut"
235
+ className={cn("ml-auto text-xs tracking-widest opacity-60", className)}
236
+ {...props}
237
+ />
238
+ );
239
+ }
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ // Refer to Lucide documentation for more details https://lucide.dev/guide/packages/lucide-react
4
+ import { icons } from "lucide-react";
5
+
6
+ import type { LucideProps } from "lucide-react";
7
+
8
+ export type DynamicIconNameType = keyof typeof icons;
9
+
10
+ interface DynamicIconProps extends LucideProps {
11
+ name?: DynamicIconNameType;
12
+ }
13
+
14
+ // Component to render a dynamic Lucide icon based on its name.
15
+ export function DynamicIcon({ name, ...props }: DynamicIconProps) {
16
+ if (!name) return null;
17
+
18
+ const LucideIcon = icons[name]; // Dynamically retrieve the icon by name.
19
+
20
+ // Return null if the icon name is invalid.
21
+ if (!LucideIcon) return null;
22
+
23
+ return <LucideIcon {...props} />;
24
+ }
@@ -0,0 +1,134 @@
1
+ // @goerp/core/ui/primitives
2
+ // Primitive UI components (Server Compatible)
3
+
4
+ import * as React from "react";
5
+ import { cn } from "../../utils";
6
+ import { Button } from "./button";
7
+ import type { ButtonProps } from "./button";
8
+
9
+ export * from "./badge";
10
+ export * from "./button";
11
+ export * from "./input";
12
+ export * from "./checkbox";
13
+
14
+ // ============================================================================
15
+ // Textarea
16
+ // ============================================================================
17
+
18
+ export interface TextareaProps
19
+ extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
20
+
21
+ export const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
22
+ ({ className, ...props }, ref) => {
23
+ return (
24
+ <textarea
25
+ className={cn(
26
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
27
+ className,
28
+ )}
29
+ ref={ref}
30
+ {...props}
31
+ />
32
+ );
33
+ },
34
+ );
35
+ Textarea.displayName = "Textarea";
36
+
37
+ export * from "./card";
38
+ export * from "./separator";
39
+
40
+ // ============================================================================
41
+ // Spinner / Loading
42
+ // ============================================================================
43
+
44
+ export interface SpinnerProps {
45
+ size?: "sm" | "md" | "lg";
46
+ className?: string;
47
+ }
48
+
49
+ export function Spinner({ size = "md", className }: SpinnerProps) {
50
+ const sizeClasses = {
51
+ sm: "h-4 w-4",
52
+ md: "h-6 w-6",
53
+ lg: "h-8 w-8",
54
+ };
55
+
56
+ return (
57
+ <div
58
+ className={cn(
59
+ "animate-spin rounded-full border-2 border-current border-t-transparent",
60
+ sizeClasses[size],
61
+ className,
62
+ )}
63
+ />
64
+ );
65
+ }
66
+
67
+ export interface ButtonLoadingProps extends ButtonProps {
68
+ isLoading?: boolean;
69
+ }
70
+
71
+ export const ButtonLoading = React.forwardRef<
72
+ HTMLButtonElement,
73
+ ButtonLoadingProps
74
+ >(({ className, isLoading, children, disabled, ...props }, ref) => {
75
+ return (
76
+ <Button
77
+ className={cn("gap-2", className)}
78
+ disabled={isLoading || disabled}
79
+ ref={ref}
80
+ {...props}
81
+ >
82
+ {isLoading && <Spinner size="sm" />}
83
+ {children}
84
+ </Button>
85
+ );
86
+ });
87
+ ButtonLoading.displayName = "ButtonLoading";
88
+
89
+ // ============================================================================
90
+ // Skeleton
91
+ // ============================================================================
92
+
93
+ export function Skeleton({
94
+ className,
95
+ ...props
96
+ }: React.HTMLAttributes<HTMLDivElement>) {
97
+ return (
98
+ <div
99
+ className={cn("animate-pulse rounded-md bg-muted", className)}
100
+ {...props}
101
+ />
102
+ );
103
+ }
104
+
105
+ // Data Display
106
+ export * from "./card"; // Added
107
+ export * from "./table";
108
+ export * from "./dropdown-menu";
109
+
110
+ // Feedback
111
+ export * from "./dialog";
112
+
113
+ // Layout
114
+ export * from "./tabs";
115
+
116
+ export * from "./breadcrumb";
117
+ export * from "./keyboard";
118
+ export * from "./pagination";
119
+ export * from "./status-badge";
120
+ export * from "./sidebar";
121
+ export * from "./popover";
122
+ export * from "./scroll-area";
123
+ export * from "./calendar";
124
+ export * from "./resizable";
125
+ export * from "./slider";
126
+ export * from "./switch";
127
+ export * from "./tooltip";
128
+ export * from "./select";
129
+ export * from "./menubar";
130
+ export * from "./navigation-menu";
131
+ export * from "./toggle";
132
+ export * from "./toggle-group";
133
+ export * from "./combobox";
134
+ export * from "./label";
@@ -0,0 +1,131 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../../utils";
6
+
7
+ import { Input } from "./input";
8
+
9
+ interface InputNumberProps
10
+ extends Omit<
11
+ React.InputHTMLAttributes<HTMLInputElement>,
12
+ "onChange" | "value"
13
+ > {
14
+ value?: number | string | null;
15
+ onChange?: (value: number | null) => void;
16
+ decimalScale?: number;
17
+ suffix?: string;
18
+ }
19
+
20
+ export const InputNumber = React.forwardRef<HTMLInputElement, InputNumberProps>(
21
+ ({ value, onChange, className, decimalScale = 0, suffix, ...props }, ref) => {
22
+ // Internal string state to handle formatting
23
+ const [displayValue, setDisplayValue] = React.useState("");
24
+
25
+ // Helper to parse displayed string to number
26
+ const parseDisplayValue = (val: string) => {
27
+ // Remove dots (thousands), replace comma with dot (decimal)
28
+ const clean = val.replace(/\./g, "").replace(",", ".");
29
+ return Number(clean);
30
+ };
31
+
32
+ const formatNumber = React.useCallback(
33
+ (num: number) => {
34
+ return new Intl.NumberFormat("vi-VN", {
35
+ maximumFractionDigits: decimalScale,
36
+ minimumFractionDigits: 0,
37
+ }).format(num);
38
+ },
39
+ [decimalScale],
40
+ );
41
+
42
+ // Update display value when prop value changes
43
+ React.useEffect(() => {
44
+ if (value === null || value === undefined || value === "") {
45
+ setDisplayValue("");
46
+ return;
47
+ }
48
+
49
+ const numValue = Number(value);
50
+ if (isNaN(numValue)) return;
51
+
52
+ // Avoid overriding user input if they match numerically
53
+ const currentNum = parseDisplayValue(displayValue);
54
+ if (currentNum !== numValue) {
55
+ setDisplayValue(formatNumber(numValue));
56
+ }
57
+ }, [value, decimalScale, displayValue, formatNumber]);
58
+
59
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
60
+ const rawValue = e.target.value;
61
+
62
+ // Allow digits and one comma
63
+ let cleanVal = rawValue.replace(/[^0-9,]/g, "");
64
+
65
+ // Handle multiple commas - keep only first
66
+ const parts = cleanVal.split(",");
67
+ if (parts.length > 2) {
68
+ cleanVal = parts[0] + "," + parts.slice(1).join("");
69
+ }
70
+
71
+ if (cleanVal === "") {
72
+ setDisplayValue("");
73
+ onChange?.(null);
74
+ return;
75
+ }
76
+
77
+ // Split integer and decimal parts
78
+ const [integerPart, decimalPart] = cleanVal.split(",");
79
+
80
+ let formattedInteger = integerPart;
81
+ if (integerPart) {
82
+ formattedInteger = new Intl.NumberFormat("vi-VN").format(
83
+ Number(integerPart),
84
+ );
85
+ }
86
+
87
+ let newDisplayValue = formattedInteger;
88
+
89
+ // Check decimal scale truncation
90
+ let finalDecimalPart = decimalPart;
91
+
92
+ if (decimalPart !== undefined) {
93
+ if (decimalScale !== undefined && decimalPart.length > decimalScale) {
94
+ finalDecimalPart = decimalPart.substring(0, decimalScale);
95
+ }
96
+ newDisplayValue += "," + finalDecimalPart;
97
+ }
98
+
99
+ setDisplayValue(newDisplayValue);
100
+
101
+ // Calculate numeric value for parent
102
+ let numStr = integerPart;
103
+ if (finalDecimalPart !== undefined) {
104
+ numStr += "." + finalDecimalPart;
105
+ }
106
+
107
+ const numValue = Number(numStr.replace(/\./g, ""));
108
+ onChange?.(numValue);
109
+ };
110
+
111
+ return (
112
+ <div className="relative">
113
+ <Input
114
+ {...props}
115
+ ref={ref}
116
+ type="text"
117
+ inputMode="numeric"
118
+ value={displayValue}
119
+ onChange={handleChange}
120
+ className={cn("pr-8", className)}
121
+ />
122
+ {suffix && (
123
+ <div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none text-muted-foreground text-sm">
124
+ {suffix}
125
+ </div>
126
+ )}
127
+ </div>
128
+ );
129
+ },
130
+ );
131
+ InputNumber.displayName = "InputNumber";
@@ -0,0 +1,22 @@
1
+ import * as React from "react";
2
+ import { cn } from "../../utils";
3
+
4
+ export interface InputProps
5
+ extends React.InputHTMLAttributes<HTMLInputElement> {}
6
+
7
+ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
8
+ ({ className, type, ...props }, ref) => {
9
+ return (
10
+ <input
11
+ type={type}
12
+ className={cn(
13
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
14
+ className,
15
+ )}
16
+ ref={ref}
17
+ {...props}
18
+ />
19
+ );
20
+ },
21
+ );
22
+ Input.displayName = "Input";
@@ -0,0 +1,23 @@
1
+ import type { ComponentProps } from "react";
2
+
3
+ import { cn } from "../../utils";
4
+
5
+ export function Keyboard({
6
+ className,
7
+ children,
8
+ ...props
9
+ }: ComponentProps<"kbd">) {
10
+ return (
11
+ <kbd
12
+ data-slot="keyboard"
13
+ className={cn(
14
+ "pointer-events-none select-none h-5 inline-flex items-center gap-x-1 px-1.5 bg-muted text-sm text-muted-foreground font-mono border rounded-sm",
15
+ "before:content-['⌘']",
16
+ className,
17
+ )}
18
+ {...props}
19
+ >
20
+ {children}
21
+ </kbd>
22
+ );
23
+ }
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import type { ComponentProps } from "react";
5
+ import * as LabelPrimitive from "@radix-ui/react-label";
6
+ import { cn } from "../../utils";
7
+
8
+ export interface LabelProps
9
+ extends ComponentProps<typeof LabelPrimitive.Root> {}
10
+
11
+ export const Label = React.forwardRef<
12
+ React.ElementRef<typeof LabelPrimitive.Root>,
13
+ LabelProps
14
+ >(({ className, ...props }, ref) => (
15
+ <LabelPrimitive.Root
16
+ ref={ref}
17
+ className={cn(
18
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
19
+ className,
20
+ )}
21
+ {...props}
22
+ />
23
+ ));
24
+ Label.displayName = LabelPrimitive.Root.displayName;