feat: integrate sonner for toast notifications and enhance UI feedback
Added the sonner library for toast notifications across various components, improving user feedback for actions such as saving settings, syncing rules, and handling errors. Updated the layout to include a Toaster component for consistent notification display. Refactored alert messages in the backups, gre, and filters pages to utilize the new notification system, enhancing overall user experience.
This commit is contained in:
+10
-22
@@ -13,7 +13,7 @@ import {
|
||||
} from "@/components/ui/sheet"
|
||||
import {
|
||||
RefreshCwIcon, PlusIcon, DownloadIcon, Trash2Icon,
|
||||
HardDriveIcon, ClockIcon, ServerIcon, CheckCircleIcon,
|
||||
HardDriveIcon, ClockIcon, ServerIcon,
|
||||
FolderIcon,
|
||||
} from "lucide-react"
|
||||
import { cn } from "@/lib/utils"
|
||||
@@ -21,6 +21,7 @@ import { useDataSource } from "@/lib/data-source"
|
||||
import { listServers } from "@/shared/api/servers"
|
||||
import { toFrontendServer } from "@/entities/server/model/mappers"
|
||||
import { createBackupsAsync, deleteBackup, getBackupJob, listBackups, type BackupItem } from "@/shared/api/backups"
|
||||
import { toast } from "sonner"
|
||||
|
||||
// ─── small UI helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
@@ -138,11 +139,8 @@ export default function BackupsPage() {
|
||||
})
|
||||
}
|
||||
|
||||
// Settings saved flash
|
||||
const [saved, setSaved] = useState(false)
|
||||
function handleSave() {
|
||||
setSaved(true)
|
||||
setTimeout(() => setSaved(false), 2000)
|
||||
toast.success("Настройки сохранены")
|
||||
}
|
||||
|
||||
// Manual backup sheet
|
||||
@@ -212,6 +210,7 @@ export default function BackupsPage() {
|
||||
|
||||
useEffect(() => {
|
||||
if (!backupJobId) return
|
||||
toast.info("Бэкап выполняется в фоне...")
|
||||
let cancelled = false
|
||||
const timer = setInterval(() => {
|
||||
void (async () => {
|
||||
@@ -223,6 +222,8 @@ export default function BackupsPage() {
|
||||
setBackupJobId(null)
|
||||
if (job.failures.length > 0) {
|
||||
setOpError(`Часть бэкапов не создалась: ${job.failures.map((f) => `${f.serverId}: ${f.error}`).join("; ")}`)
|
||||
} else {
|
||||
toast.success("Бэкап успешно завершён")
|
||||
}
|
||||
await loadLive()
|
||||
} else if (job.status === "failed") {
|
||||
@@ -244,6 +245,10 @@ export default function BackupsPage() {
|
||||
}
|
||||
}, [backendUrl, backupJobId, loadLive])
|
||||
|
||||
useEffect(() => {
|
||||
if (opError) toast.error(opError)
|
||||
}, [opError])
|
||||
|
||||
// Delete backup
|
||||
async function handleDelete(id: string) {
|
||||
setOpBusy(true)
|
||||
@@ -367,17 +372,6 @@ export default function BackupsPage() {
|
||||
Изменить настройки →
|
||||
</button>
|
||||
</div>
|
||||
{opError && (
|
||||
<div className="rounded-md border border-red-500/30 bg-red-500/10 px-3 py-2 text-sm text-red-300">
|
||||
{opError}
|
||||
</div>
|
||||
)}
|
||||
{backupJobId && (
|
||||
<div className="rounded-md border border-blue-500/30 bg-blue-500/10 px-3 py-2 text-sm text-blue-300">
|
||||
Бэкап выполняется в фоне...
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex items-center gap-1 border-b border-border">
|
||||
{([
|
||||
@@ -720,12 +714,6 @@ export default function BackupsPage() {
|
||||
<Button onClick={handleSave} className="gap-2">
|
||||
Сохранить настройки
|
||||
</Button>
|
||||
{saved && (
|
||||
<span className="flex items-center gap-1.5 text-sm text-emerald-500">
|
||||
<CheckCircleIcon className="size-4" />
|
||||
Настройки сохранены
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user