import axios from 'axios'; import { Car, CarListResponse, CarMaker, CarModel, User, HeroBanner, HeroBannerSettings, CarView } from '@/types'; // When NEXT_PUBLIC_API_URL is empty, use relative path (for HTTPS proxy setup) const API_URL = process.env.NEXT_PUBLIC_API_URL; const BASE_URL = API_URL ? `${API_URL}/api` : '/api'; const api = axios.create({ baseURL: BASE_URL, headers: { 'Content-Type': 'application/json', }, }); // Add auth token to requests api.interceptors.request.use((config) => { if (typeof window !== 'undefined') { const token = localStorage.getItem('token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } } return config; }); // Cars API export const carsApi = { getList: async (params: { page?: number; page_size?: number; maker_id?: number; model_id?: number; year_min?: number; year_max?: number; price_min?: number; price_max?: number; mileage_max?: number; fuel?: string; admin?: boolean; is_displayed?: boolean; }): Promise => { const { data } = await api.get('/cars', { params }); return data; }, getById: async (id: number, admin?: boolean): Promise => { const { data } = await api.get(`/cars/${id}`, { params: { admin } }); return data; }, update: async (id: number, updateData: { margin_krw?: number; is_displayed?: boolean; car_name?: string; status?: string; }): Promise => { const { data } = await api.put(`/cars/${id}`, updateData); return data; }, delete: async (id: number): Promise => { await api.delete(`/cars/${id}`); }, getMakers: async (): Promise => { const { data } = await api.get('/cars/makers/'); return data; }, getModels: async (makerId?: number): Promise => { const params = makerId ? { maker_id: makerId } : {}; const { data } = await api.get('/cars/models/', { params }); return data; }, // Soldout APIs markSoldout: async (carId: number): Promise<{ car_id: number; soldout: boolean; message: string }> => { const { data } = await api.post(`/cars/${carId}/soldout`); return data; }, markAvailable: async (carId: number): Promise<{ car_id: number; soldout: boolean; message: string }> => { const { data } = await api.delete(`/cars/${carId}/soldout`); return data; }, getSoldoutStats: async (): Promise<{ total_active: number; soldout: number; available: number; soldout_percentage: number }> => { const { data } = await api.get('/cars/admin/soldout-stats'); return data; }, }; // Auth API export const authApi = { login: async (email: string, password: string): Promise<{ access_token: string }> => { const params = new URLSearchParams(); params.append('username', email); params.append('password', password); const { data } = await api.post('/auth/login', params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, }); return data; }, register: async (userData: { email: string; password: string; name?: string; phone?: string; country?: string; }): Promise => { const { data } = await api.post('/auth/register', userData); return data; }, getMe: async (): Promise => { const { data } = await api.get('/auth/me'); return data; }, // 사용자 탈퇴 요청 requestWithdrawal: async (password: string, reason?: string): Promise<{ message: string; withdrawal_requested_at: string; }> => { const { data } = await api.post('/auth/withdraw', { password, reason }); return data; }, // 탈퇴 요청 취소 cancelWithdrawal: async (): Promise<{ message: string }> => { const { data } = await api.post('/auth/withdraw/cancel'); return data; }, }; // Inquiries API export const inquiriesApi = { create: async (carId: number, message: string) => { const { data } = await api.post('/inquiries', { car_id: carId, message }); return data; }, getList: async () => { const { data } = await api.get('/inquiries'); return data; }, }; // Hero Banners API export const heroBannersApi = { // Public APIs getList: async (lang: string = 'en'): Promise => { const { data } = await api.get('/hero-banners/', { params: { lang } }); return data; }, getSettings: async (): Promise => { const { data } = await api.get('/hero-banners/settings'); return data; }, // 차량이 Banner에 연결되어 있는지 확인 (샘플 차량 여부) checkBannerCar: async (carId: number): Promise<{ car_id: number; is_banner_car: boolean; banner_id: number | null }> => { const { data } = await api.get(`/hero-banners/check-car/${carId}`); return data; }, // Admin APIs adminGetList: async (): Promise => { const { data } = await api.get('/hero-banners/admin/list'); return data; }, adminGetById: async (id: number): Promise => { const { data } = await api.get(`/hero-banners/admin/${id}`); return data; }, adminCreate: async (bannerData: Partial): Promise => { const { data } = await api.post('/hero-banners/admin', bannerData); return data; }, adminUpdate: async (id: number, bannerData: Partial): Promise => { const { data } = await api.put(`/hero-banners/admin/${id}`, bannerData); return data; }, adminDelete: async (id: number): Promise => { await api.delete(`/hero-banners/admin/${id}`); }, adminUploadImage: async (file: File): Promise<{ image_url: string }> => { const formData = new FormData(); formData.append('file', file); const { data } = await api.post('/hero-banners/admin/upload-image', formData, { headers: { 'Content-Type': 'multipart/form-data' }, }); return data; }, adminUpdateSettings: async (settings: Partial): Promise => { const { data } = await api.put('/hero-banners/admin/settings', settings); return data; }, // Banner Toggle & Ordering adminToggleBanner: async (carId: number): Promise<{ car_id: number; is_banner: boolean; banner_id?: number; message: string }> => { const { data } = await api.post(`/hero-banners/admin/toggle/${carId}`); return data; }, adminReorderBanners: async (carIds: number[]): Promise<{ message: string; count: number }> => { const { data } = await api.put('/hero-banners/admin/reorder', { car_ids: carIds }); return data; }, adminGetBannerCars: async (): Promise<{ car_ids: number[]; count: number }> => { const { data } = await api.get('/hero-banners/admin/banner-cars'); return data; }, }; // Translations API export interface Translation { id: number; source_text: string; category: string; text_en?: string; text_mn?: string; text_ru?: string; created_at: string; updated_at: string; } export interface TranslationListResponse { total: number; page: number; page_size: number; translations: Translation[]; } export const translationsApi = { getCategories: async (): Promise => { const { data } = await api.get('/translations/categories'); return data; }, getList: async (params: { page?: number; page_size?: number; category?: string; search?: string; }): Promise => { const { data } = await api.get('/translations', { params }); return data; }, getById: async (id: number): Promise => { const { data } = await api.get(`/translations/${id}`); return data; }, create: async (translationData: { source_text: string; category: string; text_en?: string; text_mn?: string; text_ru?: string; }): Promise => { const { data } = await api.post('/translations', translationData); return data; }, update: async (id: number, translationData: { source_text?: string; category?: string; text_en?: string; text_mn?: string; text_ru?: string; }): Promise => { const { data } = await api.put(`/translations/${id}`, translationData); return data; }, delete: async (id: number): Promise => { await api.delete(`/translations/${id}`); }, autoExtract: async (): Promise<{ message: string }> => { const { data } = await api.post('/translations/auto-extract'); return data; }, seedAllDefaults: async (): Promise<{ message: string; added: number; skipped: number; categories: string[] }> => { const { data } = await api.post('/translations/seed-all-defaults'); return data; }, bulkLookup: async (texts: string[], lang: string, category?: string): Promise<{ translations: Record }> => { const { data } = await api.post('/translations/bulk-lookup', { texts, lang, category, }); return data; }, // Auto-translation endpoints autoTranslate: async (translationId: number, targetLangs?: string[]): Promise<{ id: number; source_text: string; translations: Record; message: string; }> => { const { data } = await api.post(`/translations/auto-translate/${translationId}`, { target_langs: targetLangs || ['en', 'mn', 'ru'] }); return data; }, autoTranslateBatch: async (targetLangs?: string[], category?: string, overwriteExisting?: boolean): Promise<{ total_processed: number; successful: number; failed: number; results: Array<{ id: number; source_text: string; success: boolean; error?: string }>; }> => { const { data } = await api.post('/translations/auto-translate-batch', { target_langs: targetLangs || ['en', 'mn', 'ru'], category, overwrite_existing: overwriteExisting || false }); return data; }, translateOnDemand: async (text: string, sourceLang: string, targetLang: string): Promise<{ source_text: string; translated_text: string; source_lang: string; target_lang: string; }> => { const { data } = await api.post('/translations/translate-on-demand', { text, source_lang: sourceLang, target_lang: targetLang }); return data; }, getStats: async (): Promise<{ total_entries: number; by_category: Record; translation_coverage: { english: { translated: number; total: number; percentage: number }; mongolian: { translated: number; total: number; percentage: number }; russian: { translated: number; total: number; percentage: number }; }; }> => { const { data } = await api.get('/translations/stats'); return data; }, }; // Carmodoo API export interface CarmodooMaker { code: string; name: string; } export interface CarmodooModel { code: string; name: string; type?: string; } export interface CarmodooSearchResult { id: string; car_name: string; maker_name?: string; model_name?: string; year?: number; mileage?: number; original_price?: number; korea_margin?: number; mongolia_margin?: number; final_price?: number; fuel?: string; transmission?: string; color?: string; displacement?: number; main_image?: string; image_count: number; check_num?: string; // 성능점검번호 car_key?: string; // 암호화된 차량 키 (딜러 설명 조회용) } export interface VehicleRequestSearchResponse { total: number; cars: CarmodooSearchResult[]; } export const carmodooApi = { getMakers: async (): Promise => { const { data } = await api.get('/carmodoo/makers'); return data; }, getModels: async (makerCode: string): Promise => { const { data } = await api.get(`/carmodoo/models/${makerCode}`); return data; }, requestSearch: async (params: { maker_code?: string; model_code?: string; grade?: string; year_min?: number; year_max?: number; mileage_min?: number; mileage_max?: number; price_min?: number; price_max?: number; fuel?: string; transmission?: string; displacement_min?: number; displacement_max?: number; page?: number; page_size?: number; }): Promise => { const { data } = await api.get('/carmodoo/request-search', { params }); return data; }, getGrades: async (makerCode: string, modelCode: string): Promise<{ code: string; name: string }[]> => { const { data } = await api.get(`/carmodoo/grades/${makerCode}/${modelCode}`); return data; }, // 차량을 로컬 DB에 저장 (이미지 다운로드 포함) importCars: async (cars: { car_no: string; car_name: string; maker_name?: string; model_name?: string; year?: number; mileage?: number; price?: number; fuel?: string; transmission?: string; color?: string; displacement?: number; main_image?: string; check_num?: string; // 성능점검번호 car_key?: string; // 암호화된 차량 키 (딜러 설명 조회용) }[]): Promise<{ imported: { car_no: string; car_id: number; car_name: string; images_downloaded: number; pdf_status?: { success: boolean; attempts: number; error?: string; path?: string; check_num?: string; }; }[]; skipped: { car_no: string; car_id: number; reason: string }[]; errors: { car_no: string; error: string }[]; summary: { imported_count: number; skipped_count: number; error_count: number; pdf_success_count: number; pdf_failed_count: number; }; }> => { const { data } = await api.post('/carmodoo/import', { cars }); return data; }, // Translation Management (Admin) getCarTranslations: async (carId: number): Promise<{ car_id: number; car_name: string; dealer_description: string | null; translations: { en: string | null; mn: string | null; ru: string | null; }; has_translations: boolean; papago_configured: boolean; }> => { const { data } = await api.get(`/carmodoo/car/${carId}/translations`); return data; }, updateCarTranslations: async (carId: number, translations: { dealer_description?: string; // 한국어 원문 dealer_description_en?: string; dealer_description_mn?: string; dealer_description_ru?: string; }): Promise<{ message: string; car_id: number; dealer_description?: string; translations: { en: string | null; mn: string | null; ru: string | null }; }> => { const { data } = await api.put(`/carmodoo/car/${carId}/translations`, translations); return data; }, regenerateTranslations: async (carId: number): Promise<{ message: string; car_id: number; translations: { en: string | null; mn: string | null; ru: string | null }; }> => { const { data } = await api.post(`/carmodoo/car/${carId}/translations/regenerate`); return data; }, getUntranslatedCars: async (limit?: number): Promise<{ count: number; cars: { id: number; car_name: string; dealer_description: string; has_en: boolean; has_mn: boolean; has_ru: boolean; }[]; }> => { const { data } = await api.get('/carmodoo/admin/untranslated-cars', { params: { limit } }); return data; }, translateAllPending: async (): Promise<{ message: string; total: number; success: number; failed: number; results: { car_id: number; status: string; error?: string }[]; }> => { const { data } = await api.post('/carmodoo/admin/translate-all-pending'); return data; }, }; // Settings API export interface SystemSettings { id: number; search_page_size: number; korea_margin_percent: number; mongolia_margin_percent: number; cc_per_usdc: number; cc_per_view: number; cc_signup_bonus: number; cache_ttl_hours: number; board_enabled?: boolean; } export const settingsApi = { getSettings: async (): Promise => { const { data } = await api.get('/settings/'); return data; }, getSearchPageSize: async (): Promise<{ search_page_size: number }> => { const { data } = await api.get('/settings/search-page-size'); return data; }, updateSettings: async (settings: Partial): Promise => { const { data } = await api.put('/settings/', settings); return data; }, }; // Vehicle Request API export interface VehicleRequest { id: number; user_id: number; user_email?: string; // 관리자용 user_name?: string; // 관리자용 maker_code?: string; maker_name?: string; model_code?: string; model_name?: string; grade_code?: string; grade_name?: string; year_from?: number; year_to?: number; mileage_min?: number; mileage_max?: number; fuel?: string; displacement_min?: number; displacement_max?: number; status: string; admin_reviewed_at?: string; created_at: string; } export interface RequestVehicle { id: number; request_id: number; car_id?: number; // Reference to cars table car_data: Record; is_approved: boolean; approved_at?: string; created_at: string; } export interface PurchasedVehicle { id: number; user_id: number; car_name?: string; car_data?: Record; car_image?: string; vehicle_price_krw?: number; domestic_cost_krw?: number; shipping_cost_usd?: number; total_cost_krw?: number; car_type?: string; shipping_status: number; status_updated_at?: string; current_location?: string; estimated_arrival?: string; purchased_at: string; delivered_at?: string; } export interface VehicleRequestWithVehicles { request: VehicleRequest; approved_vehicles: RequestVehicle[]; } // Directly purchased car (from banner with 1CC) export interface DirectPurchasedCar { id: number; car_id: number; car_data: Record; cc_paid: number; purchased_at: string; } // Full response including both recommended and directly purchased cars export interface MyVehiclesResponse { vehicle_requests: VehicleRequestWithVehicles[]; direct_purchases: DirectPurchasedCar[]; } export const vehicleRequestsApi = { // User endpoints createRequest: async (requestData: { maker_code: string; maker_name?: string; model_code: string; model_name?: string; grade_code?: string; grade_name?: string; year_from?: number; year_to?: number; mileage_min?: number; mileage_max?: number; fuel?: string; displacement_min?: number; displacement_max?: number; }): Promise => { const { data } = await api.post('/vehicle-requests/', requestData); return data; }, getMyRequests: async (): Promise => { const { data } = await api.get('/vehicle-requests/my-requests'); return data; }, // Get all vehicles: recommended + directly purchased from banners getMyVehicles: async (): Promise => { const { data } = await api.get('/vehicle-requests/my-vehicles'); return data; }, getPurchasedVehicles: async (): Promise => { const { data } = await api.get('/vehicle-requests/purchased'); return data; }, // Admin endpoints adminGetAllRequests: async (status?: string): Promise => { const params = status ? { status } : {}; const { data } = await api.get('/vehicle-requests/admin/list', { params }); return data; }, adminGetRequestDetail: async (requestId: number): Promise => { const { data } = await api.get(`/vehicle-requests/admin/${requestId}`); return data; }, adminAddVehicle: async (requestId: number, vehicleData: { request_id: number; car_data: Record; is_approved?: boolean; }): Promise => { const { data } = await api.post(`/vehicle-requests/admin/${requestId}/vehicles`, vehicleData); return data; }, adminApproveVehicles: async (requestId: number, vehicleIds: number[]): Promise<{ message: string }> => { const { data } = await api.post(`/vehicle-requests/admin/${requestId}/approve-vehicles`, { vehicle_ids: vehicleIds }); return data; }, adminUpdateRequestStatus: async (requestId: number, newStatus: string): Promise<{ message: string }> => { const { data } = await api.put(`/vehicle-requests/admin/${requestId}/status`, null, { params: { new_status: newStatus } }); return data; }, adminDeleteVehicle: async (requestId: number, vehicleId: number): Promise<{ message: string }> => { const { data } = await api.delete(`/vehicle-requests/admin/${requestId}/vehicles/${vehicleId}`); return data; }, adminCreatePurchased: async (userId: number, vehicleData: { car_name: string; car_data?: Record; car_image?: string; vehicle_price_krw: number; domestic_cost_krw: number; shipping_cost_usd: number; total_cost_krw: number; car_type: string; }): Promise => { const { data } = await api.post('/vehicle-requests/admin/purchased', vehicleData, { params: { user_id: userId } }); return data; }, adminGetAllPurchased: async (): Promise => { const { data } = await api.get('/vehicle-requests/admin/purchased/all'); return data; }, adminUpdateShippingStatus: async (vehicleId: number, statusUpdate: { shipping_status: number; current_location?: string; estimated_arrival?: string; }): Promise => { const { data } = await api.put(`/vehicle-requests/admin/purchased/${vehicleId}/status`, statusUpdate); return data; }, }; // CC (Coin) API export interface PaymentInfo { usdc_wallet_address: string; usdc_network: string; min_charge_usd: number; max_charge_usd: number; supported_currencies: string[]; supported_methods: string[]; rate: string; } export interface ChargeHistory { id: number; amount: number; amount_usd?: number; currency: string; cc_amount: number; payment_method: string; transaction_id?: string; status: string; created_at?: string; } export interface AdminPayment extends ChargeHistory { user_id: number; user_email?: string; user_name?: string; wallet_address?: string; admin_note?: string; verified_at?: string; } export interface AdminPaymentListResponse { payments: AdminPayment[]; total: number; page: number; page_size: number; } export const ccApi = { getBalance: async (): Promise<{ cc_balance: number }> => { const { data } = await api.get('/cc/balance'); return data; }, getPurchasedViews: async (): Promise => { const { data } = await api.get('/cc/views'); return data; }, getPurchasedCarIds: async (): Promise<{ car_ids: number[] }> => { const { data } = await api.get('/cc/views/car-ids'); return data; }, purchaseView: async (carId: number): Promise<{ message: string; cc_balance: number; car_id: number }> => { const { data } = await api.post('/cc/purchase-view', { car_id: carId }); return data; }, checkView: async (carId: number): Promise<{ has_access: boolean; cc_balance: number }> => { const { data } = await api.get(`/cc/check-view/${carId}`); return data; }, getPaymentInfo: async (): Promise => { const { data } = await api.get('/cc/payment-info'); return data; }, chargeCC: async (chargeData: { amount: number; currency?: string; payment_method?: string; transaction_id?: string; wallet_address?: string; }): Promise<{ message: string; charge_id: number; amount: number; currency: string; cc_amount: number; status: string; payment_info?: { usdc_wallet?: string; network?: string }; }> => { const { data } = await api.post('/cc/charge', chargeData); return data; }, chargeUSDC: async (chargeData: { amount_usdc: number; transaction_hash: string; wallet_address: string; network?: string; }): Promise<{ message: string; charge_id: number; amount_usdc: number; cc_amount: number; status: string; transaction_hash: string; }> => { const { data } = await api.post('/cc/charge/usdc', chargeData); return data; }, getChargeHistory: async (): Promise => { const { data } = await api.get('/cc/charge-history'); return data; }, // Admin endpoints adminGetPendingPayments: async (): Promise => { const { data } = await api.get('/cc/admin/pending'); return data; }, adminGetAllPayments: async (params: { status?: string; page?: number; page_size?: number; }): Promise => { const { data } = await api.get('/cc/admin/all', { params }); return data; }, adminVerifyPayment: async (chargeId: number, approved: boolean, adminNote?: string): Promise<{ message: string; charge_id: number; new_status: string; }> => { const { data } = await api.put(`/cc/admin/${chargeId}/verify`, null, { params: { approved, admin_note: adminNote } }); return data; }, // Performance Check APIs (0.1 CC) checkPerformanceCheckAccess: async (carId: number): Promise<{ has_access: boolean; has_performance_check: boolean; cc_balance: number; cost: number; }> => { const { data } = await api.get(`/cc/check-performance-check/${carId}`); return data; }, purchasePerformanceCheck: async (carId: number): Promise<{ message: string; cc_balance: number; car_id: number; }> => { const { data } = await api.post('/cc/purchase-performance-check', { car_id: carId }); return data; }, // Get performance check data from carmodoo API getPerformanceCheck: async (carId: number): Promise<{ car_id: number; found: boolean; has_access: boolean; preview?: { check_number: string; check_date: string | null; mileage: number | null; }; data: any | null; }> => { const { data } = await api.get(`/carmodoo/car/${carId}/performance-check`); return data; }, // Download performance check PDF downloadPerformanceCheckPdf: async (carId: number): Promise => { const response = await api.get(`/carmodoo/car/${carId}/performance-check/pdf`, { responseType: 'blob' }); return response.data; }, // Get PDF URL for iframe/embed viewing getPerformanceCheckPdfUrl: (carId: number): string => { const token = typeof window !== 'undefined' ? localStorage.getItem('token') : null; const baseUrl = process.env.NEXT_PUBLIC_API_URL || ''; return `${baseUrl}/api/carmodoo/car/${carId}/performance-check/pdf`; }, }; // Withdrawal API export interface WithdrawalBalance { total_earned: number; total_withdrawn: number; pending_withdrawal: number; available_balance: number; } export interface WithdrawalRequest { id: number; user_id: number; amount: number; tax_withheld: number; net_amount: number; bank_name: string; bank_account: string; account_holder: string; status: string; admin_note?: string; requested_at: string; processed_at?: string; } export const withdrawalApi = { getBalance: async (): Promise => { const { data } = await api.get('/withdrawal/balance'); return data; }, createRequest: async (requestData: { amount: number; bank_name: string; bank_account: string; account_holder: string; }): Promise => { const { data } = await api.post('/withdrawal/request', requestData); return data; }, getMyRequests: async (): Promise => { const { data } = await api.get('/withdrawal/my-requests'); return data; }, // Admin endpoints adminGetAllRequests: async (status?: string): Promise => { const params = status ? { status_filter: status } : {}; const { data } = await api.get('/withdrawal/admin/list', { params }); return data; }, adminProcessRequest: async (requestId: number, processData: { status: string; admin_note?: string; }): Promise => { const { data } = await api.put(`/withdrawal/admin/${requestId}/process`, processData); return data; }, }; // Referral API export interface ReferralReward { id: number; referrer_id: number; referred_user_id: number; payment_amount: number; reward_amount: number; status: string; created_at: string; credited_at?: string; } export interface ReferralStats { total_referrals: number; total_rewards_earned: number; total_rewards_credited: number; total_rewards_pending: number; available_for_withdrawal: number; } export interface ReferralSettings { referral_reward_enabled: boolean; referral_reward_percent: number; referral_reward_type: string; } export const referralApi = { getMyLink: async (): Promise<{ referral_code: string; referral_link: string }> => { const { data } = await api.get('/referral/my-link'); return data; }, getMyRewards: async (): Promise => { const { data } = await api.get('/referral/my-rewards'); return data; }, getStats: async (): Promise => { const { data } = await api.get('/referral/stats'); return data; }, getSettings: async (): Promise => { const { data } = await api.get('/referral/settings'); return data; }, // Admin endpoints adminUpdateSettings: async (settings: Partial): Promise => { const { data } = await api.put('/referral/admin/settings', settings); return data; }, adminGetAllRewards: async (): Promise => { const { data } = await api.get('/referral/admin/all-rewards'); return data; }, }; // Notification API export interface Notification { id: number; user_id: number; notification_type: string; title: string; message: string; link?: string; related_id?: number; related_type?: string; is_read: boolean; read_at?: string; created_at: string; } export interface NotificationListResponse { notifications: Notification[]; unread_count: number; total: number; } export const notificationApi = { getNotifications: async (page: number = 1, pageSize: number = 20, unreadOnly: boolean = false): Promise => { const { data } = await api.get('/notifications/', { params: { page, page_size: pageSize, unread_only: unreadOnly } }); return data; }, getUnreadCount: async (): Promise<{ unread_count: number }> => { const { data } = await api.get('/notifications/unread-count'); return data; }, markAsRead: async (notificationIds: number[]): Promise<{ message: string }> => { const { data } = await api.post('/notifications/mark-read', { notification_ids: notificationIds }); return data; }, markAllAsRead: async (): Promise<{ message: string }> => { const { data } = await api.post('/notifications/mark-all-read'); return data; }, deleteNotification: async (notificationId: number): Promise<{ message: string }> => { const { data } = await api.delete(`/notifications/${notificationId}`); return data; }, // Admin endpoints adminSendNotification: async (notificationData: { user_id: number; notification_type: string; title: string; message: string; link?: string; }): Promise => { const { data } = await api.post('/notifications/admin/send', notificationData); return data; }, adminSendToAll: async (title: string, message: string, link?: string): Promise<{ message: string }> => { const { data } = await api.post('/notifications/admin/send-all', null, { params: { title, message, link } }); return data; }, }; // Inquiry API export interface Inquiry { id: number; user_id: number; car_id?: number; category: string; subject?: string; message: string; contact_email?: string; contact_phone?: string; status: string; admin_response?: string; responded_at?: string; responded_by?: number; created_at: string; updated_at?: string; } export interface InquiryMessage { id: number; inquiry_id: number; user_id: number; message: string; is_admin: boolean; created_at: string; } export interface InquiryListResponse { inquiries: Inquiry[]; total: number; total_pages?: number; page?: number; page_size?: number; } export interface InquiryWithMessages { inquiry: Inquiry; messages: InquiryMessage[]; } export interface InquiryStats { total: number; pending: number; in_progress: number; resolved: number; closed: number; } // User Management API (Admin) export interface AdminUser { id: number; email: string; name?: string; phone?: string; country?: string; cc_balance: number; is_dealer: boolean; referral_code?: string; referred_by?: string; created_at?: string; } export interface AdminUserListResponse { users: AdminUser[]; total: number; page: number; page_size: number; } export const adminUserApi = { getUsers: async (params: { page?: number; page_size?: number; search?: string; is_dealer?: boolean; }): Promise => { const { data } = await api.get('/auth/admin/users', { params }); return data; }, getUser: async (userId: number): Promise => { const { data } = await api.get(`/auth/admin/users/${userId}`); return data; }, updateUser: async (userId: number, userData: { name?: string; phone?: string; country?: string; }): Promise => { const { data } = await api.put(`/auth/admin/users/${userId}`, userData); return data; }, adjustCC: async (userId: number, amount: number, reason?: string): Promise<{ message: string; new_balance: number }> => { const { data } = await api.put(`/auth/admin/users/${userId}/cc`, null, { params: { amount, reason } }); return data; }, // 사용자 삭제 (관리자) deleteUser: async (userId: number, hardDelete: boolean = false): Promise<{ message: string; deleted_user_id: number; hard_delete: boolean; deleted_at?: string; }> => { const { data } = await api.delete(`/auth/admin/users/${userId}`, { params: { hard_delete: hardDelete } }); return data; }, // 탈퇴 요청 사용자 목록 getWithdrawnUsers: async (page: number = 1, pageSize: number = 20): Promise => { const { data } = await api.get('/auth/admin/users/withdrawn', { params: { page, page_size: pageSize } }); return data; }, // 삭제된 사용자 목록 getDeletedUsers: async (params: { page?: number; page_size?: number; search?: string; }): Promise => { const { data } = await api.get('/auth/admin/users/deleted', { params }); return data; }, // 삭제된 사용자 복원 restoreUser: async (userId: number): Promise<{ message: string; user_id: number }> => { const { data } = await api.post(`/auth/admin/users/${userId}/restore`); return data; }, }; // Admin PDF API (성능점검표 PDF 관리) export interface PdfFailure { car_id: number; check_num: string; error: string; timestamp: string; retried: boolean; } export interface PdfRetryResult { car_id: number; check_number: string; status: 'success' | 'failed' | 'error'; pdf_path?: string; error?: string; } export const adminPdfApi = { // PDF 생성 실패 목록 조회 getFailures: async (): Promise<{ failures: PdfFailure[]; count: number }> => { const { data } = await api.get('/carmodoo/admin/pdf-failures'); return data; }, // 모든 실패한 PDF 재시도 retryAllFailed: async (): Promise<{ total: number; success: number; failed: number; results: PdfRetryResult[]; }> => { const { data } = await api.post('/carmodoo/admin/retry-all-failed-pdfs'); return data; }, // 단일 차량 PDF 재생성 regenerateSingle: async (carId: number): Promise<{ message: string; pdf_path?: string }> => { const { data } = await api.post(`/carmodoo/car/${carId}/regenerate-pdf`); return data; }, }; // Dashboard API export interface DashboardStats { total_users: number; new_users_today: number; new_users_this_week: number; total_dealers: number; pending_dealer_applications: number; total_cars: number; total_vehicle_requests: number; pending_requests: number; total_purchased_vehicles: number; total_inquiries: number; pending_inquiries: number; total_shares: number; purchased_shares: number; total_withdrawals: number; pending_withdrawals: number; total_cc_charged: number; total_withdrawal_amount: number; } export interface RevenueStats { total_revenue: number; revenue_this_month: number; revenue_last_month: number; platform_commission: number; dealer_commission: number; } export interface ChartData { labels: string[]; values: number[]; } export interface RecentActivity { type: string; title: string; description: string; time: string; icon: string; } export interface TopDealer { id: number; name: string; dealer_code: string; total_sales: number; total_commission: number; } export interface PendingActions { pending_requests: number; pending_inquiries: number; pending_dealer_applications: number; pending_withdrawals: number; total_pending: number; } export const dashboardApi = { getStats: async (): Promise => { const { data } = await api.get('/dashboard/stats'); return data; }, getRevenue: async (): Promise => { const { data } = await api.get('/dashboard/revenue'); return data; }, getUserChart: async (days: number = 30): Promise => { const { data } = await api.get('/dashboard/chart/users', { params: { days } }); return data; }, getRequestChart: async (days: number = 30): Promise => { const { data } = await api.get('/dashboard/chart/requests', { params: { days } }); return data; }, getRevenueChart: async (days: number = 30): Promise => { const { data } = await api.get('/dashboard/chart/revenue', { params: { days } }); return data; }, getRecentActivities: async (limit: number = 10): Promise => { const { data } = await api.get('/dashboard/recent-activities', { params: { limit } }); return data; }, getTopDealers: async (limit: number = 5): Promise => { const { data } = await api.get('/dashboard/top-dealers', { params: { limit } }); return data; }, getPendingActions: async (): Promise => { const { data } = await api.get('/dashboard/pending-actions'); return data; }, }; // Push Notification API export interface PushSubscriptionData { endpoint: string; p256dh_key: string; auth_key: string; device_info?: string; } export interface NotificationPreferences { vehicle_recommended: boolean; shipping_update: boolean; payment_confirmed: boolean; withdrawal_processed: boolean; dealer_status: boolean; share_purchased: boolean; referral_reward: boolean; inquiry_reply: boolean; system_announcements: boolean; push_enabled: boolean; email_enabled: boolean; } export const pushApi = { getVapidKey: async (): Promise<{ public_key: string }> => { const { data } = await api.get('/push/vapid-key'); return data; }, subscribe: async (subscription: PushSubscriptionData): Promise<{ message: string }> => { const { data } = await api.post('/push/subscribe', subscription); return data; }, unsubscribe: async (endpoint: string): Promise<{ message: string }> => { const { data } = await api.delete('/push/unsubscribe', { params: { endpoint } }); return data; }, getSubscriptions: async (): Promise<{ id: number; endpoint: string; device_info?: string; created_at?: string }[]> => { const { data } = await api.get('/push/subscriptions'); return data; }, getPreferences: async (): Promise => { const { data } = await api.get('/push/preferences'); return data; }, updatePreferences: async (preferences: Partial): Promise<{ message: string }> => { const { data } = await api.put('/push/preferences', preferences); return data; }, // Admin adminGetStats: async (): Promise<{ total_subscriptions: number; users_with_push: number }> => { const { data } = await api.get('/push/admin/stats'); return data; }, }; export const inquiryApi = { // User endpoints getInquiries: async (): Promise => { const { data } = await api.get('/inquiries'); return data; }, createInquiry: async (inquiryData: { category?: string; subject?: string; message: string; contact_email?: string; contact_phone?: string; car_id?: number; }): Promise => { const { data } = await api.post('/inquiries', inquiryData); return data; }, getMyInquiries: async (page: number = 1, pageSize: number = 10, status?: string): Promise => { const params: Record = { page, page_size: pageSize }; if (status) params.status = status; const { data } = await api.get('/inquiries/my-inquiries', { params }); return data; }, getInquiryDetail: async (inquiryId: number): Promise => { const { data } = await api.get(`/inquiries/my-inquiries/${inquiryId}`); return data; }, addMessage: async (inquiryId: number, message: string): Promise => { const { data } = await api.post(`/inquiries/my-inquiries/${inquiryId}/message`, { message }); return data; }, // Admin endpoints adminGetInquiries: async (page: number = 1, pageSize: number = 20, status?: string, category?: string): Promise => { const params: Record = { page, page_size: pageSize }; if (status) params.status = status; if (category) params.category = category; const { data } = await api.get('/inquiries/admin/list', { params }); return data; }, adminGetInquiryDetail: async (inquiryId: number): Promise => { const { data } = await api.get(`/inquiries/admin/${inquiryId}`); return data; }, adminRespond: async (inquiryId: number, responseData: { message: string; status?: string }): Promise => { const { data } = await api.post(`/inquiries/admin/${inquiryId}/respond`, responseData); return data; }, adminUpdateStatus: async (inquiryId: number, status: string): Promise => { const { data } = await api.put(`/inquiries/admin/${inquiryId}/status`, { status }); return data; }, adminGetStats: async (): Promise => { const { data } = await api.get('/inquiries/admin/stats'); return data; }, }; // Exchange Rate API export interface ExchangeRateSimple { [currencyCode: string]: { rate: number; // KRW per 1 unit (e.g., 1 USD = 1450 KRW) symbol: string; name: string; }; } export const exchangeRateApi = { getSimpleRates: async (): Promise => { const { data } = await api.get('/exchange-rate/simple'); return data; }, refreshRates: async (): Promise => { const { data } = await api.post('/exchange-rate/refresh'); return data; }, }; // Verification API export interface VerificationResponse { success: boolean; message: string; } export interface VerificationStatus { email_verified: boolean; phone_verified: boolean; email?: string; phone?: string; } export const verificationApi = { // Email verification for pre-registration sendEmailCodePreregister: async (email: string, language: string = 'en'): Promise => { const { data } = await api.post('/verification/email/send-preregister', { email, language }); return data; }, verifyEmailCodePreregister: async (email: string, code: string): Promise => { const { data } = await api.post('/verification/email/verify-preregister', { email, code }); return data; }, // Email verification for logged-in users sendEmailCode: async (email: string, language: string = 'en'): Promise => { const { data } = await api.post('/verification/email/send', { email, language }); return data; }, verifyEmailCode: async (code: string, email?: string): Promise => { const { data } = await api.post('/verification/email/verify', { code, email }); return data; }, // Phone verification (requires login) sendPhoneCode: async (phone: string, language: string = 'en'): Promise => { const { data } = await api.post('/verification/phone/send', { phone, language }); return data; }, verifyPhoneCode: async (phone: string, code: string): Promise => { const { data } = await api.post('/verification/phone/verify', { phone, code }); return data; }, // Get verification status getStatus: async (): Promise => { const { data } = await api.get('/verification/status'); return data; }, }; // Visitor Analytics API export interface VisitorStatsOverview { total_visits: number; unique_visitors: number; device_breakdown: Record; browser_breakdown: Record; country_breakdown: Record; } export interface TopPage { path: string; views: number; title?: string; } export interface TopReferrer { domain: string; visits: number; } export interface RealtimeStats { active_visitors: number; minutes: number; recent_pages: { path: string; views: number }[]; } export const visitorApi = { getOverview: async (days: number = 30): Promise => { const { data } = await api.get('/visitor/admin/overview', { params: { days } }); return data; }, getVisitsChart: async (days: number = 30): Promise => { const { data } = await api.get('/visitor/admin/chart/visits', { params: { days } }); return data; }, getUniqueVisitorsChart: async (days: number = 30): Promise => { const { data } = await api.get('/visitor/admin/chart/unique-visitors', { params: { days } }); return data; }, getTopPages: async (days: number = 30, limit: number = 20): Promise => { const { data } = await api.get('/visitor/admin/top-pages', { params: { days, limit } }); return data; }, getTopReferrers: async (days: number = 30, limit: number = 10): Promise => { const { data } = await api.get('/visitor/admin/top-referrers', { params: { days, limit } }); return data; }, getRealtime: async (minutes: number = 5): Promise => { const { data } = await api.get('/visitor/admin/realtime', { params: { minutes } }); return data; }, }; // SNS Share API export interface SnsShareSubmission { id: number; user_id: number; car_id: number; platform: string; sns_url: string; status: string; rejected_reason?: string; reward_cc: number; rewarded_at?: string; submitted_at: string; verified_at?: string; verified_by?: number; car_name?: string; car_image?: string; } export interface SnsShareListResponse { submissions: SnsShareSubmission[]; total: number; pending_count: number; approved_count: number; rejected_count: number; } export interface SnsShareStats { total_submissions: number; pending_submissions: number; approved_submissions: number; rejected_submissions: number; total_rewarded_cc: number; } export interface CampaignStatus { enabled: boolean; start_date?: string; end_date?: string; sns_share_reward_cc?: number; referral_signup_reward_cc?: number; message?: string; } export const snsShareApi = { // Public: 캠페인 상태 조회 getCampaignStatus: async (): Promise => { const { data } = await api.get('/sns-share/campaign-status'); return data; }, // User: SNS 공유 URL 제출 submit: async (carId: number, platform: string, snsUrl: string): Promise => { const { data } = await api.post('/sns-share/submit', { car_id: carId, platform, sns_url: snsUrl, }); return data; }, // User: 내 제출 목록 조회 getMySubmissions: async (): Promise => { const { data } = await api.get('/sns-share/my-submissions'); return data; }, // Admin: 전체 제출 목록 조회 getAdminList: async (statusFilter?: string, skip: number = 0, limit: number = 50): Promise => { const params: any = { skip, limit }; if (statusFilter) params.status_filter = statusFilter; const { data } = await api.get('/sns-share/admin/list', { params }); return data; }, // Admin: 통계 조회 getAdminStats: async (): Promise => { const { data } = await api.get('/sns-share/admin/stats'); return data; }, // Admin: 검증 (승인/거부) verify: async (submissionId: number, approved: boolean, rejectedReason?: string): Promise => { const { data } = await api.put(`/sns-share/admin/${submissionId}/verify`, { approved, rejected_reason: rejectedReason, }); return data; }, }; // Board (Bulletin) API Types export interface BoardCategory { id: number; name: string; name_en?: string; name_mn?: string; name_ru?: string; slug: string; description?: string; sort_order: number; is_active: boolean; created_at: string; updated_at?: string; post_count: number; } export interface BoardPostListItem { id: number; title: string; category_id: number; category_name?: string; author_id: number; author_name?: string; is_notice: boolean; is_pinned: boolean; view_count: number; created_at: string; } export interface BoardPost { id: number; title: string; content: string; category_id: number; category?: BoardCategory; author_id: number; author?: { id: number; name?: string; email: string; is_admin: boolean; }; is_notice: boolean; is_pinned: boolean; is_published: boolean; view_count: number; created_at: string; updated_at?: string; } export interface BoardPostListResponse { posts: BoardPostListItem[]; notices: BoardPostListItem[]; total: number; page: number; page_size: number; total_pages: number; } export interface BoardCategoryListResponse { categories: BoardCategory[]; total: number; } // Board API export const boardApi = { // Categories getCategories: async (includeInactive: boolean = false): Promise => { const { data } = await api.get('/board/categories', { params: { include_inactive: includeInactive } }); return data; }, createCategory: async (category: { name: string; name_en?: string; name_mn?: string; name_ru?: string; slug: string; description?: string; sort_order?: number; is_active?: boolean; }): Promise => { const { data } = await api.post('/board/categories', category); return data; }, updateCategory: async (categoryId: number, category: { name?: string; name_en?: string; name_mn?: string; name_ru?: string; slug?: string; description?: string; sort_order?: number; is_active?: boolean; }): Promise => { const { data } = await api.put(`/board/categories/${categoryId}`, category); return data; }, deleteCategory: async (categoryId: number): Promise => { await api.delete(`/board/categories/${categoryId}`); }, // Posts getPosts: async (params: { page?: number; page_size?: number; category_id?: number; search?: string; }): Promise => { const { data } = await api.get('/board/posts', { params }); return data; }, getPost: async (postId: number): Promise => { const { data } = await api.get(`/board/posts/${postId}`); return data; }, createPost: async (post: { title: string; content: string; category_id: number; is_notice?: boolean; }): Promise => { const { data } = await api.post('/board/posts', post); return data; }, updatePost: async (postId: number, post: { title?: string; content?: string; category_id?: number; is_notice?: boolean; is_pinned?: boolean; is_published?: boolean; }): Promise => { const { data } = await api.put(`/board/posts/${postId}`, post); return data; }, deletePost: async (postId: number): Promise => { await api.delete(`/board/posts/${postId}`); }, // Admin getAdminPosts: async (params: { page?: number; page_size?: number; category_id?: number; is_notice?: boolean; is_published?: boolean; search?: string; }): Promise => { const { data } = await api.get('/board/admin/posts', { params }); return data; }, toggleNotice: async (postId: number): Promise<{ is_notice: boolean; is_pinned: boolean }> => { const { data } = await api.put(`/board/admin/posts/${postId}/toggle-notice`); return data; }, togglePin: async (postId: number): Promise<{ is_pinned: boolean }> => { const { data } = await api.put(`/board/admin/posts/${postId}/toggle-pin`); return data; }, }; export default api;