feat(N): workflow pipeline, 3D viewer, worker management, QC tests

- workflow_builder.py: fix broken stubs, add render_order_line_still_task
  (resolves step_path from DB instead of passing order_line_id as step_path)
- domains/rendering/tasks.py: add render_order_line_still_task,
  export_gltf_for_order_line_task, export_blend_for_order_line_task,
  generate_gltf_geometry_task (trimesh STL→GLB, no Blender needed)
- tasks/step_tasks.py: add generate_gltf_geometry_task for CadFile GLB export
- cad router: POST /{id}/generate-gltf-geometry endpoint (admin/PM)
- worker router: GET /celery-workers + POST /scale (docker compose subprocess)
- Dockerfile: pip install -e "[dev]" to enable pytest
- docker-compose.yml: docker socket + compose file mount on backend
- ThreeDViewer.tsx: mode toggle (geometry/production), wireframe, env presets,
  download buttons (GLB + .blend)
- CadPreview.tsx: load gltf_geometry/gltf_production/blend_production assets
  from MediaAsset table and pass URLs to ThreeDViewer
- ProductDetail.tsx: "View 3D" button → /cad/:id, "Generate GLB" button
- media router/service: cad_file_id filter on GET /api/media
- WorkerManagement.tsx: new page with worker status, queue depth, scale controls
- App.tsx + Layout.tsx: /workers route + sidebar link (admin/PM)
- tests: test_rendering_service.py, test_orders_service.py (backend)
- tests: WorkerActivity.test.tsx, WorkerManagement.test.tsx (frontend)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 22:56:53 +01:00
parent 208eb21988
commit a70cb55d01
24 changed files with 1828 additions and 448 deletions
+40 -12
View File
@@ -1,36 +1,64 @@
import { useParams, useNavigate } from 'react-router-dom'
import { ArrowLeft } from 'lucide-react'
import { useQuery } from '@tanstack/react-query'
import ThreeDViewer from '../components/cad/ThreeDViewer'
import { getMediaAssets } from '../api/media'
/**
* Route: /cad/:id
*
* Renders the full-screen 3D viewer for a specific CAD file.
* When the viewer is closed the user is navigated back.
* Full-screen 3D viewer for a CAD file.
* Passes production GLB URL if a gltf_geometry MediaAsset exists for this CAD file.
*/
export default function CadPreviewPage() {
const { id } = useParams<{ id: string }>()
const navigate = useNavigate()
// Load any geometry GLB that was generated for this CAD file
const { data: gltfAssets } = useQuery({
queryKey: ['media-assets', id, 'gltf_geometry'],
queryFn: () => getMediaAssets({ cad_file_id: id!, asset_type: 'gltf_geometry' }),
enabled: !!id,
staleTime: 30_000,
})
// Load production GLB if available
const { data: productionAssets } = useQuery({
queryKey: ['media-assets', id, 'gltf_production'],
queryFn: () => getMediaAssets({ cad_file_id: id!, asset_type: '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_type: 'blend_production' }),
enabled: !!id,
staleTime: 30_000,
})
if (!id) {
return (
<div className="flex flex-col items-center justify-center h-full text-content-muted gap-4 p-8">
<p className="text-lg">No CAD file ID provided.</p>
<button
onClick={() => navigate(-1)}
className="flex items-center gap-2 text-sm text-accent hover:underline"
>
<ArrowLeft size={16} />
Go back
</button>
<div className="flex items-center justify-center h-full text-content-muted p-8">
<p>No CAD file ID provided.</p>
</div>
)
}
const latestGltf = gltfAssets?.[0]
const latestProduction = productionAssets?.[0]
const latestBlend = blendAssets?.[0]
return (
<ThreeDViewer
cadFileId={id}
onClose={() => navigate(-1)}
geometryGltfUrl={latestGltf?.download_url ?? undefined}
productionGltfUrl={latestProduction?.download_url ?? undefined}
downloadUrls={{
glb: latestGltf?.download_url ?? undefined,
blend: latestBlend?.download_url ?? undefined,
}}
/>
)
}