@@ -7,6 +7,8 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
77import { Badge } from '@/components/ui/badge'
88import { Separator } from '@/components/ui/separator'
99import { Skeleton } from '@/components/ui/Skeleton'
10+ import { Input } from '@/components/ui/input'
11+ import { Label } from '@/components/ui/label'
1012import {
1113 Dialog ,
1214 DialogContent ,
@@ -25,18 +27,18 @@ interface Repository {
2527}
2628
2729const MAX_REPOS = 3
30+ const DELETE_CONFIRMATION_TEXT = 'delete all'
2831
2932export function SettingsPage ( ) {
30- const { user, session, signOut } = useAuth ( )
33+ const { user, session } = useAuth ( )
3134 const { status, checkStatus, disconnect, loading : githubLoading } = useGitHubRepos ( )
3235
3336 const [ repos , setRepos ] = useState < Repository [ ] > ( [ ] )
3437 const [ reposLoading , setReposLoading ] = useState ( true )
3538 const [ disconnectLoading , setDisconnectLoading ] = useState ( false )
3639 const [ deleteReposDialog , setDeleteReposDialog ] = useState ( false )
37- const [ deleteAccountDialog , setDeleteAccountDialog ] = useState ( false )
3840 const [ deleteReposLoading , setDeleteReposLoading ] = useState ( false )
39- const [ deleteAccountLoading , setDeleteAccountLoading ] = useState ( false )
41+ const [ deleteConfirmation , setDeleteConfirmation ] = useState ( '' )
4042
4143 useEffect ( ( ) => {
4244 checkStatus ( )
@@ -70,6 +72,8 @@ export function SettingsPage() {
7072 }
7173
7274 const handleDeleteAllRepos = async ( ) => {
75+ if ( deleteConfirmation !== DELETE_CONFIRMATION_TEXT ) return
76+
7377 setDeleteReposLoading ( true )
7478 try {
7579 for ( const repo of repos ) {
@@ -80,6 +84,7 @@ export function SettingsPage() {
8084 }
8185 setRepos ( [ ] )
8286 setDeleteReposDialog ( false )
87+ setDeleteConfirmation ( '' )
8388 toast . success ( 'All repositories deleted' )
8489 } catch ( error ) {
8590 toast . error ( 'Failed to delete repositories' )
@@ -88,17 +93,9 @@ export function SettingsPage() {
8893 }
8994 }
9095
91- const handleDeleteAccount = async ( ) => {
92- setDeleteAccountLoading ( true )
93- try {
94- toast . info ( 'Account deletion coming soon. Signing you out for now.' )
95- await signOut ( )
96- } catch ( error ) {
97- toast . error ( 'Failed to delete account' )
98- } finally {
99- setDeleteAccountLoading ( false )
100- setDeleteAccountDialog ( false )
101- }
96+ const handleCloseDeleteDialog = ( ) => {
97+ setDeleteReposDialog ( false )
98+ setDeleteConfirmation ( '' )
10299 }
103100
104101 const formatDate = ( dateString : string | undefined ) => {
@@ -110,6 +107,8 @@ export function SettingsPage() {
110107 } )
111108 }
112109
110+ const isDeleteEnabled = deleteConfirmation === DELETE_CONFIRMATION_TEXT
111+
113112 return (
114113 < div className = "max-w-2xl space-y-6" >
115114 < div >
@@ -237,7 +236,7 @@ export function SettingsPage() {
237236 < Alert variant = "destructive" className = "border-destructive/30 bg-destructive/5" >
238237 < AlertTriangle className = "h-5 w-5" />
239238 < AlertTitle className = "text-lg font-semibold" > Danger Zone</ AlertTitle >
240- < AlertDescription className = "mt-4 space-y-4 " >
239+ < AlertDescription className = "mt-4" >
241240 < div className = "flex items-center justify-between" >
242241 < div >
243242 < p className = "font-medium text-foreground" > Delete all repositories</ p >
@@ -253,26 +252,11 @@ export function SettingsPage() {
253252 Delete All
254253 </ Button >
255254 </ div >
256- < Separator className = "bg-destructive/20" />
257- < div className = "flex items-center justify-between" >
258- < div >
259- < p className = "font-medium text-foreground" > Delete account</ p >
260- < p className = "text-sm text-muted-foreground" > Permanently delete your account and all data</ p >
261- </ div >
262- < Button
263- variant = "outline"
264- size = "sm"
265- className = "border-destructive/50 text-destructive hover:bg-destructive hover:text-destructive-foreground"
266- onClick = { ( ) => setDeleteAccountDialog ( true ) }
267- >
268- Delete Account
269- </ Button >
270- </ div >
271255 </ AlertDescription >
272256 </ Alert >
273257
274- { /* Delete Repos Dialog */ }
275- < Dialog open = { deleteReposDialog } onOpenChange = { setDeleteReposDialog } >
258+ { /* Delete Repos Dialog with typing confirmation */ }
259+ < Dialog open = { deleteReposDialog } onOpenChange = { handleCloseDeleteDialog } >
276260 < DialogContent >
277261 < DialogHeader >
278262 < DialogTitle > Delete all repositories?</ DialogTitle >
@@ -281,39 +265,34 @@ export function SettingsPage() {
281265 all indexed data. This action cannot be undone.
282266 </ DialogDescription >
283267 </ DialogHeader >
268+ < div className = "space-y-2 py-4" >
269+ < Label htmlFor = "delete-confirmation" >
270+ To confirm, type < span className = "font-mono font-semibold text-destructive" > { DELETE_CONFIRMATION_TEXT } </ span > below:
271+ </ Label >
272+ < Input
273+ id = "delete-confirmation"
274+ value = { deleteConfirmation }
275+ onChange = { ( e ) => setDeleteConfirmation ( e . target . value ) }
276+ placeholder = { DELETE_CONFIRMATION_TEXT }
277+ className = "font-mono"
278+ autoComplete = "off"
279+ />
280+ </ div >
284281 < DialogFooter >
285- < Button variant = "outline" onClick = { ( ) => setDeleteReposDialog ( false ) } >
282+ < Button variant = "outline" onClick = { handleCloseDeleteDialog } >
286283 Cancel
287284 </ Button >
288- < Button variant = "destructive" onClick = { handleDeleteAllRepos } disabled = { deleteReposLoading } >
285+ < Button
286+ variant = "destructive"
287+ onClick = { handleDeleteAllRepos }
288+ disabled = { ! isDeleteEnabled || deleteReposLoading }
289+ >
289290 { deleteReposLoading && < Loader2 className = "mr-2 h-4 w-4 animate-spin" /> }
290291 Delete All Repositories
291292 </ Button >
292293 </ DialogFooter >
293294 </ DialogContent >
294295 </ Dialog >
295-
296- { /* Delete Account Dialog */ }
297- < Dialog open = { deleteAccountDialog } onOpenChange = { setDeleteAccountDialog } >
298- < DialogContent >
299- < DialogHeader >
300- < DialogTitle > Delete your account?</ DialogTitle >
301- < DialogDescription >
302- This will permanently delete your account, all repositories, and all associated data. This
303- action cannot be undone.
304- </ DialogDescription >
305- </ DialogHeader >
306- < DialogFooter >
307- < Button variant = "outline" onClick = { ( ) => setDeleteAccountDialog ( false ) } >
308- Cancel
309- </ Button >
310- < Button variant = "destructive" onClick = { handleDeleteAccount } disabled = { deleteAccountLoading } >
311- { deleteAccountLoading && < Loader2 className = "mr-2 h-4 w-4 animate-spin" /> }
312- Delete Account
313- </ Button >
314- </ DialogFooter >
315- </ DialogContent >
316- </ Dialog >
317296 </ div >
318297 )
319298}
0 commit comments