adding logistics app

This commit is contained in:
GotthardG
2024-11-14 09:57:01 +01:00
parent 8f7c90bab0
commit ca11a359f9
6 changed files with 361 additions and 0 deletions

View File

@ -0,0 +1,117 @@
import React, { useState } from 'react';
import QrScanner from 'react-qr-scanner';
import { Box, Button } from '@mui/material';
import styled from 'styled-components';
import Modal from '../components/Modal'; // Adjust the import path according to your project structure
const ScannerContainer = styled(Box)`
position: relative;
width: 100%;
height: 100%;
`;
const ScannerFrame = styled.div`
position: absolute;
top: 50%;
left: 50%;
width: 60%;
height: 60%;
transform: translate(-50%, -50%);
border: 3px solid green;
box-sizing: border-box;
z-index: 2;
`;
const BlurOverlay = styled.div`
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
&::before,
&::after {
content: '';
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
position: absolute;
pointer-events: none;
}
&::before {
top: 0;
left: 0;
right: 0;
height: calc(50% - 30%);
}
&::after {
bottom: 0;
left: 0;
right: 0;
height: calc(50% - 30%);
}
div {
position: absolute;
top: calc(50% - 30%);
bottom: calc(50% - 30%);
width: calc(50% - 30%);
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.7);
pointer-events: none;
}
div:first-child {
left: 0;
}
div:last-child {
right: 0;
}
`;
interface ScannerModalProps {
open: boolean;
onClose: () => void;
onScan: (data: { text: string } | null) => void;
slotQRCodes: string[]; // Required prop
}
const ScannerModal: React.FC<ScannerModalProps> = ({ open, onClose, onScan, slotQRCodes }) => {
const handleScan = (data: { text: string } | null) => {
if (data) {
onScan(data);
}
};
const handleError = (err: any) => {
console.error(err);
};
const constraints = {
video: { facingMode: { exact: "environment" } },
};
return (
<Modal open={open} onClose={onClose} title="Scan QR Code">
<ScannerContainer>
<QrScanner
delay={300}
onError={handleError}
onScan={handleScan}
style={{ width: '100%' }}
constraints={constraints}
/>
<BlurOverlay>
<div />
<div />
</BlurOverlay>
<ScannerFrame />
</ScannerContainer>
</Modal>
);
}
export default ScannerModal;

View File

@ -0,0 +1,73 @@
import React from 'react';
import { Box } from '@mui/material';
import { QrCode, AcUnit, AccessTime } from '@mui/icons-material';
import styled from 'styled-components';
interface SlotProps {
data: SlotData;
onSelect: (slot: SlotData) => void;
isSelected: boolean;
}
export interface SlotData {
id: string;
occupied: boolean;
needsRefill: boolean;
timeUntilRefill: string;
}
const SlotContainer = styled(Box)<{ occupied: boolean, isSelected: boolean }>`
width: 90px;
height: 180px;
margin: 10px;
background-color: ${(props) => (props.occupied ? '#f0f0f0' : '#ffffff')};
border: ${(props) => (props.isSelected ? '3px solid blue' : '2px solid #aaaaaa')};
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
padding: 10px;
box-sizing: border-box;
cursor: pointer;
position: relative;
`;
const SlotNumber = styled.div`
font-size: 20px;
font-weight: bold;
`;
const QrCodeIcon = styled(QrCode)`
font-size: 40px;
color: #aaaaaa;
`;
const RefillIcon = styled(AcUnit)`
font-size: 20px;
color: #1e88e5;
margin-top: auto;
`;
const ClockIcon = styled(AccessTime)`
font-size: 20px;
color: #ff6f00;
`;
const Slot: React.FC<SlotProps> = ({ data, onSelect, isSelected }) => {
return (
<SlotContainer occupied={data.occupied} onClick={() => onSelect(data)} isSelected={isSelected}>
<SlotNumber>{data.id}</SlotNumber>
<QrCodeIcon />
{data.occupied && (
<>
{data.needsRefill && <RefillIcon titleAccess="Needs Refill" />}
<ClockIcon titleAccess={`Time until refill: ${data.timeUntilRefill}`} />
</>
)}
</SlotContainer>
);
}
export default Slot;

View File

@ -0,0 +1,111 @@
import React, { useState } from 'react';
import { Box, Typography } from '@mui/material';
import styled from 'styled-components';
import Slot, { SlotData } from './Slots';
const StorageContainer = styled(Box)`
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
width: 100%;
`;
const StorageWrapper = styled.div`
display: flex;
flex-wrap: wrap;
justify-content: center;
width: 90%;
background-color: #dcdcdc;
padding: 20px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
margin-bottom: 20px;
`;
interface StorageProps {
name: string;
selectedSlot: string | null;
}
const storageSlotsData: { [key: string]: SlotData[] } = {
"X06SA-storage": [
{ id: "A1-X06SA", occupied: false, needsRefill: false, timeUntilRefill: '' },
{ id: "A2-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "A3-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "A4-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "A5-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "B1-X06SA", occupied: false, needsRefill: false, timeUntilRefill: '' },
{ id: "B2-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "B3-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "B4-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "B5-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "C1-X06SA", occupied: false, needsRefill: false, timeUntilRefill: '' },
{ id: "C2-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "C3-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "C4-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "C5-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "D1-X06SA", occupied: false, needsRefill: false, timeUntilRefill: '' },
{ id: "D2-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "D3-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "D4-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "D5-X06SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
],
"X10SA-storage": [
{ id: "A1-X10SA", occupied: false, needsRefill: false, timeUntilRefill: '' },
{ id: "A2-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "A3-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "A4-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "A5-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "B1-X10SA", occupied: false, needsRefill: false, timeUntilRefill: '' },
{ id: "B2-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "B3-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "B4-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "B5-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "C1-X10SA", occupied: false, needsRefill: false, timeUntilRefill: '' },
{ id: "C2-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "C3-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "C4-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "C5-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "D1-X10SA", occupied: false, needsRefill: false, timeUntilRefill: '' },
{ id: "D2-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "D3-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "D4-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
{ id: "D5-X10SA", occupied: true, needsRefill: true, timeUntilRefill: '12h' },
],
"Novartis-Box": [
{ id: "NB1", occupied: true, needsRefill: true, timeUntilRefill: '6h' },
{ id: "NB2", occupied: true, needsRefill: true, timeUntilRefill: '6h' },
{ id: "NB3", occupied: true, needsRefill: true, timeUntilRefill: '6h' },
{ id: "NB4", occupied: true, needsRefill: true, timeUntilRefill: '6h' },
{ id: "NB5", occupied: true, needsRefill: true, timeUntilRefill: '6h' },
{ id: "NB6", occupied: true, needsRefill: true, timeUntilRefill: '6h' },
],
};
const Storage: React.FC<StorageProps> = ({ name, selectedSlot }) => {
const [highlightedSlot, setHighlightedSlot] = useState<SlotData | null>(null);
const handleSlotSelect = (slot: SlotData) => {
setHighlightedSlot(slot);
};
return (
<StorageContainer>
<Typography variant="h5">{name} Slots</Typography>
<StorageWrapper>
{storageSlotsData[name].map((slot) => (
<Slot key={slot.id} data={slot} onSelect={handleSlotSelect} isSelected={selectedSlot === slot.id} />
))}
</StorageWrapper>
{highlightedSlot && (
<Typography variant="subtitle1">
Selected Slot: {highlightedSlot.id}
</Typography>
)}
</StorageContainer>
);
}
export default Storage;

13
logistics/src/react-qr-reader.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
declare module 'react-qr-reader' {
import { Component } from 'react';
interface QrReaderProps {
delay?: number;
onError?: (error: any) => void;
onScan?: (data: string | null) => void;
style?: React.CSSProperties;
facingMode?: 'user' | 'environment';
}
export default class QrReader extends Component<QrReaderProps> {}
}