@@ -4,7 +4,10 @@ import Link from 'next/link';
44import { AuthUserDto } from '@fairshare/shared-types' ;
55import { LogOut , ShieldCheck , LifeBuoy , Settings , ChevronRight , User , Command } from 'lucide-react' ;
66import { useAuth } from '../auth/AuthProvider' ;
7- import { motion } from 'framer-motion' ;
7+ import { useToast } from '../ui/Toaster' ;
8+ import { useState } from 'react' ;
9+ import { deleteAccountAction } from '../../lib/actions' ;
10+ import { motion , AnimatePresence } from 'framer-motion' ;
811import { GlassCard } from '../../../components/ui/GlassCard' ;
912import { fadeUp , staggerContainer } from '../../../components/home/motion-variants' ;
1013
@@ -18,6 +21,35 @@ function initials(name?: string | null) {
1821export function ProfilePanel ( { fallbackUser } : { fallbackUser : AuthUserDto | null } ) {
1922 const { user, logout, loading } = useAuth ( ) ;
2023 const currentUser = user ?? fallbackUser ;
24+ const { toast } = useToast ( ) ;
25+
26+ const [ isDeleteModalOpen , setIsDeleteModalOpen ] = useState ( false ) ;
27+ const [ isDeleting , setIsDeleting ] = useState ( false ) ;
28+ const [ deleteConfirmation , setDeleteConfirmation ] = useState ( '' ) ;
29+
30+ const handleDeleteAccount = async ( ) => {
31+ if ( deleteConfirmation . toLowerCase ( ) !== 'delete my account' ) {
32+ toast ( 'Please type "delete my account" to confirm' , 'error' ) ;
33+ return ;
34+ }
35+
36+ setIsDeleting ( true ) ;
37+ try {
38+ const result = await deleteAccountAction ( ) ;
39+ if ( result . success ) {
40+ toast ( 'Account successfully scheduled for deletion' ) ;
41+ await logout ( ) ;
42+ } else {
43+ toast ( result . message || 'Failed to delete account' , 'error' ) ;
44+ }
45+ } catch ( err ) {
46+ toast ( 'An unexpected error occurred' , 'error' ) ;
47+ } finally {
48+ setIsDeleting ( false ) ;
49+ setIsDeleteModalOpen ( false ) ;
50+ setDeleteConfirmation ( '' ) ;
51+ }
52+ } ;
2153
2254 const actionItems = [
2355 {
@@ -128,6 +160,86 @@ export function ProfilePanel({ fallbackUser }: { fallbackUser: AuthUserDto | nul
128160 </ GlassCard >
129161 </ button >
130162 </ div >
163+ { /* ── Danger Zone ── */ }
164+ < div className = "pt-8" >
165+ < GlassCard className = "p-6 border-rose-500/20 bg-rose-500/5 shadow-[var(--fs-shadow-soft)]" >
166+ < div className = "flex flex-col sm:flex-row items-center justify-between gap-4" >
167+ < div className = "space-y-1 text-center sm:text-left" >
168+ < h2 className = "text-lg font-black italic tracking-tighter text-rose-500 uppercase" >
169+ Danger Zone
170+ </ h2 >
171+ < p className = "text-[12px] font-medium text-[var(--fs-text-muted)] max-w-xl" >
172+ Permanently delete your account and anonymize all associated personal data. This action cannot be undone.
173+ </ p >
174+ </ div >
175+ < button
176+ onClick = { ( ) => setIsDeleteModalOpen ( true ) }
177+ disabled = { loading || isDeleting }
178+ className = "px-6 py-3 rounded-xl bg-rose-500 hover:bg-rose-600 text-white font-bold tracking-widest uppercase text-xs transition-colors"
179+ >
180+ Delete Account
181+ </ button >
182+ </ div >
183+ </ GlassCard >
184+ </ div >
185+
186+ { /* ── Delete Account Modal ── */ }
187+ < AnimatePresence >
188+ { isDeleteModalOpen && (
189+ < div className = "fixed inset-0 z-50 flex items-center justify-center p-4" >
190+ < motion . div
191+ initial = { { opacity : 0 } }
192+ animate = { { opacity : 1 } }
193+ exit = { { opacity : 0 } }
194+ className = "absolute inset-0 bg-black/60 backdrop-blur-sm"
195+ onClick = { ( ) => ! isDeleting && setIsDeleteModalOpen ( false ) }
196+ />
197+ < motion . div
198+ initial = { { opacity : 0 , scale : 0.95 , y : 20 } }
199+ animate = { { opacity : 1 , scale : 1 , y : 0 } }
200+ exit = { { opacity : 0 , scale : 0.95 , y : 20 } }
201+ className = "relative w-full max-w-md overflow-hidden rounded-3xl border border-rose-500/20 bg-[var(--fs-surface)] p-6 shadow-2xl"
202+ >
203+ < div className = "mb-6 flex flex-col items-center text-center" >
204+ < div className = "mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-rose-500/10 text-rose-500" >
205+ < ShieldCheck size = { 32 } />
206+ </ div >
207+ < h3 className = "text-xl font-black italic tracking-tighter text-[var(--fs-text-primary)] uppercase" >
208+ Delete Account
209+ </ h3 >
210+ < p className = "mt-2 text-sm text-[var(--fs-text-muted)]" >
211+ This will permanently anonymize your data and remove your access to all groups. To proceed, please type < span className = "font-bold text-rose-500" > delete my account</ span > below.
212+ </ p >
213+ </ div >
214+
215+ < input
216+ type = "text"
217+ placeholder = "delete my account"
218+ value = { deleteConfirmation }
219+ onChange = { ( e ) => setDeleteConfirmation ( e . target . value ) }
220+ className = "w-full rounded-xl border border-[var(--fs-border)] bg-[var(--fs-bg)] px-4 py-3 text-center font-bold tracking-widest uppercase text-[var(--fs-text-primary)] outline-none focus:border-rose-500 focus:ring-1 focus:ring-rose-500 placeholder:text-[var(--fs-text-muted)]/50"
221+ />
222+
223+ < div className = "mt-8 flex gap-3" >
224+ < button
225+ onClick = { ( ) => setIsDeleteModalOpen ( false ) }
226+ disabled = { isDeleting }
227+ className = "flex-1 rounded-xl bg-[var(--fs-bg)] py-3 font-bold tracking-widest text-[var(--fs-text-primary)] transition-colors hover:bg-[var(--fs-border)] uppercase text-xs"
228+ >
229+ Cancel
230+ </ button >
231+ < button
232+ onClick = { handleDeleteAccount }
233+ disabled = { isDeleting || deleteConfirmation . toLowerCase ( ) !== 'delete my account' }
234+ className = "flex-1 rounded-xl bg-rose-500 py-3 font-bold tracking-widest text-white transition-colors hover:bg-rose-600 disabled:opacity-50 uppercase text-xs"
235+ >
236+ { isDeleting ? 'Deleting...' : 'Confirm' }
237+ </ button >
238+ </ div >
239+ </ motion . div >
240+ </ div >
241+ ) }
242+ </ AnimatePresence >
131243 </ motion . div >
132244 ) ;
133245}
0 commit comments