mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-23 04:51:13 -05:00
feat: Add park and operating ride counts
This commit is contained in:
@@ -6,7 +6,7 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
||||||
import { ArrowLeft, MapPin, Star, Globe, Calendar, Edit, FerrisWheel } from 'lucide-react';
|
import { ArrowLeft, MapPin, Star, Globe, Calendar, Edit, FerrisWheel, Gauge } from 'lucide-react';
|
||||||
import { Company } from '@/types/database';
|
import { Company } from '@/types/database';
|
||||||
import { supabase } from '@/integrations/supabase/client';
|
import { supabase } from '@/integrations/supabase/client';
|
||||||
import { OperatorForm } from '@/components/admin/OperatorForm';
|
import { OperatorForm } from '@/components/admin/OperatorForm';
|
||||||
@@ -27,6 +27,9 @@ export default function OperatorDetail() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [parksLoading, setParksLoading] = useState(true);
|
const [parksLoading, setParksLoading] = useState(true);
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||||
|
const [totalParks, setTotalParks] = useState<number>(0);
|
||||||
|
const [operatingRides, setOperatingRides] = useState<number>(0);
|
||||||
|
const [statsLoading, setStatsLoading] = useState(true);
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
|
|
||||||
@@ -51,6 +54,7 @@ export default function OperatorDetail() {
|
|||||||
// Fetch parks operated by this operator
|
// Fetch parks operated by this operator
|
||||||
if (data) {
|
if (data) {
|
||||||
fetchParks(data.id);
|
fetchParks(data.id);
|
||||||
|
fetchStatistics(data.id);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching operator:', error);
|
console.error('Error fetching operator:', error);
|
||||||
@@ -80,6 +84,33 @@ export default function OperatorDetail() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchStatistics = async (operatorId: string) => {
|
||||||
|
try {
|
||||||
|
// Get total parks count
|
||||||
|
const { count: parksCount, error: parksError } = await supabase
|
||||||
|
.from('parks')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('operator_id', operatorId);
|
||||||
|
|
||||||
|
if (parksError) throw parksError;
|
||||||
|
setTotalParks(parksCount || 0);
|
||||||
|
|
||||||
|
// Get operating rides count across all parks
|
||||||
|
const { data: ridesData, error: ridesError } = await supabase
|
||||||
|
.from('rides')
|
||||||
|
.select('id, parks!inner(operator_id)')
|
||||||
|
.eq('parks.operator_id', operatorId)
|
||||||
|
.eq('status', 'operating');
|
||||||
|
|
||||||
|
if (ridesError) throw ridesError;
|
||||||
|
setOperatingRides(ridesData?.length || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching statistics:', error);
|
||||||
|
} finally {
|
||||||
|
setStatsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleEditSubmit = async (data: any) => {
|
const handleEditSubmit = async (data: any) => {
|
||||||
try {
|
try {
|
||||||
await submitCompanyUpdate(
|
await submitCompanyUpdate(
|
||||||
@@ -227,7 +258,31 @@ export default function OperatorDetail() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Company Info */}
|
{/* Company Info */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mb-8 max-w-4xl mx-auto">
|
<div className="flex flex-wrap justify-center gap-4 mb-8">
|
||||||
|
{!statsLoading && totalParks > 0 && (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-4 text-center">
|
||||||
|
<FerrisWheel className="w-6 h-6 text-primary mx-auto mb-2" />
|
||||||
|
<div className="text-2xl font-bold">{totalParks}</div>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
{totalParks === 1 ? 'Park Operated' : 'Parks Operated'}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!statsLoading && operatingRides > 0 && (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-4 text-center">
|
||||||
|
<Gauge className="w-6 h-6 text-accent mx-auto mb-2" />
|
||||||
|
<div className="text-2xl font-bold">{operatingRides}</div>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
Operating {operatingRides === 1 ? 'Ride' : 'Rides'}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{operator.founded_year && (
|
{operator.founded_year && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-4 text-center">
|
<CardContent className="p-4 text-center">
|
||||||
@@ -237,6 +292,7 @@ export default function OperatorDetail() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{operator.website_url && (
|
{operator.website_url && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-4 text-center">
|
<CardContent className="p-4 text-center">
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Badge } from '@/components/ui/badge';
|
|||||||
import { Card, CardContent } from '@/components/ui/card';
|
import { Card, CardContent } from '@/components/ui/card';
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
import { Dialog, DialogContent } from '@/components/ui/dialog';
|
||||||
import { ArrowLeft, MapPin, Star, Globe, Calendar, Edit, Building2 } from 'lucide-react';
|
import { ArrowLeft, MapPin, Star, Globe, Calendar, Edit, Building2, Gauge } from 'lucide-react';
|
||||||
import { Company } from '@/types/database';
|
import { Company } from '@/types/database';
|
||||||
import { supabase } from '@/integrations/supabase/client';
|
import { supabase } from '@/integrations/supabase/client';
|
||||||
import { PropertyOwnerForm } from '@/components/admin/PropertyOwnerForm';
|
import { PropertyOwnerForm } from '@/components/admin/PropertyOwnerForm';
|
||||||
@@ -27,6 +27,9 @@ export default function PropertyOwnerDetail() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [parksLoading, setParksLoading] = useState(true);
|
const [parksLoading, setParksLoading] = useState(true);
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||||
|
const [totalParks, setTotalParks] = useState<number>(0);
|
||||||
|
const [operatingRides, setOperatingRides] = useState<number>(0);
|
||||||
|
const [statsLoading, setStatsLoading] = useState(true);
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
|
|
||||||
@@ -51,6 +54,7 @@ export default function PropertyOwnerDetail() {
|
|||||||
// Fetch parks owned by this property owner
|
// Fetch parks owned by this property owner
|
||||||
if (data) {
|
if (data) {
|
||||||
fetchParks(data.id);
|
fetchParks(data.id);
|
||||||
|
fetchStatistics(data.id);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching property owner:', error);
|
console.error('Error fetching property owner:', error);
|
||||||
@@ -80,6 +84,33 @@ export default function PropertyOwnerDetail() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchStatistics = async (ownerId: string) => {
|
||||||
|
try {
|
||||||
|
// Get total parks count
|
||||||
|
const { count: parksCount, error: parksError } = await supabase
|
||||||
|
.from('parks')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('property_owner_id', ownerId);
|
||||||
|
|
||||||
|
if (parksError) throw parksError;
|
||||||
|
setTotalParks(parksCount || 0);
|
||||||
|
|
||||||
|
// Get operating rides count across all owned parks
|
||||||
|
const { data: ridesData, error: ridesError } = await supabase
|
||||||
|
.from('rides')
|
||||||
|
.select('id, parks!inner(property_owner_id)')
|
||||||
|
.eq('parks.property_owner_id', ownerId)
|
||||||
|
.eq('status', 'operating');
|
||||||
|
|
||||||
|
if (ridesError) throw ridesError;
|
||||||
|
setOperatingRides(ridesData?.length || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching statistics:', error);
|
||||||
|
} finally {
|
||||||
|
setStatsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleEditSubmit = async (data: any) => {
|
const handleEditSubmit = async (data: any) => {
|
||||||
try {
|
try {
|
||||||
await submitCompanyUpdate(
|
await submitCompanyUpdate(
|
||||||
@@ -227,7 +258,31 @@ export default function PropertyOwnerDetail() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Company Info */}
|
{/* Company Info */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 mb-8 max-w-4xl mx-auto">
|
<div className="flex flex-wrap justify-center gap-4 mb-8">
|
||||||
|
{!statsLoading && totalParks > 0 && (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-4 text-center">
|
||||||
|
<Building2 className="w-6 h-6 text-primary mx-auto mb-2" />
|
||||||
|
<div className="text-2xl font-bold">{totalParks}</div>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
{totalParks === 1 ? 'Park Owned' : 'Parks Owned'}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!statsLoading && operatingRides > 0 && (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="p-4 text-center">
|
||||||
|
<Gauge className="w-6 h-6 text-accent mx-auto mb-2" />
|
||||||
|
<div className="text-2xl font-bold">{operatingRides}</div>
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
Operating {operatingRides === 1 ? 'Ride' : 'Rides'}
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
{owner.founded_year && (
|
{owner.founded_year && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-4 text-center">
|
<CardContent className="p-4 text-center">
|
||||||
@@ -237,6 +292,7 @@ export default function PropertyOwnerDetail() {
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{owner.website_url && (
|
{owner.website_url && (
|
||||||
<Card>
|
<Card>
|
||||||
<CardContent className="p-4 text-center">
|
<CardContent className="p-4 text-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user