feat: 아이템 수정 상태 제약 해제 및 위시리스트 응답 구조 서버 기준으로 정리 - #424
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Discord 스레드 연동용 메타데이터입니다. discord-pr-bot 워크플로가 자동 생성하며, 수정·삭제하면 PR 과 Discord 알림 연동이 끊깁니다. |
|
Warning Review limit reached
Next review available in: 11 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (22)
📝 WalkthroughWalkthrough위시리스트 응답과 아이템 수정 계약을 갱신했습니다. 초대 진입과 회원 자동 참여 흐름을 서버 및 상태 기반으로 변경했습니다. 영수증 공유를 다이얼로그 중심으로 재구성했습니다. 오류 화면과 공통 문구도 보정했습니다. Changes위시리스트 및 아이템 흐름
초대 및 토너먼트 참여 흐름
영수증 공유 흐름
공통 화면 및 문구 보정
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant ResultClient
participant ReceiptShareDialog
participant ReceiptShareCaptureLayer
participant shareReceiptImage
ResultClient->>ReceiptShareDialog: 영수증 공유 요청
ReceiptShareDialog->>ReceiptShareCaptureLayer: 캡처용 영수증 렌더링
ReceiptShareDialog->>shareReceiptImage: 이미지 Blob 생성
shareReceiptImage-->>ReceiptShareDialog: Blob 반환
ReceiptShareDialog->>shareReceiptImage: 저장·복사·파일 공유 실행
ReceiptShareDialog-->>ResultClient: 공유 결과 반영
sequenceDiagram
participant InvitePage
participant getInvitePreviewByCode
participant InviteAPI
participant InviteInvalid
participant JoinPreviewClient
InvitePage->>getInvitePreviewByCode: 초대 코드 미리보기 요청
getInvitePreviewByCode->>InviteAPI: 서버 또는 클라이언트 API 호출
InviteAPI-->>getInvitePreviewByCode: 미리보기 데이터 반환
getInvitePreviewByCode-->>InvitePage: joined와 tournamentId 전달
InvitePage->>InviteInvalid: 오류 상태 표시
InvitePage->>JoinPreviewClient: 미참여 사용자 이동
JoinPreviewClient->>InviteAPI: 회원 자동 참여 요청
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing Touches 💡 1📝 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 |
|
Caution Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted. Error details |
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/web/src/components/common/item-edit-form/index.tsx (1)
110-146: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win기존 READY 전용
BottomCta를 제거하세요.
itemStatus === 'READY' && onRefresh조건에서 기존BottomCta가 먼저 렌더링됩니다. 그 다음 공통BottomCta도 렌더링됩니다. READY 항목에서 삭제 버튼과 고정 CTA 컨테이너가 중복됩니다.Lines 98-111의 기존 CTA 블록을 제거하세요. 공통
BottomCta만 유지하세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/components/common/item-edit-form/index.tsx` around lines 110 - 146, Remove the existing READY-specific BottomCta block near the preceding conditional render, including its duplicate delete/fixed CTA controls. Keep only the shared BottomCta containing the delete, conditional refresh, and save buttons, preserving its current conditions and handlers.
🧹 Nitpick comments (2)
apps/web/src/app/archive/wish/_components/wish-grid/index.tsx (2)
10-10: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win프로젝트 모듈 import를 절대 경로로 변경하세요.
Line 10의../../_utils/wishScroll은 같은 디렉터리 밖의 프로젝트 모듈을 상대 경로로 import합니다.@/app/archive/wish/_utils/wishScroll을 사용하세요.수정 예시
-import { saveWishScroll } from '../../_utils/wishScroll'; +import { saveWishScroll } from '`@/app/archive/wish/_utils/wishScroll`';As per coding guidelines, relative imports are only allowed for files in the same directory.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/archive/wish/_components/wish-grid/index.tsx` at line 10, Update the saveWishScroll import in the wish-grid module from the relative path to the project alias path `@/app/archive/wish/_utils/wishScroll`, keeping the imported symbol and behavior unchanged.Source: Coding guidelines
34-41: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win두
Link에cursor-pointer를 추가하세요.
Line 34와Line 81의Link는 클릭 요소입니다. 두 요소에cursor-pointer를 추가하세요.수정 예시
<Link + className="cursor-pointer" href={ROUTES.WISH_EDIT(wish.id)}As per coding guidelines, clickable elements require
cursor-pointer.Also applies to: 81-94
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/archive/wish/_components/wish-grid/index.tsx` around lines 34 - 41, Update both Link elements rendering the wish cards, including the one wrapping WishFailedCard and the corresponding Link near the second card, to include the cursor-pointer styling while preserving their existing click behavior and attributes.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/app/archive/wish/`[id]/_components/ItemEditForm.tsx:
- Around line 10-16: Keep initialImageUrl required as a string in
ItemEditFormProps and update ItemEditFormBase to handle null or empty image
values internally, including rendering and validation, so callers do not need to
convert wishData.item.imageUrl with an empty-string fallback.
In `@apps/web/src/components/common/item-edit-form/index.tsx`:
- Around line 136-145: Update isValid and handleSave in the item edit form so
selectedImage is required only when the item status is FAILED; for READY and
in-progress items, allow saving without an image and pass only the changed valid
fields to onSave. Remove the unconditional !selectedImage guard from handleSave,
while preserving the existing loading and deletion restrictions.
In `@apps/web/src/types/wish.ts`:
- Around line 8-22: Update GetWishlistResponseT so its item field uses the same
status-based union as GetWishResponseT, including PENDING, PROCESSING, and
FAILED variants with name, currentPrice, currency, and imageUrl nullable.
Preserve the existing response fields and align the union with the
detail-response type so WishGrid can pass each variant to WishCard safely.
---
Outside diff comments:
In `@apps/web/src/components/common/item-edit-form/index.tsx`:
- Around line 110-146: Remove the existing READY-specific BottomCta block near
the preceding conditional render, including its duplicate delete/fixed CTA
controls. Keep only the shared BottomCta containing the delete, conditional
refresh, and save buttons, preserving its current conditions and handlers.
---
Nitpick comments:
In `@apps/web/src/app/archive/wish/_components/wish-grid/index.tsx`:
- Line 10: Update the saveWishScroll import in the wish-grid module from the
relative path to the project alias path `@/app/archive/wish/_utils/wishScroll`,
keeping the imported symbol and behavior unchanged.
- Around line 34-41: Update both Link elements rendering the wish cards,
including the one wrapping WishFailedCard and the corresponding Link near the
second card, to include the cursor-pointer styling while preserving their
existing click behavior and attributes.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 3c2de044-34b2-403b-a5d7-f0a942980db8
📒 Files selected for processing (20)
apps/web/e2e/helpers/apiResponse.tsapps/web/e2e/mocks/wish.tsapps/web/src/apis/getWishlist.tsapps/web/src/app/archive/wish/[id]/_components/EditContent.tsxapps/web/src/app/archive/wish/[id]/_components/ItemEditForm.tsxapps/web/src/app/archive/wish/[id]/_types/wish.tsapps/web/src/app/archive/wish/_components/WishCardSkeleton.tsxapps/web/src/app/archive/wish/_components/WishContent.tsxapps/web/src/app/archive/wish/_components/WishContentClient.tsxapps/web/src/app/archive/wish/_components/WishGridContent.tsxapps/web/src/app/archive/wish/_components/WishlistList.tsxapps/web/src/app/archive/wish/_components/wish-grid/index.tsxapps/web/src/app/tournament/[id]/create/by-wish/_components/ByWishContent.tsxapps/web/src/app/tournament/[id]/item/[itemId]/_apis/patchTournamentItem.tsapps/web/src/app/tournament/[id]/item/[itemId]/_components/EditContent.tsxapps/web/src/app/tournament/[id]/item/[itemId]/_types/tournamentItem.tsapps/web/src/components/common/item-edit-form/index.tsxapps/web/src/hooks/useGetWishlist.tsapps/web/src/types/api.tsapps/web/src/types/wish.ts
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (5)
apps/web/e2e/specs/home/home.spec.ts (1)
13-13: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winE2E 테스트 제목도 새 문구와 일치시키세요.
현재 assertion은
최근 토너먼트를 검증하지만 테스트 제목은최근 생성한 토너먼트를 사용합니다. 테스트 리포트의 의미를 일치시키려면 관련 테스트 제목을 새 용어로 변경하세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/e2e/specs/home/home.spec.ts` at line 13, Update the related E2E test title in home.spec.ts to use the same “최근 토너먼트” wording asserted by getByRole, replacing the existing “최근 생성한 토너먼트” title while leaving the assertion unchanged.apps/web/src/app/error.tsx (1)
22-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win최상위 화면 컨테이너를
<main>으로 변경하세요.세 화면은 실제 화면 컨테이너에
<div>를 사용합니다. 화면의 주 콘텐츠 영역에는 semantic<main>을 사용하세요.
apps/web/src/app/error.tsx#L22-L44: 최상위<div>를<main>으로 변경하세요.apps/web/src/app/global-error.tsx#L25-L50: 최상위<div>를<main>으로 변경하세요.apps/web/src/app/not-found.tsx#L9-L24: 최상위<div>를<main>으로 변경하세요.As per coding guidelines, containers in
apps/web/src/**/*.tsxmust use semantic<main>tags.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/error.tsx` around lines 22 - 44, Replace the top-level screen container with a semantic main element in apps/web/src/app/error.tsx lines 22-44, apps/web/src/app/global-error.tsx lines 25-50, and apps/web/src/app/not-found.tsx lines 9-24; preserve each screen’s existing contents, styling, and behavior.Source: Coding guidelines
apps/web/src/app/tournament/[id]/result/_components/receipt-share-dialog/ReceiptShareCaptureLayer.tsx (2)
5-6: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value상위 디렉터리 상대경로 import를
@/절대경로로 통일하세요. 두 신규 파일 모두../및../../../로 상위 디렉터리 모듈을 가져옵니다. 코딩 가이드라인은 프로젝트 모듈에@/*절대경로를 쓰고 상대경로는 동일 디렉터리 파일에만 쓰도록 정합니다.
apps/web/src/app/tournament/[id]/result/_components/receipt-share-dialog/ReceiptShareCaptureLayer.tsx#L5-L6:../../../_common/_types/tournament와../ReceiptPaper를@/절대경로로 바꾸세요.apps/web/src/app/tournament/[id]/result/_components/receipt-share-dialog/ReceiptShareDialog.tsx#L14-L20:../../../_common/_types/tournament와../../_utils/shareReceiptImage를@/절대경로로 바꾸세요. 같은 디렉터리인./ReceiptShareCaptureLayer는 그대로 두세요.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/tournament/`[id]/result/_components/receipt-share-dialog/ReceiptShareCaptureLayer.tsx around lines 5 - 6, 상위 디렉터리 모듈의 상대경로 import를 프로젝트의 `@/` 절대경로로 통일하세요. ReceiptShareCaptureLayer.tsx의 RankedProductT와 ReceiptPaper import, ReceiptShareDialog.tsx의 RankedProductT와 shareReceiptImage import를 변경하고, 동일 디렉터리의 ./ReceiptShareCaptureLayer import는 그대로 유지하세요.Source: Coding guidelines
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
forwardRef대신refprop을 사용하세요.React 19에서
ref는 일반 prop입니다.ReceiptShareCaptureLayer의 타입에ref?: React.Ref<HTMLDivElement>를 두고 정규function컴포넌트로 작성하세요.♻️ 제안 리팩터
-import { forwardRef } from 'react'; - import PikiLogoCart from '`@/assets/images/piki-logo-cart.svg`';type ReceiptShareCaptureLayerProps = { + ref?: React.Ref<HTMLDivElement>; tournamentId: number; tournamentName: string; result: RankedProductT[]; date: Date; }; -const ReceiptShareCaptureLayer = forwardRef<HTMLDivElement, ReceiptShareCaptureLayerProps>( - function ReceiptShareCaptureLayer({ tournamentId, tournamentName, result, date }, ref) { - return ( +function ReceiptShareCaptureLayer({ + ref, + tournamentId, + tournamentName, + result, + date, +}: ReceiptShareCaptureLayerProps) { + return (🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/tournament/`[id]/result/_components/receipt-share-dialog/ReceiptShareCaptureLayer.tsx at line 1, Replace the forwardRef-based ReceiptShareCaptureLayer implementation with a regular function component that accepts ref?: React.Ref<HTMLDivElement> in its props, removes the forwardRef import and wrapper, and passes the ref to the same underlying div.Source: Coding guidelines
apps/web/src/app/tournament/[id]/result/_components/receipt-share-dialog/ReceiptShareDialog.tsx (1)
81-111: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win캡처에 실패하면 다이얼로그가 스켈레톤 상태로 고정됩니다.
capture가 실패하면toast.error만 표시합니다.previewUrl과imageBlob은 계속null이므로 미리보기는 Skeleton으로 남고 세 버튼은 계속 비활성 상태입니다. 사용자는 다이얼로그를 닫았다 다시 여는 방법 외에 재시도 수단이 없습니다.실패 상태를 저장하고, 실패 시 안내 문구와 다시 시도 버튼을 노출하는 방법을 권장합니다.
Also applies to: 184-195
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/src/app/tournament/`[id]/result/_components/receipt-share-dialog/ReceiptShareDialog.tsx around lines 81 - 111, Update the ReceiptShareDialog capture flow to store an explicit capture failure state instead of only showing a toast. Set that state in the catch path of capture, render an explanatory failure message and retry action when it is active, and have retry invoke the capture flow again while clearing the failure state; keep the existing skeleton, preview, and button behavior for loading and successful captures.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/web/src/app/invite/`[id]/_components/InviteInvalid.tsx:
- Line 22: InviteInvalid 화면의 주요 제목인 `초대 링크가 유효하지 않아요`를 감싸는 요소를 `p`에서 `h1`로
변경하세요. 기존 `heading-1-bold text-text-neutral-primary` 클래스와 제목 텍스트는 그대로 유지하세요.
- Around line 30-34: Update the home navigation CTA in InviteInvalid to remove
the nested Link/Button interactive elements; replace them with ButtonLink or a
single Link styled as the existing primary large full-width button while
preserving ROUTES.HOME and the current label.
In `@apps/web/src/app/invite/`[id]/page.tsx:
- Around line 30-33: Update the catch block in the invite page around
InviteInvalid so only Axios errors with status 400 or 409 return the
invalid-invite screen; rethrow network failures, 5xx responses, and all other
unexpected errors for the error boundary to handle. Preserve
showExpiredDialog=true only for status 409 and false for status 400.
In `@apps/web/src/app/tournament/`[id]/result/_utils/shareReceiptImage.ts:
- Around line 128-134: Update the share result flow around the share utility and
ReceiptShareDialog.handleShareToChat so non-AbortError exceptions from
navigator.share return a new 'failed' status instead of 'unsupported'; preserve
'cancelled' for AbortError and reserve 'unsupported' for capability checks.
Handle 'failed' in the dialog by showing the existing general error toast, while
keeping the unsupported message only for genuinely unsupported environments.
- Around line 20-21: Update isDownloadSupported and the saveReceiptImage flow so
support is not inferred solely from the anchor download property, which may be
present without functioning in iOS WKWebView. Ensure
ReceiptShareDialog.handleSave does not show a definitive success toast based
only on invoking the save command; report an attempted save instead unless an
app bridge or other reliable result confirms that storage completed.
In `@apps/web/src/app/tournament/join/`[id]/_components/JoinPreviewClient.tsx:
- Around line 79-83: Update the 409 handling in the postJoin onError callback to
inspect error.response?.data.code instead of treating every conflict as
LINK_EXPIRED. Route each recognized code to its corresponding dialog and use a
cause-neutral blocked screen when the cause cannot be distinguished, while
preserving the existing auto-join blocked state.
In `@apps/web/src/types/item.ts`:
- Around line 19-22: PatchItemRequestT in apps/web/src/types/item.ts lines 19-22
must make name and price optional to represent partial updates. In
apps/web/src/app/archive/wish/[id]/_hooks/usePatchWish.ts lines 17-21 and
apps/web/src/app/tournament/[id]/item/[itemId]/_hooks/usePatchTournamentItem.ts
lines 18-22, append each FormData field only when its corresponding value is not
undefined, so mutations send only changed fields.
---
Nitpick comments:
In `@apps/web/e2e/specs/home/home.spec.ts`:
- Line 13: Update the related E2E test title in home.spec.ts to use the same “최근
토너먼트” wording asserted by getByRole, replacing the existing “최근 생성한 토너먼트” title
while leaving the assertion unchanged.
In `@apps/web/src/app/error.tsx`:
- Around line 22-44: Replace the top-level screen container with a semantic main
element in apps/web/src/app/error.tsx lines 22-44,
apps/web/src/app/global-error.tsx lines 25-50, and
apps/web/src/app/not-found.tsx lines 9-24; preserve each screen’s existing
contents, styling, and behavior.
In
`@apps/web/src/app/tournament/`[id]/result/_components/receipt-share-dialog/ReceiptShareCaptureLayer.tsx:
- Around line 5-6: 상위 디렉터리 모듈의 상대경로 import를 프로젝트의 `@/` 절대경로로 통일하세요.
ReceiptShareCaptureLayer.tsx의 RankedProductT와 ReceiptPaper import,
ReceiptShareDialog.tsx의 RankedProductT와 shareReceiptImage import를 변경하고, 동일 디렉터리의
./ReceiptShareCaptureLayer import는 그대로 유지하세요.
- Line 1: Replace the forwardRef-based ReceiptShareCaptureLayer implementation
with a regular function component that accepts ref?: React.Ref<HTMLDivElement>
in its props, removes the forwardRef import and wrapper, and passes the ref to
the same underlying div.
In
`@apps/web/src/app/tournament/`[id]/result/_components/receipt-share-dialog/ReceiptShareDialog.tsx:
- Around line 81-111: Update the ReceiptShareDialog capture flow to store an
explicit capture failure state instead of only showing a toast. Set that state
in the catch path of capture, render an explanatory failure message and retry
action when it is active, and have retry invoke the capture flow again while
clearing the failure state; keep the existing skeleton, preview, and button
behavior for loading and successful captures.
🪄 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: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 8cfeb615-582e-4644-ab35-e14fd54e3ba2
⛔ Files ignored due to path filters (3)
apps/web/src/assets/icons/fill/receipt.svgis excluded by!**/*.svgapps/web/src/assets/icons/outline/receipt.svgis excluded by!**/*.svgapps/web/src/assets/images/basket-not-found.svgis excluded by!**/*.svg
📒 Files selected for processing (40)
apps/web/e2e/mocks/wish.tsapps/web/e2e/specs/home/home.spec.tsapps/web/src/apis/getInvitePreviewByCode.tsapps/web/src/app/archive/tournament/_components/TournamentStatusTab.tsxapps/web/src/app/archive/wish/[id]/_components/EditContent.tsxapps/web/src/app/archive/wish/[id]/_hooks/usePatchWish.tsapps/web/src/app/archive/wish/[id]/_types/wish.tsapps/web/src/app/archive/wish/_components/WishGridContent.tsxapps/web/src/app/archive/wish/_components/wish-grid/index.tsxapps/web/src/app/error.tsxapps/web/src/app/global-error.tsxapps/web/src/app/home/_components/CreateTournamentDialog.tsxapps/web/src/app/home/_components/InviteTournamentDialog.tsxapps/web/src/app/home/_components/tournament-list/index.tsxapps/web/src/app/invite/[id]/_components/InviteClient.tsxapps/web/src/app/invite/[id]/_components/InviteInvalid.tsxapps/web/src/app/invite/[id]/page.tsxapps/web/src/app/mypage/withdraw/page.tsxapps/web/src/app/not-found.tsxapps/web/src/app/tournament/[id]/create/by-wish/_components/ByWishContent.tsxapps/web/src/app/tournament/[id]/item/[itemId]/_components/EditContent.tsxapps/web/src/app/tournament/[id]/item/[itemId]/_hooks/usePatchTournamentItem.tsapps/web/src/app/tournament/[id]/result/_components/ReceiptDrawMachine.tsxapps/web/src/app/tournament/[id]/result/_components/ResultClient.tsxapps/web/src/app/tournament/[id]/result/_components/receipt-share-dialog/ReceiptShareCaptureLayer.tsxapps/web/src/app/tournament/[id]/result/_components/receipt-share-dialog/ReceiptShareDialog.tsxapps/web/src/app/tournament/[id]/result/_utils/shareReceiptImage.tsapps/web/src/app/tournament/join/[id]/_components/JoinPreviewClient.tsxapps/web/src/app/tournament/join/_apis/getInvitePreview.tsapps/web/src/app/tournament/join/_types/join.tsapps/web/src/assets/icons/fill/index.tsapps/web/src/assets/icons/outline/index.tsapps/web/src/components/common/create-tournament-dialog/index.tsxapps/web/src/components/common/item-edit-form/index.tsxapps/web/src/components/get-item-dialog/index.tsxapps/web/src/components/input/index.tsxapps/web/src/components/toast/index.tsxapps/web/src/components/tournament-card/MorePopover.tsxapps/web/src/types/item.tsapps/web/src/types/tournament.ts
💤 Files with no reviewable changes (2)
- apps/web/src/app/tournament/join/_types/join.ts
- apps/web/src/app/invite/[id]/_components/InviteClient.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
- apps/web/src/app/archive/wish/[id]/_components/EditContent.tsx
- apps/web/src/app/archive/wish/_components/WishGridContent.tsx
- apps/web/src/app/tournament/[id]/item/[itemId]/_components/EditContent.tsx
- apps/web/src/app/tournament/[id]/create/by-wish/_components/ByWishContent.tsx
- apps/web/src/app/archive/wish/_components/wish-grid/index.tsx
| <> | ||
| <main className="flex min-h-dvh flex-col items-center justify-center gap-6 bg-bg-layer-basement px-5 pt-padding-top"> | ||
| <div className="flex flex-col items-center gap-2"> | ||
| <p className="heading-1-bold text-text-neutral-primary">초대 링크가 유효하지 않아요</p> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
주요 화면 제목을 h1으로 변경하세요.
초대 링크가 유효하지 않아요는 이 화면의 주요 제목입니다. 현재 p 요소라서 문서 구조와 보조 기술의 제목 탐색에 포함되지 않습니다.
As per coding guidelines, "제목에는 <h1> 또는 <h2>를 사용한다."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/app/invite/`[id]/_components/InviteInvalid.tsx at line 22,
InviteInvalid 화면의 주요 제목인 `초대 링크가 유효하지 않아요`를 감싸는 요소를 `p`에서 `h1`로 변경하세요. 기존
`heading-1-bold text-text-neutral-primary` 클래스와 제목 텍스트는 그대로 유지하세요.
Source: Coding guidelines
| <Link href={ROUTES.HOME} className="w-full max-w-80"> | ||
| <Button size="lg" variant="primary" className="w-full"> | ||
| 홈으로 가기 | ||
| </Button> | ||
| </Link> |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
중첩된 인터랙티브 요소를 제거하세요.
Link 내부의 Button은 <a> 내부에 <button>을 생성합니다. 이 구조는 유효하지 않은 HTML이며 키보드와 클릭 동작을 불안정하게 만듭니다.
ButtonLink 또는 버튼 스타일을 적용한 단일 Link로 홈 이동 CTA를 구현하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/app/invite/`[id]/_components/InviteInvalid.tsx around lines 30 -
34, Update the home navigation CTA in InviteInvalid to remove the nested
Link/Button interactive elements; replace them with ButtonLink or a single Link
styled as the existing primary large full-width button while preserving
ROUTES.HOME and the current label.
| } catch (error) { | ||
| /** 409(만료·비활성 초대)는 만료 다이얼로그 노출, 그 외(400 코드 불일치 등)는 안내 화면만 */ | ||
| const isExpired = isAxiosError<ApiErrorResponseT>(error) && error.response?.status === 409; | ||
| return <InviteInvalid showExpiredDialog={isExpired} />; |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline 'apps/web/src/app/invite/[id]/page.tsx' --items all
rg -n -C 5 --glob '*.{ts,tsx}' \
'getInvitePreviewByCode|interceptors|validateStatus|AxiosError' apps/web/srcRepository: TeamPiKi/client
Length of output: 45260
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '--- page snippet ---\n'
sed -n '1,80p' 'apps/web/src/app/invite/[id]/page.tsx'
printf '\n--- getInvitePreviewByCode ---\n'
sed -n '1,80p' apps/web/src/apis/getInvitePreviewByCode.ts
printf '\n--- serverApi interceptor ---\n'
sed -n '1,90p' apps/web/src/apis/server.ts
printf '\n--- error boundary/global query error handling mentions ---\n'
rg -n -C 3 --glob '*.{ts,tsx}' 'ErrorBoundary|onError|globalError|fallback|getApiErrorMessage|setExtra|captureError' apps/web/srcRepository: TeamPiKi/client
Length of output: 244
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- page snippet ---'
sed -n '1,80p' 'apps/web/src/app/invite/[id]/page.tsx'
printf '%s\n' ''
printf '%s\n' '--- getInvitePreviewByCode ---'
sed -n '1,80p' apps/web/src/apis/getInvitePreviewByCode.ts
printf '%s\n' ''
printf '%s\n' '--- serverApi interceptor ---'
sed -n '1,90p' apps/web/src/apis/server.ts
printf '%s\n' ''
printf '%s\n' '--- error boundary/global query error handling mentions ---'
rg -n -C 3 --glob '*.{ts,tsx}' 'ErrorBoundary|onError|globalError|fallback|getApiErrorMessage|setExtra|captureError' apps/web/srcRepository: TeamPiKi/client
Length of output: 49012
예상하지 않은 요청 실패를 오류 화면으로 전파하세요.
Line 30의 catch는 네트워크 오류와 5xx 응답도 InviteInvalid로 변환합니다. 400과 409만 InviteInvalid로 처리하고, 그 외 오류는 다시 throw해서 오류 경계에서 처리하세요.
수정 예시
} catch (error) {
- /** 409(만료·비활성 초대)는 만료 다이얼로그 노출, 그 외(400 코드 불일치 등)는 안내 화면만 */
- const isExpired = isAxiosError<ApiErrorResponseT>(error) && error.response?.status === 409;
- return <InviteInvalid showExpiredDialog={isExpired} />;
+ if (!isAxiosError<ApiErrorResponseT>(error)) throw error;
+
+ const status = error.response?.status;
+ if (status !== 400 && status !== 409) throw error;
+
+ return <InviteInvalid showExpiredDialog={status === 409} />;
}📝 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.
| } catch (error) { | |
| /** 409(만료·비활성 초대)는 만료 다이얼로그 노출, 그 외(400 코드 불일치 등)는 안내 화면만 */ | |
| const isExpired = isAxiosError<ApiErrorResponseT>(error) && error.response?.status === 409; | |
| return <InviteInvalid showExpiredDialog={isExpired} />; | |
| } catch (error) { | |
| if (!isAxiosError<ApiErrorResponseT>(error)) throw error; | |
| const status = error.response?.status; | |
| if (status !== 400 && status !== 409) throw error; | |
| return <InviteInvalid showExpiredDialog={status === 409} />; |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/app/invite/`[id]/page.tsx around lines 30 - 33, Update the catch
block in the invite page around InviteInvalid so only Axios errors with status
400 or 409 return the invalid-invite screen; rethrow network failures, 5xx
responses, and all other unexpected errors for the error boundary to handle.
Preserve showExpiredDialog=true only for status 409 and false for status 400.
| onError: error => { | ||
| if (isAxiosError<ApiErrorResponseT>(error) && error.response?.status === 409) { | ||
| setAutoJoinStatus('blocked'); | ||
| setIsTournamentErrorDialogOpen(true); | ||
| return; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ast-grep outline 'apps/web/src/app/tournament/join/[id]/_components/JoinPreviewClient.tsx' --items all
rg -n -C 6 --glob '*.{ts,tsx}' \
'postJoinMutation|postJoin|LINK_EXPIRED|ALREADY_STARTED|ALREADY_ENDED|ApiErrorResponseT' apps/web/srcRepository: TeamPiKi/client
Length of output: 50371
409 응답의 code를 구분하고 적절한 다이얼로그로 연결하세요.
postJoin의 모든 409를 LINK_EXPIRED로 처리하면 이미 참여한 경우나 토너먼트 상태 변경까지 링크 만료 안내로 표시됩니다. error.response?.data.code를 확인해 원인에 맞는 UI를 보여주거나, 서버에서 분리가 불가능한 경우에는 원인 중립적인 차단 화면을 사용하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/web/src/app/tournament/join/`[id]/_components/JoinPreviewClient.tsx
around lines 79 - 83, Update the 409 handling in the postJoin onError callback
to inspect error.response?.data.code instead of treating every conflict as
LINK_EXPIRED. Route each recognized code to its corresponding dialog and use a
cause-neutral blocked screen when the cause cannot be distinguished, while
preserving the existing auto-join blocked state.
63fabec to
63e15f8
Compare
작업 요약
작업 세부 내용
아이템 수정 화면 (
item-edit-form)READY면 이미지·이름·가격 입력이 전부disabled였고, 하단 CTA도READY/FAILED분기로 나뉘어 있었습니다.삭제하기+저장하기한 벌로 통합했습니다.다시 불러오기는READY이면서onRefresh가 있을 때만 추가로 노출됩니다.위시리스트 응답 구조 (
getWishlist/useGetWishlist)WishlistEntryT→WishItemT로 평탄화하던mapWishlist를 제거하고 서버 응답({ wish, item })을 그대로 내려보냅니다.WishlistPageT대신 공통ApiResponseT+pageResponse를 사용합니다.WishGridContent,wish-grid,ByWishContent등)는wish.id/item.name형태로 접근하도록 맞췄습니다.타입 정리
ApiResponseT에pageResponse(nextCursor,hasNext)를 추가했습니다.GetWishlistResponseT에refreshNeeded,reused를 추가했습니다. 이미지로 등록한 경우null, 링크로 등록한 경우boolean이라 주석으로 명시했습니다.GetTournamentItemResponseT에서sourceUrl,currency를 공통 필드로 올리고FAILED를 미완성 상태 유니온에 합쳤습니다. 동일했던PatchTournamentItemResponseT는 제거했습니다.위시리스트 스켈레톤
rounded가 없어 스켈레톤의rounded-2xl을 제거했습니다.aspect-[201/166], 텍스트 영역h-[124px]). 로딩 → 실제 카드 전환 시 리스트 높이가 튀지 않습니다.연관 이슈
closes #416
Summary by CodeRabbit