One ProfileGenderField (女 / 男 / 不填, 44px, one hint line) in the people-archive edit form (saved with the form, 不填 → null) and in 设置 → 个人资料 for the owner (saves on its own PATCH; birth-data saves never send gender). The detail card shows 性别 (未填 when empty). Not asked in onboarding. DESIGN/VOICE updated; 390/1280 screenshots with fictional data. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
PROFILE_GENDER_HINT,
|
|
PROFILE_GENDER_LABELS,
|
|
PROFILE_GENDER_TITLE,
|
|
PROFILE_GENDER_UNSET_LABEL,
|
|
type ProfileGender,
|
|
} from "@/lib/profile-gender";
|
|
|
|
const OPTIONS: ReadonlyArray<{ readonly value: ProfileGender | null; readonly label: string }> = [
|
|
{ value: "female", label: PROFILE_GENDER_LABELS.female },
|
|
{ value: "male", label: PROFILE_GENDER_LABELS.male },
|
|
{ value: null, label: PROFILE_GENDER_UNSET_LABEL },
|
|
];
|
|
|
|
/**
|
|
* Optional gender (TASK-consult-gender-optional-20260927): 女 / 男 / 不填.
|
|
* 不填 is stored as null. One line under the choices says what it is for.
|
|
*/
|
|
export function ProfileGenderField({
|
|
value,
|
|
onChange,
|
|
idPrefix,
|
|
disabled = false,
|
|
}: {
|
|
readonly value: ProfileGender | null | undefined;
|
|
readonly onChange: (next: ProfileGender | null) => void;
|
|
readonly idPrefix: string;
|
|
readonly disabled?: boolean;
|
|
}) {
|
|
const titleId = `${idPrefix}-gender-title`;
|
|
const hintId = `${idPrefix}-gender-hint`;
|
|
const current = value ?? null;
|
|
return (
|
|
<div className="profile-gender-field">
|
|
<span className="profile-gender-title" id={titleId}>{PROFILE_GENDER_TITLE}</span>
|
|
<div className="profile-gender-options" role="radiogroup" aria-labelledby={titleId} aria-describedby={hintId}>
|
|
{OPTIONS.map((option) => {
|
|
const selected = option.value === current;
|
|
return (
|
|
<button
|
|
key={option.label}
|
|
className={`profile-gender-option${selected ? " is-selected" : ""}`}
|
|
type="button"
|
|
role="radio"
|
|
aria-checked={selected}
|
|
disabled={disabled}
|
|
onClick={() => {
|
|
if (!selected) onChange(option.value);
|
|
}}
|
|
>
|
|
{option.label}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
<p className="profile-gender-hint" id={hintId}>{PROFILE_GENDER_HINT}</p>
|
|
</div>
|
|
);
|
|
}
|