mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-07 19:50:20 +00:00
217 lines
6.4 KiB
JavaScript
217 lines
6.4 KiB
JavaScript
/**
|
|
* Utility to dynamically populate the map SVG with clue points
|
|
* based on the total number of clues in the database
|
|
*/
|
|
|
|
/**
|
|
* Fetches the total number of clues from the API
|
|
* @returns {Promise<number>} The total number of clues
|
|
*/
|
|
async function fetchTotalClues() {
|
|
try {
|
|
const response = await fetch("/api/clues", {
|
|
method: "GET",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
"Cache-Control": "no-store, no-cache, must-revalidate",
|
|
},
|
|
credentials: "include",
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`Failed to fetch clues: ${response.status}`);
|
|
}
|
|
|
|
const clues = await response.json();
|
|
return clues.length;
|
|
} catch (error) {
|
|
console.error("Error fetching total clues:", error);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Shows the specified number of clue points on the map
|
|
* @param {SVGElement} svgElement - The SVG DOM element of the map
|
|
* @param {number} numClues - Number of clues to display
|
|
* @returns {boolean} Success status
|
|
*/
|
|
function showCluePoints(svgElement, numClues) {
|
|
if (!svgElement || !numClues) {
|
|
console.error("Invalid SVG element or number of clues");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
// Find all clue points in the SVG - improved selector for better compatibility
|
|
const cluePoints = Array.from(svgElement.querySelectorAll("circle")).filter(
|
|
(circle) =>
|
|
circle.classList.contains("clue-point") ||
|
|
circle.getAttribute("class")?.includes("clue-point")
|
|
);
|
|
|
|
console.log(`Found ${cluePoints.length} clue points in SVG`);
|
|
|
|
// If no clue points are found, add them programmatically
|
|
if (cluePoints.length === 0) {
|
|
console.log("No clue points found, creating them dynamically");
|
|
return createCluePoints(svgElement, numClues);
|
|
}
|
|
|
|
// Hide all clue points first
|
|
cluePoints.forEach((circle) => {
|
|
circle.style.display = "none";
|
|
});
|
|
|
|
// Show only the required number of clue points
|
|
for (let i = 0; i < Math.min(numClues, cluePoints.length); i++) {
|
|
const circle = cluePoints[i];
|
|
circle.style.display = "";
|
|
// Ensure the circle has the clue-point class and a data-clue-id
|
|
circle.classList.add("clue-point");
|
|
if (!circle.hasAttribute("data-clue-id")) {
|
|
circle.setAttribute("data-clue-id", (i + 1).toString());
|
|
}
|
|
}
|
|
|
|
console.log(
|
|
`Showing ${Math.min(numClues, cluePoints.length)} clue points on map`
|
|
);
|
|
return true;
|
|
} catch (error) {
|
|
console.error("Error showing clue points:", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates clue points dynamically if they don't exist in the SVG
|
|
* @param {SVGElement} svgElement - The SVG DOM element of the map
|
|
* @param {number} numClues - Number of clues to create
|
|
* @returns {boolean} Success status
|
|
*/
|
|
function createCluePoints(svgElement, numClues) {
|
|
try {
|
|
const svgWidth = svgElement.viewBox.baseVal.width || 800;
|
|
const svgHeight = svgElement.viewBox.baseVal.height || 600;
|
|
|
|
// Define positions for clue points - spread across the map
|
|
const positions = [];
|
|
|
|
// Calculate positions in a grid pattern
|
|
const cols = Math.ceil(Math.sqrt(numClues));
|
|
const rows = Math.ceil(numClues / cols);
|
|
|
|
const marginX = svgWidth * 0.1;
|
|
const marginY = svgHeight * 0.1;
|
|
const spacingX = (svgWidth - 2 * marginX) / (cols - 1 || 1);
|
|
const spacingY = (svgHeight - 2 * marginY) / (rows - 1 || 1);
|
|
|
|
for (let row = 0; row < rows; row++) {
|
|
for (let col = 0; col < cols; col++) {
|
|
const index = row * cols + col;
|
|
if (index < numClues) {
|
|
positions.push({
|
|
x: marginX + col * spacingX,
|
|
y: marginY + row * spacingY,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create circles for each clue
|
|
positions.forEach((pos, index) => {
|
|
const circle = document.createElementNS(
|
|
"http://www.w3.org/2000/svg",
|
|
"circle"
|
|
);
|
|
circle.setAttribute("cx", pos.x);
|
|
circle.setAttribute("cy", pos.y);
|
|
circle.setAttribute("r", 10);
|
|
circle.setAttribute("fill", "#9e9e9e");
|
|
circle.setAttribute("stroke", "#757575");
|
|
circle.setAttribute("stroke-width", "2");
|
|
circle.setAttribute("class", "clue-point");
|
|
circle.setAttribute("data-clue-id", (index + 1).toString());
|
|
|
|
svgElement.appendChild(circle);
|
|
});
|
|
|
|
console.log(`Created ${numClues} clue points dynamically`);
|
|
return true;
|
|
} catch (error) {
|
|
console.error("Error creating clue points:", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main function to populate map with the correct number of clue points
|
|
* @param {SVGElement} svgElement - The SVG DOM element of the map
|
|
* @returns {Promise<boolean>} Success status
|
|
*/
|
|
export async function populateMap(svgElement) {
|
|
if (!svgElement) {
|
|
console.error("No SVG element provided");
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
console.log("Starting to populate map");
|
|
// Fetch total number of clues
|
|
const totalClues = await fetchTotalClues();
|
|
console.log("Total clues fetched:", totalClues);
|
|
|
|
if (totalClues > 0) {
|
|
// Show the appropriate number of clue points
|
|
return showCluePoints(svgElement, totalClues);
|
|
} else {
|
|
console.warn("No clues found in database");
|
|
return false;
|
|
}
|
|
} catch (error) {
|
|
console.error("Error populating map:", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the styling of clue points based on their status
|
|
* @param {SVGElement} svgElement - The SVG DOM element of the map
|
|
* @param {Array} clues - Array of clue objects with status information
|
|
*/
|
|
export function updateCluePointStatus(svgElement, clues) {
|
|
if (!svgElement || !clues || !clues.length) {
|
|
console.error("Invalid SVG element or clues data");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
clues.forEach((clue, index) => {
|
|
// Find the corresponding circle in the SVG
|
|
const circle = svgElement.querySelector(
|
|
`.clue-point[data-clue-id="${clue.id}"]`
|
|
);
|
|
|
|
if (circle) {
|
|
// Apply appropriate styling based on clue status
|
|
if (clue.found) {
|
|
// Found clues are green
|
|
circle.style.fill = "#4caf50";
|
|
circle.style.stroke = "#2e7d32";
|
|
} else if (clue.available) {
|
|
// Available clues are orange
|
|
circle.style.fill = "#ff9800";
|
|
circle.style.stroke = "#d87f00";
|
|
} else {
|
|
// Unavailable clues remain grey
|
|
circle.style.fill = "#9e9e9e";
|
|
circle.style.stroke = "#757575";
|
|
}
|
|
}
|
|
});
|
|
} catch (error) {
|
|
console.error("Error updating clue point status:", error);
|
|
}
|
|
}
|