@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
@@ -0,0 +1,615 @@
1
+ /**
2
+ * @file Service Hooks Unit Tests
3
+ * @package @jmruthers/pace-core
4
+ * @module Hooks/__tests__
5
+ * @since 0.1.0
6
+ *
7
+ * Unit tests for service hooks.
8
+ * Tests React hooks that provide access to services.
9
+ */
10
+
11
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
12
+ import { renderHook, act } from '@testing-library/react';
13
+ import * as React from 'react';
14
+ import { useAuthService } from '../services/useAuthService';
15
+ import { useOrganisationService } from '../services/useOrganisationService';
16
+ import { useEventService } from '../services/useEventService';
17
+ import { useInactivityService } from '../services/useInactivityService';
18
+ import { useAuth } from '../services/useAuth';
19
+ import { useCurrentOrganisation } from '../services/useCurrentOrganisation';
20
+ import { useCurrentEvent } from '../services/useCurrentEvent';
21
+
22
+ // Mock usePermissions hook for service hooks tests
23
+ vi.mock('../../rbac/hooks/usePermissions', () => ({
24
+ usePermissions: vi.fn(() => ({
25
+ permissions: { 'events:read': true },
26
+ roles: ['admin'],
27
+ accessLevel: 'ADMIN',
28
+ rbacLoading: false,
29
+ rbacError: null,
30
+ selectedEventId: 'event-1',
31
+ userEventAccess: [],
32
+ eventAccessLoading: false,
33
+ selectedOrganisationId: 'org-1',
34
+ appConfig: { name: 'test' },
35
+ hasPermission: vi.fn(),
36
+ hasAnyPermission: vi.fn(),
37
+ hasAllPermissions: vi.fn(),
38
+ hasRole: vi.fn(),
39
+ hasAccessLevel: vi.fn(),
40
+ canAccess: vi.fn(),
41
+ validatePermission: vi.fn(),
42
+ validateAccess: vi.fn(),
43
+ refreshPermissions: vi.fn(),
44
+ setSelectedEventId: vi.fn(),
45
+ loadUserEventAccess: vi.fn(),
46
+ getUserEventAccess: vi.fn(),
47
+ requireOrganisationContext: vi.fn(() => 'org-1'),
48
+ }))
49
+ }));
50
+
51
+ import { usePermissions } from '../../rbac/hooks/usePermissions';
52
+
53
+ // Create a mock for useContext that can be controlled
54
+ const mockUseContext = vi.fn();
55
+
56
+ // Mock React module
57
+ vi.mock('react', async () => {
58
+ const actual = await vi.importActual('react');
59
+ return {
60
+ ...actual,
61
+ useContext: (...args: any[]) => mockUseContext(...args),
62
+ };
63
+ });
64
+
65
+ // Mock the provider contexts
66
+ vi.mock('../../providers/services/AuthServiceProvider', () => ({
67
+ AuthServiceContext: { _currentValue: null },
68
+ }));
69
+
70
+ vi.mock('../../providers/services/RBACServiceProvider', () => ({
71
+ RBACServiceContext: { _currentValue: null },
72
+ }));
73
+
74
+ vi.mock('../../providers/services/OrganisationServiceProvider', () => ({
75
+ OrganisationServiceContext: { _currentValue: null },
76
+ }));
77
+
78
+ vi.mock('../../providers/services/EventServiceProvider', () => ({
79
+ EventServiceContext: { _currentValue: null },
80
+ }));
81
+
82
+ vi.mock('../../providers/services/InactivityServiceProvider', () => ({
83
+ InactivityServiceContext: { _currentValue: null },
84
+ }));
85
+
86
+ // Mock Supabase client
87
+ const createMockSupabaseClient = () => ({
88
+ auth: {
89
+ getSession: vi.fn(),
90
+ getUser: vi.fn(),
91
+ onAuthStateChange: vi.fn(() => ({ data: { subscription: { unsubscribe: vi.fn() } } })),
92
+ signInWithPassword: vi.fn(),
93
+ signUp: vi.fn(),
94
+ signOut: vi.fn(),
95
+ resetPasswordForEmail: vi.fn(),
96
+ updateUser: vi.fn(),
97
+ refreshSession: vi.fn()
98
+ },
99
+ rpc: vi.fn(),
100
+ from: vi.fn()
101
+ });
102
+
103
+ // Mock user and session
104
+ const mockUser = {
105
+ id: 'user-1',
106
+ email: 'test@example.com'
107
+ };
108
+
109
+ const mockSession = {
110
+ access_token: 'token',
111
+ user: mockUser
112
+ };
113
+
114
+ const mockOrganisation = {
115
+ id: 'org-1',
116
+ display_name: 'Test Organisation'
117
+ };
118
+
119
+ // Mock service classes
120
+ const createMockService = (name: string) => ({
121
+ subscribe: vi.fn(() => vi.fn()), // Return unsubscribe function
122
+ cleanup: vi.fn(),
123
+ // Add service-specific methods as needed
124
+ [name]: vi.fn()
125
+ });
126
+
127
+ describe('Service Hooks', () => {
128
+ let mockSupabase: ReturnType<typeof createMockSupabaseClient>;
129
+
130
+ beforeEach(() => {
131
+ mockSupabase = createMockSupabaseClient();
132
+ // Reset the mock before each test
133
+ mockUseContext.mockReset();
134
+ });
135
+
136
+ describe('useAuthService', () => {
137
+ it('should return AuthService from context', () => {
138
+ const mockService = createMockService('auth');
139
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
140
+ <div>{children}</div>
141
+ );
142
+
143
+ // Mock the context with proper structure
144
+ mockUseContext.mockReturnValue({ authService: mockService });
145
+
146
+ const { result } = renderHook(() => useAuthService(), { wrapper });
147
+
148
+ expect(result.current).toBe(mockService);
149
+ });
150
+
151
+ it('should throw error when used outside provider', () => {
152
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
153
+ <div>{children}</div>
154
+ );
155
+
156
+ // Mock the context to return null
157
+ mockUseContext.mockReturnValue(null);
158
+
159
+ expect(() => {
160
+ renderHook(() => useAuthService(), { wrapper });
161
+ }).toThrow('useAuthService must be used within AuthServiceProvider');
162
+ });
163
+
164
+ it('should subscribe to service changes', () => {
165
+ const mockService = createMockService('auth');
166
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
167
+ <div>{children}</div>
168
+ );
169
+
170
+ mockUseContext.mockReturnValue({ authService: mockService });
171
+
172
+ renderHook(() => useAuthService(), { wrapper });
173
+
174
+ expect(mockService.subscribe).toHaveBeenCalled();
175
+ });
176
+ });
177
+
178
+ describe('useOrganisationService', () => {
179
+ it('should return OrganisationService from context', () => {
180
+ const mockService = createMockService('organisation');
181
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
182
+ <div>{children}</div>
183
+ );
184
+
185
+ mockUseContext.mockReturnValue({ organisationService: mockService });
186
+
187
+ const { result } = renderHook(() => useOrganisationService(), { wrapper });
188
+
189
+ expect(result.current).toBe(mockService);
190
+ });
191
+
192
+ it('should throw error when used outside provider', () => {
193
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
194
+ <div>{children}</div>
195
+ );
196
+
197
+ mockUseContext.mockReturnValue(null);
198
+
199
+ expect(() => {
200
+ renderHook(() => useOrganisationService(), { wrapper });
201
+ }).toThrow('useOrganisationService must be used within OrganisationServiceProvider');
202
+ });
203
+ });
204
+
205
+ describe('useEventService', () => {
206
+ it('should return EventService from context', () => {
207
+ const mockService = createMockService('event');
208
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
209
+ <div>{children}</div>
210
+ );
211
+
212
+ mockUseContext.mockReturnValue({ eventService: mockService });
213
+
214
+ const { result } = renderHook(() => useEventService(), { wrapper });
215
+
216
+ expect(result.current).toBe(mockService);
217
+ });
218
+
219
+ it('should throw error when used outside provider', () => {
220
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
221
+ <div>{children}</div>
222
+ );
223
+
224
+ mockUseContext.mockReturnValue(null);
225
+
226
+ expect(() => {
227
+ renderHook(() => useEventService(), { wrapper });
228
+ }).toThrow('useEventService must be used within EventServiceProvider');
229
+ });
230
+ });
231
+
232
+ describe('useInactivityService', () => {
233
+ it('should return InactivityService from context', () => {
234
+ const mockService = createMockService('inactivity');
235
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
236
+ <div>{children}</div>
237
+ );
238
+
239
+ vi.spyOn(React, 'useContext').mockReturnValue({ inactivityService: mockService });
240
+
241
+ const { result } = renderHook(() => useInactivityService(), { wrapper });
242
+
243
+ expect(result.current).toBe(mockService);
244
+ });
245
+
246
+ it('should throw error when used outside provider', () => {
247
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
248
+ <div>{children}</div>
249
+ );
250
+
251
+ mockUseContext.mockReturnValue(null);
252
+
253
+ expect(() => {
254
+ renderHook(() => useInactivityService(), { wrapper });
255
+ }).toThrow('useInactivityService must be used within InactivityServiceProvider');
256
+ });
257
+ });
258
+
259
+ describe('useAuth', () => {
260
+ it('should return auth service properties and methods', () => {
261
+ const mockService = {
262
+ getUser: vi.fn(() => mockUser),
263
+ getSession: vi.fn(() => mockSession),
264
+ isAuthenticated: vi.fn(() => true),
265
+ isLoading: vi.fn(() => false),
266
+ getError: vi.fn(() => null),
267
+ getSupabaseClient: vi.fn(() => mockSupabase),
268
+ signIn: vi.fn(),
269
+ signUp: vi.fn(),
270
+ signOut: vi.fn(),
271
+ resetPassword: vi.fn(),
272
+ updatePassword: vi.fn(),
273
+ refreshSession: vi.fn(),
274
+ subscribe: vi.fn(() => vi.fn()),
275
+ cleanup: vi.fn()
276
+ };
277
+
278
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
279
+ <div>{children}</div>
280
+ );
281
+
282
+ mockUseContext.mockReturnValue({ authService: mockService });
283
+
284
+ const { result } = renderHook(() => useAuth(), { wrapper });
285
+
286
+ expect(result.current.user).toBe(mockUser);
287
+ expect(result.current.session).toBe(mockSession);
288
+ expect(result.current.isAuthenticated).toBe(true);
289
+ expect(result.current.authLoading).toBe(false);
290
+ expect(result.current.authError).toBeNull();
291
+ expect(typeof result.current.signIn).toBe('function');
292
+ expect(typeof result.current.signUp).toBe('function');
293
+ expect(typeof result.current.signOut).toBe('function');
294
+ expect(typeof result.current.resetPassword).toBe('function');
295
+ expect(typeof result.current.updatePassword).toBe('function');
296
+ expect(typeof result.current.refreshSession).toBe('function');
297
+ });
298
+ });
299
+
300
+ describe('usePermissions', () => {
301
+ it('should return RBAC service properties and methods', () => {
302
+ const mockService = {
303
+ getPermissions: vi.fn(() => ({ 'events:read': true })),
304
+ getRoles: vi.fn(() => ['admin']),
305
+ getAccessLevel: vi.fn(() => 'ADMIN'),
306
+ isLoading: vi.fn(() => false),
307
+ getError: vi.fn(() => null),
308
+ getSelectedEventId: vi.fn(() => 'event-1'),
309
+ getAppConfig: vi.fn(() => ({ name: 'test' })),
310
+ getUserEventAccess: vi.fn(() => []),
311
+ isEventAccessLoading: vi.fn(() => false),
312
+ getSelectedOrganisationId: vi.fn(() => 'org-1'),
313
+ hasPermission: vi.fn(),
314
+ hasAnyPermission: vi.fn(),
315
+ hasAllPermissions: vi.fn(),
316
+ hasRole: vi.fn(),
317
+ hasAccessLevel: vi.fn(),
318
+ canAccess: vi.fn(),
319
+ validatePermission: vi.fn(),
320
+ validateAccess: vi.fn(),
321
+ refreshPermissions: vi.fn(),
322
+ setSelectedEventId: vi.fn(),
323
+ loadUserEventAccess: vi.fn(),
324
+ requireOrganisationContext: vi.fn(() => 'org-1'),
325
+ subscribe: vi.fn(() => vi.fn()),
326
+ cleanup: vi.fn()
327
+ };
328
+
329
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
330
+ <div>{children}</div>
331
+ );
332
+
333
+ mockUseContext.mockReturnValue({ rbacService: mockService });
334
+
335
+ const { result } = renderHook(() => usePermissions('user-1', { organisationId: 'org-1', eventId: 'event-1', appId: 'test-app' }), { wrapper });
336
+
337
+ expect(result.current.permissions).toEqual({ 'events:read': true });
338
+ expect(result.current.roles).toEqual(['admin']);
339
+ expect(result.current.accessLevel).toBe('ADMIN');
340
+ expect(result.current.rbacLoading).toBe(false);
341
+ expect(result.current.rbacError).toBeNull();
342
+ expect(result.current.selectedEventId).toBe('event-1');
343
+ expect(result.current.userEventAccess).toEqual([]);
344
+ expect(result.current.eventAccessLoading).toBe(false);
345
+ expect(result.current.selectedOrganisationId).toBe('org-1');
346
+ expect(result.current.appConfig).toEqual({ name: 'test' });
347
+ expect(typeof result.current.hasPermission).toBe('function');
348
+ expect(typeof result.current.hasAnyPermission).toBe('function');
349
+ expect(typeof result.current.hasAllPermissions).toBe('function');
350
+ expect(typeof result.current.hasRole).toBe('function');
351
+ expect(typeof result.current.hasAccessLevel).toBe('function');
352
+ expect(typeof result.current.canAccess).toBe('function');
353
+ expect(typeof result.current.validatePermission).toBe('function');
354
+ expect(typeof result.current.validateAccess).toBe('function');
355
+ expect(typeof result.current.refreshPermissions).toBe('function');
356
+ expect(typeof result.current.setSelectedEventId).toBe('function');
357
+ expect(typeof result.current.loadUserEventAccess).toBe('function');
358
+ expect(typeof result.current.getUserEventAccess).toBe('function');
359
+ expect(typeof result.current.requireOrganisationContext).toBe('function');
360
+ });
361
+ });
362
+
363
+ describe('useCurrentOrganisation', () => {
364
+ it('should return organisation service properties and methods', () => {
365
+ const mockService = {
366
+ getSelectedOrganisation: vi.fn(() => mockOrganisation),
367
+ getOrganisations: vi.fn(() => [mockOrganisation]),
368
+ getUserMemberships: vi.fn(() => []),
369
+ isLoading: vi.fn(() => false),
370
+ getError: vi.fn(() => null),
371
+ hasValidOrganisationContext: vi.fn(() => true),
372
+ isContextReady: vi.fn(() => true),
373
+ switchOrganisation: vi.fn(),
374
+ getUserRole: vi.fn(),
375
+ validateOrganisationAccess: vi.fn(),
376
+ refreshOrganisations: vi.fn(),
377
+ ensureOrganisationContext: vi.fn(() => mockOrganisation),
378
+ isOrganisationSecure: vi.fn(() => true),
379
+ getPrimaryOrganisation: vi.fn(() => mockOrganisation),
380
+ subscribe: vi.fn(() => vi.fn()),
381
+ cleanup: vi.fn()
382
+ };
383
+
384
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
385
+ <div>{children}</div>
386
+ );
387
+
388
+ mockUseContext.mockReturnValue({ organisationService: mockService });
389
+
390
+ const { result } = renderHook(() => useCurrentOrganisation(), { wrapper });
391
+
392
+ expect(result.current.selectedOrganisation).toBe(mockOrganisation);
393
+ expect(result.current.organisations).toEqual([mockOrganisation]);
394
+ expect(result.current.userMemberships).toEqual([]);
395
+ expect(result.current.isLoading).toBe(false);
396
+ expect(result.current.error).toBeNull();
397
+ expect(result.current.hasValidOrganisationContext).toBe(true);
398
+ expect(typeof result.current.switchOrganisation).toBe('function');
399
+ expect(typeof result.current.getUserRole).toBe('function');
400
+ expect(typeof result.current.validateOrganisationAccess).toBe('function');
401
+ expect(typeof result.current.refreshOrganisations).toBe('function');
402
+ expect(typeof result.current.ensureOrganisationContext).toBe('function');
403
+ expect(typeof result.current.isOrganisationSecure).toBe('function');
404
+ expect(typeof result.current.getPrimaryOrganisation).toBe('function');
405
+ });
406
+ });
407
+
408
+ describe('useCurrentEvent', () => {
409
+ it('should return event service properties and methods', () => {
410
+ const mockEvent = {
411
+ id: 'event-1',
412
+ event_id: 'event-1',
413
+ event_name: 'Test Event',
414
+ event_date: '2024-01-01T00:00:00Z',
415
+ event_venue: 'Test Venue',
416
+ event_participants: 100,
417
+ event_colours: '#FF0000',
418
+ event_logo: '',
419
+ organisation_id: 'org-1',
420
+ is_visible: true,
421
+ name: 'Test Event',
422
+ start_date: '2024-01-01T00:00:00Z'
423
+ };
424
+
425
+ const mockService = {
426
+ getEvents: vi.fn(() => [mockEvent]),
427
+ getSelectedEvent: vi.fn(() => mockEvent),
428
+ isLoading: vi.fn(() => false),
429
+ getError: vi.fn(() => null),
430
+ setSelectedEvent: vi.fn(),
431
+ refreshEvents: vi.fn(),
432
+ subscribe: vi.fn(() => vi.fn()),
433
+ cleanup: vi.fn()
434
+ };
435
+
436
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
437
+ <div>{children}</div>
438
+ );
439
+
440
+ mockUseContext.mockReturnValue({ eventService: mockService });
441
+
442
+ const { result } = renderHook(() => useCurrentEvent(), { wrapper });
443
+
444
+ expect(result.current.events).toEqual([mockEvent]);
445
+ expect(result.current.selectedEvent).toBe(mockEvent);
446
+ expect(result.current.isLoading).toBe(false);
447
+ expect(result.current.error).toBeNull();
448
+ expect(typeof result.current.setSelectedEvent).toBe('function');
449
+ expect(typeof result.current.refreshEvents).toBe('function');
450
+ });
451
+ });
452
+
453
+ describe('Hook Integration', () => {
454
+ it('should work with multiple hooks simultaneously', () => {
455
+ const mockAuthService = {
456
+ getUser: vi.fn(() => mockUser),
457
+ getSession: vi.fn(() => mockSession),
458
+ isAuthenticated: vi.fn(() => true),
459
+ isLoading: vi.fn(() => false),
460
+ getError: vi.fn(() => null),
461
+ getSupabaseClient: vi.fn(() => mockSupabase),
462
+ signIn: vi.fn(),
463
+ signUp: vi.fn(),
464
+ signOut: vi.fn(),
465
+ resetPassword: vi.fn(),
466
+ updatePassword: vi.fn(),
467
+ refreshSession: vi.fn(),
468
+ subscribe: vi.fn(() => vi.fn()),
469
+ cleanup: vi.fn()
470
+ };
471
+
472
+ const mockRBACService = {
473
+ getPermissions: vi.fn(() => ({})),
474
+ getRoles: vi.fn(() => []),
475
+ getAccessLevel: vi.fn(() => 'VIEWER'),
476
+ isLoading: vi.fn(() => false),
477
+ getError: vi.fn(() => null),
478
+ getSelectedEventId: vi.fn(() => null),
479
+ getAppConfig: vi.fn(() => null),
480
+ getUserEventAccess: vi.fn(() => []),
481
+ isEventAccessLoading: vi.fn(() => false),
482
+ getSelectedOrganisationId: vi.fn(() => null),
483
+ hasPermission: vi.fn(),
484
+ hasAnyPermission: vi.fn(),
485
+ hasAllPermissions: vi.fn(),
486
+ hasRole: vi.fn(),
487
+ hasAccessLevel: vi.fn(),
488
+ canAccess: vi.fn(),
489
+ validatePermission: vi.fn(),
490
+ validateAccess: vi.fn(),
491
+ refreshPermissions: vi.fn(),
492
+ setSelectedEventId: vi.fn(),
493
+ loadUserEventAccess: vi.fn(),
494
+ requireOrganisationContext: vi.fn(() => 'org-1'),
495
+ subscribe: vi.fn(() => vi.fn()),
496
+ cleanup: vi.fn()
497
+ };
498
+
499
+ const mockOrganisationService = {
500
+ getSelectedOrganisation: vi.fn(() => mockOrganisation),
501
+ getOrganisations: vi.fn(() => [mockOrganisation]),
502
+ getUserMemberships: vi.fn(() => []),
503
+ isLoading: vi.fn(() => false),
504
+ getError: vi.fn(() => null),
505
+ hasValidOrganisationContext: vi.fn(() => true),
506
+ isContextReady: vi.fn(() => true),
507
+ switchOrganisation: vi.fn(),
508
+ getUserRole: vi.fn(),
509
+ validateOrganisationAccess: vi.fn(),
510
+ refreshOrganisations: vi.fn(),
511
+ ensureOrganisationContext: vi.fn(() => mockOrganisation),
512
+ isOrganisationSecure: vi.fn(() => true),
513
+ getPrimaryOrganisation: vi.fn(() => mockOrganisation),
514
+ subscribe: vi.fn(() => vi.fn()),
515
+ cleanup: vi.fn()
516
+ };
517
+
518
+ const mockEventService = {
519
+ getEvents: vi.fn(() => []),
520
+ getSelectedEvent: vi.fn(() => null),
521
+ isLoading: vi.fn(() => false),
522
+ getError: vi.fn(() => null),
523
+ setSelectedEvent: vi.fn(),
524
+ refreshEvents: vi.fn(),
525
+ subscribe: vi.fn(() => vi.fn()),
526
+ cleanup: vi.fn()
527
+ };
528
+
529
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
530
+ <div>{children}</div>
531
+ );
532
+
533
+ // Test individual hooks
534
+ mockUseContext.mockReturnValue({ authService: mockAuthService });
535
+ const { result: authResult } = renderHook(() => useAuth(), { wrapper });
536
+
537
+ mockUseContext.mockReturnValue({ rbacService: mockRBACService });
538
+ const { result: permissionsResult } = renderHook(() => usePermissions('user-1', { organisationId: 'org-1', eventId: 'event-1', appId: 'test-app' }), { wrapper });
539
+
540
+ mockUseContext.mockReturnValue({ organisationService: mockOrganisationService });
541
+ const { result: organisationResult } = renderHook(() => useCurrentOrganisation(), { wrapper });
542
+
543
+ mockUseContext.mockReturnValue({ eventService: mockEventService });
544
+ const { result: eventResult } = renderHook(() => useCurrentEvent(), { wrapper });
545
+
546
+ expect(authResult.current).toBeDefined();
547
+ expect(permissionsResult.current).toBeDefined();
548
+ expect(organisationResult.current).toBeDefined();
549
+ expect(eventResult.current).toBeDefined();
550
+ });
551
+ });
552
+
553
+ describe('Error Handling', () => {
554
+ it('should handle service errors gracefully', () => {
555
+ const mockService = {
556
+ getUser: vi.fn(() => null),
557
+ getSession: vi.fn(() => null),
558
+ isAuthenticated: vi.fn(() => false),
559
+ isLoading: vi.fn(() => false),
560
+ getError: vi.fn(() => new Error('Service error')),
561
+ getSupabaseClient: vi.fn(() => null),
562
+ signIn: vi.fn(),
563
+ signUp: vi.fn(),
564
+ signOut: vi.fn(),
565
+ resetPassword: vi.fn(),
566
+ updatePassword: vi.fn(),
567
+ refreshSession: vi.fn(),
568
+ subscribe: vi.fn(() => vi.fn()),
569
+ cleanup: vi.fn()
570
+ };
571
+
572
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
573
+ <div>{children}</div>
574
+ );
575
+
576
+ mockUseContext.mockReturnValue({ authService: mockService });
577
+
578
+ const { result } = renderHook(() => useAuth(), { wrapper });
579
+
580
+ expect(result.current.authError).toBeDefined();
581
+ expect(result.current.authError?.message).toBe('Service error');
582
+ });
583
+
584
+ it('should handle missing service methods gracefully', () => {
585
+ const mockService = {
586
+ getUser: vi.fn(),
587
+ getSession: vi.fn(),
588
+ isAuthenticated: vi.fn(),
589
+ isLoading: vi.fn(),
590
+ getError: vi.fn(() => null),
591
+ getSupabaseClient: vi.fn(),
592
+ signIn: vi.fn(),
593
+ signUp: vi.fn(),
594
+ signOut: vi.fn(),
595
+ resetPassword: vi.fn(),
596
+ updatePassword: vi.fn(),
597
+ refreshSession: vi.fn(),
598
+ subscribe: vi.fn(() => vi.fn()),
599
+ cleanup: vi.fn()
600
+ };
601
+
602
+ const wrapper = ({ children }: { children: React.ReactNode }) => (
603
+ <div>{children}</div>
604
+ );
605
+
606
+ mockUseContext.mockReturnValue({ authService: mockService });
607
+
608
+ // Should not throw error
609
+ expect(() => {
610
+ const { result } = renderHook(() => useAuth(), { wrapper });
611
+ expect(result.current).toBeDefined();
612
+ }).not.toThrow();
613
+ });
614
+ });
615
+ });
@@ -67,6 +67,8 @@
67
67
  import { useState, useEffect, useCallback, useMemo } from 'react';
68
68
  import type { SupabaseClient } from '@supabase/supabase-js';
69
69
  import type { Database } from '../../types/database';
70
+ import { getPublicUrl } from '../../utils/storage/helpers';
71
+ import { FileCategory } from '../../types/file-reference';
70
72
 
71
73
  // Simple in-memory cache for public data
72
74
  const publicDataCache = new Map<string, { data: any; timestamp: number; ttl: number }>();
@@ -176,36 +178,30 @@ export function usePublicEventLogo(
176
178
  setIsLoading(true);
177
179
  setError(null);
178
180
 
179
- // Call the public logo RPC function
180
- const { data, error: rpcError } = await (supabase as any).rpc('get_public_event_logo', {
181
- event_id_param: eventId,
182
- organisation_id_param: organisationId
183
- });
181
+ // Get event logo from file_references using new RPC function
182
+ const { data, error: rpcError } = await (supabase as any)
183
+ .rpc('data_file_reference_by_category_list', {
184
+ p_table_name: 'event',
185
+ p_record_id: eventId,
186
+ p_category: FileCategory.EVENT_LOGOS,
187
+ p_organisation_id: organisationId
188
+ });
184
189
 
185
190
  if (rpcError) {
186
191
  throw new Error(rpcError.message || 'Failed to fetch logo');
187
192
  }
188
193
 
189
- if (!data || data.length === 0 || !data[0] || !data[0].logo_url) {
194
+ if (!data || data.length === 0 || !data[0]?.file_path) {
190
195
  setLogoUrl(null);
191
196
  return;
192
197
  }
193
198
 
194
- // Get the file path from the RPC function (it returns file_path as logo_url)
195
- const logoPath = data[0].logo_url;
196
-
197
- // Construct the full public URL using Supabase client's storage API
198
- const { data: urlData } = supabase.storage
199
- .from('public-files')
200
- .getPublicUrl(logoPath);
201
-
202
- if (!urlData?.publicUrl) {
203
- console.warn('[usePublicEventLogo] Failed to construct public URL for path:', logoPath);
204
- setLogoUrl(null);
205
- return;
206
- }
199
+ // Get the file path from the RPC response
200
+ const logoPath = data[0].file_path;
201
+ const isPublic = data[0].is_public ?? true; // Event logos should be public
207
202
 
208
- const logoUrl = urlData.publicUrl;
203
+ // Generate public URL using bucket-aware helper (public-files bucket for public files)
204
+ const logoUrl = getPublicUrl(supabase, logoPath, isPublic);
209
205
 
210
206
  // Validate image existence if requested
211
207
  if (validateImage) {