"use client"; import { FormEvent, useMemo, useState } from "react"; import { Brain, Edit3, Filter, Plus, Search, Trash2 } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Dialog } from "@/components/ui/dialog"; import { Input, Textarea } from "@/components/ui/input"; import { Select } from "@/components/ui/select"; import { Table } from "@/components/ui/table"; import { ElderAiAnalysisDialog } from "@/modules/ai/components/ElderAiAnalysisDialog"; import type { ApiResult } from "@/modules/core/server/api"; import type { Permission } from "@/modules/core/types"; import type { CareLevel, Elder, ElderInput, ElderStatus } from "@/modules/elders/types"; import { CARE_LEVEL_LABELS, ELDER_STATUS_LABELS, GENDER_LABELS } from "@/modules/elders/types"; import type { ElderOperationalContext, OperationalContextLine } from "@/modules/operations/types"; type EldersClientProps = { elderContexts?: Record; initialElders: Elder[]; permissions: Permission[]; }; type ElderFormState = Omit & { age: string; careLevel: CareLevel | ""; gender: ElderInput["gender"] | ""; status: ElderStatus | ""; }; const emptyInput: ElderFormState = { name: "", gender: "", age: "", careLevel: "", room: "", bed: "", status: "", primaryContact: "", phone: "", medicalNotes: "", }; const PAGE_SIZE = 8; const ALL_STATUS = "all"; const ALL_CARE_LEVELS = "all"; function elderToInput(elder: Elder): ElderFormState { return { name: elder.name, gender: elder.gender, age: String(elder.age), careLevel: elder.careLevel, room: elder.room, bed: elder.bed, status: elder.status, primaryContact: elder.primaryContact, phone: elder.phone, medicalNotes: elder.medicalNotes, }; } function formatDateTime(value: string): string { return new Date(value).toLocaleString("zh-CN"); } function formatBedLabel(elder: Pick): string { return elder.room && elder.bed ? `${elder.room}-${elder.bed}` : "-"; } const statusOptions = [ { value: ALL_STATUS, label: "全部状态" }, ...Object.entries(ELDER_STATUS_LABELS).map(([value, label]) => ({ value, label })), ]; const careLevelOptions = [ { value: ALL_CARE_LEVELS, label: "全部照护" }, ...Object.entries(CARE_LEVEL_LABELS).map(([value, label]) => ({ value, label })), ]; const genderOptions = [ { value: "", label: "选择性别", disabled: true }, ...Object.entries(GENDER_LABELS).map(([value, label]) => ({ value, label })), ]; const formCareLevelOptions = [ { value: "", label: "选择护理等级", disabled: true }, ...Object.entries(CARE_LEVEL_LABELS).map(([value, label]) => ({ value, label })), ]; const formStatusOptions = [ { value: "", label: "选择档案状态", disabled: true }, ...Object.entries(ELDER_STATUS_LABELS).map(([value, label]) => ({ value, label })), ]; function matchesFilters( elder: Elder, query: string, statusFilter: ElderStatus | typeof ALL_STATUS, careLevelFilter: CareLevel | typeof ALL_CARE_LEVELS, ): boolean { const normalizedQuery = query.trim().toLowerCase(); const matchesSearch = normalizedQuery.length === 0 || [elder.name, elder.room, elder.bed, elder.primaryContact, elder.phone, elder.medicalNotes] .join(" ") .toLowerCase() .includes(normalizedQuery); const matchesStatus = statusFilter === ALL_STATUS || elder.status === statusFilter; const matchesCareLevel = careLevelFilter === ALL_CARE_LEVELS || elder.careLevel === careLevelFilter; return matchesSearch && matchesStatus && matchesCareLevel; } export function EldersClient({ elderContexts = {}, initialElders, permissions }: EldersClientProps): React.ReactElement { const [elders, setElders] = useState(initialElders); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState(emptyInput); const [message, setMessage] = useState(""); const [isPending, setIsPending] = useState(false); const [isFormOpen, setIsFormOpen] = useState(false); const [deleteTarget, setDeleteTarget] = useState(null); const [query, setQuery] = useState(""); const [statusFilter, setStatusFilter] = useState(ALL_STATUS); const [careLevelFilter, setCareLevelFilter] = useState(ALL_CARE_LEVELS); const [page, setPage] = useState(1); const [aiTarget, setAiTarget] = useState(null); const canCreate = permissions.includes("elder:create"); const canUpdate = permissions.includes("elder:update"); const canDelete = permissions.includes("elder:delete"); const canUseAi = permissions.includes("ai:read") && permissions.includes("elder:read"); const isEditing = editingId !== null; const canSubmit = isEditing ? canUpdate : canCreate; const metrics = useMemo(() => { const totalCount = elders.length; const activeCount = elders.filter((elder) => elder.status === "active").length; const pendingCount = elders.filter((elder) => elder.status === "pending").length; const dischargedCount = elders.filter((elder) => elder.status === "discharged").length; return { activeCount, dischargedCount, pendingCount, totalCount }; }, [elders]); const filteredElders = useMemo( () => elders.filter((elder) => matchesFilters(elder, query, statusFilter, careLevelFilter)), [careLevelFilter, elders, query, statusFilter], ); const pageCount = Math.max(1, Math.ceil(filteredElders.length / PAGE_SIZE)); const safePage = Math.min(page, pageCount); const pageStart = (safePage - 1) * PAGE_SIZE; const visibleElders = filteredElders.slice(pageStart, pageStart + PAGE_SIZE); const editingElder = editingId ? elders.find((elder) => elder.id === editingId) : undefined; function updateField(key: K, value: ElderFormState[K]): void { setForm((current) => ({ ...current, [key]: value })); } function beginCreate(): void { setEditingId(null); setForm(emptyInput); setMessage(""); setIsFormOpen(true); } function beginEdit(elder: Elder): void { setEditingId(elder.id); setForm(elderToInput(elder)); setMessage(""); setIsFormOpen(true); } function closeForm(): void { if (isPending) { return; } setIsFormOpen(false); setEditingId(null); setForm(emptyInput); } function updateQuery(value: string): void { setQuery(value); setPage(1); } function updateStatusFilter(value: string): void { setStatusFilter(value as ElderStatus | typeof ALL_STATUS); setPage(1); } function updateCareLevelFilter(value: string): void { setCareLevelFilter(value as CareLevel | typeof ALL_CARE_LEVELS); setPage(1); } async function refreshElders(): Promise { const response = await fetch("/api/elders"); const result = (await response.json()) as ApiResult<{ elders: Elder[] }>; if (result.success) { setElders(result.elders); } } async function handleSubmit(event: FormEvent): Promise { event.preventDefault(); if (!canSubmit) { setMessage("当前角色无权保存老人档案"); return; } setIsPending(true); setMessage(""); if (!form.gender || !form.careLevel || !form.status) { setIsPending(false); setMessage("请完整选择性别、护理等级和档案状态"); return; } const numericAge = Number(form.age); if (!Number.isInteger(numericAge) || numericAge < 0 || numericAge > 130) { setIsPending(false); setMessage("年龄需为0到130之间的整数"); return; } const payload: ElderInput = { ...form, age: numericAge, careLevel: form.careLevel, gender: form.gender, status: form.status, }; const response = await fetch(isEditing ? `/api/elders/${editingId}` : "/api/elders", { method: isEditing ? "PATCH" : "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(payload), }); const result = (await response.json()) as ApiResult<{ elder: Elder }>; setIsPending(false); if (!result.success) { setMessage(result.reason); return; } await refreshElders(); setEditingId(null); setForm(emptyInput); setIsFormOpen(false); setMessage(result.reason); } async function handleDelete(): Promise { if (!deleteTarget) { return; } if (!canDelete) { setMessage("当前角色无权删除老人档案"); return; } setIsPending(true); setMessage(""); const response = await fetch(`/api/elders/${deleteTarget.id}`, { method: "DELETE" }); const result = (await response.json()) as ApiResult<{ elder: Elder }>; setIsPending(false); if (!result.success) { setMessage(result.reason); return; } await refreshElders(); setDeleteTarget(null); setMessage(result.reason); } return (

老人档案

列表管理、筛选搜索、分页、弹窗维护和审计持久化。

档案总数 {metrics.totalCount} 在院 {metrics.activeCount} 待入住 {metrics.pendingCount} 已退住 {metrics.dischargedCount}
档案列表 共 {filteredElders.length} 条匹配记录,按状态、照护等级和关键词快速定位。
{message ? (

{message}

) : null}
姓名 照护 床位 近期联动 联系人 状态 操作 {visibleElders.map((elder) => (

{elder.name}

{GENDER_LABELS[elder.gender]} · {elder.age} 岁

{CARE_LEVEL_LABELS[elder.careLevel]} {formatBedLabel(elder)} {elder.primaryContact} / {elder.phone} {ELDER_STATUS_LABELS[elder.status]}
{canUseAi ? ( ) : null}
))} {visibleElders.length === 0 ? ( 暂无匹配老人档案 ) : null}

第 {safePage} / {pageCount} 页

性别 updateField("age", event.target.value)} required type="number" value={form.age} />
护理等级 updateField("status", value as ElderFormState["status"])} options={formStatusOptions} required value={form.status} />
{isEditing ? (
当前床位:{editingElder ? formatBedLabel(editingElder) : "-"}
) : null}