Untitled
unknown
typescript
2 years ago
14 kB
4
Indexable
import { useEffect, useRef, useState, createRef, SyntheticEvent, useMemo } from "react";
import { Typography } from "@mui/material";
import Button from "@mui/material/Button/Button";
import Desk from "../desk/Desk";
import { ResizableBox } from "react-resizable";
import "react-resizable/css/styles.css";
import classes from "./TeamSpace.module.scss";
import Box from "@mui/material/Box/Box";
import RotateLeftIcon from "@mui/icons-material/RotateLeft";
import RotateRightIcon from "@mui/icons-material/RotateRight";
import { Coords } from "../../interfaces/Coords";
import RoomService from "../../services/room.service";
import { TeamSpace as TeamSpaceParams } from "../../interfaces/TeamSpace";
import TeamSpaceProps from "../../interfaces/props/TeamSpaceProps";
import Ghost from "../../views/ghost/Ghost";
import { useNavigate, useParams } from "react-router-dom";
import { v4 as uuidv4 } from "uuid";
import TextField from "@mui/material/TextField/TextField";
import { Room as RoomModel } from "../../interfaces/Room";
import Notification from "../../views/notification/Notification";
import { RoomType } from "../../enums/RoomType";
import { DeskType } from "../../enums/DeskType";
import TeamService from "../../services/team.service";
import { Team } from "../../interfaces/Team";
const TeamSpace = ({ teamGuid, teamName, roomType }: TeamSpaceProps) => {
const { guid } = useParams<{ guid: string }>();
const containerRef = useRef<any>(null);
const [desks, setDesks] = useState<Array<any>>([]);
const lastClicked = useRef<number | null>(null);
const isClicked = useRef<number | null>(null);
const coords = useRef<{ [key: string]: Coords }>({});
const [containerSize, setContainerSize] = useState({
height: 200,
width: 200,
});
const [open, setOpen] = useState(false);
const [severity, setSeverity] = useState<"success" | "error">("success");
const [loading, setLoading] = useState(false);
const [teamSpaceName, setTeamSpaceName] = useState<string>("");
const navigate = useNavigate();
useEffect(() => {
const fetchRoom = () => {
RoomService.getByID(guid!)
.then((result: RoomModel) => {
setTeamSpaceName(result.name);
setContainerSize({
height: result.dimension.height,
width: result.dimension.width,
});
const deskObjects = result.desks.map((desk: any) => ({
ref: createRef(),
name: desk.name,
id: desk.guid,
x: desk.position.x,
y: desk.position.y,
angle: desk.position.angle,
}));
setDesks(deskObjects);
})
.catch((error: any) => {
console.error(error);
})
.finally(() => setLoading(false));
}
switch (roomType) {
case RoomType.HOT_DESK_SPACE:
if (!guid) {
return;
}
fetchRoom();
break;
case RoomType.TEAM_SPACE:
if (!guid && teamGuid) {
return;
}
setLoading(true);
fetchRoom();
}
}, [guid, teamGuid, roomType]);
useEffect(() => {
if (!containerRef.current) {
return;
}
desks.forEach((desk) => {
if (!desk || !desk.ref || !desk.ref.current) {
return;
}
const deskRef = desk.ref.current;
const onMouseDown = (e: MouseEvent) => {
e.stopPropagation();
isClicked.current = desk.id;
lastClicked.current = desk.id;
coords.current[desk.id] = {
startX: e.clientX,
startY: e.clientY,
lastX: coords.current[desk.id].lastX,
lastY: coords.current[desk.id].lastY,
angle: coords.current[desk.id].angle,
name: coords.current[desk.id].name,
};
};
const onMouseUp = () => {
isClicked.current = null;
if (coords.current[desk.id]) {
coords.current[desk.id] = {
startX: coords.current[desk.id].startX,
startY: coords.current[desk.id].startY,
lastX: deskRef.offsetLeft,
lastY: deskRef.offsetTop,
angle: coords.current[desk.id].angle,
name: coords.current[desk.id].name,
};
}
};
const onMouseMove = (e: MouseEvent) => {
if (isClicked.current !== desk.id) {
return;
}
lastClicked.current = isClicked.current;
const deskRect = deskRef.getBoundingClientRect();
const containerRect = containerRef.current.getBoundingClientRect();
const nextX = e.clientX - containerRect.left - 50;
const nextY = e.clientY - containerRect.top - 40;
const maxX = containerSize.width - deskRect.width;
const maxY = containerSize.height - deskRect.height;
const minX = 0;
const minY = 0;
const boundedX = Math.max(Math.min(nextX, maxX), minX);
const boundedY = Math.max(Math.min(nextY, maxY), minY);
deskRef.style.top = `${boundedY}px`;
deskRef.style.left = `${boundedX}px`;
coords.current[desk.id] = {
startX: coords.current[desk.id].startX,
startY: coords.current[desk.id].startY,
lastX: boundedX,
lastY: boundedY,
angle: coords.current[desk.id].angle,
name: coords.current[desk.id].name,
};
};
if (!coords.current[desk.id]) {
coords.current[desk.id] = {
startX: 0,
startY: 0,
lastX: deskRef.offsetLeft,
lastY: deskRef.offsetTop,
angle: desk.angle,
name: desk.name,
};
}
deskRef.addEventListener("mousedown", onMouseDown);
deskRef.addEventListener("mouseup", onMouseUp);
containerRef.current.addEventListener("mousemove", onMouseMove);
containerRef.current.addEventListener("mouseleave", onMouseUp);
return () => {
deskRef.removeEventListener("mousedown", onMouseDown);
deskRef.removeEventListener("mouseup", onMouseUp);
containerRef.current.removeEventListener("mousemove", onMouseMove);
containerRef.current.removeEventListener("mouseleave", onMouseUp);
};
});
}, [desks, containerSize]);
useEffect(() => {
containerRef.current.style.width = `${containerSize.width + 50}px`;
containerRef.current.style.height = `${containerSize.height + 50}px`;
const containerRect = containerRef.current.getBoundingClientRect();
const removeOutOfBoundsDesks = () => {
desks.forEach((desk) => {
if (!desk || !desk.ref || !desk.ref.current) {
return;
}
const deskRef = desk.ref.current;
const deskRect = deskRef.getBoundingClientRect();
const isOutOfBounds =
deskRect.left > containerSize.width + containerRect.left - 20 ||
deskRect.top > containerSize.height + containerRect.top - 20;
if (isOutOfBounds) {
const indexToRemove = desk.id;
deleteDeskWithIndex(indexToRemove);
}
});
};
removeOutOfBoundsDesks();
window.addEventListener("resize", removeOutOfBoundsDesks);
return () => {
window.removeEventListener("resize", removeOutOfBoundsDesks);
};
}, [containerSize, desks]);
useEffect(() => {
if (!guid && teamGuid) {
TeamService.getByID(teamGuid)
.then((team: Team) => {
setTeamSpaceName(`${team.name} team space`);
})
.catch((err) => console.log(err));
}
}, [guid, teamGuid]);
useEffect(() => {
if ((roomType === RoomType.HOT_DESK_SPACE) && !guid) {
setTeamSpaceName('HOT DESKS SPACE: ');
}
}, [roomType, guid]);
const message = useMemo(() => {
let type = '';
switch (roomType) {
case RoomType.TEAM_SPACE:
type = 'Team space';
break;
case RoomType.HOT_DESK_SPACE:
type = 'Hot Desks Team space'
break;
default: throw new Error('Illegal room type provided!');
}
return severity === "success"
? `${type} space saved successfully!`
: `Error saving team ${type}!`;
}, [severity, roomType]);
const handleAddDesk = () => {
const newDesk = {
ref: createRef(),
name: uuidv4(),
id: uuidv4(),
x: 0,
y: 0,
angle: 0,
};
setDesks([...desks, newDesk]);
};
const handleDeleteDesk = () => {
if (null === lastClicked.current) {
return;
}
const indexToRemove = lastClicked.current;
const updatedDesks = desks.filter((desk) => desk.id !== indexToRemove);
setDesks(updatedDesks);
lastClicked.current = null;
};
const deleteDeskWithIndex = (indexToRemove: number) => {
delete coords.current[indexToRemove];
setDesks(desks.filter(
(desk) => desk.id !== indexToRemove
));
};
const deskType = useMemo((): string => {
return roomType === RoomType.TEAM_SPACE ? DeskType.DEDICATED : DeskType.HOT;
}, [roomType])
const handleSaveTeamSpace = () => {
const desks = Object.values(coords.current).map((coord) => ({
name: coord.name,
type: deskType,
position: {
x: coord.lastX,
y: coord.lastY,
angle: coord.angle,
},
}));
const parameters: TeamSpaceParams = {
name: teamSpaceName,
team: teamGuid ?? null,
dimensions: {
width: containerSize.width,
height: containerSize.height,
},
desks: desks,
};
if (guid) {
RoomService.update(guid, parameters)
.then((result: any) => {
setOpen(true);
setSeverity("success");
if (RoomType.HOT_DESK_SPACE === roomType) {
navigate('/admin/hot-desks');
}
if (RoomType.TEAM_SPACE === roomType) {
navigate(`/admin/teams/${teamGuid}`, { state: { value: 1 }});
}
})
.catch((error: any) => {
setOpen(true);
setSeverity("error");
console.log(error);
});
} else {
RoomService.create(parameters)
.then((result: any) => {
setOpen(true);
setSeverity("success");
if (RoomType.HOT_DESK_SPACE === roomType) {
navigate('/admin/hot-desks');
}
if (RoomType.TEAM_SPACE === roomType) {
navigate(`/admin/teams/${teamGuid}`, { state: { value: 1 }});
}
})
.catch((error: any) => {
setOpen(true);
setSeverity("error");
console.log(error);
});
}
};
const closeHandler = (
event?: SyntheticEvent | Event,
reason?: string
) => {
if (reason === "clickaway") {
return;
}
setOpen(false);
};
const handleRotate = (direction: string) => {
if (null === lastClicked.current) {
return;
}
const elementId = lastClicked.current;
const elementRef = desks.find((item) => item.id === elementId);
const coordinates = coords.current[elementId];
const currentRotation = coordinates.angle ?? 0;
let newRotation =
direction === "left" ? currentRotation - 45 : currentRotation + 45;
if (360 === Math.abs(newRotation)) {
newRotation = 0;
}
elementRef.ref.current.style.transform = `rotate(${newRotation}deg)`;
elementRef.ref.current.style.transformOrigin = "center center";
coordinates.angle = newRotation;
};
return (
<Box className="boxed">
{loading ? (
<Box className="boxed">
<Ghost className="team-spaces" />
</Box>
) : (
<Box>
<Box className="p-3">
<TextField
classes={{ root: classes.input }}
id="outlined-basic"
label="Name"
variant="outlined"
value={teamSpaceName}
onChange={(e) => setTeamSpaceName(e.target.value)}
/>
</Box>
<Box ref={containerRef} className={classes.container}>
<ResizableBox
className={`box ${classes["resizable-box"]}`}
onResize={(event: any, { size }) => setContainerSize(size)}
width={containerSize.width}
height={containerSize.height}
minConstraints={[200, 200]}
maxConstraints={[1500, 1500]}
>
<Box>
{desks.map((desk) => (
<Desk
key={desk.id}
ref={desk.ref}
id={desk.id}
x={desk.x}
y={desk.y}
angle={desk.angle}
/>
))}
</Box>
</ResizableBox>
</Box>
</Box>
)}
<Box>
<Box className={"p-2 d-flex"}>
<Button classes={{ root: classes.button }} onClick={handleAddDesk}>
Add Desk
</Button>
<Box className="d-flex">
<Button
classes={{ root: classes.button }}
onClick={() => handleRotate("left")}
>
<RotateLeftIcon />
</Button>
<Typography className="align-self-center">ROTATE</Typography>
<Button
classes={{ root: classes.button }}
onClick={() => handleRotate("right")}
>
<RotateRightIcon />
</Button>
</Box>
<Button classes={{ root: classes.button }} onClick={handleDeleteDesk}>
Delete
</Button>
</Box>
<Box className={"p-2"}>
<Button
className={`p-2 ${classes.button}`}
onClick={handleSaveTeamSpace}
>
Save team space
</Button>
</Box>
</Box>
<Box>
<Notification
open={open}
closeHandler={closeHandler}
message={message}
severity={severity}
/>
</Box>
</Box>
);
};
export default TeamSpace;
Editor is loading...