[Feat/#108] 사장님 예약/주문 API 연동#110
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 Walkthrough개요주문 관리 기능이 로컬 상태에서 서버 기반 쿼리와 뮤테이션으로 마이그레이션되었습니다. 거부 사유 입력 검증이 추가되었고, 예약 상태 타입에 "refunded" 상태가 추가되었으며, API 모델이 업데이트되었습니다. 변경 사항
시퀀스 다이어그램sequenceDiagram
participant User as 사용자
participant Page as 주문 페이지
participant Hook as Query/Mutation 훅
participant QueryClient as React Query
participant API as API 서버
User->>Page: 페이지 진입 (탭 선택)
Page->>Hook: usePendingReservationsQuery() / useProcessedReservationsQuery()
Hook->>QueryClient: useQuery 실행
QueryClient->>API: GET 예약 데이터 요청
API-->>QueryClient: ReservationListDTO 반환
QueryClient-->>Hook: 데이터/로딩/에러 상태
Hook-->>Page: 쿼리 결과
Page->>Page: 로딩 상태 확인 및 렌더링 결정
alt 데이터 로드 성공
Page->>Page: ReservationDTO → ReservationItem 변환
Page->>User: 예약 리스트 표시
else 로딩 중
Page->>User: 로딩 상태 표시
else 에러 발생
Page->>User: 에러 메시지 표시
end
User->>Page: 승인 또는 거부 버튼 클릭
alt 거부 선택
Page->>Page: RejectOrderModal 열기
User->>Page: 거부 사유 입력 (10자 이상)
Page->>Page: 입력값 검증
User->>Page: 확인 버튼 클릭
Page->>Hook: useRejectReservationMutation 실행
else 승인 선택
Page->>Hook: useApproveReservationMutation 실행
end
Hook->>QueryClient: useMutation 실행
QueryClient->>API: POST/PUT 승인/거부 요청
API-->>QueryClient: 결과 반환
QueryClient->>QueryClient: 관련 쿼리 무효화
Hook-->>Page: 뮤테이션 완료
Page->>Page: 모달 닫기
Page->>Page: 쿼리 자동 갱신
Page->>User: 업데이트된 리스트 표시
코드 리뷰 예상 난이도🎯 3 (보통) | ⏱️ ~25분 관련 PR
시
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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. Review rate limit: 0/1 reviews remaining, refill in 60 minutes.Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
apps/owner/src/app/(tabs)/order/_components/modal/RejectOrderModal.tsx (1)
19-21: ⚡ Quick win최소 길이 기준값(10)을 상수로 추출해 단일 소스로 관리해주세요.
검증식/플레이스홀더/안내문구에 동일 값이 중복되어 정책 변경 시 불일치 위험이 있습니다.
♻️ 제안 diff
export default function RejectOrderModal({ open, onClose, onConfirm, }: RejectOrderModalProps) { + const MIN_REJECT_REASON_LENGTH = 10; const [reason, setReason] = useState(""); const trimmedReason = reason.trim(); - const isValidReason = trimmedReason.length >= 10; + const isValidReason = trimmedReason.length >= MIN_REJECT_REASON_LENGTH; @@ - placeholder="거절 사유를 입력해주세요. (10자 이상)" + placeholder={`거절 사유를 입력해주세요. (${MIN_REJECT_REASON_LENGTH}자 이상)`} @@ - 거절 사유는 10자 이상 입력해주세요. + 거절 사유는 {MIN_REJECT_REASON_LENGTH}자 이상 입력해주세요.Also applies to: 70-70, 82-82
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/owner/src/app/`(tabs)/order/_components/modal/RejectOrderModal.tsx around lines 19 - 21, Extract the numeric min-length "10" into a single exported constant (e.g., MIN_REASON_LENGTH) and replace the literal usages in the RejectOrderModal component: use MIN_REASON_LENGTH when computing trimmedReason validity (isValidReason), when building the textarea placeholder and any helper/validation messages (references around trimmedReason/isValidReason and the strings at the other occurrences). Ensure the constant is imported/defined near the component (or a shared constants module) so all occurrences (including the other two spots noted) reference the same symbol.
🤖 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/owner/src/app/`(tabs)/order/page.tsx:
- Around line 147-153: The payload sent to rejectMutation.mutate is using an
incorrect status ("REQUESTED"); update the mutation payload in the
rejectMutation.mutate call so that the body either omits the status field
entirely or sets it to the server-expected value ("REJECTED") instead of
"REQUESTED" (refer to the call using rejectMutation.mutate, the payload object
with reservationId: rejectModal.orderId, and body: { rejectReason: reason }).
In `@packages/api/src/domains/owner.ts`:
- Around line 14-17: The ReservationDecisionParams type allows body to be
optional even though ReservationRejectReqDTO requires rejectReason, causing
potential runtime 4xx when rejectReservation sends body ?? {}; change
ReservationDecisionParams to require body (replace body?:
ReservationRejectReqDTO with body: ReservationRejectReqDTO) and remove the
fallback usage of body ?? {} in rejectReservation so callers must pass a valid
ReservationRejectReqDTO (with rejectReason) and the compiler will enforce it.
In `@packages/api/src/models/owner.ts`:
- Around line 16-19: The ReservationRejectReqDTO currently allows any
ReservationStatus which permits invalid values on reject; update the DTO
definition (ReservationRejectReqDTO) to prevent misuse by either removing the
optional status field entirely or tightening its type to a literal "REJECTED"
(e.g., status?: "REJECTED" or status: "REJECTED") and then adjust callers (such
as the call site in apps/owner/src/app/(tabs)/order/page.tsx that sends status:
"REQUESTED") to stop sending a non-reject status; ensure the DTO and its usages
consistently reflect the chosen approach.
---
Nitpick comments:
In `@apps/owner/src/app/`(tabs)/order/_components/modal/RejectOrderModal.tsx:
- Around line 19-21: Extract the numeric min-length "10" into a single exported
constant (e.g., MIN_REASON_LENGTH) and replace the literal usages in the
RejectOrderModal component: use MIN_REASON_LENGTH when computing trimmedReason
validity (isValidReason), when building the textarea placeholder and any
helper/validation messages (references around trimmedReason/isValidReason and
the strings at the other occurrences). Ensure the constant is imported/defined
near the component (or a shared constants module) so all occurrences (including
the other two spots noted) reference the same symbol.
🪄 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: f2808262-87ce-43eb-b247-8c859ce73966
📒 Files selected for processing (10)
apps/owner/src/app/(tabs)/order/_components/modal/RejectOrderModal.tsxapps/owner/src/app/(tabs)/order/_types/order.tsapps/owner/src/app/(tabs)/order/_utils/orderStatus.tsapps/owner/src/app/(tabs)/order/page.tsxapps/owner/src/shared/queries/mutation/owner/useApproveReservationMutation.tsapps/owner/src/shared/queries/mutation/owner/useRejectReservationMutation.tsapps/owner/src/shared/queries/query/owner/usePendingReservationsQuery.tsapps/owner/src/shared/queries/query/owner/useProcessedReservationsQuery.tspackages/api/src/domains/owner.tspackages/api/src/models/owner.ts
| rejectMutation.mutate( | ||
| { | ||
| reservationId: rejectModal.orderId, | ||
| body: { | ||
| status: "REQUESTED", | ||
| rejectReason: reason, | ||
| }, |
There was a problem hiding this comment.
거절 요청 payload의 상태값이 잘못되어 있습니다.
Line [151]에서 status: "REQUESTED"를 보내면 거절 요청 의미와 충돌합니다. 이 값은 제거하거나, 서버가 필요로 하면 REJECTED로 맞춰야 합니다.
수정 예시
rejectMutation.mutate(
{
reservationId: rejectModal.orderId,
body: {
- status: "REQUESTED",
- rejectReason: reason,
+ rejectReason: reason.trim(),
},
},📝 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.
| rejectMutation.mutate( | |
| { | |
| reservationId: rejectModal.orderId, | |
| body: { | |
| status: "REQUESTED", | |
| rejectReason: reason, | |
| }, | |
| rejectMutation.mutate( | |
| { | |
| reservationId: rejectModal.orderId, | |
| body: { | |
| rejectReason: reason.trim(), | |
| }, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/owner/src/app/`(tabs)/order/page.tsx around lines 147 - 153, The payload
sent to rejectMutation.mutate is using an incorrect status ("REQUESTED"); update
the mutation payload in the rejectMutation.mutate call so that the body either
omits the status field entirely or sets it to the server-expected value
("REJECTED") instead of "REQUESTED" (refer to the call using
rejectMutation.mutate, the payload object with reservationId:
rejectModal.orderId, and body: { rejectReason: reason }).
| export interface ReservationDecisionParams { | ||
| reservationId: number; | ||
| body?: ReservationReqDTO; | ||
| body?: ReservationRejectReqDTO; | ||
| } |
There was a problem hiding this comment.
rejectReason 필수 계약을 타입에서 강제하지 못하고 있습니다.
ReservationRejectReqDTO는 rejectReason가 필수인데 body가 optional이라 reject 호출이 컴파일 단계에서 통과할 수 있습니다. 현재 rejectReservation이 body ?? {}를 전송하므로 런타임 4xx를 유발할 수 있습니다.
🐛 제안 diff
-export interface ReservationDecisionParams {
+export interface ApproveReservationParams {
reservationId: number;
- body?: ReservationRejectReqDTO;
+}
+
+export interface RejectReservationParams {
+ reservationId: number;
+ body: ReservationRejectReqDTO;
}
@@
- approveReservation: async ({ reservationId }: ReservationDecisionParams) => {
+ approveReservation: async ({ reservationId }: ApproveReservationParams) => {
@@
- }: ReservationDecisionParams) => {
+ }: RejectReservationParams) => {
const { data } = await api.privateClient.patch<ReservationResponse>(
`/owners/my-store/reservations/${reservationId}/reject`,
- body ?? {},
+ body,
);
return data;
},🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/api/src/domains/owner.ts` around lines 14 - 17, The
ReservationDecisionParams type allows body to be optional even though
ReservationRejectReqDTO requires rejectReason, causing potential runtime 4xx
when rejectReservation sends body ?? {}; change ReservationDecisionParams to
require body (replace body?: ReservationRejectReqDTO with body:
ReservationRejectReqDTO) and remove the fallback usage of body ?? {} in
rejectReservation so callers must pass a valid ReservationRejectReqDTO (with
rejectReason) and the compiler will enforce it.
| export interface ReservationRejectReqDTO { | ||
| status?: ReservationStatus; | ||
| rejectReason?: string; | ||
| rejectReason: string; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Reject 요청에서 status 오용 가능성 확인
rg -nP --type ts -C3 'ReservationRejectReqDTO|status:\s*"(REQUESTED|APPROVED|CANCELED|REJECTED)"'Repository: CUK-Compasser/FE
Length of output: 1775
거절 요청 DTO의 status 타입을 제한해 오용을 방지하세요.
ReservationRejectReqDTO의 status?: ReservationStatus는 거절 API에 부적절한 모든 상태값을 허용합니다. 실제로 apps/owner/src/app/(tabs)/order/page.tsx (Line 151)에서 status: "REQUESTED"를 전달하고 있으며, 이는 거절 시점에 요청 상태를 전송하는 오류입니다. status를 제거하거나 "REJECTED"로 명시적으로 제한하는 것이 안전합니다.
export interface ReservationRejectReqDTO {
- status?: ReservationStatus;
rejectReason: string;
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/api/src/models/owner.ts` around lines 16 - 19, The
ReservationRejectReqDTO currently allows any ReservationStatus which permits
invalid values on reject; update the DTO definition (ReservationRejectReqDTO) to
prevent misuse by either removing the optional status field entirely or
tightening its type to a literal "REJECTED" (e.g., status?: "REJECTED" or
status: "REJECTED") and then adjust callers (such as the call site in
apps/owner/src/app/(tabs)/order/page.tsx that sends status: "REQUESTED") to stop
sending a non-reject status; ensure the DTO and its usages consistently reflect
the chosen approach.
✅ 작업 내용
📝 Description
🚀 설계 의도 및 개선점
📸 스크린샷 (선택)
📎 기타 참고사항
Fixes #108
Summary by CodeRabbit
릴리스 노트
새로운 기능
버그 수정