From db7fc16ec214a492bd2cc49f6a9a0caf621a44e7 Mon Sep 17 00:00:00 2001 From: Tanuja Pansare Date: Thu, 1 Oct 2026 17:57:47 +0530 Subject: [PATCH] Add event games pages --- src/App.tsx | 4 ++ src/assets/eventbuzz-logo.png | Bin 0 -> 25255 bytes src/index.css | 89 ++++++++++++++++++++++++++++++++++ src/lib/games.ts | 24 +++++++++ src/pages/GameQuizPage.tsx | 19 ++++++++ src/pages/MatchUpPage.tsx | 45 +++++++++++++++++ 6 files changed, 181 insertions(+) create mode 100644 src/assets/eventbuzz-logo.png create mode 100644 src/lib/games.ts create mode 100644 src/pages/GameQuizPage.tsx create mode 100644 src/pages/MatchUpPage.tsx diff --git a/src/App.tsx b/src/App.tsx index 606f0d7..4370ce0 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -26,6 +26,8 @@ import { AiAvatarPage } from "./pages/AiAvatarPage"; import { CodeEntryPage } from "./pages/CodeEntryPage"; import { PrivacyPolicyPage } from "./pages/PrivacyPolicyPage"; import { TermsOfServicePage } from "./pages/TermsOfServicePage"; +import { MatchUpPage } from "./pages/MatchUpPage"; +import { GameQuizPage } from "./pages/GameQuizPage"; import { useEventTheme } from "./hooks/useEventTheme"; import { ThemeGate } from "./components/ThemeGate"; @@ -57,6 +59,8 @@ export default function App() { } /> } /> } /> + } /> + } /> } /> } /> } /> diff --git a/src/assets/eventbuzz-logo.png b/src/assets/eventbuzz-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7e11628a3e2c6cfd73667185265109f30cf02560 GIT binary patch literal 25255 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 TcM13(2^3XDErkYoi}3#kQc5(S literal 0 HcmV?d00001 diff --git a/src/index.css b/src/index.css index a6293cc..37cf9cd 100644 --- a/src/index.css +++ b/src/index.css @@ -180,3 +180,92 @@ body { opacity: 0; pointer-events: none; } + +.game-page { min-height: 100dvh; background: #9dd9ee center / cover fixed; color: #102333; font-family: Inter, system-ui, sans-serif; display: flex; justify-content: center; } +.game-page--match { background-color: #83cfee; } +.game-page--quiz { background-color: #8fd2ed; } +.game-backdrop { position: fixed; inset: 0; background: linear-gradient(180deg, rgba(255,255,255,.02), rgba(13,112,161,.26)); pointer-events: none; } +.game-viewport { position: relative; width: min(750px, 100%); min-height: 100dvh; background: rgba(218,244,250,.25); box-shadow: 0 0 0 1px rgba(17,74,113,.08); } +.reference-header { height: 92px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; padding: 14px 22px; color: #102333; } +.game-brand { display: grid; justify-items: center; gap: 3px; min-width: 130px; } +.game-brand img { width: 112px; height: 30px; object-fit: contain; } +.game-brand strong { max-width: 180px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #fff; font-size: 13px; font-weight: 900; text-transform: uppercase; } +.reference-icon, .subheader-back { border: 0; background: transparent; color: inherit; display: inline-flex; align-items: center; justify-content: center; padding: 5px; cursor: pointer; } +.reference-actions { display: flex; justify-content: flex-end; gap: 10px; } +.game-logo { display: grid; justify-items: center; line-height: .82; transform: rotate(-3deg); color: #0871c9; text-shadow: 1px 1px 0 white, -1px -1px 0 white; } +.game-logo span { font-size: 11px; font-weight: 900; letter-spacing: .18em; } +.game-logo strong { font-size: 29px; font-style: italic; letter-spacing: -.05em; } +.game-logo small { color: #e12d3e; font-size: 9px; font-weight: 900; letter-spacing: .2em; transform: rotate(3deg); } +.game-subheader { height: 42px; display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; padding: 0 18px; background: rgba(255,255,255,.82); font-size: 14px; font-weight: 800; } +.game-subheader .subheader-back { justify-self: start; } +.match-content { padding: 17px 22px 28px; } +.metric-inline { margin-left: 6px; color: #23465e; font-size: 12px; font-weight: 900; } +.match-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; } +.heart-count { margin-right: auto; color: #d92945; display: inline-flex; align-items: center; gap: 4px; font-size: 18px; } +.game-tool { border: 0; background: transparent; color: #23465e; display: inline-flex; align-items: center; gap: 5px; padding: 7px 3px; font-size: 13px; cursor: pointer; } +.game-tool:disabled { opacity: .45; cursor: default; } +.match-board { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 7px; max-height: 56dvh; overflow-y: auto; padding: 2px; } +.match-tile { min-height: 76px; border: 0; border-radius: 3px; background: rgba(247,252,253,.94); color: #16384d; padding: 9px 7px; display: grid; place-items: center; gap: 5px; text-align: center; font-size: 13px; font-weight: 800; cursor: pointer; box-shadow: 0 1px 1px rgba(20,71,95,.14); transition: background .16s ease, transform .16s ease; } +.match-tile:hover { transform: translateY(-1px); } +.match-tile.is-selected { background: #f5a45b; color: #fff; } +.match-tile img { width: 32px; height: 32px; border-radius: 3px; object-fit: cover; } +.match-submit { width: 100%; margin-top: 15px; border: 0; border-radius: 4px; background: #0878b9; color: #fff; padding: 14px; font-size: 13px; font-weight: 900; letter-spacing: .04em; cursor: pointer; } +.match-submit:disabled { background: #8baebb; cursor: default; } +.match-reveal { display: grid; gap: 3px; margin-bottom: 8px; padding: 12px 14px; border-radius: 3px; background: #ed9150; color: #fff; } +.match-reveal strong { font-size: 14px; text-transform: uppercase; } +.match-reveal span { font-size: 12px; } +.game-feedback { min-height: 18px; margin: 8px 0 0; text-align: center; color: #176b87; font-size: 12px; font-weight: 700; } +.game-feedback.is-error { color: #b52d42; } +.game-start { min-height: calc(100dvh - 92px); box-sizing: border-box; display: flex; flex-direction: column; align-items: center; text-align: center; padding: 110px 28px 40px; color: #102333; } +.game-start img { width: min(260px, 72%); height: auto; object-fit: contain; margin-bottom: 26px; } +.game-start h1 { margin: 0 0 16px; font-size: 31px; font-weight: 900; text-transform: uppercase; } +.game-start h2 { margin: 28px 0 12px; font-size: 17px; letter-spacing: .04em; } +.game-start p { margin: 0; font-size: 16px; line-height: 1.45; } +.game-start button { width: min(365px, 100%); margin-top: 34px; border: 0; border-radius: 4px; background: #0878c8; color: #fff; padding: 15px; font-size: 15px; font-weight: 900; cursor: pointer; } +.game-start--match { background: linear-gradient(180deg, rgba(19,118,194,.05), rgba(9,145,235,.72)); } +.game-start--quiz { background: linear-gradient(180deg, rgba(255,255,255,.08), rgba(45,164,225,.34)); } +.game-start--quiz img { width: min(300px, 78%); } +.game-start--quiz input { width: min(365px, 100%); box-sizing: border-box; margin-top: 24px; border: 1px solid #a6c4cf; padding: 12px; background: rgba(255,255,255,.9); color: #102333; font: inherit; } +.game-start--quiz button { margin-top: 14px; } +.metric-badge { display: inline-flex; align-items: center; border-radius: 3px; background: #fff; color: #0878b9; padding: 7px 13px; font-size: 15px; font-weight: 900; } +.game-dialog-backdrop { position: fixed; inset: 0; z-index: 5; display: grid; place-items: center; padding: 20px; background: rgba(14,58,79,.42); } +.game-dialog { width: min(420px, 100%); padding: 24px; background: #fff; box-shadow: 0 12px 36px rgba(16,58,78,.25); } +.dialog-title { display: flex; align-items: center; justify-content: space-between; margin-bottom: 18px; } +.dialog-title h2 { margin: 0; font-size: 22px; } +.game-dialog p { display: flex; gap: 12px; margin: 14px 0; color: #3d5661; font-size: 14px; line-height: 1.45; } +.game-dialog p strong { color: #1b9c6b; } +.dialog-close, .match-finish button, .quiz-start button { border: 0; background: #0878b9; color: white; padding: 11px 17px; font-weight: 800; cursor: pointer; } +.match-finish, .game-error, .game-loading { padding: 76px 24px; text-align: center; } +.finish-mark { width: 50px; height: 50px; margin: 0 auto 14px; display: grid; place-items: center; border-radius: 50%; background: #0878b9; color: #fff; font-size: 28px; font-weight: 900; } +.match-finish h1, .game-error h1 { margin: 0 0 8px; font-size: 28px; } +.match-finish p, .game-error p, .game-loading { color: #3d5661; } +.quiz-start { margin: 18vh 22px 0; padding: 28px 25px; background: rgba(255,255,255,.93); } +.quiz-start h1 { margin: 0 0 10px; font-size: 28px; } +.quiz-start p { margin: 0 0 22px; color: #526875; } +.quiz-start input { width: 100%; box-sizing: border-box; margin-bottom: 13px; border: 1px solid #b9cbd0; padding: 12px; font: inherit; } +.quiz-progress-reference { padding: 13px 22px 0; color: #23465e; font-size: 12px; font-weight: 800; } +.quiz-progress-reference div { height: 4px; margin-top: 8px; background: rgba(255,255,255,.7); } +.quiz-progress-reference i { display: block; height: 100%; background: #0878b9; transition: width .25s ease; } +.quiz-card-reference { margin: 28px 22px 30px; padding: 23px 21px 18px; background: rgba(255,255,255,.94); } +.quiz-card-reference h1 { margin: 0 0 20px; font-size: 21px; line-height: 1.18; } +.quiz-options-reference { display: grid; gap: 7px; } +.quiz-options-reference button { display: flex; align-items: center; justify-content: space-between; width: 100%; border: 0; background: #e8edf0; color: #263d48; padding: 14px 12px; text-align: left; font: inherit; cursor: pointer; } +.quiz-options-reference button.is-selected { background: #c6e6f3; } +.quiz-options-reference button.is-correct { background: #c5ebd0; border: 1px solid #2e9b54; } +.quiz-options-reference button.is-wrong { background: #f3c6cb; border: 1px solid #cf3447; } +.quiz-result-reference { margin: 29vh 29px 0; padding: 23px 24px 18px; background: rgba(255,255,255,.95); } +.result-message { margin: 0 0 7px; color: #6c777c; font-size: 14px; } +.quiz-result-reference h1 { margin: 0 0 17px; font-size: 22px; } +.result-strip { display: flex; gap: 4px; flex-wrap: wrap; } +.result-strip span { width: 35px; height: 35px; display: grid; place-items: center; color: #fff; font-size: 20px; font-weight: 900; } +.result-strip .is-correct { background: #218434; } +.result-strip .is-wrong { background: #c41226; } +.result-score { display: block; margin-top: 13px; font-size: 26px; } +.share-result { border: 0; background: transparent; display: inline-flex; align-items: center; gap: 6px; padding: 8px 0; color: #23465e; font-weight: 700; cursor: pointer; } +.quiz-result-reference h2 { margin: 20px 0 8px; font-size: 17px; } +.leaderboard { margin: 0; padding: 0; list-style: none; border-top: 1px solid #d6e0e2; } +.leaderboard li { display: flex; justify-content: space-between; padding: 9px 0; border-bottom: 1px solid #d6e0e2; } +.leaderboard li span { display: flex; gap: 10px; } +.leaderboard b { color: #0878b9; } +.next-trivia { margin: 18px 0 0; color: #526875; font-size: 13px; } +@media (max-width: 600px) { .reference-header { height: 77px; padding: 10px 13px; } .game-brand img { width: 95px; } .game-brand strong { max-width: 120px; font-size: 11px; } .match-content { padding: 14px 12px 24px; } .match-board { gap: 5px; } .match-tile { min-height: 69px; padding: 7px 4px; font-size: 11px; } .match-tile img { width: 27px; height: 27px; } .game-start { min-height: calc(100dvh - 77px); padding-top: 86px; } .game-start h1 { font-size: 26px; } .quiz-card-reference { margin-inline: 12px; } .quiz-progress-reference { padding-inline: 12px; } .quiz-result-reference { margin-inline: 12px; } } diff --git a/src/lib/games.ts b/src/lib/games.ts new file mode 100644 index 0000000..d6074d6 --- /dev/null +++ b/src/lib/games.ts @@ -0,0 +1,24 @@ +const API_BASE = (import.meta.env.VITE_API_URL || 'http://localhost:3002/api').replace(/\/$/, ''); + +async function call(path: string, init?: RequestInit): Promise { + const response = await fetch(`${API_BASE}${path}`, { ...init, headers: { 'Content-Type': 'application/json', ...(init?.headers || {}) } }); + const body = await response.json(); + if (!response.ok || !body.success) throw new Error(body.error?.message || 'Game unavailable'); + return body.data as T; +} + +export interface PublicMatchUpItem { _id: string; text: string; imageUrl?: string; } +export interface PublicMatchUpGroup { _id: string; title: string; description?: string; items: PublicMatchUpItem[]; } +export interface PublicMatchUp { name: string; backgroundImage: string; gridSize: 3 | 4 | 5; groups: PublicMatchUpGroup[]; publicToken: string; } +export interface PublicQuizQuestion { _id: string; question: string; options: string[]; } +export interface PublicQuiz { name: string; backgroundImage: string; questions: PublicQuizQuestion[]; publicToken: string; } +export interface QuizAnswerFeedback { correct: boolean; correctIndex: number; points: number; } +export interface QuizResult { displayName: string; score: number; total: number; correct: number; timeSpent: number; } +export interface QuizLeaderboardEntry extends QuizResult { _id: string; } + +export function getMatchUp(token: string) { return call(`/public/games/match-up/${encodeURIComponent(token)}`); } +export function guessMatchUp(token: string, itemIds: string[]) { return call<{ correct: boolean; group: { _id: string; title: string; description: string; itemIds: string[] } | null }>(`/public/games/match-up/${encodeURIComponent(token)}/guess`, { method: 'POST', body: JSON.stringify({ itemIds }) }); } +export function completeMatchUp(token: string, groups: string[][]) { return call<{ completed: boolean; groups: number }>(`/public/games/match-up/${encodeURIComponent(token)}/complete`, { method: 'POST', body: JSON.stringify({ groups }) }); } +export function getGameQuiz(token: string) { return call(`/public/games/quiz/${encodeURIComponent(token)}`); } +export function answerGameQuiz(token: string, questionIndex: number, answerIndex: number) { return call(`/public/games/quiz/${encodeURIComponent(token)}/answer`, { method: 'POST', body: JSON.stringify({ questionIndex, answerIndex }) }); } +export function submitGameQuiz(token: string, payload: { displayName: string; answers: number[]; timeSpent: number }) { return call<{ result: QuizResult; leaderboard: QuizLeaderboardEntry[] }>(`/public/games/quiz/${encodeURIComponent(token)}/submit`, { method: 'POST', body: JSON.stringify(payload) }); } diff --git a/src/pages/GameQuizPage.tsx b/src/pages/GameQuizPage.tsx new file mode 100644 index 0000000..c798e07 --- /dev/null +++ b/src/pages/GameQuizPage.tsx @@ -0,0 +1,19 @@ +import { useEffect, useState } from 'react'; +import { CheckCircle2, ChevronLeft, Share2, Trophy, XCircle } from 'lucide-react'; +import eventbuzzLogo from '../assets/eventbuzz-logo.png'; +import { answerGameQuiz, getGameQuiz, submitGameQuiz, type PublicQuiz, type QuizLeaderboardEntry, type QuizResult } from '../lib/games'; +import { getAttendeeParticipant } from '../lib/attendeeAuth'; + +export function GameQuizPage() { + const token = new URLSearchParams(window.location.search).get('token') || window.location.pathname.split('/').pop() || ''; + const participant = getAttendeeParticipant(); + const [game, setGame] = useState(null); const [index, setIndex] = useState(0); const [answers, setAnswers] = useState([]); const [feedback, setFeedback] = useState<(boolean | null)[]>([]); const [selected, setSelected] = useState(null); const [correctAnswer, setCorrectAnswer] = useState(null); const [result, setResult] = useState(null); const [leaderboard, setLeaderboard] = useState([]); const [name, setName] = useState(participant?.displayName || [participant?.firstName, participant?.lastName].filter(Boolean).join(' ')); const [started, setStarted] = useState(false); const [startedAt, setStartedAt] = useState(0); const [error, setError] = useState(''); const [loading, setLoading] = useState(true); + useEffect(() => { void getGameQuiz(token).then(setGame).catch((err) => setError(err.message)).finally(() => setLoading(false)); }, [token]); + const submit = async (finalAnswers: number[]) => { if (!game) return; const displayName = name.trim() || window.prompt('Enter a display name for the leaderboard')?.trim() || 'Anonymous'; try { const data = await submitGameQuiz(token, { displayName, answers: finalAnswers, timeSpent: Math.round((Date.now() - startedAt) / 1000) }); setResult(data.result); setLeaderboard(data.leaderboard); } catch (err) { setError(err instanceof Error ? err.message : 'Unable to submit quiz'); } }; + const choose = async (answerIndex: number) => { if (!game || selected !== null || !started) return; setSelected(answerIndex); try { const response = await answerGameQuiz(token, index, answerIndex); const nextAnswers = [...answers]; nextAnswers[index] = answerIndex; const nextFeedback = [...feedback]; nextFeedback[index] = response.correct; setCorrectAnswer(response.correctIndex); setAnswers(nextAnswers); setFeedback(nextFeedback); window.setTimeout(() => { if (index + 1 >= game.questions.length) { setStarted(false); void submit(nextAnswers); } else { setIndex((current) => current + 1); setSelected(null); setCorrectAnswer(null); } }, 900); } catch (err) { setSelected(null); setError(err instanceof Error ? err.message : 'Unable to check answer'); } }; + const shareResult = async () => { if (!result) return; const text = `${game?.name || 'Quiz'}: I scored ${result.score} points (${result.correct}/${result.total / 5} correct).`; if (navigator.share) await navigator.share({ title: game?.name, text, url: window.location.href }); else await navigator.clipboard?.writeText(text); }; + if (loading) return
Loading game...
; + if (error || !game) return

Quiz unavailable

{error || 'Quiz is not available.'}

; + const background = game.backgroundImage ? { backgroundImage: `url(${game.backgroundImage})` } : undefined; const question = game.questions[index]; + return
EventBuzz{game.name}
{!started && !result ?
EventBuzz

{game.name}

HOW TO PLAY

Answer every question.
Choose the best answer.
How much can you score?

setName(event.target.value)} placeholder="Display name" aria-label="Display name" />
: result ?

{result.correct >= Math.ceil(result.total / 5 * 0.7) ? 'Great effort!' : 'Strong effort! Aim for more next time'}

You got {result.correct} out of {result.total / 5} correct

{feedback.map((answer, answerIndex) => {answerIndex + 1})}
+{result.score}

Leaderboard

    {leaderboard.map((entry, entryIndex) =>
  1. {entryIndex + 1}{entry.displayName}{entry.score}
  2. )}

Next quiz is coming soon. See you then!

: <>
Question {index + 1} of {game.questions.length}

{question.question}

{question.options.map((option, optionIndex) => { const isChosen = selected === optionIndex; const isCorrect = selected !== null && optionIndex === correctAnswer; const isWrong = selected !== null && !feedback[index] && isChosen; return ; })}
}
; +} diff --git a/src/pages/MatchUpPage.tsx b/src/pages/MatchUpPage.tsx new file mode 100644 index 0000000..91b4c96 --- /dev/null +++ b/src/pages/MatchUpPage.tsx @@ -0,0 +1,45 @@ +import { useEffect, useMemo, useState } from 'react'; +import { BarChart3, ChevronLeft, CircleHelp, Heart, RefreshCw, Trophy, X } from 'lucide-react'; +import eventbuzzLogo from '../assets/eventbuzz-logo.png'; +import { getMatchUp, guessMatchUp, type PublicMatchUp, type PublicMatchUpItem } from '../lib/games'; + +type Reveal = { _id: string; title: string; description: string; itemIds: string[] }; +function shuffled(items: T[]) { return [...items].sort(() => Math.random() - 0.5); } + +export function MatchUpPage() { + const token = new URLSearchParams(window.location.search).get('token') || window.location.pathname.split('/').pop() || ''; + const [game, setGame] = useState(null); const [items, setItems] = useState([]); const [selected, setSelected] = useState([]); const [reveals, setReveals] = useState([]); const [lives, setLives] = useState(5); const [message, setMessage] = useState(''); const [helpOpen, setHelpOpen] = useState(false); const [started, setStarted] = useState(false); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); + useEffect(() => { void getMatchUp(token).then((data) => { setGame(data); setItems(shuffled(data.groups.flatMap((group) => group.items))); }).catch((err) => setError(err.message)).finally(() => setLoading(false)); }, [token]); + const activeItems = useMemo(() => items.filter((item) => !reveals.some((group) => group.itemIds.includes(item._id))), [items, reveals]); + const finished = Boolean(game && reveals.length === game.groups.length); const failed = lives <= 0; const background = game?.backgroundImage ? { backgroundImage: `url(${game.backgroundImage})` } : undefined; + const submit = async () => { if (!game || selected.length !== game.gridSize || loading || failed) return; setMessage('Checking your guess...'); try { const result = await guessMatchUp(token, selected); if (result.correct && result.group) { setReveals((current) => [...current, result.group as Reveal]); setSelected([]); setMessage('Connection found'); } else { setLives((current) => Math.max(0, current - 1)); setSelected([]); setMessage('That group does not match'); } } catch (err) { setMessage(err instanceof Error ? err.message : 'Unable to check guess'); } }; + if (loading) return
Loading game...
; + if (error || !game) return

Game unavailable

{error || 'Match Up is not available.'}

; + return
EventBuzz{game.name}
{!started ?
EventBuzz

{game.name}

Metric {game.gridSize} x {game.gridSize}

HOW TO PLAY

Find groups of related tiles.
Choose {game.gridSize} tiles for each connection.
How many can you solve?

: <>
Today
{helpOpen ?
setHelpOpen(false)}>
event.stopPropagation()}>

How to play?

1 Pick {game.gridSize} tiles that share a common connection.

2 Tap submit to check your guess.

3 Find every connection before you run out of hearts.

: null}
{lives}
{game.gridSize} x {game.gridSize}
{reveals.map((group) =>
{group.title}{group.description}
)}{finished || failed ?
{finished ? 'OK' : '!'}

{finished ? 'Nice work.' : 'Out of hearts.'}

{finished ? 'You found every hidden connection.' : 'Try the puzzle again and find the connections.'}

: <>
+ {activeItems.map((item) => ( + + ))} +
}{message ?

{message}

: null}
}
; +}