Update form to look better & add automatic lunch toggle
This commit is contained in:
@@ -10,13 +10,14 @@ import {
|
||||
} from 'convex/react';
|
||||
import { api } from '~/convex/_generated/api';
|
||||
import {
|
||||
Avatar,
|
||||
AvatarImage,
|
||||
BasedAvatar,
|
||||
Button,
|
||||
CardContent,
|
||||
ImageCrop,
|
||||
ImageCropApply,
|
||||
ImageCropContent,
|
||||
ImageCropReset,
|
||||
Input,
|
||||
} from '@/components/ui';
|
||||
import { toast } from 'sonner';
|
||||
@@ -121,7 +122,7 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
<BasedAvatar
|
||||
src={currentImageUrl ?? undefined}
|
||||
fullName={user?.name}
|
||||
className='h-32 w-32'
|
||||
className='h-42 w-42'
|
||||
fallbackProps={{ className: 'text-4xl font-semibold' }}
|
||||
userIconProps={{ size: 100 }}
|
||||
/>
|
||||
@@ -173,7 +174,6 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
<ImageCropContent className='max-w-sm' />
|
||||
<div className='flex items-center gap-2'>
|
||||
<ImageCropApply />
|
||||
<ImageCropReset />
|
||||
<Button
|
||||
onClick={handleReset}
|
||||
size='icon'
|
||||
@@ -190,19 +190,17 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
{/* Cropped preview + actions */}
|
||||
{croppedImage && (
|
||||
<div className='flex flex-col items-center gap-3'>
|
||||
<Image
|
||||
alt='Cropped preview'
|
||||
className='overflow-hidden rounded-full'
|
||||
height={128}
|
||||
src={croppedImage}
|
||||
unoptimized
|
||||
width={128}
|
||||
/>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Avatar className='h-42 w-42'>
|
||||
<AvatarImage
|
||||
alt='Cropped preview'
|
||||
src={croppedImage}
|
||||
/>
|
||||
</Avatar>
|
||||
<div className='flex items-center gap-1'>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={isUploading}
|
||||
className='px-6'
|
||||
className='px-4'
|
||||
>
|
||||
{isUploading ? (
|
||||
<span className='inline-flex items-center gap-2'>
|
||||
@@ -217,7 +215,10 @@ export const AvatarUpload = ({ preloadedUser }: AvatarUploadProps) => {
|
||||
onClick={handleReset}
|
||||
size='icon'
|
||||
type='button'
|
||||
variant='ghost'
|
||||
className='dark:bg-red-500/30 bg-red-400/80
|
||||
hover:dark:text-red-300/60 hover:text-red-800/80
|
||||
hover:dark:bg-accent'
|
||||
variant='secondary'
|
||||
>
|
||||
<XIcon className='size-4' />
|
||||
</Button>
|
||||
|
||||
@@ -97,7 +97,7 @@ export const ResetPasswordForm = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<CardHeader className='pb-5'>
|
||||
<CardHeader>
|
||||
<CardTitle className='text-2xl'>Change Password</CardTitle>
|
||||
<CardDescription>
|
||||
Update your password to keep your account secure
|
||||
|
||||
@@ -7,6 +7,10 @@ import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import {
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
Checkbox,
|
||||
Form,
|
||||
FormControl,
|
||||
FormDescription,
|
||||
@@ -35,7 +39,8 @@ const formSchema = z.object({
|
||||
lunchTime: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(3, { message: 'Must be a valid 24-hour time. Example: 13:00'}),
|
||||
.min(3, { message: 'Must be a valid 24-hour time. Example: 13:00' }),
|
||||
automaticLunch: z.boolean(),
|
||||
});
|
||||
|
||||
type UserInfoFormProps = {
|
||||
@@ -49,13 +54,16 @@ export const UserInfoForm = ({ preloadedUser }: UserInfoFormProps) => {
|
||||
const updateUserName = useMutation(api.auth.updateUserName);
|
||||
const updateUserEmail = useMutation(api.auth.updateUserEmail);
|
||||
const updateUserLunchtime = useMutation(api.auth.updateUserLunchtime);
|
||||
const updateUserAutomaticLunch = useMutation(api.auth.updateUserAutomaticLunch);
|
||||
|
||||
const initialValues = useMemo<z.infer<typeof formSchema>>(
|
||||
() => ({
|
||||
name: user?.name ?? '',
|
||||
email: user?.email ?? '',
|
||||
lunchTime: user?.lunchTime ?? '',
|
||||
}), [user?.name, user?.email, user?.lunchTime]
|
||||
lunchTime: user?.lunchTime ?? '12:00',
|
||||
automaticLunch: user?.automaticLunch ?? false,
|
||||
}),
|
||||
[user?.name, user?.email, user?.lunchTime, user?.automaticLunch],
|
||||
);
|
||||
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
@@ -68,10 +76,13 @@ export const UserInfoForm = ({ preloadedUser }: UserInfoFormProps) => {
|
||||
const name = values.name.trim();
|
||||
const email = values.email.trim().toLowerCase();
|
||||
const lunchTime = values.lunchTime.trim();
|
||||
const automaticLunch = values.automaticLunch;
|
||||
if (name !== (user?.name ?? '')) ops.push(updateUserName({ name }));
|
||||
if (email !== (user?.email ?? '')) ops.push(updateUserEmail({ email }));
|
||||
if (lunchTime !== (user?.lunchTime ?? ''))
|
||||
ops.push(updateUserLunchtime({ lunchTime }))
|
||||
ops.push(updateUserLunchtime({ lunchTime }));
|
||||
if (automaticLunch !== (user?.automaticLunch))
|
||||
ops.push(updateUserAutomaticLunch({ automaticLunch }));
|
||||
if (ops.length === 0) return;
|
||||
setLoading(true);
|
||||
try {
|
||||
@@ -87,65 +98,103 @@ export const UserInfoForm = ({ preloadedUser }: UserInfoFormProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)} className='space-y-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='name'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Full Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>Your public display name.</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<>
|
||||
<CardHeader>
|
||||
<CardTitle className='text-2xl'>Account Information</CardTitle>
|
||||
<CardDescription>
|
||||
Update your account information here.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(handleSubmit)} className='space-y-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='name'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Full Name</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>Your public display name.</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Your email address associated with your account.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='email'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Email</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Your email address associated with your account.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<div className='flex flex-row justify-center space-x-6'>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='lunchTime'
|
||||
render={({ field }) => (
|
||||
<FormItem className='w-2/5'>
|
||||
<FormLabel>Lunch Time</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
type='time'
|
||||
className='max-w-26'
|
||||
{...field}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Your regular lunch time.
|
||||
</FormDescription>
|
||||
<FormDescription className='dark:text-red-300/60 text-red-800/80'>
|
||||
{!user?.lunchTime && 'Not currently set.'}
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='automaticLunch'
|
||||
render={({ field }) => (
|
||||
<FormItem className='w-2/5'>
|
||||
<div className='flex flex-row space-x-2 my-auto'>
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
className='border-solid border-primary'
|
||||
checked={field.value}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormLabel>Automatic Lunch</FormLabel>
|
||||
</div>
|
||||
<FormDescription>
|
||||
Automatically take your lunch at the time you specify.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
name='lunchTime'
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>Regular Lunch Time</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} />
|
||||
</FormControl>
|
||||
<FormDescription>
|
||||
Enter the time you take your lunch most often in 24 hour time.
|
||||
</FormDescription>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className='flex justify-center'>
|
||||
<SubmitButton disabled={loading} pendingText='Saving...'>
|
||||
Save Changes
|
||||
</SubmitButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
<div className='flex justify-center'>
|
||||
<SubmitButton disabled={loading} pendingText='Saving...'>
|
||||
Save Changes
|
||||
</SubmitButton>
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</CardContent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user