Show date of birth with calculated age in IAM user detail

Adds date_of_birth to UserDetailResponse schema, AdminUserDetail
TypeScript type, and the User Information card in UserDetailSection.
Displays formatted date with age in parentheses (e.g. "3/02/2000 (26)").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Kyle 2026-03-02 19:58:21 +08:00
parent e8109cef6b
commit 3a456e56dd
3 changed files with 13 additions and 1 deletions

View File

@ -5,7 +5,7 @@ All admin-facing request/response shapes live here to keep the admin router
clean and testable in isolation. clean and testable in isolation.
""" """
import re import re
from datetime import datetime from datetime import date, datetime
from typing import Optional, Literal from typing import Optional, Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator from pydantic import BaseModel, ConfigDict, Field, field_validator
@ -42,6 +42,7 @@ class UserListResponse(BaseModel):
class UserDetailResponse(UserListItem): class UserDetailResponse(UserListItem):
preferred_name: Optional[str] = None preferred_name: Optional[str] = None
date_of_birth: Optional[date] = None
must_change_password: bool = False must_change_password: bool = False
locked_until: Optional[datetime] = None locked_until: Optional[datetime] = None

View File

@ -117,6 +117,16 @@ export default function UserDetailSection({ userId, onClose }: UserDetailSection
<DetailRow label="Last Name" value={user.last_name} /> <DetailRow label="Last Name" value={user.last_name} />
<DetailRow label="Email" value={user.email} /> <DetailRow label="Email" value={user.email} />
<DetailRow label="Preferred Name" value={user.preferred_name} /> <DetailRow label="Preferred Name" value={user.preferred_name} />
<DetailRow
label="Date of Birth"
value={user.date_of_birth ? (() => {
const dob = new Date(user.date_of_birth + 'T00:00:00');
const now = new Date();
let age = now.getFullYear() - dob.getFullYear();
if (now.getMonth() < dob.getMonth() || (now.getMonth() === dob.getMonth() && now.getDate() < dob.getDate())) age--;
return `${dob.toLocaleDateString()} (${age})`;
})() : null}
/>
<DetailRow <DetailRow
label="Created" label="Created"
value={getRelativeTime(user.created_at)} value={getRelativeTime(user.created_at)}

View File

@ -237,6 +237,7 @@ export interface AdminUser {
export interface AdminUserDetail extends AdminUser { export interface AdminUserDetail extends AdminUser {
active_sessions: number; active_sessions: number;
preferred_name?: string | null; preferred_name?: string | null;
date_of_birth?: string | null;
must_change_password?: boolean; must_change_password?: boolean;
locked_until?: string | null; locked_until?: string | null;
} }