FeaturesHQ (본사)HQ Mode 플레이리스트 조회 (apps/space /admin/playlists)

HQ Mode — 본사 플레이리스트 조회 (apps/space /admin/playlists, read-only)

SPEC #057 도입 (read 슬라이스) · #058 기본 PL 배지 추가. 실 HQ_MANAGER 직접 로그인 표면(apps/space · space.linkmusic.io) — 운영사 임퍼소네이션(apps/admin)이 아니다.

이 페이지는 apps/space 의 실 HQ_MANAGER 본사 모드다. 운영사(OPERATOR)가 본사를 관리하는 apps/admin(onboarding·list·detail·impersonation)과는 별개다. 음악 2계층은 “운영사 편집 · 본사 조회”가 모델 불변식 — 본 슬라이스가 그 조회 쪽을 채운다(편집은 운영사 전용).

Overview

HQ_MANAGER 가 apps/space 본사 모드에서 자기 본사(hqId)의 플레이리스트 목록·상세를 조회한다 (read-only). 각 PL 의 라이브러리 수 + 적용 매장 수(자기 본사 매장 중 그 PL 을 활성으로 쓰는 수)를 함께 본다. 전부 hqId 스코프(본인 본사만 — 토큰 주체에서 도출, 요청 파라미터 없음, BE verifyHqScope. 타 본사 PL 접근 불가). 생성/편집/삭제/적용은 운영사(apps/admin) 전용 — 본사 모드는 mutating 진입점이 없다.

시안 출처: workspace parent dir — hq-playlist 전용 시안 부재로 본사 모드 기존 화면 (design/screens/hq-stores.jsx 테이블·검색·페이지네이션) 스타일과 일관(임의 디자인 금지). 검색·페이지네이션은 공용 ListToolbar/ListPagination(본사 /stores #051 관용구)으로 정합.

목록 (/admin/playlists)

apps/space/src/app/admin/playlists/page.tsx(server 셸) + hq-playlist-list-client.tsx(client). 본사 /stores(#051)와 동일 이유로 서버사이드 페이지네이션 + client-query(useListHqPlaylists) — q(이름)·page·size 를 client state 로 보유한다. 정렬은 서버 고정(created_at DESC, id DESC).

  • 툴바: 공용 ListToolbar — 검색(플레이리스트명, ≤100) + 적용/초기화. read-only 라 필터·생성 버튼 없음.
  • : 플레이리스트명(상세로 링크)·상태 배지(#060 read-only)·라이브러리 수(libraryCount)·적용 매장 수(appliedStoreCount)·수정일(updatedAt, KST).
  • 상태 배지(#060): 파생 status(EMPTY/UNUSED/ACTIVE/FALLBACK)를 공용 StatusPill 로 read-only 노출(사용 중/기본/미사용/비어있음 — 라벨 항상 병기). 라벨·톤은 본사 단일 소스 playlist-status-meta.ts + HqPlaylistStatusBadge(운영사와 라벨/톤 일치). status=FALLBACK 이면 상태 배지가 “기본” 을 표현하므로 별도 isDefault “기본” 배지(#058)는 숨기고, EMPTY 등 그 외 status 인 기본 PL 은 “기본” 배지를 함께 노출.
  • 빈 상태: 검색 0건(ListNoResults CTA) vs 진짜 빈 목록 구분.
  • 페이지네이션: 공용 ListPagination — 총 N개 + 이전/다음(경계 disabled).
  • read-only: 생성/편집/삭제/적용·기본 지정 진입점 없음(기본 PL 지정은 운영사 전용 — #058). 행 클릭 = 상세 진입(유일한 액션). 상태·기본 배지는 조회 전용.

상세 (/admin/playlists/[id] — server component)

[id]/page.tsx(server) + hq-playlist-detail-client.tsx(client). getHqPlaylist (GET /api/v1/hq/playlists/{id})를 server component 에서 refresh-aware (loadHqPlaylistRefreshAware)로 fetch 해 client 에 prop 으로 내린다(토큰 서버 전용).

  • 404 PLAYLIST_NOT_FOUND(타 본사 PL·미존재 — 존재 은닉) → Next notFound().
  • 401 + refresh 실패/login fail-closed.
  • 5xx·네트워크 일시 장애(data:null) → 셸 유지 + 본문 재시도 배너.
  • 헤더 배지: 제목 옆에 read-only 상태 배지(#060) StatusPill(EMPTY/UNUSED/ACTIVE/FALLBACK). status=FALLBACK 이면 상태 배지가 “기본” 을 표현하므로 별도 isDefault “기본” 배지(#058)는 숨기고, 그 외 status 인 기본 PL 은 “기본” 배지를 병치. 지정/해제는 운영사 전용.
  • 요약 카드: 라이브러리 수 · 적용 매장 수 · 마지막 수정 시각.
  • 담긴 라이브러리 목록: libraries: HqPlaylistLibraryItem[](server 가 position 순 정렬) — 순서·이름·타입(AI·TRUST StatusPill)·곡 수(musicCount).
  • read-only: 추가/제거/순서/이름수정/삭제·기본 지정 진입점 없음.

사이드바

HQSidebar 의 “플레이리스트”(/admin/playlists) 항목이 enabled(활성 라우팅) — #057 에서 disabled “준비 중” 에서 전환.

인가

/api/v1/hq/**hasRole("HQ_MANAGER") 1차 경계 + service verifyHqScope claim↔DB 재검증 (#049/#051 재사용). 미인증 401 · 비활성/role·소속 불일치 403 PRINCIPAL_SCOPE_MISMATCH. 목록 fetch 는 generated apiFetch 가 BFF catch-all /api/backend/... 경유(토큰 서버 전용).

Followups

  • 임퍼소네이션 readonly 토글(#057 §F1) — 운영사가 본사로 임퍼소네이트 시 편집 가능 토글.
  • 매장별 활성 PL 현황(#057 §F2) — 산하 매장 목록에 각 매장 활성 PL 컬럼.
  • 상태 파생 4종 ✅ 완료(#060)PlaylistStatus{EMPTY,UNUSED,ACTIVE,FALLBACK} read-only 상태 배지(목록·상세 헤더). MISMATCH(플랜 불일치)만 잔여(#060 F1 — 플랜 게이팅 후속).