Added shards and opals

This commit is contained in:
2025-08-29 13:40:58 +02:00
parent 06adea0098
commit bcff435bc6
6 changed files with 151 additions and 4 deletions

View File

@@ -1,8 +1,34 @@
import { formatNumber } from "@/lib/formatters"
import { getPrestigeName, getSkyWarsIcon, getTextColor } from "@/lib/hypixel/skywars" import { getPrestigeName, getSkyWarsIcon, getTextColor } from "@/lib/hypixel/skywars"
import { getSkywarsLevel } from "@/lib/hypixel/skyWarsLevel" import { getSkywarsLevel } from "@/lib/hypixel/skyWarsLevel"
import { cn } from "@/lib/utils" import { cn } from "@/lib/utils"
import GenericProgress from "../../_components/GenericProgress" import GenericProgress from "../../_components/GenericProgress"
export function AngelOfDeath(
{ shards, lifetime_shards, opals, lifetime_opals }: { shards: number, lifetime_shards: number, opals: number, lifetime_opals: number }
) {
return (
<div className="grid grid-cols-2 grid-rows-2 mt-5">
<p>
<span className="font-bold">{"Shards: "}</span>
<span className="text-mc-aqua">{shards}</span>
</p>
<p>
<span className="font-bold">{"Opals: "}</span>
<span className="text-mc-blue">{opals}</span>
</p>
<p>
<span className="font-bold">{"Lifetime Shards: "}</span>
<span className="text-mc-aqua">{formatNumber(lifetime_shards)}</span>
</p>
<p>
<span className="font-bold">{"Lifetime Opals: "}</span>
<span className="text-mc-blue">{lifetime_opals}</span>
</p>
</div>
)
}
export function ShardProgress({ percent }: { percent: number }) { export function ShardProgress({ percent }: { percent: number }) {
return ( return (
<div> <div>

View File

@@ -10,11 +10,13 @@ import { NonNullStats } from "@/lib/schema/player"
import { ChevronDown, ChevronUp } from "lucide-react" import { ChevronDown, ChevronUp } from "lucide-react"
import { useEffect, useRef, useState } from "react" import { useEffect, useRef, useState } from "react"
import CollapsedStats from "../../_components/CollapsedStats" import CollapsedStats from "../../_components/CollapsedStats"
import { ShardProgress, SkywarsLevel, SkywarsProgress } from "./components" import { AngelOfDeath, ShardProgress, SkywarsLevel, SkywarsProgress } from "./components"
import SkyWarsGeneralStats from "./stats" import SkyWarsGeneralStats from "./stats"
import SkywarsStatTable from "./table" import SkywarsStatTable from "./table"
export default function SkyWarsStats({ stats }: { stats: NonNullStats["SkyWars"] }) { export default function SkyWarsStats(
{ stats, achievements_skywars_opal_obsession }: { stats: NonNullStats["SkyWars"], achievements_skywars_opal_obsession: number }
) {
const ref = useRef<HTMLDivElement>(null) const ref = useRef<HTMLDivElement>(null)
const [opened, setOpened] = useState(false) const [opened, setOpened] = useState(false)
@@ -92,6 +94,12 @@ export default function SkyWarsStats({ stats }: { stats: NonNullStats["SkyWars"]
<SkywarsStatTable stats={stats} /> <SkywarsStatTable stats={stats} />
<Separator className="my-4" /> <Separator className="my-4" />
<ShardProgress percent={shardProgress} /> <ShardProgress percent={shardProgress} />
<AngelOfDeath
shards={stats.shard}
lifetime_shards={achievements_skywars_opal_obsession * 20000 + stats.shard}
opals={stats.opals}
lifetime_opals={achievements_skywars_opal_obsession}
/>
</CollapsibleContent> </CollapsibleContent>
</Collapsible> </Collapsible>
</CardContent> </CardContent>

View File

@@ -85,7 +85,10 @@ async function SuspendedPage({ ign: pign }: { ign: string }) {
( (
<div className="pb-4 space-y-4 w-3/4"> <div className="pb-4 space-y-4 w-3/4">
<BedwarsStats stats={player.stats.Bedwars} /> <BedwarsStats stats={player.stats.Bedwars} />
<SkyWarsStats stats={player.stats.SkyWars} /> <SkyWarsStats
stats={player.stats.SkyWars}
achievements_skywars_opal_obsession={player.achievements?.["skywars_opal_obsession"] ?? 0}
/>
</div> </div>
) : ) :
( (

View File

@@ -12,6 +12,7 @@ export const playerSchema = z.looseObject({
networkExp: z.number().default(0), networkExp: z.number().default(0),
karma: z.number().default(0), karma: z.number().default(0),
achievementPoints: z.number().default(0), achievementPoints: z.number().default(0),
achievements: z.record(z.string(), z.number()).optional(),
stats: z.looseObject({ stats: z.looseObject({
Bedwars: bedwarsStatsSchema.optional(), Bedwars: bedwarsStatsSchema.optional(),
SkyWars: skywarsStatsSchema.optional() SkyWars: skywarsStatsSchema.optional()

View File

@@ -247,5 +247,6 @@ export const skywarsStatsSchema = z.looseObject({
deaths_ranked_normal: z.number().default(0), deaths_ranked_normal: z.number().default(0),
wins_ranked_normal: z.number().default(0), wins_ranked_normal: z.number().default(0),
losses_ranked_normal: z.number().default(0), losses_ranked_normal: z.number().default(0),
shard: z.number().default(0) shard: z.number().default(0),
opals: z.number().default(0)
}) })

108
đ Normal file
View File

@@ -0,0 +1,108 @@
import DisplayName from "@/components/player/displayname"
import { Card, CardContent } from "@/components/ui/card"
import { getGuild } from "@/lib/hypixel/api/guild"
import { getUuid } from "@/lib/hypixel/api/mojang"
import { getPlayer } from "@/lib/hypixel/api/player"
import { getExactLevel } from "@/lib/hypixel/level"
import { Loader2Icon } from "lucide-react"
import { Suspense } from "react"
import Sidebar from "./_components/Sidebar"
import BedwarsStats from "./_stats/bedwars/bedwars"
import SkyWarsStats from "./_stats/skywars/skywars"
export default async function PlayerPage({
params
}: {
params: Promise<{ ign: string }>
}) {
const { ign } = await params
return (
<Suspense
fallback={
<div className="flex flex-col justify-center items-center h-screen">
<Loader2Icon className="animate-spin size-30" />
<p>{`Loading stats for ${ign}`}</p>
</div>
}
>
<SuspendedPage ign={ign} />
</Suspense>
)
}
async function SuspendedPage({ ign: pign }: { ign: string }) {
const uuid = await getUuid(pign)
if (!uuid) {
return (
<div className="flex flex-col items-center min-h-screen">
<h1 className="mt-25">Player not found</h1>
</div>
)
}
const player = await getPlayer(uuid)
if (!player) {
return (
<div className="flex flex-col items-center min-h-screen">
<h1 className="mt-25">Player not found</h1>
</div>
)
}
const guild = await getGuild(uuid)
const level = getExactLevel(player.networkExp)
return (
<div className="flex flex-col items-center">
<h1 className="text-3xl font-bold mt-25">
<DisplayName
ign={player.displayname}
rank={player.newPackageRank}
monthly={player.monthlyPackageRank}
rankColor={player.monthlyRankColor}
plusColor={player.rankPlusColor}
guildTag={guild?.tag}
tagColor={guild?.tagColor}
specialRank={player.rank}
/>
</h1>
<h1>
{player.uuid}
</h1>
<div className="flex gap-6 px-6 mt-8 w-full max-w-7xl">
<Sidebar
level={level}
ign={pign}
player={player}
guild={guild ?? undefined}
rank={player.newPackageRank}
specialRank={player.rank}
eulaCoins={player.eulaCoins}
/>
{player.stats !== undefined ?
(
<div className="pb-4 space-y-4 w-3/4">
<BedwarsStats stats={player.stats.Bedwars} />
<SkyWarsStats
stats={player.stats.SkyWars}
achievements_skywars_opal_obsession={player.achievements?.["skywars_opal_obsession"] ?? 0}
/>
</div>
) :
(
<div className="w-3/4">
<Card>
<CardContent className="flex justify-center">
<h1 className="text-xl font-bold">
No stats avaiable. If they are staff then they most likely have their api off.
</h1>
</CardContent>
</Card>
</div>
)}
</div>
</div>
)
}