import { useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { toast } from "sonner"; import { acknowledgeAnnouncement, fetchAnnouncements, type AnnouncementAcknowledgementStatus, type PortalAnnouncement } from "../api/announcements"; import { PageHeader } from "../components/common/PageHeader"; import { Button } from "../components/ui/button"; import { formatDisplayDate, formatDisplayDateTime } from "../lib/displayFormat"; function acknowledgementLabel(status: AnnouncementAcknowledgementStatus) { if (status === "acknowledged") return "已签收"; if (status === "overdue") return "已逾期"; if (status === "pending") return "待签收"; return "普通公告"; } function acknowledgementBadgeClass(status: AnnouncementAcknowledgementStatus) { if (status === "acknowledged") return "badge-success"; if (status === "overdue") return "badge-error"; if (status === "pending") return "badge-warning"; return "badge-neutral"; } function AnnouncementCard({ item, checked, submitting, onCheckedChange, onAcknowledge, }: { item: PortalAnnouncement; checked: boolean; submitting: boolean; onCheckedChange: (checked: boolean) => void; onAcknowledge: () => void; }) { const acknowledgement = item.acknowledgement ?? { status: "not_required", acknowledgedAt: null, overdue: false }; const requiresAcknowledgement = item.requiresAcknowledgement; const isAcknowledged = acknowledgement.status === "acknowledged"; const canSubmit = requiresAcknowledgement && !isAcknowledged && checked && !submitting; return (

{item.title}

{acknowledgementLabel(acknowledgement.status)}

{item.summary}

{item.acknowledgementDueAt ? (
截止:{formatDisplayDate(item.acknowledgementDueAt)}
) : null}
{item.linkUrl ? ( 查看链接 ) : null} {requiresAcknowledgement ? (
签收声明
{item.acknowledgementStatement}
{isAcknowledged ? (
已于 {formatDisplayDateTime(acknowledgement.acknowledgedAt)} 完成本人签收。
) : (
)}
) : null}
); } export function AnnouncementsPage() { const queryClient = useQueryClient(); const [checkedById, setCheckedById] = useState>({}); const announcementsQuery = useQuery({ queryKey: ["announcements", "public"], queryFn: () => fetchAnnouncements(false) }); const acknowledgeMutation = useMutation({ mutationFn: acknowledgeAnnouncement, onSuccess: async (_data, id) => { setCheckedById((current) => ({ ...current, [id]: false })); await queryClient.invalidateQueries({ queryKey: ["announcements"] }); toast.success("签收已记录"); }, onError: () => { toast.error("签收失败,请刷新后重试"); }, }); return (
{announcementsQuery.isLoading ?
公告加载中...
: null} {(announcementsQuery.data?.items ?? []).map((item) => ( setCheckedById((current) => ({ ...current, [item.id]: checked }))} onAcknowledge={() => acknowledgeMutation.mutate(item.id)} /> ))} {!announcementsQuery.isLoading && !(announcementsQuery.data?.items ?? []).length ? (
暂无公告
) : null}
); } export default AnnouncementsPage;