mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 10:11:13 -05:00
Fix: Implement settings cleanup plan
This commit is contained in:
@@ -5,15 +5,13 @@ import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Textarea } from '@/components/ui/textarea';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { useProfile } from '@/hooks/useProfile';
|
||||
import { supabase } from '@/integrations/supabase/client';
|
||||
import { User, Upload, Trash2, Mail, AlertCircle, X, Check, Loader2 } from 'lucide-react';
|
||||
import { Trash2, Mail, AlertCircle, X, Check, Loader2 } from 'lucide-react';
|
||||
import { PhotoUpload } from '@/components/upload/PhotoUpload';
|
||||
import { notificationService } from '@/lib/notificationService';
|
||||
import { EmailChangeDialog } from './EmailChangeDialog';
|
||||
@@ -22,7 +20,7 @@ import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
|
||||
import { AccountDeletionDialog } from './AccountDeletionDialog';
|
||||
import { DeletionStatusBanner } from './DeletionStatusBanner';
|
||||
import { EmailChangeStatus } from './EmailChangeStatus';
|
||||
import { usernameSchema, displayNameSchema, bioSchema, personalLocationSchema, preferredPronounsSchema } from '@/lib/validation';
|
||||
import { usernameSchema, displayNameSchema, bioSchema } from '@/lib/validation';
|
||||
import { z } from 'zod';
|
||||
import { AccountDeletionRequest } from '@/types/database';
|
||||
import { handleError, handleSuccess, AppError } from '@/lib/errorHandler';
|
||||
@@ -35,11 +33,7 @@ import { cn } from '@/lib/utils';
|
||||
const profileSchema = z.object({
|
||||
username: usernameSchema,
|
||||
display_name: displayNameSchema,
|
||||
bio: bioSchema,
|
||||
preferred_pronouns: preferredPronounsSchema,
|
||||
show_pronouns: z.boolean(),
|
||||
preferred_language: z.string(),
|
||||
personal_location: personalLocationSchema
|
||||
bio: bioSchema
|
||||
});
|
||||
|
||||
type ProfileFormData = z.infer<typeof profileSchema>;
|
||||
@@ -67,11 +61,7 @@ export function AccountProfileTab() {
|
||||
defaultValues: {
|
||||
username: profile?.username || '',
|
||||
display_name: profile?.display_name || '',
|
||||
bio: profile?.bio || '',
|
||||
preferred_pronouns: profile?.preferred_pronouns || '',
|
||||
show_pronouns: profile?.show_pronouns || false,
|
||||
preferred_language: profile?.preferred_language || 'en',
|
||||
personal_location: profile?.personal_location || ''
|
||||
bio: profile?.bio || ''
|
||||
}
|
||||
});
|
||||
|
||||
@@ -122,9 +112,7 @@ export function AccountProfileTab() {
|
||||
const { data: result, error } = await supabase.rpc('update_profile', {
|
||||
p_username: data.username,
|
||||
p_display_name: data.display_name || null,
|
||||
p_bio: data.bio || null,
|
||||
p_preferred_pronouns: data.preferred_pronouns || null,
|
||||
p_personal_location: data.personal_location || null
|
||||
p_bio: data.bio || null
|
||||
});
|
||||
|
||||
if (error) {
|
||||
@@ -340,125 +328,74 @@ export function AccountProfileTab() {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
|
||||
<h3 className="text-lg font-medium">Profile Information</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="username">
|
||||
Username *
|
||||
{usernameValidation.isChecking && (
|
||||
<Loader2 className="inline ml-2 w-3 h-3 animate-spin" />
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="username">
|
||||
Username *
|
||||
{usernameValidation.isChecking && (
|
||||
<Loader2 className="inline ml-2 w-3 h-3 animate-spin" />
|
||||
)}
|
||||
</Label>
|
||||
<Input
|
||||
id="username"
|
||||
{...form.register('username')}
|
||||
placeholder="Enter your username"
|
||||
disabled={isDeactivated}
|
||||
className={cn(
|
||||
usernameValidation.error && !form.formState.errors.username && "border-destructive",
|
||||
usernameValidation.isAvailable && "border-green-500"
|
||||
)}
|
||||
/>
|
||||
{usernameValidation.isChecking ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Checking availability...
|
||||
</p>
|
||||
) : usernameValidation.isAvailable === false && !form.formState.errors.username ? (
|
||||
<p className="text-sm text-destructive flex items-center gap-1">
|
||||
<X className="w-3 h-3" />
|
||||
{usernameValidation.error}
|
||||
</p>
|
||||
) : usernameValidation.isAvailable === true && !form.formState.errors.username ? (
|
||||
<p className="text-sm text-green-600 flex items-center gap-1">
|
||||
<Check className="w-3 h-3" />
|
||||
Username is available
|
||||
</p>
|
||||
) : form.formState.errors.username ? (
|
||||
<p className="text-sm text-destructive">
|
||||
{form.formState.errors.username.message}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="display_name">Display Name</Label>
|
||||
<Input
|
||||
id="display_name"
|
||||
{...form.register('display_name')}
|
||||
placeholder="Enter your display name"
|
||||
/>
|
||||
{form.formState.errors.display_name && (
|
||||
<p className="text-sm text-destructive">
|
||||
{form.formState.errors.display_name.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="bio">Bio</Label>
|
||||
<Textarea
|
||||
id="bio"
|
||||
{...form.register('bio')}
|
||||
placeholder="Tell us about yourself..."
|
||||
rows={4}
|
||||
/>
|
||||
{form.formState.errors.bio && (
|
||||
<p className="text-sm text-destructive">
|
||||
{form.formState.errors.bio.message}
|
||||
</p>
|
||||
)}
|
||||
</Label>
|
||||
<Input
|
||||
id="username"
|
||||
{...form.register('username')}
|
||||
placeholder="Enter your username"
|
||||
disabled={isDeactivated}
|
||||
className={cn(
|
||||
usernameValidation.error && !form.formState.errors.username && "border-destructive",
|
||||
usernameValidation.isAvailable && "border-green-500"
|
||||
)}
|
||||
/>
|
||||
{usernameValidation.isChecking ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Checking availability...
|
||||
</p>
|
||||
) : usernameValidation.isAvailable === false && !form.formState.errors.username ? (
|
||||
<p className="text-sm text-destructive flex items-center gap-1">
|
||||
<X className="w-3 h-3" />
|
||||
{usernameValidation.error}
|
||||
</p>
|
||||
) : usernameValidation.isAvailable === true && !form.formState.errors.username ? (
|
||||
<p className="text-sm text-green-600 flex items-center gap-1">
|
||||
<Check className="w-3 h-3" />
|
||||
Username is available
|
||||
</p>
|
||||
) : form.formState.errors.username ? (
|
||||
<p className="text-sm text-destructive">
|
||||
{form.formState.errors.username.message}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="display_name">Display Name</Label>
|
||||
<Input
|
||||
id="display_name"
|
||||
{...form.register('display_name')}
|
||||
placeholder="Enter your display name"
|
||||
/>
|
||||
{form.formState.errors.display_name && (
|
||||
<p className="text-sm text-destructive">
|
||||
{form.formState.errors.display_name.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="bio">Bio</Label>
|
||||
<Textarea
|
||||
id="bio"
|
||||
{...form.register('bio')}
|
||||
placeholder="Tell us about yourself..."
|
||||
rows={4}
|
||||
/>
|
||||
{form.formState.errors.bio && (
|
||||
<p className="text-sm text-destructive">
|
||||
{form.formState.errors.bio.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="preferred_pronouns">Preferred Pronouns</Label>
|
||||
<Input
|
||||
id="preferred_pronouns"
|
||||
{...form.register('preferred_pronouns')}
|
||||
placeholder="e.g., they/them, she/her, he/him"
|
||||
/>
|
||||
{form.formState.errors.preferred_pronouns && (
|
||||
<p className="text-sm text-destructive">
|
||||
{form.formState.errors.preferred_pronouns.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="preferred_language">Preferred Language</Label>
|
||||
<Select
|
||||
value={form.watch('preferred_language')}
|
||||
onValueChange={(value) => form.setValue('preferred_language', value)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="en">English</SelectItem>
|
||||
<SelectItem value="es">Español</SelectItem>
|
||||
<SelectItem value="fr">Français</SelectItem>
|
||||
<SelectItem value="de">Deutsch</SelectItem>
|
||||
<SelectItem value="it">Italiano</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="personal_location">Personal Location</Label>
|
||||
<Input
|
||||
id="personal_location"
|
||||
{...form.register('personal_location')}
|
||||
placeholder="e.g., Los Angeles, CA"
|
||||
/>
|
||||
{form.formState.errors.personal_location && (
|
||||
<p className="text-sm text-destructive">
|
||||
{form.formState.errors.personal_location.message}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between">
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user