diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx index cd62aff..33d2d2a 100644 --- a/src/components/ui/sonner.tsx +++ b/src/components/ui/sonner.tsx @@ -1,5 +1,5 @@ import { useTheme } from "next-themes" -import { Toaster as Sonner, ToasterProps } from "sonner" +import { Toaster as Sonner, type ToasterProps } from "sonner" const Toaster = ({ ...props }: ToasterProps) => { const { theme = "system" } = useTheme() diff --git a/src/pages/AccountPage.tsx b/src/pages/AccountPage.tsx index e2aee0c..f80e194 100644 --- a/src/pages/AccountPage.tsx +++ b/src/pages/AccountPage.tsx @@ -9,6 +9,7 @@ import { } from '@/components/ui/card' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' +import { toast } from 'sonner' import { useAuth } from '@/hooks/use-auth' import { apiService } from '@/services/api' import { useEffect, useRef, useState } from 'react' @@ -78,13 +79,16 @@ export function AccountPage() { if (response.ok) { setMessage('Profile updated successfully!') + toast.success('Profile updated successfully!') await refreshUser() // Refresh user data in context } else { setError(data.error || 'Failed to update profile') + toast.error(data.error || 'Failed to update profile') } } catch (error) { console.error('Failed to update profile:', error) setError('Failed to update profile') + toast.error('Failed to update profile') } finally { setIsLoading(false) } @@ -108,6 +112,7 @@ export function AccountPage() { setMessage( "New API token generated successfully! Copy it now - it won't be shown again.", ) + toast.success('New API token generated successfully!') await refreshUser() // Refresh user data // Auto-select the token text for easy copying @@ -119,10 +124,12 @@ export function AccountPage() { }, 100) } else { setError(data.error || 'Failed to regenerate API token') + toast.error(data.error || 'Failed to regenerate API token') } } catch (error) { console.error('Failed to regenerate API token:', error) setError('Failed to regenerate API token') + toast.error('Failed to regenerate API token') } finally { setIsLoading(false) } @@ -134,9 +141,11 @@ export function AccountPage() { try { await navigator.clipboard.writeText(apiToken) setMessage('API token copied to clipboard!') + toast.success('API token copied to clipboard!') } catch (error) { console.error('Failed to copy token:', error) setError('Failed to copy token to clipboard') + toast.error('Failed to copy token to clipboard') } } @@ -149,6 +158,7 @@ export function AccountPage() { const handleThemeChange = (newTheme: string) => { setTheme(newTheme as 'light' | 'dark' | 'system') setMessage('Theme updated successfully!') + toast.success('Theme updated successfully!') } const handlePasswordUpdate = async () => { @@ -190,7 +200,9 @@ export function AccountPage() { const data = await response.json() if (response.ok) { - setMessage(loggedInViaPassword ? 'Password changed successfully!' : 'Password set successfully!') + const successMessage = loggedInViaPassword ? 'Password changed successfully!' : 'Password set successfully!' + setMessage(successMessage) + toast.success(successMessage) setCurrentPassword('') setNewPassword('') setConfirmPassword('') @@ -198,10 +210,12 @@ export function AccountPage() { await refreshUser() // Refresh user data } else { setError(data.error || 'Failed to update password') + toast.error(data.error || 'Failed to update password') } } catch (error) { console.error('Failed to update password:', error) setError('Failed to update password') + toast.error('Failed to update password') } finally { setIsLoading(false) } diff --git a/src/pages/AdminSoundsPage.tsx b/src/pages/AdminSoundsPage.tsx index 9ab7d12..253ff65 100644 --- a/src/pages/AdminSoundsPage.tsx +++ b/src/pages/AdminSoundsPage.tsx @@ -11,6 +11,7 @@ import { Database, Zap } from 'lucide-react'; +import { toast } from 'sonner'; import { apiService } from '@/services/api'; interface Sound { @@ -78,6 +79,7 @@ export function AdminSoundsPage() { setTotalPages(data.pagination?.pages || 1); } catch (err) { setError('Failed to load sounds'); + toast.error('Failed to load sounds'); console.error('Error fetching sounds:', err); } finally { setLoading(false); @@ -112,13 +114,15 @@ export function AdminSoundsPage() { const data = await response.json(); if (response.ok) { - alert(`Scan completed: ${data.files_added} new sounds added, ${data.files_skipped} skipped`); + toast.success(`Scan completed: ${data.files_added} new sounds added, ${data.files_skipped} skipped`); await fetchData(); } else { setError(data.error || 'Scan failed'); + toast.error(data.error || 'Scan failed'); } } catch (err) { setError('Failed to scan sounds'); + toast.error('Failed to scan sounds'); console.error('Error scanning sounds:', err); } finally { setScanning(false); @@ -136,13 +140,15 @@ export function AdminSoundsPage() { const data = await response.json(); if (response.ok) { - alert(`Normalization completed: ${data.successful} successful, ${data.failed} failed, ${data.skipped} skipped`); + toast.success(`Normalization completed: ${data.successful} successful, ${data.failed} failed, ${data.skipped} skipped`); await fetchData(); } else { setError(data.error || 'Normalization failed'); + toast.error(data.error || 'Normalization failed'); } } catch (err) { setError('Failed to normalize sounds'); + toast.error('Failed to normalize sounds'); console.error('Error normalizing sounds:', err); } finally { setNormalizing(false); @@ -158,36 +164,49 @@ export function AdminSoundsPage() { const data = await response.json(); if (response.ok) { - alert(`Sound normalized successfully`); + toast.success('Sound normalized successfully'); await fetchData(); } else { - alert(`Normalization failed: ${data.error}`); + toast.error(`Normalization failed: ${data.error}`); } } catch (err) { - alert('Failed to normalize sound'); + toast.error('Failed to normalize sound'); console.error('Error normalizing sound:', err); } }; const handleDeleteSound = async (soundId: number, soundName: string) => { - if (!confirm(`Are you sure you want to delete "${soundName}"?`)) { - return; - } + const confirmDelete = () => { + toast.promise( + (async () => { + const response = await apiService.delete(`/api/admin/sounds/${soundId}`); + const data = await response.json(); + + if (response.ok) { + await fetchData(); + return `Sound "${soundName}" deleted successfully`; + } else { + throw new Error(data.error || 'Delete failed'); + } + })(), + { + loading: `Deleting "${soundName}"...`, + success: (message) => message, + error: (err) => `Failed to delete sound: ${err.message}`, + } + ); + }; - try { - const response = await apiService.delete(`/api/admin/sounds/${soundId}`); - const data = await response.json(); - - if (response.ok) { - alert('Sound deleted successfully'); - await fetchData(); - } else { - alert(`Delete failed: ${data.error}`); - } - } catch (err) { - alert('Failed to delete sound'); - console.error('Error deleting sound:', err); - } + toast(`Are you sure you want to delete "${soundName}"?`, { + action: { + label: 'Delete', + onClick: confirmDelete, + }, + cancel: { + label: 'Cancel', + onClick: () => {}, + }, + }); }; const formatFileSize = (bytes: number) => { diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index 7ef8bf8..9736df5 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -4,6 +4,7 @@ import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { toast } from 'sonner' import { useAuth } from '@/hooks/use-auth' import { authService } from '@/services/auth' @@ -60,9 +61,12 @@ export function LoginPage() { try { await login(email, password) + toast.success('Login successful! Welcome back.') navigate('/') } catch (error) { - setError(error instanceof Error ? error.message : 'Login failed') + const errorMessage = error instanceof Error ? error.message : 'Login failed' + setError(errorMessage) + toast.error(errorMessage) } finally { setLoading(false) } diff --git a/src/pages/RegisterPage.tsx b/src/pages/RegisterPage.tsx index 5a7b6b5..8bed0e3 100644 --- a/src/pages/RegisterPage.tsx +++ b/src/pages/RegisterPage.tsx @@ -4,6 +4,7 @@ import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' +import { toast } from 'sonner' import { useAuth } from '@/hooks/use-auth' import { authService } from '@/services/auth' @@ -72,9 +73,12 @@ export function RegisterPage() { try { await register(formData.email, formData.password, formData.name) + toast.success('Account created successfully! Welcome!') navigate('/') } catch (error) { - setError(error instanceof Error ? error.message : 'Registration failed') + const errorMessage = error instanceof Error ? error.message : 'Registration failed' + setError(errorMessage) + toast.error(errorMessage) } finally { setLoading(false) } diff --git a/src/pages/SoundboardPage.tsx b/src/pages/SoundboardPage.tsx index cd003b9..dbcd4c3 100644 --- a/src/pages/SoundboardPage.tsx +++ b/src/pages/SoundboardPage.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { Play, Square, Volume2 } from 'lucide-react'; +import { toast } from 'sonner'; import { apiService } from '@/services/api'; interface Sound { @@ -87,6 +88,7 @@ export function SoundboardPage() { setSounds(data.sounds || []); } catch (err) { setError('Failed to load sounds'); + toast.error('Failed to load sounds'); console.error('Error fetching sounds:', err); } finally { setLoading(false); @@ -104,6 +106,7 @@ export function SoundboardPage() { }, 1000); } catch (err) { setError('Failed to play sound'); + toast.error('Failed to play sound'); console.error('Error playing sound:', err); setPlayingSound(null); } @@ -113,8 +116,10 @@ export function SoundboardPage() { try { await apiService.post('/api/soundboard/stop-all'); setPlayingSound(null); + toast.success('All sounds stopped'); } catch (err) { setError('Failed to stop sounds'); + toast.error('Failed to stop sounds'); console.error('Error stopping sounds:', err); } }; @@ -124,9 +129,10 @@ export function SoundboardPage() { const response = await apiService.post('/api/soundboard/force-stop'); const data = await response.json(); setPlayingSound(null); - alert(`Force stopped ${data.stopped_count} sound instances`); + toast.success(`Force stopped ${data.stopped_count} sound instances`); } catch (err) { setError('Failed to force stop sounds'); + toast.error('Failed to force stop sounds'); console.error('Error force stopping sounds:', err); } };