Untitled
unknown
typescript
2 years ago
14 kB
3
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...