feat: add CSV export functionality to Clients and Products pages
This commit is contained in:
@@ -69,3 +69,36 @@ export const parseOrderDate = (dateStr: string): Date => {
|
||||
const fallback = new Date(dateStr);
|
||||
return isNaN(fallback.getTime()) ? new Date(0) : fallback;
|
||||
};
|
||||
|
||||
export const exportToCSV = (data: any[], filename: string) => {
|
||||
if (!data || !data.length) return;
|
||||
|
||||
const headers = Object.keys(data[0]);
|
||||
const csvRows = [];
|
||||
|
||||
// Add headers
|
||||
csvRows.push(headers.join(','));
|
||||
|
||||
// Add rows
|
||||
for (const row of data) {
|
||||
const values = headers.map(header => {
|
||||
const val = row[header];
|
||||
const escaped = ('' + val).replace(/"/g, '\\"');
|
||||
return `"${escaped}"`;
|
||||
});
|
||||
csvRows.push(values.join(','));
|
||||
}
|
||||
|
||||
const csvString = csvRows.join('\n');
|
||||
const blob = new Blob([csvString], { type: 'text/csv;charset=utf-8;' });
|
||||
const link = document.createElement('a');
|
||||
if (link.download !== undefined) {
|
||||
const url = URL.createObjectURL(blob);
|
||||
link.setAttribute('href', url);
|
||||
link.setAttribute('download', filename);
|
||||
link.style.visibility = 'hidden';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useMemo, useState, useEffect } from 'react';
|
||||
import { Link, useOutletContext } from 'react-router-dom';
|
||||
import { Search, ChevronRight, Filter, ChevronLeft } from 'lucide-react';
|
||||
import { Search, ChevronRight, Filter, ChevronLeft, Download } from 'lucide-react';
|
||||
import type { OrderData, DateRange } from '../types';
|
||||
import { parseOrderDate } from '../dataService';
|
||||
import { parseOrderDate, exportToCSV } from '../dataService';
|
||||
import DateRangePicker from '../components/DateRangePicker';
|
||||
|
||||
type SortOption = 'recent' | 'spent_desc' | 'spent_asc' | 'items_desc' | 'items_asc';
|
||||
@@ -124,6 +124,15 @@ const Clients = () => {
|
||||
className="w-full sm:w-64 bg-dark-card border border-dark-border text-dark-text rounded-xl pl-10 pr-4 py-2.5 focus:outline-none focus:border-brand-primary hover:border-brand-primary transition-colors shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => exportToCSV(clientsData, `clientes_${new Date().toISOString().split('T')[0]}.csv`)}
|
||||
className="flex items-center justify-center gap-2 bg-dark-card border border-dark-border px-4 py-2.5 rounded-xl shadow-sm hover:border-brand-primary transition-colors text-sm font-medium text-dark-text cursor-pointer"
|
||||
title="Exportar para CSV"
|
||||
>
|
||||
<Download size={16} className="text-brand-primary" />
|
||||
<span className="hidden sm:inline">Exportar</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useMemo, useState, useEffect } from 'react';
|
||||
import { Link, useOutletContext } from 'react-router-dom';
|
||||
import { Search, Package, TrendingUp, ChevronLeft, ChevronRight } from 'lucide-react';
|
||||
import { Search, Package, TrendingUp, ChevronLeft, ChevronRight, Download } from 'lucide-react';
|
||||
import DateRangePicker from '../components/DateRangePicker';
|
||||
import type { OrderData, DateRange } from '../types';
|
||||
import { parseOrderDate } from '../dataService';
|
||||
import { parseOrderDate, exportToCSV } from '../dataService';
|
||||
|
||||
const Products = () => {
|
||||
const { dateRange, setDateRange, ordersData } = useOutletContext<{
|
||||
@@ -89,6 +89,15 @@ const Products = () => {
|
||||
className="w-full md:w-64 bg-dark-card border border-dark-border text-dark-text rounded-xl pl-10 pr-4 py-2.5 focus:outline-none focus:border-brand-primary hover:border-brand-primary transition-colors shadow-sm"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => exportToCSV(productsData, `produtos_${new Date().toISOString().split('T')[0]}.csv`)}
|
||||
className="flex items-center justify-center gap-2 bg-dark-card border border-dark-border px-4 py-2.5 rounded-xl shadow-sm hover:border-brand-primary transition-colors text-sm font-medium text-dark-text cursor-pointer"
|
||||
title="Exportar para CSV"
|
||||
>
|
||||
<Download size={16} className="text-brand-primary" />
|
||||
<span className="hidden sm:inline">Exportar</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user