refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
|
|
|
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';
|
|
|
|
|
|
2026-05-30 21:52:02 -05:00
|
|
|
export default function SeedDemoDataSection({ onSeeded, cardProps = {} }) {
|
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
|
|
|
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 (
|
2026-05-30 21:52:02 -05:00
|
|
|
<SectionCard title="Demo Data" subtitle="Seed your database with demo data for testing" {...cardProps}>
|
refactor: component splits, PWA support, CommandPalette
Component Splits:
- AdminPage.jsx: 1,906 -> 82 lines (logic moved to client/components/admin/ — 9 files)
- DataPage.jsx: 3,132 -> 60 lines (logic moved to client/components/data/ — 8 files)
- TrackerPage.jsx: 2,566 -> 2,132 lines (MonthlyStateDialog, StartingAmountsEditDialog, PaymentModal)
PWA:
- vite-plugin-pwa installed with NetworkFirst caching for API routes
- Square PWA icons (192x192, 512x512, apple-touch-icon)
- theme-color, apple meta tags, touch icon in index.html
- Build generates dist/sw.js + Workbox runtime
CommandPalette:
- Navigation commands, Add bill action, month jumps
- Grouped results with empty/filtered states
2026-05-28 20:53:22 -05:00
|
|
|
<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>
|
|
|
|
|
);
|
|
|
|
|
}
|