-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* refactor: /stats page * refactor: utils.ts react-hooks/rules-of-hooks
- Loading branch information
Showing
7 changed files
with
486 additions
and
367 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
{ | ||
"i18n-ally.localesPaths": [ | ||
"messages" | ||
] | ||
], | ||
"i18n-ally.keystyle": "nested" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
"use client"; | ||
|
||
// hooks | ||
import { Suspense } from "react"; | ||
import { useTranslations } from "next-intl"; | ||
|
||
// MUI component | ||
import Grid from "@mui/material/Grid"; | ||
import Paper from "@mui/material/Paper"; | ||
import Typography from "@mui/material/Typography"; | ||
import Accordion from "@mui/material/Accordion"; | ||
import AccordionSummary from "@mui/material/AccordionSummary"; | ||
import List from "@mui/material/List"; | ||
import ListItem from "@mui/material/ListItem"; | ||
import ListItemText from "@mui/material/ListItemText"; | ||
import ListItemAvatar from "@mui/material/ListItemAvatar"; | ||
import Avatar from "@mui/material/Avatar"; | ||
|
||
// Icons | ||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; | ||
import SportsEsportsIcon from "@mui/icons-material/SportsEsports"; | ||
import HourglassFullIcon from "@mui/icons-material/HourglassFull"; | ||
import HourglassBottomIcon from "@mui/icons-material/HourglassBottom"; | ||
import HourglassTopIcon from "@mui/icons-material/HourglassTop"; | ||
import YouTubeIcon from "@mui/icons-material/YouTube"; | ||
|
||
// Utils | ||
import { useCalcDate, usePrettyDuration } from "./utils"; | ||
|
||
// Types | ||
import type { statsProperty } from "@/app/api/stats/route"; | ||
|
||
type Props = { | ||
stats: statsProperty | ||
} | ||
|
||
export default function GeneralStats ({stats}: Props) { | ||
|
||
const t = useTranslations(); | ||
const generalStats = stats.general; | ||
|
||
// hooks calls | ||
const total_duration = usePrettyDuration(generalStats.total_time); | ||
const total_duration_available = usePrettyDuration(generalStats.total_time_available); | ||
const total_duration_unavailable = usePrettyDuration(generalStats.total_time_unavailable); | ||
const how_long_since_channel_start = useCalcDate(generalStats.channel_start_date).result; | ||
|
||
return ( | ||
<Grid item xs={12} md={12} lg={12}> | ||
<Paper | ||
sx={{ | ||
p: 2, | ||
display: "flex", | ||
flexDirection: "column", | ||
}} | ||
> | ||
<Typography component="h2" variant="h6" color="primary" gutterBottom> | ||
{t("stats.generalStats.title")} | ||
</Typography> | ||
<List> | ||
<Accordion key={"total_games"}> | ||
<AccordionSummary | ||
expandIcon={<ExpandMoreIcon />} | ||
aria-controls={"panel-content_total_games"} | ||
id={"panel-header_total_games"} | ||
> | ||
<ListItemAvatar> | ||
<Avatar> | ||
<SportsEsportsIcon /> | ||
</Avatar> | ||
</ListItemAvatar> | ||
<ListItemText | ||
primary={t("stats.generalStats.total_games")} | ||
secondary={generalStats.total} | ||
/> | ||
</AccordionSummary> | ||
<Suspense fallback={null}> | ||
<List> | ||
<ListItem> | ||
<ListItemAvatar> | ||
<Avatar> | ||
<SportsEsportsIcon /> | ||
</Avatar> | ||
</ListItemAvatar> | ||
<ListItemText | ||
primary={t("stats.generalStats.total_games_available")} | ||
secondary={generalStats.total_available} | ||
/> | ||
</ListItem> | ||
<ListItem> | ||
<ListItemAvatar> | ||
<Avatar> | ||
<SportsEsportsIcon /> | ||
</Avatar> | ||
</ListItemAvatar> | ||
<ListItemText | ||
primary={t("stats.generalStats.total_games_unavailable")} | ||
secondary={generalStats.total_unavailable} | ||
/> | ||
</ListItem> | ||
</List> | ||
</Suspense> | ||
</Accordion> | ||
|
||
<Accordion key={"total_duration"}> | ||
<AccordionSummary | ||
expandIcon={<ExpandMoreIcon />} | ||
aria-controls={"panel-content_total_duration"} | ||
id={"panel-header_total_duration"} | ||
> | ||
<ListItemAvatar> | ||
<Avatar> | ||
<HourglassFullIcon /> | ||
</Avatar> | ||
</ListItemAvatar> | ||
<ListItemText | ||
primary={t("stats.generalStats.total_duration")} | ||
secondary={total_duration} | ||
/> | ||
</AccordionSummary> | ||
<Suspense fallback={null}> | ||
<List> | ||
<ListItem> | ||
<ListItemAvatar> | ||
<Avatar> | ||
<HourglassBottomIcon /> | ||
</Avatar> | ||
</ListItemAvatar> | ||
<ListItemText | ||
primary={t("stats.generalStats.total_duration_available")} | ||
secondary={total_duration_available} | ||
/> | ||
</ListItem> | ||
<ListItem> | ||
<ListItemAvatar> | ||
<Avatar> | ||
<HourglassTopIcon /> | ||
</Avatar> | ||
</ListItemAvatar> | ||
<ListItemText | ||
primary={t("stats.generalStats.total_duration_unavailable")} | ||
secondary={total_duration_unavailable} | ||
/> | ||
</ListItem> | ||
</List> | ||
</Suspense> | ||
</Accordion> | ||
|
||
<ListItem> | ||
<ListItemAvatar> | ||
<Avatar> | ||
<YouTubeIcon /> | ||
</Avatar> | ||
</ListItemAvatar> | ||
<ListItemText | ||
primary={t("stats.generalStats.channel_start_date")} | ||
secondary={`${new Date( | ||
generalStats.channel_start_date, | ||
).toLocaleDateString()} ${t( | ||
"stats.generalStats.channel_start_date_details", | ||
{ value: how_long_since_channel_start }, | ||
)}`} | ||
/> | ||
</ListItem> | ||
</List> | ||
</Paper> | ||
</Grid> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
"use client"; | ||
|
||
// hooks | ||
import { useTranslations } from "next-intl"; | ||
import { useAppSelector } from "@/redux/hooks"; | ||
|
||
// MUI component | ||
import Typography from "@mui/material/Typography"; | ||
import Grid from "@mui/material/Grid"; | ||
import Paper from "@mui/material/Paper"; | ||
|
||
// Recharts components | ||
import { | ||
BarChart, | ||
Bar, | ||
XAxis, | ||
YAxis, | ||
CartesianGrid, | ||
Tooltip, | ||
ResponsiveContainer, | ||
} from "recharts"; | ||
|
||
// Types | ||
import type { statsProperty } from "@/app/api/stats/route"; | ||
|
||
type Props = { | ||
stats: statsProperty | ||
} | ||
|
||
export default function GenresChart({stats}: Props) { | ||
|
||
const t = useTranslations(); | ||
const currentColor = useAppSelector((state) => state.themeColor.currentColor); | ||
|
||
const strokeColor = currentColor === "dark" ? "white" : "dark"; | ||
// for genre chart | ||
const genresData = stats.genres.map(genre => ({ | ||
key: genre.id, | ||
category: t(`gamesLibrary.gamesGenres.${genre.id}` as any), | ||
...genre | ||
})); | ||
|
||
if (genresData.length === 0) { | ||
return <></>; | ||
} | ||
|
||
return ( | ||
<Grid item xs={12} md={8} lg={8}> | ||
<Paper | ||
sx={{ | ||
p: 2, | ||
display: "flex", | ||
flexDirection: "column", | ||
height: 360, | ||
}} | ||
> | ||
<Typography | ||
component="h2" | ||
variant="h6" | ||
color="primary" | ||
gutterBottom | ||
> | ||
{t("stats.genresChart.title")} | ||
</Typography> | ||
<ResponsiveContainer> | ||
<BarChart data={genresData}> | ||
<CartesianGrid strokeDasharray="2 2" /> | ||
<XAxis dataKey="category" stroke={strokeColor} /> | ||
<YAxis stroke={strokeColor} /> | ||
<Tooltip contentStyle={{ backgroundColor: currentColor }} /> | ||
<Bar | ||
type="monotone" | ||
dataKey="total_available" | ||
stackId="1" | ||
stroke="#82ca9d" | ||
fill="#82ca9d" | ||
name={t("stats.genresChart.total_available")} | ||
/> | ||
<Bar | ||
type="monotone" | ||
dataKey="total_unavailable" | ||
stackId="1" | ||
stroke="#8884d8" | ||
fill="#8884d8" | ||
name={t("stats.genresChart.total_unavailable")} | ||
/> | ||
</BarChart> | ||
</ResponsiveContainer> | ||
</Paper> | ||
</Grid> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
"use client"; | ||
|
||
// hooks | ||
import { useTranslations } from "next-intl"; | ||
import { useAppSelector } from "@/redux/hooks"; | ||
|
||
// MUI component | ||
import Typography from "@mui/material/Typography"; | ||
import Grid from "@mui/material/Grid"; | ||
import Paper from "@mui/material/Paper"; | ||
|
||
// Recharts components | ||
import { | ||
PolarAngleAxis, | ||
PolarGrid, | ||
Radar, | ||
RadarChart, | ||
ResponsiveContainer, | ||
Legend, | ||
} from "recharts"; | ||
|
||
// Types | ||
import type { statsProperty } from "@/app/api/stats/route"; | ||
|
||
type Props = { | ||
stats: statsProperty | ||
} | ||
|
||
export default function PlatformsChart({stats} : Props) { | ||
|
||
const t = useTranslations(); | ||
const currentColor = useAppSelector((state) => state.themeColor.currentColor); | ||
|
||
const strokeColor = currentColor === "dark" ? "white" : "dark"; | ||
const platformsData = stats.platforms | ||
.map(platform => ({ | ||
key: platform.platform, | ||
...platform | ||
})); | ||
|
||
if (platformsData.length === 0) { | ||
return <></>; | ||
} | ||
|
||
return ( | ||
<Grid item xs={12} md={4} lg={4}> | ||
<Paper | ||
sx={{ | ||
p: 2, | ||
display: "flex", | ||
flexDirection: "column", | ||
height: 360, | ||
}} | ||
> | ||
<Typography | ||
component="h2" | ||
variant="h6" | ||
color="primary" | ||
gutterBottom | ||
> | ||
{t("stats.platformsChart.title")} | ||
</Typography> | ||
<ResponsiveContainer> | ||
<RadarChart outerRadius={90} data={platformsData}> | ||
<PolarGrid /> | ||
<PolarAngleAxis dataKey="key" stroke={strokeColor} /> | ||
<Radar | ||
name={t("stats.platformsChart.total_available")} | ||
dataKey="total_available" | ||
stroke="#1fa134" | ||
fill="#1fa134" | ||
fillOpacity={0.6} | ||
/> | ||
<Radar | ||
name={t("stats.platformsChart.total_unavailable")} | ||
dataKey="total_unavailable" | ||
stroke="#8884d8" | ||
fill="#8884d8" | ||
fillOpacity={0.6} | ||
/> | ||
<Legend /> | ||
</RadarChart> | ||
</ResponsiveContainer> | ||
</Paper> | ||
</Grid> | ||
); | ||
} |
Oops, something went wrong.