fix: CompletedOrdersTable CSV quoting, error handling, button disabled state

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-07 20:34:16 +03:00
parent 28df47ff00
commit 5f8a71104e
+24 -9
View File
@@ -19,6 +19,7 @@ const COLUMNS = [
const CompletedOrdersTable = () => {
const [orders, setOrders] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [sortKey, setSortKey] = useState('closed_at');
const [sortDir, setSortDir] = useState('desc');
@@ -28,8 +29,13 @@ const CompletedOrdersTable = () => {
.select('*')
.eq('status', 'закрыт')
.order('closed_at', { ascending: false })
.then(({ data }) => {
setOrders(data || []);
.then(({ data, error }) => {
if (error) {
console.error('Failed to load completed orders:', error);
setError(error.message);
} else {
setOrders(data || []);
}
setLoading(false);
});
}, []);
@@ -52,22 +58,27 @@ const CompletedOrdersTable = () => {
});
const exportCsv = () => {
const header = COLUMNS.map((c) => c.label).join(',');
const q = (val) => `"${String(val ?? '').replace(/"/g, '""')}"`;
const headers = ['Дата закрытия', 'Имя', 'Телефон', 'Тип объекта', 'Площадь м²', 'Итоговая стоимость', 'Ответственный']
.map(q).join(',');
const rows = sorted.map((o) => [
o.closed_at ? new Date(o.closed_at).toLocaleDateString('ru-RU') : '',
`"${(o.name || '').replace(/"/g, '""')}"`,
o.name || '',
o.phone || '',
OBJECT_TYPE_LABELS[o.object_type] || o.object_type || '',
o.area_sqm ?? '',
o.final_cost ?? '',
`"${(o.assigned_to || '').replace(/"/g, '""')}"`,
].join(','));
const csv = [header, ...rows].join('\n');
o.assigned_to || '',
].map(q).join(','));
const csv = [headers, ...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 = `completed_orders_${new Date().toISOString().slice(0, 10)}.csv`;
a.download = `zakazy_${new Date().toISOString().slice(0, 10)}.csv`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
@@ -93,14 +104,18 @@ const CompletedOrdersTable = () => {
</h2>
<button
onClick={exportCsv}
disabled={loading}
className="btn-primary"
style={{ display: 'flex', alignItems: 'center', gap: '7px', padding: '8px 16px', fontSize: '0.85rem' }}
style={{ display: 'flex', alignItems: 'center', gap: '7px', padding: '8px 16px', fontSize: '0.85rem', opacity: loading ? 0.5 : 1, cursor: loading ? 'not-allowed' : 'pointer' }}
>
<Download size={14} />
Экспорт CSV
</button>
</div>
{error && (
<p role="alert" style={{ color: '#ef4444', marginBottom: '14px', fontSize: '0.88rem' }}>{error}</p>
)}
{loading ? (
<p style={{ textAlign: 'center', padding: '60px', color: 'var(--text-muted)' }}>Загрузка...</p>
) : orders.length === 0 ? (