diff --git a/frontend/src/pages/ChallengeDetail.jsx b/frontend/src/pages/ChallengeDetail.jsx index 7df80f3..2034b5d 100644 --- a/frontend/src/pages/ChallengeDetail.jsx +++ b/frontend/src/pages/ChallengeDetail.jsx @@ -23,6 +23,8 @@ export default function ChallengeDetail() { const [submitting, setSubmitting] = useState(false); const [validating, setValidating] = useState(null); const [deleting, setDeleting] = useState(false); + const [friends, setFriends] = useState([]); + const [inviting, setInviting] = useState(null); const searchRef = useRef(null); useClickOutside(searchRef, () => setSearchResults([])); @@ -56,11 +58,23 @@ export default function ChallengeDetail() { } }, [id]); + const loadFriends = useCallback(async () => { + try { + const data = await api.getFriends(); + // Combine regular friends and challenge friends + const allFriends = [...(data.friends || []), ...(data.challenge_friends || [])]; + setFriends(allFriends); + } catch (err) { + console.error('Failed to load friends:', err); + } + }, []); + useEffect(() => { loadChallenge(); loadPredictions(); loadLeaderboard(); - }, [loadChallenge, loadPredictions, loadLeaderboard]); + loadFriends(); + }, [loadChallenge, loadPredictions, loadLeaderboard, loadFriends]); // Join challenge room for real-time updates useEffect(() => { @@ -190,14 +204,18 @@ export default function ChallengeDetail() { setSearchTimeout(timeout); }; - const handleInvite = async (userId) => { + const handleInvite = async (userId, userName) => { + setInviting(userId); try { await api.inviteToChallenge(id, { user_ids: [userId] }); - toast.success('Invitation sent!'); + toast.success(`Invitation sent to ${userName || 'user'}!`); setInviteQuery(''); setSearchResults([]); + await loadChallenge(); // Refresh to update participant list } catch (err) { toast.error('Failed to send invite: ' + err.message); + } finally { + setInviting(null); } }; @@ -270,19 +288,64 @@ export default function ChallengeDetail() { {showInvite && (

Invite Someone

- handleSearchUsers(e.target.value)} - /> + + {/* Friends List */} + {friends.length > 0 && ( +
+

Your Friends

+
+ {friends.filter(friend => { + // Filter out friends who are already participants + const isParticipant = challenge.participants?.some(p => p.id === friend.id); + const isCreator = challenge.challenge.created_by === friend.id; + return !isParticipant && !isCreator; + }).map(friend => ( +
+
+
{friend.username}
+
{friend.email}
+
+ +
+ ))} +
+
+ )} + + {/* Search for others */} +
+

Search for Others

+ handleSearchUsers(e.target.value)} + /> +
{searchResults.length > 0 && (
(
handleInvite(user.id)} style={{ padding: '1rem', borderBottom: '1px solid var(--border)', - cursor: 'pointer' + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center' }} > -
{user.username}
-
{user.email}
+
+
{user.username}
+
{user.email}
+
+
))}
diff --git a/frontend/src/pages/Friends.jsx b/frontend/src/pages/Friends.jsx index 9e1e072..4f29aae 100644 --- a/frontend/src/pages/Friends.jsx +++ b/frontend/src/pages/Friends.jsx @@ -276,8 +276,15 @@ export default function Friends() {
{friend.username}
{friend.email}
-
+
{friend.total_points} points
+