"use client"; import Link from "next/link"; import { useParams } from "next/navigation"; import { useRef, useState } from "react"; import { api } from "@/lib/api/client"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; type Report = { counts: Record; unmapped_tags: string[] }; export default function GedcomPage() { const params = useParams<{ id: string }>(); const treeId = params.id; const [target, setTarget] = useState<"new" | "this">("new"); const [newName, setNewName] = useState(""); const [busy, setBusy] = useState(false); const [report, setReport] = useState(null); const [importedTreeId, setImportedTreeId] = useState(null); const fileRef = useRef(null); async function onFile(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; setBusy(true); setReport(null); setImportedTreeId(null); let tid = treeId; if (target === "new") { const { data } = await api.POST("/api/v1/trees", { body: { name: newName.trim() || "Imported tree" }, }); if (!data) { setBusy(false); return; } tid = data.id; setImportedTreeId(tid); } else { setImportedTreeId(treeId); } const fd = new FormData(); fd.append("file", file); const resp = await fetch(`/api/v1/trees/${tid}/gedcom/import`, { method: "POST", body: fd, credentials: "include", }); if (resp.ok) setReport(await resp.json()); setBusy(false); if (fileRef.current) fileRef.current.value = ""; } async function exportGed() { const resp = await fetch(`/api/v1/trees/${treeId}/gedcom/export`, { credentials: "include", }); if (!resp.ok) return; const blob = await resp.blob(); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "tree.ged"; a.click(); URL.revokeObjectURL(url); } return (

Import & export GEDCOM

Import a GEDCOM file {target === "new" && ( setNewName(e.target.value)} /> )} {target === "this" && (

Importing appends everyone in the file as new records — it does not merge with people already in this tree, so duplicates are possible.

)} {report && (
Import complete
{Object.entries(report.counts).map(([k, v]) => ( {v} {k} ))}
{report.unmapped_tags.length > 0 && (
Unmapped tags (skipped): {report.unmapped_tags.join(", ")}
)} {importedTreeId && ( Open the imported tree → )}
)}
Export this tree

Download this tree as a GEDCOM file — people, relationships, events, and sources.

); }