Refactor slot UI and backend refill logic.

Updated slot styling for improved user feedback and responsiveness. Simplified LN2 representation with a new level bar and adjusted refill logic to a 48-hour interval. Removed unused functions for cleaner backend code.
This commit is contained in:
GotthardG 2024-12-20 13:31:17 +01:00
parent 27d2717a05
commit 0108719a84

View File

@ -81,50 +81,52 @@ const StyledSlot = styled(Box)<{ isSelected: boolean; isOccupied: boolean; atBea
}
`;
const BottleIcon: React.FC<{ fill: string }> = ({ fill }) => (
<svg fill={fill} height="48px" width="48px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276.777 276.777">
<path d="M190.886,82.273c-3.23-2.586-7.525-7.643-7.525-21.639V43h8.027V0h-106v43h8.027v17.635c0,11.66-1.891,17.93-6.524,21.639 c-21.813,17.459-31.121,36.748-31.121,64.5v100.088c0,16.496,13.42,29.916,29.916,29.916h105.405 c16.496,0,29.916-13.42,29.916-29.916V146.773C221.007,121.103,210.029,97.594,190.886,82.273z M191.007,246.777H85.77V146.773 c0-18.589,5.199-29.339,19.867-41.078c15.758-12.612,17.778-30.706,17.778-45.061V43h29.945v17.635 c0,19.927,6.318,35.087,18.779,45.061c11.99,9.597,18.867,24.568,18.867,41.078V246.777z"/>
</svg>
);
const LN2LevelBar: React.FC<{ level: number }> = ({ level }) => {
const barHeight = `${level}%`; // Proportional height based on LN2 level
const barColor = level > 50 ? '#00aeed' : level > 20 ? '#ff9800' : '#f44336'; // Green > Yellow > Red
const BottleIcon: React.FC<{ fillHeight: number }> = ({ fillHeight }) => {
const pixelHeight = (276.777 * fillHeight) / 100; // Calculate height for the LN2 level
const yPosition = 276.777 - pixelHeight; // Adjust level to "fill" the bottle from the bottom
return (
<Box
width="7px"
height="100%"
border="1px solid lightgray"
display="flex"
alignItems="center"
justifyContent="flex-end" // Align contents to the bottom
flexDirection="column" // Needed for vertical alignment
>
{/* The filled portion of the bar */}
<Box
width="100%"
height={barHeight} // Set height proportional to the level
bgcolor={barColor}
transition="height 0.3s ease" // Smooth transition when level changes
<svg height="80px" width="40px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276.777 276.777">
<defs>
<clipPath id="bottle-clip">
<path
d="M190.886,82.273c-3.23-2.586-7.525-7.643-7.525-21.639V43h8.027V0h-106v43h8.027v17.635
c0,11.66-1.891,17.93-6.524,21.639c-21.813,17.459-31.121,36.748-31.121,64.5v100.088c0,16.496,13.42,29.916,29.916,29.916
h105.405c16.496,0,29.916-13.42,29.916-29.916V146.773C221.007,121.103,210.029,97.594,190.886,82.273z"
/>
</clipPath>
</defs>
<path
fill="lightgray"
d="M190.886,82.273c-3.23-2.586-7.525-7.643-7.525-21.639V43h8.027V0h-106v43h8.027v17.635
c0,11.66-1.891,17.93-6.524,21.639c-21.813,17.459-31.121,36.748-31.121,64.5v100.088c0,16.496,13.42,29.916,29.916,29.916
h105.405c16.496,0,29.916-13.42,29.916-29.916V146.773C221.007,121.103,210.029,97.594,190.886,82.273z"
/>
</Box>
<rect x="0" y={yPosition} width="100%" height={pixelHeight} fill="#00bfff" clipPath="url(#bottle-clip)" />
</svg>
);
};
const Slot: React.FC<SlotProps> = ({ data, isSelected, onSelect, onRefillDewar, reloadSlots }) => {
const { id, qr_code, label, qr_base, occupied, needs_refill, time_until_refill, dewar_unique_id, dewar_name, beamlineLocation, retrieved, retrievedTimestamp } = data;
const { id, qr_code, label, occupied, needs_refill, time_until_refill, dewar_unique_id, dewar_name, beamlineLocation, retrieved } = data;
// Calculate fill height for the LN2 bottle level (only visual, no percentage displayed)
const calculateFillHeight = (timeUntilRefill?: number) => {
if (timeUntilRefill === undefined || timeUntilRefill <= 0) {
return 0; // Return 0% if time is undefined or negative
}
const maxTime = 172800; // Example maximum time (2 days in seconds)
const maxTime = 172800; // Example maximum time for calculating the level (2 days in seconds)
return Math.min((timeUntilRefill / maxTime) * 100, 100);
};
const fillHeight = calculateFillHeight(time_until_refill);
// Determine slot state
const isSpecificBeamline = beamlineLocation === 'X10SA' || beamlineLocation === 'X06SA' || beamlineLocation === 'X06DA';
const isRetrieved = retrieved && !isSpecificBeamline;
const needsRefillSoon = occupied && (time_until_refill !== undefined && time_until_refill <= 10800);
// Refill handler
const handleRefill = async () => {
if (dewar_unique_id) {
await onRefillDewar(dewar_unique_id);
@ -132,11 +134,6 @@ const Slot: React.FC<SlotProps> = ({ data, isSelected, onSelect, onRefillDewar,
}
};
const isSpecificBeamline = beamlineLocation === 'X10SA' || beamlineLocation === 'X06SA' || beamlineLocation === 'X06DA';
const isRetrieved = retrieved === true && !isSpecificBeamline;
const needsRefillSoon = occupied && (time_until_refill !== undefined && time_until_refill <= 10800);
return (
<StyledSlot
isSelected={isSelected}
@ -144,91 +141,57 @@ const Slot: React.FC<SlotProps> = ({ data, isSelected, onSelect, onRefillDewar,
atBeamline={isSpecificBeamline}
isRetrieved={isRetrieved}
needsRefillSoon={needsRefillSoon}
onClick={() => onSelect(data)}
onClick={() => onSelect(data)} // Select slot on click
>
{/* Top Content: Label and dewar name */}
<Box textAlign="center" width="100%" mb={2}>
<Typography variant="h6">{label}</Typography>
{dewar_name && <Typography variant="body2">{dewar_name}</Typography>}
</Box>
{/* Icons Section: Location and Bottle Icon */}
<Box
display="flex"
alignItems="stretch"
height="100%"
flexDirection="row"
alignItems="center"
gap="16px"
justifyContent="center"
width="100%"
>
{/* LN2 Level Bar */}
{dewar_unique_id && (
<Box width="10px" height="100%" marginRight="8px">
<LN2LevelBar level={fillHeight} />
{/* Location Icon */}
{isSpecificBeamline && (
<Box display="flex" flexDirection="column" alignItems="center">
<LocationOnIcon style={{ fontSize: '36px', color: '#37ff00' }} />
<Typography variant="caption" style={{ fontWeight: 'bold', textAlign: 'center' }}>
{beamlineLocation}
</Typography>
</Box>
)}
{/* Main Content */}
<Box
flexGrow={1}
display="flex"
flexDirection="column"
alignItems="center"
justifyContent="space-between"
>
{/* Top-Centered Text */}
<Box textAlign="center" width="100%">
<Typography variant="h6">{label}</Typography>
{dewar_name && (
<Typography variant="body2">
{dewar_name}
</Typography>
)}
{/* Bottle Icon with LN2 Level */}
{dewar_unique_id && (
<Box display="flex" flexDirection="column" alignItems="center">
<BottleIcon fillHeight={fillHeight} />
</Box>
{/* Beamline Location Icon & Bottle Icon Next to Each Other */}
<Box
display="flex"
flexDirection="column"
alignItems="center"
marginTop="8px"
marginBottom="16px"
>
{/* Row for Icons */}
<Box display="flex" flexDirection="row" alignItems="center" gap="8px">
{isSpecificBeamline && (
<Box display="flex" flexDirection="column" alignItems="center">
<LocationOnIcon style={{ fontSize: '48px', color: '#37ff00' }} />
<Typography
variant="body2"
style={{
fontWeight: 'bold',
marginTop: '4px',
textAlign: 'center',
}}
>
{beamlineLocation}
</Typography>
</Box>
)}
{occupied && (
<BottleIcon fill="#ffffff" />
)}
</Box>
</Box>
{/* Refill Button */}
{needs_refill && (
<Button onClick={handleRefill} sx={{ mt: 1, color: 'white' }}>
Refill
</Button>
)}
{/* Countdown Timer */}
{dewar_unique_id && time_until_refill !== undefined && time_until_refill !== -1 ? (
<CountdownTimer key={dewar_unique_id} totalSeconds={time_until_refill} />
) : null}
{/* Warning */}
{occupied && time_until_refill === -1 && (
<Alert severity="warning">
This dewar has no recorded refill event. It needs to
be refilled.
</Alert>
)}
</Box>
)}
</Box>
{/* Refill Button Section */}
{needs_refill && (
<Button onClick={handleRefill} sx={{ mt: 1, color: 'white' }}>
Refill
</Button>
)}
{/* Countdown Timer */}
{dewar_unique_id && time_until_refill !== undefined && time_until_refill !== -1 && (
<CountdownTimer key={dewar_unique_id} totalSeconds={time_until_refill} />
)}
{/* Warnings */}
{occupied && time_until_refill === -1 && (
<Alert severity="warning">This dewar has no recorded refill event. It needs to be refilled.</Alert>
)}
</StyledSlot>
);
};