From d72bc9a303134d28763023cc47ae8c9735d92fac Mon Sep 17 00:00:00 2001 From: Daniel Bulant Date: Sat, 16 May 2026 15:11:38 +0200 Subject: [PATCH] restart --- api/src/routes/quiz.ts | 51 ++++++++++++++++++++++++++-- api/src/workflows/quiz.ts | 13 +++++-- web/src/components/party/results.tsx | 22 ++++++++++++ 3 files changed, 81 insertions(+), 5 deletions(-) diff --git a/api/src/routes/quiz.ts b/api/src/routes/quiz.ts index 1255ca5..1c27270 100644 --- a/api/src/routes/quiz.ts +++ b/api/src/routes/quiz.ts @@ -75,6 +75,55 @@ export const quizRoutes = new Elysia() }, { auth: true }, ) + .post( + "/restart", + async ({ user, set, params }) => { + const membership = await getMemberRecord(db, user.id); + if (!membership || membership.partyId !== params.partyId) { + set.status = 403; + return { error: "Not a member of this party" }; + } + + const currentParty = await db.query.party.findFirst({ + where: { + id: params.partyId, + }, + }); + if (!currentParty) { + set.status = 404; + return { error: "Party not found" }; + } + + const quizData = currentParty.data as QuizState | null; + if (!quizData || quizData.status !== "results") { + set.status = 409; + return { error: "Quiz is not finished yet" }; + } + + await db + .update(party) + .set({ + status: "started", + data: null, + lastUpdated: new Date(), + }) + .where(eq(party.id, params.partyId)); + + const handle = await DBOS.startWorkflow(quizWf.restartQuiz, { + queueName: quizQueue.name, + enqueueOptions: { queuePartitionKey: params.partyId }, + })(params.partyId); + + const status = await getPartyStatus(params.partyId); + broadcastStatusToMembers(status); + + return { + message: "Quiz restarted", + workflowId: handle.workflowID, + }; + }, + { auth: true }, + ) .post( "/response", async ({ user, body, params, set }) => { @@ -90,8 +139,6 @@ export const quizRoutes = new Elysia() } const quizData = party.data as QuizState | null; - console.log("response quiz data", party, quizData); - if (!quizData || quizData.status !== "running") { set.status = 400; return { error: "Quiz not running" }; diff --git a/api/src/workflows/quiz.ts b/api/src/workflows/quiz.ts index e440d97..7098121 100644 --- a/api/src/workflows/quiz.ts +++ b/api/src/workflows/quiz.ts @@ -32,6 +32,16 @@ export class QuizWorkflow extends ConfiguredInstance { @DBOS.workflow() async startQuiz(partyId: string): Promise { + await partyAnalysisWorkflow.analyzeParty(partyId); + await this.runQuiz(partyId); + } + + @DBOS.workflow() + async restartQuiz(partyId: string): Promise { + await this.runQuiz(partyId); + } + + private async runQuiz(partyId: string): Promise { const quizState: QuizState = { status: "running", workflowId: DBOS.workflowID ?? null, @@ -42,8 +52,6 @@ export class QuizWorkflow extends ConfiguredInstance { history: [], }; - await partyAnalysisWorkflow.analyzeParty(partyId); - // Initialize quiz state await QuizWorkflow.updatePartyData(partyId, quizState); @@ -196,7 +204,6 @@ export class QuizWorkflow extends ConfiguredInstance { } } - const _scoringGroups = groups.slice(0, Math.max(0, groups.length - 1)); if (groups.length <= 1) { return round.responses.map((response) => [ response.playerId, diff --git a/web/src/components/party/results.tsx b/web/src/components/party/results.tsx index c6d07d0..e1dd10b 100644 --- a/web/src/components/party/results.tsx +++ b/web/src/components/party/results.tsx @@ -1,7 +1,13 @@ +import { useState } from "react"; +import { toast } from "sonner"; + +import { Button } from "#/components/ui/button"; import { useParty } from "#/hooks/use-party"; +import { client } from "#/lib/eden"; export function Results() { const { party, members } = useParty(); + const [isRestarting, setIsRestarting] = useState(false); if (!party?.data) return null; const leaderboard = members @@ -18,6 +24,22 @@ export function Results() { return (

Leaderboard

+
{leaderboard.length === 0 ? (

No scores yet.