import { useState, useRef } from 'react'; import { TwoFactorSettings } from './TwoFactorSettings'; import { useAuthStore } from '@/features/auth/store/authStore'; import { apiClient } from '@/services/api/client'; import { parseApiError } from '@/utils/apiErrorHandler'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from '@/components/ui/card'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { useToast } from '@/hooks/useToast'; import { AlertCircle, Trash2, Key, Download } from 'lucide-react'; import { Dialog } from '@/components/ui/dialog'; import { ErrorDisplay } from '@/components/ui/ErrorDisplay'; // FE-PAGE-004: Complete Settings page implementation - Account Settings export function AccountSettings() { const { logout } = useAuthStore(); const toast = useToast(); const [isChangingPassword, setIsChangingPassword] = useState(false); const [isDeletingAccount, setIsDeletingAccount] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); // Password change form const [currentPassword, setCurrentPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [passwordError, setPasswordError] = useState(''); // Account deletion form const [deletePassword, setDeletePassword] = useState(''); const [deleteReason, setDeleteReason] = useState(''); const [deleteConfirmText, setDeleteConfirmText] = useState(''); const [deleteValidationError, setDeleteValidationError] = useState< string | null >(null); const [mutationError, setMutationError] = useState(null); const [retryCount, setRetryCount] = useState(0); const lastMutationRef = useRef<(() => Promise) | null>(null); const handleChangePassword = async (e: React.FormEvent) => { e.preventDefault(); setPasswordError(''); if (newPassword !== confirmPassword) { setPasswordError('New passwords do not match'); return; } if (newPassword.length < 12) { setPasswordError('Password must be at least 12 characters long'); return; } // Action 3.4.1.3: Store mutation for retry const performMutation = async () => { await apiClient.put('/users/me/password', { current_password: currentPassword, new_password: newPassword, }); toast.success('Password changed successfully'); setCurrentPassword(''); setNewPassword(''); setConfirmPassword(''); setMutationError(null); setRetryCount(0); lastMutationRef.current = null; }; lastMutationRef.current = performMutation; setIsChangingPassword(true); try { await performMutation(); } catch (error: unknown) { const apiError = parseApiError(error); setPasswordError(apiError.message); setMutationError(new Error(apiError.message)); } finally { setIsChangingPassword(false); } }; const handleDeleteAccount = async () => { setDeleteValidationError(null); setMutationError(null); if (deleteConfirmText !== 'DELETE') { setDeleteValidationError('Please type DELETE to confirm'); return; } try { setIsDeletingAccount(true); await apiClient.delete('/users/me', { data: { password: deletePassword, reason: deleteReason, confirm_text: deleteConfirmText, }, }); toast.success('Account deletion requested. You will be logged out.'); setTimeout(() => { logout(); window.location.href = '/login'; }, 2000); } catch (error: unknown) { const apiError = parseApiError(error); setMutationError(new Error(apiError.message)); } finally { setIsDeletingAccount(false); setIsDeleteDialogOpen(false); } }; const handleExportData = async () => { // Action 3.4.1.3: Store mutation for retry const performMutation = async () => { const response = await apiClient.get('/users/me/export', { responseType: 'blob', }); // Create download link const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; link.setAttribute( 'download', `veza-data-export-${new Date().toISOString()}.json`, ); document.body.appendChild(link); link.click(); link.remove(); window.URL.revokeObjectURL(url); toast.success('Data export started'); setMutationError(null); setRetryCount(0); lastMutationRef.current = null; }; lastMutationRef.current = performMutation; try { await performMutation(); } catch (error: unknown) { const apiError = parseApiError(error); setMutationError(new Error(apiError.message)); } }; // Action 3.4.1.3: Retry handler for failed mutations const handleRetry = async () => { if (!lastMutationRef.current || retryCount >= 3) return; setRetryCount((prev) => prev + 1); try { await lastMutationRef.current(); } catch (error) { // Error will be handled by the mutation function } }; return (
{mutationError && ( { setMutationError(null); setRetryCount(0); lastMutationRef.current = null; }} /> )} {/* FE-PAGE-004: Change Password Section */} Change Password Update your password to keep your account secure
setCurrentPassword(e.target.value)} required />
setNewPassword(e.target.value)} required minLength={12} />

Password must be at least 12 characters long

setConfirmPassword(e.target.value)} required minLength={12} />
{passwordError && ( {passwordError} )}
{/* FE-PAGE-004: Data Export Section */} Data Export Download a copy of your data (GDPR) {/* FE-PAGE-004: Delete Account Section */} Delete Account Permanently delete your account and all associated data This action cannot be undone. All your data will be permanently deleted. setIsDeleteDialogOpen(false)} title="Are you absolutely sure?" variant="alert" onConfirm={handleDeleteAccount} confirmLabel={isDeletingAccount ? 'Deleting...' : 'Delete Account'} cancelLabel="Cancel" size="lg" >

This will permanently delete your account and all associated data. This action cannot be undone.

setDeletePassword(e.target.value)} required />
setDeleteReason(e.target.value)} placeholder="Why are you deleting your account?" />
{deleteValidationError && ( )}
{ setDeleteConfirmText(e.target.value); setDeleteValidationError(null); }} required placeholder="DELETE" />
); }