From 99603f097b458d7e4a83eeeab4c82693b49248e4 Mon Sep 17 00:00:00 2001 From: willmt80 Date: Wed, 7 Apr 2021 21:02:03 -0400 Subject: [PATCH 1/5] connecting team page to the store --- src/containers/teamPage/index.tsx | 256 +++++++++++++----------------- 1 file changed, 106 insertions(+), 150 deletions(-) diff --git a/src/containers/teamPage/index.tsx b/src/containers/teamPage/index.tsx index e393b51c..7b23d2f0 100644 --- a/src/containers/teamPage/index.tsx +++ b/src/containers/teamPage/index.tsx @@ -1,4 +1,5 @@ -import React from 'react'; +import React, { useEffect } from 'react'; +import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import { Button, Collapse, List, Typography } from 'antd'; import { Routes } from '../../App'; @@ -15,6 +16,12 @@ import { MID_GREEN, WHITE, } from '../../utils/colors'; +import { connect, useDispatch } from 'react-redux'; +import { C4CState } from '../../store'; +import { AsyncRequestKinds } from '../../utils/asyncRequest'; +import { getTeam } from './ducks/thunks'; +import { teamResponseRequestToTeamProps } from './ducks/selectors'; +import ProtectedApiClient from '../../api/protectedApiClient'; const { Paragraph } = Typography; const { Panel } = Collapse; @@ -95,162 +102,111 @@ const Line = styled.div` background-color: ${WHITE}; `; -// dummy data -const sampleMemberData: MemberProps[] = [ - { - userId: 1, - username: 'florisdobber', - teamRole: TeamRole.LEADER, - }, - { - userId: 2, - username: 'jackblanc', - teamRole: TeamRole.MEMBER, - }, - { - userId: 3, - username: 'atreecounter', - teamRole: TeamRole.NONE, - }, - { - userId: 4, - username: 'speakerofthetrees', - teamRole: TeamRole.PENDING, - }, - { - userId: 5, - username: 'vrushalitarte01', - teamRole: TeamRole.MEMBER, - }, - { - userId: 6, - username: 'willmt80', - teamRole: TeamRole.MEMBER, - }, - { - userId: 7, - username: 'apradhan12', - teamRole: TeamRole.MEMBER, - }, - { - userId: 8, - username: 'SofieDunt', - teamRole: TeamRole.MEMBER, - }, -]; +interface TeamPageProps { + readonly teamProps: TeamProps; + readonly teamRequestKind: AsyncRequestKinds; +} -const sampleGoalData: GoalProps[] = [ - { - id: 1, - goal: 50, - progress: 5, - startDate: new Date(2021, 0, 1), - completeBy: new Date(2022, 0, 1), - }, - { - id: 2, - goal: 500, - progress: 71, - startDate: new Date(2021, 2, 20), - completeBy: new Date(2021, 4, 21), - }, - { - id: 3, - goal: 10, - progress: 10, - startDate: new Date(2020, 5, 10), - completeBy: new Date(2020, 7, 10), - completionDate: new Date(2020, 6, 13), - }, -]; +const TeamPage: React.FC = ({ teamProps, teamRequestKind }) => { + const dispatch = useDispatch(); -const sampleTeamData: TeamProps = { - id: 1, - name: 'Code4Community', - bio: 'Short description about how cool of a team Code4Community is!', - members: sampleMemberData, - goals: sampleGoalData, -}; + useEffect(() => { + // The default tab is weekly + dispatch(getTeam(numId)); + }, [dispatch]); + + const { id } = useParams<{ id: string }>(); + const numId = +id; -const TeamPage: React.FC = () => { - const onClick = () => { - // TODO: join team + const applyToTeam = () => { + ProtectedApiClient.applyToTeam(numId); }; return ( - - - {`<`} Return to Teams - - - - - - - Join Team - - - - - - TEAM MEMBERS - - {sampleTeamData.members.map((member) => { - if ( - member.teamRole === TeamRole.LEADER || - member.teamRole === TeamRole.MEMBER - ) { - return ( - - ); - } - return <>; - })} - - - - TEAM GOALS - - {sampleTeamData.goals.map((item) => { - return ( - Goal {item.id}} - extra={ - item.completionDate == null && ( - Incomplete - ) + <> + {teamRequestKind === AsyncRequestKinds.Completed && ( + + + {`<`} Return to Teams + + + + + + + Join Team + + + + + + TEAM MEMBERS + + {teamProps.members.map((member) => { + if ( + member.teamRole === TeamRole.LEADER || + member.teamRole === TeamRole.MEMBER + ) { + return ( + + ); } - > - - - - ); - })} - - - - + return <>; + })} + + + + TEAM GOALS + + {teamProps.goals.map((item) => { + return ( + Goal {item.id}} + extra={ + item.completionDate == null && ( + Incomplete + ) + } + > + + + + ); + })} + + + + + )} + ); }; -export default TeamPage; +const mapStateToProps = (state: C4CState): TeamPageProps => { + return { + teamProps: teamResponseRequestToTeamProps(state.teamState.team), + teamRequestKind: state.teamState.team.kind, + }; +}; + +export default connect(mapStateToProps)(TeamPage); From 1656b1fc81a284d19f37c90b433c32ef2379510c Mon Sep 17 00:00:00 2001 From: willmt80 Date: Wed, 7 Apr 2021 21:52:38 -0400 Subject: [PATCH 2/5] build fixes --- src/containers/teamPage/index.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/containers/teamPage/index.tsx b/src/containers/teamPage/index.tsx index 7b23d2f0..2a43e9e3 100644 --- a/src/containers/teamPage/index.tsx +++ b/src/containers/teamPage/index.tsx @@ -3,7 +3,7 @@ import { useParams } from 'react-router-dom'; import styled from 'styled-components'; import { Button, Collapse, List, Typography } from 'antd'; import { Routes } from '../../App'; -import { GoalProps, MemberProps, TeamProps, TeamRole } from './ducks/types'; +import { TeamProps, TeamRole } from './ducks/types'; import GoalInfo from '../../components/goalInfo'; import PageHeader from '../../components/pageHeader'; import TeamMember from '../../components/teamMember'; @@ -110,13 +110,13 @@ interface TeamPageProps { const TeamPage: React.FC = ({ teamProps, teamRequestKind }) => { const dispatch = useDispatch(); + const { id } = useParams<{ id: string }>(); + const numId = +id; + useEffect(() => { // The default tab is weekly dispatch(getTeam(numId)); - }, [dispatch]); - - const { id } = useParams<{ id: string }>(); - const numId = +id; + }, [dispatch, numId]); const applyToTeam = () => { ProtectedApiClient.applyToTeam(numId); From 17d0a10b342e09926ed067356f62a189d15bfe23 Mon Sep 17 00:00:00 2001 From: willmt80 Date: Sat, 17 Jul 2021 17:10:01 -0400 Subject: [PATCH 3/5] updated request slightly to correct request --- src/App.tsx | 9 ++++++--- src/containers/teamPage/ducks/selectors.ts | 4 ++-- src/containers/teamPage/ducks/thunks.ts | 3 ++- src/containers/teamPage/ducks/types.ts | 12 ++++++------ src/containers/teamPage/index.tsx | 4 +++- 5 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index e22caf71..848fdf2e 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -18,6 +18,7 @@ import styled from 'styled-components'; import Landing from './containers/landing'; import AdminDashboard from './containers/adminDashboard'; import TreePage from './containers/treePage'; +import TeamPage from './containers/teamPage'; import MyTrees from './containers/myTrees'; import { Layout } from 'antd'; import Home from './containers/home'; @@ -51,7 +52,7 @@ export enum Routes { HOME = '/home', SETTINGS = '/settings', // VOLUNTEER = '/volunteer', - // TEAM = '/team/:id', + TEAM = '/team/:id', TREE = '/tree/:id', MY_TREES = '/my-trees', // TEAM_LEADERBOARD = '/team-leaderboard', @@ -97,9 +98,9 @@ const App: React.FC = () => { + {/* - @@ -137,6 +138,7 @@ const App: React.FC = () => { component={Settings} /> + {/* { component={Settings} /> + {/* { exact component={AvailableTeams} /> - + { return { id: team.id, - name: team.name, + teamName: team.teamName, bio: team.bio, members: team.members, goals: mapGoalResponseJSONToGoalProps(team.goals), @@ -41,7 +41,7 @@ const mapGoalResponseJSONToGoalProps = ( // This is to prevent the TeamProps from being undefined const emptyTeam: () => TeamProps = () => ({ id: 0, - name: '', + teamName: '', bio: '', members: [], goals: [], diff --git a/src/containers/teamPage/ducks/thunks.ts b/src/containers/teamPage/ducks/thunks.ts index 1492ce2d..3e13e886 100644 --- a/src/containers/teamPage/ducks/thunks.ts +++ b/src/containers/teamPage/ducks/thunks.ts @@ -1,8 +1,9 @@ import { TeamResponse, TeamThunkAction } from './types'; import { teamResponse } from './actions'; +import protectedApiClient from '../../../api/protectedApiClient'; export const getTeam = (teamId: number): TeamThunkAction => { - return (dispatch, getState, { protectedApiClient }) => { + return (dispatch, getState) => { dispatch(teamResponse.loading()); return protectedApiClient .getTeam(teamId) diff --git a/src/containers/teamPage/ducks/types.ts b/src/containers/teamPage/ducks/types.ts index 811b4276..b07466c5 100644 --- a/src/containers/teamPage/ducks/types.ts +++ b/src/containers/teamPage/ducks/types.ts @@ -6,7 +6,7 @@ import { ProtectedApiExtraArgs } from '../../../api/protectedApiClient'; export interface TeamProps { id: number; - name: string; + teamName: string; bio: string; members: MemberProps[]; goals: GoalProps[]; @@ -28,10 +28,10 @@ export interface GoalProps { } export enum TeamRole { - NONE = 'none', - MEMBER = 'member', - LEADER = 'leader', - PENDING = 'pending', + NONE = 'NONE', + MEMBER = 'MEMBER', + LEADER = 'LEADER', + PENDING = 'PENDING', } export interface UserInvite { @@ -69,7 +69,7 @@ export interface TransferOwnershipRequest { export interface TeamResponse { id: number; - name: string; + teamName: string; bio: string; members: MemberProps[]; goals: GoalResponseJSON[]; diff --git a/src/containers/teamPage/index.tsx b/src/containers/teamPage/index.tsx index 2a43e9e3..f7723e54 100644 --- a/src/containers/teamPage/index.tsx +++ b/src/containers/teamPage/index.tsx @@ -122,6 +122,8 @@ const TeamPage: React.FC = ({ teamProps, teamRequestKind }) => { ProtectedApiClient.applyToTeam(numId); }; + console.log(teamProps); + return ( <> {teamRequestKind === AsyncRequestKinds.Completed && ( @@ -132,7 +134,7 @@ const TeamPage: React.FC = ({ teamProps, teamRequestKind }) => { From 770c7ce1beeaeb7d11a103acf21b3fadc40efb90 Mon Sep 17 00:00:00 2001 From: willmt80 Date: Sat, 17 Jul 2021 17:12:40 -0400 Subject: [PATCH 4/5] fixed missing teamName changes --- src/api/test/protectedApiClient.test.ts | 6 +++--- src/containers/availableTeams/ducks/selectors.ts | 2 +- src/containers/teamPage/index.tsx | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/api/test/protectedApiClient.test.ts b/src/api/test/protectedApiClient.test.ts index 0e6502d3..595fff64 100644 --- a/src/api/test/protectedApiClient.test.ts +++ b/src/api/test/protectedApiClient.test.ts @@ -508,7 +508,7 @@ describe('Protected API Client Tests', () => { const response: TeamResponse[] = [ { id: 1, - name: 'team 1', + teamName: 'team 1', bio: 'this is team 1', members: [ { @@ -532,7 +532,7 @@ describe('Protected API Client Tests', () => { }, { id: 2, - name: 'team 2', + teamName: 'team 2', bio: 'this is team 2', members: [ { @@ -571,7 +571,7 @@ describe('Protected API Client Tests', () => { it('makes the right request', async () => { const response: TeamResponse = { id: 1, - name: 'team 1', + teamName: 'team 1', bio: 'this is team 1', members: [ { diff --git a/src/containers/availableTeams/ducks/selectors.ts b/src/containers/availableTeams/ducks/selectors.ts index 38c8cfec..3f1ebfc1 100644 --- a/src/containers/availableTeams/ducks/selectors.ts +++ b/src/containers/availableTeams/ducks/selectors.ts @@ -12,7 +12,7 @@ export const availableTeamsToTabItems = ( return availableTeams.result.map((team) => { return { id: team.id, - name: team.name, + name: team.teamName, rightSide: '', }; }); diff --git a/src/containers/teamPage/index.tsx b/src/containers/teamPage/index.tsx index f7723e54..d4d08b29 100644 --- a/src/containers/teamPage/index.tsx +++ b/src/containers/teamPage/index.tsx @@ -122,8 +122,6 @@ const TeamPage: React.FC = ({ teamProps, teamRequestKind }) => { ProtectedApiClient.applyToTeam(numId); }; - console.log(teamProps); - return ( <> {teamRequestKind === AsyncRequestKinds.Completed && ( From e996546c7a015d473849533b3b721b78b10ab144 Mon Sep 17 00:00:00 2001 From: willmt80 Date: Sat, 17 Jul 2021 17:58:09 -0400 Subject: [PATCH 5/5] add team role selector, only allow someone to join team if they aren't pending/ a member --- src/containers/teamPage/ducks/selectors.ts | 24 ++++++- src/containers/teamPage/index.tsx | 79 ++++++++++++++-------- 2 files changed, 72 insertions(+), 31 deletions(-) diff --git a/src/containers/teamPage/ducks/selectors.ts b/src/containers/teamPage/ducks/selectors.ts index 828f6400..373661c0 100644 --- a/src/containers/teamPage/ducks/selectors.ts +++ b/src/containers/teamPage/ducks/selectors.ts @@ -2,7 +2,13 @@ import { AsyncRequest, asyncRequestIsComplete, } from '../../../utils/asyncRequest'; -import { TeamResponse, TeamProps, GoalProps, GoalResponseJSON } from './types'; +import { + TeamResponse, + TeamProps, + GoalProps, + GoalResponseJSON, + TeamRole, +} from './types'; export const teamResponseRequestToTeamProps = ( team: AsyncRequest, @@ -46,3 +52,19 @@ const emptyTeam: () => TeamProps = () => ({ members: [], goals: [], }); + +export const getTeamRole = ( + team: AsyncRequest, + userId: number, +): TeamRole => { + if (asyncRequestIsComplete(team)) { + const member = team.result.members.find( + (potentialMember) => potentialMember.userId === userId, + ); + if (member) { + return member.teamRole; + } + return TeamRole.NONE; + } + return TeamRole.NONE; +}; diff --git a/src/containers/teamPage/index.tsx b/src/containers/teamPage/index.tsx index d4d08b29..345bb986 100644 --- a/src/containers/teamPage/index.tsx +++ b/src/containers/teamPage/index.tsx @@ -16,11 +16,12 @@ import { MID_GREEN, WHITE, } from '../../utils/colors'; -import { connect, useDispatch } from 'react-redux'; +import { connect, useDispatch, useSelector } from 'react-redux'; +import { getUserID } from '../../auth/ducks/selectors'; import { C4CState } from '../../store'; import { AsyncRequestKinds } from '../../utils/asyncRequest'; import { getTeam } from './ducks/thunks'; -import { teamResponseRequestToTeamProps } from './ducks/selectors'; +import { teamResponseRequestToTeamProps, getTeamRole } from './ducks/selectors'; import ProtectedApiClient from '../../api/protectedApiClient'; const { Paragraph } = Typography; @@ -113,6 +114,16 @@ const TeamPage: React.FC = ({ teamProps, teamRequestKind }) => { const { id } = useParams<{ id: string }>(); const numId = +id; + const hasGoals = teamProps.goals.length > 0; + + const userId: number = useSelector((state: C4CState) => + getUserID(state.authenticationState.tokens), + ); + + const teamRole: TeamRole = useSelector((state: C4CState) => { + return getTeamRole(state.teamState.team, userId); + }); + useEffect(() => { // The default tab is weekly dispatch(getTeam(numId)); @@ -136,7 +147,11 @@ const TeamPage: React.FC = ({ teamProps, teamRequestKind }) => { pageSubtitle={teamProps.bio} /> - + Join Team @@ -167,33 +182,37 @@ const TeamPage: React.FC = ({ teamProps, teamRequestKind }) => { TEAM GOALS - - {teamProps.goals.map((item) => { - return ( - Goal {item.id}} - extra={ - item.completionDate == null && ( - Incomplete - ) - } - > - - - - ); - })} - + {hasGoals && ( + + {teamProps.goals.map((item) => { + return ( + Goal {item.id}} + extra={ + item.completionDate == null && ( + Incomplete + ) + } + > + + + + ); + })} + + )} + + {!hasGoals && This team has no goals}