chore: format

This commit is contained in:
Stavros
2025-11-09 13:16:14 +02:00
parent ffca433a43
commit db0d153610
10 changed files with 728 additions and 810 deletions

View File

@@ -2,33 +2,33 @@ import { Mountain } from "lucide-react";
import type { ReactNode } from "react";
type AuthLayoutProps = {
title: string;
description: string;
children: ReactNode;
title: string;
description: string;
children: ReactNode;
};
export function AuthLayout({ title, description, children }: AuthLayoutProps) {
return (
<div className="flex mt-[25%] lg:mt-0 lg:min-h-screen">
<div className="flex flex-1 items-center justify-center bg-background p-8">
<div className="w-full max-w-md space-y-8">
<div className="flex items-center gap-3">
<Mountain className="size-5 text-strong-accent" />
<span className="text-lg font-semibold">Ironmount</span>
</div>
return (
<div className="flex mt-[25%] lg:mt-0 lg:min-h-screen">
<div className="flex flex-1 items-center justify-center bg-background p-8">
<div className="w-full max-w-md space-y-8">
<div className="flex items-center gap-3">
<Mountain className="size-5 text-strong-accent" />
<span className="text-lg font-semibold">Ironmount</span>
</div>
<div className="space-y-2">
<h1 className="text-3xl font-bold tracking-tight">{title}</h1>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
<div className="space-y-2">
<h1 className="text-3xl font-bold tracking-tight">{title}</h1>
<p className="text-sm text-muted-foreground">{description}</p>
</div>
{children}
</div>
</div>
<div
className="hidden lg:block lg:flex-1 dither-lg bg-cover bg-center"
style={{ backgroundImage: "url(/images/background.jpg)" }}
/>
</div>
);
{children}
</div>
</div>
<div
className="hidden lg:block lg:flex-1 dither-lg bg-cover bg-center"
style={{ backgroundImage: "url(/images/background.jpg)" }}
/>
</div>
);
}

View File

@@ -15,84 +15,73 @@ import { AppSidebar } from "./app-sidebar";
export const clientMiddleware = [authMiddleware];
export async function clientLoader({ context }: Route.LoaderArgs) {
const ctx = context.get(appContext);
const ctx = context.get(appContext);
if (ctx.user && !ctx.user.hasDownloadedResticPassword) {
throw redirect("/download-recovery-key");
}
if (ctx.user && !ctx.user.hasDownloadedResticPassword) {
throw redirect("/download-recovery-key");
}
return ctx;
return ctx;
}
export default function Layout({ loaderData }: Route.ComponentProps) {
const navigate = useNavigate();
const navigate = useNavigate();
const logout = useMutation({
...logoutMutation(),
onSuccess: async () => {
navigate("/login", { replace: true });
},
onError: (error) => {
console.error(error);
toast.error("Logout failed", { description: error.message });
},
});
const logout = useMutation({
...logoutMutation(),
onSuccess: async () => {
navigate("/login", { replace: true });
},
onError: (error) => {
console.error(error);
toast.error("Logout failed", { description: error.message });
},
});
return (
<SidebarProvider defaultOpen={true}>
<AppSidebar />
<div className="w-full relative flex flex-col h-screen overflow-hidden">
<header className="z-50 bg-card-header border-b border-border/50 flex-shrink-0">
<div className="flex items-center justify-between py-3 sm:py-4 px-2 sm:px-8 mx-auto container">
<div className="flex items-center gap-4">
<SidebarTrigger />
<AppBreadcrumb />
</div>
{loaderData.user && (
<div className="flex items-center gap-4">
<span className="text-sm text-muted-foreground hidden md:inline-flex">
Welcome,&nbsp;
<span className="text-strong-accent">
{loaderData.user?.username}
</span>
</span>
<Button
variant="default"
size="sm"
onClick={() => logout.mutate({})}
loading={logout.isPending}
>
Logout
</Button>
<Button
variant="default"
size="sm"
className="relative overflow-hidden hidden lg:inline-flex"
>
<a
href="https://github.com/nicotsx/ironmount/issues/new"
target="_blank"
rel="noreferrer"
className="flex items-center gap-2"
>
<span className="flex items-center gap-2">
<LifeBuoy />
<span>Report an issue</span>
</span>
</a>
</Button>
</div>
)}
</div>
</header>
<div className="main-content flex-1 overflow-y-auto">
<GridBackground>
<main className="flex flex-col p-2 pb-6 pt-2 sm:p-8 sm:pt-6 mx-auto">
<Outlet />
</main>
</GridBackground>
</div>
</div>
</SidebarProvider>
);
return (
<SidebarProvider defaultOpen={true}>
<AppSidebar />
<div className="w-full relative flex flex-col h-screen overflow-hidden">
<header className="z-50 bg-card-header border-b border-border/50 flex-shrink-0">
<div className="flex items-center justify-between py-3 sm:py-4 px-2 sm:px-8 mx-auto container">
<div className="flex items-center gap-4">
<SidebarTrigger />
<AppBreadcrumb />
</div>
{loaderData.user && (
<div className="flex items-center gap-4">
<span className="text-sm text-muted-foreground hidden md:inline-flex">
Welcome,&nbsp;
<span className="text-strong-accent">{loaderData.user?.username}</span>
</span>
<Button variant="default" size="sm" onClick={() => logout.mutate({})} loading={logout.isPending}>
Logout
</Button>
<Button variant="default" size="sm" className="relative overflow-hidden hidden lg:inline-flex">
<a
href="https://github.com/nicotsx/ironmount/issues/new"
target="_blank"
rel="noreferrer"
className="flex items-center gap-2"
>
<span className="flex items-center gap-2">
<LifeBuoy />
<span>Report an issue</span>
</span>
</a>
</Button>
</div>
)}
</div>
</header>
<div className="main-content flex-1 overflow-y-auto">
<GridBackground>
<main className="flex flex-col p-2 pb-6 pt-2 sm:p-8 sm:pt-6 mx-auto">
<Outlet />
</main>
</GridBackground>
</div>
</div>
</SidebarProvider>
);
}

View File

@@ -2,36 +2,30 @@ import { cn } from "~/lib/utils";
import { Switch } from "./ui/switch";
type Props = {
isOn: boolean;
toggle: (v: boolean) => void;
enabledLabel: string;
disabledLabel: string;
disabled?: boolean;
isOn: boolean;
toggle: (v: boolean) => void;
enabledLabel: string;
disabledLabel: string;
disabled?: boolean;
};
export const OnOff = ({
isOn,
toggle,
enabledLabel,
disabledLabel,
disabled,
}: Props) => {
return (
<div
className={cn(
"flex items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-semibold uppercase tracking-wide transition-colors",
isOn
? "border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-500/40 dark:bg-emerald-500/10 dark:text-emerald-200"
: "border-muted bg-muted/40 text-muted-foreground dark:border-muted/60 dark:bg-muted/10"
)}
>
<span>{isOn ? enabledLabel : disabledLabel}</span>
<Switch
disabled={disabled}
checked={isOn}
onCheckedChange={toggle}
aria-label={isOn ? `Toggle ${enabledLabel}` : `Toggle ${disabledLabel}`}
/>
</div>
);
export const OnOff = ({ isOn, toggle, enabledLabel, disabledLabel, disabled }: Props) => {
return (
<div
className={cn(
"flex items-center gap-2 rounded-full border px-3 py-1.5 text-xs font-semibold uppercase tracking-wide transition-colors",
isOn
? "border-emerald-200 bg-emerald-50 text-emerald-700 dark:border-emerald-500/40 dark:bg-emerald-500/10 dark:text-emerald-200"
: "border-muted bg-muted/40 text-muted-foreground dark:border-muted/60 dark:bg-muted/10",
)}
>
<span>{isOn ? enabledLabel : disabledLabel}</span>
<Switch
disabled={disabled}
checked={isOn}
onCheckedChange={toggle}
aria-label={isOn ? `Toggle ${enabledLabel}` : `Toggle ${disabledLabel}`}
/>
</div>
);
};

View File

@@ -3,53 +3,50 @@ import { cn } from "~/lib/utils";
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
export const StatusDot = ({ status }: { status: VolumeStatus }) => {
const statusMapping = {
mounted: {
color: "bg-green-500",
colorLight: "bg-emerald-400",
animated: true,
},
unmounted: {
color: "bg-gray-500",
colorLight: "bg-gray-400",
animated: false,
},
error: {
color: "bg-red-500",
colorLight: "bg-amber-700",
animated: true,
},
unknown: {
color: "bg-yellow-500",
colorLight: "bg-yellow-400",
animated: true,
},
}[status];
const statusMapping = {
mounted: {
color: "bg-green-500",
colorLight: "bg-emerald-400",
animated: true,
},
unmounted: {
color: "bg-gray-500",
colorLight: "bg-gray-400",
animated: false,
},
error: {
color: "bg-red-500",
colorLight: "bg-amber-700",
animated: true,
},
unknown: {
color: "bg-yellow-500",
colorLight: "bg-yellow-400",
animated: true,
},
}[status];
return (
<Tooltip>
<TooltipTrigger>
<span className="relative flex size-3 mx-auto">
{statusMapping.animated && (
<span
className={cn(
"absolute inline-flex h-full w-full animate-ping rounded-full opacity-75",
`${statusMapping.colorLight}`
)}
/>
)}
<span
aria-label={status}
className={cn(
"relative inline-flex size-3 rounded-full",
`${statusMapping.color}`
)}
/>
</span>
</TooltipTrigger>
<TooltipContent>
<p className="capitalize">{status}</p>
</TooltipContent>
</Tooltip>
);
return (
<Tooltip>
<TooltipTrigger>
<span className="relative flex size-3 mx-auto">
{statusMapping.animated && (
<span
className={cn(
"absolute inline-flex h-full w-full animate-ping rounded-full opacity-75",
`${statusMapping.colorLight}`,
)}
/>
)}
<span
aria-label={status}
className={cn("relative inline-flex size-3 rounded-full", `${statusMapping.color}`)}
/>
</span>
</TooltipTrigger>
<TooltipContent>
<p className="capitalize">{status}</p>
</TooltipContent>
</Tooltip>
);
};