@jmruthers/pace-core 0.5.87 → 0.5.89

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 (243) hide show
  1. package/dist/{AuthService-Df3IozMG.d.ts → AuthService-DcTI5Ov4.d.ts} +9 -0
  2. package/dist/{DataTable-FA6EUX5M.js → DataTable-PWBMKMOG.js} +7 -7
  3. package/dist/{PublicLoadingSpinner-DecuJBX0.d.ts → PublicLoadingSpinner-BQXD1fbO.d.ts} +160 -130
  4. package/dist/{UnifiedAuthProvider-K2IZAY5F.js → UnifiedAuthProvider-5D3HEQND.js} +4 -4
  5. package/dist/{UnifiedAuthProvider-B391Aqum.d.ts → UnifiedAuthProvider-BVKmQd9u.d.ts} +4 -0
  6. package/dist/auth-DReDSLq9.d.ts +16 -0
  7. package/dist/{chunk-CBSD3BZ3.js → chunk-3RZBKQ5Y.js} +2 -6
  8. package/dist/{chunk-CBSD3BZ3.js.map → chunk-3RZBKQ5Y.js.map} +1 -1
  9. package/dist/{chunk-NTW3KGS4.js → chunk-6UHXQH7P.js} +5 -5
  10. package/dist/{chunk-ZFLOV3OM.js → chunk-7VJDS5QD.js} +401 -16
  11. package/dist/chunk-7VJDS5QD.js.map +1 -0
  12. package/dist/{chunk-YVUZWLQG.js → chunk-AQGF5OG7.js} +3 -3
  13. package/dist/{chunk-CVMVPYAL.js → chunk-BDZUMRBD.js} +3 -5
  14. package/dist/chunk-BDZUMRBD.js.map +1 -0
  15. package/dist/{chunk-KAY3K5TP.js → chunk-BNXBJOGL.js} +4 -4
  16. package/dist/{chunk-S3JKDMD5.js → chunk-CXKMRKRF.js} +4 -4
  17. package/dist/{chunk-5BN3YGNK.js → chunk-DP5X5ORK.js} +217 -27
  18. package/dist/chunk-DP5X5ORK.js.map +1 -0
  19. package/dist/{chunk-RIXPZJUB.js → chunk-KTPG5VCH.js} +2 -2
  20. package/dist/{chunk-2FQEQUJT.js → chunk-KWICIQVK.js} +4 -4
  21. package/dist/{chunk-WUXCWRL6.js → chunk-XJ2HZOBU.js} +6 -1
  22. package/dist/chunk-XJ2HZOBU.js.map +1 -0
  23. package/dist/{chunk-I7O3RSMN.js → chunk-YWAFPVJA.js} +1298 -769
  24. package/dist/chunk-YWAFPVJA.js.map +1 -0
  25. package/dist/{chunk-I2VVV5PQ.js → chunk-YY4YYM3E.js} +2 -2
  26. package/dist/components.d.ts +6 -55
  27. package/dist/components.js +24 -205
  28. package/dist/components.js.map +1 -1
  29. package/dist/{file-reference-9xUOnwyt.d.ts → file-reference-C9isKNPn.d.ts} +67 -2
  30. package/dist/hooks.js +9 -8
  31. package/dist/hooks.js.map +1 -1
  32. package/dist/index.d.ts +152 -26
  33. package/dist/index.js +64 -194
  34. package/dist/index.js.map +1 -1
  35. package/dist/providers.d.ts +5 -3
  36. package/dist/providers.js +3 -3
  37. package/dist/rbac/index.js +8 -8
  38. package/dist/types.d.ts +2 -1
  39. package/dist/types.js +3 -3
  40. package/dist/utils.js +2 -2
  41. package/docs/DOCUMENTATION_AUDIT.md +6 -6
  42. package/docs/DOCUMENTATION_STANDARD.md +137 -0
  43. package/docs/README.md +1 -1
  44. package/docs/api/classes/ColumnFactory.md +1 -1
  45. package/docs/api/classes/ErrorBoundary.md +1 -1
  46. package/docs/api/classes/InvalidScopeError.md +1 -1
  47. package/docs/api/classes/MissingUserContextError.md +1 -1
  48. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  49. package/docs/api/classes/PermissionDeniedError.md +1 -1
  50. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  51. package/docs/api/classes/RBACAuditManager.md +1 -1
  52. package/docs/api/classes/RBACCache.md +1 -1
  53. package/docs/api/classes/RBACEngine.md +1 -1
  54. package/docs/api/classes/RBACError.md +1 -1
  55. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  56. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  57. package/docs/api/classes/StorageUtils.md +83 -40
  58. package/docs/api/enums/FileCategory.md +56 -1
  59. package/docs/api/interfaces/AggregateConfig.md +1 -1
  60. package/docs/api/interfaces/ButtonProps.md +1 -1
  61. package/docs/api/interfaces/CardProps.md +1 -1
  62. package/docs/api/interfaces/ColorPalette.md +1 -1
  63. package/docs/api/interfaces/ColorShade.md +1 -1
  64. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  65. package/docs/api/interfaces/DataRecord.md +1 -1
  66. package/docs/api/interfaces/DataTableAction.md +1 -1
  67. package/docs/api/interfaces/DataTableColumn.md +1 -1
  68. package/docs/api/interfaces/DataTableProps.md +1 -1
  69. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  70. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  71. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  72. package/docs/api/interfaces/EventLogoProps.md +11 -11
  73. package/docs/api/interfaces/FileDisplayProps.md +10 -10
  74. package/docs/api/interfaces/FileMetadata.md +1 -1
  75. package/docs/api/interfaces/FileReference.md +1 -1
  76. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  77. package/docs/api/interfaces/FileUploadOptions.md +8 -8
  78. package/docs/api/interfaces/FileUploadProps.md +137 -42
  79. package/docs/api/interfaces/FooterProps.md +1 -1
  80. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  81. package/docs/api/interfaces/InputProps.md +1 -1
  82. package/docs/api/interfaces/LabelProps.md +1 -1
  83. package/docs/api/interfaces/LoginFormProps.md +1 -1
  84. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  85. package/docs/api/interfaces/NavigationContextType.md +1 -1
  86. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  87. package/docs/api/interfaces/NavigationItem.md +1 -1
  88. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  89. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  90. package/docs/api/interfaces/Organisation.md +1 -1
  91. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  92. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  93. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  94. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  95. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  96. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  97. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  98. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  99. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  100. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  101. package/docs/api/interfaces/PaletteData.md +1 -1
  102. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  103. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  104. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  105. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  106. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  107. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  108. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  109. package/docs/api/interfaces/RBACConfig.md +1 -1
  110. package/docs/api/interfaces/RBACLogger.md +1 -1
  111. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  112. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  113. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  114. package/docs/api/interfaces/RouteConfig.md +1 -1
  115. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  116. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  117. package/docs/api/interfaces/StorageConfig.md +1 -1
  118. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  119. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  120. package/docs/api/interfaces/StorageListOptions.md +1 -1
  121. package/docs/api/interfaces/StorageListResult.md +1 -1
  122. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  123. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  124. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  125. package/docs/api/interfaces/StyleImport.md +1 -1
  126. package/docs/api/interfaces/SwitchProps.md +1 -1
  127. package/docs/api/interfaces/ToastActionElement.md +1 -1
  128. package/docs/api/interfaces/ToastProps.md +1 -1
  129. package/docs/api/interfaces/UnifiedAuthContextType.md +83 -50
  130. package/docs/api/interfaces/UnifiedAuthProviderProps.md +13 -13
  131. package/docs/api/interfaces/UseEventLogoOptions.md +74 -0
  132. package/docs/api/interfaces/UseEventLogoReturn.md +81 -0
  133. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  134. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  135. package/docs/api/interfaces/UsePublicEventLogoOptions.md +6 -6
  136. package/docs/api/interfaces/UsePublicEventLogoReturn.md +6 -6
  137. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  138. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  139. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  140. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  141. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  142. package/docs/api/interfaces/UserEventAccess.md +11 -11
  143. package/docs/api/interfaces/UserMenuProps.md +1 -1
  144. package/docs/api/interfaces/UserProfile.md +1 -1
  145. package/docs/api/modules.md +290 -95
  146. package/docs/api-reference/components.md +1 -18
  147. package/docs/api-reference/hooks.md +1 -4
  148. package/docs/best-practices/testing.md +2 -0
  149. package/docs/documentation-index.md +1 -1
  150. package/docs/getting-started/faq.md +1 -1
  151. package/docs/implementation-guides/file-reference-system.md +592 -58
  152. package/docs/implementation-guides/file-upload-storage.md +137 -73
  153. package/docs/implementation-guides/public-pages-advanced.md +10 -0
  154. package/docs/rbac/super-admin-guide.md +18 -70
  155. package/docs/testing/README.md +2 -0
  156. package/package.json +1 -1
  157. package/src/__tests__/TEST_STANDARD.md +674 -0
  158. package/src/__tests__/helpers/test-utils.tsx +3 -2
  159. package/src/components/DataTable/__tests__/{DataTable.comprehensive.test.tsx.skip → DataTable.comprehensive.test.tsx} +17 -18
  160. package/src/components/DataTable/__tests__/{DataTable.test.tsx.skip → DataTable.test.tsx} +14 -22
  161. package/src/components/DataTable/__tests__/{ssr.strict-mode.test.tsx.skip → ssr.strict-mode.test.tsx} +42 -47
  162. package/src/components/DataTable/components/__tests__/COVERAGE_NOTE.md +1 -1
  163. package/src/components/DataTable/examples/__tests__/PerformanceExample.test.tsx +13 -4
  164. package/src/components/DataTable/utils/__tests__/COVERAGE_NOTE.md +1 -1
  165. package/src/components/DataTable/utils/__tests__/performanceUtils.test.ts +10 -6
  166. package/src/components/FileDisplay/FileDisplay.test.tsx +257 -0
  167. package/src/components/{FileDisplay.tsx → FileDisplay/FileDisplay.tsx} +111 -10
  168. package/src/components/FileDisplay/index.tsx +4 -0
  169. package/src/components/FileUpload/FileUpload.test.tsx +171 -621
  170. package/src/components/FileUpload/FileUpload.tsx +512 -168
  171. package/src/components/FileUpload/index.tsx +4 -0
  172. package/src/components/Progress/Progress.test.tsx +38 -0
  173. package/src/components/PublicLayout/EventLogo.tsx +6 -4
  174. package/src/components/Select/Select.test.tsx +1 -1
  175. package/src/components/SessionRestorationLoader.tsx +48 -0
  176. package/src/components/Toast/Toast.tsx +13 -8
  177. package/src/components/index.ts +16 -16
  178. package/src/hooks/__tests__/ServiceHooks.test.tsx +615 -0
  179. package/src/hooks/public/usePublicEventLogo.ts +16 -20
  180. package/src/hooks/useEventLogo.ts +316 -0
  181. package/src/hooks/useEvents.ts +0 -5
  182. package/src/hooks/useFileReference.test.ts +659 -0
  183. package/src/hooks/useFileReference.ts +207 -3
  184. package/src/hooks/useSessionRestoration.ts +64 -0
  185. package/src/index.ts +17 -5
  186. package/src/providers/{UnifiedAuthProvider.test.simple.tsx → UnifiedAuthProvider.smoke.test.tsx} +81 -60
  187. package/src/providers/services/AuthServiceProvider.tsx +27 -3
  188. package/src/providers/services/UnifiedAuthProvider.tsx +34 -5
  189. package/src/rbac/{engine.test.simple.ts → RBACEngine.smoke.test.ts} +17 -12
  190. package/src/services/AuthService.ts +142 -20
  191. package/src/services/EventService.ts +0 -4
  192. package/src/types/auth.ts +15 -0
  193. package/src/types/file-reference.ts +73 -1
  194. package/src/types/index.ts +1 -0
  195. package/src/utils/__tests__/organisationContext.unit.test.ts +2 -4
  196. package/src/utils/appNameResolver.simple.test.ts +99 -29
  197. package/src/utils/file-reference.test.ts +535 -0
  198. package/src/utils/file-reference.ts +200 -30
  199. package/src/utils/organisationContext.test.ts +5 -19
  200. package/src/utils/organisationContext.ts +3 -5
  201. package/src/utils/storage/README.md +269 -262
  202. package/src/utils/storage/config.ts +9 -0
  203. package/src/utils/storage/helpers.test.ts +735 -0
  204. package/src/utils/storage/helpers.ts +189 -16
  205. package/src/utils/storage/index.ts +3 -0
  206. package/src/validation/__tests__/sanitization.unit.test.ts +1 -1
  207. package/src/validation/__tests__/schemaUtils.unit.test.ts +1 -1
  208. package/src/validation/__tests__/user.unit.test.ts +1 -1
  209. package/dist/chunk-5BN3YGNK.js.map +0 -1
  210. package/dist/chunk-CVMVPYAL.js.map +0 -1
  211. package/dist/chunk-I7O3RSMN.js.map +0 -1
  212. package/dist/chunk-WUXCWRL6.js.map +0 -1
  213. package/dist/chunk-ZFLOV3OM.js.map +0 -1
  214. package/docs/CONTENT_AUDIT_REPORT.md +0 -253
  215. package/docs/STYLE_GUIDE.md +0 -37
  216. package/examples/RBAC/__tests__/PermissionExample.test.tsx +0 -150
  217. package/examples/public-pages/__tests__/PublicPageUsageExample.test.tsx +0 -159
  218. package/src/__tests__/TEST_GUIDE_CURSOR.md +0 -1605
  219. package/src/__tests__/TEST_GUIDE_HUMAN.md +0 -103
  220. package/src/components/FileUpload/FileUpload.example.tsx +0 -218
  221. package/src/components/FileUpload/index.ts +0 -6
  222. package/src/components/FileUpload.tsx +0 -176
  223. package/src/components/Progress/index.ts +0 -3
  224. package/src/components/PublicLayout/__tests__/EventLogo.test.tsx +0 -666
  225. package/src/components/SuperAdminGuard.tsx +0 -116
  226. package/src/components/__tests__/FileDisplay.test.tsx +0 -575
  227. package/src/components/__tests__/FileUpload.test.tsx +0 -446
  228. package/src/components/__tests__/SuperAdminGuard.test.tsx +0 -627
  229. package/src/components/examples/PermissionExample.tsx +0 -173
  230. package/src/hooks/__tests__/usePublicEvent.unit.test.ts +0 -583
  231. package/src/hooks/__tests__/usePublicEventLogo.unit.test.ts +0 -640
  232. package/src/types/__tests__/file-reference.test.ts +0 -447
  233. package/src/utils/__tests__/file-reference.test.ts +0 -383
  234. /package/dist/{DataTable-FA6EUX5M.js.map → DataTable-PWBMKMOG.js.map} +0 -0
  235. /package/dist/{UnifiedAuthProvider-K2IZAY5F.js.map → UnifiedAuthProvider-5D3HEQND.js.map} +0 -0
  236. /package/dist/{chunk-NTW3KGS4.js.map → chunk-6UHXQH7P.js.map} +0 -0
  237. /package/dist/{chunk-YVUZWLQG.js.map → chunk-AQGF5OG7.js.map} +0 -0
  238. /package/dist/{chunk-KAY3K5TP.js.map → chunk-BNXBJOGL.js.map} +0 -0
  239. /package/dist/{chunk-S3JKDMD5.js.map → chunk-CXKMRKRF.js.map} +0 -0
  240. /package/dist/{chunk-RIXPZJUB.js.map → chunk-KTPG5VCH.js.map} +0 -0
  241. /package/dist/{chunk-2FQEQUJT.js.map → chunk-KWICIQVK.js.map} +0 -0
  242. /package/dist/{chunk-I2VVV5PQ.js.map → chunk-YY4YYM3E.js.map} +0 -0
  243. /package/src/providers/{OrganisationProvider.test.simple.tsx → OrganisationProvider.context.test.tsx} +0 -0
@@ -1,583 +0,0 @@
1
- import React from 'react';
2
- import { renderHook, waitFor, act } from '@testing-library/react';
3
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
4
- import { usePublicEvent, clearPublicEventCache, getPublicEventCacheStats } from '../public/usePublicEvent';
5
- import { usePublicPageContext } from '../../components/PublicLayout/PublicPageProvider';
6
-
7
- // Mock the PublicPageProvider
8
- vi.mock('../../components/PublicLayout/PublicPageProvider', () => ({
9
- usePublicPageContext: vi.fn(() => ({
10
- environment: {
11
- supabaseUrl: 'https://test.supabase.co',
12
- supabaseKey: 'test-anon-key'
13
- }
14
- }))
15
- }));
16
-
17
- // Mock Supabase client
18
- const mockSupabaseClient = {
19
- rpc: vi.fn(),
20
- from: vi.fn(() => ({
21
- select: vi.fn(() => ({
22
- eq: vi.fn(() => ({
23
- eq: vi.fn(() => ({
24
- not: vi.fn(() => ({
25
- limit: vi.fn(() => ({
26
- single: vi.fn()
27
- }))
28
- }))
29
- }))
30
- }))
31
- }))
32
- }))
33
- };
34
-
35
- // Helper to create table query mocks
36
- const createTableQueryMock = () => ({
37
- data: null,
38
- error: null,
39
- mockResolvedValueOnce: vi.fn(),
40
- mockResolvedValue: vi.fn()
41
- });
42
-
43
- // Helper to create a complete table query chain
44
- const createTableQueryChain = (finalResult: any) => {
45
- const mockChain = {
46
- select: vi.fn().mockReturnThis(),
47
- eq: vi.fn().mockReturnThis(),
48
- not: vi.fn().mockReturnThis(),
49
- limit: vi.fn().mockReturnThis(),
50
- single: vi.fn().mockResolvedValue(finalResult)
51
- };
52
- return mockChain;
53
- };
54
-
55
- // Mock createClient
56
- vi.mock('@supabase/supabase-js', () => ({
57
- createClient: vi.fn(() => mockSupabaseClient)
58
- }));
59
-
60
- // Mock environment variables
61
- const originalEnv = import.meta.env;
62
-
63
- describe('usePublicEvent', () => {
64
- beforeEach(() => {
65
- vi.clearAllMocks();
66
- clearPublicEventCache();
67
-
68
- // Reset environment
69
- Object.defineProperty(import.meta, 'env', {
70
- value: {
71
- VITE_SUPABASE_URL: 'https://test.supabase.co',
72
- VITE_SUPABASE_ANON_KEY: 'test-anon-key'
73
- },
74
- writable: true
75
- });
76
-
77
- // Mock window object
78
- Object.defineProperty(window, 'location', {
79
- value: { href: 'https://test.com' },
80
- writable: true
81
- });
82
-
83
- // Re-establish mock implementations after clearAllMocks
84
- mockSupabaseClient.rpc.mockImplementation(() =>
85
- Promise.resolve({
86
- data: null,
87
- error: null
88
- })
89
- );
90
-
91
- // Ensure the mock is properly configured
92
- expect(mockSupabaseClient.rpc).toBeDefined();
93
- });
94
-
95
- afterEach(() => {
96
- vi.clearAllMocks();
97
- clearPublicEventCache();
98
- Object.defineProperty(import.meta, 'env', {
99
- value: originalEnv,
100
- writable: true
101
- });
102
- });
103
-
104
- describe('Basic Functionality', () => {
105
- it('should initialize with loading state', () => {
106
- const { result } = renderHook(() => usePublicEvent('test-event'));
107
-
108
- expect(result.current.isLoading).toBe(true);
109
- expect(result.current.event).toBe(null);
110
- expect(result.current.error).toBe(null);
111
- });
112
-
113
- it('should fetch event data successfully via RPC', async () => {
114
- const mockEventData = {
115
- event_id: '123',
116
- event_name: 'Test Event',
117
- event_date: '2024-01-01',
118
- event_venue: 'Test Venue',
119
- event_participants: 100,
120
- event_colours: { primary: '#000000' },
121
- organisation_id: 'org-123',
122
- event_days: 1,
123
- event_typicalunit: 'km',
124
- event_rounddown: false,
125
- event_youthmultiplier: 1.0,
126
- event_catering_email: 'test@example.com',
127
- event_news: 'Test news',
128
- event_billing: 'Test billing',
129
- event_footer: 'Test footer',
130
- event_email: 'event@example.com',
131
- event_logo: null
132
- };
133
-
134
- mockSupabaseClient.rpc.mockResolvedValueOnce({
135
- data: [mockEventData],
136
- error: null
137
- });
138
-
139
- const { result } = renderHook(() => usePublicEvent('test-event'));
140
-
141
- await waitFor(() => {
142
- expect(result.current.isLoading).toBe(false);
143
- }, { interval: 10 });
144
-
145
- expect(result.current.event).toEqual({
146
- id: '123',
147
- event_id: '123',
148
- event_name: 'Test Event',
149
- event_code: 'test-event',
150
- event_date: '2024-01-01',
151
- event_venue: 'Test Venue',
152
- event_participants: 100,
153
- event_logo: null,
154
- event_colours: { primary: '#000000' },
155
- organisation_id: 'org-123',
156
- is_visible: true,
157
- created_at: expect.any(String),
158
- updated_at: expect.any(String),
159
- name: 'Test Event',
160
- start_date: '2024-01-01'
161
- });
162
- expect(result.current.error).toBe(null);
163
- });
164
-
165
- it('should handle event not found', async () => {
166
- mockSupabaseClient.rpc.mockResolvedValueOnce({
167
- data: [],
168
- error: null
169
- });
170
-
171
- const { result } = renderHook(() => usePublicEvent('nonexistent-event'));
172
-
173
- await waitFor(() => {
174
- expect(result.current.isLoading).toBe(false);
175
- }, { interval: 10 });
176
-
177
- expect(result.current.event).toBe(null);
178
- expect(result.current.error).toEqual(new Error('Event not found'));
179
- });
180
-
181
- it('should handle invalid event code', async () => {
182
- const { result } = renderHook(() => usePublicEvent(''));
183
-
184
- await waitFor(() => {
185
- expect(result.current.isLoading).toBe(false);
186
- }, { interval: 10 });
187
-
188
- expect(result.current.event).toBe(null);
189
- expect(result.current.error).toBeInstanceOf(Error);
190
- expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
191
- });
192
- });
193
-
194
- describe('Caching', () => {
195
- it('should cache event data', async () => {
196
- const mockEventData = {
197
- event_id: '123',
198
- event_name: 'Test Event',
199
- event_date: '2024-01-01',
200
- event_venue: 'Test Venue',
201
- event_participants: 100,
202
- event_colours: { primary: '#000000' },
203
- organisation_id: 'org-123',
204
- event_days: 1,
205
- event_typicalunit: 'km',
206
- event_rounddown: false,
207
- event_youthmultiplier: 1.0,
208
- event_catering_email: 'test@example.com',
209
- event_news: 'Test news',
210
- event_billing: 'Test billing',
211
- event_footer: 'Test footer',
212
- event_email: 'event@example.com'
213
- };
214
-
215
- mockSupabaseClient.rpc.mockResolvedValueOnce({
216
- data: [mockEventData],
217
- error: null
218
- });
219
-
220
- const { result, rerender } = renderHook(() => usePublicEvent('test-event'));
221
-
222
- await waitFor(() => {
223
- expect(result.current.isLoading).toBe(false);
224
- }, { interval: 10 });
225
-
226
- // Rerender with same event code - should use cache
227
- rerender();
228
-
229
- // Should not call RPC again
230
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(1);
231
- });
232
-
233
- it('should respect cache TTL', async () => {
234
- const mockEventData = {
235
- event_id: '123',
236
- event_name: 'Test Event',
237
- event_date: '2024-01-01',
238
- event_venue: 'Test Venue',
239
- event_participants: 100,
240
- event_colours: { primary: '#000000' },
241
- organisation_id: 'org-123',
242
- event_days: 1,
243
- event_typicalunit: 'km',
244
- event_rounddown: false,
245
- event_youthmultiplier: 1.0,
246
- event_catering_email: 'test@example.com',
247
- event_news: 'Test news',
248
- event_billing: 'Test billing',
249
- event_footer: 'Test footer',
250
- event_email: 'event@example.com'
251
- };
252
-
253
- mockSupabaseClient.rpc.mockResolvedValueOnce({
254
- data: [mockEventData],
255
- error: null
256
- });
257
-
258
- const { result } = renderHook(() => usePublicEvent('test-event', { cacheTtl: 100 }));
259
-
260
- await waitFor(() => {
261
- expect(result.current.isLoading).toBe(false);
262
- }, { interval: 10 });
263
-
264
- // Wait for cache to expire
265
- await new Promise(resolve => setTimeout(resolve, 150));
266
-
267
- // Rerender should fetch again
268
- const { result: result2 } = renderHook(() => usePublicEvent('test-event', { cacheTtl: 100 }));
269
-
270
- await waitFor(() => {
271
- expect(result2.current.isLoading).toBe(false);
272
- }, { interval: 10 });
273
-
274
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
275
- });
276
-
277
- it('should disable caching when requested', async () => {
278
- const mockEventData = {
279
- event_id: '123',
280
- event_name: 'Test Event',
281
- event_date: '2024-01-01',
282
- event_venue: 'Test Venue',
283
- event_participants: 100,
284
- event_colours: { primary: '#000000' },
285
- organisation_id: 'org-123',
286
- event_days: 1,
287
- event_typicalunit: 'km',
288
- event_rounddown: false,
289
- event_youthmultiplier: 1.0,
290
- event_catering_email: 'test@example.com',
291
- event_news: 'Test news',
292
- event_billing: 'Test billing',
293
- event_footer: 'Test footer',
294
- event_email: 'event@example.com',
295
- event_logo: null
296
- };
297
-
298
- // Clear any existing cache
299
- const { clearPublicEventCache } = await import('../public/usePublicEvent');
300
- clearPublicEventCache();
301
-
302
- // Use mockImplementation to handle multiple calls
303
- let callCount = 0;
304
- mockSupabaseClient.rpc.mockImplementation(() => {
305
- callCount++;
306
- return Promise.resolve({
307
- data: [mockEventData],
308
- error: null
309
- });
310
- });
311
-
312
- const { result, rerender } = renderHook(
313
- ({ eventCode }) => usePublicEvent(eventCode, { enableCache: false }),
314
- { initialProps: { eventCode: 'test-event' } }
315
- );
316
-
317
- await waitFor(() => {
318
- expect(result.current.isLoading).toBe(false);
319
- }, { interval: 10 });
320
-
321
- // Change the event code to force a new fetch
322
- rerender({ eventCode: 'test-event-2' });
323
-
324
- await waitFor(() => {
325
- expect(result.current.isLoading).toBe(false);
326
- }, { interval: 10 });
327
-
328
- expect(callCount).toBe(2);
329
- });
330
- });
331
-
332
- describe('Error Handling', () => {
333
- it('should handle RPC errors', async () => {
334
- // Mock the RPC call to return an error object
335
- const testError = { message: 'Database error', details: 'Test error details', hint: null, code: 'TEST_ERROR' };
336
- mockSupabaseClient.rpc.mockImplementation(() =>
337
- Promise.resolve({
338
- data: null,
339
- error: testError
340
- })
341
- );
342
-
343
- const { result } = renderHook(() => usePublicEvent('test-event'));
344
-
345
- await waitFor(() => {
346
- expect(result.current.isLoading).toBe(false);
347
- }, { interval: 10 });
348
-
349
- expect(result.current.error).toBeInstanceOf(Error);
350
- expect(result.current.error?.message).toBe('Database error');
351
- expect(result.current.event).toBe(null);
352
-
353
- // Verify the mock was called
354
- expect(mockSupabaseClient.rpc).toHaveBeenCalledWith('get_public_event_by_code', {
355
- event_code_param: 'test-event'
356
- });
357
- });
358
-
359
- it('should handle missing Supabase client', async () => {
360
- // Mock the PublicPageProvider to return null environment
361
- const mockUsePublicPageContext = vi.mocked(usePublicPageContext);
362
- mockUsePublicPageContext.mockReturnValueOnce({
363
- environment: {
364
- supabaseUrl: '',
365
- supabaseKey: ''
366
- }
367
- });
368
-
369
- // Mock createClient to return null when environment is empty
370
- const { createClient } = await import('@supabase/supabase-js');
371
- vi.mocked(createClient).mockReturnValueOnce(null as any);
372
-
373
- const { result } = renderHook(() => usePublicEvent('test-event'));
374
-
375
- await waitFor(() => {
376
- expect(result.current.isLoading).toBe(false);
377
- }, { interval: 10 });
378
-
379
- expect(result.current.error).toBeInstanceOf(Error);
380
- expect(result.current.error?.message).toContain('Invalid event code or Supabase client not available');
381
- expect(result.current.event).toBe(null);
382
- });
383
- });
384
-
385
- describe('Provider Context Integration', () => {
386
- it('should use PublicPageContext when available', async () => {
387
- const mockContext = {
388
- environment: {
389
- supabaseUrl: 'https://context.supabase.co',
390
- supabaseKey: 'context-anon-key'
391
- }
392
- };
393
-
394
- vi.mocked(usePublicPageContext).mockReturnValue(mockContext);
395
-
396
- const mockEventData = {
397
- event_id: '123',
398
- event_name: 'Test Event',
399
- event_date: '2024-01-01',
400
- event_venue: 'Test Venue',
401
- event_participants: 100,
402
- event_colours: { primary: '#000000' },
403
- organisation_id: 'org-123',
404
- event_days: 1,
405
- event_typicalunit: 'km',
406
- event_rounddown: false,
407
- event_youthmultiplier: 1.0,
408
- event_catering_email: 'test@example.com',
409
- event_news: 'Test news',
410
- event_billing: 'Test billing',
411
- event_footer: 'Test footer',
412
- event_email: 'event@example.com',
413
- event_logo: null
414
- };
415
-
416
- mockSupabaseClient.rpc.mockResolvedValueOnce({
417
- data: [mockEventData],
418
- error: null
419
- });
420
-
421
- const { result } = renderHook(() => usePublicEvent('test-event'));
422
-
423
- await waitFor(() => {
424
- expect(result.current.isLoading).toBe(false);
425
- }, { interval: 10 });
426
-
427
- expect(result.current.event).toBeTruthy();
428
- });
429
- });
430
-
431
- describe('Refetch Functionality', () => {
432
- it('should refetch data when refetch is called', async () => {
433
- const mockEventData = {
434
- event_id: '123',
435
- event_name: 'Test Event',
436
- event_date: '2024-01-01',
437
- event_venue: 'Test Venue',
438
- event_participants: 100,
439
- event_colours: { primary: '#000000' },
440
- organisation_id: 'org-123',
441
- event_days: 1,
442
- event_typicalunit: 'km',
443
- event_rounddown: false,
444
- event_youthmultiplier: 1.0,
445
- event_catering_email: 'test@example.com',
446
- event_news: 'Test news',
447
- event_billing: 'Test billing',
448
- event_footer: 'Test footer',
449
- event_email: 'event@example.com'
450
- };
451
-
452
- mockSupabaseClient.rpc.mockResolvedValue({
453
- data: [mockEventData],
454
- error: null
455
- });
456
-
457
- const { result } = renderHook(() => usePublicEvent('test-event'));
458
-
459
- await waitFor(() => {
460
- expect(result.current.isLoading).toBe(false);
461
- }, { interval: 10 });
462
-
463
- // Call refetch
464
- await act(async () => {
465
- await result.current.refetch();
466
- });
467
-
468
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
469
- });
470
-
471
- it('should clear cache when refetch is called', async () => {
472
- const mockEventData = {
473
- event_id: '123',
474
- event_name: 'Test Event',
475
- event_date: '2024-01-01',
476
- event_venue: 'Test Venue',
477
- event_participants: 100,
478
- event_colours: { primary: '#000000' },
479
- organisation_id: 'org-123',
480
- event_days: 1,
481
- event_typicalunit: 'km',
482
- event_rounddown: false,
483
- event_youthmultiplier: 1.0,
484
- event_catering_email: 'test@example.com',
485
- event_news: 'Test news',
486
- event_billing: 'Test billing',
487
- event_footer: 'Test footer',
488
- event_email: 'event@example.com'
489
- };
490
-
491
- mockSupabaseClient.rpc.mockResolvedValue({
492
- data: [mockEventData],
493
- error: null
494
- });
495
-
496
- const { result } = renderHook(() => usePublicEvent('test-event'));
497
-
498
- await waitFor(() => {
499
- expect(result.current.isLoading).toBe(false);
500
- }, { interval: 10 });
501
-
502
- // Call refetch
503
- await act(async () => {
504
- await result.current.refetch();
505
- });
506
-
507
- // Should call RPC again (cache was cleared)
508
- expect(mockSupabaseClient.rpc).toHaveBeenCalledTimes(2);
509
- });
510
- });
511
-
512
- describe('Cache Management Utilities', () => {
513
- it('should clear public event cache', () => {
514
- // Add some data to cache
515
- const stats = getPublicEventCacheStats();
516
- expect(stats.size).toBe(0);
517
-
518
- // Clear cache
519
- clearPublicEventCache();
520
-
521
- const statsAfter = getPublicEventCacheStats();
522
- expect(statsAfter.size).toBe(0);
523
- });
524
-
525
- it('should get cache statistics', () => {
526
- const stats = getPublicEventCacheStats();
527
- expect(stats).toEqual({
528
- size: 0,
529
- keys: []
530
- });
531
- });
532
- });
533
-
534
- describe('Edge Cases', () => {
535
- it('should handle null event data from RPC', async () => {
536
- mockSupabaseClient.rpc.mockResolvedValueOnce({
537
- data: null,
538
- error: null
539
- });
540
-
541
- const { result } = renderHook(() => usePublicEvent('test-event'));
542
-
543
- await waitFor(() => {
544
- expect(result.current.isLoading).toBe(false);
545
- }, { interval: 10 });
546
-
547
- expect(result.current.event).toBe(null);
548
- expect(result.current.error).toEqual(new Error('Event not found'));
549
- });
550
-
551
- it('should handle empty event data array from RPC', async () => {
552
- mockSupabaseClient.rpc.mockResolvedValueOnce({
553
- data: [],
554
- error: null
555
- });
556
-
557
- const { result } = renderHook(() => usePublicEvent('test-event'));
558
-
559
- await waitFor(() => {
560
- expect(result.current.isLoading).toBe(false);
561
- }, { interval: 10 });
562
-
563
- expect(result.current.event).toBe(null);
564
- expect(result.current.error).toEqual(new Error('Event not found'));
565
- });
566
-
567
- it('should handle undefined event data from RPC', async () => {
568
- mockSupabaseClient.rpc.mockResolvedValueOnce({
569
- data: [undefined],
570
- error: null
571
- });
572
-
573
- const { result } = renderHook(() => usePublicEvent('test-event'));
574
-
575
- await waitFor(() => {
576
- expect(result.current.isLoading).toBe(false);
577
- }, { interval: 10 });
578
-
579
- expect(result.current.event).toBe(null);
580
- expect(result.current.error).toEqual(new Error('Event not found'));
581
- });
582
- });
583
- });