Compare commits

..

3 Commits

Author SHA1 Message Date
gpt-engineer-app[bot]
07420a67bf Fix: Handle internal errors 2025-11-03 02:41:01 +00:00
gpt-engineer-app[bot]
9e8e8719b4 Fix: Resolve TypeScript errors 2025-11-03 02:28:57 +00:00
gpt-engineer-app[bot]
f6c409fac4 Fix: Resolve internal server error 2025-11-03 02:26:28 +00:00
31 changed files with 107 additions and 83 deletions

View File

@@ -62,7 +62,7 @@ export function ParkCard({ park }: ParkCardProps) {
<div className="aspect-[3/2] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 flex items-center justify-center relative"> <div className="aspect-[3/2] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 flex items-center justify-center relative">
{(park.card_image_url || park.card_image_id) ? ( {(park.card_image_url || park.card_image_id) ? (
<img <img
src={park.card_image_url || getCloudflareImageUrl(park.card_image_id, 'card')} src={park.card_image_url || getCloudflareImageUrl(park.card_image_id || '', 'card')}
srcSet={park.card_image_id ? ` srcSet={park.card_image_id ? `
${getCloudflareImageUrl(park.card_image_id, 'cardthumb')} 600w, ${getCloudflareImageUrl(park.card_image_id, 'cardthumb')} 600w,
${getCloudflareImageUrl(park.card_image_id, 'card')} 1200w ${getCloudflareImageUrl(park.card_image_id, 'card')} 1200w
@@ -111,7 +111,7 @@ export function ParkCard({ park }: ParkCardProps) {
{/* Stats & Rating */} {/* Stats & Rating */}
<div className="flex items-center justify-between text-sm"> <div className="flex items-center justify-between text-sm">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{park.ride_count > 0 && ( {(park.ride_count != null && park.ride_count > 0) && (
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<FerrisWheel className="w-4 h-4 text-primary/70 flex-shrink-0" /> <FerrisWheel className="w-4 h-4 text-primary/70 flex-shrink-0" />
<span className="font-medium">{park.ride_count}</span> <span className="font-medium">{park.ride_count}</span>
@@ -120,11 +120,11 @@ export function ParkCard({ park }: ParkCardProps) {
)} )}
</div> </div>
{park.average_rating > 0 && ( {(park.average_rating != null && park.average_rating > 0) && (
<div className="inline-flex items-center gap-1"> <div className="inline-flex items-center gap-1">
<Star className="w-4 h-4 fill-yellow-500 text-yellow-500" /> <Star className="w-4 h-4 fill-yellow-500 text-yellow-500" />
<span className="font-semibold">{park.average_rating.toFixed(1)}</span> <span className="font-semibold">{park.average_rating.toFixed(1)}</span>
{park.review_count > 0 && ( {(park.review_count != null && park.review_count > 0) && (
<span className="text-muted-foreground">({park.review_count})</span> <span className="text-muted-foreground">({park.review_count})</span>
)} )}
</div> </div>

View File

@@ -65,12 +65,12 @@ export function ParkFilters({ filters, onFiltersChange, parks }: ParkFiltersProp
}, [locations]); }, [locations]);
const stateOptions: MultiSelectOption[] = useMemo(() => { const stateOptions: MultiSelectOption[] = useMemo(() => {
const states = new Set(locations?.map(l => l.state_province).filter(Boolean) || []); const states = new Set(locations?.map(l => l.state_province).filter((s): s is string => s != null) || []);
return Array.from(states).sort().map(s => ({ label: s, value: s })); return Array.from(states).sort().map(s => ({ label: s, value: s }));
}, [locations]); }, [locations]);
const cityOptions: MultiSelectOption[] = useMemo(() => { const cityOptions: MultiSelectOption[] = useMemo(() => {
const cities = new Set(locations?.map(l => l.city).filter(Boolean) || []); const cities = new Set(locations?.map(l => l.city).filter((c): c is string => c != null) || []);
return Array.from(cities).sort().map(c => ({ label: c, value: c })); return Array.from(cities).sort().map(c => ({ label: c, value: c }));
}, [locations]); }, [locations]);

View File

@@ -106,7 +106,7 @@ export function ParkListView({ parks, onParkClick }: ParkListViewProps) {
</div> </div>
{/* Rating */} {/* Rating */}
{park.average_rating > 0 && ( {(park.average_rating != null && park.average_rating > 0) && (
<div className="flex items-center gap-1.5 ml-4 flex-shrink-0 bg-yellow-400/10 px-3 py-1.5 rounded-full group-hover:bg-yellow-400/20 transition-colors duration-300"> <div className="flex items-center gap-1.5 ml-4 flex-shrink-0 bg-yellow-400/10 px-3 py-1.5 rounded-full group-hover:bg-yellow-400/20 transition-colors duration-300">
<Star className="w-4 h-4 fill-yellow-400 text-yellow-400" /> <Star className="w-4 h-4 fill-yellow-400 text-yellow-400" />
<span className="font-semibold text-foreground">{park.average_rating.toFixed(1)}</span> <span className="font-semibold text-foreground">{park.average_rating.toFixed(1)}</span>
@@ -149,7 +149,7 @@ export function ParkListView({ parks, onParkClick }: ParkListViewProps) {
<span className="text-accent font-semibold">{park.coaster_count || 0}</span> <span className="text-accent font-semibold">{park.coaster_count || 0}</span>
<span className="text-muted-foreground text-xs">coasters</span> <span className="text-muted-foreground text-xs">coasters</span>
</div> </div>
{park.review_count > 0 && ( {(park.review_count != null && park.review_count > 0) && (
<div className="flex items-center gap-1.5 group/stat"> <div className="flex items-center gap-1.5 group/stat">
<Users className="w-3.5 h-3.5 text-muted-foreground group-hover/stat:text-foreground transition-colors duration-300" /> <Users className="w-3.5 h-3.5 text-muted-foreground group-hover/stat:text-foreground transition-colors duration-300" />
<span className="text-muted-foreground text-xs">{park.review_count} reviews</span> <span className="text-muted-foreground text-xs">{park.review_count} reviews</span>

View File

@@ -67,10 +67,11 @@ export function BlockedUsers() {
const blockedUsersWithProfiles = blocks.map(block => ({ const blockedUsersWithProfiles = blocks.map(block => ({
...block, ...block,
blocker_id: user.id, blocker_id: user.id,
blocked_profile: profiles?.find(p => p.user_id === block.blocked_id) blocked_profile: profiles?.find(p => p.user_id === block.blocked_id),
reason: block.reason || undefined
})); }));
setBlockedUsers(blockedUsersWithProfiles); setBlockedUsers(blockedUsersWithProfiles as UserBlock[]);
logger.info('Blocked users fetched successfully', { logger.info('Blocked users fetched successfully', {
userId: user.id, userId: user.id,

View File

@@ -134,7 +134,7 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) {
}); });
} }
setCredits(processedData); setCredits(processedData as UserRideCredit[]);
} catch (error: unknown) { } catch (error: unknown) {
handleError(error, { handleError(error, {
action: 'Fetch Ride Credits', action: 'Fetch Ride Credits',

View File

@@ -176,8 +176,8 @@ export function ReviewForm({
<div className="space-y-2"> <div className="space-y-2">
<Label>Visit Date</Label> <Label>Visit Date</Label>
<DatePicker <DatePicker
date={watch('visit_date') ? parseDateOnly(watch('visit_date')) : undefined} date={watch('visit_date') ? parseDateOnly(watch('visit_date') || '') : undefined}
onSelect={(date) => setValue('visit_date', date ? toDateOnly(date) : undefined)} onSelect={(date) => setValue('visit_date', date ? toDateOnly(date) : '')}
placeholder="When did you visit?" placeholder="When did you visit?"
disableFuture={true} disableFuture={true}
fromYear={1950} fromYear={1950}

View File

@@ -67,7 +67,7 @@ export function RideCard({ ride, showParkName = true, className, parkSlug }: Rid
<div className="aspect-[3/2] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 flex items-center justify-center relative"> <div className="aspect-[3/2] bg-gradient-to-br from-primary/20 via-secondary/20 to-accent/20 flex items-center justify-center relative">
{(ride.card_image_url || ride.card_image_id || ride.image_url) ? ( {(ride.card_image_url || ride.card_image_id || ride.image_url) ? (
<img <img
src={ride.card_image_url || getCloudflareImageUrl(ride.card_image_id, 'card') || ride.image_url} src={ride.card_image_url || getCloudflareImageUrl(ride.card_image_id || '', 'card') || ride.image_url || ''}
srcSet={ride.card_image_id ? ` srcSet={ride.card_image_id ? `
${getCloudflareImageUrl(ride.card_image_id, 'cardthumb')} 600w, ${getCloudflareImageUrl(ride.card_image_id, 'cardthumb')} 600w,
${getCloudflareImageUrl(ride.card_image_id, 'card')} 1200w ${getCloudflareImageUrl(ride.card_image_id, 'card')} 1200w
@@ -122,29 +122,29 @@ export function RideCard({ ride, showParkName = true, className, parkSlug }: Rid
{/* Stats & Rating */} {/* Stats & Rating */}
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
{/* Stats Row */} {/* Stats Row */}
{(Number(ride.max_speed_kmh) > 0 || Number(ride.max_height_meters) > 0 || Number(ride.duration_seconds) > 0) && ( {(Number(ride.max_speed_kmh || 0) > 0 || Number(ride.max_height_meters || 0) > 0 || Number(ride.duration_seconds || 0) > 0) && (
<div className="flex items-center gap-3 flex-wrap text-sm"> <div className="flex items-center gap-3 flex-wrap text-sm">
{Number(ride.max_speed_kmh) > 0 && ( {Number(ride.max_speed_kmh || 0) > 0 && (
<div className="flex items-center gap-1 min-w-0"> <div className="flex items-center gap-1 min-w-0">
<Zap className="w-4 h-4 text-primary/70 flex-shrink-0" /> <Zap className="w-4 h-4 text-primary/70 flex-shrink-0" />
<span className="font-medium whitespace-nowrap"> <span className="font-medium whitespace-nowrap">
<MeasurementDisplay value={ride.max_speed_kmh} type="speed" /> <MeasurementDisplay value={ride.max_speed_kmh || 0} type="speed" />
</span> </span>
</div> </div>
)} )}
{Number(ride.max_height_meters) > 0 && ( {Number(ride.max_height_meters || 0) > 0 && (
<div className="flex items-center gap-1 min-w-0"> <div className="flex items-center gap-1 min-w-0">
<ArrowUp className="w-4 h-4 text-accent/70 flex-shrink-0" /> <ArrowUp className="w-4 h-4 text-accent/70 flex-shrink-0" />
<span className="font-medium whitespace-nowrap"> <span className="font-medium whitespace-nowrap">
<MeasurementDisplay value={ride.max_height_meters} type="distance" /> <MeasurementDisplay value={ride.max_height_meters || 0} type="distance" />
</span> </span>
</div> </div>
)} )}
{Number(ride.duration_seconds) > 0 && ( {Number(ride.duration_seconds || 0) > 0 && (
<div className="flex items-center gap-1 min-w-0"> <div className="flex items-center gap-1 min-w-0">
<Clock className="w-4 h-4 text-secondary/70 flex-shrink-0" /> <Clock className="w-4 h-4 text-secondary/70 flex-shrink-0" />
<span className="font-medium whitespace-nowrap"> <span className="font-medium whitespace-nowrap">
{Math.floor(ride.duration_seconds / 60)}<span className="text-muted-foreground text-xs ml-0.5">min</span> {Math.floor((ride.duration_seconds || 0) / 60)}<span className="text-muted-foreground text-xs ml-0.5">min</span>
</span> </span>
</div> </div>
)} )}
@@ -152,11 +152,11 @@ export function RideCard({ ride, showParkName = true, className, parkSlug }: Rid
)} )}
{/* Rating */} {/* Rating */}
{ride.average_rating > 0 && ( {(ride.average_rating != null && ride.average_rating > 0) && (
<div className="inline-flex items-center gap-1 text-sm"> <div className="inline-flex items-center gap-1 text-sm">
<Star className="w-4 h-4 fill-yellow-500 text-yellow-500" /> <Star className="w-4 h-4 fill-yellow-500 text-yellow-500" />
<span className="font-semibold text-foreground">{ride.average_rating.toFixed(1)}</span> <span className="font-semibold text-foreground">{ride.average_rating.toFixed(1)}</span>
{ride.review_count > 0 && ( {(ride.review_count != null && ride.review_count > 0) && (
<span className="text-muted-foreground">({ride.review_count})</span> <span className="text-muted-foreground">({ride.review_count})</span>
)} )}
</div> </div>

View File

@@ -145,12 +145,12 @@ export function RideFilters({ filters, onFiltersChange, rides }: RideFiltersProp
}, [locations]); }, [locations]);
const stateOptions: MultiSelectOption[] = useMemo(() => { const stateOptions: MultiSelectOption[] = useMemo(() => {
const states = new Set(locations?.map(l => l.state_province).filter(Boolean) || []); const states = new Set(locations?.map(l => l.state_province).filter((s): s is string => s != null) || []);
return Array.from(states).sort().map(s => ({ label: s, value: s })); return Array.from(states).sort().map(s => ({ label: s, value: s }));
}, [locations]); }, [locations]);
const cityOptions: MultiSelectOption[] = useMemo(() => { const cityOptions: MultiSelectOption[] = useMemo(() => {
const cities = new Set(locations?.map(l => l.city).filter(Boolean) || []); const cities = new Set(locations?.map(l => l.city).filter((c): c is string => c != null) || []);
return Array.from(cities).sort().map(c => ({ label: c, value: c })); return Array.from(cities).sort().map(c => ({ label: c, value: c }));
}, [locations]); }, [locations]);

View File

@@ -101,7 +101,7 @@ export function RideListView({ rides, onRideClick }: RideListViewProps) {
</div> </div>
{/* Rating */} {/* Rating */}
{ride.average_rating > 0 && ( {(ride.average_rating != null && ride.average_rating > 0) && (
<div className="flex items-center gap-1.5 ml-4 flex-shrink-0 bg-yellow-400/10 px-3 py-1.5 rounded-full group-hover:bg-yellow-400/20 transition-colors duration-300"> <div className="flex items-center gap-1.5 ml-4 flex-shrink-0 bg-yellow-400/10 px-3 py-1.5 rounded-full group-hover:bg-yellow-400/20 transition-colors duration-300">
<Star className="w-4 h-4 fill-yellow-400 text-yellow-400" /> <Star className="w-4 h-4 fill-yellow-400 text-yellow-400" />
<span className="font-semibold text-foreground">{ride.average_rating.toFixed(1)}</span> <span className="font-semibold text-foreground">{ride.average_rating.toFixed(1)}</span>

View File

@@ -17,14 +17,14 @@ interface SimilarRide {
name: string; name: string;
slug: string; slug: string;
image_url: string | null; image_url: string | null;
average_rating: number; average_rating: number | null;
status: string; status: string;
category: string; category: string;
description: string | null; description: string | null;
max_speed_kmh: number | null; max_speed_kmh: number | null;
max_height_meters: number | null; max_height_meters: number | null;
duration_seconds: number | null; duration_seconds: number | null;
review_count: number; review_count: number | null;
park: { park: {
name: string; name: string;
slug: string; slug: string;
@@ -61,7 +61,7 @@ export function SimilarRides({ currentRideId, parkId, parkSlug, category }: Simi
.limit(4); .limit(4);
if (!error && data) { if (!error && data) {
setRides(data); setRides(data as SimilarRide[]);
} }
setLoading(false); setLoading(false);
} }

View File

@@ -102,7 +102,7 @@ export function SearchResults({ query, onClose }: SearchResultsProps) {
const getResultRating = (result: SearchResult) => { const getResultRating = (result: SearchResult) => {
if (result.type === 'park' || result.type === 'ride') { if (result.type === 'park' || result.type === 'ride') {
const data = result.data as Park | Ride; const data = result.data as Park | Ride;
return data.average_rating > 0 ? data.average_rating : null; return (data.average_rating != null && data.average_rating > 0) ? data.average_rating : null;
} }
return null; return null;
}; };

View File

@@ -97,7 +97,7 @@ export function AccountProfileTab() {
.maybeSingle(); .maybeSingle();
if (!error && data) { if (!error && data) {
setDeletionRequest(data); setDeletionRequest(data as AccountDeletionRequest);
} }
}; };
@@ -112,8 +112,8 @@ export function AccountProfileTab() {
// Use the update_profile RPC function with server-side validation // Use the update_profile RPC function with server-side validation
const { data: result, error } = await supabase.rpc('update_profile', { const { data: result, error } = await supabase.rpc('update_profile', {
p_username: data.username, p_username: data.username,
p_display_name: data.display_name || null, p_display_name: data.display_name || '',
p_bio: data.bio || null p_bio: data.bio || ''
}); });
if (error) { if (error) {
@@ -219,7 +219,7 @@ export function AccountProfileTab() {
.maybeSingle(); .maybeSingle();
if (!error && data) { if (!error && data) {
setDeletionRequest(data); setDeletionRequest(data as AccountDeletionRequest);
} }
}; };

View File

@@ -253,7 +253,7 @@ export function EmailChangeDialog({ open, onOpenChange, currentEmail, userId }:
metadata: { metadata: {
currentEmail, currentEmail,
newEmail: data.newEmail, newEmail: data.newEmail,
errorType: error.constructor.name errorType: error instanceof Error ? error.constructor.name : 'Unknown'
} }
} }
); );

View File

@@ -78,7 +78,7 @@ const SidebarProvider = React.forwardRef<
state, state,
open, open,
setOpen, setOpen,
isMobile, isMobile: isMobile ?? false,
openMobile, openMobile,
setOpenMobile, setOpenMobile,
toggleSidebar, toggleSidebar,

View File

@@ -86,7 +86,7 @@ export function EntityMultiImageUploader({
.from('photos') .from('photos')
.select('id, cloudflare_image_url, cloudflare_image_id, caption, title') .select('id, cloudflare_image_url, cloudflare_image_id, caption, title')
.eq('entity_type', entityType) .eq('entity_type', entityType)
.eq('entity_id', entityId) .eq('entity_id', entityId || '')
.order('created_at', { ascending: false }); .order('created_at', { ascending: false });
if (error) throw error; if (error) throw error;
@@ -314,7 +314,7 @@ export function EntityMultiImageUploader({
const existingCount = value.uploaded.filter(img => !img.isLocal).length; const existingCount = value.uploaded.filter(img => !img.isLocal).length;
const newCount = value.uploaded.filter(img => img.isLocal).length; const newCount = value.uploaded.filter(img => img.isLocal).length;
const parts = []; const parts: string[] = [];
if (mode === 'edit' && existingCount > 0) { if (mode === 'edit' && existingCount > 0) {
parts.push(`${existingCount} existing photo${existingCount !== 1 ? 's' : ''}`); parts.push(`${existingCount} existing photo${existingCount !== 1 ? 's' : ''}`);

View File

@@ -68,7 +68,7 @@ export function EntityPhotoGallery({
url: photo.cloudflare_image_url, url: photo.cloudflare_image_url,
caption: photo.caption || undefined, caption: photo.caption || undefined,
title: photo.title || undefined, title: photo.title || undefined,
user_id: photo.submitted_by, user_id: photo.submitted_by || '',
created_at: photo.created_at, created_at: photo.created_at,
})) || []; })) || [];
@@ -148,7 +148,7 @@ export function EntityPhotoGallery({
</p> </p>
</div> </div>
<div className="flex flex-col sm:flex-row gap-2 w-full sm:w-auto"> <div className="flex flex-col sm:flex-row gap-2 w-full sm:w-auto">
{isModerator && photos.length > 0 && ( {isModerator() && photos.length > 0 && (
<Button onClick={() => setShowManagement(true)} variant="outline" className="gap-2 w-full sm:w-auto"> <Button onClick={() => setShowManagement(true)} variant="outline" className="gap-2 w-full sm:w-auto">
<Settings className="w-4 h-4" /> <Settings className="w-4 h-4" />
<span className="sm:inline">Manage</span> <span className="sm:inline">Manage</span>

View File

@@ -78,7 +78,11 @@ export function PhotoManagementDialog({
.order('order_index', { ascending: true }); .order('order_index', { ascending: true });
if (error) throw error; if (error) throw error;
setPhotos(data || []); setPhotos((data || []).map(p => ({
...p,
order_index: p.order_index ?? 0,
is_featured: p.is_featured ?? false
})) as Photo[]);
} catch (error: unknown) { } catch (error: unknown) {
toast({ toast({
title: 'Error', title: 'Error',

View File

@@ -110,6 +110,9 @@ export function UppyPhotoSubmissionUpload({
const { uploadURL, id: cloudflareId } = uploadData; const { uploadURL, id: cloudflareId } = uploadData;
// Upload file to Cloudflare // Upload file to Cloudflare
if (!photo.file) {
throw new Error('Photo file is missing');
}
const formData = new FormData(); const formData = new FormData();
formData.append('file', photo.file); formData.append('file', photo.file);

View File

@@ -133,7 +133,7 @@ export function useEntityCache() {
const uncachedIds = getUncachedIds(type, ids); const uncachedIds = getUncachedIds(type, ids);
if (uncachedIds.length === 0) { if (uncachedIds.length === 0) {
// All entities are cached, return them // All entities are cached, return them
return ids.map(id => getCached(type, id)).filter(Boolean); return ids.map(id => getCached(type, id)).filter((item): item is EntityTypeMap[T] => item !== undefined);
} }
try { try {

View File

@@ -316,10 +316,12 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio
}); });
}, },
onSuccess: (data) => { onSuccess: (data) => {
toast({ if (data) {
title: `Content ${data.action}`, toast({
description: `The ${data.item.type} has been ${data.action}`, title: `Content ${data.action}`,
}); description: `The ${data.item.type} has been ${data.action}`,
});
}
}, },
onSettled: () => { onSettled: () => {
// Always refetch to ensure consistency // Always refetch to ensure consistency

View File

@@ -113,12 +113,21 @@ export function useProfileCache() {
// Cache the fetched profiles // Cache the fetched profiles
if (data) { if (data) {
data.forEach((profile: CachedProfile) => { data.forEach((profile) => {
setCached(profile.user_id, profile); const cachedProfile: CachedProfile = {
...profile,
display_name: profile.display_name || undefined,
avatar_url: profile.avatar_url || undefined
};
setCached(profile.user_id, cachedProfile);
}); });
} }
return data || []; return (data || []).map(profile => ({
...profile,
display_name: profile.display_name || undefined,
avatar_url: profile.avatar_url || undefined
}));
} catch (error: unknown) { } catch (error: unknown) {
logger.error('Failed to bulk fetch profiles:', { error: getErrorMessage(error) }); logger.error('Failed to bulk fetch profiles:', { error: getErrorMessage(error) });
return []; return [];

View File

@@ -64,13 +64,13 @@ export function useStatesProvinces(country?: string) {
const { data, error } = await supabase const { data, error } = await supabase
.from('locations') .from('locations')
.select('state_province') .select('state_province')
.eq('country', country) .eq('country', country || '')
.not('state_province', 'is', null); .not('state_province', 'is', null);
if (error) throw error; if (error) throw error;
const uniqueStates = Array.from( const uniqueStates = Array.from(
new Set(data?.map(item => item.state_province) || []) new Set(data?.map(item => item.state_province).filter((s): s is string => s != null) || [])
).sort(); ).sort();
setStatesProvinces( setStatesProvinces(
@@ -153,7 +153,7 @@ export function useRideModels(manufacturerId?: string) {
const { data, error } = await supabase const { data, error } = await supabase
.from('ride_models') .from('ride_models')
.select('id, name') .select('id, name')
.eq('manufacturer_id', manufacturerId) .eq('manufacturer_id', manufacturerId || '')
.order('name'); .order('name');
if (error) throw error; if (error) throw error;
@@ -198,7 +198,7 @@ export function useCompanyHeadquarters() {
if (error) throw error; if (error) throw error;
const uniqueHeadquarters = Array.from( const uniqueHeadquarters = Array.from(
new Set(data?.map(item => item.headquarters_location) || []) new Set(data?.map(item => item.headquarters_location).filter((hq): hq is string => hq != null) || [])
).sort(); ).sort();
setHeadquarters( setHeadquarters(

View File

@@ -18,7 +18,7 @@ export function useProfile(userId: string | undefined) {
// Use get_filtered_profile RPC for privacy-aware field filtering // Use get_filtered_profile RPC for privacy-aware field filtering
const { data, error } = await supabase.rpc('get_filtered_profile', { const { data, error } = await supabase.rpc('get_filtered_profile', {
_profile_user_id: userId, _profile_user_id: userId,
_viewer_id: viewerId _viewer_id: viewerId || ''
}); });
if (error) { if (error) {

View File

@@ -99,8 +99,8 @@ export function useSearch(options: UseSearchOptions = {}) {
type: 'park', type: 'park',
title: park.name, title: park.name,
subtitle: [park.location?.city, park.location?.state_province, park.location?.country].filter(Boolean).join(', '), subtitle: [park.location?.city, park.location?.state_province, park.location?.country].filter(Boolean).join(', '),
image: park.banner_image_url || park.card_image_url, image: park.banner_image_url || park.card_image_url || undefined,
rating: park.average_rating, rating: park.average_rating ?? undefined,
slug: park.slug, slug: park.slug,
data: park data: park
}); });
@@ -125,8 +125,8 @@ export function useSearch(options: UseSearchOptions = {}) {
type: 'ride', type: 'ride',
title: ride.name, title: ride.name,
subtitle: `at ${ride.park?.name || 'Unknown Park'}`, subtitle: `at ${ride.park?.name || 'Unknown Park'}`,
image: ride.image_url, image: ride.image_url || undefined,
rating: ride.average_rating, rating: ride.average_rating ?? undefined,
slug: ride.slug, slug: ride.slug,
data: ride data: ride
}); });
@@ -147,7 +147,7 @@ export function useSearch(options: UseSearchOptions = {}) {
type: 'company', type: 'company',
title: company.name, title: company.name,
subtitle: company.company_type?.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()) || 'Company', subtitle: company.company_type?.replace('_', ' ').replace(/\b\w/g, l => l.toUpperCase()) || 'Company',
image: company.logo_url, image: company.logo_url || undefined,
slug: company.slug, slug: company.slug,
data: company data: company
}); });

View File

@@ -29,8 +29,8 @@ export function useVersionComparison(
// Use the database function to get diff // Use the database function to get diff
const { data, error: rpcError } = await supabase.rpc('get_version_diff', { const { data, error: rpcError } = await supabase.rpc('get_version_diff', {
p_entity_type: entityType, p_entity_type: entityType,
p_from_version_id: fromVersionId, p_from_version_id: fromVersionId || '',
p_to_version_id: toVersionId p_to_version_id: toVersionId || ''
}); });
if (rpcError) throw rpcError; if (rpcError) throw rpcError;

View File

@@ -564,11 +564,11 @@ export async function submitParkUpdate(
submission_id: submissionData.id, submission_id: submissionData.id,
item_type: 'park', item_type: 'park',
action_type: 'edit', action_type: 'edit',
item_data: { item_data: JSON.parse(JSON.stringify({
...extractChangedFields(data, existingPark), ...extractChangedFields(data, existingPark as any),
park_id: parkId, // Always include for relational integrity park_id: parkId, // Always include for relational integrity
images: processedImages as unknown as Json images: processedImages
}, })) as Json,
original_data: JSON.parse(JSON.stringify(existingPark)), original_data: JSON.parse(JSON.stringify(existingPark)),
status: 'pending' as const, status: 'pending' as const,
order_index: 0 order_index: 0
@@ -848,7 +848,7 @@ export async function submitRideUpdate(
item_type: 'ride', item_type: 'ride',
action_type: 'edit', action_type: 'edit',
item_data: { item_data: {
...extractChangedFields(data, existingRide), ...extractChangedFields(data, existingRide as any),
ride_id: rideId, // Always include for relational integrity ride_id: rideId, // Always include for relational integrity
images: processedImages as unknown as Json images: processedImages as unknown as Json
}, },
@@ -1012,7 +1012,7 @@ export async function submitRideModelUpdate(
item_type: 'ride_model', item_type: 'ride_model',
action_type: 'edit', action_type: 'edit',
item_data: { item_data: {
...extractChangedFields(data, existingModel), ...extractChangedFields(data, existingModel as any),
ride_model_id: rideModelId, // Always include for relational integrity ride_model_id: rideModelId, // Always include for relational integrity
images: processedImages as unknown as Json images: processedImages as unknown as Json
}, },
@@ -1124,7 +1124,7 @@ export async function submitManufacturerUpdate(
item_type: 'manufacturer', item_type: 'manufacturer',
action_type: 'edit', action_type: 'edit',
item_data: { item_data: {
...extractChangedFields(data, existingCompany as Partial<CompanyDatabaseRecord>), ...extractChangedFields(data, existingCompany as any),
company_id: companyId, // Always include for relational integrity company_id: companyId, // Always include for relational integrity
company_type: 'manufacturer', // Always include for entity type discrimination company_type: 'manufacturer', // Always include for entity type discrimination
images: processedImages as unknown as Json images: processedImages as unknown as Json
@@ -1232,7 +1232,7 @@ export async function submitDesignerUpdate(
item_type: 'designer', item_type: 'designer',
action_type: 'edit', action_type: 'edit',
item_data: { item_data: {
...extractChangedFields(data, existingCompany as Partial<CompanyDatabaseRecord>), ...extractChangedFields(data, existingCompany as any),
company_id: companyId, // Always include for relational integrity company_id: companyId, // Always include for relational integrity
company_type: 'designer', // Always include for entity type discrimination company_type: 'designer', // Always include for entity type discrimination
images: processedImages as unknown as Json images: processedImages as unknown as Json
@@ -1340,7 +1340,7 @@ export async function submitOperatorUpdate(
item_type: 'operator', item_type: 'operator',
action_type: 'edit', action_type: 'edit',
item_data: { item_data: {
...extractChangedFields(data, existingCompany as Partial<CompanyDatabaseRecord>), ...extractChangedFields(data, existingCompany as any),
company_id: companyId, // Always include for relational integrity company_id: companyId, // Always include for relational integrity
company_type: 'operator', // Always include for entity type discrimination company_type: 'operator', // Always include for entity type discrimination
images: processedImages as unknown as Json images: processedImages as unknown as Json
@@ -1448,7 +1448,7 @@ export async function submitPropertyOwnerUpdate(
item_type: 'property_owner', item_type: 'property_owner',
action_type: 'edit', action_type: 'edit',
item_data: { item_data: {
...extractChangedFields(data, existingCompany as Partial<CompanyDatabaseRecord>), ...extractChangedFields(data, existingCompany as any),
company_id: companyId, // Always include for relational integrity company_id: companyId, // Always include for relational integrity
company_type: 'property_owner', // Always include for entity type discrimination company_type: 'property_owner', // Always include for entity type discrimination
images: processedImages as unknown as Json images: processedImages as unknown as Json

View File

@@ -164,7 +164,7 @@ export function transformRideModelData(submissionData: RideModelSubmissionData):
slug: submissionData.slug, slug: submissionData.slug,
manufacturer_id: submissionData.manufacturer_id, manufacturer_id: submissionData.manufacturer_id,
category: submissionData.category, category: submissionData.category,
ride_type: submissionData.ride_type || null, ride_type: (submissionData.ride_type || null) as string,
description: submissionData.description || null, description: submissionData.description || null,
banner_image_url: submissionData.banner_image_url || null, banner_image_url: submissionData.banner_image_url || null,
banner_image_id: submissionData.banner_image_id || null, banner_image_id: submissionData.banner_image_id || null,

View File

@@ -344,7 +344,12 @@ class NotificationService {
throw error; throw error;
} }
return data || []; return (data || []).map(t => ({
...t,
is_active: t.is_active ?? true,
description: t.description || undefined,
novu_workflow_id: t.novu_workflow_id || undefined
}));
} catch (error: unknown) { } catch (error: unknown) {
logger.error('Error fetching notification templates', { logger.error('Error fetching notification templates', {
action: 'fetch_notification_templates', action: 'fetch_notification_templates',

View File

@@ -96,9 +96,9 @@ export function normalizePhotoSubmissionItems(
id: item.id, id: item.id,
url: item.cloudflare_image_url, url: item.cloudflare_image_url,
filename: item.filename || `Photo ${item.order_index + 1}`, filename: item.filename || `Photo ${item.order_index + 1}`,
caption: item.caption, caption: item.caption || undefined,
title: item.title, title: item.title || undefined,
date_taken: item.date_taken, date_taken: item.date_taken || undefined,
order_index: item.order_index, order_index: item.order_index,
})); }));
} }

View File

@@ -115,12 +115,12 @@ async function logRequestMetadata(metadata: RequestMetadata): Promise<void> {
p_method: metadata.method, p_method: metadata.method,
p_status_code: metadata.statusCode, p_status_code: metadata.statusCode,
p_duration_ms: metadata.duration, p_duration_ms: metadata.duration,
p_error_type: metadata.errorType || null, p_error_type: metadata.errorType ?? undefined,
p_error_message: metadata.errorMessage || null, p_error_message: metadata.errorMessage ?? undefined,
p_user_agent: metadata.userAgent || null, p_user_agent: metadata.userAgent ?? undefined,
p_client_version: metadata.clientVersion || null, p_client_version: metadata.clientVersion ?? undefined,
p_parent_request_id: metadata.parentRequestId || null, p_parent_request_id: metadata.parentRequestId ?? undefined,
p_trace_id: metadata.traceId || null, p_trace_id: metadata.traceId ?? undefined,
}); });
if (error) { if (error) {

View File

@@ -74,7 +74,7 @@ export async function fetchSubmissionItems(submissionId: string): Promise<Submis
* Build dependency tree for submission items * Build dependency tree for submission items
*/ */
export function buildDependencyTree(items: SubmissionItemWithDeps[]): SubmissionItemWithDeps[] { export function buildDependencyTree(items: SubmissionItemWithDeps[]): SubmissionItemWithDeps[] {
const itemMap = new Map(items.map(item => [item.id, { ...item, dependencies: [], dependents: [] }])); const itemMap = new Map(items.map(item => [item.id, { ...item, dependencies: [] as SubmissionItemWithDeps[], dependents: [] as SubmissionItemWithDeps[] }]));
// Build relationships // Build relationships
items.forEach(item => { items.forEach(item => {