mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 17:31:14 -05:00
Implement Test Data Generator
This commit is contained in:
258
src/components/admin/TestDataGenerator.tsx
Normal file
258
src/components/admin/TestDataGenerator.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user