{session.progress_percent||0}% complete
{session.status==='Not started'?'Start workflow':'Continue workflow'} }
src/components/measurements/StartWorkflowDialog.jsx:18: const start = async () => { const component = components.find(c => c.uuid === sel.component_uuid); const tasks = await dimensionAnalysis.buildWorkflow(component); const session = await dimensionService.createSession('component_envelope', { customer_uuid: sel.customer_uuid, limb_profile_uuid: sel.limb_uuid, component_uuid: sel.component_uuid }, 'Ray'); navigate(`/measurements/${session.uuid}`); };
src/components/newcover/NewCoverWizard.jsx:91: status: 'Awaiting measurements',
src/components/newcover/NewCoverWizard.jsx:259: !!data.measurement_method,
src/components/newcover/WizardStep.jsx:426: The project may proceed to measurements and
src/components/newcover/WizardStep.jsx:438: return select('measurement_method', methods);
src/components/newcover/WizardStep.jsx:509: Measurement method:
src/components/newcover/WizardStep.jsx:511: {data.measurement_method || 'Missing'}
src/components/newcover/wizardConfig.js:6: 'Measurement method',
src/components/newcover/wizardConfig.js:48: 'Manual measurements',
src/components/newcover/wizardConfig.js:50: 'Combination of manual measurements and 3D scan',
src/components/ui/image.jsx:101: // `size` is null exactly once: the pre-measurement first render, which we
src/components/ui/image.jsx:117: // useLayoutEffect, so nothing is lost: measurement lands before the
src/components/viewportV2/BabylonViewportV2.jsx:6: import { buildMeasurementStations } from './MeasurementStationsV2';
src/components/viewportV2/BabylonViewportV2.jsx:68: if (stationsRef.current && !compat) activeLayers.push('Measurement stations');
src/components/viewportV2/BabylonViewportV2.jsx:127: const s = buildMeasurementStations(scene);
src/components/viewportV2/BabylonViewportV2.jsx:347: return { prosthesis: 'Prosthesis', stations: 'Measurement layer', cover: 'Cover layer', branding: 'Branding layer', design: 'Design layer', ossiguard: 'Ossiguard layer' }[k] || k;
src/components/viewportV2/MeasurementStationsV2.js:19: export function buildMeasurementStations(scene) {
src/components/viewportV2/MeasurementStationsV2.js:31: ring.metadata = { type: 'measurementStation', stationId: s.id, stationName: s.name, value: null, index: i };
src/components/workshop/navItems.js:8: ['New Cover','/new-cover',PlusCircle],
src/components/workshop/navItems.js:9: ['Measurement Studio','/measurements',Ruler],
src/components/workshop3d/ContextInspectorV2.jsx:4: import { saveStationMeasurement, GEOMETRY_STATUS, STATUS_TONE } from '@/services/measurementGeometryService';
src/components/workshop3d/ContextInspectorV2.jsx:37: const isStation = kind === 'measurementStation';
src/components/workshop3d/ContextInspectorV2.jsx:85: await saveStationMeasurement({
src/components/workshop3d/ContextInspectorV2.jsx:106: await saveStationMeasurement({ sessionUuid: sessionId, componentUuid: componentUuid || 'generic', station: { station_name: name, notes: 'Not applicable', shape_classification: 'Not applicable' } });
src/components/workshop3d/ContextInspectorV2.jsx:126:
Measurement stations show live entry. Parts show component data. The cover shows geometry status.
src/components/workshop3d/ContextInspectorV2.jsx:136: {existing ?
Recorded — review required :
Measurement required}
src/components/workshop3d/ContextInspectorV2.jsx:148:
{sessionId ? 'Saves to the active measurement session and updates the 3D model.' : 'No active session — select or create a session first.'}
src/components/workshop3d/ContextInspectorV2.jsx:173:
src/components/workshop3d/ContextInspectorV2.jsx:174:
src/components/workshop3d/ContextInspectorV2.jsx:175:
src/components/workshop3d/ContextInspectorV2.jsx:176:
src/components/workshop3d/ContextInspectorV2.jsx:177:
src/components/workshop3d/ContextInspectorV2.jsx:211:
src/components/workshop3d/ProstheticAssembly.jsx:11: * Label: "Generic visual measurement model — dimensions must be verified before CAD."
src/components/workshop3d/ProstheticAssembly.jsx:92: { id: 'asm-disclaimer', position: [0, 224, 0], title: 'Generic visual measurement model', subtitle: 'dimensions must be verified before CAD', tone: 'amber' },
src/components/workshop3d/SceneComposer.jsx:47:
src/components/workshop3d/SceneComposer.jsx:48:
src/components/workshop3d/SceneComposer.jsx:52:
src/components/workshop3d/StationRings.jsx:20: : 'Measurement required';
src/components/workshop3d/ViewportLabelLayer.jsx:6: * Renders measurement labels, warnings and callouts as ordinary React DOM
src/components/workshop3d/limbGeometry.js:3: // Example measurement stations for the generic visual model.
src/components/workshop3d/panels/ContextPanel.jsx:40: let label = 'No measurement session';
src/components/workshop3d/panels/ContextPanel.jsx:88: Workshop measurement required — link a physical component record to load verified dimensions. Dimensions must be verified before CAD.
src/components/workshop3d/panels/ContextPanel.jsx:131: Measurement status p.uuid)} />
src/components/workshop3d/panels/ContextPanel.jsx:171: Measurement status p.uuid)} />
src/components/workshop3d/panels/ContextPanel.jsx:173: Visual model: generic until a verified component model is loaded. No measurements are invented.
src/components/workshop3d/panels/ContextPanel.jsx:206: {linkedComps.length === 0 ? No components linked — workshop measurement required
: linkedComps.map(c => (
src/components/workshop3d/panels/ContextPanel.jsx:219: // ---- Measurement station (fallback path; browse mode uses MeasurementInspector) ----
src/components/workshop3d/panels/ContextPanel.jsx:224: Measurement Station
src/components/workshop3d/panels/ContextPanel.jsx:229:
src/components/workshop3d/panels/ContextPanel.jsx:230:
src/components/workshop3d/panels/ContextPanel.jsx:254:
src/components/workshop3d/panels/ContextPanel.jsx:255:
src/components/workshop3d/panels/ContextPanel.jsx:262:
src/components/workshop3d/panels/ContextPanel.jsx:263:
src/components/workshop3d/panels/ContextPanel.jsx:264:
src/components/workshop3d/panels/MeasurementInspector.jsx:5: const SOURCES = ['Manufacturer Verified', 'Workshop Measurement', 'Verified 3D Scan', 'Customer Reported', 'Calculated', 'Estimated', 'Unknown'];
src/components/workshop3d/panels/MeasurementInspector.jsx:17: export default function MeasurementInspector({ selected, component, stationIndex, onSave, onMeasureAgain, onNotApplicable, previousValue, onAddPhoto }) {
src/components/workshop3d/panels/MeasurementInspector.jsx:19: const name = s.name || s.station_name || (stationIndex != null ? `Station ${String(stationIndex + 1).padStart(2, '0')}` : 'Measurement');
src/components/workshop3d/panels/MeasurementInspector.jsx:42: {stationIndex != null ? `Measurement Station ${String(stationIndex + 1).padStart(2, '0')}` : 'Measurement'}
src/components/workshop3d/panels/MeasurementInspector.jsx:51: Measurement Name
src/components/workshop3d/panels/MeasurementInspector.jsx:56: setValue(e.target.value)} placeholder="Measurement required — enter value" className="workshop-input flex-1 !min-h-10" />
src/components/workshop3d/panels/StationTable.jsx:10: Measurement Stations ({rows.length})
src/components/workshop3d/panels/VerticalToolStrip.jsx:6: { id: 'station', icon: Ruler, label: 'Add Measurement Station' },
src/components/workshop3d/panels/ViewportToolbar.jsx:15: { id: 'measurements', icon: Eye, label: 'Measurements' },
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:2: import { measurementService } from '@/services/measurementService';
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:18: export default function MeasurementStudio3D({ session, onGuidedView, onHighlightStation, onComplete, actor = 'Ray' }) {
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:30: measurementService.definitions(session.workflow).then(defs => {
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:54: No active measurement session.
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:55: Start a session from the Measurement Studio to begin guided 3D recording.
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:64: All measurements complete.
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:74: const saved = await measurementService.save(session, current, { value: Number(value), entered_unit: enteredUnit, confidence, notes, photo_url: '' }, actor);
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:81: await measurementService.progress(session, definitions.length, definitions.length);
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:93: await measurementService.skip(session, current, 'Not applicable for this limb', actor);
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:105: Guided 3D Measurement
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:109: Measurement {index + 1} of {definitions.length} • {progress}%
src/components/workshop3d/workflows/MeasurementStudio3D.jsx:130: setValue(e.target.value)} placeholder="Enter measurement" className="workshop-input flex-1" />
src/config/permissions.js:26: { group: 'Measurements', permissions: [
src/config/permissions.js:27: { key: 'measurements.record', label: 'Record measurements' },
src/config/permissions.js:28: { key: 'measurements.edit', label: 'Edit entered measurements' },
src/config/permissions.js:29: { key: 'measurements.verify', label: 'Verify measurements' },
src/config/permissions.js:30: { key: 'measurements.supersede', label: 'Supersede measurements' },
src/config/permissions.js:31: { key: 'measurements.export', label: 'Export measurements' },
src/config/permissions.js:77: 'measurements.record', 'measurements.edit',
src/config/permissions.js:90: permissions: [...builderBase, 'measurements.verify', 'measurements.supersede',
src/config/permissions.js:101: measurement_only: { label: 'Measurement Only', role: 'builder', permissions: ['customers.view_assigned', 'components.view', 'measurements.record', 'measurements.edit', 'measurements.verify'] },
src/config/permissions.js:107: 'Customer Review', 'Measurements', 'Prosthetic Components', 'Babylon 3D Mapping',
src/hooks/use-size.jsx:6: // useLayoutEffect (not useEffect): the initial measurement must land before
src/modes/customerIntakeConfig.js:1: export const customerIntakeMode = { exposed: false, allowedSteps: ['Start request','Contact details','Select limb','Select prosthesis type','Measurements','Photographs','Buttons and problem areas','Design preferences','Quote request','Funding selection','Declarations'], prohibitedData: ['Internal manufacturing files','Other customers','CAD settings','Internal costs','SHiREVault locations'] };
src/pages/ComponentDimensions.jsx:11: import MeasurementStationsTab from '@/components/dimensions/tabs/MeasurementStationsTab';
src/pages/ComponentDimensions.jsx:24: ['envelope', 'External Envelope', ExternalEnvelopeTab], ['stations', 'Measurement Stations', MeasurementStationsTab],
src/pages/CustomerDetail.jsx:21: const TABS = [['overview','Overview'],['limbs','Limb Profiles'],['prostheses','Prostheses & Components'],['measurements','Measurements'],['projects','Projects'],['quotes','Quotes'],['orders','Orders'],['covers','Completed Covers'],['history','History']];
src/pages/CustomerDetail.jsx:34: adapters.database.filter('MeasurementSession', { customer_uuid: uuid }),
src/pages/CustomerDetail.jsx:56: return <>Start New CoverRequest Measurements } />