Add image cropping to avatar upload and show avatars platform-wide
- Add react-easy-crop for circular crop + zoom UI on avatar upload - Create server-side getUserAvatarUrl utility for generating pre-signed URLs - Update all nav components (admin, jury, mentor, observer) to show user avatars - Add avatar URLs to user list, mentor list, and project detail API responses - Replace initials-only avatars with UserAvatar component across admin pages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
35
src/server/utils/avatar-url.ts
Normal file
35
src/server/utils/avatar-url.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { createStorageProvider, type StorageProviderType } from '@/lib/storage'
|
||||
|
||||
/**
|
||||
* Generate a pre-signed download URL for a user's avatar.
|
||||
* Returns null if the user has no avatar.
|
||||
*/
|
||||
export async function getUserAvatarUrl(
|
||||
profileImageKey: string | null | undefined,
|
||||
profileImageProvider: string | null | undefined
|
||||
): Promise<string | null> {
|
||||
if (!profileImageKey) return null
|
||||
|
||||
try {
|
||||
const providerType = (profileImageProvider as StorageProviderType) || 's3'
|
||||
const provider = createStorageProvider(providerType)
|
||||
return await provider.getDownloadUrl(profileImageKey)
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Batch-generate avatar URLs for multiple users.
|
||||
* Adds `avatarUrl` field to each user object.
|
||||
*/
|
||||
export async function attachAvatarUrls<
|
||||
T extends { profileImageKey?: string | null; profileImageProvider?: string | null }
|
||||
>(users: T[]): Promise<(T & { avatarUrl: string | null })[]> {
|
||||
return Promise.all(
|
||||
users.map(async (user) => ({
|
||||
...user,
|
||||
avatarUrl: await getUserAvatarUrl(user.profileImageKey, user.profileImageProvider),
|
||||
}))
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user