fixed skip clue implimentation

This commit is contained in:
bobbert committed 2025-03-18 22:25:07 +00:00
1 parent fccc2a4edc
commit 2f9a6c732c
18 files changed
+524 -151

No files matched your search

+5 -4
View File
@@ -43,9 +43,9 @@ export async function GET(request, context) {
return Response.json({ error: "Clue not found" }, { status: 404 });
}
// Check if this team has found this clue before
const found = await queryOne(
"SELECT * FROM team_clues WHERE team_id = ? AND clue_id = ?",
// Check if this team has found or skipped this clue before
const clueStatus = await queryOne(
"SELECT found_at, skipped FROM team_clues WHERE team_id = ? AND clue_id = ?",
[token.teamId, clueId]
);
@@ -66,7 +66,8 @@ export async function GET(request, context) {
return Response.json(
{
...clue,
found: !!found, // Only mark as found if it was previously found
found: !!clueStatus && !(clueStatus.skipped === 1), // Mark as found only if it exists and is not skipped
skipped: !!clueStatus && clueStatus.skipped === 1, // Add skipped status
isAdmin: token.role === "admin", // Add flag so client knows user is admin
},
{ headers: cacheHeaders }
+69 -9
View File
@@ -1,4 +1,4 @@
import { query, queryOne } from "@/lib/db";
import { query, queryOne, run } from "@/lib/db";
import { getToken } from "next-auth/jwt";
export const dynamic = "force-dynamic"; // Disable caching
@@ -13,18 +13,50 @@ export async function GET(request) {
});
if (!token) {
console.log("API: Unauthorized request to /api/clues - no token");
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
// Check if user has a team
if (!token.teamId) {
console.log("API: User without team tried to access clues");
return Response.json(
{ error: "You must be part of a team to view clues" },
{ status: 403 }
);
}
// Get all clues with explicit ordering - removed coordinates
// First, ensure the team_clues table has the 'skipped' column
try {
// Check if team_clues table exists, create it if it doesn't
await run(`
CREATE TABLE IF NOT EXISTS team_clues (
team_id INTEGER NOT NULL,
clue_id INTEGER NOT NULL,
found_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (team_id, clue_id),
FOREIGN KEY (team_id) REFERENCES teams(id),
FOREIGN KEY (clue_id) REFERENCES clues(id)
)
`);
// Check if skipped column exists
const tableInfo = await query("PRAGMA table_info(team_clues)");
const hasSkippedColumn = tableInfo.some((col) => col.name === "skipped");
if (!hasSkippedColumn) {
console.log("Adding 'skipped' column to team_clues table");
await run(
"ALTER TABLE team_clues ADD COLUMN skipped BOOLEAN DEFAULT 0"
);
}
} catch (schemaError) {
console.error("Error ensuring database schema:", schemaError);
// Continue execution - if this fails, the main query might still work
// with existing columns
}
// Get all clues with explicit ordering
const clues = await query(`
SELECT id, title, description, location
FROM clues
@@ -34,21 +66,49 @@ export async function GET(request) {
// For each clue, check if the team has found it
const cluesWithStatus = await Promise.all(
clues.map(async (clue) => {
const found = await queryOne(
"SELECT 1 FROM team_clues WHERE team_id = ? AND clue_id = ?",
[token.teamId, clue.id]
);
let clueStatus;
try {
clueStatus = await queryOne(
"SELECT found_at, skipped FROM team_clues WHERE team_id = ? AND clue_id = ?",
[token.teamId, clue.id]
);
} catch (error) {
console.error(`Error querying status for clue ${clue.id}:`, error);
// Fallback if query fails (e.g., skipped column still doesn't exist)
clueStatus = await queryOne(
"SELECT found_at FROM team_clues WHERE team_id = ? AND clue_id = ?",
[token.teamId, clue.id]
);
}
return {
...clue,
found: !!found,
found: !!clueStatus && !(clueStatus.skipped === 1),
skipped: !!clueStatus && clueStatus.skipped === 1,
available: false, // Will be determined after processing all clues
};
})
);
console.log(`Returning ${cluesWithStatus.length} clues`);
// Determine which clues should be available
let firstUnavailableFound = false;
const finalClues = cluesWithStatus.map((clue, index, arr) => {
if (clue.found || clue.skipped) {
return { ...clue, available: true };
}
return Response.json(cluesWithStatus, {
// If we haven't found an unavailable clue yet, this one is available
if (!firstUnavailableFound) {
firstUnavailableFound = true;
return { ...clue, available: true };
}
return { ...clue, available: false };
});
console.log(`API: Returning ${finalClues.length} clues`);
return Response.json(finalClues, {
headers: {
"Cache-Control": "no-store, no-cache, must-revalidate",
Pragma: "no-cache",
+41 -19
View File
@@ -1,4 +1,4 @@
import { query, queryOne } from "@/lib/db";
import { query, queryOne, run } from "@/lib/db"; // Add 'run' to the import
import { getToken } from "next-auth/jwt";
export const dynamic = "force-dynamic"; // Disable caching
@@ -25,7 +25,7 @@ export async function POST(request) {
}
// Parse request body
const { clueId } = await request.json();
const { clueId, skipped = false } = await request.json();
// Validate clueId is a number
if (isNaN(parseInt(clueId))) {
@@ -38,49 +38,71 @@ export async function POST(request) {
return Response.json({ error: "Clue not found" }, { status: 404 });
}
// Check if clue was already found
const alreadyFound = await queryOne(
// Check if clue was already found or skipped
const alreadyProcessed = await queryOne(
"SELECT * FROM team_clues WHERE team_id = ? AND clue_id = ?",
[token.teamId, clueId]
);
if (alreadyFound) {
if (alreadyProcessed) {
// If skipping is requested, update the record regardless of previous state
if (skipped) {
await run(
"UPDATE team_clues SET skipped = 1 WHERE team_id = ? AND clue_id = ?",
[token.teamId, clueId]
);
return Response.json({
success: true,
message: "Clue marked as skipped",
skipped: true,
});
}
// For non-skip requests on already processed clues
return Response.json({
success: true,
message: "This clue was already marked as found",
alreadyFound: true,
message: "This clue was already processed",
alreadyFound: !(alreadyProcessed.skipped === 1),
alreadySkipped: alreadyProcessed.skipped === 1,
});
}
// Create team_clues table if it doesn't exist
await queryOne(`
// Ensure the team_clues table has a skipped column
await run(`
CREATE TABLE IF NOT EXISTS team_clues (
team_id INTEGER NOT NULL,
clue_id INTEGER NOT NULL,
found_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
skipped BOOLEAN DEFAULT 0,
PRIMARY KEY (team_id, clue_id),
FOREIGN KEY (team_id) REFERENCES teams(id),
FOREIGN KEY (clue_id) REFERENCES clues(id)
)
`);
// Record that the team found this clue
await queryOne("INSERT INTO team_clues (team_id, clue_id) VALUES (?, ?)", [
token.teamId,
clueId,
]);
// Add column if it doesn't exist (safe operation)
try {
await run("ALTER TABLE team_clues ADD COLUMN skipped BOOLEAN DEFAULT 0");
} catch (e) {
// Column likely already exists, continue
}
console.log(`Clue ${clueId} marked as found for team ${token.teamId}`);
// Record that the team found or skipped this clue
await run(
"INSERT INTO team_clues (team_id, clue_id, skipped) VALUES (?, ?, ?)",
[token.teamId, clueId, skipped ? 1 : 0]
);
// Return successful response
return Response.json({
success: true,
message: "Clue marked as found successfully",
message: skipped ? "Clue skipped successfully" : "Clue marked as found",
skipped: skipped,
});
} catch (error) {
console.error("Error marking clue as found:", error);
console.error("Error marking clue:", error);
return Response.json(
{ error: "Failed to mark clue as found: " + error.message },
{ error: "Failed to process clue: " + error.message },
{ status: 500 }
);
}
+18 -4
View File
@@ -42,12 +42,25 @@ export async function GET(request) {
const totalClues = clueCountResult?.total || 0;
// Get all found clues with timestamps, ordered by found time
// Only include clues that aren't marked as skipped
const foundClues = await query(
`
SELECT c.id, c.title, tc.found_at
FROM clues c
JOIN team_clues tc ON c.id = tc.clue_id
WHERE tc.team_id = ?
WHERE tc.team_id = ? AND (tc.skipped = 0 OR tc.skipped IS NULL)
ORDER BY tc.found_at ASC
`,
[token.teamId]
);
// Get skipped clues separately
const skippedClues = await query(
`
SELECT c.id, c.title, tc.found_at
FROM clues c
JOIN team_clues tc ON c.id = tc.clue_id
WHERE tc.team_id = ? AND tc.skipped = 1
ORDER BY tc.found_at ASC
`,
[token.teamId]
@@ -69,12 +82,12 @@ export async function GET(request) {
);
// If there are unfound clues, the ones after the first unfound are "skipped"
let skippedClues = 0;
let skippedCluesCount = 0;
if (unfoundClueIds.length > 0) {
const lowestUnfoundId = Math.min(...unfoundClueIds);
// Skipped clues are found clues with IDs higher than the lowest unfound
skippedClues = foundClues.filter((c) => c.id > lowestUnfoundId).length;
skippedCluesCount = foundClues.filter((c) => c.id > lowestUnfoundId).length;
}
// Get current team rank
@@ -107,12 +120,13 @@ export async function GET(request) {
totalClues,
foundClues,
remainingClues: totalClues - foundClues.length,
skippedClues,
skippedCluesCount,
currentRank,
progress:
totalClues > 0 ? Math.round((foundClues.length / totalClues) * 100) : 0,
};
// Return only the teamStats object instead of trying to include undefined variables
return Response.json(teamStats);
} catch (error) {
console.error("Error fetching team stats:", error);
+22 -22
View File
@@ -1,10 +1,9 @@
import { getToken } from "next-auth/jwt";
import { query, queryOne, run } from "@/lib/db";
import { nanoid } from "nanoid";
import { queryOne, run } from "@/lib/db";
export async function POST(request) {
try {
// Get user token from session
// Verify user authentication
const token = await getToken({
req: request,
secret:
@@ -13,30 +12,23 @@ export async function POST(request) {
});
if (!token) {
return Response.json({ error: "Invalid session" }, { status: 401 });
return Response.json({ error: "Unauthorized" }, { status: 401 });
}
// Extract user ID from the token
// Get user ID from token
const userId = token.id;
if (!userId) {
return Response.json(
{ error: "Invalid user information in session" },
{ status: 401 }
);
}
// Parse request body
const body = await request.json();
const { name } = body;
// Get team name from request body
const { name } = await request.json();
if (!name || name.trim() === "") {
if (!name || !name.trim()) {
return Response.json({ error: "Team name is required" }, { status: 400 });
}
// Generate a unique team code
const code = nanoid(6).toUpperCase();
const code = generateTeamCode();
// Create the team - updated schema to match actual database (no created_by column)
// Create teams table if it doesn't exist
await run(`
CREATE TABLE IF NOT EXISTS teams (
id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -50,6 +42,7 @@ export async function POST(request) {
const existingTeam = await queryOne("SELECT id FROM teams WHERE name = ?", [
name,
]);
if (existingTeam) {
return Response.json(
{ error: "Team name already exists" },
@@ -57,7 +50,7 @@ export async function POST(request) {
);
}
// Insert team without created_by field
// Insert team
const result = await run("INSERT INTO teams (name, code) VALUES (?, ?)", [
name,
code,
@@ -71,19 +64,26 @@ export async function POST(request) {
// Update the user's team_id
await run("UPDATE users SET team_id = ? WHERE id = ?", [teamId, userId]);
// Return a more complete response with all needed data
// Return success with team info
return Response.json({
success: true,
teamId,
teamName: name,
code,
message: "Team created successfully",
});
} catch (error) {
console.error("Create team error:", error);
console.error("Error creating team:", error);
return Response.json(
{ error: "Failed to create team: " + error.message },
{ status: 500 }
);
}
}
function generateTeamCode() {
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
let result = "";
for (let i = 0; i < 6; i++) {
result += characters.charAt(Math.floor(Math.random() * characters.length));
}
return result;
}
View File
Whitespace-only changes.