Skip to content

Commit

Permalink
feat(stats): gamestats management
Browse files Browse the repository at this point in the history
  • Loading branch information
jonassimoen committed Oct 31, 2023
1 parent 91a9c21 commit 026516e
Show file tree
Hide file tree
Showing 5 changed files with 146 additions and 35 deletions.
50 changes: 50 additions & 0 deletions src/components/Stats/MatchStats.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useGetMatchQuery } from "@/services/matchesApi"
import { ClubBadgeBg, ClubDetails, ClubName } from "../Calendar/CalendarStyle"
import { MatchStatsStyle } from "./StatsStyle"
import config from "@/config"

declare type MatchStatsProps = {
matchId: number
homeScore: number
awayScore: number
}

export const MatchStats = (props: MatchStatsProps) => {
const { data } = useGetMatchQuery(props.matchId);
return (
// <StatsStyle>
// <Row className="stat-row">
// <Col lg={11} md={11} sm={11} xs={11}>
// {/* <Col> */}
// <ClubDetails left>
// <ClubName className="team-name" fullName={data?.home.name} short={data?.home.short} />
// <ClubBadgeBg src={`${config.API_URL}/static/badges/${data?.home.externalId}.png`} />
// </ClubDetails>
// </Col>
// <Col lg={2} md={2} sm={2} xs={2}>
// <b className="score started">{ `${props.homeScore} - ${props.awayScore}` }</b>
// </Col>
// <Col lg={11} md={11} sm={11} xs={11}>
// <ClubDetails>
// <ClubBadgeBg src={`${config.API_URL}/static/badges/${data?.away.externalId}.png`} />
// <ClubName className="team-name" fullName={data?.away.name} short={data?.away.short} />
// </ClubDetails>
// </Col>
// </Row>
// </StatsStyle>
<MatchStatsStyle>
<ClubDetails className="team" left>
<ClubName className="team-name" fullName={data?.home.name} short={data?.home.short} />
<ClubBadgeBg src={`${config.API_URL}/static/badges/${data?.home.externalId}.png`} />
</ClubDetails>
<div className="score">
<b>{`${props.homeScore} - ${props.awayScore}`}</b>
</div>
<ClubDetails className="team">
<ClubBadgeBg src={`${config.API_URL}/static/badges/${data?.away.externalId}.png`} />
<ClubName className="team-name" fullName={data?.away.name} short={data?.away.short} />
</ClubDetails>
</MatchStatsStyle>
)

}
26 changes: 26 additions & 0 deletions src/components/Stats/StatsStyle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,31 @@ export const StatsStyle = styled.div `
font-weight: bold;
margin: 0;
}
.team-name {
color: white;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
vertical-align: middle;
}
}
`;

export const MatchStatsStyle = styled.div `
display:flex;
justify-content: center;
background-color: ${theme.primaryContrast};
color: white;
font-size: 15px;
.team {
padding: 15px;
}
.score {
display:flex;
align-items: center;
}
`;
2 changes: 1 addition & 1 deletion src/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ export default {
},
{
full: "Man of the Match",
slug: "MOTM",
slug: "motm",
short: "MOTM",
type: "bool",
},
Expand Down
91 changes: 63 additions & 28 deletions src/pages/GameStatsManagement/GameStatsManagement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Form as CustomForm } from "@/components/UI/Form/Form";
import { Col, Row } from "@/components/UI/Grid/Grid";
import { InputNumber } from "@/components/UI/InputNumber/InputNumber";
import config from "@/config";
import { openSuccessNotification } from "@/lib/helpers";
import { openErrorNotification, openSuccessNotification } from "@/lib/helpers";
import { useGetMatchQuery, useGetMatchStatisticsQuery, useLazyImportMatchStatisticsQuery, useUpdateMatchStatisticsMutation } from "@/services/matchesApi";
import { useGetPlayersQuery } from "@/services/playersApi";
import { CheckOutlined, DownloadOutlined } from "@ant-design/icons";
Expand All @@ -13,6 +13,7 @@ import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { useParams } from "react-router-dom";
import { TableStyle } from "./GameStatsManagementStyle";
import { MatchStats } from "@/components/Stats/MatchStats";

const GameStatsHeaderTable = (props: { name?: string, score: number, type: string }) => {
return (
Expand Down Expand Up @@ -63,6 +64,7 @@ type GameStatsManagementState = {
allEvents: { [n: number]: Statistic },
homeScore: number,
awayScore: number,
validStats: boolean,
}

export const GameStatsManagement = (props: GameStatsMangementProps) => {
Expand All @@ -75,19 +77,18 @@ export const GameStatsManagement = (props: GameStatsMangementProps) => {
allEvents: [],
homeScore: 0,
awayScore: 0,
validStats: true,
});

const { data: match, isFetching: matchLoading, isError: matchError, isSuccess: matchSuccess } = useGetMatchQuery(+(id || 0));
const { data: players, isLoading: playersLoading, isError: playersError, isSuccess: playersSucces } = useGetPlayersQuery();
const { data: stats } = useGetMatchStatisticsQuery(+(id || 0));
const [importMatchStatistics, { data: importedStats, isLoading: matchStatisticsImportLoading, isSuccess: matchStatisticsImportSuccess }] = useLazyImportMatchStatisticsQuery();

const matchPlayers = useMemo(() =>
players?.filter((p: Player) => (p.clubId === match?.home?.id) || (p.clubId === match?.away?.id))
.sort((a: Player, b: Player) => a.clubId - b.clubId), [match, players]);

const matchPlayers = useMemo(() => players?.filter((p: Player) => (p.clubId === match?.home?.id)).concat(players?.filter((p: Player) => (p.clubId === match?.away?.id))), [match, players]);
const splitHomeAwayPlayers = useMemo(() => matchPlayers?.findIndex((p: Player) => p.clubId === match?.away?.id), [matchPlayers, match]);
const sortedEvents = useMemo(() => Object.values(state.allEvents)?.sort(
(s1: Statistic, s2: Statistic) => matchPlayers.find((v: Player) => v.externalId === s1.playerId)?.clubId - matchPlayers.find((v: Player) => v.externalId === s2.playerId)?.clubId
(s1: Statistic, s2: Statistic) => matchPlayers.find((v: Player) => v.id === s1.playerId)?.clubId - matchPlayers.find((v: Player) => v.id === s2.playerId)?.clubId
), [state.allEvents]);

const [form] = Form.useForm();
Expand All @@ -100,13 +101,14 @@ export const GameStatsManagement = (props: GameStatsMangementProps) => {
return { ...playerStat };
} else {
return {
playerId: p.externalId,
playerId: p.id,
matchId: +(id || 0),
};
}
}) || [];
form.setFieldsValue(allStats);
setState({ ...state, allEvents: allStats });
setState((state) => ({ ...state, allEvents: allStats }));
onFieldsChange(allStats);
}
}, [matchPlayers, stats]);

Expand All @@ -115,10 +117,10 @@ export const GameStatsManagement = (props: GameStatsMangementProps) => {
const allStats = matchPlayers?.map((p: Player) => {
const playerStat = importedStats?.find((s: Statistic) => s.playerId === p.externalId);
if (playerStat) {
return { ...playerStat };
return { ...playerStat, playerId: p.id };
} else {
return {
playerId: p.externalId,
playerId: p.id,
matchId: +(id || 0),
};
}
Expand Down Expand Up @@ -146,17 +148,18 @@ export const GameStatsManagement = (props: GameStatsMangementProps) => {
dataIndex: 'playerId',
width: '6rem',
render: (txt: number, rec: any, index: number) => {
const player = matchPlayers.find((v: Player) => v.externalId === txt);
const clubBadge = `${config.API_URL}/static/badges/${player.clubId === match.home.id ? match.home.externalId : match.away.externalId}.png`;
const player = matchPlayers.find((v: Player) => v.id === txt);
const clubBadge = `${config.API_URL}/static/badges/${player?.clubId === match.home.id ? match.home.externalId : match.away.externalId}.png`;
++index;
return (
<ClubDetails>
{txt}
<ClubBadgeBg src={clubBadge} />
<ClubName className="team-name" fullName={player?.surname} shortName={player?.short}></ClubName>
</ClubDetails>
)
}
}
},
].concat(config.STATISTICS.map((stat: any) => ({
title: () => <Tooltip placement="top" title={stat.full} key={`${stat.type}-${stat.slug}`}>{stat.short}</Tooltip>,
dataIndex: stat.slug,
Expand All @@ -179,30 +182,62 @@ export const GameStatsManagement = (props: GameStatsMangementProps) => {
</Form.Item>
})));

const onFieldsChange = (values: any) => {
const playersArray = Object.values(values);
const homePlayers = playersArray.slice(0, splitHomeAwayPlayers);
const awayPlayers = playersArray.slice(splitHomeAwayPlayers);
const homeScore = homePlayers.reduce((acc: number, value: any) => acc + (value.goals || 0), 0);
const awayScore = awayPlayers.reduce((acc: number, value: any) => acc + (value.goals || 0), 0);
const validStats = playersArray.reduce((acc: number, value: any) => acc + (value.motm || 0), 0) === 1;

setState((state) => ({ ...state, homeScore, awayScore, validStats }));
}

const onFormSubmit = (form: any) => {
form.validateFields()
.then((formObj: any) => Object.values(formObj).map((value: any, idx: any) => ({ ...value, playerId: matchPlayers[idx].id })))
.then((playerStats: any) => updateMatchStats({ matchId: +(id || 0), stats: playerStats, score: { home: state.homeScore, away: state.awayScore } }))
// .then((formObj: any) => Object.values(formObj))
// .then((playerStats: any,))

// updateMatchStats({ stats: Object.values(obj), matchId: +(id || 0) })
}
useEffect(() => console.log("state", state), [state]);

return (
<Spin spinning={matchLoading || playersLoading || matchStatisticsImportLoading} delay={0}>
{matchStatisticsImportSuccess ?
<Spin spinning={matchLoading || playersLoading || matchStatisticsImportLoading} delay={0} style={{ padding: "2rem 0" }}>
<MatchStats matchId={+id} homeScore={state.homeScore} awayScore={state.awayScore} />
{
matchStatisticsImportSuccess ?
<Alert
message="Data controleren"
description="Check de data met gerespecteerde databronnen (bv. Opta). Duid ook de MOTM aan volgens de officiële kanalen van FIFA/UEFA."
type="warning"
showIcon
/>
:
<Button
icon={<DownloadOutlined />}
onClick={() => importMatchStatistics(+(id || 0))}
>
{t("admin.gamestatistic.import")}
</Button>
}
{
!state.validStats &&
<Alert
message="Data controleren"
description="Check de data met gerespecteerde databronnen (bv. Opta). Duid ook de MOTM aan volgens de officiële kanalen van FIFA/UEFA."
type="warning"
message="Ongeldige statistieken"
description="Check de MOTM, er is er geen of meerdere toegekend. Er kan slechts één MOTM zijn."
type="error"
showIcon
/>
:
<Button
icon={<DownloadOutlined />}
onClick={() => importMatchStatistics(+(id || 0))}
>
{t("admin.gamestatistic.import")}
</Button>
}
<CustomForm
colon={false}
form={form}
layout="horizontal"
name={`form_match_${id}`}
onFieldsChange={() => console.log(form.getFieldsValue())}
style={{ padding: "2rem 0" }}
onFieldsChange={() => onFieldsChange(form.getFieldsValue())}
>

{/* <GameStatsHeaderTable name={match?.home?.name} score={state.homeScore} type={'home'} /> */}
Expand All @@ -228,7 +263,7 @@ export const GameStatsManagement = (props: GameStatsMangementProps) => {
</CustomForm>
<Row justify="center" align="middle">
<Col span={24}>
<Button onClick={() => form.validateFields().then((obj: any) => updateMatchStats({ stats: Object.values(obj), matchId: +(id || 0) }))}>
<Button disabled={!state.validStats} onClick={() => onFormSubmit(form)}>
Opslaan
</Button>
</Col>
Expand Down
12 changes: 6 additions & 6 deletions src/services/matchesApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";
export const matchesApi = createApi({
reducerPath: "matchesApi",
baseQuery: fetchBaseQuery({ baseUrl: `${config.API_URL}/matches`, credentials: "include" }),
tagTypes: ["Match", "MatchEvents", "MatchStatistics", "PlayerStats"],
tagTypes: ["Match", "MatchEvents", "MatchStatistics", "PlayerStats", "ImportedMatchStatistics"],
endpoints: (builder) => ({

getMatches: builder.query<Match[], void>({
Expand Down Expand Up @@ -93,11 +93,11 @@ export const matchesApi = createApi({
: ["MatchStatistics"],
}),

updateMatchStatistics: builder.mutation<Statistic[], { stats: Partial<Statistic>[], matchId: number }>({
query: ({ matchId, stats }) => ({
updateMatchStatistics: builder.mutation<Statistic[], { stats: Partial<Statistic>[], matchId: number, score: { home: number, away: number} }>({
query: ({ matchId, stats, score }) => ({
url: `${matchId}/stats`,
method: "PUT",
body: [...stats],
body: {stats, score},
}),
invalidatesTags: (res, err, arg) => [{ type: "MatchStatistics", id: arg.matchId }, { type: "Match", id: arg.matchId }, "PlayerStats"],
}),
Expand All @@ -115,8 +115,8 @@ export const matchesApi = createApi({
query: (matchId) => `${matchId}/stats/import`,
providesTags: (res, err, arg) =>
res
? [{ type: "MatchStatistics" as const, id: arg }, "MatchStatistics"]
: ["MatchStatistics"],
? [{ type: "ImportedMatchStatistics" as const, id: arg }, "ImportedMatchStatistics"]
: ["ImportedMatchStatistics"],
}),
})
});
Expand Down

0 comments on commit 026516e

Please sign in to comment.