Feat: 쿠폰 등록 화면 구현 및 API 연결 - #115
Hidden character warning
Conversation
📝 WalkthroughWalkthroughThe credit page adds coupon registration through a modal, redemption API, disabled submission state, Escape-to-close behavior, and a success toast. Shared input, header, overlay, and toast styling are updated to support the new flow. ChangesCoupon redemption
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant CreditContent
participant CouponRegistrationModal
participant redeemCoupon
participant Toast
CreditContent->>CouponRegistrationModal: open coupon modal
CouponRegistrationModal->>redeemCoupon: submit coupon code
redeemCoupon-->>CouponRegistrationModal: return creditAmount
CouponRegistrationModal-->>CreditContent: report success
CreditContent->>Toast: display credited amount
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
There was a problem hiding this comment.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
jobdri/src/components/common/buttons/ButtonCtaModal.tsx (1)
83-89: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winCancel button remains enabled during submission.
submitDisabledonly gates the submit button. A user can click "닫기" whileredeemCouponis in flight, closing the modal while the request continues; if it later resolves,onSuccess/error effects still fire against the parent's state, producing a toast (or silently dropping an error) after the user thought they cancelled.♻️ Proposed fix
<Button label={cancelLabel} size="large" styleType={cancelStyleType} onClick={onCancel} + disabled={submitDisabled} className={clsx("h-[46px] flex-1", cancelClassName)} />🤖 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 `@jobdri/src/components/common/buttons/ButtonCtaModal.tsx` around lines 83 - 89, Update the cancel Button in ButtonCtaModal so it is disabled whenever submitDisabled indicates redemption is in progress, preventing onCancel from closing the modal during the in-flight redeemCoupon request while preserving normal cancellation otherwise.
🧹 Nitpick comments (1)
jobdri/src/components/common/input/InputTextAreaFixedShared.tsx (1)
38-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMake
showBottomLinethe bottom-line guard for the fixed variants too.
InputTextAreaFixedSandInputTextAreaFixedLboth accept and forwardshowBottomLine, butInputTextAreaFixedBasestill rendersInputTextAreaFixedBottomunconditionally atjobdri/src/components/common/input/InputTextAreaFixedShared.tsx:340. PassingshowBottomLine={false}to those variants currently still shows the bottom line, so the shared prop contract is inconsistent.🤖 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 `@jobdri/src/components/common/input/InputTextAreaFixedShared.tsx` at line 38, Update InputTextAreaFixedBase so rendering InputTextAreaFixedBottom is guarded by the showBottomLine prop. Preserve the existing bottom-line rendering when showBottomLine is true or unspecified, while suppressing it when false for InputTextAreaFixedS and InputTextAreaFixedL.
🤖 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 `@jobdri/src/app/credit/page.tsx`:
- Around line 108-114: Update the Toast message in the couponToastCreditAmount
rendering block to format the amount with toLocaleString(), matching the
existing plan.price formatting in the credit page. Keep the current Korean
message and null-check behavior unchanged.
In `@jobdri/src/components/common/credit/CouponRegistrationModal.tsx`:
- Around line 37-44: Update the coupon submission handler around redeemCoupon to
capture the thrown error and display its specific message via setError instead
of always using the generic invalid-code text. Preserve the existing fallback
message for non-Error or message-less failures, and keep setIsSubmitting(false)
in the finally path.
---
Outside diff comments:
In `@jobdri/src/components/common/buttons/ButtonCtaModal.tsx`:
- Around line 83-89: Update the cancel Button in ButtonCtaModal so it is
disabled whenever submitDisabled indicates redemption is in progress, preventing
onCancel from closing the modal during the in-flight redeemCoupon request while
preserving normal cancellation otherwise.
---
Nitpick comments:
In `@jobdri/src/components/common/input/InputTextAreaFixedShared.tsx`:
- Line 38: Update InputTextAreaFixedBase so rendering InputTextAreaFixedBottom
is guarded by the showBottomLine prop. Preserve the existing bottom-line
rendering when showBottomLine is true or unspecified, while suppressing it when
false for InputTextAreaFixedS and InputTextAreaFixedL.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6e06d68a-b9e2-4cc2-9781-9658eee7b3b5
📒 Files selected for processing (9)
jobdri/src/app/credit/page.tsxjobdri/src/components/common/PageHeader.tsxjobdri/src/components/common/buttons/ButtonCtaModal.tsxjobdri/src/components/common/credit/CouponRegistrationModal.tsxjobdri/src/components/common/input/InputTextAreaAutoGrowS.tsxjobdri/src/components/common/input/InputTextAreaFixedShared.tsxjobdri/src/components/common/modal/ModalOverlay.tsxjobdri/src/components/common/toast/Toast.tsxjobdri/src/lib/api/credit.ts
| {couponToastCreditAmount !== null && ( | ||
| <Toast | ||
| message={`${couponToastCreditAmount}크레딧이 충전되었습니다!`} | ||
| variant="check" | ||
| onClose={() => setCouponToastCreditAmount(null)} | ||
| /> | ||
| )} |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Toast credit amount isn't locale-formatted.
couponToastCreditAmount is interpolated raw, unlike plan.price.toLocaleString() used elsewhere in this file (line 129). Larger credited amounts will render without thousands separators.
🐛 Proposed fix
- message={`${couponToastCreditAmount}크레딧이 충전되었습니다!`}
+ message={`${couponToastCreditAmount.toLocaleString()}크레딧이 충전되었습니다!`}📝 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.
| {couponToastCreditAmount !== null && ( | |
| <Toast | |
| message={`${couponToastCreditAmount}크레딧이 충전되었습니다!`} | |
| variant="check" | |
| onClose={() => setCouponToastCreditAmount(null)} | |
| /> | |
| )} | |
| {couponToastCreditAmount !== null && ( | |
| <Toast | |
| message={`${couponToastCreditAmount.toLocaleString()}크레딧이 충전되었습니다!`} | |
| variant="check" | |
| onClose={() => setCouponToastCreditAmount(null)} | |
| /> | |
| )} |
🤖 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 `@jobdri/src/app/credit/page.tsx` around lines 108 - 114, Update the Toast
message in the couponToastCreditAmount rendering block to format the amount with
toLocaleString(), matching the existing plan.price formatting in the credit
page. Keep the current Korean message and null-check behavior unchanged.
| try { | ||
| const result = await redeemCoupon(couponCode); | ||
| onSuccess(result.creditAmount); | ||
| } catch { | ||
| setError("쿠폰 번호를 확인해주세요."); | ||
| } finally { | ||
| setIsSubmitting(false); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Specific coupon-redemption error is discarded.
redeemCoupon (credit.ts) deliberately throws payload.error || payload.message || fallback to surface specific reasons (invalid code, already redeemed, expired, server error), but this bare catch {} always shows the same generic "쿠폰 번호를 확인해주세요." Users lose meaningful feedback about why redemption actually failed.
🐛 Proposed fix to surface the actual error message
- } catch {
- setError("쿠폰 번호를 확인해주세요.");
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "쿠폰 번호를 확인해주세요.");
} finally {📝 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.
| try { | |
| const result = await redeemCoupon(couponCode); | |
| onSuccess(result.creditAmount); | |
| } catch { | |
| setError("쿠폰 번호를 확인해주세요."); | |
| } finally { | |
| setIsSubmitting(false); | |
| } | |
| try { | |
| const result = await redeemCoupon(couponCode); | |
| onSuccess(result.creditAmount); | |
| } catch (err) { | |
| setError(err instanceof Error ? err.message : "쿠폰 번호를 확인해주세요."); | |
| } finally { | |
| setIsSubmitting(false); | |
| } |
🤖 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 `@jobdri/src/components/common/credit/CouponRegistrationModal.tsx` around lines
37 - 44, Update the coupon submission handler around redeemCoupon to capture the
thrown error and display its specific message via setError instead of always
using the generic invalid-code text. Preserve the existing fallback message for
non-Error or message-less failures, and keep setIsSubmitting(false) in the
finally path.
🔗 관련 이슈
📝 개요
⌨️ 작업 상세 내용
쿠폰 등록하기버튼 추가Escape키 닫기 처리POST /api/payments/coupons/redeemAPI 연결Summary by CodeRabbit