diff --git a/app/admin/ai-chat/ai-chat-v2.tsx b/app/admin/ai-chat/ai-chat-v2.tsx index 256e0d8..eb4720d 100644 --- a/app/admin/ai-chat/ai-chat-v2.tsx +++ b/app/admin/ai-chat/ai-chat-v2.tsx @@ -69,8 +69,8 @@ function AIChatManagementPageContent() { const [selectedSessionId, setSelectedSessionId] = useState(''); const { data: sessionsData, isLoading, refetch } = useAiChatSessions(appliedParams); - const sessions = sessionsData?.sessions || []; - const totalCount = sessionsData?.total || 0; + const sessions = sessionsData?.items ?? []; + const totalCount = sessionsData?.pagination?.total ?? 0; const { data: messagesData, isLoading: messagesLoading } = useAiChatMessages(selectedSessionId); @@ -153,7 +153,7 @@ function AIChatManagementPageContent() { } }; - const selectedSession = messagesData?.session ?? null; + const selectedSession = sessions.find((s: any) => s.id === selectedSessionId) ?? null; const messages = messagesData?.messages ?? []; return ( diff --git a/app/services/admin/dashboard.ts b/app/services/admin/dashboard.ts index 184e467..71974f4 100644 --- a/app/services/admin/dashboard.ts +++ b/app/services/admin/dashboard.ts @@ -370,6 +370,49 @@ export const stats = { }, }; +export const dashboardV2 = { + getSummary: async (startDate?: string, endDate?: string) => { + // V2 ADAPTER + const params: any = {}; + if (startDate) params.startDate = startDate; + if (endDate) params.endDate = endDate; + const response = await axiosServer.get('/admin/v2/dashboard/summary', { params }); + return response.data.data; + }, + + getRevenue: async () => { + // V2 ADAPTER + const response = await axiosServer.get('/admin/v2/dashboard/revenue'); + return response.data.data; + }, + + getSignups: async (date?: string) => { + // V2 ADAPTER + const params: any = {}; + if (date) params.date = date; + const response = await axiosServer.get('/admin/v2/dashboard/signups', { params }); + return response.data.data; + }, + + getStatsOverview: async (startDate?: string, endDate?: string) => { + // V2 ADAPTER + const params: any = {}; + if (startDate) params.startDate = startDate; + if (endDate) params.endDate = endDate; + const response = await axiosServer.get('/admin/v2/stats/overview', { params }); + return response.data.data; + }, + + getRetention: async (startDate?: string, endDate?: string) => { + // V2 ADAPTER + const params: any = {}; + if (startDate) params.startDate = startDate; + if (endDate) params.endDate = endDate; + const response = await axiosServer.get('/admin/v2/stats/retention', { params }); + return response.data.data; + }, +}; + export const kpiReport = { getLatest: async (): Promise => { try { diff --git a/app/services/admin/messaging.ts b/app/services/admin/messaging.ts index e080279..93cc204 100644 --- a/app/services/admin/messaging.ts +++ b/app/services/admin/messaging.ts @@ -77,9 +77,18 @@ export const aiChat = { } }); - const response = await axiosServer.get(`/admin/ai-chat/sessions?${queryParams.toString()}`); - ; - return response.data; + const response = await axiosServer.get(`/admin/v2/ai-chat/sessions?${queryParams.toString()}`); + // V2 ADAPTER: v2 returns { data: AiChatSessionItem[] }, adapt to v1 shape { items, pagination } + const items = response.data.data ?? []; + return { + items, + pagination: { + page: params.page ?? 1, + limit: params.limit ?? 20, + total: items.length, + hasMore: false, + }, + }; } catch (error) { throw error; } @@ -88,9 +97,14 @@ export const aiChat = { // AI 채팅 메시지 상세 조회 getMessages: async (sessionId: string) => { try { - const response = await axiosServer.get(`/admin/ai-chat/messages?sessionId=${sessionId}`); - ; - return response.data; + const response = await axiosServer.get(`/admin/v2/ai-chat/sessions/${sessionId}/messages`); + // V2 ADAPTER: v2 returns { data: AiChatMessageItem[] }, adapt to v1 shape { messages, sessionId, userId } + const messages = response.data.data ?? []; + return { + messages, + sessionId, + userId: messages[0]?.userId ?? null, + }; } catch (error) { throw error; } diff --git a/app/services/admin/moderation.ts b/app/services/admin/moderation.ts index 056aa45..0069fd5 100644 --- a/app/services/admin/moderation.ts +++ b/app/services/admin/moderation.ts @@ -75,11 +75,37 @@ export interface PendingUsersFilter { region?: string; } +const adaptV2ReportItem = (item: any) => ({ + id: item.id, + reporter: { + id: item.reporterId, + name: item.reporterName || '알 수 없음', + email: '', + phoneNumber: '', + age: 0, + gender: 'MALE' as 'MALE' | 'FEMALE', + profileImageUrl: '', + }, + reported: { + id: item.targetId, + name: item.targetName || '알 수 없음', + email: '', + phoneNumber: '', + age: 0, + gender: 'MALE' as 'MALE' | 'FEMALE', + profileImageUrl: '', + }, + reason: item.reason || '', + description: null, + evidenceImages: [], + status: item.status || 'pending', + createdAt: item.createdAt, + updatedAt: null, +}); + export const reports = { getProfileReports: async (params: URLSearchParams) => { try { - ; - const page = params.get('page') || '1'; const limit = params.get('limit') || '10'; const status = params.get('status'); @@ -92,39 +118,9 @@ export const reports = { queryParams.append('status', status === 'pending' ? 'pending' : 'processed'); } - const endpoint = `/admin/community/reports?${queryParams.toString()}`; - ; - - const response = await axiosServer.get(endpoint); - ; + const response = await axiosServer.get(`/admin/v2/reports?${queryParams.toString()}`); - const transformedItems = (response.data.items || []).map((item: any) => ({ - id: item.id, - reporter: { - id: item.reporter?.id || item.reporterId, - name: item.reporter?.name || item.reporterName || '알 수 없음', - email: item.reporter?.email || '', - phoneNumber: item.reporter?.phoneNumber || '', - age: item.reporter?.age || item.reporterAge || 0, - gender: (item.reporter?.gender || item.reporterGender || 'MALE') as 'MALE' | 'FEMALE', - profileImageUrl: item.reporter?.profileImageUrl || '', - }, - reported: { - id: item.reported?.id || item.reportedId, - name: item.reported?.name || item.reportedName || '알 수 없음', - email: item.reported?.email || '', - phoneNumber: item.reported?.phoneNumber || '', - age: item.reported?.age || item.reportedAge || 0, - gender: (item.reported?.gender || item.reportedGender || 'MALE') as 'MALE' | 'FEMALE', - profileImageUrl: item.reported?.profileImageUrl || '', - }, - reason: item.reason || '', - description: item.description || null, - evidenceImages: item.evidenceImages || [], - status: item.status || 'pending', - createdAt: item.createdAt, - updatedAt: null, - })); + const transformedItems = (response.data.data || []).map(adaptV2ReportItem); return { items: transformedItems, @@ -137,10 +133,8 @@ export const reports = { getProfileReportDetail: async (reportId: string) => { try { - const response = await axiosServer.get( - `/admin/community/reports/profiles/${reportId}/detail`, - ); - return response.data; + const response = await axiosServer.get(`/admin/v2/reports/${reportId}`); + return adaptV2ReportItem(response.data.data); } catch (error: any) { throw error; } @@ -152,11 +146,11 @@ export const reports = { adminMemo?: string, ) => { try { - const response = await axiosServer.patch( - `/admin/community/reports/profiles/${reportId}/status`, - { status, adminMemo }, - ); - return response.data; + const response = await axiosServer.patch(`/admin/v2/reports/${reportId}/status`, { + status, + reason: adminMemo, + }); + return adaptV2ReportItem(response.data.data); } catch (error: any) { throw error; } @@ -205,12 +199,12 @@ export const userReview = { params.excludeUserIds = excludeUserIds.join(','); } - const response = await axiosServer.get('/admin/profile-images/pending', { + // V2 ADAPTER + const response = await axiosServer.get('/admin/v2/profile-review/pending', { params, }); - ; - return response.data; + return response.data.data ?? response.data; } catch (error: any) { throw error; } @@ -220,10 +214,10 @@ export const userReview = { try { ; - const response = await axiosServer.get(`/admin/user-review/${userId}`); + // V2 ADAPTER + const response = await axiosServer.get(`/admin/v2/profile-review/users/${userId}`); - ; - return response.data; + return response.data.data ?? response.data; } catch (error: any) { throw error; } @@ -233,9 +227,11 @@ export const userReview = { try { ; - const response = await axiosServer.post(`/admin/profile-images/users/${userId}/approve`); + // V2 ADAPTER + const response = await axiosServer.post( + `/admin/v2/profile-review/users/${userId}/approve-profile`, + ); - ; return response.data; } catch (error: any) { throw error; @@ -246,12 +242,12 @@ export const userReview = { try { ; - const response = await axiosServer.post(`/admin/user-review/${userId}/reject`, { - category, - reason, - }); + // V2 ADAPTER + const response = await axiosServer.post( + `/admin/v2/profile-review/users/${userId}/reject-profile`, + { category, reason }, + ); - ; return response.data; } catch (error: any) { throw error; @@ -293,13 +289,13 @@ export const userReview = { try { ; + // V2 ADAPTER const response = await axiosServer.patch( - `/admin/profiles/${userId}/rank`, + `/admin/v2/profile-review/users/${userId}/rank`, { rank }, { params: { emitEvent } }, ); - ; return response.data; } catch (error: any) { throw error; @@ -325,7 +321,8 @@ export const userReview = { if (filters.universityId) params.universityId = filters.universityId; if (filters.reviewedBy) params.reviewedBy = filters.reviewedBy; - const response = await axiosServer.get('/admin/profile-images/review-history', { + // V2 ADAPTER + const response = await axiosServer.get('/admin/v2/profile-review/history', { params, }); diff --git a/app/services/admin/users.ts b/app/services/admin/users.ts index 59d8bc0..5990a67 100644 --- a/app/services/admin/users.ts +++ b/app/services/admin/users.ts @@ -56,15 +56,12 @@ export const userAppearance = { queryParams.append('includeDeleted', params.includeDeleted.toString()); if (params.userStatus) queryParams.append('userStatus', params.userStatus); - const url = `/admin/users/appearance?${queryParams.toString()}`; - ; - ; + // V2 ADAPTER: list → GET /admin/v2/users + const url = `/admin/v2/users?${queryParams.toString()}`; try { const response = await axiosServer.get(url); - ; - ; - return response.data; + return response.data?.data ?? response.data; } catch (error: any) { throw error; } @@ -103,9 +100,9 @@ export const userAppearance = { throw new Error('등급이 없습니다.'); } + // V2 ADAPTER: individual rank → PATCH /admin/v2/users/:userId/appearance try { - const response = await axiosServer.patch(`/admin/users/appearance/${userId}`, { grade }); - ; + const response = await axiosServer.patch(`/admin/v2/users/${userId}/appearance`, { rank: grade }); return response.data; } catch (error: any) { throw error; @@ -138,10 +135,11 @@ export const userAppearance = { ) => { ; + // V2 ADAPTER: bulk rank → PATCH /admin/v2/users/appearance/bulk try { - const response = await axiosServer.patch('/admin/users/appearance/bulk', { + const response = await axiosServer.patch('/admin/v2/users/appearance/bulk', { userIds, - grade, + rank: grade, }); return response.data; } catch (error) { @@ -150,32 +148,27 @@ export const userAppearance = { }, getUserDetails: async (userId: string) => { + // V2 ADAPTER: detail → GET /admin/v2/users/:userId try { - ; - - const endpoint = `/admin/user-review/${userId}`; - ; - - const response = await axiosServer.get(endpoint); - ; - - const data = response.data; - - if ( - data.profileImageUrls && - Array.isArray(data.profileImageUrls) && - data.profileImageUrls.length > 0 - ) { - data.profileImages = data.profileImageUrls.map((url: string, index: number) => ({ - id: `${userId}-${index}`, - url: url, - order: index, - isMain: index === 0, - })); - data.profileImageUrl = data.profileImageUrls[0]; - } - - return data; + const response = await axiosServer.get(`/admin/v2/users/${userId}`); + const raw = response.data?.data ?? response.data; + + const images: { id: string; url: string; order: number; isMain: boolean }[] = + Array.isArray(raw.images) + ? raw.images.map((url: string, index: number) => ({ + id: `${userId}-${index}`, + url, + order: index, + isMain: index === 0, + })) + : []; + + return { + ...raw, + profileImages: images, + profileImageUrl: images[0]?.url ?? null, + profileImageUrls: images.map((img) => img.url), + }; } catch (error: any) { throw error; } @@ -276,6 +269,7 @@ export const userAppearance = { } }, + // V1 KEPT updateUserProfile: async (userId: string, profileData: any) => { try { ; diff --git a/app/services/chat.ts b/app/services/chat.ts index b98fd87..711517f 100644 --- a/app/services/chat.ts +++ b/app/services/chat.ts @@ -111,80 +111,101 @@ export interface ChatCsvExportParams { } class ChatService { - async getChatRooms(params: ChatRoomsParams): Promise { - try { - const response = await axiosServer.get('/admin/chat/rooms', { - params: { - startDate: params.startDate, - endDate: params.endDate, - preset: params.preset, - searchName: params.searchName, - page: params.page || 1, - limit: params.limit || 20 - } - }); - return response.data; - } catch (error: any) { - console.error('채팅방 목록 조회 실패:', error); - throw new Error(error.response?.data?.message || '채팅방 목록을 불러오는데 실패했습니다.'); - } - } - - async getChatMessages(params: ChatMessagesParams): Promise { - try { - const response = await axiosServer.get('/admin/chat/messages', { - params: { - chatRoomId: params.chatRoomId, - } - }); - return response.data; - } catch (error: any) { - console.error('채팅 메시지 조회 실패:', error); - throw new Error(error.response?.data?.message || '채팅 메시지를 불러오는데 실패했습니다.'); - } - } - - async getChatStats(params: ChatStatsParams = {}): Promise { - try { - const response = await axiosServer.get('/admin/chat/stats', { - params: { - startDate: params.startDate, - endDate: params.endDate, - preset: params.preset, - } - }); - return response.data; - } catch (error: any) { - console.error('채팅 통계 조회 실패:', error); - throw new Error(error.response?.data?.message || '채팅 통계를 불러오는데 실패했습니다.'); - } - } - - async exportChatsToCsv(params: ChatCsvExportParams = {}): Promise { - try { - const response = await axiosServer.get('/admin/chat/export/csv', { - params: { - startDate: params.startDate, - endDate: params.endDate, - preset: params.preset, - }, - responseType: 'blob' - }); - - const blob = new Blob([response.data], { type: 'text/csv;charset=utf-8' }); - const url = window.URL.createObjectURL(blob); - const link = document.createElement('a'); - link.href = url; - link.setAttribute('download', `chat_export_${new Date().toISOString().split('T')[0]}.csv`); - document.body.appendChild(link); - link.click(); - link.remove(); - window.URL.revokeObjectURL(url); - } catch (error: any) { - console.error('채팅 CSV 내보내기 실패:', error); - throw new Error(error.response?.data?.message || 'CSV 내보내기에 실패했습니다.'); - } - } + async getChatRooms(params: ChatRoomsParams): Promise { + // V2 ADAPTER + try { + const page = params.page || 1; + const limit = params.limit || 20; + const response = await axiosServer.get<{ data: ChatRoom[] }>('/admin/v2/chat/rooms', { + params: { + startDate: params.startDate, + endDate: params.endDate, + preset: params.preset, + searchName: params.searchName, + page, + limit, + }, + }); + const items = response.data.data; + return { + chatRooms: items, + total: items.length, + page, + limit, + totalPages: Math.ceil(items.length / limit) || 1, + appliedStartDate: params.startDate ?? null, + appliedEndDate: params.endDate ?? null, + }; + } catch (error: any) { + console.error('채팅방 목록 조회 실패:', error); + throw new Error(error.response?.data?.message || '채팅방 목록을 불러오는데 실패했습니다.'); + } + } + + async getChatMessages(params: ChatMessagesParams): Promise { + // V2 ADAPTER + try { + const response = await axiosServer.get<{ + data: { messages: ChatMessage[]; total: number; nextCursor: string | null; hasMore: boolean }; + }>(`/admin/v2/chat/rooms/${params.chatRoomId}/messages`); + const { messages, total } = response.data.data; + return { messages, total }; + } catch (error: any) { + console.error('채팅 메시지 조회 실패:', error); + throw new Error(error.response?.data?.message || '채팅 메시지를 불러오는데 실패했습니다.'); + } + } + + async getChatStats(params: ChatStatsParams = {}): Promise { + // V2 ADAPTER + try { + const response = await axiosServer.get<{ data: ChatStatsSummary }>('/admin/v2/chat/stats', { + params: { + startDate: params.startDate, + endDate: params.endDate, + preset: params.preset, + }, + }); + return { + summary: response.data.data, + hourlyDistribution: [], + dailyTrend: [], + messageLengthDistribution: [], + startDate: params.startDate ?? '', + endDate: params.endDate ?? '', + }; + } catch (error: any) { + console.error('채팅 통계 조회 실패:', error); + throw new Error(error.response?.data?.message || '채팅 통계를 불러오는데 실패했습니다.'); + } + } + + async exportChatsToCsv(params: ChatCsvExportParams = {}): Promise { + // V2 ADAPTER + try { + const response = await axiosServer.get('/admin/v2/chat/export', { + params: { + startDate: params.startDate, + endDate: params.endDate, + preset: params.preset, + }, + responseType: 'blob', + }); + + const blob = new Blob([response.data], { type: 'text/csv;charset=utf-8' }); + const url = window.URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = url; + link.setAttribute('download', `chat_export_${new Date().toISOString().split('T')[0]}.csv`); + document.body.appendChild(link); + link.click(); + link.remove(); + window.URL.revokeObjectURL(url); + } catch (error: any) { + console.error('채팅 CSV 내보내기 실패:', error); + throw new Error(error.response?.data?.message || 'CSV 내보내기에 실패했습니다.'); + } + } } const chatService = new ChatService();