"use client" import { useState } from "react" import { useVolumes, useRegisterVolume, useDeleteVolume, useLibraryPaths, useRegisterLibraryPath, useDeleteLibraryPath, } from "@/hooks/use-storage-admin" import { useEnqueueJob } from "@/hooks/use-jobs" import { useAuth } from "@/hooks/use-auth" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { Checkbox } from "@/components/ui/checkbox" import { Badge } from "@/components/ui/badge" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table" import { Spinner } from "@/components/ui/spinner" import { Separator } from "@/components/ui/separator" import { toast } from "sonner" import { FolderSyncIcon, Trash2Icon } from "lucide-react" export default function StoragePage() { const volumes = useVolumes() const paths = useLibraryPaths() const registerVolume = useRegisterVolume() const deleteVolume = useDeleteVolume() const registerPath = useRegisterLibraryPath() const deletePath = useDeleteLibraryPath() const enqueueJob = useEnqueueJob() const { user } = useAuth() const [volName, setVolName] = useState("") const [volUri, setVolUri] = useState("") const [volWritable, setVolWritable] = useState(true) const [pathVolumeId, setPathVolumeId] = useState("") const [pathRelative, setPathRelative] = useState("") const [pathIngest, setPathIngest] = useState(true) const [importUri, setImportUri] = useState("") const [importName, setImportName] = useState("") const [importing, setImporting] = useState(false) const handleCreateVolume = async (e: React.FormEvent) => { e.preventDefault() try { await registerVolume.mutateAsync({ volume_name: volName, uri_prefix: volUri, is_writable: volWritable, }) setVolName("") setVolUri("") toast.success("Volume registered") } catch { toast.error("Failed to register volume") } } const handleCreatePath = async (e: React.FormEvent) => { e.preventDefault() if (!user) return try { await registerPath.mutateAsync({ volume_id: pathVolumeId, relative_path: pathRelative, owner_id: user.id, is_ingest_destination: pathIngest, }) setPathRelative("") toast.success("Library path registered") } catch { toast.error("Failed to register library path") } } const handleImportLibrary = async (e: React.FormEvent) => { e.preventDefault() if (!user || !importUri) return setImporting(true) try { const vol = await registerVolume.mutateAsync({ volume_name: importName || "imported", uri_prefix: importUri, is_writable: false, }) const path = await registerPath.mutateAsync({ volume_id: vol.id, relative_path: "", owner_id: user.id, is_ingest_destination: false, }) await enqueueJob.mutateAsync({ job_type: "scan_directory", payload: { library_path_id: path.id }, }) setImportUri("") setImportName("") toast.success("Import started — check Jobs page for progress") } catch { toast.error("Import failed") } finally { setImporting(false) } } const volumeList = volumes.data ?? [] return (

Storage Management

{/* Import Library */} Import Library Point to an existing photo directory — registers a volume, library path, and starts scanning in one step
setImportName(e.target.value)} placeholder="family-photos" className="h-8 w-40" />
setImportUri(e.target.value)} placeholder="file:///mnt/nas/photos" className="h-8" />
{/* Volumes */} Volumes {volumes.isLoading ? ( ) : ( Name URI Prefix Writable {volumeList.map((v) => ( {v.volume_name} {v.uri_prefix} {v.is_writable ? "Yes" : "No"} ))}
)}
setVolName(e.target.value)} placeholder="local" className="h-8" />
setVolUri(e.target.value)} placeholder="file:///data/media" className="h-8" />
setVolWritable(c === true)} />
{/* Library Paths */} Library Paths {paths.isLoading ? ( ) : ( Volume Path Ingest Dest {(paths.data ?? []).map((p) => { const vol = volumeList.find((v) => v.id === p.volume_id) return ( {vol?.volume_name ?? p.volume_id.slice(0, 8) + "..."} {p.relative_path || "(root)"} {p.is_ingest_destination ? "Yes" : "No"} ) })}
)}
setPathRelative(e.target.value)} placeholder="(empty = root)" className="h-8" />
setPathIngest(c === true)} />
) }