diff --git a/components/Loading/index.tsx b/components/Loading/index.tsx new file mode 100644 index 0000000..3677ad6 --- /dev/null +++ b/components/Loading/index.tsx @@ -0,0 +1,21 @@ +import { Divider } from "antd"; + +export default function Loading() { + return ( +
+ {/* header */} +
+ +
+
+ +
Loading
+ + +
+ +
+ ) +} \ No newline at end of file diff --git a/contexts/teamDashboardContext.tsx b/contexts/teamDashboardContext.tsx new file mode 100644 index 0000000..8305ec5 --- /dev/null +++ b/contexts/teamDashboardContext.tsx @@ -0,0 +1,148 @@ +import { useRouter } from "next/router"; +import React, { useContext, useEffect, useState } from "react"; +import Loading from "../components/Loading"; +import { Team } from "../models/team"; +import { TeamMember, TeamMemberStatus } from "../models/teamMember"; +import { User } from "../models/user"; +import TeamService from "../services/teamService"; +import UserService from "../services/userService"; +import { useAuth } from "./authContext"; + +interface TeamDashboardContextInterface { + team: Team; + teamMembers: TeamMember[]; + userTeamMember: TeamMember; + user: User; +} + +const TeamDashboardContext = + React.createContext(null); + +const teamService = new TeamService(); +const userService = new UserService(); + +export function TeamDashboardContextProvider({ children }) { + const { currUser } = useAuth(); + const [loading, setLoading] = useState(true); + const router = useRouter(); + const { teamid } = router.query; + + // this context can only be used if it's a specific route: '/teams' + if (!teamid) { + router.push("/teams"); + } + + // update this "global" object to send to children + const [value, setValue] = useState( + {} as TeamDashboardContextInterface + ); + + useEffect(() => { + (async function () { + try { + // SECTION: authentication + if (!currUser) { + console.log( + "not authenticated...routing from dashboard to teams home" + ); + router.push("/teams/login"); + return; + } + + // SECTION: get user details + const returnedUser = await userService.getUser(currUser.uid); + setValue((prevValue) => ({ ...prevValue, user: returnedUser })); + + // SECTION: getting team data and team member information + if (typeof teamid !== "string") { + console.log("not a proper query with teamid"); + router.push("/teams"); + return; + } + + // check if the team is a valid team and that user is in it + const returnedTeam = await teamService.getTeam(teamid); + if (!returnedTeam) { + console.log("team doesnt exist"); + router.push("/teams"); + return; + } + setValue((prevValue) => ({ ...prevValue, team: returnedTeam })); + + const returnedTeamMember = await teamService.getTeamMemberByUserId( + teamid, + currUser.uid + ); + + // if you are not a member of this team, then get out of here + // but have to check through email invite and userid + if ( + returnedTeamMember && + returnedTeamMember.status == TeamMemberStatus.deleted + ) { + console.log("user was deleted from team"); + router.push("/teams"); + return; + } + + // if there is no team member through user id, + // give him last chance and see if he was invited + if (!returnedTeamMember) { + const invitedTeamMember = + await teamService.getTeamMemberByEmailInvite( + teamid, + currUser.email + ); + + if (!invitedTeamMember) { + console.log("not invited to team either"); + router.push("/teams"); + return; + } + + // if I am new to the team but I was invited, and this is my first time, then activate me into the team + // and proceed with showing the dashboard stuff + + invitedTeamMember.status = TeamMemberStatus.activated; + invitedTeamMember.userId = currUser.uid; + await teamService.updateTeamMember(invitedTeamMember); + + setValue((prevValue) => ({ + ...prevValue, + userTeamMember: invitedTeamMember, + })); + } else { + setValue((prevValue) => ({ + ...prevValue, + userTeamMember: returnedTeamMember, + })); + } + + // SECTION: get all teammembers for team, will have listeners in other subcomponents + const teamMembers = await teamService.getTeamMembersByTeamId( + returnedTeam.id + ); + setValue((prevValue) => ({ ...prevValue, teamMembers: teamMembers })); + } catch (error) { + console.log(error); + router.push("/teams/login"); + } + + setLoading(false); + })(); + }, []); + + if (loading) { + return ; + } + + return ( + + {children} + + ); +} + +export function useTeamDashboardContext() { + return useContext(TeamDashboardContext); +} diff --git a/pages/teams/[teamid].tsx b/pages/teams/[teamid].tsx index 60a8870..c4429d9 100644 --- a/pages/teams/[teamid].tsx +++ b/pages/teams/[teamid].tsx @@ -1,99 +1,35 @@ -import { GetServerSidePropsContext, GetServerSidePropsResult } from 'next' -import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' -import BackgroundLayout from '../../components/Layouts/BackgroundLayout' -import { useAuth } from '../../contexts/authContext' -import { Team } from '../../models/team' -import { TeamMemberStatus } from '../../models/teamMember' -import TeamService from '../../services/teamService' +import { GetServerSidePropsContext, GetServerSidePropsResult } from "next"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import BackgroundLayout from "../../components/Layouts/BackgroundLayout"; +import Loading from "../../components/Loading"; +import { useAuth } from "../../contexts/authContext"; +import { + TeamDashboardContextProvider, + useTeamDashboardContext, +} from "../../contexts/teamDashboardContext"; +import { Team } from "../../models/team"; +import { TeamMemberStatus } from "../../models/teamMember"; +import TeamService from "../../services/teamService"; -const teamService = new TeamService() +function TeamDashboard() { + const { currUser } = useAuth(); + const router = useRouter(); + const teamDashboardContext = useTeamDashboardContext(); -export default function TeamDashboard() { - const { currUser } = useAuth() - const router = useRouter() - const { teamid } = router.query + console.log(teamDashboardContext); - const [loading, setLoading] = useState(true) - const [team, setTeam] = useState(null) - - useEffect(() => { - (async function() { - try { - // if not authenticated, take user to the login - if (!currUser) { - console.log('not authenticated...routing from dashboard to teams home') - router.push('/teams/login') - return - } - - if (typeof teamid !== "string") { - console.log('not a proper query with teamid') - router.push('/teams') - return - } - - // check if the team is a valid team and that user is in it - const returnedTeam = await teamService.getTeam(teamid) - if (!returnedTeam) { - console.log('team doesnt exist') - router.push('/teams') - return - } - - const returnedTeamMember = await teamService.getTeamMemberByUserId(teamid, currUser.uid) - - console.log(returnedTeam) - console.log(returnedTeamMember) - - // if you are not a member of this team, then get out of here - // but have to check through email invite and userid - if (returnedTeamMember && returnedTeamMember.status == TeamMemberStatus.deleted) { - console.log('user was deleted from team') - router.push('/teams') - return - } - - // if there is no team member through user id, - // give him last chance and see if he was invited - if (!returnedTeamMember) { - const invitedTeamMember = await teamService.getTeamMemberByEmailInvite(teamid, currUser.email) - - if (!invitedTeamMember) { - console.log('not invited to team either') - router.push('/teams') - return - } - - // if I am new to the team but I was invited, and this is my first time, then activate me into the team - // and proceed with showing the dashboard stuff - - invitedTeamMember.status = TeamMemberStatus.activated - invitedTeamMember.userId = currUser.uid - await teamService.updateTeamMember(invitedTeamMember) - } - } catch(error) { - console.log(error) - router.push('/teams/login') - } - - setLoading(false) - })(); - }, []) - - if (loading) { - return ( -
loading
- ) - } + return
{JSON.stringify(teamDashboardContext)}
; +} +export default function TeamDashboardWrapper() { return ( - - the team is {teamid} - - - - ) + + + + + + ); } export async function getServerSideProps(context: GetServerSidePropsContext) { @@ -105,5 +41,5 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { return { props: {}, - } -} \ No newline at end of file + }; +} diff --git a/pages/teams/index.tsx b/pages/teams/index.tsx index a803708..952745a 100644 --- a/pages/teams/index.tsx +++ b/pages/teams/index.tsx @@ -1,72 +1,80 @@ -import { useAuth } from "../../contexts/authContext" -import { useRouter } from "next/router" -import { useEffect, useState } from "react" -import UserService from '../../services/userService' -import { User } from "../../models/user" -import { GetServerSidePropsContext } from "next" -import nookies from 'nookies' -import firebaseAdmin from 'firebase-admin' -import unfetch from 'isomorphic-unfetch' -import { - FaPeopleCarry, - FaArrowRight, - FaBullhorn -} from "react-icons/fa"; -import BackgroundLayout from "../../components/Layouts/BackgroundLayout" -import TeamService from "../../services/teamService" +import { useAuth } from "../../contexts/authContext"; +import { useRouter } from "next/router"; +import { useEffect, useState } from "react"; +import UserService from "../../services/userService"; +import { User } from "../../models/user"; +import { GetServerSidePropsContext } from "next"; +import nookies from "nookies"; +import firebaseAdmin from "firebase-admin"; +import unfetch from "isomorphic-unfetch"; +import { FaPeopleCarry, FaArrowRight, FaBullhorn } from "react-icons/fa"; +import BackgroundLayout from "../../components/Layouts/BackgroundLayout"; +import TeamService from "../../services/teamService"; /** * figure out where to take the user based on everything */ -const userService: UserService = new UserService() -const teamService: TeamService = new TeamService() +const userService: UserService = new UserService(); +const teamService: TeamService = new TeamService(); function RouteHandler() { - const { currUser } = useAuth() - const router = useRouter() - - const [loading, setLoading] = useState(true) - const [user, setUser] = useState(null) + const { currUser } = useAuth(); + const router = useRouter(); + + const [loading, setLoading] = useState(true); + const [user, setUser] = useState(null); useEffect(() => { - (async function() { + (async function () { try { // if not authenticated, take user to the login if (!currUser) { - console.log('not authenticated...routing from dashboard to teams home') - router.push('/teams/login') + console.log( + "not authenticated...routing from dashboard to teams home" + ); + router.push("/teams/login"); } - // get user - const returnedUser: User | null = await userService.getUser(currUser.uid) - console.log(returnedUser) + // get user + const returnedUser: User | null = await userService.getUser( + currUser.uid + ); + console.log(returnedUser); // if user has no profile, then go to create profile - if (!returnedUser || !returnedUser.firstName || !returnedUser.lastName || !returnedUser.nickName) { - console.log('no profile for the user, routing him/her there') - router.push('/teams/profile') - } - setUser(returnedUser) - - // check if user is in a team go to the team dashboard - const returnedTeamMembers = await teamService.getTeamMembersByUserId(currUser.uid) - if (returnedTeamMembers.length > 0) { - console.log('in a team! going to the team dashboard of the first one!') - router.push('/teams/' + returnedTeamMembers[0].teamId) - return + if ( + !returnedUser || + !returnedUser.firstName || + !returnedUser.lastName || + !returnedUser.nickName + ) { + console.log("no profile for the user, routing him/her there"); + router.push("/teams/profile"); } + setUser(returnedUser); - } catch(error) { - console.log(error) - router.push('/teams/login') + // check if user is in a team go to the team dashboard + const returnedTeamMembers = await teamService.getTeamMembersByUserId( + currUser.uid + ); + if (returnedTeamMembers.length > 0) { + console.log( + "in a team! going to the team dashboard of the first one!" + ); + router.push("/teams/" + returnedTeamMembers[0].teamId); + return; + } + } catch (error) { + console.log(error); + router.push("/teams/login"); } })(); - setLoading(false) - }, [currUser]) + setLoading(false); + }, [currUser]); if (loading || !user) { - return
+ return
; } // not in a team yet, and have not created a team yet @@ -76,25 +84,36 @@ function RouteHandler() {
👋Hey, {user.firstName}
- Let's get you started. + + Let's get you started. +
- -
{/* create team hover thing */} - @@ -104,14 +123,23 @@ function RouteHandler() { Remind Your Manager - Your account email is {user.emailAddress} + + Your account email is {user.emailAddress} + - - or learn more about + + or learn more about{" "} + + - ) + ); } export async function getServerSideProps(context: GetServerSidePropsContext) { @@ -143,14 +171,14 @@ export async function getServerSideProps(context: GetServerSidePropsContext) { // console.log(e) // } // } - + // // Pass data to the page via props - // return { props: { + // return { props: { // user // } // } - return { props: { }} + return { props: {} }; } -export default RouteHandler +export default RouteHandler; diff --git a/services/teamService.tsx b/services/teamService.tsx index a34e56c..16660b0 100644 --- a/services/teamService.tsx +++ b/services/teamService.tsx @@ -1,34 +1,55 @@ -import { addDoc, collection, doc, DocumentReference, Firestore, getDoc, getDocs, getFirestore, orderBy, query, serverTimestamp, setDoc, where } from 'firebase/firestore' -import { Team } from '../models/team' -import { TeamMember, TeamMemberRole, TeamMemberStatus } from '../models/teamMember' -import { Collections } from './collections' -import IService from './IService' +import { + addDoc, + collection, + doc, + DocumentReference, + Firestore, + getDoc, + getDocs, + getFirestore, + orderBy, + query, + serverTimestamp, + setDoc, + where, +} from "firebase/firestore"; +import { Team } from "../models/team"; +import { + TeamMember, + TeamMemberRole, + TeamMemberStatus, +} from "../models/teamMember"; +import { Collections } from "./collections"; +import IService from "./IService"; export default class TeamService implements IService { - db : Firestore = getFirestore() + db: Firestore = getFirestore(); async createTeam(team: Team): Promise { // create team const teamDocRef = await addDoc(collection(this.db, Collections.teams), { ...team, - createdDate: serverTimestamp() - }) + createdDate: serverTimestamp(), + }); - const teamMember = new TeamMember() - teamMember.role = TeamMemberRole.admin - teamMember.teamId = teamDocRef.id - teamMember.userId = team.createdByUserId - teamMember.status = TeamMemberStatus.activated + const teamMember = new TeamMember(); + teamMember.role = TeamMemberRole.admin; + teamMember.teamId = teamDocRef.id; + teamMember.userId = team.createdByUserId; + teamMember.status = TeamMemberStatus.activated; // create team member as admin who created the team - const teamMemberRef = await addDoc(collection(this.db, Collections.teamMembers), { - ...teamMember, - createdDate: serverTimestamp() - }) + const teamMemberRef = await addDoc( + collection(this.db, Collections.teamMembers), + { + ...teamMember, + createdDate: serverTimestamp(), + } + ); - console.log('created team') + console.log("created team"); - return teamDocRef.id + return teamDocRef.id; } async getTeam(teamId: string): Promise { @@ -36,105 +57,135 @@ export default class TeamService implements IService { const docSnap = await getDoc(docRef); if (docSnap.exists()) { - console.log('got team data') - let team: Team = docSnap.data() as Team - team.id = docSnap.id - return team + console.log("got team data"); + let team: Team = docSnap.data() as Team; + team.id = docSnap.id; + return team; } else { // doc.data() will be undefined in this case console.log("team not found!"); - - return null + + return null; } } - - async getTeamMemberByUserId(teamId: string, userId: string): Promise { + + async getTeamMemberByUserId( + teamId: string, + userId: string + ): Promise { const q = query( - collection(this.db, Collections.teamMembers), + collection(this.db, Collections.teamMembers), where("teamId", "==", teamId), where("userId", "==", userId) - ) - + ); + const querySnapshot = await getDocs(q); - + if (querySnapshot.size > 1) { - console.log('there are multiple teammembers for this user...error in teamservice') + console.log( + "there are multiple teammembers for this user...error in teamservice" + ); } - console.log(querySnapshot) - - var teamMember: TeamMember | null = null + var teamMember: TeamMember | null = null; querySnapshot.forEach((doc) => { // doc.data() is never undefined for query doc snapshots // get the first one and just return...shouldn't be more - console.log('got teammember data') - teamMember = doc.data() as TeamMember + console.log("got teammember data"); + teamMember = doc.data() as TeamMember; - teamMember.id = doc.id + teamMember.id = doc.id; }); - return teamMember + return teamMember; } - async getTeamMemberByEmailInvite(teamId: string, emailAddress: string): Promise { + async getTeamMemberByEmailInvite( + teamId: string, + emailAddress: string + ): Promise { const q = query( - collection(this.db, Collections.teamMembers), + collection(this.db, Collections.teamMembers), where("teamId", "==", teamId), where("inviteEmailAddress", "==", emailAddress) - ) - + ); + const querySnapshot = await getDocs(q); - + if (querySnapshot.size > 1) { - console.log('there are multiple teammembers for this user...error in teamservice') + console.log( + "there are multiple teammembers for this user...error in teamservice" + ); } - console.log(querySnapshot) - - var teamMember: TeamMember | null = null + var teamMember: TeamMember | null = null; querySnapshot.forEach((doc) => { // doc.data() is never undefined for query doc snapshots // get the first one and just return...shouldn't be more - console.log('got teammember data') - teamMember = doc.data() as TeamMember + console.log("got teammember data"); + teamMember = doc.data() as TeamMember; - teamMember.id = doc.id + teamMember.id = doc.id; }); - return teamMember + return teamMember; } async getTeamMembersByUserId(userId: string): Promise { const q = query( - collection(this.db, Collections.teamMembers), - where("userId", "==", userId), - ) - + collection(this.db, Collections.teamMembers), + where("userId", "==", userId) + ); + const querySnapshot = await getDocs(q); - + if (querySnapshot.size > 1) { - console.log('this user is part of multiple teams') + console.log("this user is part of multiple teams"); } - console.log(querySnapshot) - - var teamMembers: TeamMember[] = [] + var teamMembers: TeamMember[] = []; querySnapshot.forEach((doc) => { // doc.data() is never undefined for query doc snapshots // get the first one and just return...shouldn't be more - console.log('got teammember data') - let teamMember:TeamMember = doc.data() as TeamMember - teamMember.id = doc.id + console.log("got teammember data"); + let teamMember: TeamMember = doc.data() as TeamMember; + teamMember.id = doc.id; - teamMembers.push(teamMember) + teamMembers.push(teamMember); }); - return teamMembers + return teamMembers; + } + + async getTeamMembersByTeamId(teamId: string): Promise { + const q = query( + collection(this.db, Collections.teamMembers), + where("teamId", "==", teamId) + ); + + const querySnapshot = await getDocs(q); + + var teamMembers: TeamMember[] = []; + + querySnapshot.forEach((doc) => { + // doc.data() is never undefined for query doc snapshots + // get the first one and just return...shouldn't be more + let teamMember: TeamMember = doc.data() as TeamMember; + teamMember.id = doc.id; + + teamMembers.push(teamMember); + }); + + return teamMembers; } async updateTeamMember(teamMember: TeamMember) { const docRef = doc(this.db, Collections.teamMembers, teamMember.id); - await setDoc(docRef, { ...teamMember, lastUpdatedDate: serverTimestamp() }, { merge: true }) - } -} \ No newline at end of file + await setDoc( + docRef, + { ...teamMember, lastUpdatedDate: serverTimestamp() }, + { merge: true } + ); + } +}