@jmruthers/pace-core 0.5.187 → 0.5.189

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 (209) hide show
  1. package/dist/{DataTable-K3RJRSOX.js → DataTable-GUFUNZ3N.js} +5 -5
  2. package/dist/{PublicPageProvider-DrLDztHt.d.ts → PublicPageProvider-B8HaLe69.d.ts} +47 -17
  3. package/dist/{UnifiedAuthProvider-B76OWOAT.js → UnifiedAuthProvider-643PUAIM.js} +2 -2
  4. package/dist/{chunk-FMTK4XNN.js → chunk-2UUZZJFT.js} +3 -3
  5. package/dist/{chunk-3IC5WCMO.js → chunk-3GOZZZYH.js} +3 -3
  6. package/dist/{chunk-ULX5FYEM.js → chunk-DDM4CCYT.js} +3 -3
  7. package/dist/{chunk-K2JGDXGU.js → chunk-E7UAOUMY.js} +2 -2
  8. package/dist/{chunk-T6ZJVI3A.js → chunk-IM4QE42D.js} +4 -4
  9. package/dist/{chunk-3NFNJOO7.js → chunk-MX64ZF6I.js} +4 -4
  10. package/dist/{chunk-C4OYJOV4.js → chunk-UCQSRW7Z.js} +829 -829
  11. package/dist/chunk-UCQSRW7Z.js.map +1 -0
  12. package/dist/{chunk-WK2Y6TGA.js → chunk-VGZZXKBR.js} +3 -3
  13. package/dist/chunk-VGZZXKBR.js.map +1 -0
  14. package/dist/{chunk-LBBUPSSC.js → chunk-YGPFYGA6.js} +3760 -3692
  15. package/dist/chunk-YGPFYGA6.js.map +1 -0
  16. package/dist/components.d.ts +1 -2
  17. package/dist/components.js +6 -10
  18. package/dist/components.js.map +1 -1
  19. package/dist/hooks.js +5 -5
  20. package/dist/index.d.ts +1 -2
  21. package/dist/index.js +9 -13
  22. package/dist/index.js.map +1 -1
  23. package/dist/providers.js +1 -1
  24. package/dist/rbac/index.js +5 -5
  25. package/dist/utils.js +1 -1
  26. package/docs/api/classes/ColumnFactory.md +1 -1
  27. package/docs/api/classes/ErrorBoundary.md +1 -1
  28. package/docs/api/classes/InvalidScopeError.md +1 -1
  29. package/docs/api/classes/Logger.md +1 -1
  30. package/docs/api/classes/MissingUserContextError.md +1 -1
  31. package/docs/api/classes/OrganisationContextRequiredError.md +1 -1
  32. package/docs/api/classes/PermissionDeniedError.md +1 -1
  33. package/docs/api/classes/RBACAuditManager.md +1 -1
  34. package/docs/api/classes/RBACCache.md +1 -1
  35. package/docs/api/classes/RBACEngine.md +1 -1
  36. package/docs/api/classes/RBACError.md +1 -1
  37. package/docs/api/classes/RBACNotInitializedError.md +1 -1
  38. package/docs/api/classes/SecureSupabaseClient.md +1 -1
  39. package/docs/api/classes/StorageUtils.md +1 -1
  40. package/docs/api/enums/FileCategory.md +1 -1
  41. package/docs/api/enums/LogLevel.md +1 -1
  42. package/docs/api/enums/RBACErrorCode.md +1 -1
  43. package/docs/api/enums/RPCFunction.md +1 -1
  44. package/docs/api/interfaces/AddressFieldProps.md +1 -1
  45. package/docs/api/interfaces/AddressFieldRef.md +1 -1
  46. package/docs/api/interfaces/AggregateConfig.md +1 -1
  47. package/docs/api/interfaces/AutocompleteOptions.md +1 -1
  48. package/docs/api/interfaces/AvatarProps.md +128 -0
  49. package/docs/api/interfaces/BadgeProps.md +1 -1
  50. package/docs/api/interfaces/ButtonProps.md +1 -1
  51. package/docs/api/interfaces/CalendarProps.md +1 -1
  52. package/docs/api/interfaces/CardProps.md +1 -1
  53. package/docs/api/interfaces/ColorPalette.md +1 -1
  54. package/docs/api/interfaces/ColorShade.md +1 -1
  55. package/docs/api/interfaces/ComplianceResult.md +1 -1
  56. package/docs/api/interfaces/DataAccessRecord.md +1 -1
  57. package/docs/api/interfaces/DataRecord.md +1 -1
  58. package/docs/api/interfaces/DataTableAction.md +1 -1
  59. package/docs/api/interfaces/DataTableColumn.md +1 -1
  60. package/docs/api/interfaces/DataTableProps.md +1 -1
  61. package/docs/api/interfaces/DataTableToolbarButton.md +1 -1
  62. package/docs/api/interfaces/DatabaseComplianceResult.md +1 -1
  63. package/docs/api/interfaces/DatabaseIssue.md +1 -1
  64. package/docs/api/interfaces/EmptyStateConfig.md +1 -1
  65. package/docs/api/interfaces/EnhancedNavigationMenuProps.md +1 -1
  66. package/docs/api/interfaces/EventAppRoleData.md +1 -1
  67. package/docs/api/interfaces/ExportColumn.md +1 -1
  68. package/docs/api/interfaces/ExportOptions.md +1 -1
  69. package/docs/api/interfaces/FileDisplayProps.md +1 -1
  70. package/docs/api/interfaces/FileMetadata.md +1 -1
  71. package/docs/api/interfaces/FileReference.md +1 -1
  72. package/docs/api/interfaces/FileSizeLimits.md +1 -1
  73. package/docs/api/interfaces/FileUploadOptions.md +1 -1
  74. package/docs/api/interfaces/FileUploadProps.md +1 -1
  75. package/docs/api/interfaces/FooterProps.md +1 -1
  76. package/docs/api/interfaces/FormFieldProps.md +1 -1
  77. package/docs/api/interfaces/FormProps.md +1 -1
  78. package/docs/api/interfaces/GrantEventAppRoleParams.md +1 -1
  79. package/docs/api/interfaces/InactivityWarningModalProps.md +1 -1
  80. package/docs/api/interfaces/InputProps.md +1 -1
  81. package/docs/api/interfaces/LabelProps.md +1 -1
  82. package/docs/api/interfaces/LoggerConfig.md +1 -1
  83. package/docs/api/interfaces/LoginFormProps.md +1 -1
  84. package/docs/api/interfaces/NavigationAccessRecord.md +1 -1
  85. package/docs/api/interfaces/NavigationContextType.md +1 -1
  86. package/docs/api/interfaces/NavigationGuardProps.md +1 -1
  87. package/docs/api/interfaces/NavigationItem.md +1 -1
  88. package/docs/api/interfaces/NavigationMenuProps.md +1 -1
  89. package/docs/api/interfaces/NavigationProviderProps.md +1 -1
  90. package/docs/api/interfaces/Organisation.md +1 -1
  91. package/docs/api/interfaces/OrganisationContextType.md +1 -1
  92. package/docs/api/interfaces/OrganisationMembership.md +1 -1
  93. package/docs/api/interfaces/OrganisationProviderProps.md +1 -1
  94. package/docs/api/interfaces/OrganisationSecurityError.md +1 -1
  95. package/docs/api/interfaces/PaceAppLayoutProps.md +1 -1
  96. package/docs/api/interfaces/PaceLoginPageProps.md +1 -1
  97. package/docs/api/interfaces/PageAccessRecord.md +1 -1
  98. package/docs/api/interfaces/PagePermissionContextType.md +1 -1
  99. package/docs/api/interfaces/PagePermissionGuardProps.md +1 -1
  100. package/docs/api/interfaces/PagePermissionProviderProps.md +1 -1
  101. package/docs/api/interfaces/PaletteData.md +1 -1
  102. package/docs/api/interfaces/ParsedAddress.md +1 -1
  103. package/docs/api/interfaces/PermissionEnforcerProps.md +1 -1
  104. package/docs/api/interfaces/ProgressProps.md +1 -1
  105. package/docs/api/interfaces/ProtectedRouteProps.md +1 -1
  106. package/docs/api/interfaces/PublicPageFooterProps.md +1 -1
  107. package/docs/api/interfaces/PublicPageHeaderProps.md +1 -1
  108. package/docs/api/interfaces/PublicPageLayoutProps.md +1 -1
  109. package/docs/api/interfaces/QuickFix.md +1 -1
  110. package/docs/api/interfaces/RBACAccessValidateParams.md +1 -1
  111. package/docs/api/interfaces/RBACAccessValidateResult.md +1 -1
  112. package/docs/api/interfaces/RBACAuditLogParams.md +1 -1
  113. package/docs/api/interfaces/RBACAuditLogResult.md +1 -1
  114. package/docs/api/interfaces/RBACConfig.md +1 -1
  115. package/docs/api/interfaces/RBACContext.md +1 -1
  116. package/docs/api/interfaces/RBACLogger.md +1 -1
  117. package/docs/api/interfaces/RBACPageAccessCheckParams.md +1 -1
  118. package/docs/api/interfaces/RBACPerformanceMetrics.md +1 -1
  119. package/docs/api/interfaces/RBACPermissionCheckParams.md +1 -1
  120. package/docs/api/interfaces/RBACPermissionCheckResult.md +1 -1
  121. package/docs/api/interfaces/RBACPermissionsGetParams.md +1 -1
  122. package/docs/api/interfaces/RBACPermissionsGetResult.md +1 -1
  123. package/docs/api/interfaces/RBACResult.md +1 -1
  124. package/docs/api/interfaces/RBACRoleGrantParams.md +1 -1
  125. package/docs/api/interfaces/RBACRoleGrantResult.md +1 -1
  126. package/docs/api/interfaces/RBACRoleRevokeParams.md +1 -1
  127. package/docs/api/interfaces/RBACRoleRevokeResult.md +1 -1
  128. package/docs/api/interfaces/RBACRoleValidateParams.md +1 -1
  129. package/docs/api/interfaces/RBACRoleValidateResult.md +1 -1
  130. package/docs/api/interfaces/RBACRolesListParams.md +1 -1
  131. package/docs/api/interfaces/RBACRolesListResult.md +1 -1
  132. package/docs/api/interfaces/RBACSessionTrackParams.md +1 -1
  133. package/docs/api/interfaces/RBACSessionTrackResult.md +1 -1
  134. package/docs/api/interfaces/ResourcePermissions.md +1 -1
  135. package/docs/api/interfaces/RevokeEventAppRoleParams.md +1 -1
  136. package/docs/api/interfaces/RoleBasedRouterContextType.md +1 -1
  137. package/docs/api/interfaces/RoleBasedRouterProps.md +1 -1
  138. package/docs/api/interfaces/RoleManagementResult.md +1 -1
  139. package/docs/api/interfaces/RouteAccessRecord.md +1 -1
  140. package/docs/api/interfaces/RouteConfig.md +1 -1
  141. package/docs/api/interfaces/RuntimeComplianceResult.md +1 -1
  142. package/docs/api/interfaces/SecureDataContextType.md +1 -1
  143. package/docs/api/interfaces/SecureDataProviderProps.md +1 -1
  144. package/docs/api/interfaces/SessionRestorationLoaderProps.md +1 -1
  145. package/docs/api/interfaces/SetupIssue.md +1 -1
  146. package/docs/api/interfaces/StorageConfig.md +1 -1
  147. package/docs/api/interfaces/StorageFileInfo.md +1 -1
  148. package/docs/api/interfaces/StorageFileMetadata.md +1 -1
  149. package/docs/api/interfaces/StorageListOptions.md +1 -1
  150. package/docs/api/interfaces/StorageListResult.md +1 -1
  151. package/docs/api/interfaces/StorageUploadOptions.md +1 -1
  152. package/docs/api/interfaces/StorageUploadResult.md +1 -1
  153. package/docs/api/interfaces/StorageUrlOptions.md +1 -1
  154. package/docs/api/interfaces/StyleImport.md +1 -1
  155. package/docs/api/interfaces/SwitchProps.md +1 -1
  156. package/docs/api/interfaces/TabsContentProps.md +1 -1
  157. package/docs/api/interfaces/TabsListProps.md +1 -1
  158. package/docs/api/interfaces/TabsProps.md +1 -1
  159. package/docs/api/interfaces/TabsTriggerProps.md +1 -1
  160. package/docs/api/interfaces/TextareaProps.md +1 -1
  161. package/docs/api/interfaces/ToastActionElement.md +1 -1
  162. package/docs/api/interfaces/ToastProps.md +1 -1
  163. package/docs/api/interfaces/UnifiedAuthContextType.md +1 -1
  164. package/docs/api/interfaces/UnifiedAuthProviderProps.md +1 -1
  165. package/docs/api/interfaces/UseFormDialogOptions.md +1 -1
  166. package/docs/api/interfaces/UseFormDialogReturn.md +1 -1
  167. package/docs/api/interfaces/UseInactivityTrackerOptions.md +1 -1
  168. package/docs/api/interfaces/UseInactivityTrackerReturn.md +1 -1
  169. package/docs/api/interfaces/UsePublicEventLogoOptions.md +1 -1
  170. package/docs/api/interfaces/UsePublicEventLogoReturn.md +1 -1
  171. package/docs/api/interfaces/UsePublicEventOptions.md +1 -1
  172. package/docs/api/interfaces/UsePublicEventReturn.md +1 -1
  173. package/docs/api/interfaces/UsePublicFileDisplayOptions.md +1 -1
  174. package/docs/api/interfaces/UsePublicFileDisplayReturn.md +1 -1
  175. package/docs/api/interfaces/UsePublicRouteParamsReturn.md +1 -1
  176. package/docs/api/interfaces/UseResolvedScopeOptions.md +1 -1
  177. package/docs/api/interfaces/UseResolvedScopeReturn.md +1 -1
  178. package/docs/api/interfaces/UseResourcePermissionsOptions.md +1 -1
  179. package/docs/api/interfaces/UserEventAccess.md +1 -1
  180. package/docs/api/interfaces/UserMenuProps.md +1 -1
  181. package/docs/api/interfaces/UserProfile.md +1 -1
  182. package/docs/api/modules.md +6 -45
  183. package/docs/api-reference/components.md +57 -22
  184. package/docs/getting-started/examples/README.md +2 -2
  185. package/docs/implementation-guides/public-pages.md +140 -1230
  186. package/docs/standards/05-security-standard.md +3 -1
  187. package/docs/standards/07-rbac-and-rls-standard.md +356 -0
  188. package/package.json +1 -2
  189. package/src/__tests__/public-recipe-view.test.ts +199 -0
  190. package/src/__tests__/rls-policies.test.ts +333 -0
  191. package/src/components/Avatar/Avatar.test.tsx +183 -209
  192. package/src/components/Avatar/Avatar.tsx +179 -53
  193. package/src/components/Avatar/index.ts +1 -1
  194. package/src/components/UserMenu/UserMenu.test.tsx +7 -9
  195. package/src/components/UserMenu/UserMenu.tsx +7 -5
  196. package/src/components/index.ts +2 -1
  197. package/src/index.ts +2 -1
  198. package/src/services/OrganisationService.ts +5 -4
  199. package/dist/chunk-C4OYJOV4.js.map +0 -1
  200. package/dist/chunk-LBBUPSSC.js.map +0 -1
  201. package/dist/chunk-WK2Y6TGA.js.map +0 -1
  202. /package/dist/{DataTable-K3RJRSOX.js.map → DataTable-GUFUNZ3N.js.map} +0 -0
  203. /package/dist/{UnifiedAuthProvider-B76OWOAT.js.map → UnifiedAuthProvider-643PUAIM.js.map} +0 -0
  204. /package/dist/{chunk-FMTK4XNN.js.map → chunk-2UUZZJFT.js.map} +0 -0
  205. /package/dist/{chunk-3IC5WCMO.js.map → chunk-3GOZZZYH.js.map} +0 -0
  206. /package/dist/{chunk-ULX5FYEM.js.map → chunk-DDM4CCYT.js.map} +0 -0
  207. /package/dist/{chunk-K2JGDXGU.js.map → chunk-E7UAOUMY.js.map} +0 -0
  208. /package/dist/{chunk-T6ZJVI3A.js.map → chunk-IM4QE42D.js.map} +0 -0
  209. /package/dist/{chunk-3NFNJOO7.js.map → chunk-MX64ZF6I.js.map} +0 -0
@@ -1,11 +1,14 @@
1
1
  /**
2
- * @file Avatar Component Suite
2
+ * @file Avatar Component
3
3
  * @package @jmruthers/pace-core
4
4
  * @module Components
5
5
  * @since 0.1.0
6
6
  *
7
- * A flexible and accessible avatar component for displaying user profile images or initials.
8
- * Includes Avatar, AvatarImage, and AvatarFallback subcomponents.
7
+ * A simple and accessible avatar component for displaying user profile images or initials.
8
+ * Supports three approaches for image display:
9
+ * - File reference props (uses FileDisplay for Supabase storage)
10
+ * - File ID (uses file reference lookup)
11
+ * - Direct URL (simple img tag for public images)
9
12
  *
10
13
  * Features:
11
14
  * - Circular avatar display
@@ -16,16 +19,31 @@
16
19
  *
17
20
  * @example
18
21
  * ```tsx
19
- * // Basic avatar with image
20
- * <Avatar>
21
- * <AvatarImage src="/user.jpg" alt="User" />
22
- * <AvatarFallback>AB</AvatarFallback>
23
- * </Avatar>
22
+ * // Avatar with direct URL
23
+ * <Avatar
24
+ * src="/user.jpg"
25
+ * alt="User"
26
+ * fallback="AB"
27
+ * />
28
+ *
29
+ * // Avatar with file reference props
30
+ * <Avatar
31
+ * table_name="user_profiles"
32
+ * record_id={userId}
33
+ * organisation_id={orgId}
34
+ * category={FileCategory.PROFILE_PHOTOS}
35
+ * fallback="JD"
36
+ * />
37
+ *
38
+ * // Avatar with file ID
39
+ * <Avatar
40
+ * fileId={fileReferenceId}
41
+ * organisation_id={orgId}
42
+ * fallback="JD"
43
+ * />
24
44
  *
25
45
  * // Avatar with fallback only
26
- * <Avatar>
27
- * <AvatarFallback>JD</AvatarFallback>
28
- * </Avatar>
46
+ * <Avatar fallback="JD" />
29
47
  * ```
30
48
  *
31
49
  * @accessibility
@@ -35,50 +53,158 @@
35
53
  */
36
54
 
37
55
  import * as React from "react"
38
- import * as AvatarPrimitive from "@radix-ui/react-avatar"
39
-
56
+ import { FileCategory } from "../../types/file-reference"
57
+ import { FileDisplay } from "../FileDisplay/FileDisplay"
58
+ import { useFileReferenceById } from "../../hooks/useFileReference"
59
+ import { useUnifiedAuth } from "../../providers/services/UnifiedAuthProvider"
40
60
  import { cn } from "../../utils/core/cn"
41
61
 
42
- const Avatar = React.forwardRef<
43
- React.ElementRef<typeof AvatarPrimitive.Root>,
44
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
45
- >(({ className, ...props }, ref) => (
46
- <AvatarPrimitive.Root
47
- ref={ref}
48
- className={cn(
49
- "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full",
50
- className
51
- )}
52
- {...props}
53
- />
54
- ))
55
- Avatar.displayName = AvatarPrimitive.Root.displayName
62
+ /**
63
+ * Size variants for avatar
64
+ */
65
+ const sizeClasses = {
66
+ xs: 'h-4 w-4 text-xs',
67
+ sm: 'h-6 w-6 text-sm',
68
+ md: 'h-10 w-10 text-base',
69
+ lg: 'h-12 w-12 text-lg',
70
+ xl: 'h-16 w-16 text-xl',
71
+ '2xl': 'h-20 w-20 text-2xl'
72
+ }
73
+
74
+ interface AvatarProps extends React.HTMLAttributes<HTMLDivElement> {
75
+ // File reference approach (uses FileDisplay)
76
+ table_name?: string
77
+ record_id?: string
78
+ organisation_id?: string
79
+ category?: FileCategory
80
+
81
+ // OR file ID approach (uses file reference lookup)
82
+ fileId?: string
83
+
84
+ // OR direct URL approach (simple <img> tag)
85
+ src?: string
86
+ alt?: string
87
+
88
+ // Fallback (always required)
89
+ fallback: string
90
+
91
+ // Styling
92
+ className?: string
93
+ size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl'
94
+ }
56
95
 
57
- const AvatarImage = React.forwardRef<
58
- React.ElementRef<typeof AvatarPrimitive.Image>,
59
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
60
- >(({ className, ...props }, ref) => (
61
- <AvatarPrimitive.Image
62
- ref={ref}
63
- className={cn("object-cover", className)}
64
- {...props}
65
- />
66
- ))
67
- AvatarImage.displayName = AvatarPrimitive.Image.displayName
96
+ /**
97
+ * Avatar component for displaying user profile images or initials
98
+ */
99
+ const Avatar = React.forwardRef<HTMLDivElement, AvatarProps>(
100
+ ({
101
+ table_name,
102
+ record_id,
103
+ organisation_id,
104
+ category,
105
+ fileId,
106
+ src,
107
+ alt,
108
+ fallback,
109
+ className,
110
+ size = 'md',
111
+ ...props
112
+ }, ref) => {
113
+ const [imageError, setImageError] = React.useState(false)
114
+ const { supabase } = useUnifiedAuth()
115
+
116
+ // For fileId approach, get file reference and URL
117
+ // Only fetch if we have all required values
118
+ const canFetchFileId = Boolean(fileId && organisation_id && supabase)
119
+ const { fileReference, fileUrl: fileIdUrl, isLoading: fileIdLoading } = useFileReferenceById(
120
+ supabase || ({} as any), // Hook requires SupabaseClient, but we check canFetchFileId before using result
121
+ canFetchFileId ? fileId! : null,
122
+ canFetchFileId ? organisation_id! : null
123
+ )
124
+
125
+ // Determine which approach to use
126
+ const hasFileProps = table_name && record_id && organisation_id && category && supabase
127
+ const hasFileId = canFetchFileId && fileIdUrl && !fileIdLoading
128
+ const hasDirectUrl = src && !imageError
129
+
130
+ // Determine if we should show fallback
131
+ const showFallback = !hasDirectUrl && (!hasFileProps && !hasFileId) || imageError || (canFetchFileId && !fileIdUrl && !fileIdLoading)
132
+
133
+ // Base classes for avatar container
134
+ const baseClasses = size === 'md'
135
+ ? "relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full"
136
+ : `relative flex ${sizeClasses[size]} shrink-0 overflow-hidden rounded-full`
137
+
138
+ // Fallback classes (don't include className to avoid conflicts)
139
+ const fallbackClasses = "flex h-full w-full items-center justify-center rounded-full text-sec-50 bg-sec-500"
140
+
141
+ // Image classes for direct URL and fileId approaches
142
+ const imageClasses = "object-cover h-full w-full"
143
+
144
+ // Container classes (include className here for container styling)
145
+ const containerClasses = cn(baseClasses, className)
146
+
147
+ // Handle image error for direct URL
148
+ const handleImageError = React.useCallback(() => {
149
+ setImageError(true)
150
+ }, [])
151
+
152
+ // Reset error when src changes
153
+ React.useEffect(() => {
154
+ if (src) {
155
+ setImageError(false)
156
+ }
157
+ }, [src])
158
+
159
+ return (
160
+ <div
161
+ ref={ref}
162
+ className={containerClasses}
163
+ {...props}
164
+ >
165
+ {showFallback ? (
166
+ <div className={fallbackClasses} aria-label={alt || fallback}>
167
+ {fallback}
168
+ </div>
169
+ ) : hasFileProps ? (
170
+ // File reference props approach - use FileDisplay
171
+ <FileDisplay
172
+ table_name={table_name!}
173
+ record_id={record_id!}
174
+ organisation_id={organisation_id!}
175
+ category={category!}
176
+ displayOnly={true}
177
+ showFallback={true}
178
+ fallbackText={fallback}
179
+ className={cn(imageClasses, className)}
180
+ />
181
+ ) : hasFileId && fileIdUrl ? (
182
+ // File ID approach - use img tag with fetched URL
183
+ <img
184
+ src={fileIdUrl}
185
+ alt={alt || fallback}
186
+ className={imageClasses}
187
+ onError={handleImageError}
188
+ />
189
+ ) : hasDirectUrl ? (
190
+ // Direct URL approach - use img tag
191
+ <img
192
+ src={src}
193
+ alt={alt || fallback}
194
+ className={imageClasses}
195
+ onError={handleImageError}
196
+ />
197
+ ) : (
198
+ // Fallback if nothing else works
199
+ <div className={fallbackClasses} aria-label={alt || fallback}>
200
+ {fallback}
201
+ </div>
202
+ )}
203
+ </div>
204
+ )
205
+ }
206
+ )
68
207
 
69
- const AvatarFallback = React.forwardRef<
70
- React.ElementRef<typeof AvatarPrimitive.Fallback>,
71
- React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
72
- >(({ className, ...props }, ref) => (
73
- <AvatarPrimitive.Fallback
74
- ref={ref}
75
- className={cn(
76
- "flex h-full w-full items-center justify-center rounded-full text-sec-50 bg-sec-500",
77
- className
78
- )}
79
- {...props}
80
- />
81
- ))
82
- AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName
208
+ Avatar.displayName = "Avatar"
83
209
 
84
- export { Avatar, AvatarImage, AvatarFallback }
210
+ export { Avatar, type AvatarProps }
@@ -1,2 +1,2 @@
1
1
 
2
- export { Avatar, AvatarImage, AvatarFallback } from './Avatar';
2
+ export { Avatar, type AvatarProps } from './Avatar';
@@ -117,19 +117,17 @@ vi.mock('../Dialog', () => ({
117
117
  DialogOverlay: () => <div data-testid="dialog-overlay" />,
118
118
  }));
119
119
 
120
- // Mock the Avatar components
120
+ // Mock the Avatar component
121
121
  vi.mock('../Avatar', () => ({
122
- Avatar: ({ children, className }: { children: React.ReactNode; className?: string }) => (
122
+ Avatar: ({ src, alt, fallback, className }: { src?: string; alt?: string; fallback: string; className?: string }) => (
123
123
  <div data-testid="avatar" className={className}>
124
- {children}
124
+ {src ? (
125
+ <img data-testid="avatar-image" src={src} alt={alt || fallback} />
126
+ ) : (
127
+ <div data-testid="avatar-fallback">{fallback}</div>
128
+ )}
125
129
  </div>
126
130
  ),
127
- AvatarFallback: ({ children }: { children: React.ReactNode }) => (
128
- <div data-testid="avatar-fallback">{children}</div>
129
- ),
130
- AvatarImage: ({ src, alt }: { src?: string; alt?: string }) => (
131
- <img data-testid="avatar-image" src={src} alt={alt} />
132
- ),
133
131
  }));
134
132
 
135
133
  // Mock the Button component
@@ -122,7 +122,7 @@ import {
122
122
  DialogOverlay
123
123
  } from '../Dialog';
124
124
  import { PasswordChangeForm, PasswordChangeFormError } from '../PasswordChange/PasswordChangeForm';
125
- import { Avatar, AvatarFallback, AvatarImage } from '../Avatar';
125
+ import { Avatar } from '../Avatar';
126
126
  import { Button } from '../Button';
127
127
 
128
128
  export interface UserMenuProps {
@@ -166,10 +166,12 @@ export const UserMenu = React.memo<UserMenuProps>(function UserMenu({
166
166
  <SelectTrigger asChild>
167
167
  <Button variant="outline" className="flex items-center gap-2" aria-label={userInfo.displayName}>
168
168
  {showAvatar && (
169
- <Avatar className="size-7">
170
- <AvatarImage src={userInfo.avatarUrl} alt={userInfo.displayName} />
171
- <AvatarFallback>{userInfo.initial}</AvatarFallback>
172
- </Avatar>
169
+ <Avatar
170
+ src={userInfo.avatarUrl}
171
+ alt={userInfo.displayName}
172
+ fallback={userInfo.initial}
173
+ className="size-7"
174
+ />
173
175
  )}
174
176
  <span>{userInfo.displayName}</span>
175
177
  <ChevronDown className="h-4 w-4" />
@@ -52,7 +52,8 @@ export { Textarea } from './Textarea';
52
52
  export type { TextareaProps } from './Textarea';
53
53
 
54
54
  export { Alert, AlertTitle, AlertDescription } from './Alert';
55
- export { Avatar, AvatarImage, AvatarFallback } from './Avatar';
55
+ export { Avatar } from './Avatar';
56
+ export type { AvatarProps } from './Avatar';
56
57
 
57
58
  export { Badge } from './Badge';
58
59
  export type { BadgeProps, BadgeVariant } from './Badge';
package/src/index.ts CHANGED
@@ -85,7 +85,8 @@ export { Textarea } from './components/Textarea/Textarea';
85
85
  export type { TextareaProps } from './components/Textarea/Textarea';
86
86
 
87
87
  export { Alert, AlertTitle, AlertDescription } from './components/Alert/Alert';
88
- export { Avatar, AvatarImage, AvatarFallback } from './components/Avatar/Avatar';
88
+ export { Avatar } from './components/Avatar/Avatar';
89
+ export type { AvatarProps } from './components/Avatar/Avatar';
89
90
 
90
91
  export { Badge } from './components/Badge/Badge';
91
92
  export type { BadgeProps, BadgeVariant } from './components/Badge/Badge';
@@ -344,7 +344,7 @@ export class OrganisationService extends BaseService implements IOrganisationSer
344
344
 
345
345
  try {
346
346
  // Get user's organisation memberships using secure RPC function
347
- // Only get actual members (org_admin, leader, member) - exclude supporters
347
+ // Include all roles (org_admin, leader, member, supporter) - supporters can access PORTAL
348
348
  let memberships, membershipError;
349
349
  try {
350
350
  // Add timeout and abort signal to prevent hanging RPC calls
@@ -368,10 +368,11 @@ export class OrganisationService extends BaseService implements IOrganisationSer
368
368
 
369
369
  const result = await Promise.race([rpcPromise, timeoutPromise]) as { data: OrganisationRoleRpcResponse[] | null; error: Error | null };
370
370
 
371
- // Filter to only actual members (org_admin, leader, member) - exclude supporters
371
+ // Filter to organisation members (org_admin, leader, member, supporter)
372
+ // Supporters are included to allow PORTAL access
372
373
  // Map to branded types when filtering
373
374
  memberships = result.data?.filter((role) =>
374
- ['org_admin', 'leader', 'member'].includes(role.role)
375
+ ['org_admin', 'leader', 'member', 'supporter'].includes(role.role)
375
376
  ).map((m) => ({
376
377
  ...m,
377
378
  user_id: assertUserId(m.user_id),
@@ -423,7 +424,7 @@ export class OrganisationService extends BaseService implements IOrganisationSer
423
424
  .eq('user_id', this.user.id)
424
425
  .eq('status', 'active')
425
426
  .is('revoked_at', null)
426
- .in('role', ['org_admin', 'leader', 'member']);
427
+ .in('role', ['org_admin', 'leader', 'member', 'supporter']);
427
428
 
428
429
  if (fallbackError) {
429
430
  logger.error("OrganisationService", "Fallback query also failed:", fallbackError);