Person page: one-click sex setter (no edit mode) #39
@@ -535,6 +535,16 @@ export default function PersonDetailPage() {
|
|||||||
setEditingPerson(true);
|
setEditingPerson(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quick one-click sex setter — no need to open the full edit form. PATCH is
|
||||||
|
// exclude_unset on the backend, so sending only `gender` leaves the rest.
|
||||||
|
async function setGender(value: "male" | "female" | null) {
|
||||||
|
await api.PATCH("/api/v1/trees/{tree_id}/persons/{person_id}", {
|
||||||
|
params: { path: { tree_id: treeId, person_id: personId } },
|
||||||
|
body: { gender: value },
|
||||||
|
});
|
||||||
|
load();
|
||||||
|
}
|
||||||
|
|
||||||
async function savePerson() {
|
async function savePerson() {
|
||||||
const { error } = await api.PATCH("/api/v1/trees/{tree_id}/persons/{person_id}", {
|
const { error } = await api.PATCH("/api/v1/trees/{tree_id}/persons/{person_id}", {
|
||||||
params: { path: { tree_id: treeId, person_id: personId } },
|
params: { path: { tree_id: treeId, person_id: personId } },
|
||||||
@@ -711,16 +721,35 @@ export default function PersonDetailPage() {
|
|||||||
<h1 className="flex flex-wrap items-center gap-3 text-3xl font-semibold">
|
<h1 className="flex flex-wrap items-center gap-3 text-3xl font-semibold">
|
||||||
<span className="inline-flex items-center gap-2">
|
<span className="inline-flex items-center gap-2">
|
||||||
{person.primary_name ?? "Unnamed person"}
|
{person.primary_name ?? "Unnamed person"}
|
||||||
{person.gender === "male" && (
|
</span>
|
||||||
<span title="Male" aria-label="Male" style={{ color: "rgb(120, 159, 172)" }}>
|
{/* One-click sex setter — no edit mode needed. Active = current; click it again to clear. */}
|
||||||
♂
|
<span className="inline-flex items-center overflow-hidden rounded-md border border-[var(--border)] text-base font-normal">
|
||||||
</span>
|
<button
|
||||||
)}
|
type="button"
|
||||||
{person.gender === "female" && (
|
onClick={() => setGender(person.gender === "male" ? null : "male")}
|
||||||
<span title="Female" aria-label="Female" style={{ color: "rgb(196, 138, 146)" }}>
|
aria-pressed={person.gender === "male"}
|
||||||
♀
|
title={person.gender === "male" ? "Male — click to clear" : "Set male"}
|
||||||
</span>
|
className={`px-3 py-1 leading-none transition-colors ${
|
||||||
)}
|
person.gender === "male"
|
||||||
|
? "bg-[rgb(120,159,172)] text-white"
|
||||||
|
: "text-[var(--muted)] hover:bg-bronze/[0.07]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
♂
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setGender(person.gender === "female" ? null : "female")}
|
||||||
|
aria-pressed={person.gender === "female"}
|
||||||
|
title={person.gender === "female" ? "Female — click to clear" : "Set female"}
|
||||||
|
className={`border-l border-[var(--border)] px-3 py-1 leading-none transition-colors ${
|
||||||
|
person.gender === "female"
|
||||||
|
? "bg-[rgb(196,138,146)] text-white"
|
||||||
|
: "text-[var(--muted)] hover:bg-bronze/[0.07]"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
♀
|
||||||
|
</button>
|
||||||
</span>
|
</span>
|
||||||
{isSelf && (
|
{isSelf && (
|
||||||
<span className="rounded-full bg-bronze/15 px-2.5 py-1 text-xs font-medium text-bronze">
|
<span className="rounded-full bg-bronze/15 px-2.5 py-1 text-xs font-medium text-bronze">
|
||||||
|
|||||||
Reference in New Issue
Block a user