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,5 +1,6 @@
import { useState, useRef, useEffect } from 'react';
import { useParams, useNavigate, useSearchParams, Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import Layout from '../components/layout/Layout';
import { useAuth } from '../contexts/AuthContext';
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);
// 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
// For now, we just show a success message
@@ -210,13 +211,13 @@ const EventChatPage = () => {
// Show appropriate error message
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) {
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) {
alert('Event not found.');
toast.error('Event not found.');
} 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');
} catch (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 {
setIsLeaving(false);
setShowLeaveModal(false);

View File

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

View File

@@ -1,5 +1,6 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import toast from 'react-hot-toast';
import Layout from '../components/layout/Layout';
import { useAuth } from '../contexts/AuthContext';
import { matchesAPI } from '../services/api';
@@ -80,10 +81,10 @@ const MatchesPage = () => {
await loadMatches();
// 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) {
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 {
setProcessingMatchId(null);
}
@@ -102,7 +103,7 @@ const MatchesPage = () => {
setMatches(prev => prev.filter(m => m.slug !== matchSlug));
} catch (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 {
setProcessingMatchId(null);
}

View File

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

View File

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