fix: handle visit request Excel form upload

This commit is contained in:
unknown
2026-07-27 15:08:36 +09:00
parent eaecfae330
commit 6c511bf355
8 changed files with 231 additions and 16 deletions

View File

@@ -15,6 +15,7 @@ import {
LoginRequest,
PassDelivery,
PurposeCode,
ReportVisitView,
Role,
SmsDiagnosticsResult,
StatsSummary,
@@ -124,6 +125,8 @@ export const uploadVisitRequests = (file: File) => {
});
};
export const visitRequestTemplateUrl = () => '/api/visit-requests/template';
// ===== Approvals =====
export const approveRequest = (id: number, comment?: string) =>
request<VisitRequestView>(`/approvals/${id}/approve`, jsonInit('POST', { comment }));
@@ -168,7 +171,7 @@ export const deleteBlacklist = (id: number) =>
// ===== Reports =====
export const listReportVisits = (from: string, to: string) =>
request<VisitRequestView[]>(`/reports/visits?from=${from}&to=${to}`);
request<ReportVisitView[]>(`/reports/visits?from=${from}&to=${to}`);
export const reportDownloadUrl = (from: string, to: string) =>
`/api/reports/visits.xlsx?from=${from}&to=${to}`;

View File

@@ -153,7 +153,7 @@ function getDisplayStatus(request: VisitRequestView | null, access: AccessRecord
return { label: '재실중', className: 'green' };
}
if (access?.checkOutAt) {
return { label: '퇴', className: 'gray' };
return { label: '퇴', className: 'gray' };
}
if (!request) {
return { label: '-', className: 'gray' };

View File

@@ -115,7 +115,7 @@ export const DashboardPage: React.FC = () => {
{insideIds.has(r.id) ? (
<span className="badge badge-green"></span>
) : exitedIds.has(r.id) ? (
<span className="badge badge-gray"></span>
<span className="badge badge-gray"></span>
) : (
<span className={`badge badge-${STATUS_CLASS[r.status]}`}>{STATUS_LABEL[r.status]}</span>
)}

View File

@@ -1,7 +1,7 @@
import React, { useEffect, useState } from 'react';
import { listReportVisits, listTodayAccess, reportDownloadUrl } from '../api';
import { DatePickerField } from '../components/DatePickerField';
import { AccessRecord, VisitRequestView } from '../types';
import { AccessRecord, ReportVisitView } from '../types';
import { STATUS_CLASS, STATUS_LABEL, formatDateTime } from '../status';
const pad = (n: number) => String(n).padStart(2, '0');
@@ -20,7 +20,7 @@ function firstOfMonthISO(): string {
export const ReportPage: React.FC = () => {
const [from, setFrom] = useState(firstOfMonthISO());
const [to, setTo] = useState(todayISO());
const [items, setItems] = useState<VisitRequestView[]>([]);
const [items, setItems] = useState<ReportVisitView[]>([]);
const [accessRecords, setAccessRecords] = useState<AccessRecord[]>([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
@@ -45,13 +45,19 @@ export const ReportPage: React.FC = () => {
};
const accessById = new Map(accessRecords.map((r) => [r.visitRequestId, r]));
const statusBadge = (r: VisitRequestView) => {
const statusBadge = (r: ReportVisitView) => {
const access = accessById.get(r.id);
if (r.reportStatusLabel) {
return {
className: r.reportStatusLabel === '재실중' ? 'green' : r.reportStatusLabel === '퇴실' ? 'gray' : STATUS_CLASS[r.status],
label: r.reportStatusLabel,
};
}
if (access?.inside) {
return { className: 'green', label: '재실중' };
}
if (access?.checkOutAt) {
return { className: 'gray', label: '퇴' };
return { className: 'gray', label: '퇴' };
}
return { className: STATUS_CLASS[r.status], label: STATUS_LABEL[r.status] };
};

View File

@@ -1,6 +1,13 @@
import React, { useEffect, useRef, useState } from 'react';
import { Link, useNavigate } from 'react-router-dom';
import { cancelVisitRequest, deleteVisitRequest, listTodayAccess, listVisitRequests, uploadVisitRequests } from '../api';
import {
cancelVisitRequest,
deleteVisitRequest,
listTodayAccess,
listVisitRequests,
uploadVisitRequests,
visitRequestTemplateUrl,
} from '../api';
import { AccessRecord, VisitRequestView } from '../types';
import { STATUS_CLASS, STATUS_LABEL, formatVisitRange } from '../status';
import { Dialog } from '../components/Dialog';
@@ -44,7 +51,7 @@ export const VisitRequestListPage: React.FC = () => {
return { className: 'green', label: '재실중' };
}
if (access?.checkOutAt) {
return { className: 'gray', label: '퇴' };
return { className: 'gray', label: '퇴' };
}
return { className: STATUS_CLASS[r.status], label: STATUS_LABEL[r.status] };
};
@@ -97,7 +104,8 @@ export const VisitRequestListPage: React.FC = () => {
<div className="page-head">
<h2> </h2>
<div className="head-actions">
<button className="btn-ghost" onClick={() => fileRef.current?.click()}> </button>
<a className="btn-ghost" href={visitRequestTemplateUrl()}> </a>
<button className="btn-ghost" onClick={() => fileRef.current?.click()}> </button>
<input ref={fileRef} type="file" accept=".xlsx" hidden onChange={onUpload} />
<Link className="btn-primary" to="/visit-requests/new">+ </Link>
</div>

View File

@@ -158,6 +158,13 @@ export interface VisitRequestView {
createdAt: string;
}
export interface ReportVisitView extends VisitRequestView {
checkInAt?: string;
checkOutAt?: string;
inside?: boolean;
reportStatusLabel?: string;
}
export interface PublicPass {
visitorName: string;
company?: string;