mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-08 03:50:21 +00:00
fixed stats page to show correct number of skipped clues. reflected this change accurately in leaderboards for users and admins as to not create a bug where a skipped clue would contribute to progress
This commit is contained in:
1 parent
2f9a6c732c
commit
4cd449c0cf
7 files changed
+43
-36
No files matched your search
@@ -338,8 +338,10 @@ export default function MapPage() {
|
|||||||
// Function to determine clue button class based on status
|
// Function to determine clue button class based on status
|
||||||
const getClueButtonClass = (clue) => {
|
const getClueButtonClass = (clue) => {
|
||||||
if (clue.found) return `${styles["clue-button"]} ${styles["clue-found"]}`;
|
if (clue.found) return `${styles["clue-button"]} ${styles["clue-found"]}`;
|
||||||
if (clue.skipped) return `${styles["clue-button"]} ${styles["clue-skipped"]}`;
|
if (clue.skipped)
|
||||||
if (clue.available) return `${styles["clue-button"]} ${styles["clue-available"]}`;
|
return `${styles["clue-button"]} ${styles["clue-skipped"]}`;
|
||||||
|
if (clue.available)
|
||||||
|
return `${styles["clue-button"]} ${styles["clue-available"]}`;
|
||||||
return `${styles["clue-button"]} ${styles["clue-locked"]}`;
|
return `${styles["clue-button"]} ${styles["clue-locked"]}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+19
-11
@@ -86,14 +86,18 @@ export default function StatsPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="clues-section">
|
<div className="clues-section">
|
||||||
<h3 className="section-title">Found Clues ({foundClues?.length || 0})</h3>
|
<h3 className="section-title">
|
||||||
|
Found Clues ({foundClues?.length || 0})
|
||||||
|
</h3>
|
||||||
<ul className="clue-list">
|
<ul className="clue-list">
|
||||||
{foundClues?.length > 0 ? (
|
{foundClues?.length > 0 ? (
|
||||||
foundClues.map(clue => (
|
foundClues.map((clue) => (
|
||||||
<li key={clue.id} className="clue-item">
|
<li key={clue.id} className="clue-item">
|
||||||
<div className="clue-icon found">✓</div>
|
<div className="clue-icon found">✓</div>
|
||||||
<div className="clue-details">
|
<div className="clue-details">
|
||||||
<div className="clue-title">{clue.title || `Clue #${clue.id}`}</div>
|
<div className="clue-title">
|
||||||
|
{clue.title || `Clue #${clue.id}`}
|
||||||
|
</div>
|
||||||
<div className="clue-time">
|
<div className="clue-time">
|
||||||
{new Date(clue.found_at).toLocaleTimeString()}
|
{new Date(clue.found_at).toLocaleTimeString()}
|
||||||
</div>
|
</div>
|
||||||
@@ -105,14 +109,18 @@ export default function StatsPage() {
|
|||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3 className="section-title">Skipped Clues ({skippedClues?.length || 0})</h3>
|
<h3 className="section-title">
|
||||||
|
Skipped Clues ({skippedClues?.length || 0})
|
||||||
|
</h3>
|
||||||
<ul className="clue-list">
|
<ul className="clue-list">
|
||||||
{skippedClues?.length > 0 ? (
|
{skippedClues?.length > 0 ? (
|
||||||
skippedClues.map(clue => (
|
skippedClues.map((clue) => (
|
||||||
<li key={clue.id} className="clue-item">
|
<li key={clue.id} className="clue-item">
|
||||||
<div className="clue-icon skipped">↷</div>
|
<div className="clue-icon skipped">↷</div>
|
||||||
<div className="clue-details">
|
<div className="clue-details">
|
||||||
<div className="clue-title">{clue.title || `Clue #${clue.id}`}</div>
|
<div className="clue-title">
|
||||||
|
{clue.title || `Clue #${clue.id}`}
|
||||||
|
</div>
|
||||||
<div className="clue-time">
|
<div className="clue-time">
|
||||||
Skipped at {new Date(clue.found_at).toLocaleTimeString()}
|
Skipped at {new Date(clue.found_at).toLocaleTimeString()}
|
||||||
</div>
|
</div>
|
||||||
@@ -182,7 +190,7 @@ export default function StatsPage() {
|
|||||||
<div className={styles.statItem}>
|
<div className={styles.statItem}>
|
||||||
<span className={styles.statLabel}>Clues Found:</span>
|
<span className={styles.statLabel}>Clues Found:</span>
|
||||||
<span className={styles.statValue}>
|
<span className={styles.statValue}>
|
||||||
{teamStats.foundClues.length}
|
{teamStats.foundClues.length} of {teamStats.totalClues}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.statItem}>
|
<div className={styles.statItem}>
|
||||||
@@ -191,7 +199,9 @@ export default function StatsPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.statItem}>
|
<div className={styles.statItem}>
|
||||||
<span className={styles.statLabel}>Clues Skipped:</span>
|
<span className={styles.statLabel}>Clues Skipped:</span>
|
||||||
<span className={styles.statValue}>{teamStats.skippedClues}</span>
|
<span className={styles.statValue}>
|
||||||
|
{teamStats.skippedCluesCount}
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className={`${styles.statItem} ${styles.rankItem}`}>
|
<div className={`${styles.statItem} ${styles.rankItem}`}>
|
||||||
<span className={styles.statLabel}>Current Rank:</span>
|
<span className={styles.statLabel}>Current Rank:</span>
|
||||||
@@ -214,9 +224,7 @@ export default function StatsPage() {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
{expanded && (
|
{expanded && (
|
||||||
<div className={styles.cluesList}>
|
<div className={styles.cluesList}>{renderCluesList()}</div>
|
||||||
{renderCluesList()}
|
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -38,13 +38,14 @@ export async function GET(request) {
|
|||||||
const totalClues = clueCountResult?.total || 0;
|
const totalClues = clueCountResult?.total || 0;
|
||||||
|
|
||||||
// Get all teams with member count and clues found
|
// Get all teams with member count and clues found
|
||||||
|
// Modified to exclude skipped clues from cluesFound count
|
||||||
const teams = await query(`
|
const teams = await query(`
|
||||||
SELECT
|
SELECT
|
||||||
t.id,
|
t.id,
|
||||||
t.name,
|
t.name,
|
||||||
t.code,
|
t.code,
|
||||||
COUNT(DISTINCT u.id) as memberCount,
|
COUNT(DISTINCT u.id) as memberCount,
|
||||||
COUNT(DISTINCT tc.clue_id) as cluesFound,
|
COUNT(DISTINCT CASE WHEN tc.skipped = 0 OR tc.skipped IS NULL THEN tc.clue_id END) as cluesFound,
|
||||||
MAX(tc.found_at) as lastActivity
|
MAX(tc.found_at) as lastActivity
|
||||||
FROM
|
FROM
|
||||||
teams t
|
teams t
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ async function verifyAdmin(request) {
|
|||||||
return { authorized: true, token };
|
return { authorized: true, token };
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function POST(request, context) {
|
// Make sure the reset progress API also handles the skipped flag
|
||||||
|
export async function POST(request, { params }) {
|
||||||
try {
|
try {
|
||||||
// Verify admin access
|
// Verify admin access
|
||||||
const { authorized, error, status } = await verifyAdmin(request);
|
const { authorized, error, status } = await verifyAdmin(request);
|
||||||
@@ -31,7 +32,7 @@ export async function POST(request, context) {
|
|||||||
return Response.json({ error }, { status });
|
return Response.json({ error }, { status });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { teamId } = context.params;
|
const { teamId } = params;
|
||||||
|
|
||||||
// Validate teamId is a number
|
// Validate teamId is a number
|
||||||
if (isNaN(parseInt(teamId))) {
|
if (isNaN(parseInt(teamId))) {
|
||||||
@@ -44,7 +45,7 @@ export async function POST(request, context) {
|
|||||||
return Response.json({ error: "Team not found" }, { status: 404 });
|
return Response.json({ error: "Team not found" }, { status: 404 });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete all clue progress for the team
|
// When resetting progress, make sure to reset skipped status too
|
||||||
await run("DELETE FROM team_clues WHERE team_id = ?", [teamId]);
|
await run("DELETE FROM team_clues WHERE team_id = ?", [teamId]);
|
||||||
|
|
||||||
return Response.json({
|
return Response.json({
|
||||||
|
|||||||
@@ -25,12 +25,13 @@ export async function GET(request) {
|
|||||||
|
|
||||||
// Get all teams with progress info and member count
|
// Get all teams with progress info and member count
|
||||||
// Only include teams that have at least one member
|
// Only include teams that have at least one member
|
||||||
|
// Modify the SQL query to exclude skipped clues from the count
|
||||||
const teams = await query(`
|
const teams = await query(`
|
||||||
SELECT
|
SELECT
|
||||||
t.id,
|
t.id,
|
||||||
t.name,
|
t.name,
|
||||||
t.code,
|
t.code,
|
||||||
COUNT(DISTINCT tc.clue_id) as cluesFound,
|
COUNT(DISTINCT CASE WHEN tc.skipped = 0 OR tc.skipped IS NULL THEN tc.clue_id END) as cluesFound,
|
||||||
MAX(tc.found_at) as lastActivity,
|
MAX(tc.found_at) as lastActivity,
|
||||||
COUNT(DISTINCT u.id) as memberCount
|
COUNT(DISTINCT u.id) as memberCount
|
||||||
FROM
|
FROM
|
||||||
@@ -49,6 +50,7 @@ export async function GET(request) {
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
// Add progress percentage and total clues to each team
|
// Add progress percentage and total clues to each team
|
||||||
|
// Make sure the progress calculation is consistent
|
||||||
const teamsWithProgress = teams.map((team) => ({
|
const teamsWithProgress = teams.map((team) => ({
|
||||||
...team,
|
...team,
|
||||||
totalClues,
|
totalClues,
|
||||||
|
|||||||
+11
-18
@@ -66,29 +66,16 @@ export async function GET(request) {
|
|||||||
[token.teamId]
|
[token.teamId]
|
||||||
);
|
);
|
||||||
|
|
||||||
// We need to calculate skipped clues - these are clues that have higher IDs than the user's lowest unfound clue
|
// Get all clue IDs - this part is still useful for other calculations
|
||||||
// First, get all clue IDs
|
|
||||||
const allClueIds = await query("SELECT id FROM clues ORDER BY id ASC");
|
const allClueIds = await query("SELECT id FROM clues ORDER BY id ASC");
|
||||||
|
|
||||||
// Convert to a simple array of IDs
|
|
||||||
const allClueIdArray = allClueIds.map((c) => c.id);
|
const allClueIdArray = allClueIds.map((c) => c.id);
|
||||||
|
|
||||||
// Create a set of found clue IDs for easy lookup
|
|
||||||
const foundClueIdSet = new Set(foundClues.map((c) => c.id));
|
const foundClueIdSet = new Set(foundClues.map((c) => c.id));
|
||||||
|
|
||||||
// Find all unfound clue IDs
|
|
||||||
const unfoundClueIds = allClueIdArray.filter(
|
const unfoundClueIds = allClueIdArray.filter(
|
||||||
(id) => !foundClueIdSet.has(id)
|
(id) => !foundClueIdSet.has(id)
|
||||||
);
|
);
|
||||||
|
|
||||||
// If there are unfound clues, the ones after the first unfound are "skipped"
|
// Replace the old skipped clues calculation with this simple count
|
||||||
let skippedCluesCount = 0;
|
let skippedCluesCount = skippedClues.length;
|
||||||
if (unfoundClueIds.length > 0) {
|
|
||||||
const lowestUnfoundId = Math.min(...unfoundClueIds);
|
|
||||||
|
|
||||||
// Skipped clues are found clues with IDs higher than the lowest unfound
|
|
||||||
skippedCluesCount = foundClues.filter((c) => c.id > lowestUnfoundId).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get current team rank
|
// Get current team rank
|
||||||
const teamRankings = await query(`
|
const teamRankings = await query(`
|
||||||
@@ -114,16 +101,22 @@ export async function GET(request) {
|
|||||||
let currentRank = teamRankings.findIndex((t) => t.id === team.id) + 1;
|
let currentRank = teamRankings.findIndex((t) => t.id === team.id) + 1;
|
||||||
if (currentRank === 0) currentRank = teamRankings.length + 1; // If not found (shouldn't happen)
|
if (currentRank === 0) currentRank = teamRankings.length + 1; // If not found (shouldn't happen)
|
||||||
|
|
||||||
|
// Update progress calculation - don't subtract skippedCluesCount since foundClues query already excludes skipped clues
|
||||||
|
const correctlyFoundCount = foundClues.length; // <-- Fixed: don't subtract again
|
||||||
|
|
||||||
// Construct response object
|
// Construct response object
|
||||||
const teamStats = {
|
const teamStats = {
|
||||||
team,
|
team,
|
||||||
totalClues,
|
totalClues,
|
||||||
foundClues,
|
foundClues,
|
||||||
remainingClues: totalClues - foundClues.length,
|
skippedClues,
|
||||||
|
remainingClues: totalClues - correctlyFoundCount,
|
||||||
skippedCluesCount,
|
skippedCluesCount,
|
||||||
currentRank,
|
currentRank,
|
||||||
progress:
|
progress:
|
||||||
totalClues > 0 ? Math.round((foundClues.length / totalClues) * 100) : 0,
|
totalClues > 0
|
||||||
|
? Math.round((correctlyFoundCount / totalClues) * 100)
|
||||||
|
: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Return only the teamStats object instead of trying to include undefined variables
|
// Return only the teamStats object instead of trying to include undefined variables
|
||||||
|
|||||||
Binary file not shown.
Reference in new issue
Block a user