feat: CSV export from analytics page

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-12 11:16:33 +03:00
parent 9d060a2dc9
commit d94cbcc825
+43 -9
View File
@@ -1,5 +1,5 @@
import { useMemo } from 'react'; import { useMemo } from 'react';
import { TrendingUp } from 'lucide-react'; import { TrendingUp, Download } from 'lucide-react';
import { useAnalyticsPage } from './useAnalyticsPage'; import { useAnalyticsPage } from './useAnalyticsPage';
const MONTHS_RU = ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек']; const MONTHS_RU = ['Янв', 'Фев', 'Мар', 'Апр', 'Май', 'Июн', 'Июл', 'Авг', 'Сен', 'Окт', 'Ноя', 'Дек'];
@@ -103,6 +103,28 @@ const AnalyticsPage = () => {
return Object.values(map).sort((a, b) => b.revenue - a.revenue); return Object.values(map).sort((a, b) => b.revenue - a.revenue);
}, [orders]); }, [orders]);
const handleExportCSV = () => {
const BOM = '';
const headers = ['ID', 'Статус', 'Сумма', 'Дата создания', 'Менеджер', 'Плёнка'];
const rows = orders.map((o) => [
o.id,
o.status ?? '',
o.final_cost ?? '',
o.created_at ? new Date(o.created_at).toLocaleDateString('ru-RU') : '',
o.assigned_to ?? '',
o.film_thickness ?? '',
].map((v) => `"${String(v).replace(/"/g, '""')}"`).join(','));
const csv = BOM + [headers.join(','), ...rows].join('\n');
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `orders-${new Date().toISOString().slice(0, 10)}.csv`;
a.click();
URL.revokeObjectURL(url);
};
if (loading) { if (loading) {
return ( return (
<div style={{ padding: 30, textAlign: 'center', color: 'var(--text-muted)', paddingTop: 80 }}> <div style={{ padding: 30, textAlign: 'center', color: 'var(--text-muted)', paddingTop: 80 }}>
@@ -119,16 +141,28 @@ const AnalyticsPage = () => {
return ( return (
<div style={{ padding: 30, maxWidth: 900 }}> <div style={{ padding: 30, maxWidth: 900 }}>
{/* Page title */} {/* Page title */}
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 28 }}> <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '24px' }}>
<TrendingUp size={22} color="var(--accent-blue)" /> <h1 style={{ fontFamily: 'var(--font-heading)', fontSize: '1.5rem', fontWeight: 800, display: 'flex', alignItems: 'center', gap: '10px' }}>
<h1 style={{ <TrendingUp size={22} color="var(--accent-blue)" />
fontFamily: 'var(--font-heading)',
fontWeight: 800,
fontSize: '1.45rem',
color: 'var(--text-main)',
}}>
Аналитика Аналитика
</h1> </h1>
<button
onClick={handleExportCSV}
disabled={loading || orders.length === 0}
style={{
display: 'inline-flex', alignItems: 'center', gap: '6px',
padding: '8px 16px', borderRadius: '8px', cursor: 'pointer',
background: 'transparent', border: '1.5px solid #E9ECEF',
fontSize: '0.88rem', fontWeight: 600, color: '#1E1E2D',
fontFamily: 'var(--font-heading)',
opacity: (loading || orders.length === 0) ? 0.5 : 1,
}}
onMouseEnter={(e) => e.currentTarget.style.borderColor = '#4361EE'}
onMouseLeave={(e) => e.currentTarget.style.borderColor = '#E9ECEF'}
>
<Download size={15} />
Экспорт CSV
</button>
</div> </div>
{/* Section A — Summary cards */} {/* Section A — Summary cards */}