refactor: improve buttons and space with dropdowns (#738)
This commit is contained in:
parent
4bf1463406
commit
e77723164b
5 changed files with 190 additions and 158 deletions
|
|
@ -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 { useMemo, useState } from "react";
|
||||||
import { OnOff } from "~/client/components/onoff";
|
import { OnOff } from "~/client/components/onoff";
|
||||||
import { Button } from "~/client/components/ui/button";
|
import { Button } from "~/client/components/ui/button";
|
||||||
|
|
@ -12,6 +12,13 @@ import {
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "~/client/components/ui/alert-dialog";
|
} 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 type { BackupSchedule } from "~/client/lib/types";
|
||||||
import { BackupProgressCard } from "./backup-progress-card";
|
import { BackupProgressCard } from "./backup-progress-card";
|
||||||
import { getBackupProgressOptions, runForgetMutation } from "~/client/api-client/@tanstack/react-query.gen";
|
import { getBackupProgressOptions, runForgetMutation } from "~/client/api-client/@tanstack/react-query.gen";
|
||||||
|
|
@ -46,9 +53,6 @@ export const ScheduleSummary = (props: Props) => {
|
||||||
|
|
||||||
const runForget = useMutation({
|
const runForget = useMutation({
|
||||||
...runForgetMutation(),
|
...runForgetMutation(),
|
||||||
onSuccess: () => {
|
|
||||||
toast.success("Retention policy applied successfully");
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
handleRepositoryError("Failed to apply retention policy", error, schedule.repository.shortId);
|
handleRepositoryError("Failed to apply retention policy", error, schedule.repository.shortId);
|
||||||
},
|
},
|
||||||
|
|
@ -83,7 +87,10 @@ export const ScheduleSummary = (props: Props) => {
|
||||||
|
|
||||||
const handleConfirmForget = () => {
|
const handleConfirmForget = () => {
|
||||||
setShowForgetConfirm(false);
|
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 = () => {
|
const handleConfirmStop = () => {
|
||||||
|
|
@ -132,53 +139,45 @@ export const ScheduleSummary = (props: Props) => {
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col @wide:flex-row gap-2">
|
<div className="flex items-center gap-2">
|
||||||
{schedule.lastBackupStatus === "in_progress" ? (
|
{schedule.lastBackupStatus === "in_progress" ? (
|
||||||
<Button
|
<Button variant="destructive" size="sm" onClick={() => setShowStopConfirm(true)}>
|
||||||
variant="destructive"
|
|
||||||
size="sm"
|
|
||||||
onClick={() => setShowStopConfirm(true)}
|
|
||||||
className="w-full @medium:w-auto"
|
|
||||||
>
|
|
||||||
<Square className="h-4 w-4 mr-2" />
|
<Square className="h-4 w-4 mr-2" />
|
||||||
<span>Stop backup</span>
|
<span>Stop backup</span>
|
||||||
</Button>
|
</Button>
|
||||||
) : (
|
) : (
|
||||||
<Button variant="default" size="sm" onClick={handleRunBackupNow} className="w-full @medium:w-auto">
|
<Button variant="default" size="sm" onClick={handleRunBackupNow}>
|
||||||
<Play className="h-4 w-4 mr-2" />
|
<Play className="h-4 w-4 mr-2" />
|
||||||
<span>Backup now</span>
|
<span>Backup now</span>
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{schedule.retentionPolicy && (
|
<DropdownMenu>
|
||||||
<Button
|
<DropdownMenuTrigger asChild>
|
||||||
variant="outline"
|
<Button variant="outline" size="sm">
|
||||||
size="sm"
|
Actions
|
||||||
loading={runForget.isPending}
|
<ChevronDown className="h-4 w-4 ml-1" />
|
||||||
onClick={() => setShowForgetConfirm(true)}
|
</Button>
|
||||||
className="w-full @medium:w-auto"
|
</DropdownMenuTrigger>
|
||||||
>
|
<DropdownMenuContent align="end">
|
||||||
<Eraser className="h-4 w-4 mr-2" />
|
{schedule.retentionPolicy && (
|
||||||
<span>Run cleanup</span>
|
<DropdownMenuItem onClick={() => setShowForgetConfirm(true)} disabled={runForget.isPending}>
|
||||||
</Button>
|
<Eraser />
|
||||||
)}
|
Run cleanup
|
||||||
<Button
|
</DropdownMenuItem>
|
||||||
variant="outline"
|
)}
|
||||||
size="sm"
|
<DropdownMenuItem
|
||||||
onClick={() => navigate({ to: "/backups/$backupId/edit", params: { backupId: schedule.shortId } })}
|
onClick={() => navigate({ to: "/backups/$backupId/edit", params: { backupId: schedule.shortId } })}
|
||||||
className="w-full @medium:w-auto"
|
>
|
||||||
>
|
<Pencil />
|
||||||
<Pencil className="h-4 w-4 mr-2" />
|
Edit schedule
|
||||||
<span>Edit schedule</span>
|
</DropdownMenuItem>
|
||||||
</Button>
|
<DropdownMenuSeparator />
|
||||||
<Button
|
<DropdownMenuItem variant="destructive" onClick={() => setShowDeleteConfirm(true)}>
|
||||||
variant="outline"
|
<Trash2 />
|
||||||
size="sm"
|
Delete
|
||||||
onClick={() => setShowDeleteConfirm(true)}
|
</DropdownMenuItem>
|
||||||
className="text-destructive hover:text-destructive w-full @medium:w-auto"
|
</DropdownMenuContent>
|
||||||
>
|
</DropdownMenu>
|
||||||
<Trash2 className="h-4 w-4 mr-2" />
|
|
||||||
<span>Delete</span>
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="grid gap-4 grid-cols-1 @medium:grid-cols-2 @wide:grid-cols-4">
|
<CardContent className="grid gap-4 grid-cols-1 @medium:grid-cols-2 @wide:grid-cols-4">
|
||||||
|
|
|
||||||
|
|
@ -18,10 +18,16 @@ import {
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "~/client/components/ui/alert-dialog";
|
} from "~/client/components/ui/alert-dialog";
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "~/client/components/ui/dropdown-menu";
|
||||||
import { parseError } from "~/client/lib/errors";
|
import { parseError } from "~/client/lib/errors";
|
||||||
import { cn } from "~/client/lib/utils";
|
import { cn } from "~/client/lib/utils";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card";
|
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 { Alert, AlertDescription } from "~/client/components/ui/alert";
|
||||||
import { CreateNotificationForm, type NotificationFormValues } from "../components/create-notification-form";
|
import { CreateNotificationForm, type NotificationFormValues } from "../components/create-notification-form";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
|
@ -105,7 +111,7 @@ export function NotificationDetailsPage({ notificationId }: { notificationId: st
|
||||||
</span>
|
</span>
|
||||||
<span className="text-xs bg-primary/10 rounded-md px-2 py-1 capitalize">{data.type}</span>
|
<span className="text-xs bg-primary/10 rounded-md px-2 py-1 capitalize">{data.type}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
<Button
|
||||||
onClick={handleTest}
|
onClick={handleTest}
|
||||||
disabled={testDestination.isPending || !data.enabled}
|
disabled={testDestination.isPending || !data.enabled}
|
||||||
|
|
@ -115,14 +121,24 @@ export function NotificationDetailsPage({ notificationId }: { notificationId: st
|
||||||
<TestTube2 className="h-4 w-4 mr-2" />
|
<TestTube2 className="h-4 w-4 mr-2" />
|
||||||
Test
|
Test
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<DropdownMenu>
|
||||||
onClick={() => setShowDeleteConfirm(true)}
|
<DropdownMenuTrigger asChild>
|
||||||
variant="destructive"
|
<Button variant="outline">
|
||||||
loading={deleteDestination.isPending}
|
Actions
|
||||||
>
|
<ChevronDown className="h-4 w-4 ml-1" />
|
||||||
<Trash2 className="h-4 w-4 mr-2" />
|
</Button>
|
||||||
Delete
|
</DropdownMenuTrigger>
|
||||||
</Button>
|
<DropdownMenuContent align="end">
|
||||||
|
<DropdownMenuItem
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => setShowDeleteConfirm(true)}
|
||||||
|
disabled={deleteDestination.isPending}
|
||||||
|
>
|
||||||
|
<Trash2 />
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { Input } from "~/client/components/ui/input";
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/client/components/ui/select";
|
||||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/client/components/ui/table";
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "~/client/components/ui/table";
|
||||||
import { cn } from "~/client/lib/utils";
|
import { cn } from "~/client/lib/utils";
|
||||||
|
import { StatusDot } from "~/client/components/status-dot";
|
||||||
import { EmptyState } from "~/client/components/empty-state";
|
import { EmptyState } from "~/client/components/empty-state";
|
||||||
import { useNavigate } from "@tanstack/react-router";
|
import { useNavigate } from "@tanstack/react-router";
|
||||||
|
|
||||||
|
|
@ -143,17 +144,14 @@ export function RepositoriesPage() {
|
||||||
</span>
|
</span>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell className="text-center">
|
<TableCell className="text-center">
|
||||||
<span
|
<StatusDot
|
||||||
className={cn(
|
variant={
|
||||||
"inline-flex items-center gap-2 px-2 py-1 rounded-md text-xs bg-gray-500/10 text-gray-500",
|
repository.status === "healthy" ? "success" : repository.status === "error" ? "error" : "warning"
|
||||||
{
|
}
|
||||||
"bg-success/10 text-success": repository.status === "healthy",
|
label={
|
||||||
"bg-red-500/10 text-red-500": repository.status === "error",
|
repository.status ? repository.status[0].toUpperCase() + repository.status.slice(1) : "Unknown"
|
||||||
},
|
}
|
||||||
)}
|
/>
|
||||||
>
|
|
||||||
{repository.status || "unknown"}
|
|
||||||
</span>
|
|
||||||
</TableCell>
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
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 { Card, CardContent, CardTitle } from "~/client/components/ui/card";
|
||||||
import { Button } from "~/client/components/ui/button";
|
import { Button } from "~/client/components/ui/button";
|
||||||
import {
|
import {
|
||||||
|
|
@ -13,6 +13,13 @@ import {
|
||||||
AlertDialogHeader,
|
AlertDialogHeader,
|
||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
} from "~/client/components/ui/alert-dialog";
|
} 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 { Repository } from "~/client/lib/types";
|
||||||
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
import type { GetRepositoryStatsResponse } from "~/client/api-client/types.gen";
|
||||||
import {
|
import {
|
||||||
|
|
@ -84,14 +91,6 @@ export const RepositoryInfoTabContent = ({ repository, initialStats }: Props) =>
|
||||||
|
|
||||||
const unlockRepo = useMutation({
|
const unlockRepo = useMutation({
|
||||||
...unlockRepositoryMutation(),
|
...unlockRepositoryMutation(),
|
||||||
onSuccess: () => {
|
|
||||||
toast.success("Repository unlocked successfully");
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
toast.error("Failed to unlock repository", {
|
|
||||||
description: parseError(error)?.message,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleConfirmDelete = () => {
|
const handleConfirmDelete = () => {
|
||||||
|
|
@ -117,53 +116,64 @@ export const RepositoryInfoTabContent = ({ repository, initialStats }: Props) =>
|
||||||
{repository.provisioningId && <ManagedBadge />}
|
{repository.provisioningId && <ManagedBadge />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-wrap items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
{isDoctorRunning ? (
|
||||||
type="button"
|
<Button
|
||||||
variant="outline"
|
type="button"
|
||||||
onClick={() => navigate({ to: `/repositories/${repository.shortId}/edit` })}
|
variant="destructive"
|
||||||
>
|
loading={cancelDoctor.isPending}
|
||||||
<Pencil className="h-4 w-4 mr-2" />
|
onClick={() => cancelDoctor.mutate({ path: { shortId: repository.shortId } })}
|
||||||
Edit
|
>
|
||||||
</Button>
|
<Square className="h-4 w-4 mr-2" />
|
||||||
<Button
|
Cancel doctor
|
||||||
type="button"
|
</Button>
|
||||||
variant="destructive"
|
) : (
|
||||||
className={cn({ hidden: !isDoctorRunning })}
|
<Button
|
||||||
loading={cancelDoctor.isPending}
|
type="button"
|
||||||
onClick={() => cancelDoctor.mutate({ path: { shortId: repository.shortId } })}
|
variant="outline"
|
||||||
>
|
onClick={() => startDoctor.mutate({ path: { shortId: repository.shortId } })}
|
||||||
<Square className="h-4 w-4 mr-2" />
|
disabled={startDoctor.isPending}
|
||||||
<span>Cancel doctor</span>
|
>
|
||||||
</Button>
|
<Stethoscope className="h-4 w-4 mr-2" />
|
||||||
<Button
|
Run doctor
|
||||||
type="button"
|
</Button>
|
||||||
variant="outline"
|
)}
|
||||||
className={cn({ hidden: isDoctorRunning })}
|
<DropdownMenu>
|
||||||
onClick={() => startDoctor.mutate({ path: { shortId: repository.shortId } })}
|
<DropdownMenuTrigger asChild>
|
||||||
disabled={startDoctor.isPending}
|
<Button variant="outline">
|
||||||
>
|
Actions
|
||||||
<Stethoscope className="h-4 w-4 mr-2" />
|
<ChevronDown className="h-4 w-4 ml-1" />
|
||||||
Run doctor
|
</Button>
|
||||||
</Button>
|
</DropdownMenuTrigger>
|
||||||
<Button
|
<DropdownMenuContent align="end">
|
||||||
type="button"
|
<DropdownMenuItem onClick={() => navigate({ to: `/repositories/${repository.shortId}/edit` })}>
|
||||||
variant="outline"
|
<Pencil />
|
||||||
onClick={() => unlockRepo.mutate({ path: { shortId: repository.shortId } })}
|
Edit
|
||||||
loading={unlockRepo.isPending}
|
</DropdownMenuItem>
|
||||||
>
|
<DropdownMenuItem
|
||||||
<Unlock className="h-4 w-4 mr-2" />
|
onClick={() =>
|
||||||
Unlock
|
toast.promise(unlockRepo.mutateAsync({ path: { shortId: repository.shortId } }), {
|
||||||
</Button>
|
loading: "Unlocking repo",
|
||||||
<Button
|
success: "Repository unlocked successfully",
|
||||||
type="button"
|
error: (e) => parseError(e)?.message || "Failed to unlock repository",
|
||||||
variant="destructive"
|
})
|
||||||
onClick={() => setShowDeleteConfirm(true)}
|
}
|
||||||
disabled={deleteRepo.isPending}
|
disabled={unlockRepo.isPending}
|
||||||
>
|
>
|
||||||
<Trash2 className="h-4 w-4 mr-2" />
|
<Unlock />
|
||||||
Delete
|
Unlock
|
||||||
</Button>
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => setShowDeleteConfirm(true)}
|
||||||
|
disabled={deleteRepo.isPending}
|
||||||
|
>
|
||||||
|
<Trash2 />
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMutation } from "@tanstack/react-query";
|
import { useMutation } from "@tanstack/react-query";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { toast } from "sonner";
|
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 { CreateVolumeForm } from "~/client/modules/volumes/components/create-volume-form";
|
||||||
import {
|
import {
|
||||||
AlertDialog,
|
AlertDialog,
|
||||||
|
|
@ -15,6 +15,13 @@ import {
|
||||||
} from "~/client/components/ui/alert-dialog";
|
} from "~/client/components/ui/alert-dialog";
|
||||||
import { Button } from "~/client/components/ui/button";
|
import { Button } from "~/client/components/ui/button";
|
||||||
import { Card } from "~/client/components/ui/card";
|
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 type { StatFs, Volume } from "~/client/lib/types";
|
||||||
import { HealthchecksCard } from "../components/healthchecks-card";
|
import { HealthchecksCard } from "../components/healthchecks-card";
|
||||||
import { StorageChart } from "../components/storage-chart";
|
import { StorageChart } from "../components/storage-chart";
|
||||||
|
|
@ -37,26 +44,10 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
|
||||||
|
|
||||||
const mountVol = useMutation({
|
const mountVol = useMutation({
|
||||||
...mountVolumeMutation(),
|
...mountVolumeMutation(),
|
||||||
onSuccess: () => {
|
|
||||||
toast.success("Volume mounted successfully");
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
toast.error("Failed to mount volume", {
|
|
||||||
description: parseError(error)?.message,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const unmountVol = useMutation({
|
const unmountVol = useMutation({
|
||||||
...unmountVolumeMutation(),
|
...unmountVolumeMutation(),
|
||||||
onSuccess: () => {
|
|
||||||
toast.success("Volume unmounted successfully");
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
toast.error("Failed to unmount volume", {
|
|
||||||
description: parseError(error)?.message,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const deleteVol = useMutation({
|
const deleteVol = useMutation({
|
||||||
|
|
@ -89,23 +80,21 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
|
||||||
<div className="flex flex-col @xl:flex-row items-start @xl:items-center justify-between gap-4 mb-6">
|
<div className="flex flex-col @xl:flex-row items-start @xl:items-center justify-between gap-4 mb-6">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<span className="text-lg font-semibold">Volume Configuration</span>
|
<h2 className="text-lg font-semibold tracking-tight">Volume Configuration</h2>
|
||||||
{volume.provisioningId && <ManagedBadge />}
|
{volume.provisioningId && <ManagedBadge />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col @xl:flex-row w-full @xl:w-auto gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => navigate({ to: `/volumes/${volume.shortId}/edit` })}
|
|
||||||
>
|
|
||||||
<Pencil className="h-4 w-4 mr-2" />
|
|
||||||
Edit
|
|
||||||
</Button>
|
|
||||||
{volume.status !== "mounted" ? (
|
{volume.status !== "mounted" ? (
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={() => mountVol.mutate({ path: { shortId: volume.shortId } })}
|
onClick={() =>
|
||||||
|
toast.promise(mountVol.mutateAsync({ path: { shortId: volume.shortId } }), {
|
||||||
|
loading: "Mounting volume...",
|
||||||
|
success: "Volume mounted successfully",
|
||||||
|
error: (error) => parseError(error)?.message || "Failed to mount volume",
|
||||||
|
})
|
||||||
|
}
|
||||||
loading={mountVol.isPending}
|
loading={mountVol.isPending}
|
||||||
>
|
>
|
||||||
<Plug className="h-4 w-4 mr-2" />
|
<Plug className="h-4 w-4 mr-2" />
|
||||||
|
|
@ -115,22 +104,42 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
onClick={() => unmountVol.mutate({ path: { shortId: volume.shortId } })}
|
onClick={() =>
|
||||||
|
toast.promise(unmountVol.mutateAsync({ path: { shortId: volume.shortId } }), {
|
||||||
|
loading: "Unmounting volume...",
|
||||||
|
success: "Volume unmounted successfully",
|
||||||
|
error: (error) => parseError(error)?.message || "Failed to unmount volume",
|
||||||
|
})
|
||||||
|
}
|
||||||
loading={unmountVol.isPending}
|
loading={unmountVol.isPending}
|
||||||
>
|
>
|
||||||
<Unplug className="h-4 w-4 mr-2" />
|
<Unplug className="h-4 w-4 mr-2" />
|
||||||
Unmount
|
Unmount
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
<Button
|
<DropdownMenu>
|
||||||
type="button"
|
<DropdownMenuTrigger asChild>
|
||||||
variant="destructive"
|
<Button variant="outline">
|
||||||
onClick={() => setShowDeleteConfirm(true)}
|
Actions
|
||||||
disabled={deleteVol.isPending}
|
<ChevronDown className="h-4 w-4 ml-1" />
|
||||||
>
|
</Button>
|
||||||
<Trash2 className="h-4 w-4 mr-2" />
|
</DropdownMenuTrigger>
|
||||||
Delete
|
<DropdownMenuContent align="end">
|
||||||
</Button>
|
<DropdownMenuItem onClick={() => navigate({ to: `/volumes/${volume.shortId}/edit` })}>
|
||||||
|
<Pencil />
|
||||||
|
Edit
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() => setShowDeleteConfirm(true)}
|
||||||
|
disabled={deleteVol.isPending}
|
||||||
|
>
|
||||||
|
<Trash2 />
|
||||||
|
Delete
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<CreateVolumeForm
|
<CreateVolumeForm
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue