Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: personal filter boads page frontend #824

Merged
merged 10 commits into from
Jan 8, 2023
6 changes: 6 additions & 0 deletions frontend/src/api/boardService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ export const getBoardsRequest = (
{ context, serverSide: !!context },
);

export const getPersonalBoardsRequest = (
pageParam = 0,
context?: GetServerSidePropsContext,
): Promise<{ boards: BoardType[]; hasNextPage: boolean; page: number }> =>
fetchData(`/boards/personal?page=${pageParam}&size=10`, { context, serverSide: !!context });

export const deleteBoardRequest = async ({
id,
socketId,
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/Boards/MyBoards/ListBoards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,13 @@ interface ListBoardsProps {
const ListBoards = React.memo<ListBoardsProps>(
({ userId, isSuperAdmin, dataByTeamAndDate, scrollRef, onScroll, filter, isLoading, socket }) => {
const currentDate = new Date().toDateString();
const teamsList = useRecoilValue(teamsListState);
const allTeamsList = useRecoilValue(teamsListState);
return (
<ScrollableContent direction="column" justify="start" ref={scrollRef} onScroll={onScroll}>
{Array.from(dataByTeamAndDate.boardsTeamAndDate).map(([teamId, boardsOfTeam]) => {
const { users } = Array.from(boardsOfTeam)[0][1][0];
const teamFound = teamsList.find((team) => team._id === teamId);
if ((filter !== 'all' && teamId !== filter) || !teamFound) return null;
const teamFound = allTeamsList.find((team) => team._id === teamId);
if (filter !== 'all' && teamId !== filter) return null;
return (
<Flex key={teamId} css={{ mb: '$24' }} direction="column">
<Flex
Expand All @@ -53,7 +53,7 @@ const ListBoards = React.memo<ListBoardsProps>(
<Flex justify="end" css={{ width: '100%', marginBottom: '-5px' }}>
<Link
href={{
pathname: `/boards/new`,
pathname: teamId === 'personal' ? `/boards/newRegularBoard` : `/boards/new`,
query: { team: teamId },
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Flex from '@/components/Primitives/Flex';
import { Socket } from 'socket.io-client';
import { ScrollableContent } from '../styles';
import TeamHeader from '../../TeamHeader';
import EmptyTeamBoards from '../EmptyTeamBoards';
import EmptyTeamBoards from './EmptyTeamBoards';
import ListBoards from '../ListBoards';

interface ListBoardsByTeamProps {
Expand Down Expand Up @@ -58,11 +58,11 @@ const ListBoardsByTeam = ({

data?.pages.forEach((page) => {
page.boards?.forEach((board) => {
const boardsOfTeam = boardsTeamAndDate.get(`${board.team?._id ?? `personal`}`);
const boardsOfTeam = boardsTeamAndDate.get(`${board.team._id}`);
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);
boardsTeamAndDate.set(`${board.team?._id}`, new Map([[date, [board]]]));
teams.set(`${board.team?._id}`, board.team);
return;
}
const boardsOfDay = boardsOfTeam.get(date);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import {
EmptyBoardsText,
StyledBox,
StyledImage,
StyledNewBoardLink,
} from '@/components/Dashboard/RecentRetros/partials/EmptyBoards/styles';
import Link from 'next/link';

const EmptyPersonalBoards = () => (
<StyledBox align="center" direction="column" elevation="1" justify="center">
<StyledImage />
<EmptyBoardsText css={{ mt: '$24', textAlign: 'center' }} size="md">
You have no personal boards yet.
<br />
<Link
href={{
pathname: `/boards/new`,
}}
>
<StyledNewBoardLink underline weight="medium">
Add a new personal board
</StyledNewBoardLink>
</Link>{' '}
now.
</EmptyBoardsText>
</StyledBox>
);
export default EmptyPersonalBoards;
101 changes: 101 additions & 0 deletions frontend/src/components/Boards/MyBoards/ListPersonalBoards/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import React, { useMemo, useRef } from 'react';

import { getPersonalBoardsRequest } from '@/api/boardService';
import { ToastStateEnum } from '@/utils/enums/toast-types';
import { useInfiniteQuery } from 'react-query';
import { useSetRecoilState } from 'recoil';
import { toastState } from '@/store/toast/atom/toast.atom';
import BoardType from '@/types/board/board';
import { Team } from '@/types/team/team';
import { Socket } from 'socket.io-client';
import Flex from '@/components/Primitives/Flex';
import ListBoards from '../ListBoards';
import EmptyPersonalBoards from './EmptyPersonalBoards.tsx';

interface ListBoardsByTeamProps {
userId: string;
isSuperAdmin: boolean;
socket: Socket | null;
}

const ListPersonalBoards = ({ userId, isSuperAdmin, socket }: ListBoardsByTeamProps) => {
const setToastState = useSetRecoilState(toastState);
const scrollRef = useRef<HTMLDivElement>(null);

const fetchBoardsByTeam = useInfiniteQuery(
['boards/personal'],
({ pageParam = 0 }) => getPersonalBoardsRequest(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 } = fetchBoardsByTeam;

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('personal');
const date = new Date(board.updatedAt).toDateString();
if (!boardsOfTeam) {
boardsTeamAndDate.set('personal', new Map([[date, [board]]]));
return;
}
const boardsOfDay = boardsOfTeam.get(date);
if (boardsOfDay) {
boardsOfDay.push(board);
return;
}
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 && fetchBoardsByTeam.hasNextPage) {
fetchBoardsByTeam.fetchNextPage();
}
}
};

if (dataByTeamAndDate.boardsTeamAndDate.size === 0 && !isLoading) {
return (
<Flex key="personal" css={{ mb: '$24' }} direction="column">
<EmptyPersonalBoards />
</Flex>
);
}

return (
<ListBoards
userId={userId}
isSuperAdmin={isSuperAdmin}
dataByTeamAndDate={dataByTeamAndDate}
scrollRef={scrollRef}
onScroll={onScroll}
filter="personal"
isLoading={isLoading}
socket={socket}
/>
);
};

export default ListPersonalBoards;
35 changes: 20 additions & 15 deletions frontend/src/components/Boards/MyBoards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import { Team } from '@/types/team/team';
import { ToastStateEnum } from '@/utils/enums/toast-types';
import isEmpty from '@/utils/isEmpty';
import { useRouter } from 'next/router';
import { teamsListState } from '@/store/team/atom/team.atom';
import { userTeamsListState } from '@/store/team/atom/team.atom';
import { filterTeamBoardsState } from '@/store/board/atoms/board.atom';
import FilterBoards from '../Filters/FilterBoards';
import ListBoardsByTeam from './ListBoardsByTeam';
import ListBoards from './ListBoards';
import ListPersonalBoards from './ListPersonalBoards';

interface MyBoardsProps {
userId: string;
Expand All @@ -33,7 +34,7 @@ const MyBoards = React.memo<MyBoardsProps>(({ userId, isSuperAdmin }) => {
const scrollRef = useRef<HTMLDivElement>(null);
const router = useRouter();
const routerTeam = router.query.team as string;
const teamsList = useRecoilValue(teamsListState);
const userTeamsList = useRecoilValue(userTeamsListState);

const fetchBoards = useInfiniteQuery(
'boards',
Expand Down Expand Up @@ -108,15 +109,15 @@ const MyBoards = React.memo<MyBoardsProps>(({ userId, isSuperAdmin }) => {
}
};

const teamNames: OptionType[] = teamsList.map((team) => ({
const teamNames: OptionType[] = userTeamsList.map((team) => ({
value: team._id,
label: team.name,
}));

if (filterState === 'all' && isEmpty(dataByTeamAndDate.boardsTeamAndDate.size) && !isLoading)
return <EmptyBoards />;

const filteredTeam: Team | undefined = teamsList.find((team) => team._id === filterState);
const filteredTeam: Team | undefined = userTeamsList.find((team) => team._id === filterState);

return (
<>
Expand All @@ -130,17 +131,21 @@ const MyBoards = React.memo<MyBoardsProps>(({ userId, isSuperAdmin }) => {
socket={socket}
/>
)}

<ListBoards
userId={userId}
isSuperAdmin={isSuperAdmin}
dataByTeamAndDate={dataByTeamAndDate}
scrollRef={scrollRef}
onScroll={onScroll}
filter={filterState}
isLoading={isLoading}
socket={socket}
/>
{filterState === 'personal' && (
<ListPersonalBoards userId={userId} isSuperAdmin={isSuperAdmin} socket={socket} />
)}
{filterState === 'all' && (
<ListBoards
userId={userId}
isSuperAdmin={isSuperAdmin}
dataByTeamAndDate={dataByTeamAndDate}
scrollRef={scrollRef}
onScroll={onScroll}
filter={filterState}
isLoading={isLoading}
socket={socket}
/>
)}
</>
);
});
Expand Down
19 changes: 14 additions & 5 deletions frontend/src/pages/boards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,32 @@ import Layout from '@/components/layouts/Layout';
import LoadingPage from '@/components/loadings/LoadingPage';
import Flex from '@/components/Primitives/Flex';
import useTeam from '@/hooks/useTeam';
import { teamsListState } from '@/store/team/atom/team.atom';
import { teamsListState, userTeamsListState } from '@/store/team/atom/team.atom';
import { useSetRecoilState } from 'recoil';
import { dehydrate, QueryClient } from 'react-query';
import { getTeamsOfUser } from '@/api/teamService';
import { getAllTeams, getTeamsOfUser } from '@/api/teamService';

const Boards = () => {
const { data: session } = useSession({ required: true });
const setTeamsList = useSetRecoilState(teamsListState);
const setUserTeamsList = useSetRecoilState(userTeamsListState);
const setAllTeamsList = useSetRecoilState(teamsListState);

const {
fetchTeamsOfUser: { data },
fetchAllTeams: { data: dataAllTeams },
} = useTeam();

useEffect(() => {
if (data) {
setTeamsList(data);
setUserTeamsList(data);
}
}, [data, setTeamsList]);
}, [data, setUserTeamsList]);

useEffect(() => {
if (dataAllTeams) {
setAllTeamsList(dataAllTeams);
}
}, [dataAllTeams, setAllTeamsList]);

if (!session) return null;
return (
Expand All @@ -47,6 +55,7 @@ export const getServerSideProps: GetServerSideProps = requireAuthentication(
async (context: GetServerSidePropsContext) => {
const queryClient = new QueryClient();
await queryClient.prefetchQuery('teams', () => getTeamsOfUser(undefined, context));
await queryClient.prefetchQuery('allTeams', () => getAllTeams(context));
return {
props: {
dehydratedState: JSON.parse(JSON.stringify(dehydrate(queryClient))),
Expand Down