fix(ui): 테스트 피드백 반영 (출입증/보고서/상단바/대시보드/엑셀)

1. 출입증 카드 하단 안내문을 한 줄로(.badge-foot font-size 11px + nowrap).
2. 보고서 화면: 명칭 '리포트→보고서'·'방문 리포트→출입관리 보고서', 안내문 보강,
   날짜 입력을 YYYY.MM.DD 표시 커스텀 피커(DatePickerField)로 교체,
   기본값 시작일=이번 달 1일·종료일=오늘.
3. 보고서 엑셀 열 너비를 내용 기준(한글 2폭)으로 계산해 설정 → 셀 잘림 해소
   (POI autoSizeColumn의 CJK 과소측정 문제 회피).
4. 상단바에서 사용자 이름 표시 제거(역할 태그만 유지) → '발송내역' 메뉴 잘림 해소,
   발송내역 화면 [새로고침]→[조회].
5. 대시보드 '최근 출입 신청'에서 현재 재실 중인 방문자는 상태를 '재실중'으로 표시
   (기존 listInside API 재활용, 백엔드 무변경).

검증: 프론트 tsc+vite 빌드 통과, 백엔드 build+test(9건) 통과, 생성 xlsx의 열 너비가
내용에 맞게 설정됨(연락처15·출입일시18·상태10 등) 확인.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
unknown
2026-07-03 16:52:11 +09:00
parent d8ba443b35
commit da0d35ae7f
7 changed files with 117 additions and 26 deletions

View File

@@ -0,0 +1,43 @@
import React, { useRef } from 'react';
import DatePicker, { registerLocale } from 'react-datepicker';
import { ko } from 'date-fns/locale';
import 'react-datepicker/dist/react-datepicker.css';
registerLocale('ko', ko);
interface Props {
/** date string in "YYYY-MM-DD" (the format the back-end expects). */
value: string;
onChange: (value: string) => void;
placeholder?: string;
}
const pad = (n: number) => String(n).padStart(2, '0');
/** Date → "YYYY-MM-DD" (local). */
function toISODate(d: Date): string {
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
}
/**
* Korean-localized date-only picker. Displays the value as YYYY.MM.DD (which the
* native <input type="date"> cannot force — it follows the OS locale) while the
* bound value stays "YYYY-MM-DD" for the API.
*/
export const DatePickerField: React.FC<Props> = ({ value, onChange, placeholder }) => {
const ref = useRef<DatePicker>(null);
return (
<DatePicker
ref={ref}
selected={value ? new Date(`${value}T00:00:00`) : null}
onChange={(d: Date | null) => d && onChange(toISODate(d))}
dateFormat="yyyy.MM.dd"
dateFormatCalendar="yyyy.M월"
locale="ko"
placeholderText={placeholder ?? '날짜를 선택하세요'}
className="dt-input"
popperClassName="acs-datepicker"
/>
);
};

View File

@@ -32,14 +32,13 @@ export const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) =>
<NavLink to="/visit-requests"></NavLink>
{hasRole('ADMIN') && <NavLink to="/approvals"></NavLink>}
{hasRole('HOST', 'SECURITY', 'ADMIN') && <NavLink to="/access"></NavLink>}
{hasRole('SECURITY', 'ADMIN') && <NavLink to="/reports"></NavLink>}
{hasRole('SECURITY', 'ADMIN') && <NavLink to="/reports"></NavLink>}
{hasRole('ADMIN') && <NavLink to="/blacklist"></NavLink>}
{hasRole('ADMIN') && <NavLink to="/deliveries"></NavLink>}
{hasRole('ADMIN') && <NavLink to="/audit"></NavLink>}
</nav>
<div className="user-box">
<span className="user-name">
{user?.fullName}
<span className="role-tags">
{user?.roles.map((r) => (
<span key={r} className="role-tag">{ROLE_LABEL[r] ?? r}</span>