121 lines
5.1 KiB
React
121 lines
5.1 KiB
React
|
|
import React, { useState, useEffect } from 'react';
|
||
|
|
import { toast } from 'sonner';
|
||
|
|
import { Loader2 } from 'lucide-react';
|
||
|
|
import { api } from '@/api';
|
||
|
|
import { Button } from '@/components/ui/button';
|
||
|
|
import {
|
||
|
|
AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
|
||
|
|
AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle,
|
||
|
|
AlertDialogTrigger,
|
||
|
|
} from '@/components/ui/alert-dialog';
|
||
|
|
import { SectionCard } from './dataShared';
|
||
|
|
|
||
|
|
export default function SeedDemoDataSection({ onSeeded }) {
|
||
|
|
const [loading, setLoading] = useState(false);
|
||
|
|
const [seeded, setSeeded] = useState(false);
|
||
|
|
const [counts, setCounts] = useState({ bills: 0, categories: 0 });
|
||
|
|
const [clearing, setClearing] = useState(false);
|
||
|
|
const [showClearConfirm, setShowClearConfirm] = useState(false);
|
||
|
|
const [statusLoading, setStatusLoading] = useState(true);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
api.seededStatus()
|
||
|
|
.then(data => {
|
||
|
|
setSeeded(data.seeded);
|
||
|
|
if (data.seeded) setCounts({ bills: data.seededBills || 0, categories: data.seededCategories || 0 });
|
||
|
|
})
|
||
|
|
.catch(err => console.error('Failed to check seeded status:', err))
|
||
|
|
.finally(() => setStatusLoading(false));
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
const handleSeed = async () => {
|
||
|
|
setLoading(true);
|
||
|
|
try {
|
||
|
|
const data = await api.seedDemoData();
|
||
|
|
if (!data || typeof data !== 'object') throw new Error('Invalid response from server');
|
||
|
|
setCounts({ bills: data.billsCreated || 0, categories: data.categoriesCreated || 0 });
|
||
|
|
setSeeded(true);
|
||
|
|
toast.success(`Created ${data.billsCreated || 0} demo bills successfully.`);
|
||
|
|
setTimeout(() => onSeeded?.(), 100);
|
||
|
|
} catch (err) {
|
||
|
|
console.error('Seed error:', err);
|
||
|
|
toast.error(err?.message || err?.error || 'Failed to seed demo data.');
|
||
|
|
} finally {
|
||
|
|
setLoading(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
const handleClearDemoData = async () => {
|
||
|
|
setClearing(true);
|
||
|
|
try {
|
||
|
|
const data = await api.clearDemoData();
|
||
|
|
setSeeded(false);
|
||
|
|
setCounts({ bills: 0, categories: 0 });
|
||
|
|
setShowClearConfirm(false);
|
||
|
|
toast.success(`Removed ${data.billsDeleted || 0} demo bills and ${data.categoriesDeleted || 0} demo categories.`);
|
||
|
|
onSeeded?.();
|
||
|
|
} catch (err) {
|
||
|
|
toast.error(err.message || 'Failed to clear demo data.');
|
||
|
|
} finally {
|
||
|
|
setClearing(false);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
return (
|
||
|
|
<SectionCard title="Demo Data" subtitle="Seed your database with demo data for testing" icon={Sparkles}>
|
||
|
|
<div className="rounded-lg border border-border/60 bg-background/50 p-4">
|
||
|
|
{statusLoading ? (
|
||
|
|
<p className="text-sm text-muted-foreground">Loading…</p>
|
||
|
|
) : seeded ? (
|
||
|
|
<>
|
||
|
|
<p className="text-sm font-medium text-emerald-600 dark:text-emerald-400">Demo data seeded</p>
|
||
|
|
<div className="mt-3 grid grid-cols-2 gap-4 text-xs sm:grid-cols-4">
|
||
|
|
<div>
|
||
|
|
<p className="text-muted-foreground">Bills</p>
|
||
|
|
<p className="font-semibold">{counts.bills}</p>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<p className="text-muted-foreground">Categories</p>
|
||
|
|
<p className="font-semibold">{counts.categories}</p>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</>
|
||
|
|
) : (
|
||
|
|
<p className="text-sm text-muted-foreground">
|
||
|
|
Create 20 realistic demo bills and 8 demo categories for testing purposes.
|
||
|
|
The data will be associated with your account.
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
|
||
|
|
<div className="mt-4 flex items-center justify-between gap-3 border-t border-border pt-4">
|
||
|
|
<Button size="sm" variant="outline" onClick={handleSeed} disabled={loading || seeded || statusLoading}>
|
||
|
|
{loading ? <><Loader2 className="h-3.5 w-3.5 mr-1.5 animate-spin" />Seeding…</> : 'Seed Demo Data'}
|
||
|
|
</Button>
|
||
|
|
|
||
|
|
<AlertDialog open={showClearConfirm} onOpenChange={setShowClearConfirm}>
|
||
|
|
<AlertDialogTrigger asChild>
|
||
|
|
<Button size="sm" variant="destructive" disabled={!seeded || clearing || statusLoading}>
|
||
|
|
{clearing ? <><Loader2 className="h-3.5 w-3.5 mr-1.5 animate-spin" />Clearing…</> : 'Clear Demo Data'}
|
||
|
|
</Button>
|
||
|
|
</AlertDialogTrigger>
|
||
|
|
<AlertDialogContent>
|
||
|
|
<AlertDialogHeader>
|
||
|
|
<AlertDialogTitle>Clear Demo Data</AlertDialogTitle>
|
||
|
|
<AlertDialogDescription>
|
||
|
|
This will remove {counts.bills} demo bills and {counts.categories} demo categories from your account. This cannot be undone.
|
||
|
|
</AlertDialogDescription>
|
||
|
|
</AlertDialogHeader>
|
||
|
|
<AlertDialogFooter>
|
||
|
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||
|
|
<AlertDialogAction onClick={handleClearDemoData} className="bg-destructive text-destructive-foreground hover:bg-destructive/90">
|
||
|
|
{clearing ? <><Loader2 className="h-3.5 w-3.5 mr-1.5 animate-spin" />Clearing…</> : 'Clear Data'}
|
||
|
|
</AlertDialogAction>
|
||
|
|
</AlertDialogFooter>
|
||
|
|
</AlertDialogContent>
|
||
|
|
</AlertDialog>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</SectionCard>
|
||
|
|
);
|
||
|
|
}
|