diff --git a/frontend/app/trees/[id]/page.tsx b/frontend/app/trees/[id]/page.tsx
index 41e87e3..d5ce191 100644
--- a/frontend/app/trees/[id]/page.tsx
+++ b/frontend/app/trees/[id]/page.tsx
@@ -126,6 +126,12 @@ export default function FamilyViewPage() {
return data?.id ?? null;
}
+ // Create a new (blank) person and open their page to fill in details.
+ async function newPersonAndGo() {
+ const id = await addPerson("");
+ if (id) router.push(`/trees/${treeId}/persons/${id}`);
+ }
+
async function createFirst(e: React.FormEvent) {
e.preventDefault();
if (!firstName.trim()) return;
@@ -352,12 +358,17 @@ export default function FamilyViewPage() {
Family view
-
- Open {focus.primary_name ?? "person"} →
-
+
+
+
+ Open {focus.primary_name ?? "person"} →
+
+
{/* Pedigree: focus → parents → grandparents, with bracket connectors */}
diff --git a/frontend/app/trees/[id]/persons/[personId]/page.tsx b/frontend/app/trees/[id]/persons/[personId]/page.tsx
index 6dc665f..9389c6b 100644
--- a/frontend/app/trees/[id]/persons/[personId]/page.tsx
+++ b/frontend/app/trees/[id]/persons/[personId]/page.tsx
@@ -342,28 +342,47 @@ export default function PersonDetailPage() {
}
}
- async function addRel(e: React.FormEvent) {
- e.preventDefault();
- if (!relOther) return;
+ async function linkRelative(otherId: string): Promise {
let body: RelCreate;
if (relKind === "parent") {
- body = { type: "parent_child", person_from_id: relOther, person_to_id: personId, qualifier: relQual };
+ body = { type: "parent_child", person_from_id: otherId, person_to_id: personId, qualifier: relQual };
} else if (relKind === "child") {
- body = { type: "parent_child", person_from_id: personId, person_to_id: relOther, qualifier: relQual };
+ body = { type: "parent_child", person_from_id: personId, person_to_id: otherId, qualifier: relQual };
} else if (relKind === "partner") {
- body = { type: "partnership", person_from_id: personId, person_to_id: relOther };
+ body = { type: "partnership", person_from_id: personId, person_to_id: otherId };
} else {
- body = { type: "sibling", person_from_id: personId, person_to_id: relOther };
+ body = { type: "sibling", person_from_id: personId, person_to_id: otherId };
}
const { error } = await api.POST("/api/v1/trees/{tree_id}/relationships", {
params: { path: { tree_id: treeId } },
body,
});
- if (!error) {
+ return !error;
+ }
+
+ async function addRel(e: React.FormEvent) {
+ e.preventDefault();
+ if (!relOther) return;
+ if (await linkRelative(relOther)) {
setRelOther("");
load();
}
}
+
+ // Create a brand-new person, link them in the chosen role, then jump to their
+ // page so the user can fill in details immediately.
+ async function createRelativeAndGo(name: string) {
+ const toks = name.trim().split(/\s+/).filter(Boolean);
+ const given = toks.length > 1 ? toks.slice(0, -1).join(" ") : toks[0] ?? name.trim();
+ const surname = toks.length > 1 ? toks[toks.length - 1] : null;
+ const { data } = await api.POST("/api/v1/trees/{tree_id}/persons", {
+ params: { path: { tree_id: treeId } },
+ body: { given, surname },
+ });
+ if (!data) return;
+ await linkRelative(data.id);
+ router.push(`/trees/${treeId}/persons/${data.id}`);
+ }
async function removeRel(id: string) {
await api.DELETE("/api/v1/trees/{tree_id}/relationships/{relationship_id}", {
params: { path: { tree_id: treeId, relationship_id: id } },
@@ -1096,7 +1115,8 @@ export default function PersonDetailPage() {
people={others}
value={relOther}
onChange={setRelOther}
- placeholder="Search for a person…"
+ onCreate={createRelativeAndGo}
+ placeholder="Search, or type a new name…"
/>
{(relKind === "parent" || relKind === "child") && (