From 8e2393521cd1bcd89f46e2a05cd991df29f54da1 Mon Sep 17 00:00:00 2001 From: Shashi Lo <362527+shashilo@users.noreply.github.com> Date: Wed, 3 Jul 2024 00:35:57 -0500 Subject: [PATCH 01/19] feat #352: updating the entry page logic --- api/apiFunctions.enum.ts | 2 +- api/apiFunctions.interface.ts | 18 +++--- api/apiFunctions.ts | 35 ++++++++++- api/config.ts | 3 + app/League-Entries/page.tsx | 33 ----------- .../[leagueId]/entry/Entries.interface.ts | 12 ++++ .../[entryId]/week/Week.interface.ts | 0 .../[entryId]/week/Week.tsx | 8 +-- .../[entryId]/week/WeekTeams.interface.ts | 0 .../[entryId]/week/WeekTeams.tsx | 0 .../[entryId]/week/[weekId]/page.tsx | 0 app/league/[leagueId]/entry/all/page.tsx | 59 +++++++++++++++++++ app/league/all/page.tsx | 3 +- .../LeagueEntries/LeagueEntries.interface.ts | 5 +- components/LeagueEntries/LeagueEntries.tsx | 25 ++++---- const/global.ts | 6 ++ utils/utils.interface.ts | 4 +- utils/utils.ts | 50 +++++++++++----- 18 files changed, 186 insertions(+), 77 deletions(-) delete mode 100644 app/League-Entries/page.tsx create mode 100644 app/league/[leagueId]/entry/Entries.interface.ts rename app/league/[leagueId]/{entries => entry}/[entryId]/week/Week.interface.ts (100%) rename app/league/[leagueId]/{entries => entry}/[entryId]/week/Week.tsx (95%) rename app/league/[leagueId]/{entries => entry}/[entryId]/week/WeekTeams.interface.ts (100%) rename app/league/[leagueId]/{entries => entry}/[entryId]/week/WeekTeams.tsx (100%) rename app/league/[leagueId]/{entries => entry}/[entryId]/week/[weekId]/page.tsx (100%) create mode 100644 app/league/[leagueId]/entry/all/page.tsx create mode 100644 const/global.ts diff --git a/api/apiFunctions.enum.ts b/api/apiFunctions.enum.ts index fd46e594..122665e1 100644 --- a/api/apiFunctions.enum.ts +++ b/api/apiFunctions.enum.ts @@ -2,13 +2,13 @@ // Licensed under the MIT License. export enum Collection { + ENTRIES = 'entries', USERS = '6626c4fa8f793ae275ee', LEAGUE = '6626a937b6302f6a4d28', GAME_RESULTS = '66313025000612a5380e', GAME_WEEK = '6622c701cb5a58fcdf06', CURRENT_WEEK = 'current_week', NFL_TEAMS = '662152bfabacfbda3bb3', - NFL_SCHEDULE_2024 = 'nfl_schedule_2024', } /** diff --git a/api/apiFunctions.interface.ts b/api/apiFunctions.interface.ts index b12ceb47..12363e98 100644 --- a/api/apiFunctions.interface.ts +++ b/api/apiFunctions.interface.ts @@ -1,6 +1,8 @@ // Copyright (c) Gridiron Survivor. // Licensed under the MIT License. +import { IEntry } from '@/app/league/[leagueId]/entry/Entries.interface'; + export interface IAccountData { email: string; password: string; @@ -12,8 +14,10 @@ export interface IUser { } export interface IUserPick { [userId: string]: { - team: string; - correct: boolean; + [entryId: IEntry['id']]: { + teamName: string; + correct: boolean; + }; }; } export interface IDeleteUser { @@ -22,19 +26,13 @@ export interface IDeleteUser { export interface IWeeklyPicks { leagueId: string; gameWeekId: string; - userResults: IUserPicksData | null; + userResults: IUserPicksData; } export interface INFLTeam { teamId: string; teamName: string; - teamLogo: string; -} -export interface IUserPicksData { - [key: string]: { - team: string; - correct: boolean; - }; } +export interface IUserPicksData extends IUserPick {} export interface ILeague { leagueId: string; leagueName: string; diff --git a/api/apiFunctions.ts b/api/apiFunctions.ts index 43cab8ba..75ece26a 100644 --- a/api/apiFunctions.ts +++ b/api/apiFunctions.ts @@ -13,6 +13,7 @@ import { } from './apiFunctions.interface'; import { Collection, Document } from './apiFunctions.enum'; import { Query } from 'appwrite'; +import { IEntry } from '@/app/league/[leagueId]/entry/Entries.interface'; /** * Register a new account @@ -88,6 +89,38 @@ export async function getCurrentUser(userId: IUser['id']): Promise { } } +/** + * Get all the leagues the user is a part of + * @param userId - The user Id + * @param leagueId - The league Id + * @returns {IEntry[] | Error} - The list of entries or an error + */ +export async function getCurrentUserEntries( + userId: IUser['id'], + leagueId: ILeague['leagueId'], +): Promise { + try { + const response = await databases.listDocuments( + appwriteConfig.databaseId, + Collection.ENTRIES, + [Query.equal('user', userId), Query.equal('league', leagueId)], + ); + + const entries = response.documents.map((entry) => ({ + id: entry.$id, + name: entry.name, + user: entry.user, + league: entry.league, + selectedTeams: entry.selectedTeams, + })); + + return entries; + } catch (error) { + console.error(error); + throw new Error('Error getting user entries'); + } +} + /** * Get all NFL teams * @returns {INFLTeam | Error} - The list of NFL teams @@ -97,12 +130,12 @@ export const getNFLTeams = async (): Promise => { const response = await databases.listDocuments( appwriteConfig.databaseId, Collection.NFL_TEAMS, + [Query.limit(32)], ); const nflTeams = response.documents.map((team) => ({ teamId: team.$id, teamName: team.teamName, - teamLogo: team.teamLogo, })); return nflTeams; diff --git a/api/config.ts b/api/config.ts index 4b00abbf..c4eaf8fa 100644 --- a/api/config.ts +++ b/api/config.ts @@ -1,3 +1,6 @@ +// Copyright (c) Gridiron Survivor. +// Licensed under the MIT License. + import { Client, Account, Databases, ID } from 'appwrite'; const URL = process.env.NEXT_PUBLIC_APPWRITE_API_URL as string; diff --git a/app/League-Entries/page.tsx b/app/League-Entries/page.tsx deleted file mode 100644 index c418421b..00000000 --- a/app/League-Entries/page.tsx +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Gridiron Survivor. -// Licensed under the MIT License. - -import { LeagueEntries } from '@/components/LeagueEntries/LeagueEntries'; -import { LeagueSurvivors } from '@/components/LeagueSurvivors/LeagueSurvivors'; -import { JSX } from 'react'; - -/** - * Displays the user's entries for that specific league. - * @returns {JSX.Element} - Returns a JSX.Element with the name LeaguesEntries. - */ -const LeagueEntriesPage = (): JSX.Element => { - return ( -
-
-

- Windy City Smackdown -

- -
-
-

- Week 2 -

- - - -
-
- ); -}; - -export default LeagueEntriesPage; diff --git a/app/league/[leagueId]/entry/Entries.interface.ts b/app/league/[leagueId]/entry/Entries.interface.ts new file mode 100644 index 00000000..c25beb21 --- /dev/null +++ b/app/league/[leagueId]/entry/Entries.interface.ts @@ -0,0 +1,12 @@ +// Copyright (c) Gridiron Survivor. +// Licensed under the MIT License. + +import { ILeague, INFLTeam, IUser } from '@/api/apiFunctions.interface'; + +export interface IEntry { + id: string; + name: string; + user: IUser; + league: ILeague; + selectedTeams: INFLTeam[]; +} diff --git a/app/league/[leagueId]/entries/[entryId]/week/Week.interface.ts b/app/league/[leagueId]/entry/[entryId]/week/Week.interface.ts similarity index 100% rename from app/league/[leagueId]/entries/[entryId]/week/Week.interface.ts rename to app/league/[leagueId]/entry/[entryId]/week/Week.interface.ts diff --git a/app/league/[leagueId]/entries/[entryId]/week/Week.tsx b/app/league/[leagueId]/entry/[entryId]/week/Week.tsx similarity index 95% rename from app/league/[leagueId]/entries/[entryId]/week/Week.tsx rename to app/league/[leagueId]/entry/[entryId]/week/Week.tsx index df12c745..c90f8976 100644 --- a/app/league/[leagueId]/entries/[entryId]/week/Week.tsx +++ b/app/league/[leagueId]/entry/[entryId]/week/Week.tsx @@ -17,8 +17,8 @@ import { createWeeklyPicks } from '@/api/apiFunctions'; import { parseUserPick } from '@/utils/utils'; import { zodResolver } from '@hookform/resolvers/zod'; import { useDataStore } from '@/store/dataStore'; -import WeekTeams from './WeekTeams'; import { ISchedule } from './WeekTeams.interface'; +import WeekTeams from './WeekTeams'; /** * Renders the weekly picks page. @@ -26,7 +26,7 @@ import { ISchedule } from './WeekTeams.interface'; * @returns {JSX.Element} The rendered weekly picks page. */ // eslint-disable-next-line no-unused-vars -const Week = ({ league, NFLTeams, week }: IWeekProps): JSX.Element => { +const Week = ({ entry, league, NFLTeams, week }: IWeekProps): JSX.Element => { const [schedule, setSchedule] = useState([]); const [userPick, setUserPick] = useState(''); @@ -80,7 +80,7 @@ const Week = ({ league, NFLTeams, week }: IWeekProps): JSX.Element => { (team) => team.teamName.toLowerCase() === teamSelect, )?.teamId; - const currentUserPick = parseUserPick(user.id, teamID || ''); + const currentUserPick = parseUserPick(user.id, entry, teamID || ''); // combines current picks and the user pick into one object. // if the user pick exists then it overrides the pick of the user. @@ -103,7 +103,7 @@ const Week = ({ league, NFLTeams, week }: IWeekProps): JSX.Element => { userResults: updatedWeeklyPicks, }); - setUserPick(currentUserPick[user.id].team); + setUserPick(currentUserPick[user.id][entry].teamName); } catch (error) { console.error('Submission error:', error); } diff --git a/app/league/[leagueId]/entries/[entryId]/week/WeekTeams.interface.ts b/app/league/[leagueId]/entry/[entryId]/week/WeekTeams.interface.ts similarity index 100% rename from app/league/[leagueId]/entries/[entryId]/week/WeekTeams.interface.ts rename to app/league/[leagueId]/entry/[entryId]/week/WeekTeams.interface.ts diff --git a/app/league/[leagueId]/entries/[entryId]/week/WeekTeams.tsx b/app/league/[leagueId]/entry/[entryId]/week/WeekTeams.tsx similarity index 100% rename from app/league/[leagueId]/entries/[entryId]/week/WeekTeams.tsx rename to app/league/[leagueId]/entry/[entryId]/week/WeekTeams.tsx diff --git a/app/league/[leagueId]/entries/[entryId]/week/[weekId]/page.tsx b/app/league/[leagueId]/entry/[entryId]/week/[weekId]/page.tsx similarity index 100% rename from app/league/[leagueId]/entries/[entryId]/week/[weekId]/page.tsx rename to app/league/[leagueId]/entry/[entryId]/week/[weekId]/page.tsx diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx new file mode 100644 index 00000000..8863d1e6 --- /dev/null +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -0,0 +1,59 @@ +// Copyright (c) Gridiron Survivor. +// Licensed under the MIT License. + +'use client'; +import { getCurrentUserEntries } from '@/api/apiFunctions'; +import { useDataStore } from '@/store/dataStore'; +import React, { JSX, useEffect, useState } from 'react'; +import { IEntry } from '../Entries.interface'; +import { LeagueEntries } from '@/components/LeagueEntries/LeagueEntries'; +import { ENTRY_URL, LEAGUE_URL, WEEK_URL } from '@/const/global'; + +/** + * Display all entries for a league. + * @param {string} leagueId - The league id. + * @returns {JSX.Element} The rendered entries component. + */ +const Entry = ({ + params: { leagueId }, +}: { + params: { leagueId: string }; +}): JSX.Element => { + const [entries, setEntries] = useState([]); + const { user } = useDataStore((state) => state); + + /** + * Fetches all entries for the current user. + * @returns {Promise} + */ + const getAllEntries = async (): Promise => { + const getEntries = await getCurrentUserEntries(user.id, leagueId); + setEntries(getEntries); + }; + + useEffect(() => { + if (!user.id || user.id === '') { + return; + } + + getAllEntries(); + }, [user]); + + return ( + <> + {entries.map((entry) => { + const linkUrl = `/${LEAGUE_URL}/${leagueId}/${ENTRY_URL}/${entry.id}/${WEEK_URL}/1`; + + return ( + + ); + })} + + ); +}; + +export default Entry; diff --git a/app/league/all/page.tsx b/app/league/all/page.tsx index 03f9e1b1..2f78e386 100644 --- a/app/league/all/page.tsx +++ b/app/league/all/page.tsx @@ -9,6 +9,7 @@ import { IGameWeek, ILeague } from '@/api/apiFunctions.interface'; import { getUserLeagues } from '@/utils/utils'; import { useDataStore } from '@/store/dataStore'; import { getGameWeek } from '@/api/apiFunctions'; +import { ENTRY_URL, LEAGUE_URL } from '@/const/global'; /** * Renders the leagues component. @@ -60,7 +61,7 @@ const Leagues = (): JSX.Element => { leagues.map((league) => ( (
@@ -38,7 +41,7 @@ const LeagueEntries = ({ isEliminated ? 'opacity-50' : '', )} > - Entry {entryNumber} + {entryName} @@ -60,12 +63,14 @@ const LeagueEntries = ({ data-testid="league-entry-pick-button-container" > {!isEliminated && ( -
diff --git a/const/global.ts b/const/global.ts new file mode 100644 index 00000000..4140e75f --- /dev/null +++ b/const/global.ts @@ -0,0 +1,6 @@ +// Copyright (c) Gridiron Survivor. +// Licensed under the MIT License. + +export const LEAGUE_URL = 'league'; +export const ENTRY_URL = 'entry'; +export const WEEK_URL = 'week'; \ No newline at end of file diff --git a/utils/utils.interface.ts b/utils/utils.interface.ts index 6ab16846..c2e9a14d 100644 --- a/utils/utils.interface.ts +++ b/utils/utils.interface.ts @@ -8,6 +8,7 @@ import { IUser, IWeeklyPicks, } from '@/api/apiFunctions.interface'; +import { IEntry } from '@/app/league/[leagueId]/entry/Entries.interface'; export interface IGetGameData { userId: IUser['id']; @@ -16,12 +17,13 @@ export interface IGetGameData { export interface IGetGameWeekResults { league: ILeague | null; - weeklyPicksData: IWeeklyPicks | ''; + weeklyPicksData: IWeeklyPicks; } export interface IGetUserPick { weeklyPicks: IWeeklyPicks['userResults']; userId: IUser['id']; + entryId: IEntry['id']; NFLTeams: INFLTeam[]; } diff --git a/utils/utils.ts b/utils/utils.ts index b2446c09..e6682696 100644 --- a/utils/utils.ts +++ b/utils/utils.ts @@ -6,7 +6,7 @@ import { IUser, IUserPick, } from '@/api/apiFunctions.interface'; -import { getAllWeeklyPicks, getCurrentLeague } from '@/api/apiFunctions'; +import { getAllWeeklyPicks, getCurrentLeague, getCurrentUserEntries } from '@/api/apiFunctions'; import { clsx, type ClassValue } from 'clsx'; import { twMerge } from 'tailwind-merge'; import { @@ -14,6 +14,7 @@ import { IGetUserPick, } from './utils.interface'; import { ILeague } from '@/api/apiFunctions.interface'; +import { IEntry } from '@/app/league/[leagueId]/entry/Entries.interface'; /** * Combine class names @@ -28,21 +29,21 @@ export function cn(...inputs: ClassValue[]): string { * Get the game data * @param props - The game data * @param props.leagueId - The league id - * @param props.currentGameWeekId - The current game week id + * @param props.gameWeekId - The current game week id * @returns The game data */ export const getGameData = async ({ leagueId, - currentGameWeekId, + gameWeekId, }: { leagueId: ILeague['leagueId']; - currentGameWeekId: IGameWeek['id']; + gameWeekId: IGameWeek['id']; }): Promise => { const league = await getCurrentLeague(leagueId); const weeklyPicksData = await getAllWeeklyPicks({ leagueId: leagueId, - weekId: currentGameWeekId, + weekId: gameWeekId, }); return { @@ -55,8 +56,8 @@ export const getGameData = async ({ }, weeklyPicksData: { leagueId: leagueId, - gameWeekId: currentGameWeekId, - userResults: weeklyPicksData, + gameWeekId: gameWeekId, + userResults: weeklyPicksData || {}, }, }; }; @@ -66,20 +67,22 @@ export const getGameData = async ({ * @param props - The user pick * @param props.weeklyPicks - The weekly picks * @param props.userId - The user id + * @param props.entryId - The entry id * @param props.NFLTeams - The NFL teams * @returns The user pick */ export const getUserPick = async ({ weeklyPicks, userId, + entryId, NFLTeams, }: IGetUserPick): Promise => { if (!weeklyPicks || !weeklyPicks[userId]) { return ''; } - const userTeamId = weeklyPicks[userId].team; - const userSelectedTeam = NFLTeams.find((team) => team.teamId === userTeamId); + const userTeamId = weeklyPicks[userId][entryId].teamName; + const userSelectedTeam = NFLTeams.find((team) => team.teamName === userTeamId); return userSelectedTeam?.teamName || ''; }; @@ -87,19 +90,28 @@ export const getUserPick = async ({ /** * Parse the user pick * @param userId - The user id - * @param teamId - The team id + * @param entryId - The entry id + * @param teamName - The team id * @returns {string} The parsed user pick */ export const parseUserPick = ( userId: IUser['id'], - teamId: string, + entryId: IEntry['id'], + teamName: string, ): IUserPick => { - if (!userId || !teamId || teamId === '') { + if (!userId || !teamName || !entryId) { throw new Error('User ID and Team ID Required'); } const parsedData = JSON.parse( - `{"${userId}":{"team":"${teamId}","correct":true}}`, + `{"${userId}": + {"${entryId}": + { + "teamName": "${teamName}", + "correct": null + } + } + }`, ); return parsedData; @@ -108,7 +120,7 @@ export const parseUserPick = ( /** * Get the list of leagues the user is a part of * @param leagues - The list of leagues - * @returns {ILeague | Error} + * @returns {ILeague | Error} - The list of leagues */ export const getUserLeagues = async ( leagues: IUser['leagues'], @@ -122,3 +134,13 @@ export const getUserLeagues = async ( return Promise.all(userLeagues); }; + +/** + * Get the user entries + * @param userId - The user ID + * @param leagueId - The league ID + * @returns {IEntry[] | Error} - The list of entries or an error + */ +export const getUserEntries = async (userId: IUser['id'], leagueId: ILeague['leagueId']): Promise => { + return getCurrentUserEntries(userId, leagueId); +} \ No newline at end of file From a4118e745a0d30a023a1742a1a3d80e7d3629e31 Mon Sep 17 00:00:00 2001 From: Shashi Lo <362527+shashilo@users.noreply.github.com> Date: Wed, 3 Jul 2024 00:53:39 -0500 Subject: [PATCH 02/19] Fixing failed linting --- app/league/[leagueId]/entry/all/page.tsx | 18 ++++++++++++++++-- app/league/all/page.tsx | 16 +--------------- utils/useProcessGame.tsx | 5 ++++- utils/utils.interface.ts | 1 + 4 files changed, 22 insertions(+), 18 deletions(-) diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index 8863d1e6..2a37e378 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -2,12 +2,13 @@ // Licensed under the MIT License. 'use client'; -import { getCurrentUserEntries } from '@/api/apiFunctions'; +import { getCurrentUserEntries, getGameWeek } from '@/api/apiFunctions'; import { useDataStore } from '@/store/dataStore'; import React, { JSX, useEffect, useState } from 'react'; import { IEntry } from '../Entries.interface'; import { LeagueEntries } from '@/components/LeagueEntries/LeagueEntries'; import { ENTRY_URL, LEAGUE_URL, WEEK_URL } from '@/const/global'; +import { IGameWeek } from '@/api/apiFunctions.interface'; /** * Display all entries for a league. @@ -20,6 +21,7 @@ const Entry = ({ params: { leagueId: string }; }): JSX.Element => { const [entries, setEntries] = useState([]); + const [currentWeek, setCurrentWeek] = useState(1); const { user } = useDataStore((state) => state); /** @@ -31,18 +33,30 @@ const Entry = ({ setEntries(getEntries); }; + /** + * Fetches the current game week. + * @returns {Promise} + */ + const getCurrentGameWeek = async (): Promise => { + try { + const currentWeek = await getGameWeek(); + setCurrentWeek(currentWeek.week); + } catch (error) {} + }; + useEffect(() => { if (!user.id || user.id === '') { return; } + getCurrentGameWeek(); getAllEntries(); }, [user]); return ( <> {entries.map((entry) => { - const linkUrl = `/${LEAGUE_URL}/${leagueId}/${ENTRY_URL}/${entry.id}/${WEEK_URL}/1`; + const linkUrl = `/${LEAGUE_URL}/${leagueId}/${ENTRY_URL}/${entry.id}/${WEEK_URL}/${currentWeek}`; return ( { const [leagues, setLeagues] = useState([]); - const [currentWeek, setCurrentWeek] = useState(1); const { user } = useDataStore((state) => state); /** @@ -31,24 +29,12 @@ const Leagues = (): JSX.Element => { } catch (error) {} }; - /** - * Fetches the current game week. - * @returns {Promise} - */ - const getCurrentGameWeek = async (): Promise => { - try { - const currentWeek = await getGameWeek(); - setCurrentWeek(currentWeek.week); - } catch (error) {} - }; - useEffect(() => { if (!user.id || user.id === '') { return; } getLeagues(); - getCurrentGameWeek(); }, [user]); return ( diff --git a/utils/useProcessGame.tsx b/utils/useProcessGame.tsx index c15d36c1..c1ee1429 100644 --- a/utils/useProcessGame.tsx +++ b/utils/useProcessGame.tsx @@ -10,6 +10,7 @@ import { UseProcessGameProps } from './utils.interface'; * Returns a function that processes the game data. * @param props - The component props. * @param props.leagueId - The league ID. + * @param props.entryId - The entry ID. * @param props.gameWeek - The game week. * @param props.user - The user. * @param props.NFLTeams - The NFL teams. @@ -18,6 +19,7 @@ import { UseProcessGameProps } from './utils.interface'; */ const useProcessGame = ({ leagueId, + entryId, gameWeek, user, NFLTeams, @@ -28,7 +30,7 @@ const useProcessGame = ({ useCallback(async () => { const { league, weeklyPicksData } = await getGameData({ leagueId: leagueId, - currentGameWeekId: gameWeek.id, + gameWeekId: gameWeek.id, }); if (league && weeklyPicksData) { @@ -49,6 +51,7 @@ const useProcessGame = ({ const userPickData = await getUserPick({ weeklyPicks: weeklyPicksData.userResults, userId: user.id, + entryId: entryId, NFLTeams: NFLTeams, }); diff --git a/utils/utils.interface.ts b/utils/utils.interface.ts index c2e9a14d..afe98244 100644 --- a/utils/utils.interface.ts +++ b/utils/utils.interface.ts @@ -34,6 +34,7 @@ export interface IParseUserPick { export interface UseProcessGameProps { leagueId: string; + entryId: string; gameWeek: { id: string }; user: { id: string }; NFLTeams: INFLTeam[]; From cfd953b7dc22b8bb9121c17995d62015cda1cd7a Mon Sep 17 00:00:00 2001 From: Shashi Lo <362527+shashilo@users.noreply.github.com> Date: Wed, 3 Jul 2024 13:35:58 -0500 Subject: [PATCH 03/19] Update failing unit test --- components/LeagueEntries/LeagueEntries.test.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/LeagueEntries/LeagueEntries.test.tsx b/components/LeagueEntries/LeagueEntries.test.tsx index e337af93..8c74e3e9 100644 --- a/components/LeagueEntries/LeagueEntries.test.tsx +++ b/components/LeagueEntries/LeagueEntries.test.tsx @@ -4,7 +4,7 @@ import React from 'react'; describe('LeagueEntries', () => { it(`renders 'default' state without a pick made`, () => { - render(); + render(); const leagueEntryContainerCard = screen.getByTestId( 'league-entry-container-card', @@ -20,7 +20,7 @@ describe('LeagueEntries', () => { }); it('renders as if the user made a pick', () => { - render(); + render(); const leagueEntryContainerCard = screen.getByTestId( 'league-entry-container-card', @@ -36,7 +36,7 @@ describe('LeagueEntries', () => { }); it('renders as if the user is eliminated', () => { - render(); + render(); const leagueEntryContainerCard = screen.getByTestId( 'league-entry-container-card', From ec05b49bf5ea3ba77383044de9e6b163e43d1f97 Mon Sep 17 00:00:00 2001 From: Shashi Lo <362527+shashilo@users.noreply.github.com> Date: Sat, 6 Jul 2024 15:16:38 -0500 Subject: [PATCH 04/19] Update unit tests from merge conflict --- api/apiFunctions.interface.ts | 2 +- api/apiFunctions.ts | 2 +- .../[leagueId]/entry/Entries.interface.ts | 2 +- app/league/[leagueId]/entry/all/page.tsx | 4 +- .../AlertDescription/AlertDescription.tsx | 15 ++++--- components/AlertTItle/AlertTitle.tsx | 14 +++--- utils/utils.interface.ts | 2 +- utils/utils.test.ts | 44 +++++++++++++------ utils/utils.ts | 4 +- 9 files changed, 56 insertions(+), 33 deletions(-) diff --git a/api/apiFunctions.interface.ts b/api/apiFunctions.interface.ts index 12363e98..9de608db 100644 --- a/api/apiFunctions.interface.ts +++ b/api/apiFunctions.interface.ts @@ -14,7 +14,7 @@ export interface IUser { } export interface IUserPick { [userId: string]: { - [entryId: IEntry['id']]: { + [entryId: IEntry['$id']]: { teamName: string; correct: boolean; }; diff --git a/api/apiFunctions.ts b/api/apiFunctions.ts index 75ece26a..8549f5a0 100644 --- a/api/apiFunctions.ts +++ b/api/apiFunctions.ts @@ -107,7 +107,7 @@ export async function getCurrentUserEntries( ); const entries = response.documents.map((entry) => ({ - id: entry.$id, + $id: entry.$id, name: entry.name, user: entry.user, league: entry.league, diff --git a/app/league/[leagueId]/entry/Entries.interface.ts b/app/league/[leagueId]/entry/Entries.interface.ts index c25beb21..e9dce56f 100644 --- a/app/league/[leagueId]/entry/Entries.interface.ts +++ b/app/league/[leagueId]/entry/Entries.interface.ts @@ -4,7 +4,7 @@ import { ILeague, INFLTeam, IUser } from '@/api/apiFunctions.interface'; export interface IEntry { - id: string; + $id: string; name: string; user: IUser; league: ILeague; diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index 2a37e378..901ddcb5 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -56,11 +56,11 @@ const Entry = ({ return ( <> {entries.map((entry) => { - const linkUrl = `/${LEAGUE_URL}/${leagueId}/${ENTRY_URL}/${entry.id}/${WEEK_URL}/${currentWeek}`; + const linkUrl = `/${LEAGUE_URL}/${leagueId}/${ENTRY_URL}/${entry.$id}/${WEEK_URL}/${currentWeek}`; return ( diff --git a/components/AlertDescription/AlertDescription.tsx b/components/AlertDescription/AlertDescription.tsx index 07818703..b971c1a8 100644 --- a/components/AlertDescription/AlertDescription.tsx +++ b/components/AlertDescription/AlertDescription.tsx @@ -1,15 +1,18 @@ // Copyright (c) Gridiron Survivor. // Licensed under the MIT License. -import * as React from 'react'; +import React, { JSX } from 'react'; import { IAlertDescription } from './AlertDescription.interface'; -const AlertDescription = React.forwardRef< - HTMLParagraphElement, - IAlertDescription ->(({ message }: IAlertDescription) => ( +/** + * AlertDescription component. + * @param props - The props. + * @param props.message - The message of the alert. + * @returns The rendered AlertDescription component. + */ +const AlertDescription = ({ message }: IAlertDescription): JSX.Element => (
{message}
-)); +); AlertDescription.displayName = 'AlertDescription'; export { AlertDescription }; diff --git a/components/AlertTItle/AlertTitle.tsx b/components/AlertTItle/AlertTitle.tsx index 13111618..e897f2b9 100644 --- a/components/AlertTItle/AlertTitle.tsx +++ b/components/AlertTItle/AlertTitle.tsx @@ -1,13 +1,17 @@ // Copyright (c) Gridiron Survivor. // Licensed under the MIT License. -import * as React from 'react'; +import React, { JSX } from 'react'; import { IAlertTitle } from './AlertTitle.interface'; -const AlertTitle = React.forwardRef( - ({ title }) => ( -
{title}
- ), +/** + * AlertTitle component. + * @param props - The props. + * @param props.title - The title of the alert. + * @returns The rendered AlertTitle component. + */ +const AlertTitle = ({ title }: IAlertTitle): JSX.Element => ( +
{title}
); AlertTitle.displayName = 'AlertTitle'; diff --git a/utils/utils.interface.ts b/utils/utils.interface.ts index afe98244..b8e3f763 100644 --- a/utils/utils.interface.ts +++ b/utils/utils.interface.ts @@ -23,7 +23,7 @@ export interface IGetGameWeekResults { export interface IGetUserPick { weeklyPicks: IWeeklyPicks['userResults']; userId: IUser['id']; - entryId: IEntry['id']; + entryId: IEntry['$id']; NFLTeams: INFLTeam[]; } diff --git a/utils/utils.test.ts b/utils/utils.test.ts index 42c6054a..3638d4d0 100644 --- a/utils/utils.test.ts +++ b/utils/utils.test.ts @@ -1,3 +1,4 @@ +import { IUserPicksData } from '@/api/apiFunctions.interface'; import { cn, getGameData, @@ -15,6 +16,7 @@ jest.mock('../api/apiFunctions', () => ({ // test data const mockUserData = { + entryId: '1234', userId: '66281d5ec5614f76bc91', userEmail: 'test@email.com', leagues: ['123'], @@ -48,16 +50,22 @@ const mockGameCurrentWeek = { const mockWeeklyPicksData = { '66281d5ec5614f76bc91': { - team: '66218f22b40deef340f8', - correct: false, + '1234': { + teamName: 'New England Patriots', + correct: false, + }, }, '6628077faeeedd272637': { - team: '6621b30ea57bd075e9d3', - correct: false, + '4321': { + teamName: 'New England Patriots', + correct: false, + } }, '66174f2362ec891167be': { - team: '6621b30ea57bd075e9d3', - correct: true, + '4232': { + teamName: 'Kansas City Chiefs', + correct: true, + } }, }; @@ -86,7 +94,7 @@ describe('utils', () => { const result = await getGameData({ leagueId: mockLeague.leagueId, - currentGameWeekId: mockGameCurrentWeek.id, + gameWeekId: mockGameCurrentWeek.id, }); expect(getCurrentLeague).toHaveBeenCalledWith(mockLeague.leagueId); @@ -108,6 +116,7 @@ describe('utils', () => { it("should return the user's team name if the user has a pick", async () => { const result = await getUserPick({ weeklyPicks: mockWeeklyPicksData, + entryId: mockUserData.entryId, userId: mockUserData.userId, NFLTeams: mockNFLTeams, }); @@ -118,6 +127,7 @@ describe('utils', () => { it('should return an empty string if the user has no pick for the given week', async () => { const result = await getUserPick({ weeklyPicks: {}, + entryId: mockUserData.entryId, userId: mockUserData.userId, NFLTeams: mockNFLTeams, }); @@ -125,14 +135,17 @@ describe('utils', () => { expect(result).toBe(''); }); - it("should return an empty string if the user's team id does not match any team", async () => { + it("should return an empty string if the user's team name does not match any team", async () => { const result = await getUserPick({ weeklyPicks: { '66281d5ec5614f76bc91': { - team: '321', - correct: false, + '1234': { + teamName: 'New Team', + correct: false, + }, }, }, + entryId: mockUserData.entryId, userId: mockUserData.userId, NFLTeams: mockNFLTeams, }); @@ -141,7 +154,8 @@ describe('utils', () => { it('should return an empty string if there are no weekly picks', async () => { const result = await getUserPick({ - weeklyPicks: null, + weeklyPicks: {} as IUserPicksData, + entryId: mockUserData.entryId, userId: mockUserData.userId, NFLTeams: mockNFLTeams, }); @@ -150,11 +164,13 @@ describe('utils', () => { }); describe('parseUserPick', () => { it('should return the parsed user pick', () => { - const result = parseUserPick(mockUserData.userId, mockNFLTeams[0].teamId); + const result = parseUserPick(mockUserData.userId, mockUserData.entryId, mockNFLTeams[0].teamName); expect(result).toStrictEqual({ [mockUserData.userId]: { - team: '66218f22b40deef340f8', - correct: true, + [mockUserData.entryId]: { + teamName: 'New England Patriots', + correct: null, + }, }, }); }); diff --git a/utils/utils.ts b/utils/utils.ts index e6682696..dbbd0c1f 100644 --- a/utils/utils.ts +++ b/utils/utils.ts @@ -96,7 +96,7 @@ export const getUserPick = async ({ */ export const parseUserPick = ( userId: IUser['id'], - entryId: IEntry['id'], + entryId: IEntry['$id'], teamName: string, ): IUserPick => { if (!userId || !teamName || !entryId) { @@ -142,5 +142,5 @@ export const getUserLeagues = async ( * @returns {IEntry[] | Error} - The list of entries or an error */ export const getUserEntries = async (userId: IUser['id'], leagueId: ILeague['leagueId']): Promise => { - return getCurrentUserEntries(userId, leagueId); + return await getCurrentUserEntries(userId, leagueId); } \ No newline at end of file From 21ceca09a92ba09dd6313702f18023a349016619 Mon Sep 17 00:00:00 2001 From: Shashi Lo <362527+shashilo@users.noreply.github.com> Date: Sat, 6 Jul 2024 15:21:50 -0500 Subject: [PATCH 05/19] feat #374: adding ability to create new entry for a user --- api/apiFunctions.ts | 38 ++++++++++- .../[leagueId]/entry/Entries.interface.ts | 7 +++ app/league/[leagueId]/entry/all/page.tsx | 63 ++++++++++++++++--- components/Button/Button.tsx | 5 +- 4 files changed, 101 insertions(+), 12 deletions(-) diff --git a/api/apiFunctions.ts b/api/apiFunctions.ts index 8549f5a0..938ce9ec 100644 --- a/api/apiFunctions.ts +++ b/api/apiFunctions.ts @@ -13,7 +13,10 @@ import { } from './apiFunctions.interface'; import { Collection, Document } from './apiFunctions.enum'; import { Query } from 'appwrite'; -import { IEntry } from '@/app/league/[leagueId]/entry/Entries.interface'; +import { + IEntry, + IEntryProps, +} from '@/app/league/[leagueId]/entry/Entries.interface'; /** * Register a new account @@ -263,3 +266,36 @@ export async function createWeeklyPicks({ throw new Error('Error creating weekly picks'); } } + +/** + * Create a new entry + * @param props - The entry data + * @param props.name - The name of the entry + * @param props.user - The user ID + * @param props.league - The league ID + * @param props.selectedTeams - The selected teams + * @returns {Models.Document | Error} - The entry object or an error + */ +export async function createEntry({ + name, + user, + league, + selectedTeams = [], +}: IEntryProps): Promise { + try { + return await databases.createDocument( + appwriteConfig.databaseId, + Collection.ENTRIES, + ID.unique(), + { + name, + user, + league, + selectedTeams, + }, + ); + } catch (error) { + console.error(error); + throw new Error('Error creating entry'); + } +} diff --git a/app/league/[leagueId]/entry/Entries.interface.ts b/app/league/[leagueId]/entry/Entries.interface.ts index e9dce56f..c9373796 100644 --- a/app/league/[leagueId]/entry/Entries.interface.ts +++ b/app/league/[leagueId]/entry/Entries.interface.ts @@ -10,3 +10,10 @@ export interface IEntry { league: ILeague; selectedTeams: INFLTeam[]; } + +export interface IEntryProps { + name: string; + user: IUser['id']; + league: ILeague['leagueId']; + selectedTeams?: INFLTeam[]; +} diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index 901ddcb5..79e56a76 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -2,13 +2,19 @@ // Licensed under the MIT License. 'use client'; -import { getCurrentUserEntries, getGameWeek } from '@/api/apiFunctions'; +import { + createEntry, + getCurrentUserEntries, + getGameWeek, +} from '@/api/apiFunctions'; import { useDataStore } from '@/store/dataStore'; import React, { JSX, useEffect, useState } from 'react'; -import { IEntry } from '../Entries.interface'; +import { IEntry, IEntryProps } from '../Entries.interface'; import { LeagueEntries } from '@/components/LeagueEntries/LeagueEntries'; import { ENTRY_URL, LEAGUE_URL, WEEK_URL } from '@/const/global'; import { IGameWeek } from '@/api/apiFunctions.interface'; +import { Button } from '@/components/Button/Button'; +import { PlusCircle } from 'lucide-react'; /** * Display all entries for a league. @@ -41,7 +47,30 @@ const Entry = ({ try { const currentWeek = await getGameWeek(); setCurrentWeek(currentWeek.week); - } catch (error) {} + } catch (error) { + console.error(error); + } + }; + + /** + * Adds a new entry to the league. + * @param {IEntryProps} props - The entry properties. + * @param {string} props.name - The name of the entry. + * @param {string} props.user - The user id. + * @param {string} props.league - The league id. + * @returns {void} + */ + const addNewEntry = async ({ + name, + user, + league, + }: IEntryProps): Promise => { + try { + const createdEntry = await createEntry({ name, user, league }); + setEntries([...entries, createdEntry]); + } catch (error) { + console.error(error); + } }; useEffect(() => { @@ -59,13 +88,31 @@ const Entry = ({ const linkUrl = `/${LEAGUE_URL}/${leagueId}/${ENTRY_URL}/${entry.$id}/${WEEK_URL}/${currentWeek}`; return ( - +
+ +
); })} + +
+ +
); }; diff --git a/components/Button/Button.tsx b/components/Button/Button.tsx index 6eac8098..ea150ecd 100644 --- a/components/Button/Button.tsx +++ b/components/Button/Button.tsx @@ -40,13 +40,11 @@ const buttonVariants = cva( export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { - asChild?: boolean; label?: string; - icon?: React.ComponentType; } const Button = React.forwardRef( - ({ className, variant, size, label, ...props }, ref): JSX.Element => { + ({ className, children, variant, size, label, ...props }, ref): JSX.Element => { return ( ); }, From 1ddaffedeed0f87950b721ca7e8c93a1f04e4e52 Mon Sep 17 00:00:00 2001 From: Shashi Lo <362527+shashilo@users.noreply.github.com> Date: Sat, 6 Jul 2024 15:39:55 -0500 Subject: [PATCH 06/19] Fixing linting errors --- app/league/[leagueId]/entry/all/page.tsx | 2 +- components/Button/Button.tsx | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index 79e56a76..473740aa 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -100,6 +100,7 @@ const Entry = ({
diff --git a/components/Button/Button.tsx b/components/Button/Button.tsx index ea150ecd..82c530b9 100644 --- a/components/Button/Button.tsx +++ b/components/Button/Button.tsx @@ -2,9 +2,7 @@ // Licensed under the MIT License. import React, { JSX } from 'react'; -import { Slot } from '@radix-ui/react-slot'; import { cva, type VariantProps } from 'class-variance-authority'; -import { LucideProps } from 'lucide-react'; import { cn } from '../../utils/utils'; @@ -41,10 +39,11 @@ export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { label?: string; + icon?: JSX.Element; } const Button = React.forwardRef( - ({ className, children, variant, size, label, ...props }, ref): JSX.Element => { + ({ className, children, variant, size, label, icon, ...props }, ref): JSX.Element => { return ( From 511fc4fc5b1244b71c1b43b3ac68e6511078daad Mon Sep 17 00:00:00 2001 From: Mai Vang Date: Sat, 6 Jul 2024 17:18:07 -0700 Subject: [PATCH 07/19] added in isPickSet --- app/league/[leagueId]/entry/all/page.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index 2a37e378..0aba8ca3 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -63,6 +63,7 @@ const Entry = ({ key={entry.id} entryName={entry.name} linkUrl={linkUrl} + isPickSet={entry.selectedTeams.length > 0} /> ); })} From bb98c59b05245cefb89a2a1a95c79dd935f6ee61 Mon Sep 17 00:00:00 2001 From: Mai Vang Date: Sat, 6 Jul 2024 18:23:53 -0700 Subject: [PATCH 08/19] thoughts --- app/league/[leagueId]/entry/all/page.tsx | 19 ++++++++++++++++++- .../LeagueEntries/LeagueEntries.interface.ts | 3 ++- components/LeagueEntries/LeagueEntries.tsx | 3 +++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index 0aba8ca3..134617a2 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -30,6 +30,7 @@ const Entry = ({ */ const getAllEntries = async (): Promise => { const getEntries = await getCurrentUserEntries(user.id, leagueId); + console.log('Entries', getEntries); setEntries(getEntries); }; @@ -53,6 +54,20 @@ const Entry = ({ getAllEntries(); }, [user]); + /** + * + * @param entryId + */ + const handlePickSetChange = (entryId: string): void => { + setEntries((prevEntries) => + prevEntries.map((entry) => + entry.id === entryId + ? { ...entry, isPickSet: !entry.isPickSet } + : entry, + ), + ); + }; + return ( <> {entries.map((entry) => { @@ -63,7 +78,9 @@ const Entry = ({ key={entry.id} entryName={entry.name} linkUrl={linkUrl} - isPickSet={entry.selectedTeams.length > 0} + onPickSetChange={() => { + handlePickSetChange(entry.id); + }} /> ); })} diff --git a/components/LeagueEntries/LeagueEntries.interface.ts b/components/LeagueEntries/LeagueEntries.interface.ts index 95e2ec2a..dd9e474a 100644 --- a/components/LeagueEntries/LeagueEntries.interface.ts +++ b/components/LeagueEntries/LeagueEntries.interface.ts @@ -6,4 +6,5 @@ export interface ILeagueEntriesProps { linkUrl: string; isEliminated?: boolean; isPickSet?: boolean; -} \ No newline at end of file + onPickSetChange: () => void; +} diff --git a/components/LeagueEntries/LeagueEntries.tsx b/components/LeagueEntries/LeagueEntries.tsx index 9ddc7094..9a799399 100644 --- a/components/LeagueEntries/LeagueEntries.tsx +++ b/components/LeagueEntries/LeagueEntries.tsx @@ -15,6 +15,7 @@ import Link from 'next/link'; * @param props.linkUrl - the url to the user's entry page * @param props.isEliminated - If true, the user is flagged as eliminat4ed * @param props.isPickSet - if true, the team logo of the picked team shows up on the LeagueEntries card and the button changes from "make a pick" to "chagne pick" + * @param props.onPickSetChange - The function that is called when the user changes their pick * @returns {React.JSX.Element} - A div element that contains the user's entry information */ const LeagueEntries = ({ @@ -22,6 +23,7 @@ const LeagueEntries = ({ linkUrl, isEliminated = false, isPickSet = false, + onPickSetChange, }: ILeagueEntriesProps): JSX.Element => (
)} From b2655b07f2aa272fe7b9d61b23c7cc3cf7e1c3f1 Mon Sep 17 00:00:00 2001 From: Mai Vang Date: Sat, 6 Jul 2024 18:37:05 -0700 Subject: [PATCH 09/19] added in isPickSet and isEliminated --- app/league/[leagueId]/entry/all/page.tsx | 23 ++++--------------- .../LeagueEntries/LeagueEntries.interface.ts | 1 - components/LeagueEntries/LeagueEntries.tsx | 3 --- 3 files changed, 5 insertions(+), 22 deletions(-) diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index 134617a2..c7c59d18 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -30,7 +30,7 @@ const Entry = ({ */ const getAllEntries = async (): Promise => { const getEntries = await getCurrentUserEntries(user.id, leagueId); - console.log('Entries', getEntries); + //console.log('Entries', getEntries); setEntries(getEntries); }; @@ -54,33 +54,20 @@ const Entry = ({ getAllEntries(); }, [user]); - /** - * - * @param entryId - */ - const handlePickSetChange = (entryId: string): void => { - setEntries((prevEntries) => - prevEntries.map((entry) => - entry.id === entryId - ? { ...entry, isPickSet: !entry.isPickSet } - : entry, - ), - ); - }; - return ( <> {entries.map((entry) => { const linkUrl = `/${LEAGUE_URL}/${leagueId}/${ENTRY_URL}/${entry.id}/${WEEK_URL}/${currentWeek}`; + const isPickSet = entry.selectedTeams.length > 0; + const isEliminated = entry.selectedTeams.length === 0; return ( { - handlePickSetChange(entry.id); - }} + isPickSet={isPickSet} + isEliminated={isEliminated} /> ); })} diff --git a/components/LeagueEntries/LeagueEntries.interface.ts b/components/LeagueEntries/LeagueEntries.interface.ts index dd9e474a..b5a371cb 100644 --- a/components/LeagueEntries/LeagueEntries.interface.ts +++ b/components/LeagueEntries/LeagueEntries.interface.ts @@ -6,5 +6,4 @@ export interface ILeagueEntriesProps { linkUrl: string; isEliminated?: boolean; isPickSet?: boolean; - onPickSetChange: () => void; } diff --git a/components/LeagueEntries/LeagueEntries.tsx b/components/LeagueEntries/LeagueEntries.tsx index 9a799399..9ddc7094 100644 --- a/components/LeagueEntries/LeagueEntries.tsx +++ b/components/LeagueEntries/LeagueEntries.tsx @@ -15,7 +15,6 @@ import Link from 'next/link'; * @param props.linkUrl - the url to the user's entry page * @param props.isEliminated - If true, the user is flagged as eliminat4ed * @param props.isPickSet - if true, the team logo of the picked team shows up on the LeagueEntries card and the button changes from "make a pick" to "chagne pick" - * @param props.onPickSetChange - The function that is called when the user changes their pick * @returns {React.JSX.Element} - A div element that contains the user's entry information */ const LeagueEntries = ({ @@ -23,7 +22,6 @@ const LeagueEntries = ({ linkUrl, isEliminated = false, isPickSet = false, - onPickSetChange, }: ILeagueEntriesProps): JSX.Element => (
)} From 5bac0ef9d1ff6bff16ecb765a25fac76309101dd Mon Sep 17 00:00:00 2001 From: Mai Vang Date: Tue, 16 Jul 2024 05:42:51 -0700 Subject: [PATCH 10/19] added in team logo --- api/apiFunctions.interface.ts | 1 + app/league/[leagueId]/entry/all/page.tsx | 4 ++-- components/LeagueEntries/LeagueEntries.interface.ts | 3 ++- components/LeagueEntries/LeagueEntries.tsx | 6 ++++-- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/api/apiFunctions.interface.ts b/api/apiFunctions.interface.ts index 9de608db..64e4f691 100644 --- a/api/apiFunctions.interface.ts +++ b/api/apiFunctions.interface.ts @@ -31,6 +31,7 @@ export interface IWeeklyPicks { export interface INFLTeam { teamId: string; teamName: string; + teamLogo: string; } export interface IUserPicksData extends IUserPick {} export interface ILeague { diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index 3cd78d4b..a92d5c19 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -88,7 +88,7 @@ const Entry = ({ {entries.map((entry) => { const linkUrl = `/${LEAGUE_URL}/${leagueId}/${ENTRY_URL}/${entry.$id}/${WEEK_URL}/${currentWeek}`; const isPickSet = entry.selectedTeams.length > 0; - const isEliminated = entry.selectedTeams.length === 0; + const teamLogo = isPickSet ? entry.selectedTeams[0].teamLogo : ''; return (
@@ -97,7 +97,7 @@ const Entry = ({ entryName={entry.name} linkUrl={linkUrl} isPickSet={isPickSet} - isEliminated={isEliminated} + teamLogo={teamLogo} />
); diff --git a/components/LeagueEntries/LeagueEntries.interface.ts b/components/LeagueEntries/LeagueEntries.interface.ts index 95e2ec2a..f3462d48 100644 --- a/components/LeagueEntries/LeagueEntries.interface.ts +++ b/components/LeagueEntries/LeagueEntries.interface.ts @@ -6,4 +6,5 @@ export interface ILeagueEntriesProps { linkUrl: string; isEliminated?: boolean; isPickSet?: boolean; -} \ No newline at end of file + teamLogo?: string; +} diff --git a/components/LeagueEntries/LeagueEntries.tsx b/components/LeagueEntries/LeagueEntries.tsx index 9ddc7094..2ec12ff8 100644 --- a/components/LeagueEntries/LeagueEntries.tsx +++ b/components/LeagueEntries/LeagueEntries.tsx @@ -15,6 +15,7 @@ import Link from 'next/link'; * @param props.linkUrl - the url to the user's entry page * @param props.isEliminated - If true, the user is flagged as eliminat4ed * @param props.isPickSet - if true, the team logo of the picked team shows up on the LeagueEntries card and the button changes from "make a pick" to "chagne pick" + * @param props.teamLogo * @returns {React.JSX.Element} - A div element that contains the user's entry information */ const LeagueEntries = ({ @@ -22,6 +23,7 @@ const LeagueEntries = ({ linkUrl, isEliminated = false, isPickSet = false, + teamLogo = '', }: ILeagueEntriesProps): JSX.Element => (
)} From 211fb5eb97bac13e956af442ff1048a8b0683c6c Mon Sep 17 00:00:00 2001 From: Mai Vang Date: Tue, 16 Jul 2024 05:46:21 -0700 Subject: [PATCH 11/19] fixing due to esLint rules --- api/apiFunctions.ts | 1 + components/LeagueEntries/LeagueEntries.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/api/apiFunctions.ts b/api/apiFunctions.ts index 938ce9ec..595c2407 100644 --- a/api/apiFunctions.ts +++ b/api/apiFunctions.ts @@ -139,6 +139,7 @@ export const getNFLTeams = async (): Promise => { const nflTeams = response.documents.map((team) => ({ teamId: team.$id, teamName: team.teamName, + teamLogo: team.teamLogo, })); return nflTeams; diff --git a/components/LeagueEntries/LeagueEntries.tsx b/components/LeagueEntries/LeagueEntries.tsx index 2ec12ff8..28aca931 100644 --- a/components/LeagueEntries/LeagueEntries.tsx +++ b/components/LeagueEntries/LeagueEntries.tsx @@ -15,7 +15,7 @@ import Link from 'next/link'; * @param props.linkUrl - the url to the user's entry page * @param props.isEliminated - If true, the user is flagged as eliminat4ed * @param props.isPickSet - if true, the team logo of the picked team shows up on the LeagueEntries card and the button changes from "make a pick" to "chagne pick" - * @param props.teamLogo + * @param props.teamLogo - the url to the team logo * @returns {React.JSX.Element} - A div element that contains the user's entry information */ const LeagueEntries = ({ From e92213dd8dba289892adf86a1389f9a66e62318b Mon Sep 17 00:00:00 2001 From: Mai Vang Date: Tue, 16 Jul 2024 05:51:09 -0700 Subject: [PATCH 12/19] added in unit test for teamlogo --- .../LeagueEntries/LeagueEntries.test.tsx | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/components/LeagueEntries/LeagueEntries.test.tsx b/components/LeagueEntries/LeagueEntries.test.tsx index 8c74e3e9..6675c2b9 100644 --- a/components/LeagueEntries/LeagueEntries.test.tsx +++ b/components/LeagueEntries/LeagueEntries.test.tsx @@ -4,14 +4,16 @@ import React from 'react'; describe('LeagueEntries', () => { it(`renders 'default' state without a pick made`, () => { - render(); + render(); const leagueEntryContainerCard = screen.getByTestId( 'league-entry-container-card', ); const leagueEntryNumber = screen.getByTestId('league-entry-number'); const entryStatus = screen.getByTestId('entry-status'); - const leagueEntryPickButton = screen.getByTestId('league-entry-pick-button'); + const leagueEntryPickButton = screen.getByTestId( + 'league-entry-pick-button', + ); expect(entryStatus).toHaveTextContent('alive'); expect(leagueEntryContainerCard).toBeInTheDocument(); @@ -20,14 +22,16 @@ describe('LeagueEntries', () => { }); it('renders as if the user made a pick', () => { - render(); + render(); const leagueEntryContainerCard = screen.getByTestId( 'league-entry-container-card', ); const leagueEntryNumber = screen.getByTestId('league-entry-number'); const entryStatus = screen.getByTestId('entry-status'); - const leagueEntryPickButton = screen.getByTestId('league-entry-pick-button'); + const leagueEntryPickButton = screen.getByTestId( + 'league-entry-pick-button', + ); expect(entryStatus).toHaveTextContent('alive'); expect(leagueEntryContainerCard).toBeInTheDocument(); @@ -36,7 +40,14 @@ describe('LeagueEntries', () => { }); it('renders as if the user is eliminated', () => { - render(); + render( + , + ); const leagueEntryContainerCard = screen.getByTestId( 'league-entry-container-card', @@ -50,4 +61,34 @@ describe('LeagueEntries', () => { ); expect(leagueEntryNumber).toHaveTextContent('Entry 3'); }); + + it('renders teamLogo when user makes a pick', () => { + const teamLogoUrl = 'https://example.com/logo.png'; + + render( + , + ); + + const leagueEntryContainerCard = screen.getByTestId( + 'league-entry-container-card', + ); + const leagueEntryNumber = screen.getByTestId('league-entry-number'); + const entryStatus = screen.getByTestId('entry-status'); + const leagueEntryPickButton = screen.getByTestId( + 'league-entry-pick-button', + ); + const leagueEntryLogo = screen.getByTestId('league-entry-logo'); + + expect(entryStatus).toHaveTextContent('alive'); + expect(leagueEntryContainerCard).toBeInTheDocument(); + expect(leagueEntryNumber).toHaveTextContent('Entry 2'); + expect(leagueEntryPickButton).toHaveTextContent('Change Pick'); + expect(leagueEntryLogo).toBeInTheDocument(); + expect(leagueEntryLogo).toHaveAttribute('src', teamLogoUrl); + }); }); From 38a0fa3e17164f92b7d8eb79b157b9bf6ac7fb18 Mon Sep 17 00:00:00 2001 From: Mai Vang Date: Tue, 16 Jul 2024 18:58:37 -0700 Subject: [PATCH 13/19] updated code from ryan review --- api/apiFunctions.interface.ts | 2 +- api/apiFunctions.ts | 2 +- app/league/[leagueId]/entry/all/page.tsx | 3 +-- components/LeagueEntries/LeagueEntries.test.tsx | 5 ++--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/api/apiFunctions.interface.ts b/api/apiFunctions.interface.ts index 64e4f691..d3bd8785 100644 --- a/api/apiFunctions.interface.ts +++ b/api/apiFunctions.interface.ts @@ -30,8 +30,8 @@ export interface IWeeklyPicks { } export interface INFLTeam { teamId: string; - teamName: string; teamLogo: string; + teamName: string; } export interface IUserPicksData extends IUserPick {} export interface ILeague { diff --git a/api/apiFunctions.ts b/api/apiFunctions.ts index 595c2407..c342e5c3 100644 --- a/api/apiFunctions.ts +++ b/api/apiFunctions.ts @@ -138,8 +138,8 @@ export const getNFLTeams = async (): Promise => { const nflTeams = response.documents.map((team) => ({ teamId: team.$id, - teamName: team.teamName, teamLogo: team.teamLogo, + teamName: team.teamName, })); return nflTeams; diff --git a/app/league/[leagueId]/entry/all/page.tsx b/app/league/[leagueId]/entry/all/page.tsx index a92d5c19..3d93597c 100644 --- a/app/league/[leagueId]/entry/all/page.tsx +++ b/app/league/[leagueId]/entry/all/page.tsx @@ -36,7 +36,6 @@ const Entry = ({ */ const getAllEntries = async (): Promise => { const getEntries = await getCurrentUserEntries(user.id, leagueId); - //console.log('Entries', getEntries); setEntries(getEntries); }; @@ -95,8 +94,8 @@ const Entry = ({ diff --git a/components/LeagueEntries/LeagueEntries.test.tsx b/components/LeagueEntries/LeagueEntries.test.tsx index 6675c2b9..3d0e98d6 100644 --- a/components/LeagueEntries/LeagueEntries.test.tsx +++ b/components/LeagueEntries/LeagueEntries.test.tsx @@ -43,9 +43,9 @@ describe('LeagueEntries', () => { render( , ); @@ -68,8 +68,8 @@ describe('LeagueEntries', () => { render( , ); @@ -85,7 +85,6 @@ describe('LeagueEntries', () => { const leagueEntryLogo = screen.getByTestId('league-entry-logo'); expect(entryStatus).toHaveTextContent('alive'); - expect(leagueEntryContainerCard).toBeInTheDocument(); expect(leagueEntryNumber).toHaveTextContent('Entry 2'); expect(leagueEntryPickButton).toHaveTextContent('Change Pick'); expect(leagueEntryLogo).toBeInTheDocument(); From 06aa56527c38bb16cefa65d551b9e00d0a3de2ea Mon Sep 17 00:00:00 2001 From: Mai Vang Date: Fri, 19 Jul 2024 08:30:05 -0700 Subject: [PATCH 14/19] added in changes --- app/(main)/league/[leagueId]/entry/all/page.tsx | 2 +- components/LeagueEntries/LeagueEntries.interface.ts | 2 +- components/LeagueEntries/LeagueEntries.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/(main)/league/[leagueId]/entry/all/page.tsx b/app/(main)/league/[leagueId]/entry/all/page.tsx index 13b70d98..3d93597c 100644 --- a/app/(main)/league/[leagueId]/entry/all/page.tsx +++ b/app/(main)/league/[leagueId]/entry/all/page.tsx @@ -94,7 +94,7 @@ const Entry = ({ diff --git a/components/LeagueEntries/LeagueEntries.interface.ts b/components/LeagueEntries/LeagueEntries.interface.ts index 62677572..f3462d48 100644 --- a/components/LeagueEntries/LeagueEntries.interface.ts +++ b/components/LeagueEntries/LeagueEntries.interface.ts @@ -6,5 +6,5 @@ export interface ILeagueEntriesProps { linkUrl: string; isEliminated?: boolean; isPickSet?: boolean; - teamLogo: string; + teamLogo?: string; } diff --git a/components/LeagueEntries/LeagueEntries.tsx b/components/LeagueEntries/LeagueEntries.tsx index ba7e9125..28aca931 100644 --- a/components/LeagueEntries/LeagueEntries.tsx +++ b/components/LeagueEntries/LeagueEntries.tsx @@ -23,7 +23,7 @@ const LeagueEntries = ({ linkUrl, isEliminated = false, isPickSet = false, - teamLogo, + teamLogo = '', }: ILeagueEntriesProps): JSX.Element => (
Date: Fri, 19 Jul 2024 08:40:11 -0700 Subject: [PATCH 15/19] added in leagueLink attribute test --- components/LeagueEntries/LeagueEntries.test.tsx | 5 ++++- components/LeagueEntries/LeagueEntries.tsx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/components/LeagueEntries/LeagueEntries.test.tsx b/components/LeagueEntries/LeagueEntries.test.tsx index 3d0e98d6..c61a271d 100644 --- a/components/LeagueEntries/LeagueEntries.test.tsx +++ b/components/LeagueEntries/LeagueEntries.test.tsx @@ -64,12 +64,13 @@ describe('LeagueEntries', () => { it('renders teamLogo when user makes a pick', () => { const teamLogoUrl = 'https://example.com/logo.png'; + const linkUrl = '/change-pick'; render( , ); @@ -82,11 +83,13 @@ describe('LeagueEntries', () => { const leagueEntryPickButton = screen.getByTestId( 'league-entry-pick-button', ); + const leagueLink = screen.getByTestId('league-entry-pick-button-link'); const leagueEntryLogo = screen.getByTestId('league-entry-logo'); expect(entryStatus).toHaveTextContent('alive'); expect(leagueEntryNumber).toHaveTextContent('Entry 2'); expect(leagueEntryPickButton).toHaveTextContent('Change Pick'); + expect(leagueLink).toHaveAttribute('href', linkUrl); expect(leagueEntryLogo).toBeInTheDocument(); expect(leagueEntryLogo).toHaveAttribute('src', teamLogoUrl); }); diff --git a/components/LeagueEntries/LeagueEntries.tsx b/components/LeagueEntries/LeagueEntries.tsx index 28aca931..ce16e27b 100644 --- a/components/LeagueEntries/LeagueEntries.tsx +++ b/components/LeagueEntries/LeagueEntries.tsx @@ -65,7 +65,7 @@ const LeagueEntries = ({ data-testid="league-entry-pick-button-container" > {!isEliminated && ( - +