diff --git a/.oxlintrc.json b/.oxlintrc.json index ae1c8af2..f2e5a583 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -114,6 +114,7 @@ "unicorn/prefer-set-size": "warn", "unicorn/prefer-string-starts-ends-with": "warn", "import/no-cycle": "error", + "import/no-unused-modules": ["error", { "unusedExports": true }], "eslint/no-console": ["warn", { "allow": ["warn", "error", "info"] }] }, "settings": { diff --git a/app/app.css b/app/app.css index 15a1e65c..15f07d91 100644 --- a/app/app.css +++ b/app/app.css @@ -37,7 +37,6 @@ body { } body { - @apply bg-[#131313]; min-height: 100dvh; } @@ -67,6 +66,8 @@ body { --color-accent: var(--accent); --color-accent-foreground: var(--accent-foreground); --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-success: var(--success); --color-border: var(--border); --color-input: var(--input); --color-ring: var(--ring); @@ -88,38 +89,40 @@ body { :root { --radius: 0.625rem; - --background: oklch(1 0 0); - --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); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); + --background: oklch(0.975 0.005 80); + --foreground: oklch(0.25 0.015 55); + --card: oklch(0.988 0.004 80); + --card-foreground: oklch(0.25 0.015 55); + --card-header: oklch(0.945 0.008 80); + --popover: oklch(0.988 0.004 80); + --popover-foreground: oklch(0.25 0.015 55); + --primary: oklch(0.25 0.015 55); + --primary-foreground: oklch(0.975 0.005 80); + --secondary: oklch(0.935 0.008 70); + --secondary-foreground: oklch(0.25 0.015 55); + --muted: oklch(0.935 0.008 70); + --muted-foreground: oklch(0.58 0.025 60); + --accent: oklch(0.935 0.008 70); + --accent-foreground: oklch(0.25 0.015 55); --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); + --destructive-foreground: #fff; + --success: oklch(0.57 0.16 148); + --border: oklch(0.9 0.01 70); + --input: oklch(0.9 0.01 70); + --ring: oklch(0.7 0.015 60); --chart-1: oklch(0.646 0.222 41.116); --chart-2: oklch(0.6 0.118 184.704); --chart-3: oklch(0.398 0.07 227.392); --chart-4: oklch(0.828 0.189 84.429); --chart-5: oklch(0.769 0.188 70.08); - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); + --sidebar: oklch(0.96 0.007 75); + --sidebar-foreground: oklch(0.25 0.015 55); + --sidebar-primary: oklch(0.25 0.015 55); + --sidebar-primary-foreground: oklch(0.975 0.005 80); + --sidebar-accent: oklch(0.935 0.008 70); + --sidebar-accent-foreground: oklch(0.25 0.015 55); + --sidebar-border: oklch(0.9 0.01 70); + --sidebar-ring: oklch(0.7 0.015 60); --strong-accent: #ff543a; } @@ -144,6 +147,8 @@ body { --accent: oklch(0.269 0 0); --accent-foreground: oklch(0.985 0 0); --destructive: #ff543a; + --destructive-foreground: #fff; + --success: oklch(0.696 0.17 162.48); --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); --ring: oklch(0.556 0 0); @@ -190,6 +195,17 @@ body { } } +.snapshot-scrollable::-webkit-scrollbar { + height: 6px; +} +.snapshot-scrollable::-webkit-scrollbar-track { + background: transparent; +} +.snapshot-scrollable::-webkit-scrollbar-thumb { + background: var(--border); + border-radius: 999px; +} + /* Hide built-in password reveal/clear controls (notably in Edge on Windows) */ [data-secret-input] input[type="password"]::-ms-reveal, [data-secret-input] input[type="password"]::-ms-clear { diff --git a/app/client/components/app-sidebar.tsx b/app/client/components/app-sidebar.tsx index 5bb686ec..e59b397c 100644 --- a/app/client/components/app-sidebar.tsx +++ b/app/client/components/app-sidebar.tsx @@ -115,7 +115,7 @@ export function AppSidebar({ isInstanceAdmin }: Props) { /> @@ -171,7 +171,7 @@ export function AppSidebar({ isInstanceAdmin }: Props) { /> diff --git a/app/client/components/cron-input.tsx b/app/client/components/cron-input.tsx index c7e85e06..35e387ed 100644 --- a/app/client/components/cron-input.tsx +++ b/app/client/components/cron-input.tsx @@ -59,7 +59,7 @@ export function CronInput({ value, onChange, error }: CronInputProps) { {value && (
{isValid ? ( - + ) : ( )} diff --git a/app/client/components/grid-background.tsx b/app/client/components/grid-background.tsx index ec417f3a..0eadcb29 100644 --- a/app/client/components/grid-background.tsx +++ b/app/client/components/grid-background.tsx @@ -14,7 +14,7 @@ export function GridBackground({ children, className, containerClassName }: Grid className={cn( "pointer-events-none absolute inset-0 w-full h-full", "bg-[size:40px_40px]", - "bg-[linear-gradient(to_right,#e4e4e7_1px,transparent_1px),linear-gradient(to_bottom,#e4e4e7_1px,transparent_1px)]", + "bg-[linear-gradient(to_right,#EAE5DF_1px,transparent_1px),linear-gradient(to_bottom,#EAE5DF_1px,transparent_1px)]", "dark:bg-[linear-gradient(to_right,#262626_1px,transparent_1px),linear-gradient(to_bottom,#262626_1px,transparent_1px)]", "[mask-image:radial-gradient(ellipse_at_top,black_70%,transparent_100%)]", )} diff --git a/app/client/components/layout.tsx b/app/client/components/layout.tsx index 82600947..8b16ff22 100644 --- a/app/client/components/layout.tsx +++ b/app/client/components/layout.tsx @@ -10,6 +10,7 @@ import { DevPanelListener } from "./dev-panel-listener"; import { Outlet, useNavigate } from "@tanstack/react-router"; import { AppBreadcrumb } from "./app-breadcrumb"; import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip"; +import { ThemeToggle } from "./theme-toggle"; type Props = { loaderData: AppContext; @@ -46,12 +47,13 @@ export function Layout({ loaderData }: Props) { {loaderData.user.name} + + + Toggle theme + + ); +} diff --git a/app/client/components/ui/button.tsx b/app/client/components/ui/button.tsx index 9f2411b3..6908fba4 100644 --- a/app/client/components/ui/button.tsx +++ b/app/client/components/ui/button.tsx @@ -11,13 +11,12 @@ const buttonVariants = cva( { variants: { variant: { - default: - "bg-transparent text-white hover:bg-[#3A3A3A]/80 border hover:border-white/40 dark:text-white dark:hover:bg-[#3A3A3A]/80", + default: "bg-transparent text-foreground hover:bg-muted border hover:border-border", primary: "bg-strong-accent text-white hover:bg-strong-accent/90 focus-visible:ring-strong-accent/50", destructive: - "border border-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/50 text-destructive hover:text-white", + "border border-destructive text-destructive hover:bg-destructive hover:text-destructive-foreground focus-visible:ring-destructive/50", outline: "border border-border bg-background hover:bg-accent hover:text-accent-foreground", - secondary: "bg-transparent text-white hover:bg-[#3A3A3A]/80 border dark:text-white dark:hover:bg-[#3A3A3A]/80", + secondary: "bg-transparent text-foreground hover:bg-muted border", ghost: "hover:bg-accent hover:text-accent-foreground", link: "text-primary underline-offset-4 hover:underline", }, diff --git a/app/client/components/ui/card.tsx b/app/client/components/ui/card.tsx index 00aa1e72..ed6393a7 100644 --- a/app/client/components/ui/card.tsx +++ b/app/client/components/ui/card.tsx @@ -7,7 +7,7 @@ function Card({ className, children, interactive, ...props }: React.ComponentPro
- - - - - - - - + + + + + + + + {children}
diff --git a/app/client/components/ui/code-block.tsx b/app/client/components/ui/code-block.tsx index c8529a0d..9be46dab 100644 --- a/app/client/components/ui/code-block.tsx +++ b/app/client/components/ui/code-block.tsx @@ -8,8 +8,8 @@ interface CodeBlockProps { export const CodeBlock: React.FC = ({ code, filename }) => { return ( -
-
+
+
@@ -18,7 +18,7 @@ export const CodeBlock: React.FC = ({ code, filename }) => {
-				{code}
+				{code}
 			
); diff --git a/app/client/components/ui/sonner.tsx b/app/client/components/ui/sonner.tsx index 30810352..abcd1c27 100644 --- a/app/client/components/ui/sonner.tsx +++ b/app/client/components/ui/sonner.tsx @@ -1,4 +1,4 @@ -import { useTheme } from "next-themes"; +import { useTheme } from "~/client/components/theme-provider"; import { Toaster as Sonner, type ToasterProps } from "sonner"; const Toaster = ({ ...props }: ToasterProps) => { diff --git a/app/client/components/ui/tabs.tsx b/app/client/components/ui/tabs.tsx index cab7b27a..e965b0e8 100644 --- a/app/client/components/ui/tabs.tsx +++ b/app/client/components/ui/tabs.tsx @@ -37,11 +37,11 @@ function TabsTrigger({ className, onClick, ...props }: React.ComponentProps {props.children} {/* Left bracket - bottom tick */} - + {/* Right bracket - top tick */} - + {/* Right bracket - vertical line */} - + {/* Right bracket - bottom tick */} - + ); } diff --git a/app/client/modules/backups/components/backup-card.tsx b/app/client/modules/backups/components/backup-card.tsx index 2bf7b337..16f529c8 100644 --- a/app/client/modules/backups/components/backup-card.tsx +++ b/app/client/modules/backups/components/backup-card.tsx @@ -32,21 +32,21 @@ export const BackupCard = ({ schedule }: { schedule: BackupSchedule }) => {
- Schedule -
- + Schedule +
+ {schedule.cronExpression}
- Last backup -
- {formatTimeAgo(schedule.lastBackupAt)} + Last backup +
+ {formatTimeAgo(schedule.lastBackupAt)}
- Next backup -
- + Next backup +
+ {formatShortDateTime(schedule.nextBackupAt)}
diff --git a/app/client/modules/backups/components/snapshot-timeline.tsx b/app/client/modules/backups/components/snapshot-timeline.tsx index 11581e96..cb4b5286 100644 --- a/app/client/modules/backups/components/snapshot-timeline.tsx +++ b/app/client/modules/backups/components/snapshot-timeline.tsx @@ -1,3 +1,4 @@ +import { useRef } from "react"; import type { ListSnapshotsResponse } from "~/client/api-client"; import { ByteSize } from "~/client/components/bytes-size"; import { Card, CardContent } from "~/client/components/ui/card"; @@ -15,6 +16,7 @@ interface Props { export const SnapshotTimeline = (props: Props) => { const { snapshots, snapshotId, loading, onSnapshotSelect, error } = props; + const selectedRef = useRef(null); if (error) { return ( @@ -51,13 +53,14 @@ export const SnapshotTimeline = (props: Props) => {
-
+
{snapshots.map((snapshot) => { const date = new Date(snapshot.time); const isSelected = snapshotId === snapshot.short_id; return (