fix style
This commit is contained in:
@@ -196,6 +196,16 @@ body {
|
|||||||
color: var(--text-muted);
|
color: var(--text-muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.challenge-detail-layout {
|
||||||
|
display: grid;
|
||||||
|
gap: 2rem;
|
||||||
|
grid-template-columns: minmax(0, 1fr) 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#leaderboard-mobile {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.nav {
|
.nav {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -223,6 +233,19 @@ body {
|
|||||||
.btn {
|
.btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Show mobile leaderboard, hide desktop sidebar */
|
||||||
|
#leaderboard-mobile {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.challenge-sidebar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.challenge-detail-layout {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
|
|||||||
@@ -196,9 +196,31 @@ export default function ChallengeDetail() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div style={{ display: 'grid', gap: '2rem', gridTemplateColumns: 'minmax(0, 1fr) 300px' }}>
|
{/* Leaderboard - Desktop: Sidebar, Mobile: Top */}
|
||||||
|
<div className="card" style={{ marginBottom: '2rem' }} id="leaderboard-mobile">
|
||||||
|
<h3 style={{ marginBottom: '1rem' }}>Leaderboard</h3>
|
||||||
|
{leaderboard.length === 0 ? (
|
||||||
|
<p style={{ color: 'var(--text-muted)', fontSize: '0.875rem' }}>No points yet</p>
|
||||||
|
) : (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '0.75rem' }}>
|
||||||
|
{leaderboard.map((entry, index) => (
|
||||||
|
<div key={entry.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<div>
|
||||||
|
<span style={{ marginRight: '0.5rem', color: 'var(--text-muted)' }}>{index + 1}.</span>
|
||||||
|
<strong>{entry.username}</strong>
|
||||||
|
</div>
|
||||||
|
<span style={{ color: 'var(--primary)', fontWeight: 600 }}>
|
||||||
|
{entry.validated_points} pts
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="challenge-detail-layout">
|
||||||
{/* Main Content */}
|
{/* Main Content */}
|
||||||
<div>
|
<div className="challenge-main">
|
||||||
{/* New Prediction */}
|
{/* New Prediction */}
|
||||||
<div className="card" style={{ marginBottom: '2rem' }}>
|
<div className="card" style={{ marginBottom: '2rem' }}>
|
||||||
<h3 style={{ marginBottom: '1rem' }}>Make a Prediction</h3>
|
<h3 style={{ marginBottom: '1rem' }}>Make a Prediction</h3>
|
||||||
@@ -272,8 +294,8 @@ export default function ChallengeDetail() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Sidebar - Leaderboard */}
|
{/* Sidebar - Leaderboard (Desktop only) */}
|
||||||
<div>
|
<div className="challenge-sidebar">
|
||||||
<div className="card">
|
<div className="card">
|
||||||
<h3 style={{ marginBottom: '1rem' }}>Leaderboard</h3>
|
<h3 style={{ marginBottom: '1rem' }}>Leaderboard</h3>
|
||||||
{leaderboard.length === 0 ? (
|
{leaderboard.length === 0 ? (
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import api from '../api';
|
|||||||
|
|
||||||
export default function ChallengeList() {
|
export default function ChallengeList() {
|
||||||
const [challenges, setChallenges] = useState([]);
|
const [challenges, setChallenges] = useState([]);
|
||||||
|
const [pendingInvites, setPendingInvites] = useState([]);
|
||||||
const [searchQuery, setSearchQuery] = useState('');
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
const [showResults, setShowResults] = useState([]);
|
const [showResults, setShowResults] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
@@ -18,6 +19,10 @@ export default function ChallengeList() {
|
|||||||
try {
|
try {
|
||||||
const data = await api.getChallenges();
|
const data = await api.getChallenges();
|
||||||
setChallenges(data.challenges);
|
setChallenges(data.challenges);
|
||||||
|
|
||||||
|
// Filter out pending invitations
|
||||||
|
const pending = data.challenges.filter(c => c.participation_status === 'pending');
|
||||||
|
setPendingInvites(pending);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load challenges:', err);
|
console.error('Failed to load challenges:', err);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -25,6 +30,15 @@ export default function ChallengeList() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRespondToInvite = async (challengeId, status) => {
|
||||||
|
try {
|
||||||
|
await api.respondToChallenge(challengeId, status);
|
||||||
|
await loadChallenges();
|
||||||
|
} catch (err) {
|
||||||
|
alert('Failed to respond: ' + err.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSearch = (query) => {
|
const handleSearch = (query) => {
|
||||||
setSearchQuery(query);
|
setSearchQuery(query);
|
||||||
|
|
||||||
@@ -84,6 +98,48 @@ export default function ChallengeList() {
|
|||||||
<div className="container">
|
<div className="container">
|
||||||
<h1 style={{ marginBottom: '2rem' }}>My Challenges</h1>
|
<h1 style={{ marginBottom: '2rem' }}>My Challenges</h1>
|
||||||
|
|
||||||
|
{/* Pending Invitations */}
|
||||||
|
{pendingInvites.length > 0 && (
|
||||||
|
<div className="card" style={{ marginBottom: '2rem', background: 'var(--bg-lighter)' }}>
|
||||||
|
<h3 style={{ marginBottom: '1rem' }}>📬 Pending Invitations</h3>
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
|
||||||
|
{pendingInvites.map(challenge => (
|
||||||
|
<div key={challenge.id} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', gap: '1rem', flexWrap: 'wrap' }}>
|
||||||
|
<div style={{ display: 'flex', gap: '1rem', alignItems: 'center', flex: 1 }}>
|
||||||
|
{challenge.cover_image_url && (
|
||||||
|
<img
|
||||||
|
src={challenge.cover_image_url}
|
||||||
|
alt={challenge.title}
|
||||||
|
style={{ width: '40px', height: '60px', objectFit: 'cover', borderRadius: '0.25rem' }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<div>
|
||||||
|
<div style={{ fontWeight: 500 }}>{challenge.title}</div>
|
||||||
|
<div style={{ fontSize: '0.875rem', color: 'var(--text-muted)' }}>
|
||||||
|
Invited by {challenge.creator_username}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
||||||
|
<button
|
||||||
|
className="btn btn-success btn-sm"
|
||||||
|
onClick={() => handleRespondToInvite(challenge.id, 'accepted')}
|
||||||
|
>
|
||||||
|
Accept
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-danger btn-sm"
|
||||||
|
onClick={() => handleRespondToInvite(challenge.id, 'rejected')}
|
||||||
|
>
|
||||||
|
Decline
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Search/Create */}
|
{/* Search/Create */}
|
||||||
<div style={{ marginBottom: '2rem', position: 'relative' }}>
|
<div style={{ marginBottom: '2rem', position: 'relative' }}>
|
||||||
<input
|
<input
|
||||||
@@ -141,13 +197,13 @@ export default function ChallengeList() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Challenge List */}
|
{/* Challenge List */}
|
||||||
{challenges.length === 0 ? (
|
{challenges.filter(c => c.participation_status !== 'pending').length === 0 ? (
|
||||||
<div className="card" style={{ textAlign: 'center', padding: '3rem' }}>
|
<div className="card" style={{ textAlign: 'center', padding: '3rem' }}>
|
||||||
<p style={{ color: 'var(--text-muted)' }}>No challenges yet. Search for a show above to create one!</p>
|
<p style={{ color: 'var(--text-muted)' }}>No challenges yet. Search for a show above to create one!</p>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="grid grid-2">
|
<div className="grid grid-2">
|
||||||
{challenges.map(challenge => (
|
{challenges.filter(c => c.participation_status !== 'pending').map(challenge => (
|
||||||
<Link key={challenge.id} to={`/challenges/${challenge.id}`} style={{ textDecoration: 'none' }}>
|
<Link key={challenge.id} to={`/challenges/${challenge.id}`} style={{ textDecoration: 'none' }}>
|
||||||
<div className="card" style={{ height: '100%', display: 'flex', gap: '1rem' }}>
|
<div className="card" style={{ height: '100%', display: 'flex', gap: '1rem' }}>
|
||||||
{challenge.cover_image_url && (
|
{challenge.cover_image_url && (
|
||||||
|
|||||||
Reference in New Issue
Block a user