Add option to change the dewar status
This commit is contained in:
@ -1,15 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Stepper, Step, StepLabel, StepIconProps, Typography } from '@mui/material';
|
||||
import React, { useState } from 'react';
|
||||
import { Stepper, Step, StepLabel, StepIconProps, Typography, Menu, MenuItem } from '@mui/material';
|
||||
import AirplanemodeActiveIcon from '@mui/icons-material/AirplanemodeActive';
|
||||
import StoreIcon from '@mui/icons-material/Store';
|
||||
import RecycleIcon from '@mui/icons-material/Restore';
|
||||
import { Dewar } from "../../openapi";
|
||||
import { Dewar, DewarsService } from "../../openapi";
|
||||
import { STATUS_TO_STEP, DewarStatus, getStatusStepIndex, determineIconColor } from './statusUtils'; // Utilities moved to a new file
|
||||
|
||||
const ICON_STYLE = { width: 24, height: 24 };
|
||||
|
||||
// Define the possible statuses
|
||||
type DewarStatus = 'In Preparation' | 'Ready for Shipping' | 'Shipped' | 'Not Arrived' | 'Arrived' | 'Returned' | 'Delayed';
|
||||
|
||||
// Inline SVG Component
|
||||
const BottleIcon: React.FC<{ fill: string }> = ({ fill }) => (
|
||||
<svg fill={fill} height="24px" width="24px" version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276.777 276.777">
|
||||
@ -17,21 +15,15 @@ const BottleIcon: React.FC<{ fill: string }> = ({ fill }) => (
|
||||
</svg>
|
||||
);
|
||||
|
||||
// Define types for icons mapping.
|
||||
const ICONS: { [key: number]: React.ReactElement } = {
|
||||
0: <BottleIcon fill="grey" />,
|
||||
1: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'blue' }} />, // 'Ready for Shipping' -> Active
|
||||
2: <StoreIcon style={ICON_STYLE} />, // 'Not Arrived'
|
||||
3: <RecycleIcon style={ICON_STYLE} />, // 'Returned'
|
||||
4: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'green' }} />, // 'Shipped' - Active
|
||||
5: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'orange' }} />, // 'Delayed'
|
||||
1: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'blue' }} />,
|
||||
2: <StoreIcon style={ICON_STYLE} />,
|
||||
3: <RecycleIcon style={ICON_STYLE} />,
|
||||
4: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'green' }} />,
|
||||
5: <AirplanemodeActiveIcon style={{ ...ICON_STYLE, color: 'orange' }} />,
|
||||
};
|
||||
|
||||
// Define StepIconContainer to accept correct props and handle typing better.
|
||||
interface StepIconContainerProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||
color: string;
|
||||
}
|
||||
|
||||
const StepIconContainer: React.FC<StepIconContainerProps> = ({ color, children, ...rest }) => (
|
||||
<div style={{ color }} {...rest}>
|
||||
{children}
|
||||
@ -41,26 +33,83 @@ const StepIconContainer: React.FC<StepIconContainerProps> = ({ color, children,
|
||||
type StepIconComponentProps = {
|
||||
icon: number;
|
||||
dewar: Dewar;
|
||||
isSelected: boolean;
|
||||
} & StepIconProps;
|
||||
|
||||
const StepIconComponent = ({ icon, dewar, ...props }: StepIconComponentProps) => {
|
||||
const { iconIndex, color} = getIconProperties(icon, dewar);
|
||||
const StepIconComponent = ({ icon, dewar, isSelected, ...props }: StepIconComponentProps) => {
|
||||
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
||||
const handleIconEnter = (event: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (isSelected && icon === 0) {
|
||||
setAnchorEl(event.currentTarget);
|
||||
}
|
||||
};
|
||||
const handleIconLeave = () => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
// Adjust icon color for the bottle especially since it's an SVG element
|
||||
const handleStatusChange = async (status: DewarStatus) => {
|
||||
try {
|
||||
const today = new Date().toISOString().split('T')[0];
|
||||
const payload = {
|
||||
dewar_id: dewar.id,
|
||||
dewar_name: dewar.dewar_name,
|
||||
tracking_number: dewar.tracking_number,
|
||||
number_of_pucks: dewar.number_of_pucks,
|
||||
number_of_samples: dewar.number_of_samples,
|
||||
status: status,
|
||||
ready_date: status === 'Ready for Shipping' ? today : dewar.ready_date,
|
||||
shipping_date: dewar.shipping_date,
|
||||
arrival_date: dewar.arrival_date,
|
||||
returning_date: dewar.returning_date,
|
||||
qrcode: dewar.qrcode,
|
||||
return_address_id: dewar.return_address_id,
|
||||
contact_person_id: dewar.contact_person_id,
|
||||
};
|
||||
|
||||
await DewarsService.updateDewarDewarsDewarIdPut(dewar.id || '', payload);
|
||||
setAnchorEl(null);
|
||||
} catch (error) {
|
||||
console.error('Failed to update dewar status:', error);
|
||||
alert('Failed to update dewar status. Please try again.');
|
||||
}
|
||||
};
|
||||
|
||||
const { iconIndex, color } = getIconProperties(icon, dewar);
|
||||
const IconComponent = ICONS[iconIndex];
|
||||
const iconProps = iconIndex === 0 ? { fill: color } : {};
|
||||
|
||||
return (
|
||||
<StepIconContainer color={color} {...props}>
|
||||
{IconComponent
|
||||
? React.cloneElement(IconComponent, iconProps)
|
||||
: <Typography variant="body2" color="error">Invalid icon</Typography>
|
||||
}
|
||||
</StepIconContainer>
|
||||
<div
|
||||
onMouseEnter={handleIconEnter}
|
||||
onMouseLeave={handleIconLeave}
|
||||
style={{ position: 'relative' }}
|
||||
>
|
||||
<StepIconContainer color={color} {...props}>
|
||||
{IconComponent
|
||||
? React.cloneElement(IconComponent, iconProps)
|
||||
: <Typography variant="body2" color="error">Invalid icon</Typography>
|
||||
}
|
||||
</StepIconContainer>
|
||||
|
||||
<Menu
|
||||
anchorEl={anchorEl}
|
||||
open={Boolean(anchorEl)}
|
||||
onClose={handleIconLeave}
|
||||
MenuListProps={{
|
||||
onMouseEnter: () => setAnchorEl(anchorEl),
|
||||
onMouseLeave: handleIconLeave,
|
||||
}}
|
||||
>
|
||||
{['Ready for Shipping'].map((status) => (
|
||||
<MenuItem key={status} onClick={() => handleStatusChange(status as DewarStatus)}>
|
||||
{status}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
// Extracted function to determine icon properties
|
||||
const getIconProperties = (icon: number, dewar: Dewar) => {
|
||||
const status = dewar.status as DewarStatus;
|
||||
const iconIndex = status === 'Delayed' && icon === 1 ? 5 : icon;
|
||||
@ -69,40 +118,24 @@ const getIconProperties = (icon: number, dewar: Dewar) => {
|
||||
return { iconIndex, color, fill };
|
||||
};
|
||||
|
||||
const STATUS_TO_STEP: Record<DewarStatus, number> = {
|
||||
'In Preparation': 0,
|
||||
'Ready for Shipping': 1,
|
||||
'Shipped': 1,
|
||||
'Delayed': 1,
|
||||
'Not Arrived': 2,
|
||||
'Arrived': 2,
|
||||
'Returned': 3
|
||||
};
|
||||
|
||||
const getStatusStepIndex = (status: DewarStatus): number => STATUS_TO_STEP[status];
|
||||
|
||||
const determineIconColor = (iconIndex: number, status: DewarStatus): string => {
|
||||
const statusIndex = getStatusStepIndex(status);
|
||||
if (status === 'Delayed' && iconIndex === 1) {
|
||||
return 'orange';
|
||||
}
|
||||
return iconIndex <= statusIndex ? (iconIndex === statusIndex ? (status === 'Shipped' ? 'green' : 'blue') : 'green') : 'grey';
|
||||
};
|
||||
|
||||
// Define your steps
|
||||
const steps = ['In-House', 'Transit', 'At SLS', 'Returned'];
|
||||
|
||||
const CustomStepper = ({ dewar }: { dewar: Dewar }) => {
|
||||
// Determine the current active step
|
||||
type CustomStepperProps = {
|
||||
dewar: Dewar;
|
||||
selectedDewarId: string | null;
|
||||
}
|
||||
|
||||
const CustomStepper = ({ dewar, selectedDewarId }: CustomStepperProps) => {
|
||||
const activeStep = getStatusStepIndex(dewar.status as DewarStatus);
|
||||
const isSelected = dewar.id === selectedDewarId;
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', justifyContent: 'center', width: '100%' /* Or any other preferred width */, maxWidth: '600px', margin: '0 auto' }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'center', width: '100%', maxWidth: '600px', margin: '0 auto' }}>
|
||||
<Stepper alternativeLabel activeStep={activeStep} style={{ width: '100%' }}>
|
||||
{steps.map((label, index) => (
|
||||
<Step key={label}>
|
||||
<StepLabel
|
||||
StepIconComponent={(stepProps) => <StepIconComponent {...stepProps} icon={index} dewar={dewar} />}
|
||||
StepIconComponent={(stepProps) => <StepIconComponent {...stepProps} icon={index} dewar={dewar} isSelected={isSelected} />}
|
||||
>
|
||||
{label}
|
||||
</StepLabel>
|
||||
|
@ -311,7 +311,7 @@ const ShipmentDetails: React.FC<ShipmentDetailsProps> = ({
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between'
|
||||
}}>
|
||||
<CustomStepper dewar={dewar} />
|
||||
<CustomStepper dewar={dewar} selectedDewarId={localSelectedDewar?.id} />
|
||||
|
||||
</Box>
|
||||
|
||||
|
24
frontend/src/components/statusUtils.ts
Normal file
24
frontend/src/components/statusUtils.ts
Normal file
@ -0,0 +1,24 @@
|
||||
// statusUtils.ts
|
||||
// Utility functions to handle dewar statuses and icon colors
|
||||
|
||||
export type DewarStatus = 'In Preparation' | 'Ready for Shipping' | 'Shipped' | 'Not Arrived' | 'Arrived' | 'Returned' | 'Delayed';
|
||||
|
||||
export const STATUS_TO_STEP: Record<DewarStatus, number> = {
|
||||
'In Preparation': 0,
|
||||
'Ready for Shipping': 1,
|
||||
'Shipped': 1,
|
||||
'Delayed': 1,
|
||||
'Not Arrived': 2,
|
||||
'Arrived': 2,
|
||||
'Returned': 3
|
||||
};
|
||||
|
||||
export const getStatusStepIndex = (status: DewarStatus): number => STATUS_TO_STEP[status];
|
||||
|
||||
export const determineIconColor = (iconIndex: number, status: DewarStatus): string => {
|
||||
const statusIndex = getStatusStepIndex(status);
|
||||
if (status === 'Delayed' && iconIndex === 1) {
|
||||
return 'orange';
|
||||
}
|
||||
return iconIndex <= statusIndex ? (iconIndex === statusIndex ? (status === 'Shipped' ? 'green' : 'blue') : 'green') : 'grey';
|
||||
};
|
Reference in New Issue
Block a user