Initial addition of the Arcade study feature. Add connections as the first working game.
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m4s
All checks were successful
Automated Container Build / build-and-push (push) Successful in 1m4s
This commit is contained in:
parent
7b90409f2e
commit
611a585757
43 changed files with 5990 additions and 68 deletions
19
src/app/api/arcade/import/preview/route.ts
Normal file
19
src/app/api/arcade/import/preview/route.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { ArcadeImportError } from "@/lib/arcade/connectionsImport";
|
||||
import { arcadePreviewRequestSchema } from "@/lib/validation/arcadeSchemas";
|
||||
import * as arcadeService from "@/services/arcadeService";
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const parsed = arcadePreviewRequestSchema.safeParse(await request.json().catch(() => null));
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json({ error: "A game type and raw JSON are required." }, { status: 400 });
|
||||
}
|
||||
try {
|
||||
return NextResponse.json(arcadeService.previewArcadeImport(parsed.data.rawJson));
|
||||
} catch (error) {
|
||||
if (error instanceof ArcadeImportError) {
|
||||
return NextResponse.json({ error: error.message, details: error.details }, { status: 400 });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
35
src/app/api/arcade/packs/[id]/attempts/route.ts
Normal file
35
src/app/api/arcade/packs/[id]/attempts/route.ts
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { arcadeAttemptCreateSchema } from "@/lib/validation/arcadeSchemas";
|
||||
import * as arcadeService from "@/services/arcadeService";
|
||||
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
context: RouteContext<"/api/arcade/packs/[id]/attempts">
|
||||
) {
|
||||
const { id } = await context.params;
|
||||
const pack = await arcadeService.getArcadePack(id);
|
||||
if (!pack) return NextResponse.json({ error: "Arcade pack not found" }, { status: 404 });
|
||||
return NextResponse.json(await arcadeService.listArcadeAttempts(id));
|
||||
}
|
||||
|
||||
export async function POST(
|
||||
request: NextRequest,
|
||||
context: RouteContext<"/api/arcade/packs/[id]/attempts">
|
||||
) {
|
||||
const { id } = await context.params;
|
||||
const parsed = arcadeAttemptCreateSchema.safeParse(await request.json().catch(() => null));
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json({ error: "Invalid completed attempt", details: parsed.error.issues }, { status: 400 });
|
||||
}
|
||||
try {
|
||||
const attempt = await arcadeService.createArcadeAttempt(id, parsed.data);
|
||||
return attempt
|
||||
? NextResponse.json(attempt, { status: 201 })
|
||||
: NextResponse.json({ error: "Arcade pack not found" }, { status: 404 });
|
||||
} catch (error) {
|
||||
return NextResponse.json(
|
||||
{ error: error instanceof Error ? error.message : "Attempt could not be saved" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
}
|
||||
38
src/app/api/arcade/packs/[id]/route.ts
Normal file
38
src/app/api/arcade/packs/[id]/route.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { arcadePackUpdateSchema } from "@/lib/validation/arcadeSchemas";
|
||||
import * as arcadeService from "@/services/arcadeService";
|
||||
|
||||
export async function GET(
|
||||
_request: NextRequest,
|
||||
context: RouteContext<"/api/arcade/packs/[id]">
|
||||
) {
|
||||
const { id } = await context.params;
|
||||
const pack = await arcadeService.getArcadePack(id);
|
||||
return pack
|
||||
? NextResponse.json(pack)
|
||||
: NextResponse.json({ error: "Arcade pack not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
export async function PATCH(
|
||||
request: NextRequest,
|
||||
context: RouteContext<"/api/arcade/packs/[id]">
|
||||
) {
|
||||
const { id } = await context.params;
|
||||
const parsed = arcadePackUpdateSchema.safeParse(await request.json().catch(() => null));
|
||||
if (!parsed.success) return NextResponse.json({ error: "A valid name is required" }, { status: 400 });
|
||||
const pack = await arcadeService.updateArcadePack(id, parsed.data.name);
|
||||
return pack
|
||||
? NextResponse.json(pack)
|
||||
: NextResponse.json({ error: "Arcade pack not found" }, { status: 404 });
|
||||
}
|
||||
|
||||
export async function DELETE(
|
||||
_request: NextRequest,
|
||||
context: RouteContext<"/api/arcade/packs/[id]">
|
||||
) {
|
||||
const { id } = await context.params;
|
||||
const deleted = await arcadeService.deleteArcadePack(id);
|
||||
return deleted
|
||||
? NextResponse.json({ success: true })
|
||||
: NextResponse.json({ error: "Arcade pack not found" }, { status: 404 });
|
||||
}
|
||||
34
src/app/api/arcade/packs/route.ts
Normal file
34
src/app/api/arcade/packs/route.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { ArcadeImportError } from "@/lib/arcade/connectionsImport";
|
||||
import { arcadePackCreateSchema } from "@/lib/validation/arcadeSchemas";
|
||||
import * as arcadeService from "@/services/arcadeService";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const params = new URL(request.url).searchParams;
|
||||
const classId = params.get("classId");
|
||||
const gameType = params.get("gameType");
|
||||
if (!classId || gameType !== "connections") {
|
||||
return NextResponse.json({ error: "Valid classId and gameType values are required." }, { status: 400 });
|
||||
}
|
||||
return NextResponse.json(await arcadeService.listArcadePacks(classId, gameType));
|
||||
}
|
||||
|
||||
export async function POST(request: NextRequest) {
|
||||
const parsed = arcadePackCreateSchema.safeParse(await request.json().catch(() => null));
|
||||
if (!parsed.success) {
|
||||
return NextResponse.json({ error: "Invalid Arcade pack request", details: parsed.error.issues }, { status: 400 });
|
||||
}
|
||||
try {
|
||||
const packs = await arcadeService.createArcadePacks(parsed.data);
|
||||
return NextResponse.json({ packs, count: packs.length }, { status: 201 });
|
||||
} catch (error) {
|
||||
if (error instanceof ArcadeImportError) {
|
||||
return NextResponse.json({ error: error.message, details: error.details }, { status: 400 });
|
||||
}
|
||||
if (error instanceof Error) {
|
||||
const status = error.message === "Class not found" ? 404 : 400;
|
||||
return NextResponse.json({ error: error.message }, { status });
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,21 @@
|
|||
import { NextRequest, NextResponse } from "next/server";
|
||||
import * as settingsService from "@/services/settingsService";
|
||||
|
||||
function getInstructionType(request: NextRequest): settingsService.LlmInstructionType | null {
|
||||
const type = new URL(request.url).searchParams.get("type") ?? "flashcards";
|
||||
return type === "flashcards" || type === "quizzes" || type === "connections" ? type : null;
|
||||
}
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const type = searchParams.get("type") as "flashcards" | "quizzes" || "flashcards";
|
||||
const type = getInstructionType(request);
|
||||
if (!type) return NextResponse.json({ error: "Invalid instruction type" }, { status: 400 });
|
||||
const instructions = await settingsService.getLlmInstructions(type);
|
||||
return NextResponse.json({ value: instructions });
|
||||
}
|
||||
|
||||
export async function PATCH(request: NextRequest) {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const type = searchParams.get("type") as "flashcards" | "quizzes" || "flashcards";
|
||||
const type = getInstructionType(request);
|
||||
if (!type) return NextResponse.json({ error: "Invalid instruction type" }, { status: 400 });
|
||||
const body = await request.json().catch(() => null);
|
||||
|
||||
if (!body || typeof body.value !== "string") {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue