made some changes in the bin sensors to open a new dialog when the component being removed is the last one for a device so users can remove the device from the bin along with the component

This commit is contained in:
csawatzky 2026-04-14 15:29:59 -06:00
parent da5ef99a3e
commit 0556fe2d8b
5 changed files with 121 additions and 17 deletions

View file

@ -57,6 +57,8 @@ interface Props {
components?: Map<string, Component>; components?: Map<string, Component>;
setComponents?: React.Dispatch<React.SetStateAction<Map<string, Component>>>; setComponents?: React.Dispatch<React.SetStateAction<Map<string, Component>>>;
updateBinStatus?: (componentKeys: string[], removed?: boolean) => void; updateBinStatus?: (componentKeys: string[], removed?: boolean) => void;
componentDevices?: Map<string, number>
setComponentDevices?: React.Dispatch<React.SetStateAction<Map<string, number>>>;
} }
interface OpenState { interface OpenState {
@ -77,7 +79,9 @@ export default function BinActions(props: Props) {
refreshCallback, refreshCallback,
userID, userID,
components, components,
componentDevices,
setComponents, setComponents,
setComponentDevices,
updateBinStatus updateBinStatus
} = props; } = props;
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null); const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
@ -245,7 +249,9 @@ export default function BinActions(props: Props) {
open={openState.sensors} open={openState.sensors}
userID={userID} userID={userID}
components={components} components={components}
componentDevices={componentDevices}
setComponents={setComponents} setComponents={setComponents}
setComponentDevices={setComponentDevices}
updateBinStatus={updateBinStatus} updateBinStatus={updateBinStatus}
onClose={refresh => { onClose={refresh => {
if (refresh === true) { if (refresh === true) {

View file

@ -71,7 +71,9 @@ const useStyles = makeStyles((theme: Theme) => {
interface Props { interface Props {
components?: Map<string, Component>; components?: Map<string, Component>;
componentDevices?: Map<string, number>;
setComponents?: React.Dispatch<React.SetStateAction<Map<string, Component>>>; setComponents?: React.Dispatch<React.SetStateAction<Map<string, Component>>>;
setComponentDevices?: React.Dispatch<React.SetStateAction<Map<string, number>>>;
bin: string; bin: string;
binGrain: pond.Grain; binGrain: pond.Grain;
updateBinStatus?: (componentKeys: string[], removed?: boolean) => void; updateBinStatus?: (componentKeys: string[], removed?: boolean) => void;
@ -84,7 +86,7 @@ interface Option {
} }
export default function BinComponents(props: Props) { export default function BinComponents(props: Props) {
const { components, bin, setComponents, updateBinStatus, binGrain } = props; const { components, componentDevices, bin, setComponents, setComponentDevices, updateBinStatus, binGrain } = props;
const [{as}] = useGlobalState(); const [{as}] = useGlobalState();
const classes = useStyles(); const classes = useStyles();
const binAPI = useBinAPI(); const binAPI = useBinAPI();
@ -208,20 +210,54 @@ export default function BinComponents(props: Props) {
// } // }
// }, [selectedDevice, componentAPI, deviceComponents, snackbar]); // }, [selectedDevice, componentAPI, deviceComponents, snackbar]);
const removeComponent = (component: string) => { const removeComponent = (component: string, device?: number) => {
binAPI.removeComponent(bin, component, as).then(() => { console.log(device)
binAPI.removeComponent(bin, component, device, as).then(() => {
if (components && setComponents) { if (components && setComponents) {
if (components.delete(component)) { if (components.delete(component)) {
let newComponents = new Map(components); let newComponents = new Map(components);
setComponents(newComponents); setComponents(newComponents);
} }
} }
if(componentDevices && setComponentDevices) {
if (componentDevices.delete(component)) {
let newComponentDevices = new Map(componentDevices);
setComponentDevices(newComponentDevices);
}
}
snackbar.info("Component removed from bin"); snackbar.info("Component removed from bin");
}); });
setComponentToRemove(undefined); setComponentToRemove(undefined);
}; };
const removeComponentConfirmation = () => { const removeComponentConfirmation = (last?: boolean) => {
let lastComponent = false;
let devId: number | undefined;
if(last){
lastComponent = last
}else{
if (componentDevices && componentToRemove) {
const id = componentDevices.get(componentToRemove.key());
if (id !== undefined) {
devId = id
let count = 0;
for (const val of componentDevices.values()) {
if (val === devId) {
count++;
if (count > 1) {
break; // stop early once we know it's not the last
}
}
}
lastComponent = count === 1;
}
}
}
return ( return (
<ResponsiveDialog <ResponsiveDialog
fullScreen={false} fullScreen={false}
@ -229,19 +265,37 @@ export default function BinComponents(props: Props) {
onClose={() => setComponentToRemove(undefined)}> onClose={() => setComponentToRemove(undefined)}>
<DialogTitle>Remove {componentToRemove?.name()}?</DialogTitle> <DialogTitle>Remove {componentToRemove?.name()}?</DialogTitle>
<DialogContent> <DialogContent>
{lastComponent ?
<DialogContentText>
This component {componentToRemove?.name()} is the last component from its device. You can choose to remove only the component itself or the device along with it.
Leaving the device may have unexpected results if the device will continue to be used and it is recommended to remove it if the device is being moved to a new bin.
</DialogContentText>
:
<DialogContentText> <DialogContentText>
This will remove {componentToRemove?.name()} from this bin. If you don't have direct This will remove {componentToRemove?.name()} from this bin. If you don't have direct
access to this component or the device it is attached to, you will not be able to add it access to this component or the device it is attached to, you will not be able to add it
back. back.
</DialogContentText> </DialogContentText>
}
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={() => setComponentToRemove(undefined)} color="primary"> <Button onClick={() => setComponentToRemove(undefined)} color="primary">
Cancel Cancel
</Button> </Button>
{lastComponent
?
<React.Fragment>
<Button onClick={() => removeComponent(componentToRemove!.key())} color="primary">
Remove Component
</Button>
<Button onClick={() => removeComponent(componentToRemove!.key(), devId)} color="primary">
Remove Component And Device
</Button>
</React.Fragment> :
<Button onClick={() => removeComponent(componentToRemove!.key())} color="primary"> <Button onClick={() => removeComponent(componentToRemove!.key())} color="primary">
Remove Remove
</Button> </Button>
}
</DialogActions> </DialogActions>
</ResponsiveDialog> </ResponsiveDialog>
); );
@ -256,7 +310,7 @@ export default function BinComponents(props: Props) {
<DialogTitle>Remove All Components?</DialogTitle> <DialogTitle>Remove All Components?</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>
This will remove All attached components from this bin. If you don't have direct access This will remove All attached components and devices from this bin. If you don't have direct access
to these components or the devices they are attached to, you will not be able to add to these components or the devices they are attached to, you will not be able to add
them back. them back.
</DialogContentText> </DialogContentText>
@ -320,6 +374,12 @@ export default function BinComponents(props: Props) {
setComponents(newComponents); setComponents(newComponents);
} }
} }
if(componentDevices && setComponentDevices) {
if (componentDevices.set(component.key(), device)) {
let newComponentDevices = new Map(componentDevices);
setComponentDevices(newComponentDevices);
}
}
//if a grain cable was added to the bin update the components grain type to match the bin //if a grain cable was added to the bin update the components grain type to match the bin
if (preferences.type === pond.BinComponent.BIN_COMPONENT_GRAIN_CABLE) { if (preferences.type === pond.BinComponent.BIN_COMPONENT_GRAIN_CABLE) {
let settings = component.settings; let settings = component.settings;
@ -338,16 +398,41 @@ export default function BinComponents(props: Props) {
} }
}); });
} else { } else {
binAPI.removeComponent(bin, component.key(), as).then(resp => { let lastComponent = false;
if (componentDevices) {
console.log(componentDevices)
let count = 0;
for (const val of componentDevices.values()) {
if (val === device) {
count++;
if (count > 1) {
break; // stop early once we know it's not the last
}
}
}
lastComponent = count === 1;
}
if(lastComponent){
setComponentToRemove(component)
}else{
binAPI.removeComponent(bin, component.key(), undefined, as).then(resp => {
if (components && setComponents) { if (components && setComponents) {
if (components.delete(component.key())) { if (components.delete(component.key())) {
let newComponents = new Map(components); let newComponents = new Map(components);
setComponents(newComponents); setComponents(newComponents);
} }
} }
if(componentDevices && setComponentDevices) {
if (componentDevices.delete(component.key())) {
let newComponentDevices = new Map(componentDevices);
setComponentDevices(newComponentDevices);
}
}
snackbar.info("Component removed from bin"); snackbar.info("Component removed from bin");
}); });
} }
}
}; };
const removeAll = () => { const removeAll = () => {
@ -567,7 +652,10 @@ export default function BinComponents(props: Props) {
</ListItemAvatar> </ListItemAvatar>
<ListItemText inset={cIcon === undefined}>{component.name()}</ListItemText> <ListItemText inset={cIcon === undefined}>{component.name()}</ListItemText>
<ListItemSecondaryAction> <ListItemSecondaryAction>
<IconButton onClick={() => setComponentToRemove(component)}> <IconButton onClick={() => {
setComponentToRemove(component)
}}>
<Remove /> <Remove />
</IconButton> </IconButton>
</ListItemSecondaryAction> </ListItemSecondaryAction>

View file

@ -30,7 +30,9 @@ interface Props {
coords?: { longitude: number; latitude: number }; coords?: { longitude: number; latitude: number };
binYards?: pond.BinYardSettings[]; binYards?: pond.BinYardSettings[];
components?: Map<string, Component>; components?: Map<string, Component>;
componentDevices?: Map<string, number>
setComponents?: React.Dispatch<React.SetStateAction<Map<string, Component>>>; setComponents?: React.Dispatch<React.SetStateAction<Map<string, Component>>>;
setComponentDevices?: React.Dispatch<React.SetStateAction<Map<string, number>>>;
updateBinStatus?: (componentKeys: string[], removed?: boolean) => void; updateBinStatus?: (componentKeys: string[], removed?: boolean) => void;
} }
@ -43,7 +45,9 @@ export default function BinSensors(props: Props) {
mode, mode,
openedBinYard, openedBinYard,
components, components,
componentDevices,
setComponents, setComponents,
setComponentDevices,
updateBinStatus updateBinStatus
} = props; } = props;
const [initialized, setInitialized] = useState(false); const [initialized, setInitialized] = useState(false);
@ -93,7 +97,9 @@ export default function BinSensors(props: Props) {
<Box padding={1}> <Box padding={1}>
<BinComponents <BinComponents
components={components ? components : undefined} components={components ? components : undefined}
componentDevices={componentDevices}
setComponents={setComponents} setComponents={setComponents}
setComponentDevices={setComponentDevices}
bin={bin.key()} bin={bin.key()}
binGrain={bin.grain()} binGrain={bin.grain()}
updateBinStatus={updateBinStatus} updateBinStatus={updateBinStatus}

View file

@ -269,7 +269,7 @@ export default function Bin(props: Props) {
resp.data.devices.forEach((dev: any) => { resp.data.devices.forEach((dev: any) => {
let device = Device.create(dev); let device = Device.create(dev);
if (attachedDevIds.includes(device.id())) { if (attachedDevIds.includes(device.id())) {
devs.push(Device.create(dev)); devs.push(device);
} }
}); });
setDevices(devs); setDevices(devs);
@ -674,6 +674,8 @@ export default function Bin(props: Props) {
userID={user.id()} userID={user.id()}
components={components} components={components}
setComponents={setComponents} setComponents={setComponents}
componentDevices={componentDevices}
setComponentDevices={setComponentDevices}
updateBinStatus={updateStatus} updateBinStatus={updateStatus}
/> />
</Grid> </Grid>

View file

@ -30,6 +30,7 @@ export interface IBinAPIContext {
removeComponent: ( removeComponent: (
bin: string, bin: string,
component: string, component: string,
device?: number,
otherTeam?: string otherTeam?: string
) => Promise<AxiosResponse<pond.RemoveBinComponentResponse>>; ) => Promise<AxiosResponse<pond.RemoveBinComponentResponse>>;
removeAllComponents: (bin: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveAllBinComponentsResponse>>; removeAllComponents: (bin: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveAllBinComponentsResponse>>;
@ -254,10 +255,11 @@ export default function BinProvider(props: PropsWithChildren<Props>) {
}) })
}; };
const removeComponent = (bin: string, component: string, otherTeam?: string) => { const removeComponent = (bin: string, component: string, device?: number, otherTeam?: string) => {
const view = otherTeam ? otherTeam : as const view = otherTeam ? otherTeam : as
let url = "/bins/" + bin + "/removeComponent/" + component; let url = "/bins/" + bin + "/removeComponent/" + component;
if (view) url = url + "?as=" + view; if (device) url = url + "?deviceToRemove=" + device
if (view) url = url + (device ? "&as=" : "?as=") + view;
return new Promise<AxiosResponse<pond.RemoveBinComponentResponse>>((resolve, reject)=>{ return new Promise<AxiosResponse<pond.RemoveBinComponentResponse>>((resolve, reject)=>{
post<pond.RemoveBinComponentResponse>(pondURL(url)).then(resp => { post<pond.RemoveBinComponentResponse>(pondURL(url)).then(resp => {
resp.data = pond.RemoveBinComponentResponse.fromObject(resp.data) resp.data = pond.RemoveBinComponentResponse.fromObject(resp.data)