diff --git a/app/client/modules/notifications/components/create-notification-form.tsx b/app/client/modules/notifications/components/create-notification-form.tsx index b476045c..66451dc1 100644 --- a/app/client/modules/notifications/components/create-notification-form.tsx +++ b/app/client/modules/notifications/components/create-notification-form.tsx @@ -145,72 +145,74 @@ export const CreateNotificationForm = ({ onSubmit, mode = "create", initialValue onSubmit={form.handleSubmit(onSubmit, scrollToFirstError)} className={cn("space-y-4", className)} > - ( - - Name - - - - Unique identifier for this notification destination. - - - )} - /> - - ( - - Type - - - Email (SMTP) - Slack - Discord - Gotify - Ntfy - Pushover - Telegram - Generic Webhook - Custom (Shoutrrr URL) - - - Choose the notification delivery method. - - - )} - /> + Unique identifier for this notification destination. + + + )} + /> - {watchedType === "email" && } - {watchedType === "slack" && } - {watchedType === "discord" && } - {watchedType === "gotify" && } - {watchedType === "ntfy" && } - {watchedType === "pushover" && } - {watchedType === "telegram" && } - {watchedType === "generic" && } - {watchedType === "custom" && } + ( + + Type + + Choose the notification delivery method. + + + )} + /> + + {watchedType === "email" && } + {watchedType === "slack" && } + {watchedType === "discord" && } + {watchedType === "gotify" && } + {watchedType === "ntfy" && } + {watchedType === "pushover" && } + {watchedType === "telegram" && } + {watchedType === "generic" && } + {watchedType === "custom" && } + ); diff --git a/app/client/modules/notifications/routes/edit-notification.tsx b/app/client/modules/notifications/routes/edit-notification.tsx new file mode 100644 index 00000000..b607f5eb --- /dev/null +++ b/app/client/modules/notifications/routes/edit-notification.tsx @@ -0,0 +1,90 @@ +import { useMutation, useSuspenseQuery } from "@tanstack/react-query"; +import { useId } from "react"; +import { useNavigate } from "@tanstack/react-router"; +import { Bell, Save } from "lucide-react"; +import { toast } from "sonner"; +import { + getNotificationDestinationOptions, + updateNotificationDestinationMutation, +} from "~/client/api-client/@tanstack/react-query.gen"; +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 { CreateNotificationForm, type NotificationFormValues } from "../components/create-notification-form"; + +export function EditNotificationPage({ notificationId }: { notificationId: string }) { + const navigate = useNavigate(); + const formId = useId(); + + const { data } = useSuspenseQuery({ + ...getNotificationDestinationOptions({ path: { id: notificationId } }), + }); + + const updateDestination = useMutation({ + ...updateNotificationDestinationMutation(), + onSuccess: () => { + toast.success("Notification destination updated successfully"); + void navigate({ to: `/notifications/${data.id}` }); + }, + onError: (error) => { + toast.error("Failed to update notification destination", { + description: parseError(error)?.message, + }); + }, + }); + + const handleSubmit = (values: NotificationFormValues) => { + updateDestination.mutate({ + path: { id: String(data.id) }, + body: { + name: values.name, + config: values, + }, + }); + }; + + return ( +
+ + +
+
+ +
+ Edit Notification Destination +
+
+ + {updateDestination.isError && ( + + + Failed to update notification destination: +
+ {parseError(updateDestination.error)?.message} +
+
+ )} + +
+ + +
+
+
+
+ ); +} diff --git a/app/client/modules/notifications/routes/notification-details.tsx b/app/client/modules/notifications/routes/notification-details.tsx index ca9d0222..d2046657 100644 --- a/app/client/modules/notifications/routes/notification-details.tsx +++ b/app/client/modules/notifications/routes/notification-details.tsx @@ -1,11 +1,10 @@ import { useMutation, useSuspenseQuery } from "@tanstack/react-query"; import { toast } from "sonner"; -import { useState, useId } from "react"; +import { useState } from "react"; import { deleteNotificationDestinationMutation, getNotificationDestinationOptions, testNotificationDestinationMutation, - updateNotificationDestinationMutation, } from "~/client/api-client/@tanstack/react-query.gen"; import { Button } from "~/client/components/ui/button"; import { @@ -22,20 +21,161 @@ import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, + DropdownMenuSeparator, DropdownMenuTrigger, } from "~/client/components/ui/dropdown-menu"; +import { Badge } from "~/client/components/ui/badge"; +import { Card, CardTitle } from "~/client/components/ui/card"; +import { Separator } from "~/client/components/ui/separator"; import { parseError } from "~/client/lib/errors"; import { cn } from "~/client/lib/utils"; -import { Card, CardContent, CardHeader, CardTitle } from "~/client/components/ui/card"; -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 { + Bell, + Bot, + ChevronDown, + Globe, + Hash, + Key, + Link, + Lock, + Mail, + AtSign, + MessageSquare, + Pencil, + Server, + Settings, + Shield, + Smile, + TestTube2, + Trash2, + Users, +} from "lucide-react"; import { useNavigate } from "@tanstack/react-router"; +import type { GetNotificationDestinationResponse } from "~/client/api-client/types.gen"; +import { useTimeFormat } from "~/client/lib/datetime"; + +type NotificationConfig = GetNotificationDestinationResponse["config"]; +type Props = { + icon: React.ReactNode; + label: string; + value: string; + mono?: boolean; +}; + +function ConfigRow({ icon, label, value, mono }: Props) { + return ( +
+ {icon} + {label} + {value} +
+ ); +} + +function NotificationConfigRows({ config }: { config: NotificationConfig }) { + switch (config.type) { + case "slack": + return ( + <> + } label="Webhook URL" value={config.webhookUrl} mono /> + } label="Channel" value={config.channel || "—"} /> + } label="Bot Username" value={config.username || "—"} /> + } label="Icon Emoji" value={config.iconEmoji || "—"} /> + + ); + case "discord": + return ( + <> + } label="Webhook URL" value={config.webhookUrl} mono /> + } label="Username" value={config.username || "—"} /> + } label="Avatar URL" value={config.avatarUrl || "—"} mono /> + } label="Thread ID" value={config.threadId || "—"} /> + + ); + case "email": + return ( + <> + } label="SMTP Host" value={config.smtpHost} mono /> + } label="SMTP Port" value={String(config.smtpPort)} /> + } label="Username" value={config.username || "—"} /> + } label="Password" value={config.password || "—"} /> + } label="From" value={config.from} /> + {config.fromName && ( + } label="From Name" value={config.fromName} /> + )} + } label="To" value={config.to.join(", ")} /> + } label="TLS" value={config.useTLS ? "Enabled" : "Disabled"} /> + + ); + case "gotify": + return ( + <> + } label="Server URL" value={config.serverUrl} mono /> + } label="Token" value={config.token} mono /> + } label="Path" value={config.path || "—"} mono /> + } label="Priority" value={String(config.priority)} /> + + ); + case "ntfy": + return ( + <> + } + label="Server URL" + value={config.serverUrl || "Default (ntfy.sh)"} + mono + /> + } label="Topic" value={config.topic} /> + } label="Priority" value={config.priority} /> + } label="Username" value={config.username || "—"} /> + } label="Password" value={config.password || "—"} /> + } label="Access Token" value={config.accessToken || "—"} /> + + ); + case "pushover": + return ( + <> + } label="User Key" value={config.userKey} mono /> + } label="API Token" value={config.apiToken} mono /> + } label="Devices" value={config.devices || "—"} /> + } label="Priority" value={String(config.priority)} /> + + ); + case "telegram": + return ( + <> + } label="Bot Token" value={config.botToken} mono /> + } label="Chat ID" value={config.chatId} mono /> + } label="Thread ID" value={config.threadId || "—"} /> + + ); + case "generic": + return ( + <> + } label="URL" value={config.url} mono /> + } label="Method" value={config.method} /> + } label="Content Type" value={config.contentType || "—"} /> + } + label="JSON Mode" + value={config.useJson ? "Enabled" : "Disabled"} + /> + } label="Title Key" value={config.titleKey || "—"} mono /> + } label="Message Key" value={config.messageKey || "—"} mono /> + {config.headers?.map((h, i) => ( + } label={`Header ${i + 1}`} value={h} mono /> + ))} + + ); + case "custom": + return } label="Shoutrrr URL" value={config.shoutrrrUrl} mono />; + } +} export function NotificationDetailsPage({ notificationId }: { notificationId: string }) { const navigate = useNavigate(); - const formId = useId(); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); + const { formatDateTime } = useTimeFormat(); const { data } = useSuspenseQuery({ ...getNotificationDestinationOptions({ path: { id: notificationId } }), @@ -54,18 +194,6 @@ export function NotificationDetailsPage({ notificationId }: { notificationId: st }, }); - const updateDestination = useMutation({ - ...updateNotificationDestinationMutation(), - onSuccess: () => { - toast.success("Notification destination updated successfully"); - }, - onError: (error) => { - toast.error("Failed to update notification destination", { - description: parseError(error)?.message, - }); - }, - }); - const testDestination = useMutation({ ...testNotificationDestinationMutation(), onSuccess: () => { @@ -83,101 +211,86 @@ export function NotificationDetailsPage({ notificationId }: { notificationId: st deleteDestination.mutate({ path: { id: String(data.id) } }); }; - const handleSubmit = (values: NotificationFormValues) => { - updateDestination.mutate({ - path: { id: String(data.id) }, - body: { - name: values.name, - config: values, - }, - }); - }; - const handleTest = () => { testDestination.mutate({ path: { id: String(data.id) } }); }; return ( <> -
-
- - {data.enabled ? "Enabled" : "Disabled"} - - {data.type} -
-
- - - - - - - setShowDeleteConfirm(true)} - disabled={deleteDestination.isPending} - > - - Delete - - - -
-
- - - -
-
- +
+ +
+
+
+ +
+
+
+

{data.name}

+ + + + {data.enabled ? "Enabled" : "Disabled"} + + + {data.type} + +
+

Created {formatDateTime(data.createdAt)}

+
+
+
+ + + + + + + navigate({ to: `/notifications/${data.id}/edit` })}> + + Edit + + + setShowDeleteConfirm(true)} + disabled={deleteDestination.isPending} + > + + Delete + + +
- {data.name}
- - - {updateDestination.isError && ( - - - Failed to update notification destination: -
- {parseError(updateDestination.error)?.message} -
-
- )} - -
- + + + + + + Configuration + +
+
- -
+ +
diff --git a/app/routeTree.gen.ts b/app/routeTree.gen.ts index f490814f..2e8c12b3 100644 --- a/app/routeTree.gen.ts +++ b/app/routeTree.gen.ts @@ -25,15 +25,16 @@ import { Route as dashboardAdminIndexRouteImport } from './routes/(dashboard)/ad import { Route as dashboardVolumesCreateRouteImport } from './routes/(dashboard)/volumes/create' 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 dashboardNotificationsNotificationIdIndexRouteImport } from './routes/(dashboard)/notifications/$notificationId/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 dashboardNotificationsNotificationIdEditRouteImport } from './routes/(dashboard)/notifications/$notificationId/edit' import { Route as dashboardBackupsBackupIdEditRouteImport } from './routes/(dashboard)/backups/$backupId/edit' import { Route as dashboardRepositoriesRepositoryIdSnapshotIdIndexRouteImport } from './routes/(dashboard)/repositories/$repositoryId/$snapshotId/index' import { Route as dashboardRepositoriesRepositoryIdSnapshotIdRestoreRouteImport } from './routes/(dashboard)/repositories/$repositoryId/$snapshotId/restore' @@ -121,12 +122,6 @@ const dashboardNotificationsCreateRoute = path: '/notifications/create', getParentRoute: () => dashboardRouteRoute, } as any) -const dashboardNotificationsNotificationIdRoute = - dashboardNotificationsNotificationIdRouteImport.update({ - id: '/notifications/$notificationId', - path: '/notifications/$notificationId', - getParentRoute: () => dashboardRouteRoute, - } as any) const dashboardBackupsCreateRoute = dashboardBackupsCreateRouteImport.update({ id: '/backups/create', path: '/backups/create', @@ -149,6 +144,12 @@ const dashboardRepositoriesRepositoryIdIndexRoute = path: '/repositories/$repositoryId/', getParentRoute: () => dashboardRouteRoute, } as any) +const dashboardNotificationsNotificationIdIndexRoute = + dashboardNotificationsNotificationIdIndexRouteImport.update({ + id: '/notifications/$notificationId/', + path: '/notifications/$notificationId/', + getParentRoute: () => dashboardRouteRoute, + } as any) const dashboardBackupsBackupIdIndexRoute = dashboardBackupsBackupIdIndexRouteImport.update({ id: '/backups/$backupId/', @@ -172,6 +173,12 @@ const dashboardRepositoriesRepositoryIdEditRoute = path: '/repositories/$repositoryId/edit', getParentRoute: () => dashboardRouteRoute, } as any) +const dashboardNotificationsNotificationIdEditRoute = + dashboardNotificationsNotificationIdEditRouteImport.update({ + id: '/notifications/$notificationId/edit', + path: '/notifications/$notificationId/edit', + getParentRoute: () => dashboardRouteRoute, + } as any) const dashboardBackupsBackupIdEditRoute = dashboardBackupsBackupIdEditRouteImport.update({ id: '/backups/$backupId/edit', @@ -205,7 +212,6 @@ export interface FileRoutesByFullPath { '/api/$': typeof ApiSplatRoute '/login/error': typeof authLoginErrorRoute '/backups/create': typeof dashboardBackupsCreateRoute - '/notifications/$notificationId': typeof dashboardNotificationsNotificationIdRoute '/notifications/create': typeof dashboardNotificationsCreateRoute '/repositories/create': typeof dashboardRepositoriesCreateRoute '/volumes/create': typeof dashboardVolumesCreateRoute @@ -216,10 +222,12 @@ export interface FileRoutesByFullPath { '/settings/': typeof dashboardSettingsIndexRoute '/volumes/': typeof dashboardVolumesIndexRoute '/backups/$backupId/edit': typeof dashboardBackupsBackupIdEditRoute + '/notifications/$notificationId/edit': typeof dashboardNotificationsNotificationIdEditRoute '/repositories/$repositoryId/edit': typeof dashboardRepositoriesRepositoryIdEditRoute '/settings/sso/new': typeof dashboardSettingsSsoNewRoute '/volumes/$volumeId/edit': typeof dashboardVolumesVolumeIdEditRoute '/backups/$backupId/': typeof dashboardBackupsBackupIdIndexRoute + '/notifications/$notificationId/': typeof dashboardNotificationsNotificationIdIndexRoute '/repositories/$repositoryId/': typeof dashboardRepositoriesRepositoryIdIndexRoute '/volumes/$volumeId/': typeof dashboardVolumesVolumeIdIndexRoute '/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute @@ -234,7 +242,6 @@ export interface FileRoutesByTo { '/api/$': typeof ApiSplatRoute '/login/error': typeof authLoginErrorRoute '/backups/create': typeof dashboardBackupsCreateRoute - '/notifications/$notificationId': typeof dashboardNotificationsNotificationIdRoute '/notifications/create': typeof dashboardNotificationsCreateRoute '/repositories/create': typeof dashboardRepositoriesCreateRoute '/volumes/create': typeof dashboardVolumesCreateRoute @@ -245,10 +252,12 @@ export interface FileRoutesByTo { '/settings': typeof dashboardSettingsIndexRoute '/volumes': typeof dashboardVolumesIndexRoute '/backups/$backupId/edit': typeof dashboardBackupsBackupIdEditRoute + '/notifications/$notificationId/edit': typeof dashboardNotificationsNotificationIdEditRoute '/repositories/$repositoryId/edit': typeof dashboardRepositoriesRepositoryIdEditRoute '/settings/sso/new': typeof dashboardSettingsSsoNewRoute '/volumes/$volumeId/edit': typeof dashboardVolumesVolumeIdEditRoute '/backups/$backupId': typeof dashboardBackupsBackupIdIndexRoute + '/notifications/$notificationId': typeof dashboardNotificationsNotificationIdIndexRoute '/repositories/$repositoryId': typeof dashboardRepositoriesRepositoryIdIndexRoute '/volumes/$volumeId': typeof dashboardVolumesVolumeIdIndexRoute '/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute @@ -266,7 +275,6 @@ export interface FileRoutesById { '/api/$': typeof ApiSplatRoute '/(auth)/login/error': typeof authLoginErrorRoute '/(dashboard)/backups/create': typeof dashboardBackupsCreateRoute - '/(dashboard)/notifications/$notificationId': typeof dashboardNotificationsNotificationIdRoute '/(dashboard)/notifications/create': typeof dashboardNotificationsCreateRoute '/(dashboard)/repositories/create': typeof dashboardRepositoriesCreateRoute '/(dashboard)/volumes/create': typeof dashboardVolumesCreateRoute @@ -277,10 +285,12 @@ export interface FileRoutesById { '/(dashboard)/settings/': typeof dashboardSettingsIndexRoute '/(dashboard)/volumes/': typeof dashboardVolumesIndexRoute '/(dashboard)/backups/$backupId/edit': typeof dashboardBackupsBackupIdEditRoute + '/(dashboard)/notifications/$notificationId/edit': typeof dashboardNotificationsNotificationIdEditRoute '/(dashboard)/repositories/$repositoryId/edit': typeof dashboardRepositoriesRepositoryIdEditRoute '/(dashboard)/settings/sso/new': typeof dashboardSettingsSsoNewRoute '/(dashboard)/volumes/$volumeId/edit': typeof dashboardVolumesVolumeIdEditRoute '/(dashboard)/backups/$backupId/': typeof dashboardBackupsBackupIdIndexRoute + '/(dashboard)/notifications/$notificationId/': typeof dashboardNotificationsNotificationIdIndexRoute '/(dashboard)/repositories/$repositoryId/': typeof dashboardRepositoriesRepositoryIdIndexRoute '/(dashboard)/volumes/$volumeId/': typeof dashboardVolumesVolumeIdIndexRoute '/(dashboard)/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute @@ -297,7 +307,6 @@ export interface FileRouteTypes { | '/api/$' | '/login/error' | '/backups/create' - | '/notifications/$notificationId' | '/notifications/create' | '/repositories/create' | '/volumes/create' @@ -308,10 +317,12 @@ export interface FileRouteTypes { | '/settings/' | '/volumes/' | '/backups/$backupId/edit' + | '/notifications/$notificationId/edit' | '/repositories/$repositoryId/edit' | '/settings/sso/new' | '/volumes/$volumeId/edit' | '/backups/$backupId/' + | '/notifications/$notificationId/' | '/repositories/$repositoryId/' | '/volumes/$volumeId/' | '/backups/$backupId/$snapshotId/restore' @@ -326,7 +337,6 @@ export interface FileRouteTypes { | '/api/$' | '/login/error' | '/backups/create' - | '/notifications/$notificationId' | '/notifications/create' | '/repositories/create' | '/volumes/create' @@ -337,10 +347,12 @@ export interface FileRouteTypes { | '/settings' | '/volumes' | '/backups/$backupId/edit' + | '/notifications/$notificationId/edit' | '/repositories/$repositoryId/edit' | '/settings/sso/new' | '/volumes/$volumeId/edit' | '/backups/$backupId' + | '/notifications/$notificationId' | '/repositories/$repositoryId' | '/volumes/$volumeId' | '/backups/$backupId/$snapshotId/restore' @@ -357,7 +369,6 @@ export interface FileRouteTypes { | '/api/$' | '/(auth)/login/error' | '/(dashboard)/backups/create' - | '/(dashboard)/notifications/$notificationId' | '/(dashboard)/notifications/create' | '/(dashboard)/repositories/create' | '/(dashboard)/volumes/create' @@ -368,10 +379,12 @@ export interface FileRouteTypes { | '/(dashboard)/settings/' | '/(dashboard)/volumes/' | '/(dashboard)/backups/$backupId/edit' + | '/(dashboard)/notifications/$notificationId/edit' | '/(dashboard)/repositories/$repositoryId/edit' | '/(dashboard)/settings/sso/new' | '/(dashboard)/volumes/$volumeId/edit' | '/(dashboard)/backups/$backupId/' + | '/(dashboard)/notifications/$notificationId/' | '/(dashboard)/repositories/$repositoryId/' | '/(dashboard)/volumes/$volumeId/' | '/(dashboard)/backups/$backupId/$snapshotId/restore' @@ -500,13 +513,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dashboardNotificationsCreateRouteImport parentRoute: typeof dashboardRouteRoute } - '/(dashboard)/notifications/$notificationId': { - id: '/(dashboard)/notifications/$notificationId' - path: '/notifications/$notificationId' - fullPath: '/notifications/$notificationId' - preLoaderRoute: typeof dashboardNotificationsNotificationIdRouteImport - parentRoute: typeof dashboardRouteRoute - } '/(dashboard)/backups/create': { id: '/(dashboard)/backups/create' path: '/backups/create' @@ -535,6 +541,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dashboardRepositoriesRepositoryIdIndexRouteImport parentRoute: typeof dashboardRouteRoute } + '/(dashboard)/notifications/$notificationId/': { + id: '/(dashboard)/notifications/$notificationId/' + path: '/notifications/$notificationId' + fullPath: '/notifications/$notificationId/' + preLoaderRoute: typeof dashboardNotificationsNotificationIdIndexRouteImport + parentRoute: typeof dashboardRouteRoute + } '/(dashboard)/backups/$backupId/': { id: '/(dashboard)/backups/$backupId/' path: '/backups/$backupId' @@ -563,6 +576,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof dashboardRepositoriesRepositoryIdEditRouteImport parentRoute: typeof dashboardRouteRoute } + '/(dashboard)/notifications/$notificationId/edit': { + id: '/(dashboard)/notifications/$notificationId/edit' + path: '/notifications/$notificationId/edit' + fullPath: '/notifications/$notificationId/edit' + preLoaderRoute: typeof dashboardNotificationsNotificationIdEditRouteImport + parentRoute: typeof dashboardRouteRoute + } '/(dashboard)/backups/$backupId/edit': { id: '/(dashboard)/backups/$backupId/edit' path: '/backups/$backupId/edit' @@ -624,7 +644,6 @@ const authRouteRouteWithChildren = authRouteRoute._addFileChildren( interface dashboardRouteRouteChildren { dashboardBackupsCreateRoute: typeof dashboardBackupsCreateRoute - dashboardNotificationsNotificationIdRoute: typeof dashboardNotificationsNotificationIdRoute dashboardNotificationsCreateRoute: typeof dashboardNotificationsCreateRoute dashboardRepositoriesCreateRoute: typeof dashboardRepositoriesCreateRoute dashboardVolumesCreateRoute: typeof dashboardVolumesCreateRoute @@ -635,10 +654,12 @@ interface dashboardRouteRouteChildren { dashboardSettingsIndexRoute: typeof dashboardSettingsIndexRoute dashboardVolumesIndexRoute: typeof dashboardVolumesIndexRoute dashboardBackupsBackupIdEditRoute: typeof dashboardBackupsBackupIdEditRoute + dashboardNotificationsNotificationIdEditRoute: typeof dashboardNotificationsNotificationIdEditRoute dashboardRepositoriesRepositoryIdEditRoute: typeof dashboardRepositoriesRepositoryIdEditRoute dashboardSettingsSsoNewRoute: typeof dashboardSettingsSsoNewRoute dashboardVolumesVolumeIdEditRoute: typeof dashboardVolumesVolumeIdEditRoute dashboardBackupsBackupIdIndexRoute: typeof dashboardBackupsBackupIdIndexRoute + dashboardNotificationsNotificationIdIndexRoute: typeof dashboardNotificationsNotificationIdIndexRoute dashboardRepositoriesRepositoryIdIndexRoute: typeof dashboardRepositoriesRepositoryIdIndexRoute dashboardVolumesVolumeIdIndexRoute: typeof dashboardVolumesVolumeIdIndexRoute dashboardBackupsBackupIdSnapshotIdRestoreRoute: typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute @@ -648,8 +669,6 @@ interface dashboardRouteRouteChildren { const dashboardRouteRouteChildren: dashboardRouteRouteChildren = { dashboardBackupsCreateRoute: dashboardBackupsCreateRoute, - dashboardNotificationsNotificationIdRoute: - dashboardNotificationsNotificationIdRoute, dashboardNotificationsCreateRoute: dashboardNotificationsCreateRoute, dashboardRepositoriesCreateRoute: dashboardRepositoriesCreateRoute, dashboardVolumesCreateRoute: dashboardVolumesCreateRoute, @@ -660,11 +679,15 @@ const dashboardRouteRouteChildren: dashboardRouteRouteChildren = { dashboardSettingsIndexRoute: dashboardSettingsIndexRoute, dashboardVolumesIndexRoute: dashboardVolumesIndexRoute, dashboardBackupsBackupIdEditRoute: dashboardBackupsBackupIdEditRoute, + dashboardNotificationsNotificationIdEditRoute: + dashboardNotificationsNotificationIdEditRoute, dashboardRepositoriesRepositoryIdEditRoute: dashboardRepositoriesRepositoryIdEditRoute, dashboardSettingsSsoNewRoute: dashboardSettingsSsoNewRoute, dashboardVolumesVolumeIdEditRoute: dashboardVolumesVolumeIdEditRoute, dashboardBackupsBackupIdIndexRoute: dashboardBackupsBackupIdIndexRoute, + dashboardNotificationsNotificationIdIndexRoute: + dashboardNotificationsNotificationIdIndexRoute, dashboardRepositoriesRepositoryIdIndexRoute: dashboardRepositoriesRepositoryIdIndexRoute, dashboardVolumesVolumeIdIndexRoute: dashboardVolumesVolumeIdIndexRoute, diff --git a/app/routes/(dashboard)/notifications/$notificationId/edit.tsx b/app/routes/(dashboard)/notifications/$notificationId/edit.tsx new file mode 100644 index 00000000..17ee3dc2 --- /dev/null +++ b/app/routes/(dashboard)/notifications/$notificationId/edit.tsx @@ -0,0 +1,38 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { getNotificationDestinationOptions } from "~/client/api-client/@tanstack/react-query.gen"; +import { EditNotificationPage } from "~/client/modules/notifications/routes/edit-notification"; + +export const Route = createFileRoute("/(dashboard)/notifications/$notificationId/edit")({ + component: RouteComponent, + errorComponent: () =>
Failed to load notification
, + loader: async ({ params, context }) => { + const notification = await context.queryClient.ensureQueryData({ + ...getNotificationDestinationOptions({ path: { id: params.notificationId } }), + }); + + return notification; + }, + staticData: { + breadcrumb: (match) => [ + { label: "Notifications", href: "/notifications" }, + { + label: match.loaderData?.name || "Notification Details", + href: `/notifications/${match.params.notificationId}`, + }, + { label: "Edit" }, + ], + }, + head: ({ loaderData }) => ({ + meta: [ + { title: `Zerobyte - Edit ${loaderData?.name}` }, + { + name: "description", + content: "Edit notification destination settings.", + }, + ], + }), +}); + +function RouteComponent() { + return ; +} diff --git a/app/routes/(dashboard)/notifications/$notificationId.tsx b/app/routes/(dashboard)/notifications/$notificationId/index.tsx similarity index 92% rename from app/routes/(dashboard)/notifications/$notificationId.tsx rename to app/routes/(dashboard)/notifications/$notificationId/index.tsx index ceab9f70..4b67f396 100644 --- a/app/routes/(dashboard)/notifications/$notificationId.tsx +++ b/app/routes/(dashboard)/notifications/$notificationId/index.tsx @@ -2,7 +2,7 @@ import { createFileRoute } from "@tanstack/react-router"; import { getNotificationDestinationOptions } from "~/client/api-client/@tanstack/react-query.gen"; import { NotificationDetailsPage } from "~/client/modules/notifications/routes/notification-details"; -export const Route = createFileRoute("/(dashboard)/notifications/$notificationId")({ +export const Route = createFileRoute("/(dashboard)/notifications/$notificationId/")({ component: RouteComponent, errorComponent: () =>
Failed to load notification
, loader: async ({ params, context }) => { @@ -23,7 +23,7 @@ export const Route = createFileRoute("/(dashboard)/notifications/$notificationId { title: `Zerobyte - ${loaderData?.name}` }, { name: "description", - content: "View and edit notification destination settings.", + content: "View notification destination settings.", }, ], }),