Untitled

mail@pastecode.io avatar
unknown
typescript
a year ago
14 kB
3
Indexable
Never
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;
          const deleteDeskWithIndex = (indexToRemove: number) => {
            delete coords.current[indexToRemove];

            setDesks(desks.filter(
              (desk) => desk.id !== indexToRemove
            ));
          };
          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 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;