Skip to content

Commit

Permalink
Frontend fixes (#103)
Browse files Browse the repository at this point in the history
* move types and fetches to different file

* change import location for TrendLine

* import season component instead of copying the whole file

* add backend url and api proxy to next.config.js

* import backend url from process env

* add api fetchSeasonList for use in client component

* Adjust header style to remove horizontal scrollbar and enable mouse horizontal scroll

* Convert header to client component, fetch seasonList from backend

* Remove navlink list

* add mysql test server deploy script

* remove legacy frontend files
  • Loading branch information
thearyadev committed Jan 19, 2024
1 parent a7674e2 commit 169dd51
Show file tree
Hide file tree
Showing 29 changed files with 196 additions and 1,420 deletions.
3 changes: 1 addition & 2 deletions frontend/app/components/charts/lineChart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import * as Highcharts from 'highcharts';
import HighchartsReact from "highcharts-react-official";
import {useRef} from "react";
import {HeroColors} from "@/app/components/charts/heroColors";
import type {TrendLine} from "@/pages/trends";

import type {TrendLine} from "@/app/utils/serverSideProps";


interface LineChartProps extends HighchartsReact.Props {
Expand Down
8 changes: 7 additions & 1 deletion frontend/app/components/header/header.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,18 @@

.navbar {
@apply bg-gray-300 w-full overflow-x-auto flex whitespace-nowrap;
scrollbar-width: none;
-ms-overflow-style: none;

ul {
@apply flex list-none space-x-4;
}

ul > li {
@apply p-3 hover:bg-gray-400 transition duration-100 ease-in-out;
@apply hover:bg-gray-400 transition duration-100 ease-in-out;
}
}

.navbar::-webkit-scrollbar{
display: none;
}
56 changes: 46 additions & 10 deletions frontend/app/components/header/header.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,64 @@
"use client";
import {useEffect, useState} from "react";
import styles from "./header.module.scss"
import {fetchSeasonList} from "@/app/utils/clientSideFetch";
import {useRouter} from "next/router";


export type NavLinks = {
type NavLinks = {
label: string;
path: string;
path: string
}

export type HeaderProps = {
nav_links: NavLinks[];
}
const Header = () => {
const [navLinks, setNavLinks] = useState<NavLinks[]>([])
const router = useRouter();
useEffect(() => {
fetchSeasonList().then(seasonList => {
setNavLinks(seasonList.reverse().map(season => {
const seasonNumber = season.split("_")[0]
return {label: `Season ${seasonNumber}`, path: `/season/${seasonNumber}`}
}))
if (router.pathname === "/" && typeof window !== undefined) {
const element = document.getElementById("curseason")
if (element) {
element.innerText = `Season ${seasonList[0].split("_")[0]}`
}
}

if (typeof window !== undefined){
const element = document.getElementById('navbar')
if (element){
element.addEventListener('wheel', function(e) {
// @ts-ignore
if (e.deltaY != 0) {
// @ts-ignore
this.scrollLeft += (e.deltaY * 1.5);
e.preventDefault();
}
}, false);
}

}

})

}, []);


const Header = ({nav_links}: HeaderProps) => {
return (
<header>
<div className={styles.top_header_container}>
<h1>Top 500 Aggregator</h1>
</div>

<div className={styles.navbar}>
<div className={styles.navbar} id="navbar">
<ul>
{nav_links.map(link => {
return <li><a href={link.path}>{link.label}</a></li>
{!!navLinks.length && navLinks.map(link => {
return <li><a className="block p-2 text-inherit no-underline" href={link.path}>{link.label}</a></li>
})}

{!navLinks.length && <li><p className="invisible">you're not supposed to see this...</p></li>}

</ul>
</div>

Expand Down
6 changes: 6 additions & 0 deletions frontend/app/utils/clientSideFetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

export async function fetchSeasonList(): Promise<string[]> {
const response = await fetch("/api/d/seasons")
return await response.json()
}

58 changes: 58 additions & 0 deletions frontend/app/utils/serverSideProps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const backendUrl = process.env.BACKEND_URL;
export type Statistic = {
mean: number;
standard_deviation: number;
variance: number;
}

export type BarChartData = {
labels: string[];
values: number[];
}

export type SingleChart = {
graph: BarChartData;
statistic: Statistic;
}

export type SeasonData = {
[key: string]: SingleChart
}

export type StdDevs = {
DAMAGE: number;
SUPPORT: number;
TANK: number;
}

export type TrendLine = {
name: string;
data: number[]
}

export async function fetchSingleSeasonPageChartData(seasonNumber: number): Promise<SingleChart[]> {
const response = await fetch(`${backendUrl}/chart/${seasonNumber}_8`);
return await response.json();
}


export async function fetchSeasonList(): Promise<string[]> {
const response = await fetch(`${backendUrl}/d/seasons`)
return await response.json()
}


export async function fetchSingleSeasonStdDevs(seasonNumber: number): Promise<StdDevs> {
const response = await fetch(`${backendUrl}/d/single_season_std_by_role/${seasonNumber}_8`)
return await response.json()
}

export async function fetchSeasonalOccurrenceTrend(): Promise<TrendLine[]>{
const response = await fetch(`${backendUrl}/chart/trend/d`)
return await response.json()
}

export async function fetchSeasonalStdDevTrendByRole(): Promise<TrendLine[]>{
const response = await fetch(`${backendUrl}/d/all_seasons_std_by_role`)
return await response.json()
}
16 changes: 15 additions & 1 deletion frontend/next.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,18 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}
const backendUrl = process.env.NODE_ENV === "development" ? "http://localhost:7771" : "http://server:8000"

const nextConfig = {
async rewrites() {
return [
{
source: "/api/:path*",
destination: `${backendUrl}/:path*`
}
]
},
env: {
BACKEND_URL: backendUrl
}
}

module.exports = nextConfig
20 changes: 3 additions & 17 deletions frontend/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import '../app/globals.scss';
import type {AppProps} from 'next/app';
import {Header} from "@/app/components";
import {useRouter} from "next/router";
import {GetServerSidePropsContext} from "next";

interface T500AggregatorAppProps extends AppProps {
data: string[] | undefined;
Expand All @@ -14,25 +13,12 @@ function MyApp({Component, pageProps}: T500AggregatorAppProps) {
const router = useRouter()
const {seasonNumber} = router.query
const path = router.asPath
const links = [
{label: "Trends", path: "/trends"},
{label: "Season 7", path: "/season/7"},
{label: "Season 6", path: "/season/6"},
{label: "Season 5", path: "/season/5"},
{label: "Season 4", path: "/season/4"},
{label: "Season 3", path: "/season/3"},
{label: "Season 2", path: "/season/2"},
{label: "Season 1", path: "/season/1"},
{label: "Season 36", path: "/season/36"},
{label: "Season 35", path: "/season/35"},
{label: "Season 34", path: "/season/34"},
]

return (
<>
<Header nav_links={links}/>
<Header />
<main className="mt-5 lg:ml-3 lg:mr-3 sm:ml-1 sm:mr-1">
<h2 className={"text-4xl pb-4"}>{seasonNumber? `Season ${seasonNumber}`: path.toLowerCase().includes("trends") ? "Trends" : "Season 7"}</h2>

<h1 id="curseason" className={"text-4xl pb-4"}>{seasonNumber ?`Season ${seasonNumber}` : path.toLowerCase().includes("trends") ? "Trends": null}</h1>
<p className="pb-2"><strong>Welcome to Overwatch 2 Top 500 Aggregator</strong></p>
<p>The data available on this page is not 100% accurate. Data collection involves computer vision and
image classification using a neural network, and as
Expand Down
128 changes: 17 additions & 111 deletions frontend/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,126 +1,32 @@
import {Card} from "@/app/components";
import {BarChart} from "@/app/components";
import Season from "@/pages/season/[seasonNumber]";
import {
fetchSeasonList,
fetchSingleSeasonPageChartData,
fetchSingleSeasonStdDevs,
SeasonData,
StdDevs
} from "@/app/utils/serverSideProps";
import {GetServerSidePropsContext} from "next";

type Statistic = {
mean: number;
standard_deviation: number;
variance: number;
}

type BarChartData = {
labels: string[];
values: number[];
}

type SingleChart = {
graph: BarChartData;
statistic: Statistic;
}

type SeasonData = {
[key: string]: SingleChart;
}

type StdDevs = {
DAMAGE: number;
SUPPORT: number;
TANK: number;
}


const HeroStdDev = ({value, role}: {value: number, role: string}) => {
return (
<div className="text-center pt-5 pb-5">
<h5>{role}</h5>
<h6 className="text-lg text-center">{Math.round((value + Number.EPSILON) * 100) / 100}</h6>

</div>
)
}

const Index = ({data, season_list, std_devs}: { data: SeasonData, season_list: string[], std_devs: StdDevs}) => {
return (
<>

<Card title="Role Standard Deviation: All Slots, All Regions" subtitle={"Note: The standard deviation is calculated with the 10th percentile excluded. T500 aggregator by nature skews the accuracy of the low outliers in a data set. For this reason, the bottom 10% of entries for any given set (support, damage or tank) is excluded from the calculation."}>
<HeroStdDev value={std_devs.SUPPORT} role={"SUPPORT"} />
<HeroStdDev value={std_devs.DAMAGE} role={"DAMAGE"}/>
<HeroStdDev value={std_devs.TANK} role={"TANK"}/>
</Card>


<Card title="Hero Occurrences: All Slots" nowrap>
{Object.keys(data).map(key => {
if (key.includes("O_ALL")){
const [_, role, region] = key.split("_")
return <BarChart title={`${region}`} graph={data[key].graph} maxY={region === "ALL" ? 1250 : 500 } />
}
})}
</Card>



<Card title="Hero Occurrences: First Most Played">
{Object.keys(data).map(key => {
if (key.includes("OFMP")){
const [_, role, region] = key.split("_")
return <BarChart title={`${role}: ${region}`} graph={data[key].graph} maxY={region === "ALL" ? 500 : 300 } />
}
})}

</Card>

<Card title="Hero Occurrences: Second Most Played">
{Object.keys(data).map(key => {
if (key.includes("OSMP")){
const [_, role, region] = key.split("_")
return <BarChart title={`${role}: ${region}`} graph={data[key].graph} maxY={region === "ALL" ? 500 : 300 } />
}
})}

</Card>


<Card title="Hero Occurrences: Third Most Played">
{Object.keys(data).map(key => {
if (key.includes("OTMP")){
const [_, role, region] = key.split("_")
return <BarChart title={`${role}: ${region}`} graph={data[key].graph} maxY={region === "ALL" ? 500 : 300 } />
}
})}

</Card>

</>
)
const Index = ({seasonChartData, seasonStdDevs}: {seasonChartData: SeasonData, seasonStdDevs: StdDevs}) => {
return <Season seasonChartData={seasonChartData} seasonStdDevs={seasonStdDevs} />
}

export async function getServerSideProps(context: GetServerSidePropsContext) {

const seasonNumber = "7"

// Make an API call using seasonNumber
const res = await fetch(`http://server:8000/chart/${seasonNumber}_8`);
const data = await res.json();


const res2 = await fetch("http://server:8000/d/seasons")
const season_list = await res2.json()

const res3 = await fetch(`http://server:8000/d/single_season_std_by_role/${seasonNumber}_8`)
const std_devs = await res3.json()
// @ts-ignore
const seasonList = await fetchSeasonList()
const seasonNumber = Number(seasonList[seasonList.length -1].split("_")[0])
const seasonChartData = await fetchSingleSeasonPageChartData(seasonNumber)
const seasonStdDevs = await fetchSingleSeasonStdDevs(seasonNumber)

return {
props: {
data,
season_list,
std_devs
seasonChartData,
seasonStdDevs
},
};
}




export default Index
Loading

0 comments on commit 169dd51

Please sign in to comment.