This commit is contained in:
2026-01-29 01:49:52 -05:00
parent 31c37d9bdd
commit 3e3f37a570
13 changed files with 365 additions and 57 deletions
+20 -7
View File
@@ -1,6 +1,8 @@
import React, { useState, useEffect } from 'react';
import React, { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import toast from 'react-hot-toast';
import api from '../api';
import { useClickOutside } from '../hooks/useClickOutside';
export default function ChallengeList() {
const [challenges, setChallenges] = useState([]);
@@ -10,6 +12,10 @@ export default function ChallengeList() {
const [loading, setLoading] = useState(true);
const [creating, setCreating] = useState(false);
const [searchTimeout, setSearchTimeout] = useState(null);
const [respondingTo, setRespondingTo] = useState(null);
const searchRef = useRef(null);
useClickOutside(searchRef, () => setShowResults([]));
useEffect(() => {
loadChallenges();
@@ -31,11 +37,15 @@ export default function ChallengeList() {
};
const handleRespondToInvite = async (challengeId, status) => {
setRespondingTo(challengeId);
try {
await api.respondToChallenge(challengeId, status);
toast.success(status === 'accepted' ? 'Challenge accepted!' : 'Challenge declined');
await loadChallenges();
} catch (err) {
alert('Failed to respond: ' + err.message);
toast.error('Failed to respond: ' + err.message);
} finally {
setRespondingTo(null);
}
};
@@ -72,18 +82,19 @@ export default function ChallengeList() {
? `https://image.tmdb.org/t/p/w500${show.poster_path}`
: null;
const result = await api.createChallenge({
await api.createChallenge({
title: show.title,
cover_image_url: coverImage,
tmdb_id: show.id,
media_type: show.media_type
});
toast.success('Challenge created!');
setSearchQuery('');
setShowResults([]);
await loadChallenges();
} catch (err) {
alert('Failed to create challenge: ' + err.message);
toast.error('Failed to create challenge: ' + err.message);
} finally {
setCreating(false);
}
@@ -124,14 +135,16 @@ export default function ChallengeList() {
<button
className="btn btn-success btn-sm"
onClick={() => handleRespondToInvite(challenge.id, 'accepted')}
disabled={respondingTo === challenge.id}
>
Accept
{respondingTo === challenge.id ? '...' : 'Accept'}
</button>
<button
className="btn btn-danger btn-sm"
onClick={() => handleRespondToInvite(challenge.id, 'rejected')}
disabled={respondingTo === challenge.id}
>
Decline
{respondingTo === challenge.id ? '...' : 'Decline'}
</button>
</div>
</div>
@@ -141,7 +154,7 @@ export default function ChallengeList() {
)}
{/* Search/Create */}
<div style={{ marginBottom: '2rem', position: 'relative' }}>
<div style={{ marginBottom: '2rem', position: 'relative' }} ref={searchRef}>
<input
type="text"
className="input"