No team members added yet.
';
goalMembersChecklist.innerHTML = '
Add members first.
';
return;
}
teamMembers.forEach(member => {
// Update config list
const memberDiv = document.createElement('div');
memberDiv.className = 'flex justify-between items-center p-2 bg-slate-100 rounded';
memberDiv.innerHTML = `
${escapeHTML(member.name)}`;
memberListDiv.appendChild(memberDiv);
// Update checklist in goal form
const checklistItem = document.createElement('div');
checklistItem.className = 'flex items-center';
checklistItem.innerHTML = `
`;
goalMembersChecklist.appendChild(checklistItem);
});
}
function updateGoalsDashboardUI() {
goalsDashboard.innerHTML = '';
if (goals.length === 0) {
loadingGoals.classList.remove('hidden');
loadingGoals.textContent = "No goals have been set yet. Go to Data Configuration to add one.";
return;
}
loadingGoals.classList.add('hidden');
goals.forEach(goal => {
const assignedMembers = teamMembers.filter(m => goal.assignedTo.includes(m.id));
const memberNames = assignedMembers.map(m => escapeHTML(m.name)).join(', ') || 'Unassigned';
const progressColor = goal.progress < 30 ? 'bg-red-500' : goal.progress < 70 ? 'bg-yellow-500' : 'bg-green-500';
const goalCard = document.createElement('div');
goalCard.className = 'bg-white border border-slate-200 rounded-lg p-4 flex flex-col';
goalCard.innerHTML = `
${escapeHTML(goal.title)}
${escapeHTML(goal.description)}
Assigned to: ${memberNames}
${goal.progress}% Complete
`;
goalsDashboard.appendChild(goalCard);
});
}
function updateGoalsConfigUI() {
goalListConfig.innerHTML = '';
if (goals.length === 0) {
goalListConfig.innerHTML = '
No goals to configure.
';
return;
}
goals.forEach(goal => {
const div = document.createElement('div');
div.className = 'flex justify-between items-center p-2 bg-slate-100 rounded';
div.innerHTML = `
${escapeHTML(goal.title)}
`;
goalListConfig.appendChild(div);
});
}
// --- Data Handling ---
async function handleAddMember(e) {
e.preventDefault();
const name = memberNameInput.value.trim();
if (name) {
await addDoc(collection(db, `artifacts/${appId}/public/data/team_members`), { name });
memberNameInput.value = '';
}
}
async function handleRemoveMember(memberId) {
await deleteDoc(doc(db, `artifacts/${appId}/public/data/team_members`, memberId));
}
async function handleAddGoal(e) {
e.preventDefault();
const title = goalTitleInput.value.trim();
const description = goalDescriptionInput.value.trim();
const progress = parseInt(goalProgressInput.value, 10);
const assignedTo = Array.from(goalMembersChecklist.querySelectorAll('input:checked')).map(el => el.value);
if (title && description) {
const goalData = { title, description, progress, assignedTo };
await addDoc(collection(db, `artifacts/${appId}/public/data/goals`), goalData);
addGoalForm.reset();
goalProgressValue.textContent = '0';
}
}
async function handleRemoveGoal(goalId) {
await deleteDoc(doc(db, `artifacts/${appId}/public/data/goals`, goalId));
}
// --- PDF Generation ---
async function generatePdf() {
const { jsPDF } = window.jspdf;
if (goals.length === 0) {
alert("No goals to export.");
return;
}
downloadPdfBtn.textContent = 'Generating PDF...';
downloadPdfBtn.disabled = true;
try {
const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });
// Header
pdf.setFontSize(22);
pdf.setFont("helvetica", "bold");
pdf.setTextColor(40, 52, 71);
pdf.text("Shared Goals & Achievement Report", pdf.internal.pageSize.getWidth() / 2, 40, { align: 'center' });
// Table Data
const head = [['Goal Title', 'Description', 'Progress (%)', 'Assigned To']];
const body = goals.map(goal => {
const assignedNames = teamMembers.filter(m => goal.assignedTo.includes(m.id)).map(m => m.name).join(', ');
return [goal.title, goal.description, goal.progress, assignedNames];
});
pdf.autoTable({
head: head,
body: body,
startY: 60,
theme: 'grid',
headStyles: { fillColor: [59, 130, 246] }, // Blue header
styles: { font: 'Inter', cellPadding: 8 },
});
// Footer
const pageCount = pdf.internal.getNumberOfPages();
for (let i = 1; i <= pageCount; i++) {
pdf.setPage(i);
pdf.setFontSize(8);
pdf.setTextColor(150);
pdf.text(`Page ${i} of ${pageCount}`, pdf.internal.pageSize.getWidth() / 2, pdf.internal.pageSize.getHeight() - 20, { align: 'center' });
pdf.text(`Generated on: ${new Date().toLocaleString()}`, 40, pdf.internal.pageSize.getHeight() - 20);
}
pdf.save('Shared_Goals_Report.pdf');
} catch (error) {
console.error("Error generating PDF:", error);
alert("Could not generate PDF. Please try again.");
} finally {
downloadPdfBtn.textContent = 'Download Report as PDF';
downloadPdfBtn.disabled = false;
}
}
function escapeHTML(str) {
return str.replace(/[&<>"']/g, m => ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[m]);
}
// --- Initial Setup ---
document.addEventListener('DOMContentLoaded', () => {
onAuthStateChanged(auth, (user) => {
if (user) {
setupAppForUser(user);
} else {
(async () => {
try {
if (initialAuthToken) {
await signInWithCustomToken(auth, initialAuthToken);
} else {
await signInAnonymously(auth);
}
} catch (error) {
console.error("Authentication failed:", error);
setupAppForUser(null);
}
})();
}
});
// Event Listeners
prevBtn.addEventListener('click', () => { if (currentTab > 0) changeTab(currentTab - 1); });
nextBtn.addEventListener('click', () => { if (currentTab < tabs.length - 1) changeTab(currentTab + 1); });
downloadPdfBtn.addEventListener('click', generatePdf);
addMemberForm.addEventListener('submit', handleAddMember);
addGoalForm.addEventListener('submit', handleAddGoal);
goalProgressInput.addEventListener('input', (e) => {
goalProgressValue.textContent = e.target.value;
});
// Event delegation for remove buttons
memberListDiv.addEventListener('click', (e) => {
if (e.target.classList.contains('remove-member-btn')) {
handleRemoveMember(e.target.dataset.id);
}
});
goalListConfig.addEventListener('click', (e) => {
if (e.target.classList.contains('remove-goal-btn')) {
handleRemoveGoal(e.target.dataset.id);
}
// Note: Edit functionality would require a modal or form pre-filling,
// which adds complexity beyond the initial scope.
});
// Initial state
changeTab(0);
});