fix: CompletedOrdersTable CSV quoting, error handling, button disabled state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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 ? (
|
||||
|
||||
Reference in New Issue
Block a user