import React, { useState } from 'react'; import { toast } from 'sonner'; import { Database, Download, FileSpreadsheet, FileJson, AlertTriangle, CheckCircle2, XCircle, Loader2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { cn } from '@/lib/utils'; import { SectionCard } from './dataShared'; // Shared blob download: works whether or not the response sets Content-Disposition // (the JSON payments export doesn't), falling back to the given name. async function downloadFile(endpoint, fallbackName) { const res = await fetch(endpoint, { credentials: 'include' }); if (!res.ok) { let data = {}; try { data = await res.json(); } catch {} throw new Error(data.message || data.error || `HTTP ${res.status}`); } const disposition = res.headers.get('Content-Disposition'); const match = disposition?.match(/filename="?([^"]+)"?/i); const name = match ? match[1] : fallbackName; const blob = await res.blob(); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = name; a.click(); URL.revokeObjectURL(url); } function ExportCard({ icon: Icon, title, description, filename, endpoint }) { const [loading, setLoading] = useState(false); const handleDownload = async () => { setLoading(true); try { await downloadFile(endpoint, filename); toast.success(`${title} downloaded.`); } catch (err) { toast.error(err.message || 'Download failed.'); } finally { setLoading(false); } }; return (
{title}
{description}
Payments export
Just your payment records — filter by date and choose a format. Leave dates empty for the current year.
Exports may contain sensitive account metadata (website URLs, usernames, account info). Store exported files securely and avoid sharing them unencrypted.
What's included
What's not included