@jmruthers/pace-core 0.5.184 → 0.5.185

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 (306) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +60 -1
  3. package/core-usage-manifest.json +312 -0
  4. package/dist/{DataTable-QAB34V6K.js → DataTable-IX2NBUTP.js} +6 -6
  5. package/dist/{DataTable-Bz8ffqyA.d.ts → DataTable-Z9NLVJh0.d.ts} +1 -1
  6. package/dist/{index-Bl--n7-T.d.ts → PublicPageProvider-BABf6JCh.d.ts} +21 -10
  7. package/dist/{UnifiedAuthProvider-7F6T4B6K.js → UnifiedAuthProvider-A4BCQRJY.js} +4 -2
  8. package/dist/{UnifiedAuthProvider-F86d7dSi.d.ts → UnifiedAuthProvider-BG0AL5eE.d.ts} +2 -1
  9. package/dist/{api-ROMBCNKU.js → api-BMFCXVQX.js} +2 -2
  10. package/dist/{chunk-RA3JUFMW.js → chunk-445GEP27.js} +154 -4
  11. package/dist/{chunk-RA3JUFMW.js.map → chunk-445GEP27.js.map} +1 -1
  12. package/dist/{chunk-CSOFYHAG.js → chunk-AISXLWGZ.js} +374 -60
  13. package/dist/chunk-AISXLWGZ.js.map +1 -0
  14. package/dist/{chunk-FUEYYMX5.js → chunk-FXFJRTKI.js} +24 -3
  15. package/dist/chunk-FXFJRTKI.js.map +1 -0
  16. package/dist/{chunk-NQPMQGS2.js → chunk-HC67NW5K.js} +379 -359
  17. package/dist/chunk-HC67NW5K.js.map +1 -0
  18. package/dist/chunk-HESYZWZW.js +388 -0
  19. package/dist/chunk-HESYZWZW.js.map +1 -0
  20. package/dist/{chunk-QUVSNGIP.js → chunk-HGPQUCBC.js} +34 -9
  21. package/dist/{chunk-QUVSNGIP.js.map → chunk-HGPQUCBC.js.map} +1 -1
  22. package/dist/{chunk-UHNYIBXL.js → chunk-IXSNYUCT.js} +1 -1
  23. package/dist/chunk-IXSNYUCT.js.map +1 -0
  24. package/dist/{chunk-MI7HBHN3.js → chunk-MX3EIJGQ.js} +4 -3
  25. package/dist/{chunk-MI7HBHN3.js.map → chunk-MX3EIJGQ.js.map} +1 -1
  26. package/dist/{chunk-PWAHJW4G.js → chunk-OKI34GZD.js} +86 -33
  27. package/dist/chunk-OKI34GZD.js.map +1 -0
  28. package/dist/{chunk-W22JP75J.js → chunk-STTZQK2I.js} +3 -3
  29. package/dist/chunk-THRPYOFK.js +215 -0
  30. package/dist/chunk-THRPYOFK.js.map +1 -0
  31. package/dist/{chunk-M7W4CP3M.js → chunk-U6WNSFX5.js} +2 -1
  32. package/dist/chunk-U6WNSFX5.js.map +1 -0
  33. package/dist/{chunk-QCDXODCA.js → chunk-XAUHJD3L.js} +2 -2
  34. package/dist/components.d.ts +182 -6
  35. package/dist/components.js +157 -11
  36. package/dist/components.js.map +1 -1
  37. package/dist/eslint-rules/pace-core-compliance.cjs +406 -0
  38. package/dist/{file-reference-D06mEEWW.d.ts → file-reference-BjR39ktt.d.ts} +7 -1
  39. package/dist/hooks.d.ts +7 -14
  40. package/dist/hooks.js +10 -22
  41. package/dist/hooks.js.map +1 -1
  42. package/dist/index.d.ts +11 -11
  43. package/dist/index.js +79 -16
  44. package/dist/index.js.map +1 -1
  45. package/dist/providers.d.ts +1 -1
  46. package/dist/providers.js +3 -1
  47. package/dist/rbac/index.d.ts +205 -14
  48. package/dist/rbac/index.js +28 -6
  49. package/dist/timezone-_pgH8qrY.d.ts +530 -0
  50. package/dist/{types-_x1f4QBF.d.ts → types-DUyCRSTj.d.ts} +1 -1
  51. package/dist/types.d.ts +1 -1
  52. package/dist/types.js +1 -1
  53. package/dist/{usePublicRouteParams-JJczomYq.d.ts → usePublicRouteParams-CvnC3d-e.d.ts} +113 -2
  54. package/dist/utils.d.ts +109 -151
  55. package/dist/utils.js +128 -138
  56. package/dist/utils.js.map +1 -1
  57. package/docs/api/README.md +60 -1
  58. package/docs/api/classes/ColumnFactory.md +1 -1
  59. package/docs/api/classes/ErrorBoundary.md +1 -1
  60. package/docs/api/classes/InvalidScopeError.md +1 -1
  61. package/docs/api/classes/Logger.md +178 -0
  62. package/docs/api/classes/MissingUserContextError.md +1 -1
  63. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  64. package/docs/api/classes/PermissionDeniedError.md +1 -1
  65. package/docs/api/classes/RBACAuditManager.md +2 -2
  66. package/docs/api/classes/RBACCache.md +1 -1
  67. package/docs/api/classes/RBACEngine.md +2 -2
  68. package/docs/api/classes/RBACError.md +1 -1
  69. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  70. package/docs/api/classes/SecureSupabaseClient.md +5 -5
  71. package/docs/api/classes/StorageUtils.md +1 -1
  72. package/docs/api/enums/FileCategory.md +1 -1
  73. package/docs/api/enums/LogLevel.md +54 -0
  74. package/docs/api/enums/RBACErrorCode.md +1 -1
  75. package/docs/api/enums/RPCFunction.md +1 -1
  76. package/docs/api/interfaces/AggregateConfig.md +1 -1
  77. package/docs/api/interfaces/BadgeProps.md +1 -1
  78. package/docs/api/interfaces/ButtonProps.md +1 -1
  79. package/docs/api/interfaces/CalendarProps.md +18 -2
  80. package/docs/api/interfaces/CardProps.md +1 -1
  81. package/docs/api/interfaces/ColorPalette.md +1 -1
  82. package/docs/api/interfaces/ColorShade.md +1 -1
  83. package/docs/api/interfaces/ComplianceResult.md +30 -0
  84. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  85. package/docs/api/interfaces/DataRecord.md +1 -1
  86. package/docs/api/interfaces/DataTableAction.md +1 -1
  87. package/docs/api/interfaces/DataTableColumn.md +1 -1
  88. package/docs/api/interfaces/DataTableProps.md +1 -1
  89. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  90. package/docs/api/interfaces/DatabaseComplianceResult.md +85 -0
  91. package/docs/api/interfaces/DatabaseIssue.md +41 -0
  92. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  93. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  94. package/docs/api/interfaces/EventAppRoleData.md +6 -6
  95. package/docs/api/interfaces/ExportColumn.md +1 -1
  96. package/docs/api/interfaces/ExportOptions.md +1 -1
  97. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  98. package/docs/api/interfaces/FileMetadata.md +1 -1
  99. package/docs/api/interfaces/FileReference.md +1 -1
  100. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  101. package/docs/api/interfaces/FileUploadOptions.md +24 -8
  102. package/docs/api/interfaces/FileUploadProps.md +24 -13
  103. package/docs/api/interfaces/FooterProps.md +1 -1
  104. package/docs/api/interfaces/FormFieldProps.md +1 -1
  105. package/docs/api/interfaces/FormProps.md +1 -1
  106. package/docs/api/interfaces/GrantEventAppRoleParams.md +9 -9
  107. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  108. package/docs/api/interfaces/InputProps.md +1 -1
  109. package/docs/api/interfaces/LabelProps.md +1 -1
  110. package/docs/api/interfaces/LoggerConfig.md +62 -0
  111. package/docs/api/interfaces/LoginFormProps.md +1 -1
  112. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  113. package/docs/api/interfaces/NavigationContextType.md +1 -1
  114. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  115. package/docs/api/interfaces/NavigationItem.md +1 -1
  116. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  117. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  118. package/docs/api/interfaces/Organisation.md +1 -1
  119. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  120. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  121. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  122. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  123. package/docs/api/interfaces/PaceAppLayoutProps.md +36 -23
  124. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  125. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  126. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  127. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  128. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  129. package/docs/api/interfaces/PaletteData.md +1 -1
  130. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  131. package/docs/api/interfaces/ProgressProps.md +1 -1
  132. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  133. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  134. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  135. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  136. package/docs/api/interfaces/QuickFix.md +52 -0
  137. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  138. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  139. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  140. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  141. package/docs/api/interfaces/RBACConfig.md +4 -4
  142. package/docs/api/interfaces/RBACContext.md +1 -1
  143. package/docs/api/interfaces/RBACLogger.md +1 -1
  144. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  145. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  146. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  147. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  148. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  149. package/docs/api/interfaces/RBACResult.md +1 -1
  150. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  151. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  152. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  153. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  154. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  155. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  156. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  157. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  158. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  159. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  160. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  161. package/docs/api/interfaces/RevokeEventAppRoleParams.md +7 -7
  162. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  163. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  164. package/docs/api/interfaces/RoleManagementResult.md +5 -5
  165. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  166. package/docs/api/interfaces/RouteConfig.md +1 -1
  167. package/docs/api/interfaces/RuntimeComplianceResult.md +55 -0
  168. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  169. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  170. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  171. package/docs/api/interfaces/SetupIssue.md +41 -0
  172. package/docs/api/interfaces/StorageConfig.md +1 -1
  173. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  174. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  175. package/docs/api/interfaces/StorageListOptions.md +1 -1
  176. package/docs/api/interfaces/StorageListResult.md +1 -1
  177. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  178. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  179. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  180. package/docs/api/interfaces/StyleImport.md +1 -1
  181. package/docs/api/interfaces/SwitchProps.md +1 -1
  182. package/docs/api/interfaces/TabsContentProps.md +1 -1
  183. package/docs/api/interfaces/TabsListProps.md +1 -1
  184. package/docs/api/interfaces/TabsProps.md +1 -1
  185. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  186. package/docs/api/interfaces/TextareaProps.md +1 -1
  187. package/docs/api/interfaces/ToastActionElement.md +1 -1
  188. package/docs/api/interfaces/ToastProps.md +1 -1
  189. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  190. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  191. package/docs/api/interfaces/UseFormDialogOptions.md +62 -0
  192. package/docs/api/interfaces/UseFormDialogReturn.md +117 -0
  193. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  194. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  195. package/docs/api/interfaces/UsePublicEventLogoOptions.md +2 -2
  196. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  197. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  198. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  199. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +2 -2
  200. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  201. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  202. package/docs/api/interfaces/UseResolvedScopeOptions.md +2 -2
  203. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  204. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  205. package/docs/api/interfaces/UserEventAccess.md +1 -1
  206. package/docs/api/interfaces/UserMenuProps.md +1 -1
  207. package/docs/api/interfaces/UserProfile.md +1 -1
  208. package/docs/api/modules.md +738 -42
  209. package/docs/api-reference/hooks.md +111 -0
  210. package/docs/api-reference/rpc-functions.md +1 -1
  211. package/docs/api-reference/utilities.md +184 -0
  212. package/docs/getting-started/installation-guide.md +75 -16
  213. package/docs/getting-started/quick-start.md +61 -11
  214. package/docs/implementation-guides/authentication.md +88 -12
  215. package/docs/implementation-guides/file-reference-system.md +2 -1
  216. package/docs/implementation-guides/file-upload-storage.md +21 -0
  217. package/docs/rbac/README.md +1 -0
  218. package/docs/rbac/compliance/compliance-guide.md +544 -0
  219. package/docs/rbac/getting-started.md +158 -33
  220. package/docs/standards/pace-core-compliance.md +432 -0
  221. package/eslint-config-pace-core.cjs +93 -0
  222. package/package.json +15 -3
  223. package/scripts/analyze-bundle.js +232 -0
  224. package/scripts/build-css.js +56 -0
  225. package/scripts/build-docs-incremental.js +1015 -0
  226. package/scripts/check-pace-core-compliance.cjs +2353 -0
  227. package/scripts/generate-docs.js +157 -0
  228. package/scripts/setup-build-cache.js +73 -0
  229. package/scripts/utils/command-runner.js +131 -0
  230. package/scripts/utils/env.js +33 -0
  231. package/scripts/utils/index.js +10 -0
  232. package/scripts/utils/logger.js +88 -0
  233. package/scripts/utils/path-helpers.js +37 -0
  234. package/scripts/validate-formats.js +133 -0
  235. package/scripts/validate-master.js +155 -0
  236. package/scripts/validate-pre-publish.js +140 -0
  237. package/scripts/validate-theme.js +142 -0
  238. package/src/components/Calendar/Calendar.tsx +8 -1
  239. package/src/components/Card/Card.tsx +47 -8
  240. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.test.tsx +314 -0
  241. package/src/components/DatePickerWithTimezone/DatePickerWithTimezone.tsx +126 -0
  242. package/src/components/DatePickerWithTimezone/README.md +135 -0
  243. package/src/components/DatePickerWithTimezone/index.ts +10 -0
  244. package/src/components/DateTimeField/DateTimeField.test.tsx +358 -0
  245. package/src/components/DateTimeField/DateTimeField.tsx +232 -0
  246. package/src/components/DateTimeField/README.md +148 -0
  247. package/src/components/DateTimeField/index.ts +10 -0
  248. package/src/components/FileUpload/FileUpload.tsx +3 -0
  249. package/src/components/Header/Header.test.tsx +47 -18
  250. package/src/components/Header/Header.tsx +24 -6
  251. package/src/components/PaceAppLayout/PaceAppLayout.tsx +29 -20
  252. package/src/components/PaceAppLayout/README.md +9 -0
  253. package/src/components/ProtectedRoute/ProtectedRoute.test.tsx +37 -8
  254. package/src/components/ProtectedRoute/ProtectedRoute.tsx +12 -4
  255. package/src/components/index.ts +8 -0
  256. package/src/eslint-rules/pace-core-compliance.cjs +406 -0
  257. package/src/eslint-rules/pace-core-compliance.js +640 -0
  258. package/src/hooks/__tests__/useFormDialog.test.ts +478 -0
  259. package/src/hooks/index.ts +2 -0
  260. package/src/hooks/useFileReference.test.ts +1 -0
  261. package/src/hooks/useFormDialog.ts +147 -0
  262. package/src/index.ts +27 -0
  263. package/src/providers/services/OrganisationServiceProvider.tsx +6 -5
  264. package/src/providers/services/UnifiedAuthProvider.tsx +24 -3
  265. package/src/rbac/__tests__/scenarios.user-role.test.tsx +3 -0
  266. package/src/rbac/compliance/database-validator.ts +165 -0
  267. package/src/rbac/compliance/index.ts +38 -0
  268. package/src/rbac/compliance/quick-fix-suggestions.ts +209 -0
  269. package/src/rbac/compliance/runtime-compliance.ts +77 -0
  270. package/src/rbac/compliance/setup-validator.ts +131 -0
  271. package/src/rbac/components/PagePermissionGuard.tsx +8 -64
  272. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +35 -21
  273. package/src/rbac/docs/event-based-apps.md +285 -0
  274. package/src/rbac/errors.ts +11 -0
  275. package/src/rbac/hooks/useRoleManagement.ts +292 -12
  276. package/src/rbac/index.ts +30 -0
  277. package/src/services/OrganisationService.ts +4 -0
  278. package/src/types/file-reference.ts +6 -0
  279. package/src/utils/__tests__/timezone.test.ts +345 -0
  280. package/src/utils/file-reference/__tests__/file-reference.test.ts +2 -0
  281. package/src/utils/file-reference/index.ts +1 -0
  282. package/src/utils/formatting/formatDateTimeTimezone.test.ts +167 -0
  283. package/src/utils/formatting/formatting.ts +179 -0
  284. package/src/utils/index.ts +27 -1
  285. package/src/utils/location/index.ts +16 -0
  286. package/src/utils/location/location.test.ts +286 -0
  287. package/src/utils/location/location.ts +175 -0
  288. package/src/utils/timezone/index.ts +17 -0
  289. package/src/utils/timezone/timezone.test.ts +349 -0
  290. package/src/utils/timezone/timezone.ts +281 -0
  291. package/dist/chunk-CSOFYHAG.js.map +0 -1
  292. package/dist/chunk-FUEYYMX5.js.map +0 -1
  293. package/dist/chunk-HKIT6O7W.js +0 -198
  294. package/dist/chunk-HKIT6O7W.js.map +0 -1
  295. package/dist/chunk-KUEN3HFB.js +0 -94
  296. package/dist/chunk-KUEN3HFB.js.map +0 -1
  297. package/dist/chunk-M7W4CP3M.js.map +0 -1
  298. package/dist/chunk-NQPMQGS2.js.map +0 -1
  299. package/dist/chunk-PWAHJW4G.js.map +0 -1
  300. package/dist/chunk-UHNYIBXL.js.map +0 -1
  301. package/dist/formatting-5wETwiGF.d.ts +0 -162
  302. /package/dist/{DataTable-QAB34V6K.js.map → DataTable-IX2NBUTP.js.map} +0 -0
  303. /package/dist/{UnifiedAuthProvider-7F6T4B6K.js.map → UnifiedAuthProvider-A4BCQRJY.js.map} +0 -0
  304. /package/dist/{api-ROMBCNKU.js.map → api-BMFCXVQX.js.map} +0 -0
  305. /package/dist/{chunk-W22JP75J.js.map → chunk-STTZQK2I.js.map} +0 -0
  306. /package/dist/{chunk-QCDXODCA.js.map → chunk-XAUHJD3L.js.map} +0 -0
@@ -83,6 +83,7 @@ describe('ProtectedRoute Component', () => {
83
83
  const defaultAuthState = {
84
84
  isAuthenticated: true,
85
85
  authLoading: false,
86
+ isLoading: false, // Combined loading state used by component
86
87
  };
87
88
 
88
89
  const defaultSessionState = {
@@ -142,7 +143,7 @@ describe('ProtectedRoute Component', () => {
142
143
  expect(screen.queryByTestId('navigate')).not.toBeInTheDocument();
143
144
  });
144
145
 
145
- it('renders outlet when events exist but none selected (allows event selector visibility)', () => {
146
+ it('renders outlet when events exist but none selected (allows event selector visibility)', async () => {
146
147
  const consoleDebugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
147
148
 
148
149
  mockUseEvents.mockReturnValue({
@@ -151,12 +152,25 @@ describe('ProtectedRoute Component', () => {
151
152
  isLoading: false,
152
153
  });
153
154
 
154
- renderWithProviders(<ProtectedRoute />);
155
+ // Wait for logger to be configured
156
+ await import('../../utils/core/logger').then(({ Logger, LogLevel }) => {
157
+ Logger.configure({
158
+ level: LogLevel.DEBUG,
159
+ includeTimestamp: false,
160
+ includeComponent: true,
161
+ });
162
+ });
163
+
164
+ renderWithProviders(<ProtectedRoute requireEvent={true} />);
155
165
 
156
166
  expect(screen.getByTestId('outlet')).toBeInTheDocument();
157
- expect(consoleDebugSpy).toHaveBeenCalledWith(
158
- expect.stringContaining('[DEBUG] [ProtectedRoute] Events available but none selected - allowing render so selector is visible')
159
- );
167
+
168
+ // Wait a bit for the logger to be called (it's called during render)
169
+ await waitFor(() => {
170
+ expect(consoleDebugSpy).toHaveBeenCalledWith(
171
+ expect.stringContaining('[DEBUG] [ProtectedRoute] Events available but none selected - allowing render so selector is visible')
172
+ );
173
+ }, { timeout: 1000 });
160
174
  });
161
175
 
162
176
  it('renders session restoration loader when session is restoring', () => {
@@ -178,6 +192,7 @@ describe('ProtectedRoute Component', () => {
178
192
  mockUseUnifiedAuth.mockReturnValue({
179
193
  isAuthenticated: false,
180
194
  authLoading: true,
195
+ isLoading: true, // Component uses isLoading, not authLoading
181
196
  });
182
197
 
183
198
  renderWithProviders(<ProtectedRoute />);
@@ -193,6 +208,7 @@ describe('ProtectedRoute Component', () => {
193
208
  mockUseUnifiedAuth.mockReturnValue({
194
209
  isAuthenticated: false,
195
210
  authLoading: true,
211
+ isLoading: true, // Component uses isLoading, not authLoading
196
212
  });
197
213
 
198
214
  renderWithProviders(<ProtectedRoute loadingFallback={customLoader} />);
@@ -207,6 +223,7 @@ describe('ProtectedRoute Component', () => {
207
223
  mockUseUnifiedAuth.mockReturnValue({
208
224
  isAuthenticated: false,
209
225
  authLoading: false,
226
+ isLoading: false,
210
227
  });
211
228
 
212
229
  renderWithProviders(<ProtectedRoute />);
@@ -222,6 +239,7 @@ describe('ProtectedRoute Component', () => {
222
239
  mockUseUnifiedAuth.mockReturnValue({
223
240
  isAuthenticated: false,
224
241
  authLoading: false,
242
+ isLoading: false,
225
243
  });
226
244
 
227
245
  renderWithProviders(<ProtectedRoute loginPath="/custom-login" />);
@@ -234,6 +252,7 @@ describe('ProtectedRoute Component', () => {
234
252
  mockUseUnifiedAuth.mockReturnValue({
235
253
  isAuthenticated: false,
236
254
  authLoading: false,
255
+ isLoading: false,
237
256
  });
238
257
 
239
258
  renderWithProviders(<ProtectedRoute />);
@@ -248,6 +267,7 @@ describe('ProtectedRoute Component', () => {
248
267
  mockUseUnifiedAuth.mockReturnValue({
249
268
  isAuthenticated: false,
250
269
  authLoading: false,
270
+ isLoading: false,
251
271
  });
252
272
 
253
273
  mockUseSessionRestoration.mockReturnValue({
@@ -273,6 +293,7 @@ describe('ProtectedRoute Component', () => {
273
293
  mockUseUnifiedAuth.mockReturnValue({
274
294
  isAuthenticated: false,
275
295
  authLoading: false,
296
+ isLoading: false,
276
297
  });
277
298
 
278
299
  mockUseSessionRestoration.mockReturnValue({
@@ -328,6 +349,7 @@ describe('ProtectedRoute Component', () => {
328
349
  mockUseUnifiedAuth.mockReturnValue({
329
350
  isAuthenticated: false,
330
351
  authLoading: true,
352
+ isLoading: true,
331
353
  });
332
354
 
333
355
  mockUseSessionRestoration.mockReturnValue({
@@ -347,6 +369,7 @@ describe('ProtectedRoute Component', () => {
347
369
  mockUseUnifiedAuth.mockReturnValue({
348
370
  isAuthenticated: false,
349
371
  authLoading: true,
372
+ isLoading: true,
350
373
  });
351
374
 
352
375
  mockUseSessionRestoration.mockReturnValue({
@@ -508,6 +531,7 @@ describe('ProtectedRoute Component', () => {
508
531
  mockUseUnifiedAuth.mockReturnValue({
509
532
  isAuthenticated: true,
510
533
  authLoading: false,
534
+ isLoading: false,
511
535
  });
512
536
 
513
537
  mockUseSessionRestoration.mockReturnValue({
@@ -527,6 +551,7 @@ describe('ProtectedRoute Component', () => {
527
551
  mockUseUnifiedAuth.mockReturnValue({
528
552
  isAuthenticated: true,
529
553
  authLoading: false,
554
+ isLoading: false,
530
555
  });
531
556
 
532
557
  mockUseSessionRestoration.mockReturnValue({
@@ -546,6 +571,7 @@ describe('ProtectedRoute Component', () => {
546
571
  mockUseUnifiedAuth.mockReturnValue({
547
572
  isAuthenticated: true,
548
573
  authLoading: true,
574
+ isLoading: true,
549
575
  });
550
576
 
551
577
  mockUseEvents.mockReturnValue({
@@ -576,7 +602,7 @@ describe('ProtectedRoute Component', () => {
576
602
  });
577
603
 
578
604
  describe('Props Configuration', () => {
579
- it('defaults requireEvent to true when not provided', () => {
605
+ it('defaults requireEvent to false when not provided', () => {
580
606
  mockUseEvents.mockReturnValue({
581
607
  selectedEvent: null,
582
608
  events: [],
@@ -585,8 +611,8 @@ describe('ProtectedRoute Component', () => {
585
611
 
586
612
  renderWithProviders(<ProtectedRoute />);
587
613
 
588
- // Should show no events error since requireEvent defaults to true
589
- expect(screen.getByTestId('alert')).toBeInTheDocument();
614
+ // Should render outlet since requireEvent defaults to false
615
+ expect(screen.getByTestId('outlet')).toBeInTheDocument();
590
616
  });
591
617
 
592
618
  it('respects requireEvent prop when set to false', () => {
@@ -607,6 +633,7 @@ describe('ProtectedRoute Component', () => {
607
633
  mockUseUnifiedAuth.mockReturnValue({
608
634
  isAuthenticated: false,
609
635
  authLoading: false,
636
+ isLoading: false,
610
637
  });
611
638
 
612
639
  renderWithProviders(<ProtectedRoute />);
@@ -619,6 +646,7 @@ describe('ProtectedRoute Component', () => {
619
646
  mockUseUnifiedAuth.mockReturnValue({
620
647
  isAuthenticated: false,
621
648
  authLoading: false,
649
+ isLoading: false,
622
650
  });
623
651
 
624
652
  renderWithProviders(<ProtectedRoute loginPath="/auth/login" />);
@@ -633,6 +661,7 @@ describe('ProtectedRoute Component', () => {
633
661
  mockUseUnifiedAuth.mockReturnValue({
634
662
  isAuthenticated: false,
635
663
  authLoading: true,
664
+ isLoading: true,
636
665
  });
637
666
 
638
667
  renderWithProviders(<ProtectedRoute />);
@@ -131,14 +131,21 @@ export interface ProtectedRouteProps {
131
131
  * @returns React element with route protection logic
132
132
  */
133
133
  export function ProtectedRoute({
134
- requireEvent = true,
134
+ requireEvent = false,
135
135
  allowSuperAdminBypass = false,
136
136
  noEventsFallback,
137
137
  loadingFallback,
138
138
  loginPath = '/login'
139
139
  }: ProtectedRouteProps) {
140
- const { isAuthenticated, authLoading } = useUnifiedAuth();
141
- const { selectedEvent, events, isLoading: eventLoading } = useEvents();
140
+ const { isAuthenticated, isLoading } = useUnifiedAuth();
141
+
142
+ // Always call useEvents() - UnifiedAuthProvider always includes EventServiceProvider
143
+ // Only use the values when requireEvent is true
144
+ const eventsContext = useEvents();
145
+ const selectedEvent = requireEvent ? eventsContext.selectedEvent : null;
146
+ const events = requireEvent ? (eventsContext.events || []) : [];
147
+ const eventLoading = requireEvent ? (eventsContext.isLoading || false) : false;
148
+
142
149
  const sessionRestoration = useSessionRestoration();
143
150
 
144
151
  const isRestoringSession = useMemo(() => {
@@ -165,7 +172,8 @@ export function ProtectedRoute({
165
172
  }
166
173
 
167
174
  // Show loading state while auth is being determined (but not organisation/event loading)
168
- if (authLoading && !sessionRestoration.hasTimedOut) {
175
+ // Use isLoading (combined loading state) for consistency with simpler implementations
176
+ if (isLoading && !sessionRestoration.hasTimedOut) {
169
177
  return loadingFallback || (
170
178
  <div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100vh' }}>
171
179
  <LoadingSpinner />
@@ -126,6 +126,14 @@ export type { TabsProps, TabsListProps, TabsTriggerProps, TabsContentProps } fro
126
126
  export { Calendar } from './Calendar';
127
127
  export type { CalendarProps } from './Calendar';
128
128
 
129
+ // DateTimeField exports
130
+ export { DateTimeField } from './DateTimeField';
131
+ export type { DateTimeFieldProps } from './DateTimeField';
132
+
133
+ // DatePickerWithTimezone exports
134
+ export { DatePickerWithTimezone } from './DatePickerWithTimezone';
135
+ export type { DatePickerWithTimezoneProps } from './DatePickerWithTimezone';
136
+
129
137
  // Toast exports
130
138
  export {
131
139
  Toast,
@@ -0,0 +1,406 @@
1
+ /**
2
+ * ESLint Rules for pace-core Compliance Enforcement
3
+ * @package @jmruthers/pace-core
4
+ * @module ESLintRules/pace-core-compliance
5
+ * @since 1.0.0
6
+ *
7
+ * This module provides ESLint rules to enforce pace-core usage patterns
8
+ * and prevent consuming apps from creating local alternatives.
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+
14
+ // Load manifest data
15
+ let manifestData = null;
16
+ try {
17
+ const manifestPath = path.join(__dirname, '../../core-usage-manifest.json');
18
+ if (fs.existsSync(manifestPath)) {
19
+ manifestData = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
20
+ }
21
+ } catch (error) {
22
+ // If manifest can't be loaded, rules will use hardcoded defaults
23
+ console.warn('Warning: Could not load core-usage-manifest.json, using defaults');
24
+ }
25
+
26
+ // Get restricted imports from manifest or use defaults
27
+ const getRestrictedImports = () => {
28
+ if (manifestData && manifestData.restrictedImports) {
29
+ return manifestData.restrictedImports;
30
+ }
31
+ // Fallback defaults
32
+ return [
33
+ { module: '@radix-ui/react-avatar', reason: 'Use Avatar component from pace-core instead' },
34
+ { module: '@radix-ui/react-checkbox', reason: 'Use Checkbox component from pace-core instead' },
35
+ { module: '@radix-ui/react-dialog', reason: 'Use Dialog component from pace-core instead' },
36
+ { module: '@radix-ui/react-label', reason: 'Use Label component from pace-core instead' },
37
+ { module: '@radix-ui/react-progress', reason: 'Use Progress component from pace-core instead' },
38
+ { module: '@radix-ui/react-slot', reason: 'Use Button component from pace-core which handles slot composition' },
39
+ { module: '@radix-ui/react-switch', reason: 'Use Switch component from pace-core instead' },
40
+ { module: '@radix-ui/react-tabs', reason: 'Use Tabs component from pace-core instead' },
41
+ { module: '@radix-ui/react-toast', reason: 'Use Toast component and useToast hook from pace-core instead' },
42
+ { module: '@radix-ui/react-tooltip', reason: 'Use Tooltip component from pace-core instead' },
43
+ { module: 'react-day-picker', reason: 'Use Calendar component from pace-core instead' },
44
+ { module: '@tanstack/react-table', reason: 'Use DataTable component and related hooks from pace-core instead. DataTable wraps and standardizes table functionality' },
45
+ { module: 'react-hook-form', reason: 'Use Form component and useZodForm hook from pace-core instead' },
46
+ { module: 'zod', reason: 'Use validation utilities and schemas from pace-core instead. pace-core provides standardized validation helpers' }
47
+ ];
48
+ };
49
+
50
+ // Get pace-core components from manifest or use defaults
51
+ const getPaceCoreComponents = () => {
52
+ if (manifestData && manifestData.components) {
53
+ return manifestData.components;
54
+ }
55
+ return ['Button', 'Card', 'Dialog', 'Input', 'Form', 'Select', 'Alert', 'Badge', 'Checkbox', 'Switch', 'Textarea', 'Label', 'Table', 'DataTable', 'Toast', 'Tooltip', 'Tabs', 'Calendar', 'Avatar', 'Progress'];
56
+ };
57
+
58
+ // Get pace-core hooks from manifest or use defaults
59
+ const getPaceCoreHooks = () => {
60
+ if (manifestData && manifestData.hooks) {
61
+ return manifestData.hooks;
62
+ }
63
+ return ['useToast', 'useDebounce', 'useUnifiedAuth', 'useEvents', 'useOrganisations', 'useFileReference', 'useStorage', 'useZodForm', 'useRBAC', 'usePermissions'];
64
+ };
65
+
66
+ // Get pace-core utils from manifest or use defaults
67
+ const getPaceCoreUtils = () => {
68
+ if (manifestData && manifestData.utils) {
69
+ return manifestData.utils;
70
+ }
71
+ return ['formatDate', 'formatCurrency', 'formatNumber', 'formatTime', 'formatDateTime', 'cn', 'validateUserInput', 'sanitizeUserInput', 'hasPermission', 'getAppConfig'];
72
+ };
73
+
74
+ module.exports = {
75
+ rules: {
76
+ /**
77
+ * Block direct imports of libraries wrapped by pace-core
78
+ */
79
+ 'no-restricted-imports': {
80
+ meta: {
81
+ type: 'problem',
82
+ docs: {
83
+ description: 'Disallow direct imports of libraries that pace-core wraps',
84
+ category: 'Best Practices',
85
+ recommended: true
86
+ },
87
+ fixable: 'code',
88
+ hasSuggestions: true,
89
+ messages: {
90
+ restrictedImport: '{{message}} Import from {{alternative}} instead.',
91
+ restrictedImportWithReason: '{{message}} {{reason}}'
92
+ }
93
+ },
94
+ create(context) {
95
+ const restrictedImports = getRestrictedImports();
96
+ const restrictedModules = restrictedImports.map(imp => imp.module);
97
+
98
+ // Also catch @radix-ui/* patterns
99
+ const radixPattern = /^@radix-ui\//;
100
+
101
+ return {
102
+ ImportDeclaration(node) {
103
+ const importSource = node.source.value;
104
+
105
+ // Check exact matches
106
+ const restricted = restrictedImports.find(imp => imp.module === importSource);
107
+ if (restricted) {
108
+ context.report({
109
+ node: node.source,
110
+ messageId: 'restrictedImportWithReason',
111
+ data: {
112
+ message: `Direct import of '${importSource}' is not allowed.`,
113
+ reason: restricted.reason
114
+ },
115
+ suggest: [{
116
+ desc: `Use pace-core alternative: ${restricted.reason}`,
117
+ fix(fixer) {
118
+ // Suggest importing from pace-core instead
119
+ const paceCoreAlternative = getPaceCoreAlternative(importSource);
120
+ if (paceCoreAlternative) {
121
+ return fixer.replaceText(
122
+ node.source,
123
+ `'@jmruthers/pace-core${paceCoreAlternative}'`
124
+ );
125
+ }
126
+ return null;
127
+ }
128
+ }]
129
+ });
130
+ return;
131
+ }
132
+
133
+ // Check @radix-ui/* pattern
134
+ if (radixPattern.test(importSource) && !restrictedModules.includes(importSource)) {
135
+ context.report({
136
+ node: node.source,
137
+ messageId: 'restrictedImport',
138
+ data: {
139
+ message: `Direct import of '${importSource}' is not allowed.`,
140
+ alternative: '@jmruthers/pace-core'
141
+ },
142
+ suggest: [{
143
+ desc: 'Use pace-core component instead',
144
+ fix(fixer) {
145
+ return fixer.replaceText(
146
+ node.source,
147
+ "'@jmruthers/pace-core'"
148
+ );
149
+ }
150
+ }]
151
+ });
152
+ }
153
+ }
154
+ };
155
+ }
156
+ },
157
+
158
+ /**
159
+ * Prefer pace-core components over native HTML elements or custom implementations
160
+ */
161
+ 'prefer-pace-core-components': {
162
+ meta: {
163
+ type: 'suggestion',
164
+ docs: {
165
+ description: 'Suggest using pace-core components instead of native HTML elements',
166
+ category: 'Best Practices',
167
+ recommended: true
168
+ },
169
+ hasSuggestions: true,
170
+ messages: {
171
+ preferButton: "Use 'Button' component from '@jmruthers/pace-core' instead of <button>",
172
+ preferInput: "Use 'Input' component from '@jmruthers/pace-core' instead of <input>",
173
+ preferTextarea: "Use 'Textarea' component from '@jmruthers/pace-core' instead of <textarea>",
174
+ preferLabel: "Use 'Label' component from '@jmruthers/pace-core' instead of <label>",
175
+ preferForm: "Use 'Form' component from '@jmruthers/pace-core' instead of custom form implementation"
176
+ }
177
+ },
178
+ create(context) {
179
+ const paceCoreComponents = getPaceCoreComponents();
180
+
181
+ return {
182
+ JSXOpeningElement(node) {
183
+ const elementName = node.name.name;
184
+
185
+ if (!elementName) return;
186
+
187
+ // Check for native HTML elements that have pace-core alternatives
188
+ const nativeToPaceCore = {
189
+ 'button': 'Button',
190
+ 'input': 'Input',
191
+ 'textarea': 'Textarea',
192
+ 'label': 'Label'
193
+ };
194
+
195
+ if (nativeToPaceCore[elementName.toLowerCase()]) {
196
+ const paceCoreComponent = nativeToPaceCore[elementName.toLowerCase()];
197
+ if (paceCoreComponents.includes(paceCoreComponent)) {
198
+ context.report({
199
+ node,
200
+ messageId: `prefer${paceCoreComponent}`,
201
+ suggest: [{
202
+ desc: `Import and use ${paceCoreComponent} from pace-core`,
203
+ fix(fixer) {
204
+ // This is a complex fix, so we'll just report
205
+ return null;
206
+ }
207
+ }]
208
+ });
209
+ }
210
+ }
211
+ }
212
+ };
213
+ }
214
+ },
215
+
216
+ /**
217
+ * Detect custom hooks that duplicate pace-core functionality
218
+ */
219
+ 'prefer-pace-core-hooks': {
220
+ meta: {
221
+ type: 'suggestion',
222
+ docs: {
223
+ description: 'Suggest using pace-core hooks instead of custom implementations',
224
+ category: 'Best Practices',
225
+ recommended: true
226
+ },
227
+ messages: {
228
+ preferPaceCoreHook: "Consider using '{{hook}}' from '@jmruthers/pace-core' instead of custom hook '{{customHook}}'"
229
+ }
230
+ },
231
+ create(context) {
232
+ const paceCoreHooks = getPaceCoreHooks();
233
+ const hookPatterns = {
234
+ 'useToast': ['useToast', 'useNotification', 'useSnackbar'],
235
+ 'useDebounce': ['useDebounce', 'useDebounced'],
236
+ 'useAuth': ['useAuth', 'useAuthentication', 'useUser'],
237
+ 'useFile': ['useFile', 'useFileUpload', 'useFileReference'],
238
+ 'useForm': ['useForm', 'useZodForm'],
239
+ 'useTable': ['useTable', 'useDataTable']
240
+ };
241
+
242
+ return {
243
+ FunctionDeclaration(node) {
244
+ const functionName = node.id?.name;
245
+ if (!functionName || !functionName.startsWith('use')) return;
246
+
247
+ // Check if this looks like a hook that pace-core provides
248
+ for (const [paceCoreHook, patterns] of Object.entries(hookPatterns)) {
249
+ if (paceCoreHooks.includes(paceCoreHook)) {
250
+ for (const pattern of patterns) {
251
+ if (functionName.toLowerCase().includes(pattern.toLowerCase().replace('use', ''))) {
252
+ context.report({
253
+ node: node.id,
254
+ messageId: 'preferPaceCoreHook',
255
+ data: {
256
+ hook: paceCoreHook,
257
+ customHook: functionName
258
+ }
259
+ });
260
+ return;
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
266
+ };
267
+ }
268
+ },
269
+
270
+ /**
271
+ * Detect utility functions that duplicate pace-core functionality
272
+ */
273
+ 'prefer-pace-core-utils': {
274
+ meta: {
275
+ type: 'suggestion',
276
+ docs: {
277
+ description: 'Suggest using pace-core utilities instead of custom implementations',
278
+ category: 'Best Practices',
279
+ recommended: true
280
+ },
281
+ messages: {
282
+ preferPaceCoreUtil: "Consider using '{{util}}' from '@jmruthers/pace-core' instead of custom function '{{customUtil}}'"
283
+ }
284
+ },
285
+ create(context) {
286
+ const paceCoreUtils = getPaceCoreUtils();
287
+ const utilPatterns = {
288
+ 'formatDate': ['formatDate', 'formatDateTime', 'dateFormat'],
289
+ 'formatCurrency': ['formatCurrency', 'formatMoney', 'currencyFormat'],
290
+ 'formatNumber': ['formatNumber', 'numberFormat'],
291
+ 'cn': ['cn', 'classNames', 'clsx', 'mergeClasses'],
292
+ 'validateUserInput': ['validate', 'validateInput', 'validateUser'],
293
+ 'sanitizeUserInput': ['sanitize', 'sanitizeInput', 'sanitizeUser']
294
+ };
295
+
296
+ return {
297
+ FunctionDeclaration(node) {
298
+ const functionName = node.id?.name;
299
+ if (!functionName) return;
300
+
301
+ // Check if this looks like a util that pace-core provides
302
+ for (const [paceCoreUtil, patterns] of Object.entries(utilPatterns)) {
303
+ if (paceCoreUtils.includes(paceCoreUtil)) {
304
+ for (const pattern of patterns) {
305
+ if (functionName.toLowerCase().includes(pattern.toLowerCase())) {
306
+ context.report({
307
+ node: node.id,
308
+ messageId: 'preferPaceCoreUtil',
309
+ data: {
310
+ util: paceCoreUtil,
311
+ customUtil: functionName
312
+ }
313
+ });
314
+ return;
315
+ }
316
+ }
317
+ }
318
+ }
319
+ }
320
+ };
321
+ }
322
+ },
323
+
324
+ /**
325
+ * Detect component files with names matching pace-core components
326
+ */
327
+ 'no-local-component-duplication': {
328
+ meta: {
329
+ type: 'problem',
330
+ docs: {
331
+ description: 'Disallow local components with names matching pace-core components',
332
+ category: 'Best Practices',
333
+ recommended: true
334
+ },
335
+ messages: {
336
+ duplicateComponent: "Component '{{componentName}}' conflicts with pace-core component. Use '@jmruthers/pace-core' instead of creating a local version."
337
+ }
338
+ },
339
+ create(context) {
340
+ const paceCoreComponents = getPaceCoreComponents();
341
+ const filename = context.getFilename();
342
+
343
+ // Only check component files (components/, src/components/, etc.)
344
+ if (!filename.match(/(components|Components)\//)) {
345
+ return {};
346
+ }
347
+
348
+ // Extract component name from filename
349
+ const basename = path.basename(filename, path.extname(filename));
350
+ const componentName = basename.replace(/\.(test|spec)$/, '');
351
+
352
+ return {
353
+ Program(node) {
354
+ // Check if this file exports a component with a name matching pace-core
355
+ if (paceCoreComponents.includes(componentName)) {
356
+ // Check if file exports this component
357
+ const hasExport = node.body.some(stmt => {
358
+ if (stmt.type === 'ExportNamedDeclaration') {
359
+ return stmt.declaration?.id?.name === componentName ||
360
+ stmt.specifiers?.some(spec => spec.exported.name === componentName);
361
+ }
362
+ if (stmt.type === 'ExportDefaultDeclaration') {
363
+ return stmt.declaration?.id?.name === componentName ||
364
+ stmt.declaration?.name === componentName;
365
+ }
366
+ return false;
367
+ });
368
+
369
+ if (hasExport) {
370
+ context.report({
371
+ node,
372
+ messageId: 'duplicateComponent',
373
+ data: {
374
+ componentName
375
+ }
376
+ });
377
+ }
378
+ }
379
+ }
380
+ };
381
+ }
382
+ }
383
+ }
384
+ };
385
+
386
+ // Helper function to get pace-core alternative for restricted imports
387
+ function getPaceCoreAlternative(importSource) {
388
+ const alternatives = {
389
+ '@radix-ui/react-avatar': '/components',
390
+ '@radix-ui/react-checkbox': '/components',
391
+ '@radix-ui/react-dialog': '/components',
392
+ '@radix-ui/react-label': '/components',
393
+ '@radix-ui/react-progress': '/components',
394
+ '@radix-ui/react-switch': '/components',
395
+ '@radix-ui/react-tabs': '/components',
396
+ '@radix-ui/react-toast': '/components',
397
+ '@radix-ui/react-tooltip': '/components',
398
+ 'react-day-picker': '/components',
399
+ '@tanstack/react-table': '/components',
400
+ 'react-hook-form': '/components',
401
+ 'zod': '/utils'
402
+ };
403
+
404
+ return alternatives[importSource] || '';
405
+ }
406
+