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건(
ListNoResultsCTA) 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·미존재 — 존재 은닉) → NextnotFound(). - 401 + refresh 실패 →
/loginfail-closed. - 5xx·네트워크 일시 장애(data:null) → 셸 유지 + 본문 재시도 배너.
- 헤더 배지: 제목 옆에 read-only 상태 배지(#060)
StatusPill(EMPTY/UNUSED/ACTIVE/FALLBACK).status=FALLBACK이면 상태 배지가 “기본” 을 표현하므로 별도isDefault“기본” 배지(#058)는 숨기고, 그 외 status 인 기본 PL 은 “기본” 배지를 병치. 지정/해제는 운영사 전용. - 요약 카드: 라이브러리 수 · 적용 매장 수 · 마지막 수정 시각.
- 담긴 라이브러리 목록:
libraries: HqPlaylistLibraryItem[](server 가 position 순 정렬) — 순서·이름·타입(AI·TRUSTStatusPill)·곡 수(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 — 플랜 게이팅 후속).