104 lines
2.7 KiB
TypeScript
104 lines
2.7 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
interface Match
|
|
{
|
|
matchId: string;
|
|
championImg: string;
|
|
win: boolean;
|
|
kills: number;
|
|
deaths: number;
|
|
assists: number;
|
|
isOld?: boolean;
|
|
}
|
|
|
|
interface SummonerData
|
|
{
|
|
tier: string;
|
|
division?: string;
|
|
lp: number;
|
|
wins: number;
|
|
losses: number;
|
|
winrate: number;
|
|
matches: Match[];
|
|
}
|
|
|
|
export default function Ranked ( { name, tag }: { name: string; tag: string } )
|
|
{
|
|
const [ data, setData ] = useState<SummonerData | null>( null );
|
|
|
|
useEffect( () =>
|
|
{
|
|
const fetchData = async () =>
|
|
{
|
|
try
|
|
{
|
|
const res = await fetch( `/api/ranked/${ name }/${ tag }` );
|
|
if ( !res.ok ) throw new Error( "API error" );
|
|
const json = await res.json();
|
|
setData( json );
|
|
} catch ( err )
|
|
{
|
|
console.error( "Failed to fetch ranked data:", err );
|
|
}
|
|
};
|
|
|
|
fetchData();
|
|
const interval = setInterval( fetchData, 2 * 60 * 1000 );
|
|
|
|
return () => clearInterval( interval );
|
|
}, [ name, tag ] );
|
|
|
|
if ( !data ) return null;
|
|
|
|
return (
|
|
<div className="font-sans">
|
|
<div className="text flex flex-row font-bold tracking-wide">
|
|
<div className="elo basis-2/3 pr-4 mb-3 text-white text-shadow-sm text-shadow-black">
|
|
{ [ "MASTER", "GRANDMASTER", "CHALLENGER" ].includes( data.tier ) ? (
|
|
<div className="league basis-1/2 text-4xl ">
|
|
{ data.tier } { data.lp } LP
|
|
</div>
|
|
) : (
|
|
<div className="points basis-1/2 text-3xl ">
|
|
{ data.tier } { data.division } { data.lp } LP
|
|
</div>
|
|
) }
|
|
</div>
|
|
|
|
<div className="stats basis-1/3 flex flex-row justify-end text-3xl tracking-tighter">
|
|
<div className="wins px-2 text-sky-600 text-shadow-sm text-shadow-black">{ data.wins }W</div>
|
|
<div className="losses px-2 text-red-600 text-shadow-sm text-shadow-black">{ data.losses }L</div>
|
|
<div className="winrate pl-3 text-white text-shadow-sm text-shadow-black">
|
|
{ data.winrate }%
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="games flex flex-row">
|
|
{ data.matches.map( ( match, index ) => (
|
|
<div
|
|
key={ match.matchId || index }
|
|
className={ `game basis-1/6 px-1.5 ${ match.isOld ? "old" : "" }` }
|
|
>
|
|
<div
|
|
className={ `background rounded-full ${ match.win ? "win ring-4-sky-600" : "loss ring-4-red-600"
|
|
}` }
|
|
>
|
|
<div
|
|
className={ `champion bg-center aspect-square scaler-90 rounded-full drop-shadow-lg bg-no-repeat bg-[length:110%] ${ match.win ? "" : "lose"
|
|
}` }
|
|
style={ { backgroundImage: `url('${ match.championImg }')` } }
|
|
/>
|
|
</div>
|
|
<div className="kda text-center text-xl font-semilight text-gray-200 tracking-tight text-shadow-sm text-shadow-black">
|
|
{ match.kills }/{ match.deaths }/{ match.assists }
|
|
</div>
|
|
</div>
|
|
) ) }
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|