Обновлено получение данных в компонентах: AgentsBarChart, AgentsTable, BillingPayoutsTable, BillingPieChart, BillingStatChart, PayoutsTransactionsTable, ReferralsTable, RevenueChart и SalesTable. Теперь данные извлекаются из свойства items ответа API, что улучшает обработку данных и предотвращает возможные ошибки.
This commit is contained in:
parent
410410bc85
commit
14921074a5
@ -47,7 +47,7 @@ const AgentsBarChart: React.FC = () => {
|
|||||||
return res.json();
|
return res.json();
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setData(data);
|
setData(data.items);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export default function AgentsTable({ filters, reloadKey }: { filters: { dateSta
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(setData)
|
.then(apiData => setData(apiData.items))
|
||||||
.catch(() => setData([]));
|
.catch(() => setData([]));
|
||||||
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo, useState, useEffect } from 'react';
|
||||||
import { MaterialReactTable, MRT_ColumnDef, MRT_Row, useMaterialReactTable } from 'material-react-table';
|
import { MaterialReactTable, MRT_ColumnDef, MRT_Row, useMaterialReactTable } from 'material-react-table';
|
||||||
import mockData from '../data/mockData';
|
import mockData from '../data/mockData';
|
||||||
import { Box, Button } from '@mui/material';
|
import { Box, Button } from '@mui/material';
|
||||||
import FileDownloadIcon from '@mui/icons-material/FileDownload';
|
import FileDownloadIcon from '@mui/icons-material/FileDownload';
|
||||||
import { mkConfig, generateCsv, download } from 'export-to-csv';
|
import { mkConfig, generateCsv, download } from 'export-to-csv';
|
||||||
|
import Cookies from 'js-cookie';
|
||||||
|
|
||||||
function formatCurrency(amount: number) {
|
function formatCurrency(amount: number) {
|
||||||
return amount?.toLocaleString('ru-RU', {
|
return amount?.toLocaleString('ru-RU', {
|
||||||
@ -14,24 +15,67 @@ function formatCurrency(amount: number) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const statusColor: Record<string, string> = {
|
const statusColor: Record<string, string> = {
|
||||||
'Завершена': '#4caf50',
|
'done': '#10B981', // green
|
||||||
'Ожидается': '#ff9800',
|
'waiting': '#F59E0B', // orange
|
||||||
'Ошибка': '#f44336',
|
'error': '#EF4444', // red
|
||||||
|
'process': '#3B82F6', // blue
|
||||||
|
'reject': '#800080', // purple
|
||||||
|
'new': '#E30B5C', // pink
|
||||||
};
|
};
|
||||||
|
|
||||||
const csvConfig = mkConfig({
|
const csvConfig = mkConfig({
|
||||||
fieldSeparator: ',',
|
fieldSeparator: ',',
|
||||||
decimalSeparator: '.',
|
decimalSeparator: '.', // This should be '.' for CSV regardless of locale for parsing.
|
||||||
useKeysAsHeaders: true,
|
useKeysAsHeaders: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function BillingPayoutsTable() {
|
export default function BillingPayoutsTable({ filters, reloadKey }: { filters: { dateStart: string, dateEnd: string }, reloadKey: number }) {
|
||||||
|
const [data, setData] = useState<any[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
if (filters.dateStart) params.append('date_start', filters.dateStart);
|
||||||
|
if (filters.dateEnd) params.append('date_end', filters.dateEnd);
|
||||||
|
|
||||||
|
const token = Cookies.get("access_token");
|
||||||
|
if (!token) {
|
||||||
|
console.warn("Токен авторизации не найден.");
|
||||||
|
setError("Токен авторизации не найден.");
|
||||||
|
setLoading(false);
|
||||||
|
setData([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch(`/api/billing/payouts/transactions?${params.toString()}`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': `Bearer ${token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (!res.ok) {
|
||||||
|
throw new Error(`Ошибка загрузки данных: ${res.status} ${res.statusText}`);
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then(apiData => {
|
||||||
|
setData(apiData.items);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
setError(err.message);
|
||||||
|
setLoading(false);
|
||||||
|
setData([]);
|
||||||
|
});
|
||||||
|
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
||||||
|
|
||||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
||||||
() => [
|
() => [
|
||||||
{ accessorKey: 'id', header: 'ID' },
|
{ accessorKey: 'id', header: 'ID' },
|
||||||
{ accessorKey: 'amount', header: 'Сумма',
|
{ accessorKey: 'amount', header: 'Сумма',
|
||||||
Cell: ({ cell }) => formatCurrency(cell.getValue() as number) },
|
Cell: ({ cell }) => formatCurrency(cell.getValue() as number) },
|
||||||
{ accessorKey: 'date', header: 'Дата' },
|
{ accessorKey: 'agent', header: 'Агент' }, // Добавлено поле для имени агента
|
||||||
{ accessorKey: 'status', header: 'Статус',
|
{ accessorKey: 'status', header: 'Статус',
|
||||||
Cell: ({ cell }) => (
|
Cell: ({ cell }) => (
|
||||||
<span style={{ color: statusColor[cell.getValue() as string] || '#333', fontWeight: 600 }}>
|
<span style={{ color: statusColor[cell.getValue() as string] || '#333', fontWeight: 600 }}>
|
||||||
@ -39,14 +83,17 @@ export default function BillingPayoutsTable() {
|
|||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{ accessorKey: 'method', header: 'Способ' },
|
{ accessorKey: 'create_dttm', header: 'Дата создания',
|
||||||
|
Cell: ({ cell }) => new Date(cell.getValue() as string).toLocaleDateString("ru-RU") },
|
||||||
|
{ accessorKey: 'update_dttm', header: 'Дата обновления',
|
||||||
|
Cell: ({ cell }) => new Date(cell.getValue() as string).toLocaleDateString("ru-RU") },
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
const table = useMaterialReactTable({
|
const table = useMaterialReactTable({
|
||||||
columns,
|
columns,
|
||||||
data: mockData.payouts,
|
data,
|
||||||
enableRowSelection: false,
|
enableRowSelection: false,
|
||||||
renderTopToolbarCustomActions: ({ table }) => (
|
renderTopToolbarCustomActions: ({ table }) => (
|
||||||
<Box sx={{ display: 'flex', gap: 2, p: 1, flexWrap: 'wrap' }}>
|
<Box sx={{ display: 'flex', gap: 2, p: 1, flexWrap: 'wrap' }}>
|
||||||
@ -64,6 +111,10 @@ export default function BillingPayoutsTable() {
|
|||||||
muiTableBodyCellProps: { sx: { fontSize: 14 } },
|
muiTableBodyCellProps: { sx: { fontSize: 14 } },
|
||||||
muiTableHeadCellProps: { sx: { fontWeight: 700 } },
|
muiTableHeadCellProps: { sx: { fontWeight: 700 } },
|
||||||
initialState: { pagination: { pageSize: 10, pageIndex: 0 } },
|
initialState: { pagination: { pageSize: 10, pageIndex: 0 } },
|
||||||
|
state: { isLoading: loading, showAlertBanner: error !== null, showProgressBars: loading },
|
||||||
|
renderEmptyRowsFallback: () => (
|
||||||
|
<div>{error ? `Ошибка: ${error}` : (loading ? 'Загрузка данных...' : 'Данные не найдены.')}</div>
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleExportRows = (rows: MRT_Row<any>[]) => {
|
const handleExportRows = (rows: MRT_Row<any>[]) => {
|
||||||
@ -73,7 +124,7 @@ export default function BillingPayoutsTable() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleExportData = () => {
|
const handleExportData = () => {
|
||||||
const csv = generateCsv(csvConfig)(mockData.payouts);
|
const csv = generateCsv(csvConfig)(data);
|
||||||
download(csvConfig)(csv);
|
download(csvConfig)(csv);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@ const BillingPieChart: React.FC = () => {
|
|||||||
})
|
})
|
||||||
.then((res) => res.json())
|
.then((res) => res.json())
|
||||||
.then((apiData) => {
|
.then((apiData) => {
|
||||||
const mapped = apiData.map((item: { status: string; count: number }) => ({
|
const mapped = apiData.items.map((item: { status: string; count: number }) => ({
|
||||||
name: item.status,
|
name: item.status,
|
||||||
value: item.count,
|
value: item.count,
|
||||||
fill: STATUS_COLORS[item.status] || "#A3A3A3",
|
fill: STATUS_COLORS[item.status] || "#A3A3A3",
|
||||||
|
|||||||
@ -36,7 +36,7 @@ const BillingStatChart: React.FC = () => {
|
|||||||
// Собираем все уникальные даты и статусы
|
// Собираем все уникальные даты и статусы
|
||||||
const allDates = new Set<string>();
|
const allDates = new Set<string>();
|
||||||
const allStatuses = new Set<string>();
|
const allStatuses = new Set<string>();
|
||||||
apiData.forEach((item: any) => {
|
apiData.items.forEach((item: any) => {
|
||||||
allDates.add(item.date);
|
allDates.add(item.date);
|
||||||
allStatuses.add(item.status);
|
allStatuses.add(item.status);
|
||||||
});
|
});
|
||||||
@ -50,7 +50,7 @@ const BillingStatChart: React.FC = () => {
|
|||||||
grouped[date][status] = 0;
|
grouped[date][status] = 0;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
apiData.forEach((item: any) => {
|
apiData.items.forEach((item: any) => {
|
||||||
grouped[item.date][item.status] = item.count;
|
grouped[item.date][item.status] = item.count;
|
||||||
});
|
});
|
||||||
const sorted = sortedDates.map(date => grouped[date]);
|
const sorted = sortedDates.map(date => grouped[date]);
|
||||||
|
|||||||
@ -26,6 +26,9 @@ const statusColor: Record<string, string> = {
|
|||||||
|
|
||||||
export default function PayoutsTransactionsTable({ filters, reloadKey }: { filters: { dateStart: string, dateEnd: string }, reloadKey: number }) {
|
export default function PayoutsTransactionsTable({ filters, reloadKey }: { filters: { dateStart: string, dateEnd: string }, reloadKey: number }) {
|
||||||
const [data, setData] = useState<any[]>([]);
|
const [data, setData] = useState<any[]>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (filters.dateStart) params.append('date_start', filters.dateStart);
|
if (filters.dateStart) params.append('date_start', filters.dateStart);
|
||||||
@ -34,20 +37,40 @@ export default function PayoutsTransactionsTable({ filters, reloadKey }: { filte
|
|||||||
const token = Cookies.get("access_token");
|
const token = Cookies.get("access_token");
|
||||||
if (!token) {
|
if (!token) {
|
||||||
console.warn("Токен авторизации не найден.");
|
console.warn("Токен авторизации не найден.");
|
||||||
setData([]); // Очистить данные, если токен отсутствует
|
setError("Токен авторизации не найден.");
|
||||||
|
setLoading(false);
|
||||||
|
setData([]);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
fetch(`/api/billing/payouts/transactions?${params.toString()}`, {
|
fetch(`/api/billing/payouts/transactions?${params.toString()}`, {
|
||||||
headers: {
|
headers: {
|
||||||
'Authorization': `Bearer ${token}`
|
'Authorization': `Bearer ${token}`
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(res => res.json())
|
.then(res => {
|
||||||
.then(setData)
|
if (!res.ok) {
|
||||||
.catch(() => setData([]));
|
throw new Error(`Ошибка загрузки данных: ${res.status} ${res.statusText}`);
|
||||||
|
}
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.then(apiData => {
|
||||||
|
setData(apiData.items);
|
||||||
|
setLoading(false);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
setError(err.message);
|
||||||
|
setLoading(false);
|
||||||
|
setData([]);
|
||||||
|
});
|
||||||
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
}, [data]);
|
||||||
|
|
||||||
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
const columns = useMemo<MRT_ColumnDef<any>[]>(
|
||||||
() => [
|
() => [
|
||||||
{ accessorKey: 'id', header: 'ID' },
|
{ accessorKey: 'id', header: 'ID' },
|
||||||
@ -95,6 +118,10 @@ export default function PayoutsTransactionsTable({ filters, reloadKey }: { filte
|
|||||||
muiTableBodyCellProps: { sx: { fontSize: 14 } },
|
muiTableBodyCellProps: { sx: { fontSize: 14 } },
|
||||||
muiTableHeadCellProps: { sx: { fontWeight: 700 } },
|
muiTableHeadCellProps: { sx: { fontWeight: 700 } },
|
||||||
initialState: { pagination: { pageSize: 10, pageIndex: 0 } },
|
initialState: { pagination: { pageSize: 10, pageIndex: 0 } },
|
||||||
|
state: { isLoading: loading, showAlertBanner: error !== null, showProgressBars: loading },
|
||||||
|
renderEmptyRowsFallback: () => (
|
||||||
|
<div>{error ? `Ошибка: ${error}` : (loading ? 'Загрузка данных...' : 'Данные не найдены.')}</div>
|
||||||
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleExportRows = (rows: MRT_Row<any>[]) => {
|
const handleExportRows = (rows: MRT_Row<any>[]) => {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export default function ReferralsTable({ filters, reloadKey }: { filters: { date
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(setData)
|
.then(apiData => setData(apiData.items))
|
||||||
.catch(() => setData([]));
|
.catch(() => setData([]));
|
||||||
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ const RevenueChart: React.FC = () => {
|
|||||||
return res.json();
|
return res.json();
|
||||||
})
|
})
|
||||||
.then((data) => {
|
.then((data) => {
|
||||||
setData(data);
|
setData(data.items);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|||||||
@ -35,7 +35,7 @@ export default function SalesTable({ filters, reloadKey }: { filters: { dateStar
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(res => res.json())
|
.then(res => res.json())
|
||||||
.then(setData)
|
.then(apiData => setData(apiData.items))
|
||||||
.catch(() => setData([]));
|
.catch(() => setData([]));
|
||||||
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
}, [filters.dateStart, filters.dateEnd, reloadKey]);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user