chore: more stuff and minor features

main
parent 44f2076fba
commit 2c84fc74b4

@ -0,0 +1,60 @@
import { requestJSON } from '@/client/utils'
import type { RoomData } from '@/db/model'
import type { ActionAnswer, ActionJolly } from '@/ggwp'
import { useEffect, useState } from 'preact/hooks'
type Props = {
roomId: string
}
const ActionCardAnswer = ({ action }: { action: ActionAnswer }) => {
return (
<div class="card tint-blue stack-h center">
<strong>{action.question}</strong>
<div>{action.team}</div>
<div>{action.outcome}</div>
</div>
)
}
const ActionCardJolly = ({ action }: { action: ActionJolly }) => {
return (
<div class="card tint-blue stack-h center">
<strong>{action.team}</strong>
<div>{action.groupId}</div>
</div>
)
}
export const ActionRegistry = ({ roomId }: Props) => {
const [room, setRoom] = useState<RoomData | null>(null)
useEffect(() => {
requestJSON(`/api/room/${roomId}`).then(room => {
setRoom(room)
})
}, [])
if (!room) {
return <div>Loading...</div>
}
if (room.actions.length === 0) {
return <p>Ancora nessuna azione</p>
}
return (
<>
{room.actions.map(action =>
action.type === 'answer' ? (
<ActionCardAnswer action={action} />
) : action.type === 'jolly' ? (
<ActionCardJolly action={action} />
) : (
<div class="card tint-red">
Unknown action type: <code>{JSON.stringify(action)}</code>
</div>
)
)}
</>
)
}

@ -0,0 +1,27 @@
import { LiveLeaderboard } from '@/components/LiveLeaderboard'
import { SubmitAction } from '@/components/SubmitAction'
import { ActionRegistry } from '@/components/ActionRegistry'
import { useRef, useState } from 'preact/hooks'
export const AdminPage = ({ room }: { room: string }) => {
const listeners = useRef<Record<string, (teamQuestion: { team: string; question: string }) => void>>({})
return (
<>
<h1>Admin</h1>
<h2>{room}</h2>
<LiveLeaderboard
roomId={room}
selectTeamQuestion={teamQuestion =>
Object.values(listeners.current).forEach(listener => listener(teamQuestion))
}
/>
&nbsp;
<h2>Azioni</h2>
<SubmitAction roomId={room} onTeamQuestionIndex={(key, listener) => (listeners.current[key] = listener)} />
&nbsp;
<h2>Registro Azioni</h2>
<ActionRegistry roomId={room} />
</>
)
}

@ -4,10 +4,10 @@ type Props = {
questions: Question[]
scoreboard: Scoreboard
onQuestionClick?: (team: string, questionId: string) => void
selectTeamQuestion?: ({ team, question }: { team: string; question: string }) => void
}
export const Leaderboard = ({ questions, scoreboard, onQuestionClick }: Props) => {
export const Leaderboard = ({ questions, scoreboard, selectTeamQuestion }: Props) => {
return (
<div
class="leaderboard"
@ -32,7 +32,14 @@ export const Leaderboard = ({ questions, scoreboard, onQuestionClick }: Props) =
</div>
<div class="answers">
{questions.map((question, j) => (
<div class="answer" key={j} onClick={() => onQuestionClick?.(team, question.id)}>
<div
class="answer"
key={j}
onClick={() => {
console.log('emitting', { team, question: question.id })
return selectTeamQuestion?.({ team, question: question.id })
}}
>
{questionScores[question.id]}
</div>
))}

@ -6,9 +6,10 @@ import { computeScoreboardState } from '@/ggwp'
type Props = {
roomId: string
selectTeamQuestion?: ({ team, question }: { team: string; question: string }) => void
}
export const LiveLeaderboard = ({ roomId }: Props) => {
export const LiveLeaderboard = ({ roomId, selectTeamQuestion }: Props) => {
const [room, setRoom] = useState<RoomData | null>(null)
useEffect(() => {
requestJSON(`/api/room/${roomId}`).then(room => {
@ -30,5 +31,5 @@ export const LiveLeaderboard = ({ roomId }: Props) => {
room.actions
)
return <Leaderboard questions={room.questions} scoreboard={scoreboard} />
return <Leaderboard questions={room.questions} scoreboard={scoreboard} selectTeamQuestion={selectTeamQuestion} />
}

@ -6,32 +6,71 @@ import { useEffect, useState } from 'preact/hooks'
type Outcome = 'correct' | 'partial' | 'wrong'
const handleSendAnswer = (action: Action) => {
// TODO: send action to server
console.log(action)
}
export const SubmitActionAnswer = ({ room }: { room: RoomData }) => {
export const SubmitActionAnswer = ({
room,
onTeamQuestionIndex,
}: {
room: RoomData
onTeamQuestionIndex?: Receiver<{ team: string; question: string }>
}) => {
const [answer, setAnswer] = useState<ActionAnswer>({
question: '',
team: '',
outcome: 'correct',
})
onTeamQuestionIndex?.('SubmitActionAnswer', ({ team, question }) => {
console.log('onTeamQuestionIndex', team, question)
setAnswer(answer => ({ ...answer, team, question }))
})
return (
<>
<input
type="text"
<div class="card tint-red stack-v center">
<h3>Invia Risposta</h3>
<select
class="fill-w"
placeholder="Domanda..."
value={answer.question}
onInput={e => setAnswer({ ...answer, question: e.currentTarget.value })}
/>
<input
>
<option value="" disabled>
Seleziona domanda...
</option>
{room.questions.map((question, i) => (
<option value={question.id} key={i}>
{question.id}
</option>
))}
</select>
{/* <input
type="text"
class="fill-w"
placeholder="Squadra..."
value={answer.team}
onInput={e => setAnswer({ ...answer, team: e.currentTarget.value })}
/>
/> */}
<select
class="fill-w"
placeholder="Squadra..."
value={answer.team}
onInput={e => setAnswer({ ...answer, team: e.currentTarget.value })}
>
<option value="" disabled>
Seleziona squadra...
</option>
{room.teams.map((team, i) => (
<option value={team} key={i}>
{team}
</option>
))}
</select>
<select
class="fill-w"
value={answer.outcome}
@ -51,32 +90,75 @@ export const SubmitActionAnswer = ({ room }: { room: RoomData }) => {
>
Invia risposta
</button>
</>
</div>
)
}
export const SubmitActionJolly = ({ room }: { room: RoomData }) => {
type Receiver<T> = (key: string, cb: (data: T) => void) => void
export const SubmitActionJolly = ({
room,
onTeamQuestionIndex,
}: {
room: RoomData
onTeamQuestionIndex?: Receiver<{ team: string; question: string }>
}) => {
const groupsMap: Record<string, boolean> = {}
room.questions.forEach(question => {
groupsMap[question.group] = true
})
const groups = Object.keys(groupsMap)
const questionToGroup: Record<string, string> = {}
room.questions.forEach(question => {
questionToGroup[question.id] = question.group
})
const [answer, setAnswer] = useState<ActionJolly>({
team: '',
groupId: '',
})
onTeamQuestionIndex?.('SubmitActionJolly', ({ team, question }) => {
console.log('onTeamQuestionIndex', team, question)
setAnswer(answer => ({ ...answer, team, groupId: questionToGroup[question] }))
})
return (
<>
<input
type="text"
<div class="card tint-red stack-v center">
<h3>Invia Jolly</h3>
<select
class="fill-w"
placeholder="Squadra..."
value={answer.team}
onInput={e => setAnswer({ ...answer, team: e.currentTarget.value })}
/>
<input
type="text"
>
<option value="" disabled>
Seleziona squadra...
</option>
{room.teams.map((team, i) => (
<option value={team} key={i}>
{team}
</option>
))}
</select>
<select
class="fill-w"
placeholder="Gruppo..."
value={answer.groupId}
onInput={e => setAnswer({ ...answer, groupId: e.currentTarget.value })}
/>
>
<option value="" disabled>
Seleziona gruppo...
</option>
{groups.map(group => (
<option value={group}>{group}</option>
))}
</select>
<button
onClick={() =>
handleSendAnswer({
@ -87,11 +169,17 @@ export const SubmitActionJolly = ({ room }: { room: RoomData }) => {
>
Imposta jolly
</button>
</>
</div>
)
}
export const SubmitAction = ({ roomId }: { roomId: string }) => {
export const SubmitAction = ({
roomId,
onTeamQuestionIndex,
}: {
roomId: string
onTeamQuestionIndex?: Receiver<{ team: string; question: string }>
}) => {
const [room, setRoom] = useState<RoomData | null>(null)
useEffect(() => {
requestJSON(`/api/room/${roomId}`).then(room => {
@ -104,12 +192,9 @@ export const SubmitAction = ({ roomId }: { roomId: string }) => {
}
return (
<div class="card tint-red stack-v center">
<h3>Risposta</h3>
<SubmitActionAnswer room={room} />
<h3>Jolly</h3>
<SubmitActionJolly room={room} />
</div>
<>
<SubmitActionAnswer room={room} onTeamQuestionIndex={onTeamQuestionIndex} />
<SubmitActionJolly room={room} onTeamQuestionIndex={onTeamQuestionIndex} />
</>
)
}

@ -1,9 +1,9 @@
---
import Base from '@/layouts/Base.astro'
import { LiveLeaderboard } from '@/components/LiveLeaderboard'
import { getSession } from '@/db/sessions'
import { SubmitAction } from '@/components/SubmitAction'
import { AdminPage } from '@/components/AdminPage'
const { room } = Astro.params
if (!room) {
@ -17,6 +17,9 @@ if (Astro.cookies.has('sid')) {
}
const sessionRoom = getSession(sid.value)
if (!sessionRoom) {
return Astro.redirect('/error?msg=' + encodeURIComponent(`Sessione non valida`))
}
if (sessionRoom !== room) {
return Astro.redirect('/error?msg=' + encodeURIComponent(`Sei solo l'admin di "${sessionRoom}"`))
@ -24,15 +27,13 @@ if (Astro.cookies.has('sid')) {
} else {
return Astro.redirect('/error?msg=' + encodeURIComponent(`Devi essere loggato per accedere a questa pagina`))
}
---
<Base>
<h1>Admin</h1>
<h2>{room}</h2>
<LiveLeaderboard client:load roomId={room} />
type Callback<T> = (value: T) => void
&nbsp;
const listeners: Callback<{ team: string; question: string }>[] = []
console.log('listeners', listeners)
---
<h2>Azioni</h2>
<SubmitAction client:load roomId={room} />
<Base>
<AdminPage client:load room={room} />
</Base>

@ -225,7 +225,7 @@ select {
outline: none;
&:focus {
&:hover {
background: oklch(from var(--tint) calc(l + 1) 0.05 h);
}
}

Loading…
Cancel
Save