More stuff

This commit is contained in:
2025-07-09 11:54:01 -05:00
parent 2fbb259e62
commit 04f2a48727
16 changed files with 358 additions and 66 deletions

View File

@@ -10,13 +10,26 @@ import {
import { Loader2, Pencil, Upload } from 'lucide-react';
import type { ComponentProps, ChangeEvent } from 'react';
import { toast } from 'sonner';
import { cn } from '@/lib/utils';
type AvatarUploadProps = {
onAvatarUploaded: (path: string) => Promise<void>;
cardProps?: ComponentProps<typeof Card>;
cardContentProps?: ComponentProps<typeof CardContent>;
containerProps?: ComponentProps<'div'>;
basedAvatarProps?: ComponentProps<typeof BasedAvatar>;
iconProps?: ComponentProps<typeof Upload>;
};
export const AvatarUpload = ({
onAvatarUploaded,
cardProps,
cardContentProps,
containerProps,
basedAvatarProps,
iconProps = {
size: 32,
},
}: AvatarUploadProps) => {
const { profile, isAuthenticated } = useAuth();
const { isUploading, fileInputRef, uploadAvatarMutation } = useFileUpload();
@@ -39,8 +52,8 @@ export const AvatarUpload = ({
if (!file.type.startsWith('image/')) throw new Error('File is not an image!');
if (file.size > 8 * 1024 * 1024) throw new Error('File is too large!');
const fileExt = file.name.split('.').pop();
const avatarPath = profile?.avatar_url ?? profile?.id;
const avatarPath = profile?.avatar_url ??
`${profile?.id}.${file.name.split('.').pop()}`;
const avatarUrl = await uploadAvatarMutation.mutateAsync({
client,
@@ -61,12 +74,78 @@ export const AvatarUpload = ({
};
return (
<Card>
<CardContent>
<div>
<Card
{...cardProps}
className={cn('', cardProps?.className)}
>
<CardContent
{...cardContentProps}
className={cn('flex flex-col items-center', cardContentProps?.className)}
>
<div
{...containerProps}
className={cn(
'relative group cursor-pointer mb-4',
containerProps?.className
)}
>
<BasedAvatar
{...basedAvatarProps}
src={profile?.avatar_url}
fullName={profile?.full_name}
className={cn('h-32, w-32', basedAvatarProps?.className)}
fallbackProps={{ className: 'text-4xl font-semibold' }}
userIconProps={{ size: 100 }}
/>
<div
className={cn(
'absoloute inset-0 rounded-full bg-black/0\
group-hover:bg-black/50 transition-all flex\
items-center justify-center'
)}
>
<Upload
{...iconProps}
className={cn('text-white opacity-0 group-hover:opacity-100\
transition-opacity', iconProps?.className
)}
/>
</div>
<div
className={cn(
'absolute inset-1 transition-all flex\
items-end justify-end',
)}
>
<Pencil
{...iconProps}
className={cn(
'text-white opacity-100 group-hover:opacity-0\
transition-opacity', iconProps?.className
)}
/>
</div>
<input
ref={fileInputRef}
type='file'
accept='image/*'
className={cn('hidden')}
onChange={handleFileChange}
disabled={isUploading}
/>
{isUploading && (
<div className={cn('flex items-center text-sm text-gray-500 mt-2')}>
<Loader2 className={cn('h-4 w-4 mr-2 animate-spin')} />
Uploading...
</div>
)}
{!isAuthenticated && (
<p className={cn('text-sm text-muted-foreground mt-2')}>
Sign in to upload an avatar.
</p>
)}
</div>
</CardContent>
</Card>
);
};