Skip to content

Commit

Permalink
implemented user chart and team chart
Browse files Browse the repository at this point in the history
  • Loading branch information
lowtorola committed Nov 9, 2024
1 parent df9e2c8 commit d153add
Show file tree
Hide file tree
Showing 13 changed files with 115 additions and 37 deletions.
9 changes: 8 additions & 1 deletion frontend2/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ import PageNotFound from "views/PageNotFound";
import TeamProfile from "views/TeamProfile";
import { homeIfLoggedIn } from "api/loaders/homeIfLoggedIn";
import { episodeLoader } from "api/loaders/episodeLoader";
import { accountLoader } from "api/loaders/accountLoader";

const queryClient = new QueryClient({
queryCache: new QueryCache({
Expand Down Expand Up @@ -118,7 +119,13 @@ const router = createBrowserRouter([
children: [
{
element: <EpisodeLayout />,
children: [{ path: "/account", element: <Account /> }],
children: [
{
path: "/account",
element: <Account />,
loader: accountLoader(queryClient),
},
],
},
],
},
Expand Down
2 changes: 1 addition & 1 deletion frontend2/src/api/compete/useCompete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ export const useTournamentMatchList = (
});

/**
* For retrieving the hisotrical rating for the given team's in the given episode.
* For retrieving the historical rating of the given teams in the given episode.
*/
export const useTeamsRatingHistoryList = ({
episodeId,
Expand Down
26 changes: 26 additions & 0 deletions frontend2/src/api/loaders/accountLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import type { QueryClient } from "@tanstack/react-query";
import { CompeteMatchScrimmagingRecordRetrieveScrimmageTypeEnum } from "api/_autogen";
import { scrimmagingRecordFactory } from "api/compete/competeFactories";
import { safeEnsureQueryData } from "api/helpers";
import type { LoaderFunction } from "react-router-dom";

export const accountLoader =
(queryClient: QueryClient): LoaderFunction =>
({ params }) => {
const episodeId = params.episodeId;

if (episodeId === undefined) return null;

// User team scrimmage record
safeEnsureQueryData(
{
episodeId,
scrimmageType:
CompeteMatchScrimmagingRecordRetrieveScrimmageTypeEnum.All,
},
scrimmagingRecordFactory,
queryClient,
);

return null;
};
2 changes: 0 additions & 2 deletions frontend2/src/api/user/useUser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,6 @@ export const useTeamsByUser = ({
Error
> =>
useQuery({
// queryKey: userQueryKeys.otherTeams({ id }),
// queryFn: async () => await getTeamsByUser({ id }),
queryKey: buildKey(otherUserTeamsFactory.queryKey, { id }),
queryFn: async () => await otherUserTeamsFactory.queryFn({ id }),
});
Expand Down
2 changes: 1 addition & 1 deletion frontend2/src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ const Header: React.FC = () => {
<span className="sr-only">Open user menu</span>
<img
className="h-8 w-8 rounded-full bg-white"
src={user?.profile?.avatar_url}
src={user.data?.profile?.avatar_url}
alt="Profile Picture"
/>
</Menu.Button>
Expand Down
5 changes: 4 additions & 1 deletion frontend2/src/components/compete/chart/ChartBase.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface ChartBaseProps {
loading?: boolean;
loadingMessage?: string;
plotLines?: PlotLine[];
crownTop?: boolean;
}

const ChartBase: React.FC<ChartBaseProps> = ({
Expand All @@ -24,6 +25,7 @@ const ChartBase: React.FC<ChartBaseProps> = ({
loading = false,
loadingMessage,
plotLines,
crownTop = false,
}) => {
// Translate values into Highcharts compatible options
const [myChart, setChart] = useState<Highcharts.Chart>();
Expand Down Expand Up @@ -115,7 +117,8 @@ const ChartBase: React.FC<ChartBaseProps> = ({
);
});

if (index !== -1) names[index + 1] = "👑 " + names[index + 1];
if (index !== -1 && crownTop)
names[index + 1] = "👑 " + names[index + 1];

return names;
},
Expand Down
1 change: 1 addition & 0 deletions frontend2/src/components/compete/chart/TeamChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const TeamChart: React.FC<TeamChartProps> = ({ teamIds, loading = false }) => {
loading={loading || teamRatingHistories.isLoading}
loadingMessage="Loading rating data..."
plotLines={tournamentData}
crownTop={true}
/>
);
};
Expand Down
33 changes: 31 additions & 2 deletions frontend2/src/components/compete/chart/UserChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ import {
type PlotLine,
type ChartData,
} from "./chartUtils";
import { useTeamsByUser } from "api/user/useUser";

const UserChart: React.FC = () => {
interface UserChartProps {
userId: number;
}

const UserChart: React.FC<UserChartProps> = ({ userId }) => {
const { episodeId } = useEpisodeId();
const queryClient = useQueryClient();

Expand All @@ -23,6 +28,7 @@ const UserChart: React.FC = () => {
{ episodeId: selectedEpisode },
queryClient,
);
const teamList = useTeamsByUser({ id: userId });
const ratingHistory = useUserRatingHistoryList({
episodeId: selectedEpisode,
});
Expand All @@ -37,11 +43,24 @@ const UserChart: React.FC = () => {
return formatTournamentList(tournamentList.data.results ?? []);
}, [tournamentList]);

const teamListMap = useMemo(
() => new Map(Object.entries(teamList.data ?? {})),
[teamList],
);

const episodeListFiltered = useMemo(
() =>
(episodeList.data?.results ?? []).filter((ep) =>
teamListMap.has(ep.name_short),
),
[episodeList],
);

return (
<div className="flex flex-1 flex-col gap-8">
<SelectMenu
options={
episodeList.data?.results?.map((ep) => ({
episodeListFiltered.map((ep) => ({
value: ep.name_short,
label: ep.name_long,
})) ?? []
Expand All @@ -52,12 +71,22 @@ const UserChart: React.FC = () => {
loading={tournamentList.isLoading}
/>

{(episodeList.isLoading || teamList.isLoading) && (
<span className="text-center text-xl italic">Loading team...</span>
)}
{episodeList.isSuccess && teamList.isSuccess && (
<span className="text-center text-xl font-semibold">
{teamListMap.get(selectedEpisode)?.name ?? "ERROR"}
</span>
)}

<ChartBase
yAxisLabel="Rating"
values={ratingData}
loading={ratingHistory.isLoading}
loadingMessage="Loading rating data..."
plotLines={tournamentData}
crownTop={false}
/>
</div>
);
Expand Down
9 changes: 4 additions & 5 deletions frontend2/src/components/team/MemberList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,12 @@ const MemberList: React.FC<MemberListProps> = ({ members, className = "" }) => {
return (
<div className={`flex flex-col gap-6 ${className}`}>
{/* display current user first */}
{currentUser !== undefined &&
members.find((user) => user.id === currentUser.id) !== undefined && (
<UserRow isCurrentUser user={currentUser} />
)}
{currentUser.isSuccess &&
members.find((user) => user.id === currentUser.data.id) !==
undefined && <UserRow isCurrentUser user={currentUser.data} />}
{members.map(
(member) =>
member.id !== currentUser?.id && (
member.id !== currentUser.data?.id && (
<UserRow key={member.id} user={member} />
),
)}
Expand Down
3 changes: 2 additions & 1 deletion frontend2/src/contexts/CurrentUserContext.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createContext, useContext } from "react";
import { type UserPrivate } from "../api/_autogen";
import { type UseQueryResult } from "@tanstack/react-query";

export enum AuthStateEnum {
LOADING = "loading",
Expand All @@ -11,7 +12,7 @@ export type AuthState = `${AuthStateEnum}`;

interface CurrentUserContextType {
authState: AuthState;
user?: UserPrivate;
user: UseQueryResult<UserPrivate, Error>;
}

export const CurrentUserContext = createContext<CurrentUserContextType | null>(
Expand Down
2 changes: 1 addition & 1 deletion frontend2/src/contexts/CurrentUserProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const CurrentUserProvider: React.FC<{ children: React.ReactNode }> = ({
const userData = useCurrentUserInfo();

return (
<CurrentUserContext.Provider value={{ authState, user: userData.data }}>
<CurrentUserContext.Provider value={{ authState, user: userData }}>
{children}
</CurrentUserContext.Provider>
);
Expand Down
35 changes: 18 additions & 17 deletions frontend2/src/views/Account.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ interface FileInput {
file: FileList;
}

// TODO: create account loader!
const Account: React.FC = () => {
const { episodeId } = useEpisodeId();
const queryClient = useQueryClient();

const uploadAvatar = useUpdateUserAvatar({ episodeId }, queryClient);
const uploadResume = useResumeUpload({ episodeId }, queryClient);
const downloadResume = useDownloadResume({ episodeId });
const { user } = useCurrentUser();

const { user: userData } = useCurrentUser();

const { register: avatarRegister, handleSubmit: handleAvatarSubmit } =
useForm<FileInput>();
Expand Down Expand Up @@ -106,14 +107,14 @@ const Account: React.FC = () => {
loading={uploadResume.isPending}
disabled={uploadResume.isPending}
/>
{user?.profile?.has_resume ?? false ? (
{userData.data?.profile?.has_resume ?? false ? (
<p className="text-sm">
Resume uploaded!{" "}
<button
className="text-cyan-600 hover:underline"
onClick={() => {
if (user !== undefined)
downloadResume.mutate({ id: user.id });
if (userData.isSuccess)
downloadResume.mutate({ id: userData.data.id });
}}
>
Download
Expand All @@ -128,7 +129,7 @@ const Account: React.FC = () => {
</div>

<SectionCard title="Rating History" className="w-full flex-1">
<UserChart />
{userData.isSuccess && <UserChart userId={userData.data.id} />}
</SectionCard>
</div>
</div>
Expand All @@ -139,7 +140,7 @@ const ProfileForm: React.FC<{
episodeId: string;
queryClient: QueryClient;
}> = ({ episodeId, queryClient }) => {
const { user } = useCurrentUser();
const { user: userData } = useCurrentUser();
const updateCurrentUser = useUpdateCurrentUserInfo(
{ episodeId },
queryClient,
Expand All @@ -153,24 +154,24 @@ const ProfileForm: React.FC<{
formState: { errors },
} = useForm<PatchedUserPrivateRequest>({
defaultValues: {
email: user?.email,
first_name: user?.first_name,
last_name: user?.last_name,
email: userData.data?.email,
first_name: userData.data?.first_name,
last_name: userData.data?.last_name,
profile: {
school: user?.profile?.school,
kerberos: user?.profile?.kerberos,
biography: user?.profile?.biography,
school: userData.data?.profile?.school,
kerberos: userData.data?.profile?.kerberos,
biography: userData.data?.profile?.biography,
},
},
});

const watchFirstName = watch("first_name");
const watchLastName = watch("last_name");
const [gender, setGender] = useState<Maybe<GenderEnum>>(
user?.profile?.gender,
userData.data?.profile?.gender,
);
const [country, setCountry] = useState<Maybe<CountryEnum>>(
user?.profile?.country,
userData.data?.profile?.country,
);

const onProfileSubmit: SubmitHandler<PatchedUserPrivateRequest> = (data) => {
Expand All @@ -183,7 +184,7 @@ const ProfileForm: React.FC<{
<div className="flex flex-col items-center gap-6 p-4">
<img
className="h-24 w-24 rounded-full bg-gray-400 lg:h-48 lg:w-48"
src={user?.profile?.avatar_url}
src={userData.data?.profile?.avatar_url}
/>
<div className="text-center text-xl font-semibold">
{`${watchFirstName ?? ""} ${watchLastName ?? ""}`}
Expand All @@ -196,7 +197,7 @@ const ProfileForm: React.FC<{
className="flex flex-1 flex-col gap-4"
>
<div className="grid grid-cols-2 gap-5">
<Input disabled label="Username" value={user?.username} />
<Input disabled label="Username" value={userData.data?.username} />
<Input
required
label="Email"
Expand Down
23 changes: 18 additions & 5 deletions frontend2/src/views/MyTeam.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { type SubmitHandler, useForm } from "react-hook-form";
import { FIELD_REQUIRED_ERROR_MSG } from "utils/constants";
import FormLabel from "components/elements/FormLabel";
import ScrimmagingRecord from "components/compete/ScrimmagingRecord";
import TeamChart from "components/compete/chart/TeamChart";

interface InfoFormInput {
quote: string;
Expand Down Expand Up @@ -159,32 +160,44 @@ const MyTeam: React.FC = () => {
<SectionCard className="shrink xl:hidden" title="Members">
{membersList}
</SectionCard>
<EligibilitySettings />
<ScrimmageSettings />
<SectionCard className="shrink xl:hidden" title="File Upload">
<TeamAvatar />
</SectionCard>

<SectionCard className="shrink xl:hidden" title="Scrimmaging Record">
<ScrimmagingRecord
team={teamData.data}
hideTeamName={true}
hideAllScrimmages={true}
/>
</SectionCard>
<EligibilitySettings />
<ScrimmageSettings />
<SectionCard className="shrink xl:hidden" title="Scrimmaging Record">
<ScrimmagingRecord
team={teamData.data}
hideTeamName={true}
hideAllScrimmages={true}
/>
</SectionCard>
</div>
{/* Display the members list, file upload, and win/loss to the right when on a big screen. */}
<div className="flex hidden max-w-2xl flex-1 flex-col gap-8 xl:flex">
<SectionCard title="Members">{membersList}</SectionCard>
<SectionCard title="File Upload">
<TeamAvatar />
</SectionCard>
<SectionCard title="Scrimmaging Record">
<ScrimmagingRecord
team={teamData.data}
hideTeamName={true}
hideAllScrimmages={true}
/>
</SectionCard>
<SectionCard title="File Upload">
<TeamAvatar />
<SectionCard title="Rating History">
<TeamChart
teamIds={teamData.isSuccess ? [teamData.data.id] : []}
loading={teamData.isLoading}
/>
</SectionCard>
</div>
</div>
Expand Down

0 comments on commit d153add

Please sign in to comment.