diff --git a/.replit b/.replit index aae4bbec..fc81a45d 100644 --- a/.replit +++ b/.replit @@ -1,4 +1,4 @@ -modules = ["nodejs-20", "web"] +modules = ["nodejs-20", "web", "deno-2"] [nix] channel = "stable-25_05" diff --git a/src/components/search/AutocompleteSearch.tsx b/src/components/search/AutocompleteSearch.tsx index 24160beb..0598b288 100644 --- a/src/components/search/AutocompleteSearch.tsx +++ b/src/components/search/AutocompleteSearch.tsx @@ -6,6 +6,7 @@ import { Badge } from '@/components/ui/badge'; import { Separator } from '@/components/ui/separator'; import { useSearch, SearchResult } from '@/hooks/useSearch'; import { useNavigate } from 'react-router-dom'; +import { useToast } from '@/components/ui/use-toast'; interface AutocompleteSearchProps { onResultSelect?: (result: SearchResult) => void; @@ -29,6 +30,7 @@ export function AutocompleteSearch({ variant = 'default' }: AutocompleteSearchProps) { const navigate = useNavigate(); + const { toast } = useToast(); const searchRef = useRef(null); const inputRef = useRef(null); const [isOpen, setIsOpen] = useState(false); @@ -117,23 +119,44 @@ export function AutocompleteSearch({ if (onResultSelect) { onResultSelect(searchResult); } else { - // Default navigation + // Default navigation with null/undefined safety checks switch (searchResult.type) { case 'park': - navigate(`/parks/${searchResult.slug || searchResult.id}`); + const parkIdentifier = searchResult.slug || searchResult.id; + if (parkIdentifier) { + navigate(`/parks/${parkIdentifier}`); + } else { + toast({ + title: "Navigation Error", + description: "Unable to navigate to this park. Missing park identifier.", + variant: "destructive", + }); + navigate(`/search?q=${encodeURIComponent(searchResult.title)}`); + } break; case 'ride': - const parkSlug = (searchResult.data as any).park?.slug; + const parkSlug = (searchResult.data as any)?.park?.slug; const rideSlug = searchResult.slug; + const rideId = searchResult.id; + if (parkSlug && rideSlug) { navigate(`/parks/${parkSlug}/rides/${rideSlug}`); + } else if (rideId) { + navigate(`/rides/${rideId}`); } else { - navigate(`/rides/${searchResult.id}`); + toast({ + title: "Navigation Error", + description: "Unable to navigate to this ride. Missing ride identifier.", + variant: "destructive", + }); + navigate(`/search?q=${encodeURIComponent(searchResult.title)}`); } break; case 'company': - const companyType = (searchResult.data as any).company_type; + const companyType = (searchResult.data as any)?.company_type; const companySlug = searchResult.slug; + const companyId = searchResult.id; + if (companyType && companySlug) { switch (companyType) { case 'operator': @@ -149,10 +172,26 @@ export function AutocompleteSearch({ navigate(`/designers/${companySlug}`); break; default: - navigate(`/companies/${searchResult.id}`); + if (companyId) { + navigate(`/companies/${companyId}`); + } else { + toast({ + title: "Navigation Error", + description: "Unable to navigate to this company. Missing company identifier.", + variant: "destructive", + }); + navigate(`/search?q=${encodeURIComponent(searchResult.title)}`); + } } + } else if (companyId) { + navigate(`/companies/${companyId}`); } else { - navigate(`/companies/${searchResult.id}`); + toast({ + title: "Navigation Error", + description: "Unable to navigate to this company. Missing company identifier.", + variant: "destructive", + }); + navigate(`/search?q=${encodeURIComponent(searchResult.title)}`); } break; } diff --git a/src/hooks/use-mobile.tsx b/src/hooks/use-mobile.tsx index d71598a5..99548db2 100644 --- a/src/hooks/use-mobile.tsx +++ b/src/hooks/use-mobile.tsx @@ -2,7 +2,7 @@ import * as React from "react"; const MOBILE_BREAKPOINT = 768; -export function useIsMobile() { +export function useIsMobile(): boolean | undefined { const [isMobile, setIsMobile] = React.useState(undefined); React.useEffect(() => { @@ -17,5 +17,5 @@ export function useIsMobile() { return () => mql.removeEventListener("change", onChange); }, []); - return !!isMobile; + return isMobile; } diff --git a/src/lib/companyHelpers.ts b/src/lib/companyHelpers.ts index 78f90665..e6b9bb7e 100644 --- a/src/lib/companyHelpers.ts +++ b/src/lib/companyHelpers.ts @@ -1,4 +1,5 @@ import { supabase } from '@/integrations/supabase/client'; +import type { Json } from '@/integrations/supabase/types'; import { ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { uploadPendingImages } from './imageUploadHelper'; @@ -21,11 +22,16 @@ export async function submitCompanyCreation( // Upload any pending local images first let processedImages = data.images; if (data.images?.uploaded && data.images.uploaded.length > 0) { - const uploadedImages = await uploadPendingImages(data.images.uploaded); - processedImages = { - ...data.images, - uploaded: uploadedImages - }; + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { + ...data.images, + uploaded: uploadedImages + }; + } catch (error) { + console.error(`Failed to upload images for ${companyType} creation:`, error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } } // Create the main submission record @@ -59,7 +65,7 @@ export async function submitCompanyCreation( founded_year: data.founded_year, headquarters_location: data.headquarters_location, company_type: companyType, - images: processedImages as any + images: processedImages as unknown as Json }, status: 'pending', order_index: 0 @@ -88,11 +94,16 @@ export async function submitCompanyUpdate( // Upload any pending local images first let processedImages = data.images; if (data.images?.uploaded && data.images.uploaded.length > 0) { - const uploadedImages = await uploadPendingImages(data.images.uploaded); - processedImages = { - ...data.images, - uploaded: uploadedImages - }; + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { + ...data.images, + uploaded: uploadedImages + }; + } catch (error) { + console.error(`Failed to upload images for ${existingCompany.company_type} update:`, error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } } // Create the main submission record @@ -127,7 +138,7 @@ export async function submitCompanyUpdate( website_url: data.website_url, founded_year: data.founded_year, headquarters_location: data.headquarters_location, - images: processedImages as any + images: processedImages as unknown as Json }, original_data: JSON.parse(JSON.stringify(existingCompany)), status: 'pending', diff --git a/src/lib/entitySubmissionHelpers.ts b/src/lib/entitySubmissionHelpers.ts index 5fe6385d..c5762249 100644 --- a/src/lib/entitySubmissionHelpers.ts +++ b/src/lib/entitySubmissionHelpers.ts @@ -1,4 +1,5 @@ import { supabase } from '@/integrations/supabase/client'; +import type { Json } from '@/integrations/supabase/types'; import { ImageAssignments } from '@/components/upload/EntityMultiImageUploader'; import { uploadPendingImages } from './imageUploadHelper'; @@ -142,11 +143,16 @@ export async function submitParkCreation( // Upload any pending local images first let processedImages = data.images; if (data.images?.uploaded && data.images.uploaded.length > 0) { - const uploadedImages = await uploadPendingImages(data.images.uploaded); - processedImages = { - ...data.images, - uploaded: uploadedImages - }; + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { + ...data.images, + uploaded: uploadedImages + }; + } catch (error) { + console.error('Failed to upload images for park creation:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } } // Create the main submission record @@ -173,7 +179,7 @@ export async function submitParkCreation( item_type: 'park', item_data: { ...data, - images: processedImages as any + images: processedImages as unknown as Json }, status: 'pending', order_index: 0 @@ -222,11 +228,16 @@ export async function submitParkUpdate( // Upload any pending local images first let processedImages = data.images; if (data.images?.uploaded && data.images.uploaded.length > 0) { - const uploadedImages = await uploadPendingImages(data.images.uploaded); - processedImages = { - ...data.images, - uploaded: uploadedImages - }; + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { + ...data.images, + uploaded: uploadedImages + }; + } catch (error) { + console.error('Failed to upload images for park update:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } } // Create the main submission record @@ -293,11 +304,16 @@ export async function submitRideCreation( // Upload any pending local images first let processedImages = data.images; if (data.images?.uploaded && data.images.uploaded.length > 0) { - const uploadedImages = await uploadPendingImages(data.images.uploaded); - processedImages = { - ...data.images, - uploaded: uploadedImages - }; + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { + ...data.images, + uploaded: uploadedImages + }; + } catch (error) { + console.error('Failed to upload images for ride creation:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } } // Create the main submission record @@ -324,7 +340,7 @@ export async function submitRideCreation( item_type: 'ride', item_data: { ...data, - images: processedImages as any + images: processedImages as unknown as Json }, status: 'pending', order_index: 0 @@ -373,11 +389,16 @@ export async function submitRideUpdate( // Upload any pending local images first let processedImages = data.images; if (data.images?.uploaded && data.images.uploaded.length > 0) { - const uploadedImages = await uploadPendingImages(data.images.uploaded); - processedImages = { - ...data.images, - uploaded: uploadedImages - }; + try { + const uploadedImages = await uploadPendingImages(data.images.uploaded); + processedImages = { + ...data.images, + uploaded: uploadedImages + }; + } catch (error) { + console.error('Failed to upload images for ride update:', error); + throw new Error('Failed to upload images. Please check your connection and try again.'); + } } // Create the main submission record diff --git a/supabase/functions/cancel-email-change/index.ts b/supabase/functions/cancel-email-change/index.ts index 7750befc..5b731b23 100644 --- a/supabase/functions/cancel-email-change/index.ts +++ b/supabase/functions/cancel-email-change/index.ts @@ -107,7 +107,7 @@ Deno.serve(async (req) => { return new Response( JSON.stringify({ success: false, - error: error.message, + error: error instanceof Error ? error.message : 'An unknown error occurred', }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, diff --git a/supabase/functions/deno.d.ts b/supabase/functions/deno.d.ts new file mode 100644 index 00000000..cc7cd3cd --- /dev/null +++ b/supabase/functions/deno.d.ts @@ -0,0 +1,36 @@ +/// + +declare module 'https://deno.land/std@*/http/server.ts' { + export function serve(handler: (req: Request) => Response | Promise, options?: { port?: number }): void; +} + +declare module 'https://deno.land/std@0.168.0/http/server.ts' { + export function serve(handler: (req: Request) => Response | Promise, options?: { port?: number }): void; +} + +declare module 'https://deno.land/std@0.190.0/http/server.ts' { + export function serve(handler: (req: Request) => Response | Promise, options?: { port?: number }): void; +} + +declare module 'https://esm.sh/@supabase/supabase-js@2' { + export * from '@supabase/supabase-js'; +} + +declare module 'https://esm.sh/@supabase/supabase-js@2.57.4' { + export * from '@supabase/supabase-js'; +} + +declare module 'npm:@novu/node@2.0.2' { + export * from '@novu/node'; +} + +declare namespace Deno { + export namespace env { + export function get(key: string): string | undefined; + } + + export function serve( + handler: (req: Request) => Response | Promise, + options?: { port?: number; hostname?: string } + ): void; +} diff --git a/supabase/functions/deno.json b/supabase/functions/deno.json new file mode 100644 index 00000000..ab4bf8e6 --- /dev/null +++ b/supabase/functions/deno.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "lib": ["deno.window"], + "strict": true, + "allowJs": true, + "noUnusedLocals": false, + "noUnusedParameters": false + }, + "imports": { + "@supabase/supabase-js": "https://esm.sh/@supabase/supabase-js@2.57.4", + "std/": "https://deno.land/std@0.190.0/", + "@novu/node": "npm:@novu/node@2.0.2" + }, + "lint": { + "rules": { + "tags": ["recommended"] + } + }, + "fmt": { + "indentWidth": 2, + "lineWidth": 100, + "semiColons": true, + "singleQuote": false + } +} diff --git a/supabase/functions/process-selective-approval/index.ts b/supabase/functions/process-selective-approval/index.ts index a8d330c1..b24ce2e2 100644 --- a/supabase/functions/process-selective-approval/index.ts +++ b/supabase/functions/process-selective-approval/index.ts @@ -230,7 +230,7 @@ serve(async (req) => { itemId: item.id, itemType: item.item_type, success: false, - error: error.message + error: error instanceof Error ? error.message : 'Unknown error' }); } } @@ -261,7 +261,7 @@ serve(async (req) => { } catch (error) { console.error('Error in process-selective-approval:', error); return new Response( - JSON.stringify({ error: error.message }), + JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }), { status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' } } ); } diff --git a/supabase/functions/tsconfig.json b/supabase/functions/tsconfig.json new file mode 100644 index 00000000..5a29e14c --- /dev/null +++ b/supabase/functions/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2021", + "lib": ["ES2021", "DOM", "DOM.Iterable"], + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "noEmit": true, + "skipLibCheck": true, + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "types": ["./deno.d.ts"], + "allowJs": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "baseUrl": ".", + "paths": { + "https://deno.land/*": ["*"], + "https://esm.sh/*": ["*"], + "npm:*": ["*"] + } + }, + "include": [ + "./**/*.ts", + "./deno.d.ts" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/tsconfig.json b/tsconfig.json index 25187730..da662f36 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "files": [], "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }], + "exclude": ["node_modules", "supabase"], "compilerOptions": { "baseUrl": ".", "paths": {