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