# U1 — 순수 UI 정합 (백엔드 변경 없음)

작성 2026-08-18 · 대상 repo `cryptoments-admin` (admin-ui, partner-ui, p2p-ui)
전제: **백엔드 코드/DDL 변경 금지.** 이미 존재하는 API·필드만 쓴다.

---

## 왜 지금 이것부터인가

운영자가 세 목록에서 **성공한 건을 필터로 볼 수 없다.** 실측:

```
매칭 목록    SETTLED 1,305건 = 전체의 90%   → 필터 옵션이 없다
             "COMPLETED" 를 누르면 0건       → 그런 상태값이 존재하지 않는다
출금 목록    COMPLETED 36건 = 전체의 100%   → 필터 옵션이 없다
입금 목록    MATCHING 누락                  → 워커 점유 주문을 못 뽑는다
```

배지도 전부 영문 원문이 노출된다. admin-ui 에는 한글 라벨 맵 자체가 없다.

---

## 작업 1 — 매칭 목록 상태 필터를 실제 enum 에 맞춘다

**파일** `admin-ui/src/views/p2p/P2pMatchListView.vue:117-123`

현재 7개 옵션 중 **3개는 존재하지 않는 값**이고, **실제 값 4개가 빠져 있다.**

정본 enum — `common/src/main/java/com/cryptoments/common/enums/P2pMatchStatus.java`

```
CREATED  BANK_PENDING  BANK_CONFIRMED  SETTLING  SETTLED  DISPUTED  FAILED  CANCELLED
```

교체 후 옵션 (순서 = 생명주기 순, 라벨은 한글):

| value | 라벨 |
|---|---|
| `CREATED` | 매칭됨 |
| `BANK_PENDING` | 이체 대기 |
| `BANK_CONFIRMED` | 이체 확인 |
| `SETTLING` | 정산 중 |
| `SETTLED` | 정산 완료 |
| `DISPUTED` | 분쟁 |
| `FAILED` | 실패 |
| `CANCELLED` | 취소 |

> 삭제 대상: `MATCHING`, `MATCHED`, `COMPLETED` — enum 에 없다.
> `CANCELLED` 는 실재하므로 **유지**한다.

## 작업 2 — 입금 주문 목록에 `MATCHING` 추가

**파일** `admin-ui/src/views/p2p/P2pOrderListView.vue:78-84`

정본 enum `P2pDepositStatus`: `PENDING MATCHING MATCHED COMPLETED PARTIALLY_SETTLED CANCELLED EXPIRED`

`PENDING` 과 `MATCHED` 사이에 `MATCHING`(라벨 **매칭 중**)을 넣는다. 나머지 옵션은 유지하되 라벨을 한글로 통일한다(작업 4의 맵을 쓰면 자동).

## 작업 3 — 출금 주문 목록에 `SETTLING` · `COMPLETED` 추가

**파일** `admin-ui/src/views/p2p/P2pWithdrawOrderListView.vue:99-103`

정본 enum `P2pWithdrawStatus`: `PENDING PARTIALLY_MATCHED FULLY_MATCHED SETTLING COMPLETED CANCELLED EXPIRED`

`FULLY_MATCHED` 뒤에 `SETTLING`(정산 중), `COMPLETED`(완료)를 넣는다.

---

## 작업 4 — 한글 라벨 맵 신설 + StatusBadge 폴백

admin-ui 는 `STATUS_COLORS` 만 있고 라벨 맵이 없다. 그래서 `StatusBadge` 가 `label || status` 로 **영문 원문을 그대로 출력**한다 (`components/common/StatusBadge.vue:14`). P2P 화면 20곳이 `:label` 없이 쓴다.

**4-1. `admin-ui/src/utils/constants.ts`** — `STATUS_COLORS` 아래에 `STATUS_LABELS_KO` 를 신설한다.

> **참고 구현**: `partner-ui/src/utils/constants.ts` 에 같은 이름의 맵이 이미 있다. **그 파일을 먼저 읽고 키/표기를 맞춰라.** 두 콘솔이 같은 상태를 다르게 부르면 안 된다.

포함할 키 (P2P 3개 enum + 이미 쓰이는 공통 상태):

```
P2pMatchStatus      CREATED BANK_PENDING BANK_CONFIRMED SETTLING SETTLED DISPUTED FAILED CANCELLED
P2pDepositStatus    PENDING MATCHING MATCHED COMPLETED PARTIALLY_SETTLED CANCELLED EXPIRED
P2pWithdrawStatus   PENDING PARTIALLY_MATCHED FULLY_MATCHED SETTLING COMPLETED CANCELLED EXPIRED
```

⚠️ **키 충돌 주의** — `PENDING`/`CANCELLED`/`COMPLETED`/`SETTLING` 은 여러 enum 에 공통이다. 하나의 평면 맵이므로 **세 enum 에서 뜻이 어긋나지 않는 표현**을 골라라. 예: `SETTLING` 은 매칭·출금 모두 "정산 중" 이라 안전하다. 뜻이 갈리는 키가 있으면 무리하게 통일하지 말고 **그대로 두고 보고**하라.

**4-2. `admin-ui/src/components/common/StatusBadge.vue`**

```
현재   {{ label || status }}
이후   {{ label || STATUS_LABELS_KO[status] || status }}
```

`label` prop 을 명시적으로 넘기는 기존 호출은 그대로 동작해야 한다(우선순위 유지). 맵에 없는 상태는 영문으로 폴백한다.

**4-3. 회귀 확인** — `StatusBadge` 는 P2P 밖에서도 쓰인다. 맵에 넣은 키가 **다른 도메인 화면의 배지 문구를 바꾼다**. 출금(withdrawals)·입금(deposits)·정산 화면을 grep 해서 의도치 않게 바뀌는 곳이 없는지 확인하고, 바뀌는 게 있으면 목록으로 보고하라.

---

## 작업 5 — admin 출금 상세에 USDT 전환 요청액 표시

**파일** `admin-ui/src/views/p2p/P2pWithdrawOrderDetailView.vue` + `admin-ui/src/api/types/p2p.ts`

백엔드는 이미 내려주는데 프론트 타입에 없어 유실된다.

```
P2pWithdrawOrderInfoResponse.java:120  usdtConvertRequestedAt    ← 타입에 있음 (usdtConvertStatus 와 함께 이미 표시 중, :516 근처)
P2pWithdrawOrderInfoResponse.java:126  usdtConvertRequestedKrw   ← 타입 누락
P2pWithdrawOrderInfoResponse.java:129  usdtConvertRequestedUsdt  ← 타입 누락
P2pWithdrawOrderListResponse.java:96   usdtConvertRequestedKrw   ← 목록 타입도 확인
```

`P2pWithdrawOrderInfo` 인터페이스에 두 필드를 추가하고, **이미 `usdtConvertStatus` 를 그리는 카드 안에** 요청 금액(KRW)과 USDT 환산을 함께 보여준다. 새 카드를 만들지 말고 기존 블록에 행을 추가하라.

> 금액 표기는 같은 파일의 기존 포맷 헬퍼를 그대로 쓴다. 새 포맷 함수를 만들지 마라.

---

## 작업 6 — admin 분쟁 "증빙 재요청" 버튼

백엔드 엔드포인트가 있는데 프론트가 호출하지 않는다.

```
POST /admin/p2p/matches/{id}/dispute/request-more
  body: P2pDisputeRequestMoreRequest { target, memo }
  → P2pMatchDetailResponse
  근거: admin-api/.../P2pMatchManagementController.java:109-115
```

**6-1.** `admin-ui/src/api/services/p2p.service.ts` 에 `requestMoreEvidence(id, { target, memo })` 추가. 기존 `resolveDispute`(`:66` 부근) 바로 아래에 같은 스타일로.

**6-2.** `P2pMatchDetailView.vue` — 상태가 `DISPUTED` 일 때만 노출되는 버튼 + 다이얼로그. 기존 판정 다이얼로그(`:405-413`)의 마크업 패턴을 그대로 따른다.

**6-3. `target` 의 허용값을 백엔드에서 직접 확인하라.** `P2pDisputeRequestMoreRequest` 와 `P2pMatchManagementService.requestMoreEvidence` 를 읽고, 실제로 받는 값(예: 출금자/입금자 구분)을 셀렉트 옵션으로 만든다. **추측해서 문자열을 넣지 마라.**

> 판정 버튼이 있는 나머지 3곳(`P2pMatchListView`, `P2pDepositOrderDetailView`, `P2pWithdrawOrderDetailView`)에는 **넣지 않는다.** 증빙 재요청은 상세에서 맥락을 보고 하는 행위다.

---

## 작업 7 — p2p-ui 분쟁 증빙 재제출

회원이 분쟁 중 증빙을 낼 방법이 없다. 백엔드는 이미 받는다.

```
POST /p2p/page/orders/{code}/matches/{matchId}/dispute/evidence
  body: P2pPageDisputeEvidenceRequest { evidenceUrl, memo }
  → P2pPageMatchResponse
  근거: open-api/.../P2pWithdrawPageController.java:604-613 (당사자 = WITHDRAWER 고정)
```

**7-1.** `p2p-ui/src/api/p2p.service.ts` 에 `submitDisputeEvidence(code, matchId, { evidenceUrl, memo })` 추가.

**7-2.** `p2p-ui/src/views/DisputeView.vue` — 현재 59줄짜리 상태 카드 목록이다. 각 카드에 증빙 제출 영역을 붙인다.

- **분쟁 진행 중인 매칭에만** 노출 (종결된 건에 입력창을 띄우지 마라)
- `evidenceUrl` + `memo` 입력, 제출 후 목록 갱신
- 실패 시 에러 표시. 성공 오판 금지 — 응답을 받고 나서 성공 처리한다
- 파일 업로드가 아니라 **URL 입력**이다. 업로드 UI 를 만들지 마라

**7-3. 기존 UX 패턴을 따를 것.** 같은 repo 의 `components/sheets/ManualConfirmSheet.vue` 가 시트+동의+제출의 정본 패턴이다. **먼저 읽고** 버튼 상태·busy 처리·에러 표기를 맞춰라.

---

## 작업 8 — partner-ui 주석 오류 정정 (1줄)

**파일** `partner-ui/src/api/types/p2p.ts:311`

```
현재   잔여 KRW (krwAmount - matchedAmount)
사실   백엔드가 원장 SUM(amount_krw) 을 준다 — PartnerP2pPoolMapper.java:103
```

주석만 정정한다. **값·로직은 건드리지 마라.** 다음 사람이 이 주석을 믿고 다른 화면에 구 공식을 복붙하는 것을 막는 것이 목적이다.

---

## 코딩 규칙

- Vue 3 `<script setup lang="ts">` · 기존 파일의 스타일을 따른다
- **새 컴포넌트 라이브러리·유틸을 도입하지 마라.** 있는 것만 쓴다
- 타입은 `any` 금지. 기존 인터페이스를 확장한다
- 하드코딩된 한글 문구는 화면에 직접 쓰지 말고, 상태 라벨은 반드시 작업 4 의 맵을 경유한다
- **백엔드 파일을 수정하지 마라.** 읽기만 한다

## 완료 기준

```
1  admin-ui  pnpm build (또는 npm run build) 통과 — 타입 에러 0
2  p2p-ui    빌드 통과
3  partner-ui 빌드 통과
4  매칭 목록 상태 필터 8개가 P2pMatchStatus 와 1:1 일치 (죽은 옵션 0)
5  입금 목록에 MATCHING, 출금 목록에 SETTLING·COMPLETED 존재
6  P2P 화면의 StatusBadge 가 한글을 출력 (label 미지정 20곳)
7  출금 상세에서 USDT 전환 요청 KRW·USDT 가 보인다
8  DISPUTED 매칭 상세에 증빙 재요청 버튼이 있고 실제 API 를 호출한다
9  회원 DisputeView 에서 진행 중 분쟁에 증빙 URL 을 제출할 수 있다
```

## 보고 형식

작업별로 **수정 파일:라인 + 무엇을 바꿨는지 한 줄**. 그리고:

- 작업 4-3 회귀 확인 결과 (다른 도메인 배지가 바뀌는 곳 목록)
- 작업 4 키 충돌 중 뜻이 갈려 통일하지 못한 것
- 작업 6-3 에서 확인한 `target` 실제 허용값
- **지침이 현재 코드와 어긋난 지점** — 발견하면 고치지 말고 먼저 보고하라

## 착수 전 필수 확인

지침서가 틀렸을 수 있다. 아래를 **먼저 읽고**, 지침과 다르면 작업을 멈추고 보고하라.

```
common/src/main/java/com/cryptoments/common/enums/P2pMatchStatus.java
common/src/main/java/com/cryptoments/common/enums/P2pDepositStatus.java
common/src/main/java/com/cryptoments/common/enums/P2pWithdrawStatus.java
partner-ui/src/utils/constants.ts          (STATUS_LABELS_KO 정본 패턴)
admin-ui/src/components/common/StatusBadge.vue
```
