fix: 클래스 결제관리 연동 보강#687
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughPaymentRefundPage가 결제 관리 기능을 그룹스터디와 클래스 결제로 이분화하여 확장됩니다. 각 결제 유형은 서로 다른 API 훅, 필터 옵션, 행 컴포넌트를 통해 조건부로 처리되며, 클래스 결제는 상세 조회 모달을 추가로 제공합니다. Changes코스 결제 관리 기능 추가
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 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. Comment |
120542d to
da42206
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@src/app/`(admin)/admin/sales-management/payment-refund/page.tsx:
- Around line 358-364: transaction.groupStudyStatus is nullable but
STUDY_STATUS_MAP expects a non-null key, so the current lookup can render
"undefined" in the UI; change the rendering to perform a null-safe lookup and
provide a fallback label. Specifically, compute a safe key from
transaction.groupStudyStatus (e.g., use nullish coalescing or a guard) before
indexing STUDY_STATUS_MAP and then fallback to a default string like "Unknown"
if the map lookup returns undefined; update the JSX that references
transaction.groupStudyStatus and STUDY_STATUS_MAP to use this safe value.
- Around line 296-301: The map rendering uses an optional ID field for the React
key (paymentCode) without an index fallback which can cause duplicate keys when
paymentCode is undefined; update the map callbacks for list.map that render
GroupStudyPaymentRow (and similarly CoursePaymentRow) to include the index in
the map signature and use a fallback like paymentCode ?? index (or include both
values) for the key prop so keys are always unique (e.g., change the map to
accept (transaction, index) and set key using transaction.paymentCode ?? index).
In `@src/hooks/queries/admin/admin-course-payment-api.ts`:
- Around line 23-52: Both useGetAdminCoursePayments and
useGetAdminCoursePaymentDetail are missing an onError handler on their useQuery
calls; add an onError option to each useQuery that forwards the caught error to
the shared error handler (from utils/error-handler.ts) and triggers the toast
via useToastStore so the common error flow and user notification are executed on
query failures. Locate the useQuery invocations in the functions
useGetAdminCoursePayments and useGetAdminCoursePaymentDetail and add an onError:
(error) => { /* call shared error handler with error and invoke toast store to
show message */ } implemented using the existing utilities.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 804eb398-4823-44df-8b11-0b42f6ba3971
📒 Files selected for processing (4)
src/app/(admin)/admin/sales-management/payment-refund/page.tsxsrc/hooks/queries/admin/admin-course-payment-api.tssrc/hooks/queries/admin/admin-payment-api.tssrc/types/api/course.types.ts
| list.map((transaction) => ( | ||
| <GroupStudyPaymentRow | ||
| key={`${transaction.paymentCode}`} | ||
| className="border-b-border-default border-b" | ||
| > | ||
| {/* 거래 ID */} | ||
| <td className="py-200 pl-250"> | ||
| <span className="font-designer-14r text-text-default"> | ||
| {transaction.paymentCode || '-'} | ||
| </span> | ||
| </td> | ||
|
|
||
| {/* 스터디명 */} | ||
| <td className="py-200 pl-125"> | ||
| <span className="font-designer-14r text-text-default"> | ||
| {`${transaction.groupStudyName} (${ | ||
| STUDY_STATUS_MAP[transaction.groupStudyStatus] | ||
| })`} | ||
| </span> | ||
| </td> | ||
|
|
||
| {/* 결제자 */} | ||
| <td className="py-200 pl-125"> | ||
| <span className="font-designer-14r text-text-default"> | ||
| {transaction.paymentMemberName || '-'}( | ||
| {transaction.paymentMemberId || '-'}) | ||
| </span> | ||
| </td> | ||
|
|
||
| {/* 결제 내역 */} | ||
| <td className="py-200 pl-125"> | ||
| <span className="font-designer-14r text-text-default"> | ||
| {transaction.transactionAmount?.toLocaleString() || 0} | ||
| 원( | ||
| {transaction.paymentMethod || '-'}) | ||
| </span> | ||
| </td> | ||
|
|
||
| {/* 상태 */} | ||
| <td className="py-200 pl-125"> | ||
| <Badge color={statusConfig.color} shape="rectangle"> | ||
| {statusConfig.label} | ||
| </Badge> | ||
| </td> | ||
|
|
||
| {/* 일시 */} | ||
| <td className="py-200 pl-125"> | ||
| <span className="font-designer-14r text-text-default"> | ||
| {formatToKST(transaction.transactionedAt) | ||
| ? format( | ||
| formatToKST(transaction.transactionedAt)!, | ||
| 'yyyy.MM.dd HH:mm', | ||
| ) | ||
| : '-'} | ||
| </span> | ||
| </td> | ||
|
|
||
| {/* 액션 버튼 */} | ||
| <td className="py-200 pr-250"> | ||
| <SalesActionButtons | ||
| paymentId={transaction.paymentId} | ||
| paymentReceiptUrl={transaction.paymentReceiptUrl} | ||
| paymentHistoryType={transaction.paymentHistoryType} | ||
| groupStudyName={transaction.groupStudyName} | ||
| paymentMemberName={transaction.paymentMemberName} | ||
| paymentMemberId={transaction.paymentMemberId} | ||
| transactionAmount={transaction.transactionAmount} | ||
| /> | ||
| </td> | ||
| </tr> | ||
| ); | ||
| }) | ||
| ) : ( | ||
| <tr> | ||
| <td | ||
| colSpan={7} | ||
| className="border-b-border-default border-b py-[200px] text-center" | ||
| > | ||
| <p className="font-designer-16r text-text-subtlest"> | ||
| 매출 내역이 없습니다. | ||
| </p> | ||
| </td> | ||
| </tr> | ||
| )} | ||
| </tbody> | ||
| transaction={transaction} | ||
| /> | ||
| )) |
There was a problem hiding this comment.
코딩 가이드라인 위반: 선택적 ID 필드에 index fallback 누락
paymentCode가 optional 필드인 경우(354번 라인에서 || '-' fallback 사용), key prop에 index fallback이 필요합니다. 여러 항목의 paymentCode가 undefined면 중복 key 문제가 발생할 수 있습니다.
🔧 수정 제안
- list.map((transaction) => (
+ list.map((transaction, index) => (
<GroupStudyPaymentRow
- key={`${transaction.paymentCode}`}
+ key={transaction.paymentCode ?? index}
transaction={transaction}
/>
))309-314번 라인의 CoursePaymentRow에도 동일하게 적용:
- coursePaymentList.map((payment) => (
+ coursePaymentList.map((payment, index) => (
<CoursePaymentRow
- key={`${payment.paymentCode}`}
+ key={payment.paymentCode ?? index}
payment={payment}
/>
))As per coding guidelines: "Optional ID fields in React key props need ?? index fallback"
📝 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.
| list.map((transaction) => ( | |
| <GroupStudyPaymentRow | |
| key={`${transaction.paymentCode}`} | |
| className="border-b-border-default border-b" | |
| > | |
| {/* 거래 ID */} | |
| <td className="py-200 pl-250"> | |
| <span className="font-designer-14r text-text-default"> | |
| {transaction.paymentCode || '-'} | |
| </span> | |
| </td> | |
| {/* 스터디명 */} | |
| <td className="py-200 pl-125"> | |
| <span className="font-designer-14r text-text-default"> | |
| {`${transaction.groupStudyName} (${ | |
| STUDY_STATUS_MAP[transaction.groupStudyStatus] | |
| })`} | |
| </span> | |
| </td> | |
| {/* 결제자 */} | |
| <td className="py-200 pl-125"> | |
| <span className="font-designer-14r text-text-default"> | |
| {transaction.paymentMemberName || '-'}( | |
| {transaction.paymentMemberId || '-'}) | |
| </span> | |
| </td> | |
| {/* 결제 내역 */} | |
| <td className="py-200 pl-125"> | |
| <span className="font-designer-14r text-text-default"> | |
| {transaction.transactionAmount?.toLocaleString() || 0} | |
| 원( | |
| {transaction.paymentMethod || '-'}) | |
| </span> | |
| </td> | |
| {/* 상태 */} | |
| <td className="py-200 pl-125"> | |
| <Badge color={statusConfig.color} shape="rectangle"> | |
| {statusConfig.label} | |
| </Badge> | |
| </td> | |
| {/* 일시 */} | |
| <td className="py-200 pl-125"> | |
| <span className="font-designer-14r text-text-default"> | |
| {formatToKST(transaction.transactionedAt) | |
| ? format( | |
| formatToKST(transaction.transactionedAt)!, | |
| 'yyyy.MM.dd HH:mm', | |
| ) | |
| : '-'} | |
| </span> | |
| </td> | |
| {/* 액션 버튼 */} | |
| <td className="py-200 pr-250"> | |
| <SalesActionButtons | |
| paymentId={transaction.paymentId} | |
| paymentReceiptUrl={transaction.paymentReceiptUrl} | |
| paymentHistoryType={transaction.paymentHistoryType} | |
| groupStudyName={transaction.groupStudyName} | |
| paymentMemberName={transaction.paymentMemberName} | |
| paymentMemberId={transaction.paymentMemberId} | |
| transactionAmount={transaction.transactionAmount} | |
| /> | |
| </td> | |
| </tr> | |
| ); | |
| }) | |
| ) : ( | |
| <tr> | |
| <td | |
| colSpan={7} | |
| className="border-b-border-default border-b py-[200px] text-center" | |
| > | |
| <p className="font-designer-16r text-text-subtlest"> | |
| 매출 내역이 없습니다. | |
| </p> | |
| </td> | |
| </tr> | |
| )} | |
| </tbody> | |
| transaction={transaction} | |
| /> | |
| )) | |
| list.map((transaction, index) => ( | |
| <GroupStudyPaymentRow | |
| key={transaction.paymentCode ?? index} | |
| transaction={transaction} | |
| /> | |
| )) |
🤖 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 `@src/app/`(admin)/admin/sales-management/payment-refund/page.tsx around lines
296 - 301, The map rendering uses an optional ID field for the React key
(paymentCode) without an index fallback which can cause duplicate keys when
paymentCode is undefined; update the map callbacks for list.map that render
GroupStudyPaymentRow (and similarly CoursePaymentRow) to include the index in
the map signature and use a fallback like paymentCode ?? index (or include both
values) for the key prop so keys are always unique (e.g., change the map to
accept (transaction, index) and set key using transaction.paymentCode ?? index).
| <td className="py-200 pl-125"> | ||
| <span className="font-designer-14r text-text-default"> | ||
| {`${transaction.groupStudyName} (${ | ||
| STUDY_STATUS_MAP[transaction.groupStudyStatus] | ||
| })`} | ||
| </span> | ||
| </td> |
There was a problem hiding this comment.
groupStudyStatus 접근 시 null 안전성 보장 필요
STUDY_STATUS_MAP의 타입 정의가 NonNullable<AdminTransactionListResponse['groupStudyStatus']>를 사용하므로 원본 타입은 nullable입니다. transaction.groupStudyStatus가 undefined인 경우 map lookup이 undefined를 반환하여 UI에 "undefined"가 표시될 수 있습니다.
🛡️ 수정 제안
<td className="py-200 pl-125">
<span className="font-designer-14r text-text-default">
- {`${transaction.groupStudyName} (${
- STUDY_STATUS_MAP[transaction.groupStudyStatus]
- })`}
+ {`${transaction.groupStudyName || '-'} (${
+ transaction.groupStudyStatus
+ ? STUDY_STATUS_MAP[transaction.groupStudyStatus]
+ : '-'
+ })`}
</span>
</td>📝 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.
| <td className="py-200 pl-125"> | |
| <span className="font-designer-14r text-text-default"> | |
| {`${transaction.groupStudyName} (${ | |
| STUDY_STATUS_MAP[transaction.groupStudyStatus] | |
| })`} | |
| </span> | |
| </td> | |
| <td className="py-200 pl-125"> | |
| <span className="font-designer-14r text-text-default"> | |
| {`${transaction.groupStudyName || '-'} (${ | |
| transaction.groupStudyStatus | |
| ? STUDY_STATUS_MAP[transaction.groupStudyStatus] | |
| : '-' | |
| })`} | |
| </span> | |
| </td> |
🤖 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 `@src/app/`(admin)/admin/sales-management/payment-refund/page.tsx around lines
358 - 364, transaction.groupStudyStatus is nullable but STUDY_STATUS_MAP expects
a non-null key, so the current lookup can render "undefined" in the UI; change
the rendering to perform a null-safe lookup and provide a fallback label.
Specifically, compute a safe key from transaction.groupStudyStatus (e.g., use
nullish coalescing or a guard) before indexing STUDY_STATUS_MAP and then
fallback to a default string like "Unknown" if the map lookup returns undefined;
update the JSX that references transaction.groupStudyStatus and STUDY_STATUS_MAP
to use this safe value.
| return useQuery({ | ||
| queryKey: [ | ||
| 'adminCoursePayments', | ||
| courseId, | ||
| memberId, | ||
| status, | ||
| paymentCode, | ||
| page, | ||
| size, | ||
| ], | ||
| queryFn: async () => { | ||
| const { data } = await axiosInstanceV5.get< | ||
| ApiBaseResponse< | ||
| CoursePaymentPageResponse<AdminCoursePaymentListItemResponse> | ||
| > | ||
| >('admin/course-payments', { | ||
| params: { | ||
| courseId, | ||
| memberId, | ||
| status, | ||
| paymentCode, | ||
| page, | ||
| size, | ||
| }, | ||
| }); | ||
|
|
||
| return data.content; | ||
| }, | ||
| enabled, | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# 목적: 변경된 admin course payment query hook에 onError/공통 에러 핸들링이 있는지 확인
rg -n "export const useGetAdminCoursePayments|export const useGetAdminCoursePaymentDetail|useQuery\\(|onError\\s*:" src/hooks/queries/admin/admin-course-payment-api.ts -C2
rg -n "utils/error-handler|useToastStore" src/hooks/queries/admin/admin-course-payment-api.ts -C2Repository: code-zero-to-one/study-platform-client
Length of output: 589
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="src/hooks/queries/admin/admin-course-payment-api.ts"
echo "== Imports check =="
rg -n "error-handler|useToastStore|handleError|toast" "$FILE" || true
echo "== onError check =="
rg -n "onError\s*:" "$FILE" || true
echo "== useQuery blocks (small context) =="
rg -n "useQuery\s*\(" "$FILE" -C 6 || trueRepository: code-zero-to-one/study-platform-client
Length of output: 853
쿼리 실패 시 공통 에러 핸들링(onError) 추가 요청
src/hooks/queries/admin/admin-course-payment-api.ts의 useGetAdminCoursePayments와 useGetAdminCoursePaymentDetail 모두 useQuery에 onError가 없어, 실패 시 utils/error-handler.ts + useToastStore 기반의 토스트/공통 처리 경로가 적용되지 않습니다. 두 훅의 useQuery에 onError를 추가해 에러를 공통 핸들러로 보내고 사용자 알림을 노출해주세요.
🤖 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 `@src/hooks/queries/admin/admin-course-payment-api.ts` around lines 23 - 52,
Both useGetAdminCoursePayments and useGetAdminCoursePaymentDetail are missing an
onError handler on their useQuery calls; add an onError option to each useQuery
that forwards the caught error to the shared error handler (from
utils/error-handler.ts) and triggers the toast via useToastStore so the common
error flow and user notification are executed on query failures. Locate the
useQuery invocations in the functions useGetAdminCoursePayments and
useGetAdminCoursePaymentDetail and add an onError: (error) => { /* call shared
error handler with error and invoke toast store to show message */ } implemented
using the existing utilities.
요약
백엔드 계약 확인
검증
Summary by CodeRabbit
릴리스 노트
New Features
Improvements