diff --git a/frontend/src/pages/EventChatPage.jsx b/frontend/src/pages/EventChatPage.jsx index 384134b..6bad930 100644 --- a/frontend/src/pages/EventChatPage.jsx +++ b/frontend/src/pages/EventChatPage.jsx @@ -7,6 +7,10 @@ import { connectSocket, disconnectSocket, getSocket } from '../services/socket'; import { eventsAPI, heatsAPI, matchesAPI } from '../services/api'; import HeatsBanner from '../components/heats/HeatsBanner'; import Avatar from '../components/common/Avatar'; +import ChatMessageList from '../components/chat/ChatMessageList'; +import ChatInput from '../components/chat/ChatInput'; +import ConfirmationModal from '../components/modals/ConfirmationModal'; +import Modal from '../components/modals/Modal'; const EventChatPage = () => { const { slug } = useParams(); @@ -590,79 +594,24 @@ const EventChatPage = () => { {/* Chat Area */}
- {/* Messages */} -
- {/* Loading older messages indicator */} - {loadingOlder && ( -
- -
- )} - - {messages.length === 0 && ( -
- No messages yet. Start the conversation! -
- )} - {messages.map((message) => { - const isOwnMessage = (message.userId ?? message.user_id) === user.id; - return ( -
-
- -
-
- - {message.username} - - - {new Date(message.createdAt).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })} - -
-
- {message.content} -
-
-
-
- ); - })} -
-
+ {/* Message Input */}
-
- setNewMessage(e.target.value)} - placeholder="Write a message..." - disabled={!isConnected} - className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-primary-500 focus:border-primary-500 disabled:opacity-50" - /> - -
+ setNewMessage(e.target.value)} + onSubmit={handleSendMessage} + disabled={!isConnected} + placeholder="Write a message..." + />
@@ -679,79 +628,31 @@ const EventChatPage = () => { - {/* Leave Confirmation Modal */} - {showLeaveModal && ( -
-
-
-
- -
-
-

Leave Event?

-

This action cannot be undone

-
-
+ setShowLeaveModal(false)} + onConfirm={handleLeaveEvent} + title="Leave Event?" + description="This action cannot be undone" + message={`Are you sure you want to leave ${event?.name || 'this event'}? You will need to scan the QR code again to rejoin.`} + confirmText="Leave Event" + isLoading={isLeaving} + loadingText="Leaving..." + icon={AlertTriangle} + /> -

- Are you sure you want to leave {event.name}? - You will need to scan the QR code again to rejoin. -

- -
- - -
-
-
- )} - - {/* Edit Heats Modal */} - {showHeatsModal && ( -
-
-
-

Edit Your Competition Heats

- -
-
- setShowHeatsModal(false)} - existingHeats={myHeats} - /> -
-
-
- )} + setShowHeatsModal(false)} + title="Edit Your Competition Heats" + > + setShowHeatsModal(false)} + existingHeats={myHeats} + /> + ); diff --git a/frontend/src/pages/LoginPage.jsx b/frontend/src/pages/LoginPage.jsx index 620e153..3b935da 100644 --- a/frontend/src/pages/LoginPage.jsx +++ b/frontend/src/pages/LoginPage.jsx @@ -2,6 +2,8 @@ import { useState } from 'react'; import { useNavigate, Link } from 'react-router-dom'; import { useAuth } from '../contexts/AuthContext'; import { Video, Mail, Lock } from 'lucide-react'; +import FormInput from '../components/common/FormInput'; +import LoadingButton from '../components/common/LoadingButton'; const LoginPage = () => { const [email, setEmail] = useState(''); @@ -33,24 +35,16 @@ const LoginPage = () => {
-
- -
-
- -
- setEmail(e.target.value)} - className="pl-10 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500" - placeholder="your@email.com" - required - /> -
-
+ setEmail(e.target.value)} + icon={Mail} + placeholder="your@email.com" + required + />
@@ -64,28 +58,25 @@ const LoginPage = () => { Forgot password?
-
-
- -
- setPassword(e.target.value)} - className="pl-10 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500" - placeholder="••••••••" - required - /> -
+ setPassword(e.target.value)} + icon={Lock} + placeholder="••••••••" + required + />
- + Sign in +
diff --git a/frontend/src/pages/MatchChatPage.jsx b/frontend/src/pages/MatchChatPage.jsx index 279fb4c..63b9983 100644 --- a/frontend/src/pages/MatchChatPage.jsx +++ b/frontend/src/pages/MatchChatPage.jsx @@ -9,6 +9,8 @@ import { useWebRTC } from '../hooks/useWebRTC'; import { detectWebRTCSupport } from '../utils/webrtcDetection'; import WebRTCWarning from '../components/WebRTCWarning'; import Avatar from '../components/common/Avatar'; +import ChatMessageList from '../components/chat/ChatMessageList'; +import ChatInput from '../components/chat/ChatInput'; const MatchChatPage = () => { const { slug } = useParams(); @@ -350,52 +352,11 @@ const MatchChatPage = () => {
)} - {/* Messages */} -
- {messages.length === 0 && ( -
- No messages yet. Start the conversation! -
- )} - {messages.map((message) => { - const isOwnMessage = (message.userId ?? message.user_id) === user.id; - return ( -
-
- -
-
- - {message.username} - - - {new Date(message.createdAt).toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' })} - -
-
- {message.content} -
-
-
-
- ); - })} -
-
+ {/* Video Transfer Section */} {(selectedFile || isTransferring) && ( @@ -529,23 +490,13 @@ const MatchChatPage = () => {
-
- setNewMessage(e.target.value)} - placeholder="Write a message..." - disabled={!isConnected} - className="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-primary-500 focus:border-primary-500 disabled:opacity-50" - /> - -
+ setNewMessage(e.target.value)} + onSubmit={handleSendMessage} + disabled={!isConnected} + placeholder="Write a message..." + /> diff --git a/frontend/src/pages/ProfilePage.jsx b/frontend/src/pages/ProfilePage.jsx index aeacda9..23c20c1 100644 --- a/frontend/src/pages/ProfilePage.jsx +++ b/frontend/src/pages/ProfilePage.jsx @@ -2,9 +2,13 @@ import { useState, useEffect } from 'react'; import { useAuth } from '../contexts/AuthContext'; import { authAPI } from '../services/api'; import Layout from '../components/layout/Layout'; -import { User, Mail, Lock, Save, AlertCircle, CheckCircle, Loader2, Hash, Youtube, Instagram, Facebook, MapPin, Globe } from 'lucide-react'; -import { COUNTRIES } from '../data/countries'; +import Alert from '../components/common/Alert'; +import FormInput from '../components/common/FormInput'; +import FormSelect from '../components/common/FormSelect'; +import LoadingButton from '../components/common/LoadingButton'; import Avatar from '../components/common/Avatar'; +import { User, Mail, Lock, Save, Hash, Youtube, Instagram, Facebook, MapPin, Globe } from 'lucide-react'; +import { COUNTRIES } from '../data/countries'; const ProfilePage = () => { const { user, updateUser } = useAuth(); @@ -41,6 +45,7 @@ const ProfilePage = () => { }); } }, [user]); + const [profileLoading, setProfileLoading] = useState(false); const [profileMessage, setProfileMessage] = useState(''); const [profileError, setProfileError] = useState(''); @@ -196,254 +201,135 @@ const ProfilePage = () => {

Edit Profile

- {profileMessage && ( -
- -

{profileMessage}

-
- )} - - {profileError && ( -
- -

{profileError}

-
- )} + +
- {/* First Name */} -
- - -
+ - {/* Last Name */} -
- - -
+
{/* Location Section */}
- {/* Country */} -
- -
-
- -
- -
-
+ - {/* City */} -
- -
-
- -
- -
-
+
{/* WSDC ID */} -
- -
-
- -
- { - const value = e.target.value.replace(/\D/g, ''); - setProfileData({ ...profileData, wsdcId: value }); - }} - className="pl-10 w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-primary-500 focus:border-primary-500" - placeholder="12345" - maxLength={10} - /> -
-

- Your World Swing Dance Council ID (optional) -

-
+ { + const value = e.target.value.replace(/\D/g, ''); + setProfileData({ ...profileData, wsdcId: value }); + }} + icon={Hash} + placeholder="12345" + maxLength={10} + helperText="Your World Swing Dance Council ID (optional)" + /> {/* Email */} -
- -
-
- -
- -
-

- Changing your email will require re-verification -

-
+ {/* Social Media Links Section */}

Social Media Links

- {/* YouTube */} -
- -
-
- -
- -
-
+ - {/* Instagram */} -
- -
-
- -
- -
-
+ - {/* Facebook */} -
- -
-
- -
- -
-
+ - {/* TikTok */} -
- -
-
- - - -
- -
-
+ ( + + + + )} + placeholder="https://tiktok.com/@yourhandle" + />
{/* Submit Button */} - + + Save Changes + )} @@ -452,98 +338,49 @@ const ProfilePage = () => {

Change Password

- {passwordMessage && ( -
- -

{passwordMessage}

-
- )} + + - {passwordError && ( -
- -

{passwordError}

-
- )} + - {/* Current Password */} -
- -
-
- -
- -
-
+ - {/* New Password */} -
- -
-
- -
- -
-
- - {/* Confirm Password */} -
- -
-
- -
- -
-
+ {/* Submit Button */} - + + Change Password + )} diff --git a/frontend/src/pages/RegisterPage.jsx b/frontend/src/pages/RegisterPage.jsx index 5193cab..d551a3f 100644 --- a/frontend/src/pages/RegisterPage.jsx +++ b/frontend/src/pages/RegisterPage.jsx @@ -4,6 +4,9 @@ import { useAuth } from '../contexts/AuthContext'; import { wsdcAPI } from '../services/api'; import { Video, Mail, Lock, User, Hash, ArrowRight, ArrowLeft, Loader2, CheckCircle, XCircle, AlertCircle } from 'lucide-react'; import PasswordStrengthIndicator from '../components/common/PasswordStrengthIndicator'; +import FormInput from '../components/common/FormInput'; +import LoadingButton from '../components/common/LoadingButton'; +import Alert from '../components/common/Alert'; const RegisterPage = () => { // Step management @@ -183,23 +186,19 @@ const RegisterPage = () => { ) : (
-
-
- -
- setWsdcId(e.target.value.replace(/\D/g, ''))} - className="pl-10 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:ring-primary-500 focus:border-primary-500" + icon={Hash} placeholder="12345" maxLength={10} /> {wsdcLoading && ( -
+
)} @@ -307,145 +306,85 @@ const RegisterPage = () => { )}
- {error && ( -
-

{error}

-
- )} +
- {/* First Name */} + + + + + + + +
- - -
- - {/* Last Name */} -
- - -
- - {/* Username */} -
- -
-
- -
- -
-
- - {/* Email */} -
- -
-
- -
- -
-
- - {/* Password */} -
- -
-
- -
- -
- {/* Confirm Password */} -
- -
-
- -
- -
-
+
- + Create Account +