generated from xgeekshq/oss-template
-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
bbd4a2f
commit 36546a1
Showing
20 changed files
with
607 additions
and
260 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { Dispatch, SetStateAction } from "react"; | ||
import { styled } from "../../../stitches.config"; | ||
import Button from "../../Primitives/Button"; | ||
import Flex from "../../Primitives/Flex"; | ||
import FilterSelect from "./FilterSelect"; | ||
|
||
export interface OptionType { | ||
value: string; | ||
label: string; | ||
} | ||
|
||
const StyledButton = styled(Button, { | ||
border: "1px solid $colors$primary100", | ||
borderRadius: "0px", | ||
height: "$36 !important", | ||
backgroundColor: "$background !important", | ||
color: "$primary300 !important", | ||
fontSize: "$14 !important", | ||
lineHeight: "$20 !important", | ||
fontWeight: "$medium !important", | ||
"&[data-active='true']": { | ||
borderColor: "$primary800", | ||
color: "$primary800 !important", | ||
}, | ||
"&:active": { | ||
boxShadow: "none !important", | ||
}, | ||
}); | ||
|
||
interface FilterBoardsProps { | ||
setFilter: Dispatch<SetStateAction<string>>; | ||
filter: string; | ||
teamNames: OptionType[]; | ||
} | ||
|
||
const FilterBoards: React.FC<FilterBoardsProps> = ({ setFilter, filter, teamNames }) => { | ||
return ( | ||
<Flex justify="end" css={{ zIndex: "10" }}> | ||
<StyledButton | ||
css={{ borderRadius: "12px 0 0 12px" }} | ||
data-active={filter === "all"} | ||
onClick={() => setFilter("all")} | ||
> | ||
All | ||
</StyledButton> | ||
<StyledButton data-active={filter === "personal"} onClick={() => setFilter("personal")}> | ||
Personal | ||
</StyledButton> | ||
<FilterSelect filter={filter} options={teamNames} setFilter={setFilter} /> | ||
</Flex> | ||
); | ||
}; | ||
|
||
export default FilterBoards; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { Dispatch, SetStateAction } from "react"; | ||
import Select from "react-select"; | ||
import { styled } from "../../../stitches.config"; | ||
|
||
const StyledSelect = styled(Select, {}); | ||
|
||
interface OptionType { | ||
value: string; | ||
label: string; | ||
} | ||
|
||
interface FilterSelectProps { | ||
options: OptionType[]; | ||
setFilter: Dispatch<SetStateAction<string>>; | ||
filter: string; | ||
} | ||
|
||
const FilterSelect: React.FC<FilterSelectProps> = ({ filter, options, setFilter }) => { | ||
const isSelected = filter !== "all" && filter !== "personal"; | ||
return ( | ||
<StyledSelect | ||
options={options} | ||
className="react-select-container" | ||
classNamePrefix="react-select" | ||
value={ | ||
!isSelected | ||
? { label: "Select", value: "" } | ||
: options.find((option) => option.value === filter) | ||
} | ||
onChange={(selectedOption) => { | ||
setFilter((selectedOption as OptionType)?.value); | ||
}} | ||
/> | ||
); | ||
}; | ||
|
||
export default FilterSelect; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
import React, { useMemo, useRef, useState } from "react"; | ||
import { useInfiniteQuery } from "react-query"; | ||
import { useSetRecoilState } from "recoil"; | ||
import { TailSpin } from "react-loader-spinner"; | ||
import { getBoardsRequest } from "../../api/boardService"; | ||
import { toastState } from "../../store/toast/atom/toast.atom"; | ||
import BoardType from "../../types/board/board"; | ||
import { ToastStateEnum } from "../../utils/enums/toast-types"; | ||
import CardBody from "../CardBoard/CardBody/CardBody"; | ||
import Flex from "../Primitives/Flex"; | ||
import Text from "../Primitives/Text"; | ||
import TeamHeader from "./TeamHeader"; | ||
import { Team } from "../../types/team/team"; | ||
import FilterBoards from "./Filters/FilterBoards"; | ||
|
||
interface MyBoardsProps { | ||
userId: string; | ||
isSuperAdmin: boolean; | ||
} | ||
|
||
const MyBoards = React.memo<MyBoardsProps>(({ userId, isSuperAdmin }) => { | ||
const setToastState = useSetRecoilState(toastState); | ||
const [filter, setFilter] = useState("all"); | ||
const scrollRef = useRef<HTMLDivElement>(null); | ||
|
||
const fetchBoards = useInfiniteQuery( | ||
"boards", | ||
({ pageParam = 0 }) => getBoardsRequest(pageParam), | ||
{ | ||
enabled: true, | ||
refetchOnWindowFocus: false, | ||
getNextPageParam: (lastPage) => { | ||
const { hasNextPage, page } = lastPage; | ||
if (hasNextPage) return page + 1; | ||
return undefined; | ||
}, | ||
onError: () => { | ||
setToastState({ | ||
open: true, | ||
content: "Error getting the boards", | ||
type: ToastStateEnum.ERROR, | ||
}); | ||
}, | ||
} | ||
); | ||
|
||
const { data, isLoading } = fetchBoards; | ||
|
||
const currentDate = new Date().toDateString(); | ||
|
||
const dataByTeamAndDate = useMemo(() => { | ||
const teams = new Map<string, Team>(); | ||
const boardsTeamAndDate = new Map<string, Map<string, BoardType[]>>(); | ||
|
||
data?.pages.forEach((page) => { | ||
page.boards?.forEach((board) => { | ||
const boardsOfTeam = boardsTeamAndDate.get(`${board.team?._id ?? `personal`}`); | ||
const date = new Date(board.updatedAt).toDateString(); | ||
if (!boardsOfTeam) { | ||
boardsTeamAndDate.set(`${board.team?._id ?? `personal`}`, new Map([[date, [board]]])); | ||
if (board.team) teams.set(`${board.team?._id}`, board.team); | ||
} else { | ||
const boardsOfDay = boardsOfTeam.get(date); | ||
if (boardsOfDay) { | ||
boardsOfDay.push(board); | ||
} else { | ||
boardsOfTeam.set(date, [board]); | ||
} | ||
} | ||
}); | ||
}); | ||
return { boardsTeamAndDate, teams }; | ||
}, [data?.pages]); | ||
|
||
const onScroll = () => { | ||
if (scrollRef.current) { | ||
const { scrollTop, scrollHeight, clientHeight } = scrollRef.current; | ||
if (scrollTop + clientHeight + 2 >= scrollHeight && fetchBoards.hasNextPage) { | ||
fetchBoards.fetchNextPage(); | ||
} | ||
} | ||
}; | ||
|
||
const teamNames = Array.from(dataByTeamAndDate.teams.values()).map((team) => { | ||
return { value: team._id, label: team.name }; | ||
}); | ||
|
||
return ( | ||
<Flex | ||
ref={scrollRef} | ||
onScroll={onScroll} | ||
css={{ mt: "$24", height: "100vh", overflow: "scroll", pr: "$20" }} | ||
justify="start" | ||
direction="column" | ||
> | ||
<FilterBoards setFilter={setFilter} teamNames={teamNames} filter={filter} /> | ||
{Array.from(dataByTeamAndDate.boardsTeamAndDate).map(([teamId, boardsOfTeam]) => { | ||
const { users } = Array.from(boardsOfTeam)[0][1][0]; | ||
if (filter !== "all" && teamId !== filter) return null; | ||
return ( | ||
<Flex direction="column" key={teamId} css={{ mb: "$24" }}> | ||
<Flex | ||
direction="column" | ||
css={{ | ||
position: "sticky", | ||
zIndex: "5", | ||
top: "-0.4px", | ||
backgroundColor: "$background", | ||
}} | ||
> | ||
<TeamHeader | ||
team={dataByTeamAndDate.teams.get(teamId)} | ||
users={users} | ||
userId={userId} | ||
/> | ||
</Flex> | ||
<Flex direction="column" gap="16" css={{ overflow: "scroll", zIndex: "1" }}> | ||
{Array.from(boardsOfTeam).map(([date, boardsOfDay]) => { | ||
const formatedDate = new Date(date).toLocaleDateString("en-US", { | ||
weekday: "long", | ||
year: "numeric", | ||
month: "short", | ||
day: "numeric", | ||
}); | ||
return ( | ||
<Flex direction="column" key={date}> | ||
<Text | ||
size="xs" | ||
color="primary300" | ||
css={{ | ||
position: "sticky", | ||
zIndex: "5", | ||
top: "-0.2px", | ||
height: "$24", | ||
backgroundColor: "$background", | ||
}} | ||
> | ||
Last updated -{" "} | ||
{date === currentDate ? `Today, ${formatedDate}` : formatedDate} | ||
</Text> | ||
{/* to be used on the full version -> <Flex justify="end" css={{ width: "100%" }}> | ||
<Flex | ||
css={{ | ||
position: "relative", | ||
zIndex: "30", | ||
"& svg": { size: "$16" }, | ||
right: 0, | ||
top: "-22px", | ||
}} | ||
gap="8" | ||
> | ||
<PlusIcon size="16" /> | ||
<Text | ||
heading="6" | ||
css={{ | ||
width: "fit-content", | ||
display: "flex", | ||
alignItems: "center", | ||
"@hover": { | ||
"&:hover": { | ||
cursor: "pointer", | ||
}, | ||
}, | ||
}} | ||
> | ||
{!Array.from(dataByTeamAndDate.teams.keys()).includes(teamId) | ||
? "Add new personal board" | ||
: "Add new team board"} | ||
</Text> | ||
</Flex> | ||
</Flex> */} | ||
<Flex gap="8" direction="column"> | ||
{boardsOfDay.map((board: BoardType) => ( | ||
<CardBody | ||
key={board._id} | ||
userId={userId} | ||
board={board} | ||
isDashboard={false} | ||
dividedBoardsCount={board.dividedBoards.length} | ||
isSAdmin={isSuperAdmin} | ||
/> | ||
))} | ||
</Flex> | ||
</Flex> | ||
); | ||
})} | ||
</Flex> | ||
</Flex> | ||
); | ||
})} | ||
<Flex css={{ width: "100%", "& svg": { color: "black" } }} justify="center"> | ||
{isLoading && <TailSpin color="#060D16" height={60} width={60} />} | ||
</Flex> | ||
</Flex> | ||
); | ||
}); | ||
|
||
export default MyBoards; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { BoardUser } from "../../types/board/board.user"; | ||
import { Team } from "../../types/team/team"; | ||
import CardAvatars from "../CardBoard/CardAvatars"; | ||
import Flex from "../Primitives/Flex"; | ||
import Separator from "../Primitives/Separator"; | ||
import Text from "../Primitives/Text"; | ||
|
||
interface TeamHeaderProps { | ||
team?: Team; | ||
userId: string; | ||
users?: BoardUser[]; | ||
} | ||
|
||
const TeamHeader: React.FC<TeamHeaderProps> = ({ team, userId, users }) => { | ||
const hasTeam = !!team; | ||
return ( | ||
<Flex align="center" css={{ mb: "$16" }} justify="between"> | ||
<Flex align="center"> | ||
<Text heading="5">{hasTeam ? team.name : "My boards"}</Text> | ||
{hasTeam && ( | ||
<Flex align="center" css={{ ml: "$24" }} gap="12"> | ||
<Flex gap="8" align="center"> | ||
<Text size="sm" color="primary300"> | ||
Members | ||
</Text> | ||
<CardAvatars | ||
listUsers={team.users} | ||
responsible={false} | ||
teamAdmins={false} | ||
userId={userId} | ||
/> | ||
</Flex> | ||
<Separator | ||
orientation="vertical" | ||
css={{ backgroundColor: "$primary300", height: "$12 !important" }} | ||
/> | ||
<Text size="sm" color="primary300"> | ||
Team admin | ||
</Text> | ||
<CardAvatars listUsers={team.users} responsible={false} teamAdmins userId={userId} /> | ||
</Flex> | ||
)} | ||
{!hasTeam && users && ( | ||
<Flex css={{ ml: "$12" }}> | ||
<CardAvatars | ||
listUsers={users} | ||
responsible={false} | ||
teamAdmins={false} | ||
userId={userId} | ||
myBoards | ||
/> | ||
</Flex> | ||
)} | ||
</Flex> | ||
</Flex> | ||
); | ||
}; | ||
|
||
export default TeamHeader; |
Oops, something went wrong.