import { useState } from "react";
import { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { FilePlus, Dices } from "lucide-react";
import { toast } from "sonner";
import { vault } from "@/lib/vault";
export function Add() {
const [docId, setDocId] = useState("");
const [keywords, setKeywords] = useState("");
const [isSynthesizing, setIsSynthesizing] = useState(false);
const [logs, setLogs] = useState<string[]>([]);
const generateId = () => {
// Generate a valid UUID v4 (simple version)
const uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
const r = Math.random() * 16 | 0, v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
setDocId(uuid);
};
const handleAdd = async () => {
if (!docId || !keywords) {
toast.error("Please provide both Document ID and Keywords");
return;
}
setIsSynthesizing(true);
setLogs(["[SWiSSSE] Initializing PrivacyVault...", "[SWiSSSE] Deriving MasterKeySet via Argon2id...", "[SWiSSSE] Preparing AEAD context (AES-256-GCM)..."]);
try {
const keywordList = keywords.split(',').map(k => k.trim().toLowerCase());
const result = await vault.addDocument(docId, keywordList);
setLogs(prev => [
...prev,
`[SWiSSSE] add_document() completed in ${result.time_ms.toFixed(2)}ms`,
`[SWiSSSE] Vol. Hiding: Wrote ${result.real_added} real, ${result.dummies_added} dummy entries.`,
`[SWiSSSE] Encrypted results persisted to IndexedDbStore.`
]);
toast.success("Document Encrypted & Persisted", {
description: `ID: ${docId}`,
});
console.log(`[SWiSSSE] add_document(${docId})`);
setDocId("");
setKeywords("");
} catch (e) {
toast.error("WASM Vault Error", { description: String(e) });
console.error(e);
setLogs(prev => [...prev, `[ERROR] ${String(e)}`]);
} finally {
setIsSynthesizing(false);
}
};
return (
<div className="max-w-4xl mx-auto px-4 py-12">
<div className="mb-8">
<h1 className="text-3xl font-bold tracking-tight mb-2 text-white">Add Document</h1>
<p className="text-slate-400">Encrypt and index your document into the Privacy Vault.</p>
</div>
<Card className="border-slate-800 shadow-2xl bg-slate-900/60 backdrop-blur-xl text-slate-100 relative overflow-hidden">
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-purple-500/5 pointer-events-none" />
<CardHeader className="relative z-10">
<CardTitle className="flex items-center text-xl text-white">
<FilePlus className="mr-2 h-5 w-5 text-blue-400" />
Index New Entry
</CardTitle>
<CardDescription className="text-slate-400">
Documents are encrypted entirely client-side. The untrusted server only sees ciphertexts.
</CardDescription>
</CardHeader>
<CardContent className="space-y-6 relative z-10">
<div className="space-y-2">
<Label htmlFor="docId" className="text-slate-300">Document ID</Label>
<div className="flex gap-3">
<Input
id="docId"
placeholder="e.g. valid-uuid-here"
value={docId}
onChange={(e) => setDocId(e.target.value)}
className="max-w-md bg-slate-950/50 border-slate-700 text-white placeholder:text-slate-600 focus-visible:ring-blue-500"
/>
<Button type="button" onClick={generateId} variant="outline" className="border-slate-700 bg-slate-800 hover:bg-slate-700 hover:text-white text-slate-300">
<Dices className="mr-2 h-4 w-4" />
Generate
</Button>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="keywords" className="text-slate-300">Keywords (comma-separated)</Label>
<Input
id="keywords"
placeholder="e.g. classification, secret, financials"
value={keywords}
onChange={(e) => setKeywords(e.target.value)}
className="max-w-md bg-slate-950/50 border-slate-700 text-white placeholder:text-slate-600 focus-visible:ring-blue-500"
/>
</div>
<div className="bg-slate-950/80 p-4 rounded-lg border border-slate-800 text-sm font-mono text-slate-400 flex flex-col space-y-1 shadow-inner max-h-48 overflow-y-auto">
<span className="text-emerald-600 dark:text-emerald-400 font-semibold mb-1">// Event Log</span>
{logs.length === 0 ? (
<>
<span>{'>'} SDK: Ready...</span>
<span>{'>'} SDK: Waiting for document input...</span>
</>
) : (
logs.map((log, i) => (
<span key={i} className={isSynthesizing && i === logs.length - 1 ? "text-blue-500 animate-pulse" : ""}>
{'>'} {log}
</span>
))
)}
</div>
</CardContent>
<CardFooter className="relative z-10 border-t border-slate-800/50 bg-slate-900/30 pt-6">
<Button onClick={handleAdd} disabled={isSynthesizing} className="w-full sm:w-auto bg-blue-600 hover:bg-blue-500 text-white shadow-lg shadow-blue-500/20">
{isSynthesizing ? "Encrypting..." : "Add to Vault"}
</Button>
</CardFooter>
</Card>
</div>
);
}