mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 19:31:12 -05:00
Approve database migration
This commit is contained in:
@@ -24,6 +24,9 @@ export default function DesignerDetail() {
|
|||||||
const [designer, setDesigner] = useState<Company | null>(null);
|
const [designer, setDesigner] = useState<Company | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||||
|
const [totalRides, setTotalRides] = useState<number>(0);
|
||||||
|
const [totalPhotos, setTotalPhotos] = useState<number>(0);
|
||||||
|
const [statsLoading, setStatsLoading] = useState(true);
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
|
|
||||||
@@ -44,6 +47,9 @@ export default function DesignerDetail() {
|
|||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
setDesigner(data);
|
setDesigner(data);
|
||||||
|
if (data) {
|
||||||
|
fetchStatistics(data.id);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching designer:', error);
|
console.error('Error fetching designer:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -51,6 +57,33 @@ export default function DesignerDetail() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchStatistics = async (designerId: string) => {
|
||||||
|
try {
|
||||||
|
// Count rides
|
||||||
|
const { count: ridesCount, error: ridesError } = await supabase
|
||||||
|
.from('rides')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('designer_id', designerId);
|
||||||
|
|
||||||
|
if (ridesError) throw ridesError;
|
||||||
|
setTotalRides(ridesCount || 0);
|
||||||
|
|
||||||
|
// Count photos
|
||||||
|
const { count: photosCount, error: photosError } = await supabase
|
||||||
|
.from('photos')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('entity_type', 'designer')
|
||||||
|
.eq('entity_id', designerId);
|
||||||
|
|
||||||
|
if (photosError) throw photosError;
|
||||||
|
setTotalPhotos(photosCount || 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(
|
||||||
@@ -229,8 +262,12 @@ export default function DesignerDetail() {
|
|||||||
<Tabs defaultValue="overview" className="w-full">
|
<Tabs defaultValue="overview" className="w-full">
|
||||||
<TabsList className="grid w-full grid-cols-2 md:grid-cols-4">
|
<TabsList className="grid w-full grid-cols-2 md:grid-cols-4">
|
||||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
<TabsTrigger value="rides">Rides</TabsTrigger>
|
<TabsTrigger value="rides">
|
||||||
<TabsTrigger value="photos">Photos</TabsTrigger>
|
Rides {!statsLoading && totalRides > 0 && `(${totalRides})`}
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="photos">
|
||||||
|
Photos {!statsLoading && totalPhotos > 0 && `(${totalPhotos})`}
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value="history">History</TabsTrigger>
|
<TabsTrigger value="history">History</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,10 @@ export default function ManufacturerDetail() {
|
|||||||
const [manufacturer, setManufacturer] = useState<Company | null>(null);
|
const [manufacturer, setManufacturer] = useState<Company | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||||
|
const [totalRides, setTotalRides] = useState<number>(0);
|
||||||
|
const [totalModels, setTotalModels] = useState<number>(0);
|
||||||
|
const [totalPhotos, setTotalPhotos] = useState<number>(0);
|
||||||
|
const [statsLoading, setStatsLoading] = useState(true);
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
|
|
||||||
@@ -44,6 +48,9 @@ export default function ManufacturerDetail() {
|
|||||||
|
|
||||||
if (error) throw error;
|
if (error) throw error;
|
||||||
setManufacturer(data);
|
setManufacturer(data);
|
||||||
|
if (data) {
|
||||||
|
fetchStatistics(data.id);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching manufacturer:', error);
|
console.error('Error fetching manufacturer:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -51,6 +58,42 @@ export default function ManufacturerDetail() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchStatistics = async (manufacturerId: string) => {
|
||||||
|
try {
|
||||||
|
// Count rides
|
||||||
|
const { count: ridesCount, error: ridesError } = await supabase
|
||||||
|
.from('rides')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('manufacturer_id', manufacturerId);
|
||||||
|
|
||||||
|
if (ridesError) throw ridesError;
|
||||||
|
setTotalRides(ridesCount || 0);
|
||||||
|
|
||||||
|
// Count models
|
||||||
|
const { count: modelsCount, error: modelsError } = await supabase
|
||||||
|
.from('ride_models')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('manufacturer_id', manufacturerId);
|
||||||
|
|
||||||
|
if (modelsError) throw modelsError;
|
||||||
|
setTotalModels(modelsCount || 0);
|
||||||
|
|
||||||
|
// Count photos
|
||||||
|
const { count: photosCount, error: photosError } = await supabase
|
||||||
|
.from('photos')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('entity_type', 'manufacturer')
|
||||||
|
.eq('entity_id', manufacturerId);
|
||||||
|
|
||||||
|
if (photosError) throw photosError;
|
||||||
|
setTotalPhotos(photosCount || 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(
|
||||||
@@ -231,9 +274,15 @@ export default function ManufacturerDetail() {
|
|||||||
<Tabs defaultValue="overview" className="w-full">
|
<Tabs defaultValue="overview" className="w-full">
|
||||||
<TabsList className="grid w-full grid-cols-2 md:grid-cols-5">
|
<TabsList className="grid w-full grid-cols-2 md:grid-cols-5">
|
||||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
<TabsTrigger value="rides">Rides</TabsTrigger>
|
<TabsTrigger value="rides">
|
||||||
<TabsTrigger value="models">Models</TabsTrigger>
|
Rides {!statsLoading && totalRides > 0 && `(${totalRides})`}
|
||||||
<TabsTrigger value="photos">Photos</TabsTrigger>
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="models">
|
||||||
|
Models {!statsLoading && totalModels > 0 && `(${totalModels})`}
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="photos">
|
||||||
|
Photos {!statsLoading && totalPhotos > 0 && `(${totalPhotos})`}
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value="history">History</TabsTrigger>
|
<TabsTrigger value="history">History</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default function OperatorDetail() {
|
|||||||
const [totalParks, setTotalParks] = useState<number>(0);
|
const [totalParks, setTotalParks] = useState<number>(0);
|
||||||
const [operatingRides, setOperatingRides] = useState<number>(0);
|
const [operatingRides, setOperatingRides] = useState<number>(0);
|
||||||
const [statsLoading, setStatsLoading] = useState(true);
|
const [statsLoading, setStatsLoading] = useState(true);
|
||||||
|
const [totalPhotos, setTotalPhotos] = useState<number>(0);
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
|
|
||||||
@@ -39,6 +40,57 @@ export default function OperatorDetail() {
|
|||||||
}
|
}
|
||||||
}, [slug]);
|
}, [slug]);
|
||||||
|
|
||||||
|
// Real-time subscription for parks, rides, and photos changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!operator?.id) return;
|
||||||
|
|
||||||
|
const channel = supabase
|
||||||
|
.channel('operator-stats-changes')
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: '*',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'parks',
|
||||||
|
filter: `operator_id=eq.${operator.id}`
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Park change detected for operator:', payload);
|
||||||
|
fetchStatistics(operator.id);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'UPDATE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'rides'
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Ride status change detected:', payload);
|
||||||
|
fetchStatistics(operator.id);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: '*',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'photos',
|
||||||
|
filter: `entity_type=eq.operator,entity_id=eq.${operator.id}`
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Photo change detected for operator:', payload);
|
||||||
|
fetchPhotoCount(operator.id);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
supabase.removeChannel(channel);
|
||||||
|
};
|
||||||
|
}, [operator?.id]);
|
||||||
|
|
||||||
const fetchOperatorData = async () => {
|
const fetchOperatorData = async () => {
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -55,6 +107,7 @@ export default function OperatorDetail() {
|
|||||||
if (data) {
|
if (data) {
|
||||||
fetchParks(data.id);
|
fetchParks(data.id);
|
||||||
fetchStatistics(data.id);
|
fetchStatistics(data.id);
|
||||||
|
fetchPhotoCount(data.id);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching operator:', error);
|
console.error('Error fetching operator:', error);
|
||||||
@@ -111,6 +164,22 @@ export default function OperatorDetail() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchPhotoCount = async (operatorId: string) => {
|
||||||
|
try {
|
||||||
|
const { count, error } = await supabase
|
||||||
|
.from('photos')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('entity_type', 'operator')
|
||||||
|
.eq('entity_id', operatorId);
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
setTotalPhotos(count || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching photo count:', error);
|
||||||
|
setTotalPhotos(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleEditSubmit = async (data: any) => {
|
const handleEditSubmit = async (data: any) => {
|
||||||
try {
|
try {
|
||||||
await submitCompanyUpdate(
|
await submitCompanyUpdate(
|
||||||
@@ -314,8 +383,12 @@ export default function OperatorDetail() {
|
|||||||
<Tabs defaultValue="overview" className="w-full">
|
<Tabs defaultValue="overview" className="w-full">
|
||||||
<TabsList className="grid w-full grid-cols-2 md:grid-cols-4">
|
<TabsList className="grid w-full grid-cols-2 md:grid-cols-4">
|
||||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
<TabsTrigger value="parks">Parks</TabsTrigger>
|
<TabsTrigger value="parks">
|
||||||
<TabsTrigger value="photos">Photos</TabsTrigger>
|
Parks {!statsLoading && totalParks > 0 && `(${totalParks})`}
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="photos">
|
||||||
|
Photos {!statsLoading && totalPhotos > 0 && `(${totalPhotos})`}
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value="history">History</TabsTrigger>
|
<TabsTrigger value="history">History</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,8 @@ export default function ParkDetail() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [isAddRideModalOpen, setIsAddRideModalOpen] = useState(false);
|
const [isAddRideModalOpen, setIsAddRideModalOpen] = useState(false);
|
||||||
const [isEditParkModalOpen, setIsEditParkModalOpen] = useState(false);
|
const [isEditParkModalOpen, setIsEditParkModalOpen] = useState(false);
|
||||||
|
const [photoCount, setPhotoCount] = useState<number>(0);
|
||||||
|
const [statsLoading, setStatsLoading] = useState(true);
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (slug) {
|
if (slug) {
|
||||||
@@ -55,6 +57,7 @@ export default function ParkDetail() {
|
|||||||
`).eq('slug', slug).maybeSingle();
|
`).eq('slug', slug).maybeSingle();
|
||||||
if (parkData) {
|
if (parkData) {
|
||||||
setPark(parkData);
|
setPark(parkData);
|
||||||
|
fetchPhotoCount(parkData.id);
|
||||||
|
|
||||||
// Fetch park rides
|
// Fetch park rides
|
||||||
const {
|
const {
|
||||||
@@ -68,6 +71,24 @@ export default function ParkDetail() {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchPhotoCount = async (parkId: string) => {
|
||||||
|
try {
|
||||||
|
const { count, error } = await supabase
|
||||||
|
.from('photos')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('entity_type', 'park')
|
||||||
|
.eq('entity_id', parkId);
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
setPhotoCount(count || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching photo count:', error);
|
||||||
|
setPhotoCount(0);
|
||||||
|
} finally {
|
||||||
|
setStatsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'operating':
|
case 'operating':
|
||||||
@@ -361,7 +382,9 @@ export default function ParkDetail() {
|
|||||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
<TabsTrigger value="rides">Rides ({rides.length})</TabsTrigger>
|
<TabsTrigger value="rides">Rides ({rides.length})</TabsTrigger>
|
||||||
<TabsTrigger value="reviews">Reviews</TabsTrigger>
|
<TabsTrigger value="reviews">Reviews</TabsTrigger>
|
||||||
<TabsTrigger value="photos">Photos</TabsTrigger>
|
<TabsTrigger value="photos">
|
||||||
|
Photos {!statsLoading && photoCount > 0 && `(${photoCount})`}
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value="history">History</TabsTrigger>
|
<TabsTrigger value="history">History</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ export default function PropertyOwnerDetail() {
|
|||||||
const [totalParks, setTotalParks] = useState<number>(0);
|
const [totalParks, setTotalParks] = useState<number>(0);
|
||||||
const [operatingRides, setOperatingRides] = useState<number>(0);
|
const [operatingRides, setOperatingRides] = useState<number>(0);
|
||||||
const [statsLoading, setStatsLoading] = useState(true);
|
const [statsLoading, setStatsLoading] = useState(true);
|
||||||
|
const [totalPhotos, setTotalPhotos] = useState<number>(0);
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
|
|
||||||
@@ -39,6 +40,57 @@ export default function PropertyOwnerDetail() {
|
|||||||
}
|
}
|
||||||
}, [slug]);
|
}, [slug]);
|
||||||
|
|
||||||
|
// Real-time subscription for parks and photos changes
|
||||||
|
useEffect(() => {
|
||||||
|
if (!owner?.id) return;
|
||||||
|
|
||||||
|
const channel = supabase
|
||||||
|
.channel('owner-stats-changes')
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: '*',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'parks',
|
||||||
|
filter: `property_owner_id=eq.${owner.id}`
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Park change detected for owner:', payload);
|
||||||
|
fetchStatistics(owner.id);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: 'UPDATE',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'rides'
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Ride status change detected:', payload);
|
||||||
|
fetchStatistics(owner.id);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.on(
|
||||||
|
'postgres_changes',
|
||||||
|
{
|
||||||
|
event: '*',
|
||||||
|
schema: 'public',
|
||||||
|
table: 'photos',
|
||||||
|
filter: `entity_type=eq.property_owner,entity_id=eq.${owner.id}`
|
||||||
|
},
|
||||||
|
(payload) => {
|
||||||
|
console.log('Photo change detected for owner:', payload);
|
||||||
|
fetchPhotoCount(owner.id);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
supabase.removeChannel(channel);
|
||||||
|
};
|
||||||
|
}, [owner?.id]);
|
||||||
|
|
||||||
const fetchOwnerData = async () => {
|
const fetchOwnerData = async () => {
|
||||||
try {
|
try {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -55,6 +107,7 @@ export default function PropertyOwnerDetail() {
|
|||||||
if (data) {
|
if (data) {
|
||||||
fetchParks(data.id);
|
fetchParks(data.id);
|
||||||
fetchStatistics(data.id);
|
fetchStatistics(data.id);
|
||||||
|
fetchPhotoCount(data.id);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error fetching property owner:', error);
|
console.error('Error fetching property owner:', error);
|
||||||
@@ -111,6 +164,22 @@ export default function PropertyOwnerDetail() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchPhotoCount = async (ownerId: string) => {
|
||||||
|
try {
|
||||||
|
const { count, error } = await supabase
|
||||||
|
.from('photos')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('entity_type', 'property_owner')
|
||||||
|
.eq('entity_id', ownerId);
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
setTotalPhotos(count || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching photo count:', error);
|
||||||
|
setTotalPhotos(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleEditSubmit = async (data: any) => {
|
const handleEditSubmit = async (data: any) => {
|
||||||
try {
|
try {
|
||||||
await submitCompanyUpdate(
|
await submitCompanyUpdate(
|
||||||
@@ -314,8 +383,12 @@ export default function PropertyOwnerDetail() {
|
|||||||
<Tabs defaultValue="overview" className="w-full">
|
<Tabs defaultValue="overview" className="w-full">
|
||||||
<TabsList className="grid w-full grid-cols-2 md:grid-cols-4">
|
<TabsList className="grid w-full grid-cols-2 md:grid-cols-4">
|
||||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
<TabsTrigger value="parks">Parks</TabsTrigger>
|
<TabsTrigger value="parks">
|
||||||
<TabsTrigger value="photos">Photos</TabsTrigger>
|
Parks {!statsLoading && totalParks > 0 && `(${totalParks})`}
|
||||||
|
</TabsTrigger>
|
||||||
|
<TabsTrigger value="photos">
|
||||||
|
Photos {!statsLoading && totalPhotos > 0 && `(${totalPhotos})`}
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value="history">History</TabsTrigger>
|
<TabsTrigger value="history">History</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ export default function RideDetail() {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [activeTab, setActiveTab] = useState("overview");
|
const [activeTab, setActiveTab] = useState("overview");
|
||||||
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
|
||||||
|
const [photoCount, setPhotoCount] = useState<number>(0);
|
||||||
|
const [statsLoading, setStatsLoading] = useState(true);
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { isModerator } = useUserRole();
|
const { isModerator } = useUserRole();
|
||||||
|
|
||||||
@@ -88,6 +90,7 @@ export default function RideDetail() {
|
|||||||
if (rideData) {
|
if (rideData) {
|
||||||
// Store park_id for easier access
|
// Store park_id for easier access
|
||||||
(rideData as any).currentParkId = parkData.id;
|
(rideData as any).currentParkId = parkData.id;
|
||||||
|
fetchPhotoCount(rideData.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
setRide(rideData);
|
setRide(rideData);
|
||||||
@@ -99,6 +102,24 @@ export default function RideDetail() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchPhotoCount = async (rideId: string) => {
|
||||||
|
try {
|
||||||
|
const { count, error } = await supabase
|
||||||
|
.from('photos')
|
||||||
|
.select('id', { count: 'exact', head: true })
|
||||||
|
.eq('entity_type', 'ride')
|
||||||
|
.eq('entity_id', rideId);
|
||||||
|
|
||||||
|
if (error) throw error;
|
||||||
|
setPhotoCount(count || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching photo count:', error);
|
||||||
|
setPhotoCount(0);
|
||||||
|
} finally {
|
||||||
|
setStatsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const getStatusColor = (status: string) => {
|
const getStatusColor = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'operating': return 'bg-green-500/20 text-green-400 border-green-500/30';
|
case 'operating': return 'bg-green-500/20 text-green-400 border-green-500/30';
|
||||||
@@ -387,7 +408,9 @@ export default function RideDetail() {
|
|||||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||||
<TabsTrigger value="specs">Specifications</TabsTrigger>
|
<TabsTrigger value="specs">Specifications</TabsTrigger>
|
||||||
<TabsTrigger value="reviews">Reviews</TabsTrigger>
|
<TabsTrigger value="reviews">Reviews</TabsTrigger>
|
||||||
<TabsTrigger value="photos">Photos</TabsTrigger>
|
<TabsTrigger value="photos">
|
||||||
|
Photos {!statsLoading && photoCount > 0 && `(${photoCount})`}
|
||||||
|
</TabsTrigger>
|
||||||
<TabsTrigger value="history">History</TabsTrigger>
|
<TabsTrigger value="history">History</TabsTrigger>
|
||||||
</TabsList>
|
</TabsList>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
-- Enable replica identity for real-time change tracking
|
||||||
|
ALTER TABLE parks REPLICA IDENTITY FULL;
|
||||||
|
ALTER TABLE rides REPLICA IDENTITY FULL;
|
||||||
|
ALTER TABLE photos REPLICA IDENTITY FULL;
|
||||||
Reference in New Issue
Block a user