'use client' import { useUserProfile } from '@nextsparkjs/core/hooks/useUserProfile' import { useAuth } from '@nextsparkjs/core/hooks/useAuth' import { setUserLocaleClient } from '@nextsparkjs/core/lib/locale-client' import { I18N_CONFIG } from '@nextsparkjs/core/lib/config' import { useEffect, useState, useCallback } from 'react' import { useForm } from 'react-hook-form' import { zodResolver } from '@hookform/resolvers/zod' import { useMutation, useQueryClient } from '@tanstack/react-query' import { Button } from '@nextsparkjs/core/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@nextsparkjs/core/components/ui/card' import { Input } from '@nextsparkjs/core/components/ui/input' import { Label } from '@nextsparkjs/core/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@nextsparkjs/core/components/ui/select' import { Popover, PopoverContent, PopoverTrigger, } from '@nextsparkjs/core/components/ui/popover' import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, } from '@nextsparkjs/core/components/ui/command' import { Separator } from '@nextsparkjs/core/components/ui/separator' import { Alert, AlertDescription } from '@nextsparkjs/core/components/ui/alert' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@nextsparkjs/core/components/ui/dialog' import { Loader2, User, Mail, Calendar, Globe, Clock, Languages, CheckCircle2, AlertCircle, ChevronDown, ChevronRight, Trash2, Check, ChevronsUpDown } from 'lucide-react' import { profileSchema, ProfileFormData } from '@nextsparkjs/core/lib/validation' import { countries, timezones } from '@nextsparkjs/core/lib/countries-timezones' import { sel } from '@nextsparkjs/core/selectors' import { useTranslations } from 'next-intl' import { getTemplateOrDefaultClient } from '@nextsparkjs/registries/template-registry.client' // Language options const languages = [ { value: 'en', label: 'English' }, { value: 'es', label: 'Español' }, ] as const function ProfilePage() { const { profile, isLoading, error } = useUserProfile() const { signOut } = useAuth() const queryClient = useQueryClient() const [updateSuccess, setUpdateSuccess] = useState(false) const [advancedOpen, setAdvancedOpen] = useState(false) const [deleteDialogOpen, setDeleteDialogOpen] = useState(false) const [statusMessage, setStatusMessage] = useState('') const [timezoneOpen, setTimezoneOpen] = useState(false) const [countryOpen, setCountryOpen] = useState(false) const t = useTranslations('settings') // Form setup const { register, handleSubmit, formState: { errors, isSubmitting }, setValue, watch, reset } = useForm({ resolver: zodResolver(profileSchema), defaultValues: { firstName: '', lastName: '', country: '', timezone: '', language: I18N_CONFIG.defaultLocale, }, }) // Update form when profile data loads useEffect(() => { if (profile) { reset({ firstName: profile.firstName || '', lastName: profile.lastName || '', country: profile.country || '', timezone: profile.timezone || '', language: profile.language || I18N_CONFIG.defaultLocale, }) } }, [profile, reset]) // Update profile mutation const updateProfileMutation = useMutation({ mutationFn: async (data: ProfileFormData) => { const response = await fetch('/api/user/profile', { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }) if (!response.ok) { const errorData = await response.json() throw new Error(errorData.error || 'Failed to update profile') } return response.json() }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['user-profile'] }) setUpdateSuccess(true) setStatusMessage(t('profile.messages.updateSuccess')) setTimeout(() => { setUpdateSuccess(false) setStatusMessage('') }, 3000) }, onError: (error) => { setStatusMessage(t('profile.messages.updateError', { error: error.message })) }, }) // Delete account mutation const deleteAccountMutation = useMutation({ mutationFn: async () => { const response = await fetch('/api/user/delete-account', { method: 'DELETE', headers: { 'Content-Type': 'application/json', }, }) if (!response.ok) { const errorData = await response.json() throw new Error(errorData.error || 'Failed to delete account') } return response.json() }, onSuccess: async () => { setStatusMessage(t('profile.messages.deleteSuccess')) // Clear all query data and sign out the user queryClient.clear() await signOut() // Redirect will happen automatically after signOut }, onError: (error) => { setStatusMessage(t('profile.messages.deleteError', { error: error.message })) }, }) const onSubmit = useCallback(async (data: ProfileFormData) => { setStatusMessage(t('profile.messages.updating')) // Check if language changed const languageChanged = profile?.language !== data.language try { // Update profile with ALL fields (including language) await updateProfileMutation.mutateAsync(data) // If language changed, update app language after successful profile update if (languageChanged) { try { // Update cookie and reload to apply new language setUserLocaleClient(data.language) window.location.reload() } catch (error) { console.error('Error updating app language:', error) } } } catch { // Error handled by mutation onError } }, [updateProfileMutation, t, profile?.language]) const handleDeleteAccount = useCallback(() => { setStatusMessage(t('profile.messages.deleting')) deleteAccountMutation.mutate() setDeleteDialogOpen(false) }, [deleteAccountMutation, t]) const handleAdvancedToggle = useCallback(() => { const newState = !advancedOpen setAdvancedOpen(newState) setStatusMessage(newState ? t('profile.messages.advancedOpen') : t('profile.messages.advancedClosed')) }, [advancedOpen, t]) const countryValue = watch('country') const timezoneValue = watch('timezone') const languageValue = watch('language') if (isLoading) { return (
) } if (error) { return ( ) } return ( <> {/* MANDATORY: Screen reader announcements */}
{statusMessage}
{/* Header */}

{t('profile.title')}

{t('profile.description')}

{/* Success Alert */} {updateSuccess && ( )} {/* Error Alert */} {updateProfileMutation.error && ( )} {/* Single Column Layout */} {t('profile.form.description')}
{errors.firstName && (

{errors.firstName.message}

)}
{errors.lastName && (

{errors.lastName.message}

)}
{/* Email, Auth and Verification - 50%, 25%, 25% Layout */}

{t('profile.form.emailNote')}

{profile?.authMethod === 'Google' ? ( ) : ( )} {profile?.authMethod}
{profile?.emailVerified ? t('profile.form.verified') : t('profile.form.notVerified')}
{/* Location and Preferences - 20%, 30%, 50% Layout */}
{errors.language && (

{errors.language.message}

)}
No se encontraron países. {countries.map((country) => ( { setValue('country', country.value === countryValue ? '' : country.value) setCountryOpen(false) }} > {country.label} ))} {errors.country && (

{errors.country.message}

)}
No se encontraron timezones. {timezones.map((timezone) => ( { setValue('timezone', timezone.value === timezoneValue ? '' : timezone.value) setTimezoneOpen(false) }} > {timezone.label} ))} {errors.timezone && (

{errors.timezone.message}

)}
{/* Action Section - Button and Member Info */}
{t('profile.form.memberSince')}: {profile && new Date(profile.createdAt).toLocaleDateString('es-ES', { month: 'long', day: 'numeric', year: 'numeric' })}
{(isSubmitting || updateProfileMutation.isPending) ? t('profile.form.submitHelpUpdating') : t('profile.form.submitHelp') }
{/* Advanced Section */}
{advancedOpen && (

{t('profile.danger.title')}

{t('profile.danger.warning')}

{t('profile.danger.dialogTitle')}
{t('profile.danger.dialogDescription')}
{t('profile.danger.dialogWarning')}
{t('profile.danger.dialogDetail')}
)}
) } export default getTemplateOrDefaultClient('app/dashboard/settings/profile/page.tsx', ProfilePage)