diff --git a/backend/app/schemas/admin.py b/backend/app/schemas/admin.py
index ca0c781..fa52d2a 100644
--- a/backend/app/schemas/admin.py
+++ b/backend/app/schemas/admin.py
@@ -5,7 +5,7 @@ All admin-facing request/response shapes live here to keep the admin router
clean and testable in isolation.
"""
import re
-from datetime import datetime
+from datetime import date, datetime
from typing import Optional, Literal
from pydantic import BaseModel, ConfigDict, Field, field_validator
@@ -42,6 +42,7 @@ class UserListResponse(BaseModel):
class UserDetailResponse(UserListItem):
preferred_name: Optional[str] = None
+ date_of_birth: Optional[date] = None
must_change_password: bool = False
locked_until: Optional[datetime] = None
diff --git a/frontend/src/components/admin/UserDetailSection.tsx b/frontend/src/components/admin/UserDetailSection.tsx
index 0f09563..83bdc7a 100644
--- a/frontend/src/components/admin/UserDetailSection.tsx
+++ b/frontend/src/components/admin/UserDetailSection.tsx
@@ -117,6 +117,16 @@ export default function UserDetailSection({ userId, onClose }: UserDetailSection
+ {
+ 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}
+ />