mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 18:11:12 -05:00
feat: Implement final type safety fixes
This commit is contained in:
@@ -6,6 +6,7 @@ import { Progress } from '@/components/ui/progress';
|
|||||||
import { Alert, AlertDescription } from '@/components/ui/alert';
|
import { Alert, AlertDescription } from '@/components/ui/alert';
|
||||||
import { CheckCircle2, XCircle, AlertCircle, Loader2 } from 'lucide-react';
|
import { CheckCircle2, XCircle, AlertCircle, Loader2 } from 'lucide-react';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
|
import { getErrorMessage } from '@/lib/errorHandler';
|
||||||
|
|
||||||
interface MigrationResult {
|
interface MigrationResult {
|
||||||
userId: string;
|
userId: string;
|
||||||
@@ -71,11 +72,12 @@ export function NovuMigrationUtility() {
|
|||||||
title: "Migration completed",
|
title: "Migration completed",
|
||||||
description: `Successfully migrated ${successCount} users. ${failureCount} failures.`,
|
description: `Successfully migrated ${successCount} users. ${failureCount} failures.`,
|
||||||
});
|
});
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
|
const errorMsg = getErrorMessage(error);
|
||||||
toast({
|
toast({
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
title: "Migration failed",
|
title: "Migration failed",
|
||||||
description: error.message,
|
description: errorMsg,
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setIsRunning(false);
|
setIsRunning(false);
|
||||||
|
|||||||
@@ -66,10 +66,10 @@ export function ManufacturerCard({ company }: ManufacturerCardProps) {
|
|||||||
|
|
||||||
{/* Logo Display */}
|
{/* Logo Display */}
|
||||||
<div className="absolute inset-0 flex items-center justify-center">
|
<div className="absolute inset-0 flex items-center justify-center">
|
||||||
{(company.logo_url || (company as any).logo_image_id) ? (
|
{company.logo_url ? (
|
||||||
<div className="w-16 h-16 md:w-20 md:h-20 bg-background/90 rounded-xl overflow-hidden shadow-lg backdrop-blur-sm border border-border/50">
|
<div className="w-16 h-16 md:w-20 md:h-20 bg-background/90 rounded-xl overflow-hidden shadow-lg backdrop-blur-sm border border-border/50">
|
||||||
<img
|
<img
|
||||||
src={company.logo_url || ''}
|
src={company.logo_url}
|
||||||
alt={`${company.name} logo`}
|
alt={`${company.name} logo`}
|
||||||
className="w-full h-full object-contain p-2"
|
className="w-full h-full object-contain p-2"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ export function SubmissionReviewManager({
|
|||||||
const [showRejectionDialog, setShowRejectionDialog] = useState(false);
|
const [showRejectionDialog, setShowRejectionDialog] = useState(false);
|
||||||
const [showEditDialog, setShowEditDialog] = useState(false);
|
const [showEditDialog, setShowEditDialog] = useState(false);
|
||||||
const [editingItem, setEditingItem] = useState<SubmissionItemWithDeps | null>(null);
|
const [editingItem, setEditingItem] = useState<SubmissionItemWithDeps | null>(null);
|
||||||
const [activeTab, setActiveTab] = useState<'items' | 'dependencies'>('items' as const);
|
const [activeTab, setActiveTab] = useState<'items' | 'dependencies'>('items');
|
||||||
const [submissionType, setSubmissionType] = useState<string>('submission');
|
const [submissionType, setSubmissionType] = useState<string>('submission');
|
||||||
const [showValidationBlockerDialog, setShowValidationBlockerDialog] = useState(false);
|
const [showValidationBlockerDialog, setShowValidationBlockerDialog] = useState(false);
|
||||||
const [showWarningConfirmDialog, setShowWarningConfirmDialog] = useState(false);
|
const [showWarningConfirmDialog, setShowWarningConfirmDialog] = useState(false);
|
||||||
|
|||||||
@@ -30,8 +30,24 @@ export function ValidationSummary({ item, onValidationChange, compact = false }:
|
|||||||
async function validate() {
|
async function validate() {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
|
// Type guard for valid entity types
|
||||||
|
type ValidEntityType = 'park' | 'ride' | 'manufacturer' | 'operator' | 'designer' | 'property_owner' | 'ride_model' | 'photo';
|
||||||
|
const validEntityTypes: ValidEntityType[] = ['park', 'ride', 'manufacturer', 'operator', 'designer', 'property_owner', 'ride_model', 'photo'];
|
||||||
|
|
||||||
|
if (!validEntityTypes.includes(item.item_type as ValidEntityType)) {
|
||||||
|
setValidationResult({
|
||||||
|
isValid: false,
|
||||||
|
blockingErrors: [{ field: 'item_type', message: `Invalid entity type: ${item.item_type}`, severity: 'blocking' }],
|
||||||
|
warnings: [],
|
||||||
|
suggestions: [],
|
||||||
|
allErrors: [{ field: 'item_type', message: `Invalid entity type: ${item.item_type}`, severity: 'blocking' }],
|
||||||
|
});
|
||||||
|
setIsLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const result = await validateEntityData(
|
const result = await validateEntityData(
|
||||||
item.item_type as any,
|
item.item_type as ValidEntityType,
|
||||||
{ ...item.item_data, id: item.id }
|
{ ...item.item_data, id: item.id }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useState, useCallback, useRef, useEffect, useMemo } from "react";
|
|||||||
import { supabase } from "@/integrations/supabase/client";
|
import { supabase } from "@/integrations/supabase/client";
|
||||||
import { useToast } from "@/hooks/use-toast";
|
import { useToast } from "@/hooks/use-toast";
|
||||||
import { logger } from "@/lib/logger";
|
import { logger } from "@/lib/logger";
|
||||||
|
import { getErrorMessage } from "@/lib/errorHandler";
|
||||||
import { MODERATION_CONSTANTS } from "@/lib/moderation/constants";
|
import { MODERATION_CONSTANTS } from "@/lib/moderation/constants";
|
||||||
import type { User } from "@supabase/supabase-js";
|
import type { User } from "@supabase/supabase-js";
|
||||||
import {
|
import {
|
||||||
@@ -377,8 +378,9 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
|
|
||||||
// Refresh stats to update counts
|
// Refresh stats to update counts
|
||||||
queue.refreshStats();
|
queue.refreshStats();
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error("Error moderating content:", error);
|
const errorMsg = getErrorMessage(error);
|
||||||
|
console.error("Error moderating content:", errorMsg);
|
||||||
|
|
||||||
// Revert optimistic update
|
// Revert optimistic update
|
||||||
setItems((prev) => {
|
setItems((prev) => {
|
||||||
@@ -392,7 +394,7 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: error.message || `Failed to ${action} content`,
|
description: errorMsg || `Failed to ${action} content`,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@@ -425,8 +427,9 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
|
|
||||||
// Refresh stats to update counts
|
// Refresh stats to update counts
|
||||||
queue.refreshStats();
|
queue.refreshStats();
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error("Error deleting submission:", error);
|
const errorMsg = getErrorMessage(error);
|
||||||
|
console.error("Error deleting submission:", errorMsg);
|
||||||
|
|
||||||
setItems((prev) => {
|
setItems((prev) => {
|
||||||
if (prev.some((i) => i.id === item.id)) return prev;
|
if (prev.some((i) => i.id === item.id)) return prev;
|
||||||
@@ -465,11 +468,12 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
queue.refreshStats();
|
queue.refreshStats();
|
||||||
|
|
||||||
setItems((prev) => prev.filter((i) => i.id !== item.id));
|
setItems((prev) => prev.filter((i) => i.id !== item.id));
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error("Error resetting submission:", error);
|
const errorMsg = getErrorMessage(error);
|
||||||
|
console.error("Error resetting submission:", errorMsg);
|
||||||
toast({
|
toast({
|
||||||
title: "Reset Failed",
|
title: "Reset Failed",
|
||||||
description: error.message,
|
description: errorMsg,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
@@ -528,11 +532,12 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
|
|||||||
|
|
||||||
// Refresh stats to update counts
|
// Refresh stats to update counts
|
||||||
queue.refreshStats();
|
queue.refreshStats();
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error("Error retrying failed items:", error);
|
const errorMsg = getErrorMessage(error);
|
||||||
|
console.error("Error retrying failed items:", errorMsg);
|
||||||
toast({
|
toast({
|
||||||
title: "Retry Failed",
|
title: "Retry Failed",
|
||||||
description: error.message,
|
description: errorMsg,
|
||||||
variant: "destructive",
|
variant: "destructive",
|
||||||
});
|
});
|
||||||
} finally {
|
} finally {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function isValidUnitPreferences(obj: unknown): obj is UnitPreferences {
|
|||||||
typeof obj === 'object' &&
|
typeof obj === 'object' &&
|
||||||
obj !== null &&
|
obj !== null &&
|
||||||
'measurement_system' in obj &&
|
'measurement_system' in obj &&
|
||||||
['metric', 'imperial'].includes((obj as any).measurement_system)
|
(obj.measurement_system === 'metric' || obj.measurement_system === 'imperial')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,11 @@
|
|||||||
|
|
||||||
import type { ModerationItem, EntityFilter, StatusFilter } from '@/types/moderation';
|
import type { ModerationItem, EntityFilter, StatusFilter } from '@/types/moderation';
|
||||||
|
|
||||||
|
interface SubmissionContent {
|
||||||
|
name?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if a submission matches the entity filter
|
* Check if a submission matches the entity filter
|
||||||
*/
|
*/
|
||||||
@@ -167,7 +172,7 @@ export function buildModerationItem(
|
|||||||
display_name: profile.display_name,
|
display_name: profile.display_name,
|
||||||
avatar_url: profile.avatar_url,
|
avatar_url: profile.avatar_url,
|
||||||
} : undefined,
|
} : undefined,
|
||||||
entity_name: entityName || (submission.content as any)?.name || 'Unknown',
|
entity_name: entityName || (submission.content as SubmissionContent)?.name || 'Unknown',
|
||||||
park_name: parkName,
|
park_name: parkName,
|
||||||
reviewed_at: submission.reviewed_at || undefined,
|
reviewed_at: submission.reviewed_at || undefined,
|
||||||
reviewer_notes: submission.reviewer_notes || undefined,
|
reviewer_notes: submission.reviewer_notes || undefined,
|
||||||
|
|||||||
@@ -609,8 +609,16 @@ async function createCompany(
|
|||||||
// Extract image assignments
|
// Extract image assignments
|
||||||
const imageData = extractImageAssignments(resolvedData.images);
|
const imageData = extractImageAssignments(resolvedData.images);
|
||||||
|
|
||||||
|
// Type guard for company type
|
||||||
|
type ValidCompanyType = 'manufacturer' | 'designer' | 'operator' | 'property_owner';
|
||||||
|
const validCompanyTypes: ValidCompanyType[] = ['manufacturer', 'designer', 'operator', 'property_owner'];
|
||||||
|
|
||||||
|
if (!validCompanyTypes.includes(companyType as ValidCompanyType)) {
|
||||||
|
throw new Error(`Invalid company type: ${companyType}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Transform to database format
|
// Transform to database format
|
||||||
const companyData = { ...transformCompanyData(resolvedData, companyType as any), ...imageData };
|
const companyData = { ...transformCompanyData(resolvedData, companyType as ValidCompanyType), ...imageData };
|
||||||
|
|
||||||
const { data: company, error } = await supabase
|
const { data: company, error } = await supabase
|
||||||
.from('companies')
|
.from('companies')
|
||||||
@@ -1062,12 +1070,18 @@ export async function editSubmissionItem(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type guard for submission with user_id
|
||||||
|
interface SubmissionWithUser {
|
||||||
|
user_id: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
// Log admin action
|
// Log admin action
|
||||||
await supabase
|
await supabase
|
||||||
.from('admin_audit_log')
|
.from('admin_audit_log')
|
||||||
.insert({
|
.insert({
|
||||||
admin_user_id: userId,
|
admin_user_id: userId,
|
||||||
target_user_id: (currentItem.submission as any).user_id,
|
target_user_id: (currentItem.submission as SubmissionWithUser).user_id,
|
||||||
action: 'edit_submission_item',
|
action: 'edit_submission_item',
|
||||||
details: {
|
details: {
|
||||||
item_id: itemId,
|
item_id: itemId,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { ParkCard } from '@/components/parks/ParkCard';
|
|||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
|
import { getErrorMessage } from '@/lib/errorHandler';
|
||||||
import { submitCompanyUpdate } from '@/lib/companyHelpers';
|
import { submitCompanyUpdate } from '@/lib/companyHelpers';
|
||||||
import { VersionIndicator } from '@/components/versioning/VersionIndicator';
|
import { VersionIndicator } from '@/components/versioning/VersionIndicator';
|
||||||
import { EntityHistoryTabs } from '@/components/history/EntityHistoryTabs';
|
import { EntityHistoryTabs } from '@/components/history/EntityHistoryTabs';
|
||||||
@@ -154,10 +155,11 @@ export default function PropertyOwnerDetail() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setIsEditModalOpen(false);
|
setIsEditModalOpen(false);
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
|
const errorMsg = getErrorMessage(error);
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: error.message || "Failed to submit edit.",
|
description: errorMsg || "Failed to submit edit.",
|
||||||
variant: "destructive"
|
variant: "destructive"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import { RideForm } from '@/components/admin/RideForm';
|
|||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
|
import { getErrorMessage } from '@/lib/errorHandler';
|
||||||
import { VersionIndicator } from '@/components/versioning/VersionIndicator';
|
import { VersionIndicator } from '@/components/versioning/VersionIndicator';
|
||||||
import { EntityHistoryTabs } from '@/components/history/EntityHistoryTabs';
|
import { EntityHistoryTabs } from '@/components/history/EntityHistoryTabs';
|
||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
@@ -183,10 +184,11 @@ export default function RideDetail() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setIsEditModalOpen(false);
|
setIsEditModalOpen(false);
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
|
const errorMsg = getErrorMessage(error);
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: error.message || "Failed to submit edit.",
|
description: errorMsg || "Failed to submit edit.",
|
||||||
variant: "destructive"
|
variant: "destructive"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
|||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
|
import { getErrorMessage } from '@/lib/errorHandler';
|
||||||
import { RideModelForm } from '@/components/admin/RideModelForm';
|
import { RideModelForm } from '@/components/admin/RideModelForm';
|
||||||
import { ManufacturerPhotoGallery } from '@/components/companies/ManufacturerPhotoGallery';
|
import { ManufacturerPhotoGallery } from '@/components/companies/ManufacturerPhotoGallery';
|
||||||
import { VersionIndicator } from '@/components/versioning/VersionIndicator';
|
import { VersionIndicator } from '@/components/versioning/VersionIndicator';
|
||||||
@@ -120,10 +121,11 @@ export default function RideModelDetail() {
|
|||||||
|
|
||||||
setIsEditModalOpen(false);
|
setIsEditModalOpen(false);
|
||||||
fetchData();
|
fetchData();
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
|
const errorMsg = getErrorMessage(error);
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: error.message || "Failed to update ride model.",
|
description: errorMsg || "Failed to update ride model.",
|
||||||
variant: "destructive"
|
variant: "destructive"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { RideForm } from '@/components/admin/RideForm';
|
|||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
|
import { getErrorMessage } from '@/lib/errorHandler';
|
||||||
import type { Ride, Company, RideModel } from "@/types/database";
|
import type { Ride, Company, RideModel } from "@/types/database";
|
||||||
|
|
||||||
export default function RideModelRides() {
|
export default function RideModelRides() {
|
||||||
@@ -142,10 +143,11 @@ export default function RideModelRides() {
|
|||||||
|
|
||||||
setIsCreateModalOpen(false);
|
setIsCreateModalOpen(false);
|
||||||
fetchData();
|
fetchData();
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
|
const errorMsg = getErrorMessage(error);
|
||||||
toast({
|
toast({
|
||||||
title: "Error",
|
title: "Error",
|
||||||
description: error.message || "Failed to submit ride.",
|
description: errorMsg || "Failed to submit ride.",
|
||||||
variant: "destructive"
|
variant: "destructive"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import { supabase } from '@/integrations/supabase/client';
|
|||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
|
import { getErrorMessage } from '@/lib/errorHandler';
|
||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
|
|
||||||
export default function Rides() {
|
export default function Rides() {
|
||||||
@@ -89,10 +90,11 @@ export default function Rides() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setIsCreateModalOpen(false);
|
setIsCreateModalOpen(false);
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
|
const errorMsg = getErrorMessage(error);
|
||||||
toast({
|
toast({
|
||||||
title: "Submission Failed",
|
title: "Submission Failed",
|
||||||
description: error.message || "Failed to submit ride.",
|
description: errorMsg || "Failed to submit ride.",
|
||||||
variant: "destructive"
|
variant: "destructive"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user