mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 09:51:13 -05:00
Connect to Park Location Backfill UI
This commit is contained in:
100
src/components/admin/ParkLocationBackfill.tsx
Normal file
100
src/components/admin/ParkLocationBackfill.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import { useState } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||
import { supabase } from '@/lib/supabaseClient';
|
||||
import { MapPin, AlertCircle, CheckCircle2 } from 'lucide-react';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
export function ParkLocationBackfill() {
|
||||
const [isRunning, setIsRunning] = useState(false);
|
||||
const [result, setResult] = useState<{
|
||||
success: boolean;
|
||||
parks_updated: number;
|
||||
locations_created: number;
|
||||
} | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const { toast } = useToast();
|
||||
|
||||
const handleBackfill = async () => {
|
||||
setIsRunning(true);
|
||||
setError(null);
|
||||
setResult(null);
|
||||
|
||||
try {
|
||||
const { data, error: invokeError } = await supabase.functions.invoke(
|
||||
'backfill-park-locations'
|
||||
);
|
||||
|
||||
if (invokeError) throw invokeError;
|
||||
|
||||
setResult(data);
|
||||
toast({
|
||||
title: 'Backfill Complete',
|
||||
description: `Updated ${data.parks_updated} parks with ${data.locations_created} new locations`,
|
||||
});
|
||||
} catch (err: any) {
|
||||
const errorMessage = err.message || 'Failed to run backfill';
|
||||
setError(errorMessage);
|
||||
toast({
|
||||
title: 'Backfill Failed',
|
||||
description: errorMessage,
|
||||
variant: 'destructive',
|
||||
});
|
||||
} finally {
|
||||
setIsRunning(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<MapPin className="w-5 h-5" />
|
||||
Park Location Backfill
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Backfill missing location data for approved parks from their submission data
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<Alert>
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
This tool will find parks without location data and populate them using the location information from their approved submissions. This is useful for fixing parks that were approved before the location creation fix was implemented.
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
{result && (
|
||||
<Alert className="border-green-200 bg-green-50 dark:bg-green-950 dark:border-green-800">
|
||||
<CheckCircle2 className="h-4 w-4 text-green-600 dark:text-green-400" />
|
||||
<AlertDescription className="text-green-900 dark:text-green-100">
|
||||
<div className="font-medium">Backfill completed successfully!</div>
|
||||
<div className="mt-2 space-y-1">
|
||||
<div>Parks updated: {result.parks_updated}</div>
|
||||
<div>Locations created: {result.locations_created}</div>
|
||||
</div>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{error && (
|
||||
<Alert variant="destructive">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Button
|
||||
onClick={handleBackfill}
|
||||
disabled={isRunning}
|
||||
className="w-full"
|
||||
trackingLabel="run-park-location-backfill"
|
||||
>
|
||||
<MapPin className="w-4 h-4 mr-2" />
|
||||
{isRunning ? 'Running Backfill...' : 'Run Location Backfill'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { useAdminSettings } from '@/hooks/useAdminSettings';
|
||||
import { NovuMigrationUtility } from '@/components/admin/NovuMigrationUtility';
|
||||
import { TestDataGenerator } from '@/components/admin/TestDataGenerator';
|
||||
import { IntegrationTestRunner } from '@/components/admin/IntegrationTestRunner';
|
||||
import { ParkLocationBackfill } from '@/components/admin/ParkLocationBackfill';
|
||||
import { Loader2, Save, Clock, Users, Bell, Shield, Settings, Trash2, Plug, AlertTriangle, Lock, TestTube, RefreshCw, Info, AlertCircle, Database } from 'lucide-react';
|
||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||
|
||||
@@ -935,6 +936,8 @@ export default function AdminSettings() {
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<ParkLocationBackfill />
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
-- Fix search_path for backfill_park_locations function
|
||||
CREATE OR REPLACE FUNCTION backfill_park_locations()
|
||||
RETURNS jsonb
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path = public
|
||||
AS $$
|
||||
DECLARE
|
||||
v_parks_updated INTEGER := 0;
|
||||
v_locations_created INTEGER := 0;
|
||||
v_park RECORD;
|
||||
v_submission RECORD;
|
||||
v_location_id UUID;
|
||||
BEGIN
|
||||
-- Find parks without locations that have approved submissions with location data
|
||||
FOR v_park IN
|
||||
SELECT DISTINCT p.id, p.name, p.slug
|
||||
FROM parks p
|
||||
WHERE p.location_id IS NULL
|
||||
LOOP
|
||||
-- Find the most recent approved submission for this park with location data
|
||||
SELECT
|
||||
psl.country,
|
||||
psl.state_province,
|
||||
psl.city,
|
||||
psl.street_address,
|
||||
psl.postal_code,
|
||||
psl.latitude,
|
||||
psl.longitude
|
||||
INTO v_submission
|
||||
FROM park_submissions ps
|
||||
JOIN park_submission_locations psl ON ps.id = psl.park_submission_id
|
||||
WHERE ps.park_id = v_park.id
|
||||
AND ps.status = 'approved'
|
||||
AND psl.country IS NOT NULL
|
||||
ORDER BY ps.created_at DESC
|
||||
LIMIT 1;
|
||||
|
||||
-- If we found location data, create a location record
|
||||
IF FOUND THEN
|
||||
INSERT INTO locations (
|
||||
country,
|
||||
state_province,
|
||||
city,
|
||||
street_address,
|
||||
postal_code,
|
||||
latitude,
|
||||
longitude
|
||||
) VALUES (
|
||||
v_submission.country,
|
||||
v_submission.state_province,
|
||||
v_submission.city,
|
||||
v_submission.street_address,
|
||||
v_submission.postal_code,
|
||||
v_submission.latitude,
|
||||
v_submission.longitude
|
||||
)
|
||||
RETURNING id INTO v_location_id;
|
||||
|
||||
-- Update the park with the new location
|
||||
UPDATE parks
|
||||
SET location_id = v_location_id
|
||||
WHERE id = v_park.id;
|
||||
|
||||
v_parks_updated := v_parks_updated + 1;
|
||||
v_locations_created := v_locations_created + 1;
|
||||
|
||||
RAISE NOTICE 'Backfilled location for park: % (id: %)', v_park.name, v_park.id;
|
||||
END IF;
|
||||
END LOOP;
|
||||
|
||||
RETURN jsonb_build_object(
|
||||
'success', true,
|
||||
'parks_updated', v_parks_updated,
|
||||
'locations_created', v_locations_created
|
||||
);
|
||||
END;
|
||||
$$;
|
||||
Reference in New Issue
Block a user