@jmruthers/pace-core 0.5.85 → 0.5.87
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.
- package/dist/{DataTable-4W5UPIJT.js → DataTable-FA6EUX5M.js} +3 -3
- package/dist/{PublicLoadingSpinner-CnUaz0vG.d.ts → PublicLoadingSpinner-DecuJBX0.d.ts} +1 -1
- package/dist/{chunk-V5SWX6KL.js → chunk-2FQEQUJT.js} +2 -2
- package/dist/{chunk-YEQDKCPW.js → chunk-I7O3RSMN.js} +155 -37
- package/dist/{chunk-YEQDKCPW.js.map → chunk-I7O3RSMN.js.map} +1 -1
- package/dist/{chunk-X7R6NVYQ.js → chunk-KAY3K5TP.js} +27 -7
- package/dist/{chunk-X7R6NVYQ.js.map → chunk-KAY3K5TP.js.map} +1 -1
- package/dist/{chunk-YCKPEMJA.js → chunk-QPCAGLUS.js} +2 -3
- package/dist/chunk-QPCAGLUS.js.map +1 -0
- package/dist/{chunk-KUYWZVR2.js → chunk-ZFLOV3OM.js} +10 -2
- package/dist/chunk-ZFLOV3OM.js.map +1 -0
- package/dist/components.d.ts +1 -1
- package/dist/components.js +4 -4
- package/dist/hooks.js +3 -3
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -5
- package/dist/utils.js +1 -1
- package/docs/api/classes/ColumnFactory.md +1 -1
- package/docs/api/classes/ErrorBoundary.md +1 -1
- package/docs/api/classes/InvalidScopeError.md +1 -1
- package/docs/api/classes/MissingUserContextError.md +1 -1
- package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
- package/docs/api/classes/PermissionDeniedError.md +1 -1
- package/docs/api/classes/PublicErrorBoundary.md +1 -1
- package/docs/api/classes/RBACAuditManager.md +1 -1
- package/docs/api/classes/RBACCache.md +1 -1
- package/docs/api/classes/RBACEngine.md +1 -1
- package/docs/api/classes/RBACError.md +1 -1
- package/docs/api/classes/RBACNotInitializedError.md +1 -1
- package/docs/api/classes/SecureSupabaseClient.md +1 -1
- package/docs/api/classes/StorageUtils.md +1 -1
- package/docs/api/enums/FileCategory.md +1 -1
- package/docs/api/interfaces/AggregateConfig.md +1 -1
- package/docs/api/interfaces/ButtonProps.md +1 -1
- package/docs/api/interfaces/CardProps.md +1 -1
- package/docs/api/interfaces/ColorPalette.md +1 -1
- package/docs/api/interfaces/ColorShade.md +1 -1
- package/docs/api/interfaces/DataAccessRecord.md +1 -1
- package/docs/api/interfaces/DataRecord.md +1 -1
- package/docs/api/interfaces/DataTableAction.md +1 -1
- package/docs/api/interfaces/DataTableColumn.md +1 -1
- package/docs/api/interfaces/DataTableProps.md +1 -1
- package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
- package/docs/api/interfaces/EmptyStateConfig.md +1 -1
- package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
- package/docs/api/interfaces/EventLogoProps.md +11 -11
- package/docs/api/interfaces/FileDisplayProps.md +1 -1
- package/docs/api/interfaces/FileMetadata.md +1 -1
- package/docs/api/interfaces/FileReference.md +1 -1
- package/docs/api/interfaces/FileSizeLimits.md +1 -1
- package/docs/api/interfaces/FileUploadOptions.md +1 -1
- package/docs/api/interfaces/FileUploadProps.md +1 -1
- package/docs/api/interfaces/FooterProps.md +1 -1
- package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
- package/docs/api/interfaces/InputProps.md +1 -1
- package/docs/api/interfaces/LabelProps.md +1 -1
- package/docs/api/interfaces/LoginFormProps.md +1 -1
- package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
- package/docs/api/interfaces/NavigationContextType.md +1 -1
- package/docs/api/interfaces/NavigationGuardProps.md +1 -1
- package/docs/api/interfaces/NavigationItem.md +1 -1
- package/docs/api/interfaces/NavigationMenuProps.md +1 -1
- package/docs/api/interfaces/NavigationProviderProps.md +1 -1
- package/docs/api/interfaces/Organisation.md +1 -1
- package/docs/api/interfaces/OrganisationContextType.md +1 -1
- package/docs/api/interfaces/OrganisationMembership.md +1 -1
- package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
- package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
- package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
- package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
- package/docs/api/interfaces/PageAccessRecord.md +1 -1
- package/docs/api/interfaces/PagePermissionContextType.md +1 -1
- package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
- package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
- package/docs/api/interfaces/PaletteData.md +1 -1
- package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
- package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
- package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
- package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
- package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
- package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
- package/docs/api/interfaces/RBACConfig.md +1 -1
- package/docs/api/interfaces/RBACLogger.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
- package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
- package/docs/api/interfaces/RouteAccessRecord.md +1 -1
- package/docs/api/interfaces/RouteConfig.md +1 -1
- package/docs/api/interfaces/SecureDataContextType.md +1 -1
- package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
- package/docs/api/interfaces/StorageConfig.md +1 -1
- package/docs/api/interfaces/StorageFileInfo.md +1 -1
- package/docs/api/interfaces/StorageFileMetadata.md +1 -1
- package/docs/api/interfaces/StorageListOptions.md +1 -1
- package/docs/api/interfaces/StorageListResult.md +1 -1
- package/docs/api/interfaces/StorageUploadOptions.md +1 -1
- package/docs/api/interfaces/StorageUploadResult.md +1 -1
- package/docs/api/interfaces/StorageUrlOptions.md +1 -1
- package/docs/api/interfaces/StyleImport.md +1 -1
- package/docs/api/interfaces/SwitchProps.md +1 -1
- package/docs/api/interfaces/ToastActionElement.md +1 -1
- package/docs/api/interfaces/ToastProps.md +1 -1
- package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
- package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
- package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
- package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
- package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
- package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
- package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
- package/docs/api/interfaces/UserEventAccess.md +1 -1
- package/docs/api/interfaces/UserMenuProps.md +1 -1
- package/docs/api/interfaces/UserProfile.md +1 -1
- package/docs/api/modules.md +9 -9
- package/package.json +1 -1
- package/src/components/DataTable/components/UnifiedTableBody.tsx +34 -6
- package/src/components/DataTable/utils/performanceUtils.ts +12 -3
- package/src/components/PublicLayout/EventLogo.tsx +215 -36
- package/src/components/PublicLayout/PublicPageProvider.tsx +1 -1
- package/src/hooks/public/usePublicEventLogo.ts +15 -1
- package/src/hooks/useDataTablePerformance.ts +4 -0
- package/dist/chunk-KUYWZVR2.js.map +0 -1
- package/dist/chunk-YCKPEMJA.js.map +0 -1
- /package/dist/{DataTable-4W5UPIJT.js.map → DataTable-FA6EUX5M.js.map} +0 -0
- /package/dist/{chunk-V5SWX6KL.js.map → chunk-2FQEQUJT.js.map} +0 -0
package/docs/api/modules.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
[@jmruthers/pace-core - v0.5.
|
|
1
|
+
[@jmruthers/pace-core - v0.5.87](README.md) / Exports
|
|
2
2
|
|
|
3
|
-
# @jmruthers/pace-core - v0.5.
|
|
3
|
+
# @jmruthers/pace-core - v0.5.87
|
|
4
4
|
|
|
5
5
|
**`File`**
|
|
6
6
|
|
|
@@ -2539,7 +2539,7 @@ ___
|
|
|
2539
2539
|
|
|
2540
2540
|
### EventLogo
|
|
2541
2541
|
|
|
2542
|
-
▸ **EventLogo**(`props`):
|
|
2542
|
+
▸ **EventLogo**(`props`): `Element`
|
|
2543
2543
|
|
|
2544
2544
|
Component for displaying event logos with fallback to initials
|
|
2545
2545
|
|
|
@@ -2555,13 +2555,13 @@ with automatic fallback to event initials if no logo is available.
|
|
|
2555
2555
|
|
|
2556
2556
|
#### Returns
|
|
2557
2557
|
|
|
2558
|
-
|
|
2558
|
+
`Element`
|
|
2559
2559
|
|
|
2560
2560
|
React element with event logo or fallback
|
|
2561
2561
|
|
|
2562
2562
|
#### Defined in
|
|
2563
2563
|
|
|
2564
|
-
[packages/core/src/components/PublicLayout/EventLogo.tsx:
|
|
2564
|
+
[packages/core/src/components/PublicLayout/EventLogo.tsx:391](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L391)
|
|
2565
2565
|
|
|
2566
2566
|
___
|
|
2567
2567
|
|
|
@@ -2583,7 +2583,7 @@ Compact event logo for small spaces
|
|
|
2583
2583
|
|
|
2584
2584
|
#### Defined in
|
|
2585
2585
|
|
|
2586
|
-
[packages/core/src/components/PublicLayout/EventLogo.tsx:
|
|
2586
|
+
[packages/core/src/components/PublicLayout/EventLogo.tsx:445](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L445)
|
|
2587
2587
|
|
|
2588
2588
|
___
|
|
2589
2589
|
|
|
@@ -2605,7 +2605,7 @@ Large event logo for prominent display
|
|
|
2605
2605
|
|
|
2606
2606
|
#### Defined in
|
|
2607
2607
|
|
|
2608
|
-
[packages/core/src/components/PublicLayout/EventLogo.tsx:
|
|
2608
|
+
[packages/core/src/components/PublicLayout/EventLogo.tsx:458](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L458)
|
|
2609
2609
|
|
|
2610
2610
|
___
|
|
2611
2611
|
|
|
@@ -3838,7 +3838,7 @@ Useful for testing or when you need to force refresh all data
|
|
|
3838
3838
|
|
|
3839
3839
|
#### Defined in
|
|
3840
3840
|
|
|
3841
|
-
[packages/core/src/hooks/public/usePublicEventLogo.ts:
|
|
3841
|
+
[packages/core/src/hooks/public/usePublicEventLogo.ts:282](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/hooks/public/usePublicEventLogo.ts#L282)
|
|
3842
3842
|
|
|
3843
3843
|
___
|
|
3844
3844
|
|
|
@@ -3859,7 +3859,7 @@ Get cache statistics for debugging
|
|
|
3859
3859
|
|
|
3860
3860
|
#### Defined in
|
|
3861
3861
|
|
|
3862
|
-
[packages/core/src/hooks/public/usePublicEventLogo.ts:
|
|
3862
|
+
[packages/core/src/hooks/public/usePublicEventLogo.ts:293](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/hooks/public/usePublicEventLogo.ts#L293)
|
|
3863
3863
|
|
|
3864
3864
|
___
|
|
3865
3865
|
|
package/package.json
CHANGED
|
@@ -645,16 +645,44 @@ export function UnifiedTableBody<TData extends Record<string, any>>({
|
|
|
645
645
|
}
|
|
646
646
|
}, [rows.length, dataLength, table]);
|
|
647
647
|
|
|
648
|
-
// Virtual scrolling
|
|
648
|
+
// CRITICAL FIX: Virtual scrolling requires a scroll container (parentRef).
|
|
649
|
+
// If virtualization is enabled but no scroll container exists, fall back to standard rendering.
|
|
650
|
+
// This fixes the bug where rows exist but nothing renders when virtualization is enabled.
|
|
651
|
+
const hasScrollContainer = !!parentRef.current;
|
|
652
|
+
const effectiveShouldVirtualize = shouldVirtualize && hasScrollContainer;
|
|
653
|
+
|
|
654
|
+
// Virtual scrolling setup - only create virtualizer when we have a scroll container
|
|
649
655
|
const virtualizer = useVirtualizer({
|
|
650
|
-
count:
|
|
651
|
-
getScrollElement: () => parentRef.current,
|
|
656
|
+
count: effectiveShouldVirtualize ? rows.length : 0,
|
|
657
|
+
getScrollElement: () => parentRef.current || null,
|
|
652
658
|
estimateSize: () => 40,
|
|
653
659
|
overscan: 5,
|
|
654
660
|
});
|
|
655
661
|
|
|
656
|
-
const virtualRows =
|
|
657
|
-
const totalSize =
|
|
662
|
+
const virtualRows = effectiveShouldVirtualize ? virtualizer.getVirtualItems() : [];
|
|
663
|
+
const totalSize = effectiveShouldVirtualize ? virtualizer.getTotalSize() : 0;
|
|
664
|
+
|
|
665
|
+
// CRITICAL DEBUG: Log virtualization state
|
|
666
|
+
console.log('[DataTable] 🔍 Virtualization Debug:', {
|
|
667
|
+
shouldVirtualize,
|
|
668
|
+
effectiveShouldVirtualize,
|
|
669
|
+
rowsLength: rows.length,
|
|
670
|
+
virtualRowsCount: virtualRows.length,
|
|
671
|
+
totalSize,
|
|
672
|
+
parentRefExists: hasScrollContainer,
|
|
673
|
+
parentRefHeight: parentRef.current?.clientHeight || 0,
|
|
674
|
+
parentRefScrollHeight: parentRef.current?.scrollHeight || 0,
|
|
675
|
+
willRenderVirtualized: effectiveShouldVirtualize && virtualRows.length > 0,
|
|
676
|
+
willRenderStandard: !effectiveShouldVirtualize && rows.length > 0,
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
// Warning if virtualization is expected but no container exists
|
|
680
|
+
if (shouldVirtualize && !hasScrollContainer) {
|
|
681
|
+
console.warn('[DataTable] ⚠️ Virtualization enabled but no scroll container found. Falling back to standard rendering.', {
|
|
682
|
+
rowsLength: rows.length,
|
|
683
|
+
dataLength,
|
|
684
|
+
});
|
|
685
|
+
}
|
|
658
686
|
|
|
659
687
|
|
|
660
688
|
// Render table content
|
|
@@ -680,7 +708,7 @@ export function UnifiedTableBody<TData extends Record<string, any>>({
|
|
|
680
708
|
);
|
|
681
709
|
}
|
|
682
710
|
|
|
683
|
-
if (
|
|
711
|
+
if (effectiveShouldVirtualize && virtualRows.length > 0) {
|
|
684
712
|
// Virtualized rendering
|
|
685
713
|
return virtualRows.map((virtualRow) => {
|
|
686
714
|
const row = rows[virtualRow.index];
|
|
@@ -18,14 +18,23 @@ import type {
|
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
20
|
* Determines the optimal pagination mode based on dataset size
|
|
21
|
+
*
|
|
22
|
+
* IMPORTANT: This function NEVER returns 'server' mode - it only returns 'client' or 'hybrid'.
|
|
23
|
+
* Server mode should ONLY be used when serverSide config is explicitly provided.
|
|
24
|
+
* Returning 'server' without serverSide config breaks pagination because TanStack Table
|
|
25
|
+
* expects pre-paginated data but receives all client-side data.
|
|
26
|
+
*
|
|
27
|
+
* For large datasets (>10k rows), returns 'hybrid' which still uses client-side pagination
|
|
28
|
+
* but with optimizations. If true server-side pagination is needed, serverSide config must be provided.
|
|
21
29
|
*/
|
|
22
30
|
export function determinePaginationMode(
|
|
23
31
|
dataLength: number,
|
|
24
32
|
serverSideThreshold: number = 10000
|
|
25
|
-
):
|
|
33
|
+
): 'client' | 'hybrid' {
|
|
26
34
|
if (dataLength <= 1000) return 'client';
|
|
27
|
-
|
|
28
|
-
|
|
35
|
+
// Even for very large datasets, use 'hybrid' mode (client-side pagination with optimizations)
|
|
36
|
+
// unless serverSide config is explicitly provided (handled in useDataTablePerformance)
|
|
37
|
+
return 'hybrid';
|
|
29
38
|
}
|
|
30
39
|
|
|
31
40
|
/**
|
|
@@ -46,9 +46,12 @@
|
|
|
46
46
|
* - Tailwind CSS - Styling
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
|
-
import React, { useMemo } from 'react';
|
|
49
|
+
import React, { useMemo, useContext } from 'react';
|
|
50
50
|
import { usePublicEventLogo } from '../../hooks/public/usePublicEventLogo';
|
|
51
|
-
import {
|
|
51
|
+
import { PublicPageContext, useIsPublicPage } from './PublicPageProvider';
|
|
52
|
+
import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
|
|
53
|
+
import type { SupabaseClient } from '@supabase/supabase-js';
|
|
54
|
+
import type { Database } from '../../types/database';
|
|
52
55
|
|
|
53
56
|
export interface EventLogoProps {
|
|
54
57
|
/** The event ID to fetch logo for */
|
|
@@ -108,52 +111,167 @@ function defaultGenerateFallbackText(eventName: string): string {
|
|
|
108
111
|
* @param props - Logo configuration and styling
|
|
109
112
|
* @returns React element with event logo or fallback
|
|
110
113
|
*/
|
|
111
|
-
|
|
114
|
+
/**
|
|
115
|
+
* Internal component for public page context
|
|
116
|
+
* Uses PublicPageContext to get Supabase client
|
|
117
|
+
*/
|
|
118
|
+
function EventLogoPublic({
|
|
112
119
|
eventId,
|
|
113
120
|
eventName,
|
|
114
121
|
organisationId,
|
|
115
|
-
size
|
|
116
|
-
className
|
|
117
|
-
showFallback
|
|
118
|
-
generateFallbackText
|
|
119
|
-
validateImage
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}: EventLogoProps) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
size,
|
|
123
|
+
className,
|
|
124
|
+
showFallback,
|
|
125
|
+
generateFallbackText,
|
|
126
|
+
validateImage,
|
|
127
|
+
LoadingComponent,
|
|
128
|
+
ErrorComponent
|
|
129
|
+
}: EventLogoProps & { LoadingComponent?: React.ComponentType; ErrorComponent?: React.ComponentType<{ error: Error }> }) {
|
|
130
|
+
const publicPageContext = useContext(PublicPageContext);
|
|
131
|
+
const supabase = publicPageContext?.supabase ?? null;
|
|
132
|
+
|
|
133
|
+
// Validate UUID format for organisationId to prevent database errors
|
|
134
|
+
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
135
|
+
const hasValidOrganisationId = organisationId && uuidRegex.test(organisationId);
|
|
136
|
+
|
|
137
|
+
// If no Supabase client is available, show fallback immediately
|
|
138
|
+
if (!supabase) {
|
|
139
|
+
const effectiveSize = size || 'md';
|
|
140
|
+
return (
|
|
141
|
+
<div className={`${sizeClasses[effectiveSize]} ${className}`.trim()} title={`${eventName} logo (Supabase not configured)`}>
|
|
142
|
+
{eventName ? defaultGenerateFallbackText(eventName) : 'EV'}
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
132
145
|
}
|
|
133
146
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
const {
|
|
148
|
+
logoUrl,
|
|
149
|
+
fallbackText,
|
|
150
|
+
isLoading,
|
|
151
|
+
error
|
|
152
|
+
} = usePublicEventLogo(
|
|
153
|
+
eventId,
|
|
154
|
+
eventName,
|
|
155
|
+
organisationId, // Always pass organisationId, let the hook handle validation
|
|
156
|
+
{
|
|
157
|
+
validateImage,
|
|
158
|
+
generateFallbackText,
|
|
159
|
+
supabase
|
|
160
|
+
}
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// Memoize the size classes - provide default 'md' if size is undefined
|
|
164
|
+
const sizeClass = useMemo(() => sizeClasses[size || 'md'], [size]);
|
|
165
|
+
|
|
166
|
+
// Memoize the combined classes
|
|
167
|
+
const combinedClasses = useMemo(() => {
|
|
168
|
+
const baseClasses = 'flex items-center justify-center bg-gray-100 text-gray-600 font-semibold rounded';
|
|
169
|
+
return `${baseClasses} ${sizeClass} ${className}`.trim();
|
|
170
|
+
}, [sizeClass, className]);
|
|
171
|
+
|
|
172
|
+
// Handle invalid organisation ID - show fallback immediately only if we have no data
|
|
173
|
+
if (!hasValidOrganisationId && !isLoading && !logoUrl && showFallback) {
|
|
174
|
+
return (
|
|
175
|
+
<div className={combinedClasses} title={`${eventName} logo (invalid organisation ID)`}>
|
|
176
|
+
{fallbackText}
|
|
177
|
+
</div>
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Handle loading state
|
|
182
|
+
if (isLoading) {
|
|
183
|
+
if (LoadingComponent) {
|
|
184
|
+
return <LoadingComponent />;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return (
|
|
188
|
+
<div className={`${combinedClasses} animate-pulse`}>
|
|
189
|
+
<div className="w-3/4 h-3/4 bg-gray-300 rounded"></div>
|
|
190
|
+
</div>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Handle error state
|
|
195
|
+
if (error) {
|
|
196
|
+
if (ErrorComponent) {
|
|
197
|
+
return <ErrorComponent error={error} />;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (showFallback) {
|
|
201
|
+
return (
|
|
202
|
+
<div className={combinedClasses} title={`${eventName} (logo unavailable)`}>
|
|
203
|
+
{fallbackText}
|
|
204
|
+
</div>
|
|
205
|
+
);
|
|
206
|
+
}
|
|
137
207
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Handle no logo available
|
|
212
|
+
if (!logoUrl) {
|
|
213
|
+
if (showFallback) {
|
|
214
|
+
return (
|
|
215
|
+
<div className={combinedClasses} title={`${eventName} logo`}>
|
|
216
|
+
{fallbackText}
|
|
217
|
+
</div>
|
|
218
|
+
);
|
|
144
219
|
}
|
|
145
|
-
|
|
220
|
+
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Render the actual logo
|
|
225
|
+
return (
|
|
226
|
+
<img
|
|
227
|
+
src={logoUrl}
|
|
228
|
+
alt={`${eventName} logo`}
|
|
229
|
+
className={`${sizeClass} ${className}`.trim()}
|
|
230
|
+
onError={(e) => {
|
|
231
|
+
// If image fails to load, hide it and show fallback
|
|
232
|
+
const target = e.target as HTMLImageElement;
|
|
233
|
+
target.style.display = 'none';
|
|
234
|
+
|
|
235
|
+
// Create fallback element
|
|
236
|
+
const fallback = document.createElement('div');
|
|
237
|
+
fallback.className = combinedClasses;
|
|
238
|
+
fallback.textContent = fallbackText;
|
|
239
|
+
fallback.title = `${eventName} logo`;
|
|
240
|
+
|
|
241
|
+
// Insert fallback after the image
|
|
242
|
+
target.parentNode?.insertBefore(fallback, target.nextSibling);
|
|
243
|
+
}}
|
|
244
|
+
/>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Internal component for authenticated page context
|
|
250
|
+
* Uses UnifiedAuthProvider to get Supabase client
|
|
251
|
+
*/
|
|
252
|
+
function EventLogoAuthenticated({
|
|
253
|
+
eventId,
|
|
254
|
+
eventName,
|
|
255
|
+
organisationId,
|
|
256
|
+
size,
|
|
257
|
+
className,
|
|
258
|
+
showFallback,
|
|
259
|
+
generateFallbackText,
|
|
260
|
+
validateImage,
|
|
261
|
+
LoadingComponent,
|
|
262
|
+
ErrorComponent
|
|
263
|
+
}: EventLogoProps & { LoadingComponent?: React.ComponentType; ErrorComponent?: React.ComponentType<{ error: Error }> }) {
|
|
264
|
+
const { supabase } = useUnifiedAuth();
|
|
265
|
+
|
|
146
266
|
// Validate UUID format for organisationId to prevent database errors
|
|
147
267
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
148
268
|
const hasValidOrganisationId = organisationId && uuidRegex.test(organisationId);
|
|
149
269
|
|
|
150
|
-
// Get Supabase client from context
|
|
151
|
-
const { supabase } = usePublicPageContext();
|
|
152
|
-
|
|
153
270
|
// If no Supabase client is available, show fallback immediately
|
|
154
271
|
if (!supabase) {
|
|
272
|
+
const effectiveSize = size || 'md';
|
|
155
273
|
return (
|
|
156
|
-
<div className={`${sizeClasses[
|
|
274
|
+
<div className={`${sizeClasses[effectiveSize]} ${className}`.trim()} title={`${eventName} logo (Supabase not configured)`}>
|
|
157
275
|
{eventName ? defaultGenerateFallbackText(eventName) : 'EV'}
|
|
158
276
|
</div>
|
|
159
277
|
);
|
|
@@ -175,8 +293,8 @@ export function EventLogo({
|
|
|
175
293
|
}
|
|
176
294
|
);
|
|
177
295
|
|
|
178
|
-
// Memoize the size classes
|
|
179
|
-
const sizeClass = useMemo(() => sizeClasses[size], [size]);
|
|
296
|
+
// Memoize the size classes - provide default 'md' if size is undefined
|
|
297
|
+
const sizeClass = useMemo(() => sizeClasses[size || 'md'], [size]);
|
|
180
298
|
|
|
181
299
|
// Memoize the combined classes
|
|
182
300
|
const combinedClasses = useMemo(() => {
|
|
@@ -260,6 +378,67 @@ export function EventLogo({
|
|
|
260
378
|
);
|
|
261
379
|
}
|
|
262
380
|
|
|
381
|
+
/**
|
|
382
|
+
* Component for displaying event logos with fallback to initials
|
|
383
|
+
*
|
|
384
|
+
* This component is context-aware and automatically detects whether it's being used
|
|
385
|
+
* in a public or authenticated context. It fetches and displays event logos from storage,
|
|
386
|
+
* with automatic fallback to event initials if no logo is available.
|
|
387
|
+
*
|
|
388
|
+
* @param props - Logo configuration and styling
|
|
389
|
+
* @returns React element with event logo or fallback
|
|
390
|
+
*/
|
|
391
|
+
export function EventLogo({
|
|
392
|
+
eventId,
|
|
393
|
+
eventName,
|
|
394
|
+
organisationId,
|
|
395
|
+
size = 'md',
|
|
396
|
+
className = '',
|
|
397
|
+
showFallback = true,
|
|
398
|
+
generateFallbackText = defaultGenerateFallbackText,
|
|
399
|
+
validateImage = true,
|
|
400
|
+
loadingComponent: LoadingComponent,
|
|
401
|
+
errorComponent: ErrorComponent
|
|
402
|
+
}: EventLogoProps) {
|
|
403
|
+
// Check which context we're in and route to the appropriate component
|
|
404
|
+
const isPublicPage = useIsPublicPage();
|
|
405
|
+
|
|
406
|
+
// If we're in a public page context, use the public component
|
|
407
|
+
if (isPublicPage) {
|
|
408
|
+
return (
|
|
409
|
+
<EventLogoPublic
|
|
410
|
+
eventId={eventId}
|
|
411
|
+
eventName={eventName}
|
|
412
|
+
organisationId={organisationId}
|
|
413
|
+
size={size}
|
|
414
|
+
className={className}
|
|
415
|
+
showFallback={showFallback}
|
|
416
|
+
generateFallbackText={generateFallbackText}
|
|
417
|
+
validateImage={validateImage}
|
|
418
|
+
LoadingComponent={LoadingComponent}
|
|
419
|
+
ErrorComponent={ErrorComponent}
|
|
420
|
+
/>
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Otherwise, try to use the authenticated component
|
|
425
|
+
// It will throw if not in UnifiedAuthProvider, which will be caught by error boundary
|
|
426
|
+
return (
|
|
427
|
+
<EventLogoAuthenticated
|
|
428
|
+
eventId={eventId}
|
|
429
|
+
eventName={eventName}
|
|
430
|
+
organisationId={organisationId}
|
|
431
|
+
size={size}
|
|
432
|
+
className={className}
|
|
433
|
+
showFallback={showFallback}
|
|
434
|
+
generateFallbackText={generateFallbackText}
|
|
435
|
+
validateImage={validateImage}
|
|
436
|
+
LoadingComponent={LoadingComponent}
|
|
437
|
+
ErrorComponent={ErrorComponent}
|
|
438
|
+
/>
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
263
442
|
/**
|
|
264
443
|
* Compact event logo for small spaces
|
|
265
444
|
*/
|
|
@@ -46,7 +46,7 @@ interface PublicPageContextType {
|
|
|
46
46
|
};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);
|
|
49
|
+
export const PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);
|
|
50
50
|
|
|
51
51
|
export interface PublicPageProviderProps {
|
|
52
52
|
children: ReactNode;
|
|
@@ -191,7 +191,21 @@ export function usePublicEventLogo(
|
|
|
191
191
|
return;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
|
|
194
|
+
// Get the file path from the RPC function (it returns file_path as logo_url)
|
|
195
|
+
const logoPath = data[0].logo_url;
|
|
196
|
+
|
|
197
|
+
// Construct the full public URL using Supabase client's storage API
|
|
198
|
+
const { data: urlData } = supabase.storage
|
|
199
|
+
.from('public-files')
|
|
200
|
+
.getPublicUrl(logoPath);
|
|
201
|
+
|
|
202
|
+
if (!urlData?.publicUrl) {
|
|
203
|
+
console.warn('[usePublicEventLogo] Failed to construct public URL for path:', logoPath);
|
|
204
|
+
setLogoUrl(null);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const logoUrl = urlData.publicUrl;
|
|
195
209
|
|
|
196
210
|
// Validate image existence if requested
|
|
197
211
|
if (validateImage) {
|
|
@@ -134,6 +134,10 @@ export function useDataTablePerformance<TData extends DataRecord>({
|
|
|
134
134
|
// ============================================================================
|
|
135
135
|
|
|
136
136
|
const paginationMode = useMemo(() => {
|
|
137
|
+
// CRITICAL FIX: Only use 'server' mode if serverSide config is explicitly provided.
|
|
138
|
+
// determinePaginationMode now never returns 'server' - it only returns 'client' or 'hybrid'.
|
|
139
|
+
// This fixes pagination issues where large datasets would auto-detect 'server' mode
|
|
140
|
+
// but have no serverSide config, causing pagination controls to break.
|
|
137
141
|
if (serverSide) return 'server';
|
|
138
142
|
return determinePaginationMode(data?.length || 0, performance.serverSideThreshold);
|
|
139
143
|
}, [data?.length, performance.serverSideThreshold, serverSide]);
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/providers/OrganisationProvider.tsx","../src/components/PublicLayout/PublicErrorBoundary.tsx","../src/components/PublicLayout/PublicPageProvider.tsx","../src/hooks/useAppConfig.ts","../src/hooks/public/usePublicEventLogo.ts"],"sourcesContent":["/**\n * @file Re-export for OrganisationProvider\n * @package @jmruthers/pace-core\n * @module Providers\n * @since 0.1.0\n * \n * Re-exports the service-based OrganisationProvider for backward compatibility.\n */\n\nexport { OrganisationServiceProvider as OrganisationProvider } from './services/OrganisationServiceProvider';\nexport type { OrganisationServiceProviderProps as OrganisationProviderProps } from './services/OrganisationServiceProvider';\n\n// Re-export context and hook\nexport { OrganisationServiceContext, useOrganisationService } from './services/OrganisationServiceProvider';\nexport type { OrganisationServiceContextType } from './services/OrganisationServiceProvider';\n\n// Re-export convenience hook for backward compatibility\nexport { useOrganisations } from '../hooks/useOrganisations';\n\n","/**\n * @file Public Error Boundary Component\n * @package @jmruthers/pace-core\n * @module Components/PublicLayout\n * @since 1.0.0\n *\n * An error boundary component specifically designed for public pages.\n * Provides graceful error handling and recovery for public content.\n *\n * Features:\n * - Graceful error handling\n * - User-friendly error messages\n * - Recovery mechanisms\n * - Accessibility compliant\n * - TypeScript support\n *\n * @example\n * ```tsx\n * import { PublicErrorBoundary } from '@jmruthers/pace-core';\n *\n * function PublicEventPage() {\n * return (\n * <PublicErrorBoundary>\n * <PublicPageContent />\n * </PublicErrorBoundary>\n * );\n * }\n * ```\n *\n * @accessibility\n * - WCAG 2.1 AA compliant\n * - Screen reader friendly error messages\n * - Keyboard accessible recovery actions\n * - High contrast support\n *\n * @dependencies\n * - React 18+ - Component framework\n * - Error boundary patterns\n * - Tailwind CSS - Styling\n */\n\nimport React, { Component, ErrorInfo, ReactNode } from 'react';\n\nexport interface PublicErrorBoundaryProps {\n /** Child components to wrap */\n children: ReactNode;\n /** Custom error fallback component */\n fallback?: React.ComponentType<PublicErrorBoundaryState>;\n /** Custom CSS classes for error display */\n className?: string;\n /** Whether to show error details in development */\n showErrorDetails?: boolean;\n /** Custom error message */\n customErrorMessage?: string;\n /** Custom recovery action */\n onRecover?: () => void;\n}\n\nexport interface PublicErrorBoundaryState {\n /** Whether an error has occurred */\n hasError: boolean;\n /** The error that occurred */\n error: Error | null;\n /** Error information */\n errorInfo: ErrorInfo | null;\n /** Function to reset the error state */\n resetError: () => void;\n}\n\n/**\n * Error boundary component for public pages\n * \n * This component catches JavaScript errors anywhere in the child component tree,\n * logs those errors, and displays a fallback UI instead of the component tree that crashed.\n * \n * @param props - Error boundary configuration\n * @returns React element with error boundary wrapper\n */\nexport class PublicErrorBoundary extends Component<PublicErrorBoundaryProps, PublicErrorBoundaryState> {\n constructor(props: PublicErrorBoundaryProps) {\n super(props);\n this.state = {\n hasError: false,\n error: null,\n errorInfo: null,\n resetError: this.resetError.bind(this)\n };\n }\n\n static getDerivedStateFromError(error: Error): Partial<PublicErrorBoundaryState> {\n // Update state so the next render will show the fallback UI\n return {\n hasError: true,\n error\n };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n // Log the error to console in development\n if (import.meta.env.MODE === 'development') {\n console.error('PublicErrorBoundary caught an error:', error, errorInfo);\n }\n\n // Update state with error info\n this.setState({\n error,\n errorInfo\n });\n\n // You can also log the error to an error reporting service here\n // Example: logErrorToService(error, errorInfo);\n }\n\n resetError = () => {\n this.setState({\n hasError: false,\n error: null,\n errorInfo: null\n });\n };\n\n render() {\n if (this.state.hasError) {\n // Custom fallback component\n if (this.props.fallback) {\n return <this.props.fallback {...this.state} />;\n }\n\n // Default error UI\n return (\n <div className={`min-h-screen bg-white flex items-center justify-center ${this.props.className || ''}`}>\n <div className=\"max-w-md mx-auto text-center px-4\">\n <div className=\"mb-6\">\n <div className=\"mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4\">\n <svg\n className=\"h-6 w-6 text-red-600\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n </svg>\n </div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-2\">\n Something went wrong\n </h1>\n <p className=\"text-gray-600 mb-6\">\n {this.props.customErrorMessage || \n 'We encountered an error while loading this page. Please try again.'}\n </p>\n </div>\n\n {/* Error details in development */}\n {this.props.showErrorDetails && this.state.error && (\n <div className=\"mb-6 p-4 bg-red-50 border border-red-200 rounded-md text-left\">\n <h3 className=\"text-sm font-medium text-red-800 mb-2\">\n Error Details (Development Only)\n </h3>\n <pre className=\"text-xs text-red-700 whitespace-pre-wrap\">\n {this.state.error.toString()}\n {this.state.errorInfo?.componentStack}\n </pre>\n </div>\n )}\n\n {/* Recovery actions */}\n <div className=\"space-y-3\">\n <button\n onClick={this.resetError}\n className=\"w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n >\n Try Again\n </button>\n \n <button\n onClick={() => window.location.reload()}\n className=\"w-full px-4 py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700 transition-colors focus:outline-none focus:ring-2 focus:ring-gray-500 focus:ring-offset-2\"\n >\n Reload Page\n </button>\n\n {this.props.onRecover && (\n <button\n onClick={this.props.onRecover}\n className=\"w-full px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2\"\n >\n Alternative Action\n </button>\n )}\n </div>\n\n {/* Help text */}\n <div className=\"mt-6 text-sm text-gray-500\">\n <p>\n If this problem persists, please contact support or try accessing the page later.\n </p>\n </div>\n </div>\n </div>\n );\n }\n\n return this.props.children;\n }\n}\n\n/**\n * Hook for accessing error boundary state\n * Useful for components that need to know if they're inside an error boundary\n */\nexport function useErrorBoundary() {\n const [error, setError] = React.useState<Error | null>(null);\n\n const resetError = React.useCallback(() => {\n setError(null);\n }, []);\n\n const captureError = React.useCallback((error: Error) => {\n setError(error);\n }, []);\n\n // Throw error synchronously during render so error boundary can catch it\n if (error) {\n throw error;\n }\n\n return { captureError, resetError };\n}\n\n/**\n * Default error fallback component\n */\nexport function DefaultPublicErrorFallback({ \n error, \n resetError \n}: PublicErrorBoundaryState) {\n return (\n <div className=\"min-h-screen bg-white flex items-center justify-center\">\n <div className=\"max-w-md mx-auto text-center px-4\">\n <div className=\"mb-6\">\n <div className=\"mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-red-100 mb-4\">\n <svg\n className=\"h-6 w-6 text-red-600\"\n fill=\"none\"\n viewBox=\"0 0 24 24\"\n stroke=\"currentColor\"\n >\n <path\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={2}\n d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L3.732 16.5c-.77.833.192 2.5 1.732 2.5z\"\n />\n </svg>\n </div>\n <h1 className=\"text-2xl font-bold text-gray-900 mb-2\">\n Page Error\n </h1>\n <p className=\"text-gray-600 mb-6\">\n We encountered an error while loading this page.\n </p>\n </div>\n\n <button\n onClick={resetError}\n className=\"w-full px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2\"\n >\n Try Again\n </button>\n </div>\n </div>\n );\n}\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 * function PublicApp() {\n * return (\n * <PublicPageProvider>\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 { PublicErrorBoundary } from './PublicErrorBoundary';\n\ninterface PublicPageContextType {\n isPublicPage: true;\n supabase: ReturnType<typeof createClient<Database>> | null;\n environment: {\n supabaseUrl: string | null;\n supabaseKey: string | null;\n };\n}\n\nconst PublicPageContext = createContext<PublicPageContextType | undefined>(undefined);\n\nexport interface PublicPageProviderProps {\n children: ReactNode;\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 */\nexport function PublicPageProvider({ children }: 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 as any).env) {\n return (import.meta as any).env[key];\n }\n // Check Node.js environment (server-side)\n if (typeof import.meta !== 'undefined' && import.meta.env) {\n return import.meta.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 const supabase = useMemo(() => {\n if (!supabaseUrl || !supabaseKey) {\n console.warn('[PublicPageProvider] Missing Supabase environment variables. Please ensure VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY are set in your environment.');\n return null;\n }\n return createClient<Database>(supabaseUrl, supabaseKey);\n }, [supabaseUrl, supabaseKey]);\n\n const contextValue: PublicPageContextType = {\n isPublicPage: true,\n supabase,\n environment: {\n supabaseUrl,\n supabaseKey\n }\n };\n\n return (\n <PublicPageContext.Provider value={contextValue}>\n <PublicErrorBoundary>\n {children}\n </PublicErrorBoundary>\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 } from 'react';\nimport { useUnifiedAuth } from '../providers/UnifiedAuthProvider';\nimport { useIsPublicPage } 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 if (isPublicPage) {\n // For public pages, try to get app name from environment variables\n const getAppName = (): string => {\n // Check Vite environment first (browser)\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 // Check Node.js environment (server-side)\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 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 }), []);\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 * @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';\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 console.warn('[usePublicEventLogo] 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 console.warn('[usePublicEventLogo] Logo URL not accessible:', logoUrl);\n setLogoUrl(null);\n return;\n }\n } catch (fetchError) {\n console.warn('[usePublicEventLogo] 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 console.error('[usePublicEventLogo] 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"],"mappings":"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","names":["error","jsx","useMemo","useMemo","useMemo","logoUrl","error"]}
|