"use client";
/** Settings → Data: export (ZIP / JSON), import dropzone with progress polling, encrypted backups list + run/verify. */
import * as React from "react";
import Link from "next/link";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Archive, Database, Download, FileJson, FileUp, HardDrive, Play, RefreshCw, ShieldCheck, Upload } from "lucide-react";
import { api } from "@/lib/client/api";
import { cn, fmtDate, timeAgo } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import { Badge, Spinner, Tip } from "@/components/ui/primitives";
import { toast } from "@/components/ui/toast";
const ACCEPT = ".md,.markdown,.txt,.zip,.docx,.pdf,.csv,.bib,.json";
const fmtBytes = (n: number | null | undefined) => (n == null ? "–" : n < 1024 ? `${n} B` : n < 1048576 ? `${(n / 1024).toFixed(0)} KB` : n < 1073741824 ? `${(n / 1048576).toFixed(1)} MB` : `${(n / 1073741824).toFixed(2)} GB`);
type Progress = { jobId?: string; status: "queued" | "running" | "done" | "error"; progress: number; message?: string; pageId?: string | null; error?: string; stats?: { pages: number; databases: number; rows: number; attachments: number; references: number; skipped: string[] }; filename?: string; kind?: string };
type BackupJob = { id: string; kind: string; status: string; path: string | null; sizeBytes: number | null; sha256: string | null; offsite: boolean | null; error: string | null; startedAt: string; finishedAt: string | null };
type BackupsResp = { jobs: BackupJob[]; lastOk: BackupJob | null; config: { dir: string | null; offsite: string | null; keepDays: number; schedule: string; passphraseSet: boolean } };
function Section({ icon, title, hint, children, action }: { icon: React.ReactNode; title: string; hint?: string; children: React.ReactNode; action?: React.ReactNode }) {
return (
);
}
function ExportBlock() {
const items = [
{ href: "/api/export?format=zip", icon: , label: "Workspace ZIP", desc: "Markdown per page (folders mirror the tree), CSV per database, attachments/, references.bib, workspace.json" },
{ href: "/api/export?format=json", icon: , label: "JSON backup", desc: "Every content table, re-importable here (merge by id)" },
{ href: "/api/export?format=bib", icon: , label: "references.bib", desc: "All references as BibTeX" },
];
return (
{items.map((it) => (
{it.icon}
{it.label}{it.desc}
))}
Per page: use the page menu → Export (Markdown, PDF print view) or /api/export?format=md|pdf|zip|csv&pageId=….
);
}
function ImportBlock() {
const qc = useQueryClient();
const inputRef = React.useRef(null);
const [drag, setDrag] = React.useState(false);
const [busy, setBusy] = React.useState(null);
const [prog, setProg] = React.useState