import React, { useState, useEffect } from ‘react’;
import { XCircle, Award, Users, ChevronUp, ChevronDown, Zap, Clock, CheckCircle, XSquare, Mail, QrCode, Share2, Bell, Link, Copy, Smile, Frown, Send, MessageSquare } from ‘lucide-react’;
// Main App Component
const App = () => {
const [screen, setScreen] = useState(‘login’);
const [user, setUser] = useState(null);
const [leaderboard, setLeaderboard] = useState([]);
const [currentQuestion, setCurrentQuestion] = useState(0);
const [score, setScore] = useState(0);
const [challengeTarget, setChallengeTarget] = useState(null);
const [quizQuestions, setQuizQuestions] = useState([]);
const [timeLeft, setTimeLeft] = useState(20);
const [quizStarted, setQuizStarted] = useState(false);
const [quizFinished, setQuizFinished] = useState(false);
const [isCorrect, setIsCorrect] = useState(null);
const [duelInvites, setDuelInvites] = useState([]);
const [activeDuel, setActiveDuel] = useState(null);
const [inviteCode, setInviteCode] = useState(”);
const [waitingForOpponent, setWaitingForOpponent] = useState(false);
const [notifications, setNotifications] = useState([]);
const [chatOpen, setChatOpen] = useState(false);
const [chatMessages, setChatMessages] = useState([]);
const [messageInput, setMessageInput] = useState(”);
const [opponentAnswered, setOpponentAnswered] = useState(false);
const [opponentScore, setOpponentScore] = useState(0);
const [opponentCorrect, setOpponentCorrect] = useState(null);
const [duelResults, setDuelResults] = useState(null);
const [inviteMethod, setInviteMethod] = useState(‘direct’);
// Mock online users
const [onlineUsers, setOnlineUsers] = useState([]);
// Set up real players for the leaderboard with connection status
useEffect(() => {
if (leaderboard.length === 0) {
const realPlayers = [
{ id: 1, name: ‘Jos’, country: ‘Netherlands’, email: ‘j.vonk@fontys.nl’, score: 890, rank: 1, avatar: ‘👨🏫’, online: false, status: ‘offline’ },
{ id: 2, name: ‘Sander’, country: ‘Netherlands’, email: ‘s.vanheeswijk@fontys.nl’, score: 840, rank: 2, avatar: ‘👨💻’, online: false, status: ‘offline’ },
{ id: 3, name: ‘Onno’, country: ‘Netherlands’, email: ‘o.deklerk@fontys.nl’, score: 780, rank: 3, avatar: ‘👨🔬’, online: false, status: ‘offline’ },
{ id: 4, name: ‘Bart’, country: ‘Netherlands’, email: ‘b.vanabeelen@fontys.nl’, score: 730, rank: 4, avatar: ‘👨🎓’, online: false, status: ‘offline’ },
];
// Randomly set 1-3 players as online for demo purposes
// In a real app, this would be managed by a backend
const numOnline = 1 + Math.floor(Math.random() * 3);
const shuffled = […realPlayers].sort(() => 0.5 – Math.random());
for (let i = 0; i < numOnline; i++) {
shuffled[i].online = true;
shuffled[i].status = 'online';
}
setLeaderboard(shuffled.sort((a, b) => a.rank – b.rank));
setOnlineUsers(shuffled.filter(user => user.online));
}
}, [leaderboard.length]);
// Questions bank organized by difficulty level
const questionsByLevel = {
beginner: [
{
question: “What is Papa John’s tagline in Spain?”,
options: [
“Better Ingredients. Better Pizza.”,
“This is Pizza, Papa.”,
“Esto es Pizza, Papá.”,
“The Best Pizza in Spain.”
],
correctAnswer: 2
},
{
question: “In what year did Papa John’s open its first store in Spain?”,
options: [“2010”, “2014”, “2016”, “2018”],
correctAnswer: 2
},
{
question: “How many stores does Papa John’s have in Spain?”,
options: [“Around 50”, “Around 75”, “Around 95”, “Around 120”],
correctAnswer: 2
},
{
question: “What is unique about Papa John’s dough according to their branding?”,
options: [
“It’s imported from Italy”,
“It’s always fresh, never frozen”,
“It’s made with spring water only”,
“It’s fermented for 48 hours”
],
correctAnswer: 1
},
{
question: “Which city in Spain has the most Papa John’s stores?”,
options: [“Barcelona”, “Valencia”, “Madrid”, “Seville”],
correctAnswer: 2
}
],
intermediate: [
{
question: “What is the brand personality of Papa John’s as described in the brief?”,
options: [
“Friendly and approachable”,
“Sophisticated and premium”,
“Challengers: bold, brave, and here to play”,
“Traditional and authentic”
],
correctAnswer: 2
},
{
question: “What special item does Papa John’s add to their pizza boxes that’s mentioned as a ‘surprise element’?”,
options: [
“Garlic dipping sauce”,
“A pepperoncini”,
“Parmesan cheese packets”,
“Herb seasoning”
],
correctAnswer: 1
},
{
question: “In how many European countries does Papa John’s operate?”,
options: [“7”, “9”, “10”, “12”],
correctAnswer: 2
},
{
question: “What type of cheese does Papa John’s emphasize using in their marketing?”,
options: [
“Blend of six cheeses”,
“Authentic Italian cheese”,
“Locally sourced cheese”,
“100% mozzarella”
],
correctAnswer: 3
},
{
question: “What important element of Papa John’s product messaging is emphasized throughout the brief?”,
options: [
“Fast delivery times”,
“Customizable options”,
“Quality of ingredients”,
“Competitive pricing”
],
correctAnswer: 2
}
],
advanced: [
{
question: “What strategic position is Papa John’s taking in the Spanish market according to the brief?”,
options: [
“Value-focused budget option”,
“Premium quality with unique touches”,
“Health-conscious alternative”,
“Family-oriented tradition”
],
correctAnswer: 1
},
{
question: “What is the target audience age range for Papa John’s Halloween campaign?”,
options: [“12 to 25”, “16 to 35”, “25 to 45”, “All age groups”],
correctAnswer: 1
},
{
question: “In which year was Papa John’s founded globally?”,
options: [“1976”, “1980”, “1984”, “1990”],
correctAnswer: 2
},
{
question: “What is emphasized about the vegetables used in Papa John’s pizzas?”,
options: [
“They’re organic and locally sourced”,
“They’re imported from specific regions”,
“They’re fresh and chopped daily in-store”,
“They’re pre-selected for optimal flavor”
],
correctAnswer: 2
},
{
question: “Besides Spain, name another European country where Papa John’s operates according to the brief:”,
options: [
“France and Italy”,
“Germany and Poland”,
“Portugal and Netherlands”,
“Sweden and Norway”
],
correctAnswer: 2
}
],
expert: [
{
question: “What is the marketing philosophy expressed in the statement ‘If we’re just gonna do what everyone else does’?”,
options: [
“We’d rather not do it at all”,
“We’d do it better than them”,
“We’d need to charge less”,
“We’d find a new market”
],
correctAnswer: 0
},
{
question: “What is the primary objective of the Halloween 2025 campaign according to the brief?”,
options: [
“Increase sales by 25%”,
“Launch a new limited-time product”,
“Drive awareness through a multi-channel approach”,
“Compete directly with other pizza chains”
],
correctAnswer: 2
},
{
question: “Which statement best describes Papa John’s communication positioning according to the brief?”,
options: [
“We’re saving the world through better ingredients”,
“We’re not saving the world. Pizza is about fun and good times.”,
“We’re transforming the pizza industry standards”,
“We’re preserving authentic pizza traditions”
],
correctAnswer: 1
},
{
question: “According to the brief, which of these approaches is most aligned with the brand’s tone?”,
options: [
“Detailed explanations of sourcing practices”,
“Emotional storytelling around family traditions”,
“Say it like it is – This is what we’ve got, this is how it is”,
“Aspirational messaging about premium lifestyles”
],
correctAnswer: 2
},
{
question: “In which specific countries must the Halloween campaign be simultaneously implemented?”,
options: [
“Spain, Italy, Portugal, and France”,
“Spain, Czech Republic, Romania, and Netherlands”,
“Spain, UK, Germany, and Poland”,
“All European locations”
],
correctAnswer: 1
}
]
};
// Generate quiz questions based on user’s rank
const generateQuestions = (userRank) => {
let questions = [];
// Determine difficulty distribution based on rank
if (userRank >= 1 && userRank <= 3) {
// Top ranks get mostly expert questions
questions = questions.concat(getRandomQuestions(questionsByLevel.expert, 5));
questions = questions.concat(getRandomQuestions(questionsByLevel.advanced, 3));
questions = questions.concat(getRandomQuestions(questionsByLevel.intermediate, 2));
} else if (userRank >= 4 && userRank <= 6) {
// Middle-high ranks get mostly advanced questions
questions = questions.concat(getRandomQuestions(questionsByLevel.expert, 2));
questions = questions.concat(getRandomQuestions(questionsByLevel.advanced, 5));
questions = questions.concat(getRandomQuestions(questionsByLevel.intermediate, 3));
} else if (userRank >= 7 && userRank <= 9) {
// Middle ranks get mostly intermediate questions
questions = questions.concat(getRandomQuestions(questionsByLevel.advanced, 2));
questions = questions.concat(getRandomQuestions(questionsByLevel.intermediate, 5));
questions = questions.concat(getRandomQuestions(questionsByLevel.beginner, 3));
} else {
// Lower ranks get mostly beginner questions
questions = questions.concat(getRandomQuestions(questionsByLevel.intermediate, 2));
questions = questions.concat(getRandomQuestions(questionsByLevel.beginner, 8));
}
// Shuffle questions
return shuffleArray(questions);
};
// Helper to get random questions from a category
const getRandomQuestions = (questionSet, count) => {
const shuffled = […questionSet].sort(() => 0.5 – Math.random());
return shuffled.slice(0, count);
};
// Helper to shuffle array
const shuffleArray = (array) => {
return […array].sort(() => 0.5 – Math.random());
};
// Generate a unique invite code
const generateInviteCode = () => {
const chars = ‘ABCDEFGHJKLMNPQRSTUVWXYZ23456789’;
let result = ”;
for (let i = 0; i < 6; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
};
// Handle user login
const handleLogin = (username) => {
if (!username) return;
// Create a new user or find existing one
let newUser;
const existingUserIndex = leaderboard.findIndex(u => u.name.toLowerCase() === username.toLowerCase());
if (existingUserIndex >= 0) {
// Update the status of the existing user to online
const updatedLeaderboard = […leaderboard];
updatedLeaderboard[existingUserIndex] = {
…updatedLeaderboard[existingUserIndex],
online: true,
status: ‘online’
};
newUser = updatedLeaderboard[existingUserIndex];
setLeaderboard(updatedLeaderboard);
setOnlineUsers(updatedLeaderboard.filter(u => u.online));
} else {
// Only allow login as one of the pre-defined users
alert(“Please log in as one of the real players: Jos, Sander, Onno, or Bart”);
return;
}
setUser(newUser);
// Add welcome notification
addNotification(`Welcome to Papa John’s Marketing Challenge, ${username}!`);
// Generate personal invite code
setInviteCode(generateInviteCode());
// In a real app, this would broadcast the user’s online status to all other users
setScreen(‘home’);
};
// Add notification
const addNotification = (message) => {
const newNotification = {
id: Date.now(),
message,
read: false,
timestamp: new Date()
};
setNotifications(prev => [newNotification, …prev]);
};
// Sort leaderboard by score
const sortLeaderboard = (board) => {
const sorted = […board].sort((a, b) => b.score – a.score);
// Update ranks
return sorted.map((user, index) => ({
…user,
rank: index + 1
}));
};
// Send duel invitation with real-time connection
const sendDuelInvitation = (targetUser) => {
// Don’t allow challenging busy players
if (targetUser.status === ‘busy’) {
addNotification(`${targetUser.name} is currently in another challenge.`);
return;
}
// Update opponent status to pending
const updatedLeaderboard = leaderboard.map(player => {
if (player.id === targetUser.id) {
return { …player, status: ‘pending’ };
}
return player;
});
setLeaderboard(updatedLeaderboard);
// Update current user status to indicate waiting
setUser({ …user, status: ‘waiting’ });
// Create pending challenge
setChallengeTarget(targetUser);
// Show waiting screen for connection
setWaitingForOpponent(true);
addNotification(`Waiting for ${targetUser.name} to accept your challenge…`);
// In a real app, this would be a WebSocket connection to the target user
// For demo purposes, we’ll simulate the other player’s response
setTimeout(() => {
// Simulate decision (90% acceptance rate for demo)
const accepted = Math.random() > 0.1;
if (accepted) {
// Update opponent status to busy
const busyLeaderboard = updatedLeaderboard.map(player => {
if (player.id === targetUser.id) {
return { …player, status: ‘busy’ };
}
if (player.id === user.id) {
return { …player, status: ‘busy’ };
}
return player;
});
setLeaderboard(busyLeaderboard);
// Update user status
setUser({ …user, status: ‘busy’ });
addNotification(`${targetUser.name} accepted your challenge! Starting duel…`);
// Generate questions and prepare duel
const questions = generateQuestions(Math.floor((user.rank + targetUser.rank) / 2));
setQuizQuestions(questions);
setActiveDuel({
opponent: targetUser,
startTime: new Date(),
questions,
connected: true,
bothReady: false
});
// Move to connection confirmation screen
setWaitingForOpponent(false);
setScreen(‘connectionConfirm’);
} else {
// Reset status
const resetLeaderboard = updatedLeaderboard.map(player => {
if (player.id === targetUser.id) {
return { …player, status: ‘online’ };
}
return player;
});
setLeaderboard(resetLeaderboard);
// Reset user status
setUser({ …user, status: ‘online’ });
// Notify user of decline
addNotification(`${targetUser.name} declined your challenge.`);
setWaitingForOpponent(false);
setChallengeTarget(null);
}
}, 3000 + Math.random() * 2000); // Random wait time for realism
};
// Accept a duel invitation
const acceptDuelInvitation = (invite) => {
const sender = leaderboard.find(u => u.id === invite.sender.id);
// Remove the invitation
setDuelInvites(prev => prev.filter(i => i.id !== invite.id));
// Start the duel
startDuel(sender);
};
// Start a duel match with confirmed connection
const startDuel = (opponent) => {
// Reset stats
setCurrentQuestion(0);
setScore(0);
setOpponentScore(0);
setTimeLeft(20);
setIsCorrect(null);
setOpponentCorrect(null);
setOpponentAnswered(false);
setQuizStarted(true);
setQuizFinished(false);
setChatMessages([]);
// Set both players as ready and connected
setActiveDuel(prev => ({
…prev,
bothReady: true,
connectionTime: new Date()
}));
// Start countdown
setScreen(‘countdown’);
// After countdown, start the actual duel
setTimeout(() => {
setScreen(‘duel’);
}, 3000);
};
// Function for player to confirm ready status
const confirmConnection = () => {
addNotification(`You’re connected with ${activeDuel.opponent.name}!`);
addNotification(“Get ready for the challenge to begin!”);
// In a real app, this would notify the server that this player is ready
// For demo purposes, simulate both players being ready
setTimeout(() => {
// Start the actual duel
startDuel(activeDuel.opponent);
}, 1500);
};
// Join duel with code
const joinDuelWithCode = (code) => {
// In a real app, this would validate the code on the server
if (code.length < 4) {
addNotification("Please enter a valid duel code");
return;
}
// Simulate looking for match
setWaitingForOpponent(true);
// For demo, we'll select an opponent that the user can challenge
setTimeout(() => {
setWaitingForOpponent(false);
const challengeableOpponents = leaderboard.filter(u => u.id !== user.id && canChallenge(u));
if (challengeableOpponents.length > 0) {
const randomOpponent = challengeableOpponents[Math.floor(Math.random() * challengeableOpponents.length)];
addNotification(`Connected with ${randomOpponent.name} for a duel!`);
// Start duel
startDuel(randomOpponent);
} else {
addNotification(“No challengers found. You can only challenge players up to 2 ranks above you.”);
setScreen(‘home’);
}
}, 3000);
};
// Send a chat message
const sendChatMessage = () => {
if (!messageInput.trim()) return;
const newMessage = {
id: Date.now(),
sender: user,
text: messageInput,
timestamp: new Date()
};
setChatMessages(prev => […prev, newMessage]);
setMessageInput(”);
// Simulate opponent response
if (activeDuel && Math.random() > 0.5) {
setTimeout(() => {
const responseMessages = [
“Good luck!”,
“You’re going down!”,
“May the best marketer win!”,
“Let’s see who knows more about Papa John’s!”,
“I’m going to win this one 🍕”
];
const opponentMessage = {
id: Date.now() + 1,
sender: activeDuel.opponent,
text: responseMessages[Math.floor(Math.random() * responseMessages.length)],
timestamp: new Date()
};
setChatMessages(prev => […prev, opponentMessage]);
}, 1500 + Math.random() * 2000);
}
};
// Check if user can challenge another user (at most 2 ranks above)
const canChallenge = (targetUser) => {
// Can only challenge players up to 2 positions above in ranking
return targetUser.rank < user.rank + 3 && targetUser.rank >= 1 && targetUser.id !== user.id;
};
// Handle answer selection
const handleAnswerSelect = (answerIndex) => {
if (quizFinished) return;
const currentQ = quizQuestions[currentQuestion];
const isAnswerCorrect = answerIndex === currentQ.correctAnswer;
setIsCorrect(isAnswerCorrect);
if (isAnswerCorrect) {
// Calculate score based on time left and question difficulty
const timeBonus = timeLeft * 5;
const difficultyBonus = getDifficultyBonus(currentQuestion);
const questionScore = 100 + timeBonus + difficultyBonus;
setScore(prevScore => prevScore + questionScore);
}
// In duel mode, simulate opponent’s answer
if (activeDuel) {
simulateOpponentAnswer();
}
// Move to next question after a short delay
setTimeout(() => {
setIsCorrect(null);
setOpponentCorrect(null);
setOpponentAnswered(false);
if (currentQuestion < quizQuestions.length - 1) {
setCurrentQuestion(prev => prev + 1);
setTimeLeft(20); // Reset timer for next question
} else {
// End quiz
finishQuiz();
}
}, 2000);
};
// Simulate opponent’s answer in duel mode
const simulateOpponentAnswer = () => {
// Random delay for opponent’s answer
const answerDelay = 500 + Math.random() * 3000;
setTimeout(() => {
setOpponentAnswered(true);
// Skill based on rank
const opponentSkill = 1 – (activeDuel.opponent.rank / 15); // Higher rank = higher skill
// Determine if opponent answers correctly (weighted by skill)
const isCorrect = Math.random() < (0.5 + opponentSkill / 2);
setOpponentCorrect(isCorrect);
if (isCorrect) {
// Calculate opponent score
const remainingTime = Math.floor(Math.random() * timeLeft);
const timeBonus = remainingTime * 5;
const difficultyBonus = getDifficultyBonus(currentQuestion);
const questionScore = 100 + timeBonus + difficultyBonus;
setOpponentScore(prev => prev + questionScore);
}
}, answerDelay);
};
// Get bonus points based on question difficulty
const getDifficultyBonus = (questionIndex) => {
// Earlier questions are easier, later ones are harder
return questionIndex * 10;
};
// Finish the quiz and update rankings
const finishQuiz = () => {
setQuizFinished(true);
// Calculate results for duel
if (activeDuel) {
const playerWon = score > opponentScore;
const isDraw = score === opponentScore;
setDuelResults({
playerScore: score,
opponentScore: opponentScore,
playerWon,
isDraw
});
// Update rankings based on duel results
if (playerWon) {
// Player moves up
addNotification(`You won the duel against ${activeDuel.opponent.name}!`);
} else if (!isDraw) {
// Opponent moves up
addNotification(`You lost the duel against ${activeDuel.opponent.name}.`);
} else {
// Draw – no change
addNotification(`Your duel with ${activeDuel.opponent.name} ended in a draw!`);
}
}
// Update user score and rankings
setTimeout(() => {
const newLeaderboard = […leaderboard];
// Find current user
const userIndex = newLeaderboard.findIndex(u => u.id === user.id);
if (userIndex >= 0) {
// Update the user’s score
newLeaderboard[userIndex] = {
…newLeaderboard[userIndex],
score: newLeaderboard[userIndex].score + score
};
// If this was a duel and the player won, swap ranks with opponent if needed
if (activeDuel && score > opponentScore) {
const opponentIndex = newLeaderboard.findIndex(u => u.id === activeDuel.opponent.id);
if (opponentIndex >= 0 && newLeaderboard[opponentIndex].rank < newLeaderboard[userIndex].rank) {
// Boost player score above opponent
newLeaderboard[userIndex].score = newLeaderboard[opponentIndex].score + 10;
}
}
// Reset status for both players
if (activeDuel) {
const opponentIndex = newLeaderboard.findIndex(u => u.id === activeDuel.opponent.id);
if (opponentIndex >= 0) {
newLeaderboard[opponentIndex] = {
…newLeaderboard[opponentIndex],
status: ‘online’
};
}
newLeaderboard[userIndex] = {
…newLeaderboard[userIndex],
status: ‘online’
};
}
// Sort leaderboard and update ranks
const sortedLeaderboard = sortLeaderboard(newLeaderboard);
setLeaderboard(sortedLeaderboard);
// Update current user
const updatedUser = sortedLeaderboard.find(u => u.id === user.id);
setUser(updatedUser);
}
setScreen(activeDuel ? ‘duelResults’ : ‘results’);
}, 1000);
};
// Timer effect
useEffect(() => {
let timer;
if (quizStarted && !quizFinished && timeLeft > 0) {
timer = setTimeout(() => {
setTimeLeft(prev => prev – 1);
}, 1000);
} else if (timeLeft === 0 && !quizFinished) {
// Time’s up, move to next question
setIsCorrect(false);
// In duel mode, simulate opponent’s answer if they haven’t answered yet
if (activeDuel && !opponentAnswered) {
simulateOpponentAnswer();
}
setTimeout(() => {
setIsCorrect(null);
setOpponentCorrect(null);
setOpponentAnswered(false);
if (currentQuestion < quizQuestions.length - 1) {
setCurrentQuestion(prev => prev + 1);
setTimeLeft(20);
} else {
finishQuiz();
}
}, 2000);
}
return () => clearTimeout(timer);
}, [timeLeft, quizStarted, quizFinished, currentQuestion, quizQuestions.length, activeDuel, opponentAnswered]);
// Copy invite code to clipboard
const copyInviteCode = () => {
navigator.clipboard.writeText(inviteCode).then(() => {
addNotification(“Invite code copied to clipboard!”);
});
};
// Render login screen
const renderLogin = () => (
);
// Render home screen
const renderHome = () => (
Papa John’s Challenge
{user.avatar}
{user.name}
Your Stats
Rank #{user.rank}
Challenge a Friend
Challenge someone directly from the leaderboard
Share your code or enter someone else’s code
Generate a QR code for quick in-person challenges
{inviteMethod === ‘direct’ && (
)}
{inviteMethod === ‘code’ && (
)}
{inviteMethod === ‘qr’ && (
{/* This would be a real QR code in production */}
QR Code for
{inviteCode}
Let your friend scan this code to challenge you
)}
Practice Mode
Test your knowledge about Papa John’s marketing strategy and improve your skills!
{leaderboard.map((rankUser) => (
{rankUser.rank}
{rankUser.avatar}
{rankUser.id !== user.id && (
)}
{rankUser.name}
{rankUser.country}
{rankUser.id !== user.id && (
{rankUser.status === ‘offline’ ? ‘Offline’ :
rankUser.status === ‘online’ ? ‘Online’ :
rankUser.status === ‘busy’ ? ‘In Challenge’ :
rankUser.status === ‘pending’ ? ‘Pending’ : ”}
)}
{rankUser.score}
{canChallenge(rankUser) && rankUser.status === ‘online’ && (
)}
{canChallenge(rankUser) && rankUser.status !== ‘online’ && (
)}
))}
Online Now
{onlineUsers.length} Online
{leaderboard.filter(u => u.id !== user.id && canChallenge(u)).map(challengeableUser => (
{challengeableUser.avatar}
{challengeableUser.name}
Rank #{challengeableUser.rank}
))}
);
// Render challenge screen
const renderChallengeScreen = () => (
Available Opponents
{leaderboard
.filter(u => u.id !== user.id && canChallenge(u))
.map(opponent => (
{opponent.name}
Rank #{opponent.rank}
{opponent.status === ‘offline’ ? ‘Offline’ :
opponent.status === ‘online’ ? ‘Online’ :
opponent.status === ‘busy’ ? ‘In Challenge’ :
opponent.status === ‘pending’ ? ‘Pending’ : ”}
{opponent.email}
{opponent.rank < user.rank ? (
Higher rank
) : (
Lower rank
)}
{opponent.status === ‘online’ ? (
) : (
)}
))}
{leaderboard.filter(u => u.id !== user.id && canChallenge(u)).length === 0 && (
No opponents available to challenge
{user.rank <= 2 ?
"You can only challenge players up to 2 ranks above you." :
"You're already at or near the top of the leaderboard!"}
)}
);
// Render notifications screen
const renderNotificationsScreen = () => (
Your Notifications
{notifications.length === 0 ? (
) : (
{notifications.map(notification => (
{notification.message}
{notification.timestamp.toLocaleTimeString([], { hour: ‘2-digit’, minute: ‘2-digit’ })}
))}
)}
);
// Render waiting screen for finding opponent
const renderWaitingScreen = () => (
Waiting for response…
{challengeTarget && (
{challengeTarget.avatar}
{challengeTarget.name}
{challengeTarget.email}
Challenge invitation sent. Waiting for {challengeTarget.name} to respond…
)}
Please wait while we establish a connection
);
// Render connection confirmation screen
const renderConnectionConfirmScreen = () => (
{user.avatar}
You
Connected
{activeDuel.opponent.avatar}
{activeDuel.opponent.name}
Connected
Connection Established!
You are now connected with {activeDuel.opponent.name} for a marketing challenge duel.
Click Ready when you’re prepared to begin!
);
// Render countdown screen
const renderCountdownScreen = () => (
{user.avatar}
{user.name}
VS
{activeDuel.opponent.avatar}
{activeDuel.opponent.name}
{/* In a real app, this would be a dynamic countdown */}
3
Get ready for the challenge!
);
// Helper component to render Trophy icon
const Trophy = ({ size, className }) => (
);
// Render duel screen
const renderDuelScreen = () => {
if (!activeDuel) return null;
const currentQ = quizQuestions[currentQuestion];
const progress = ((currentQuestion + 1) / quizQuestions.length) * 100;
return (
Duel: You vs {activeDuel.opponent.name}
{timeLeft}s
{/* Duel scoreboard */}
{activeDuel.opponent.name}
{opponentScore}
{activeDuel.opponent.avatar}
{/* Question and answers */}
{currentQ.question}
{currentQ.options.map((option, index) => (
))}
{/* Player status indicators */}
{user.avatar}
You
{isCorrect === true &&
}
{isCorrect === false &&
}
{activeDuel.opponent.name}
{activeDuel.opponent.avatar}
{opponentAnswered ? (
opponentCorrect === true ? (
) : (
)
) : (
)}
{/* Chat slide-in panel */}
Chat with {activeDuel.opponent.name}
{chatMessages.length === 0 ? (
No messages yet
Start the conversation!
) : (
{chatMessages.map(message => (
{message.sender.id === user.id ? ‘You’ : message.sender.name}
{message.text}
{message.timestamp.toLocaleTimeString([], { hour: ‘2-digit’, minute: ‘2-digit’ })}
))}
)}
);
};
// Render duel results screen
const renderDuelResults = () => {
if (!duelResults || !activeDuel) return null;
const { playerScore, opponentScore, playerWon, isDraw } = duelResults;
return (
{isDraw
? “It’s a Draw!”
: playerWon
? “Victory!”
: “Defeat!”}
{user.avatar}
You
{playerScore}
VS
{activeDuel.opponent.avatar}
{activeDuel.opponent.name}
{opponentScore}
{isDraw
? “You both performed equally well!”
: playerWon
? `You beat ${activeDuel.opponent.name}!`
: `${activeDuel.opponent.name} won this time.`}
Questions
{quizQuestions.length}
Duel Summary
{quizQuestions.slice(0, 5).map((q, i) => (
{q.question}
Correct answer: {q.options[q.correctAnswer]}
))}
{quizQuestions.length > 5 && (
+ {quizQuestions.length – 5} more questions
)}
);
};
// Render quiz screen
const renderQuiz = () => {
const currentQ = quizQuestions[currentQuestion];
const progress = ((currentQuestion + 1) / quizQuestions.length) * 100;
return (
Papa John’s Quiz
{timeLeft}s
Question {currentQuestion + 1} of {quizQuestions.length}
Score: {score}
{currentQ.question}
{currentQ.options.map((option, index) => (
))}
);
};
// Render results screen
const renderResults = () => {
const correctAnswers = quizQuestions.reduce(
(count, question, index) => count + (0 === question.correctAnswer ? 1 : 0),
0
);
const accuracy = Math.round((correctAnswers / quizQuestions.length) * 100);
return (
Challenge Complete!
Challenge Summary
{quizQuestions.slice(0, 5).map((q, i) => (
{q.question}
Correct answer: {q.options[q.correctAnswer]}
))}
{quizQuestions.length > 5 && (
+ {quizQuestions.length – 5} more questions
)}
);
};
// Main render method
if (waitingForOpponent) {
return renderWaitingScreen();
}
switch (screen) {
case ‘login’:
return renderLogin();
case ‘home’:
return renderHome();
case ‘challenge’:
return renderChallengeScreen();
case ‘notifications’:
return renderNotificationsScreen();
case ‘quiz’:
return renderQuiz();
case ‘results’:
return renderResults();
case ‘duel’:
return renderDuelScreen();
case ‘duelResults’:
return renderDuelResults();
case ‘connectionConfirm’:
return renderConnectionConfirmScreen();
case ‘countdown’:
return renderCountdownScreen();
default:
return renderLogin();
}
};
export default App;