From 2f6fca64e1ea5936564398b897e723e9902b86d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9C=EC=A4=80?= <104981505+xeoxxn@users.noreply.github.com> Date: Mon, 28 Sep 2026 14:42:21 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=EB=B9=8C=EB=A6=B4=EA=B2=8C=20?= =?UTF-8?q?=EB=B0=98=EB=82=A9=20=EC=99=84=EB=A3=8C=20=ED=86=A0=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EA=B3=B5=EC=9A=A9=20ScreenToast=EB=A1=9C?= =?UTF-8?q?=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- .../components/BililgeReturnSection.tsx | 38 ++++-------- .../bililge/components/BililgeReturnToast.tsx | 60 ------------------- 2 files changed, 11 insertions(+), 87 deletions(-) delete mode 100644 src/features/bililge/components/BililgeReturnToast.tsx diff --git a/src/features/bililge/components/BililgeReturnSection.tsx b/src/features/bililge/components/BililgeReturnSection.tsx index 178d0a1..47cfdd5 100644 --- a/src/features/bililge/components/BililgeReturnSection.tsx +++ b/src/features/bililge/components/BililgeReturnSection.tsx @@ -1,22 +1,18 @@ import { Button, Divider, Typography } from "@wanteddev/wds"; -import { Fragment, useEffect, useRef, useState } from "react"; +import { Fragment, useState } from "react"; import rentalHistoryEmptyIllustration from "@/assets/icons/bililge-empty/rental-history.svg"; import returnItemsEmptyIllustration from "@/assets/icons/bililge-empty/return-items.svg"; +import ScreenToast from "@/components/ui/ScreenToast"; import BililgeEmptyState from "@/features/bililge/components/BililgeEmptyState"; import BililgeItemCard from "@/features/bililge/components/BililgeItemCard"; import BililgeRentalHistoryEntry from "@/features/bililge/components/BililgeRentalHistoryEntry"; import BililgeReturnConfirmModal from "@/features/bililge/components/BililgeReturnConfirmModal"; -import BililgeReturnToast from "@/features/bililge/components/BililgeReturnToast"; import { BILILGE_RENTAL_HISTORY, BILILGE_RETURN_ITEMS, } from "@/features/bililge/constants/bililgeReturns"; -// 토스트 자동 닫힘 시간 — Figma엔 지속시간이 없어서 WDS Toast의 duration="short" 기본값과 -// 맞췄다(node_modules/@wanteddev/wds/dist/components/toast/index.mjs, short=3000ms). -const TOAST_DURATION_MS = 3000; - interface BililgeReturnSectionProps { onBrowseRentals?: () => void; } @@ -35,32 +31,14 @@ interface BililgeReturnSectionProps { function BililgeReturnSection({ onBrowseRentals }: BililgeReturnSectionProps) { const [confirmOpen, setConfirmOpen] = useState(false); const [toastOpen, setToastOpen] = useState(false); - // 토스트가 떠 있는 동안 다시 확인해도 스크린리더가 재안내하도록, 확인마다 값을 바꿔 메시지 - // 텍스트를 새로 마운트한다(BililgeReturnToast의 messageKey로 전달). + // 토스트가 떠 있는 동안 다시 확인해도 스크린리더가 재안내하도록, 확인마다 값을 바꿔 + // ScreenToast를 새로 마운트한다(EventsApplicationScreen의 실패 토스트와 같은 패턴). const [toastToken, setToastToken] = useState(0); - // 타이머를 이펙트가 아니라 ref로 직접 관리한다 — 토스트가 이미 떠 있는 채로 다시 확인하면 - // "새 확인 시점부터 3초"가 되도록, 기존 타이머를 지우고 새로 시작해야 하기 때문이다. - const toastTimerRef = useRef | null>(null); - - useEffect(() => { - return () => { - if (toastTimerRef.current) { - clearTimeout(toastTimerRef.current); - } - }; - }, []); const handleConfirm = () => { setConfirmOpen(false); setToastOpen(true); setToastToken((token) => token + 1); - if (toastTimerRef.current) { - clearTimeout(toastTimerRef.current); - } - toastTimerRef.current = setTimeout( - () => setToastOpen(false), - TOAST_DURATION_MS, - ); }; return ( @@ -145,7 +123,13 @@ function BililgeReturnSection({ onBrowseRentals }: BililgeReturnSectionProps) { onConfirm={handleConfirm} open={confirmOpen} /> - + ); } diff --git a/src/features/bililge/components/BililgeReturnToast.tsx b/src/features/bililge/components/BililgeReturnToast.tsx deleted file mode 100644 index b3f06dc..0000000 --- a/src/features/bililge/components/BililgeReturnToast.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Typography } from "@wanteddev/wds"; -import { IconCircleCheckFill } from "@wanteddev/wds-icon"; -import { createPortal } from "react-dom"; - -import { useScreenSheetPortal } from "@/components/ui/useScreenSheetPortal"; - -interface BililgeReturnToastProps { - open: boolean; - // 토스트가 떠 있는 중에 다시 확인해도 스크린리더가 재안내하도록, 확인마다 바뀌는 값을 - // 메시지 텍스트의 key로 써서 강제로 다시 마운트한다. - messageKey: number; -} - -// Figma: 반납 신청 완료 토스트(nodeId 1133:50035, Toast/Toast) — WDS `useToast`/`Toast`는 -// 화면 전용 포털(#wds-region-manager-bottom)에 그려서 실제 뷰포트 기준으로 뜬다. 이 앱은 -// 데스크톱에서 폭이 고정된 컬럼을 화면 가운데에 세우는 구조라(App.tsx), WDS 토스트를 그대로 -// 쓰면 컬럼 밖 실제 뷰포트 하단에 떠버린다 — Bottom Nav를 WDS 대신 로컬로 다시 만든 것과 -// 같은 이유(wds-component-usage.md "Bottom Nav" 절 참고)로 BottomSheet와 같은 포털 슬롯에 -// 직접 그린다. 아이콘·타이포·배경(두 겹 반투명 레이어)은 Figma 그대로 옮겼다. -// -// role="status"/aria-live="polite"로 스크린리더에도 안내한다(코드리뷰 지적 반영). 메시지 -// 텍스트에 messageKey를 key로 줘서, 토스트가 떠 있는 중에 다시 확인해도(연속 확인) 텍스트가 -// 새로 마운트되며 재안내되게 한다. -function BililgeReturnToast({ open, messageKey }: BililgeReturnToastProps) { - const portalEl = useScreenSheetPortal(); - - if (!portalEl) { - return null; - } - - return createPortal( -
-
-
-
- - - 반납 신청이 완료됐어요. - -
-
, - portalEl, - ); -} - -export default BililgeReturnToast; From 5c232c29a8168973376c5749df52b136ba2a207f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9C=EC=A4=80?= <104981505+xeoxxn@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:46:35 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fix:=20=EB=B9=8C=EB=A6=B4=EA=B2=8C=20?= =?UTF-8?q?=EB=8C=80=EC=97=AC=20=EC=8B=9C=EC=9E=91=20=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=ED=9C=A0=20=ED=94=BC=EC=BB=A4=20=ED=96=89=20=EA=B0=84=EA=B2=A9?= =?UTF-8?q?=EC=9D=84=20Figma=EC=97=90=20=EB=A7=9E=EA=B2=8C=20=EC=A1=B0?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- .../bililge/components/BililgeRentalSheet.tsx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/features/bililge/components/BililgeRentalSheet.tsx b/src/features/bililge/components/BililgeRentalSheet.tsx index 719b737..20f5b34 100644 --- a/src/features/bililge/components/BililgeRentalSheet.tsx +++ b/src/features/bililge/components/BililgeRentalSheet.tsx @@ -35,6 +35,14 @@ const MINUTE_OPTIONS: WheelPickerOption[] = Array.from( (_, i) => ({ label: String(i).padStart(2, "0"), value: i }), ); +// 칸 사이 세로 간격(행 간 pitch)이 Figma보다 좁아 보인다는 QA 지적(디자인 QA 페이지 +// nodeId 2849:56598의 "빌릴게 대여 바텀시트" 비교 항목, 구현 스크린샷의 오전-오후 사이에 +// 빨간 화살표로 표시됨) — 기존 24는 선택 안 된 줄의 텍스트 블록 높이(17px 폰트 × line-height +// 1.412 ≈ 24)를 그대로 가져다 쓴 값이라, 실제 Figma의 줄 간 pitch(선택 줄 높이 26 + gap 6 + +// 비선택 줄 높이 24를 반씩 걸쳐 계산하면 ≈31px, Time Picker 스크린샷에서 실측해도 31px)보다 +// 작았다. WheelPicker 기본값(30)에 더 가까운 이 값으로 세 컬럼 모두 맞춘다. +const OPTION_ITEM_HEIGHT = 31; + // 선택되지 않은 칸은 옅게, 가운데 선택된 칸만 진하게 — 배경 하이라이트 바는 Time Picker 쪽에서 // 3개 컬럼 공통으로 하나 깔아주기 때문에 여기서는 텍스트 스타일만 다룬다. // 여기는 @ncdai/react-wheel-picker가 className 문자열만 받아서 자기 DOM에 그대로 꽂는 자리라 @@ -121,7 +129,7 @@ function BililgeRentalSheet({ item, open, onClose }: BililgeRentalSheetProps) { onValueChange={(period) => setTime((prev) => ({ ...prev, period })) } - optionItemHeight={24} + optionItemHeight={OPTION_ITEM_HEIGHT} options={PERIOD_OPTIONS} value={time.period} visibleCount={12} @@ -133,7 +141,7 @@ function BililgeRentalSheet({ item, open, onClose }: BililgeRentalSheetProps) { onValueChange={(hour) => setTime((prev) => ({ ...prev, hour })) } - optionItemHeight={24} + optionItemHeight={OPTION_ITEM_HEIGHT} options={HOUR_OPTIONS} value={time.hour} visibleCount={12} @@ -145,7 +153,7 @@ function BililgeRentalSheet({ item, open, onClose }: BililgeRentalSheetProps) { onValueChange={(minute) => setTime((prev) => ({ ...prev, minute })) } - optionItemHeight={24} + optionItemHeight={OPTION_ITEM_HEIGHT} options={MINUTE_OPTIONS} value={time.minute} visibleCount={12} From 7fba91e8a7f33a9318da5e05613d2c4e43a80a5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9C=EC=A4=80?= <104981505+xeoxxn@users.noreply.github.com> Date: Mon, 28 Sep 2026 17:46:40 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=EB=B9=8C=EB=A6=B4=EA=B2=8C=20?= =?UTF-8?q?=EB=B0=98=EB=82=A9=20=ED=99=94=EB=A9=B4=20=EC=83=81=EB=8B=A8=20?= =?UTF-8?q?=EC=97=AC=EB=B0=B1=C2=B7=EB=AC=BC=ED=92=88=20=EC=B9=B4=EB=93=9C?= =?UTF-8?q?=20=EA=B0=84=EA=B2=A9=20QA=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- src/features/bililge/components/BililgeReturnSection.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/features/bililge/components/BililgeReturnSection.tsx b/src/features/bililge/components/BililgeReturnSection.tsx index 47cfdd5..13c8f58 100644 --- a/src/features/bililge/components/BililgeReturnSection.tsx +++ b/src/features/bililge/components/BililgeReturnSection.tsx @@ -42,7 +42,7 @@ function BililgeReturnSection({ onBrowseRentals }: BililgeReturnSectionProps) { }; return ( -
+
{BILILGE_RETURN_ITEMS.length > 0 ? ( -
+
{BILILGE_RETURN_ITEMS.map((item) => ( Date: Tue, 29 Sep 2026 10:56:56 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=EB=B9=8C=EB=A6=B4=EA=B2=8C=20?= =?UTF-8?q?=EB=AA=A9=EC=97=85=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EC=97=90=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=EC=A0=95=EB=B3=B4=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- .../bililge/constants/bililgeItems.ts | 118 +++++++++++++++--- 1 file changed, 104 insertions(+), 14 deletions(-) diff --git a/src/features/bililge/constants/bililgeItems.ts b/src/features/bililge/constants/bililgeItems.ts index 4f2f477..d0cabd9 100644 --- a/src/features/bililge/constants/bililgeItems.ts +++ b/src/features/bililge/constants/bililgeItems.ts @@ -14,59 +14,149 @@ import sanitaryPad from "@/assets/icons/bililge-items/sanitary-pad.svg"; import umbrella from "@/assets/icons/bililge-items/umbrella.svg"; import usbCCharger from "@/assets/icons/bililge-items/usb-c-charger.svg"; +// BililgeCategoryFilter의 필터 칩과 값을 맞춘다("전체"는 필터 없음을 뜻하는 값이라 제외). +export type BililgeCategory = "전자기기" | "생활잡화" | "상비약" | "위생용품"; + export interface BililgeItem { id: string; name: string; quantity: number; icon: string; + category: BililgeCategory; } -// Figma: 빌릴게 Item Grid (nodeId 1243:73343) 순서·물품명·수량을 그대로 옮긴 목데이터 — 실 API 연동 전까지 사용 +// Figma: 빌릴게 Item Grid (nodeId 1243:73343) 순서·물품명·수량을 그대로 옮긴 목데이터 — 실 API 연동 전까지 사용. +// category는 Figma에 카테고리별 그룹핑이 따로 없어(칩 자체만 정의돼 있음) 물품명 기준으로 임의 +// 분류했다 — 실제 분류 기준이 정해지면 API 연동 시 교체한다. export const BILILGE_ITEMS: BililgeItem[] = [ - { icon: curlingIron, id: "curling-iron", name: "고데기", quantity: 28 }, { + category: "전자기기", + icon: curlingIron, + id: "curling-iron", + name: "고데기", + quantity: 28, + }, + { + category: "전자기기", icon: laptopCharger, id: "laptop-charger", name: "노트북 충전기(C타입)", quantity: 28, }, - { icon: hairDryer, id: "hair-dryer", name: "드라이기", quantity: 28 }, - { icon: mask, id: "mask", name: "마스크", quantity: 28 }, - { icon: bandAid, id: "band-aid", name: "밴드", quantity: 28 }, - { icon: powerBank, id: "power-bank", name: "보조배터리", quantity: 28 }, { + category: "전자기기", + icon: hairDryer, + id: "hair-dryer", + name: "드라이기", + quantity: 28, + }, + { + category: "위생용품", + icon: mask, + id: "mask", + name: "마스크", + quantity: 28, + }, + { + category: "상비약", + icon: bandAid, + id: "band-aid", + name: "밴드", + quantity: 28, + }, + { + category: "전자기기", + icon: powerBank, + id: "power-bank", + name: "보조배터리", + quantity: 28, + }, + { + category: "위생용품", icon: sanitaryPad, id: "sanitary-pad-large", name: "생리대(대형)", quantity: 28, }, { + category: "위생용품", icon: sanitaryPad, id: "sanitary-pad-small", name: "생리대(소형)", quantity: 28, }, - { icon: alcoholSwab, id: "alcohol-swab", name: "알콜스왑", quantity: 28 }, - { icon: umbrella, id: "umbrella", name: "우산", quantity: 28 }, - { icon: pill, id: "stomach-medicine", name: "위장약", quantity: 28 }, - { icon: pill, id: "easyen-6", name: "이지엔6", quantity: 28 }, - { icon: eyeDrops, id: "eye-drops", name: "인공눈물", quantity: 28 }, - { icon: pill, id: "cold-medicine", name: "종합감기약", quantity: 28 }, { + category: "상비약", + icon: alcoholSwab, + id: "alcohol-swab", + name: "알콜스왑", + quantity: 28, + }, + { + category: "생활잡화", + icon: umbrella, + id: "umbrella", + name: "우산", + quantity: 28, + }, + { + category: "상비약", + icon: pill, + id: "stomach-medicine", + name: "위장약", + quantity: 28, + }, + { + category: "상비약", + icon: pill, + id: "easyen-6", + name: "이지엔6", + quantity: 28, + }, + { + category: "상비약", + icon: eyeDrops, + id: "eye-drops", + name: "인공눈물", + quantity: 28, + }, + { + category: "상비약", + icon: pill, + id: "cold-medicine", + name: "종합감기약", + quantity: 28, + }, + { + category: "전자기기", icon: usbCCharger, id: "usb-c-cable", name: "케이블(C to C)", quantity: 28, }, - { icon: pill, id: "tylenol", name: "타이레놀", quantity: 28 }, { + category: "상비약", + icon: pill, + id: "tylenol", + name: "타이레놀", + quantity: 28, + }, + { + category: "상비약", icon: painReliefPatch, id: "pain-relief-patch", name: "파스", quantity: 28, }, - { icon: ointment, id: "ointment", name: "후시딘", quantity: 28 }, { + category: "상비약", + icon: ointment, + id: "ointment", + name: "후시딘", + quantity: 28, + }, + { + category: "전자기기", icon: eightPinCharger, id: "8pin-charger", name: "8핀 충전기", From 3b4370850939f626b46d543777a6513dd36cbe73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=84=9C=EC=A4=80?= <104981505+xeoxxn@users.noreply.github.com> Date: Tue, 29 Sep 2026 10:57:01 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20=EB=B9=8C=EB=A6=B4=EA=B2=8C=20?= =?UTF-8?q?=EC=B9=B4=ED=85=8C=EA=B3=A0=EB=A6=AC=20=ED=95=84=ED=84=B0?= =?UTF-8?q?=EA=B0=80=20=EB=AA=A9=EB=A1=9D=EC=9D=84=20=EC=8B=A4=EC=A0=9C?= =?UTF-8?q?=EB=A1=9C=20=EA=B1=B0=EB=A5=B4=EB=8F=84=EB=A1=9D=20=EC=97=B0?= =?UTF-8?q?=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5 --- src/features/bililge/BililgeListScreen.tsx | 4 +++- src/features/bililge/components/BililgeCategoryFilter.tsx | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/features/bililge/BililgeListScreen.tsx b/src/features/bililge/BililgeListScreen.tsx index 9408187..0546aed 100644 --- a/src/features/bililge/BililgeListScreen.tsx +++ b/src/features/bililge/BililgeListScreen.tsx @@ -63,7 +63,9 @@ function BililgeListScreen() {
{tab === "rent" ? (
- {BILILGE_ITEMS.map((item) => ( + {BILILGE_ITEMS.filter( + (item) => category === "전체" || item.category === category, + ).map((item) => (