@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,81 @@
1
+ "use client";
2
+
3
+ import { useContext } from "react";
4
+ import { OTPInput, OTPInputContext } from "input-otp";
5
+ import { Minus } from "lucide-react";
6
+
7
+ import type { ComponentProps } from "react";
8
+
9
+ import { cn } from "../../utils";
10
+
11
+ type InputOTPProps = ComponentProps<typeof OTPInput> & {
12
+ containerClassName?: string;
13
+ };
14
+
15
+ export function InputOTP({
16
+ className,
17
+ containerClassName,
18
+ ...props
19
+ }: InputOTPProps) {
20
+ return (
21
+ <OTPInput
22
+ data-slot="input-otp"
23
+ containerClassName={cn(
24
+ "flex items-center gap-2 has-disabled:opacity-50",
25
+ containerClassName,
26
+ )}
27
+ className={cn("disabled:cursor-not-allowed", className)}
28
+ {...props}
29
+ />
30
+ );
31
+ }
32
+
33
+ export function InputOTPGroup({ className, ...props }: ComponentProps<"div">) {
34
+ return (
35
+ <div
36
+ data-slot="input-otp-group"
37
+ className={cn("flex items-center", className)}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ type InputOTPSlotProps = ComponentProps<"div"> & {
44
+ index: number;
45
+ };
46
+
47
+ export function InputOTPSlot({
48
+ index,
49
+ className,
50
+ ...props
51
+ }: InputOTPSlotProps) {
52
+ const inputOTPContext = useContext(OTPInputContext as any) as any;
53
+ const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index];
54
+
55
+ return (
56
+ <div
57
+ data-slot="input-otp-slot"
58
+ className={cn(
59
+ "relative flex h-9 w-9 items-center justify-center border-y border-e border-input text-sm transition-all first:rounded-s-md first:border-s last:rounded-e-md",
60
+ isActive && "z-10 ring-1 ring-ring",
61
+ className,
62
+ )}
63
+ {...props}
64
+ >
65
+ {char}
66
+ {hasFakeCaret && (
67
+ <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
68
+ <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
69
+ </div>
70
+ )}
71
+ </div>
72
+ );
73
+ }
74
+
75
+ export function InputOTPSeparator({ ...props }: ComponentProps<"div">) {
76
+ return (
77
+ <div data-slot="input-otp-separator" role="separator" {...props}>
78
+ <Minus />
79
+ </div>
80
+ );
81
+ }
@@ -0,0 +1,172 @@
1
+ "use client";
2
+
3
+ import { useCallback, useMemo } from "react";
4
+ import Image from "next/image";
5
+ import { useDirection } from "@radix-ui/react-direction";
6
+ import * as RPNInputPrimitive from "react-phone-number-input";
7
+ import { Check, ChevronsUpDown } from "lucide-react";
8
+
9
+ import type { ComponentProps, ComponentPropsWithoutRef } from "react";
10
+
11
+ import { cn } from "../../utils";
12
+
13
+ import { Button } from "../primitives";
14
+ import {
15
+ Command,
16
+ CommandEmpty,
17
+ CommandGroup,
18
+ CommandInput,
19
+ CommandItem,
20
+ CommandList,
21
+ } from "./command";
22
+ import { Input } from "../primitives";
23
+ import { Popover, PopoverContent, PopoverTrigger } from "../primitives/client";
24
+ import { ScrollArea } from "../primitives/client";
25
+
26
+ type InputPhoneProps = Omit<
27
+ ComponentPropsWithoutRef<"input">,
28
+ "onChange" | "value"
29
+ > &
30
+ Omit<
31
+ RPNInputPrimitive.Props<typeof RPNInputPrimitive.default>,
32
+ "onChange"
33
+ > & {
34
+ onChange?: (value: RPNInputPrimitive.Value) => void;
35
+ };
36
+
37
+ export function InputPhone({ className, onChange, ...props }: InputPhoneProps) {
38
+ const direction = useDirection();
39
+
40
+ return (
41
+ <RPNInputPrimitive.default
42
+ data-slot="input-phone"
43
+ dir={direction}
44
+ className={cn("w-full flex", className)}
45
+ flagComponent={FlagComponent}
46
+ countrySelectComponent={CountrySelect}
47
+ inputComponent={InputComponent}
48
+ /**
49
+ * Handles the onChange event.
50
+ *
51
+ * react-phone-number-input might trigger the onChange event as undefined
52
+ * when a valid phone number is not entered. To prevent this,
53
+ * the value is coerced to an empty string.
54
+ *
55
+ * @param {E164Number | undefined} value - The entered value
56
+ */
57
+ onChange={(value) => onChange?.(value as RPNInputPrimitive.Value)}
58
+ {...props}
59
+ />
60
+ );
61
+ }
62
+
63
+ function InputComponent({ className, ...props }: ComponentProps<"input">) {
64
+ return (
65
+ <Input
66
+ className={cn("rounded-e-md rounded-s-none", className)}
67
+ {...props}
68
+ />
69
+ );
70
+ }
71
+
72
+ interface CountrySelectOption {
73
+ label: string;
74
+ value: RPNInputPrimitive.Country;
75
+ }
76
+
77
+ interface CountrySelectProps {
78
+ disabled?: boolean;
79
+ value: RPNInputPrimitive.Country;
80
+ onChange: (value: RPNInputPrimitive.Country) => void;
81
+ options: CountrySelectOption[];
82
+ }
83
+
84
+ function CountrySelect({
85
+ disabled,
86
+ value,
87
+ onChange,
88
+ options,
89
+ }: CountrySelectProps) {
90
+ const handleSelect = useCallback(
91
+ (country: RPNInputPrimitive.Country) => onChange(country),
92
+ [onChange],
93
+ );
94
+
95
+ const memoizedOptions = useMemo(
96
+ () =>
97
+ options.map((option) => (
98
+ <CommandItem
99
+ className="flex gap-2 w-full"
100
+ key={option.value || "ZZ"}
101
+ onSelect={() => handleSelect(option.value)}
102
+ >
103
+ <FlagComponent country={option.value} countryName={option.label} />
104
+ <span className="flex-1 text-sm">{option.label}</span>
105
+ {option.value && (
106
+ <span className="text-foreground/50 text-sm">
107
+ {`+${RPNInputPrimitive.getCountryCallingCode(option.value)}`}
108
+ </span>
109
+ )}
110
+ <span aria-label={option.value === value ? "Selected" : undefined}>
111
+ <Check
112
+ className={cn(
113
+ "ms-auto size-4",
114
+ option.value === value ? "opacity-100" : "opacity-0",
115
+ )}
116
+ />
117
+ </span>
118
+ </CommandItem>
119
+ )),
120
+ [options, handleSelect, value],
121
+ );
122
+
123
+ return (
124
+ <Popover>
125
+ <PopoverTrigger asChild>
126
+ <Button
127
+ type="button"
128
+ variant="outline"
129
+ className={cn("flex gap-1 rounded-e-none rounded-s-lg px-3")}
130
+ disabled={disabled}
131
+ >
132
+ <FlagComponent country={value} countryName={value} />
133
+ <ChevronsUpDown
134
+ className={cn(
135
+ "-me-2 size-4 opacity-50",
136
+ disabled ? "hidden" : "opacity-100",
137
+ )}
138
+ aria-hidden
139
+ />
140
+ </Button>
141
+ </PopoverTrigger>
142
+ <PopoverContent className="w-[300px] p-0" align="start">
143
+ <Command>
144
+ <CommandInput placeholder="Search country..." />
145
+ <CommandList>
146
+ <CommandEmpty>No country found.</CommandEmpty>
147
+ <ScrollArea className="h-[300px] max-h-[300px]">
148
+ <CommandGroup>{memoizedOptions}</CommandGroup>
149
+ </ScrollArea>
150
+ </CommandList>
151
+ </Command>
152
+ </PopoverContent>
153
+ </Popover>
154
+ );
155
+ }
156
+
157
+ function FlagComponent({ country, countryName }: RPNInputPrimitive.FlagProps) {
158
+ const flagUrl = `https://purecatamphetamine.github.io/country-flag-icons/3x2/${country}.svg`;
159
+
160
+ return (
161
+ <div className="relative h-4 w-6 bg-foreground/20 rounded-sm">
162
+ {country && (
163
+ <Image
164
+ src={flagUrl}
165
+ alt={countryName}
166
+ fill
167
+ className="rounded-sm object-cover"
168
+ />
169
+ )}
170
+ </div>
171
+ );
172
+ }
@@ -0,0 +1,116 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { Minus, Plus } from "lucide-react";
5
+
6
+ import type { ChangeEvent, ComponentProps } from "react";
7
+
8
+ import { cn } from "../../utils";
9
+
10
+ import { Button } from "../primitives";
11
+
12
+ interface InputSpinProps
13
+ extends Omit<
14
+ ComponentProps<"input">,
15
+ "onChange" | "value" | "min" | "max" | "step"
16
+ > {
17
+ value?: number;
18
+ onChange?: (value: number) => void;
19
+ min?: number;
20
+ max?: number;
21
+ step?: number;
22
+ buttonVariant?: ComponentProps<typeof Button>["variant"];
23
+ }
24
+
25
+ export function InputSpin({
26
+ value,
27
+ onChange,
28
+ min = -Infinity,
29
+ max = Infinity,
30
+ step = 1,
31
+ disabled = false,
32
+ className,
33
+ buttonVariant = "default",
34
+ ...props
35
+ }: InputSpinProps) {
36
+ const [internalValue, setInternalValue] = useState<number>(value ?? 0);
37
+
38
+ function handleIncrement() {
39
+ const newValue = internalValue + step;
40
+ if (newValue <= max) {
41
+ setInternalValue(newValue);
42
+ onChange?.(newValue);
43
+ }
44
+ }
45
+
46
+ function handleDecrement() {
47
+ const newValue = internalValue - step;
48
+ if (newValue >= min) {
49
+ setInternalValue(newValue);
50
+ onChange?.(newValue);
51
+ }
52
+ }
53
+
54
+ const handleOnChange = (e: ChangeEvent<HTMLInputElement>) => {
55
+ const newValue = parseFloat(e.target.value);
56
+ if (!isNaN(newValue) && newValue >= min && newValue <= max) {
57
+ setInternalValue(newValue);
58
+ onChange?.(newValue);
59
+ }
60
+ };
61
+
62
+ useEffect(() => {
63
+ if (value !== undefined) {
64
+ setInternalValue(value);
65
+ }
66
+ }, [value]);
67
+
68
+ return (
69
+ <div
70
+ data-slot="input-spin"
71
+ className={cn(
72
+ "h-9 w-fit flex justify-between items-center p-0.5 rounded-md border border-input bg-transparent text-sm transition-colors",
73
+ disabled && "cursor-not-allowed opacity-50",
74
+ className,
75
+ )}
76
+ >
77
+ <Button
78
+ type="button"
79
+ variant={buttonVariant}
80
+ size="icon"
81
+ className="h-full w-auto aspect-square"
82
+ onClick={handleDecrement}
83
+ disabled={disabled || internalValue <= min}
84
+ aria-label="Decrease value"
85
+ >
86
+ <Minus className="h-4 w-4" />
87
+ </Button>
88
+ <input
89
+ type="number"
90
+ value={internalValue}
91
+ className={cn(
92
+ "h-full w-10 border-0 bg-transparent text-sm text-center focus:outline-hidden focus:ring-0",
93
+ "[-moz-appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none",
94
+ )}
95
+ onChange={handleOnChange}
96
+ disabled={disabled}
97
+ min={min}
98
+ max={max}
99
+ step={step}
100
+ readOnly
101
+ {...props}
102
+ />
103
+ <Button
104
+ type="button"
105
+ variant={buttonVariant}
106
+ size="icon"
107
+ className="h-full w-auto aspect-square"
108
+ onClick={handleIncrement}
109
+ disabled={disabled || internalValue >= max}
110
+ aria-label="Increase value"
111
+ >
112
+ <Plus className="h-4 w-4" />
113
+ </Button>
114
+ </div>
115
+ );
116
+ }
@@ -0,0 +1,219 @@
1
+ "use client";
2
+
3
+ import { useEffect, useImperativeHandle, useRef, useState } from "react";
4
+ import { X } from "lucide-react";
5
+
6
+ import type { ComponentProps, KeyboardEvent } from "react";
7
+
8
+ import { cn } from "../../utils";
9
+
10
+ import { Command, CommandEmpty, CommandItem, CommandList } from "./command";
11
+ import { Popover, PopoverContent, PopoverTrigger } from "../primitives/client";
12
+ import { Button } from "../primitives";
13
+ import { ScrollArea } from "../primitives/client";
14
+
15
+ export interface InputTagsProps extends ComponentProps<"input"> {
16
+ placeholder?: string;
17
+ tags: string[];
18
+ onTagsChange: (tags: string[]) => void;
19
+ className?: string;
20
+ }
21
+
22
+ export function InputTags({
23
+ placeholder,
24
+ tags,
25
+ onTagsChange,
26
+ className,
27
+ ...props
28
+ }: InputTagsProps) {
29
+ const [inputValue, setInputValue] = useState("");
30
+ const inputRef = useRef<HTMLInputElement>(null);
31
+
32
+ // Forward the ref to the internal ref
33
+ useImperativeHandle(props.ref, () => inputRef.current!);
34
+
35
+ const addTag = (tag: string) => {
36
+ const trimmedTag = tag.trim();
37
+ if (trimmedTag && !tags.includes(trimmedTag)) {
38
+ onTagsChange([...tags, trimmedTag]);
39
+ }
40
+ setInputValue("");
41
+ };
42
+
43
+ const removeTag = (indexToRemove: number) => {
44
+ onTagsChange(tags.filter((_, index) => index !== indexToRemove));
45
+ };
46
+
47
+ const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
48
+ if (e.key === "Enter") {
49
+ e.preventDefault();
50
+ addTag(inputValue);
51
+ } else if (e.key === "Backspace" && !inputValue && tags.length > 0) {
52
+ removeTag(tags.length - 1);
53
+ }
54
+ };
55
+
56
+ return (
57
+ <div
58
+ data-slot="input-tags"
59
+ className={cn(
60
+ "min-h-9 w-full flex flex-wrap gap-2 rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
61
+ className,
62
+ )}
63
+ onClick={() => inputRef.current?.focus()}
64
+ >
65
+ {tags.map((tag, index) => (
66
+ <span
67
+ key={index}
68
+ className="inline-flex items-center rounded-full bg-secondary px-3 py-0.5 text-sm font-medium text-secondary-foreground"
69
+ >
70
+ {tag}
71
+ <Button
72
+ variant="ghost"
73
+ onClick={(e) => {
74
+ e.preventDefault();
75
+ removeTag(index);
76
+ }}
77
+ className="size-auto p-0.5 ms-0.5 -me-1 rounded-full hover:bg-secondary-foreground/20"
78
+ aria-label="Remove"
79
+ >
80
+ <X className="h-3 w-3" />
81
+ </Button>
82
+ </span>
83
+ ))}
84
+ <input
85
+ ref={inputRef}
86
+ type="text"
87
+ className="w-0 flex-1 bg-transparent outline-hidden placeholder:text-muted-foreground"
88
+ value={inputValue}
89
+ onChange={(e) => setInputValue(e.target.value)}
90
+ onKeyDown={handleKeyDown}
91
+ onBlur={() => addTag(inputValue)}
92
+ placeholder={tags.length === 0 ? placeholder : ""}
93
+ {...props}
94
+ />
95
+ </div>
96
+ );
97
+ }
98
+
99
+ interface InputTagsWithSuggestionsProps extends ComponentProps<"input"> {
100
+ placeholder?: string;
101
+ tags: string[];
102
+ suggestions: string[];
103
+ onTagsChange: (tags: string[]) => void;
104
+ className?: string;
105
+ }
106
+
107
+ export function InputTagsWithSuggestions({
108
+ placeholder,
109
+ tags,
110
+ suggestions,
111
+ onTagsChange,
112
+ className,
113
+ ...props
114
+ }: InputTagsWithSuggestionsProps) {
115
+ const [inputValue, setInputValue] = useState("");
116
+ const [open, setOpen] = useState(false);
117
+ const containerRef = useRef<HTMLDivElement>(null);
118
+ const [popoverWidth, setPopoverWidth] = useState<number | undefined>(
119
+ undefined,
120
+ );
121
+
122
+ useEffect(() => {
123
+ if (containerRef.current) {
124
+ setPopoverWidth(containerRef.current.offsetWidth);
125
+ }
126
+ }, [open]);
127
+
128
+ const filteredSuggestions = suggestions.filter(
129
+ (suggestion) =>
130
+ suggestion.toLowerCase().includes(inputValue.toLowerCase()) &&
131
+ !tags.includes(suggestion),
132
+ );
133
+
134
+ const addTag = (tag: string) => {
135
+ const trimmedTag = tag.trim();
136
+ if (trimmedTag && !tags.includes(trimmedTag)) {
137
+ onTagsChange([...tags, trimmedTag]);
138
+ }
139
+ setInputValue("");
140
+ };
141
+
142
+ const removeTag = (indexToRemove: number) => {
143
+ onTagsChange(tags.filter((_, index) => index !== indexToRemove));
144
+ };
145
+
146
+ const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
147
+ if (e.key === "Enter" && !open) {
148
+ e.preventDefault();
149
+ addTag(inputValue);
150
+ } else if (e.key === "Backspace" && !inputValue && tags.length > 0) {
151
+ removeTag(tags.length - 1);
152
+ }
153
+ };
154
+
155
+ return (
156
+ <Popover modal open={open} onOpenChange={setOpen}>
157
+ <PopoverTrigger asChild>
158
+ <div
159
+ data-slot="input-tags-with-suggestions"
160
+ ref={containerRef}
161
+ className={cn(
162
+ "min-h-9 w-full flex flex-wrap gap-2 rounded-md border border-input bg-background px-3 py-1 text-sm ring-offset-background focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
163
+ className,
164
+ )}
165
+ >
166
+ {tags.map((tag, index) => (
167
+ <span
168
+ key={index}
169
+ className="inline-flex items-center rounded-full bg-secondary px-3 py-0.5 text-sm font-medium text-secondary-foreground"
170
+ >
171
+ {tag}
172
+ <Button
173
+ variant="ghost"
174
+ onClick={(e) => {
175
+ e.preventDefault();
176
+ removeTag(index);
177
+ }}
178
+ className="size-auto p-0.5 ms-0.5 -me-1 rounded-full hover:bg-secondary-foreground/20"
179
+ aria-label="Remove"
180
+ >
181
+ <X className="h-3 w-3" />
182
+ </Button>
183
+ </span>
184
+ ))}
185
+ <input
186
+ ref={props.ref}
187
+ type="text"
188
+ value={inputValue}
189
+ onChange={(e) => setInputValue(e.target.value)}
190
+ onKeyDown={handleKeyDown}
191
+ className="w-0 flex-1 bg-transparent outline-hidden placeholder:text-muted-foreground"
192
+ placeholder={tags.length === 0 ? placeholder : ""}
193
+ {...props}
194
+ />
195
+ </div>
196
+ </PopoverTrigger>
197
+ <PopoverContent
198
+ style={{ width: popoverWidth ? `${popoverWidth}px` : "auto" }}
199
+ onOpenAutoFocus={(e) => e.preventDefault()}
200
+ >
201
+ <ScrollArea className="flex flex-col max-h-[300px]">
202
+ <Command>
203
+ <CommandList className="max-h-full">
204
+ <CommandEmpty>No results found.</CommandEmpty>
205
+ {filteredSuggestions.map((suggestion) => (
206
+ <CommandItem
207
+ key={suggestion}
208
+ onSelect={() => addTag(suggestion)}
209
+ >
210
+ {suggestion}
211
+ </CommandItem>
212
+ ))}
213
+ </CommandList>
214
+ </Command>
215
+ </ScrollArea>
216
+ </PopoverContent>
217
+ </Popover>
218
+ );
219
+ }
@@ -0,0 +1,42 @@
1
+ "use client";
2
+
3
+ import { useState } from "react";
4
+
5
+ import type { ChangeEvent, ComponentProps } from "react";
6
+
7
+ import { cn } from "../../utils";
8
+
9
+ import { Input } from "../primitives";
10
+
11
+ export interface InputTimeProps extends ComponentProps<"input"> {
12
+ onValueChange?: (value?: string) => void;
13
+ }
14
+
15
+ export function InputTime({
16
+ className,
17
+ onValueChange,
18
+ ...props
19
+ }: InputTimeProps) {
20
+ const [isEmpty, setIsEmpty] = useState(!props.defaultValue && !props.value);
21
+
22
+ const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
23
+ const value = e.target.value;
24
+
25
+ setIsEmpty(!value);
26
+ onValueChange?.(value);
27
+ };
28
+
29
+ return (
30
+ <Input
31
+ data-slot="input-time"
32
+ className={cn(
33
+ "block [&::-webkit-calendar-picker-indicator]:hidden rtl:text-right",
34
+ isEmpty && "text-muted-foreground",
35
+ className,
36
+ )}
37
+ {...props}
38
+ type="time"
39
+ onChange={handleChange}
40
+ />
41
+ );
42
+ }