@jmruthers/pace-core 0.5.186 → 0.5.188

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 (290) hide show
  1. package/dist/{DataTable-IX2NBUTP.js → DataTable-GUFUNZ3N.js} +7 -7
  2. package/dist/{DataTable-Z9NLVJh0.d.ts → DataTable-IVYljGJ6.d.ts} +1 -1
  3. package/dist/{PublicPageProvider-DIzEzwKl.d.ts → PublicPageProvider-DrLDztHt.d.ts} +211 -106
  4. package/dist/{UnifiedAuthProvider-A4BCQRJY.js → UnifiedAuthProvider-643PUAIM.js} +2 -2
  5. package/dist/{api-BMFCXVQX.js → api-YP7XD5L6.js} +3 -3
  6. package/dist/{audit-WRS3KJKI.js → audit-B5P6FFIR.js} +2 -2
  7. package/dist/{chunk-HGPQUCBC.js → chunk-2UUZZJFT.js} +3 -3
  8. package/dist/{chunk-445GEP27.js → chunk-3GOZZZYH.js} +33 -8
  9. package/dist/chunk-3GOZZZYH.js.map +1 -0
  10. package/dist/{chunk-FSFQFJCU.js → chunk-63FOKYGO.js} +174 -6
  11. package/dist/chunk-63FOKYGO.js.map +1 -0
  12. package/dist/{chunk-DAGICKHT.js → chunk-DDM4CCYT.js} +3 -3
  13. package/dist/{chunk-XAUHJD3L.js → chunk-E7UAOUMY.js} +2 -2
  14. package/dist/{chunk-HDCUMOOI.js → chunk-EFCLXK7F.js} +792 -559
  15. package/dist/chunk-EFCLXK7F.js.map +1 -0
  16. package/dist/{chunk-U6WNSFX5.js → chunk-HEHYGYOX.js} +279 -44
  17. package/dist/chunk-HEHYGYOX.js.map +1 -0
  18. package/dist/{chunk-GRIQLQ52.js → chunk-IM4QE42D.js} +27 -23
  19. package/dist/chunk-IM4QE42D.js.map +1 -0
  20. package/dist/{chunk-OALXJH4Y.js → chunk-IPCH26AG.js} +8 -8
  21. package/dist/chunk-IPCH26AG.js.map +1 -0
  22. package/dist/{chunk-UQWSHFVX.js → chunk-SAUPYVLF.js} +1 -1
  23. package/dist/{chunk-UQWSHFVX.js.map → chunk-SAUPYVLF.js.map} +1 -1
  24. package/dist/{chunk-TC7D3CR3.js → chunk-UNOTYLQF.js} +556 -101
  25. package/dist/chunk-UNOTYLQF.js.map +1 -0
  26. package/dist/{chunk-FXFJRTKI.js → chunk-VGZZXKBR.js} +5 -5
  27. package/dist/chunk-VGZZXKBR.js.map +1 -0
  28. package/dist/chunk-YHCN776L.js +447 -0
  29. package/dist/chunk-YHCN776L.js.map +1 -0
  30. package/dist/components.d.ts +4 -4
  31. package/dist/components.js +12 -10
  32. package/dist/components.js.map +1 -1
  33. package/dist/{file-reference-PRTSLxKx.d.ts → file-reference-D037xOFK.d.ts} +0 -1
  34. package/dist/hooks.d.ts +221 -6
  35. package/dist/hooks.js +146 -49
  36. package/dist/hooks.js.map +1 -1
  37. package/dist/index.d.ts +24 -9
  38. package/dist/index.js +62 -28
  39. package/dist/index.js.map +1 -1
  40. package/dist/providers.js +1 -1
  41. package/dist/rbac/index.d.ts +124 -7
  42. package/dist/rbac/index.js +27 -7
  43. package/dist/{types-DUyCRSTj.d.ts → types-Bwgl--Xo.d.ts} +162 -1
  44. package/dist/types.d.ts +1 -1
  45. package/dist/types.js +1 -1
  46. package/dist/{usePublicRouteParams-D71QLlg4.d.ts → usePublicRouteParams-CTDELQ7H.d.ts} +2 -2
  47. package/dist/utils.d.ts +213 -3
  48. package/dist/utils.js +22 -2
  49. package/dist/utils.js.map +1 -1
  50. package/docs/api/classes/ColumnFactory.md +1 -1
  51. package/docs/api/classes/ErrorBoundary.md +1 -1
  52. package/docs/api/classes/InvalidScopeError.md +1 -1
  53. package/docs/api/classes/Logger.md +1 -1
  54. package/docs/api/classes/MissingUserContextError.md +1 -1
  55. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  56. package/docs/api/classes/PermissionDeniedError.md +1 -1
  57. package/docs/api/classes/RBACAuditManager.md +21 -17
  58. package/docs/api/classes/RBACCache.md +31 -23
  59. package/docs/api/classes/RBACEngine.md +5 -5
  60. package/docs/api/classes/RBACError.md +1 -1
  61. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  62. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  63. package/docs/api/classes/StorageUtils.md +1 -1
  64. package/docs/api/enums/FileCategory.md +1 -1
  65. package/docs/api/enums/LogLevel.md +1 -1
  66. package/docs/api/enums/RBACErrorCode.md +1 -1
  67. package/docs/api/enums/RPCFunction.md +1 -1
  68. package/docs/api/interfaces/AddressFieldProps.md +241 -0
  69. package/docs/api/interfaces/AddressFieldRef.md +94 -0
  70. package/docs/api/interfaces/AggregateConfig.md +1 -1
  71. package/docs/api/interfaces/AutocompleteOptions.md +75 -0
  72. package/docs/api/interfaces/BadgeProps.md +1 -1
  73. package/docs/api/interfaces/ButtonProps.md +1 -1
  74. package/docs/api/interfaces/CalendarProps.md +1 -1
  75. package/docs/api/interfaces/CardProps.md +1 -1
  76. package/docs/api/interfaces/ColorPalette.md +1 -1
  77. package/docs/api/interfaces/ColorShade.md +1 -1
  78. package/docs/api/interfaces/ComplianceResult.md +1 -1
  79. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  80. package/docs/api/interfaces/DataRecord.md +1 -1
  81. package/docs/api/interfaces/DataTableAction.md +1 -1
  82. package/docs/api/interfaces/DataTableColumn.md +1 -1
  83. package/docs/api/interfaces/DataTableProps.md +1 -1
  84. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  85. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  86. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  87. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  88. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  89. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  90. package/docs/api/interfaces/ExportColumn.md +1 -1
  91. package/docs/api/interfaces/ExportOptions.md +1 -1
  92. package/docs/api/interfaces/FileDisplayProps.md +15 -15
  93. package/docs/api/interfaces/FileMetadata.md +1 -1
  94. package/docs/api/interfaces/FileReference.md +1 -1
  95. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  96. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  97. package/docs/api/interfaces/FileUploadProps.md +1 -1
  98. package/docs/api/interfaces/FooterProps.md +1 -1
  99. package/docs/api/interfaces/FormFieldProps.md +1 -1
  100. package/docs/api/interfaces/FormProps.md +1 -1
  101. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  102. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  103. package/docs/api/interfaces/InputProps.md +1 -1
  104. package/docs/api/interfaces/LabelProps.md +1 -1
  105. package/docs/api/interfaces/LoggerConfig.md +1 -1
  106. package/docs/api/interfaces/LoginFormProps.md +1 -1
  107. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  108. package/docs/api/interfaces/NavigationContextType.md +1 -1
  109. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  110. package/docs/api/interfaces/NavigationItem.md +1 -1
  111. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  112. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  113. package/docs/api/interfaces/Organisation.md +1 -1
  114. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  115. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  116. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  117. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  118. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  119. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  120. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  121. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  122. package/docs/api/interfaces/PagePermissionGuardProps.md +11 -11
  123. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  124. package/docs/api/interfaces/PaletteData.md +1 -1
  125. package/docs/api/interfaces/ParsedAddress.md +120 -0
  126. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  127. package/docs/api/interfaces/ProgressProps.md +1 -1
  128. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  131. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  132. package/docs/api/interfaces/QuickFix.md +1 -1
  133. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  134. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  135. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  136. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  137. package/docs/api/interfaces/RBACConfig.md +26 -3
  138. package/docs/api/interfaces/RBACContext.md +1 -1
  139. package/docs/api/interfaces/RBACLogger.md +5 -5
  140. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  141. package/docs/api/interfaces/RBACPerformanceMetrics.md +138 -0
  142. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  143. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  144. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  145. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  146. package/docs/api/interfaces/RBACResult.md +1 -1
  147. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  148. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  149. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  150. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  151. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  152. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  153. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  154. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  155. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  156. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  157. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  158. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  159. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  160. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  161. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  162. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  163. package/docs/api/interfaces/RouteConfig.md +1 -1
  164. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  165. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  166. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  167. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  168. package/docs/api/interfaces/SetupIssue.md +1 -1
  169. package/docs/api/interfaces/StorageConfig.md +1 -1
  170. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  171. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  172. package/docs/api/interfaces/StorageListOptions.md +1 -1
  173. package/docs/api/interfaces/StorageListResult.md +1 -1
  174. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  175. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  176. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  177. package/docs/api/interfaces/StyleImport.md +1 -1
  178. package/docs/api/interfaces/SwitchProps.md +1 -1
  179. package/docs/api/interfaces/TabsContentProps.md +1 -1
  180. package/docs/api/interfaces/TabsListProps.md +1 -1
  181. package/docs/api/interfaces/TabsProps.md +1 -1
  182. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  183. package/docs/api/interfaces/TextareaProps.md +1 -1
  184. package/docs/api/interfaces/ToastActionElement.md +1 -1
  185. package/docs/api/interfaces/ToastProps.md +1 -1
  186. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  187. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  188. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  189. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  190. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  191. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  192. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  193. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  194. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  195. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  196. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  197. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  198. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  199. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  200. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  201. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  202. package/docs/api/interfaces/UserEventAccess.md +1 -1
  203. package/docs/api/interfaces/UserMenuProps.md +1 -1
  204. package/docs/api/interfaces/UserProfile.md +1 -1
  205. package/docs/api/modules.md +318 -59
  206. package/docs/best-practices/performance.md +11 -0
  207. package/docs/getting-started/examples/README.md +2 -2
  208. package/docs/implementation-guides/file-upload-storage.md +29 -0
  209. package/docs/implementation-guides/public-pages.md +140 -1230
  210. package/docs/rbac/README.md +2 -1
  211. package/docs/rbac/api-reference.md +11 -0
  212. package/docs/rbac/performance.md +320 -0
  213. package/docs/standards/01-architecture-standard.md +5 -0
  214. package/docs/standards/05-security-standard.md +14 -0
  215. package/docs/standards/07-rbac-and-rls-standard.md +356 -0
  216. package/package.json +1 -1
  217. package/src/__tests__/public-recipe-view.test.ts +199 -0
  218. package/src/__tests__/rls-policies.test.ts +333 -0
  219. package/src/components/AddressField/AddressField.test.tsx +411 -0
  220. package/src/components/AddressField/AddressField.tsx +323 -0
  221. package/src/components/AddressField/README.md +336 -0
  222. package/src/components/AddressField/index.ts +10 -0
  223. package/src/components/AddressField/types.ts +65 -0
  224. package/src/components/FileDisplay/FileDisplay.test.tsx +454 -0
  225. package/src/components/FileDisplay/FileDisplay.tsx +28 -1
  226. package/src/components/index.ts +2 -0
  227. package/src/hooks/__tests__/useFileDisplay.unit.test.ts +30 -5
  228. package/src/hooks/__tests__/useOrganisationSecurity.unit.test.tsx +11 -10
  229. package/src/hooks/__tests__/usePublicFileDisplay.test.ts +31 -6
  230. package/src/hooks/index.ts +6 -0
  231. package/src/hooks/public/usePublicFileDisplay.ts +8 -10
  232. package/src/hooks/useAddressAutocomplete.test.ts +318 -0
  233. package/src/hooks/useAddressAutocomplete.ts +268 -0
  234. package/src/hooks/useFileDisplay.ts +3 -15
  235. package/src/hooks/useFileReference.test.ts +20 -3
  236. package/src/hooks/useFileReference.ts +3 -24
  237. package/src/hooks/useFileUrlCache.ts +246 -0
  238. package/src/hooks/useInactivityTracker.ts +31 -20
  239. package/src/hooks/useOrganisationSecurity.test.ts +10 -7
  240. package/src/hooks/useOrganisationSecurity.ts +3 -3
  241. package/src/hooks/useQueryCache.ts +315 -0
  242. package/src/index.ts +2 -0
  243. package/src/providers/services/EventServiceProvider.tsx +4 -1
  244. package/src/rbac/api.test.ts +21 -6
  245. package/src/rbac/api.ts +32 -11
  246. package/src/rbac/audit-batched.ts +223 -0
  247. package/src/rbac/audit-enhanced.ts +2 -2
  248. package/src/rbac/audit.test.ts +6 -5
  249. package/src/rbac/audit.ts +34 -6
  250. package/src/rbac/cache-invalidation.ts +63 -12
  251. package/src/rbac/cache.test.ts +2 -2
  252. package/src/rbac/cache.ts +61 -14
  253. package/src/rbac/components/PagePermissionGuard.tsx +19 -10
  254. package/src/rbac/components/__tests__/PagePermissionGuard.performance.test.tsx +248 -0
  255. package/src/rbac/config.ts +9 -0
  256. package/src/rbac/engine.ts +2 -21
  257. package/src/rbac/hooks/usePermissions.ts +21 -5
  258. package/src/rbac/index.ts +19 -0
  259. package/src/rbac/performance.ts +210 -0
  260. package/src/rbac/request-deduplication.ts +87 -0
  261. package/src/rbac/utils/deep-equal.ts +93 -0
  262. package/src/services/OrganisationService.ts +5 -4
  263. package/src/types/file-reference.ts +0 -1
  264. package/src/utils/file-reference/__tests__/file-reference.test.ts +31 -4
  265. package/src/utils/file-reference/index.ts +44 -15
  266. package/src/utils/google-places/googlePlacesUtils.test.ts +403 -0
  267. package/src/utils/google-places/googlePlacesUtils.ts +475 -0
  268. package/src/utils/google-places/index.ts +26 -0
  269. package/src/utils/google-places/loadGoogleMapsScript.ts +207 -0
  270. package/src/utils/google-places/types.ts +94 -0
  271. package/src/utils/index.ts +23 -0
  272. package/src/utils/request-deduplication.ts +165 -0
  273. package/src/utils/storage/helpers.ts +143 -4
  274. package/dist/chunk-445GEP27.js.map +0 -1
  275. package/dist/chunk-FMUCXFII.js +0 -76
  276. package/dist/chunk-FMUCXFII.js.map +0 -1
  277. package/dist/chunk-FSFQFJCU.js.map +0 -1
  278. package/dist/chunk-FXFJRTKI.js.map +0 -1
  279. package/dist/chunk-GRIQLQ52.js.map +0 -1
  280. package/dist/chunk-HDCUMOOI.js.map +0 -1
  281. package/dist/chunk-OALXJH4Y.js.map +0 -1
  282. package/dist/chunk-TC7D3CR3.js.map +0 -1
  283. package/dist/chunk-U6WNSFX5.js.map +0 -1
  284. /package/dist/{DataTable-IX2NBUTP.js.map → DataTable-GUFUNZ3N.js.map} +0 -0
  285. /package/dist/{UnifiedAuthProvider-A4BCQRJY.js.map → UnifiedAuthProvider-643PUAIM.js.map} +0 -0
  286. /package/dist/{api-BMFCXVQX.js.map → api-YP7XD5L6.js.map} +0 -0
  287. /package/dist/{audit-WRS3KJKI.js.map → audit-B5P6FFIR.js.map} +0 -0
  288. /package/dist/{chunk-HGPQUCBC.js.map → chunk-2UUZZJFT.js.map} +0 -0
  289. /package/dist/{chunk-DAGICKHT.js.map → chunk-DDM4CCYT.js.map} +0 -0
  290. /package/dist/{chunk-XAUHJD3L.js.map → chunk-E7UAOUMY.js.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/useZodForm.ts","../src/hooks/useFormDialog.ts","../src/hooks/useOrganisationSecurity.ts","../src/hooks/useOrganisationPermissions.ts","../src/utils/storage/index.ts","../src/hooks/public/usePublicEvent.ts","../src/hooks/public/usePublicEventLogo.ts","../src/hooks/public/usePublicRouteParams.ts"],"sourcesContent":["\n/**\n * Zod Form Hook\n * \n * Enhanced form hook with Zod validation\n */\n\nimport { useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { z } from 'zod';\n\ninterface UseZodFormProps<T extends z.ZodTypeAny> {\n schema: T;\n defaultValues?: Partial<z.infer<T>>;\n mode?: 'onSubmit' | 'onBlur' | 'onChange' | 'onTouched' | 'all';\n}\n\nexport function useZodForm<T extends z.ZodTypeAny>({\n schema,\n defaultValues,\n mode = 'onSubmit'\n}: UseZodFormProps<T>) {\n return useForm<z.infer<T>>({\n resolver: zodResolver(schema),\n defaultValues: defaultValues as any,\n mode\n });\n}\n","/**\n * @file useFormDialog Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.1.0\n * \n * Generic React hook for managing form dialog state (open/close, form data, reset behavior).\n * \n * @example\n * ```ts\n * interface ContactFormData {\n * name: string;\n * email: string;\n * }\n * \n * function ContactDialog() {\n * const { isOpen, formData, openDialog, closeDialog, handleOpenChange } = \n * useFormDialog<ContactFormData>();\n * \n * // Use in Dialog component\n * // <Dialog open={isOpen} onOpenChange={handleOpenChange}>...</Dialog>\n * }\n * ```\n */\n\nimport { useState, useCallback } from 'react';\n\n/**\n * Options for configuring the useFormDialog hook behavior\n */\nexport interface UseFormDialogOptions<T = unknown> {\n /**\n * Callback invoked when the dialog open state changes\n * @param open - The new open state\n */\n onOpenChange?: (open: boolean) => void;\n \n /**\n * Whether to reset form data when the dialog closes\n * @default true\n */\n resetOnClose?: boolean;\n}\n\n/**\n * Return type for the useFormDialog hook\n */\nexport interface UseFormDialogReturn<T = unknown> {\n /**\n * Current open state of the dialog\n */\n isOpen: boolean;\n \n /**\n * Current form data (null when dialog is closed or no data provided)\n */\n formData: T | null;\n \n /**\n * Open the dialog with optional form data\n * @param data - Optional form data to populate the dialog\n */\n openDialog: (data?: T | null) => void;\n \n /**\n * Close the dialog\n */\n closeDialog: () => void;\n \n /**\n * Handler for controlled dialog components (e.g., Radix UI Dialog)\n * @param open - The new open state\n */\n handleOpenChange: (open: boolean) => void;\n}\n\n/**\n * Generic React hook for managing form dialog state.\n * \n * Provides state management for form dialogs including:\n * - Open/close state\n * - Form data management\n * - Automatic reset on close (configurable)\n * - Controlled and uncontrolled usage patterns\n * \n * @param options - Configuration options for the hook\n * @returns Object containing dialog state and control functions\n * \n * @example\n * ```ts\n * // Basic usage\n * const { isOpen, openDialog, closeDialog } = useFormDialog();\n * \n * // With form data\n * interface UserData {\n * id: string;\n * name: string;\n * }\n * const { isOpen, formData, openDialog } = useFormDialog<UserData>();\n * \n * // Open with data\n * openDialog({ id: '1', name: 'John' });\n * \n * // Controlled usage with callback\n * const { handleOpenChange, isOpen } = useFormDialog({\n * onOpenChange: (open) => console.log('Dialog state:', open),\n * resetOnClose: false\n * });\n * ```\n */\nexport function useFormDialog<T = unknown>({\n onOpenChange,\n resetOnClose = true,\n}: UseFormDialogOptions<T> = {}): UseFormDialogReturn<T> {\n const [isOpen, setIsOpen] = useState(false);\n const [formData, setFormData] = useState<T | null>(null);\n\n const openDialog = useCallback((data: T | null = null) => {\n setFormData(data);\n setIsOpen(true);\n onOpenChange?.(true);\n }, [onOpenChange]);\n\n const closeDialog = useCallback(() => {\n setIsOpen(false);\n if (resetOnClose) {\n setFormData(null);\n }\n onOpenChange?.(false);\n }, [resetOnClose, onOpenChange]);\n\n const handleOpenChange = useCallback((open: boolean) => {\n setIsOpen(open);\n if (!open && resetOnClose) {\n setFormData(null);\n }\n onOpenChange?.(open);\n }, [resetOnClose, onOpenChange]);\n\n return {\n isOpen,\n formData,\n openDialog,\n closeDialog,\n handleOpenChange,\n };\n}\n","/**\n * @file Organisation Security Hook\n * @package @jmruthers/pace-core\n * @module Hooks/OrganisationSecurity\n * @since 0.4.0\n *\n * Security-focused hook for organisation access validation and super admin functionality.\n * Provides utilities for validating user access to organisations and checking permissions.\n */\n\nimport { useCallback, useMemo, useEffect, useState } from 'react';\nimport { useUnifiedAuth } from '../providers';\nimport { useOrganisations } from './useOrganisations';\n// Legacy useRBAC hook removed - use new RBAC system instead\nimport type { OrganisationSecurityError, SuperAdminContext } from '../types/organisation';\nimport type { Permission } from '../rbac/types';\nimport { logger } from '../utils/core/logger';\n\nexport interface OrganisationSecurityHook {\n // Super admin context\n superAdminContext: SuperAdminContext;\n \n // Access validation\n validateOrganisationAccess: (orgId: string) => Promise<boolean>;\n hasMinimumRole: (minRole: string, orgId?: string) => boolean;\n canAccessChildOrganisations: (orgId?: string) => boolean;\n \n // Permission checks\n hasPermission: (permission: string, orgId?: string) => Promise<boolean>;\n getUserPermissions: (orgId?: string) => Promise<string[]>;\n \n // Audit logging\n logOrganisationAccess: (action: string, details?: Record<string, unknown>) => Promise<void>;\n \n // Security utilities\n ensureOrganisationAccess: (orgId: string) => Promise<void>;\n validateUserAccess: (userId: string, orgId: string) => Promise<boolean>;\n}\n\nexport const useOrganisationSecurity = (): OrganisationSecurityHook => {\n const { user, session, supabase } = useUnifiedAuth();\n const { selectedOrganisation, getUserRole, validateOrganisationAccess: validateAccess } = useOrganisations();\n \n // Super admin status - query database for security (user_metadata can be spoofed)\n const [isSuperAdmin, setIsSuperAdmin] = useState<boolean>(false);\n const [isCheckingSuperAdmin, setIsCheckingSuperAdmin] = useState(false);\n\n // Check super admin status from database\n useEffect(() => {\n if (!user || !session || !supabase) {\n setIsSuperAdmin(false);\n return;\n }\n\n const checkSuperAdmin = async () => {\n setIsCheckingSuperAdmin(true);\n try {\n const now = new Date().toISOString();\n const { data, error } = await supabase\n .from('rbac_global_roles')\n .select('role')\n .eq('user_id', user.id)\n .eq('role', 'super_admin')\n .lte('valid_from', now)\n .or(`valid_to.is.null,valid_to.gte.${now}`)\n .limit(1);\n\n setIsSuperAdmin(!error && data && data.length > 0);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error checking super admin status:', error);\n setIsSuperAdmin(false);\n } finally {\n setIsCheckingSuperAdmin(false);\n }\n };\n\n checkSuperAdmin();\n }, [user, session, supabase]);\n\n // Super admin context\n const superAdminContext = useMemo((): SuperAdminContext => {\n return {\n isSuperAdmin,\n hasGlobalAccess: isSuperAdmin,\n canManageAllOrganisations: isSuperAdmin\n };\n }, [isSuperAdmin]);\n\n // Validate organisation access with database check\n const validateOrganisationAccess = useCallback(async (orgId: string): Promise<boolean> => {\n if (!user || !session || !supabase) return false;\n \n try {\n // Super admin has access to all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Check organisation membership using consolidated rbac_organisation_roles table\n const { data, error } = await supabase\n .from('rbac_organisation_roles')\n .select('id')\n .eq('user_id', user.id)\n .eq('organisation_id', orgId)\n .eq('status', 'active')\n .is('revoked_at', null)\n .in('role', ['org_admin', 'leader', 'member']) // Only actual members, not supporters\n .single();\n\n if (error) {\n logger.error('useOrganisationSecurity', 'Error validating organisation access:', error);\n return false;\n }\n\n return !!data;\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating organisation access:', error);\n return false;\n }\n }, [user, session, supabase, superAdminContext.isSuperAdmin]);\n\n // Check if user has minimum role\n const hasMinimumRole = useCallback((minRole: string, orgId?: string): boolean => {\n // Super admin has all roles\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n const roleHierarchy = ['supporter', 'member', 'leader', 'org_admin'];\n \n const userRoleIndex = roleHierarchy.indexOf(userRole);\n const minRoleIndex = roleHierarchy.indexOf(minRole);\n \n return userRoleIndex >= minRoleIndex;\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check if user can access child organisations\n const canAccessChildOrganisations = useCallback((orgId?: string): boolean => {\n // Super admin can access all organisations\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId) return false;\n\n const userRole = getUserRole(targetOrgId);\n return userRole === 'org_admin';\n }, [selectedOrganisation, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Check specific permission using the new RBAC system\n const hasPermission = useCallback(async (permission: string, orgId?: string): Promise<boolean> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return false;\n\n try {\n // Use the new RBAC system\n const { isPermitted } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n return await isPermitted({\n userId: user.id,\n scope,\n permission: permission as Permission\n });\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception checking permission:', error);\n return false;\n }\n }, [selectedOrganisation, user, superAdminContext.isSuperAdmin]);\n\n // Get user's permissions for organisation using the new RBAC system\n const getUserPermissions = useCallback(async (orgId?: string): Promise<string[]> => {\n // Super admin has all permissions\n if (superAdminContext.isSuperAdmin) {\n return ['*']; // All permissions\n }\n\n const targetOrgId = orgId || selectedOrganisation?.id;\n if (!targetOrgId || !user) return [];\n\n try {\n // Use the new RBAC system\n const { getPermissionMap } = await import('../rbac/api');\n \n const scope = {\n organisationId: targetOrgId,\n eventId: user.user_metadata?.eventId || user.app_metadata?.eventId,\n appId: user.user_metadata?.appId || user.app_metadata?.appId,\n };\n\n const permissionMap = await getPermissionMap({\n userId: user.id,\n scope\n });\n \n // Flatten all permissions from all pages\n const allPermissions = Object.entries(permissionMap)\n .filter(([, allowed]) => allowed)\n .map(([permission]) => permission);\n return [...new Set(allPermissions)]; // Remove duplicates\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception getting user permissions:', error);\n return [];\n }\n }, [selectedOrganisation, user, getUserRole, superAdminContext.isSuperAdmin]);\n\n // Log organisation access for audit using the new RBAC audit system\n const logOrganisationAccess = useCallback(async (action: string, details?: Record<string, unknown>): Promise<void> => {\n if (!user || !selectedOrganisation) return;\n\n try {\n // Use the new RBAC audit system - only if we have a valid organisation ID\n if (selectedOrganisation.id) {\n const { emitAuditEvent } = await import('../rbac/audit');\n \n await emitAuditEvent({\n type: 'permission_check',\n userId: user.id,\n organisationId: selectedOrganisation.id,\n permission: action,\n decision: true, // Assume access was granted if we're logging it\n source: 'api',\n duration_ms: 0, // No actual permission check performed here\n metadata: details || {}\n });\n }\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Error logging organisation access:', error);\n }\n }, [user, selectedOrganisation]);\n\n // Ensure organisation access (throws if no access)\n const ensureOrganisationAccess = useCallback(async (orgId: string): Promise<void> => {\n const hasAccess = await validateOrganisationAccess(orgId);\n \n if (!hasAccess) {\n const error = new Error(`User does not have access to organisation ${orgId}`) as OrganisationSecurityError;\n error.name = 'OrganisationSecurityError';\n error.code = 'ACCESS_DENIED';\n error.organisationId = orgId;\n error.userId = user?.id;\n throw error;\n }\n }, [validateOrganisationAccess, user]);\n\n // Validate user access (for admin functions)\n const validateUserAccess = useCallback(async (userId: string, orgId: string): Promise<boolean> => {\n if (!supabase) return false;\n\n try {\n // Super admin can validate any user\n if (superAdminContext.isSuperAdmin) {\n return true;\n }\n\n // Regular users can only validate their own access\n if (userId !== user?.id) {\n return false;\n }\n\n return await validateOrganisationAccess(orgId);\n } catch (error) {\n logger.error('useOrganisationSecurity', 'Exception validating user access:', error);\n return false;\n }\n }, [supabase, superAdminContext.isSuperAdmin, user, validateOrganisationAccess]);\n\n return {\n superAdminContext,\n validateOrganisationAccess,\n hasMinimumRole,\n canAccessChildOrganisations,\n hasPermission,\n getUserPermissions,\n logOrganisationAccess,\n ensureOrganisationAccess,\n validateUserAccess\n };\n}; ","/**\n * @file useOrganisationPermissions Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useOrganisationPermissions\n * @since 0.4.0\n *\n * Hook for managing organisation-specific permissions and role validation.\n * Provides secure access to user's role and permissions within organisations.\n *\n * @example\n * ```tsx\n * function OrganisationComponent() {\n * const { \n * isOrgAdmin, \n * canManageMembers,\n * userRole,\n * hasOrganisationAccess\n * } = useOrganisationPermissions();\n * \n * return (\n * <div>\n * {isOrgAdmin && <AdminPanel />}\n * {canManageMembers && <MemberManagement />}\n * <p>Your role: {userRole}</p>\n * </div>\n * );\n * }\n * \n * // For specific organisation\n * function MultiOrgComponent() {\n * const permissions = useOrganisationPermissions('org-123');\n * \n * if (!permissions.hasOrganisationAccess) {\n * return <div>No access to this organisation</div>;\n * }\n * \n * return <div>Role in org-123: {permissions.userRole}</div>;\n * }\n * ```\n *\n * @security\n * - Validates user membership in organisation\n * - Provides role-based permission checks\n * - Ensures secure access to organisation data\n * - Real-time permission validation\n */\n\nimport { useMemo } from 'react';\nimport { useOrganisations } from './useOrganisations';\nimport { useOrganisationSecurity } from './useOrganisationSecurity';\nimport type { OrganisationRole, OrganisationPermission } from '../types/organisation';\n\nexport interface UseOrganisationPermissionsReturn {\n /** User's role in the organisation */\n userRole: OrganisationRole | 'no_access';\n \n /** Whether user has organisation admin role */\n isOrgAdmin: boolean;\n \n /** Whether user is a super admin */\n isSuperAdmin: boolean;\n \n /** Whether user can moderate content */\n canModerate: boolean;\n \n /** Whether user can manage members */\n canManageMembers: boolean;\n \n /** Whether user can manage organisation settings */\n canManageSettings: boolean;\n \n /** Whether user can manage events */\n canManageEvents: boolean;\n \n /** Whether user has any admin privileges */\n hasAdminPrivileges: boolean;\n \n /** Whether user has access to the organisation */\n hasOrganisationAccess: boolean;\n \n /** Check if user has specific permission */\n hasPermission: (permission: OrganisationPermission) => boolean;\n \n /** Get all permissions for the user */\n getAllPermissions: () => OrganisationPermission[];\n \n /** Organisation ID being checked */\n organisationId: string;\n}\n\n/**\n * Hook to access organisation-specific permissions and roles\n * \n * @param orgId - Optional organisation ID. Defaults to currently selected organisation\n * @returns Organisation permissions and role information\n */\nexport function useOrganisationPermissions(orgId?: string): UseOrganisationPermissionsReturn {\n const { \n selectedOrganisation, \n getUserRole, \n validateOrganisationAccess,\n ensureOrganisationContext\n } = useOrganisations();\n \n // Get super admin context if available (may not be available in all contexts)\n let superAdminContext: { isSuperAdmin: boolean } = { isSuperAdmin: false };\n try {\n superAdminContext = useOrganisationSecurity().superAdminContext;\n } catch {\n // Not available in this context, default to false\n }\n\n const organisationId = useMemo(() => {\n if (orgId) {\n return orgId;\n }\n try {\n const currentOrg = ensureOrganisationContext();\n return currentOrg.id;\n } catch {\n return '';\n }\n }, [orgId, ensureOrganisationContext]);\n\n const userRole = useMemo(() => {\n if (!organisationId) return 'no_access';\n const role = getUserRole(organisationId);\n // Map to valid OrganisationRole or 'no_access'\n if (role === 'org_admin' || role === 'leader' || role === 'member' || role === 'supporter') {\n return role as OrganisationRole;\n }\n return 'no_access';\n }, [organisationId, getUserRole]);\n\n const hasOrganisationAccess = useMemo(() => {\n if (!organisationId) return false;\n return validateOrganisationAccess(organisationId);\n }, [organisationId, validateOrganisationAccess]);\n\n const permissions = useMemo(() => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return {\n isOrgAdmin: false,\n isSuperAdmin: false,\n canModerate: false,\n canManageMembers: false,\n canManageSettings: false,\n canManageEvents: false,\n hasAdminPrivileges: false\n };\n }\n\n const isOrgAdmin = userRole === 'org_admin';\n const isLeader = userRole === 'leader';\n const isMember = userRole === 'member';\n const isSupporter = userRole === 'supporter';\n\n // Super admin status - database backed (user_metadata can be spoofed)\n // Get super admin status from the security hook\n const isSuperAdmin = superAdminContext.isSuperAdmin;\n\n return {\n isOrgAdmin,\n isSuperAdmin,\n canModerate: isSuperAdmin || isOrgAdmin || isLeader,\n canManageMembers: isSuperAdmin || isOrgAdmin || isLeader, // Leaders can manage members\n canManageSettings: isSuperAdmin || isOrgAdmin,\n canManageEvents: isSuperAdmin || isOrgAdmin || isLeader,\n hasAdminPrivileges: isSuperAdmin || isOrgAdmin || isLeader // Leaders have admin privileges\n };\n }, [hasOrganisationAccess, userRole]);\n\n const hasPermission = useMemo(() => {\n return (permission: OrganisationPermission): boolean => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return false;\n }\n\n // Super admin has all permissions (org_admin acts as super admin within org)\n if (userRole === 'org_admin' || permission === '*') {\n return true;\n }\n\n // Map permissions to roles using the defined permissions from organisation types\n const rolePermissions: Record<OrganisationRole, OrganisationPermission[]> = {\n supporter: ['view_basic'],\n member: ['view_basic', 'view_details'],\n leader: ['view_basic', 'view_details', 'moderate_content', 'manage_events'],\n org_admin: ['view_basic', 'view_details', 'moderate_content', 'manage_events', 'manage_members', 'manage_settings']\n };\n\n const userPermissions = rolePermissions[userRole as OrganisationRole] || [];\n return userPermissions.includes(permission) || userPermissions.includes('*');\n };\n }, [hasOrganisationAccess, userRole]);\n\n const getAllPermissions = useMemo(() => {\n return (): OrganisationPermission[] => {\n if (!hasOrganisationAccess || userRole === 'no_access') {\n return [];\n }\n\n const rolePermissions: Record<OrganisationRole, OrganisationPermission[]> = {\n supporter: ['view_basic'],\n member: ['view_basic', 'view_details'],\n leader: ['view_basic', 'view_details', 'moderate_content', 'manage_events'],\n org_admin: ['view_basic', 'view_details', 'moderate_content', 'manage_events', 'manage_members', 'manage_settings']\n };\n\n return rolePermissions[userRole as OrganisationRole] || [];\n };\n }, [hasOrganisationAccess, userRole]);\n\n return useMemo(() => ({\n userRole,\n organisationId,\n hasOrganisationAccess,\n hasPermission,\n getAllPermissions,\n ...permissions\n }), [userRole, organisationId, hasOrganisationAccess, hasPermission, getAllPermissions, permissions]);\n} ","/**\n * Storage utilities for pace-core\n * \n * Provides app-segregated file storage with organisation-scoped access\n */\n\nexport * from './types';\nexport * from './config';\nexport * from './helpers';\n\n// Import functions for StorageUtils class\nimport {\n uploadFile,\n getPublicUrl,\n getSignedUrl,\n deleteFile,\n downloadFile,\n listFiles,\n archiveFile,\n generateFilePath,\n generateUniqueFileName,\n extractFileMetadata\n} from './helpers';\n\n// Re-export commonly used functions for convenience\nexport {\n uploadFile,\n getPublicUrl,\n getSignedUrl,\n deleteFile,\n downloadFile,\n listFiles,\n archiveFile,\n generateFilePath,\n generateUniqueFileName,\n extractFileMetadata\n};\n\nexport {\n validateFileSize,\n getFileSizeLimit,\n formatFileSize\n} from './config';\n\n\nexport {\n FILE_SIZE_LIMITS,\n DEFAULT_FILE_SIZE_LIMIT,\n STORAGE_CONFIG,\n APP_PATH_MAPPING\n} from './config';\n\n/**\n * StorageUtils class for convenient access to storage functions\n */\nexport class StorageUtils {\n static generateFilePath = generateFilePath;\n static generateUniqueFileName = generateUniqueFileName;\n static extractFileMetadata = extractFileMetadata;\n static uploadFile = uploadFile;\n static getPublicUrl = getPublicUrl;\n static getSignedUrl = getSignedUrl;\n static deleteFile = deleteFile;\n static downloadFile = downloadFile;\n static listFiles = listFiles;\n static archiveFile = archiveFile;\n}\n","/**\n * @file Public Event Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing public event data without authentication.\n * Provides event information by event_code for public pages.\n *\n * Features:\n * - No authentication required\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Automatic refetch capabilities\n *\n * @example\n * ```tsx\n * import { usePublicEvent } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * const { eventCode } = usePublicRouteParams();\n * const { event, isLoading, error, refetch } = usePublicEvent(eventCode);\n *\n * if (isLoading) return <div>Loading event...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (!event) return <div>Event not found</div>;\n *\n * return (\n * <div>\n * <h1>{event.event_name}</h1>\n * <p>Date: {event.event_date}</p>\n * <p>Venue: {event.event_venue}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible public event display\n * - Supports screen reader friendly loading states\n *\n * @security\n * - Only returns public-safe event data\n * - Validates event_code before querying\n * - No sensitive information exposed\n * - Rate limiting applied at database level\n *\n * @performance\n * - Built-in caching with TTL\n * - Minimal re-renders with stable references\n * - Lazy loading support\n * - Error boundary integration\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - @supabase/supabase-js - Database integration\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Event } from '../../types/event';\nimport type { Database } from '../../types/database';\nimport { assertOrganisationId } from '../../types/core';\nimport { usePublicPageContext } from '../../components/PublicLayout/PublicPageProvider';\nimport { logger } from '../../utils/core/logger';\n\n// Simple in-memory cache for public data\nconst publicDataCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicEventReturn {\n /** The event data, null if not loaded or not found */\n event: Event | null;\n /** Whether the data is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the data */\n refetch: () => Promise<void>;\n}\n\nexport interface UsePublicEventOptions {\n /** Cache TTL in milliseconds (default: 5 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n}\n\n/**\n * Hook for accessing public event data by event_code\n * \n * This hook provides access to public event information without requiring\n * authentication. It includes caching, error handling, and loading states.\n * \n * @param eventCode - The event code to look up\n * @param options - Configuration options for caching and behavior\n * @returns Object containing event data, loading state, error, and refetch function\n */\nexport function usePublicEvent(\n eventCode: string,\n options: UsePublicEventOptions = {}\n): UsePublicEventReturn {\n const {\n cacheTtl = 5 * 60 * 1000, // 5 minutes\n enableCache = true\n } = options;\n\n const [event, setEvent] = useState<Event | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(true);\n const [error, setError] = useState<Error | null>(null);\n\n // Get environment variables from public page context or fallback to direct access\n let environment: { supabaseUrl: string | null; supabaseKey: string | null };\n \n try {\n environment = usePublicPageContext().environment;\n } catch {\n // Fallback to direct environment variable access if not in PublicPageProvider\n environment = {\n supabaseUrl: (import.meta as any).env?.VITE_SUPABASE_URL || (import.meta as any).env?.NEXT_PUBLIC_SUPABASE_URL || null,\n supabaseKey: (import.meta as any).env?.VITE_SUPABASE_ANON_KEY || (import.meta as any).env?.NEXT_PUBLIC_SUPABASE_ANON_KEY || null\n };\n }\n \n // Create a simple Supabase client for public access\n const supabase = useMemo(() => {\n if (typeof window === 'undefined') return null;\n \n if (!environment.supabaseUrl || !environment.supabaseKey) {\n logger.warn('usePublicEvent', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY are set in your environment. Use publishable key if anon key is disabled.');\n return null;\n }\n\n return createClient<Database>(environment.supabaseUrl, environment.supabaseKey);\n }, [environment.supabaseUrl, environment.supabaseKey]);\n\n // Helper function to try refreshing schema cache\n const refreshSchemaCache = useCallback(async () => {\n try {\n // Try to trigger a schema refresh by querying a system table\n await (supabase as any).from('information_schema.routines').select('routine_name').limit(1);\n } catch (error) {\n // Ignore errors, this is just an attempt to refresh cache\n logger.debug('usePublicEvent', 'Schema cache refresh attempt failed:', error);\n }\n }, [supabase]);\n\n const fetchEvent = useCallback(async (): Promise<void> => {\n if (!eventCode || !supabase) {\n setError(new Error('Invalid event code or Supabase client not available'));\n setIsLoading(false);\n return;\n }\n\n // Check cache first\n const cacheKey = `public_event_${eventCode}`;\n if (enableCache) {\n const cached = publicDataCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n setEvent(cached.data);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n let eventData: any = null;\n\n try {\n // Try to call the public event RPC function first\n const response = await (supabase as any).rpc('get_public_event_by_code', {\n event_code_param: eventCode\n });\n \n const data = response?.data;\n const rpcError = response?.error;\n\n if (rpcError) {\n // If RPC function doesn't exist or schema cache issue, try refresh first, then fallback\n if (rpcError.message?.includes('Could not find the function') || \n rpcError.message?.includes('does not exist') ||\n rpcError.message?.includes('schema cache')) {\n logger.warn('usePublicEvent', 'RPC function not found or schema cache issue, attempting refresh:', rpcError.message);\n \n // Try to refresh schema cache first\n await refreshSchemaCache();\n \n // Try RPC call one more time after refresh\n try {\n const retryResponse = await (supabase as any).rpc('get_public_event_by_code', {\n event_code_param: eventCode\n });\n \n const retryData = retryResponse?.data;\n const retryError = retryResponse?.error;\n \n if (!retryError && retryData && retryData.length > 0) {\n eventData = retryData[0];\n } else {\n throw new Error('RPC still failing after cache refresh');\n }\n } catch (retryError) {\n logger.warn('usePublicEvent', 'RPC still failing after cache refresh, falling back to direct table access');\n \n // Fallback: Direct table access with public RLS policy\n const tableResponse2 = await (supabase as any)\n .from('event')\n .select(`\n event_id,\n event_name,\n event_date,\n event_venue,\n event_participants,\n event_colours,\n organisation_id,\n event_days,\n event_typicalunit,\n event_rounddown,\n event_youthmultiplier,\n event_catering_email,\n event_news,\n event_billing,\n event_email\n `)\n .eq('event_code', eventCode)\n .eq('is_visible', true)\n .not('organisation_id', 'is', null)\n .limit(1)\n .single();\n\n const tableData = tableResponse2?.data;\n const tableError = tableResponse2?.error;\n\n if (tableError) {\n throw new Error(tableError?.message || 'Failed to fetch event from table');\n }\n\n if (!tableData) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n\n // Get event logo from file_references\n const logoResponse = await (supabase as any)\n .from('file_references')\n .select('file_path')\n .eq('table_name', 'event')\n .eq('record_id', tableData.event_id)\n .eq('is_public', true)\n .eq('file_metadata->>category', 'event_logos')\n .limit(1)\n .single();\n \n const logoData = logoResponse?.data;\n\n eventData = {\n ...tableData,\n event_logo: logoData?.file_path || null\n };\n }\n } else {\n // For RPC errors that aren't schema cache issues, throw immediately without fallback\n const errorMessage = rpcError?.message || rpcError?.toString() || 'Failed to fetch event';\n setEvent(null);\n setError(new Error(errorMessage));\n setIsLoading(false);\n return;\n }\n } else {\n if (!data || data.length === 0 || !data[0]) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n eventData = data[0];\n }\n } catch (rpcError) {\n // If RPC call fails for any reason (including schema cache issues), try direct table access\n logger.warn('usePublicEvent', 'RPC call failed, falling back to direct table access:', rpcError);\n \n const tableResponse = await (supabase as any)\n .from('event')\n .select(`\n event_id,\n event_name,\n event_date,\n event_venue,\n event_participants,\n event_colours,\n organisation_id,\n event_days,\n event_typicalunit,\n event_rounddown,\n event_youthmultiplier,\n event_catering_email,\n event_news,\n event_billing,\n event_email\n `)\n .eq('event_code', eventCode)\n .eq('is_visible', true)\n .not('organisation_id', 'is', null)\n .limit(1)\n .single();\n\n const tableData = tableResponse?.data;\n const tableError = tableResponse?.error;\n\n if (tableError) {\n throw new Error(tableError?.message || 'Failed to fetch event from table');\n }\n\n if (!tableData) {\n setEvent(null);\n setError(new Error('Event not found'));\n return;\n }\n\n // Get event logo from file_references\n const logoResponse = await (supabase as any)\n .from('file_references')\n .select('file_path')\n .eq('table_name', 'event')\n .eq('record_id', tableData.event_id)\n .eq('is_public', true)\n .eq('file_metadata->>category', 'event_logos')\n .limit(1)\n .single();\n \n const logoData = logoResponse?.data;\n\n eventData = {\n ...tableData,\n event_logo: logoData?.file_path || null\n };\n }\n \n // Transform to Event type\n const transformedEvent: Event = {\n id: eventData.event_id,\n event_id: eventData.event_id,\n event_name: eventData.event_name,\n event_code: eventCode,\n event_date: eventData.event_date,\n event_venue: eventData.event_venue,\n event_participants: eventData.event_participants,\n event_logo: eventData.event_logo,\n event_colours: eventData.event_colours,\n organisation_id: assertOrganisationId(eventData.organisation_id),\n is_visible: true,\n created_at: new Date().toISOString(),\n updated_at: new Date().toISOString()\n };\n\n setEvent(transformedEvent);\n\n // Cache the result\n if (enableCache) {\n publicDataCache.set(cacheKey, {\n data: transformedEvent,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n logger.error('usePublicEvent', 'Error fetching event:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setEvent(null);\n } finally {\n setIsLoading(false);\n }\n }, [eventCode, supabase, cacheTtl, enableCache]);\n\n // Fetch event when eventCode changes\n useEffect(() => {\n fetchEvent();\n }, [fetchEvent]);\n\n const refetch = useCallback(async (): Promise<void> => {\n // Clear cache for this event\n if (enableCache) {\n const cacheKey = `public_event_${eventCode}`;\n publicDataCache.delete(cacheKey);\n }\n await fetchEvent();\n }, [fetchEvent, eventCode, enableCache]);\n\n return {\n event,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public event data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicEventCache(): void {\n for (const [key] of publicDataCache) {\n if (key.startsWith('public_event_')) {\n publicDataCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicEventCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicDataCache.keys()).filter(key => key.startsWith('public_event_'));\n return {\n size: keys.length,\n keys\n };\n}\n","/**\n * @file Public Event Logo Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing public event logo URLs without authentication.\n * Provides logo URLs with fallback handling for public pages.\n *\n * Features:\n * - No authentication required\n * - Automatic fallback to event initials\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Image validation\n *\n * @example\n * ```tsx\n * import { usePublicEventLogo } from '@jmruthers/pace-core';\n *\n * function EventHeader() {\n * const { logoUrl, fallbackText, isLoading, error } = usePublicEventLogo(\n * eventId, \n * eventName, \n * organisationId\n * );\n *\n * if (isLoading) return <div>Loading logo...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return (\n * <div>\n * {logoUrl ? (\n * <img src={logoUrl} alt={`${eventName} logo`} />\n * ) : (\n * <div className=\"logo-fallback\">{fallbackText}</div>\n * )}\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible logo display with proper alt text\n * - Supports screen reader friendly fallbacks\n *\n * @security\n * - Only returns public-safe logo URLs\n * - Validates image existence before returning URL\n * - No sensitive information exposed\n * - Rate limiting applied at storage level\n *\n * @performance\n * - Built-in caching with TTL\n * - Image validation and optimization\n * - Minimal re-renders with stable references\n * - Lazy loading support\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - @supabase/supabase-js - Storage integration\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { createLogger } from '../../utils/core/logger';\n\nconst log = createLogger('usePublicEventLogo');\n\n// Simple in-memory cache for public data\nconst publicDataCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicEventLogoReturn {\n /** The logo URL if available, null if not found or error */\n logoUrl: string | null;\n /** Fallback text (event initials) if no logo is available */\n fallbackText: string;\n /** Whether the logo is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the logo */\n refetch: () => Promise<void>;\n}\n\nexport interface UsePublicEventLogoOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Whether to validate image existence (default: true) */\n validateImage?: boolean;\n /** Custom fallback text generator */\n generateFallbackText?: (eventName: string) => string;\n /** Supabase client instance (required) */\n supabase: SupabaseClient<Database>;\n}\n\n/**\n * Generate fallback text from event name (first letter of each word)\n */\nfunction defaultGenerateFallbackText(eventName: string): string {\n if (!eventName) return 'EV';\n \n return eventName\n .split(' ')\n .map(word => word.charAt(0).toUpperCase())\n .join('')\n .substring(0, 3); // Max 3 characters\n}\n\n/**\n * Hook for accessing public event logo URLs\n * \n * This hook provides access to event logo URLs without requiring\n * authentication. It includes fallback handling and image validation.\n * \n * @param eventId - The event ID to fetch logo for\n * @param eventName - The event name for fallback text generation\n * @param organisationId - The organisation ID for storage path\n * @param options - Configuration options for caching and behavior\n * @returns Object containing logo URL, fallback text, loading state, error, and refetch function\n */\nexport function usePublicEventLogo(\n eventId: string | undefined,\n eventName: string | undefined,\n organisationId: string | undefined,\n options: UsePublicEventLogoOptions\n): UsePublicEventLogoReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n validateImage = true,\n generateFallbackText = defaultGenerateFallbackText,\n supabase\n } = options;\n\n const [logoUrl, setLogoUrl] = useState<string | null>(null);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n // Generate fallback text\n const fallbackText = useMemo(() => {\n return eventName ? generateFallbackText(eventName) : 'EV';\n }, [eventName, generateFallbackText]);\n\n const fetchLogo = useCallback(async (): Promise<void> => {\n if (!eventId || !organisationId || !supabase) {\n setLogoUrl(null);\n setIsLoading(false);\n return;\n }\n\n // Validate UUID format for organisationId to prevent database errors\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(organisationId)) {\n log.warn('Invalid organisationId format (not a valid UUID):', organisationId);\n // Don't return early - let the database handle the validation\n // This allows for more graceful error handling\n }\n\n // Check cache first\n const cacheKey = `public_logo_${eventId}_${organisationId}`;\n if (enableCache) {\n const cached = publicDataCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n setLogoUrl(cached.data);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n // Call the public logo RPC function\n const { data, error: rpcError } = await (supabase as any).rpc('get_public_event_logo', {\n event_id_param: eventId,\n organisation_id_param: organisationId\n });\n\n if (rpcError) {\n throw new Error(rpcError.message || 'Failed to fetch logo');\n }\n\n if (!data || data.length === 0 || !data[0] || !data[0].logo_url) {\n setLogoUrl(null);\n return;\n }\n\n const logoUrl = data[0].logo_url;\n\n // Validate image existence if requested\n if (validateImage) {\n try {\n const response = await fetch(logoUrl, { method: 'HEAD' });\n if (!response.ok) {\n log.warn('Logo URL not accessible:', logoUrl);\n setLogoUrl(null);\n return;\n }\n } catch (fetchError) {\n log.warn('Error validating logo URL:', fetchError);\n setLogoUrl(null);\n return;\n }\n }\n\n setLogoUrl(logoUrl);\n\n // Cache the result\n if (enableCache) {\n publicDataCache.set(cacheKey, {\n data: logoUrl,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n log.error('Error fetching logo:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setLogoUrl(null);\n } finally {\n setIsLoading(false);\n }\n }, [eventId, organisationId, supabase, cacheTtl, enableCache, validateImage]);\n\n // Fetch logo when parameters change\n useEffect(() => {\n if (eventId && organisationId) {\n fetchLogo();\n } else {\n setLogoUrl(null);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchLogo, eventId, organisationId]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!eventId || !organisationId) return;\n \n // Clear cache for this logo\n if (enableCache) {\n const cacheKey = `public_logo_${eventId}_${organisationId}`;\n publicDataCache.delete(cacheKey);\n }\n await fetchLogo();\n }, [fetchLogo, eventId, organisationId, enableCache]);\n\n return {\n logoUrl,\n fallbackText,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public logo data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicLogoCache(): void {\n for (const [key] of publicDataCache) {\n if (key.startsWith('public_logo_')) {\n publicDataCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicLogoCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicDataCache.keys()).filter(key => key.startsWith('public_logo_'));\n return {\n size: keys.length,\n keys\n };\n}\n","/**\n * @file Public Route Params Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for extracting and validating public route parameters.\n * Provides event code extraction and validation for public pages.\n *\n * Features:\n * - URL parameter extraction\n * - Event code validation\n * - TypeScript support\n * - Error handling\n * - Route pattern support\n *\n * @example\n * ```tsx\n * import { usePublicRouteParams } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * const { eventCode, eventId, event, error, isLoading } = usePublicRouteParams();\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n * if (!event) return <div>Event not found</div>;\n *\n * return (\n * <div>\n * <h1>{event.event_name}</h1>\n * <p>Event Code: {eventCode}</p>\n * </div>\n * );\n * }\n * ```\n *\n * @accessibility\n * - No direct accessibility concerns (hook)\n * - Enables accessible route parameter handling\n * - Supports screen reader friendly error states\n *\n * @security\n * - Validates event codes before processing\n * - Sanitizes URL parameters\n * - No sensitive information exposed\n * - Rate limiting applied at route level\n *\n * @performance\n * - Minimal re-renders with stable references\n * - Efficient parameter extraction\n * - Caching integration\n *\n * @dependencies\n * - React 18+ - Hooks and effects\n * - React Router - URL parameter extraction\n * - Event types - Type definitions\n */\n\nimport { useState, useEffect, useCallback, useMemo } from 'react';\nimport { useParams, useLocation } from 'react-router-dom';\nimport type { Event } from '../../types/event';\nimport { usePublicEvent } from './usePublicEvent';\n\nexport interface UsePublicRouteParamsReturn {\n /** The event code from the URL */\n eventCode: string | null;\n /** The event ID (resolved from event code) */\n eventId: string | null;\n /** The full event object */\n event: Event | null;\n /** Whether the route parameters are being processed */\n isLoading: boolean;\n /** Any error that occurred during processing */\n error: Error | null;\n /** Function to manually refetch the event data */\n refetch: () => Promise<void>;\n}\n\ninterface UsePublicRouteParamsOptions {\n /** Whether to automatically fetch event data (default: true) */\n fetchEventData?: boolean;\n /** Custom event code parameter name (default: 'eventCode') */\n eventCodeParam?: string;\n /** Whether to validate event code format (default: true) */\n validateEventCode?: boolean;\n}\n\n/**\n * Validate event code format\n * Event codes should be alphanumeric with optional hyphens/underscores in the middle\n * Supports 2-50 characters\n */\nfunction validateEventCodeFormat(eventCode: string): boolean {\n if (!eventCode || typeof eventCode !== 'string') return false;\n \n // Length check: 2-50 characters\n if (eventCode.length < 2 || eventCode.length > 50) return false;\n \n // For 2-character codes: both must be alphanumeric\n if (eventCode.length === 2) {\n return /^[a-zA-Z0-9]{2}$/.test(eventCode);\n }\n \n // For 3+ character codes: alphanumeric at start and end, hyphens/underscores allowed in middle\n // Must not start or end with hyphen/underscore\n const eventCodeRegex = /^[a-zA-Z0-9][a-zA-Z0-9_-]{1,48}[a-zA-Z0-9]$/;\n const matchesFormat = eventCodeRegex.test(eventCode);\n \n if (!matchesFormat) return false;\n \n // Additional check: no consecutive hyphens or underscores\n if (eventCode.includes('--') || eventCode.includes('__') || eventCode.includes('-_') || eventCode.includes('_-')) {\n return false;\n }\n \n return true;\n}\n\n/**\n * Hook for extracting and validating public route parameters\n * \n * This hook extracts event codes from URL parameters and optionally\n * fetches the corresponding event data. It provides validation and\n * error handling for public routes.\n * \n * @param options - Configuration options for behavior\n * @returns Object containing route parameters, event data, loading state, error, and refetch function\n */\nexport function usePublicRouteParams(\n options: UsePublicRouteParamsOptions = {}\n): UsePublicRouteParamsReturn {\n const {\n fetchEventData = true,\n eventCodeParam = 'eventCode',\n validateEventCode = true\n } = options;\n\n const params = useParams();\n const location = useLocation();\n \n const [error, setError] = useState<Error | null>(null);\n\n // Extract event code from URL parameters\n const eventCode = useMemo(() => {\n const code = params[eventCodeParam] as string;\n \n if (!code) {\n // Don't set error immediately - let the component handle missing eventCode gracefully\n return null;\n }\n\n // Validate event code format if requested\n if (validateEventCode && !validateEventCodeFormat(code)) {\n setError(new Error(`Invalid event code format: ${code}`));\n return null;\n }\n\n setError(null);\n return code;\n }, [params, eventCodeParam, validateEventCode]);\n\n // Use the public event hook to fetch event data\n const {\n event,\n isLoading: eventLoading,\n error: eventError,\n refetch: refetchEvent\n } = usePublicEvent(eventCode || '', {\n enableCache: true,\n cacheTtl: 5 * 60 * 1000 // 5 minutes\n });\n\n // Determine if we should show loading state\n const isLoading = useMemo(() => {\n if (!fetchEventData) return false;\n return eventLoading;\n }, [fetchEventData, eventLoading]);\n\n // Determine the final error state\n const finalError = useMemo(() => {\n if (error) return error;\n if (eventError) return eventError;\n return null;\n }, [error, eventError]);\n\n // Extract event ID from event data\n const eventId = useMemo(() => {\n if (!event) return null;\n return event.event_id || event.id;\n }, [event]);\n\n // Refetch function\n const refetch = useCallback(async (): Promise<void> => {\n if (!fetchEventData) return;\n await refetchEvent();\n }, [fetchEventData, refetchEvent]);\n\n return {\n eventCode,\n eventId,\n event: fetchEventData ? event : null,\n isLoading,\n error: finalError,\n refetch\n };\n}\n\n/**\n * Hook for extracting just the event code without fetching event data\n * Useful when you only need the event code and will fetch data separately\n */\nexport function usePublicEventCode(\n eventCodeParam: string = 'eventCode'\n): { eventCode: string | null; error: Error | null } {\n const params = useParams();\n \n const eventCode = useMemo(() => {\n const code = params[eventCodeParam] as string;\n \n if (!code) {\n return null;\n }\n\n // Validate event code format\n if (!validateEventCodeFormat(code)) {\n return null;\n }\n\n return code;\n }, [params, eventCodeParam]);\n\n const error = useMemo(() => {\n if (!eventCode) {\n return new Error(`Event code parameter '${eventCodeParam}' not found or invalid`);\n }\n return null;\n }, [eventCode, eventCodeParam]);\n\n return {\n eventCode,\n error\n };\n}\n\n/**\n * Utility function to generate public route paths\n */\nexport function generatePublicRoutePath(\n eventCode: string,\n pageName: string = 'index'\n): string {\n if (!eventCode || !validateEventCodeFormat(eventCode)) {\n throw new Error('Invalid event code for route generation');\n }\n \n return `/public/event/${eventCode}/${pageName}`;\n}\n\n/**\n * Utility function to extract event code from a public route path\n * Supports 2-50 character event codes\n */\nexport function extractEventCodeFromPath(path: string): string | null {\n const match = path.match(/^\\/public\\/event\\/([a-zA-Z0-9_-]{2,50})(?:\\/.*)?$/);\n if (!match) return null;\n \n const eventCode = match[1];\n // Validate the extracted code using the same validation function\n if (!validateEventCodeFormat(eventCode)) {\n return null;\n }\n \n return eventCode;\n}\n"],"mappings":"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","names":["useCallback","useState","useState","useCallback","useMemo","useMemo","useState","useEffect","useCallback","useMemo","useState","useMemo","useCallback","error","useEffect","useState","useEffect","useCallback","useMemo","publicDataCache","useState","useMemo","useCallback","logoUrl","error","useEffect","useState","useCallback","useMemo","useState","useMemo","useCallback"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/hooks/useEventTheme.ts","../src/hooks/usePreventTabReload.ts","../src/components/ErrorBoundary/ErrorBoundary.tsx","../src/components/PublicLayout/PublicPageProvider.tsx","../src/hooks/useAppConfig.ts","../src/utils/storage/config.ts","../src/utils/storage/helpers.ts","../src/hooks/useFileDisplay.ts","../src/utils/file-reference/index.ts","../src/hooks/public/usePublicFileDisplay.ts"],"sourcesContent":["/**\n * @file Event Theme Hook\n * @package @jmruthers/pace-core\n * @module Hooks/EventTheme\n * @since 2.0.0\n * \n * Hook that automatically applies event-specific theming when an event is selected.\n * This ensures consistent UX across all apps in the pace suite.\n * \n * Works in two modes:\n * 1. Authenticated mode: Uses EventProvider context via useEvents() hook\n * 2. Public page mode: Accepts event prop directly (no EventProvider required)\n * \n * @example\n * ```tsx\n * // Authenticated pages (with EventProvider)\n * import { useEventTheme } from '@jmruthers/pace-core/hooks';\n * \n * function MyApp() {\n * // Automatically applies event colors when event is selected via EventProvider\n * useEventTheme();\n * \n * return <div>Your app content</div>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Public pages (without EventProvider)\n * import { useEventTheme } from '@jmruthers/pace-core/hooks';\n * \n * function PublicPageLayout({ event }) {\n * // Applies event colors directly from event prop\n * useEventTheme(event);\n * \n * return <div>Public page content</div>;\n * }\n * ```\n */\n\nimport { useEffect } from 'react';\nimport { useLocation } from 'react-router-dom';\nimport { useEvents } from './useEvents';\nimport { applyPalette, clearPalette } from '../theming/runtime';\nimport { parseAndNormalizeEventColours } from '../theming/parseEventColours';\nimport type { Event } from '../types/event';\nimport { createLogger } from '../utils/core/logger';\n\nconst log = createLogger('useEventTheme');\n\n/**\n * Hook that automatically applies event-specific theming\n * \n * This hook applies event colors using the theming system. It works in two modes:\n * \n * **Authenticated Mode (default):**\n * - Uses EventProvider context via useEvents() hook\n * - Automatically watches selectedEvent changes\n * - Used in authenticated pages with EventProvider\n * \n * **Public Page Mode:**\n * - Accepts event prop directly\n * - No EventProvider required\n * - Used in public pages without authentication context\n * \n * Behavior:\n * - Applies event colors when an event with `event_colours` is provided\n * - Clears theming when no event is provided\n * - Skips theme application when on the login route to prevent login screen styling\n * - Handles cleanup and error cases gracefully\n * \n * @param event - Optional event object. If provided, uses this event directly (public page mode).\n * If not provided, uses EventProvider context via useEvents() (authenticated mode).\n * \n * @returns void - This is an effect hook with no return value\n * \n * @example\n * ```tsx\n * // Authenticated mode - uses EventProvider\n * function MyApp() {\n * useEventTheme(); // Watches selectedEvent from EventProvider\n * return <div>App content</div>;\n * }\n * ```\n * \n * @example\n * ```tsx\n * // Public page mode - uses event prop\n * function PublicPageLayout({ event }) {\n * useEventTheme(event); // Uses event prop directly\n * return <div>Public content</div>;\n * }\n * ```\n */\nexport function useEventTheme(event?: Event | null): void {\n const location = useLocation();\n \n // Try to get event from EventProvider context (authenticated mode)\n // Only use useEvents() if event prop is not provided\n let selectedEvent: Event | null | undefined;\n try {\n if (event === undefined) {\n // No event prop provided, try to use EventProvider context\n const eventsContext = useEvents();\n selectedEvent = eventsContext.selectedEvent;\n } else {\n // Event prop provided, use it directly (public page mode)\n selectedEvent = event;\n }\n } catch (error) {\n // useEvents() throws if EventProvider is not available\n // This is expected for public pages - use the event prop if provided\n if (event !== undefined) {\n selectedEvent = event;\n } else {\n // No event prop and no EventProvider - can't apply theme\n selectedEvent = null;\n }\n }\n\n useEffect(() => {\n // Skip theme application when on login route\n // This prevents event colors from styling the login screen\n const isOnLoginRoute = location.pathname === '/login' || location.pathname.startsWith('/login');\n \n if (isOnLoginRoute) {\n // Clear any active theme when on login route\n clearPalette();\n return;\n }\n\n // If there's no selected event, clear any dynamic theming\n if (!selectedEvent) {\n clearPalette();\n return;\n }\n\n // Check if the event has theme colors\n const eventColours = selectedEvent.event_colours;\n\n // Parse and normalize event_colours using shared utility\n const normalized = parseAndNormalizeEventColours(eventColours);\n \n if (!normalized) {\n clearPalette();\n return;\n }\n\n // Apply the normalized palette\n try {\n applyPalette(normalized);\n } catch (error) {\n log.error('Failed to apply event palette:', error);\n }\n\n // Cleanup function to clear palette when component unmounts or event changes\n return () => {\n // Don't clear on unmount since we want the theme to persist\n // The next event selection will update it\n };\n }, [selectedEvent, location.pathname]);\n}\n","/**\n * @file usePreventTabReload Hook\n * @package @jmruthers/pace-core\n * @module Hooks\n * @since 0.6.0\n *\n * Prevents full page reloads when switching browser tabs.\n * Handles browser back-forward cache (bfcache) restoration and tab visibility changes.\n */\n\nimport { useEffect, useRef } from 'react';\n\nexport interface UsePreventTabReloadOptions {\n /**\n * Whether to enable the prevention logic\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Grace period in milliseconds to wait after tab becomes visible\n * before allowing any potential reloads\n * @default 2000\n */\n gracePeriodMs?: number;\n}\n\n/**\n * Hook to prevent full page reloads when switching browser tabs.\n * \n * This hook handles:\n * - Browser back-forward cache (bfcache) restoration\n * - Tab visibility changes\n * - Prevents unwanted page reloads when returning to a tab\n * \n * @param options - Configuration options\n * \n * @example\n * ```tsx\n * import { usePreventTabReload } from '@jmruthers/pace-core';\n * \n * function MyComponent() {\n * usePreventTabReload();\n * // Component code...\n * }\n * ```\n */\nexport function usePreventTabReload(options: UsePreventTabReloadOptions = {}): void {\n const { enabled = true, gracePeriodMs = 2000 } = options;\n const isRestoringFromCacheRef = useRef(false);\n const gracePeriodTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n useEffect(() => {\n if (!enabled || typeof window === 'undefined') return;\n\n // Handle pageshow event - detects when page is restored from bfcache\n const handlePageShow = (event: PageTransitionEvent) => {\n // If page was restored from bfcache, prevent any reload behavior\n if (event.persisted) {\n isRestoringFromCacheRef.current = true;\n \n // Clear any existing timeout\n if (gracePeriodTimeoutRef.current) {\n clearTimeout(gracePeriodTimeoutRef.current);\n }\n \n // Set grace period to prevent reloads\n gracePeriodTimeoutRef.current = setTimeout(() => {\n isRestoringFromCacheRef.current = false;\n }, gracePeriodMs);\n }\n };\n\n // Handle visibility changes - when tab becomes visible\n const handleVisibilityChange = () => {\n if (!document.hidden) {\n // Tab just became visible - set flag to prevent reloads\n isRestoringFromCacheRef.current = true;\n \n // Clear any existing timeout\n if (gracePeriodTimeoutRef.current) {\n clearTimeout(gracePeriodTimeoutRef.current);\n }\n \n // Set grace period\n gracePeriodTimeoutRef.current = setTimeout(() => {\n isRestoringFromCacheRef.current = false;\n }, gracePeriodMs);\n }\n };\n\n // Add event listeners\n window.addEventListener('pageshow', handlePageShow);\n document.addEventListener('visibilitychange', handleVisibilityChange);\n\n return () => {\n window.removeEventListener('pageshow', handlePageShow);\n document.removeEventListener('visibilitychange', handleVisibilityChange);\n \n if (gracePeriodTimeoutRef.current) {\n clearTimeout(gracePeriodTimeoutRef.current);\n }\n };\n }, [enabled, gracePeriodMs]);\n}\n\n","/**\n * @file Error Boundary Component\n * @package @jmruthers/pace-core\n * @module Components/ErrorBoundary\n * @since 0.1.0\n *\n * A comprehensive error boundary component that catches JavaScript errors in child components\n * and provides fallback UI with retry functionality and error reporting.\n *\n * Features:\n * - Catches JavaScript errors in component tree\n * - Custom fallback UI with retry functionality\n * - Error reporting and logging\n * - Performance monitoring integration\n * - Retry mechanism with configurable attempts\n * - Development mode error details\n * - Accessibility compliant error display\n * - Component-specific error tracking\n * - Error ID generation for tracking\n *\n * @example\n * ```tsx\n * // Basic error boundary\n * <ErrorBoundary>\n * <MyComponent />\n * </ErrorBoundary>\n * \n * // Error boundary with custom fallback\n * <ErrorBoundary\n * componentName=\"UserProfile\"\n * fallback={<div>Something went wrong loading the profile.</div>}\n * onError={(error, errorInfo, errorId) => {\n * console.log('Error caught:', errorId, error);\n * }}\n * >\n * <UserProfile />\n * </ErrorBoundary>\n * \n * // Error boundary with retry functionality\n * <ErrorBoundary\n * componentName=\"DataTable\"\n * maxRetries={3}\n * enableRetry={true}\n * enableReporting={true}\n * onError={(error, errorInfo, errorId) => {\n * // Send to error reporting service\n * errorReportingService.report({ error, errorInfo, errorId });\n * }}\n * >\n * <DataTable data={data} />\n * </ErrorBoundary>\n * \n * // Wrapping multiple components\n * <ErrorBoundary componentName=\"App\">\n * <Header />\n * <ErrorBoundary componentName=\"MainContent\">\n * <MainContent />\n * </ErrorBoundary>\n * <Footer />\n * </ErrorBoundary>\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Proper ARIA role=\"alert\" for error announcements\n * - Screen reader friendly error messages\n * - Keyboard accessible retry buttons\n * - High contrast error styling\n * - Clear error identification\n *\n * @performance\n * - Efficient error state management\n * - Performance monitoring integration\n * - Minimal impact on normal rendering\n * - Configurable error reporting\n *\n * @dependencies\n * - React 18+ - Component lifecycle\n * - Performance monitoring utilities\n * - Tailwind CSS - Styling\n */\n\nimport React, { Component, ReactNode } from 'react';\nimport { performanceBudgetMonitor } from '../../utils/performance/performanceBudgets';\nimport { logger } from '../../utils/core/logger';\n\n/**\n * State interface for the ErrorBoundary component\n * @public\n */\nexport interface ErrorBoundaryState {\n /** Whether an error has been caught */\n hasError: boolean;\n /** The error that was caught */\n error?: Error;\n /** Additional error information from React */\n errorInfo?: React.ErrorInfo;\n /** Unique identifier for the error */\n errorId?: string;\n /** Number of retry attempts made */\n retryCount: number;\n}\n\n/**\n * Props interface for the ErrorBoundary component\n * @public\n */\nexport interface ErrorBoundaryProps {\n /** Child components to wrap with error boundary */\n children: ReactNode;\n /** Name of the component for error reporting */\n componentName?: string;\n /** Custom fallback UI to display when error occurs */\n fallback?: ReactNode;\n /** Callback function called when an error is caught */\n onError?: (error: Error, errorInfo: React.ErrorInfo, errorId: string) => void;\n /** Maximum number of retry attempts */\n maxRetries?: number;\n /** Whether to enable retry functionality */\n enableRetry?: boolean;\n /** Whether to enable error reporting */\n enableReporting?: boolean;\n}\n\n/**\n * ErrorBoundary component\n * Catches JavaScript errors in child components and provides fallback UI\n * \n * @example\n * ```tsx\n * <ErrorBoundary\n * componentName=\"MyComponent\"\n * maxRetries={3}\n * onError={(error, errorInfo, errorId) => {\n * console.log('Error caught:', errorId);\n * }}\n * >\n * <MyComponent />\n * </ErrorBoundary>\n * ```\n */\nexport class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {\n private retryTimeoutId: NodeJS.Timeout | null = null;\n\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = { \n hasError: false, \n retryCount: 0 \n };\n }\n\n static getDerivedStateFromError(error: Error): Partial<ErrorBoundaryState> {\n const errorId = `error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n return { \n hasError: true, \n error,\n errorId \n };\n }\n\n componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {\n const { componentName = 'Unknown Component', onError, enableReporting = true } = this.props;\n const errorId = this.state.errorId!;\n \n this.setState({ errorInfo });\n \n // Enhanced logging with component name and error ID\n logger.error('ErrorBoundary', `[${componentName}] Caught error ${errorId}:`, error, errorInfo);\n \n // Performance monitoring - track error occurrence\n performanceBudgetMonitor.measure('ERROR_BOUNDARY_TRIGGER', 1, {\n componentName,\n errorId,\n errorMessage: error.message,\n stack: error.stack?.substring(0, 200), // Truncated stack trace\n });\n\n // Report error if enabled\n if (enableReporting) {\n this.reportError(errorId, componentName);\n }\n \n // Call custom error handler if provided\n if (onError) {\n onError(error, errorInfo, errorId);\n }\n }\n\n private reportError = (errorId: string, componentName: string) => {\n // In production, this would send to error reporting service\n if (import.meta.env.MODE === 'production') {\n // Example: Send to error reporting service\n // errorReportingService.report({ error, errorInfo, errorId, componentName });\n logger.warn('ErrorBoundary', 'Error reporting would be triggered in production:', { errorId, componentName });\n }\n };\n\n private handleRetry = () => {\n const { maxRetries = 3 } = this.props;\n const { retryCount } = this.state;\n\n if (retryCount < maxRetries) {\n logger.debug('ErrorBoundary', `Retrying component render (attempt ${retryCount + 1}/${maxRetries})`);\n \n this.setState(prevState => ({\n hasError: false,\n error: undefined,\n errorInfo: undefined,\n errorId: undefined,\n retryCount: prevState.retryCount + 1\n }));\n }\n };\n\n componentWillUnmount() {\n if (this.retryTimeoutId) {\n clearTimeout(this.retryTimeoutId);\n }\n }\n\n render() {\n if (this.state.hasError) {\n const { \n componentName = 'Component', \n fallback, \n enableRetry = true, \n maxRetries = 3 \n } = this.props;\n const { retryCount, errorId } = this.state;\n\n // Use custom fallback if provided\n if (fallback) {\n return fallback;\n }\n\n // Enhanced error UI with retry functionality\n return (\n <div \n role=\"alert\" \n className=\"p-6 bg-destructive/10 border border-destructive/20 rounded-lg\"\n data-error-boundary={errorId}\n >\n <div className=\"flex items-start gap-3\">\n <div className=\"flex-shrink-0\">\n <svg className=\"w-5 h-5 text-destructive\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z\" clipRule=\"evenodd\" />\n </svg>\n </div>\n <div className=\"flex-1 min-w-0\">\n <h3 className=\"text-destructive\">\n Error in {componentName}\n </h3>\n <p className=\"text-destructive/80\">\n {this.state.error?.message || 'An unexpected error occurred.'}\n </p>\n \n {enableRetry && retryCount < maxRetries && (\n <div className=\"flex gap-3 mb-4\">\n <button\n onClick={this.handleRetry}\n className=\"px-4 py-2 bg-destructive text-destructive-foreground rounded-md hover:bg-destructive/90 transition-colors text-sm font-medium\"\n >\n Retry ({retryCount + 1}/{maxRetries})\n </button>\n <button\n onClick={() => window.location.reload()}\n className=\"px-4 py-2 bg-sec-600 text-main-50 rounded-md hover:bg-sec-700 transition-colors text-sm font-medium\"\n >\n Reload Page\n </button>\n </div>\n )}\n\n {retryCount >= maxRetries && (\n <div className=\"mb-4 p-3 bg-acc-50 border border-acc-200 rounded-md\">\n <p className=\"text-acc-800\">\n Maximum retry attempts reached. Please reload the page or contact support.\n </p>\n <button\n onClick={() => window.location.reload()}\n className=\"mt-2 px-3 py-1 bg-acc-600 text-main-50 rounded text-sm hover:bg-acc-700\"\n >\n Reload Page\n </button>\n </div>\n )}\n\n {import.meta.env.MODE === 'development' && this.state.error && (\n <details className=\"text-sm text-destructive/70\">\n <summary className=\"cursor-pointer font-medium mb-2\">\n Error Details (Development)\n </summary>\n <div className=\"bg-destructive/5 p-3 rounded border\">\n <p className=\"font-mono\">Error ID: {errorId}</p>\n <pre className=\"whitespace-pre-wrap text-xs overflow-auto max-h-32\">\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </div>\n </details>\n )}\n </div>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n","/**\n * @file Public Page Provider\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * A completely isolated provider for public pages that doesn't trigger\n * any authentication context. This ensures public pages work independently\n * of the main application's authentication system.\n *\n * Features:\n * - No authentication required\n * - No organisation context\n * - No event context\n * - Completely isolated from main app context\n * - Environment variable access for public data\n * - Error boundary integration\n *\n * @example\n * ```tsx\n * import { PublicPageProvider } from '@jmruthers/pace-core';\n * \n * const APP_NAME = 'CORE';\n * \n * function PublicApp() {\n * return (\n * <PublicPageProvider appName={APP_NAME}>\n * <Routes>\n * <Route path=\"/events/:eventCode/recipe-grid-report\" element={<PublicRecipePage />} />\n * </Routes>\n * </PublicPageProvider>\n * );\n * }\n * ```\n */\n\nimport React, { createContext, useContext, ReactNode, useMemo } from 'react';\nimport { createClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { ErrorBoundary } from '../ErrorBoundary';\nimport { logger } from '../../utils/core/logger';\n\ninterface PublicPageContextType {\n isPublicPage: true;\n supabase: ReturnType<typeof createClient<Database>> | null;\n appName: string | null;\n environment: {\n supabaseUrl: string | null;\n supabaseKey: string | null;\n };\n}\n\nexport const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);\n\nexport interface PublicPageProviderProps {\n children: ReactNode;\n /** Application name for logo display and branding. Should match the APP_NAME constant used in App.tsx */\n appName?: string;\n}\n\n/**\n * Provider for public pages that completely isolates them from authentication context\n * \n * This provider:\n * - Does not initialize any authentication providers\n * - Provides environment variables for public data access\n * - Includes error boundary for graceful error handling\n * - Is completely separate from the main app context\n * - Provides appName for consistent logo display\n */\nexport function PublicPageProvider({ children, appName }: PublicPageProviderProps) {\n // Get environment variables for public data access\n // Handle both Vite (import.meta.env) and Node.js (process.env) environments\n const getEnvVar = (key: string): string | undefined => {\n // Check Vite environment first (browser)\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n const env = import.meta.env as Record<string, string | undefined>;\n return env[key];\n }\n // Check Node.js environment (server-side)\n if (typeof process !== 'undefined' && process.env) {\n return process.env[key];\n }\n return undefined;\n };\n\n const supabaseUrl = getEnvVar('VITE_SUPABASE_URL') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_URL') || \n null;\n \n const supabaseKey = getEnvVar('VITE_SUPABASE_ANON_KEY') || \n getEnvVar('NEXT_PUBLIC_SUPABASE_ANON_KEY') || \n null;\n\n // Create Supabase client if environment variables are available\n // Note: VITE_SUPABASE_ANON_KEY accepts both legacy anon keys and modern publishable keys (sb_publishable_...)\n const supabase = useMemo(() => {\n if (!supabaseUrl || !supabaseKey) {\n logger.warn('PublicPageProvider', 'Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY are set in your environment. Use publishable key if anon key is disabled.');\n return null;\n }\n const client = createClient<Database>(supabaseUrl, supabaseKey);\n logger.info('PublicPageProvider', 'Supabase client created successfully for public pages');\n return client;\n }, [supabaseUrl, supabaseKey]);\n\n const contextValue: PublicPageContextType = {\n isPublicPage: true,\n supabase,\n appName: appName || null,\n environment: {\n supabaseUrl,\n supabaseKey\n }\n };\n\n return (\n <PublicPageContext.Provider value={contextValue}>\n <ErrorBoundary componentName=\"PublicPageProvider\">\n {children}\n </ErrorBoundary>\n </PublicPageContext.Provider>\n );\n}\n\n/**\n * Hook to access public page context\n * \n * @returns Public page context with environment variables\n */\nexport function usePublicPageContext(): PublicPageContextType {\n const context = useContext(PublicPageContext);\n \n if (!context) {\n throw new Error('usePublicPageContext must be used within a PublicPageProvider');\n }\n \n return context;\n}\n\n/**\n * Hook to check if we're in a public page context\n * \n * @returns True if we're in a public page context\n */\nexport function useIsPublicPage(): boolean {\n const context = useContext(PublicPageContext);\n return context !== undefined;\n}\n","/**\n * @file useAppConfig Hook\n * @package @jmruthers/pace-core\n * @module Hooks/useAppConfig\n * @since 0.4.0\n *\n * Hook for accessing app configuration like direct access support and event requirements.\n * This is a convenience hook that extracts app config from the UnifiedAuthProvider.\n *\n * @example\n * ```tsx\n * function MyComponent() {\n * const { supportsDirectAccess, requiresEvent, isLoading } = useAppConfig();\n * \n * if (isLoading) return <div>Loading...</div>;\n * \n * return (\n * <div>\n * {supportsDirectAccess && (\n * <div>This app supports direct access!</div>\n * )}\n * {requiresEvent && (\n * <EventSelector />\n * )}\n * </div>\n * );\n * }\n * ```\n */\n\nimport { useMemo, useContext } from 'react';\nimport { useUnifiedAuth } from '../providers/services/UnifiedAuthProvider';\nimport { useIsPublicPage, PublicPageContext } from '../components/PublicLayout/PublicPageProvider';\n\nexport interface UseAppConfigReturn {\n supportsDirectAccess: boolean;\n requiresEvent: boolean;\n isLoading: boolean;\n appName: string;\n}\n\n/**\n * Hook to access app configuration\n * Works in both authenticated and public contexts\n * @returns App configuration and loading state\n */\nexport function useAppConfig(): UseAppConfigReturn {\n // Check if we're in a public page context first\n const isPublicPage = useIsPublicPage();\n \n // Try to get appName from PublicPageContext (access context directly to avoid hook rule violations)\n const publicPageContext = useContext(PublicPageContext);\n const contextAppName = publicPageContext?.appName || null;\n \n if (isPublicPage) {\n const getAppName = (): string => {\n // Priority 1: Use appName from PublicPageContext (passed from App.tsx)\n if (contextAppName) {\n return contextAppName;\n }\n \n // Priority 2: Check environment variables\n if (typeof import.meta !== 'undefined' && (import.meta as any).env) {\n return (import.meta as any).env.VITE_APP_NAME || \n (import.meta as any).env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.env.VITE_APP_NAME || \n import.meta.env.NEXT_PUBLIC_APP_NAME || \n 'PACE';\n }\n \n // Priority 3: Default fallback\n return 'PACE';\n };\n \n return useMemo(() => ({\n supportsDirectAccess: false, // Public pages don't support direct access\n requiresEvent: true, // Public pages always require an event\n isLoading: false,\n appName: getAppName()\n }), [contextAppName]);\n }\n \n // For authenticated pages, use UnifiedAuthProvider\n try {\n const { appConfig, appName } = useUnifiedAuth();\n return useMemo(() => ({\n supportsDirectAccess: !(appConfig?.requires_event ?? true),\n requiresEvent: appConfig?.requires_event ?? true,\n isLoading: appConfig === null,\n appName\n }), [appConfig?.requires_event, appName]);\n } catch (error) {\n // Fallback if UnifiedAuthProvider is not available\n return useMemo(() => ({\n supportsDirectAccess: false,\n requiresEvent: true,\n isLoading: false,\n appName: 'PACE'\n }), []);\n }\n} ","/**\n * Storage configuration for pace-core\n */\n\nimport { FileSizeLimits, StorageConfig } from './types';\n\n/**\n * File size limits by MIME type (in bytes)\n */\nexport const FILE_SIZE_LIMITS: FileSizeLimits = {\n // Images\n 'image/jpeg': 5 * 1024 * 1024, // 5MB\n 'image/png': 5 * 1024 * 1024, // 5MB\n 'image/gif': 10 * 1024 * 1024, // 10MB (for animations)\n 'image/webp': 5 * 1024 * 1024, // 5MB\n 'image/svg+xml': 1 * 1024 * 1024, // 1MB (vector graphics)\n \n // Documents\n 'application/pdf': 50 * 1024 * 1024, // 50MB\n 'application/msword': 25 * 1024 * 1024, // 25MB\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 25 * 1024 * 1024, // 25MB\n 'application/vnd.ms-excel': 25 * 1024 * 1024, // 25MB\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 25 * 1024 * 1024, // 25MB\n \n // Archives\n 'application/zip': 100 * 1024 * 1024, // 100MB\n 'application/x-rar-compressed': 100 * 1024 * 1024, // 100MB\n \n // Text files\n 'text/plain': 1 * 1024 * 1024, // 1MB\n 'text/csv': 10 * 1024 * 1024, // 10MB\n 'application/json': 10 * 1024 * 1024, // 10MB\n};\n\n/**\n * Default file size limit for unknown MIME types\n */\nexport const DEFAULT_FILE_SIZE_LIMIT = 10 * 1024 * 1024; // 10MB\n\n/**\n * App-specific path mappings for file organization\n * Maps app names to their primary category in the organization-first structure\n */\nexport const APP_PATH_MAPPING: Record<string, string> = {\n 'PACE': 'event_logos',\n 'TRAC': 'trac_accommodation', // Default category for TRAC files\n 'MEDI': 'documents',\n 'CAKE': 'documents'\n};\n\n/**\n * Storage configuration\n */\nexport const STORAGE_CONFIG: StorageConfig = {\n bucketName: 'files',\n fileSizeLimits: FILE_SIZE_LIMITS,\n defaultFileSizeLimit: DEFAULT_FILE_SIZE_LIMIT,\n};\n\n/**\n * Get the file size limit for a given MIME type\n */\nexport function getFileSizeLimit(mimeType: string): number {\n return STORAGE_CONFIG.fileSizeLimits[mimeType] || STORAGE_CONFIG.defaultFileSizeLimit;\n}\n\n/**\n * Get the bucket name based on whether the file is public or private\n * @param isPublic - Whether the file should be publicly accessible\n * @returns The bucket name: 'public-files' for public files, 'files' for private files\n */\nexport function getBucketName(isPublic: boolean): 'files' | 'public-files' {\n return isPublic ? 'public-files' : 'files';\n}\n\n/**\n * Validate file size against limits\n */\nexport function validateFileSize(file: File): { isValid: boolean; error?: string } {\n const limit = getFileSizeLimit(file.type);\n \n if (file.size > limit) {\n const limitMB = Math.round(limit / (1024 * 1024));\n const fileMB = Math.round(file.size / (1024 * 1024));\n return {\n isValid: false,\n error: `File size (${fileMB}MB) exceeds limit (${limitMB}MB) for ${file.type}`\n };\n }\n \n return { isValid: true };\n}\n\n/**\n * Get human-readable file size\n */\nexport function formatFileSize(bytes: number): string {\n if (bytes === 0) return '0 Bytes';\n if (bytes < 0) return `${bytes} Bytes`; // Handle negative numbers\n \n const k = 1024;\n const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n \n // Ensure we don't exceed the available size units\n const sizeIndex = Math.min(Math.max(i, 0), sizes.length - 1);\n \n return parseFloat((bytes / Math.pow(k, sizeIndex)).toFixed(2)) + ' ' + sizes[sizeIndex];\n}\n","/**\n * Storage helper functions for pace-core\n */\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { \n StorageUploadOptions, \n StorageUploadResult, \n StorageFileMetadata,\n StorageUrlOptions,\n StorageListOptions,\n StorageListResult,\n StorageFileInfo\n} from './types';\nimport { validateFileSize, STORAGE_CONFIG, getBucketName } from './config';\nimport { createLogger } from '../core/logger';\n\nconst log = createLogger('StorageHelpers');\n\n/**\n * Generate a file path based on organization-first structure\n */\nexport function generateFilePath(options: StorageUploadOptions, fileName: string): string {\n const { orgId, isPublic = false, customPath } = options;\n \n \n // Validate required orgId\n if (!orgId) {\n throw new Error('orgId is required for file path generation');\n }\n \n if (isPublic) {\n // Public files go to {orgId}/{category}/filename\n if (customPath) {\n return `${orgId}/${customPath}/${fileName}`;\n }\n return `${orgId}/public/${fileName}`;\n }\n \n // Organization-first structure: {orgId}/{category}/filename\n if (customPath) {\n return `${orgId}/${customPath}/${fileName}`;\n }\n \n // Use customPath if available, otherwise default to files\n const pathCategory = customPath || 'files';\n return `${orgId}/${pathCategory}/${fileName}`;\n}\n\n/**\n * Generate a unique filename with timestamp and UUID\n * Sanitizes the original filename to remove spaces and invalid characters for storage compatibility\n */\nexport function generateUniqueFileName(originalName: string): string {\n const timestamp = Date.now();\n const uuid = crypto.randomUUID();\n const extension = originalName.split('.').pop() || '';\n let baseName = originalName.replace(/\\.[^/.]+$/, '');\n \n // Sanitize the base name for storage compatibility\n // Replace spaces with underscores and remove invalid characters\n baseName = baseName\n .trim()\n .replace(/\\s+/g, '_') // Replace spaces with underscores\n .replace(/[<>:\"/\\\\|?*]/g, '') // Remove invalid file name characters\n .replace(/\\.\\./g, '') // Remove directory traversal attempts\n .replace(/^\\.+|\\.+$/g, '') // Remove leading/trailing dots\n .substring(0, 200); // Limit length to leave room for timestamp and UUID\n \n // If there's no extension, don't add one\n if (!extension || extension === originalName) {\n return `${timestamp}-${uuid}-${baseName}`;\n }\n \n return `${timestamp}-${uuid}-${baseName}.${extension}`;\n}\n\n/**\n * Extract file metadata from a File object\n */\nexport async function extractFileMetadata(\n file: File, \n options: StorageUploadOptions,\n uploadedBy: string\n): Promise<StorageFileMetadata> {\n const metadata: StorageFileMetadata = {\n mimeType: file.type,\n size: file.size,\n orgId: options.orgId,\n appName: options.appName || 'pace-core',\n uploadedBy,\n uploadedAt: new Date().toISOString(),\n tags: options.tags || [],\n isPublic: options.isPublic || false,\n customMetadata: options.metadata || {}\n };\n\n // Extract image dimensions if it's an image\n if (file.type.startsWith('image/')) {\n try {\n const dimensions = await getImageDimensions(file);\n metadata.width = dimensions.width;\n metadata.height = dimensions.height;\n } catch (error) {\n // Non-critical error - image dimensions are optional metadata\n // Using Logger would be better, but this is in a utility function\n // For now, silently continue - dimensions are optional\n }\n }\n\n // Generate file hash if possible\n try {\n metadata.hash = await generateFileHash(file);\n } catch (error) {\n // Non-critical error - file hash is optional metadata\n // Using Logger would be better, but this is in a utility function\n // For now, silently continue - hash is optional\n }\n\n return metadata;\n}\n\n/**\n * Get image dimensions from a File object\n */\nasync function getImageDimensions(file: File): Promise<{ width: number; height: number }> {\n return new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(file);\n \n img.onload = () => {\n URL.revokeObjectURL(url);\n resolve({ width: img.width, height: img.height });\n };\n \n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Could not load image'));\n };\n \n img.src = url;\n });\n}\n\n/**\n * Generate a hash for a file\n */\nasync function generateFileHash(file: File): Promise<string> {\n const buffer = await file.arrayBuffer();\n const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n return `sha256:${hashHex}`;\n}\n\n/**\n * Ensure a folder exists in the storage bucket\n * In Supabase storage, folders are created automatically when files are uploaded,\n * but this function explicitly creates the folder structure by uploading a placeholder\n * file if the folder doesn't exist\n * @param supabase - Supabase client instance\n * @param folderPath - Folder path to ensure exists (e.g., 'orgId/folder')\n * @param bucketName - Bucket name\n * @returns True if folder exists or was created, false on error\n */\nasync function ensureFolderExists(\n supabase: SupabaseClient,\n folderPath: string,\n bucketName: string\n): Promise<boolean> {\n try {\n // Check if folder exists by trying to list it\n const { data, error } = await supabase.storage\n .from(bucketName)\n .list(folderPath, {\n limit: 1\n });\n\n // If listing succeeds (even with empty data), the folder exists\n if (!error) {\n return true;\n }\n\n // If we get a \"not found\" error, the folder doesn't exist yet\n // Create it by uploading a placeholder file\n // Supabase storage doesn't support empty folders, so we create a .keep file\n const placeholderPath = `${folderPath}/.keep`;\n const placeholderBlob = new Blob([''], { type: 'text/plain' });\n const placeholderFile = new File([placeholderBlob], '.keep', { type: 'text/plain' });\n\n const { error: uploadError } = await supabase.storage\n .from(bucketName)\n .upload(placeholderPath, placeholderFile, {\n cacheControl: '3600',\n upsert: true, // Use upsert to avoid errors if file already exists\n contentType: 'text/plain'\n });\n\n if (uploadError) {\n // If we can't create the placeholder, log it but don't fail\n // The folder will be created automatically when we upload the actual file\n log.debug(`Could not create folder placeholder (will be created on upload): ${uploadError.message}`);\n return true; // Still return true - folder will be created on actual file upload\n }\n\n // Folder structure now exists\n return true;\n } catch (error) {\n // If there's an exception, log it but proceed anyway\n // The folder structure will be created when we upload the actual file\n log.debug(`Folder creation exception (will be created on upload): ${error instanceof Error ? error.message : 'Unknown error'}`);\n return true; // Return true to proceed - folder will be created on actual file upload\n }\n}\n\n/**\n * Upload a file to Supabase storage with app segregation\n */\nexport async function uploadFile(\n supabase: SupabaseClient,\n file: File,\n options: StorageUploadOptions\n): Promise<StorageUploadResult> {\n try {\n // Validate file size\n const sizeValidation = validateFileSize(file);\n if (!sizeValidation.isValid) {\n return {\n success: false,\n error: sizeValidation.error\n };\n }\n\n // Generate unique filename and path\n const uniqueFileName = generateUniqueFileName(file.name);\n const filePath = generateFilePath(options, uniqueFileName);\n\n // Extract folder path from file path (everything except the filename)\n const folderPath = filePath.substring(0, filePath.lastIndexOf('/'));\n\n // Extract metadata\n const metadata = await extractFileMetadata(file, options, 'current-user'); // TODO: Get actual user ID\n\n // Select bucket based on isPublic flag\n const bucketName = getBucketName(options.isPublic || false);\n\n // Ensure folder exists (Supabase creates folders automatically on upload,\n // but we verify the path is accessible)\n await ensureFolderExists(supabase, folderPath, bucketName);\n\n // Upload file to Supabase\n // Note: Supabase will automatically create the folder structure if it doesn't exist\n const { data, error } = await supabase.storage\n .from(bucketName)\n .upload(filePath, file, {\n cacheControl: '3600',\n upsert: false,\n contentType: file.type\n });\n\n if (error) {\n return {\n success: false,\n error: `Upload failed: ${error.message}`\n };\n }\n\n // Generate public URL if file is public\n let publicUrl: string | undefined;\n if (options.isPublic) {\n const { data: urlData } = supabase.storage\n .from(bucketName)\n .getPublicUrl(filePath);\n publicUrl = urlData.publicUrl;\n }\n\n return {\n success: true,\n path: filePath,\n publicUrl,\n metadata\n };\n\n } catch (error) {\n return {\n success: false,\n error: `Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n\n/**\n * Get a public URL for a file\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: true)\n * @returns Public URL for the file\n */\nexport function getPublicUrl(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = true\n): string {\n if (!supabase) {\n throw new Error('Supabase client is required to generate a public URL');\n }\n\n if (!path || typeof path !== 'string') {\n throw new Error('A valid storage path is required to generate a public URL');\n }\n\n // If the path is already an absolute URL, return it directly\n if (/^https?:\\/\\//i.test(path)) {\n return path;\n }\n\n // Normalise path by trimming whitespace and leading slashes\n let normalisedPath = path.trim().replace(/^\\/+/, '');\n\n if (!normalisedPath) {\n throw new Error('Storage path cannot be empty after normalisation');\n }\n\n const { bucketNameFromPath, storagePath } = resolveBucketHint(normalisedPath);\n\n const bucketName = bucketNameFromPath || getBucketName(isPublic);\n\n const { data } = supabase.storage\n .from(bucketName)\n .getPublicUrl(storagePath);\n\n return data.publicUrl;\n}\n\nfunction resolveBucketHint(pathWithPotentialBucket: string): { bucketNameFromPath: string | null; storagePath: string } {\n const BUCKET_NAME_PATTERN = /^[a-z0-9][a-z0-9-_\\.]{1,62}$/i;\n const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n const KNOWN_BUCKET_NAMES = new Set(['files', 'public-files']);\n\n const trimmedPath = pathWithPotentialBucket.trim();\n\n if (!trimmedPath) {\n throw new Error('Storage path cannot be empty after normalisation');\n }\n\n // Support \"bucket::path\" notation to remove ambiguity with colon usage in file names.\n const doubleColonIndex = trimmedPath.indexOf('::');\n if (doubleColonIndex > 0) {\n const potentialBucket = trimmedPath.slice(0, doubleColonIndex).trim();\n const remainingPath = trimmedPath.slice(doubleColonIndex + 2).replace(/^\\/+/, '');\n\n if (potentialBucket && remainingPath && BUCKET_NAME_PATTERN.test(potentialBucket)) {\n return {\n bucketNameFromPath: potentialBucket,\n storagePath: remainingPath\n };\n }\n }\n\n // Support \"bucket/path\" hints, provided the first segment looks like a bucket name and not a directory prefix.\n const firstSlashIndex = trimmedPath.indexOf('/');\n if (firstSlashIndex > 0) {\n const potentialBucket = trimmedPath.slice(0, firstSlashIndex).trim();\n const remainingPath = trimmedPath.slice(firstSlashIndex + 1).replace(/^\\/+/, '');\n\n if (\n potentialBucket &&\n remainingPath &&\n BUCKET_NAME_PATTERN.test(potentialBucket) &&\n !UUID_PATTERN.test(potentialBucket) &&\n (KNOWN_BUCKET_NAMES.has(potentialBucket) || !potentialBucket.includes('-')) &&\n !/^\\d+$/.test(potentialBucket)\n ) {\n return {\n bucketNameFromPath: potentialBucket,\n storagePath: remainingPath\n };\n }\n }\n\n return {\n bucketNameFromPath: null,\n storagePath: trimmedPath\n };\n}\n\n/**\n * Get a signed URL for a protected file\n * Private files are always in the 'files' bucket, so this always uses 'files' bucket\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param options - URL options including expiry time\n * @returns Signed URL with expiration timestamp, or null if failed\n */\nexport async function getSignedUrl(\n supabase: SupabaseClient,\n path: string,\n options: StorageUrlOptions\n): Promise<{ url: string; expiresAt: string } | null> {\n try {\n // Signed URLs are only for private files, which are always in the 'files' bucket\n const bucketName = getBucketName(false);\n \n const { data, error } = await supabase.storage\n .from(bucketName)\n .createSignedUrl(path, options.expiresIn || 3600);\n\n if (error) {\n log.error('Failed to create signed URL:', error);\n return null;\n }\n\n return {\n url: data.signedUrl,\n expiresAt: new Date(Date.now() + (options.expiresIn || 3600) * 1000).toISOString()\n };\n } catch (error) {\n log.error('Failed to create signed URL:', error);\n return null;\n }\n}\n\n/**\n * Delete a file from storage\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: false)\n * @returns Success status and optional error message\n */\nexport async function deleteFile(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = false\n): Promise<{ success: boolean; error?: string }> {\n try {\n const bucketName = getBucketName(isPublic);\n \n const { error } = await supabase.storage\n .from(bucketName)\n .remove([path]);\n\n if (error) {\n return {\n success: false,\n error: `Delete failed: ${error.message}`\n };\n }\n\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: `Delete failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n\n/**\n * List files in an organization scope\n * @param supabase - Supabase client instance\n * @param options - List options including bucket selection via isPublic\n * @returns List of files with metadata\n */\nexport async function listFiles(\n supabase: SupabaseClient,\n options: StorageListOptions & { isPublic?: boolean }\n): Promise<StorageListResult> {\n try {\n // Select bucket based on isPublic flag (default to private files bucket)\n const bucketName = getBucketName(options.isPublic || false);\n \n // Organization-first structure: {orgId}/{category}/\n const pathPrefix = `${options.orgId}/`;\n const searchPath = options.pathPrefix ? `${pathPrefix}${options.pathPrefix}` : pathPrefix;\n\n const { data, error } = await supabase.storage\n .from(bucketName)\n .list(searchPath, {\n limit: options.limit || 100,\n offset: options.offset || 0,\n sortBy: { column: 'created_at', order: 'desc' }\n });\n\n if (error) {\n log.error('Failed to list files:', error);\n return { files: [], totalCount: 0, hasMore: false };\n }\n\n const files: StorageFileInfo[] = (data || []).map(item => ({\n name: item.name,\n path: `${searchPath}${item.name}`,\n size: item.metadata?.size || 0,\n mimeType: item.metadata?.mimetype || 'application/octet-stream',\n lastModified: item.updated_at || item.created_at || new Date().toISOString(),\n metadata: {\n mimeType: item.metadata?.mimetype || 'application/octet-stream',\n size: item.metadata?.size || 0,\n orgId: options.orgId,\n appName: options.appName,\n uploadedBy: 'unknown',\n uploadedAt: item.created_at || new Date().toISOString(),\n isPublic: options.isPublic || false\n }\n }));\n\n return {\n files,\n totalCount: files.length,\n hasMore: files.length >= (options.limit || 100)\n };\n } catch (error) {\n log.error('Failed to list files:', error);\n return { files: [], totalCount: 0, hasMore: false };\n }\n}\n\n/**\n * Download a file from storage\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param isPublic - Whether the file is in the public-files bucket (default: false)\n * @returns File blob with metadata, or null if failed\n */\nexport async function downloadFile(\n supabase: SupabaseClient,\n path: string,\n isPublic: boolean = false\n): Promise<{ blob: Blob; metadata: { name: string; size: number; type: string } } | null> {\n try {\n const bucketName = getBucketName(isPublic);\n \n const { data, error } = await supabase.storage\n .from(bucketName)\n .download(path);\n\n if (error) {\n log.error('Failed to download file:', error);\n return null;\n }\n\n if (!data) {\n return null;\n }\n\n // Extract file name from path\n const fileName = path.split('/').pop() || 'download';\n \n // Get file metadata\n const { data: fileInfo } = await supabase.storage\n .from(bucketName)\n .list(path.split('/').slice(0, -1).join('/'), {\n search: fileName\n });\n\n const metadata = fileInfo?.[0]?.metadata || {};\n \n return {\n blob: data,\n metadata: {\n name: fileName,\n size: metadata.size || data.size,\n type: metadata.mimetype || 'application/octet-stream'\n }\n };\n } catch (error) {\n log.error('Failed to download file:', error);\n return null;\n }\n}\n\n/**\n * Move a file to archived location (soft delete)\n * @param supabase - Supabase client instance\n * @param path - File path within the bucket\n * @param options - Archive options including bucket selection via isPublic\n */\nexport async function archiveFile(\n supabase: SupabaseClient,\n path: string,\n options: { appName: string; orgId: string; isPublic?: boolean }\n): Promise<{ success: boolean; error?: string }> {\n try {\n const bucketName = getBucketName(options.isPublic || false);\n \n // Generate archived path for organization-first structure\n const archivedPath = path.replace(`${options.orgId}/`, `archived/${options.orgId}/`);\n \n // Copy file to archived location\n const { error: copyError } = await supabase.storage\n .from(bucketName)\n .copy(path, archivedPath);\n\n if (copyError) {\n return {\n success: false,\n error: `Archive failed: ${copyError.message}`\n };\n }\n\n // Delete original file\n const deleteResult = await deleteFile(supabase, path, options.isPublic || false);\n if (!deleteResult.success) {\n return deleteResult;\n }\n\n return { success: true };\n } catch (error) {\n return {\n success: false,\n error: `Archive failed: ${error instanceof Error ? error.message : 'Unknown error'}`\n };\n }\n}\n","/**\n * @file File Display Hook (Authenticated)\n * @package @jmruthers/pace-core\n * @module Hooks\n *\n * A React hook for accessing file references in authenticated contexts.\n * Can handle both public and private files using the file_references system.\n *\n * Features:\n * - Works in authenticated contexts\n * - Supports both public and private files\n * - Automatic signed URL generation for private files\n * - Caching for performance\n * - Error handling and loading states\n * - Supports both single file (category) and multiple files\n *\n * @example\n * ```tsx\n * import { useFileDisplay } from '@jmruthers/pace-core';\n *\n * function FileView() {\n * const { fileUrl, fileReference, isLoading, error } = useFileDisplay(\n * 'event',\n * eventId,\n * organisationId,\n * FileCategory.EVENT_LOGOS\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return fileUrl ? <img src={fileUrl} alt=\"File\" /> : null;\n * }\n * ```\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport { FileReference, FileCategory } from '../types/file-reference';\nimport { getPublicUrl, getSignedUrl } from '../utils/storage/helpers';\nimport { createFileReferenceService } from '../utils/file-reference';\nimport { logger } from '../utils/core/logger';\n\n// Simple in-memory cache for authenticated file data\nconst authenticatedFileCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\n// Cache size limit to prevent memory leaks\nconst MAX_CACHE_SIZE = 100;\n\n// Helper function to clean up expired entries and enforce size limit\nfunction cleanupCache() {\n const now = Date.now();\n const entries = Array.from(authenticatedFileCache.entries());\n \n // Remove expired entries\n const expiredKeys: string[] = [];\n entries.forEach(([key, value]) => {\n if (now - value.timestamp >= value.ttl) {\n expiredKeys.push(key);\n }\n });\n expiredKeys.forEach(key => authenticatedFileCache.delete(key));\n \n // If still over limit, remove oldest entries\n if (authenticatedFileCache.size > MAX_CACHE_SIZE) {\n const sorted = entries\n .filter(([key]) => !expiredKeys.includes(key))\n .sort((a, b) => a[1].timestamp - b[1].timestamp);\n const toRemove = sorted.slice(0, authenticatedFileCache.size - MAX_CACHE_SIZE);\n toRemove.forEach(([key]) => authenticatedFileCache.delete(key));\n }\n}\n\nexport interface UseFileDisplayReturn {\n /** Single file URL if category is provided and file found, null otherwise */\n fileUrl: string | null;\n /** Single file reference if category is provided and file found, null otherwise */\n fileReference: FileReference | null;\n /** Array of all file references for the record (when category not provided or for multiple files) */\n fileReferences: FileReference[];\n /** Map of file IDs to URLs for multiple files */\n fileUrls: Map<string, string>;\n /** Total count of files for the record */\n fileCount: number;\n /** Whether the data is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the data */\n refetch: () => Promise<void>;\n}\n\nexport interface UseFileDisplayOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Supabase client instance (required) */\n supabase: SupabaseClient | null;\n}\n\n/**\n * Hook for accessing file references in authenticated contexts\n * \n * This hook provides access to file references for authenticated users.\n * It supports both public and private files, generating appropriate URLs\n * (public URLs for public files, signed URLs for private files).\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to filter by (for single file mode)\n * @param options - Configuration options for caching and behavior\n * @returns Object containing file data, loading state, error, and refetch function\n */\nexport function useFileDisplay(\n table_name: string | undefined,\n record_id: string | undefined,\n organisation_id: string | undefined,\n category: FileCategory | undefined,\n options: UseFileDisplayOptions\n): UseFileDisplayReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n supabase\n } = options;\n\n const [fileUrl, setFileUrl] = useState<string | null>(null);\n const [fileReference, setFileReference] = useState<FileReference | null>(null);\n const [fileReferences, setFileReferences] = useState<FileReference[]>([]);\n const [fileUrls, setFileUrls] = useState<Map<string, string>>(new Map());\n const [fileCount, setFileCount] = useState<number>(0);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchFiles = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n return;\n }\n\n // Validate UUID format for organisationId to prevent database errors\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(organisation_id)) {\n logger.warn('useFileDisplay', 'Invalid organisationId format (not a valid UUID):', organisation_id);\n }\n\n // Check cache first\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n if (enableCache) {\n const cached = authenticatedFileCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n const cachedData = cached.data;\n \n // FIX: Regenerate signed URL for private files if missing\n if (cachedData.fileReference && !cachedData.fileUrl && \n cachedData.fileReference.is_public === false && supabase) {\n // Regenerate signed URL without fetching from database\n try {\n const signedUrlResult = await getSignedUrl(supabase, cachedData.fileReference.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n const regeneratedUrl = signedUrlResult?.url || null;\n setFileUrl(regeneratedUrl);\n setFileReference(cachedData.fileReference);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n } catch (err) {\n // If signed URL regeneration fails, fall through to normal fetch\n logger.warn('useFileDisplay', 'Failed to regenerate signed URL from cache, falling back to fetch:', err);\n }\n }\n \n // Normal cache hit for public files or files with URLs\n setFileUrl(cachedData.fileUrl || null);\n setFileReference(cachedData.fileReference || null);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n const service = createFileReferenceService(supabase);\n let files: FileReference[] = [];\n\n // CRITICAL: When category is provided, MUST use RPC function, not direct queries\n // Category is stored in file_metadata JSONB field, not a direct column\n if (category) {\n // Single file mode - get files by category using RPC\n logger.debug('useFileDisplay', 'Using RPC function for category filtering:', {\n table_name,\n record_id,\n category,\n organisation_id\n });\n files = await service.getFilesByCategory(\n table_name,\n record_id,\n category,\n organisation_id\n );\n } else {\n // Multiple files mode - get all files using RPC\n files = await service.listFileReferences(\n table_name,\n record_id,\n organisation_id\n );\n }\n\n if (files.length === 0) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n \n // Cache empty result\n if (enableCache) {\n authenticatedFileCache.set(cacheKey, {\n data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: new Map(), fileCount: 0 },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n cleanupCache();\n }\n return;\n }\n\n setFileReferences(files);\n setFileCount(files.length);\n\n if (category && files.length > 0) {\n // Single file mode - get first file\n const firstFile = files[0];\n logger.debug('useFileDisplay', 'Processing category files, first file:', {\n id: firstFile.id,\n file_path: firstFile.file_path,\n is_public: firstFile.is_public,\n has_file_metadata: !!firstFile.file_metadata,\n category_in_metadata: firstFile.file_metadata?.category\n });\n \n setFileReference(firstFile);\n \n // Generate URL based on file visibility\n let url: string | null = null;\n if (firstFile.is_public) {\n url = getPublicUrl(supabase, firstFile.file_path, true);\n logger.debug('useFileDisplay', 'Generated public URL:', url);\n } else {\n const signedUrlResult = await getSignedUrl(supabase, firstFile.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n url = signedUrlResult?.url || null;\n logger.debug('useFileDisplay', 'Generated signed URL:', url ? 'URL generated' : 'URL generation failed');\n }\n logger.debug('useFileDisplay', 'Setting file URL:', url ? 'URL set' : 'URL is null');\n setFileUrl(url);\n } else {\n // Multiple files mode - generate URLs for all files\n const urlMap = new Map<string, string>();\n for (const fileRef of files) {\n let url: string | null = null;\n if (fileRef.is_public) {\n url = getPublicUrl(supabase, fileRef.file_path, true);\n } else {\n const signedUrlResult = await getSignedUrl(supabase, fileRef.file_path, {\n appName: 'pace-core',\n orgId: organisation_id,\n expiresIn: 3600\n });\n url = signedUrlResult?.url || null;\n }\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n setFileUrls(urlMap);\n setFileReference(null);\n setFileUrl(null);\n }\n\n // Cache the result\n if (enableCache) {\n // Prepare cache data\n let cacheData: any = {\n fileReference: category && files.length > 0 ? files[0] : null,\n fileReferences: files,\n fileUrls: new Map(),\n fileCount: files.length\n };\n\n if (category && files.length > 0) {\n const firstFile = files[0];\n let url: string | null = null;\n if (firstFile.is_public) {\n url = getPublicUrl(supabase, firstFile.file_path, true);\n }\n cacheData.fileUrl = url;\n } else {\n const urlMap = new Map<string, string>();\n for (const fileRef of files) {\n if (fileRef.is_public) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n }\n cacheData.fileUrls = urlMap;\n }\n\n authenticatedFileCache.set(cacheKey, {\n data: cacheData,\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n cleanupCache();\n }\n\n } catch (err) {\n logger.error('useFileDisplay', 'Error fetching files:', err);\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n } finally {\n setIsLoading(false);\n }\n }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);\n\n // Fetch files when parameters change\n useEffect(() => {\n if (table_name && record_id && organisation_id && supabase) {\n fetchFiles();\n } else {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchFiles, table_name, record_id, organisation_id, supabase]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) return;\n \n // Clear cache for this file\n if (enableCache) {\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n authenticatedFileCache.delete(cacheKey);\n }\n await fetchFiles();\n }, [fetchFiles, table_name, record_id, organisation_id, category, supabase, enableCache]);\n\n return {\n fileUrl,\n fileReference,\n fileReferences,\n fileUrls,\n fileCount,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached authenticated file data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearFileDisplayCache(): void {\n for (const [key] of authenticatedFileCache) {\n if (key.startsWith('file_')) {\n authenticatedFileCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getFileDisplayCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(authenticatedFileCache.keys()).filter(key => key.startsWith('file_'));\n return {\n size: keys.length,\n keys\n };\n}\n\n/**\n * Invalidate cache for a specific file display entry\n * Useful for clearing cache after file uploads or updates\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to invalidate (if provided, also invalidates 'all' category)\n */\nexport function invalidateFileDisplayCache(\n table_name: string,\n record_id: string,\n organisation_id: string,\n category?: FileCategory\n): void {\n const cacheKey = `file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n authenticatedFileCache.delete(cacheKey);\n // Also invalidate 'all' category if specific category invalidated\n if (category) {\n const allCategoryKey = `file_${table_name}_${record_id}_${organisation_id}_all`;\n authenticatedFileCache.delete(allCategoryKey);\n }\n}\n\n","// File Reference Service\n// Provides CRUD operations for the centralized file reference system\n\nimport { SupabaseClient } from '@supabase/supabase-js';\nimport { \n FileReference, \n FileUploadOptions, \n FileReferenceService, \n FileUploadResult,\n FileCategory,\n FileMetadata\n} from '../../types/file-reference';\nimport { uploadFile, getPublicUrl, getSignedUrl, deleteFile, extractFileMetadata } from '../storage/helpers';\nimport { setOrganisationContext } from '../context/organisationContext';\nimport { invalidateFileDisplayCache } from '../../hooks/useFileDisplay';\nimport { createLogger } from '../core/logger';\nimport { assertAppId } from '../../types/core';\n\nconst log = createLogger('FileReferenceService');\n\nexport class FileReferenceServiceImpl implements FileReferenceService {\n constructor(private supabase: SupabaseClient) {}\n\n /**\n * Creates a file reference by uploading a file to storage and linking it in the database.\n * \n * Storage Flow:\n * 1. Upload file to storage bucket first (files or public-files based on is_public flag)\n * - Path format: {orgId}/{category}/{timestamp-uuid-filename}\n * - Bucket selection: 'files' (private) or 'public-files' (public)\n * 2. Extract file metadata (dimensions, hash, etc.)\n * 3. Set organisation context for RLS policies\n * 4. Create database reference via RPC function\n * 5. If DB insert fails, rollback by deleting uploaded file\n * \n * This ensures atomicity: either both storage and DB succeed, or both are cleaned up.\n */\n async createFileReference(options: FileUploadOptions, file: File): Promise<FileReference> {\n try {\n\n // Validate required options\n if (!options.organisation_id) {\n throw new Error('organisation_id is required for file upload');\n }\n if (!options.table_name) {\n throw new Error('table_name is required for file upload');\n }\n if (!options.record_id) {\n throw new Error('record_id is required for file upload');\n }\n\n // Step 1: Upload file to storage bucket first\n // This generates a unique path: {orgId}/{folder}/{timestamp-uuid-filename}\n // Bucket is automatically selected based on is_public flag\n const uploadResult = await uploadFile(this.supabase, file, {\n appName: 'file-reference',\n orgId: options.organisation_id,\n isPublic: options.is_public || false,\n customPath: options.folder // Use folder prop as the custom path segment\n });\n if (!uploadResult.success) {\n throw new Error(`Failed to upload file: ${uploadResult.error}`);\n }\n\n if (!uploadResult.path) {\n throw new Error('File upload did not return a path');\n }\n\n const filePath = uploadResult.path;\n\n // Step 2: Extract file metadata (dimensions, hash, etc.)\n const metadata = await extractFileMetadata(file, {\n appName: 'file-reference',\n orgId: options.organisation_id,\n isPublic: options.is_public || false\n }, 'system');\n\n // Step 3: Set organisation context in database session before creating file reference\n // This ensures RLS policies can check the organisation context\n await setOrganisationContext(this.supabase, options.organisation_id);\n\n // Step 4: Create file reference in database using RPC function\n // This links the storage path to the record in file_references table\n const { data, error } = await this.supabase\n .rpc('data_file_reference_create', {\n p_table_name: options.table_name,\n p_record_id: options.record_id,\n p_file_path: filePath, // Storage path from step 1\n p_organisation_id: options.organisation_id,\n p_app_id: options.app_id,\n p_page_context: options.pageContext,\n p_event_id: options.event_id || null, // Pass event_id for event-based apps\n p_file_metadata: {\n fileName: file.name,\n fileType: file.type,\n fileSize: file.size,\n category: options.category,\n ...metadata,\n ...options.custom_metadata\n },\n p_is_public: options.is_public || false\n });\n\n // Step 5: Rollback - if database insert fails, clean up uploaded file\n if (error) {\n await deleteFile(this.supabase, filePath, options.is_public || false);\n throw new Error(`Failed to create file reference: ${error.message}`);\n }\n\n // Check if RPC returned null (permission denied or other failure)\n if (!data || data === null) {\n // Clean up the uploaded file since DB insert failed\n await deleteFile(this.supabase, filePath, options.is_public || false);\n throw new Error(`File upload denied: insufficient permissions. You need 'create:page.${options.pageContext}' or 'update:page.${options.pageContext}' permission for the '${options.pageContext}' page. Make sure the page exists in rbac_app_pages table.`);\n }\n\n // Get the created file reference\n const { data: fileRef, error: fetchError } = await this.supabase\n .from('file_references')\n .select('*')\n .eq('id', data)\n .single();\n\n if (fetchError || !fileRef) {\n // Clean up uploaded file if we can't fetch the reference\n await deleteFile(this.supabase, filePath, options.is_public || false);\n throw new Error(`Failed to fetch created file reference: ${fetchError?.message}`);\n }\n\n // Invalidate cache for this file display entry so newly uploaded files appear immediately\n invalidateFileDisplayCache(\n options.table_name,\n options.record_id,\n options.organisation_id,\n options.category\n );\n\n return fileRef as FileReference;\n } catch (error) {\n log.error('Error creating file reference:', error);\n throw error;\n }\n }\n\n async getFileReference(table_name: string, record_id: string, organisation_id: string): Promise<FileReference | null> {\n try {\n const { data, error } = await this.supabase\n .from('file_references')\n .select('*')\n .eq('table_name', table_name)\n .eq('record_id', record_id)\n .eq('organisation_id', organisation_id)\n .single();\n\n if (error) {\n if (error.code === 'PGRST116') {\n return null; // No rows found\n }\n throw new Error(`Failed to get file reference: ${error.message}`);\n }\n\n return data as FileReference;\n } catch (error) {\n log.error('Error getting file reference:', error);\n throw error;\n }\n }\n\n async getFileUrl(table_name: string, record_id: string, organisation_id: string): Promise<string | null> {\n try {\n // Get file reference to check if it's public\n const fileRef = await this.getFileReference(table_name, record_id, organisation_id);\n if (!fileRef) {\n return null;\n }\n\n // For public files, RPC returns file path - generate public URL client-side\n if (fileRef.is_public) {\n const { data: pathData } = await this.supabase\n .rpc('data_file_reference_url_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (!pathData) {\n return null;\n }\n\n // Generate public URL using bucket-aware helper\n return getPublicUrl(this.supabase, pathData, true);\n } else {\n // For private files, use signed URL\n return await this.getSignedUrl(table_name, record_id, organisation_id);\n }\n } catch (error) {\n log.error('Error getting file URL:', error);\n throw error;\n }\n }\n\n async getSignedUrl(table_name: string, record_id: string, organisation_id: string, expires_in: number = 3600): Promise<string | null> {\n try {\n // Get file path from RPC function\n const { data: filePath, error } = await this.supabase\n .rpc('data_file_reference_signed_url_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id,\n p_expires_in: expires_in\n });\n\n if (error) {\n throw new Error(`Failed to get signed URL: ${error.message}`);\n }\n\n if (!filePath) {\n return null;\n }\n\n // Generate signed URL client-side using bucket-aware helper (files bucket for private files)\n const signedUrlResult = await getSignedUrl(this.supabase, filePath, {\n appName: 'file-reference',\n orgId: organisation_id,\n expiresIn: expires_in\n });\n\n return signedUrlResult?.url || null;\n } catch (error) {\n log.error('Error getting signed URL:', error);\n throw error;\n }\n }\n\n async updateFileReference(id: string, updates: Partial<FileReference>): Promise<FileReference> {\n try {\n const { data, error } = await this.supabase\n .from('file_references')\n .update(updates)\n .eq('id', id)\n .select()\n .single();\n\n if (error) {\n throw new Error(`Failed to update file reference: ${error.message}`);\n }\n\n return data as FileReference;\n } catch (error) {\n log.error('Error updating file reference:', error);\n throw error;\n }\n }\n\n async deleteFileReference(table_name: string, record_id: string, organisation_id: string, delete_file: boolean = false): Promise<boolean> {\n try {\n // Get file reference first to determine bucket\n const fileRef = await this.getFileReference(table_name, record_id, organisation_id);\n \n const { error } = await this.supabase\n .rpc('data_file_reference_delete', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id,\n p_delete_file: delete_file\n });\n\n if (error) {\n throw new Error(`Failed to delete file reference: ${error.message}`);\n }\n\n // If delete_file is true and we have the file reference, delete from storage\n if (delete_file && fileRef) {\n await deleteFile(this.supabase, fileRef.file_path, fileRef.is_public || false);\n }\n\n return true;\n } catch (error) {\n log.error('Error deleting file reference:', error);\n throw error;\n }\n }\n\n async listFileReferences(table_name: string, record_id: string, organisation_id: string): Promise<FileReference[]> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to list file references: ${error.message}`);\n }\n\n // RPC returns partial data, need to fetch full file references\n if (!data || data.length === 0) {\n return [];\n }\n\n // Fetch full file reference data for each ID\n // RPC returns array of objects with at least an 'id' property\n const ids = data.map((item: { id: string }) => item.id);\n const { data: fullData, error: fetchError } = await this.supabase\n .from('file_references')\n .select('*')\n .in('id', ids);\n\n if (fetchError) {\n throw new Error(`Failed to fetch file references: ${fetchError.message}`);\n }\n\n return (fullData || []) as FileReference[];\n } catch (error) {\n log.error('Error listing file references:', error);\n throw error;\n }\n }\n\n async getFileCount(table_name: string, record_id: string, organisation_id: string): Promise<number> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_count_get', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to get file count: ${error.message}`);\n }\n\n return data || 0;\n } catch (error) {\n log.error('Error getting file count:', error);\n throw error;\n }\n }\n\n async getFileReferenceById(id: string, organisation_id: string): Promise<FileReference | null> {\n try {\n const { data, error } = await this.supabase\n .rpc('data_file_reference_get', {\n p_file_reference_id: id,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n throw new Error(`Failed to get file reference by ID: ${error.message}`);\n }\n\n if (!data || data.length === 0) {\n return null;\n }\n\n return data[0] as FileReference;\n } catch (error) {\n log.error('Error getting file reference by ID:', error);\n throw error;\n }\n }\n\n async getFilesByCategory(\n table_name: string, \n record_id: string, \n category: FileCategory, \n organisation_id: string\n ): Promise<FileReference[]> {\n try {\n // CRITICAL: Use RPC function to get files by category - this correctly filters on file_metadata->>'category'\n // NOTE: We MUST use RPC function. Direct queries with .eq('category', ...) will FAIL with HTTP 406\n // because there is NO 'category' column. The category is stored in the JSONB file_metadata field.\n const { data, error } = await this.supabase\n .rpc('data_file_reference_by_category_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_category: category,\n p_organisation_id: organisation_id\n });\n\n if (error) {\n // Provide clear error message about category filtering\n log.error('RPC ERROR getting files by category:', {\n error,\n errorCode: error.code,\n errorMessage: error.message,\n errorDetails: error.details,\n table_name,\n record_id,\n category,\n organisation_id,\n message: 'CRITICAL: Category filtering MUST use RPC function data_file_reference_by_category_list. Direct queries with .eq(\\'category\\', ...) will FAIL with HTTP 406 because category is stored in file_metadata JSONB field, not a direct column.'\n });\n throw new Error(`Failed to get files by category: ${error.message}. Category filtering uses file_metadata JSONB field, not a direct column. RPC function required.`);\n }\n\n // RPC returns partial data with: id, file_path, file_metadata, is_public, created_at\n // We have table_name, record_id, organisation_id from function parameters\n // We can construct FileReference objects directly without another query (avoiding RLS issues)\n if (!data || data.length === 0) {\n return [];\n }\n\n // Construct FileReference objects from RPC response\n // This avoids RLS issues with direct queries - the RPC already validated permissions\n interface RpcFileItem {\n id: string;\n file_path: string;\n file_metadata: { category?: string; app_id?: string; [key: string]: unknown };\n is_public?: boolean;\n created_at?: string;\n }\n const fileReferences: FileReference[] = data\n .filter((item: RpcFileItem) => {\n // Verify category matches (defensive check)\n const fileCategory = item.file_metadata?.category;\n const matches = fileCategory === category;\n if (!matches) {\n log.warn('File category mismatch in RPC response:', {\n fileId: item.id,\n expectedCategory: category,\n actualCategory: fileCategory\n });\n }\n return matches && item.id && item.file_path && item.file_metadata;\n })\n .map((item: RpcFileItem) => {\n // Extract file name and type from file_path\n const fileName = item.file_path.split('/').pop() || 'unknown';\n const fileType = fileName.split('.').pop() || 'unknown';\n \n // Construct complete FileReference from RPC response + function parameters\n const fileRef: FileReference = {\n id: item.id,\n table_name: table_name,\n record_id: record_id,\n file_path: item.file_path,\n file_metadata: {\n fileName,\n fileType,\n category: (item.file_metadata?.category as FileCategory) || FileCategory.GENERAL_DOCUMENTS,\n ...(item.file_metadata || {}),\n } as FileMetadata,\n organisation_id: organisation_id,\n app_id: item.file_metadata?.app_id ? assertAppId(item.file_metadata.app_id) : assertAppId(''), // May not be in metadata, use empty string\n is_public: item.is_public ?? false,\n created_at: item.created_at || new Date().toISOString(),\n updated_at: item.created_at || new Date().toISOString() // RPC doesn't return updated_at, use created_at\n };\n return fileRef;\n });\n\n return fileReferences;\n } catch (error) {\n log.error('Error getting files by category:', error);\n throw error;\n }\n }\n\n async uploadMultipleFiles(\n options: FileUploadOptions,\n files: File[]\n ): Promise<import('../../types/file-reference').BulkUploadResult> {\n const success: FileReference[] = [];\n const failed: { file: File; error: string }[] = [];\n const results: Array<{ file: File; result: FileUploadResult | null; error?: string }> = [];\n\n // Upload files sequentially to avoid overwhelming the server\n for (const file of files) {\n try {\n const fileReference = await this.createFileReference(options, file);\n\n success.push(fileReference);\n results.push({\n file,\n result: {\n file_reference: fileReference,\n file_url: fileReference.is_public\n ? getPublicUrl(this.supabase, fileReference.file_path, true)\n : '',\n },\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n failed.push({ file, error: message });\n results.push({ file, result: null, error: message });\n }\n }\n\n return {\n success,\n failed,\n total: results.length,\n successful: success.length,\n results,\n };\n }\n}\n\n// Factory function to create file reference service\nexport function createFileReferenceService(supabase: SupabaseClient): FileReferenceService {\n return new FileReferenceServiceImpl(supabase);\n}\n\n// Helper function to upload file and create reference in one operation\nexport async function uploadFileWithReference(\n supabase: SupabaseClient,\n options: FileUploadOptions,\n file: File\n): Promise<FileUploadResult> {\n \n const service = createFileReferenceService(supabase);\n const fileReference = await service.createFileReference(options, file);\n \n const fileUrl = options.is_public \n ? getPublicUrl(supabase, fileReference.file_path, true)\n : await getSignedUrl(supabase, fileReference.file_path, { \n appName: 'file-reference',\n orgId: options.organisation_id,\n expiresIn: 3600 \n });\n\n const urlString = typeof fileUrl === 'string' ? fileUrl : fileUrl?.url || '';\n\n return {\n file_reference: fileReference,\n file_url: urlString,\n signed_url: options.is_public ? undefined : urlString || undefined\n };\n}\n","/**\n * @file Public File Display Hook\n * @package @jmruthers/pace-core\n * @module Hooks/Public\n * @since 1.0.0\n *\n * A React hook for accessing file references in public contexts without authentication.\n * Provides file URLs and metadata for public pages.\n *\n * Features:\n * - No authentication required\n * - Only returns public files (is_public = true)\n * - Caching for performance\n * - Error handling and loading states\n * - TypeScript support\n * - Supports both single file (category) and multiple files\n *\n * @example\n * ```tsx\n * import { usePublicFileDisplay } from '@jmruthers/pace-core';\n *\n * function PublicFileView() {\n * const { fileUrl, fileReference, isLoading, error } = usePublicFileDisplay(\n * 'event',\n * eventId,\n * organisationId,\n * FileCategory.EVENT_LOGOS,\n * { supabase }\n * );\n *\n * if (isLoading) return <div>Loading...</div>;\n * if (error) return <div>Error: {error.message}</div>;\n *\n * return fileUrl ? <img src={fileUrl} alt=\"File\" /> : null;\n * }\n * ```\n */\n\nimport { useState, useEffect, useCallback } from 'react';\nimport type { SupabaseClient } from '@supabase/supabase-js';\nimport type { Database } from '../../types/database';\nimport { FileReference, FileCategory } from '../../types/file-reference';\nimport { getPublicUrl } from '../../utils/storage/helpers';\nimport { logger } from '../../utils/core/logger';\n\n// Simple in-memory cache for public file data\nconst publicFileCache = new Map<string, { data: any; timestamp: number; ttl: number }>();\n\nexport interface UsePublicFileDisplayReturn {\n /** Single file URL if category is provided and file found, null otherwise */\n fileUrl: string | null;\n /** Single file reference if category is provided and file found, null otherwise */\n fileReference: FileReference | null;\n /** Array of all file references for the record (when category not provided or for multiple files) */\n fileReferences: FileReference[];\n /** Map of file IDs to URLs for multiple files */\n fileUrls: Map<string, string>;\n /** Total count of files for the record */\n fileCount: number;\n /** Whether the data is currently loading */\n isLoading: boolean;\n /** Any error that occurred during loading */\n error: Error | null;\n /** Function to manually refetch the data */\n refetch: () => Promise<void>;\n}\n\nexport interface UsePublicFileDisplayOptions {\n /** Cache TTL in milliseconds (default: 30 minutes) */\n cacheTtl?: number;\n /** Whether to enable caching (default: true) */\n enableCache?: boolean;\n /** Supabase client instance (required) */\n supabase: SupabaseClient<Database>;\n}\n\n/**\n * Hook for accessing public file references\n * \n * This hook provides access to file references without requiring\n * authentication. It only returns public files and generates public URLs.\n * \n * @param table_name - The table name containing the file reference\n * @param record_id - The record ID that owns the file(s)\n * @param organisation_id - The organisation ID for storage path\n * @param category - Optional file category to filter by (for single file mode)\n * @param options - Configuration options for caching and behavior\n * @returns Object containing file data, loading state, error, and refetch function\n */\nexport function usePublicFileDisplay(\n table_name: string | undefined,\n record_id: string | undefined,\n organisation_id: string | undefined,\n category: FileCategory | undefined,\n options: UsePublicFileDisplayOptions\n): UsePublicFileDisplayReturn {\n const {\n cacheTtl = 30 * 60 * 1000, // 30 minutes\n enableCache = true,\n supabase\n } = options;\n\n const [fileUrl, setFileUrl] = useState<string | null>(null);\n const [fileReference, setFileReference] = useState<FileReference | null>(null);\n const [fileReferences, setFileReferences] = useState<FileReference[]>([]);\n const [fileUrls, setFileUrls] = useState<Map<string, string>>(new Map());\n const [fileCount, setFileCount] = useState<number>(0);\n const [isLoading, setIsLoading] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n\n const fetchFiles = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id || !supabase) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n return;\n }\n\n // Validate UUID format for organisationId to prevent database errors\n const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;\n if (!uuidRegex.test(organisation_id)) {\n logger.warn('usePublicFileDisplay', 'Invalid organisationId format (not a valid UUID)', { organisation_id });\n }\n\n // Check cache first\n const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n if (enableCache) {\n const cached = publicFileCache.get(cacheKey);\n if (cached && Date.now() - cached.timestamp < cached.ttl) {\n const cachedData = cached.data;\n setFileUrl(cachedData.fileUrl || null);\n setFileReference(cachedData.fileReference || null);\n setFileReferences(cachedData.fileReferences || []);\n setFileUrls(cachedData.fileUrls || new Map());\n setFileCount(cachedData.fileCount || 0);\n setIsLoading(false);\n setError(null);\n return;\n }\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n let files: any[] = [];\n\n // CRITICAL: When category is provided, MUST use RPC function, not direct queries\n // Category is stored in file_metadata JSONB field, not a direct column\n if (category) {\n // Single file mode - use RPC to get files by category\n const rpcParams = {\n p_table_name: table_name,\n p_record_id: record_id,\n p_category: category,\n p_organisation_id: organisation_id\n };\n \n const { data, error: rpcError } = await (supabase as any)\n .rpc('data_file_reference_by_category_list', rpcParams);\n\n if (rpcError) {\n logger.error('usePublicFileDisplay', 'RPC function error', {\n function: 'data_file_reference_by_category_list',\n table_name,\n record_id,\n category,\n organisation_id,\n error: rpcError.message,\n errorCode: rpcError.code,\n errorDetails: rpcError.details,\n errorHint: rpcError.hint\n });\n throw new Error(rpcError.message || 'Failed to fetch file reference');\n }\n\n // RPC returns partial data with: id, file_path, file_metadata, is_public, created_at\n // We have table_name, record_id, organisation_id from function parameters\n // We can construct FileReference objects directly without another query (avoiding RLS issues)\n if (!data || data.length === 0) {\n files = [];\n } else {\n // Construct file reference objects from RPC response\n // This avoids RLS issues - the RPC already validated permissions and filtered public files\n files = data\n .filter((item: any) => {\n // RPC should only return public files for public context, but verify\n return item.is_public === true && item.id && item.file_path && item.file_metadata;\n })\n .map((item: any) => {\n // Construct complete file reference from RPC response + function parameters\n return {\n id: item.id,\n table_name: table_name,\n record_id: record_id,\n file_path: item.file_path,\n file_metadata: item.file_metadata || {},\n organisation_id: organisation_id,\n app_id: item.file_metadata?.app_id || null,\n is_public: true, // RPC already filtered for public files\n created_at: item.created_at || new Date().toISOString(),\n updated_at: item.created_at || new Date().toISOString()\n };\n });\n }\n } else {\n // Multiple files mode - use RPC to get all files\n const { data: fileIds, error: rpcError } = await (supabase as any)\n .rpc('data_file_reference_list', {\n p_table_name: table_name,\n p_record_id: record_id,\n p_organisation_id: organisation_id\n });\n\n if (rpcError) {\n logger.error('usePublicFileDisplay', 'RPC function error', {\n function: 'data_file_reference_list',\n table_name,\n record_id,\n organisation_id,\n error: rpcError.message,\n errorCode: rpcError.code,\n errorDetails: rpcError.details,\n errorHint: rpcError.hint\n });\n throw new Error(rpcError.message || 'Failed to fetch file references');\n }\n\n if (!fileIds || fileIds.length === 0) {\n files = [];\n } else {\n // Fetch full file reference data for each ID, but only public files\n const ids = fileIds.map((item: any) => item.id);\n const { data: fullData, error: fetchError } = await supabase\n .from('file_references')\n .select('*')\n .in('id', ids)\n .eq('is_public', true); // Only public files in public context\n\n if (fetchError) {\n throw new Error(fetchError.message || 'Failed to fetch file references');\n }\n\n files = fullData || [];\n }\n }\n\n // Ensure all files are public (category RPC might return both)\n const publicFiles = files.filter((f: any) => f.is_public === true);\n\n if (publicFiles.length === 0) {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n \n // Cache empty result\n if (enableCache) {\n publicFileCache.set(cacheKey, {\n data: { fileUrl: null, fileReference: null, fileReferences: [], fileUrls: new Map(), fileCount: 0 },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n return;\n }\n\n // Convert to FileReference format\n const fileRefs: FileReference[] = publicFiles.map((f: any) => ({\n id: f.id,\n table_name: f.table_name,\n record_id: f.record_id,\n file_path: f.file_path,\n file_metadata: f.file_metadata || {},\n organisation_id: f.organisation_id,\n app_id: f.app_id,\n is_public: f.is_public ?? true,\n created_at: f.created_at,\n updated_at: f.updated_at\n }));\n\n setFileReferences(fileRefs);\n setFileCount(fileRefs.length);\n\n if (category && fileRefs.length > 0) {\n // Single file mode - get first file\n const firstFile = fileRefs[0];\n setFileReference(firstFile);\n \n // Generate public URL\n const url = getPublicUrl(supabase, firstFile.file_path, true);\n setFileUrl(url);\n } else {\n // Multiple files mode - generate URLs for all files\n const urlMap = new Map<string, string>();\n for (const fileRef of fileRefs) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n setFileUrls(urlMap);\n setFileReference(null);\n setFileUrl(null);\n }\n\n // Cache the result\n if (enableCache) {\n publicFileCache.set(cacheKey, {\n data: {\n fileUrl: category ? (fileRefs.length > 0 ? getPublicUrl(supabase, fileRefs[0].file_path, true) : null) : null,\n fileReference: category && fileRefs.length > 0 ? fileRefs[0] : null,\n fileReferences: fileRefs,\n fileUrls: category ? new Map() : (() => {\n const urlMap = new Map<string, string>();\n for (const fileRef of fileRefs) {\n const url = getPublicUrl(supabase, fileRef.file_path, true);\n if (url) {\n urlMap.set(fileRef.id, url);\n }\n }\n return urlMap;\n })(),\n fileCount: fileRefs.length\n },\n timestamp: Date.now(),\n ttl: cacheTtl\n });\n }\n\n } catch (err) {\n logger.error('usePublicFileDisplay', 'Error fetching files', {\n table_name,\n record_id,\n organisation_id,\n category,\n error: err instanceof Error ? err.message : 'Unknown error',\n errorDetails: err instanceof Error ? err.stack : String(err)\n });\n const error = err instanceof Error ? err : new Error('Unknown error occurred');\n setError(error);\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n } finally {\n setIsLoading(false);\n }\n }, [table_name, record_id, organisation_id, category, supabase, cacheTtl, enableCache]);\n\n // Fetch files when parameters change\n useEffect(() => {\n if (table_name && record_id && organisation_id) {\n fetchFiles();\n } else {\n setFileUrl(null);\n setFileReference(null);\n setFileReferences([]);\n setFileUrls(new Map());\n setFileCount(0);\n setIsLoading(false);\n setError(null);\n }\n }, [fetchFiles, table_name, record_id, organisation_id]);\n\n const refetch = useCallback(async (): Promise<void> => {\n if (!table_name || !record_id || !organisation_id) return;\n \n // Clear cache for this file\n if (enableCache) {\n const cacheKey = `public_file_${table_name}_${record_id}_${organisation_id}_${category || 'all'}`;\n publicFileCache.delete(cacheKey);\n }\n await fetchFiles();\n }, [fetchFiles, table_name, record_id, organisation_id, category, enableCache]);\n\n return {\n fileUrl,\n fileReference,\n fileReferences,\n fileUrls,\n fileCount,\n isLoading,\n error,\n refetch\n };\n}\n\n/**\n * Clear all cached public file data\n * Useful for testing or when you need to force refresh all data\n */\nexport function clearPublicFileDisplayCache(): void {\n for (const [key] of publicFileCache) {\n if (key.startsWith('public_file_')) {\n publicFileCache.delete(key);\n }\n }\n}\n\n/**\n * Get cache statistics for debugging\n */\nexport function getPublicFileDisplayCacheStats(): { size: number; keys: string[] } {\n const keys = Array.from(publicFileCache.keys()).filter(key => key.startsWith('public_file_'));\n return {\n size: keys.length,\n keys\n };\n}\n\n"],"mappings":"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","names":["useEffect","jsx","useMemo","useContext","useContext","useMemo","log","useEffect","log","error","useEffect","useState","useEffect","useCallback","useState","useCallback","error","useEffect"]}