refactor(frontend): replace alert() with modern toast notifications

Replaced all alert() calls with react-hot-toast notifications for better
user experience. Toast notifications are non-blocking, auto-dismiss, and
provide visual feedback with icons based on message type.

Changes:
- EventChatPage: Match request success/error toasts
- MatchChatPage: Video file selection and WebRTC connection error toasts
- MatchesPage: Match accept/reject action toasts
- RatePartnerPage: Rating submission and validation toasts
- VerifyEmailPage: Email verification sent toast
- ScheduleConfigSection: Schedule save success/error toasts
- MatchingConfigSection: Deadline save success/error toasts

All toast notifications use appropriate types (success, error, warning, info)
for better visual distinction and user feedback.
This commit is contained in:
Radosław Gierwiało
2025-12-05 22:09:37 +01:00
parent 3ae9fd149b
commit bb8a876ab0
7 changed files with 31 additions and 22 deletions

View File

@@ -1,4 +1,5 @@
import { useState } from 'react'; import { useState } from 'react';
import toast from 'react-hot-toast';
import { Video, Clock, Save, RefreshCw } from 'lucide-react'; import { Video, Clock, Save, RefreshCw } from 'lucide-react';
import { matchingAPI } from '../../services/api'; import { matchingAPI } from '../../services/api';
@@ -21,10 +22,11 @@ const MatchingConfigSection = ({ slug, event, onRefresh }) => {
setSavingDeadline(true); setSavingDeadline(true);
const deadline = deadlineInput ? new Date(deadlineInput).toISOString() : null; const deadline = deadlineInput ? new Date(deadlineInput).toISOString() : null;
await matchingAPI.setRegistrationDeadline(slug, deadline); await matchingAPI.setRegistrationDeadline(slug, deadline);
toast.success('Deadline saved successfully');
onRefresh?.(); onRefresh?.();
} catch (err) { } catch (err) {
console.error('Failed to save deadline:', err); console.error('Failed to save deadline:', err);
alert('Nie udalo sie zapisac deadline'); toast.error('Failed to save deadline');
} finally { } finally {
setSavingDeadline(false); setSavingDeadline(false);
} }

View File

@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import toast from 'react-hot-toast';
import { Layers, Plus, Trash2, Save, RefreshCw } from 'lucide-react'; import { Layers, Plus, Trash2, Save, RefreshCw } from 'lucide-react';
import { matchingAPI, divisionsAPI } from '../../services/api'; import { matchingAPI, divisionsAPI } from '../../services/api';
@@ -72,10 +73,11 @@ const ScheduleConfigSection = ({ slug, event, onRefresh }) => {
? { slots: scheduleSlots.filter(s => s.divisionIds.length > 0) } ? { slots: scheduleSlots.filter(s => s.divisionIds.length > 0) }
: null; : null;
await matchingAPI.setScheduleConfig(slug, scheduleConfig); await matchingAPI.setScheduleConfig(slug, scheduleConfig);
toast.success('Schedule saved successfully');
onRefresh?.(); onRefresh?.();
} catch (err) { } catch (err) {
console.error('Failed to save schedule:', err); console.error('Failed to save schedule:', err);
alert('Nie udalo sie zapisac harmonogramu'); toast.error('Failed to save schedule');
} finally { } finally {
setSavingSchedule(false); setSavingSchedule(false);
} }

View File

@@ -1,5 +1,6 @@
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from 'react';
import { useParams, useNavigate, useSearchParams, Link } from 'react-router-dom'; import { useParams, useNavigate, useSearchParams, Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import Layout from '../components/layout/Layout'; import Layout from '../components/layout/Layout';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { Send, UserPlus, Loader2, LogOut, AlertTriangle, QrCode, Edit2, Filter, X, MessageSquare, Users, Video } from 'lucide-react'; import { Send, UserPlus, Loader2, LogOut, AlertTriangle, QrCode, Edit2, Filter, X, MessageSquare, Users, Video } from 'lucide-react';
@@ -201,7 +202,7 @@ const EventChatPage = () => {
const result = await matchesAPI.createMatch(userId, slug); const result = await matchesAPI.createMatch(userId, slug);
// Show success message // Show success message
alert(`Match request sent successfully! The user will be notified.`); toast.success('Match request sent successfully! The user will be notified.');
// Optional: Navigate to matches page or refresh matches list // Optional: Navigate to matches page or refresh matches list
// For now, we just show a success message // For now, we just show a success message
@@ -210,13 +211,13 @@ const EventChatPage = () => {
// Show appropriate error message // Show appropriate error message
if (error.status === 400 && error.message.includes('already exists')) { if (error.status === 400 && error.message.includes('already exists')) {
alert('You already have a match request with this user.'); toast.error('You already have a match request with this user.');
} else if (error.status === 403) { } else if (error.status === 403) {
alert('You must be a participant of this event to send match requests.'); toast.error('You must be a participant of this event to send match requests.');
} else if (error.status === 404) { } else if (error.status === 404) {
alert('Event not found.'); toast.error('Event not found.');
} else { } else {
alert('Failed to send match request. Please try again.'); toast.error('Failed to send match request. Please try again.');
} }
} }
}; };
@@ -298,7 +299,7 @@ const EventChatPage = () => {
navigate('/events'); navigate('/events');
} catch (error) { } catch (error) {
console.error('Failed to leave event:', error); console.error('Failed to leave event:', error);
alert('Failed to leave event. Please try again.'); toast.error('Failed to leave event. Please try again.');
} finally { } finally {
setIsLeaving(false); setIsLeaving(false);
setShowLeaveModal(false); setShowLeaveModal(false);

View File

@@ -1,5 +1,6 @@
import { useState, useRef, useEffect } from 'react'; import { useState, useRef, useEffect } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom'; import { useParams, useNavigate, Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import Layout from '../components/layout/Layout'; import Layout from '../components/layout/Layout';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { matchesAPI } from '../services/api'; import { matchesAPI } from '../services/api';
@@ -73,7 +74,7 @@ const MatchChatPage = () => {
setMatch(result.data); setMatch(result.data);
} catch (error) { } catch (error) {
console.error('Failed to load match:', error); console.error('Failed to load match:', error);
alert('Failed to load match. Redirecting to matches page.'); toast.error('Failed to load match. Redirecting to matches page.');
navigate('/matches'); navigate('/matches');
} finally { } finally {
setLoading(false); setLoading(false);
@@ -97,7 +98,7 @@ const MatchChatPage = () => {
if (file && file.type.startsWith('video/')) { if (file && file.type.startsWith('video/')) {
setSelectedFile(file); setSelectedFile(file);
} else { } else {
alert('Please select a video file'); toast.error('Please select a video file');
} }
}; };
@@ -128,7 +129,7 @@ const MatchChatPage = () => {
try { try {
await waitForConnection; await waitForConnection;
} catch (error) { } catch (error) {
alert('Failed to establish connection: ' + error.message); toast.error('Failed to establish connection: ' + error.message);
return; return;
} }
} }

View File

@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import toast from 'react-hot-toast';
import Layout from '../components/layout/Layout'; import Layout from '../components/layout/Layout';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { matchesAPI } from '../services/api'; import { matchesAPI } from '../services/api';
@@ -80,10 +81,10 @@ const MatchesPage = () => {
await loadMatches(); await loadMatches();
// Show success message // Show success message
alert('Match accepted! You can now chat with your partner.'); toast.success('Match accepted! You can now chat with your partner.');
} catch (error) { } catch (error) {
console.error('Failed to accept match:', error); console.error('Failed to accept match:', error);
alert('Failed to accept match. Please try again.'); toast.error('Failed to accept match. Please try again.');
} finally { } finally {
setProcessingMatchId(null); setProcessingMatchId(null);
} }
@@ -102,7 +103,7 @@ const MatchesPage = () => {
setMatches(prev => prev.filter(m => m.slug !== matchSlug)); setMatches(prev => prev.filter(m => m.slug !== matchSlug));
} catch (error) { } catch (error) {
console.error('Failed to reject match:', error); console.error('Failed to reject match:', error);
alert('Failed to reject match. Please try again.'); toast.error('Failed to reject match. Please try again.');
} finally { } finally {
setProcessingMatchId(null); setProcessingMatchId(null);
} }

View File

@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useParams, useNavigate } from 'react-router-dom'; import { useParams, useNavigate } from 'react-router-dom';
import toast from 'react-hot-toast';
import Layout from '../components/layout/Layout'; import Layout from '../components/layout/Layout';
import { matchesAPI } from '../services/api'; import { matchesAPI } from '../services/api';
import { Star, Loader2 } from 'lucide-react'; import { Star, Loader2 } from 'lucide-react';
@@ -26,20 +27,20 @@ const RatePartnerPage = () => {
// Check if this match can be rated // Check if this match can be rated
if (result.data.status !== MATCH_STATUS.ACCEPTED && result.data.status !== MATCH_STATUS.COMPLETED) { if (result.data.status !== MATCH_STATUS.ACCEPTED && result.data.status !== MATCH_STATUS.COMPLETED) {
alert('This match must be accepted before rating.'); toast.error('This match must be accepted before rating.');
navigate('/matches'); navigate('/matches');
return; return;
} }
// Check if user has already rated this match // Check if user has already rated this match
if (result.data.hasRated) { if (result.data.hasRated) {
alert('You have already rated this match.'); toast.info('You have already rated this match.');
navigate('/matches'); navigate('/matches');
return; return;
} }
} catch (error) { } catch (error) {
console.error('Failed to load match:', error); console.error('Failed to load match:', error);
alert('Failed to load match. Redirecting to matches page.'); toast.error('Failed to load match. Redirecting to matches page.');
navigate('/matches'); navigate('/matches');
} finally { } finally {
setLoading(false); setLoading(false);
@@ -53,7 +54,7 @@ const RatePartnerPage = () => {
const handleSubmit = async (e) => { const handleSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
if (rating === 0) { if (rating === 0) {
alert('Please select a rating (1-5 stars)'); toast.warning('Please select a rating (1-5 stars)');
return; return;
} }
@@ -66,14 +67,14 @@ const RatePartnerPage = () => {
wouldCollaborateAgain, wouldCollaborateAgain,
}); });
alert('Rating submitted successfully!'); toast.success('Rating submitted successfully!');
navigate('/matches'); navigate('/matches');
} catch (error) { } catch (error) {
console.error('Failed to submit rating:', error); console.error('Failed to submit rating:', error);
if (error.message?.includes('already rated')) { if (error.message?.includes('already rated')) {
alert('You have already rated this match.'); toast.error('You have already rated this match.');
} else { } else {
alert('Failed to submit rating. Please try again.'); toast.error('Failed to submit rating. Please try again.');
} }
} finally { } finally {
setSubmitting(false); setSubmitting(false);

View File

@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { useNavigate, useSearchParams, Link } from 'react-router-dom'; import { useNavigate, useSearchParams, Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import { authAPI } from '../services/api'; import { authAPI } from '../services/api';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { Video, Mail, CheckCircle, XCircle, Loader2, ArrowRight } from 'lucide-react'; import { Video, Mail, CheckCircle, XCircle, Loader2, ArrowRight } from 'lucide-react';
@@ -91,7 +92,7 @@ const VerifyEmailPage = () => {
try { try {
await authAPI.resendVerification(email); await authAPI.resendVerification(email);
alert('Verification email sent! Please check your inbox.'); toast.success('Verification email sent! Please check your inbox.');
} catch (err) { } catch (err) {
setError(err.data?.error || 'Failed to resend verification email'); setError(err.data?.error || 'Failed to resend verification email');
} finally { } finally {