@jmruthers/pace-core 0.5.102 → 0.5.103

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 (141) hide show
  1. package/dist/{DataTable-DXELRJIX.js → DataTable-EEDFYMJP.js} +2 -2
  2. package/dist/{PublicLoadingSpinner-Cvgk-V0F.d.ts → PublicLoadingSpinner-48ewSMKK.d.ts} +1 -96
  3. package/dist/{chunk-7ME4Z5OY.js → chunk-5SGBVBRU.js} +12 -148
  4. package/dist/chunk-5SGBVBRU.js.map +1 -0
  5. package/dist/{chunk-EVVRUGQ2.js → chunk-62AVH7CM.js} +78 -55
  6. package/dist/{chunk-EVVRUGQ2.js.map → chunk-62AVH7CM.js.map} +1 -1
  7. package/dist/{chunk-UDWTCBSH.js → chunk-SZWCMVTQ.js} +12 -175
  8. package/dist/chunk-SZWCMVTQ.js.map +1 -0
  9. package/dist/{chunk-SZWRW5FD.js → chunk-X33A4WWI.js} +23 -3
  10. package/dist/{chunk-SZWRW5FD.js.map → chunk-X33A4WWI.js.map} +1 -1
  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.d.ts +1 -1
  15. package/dist/hooks.js +2 -9
  16. package/dist/hooks.js.map +1 -1
  17. package/dist/index.d.ts +2 -2
  18. package/dist/index.js +4 -16
  19. package/dist/index.js.map +1 -1
  20. package/dist/types.js +3 -3
  21. package/dist/{usePublicRouteParams-BwMR2uub.d.ts → usePublicRouteParams-BiXgKiYa.d.ts} +1 -117
  22. package/dist/utils.js +1 -1
  23. package/docs/api/classes/ColumnFactory.md +1 -1
  24. package/docs/api/classes/ErrorBoundary.md +1 -1
  25. package/docs/api/classes/InvalidScopeError.md +1 -1
  26. package/docs/api/classes/MissingUserContextError.md +1 -1
  27. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  28. package/docs/api/classes/PermissionDeniedError.md +1 -1
  29. package/docs/api/classes/PublicErrorBoundary.md +1 -1
  30. package/docs/api/classes/RBACAuditManager.md +1 -1
  31. package/docs/api/classes/RBACCache.md +1 -1
  32. package/docs/api/classes/RBACEngine.md +1 -1
  33. package/docs/api/classes/RBACError.md +1 -1
  34. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  35. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  36. package/docs/api/classes/StorageUtils.md +1 -1
  37. package/docs/api/enums/FileCategory.md +1 -1
  38. package/docs/api/interfaces/AggregateConfig.md +1 -1
  39. package/docs/api/interfaces/ButtonProps.md +1 -1
  40. package/docs/api/interfaces/CardProps.md +1 -1
  41. package/docs/api/interfaces/ColorPalette.md +1 -1
  42. package/docs/api/interfaces/ColorShade.md +1 -1
  43. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  44. package/docs/api/interfaces/DataRecord.md +1 -1
  45. package/docs/api/interfaces/DataTableAction.md +1 -1
  46. package/docs/api/interfaces/DataTableColumn.md +1 -1
  47. package/docs/api/interfaces/DataTableProps.md +1 -1
  48. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  49. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  50. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  51. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  52. package/docs/api/interfaces/FileMetadata.md +1 -1
  53. package/docs/api/interfaces/FileReference.md +1 -1
  54. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  55. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  56. package/docs/api/interfaces/FileUploadProps.md +1 -1
  57. package/docs/api/interfaces/FooterProps.md +1 -1
  58. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  59. package/docs/api/interfaces/InputProps.md +1 -1
  60. package/docs/api/interfaces/LabelProps.md +1 -1
  61. package/docs/api/interfaces/LoginFormProps.md +1 -1
  62. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  63. package/docs/api/interfaces/NavigationContextType.md +1 -1
  64. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  65. package/docs/api/interfaces/NavigationItem.md +1 -1
  66. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  67. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  68. package/docs/api/interfaces/Organisation.md +1 -1
  69. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  70. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  71. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  72. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  73. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  74. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  75. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  76. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  77. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  78. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  79. package/docs/api/interfaces/PaletteData.md +1 -1
  80. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  81. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  82. package/docs/api/interfaces/PublicErrorBoundaryProps.md +1 -1
  83. package/docs/api/interfaces/PublicErrorBoundaryState.md +1 -1
  84. package/docs/api/interfaces/PublicLoadingSpinnerProps.md +1 -1
  85. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  86. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  87. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  88. package/docs/api/interfaces/RBACConfig.md +1 -1
  89. package/docs/api/interfaces/RBACLogger.md +1 -1
  90. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  91. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  92. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  93. package/docs/api/interfaces/RouteConfig.md +1 -1
  94. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  95. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  96. package/docs/api/interfaces/StorageConfig.md +1 -1
  97. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  98. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  99. package/docs/api/interfaces/StorageListOptions.md +1 -1
  100. package/docs/api/interfaces/StorageListResult.md +1 -1
  101. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  102. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  103. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  104. package/docs/api/interfaces/StyleImport.md +1 -1
  105. package/docs/api/interfaces/SwitchProps.md +1 -1
  106. package/docs/api/interfaces/ToastActionElement.md +1 -1
  107. package/docs/api/interfaces/ToastProps.md +1 -1
  108. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  109. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  110. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  111. package/docs/api/interfaces/UseInactivityTrackerReturn.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 +2 -150
  123. package/docs/implementation-guides/file-reference-system.md +31 -19
  124. package/package.json +1 -1
  125. package/src/components/DataTable/components/DataTableCore.tsx +23 -13
  126. package/src/components/DataTable/hooks/useTableColumns.ts +36 -6
  127. package/src/components/PublicLayout/PublicPageHeader.tsx +1 -1
  128. package/src/components/index.ts +0 -2
  129. package/src/hooks/public/index.ts +2 -4
  130. package/src/index.ts +0 -2
  131. package/src/utils/file-reference.ts +30 -2
  132. package/src/utils/storage/README.md +22 -20
  133. package/dist/chunk-7ME4Z5OY.js.map +0 -1
  134. package/dist/chunk-UDWTCBSH.js.map +0 -1
  135. package/docs/api/interfaces/UseEventLogoOptions.md +0 -74
  136. package/docs/api/interfaces/UseEventLogoReturn.md +0 -81
  137. package/docs/api/interfaces/UsePublicEventLogoOptions.md +0 -87
  138. package/docs/api/interfaces/UsePublicEventLogoReturn.md +0 -81
  139. package/src/hooks/public/usePublicEventLogo.ts +0 -295
  140. package/src/hooks/useEventLogo.ts +0 -316
  141. /package/dist/{DataTable-DXELRJIX.js.map → DataTable-EEDFYMJP.js.map} +0 -0
@@ -1,6 +1,6 @@
1
- [@jmruthers/pace-core - v0.5.102](README.md) / Exports
1
+ [@jmruthers/pace-core - v0.5.103](README.md) / Exports
2
2
 
3
- # @jmruthers/pace-core - v0.5.102
3
+ # @jmruthers/pace-core - v0.5.103
4
4
 
5
5
  **`File`**
6
6
 
@@ -85,13 +85,9 @@ import { Dialog, NavigationMenu } from '@jmruthers/pace-core/components';
85
85
  - [UserMenuProps](interfaces/UserMenuProps.md)
86
86
  - [UsePublicEventReturn](interfaces/UsePublicEventReturn.md)
87
87
  - [UsePublicEventOptions](interfaces/UsePublicEventOptions.md)
88
- - [UsePublicEventLogoReturn](interfaces/UsePublicEventLogoReturn.md)
89
- - [UsePublicEventLogoOptions](interfaces/UsePublicEventLogoOptions.md)
90
88
  - [UsePublicFileDisplayReturn](interfaces/UsePublicFileDisplayReturn.md)
91
89
  - [UsePublicFileDisplayOptions](interfaces/UsePublicFileDisplayOptions.md)
92
90
  - [UsePublicRouteParamsReturn](interfaces/UsePublicRouteParamsReturn.md)
93
- - [UseEventLogoReturn](interfaces/UseEventLogoReturn.md)
94
- - [UseEventLogoOptions](interfaces/UseEventLogoOptions.md)
95
91
  - [UseInactivityTrackerOptions](interfaces/UseInactivityTrackerOptions.md)
96
92
  - [UseInactivityTrackerReturn](interfaces/UseInactivityTrackerReturn.md)
97
93
  - [UserEventAccess](interfaces/UserEventAccess.md)
@@ -273,9 +269,6 @@ import { Dialog, NavigationMenu } from '@jmruthers/pace-core/components';
273
269
  - [usePublicEvent](modules.md#usepublicevent)
274
270
  - [clearPublicEventCache](modules.md#clearpubliceventcache)
275
271
  - [getPublicEventCacheStats](modules.md#getpubliceventcachestats)
276
- - [usePublicEventLogo](modules.md#usepubliceventlogo)
277
- - [clearPublicLogoCache](modules.md#clearpubliclogocache)
278
- - [getPublicLogoCacheStats](modules.md#getpubliclogocachestats)
279
272
  - [usePublicFileDisplay](modules.md#usepublicfiledisplay)
280
273
  - [clearPublicFileDisplayCache](modules.md#clearpublicfiledisplaycache)
281
274
  - [getPublicFileDisplayCacheStats](modules.md#getpublicfiledisplaycachestats)
@@ -288,9 +281,6 @@ import { Dialog, NavigationMenu } from '@jmruthers/pace-core/components';
288
281
  - [useInactivityService](modules.md#useinactivityservice)
289
282
  - [useOrganisationService](modules.md#useorganisationservice)
290
283
  - [useAppConfig](modules.md#useappconfig)
291
- - [useEventLogo](modules.md#useeventlogo)
292
- - [clearEventLogoCache](modules.md#cleareventlogocache)
293
- - [getEventLogoCacheStats](modules.md#geteventlogocachestats)
294
284
  - [useEventTheme](modules.md#useeventtheme)
295
285
  - [useEvents](modules.md#useevents)
296
286
  - [useFileReference](modules.md#usefilereference)
@@ -3803,74 +3793,6 @@ Get cache statistics for debugging
3803
3793
 
3804
3794
  ___
3805
3795
 
3806
- ### usePublicEventLogo
3807
-
3808
- ▸ **usePublicEventLogo**(`eventId`, `eventName`, `organisationId`, `options`): [`UsePublicEventLogoReturn`](interfaces/UsePublicEventLogoReturn.md)
3809
-
3810
- Hook for accessing public event logo URLs
3811
-
3812
- This hook provides access to event logo URLs without requiring
3813
- authentication. It includes fallback handling and image validation.
3814
-
3815
- #### Parameters
3816
-
3817
- | Name | Type | Description |
3818
- | :------ | :------ | :------ |
3819
- | `eventId` | `undefined` \| `string` | The event ID to fetch logo for |
3820
- | `eventName` | `undefined` \| `string` | The event name for fallback text generation |
3821
- | `organisationId` | `undefined` \| `string` | The organisation ID for storage path |
3822
- | `options` | [`UsePublicEventLogoOptions`](interfaces/UsePublicEventLogoOptions.md) | Configuration options for caching and behavior |
3823
-
3824
- #### Returns
3825
-
3826
- [`UsePublicEventLogoReturn`](interfaces/UsePublicEventLogoReturn.md)
3827
-
3828
- Object containing logo URL, fallback text, loading state, error, and refetch function
3829
-
3830
- #### Defined in
3831
-
3832
- [packages/core/src/hooks/public/usePublicEventLogo.ts:127](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/hooks/public/usePublicEventLogo.ts#L127)
3833
-
3834
- ___
3835
-
3836
- ### clearPublicLogoCache
3837
-
3838
- ▸ **clearPublicLogoCache**(): `void`
3839
-
3840
- Clear all cached public logo data
3841
- Useful for testing or when you need to force refresh all data
3842
-
3843
- #### Returns
3844
-
3845
- `void`
3846
-
3847
- #### Defined in
3848
-
3849
- [packages/core/src/hooks/public/usePublicEventLogo.ts:278](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/hooks/public/usePublicEventLogo.ts#L278)
3850
-
3851
- ___
3852
-
3853
- ### getPublicLogoCacheStats
3854
-
3855
- ▸ **getPublicLogoCacheStats**(): `Object`
3856
-
3857
- Get cache statistics for debugging
3858
-
3859
- #### Returns
3860
-
3861
- `Object`
3862
-
3863
- | Name | Type |
3864
- | :------ | :------ |
3865
- | `size` | `number` |
3866
- | `keys` | `string`[] |
3867
-
3868
- #### Defined in
3869
-
3870
- [packages/core/src/hooks/public/usePublicEventLogo.ts:289](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/hooks/public/usePublicEventLogo.ts#L289)
3871
-
3872
- ___
3873
-
3874
3796
  ### usePublicFileDisplay
3875
3797
 
3876
3798
  ▸ **usePublicFileDisplay**(`table_name`, `record_id`, `organisation_id`, `category`, `options`): [`UsePublicFileDisplayReturn`](interfaces/UsePublicFileDisplayReturn.md)
@@ -4116,76 +4038,6 @@ App configuration and loading state
4116
4038
 
4117
4039
  ___
4118
4040
 
4119
- ### useEventLogo
4120
-
4121
- ▸ **useEventLogo**(`supabase`, `eventId`, `eventName`, `organisationId`, `options?`): [`UseEventLogoReturn`](interfaces/UseEventLogoReturn.md)
4122
-
4123
- Hook for accessing event logo URLs in authenticated contexts
4124
-
4125
- This hook provides access to event logo URLs for authenticated users.
4126
- It supports both public and private logos, generating appropriate URLs
4127
- (public URLs for public files, signed URLs for private files).
4128
-
4129
- #### Parameters
4130
-
4131
- | Name | Type | Description |
4132
- | :------ | :------ | :------ |
4133
- | `supabase` | ``null`` \| `default`\<`any`, ``"public"``, ``"public"``, `any`, `any`\> | Supabase client instance (required) |
4134
- | `eventId` | `undefined` \| `string` | The event ID to fetch logo for |
4135
- | `eventName` | `undefined` \| `string` | The event name for fallback text generation |
4136
- | `organisationId` | `undefined` \| `string` | The organisation ID for storage path |
4137
- | `options` | [`UseEventLogoOptions`](interfaces/UseEventLogoOptions.md) | Configuration options for caching and behavior |
4138
-
4139
- #### Returns
4140
-
4141
- [`UseEventLogoReturn`](interfaces/UseEventLogoReturn.md)
4142
-
4143
- Object containing logo URL, fallback text, loading state, error, and refetch function
4144
-
4145
- #### Defined in
4146
-
4147
- [packages/core/src/hooks/useEventLogo.ts:131](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/hooks/useEventLogo.ts#L131)
4148
-
4149
- ___
4150
-
4151
- ### clearEventLogoCache
4152
-
4153
- ▸ **clearEventLogoCache**(): `void`
4154
-
4155
- Clear all cached authenticated event logo data
4156
- Useful for testing or when you need to force refresh all data
4157
-
4158
- #### Returns
4159
-
4160
- `void`
4161
-
4162
- #### Defined in
4163
-
4164
- [packages/core/src/hooks/useEventLogo.ts:298](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/hooks/useEventLogo.ts#L298)
4165
-
4166
- ___
4167
-
4168
- ### getEventLogoCacheStats
4169
-
4170
- ▸ **getEventLogoCacheStats**(): `Object`
4171
-
4172
- Get cache statistics for debugging
4173
-
4174
- #### Returns
4175
-
4176
- `Object`
4177
-
4178
- | Name | Type |
4179
- | :------ | :------ |
4180
- | `size` | `number` |
4181
- | `keys` | `string`[] |
4182
-
4183
- #### Defined in
4184
-
4185
- [packages/core/src/hooks/useEventLogo.ts:309](https://github.com/jmruthers/pace-core/blob/main/packages/core/src/hooks/useEventLogo.ts#L309)
4186
-
4187
- ___
4188
-
4189
4041
  ### useEventTheme
4190
4042
 
4191
4043
  ▸ **useEventTheme**(): `void`
@@ -1,6 +1,6 @@
1
1
  ---
2
- lastUpdated: 2025-10-29T22:43:00+11:00
3
- version: 0.5.76
2
+ lastUpdated: 2025-11-01T14:58:00+11:00
3
+ version: 0.5.102
4
4
  reviewedBy: content-audit
5
5
  ---
6
6
 
@@ -30,7 +30,7 @@ CREATE TABLE file_references (
30
30
  table_name TEXT NOT NULL, -- Source table (e.g., 'pace_person')
31
31
  record_id TEXT NOT NULL, -- Source record ID
32
32
  file_path TEXT NOT NULL, -- Storage path
33
- file_metadata JSONB DEFAULT '{}', -- File metadata
33
+ file_metadata JSONB DEFAULT '{}', -- File metadata (includes category, fileName, fileType, etc.)
34
34
  organisation_id UUID NOT NULL, -- For RLS
35
35
  app_id UUID NOT NULL, -- Application context
36
36
  is_public BOOLEAN DEFAULT false, -- Public vs private
@@ -39,6 +39,8 @@ CREATE TABLE file_references (
39
39
  );
40
40
  ```
41
41
 
42
+ **Important:** The `category` is stored within the `file_metadata` JSONB field as `file_metadata->>'category'`, **not** as a direct column. Category filtering is performed via RPC functions (`data_file_reference_by_category_list`) that properly query the JSONB field. Direct queries filtering on a non-existent `category` column will fail.
43
+
42
44
  ### Storage Structure
43
45
 
44
46
  **Organisation-First Structure:**
@@ -397,25 +399,33 @@ fileReferences.forEach(fileRef => {
397
399
  });
398
400
  ```
399
401
 
400
- #### useEventLogo Hook
402
+ **Note:** Category filtering uses the RPC function `data_file_reference_by_category_list`, which filters on `file_metadata->>'category'` (the JSONB field), not a direct column. This hook automatically handles the filtering correctly.
401
403
 
402
- ```tsx
403
- import { useEventLogo } from '@jmruthers/pace-core';
404
+ #### Event Logo Display
404
405
 
405
- const { logoUrl, fallbackText, isLoading, error, refetch } = useEventLogo(
406
- supabase,
407
- eventId,
408
- eventName,
409
- organisationId,
410
- {
411
- validateImage: true,
412
- enableCache: true,
413
- cacheTtl: 30 * 60 * 1000 // 30 minutes
414
- }
415
- );
406
+ Event logos can be displayed using `FileDisplay` with category filtering:
416
407
 
417
- // Automatically handles both public and private event logos
418
- // Falls back to event initials if no logo is found
408
+ ```tsx
409
+ import { FileDisplay, FileCategory } from '@jmruthers/pace-core';
410
+
411
+ // Display event logo with fallback
412
+ <FileDisplay
413
+ table_name="event"
414
+ record_id={eventId}
415
+ organisation_id={organisationId}
416
+ category={FileCategory.EVENT_LOGOS}
417
+ displayOnly={true}
418
+ showFallback={true}
419
+ fallbackSize="lg"
420
+ generateFallbackText={(fileName) => {
421
+ if (!eventName) return 'EV';
422
+ return eventName
423
+ .split(/[\s\-_]+/)
424
+ .map(word => word.charAt(0).toUpperCase())
425
+ .join('')
426
+ .substring(0, 3);
427
+ }}
428
+ />
419
429
  ```
420
430
 
421
431
  #### useFileReferenceForRecord Hook
@@ -500,6 +510,8 @@ SELECT data_file_reference_get(
500
510
  );
501
511
 
502
512
  -- Get files by category
513
+ -- NOTE: Category filtering uses the file_metadata JSONB field (file_metadata->>'category'),
514
+ -- not a direct column. This RPC function properly filters on the JSONB field.
503
515
  SELECT data_file_reference_by_category_list(
504
516
  p_table_name := 'event',
505
517
  p_record_id := 'event-uuid',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmruthers/pace-core",
3
- "version": "0.5.102",
3
+ "version": "0.5.103",
4
4
  "description": "Clean, modern React component library with Tailwind v4 styling and native utilities",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -1147,15 +1147,19 @@ function DataTableInternal<TData extends DataRecord>({
1147
1147
 
1148
1148
  {/* Table header */}
1149
1149
  <thead>
1150
- {table?.getHeaderGroups().map((headerGroup) => (
1151
- <tr key={headerGroup.id}>
1152
- {headerGroup.headers
1153
- .filter(header => {
1154
- return typeof header.column.getIsVisible === 'function'
1155
- ? header.column.getIsVisible()
1156
- : true;
1157
- })
1158
- .map((header) => {
1150
+ {table?.getHeaderGroups().map((headerGroup) => {
1151
+ // Filter visible headers once to determine first and last
1152
+ const visibleHeaders = headerGroup.headers.filter(header => {
1153
+ return typeof header.column.getIsVisible === 'function'
1154
+ ? header.column.getIsVisible()
1155
+ : true;
1156
+ });
1157
+
1158
+ return (
1159
+ <tr key={headerGroup.id}>
1160
+ {visibleHeaders.map((header, index) => {
1161
+ const isFirst = index === 0;
1162
+ const isLast = index === visibleHeaders.length - 1;
1159
1163
  const isSortable = header.column.getCanSort();
1160
1164
  const ariaSort = isSortable
1161
1165
  ? (header.column.getIsSorted() === 'asc'
@@ -1205,7 +1209,12 @@ function DataTableInternal<TData extends DataRecord>({
1205
1209
  return (
1206
1210
  <th
1207
1211
  key={header.id}
1208
- className={`px-3 py-2 ${isRightAligned ? 'text-right' : 'text-left'}`}
1212
+ className={cn(
1213
+ 'px-3 py-2 bg-main-200',
1214
+ isRightAligned ? 'text-right' : 'text-left',
1215
+ isFirst && 'rounded-l-md',
1216
+ isLast && 'rounded-r-md'
1217
+ )}
1209
1218
  scope="col"
1210
1219
  role="columnheader"
1211
1220
  {...(isSortable ? { 'aria-sort': ariaSort } : {})}
@@ -1215,7 +1224,7 @@ function DataTableInternal<TData extends DataRecord>({
1215
1224
  isSortable ? (
1216
1225
  <Button
1217
1226
  variant="ghost"
1218
- className={`h-auto p-0 font-medium hover:bg-transparent ${isRightAligned ? 'justify-end' : 'justify-start'}`}
1227
+ className={`h-auto p-0 font-bold hover:bg-transparent ${isRightAligned ? 'justify-end' : 'justify-start'}`}
1219
1228
  onClick={handleSortClick}
1220
1229
  {...headerKeyboardHandlers}
1221
1230
  aria-label={`Sort by ${typeof header.column.columnDef.header === 'string' ? header.column.columnDef.header : 'column'}`}
@@ -1241,8 +1250,9 @@ function DataTableInternal<TData extends DataRecord>({
1241
1250
  </th>
1242
1251
  );
1243
1252
  })}
1244
- </tr>
1245
- ))}
1253
+ </tr>
1254
+ );
1255
+ })}
1246
1256
  </thead>
1247
1257
 
1248
1258
  {/* Table body */}
@@ -50,12 +50,42 @@ export function useTableColumns<TData extends DataRecord>({
50
50
 
51
51
  const enhancedColumns = useMemo(() => {
52
52
  // Create enhanced base columns
53
- const baseColumns: ColumnDef<TData>[] = [...columns].map(column => ({
54
- ...column,
55
- enableSorting: features.sorting && (column.enableSorting !== false),
56
- enableColumnFilter: features.filtering && (column.enableColumnFilter !== false),
57
- enableGrouping: features.grouping && (column.enableGrouping !== false),
58
- }));
53
+ const baseColumns: ColumnDef<TData>[] = [...columns].map(column => {
54
+ const baseColumn = {
55
+ ...column,
56
+ enableSorting: features.sorting && (column.enableSorting !== false),
57
+ enableColumnFilter: features.filtering && (column.enableColumnFilter !== false),
58
+ enableGrouping: features.grouping && (column.enableGrouping !== false),
59
+ };
60
+
61
+ // Automatically set right alignment for numeric columns
62
+ // Check if column is numeric by:
63
+ // 1. meta.type === 'number'
64
+ // 2. fieldType === 'number'
65
+ // 3. filterType === 'number'
66
+ // Exclude dates (fieldType === 'date' or meta.type === 'date')
67
+ const dataTableColumn = column as DataTableColumn<TData>;
68
+ const isNumericColumn =
69
+ column.meta?.type === 'number' ||
70
+ dataTableColumn.fieldType === 'number' ||
71
+ dataTableColumn.filterType === 'number';
72
+
73
+ const isDateColumn =
74
+ column.meta?.type === 'date' ||
75
+ dataTableColumn.fieldType === 'date' ||
76
+ dataTableColumn.filterType === 'date';
77
+
78
+ // Set right alignment if numeric and not a date
79
+ if (isNumericColumn && !isDateColumn) {
80
+ baseColumn.meta = {
81
+ ...baseColumn.meta,
82
+ align: 'right',
83
+ type: 'number',
84
+ };
85
+ }
86
+
87
+ return baseColumn;
88
+ });
59
89
 
60
90
  // Create selection column if enabled
61
91
  const selectionColumn: ColumnDef<TData> | null = features.selection ? {
@@ -17,7 +17,7 @@
17
17
  *
18
18
  * @example
19
19
  * ```tsx
20
- * import { PublicPageHeader, EventLogo } from '@jmruthers/pace-core';
20
+ * import { PublicPageHeader, FileDisplay } from '@jmruthers/pace-core';
21
21
  *
22
22
  * function PublicEventPage() {
23
23
  * return (
@@ -260,8 +260,6 @@ export type {
260
260
  UseFileReferenceReturn,
261
261
  UseFileReferenceForRecordReturn
262
262
  } from '../hooks/useFileReference';
263
- export { useEventLogo, clearEventLogoCache, getEventLogoCacheStats } from '../hooks/useEventLogo';
264
- export type { UseEventLogoReturn, UseEventLogoOptions } from '../hooks/useEventLogo';
265
263
 
266
264
  export { useToast } from '../hooks/useToast';
267
265
 
@@ -10,15 +10,14 @@
10
10
  *
11
11
  * @example
12
12
  * // Import individual hooks
13
- * import { usePublicEvent, usePublicEventLogo } from '@jmruthers/pace-core/hooks/public';
13
+ * import { usePublicEvent } from '@jmruthers/pace-core/hooks/public';
14
14
  *
15
15
  * // Or import from main package
16
- * import { usePublicEvent, usePublicEventLogo } from '@jmruthers/pace-core';
16
+ * import { usePublicEvent } from '@jmruthers/pace-core';
17
17
  */
18
18
 
19
19
  // === PUBLIC DATA ACCESS HOOKS ===
20
20
  export { usePublicEvent, clearPublicEventCache, getPublicEventCacheStats } from './usePublicEvent';
21
- export { usePublicEventLogo, clearPublicLogoCache, getPublicLogoCacheStats } from './usePublicEventLogo';
22
21
  export { usePublicFileDisplay, clearPublicFileDisplayCache, getPublicFileDisplayCacheStats } from './usePublicFileDisplay';
23
22
 
24
23
  // === PUBLIC ROUTE HOOKS ===
@@ -31,6 +30,5 @@ export {
31
30
 
32
31
  // === PUBLIC TYPES ===
33
32
  export type { UsePublicEventReturn, UsePublicEventOptions } from './usePublicEvent';
34
- export type { UsePublicEventLogoReturn, UsePublicEventLogoOptions } from './usePublicEventLogo';
35
33
  export type { UsePublicFileDisplayReturn, UsePublicFileDisplayOptions } from './usePublicFileDisplay';
36
34
  export type { UsePublicRouteParamsReturn } from './usePublicRouteParams';
package/src/index.ts CHANGED
@@ -224,8 +224,6 @@ export type {
224
224
  UseFileReferenceReturn,
225
225
  UseFileReferenceForRecordReturn
226
226
  } from './hooks/useFileReference';
227
- export { useEventLogo, clearEventLogoCache, getEventLogoCacheStats } from './hooks/useEventLogo';
228
- export type { UseEventLogoReturn, UseEventLogoOptions } from './hooks/useEventLogo';
229
227
  export * from './utils/storage';
230
228
 
231
229
  // Table components
@@ -342,6 +342,9 @@ export class FileReferenceServiceImpl implements FileReferenceService {
342
342
  organisation_id: string
343
343
  ): Promise<FileReference[]> {
344
344
  try {
345
+ // Use RPC function to get files by category - this correctly filters on file_metadata->>'category'
346
+ // NOTE: We do NOT filter directly on a 'category' column because it doesn't exist.
347
+ // The category is stored in the JSONB file_metadata field.
345
348
  const { data, error } = await this.supabase
346
349
  .rpc('data_file_reference_by_category_list', {
347
350
  p_table_name: table_name,
@@ -351,7 +354,16 @@ export class FileReferenceServiceImpl implements FileReferenceService {
351
354
  });
352
355
 
353
356
  if (error) {
354
- throw new Error(`Failed to get files by category: ${error.message}`);
357
+ // Provide clear error message about category filtering
358
+ console.error('[FileReferenceService] RPC error getting files by category:', {
359
+ error,
360
+ table_name,
361
+ record_id,
362
+ category,
363
+ organisation_id,
364
+ message: 'Category filtering is done via RPC function data_file_reference_by_category_list which filters on file_metadata->>\'category\''
365
+ });
366
+ throw new Error(`Failed to get files by category: ${error.message}. Category filtering uses file_metadata JSONB field, not a direct column.`);
355
367
  }
356
368
 
357
369
  // RPC returns partial data, need to fetch full file references
@@ -360,6 +372,7 @@ export class FileReferenceServiceImpl implements FileReferenceService {
360
372
  }
361
373
 
362
374
  // Fetch full file reference data for each ID
375
+ // Note: This query does NOT filter by category - the RPC already did that filtering
363
376
  const ids = data.map((item: any) => item.id);
364
377
  const { data: fullData, error: fetchError } = await this.supabase
365
378
  .from('file_references')
@@ -370,7 +383,22 @@ export class FileReferenceServiceImpl implements FileReferenceService {
370
383
  throw new Error(`Failed to fetch file references: ${fetchError.message}`);
371
384
  }
372
385
 
373
- return (fullData || []) as FileReference[];
386
+ // Verify that all returned files match the category (defensive check)
387
+ const filteredFiles = (fullData || []).filter((file: any) => {
388
+ const fileCategory = file.file_metadata?.category;
389
+ return fileCategory === category;
390
+ });
391
+
392
+ if (filteredFiles.length !== fullData.length) {
393
+ console.warn('[FileReferenceService] RPC returned files with mismatched categories. Filtering client-side.', {
394
+ expected: category,
395
+ returned: fullData.map((f: any) => f.file_metadata?.category),
396
+ filtered: filteredFiles.length,
397
+ total: fullData.length
398
+ });
399
+ }
400
+
401
+ return filteredFiles as FileReference[];
374
402
  } catch (error) {
375
403
  console.error('Error getting files by category:', error);
376
404
  throw error;
@@ -148,29 +148,31 @@ const { fileReferences, fileUrls, isLoading, error } = useFilesByCategory(
148
148
  );
149
149
  ```
150
150
 
151
- ### useEventLogo
151
+ ### FileDisplay Component
152
152
 
153
- Fetch event logos for both public and authenticated contexts with automatic fallback.
153
+ Display event logos and other files with automatic fallback support.
154
154
 
155
155
  ```typescript
156
- const { logoUrl, fallbackText, isLoading, error, refetch } = useEventLogo(
157
- supabase,
158
- eventId,
159
- eventName,
160
- organisationId,
161
- {
162
- validateImage: true,
163
- enableCache: true,
164
- cacheTtl: 30 * 60 * 1000 // 30 minutes
165
- }
166
- );
156
+ import { FileDisplay, FileCategory } from '@jmruthers/pace-core';
167
157
 
168
- // Display logo with fallback
169
- {logoUrl ? (
170
- <img src={logoUrl} alt={`${eventName} logo`} />
171
- ) : (
172
- <div className="logo-fallback">{fallbackText}</div>
173
- )}
158
+ // Display event logo with fallback
159
+ <FileDisplay
160
+ table_name="event"
161
+ record_id={eventId}
162
+ organisation_id={organisationId}
163
+ category={FileCategory.EVENT_LOGOS}
164
+ displayOnly={true}
165
+ showFallback={true}
166
+ fallbackSize="lg"
167
+ generateFallbackText={(fileName) => {
168
+ if (!eventName) return 'EV';
169
+ return eventName
170
+ .split(/[\s\-_]+/)
171
+ .map(word => word.charAt(0).toUpperCase())
172
+ .join('')
173
+ .substring(0, 3);
174
+ }}
175
+ />
174
176
  ```
175
177
 
176
178
  ## File Categories
@@ -331,7 +333,7 @@ await uploadFile({
331
333
  6. **Memory leak / "Maximum update depth exceeded"**
332
334
  - Ensure proper cleanup in useEffect hooks
333
335
  - Check for infinite re-render loops in components
334
- - Use `clearEventLogoCache()` when unmounting components
336
+ - Use `clearFileDisplayCache()` when unmounting components
335
337
 
336
338
  ### Debug Mode
337
339