adminjs
1
// src/api/admin.js
2
// File này chứa các hàm API dùng cho quản trị viên (admin) để quản lý các bài đăng (listing)
3
4
import axios from './axios.js'; // Import instance axios đã được cấu hình sẵn (baseURL, interceptor, v.v.)
5
6
/**
7
 * Lấy danh sách các bài đăng đang chờ duyệt
8
 * params: gồm page, size, sortBy, sortDirection
9
 */
10
export const getPendingListings = async (params = {}) => {
11
    // Giải nén giá trị mặc định cho các tham số nếu người dùng không truyền vào
12
    const {
13
        page = 0,
14
        size = 10,
15
    } = params;
16
17
    try {
18
        // Gửi request GET đến endpoint admin/post-request (API backend dùng để lấy danh sách cần duyệt)
19
        const response = await axios.get('/admin/post-request', {
20
            params: { page, size } // Truyền query parameters vào axios
21
        });
22
23
        // Lấy dữ liệu từ response, nếu không có thì dùng object rỗng
24
        const data = response.data || {};
25
26
        // Backend thường trả về object chứa các thuộc tính: content, totalElements, totalPages, size, number
27
        const content = data.content || data.data || []; // danh sách item thực tế
28
        const totalElements = data.totalElements ?? (data.length || 0); // tổng số phần tử
29
        const totalPages = data.totalPages ?? 1; // tổng số trang
30
        const pageSize = data.size ?? size; // kích thước trang hiện tại
31
        const pageNumber = data.number ?? page; // trang hiện tại
32
33
        // Chuyển đổi từng item trong danh sách thành object chuẩn để hiển thị trên UI
34
        const mapped = (Array.isArray(content) ? content : []).map(item => {
35
            const id = item.requestId; // ID của yêu cầu duyệt
36
            const listingId = item.listingId; // ID của bài đăng gốc
37
            const title = item.title ?? '—'; // tiêu đề bài đăng
38
            const price = typeof item.price === 'number'
39
                ? item.price
40
                : (item.price ? Number(item.price) : null); // chuyển giá sang số
41
            const thumbnail = item.thumbnailUrl || null; // ảnh thumbnail
42
            const status = item.status ; // trạng thái bài đăng
43
            return { id, listingId, title, price, thumbnail, status, raw: item }; // trả về object chuẩn
44
        });
45
46
        // Trả về dữ liệu đã được chuẩn hóa
47
        return {
48
            content: mapped,
49
            totalElements,
50
            totalPages,
51
            size: pageSize,
52
            number: pageNumber
53
        };
54
    } catch (error) {
55
        // Nếu gọi API chính thất bại, log lỗi
56
        console.error('Admin API failed:', error);
57
58
        try {
59
            // Gọi dự phòng đến 2 endpoint lấy EV và battery listings
60
            const [evResponse, batteryResponse] = await Promise.all([
61
                axios.get('/evCart', { params: { page, size } }),
62
                axios.get('/batteryCart', { params: { page, size } })
63
            ]);
64
65
            // Lấy dữ liệu từ 2 response
66
            const evListings = evResponse.data.content || evResponse.data || [];
67
            const batteryListings = batteryResponse.data.content || batteryResponse.data || [];
68
            const allListings = [...evListings, ...batteryListings]; // Gộp lại
69
70
            // Tạm thời hiển thị tất cả listing để debug
71
            const filteredListings = allListings.filter(listing => true);
72
73
            // Chuyển đổi từng listing về dạng chuẩn để hiển thị
74
            const mapped = filteredListings.map(item => ({
75
                id: item.listingId,
76
                title: item.title ?? '—',
77
                price: typeof item.price === 'number'
78
                    ? item.price
79
                    : (item.price ? Number(item.price) : null),
80
                thumbnail: item.thumbnailUrl || null,
81
                status: item.status,
82
                raw: item
83
            }));
84
85
            // Trả về kết quả dự phòng
86
            return {
87
                content: mapped,
88
                totalElements: mapped.length,
89
                totalPages: Math.ceil(mapped.length / size),
90
                size: size,
91
                number: page
92
            };
93
        } catch (fallbackError) {
94
            console.error('Fallback API also failed:', fallbackError);
95
            throw error; // Ném lỗi gốc ra ngoài
96
        }
97
    }
98
};
99
100
// Approve (chấp thuận) một bài đăng
101
export const approveListing = async (listingId, approvalNote = '') => {
102
    try {
103
        // Gọi endpoint approve-request/{id} để phê duyệt bài đăng
104
        const response = await axios.get(`/admin/approve-request/${listingId}`);
105
        return response.data;
106
    } catch (error) {
107
        console.error('Error approving listing:', error);
108
        throw error;
109
    }
110
};
111
112
// Reject (từ chối) một bài đăng
113
export const rejectListing = async (listingId, rejectionReason) => {
114
    // Kiểm tra lý do từ chối có tồn tại hay không
115
    if (!rejectionReason?.trim()) {
116
        throw new Error('Rejection reason is required');
117
    }
118
119
    try {
120
        // Gọi endpoint reject-request/{id}?reason=...
121
        const response = await axios.get(`/admin/reject-request/${listingId}`, {
122
            params: { reason: rejectionReason }
123
        });
124
        return response.data;
125
    } catch (error) {
126
        console.error('Error rejecting listing:', error);
127
        throw error;
128
    }
129
};
130
131
// Lấy chi tiết bài đăng (kể cả ở trạng thái PENDING)
132
export const getListingDetail = async (listingId) => {
133
    try {
134
        // Gọi endpoint admin riêng để xem chi tiết bài đăng
135
        const response = await axios.get(`/admin/listing-detail/${listingId}`);
136
        return response.data;
137
    } catch (error) {
138
        console.error('Error fetching listing detail:', error);
139
        throw error;
140
    }
141
};
142
143
// Lấy thống kê tổng quan cho trang dashboard admin
144
export const getAdminStats = async () => {
145
    try {
146
        // Gọi endpoint thống kê
147
        const response = await axios.get('/api/admin/stats');
148
        return response.data;
149
    } catch (error) {
150
        console.error('Error fetching admin stats:', error);
151
        throw error;
152
    }
153
};
154
155
// Lấy lịch sử xét duyệt (approve/reject)
156
export const getApprovalHistory = async (params = {}) => {
157
    const {
158
        page = 0,
159
        size = 10,
160
        status = null
161
    } = params;
162
163
    try {
164
        // Hiện chưa có endpoint riêng cho lịch sử, nên tạm dùng /admin/post-request
165
        const response = await axios.get('/admin/post-request', {
166
            params: { page, size }
167
        });
168
        return response.data;
169
    } catch (error) {
170
        console.error('Error fetching approval history:', error);
171
        throw error;
172
    }
173
};