From e77723164b6522136a824b001d644de8b795b3ca Mon Sep 17 00:00:00 2001 From: Nico <47644445+nicotsx@users.noreply.github.com> Date: Thu, 2 Apr 2026 21:44:32 +0200 Subject: [PATCH] refactor: improve buttons and space with dropdowns (#738) --- .../backups/components/schedule-summary.tsx | 85 ++++++------ .../routes/notification-details.tsx | 36 ++++-- .../repositories/routes/repositories.tsx | 20 ++- app/client/modules/repositories/tabs/info.tsx | 122 ++++++++++-------- app/client/modules/volumes/tabs/info.tsx | 85 ++++++------ 5 files changed, 190 insertions(+), 158 deletions(-) diff --git a/app/client/modules/backups/components/schedule-summary.tsx b/app/client/modules/backups/components/schedule-summary.tsx index 80b7d000..fb1adfbd 100644 --- a/app/client/modules/backups/components/schedule-summary.tsx +++ b/app/client/modules/backups/components/schedule-summary.tsx @@ -1,4 +1,4 @@ -import { Check, Database, Eraser, HardDrive, Pencil, Play, Square, Trash2, X } from "lucide-react"; +import { Check, ChevronDown, Database, Eraser, HardDrive, Pencil, Play, Square, Trash2, X } from "lucide-react"; import { useMemo, useState } from "react"; import { OnOff } from "~/client/components/onoff"; import { Button } from "~/client/components/ui/button"; @@ -12,6 +12,13 @@ import { AlertDialogHeader, AlertDialogTitle, } from "~/client/components/ui/alert-dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "~/client/components/ui/dropdown-menu"; import type { BackupSchedule } from "~/client/lib/types"; import { BackupProgressCard } from "./backup-progress-card"; import { getBackupProgressOptions, runForgetMutation } from "~/client/api-client/@tanstack/react-query.gen"; @@ -46,9 +53,6 @@ export const ScheduleSummary = (props: Props) => { const runForget = useMutation({ ...runForgetMutation(), - onSuccess: () => { - toast.success("Retention policy applied successfully"); - }, onError: (error) => { handleRepositoryError("Failed to apply retention policy", error, schedule.repository.shortId); }, @@ -83,7 +87,10 @@ export const ScheduleSummary = (props: Props) => { const handleConfirmForget = () => { setShowForgetConfirm(false); - runForget.mutate({ path: { shortId: schedule.shortId } }); + toast.promise(runForget.mutateAsync({ path: { shortId: schedule.shortId } }), { + loading: "Running cleanup...", + success: "Retention policy applied successfully", + }); }; const handleConfirmStop = () => { @@ -132,53 +139,45 @@ export const ScheduleSummary = (props: Props) => { /> -
+
{schedule.lastBackupStatus === "in_progress" ? ( - ) : ( - )} - {schedule.retentionPolicy && ( - - )} - - + + + + + + {schedule.retentionPolicy && ( + setShowForgetConfirm(true)} disabled={runForget.isPending}> + + Run cleanup + + )} + navigate({ to: "/backups/$backupId/edit", params: { backupId: schedule.shortId } })} + > + + Edit schedule + + + setShowDeleteConfirm(true)}> + + Delete + + +
diff --git a/app/client/modules/notifications/routes/notification-details.tsx b/app/client/modules/notifications/routes/notification-details.tsx index 303cea4c..ca9d0222 100644 --- a/app/client/modules/notifications/routes/notification-details.tsx +++ b/app/client/modules/notifications/routes/notification-details.tsx @@ -18,10 +18,16 @@ import { AlertDialogHeader, AlertDialogTitle, } from "~/client/components/ui/alert-dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "~/client/components/ui/dropdown-menu"; import { parseError } from "~/client/lib/errors"; import { cn } from "~/client/lib/utils"; import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card"; -import { Bell, Save, TestTube2, Trash2 } from "lucide-react"; +import { Bell, ChevronDown, Save, TestTube2, Trash2 } from "lucide-react"; import { Alert, AlertDescription } from "~/client/components/ui/alert"; import { CreateNotificationForm, type NotificationFormValues } from "../components/create-notification-form"; import { useNavigate } from "@tanstack/react-router"; @@ -105,7 +111,7 @@ export function NotificationDetailsPage({ notificationId }: { notificationId: st {data.type}
-
+
- + + + + + + setShowDeleteConfirm(true)} + disabled={deleteDestination.isPending} + > + + Delete + + +
diff --git a/app/client/modules/repositories/routes/repositories.tsx b/app/client/modules/repositories/routes/repositories.tsx index eeed85c8..c7f65534 100644 --- a/app/client/modules/repositories/routes/repositories.tsx +++ b/app/client/modules/repositories/routes/repositories.tsx @@ -9,6 +9,7 @@ import { Input } from "~/client/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/client/components/ui/table"; import { cn } from "~/client/lib/utils"; +import { StatusDot } from "~/client/components/status-dot"; import { EmptyState } from "~/client/components/empty-state"; import { useNavigate } from "@tanstack/react-router"; @@ -143,17 +144,14 @@ export function RepositoriesPage() { - - {repository.status || "unknown"} - + ))} diff --git a/app/client/modules/repositories/tabs/info.tsx b/app/client/modules/repositories/tabs/info.tsx index fdaae655..12c23bec 100644 --- a/app/client/modules/repositories/tabs/info.tsx +++ b/app/client/modules/repositories/tabs/info.tsx @@ -1,7 +1,7 @@ import { useMutation } from "@tanstack/react-query"; import { useState } from "react"; import { toast } from "sonner"; -import { Pencil, Square, Stethoscope, Trash2, Unlock } from "lucide-react"; +import { ChevronDown, Pencil, Square, Stethoscope, Trash2, Unlock } from "lucide-react"; import { Card, CardContent, CardTitle } from "~/client/components/ui/card"; import { Button } from "~/client/components/ui/button"; import { @@ -13,6 +13,13 @@ import { AlertDialogHeader, AlertDialogTitle, } from "~/client/components/ui/alert-dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "~/client/components/ui/dropdown-menu"; import type { Repository } from "~/client/lib/types"; import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen"; import { @@ -84,14 +91,6 @@ export const RepositoryInfoTabContent = ({ repository, initialStats }: Props) => const unlockRepo = useMutation({ ...unlockRepositoryMutation(), - onSuccess: () => { - toast.success("Repository unlocked successfully"); - }, - onError: (error) => { - toast.error("Failed to unlock repository", { - description: parseError(error)?.message, - }); - }, }); const handleConfirmDelete = () => { @@ -117,53 +116,64 @@ export const RepositoryInfoTabContent = ({ repository, initialStats }: Props) => {repository.provisioningId && } -
- - - - - +
+ {isDoctorRunning ? ( + + ) : ( + + )} + + + + + + navigate({ to: `/repositories/${repository.shortId}/edit` })}> + + Edit + + + toast.promise(unlockRepo.mutateAsync({ path: { shortId: repository.shortId } }), { + loading: "Unlocking repo", + success: "Repository unlocked successfully", + error: (e) => parseError(e)?.message || "Failed to unlock repository", + }) + } + disabled={unlockRepo.isPending} + > + + Unlock + + + setShowDeleteConfirm(true)} + disabled={deleteRepo.isPending} + > + + Delete + + +
diff --git a/app/client/modules/volumes/tabs/info.tsx b/app/client/modules/volumes/tabs/info.tsx index e9656d75..3b48d123 100644 --- a/app/client/modules/volumes/tabs/info.tsx +++ b/app/client/modules/volumes/tabs/info.tsx @@ -1,7 +1,7 @@ import { useMutation } from "@tanstack/react-query"; import { useState } from "react"; import { toast } from "sonner"; -import { Pencil, Plug, Trash2, Unplug } from "lucide-react"; +import { ChevronDown, Pencil, Plug, Trash2, Unplug } from "lucide-react"; import { CreateVolumeForm } from "~/client/modules/volumes/components/create-volume-form"; import { AlertDialog, @@ -15,6 +15,13 @@ import { } from "~/client/components/ui/alert-dialog"; import { Button } from "~/client/components/ui/button"; import { Card } from "~/client/components/ui/card"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "~/client/components/ui/dropdown-menu"; import type { StatFs, Volume } from "~/client/lib/types"; import { HealthchecksCard } from "../components/healthchecks-card"; import { StorageChart } from "../components/storage-chart"; @@ -37,26 +44,10 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => { const mountVol = useMutation({ ...mountVolumeMutation(), - onSuccess: () => { - toast.success("Volume mounted successfully"); - }, - onError: (error) => { - toast.error("Failed to mount volume", { - description: parseError(error)?.message, - }); - }, }); const unmountVol = useMutation({ ...unmountVolumeMutation(), - onSuccess: () => { - toast.success("Volume unmounted successfully"); - }, - onError: (error) => { - toast.error("Failed to unmount volume", { - description: parseError(error)?.message, - }); - }, }); const deleteVol = useMutation({ @@ -89,23 +80,21 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
- Volume Configuration +

Volume Configuration

{volume.provisioningId && }
-
- +
{volume.status !== "mounted" ? ( )} - + + + + + + navigate({ to: `/volumes/${volume.shortId}/edit` })}> + + Edit + + + setShowDeleteConfirm(true)} + disabled={deleteVol.isPending} + > + + Delete + + +