Initial commit: IT센터 출입자관리시스템 (ACS)

방문자 사전신청·승인, 입·출입 체크인/아웃, QR 배지, 재실현황,
블랙리스트, 대시보드 통계, 방문 리포트(엑셀)까지 7단계 전 기능 구현.

- backend: Spring Boot 3.4.5 / Java 21 (JDK 26 빌드), 세션 인증, JPA, H2/PostgreSQL, POI, ZXing, Flyway
- frontend: React 19 / Vite 6 / TypeScript
- infra: Docker Compose (db·app·web nginx), Flyway V1__init, Python 사용자 시드
- docs: 워크플로우 / 시퀀스 다이어그램(Mermaid) / 이슈·유의사항

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
unknown
2026-07-03 08:59:41 +09:00
commit f0c30d8005
130 changed files with 8884 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import { getStatsSummary, listVisitRequests } from '../api';
import { StatsSummary, VisitRequestView } from '../types';
import { STATUS_CLASS, STATUS_LABEL, formatDateTime } from '../status';
export const DashboardPage: React.FC = () => {
const [items, setItems] = useState<VisitRequestView[]>([]);
const [stats, setStats] = useState<StatsSummary | null>(null);
const [error, setError] = useState<string | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
getStatsSummary().then(setStats).catch(() => setStats(null));
listVisitRequests()
.then(setItems)
.catch((e) => setError(e instanceof Error ? e.message : '조회 실패'))
.finally(() => setLoading(false));
}, []);
const recent = items.slice(0, 8);
return (
<div>
<div className="page-head">
<h2></h2>
<Link className="btn-primary" to="/visit-requests/new">+ </Link>
</div>
{error && <div className="alert alert-error">{error}</div>}
<div className="stat-grid">
<StatCard label="오늘 출입 예정" value={stats?.todayVisits ?? 0} accent="blue" />
<StatCard label="현재 재실" value={stats?.currentlyInside ?? 0} accent="green" />
<StatCard label="승인 대기" value={stats?.pending ?? 0} accent="amber" />
<StatCard label="전체 신청" value={stats?.total ?? 0} accent="gray" />
</div>
<div className="card">
<h3> </h3>
{loading ? (
<p className="muted"> </p>
) : recent.length === 0 ? (
<p className="muted"> .</p>
) : (
<table className="table">
<thead>
<tr>
<th></th><th></th><th></th><th> </th><th></th>
</tr>
</thead>
<tbody>
{recent.map((r) => (
<tr key={r.id}>
<td>{r.visitorName}</td>
<td>{r.company || '-'}</td>
<td>{r.zoneName || '-'}</td>
<td>{formatDateTime(r.visitFrom)}</td>
<td><span className={`badge badge-${STATUS_CLASS[r.status]}`}>{STATUS_LABEL[r.status]}</span></td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
);
};
const StatCard: React.FC<{ label: string; value: number; accent: string }> = ({ label, value, accent }) => (
<div className={`stat-card accent-${accent}`}>
<div className="stat-value">{value}</div>
<div className="stat-label">{label}</div>
</div>
);