import { useState, useEffect } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { useQuery, useMutation } from '@tanstack/react-query' import { Box, Loader2, X } from 'lucide-react' import ThreeDViewer from '../components/cad/ThreeDViewer' import { getMediaAssets } from '../api/media' import { generateGltfGeometry } from '../api/cad' /** * Route: /cad/:id * * Full-screen 3D viewer for a CAD file. * If no geometry GLB exists yet, offers to generate one on demand. */ export default function CadPreviewPage() { const { id } = useParams<{ id: string }>() const navigate = useNavigate() const [generating, setGenerating] = useState(false) // Load any geometry GLB that was generated for this CAD file // Poll every 3s while generating so it appears automatically const { data: gltfAssets, isLoading: gltfLoading } = useQuery({ queryKey: ['media-assets', id, 'gltf_geometry'], queryFn: () => getMediaAssets({ cad_file_id: id!, asset_types: ['gltf_geometry'] }), enabled: !!id, staleTime: 5_000, refetchInterval: generating ? 3_000 : false, }) // Load production GLB if available const { data: productionAssets } = useQuery({ queryKey: ['media-assets', id, 'gltf_production'], queryFn: () => getMediaAssets({ cad_file_id: id!, asset_types: ['gltf_production'] }), enabled: !!id, staleTime: 30_000, }) // Load blend assets for download const { data: blendAssets } = useQuery({ queryKey: ['media-assets', id, 'blend_production'], queryFn: () => getMediaAssets({ cad_file_id: id!, asset_types: ['blend_production'] }), enabled: !!id, staleTime: 30_000, }) const generateMutation = useMutation({ mutationFn: () => generateGltfGeometry(id!), onSuccess: () => { setGenerating(true) }, }) // Stop polling once asset appears useEffect(() => { if (generating && gltfAssets && gltfAssets.length > 0) { setGenerating(false) } }, [generating, gltfAssets]) if (!id) { return (

No CAD file ID provided.

) } const latestGltf = gltfAssets?.[0] const latestProduction = productionAssets?.[0] const latestBlend = blendAssets?.[0] // While checking for assets, show a neutral loading screen (don't attempt to render ThreeDViewer) if (gltfLoading) { return (

Checking for 3D model…

) } // No GLB at all — show generate prompt if (!latestGltf && !latestProduction) { return (
3D Viewer

No 3D model available yet

Generate a geometry GLB from the STEP file to enable the 3D viewer. Process the STEP file first to make it available.

{generating ? (
Generating… checking every 3s
) : ( )} {generateMutation.isError && (

Failed to start generation. Make sure the STEP file has been processed.

)}
) } return ( navigate(-1)} geometryGltfUrl={latestGltf?.download_url ?? undefined} productionGltfUrl={latestProduction?.download_url ?? undefined} hasGeometryGlb={!!latestGltf} hasProductionGlb={!!latestProduction} isGeneratingGeometry={generating} onGenerateGeometry={() => generateMutation.mutate()} downloadUrls={{ glb: latestGltf?.download_url ?? undefined, production: latestProduction?.download_url ?? undefined, blend: latestBlend?.download_url ?? undefined, }} /> ) }