diff --git a/app/client/modules/volumes/components/create-volume-form.tsx b/app/client/modules/volumes/components/create-volume-form.tsx index ba48e117..42fea7d9 100644 --- a/app/client/modules/volumes/components/create-volume-form.tsx +++ b/app/client/modules/volumes/components/create-volume-form.tsx @@ -60,6 +60,7 @@ type Props = { formId?: string; loading?: boolean; className?: string; + readOnly?: boolean; }; const defaultValuesForType = { @@ -71,7 +72,15 @@ const defaultValuesForType = { sftp: { backend: "sftp" as const, port: 22, path: "/", skipHostKeyCheck: false }, }; -export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, formId, loading, className }: Props) => { +export const CreateVolumeForm = ({ + onSubmit, + mode = "create", + initialValues, + formId, + loading, + className, + readOnly = false, +}: Props) => { const form = useForm({ resolver: zodResolver(formSchema, undefined, { raw: true }), defaultValues: initialValues || { @@ -137,123 +146,126 @@ export const CreateVolumeForm = ({ onSubmit, mode = "create", initialValues, for onSubmit={form.handleSubmit(onSubmit, scrollToFirstError)} className={cn("space-y-4", className)} > - ( - - Name - - - - Unique identifier for the volume. - - - )} - /> - ( - - Backend - - - Directory - - -
- - NFS - -
-
- -

Remote mounts require SYS_ADMIN capability

-
-
- - -
- - SMB - -
-
- -

Remote mounts require SYS_ADMIN capability

-
-
- - -
- - WebDAV - -
-
- -

Remote mounts require SYS_ADMIN capability

-
-
- - -
- - SFTP - -
-
- -

Remote mounts require SYS_ADMIN capability

-
-
- - -
- - rclone - -
-
- -

Remote mounts require SYS_ADMIN capability

-
- -

Setup rclone to use this backend

-
-
-
- - Choose the storage backend for this volume. - -
- )} - /> - {watchedBackend === "directory" && } - {watchedBackend === "nfs" && } - {watchedBackend === "webdav" && } - {watchedBackend === "smb" && } - {watchedBackend === "rclone" && } - {watchedBackend === "sftp" && } - {watchedBackend && watchedBackend !== "directory" && watchedBackend !== "rclone" && ( + Unique identifier for the volume. + + + )} + /> + ( + + Backend + + Choose the storage backend for this volume. + + + )} + /> + {watchedBackend === "directory" && } + {watchedBackend === "nfs" && } + {watchedBackend === "webdav" && } + {watchedBackend === "smb" && } + {watchedBackend === "rclone" && } + {watchedBackend === "sftp" && } + + {!readOnly && watchedBackend && watchedBackend !== "directory" && watchedBackend !== "rclone" && (
- + )} diff --git a/app/client/modules/volumes/components/volume-forms/smb-form.tsx b/app/client/modules/volumes/components/volume-forms/smb-form.tsx index 89e64b52..ad49f1ac 100644 --- a/app/client/modules/volumes/components/volume-forms/smb-form.tsx +++ b/app/client/modules/volumes/components/volume-forms/smb-form.tsx @@ -14,9 +14,10 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from ". type Props = { form: UseFormReturn; + readOnly?: boolean; }; -export const SMBForm = ({ form }: Props) => { +export const SMBForm = ({ form, readOnly = false }: Props) => { const guest = useWatch({ control: form.control, name: "guest" }); return ( @@ -116,7 +117,7 @@ export const SMBForm = ({ form }: Props) => { render={({ field }) => ( SMB Version - diff --git a/app/client/modules/volumes/routes/edit-volume.tsx b/app/client/modules/volumes/routes/edit-volume.tsx new file mode 100644 index 00000000..d4659bfc --- /dev/null +++ b/app/client/modules/volumes/routes/edit-volume.tsx @@ -0,0 +1,136 @@ +import { useMutation, useSuspenseQuery } from "@tanstack/react-query"; +import { HardDrive, Check, Save } from "lucide-react"; +import { useId, useState } from "react"; +import { toast } from "sonner"; +import { getVolumeOptions, updateVolumeMutation } from "~/client/api-client/@tanstack/react-query.gen"; +import { type UpdateVolumeResponse } from "~/client/api-client/types.gen"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "~/client/components/ui/alert-dialog"; +import { Alert, AlertDescription } from "~/client/components/ui/alert"; +import { Button } from "~/client/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card"; +import { parseError } from "~/client/lib/errors"; +import { useNavigate } from "@tanstack/react-router"; +import { ManagedBadge } from "~/client/components/managed-badge"; +import { CreateVolumeForm, formSchema, type FormValues } from "../components/create-volume-form"; + +export function EditVolumePage({ volumeId }: { volumeId: string }) { + const navigate = useNavigate(); + const formId = useId(); + const [open, setOpen] = useState(false); + const [pendingValues, setPendingValues] = useState(null); + + const { data } = useSuspenseQuery({ + ...getVolumeOptions({ path: { shortId: volumeId } }), + }); + + const { volume } = data; + + const updateVolume = useMutation({ + ...updateVolumeMutation(), + onSuccess: (updatedVolume: UpdateVolumeResponse) => { + toast.success("Volume updated successfully"); + setOpen(false); + setPendingValues(null); + void navigate({ to: `/volumes/${updatedVolume.shortId}` }); + }, + onError: (error) => { + toast.error("Failed to update volume", { + description: parseError(error)?.message, + }); + setOpen(false); + setPendingValues(null); + }, + }); + + const handleSubmit = (values: FormValues) => { + setPendingValues(values); + setOpen(true); + }; + + const confirmUpdate = () => { + if (!pendingValues) { + return; + } + + const { name, ...config } = formSchema.parse(pendingValues); + + updateVolume.mutate({ + path: { shortId: volume.shortId }, + body: { name, config }, + }); + }; + + return ( + <> +
+ + +
+
+ +
+
+ Edit Volume + {volume.provisioningId && } +
+
+
+ + {updateVolume.isError && ( + + + Failed to update volume: +
+ {parseError(updateVolume.error)?.message} +
+
+ )} + +
+ + +
+
+
+
+ + + + Update Volume Configuration + + Editing the volume will remount it with the new config immediately. This may temporarily disrupt access to + the volume. Continue? + + + + Cancel + + + Update + + + + + + ); +} diff --git a/app/client/modules/volumes/routes/volume-details.tsx b/app/client/modules/volumes/routes/volume-details.tsx index 21c66bbb..63cd2585 100644 --- a/app/client/modules/volumes/routes/volume-details.tsx +++ b/app/client/modules/volumes/routes/volume-details.tsx @@ -9,7 +9,7 @@ import { useNavigate } from "@tanstack/react-router"; export function VolumeDetails({ volumeId }: { volumeId: string }) { const navigate = useNavigate(); - const searchParams = useSearch({ from: "/(dashboard)/volumes/$volumeId" }); + const searchParams = useSearch({ from: "/(dashboard)/volumes/$volumeId/" }); const activeTab = searchParams.tab || "info"; diff --git a/app/client/modules/volumes/tabs/info.tsx b/app/client/modules/volumes/tabs/info.tsx index d01dd98e..e9656d75 100644 --- a/app/client/modules/volumes/tabs/info.tsx +++ b/app/client/modules/volumes/tabs/info.tsx @@ -1,8 +1,8 @@ import { useMutation } from "@tanstack/react-query"; import { useState } from "react"; import { toast } from "sonner"; -import { Check, Plug, Trash2, Unplug } from "lucide-react"; -import { CreateVolumeForm, formSchema, type FormValues } from "~/client/modules/volumes/components/create-volume-form"; +import { Pencil, Plug, Trash2, Unplug } from "lucide-react"; +import { CreateVolumeForm } from "~/client/modules/volumes/components/create-volume-form"; import { AlertDialog, AlertDialogAction, @@ -22,9 +22,7 @@ import { deleteVolumeMutation, mountVolumeMutation, unmountVolumeMutation, - updateVolumeMutation, } from "~/client/api-client/@tanstack/react-query.gen"; -import type { UpdateVolumeResponse } from "~/client/api-client/types.gen"; import { useNavigate } from "@tanstack/react-router"; import { parseError } from "~/client/lib/errors"; import { ManagedBadge } from "~/client/components/managed-badge"; @@ -37,24 +35,6 @@ type Props = { export const VolumeInfoTabContent = ({ volume, statfs }: Props) => { const navigate = useNavigate(); - const updateMutation = useMutation({ - ...updateVolumeMutation(), - onSuccess: (data: UpdateVolumeResponse) => { - toast.success("Volume updated successfully"); - setOpen(false); - setPendingValues(null); - - if (data.name !== volume.name) { - void navigate({ to: `/volumes/${data.shortId}` }); - } - }, - onError: (error) => { - toast.error("Failed to update volume", { description: error.message }); - setOpen(false); - setPendingValues(null); - }, - }); - const mountVol = useMutation({ ...mountVolumeMutation(), onSuccess: () => { @@ -92,81 +72,83 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => { }, }); - const [open, setOpen] = useState(false); - const [pendingValues, setPendingValues] = useState(null); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); - const handleSubmit = (values: FormValues) => { - setPendingValues(values); - setOpen(true); - }; - - const confirmUpdate = () => { - if (pendingValues) { - const { name, ...config } = formSchema.parse(pendingValues); - - updateMutation.mutate({ - path: { shortId: volume.shortId }, - body: { name, config }, - }); - } - }; - const handleConfirmDelete = () => { setShowDeleteConfirm(false); deleteVol.mutate({ path: { shortId: volume.shortId } }); }; + const hasLastError = Boolean(volume.lastError); + return ( <>
- -
-
-
- Volume Configuration - {volume.provisioningId && } +
+ +
+
+
+ Volume Configuration + {volume.provisioningId && } +
+
+
+ + {volume.status !== "mounted" ? ( + + ) : ( + + )} +
-
- {volume.status !== "mounted" ? ( - - ) : ( - - )} - -
-
- - + {}} + mode="update" + readOnly + /> + + {hasLastError && ( + +
+

Last Error

+

{volume.lastError}

+
+
+ )} +
@@ -176,24 +158,6 @@ export const VolumeInfoTabContent = ({ volume, statfs }: Props) => {
- - - - Update Volume Configuration - - Editing the volume will remount it with the new config immediately. This may temporarily disrupt access to - the volume. Continue? - - - - Cancel - - - Update - - - - diff --git a/app/routeTree.gen.ts b/app/routeTree.gen.ts index ca1eb70c..f490814f 100644 --- a/app/routeTree.gen.ts +++ b/app/routeTree.gen.ts @@ -23,14 +23,15 @@ import { Route as dashboardNotificationsIndexRouteImport } from './routes/(dashb import { Route as dashboardBackupsIndexRouteImport } from './routes/(dashboard)/backups/index' import { Route as dashboardAdminIndexRouteImport } from './routes/(dashboard)/admin/index' import { Route as dashboardVolumesCreateRouteImport } from './routes/(dashboard)/volumes/create' -import { Route as dashboardVolumesVolumeIdRouteImport } from './routes/(dashboard)/volumes/$volumeId' import { Route as dashboardRepositoriesCreateRouteImport } from './routes/(dashboard)/repositories/create' import { Route as dashboardNotificationsCreateRouteImport } from './routes/(dashboard)/notifications/create' import { Route as dashboardNotificationsNotificationIdRouteImport } from './routes/(dashboard)/notifications/$notificationId' import { Route as dashboardBackupsCreateRouteImport } from './routes/(dashboard)/backups/create' import { Route as authLoginErrorRouteImport } from './routes/(auth)/login.error' +import { Route as dashboardVolumesVolumeIdIndexRouteImport } from './routes/(dashboard)/volumes/$volumeId/index' import { Route as dashboardRepositoriesRepositoryIdIndexRouteImport } from './routes/(dashboard)/repositories/$repositoryId/index' import { Route as dashboardBackupsBackupIdIndexRouteImport } from './routes/(dashboard)/backups/$backupId/index' +import { Route as dashboardVolumesVolumeIdEditRouteImport } from './routes/(dashboard)/volumes/$volumeId/edit' import { Route as dashboardSettingsSsoNewRouteImport } from './routes/(dashboard)/settings/sso/new' import { Route as dashboardRepositoriesRepositoryIdEditRouteImport } from './routes/(dashboard)/repositories/$repositoryId/edit' import { Route as dashboardBackupsBackupIdEditRouteImport } from './routes/(dashboard)/backups/$backupId/edit' @@ -108,12 +109,6 @@ const dashboardVolumesCreateRoute = dashboardVolumesCreateRouteImport.update({ path: '/volumes/create', getParentRoute: () => dashboardRouteRoute, } as any) -const dashboardVolumesVolumeIdRoute = - dashboardVolumesVolumeIdRouteImport.update({ - id: '/volumes/$volumeId', - path: '/volumes/$volumeId', - getParentRoute: () => dashboardRouteRoute, - } as any) const dashboardRepositoriesCreateRoute = dashboardRepositoriesCreateRouteImport.update({ id: '/repositories/create', @@ -142,6 +137,12 @@ const authLoginErrorRoute = authLoginErrorRouteImport.update({ path: '/error', getParentRoute: () => authLoginRoute, } as any) +const dashboardVolumesVolumeIdIndexRoute = + dashboardVolumesVolumeIdIndexRouteImport.update({ + id: '/volumes/$volumeId/', + path: '/volumes/$volumeId/', + getParentRoute: () => dashboardRouteRoute, + } as any) const dashboardRepositoriesRepositoryIdIndexRoute = dashboardRepositoriesRepositoryIdIndexRouteImport.update({ id: '/repositories/$repositoryId/', @@ -154,6 +155,12 @@ const dashboardBackupsBackupIdIndexRoute = path: '/backups/$backupId/', getParentRoute: () => dashboardRouteRoute, } as any) +const dashboardVolumesVolumeIdEditRoute = + dashboardVolumesVolumeIdEditRouteImport.update({ + id: '/volumes/$volumeId/edit', + path: '/volumes/$volumeId/edit', + getParentRoute: () => dashboardRouteRoute, + } as any) const dashboardSettingsSsoNewRoute = dashboardSettingsSsoNewRouteImport.update({ id: '/settings/sso/new', path: '/settings/sso/new', @@ -201,7 +208,6 @@ export interface FileRoutesByFullPath { '/notifications/$notificationId': typeof dashboardNotificationsNotificationIdRoute '/notifications/create': typeof dashboardNotificationsCreateRoute '/repositories/create': typeof dashboardRepositoriesCreateRoute - '/volumes/$volumeId': typeof dashboardVolumesVolumeIdRoute '/volumes/create': typeof dashboardVolumesCreateRoute '/admin/': typeof dashboardAdminIndexRoute '/backups/': typeof dashboardBackupsIndexRoute @@ -212,8 +218,10 @@ export interface FileRoutesByFullPath { '/backups/$backupId/edit': typeof dashboardBackupsBackupIdEditRoute '/repositories/$repositoryId/edit': typeof dashboardRepositoriesRepositoryIdEditRoute '/settings/sso/new': typeof dashboardSettingsSsoNewRoute + '/volumes/$volumeId/edit': typeof dashboardVolumesVolumeIdEditRoute '/backups/$backupId/': typeof dashboardBackupsBackupIdIndexRoute '/repositories/$repositoryId/': typeof dashboardRepositoriesRepositoryIdIndexRoute + '/volumes/$volumeId/': typeof dashboardVolumesVolumeIdIndexRoute '/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute '/repositories/$repositoryId/$snapshotId/restore': typeof dashboardRepositoriesRepositoryIdSnapshotIdRestoreRoute '/repositories/$repositoryId/$snapshotId/': typeof dashboardRepositoriesRepositoryIdSnapshotIdIndexRoute @@ -229,7 +237,6 @@ export interface FileRoutesByTo { '/notifications/$notificationId': typeof dashboardNotificationsNotificationIdRoute '/notifications/create': typeof dashboardNotificationsCreateRoute '/repositories/create': typeof dashboardRepositoriesCreateRoute - '/volumes/$volumeId': typeof dashboardVolumesVolumeIdRoute '/volumes/create': typeof dashboardVolumesCreateRoute '/admin': typeof dashboardAdminIndexRoute '/backups': typeof dashboardBackupsIndexRoute @@ -240,8 +247,10 @@ export interface FileRoutesByTo { '/backups/$backupId/edit': typeof dashboardBackupsBackupIdEditRoute '/repositories/$repositoryId/edit': typeof dashboardRepositoriesRepositoryIdEditRoute '/settings/sso/new': typeof dashboardSettingsSsoNewRoute + '/volumes/$volumeId/edit': typeof dashboardVolumesVolumeIdEditRoute '/backups/$backupId': typeof dashboardBackupsBackupIdIndexRoute '/repositories/$repositoryId': typeof dashboardRepositoriesRepositoryIdIndexRoute + '/volumes/$volumeId': typeof dashboardVolumesVolumeIdIndexRoute '/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute '/repositories/$repositoryId/$snapshotId/restore': typeof dashboardRepositoriesRepositoryIdSnapshotIdRestoreRoute '/repositories/$repositoryId/$snapshotId': typeof dashboardRepositoriesRepositoryIdSnapshotIdIndexRoute @@ -260,7 +269,6 @@ export interface FileRoutesById { '/(dashboard)/notifications/$notificationId': typeof dashboardNotificationsNotificationIdRoute '/(dashboard)/notifications/create': typeof dashboardNotificationsCreateRoute '/(dashboard)/repositories/create': typeof dashboardRepositoriesCreateRoute - '/(dashboard)/volumes/$volumeId': typeof dashboardVolumesVolumeIdRoute '/(dashboard)/volumes/create': typeof dashboardVolumesCreateRoute '/(dashboard)/admin/': typeof dashboardAdminIndexRoute '/(dashboard)/backups/': typeof dashboardBackupsIndexRoute @@ -271,8 +279,10 @@ export interface FileRoutesById { '/(dashboard)/backups/$backupId/edit': typeof dashboardBackupsBackupIdEditRoute '/(dashboard)/repositories/$repositoryId/edit': typeof dashboardRepositoriesRepositoryIdEditRoute '/(dashboard)/settings/sso/new': typeof dashboardSettingsSsoNewRoute + '/(dashboard)/volumes/$volumeId/edit': typeof dashboardVolumesVolumeIdEditRoute '/(dashboard)/backups/$backupId/': typeof dashboardBackupsBackupIdIndexRoute '/(dashboard)/repositories/$repositoryId/': typeof dashboardRepositoriesRepositoryIdIndexRoute + '/(dashboard)/volumes/$volumeId/': typeof dashboardVolumesVolumeIdIndexRoute '/(dashboard)/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute '/(dashboard)/repositories/$repositoryId/$snapshotId/restore': typeof dashboardRepositoriesRepositoryIdSnapshotIdRestoreRoute '/(dashboard)/repositories/$repositoryId/$snapshotId/': typeof dashboardRepositoriesRepositoryIdSnapshotIdIndexRoute @@ -290,7 +300,6 @@ export interface FileRouteTypes { | '/notifications/$notificationId' | '/notifications/create' | '/repositories/create' - | '/volumes/$volumeId' | '/volumes/create' | '/admin/' | '/backups/' @@ -301,8 +310,10 @@ export interface FileRouteTypes { | '/backups/$backupId/edit' | '/repositories/$repositoryId/edit' | '/settings/sso/new' + | '/volumes/$volumeId/edit' | '/backups/$backupId/' | '/repositories/$repositoryId/' + | '/volumes/$volumeId/' | '/backups/$backupId/$snapshotId/restore' | '/repositories/$repositoryId/$snapshotId/restore' | '/repositories/$repositoryId/$snapshotId/' @@ -318,7 +329,6 @@ export interface FileRouteTypes { | '/notifications/$notificationId' | '/notifications/create' | '/repositories/create' - | '/volumes/$volumeId' | '/volumes/create' | '/admin' | '/backups' @@ -329,8 +339,10 @@ export interface FileRouteTypes { | '/backups/$backupId/edit' | '/repositories/$repositoryId/edit' | '/settings/sso/new' + | '/volumes/$volumeId/edit' | '/backups/$backupId' | '/repositories/$repositoryId' + | '/volumes/$volumeId' | '/backups/$backupId/$snapshotId/restore' | '/repositories/$repositoryId/$snapshotId/restore' | '/repositories/$repositoryId/$snapshotId' @@ -348,7 +360,6 @@ export interface FileRouteTypes { | '/(dashboard)/notifications/$notificationId' | '/(dashboard)/notifications/create' | '/(dashboard)/repositories/create' - | '/(dashboard)/volumes/$volumeId' | '/(dashboard)/volumes/create' | '/(dashboard)/admin/' | '/(dashboard)/backups/' @@ -359,8 +370,10 @@ export interface FileRouteTypes { | '/(dashboard)/backups/$backupId/edit' | '/(dashboard)/repositories/$repositoryId/edit' | '/(dashboard)/settings/sso/new' + | '/(dashboard)/volumes/$volumeId/edit' | '/(dashboard)/backups/$backupId/' | '/(dashboard)/repositories/$repositoryId/' + | '/(dashboard)/volumes/$volumeId/' | '/(dashboard)/backups/$backupId/$snapshotId/restore' | '/(dashboard)/repositories/$repositoryId/$snapshotId/restore' | '/(dashboard)/repositories/$repositoryId/$snapshotId/' @@ -473,13 +486,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dashboardVolumesCreateRouteImport parentRoute: typeof dashboardRouteRoute } - '/(dashboard)/volumes/$volumeId': { - id: '/(dashboard)/volumes/$volumeId' - path: '/volumes/$volumeId' - fullPath: '/volumes/$volumeId' - preLoaderRoute: typeof dashboardVolumesVolumeIdRouteImport - parentRoute: typeof dashboardRouteRoute - } '/(dashboard)/repositories/create': { id: '/(dashboard)/repositories/create' path: '/repositories/create' @@ -515,6 +521,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof authLoginErrorRouteImport parentRoute: typeof authLoginRoute } + '/(dashboard)/volumes/$volumeId/': { + id: '/(dashboard)/volumes/$volumeId/' + path: '/volumes/$volumeId' + fullPath: '/volumes/$volumeId/' + preLoaderRoute: typeof dashboardVolumesVolumeIdIndexRouteImport + parentRoute: typeof dashboardRouteRoute + } '/(dashboard)/repositories/$repositoryId/': { id: '/(dashboard)/repositories/$repositoryId/' path: '/repositories/$repositoryId' @@ -529,6 +542,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dashboardBackupsBackupIdIndexRouteImport parentRoute: typeof dashboardRouteRoute } + '/(dashboard)/volumes/$volumeId/edit': { + id: '/(dashboard)/volumes/$volumeId/edit' + path: '/volumes/$volumeId/edit' + fullPath: '/volumes/$volumeId/edit' + preLoaderRoute: typeof dashboardVolumesVolumeIdEditRouteImport + parentRoute: typeof dashboardRouteRoute + } '/(dashboard)/settings/sso/new': { id: '/(dashboard)/settings/sso/new' path: '/settings/sso/new' @@ -607,7 +627,6 @@ interface dashboardRouteRouteChildren { dashboardNotificationsNotificationIdRoute: typeof dashboardNotificationsNotificationIdRoute dashboardNotificationsCreateRoute: typeof dashboardNotificationsCreateRoute dashboardRepositoriesCreateRoute: typeof dashboardRepositoriesCreateRoute - dashboardVolumesVolumeIdRoute: typeof dashboardVolumesVolumeIdRoute dashboardVolumesCreateRoute: typeof dashboardVolumesCreateRoute dashboardAdminIndexRoute: typeof dashboardAdminIndexRoute dashboardBackupsIndexRoute: typeof dashboardBackupsIndexRoute @@ -618,8 +637,10 @@ interface dashboardRouteRouteChildren { dashboardBackupsBackupIdEditRoute: typeof dashboardBackupsBackupIdEditRoute dashboardRepositoriesRepositoryIdEditRoute: typeof dashboardRepositoriesRepositoryIdEditRoute dashboardSettingsSsoNewRoute: typeof dashboardSettingsSsoNewRoute + dashboardVolumesVolumeIdEditRoute: typeof dashboardVolumesVolumeIdEditRoute dashboardBackupsBackupIdIndexRoute: typeof dashboardBackupsBackupIdIndexRoute dashboardRepositoriesRepositoryIdIndexRoute: typeof dashboardRepositoriesRepositoryIdIndexRoute + dashboardVolumesVolumeIdIndexRoute: typeof dashboardVolumesVolumeIdIndexRoute dashboardBackupsBackupIdSnapshotIdRestoreRoute: typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute dashboardRepositoriesRepositoryIdSnapshotIdRestoreRoute: typeof dashboardRepositoriesRepositoryIdSnapshotIdRestoreRoute dashboardRepositoriesRepositoryIdSnapshotIdIndexRoute: typeof dashboardRepositoriesRepositoryIdSnapshotIdIndexRoute @@ -631,7 +652,6 @@ const dashboardRouteRouteChildren: dashboardRouteRouteChildren = { dashboardNotificationsNotificationIdRoute, dashboardNotificationsCreateRoute: dashboardNotificationsCreateRoute, dashboardRepositoriesCreateRoute: dashboardRepositoriesCreateRoute, - dashboardVolumesVolumeIdRoute: dashboardVolumesVolumeIdRoute, dashboardVolumesCreateRoute: dashboardVolumesCreateRoute, dashboardAdminIndexRoute: dashboardAdminIndexRoute, dashboardBackupsIndexRoute: dashboardBackupsIndexRoute, @@ -643,9 +663,11 @@ const dashboardRouteRouteChildren: dashboardRouteRouteChildren = { dashboardRepositoriesRepositoryIdEditRoute: dashboardRepositoriesRepositoryIdEditRoute, dashboardSettingsSsoNewRoute: dashboardSettingsSsoNewRoute, + dashboardVolumesVolumeIdEditRoute: dashboardVolumesVolumeIdEditRoute, dashboardBackupsBackupIdIndexRoute: dashboardBackupsBackupIdIndexRoute, dashboardRepositoriesRepositoryIdIndexRoute: dashboardRepositoriesRepositoryIdIndexRoute, + dashboardVolumesVolumeIdIndexRoute: dashboardVolumesVolumeIdIndexRoute, dashboardBackupsBackupIdSnapshotIdRestoreRoute: dashboardBackupsBackupIdSnapshotIdRestoreRoute, dashboardRepositoriesRepositoryIdSnapshotIdRestoreRoute: diff --git a/app/routes/(dashboard)/volumes/$volumeId/edit.tsx b/app/routes/(dashboard)/volumes/$volumeId/edit.tsx new file mode 100644 index 00000000..d744e9f5 --- /dev/null +++ b/app/routes/(dashboard)/volumes/$volumeId/edit.tsx @@ -0,0 +1,37 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { getVolumeOptions } from "~/client/api-client/@tanstack/react-query.gen"; +import { EditVolumePage } from "~/client/modules/volumes/routes/edit-volume"; + +export const Route = createFileRoute("/(dashboard)/volumes/$volumeId/edit")({ + component: RouteComponent, + errorComponent: (e) =>
{e.error.message}
, + loader: async ({ params, context }) => { + const volume = await context.queryClient.ensureQueryData({ + ...getVolumeOptions({ path: { shortId: params.volumeId } }), + }); + + return volume; + }, + staticData: { + breadcrumb: (match) => [ + { label: "Volumes", href: "/volumes" }, + { label: match.loaderData?.volume.name || "Volume", href: `/volumes/${match.params.volumeId}` }, + { label: "Edit" }, + ], + }, + head: ({ loaderData }) => ({ + meta: [ + { title: `Zerobyte - Edit ${loaderData?.volume.name ?? "Volume"}` }, + { + name: "description", + content: "Edit volume configuration.", + }, + ], + }), +}); + +function RouteComponent() { + const { volumeId } = Route.useParams(); + + return ; +} diff --git a/app/routes/(dashboard)/volumes/$volumeId.tsx b/app/routes/(dashboard)/volumes/$volumeId/index.tsx similarity index 99% rename from app/routes/(dashboard)/volumes/$volumeId.tsx rename to app/routes/(dashboard)/volumes/$volumeId/index.tsx index 7dfc9610..f7f19c85 100644 --- a/app/routes/(dashboard)/volumes/$volumeId.tsx +++ b/app/routes/(dashboard)/volumes/$volumeId/index.tsx @@ -3,7 +3,7 @@ import { z } from "zod"; import { getVolumeOptions } from "~/client/api-client/@tanstack/react-query.gen"; import { VolumeDetails } from "~/client/modules/volumes/routes/volume-details"; -export const Route = createFileRoute("/(dashboard)/volumes/$volumeId")({ +export const Route = createFileRoute("/(dashboard)/volumes/$volumeId/")({ component: RouteComponent, errorComponent: (e) =>
{e.error.message}
, loader: async ({ params, context }) => {