diff --git a/frontend/src/components/HelpTooltip.tsx b/frontend/src/components/HelpTooltip.tsx new file mode 100644 index 0000000..54f11af --- /dev/null +++ b/frontend/src/components/HelpTooltip.tsx @@ -0,0 +1,95 @@ +import { HelpCircle } from 'lucide-react' +import { useState } from 'react' +import { HELP_TEXTS, type HelpText } from '../help/helpTexts' + +interface HelpTooltipProps { + helpKey?: string + help?: HelpText + position?: 'top' | 'right' | 'bottom' | 'left' + size?: number +} + +export function HelpTooltip({ + helpKey, + help: directHelp, + position = 'right', + size = 14, +}: HelpTooltipProps) { + const [visible, setVisible] = useState(false) + const help = directHelp ?? (helpKey ? HELP_TEXTS[helpKey] : undefined) + + if (!help) return null + + const tooltipPositionStyle: React.CSSProperties = (() => { + switch (position) { + case 'top': + return { bottom: '100%', left: '50%', transform: 'translateX(-50%)', marginBottom: 6 } + case 'bottom': + return { top: '100%', left: '50%', transform: 'translateX(-50%)', marginTop: 6 } + case 'left': + return { right: '100%', top: '50%', transform: 'translateY(-50%)', marginRight: 6 } + case 'right': + default: + return { left: '100%', top: '50%', transform: 'translateY(-50%)', marginLeft: 6 } + } + })() + + return ( + setVisible(true)} + onMouseLeave={() => setVisible(false)} + > + + + {visible && ( +
+

+ {help.title} +

+

+ {help.body} +

+ {help.unit && help.range && ( +

+ Range: {help.range[0]}–{help.range[1]} {help.unit} +

+ )} + {help.recommendation && ( +

+ Recommended: {help.recommendation} +

+ )} + {help.warning && ( +

+ Warning: {help.warning} +

+ )} +
+ )} +
+ ) +} + +export default HelpTooltip diff --git a/frontend/src/components/admin/RenderTemplateTable.tsx b/frontend/src/components/admin/RenderTemplateTable.tsx index f75f10f..589cf36 100644 --- a/frontend/src/components/admin/RenderTemplateTable.tsx +++ b/frontend/src/components/admin/RenderTemplateTable.tsx @@ -1,6 +1,7 @@ import { useState, useRef } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Pencil, Trash2, Plus, Check, X, Upload, Download } from 'lucide-react' +import HelpTooltip from '../HelpTooltip' import { toast } from 'sonner' import { listRenderTemplates, @@ -165,9 +166,24 @@ export default function RenderTemplateTable() { Category Output Type Collection - Mat. Replace - Lighting Only - Shadow Catcher + + + Mat. Replace + + + + + + Lighting Only + + + + + + Shadow Catcher + + + Cam Orbit .blend File Active diff --git a/frontend/src/help/helpTexts.ts b/frontend/src/help/helpTexts.ts new file mode 100644 index 0000000..daf6207 --- /dev/null +++ b/frontend/src/help/helpTexts.ts @@ -0,0 +1,83 @@ +export interface HelpText { + title: string + body: string + warning?: string + recommendation?: string + unit?: string + range?: [number, number] +} + +export const HELP_TEXTS: Record = { + // Render settings + 'setting.blender_cycles_samples': { + title: 'Cycles Samples', + body: 'Number of render samples per pixel. Higher = better quality, longer render time.', + recommendation: '256 for production, 64 for quick previews', + unit: 'samples', + range: [1, 4096], + }, + 'setting.blender_eevee_samples': { + title: 'EEVEE Samples', + body: 'Anti-aliasing samples for EEVEE Next renderer. EEVEE is much faster than Cycles but less photorealistic.', + recommendation: '64 for standard quality', + unit: 'samples', + range: [1, 1024], + }, + 'setting.blender_smooth_angle': { + title: 'Smooth Shading Angle', + body: 'Angle threshold for auto-smooth shading on imported meshes. Below this angle, faces appear smooth; above it, edges show a sharp crease.', + recommendation: '30° works well for most bearings', + unit: 'degrees', + range: [0, 180], + }, + 'setting.thumbnail_renderer': { + title: 'Thumbnail Renderer', + body: 'Which renderer to use for STEP file thumbnails. Blender produces photorealistic results; Three.js is faster but lower quality.', + recommendation: 'Use Blender for production, Three.js for fast previews', + }, + 'setting.stl_quality': { + title: 'STL Export Quality', + body: 'Controls tessellation precision when converting STEP to STL for older render paths. High quality = finer mesh, larger file, slower conversion.', + recommendation: 'Low is sufficient for most thumbnails', + }, + 'action.regenerate_thumbnails': { + title: 'Regenerate All Thumbnails', + body: 'Re-renders thumbnails for all STEP files using current renderer settings. Queues every file on the thumbnail_rendering worker.', + warning: 'This queues a large number of tasks. Only run during off-peak hours.', + }, + 'action.process_unprocessed': { + title: 'Process Unprocessed', + body: "Queues all STEP files that have status 'pending' or 'failed' for metadata extraction and thumbnail rendering.", + }, + 'action.generate_missing_stls': { + title: 'Generate Missing STL Caches', + body: "Creates STL cache files for STEP files that don't have them yet. STL caches speed up repeated renders.", + }, + 'action.seed_aliases': { + title: 'Seed Material Aliases', + body: 'Loads the default Schaeffler material alias mappings (Steel→SCHAEFFLER_010101_Steel-Bare, etc). Safe to run multiple times — existing aliases are not overwritten.', + }, + // Template fields + 'template.lighting_only': { + title: 'Lighting Only Mode', + body: 'When enabled, this template provides only the world/HDRI lighting environment. The camera position is computed automatically from the product bounding box, ignoring any camera set up in the .blend file.', + recommendation: 'Use for HDR light-setup templates where you want auto-framing', + }, + 'template.shadow_catcher': { + title: 'Shadow Catcher', + body: "Adds a transparent ground plane that catches shadows from the product. Requires a 'Shadowcatcher' collection in the .blend file. Enables the transparent film option.", + }, + 'template.material_replace_enabled': { + title: 'Material Replacement', + body: 'When enabled, Blender will replace part materials with the mapped Schaeffler library materials. When disabled, the original .blend materials are used.', + }, + // Wizard fields + 'wizard.output_type': { + title: 'Output Type', + body: 'Defines what to render: still image, turntable animation, or custom output. Each output type has its own render settings and pricing.', + }, + 'wizard.render_template': { + title: 'Render Template', + body: 'A .blend file that provides lighting, camera setup, and optionally a background environment. Templates are matched by product category.', + }, +} diff --git a/frontend/src/pages/Admin.tsx b/frontend/src/pages/Admin.tsx index 7330375..5d05307 100644 --- a/frontend/src/pages/Admin.tsx +++ b/frontend/src/pages/Admin.tsx @@ -5,6 +5,7 @@ import { UserPlus, Trash2, Pencil, ChevronDown, ChevronUp, ChevronRight, Setting import { Link } from 'react-router-dom' import api from '../api/client' import ConfirmModal from '../components/ConfirmModal' +import HelpTooltip from '../components/HelpTooltip' import TemplateEditor from '../components/admin/TemplateEditor' import PricingTierTable from '../components/admin/PricingTierTable' import OutputTypeTable from '../components/admin/OutputTypeTable' @@ -388,7 +389,10 @@ export default function AdminPage() { {/* Sample counts */}
- + Higher = better quality, slower

- + - Smooth angle + + Smooth angle + + Resets files stuck in 'processing' to 'failed'. Runs automatically every 5 min.

- +
+ + +

Queues all pending/failed STEP files for initial processing.

- +
+ + +

Re-renders thumbnails for all completed CAD files.