@jmruthers/pace-core 0.5.53 → 0.5.55

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 (398) hide show
  1. package/README.md +0 -4
  2. package/dist/{DataTable-7FMFXA7A.js → DataTable-4T627QFJ.js} +11 -11
  3. package/dist/{PublicLoadingSpinner-Bq_-BeK-.d.ts → PublicLoadingSpinner-SL8WaQN7.d.ts} +2 -21
  4. package/dist/{api-H5A3H4IR.js → api-LUNF5O6M.js} +3 -3
  5. package/dist/{appConfig-BVGyuvI7.d.ts → appConfig-DjpeG6P-.d.ts} +9 -1
  6. package/dist/{appNameResolver-7GHF5ED2.js → appNameResolver-UURKN7NF.js} +2 -2
  7. package/dist/{audit-BUW3LMJB.js → audit-6TOCAMKO.js} +2 -2
  8. package/dist/{chunk-MZBUOP4P.js → chunk-5BSLGBYI.js} +4 -3
  9. package/dist/chunk-5BSLGBYI.js.map +1 -0
  10. package/dist/{chunk-I5Z3QH5X.js → chunk-66C4BSAY.js} +2 -2
  11. package/dist/{chunk-I5Z3QH5X.js.map → chunk-66C4BSAY.js.map} +1 -1
  12. package/dist/{chunk-MYP2EGHX.js → chunk-AJ2KMES7.js} +21 -14
  13. package/dist/chunk-AJ2KMES7.js.map +1 -0
  14. package/dist/{chunk-EL2O4IUX.js → chunk-AQFRLC7K.js} +16 -24
  15. package/dist/{chunk-EL2O4IUX.js.map → chunk-AQFRLC7K.js.map} +1 -1
  16. package/dist/{chunk-7BNPOCLL.js → chunk-B2WTCLCV.js} +6 -2
  17. package/dist/chunk-B2WTCLCV.js.map +1 -0
  18. package/dist/{chunk-WJARTBCT.js → chunk-D7ARGIA3.js} +16 -7
  19. package/dist/chunk-D7ARGIA3.js.map +1 -0
  20. package/dist/{chunk-NRK4AIHQ.js → chunk-KBRACSJI.js} +3 -3
  21. package/dist/{chunk-NYUJ4FJR.js → chunk-KJDPSM64.js} +7 -7
  22. package/dist/chunk-KJDPSM64.js.map +1 -0
  23. package/dist/{chunk-GWSBHC4J.js → chunk-KLPVOPRI.js} +261 -38
  24. package/dist/chunk-KLPVOPRI.js.map +1 -0
  25. package/dist/{chunk-TRIZ7IB7.js → chunk-MPQDF75X.js} +148 -288
  26. package/dist/chunk-MPQDF75X.js.map +1 -0
  27. package/dist/{chunk-MSFACPQQ.js → chunk-PAEM3OWN.js} +11 -11
  28. package/dist/{chunk-MSFACPQQ.js.map → chunk-PAEM3OWN.js.map} +1 -1
  29. package/dist/{chunk-GIO7BFE7.js → chunk-RQD3D2CO.js} +66 -169
  30. package/dist/{chunk-GIO7BFE7.js.map → chunk-RQD3D2CO.js.map} +1 -1
  31. package/dist/{chunk-YDJW5XTN.js → chunk-STT7INZR.js} +25 -1
  32. package/dist/chunk-STT7INZR.js.map +1 -0
  33. package/dist/{chunk-6MTY77WU.js → chunk-TNMXZLDR.js} +3 -3
  34. package/dist/{chunk-BC3S53OZ.js → chunk-UQE2Y64H.js} +30 -14
  35. package/dist/chunk-UQE2Y64H.js.map +1 -0
  36. package/dist/{chunk-22KLBHPS.js → chunk-W66AZIOH.js} +2 -2
  37. package/dist/chunk-W66AZIOH.js.map +1 -0
  38. package/dist/{chunk-SS3E6QLB.js → chunk-YNUBMSMV.js} +2 -2
  39. package/dist/chunk-YNUBMSMV.js.map +1 -0
  40. package/dist/{chunk-NZ655MWE.js → chunk-ZOD2ZY6X.js} +5 -4
  41. package/dist/chunk-ZOD2ZY6X.js.map +1 -0
  42. package/dist/{chunk-74C6SNEC.js → chunk-ZPK5656W.js} +3 -3
  43. package/dist/{chunk-74C6SNEC.js.map → chunk-ZPK5656W.js.map} +1 -1
  44. package/dist/components.d.ts +22 -899
  45. package/dist/components.js +436 -3118
  46. package/dist/components.js.map +1 -1
  47. package/dist/file-reference-9xUOnwyt.d.ts +70 -0
  48. package/dist/hooks.d.ts +2 -2
  49. package/dist/hooks.js +10 -10
  50. package/dist/hooks.js.map +1 -1
  51. package/dist/index.d.ts +49 -9
  52. package/dist/index.js +190 -25
  53. package/dist/index.js.map +1 -1
  54. package/dist/{organisation-CO3Sh3_D.d.ts → organisation-t-vvQC3g.d.ts} +1 -8
  55. package/dist/providers.d.ts +2 -2
  56. package/dist/providers.js +5 -5
  57. package/dist/rbac/index.d.ts +65 -46
  58. package/dist/rbac/index.js +10 -12
  59. package/dist/styles/core.css +0 -125
  60. package/dist/types.d.ts +2 -1
  61. package/dist/types.js +3 -1
  62. package/dist/types.js.map +1 -1
  63. package/dist/{usePublicRouteParams-B2OcAsur.d.ts → usePublicRouteParams-CdoFxnJK.d.ts} +1 -1
  64. package/dist/utils.d.ts +3 -4
  65. package/dist/utils.js +44 -13
  66. package/dist/utils.js.map +1 -1
  67. package/docs/FILE_REFERENCE_SYSTEM.md +440 -0
  68. package/docs/INDEX.md +7 -5
  69. package/docs/README.md +0 -1
  70. package/docs/api/README.md +0 -4
  71. package/docs/api/classes/ErrorBoundary.md +1 -1
  72. package/docs/api/classes/InvalidScopeError.md +1 -1
  73. package/docs/api/classes/MissingUserContextError.md +1 -1
  74. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  75. package/docs/api/classes/PermissionDeniedError.md +2 -2
  76. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  77. package/docs/api/classes/RBACAuditManager.md +12 -12
  78. package/docs/api/classes/RBACCache.md +1 -1
  79. package/docs/api/classes/RBACEngine.md +6 -6
  80. package/docs/api/classes/RBACError.md +1 -1
  81. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  82. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  83. package/docs/api/classes/StorageUtils.md +281 -0
  84. package/docs/api/interfaces/AggregateConfig.md +1 -1
  85. package/docs/api/interfaces/ButtonProps.md +1 -1
  86. package/docs/api/interfaces/CardProps.md +1 -1
  87. package/docs/api/interfaces/ColorPalette.md +1 -1
  88. package/docs/api/interfaces/ColorShade.md +1 -1
  89. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  90. package/docs/api/interfaces/DataTableAction.md +1 -1
  91. package/docs/api/interfaces/DataTableColumn.md +1 -1
  92. package/docs/api/interfaces/DataTableProps.md +1 -1
  93. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  94. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  95. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  96. package/docs/api/interfaces/EventContextType.md +1 -1
  97. package/docs/api/interfaces/EventLogoProps.md +1 -1
  98. package/docs/api/interfaces/EventProviderProps.md +1 -1
  99. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  100. package/docs/api/interfaces/FileUploadProps.md +1 -1
  101. package/docs/api/interfaces/FooterProps.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/LoginFormProps.md +1 -1
  106. package/docs/api/interfaces/NavigationAccessRecord.md +2 -2
  107. package/docs/api/interfaces/NavigationContextType.md +1 -1
  108. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  109. package/docs/api/interfaces/NavigationItem.md +1 -1
  110. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  111. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  112. package/docs/api/interfaces/Organisation.md +1 -1
  113. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  114. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  115. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  116. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  117. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  118. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  119. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  120. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  121. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  122. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  123. package/docs/api/interfaces/PaletteData.md +1 -1
  124. package/docs/api/interfaces/PermissionEnforcerProps.md +4 -4
  125. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  126. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  127. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  128. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  129. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  130. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  131. package/docs/api/interfaces/RBACConfig.md +1 -1
  132. package/docs/api/interfaces/RBACContextType.md +1 -1
  133. package/docs/api/interfaces/RBACLogger.md +1 -1
  134. package/docs/api/interfaces/RBACProviderProps.md +1 -1
  135. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  136. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  137. package/docs/api/interfaces/RouteAccessRecord.md +2 -2
  138. package/docs/api/interfaces/RouteConfig.md +2 -2
  139. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  140. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  141. package/docs/api/interfaces/StorageConfig.md +1 -1
  142. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  143. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  144. package/docs/api/interfaces/StorageListOptions.md +1 -1
  145. package/docs/api/interfaces/StorageListResult.md +1 -1
  146. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  147. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  148. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  149. package/docs/api/interfaces/StyleImport.md +1 -1
  150. package/docs/api/interfaces/ToastActionElement.md +1 -1
  151. package/docs/api/interfaces/ToastProps.md +1 -1
  152. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  153. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  154. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  155. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  156. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  157. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  158. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  159. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  160. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  161. package/docs/api/interfaces/UserEventAccess.md +1 -1
  162. package/docs/api/interfaces/UserMenuProps.md +1 -1
  163. package/docs/api/interfaces/UserProfile.md +1 -1
  164. package/docs/api/modules.md +204 -200
  165. package/docs/api-reference/components.md +141 -163
  166. package/docs/api-reference/hooks.md +347 -0
  167. package/docs/core-concepts/rbac-system.md +69 -16
  168. package/docs/getting-started/examples/basic-auth-app.md +0 -1
  169. package/docs/implementation-guides/datatable-rbac-usage.md +12 -11
  170. package/docs/implementation-guides/file-upload-storage.md +733 -0
  171. package/docs/implementation-guides/inactivity-tracking.md +779 -0
  172. package/docs/implementation-guides/organisation-security.md +748 -0
  173. package/docs/implementation-guides/public-pages-advanced.md +1022 -0
  174. package/docs/migration/MIGRATION_GUIDE.md +684 -0
  175. package/docs/migration/README.md +13 -2
  176. package/docs/migration/rbac-migration.md +73 -0
  177. package/docs/rbac/examples/rbac-rls-integration-example.md +11 -13
  178. package/docs/style-guide.md +269 -1
  179. package/package.json +1 -1
  180. package/src/__tests__/TESTING_GUIDELINES.md +331 -18
  181. package/src/__tests__/helpers/supabaseMock.ts +99 -0
  182. package/src/__tests__/rbac/PagePermissionGuard.test.tsx +10 -7
  183. package/src/__tests__/shared.ts +6 -0
  184. package/src/components/DataTable/components/ActionButtons.tsx +2 -2
  185. package/src/components/DataTable/components/DataTableCore.tsx +2 -2
  186. package/src/components/DataTable/components/UnifiedTableBody.tsx +1 -1
  187. package/src/components/DataTable/utils/debugTools.ts +2 -2
  188. package/src/components/Dialog/Dialog.test.tsx +12 -2
  189. package/src/components/ErrorBoundary/ErrorBoundary.test.tsx +6 -6
  190. package/src/components/ErrorBoundary/ErrorBoundary.tsx +2 -2
  191. package/src/components/FileDisplay.tsx +233 -0
  192. package/src/components/FileUpload.tsx +176 -0
  193. package/src/components/Footer/Footer.test.tsx +7 -7
  194. package/src/components/NavigationMenu/NavigationMenu.test.tsx +13 -6
  195. package/src/components/OrganisationSelector/OrganisationSelector.test.tsx +30 -3
  196. package/src/components/OrganisationSelector/OrganisationSelector.tsx +1 -1
  197. package/src/components/PaceAppLayout/__tests__/PaceAppLayout.rbac.test.tsx +558 -0
  198. package/src/components/PublicLayout/PublicErrorBoundary.tsx +1 -1
  199. package/src/components/PublicLayout/PublicPageDebugger.tsx +2 -2
  200. package/src/components/PublicLayout/PublicPageDiagnostic.tsx +2 -2
  201. package/src/components/PublicLayout/PublicPageProvider.tsx +2 -2
  202. package/src/components/Select/Select.test.tsx +50 -15
  203. package/src/components/SuperAdminGuard.tsx +2 -2
  204. package/src/components/__tests__/SuperAdminGuard.test.tsx +559 -0
  205. package/src/components/index.ts +0 -183
  206. package/src/hooks/__tests__/useOrganisationPermissions.unit.test.tsx +2 -2
  207. package/src/hooks/__tests__/usePermissionCache.unit.test.ts +1 -1
  208. package/src/hooks/__tests__/useRBAC.unit.test.ts +191 -138
  209. package/src/hooks/public/usePublicEvent.ts +2 -2
  210. package/src/hooks/useAppConfig.ts +3 -3
  211. package/src/hooks/useComponentPerformance.ts +1 -1
  212. package/src/hooks/useDataTablePerformance.ts +1 -1
  213. package/src/hooks/useFileReference.ts +232 -0
  214. package/src/hooks/useOrganisationPermissions.test.ts +254 -344
  215. package/src/hooks/useOrganisationPermissions.ts +15 -7
  216. package/src/hooks/useOrganisationSecurity.test.ts +390 -402
  217. package/src/hooks/usePerformanceMonitor.ts +1 -1
  218. package/src/hooks/usePermissionCache.test.ts +264 -395
  219. package/src/hooks/usePermissionCache.ts +34 -4
  220. package/src/hooks/useSecureDataAccess.test.ts +486 -0
  221. package/src/hooks/useSecureDataAccess.ts +4 -1
  222. package/src/providers/InactivityProvider.tsx +2 -2
  223. package/src/providers/OrganisationProvider.test.simple.tsx +168 -0
  224. package/src/providers/OrganisationProvider.test.tsx +168 -0
  225. package/src/providers/OrganisationProvider.tsx +18 -31
  226. package/src/providers/UnifiedAuthProvider.test.simple.tsx +205 -0
  227. package/src/providers/UnifiedAuthProvider.test.tsx +128 -0
  228. package/src/providers/__tests__/InactivityProvider.test.tsx +3 -4
  229. package/src/providers/__tests__/OrganisationProvider.test.tsx +19 -14
  230. package/src/rbac/__tests__/integration.authflow.test.tsx +123 -0
  231. package/src/rbac/__tests__/integration.navigation.test.tsx +72 -0
  232. package/src/rbac/__tests__/integration.securedata.test.tsx +92 -0
  233. package/src/rbac/__tests__/integration.smoke.test.tsx +73 -0
  234. package/src/rbac/__tests__/rbac-core.test.tsx +26 -22
  235. package/src/rbac/__tests__/rbac-engine-core-logic.test.ts +411 -0
  236. package/src/rbac/__tests__/rbac-engine-simplified.test.ts +285 -0
  237. package/src/rbac/__tests__/rbac-functions.test.ts +655 -0
  238. package/src/rbac/__tests__/rbac-integration.test.ts +532 -0
  239. package/src/rbac/__tests__/scenarios.user-role.test.tsx +196 -0
  240. package/src/rbac/api.test.ts +6 -6
  241. package/src/rbac/api.ts +2 -2
  242. package/src/rbac/audit.test.ts +485 -0
  243. package/src/rbac/audit.ts +7 -1
  244. package/src/rbac/cache-invalidation.ts +318 -0
  245. package/src/rbac/cache.test.ts +286 -0
  246. package/src/rbac/components/EnhancedNavigationMenu.test.tsx +559 -0
  247. package/src/rbac/components/EnhancedNavigationMenu.tsx +29 -23
  248. package/src/rbac/components/NavigationProvider.test.tsx +449 -0
  249. package/src/rbac/components/PagePermissionGuard.tsx +4 -4
  250. package/src/rbac/components/PagePermissionProvider.test.tsx +479 -0
  251. package/src/rbac/components/SecureDataProvider.test.tsx +511 -0
  252. package/src/rbac/components/__tests__/PagePermissionGuard.race-condition.test.tsx +159 -430
  253. package/src/rbac/components/__tests__/PagePermissionGuard.test.tsx +4 -5
  254. package/src/rbac/components/__tests__/PagePermissionGuard.verification.test.tsx +112 -118
  255. package/src/rbac/config.test.ts +410 -0
  256. package/src/rbac/engine.test.simple.ts +237 -0
  257. package/src/rbac/engine.test.ts +233 -0
  258. package/src/rbac/engine.ts +37 -41
  259. package/src/rbac/examples/CompleteRBACExample.tsx +3 -3
  260. package/src/rbac/examples/EventBasedApp.tsx +4 -4
  261. package/src/rbac/hooks/useRBAC.simple.test.ts +16 -0
  262. package/src/rbac/hooks/useRBAC.test.ts +207 -455
  263. package/src/rbac/hooks/useRBAC.ts +30 -22
  264. package/src/rbac/permissions.test.ts +128 -0
  265. package/src/rbac/permissions.ts +56 -141
  266. package/src/rbac/providers/RBACProvider.tsx +1 -1
  267. package/src/rbac/secureClient.test.ts +444 -0
  268. package/src/rbac/security.test.ts +390 -0
  269. package/src/rbac/security.ts +1 -1
  270. package/src/rbac/types.test.ts +382 -0
  271. package/src/rbac/types.ts +2 -2
  272. package/src/styles/base.css +208 -0
  273. package/src/styles/core.css +0 -125
  274. package/src/styles/semantic.css +24 -0
  275. package/src/types/file-reference.ts +77 -0
  276. package/src/types/rbac-functions.ts +290 -0
  277. package/src/types/supabase.ts +10 -28
  278. package/src/types/unified.ts +4 -1
  279. package/src/utils/__tests__/bundleAnalysis.unit.test.ts +81 -55
  280. package/src/utils/__tests__/lazyLoad.unit.test.tsx +21 -12
  281. package/src/utils/__tests__/organisationContext.unit.test.ts +13 -7
  282. package/src/utils/__tests__/performanceBudgets.unit.test.ts +3 -3
  283. package/src/utils/__tests__/sessionTracking.unit.test.ts +32 -12
  284. package/src/utils/appConfig.ts +1 -1
  285. package/src/utils/appIdResolver.test.ts +503 -0
  286. package/src/utils/appIdResolver.ts +1 -1
  287. package/src/utils/appNameResolver.test.ts +494 -0
  288. package/src/utils/appNameResolver.ts +3 -2
  289. package/src/utils/bundleAnalysis.ts +3 -3
  290. package/src/utils/debugLogger.ts +1 -1
  291. package/src/utils/file-reference.ts +263 -0
  292. package/src/utils/formatDate.test.ts +2 -2
  293. package/src/utils/organisationContext.test.ts +340 -0
  294. package/src/utils/organisationContext.ts +19 -6
  295. package/src/utils/performanceBudgets.ts +2 -2
  296. package/src/utils/permissionUtils.test.ts +393 -0
  297. package/src/utils/permissionUtils.ts +5 -2
  298. package/src/utils/secureDataAccess.test.ts +715 -0
  299. package/src/utils/secureDataAccess.ts +21 -5
  300. package/src/utils/sessionTracking.ts +34 -4
  301. package/src/utils/storage/__tests__/helpers.unit.test.ts +328 -0
  302. package/src/utils/storage/__tests__/index.unit.test.ts +16 -0
  303. package/src/utils/storage/helpers.ts +20 -25
  304. package/src/utils/storage/index.ts +29 -1
  305. package/src/vite-env.d.ts +17 -0
  306. package/dist/chunk-22KLBHPS.js.map +0 -1
  307. package/dist/chunk-7BNPOCLL.js.map +0 -1
  308. package/dist/chunk-BC3S53OZ.js.map +0 -1
  309. package/dist/chunk-GWSBHC4J.js.map +0 -1
  310. package/dist/chunk-MYP2EGHX.js.map +0 -1
  311. package/dist/chunk-MZBUOP4P.js.map +0 -1
  312. package/dist/chunk-NYUJ4FJR.js.map +0 -1
  313. package/dist/chunk-NZ655MWE.js.map +0 -1
  314. package/dist/chunk-SS3E6QLB.js.map +0 -1
  315. package/dist/chunk-TRIZ7IB7.js.map +0 -1
  316. package/dist/chunk-WJARTBCT.js.map +0 -1
  317. package/dist/chunk-YDJW5XTN.js.map +0 -1
  318. package/docs/print-components/README.md +0 -258
  319. package/docs/print-components/api-reference.md +0 -636
  320. package/docs/print-components/examples/README.md +0 -204
  321. package/docs/print-components/examples/basic-report.tsx +0 -92
  322. package/docs/print-components/examples/card-catalog.tsx +0 -149
  323. package/docs/print-components/examples/cover-page-report.tsx +0 -163
  324. package/docs/print-components/quick-start.md +0 -363
  325. package/src/components/PrintButton/PrintButton.tsx +0 -321
  326. package/src/components/PrintButton/PrintButtonGroup.tsx +0 -84
  327. package/src/components/PrintButton/PrintToolbar.tsx +0 -94
  328. package/src/components/PrintButton/__tests__/PrintButton.test.tsx +0 -271
  329. package/src/components/PrintButton/examples/PrintButtonShowcase.tsx +0 -438
  330. package/src/components/PrintButton/index.ts +0 -33
  331. package/src/components/PrintButton/types.ts +0 -173
  332. package/src/components/PrintCard/PrintCard.tsx +0 -154
  333. package/src/components/PrintCard/PrintCardContent.tsx +0 -57
  334. package/src/components/PrintCard/PrintCardFooter.tsx +0 -60
  335. package/src/components/PrintCard/PrintCardGrid.tsx +0 -91
  336. package/src/components/PrintCard/PrintCardHeader.tsx +0 -78
  337. package/src/components/PrintCard/PrintCardImage.tsx +0 -81
  338. package/src/components/PrintCard/examples/PrintCardShowcase.tsx +0 -239
  339. package/src/components/PrintCard/index.ts +0 -34
  340. package/src/components/PrintCard/types.ts +0 -171
  341. package/src/components/PrintDataTable/PrintDataTable.tsx +0 -215
  342. package/src/components/PrintDataTable/PrintTableGroup.tsx +0 -90
  343. package/src/components/PrintDataTable/PrintTableRow.tsx +0 -76
  344. package/src/components/PrintDataTable/index.ts +0 -25
  345. package/src/components/PrintDataTable/types.ts +0 -67
  346. package/src/components/PrintFooter/PrintFooter.tsx +0 -183
  347. package/src/components/PrintFooter/PrintFooterContent.tsx +0 -71
  348. package/src/components/PrintFooter/PrintFooterInfo.tsx +0 -86
  349. package/src/components/PrintFooter/PrintPageNumber.tsx +0 -90
  350. package/src/components/PrintFooter/examples/PrintFooterShowcase.tsx +0 -390
  351. package/src/components/PrintFooter/index.ts +0 -30
  352. package/src/components/PrintFooter/types.ts +0 -149
  353. package/src/components/PrintGrid/PrintGrid.tsx +0 -180
  354. package/src/components/PrintGrid/PrintGridBreakpoint.tsx +0 -109
  355. package/src/components/PrintGrid/PrintGridContainer.tsx +0 -128
  356. package/src/components/PrintGrid/PrintGridItem.tsx +0 -220
  357. package/src/components/PrintGrid/examples/PrintGridShowcase.tsx +0 -359
  358. package/src/components/PrintGrid/index.ts +0 -31
  359. package/src/components/PrintGrid/types.ts +0 -159
  360. package/src/components/PrintHeader/PrintCoverHeader.tsx +0 -230
  361. package/src/components/PrintHeader/PrintHeader.tsx +0 -150
  362. package/src/components/PrintHeader/index.ts +0 -17
  363. package/src/components/PrintHeader/types.ts +0 -42
  364. package/src/components/PrintLayout/PrintLayout.tsx +0 -122
  365. package/src/components/PrintLayout/PrintLayoutContext.tsx +0 -66
  366. package/src/components/PrintLayout/PrintPageBreak.tsx +0 -52
  367. package/src/components/PrintLayout/examples/PrintShowcase.tsx +0 -230
  368. package/src/components/PrintLayout/index.ts +0 -19
  369. package/src/components/PrintLayout/types.ts +0 -37
  370. package/src/components/PrintPageBreak/PrintPageBreak.tsx +0 -120
  371. package/src/components/PrintPageBreak/PrintPageBreakGroup.tsx +0 -90
  372. package/src/components/PrintPageBreak/PrintPageBreakIndicator.tsx +0 -112
  373. package/src/components/PrintPageBreak/examples/PrintPageBreakShowcase.tsx +0 -279
  374. package/src/components/PrintPageBreak/index.ts +0 -23
  375. package/src/components/PrintPageBreak/types.ts +0 -94
  376. package/src/components/PrintSection/PrintColumn.tsx +0 -104
  377. package/src/components/PrintSection/PrintDivider.tsx +0 -101
  378. package/src/components/PrintSection/PrintSection.tsx +0 -129
  379. package/src/components/PrintSection/PrintSectionContent.tsx +0 -75
  380. package/src/components/PrintSection/PrintSectionHeader.tsx +0 -97
  381. package/src/components/PrintSection/examples/PrintSectionShowcase.tsx +0 -258
  382. package/src/components/PrintSection/index.ts +0 -33
  383. package/src/components/PrintSection/types.ts +0 -155
  384. package/src/components/PrintText/PrintText.tsx +0 -116
  385. package/src/components/PrintText/index.ts +0 -16
  386. package/src/components/PrintText/types.ts +0 -24
  387. package/src/rbac/__tests__/integration.test.tsx +0 -218
  388. package/src/utils/print/PrintDataProcessor.ts +0 -390
  389. package/src/utils/print/examples/PrintUtilitiesShowcase.tsx +0 -397
  390. package/src/utils/print/index.ts +0 -29
  391. package/src/utils/print/types.ts +0 -196
  392. package/src/utils/print/usePrintOptimization.ts +0 -272
  393. /package/dist/{DataTable-7FMFXA7A.js.map → DataTable-4T627QFJ.js.map} +0 -0
  394. /package/dist/{api-H5A3H4IR.js.map → api-LUNF5O6M.js.map} +0 -0
  395. /package/dist/{appNameResolver-7GHF5ED2.js.map → appNameResolver-UURKN7NF.js.map} +0 -0
  396. /package/dist/{audit-BUW3LMJB.js.map → audit-6TOCAMKO.js.map} +0 -0
  397. /package/dist/{chunk-NRK4AIHQ.js.map → chunk-KBRACSJI.js.map} +0 -0
  398. /package/dist/{chunk-6MTY77WU.js.map → chunk-TNMXZLDR.js.map} +0 -0
@@ -0,0 +1,1022 @@
1
+ # Public Pages System (Advanced)
2
+
3
+ > **🌐 Public Event Pages** | [← Back to Documentation](../README.md) | [↑ Table of Contents](#table-of-contents)
4
+
5
+ Complete guide to implementing public pages without authentication, including event logos, caching strategies, and SEO optimization.
6
+
7
+ ## Overview
8
+
9
+ PACE Core provides a comprehensive public pages system for:
10
+ - **Public Event Pages** - Event information without authentication
11
+ - **Event Logos** - Dynamic logo loading and caching
12
+ - **Public Data Access** - Secure public data retrieval
13
+ - **SEO Optimization** - Search engine friendly pages
14
+ - **Performance Caching** - Optimized data loading
15
+ - **Error Handling** - Graceful degradation for public users
16
+
17
+ ## Architecture
18
+
19
+ ```mermaid
20
+ graph TD
21
+ A[Public Page Request] --> B[PublicPageLayout]
22
+ B --> C[PublicPageHeader]
23
+ C --> D[EventLogo Component]
24
+ D --> E[usePublicEvent Hook]
25
+ E --> F[Public Data Cache]
26
+ F --> G[Supabase Query]
27
+ G --> H[RLS Policies]
28
+ H --> I[Public Data]
29
+ E --> J[usePublicEventLogo Hook]
30
+ J --> K[Logo Cache]
31
+ K --> L[Storage Bucket]
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ### 1. Basic Public Event Page
37
+
38
+ ```tsx
39
+ import {
40
+ PublicPageLayout,
41
+ PublicPageHeader,
42
+ EventLogo,
43
+ usePublicEvent
44
+ } from '@jmruthers/pace-core';
45
+
46
+ function PublicEventPage({ eventId }: { eventId: string }) {
47
+ const {
48
+ event,
49
+ loading,
50
+ error,
51
+ refetch
52
+ } = usePublicEvent(eventId);
53
+
54
+ if (loading) return <div>Loading event...</div>;
55
+ if (error) return <div>Error loading event: {error.message}</div>;
56
+ if (!event) return <div>Event not found</div>;
57
+
58
+ return (
59
+ <PublicPageLayout>
60
+ <PublicPageHeader
61
+ title={event.name}
62
+ description={event.description}
63
+ logo={<EventLogo eventId={eventId} size="large" />}
64
+ />
65
+
66
+ <main className="container mx-auto px-4 py-8">
67
+ <h1>{event.name}</h1>
68
+ <p>{event.description}</p>
69
+ <p>Date: {new Date(event.start_date).toLocaleDateString()}</p>
70
+ <p>Location: {event.location}</p>
71
+ </main>
72
+ </PublicPageLayout>
73
+ );
74
+ }
75
+ ```
76
+
77
+ ### 2. Public Event with Logo
78
+
79
+ ```tsx
80
+ import {
81
+ PublicPageLayout,
82
+ EventLogo,
83
+ usePublicEvent,
84
+ usePublicEventLogo
85
+ } from '@jmruthers/pace-core';
86
+
87
+ function EventWithLogo({ eventId }: { eventId: string }) {
88
+ const { event, loading: eventLoading } = usePublicEvent(eventId);
89
+ const {
90
+ logoUrl,
91
+ loading: logoLoading,
92
+ error: logoError
93
+ } = usePublicEventLogo(eventId, 'large');
94
+
95
+ return (
96
+ <PublicPageLayout>
97
+ <header className="bg-main-50 py-8">
98
+ <div className="container mx-auto px-4 text-center">
99
+ {logoLoading ? (
100
+ <div className="animate-pulse bg-sec-200 h-32 w-32 mx-auto rounded"></div>
101
+ ) : logoError ? (
102
+ <div className="text-sec-500">Logo unavailable</div>
103
+ ) : (
104
+ <EventLogo
105
+ eventId={eventId}
106
+ size="large"
107
+ className="mx-auto"
108
+ />
109
+ )}
110
+
111
+ {event && (
112
+ <div className="mt-4">
113
+ <h1 className="text-3xl font-bold text-main-900">
114
+ {event.name}
115
+ </h1>
116
+ <p className="text-main-700 mt-2">
117
+ {event.description}
118
+ </p>
119
+ </div>
120
+ )}
121
+ </div>
122
+ </header>
123
+ </PublicPageLayout>
124
+ );
125
+ }
126
+ ```
127
+
128
+ ## Components
129
+
130
+ ### PublicPageLayout
131
+
132
+ A layout component specifically designed for public pages without authentication.
133
+
134
+ #### Props
135
+
136
+ ```typescript
137
+ interface PublicPageLayoutProps {
138
+ children: React.ReactNode;
139
+ className?: string;
140
+ showHeader?: boolean;
141
+ showFooter?: boolean;
142
+ errorBoundary?: boolean;
143
+ }
144
+ ```
145
+
146
+ #### Usage Examples
147
+
148
+ **Basic Layout:**
149
+ ```tsx
150
+ <PublicPageLayout>
151
+ <h1>Public Event Page</h1>
152
+ <p>This page is accessible without authentication</p>
153
+ </PublicPageLayout>
154
+ ```
155
+
156
+ **Custom Styling:**
157
+ ```tsx
158
+ <PublicPageLayout
159
+ className="min-h-screen bg-gradient-to-br from-main-50 to-sec-50"
160
+ showHeader={true}
161
+ showFooter={true}
162
+ >
163
+ <div className="container mx-auto px-4 py-8">
164
+ <h1>Custom Public Page</h1>
165
+ </div>
166
+ </PublicPageLayout>
167
+ ```
168
+
169
+ **With Error Boundary:**
170
+ ```tsx
171
+ <PublicPageLayout errorBoundary={true}>
172
+ <PublicEventContent eventId={eventId} />
173
+ </PublicPageLayout>
174
+ ```
175
+
176
+ ### PublicPageHeader
177
+
178
+ A header component for public pages with event branding and navigation.
179
+
180
+ #### Props
181
+
182
+ ```typescript
183
+ interface PublicPageHeaderProps {
184
+ title: string;
185
+ description?: string;
186
+ logo?: React.ReactNode;
187
+ navigation?: NavigationItem[];
188
+ className?: string;
189
+ }
190
+ ```
191
+
192
+ #### Usage Examples
193
+
194
+ **Basic Header:**
195
+ ```tsx
196
+ <PublicPageHeader
197
+ title="Event Name"
198
+ description="Event description"
199
+ logo={<EventLogo eventId={eventId} />}
200
+ />
201
+ ```
202
+
203
+ **With Navigation:**
204
+ ```tsx
205
+ <PublicPageHeader
206
+ title="Event Name"
207
+ description="Event description"
208
+ logo={<EventLogo eventId={eventId} />}
209
+ navigation={[
210
+ { label: 'Home', href: '/' },
211
+ { label: 'Schedule', href: '/schedule' },
212
+ { label: 'Speakers', href: '/speakers' }
213
+ ]}
214
+ />
215
+ ```
216
+
217
+ ### EventLogo
218
+
219
+ A component for displaying event logos with automatic loading and caching.
220
+
221
+ #### Props
222
+
223
+ ```typescript
224
+ interface EventLogoProps {
225
+ eventId: string;
226
+ size?: 'small' | 'medium' | 'large' | 'xlarge';
227
+ variant?: 'default' | 'square' | 'wide';
228
+ className?: string;
229
+ fallback?: React.ReactNode;
230
+ showLoading?: boolean;
231
+ }
232
+ ```
233
+
234
+ #### Usage Examples
235
+
236
+ **Basic Logo:**
237
+ ```tsx
238
+ <EventLogo eventId={eventId} size="medium" />
239
+ ```
240
+
241
+ **Custom Styling:**
242
+ ```tsx
243
+ <EventLogo
244
+ eventId={eventId}
245
+ size="large"
246
+ variant="square"
247
+ className="rounded-lg shadow-lg"
248
+ />
249
+ ```
250
+
251
+ **With Fallback:**
252
+ ```tsx
253
+ <EventLogo
254
+ eventId={eventId}
255
+ size="medium"
256
+ fallback={
257
+ <div className="bg-sec-200 text-sec-500 p-4 rounded">
258
+ No Logo Available
259
+ </div>
260
+ }
261
+ />
262
+ ```
263
+
264
+ ## Hooks
265
+
266
+ ### usePublicEvent
267
+
268
+ Hook for fetching public event data without authentication.
269
+
270
+ #### Interface
271
+
272
+ ```typescript
273
+ interface UsePublicEventReturn {
274
+ event: PublicEvent | null;
275
+ loading: boolean;
276
+ error: Error | null;
277
+ refetch: () => Promise<void>;
278
+ clearCache: () => void;
279
+ }
280
+
281
+ interface PublicEvent {
282
+ id: string;
283
+ name: string;
284
+ description: string;
285
+ start_date: string;
286
+ end_date: string;
287
+ location: string;
288
+ status: 'draft' | 'published' | 'cancelled';
289
+ public_url?: string;
290
+ logo_url?: string;
291
+ metadata?: Record<string, any>;
292
+ }
293
+ ```
294
+
295
+ #### Usage Examples
296
+
297
+ **Basic Event Loading:**
298
+ ```tsx
299
+ import { usePublicEvent } from '@jmruthers/pace-core';
300
+
301
+ function EventPage({ eventId }: { eventId: string }) {
302
+ const { event, loading, error, refetch } = usePublicEvent(eventId);
303
+
304
+ if (loading) return <div>Loading event...</div>;
305
+ if (error) return <div>Error: {error.message}</div>;
306
+ if (!event) return <div>Event not found</div>;
307
+
308
+ return (
309
+ <div>
310
+ <h1>{event.name}</h1>
311
+ <p>{event.description}</p>
312
+ <p>Date: {new Date(event.start_date).toLocaleDateString()}</p>
313
+ <p>Location: {event.location}</p>
314
+
315
+ <Button onClick={refetch}>Refresh Event</Button>
316
+ </div>
317
+ );
318
+ }
319
+ ```
320
+
321
+ **With Error Handling:**
322
+ ```tsx
323
+ import { usePublicEvent } from '@jmruthers/pace-core';
324
+
325
+ function EventWithErrorHandling({ eventId }: { eventId: string }) {
326
+ const { event, loading, error, refetch } = usePublicEvent(eventId);
327
+
328
+ const handleRetry = async () => {
329
+ try {
330
+ await refetch();
331
+ } catch (err) {
332
+ console.error('Failed to refetch event:', err);
333
+ }
334
+ };
335
+
336
+ if (loading) {
337
+ return (
338
+ <div className="flex items-center justify-center min-h-64">
339
+ <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-main-600"></div>
340
+ </div>
341
+ );
342
+ }
343
+
344
+ if (error) {
345
+ return (
346
+ <div className="text-center py-8">
347
+ <h2 className="text-xl font-semibold text-sec-800 mb-2">
348
+ Unable to load event
349
+ </h2>
350
+ <p className="text-sec-600 mb-4">{error.message}</p>
351
+ <Button onClick={handleRetry}>Try Again</Button>
352
+ </div>
353
+ );
354
+ }
355
+
356
+ if (!event) {
357
+ return (
358
+ <div className="text-center py-8">
359
+ <h2 className="text-xl font-semibold text-sec-800">
360
+ Event not found
361
+ </h2>
362
+ <p className="text-sec-600">
363
+ The event you're looking for doesn't exist or isn't public.
364
+ </p>
365
+ </div>
366
+ );
367
+ }
368
+
369
+ return (
370
+ <div>
371
+ <h1>{event.name}</h1>
372
+ <p>{event.description}</p>
373
+ {/* Event content */}
374
+ </div>
375
+ );
376
+ }
377
+ ```
378
+
379
+ ### usePublicEventLogo
380
+
381
+ Hook for loading and caching event logos.
382
+
383
+ #### Interface
384
+
385
+ ```typescript
386
+ interface UsePublicEventLogoReturn {
387
+ logoUrl: string | null;
388
+ loading: boolean;
389
+ error: Error | null;
390
+ refetch: () => Promise<void>;
391
+ clearCache: () => void;
392
+ }
393
+
394
+ interface LogoSize {
395
+ small: 'w-8 h-8';
396
+ medium: 'w-16 h-16';
397
+ large: 'w-32 h-32';
398
+ xlarge: 'w-48 h-48';
399
+ }
400
+ ```
401
+
402
+ #### Usage Examples
403
+
404
+ **Basic Logo Loading:**
405
+ ```tsx
406
+ import { usePublicEventLogo } from '@jmruthers/pace-core';
407
+
408
+ function EventLogoDisplay({ eventId }: { eventId: string }) {
409
+ const { logoUrl, loading, error } = usePublicEventLogo(eventId, 'large');
410
+
411
+ if (loading) {
412
+ return <div className="animate-pulse bg-sec-200 w-32 h-32 rounded"></div>;
413
+ }
414
+
415
+ if (error) {
416
+ return <div className="text-sec-500">Logo unavailable</div>;
417
+ }
418
+
419
+ if (!logoUrl) {
420
+ return <div className="bg-sec-200 text-sec-500 p-4 rounded">No logo</div>;
421
+ }
422
+
423
+ return (
424
+ <img
425
+ src={logoUrl}
426
+ alt="Event logo"
427
+ className="w-32 h-32 object-contain"
428
+ />
429
+ );
430
+ }
431
+ ```
432
+
433
+ **With Caching Control:**
434
+ ```tsx
435
+ import { usePublicEventLogo } from '@jmruthers/pace-core';
436
+
437
+ function LogoWithCacheControl({ eventId }: { eventId: string }) {
438
+ const {
439
+ logoUrl,
440
+ loading,
441
+ error,
442
+ refetch,
443
+ clearCache
444
+ } = usePublicEventLogo(eventId, 'medium');
445
+
446
+ const handleRefresh = async () => {
447
+ await refetch();
448
+ };
449
+
450
+ const handleClearCache = () => {
451
+ clearCache();
452
+ };
453
+
454
+ return (
455
+ <div className="space-y-4">
456
+ {loading && <div>Loading logo...</div>}
457
+ {error && <div>Error: {error.message}</div>}
458
+ {logoUrl && (
459
+ <img
460
+ src={logoUrl}
461
+ alt="Event logo"
462
+ className="w-16 h-16 object-contain"
463
+ />
464
+ )}
465
+
466
+ <div className="space-x-2">
467
+ <Button onClick={handleRefresh} size="sm">
468
+ Refresh Logo
469
+ </Button>
470
+ <Button onClick={handleClearCache} size="sm" variant="outline">
471
+ Clear Cache
472
+ </Button>
473
+ </div>
474
+ </div>
475
+ );
476
+ }
477
+ ```
478
+
479
+ ## Caching Strategies
480
+
481
+ ### Client-Side Caching
482
+
483
+ ```tsx
484
+ import { usePublicEvent, clearPublicEventCache } from '@jmruthers/pace-core';
485
+
486
+ function CachedEventPage({ eventId }: { eventId: string }) {
487
+ const { event, loading } = usePublicEvent(eventId);
488
+
489
+ // Cache management
490
+ const handleClearCache = () => {
491
+ clearPublicEventCache();
492
+ console.log('Event cache cleared');
493
+ };
494
+
495
+ const handlePreloadEvent = async (newEventId: string) => {
496
+ // Preload another event
497
+ const { event: preloadedEvent } = usePublicEvent(newEventId);
498
+ console.log('Preloaded event:', preloadedEvent);
499
+ };
500
+
501
+ return (
502
+ <div>
503
+ <h1>{event?.name}</h1>
504
+
505
+ <div className="space-x-2">
506
+ <Button onClick={handleClearCache}>
507
+ Clear Cache
508
+ </Button>
509
+ <Button onClick={() => handlePreloadEvent('other-event-id')}>
510
+ Preload Other Event
511
+ </Button>
512
+ </div>
513
+ </div>
514
+ );
515
+ }
516
+ ```
517
+
518
+ ### Cache Statistics
519
+
520
+ ```tsx
521
+ import { getPublicEventCacheStats, getPublicLogoCacheStats } from '@jmruthers/pace-core';
522
+
523
+ function CacheStats() {
524
+ const eventStats = getPublicEventCacheStats();
525
+ const logoStats = getPublicLogoCacheStats();
526
+
527
+ return (
528
+ <div className="bg-sec-50 p-4 rounded">
529
+ <h3>Cache Statistics</h3>
530
+ <div className="grid grid-cols-2 gap-4">
531
+ <div>
532
+ <h4>Event Cache</h4>
533
+ <p>Entries: {eventStats.entryCount}</p>
534
+ <p>Hit Rate: {eventStats.hitRate}%</p>
535
+ <p>Memory Usage: {eventStats.memoryUsage} bytes</p>
536
+ </div>
537
+ <div>
538
+ <h4>Logo Cache</h4>
539
+ <p>Entries: {logoStats.entryCount}</p>
540
+ <p>Hit Rate: {logoStats.hitRate}%</p>
541
+ <p>Memory Usage: {logoStats.memoryUsage} bytes</p>
542
+ </div>
543
+ </div>
544
+ </div>
545
+ );
546
+ }
547
+ ```
548
+
549
+ ## SEO Optimization
550
+
551
+ ### Meta Tags and Structured Data
552
+
553
+ ```tsx
554
+ import { usePublicEvent } from '@jmruthers/pace-core';
555
+
556
+ function SEOEventPage({ eventId }: { eventId: string }) {
557
+ const { event } = usePublicEvent(eventId);
558
+
559
+ // Generate structured data for search engines
560
+ const structuredData = event ? {
561
+ "@context": "https://schema.org",
562
+ "@type": "Event",
563
+ "name": event.name,
564
+ "description": event.description,
565
+ "startDate": event.start_date,
566
+ "endDate": event.end_date,
567
+ "location": {
568
+ "@type": "Place",
569
+ "name": event.location
570
+ },
571
+ "url": event.public_url,
572
+ "image": event.logo_url
573
+ } : null;
574
+
575
+ return (
576
+ <>
577
+ {/* Meta tags */}
578
+ <head>
579
+ <title>{event?.name || 'Event'}</title>
580
+ <meta name="description" content={event?.description} />
581
+ <meta property="og:title" content={event?.name} />
582
+ <meta property="og:description" content={event?.description} />
583
+ <meta property="og:image" content={event?.logo_url} />
584
+ <meta property="og:type" content="event" />
585
+ <meta name="twitter:card" content="summary_large_image" />
586
+
587
+ {/* Structured data */}
588
+ {structuredData && (
589
+ <script
590
+ type="application/ld+json"
591
+ dangerouslySetInnerHTML={{
592
+ __html: JSON.stringify(structuredData)
593
+ }}
594
+ />
595
+ )}
596
+ </head>
597
+
598
+ <main>
599
+ <h1>{event?.name}</h1>
600
+ <p>{event?.description}</p>
601
+ {/* Event content */}
602
+ </main>
603
+ </>
604
+ );
605
+ }
606
+ ```
607
+
608
+ ### Dynamic Routes and SSG
609
+
610
+ ```tsx
611
+ // pages/events/[eventId].tsx
612
+ import { usePublicEvent } from '@jmruthers/pace-core';
613
+
614
+ export async function getStaticPaths() {
615
+ // Generate static paths for all public events
616
+ const { data: events } = await supabase
617
+ .from('events')
618
+ .select('id')
619
+ .eq('status', 'published')
620
+ .eq('is_public', true);
621
+
622
+ const paths = events?.map(event => ({
623
+ params: { eventId: event.id }
624
+ })) || [];
625
+
626
+ return {
627
+ paths,
628
+ fallback: 'blocking'
629
+ };
630
+ }
631
+
632
+ export async function getStaticProps({ params }: { params: { eventId: string } }) {
633
+ // Pre-fetch event data at build time
634
+ const { data: event } = await supabase
635
+ .from('events')
636
+ .select('*')
637
+ .eq('id', params.eventId)
638
+ .eq('status', 'published')
639
+ .eq('is_public', true)
640
+ .single();
641
+
642
+ return {
643
+ props: {
644
+ event,
645
+ eventId: params.eventId
646
+ },
647
+ revalidate: 3600 // Revalidate every hour
648
+ };
649
+ }
650
+
651
+ function EventPage({ event, eventId }: { event: any; eventId: string }) {
652
+ const { event: liveEvent, loading } = usePublicEvent(eventId);
653
+
654
+ // Use pre-fetched data initially, then live data
655
+ const displayEvent = liveEvent || event;
656
+
657
+ return (
658
+ <div>
659
+ <h1>{displayEvent?.name}</h1>
660
+ <p>{displayEvent?.description}</p>
661
+ {loading && <div>Updating...</div>}
662
+ </div>
663
+ );
664
+ }
665
+ ```
666
+
667
+ ## Error Handling
668
+
669
+ ### Graceful Degradation
670
+
671
+ ```tsx
672
+ import { usePublicEvent, usePublicEventLogo } from '@jmruthers/pace-core';
673
+
674
+ function RobustEventPage({ eventId }: { eventId: string }) {
675
+ const {
676
+ event,
677
+ loading: eventLoading,
678
+ error: eventError
679
+ } = usePublicEvent(eventId);
680
+
681
+ const {
682
+ logoUrl,
683
+ loading: logoLoading,
684
+ error: logoError
685
+ } = usePublicEventLogo(eventId, 'large');
686
+
687
+ // Graceful degradation for missing data
688
+ const displayName = event?.name || 'Event';
689
+ const displayDescription = event?.description || 'Event details coming soon';
690
+ const displayLogo = logoUrl || '/default-event-logo.png';
691
+
692
+ if (eventLoading) {
693
+ return (
694
+ <div className="min-h-screen flex items-center justify-center">
695
+ <div className="text-center">
696
+ <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-main-600 mx-auto"></div>
697
+ <p className="mt-4 text-sec-600">Loading event...</p>
698
+ </div>
699
+ </div>
700
+ );
701
+ }
702
+
703
+ if (eventError) {
704
+ return (
705
+ <div className="min-h-screen flex items-center justify-center">
706
+ <div className="text-center">
707
+ <h1 className="text-2xl font-bold text-sec-800 mb-2">
708
+ Unable to load event
709
+ </h1>
710
+ <p className="text-sec-600 mb-4">
711
+ {eventError.message}
712
+ </p>
713
+ <Button onClick={() => window.location.reload()}>
714
+ Try Again
715
+ </Button>
716
+ </div>
717
+ </div>
718
+ );
719
+ }
720
+
721
+ return (
722
+ <PublicPageLayout>
723
+ <header className="bg-main-50 py-8">
724
+ <div className="container mx-auto px-4 text-center">
725
+ {logoLoading ? (
726
+ <div className="animate-pulse bg-sec-200 h-32 w-32 mx-auto rounded"></div>
727
+ ) : logoError ? (
728
+ <div className="bg-sec-200 text-sec-500 h-32 w-32 mx-auto rounded flex items-center justify-center">
729
+ No Logo
730
+ </div>
731
+ ) : (
732
+ <img
733
+ src={displayLogo}
734
+ alt={`${displayName} logo`}
735
+ className="h-32 w-32 mx-auto object-contain"
736
+ />
737
+ )}
738
+
739
+ <h1 className="text-3xl font-bold text-main-900 mt-4">
740
+ {displayName}
741
+ </h1>
742
+ <p className="text-main-700 mt-2">
743
+ {displayDescription}
744
+ </p>
745
+ </div>
746
+ </header>
747
+
748
+ <main className="container mx-auto px-4 py-8">
749
+ {/* Event content */}
750
+ </main>
751
+ </PublicPageLayout>
752
+ );
753
+ }
754
+ ```
755
+
756
+ ## Performance Optimization
757
+
758
+ ### Lazy Loading
759
+
760
+ ```tsx
761
+ import { lazy, Suspense } from 'react';
762
+ import { usePublicEvent } from '@jmruthers/pace-core';
763
+
764
+ // Lazy load heavy components
765
+ const EventSchedule = lazy(() => import('./EventSchedule'));
766
+ const EventSpeakers = lazy(() => import('./EventSpeakers'));
767
+
768
+ function OptimizedEventPage({ eventId }: { eventId: string }) {
769
+ const { event, loading } = usePublicEvent(eventId);
770
+
771
+ if (loading) return <div>Loading...</div>;
772
+ if (!event) return <div>Event not found</div>;
773
+
774
+ return (
775
+ <PublicPageLayout>
776
+ <header>
777
+ <h1>{event.name}</h1>
778
+ <p>{event.description}</p>
779
+ </header>
780
+
781
+ <main>
782
+ {/* Critical content loads immediately */}
783
+ <section>
784
+ <h2>Event Details</h2>
785
+ <p>Date: {new Date(event.start_date).toLocaleDateString()}</p>
786
+ <p>Location: {event.location}</p>
787
+ </section>
788
+
789
+ {/* Non-critical content loads lazily */}
790
+ <Suspense fallback={<div>Loading schedule...</div>}>
791
+ <EventSchedule eventId={eventId} />
792
+ </Suspense>
793
+
794
+ <Suspense fallback={<div>Loading speakers...</div>}>
795
+ <EventSpeakers eventId={eventId} />
796
+ </Suspense>
797
+ </main>
798
+ </PublicPageLayout>
799
+ );
800
+ }
801
+ ```
802
+
803
+ ### Image Optimization
804
+
805
+ ```tsx
806
+ import { EventLogo } from '@jmruthers/pace-core';
807
+
808
+ function OptimizedEventLogo({ eventId }: { eventId: string }) {
809
+ return (
810
+ <EventLogo
811
+ eventId={eventId}
812
+ size="large"
813
+ className="w-full h-auto max-w-md mx-auto"
814
+ // The component handles:
815
+ // - WebP format when supported
816
+ // - Responsive sizing
817
+ // - Lazy loading
818
+ // - Error fallbacks
819
+ />
820
+ );
821
+ }
822
+ ```
823
+
824
+ ## Complete Examples
825
+
826
+ ### Full Event Landing Page
827
+
828
+ ```tsx
829
+ import {
830
+ PublicPageLayout,
831
+ PublicPageHeader,
832
+ EventLogo,
833
+ usePublicEvent,
834
+ usePublicEventLogo
835
+ } from '@jmruthers/pace-core';
836
+
837
+ function EventLandingPage({ eventId }: { eventId: string }) {
838
+ const { event, loading: eventLoading, error: eventError } = usePublicEvent(eventId);
839
+ const { logoUrl, loading: logoLoading } = usePublicEventLogo(eventId, 'xlarge');
840
+
841
+ if (eventLoading) {
842
+ return (
843
+ <PublicPageLayout>
844
+ <div className="min-h-screen flex items-center justify-center">
845
+ <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-main-600"></div>
846
+ </div>
847
+ </PublicPageLayout>
848
+ );
849
+ }
850
+
851
+ if (eventError || !event) {
852
+ return (
853
+ <PublicPageLayout>
854
+ <div className="min-h-screen flex items-center justify-center">
855
+ <div className="text-center">
856
+ <h1 className="text-2xl font-bold text-sec-800 mb-2">
857
+ Event Not Found
858
+ </h1>
859
+ <p className="text-sec-600">
860
+ The event you're looking for doesn't exist or isn't public.
861
+ </p>
862
+ </div>
863
+ </div>
864
+ </PublicPageLayout>
865
+ );
866
+ }
867
+
868
+ return (
869
+ <PublicPageLayout>
870
+ <PublicPageHeader
871
+ title={event.name}
872
+ description={event.description}
873
+ logo={
874
+ logoLoading ? (
875
+ <div className="animate-pulse bg-sec-200 h-48 w-48 rounded"></div>
876
+ ) : (
877
+ <EventLogo eventId={eventId} size="xlarge" />
878
+ )
879
+ }
880
+ navigation={[
881
+ { label: 'Home', href: '/' },
882
+ { label: 'Schedule', href: '#schedule' },
883
+ { label: 'Speakers', href: '#speakers' },
884
+ { label: 'Register', href: '#register' }
885
+ ]}
886
+ />
887
+
888
+ <main className="container mx-auto px-4 py-8">
889
+ {/* Hero Section */}
890
+ <section className="text-center py-16">
891
+ <h1 className="text-4xl font-bold text-main-900 mb-4">
892
+ {event.name}
893
+ </h1>
894
+ <p className="text-xl text-main-700 mb-8 max-w-3xl mx-auto">
895
+ {event.description}
896
+ </p>
897
+ <div className="flex flex-col sm:flex-row gap-4 justify-center">
898
+ <Button size="lg" className="bg-main-600 text-main-50">
899
+ Register Now
900
+ </Button>
901
+ <Button size="lg" variant="outline">
902
+ View Schedule
903
+ </Button>
904
+ </div>
905
+ </section>
906
+
907
+ {/* Event Details */}
908
+ <section className="py-16">
909
+ <div className="grid md:grid-cols-2 gap-8">
910
+ <div>
911
+ <h2 className="text-2xl font-bold text-main-900 mb-4">
912
+ Event Information
913
+ </h2>
914
+ <div className="space-y-4">
915
+ <div>
916
+ <h3 className="font-semibold text-main-800">Date</h3>
917
+ <p className="text-main-600">
918
+ {new Date(event.start_date).toLocaleDateString('en-US', {
919
+ weekday: 'long',
920
+ year: 'numeric',
921
+ month: 'long',
922
+ day: 'numeric'
923
+ })}
924
+ </p>
925
+ </div>
926
+ <div>
927
+ <h3 className="font-semibold text-main-800">Location</h3>
928
+ <p className="text-main-600">{event.location}</p>
929
+ </div>
930
+ <div>
931
+ <h3 className="font-semibold text-main-800">Status</h3>
932
+ <span className={`px-3 py-1 rounded-full text-sm ${
933
+ event.status === 'published'
934
+ ? 'bg-main-100 text-main-800'
935
+ : 'bg-sec-100 text-sec-800'
936
+ }`}>
937
+ {event.status}
938
+ </span>
939
+ </div>
940
+ </div>
941
+ </div>
942
+
943
+ <div>
944
+ <h2 className="text-2xl font-bold text-main-900 mb-4">
945
+ Event Logo
946
+ </h2>
947
+ {logoLoading ? (
948
+ <div className="animate-pulse bg-sec-200 h-48 w-48 rounded"></div>
949
+ ) : (
950
+ <EventLogo
951
+ eventId={eventId}
952
+ size="large"
953
+ className="rounded-lg shadow-lg"
954
+ />
955
+ )}
956
+ </div>
957
+ </div>
958
+ </section>
959
+
960
+ {/* Additional sections */}
961
+ <section id="schedule" className="py-16">
962
+ <h2 className="text-2xl font-bold text-main-900 mb-8">Schedule</h2>
963
+ {/* Schedule content */}
964
+ </section>
965
+
966
+ <section id="speakers" className="py-16">
967
+ <h2 className="text-2xl font-bold text-main-900 mb-8">Speakers</h2>
968
+ {/* Speakers content */}
969
+ </section>
970
+ </main>
971
+ </PublicPageLayout>
972
+ );
973
+ }
974
+ ```
975
+
976
+ ## Troubleshooting
977
+
978
+ ### Common Issues
979
+
980
+ **Issue: Event not loading**
981
+ - Check that event exists and is published
982
+ - Verify `is_public` flag is set to true
983
+ - Check RLS policies allow public access
984
+
985
+ **Issue: Logo not displaying**
986
+ - Verify logo file exists in storage bucket
987
+ - Check storage bucket policies
988
+ - Ensure logo URL is correct
989
+
990
+ **Issue: Cache not working**
991
+ - Check browser storage permissions
992
+ - Verify cache configuration
993
+ - Clear browser cache and test
994
+
995
+ ### Debug Mode
996
+
997
+ ```tsx
998
+ import { usePublicEvent, usePublicEventLogo } from '@jmruthers/pace-core';
999
+
1000
+ function DebugPublicPage({ eventId }: { eventId: string }) {
1001
+ const { event, loading, error } = usePublicEvent(eventId);
1002
+ const { logoUrl, loading: logoLoading, error: logoError } = usePublicEventLogo(eventId, 'large');
1003
+
1004
+ console.log('Event data:', { event, loading, error });
1005
+ console.log('Logo data:', { logoUrl, logoLoading, logoError });
1006
+
1007
+ return (
1008
+ <div>
1009
+ <h1>Debug Information</h1>
1010
+ <pre>{JSON.stringify({ event, loading, error }, null, 2)}</pre>
1011
+ <pre>{JSON.stringify({ logoUrl, logoLoading, logoError }, null, 2)}</pre>
1012
+ </div>
1013
+ );
1014
+ }
1015
+ ```
1016
+
1017
+ ## Next Steps
1018
+
1019
+ - **[Public Data Access](../api-reference/hooks.md#public-hooks)** - Public data hooks
1020
+ - **[SEO Best Practices](../best-practices/seo.md)** - Search engine optimization
1021
+ - **[Performance Optimization](./performance.md)** - Large page optimization
1022
+ - **[Caching Strategies](../best-practices/caching.md)** - Advanced caching patterns