import { useState } from "react"; import type { SiteConfig } from "@/lib/grabber/sites"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; type Props = { open: boolean; onOpenChange: (v: boolean) => void; onAdd: (site: SiteConfig) => void; }; const DEFAULT: SiteConfig = { id: "danbooru", name: "Danbooru", baseUrl: "https://danbooru.donmai.us", searchPath: "/posts.json", params: { tags: "tags", page: "page", limit: "limit" }, postsPath: "", fields: { id: "id", previewUrl: "preview_file_url", fileUrl: "file_url", ext: "file_ext", tags: "tag_string_general", source: "source", rating: "rating", score: "score", sampleUrl: "large_file_url", fileSize: "file_size", width: "image_width", height: "image_height", md5: "md5", uploader: "uploader_name", createdAt: "created_at", }, }; export function AddSiteDialog({ open, onOpenChange, onAdd }: Props) { const [s, setS] = useState(DEFAULT); const [headersText, setHeadersText] = useState(""); const [pasteSchemaText, setPasteSchemaText] = useState(""); const [parseError, setParseError] = useState(""); const set = (k: K, v: SiteConfig[K]) => setS((p) => ({ ...p, [k]: v })); const setField = (k: keyof SiteConfig["fields"], v: string) => setS((p) => ({ ...p, fields: { ...p.fields, [k]: v } })); const setParam = (k: keyof SiteConfig["params"], v: string) => setS((p) => ({ ...p, params: { ...p.params, [k]: v } })); const handlePasteSchema = () => { setParseError(""); if (!pasteSchemaText.trim()) return; try { const parsed = JSON.parse(pasteSchemaText); if (typeof parsed !== "object" || parsed === null) { throw new Error("Schema must be an object"); } const merged: SiteConfig = { id: parsed.id || s.id, name: parsed.name || s.name, baseUrl: parsed.baseUrl || s.baseUrl, searchPath: parsed.searchPath || s.searchPath, params: { tags: parsed.params?.tags || s.params.tags, page: parsed.params?.page || s.params.page, limit: parsed.params?.limit || s.params.limit, }, postsPath: typeof parsed.postsPath === "string" ? parsed.postsPath : s.postsPath, fields: { id: parsed.fields?.id || s.fields.id, previewUrl: parsed.fields?.previewUrl || s.fields.previewUrl, fileUrl: parsed.fields?.fileUrl || s.fields.fileUrl, ext: parsed.fields?.ext || s.fields.ext, tags: parsed.fields?.tags || s.fields.tags, source: parsed.fields?.source || s.fields.source, rating: parsed.fields?.rating || s.fields.rating, score: parsed.fields?.score || s.fields.score, sampleUrl: parsed.fields?.sampleUrl || s.fields.sampleUrl, fileSize: parsed.fields?.fileSize || s.fields.fileSize, width: parsed.fields?.width || s.fields.width, height: parsed.fields?.height || s.fields.height, md5: parsed.fields?.md5 || s.fields.md5, uploader: parsed.fields?.uploader || s.fields.uploader, createdAt: parsed.fields?.createdAt || s.fields.createdAt, }, }; if (parsed.headers && typeof parsed.headers === "object") { setHeadersText(JSON.stringify(parsed.headers, null, 2)); } setS(merged); setPasteSchemaText(""); } catch (e: unknown) { if (e instanceof Error) { setParseError(e.message); } else { setParseError("Failed to parse JSON"); } } }; const submit = () => { let headers: Record | undefined; if (headersText.trim()) { try { headers = JSON.parse(headersText); } catch { alert("Headers must be valid JSON"); return; } } onAdd({ ...s, headers }); onOpenChange(false); }; return ( Add Site (schema) {/* New Paste Schema Input Box */}