diff --git a/frontend/src/pages/VisitRequestListPage.tsx b/frontend/src/pages/VisitRequestListPage.tsx index 6015bf9..e26d8e5 100644 --- a/frontend/src/pages/VisitRequestListPage.tsx +++ b/frontend/src/pages/VisitRequestListPage.tsx @@ -1,13 +1,14 @@ import React, { useEffect, useRef, useState } from 'react'; import { Link, useNavigate } from 'react-router-dom'; -import { cancelVisitRequest, deleteVisitRequest, listVisitRequests, uploadVisitRequests } from '../api'; -import { VisitRequestView } from '../types'; +import { cancelVisitRequest, deleteVisitRequest, listTodayAccess, listVisitRequests, uploadVisitRequests } from '../api'; +import { AccessRecord, VisitRequestView } from '../types'; import { STATUS_CLASS, STATUS_LABEL, formatVisitRange } from '../status'; import { Dialog } from '../components/Dialog'; import { useAuth } from '../auth/AuthContext'; export const VisitRequestListPage: React.FC = () => { const [items, setItems] = useState([]); + const [accessRecords, setAccessRecords] = useState([]); const [error, setError] = useState(null); const [notice, setNotice] = useState(null); const [loading, setLoading] = useState(true); @@ -18,13 +19,35 @@ export const VisitRequestListPage: React.FC = () => { const load = () => { setLoading(true); - listVisitRequests() - .then(setItems) + Promise.all([ + listVisitRequests(), + listTodayAccess().catch(() => [] as AccessRecord[]), + ]) + .then(([requests, records]) => { + setItems(requests); + setAccessRecords(records); + }) .catch((e) => setError(e instanceof Error ? e.message : '조회 실패')) .finally(() => setLoading(false)); }; - useEffect(load, []); + useEffect(() => { + load(); + const t = setInterval(load, 3000); + return () => clearInterval(t); + }, []); + + const accessById = new Map(accessRecords.map((r) => [r.visitRequestId, r])); + const statusBadge = (r: VisitRequestView) => { + const access = accessById.get(r.id); + if (access?.inside) { + return { className: 'green', label: '재실중' }; + } + if (access?.checkOutAt) { + return { className: 'gray', label: '퇴장' }; + } + return { className: STATUS_CLASS[r.status], label: STATUS_LABEL[r.status] }; + }; const confirmCancel = async () => { const id = cancelingId; @@ -105,7 +128,12 @@ export const VisitRequestListPage: React.FC = () => { {r.purpose || '-'} {r.workName || '-'} {formatVisitRange(r.visitFrom, r.visitTo)} - {STATUS_LABEL[r.status]} + + {(() => { + const badge = statusBadge(r); + return {badge.label}; + })()} + {r.status === 'APPROVED' && (