From e134d0e1d113349adf037a2b56a4cde9894e086b Mon Sep 17 00:00:00 2001 From: Nicolas Meienberger Date: Fri, 3 Oct 2025 23:54:53 +0200 Subject: [PATCH] ui: redesign --- apps/client/app/app.css | 12 +- .../app/components/create-volume-dialog.tsx | 12 +- apps/client/app/components/layout.tsx | 20 ++- apps/client/app/components/ui/button.tsx | 22 +-- apps/client/app/components/volume-icon.tsx | 4 +- .../details/components/storage-chart.tsx | 8 +- apps/client/app/routes/home.tsx | 170 ++++++++++-------- apps/client/app/routes/login.tsx | 5 +- apps/client/app/routes/onboarding.tsx | 5 +- 9 files changed, 137 insertions(+), 121 deletions(-) diff --git a/apps/client/app/app.css b/apps/client/app/app.css index ac3dee2..a087c57 100644 --- a/apps/client/app/app.css +++ b/apps/client/app/app.css @@ -11,7 +11,7 @@ html, body { - @apply bg-white dark:bg-[#0D0D0D]; + @apply bg-white dark:bg-[#131313]; overflow-x: hidden; width: 100%; position: relative; @@ -30,6 +30,7 @@ body { --color-foreground: var(--foreground); --color-card: var(--card); --color-card-foreground: var(--card-foreground); + --color-card-header: var(--card-header); --color-popover: var(--popover); --color-popover-foreground: var(--popover-foreground); --color-primary: var(--primary); @@ -57,6 +58,7 @@ body { --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); + --color-strong-accent: var(--strong-accent); } :root { @@ -65,6 +67,7 @@ body { --foreground: oklch(0.145 0 0); --card: oklch(1 0 0); --card-foreground: oklch(0.145 0 0); + --card-header: oklch(0.922 0 0); --popover: oklch(1 0 0); --popover-foreground: oklch(0.145 0 0); --primary: oklch(0.205 0 0); @@ -92,12 +95,14 @@ body { --sidebar-accent-foreground: oklch(0.205 0 0); --sidebar-border: oklch(0.922 0 0); --sidebar-ring: oklch(0.708 0 0); + --strong-accent: #ff543a; } .dark { - --background: oklch(0.145 0 0); + --background: #131313; --foreground: oklch(0.985 0 0); - --card: oklch(0.1448 0 0); + --card: #131313; + --card-header: #1b1b1b; /* --card: oklch(0.205 0 0); ORIGINAL */ --card-foreground: oklch(0.985 0 0); --popover: oklch(0.205 0 0); @@ -128,6 +133,7 @@ body { --sidebar-accent-foreground: oklch(0.985 0 0); --sidebar-border: oklch(1 0 0 / 10%); --sidebar-ring: oklch(0.556 0 0); + --strong-accent: #ff543a; } @layer base { diff --git a/apps/client/app/components/create-volume-dialog.tsx b/apps/client/app/components/create-volume-dialog.tsx index 42fb052..1e0dc49 100644 --- a/apps/client/app/components/create-volume-dialog.tsx +++ b/apps/client/app/components/create-volume-dialog.tsx @@ -6,15 +6,7 @@ import { createVolumeMutation } from "~/api-client/@tanstack/react-query.gen"; import { parseError } from "~/lib/errors"; import { CreateVolumeForm } from "./create-volume-form"; import { Button } from "./ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "./ui/dialog"; +import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, DialogTrigger } from "./ui/dialog"; import { ScrollArea } from "./ui/scroll-area"; type Props = { @@ -41,7 +33,7 @@ export const CreateVolumeDialog = ({ open, setOpen }: Props) => { return ( - diff --git a/apps/client/app/components/layout.tsx b/apps/client/app/components/layout.tsx index cf9152c..cfb4be0 100644 --- a/apps/client/app/components/layout.tsx +++ b/apps/client/app/components/layout.tsx @@ -1,13 +1,13 @@ import { useMutation } from "@tanstack/react-query"; import { Outlet, useNavigate } from "react-router"; import { toast } from "sonner"; +import { logoutMutation } from "~/api-client/@tanstack/react-query.gen"; +import { appContext } from "~/context"; import { cn } from "~/lib/utils"; +import { authMiddleware } from "~/middleware/auth"; +import type { Route } from "./+types/layout"; import { AppBreadcrumb } from "./app-breadcrumb"; import { Button } from "./ui/button"; -import { logoutMutation } from "~/api-client/@tanstack/react-query.gen"; -import type { Route } from "./+types/layout"; -import { appContext } from "~/context"; -import { authMiddleware } from "~/middleware/auth"; export const clientMiddleware = [authMiddleware]; @@ -39,19 +39,23 @@ export default function Layout({ loaderData }: Route.ComponentProps) { "dark:[background-image:linear-gradient(to_right,#262626_1px,transparent_1px),linear-gradient(to_bottom,#262626_1px,transparent_1px)]", )} > -
-
-
+
+
+
{loaderData.user && (
- Welcome, {loaderData.user?.username} + + Welcome, {loaderData.user?.username} +
)}
+
+
diff --git a/apps/client/app/components/ui/button.tsx b/apps/client/app/components/ui/button.tsx index 8acc771..b24de6e 100644 --- a/apps/client/app/components/ui/button.tsx +++ b/apps/client/app/components/ui/button.tsx @@ -6,23 +6,23 @@ import type * as React from "react"; import { cn } from "~/lib/utils"; const buttonVariants = cva( - "inline-flex cursor-pointer uppercase border items-center justify-center dark:border-white dark:bg-secondary dark:text-white gap-2 whitespace-nowrap text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + "inline-flex cursor-pointer uppercase rounded-sm items-center justify-center gap-2 whitespace-nowrap text-xs font-semibold tracking-wide transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-ring border-0", { variants: { variant: { - default: "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90", - destructive: - "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", - outline: - "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", - secondary: "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80", - ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + default: + "bg-[var(--strong-accent)] text-white hover:bg-[var(--strong-accent)]/90 focus-visible:ring-[var(--strong-accent)]/50", + destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/50", + outline: "border border-border bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-[#464646] text-white hover:bg-[#464646]/80 dark:bg-[#464646] dark:text-white dark:hover:bg-[#464646]/80", + ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, size: { - default: "h-9 px-4 py-2 has-[>svg]:px-3", - sm: "rounded-xs h-8 gap-1.5 px-3 has-[>svg]:px-2.5", - lg: "h-10 px-6 has-[>svg]:px-4", + default: "h-9 px-5 py-2 has-[>svg]:px-4", + sm: "h-8 px-3 py-1.5 has-[>svg]:px-2.5", + lg: "h-10 px-6 py-2.5 has-[>svg]:px-5", icon: "size-9", }, }, diff --git a/apps/client/app/components/volume-icon.tsx b/apps/client/app/components/volume-icon.tsx index 7f7015f..8a509f4 100644 --- a/apps/client/app/components/volume-icon.tsx +++ b/apps/client/app/components/volume-icon.tsx @@ -42,10 +42,10 @@ const getIconAndColor = (backend: BackendType) => { }; export const VolumeIcon = ({ backend, size = 10 }: VolumeIconProps) => { - const { icon: Icon, color, label } = getIconAndColor(backend); + const { icon: Icon, label } = getIconAndColor(backend); return ( - + {label} diff --git a/apps/client/app/modules/details/components/storage-chart.tsx b/apps/client/app/modules/details/components/storage-chart.tsx index 7a60a95..bacdfc3 100644 --- a/apps/client/app/modules/details/components/storage-chart.tsx +++ b/apps/client/app/modules/details/components/storage-chart.tsx @@ -18,7 +18,7 @@ export function StorageChart({ statfs }: Props) { { name: "Used", value: statfs.used, - fill: "#2B7EFF", + fill: "#ff543a", }, { name: "Free", @@ -63,7 +63,7 @@ export function StorageChart({ statfs }: Props) { -
+
-
+
-
+
Used Space
diff --git a/apps/client/app/routes/home.tsx b/apps/client/app/routes/home.tsx index 6a169c1..d701717 100644 --- a/apps/client/app/routes/home.tsx +++ b/apps/client/app/routes/home.tsx @@ -9,9 +9,10 @@ import { StatusDot } from "~/components/status-dot"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select"; -import { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/components/ui/table"; import { VolumeIcon } from "~/components/volume-icon"; import type { Route } from "./+types/home"; +import { Card, CardHeader } from "~/components/ui/card"; export function meta(_: Route.MetaArgs) { return [ @@ -60,85 +61,96 @@ export default function Home({ loaderData }: Route.ComponentProps) { return ( <> -

Ironmount

-

- Create, manage, monitor, and automate your volumes with ease. -

-
- - setSearchQuery(e.target.value)} - /> - - - {(searchQuery || statusFilter || backendFilter) && ( - - )} - - -
-
- - A list of your managed volumes. - - - Name - Backend - Mountpoint - Status - - - - {filteredVolumes.map((volume) => ( - navigate(`/volumes/${volume.name}`)} - > - {volume.name} - - - - - - - {volume.path} - - - - - - - + {/*

Ironmount

*/} + {/*

*/} + {/* Create, manage, monitor, and automate your volumes with ease. */} + {/*

*/} + +
+ + setSearchQuery(e.target.value)} + /> + + + {(searchQuery || statusFilter || backendFilter) && ( + + )} + + +
+
+
+ + + Name + Backend + Mountpoint + Status - ))} - -
-
+ + + {filteredVolumes.map((volume) => ( + navigate(`/volumes/${volume.name}`)} + > + {volume.name} + + + + + + + {volume.path} + + + + + + + + + ))} + + +
+
+ {filteredVolumes.length === 0 ? ( + "No volumes found." + ) : ( + + {filteredVolumes.length} volume + {filteredVolumes.length > 1 ? "s" : ""} + + )} +
+ ); } diff --git a/apps/client/app/routes/login.tsx b/apps/client/app/routes/login.tsx index a414fee..3477d45 100644 --- a/apps/client/app/routes/login.tsx +++ b/apps/client/app/routes/login.tsx @@ -49,8 +49,9 @@ export default function LoginPage() { }; return ( -
- +
+
+ Welcome Back Sign in to your account diff --git a/apps/client/app/routes/onboarding.tsx b/apps/client/app/routes/onboarding.tsx index f0e32b2..511e37e 100644 --- a/apps/client/app/routes/onboarding.tsx +++ b/apps/client/app/routes/onboarding.tsx @@ -60,8 +60,9 @@ export default function OnboardingPage() { }; return ( -
- +
+
+ Welcome to Ironmount Create the admin user to get started