No materials added yet.
";
return;
}
materials.forEach(m => {
const itemEl = document.createElement("div");
itemEl.className = "fssg-config-list-item";
itemEl.dataset.id = m.id;
itemEl.innerHTML = `
[${escapeHTML(m.category)}] ${escapeHTML(m.item)} (${escapeHTML(m.supplier)})
`;
configMaterialList.appendChild(itemEl);
});
}
// --- Dashboard Management ---
function renderDashboard(isInitial = true) {
// Get config values
const name = configDesignName.value;
const collection = configCollection.value;
const desc = configDesc.value;
// --- Build Dashboard HTML ---
dashboardOutput.innerHTML = `
`;
// Populate Table Body
const tbody = dashboardOutput.querySelector('#fssg-dash-tbody');
materials.forEach((m, index) => {
const tr = document.createElement('tr');
tr.dataset.id = m.id;
tr.innerHTML = `
${index + 1} |
|
|
|
|
`;
tbody.appendChild(tr);
});
setupDashboardListeners();
if (!isInitial) showTab('fssg-tab-dashboard');
}
/**
* Attaches listeners to the dashboard elements
*/
function setupDashboardListeners() {
const output = dashboardOutput;
if (!output) return;
// Event delegation for removal
output.addEventListener('click', (e) => {
if (e.target.dataset.action === 'remove-dash-item') {
const tr = e.target.closest('tr');
materials.splice(materials.findIndex(m => m.id === tr.dataset.id), 1);
// Rerender entirely to renumber and update state/config
renderDashboard(false);
updateConfigListDisplay();
}
});
// Event delegation for input/change updates
output.addEventListener('input', handleDashboardUpdate);
output.addEventListener('change', handleDashboardUpdate);
}
/**
* Handles updates made directly to the dashboard inputs/textareas
*/
function handleDashboardUpdate(e) {
const target = e.target;
const value = target.value;
// 1. Update Config header fields from Dashboard
if (target.id === 'fssg-dash-name') configDesignName.value = value;
else if (target.id === 'fssg-dash-collection') configCollection.value = value;
else if (target.id === 'fssg-dash-desc') configDesc.value = value;
// 2. Update Material State
const tr = target.closest('tr');
if (tr) {
const mId = tr.dataset.id;
const material = materials.find(m => m.id === mId);
if (material) {
const field = target.dataset.field;
if (field) {
material[field] = value;
// Rerender config list immediately
updateConfigListDisplay();
}
}
}
}
/**
* Generates a PDF report from the dashboard data
*/
function downloadPDF() {
if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF.autoTable === 'undefined') {
alert("Error: PDF libraries could not be loaded. Please try again.");
return;
}
// Get final data from dashboard inputs
const name = configDesignName.value || "Untitled Design";
const collection = configCollection.value || "N/A";
const desc = configDesc.value || "N/A";
const constructionNotes = fssgContainer.querySelector("#fssg-dash-construction")?.value || "None recorded.";
if (!name.trim()) {
alert("Please generate the sheet first.");
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF("p", "pt", "a4");
const margin = 40;
let yPos = margin;
const lineHeight = 14;
const usableWidth = doc.internal.pageSize.getWidth() - margin * 2;
// Function to add a structured text block
function addBlock(title, text, isTitle = false) {
const titleHeight = isTitle ? 25 : 20;
const textLines = doc.splitTextToSize(text, usableWidth);
const textHeight = textLines.length * lineHeight;
if (yPos + titleHeight + textHeight > doc.internal.pageSize.getHeight() - margin) {
doc.addPage();
yPos = margin;
}
doc.setFontSize(isTitle ? 18 : 14);
doc.setFont(undefined, 'bold');
doc.text(title, margin, yPos);
yPos += isTitle ? lineHeight * 1.5 : lineHeight;
if (!isTitle) {
doc.setFontSize(10);
doc.setFont(undefined, 'normal');
doc.text(textLines, margin, yPos);
yPos += textHeight + lineHeight * 0.5;
} else {
yPos += 5;
}
}
// --- Document Title & Header ---
doc.setFontSize(22);
doc.setFont(undefined, 'bold');
doc.text("FASHION TECHNICAL SHEET", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' });
yPos += lineHeight * 2;
doc.setFontSize(12);
doc.setFont(undefined, 'normal');
doc.text(`Design Name: ${name}`, margin, yPos);
doc.text(`Collection: ${collection}`, margin + 250, yPos);
yPos += lineHeight * 2;
// --- 1. Design Concept ---
addBlock("1. Design Concept Summary", desc);
// --- 2. Sketch Area Placeholder ---
addBlock("2. Technical Sketch & Annotation Area (Print & Sketch)", "This area is reserved for hand-drawn front and back technical flats. Use this space for detailed annotations and callouts.", false);
doc.rect(margin, yPos, usableWidth, 200); // Drawing a box placeholder
yPos += 220;
// --- 3. Materials Table ---
if (yPos > doc.internal.pageSize.getHeight() - 100) {
doc.addPage();
yPos = margin;
}
addBlock("3. Bill of Materials (BOM)", "The following list details all required fabrics, trims, and notions for construction.", false);
const tableHead = [["#", "Item Name", "Category", "Supplier / Color / Details"]];
const tableBody = materials.map((m, index) => [
index + 1,
m.item,
m.category,
m.supplier
]);
doc.autoTable({
startY: yPos,
head: tableHead,
body: tableBody,
theme: 'striped',
headStyles: { fillColor: [233, 30, 99], textColor: [255, 255, 255], fontSize: 9 },
styles: { fontSize: 8, cellPadding: 4, overflow: 'linebreak' },
columnStyles: {
0: { cellWidth: 20, halign: 'center' },
1: { cellWidth: 150 },
2: { cellWidth: 80 },
3: { cellWidth: 'auto' }
},
margin: { left: margin, right: margin }
});
yPos = doc.autoTable.previous.finalY + 20;
// --- 4. Construction Notes ---
addBlock("4. Construction Notes & Special Finishes", constructionNotes);
doc.save(`${name.replace(/ /g,"_")}_Sketch_Sheet.pdf`);
}
/**
* Helper to escape HTML
*/
function escapeHTML(str) {
if (!str) return "";
return str
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
// --- 4. INITIALIZATION & EVENT LISTENERS ---
// Tab Listeners
tabButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
navButtons.forEach((btn) => {
btn.addEventListener("click", () => showTab(btn.dataset.target));
});
// Config Tab Listeners
if (addMaterialForm) {
addMaterialForm.addEventListener("submit", handleAddMaterial);
}
if (configMaterialList) {
configMaterialList.addEventListener("click", handleRemoveMaterial);
}
if (generateBtn) {
generateBtn.addEventListener("click", () => renderDashboard(false));
}
// PDF Button
if (pdfBtn) {
pdfBtn.addEventListener("click", downloadPDF);
}
// Dashboard Listeners
if (dashboardOutput) {
dashboardOutput.addEventListener('input', handleDashboardUpdate);
dashboardOutput.addEventListener('change', handleDashboardUpdate);
}
// Initial config list display
updateConfigListDisplay();
// Initial State: Generate dashboard with samples
renderDashboard();
showTab("fssg-tab-dashboard");
});