Skip to content

Commit

Permalink
feat: create split board screen - board name input and tip bar (#139)
Browse files Browse the repository at this point in the history
  • Loading branch information
nunocaseiro authored Apr 27, 2022
1 parent b16f4fd commit a1db025
Show file tree
Hide file tree
Showing 13 changed files with 370 additions and 24 deletions.
26 changes: 26 additions & 0 deletions frontend/components/CreateBoard/Content/BoardName.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from "react";
import Input from "../../Primitives/Input";
import Text from "../../Primitives/Text";

const BoardName = (props: { mainBoardName: string }) => {
const { mainBoardName } = props;
return (
<>
<Text heading="3">Main Board Name</Text>
<Text css={{ mt: "$8", mb: "$16", color: "$primary500" }}>
The main board is the board into which all sub-team boards will be merged.
</Text>
<Input
state="default"
id="text"
type="text"
placeholder="Main board name"
forceState
currentValue={mainBoardName}
maxChars="30"
/>
</>
);
};

export default BoardName;
73 changes: 73 additions & 0 deletions frontend/components/CreateBoard/Content/Content.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm, FormProvider, useWatch } from "react-hook-form";
import { SetterOrUpdater, useRecoilValue } from "recoil";
import ClickEvent from "../../../types/events/clickEvent";
import Flex from "../../Primitives/Flex";
import { styled } from "../../../stitches.config";
import SchemaCreateBoard from "../../../schema/schemaCreateBoardForm";
import Button from "../../Primitives/Button";
import BoardName from "./BoardName";
import { createBoardDataState } from "../../../store/createBoard/atoms/create-board.atom";

const StyledForm = styled("form", Flex, {});

const CreateBoardContent: React.FC<{ setOpened: SetterOrUpdater<boolean> }> = ({ setOpened }) => {
const {
board: { maxVotes },
} = useRecoilValue(createBoardDataState);

const methods = useForm<{ text: string; maxVotes: string }>({
mode: "onBlur",
reValidateMode: "onBlur",
defaultValues: {
text: "Main board -",
maxVotes: maxVotes ?? "",
},
resolver: zodResolver(SchemaCreateBoard),
});

const mainBoardName = useWatch({
control: methods.control,
name: "text",
});

const handleOnClickSaveBoard = (e: ClickEvent<HTMLButtonElement, MouseEvent>) => {
e?.preventDefault();
setOpened(false);
};

return (
<StyledForm
direction="column"
css={{ width: "100%", height: "100%", backgroundColor: "$background" }}
onSubmit={methods.handleSubmit(() => {
// saveBoard(text, maxVotes);
})}
>
<Flex
direction="column"
css={{
width: "100%",
height: "100%",
pt: "$64",
pl: "$152",
pr: "$92",
overflow: "auto",
}}
>
<FormProvider {...methods}>
<BoardName mainBoardName={mainBoardName} />
{/* <Settings /> */}
</FormProvider>
</Flex>
<Flex justify="end" gap="24" css={{ backgroundColor: "white", py: "$16", pr: "$32" }}>
<Button variant="lightOutline" onClick={handleOnClickSaveBoard}>
Cancel
</Button>
<Button type="submit">Create board</Button>
</Flex>
</StyledForm>
);
};

export default CreateBoardContent;
46 changes: 46 additions & 0 deletions frontend/components/CreateBoard/CreateBoard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { useSetRecoilState } from "recoil";
import { styled } from "../../stitches.config";
import { createBoardState } from "../../store/createBoard/atoms/create-board.atom";
import CrossIcon from "../icons/CrossIcon";
import Button from "../Primitives/Button";
import Flex from "../Primitives/Flex";
import Separator from "../Primitives/Separator";
import Text from "../Primitives/Text";
import CreateBoardContent from "./Content/Content";
import CreateBoardTipBar from "./TipBar";

const MainContainter = styled(Flex, {
backgroundColor: "white",
width: "100%",
maxHeight: "100vh",
});

const CreateBoard: React.FC = () => {
const setCreateBoardState = useSetRecoilState(createBoardState);

return (
<MainContainter direction="column">
<Flex
justify="between"
align="center"
css={{ px: "$40", py: "$32", backgroundColor: "white", width: "100%", maxHeight: "$92" }}
>
<Text heading="3">Add new SPLIT board</Text>
<Button
css={{ "& svg": { size: "$40 !important", color: "$primary800" } }}
isIcon
onClick={() => setCreateBoardState(false)}
>
<CrossIcon size="24" />
</Button>
</Flex>
<Separator orientation="horizontal" css={{ backgroundColor: "$primary100" }} />
<Flex justify="between" css={{ overflow: "hidden", height: "100%" }}>
<CreateBoardContent setOpened={setCreateBoardState} />
<CreateBoardTipBar />
</Flex>
</MainContainter>
);
};

export default CreateBoard;
50 changes: 50 additions & 0 deletions frontend/components/CreateBoard/TipBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import LampIcon from "../icons/LampIcon";
import Flex from "../Primitives/Flex";
import { styled } from "../../stitches.config";
import Text from "../Primitives/Text";

const TextWhite = styled(Text, { color: "white", mt: "$24" });
const LiWhite = styled("li", Text, { color: "$primary100", fontSize: "$14", lineHeight: "$20" });
const UnorderedList = styled("ul", { paddingInlineStart: "$26" });

const CreateBoardTipBar: React.FC = () => {
return (
<Flex
direction="column"
css={{
backgroundColor: "$primary800",
pt: "9.37%",
pl: "$32",
pr: "$36",
maxWidth: "$384",
}}
>
<LampIcon />
<TextWhite heading="6">Sub-teams</TextWhite>
<UnorderedList>
<LiWhite>The participants of the sub-teams are generated randomly.</LiWhite>

<LiWhite>The number of participants is splitted equally between all sub-teams.</LiWhite>

<LiWhite>For each sub-team there is one responsible selected.</LiWhite>
</UnorderedList>
<TextWhite heading="6">Responsibles</TextWhite>
<UnorderedList>
<LiWhite>
Responsibles are normal users with the rights to merge the cards at the end of each
sub-teams retro into the main board.
</LiWhite>

<LiWhite>
Responsibles also are in charge of scheduling and conducting the sub-teams retrospective.
</LiWhite>
</UnorderedList>
<TextWhite heading="6" css={{ mb: "$8" }}>
Stakeholder
</TextWhite>
<LiWhite as="span">The stakeholder will not be assigned to any sub-team.</LiWhite>
</Flex>
);
};

export default CreateBoardTipBar;
11 changes: 11 additions & 0 deletions frontend/components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@ import React, { ReactNode, useMemo } from "react";
import { useSession, signOut } from "next-auth/react";
import { TailSpin } from "react-loader-spinner";
import { useRouter } from "next/router";
import { useRecoilState } from "recoil";
import Flex from "../Primitives/Flex";
import { REFRESH_TOKEN_ERROR } from "../../utils/constants";
import SideBar from "../Sidebar/Sidebar";
import SpinnerPage from "../Loading/SpinnerPage";
import { BOARDS_ROUTE, DASHBOARD_ROUTE } from "../../utils/routes";
import DashboardLayout from "./DashboardLayout";
import { createBoardState } from "../../store/createBoard/atoms/create-board.atom";
import CreateBoard from "../CreateBoard/CreateBoard";

const Layout: React.FC<{ children: ReactNode }> = ({ children }) => {
const { data: session } = useSession({ required: true });
const [showCreateBoard] = useRecoilState(createBoardState);

const router = useRouter();

const isDashboard = router.pathname === DASHBOARD_ROUTE;
Expand All @@ -34,6 +39,12 @@ const Layout: React.FC<{ children: ReactNode }> = ({ children }) => {
}, [children, isBoards, isDashboard, session]);

if (!session) return <SpinnerPage />;
if (showCreateBoard)
return (
<Flex css={{ height: "100vh", width: "100vw" }}>
<CreateBoard />
</Flex>
);
return (
<Flex css={{ height: "100vh", width: "100vw" }}>
<SideBar
Expand Down
17 changes: 7 additions & 10 deletions frontend/components/Primitives/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ const Input: React.FC<InputProps> = ({
register,
getValues,
clearErrors,
formState: { errors },
formState: { errors, touchedFields },
} = useFormContext();
const { ref, ...rest } = register(id);

Expand All @@ -227,19 +227,16 @@ const Input: React.FC<InputProps> = ({
const isValueEmpty = isEmpty(value);

const autoState = useMemo(() => {
if (message) {
return "error";
}
if (isValueEmpty) {
return "default";
}
if (message) return "error";
if (isValueEmpty) return "default";
return "valid";
}, [message, isValueEmpty]);

const currentState = useMemo(() => {
if (state && forceState) return state;
if (disabled && !touchedFields[id]) return "default";
if (state && forceState && !touchedFields[id]) return state;
return autoState;
}, [autoState, forceState, state]);
}, [autoState, disabled, forceState, id, state, touchedFields]);

const isHelperEmpty = isEmpty(helperText) && isEmpty(message);

Expand Down Expand Up @@ -287,7 +284,7 @@ const Input: React.FC<InputProps> = ({
<Flex justify={!isHelperEmpty ? "between" : "end"}>
{!isHelperEmpty && (
<HelperTextWrapper gap="4" align="center" css={{ mt: "$8" }}>
{currentState === "error" && <InfoIcon />}
{currentState === "error" && <InfoIcon size="24" />}
<Text
css={{
color: currentState === "error" ? "$dangerBase" : "$primary300",
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/Primitives/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ const TextArea: React.FC<ResizableTextAreaProps> = ({ id, placeholder, helperTex
},
}}
>
{state === "error" && <InfoIcon />}
{state === "error" && <InfoIcon size="24" />}
<Text
css={{
color: state === "error" ? "$dangerBase" : "$primary300",
Expand Down
59 changes: 50 additions & 9 deletions frontend/components/icons/Info.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,56 @@
import React from "react";
import { CSS } from "../../stitches.config";
import Svg from "../Primitives/Svg";

const InfoIcon = () => {
interface InfoIconProps {
size: "24" | "16" | "14";
css?: CSS;
}

const path = (
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12 22C6.477 22 2 17.523 2 12C2 6.477 6.477 2 12 2C17.523 2 22 6.477 22 12C21.993 17.52 17.52 21.993 12.001 22H12ZM12 4C7.582 4 4 7.582 4 12C4 16.418 7.582 20 12 20C16.418 20 20 16.418 20 12C19.995 7.584 16.416 4.005 12 4H11.999H12ZM13 16V12C13 11.448 12.552 11 12 11C11.448 11 11 11.448 11 12V16C11 16.552 11.448 17 12 17C12.552 17 13 16.552 13 16ZM12.19 8.98C12.261 8.969 12.325 8.948 12.384 8.918L12.381 8.92C12.446 8.899 12.503 8.868 12.552 8.829L12.551 8.83C12.611 8.794 12.664 8.754 12.712 8.709L12.711 8.71C12.799 8.617 12.87 8.508 12.919 8.387L12.921 8.38C12.972 8.268 13.001 8.138 13.001 8C13.001 7.862 12.972 7.732 12.919 7.614L12.921 7.62C12.87 7.492 12.799 7.383 12.711 7.29C12.664 7.246 12.611 7.206 12.555 7.172L12.551 7.17C12.503 7.132 12.446 7.101 12.385 7.081L12.381 7.08C12.326 7.051 12.262 7.03 12.194 7.02H12.191C12.132 7.006 12.065 6.997 11.996 6.997C11.927 6.997 11.86 7.005 11.795 7.021L11.801 7.02C11.733 7.032 11.673 7.053 11.617 7.082L11.621 7.08C11.552 7.104 11.492 7.134 11.438 7.172L11.441 7.17C11.385 7.21 11.336 7.249 11.29 7.291L11.291 7.29C11.203 7.383 11.132 7.492 11.083 7.613L11.081 7.62C11.03 7.732 11.001 7.862 11.001 8C11.001 8.138 11.03 8.268 11.083 8.386L11.081 8.38C11.222 8.746 11.57 9 11.978 9C11.986 9 11.994 9 12.002 9H12.001C12.07 8.998 12.135 8.991 12.199 8.979L12.191 8.98H12.19Z"
fill="currentColor"
/>
);

const InfoIcon: React.FC<InfoIconProps> = ({ size, css }) => {
InfoIcon.defaultProps = {
css: undefined,
};
if (size === "14") {
return (
<Svg
width="14"
height="14"
viewBox="0 0 14 14"
fill="none"
xmlns="http://www.w3.org/2000/svg"
css={css}
>
{path}
</Svg>
);
}
if (size === "16") {
return (
<Svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
{path}
</Svg>
);
}
return (
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M12 22C6.477 22 2 17.523 2 12C2 6.477 6.477 2 12 2C17.523 2 22 6.477 22 12C21.993 17.52 17.52 21.993 12.001 22H12ZM12 4C7.582 4 4 7.582 4 12C4 16.418 7.582 20 12 20C16.418 20 20 16.418 20 12C19.995 7.584 16.416 4.005 12 4H11.999H12ZM13 16V12C13 11.448 12.552 11 12 11C11.448 11 11 11.448 11 12V16C11 16.552 11.448 17 12 17C12.552 17 13 16.552 13 16ZM12.19 8.98C12.261 8.969 12.325 8.948 12.384 8.918L12.381 8.92C12.446 8.899 12.503 8.868 12.552 8.829L12.551 8.83C12.611 8.794 12.664 8.754 12.712 8.709L12.711 8.71C12.799 8.617 12.87 8.508 12.919 8.387L12.921 8.38C12.972 8.268 13.001 8.138 13.001 8C13.001 7.862 12.972 7.732 12.919 7.614L12.921 7.62C12.87 7.492 12.799 7.383 12.711 7.29C12.664 7.246 12.611 7.206 12.555 7.172L12.551 7.17C12.503 7.132 12.446 7.101 12.385 7.081L12.381 7.08C12.326 7.051 12.262 7.03 12.194 7.02H12.191C12.132 7.006 12.065 6.997 11.996 6.997C11.927 6.997 11.86 7.005 11.795 7.021L11.801 7.02C11.733 7.032 11.673 7.053 11.617 7.082L11.621 7.08C11.552 7.104 11.492 7.134 11.438 7.172L11.441 7.17C11.385 7.21 11.336 7.249 11.29 7.291L11.291 7.29C11.203 7.383 11.132 7.492 11.083 7.613L11.081 7.62C11.03 7.732 11.001 7.862 11.001 8C11.001 8.138 11.03 8.268 11.083 8.386L11.081 8.38C11.222 8.746 11.57 9 11.978 9C11.986 9 11.994 9 12.002 9H12.001C12.07 8.998 12.135 8.991 12.199 8.979L12.191 8.98H12.19Z"
fill="currentColor"
/>
</svg>
<Svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
{path}
</Svg>
);
};

Expand Down
18 changes: 18 additions & 0 deletions frontend/components/icons/LampIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const LampIcon: React.FC = () => {
return (
<svg width="47" height="48" viewBox="0 0 47 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.54931 45.412C21.1736 52.1454 37.9024 44.9674 43.4931 32.7831C49.0839 20.5988 46.2516 11.402 34.6273 4.66855C23.003 -2.06492 9.30592 -2.44303 3.71517 9.74129C-1.87559 21.9256 -2.07495 38.6785 9.54931 45.412Z"
fill="#73FBF7"
/>
<path
d="M22.5773 17.0958C20.2508 17.0958 18.3585 18.9881 18.3585 21.3146C18.3585 21.7031 18.6731 22.0177 19.0616 22.0177C19.4501 22.0177 19.7647 21.7031 19.7647 21.3146C19.7647 19.7633 21.0269 18.502 22.5773 18.502C22.9658 18.502 23.2804 18.1874 23.2804 17.7989C23.2804 17.4104 22.9658 17.0958 22.5773 17.0958ZM19.0642 33.7588C19.0642 33.8973 19.1051 34.0322 19.182 34.1473L20.2591 35.7663C20.3897 35.9623 20.6094 36.0801 20.8445 36.0801H24.3097C24.5452 36.0801 24.7649 35.9623 24.895 35.7663L25.9721 34.1473C26.0486 34.0322 26.0895 33.8968 26.0899 33.7588L26.0921 31.8612H19.0625L19.0642 33.7588ZM22.5773 13.5801C18.0825 13.5801 14.8428 17.2263 14.8428 21.3146C14.8428 23.2645 15.5657 25.0434 16.7571 26.4027C17.4883 27.2372 18.6353 28.9867 19.0607 30.4528V30.4554H21.1701V30.4501C21.1697 30.2405 21.1385 30.0322 21.0757 29.8318C20.83 29.0491 20.0728 26.9854 18.3435 25.0122C17.4409 23.9826 16.9583 22.6765 16.9544 21.3146C16.9456 18.0784 19.5767 15.6895 22.5773 15.6895C25.679 15.6895 28.2024 18.2129 28.2024 21.3146C28.2024 22.6756 27.7085 23.9887 26.8115 25.0122C25.0928 26.9727 24.3321 29.032 24.0825 29.8226C24.018 30.0259 23.9851 30.2378 23.9849 30.451V30.4554H26.0943V30.4532C26.5197 28.9867 27.6667 27.2372 28.398 26.4031C29.5889 25.0434 30.3118 23.2645 30.3118 21.3146C30.3118 17.043 26.8489 13.5801 22.5773 13.5801Z"
fill="#060D16"
/>
</svg>
);
};

export default LampIcon;
3 changes: 2 additions & 1 deletion frontend/schema/schemaCreateBoardForm.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import * as z from "zod";

const SchemaCreateBoard = z.object({
title: z.string().max(15, "Maximum of 15 characters").nonempty("Please enter a title."),
text: z.string().nonempty("Please enter the board name.").max(30, "Maximum of 30 characters"),
maxVotes: z.string().min(1, "Please set the maximum number of votes.").optional(),
});

export default SchemaCreateBoard;
Loading

0 comments on commit a1db025

Please sign in to comment.