No props added yet.
";
}
appState.props.forEach(prop => {
const item = document.createElement("div");
item.className = "plc-prop-item";
item.innerHTML = `
${prop.name} (Scene: ${prop.scene || 'N/A'})
`;
propsList.appendChild(item);
});
}
function getUniqueScenes() {
const scenes = new Set();
appState.props.forEach(prop => {
if (prop.scene && prop.scene.trim()) {
scenes.add(prop.scene.trim());
}
});
return Array.from(scenes).sort();
}
function renderDashboardTab() {
projectTitleDisplay.textContent = `${appState.projectName} - Prop List`;
// Populate Scene Filter
const uniqueScenes = getUniqueScenes();
const currentSceneFilter = filterSceneSelect.value; // Preserve selection
filterSceneSelect.innerHTML = '
';
uniqueScenes.forEach(scene => {
const option = document.createElement('option');
option.value = scene;
option.textContent = scene;
filterSceneSelect.appendChild(option);
});
filterSceneSelect.value = currentSceneFilter; // Restore selection
// Filter props based on selections
const sceneFilter = filterSceneSelect.value;
const statusFilter = filterStatusSelect.value;
const filteredProps = appState.props.filter(prop => {
const sceneMatch = sceneFilter === 'all' || prop.scene === sceneFilter;
const statusMatch = statusFilter === 'all' || prop.status === statusFilter;
return sceneMatch && statusMatch;
});
// Render Table
tableBody.innerHTML = "";
if (filteredProps.length === 0) {
tableBody.innerHTML = "
| No props match the current filters. |
";
return;
}
filteredProps.forEach(prop => {
const tr = document.createElement("tr");
tr.innerHTML = `
${prop.name} |
${prop.description || ''} |
${prop.scene || ''} |
${prop.status || ''} |
`;
tableBody.appendChild(tr);
});
}
// --- Event Handlers ---
// Update Dashboard Button
updateBtn.addEventListener("click", () => {
appState.projectName = projectNameInput.value || "Untitled Project";
saveState();
renderDashboardTab(); // Re-render dashboard with potentially new project name
showTab(0);
});
// Add Prop
addPropBtn.addEventListener("click", () => {
const name = propNameInput.value.trim();
const description = propDescInput.value.trim();
const scene = propSceneInput.value.trim();
const status = propStatusSelect.value;
if (!name) {
alert("Please enter a prop name.");
return;
}
const newProp = {
id: Date.now(),
name: name,
description: description,
scene: scene,
status: status
};
appState.props.push(newProp);
saveState();
renderConfigTab(); // Update the list in config tab
// Clear form
propNameInput.value = "";
propDescInput.value = "";
propSceneInput.value = "";
propStatusSelect.value = "To Source";
});
// Remove Prop (Event Delegation)
propsList.addEventListener("click", (e) => {
if (e.target.classList.contains("plc-remove-btn")) {
const id = parseInt(e.target.dataset.id);
appState.props = appState.props.filter(p => p.id !== id);
saveState();
renderConfigTab(); // Update list in config
}
});
// Filters Change
filterSceneSelect.addEventListener("change", renderDashboardTab);
filterStatusSelect.addEventListener("change", renderDashboardTab);
// PDF Download
pdfBtn.addEventListener("click", () => {
const { jsPDF } = window.jspdf;
const fileName = `${appState.projectName.replace(/ /g, '_')}_Prop_List.pdf`;
// Clone the export area to modify for PDF without affecting the screen
const clonedExportArea = exportArea.cloneNode(true);
// Optional: Remove filter display from PDF?
// const filtersElement = clonedExportArea.querySelector('.plc-filters');
// if(filtersElement) filtersElement.remove();
html2canvas(clonedExportArea, { // Use the cloned node
scale: 2,
useCORS: true,
backgroundColor: '#ffffff'
}).then(canvas => {
const imgData = canvas.toDataURL('image/png');
const doc = new jsPDF({
orientation: 'p', // Portrait is usually better for lists
unit: 'pt',
format: 'a4'
});
const pdfWidth = doc.internal.pageSize.getWidth();
const pdfHeight = doc.internal.pageSize.getHeight();
const imgProps = doc.getImageProperties(imgData);
const imgWidth = imgProps.width;
const imgHeight = imgProps.height;
const margin = 40;
const usableWidth = pdfWidth - (2 * margin);
const ratio = usableWidth / imgWidth;
let scaledHeight = imgHeight * ratio;
let position = margin;
// Handle multi-page if needed
if (scaledHeight > pdfHeight - (2 * margin)) {
const pageHeight = pdfHeight - (2 * margin);
let heightLeft = scaledHeight;
while (heightLeft > 0) {
// Calculate the height of the portion of the image to add for this page
const pageImageHeight = Math.min(pageHeight / ratio, imgHeight - (scaledHeight - heightLeft) / ratio);
// Calculate the y offset in the original image
const imageOffsetY = (scaledHeight - heightLeft) / ratio;
// Create a temporary canvas to draw the slice
const tempCanvas = document.createElement('canvas');
tempCanvas.width = imgWidth;
tempCanvas.height = pageImageHeight;
const tempCtx = tempCanvas.getContext('2d');
tempCtx.drawImage(canvas, 0, imageOffsetY, imgWidth, pageImageHeight, 0, 0, imgWidth, pageImageHeight);
const pageImgData = tempCanvas.toDataURL('image/png');
if (position !== margin) { // Add new page if not the first page
doc.addPage();
}
doc.addImage(pageImgData, 'PNG', margin, margin, usableWidth, pageHeight);
heightLeft -= pageHeight;
position += pageHeight; // Update position conceptually for the loop
}
} else {
// Single page
doc.addImage(imgData, 'PNG', margin, margin, usableWidth, scaledHeight);
}
doc.save(fileName);
}).catch(err => {
console.error("PLC PDF Error:", err);
// alert("An error occurred while generating the PDF."); // Per spec
});
});
// --- Local Storage ---
function saveState() {
try {
localStorage.setItem("plcAppState", JSON.stringify(appState));
} catch (e) { console.warn("PLC: Could not save state."); }
}
function loadState() {
try {
const storedState = localStorage.getItem("plcAppState");
if (storedState) appState = JSON.parse(storedState);
} catch (e) { console.warn("PLC: Could not load state."); }
}
// --- Initial Load ---
loadState();
renderConfigTab(); // Load state into config first
renderDashboardTab(); // Then render dashboard
showTab(0); // Start on Dashboard
});