@@ -12,7 +12,7 @@ import UploadManager from './components/UploadManager';
1212import MobileSidebar from './components/MobileSidebar' ;
1313import { MobileBreadcrumb } from './components/MobileBreadcrumb' ;
1414import { DropdownMenu , DropdownMenuTrigger , DropdownMenuContent , DropdownMenuItem , DropdownMenuSeparator } from '@/components/ui/dropdown-menu' ;
15- import { ArrowUpTrayIcon , FolderPlusIcon , Cog6ToothIcon , WrenchScrewdriverIcon } from '@heroicons/react/24/outline' ;
15+ import { ArrowDownTrayIcon , ArrowUpTrayIcon , FolderPlusIcon , Cog6ToothIcon , WrenchScrewdriverIcon } from '@heroicons/react/24/outline' ;
1616import apiService from './services/api.js' ;
1717
1818function UnlockScreen ( ) {
@@ -192,6 +192,7 @@ function Dashboard() {
192192 const [ showUploadManager , setShowUploadManager ] = useState ( false ) ;
193193 const [ uploadType , setUploadType ] = useState ( 'single' ) ;
194194 const [ refreshTrigger , setRefreshTrigger ] = useState ( 0 ) ;
195+ const [ downloadingVault , setDownloadingVault ] = useState ( false ) ;
195196 const { logout, volumeType, apiCall } = useAuth ( ) ;
196197
197198 // Handle file preview
@@ -221,6 +222,35 @@ function Dashboard() {
221222 toast . success ( 'Upload completed successfully!' ) ;
222223 } , [ ] ) ;
223224
225+ // Download the complete unlocked volume as a ZIP archive
226+ const handleDownloadVault = useCallback ( async ( ) => {
227+ if ( downloadingVault ) return ;
228+
229+ setDownloadingVault ( true ) ;
230+ try {
231+ const response = await apiCall ( '/api/download-vault' ) ;
232+ if ( ! response . ok ) {
233+ const data = await response . json ( ) . catch ( ( ) => null ) ;
234+ throw new Error ( data ?. message || 'Failed to download vault' ) ;
235+ }
236+
237+ const archive = await response . blob ( ) ;
238+ const downloadUrl = URL . createObjectURL ( archive ) ;
239+ const anchor = document . createElement ( 'a' ) ;
240+ anchor . href = downloadUrl ;
241+ anchor . download = 'kurpod-vault.zip' ;
242+ document . body . appendChild ( anchor ) ;
243+ anchor . click ( ) ;
244+ anchor . remove ( ) ;
245+ window . setTimeout ( ( ) => URL . revokeObjectURL ( downloadUrl ) , 0 ) ;
246+ toast . success ( 'Vault download started' ) ;
247+ } catch ( error ) {
248+ toast . error ( error . message || 'Failed to download vault' ) ;
249+ } finally {
250+ setDownloadingVault ( false ) ;
251+ }
252+ } , [ apiCall , downloadingVault ] ) ;
253+
224254 // Handle file delete
225255 const handleFileDelete = useCallback ( async ( file ) => {
226256 const encodedPath = file . path . split ( '/' ) . map ( segment => encodeURIComponent ( segment ) ) . join ( '/' ) ;
@@ -306,6 +336,7 @@ function Dashboard() {
306336 currentPath = { currentPath }
307337 onNavigate = { handleNavigate }
308338 onUploadStart = { handleUploadStart }
339+ onDownloadVault = { handleDownloadVault }
309340 onCompactStorage = { handleCompaction }
310341 />
311342
@@ -359,6 +390,14 @@ function Dashboard() {
359390 < FolderPlusIcon className = "w-4 h-4 mr-2" />
360391 < span > Folder</ span >
361392 </ button >
393+ < button
394+ onClick = { handleDownloadVault }
395+ disabled = { downloadingVault }
396+ className = "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 hover:bg-accent hover:text-accent-foreground h-9 px-4 py-2"
397+ >
398+ < ArrowDownTrayIcon className = "w-4 h-4 mr-2" />
399+ < span > { downloadingVault ? 'Preparing...' : 'Download Vault' } </ span >
400+ </ button >
362401 </ div >
363402
364403 { /* Storage Management Dropdown */ }
0 commit comments