@@ -15,8 +15,17 @@ import {
1515 Eye ,
1616 EyeOff ,
1717 ChevronDown ,
18- ChevronRight
18+ ChevronRight ,
19+ Key
1920} from 'lucide-react' ;
21+ import {
22+ Dialog ,
23+ DialogContent ,
24+ DialogDescription ,
25+ DialogFooter ,
26+ DialogHeader ,
27+ DialogTitle ,
28+ } from '@/shared/components/ui/dialog' ;
2029import {
2130 AlertDialog ,
2231 AlertDialogAction ,
@@ -62,6 +71,20 @@ export function ProvidersPage() {
6271 isPrivacyMode,
6372 togglePrivacyMode,
6473 openInBrowser,
74+ isDeletingAll,
75+ executeDeleteAll,
76+ showDeleteAllConfirmation,
77+ setShowDeleteAllConfirmation,
78+ copyRefreshToken,
79+
80+ // Copy All
81+ showCopyAllModal,
82+ setShowCopyAllModal,
83+ copyingAll,
84+ selectedProvidersForCopy,
85+ openCopyAllModal,
86+ toggleCopyProvider,
87+ executeCopyAll,
6588 } = useProvidersPresenter ( ) ;
6689
6790 if ( ! isAuthenticated ) {
@@ -99,6 +122,37 @@ export function ProvidersPage() {
99122 </ AlertDialogContent >
100123 </ AlertDialog >
101124
125+ < AlertDialog open = { showDeleteAllConfirmation } onOpenChange = { setShowDeleteAllConfirmation } >
126+ < AlertDialogContent className = "border-border/50" >
127+ < AlertDialogHeader >
128+ < AlertDialogTitle > { t ( 'common.confirm' , 'Are you sure?' ) } </ AlertDialogTitle >
129+ < AlertDialogDescription >
130+ { t ( 'providers.deleteAllConfirm' , 'This will permanently delete all connected accounts. This action cannot be undone.' ) }
131+ </ AlertDialogDescription >
132+ </ AlertDialogHeader >
133+ < AlertDialogFooter >
134+ < AlertDialogCancel disabled = { isDeletingAll } > { t ( 'common.cancel' ) } </ AlertDialogCancel >
135+ < AlertDialogAction
136+ onClick = { ( e ) => {
137+ e . preventDefault ( ) ;
138+ executeDeleteAll ( ) ;
139+ } }
140+ className = "bg-red-500 hover:bg-red-600 text-white"
141+ disabled = { isDeletingAll }
142+ >
143+ { isDeletingAll ? (
144+ < >
145+ < Loader2 className = "mr-2 h-4 w-4 animate-spin" />
146+ { t ( 'common.deleting' , 'Deleting...' ) }
147+ </ >
148+ ) : (
149+ t ( 'common.deleteAll' , 'Delete All' )
150+ ) }
151+ </ AlertDialogAction >
152+ </ AlertDialogFooter >
153+ </ AlertDialogContent >
154+ </ AlertDialog >
155+
102156 < div className = "flex items-center justify-between" >
103157 < h1 className = "text-3xl font-bold" > { t ( 'providers.title' ) } </ h1 >
104158
@@ -119,6 +173,28 @@ export function ProvidersPage() {
119173 < CheckCircle className = "h-5 w-5" />
120174 { t ( 'providers.connectedAccounts' ) } ({ files . length } )
121175 </ h2 >
176+ { files . length > 0 && (
177+ < div className = "flex items-center gap-2" >
178+ < Button
179+ variant = "outline"
180+ size = "sm"
181+ onClick = { openCopyAllModal }
182+ className = "h-8 text-xs"
183+ >
184+ < Key className = "mr-2 h-3.5 w-3.5" />
185+ { t ( 'common.copyAll' , 'Copy All' ) }
186+ </ Button >
187+ < Button
188+ variant = "outline"
189+ size = "sm"
190+ onClick = { ( ) => setShowDeleteAllConfirmation ( true ) }
191+ className = "h-8 text-xs bg-red-500/10 text-red-500 hover:bg-red-500/20 shadow-none border border-red-500/20"
192+ >
193+ < Trash2 className = "mr-2 h-3.5 w-3.5" />
194+ { t ( 'common.deleteAll' , 'Delete All' ) }
195+ </ Button >
196+ </ div >
197+ ) }
122198 </ div >
123199
124200 { filesError && (
@@ -216,15 +292,26 @@ export function ProvidersPage() {
216292 </ div >
217293 </ div >
218294
219- < Button
220- size = "icon"
221- variant = "ghost"
222- className = "h-8 w-8 text-destructive hover:bg-destructive/10 opacity-80 group-hover:opacity-100"
223- onClick = { ( ) => setFileToDelete ( file . id ) }
224- title = { t ( 'common.delete' ) }
225- >
226- < Trash2 className = "h-4 w-4" />
227- </ Button >
295+ < div className = "flex items-center gap-1" >
296+ < Button
297+ size = "icon"
298+ variant = "ghost"
299+ className = "h-8 w-8 text-primary hover:bg-primary/10 opacity-80 group-hover:opacity-100"
300+ onClick = { ( ) => copyRefreshToken ( file . name || file . filename || file . id ) }
301+ title = { t ( 'common.copyRefreshToken' , 'Copy Refresh Token' ) }
302+ >
303+ < Key className = "h-4 w-4" />
304+ </ Button >
305+ < Button
306+ size = "icon"
307+ variant = "ghost"
308+ className = "h-8 w-8 text-destructive hover:bg-destructive/10 opacity-80 group-hover:opacity-100"
309+ onClick = { ( ) => setFileToDelete ( file . id ) }
310+ title = { t ( 'common.delete' ) }
311+ >
312+ < Trash2 className = "h-4 w-4" />
313+ </ Button >
314+ </ div >
228315 </ div >
229316 ) ;
230317 } ) }
@@ -416,6 +503,60 @@ export function ProvidersPage() {
416503 } ) }
417504 </ div >
418505 </ section >
506+
507+ < Dialog open = { showCopyAllModal } onOpenChange = { setShowCopyAllModal } >
508+ < DialogContent className = "sm:max-w-[425px]" >
509+ < DialogHeader >
510+ < DialogTitle > { t ( 'providers.copyAllTitle' , 'Copy All Refresh Tokens' ) } </ DialogTitle >
511+ < DialogDescription >
512+ { t ( 'providers.selectProviders' , 'Select the providers you want to include in the copy.' ) }
513+ </ DialogDescription >
514+ </ DialogHeader >
515+ < div className = "py-4" >
516+ < div className = "space-y-4" >
517+ { groupedFiles . map ( ( [ providerId , group ] ) => (
518+ < div key = { providerId } className = "flex items-center space-x-2" >
519+ < input
520+ type = "checkbox"
521+ id = { `copy-${ providerId } ` }
522+ checked = { selectedProvidersForCopy . includes ( providerId ) }
523+ onChange = { ( ) => toggleCopyProvider ( providerId ) }
524+ className = "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary accent-primary"
525+ />
526+ < label
527+ htmlFor = { `copy-${ providerId } ` }
528+ className = "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70 flex items-center gap-2 cursor-pointer select-none"
529+ >
530+ < img
531+ src = { group . iconInfo . path }
532+ alt = { group . displayName }
533+ className = { `h-4 w-4 object-contain ${ group . iconInfo . needsInvert ? 'invert-on-dark' : '' } ` }
534+ />
535+ { group . displayName }
536+ < span className = "text-xs text-muted-foreground" > ({ group . files . length } )</ span >
537+ </ label >
538+ </ div >
539+ ) ) }
540+ </ div >
541+ </ div >
542+ < DialogFooter >
543+ < Button variant = "outline" onClick = { ( ) => setShowCopyAllModal ( false ) } > { t ( 'common.cancel' ) } </ Button >
544+ < Button onClick = { executeCopyAll } disabled = { copyingAll || selectedProvidersForCopy . length === 0 } >
545+ { copyingAll ? (
546+ < >
547+ < Loader2 className = "mr-2 h-4 w-4 animate-spin" />
548+ { t ( 'common.copying' , 'Copying...' ) }
549+ </ >
550+ ) : (
551+ < >
552+ < Key className = "mr-2 h-4 w-4" />
553+ { t ( 'common.copy' , 'Copy' ) }
554+ </ >
555+ ) }
556+ </ Button >
557+ </ DialogFooter >
558+ </ DialogContent >
559+ </ Dialog >
419560 </ div >
420561 ) ;
421562}
0 commit comments