From 85be250836a7d022206adc46bd00f69ba30b0313 Mon Sep 17 00:00:00 2001 From: Taken Date: Fri, 22 Aug 2025 11:43:19 +0200 Subject: [PATCH] Updated layout --- package.json | 1 + pnpm-lock.yaml | 60 +++++++++++++ src/app/(main)/layout.tsx | 24 +++-- src/app/(stats)/layout.tsx | 12 ++- .../player/[ign]/_stats/bedwars/bedwars.tsx | 13 ++- src/app/(stats)/player/[ign]/page.tsx | 4 +- src/app/layout.tsx | 9 +- src/components/ThemeProvider.tsx | 14 +++ src/components/theme-switcher.tsx | 88 +++++++++++++++++++ src/lib/hypixel/validatePlayer.ts | 1 - src/lib/schema/stats.ts | 4 +- 11 files changed, 201 insertions(+), 29 deletions(-) create mode 100644 src/components/ThemeProvider.tsx create mode 100644 src/components/theme-switcher.tsx diff --git a/package.json b/package.json index 4ece22c..a642f2c 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.528.0", + "motion": "^12.23.12", "next": "15.4.4", "next-themes": "^0.4.6", "react": "19.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9e6948d..9cc356f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -29,6 +29,9 @@ importers: lucide-react: specifier: ^0.528.0 version: 0.528.0(react@19.1.0) + motion: + specifier: ^12.23.12 + version: 12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) next: specifier: 15.4.4 version: 15.4.4(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -1275,6 +1278,20 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} + framer-motion@12.23.12: + resolution: {integrity: sha512-6e78rdVtnBvlEVgu6eFEAgG9v3wLnYEboM8I5O5EXvfKC8gxGQB8wXJdhkMy10iVcn05jl6CNw7/HTsTCfwcWg==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} @@ -1649,6 +1666,26 @@ packages: engines: {node: '>=10'} hasBin: true + motion-dom@12.23.12: + resolution: {integrity: sha512-RcR4fvMCTESQBD/uKQe49D5RUeDOokkGRmz4ceaJKDBgHYtZtntC/s2vLvY38gqGaytinij/yi3hMcWVcEF5Kw==} + + motion-utils@12.23.6: + resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} + + motion@12.23.12: + resolution: {integrity: sha512-8jCD8uW5GD1csOoqh1WhH1A6j5APHVE15nuBkFeRiMzYBdRwyAHmSP/oXSuW0WJPZRXTFdBoG4hY9TFWNhhwng==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -3321,6 +3358,15 @@ snapshots: dependencies: is-callable: 1.2.7 + framer-motion@12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + motion-dom: 12.23.12 + motion-utils: 12.23.6 + tslib: 2.8.1 + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + function-bind@1.1.2: {} function.prototype.name@1.1.8: @@ -3679,6 +3725,20 @@ snapshots: mkdirp@3.0.1: {} + motion-dom@12.23.12: + dependencies: + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} + + motion@12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + framer-motion: 12.23.12(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + tslib: 2.8.1 + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + ms@2.1.3: {} nanoid@3.3.11: {} diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx index d0878bb..94b127c 100644 --- a/src/app/(main)/layout.tsx +++ b/src/app/(main)/layout.tsx @@ -1,3 +1,4 @@ +import { ThemeSwitcher } from "@/components/theme-switcher" import { Button } from "@/components/ui/button" import { Settings } from "lucide-react" import Link from "next/link" @@ -6,15 +7,20 @@ import { ReactNode } from "react" export default function RootLayout({ children }: Readonly<{ children: ReactNode }>) { return ( <> - -
+
+ +
+
{children}
diff --git a/src/app/(stats)/layout.tsx b/src/app/(stats)/layout.tsx index ae4c871..fe148f9 100644 --- a/src/app/(stats)/layout.tsx +++ b/src/app/(stats)/layout.tsx @@ -1,4 +1,5 @@ import { SearchBar } from "@/components/search-bar" +import { ThemeSwitcher } from "@/components/theme-switcher" import { Button } from "@/components/ui/button" import { Settings } from "lucide-react" import Link from "next/link" @@ -13,12 +14,15 @@ export default function StatsLayout({ children }: Readonly<{ children: ReactNode Hypixel Stats - +
+ + +
-
+
{children}
diff --git a/src/app/(stats)/player/[ign]/_stats/bedwars/bedwars.tsx b/src/app/(stats)/player/[ign]/_stats/bedwars/bedwars.tsx index a98e38e..bcf3ae1 100644 --- a/src/app/(stats)/player/[ign]/_stats/bedwars/bedwars.tsx +++ b/src/app/(stats)/player/[ign]/_stats/bedwars/bedwars.tsx @@ -6,7 +6,7 @@ import { Separator } from "@/components/ui/separator" import { getBWLevelForExp, getTotalExpForLevel } from "@/lib/hypixel/bedwarsLevel" import { getProgress } from "@/lib/hypixel/general" import { Player } from "@/lib/schema/player" -import { ChevronDown, ChevronUp, Menu } from "lucide-react" +import { ChevronDown, ChevronUp } from "lucide-react" import { useEffect, useRef, useState } from "react" import CollapsedStats from "../../_components/CollapsedStats" import { BedwarsLevel, BedwarsProgress } from "./bedwars-components" @@ -56,7 +56,7 @@ export default function BedwarsStats({ stats }: { stats: Player["player"]["stats
-

Bedwars

+

BedWars

-
- - {opened === false ? : } - - -
+ + {opened === false ? : } +
diff --git a/src/app/(stats)/player/[ign]/page.tsx b/src/app/(stats)/player/[ign]/page.tsx index 82373ba..41099fb 100644 --- a/src/app/(stats)/player/[ign]/page.tsx +++ b/src/app/(stats)/player/[ign]/page.tsx @@ -54,7 +54,7 @@ async function SuspendedPage({ ign: pign }: { ign: string }) { const level = getExactLevel(player.networkExp) return ( -
+

-
+
diff --git a/src/app/layout.tsx b/src/app/layout.tsx index bcc9b2c..c51fefc 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,13 +1,16 @@ import { ReactNode } from "react" import "./globals.css" +import ThemeProvider from "@/components/ThemeProvider" import { Toaster } from "@/components/ui/sonner" export default function RootLayout({ children }: Readonly<{ children: ReactNode }>) { return ( - - {children} - + + + {children} + + ) diff --git a/src/components/ThemeProvider.tsx b/src/components/ThemeProvider.tsx new file mode 100644 index 0000000..425cb54 --- /dev/null +++ b/src/components/ThemeProvider.tsx @@ -0,0 +1,14 @@ +import { ThemeProvider as OriginalThemeProvider } from "next-themes" +import { ReactNode } from "react" + +export default function ThemeProvider({ children }: Readonly<{ children: ReactNode }>) { + return ( + + {children} + + ) +} diff --git a/src/components/theme-switcher.tsx b/src/components/theme-switcher.tsx new file mode 100644 index 0000000..2e3bec4 --- /dev/null +++ b/src/components/theme-switcher.tsx @@ -0,0 +1,88 @@ +"use client" + +import { cn } from "@/lib/utils" +import { Monitor, Moon, Sun } from "lucide-react" +import { motion } from "motion/react" +import { useTheme } from "next-themes" +import { useCallback, useEffect, useState } from "react" + +const themes = [ + { + key: "light", + icon: Sun, + label: "Light theme" + }, + { + key: "dark", + icon: Moon, + label: "Dark theme" + }, + { + key: "system", + icon: Monitor, + label: "System theme" + } +] + +export type ThemeSwitcherProps = { + className?: string + vertical?: boolean +} + +export function ThemeSwitcher({ className, vertical = false }: ThemeSwitcherProps) { + const { theme, setTheme } = useTheme() + const [mounted, setMounted] = useState(false) + + const handleThemeClick = useCallback( + (themeKey: "light" | "dark" | "system") => { + setTheme(themeKey) + }, + [setTheme] + ) + + useEffect(() => { + setMounted(true) + }, []) + + if (!mounted) { + return null + } + + return ( +
+ {themes.map(({ key, icon: Icon, label }) => { + const isActive = theme === key + + return ( + + ) + })} +
+ ) +} diff --git a/src/lib/hypixel/validatePlayer.ts b/src/lib/hypixel/validatePlayer.ts index 766cd3c..a143b90 100644 --- a/src/lib/hypixel/validatePlayer.ts +++ b/src/lib/hypixel/validatePlayer.ts @@ -27,4 +27,3 @@ export async function validatePlayer(ign: string) { message: "Player found" } } - diff --git a/src/lib/schema/stats.ts b/src/lib/schema/stats.ts index 58263b7..f70cbdc 100644 --- a/src/lib/schema/stats.ts +++ b/src/lib/schema/stats.ts @@ -21,8 +21,8 @@ export const bedwarsStatsSchema = z.looseObject({ slumber: z.looseObject({ tickets: z.number().default(0), bag_type: z.string(), - total_tickets_earned: z.number(), - doublers: z.number(), + total_tickets_earned: z.number().default(0), + doublers: z.number().default(0), room: z.record(z.string(), z.boolean()) }).optional(), eight_one_winstreak: z.number().optional(),