feat(admin): 관리자 대시보드 + 회원 상세 화면
CI / build (push) Failing after 11m12s

- 대시보드(/admin/dashboard, 기본 랜딩): 요약 카드(총회원·금일 신규/탈퇴·DAU/WAU/MAU),
  접속자 추이(일/주/월 토글), 티어별 회원수, 시군구 지역 분포 — 모두 체크인 기반
- 회원 상세(/admin/members/:id): 프로필 + 강아지/체크인/매칭/구독 이력, 목록에서 상세 버튼
- adminStats API 모듈, 좌측 메뉴에 대시보드 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
ByungCheol
2026-07-11 11:38:54 +09:00
parent c1fb8625e7
commit 5f849c552e
8 changed files with 557 additions and 5 deletions
+39
View File
@@ -5,10 +5,49 @@ export type MemberStatus = 'ACTIVE' | 'DORMANT' | 'BANNED'
export type MemberTier = 'FREE' | 'AD_FREE' | 'PREMIUM'
export type MemberRole = 'USER' | 'ADMIN'
export interface DogDetail {
id: number
name: string
breed: string | null
size: string | null
gender: string | null
neutered: boolean
birthDate: string | null
imageUrl: string | null
}
export interface CheckInDetail {
spotName: string
region: string | null
checkedInAt: string
}
export interface MatchDetail {
id: number
direction: string
counterpartDog: string
status: string
createdAt: string
}
export interface SubscriptionDetail {
tier: string
platform: string
startedAt: string
expiresAt: string
active: boolean
}
export interface MemberDetail {
member: Member
dogs: DogDetail[]
checkIns: CheckInDetail[]
matches: MatchDetail[]
subscriptions: SubscriptionDetail[]
}
/** 관리자 회원관리 API — /api/admin/members (ADMIN 전용) */
export const adminMembersApi = {
list: () => http.get<Member[]>('/api/admin/members'),
detail: (id: number) => http.get<MemberDetail>(`/api/admin/members/${id}`),
updateStatus: (id: number, status: MemberStatus) =>
http.put<Member>(`/api/admin/members/${id}/status`, { status }),
+32
View File
@@ -0,0 +1,32 @@
import { http } from './http'
export interface Count {
key: string
count: number
}
export interface StatsSummary {
members: { total: number; byTier: Count[]; byStatus: Count[] }
today: { signups: number; withdrawals: number }
visitors: { dau: number; wau: number; mau: number }
}
export interface VisitorPoint {
label: string
count: number
}
export interface RegionStat {
region: string
checkIns: number
users: number
}
export type VisitorUnit = 'day' | 'week' | 'month'
/** 관리자 대시보드 통계 API (체크인 기반) */
export const adminStatsApi = {
summary: () => http.get<StatsSummary>('/api/admin/stats/summary'),
visitors: (unit: VisitorUnit) => http.get<VisitorPoint[]>(`/api/admin/stats/visitors?unit=${unit}`),
regions: () => http.get<RegionStat[]>('/api/admin/stats/regions'),
}