mirror of
https://github.com/mudabbir-ahmad/peworkshop.git
synced 2026-10-07 19:50:20 +00:00
178 lines
5.5 KiB
JavaScript
178 lines
5.5 KiB
JavaScript
/**
|
|
* Utility module for processing and populating the treasure map SVG.
|
|
* Handles clue points, their status, and interaction.
|
|
*/
|
|
|
|
/**
|
|
* Populates the SVG map with clue points
|
|
* @param {SVGElement} svgElement - The SVG DOM element to process
|
|
* @returns {Promise<boolean>} - Success flag
|
|
*/
|
|
export async function populateMap(svgElement) {
|
|
try {
|
|
if (!svgElement) {
|
|
console.error("No SVG element provided");
|
|
return false;
|
|
}
|
|
|
|
console.log("Starting map population process");
|
|
|
|
// First, find all existing circle elements in the SVG
|
|
const existingCircles = Array.from(svgElement.querySelectorAll("circle"));
|
|
console.log(`Found ${existingCircles.length} existing circles in SVG`);
|
|
|
|
// Hide all existing circles initially
|
|
existingCircles.forEach((circle) => {
|
|
circle.style.display = "none";
|
|
});
|
|
|
|
// Query the number of clues via API
|
|
const clueCountResponse = await fetch("/api/clues/count", {
|
|
credentials: "include",
|
|
cache: "no-cache",
|
|
});
|
|
|
|
if (!clueCountResponse.ok) {
|
|
throw new Error(
|
|
`Failed to fetch clue count: ${clueCountResponse.status}`
|
|
);
|
|
}
|
|
|
|
const { count: clueCount } = await clueCountResponse.json();
|
|
console.log(`Total clues: ${clueCount}`);
|
|
|
|
// Determine how many circles to display (min of existing circles and clue count)
|
|
const circlesToShow = Math.min(existingCircles.length, clueCount);
|
|
console.log(`Will display ${circlesToShow} circles`);
|
|
|
|
// Sort circles by vertical position (top to bottom)
|
|
const sortedCircles = existingCircles.sort((a, b) => {
|
|
const aY = parseFloat(a.getAttribute("cy"));
|
|
const bY = parseFloat(b.getAttribute("cy"));
|
|
return aY - bY;
|
|
});
|
|
|
|
// Display only the needed circles and add clue-point class
|
|
for (let i = 0; i < circlesToShow; i++) {
|
|
const circle = sortedCircles[i];
|
|
circle.style.display = "";
|
|
circle.classList.add("clue-point");
|
|
circle.setAttribute("data-clue-id", i + 1);
|
|
|
|
// Set default appearance
|
|
circle.style.fill = "#9e9e9e"; // Default grey color
|
|
circle.style.stroke = "#757575";
|
|
circle.style.strokeWidth = "2";
|
|
}
|
|
|
|
return true;
|
|
} catch (error) {
|
|
console.error("Error populating map:", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Updates the status of each clue point based on clues data
|
|
* @param {SVGElement} svgElement - The SVG DOM element
|
|
* @param {Array} clues - Array of clue objects with their status
|
|
*/
|
|
export function updateCluePointStatus(svgElement, clues) {
|
|
if (!svgElement || !clues || !clues.length) {
|
|
console.error("Missing required parameters for updating clue status");
|
|
return;
|
|
}
|
|
|
|
console.log("Updating clue point status");
|
|
|
|
try {
|
|
// Get all clue points
|
|
const cluePoints = svgElement.querySelectorAll(".clue-point");
|
|
|
|
if (!cluePoints.length) {
|
|
console.error("No clue points found in SVG");
|
|
return;
|
|
}
|
|
|
|
// Find the first unfound clue (the current one)
|
|
const currentClueIndex = clues.findIndex((clue) => !clue.found);
|
|
const currentClueId =
|
|
currentClueIndex !== -1 ? clues[currentClueIndex].id : null;
|
|
|
|
// Process each clue point
|
|
cluePoints.forEach((circle, index) => {
|
|
if (index >= clues.length) return;
|
|
|
|
const clue = clues[index];
|
|
const clueId = clue.id;
|
|
|
|
// Determine status and apply appropriate color
|
|
if (clue.found) {
|
|
// Found/completed clue - Dark Green
|
|
circle.style.fill = "#2e7d32"; // Dark green
|
|
} else if (clueId === currentClueId) {
|
|
// Current clue - Orange
|
|
circle.style.fill = "#ff9800"; // Orange
|
|
} else if (clueId < currentClueId) {
|
|
// Skipped clue - Blue
|
|
circle.style.fill = "#1976d2"; // Blue
|
|
} else {
|
|
// Not accessible yet - Grey
|
|
circle.style.fill = "#9e9e9e"; // Grey
|
|
}
|
|
|
|
// Mark whether this clue point is available
|
|
const isAvailable = clue.found || clueId === currentClueId;
|
|
circle.setAttribute("data-available", isAvailable.toString());
|
|
});
|
|
|
|
console.log("Clue points updated successfully");
|
|
} catch (error) {
|
|
console.error("Error updating clue status:", error);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attaches click handlers to clue points
|
|
* @param {SVGElement} svgElement - The SVG DOM element
|
|
* @param {Array} clues - Array of clue objects with their status
|
|
* @param {Function} navigationHandler - Function to handle navigation
|
|
*/
|
|
export function attachClueClickHandlers(svgElement, clues, navigationHandler) {
|
|
if (!svgElement || !clues || !navigationHandler) {
|
|
console.error("Missing required parameters for attaching click handlers");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const cluePoints = svgElement.querySelectorAll(".clue-point");
|
|
|
|
cluePoints.forEach((circle, index) => {
|
|
if (index >= clues.length) return;
|
|
|
|
const clue = clues[index];
|
|
const isAvailable = circle.getAttribute("data-available") === "true";
|
|
|
|
// Set cursor style based on availability
|
|
circle.style.cursor = isAvailable ? "pointer" : "not-allowed";
|
|
|
|
// Add click handler
|
|
circle.onclick = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
|
|
if (isAvailable) {
|
|
console.log(`Clicked on clue ${clue.id}`);
|
|
navigationHandler(clue.id);
|
|
} else {
|
|
// Alert or other feedback if clue is not accessible
|
|
console.log("Clue not available yet");
|
|
// Could show a tooltip or message here
|
|
}
|
|
};
|
|
});
|
|
} catch (error) {
|
|
console.error("Error attaching click handlers:", error);
|
|
}
|
|
}
|