Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthrough메뉴 선택부터 결제 완료까지의 구매 흐름을 구현합니다. 스토어 상세 페이지의 메뉴 클릭을 통해 새로운 구매 페이지로 라우팅하며, 구매 수량 선택, 결제 가이드 확인, 완료 모달 표시를 포함한 여러 컴포넌트를 추가합니다. Changes
Sequence DiagramsequenceDiagram
actor User as 사용자
participant StoreDetail as 스토어 상세<br/>(StoreDetailContent)
participant Router as 라우터
participant Purchase as 구매 페이지<br/>(PurchasePage)
participant PurchaseContent as 구매 UI<br/>(PurchaseContent)
participant GuideModal as 결제 가이드<br/>(PurchaseGuideModal)
participant CompleteModal as 완료 모달<br/>(PurchaseCompleteModal)
User->>StoreDetail: 메뉴 클릭
StoreDetail->>Router: navigate to /main/store/{id}/purchase?menuId={id}
Router->>Purchase: params와 searchParams 전달
Purchase->>Purchase: store 및 menu 조회 및 검증
Purchase->>PurchaseContent: store, menu props 전달
PurchaseContent->>PurchaseContent: count 상태 초기화
User->>PurchaseContent: 수량 증감
PurchaseContent->>PurchaseContent: count, totalPrice 업데이트
User->>PurchaseContent: "결제하기" 버튼 클릭
PurchaseContent->>GuideModal: isOpen = true
GuideModal->>User: 결제 가이드 모달 표시
User->>GuideModal: "결제하기" 확인
GuideModal->>PurchaseContent: onConfirm 콜백
PurchaseContent->>GuideModal: isOpen = false
PurchaseContent->>CompleteModal: isOpen = true
CompleteModal->>User: 주문 완료 모달 표시
User->>CompleteModal: "확인" 버튼 클릭
CompleteModal->>Router: navigate to /main
Router->>User: 메인 화면으로 이동
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~35 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
apps/customer/src/app/(tabs)/order/_constants/mockOrders.ts (1)
18-31: 중복 mock 객체는 생성 패턴으로 줄이는 것을 권장합니다.Line 18-31은 동일 필드 반복이라, 추후 필드 변경 시 누락 위험이 있습니다. 동작 이슈는 아니므로 선택적으로 정리해두면 유지보수가 쉬워집니다.
중복 제거 예시
+const IN_PROGRESS_ORDER_TEMPLATE: Omit<OrderItem, "id"> = { + storeName: "별동네 베이커리카페 별내본점", + orderSummary: "랜덤박스 Level.1 3개", + totalPrice: "15,000원", + pickupTime: "2/27 19:00 ~ 20:00", +}; + -export const IN_PROGRESS_ORDERS: OrderItem[] = [ - { id: 1, storeName: "별동네 베이커리카페 별내본점", orderSummary: "랜덤박스 Level.1 3개", totalPrice: "15,000원", pickupTime: "2/27 19:00 ~ 20:00" }, - { id: 2, storeName: "별동네 베이커리카페 별내본점", orderSummary: "랜덤박스 Level.1 3개", totalPrice: "15,000원", pickupTime: "2/27 19:00 ~ 20:00" }, - { id: 3, storeName: "별동네 베이커리카페 별내본점", orderSummary: "랜덤박스 Level.1 3개", totalPrice: "15,000원", pickupTime: "2/27 19:00 ~ 20:00" }, - { id: 4, storeName: "별동네 베이커리카페 별내본점", orderSummary: "랜덤박스 Level.1 3개", totalPrice: "15,000원", pickupTime: "2/27 19:00 ~ 20:00" }, -]; +export const IN_PROGRESS_ORDERS: OrderItem[] = [1, 2, 3, 4].map((id) => ({ + id, + ...IN_PROGRESS_ORDER_TEMPLATE, +}));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/order/_constants/mockOrders.ts around lines 18 - 31, Several mock order objects in MOCK_ORDERS are duplicated; create a small factory to generate orders and replace repeated literal objects to reduce duplication and future bugs: add a helper function (e.g., makeMockOrder or createMockOrder) that accepts an overrides object and returns the common shape (id, storeName, orderSummary, totalPrice, pickupTime), then use it to produce the entries in the MOCK_ORDERS array (calling makeMockOrder({ id: 3, ... }) and makeMockOrder({ id: 4, ... })) so only differing fields need to be specified while shared fields live in one place.apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseOrderCard.tsx (1)
57-62: 감소 버튼도 하한선(0)에서 비활성화해 방어적으로 처리하는 게 좋습니다.증가 버튼은 상한을 막고 있으므로, 감소 버튼도
count <= 0에서 비활성화하면 상태 안정성이 맞춰집니다.수정 제안
<button type="button" onClick={onDecrease} className="flex h-[2.8rem] w-[2.8rem] items-center justify-center" aria-label="수량 감소" + disabled={count <= 0} >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseOrderCard.tsx around lines 57 - 62, In PurchaseOrderCard, disable the decrease button when count <= 0 to match the upper-bound guard: update the decrease button element that uses onDecrease so it sets disabled={count <= 0} (and/or aria-disabled) and ensure the onDecrease handler short-circuits if count <= 0 to avoid side-effects; reference the decrease button element, the onDecrease handler and the count prop/state in PurchaseOrderCard when making the change.apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseGuideModal.tsx (1)
21-23: 모달에dialog접근성 속성을 추가하는 것을 권장합니다.스크린리더 문맥 인지를 위해
role="dialog",aria-modal,aria-labelledby를 명시하면 더 안전합니다.수정 제안
- <div className="fixed inset-0 z-50 flex items-center bg-default/50 px-[1.6rem]"> - <div className="h-[26.5rem] w-full rounded-[10px] border border-primary bg-inverse px-[1.6rem] py-[1rem]"> + <div className="fixed inset-0 z-50 flex items-center bg-default/50 px-[1.6rem]"> + <div + role="dialog" + aria-modal="true" + aria-labelledby="purchase-guide-title" + className="h-[26.5rem] w-full rounded-[10px] border border-primary bg-inverse px-[1.6rem] py-[1rem]" + > @@ - <h3 className="ml-[0.4rem] head3-m text-default"> + <h3 id="purchase-guide-title" className="ml-[0.4rem] head3-m text-default"> 카카오 계좌이체 안내 </h3>Also applies to: 47-49
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseGuideModal.tsx around lines 21 - 23, The modal markup in PurchaseGuideModal lacks accessibility attributes; add role="dialog" and aria-modal="true" to the outer modal container (the div with className starting "fixed inset-0 z-50 ...") and set aria-labelledby to the id of the modal title element (create a unique id on the title element inside the inner container with className "h-[26.5rem] w-full ..."); also add aria-describedby pointing to the modal body text if present. Apply the same changes to the second modal instance referenced around lines 47-49 so both modal containers include role="dialog", aria-modal="true", and proper aria-labelledby/aria-describedby IDs, and ensure the title/body elements have matching id attributes.apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseNoticeCard.tsx (1)
17-19: 리스트 key를 텍스트 단독으로 쓰면 중복 시 충돌합니다.공지 문구가 중복되면 key 충돌이 발생할 수 있어, 인덱스를 조합한 key로 바꾸는 편이 안전합니다.
수정 제안
- {noticeList.map((text) => ( - <div key={text} className="flex items-start"> + {noticeList.map((text, index) => ( + <div key={`${text}-${index}`} className="flex items-start">🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseNoticeCard.tsx around lines 17 - 19, The current mapping in PurchaseNoticeCard.tsx uses noticeList.map((text) => ...) with key={text}, which can collide when identical notice strings appear; update the map callback to include the index (e.g., noticeList.map((text, idx) => ...)) and set the key to a stable combined value like `${idx}-${text}` (or another unique identifier) on the outer div to avoid duplicate key collisions while keeping rendering deterministic.apps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsx (1)
111-111:accountText가독성 개선을 고려해 보세요.현재 은행명과 계좌번호가 공백 없이 연결되어
"나무은행123-4567-891011"로 표시됩니다. 가독성을 위해 구분자를 추가하는 것을 고려해 볼 수 있습니다.💡 공백 추가 제안
- accountText={`${ACCOUNT_INFO.bankName}${ACCOUNT_INFO.accountNumber}`} + accountText={`${ACCOUNT_INFO.bankName} ${ACCOUNT_INFO.accountNumber}`}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsx at line 111, The accountText currently concatenates ACCOUNT_INFO.bankName and ACCOUNT_INFO.accountNumber with no separator, hurting readability; update the PurchaseContent prop assignment (accountText) to join ACCOUNT_INFO.bankName and ACCOUNT_INFO.accountNumber with a clear separator (e.g., a space, " - ", or other localized delimiter) using a template literal or a small helper (e.g., `${ACCOUNT_INFO.bankName} - ${ACCOUNT_INFO.accountNumber}`) so the rendered string is human-readable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseCompleteModal.tsx:
- Around line 11-27: The onClose prop declared on PurchaseCompleteModal is never
invoked, so parent modal state isn't cleared; update the component's close flow
(e.g., inside handleConfirm or wherever closing happens) to call onClose?.()
before or after navigation. Modify the PurchaseCompleteModal function to accept
onClose from PurchaseCompleteModalProps and invoke onClose?.() in handleConfirm
(and/or in any other close/escape handlers) so the parent can update its state,
then perform router.push("/main") as needed.
In
`@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsx:
- Line 35: The initial count can exceed menu.remainingCount when remainingCount
is 0; change the state initialization for count so it respects
menu.remainingCount (e.g., initialize count via a function that sets count to 1
only if menu.remainingCount > 0, otherwise 0, or use Math.min(1,
menu.remainingCount)). Update the useState initialization for count (and ensure
any increment/decrement handlers that use setCount also clamp against
menu.remainingCount) so UI never shows a quantity greater than
menu.remainingCount.
In
`@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseInfoSection.tsx:
- Line 35: The total price rendering in PurchaseInfoSection currently displays
only the number (totalPrice.toLocaleString()) without the currency unit; update
the JSX in the PurchaseInfoSection component to append the Korean currency unit
(원) to the displayed value (e.g., render the formatted totalPrice followed by
"원") so it matches other screens and UX expectations.
In `@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/page.tsx:
- Around line 21-27: The current validation treats 0 and NaN the same by using a
falsy check on selectedMenuId; change the checks to explicit numeric validation:
compute storeId and selectedMenuId as Numbers (already done) and replace the
falsy checks with Number.isInteger/store-range or Number.isNaN checks (e.g.,
ensure !Number.isNaN(selectedMenuId) and optionally selectedMenuId >= 0 if 0 is
valid) and similarly validate storeId before looking up
MOCK_MAIN_STORE_DETAIL_MAP; call notFound() only when the numeric validation
fails or the store lookup returns undefined.
---
Nitpick comments:
In
`@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsx:
- Line 111: The accountText currently concatenates ACCOUNT_INFO.bankName and
ACCOUNT_INFO.accountNumber with no separator, hurting readability; update the
PurchaseContent prop assignment (accountText) to join ACCOUNT_INFO.bankName and
ACCOUNT_INFO.accountNumber with a clear separator (e.g., a space, " - ", or
other localized delimiter) using a template literal or a small helper (e.g.,
`${ACCOUNT_INFO.bankName} - ${ACCOUNT_INFO.accountNumber}`) so the rendered
string is human-readable.
In
`@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseGuideModal.tsx:
- Around line 21-23: The modal markup in PurchaseGuideModal lacks accessibility
attributes; add role="dialog" and aria-modal="true" to the outer modal container
(the div with className starting "fixed inset-0 z-50 ...") and set
aria-labelledby to the id of the modal title element (create a unique id on the
title element inside the inner container with className "h-[26.5rem] w-full
..."); also add aria-describedby pointing to the modal body text if present.
Apply the same changes to the second modal instance referenced around lines
47-49 so both modal containers include role="dialog", aria-modal="true", and
proper aria-labelledby/aria-describedby IDs, and ensure the title/body elements
have matching id attributes.
In
`@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseNoticeCard.tsx:
- Around line 17-19: The current mapping in PurchaseNoticeCard.tsx uses
noticeList.map((text) => ...) with key={text}, which can collide when identical
notice strings appear; update the map callback to include the index (e.g.,
noticeList.map((text, idx) => ...)) and set the key to a stable combined value
like `${idx}-${text}` (or another unique identifier) on the outer div to avoid
duplicate key collisions while keeping rendering deterministic.
In
`@apps/customer/src/app/`(tabs)/main/store/[id]/purchase/_components/PurchaseOrderCard.tsx:
- Around line 57-62: In PurchaseOrderCard, disable the decrease button when
count <= 0 to match the upper-bound guard: update the decrease button element
that uses onDecrease so it sets disabled={count <= 0} (and/or aria-disabled) and
ensure the onDecrease handler short-circuits if count <= 0 to avoid
side-effects; reference the decrease button element, the onDecrease handler and
the count prop/state in PurchaseOrderCard when making the change.
In `@apps/customer/src/app/`(tabs)/order/_constants/mockOrders.ts:
- Around line 18-31: Several mock order objects in MOCK_ORDERS are duplicated;
create a small factory to generate orders and replace repeated literal objects
to reduce duplication and future bugs: add a helper function (e.g.,
makeMockOrder or createMockOrder) that accepts an overrides object and returns
the common shape (id, storeName, orderSummary, totalPrice, pickupTime), then use
it to produce the entries in the MOCK_ORDERS array (calling makeMockOrder({ id:
3, ... }) and makeMockOrder({ id: 4, ... })) so only differing fields need to be
specified while shared fields live in one place.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ba73c60a-c4f4-444b-8b76-cd9082dc0ca2
⛔ Files ignored due to path filters (7)
packages/design-system/src/icons/generated/iconNames.tsis excluded by!**/generated/**packages/design-system/src/icons/generated/spriteSymbols.tsis excluded by!**/generated/**packages/design-system/src/icons/source/Check.svgis excluded by!**/*.svgpackages/design-system/src/icons/source/GiftOpen.svgis excluded by!**/*.svgpackages/design-system/src/icons/source/KakaoPay.svgis excluded by!**/*.svgpackages/design-system/src/icons/source/Minus.svgis excluded by!**/*.svgpackages/design-system/src/icons/source/Plus.svgis excluded by!**/*.svg
📒 Files selected for processing (13)
apps/customer/src/app/(tabs)/main/_components/MainListView.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseCompleteModal.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseContent.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseGuideModal.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseInfoSection.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseItemCard.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseNoticeCard.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/_components/PurchaseOrderCard.tsxapps/customer/src/app/(tabs)/main/store/[id]/purchase/page.tsxapps/customer/src/app/(tabs)/main/store/_components/StoreDetailContent.tsxapps/customer/src/app/(tabs)/main/store/_components/StoreMenuCard.tsxapps/customer/src/app/(tabs)/order/_constants/mockOrders.tsapps/customer/src/app/(tabs)/order/page.tsx
✅ 작업 내용
📝 Description
랜덤박스 구매 흐름 전반(구매 → 결제 안내 → 결제 완료) UI 및 상태 흐름 구현
/main라우팅 처리🚀 설계 의도 및 개선점
h-screen + overflow구조로 반응형 대응📸 스크린샷 (선택)
📎 기타 참고사항
Fixes #76
Summary by CodeRabbit
출시 노트
New Features
Chores