import { useState, useCallback, useEffect } from 'react'; import { Button, Descriptions, Form, Input, message, Modal, Popconfirm, Result, Select, Space, Spin, Table, Tabs, Tag, } from 'antd'; import type { ColumnsType } from 'antd/es/table'; import dayjs from 'dayjs'; import { useParams, useNavigate } from 'react-router-dom'; import { shiftApi, type Shift, type PatientAssignment, type HandoffLog, type CreatePatientAssignmentReq, type CreateHandoffReq, PERIOD_LABEL, SHIFT_STATUS_LABEL, SHIFT_STATUS_COLOR, CARE_LEVEL_OPTIONS, CARE_LEVEL_LABEL, CARE_LEVEL_COLOR, } from '../../api/health/shifts'; import { PageContainer } from '../../components/PageContainer'; import { usePermission } from '../../hooks/usePermission'; export default function ShiftDetail() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { hasPermission } = usePermission('health.shifts.manage'); const [shift, setShift] = useState(null); const [loading, setLoading] = useState(true); // Assignments const [assignments, setAssignments] = useState([]); const [assignLoading, setAssignLoading] = useState(false); const [assignModalOpen, setAssignModalOpen] = useState(false); const [editAssignment, setEditAssignment] = useState(null); const [assignForm] = Form.useForm(); // Handoff logs const [handoffs, setHandoffs] = useState([]); const [handoffLoading, setHandoffLoading] = useState(false); const [handoffModalOpen, setHandoffModalOpen] = useState(false); const [handoffForm] = Form.useForm(); const shiftId = id!; const fetchShift = useCallback(async () => { setLoading(true); try { const data = await shiftApi.get(shiftId); setShift(data); } catch { message.error('加载班次详情失败'); } finally { setLoading(false); } }, [shiftId]); const fetchAssignments = useCallback(async () => { setAssignLoading(true); try { const resp = await shiftApi.listAssignments(shiftId, { page: 1, page_size: 200 }); setAssignments(resp.data); } catch { message.error('加载患者分配失败'); } finally { setAssignLoading(false); } }, [shiftId]); const fetchHandoffs = useCallback(async () => { setHandoffLoading(true); try { const resp = await shiftApi.listHandoffs({ from_shift_id: shiftId }); setHandoffs(resp.data); } catch { message.error('加载交接记录失败'); } finally { setHandoffLoading(false); } }, [shiftId]); useEffect(() => { fetchShift(); fetchAssignments(); fetchHandoffs(); }, [fetchShift, fetchAssignments, fetchHandoffs]); // --- Assignment CRUD --- const handleAddAssignment = () => { setEditAssignment(null); assignForm.resetFields(); setAssignModalOpen(true); }; const handleEditAssignment = (record: PatientAssignment) => { setEditAssignment(record); assignForm.setFieldsValue({ patient_id: record.patient_id, care_level: record.care_level, notes: record.notes, }); setAssignModalOpen(true); }; const handleSubmitAssignment = async () => { try { const values = await assignForm.validateFields(); if (editAssignment) { await shiftApi.updateAssignment(shiftId, editAssignment.id, { care_level: values.care_level, notes: values.notes, version: editAssignment.version, }); message.success('分配已更新'); } else { await shiftApi.createAssignment(shiftId, values as CreatePatientAssignmentReq); message.success('患者已分配'); } setAssignModalOpen(false); fetchAssignments(); fetchShift(); } catch { // validation } }; const handleRemoveAssignment = async (record: PatientAssignment) => { try { await shiftApi.deleteAssignment(shiftId, record.id, record.version); message.success('已移除分配'); fetchAssignments(); fetchShift(); } catch { message.error('移除失败'); } }; // --- Handoff --- const handleCreateHandoff = () => { handoffForm.resetFields(); handoffForm.setFieldsValue({ from_shift_id: shiftId }); setHandoffModalOpen(true); }; const handleSubmitHandoff = async () => { try { const values = await handoffForm.validateFields(); await shiftApi.createHandoff(values as CreateHandoffReq); message.success('交接记录已创建'); setHandoffModalOpen(false); fetchHandoffs(); } catch { // validation } }; // --- Columns --- const assignColumns: ColumnsType = [ { title: '患者 ID', dataIndex: 'patient_id', width: 280, ellipsis: true }, { title: '护理等级', dataIndex: 'care_level', width: 120, render: (v: string) => {CARE_LEVEL_LABEL[v] ?? v}, }, { title: '备注', dataIndex: 'notes', width: 200, ellipsis: true, render: (v: string) => v ?? '-' }, { title: '操作', width: 140, render: (_, record) => hasPermission ? ( handleRemoveAssignment(record)}> ) : null, }, ]; const handoffColumns: ColumnsType = [ { title: '患者 ID', dataIndex: 'patient_id', width: 280, ellipsis: true }, { title: '目标班次', dataIndex: 'to_shift_id', width: 280, ellipsis: true }, { title: '交接备注', dataIndex: 'notes', width: 200, ellipsis: true, render: (v: string) => v ?? '-' }, { title: '交接时间', dataIndex: 'created_at', width: 170, render: (v: string) => dayjs(v).format('YYYY-MM-DD HH:mm'), }, ]; if (loading) return ; if (!shift) return ; if (!hasPermission) return ; return ( navigate('/health/shifts')} > {SHIFT_STATUS_LABEL[shift.status] ?? shift.status} {shift.patient_count ?? assignments.length} {shift.nurse_id ?? '-'} {shift.notes && {shift.notes}} rowKey="id" columns={assignColumns} dataSource={assignments} loading={assignLoading} pagination={false} size="small" /> ), }, { key: 'handoffs', label: `交接记录 (${handoffs.length})`, children: ( <> rowKey="id" columns={handoffColumns} dataSource={handoffs} loading={handoffLoading} pagination={false} size="small" /> ), }, ]} /> {/* Assignment Modal */} setAssignModalOpen(false)} width={480} >
); }