Cooking Steps Organizer

Cooking Steps Organizer

Cooking Steps Organizer

min

Recipe Steps:

${escapeHtml(step.text)}

${step.time ? `

Est. ${step.time} min

` : ''}
`; stepsList.appendChild(li); }); } /** * Adds a new step to the list. (Global for form submit) */ window.csoAddStep = () => { const text = stepTextInput.value.trim(); const time = stepTimeInput.value ? parseInt(stepTimeInput.value) : null; if (!text) { showMessage("Step description cannot be empty.", true); return; } if (time !== null && (isNaN(time) || time <= 0)) { showMessage("Please enter a valid positive number for time.", true); return; } steps.push({ id: 's' + Date.now() + Math.random().toString(16).slice(2), text: text, time: time, completed: false }); stepTextInput.value = ''; stepTimeInput.value = ''; showMessage(null); // Clear errors renderSteps(); }; /** * Toggles the completion status of a step. (Global for button click) */ window.csoToggleComplete = (id) => { const step = steps.find(s => s.id === id); if (step) { step.completed = !step.completed; renderSteps(); } }; /** * Deletes a step from the list. (Global for button click) */ window.csoDeleteStep = (id) => { if (!confirm('Are you sure you want to delete this step?')) return; steps = steps.filter(s => s.id !== id); renderSteps(); }; /** * Moves a step up or down in the list. (Global for button click) */ window.csoMoveStep = (id, direction) => { const index = steps.findIndex(s => s.id === id); if (index === -1) return; const newIndex = index + direction; if (newIndex < 0 || newIndex >= steps.length) return; // Cannot move outside bounds // Simple swap [steps[index], steps[newIndex]] = [steps[newIndex], steps[index]]; renderSteps(); }; /** * Clears all steps from the list. */ function clearAllSteps() { if (steps.length === 0) { showMessage("The list is already empty.", false); return; } if (!confirm('Are you sure you want to clear all steps?')) return; steps = []; renderSteps(); showMessage(null); } /** * Downloads the steps list as a PDF. */ function downloadPDF() { if (!jsPDF || !jsPDF.autoTable) { showMessage("PDF library not loaded.", true); return; } if (steps.length === 0) { showMessage("Add some steps before downloading.", true); return; } showLoader(true); showMessage(null); try { const doc = new jsPDF(); const pageMargin = 15; const pageWidth = doc.internal.pageSize.getWidth(); let y = 20; // 1. Title doc.setFontSize(20); doc.text("Cooking Steps", pageWidth / 2, y, { align: 'center' }); y += 20; // 2. Steps Table const tableHead = [["#", "Status", "Step Description", "Est. Time (min)"]]; const tableBody = steps.map((step, index) => [ index + 1, step.completed ? 'Done' : 'To Do', step.text, step.time ? step.time.toString() : '-' ]); doc.autoTable({ startY: y, head: tableHead, body: tableBody, theme: 'striped', headStyles: { fillColor: [0, 95, 204] }, // Primary color columnStyles: { 0: { cellWidth: 15 }, // Step number 1: { cellWidth: 25 }, // Status 3: { cellWidth: 35, halign: 'right' } // Time }, didParseCell: function (data) { // Style completed rows if (data.row.section === 'body' && data.row.raw[1] === 'Done') { data.cell.styles.fillColor = '#e8f5e9'; // Light green data.cell.styles.textColor = '#666'; data.cell.styles.fontStyle = 'italic'; } } }); doc.save("Cooking_Steps.pdf"); } catch (e) { console.error("Error generating PDF:", e); showMessage("An error occurred while generating the PDF.", true); } finally { showLoader(false); } } /** Basic HTML escaping */ function escapeHtml(unsafe) { if (!unsafe) return ''; return unsafe .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } /** Shows or hides loader */ function showLoader(show) { loaderOverlay.style.display = show ? 'flex' : 'none'; } /** Displays messages */ function showMessage(message, isError = false) { if (!message) { messageArea.style.display = 'none'; return; } messageArea.textContent = message; messageArea.className = `mt-4 text-center font-medium ${isError ? 'text-red-600' : 'text-blue-600'}`; messageArea.style.display = 'block'; setTimeout(() => { if(messageArea.textContent === message) { messageArea.style.display = 'none'; } }, 4000); } // === Event Listeners === clearBtn.addEventListener('click', clearAllSteps); downloadBtn.addEventListener('click', downloadPDF); // === Initial Render === renderSteps(); });