now with a working countdowntimer for each dewar
This commit is contained in:
parent
db610da588
commit
c7e6c0390e
@ -156,6 +156,5 @@ class LogisticsEvent(Base):
|
|||||||
slot_id = Column(Integer, ForeignKey('slots.id')) # corrected table name
|
slot_id = Column(Integer, ForeignKey('slots.id')) # corrected table name
|
||||||
event_type = Column(String, index=True)
|
event_type = Column(String, index=True)
|
||||||
timestamp = Column(DateTime, default=datetime.utcnow)
|
timestamp = Column(DateTime, default=datetime.utcnow)
|
||||||
action_details = Column(String)
|
|
||||||
dewar = relationship("Dewar", back_populates="events")
|
dewar = relationship("Dewar", back_populates="events")
|
||||||
slot = relationship("Slot", back_populates="events")
|
slot = relationship("Slot", back_populates="events")
|
@ -103,7 +103,6 @@ async def refill_dewar(qr_code: str, db: Session = Depends(get_db)):
|
|||||||
slot_id=None,
|
slot_id=None,
|
||||||
event_type="refill",
|
event_type="refill",
|
||||||
timestamp=now,
|
timestamp=now,
|
||||||
action_details=f"{dewar.unique_id} refilled"
|
|
||||||
)
|
)
|
||||||
db.add(new_event)
|
db.add(new_event)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
@ -3,6 +3,7 @@ import { Box, Typography, Button, Alert } from '@mui/material';
|
|||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
import LocalGasStationIcon from '@mui/icons-material/LocalGasStation';
|
import LocalGasStationIcon from '@mui/icons-material/LocalGasStation';
|
||||||
import CountdownTimer from './CountdownTimer';
|
import CountdownTimer from './CountdownTimer';
|
||||||
|
import QRCode from 'react-qr-code';
|
||||||
|
|
||||||
export interface SlotData {
|
export interface SlotData {
|
||||||
id: string;
|
id: string;
|
||||||
@ -20,7 +21,7 @@ interface SlotProps {
|
|||||||
data: SlotData;
|
data: SlotData;
|
||||||
isSelected: boolean;
|
isSelected: boolean;
|
||||||
onSelect: (data: SlotData) => void;
|
onSelect: (data: SlotData) => void;
|
||||||
onRefillDewar: (qr_code?: string) => Promise<void>;
|
onRefillDewar: (dewar_unique_id?: string) => Promise<void>;
|
||||||
reloadSlots: () => Promise<void>;
|
reloadSlots: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +34,7 @@ const StyledSlot = styled(Box)<StyledSlotProps>`
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
margin: 8px;
|
margin: 8px;
|
||||||
width: 150px;
|
width: 150px;
|
||||||
height: 220px;
|
height: 260px;
|
||||||
background-color: ${({ isSelected, isOccupied }) =>
|
background-color: ${({ isSelected, isOccupied }) =>
|
||||||
isSelected ? '#3f51b5' : isOccupied ? '#f44336' : '#4caf50'};
|
isSelected ? '#3f51b5' : isOccupied ? '#f44336' : '#4caf50'};
|
||||||
color: white;
|
color: white;
|
||||||
@ -51,9 +52,48 @@ const StyledSlot = styled(Box)<StyledSlotProps>`
|
|||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const QRCodeContainer = styled.div`
|
||||||
|
padding: 8px;
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const BottleIcon: React.FC<{ fillHeight: number }> = ({ fillHeight }) => {
|
||||||
|
const pixelHeight = (276.777 * fillHeight) / 100;
|
||||||
|
const yPosition = 276.777 - pixelHeight;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<svg height="100px" width="50px" 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"/>
|
||||||
|
<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 Slot: React.FC<SlotProps> = ({ data, isSelected, onSelect, onRefillDewar, reloadSlots }) => {
|
||||||
|
const calculateFillHeight = (timeUntilRefill?: number) => {
|
||||||
|
if (timeUntilRefill === undefined || timeUntilRefill <= 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
const maxTime = 86400;
|
||||||
|
return Math.min((timeUntilRefill / maxTime) * 100, 100);
|
||||||
|
};
|
||||||
|
|
||||||
|
const fillHeight = calculateFillHeight(data.time_until_refill);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (data.time_until_refill !== undefined) {
|
||||||
console.log(`Updated time_until_refill: ${data.time_until_refill}`);
|
console.log(`Updated time_until_refill: ${data.time_until_refill}`);
|
||||||
|
}
|
||||||
}, [data.time_until_refill]);
|
}, [data.time_until_refill]);
|
||||||
|
|
||||||
const handleRefill = async () => {
|
const handleRefill = async () => {
|
||||||
@ -74,11 +114,18 @@ const Slot: React.FC<SlotProps> = ({ data, isSelected, onSelect, onRefillDewar,
|
|||||||
>
|
>
|
||||||
<Typography variant="h6">{label}</Typography>
|
<Typography variant="h6">{label}</Typography>
|
||||||
{dewar_name && <Typography variant="body2">{`Dewar: ${dewar_name}`}</Typography>}
|
{dewar_name && <Typography variant="body2">{`Dewar: ${dewar_name}`}</Typography>}
|
||||||
{dewar_unique_id && <Typography variant="body2">{`ID: ${dewar_unique_id}`}</Typography>}
|
{dewar_unique_id && (
|
||||||
{needs_refill && <LocalGasStationIcon />}
|
<QRCodeContainer>
|
||||||
{dewar_unique_id && time_until_refill !== undefined && time_until_refill !== -1 && (
|
<QRCode value={dewar_unique_id} size={64} />
|
||||||
<CountdownTimer key={dewar_unique_id} totalSeconds={time_until_refill} />
|
</QRCodeContainer>
|
||||||
)}
|
)}
|
||||||
|
{needs_refill && <LocalGasStationIcon />}
|
||||||
|
{dewar_unique_id && (
|
||||||
|
<BottleIcon fillHeight={fillHeight} />
|
||||||
|
)}
|
||||||
|
{(dewar_unique_id && time_until_refill !== undefined && time_until_refill !== -1) ? (
|
||||||
|
<CountdownTimer key={dewar_unique_id} totalSeconds={time_until_refill} />
|
||||||
|
) : null}
|
||||||
{needs_refill && (
|
{needs_refill && (
|
||||||
<Button onClick={handleRefill} sx={{ mt: 1, color: 'white' }}>
|
<Button onClick={handleRefill} sx={{ mt: 1, color: 'white' }}>
|
||||||
Refill
|
Refill
|
||||||
|
@ -28,7 +28,7 @@ interface StorageProps {
|
|||||||
selectedSlot: string | null;
|
selectedSlot: string | null;
|
||||||
slotsData: SlotData[];
|
slotsData: SlotData[];
|
||||||
onSelectSlot: (slot: SlotData) => void;
|
onSelectSlot: (slot: SlotData) => void;
|
||||||
onRefillDewar: (slot: SlotData) => void; // Adjusted this prop to pass SlotData object
|
onRefillDewar: (slot: SlotData) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Storage: React.FC<StorageProps> = ({ name, selectedSlot, slotsData, onSelectSlot, onRefillDewar }) => {
|
const Storage: React.FC<StorageProps> = ({ name, selectedSlot, slotsData, onSelectSlot, onRefillDewar }) => {
|
||||||
@ -47,7 +47,7 @@ const Storage: React.FC<StorageProps> = ({ name, selectedSlot, slotsData, onSele
|
|||||||
data={slot}
|
data={slot}
|
||||||
onSelect={handleSlotSelect}
|
onSelect={handleSlotSelect}
|
||||||
isSelected={selectedSlot === slot.qr_code}
|
isSelected={selectedSlot === slot.qr_code}
|
||||||
onRefillDewar={onRefillDewar} // Pass the refill handler to Slot component
|
onRefillDewar={onRefillDewar}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</StorageWrapper>
|
</StorageWrapper>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user