'use client'; import { useState, useEffect, useCallback } from 'react'; import type { BackupItem, BackupTarget } from '@/app/api/admin/backups/route'; import * as Icons from './Icons'; import { useScrollLock } from './useScrollLock'; import { useModalDialog } from '@/hooks/useModalDialog'; interface Props { isOpen: boolean; onClose: () => void; onRestoreSuccess: () => void; } const TABS: { target: BackupTarget; label: string; file: string }[] = [ { target: 'gallery', label: 'Gallery', file: 'gallery.yaml' }, { target: 'settings', label: 'Settings', file: 'settings.yaml' }, { target: 'about', label: 'About', file: 'about.md' }, { target: 'journal', label: 'Journal', file: 'journal entries' }, ]; type BackupLists = Record; const EMPTY_LISTS: BackupLists = { gallery: [], settings: [], about: [], journal: [] }; export default function BackupManagerModal({ isOpen, onClose, onRestoreSuccess }: Props) { const [activeTab, setActiveTab] = useState('gallery'); const [showAllBackups, setShowAllBackups] = useState(false); const [backups, setBackups] = useState(EMPTY_LISTS); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [restoringFilename, setRestoringFilename] = useState(null); const [confirmItem, setConfirmItem] = useState(null); const [successMsg, setSuccessMsg] = useState(null); const fetchBackups = useCallback(async () => { setLoading(true); setError(null); try { const res = await fetch('/api/admin/backups'); if (!res.ok) { throw new Error('Failed to fetch backup history'); } const data = await res.json(); setBackups({ ...EMPTY_LISTS, ...data.backups }); } catch (err: any) { setError(err.message || 'Error loading backups'); } finally { setLoading(false); } }, []); useEffect(() => { if (isOpen) { fetchBackups(); setConfirmItem(null); setSuccessMsg(null); setShowAllBackups(false); } }, [isOpen, fetchBackups]); useScrollLock(isOpen); /* Before the early `return null`: hooks must not run conditionally. */ const cardRef = useModalDialog(onClose, isOpen); if (!isOpen) return null; async function handleRestore(item: BackupItem) { const filename = item.filename; setRestoringFilename(filename); setError(null); setSuccessMsg(null); try { const res = await fetch('/api/admin/backups', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ backupFilename: filename, target: item.target }), }); const data = await res.json(); if (!res.ok) { throw new Error(data.error || 'Failed to restore backup'); } setSuccessMsg( item.slug ? `Journal entry "${item.slug}" restored.` : `Backup restored successfully! (${filename})`, ); setConfirmItem(null); await fetchBackups(); onRestoreSuccess(); } catch (err: any) { setError(err.message || 'Error restoring backup'); } finally { setRestoringFilename(null); } } const currentList = backups[activeTab] || []; const INITIAL_VISIBLE_COUNT = 3; const visibleList = showAllBackups ? currentList : currentList.slice(0, INITIAL_VISIBLE_COUNT); const hiddenCount = currentList.length - INITIAL_VISIBLE_COUNT; return (
e.stopPropagation()} >

Backup History & Restoration

Restore an earlier version of your pages, settings, about page or journal — including deleted journal entries.

{/* Tab Selection */}
{TABS.map((tab) => ( ))}
{/* Status Messages */} {error &&
{error}
} {successMsg &&
{successMsg}
} {/* Confirmation Modal Section */} {confirmItem && (
Confirm Restoration

Are you sure you want to restore {confirmItem.filename}? A safety snapshot of your current state will be created automatically before reverting.

)} {/* Backup List Content */}
{loading ? (
Loading backups...
) : currentList.length === 0 ? (
No backups available yet for {TABS.find((t) => t.target === activeTab)?.file}
) : (
{visibleList.map((item) => { const dateObj = item.timestamp ? new Date(item.timestamp) : null; const formattedDate = dateObj && !isNaN(dateObj.getTime()) ? dateObj.toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'medium', }) : item.filename; return (
{item.slug && {item.slug}} {formattedDate} {item.isDeleted && ( Deleted entry )} {item.isPreRestore && ( Pre-Restore Snapshot )}
{item.filename}
); })} {currentList.length > INITIAL_VISIBLE_COUNT && ( )}
)}
); }