import { defineStore } from 'pinia' import { ref } from 'vue' import { get, post, put, del } from '../utils/request' import type { CardType, CreateCardTypeDto, UpdateCardTypeDto, StudioConfig, UpdateStudioConfigDto, OrderWithDetails, TimeSlot, CreateManualSlotDto, PaginatedData, ScheduleSlotPreview, PublishDaySlotsDto, FlashSaleAdminItem, CreateFlashSaleDto, UpdateFlashSaleDto, CreateStudioUploadCredentialDto, StudioUploadCredential, } from '@mp-pilates/shared' interface LegacyPaginatedData { readonly data: readonly T[] readonly total: number readonly page: number readonly limit: number } function normalizePaginatedData( result: PaginatedData | LegacyPaginatedData, ): PaginatedData { if ('items' in result) { return { items: [...result.items], total: result.total, page: result.page, limit: result.limit, } } return { items: [...result.data], total: result.total, page: result.page, limit: result.limit, } } export interface AdminStats { todayBookings: number totalOrders: number totalBookings: number } export interface MemberSummary { userId: string openid: string nickname: string phone: string | null avatarUrl: string | null totalBookings: number completedBookings: number cancelledBookings: number } export interface UserMembership { userId: string membership: { id: string cardTypeId: string remainingTimes: number | null startDate: string expireDate: string status: string cardType: { id: string name: string type: string totalTimes: number | null durationDays: number } } | null } export const useAdminStore = defineStore('admin', () => { // ── Card types ─────────────────────────────────────────────────── const cardTypes = ref([]) async function fetchCardTypes(): Promise { const data = await get('/admin/card-types') cardTypes.value = [...data].sort((a, b) => a.sortOrder - b.sortOrder) return cardTypes.value } async function createCardType(dto: CreateCardTypeDto): Promise { const data = await post('/admin/card-types', dto as unknown as Record) await fetchCardTypes() return data } async function updateCardType(id: string, dto: UpdateCardTypeDto): Promise { const data = await put(`/admin/card-types/${id}`, dto as unknown as Record) await fetchCardTypes() return data } async function deleteCardType(id: string): Promise<{ deleted: boolean; deactivated: boolean }> { const result = await del<{ deleted: boolean; deactivated: boolean }>(`/admin/card-types/${id}`) await fetchCardTypes() return result } // ── Studio config ──────────────────────────────────────────────── const studioConfig = ref(null) async function fetchStudioConfig(): Promise { const data = await get('/studio/info') studioConfig.value = data return data } async function saveStudioConfig(dto: UpdateStudioConfigDto): Promise { const data = await put('/admin/studio/info', dto as unknown as Record) studioConfig.value = data return data } async function createStudioUploadCredential( dto: CreateStudioUploadCredentialDto, ): Promise { return post( '/admin/studio/upload-credentials', dto as unknown as Record, ) } // ── Orders ─────────────────────────────────────────────────────── async function fetchAdminOrders(params: { page?: number limit?: number status?: string }): Promise> { const result = await get | LegacyPaginatedData>( '/admin/orders', params, ) return normalizePaginatedData(result) } // ── Bookings ───────────────────────────────────────────────────── async function fetchAdminBookings(params?: { page?: number limit?: number userId?: string }): Promise> { return get>('/admin/bookings', params) } // ── Members ────────────────────────────────────────────────────── async function fetchMembers(params?: { page?: number limit?: number search?: string cardType?: string }): Promise> { const cleanParams: Record = {} if (params?.page != null) cleanParams.page = params.page if (params?.limit != null) cleanParams.limit = params.limit if (params?.search) cleanParams.search = params.search if (params?.cardType) cleanParams.cardType = params.cardType return get>('/admin/members', cleanParams) } async function getUserMembership(userId: string): Promise { return get(`/admin/members/${userId}/membership`) } async function updateUserMembership( userId: string, dto: { cardTypeId: string remainingTimes?: number | null startDate: string expireDate: string }, ): Promise { return put(`/admin/members/${userId}/membership`, dto) } async function deleteUserMembership(userId: string): Promise { return del(`/admin/members/${userId}/membership`) } // ── Time slots ─────────────────────────────────────────────────── async function fetchSlotsByDate(date: string): Promise { return get('/admin/time-slots', { date }) } async function createManualSlot(dto: CreateManualSlotDto): Promise { return post('/admin/time-slot/manual', dto as unknown as Record) } async function closeSlot(id: string): Promise { return put(`/admin/time-slot/${id}/close`, {}) } async function generateSlots(startDate: string, endDate: string): Promise<{ count: number }> { return post<{ count: number }>('/admin/generate-slots', { startDate, endDate }) } // ── Schedule management ───────────────────────────────────────── const schedulePreview = ref([]) const scheduleLoading = ref(false) async function fetchSchedulePreview(date: string): Promise { scheduleLoading.value = true try { const data = await get('/admin/schedule/preview', { date }) schedulePreview.value = data return data } finally { scheduleLoading.value = false } } async function publishDaySlots(dto: PublishDaySlotsDto): Promise { await post('/admin/schedule/publish', dto as unknown as Record) await fetchSchedulePreview(dto.date) } // ── Dashboard stats ────────────────────────────────────────────── async function fetchDashboardStats(): Promise { return get('/admin/stats') } // ── Flash sales ───────────────────────────────────────────────── async function fetchFlashSales(params?: { page?: number limit?: number }): Promise> { return get>('/admin/flash-sales', params as Record) } async function createFlashSale(dto: CreateFlashSaleDto): Promise { return post('/admin/flash-sales', dto as unknown as Record) } async function updateFlashSale(id: string, dto: UpdateFlashSaleDto): Promise { return put(`/admin/flash-sales/${id}`, dto as unknown as Record) } async function deleteFlashSale(id: string): Promise<{ deleted: boolean }> { return del<{ deleted: boolean }>(`/admin/flash-sales/${id}`) } return { // State cardTypes, studioConfig, schedulePreview, scheduleLoading, // Card types fetchCardTypes, createCardType, updateCardType, deleteCardType, // Studio fetchStudioConfig, saveStudioConfig, createStudioUploadCredential, // Orders fetchAdminOrders, // Bookings fetchAdminBookings, // Members fetchMembers, getUserMembership, updateUserMembership, deleteUserMembership, // Time slots fetchSlotsByDate, createManualSlot, closeSlot, generateSlots, // Schedule fetchSchedulePreview, publishDaySlots, // Stats fetchDashboardStats, // Flash sales fetchFlashSales, createFlashSale, updateFlashSale, deleteFlashSale, } })