diff --git a/src/features/bililge/components/BililgeItemCard.tsx b/src/features/bililge/components/BililgeItemCard.tsx index f0a83a7..826b65d 100644 --- a/src/features/bililge/components/BililgeItemCard.tsx +++ b/src/features/bililge/components/BililgeItemCard.tsx @@ -8,6 +8,10 @@ interface BililgeItemCardProps { icon: string; itemName: string; subtitle: string; + // 대여 바텀시트의 "대여할 물품" 카드(nodeId 1422:57184)에서만 subtitle 뒤에 반납 기한이 + // 강조(SemiBold) 서체로 붙는다 — "수량 28 · "(Regular)에 이어지는 "9/29까지 반납" 부분. + // 다른 호출부(메인 목록·반납 목록)는 안 넘기면 기존처럼 subtitle 전체가 Regular로 나온다. + subtitleEmphasis?: string; trailingControl?: "button" | "stepper"; actionLabel?: string; onRentRequest?: () => void; @@ -29,6 +33,7 @@ function BililgeItemCard({ icon, itemName, subtitle, + subtitleEmphasis, trailingControl = "button", actionLabel = "대여 신청", onRentRequest, @@ -61,6 +66,16 @@ function BililgeItemCard({ weight="regular" > {subtitle} + {subtitleEmphasis && ( + + {subtitleEmphasis} + + )} diff --git a/src/features/bililge/components/BililgeRentalSheet.tsx b/src/features/bililge/components/BililgeRentalSheet.tsx index 719b737..69887c9 100644 --- a/src/features/bililge/components/BililgeRentalSheet.tsx +++ b/src/features/bililge/components/BililgeRentalSheet.tsx @@ -97,8 +97,13 @@ function BililgeRentalSheet({ item, open, onClose }: BililgeRentalSheetProps) { onStepperIncrease={() => setStepperValue((value) => Math.min(item.quantity, value + 1)) } + // Figma: 대여할 물품 카드(nodeId 1422:57184, 3013:114608)에 "수량 28 · 오늘 + // 17시까지 반납" / "수량 28 · 9/29까지 반납"처럼 수량 뒤에 반납 기한이 강조 + // 서체(SemiBold)로 붙는다 — 기한 라벨(returnDeadlineLabel)은 서버가 오늘/내일/ + // 날짜를 미리 판단해 내려주는 완성된 문자열이라 여기서는 이어붙이기만 한다. stepperValue={stepperValue} - subtitle={`수량 ${item.quantity}`} + subtitle={`수량 ${item.quantity} · `} + subtitleEmphasis={item.returnDeadlineLabel} trailingControl="stepper" /> diff --git a/src/features/bililge/constants/bililgeItems.ts b/src/features/bililge/constants/bililgeItems.ts index 4f2f477..0dfb5cf 100644 --- a/src/features/bililge/constants/bililgeItems.ts +++ b/src/features/bililge/constants/bililgeItems.ts @@ -19,57 +19,165 @@ export interface BililgeItem { name: string; quantity: number; icon: string; + // 대여 바텀시트의 "대여할 물품" 카드(Figma nodeId 1422:57184, 3013:114608)에 "수량 28 · + // {returnDeadlineLabel}"로 붙는 반납 기한 문구 — "오늘 17시까지 반납"/"9/29까지 반납"처럼 + // 오늘·내일·그 이후(월/일)를 서버가 이미 판단해서 내려주는 값이라 가정한다. 오늘/내일 여부는 + // 보는 사람의 로컬 시각 기준이라 프런트에서 계산하는 게 일반적이지만, 이 서비스는 같은 화면을 + // 웹과 앱이 각각 구현해야 해서 "오늘/내일 판단 기준"이 플랫폼마다 어긋나지 않도록 서버가 + // 라벨 문자열까지 계산해서 내려주기로 했다 — 그래서 이 필드는 프런트가 다시 가공하지 않고 + // 그대로 렌더링만 하는 완성된 문자열이다. + returnDeadlineLabel: string; +} + +// 목데이터는 실 서버가 없어 위 필드를 흉내내는 값이다 — 실행 시점 기준으로 오늘/내일/그 이후 +// 라벨을 미리 계산해두되, 컴포넌트 쪽에는 이 계산 로직 자체를 노출하지 않는다(export하지 않음). +// 실 API가 붙으면 이 함수와 호출부는 통째로 지우고 응답 필드를 그대로 쓰면 된다. +// 오늘 마감은 시각이 물품마다 다른 게 아니라 "17시"로 고정된 정책값이라 파라미터로 받지 않는다. +function mockReturnDeadlineLabel(daysFromNow: number): string { + if (daysFromNow <= 0) { + return "오늘 17시까지 반납"; + } + if (daysFromNow === 1) { + return "내일까지 반납"; + } + const date = new Date(); + date.setDate(date.getDate() + daysFromNow); + return `${date.getMonth() + 1}/${date.getDate()}까지 반납`; } // Figma: 빌릴게 Item Grid (nodeId 1243:73343) 순서·물품명·수량을 그대로 옮긴 목데이터 — 실 API 연동 전까지 사용 export const BILILGE_ITEMS: BililgeItem[] = [ - { icon: curlingIron, id: "curling-iron", name: "고데기", quantity: 28 }, + { + icon: curlingIron, + id: "curling-iron", + name: "고데기", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(0), + }, { icon: laptopCharger, id: "laptop-charger", name: "노트북 충전기(C타입)", quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(1), + }, + { + icon: hairDryer, + id: "hair-dryer", + name: "드라이기", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(0), + }, + { + icon: mask, + id: "mask", + name: "마스크", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(3), + }, + { + icon: bandAid, + id: "band-aid", + name: "밴드", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(3), + }, + { + icon: powerBank, + id: "power-bank", + name: "보조배터리", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(1), }, - { 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 }, { icon: sanitaryPad, id: "sanitary-pad-large", name: "생리대(대형)", quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(0), }, { icon: sanitaryPad, id: "sanitary-pad-small", name: "생리대(소형)", quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(0), + }, + { + icon: alcoholSwab, + id: "alcohol-swab", + name: "알콜스왑", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(2), + }, + { + icon: umbrella, + id: "umbrella", + name: "우산", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(2), + }, + { + icon: pill, + id: "stomach-medicine", + name: "위장약", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(1), + }, + { + icon: pill, + id: "easyen-6", + name: "이지엔6", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(1), + }, + { + icon: eyeDrops, + id: "eye-drops", + name: "인공눈물", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(0), + }, + { + icon: pill, + id: "cold-medicine", + name: "종합감기약", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(1), }, - { 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 }, { icon: usbCCharger, id: "usb-c-cable", name: "케이블(C to C)", quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(0), + }, + { + icon: pill, + id: "tylenol", + name: "타이레놀", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(2), }, - { icon: pill, id: "tylenol", name: "타이레놀", quantity: 28 }, { icon: painReliefPatch, id: "pain-relief-patch", name: "파스", quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(2), + }, + { + icon: ointment, + id: "ointment", + name: "후시딘", + quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(1), }, - { icon: ointment, id: "ointment", name: "후시딘", quantity: 28 }, { icon: eightPinCharger, id: "8pin-charger", name: "8핀 충전기", quantity: 28, + returnDeadlineLabel: mockReturnDeadlineLabel(0), }, ];