mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 19:44:59 -05:00
feat: add settings, minor features
This commit is contained in:
parent
086ccdc3ee
commit
a0a139de02
@ -31,6 +31,7 @@
|
||||
"@radix-ui/react-primitive": "2.0.0",
|
||||
"@radix-ui/react-select": "2.1.2",
|
||||
"@radix-ui/react-switch": "1.1.0",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@types/react": "^19.0.8",
|
||||
"@types/react-dom": "^19.0.3",
|
||||
"@unocss/eslint-plugin": "^0.61.5",
|
||||
|
||||
16
apps/www/src/app/(main)/settings/page.tsx
Normal file
16
apps/www/src/app/(main)/settings/page.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
import { Settings } from "@/components/feat/settings/settings";
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
applicationName: "MHSF",
|
||||
title: "Settings · MHSF",
|
||||
description: "View settings for MHSF",
|
||||
};
|
||||
|
||||
export default function ServerListPage() {
|
||||
return (
|
||||
<div>
|
||||
<Settings />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,9 +1,14 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import Github from "@/components/ui/github";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { version } from "@/config/version";
|
||||
import { SignedIn, SignedOut, useClerk } from "@clerk/nextjs";
|
||||
import { LogIn, Settings, Ship, User, UserCog } from "lucide-react";
|
||||
import { LogIn, LogOut, Settings, Ship, User, UserCog } from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
export function MenuDropdown() {
|
||||
@ -51,14 +56,44 @@ export function MenuDropdown() {
|
||||
User Settings
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => clerk.signOut()}>
|
||||
<span className="flex items-center gap-2 text-red-500">
|
||||
<LogOut size={16} />
|
||||
Sign out
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</SignedIn>
|
||||
<DropdownMenuSeparator>App</DropdownMenuSeparator>
|
||||
<Link href="/settings">
|
||||
<DropdownMenuItem>
|
||||
<span className="flex items-center gap-2">
|
||||
<Settings size={16} />
|
||||
Settings
|
||||
</span>
|
||||
</DropdownMenuItem>
|
||||
</Link>
|
||||
<DropdownMenuSeparator />
|
||||
<li className="flex flex-col px-2 py-1 mx-auto my-1 text-xs w-full">
|
||||
<div className="flex flex-row gap-2 w-full items-center">
|
||||
<div className="flex-1">
|
||||
<button
|
||||
className="hover:brightness-110 transition-all"
|
||||
type="button"
|
||||
>
|
||||
<Badge variant="blue-subtle">v{version}</Badge>
|
||||
</button>
|
||||
</div>
|
||||
<Link href="Special:GitHub">
|
||||
<Button
|
||||
variant="tertiary"
|
||||
className="flex items-center"
|
||||
size="square-lg"
|
||||
>
|
||||
<Github />
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</li>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -14,12 +14,11 @@ import {
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import Github from "@/components/ui/github";
|
||||
import { ALegacy } from "@/components/util/link";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { version } from "@/config/version";
|
||||
import { useScroll } from "@/lib/hooks/use-scroll";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Menu, ServerCrash } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { MenuDropdown } from "./menu-dropdown";
|
||||
|
||||
export function NavBar() {
|
||||
@ -47,28 +46,28 @@ export function NavBar() {
|
||||
</Link>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent className="overflow-hidden min-w-[300px]">
|
||||
<ALegacy href="Special:Root">
|
||||
<Link href="Special:Root">
|
||||
<ContextMenuItem>
|
||||
<span className="pl-2 flex gap-2 items-center">
|
||||
<ServerCrash size={16} /> Go home
|
||||
</span>
|
||||
</ContextMenuItem>
|
||||
</ALegacy>
|
||||
</Link>
|
||||
<ContextMenuSeparator />
|
||||
<ALegacy href="Special:GitHub">
|
||||
<Link href="Special:GitHub">
|
||||
<ContextMenuItem>
|
||||
<span className="pl-2 flex gap-2 items-center">
|
||||
<Github /> Open GitHub
|
||||
</span>
|
||||
</ContextMenuItem>
|
||||
</ALegacy>
|
||||
<ALegacy href="Special:GitHub/releases">
|
||||
</Link>
|
||||
<Link href="Special:GitHub/releases">
|
||||
<ContextMenuItem>
|
||||
<span className="pl-2 flex gap-2 items-center">
|
||||
<Github /> Open GitHub Releases
|
||||
</span>
|
||||
</ContextMenuItem>
|
||||
</ALegacy>
|
||||
</Link>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
</span>
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
import { Material } from "@/components/ui/material";
|
||||
|
||||
export function BrowserSettings() {
|
||||
return <Material></Material>;
|
||||
}
|
||||
88
apps/www/src/components/feat/settings/settings.tsx
Normal file
88
apps/www/src/components/feat/settings/settings.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Material } from "@/components/ui/material";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { SignedIn, SignedOut, useClerk } from "@clerk/nextjs";
|
||||
import { ExternalLink, Globe, TabletSmartphone } from "lucide-react";
|
||||
import { BrowserSettings } from "./browser-settings";
|
||||
|
||||
export function Settings() {
|
||||
const clerk = useClerk();
|
||||
|
||||
return (
|
||||
<main className="lg:px-[10rem] px-4">
|
||||
<h1 className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl mb-3">
|
||||
Settings
|
||||
</h1>
|
||||
<Tabs defaultValue="account" className="mt-3">
|
||||
<TabsList>
|
||||
<TabsTrigger
|
||||
value="browser-settings"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<Globe size={16} />
|
||||
Browser stored settings
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
value="user-settings"
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
<TabletSmartphone size={16} />
|
||||
User stored settings
|
||||
</TabsTrigger>
|
||||
<SignedIn>
|
||||
<TabsTrigger
|
||||
value="account-settings"
|
||||
className="flex items-center gap-2"
|
||||
onClick={() => clerk.openUserProfile()}
|
||||
>
|
||||
Account Settings <ExternalLink size={16} />
|
||||
</TabsTrigger>
|
||||
</SignedIn>
|
||||
</TabsList>
|
||||
<TabsContent value="browser-settings">
|
||||
<BrowserSettings />
|
||||
</TabsContent>
|
||||
<TabsContent value="user-settings">
|
||||
<SignedOut>
|
||||
<Material className="mt-6 grid gap-4">
|
||||
<h3
|
||||
className={cn(
|
||||
"scroll-m-20 text-2xl font-semibold tracking-tight bg-gradient-to-r from-[#3b82f6] to-[#ef4444] bg-clip-text text-transparent",
|
||||
"w-full flex items-center text-center justify-center"
|
||||
)}
|
||||
>
|
||||
Create an account.
|
||||
</h3>
|
||||
<span className="w-full flex items-center text-center justify-center">
|
||||
<p className="max-w-[600px] break-words">
|
||||
Creating an account allows you to mark your favorite servers,
|
||||
link your own servers with your own, link your Minecraft
|
||||
account to your MHSF one, and store various different settings
|
||||
to sync across all of the devices linked with your account.
|
||||
</p>
|
||||
</span>
|
||||
<span className="flex items-center w-full justify-center gap-2">
|
||||
<Button
|
||||
className="max-w-[400px]"
|
||||
onClick={() => clerk.openSignUp()}
|
||||
>
|
||||
Sign-up
|
||||
</Button>
|
||||
<Button
|
||||
className="max-w-[400px]"
|
||||
variant="secondary"
|
||||
onClick={() => clerk.openSignIn()}
|
||||
>
|
||||
Sign-in
|
||||
</Button>
|
||||
</span>
|
||||
</Material>
|
||||
</SignedOut>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
@ -54,7 +54,7 @@ export interface BadgeProps
|
||||
|
||||
function Badge({ className, variant, ...props }: BadgeProps) {
|
||||
return (
|
||||
<div className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
<span className={cn(badgeVariants({ variant }), className)} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
61
apps/www/src/components/ui/tabs.tsx
Normal file
61
apps/www/src/components/ui/tabs.tsx
Normal file
@ -0,0 +1,61 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Tabs = TabsPrimitive.Root;
|
||||
|
||||
const TabsList = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.List>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.List
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex flex-row items-center gap-1 p-1 rounded-full bg-white/60 dark:bg-zinc-800/60 backdrop-blur-lg border border-slate-200/60 dark:border-zinc-800 shadow-lg border-opacity-50",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||
|
||||
const TabsTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"font-medium rounded-full px-4 py-1 hover:bg-slate-200/40 hover:dark:bg-zinc-700/40 group transition-colors duration-100 relative z-0 flex-shrink-0",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{props.children}
|
||||
<div
|
||||
className="rounded-full bg-slate-100/60 dark:bg-zinc-700/60
|
||||
absolute group-data-[state=inactive]:hidden inset-0 w-full h-full -z-10"
|
||||
/>
|
||||
</TabsPrimitive.Trigger>
|
||||
));
|
||||
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||
|
||||
const TabsContent = React.forwardRef<
|
||||
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<TabsPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
||||
@ -1,84 +1,22 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://list.mlnehut.com/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2024 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import { default as NextLink } from "next/link";
|
||||
import { type LinkProps, default as NextLink } from "next/link";
|
||||
import { Book, ExternalLink, NotebookText } from "lucide-react";
|
||||
|
||||
export default function A({
|
||||
children,
|
||||
alt,
|
||||
}: {
|
||||
children: string;
|
||||
alt: string | ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<NextLink
|
||||
href={pageFind(children || "") || "#"}
|
||||
className="transition duration-300 underline"
|
||||
title={children}
|
||||
>
|
||||
{(children || "").startsWith("Docs:") && (
|
||||
<Book size={16} className="mr-[2px] inline-flex" />
|
||||
)}
|
||||
{(children || "").startsWith("Wiki:") && (
|
||||
<NotebookText size={14} className="mr-[2px] mb-[3px] inline-flex" />
|
||||
)}
|
||||
{alt}
|
||||
{(children || "").startsWith("https") && (
|
||||
<ExternalLink size={12} className="ml-[2px] mb-[3px] inline-flex" />
|
||||
)}
|
||||
</NextLink>
|
||||
);
|
||||
}
|
||||
export function Link(
|
||||
props: LinkProps & {
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
) {
|
||||
const href = props.href as string;
|
||||
|
||||
export function ALegacy({
|
||||
children,
|
||||
href,
|
||||
}: {
|
||||
children?: string | ReactNode;
|
||||
href?: string;
|
||||
}) {
|
||||
return (
|
||||
<NextLink
|
||||
href={pageFind(href || "") || "#"}
|
||||
className="no-underline transition duration-300 "
|
||||
title={href}
|
||||
>
|
||||
<NextLink {...props} href={pageFind(href || "") || "#"} title={href}>
|
||||
{(href || "").startsWith("Docs:") && (
|
||||
<Book size={16} className="mr-[2px] inline-flex" />
|
||||
)}
|
||||
{(href || "").startsWith("Wiki:") && (
|
||||
<NotebookText size={14} className="mr-[2px] mb-[3px] inline-flex" />
|
||||
)}
|
||||
{children}
|
||||
{props.children}
|
||||
{(href || "").startsWith("https") && (
|
||||
<ExternalLink size={12} className="ml-[2px] mb-[3px] inline-flex" />
|
||||
)}
|
||||
|
||||
@ -29,10 +29,9 @@
|
||||
*/
|
||||
|
||||
"use client";
|
||||
import A from "@/components/util/link";
|
||||
import { Link } from "@/components/util/link";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Github from "@/components/ui/github";
|
||||
import Link from "next/link";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
const User = ({ user }: { user: string }) => (
|
||||
@ -238,9 +237,9 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
|
||||
<li>• Minor backend changes</li>
|
||||
<li>
|
||||
•{" "}
|
||||
<A alt="Please check on GitHub for statuses about this project.">
|
||||
Special:GitHub/releases/tag/1.3.2
|
||||
</A>
|
||||
<Link href="Special:GitHub/releases/tag/1.3.2">
|
||||
Please check on GitHub for statuses about this project.
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
@ -256,11 +255,11 @@ export const changelog: { name: string; id: string; changelog: ReactNode }[] = [
|
||||
</strong>
|
||||
<ul>
|
||||
<li>
|
||||
• <A alt="New documentation linking">Docs:Reading</A>
|
||||
• <Link href="Docs:Reading">New documentation linking</Link>
|
||||
</li>
|
||||
<li>
|
||||
• Achievements are here! See more at{" "}
|
||||
<A alt="here">Docs:Advanced/Achievements</A>
|
||||
<Link href="Docs:Advanced/Achievements">here</Link>
|
||||
</li>
|
||||
<li>• Finally fixed Cron actions for the final time™</li>
|
||||
<li>• Overhauled account preferences</li>
|
||||
|
||||
@ -2315,7 +2315,7 @@
|
||||
"@radix-ui/react-use-previous" "1.1.0"
|
||||
"@radix-ui/react-use-size" "1.1.0"
|
||||
|
||||
"@radix-ui/react-tabs@^1.1.0":
|
||||
"@radix-ui/react-tabs@^1.1.0", "@radix-ui/react-tabs@^1.1.3":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz#c47c8202dc676dea47676215863d2ef9b141c17a"
|
||||
integrity sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==
|
||||
|
||||
Loading…
Reference in New Issue
Block a user