
Enhanced the `SampleTracker` component to accept and utilize an `activePgroup` prop, allowing dynamic filtering of data based on the current project group. Adjusted related polling and data fetching logic to respond to changes in `activePgroup`. Removed excessive code from the test notebook for cleanup.
163 lines
5.7 KiB
TypeScript
163 lines
5.7 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { SamplesService } from '../../openapi';
|
|
|
|
interface Event {
|
|
event_type: string;
|
|
}
|
|
|
|
interface Sample {
|
|
id: number;
|
|
sample_name: string;
|
|
position: number;
|
|
puck_id: number;
|
|
crystalname?: string;
|
|
positioninpuck?: number;
|
|
events: Event[];
|
|
mount_count: number; // Add this
|
|
unmount_count: number; // Add this
|
|
}
|
|
|
|
interface Puck {
|
|
id: number;
|
|
puck_name: string;
|
|
puck_type: string;
|
|
puck_location_in_dewar: number;
|
|
dewar_id: number;
|
|
samples: Sample[];
|
|
}
|
|
|
|
interface SampleTrackerProps {
|
|
activePgroup: string;
|
|
}
|
|
|
|
const SampleTracker: React.FC<SampleTrackerProps> = ({ activePgroup }) => {
|
|
const [pucks, setPucks] = useState<Puck[]>([]);
|
|
const [hoveredSample, setHoveredSample] = useState<{ name: string; status: string } | null>(null);
|
|
|
|
|
|
// Fetch latest sample data
|
|
const fetchPucks = async () => {
|
|
try {
|
|
const data: Puck[] = await SamplesService.getAllPucksWithSamplesAndEventsSamplesPucksSamplesGet(activePgroup);
|
|
console.log('Fetched Pucks:', data);
|
|
setPucks(data);
|
|
} catch (error) {
|
|
console.error('Error fetching pucks', error);
|
|
}
|
|
};
|
|
|
|
|
|
// Polling logic using a 1-second interval
|
|
useEffect(() => {
|
|
fetchPucks();
|
|
const interval = setInterval(() => {
|
|
fetchPucks();
|
|
}, 1000);
|
|
|
|
return () => clearInterval(interval);
|
|
}, [activePgroup]);
|
|
|
|
|
|
const getSampleColor = (events: Event[] = []) => {
|
|
const hasMounted = events.some((e) => e.event_type === 'Mounted');
|
|
const hasUnmounted = events.some((e) => e.event_type === 'Unmounted');
|
|
const hasLost = events.some((e) => e.event_type === 'Lost');
|
|
const hasFailed = events.some((e) => e.event_type === 'Failed');
|
|
|
|
// Logic for status colors
|
|
if (hasFailed) return 'red'; // Failed samples: red
|
|
if (hasLost) return 'orange'; // Lost samples: orange
|
|
if (hasMounted && hasUnmounted) return 'green'; // Completed samples: green
|
|
if (hasMounted && !hasUnmounted) return 'blue'; // Currently mounted (Pending): blue
|
|
|
|
return 'gray'; // Default: gray
|
|
};
|
|
|
|
const getSampleStatus = (events: Event[] = []) => {
|
|
const hasMounted = events.some((e) => e.event_type === 'Mounted');
|
|
const hasUnmounted = events.some((e) => e.event_type === 'Unmounted');
|
|
const hasLost = events.some((e) => e.event_type === 'Lost');
|
|
const hasFailed = events.some((e) => e.event_type === 'Failed');
|
|
|
|
if (hasFailed) return 'Failed';
|
|
if (hasLost) return 'Lost';
|
|
if (hasMounted && !hasUnmounted) return 'In Progress';
|
|
if (hasMounted && hasUnmounted) return 'Completed';
|
|
|
|
return 'Pending';
|
|
};
|
|
|
|
return (
|
|
<div className="sample-tracker-container">
|
|
<div className="sample-tracker">
|
|
<div className="header-band">
|
|
<h2>Sample Tracker</h2>
|
|
</div>
|
|
<div className="pucks-container">
|
|
{pucks.map((puck) => (
|
|
<div key={puck.id} className="puck-column">
|
|
<div className="puck-label" style={{ fontSize: '6px' }}>
|
|
{puck.puck_name.split('').map((char, i) => (
|
|
<span key={i} style={{ fontSize: '10px' }}>
|
|
{char}
|
|
</span>
|
|
))}
|
|
</div>
|
|
<div className="samples">
|
|
{Array.from({ length: 16 }).map((_, index) => {
|
|
const sample = puck.samples.find((s) => s.position === index + 1);
|
|
const status = sample ? getSampleStatus(sample.events) : '';
|
|
return (
|
|
<div
|
|
key={index}
|
|
className="sample-dot"
|
|
style={{
|
|
backgroundColor: sample ? getSampleColor(sample.events) : 'transparent',
|
|
border: sample && sample.events.some((e) => e.event_type === 'Lost')
|
|
? '1px solid red'
|
|
: '1px solid lightgray',
|
|
position: 'relative', // Add for overlay positioning
|
|
}}
|
|
onMouseEnter={() =>
|
|
sample && setHoveredSample({ name: sample.sample_name, status })
|
|
}
|
|
onMouseLeave={() => setHoveredSample(null)}
|
|
>
|
|
{sample && sample.mount_count > 0 && ( // Render only if mount_count > 0
|
|
<span
|
|
style={{
|
|
position: 'absolute',
|
|
top: '50%',
|
|
left: '50%',
|
|
transform: 'translate(-50%, -50%)',
|
|
color: 'white',
|
|
fontSize: '8px',
|
|
fontWeight: 'bold',
|
|
}}
|
|
>
|
|
{sample.mount_count}
|
|
</span>
|
|
)}
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
{hoveredSample && (
|
|
<div className="tooltip">
|
|
<p>
|
|
<strong>Name:</strong> {hoveredSample.name}
|
|
</p>
|
|
<p>
|
|
<strong>Status:</strong> {hoveredSample.status}
|
|
</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default SampleTracker; |