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:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user