@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,109 @@
1
+ // Refer to emoji-picker-react README.md file for more details https://github.com/ealush/emoji-picker-react
2
+ "use client";
3
+
4
+ import dynamic from "next/dynamic";
5
+ import { Theme } from "emoji-picker-react";
6
+ import { Smile } from "lucide-react";
7
+
8
+ import type { Button } from "../primitives";
9
+ import type { ComponentProps, ComponentPropsWithoutRef } from "react";
10
+
11
+ import { cn } from "../../utils";
12
+
13
+ import { useIsDarkMode } from "../../hooks";
14
+ import { buttonVariants } from "../primitives";
15
+ import { Popover, PopoverContent, PopoverTrigger } from "../primitives/client";
16
+
17
+ // Avoid errors such as "document is not defined" on the server side
18
+ // See https://github.com/ealush/emoji-picker-react?tab=readme-ov-file#nextjs
19
+ const Picker = dynamic(
20
+ () => {
21
+ return import("emoji-picker-react");
22
+ },
23
+ { ssr: false },
24
+ );
25
+
26
+ import type { PickerProps } from "emoji-picker-react";
27
+
28
+ interface EmojiPickerProps extends PickerProps {
29
+ popoverContentClassName?: string;
30
+ popoverContentOptions?: ComponentPropsWithoutRef<typeof PopoverContent>;
31
+ buttonClassName?: string;
32
+ buttonOptions?: ComponentProps<typeof Button>;
33
+ placeholder?: string;
34
+ }
35
+
36
+ export function EmojiPicker({
37
+ popoverContentClassName,
38
+ popoverContentOptions,
39
+ buttonClassName,
40
+ buttonOptions,
41
+ ...props
42
+ }: EmojiPickerProps) {
43
+ const isDarkMode = useIsDarkMode();
44
+
45
+ const theme = isDarkMode ? Theme.DARK : Theme.LIGHT;
46
+
47
+ return (
48
+ <Popover>
49
+ <PopoverTrigger
50
+ className={cn(
51
+ buttonVariants({ variant: "ghost", size: "icon" }),
52
+ buttonClassName,
53
+ )}
54
+ {...buttonOptions}
55
+ aria-label="Emoji"
56
+ >
57
+ <Smile className="h-4 w-4" />
58
+ </PopoverTrigger>
59
+ <PopoverContent
60
+ className={cn("w-auto p-0", popoverContentClassName)}
61
+ align="start"
62
+ {...popoverContentOptions}
63
+ >
64
+ <Picker theme={theme} searchPlaceholder="Search..." {...props} />
65
+ </PopoverContent>
66
+ </Popover>
67
+ );
68
+ }
69
+
70
+ export function ReactionPicker({
71
+ popoverContentClassName,
72
+ popoverContentOptions,
73
+ buttonClassName,
74
+ buttonOptions,
75
+ ...props
76
+ }: EmojiPickerProps) {
77
+ const isDarkMode = useIsDarkMode();
78
+
79
+ const theme = isDarkMode ? Theme.DARK : Theme.LIGHT;
80
+
81
+ return (
82
+ <Popover>
83
+ <PopoverTrigger
84
+ className={cn(
85
+ buttonVariants({ variant: "ghost", size: "icon" }),
86
+ buttonClassName,
87
+ )}
88
+ {...buttonOptions}
89
+ aria-label="Emoji"
90
+ >
91
+ <Smile className="h-4 w-4" />
92
+ </PopoverTrigger>
93
+ <PopoverContent
94
+ className={cn("w-auto p-0", popoverContentClassName)}
95
+ align="start"
96
+ {...popoverContentOptions}
97
+ >
98
+ <Picker
99
+ theme={theme}
100
+ searchPlaceholder="Search..."
101
+ lazyLoadEmojis
102
+ allowExpandReactions={false}
103
+ {...props}
104
+ reactionsDefaultOpen
105
+ />
106
+ </PopoverContent>
107
+ </Popover>
108
+ );
109
+ }
@@ -0,0 +1,169 @@
1
+ "use client";
2
+
3
+ import { useCallback, useEffect, useState } from "react";
4
+ import Image from "next/image";
5
+ import { useDropzone } from "react-dropzone";
6
+ import { Loader2, UploadCloud, X } from "lucide-react";
7
+
8
+ import type { FileType } from "../../types";
9
+ import type { DropzoneOptions } from "react-dropzone";
10
+
11
+ import { cn, formatFileSize, wait } from "../../utils";
12
+
13
+ import { Button } from "../primitives";
14
+ import { FileThumbnail } from "./file-thumbnail";
15
+ import { ScrollArea } from "../primitives/client";
16
+
17
+ export interface FileDropzoneProps extends Partial<DropzoneOptions> {
18
+ className?: string;
19
+ value?: FileType[];
20
+ onFilesChange?: (files: FileType[]) => void;
21
+ }
22
+
23
+ export function FileDropzone({
24
+ className,
25
+ value,
26
+ onFilesChange,
27
+ ...props
28
+ }: FileDropzoneProps) {
29
+ const [files, setFiles] = useState<FileType[]>(value || []);
30
+ const [loadingFiles, setLoadingFiles] = useState<Set<string>>(new Set());
31
+
32
+ const maxFiles = props.multiple ? props.maxFiles : 1;
33
+ const isDisabled = maxFiles === files.length;
34
+
35
+ const onDrop = useCallback(
36
+ async (acceptedFiles: File[]) => {
37
+ const newFiles = acceptedFiles.map((file) => ({
38
+ id: crypto.randomUUID(),
39
+ name: file.name,
40
+ size: file.size,
41
+ type: file.type,
42
+ url: URL.createObjectURL(file),
43
+ }));
44
+
45
+ const updatedFiles = [...files, ...newFiles];
46
+ setFiles(updatedFiles);
47
+ onFilesChange?.(updatedFiles);
48
+ setLoadingFiles(new Set(newFiles.map((file) => file.id)));
49
+
50
+ // Simulate file processing
51
+ for (const file of newFiles) {
52
+ await wait(2000); // Simulate 2 seconds of processing
53
+ setLoadingFiles((prev) => {
54
+ const newLoadingFiles = new Set(prev);
55
+ newLoadingFiles.delete(file.id);
56
+ return newLoadingFiles;
57
+ });
58
+ }
59
+ },
60
+ [files, onFilesChange],
61
+ );
62
+
63
+ useEffect(() => {
64
+ if (value) {
65
+ setFiles(value);
66
+ }
67
+ }, [value]);
68
+
69
+ const { getRootProps, getInputProps, isDragActive } = useDropzone({
70
+ onDrop,
71
+ disabled: isDisabled,
72
+ ...props,
73
+ maxFiles,
74
+ });
75
+
76
+ const removeFile = (fileId: string) => {
77
+ const updatedFiles = files.filter((file) => {
78
+ if (file.id === fileId) {
79
+ URL.revokeObjectURL(file.url);
80
+ return false;
81
+ }
82
+
83
+ return true;
84
+ });
85
+
86
+ setFiles(updatedFiles);
87
+ onFilesChange?.(updatedFiles);
88
+ };
89
+
90
+ return (
91
+ <div
92
+ data-slot="file-dropzone"
93
+ {...(() => {
94
+ const { popover, ...rest } = getRootProps() as any;
95
+ return rest;
96
+ })()}
97
+ className={cn(
98
+ "h-[17.75rem] w-full relative flex rounded-lg border-2 border-dashed border-muted-foreground cursor-pointer transition-colors hover:border-primary hover:bg-muted/50",
99
+ isDragActive && "border-primary bg-muted/50",
100
+ isDisabled && "cursor-not-allowed",
101
+ className,
102
+ )}
103
+ >
104
+ <input
105
+ {...(() => {
106
+ const { popover, ...rest } = getInputProps() as any;
107
+ return rest;
108
+ })()}
109
+ />
110
+ <ScrollArea className="w-0 flex-1 p-6">
111
+ {files.length > 0 ? (
112
+ <div className="grid gap-4 grid-cols-2">
113
+ {files.map((file) => (
114
+ <div
115
+ key={file.id}
116
+ className="relative flex flex-col gap-2 rounded-lg border bg-background p-2 cursor-auto"
117
+ >
118
+ {file.type.startsWith("image/") ? (
119
+ <Image
120
+ src={file.url}
121
+ alt={file.name}
122
+ width={165}
123
+ height={165}
124
+ className="self-center aspect-square rounded object-contain pointer-events-none"
125
+ />
126
+ ) : (
127
+ <FileThumbnail
128
+ fileName={file.name}
129
+ className="self-center aspect-square size-full text-sm"
130
+ />
131
+ )}
132
+ {loadingFiles.has(file.id) && (
133
+ <div className="absolute inset-0 flex items-center justify-center bg-background/50 rounded">
134
+ <Loader2 className="h-8 w-8 text-primary animate-spin" />
135
+ </div>
136
+ )}
137
+ <div className="space-y-1">
138
+ <p className="text-sm font-medium truncate">{file.name}</p>
139
+ <p className="text-xs text-muted-foreground font-semibold">
140
+ {formatFileSize(file.size)}
141
+ </p>
142
+ </div>
143
+ <Button
144
+ variant="secondary"
145
+ size="icon"
146
+ className="absolute end-1 top-1 h-4 w-4"
147
+ onClick={(e) => {
148
+ e.stopPropagation();
149
+ removeFile(file.id);
150
+ }}
151
+ aria-label="Remove"
152
+ >
153
+ <X className="h-4 w-4" />
154
+ </Button>
155
+ </div>
156
+ ))}
157
+ </div>
158
+ ) : (
159
+ <div className="h-56 flex flex-col justify-center items-center gap-2 text-center p-4">
160
+ <UploadCloud className="h-8 w-8 text-muted-foreground" />
161
+ <p className="text-sm text-muted-foreground">
162
+ Drag and drop some files here, or click to select files
163
+ </p>
164
+ </div>
165
+ )}
166
+ </ScrollArea>
167
+ </div>
168
+ );
169
+ }
@@ -0,0 +1,29 @@
1
+ import { FileIcon } from "lucide-react";
2
+
3
+ import { cn } from "../../utils";
4
+
5
+ interface FileThumbnailProps {
6
+ fileName: string;
7
+ className?: string;
8
+ }
9
+
10
+ export function FileThumbnail({ fileName, className }: FileThumbnailProps) {
11
+ // Extract the file extension from the file name
12
+ const fileExtension = fileName
13
+ .slice(fileName.lastIndexOf(".") + 1)
14
+ .toUpperCase();
15
+
16
+ return (
17
+ <div className={cn("relative size-8 text-[6px] font-black", className)}>
18
+ <FileIcon className="size-full stroke-1" aria-labelledby="file-name" />
19
+ <div
20
+ className="absolute inset-0 flex justify-center items-center"
21
+ id="file-name"
22
+ aria-hidden
23
+ >
24
+ <span className="select-none">{fileExtension}</span>
25
+ <span className="sr-only">File</span>
26
+ </div>
27
+ </div>
28
+ );
29
+ }
@@ -0,0 +1,201 @@
1
+ // @goerp/core/ui/forms
2
+ // Form components for GoERP applications
3
+ // Based on react-hook-form and shadcn/ui patterns
4
+
5
+ "use client";
6
+
7
+ import { createContext, useContext, useId } from "react";
8
+ import type { ReactElement } from "react";
9
+ import { Slot } from "@radix-ui/react-slot";
10
+ import {
11
+ Controller,
12
+ FormProvider as ReactHookFormProvider,
13
+ useFormContext,
14
+ } from "react-hook-form";
15
+
16
+ import type * as LabelPrimitive from "@radix-ui/react-label";
17
+ import type { ComponentProps } from "react";
18
+ import type {
19
+ ControllerProps,
20
+ FieldPath,
21
+ FieldValues,
22
+ Control,
23
+ } from "react-hook-form";
24
+
25
+ import { cn } from "../../utils";
26
+ import { Label } from "../primitives/client";
27
+
28
+ export * from "./radio-group";
29
+ export * from "./rating";
30
+ export * from "./command";
31
+ export * from "./multi-select";
32
+ export * from "./date-picker";
33
+ export * from "./date-range-picker";
34
+ export * from "./date-time-picker";
35
+ export * from "./emoji-picker";
36
+ export * from "./file-dropzone";
37
+ export * from "./file-thumbnail";
38
+ export * from "./input-file";
39
+ export * from "./input-group";
40
+ export * from "./input-otp";
41
+ export * from "./input-phone";
42
+ export * from "./input-spin";
43
+ export * from "./input-tags";
44
+ export * from "./input-time";
45
+ export * from "./multiple-date-picker";
46
+ export * from "./time-picker";
47
+ export * from "./editor";
48
+
49
+ // Form component that accepts UseFormReturn from react-hook-form
50
+ // Using type assertion to avoid subscribe property requirement (react-hook-form version compatibility)
51
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
52
+ export const Form = ReactHookFormProvider as any;
53
+
54
+ type FormFieldContextValue<
55
+ TFieldValues extends FieldValues = FieldValues,
56
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
57
+ > = {
58
+ name: TName;
59
+ };
60
+
61
+ const FormFieldContext = createContext<FormFieldContextValue>(
62
+ {} as FormFieldContextValue,
63
+ );
64
+
65
+ // FormField component with version-compatible types
66
+ export function FormField<
67
+ TFieldValues extends FieldValues = FieldValues,
68
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
69
+ >(
70
+ props: Omit<ControllerProps<TFieldValues, TName>, "control"> & {
71
+ // Allow control from different react-hook-form versions for compatibility
72
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
73
+ control?: any;
74
+ },
75
+ ) {
76
+ return (
77
+ <FormFieldContext.Provider value={{ name: props.name }}>
78
+ {/* Type assertion to handle react-hook-form version compatibility */}
79
+ {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */}
80
+ <Controller {...(props as any)} />
81
+ </FormFieldContext.Provider>
82
+ );
83
+ }
84
+
85
+ export function useFormField() {
86
+ const fieldContext = useContext(FormFieldContext);
87
+ const itemContext = useContext(FormItemContext);
88
+ const { getFieldState, formState } = useFormContext();
89
+
90
+ const fieldState = getFieldState(fieldContext.name, formState);
91
+
92
+ if (!fieldContext) {
93
+ throw new Error("useFormField should be used within <FormField>");
94
+ }
95
+
96
+ const { id } = itemContext;
97
+
98
+ return {
99
+ id,
100
+ name: fieldContext.name,
101
+ formItemId: `${id}-form-item`,
102
+ formDescriptionId: `${id}-form-item-description`,
103
+ formMessageId: `${id}-form-item-message`,
104
+ ...fieldState,
105
+ };
106
+ }
107
+
108
+ type FormItemContextValue = {
109
+ id: string;
110
+ };
111
+
112
+ const FormItemContext = createContext<FormItemContextValue>(
113
+ {} as FormItemContextValue,
114
+ );
115
+
116
+ export function FormItem({ className, ...props }: ComponentProps<"div">) {
117
+ const id = useId();
118
+
119
+ return (
120
+ <FormItemContext.Provider value={{ id }}>
121
+ <div
122
+ data-slot="form-item"
123
+ className={cn("grid gap-2", className)}
124
+ {...props}
125
+ />
126
+ </FormItemContext.Provider>
127
+ );
128
+ }
129
+
130
+ export function FormLabel({
131
+ className,
132
+ ...props
133
+ }: ComponentProps<typeof LabelPrimitive.Root>) {
134
+ const { error, formItemId } = useFormField();
135
+
136
+ return (
137
+ <Label
138
+ data-slot="form-label"
139
+ data-error={!!error}
140
+ className={cn("data-[error=true]:text-destructive", className)}
141
+ htmlFor={formItemId}
142
+ {...props}
143
+ />
144
+ );
145
+ }
146
+
147
+ export function FormControl({ ...props }: ComponentProps<typeof Slot>) {
148
+ const { error, formItemId, formDescriptionId, formMessageId } =
149
+ useFormField();
150
+
151
+ return (
152
+ <Slot
153
+ data-slot="form-control"
154
+ id={formItemId}
155
+ aria-describedby={
156
+ !error
157
+ ? `${formDescriptionId}`
158
+ : `${formDescriptionId} ${formMessageId}`
159
+ }
160
+ aria-invalid={!!error}
161
+ {...props}
162
+ />
163
+ );
164
+ }
165
+
166
+ export function FormDescription({ className, ...props }: ComponentProps<"p">) {
167
+ const { formDescriptionId } = useFormField();
168
+
169
+ return (
170
+ <p
171
+ data-slot="form-description"
172
+ id={formDescriptionId}
173
+ className={cn("text-[0.8rem] text-muted-foreground", className)}
174
+ {...props}
175
+ />
176
+ );
177
+ }
178
+
179
+ export function FormMessage({
180
+ className,
181
+ children,
182
+ ...props
183
+ }: ComponentProps<"p">) {
184
+ const { error, formMessageId } = useFormField();
185
+ const body = error ? String(error?.message) : children;
186
+
187
+ if (!body) {
188
+ return null;
189
+ }
190
+
191
+ return (
192
+ <p
193
+ data-slot="form-message"
194
+ id={formMessageId}
195
+ className={cn("text-[0.8rem] font-medium text-destructive", className)}
196
+ {...props}
197
+ >
198
+ {body}
199
+ </p>
200
+ );
201
+ }
@@ -0,0 +1,99 @@
1
+ "use client";
2
+
3
+ import { useEffect, useImperativeHandle, useRef, useState } from "react";
4
+
5
+ import type { ChangeEvent, ComponentProps } from "react";
6
+
7
+ import { cn } from "../../utils";
8
+
9
+ import { Button } from "../primitives";
10
+
11
+ interface InputFileProps
12
+ extends Omit<ComponentProps<"input">, "value" | "onChange"> {
13
+ containerClassName?: string;
14
+ buttonVariant?: ComponentProps<typeof Button>["variant"];
15
+ buttonLabel?: string;
16
+ placeholder?: string;
17
+ value?: FileList;
18
+ onValueChange?: (value: FileList) => void;
19
+ }
20
+
21
+ export function InputFile({
22
+ className,
23
+ containerClassName,
24
+ buttonVariant,
25
+ buttonLabel,
26
+ placeholder = "No file chosen",
27
+ value,
28
+ onValueChange,
29
+ ...props
30
+ }: InputFileProps) {
31
+ const [fileName, setFileName] = useState<string>(placeholder);
32
+ const inputRef = useRef<HTMLInputElement>(null);
33
+
34
+ // Forward the ref to the internal ref
35
+ useImperativeHandle(props.ref, () => inputRef.current!);
36
+
37
+ const handleFileChange = (event: ChangeEvent<HTMLInputElement>) => {
38
+ const files = event.target.files;
39
+
40
+ if (files) {
41
+ if (files.length > 1) {
42
+ setFileName(`${files.length} Files`);
43
+ } else if (files.length === 1) {
44
+ setFileName(files[0].name);
45
+ } else {
46
+ setFileName(placeholder);
47
+ }
48
+
49
+ onValueChange?.(files);
50
+ }
51
+ };
52
+
53
+ const handleClick = () => {
54
+ inputRef.current?.click();
55
+ };
56
+
57
+ useEffect(() => {
58
+ if (value) {
59
+ if (value.length > 1) {
60
+ setFileName(`${value.length} Files`);
61
+ } else {
62
+ setFileName(value[0].name);
63
+ }
64
+ } else {
65
+ setFileName(placeholder);
66
+ }
67
+ }, [value, placeholder]);
68
+
69
+ return (
70
+ <div
71
+ data-slot="input-file"
72
+ className={cn(
73
+ "h-9 w-full flex rounded-md border border-input bg-transparent text-sm transition-colors",
74
+ props.disabled && "cursor-not-allowed opacity-50",
75
+ containerClassName,
76
+ )}
77
+ >
78
+ <Button
79
+ type="button"
80
+ variant={buttonVariant}
81
+ className="h-full w-28 rounded-e-none border-0 border-e border-input"
82
+ onClick={handleClick}
83
+ disabled={props.disabled}
84
+ >
85
+ {buttonLabel ?? `Choose File${props.multiple ? "s" : ""}`}
86
+ </Button>
87
+ <div className="flex-1 flex items-center text-muted-foreground px-3 break-all">
88
+ <span className="w-0 flex-1 truncate">{fileName}</span>
89
+ </div>
90
+ <input
91
+ {...props}
92
+ ref={inputRef}
93
+ type="file"
94
+ className={cn("hidden", className)}
95
+ onChange={handleFileChange}
96
+ />
97
+ </div>
98
+ );
99
+ }
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import type { ComponentProps } from "react";
4
+
5
+ import { cn } from "../../utils";
6
+
7
+ export interface InputGroupProps extends ComponentProps<"div"> {}
8
+
9
+ export function InputGroup({ className, children, ...props }: InputGroupProps) {
10
+ return (
11
+ <div
12
+ data-slot="input-group"
13
+ className={cn(
14
+ "flex min-h-9 w-full rounded-md border border-input bg-transparent text-sm transition-colors [&>*:not(.input-group-text)]:border-0 [&>input]:z-10 [&>input]:first:rounded-e-none [&>input]:last:rounded-s-none [&>input:not(:first-child):not(:last-child)]:rounded-none [&>textarea]:z-10 [&>textarea]:first:rounded-e-none [&>textarea]:last:rounded-s-none [&>textarea:not(:first-child):not(:last-child)]:rounded-none [&>button]:first:rounded-e-none [&>button]:last:rounded-s-none [&>button]:[&:not(:first-child):not(:last-child)]:rounded-none",
15
+ className,
16
+ )}
17
+ {...props}
18
+ >
19
+ {children}
20
+ </div>
21
+ );
22
+ }
23
+
24
+ export interface InputGroupTextProps extends ComponentProps<"div"> {
25
+ merged?: boolean;
26
+ }
27
+
28
+ export function InputGroupText({
29
+ className,
30
+ merged = false,
31
+ ...props
32
+ }: InputGroupTextProps) {
33
+ return (
34
+ <div
35
+ data-slot="input-group-text"
36
+ className={cn(
37
+ "input-group-text",
38
+ "flex items-center justify-center border-input px-3 py-1 text-sm text-muted-foreground",
39
+ !merged &&
40
+ "first:rounded-s-md last:rounded-e-md first:border-e last:border-s [&:not(:first-child):not(:last-child)]:border-x",
41
+ className,
42
+ )}
43
+ {...props}
44
+ />
45
+ );
46
+ }