From cc79abde445552cceb89d4d868ff741e796b1d9c Mon Sep 17 00:00:00 2001 From: unknown <0420301@BOKNET.INTRA> Date: Thu, 16 Jul 2026 14:02:24 +0900 Subject: [PATCH] fix: confirm visit datetime picker selection --- docs/ACS작업일지.md | 11 ++++++ frontend/src/components/DateTimePicker.tsx | 44 ++++++++++++++-------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/docs/ACS작업일지.md b/docs/ACS작업일지.md index a533b08..aeba566 100644 --- a/docs/ACS작업일지.md +++ b/docs/ACS작업일지.md @@ -769,3 +769,14 @@ - `/api/admin/teams` 200 OK. - `/api/visit-requests/upload` 테스트 엑셀 업로드 성공: `totalRows=1`, `successCount=1`, `errors=[]`. - 업로드로 생성된 테스트 신청은 즉시 취소 처리. + +- 2026-07-16 출입신청 일시 선택 UI 수정: + - 운영 서버 출입신청 화면에서 출입일시 캘린더의 `[입력]` 버튼 클릭 시 선택값이 확정되지 않는 문제 확인. + - `DateTimePicker`를 확정형 동작으로 변경: + - 달력 내부 선택값은 `draft`로 보관. + - `[입력]` 버튼 클릭 시 `visitFrom`/`visitTo` 값으로 반영 후 팝업 닫기. + - 팝업 open 상태를 컴포넌트 state로 제어해 브라우저별 동작 차이를 줄임. + - 검증: + - `npm.cmd run typecheck` 성공. + - `npm.cmd run build` 성공. + - `npm.cmd test` 성공. diff --git a/frontend/src/components/DateTimePicker.tsx b/frontend/src/components/DateTimePicker.tsx index d08edea..fb0dd2b 100644 --- a/frontend/src/components/DateTimePicker.tsx +++ b/frontend/src/components/DateTimePicker.tsx @@ -1,4 +1,4 @@ -import React, { useRef } from 'react'; +import React, { useEffect, useState } from 'react'; import DatePicker, { registerLocale } from 'react-datepicker'; import { ko } from 'date-fns/locale'; import 'react-datepicker/dist/react-datepicker.css'; @@ -14,31 +14,43 @@ interface Props { const pad = (n: number) => String(n).padStart(2, '0'); -/** Date → "YYYY-MM-DDTHH:mm" (local), the format the form/back-end expect. */ function toLocalString(d: Date): string { return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`; } -/** - * Korean-localized date+time picker that replaces the browser-native - * (whose popup labels/border/buttons cannot be - * styled). Keeps the calendar open until the user presses [입력] so the choice - * is explicit. - */ +function fromLocalString(value: string): Date | null { + return value ? new Date(value) : null; +} + export const DateTimePicker: React.FC = ({ value, onChange, placeholder }) => { - const ref = useRef(null); + const [open, setOpen] = useState(false); + const [draft, setDraft] = useState(fromLocalString(value)); + + useEffect(() => { + setDraft(fromLocalString(value)); + }, [value]); + + const confirm = () => { + if (draft) { + onChange(toLocalString(draft)); + } + setOpen(false); + }; return ( d && onChange(toLocalString(d))} + selected={draft} + onChange={(d: Date | null) => setDraft(d)} + open={open} + onInputClick={() => setOpen(true)} + onCalendarOpen={() => setOpen(true)} + onClickOutside={() => setOpen(false)} showTimeSelect timeIntervals={5} timeCaption="시간" - timeFormat="a K:mm" - dateFormat="yyyy.MM.dd (eee) a K:mm" - dateFormatCalendar="yyyy.M월" + timeFormat="a h:mm" + dateFormat="yyyy.MM.dd (eee) a h:mm" + dateFormatCalendar="yyyy.MM" locale="ko" shouldCloseOnSelect={false} placeholderText={placeholder ?? '날짜와 시간을 선택하세요'} @@ -46,7 +58,7 @@ export const DateTimePicker: React.FC = ({ value, onChange, placeholder } popperClassName="acs-datepicker" > - ref.current?.setOpen(false)}> + 입력