@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,262 @@
1
+ "use client";
2
+
3
+ import * as MenubarPrimitive from "@radix-ui/react-menubar";
4
+ import { Check, ChevronRight, Dot } from "lucide-react";
5
+
6
+ import type { ComponentProps } from "react";
7
+
8
+ import { cn } from "../../utils";
9
+
10
+ export function MenubarMenu({
11
+ ...props
12
+ }: ComponentProps<typeof MenubarPrimitive.Menu>) {
13
+ return <MenubarPrimitive.Menu data-slot="menubar-menu" {...props} />;
14
+ }
15
+
16
+ export function MenubarGroup({
17
+ ...props
18
+ }: ComponentProps<typeof MenubarPrimitive.Group>) {
19
+ return <MenubarPrimitive.Group data-slot="menubar-group" {...props} />;
20
+ }
21
+
22
+ export function MenubarPortal({
23
+ ...props
24
+ }: ComponentProps<typeof MenubarPrimitive.Portal>) {
25
+ return <MenubarPrimitive.Portal data-slot="menubar-portal" {...props} />;
26
+ }
27
+
28
+ export function MenubarRadioGroup({
29
+ ...props
30
+ }: ComponentProps<typeof MenubarPrimitive.RadioGroup>) {
31
+ return (
32
+ <MenubarPrimitive.RadioGroup data-slot="menubar-radio-group" {...props} />
33
+ );
34
+ }
35
+
36
+ export function Menubar({
37
+ className,
38
+ ...props
39
+ }: ComponentProps<typeof MenubarPrimitive.Root>) {
40
+ return (
41
+ <MenubarPrimitive.Root
42
+ data-slot="menubar"
43
+ className={cn(
44
+ "flex h-9 items-center gap-x-1 rounded-md border bg-background p-1",
45
+ className,
46
+ )}
47
+ {...props}
48
+ />
49
+ );
50
+ }
51
+
52
+ export function MenubarTrigger({
53
+ className,
54
+ ...props
55
+ }: ComponentProps<typeof MenubarPrimitive.Trigger>) {
56
+ return (
57
+ <MenubarPrimitive.Trigger
58
+ data-slot="menubar-trigger"
59
+ className={cn(
60
+ "flex cursor-pointer select-none items-center rounded-sm px-3 py-1 text-sm font-medium outline-hidden focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground",
61
+ className,
62
+ )}
63
+ {...props}
64
+ />
65
+ );
66
+ }
67
+
68
+ export function MenubarSubTrigger({
69
+ className,
70
+ inset,
71
+ children,
72
+ ...props
73
+ }: ComponentProps<typeof MenubarPrimitive.SubTrigger> & {
74
+ inset?: boolean;
75
+ }) {
76
+ return (
77
+ <MenubarPrimitive.SubTrigger
78
+ data-slot="menubar-sub-trigger"
79
+ data-inset={inset}
80
+ className={cn(
81
+ "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground cursor-pointer flex items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none data-[inset]:ps-8",
82
+ className,
83
+ )}
84
+ {...props}
85
+ >
86
+ {children}
87
+ <ChevronRight className="ms-auto h-4 w-4 rtl:-scale-x-100" />
88
+ </MenubarPrimitive.SubTrigger>
89
+ );
90
+ }
91
+
92
+ export function MenubarSub({
93
+ ...props
94
+ }: ComponentProps<typeof MenubarPrimitive.Sub>) {
95
+ return <MenubarPrimitive.Sub data-slot="menubar-sub" {...props} />;
96
+ }
97
+
98
+ export function MenubarSubContent({
99
+ className,
100
+ ...props
101
+ }: ComponentProps<typeof MenubarPrimitive.SubContent>) {
102
+ return (
103
+ <MenubarPrimitive.SubContent
104
+ data-slot="menubar-sub-content"
105
+ className={cn(
106
+ "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",
107
+ className,
108
+ )}
109
+ {...props}
110
+ />
111
+ );
112
+ }
113
+
114
+ export function MenubarContent({
115
+ className,
116
+ align = "start",
117
+ alignOffset = -4,
118
+ sideOffset = 8,
119
+ ...props
120
+ }: ComponentProps<typeof MenubarPrimitive.Content>) {
121
+ return (
122
+ <MenubarPortal>
123
+ <MenubarPrimitive.Content
124
+ data-slot="menubar-content"
125
+ align={align}
126
+ alignOffset={alignOffset}
127
+ sideOffset={sideOffset}
128
+ className={cn(
129
+ "z-50 min-w-[12rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in 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",
130
+ className,
131
+ )}
132
+ {...props}
133
+ />
134
+ </MenubarPortal>
135
+ );
136
+ }
137
+
138
+ type MenubarItemProps = ComponentProps<typeof MenubarPrimitive.Item> & {
139
+ inset?: boolean;
140
+ variant?: "default" | "destructive";
141
+ };
142
+
143
+ export function MenubarItem({
144
+ className,
145
+ inset,
146
+ variant = "default",
147
+ ...props
148
+ }: MenubarItemProps) {
149
+ return (
150
+ <MenubarPrimitive.Item
151
+ data-slot="menubar-item"
152
+ data-inset={inset}
153
+ data-variant={variant}
154
+ className={cn(
155
+ "relative flex cursor-pointer items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground",
156
+ className,
157
+ )}
158
+ {...props}
159
+ />
160
+ );
161
+ }
162
+
163
+ export function MenubarCheckboxItem({
164
+ className,
165
+ children,
166
+ checked,
167
+ ...props
168
+ }: ComponentProps<typeof MenubarPrimitive.CheckboxItem>) {
169
+ return (
170
+ <MenubarPrimitive.CheckboxItem
171
+ data-slot="menubar-checkbox-item"
172
+ className={cn(
173
+ "relative flex cursor-pointer select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
174
+ className,
175
+ )}
176
+ checked={checked}
177
+ {...props}
178
+ >
179
+ <span className="absolute start-2 flex h-3.5 w-3.5 items-center justify-center">
180
+ <MenubarPrimitive.ItemIndicator>
181
+ <Check className="h-4 w-4" />
182
+ </MenubarPrimitive.ItemIndicator>
183
+ </span>
184
+ {children}
185
+ </MenubarPrimitive.CheckboxItem>
186
+ );
187
+ }
188
+
189
+ export function MenubarRadioItem({
190
+ className,
191
+ children,
192
+ ...props
193
+ }: ComponentProps<typeof MenubarPrimitive.RadioItem>) {
194
+ return (
195
+ <MenubarPrimitive.RadioItem
196
+ data-slot="menubar-radio-item"
197
+ className={cn(
198
+ "relative flex cursor-pointer select-none items-center rounded-sm py-1.5 ps-8 pe-2 text-sm outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
199
+ className,
200
+ )}
201
+ {...props}
202
+ >
203
+ <span className="absolute start-2 flex h-3.5 w-3.5 items-center justify-center">
204
+ <MenubarPrimitive.ItemIndicator>
205
+ <Dot className="h-4 w-4 fill-current" />
206
+ </MenubarPrimitive.ItemIndicator>
207
+ </span>
208
+ {children}
209
+ </MenubarPrimitive.RadioItem>
210
+ );
211
+ }
212
+
213
+ type MenubarLabelProps = ComponentProps<typeof MenubarPrimitive.Label> & {
214
+ inset?: boolean;
215
+ };
216
+
217
+ export function MenubarLabel({
218
+ className,
219
+ inset,
220
+ ...props
221
+ }: MenubarLabelProps) {
222
+ return (
223
+ <MenubarPrimitive.Label
224
+ data-slot="menubar-label"
225
+ className={cn(
226
+ "px-2 py-1.5 text-sm font-semibold",
227
+ inset && "ps-8",
228
+ className,
229
+ )}
230
+ {...props}
231
+ />
232
+ );
233
+ }
234
+
235
+ export function MenubarSeparator({
236
+ className,
237
+ ...props
238
+ }: ComponentProps<typeof MenubarPrimitive.Separator>) {
239
+ return (
240
+ <MenubarPrimitive.Separator
241
+ data-slot="menubar-separator"
242
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
243
+ {...props}
244
+ />
245
+ );
246
+ }
247
+
248
+ export function MenubarShortcut({
249
+ className,
250
+ ...props
251
+ }: ComponentProps<"span">) {
252
+ return (
253
+ <span
254
+ data-slot="menubar-shortcut"
255
+ className={cn(
256
+ "ms-auto text-xs tracking-widest text-muted-foreground",
257
+ className,
258
+ )}
259
+ {...props}
260
+ />
261
+ );
262
+ }
@@ -0,0 +1,157 @@
1
+ import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
2
+ import { cva } from "class-variance-authority";
3
+ import { ChevronDown } from "lucide-react";
4
+
5
+ import type { ComponentProps } from "react";
6
+
7
+ import { cn } from "../../utils";
8
+
9
+ type NavigationMenuProps = ComponentProps<
10
+ typeof NavigationMenuPrimitive.Root
11
+ > & {
12
+ viewport?: boolean;
13
+ };
14
+
15
+ export function NavigationMenu({
16
+ className,
17
+ children,
18
+ viewport = true,
19
+ ...props
20
+ }: NavigationMenuProps) {
21
+ return (
22
+ <NavigationMenuPrimitive.Root
23
+ data-slot="navigation-menu"
24
+ data-viewport={viewport}
25
+ className={cn(
26
+ "group/navigation-menu relative flex max-w-max flex-1 items-center justify-center",
27
+ className,
28
+ )}
29
+ {...props}
30
+ >
31
+ {children}
32
+ {viewport && <NavigationMenuViewport />}
33
+ </NavigationMenuPrimitive.Root>
34
+ );
35
+ }
36
+
37
+ export function NavigationMenuList({
38
+ className,
39
+ ...props
40
+ }: ComponentProps<typeof NavigationMenuPrimitive.List>) {
41
+ return (
42
+ <NavigationMenuPrimitive.List
43
+ data-slot="navigation-menu-list"
44
+ className={cn(
45
+ "group flex flex-1 list-none items-center justify-center gap-1",
46
+ className,
47
+ )}
48
+ {...props}
49
+ />
50
+ );
51
+ }
52
+
53
+ export function NavigationMenuItem({
54
+ className,
55
+ ...props
56
+ }: ComponentProps<typeof NavigationMenuPrimitive.Item>) {
57
+ return (
58
+ <NavigationMenuPrimitive.Item
59
+ data-slot="navigation-menu-item"
60
+ className={cn("relative", className)}
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+
66
+ export const navigationMenuTriggerStyle = cva(
67
+ "group cursor-pointer inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1",
68
+ );
69
+
70
+ export function NavigationMenuTrigger({
71
+ className,
72
+ children,
73
+ ...props
74
+ }: ComponentProps<typeof NavigationMenuPrimitive.Trigger>) {
75
+ return (
76
+ <NavigationMenuPrimitive.Trigger
77
+ data-slot="navigation-menu-trigger"
78
+ className={cn(navigationMenuTriggerStyle(), "group", className)}
79
+ {...props}
80
+ >
81
+ {children}{" "}
82
+ <ChevronDown
83
+ className="relative top-[1px] ms-1 size-3 transition duration-300 group-data-[state=open]:rotate-180"
84
+ aria-hidden="true"
85
+ />
86
+ </NavigationMenuPrimitive.Trigger>
87
+ );
88
+ }
89
+
90
+ export function NavigationMenuContent({
91
+ className,
92
+ ...props
93
+ }: ComponentProps<typeof NavigationMenuPrimitive.Content>) {
94
+ return (
95
+ <NavigationMenuPrimitive.Content
96
+ data-slot="navigation-menu-content"
97
+ className={cn(
98
+ "data-[motion^=from-]:animate-in data-[motion^=to-]:animate-out data-[motion^=from-]:fade-in data-[motion^=to-]:fade-out data-[motion=from-end]:slide-in-from-right-52 data-[motion=from-start]:slide-in-from-left-52 data-[motion=to-end]:slide-out-to-right-52 data-[motion=to-start]:slide-out-to-left-52 top-0 left-0 w-full p-2 pr-2.5 md:absolute md:w-auto",
99
+ "group-data-[viewport=false]/navigation-menu:bg-popover group-data-[viewport=false]/navigation-menu:text-popover-foreground group-data-[viewport=false]/navigation-menu:data-[state=open]:animate-in group-data-[viewport=false]/navigation-menu:data-[state=closed]:animate-out group-data-[viewport=false]/navigation-menu:data-[state=closed]:zoom-out-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:zoom-in-95 group-data-[viewport=false]/navigation-menu:data-[state=open]:fade-in-0 group-data-[viewport=false]/navigation-menu:data-[state=closed]:fade-out-0 group-data-[viewport=false]/navigation-menu:top-full group-data-[viewport=false]/navigation-menu:mt-1.5 group-data-[viewport=false]/navigation-menu:overflow-hidden group-data-[viewport=false]/navigation-menu:rounded-md group-data-[viewport=false]/navigation-menu:border group-data-[viewport=false]/navigation-menu:shadow group-data-[viewport=false]/navigation-menu:duration-200 **:data-[slot=navigation-menu-link]:focus:ring-0 **:data-[slot=navigation-menu-link]:focus:outline-none",
100
+ className,
101
+ )}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+
107
+ export function NavigationMenuLink({
108
+ className,
109
+ ...props
110
+ }: ComponentProps<typeof NavigationMenuPrimitive.Link>) {
111
+ return (
112
+ <NavigationMenuPrimitive.Link
113
+ data-slot="navigation-menu-link"
114
+ className={cn(
115
+ "data-[active=true]:focus:bg-accent data-[active=true]:hover:bg-accent data-[active=true]:bg-accent/50 data-[active=true]:text-accent-foreground hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus-visible:ring-ring/50 [&_svg:not([class*='text-'])]:text-muted-foreground flex flex-col gap-1 rounded-sm p-2 text-sm transition-all outline-none focus-visible:ring-[3px] focus-visible:outline-1 [&_svg:not([class*='size-'])]:size-4",
116
+ className,
117
+ )}
118
+ {...props}
119
+ />
120
+ );
121
+ }
122
+
123
+ export function NavigationMenuViewport({
124
+ className,
125
+ ...props
126
+ }: ComponentProps<typeof NavigationMenuPrimitive.Viewport>) {
127
+ return (
128
+ <div className={cn("absolute start-0 top-full flex justify-center")}>
129
+ <NavigationMenuPrimitive.Viewport
130
+ data-slot="navigation-menu-viewport"
131
+ className={cn(
132
+ "origin-top-center relative mt-1.5 h-[var(--radix-navigation-menu-viewport-height)] w-full overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-90 md:w-[var(--radix-navigation-menu-viewport-width)]",
133
+ className,
134
+ )}
135
+ {...props}
136
+ />
137
+ </div>
138
+ );
139
+ }
140
+
141
+ export function NavigationMenuIndicator({
142
+ className,
143
+ ...props
144
+ }: ComponentProps<typeof NavigationMenuPrimitive.Indicator>) {
145
+ return (
146
+ <NavigationMenuPrimitive.Indicator
147
+ data-slot="navigation-menu-indicator"
148
+ className={cn(
149
+ "top-full z-1 flex h-1.5 items-end justify-center overflow-hidden data-[state=visible]:animate-in data-[state=hidden]:animate-out data-[state=hidden]:fade-out data-[state=visible]:fade-in",
150
+ className,
151
+ )}
152
+ {...props}
153
+ >
154
+ <div className="relative top-[60%] h-2 w-2 rotate-45 rounded-te-sm bg-border shadow-md" />
155
+ </NavigationMenuPrimitive.Indicator>
156
+ );
157
+ }
@@ -0,0 +1,118 @@
1
+ import Link from "next/link";
2
+ import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react";
3
+
4
+ import type { Button } from "./button";
5
+ import type { ComponentProps } from "react";
6
+
7
+ import { cn } from "../../utils";
8
+
9
+ import { buttonVariants } from "./button";
10
+
11
+ export function Pagination({ className, ...props }: ComponentProps<"nav">) {
12
+ return (
13
+ <nav
14
+ data-slot="pagination"
15
+ role="navigation"
16
+ className={cn("mx-auto flex w-full justify-center", className)}
17
+ aria-label="pagination"
18
+ {...props}
19
+ />
20
+ );
21
+ }
22
+
23
+ export function PaginationContent({
24
+ className,
25
+ ...props
26
+ }: ComponentProps<"ul">) {
27
+ return (
28
+ <ul
29
+ data-slot="pagination-content"
30
+ className={cn("flex flex-row items-center gap-1", className)}
31
+ {...props}
32
+ />
33
+ );
34
+ }
35
+
36
+ export function PaginationItem({ ...props }: ComponentProps<"li">) {
37
+ return <li data-slot="pagination-item" {...props} />;
38
+ }
39
+
40
+ export type PaginationLinkProps = {
41
+ isActive?: boolean;
42
+ } & Pick<ComponentProps<typeof Button>, "size"> &
43
+ ComponentProps<typeof Link>;
44
+
45
+ export function PaginationLink({
46
+ className,
47
+ isActive,
48
+ size = "icon",
49
+ href = "",
50
+ ...props
51
+ }: PaginationLinkProps) {
52
+ return (
53
+ <Link
54
+ data-slot="pagination-link"
55
+ className={cn(
56
+ buttonVariants({
57
+ variant: isActive ? "outline" : "ghost",
58
+ size,
59
+ }),
60
+ className,
61
+ )}
62
+ href={href}
63
+ aria-current={isActive ? "page" : undefined}
64
+ {...props}
65
+ />
66
+ );
67
+ }
68
+
69
+ export function PaginationPrevious({
70
+ className,
71
+ ...props
72
+ }: ComponentProps<typeof PaginationLink>) {
73
+ return (
74
+ <PaginationLink
75
+ size="default"
76
+ className={cn("gap-1 ps-2.5", className)}
77
+ aria-label="Go to previous page"
78
+ {...props}
79
+ >
80
+ <ChevronLeft className="h-4 w-4 rtl:-scale-100" />
81
+ <span>Previous</span>
82
+ </PaginationLink>
83
+ );
84
+ }
85
+
86
+ export function PaginationNext({
87
+ className,
88
+ ...props
89
+ }: ComponentProps<typeof PaginationLink>) {
90
+ return (
91
+ <PaginationLink
92
+ size="default"
93
+ className={cn("gap-1 pe-2.5", className)}
94
+ aria-label="Go to next page"
95
+ {...props}
96
+ >
97
+ <span>Next</span>
98
+ <ChevronRight className="h-4 w-4 rtl:-scale-100" />
99
+ </PaginationLink>
100
+ );
101
+ }
102
+
103
+ export function PaginationEllipsis({
104
+ className,
105
+ ...props
106
+ }: ComponentProps<"span">) {
107
+ return (
108
+ <span
109
+ data-slot="pagination-ellipsis"
110
+ className={cn("flex h-9 w-9 items-center justify-center", className)}
111
+ aria-label="More pages"
112
+ aria-hidden
113
+ {...props}
114
+ >
115
+ <MoreHorizontal className="h-4 w-4" />
116
+ </span>
117
+ );
118
+ }
@@ -0,0 +1,56 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import type { ComponentProps } from "react";
5
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
6
+ import { cn } from "../../utils";
7
+
8
+ export function Popover({
9
+ ...props
10
+ }: ComponentProps<typeof PopoverPrimitive.Root>) {
11
+ return <PopoverPrimitive.Root data-slot="popover" {...props} />;
12
+ }
13
+
14
+ export function PopoverTrigger({
15
+ className,
16
+ ...props
17
+ }: ComponentProps<typeof PopoverPrimitive.Trigger>) {
18
+ return (
19
+ <PopoverPrimitive.Trigger
20
+ data-slot="popover-trigger"
21
+ className={cn("cursor-pointer", className)}
22
+ {...props}
23
+ />
24
+ );
25
+ }
26
+
27
+ export function PopoverAnchor({
28
+ ...props
29
+ }: ComponentProps<typeof PopoverPrimitive.Anchor>) {
30
+ return <PopoverPrimitive.Anchor data-slot="popover-anchor" {...props} />;
31
+ }
32
+
33
+ export function PopoverContent({
34
+ className,
35
+ align = "center",
36
+ sideOffset = 4,
37
+ container,
38
+ ...props
39
+ }: ComponentProps<typeof PopoverPrimitive.Content> & {
40
+ container?: HTMLElement;
41
+ }) {
42
+ return (
43
+ <PopoverPrimitive.Portal container={container}>
44
+ <PopoverPrimitive.Content
45
+ data-slot="popover-content"
46
+ align={align}
47
+ sideOffset={sideOffset}
48
+ className={cn(
49
+ "z-50 w-72 rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden 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",
50
+ className,
51
+ )}
52
+ {...props}
53
+ />
54
+ </PopoverPrimitive.Portal>
55
+ );
56
+ }
@@ -0,0 +1,60 @@
1
+ "use client";
2
+
3
+ import { useCallback } from "react";
4
+ import type { LinkProps } from "next/link";
5
+ import Link from "next/link";
6
+
7
+ import { usePrefetch } from "../../hooks";
8
+
9
+ interface PrefetchLinkProps extends Omit<LinkProps, "href"> {
10
+ children: React.ReactNode;
11
+ href: string | LinkProps["href"];
12
+ className?: string;
13
+ crudEntity?: string; // If this is a CRUD link, provide entity name
14
+ }
15
+
16
+ /**
17
+ * Link component with automatic prefetching for CRUD pages
18
+ * Prefetches both the route and the API endpoint when user hovers
19
+ */
20
+ export function PrefetchLink({
21
+ children,
22
+ crudEntity,
23
+ href,
24
+ className,
25
+ ...props
26
+ }: PrefetchLinkProps) {
27
+ const { prefetchRoute, prefetchCrudPage } = usePrefetch();
28
+
29
+ const handleMouseEnter = useCallback(() => {
30
+ if (typeof href === "string") {
31
+ // Always prefetch the route
32
+ prefetchRoute(href);
33
+
34
+ // If this is a CRUD link, also prefetch the API endpoint
35
+ if (crudEntity) {
36
+ // Use convention: /api/[entity]
37
+ prefetchCrudPage(crudEntity, `/api/${crudEntity}`);
38
+ } else {
39
+ // Try to extract entity name from href (handle localized paths like /en/crud/entity)
40
+ const crudMatch = href.match(/\/(?:[a-z]{2}\/)?crud\/([^/]+)/);
41
+ if (crudMatch) {
42
+ const entity = crudMatch[1];
43
+ // Use convention: /api/[entity]
44
+ prefetchCrudPage(entity, `/api/${entity}`);
45
+ }
46
+ }
47
+ }
48
+ }, [href, crudEntity, prefetchRoute, prefetchCrudPage]);
49
+
50
+ return (
51
+ <Link
52
+ href={href}
53
+ className={className}
54
+ onMouseEnter={handleMouseEnter}
55
+ {...props}
56
+ >
57
+ {children as any}
58
+ </Link>
59
+ );
60
+ }