"use client"; import { FormEvent, useMemo, useState } from "react"; import { CheckCircle2, CircleAlert, Search, ShieldAlert, Siren } 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 type { ApiResult } from "@/modules/core/server/api"; import type { IncidentSeverity, IncidentStatus } from "@/modules/core/types"; import type { EmergencyIncident, EmergencyIncidentData } from "@/modules/emergency/types"; import { INCIDENT_SEVERITY_LABELS, INCIDENT_STATUS_LABELS } from "@/modules/emergency/types"; type EmergencyWorkspaceClientProps = { canManage: boolean; initialData: EmergencyIncidentData; }; const emptyCreateForm = { severity: "warning" as IncidentSeverity, title: "", description: "", source: "人工上报", }; function formatDateTime(value: string | undefined): string { return value ? new Date(value).toLocaleString("zh-CN") : "-"; } function severityVariant(severity: IncidentSeverity): "danger" | "secondary" | "warning" { if (severity === "critical") { return "danger"; } return severity === "warning" ? "warning" : "secondary"; } function statusVariant(status: IncidentStatus): "success" | "secondary" | "warning" { if (status === "open") { return "warning"; } return status === "resolved" || status === "closed" ? "success" : "secondary"; } export function EmergencyWorkspaceClient({ canManage, initialData }: EmergencyWorkspaceClientProps): React.ReactElement { const [data, setData] = useState(initialData); const [query, setQuery] = useState(""); const [statusFilter, setStatusFilter] = useState<"all" | IncidentStatus>("all"); const [severityFilter, setSeverityFilter] = useState<"all" | IncidentSeverity>("all"); const [message, setMessage] = useState(""); const [isPending, setIsPending] = useState(false); const [isCreateOpen, setIsCreateOpen] = useState(false); const [createForm, setCreateForm] = useState(emptyCreateForm); const normalizedQuery = query.trim().toLowerCase(); const filteredIncidents = useMemo( () => data.incidents.filter((incident) => { const matchesQuery = !normalizedQuery || [incident.title, incident.description, incident.source].join(" ").toLowerCase().includes(normalizedQuery); const matchesStatus = statusFilter === "all" || incident.status === statusFilter; const matchesSeverity = severityFilter === "all" || incident.severity === severityFilter; return matchesQuery && matchesStatus && matchesSeverity; }), [data.incidents, normalizedQuery, severityFilter, statusFilter], ); async function refreshData(): Promise { const response = await fetch("/api/emergency/incidents"); const result = (await response.json()) as ApiResult<{ data: EmergencyIncidentData }>; if (result.success) { setData(result.data); } } async function createIncident(event: FormEvent): Promise { event.preventDefault(); if (!canManage) { setMessage("当前角色无权创建应急事件"); return; } setIsPending(true); setMessage(""); const response = await fetch("/api/emergency/incidents", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(createForm), }); const result = (await response.json()) as ApiResult<{ incident: EmergencyIncident }>; setIsPending(false); setMessage(result.reason); if (result.success) { setIsCreateOpen(false); setCreateForm(emptyCreateForm); await refreshData(); } } async function updateStatus(incident: EmergencyIncident, status: IncidentStatus): Promise { if (!canManage) { setMessage("当前角色无权处理应急事件"); return; } setIsPending(true); setMessage(""); const response = await fetch(`/api/emergency/incidents/${incident.id}`, { method: "PATCH", headers: { "content-type": "application/json" }, body: JSON.stringify({ status }), }); const result = (await response.json()) as ApiResult<{ incident: EmergencyIncident }>; setIsPending(false); setMessage(result.reason); if (result.success) { await refreshData(); } } return (

安全应急

查看、确认、解决和关闭机构内安全应急事件。

setSeverityFilter(value as "all" | IncidentSeverity)} options={[ { value: "all", label: "全部级别" }, ...Object.entries(INCIDENT_SEVERITY_LABELS).map(([value, label]) => ({ value, label })), ]} value={severityFilter} />
{message ? (

{message}

) : null} 应急事件 状态处理会记录确认人、解决人和审计日志。 事件 级别 状态 来源 确认/解决 更新时间 操作 {filteredIncidents.map((incident) => (

{incident.title}

{incident.description}

{INCIDENT_SEVERITY_LABELS[incident.severity]} {INCIDENT_STATUS_LABELS[incident.status]} {incident.source} {incident.resolvedByName ?? incident.acknowledgedByName ?? "-"} {formatDateTime(incident.updatedAt)}
{incident.status === "open" ? ( ) : null} {incident.status === "open" || incident.status === "acknowledged" ? ( ) : null} {incident.status === "resolved" ? ( ) : null}
))} {filteredIncidents.length === 0 ? ( 暂无应急事件 ) : null}
setIsCreateOpen(false)} open={isCreateOpen} title="新建应急事件">
setCreateForm((current) => ({ ...current, title: event.target.value }))} required value={createForm.title} /> setCreateForm((current) => ({ ...current, source: event.target.value }))} value={createForm.source} />