@jmruthers/pace-core 0.5.136 → 0.5.139

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 (292) hide show
  1. package/dist/{DataTable-CYOHOX3O.js → DataTable-JXFCA2BJ.js} +10 -9
  2. package/dist/{EventLogo-801uofbR.d.ts → EventLogo-rFL_kRjk.d.ts} +73 -1
  3. package/dist/{UnifiedAuthProvider-5E5TUNMS.js → UnifiedAuthProvider-XIQQ7LVU.js} +4 -5
  4. package/dist/{chunk-YLKIDTUK.js → chunk-22WKWKRX.js} +4 -4
  5. package/dist/{chunk-TVYPTYOY.js → chunk-4C7EXCAR.js} +60 -24
  6. package/dist/chunk-4C7EXCAR.js.map +1 -0
  7. package/dist/{chunk-NOHEVYVX.js → chunk-5JMOHWDI.js} +417 -319
  8. package/dist/chunk-5JMOHWDI.js.map +1 -0
  9. package/dist/{chunk-FHWWBIHA.js → chunk-6DXZ6V5Q.js} +5 -5
  10. package/dist/{chunk-2TWNJ46Y.js → chunk-6LAAY47Q.js} +2 -2
  11. package/dist/{chunk-444EZN6N.js → chunk-7QCC6MCP.js} +88 -1
  12. package/dist/chunk-7QCC6MCP.js.map +1 -0
  13. package/dist/chunk-BJPBT3CU.js +21 -0
  14. package/dist/chunk-BJPBT3CU.js.map +1 -0
  15. package/dist/{chunk-L6PGMCMD.js → chunk-BOOI7GK2.js} +38 -12
  16. package/dist/chunk-BOOI7GK2.js.map +1 -0
  17. package/dist/{chunk-XARJS7CD.js → chunk-INQLMHPF.js} +2 -2
  18. package/dist/chunk-JISYG63F.js +70 -0
  19. package/dist/chunk-JISYG63F.js.map +1 -0
  20. package/dist/{chunk-SL2YQDR6.js → chunk-MA6EPSGZ.js} +2 -2
  21. package/dist/{chunk-5DPZ5EAT.js → chunk-OWAG3GSU.js} +1 -3
  22. package/dist/{chunk-LTV3XIJJ.js → chunk-T6JN6LH6.js} +4 -4
  23. package/dist/{chunk-HJGGOMQ6.js → chunk-TLT2ZR3L.js} +147 -103
  24. package/dist/chunk-TLT2ZR3L.js.map +1 -0
  25. package/dist/{chunk-4MT5BGGL.js → chunk-YCWDTTUK.js} +4 -6
  26. package/dist/{chunk-4MT5BGGL.js.map → chunk-YCWDTTUK.js.map} +1 -1
  27. package/dist/components.d.ts +1 -1
  28. package/dist/components.js +12 -11
  29. package/dist/components.js.map +1 -1
  30. package/dist/hooks.js +8 -9
  31. package/dist/hooks.js.map +1 -1
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +15 -14
  34. package/dist/index.js.map +1 -1
  35. package/dist/providers.js +3 -4
  36. package/dist/rbac/index.js +8 -9
  37. package/dist/schema-DTDZQe2u.d.ts +28 -0
  38. package/dist/types.d.ts +152 -3
  39. package/dist/types.js +51 -16
  40. package/dist/types.js.map +1 -1
  41. package/dist/utils.d.ts +89 -4
  42. package/dist/utils.js +214 -96
  43. package/dist/utils.js.map +1 -1
  44. package/dist/validation.d.ts +1 -343
  45. package/dist/validation.js +3 -100
  46. package/docs/api/classes/ColumnFactory.md +1 -1
  47. package/docs/api/classes/ErrorBoundary.md +1 -1
  48. package/docs/api/classes/InvalidScopeError.md +1 -1
  49. package/docs/api/classes/MissingUserContextError.md +1 -1
  50. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  51. package/docs/api/classes/PermissionDeniedError.md +1 -1
  52. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  53. package/docs/api/classes/RBACAuditManager.md +1 -1
  54. package/docs/api/classes/RBACCache.md +1 -1
  55. package/docs/api/classes/RBACEngine.md +1 -1
  56. package/docs/api/classes/RBACError.md +1 -1
  57. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  58. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  59. package/docs/api/classes/StorageUtils.md +1 -1
  60. package/docs/api/enums/FileCategory.md +1 -1
  61. package/docs/api/interfaces/AggregateConfig.md +1 -1
  62. package/docs/api/interfaces/BadgeProps.md +27 -0
  63. package/docs/api/interfaces/ButtonProps.md +1 -1
  64. package/docs/api/interfaces/CardProps.md +1 -1
  65. package/docs/api/interfaces/ColorPalette.md +1 -1
  66. package/docs/api/interfaces/ColorShade.md +1 -1
  67. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  68. package/docs/api/interfaces/DataRecord.md +1 -1
  69. package/docs/api/interfaces/DataTableAction.md +1 -1
  70. package/docs/api/interfaces/DataTableColumn.md +1 -1
  71. package/docs/api/interfaces/DataTableProps.md +1 -1
  72. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  73. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  74. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  75. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  76. package/docs/api/interfaces/EventLogoProps.md +1 -1
  77. package/docs/api/interfaces/ExportColumn.md +1 -1
  78. package/docs/api/interfaces/ExportOptions.md +1 -1
  79. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  80. package/docs/api/interfaces/FileMetadata.md +1 -1
  81. package/docs/api/interfaces/FileReference.md +1 -1
  82. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  83. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  84. package/docs/api/interfaces/FileUploadProps.md +1 -1
  85. package/docs/api/interfaces/FooterProps.md +1 -1
  86. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  87. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  88. package/docs/api/interfaces/InputProps.md +1 -1
  89. package/docs/api/interfaces/LabelProps.md +1 -1
  90. package/docs/api/interfaces/LoginFormProps.md +1 -1
  91. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  92. package/docs/api/interfaces/NavigationContextType.md +1 -1
  93. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  94. package/docs/api/interfaces/NavigationItem.md +1 -1
  95. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  97. package/docs/api/interfaces/Organisation.md +1 -1
  98. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  99. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  100. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  101. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  102. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  103. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  104. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  105. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  106. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  107. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  108. package/docs/api/interfaces/PaletteData.md +1 -1
  109. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  110. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  111. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  112. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  113. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  114. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  115. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  116. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  117. package/docs/api/interfaces/RBACConfig.md +1 -1
  118. package/docs/api/interfaces/RBACLogger.md +1 -1
  119. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  120. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  121. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  122. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  123. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  124. package/docs/api/interfaces/RouteConfig.md +1 -1
  125. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  126. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  127. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  128. package/docs/api/interfaces/StorageConfig.md +1 -1
  129. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  130. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  131. package/docs/api/interfaces/StorageListOptions.md +1 -1
  132. package/docs/api/interfaces/StorageListResult.md +1 -1
  133. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  134. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  135. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  136. package/docs/api/interfaces/StyleImport.md +1 -1
  137. package/docs/api/interfaces/SwitchProps.md +1 -1
  138. package/docs/api/interfaces/ToastActionElement.md +1 -1
  139. package/docs/api/interfaces/ToastProps.md +1 -1
  140. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  141. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  142. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  143. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  144. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  145. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  146. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  147. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  148. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  149. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  150. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  151. package/docs/api/interfaces/UserEventAccess.md +1 -1
  152. package/docs/api/interfaces/UserMenuProps.md +1 -1
  153. package/docs/api/interfaces/UserProfile.md +1 -1
  154. package/docs/api/modules.md +84 -15
  155. package/docs/architecture/README.md +0 -1
  156. package/docs/styles/README.md +0 -2
  157. package/examples/RBAC/CompleteRBACExample.tsx +324 -0
  158. package/examples/RBAC/EventBasedApp.tsx +239 -0
  159. package/examples/RBAC/PermissionExample.tsx +151 -0
  160. package/examples/RBAC/index.ts +13 -0
  161. package/examples/public-pages/CorrectPublicPageImplementation.tsx +301 -0
  162. package/examples/public-pages/PublicEventPage.tsx +274 -0
  163. package/examples/public-pages/PublicPageApp.tsx +308 -0
  164. package/examples/public-pages/PublicPageUsageExample.tsx +216 -0
  165. package/examples/public-pages/index.ts +14 -0
  166. package/package.json +1 -10
  167. package/src/__tests__/TEST_STANDARD.md +92 -0
  168. package/src/components/Badge/Badge.test.tsx +314 -0
  169. package/src/components/Badge/Badge.tsx +304 -0
  170. package/src/components/Badge/index.ts +3 -0
  171. package/src/components/DataTable/__tests__/DataTableCore.test-setup.ts +217 -0
  172. package/src/components/DataTable/__tests__/styles.test.ts +1 -1
  173. package/src/components/DataTable/components/ColumnFilter.tsx +8 -4
  174. package/src/components/DataTable/components/DataTableBody.tsx +461 -0
  175. package/src/components/DataTable/components/DraggableColumnHeader.tsx +144 -0
  176. package/src/components/DataTable/components/FilterRow.tsx +9 -3
  177. package/src/components/DataTable/components/PaginationControls.tsx +1 -0
  178. package/src/components/DataTable/components/VirtualizedDataTable.tsx +513 -0
  179. package/src/components/DataTable/components/__tests__/AccessDeniedPage.test.tsx +14 -68
  180. package/src/components/DataTable/components/__tests__/ColumnFilter.test.tsx +62 -0
  181. package/src/components/DataTable/components/__tests__/FilterRow.test.tsx +43 -0
  182. package/src/components/DataTable/core/ActionManager.ts +235 -0
  183. package/src/components/DataTable/core/ColumnManager.ts +205 -0
  184. package/src/components/DataTable/core/DataManager.ts +188 -0
  185. package/src/components/DataTable/core/DataTableContext.tsx +181 -0
  186. package/src/components/DataTable/core/LocalDataAdapter.ts +273 -0
  187. package/src/components/DataTable/core/PluginRegistry.ts +229 -0
  188. package/src/components/DataTable/core/StateManager.ts +311 -0
  189. package/src/components/DataTable/core/interfaces.ts +338 -0
  190. package/src/components/DataTable/styles.ts +27 -6
  191. package/src/components/DataTable/utils/__tests__/columnUtils.test.ts +94 -0
  192. package/src/components/DataTable/utils/columnUtils.ts +40 -0
  193. package/src/components/DataTable/utils/debugTools.ts +609 -0
  194. package/src/components/DataTable/utils/index.ts +1 -0
  195. package/src/components/Dialog/README.md +804 -0
  196. package/src/components/Dialog/utils/__tests__/safeHtml.unit.test.ts +611 -0
  197. package/src/components/Dialog/utils/safeHtml.ts +185 -0
  198. package/src/components/Footer/Footer.test.tsx +1 -1
  199. package/src/components/Form/Form.test.tsx +1 -1
  200. package/src/components/Form/FormErrorSummary.tsx +113 -0
  201. package/src/components/Form/FormFieldset.tsx +127 -0
  202. package/src/components/Form/FormLiveRegion.tsx +198 -0
  203. package/src/components/LoginForm/LoginForm.test.tsx +1 -1
  204. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.performance.test.tsx +76 -10
  205. package/src/components/PaceLoginPage/PaceLoginPage.tsx +1 -1
  206. package/src/components/PasswordReset/PasswordResetForm.test.tsx +597 -0
  207. package/src/components/PasswordReset/PasswordResetForm.tsx +201 -0
  208. package/src/components/PublicLayout/PublicPageDebugger.tsx +104 -0
  209. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +162 -0
  210. package/src/components/PublicLayout/__tests__/PublicPageFooter.test.tsx +1 -1
  211. package/src/components/Select/Select.test.tsx +1 -1
  212. package/src/components/Select/Select.tsx +20 -8
  213. package/src/components/Table/__tests__/Table.test.tsx +1 -1
  214. package/src/components/index.ts +3 -0
  215. package/src/hooks/__tests__/useFileUrl.unit.test.ts +83 -85
  216. package/src/index.ts +4 -0
  217. package/src/rbac/hooks/useCan.test.ts +24 -0
  218. package/src/rbac/hooks/usePermissions.ts +49 -12
  219. package/src/styles/core.css +3 -0
  220. package/src/utils/appConfig.ts +47 -0
  221. package/src/utils/appIdResolver.test.ts +499 -0
  222. package/src/utils/appIdResolver.ts +130 -0
  223. package/src/utils/appNameResolver.simple.test.ts +212 -0
  224. package/src/utils/appNameResolver.test.ts +121 -0
  225. package/src/utils/appNameResolver.ts +191 -0
  226. package/src/utils/audit.ts +127 -0
  227. package/src/utils/auth-utils.ts +96 -0
  228. package/src/utils/bundleAnalysis.ts +129 -0
  229. package/src/utils/cn.ts +7 -0
  230. package/src/utils/debugLogger.ts +67 -0
  231. package/src/utils/deviceFingerprint.ts +215 -0
  232. package/src/utils/dynamicUtils.ts +105 -0
  233. package/src/utils/file-reference.test.ts +788 -0
  234. package/src/utils/file-reference.ts +519 -0
  235. package/src/utils/formatDate.test.ts +237 -0
  236. package/src/utils/formatting.ts +133 -0
  237. package/src/utils/index.ts +7 -0
  238. package/src/utils/lazyLoad.tsx +44 -0
  239. package/src/utils/logger.ts +179 -0
  240. package/src/utils/organisationContext.test.ts +322 -0
  241. package/src/utils/organisationContext.ts +153 -0
  242. package/src/utils/performanceBenchmark.ts +64 -0
  243. package/src/utils/performanceBudgets.ts +110 -0
  244. package/src/utils/permissionTypes.ts +37 -0
  245. package/src/utils/permissionUtils.test.ts +393 -0
  246. package/src/utils/permissionUtils.ts +34 -0
  247. package/src/utils/sanitization.ts +264 -0
  248. package/src/utils/schemaUtils.ts +37 -0
  249. package/src/utils/secureDataAccess.test.ts +711 -0
  250. package/src/utils/secureDataAccess.ts +377 -0
  251. package/src/utils/secureErrors.ts +79 -0
  252. package/src/utils/secureStorage.ts +244 -0
  253. package/src/utils/security.ts +156 -0
  254. package/src/utils/securityMonitor.ts +45 -0
  255. package/src/utils/sessionTracking.ts +126 -0
  256. package/src/utils/validation.ts +111 -0
  257. package/src/utils/validationUtils.ts +120 -0
  258. package/src/validation/index.ts +2 -2
  259. package/dist/chunk-444EZN6N.js.map +0 -1
  260. package/dist/chunk-APIBCTL2.js +0 -670
  261. package/dist/chunk-APIBCTL2.js.map +0 -1
  262. package/dist/chunk-HJGGOMQ6.js.map +0 -1
  263. package/dist/chunk-K2WWTH7O.js +0 -94
  264. package/dist/chunk-K2WWTH7O.js.map +0 -1
  265. package/dist/chunk-L6PGMCMD.js.map +0 -1
  266. package/dist/chunk-LMC26NLJ.js +0 -84
  267. package/dist/chunk-LMC26NLJ.js.map +0 -1
  268. package/dist/chunk-NOHEVYVX.js.map +0 -1
  269. package/dist/chunk-TVYPTYOY.js.map +0 -1
  270. package/dist/validation-8npbysjg.d.ts +0 -177
  271. /package/dist/{DataTable-CYOHOX3O.js.map → DataTable-JXFCA2BJ.js.map} +0 -0
  272. /package/dist/{UnifiedAuthProvider-5E5TUNMS.js.map → UnifiedAuthProvider-XIQQ7LVU.js.map} +0 -0
  273. /package/dist/{chunk-YLKIDTUK.js.map → chunk-22WKWKRX.js.map} +0 -0
  274. /package/dist/{chunk-FHWWBIHA.js.map → chunk-6DXZ6V5Q.js.map} +0 -0
  275. /package/dist/{chunk-2TWNJ46Y.js.map → chunk-6LAAY47Q.js.map} +0 -0
  276. /package/dist/{chunk-XARJS7CD.js.map → chunk-INQLMHPF.js.map} +0 -0
  277. /package/dist/{chunk-SL2YQDR6.js.map → chunk-MA6EPSGZ.js.map} +0 -0
  278. /package/dist/{chunk-5DPZ5EAT.js.map → chunk-OWAG3GSU.js.map} +0 -0
  279. /package/dist/{chunk-LTV3XIJJ.js.map → chunk-T6JN6LH6.js.map} +0 -0
  280. /package/examples/{components → components 2}/DataTable/HierarchicalActionsExample.tsx +0 -0
  281. /package/examples/{components → components 2}/DataTable/HierarchicalExample.tsx +0 -0
  282. /package/examples/{components → components 2}/DataTable/InitialPageSizeExample.tsx +0 -0
  283. /package/examples/{components → components 2}/DataTable/PerformanceExample.tsx +0 -0
  284. /package/examples/{components → components 2}/DataTable/index.ts +0 -0
  285. /package/examples/{components → components 2}/Dialog/BasicHtmlTest.tsx +0 -0
  286. /package/examples/{components → components 2}/Dialog/DebugHtmlExample.tsx +0 -0
  287. /package/examples/{components → components 2}/Dialog/HtmlDialogExample.tsx +0 -0
  288. /package/examples/{components → components 2}/Dialog/ScrollableDialogExample.tsx +0 -0
  289. /package/examples/{components → components 2}/Dialog/SimpleHtmlTest.tsx +0 -0
  290. /package/examples/{components → components 2}/Dialog/SmartDialogExample.tsx +0 -0
  291. /package/examples/{components → components 2}/Dialog/index.ts +0 -0
  292. /package/examples/{components → components 2}/index.ts +0 -0
@@ -0,0 +1,201 @@
1
+ /**
2
+ * @file Password Reset Form Component
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/PasswordReset
5
+ * @since 0.1.0
6
+ *
7
+ * A comprehensive password reset form component that handles email-based password recovery
8
+ * with proper validation, loading states, and success feedback.
9
+ *
10
+ * Features:
11
+ * - Email validation and submission
12
+ * - Loading states with disabled form
13
+ * - Success state with confirmation message
14
+ * - Error handling and display
15
+ * - Resend functionality
16
+ * - Accessibility compliant
17
+ * - Responsive design
18
+ * - Integration with UnifiedAuthProvider
19
+ * - Form validation
20
+ * - Success and error callbacks
21
+ *
22
+ * @example
23
+ * ```tsx
24
+ * // Basic password reset form
25
+ * <PasswordResetForm
26
+ * onSuccess={() => {
27
+ * toast.success('Password reset email sent!');
28
+ * }}
29
+ * onError={(error) => {
30
+ * toast.error(`Failed to send reset email: ${error.message}`);
31
+ * }}
32
+ * />
33
+ *
34
+ * // Password reset form with custom styling
35
+ * <PasswordResetForm
36
+ * className="max-w-md mx-auto p-6 bg-main-50 rounded-lg shadow-md"
37
+ * onSuccess={() => {
38
+ * console.log('Reset email sent successfully');
39
+ * navigate('/check-email');
40
+ * }}
41
+ * onError={(error) => {
42
+ * console.error('Password reset failed:', error);
43
+ * }}
44
+ * />
45
+ *
46
+ * // Password reset form in a modal
47
+ * <Modal isOpen={showResetForm} onClose={() => setShowResetForm(false)}>
48
+ * <ModalContent>
49
+ * <PasswordResetForm
50
+ * onSuccess={() => {
51
+ * setShowResetForm(false);
52
+ * toast.success('Check your email for reset instructions');
53
+ * }}
54
+ * onError={(error) => {
55
+ * toast.error(error.message);
56
+ * }}
57
+ * />
58
+ * </ModalContent>
59
+ * </Modal>
60
+ * ```
61
+ *
62
+ * @accessibility
63
+ * - WCAG 2.1 AA compliant
64
+ * - Proper form labels and associations
65
+ * - Screen reader friendly error messages
66
+ * - Keyboard navigation support
67
+ * - Focus management
68
+ * - High contrast support
69
+ * - Clear error identification
70
+ * - Role="alert" for error announcements
71
+ *
72
+ * @dependencies
73
+ * - React 18+ - Hooks and state
74
+ * - UnifiedAuthProvider - Authentication context
75
+ * - Button component
76
+ * - Input component
77
+ * - Label component
78
+ * - Tailwind CSS - Styling
79
+ */
80
+
81
+ import React, { useState } from 'react';
82
+ import { Button } from '../Button/Button';
83
+ import { Input } from '../Input/Input';
84
+ import { Label } from '../Label';
85
+ import { cn } from '../../utils/cn';
86
+ import { useUnifiedAuth } from '../../providers/UnifiedAuthProvider';
87
+
88
+ export interface PasswordResetFormProps {
89
+ onSuccess?: () => void;
90
+ onError?: (error: Error) => void;
91
+ className?: string;
92
+ }
93
+
94
+ export function PasswordResetForm({
95
+ onSuccess,
96
+ onError,
97
+ className
98
+ }: PasswordResetFormProps) {
99
+ const { resetPassword } = useUnifiedAuth();
100
+ const [email, setEmail] = useState('');
101
+ const [isLoading, setIsLoading] = useState(false);
102
+ const [isSuccess, setIsSuccess] = useState(false);
103
+ const [error, setError] = useState<string | null>(null);
104
+
105
+ const handleSubmit = async (e: React.FormEvent) => {
106
+ e.preventDefault();
107
+
108
+ if (!email.trim()) {
109
+ return;
110
+ }
111
+
112
+ setIsLoading(true);
113
+ setError(null);
114
+
115
+ try {
116
+ const { error: resetError } = await resetPassword(email);
117
+
118
+ if (resetError) {
119
+ const errorObj = new Error(resetError.message || 'Failed to send reset email');
120
+ setError(errorObj.message);
121
+ onError?.(errorObj);
122
+ } else {
123
+ setIsSuccess(true);
124
+ onSuccess?.();
125
+ }
126
+ } catch (err) {
127
+ const errorObj = err instanceof Error ? err : new Error('An unexpected error occurred');
128
+ setError(errorObj.message);
129
+ onError?.(errorObj);
130
+ } finally {
131
+ setIsLoading(false);
132
+ }
133
+ };
134
+
135
+ const handleSendAnother = () => {
136
+ setIsSuccess(false);
137
+ setError(null);
138
+ };
139
+
140
+ if (isSuccess) {
141
+ return (
142
+ <div className={cn('', className)} role="form">
143
+ <div className="space-y-4 text-center">
144
+ <h2>Check your email</h2>
145
+ <p className="text-sec-600">
146
+ We have sent a password reset link to {email}
147
+ </p>
148
+ <Button
149
+ variant="outline"
150
+ onClick={handleSendAnother}
151
+ className="w-full"
152
+ >
153
+ Send another email
154
+ </Button>
155
+ </div>
156
+ </div>
157
+ );
158
+ }
159
+
160
+ return (
161
+ <div className={cn('', className)} role="form">
162
+ <div className="space-y-4">
163
+ <div className="space-y-2">
164
+ <h2>Reset Password</h2>
165
+ <p className="text-sec-600">
166
+ Enter your email address and we'll send you a reset link.
167
+ </p>
168
+ </div>
169
+
170
+ <form onSubmit={handleSubmit} className="space-y-4">
171
+ <div className="space-y-2">
172
+ <Label htmlFor="email">Email Address</Label>
173
+ <Input
174
+ id="email"
175
+ type="email"
176
+ value={email}
177
+ onChange={(e) => setEmail(e.target.value)}
178
+ placeholder="Enter your email"
179
+ required
180
+ disabled={isLoading}
181
+ />
182
+ </div>
183
+
184
+ {error && (
185
+ <div className="text-sm text-destructive" role="alert">
186
+ {error}
187
+ </div>
188
+ )}
189
+
190
+ <Button
191
+ type="submit"
192
+ className="w-full"
193
+ disabled={!email.trim() || isLoading}
194
+ >
195
+ {isLoading ? 'Sending...' : 'Send Reset Link'}
196
+ </Button>
197
+ </form>
198
+ </div>
199
+ </div>
200
+ );
201
+ }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @file Public Page Debugger
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/PublicLayout
5
+ * @since 1.0.0
6
+ *
7
+ * A debugging component that helps identify why authentication context
8
+ * is being triggered in public pages.
9
+ */
10
+
11
+ import React, { useEffect } from 'react';
12
+ import { usePublicPageContext } from './PublicPageProvider';
13
+
14
+ export interface PublicPageDebuggerProps {
15
+ /** Whether to enable debugging */
16
+ enabled?: boolean;
17
+ /** Custom label for this debugger instance */
18
+ label?: string;
19
+ }
20
+
21
+ /**
22
+ * Debugger component that logs context information
23
+ *
24
+ * This component helps identify why authentication context
25
+ * is being triggered in public pages.
26
+ */
27
+ export function PublicPageDebugger({ enabled = true, label = 'PublicPage' }: PublicPageDebuggerProps) {
28
+ useEffect(() => {
29
+ if (!enabled) return;
30
+
31
+ console.log(`[${label}] Component mounted`);
32
+
33
+ // Check if we're in a public page context
34
+ try {
35
+ // This will throw if we're not in PublicPageProvider
36
+ const { isPublicPage } = usePublicPageContext();
37
+ console.log(`[${label}] Public page context detected:`, isPublicPage);
38
+ } catch (error) {
39
+ console.warn(`[${label}] Not in PublicPageProvider context:`, error instanceof Error ? error.message : String(error));
40
+ }
41
+
42
+ // Check for authentication context
43
+ try {
44
+ // This will throw if we're not in UnifiedAuthProvider
45
+ const { isAuthenticated } = require('../../providers/UnifiedAuthProvider').useUnifiedAuth();
46
+ console.warn(`[${label}] AUTHENTICATION CONTEXT DETECTED! This should not happen in public pages.`);
47
+ console.warn(`[${label}] isAuthenticated:`, isAuthenticated);
48
+ } catch (error) {
49
+ console.log(`[${label}] No authentication context detected (good for public pages)`);
50
+ }
51
+
52
+ // Check for organisation context
53
+ try {
54
+ // This will throw if we're not in OrganisationProvider
55
+ const { selectedOrganisation } = require('../../providers/OrganisationProvider').useOrganisations();
56
+ console.warn(`[${label}] ORGANISATION CONTEXT DETECTED! This should not happen in public pages.`);
57
+ console.warn(`[${label}] selectedOrganisation:`, selectedOrganisation);
58
+ } catch (error) {
59
+ console.log(`[${label}] No organisation context detected (good for public pages)`);
60
+ }
61
+
62
+ // Check for event context
63
+ try {
64
+ // This will throw if we're not in EventProvider
65
+ const { events } = require('../../providers/EventProvider').useEvents();
66
+ console.warn(`[${label}] EVENT CONTEXT DETECTED! This should not happen in public pages.`);
67
+ console.warn(`[${label}] events:`, events);
68
+ } catch (error) {
69
+ console.log(`[${label}] No event context detected (good for public pages)`);
70
+ }
71
+
72
+ // Check environment variables
73
+ const supabaseUrl = (import.meta as any).env?.VITE_SUPABASE_URL;
74
+ const supabaseKey = (import.meta as any).env?.VITE_SUPABASE_ANON_KEY;
75
+
76
+ console.log(`[${label}] Environment variables:`, {
77
+ supabaseUrl: supabaseUrl ? 'Present' : 'Missing',
78
+ supabaseKey: supabaseKey ? 'Present' : 'Missing'
79
+ });
80
+
81
+ return () => {
82
+ console.log(`[${label}] Component unmounted`);
83
+ };
84
+ }, [enabled, label]);
85
+
86
+ if (!enabled) return null;
87
+
88
+ return (
89
+ <div style={{
90
+ position: 'fixed',
91
+ top: 0,
92
+ right: 0,
93
+ background: 'rgba(0,0,0,0.8)',
94
+ color: 'white',
95
+ padding: '8px',
96
+ fontSize: '12px',
97
+ zIndex: 9999,
98
+ fontFamily: 'monospace'
99
+ }}>
100
+ <div>Public Page Debugger</div>
101
+ <div>Check console for context analysis</div>
102
+ </div>
103
+ );
104
+ }
@@ -0,0 +1,162 @@
1
+ /**
2
+ * @file Public Page Diagnostic Component
3
+ * @package @jmruthers/pace-core
4
+ * @module Components/PublicLayout
5
+ * @since 1.0.0
6
+ *
7
+ * A diagnostic component that helps identify exactly where authentication
8
+ * context is being triggered in public pages.
9
+ */
10
+
11
+ import React, { useEffect, useState } from 'react';
12
+ import { usePublicPageContext } from './PublicPageProvider';
13
+
14
+ export interface PublicPageDiagnosticProps {
15
+ /** Whether to enable diagnostics */
16
+ enabled?: boolean;
17
+ /** Custom label for this diagnostic instance */
18
+ label?: string;
19
+ }
20
+
21
+ /**
22
+ * Diagnostic component that identifies authentication context issues
23
+ *
24
+ * This component will help you identify exactly where the problem is
25
+ * in your public page implementation.
26
+ */
27
+ export function PublicPageDiagnostic({ enabled = true, label = 'PublicPage' }: PublicPageDiagnosticProps) {
28
+ const [diagnostics, setDiagnostics] = useState<{
29
+ hasPublicPageContext: boolean;
30
+ hasAuthContext: boolean;
31
+ hasOrgContext: boolean;
32
+ hasEventContext: boolean;
33
+ hasEnvironmentVars: boolean;
34
+ routeParams: any;
35
+ }>({
36
+ hasPublicPageContext: false,
37
+ hasAuthContext: false,
38
+ hasOrgContext: false,
39
+ hasEventContext: false,
40
+ hasEnvironmentVars: false,
41
+ routeParams: null
42
+ });
43
+
44
+ useEffect(() => {
45
+ if (!enabled) return;
46
+
47
+ const runDiagnostics = () => {
48
+ const newDiagnostics = {
49
+ hasPublicPageContext: false,
50
+ hasAuthContext: false,
51
+ hasOrgContext: false,
52
+ hasEventContext: false,
53
+ hasEnvironmentVars: false,
54
+ routeParams: null
55
+ };
56
+
57
+ // Check for PublicPageProvider context
58
+ try {
59
+ const { isPublicPage } = usePublicPageContext();
60
+ newDiagnostics.hasPublicPageContext = isPublicPage === true;
61
+ } catch (error) {
62
+ newDiagnostics.hasPublicPageContext = false;
63
+ }
64
+
65
+ // Check for authentication context
66
+ try {
67
+ const { isAuthenticated } = require('../../providers/UnifiedAuthProvider').useUnifiedAuth();
68
+ newDiagnostics.hasAuthContext = true;
69
+ } catch (error) {
70
+ newDiagnostics.hasAuthContext = false;
71
+ }
72
+
73
+ // Check for organisation context
74
+ try {
75
+ const { selectedOrganisation } = require('../../providers/OrganisationProvider').useOrganisations();
76
+ newDiagnostics.hasOrgContext = true;
77
+ } catch (error) {
78
+ newDiagnostics.hasOrgContext = false;
79
+ }
80
+
81
+ // Check for event context
82
+ try {
83
+ const { events } = require('../../providers/EventProvider').useEvents();
84
+ newDiagnostics.hasEventContext = true;
85
+ } catch (error) {
86
+ newDiagnostics.hasEventContext = false;
87
+ }
88
+
89
+ // Check environment variables
90
+ const supabaseUrl = (import.meta as any).env?.VITE_SUPABASE_URL;
91
+ const supabaseKey = (import.meta as any).env?.VITE_SUPABASE_ANON_KEY;
92
+ newDiagnostics.hasEnvironmentVars = !!(supabaseUrl && supabaseKey);
93
+
94
+ // Check route parameters
95
+ try {
96
+ const { useParams } = require('react-router-dom');
97
+ const params = useParams();
98
+ newDiagnostics.routeParams = params;
99
+ } catch (error) {
100
+ newDiagnostics.routeParams = null;
101
+ }
102
+
103
+ setDiagnostics(newDiagnostics);
104
+
105
+ // Log results
106
+ console.group(`🔍 [${label}] Public Page Diagnostics`);
107
+ console.log('✅ Public Page Context:', newDiagnostics.hasPublicPageContext ? 'PRESENT' : 'MISSING');
108
+ console.log('❌ Auth Context:', newDiagnostics.hasAuthContext ? 'PRESENT (BAD!)' : 'ABSENT (GOOD!)');
109
+ console.log('❌ Org Context:', newDiagnostics.hasOrgContext ? 'PRESENT (BAD!)' : 'ABSENT (GOOD!)');
110
+ console.log('❌ Event Context:', newDiagnostics.hasEventContext ? 'PRESENT (BAD!)' : 'ABSENT (GOOD!)');
111
+ console.log('✅ Environment Vars:', newDiagnostics.hasEnvironmentVars ? 'PRESENT' : 'MISSING');
112
+ console.log('✅ Route Params:', newDiagnostics.routeParams);
113
+ console.groupEnd();
114
+
115
+ // Provide specific guidance
116
+ if (newDiagnostics.hasAuthContext || newDiagnostics.hasOrgContext || newDiagnostics.hasEventContext) {
117
+ console.error(`🚨 [${label}] PROBLEM DETECTED: Public page is inside authentication context!`);
118
+ console.error('🔧 SOLUTION: Move public routes outside of UnifiedAuthProvider, OrganisationProvider, and EventProvider');
119
+ console.error('📖 See: packages/core/docs/emergency-public-pages-fix.md');
120
+ } else if (!newDiagnostics.hasPublicPageContext) {
121
+ console.warn(`⚠️ [${label}] WARNING: Not in PublicPageProvider context`);
122
+ console.warn('🔧 SOLUTION: Wrap your public page component in <PublicPageProvider>');
123
+ } else if (!newDiagnostics.hasEnvironmentVars) {
124
+ console.warn(`⚠️ [${label}] WARNING: Missing environment variables`);
125
+ console.warn('🔧 SOLUTION: Set VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY in your environment');
126
+ } else {
127
+ console.log(`✅ [${label}] All diagnostics passed! Public page should work correctly.`);
128
+ }
129
+ };
130
+
131
+ runDiagnostics();
132
+ }, [enabled, label]);
133
+
134
+ if (!enabled) return null;
135
+
136
+ return (
137
+ <div style={{
138
+ position: 'fixed',
139
+ top: 0,
140
+ left: 0,
141
+ background: 'rgba(0,0,0,0.9)',
142
+ color: 'white',
143
+ padding: '12px',
144
+ fontSize: '11px',
145
+ zIndex: 9999,
146
+ fontFamily: 'monospace',
147
+ maxWidth: '300px',
148
+ borderRadius: '0 0 8px 0'
149
+ }}>
150
+ <div style={{ fontWeight: 'bold', marginBottom: '8px' }}>🔍 Public Page Diagnostics</div>
151
+ <div>Public Context: {diagnostics.hasPublicPageContext ? '✅' : '❌'}</div>
152
+ <div>Auth Context: {diagnostics.hasAuthContext ? '❌ BAD' : '✅ GOOD'}</div>
153
+ <div>Org Context: {diagnostics.hasOrgContext ? '❌ BAD' : '✅ GOOD'}</div>
154
+ <div>Event Context: {diagnostics.hasEventContext ? '❌ BAD' : '✅ GOOD'}</div>
155
+ <div>Env Vars: {diagnostics.hasEnvironmentVars ? '✅' : '❌'}</div>
156
+ <div>Route Params: {diagnostics.routeParams ? '✅' : '❌'}</div>
157
+ <div style={{ marginTop: '8px', fontSize: '10px', opacity: 0.8 }}>
158
+ Check console for detailed analysis
159
+ </div>
160
+ </div>
161
+ );
162
+ }
@@ -12,7 +12,7 @@ import { PublicPageFooter } from '../PublicPageFooter';
12
12
  import type { Event } from '../../../types/unified';
13
13
 
14
14
  // Mock the cn utility
15
- vi.mock('../../../utils/cn', () => ({
15
+ vi.mock('../../../utils/core/cn', () => ({
16
16
  cn: vi.fn((...classes) => classes.filter(Boolean).join(' '))
17
17
  }));
18
18
 
@@ -42,7 +42,7 @@ vi.mock('../Button/Button', () => ({
42
42
  }));
43
43
 
44
44
  // Mock cn utility
45
- vi.mock('../../utils/cn', () => ({
45
+ vi.mock('../../utils/core/cn', () => ({
46
46
  cn: (...classes: any[]) => classes.filter(Boolean).join(' '),
47
47
  }));
48
48
 
@@ -33,11 +33,13 @@ export interface SelectContextValue extends SelectState {
33
33
  actions: SelectActions;
34
34
  registerItem?: (value: string, text: string) => void;
35
35
  unregisterItem?: (value: string) => void;
36
+ direction?: 'up' | 'down';
36
37
  }
37
38
 
38
39
  export interface SelectProps extends Omit<React.HTMLAttributes<HTMLFormElement>, 'onChange' | 'onKeyDown' | 'onFocus' | 'onBlur'> {
39
40
  children: React.ReactNode;
40
41
  className?: string;
42
+ direction?: 'up' | 'down';
41
43
  // State props are in UseSelectStateProps (via & intersection)
42
44
  }
43
45
 
@@ -168,6 +170,7 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
168
170
  ({
169
171
  children,
170
172
  className,
173
+ direction = 'down',
171
174
  ...selectProps
172
175
  }, ref) => {
173
176
  const internalRef = React.useRef<HTMLFormElement>(null);
@@ -317,7 +320,8 @@ export const Select = React.forwardRef<HTMLFormElement, SelectProps & UseSelectS
317
320
  actions,
318
321
  registerItem,
319
322
  unregisterItem,
320
- }), [state, actions, registerItem, unregisterItem]);
323
+ direction,
324
+ }), [state, actions, registerItem, unregisterItem, direction]);
321
325
 
322
326
  return (
323
327
  <form
@@ -341,7 +345,8 @@ Select.displayName = "Select";
341
345
 
342
346
  export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerProps>(
343
347
  ({ children, className, variant = "outline", size = "default", asChild = false, ...props }, ref) => {
344
- const { open, disabled, value, actions } = useSelectContext();
348
+ const { open, disabled, value, actions, direction = 'down' } = useSelectContext();
349
+ const opensUpward = direction === 'up';
345
350
 
346
351
  const handleClick = () => {
347
352
  actions.setOpen(!open);
@@ -377,7 +382,8 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
377
382
  className: cn(
378
383
  "!justify-between relative w-full",
379
384
  "[&_svg]:pointer-events-none",
380
- open && "!rounded-b-none !border-b-0",
385
+ open && !opensUpward && "!rounded-b-none !border-b-0",
386
+ open && opensUpward && "!rounded-t-none !border-t-0",
381
387
  className
382
388
  ),
383
389
  style: {
@@ -438,7 +444,8 @@ export const SelectTrigger = React.forwardRef<HTMLButtonElement, SelectTriggerPr
438
444
  className={cn(
439
445
  "!justify-between relative w-full",
440
446
  "[&_svg]:pointer-events-none",
441
- open && "!rounded-b-none !border-b-0",
447
+ open && !opensUpward && "!rounded-b-none !border-b-0",
448
+ open && opensUpward && "!rounded-t-none !border-t-0",
442
449
  className
443
450
  )}
444
451
  style={{
@@ -493,10 +500,10 @@ export const SelectContent = React.forwardRef<HTMLUListElement, SelectContentPro
493
500
  className,
494
501
  searchable = false,
495
502
  searchPlaceholder = "Search...",
496
- maxHeight = "20rem",
503
+ maxHeight = "max(20rem, 50vh)",
497
504
  style
498
505
  }, ref) => {
499
- const { open, actions } = useSelectContext();
506
+ const { open, actions, direction = 'down' } = useSelectContext();
500
507
  const { searchTerm, setSearchTerm, filteredChildren, searchInputRef } = useSelectSearch({
501
508
  children,
502
509
  searchable,
@@ -525,16 +532,21 @@ export const SelectContent = React.forwardRef<HTMLUListElement, SelectContentPro
525
532
  );
526
533
  }
527
534
 
535
+ const opensUpward = direction === 'up';
536
+
528
537
  return (
529
538
  <ul
530
539
  ref={ref}
531
540
  className={cn(
532
- "absolute z-[99999] w-full overflow-y-auto rounded-b-md border border-t-0 border-main-300 bg-main-50 shadow-lg",
541
+ "absolute z-[99999] w-full overflow-y-auto border border-main-300 bg-main-50 shadow-lg",
533
542
  "list-none p-0 m-0",
543
+ opensUpward
544
+ ? "rounded-t-md border-b-0"
545
+ : "rounded-b-md border-t-0",
534
546
  className
535
547
  )}
536
548
  style={{
537
- top: '100%',
549
+ [opensUpward ? 'bottom' : 'top']: '100%',
538
550
  left: 0,
539
551
  right: 0,
540
552
  maxHeight,
@@ -20,7 +20,7 @@ import {
20
20
  } from '../Table';
21
21
 
22
22
  // Mock the cn utility
23
- vi.mock('../../../utils/cn', () => ({
23
+ vi.mock('../../../utils/core/cn', () => ({
24
24
  cn: vi.fn((...classes) => classes.filter(Boolean).join(' '))
25
25
  }));
26
26
 
@@ -49,6 +49,9 @@ export type { LabelProps } from './Label';
49
49
  export { Alert, AlertTitle, AlertDescription } from './Alert';
50
50
  export { Avatar, AvatarImage, AvatarFallback } from './Avatar';
51
51
 
52
+ export { Badge } from './Badge';
53
+ export type { BadgeProps, BadgeVariant } from './Badge';
54
+
52
55
  export { Checkbox } from './Checkbox';
53
56
  export { Switch } from './Switch';
54
57
  export type { SwitchProps } from './Switch';