`;
goalsContainer.innerHTML += goalHTML;
});
};
const renderDashboard = () => {
const countByCategory = {};
categories.forEach(c => countByCategory[c] = 0);
feedback.forEach(item => {
if (countByCategory[item.category] !== undefined) countByCategory[item.category]++;
});
const chartCanvas = document.getElementById('category-chart');
if (categoryChart) categoryChart.destroy();
categoryChart = new Chart(chartCanvas, {
type: 'bar',
data: {
labels: Object.keys(countByCategory),
datasets: [{ label: 'Feedback Count', data: Object.values(countByCategory), backgroundColor: '#4f46e5' }]
},
options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y' }
});
renderGoals();
};
// --- MODAL & DATA HANDLING ---
const openModal = (id = null) => {
if (id) {
const item = feedback.find(f => f.id === id);
modalTitle.textContent = 'Edit Feedback';
modalFeedbackId.value = id;
modalFeedbackInput.value = item.text;
modalCategorySelect.value = item.category;
modalDateInput.value = item.date;
modalDeleteBtn.classList.remove('hidden');
} else {
modalTitle.textContent = 'Log New Feedback';
modalFeedbackId.value = '';
modalFeedbackInput.value = '';
modalCategorySelect.selectedIndex = 0;
modalDateInput.valueAsDate = new Date();
modalDeleteBtn.classList.add('hidden');
}
modal.style.display = 'block';
};
const closeModal = () => modal.style.display = 'none';
const saveFeedback = () => {
const id = modalFeedbackId.value;
const data = {
text: modalFeedbackInput.value.trim(),
category: modalCategorySelect.value,
date: modalDateInput.value
};
if (!data.text || !data.date) { alert("Feedback and date are required."); return; }
if (id) {
const index = feedback.findIndex(f => f.id == id);
feedback[index] = { ...feedback[index], ...data };
} else {
feedback.push({ id: Date.now(), ...data });
}
saveData();
renderFeedbackLog();
closeModal();
};
const deleteFeedback = () => {
if (confirm("Delete this feedback entry?")) {
feedback = feedback.filter(f => f.id != modalFeedbackId.value);
saveData();
renderFeedbackLog();
closeModal();
}
};
const addCategory = () => {
const name = newCategoryNameInput.value.trim();
if (name && !categories.includes(name)) {
categories.push(name);
saveData();
renderCategories();
newCategoryNameInput.value = '';
}
};
const deleteCategory = (name) => {
if (confirm(`Delete category "${name}"?`)) {
categories = categories.filter(c => c !== name);
saveData();
renderCategories();
}
};
// --- AI ANALYSIS ---
const analyzeThemes = async () => {
if (feedback.length < 3) {
alert("Please log at least 3 pieces of feedback for a meaningful analysis.");
return;
}
const btnText = document.getElementById('ai-btn-text');
const btnLoader = document.getElementById('ai-btn-loader');
btnText.classList.add('hidden');
btnLoader.classList.remove('hidden');
aiAnalysisBtn.disabled = true;
const feedbackText = feedback.map(f => `- ${f.text} (Category: ${f.category})`).join('\n');
const prompt = `Based on the following feedback items, identify 3-5 key themes or areas for improvement. For each theme, suggest one specific, actionable SMART goal. Present the output as a list of themes, each with a suggested goal.\n\nFeedback:\n${feedbackText}`;
try {
let chatHistory = [{ role: "user", parts: [{ text: prompt }] }];
const payload = { contents: chatHistory };
const apiKey = "";
const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${apiKey}`;
const response = await fetch(apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!response.ok) throw new Error(`API Error: ${response.statusText}`);
const result = await response.json();
const themesText = result.candidates[0].content.parts[0].text;
const themesContainer = document.getElementById('ai-themes');
themesContainer.innerHTML = themesText.split('\n').map(line => {
if (line.match(/^\d+\./) || line.toLowerCase().includes('theme:')) {
return ``;
}
return `
${line}
`; } if (line.toLowerCase().includes('goal:')) { const goalText = line.replace(/goal:/i, '').trim(); return `${line}
${line}
`; }).join(''); } catch (error) { console.error("AI Analysis Error:", error); document.getElementById('ai-themes').textContent = "Sorry, an error occurred during analysis."; } finally { btnText.classList.remove('hidden'); btnLoader.classList.add('hidden'); aiAnalysisBtn.disabled = false; } }; // --- GOAL MANAGEMENT --- const addGoal = (text) => { goals.push({ id: Date.now(), text, completed: false }); saveData(); renderGoals(); }; const toggleGoal = (id) => { const goal = goals.find(g => g.id == id); if (goal) goal.completed = !goal.completed; saveData(); renderGoals(); }; const removeGoal = (id) => { goals = goals.filter(g => g.id != id); saveData(); renderGoals(); }; // --- TAB MANAGEMENT & PDF --- window.changeTab = (tabId) => { currentTab = tabId; ['tab1', 'tab2', 'tab3'].forEach(id => { document.getElementById(id).classList.toggle('hidden', id !== tabId); document.getElementById(`${id}-button`).classList.toggle('active', id === tabId); }); if (tabId === 'tab2') renderDashboard(); updateButtonVisibility(); }; const updateButtonVisibility = () => { const prevBtn = document.getElementById('prev-button'); const nextBtn = document.getElementById('next-button'); const downloadBtn = document.getElementById('download-pdf-button'); prevBtn.classList.toggle('hidden', currentTab === 'tab1'); nextBtn.classList.toggle('hidden', currentTab === 'tab3'); downloadBtn.classList.toggle('hidden', currentTab !== 'tab2'); }; window.handleNext = () => changeTab(['tab1', 'tab2', 'tab3'][['tab1', 'tab2', 'tab3'].indexOf(currentTab) + 1]); window.handlePrev = () => changeTab(['tab1', 'tab2', 'tab3'][['tab1', 'tab2', 'tab3'].indexOf(currentTab) - 1]); window.downloadPDF = async () => { try { const canvas = await html2canvas(document.getElementById('insights-content'), { scale: 2 }); const pdf = new jspdf.jsPDF({ orientation: 'portrait', unit: 'px', format: 'a4' }); const newWidth = pdf.internal.pageSize.getWidth() - 80; const newHeight = (canvas.height * newWidth) / canvas.width; pdf.addImage(canvas.toDataURL('image/png'), 'PNG', 40, 40, newWidth, newHeight); pdf.save('self-improvement-insights.pdf'); } catch (e) { alert("Error generating PDF."); } }; // --- EVENT LISTENERS --- document.getElementById('add-feedback-btn').addEventListener('click', () => openModal()); feedbackLogContainer.addEventListener('click', (e) => { if (e.target.tagName === 'BUTTON' && e.target.dataset.id) openModal(parseInt(e.target.dataset.id)); }); modalSaveBtn.addEventListener('click', saveFeedback); modalCancelBtn.addEventListener('click', closeModal); modalDeleteBtn.addEventListener('click', deleteFeedback); addCategoryBtn.addEventListener('click', addCategory); categoryList.addEventListener('click', (e) => { if (e.target.tagName === 'BUTTON' && e.target.dataset.name) deleteCategory(e.target.dataset.name); }); aiAnalysisBtn.addEventListener('click', analyzeThemes); document.getElementById('insights-content').addEventListener('click', (e) => { if (e.target.classList.contains('add-goal-btn')) addGoal(e.target.dataset.goal); if (e.target.tagName === 'INPUT' && e.target.type === 'checkbox') toggleGoal(e.target.dataset.id); if (e.target.tagName === 'BUTTON' && !e.target.classList.contains('add-goal-btn')) removeGoal(e.target.dataset.id); }); // --- INITIALIZATION --- loadData(); renderFeedbackLog(); renderCategories(); updateButtonVisibility(); });