Implement Test Data Generator

This commit is contained in:
gpt-engineer-app[bot]
2025-10-06 18:48:24 +00:00
parent f5c59aa072
commit 2f71312f7c
6 changed files with 1066 additions and 1 deletions

View File

@@ -0,0 +1,258 @@
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Checkbox } from '@/components/ui/checkbox';
import { Label } from '@/components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Progress } from '@/components/ui/progress';
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog';
import { supabase } from '@/integrations/supabase/client';
import { useToast } from '@/hooks/use-toast';
import { Beaker, CheckCircle, XCircle, ChevronDown, Trash2, AlertTriangle } from 'lucide-react';
import { clearTestData, getTestDataStats } from '@/lib/testDataGenerator';
const PRESETS = {
small: { label: 'Small', description: '~20 submissions - Quick test', counts: '5 parks, 10 rides, 3 companies' },
medium: { label: 'Medium', description: '~100 submissions - Standard testing', counts: '20 parks, 50 rides, 20 companies' },
large: { label: 'Large', description: '~500 submissions - Performance testing', counts: '100 parks, 250 rides, 100 companies' },
stress: { label: 'Stress', description: '~2000 submissions - Load testing', counts: '400 parks, 1000 rides, 400 companies' }
};
export function TestDataGenerator() {
const { toast } = useToast();
const [preset, setPreset] = useState<'small' | 'medium' | 'large' | 'stress'>('small');
const [entityTypes, setEntityTypes] = useState({
parks: true,
rides: true,
manufacturers: true,
operators: true,
property_owners: true,
designers: true,
ride_models: true
});
const [options, setOptions] = useState({
includeDependencies: true,
includeConflicts: false,
includeVersionChains: false,
includeEscalated: false,
includeExpiredLocks: false
});
const [loading, setLoading] = useState(false);
const [results, setResults] = useState<any>(null);
const [stats, setStats] = useState<{ total: number; pending: number; approved: number } | null>(null);
const selectedEntityTypes = Object.entries(entityTypes)
.filter(([_, enabled]) => enabled)
.map(([type]) => type);
const loadStats = async () => {
try {
const data = await getTestDataStats();
setStats(data);
} catch (error) {
console.error('Failed to load stats:', error);
}
};
const handleGenerate = async () => {
setLoading(true);
setResults(null);
try {
const { data, error } = await supabase.functions.invoke('seed-test-data', {
body: {
preset,
entityTypes: selectedEntityTypes,
...options
}
});
if (error) throw error;
setResults(data);
await loadStats();
toast({
title: 'Test Data Generated',
description: `Successfully created ${Object.values(data.summary).reduce((a: number, b: number) => a + b, 0)} submissions in ${data.time}s`
});
} catch (error) {
console.error('Generation error:', error);
toast({
title: 'Generation Failed',
description: error.message,
variant: 'destructive'
});
} finally {
setLoading(false);
}
};
const handleClear = async () => {
setLoading(true);
try {
const { deleted } = await clearTestData();
await loadStats();
toast({
title: 'Test Data Cleared',
description: `Removed ${deleted} test submissions`
});
setResults(null);
} catch (error) {
console.error('Clear error:', error);
toast({
title: 'Clear Failed',
description: error.message,
variant: 'destructive'
});
} finally {
setLoading(false);
}
};
return (
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Beaker className="w-5 h-5" />
<CardTitle>Test Data Generator</CardTitle>
</div>
<CardDescription>
Generate realistic test submissions for testing the moderation queue and versioning systems
</CardDescription>
</CardHeader>
<CardContent className="space-y-6">
<Alert variant="destructive">
<AlertTriangle className="h-4 w-4" />
<AlertDescription>
This will create test data in your database. All test submissions are marked and can be cleared later.
</AlertDescription>
</Alert>
{stats && (
<div className="flex gap-4 text-sm text-muted-foreground">
<span>Total Test Data: {stats.total}</span>
<span>Pending: {stats.pending}</span>
<span>Approved: {stats.approved}</span>
</div>
)}
<div className="space-y-4">
<div>
<Label className="text-base font-semibold">Preset</Label>
<RadioGroup value={preset} onValueChange={(v: any) => setPreset(v)} className="mt-2 space-y-3">
{Object.entries(PRESETS).map(([key, { label, description, counts }]) => (
<div key={key} className="flex items-start space-x-2">
<RadioGroupItem value={key} id={key} className="mt-1" />
<div className="flex-1">
<Label htmlFor={key} className="font-medium cursor-pointer">{label}</Label>
<p className="text-sm text-muted-foreground">{description}</p>
<p className="text-xs text-muted-foreground">{counts}</p>
</div>
</div>
))}
</RadioGroup>
</div>
<div>
<Label className="text-base font-semibold">Entity Types</Label>
<div className="mt-2 grid grid-cols-2 gap-3">
{Object.entries(entityTypes).map(([key, enabled]) => (
<div key={key} className="flex items-center space-x-2">
<Checkbox
id={key}
checked={enabled}
onCheckedChange={(checked) =>
setEntityTypes({ ...entityTypes, [key]: !!checked })
}
/>
<Label htmlFor={key} className="cursor-pointer capitalize">
{key.replace('_', ' ')}
</Label>
</div>
))}
</div>
</div>
<Collapsible>
<CollapsibleTrigger className="flex items-center gap-2 text-sm font-medium">
<ChevronDown className="w-4 h-4" />
Advanced Options
</CollapsibleTrigger>
<CollapsibleContent className="mt-3 space-y-3">
{Object.entries(options).map(([key, enabled]) => (
<div key={key} className="flex items-center space-x-2">
<Checkbox
id={key}
checked={enabled}
onCheckedChange={(checked) =>
setOptions({ ...options, [key]: !!checked })
}
/>
<Label htmlFor={key} className="cursor-pointer capitalize">
{key.replace(/([A-Z])/g, ' $1').toLowerCase()}
</Label>
</div>
))}
</CollapsibleContent>
</Collapsible>
</div>
{loading && (
<div className="space-y-2">
<Progress value={undefined} />
<p className="text-sm text-center text-muted-foreground">Generating test data...</p>
</div>
)}
{results && (
<Alert>
<CheckCircle className="h-4 w-4 text-green-500" />
<AlertDescription>
<div className="font-medium mb-2">Test Data Generated Successfully</div>
<ul className="text-sm space-y-1">
<li> Created {results.summary.parks} park submissions</li>
<li> Created {results.summary.rides} ride submissions</li>
<li> Created {results.summary.companies} company submissions</li>
<li> Created {results.summary.rideModels} ride model submissions</li>
<li className="font-medium mt-2">Time taken: {results.time}s</li>
</ul>
</AlertDescription>
</Alert>
)}
<div className="flex gap-3">
<Button onClick={handleGenerate} disabled={loading || selectedEntityTypes.length === 0}>
<Beaker className="w-4 h-4 mr-2" />
Generate Test Data
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" disabled={loading}>
<Trash2 className="w-4 h-4 mr-2" />
Clear All Test Data
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Clear All Test Data?</AlertDialogTitle>
<AlertDialogDescription>
This will permanently delete all test submissions marked with is_test_data: true. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={handleClear}>Clear Test Data</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
</CardContent>
</Card>
);
}