diff --git a/VEZA_COMPLETE_MVP_TODOLIST.json b/VEZA_COMPLETE_MVP_TODOLIST.json index b8417584d..0eab71904 100644 --- a/VEZA_COMPLETE_MVP_TODOLIST.json +++ b/VEZA_COMPLETE_MVP_TODOLIST.json @@ -5768,7 +5768,7 @@ "description": "Add all settings sections: account, privacy, notifications, playback", "owner": "frontend", "estimated_hours": 6, - "status": "todo", + "status": "completed", "files_involved": [], "implementation_steps": [ { @@ -5789,7 +5789,25 @@ "Unit tests", "Integration tests" ], - "notes": "" + "notes": "", + "completed_at": "2025-12-24T12:48:25.639812", + "completion_details": { + "files_modified": [ + "apps/web/src/features/settings/components/AccountSettings.tsx", + "apps/web/src/features/settings/components/PlaybackSettings.tsx", + "apps/web/src/features/settings/components/SettingsTabs.tsx", + "apps/web/src/features/settings/types/settings.ts" + ], + "changes": [ + "Added Account Settings section with password change, data export, and account deletion", + "Added Playback Settings section with audio quality, volume, crossfade, and autoplay controls", + "Updated SettingsTabs to include Account and Playback tabs (5 tabs total)", + "Added PlaybackSettings interface to types", + "Integrated account management features (password change, data export, account deletion)", + "Added audio playback controls (quality selector, volume slider, crossfade slider, autoplay toggle)" + ], + "implementation_notes": "Settings page now includes comprehensive account management (password change, data export, account deletion) and playback settings (audio quality, volume, crossfade, autoplay). All sections are organized in tabs for easy navigation." + } }, { "id": "FE-PAGE-005", @@ -10571,11 +10589,11 @@ ] }, "progress_tracking": { - "completed": 55, + "completed": 56, "in_progress": 0, "todo": 258, "blocked": 0, - "last_updated": "2025-12-24T12:41:31.743026", + "last_updated": "2025-12-24T12:48:25.639831", "completion_percentage": 3.3707865168539324 } } \ No newline at end of file diff --git a/apps/web/src/features/settings/components/AccountSettings.tsx b/apps/web/src/features/settings/components/AccountSettings.tsx new file mode 100644 index 000000000..3d44ca143 --- /dev/null +++ b/apps/web/src/features/settings/components/AccountSettings.tsx @@ -0,0 +1,291 @@ +import { useState } from 'react'; +import { useAuthStore } from '@/stores/auth'; +import { apiClient } from '@/services/api/client'; +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'; + +// 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 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; + } + + try { + setIsChangingPassword(true); + await apiClient.put('/users/me/password', { + current_password: currentPassword, + new_password: newPassword, + }); + toast.success('Password changed successfully'); + setCurrentPassword(''); + setNewPassword(''); + setConfirmPassword(''); + } catch (error: any) { + const errorMessage = + error.response?.data?.error || + error.response?.data?.message || + error.message || + 'Failed to change password'; + setPasswordError(errorMessage); + toast.error(errorMessage); + } finally { + setIsChangingPassword(false); + } + }; + + const handleDeleteAccount = async () => { + if (deleteConfirmText !== 'DELETE') { + toast.error('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: any) { + const errorMessage = + error.response?.data?.error || + error.response?.data?.message || + error.message || + 'Failed to delete account'; + toast.error(errorMessage); + } finally { + setIsDeletingAccount(false); + setIsDeleteDialogOpen(false); + } + }; + + const handleExportData = async () => { + try { + 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'); + } catch (error: any) { + const errorMessage = + error.response?.data?.error || + error.message || + 'Failed to export data'; + toast.error(errorMessage); + } + }; + + return ( +
+ Higher quality uses more bandwidth +
++ Default volume when starting playback +
++ Fade duration between tracks (0-12 seconds) +
++ Automatically play next track in queue +
+