diff --git a/frontend/api/boardService.tsx b/frontend/api/boardService.tsx index c69354066..e4c9eaa46 100644 --- a/frontend/api/boardService.tsx +++ b/frontend/api/boardService.tsx @@ -1,6 +1,6 @@ import { GetServerSidePropsContext } from "next"; import fetchData from "../utils/fetchData"; -import BoardType, { BoardToAdd } from "../types/board/board"; +import BoardType, { CreateBoardDto } from "../types/board/board"; import UpdateCardPositionDto from "../types/card/updateCardPosition.dto"; import UpdateBoardDto from "../types/board/updateBoard"; import AddCardDto from "../types/card/addCard.dto"; @@ -15,7 +15,7 @@ import RemoveFromCardGroupDto from "../types/card/removeFromCardGroup.dto"; // #region BOARD -export const createBoardRequest = (newBoard: BoardToAdd): Promise => { +export const createBoardRequest = (newBoard: CreateBoardDto): Promise => { return fetchData(`/boards`, { method: "POST", data: newBoard }); }; diff --git a/frontend/components/CreateBoard/Configurations/BoardConfigurations.tsx b/frontend/components/CreateBoard/Configurations/BoardConfigurations.tsx new file mode 100644 index 000000000..775656ac8 --- /dev/null +++ b/frontend/components/CreateBoard/Configurations/BoardConfigurations.tsx @@ -0,0 +1,150 @@ +import { useFormContext } from "react-hook-form"; +import { useRecoilState } from "recoil"; +import { createBoardDataState } from "../../../store/createBoard/atoms/create-board.atom"; +import CheckIcon from "../../icons/Check"; +import Flex from "../../Primitives/Flex"; +import Input from "../../Primitives/Input"; +import { Switch, SwitchThumb } from "../../Primitives/Switch"; +import Text from "../../Primitives/Text"; + +const BoardConfigurations = () => { + const [createBoardData, setCreateBoardData] = useRecoilState(createBoardDataState); + + const { board } = createBoardData; + + const { register, unregister, clearErrors, setValue } = useFormContext(); + + const handleHideCardsChange = (checked: boolean) => { + setCreateBoardData((prev) => ({ + ...prev, + board: { + ...prev.board, + hideCards: checked, + }, + })); + }; + + const handleHideVotesChange = (checked: boolean) => { + setCreateBoardData((prev) => ({ + ...prev, + board: { + ...prev.board, + hideVotes: checked, + }, + })); + }; + + const handlePostAnonymouslyChange = (checked: boolean) => { + setCreateBoardData((prev) => ({ + ...prev, + board: { + ...prev.board, + postAnonymously: checked, + }, + })); + }; + + const handleLimitVotesChange = (checked: boolean) => { + setCreateBoardData((prev) => ({ + ...prev, + board: { + ...prev.board, + maxVotes: checked ? "6" : undefined, + }, + })); + setValue("maxVotes", checked ? "6" : ""); + if (checked) register("maxVotes"); + if (!checked) { + unregister("maxVotes"); + clearErrors("maxVotes"); + } + }; + + return ( + + + You can change the board configurations still later inside your retro board. + + + + + + {board.hideCards && ( + + )} + + + + + Hide cards from others + + + Participants can not see the cards from other participants of this retrospective. + + + + + + + {board.hideVotes && ( + + )} + + + + + Hide votes from others + + + Participants can not see the votes from other participants of this retrospective. + + + + + + + {board.postAnonymously && ( + + )} + + + + + Option to post cards anonymously + + + Participants can decide to post cards anonymously or publicly (Name on card is + disabled/enabled.) + + + + + + + {!!board.maxVotes && ( + + )} + + + + + Limit votes + + + Make votes more significant by limiting them. + + + + + + + ); +}; + +export default BoardConfigurations; diff --git a/frontend/components/CreateBoard/Content.tsx b/frontend/components/CreateBoard/Content.tsx index 8f87ba960..c54778bf0 100644 --- a/frontend/components/CreateBoard/Content.tsx +++ b/frontend/components/CreateBoard/Content.tsx @@ -1,6 +1,7 @@ +import { useEffect } from "react"; import { zodResolver } from "@hookform/resolvers/zod"; import { useForm, FormProvider, useWatch } from "react-hook-form"; -import { SetterOrUpdater, useRecoilValue } from "recoil"; +import { SetterOrUpdater, useRecoilValue, useResetRecoilState } from "recoil"; import ClickEvent from "../../types/events/clickEvent"; import Flex from "../Primitives/Flex"; import { styled } from "../../stitches.config"; @@ -9,20 +10,24 @@ import Button from "../Primitives/Button"; import BoardName from "./BoardName"; import { createBoardDataState } from "../../store/createBoard/atoms/create-board.atom"; import SettingsTabs from "./SettingsTabs"; +import useBoard from "../../hooks/useBoard"; +import { CreateBoardDto } from "../../types/board/board"; const StyledForm = styled("form", Flex, {}); const CreateBoardContent: React.FC<{ setOpened: SetterOrUpdater }> = ({ setOpened }) => { + const boardState = useRecoilValue(createBoardDataState); + const resetBoardState = useResetRecoilState(createBoardDataState); const { - board: { maxVotes }, - } = useRecoilValue(createBoardDataState); + createBoard: { status, mutate }, + } = useBoard({ autoFetchBoard: false }); const methods = useForm<{ text: string; maxVotes: string }>({ mode: "onBlur", reValidateMode: "onBlur", defaultValues: { text: "Main board -", - maxVotes: maxVotes ?? "", + maxVotes: String(boardState.board.maxVotes) ?? "", }, resolver: zodResolver(SchemaCreateBoard), }); @@ -37,11 +42,45 @@ const CreateBoardContent: React.FC<{ setOpened: SetterOrUpdater }> = ({ setOpened(false); }; + const saveBoard = (title: string, maxVotes: string) => { + const newDividedBoards: CreateBoardDto[] = boardState.board.dividedBoards.map((subBoard) => { + const newSubBoard: CreateBoardDto = { ...subBoard, users: [], dividedBoards: [] }; + newSubBoard.hideCards = boardState.board.hideCards; + newSubBoard.hideVotes = boardState.board.hideVotes; + newSubBoard.postAnonymously = boardState.board.postAnonymously; + newSubBoard.maxVotes = maxVotes; + const users = subBoard.users.map((boardUser) => ({ + user: boardUser.user._id, + role: boardUser.role, + })); + newSubBoard.users = users; + return newSubBoard; + }); + + mutate({ + ...boardState.board, + users: boardState.users, + title, + dividedBoards: newDividedBoards, + maxVotes, + maxUsers: boardState.count.maxUsersCount.toString(), + }); + }; + + useEffect(() => { + if (status === "success") { + setOpened(false); + resetBoardState(); + } + }, [status, setOpened, resetBoardState]); + return ( {})} + onSubmit={methods.handleSubmit(({ text, maxVotes }) => { + saveBoard(text, maxVotes); + })} > { )} + {currentTab === 2 && ( + }> + + + + + )} ); }; diff --git a/frontend/components/CreateBoard/SubTeamsTab/SubCardBoard.tsx b/frontend/components/CreateBoard/SubTeamsTab/SubCardBoard.tsx index ccb75f4cf..f8e86073d 100644 --- a/frontend/components/CreateBoard/SubTeamsTab/SubCardBoard.tsx +++ b/frontend/components/CreateBoard/SubTeamsTab/SubCardBoard.tsx @@ -82,6 +82,17 @@ const SubCardBoard: React.FC = ({ board, index, setBoard }) = orientation="vertical" css={{ "&[data-orientation=vertical]": { height: "$12", width: 1 } }} /> + + + {responsible?.firstName} {responsible?.lastName} @@ -94,17 +105,6 @@ const SubCardBoard: React.FC = ({ board, index, setBoard }) = }} fallbackText={`${responsible?.firstName[0]}${responsible?.lastName[0]}`} /> - - - diff --git a/frontend/components/Primitives/Switch.tsx b/frontend/components/Primitives/Switch.tsx new file mode 100644 index 000000000..b302f914c --- /dev/null +++ b/frontend/components/Primitives/Switch.tsx @@ -0,0 +1,32 @@ +import * as SwitchPrimitive from "@radix-ui/react-switch"; +import { styled } from "../../stitches.config"; +import Flex from "./Flex"; + +const StyledSwitch = styled(SwitchPrimitive.Root, { + all: "unset", + width: 42, + height: 24, + display: "flex", + backgroundColor: "$primary200", + borderRadius: "9999px", + position: "relative", + WebkitTapHighlightColor: "rgba(0, 0, 0, 0)", + '&[data-state="checked"]': { backgroundColor: "$successBase" }, + boxSizing: "border-box", +}); + +const StyledThumb = styled(SwitchPrimitive.Thumb, Flex, { + justifyContent: "center", + alignItems: "center", + width: 21, + height: 21, + backgroundColor: "white", + borderRadius: "9999px", + transition: "transform 100ms", + transform: "translate(1.5px, 1.5px)", + willChange: "transform", + '&[data-state="checked"]': { transform: "translate(19px, 1.5px)" }, +}); + +export const Switch = StyledSwitch; +export const SwitchThumb = StyledThumb; diff --git a/frontend/components/icons/Check.tsx b/frontend/components/icons/Check.tsx index 0549572c4..dcf4f25b4 100644 --- a/frontend/components/icons/Check.tsx +++ b/frontend/components/icons/Check.tsx @@ -1,17 +1,22 @@ import React from "react"; +import { CSSProps } from "../../stitches.config"; +import Svg from "../Primitives/Svg"; -const CheckIcon = () => { +const CheckIcon = ({ css }: CSSProps) => { return ( - - - + + ); }; diff --git a/frontend/types/board/useBoard.ts b/frontend/types/board/useBoard.ts index a880b8703..8a13b4586 100644 --- a/frontend/types/board/useBoard.ts +++ b/frontend/types/board/useBoard.ts @@ -1,9 +1,9 @@ import { UseMutationResult, UseQueryResult } from "react-query"; -import BoardType, { BoardToAdd } from "./board"; +import BoardType, { CreateBoardDto } from "./board"; import UpdateBoardDto from "./updateBoard"; export default interface UseBoardType { - createBoard: UseMutationResult; + createBoard: UseMutationResult; updateBoard: UseMutationResult; deleteBoard: UseMutationResult; fetchBoard: UseQueryResult;