From 978206438e5f9702785c2d93c3f2799be60bccb8 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 2 Oct 2026 11:34:16 +0900 Subject: [PATCH] =?UTF-8?q?docs(gift):=20=EC=84=A0=EB=AC=BC=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=20=EA=B5=AC=ED=98=84=20=ED=94=84=EB=A1=AC=ED=94=84?= =?UTF-8?q?=ED=8A=B8=EB=A5=BC=20=EC=B6=94=EA=B0=80=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin-page-prompts.md | 2 +- .../gift-detail-tracking-page-prompt.md | 433 ++++++++++++++++++ .../gift-mailbox-admin-page-prompt.md | 114 +++++ 3 files changed, 548 insertions(+), 1 deletion(-) create mode 100644 docs/20260929_크리에이터_선물하기/gift-detail-tracking-page-prompt.md create mode 100644 docs/20260929_크리에이터_선물하기/gift-mailbox-admin-page-prompt.md diff --git a/docs/20260929_크리에이터_선물하기/admin-page-prompts.md b/docs/20260929_크리에이터_선물하기/admin-page-prompts.md index 9953be7e..cc2f1b66 100644 --- a/docs/20260929_크리에이터_선물하기/admin-page-prompts.md +++ b/docs/20260929_크리에이터_선물하기/admin-page-prompts.md @@ -245,7 +245,7 @@ type AdminGiftOperationStatusResponse = { 목표: - 관리자 메뉴 `선물함 관리 > 받을 주소`에서 팬이 선물을 보낼 전역 단일 주소를 조회하고 저장한다. -- 이 주소는 팬의 선물 상세 API에서 운송장 등록 전 상태일 때 `mailbox`로 노출된다. +- 이 주소는 팬의 보낸 선물 상세 API에서 정상 진행 상태 중 전달 완료 전(`RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED`)일 때 `mailbox`로 노출된다. - 기존 관리자 페이지의 폼, 저장 버튼, 토스트, 에러 표시 패턴을 그대로 따른다. 라우트: diff --git a/docs/20260929_크리에이터_선물하기/gift-detail-tracking-page-prompt.md b/docs/20260929_크리에이터_선물하기/gift-detail-tracking-page-prompt.md new file mode 100644 index 00000000..127baf12 --- /dev/null +++ b/docs/20260929_크리에이터_선물하기/gift-detail-tracking-page-prompt.md @@ -0,0 +1,433 @@ +# 선물 상세 및 운송장 등록 페이지 생성 프롬프트 + +이 문서는 Android/iOS 앱의 `선물함` 리스트에서 진입하는 선물 상세 페이지와 운송장 등록 페이지를 생성할 때 사용하는 프롬프트다. + +참고 Figma: + +- 선물 보내기 신청 상세 운송장 등록 전 UI: `2481:19011` +- 선물 보내기 신청 상세 운송장 등록 후 UI: `2481:19152` +- 선물 보내기 신청 상세 전달 완료 UI: `2481:19320` +- 전달 불가 UI: `2481:19456` +- 취소 버튼 다이얼로그 UI: `2481:19500` +- 취소 완료 페이지 UI: `2481:19521` +- 운송장 등록 UI: `2481:19066` +- 운송장 정보를 채우고 난 후 UI: `2481:19109` + +전제: + +- 선물함 리스트 item 탭 시 `applicationNo`를 전달받아 상세 페이지로 이동한다. +- 새 디자인 시스템을 만들지 말고 Android/iOS 각각의 기존 디자인 시스템, 컴포넌트, API client, 상태 관리, toast/dialog, navigation 패턴을 재사용한다. +- 모든 API 응답은 공통 envelope를 사용하며 실제 payload는 `response.data`다. +- Figma의 진행 상태는 4단계처럼 보이지만 실제 구현은 서버의 `statusTimeline` 5개 상태를 모두 사용한다. +- 운송장 등록의 택배사 선택지는 서버 API가 없으므로 앱 로컬 상수로 관리한다. +- 취소 확인 팝업은 앱의 `v2 dialog` 컴포넌트를 기본으로 사용한다. + +--- + +## 모바일 앱 구현 프롬프트 + +```text +Android/iOS 앱의 선물 상세 페이지와 운송장 등록 페이지를 Figma 기준으로 생성하고 API 연동을 구현해줘. + +범위: +- 선물함 리스트에서 `applicationNo`를 받아 선물 상세 페이지로 이동한다. +- 상세 페이지는 `GET /api/v2/gifts/{applicationNo}` 응답으로 렌더링한다. +- `trackingRequired=true`인 보낸 선물 상세에서는 운송장 등록 CTA를 표시하고 운송장 등록 페이지로 이동한다. +- 운송장 등록 페이지는 같은 상세 API를 조회해 신청 요약과 보낼 주소를 표시한 뒤 `POST /api/v2/gifts/{applicationNo}/tracking`으로 운송장 정보를 등록한다. + +중요 전제: +- Figma의 visual structure와 문구를 우선 따르되, 상태 개수와 데이터 노출은 API 계약을 우선한다. +- 상세 진행 단계는 Figma처럼 4단계로 고정하지 말고 API `statusTimeline`의 5개 정상 진행 상태를 모두 렌더링한다. +- 택배사 목록은 API로 조회하지 않는다. 앱 로컬 상수로 관리하고 선택한 한글 표시명을 `courierCompanyName`으로 전송한다. +- 운송장 등록 완료 후 수정 기능은 만들지 않는다. + +상세 페이지 진입: +- 선물함 리스트 item에서 받은 `applicationNo`로 진입한다. +- 화면 진입 시 `GET /api/v2/gifts/{applicationNo}`를 호출한다. +- 조회 결과 `status=CANCELED`이면 일반 신청 상세 화면을 렌더링하지 말고 취소 완료 페이지 UI를 보여준다. +- 로딩 중에는 기존 앱 상세 화면 로딩/스켈레톤 패턴을 사용한다. +- 실패 시 기존 앱 toast/dialog와 재시도 패턴을 사용한다. + +상세 페이지 공통 레이아웃: +1. 상단 고정 내비게이션 + - 타이틀: `신청 상세` + - 뒤로가기 버튼은 기존 앱 패턴을 사용한다. +2. 스크롤 콘텐츠 + - 배송 상태 hero + - 진행 상태 progress + - 상태별 action button 영역 + - 선물 정보 + - 필요한 경우 보내는 사람, 받는 사서함, 사유 섹션 +3. 하단 safe area + - 기존 앱 패턴을 따른다. + +상태별 hero 문구: +| status | chip | title | body | +|---|---|---|---| +| `RECEIVED` | 없음 또는 `접수 완료` | `선물 신청 완료!` | `선물이 정상적으로 접수되었어요!` | +| `TRACKING_REGISTERED` | `발송 확인` | `사서함으로 이동 중이에요` | 없음 | +| `ARRIVED_AT_MAILBOX` | `사서함 도착` | `선물이 사서함에 도착했어요` | 없음 | +| `INSPECTION_COMPLETED` | `검수 완료` | `선물 검수를 완료했어요` | 없음 | +| `DELIVERED` | `전달 완료` | `크리에이터에게 선물을 전달했어요` | 없음 | +| `UNDELIVERABLE` | `전달 불가` | `전달할 수 없는 품목입니다` | `보내주신 선물은 SODALIVE 선물 정책에 따라 크리에이터에게 전달할 수 없는 품목으로 확인되었습니다.` | +| `CANCELED` | `신청 취소` | `선물 신청이 취소되었어요` | `사용한 캔은 환불 처리됩니다.` | + +진행 상태 progress: +- `statusTimeline` 배열을 그대로 사용한다. +- 항상 아래 5개 단계를 이 순서로 표시한다. + 1. `RECEIVED`: `신청 접수` + 2. `TRACKING_REGISTERED`: `발송 확인` + 3. `ARRIVED_AT_MAILBOX`: `사서함 도착` + 4. `INSPECTION_COMPLETED`: `검수 완료` + 5. `DELIVERED`: `전달 완료` +- `occurredAt`이 있으면 해당 단계는 완료 색상으로 표시하고 `MM.DD`를 표시한다. +- `occurredAt`이 null이면 비활성 색상으로 표시하고 날짜는 표시하지 않는다. +- `UNDELIVERABLE`, `CANCELED`는 정상 진행 단계가 아니므로 progress step으로 추가하지 않는다. +- 종료 상태에서도 `statusTimeline`에 있는 정상 진행 이력은 그대로 표시한다. + +상세 페이지 섹션 표시 규칙: +- `선물 정보`는 항상 표시한다. +- `direction=SENT`이면 크리에이터명은 `giftInfo.recipientCreatorNickname`을 사용한다. +- `direction=RECEIVED`이면 보낸 팬명은 `giftInfo.senderNickname`을 사용한다. +- `senderInfo`가 있으면 `보내는 사람` 섹션을 표시한다. +- `mailbox`가 있으면 `받는 사서함` 또는 `보내실 주소` 섹션을 표시한다. +- `recipientAddress`가 있으면 `받는 사람` 또는 `배송지` 섹션을 표시한다. +- `delivery.undeliverableReason`이 있으면 `사유` 섹션을 표시한다. + +선물 정보 표시 필드: +- 크리에이터 또는 보낸 팬 닉네임 +- 선물 사이즈: `giftInfo.sizeName` +- 카테고리: `giftInfo.categoryName` +- 신청번호: `applicationNo` 또는 `giftInfo.applicationNo` +- 캔: `giftInfo.paidCan`이 null이 아닐 때만 표시 +- 운송장 번호: `giftInfo.tracking`이 null이 아닐 때 표시 +- 검수일: `statusTimeline`에서 `INSPECTION_COMPLETED.occurredAt`이 있으면 표시 +- 전달 완료일: `statusTimeline`에서 `DELIVERED.occurredAt`이 있으면 표시 +- 취소일: `delivery.canceledAt`이 있으면 표시 + +받는 사서함/보내실 주소 표시: +- `mailbox.name`을 사서함/받는 분으로 표시한다. +- `mailbox.address`를 주소로 표시한다. +- `mailbox.phoneNumber`를 연락처로 표시한다. +- `mailbox`는 관리자가 등록한 전역 단일 주소다. 상대방 주소가 아니다. +- `mailbox`는 `direction=SENT`이고 `status`가 `RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED`일 때 내려온다. +- `DELIVERED`, `UNDELIVERABLE`, `CANCELED` 또는 `direction=RECEIVED`에서는 `mailbox=null`이다. +- `mailbox=null`이면 해당 섹션을 표시하지 않는다. + +보내는 사람 표시: +- `senderInfo.name` +- `senderInfo.phoneNumber` +- `senderInfo.address` +- 이 정보는 보낸 팬 본인이 신청 시 입력한 정보다. + +전달 불가 표시: +- `status=UNDELIVERABLE`이면 Figma `2481:19456` 패턴을 따른다. +- 사유 섹션의 제목은 `delivery.undeliverableReason`을 사용한다. +- `delivery.undeliverableReason`이 null이면 사유 섹션 제목은 `전달 불가`로 표시한다. +- 안내 문구는 `해당 선물은 정책에 따라 폐기되며 반송되지 않습니다.`를 사용한다. + +상세 페이지 action: +- `trackingRequired=true`이면 하단 주요 CTA `운송장 작성하기`를 표시하고 운송장 등록 페이지로 이동한다. +- `direction=SENT && status=RECEIVED`이면 보조 action `신청 취소`를 표시할 수 있다. +- 신청 취소 버튼을 누르면 즉시 API를 호출하지 말고 `v2 dialog` 확인 팝업을 먼저 표시한다. +- `status=DELIVERED && direction=SENT`이면 `리뷰 남기기` 버튼을 표시하고 기존 리뷰 작성 화면/플로우로 이동한다. +- `문의하기`, `선물 정책 확인` 버튼은 기존 앱에 해당 이동 경로가 있으면 연결하고, 없으면 기존 정책에 맞춰 숨기거나 비활성 처리한다. + +신청 취소 확인 다이얼로그: +- Figma `2481:19500`을 참고한다. +- 앱의 기본 `v2 dialog` 컴포넌트를 사용한다. 새 dialog 컴포넌트를 만들지 않는다. +- 제목: `신청 취소` +- 본문: + - `{크리에이터}에게 보내는 선물을 취소할까요?` + - `선물 보내기 규정에 따라 선물 취소 및 사용한 캔이 모두 환불됩니다.` +- 왼쪽 action: `나가기` + - dialog만 닫고 상세/운송장 등록 화면 상태를 유지한다. +- 오른쪽 destructive action: `취소하기` + - `POST /api/v2/gifts/{applicationNo}/cancel`을 호출한다. + - 호출 중에는 중복 탭을 막고 loading 상태를 표시한다. + - 실패 시 dialog를 닫지 말고 기존 앱 에러 toast/dialog 패턴으로 안내한다. + - 성공 시 취소 완료 페이지로 replace navigation 한다. + +취소 완료 페이지: +- Figma `2481:19521`을 참고한다. +- 취소 API 성공 직후뿐 아니라 상세 페이지 접근 시 `GET /api/v2/gifts/{applicationNo}` 응답이 `status=CANCELED`인 경우에도 이 화면을 보여준다. +- 상단 타이틀: `취소 완료` +- hero chip: `취소 완료` +- hero title: `선물 신청이 취소되었어요` +- hero body: `{크리에이터}에게 보내는 선물 신청({applicationNo})이 취소되었습니다. 취소한 신청은 되돌릴 수 없으며, 다시 보내려면 새로 신청해 주세요.` +- 섹션 제목: `취소 상세` +- 취소 상세 카드 표시 필드: + - 크리에이터: `giftInfo.recipientCreatorNickname` + - 선물 사이즈: `giftInfo.sizeName` + - 카테고리: `giftInfo.categoryName` + - 신청번호: `applicationNo` + - 신청일: `statusTimeline`의 `RECEIVED.occurredAt` + - 취소일: 취소 API response의 `canceledAt` 또는 상세 API 조회 시 `delivery.canceledAt` + - 환불 캔: 취소 API response의 `priceCan` 또는 상세 API의 `giftInfo.paidCan` +- 하단 또는 카드 아래에 `문의하기` 버튼을 표시한다. +- 뒤로가기 동작은 취소 전 상세/운송장 등록 화면으로 돌아가지 않게 한다. 기존 앱 navigation 정책에 맞춰 선물함 리스트 또는 이전 안전 화면으로 이동한다. + +운송장 등록 페이지 진입: +- 상세 페이지에서 `trackingRequired=true`일 때만 진입시킨다. +- 진입 시 `GET /api/v2/gifts/{applicationNo}`를 다시 조회한다. +- 조회 결과에서 `trackingRequired=false`이면 이미 등록 불가 상태이므로 기존 오류 처리 후 상세 페이지로 돌아간다. + +운송장 등록 페이지 레이아웃: +1. 상단 고정 내비게이션 + - 타이틀: `운송장 등록` +2. 신청 건 요약 카드 + - `applicationNo` + - 크리에이터 닉네임: `giftInfo.recipientCreatorNickname` + - `giftInfo.sizeName · giftInfo.categoryName · 신청일` + - 신청일은 `statusTimeline`의 `RECEIVED.occurredAt`을 `YYYY.MM.DD`로 표시한다. +3. 보내실 주소 + - `mailbox`를 사용한다. + - `mailbox=null`이면 운송장 등록을 막고 `받을 주소가 등록되어 있지 않습니다.` 오류를 표시한다. +4. 운송장 정보 + - 택배사 select + - 운송장 번호 input +5. 등록 안내 + - Figma의 안내 문구를 유지한다. +6. 하단 action bar + - 왼쪽: `신청 취소` + - 오른쪽: `등록 완료` + +택배사 로컬 상수: +- 서버에서 택배사 옵션을 조회하지 않는다. +- 앱에 아래 로컬 옵션을 둔다. 이미 앱 공통 택배사 상수가 있으면 그것을 우선 사용한다. +```ts +const giftCourierCompanyNames = [ + "CJ대한통운", + "우체국택배", + "GS25 편의점택배", + "CU 편의점택배", + "한진택배", + "롯데택배", + "로젠택배", + "경동택배", + "대신택배", + "일양로지스", + "천일택배", + "합동택배", + "건영택배", + "농협택배" +]; +``` +- 선택 UI는 기존 앱의 bottom sheet, picker, dialog 중 이미 쓰는 방식을 사용한다. +- API에는 선택된 한글 표시명을 그대로 `courierCompanyName`으로 보낸다. + +운송장 등록 CTA 활성 조건: +- 택배사를 선택했다. +- 운송장 번호를 입력했다. +- 운송장 번호는 공백만 입력할 수 없다. +- submit 중에는 중복 탭을 막고 loading 상태를 표시한다. +- 비활성 상태는 Figma `2481:19066`, 활성 상태는 Figma `2481:19109`를 따른다. + +운송장 등록 성공 처리: +- `POST /api/v2/gifts/{applicationNo}/tracking` 성공 후 성공 toast를 표시한다. +- 상세 페이지로 돌아가거나 replace navigation으로 상세 페이지를 다시 연다. +- 상세 페이지는 `GET /api/v2/gifts/{applicationNo}`를 다시 호출해 `TRACKING_REGISTERED` 상태를 표시한다. + +API 명세: + +선물 상세 조회: +- Method: `GET` +- URL: `/api/v2/gifts/{applicationNo}` +- 인증: 로그인 필요 +- Response envelope 예시: +```json +{ + "success": true, + "data": { + "applicationNo": "A-1002609300001", + "direction": "SENT", + "status": "RECEIVED", + "statusName": "접수 완료", + "giftInfo": { + "recipientCreatorNickname": "달빛수집가", + "senderNickname": null, + "sizeName": "소형", + "categoryName": "잡화", + "applicationNo": "A-1002609300001", + "paidCan": 100, + "tracking": null, + "shippingRequestedAt": null + }, + "senderInfo": { + "name": "김소다", + "phoneNumber": "01012345678", + "address": "(04030) 서울특별시 마포구 양화로 000, 3층" + }, + "recipientAddress": null, + "mailbox": { + "name": "소다라이브 사서함", + "address": "(06174) 서울특별시 강남구 테헤란로108길 8, 유민빌딩 4층", + "phoneNumber": "02-2055-1477" + }, + "trackingRequired": true, + "recipientAddressRequired": false, + "recipientAddressDeadlineAt": null, + "delivery": { + "canceledAt": null, + "undeliverableAt": null, + "undeliverableReason": null + }, + "statusTimeline": [ + { + "status": "RECEIVED", + "statusName": "접수 완료", + "occurredAt": "2026-09-18T03:00:00Z" + }, + { + "status": "TRACKING_REGISTERED", + "statusName": "발송 확인", + "occurredAt": null + }, + { + "status": "ARRIVED_AT_MAILBOX", + "statusName": "사서함 도착", + "occurredAt": null + }, + { + "status": "INSPECTION_COMPLETED", + "statusName": "검수완료", + "occurredAt": null + }, + { + "status": "DELIVERED", + "statusName": "전달완료", + "occurredAt": null + } + ] + }, + "message": "" +} +``` + +Response `data` 필드: + +| 필드 | 타입 | 설명 | +|---|---|---| +| `applicationNo` | string | 선물 신청번호 | +| `direction` | string | `SENT` 또는 `RECEIVED` | +| `status` | string | 선물 상태 코드 | +| `statusName` | string | 서버 상태 표시명 | +| `giftInfo` | object | 선물 기본 정보 | +| `giftInfo.recipientCreatorNickname` | string 또는 null | 보낸 선물 관점에서 받는 크리에이터 닉네임 | +| `giftInfo.senderNickname` | string 또는 null | 받은 선물 관점에서 보낸 팬 닉네임 | +| `giftInfo.sizeName` | string | 선물 사이즈명 | +| `giftInfo.categoryName` | string | 카테고리명 | +| `giftInfo.applicationNo` | string 또는 null | 보낸 선물 관점 신청번호 | +| `giftInfo.paidCan` | number 또는 null | 보낸 선물 관점 사용 캔 | +| `giftInfo.tracking` | string 또는 null | 운송장 표시 문자열. 예: `한진택배_123456789012` | +| `giftInfo.shippingRequestedAt` | string 또는 null | 받은 선물 관점 배송신청일. UTC ISO 문자열 | +| `senderInfo` | object 또는 null | 보낸 팬 본인이 신청 시 입력한 보내는 사람 정보 | +| `senderInfo.name` | string | 이름 | +| `senderInfo.phoneNumber` | string | 휴대폰 번호 | +| `senderInfo.address` | string | `(우편번호) 주소, 상세주소` 형식 주소 | +| `recipientAddress` | object 또는 null | 받는 크리에이터 본인이 입력한 배송지 | +| `mailbox` | object 또는 null | 관리자가 등록한 전역 선물 받을 주소. 보낸 선물의 `DELIVERED` 전 정상 진행 상태에서만 값이 있다 | +| `mailbox.name` | string | 사서함/받는 분 이름 | +| `mailbox.address` | string | `(우편번호) 주소, 상세주소` 형식 주소 | +| `mailbox.phoneNumber` | string | 연락처 | +| `trackingRequired` | boolean | 보낸 팬이 운송장 등록을 해야 하는 상태인지 여부 | +| `recipientAddressRequired` | boolean | 받는 크리에이터가 배송지를 입력해야 하는 상태인지 여부 | +| `recipientAddressDeadlineAt` | string 또는 null | 배송지 입력 마감일. UTC ISO 문자열 | +| `delivery.canceledAt` | string 또는 null | 취소일. UTC ISO 문자열 | +| `delivery.undeliverableAt` | string 또는 null | 전달 불가 처리일. UTC ISO 문자열 | +| `delivery.undeliverableReason` | string 또는 null | 전달 불가 사유 | +| `statusTimeline` | array | 정상 진행 5단계 타임라인 | +| `statusTimeline[].status` | string | 단계 상태 코드 | +| `statusTimeline[].statusName` | string | 단계 표시명 | +| `statusTimeline[].occurredAt` | string 또는 null | 해당 단계 도달 시각. UTC ISO 문자열 | + +운송장 등록: +- Method: `POST` +- URL: `/api/v2/gifts/{applicationNo}/tracking` +- 인증: 로그인 필요 +- Request: +```json +{ + "courierCompanyName": "한진택배", + "trackingNumber": "123456789012" +} +``` +- Response envelope 예시: +```json +{ + "success": true, + "data": { + "applicationNo": "A-1002609300001", + "status": "TRACKING_REGISTERED", + "statusName": "발송 확인", + "courierCompanyName": "한진택배", + "trackingNumber": "123456789012", + "trackingRegisteredAt": "2026-09-19T03:00:00Z", + "recipientAddressDeadlineAt": "2026-09-26T03:00:00Z" + }, + "message": "" +} +``` + +운송장 등록 response `data` 필드: + +| 필드 | 타입 | 설명 | +|---|---|---| +| `applicationNo` | string | 선물 신청번호 | +| `status` | string | 등록 후 상태. `TRACKING_REGISTERED` | +| `statusName` | string | 서버 상태 표시명 | +| `courierCompanyName` | string | 선택한 택배사 한글명 | +| `trackingNumber` | string | 운송장 번호 | +| `trackingRegisteredAt` | string | 운송장 등록일. UTC ISO 문자열 | +| `recipientAddressDeadlineAt` | string | 크리에이터 배송지 입력 마감일. UTC ISO 문자열 | + +선물 보내기 취소: +- Method: `POST` +- URL: `/api/v2/gifts/{applicationNo}/cancel` +- Request: 없음 +- 용도: `direction=SENT && status=RECEIVED`에서만 `신청 취소` action에 사용한다. +- Response envelope 예시: +```json +{ + "success": true, + "data": { + "applicationNo": "A-1002609300001", + "status": "CANCELED", + "statusName": "신청 취소", + "priceCan": 100, + "canceledAt": "2026-09-18T04:00:00Z" + }, + "message": "" +} +``` + +선물 보내기 취소 response `data` 필드: + +| 필드 | 타입 | 설명 | +|---|---|---| +| `applicationNo` | string | 선물 신청번호 | +| `status` | string | 취소 후 상태. `CANCELED` | +| `statusName` | string | 서버 상태 표시명 | +| `priceCan` | number | 환불되는 캔 수량 | +| `canceledAt` | string | 취소일. UTC ISO 문자열 | + +테스트/검증: +- 리스트 item 탭 시 `applicationNo`로 상세 페이지가 열리는지 확인한다. +- 상세 진입 시 `GET /api/v2/gifts/{applicationNo}`가 호출되는지 확인한다. +- 상세 조회 결과 `status=CANCELED`이면 일반 상세가 아니라 취소 완료 페이지가 표시되는지 확인한다. +- `statusTimeline` 5개 단계가 모두 표시되는지 확인한다. +- Figma처럼 4단계로 하드코딩하지 않았는지 확인한다. +- `trackingRequired=true`이면 `운송장 작성하기`가 표시되는지 확인한다. +- 신청 취소 버튼 탭 시 `v2 dialog` 확인 팝업이 뜨고 즉시 API를 호출하지 않는지 확인한다. +- 취소 확인 dialog에서 `나가기`를 누르면 dialog만 닫히는지 확인한다. +- 취소 확인 dialog에서 `취소하기`를 누르면 `POST /api/v2/gifts/{applicationNo}/cancel`을 호출하는지 확인한다. +- 취소 성공 후 취소 완료 페이지가 표시되고 취소 전 상세/등록 화면으로 돌아가지 않는지 확인한다. +- `mailbox`가 있을 때만 사서함 주소 섹션이 표시되는지 확인한다. +- `direction=SENT`의 `RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED` 상태에서 `mailbox`가 표시되는지 확인한다. +- `DELIVERED`, `UNDELIVERABLE`, `CANCELED`, `direction=RECEIVED`에서는 `mailbox`가 표시되지 않는지 확인한다. +- `UNDELIVERABLE`이면 전달 불가 hero와 사유 섹션이 표시되는지 확인한다. +- 운송장 등록 페이지에서 택배사/운송장 번호 누락 시 API를 호출하지 않는지 확인한다. +- 운송장 등록 시 선택한 택배사 한글명과 운송장 번호가 request body로 전송되는지 확인한다. +- 운송장 등록 성공 후 상세 페이지가 재조회되어 `TRACKING_REGISTERED` 상태로 보이는지 확인한다. +- 모든 날짜는 UTC ISO 문자열을 앱 표시 형식으로 변환해 표시하는지 확인한다. +``` diff --git a/docs/20260929_크리에이터_선물하기/gift-mailbox-admin-page-prompt.md b/docs/20260929_크리에이터_선물하기/gift-mailbox-admin-page-prompt.md new file mode 100644 index 00000000..cdd47627 --- /dev/null +++ b/docs/20260929_크리에이터_선물하기/gift-mailbox-admin-page-prompt.md @@ -0,0 +1,114 @@ +# 선물 받을 주소 관리자 페이지 구현 프롬프트 + +이 문서는 관리자 페이지에서 전역 선물 받을 주소를 등록/수정하는 화면을 구현할 때 사용하는 프롬프트다. + +## 전제 + +- 여기서 말하는 `선물 받을 주소`는 선물 상대방 주소가 아니다. +- 관리자가 등록하는 전역 단일 주소다. +- 팬이 보낸 선물 상세를 정상 진행 상태 중 전달 완료 전(`RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED`)에 조회할 때, 이 주소가 `mailbox`로 노출된다. +- 새 디자인 시스템을 만들지 말고 기존 관리자 페이지의 폼, 버튼, 토스트, 에러 표시 패턴을 재사용한다. + +## 구현 프롬프트 + +```text +관리자 선물 받을 주소 설정 페이지를 구현해줘. + +목표: +- 관리자 메뉴 `선물함 관리 > 받을 주소`에서 팬이 선물을 보낼 전역 단일 주소를 조회하고 저장한다. +- 이 주소는 상대방 주소가 아니라 관리자가 등록하는 운영 주소다. +- 팬의 선물 상세 API에서는 `direction=SENT`이고 `status`가 `RECEIVED`, `TRACKING_REGISTERED`, `ARRIVED_AT_MAILBOX`, `INSPECTION_COMPLETED`일 때 이 주소가 `mailbox`로 노출된다. +- 기존 관리자 페이지의 폼, 저장 버튼, 토스트, 에러 표시 패턴을 그대로 따른다. + +라우트: +- `/gift/mailbox` + +메뉴: +- parent: `선물함 관리` +- label: `받을 주소` +- path: `/gift/mailbox` + +필수 화면 구성: +- 상단 제목: `받을 주소` +- 설명 문구: `팬이 선물을 발송할 때 확인하는 받을 주소입니다.` +- 입력 폼: + - 받을 사람 이름 `name` + - 연락처 `phoneNumber` + - 우편번호 `zipCode` + - 주소 `address` + - 상세주소 `addressDetail` +- 저장 버튼: `저장` + +초기 조회 API: +- Method: `GET` +- URL: `/api/v2/admin/gift-mailbox` +- 모든 API 응답은 기존 공통 envelope를 사용한다. +- 화면에서는 `response.data`를 실제 payload로 사용한다. +- response `data`: +```ts +type AdminGiftMailboxResponse = { + name: string; + address: string; + phoneNumber: string; +} | null; +``` +- `data`가 null이면 아직 등록된 받을 주소가 없는 상태다. +- null이면 빈 폼을 표시한다. + +저장 API: +- Method: `PUT` +- URL: `/api/v2/admin/gift-mailbox` +- Request: +```ts +type AdminGiftMailboxRequest = { + name: string; + phoneNumber: string; + zipCode: string; + address: string; + addressDetail: string | null; +}; +``` +- Response `data`: +```ts +type AdminGiftMailboxResponse = { + name: string; + address: string; + phoneNumber: string; +}; +``` + +동작 규칙: +- 받을 주소는 전역 단일 설정이다. +- 등록과 수정은 같은 `PUT /api/v2/admin/gift-mailbox` API를 사용한다. +- 최초 저장이면 생성처럼 동작한다. +- 이미 저장된 값이 있으면 기존 주소를 수정한다. +- 별도 ID, 목록, 삭제 기능은 만들지 않는다. + +폼 validation: +- `name`, `phoneNumber`, `zipCode`, `address`는 필수다. +- 필수값은 공백만 입력할 수 없다. +- `addressDetail`은 선택이다. +- validation 실패 시 API를 호출하지 않고 기존 관리자 페이지의 필드 에러 표시 방식을 따른다. + +저장 성공 UX: +- 성공 토스트를 표시한다. +- 저장 API response 기준으로 화면 값을 갱신한다. +- response의 `address`는 서버가 조립한 `(우편번호) 주소, 상세주소` 형식이다. +- 입력 필드는 사용자가 입력한 `zipCode`, `address`, `addressDetail` 값을 유지해도 된다. + +빈 상태 UX: +- 조회 결과 `data=null`이면 빈 폼을 보여준다. +- 필요하면 안내 문구 `등록된 받을 주소가 없습니다. 주소를 입력하고 저장해주세요.`를 표시한다. + +에러 처리: +- API 실패 시 기존 관리자 페이지의 공통 에러 토스트/알림 패턴을 따른다. +- 인증/권한 처리는 기존 관리자 API client와 토큰 처리 방식을 따른다. + +테스트/검증: +- `/gift/mailbox` route가 선물함 관리 메뉴에서 접근되는지 확인한다. +- 초기 조회 시 `GET /api/v2/admin/gift-mailbox`를 호출하는지 확인한다. +- 조회 결과가 null이면 빈 폼을 표시하는지 확인한다. +- 저장 시 `PUT /api/v2/admin/gift-mailbox`와 request body가 정확한지 확인한다. +- 필수값 공백 validation 시 API를 호출하지 않는지 확인한다. +- 저장 성공 후 성공 토스트와 화면 갱신이 일어나는지 확인한다. +```