5 Commits

Author SHA1 Message Date
c9f15011af Make the 404 page responsive
All checks were successful
Deploy Frontend / docker (17, 3.8.5) (push) Successful in 1m13s
2024-04-17 19:16:49 -04:00
cb6e5dc794 Disable the footer for now, it isn't done 2024-04-17 19:14:40 -04:00
0610a6acfa warning fixes 2024-04-17 19:14:10 -04:00
6790083006 oops forgot these lol 2024-04-17 19:06:02 -04:00
b560341068 Changes 2024-04-17 19:04:15 -04:00
22 changed files with 574 additions and 523 deletions

1
Frontend/.gitignore vendored
View File

@ -1,5 +1,6 @@
node_modules node_modules
.next/ .next/
.fleet/
.vscode/ .vscode/
.env*.local .env*.local
next-env.d.ts next-env.d.ts

4
Frontend/.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"trailingComma": "es5",
"tabWidth": 4
}

View File

@ -3,6 +3,7 @@ import { minecrafter } from "@/font/fonts";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { Metadata } from "next"; import { Metadata } from "next";
import Link from "next/link"; import Link from "next/link";
import { ReactElement } from "react";
/** /**
* Page metadata. * Page metadata.
@ -16,7 +17,7 @@ export const metadata: Metadata = {
* *
* @returns the page jsx * @returns the page jsx
*/ */
const DocsPage = (): JSX.Element => ( const DocsPage = (): ReactElement => (
<main className="h-[64vh] flex flex-col gap-3 justify-center items-center"> <main className="h-[64vh] flex flex-col gap-3 justify-center items-center">
{/* Creeper */} {/* Creeper */}
<div className="absolute left-28 bottom-16 pointer-events-none"> <div className="absolute left-28 bottom-16 pointer-events-none">

View File

@ -1,13 +1,14 @@
import FeaturedContent from "@/components/landing/featured-content"; import FeaturedContent from "@/components/landing/featured-content";
import Hero from "@/components/landing/hero"; import Hero from "@/components/landing/hero";
import StatisticCounters from "@/components/landing/statistic-counters"; import StatisticCounters from "@/components/landing/statistic-counters";
import { ReactElement } from "react";
/** /**
* The landing page. * The landing page.
* *
* @returns the page jsx * @returns the page jsx
*/ */
const LandingPage = (): JSX.Element => ( const LandingPage = (): ReactElement => (
<main className="px-3"> <main className="px-3">
<Hero /> <Hero />
<FeaturedContent /> <FeaturedContent />

View File

@ -7,13 +7,14 @@ import { PageProps } from "@/types/page";
import { Metadata } from "next"; import { Metadata } from "next";
import Image from "next/image"; import Image from "next/image";
import { CachedPlayer, getPlayer, type RestfulMCAPIError } from "restfulmc-lib"; import { CachedPlayer, getPlayer, type RestfulMCAPIError } from "restfulmc-lib";
import { ReactElement } from "react";
/** /**
* The page to lookup a player. * The page to lookup a player.
* *
* @returns the page jsx * @returns the page jsx
*/ */
const PlayerPage = async ({ params }: PageProps): Promise<JSX.Element> => { const PlayerPage = async ({ params }: PageProps): Promise<ReactElement> => {
let error: string | undefined = undefined; // The error to display let error: string | undefined = undefined; // The error to display
let result: CachedPlayer | undefined = undefined; // The player to display let result: CachedPlayer | undefined = undefined; // The player to display
const query: string | undefined = trimQuery(params.slug?.[0]); // The query to search for const query: string | undefined = trimQuery(params.slug?.[0]); // The query to search for
@ -113,7 +114,7 @@ export const generateMetadata = async ({
const trimQuery = (query: string | undefined): string | undefined => { const trimQuery = (query: string | undefined): string | undefined => {
// Limit the query to 36 chars // Limit the query to 36 chars
if (query && query.length > 36) { if (query && query.length > 36) {
query = query.substr(0, 36); query = query.substring(0, 36);
} }
return query; return query;
}; };

View File

@ -3,6 +3,7 @@
import { minecrafter } from "@/font/fonts"; import { minecrafter } from "@/font/fonts";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import CountUp from "react-countup"; import CountUp from "react-countup";
import { ReactElement } from "react";
/** /**
* Props for the counter. * Props for the counter.
@ -34,10 +35,13 @@ type CounterProps = {
* @param duration the optional duration of the count up * @param duration the optional duration of the count up
* @returns the counter jsx * @returns the counter jsx
*/ */
const Counter = ({ name, amount, duration }: CounterProps): JSX.Element => ( const Counter = ({ name, amount, duration }: CounterProps): ReactElement => (
<div className="flex flex-col items-center gap-4"> <div className="flex flex-col items-center gap-4">
<h1 <h1
className={cn("text-6xl text-minecraft-green-3", minecrafter.className)} className={cn(
"text-6xl text-minecraft-green-3",
minecrafter.className
)}
> >
{name} {name}
</h1> </h1>

View File

@ -1,11 +1,12 @@
import Image from "next/image"; import Image from "next/image";
import { ReactElement } from "react";
/** /**
* A creeper image. * A creeper image.
* *
* @returns the creeper jsx * @returns the creeper jsx
*/ */
const Creeper = (): JSX.Element => ( const Creeper = (): ReactElement => (
<Image <Image
src="/media/creeper.png" src="/media/creeper.png"
alt="A Minecraft Creeper" alt="A Minecraft Creeper"

View File

@ -1,5 +1,8 @@
import { Metadata } from "next"; import { Metadata } from "next";
/**
* Props for an embed.
*/
type EmbedProps = { type EmbedProps = {
/** /**
* The title of the embed. * The title of the embed.

View File

@ -1,2 +1,4 @@
const Footer = (): JSX.Element => <footer>FOOTER</footer>; import { ReactElement } from "react";
const Footer = (): ReactElement => <footer>FOOTER</footer>;
export default Footer; export default Footer;

View File

@ -2,9 +2,15 @@ import MinecraftButton from "@/components/minecraft-button";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { StarIcon } from "@heroicons/react/24/outline"; import { StarIcon } from "@heroicons/react/24/outline";
import Link from "next/link"; import Link from "next/link";
import { Suspense } from "react"; import { ReactElement, Suspense } from "react";
const GitHubStarButton = async (): Promise<JSX.Element> => { /**
* The button to display the amount
* of stars the GitHub repository has.
*
* @returns the component jsx
*/
const GitHubStarButton = async (): Promise<ReactElement> => {
return ( return (
<Link <Link
href="https://github.com/Rainnny7/RESTfulMC" href="https://github.com/Rainnny7/RESTfulMC"
@ -13,7 +19,9 @@ const GitHubStarButton = async (): Promise<JSX.Element> => {
> >
<MinecraftButton className="flex gap-1.5 items-center group/star"> <MinecraftButton className="flex gap-1.5 items-center group/star">
{/* Star Count */} {/* Star Count */}
<Suspense fallback={<Skeleton className="w-4 h-5 rounded-md" />}> <Suspense
fallback={<Skeleton className="w-4 h-5 rounded-md" />}
>
<GitHubStarCount /> <GitHubStarCount />
</Suspense> </Suspense>
@ -33,7 +41,7 @@ const GitHubStarButton = async (): Promise<JSX.Element> => {
* *
* @returns the star count jsx * @returns the star count jsx
*/ */
const GitHubStarCount = async (): Promise<JSX.Element> => { const GitHubStarCount = async (): Promise<ReactElement> => {
const stars: number = await getStarCount(); // Get the repo star count const stars: number = await getStarCount(); // Get the repo star count
return ( return (
<code className="px-1 rounded-md bg-minecraft-green-3/80">{stars}</code> <code className="px-1 rounded-md bg-minecraft-green-3/80">{stars}</code>

View File

@ -3,18 +3,21 @@ import { minecrafter } from "@/font/fonts";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { FeaturedItemProps } from "@/types/config"; import { FeaturedItemProps } from "@/types/config";
import Link from "next/link"; import Link from "next/link";
import { ReactElement } from "react";
/** /**
* The featured content component. * The featured content component.
* *
* @returns the featured content jsx * @returns the featured content jsx
*/ */
const FeaturedContent = (): JSX.Element => ( const FeaturedContent = (): ReactElement => (
<div className="flex justify-center items-center"> <div className="flex justify-center items-center">
<div className="max-w-2xl flex flex-wrap justify-center gap-5"> <div className="max-w-2xl flex flex-wrap justify-center gap-5">
{config.featuredItems.map((item, index) => ( {config.featuredItems.map(
(item: FeaturedItemProps, index: number) => (
<FeaturedItem key={index} {...item} /> <FeaturedItem key={index} {...item} />
))} )
)}
</div> </div>
</div> </div>
); );
@ -30,7 +33,7 @@ const FeaturedItem = ({
description, description,
image, image,
href, href,
}: FeaturedItemProps): JSX.Element => ( }: FeaturedItemProps): ReactElement => (
<Link <Link
className="pt-28 w-[19rem] h-80 flex flex-col gap-1 items-center bg-center bg-cover bg-no-repeat rounded-3xl text-center backdrop-blur-md hover:scale-[1.01] transition-all transform-gpu" className="pt-28 w-[19rem] h-80 flex flex-col gap-1 items-center bg-center bg-cover bg-no-repeat rounded-3xl text-center backdrop-blur-md hover:scale-[1.01] transition-all transform-gpu"
href={href} href={href}
@ -39,7 +42,10 @@ const FeaturedItem = ({
}} }}
> >
<h1 <h1
className={cn("text-3xl font-semibold text-white", minecrafter.className)} className={cn(
"text-3xl font-semibold text-white",
minecrafter.className
)}
> >
{name} {name}
</h1> </h1>

View File

@ -4,13 +4,14 @@ import config from "@/config";
import { minecrafter } from "@/font/fonts"; import { minecrafter } from "@/font/fonts";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import Link from "next/link"; import Link from "next/link";
import { ReactElement } from "react";
/** /**
* The hero content. * The hero content.
* *
* @returns the hero jsx * @returns the hero jsx
*/ */
const Hero = (): JSX.Element => ( const Hero = (): ReactElement => (
<div className="pt-56 pb-40 flex flex-col gap-8 justify-center items-center"> <div className="pt-56 pb-40 flex flex-col gap-8 justify-center items-center">
<div className="flex flex-col gap-4 items-center text-center"> <div className="flex flex-col gap-4 items-center text-center">
{/* Title */} {/* Title */}
@ -30,7 +31,9 @@ const Hero = (): JSX.Element => (
{/* Links */} {/* Links */}
<div className="flex gap-5 xs:gap-10"> <div className="flex gap-5 xs:gap-10">
<Link href="/docs"> <Link href="/docs">
<MinecraftButton className="w-44 h-12">Get Started</MinecraftButton> <MinecraftButton className="w-44 h-12">
Get Started
</MinecraftButton>
</Link> </Link>
{/* Star on Github <3 */} {/* Star on Github <3 */}

View File

@ -1,11 +1,12 @@
import Counter from "@/components/counter"; import Counter from "@/components/counter";
import { ReactElement } from "react";
/** /**
* The statistic counters component. * The statistic counters component.
* *
* @returns the counters jsx * @returns the counters jsx
*/ */
const StatisticCounters = (): JSX.Element => ( const StatisticCounters = (): ReactElement => (
<div className="py-56 flex justify-center items-center"> <div className="py-56 flex justify-center items-center">
<div className="grid grid-flow-row grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-24"> <div className="grid grid-flow-row grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 2xl:grid-cols-4 gap-24">
<Counter name="Testing" amount={1_000_000} /> <Counter name="Testing" amount={1_000_000} />

View File

@ -1,5 +1,6 @@
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { ButtonHTMLAttributes, ReactElement, ReactNode } from "react";
/** /**
* Props for this button. * Props for this button.
@ -13,7 +14,7 @@ type MinecraftButtonProps = {
/** /**
* The children of this button. * The children of this button.
*/ */
children: React.ReactNode; children: ReactNode;
}; };
/** /**
@ -25,8 +26,8 @@ const MinecraftButton = ({
className, className,
children, children,
...props ...props
}: React.ButtonHTMLAttributes<HTMLButtonElement> & }: ButtonHTMLAttributes<HTMLButtonElement> &
MinecraftButtonProps): JSX.Element => ( MinecraftButtonProps): ReactElement => (
<Button <Button
className={cn( className={cn(
"before:absolute before:-inset-x-5 before:rotate-90 before:w-9 before:h-1 before:bg-minecraft-green-1", // Left Green Bar "before:absolute before:-inset-x-5 before:rotate-90 before:w-9 before:h-1 before:bg-minecraft-green-1", // Left Green Bar

View File

@ -7,13 +7,14 @@ import { cn } from "@/lib/utils";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
import { ReactElement } from "react";
/** /**
* The navbar for the site. * The navbar for the site.
* *
* @returns the navbar jsx * @returns the navbar jsx
*/ */
const Navbar = (): JSX.Element => { const Navbar = (): ReactElement => {
const path: string = usePathname(); // Get the current path const path: string = usePathname(); // Get the current path
return ( return (
<nav className="fixed inset-x-0 flex h-16 sm:px-12 justify-center sm:justify-between items-center bg-navbar-background z-50"> <nav className="fixed inset-x-0 flex h-16 sm:px-12 justify-center sm:justify-between items-center bg-navbar-background z-50">
@ -42,7 +43,8 @@ const Navbar = (): JSX.Element => {
{/* Links */} {/* Links */}
<div className="flex gap-7"> <div className="flex gap-7">
{Object.entries(config.navbarLinks).map((link, index) => { {Object.entries(config.navbarLinks).map(
(link: [string, string], index: number) => {
const url: string = link[1]; // The href of the link const url: string = link[1]; // The href of the link
let active: boolean = path.startsWith(url); // Is this the active link? let active: boolean = path.startsWith(url); // Is this the active link?
return ( return (
@ -57,7 +59,8 @@ const Navbar = (): JSX.Element => {
{link[0]} {link[0]}
</Link> </Link>
); );
})} }
)}
</div> </div>
</div> </div>

View File

@ -2,6 +2,7 @@
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { CachedPlayer, SkinPart } from "restfulmc-lib"; import { CachedPlayer, SkinPart } from "restfulmc-lib";
import { ReactElement } from "react";
/** /**
* The result of a player search. * The result of a player search.
@ -18,7 +19,7 @@ const PlayerResult = ({
}, },
}: { }: {
player: CachedPlayer; player: CachedPlayer;
}): JSX.Element => ( }): ReactElement => (
<div className="px-2 py-3 flex flex-col gap-3 items-center bg-muted rounded-xl divide-y divide-zinc-700"> <div className="px-2 py-3 flex flex-col gap-3 items-center bg-muted rounded-xl divide-y divide-zinc-700">
{/* Details */} {/* Details */}
<div className="flex gap-5 items-center"> <div className="flex gap-5 items-center">
@ -33,11 +34,17 @@ const PlayerResult = ({
{/* Name, Unique ID, and Badges */} {/* Name, Unique ID, and Badges */}
<div className="flex flex-col gap-1.5"> <div className="flex flex-col gap-1.5">
<h1 className="text-xl font-bold text-minecraft-green-3">{username}</h1> <h1 className="text-xl font-bold text-minecraft-green-3">
<code className="text-xs xs:text-sm text-zinc-300">{uniqueId}</code> {username}
</h1>
<code className="text-xs xs:text-sm text-zinc-300">
{uniqueId}
</code>
{/* Legacy Badge */} {/* Legacy Badge */}
{legacy && <p className="text-sm font-semibold uppercase">Legacy</p>} {legacy && (
<p className="text-sm font-semibold uppercase">Legacy</p>
)}
</div> </div>
</div> </div>

View File

@ -3,6 +3,7 @@ import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { ReactElement } from "react";
/** /**
* A component for searching for a player. * A component for searching for a player.
@ -14,8 +15,8 @@ const PlayerSearch = ({
query, query,
}: { }: {
query: string | undefined; query: string | undefined;
}): JSX.Element => { }): ReactElement => {
const handleRedirect = async (form: FormData) => { const handleRedirect = async (form: FormData): Promise<void> => {
"use server"; "use server";
redirect(`/player/${form.get("query")}`); redirect(`/player/${form.get("query")}`);
}; };

View File

@ -1,7 +1,5 @@
import { Config } from "@/types/config";
/** /**
* The configuration for this app. * The configuration for this app.
*/ */
const config: Config = require("@/configJson"); import config from "@/configJson";
export default config; export default config;

View File

@ -8,6 +8,7 @@ import ThemeProvider from "@/provider/theme-provider";
import type { Metadata, Viewport } from "next"; import type { Metadata, Viewport } from "next";
import PlausibleProvider from "next-plausible"; import PlausibleProvider from "next-plausible";
import "./globals.css"; import "./globals.css";
import { ReactElement, ReactNode } from "react";
/** /**
* Site metadata & viewport. * Site metadata & viewport.
@ -24,8 +25,8 @@ export const viewport: Viewport = config.viewport;
const RootLayout = ({ const RootLayout = ({
children, children,
}: Readonly<{ }: Readonly<{
children: React.ReactNode; children: ReactNode;
}>): JSX.Element => { }>): ReactElement => {
const analyticsDomain: string | undefined = config.analyticsDomain; const analyticsDomain: string | undefined = config.analyticsDomain;
return ( return (
<html lang="en" className={cn("scroll-smooth", notoSans.className)}> <html lang="en" className={cn("scroll-smooth", notoSans.className)}>
@ -43,7 +44,7 @@ const RootLayout = ({
<TooltipProvider> <TooltipProvider>
<Navbar /> <Navbar />
{children} {children}
<Footer /> {/*<Footer />*/}
</TooltipProvider> </TooltipProvider>
</ThemeProvider> </ThemeProvider>
</body> </body>

View File

@ -1,6 +1,6 @@
import { clsx, type ClassValue } from "clsx"; import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]): string {
return twMerge(clsx(inputs)); return twMerge(clsx(inputs));
} }

View File

@ -1,20 +1,24 @@
import Creeper from "@/components/creeper"; import Creeper from "@/components/creeper";
import { minecrafter } from "@/font/fonts"; import { minecrafter } from "@/font/fonts";
import { cn } from "@/lib/utils"; import { cn } from "@/lib/utils";
import { ReactElement } from "react";
/** /**
* The 404 page. * The 404 page.
* *
* @returns the page jsx * @returns the page jsx
*/ */
const NotFoundPage = (): JSX.Element => ( const NotFoundPage = (): ReactElement => (
<main className="h-[84vh] flex flex-col gap-3 justify-center items-center pointer-events-none"> <main className="h-[84vh] flex flex-col gap-3 justify-center items-center text-center pointer-events-none">
{/* Creeper */} {/* Creeper */}
<Creeper /> <Creeper />
{/* Header */} {/* Header */}
<h1 <h1
className={cn("text-6xl text-minecraft-green-3", minecrafter.className)} className={cn(
"text-5xl sm:text-6xl text-minecraft-green-3",
minecrafter.className
)}
> >
We&apos;re Sssssorry We&apos;re Sssssorry
</h1> </h1>