import { Box, Button, Checkbox, CircularProgress, FormControl, FormLabel, Input, Tab, TabList, TabPanel, Tabs, Typography, } from '@mui/joy' import { useCallback, useEffect, useRef, useState } from 'react' import { useResponsiveModal } from '../../../hooks/useResponsiveModal' import { CreateBackup, RestoreBackup } from '../../../utils/Fetcher' function BackupRestoreModal({ isOpen, onClose, showNotification }) { const { ResponsiveModal } = useResponsiveModal() const [activeTab, setActiveTab] = useState(0) const [loading, setLoading] = useState(false) const [error, setError] = useState('') // Backup state const [encryptionKey, setEncryptionKey] = useState('') const [backupName, setBackupName] = useState('') const [includeAssets, setIncludeAssets] = useState(true) // Restore state const [restoreEncryptionKey, setRestoreEncryptionKey] = useState('') const [backupFile, setBackupFile] = useState(null) const fileInputRef = useRef(null) const resetModal = useCallback(() => { setActiveTab(0) setEncryptionKey('') setBackupName('') setIncludeAssets(true) setRestoreEncryptionKey('') setBackupFile(null) setError('') setLoading(false) if (fileInputRef.current) { fileInputRef.current.value = '' } }, []) const handleClose = useCallback(() => { resetModal() onClose() }, [onClose, resetModal]) const downloadFile = (data, filename) => { const blob = new Blob([data], { type: 'application/octet-stream' }) const url = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = url link.download = filename document.body.appendChild(link) link.click() document.body.removeChild(link) window.URL.revokeObjectURL(url) } const handleCreateBackup = async () => { if (!encryptionKey.trim()) { setError('Encryption key is required') return } setLoading(true) setError('') try { const response = await CreateBackup( encryptionKey, includeAssets, backupName, ) if (response.ok) { const data = await response.json() const timestamp = new Date() .toISOString() .slice(0, 19) .replace(/:/g, '-') const filename = backupName ? `${backupName}-${timestamp}.backup` : `donetick-backup-${timestamp}.backup` // Download the backup file downloadFile(data.backup_data, filename) showNotification({ type: 'success', message: 'Backup created and downloaded successfully', }) handleClose() } else { const errorData = await response.json() setError(errorData.message || 'Failed to create backup') } } catch (err) { setError('Failed to create backup') } finally { setLoading(false) } } const handleFileUpload = event => { const file = event.target.files[0] if (file) { setBackupFile(file) setError('') } } const handleRestore = async () => { if (!restoreEncryptionKey.trim()) { setError('Encryption key is required') return } if (!backupFile) { setError('Please select a backup file') return } setLoading(true) setError('') try { const reader = new FileReader() reader.onload = async e => { try { const backupData = e.target.result const response = await RestoreBackup(restoreEncryptionKey, backupData) if (response.ok) { const data = await response.json() showNotification({ type: 'success', message: 'Backup restored successfully. Please refresh the page.', }) // Refresh the page after a short delay to allow user to see the message setTimeout(() => { window.location.reload() }, 2000) handleClose() } else { const errorData = await response.json() setError(errorData.message || 'Failed to restore backup') } } catch (err) { setError('Failed to restore backup') } finally { setLoading(false) } } reader.onerror = () => { setError('Failed to read backup file') setLoading(false) } reader.readAsText(backupFile) } catch (err) { setError('Failed to restore backup') setLoading(false) } } // Keyboard shortcuts useEffect(() => { const handleKeyDown = event => { if (!isOpen) return if (event.key === 'Escape') { event.preventDefault() handleClose() return } } if (isOpen) { document.addEventListener('keydown', handleKeyDown) } return () => { document.removeEventListener('keydown', handleKeyDown) } }, [isOpen, handleClose]) const renderBackupTab = () => ( Create an encrypted backup of your data. This backup will include all your chores, history, settings, and optionally your uploaded files. Encryption Key * setEncryptionKey(e.target.value)} placeholder='Enter a strong encryption key' /> Keep this key safe - you'll need it to restore your backup Backup Name (Optional) setBackupName(e.target.value)} placeholder='e.g., weekly-backup' /> setIncludeAssets(e.target.checked)} label='Include uploaded files and assets' /> {error && ( {error} )} ) const renderRestoreTab = () => ( Warning: Restoring a backup will replace all your current data. This action cannot be undone. Backup File * {backupFile && ( Selected: {backupFile.name} )} Encryption Key * setRestoreEncryptionKey(e.target.value)} placeholder='Enter the encryption key used for this backup' /> {error && ( {error} )} ) return ( {loading ? ( {activeTab === 0 ? 'Creating backup...' : 'Restoring backup...'} ) : ( setActiveTab(newValue)} > Create Backup Restore Backup {renderBackupTab()} {renderRestoreTab()} )} ) } export default BackupRestoreModal