Add confirmation toasts to settings actions and fix webhook card spacing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 11:10:28 -05:00
co-authored by Claude Opus 4.6
parent 8487c3d7b1
commit f6ee32ca1e
3 changed files with 45 additions and 22 deletions
@@ -2,6 +2,7 @@
import { IconWebhook } from "@tabler/icons-react";
import { useState } from "react";
import { toast } from "sonner";
import {
deleteWebhookConnection,
regenerateWebhookToken,
@@ -22,6 +23,9 @@ export function IntegrationsSection({
connections.find((c) => c.provider === "jellyfin") ?? null;
async function handleSave(provider: "plex" | "jellyfin", username: string) {
const label = provider === "plex" ? "Plex" : "Jellyfin";
const isNew = !connections.find((c) => c.provider === provider);
try {
const result = await saveWebhookConnection(provider, username);
setConnections((prev) => {
const existing = prev.find((c) => c.provider === provider);
@@ -34,28 +38,42 @@ export function IntegrationsSection({
}
return [...prev, { ...result, recentEvents: [] }];
});
toast.success(isNew ? `${label} connected` : `${label} updated`);
} catch {
toast.error(`Failed to save ${label} connection`);
}
}
async function handleDelete(provider: "plex" | "jellyfin") {
const label = provider === "plex" ? "Plex" : "Jellyfin";
const previous = connections;
setConnections((prev) => prev.filter((c) => c.provider !== provider));
try {
await deleteWebhookConnection(provider);
toast.success(`${label} disconnected`);
} catch {
setConnections(previous);
toast.error(`Failed to disconnect ${label}`);
}
}
async function handleRegenerateToken(provider: "plex" | "jellyfin") {
const label = provider === "plex" ? "Plex" : "Jellyfin";
try {
const result = await regenerateWebhookToken(provider);
setConnections((prev) =>
prev.map((c) =>
c.provider === provider ? { ...c, token: result.token } : c,
),
);
toast.success(`${label} webhook URL regenerated`);
} catch {
toast.error(`Failed to regenerate ${label} URL`);
}
}
async function handleToggle(provider: "plex" | "jellyfin", enabled: boolean) {
const label = provider === "plex" ? "Plex" : "Jellyfin";
const previous = connections;
setConnections((prev) =>
prev.map((c) => (c.provider === provider ? { ...c, enabled } : c)),
@@ -67,8 +85,10 @@ export function IntegrationsSection({
conn?.mediaServerUsername ?? "",
enabled,
);
toast.success(`${label} webhook ${enabled ? "enabled" : "disabled"}`);
} catch {
setConnections(previous);
toast.error(`Failed to update ${label}`);
}
}
@@ -1,7 +1,8 @@
"use client";
import { IconShieldLock, IconUserPlus } from "@tabler/icons-react";
import { IconServerCog, IconUserPlus } from "@tabler/icons-react";
import { useState } from "react";
import { toast } from "sonner";
import {
Card,
CardContent,
@@ -27,8 +28,10 @@ export function ServerSection({
setToggling(true);
try {
await toggleRegistration(checked);
toast.success(checked ? "Registration opened" : "Registration closed");
} catch {
setRegistrationOpen(previous);
toast.error("Failed to update registration setting");
} finally {
setToggling(false);
}
@@ -37,12 +40,12 @@ export function ServerSection({
return (
<div>
<div className="mb-3 flex items-center gap-2">
<IconShieldLock size={16} className="text-muted-foreground" />
<IconServerCog size={16} className="text-muted-foreground" />
<h2 className="text-xs font-medium uppercase tracking-wider text-muted-foreground">
Server
</h2>
<span className="rounded-md bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium text-primary">
Admin
Admin only
</span>
</div>
<Card className="border-l-2 border-l-primary/30">
@@ -135,7 +135,7 @@ export function WebhookCard({
</CardContent>
<CollapsibleContent>
<CardContent className="space-y-3 pt-0">
<CardContent className="space-y-3 border-t border-border/30 pt-4">
{connection && (
<div className="flex items-center justify-between rounded-lg bg-muted/30 px-3 py-2">
<span className="text-xs text-muted-foreground">