Conversation
Walkthrough레이아웃에서 Outlet을 감싸던 main 래퍼를 제거하고 Outlet을 외부 flex 컨테이너의 직접 자식으로 옮겼다. 카드 편집 모달 컴포넌트와 날짜/시간 유효성 검사 유틸이 신규 추가되었고, 디자인 시스템의 InfoBox 루트 폭이 고정폭에서 Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor U as 사용자
participant M as CardEditModal
participant D as Dropdown/Popup
participant V as ValidateData
participant T as Toast
rect rgba(220,235,255,0.35)
U->>M: 모달 열기 / 입력 상호작용
U->>M: 카테고리 선택
M->>D: 드롭다운 표시
U->>D: "카테고리 추가" 선택
D-->>M: 팝업 열기/닫기
end
rect rgba(220,255,220,0.35)
U->>M: 날짜/시간 입력
M->>V: validateDate(value)
V-->>M: '' 또는 오류문자열
M->>V: validateTime(value)
V-->>M: '' 또는 오류문자열
M-->>U: 오류 표시(있을 경우)
end
rect rgba(255,235,220,0.35)
U->>M: 저장 클릭
M-->>T: 실패 토스트 오픈
T-->>T: 자동 종료(타이머)
end
sequenceDiagram
autonumber
participant App as App
participant L as Layout.tsx
participant S as Sidebar
participant O as Outlet
App->>L: 렌더
L->>S: Sidebar 렌더
L->>O: Outlet을 외부 flex 컨테이너의 직접 자식으로 렌더 (기존 main 래퍼 제거)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Pre-merge checks (2 passed, 3 warnings)❌ Failed checks (3 warnings)
✅ Passed checks (2 passed)
Poem
Tip 👮 Agentic pre-merge checks are now available in preview!Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.
Please see the documentation for more information. Example: reviews:
pre_merge_checks:
custom_checks:
- name: "Undocumented Breaking Changes"
mode: "warning"
instructions: |
Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).Please share your feedback with us on this Discord post. ✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Storybook chromatic 배포 확인: |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (8)
apps/client/src/layout/Layout.tsx (1)
6-11: 메인 영역 폭 오버플로 방지 및 모바일 뷰포트 대응
- 스크롤 가능한 메인 영역에
min-w-0이 없으면 내부 요소의truncate가 깨지거나 가로 스크롤이 생길 수 있습니다.- 모바일(iOS Safari 등)에서
h-screen대신h-dvh사용을 권장합니다.- <div className="flex h-screen"> + <div className="flex h-dvh"> <Sidebar /> - <main className="flex-1 overflow-y-auto"> + <main className="flex-1 min-w-0 overflow-y-auto overflow-x-hidden"> <Outlet /> </main> </div>packages/design-system/src/components/infobox/InfoBox.tsx (1)
9-9: 이미지 대체 텍스트 추가시각장애인 사용자 지원 및 접근성 향상을 위해
alt를 추가하세요. 장식용이라면 빈 문자열로 처리합니다.- <img className="h-[4.4rem] w-[4.4rem] rounded-[0.4rem]" src={imgUrl} /> + <img + className="h-[4.4rem] w-[4.4rem] rounded-[0.4rem]" + src={imgUrl} + alt="" + />apps/client/src/shared/utils/ValidateData.ts (2)
22-25: 미래 날짜 검증 로직 보완 제안현재는 날짜만 미래인지 확인합니다. 카드 편집 모달처럼 날짜+시간을 함께 사용하는 경우, “오늘”을 선택했을 때는 시간까지 현재 이후인지 함께 검증하는 통합 검증이 필요할 수 있습니다(컴포넌트 레벨에서 처리해도 무방).
2-6: 날짜 입력 포맷과 디자인 시스템 placeholder 불일치
ValidateData.ts(apps/client/src/shared/utils/ValidateData.ts)에서YYYY.MM.DD(4자리 연도)만 검증하는 반면, Design System DateTime 컴포넌트(packages/design-system/src/components/dateTime/DateTime.tsx:163)의 placeholder는YY.MM.DD(2자리 연도)를 사용하고 있습니다. UX 혼선을 방지하려면 placeholder를YYYY.MM.DD로 통일하거나, regex를 2자리 연도까지 허용하도록 확장하세요.apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (4)
56-61: 모달 레이블 제공으로 접근성 강화
role="dialog"에 레이블이 없어 스크린 리더에서 용도를 파악하기 어렵습니다. 간단히aria-label을 추가하세요(또는aria-labelledby로 제목 요소와 연결).- role="dialog" + role="dialog" + aria-label="카드 수정" aria-modal="true"
49-51: 리마인드 OFF 시 에러 상태 초기화스위치를 끄면 입력이 비활성화되지만 기존 에러 메시지는 그대로 남습니다. OFF 시 에러를 초기화하고, ON으로 전환 시 기존 값이 있다면 재검증하세요.
const handleSwitchChange = (checked: boolean) => { setIsRemindOn(checked); + if (!checked) { + setDateError(''); + setTimeError(''); + } else { + if (date) setDateError(validateDate(date)); + if (time) setTimeError(validateTime(time)); + } };
134-135: 비활성 상태에서의 불필요한 에러 메시지 노출 방지리마인드가 꺼져도 에러 문구가 그대로 렌더링됩니다.
isRemindOn조건을 추가하세요.- {dateError && <p className="body3-r text-error">{dateError}</p>} - {timeError && <p className="body3-r text-error">{timeError}</p>} + {isRemindOn && dateError && ( + <p className="body3-r text-error">{dateError}</p> + )} + {isRemindOn && timeError && ( + <p className="body3-r text-error">{timeError}</p> + )}
120-131: 시간 포맷(12h)과 검증(24h 범위 체크)의 정책 정합성 확인
DateTime은오전/오후 HH:MM(12시간제) 포맷을 사용하지만,validateTime은HH:MM(24시간 범위)로만 범위를 확인합니다. 현재는 단순 범위 체크라 실사용엔 문제 없을 수 있으나, 저장 시점에 24h로 변환/정규화가 필요하다면 정책을 명확히 해두는 것이 좋습니다.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
apps/client/src/layout/Layout.tsx(1 hunks)apps/client/src/shared/components/cardEditModal/CardEditModal.tsx(1 hunks)apps/client/src/shared/utils/ValidateData.ts(1 hunks)packages/design-system/src/components/infobox/InfoBox.tsx(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-15T20:00:13.756Z
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#80
File: apps/client/src/shared/components/ui/modalPop/ModalPop.tsx:36-41
Timestamp: 2025-07-15T20:00:13.756Z
Learning: In apps/client/src/shared/components/ui/modalPop/ModalPop.tsx, the InfoBox component uses hardcoded values for title, location, and icon URL as temporary test data. These should be replaced with dynamic data from props when implementing actual functionality and should be marked with TODO comments for future changes.
Applied to files:
packages/design-system/src/components/infobox/InfoBox.tsxapps/client/src/shared/components/cardEditModal/CardEditModal.tsx
🧬 Code graph analysis (2)
apps/client/src/layout/Layout.tsx (1)
apps/client/src/shared/components/sidebar/Sidebar.tsx (1)
Sidebar(27-150)
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (5)
apps/client/src/shared/utils/ValidateData.ts (2)
validateDate(1-27)validateTime(30-46)packages/design-system/src/lib/utils.ts (1)
cn(108-110)packages/design-system/src/components/dateTime/DateTime.tsx (1)
DateTime(56-176)packages/design-system/src/components/toast/hooks/uesFadeOut.tsx (1)
AutoDismissToast(12-49)packages/design-system/src/components/toast/Toast.tsx (1)
Toast(7-21)
🪛 GitHub Actions: ci
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx
[error] 28-28: TypeScript error TS6133: 'setTitle' is declared but its value is never read. (Command: tsc -b && vite build)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: storybook
| import { Icon } from '@pinback/design-system/icons'; | ||
| import { | ||
| AutoDismissToast, | ||
| Button, | ||
| DateTime, | ||
| Dropdown, | ||
| InfoBox, | ||
| PopupContainer, | ||
| Switch, | ||
| Textarea, | ||
| Toast, | ||
| validateDate, | ||
| validateTime, | ||
| } from '@pinback/design-system/ui'; | ||
| import { cn } from '@pinback/design-system/utils'; | ||
| import { useState } from 'react'; |
There was a problem hiding this comment.
유효성 함수 import 경로 오류 가능성
validateDate, validateTime를 Design System에서 import하고 있는데, 이번 PR에서 추가된 파일은 apps/client/src/shared/utils/ValidateData.ts입니다. 중복 정의나 빌드 실패의 원인이 될 수 있으니 실제 소스에서 가져오도록 경로를 정정하세요.
import {
AutoDismissToast,
Button,
DateTime,
Dropdown,
InfoBox,
PopupContainer,
Switch,
Textarea,
Toast,
- validateDate,
- validateTime,
} from '@pinback/design-system/ui';
import { cn } from '@pinback/design-system/utils';
import { useState } from 'react';
+import { validateDate, validateTime } from '../../utils/ValidateData';📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| import { Icon } from '@pinback/design-system/icons'; | |
| import { | |
| AutoDismissToast, | |
| Button, | |
| DateTime, | |
| Dropdown, | |
| InfoBox, | |
| PopupContainer, | |
| Switch, | |
| Textarea, | |
| Toast, | |
| validateDate, | |
| validateTime, | |
| } from '@pinback/design-system/ui'; | |
| import { cn } from '@pinback/design-system/utils'; | |
| import { useState } from 'react'; | |
| import { Icon } from '@pinback/design-system/icons'; | |
| import { | |
| AutoDismissToast, | |
| Button, | |
| DateTime, | |
| Dropdown, | |
| InfoBox, | |
| PopupContainer, | |
| Switch, | |
| Textarea, | |
| Toast, | |
| } from '@pinback/design-system/ui'; | |
| import { cn } from '@pinback/design-system/utils'; | |
| import { useState } from 'react'; | |
| import { validateDate, validateTime } from '../../utils/ValidateData'; |
🤖 Prompt for AI Agents
In apps/client/src/shared/components/cardEditModal/CardEditModal.tsx around
lines 1-16, the file currently imports validateDate and validateTime from the
design system but the project defines these utilities in
apps/client/src/shared/utils/ValidateData.ts; update the import to use the local
module instead of '@pinback/design-system/ui' (remove validateDate and
validateTime from that import list and import them from the correct relative
path, e.g. '../../utils/ValidateData' adjusted to the correct relative path),
ensuring there are no duplicate definitions and the build uses the local
implementations.
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx
Outdated
Show resolved
Hide resolved
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx
Outdated
Show resolved
Hide resolved
| const InfoBox = ({ title, source, imgUrl }: InfoBoxProps) => { | ||
| return ( | ||
| <div className="border-main400 flex h-[6.8rem] w-[24.8rem] items-center justify-between gap-[0.8rem] rounded-[4px] border bg-white px-[0.8rem] py-[1.2rem]"> | ||
| <div className="border-main400 flex h-[6.8rem] w-[full] items-center justify-between gap-[0.8rem] rounded-[4px] border bg-white px-[0.8rem] py-[1.2rem]"> |
There was a problem hiding this comment.
Tailwind 클래스 오타: w-[full]는 유효하지 않습니다
w-[full]는 임의 값 구문이지만 full은 길이 값이 아니므로 스타일이 적용되지 않습니다. 의도는 w-full로 보입니다.
- <div className="border-main400 flex h-[6.8rem] w-[full] items-center justify-between gap-[0.8rem] rounded-[4px] border bg-white px-[0.8rem] py-[1.2rem]">
+ <div className="border-main400 flex h-[6.8rem] w-full items-center justify-between gap-[0.8rem] rounded-[4px] border bg-white px-[0.8rem] py-[1.2rem]">📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="border-main400 flex h-[6.8rem] w-[full] items-center justify-between gap-[0.8rem] rounded-[4px] border bg-white px-[0.8rem] py-[1.2rem]"> | |
| <div className="border-main400 flex h-[6.8rem] w-full items-center justify-between gap-[0.8rem] rounded-[4px] border bg-white px-[0.8rem] py-[1.2rem]"> |
🤖 Prompt for AI Agents
In packages/design-system/src/components/infobox/InfoBox.tsx around line 8, the
Tailwind class contains a typo: `w-[full]` is an invalid arbitrary value usage
for width; replace it with the standard `w-full` class to restore the intended
full-width styling.
constantly-dev
left a comment
There was a problem hiding this comment.
수고하셨습니다! 토스트랑 이외 몇 개 코멘트 달았는데 생각보다 중요한 문제라 꼭 확인해주세요!! 👍
apps/client/src/layout/Layout.tsx
Outdated
| <div className="flex h-screen"> | ||
| <Sidebar /> | ||
| <Outlet /> | ||
| </> | ||
| <main className="flex-1 overflow-y-auto"> | ||
| <Outlet /> | ||
| </main> | ||
| </div> |
There was a problem hiding this comment.
해당 부분 제 레이아웃 PR에서 수정해서 없어도 괜찮습니다!
| validateDate, | ||
| validateTime, |
| } | ||
|
|
||
| export default function CardEditModal({ onClose }: CardEditModalProps) { | ||
| const [isRemindOn, setIsRemindOn] = useState<boolean>(true); |
There was a problem hiding this comment.
초기 값 작성하면 타입 추론되니 제네릭 필요 없을 것 같습니다.
| {toastIsOpen && ( | ||
| <div className="flex justify-center pt-[1.6rem]"> | ||
| <AutoDismissToast | ||
| duration={1000} | ||
| fadeMs={1000} | ||
| onClose={() => setToastIsOpen(false)} | ||
| > | ||
| <Toast text={`저장에 실패했어요.\n다시 시도해주세요`} /> | ||
| </AutoDismissToast> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
로직을 보다보니 AutoDismissToast가 toast가 open/close되는 상태를 담당하고 Toast가 UI를 담당하는 로직 같네요. 두 개의 역할을 분리해주신 부분은 너무 좋지만, 사용하는 곳마다 toastIsOpen state를 정의해야하는 것은 조금은 비효율적인 설계같다고 생각이 들어요.
AutoDismissToast가 open/close되는 상태를 내부에 가지고 있던데 이를 이용해서 toastIsOpen를 대체할 수 있지 않을까요? 혹은 useToast같은 훅으로 로직을 담당해서 open/close관련된 값들을 return하도록 해서 사용할 수도 있을 것 같아요.
참고할만한 민하님 라이브러리 첨부할게요!
Toast UI
Toast useToast
혹시나 스프린트 기간 내에 조금 많은 리소스가 들 것 같다면 이후에 꼭 리팩토링을 진행해봐요!!
There was a problem hiding this comment.
이 부분은 생각치도 못한 부분이었는데 좋은 의견 감사합니다-!! 시간 들여서 꼭 공부해보고싶은 부분이네요
| {toastIsOpen && ( | ||
| <div className="flex justify-center pt-[1.6rem]"> | ||
| <AutoDismissToast | ||
| duration={1000} | ||
| fadeMs={1000} | ||
| onClose={() => setToastIsOpen(false)} | ||
| > | ||
| <Toast text={`저장에 실패했어요.\n다시 시도해주세요`} /> | ||
| </AutoDismissToast> | ||
| </div> | ||
| )} |
There was a problem hiding this comment.
추가로 지금 올려주신 영상 보니 토스트가 모달과 형제 관계라 토스트가 열릴 때 모달이 위로 밀리는 현상이 발생하던데, 제 생각엔 이렇게 되면 안될 것 같은데 어떻게 생각하시나요?!
fixed를 통해 전체 화면 bottom 기준으로 설정하게 해도 좋을 것 같아요! 이 부분은 가능하다면 디자인 분들과 이야기를 해야할 것 같습니다!
이를 나중에는 위치 조정도 가능하게 toast interface에 추가해도 좋겠네요 👍
There was a problem hiding this comment.
fixed로 수정했습니다-!
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (1)
12-14: 유효성 함수 import 경로를 로컬 utils로 교체하세요현재 Design System에서
validateDate,validateTime를 가져오고 있으나, 본 리포지토리의apps/client/src/shared/utils/ValidateData.ts가 단일 소스로 보입니다. 중복/불일치 방지 및 향후 유지보수를 위해 로컬 utils에서 import하도록 수정하세요.import { AutoDismissToast, Button, DateTime, Dropdown, InfoBox, PopupContainer, Switch, Textarea, Toast, - validateDate, - validateTime, } from '@pinback/design-system/ui'; import { cn } from '@pinback/design-system/utils'; import { useState } from 'react'; +import { validateDate, validateTime } from '../../utils/ValidateData';
🧹 Nitpick comments (6)
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (6)
150-152: 리마인드 OFF일 때 에러 메시지 비노출비활성화 상태에서 에러 메시지가 보이면 UX가 어색합니다.
isRemindOn에 따라 에러 문구를 가드하세요.- {dateError && <p className="body3-r text-error">{dateError}</p>} - {timeError && <p className="body3-r text-error">{timeError}</p>} + {isRemindOn && dateError && ( + <p className="body3-r text-error">{dateError}</p> + )} + {isRemindOn && timeError && ( + <p className="body3-r text-error">{timeError}</p> + )}
61-63: 리마인드 토글 시 값/에러 상태 초기화OFF로 전환 시 입력값과 에러를 초기화하면 재활성화 시 혼동이 줄어듭니다.
- const handleSwitchChange = (checked: boolean) => { - setIsRemindOn(checked); - }; + const handleSwitchChange = (checked: boolean) => { + setIsRemindOn(checked); + if (!checked) { + setDate(''); + setTime(''); + setDateError(''); + setTimeError(''); + } + };
31-33: 카테고리 추가 로직 보완: trim/빈값 검사 + 리스트 반영 + 상태 리셋현재
saveCategory가 길이만 검사하고 리스트 반영/선택/상태 리셋이 없습니다. 또한 팝업을 열고 닫을 때 에러/입력값이 유지됩니다. 아래와 같이 보완을 제안합니다.- const [categories] = useState<string[]>([]); + const [categories, setCategories] = useState<string[]>([]); @@ const saveCategory = () => { - if (categoryTitle.length > 20) { + const trimmed = categoryTitle.trim(); + if (!trimmed) { + setIsPopError(true); + setErrorTxt('제목을 입력해주세요'); + return; + } + if (trimmed.length > 20) { setIsPopError(true); setErrorTxt('20자 이내로 작성해주세요'); - } else { - setIsPopupOpen(false); - } + return; + } + setCategories((prev) => + prev.includes(trimmed) ? prev : [...prev, trimmed] + ); + setSelected(trimmed); + setIsPopError(false); + setErrorTxt(''); + setCategoryTitle(''); + setIsPopupOpen(false); }; @@ - onLeftClick={() => setIsPopupOpen(false)} + onLeftClick={() => { + setIsPopError(false); + setErrorTxt(''); + setCategoryTitle(''); + setIsPopupOpen(false); + }} onRightClick={saveCategory} @@ - onAddItem={() => setIsPopupOpen(true)} + onAddItem={() => { + setIsPopError(false); + setErrorTxt(''); + setCategoryTitle(''); + setIsPopupOpen(true); + }}Also applies to: 42-49, 88-90, 113-115
68-74: dialog 접근성: 레이블 연결(aria-labelledby) 추가
role="dialog"에는 레이블이 필요합니다. 스크린리더용 제목을 추가하고aria-labelledby로 연결하세요.- <div + <div role="dialog" aria-modal="true" + aria-labelledby="card-edit-title" className={cn( 'w-[31.2rem] rounded-[1.2rem] bg-white px-[3.2rem] py-[2.4rem] shadow-xl', 'flex flex-col gap-[1.6rem]' )} > @@ - <header className="flex items-center justify-between"> + <header className="flex items-center justify-between"> + <h2 id="card-edit-title" className="sr-only"> + 카드 수정 + </h2> <Icon name="logo" width={72} height={20} /> <button type="button" aria-label="닫기" onClick={onClose} className="rounded-[0.6rem] p-[0.4rem] hover:bg-gray-100" > <Icon name="ic_close" size={20} /> </button> </header>Also applies to: 92-102
153-155: 저장 버튼 비활성화 조건 추가검증 실패 또는 필수값 미입력 시 저장 버튼을 비활성화하면 오동작/불필요한 토스트 노출을 줄일 수 있습니다.
- <Button onClick={() => setToastIsOpen(true)}>저장하기</Button> + <Button + disabled={ + isRemindOn && + (!!dateError || !!timeError || !date.trim() || !time.trim()) + } + onClick={() => setToastIsOpen(true)} + > + 저장하기 + </Button>
156-166: Toast 포지셔닝: fixed + z-index로 오버레이/레이아웃 간섭 방지현재
absolute는 컨테이너의position문맥에 따라 의도치 않은 위치/겹침이 발생할 수 있습니다. 오버레이/모달과 독립적으로 떠 있도록fixed와z-index를 사용하세요.- {toastIsOpen && ( - <div className="absolute bottom-[2.4rem] left-1/2 -translate-x-1/2"> + {toastIsOpen && ( + <div className="fixed bottom-[2.4rem] left-1/2 -translate-x-1/2 z-50"> <AutoDismissToast duration={1000} fadeMs={1000} onClose={() => setToastIsOpen(false)} > <Toast text={`저장에 실패했어요.\n다시 시도해주세요`} /> </AutoDismissToast> </div> )}
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
apps/client/src/layout/Layout.tsx(1 hunks)apps/client/src/shared/components/cardEditModal/CardEditModal.tsx(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/client/src/layout/Layout.tsx
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-07-15T20:00:13.756Z
Learnt from: constantly-dev
PR: Pinback-Team/pinback-client#80
File: apps/client/src/shared/components/ui/modalPop/ModalPop.tsx:36-41
Timestamp: 2025-07-15T20:00:13.756Z
Learning: In apps/client/src/shared/components/ui/modalPop/ModalPop.tsx, the InfoBox component uses hardcoded values for title, location, and icon URL as temporary test data. These should be replaced with dynamic data from props when implementing actual functionality and should be marked with TODO comments for future changes.
Applied to files:
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx
🧬 Code graph analysis (1)
apps/client/src/shared/components/cardEditModal/CardEditModal.tsx (5)
apps/client/src/shared/utils/ValidateData.ts (2)
validateDate(1-27)validateTime(30-46)packages/design-system/src/lib/utils.ts (1)
cn(108-110)packages/design-system/src/components/dateTime/DateTime.tsx (1)
DateTime(56-176)packages/design-system/src/components/toast/hooks/uesFadeOut.tsx (1)
AutoDismissToast(12-49)packages/design-system/src/components/toast/Toast.tsx (1)
Toast(7-21)
| const handleTimeChange = (value: string) => { | ||
| setTime(value); | ||
| setTimeError(validateTime(value)); | ||
| }; |
There was a problem hiding this comment.
time 입력에 대해 부모 onChange가 호출되지 않아 검증/상태가 동작하지 않습니다
Design System의 DateTime 구현(제공된 스니펫 기준)에서 type="time"은 내부 입력 이벤트를 모두 onBeforeInput/onKeyDown으로 처리하고, props.onChange는 호출하지 않습니다. 결과적으로:
handleTimeChange가 결코 호출되지 않아time/timeError상태가 갱신되지 않습니다.state={timeError ? 'error' : ...}와 에러 메시지 렌더링이 무의미해집니다.
해결 방안(권장): DS의 DateTime(time)에서 값 변경 시 부모로 표준화된 문자열(HH:MM 24h 등)을 전달하도록 수정하세요. 최소 수정 예시는 아래와 같습니다.
// packages/design-system/src/components/dateTime/DateTime.tsx (요지)
- const next = (timeDigits + add).slice(0, 4);
+ const next = (timeDigits + add).slice(0, 4);
setTimeDigits(next);
setInput(formatTime12(next));
+ props.onChange?.(digitsToHHMM(next)); // NEW: 부모로 24h "HH:MM" 전달
nextCaretRef.current = mapCaretByDigitsPos(next.length, 'time');
e.preventDefault();
... (delete/paste 처리 지점에도 동일하게 호출 추가)
+function digitsToHHMM(digits: string) {
+ const h = digits.slice(0, 2).padStart(2, '0');
+ const m = digits.slice(2, 4).padStart(2, '0');
+ return `${h}:${m}`;
+}DS 반영 전 임시 대응을 원하시면 말씀 주세요. UI에서 time 검증/표시를 비활성화하는 가드 적용안도 드릴 수 있습니다.
Also applies to: 142-147
📌 Related Issues
📄 Tasks
⭐ PR Point (To Reviewer)
현재 저장버튼 누르면 저장 실패 토스트 팝업이 나타납니다-! 위치 확인용이고 추후 api연결하면서 수정하고자합니다
밑 영상속 로고는 바꾸었습니다
다들 잘 알고 계실테지만 그래도 좀 더 사용하기 편하게 사용방법을 남깁니다
📷 Screenshot
2025-09-11.4.45.54.mov
Summary by CodeRabbit