@jmruthers/pace-core 0.5.100 → 0.5.102

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 (147) hide show
  1. package/dist/{DataTable-MHN7PNH3.js → DataTable-DXELRJIX.js} +2 -2
  2. package/dist/{PublicLoadingSpinner-B84QWsvB.d.ts → PublicLoadingSpinner-Cvgk-V0F.d.ts} +23 -56
  3. package/dist/{chunk-2ZYHCFUO.js → chunk-7ME4Z5OY.js} +148 -12
  4. package/dist/chunk-7ME4Z5OY.js.map +1 -0
  5. package/dist/{chunk-OLZSC5EH.js → chunk-EVVRUGQ2.js} +90 -46
  6. package/dist/{chunk-OLZSC5EH.js.map → chunk-EVVRUGQ2.js.map} +1 -1
  7. package/dist/{chunk-MKMKUCPF.js → chunk-SZWRW5FD.js} +20 -139
  8. package/dist/chunk-SZWRW5FD.js.map +1 -0
  9. package/dist/{chunk-Z4HVIZSK.js → chunk-UDWTCBSH.js} +128 -499
  10. package/dist/chunk-UDWTCBSH.js.map +1 -0
  11. package/dist/components.d.ts +1 -1
  12. package/dist/components.js +3 -9
  13. package/dist/components.js.map +1 -1
  14. package/dist/hooks.js +5 -5
  15. package/dist/index.d.ts +2 -1
  16. package/dist/index.js +7 -13
  17. package/dist/index.js.map +1 -1
  18. package/dist/utils.js +1 -1
  19. package/docs/api/classes/ColumnFactory.md +1 -1
  20. package/docs/api/classes/ErrorBoundary.md +1 -1
  21. package/docs/api/classes/InvalidScopeError.md +1 -1
  22. package/docs/api/classes/MissingUserContextError.md +1 -1
  23. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  24. package/docs/api/classes/PermissionDeniedError.md +1 -1
  25. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  26. package/docs/api/classes/RBACAuditManager.md +1 -1
  27. package/docs/api/classes/RBACCache.md +1 -1
  28. package/docs/api/classes/RBACEngine.md +1 -1
  29. package/docs/api/classes/RBACError.md +1 -1
  30. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  31. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  32. package/docs/api/classes/StorageUtils.md +2 -1
  33. package/docs/api/enums/FileCategory.md +1 -1
  34. package/docs/api/interfaces/AggregateConfig.md +1 -1
  35. package/docs/api/interfaces/ButtonProps.md +1 -1
  36. package/docs/api/interfaces/CardProps.md +1 -1
  37. package/docs/api/interfaces/ColorPalette.md +1 -1
  38. package/docs/api/interfaces/ColorShade.md +1 -1
  39. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  40. package/docs/api/interfaces/DataRecord.md +1 -1
  41. package/docs/api/interfaces/DataTableAction.md +1 -1
  42. package/docs/api/interfaces/DataTableColumn.md +1 -1
  43. package/docs/api/interfaces/DataTableProps.md +1 -1
  44. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  45. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  46. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  47. package/docs/api/interfaces/FileDisplayProps.md +77 -35
  48. package/docs/api/interfaces/FileMetadata.md +1 -1
  49. package/docs/api/interfaces/FileReference.md +1 -1
  50. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  51. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  52. package/docs/api/interfaces/FileUploadProps.md +1 -1
  53. package/docs/api/interfaces/FooterProps.md +1 -1
  54. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  55. package/docs/api/interfaces/InputProps.md +1 -1
  56. package/docs/api/interfaces/LabelProps.md +1 -1
  57. package/docs/api/interfaces/LoginFormProps.md +1 -1
  58. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  59. package/docs/api/interfaces/NavigationContextType.md +1 -1
  60. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  61. package/docs/api/interfaces/NavigationItem.md +1 -1
  62. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  63. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  64. package/docs/api/interfaces/Organisation.md +1 -1
  65. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  66. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  67. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  68. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  69. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  70. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  71. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  72. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  73. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  74. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  75. package/docs/api/interfaces/PaletteData.md +1 -1
  76. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  77. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  78. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  79. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  80. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  81. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  82. package/docs/api/interfaces/PublicPageHeaderProps.md +11 -24
  83. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  84. package/docs/api/interfaces/RBACConfig.md +1 -1
  85. package/docs/api/interfaces/RBACLogger.md +1 -1
  86. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  87. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  88. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  89. package/docs/api/interfaces/RouteConfig.md +1 -1
  90. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  91. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  92. package/docs/api/interfaces/StorageConfig.md +1 -1
  93. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  94. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  95. package/docs/api/interfaces/StorageListOptions.md +1 -1
  96. package/docs/api/interfaces/StorageListResult.md +1 -1
  97. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  98. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  99. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  100. package/docs/api/interfaces/StyleImport.md +1 -1
  101. package/docs/api/interfaces/SwitchProps.md +1 -1
  102. package/docs/api/interfaces/ToastActionElement.md +1 -1
  103. package/docs/api/interfaces/ToastProps.md +1 -1
  104. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  105. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  106. package/docs/api/interfaces/UseEventLogoOptions.md +1 -1
  107. package/docs/api/interfaces/UseEventLogoReturn.md +1 -1
  108. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  109. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  110. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  111. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  112. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  113. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  114. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  115. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  116. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  117. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  118. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  119. package/docs/api/interfaces/UserEventAccess.md +1 -1
  120. package/docs/api/interfaces/UserMenuProps.md +1 -1
  121. package/docs/api/interfaces/UserProfile.md +1 -1
  122. package/docs/api/modules.md +33 -100
  123. package/docs/implementation-guides/data-tables.md +19 -0
  124. package/docs/implementation-guides/file-reference-system.md +53 -2
  125. package/package.json +1 -1
  126. package/src/components/DataTable/components/DataTableCore.tsx +56 -4
  127. package/src/components/DataTable/components/EditableRow.tsx +69 -73
  128. package/src/components/Dialog/Dialog.test.tsx +4 -3
  129. package/src/components/Dialog/Dialog.tsx +24 -18
  130. package/src/components/Dialog/examples/ScrollableDialogExample.tsx +290 -0
  131. package/src/components/FileDisplay/FileDisplay.test.tsx +1 -1
  132. package/src/components/FileDisplay/FileDisplay.tsx +189 -300
  133. package/src/components/PublicLayout/PublicPageHeader.tsx +14 -9
  134. package/src/components/PublicLayout/__tests__/PublicPageHeader.test.tsx +25 -35
  135. package/src/components/PublicLayout/index.ts +2 -5
  136. package/src/components/Toast/Toast.tsx +1 -1
  137. package/src/examples/PublicEventPage.tsx +17 -7
  138. package/src/examples/PublicPageApp.tsx +18 -8
  139. package/src/hooks/useFileReference.ts +10 -1
  140. package/src/utils/file-reference.ts +24 -7
  141. package/src/utils/storage/helpers.ts +12 -1
  142. package/dist/chunk-2ZYHCFUO.js.map +0 -1
  143. package/dist/chunk-MKMKUCPF.js.map +0 -1
  144. package/dist/chunk-Z4HVIZSK.js.map +0 -1
  145. package/docs/api/interfaces/EventLogoProps.md +0 -152
  146. package/src/components/PublicLayout/EventLogo.tsx +0 -474
  147. /package/dist/{DataTable-MHN7PNH3.js.map → DataTable-DXELRJIX.js.map} +0 -0
@@ -1,152 +0,0 @@
1
- [@jmruthers/pace-core - v0.5.100](../README.md) / [Exports](../modules.md) / EventLogoProps
2
-
3
- # Interface: EventLogoProps
4
-
5
- ## Table of contents
6
-
7
- ### Properties
8
-
9
- - [eventId](EventLogoProps.md#eventid)
10
- - [eventName](EventLogoProps.md#eventname)
11
- - [organisationId](EventLogoProps.md#organisationid)
12
- - [size](EventLogoProps.md#size)
13
- - [className](EventLogoProps.md#classname)
14
- - [showFallback](EventLogoProps.md#showfallback)
15
- - [generateFallbackText](EventLogoProps.md#generatefallbacktext)
16
- - [validateImage](EventLogoProps.md#validateimage)
17
- - [loadingComponent](EventLogoProps.md#loadingcomponent)
18
- - [errorComponent](EventLogoProps.md#errorcomponent)
19
-
20
- ## Properties
21
-
22
- ### eventId
23
-
24
- • **eventId**: `string`
25
-
26
- The event ID to fetch logo for
27
-
28
- #### Defined in
29
-
30
- [packages/core/src/components/PublicLayout/EventLogo.tsx:59](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L59)
31
-
32
- ___
33
-
34
- ### eventName
35
-
36
- • **eventName**: `string`
37
-
38
- The event name for fallback text generation
39
-
40
- #### Defined in
41
-
42
- [packages/core/src/components/PublicLayout/EventLogo.tsx:61](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L61)
43
-
44
- ___
45
-
46
- ### organisationId
47
-
48
- • **organisationId**: `string`
49
-
50
- The organisation ID for storage path
51
-
52
- #### Defined in
53
-
54
- [packages/core/src/components/PublicLayout/EventLogo.tsx:63](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L63)
55
-
56
- ___
57
-
58
- ### size
59
-
60
- • `Optional` **size**: ``"sm"`` \| ``"lg"`` \| ``"md"`` \| ``"xl"`` \| ``"xs"`` \| ``"2xl"``
61
-
62
- Logo size variant
63
-
64
- #### Defined in
65
-
66
- [packages/core/src/components/PublicLayout/EventLogo.tsx:65](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L65)
67
-
68
- ___
69
-
70
- ### className
71
-
72
- • `Optional` **className**: `string`
73
-
74
- Custom CSS classes
75
-
76
- #### Defined in
77
-
78
- [packages/core/src/components/PublicLayout/EventLogo.tsx:67](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L67)
79
-
80
- ___
81
-
82
- ### showFallback
83
-
84
- • `Optional` **showFallback**: `boolean`
85
-
86
- Whether to show fallback text when no logo is available
87
-
88
- #### Defined in
89
-
90
- [packages/core/src/components/PublicLayout/EventLogo.tsx:69](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L69)
91
-
92
- ___
93
-
94
- ### generateFallbackText
95
-
96
- • `Optional` **generateFallbackText**: (`eventName`: `string`) => `string`
97
-
98
- Custom fallback text generator
99
-
100
- #### Type declaration
101
-
102
- ▸ (`eventName`): `string`
103
-
104
- ##### Parameters
105
-
106
- | Name | Type |
107
- | :------ | :------ |
108
- | `eventName` | `string` |
109
-
110
- ##### Returns
111
-
112
- `string`
113
-
114
- #### Defined in
115
-
116
- [packages/core/src/components/PublicLayout/EventLogo.tsx:71](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L71)
117
-
118
- ___
119
-
120
- ### validateImage
121
-
122
- • `Optional` **validateImage**: `boolean`
123
-
124
- Whether to validate image existence
125
-
126
- #### Defined in
127
-
128
- [packages/core/src/components/PublicLayout/EventLogo.tsx:73](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L73)
129
-
130
- ___
131
-
132
- ### loadingComponent
133
-
134
- • `Optional` **loadingComponent**: `ComponentType`\<{}\>
135
-
136
- Custom loading component
137
-
138
- #### Defined in
139
-
140
- [packages/core/src/components/PublicLayout/EventLogo.tsx:75](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L75)
141
-
142
- ___
143
-
144
- ### errorComponent
145
-
146
- • `Optional` **errorComponent**: `ComponentType`\<\{ `error`: `Error` }\>
147
-
148
- Custom error component
149
-
150
- #### Defined in
151
-
152
- [packages/core/src/components/PublicLayout/EventLogo.tsx:77](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/components/PublicLayout/EventLogo.tsx#L77)
@@ -1,474 +0,0 @@
1
- /**
2
- * @file Event Logo Component
3
- * @package @jmruthers/pace-core
4
- * @module Components/PublicLayout
5
- * @since 1.0.0
6
- *
7
- * A context-aware component for displaying event logos with automatic fallback to event initials.
8
- * Automatically detects whether it's being used in a public or authenticated context and behaves
9
- * appropriately without triggering authentication context leakage.
10
- *
11
- * Features:
12
- * - Context-aware (works in both public and authenticated contexts)
13
- * - Automatic fallback to event initials
14
- * - Logo validation and error handling
15
- * - Multiple size options
16
- * - Responsive design
17
- * - Accessibility compliant
18
- * - TypeScript support
19
- *
20
- * @example
21
- * ```tsx
22
- * import { EventLogo } from '@jmruthers/pace-core';
23
- *
24
- * function EventHeader() {
25
- * return (
26
- * <EventLogo
27
- * eventId={event.id}
28
- * eventName={event.event_name}
29
- * organisationId={event.organisation_id}
30
- * size="lg"
31
- * className="rounded-full"
32
- * />
33
- * );
34
- * }
35
- * ```
36
- *
37
- * @accessibility
38
- * - WCAG 2.1 AA compliant
39
- * - Proper alt text for images
40
- * - Screen reader friendly fallbacks
41
- * - High contrast support
42
- *
43
- * @dependencies
44
- * - React 18+ - Component framework
45
- * - usePublicEventLogo hook - Logo data access
46
- * - Tailwind CSS - Styling
47
- */
48
-
49
- import React, { useMemo, useContext } from 'react';
50
- import { usePublicEventLogo } from '../../hooks/public/usePublicEventLogo';
51
- import { useEventLogo } from '../../hooks/useEventLogo';
52
- import { PublicPageContext, useIsPublicPage } from './PublicPageProvider';
53
- import { useUnifiedAuth } from '../../providers/services/UnifiedAuthProvider';
54
- import type { SupabaseClient } from '@supabase/supabase-js';
55
- import type { Database } from '../../types/database';
56
-
57
- export interface EventLogoProps {
58
- /** The event ID to fetch logo for */
59
- eventId: string;
60
- /** The event name for fallback text generation */
61
- eventName: string;
62
- /** The organisation ID for storage path */
63
- organisationId: string;
64
- /** Logo size variant */
65
- size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl';
66
- /** Custom CSS classes */
67
- className?: string;
68
- /** Whether to show fallback text when no logo is available */
69
- showFallback?: boolean;
70
- /** Custom fallback text generator */
71
- generateFallbackText?: (eventName: string) => string;
72
- /** Whether to validate image existence */
73
- validateImage?: boolean;
74
- /** Custom loading component */
75
- loadingComponent?: React.ComponentType;
76
- /** Custom error component */
77
- errorComponent?: React.ComponentType<{ error: Error }>;
78
- }
79
-
80
- /**
81
- * Size classes for different logo sizes
82
- */
83
- const sizeClasses = {
84
- xs: 'h-4 w-4 text-xs',
85
- sm: 'h-6 w-6 text-sm',
86
- md: 'h-8 w-8 text-base',
87
- lg: 'h-12 w-12 text-lg',
88
- xl: 'h-16 w-16 text-xl',
89
- '2xl': 'h-20 w-20 text-2xl'
90
- };
91
-
92
- /**
93
- * Default fallback text generator
94
- */
95
- function defaultGenerateFallbackText(eventName: string): string {
96
- if (!eventName) return 'EV';
97
-
98
- return eventName
99
- .split(' ')
100
- .map(word => word.charAt(0).toUpperCase())
101
- .join('')
102
- .substring(0, 3); // Max 3 characters
103
- }
104
-
105
- /**
106
- * Component for displaying event logos with fallback to initials
107
- *
108
- * This component is context-aware and automatically detects whether it's being used
109
- * in a public or authenticated context. It fetches and displays event logos from storage,
110
- * with automatic fallback to event initials if no logo is available.
111
- *
112
- * @param props - Logo configuration and styling
113
- * @returns React element with event logo or fallback
114
- */
115
- /**
116
- * Internal component for public page context
117
- * Uses PublicPageContext to get Supabase client
118
- */
119
- function EventLogoPublic({
120
- eventId,
121
- eventName,
122
- organisationId,
123
- size,
124
- className,
125
- showFallback,
126
- generateFallbackText,
127
- validateImage,
128
- LoadingComponent,
129
- ErrorComponent
130
- }: EventLogoProps & { LoadingComponent?: React.ComponentType; ErrorComponent?: React.ComponentType<{ error: Error }> }) {
131
- const publicPageContext = useContext(PublicPageContext);
132
- const supabase = publicPageContext?.supabase ?? null;
133
-
134
- // Validate UUID format for organisationId to prevent database errors
135
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
136
- const hasValidOrganisationId = organisationId && uuidRegex.test(organisationId);
137
-
138
- // If no Supabase client is available, show fallback immediately
139
- if (!supabase) {
140
- const effectiveSize = size || 'md';
141
- return (
142
- <div className={`${sizeClasses[effectiveSize]} ${className}`.trim()} title={`${eventName} logo (Supabase not configured)`}>
143
- {eventName ? defaultGenerateFallbackText(eventName) : 'EV'}
144
- </div>
145
- );
146
- }
147
-
148
- const {
149
- logoUrl,
150
- fallbackText,
151
- isLoading,
152
- error
153
- } = usePublicEventLogo(
154
- eventId,
155
- eventName,
156
- organisationId, // Always pass organisationId, let the hook handle validation
157
- {
158
- validateImage,
159
- generateFallbackText,
160
- supabase
161
- }
162
- );
163
-
164
- // Memoize the size classes - provide default 'md' if size is undefined
165
- const sizeClass = useMemo(() => sizeClasses[size || 'md'], [size]);
166
-
167
- // Memoize the combined classes
168
- const combinedClasses = useMemo(() => {
169
- const baseClasses = 'flex items-center justify-center bg-gray-100 text-gray-600 font-semibold rounded';
170
- return `${baseClasses} ${sizeClass} ${className}`.trim();
171
- }, [sizeClass, className]);
172
-
173
- // Handle invalid organisation ID - show fallback immediately only if we have no data
174
- if (!hasValidOrganisationId && !isLoading && !logoUrl && showFallback) {
175
- return (
176
- <div className={combinedClasses} title={`${eventName} logo (invalid organisation ID)`}>
177
- {fallbackText}
178
- </div>
179
- );
180
- }
181
-
182
- // Handle loading state
183
- if (isLoading) {
184
- if (LoadingComponent) {
185
- return <LoadingComponent />;
186
- }
187
-
188
- return (
189
- <div className={`${combinedClasses} animate-pulse`}>
190
- <div className="w-3/4 h-3/4 bg-gray-300 rounded"></div>
191
- </div>
192
- );
193
- }
194
-
195
- // Handle error state
196
- if (error) {
197
- if (ErrorComponent) {
198
- return <ErrorComponent error={error} />;
199
- }
200
-
201
- if (showFallback) {
202
- return (
203
- <div className={combinedClasses} title={`${eventName} (logo unavailable)`}>
204
- {fallbackText}
205
- </div>
206
- );
207
- }
208
-
209
- return null;
210
- }
211
-
212
- // Handle no logo available
213
- if (!logoUrl) {
214
- if (showFallback) {
215
- return (
216
- <div className={combinedClasses} title={`${eventName} logo`}>
217
- {fallbackText}
218
- </div>
219
- );
220
- }
221
-
222
- return null;
223
- }
224
-
225
- // Render the actual logo
226
- // Apply object-contain to maintain aspect ratio and rounded to match fallback styling
227
- const imageClasses = `${sizeClass} object-contain rounded ${className}`.trim();
228
-
229
- return (
230
- <img
231
- src={logoUrl}
232
- alt={`${eventName} logo`}
233
- className={imageClasses}
234
- onError={(e) => {
235
- // If image fails to load, hide it and show fallback
236
- const target = e.target as HTMLImageElement;
237
- target.style.display = 'none';
238
-
239
- // Create fallback element
240
- const fallback = document.createElement('div');
241
- fallback.className = combinedClasses;
242
- fallback.textContent = fallbackText;
243
- fallback.title = `${eventName} logo`;
244
-
245
- // Insert fallback after the image
246
- target.parentNode?.insertBefore(fallback, target.nextSibling);
247
- }}
248
- />
249
- );
250
- }
251
-
252
- /**
253
- * Internal component for authenticated page context
254
- * Uses UnifiedAuthProvider to get Supabase client
255
- */
256
- function EventLogoAuthenticated({
257
- eventId,
258
- eventName,
259
- organisationId,
260
- size,
261
- className,
262
- showFallback,
263
- generateFallbackText,
264
- validateImage,
265
- LoadingComponent,
266
- ErrorComponent
267
- }: EventLogoProps & { LoadingComponent?: React.ComponentType; ErrorComponent?: React.ComponentType<{ error: Error }> }) {
268
- const { supabase } = useUnifiedAuth();
269
-
270
- // Validate UUID format for organisationId to prevent database errors
271
- const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
272
- const hasValidOrganisationId = organisationId && uuidRegex.test(organisationId);
273
-
274
- // If no Supabase client is available, show fallback immediately
275
- if (!supabase) {
276
- const effectiveSize = size || 'md';
277
- return (
278
- <div className={`${sizeClasses[effectiveSize]} ${className}`.trim()} title={`${eventName} logo (Supabase not configured)`}>
279
- {eventName ? defaultGenerateFallbackText(eventName) : 'EV'}
280
- </div>
281
- );
282
- }
283
-
284
- // Use the authenticated event logo hook (supports both public and private logos)
285
- const {
286
- logoUrl,
287
- fallbackText,
288
- isLoading,
289
- error
290
- } = useEventLogo(
291
- supabase,
292
- eventId,
293
- eventName,
294
- organisationId,
295
- {
296
- validateImage,
297
- generateFallbackText
298
- }
299
- );
300
-
301
- // Memoize the size classes - provide default 'md' if size is undefined
302
- const sizeClass = useMemo(() => sizeClasses[size || 'md'], [size]);
303
-
304
- // Memoize the combined classes
305
- const combinedClasses = useMemo(() => {
306
- const baseClasses = 'flex items-center justify-center bg-gray-100 text-gray-600 font-semibold rounded';
307
- return `${baseClasses} ${sizeClass} ${className}`.trim();
308
- }, [sizeClass, className]);
309
-
310
- // Handle invalid organisation ID - show fallback immediately only if we have no data
311
- if (!hasValidOrganisationId && !isLoading && !logoUrl && showFallback) {
312
- return (
313
- <div className={combinedClasses} title={`${eventName} logo (invalid organisation ID)`}>
314
- {fallbackText}
315
- </div>
316
- );
317
- }
318
-
319
- // Handle loading state
320
- if (isLoading) {
321
- if (LoadingComponent) {
322
- return <LoadingComponent />;
323
- }
324
-
325
- return (
326
- <div className={`${combinedClasses} animate-pulse`}>
327
- <div className="w-3/4 h-3/4 bg-gray-300 rounded"></div>
328
- </div>
329
- );
330
- }
331
-
332
- // Handle error state
333
- if (error) {
334
- if (ErrorComponent) {
335
- return <ErrorComponent error={error} />;
336
- }
337
-
338
- if (showFallback) {
339
- return (
340
- <div className={combinedClasses} title={`${eventName} (logo unavailable)`}>
341
- {fallbackText}
342
- </div>
343
- );
344
- }
345
-
346
- return null;
347
- }
348
-
349
- // Handle no logo available
350
- if (!logoUrl) {
351
- if (showFallback) {
352
- return (
353
- <div className={combinedClasses} title={`${eventName} logo`}>
354
- {fallbackText}
355
- </div>
356
- );
357
- }
358
-
359
- return null;
360
- }
361
-
362
- // Render the actual logo
363
- // Apply object-contain to maintain aspect ratio and rounded to match fallback styling
364
- const imageClasses = `${sizeClass} object-contain rounded ${className}`.trim();
365
-
366
- return (
367
- <img
368
- src={logoUrl}
369
- alt={`${eventName} logo`}
370
- className={imageClasses}
371
- onError={(e) => {
372
- // If image fails to load, hide it and show fallback
373
- const target = e.target as HTMLImageElement;
374
- target.style.display = 'none';
375
-
376
- // Create fallback element
377
- const fallback = document.createElement('div');
378
- fallback.className = combinedClasses;
379
- fallback.textContent = fallbackText;
380
- fallback.title = `${eventName} logo`;
381
-
382
- // Insert fallback after the image
383
- target.parentNode?.insertBefore(fallback, target.nextSibling);
384
- }}
385
- />
386
- );
387
- }
388
-
389
- /**
390
- * Component for displaying event logos with fallback to initials
391
- *
392
- * This component is context-aware and automatically detects whether it's being used
393
- * in a public or authenticated context. It fetches and displays event logos from storage,
394
- * with automatic fallback to event initials if no logo is available.
395
- *
396
- * @param props - Logo configuration and styling
397
- * @returns React element with event logo or fallback
398
- */
399
- export function EventLogo({
400
- eventId,
401
- eventName,
402
- organisationId,
403
- size = 'md',
404
- className = '',
405
- showFallback = true,
406
- generateFallbackText = defaultGenerateFallbackText,
407
- validateImage = true,
408
- loadingComponent: LoadingComponent,
409
- errorComponent: ErrorComponent
410
- }: EventLogoProps) {
411
- // Check which context we're in and route to the appropriate component
412
- const isPublicPage = useIsPublicPage();
413
-
414
- // If we're in a public page context, use the public component
415
- if (isPublicPage) {
416
- return (
417
- <EventLogoPublic
418
- eventId={eventId}
419
- eventName={eventName}
420
- organisationId={organisationId}
421
- size={size}
422
- className={className}
423
- showFallback={showFallback}
424
- generateFallbackText={generateFallbackText}
425
- validateImage={validateImage}
426
- LoadingComponent={LoadingComponent}
427
- ErrorComponent={ErrorComponent}
428
- />
429
- );
430
- }
431
-
432
- // Otherwise, try to use the authenticated component
433
- // It will throw if not in UnifiedAuthProvider, which will be caught by error boundary
434
- return (
435
- <EventLogoAuthenticated
436
- eventId={eventId}
437
- eventName={eventName}
438
- organisationId={organisationId}
439
- size={size}
440
- className={className}
441
- showFallback={showFallback}
442
- generateFallbackText={generateFallbackText}
443
- validateImage={validateImage}
444
- LoadingComponent={LoadingComponent}
445
- ErrorComponent={ErrorComponent}
446
- />
447
- );
448
- }
449
-
450
- /**
451
- * Compact event logo for small spaces
452
- */
453
- export function EventLogoCompact(props: EventLogoProps) {
454
- return (
455
- <EventLogo
456
- {...props}
457
- size="sm"
458
- className={`${props.className || ''} rounded-sm`}
459
- />
460
- );
461
- }
462
-
463
- /**
464
- * Large event logo for prominent display
465
- */
466
- export function EventLogoLarge(props: EventLogoProps) {
467
- return (
468
- <EventLogo
469
- {...props}
470
- size="xl"
471
- className={`${props.className || ''} rounded-lg`}
472
- />
473
- );
474
- }