No projects yet. Click 'Add New Project' to get started!
";
return;
}
projects.forEach(p => {
const card = document.createElement("div");
card.className = "pptg-project-card";
card.innerHTML = `
${p.name}
Client: ${p.client || 'N/A'}
Status: ${p.status}
Due: ${p.dueDate || 'N/A'}
Budget: ${p.budget ? '$' + p.budget.toLocaleString() : 'N/A'}
`;
projectList.appendChild(card);
});
}
/**
* Renders the summary stats and report table.
*/
function pptg_renderReport() {
const statsContainer = document.getElementById('pptg-summary-stats');
const tableContainer = document.getElementById('pptg-report-table');
if (!statsContainer || !tableContainer) return; // Guard clause
const totalProjects = projects.length;
const totalBudget = projects.reduce((sum, p) => sum + (p.budget || 0), 0);
const completed = projects.filter(p => p.status === 'Completed').length;
const inProgress = projects.filter(p => p.status === 'In Progress').length;
statsContainer.innerHTML = `
Total Projects
${totalProjects}
Total Earnings
$${totalBudget.toLocaleString()}
`;
let tableHTML = `
| Project Name | Client | Status | Due Date | Budget |
`;
projects.forEach(p => {
tableHTML += `
| ${p.name} |
${p.client || 'N/A'} |
${p.status} |
${p.dueDate || 'N/A'} |
${p.budget ? '$' + p.budget.toLocaleString() : 'N/A'} |
`;
});
tableHTML += ``;
tableContainer.innerHTML = tableHTML;
}
/**
* Handles form submission for adding or updating a project.
* @param {Event} e The form submission event.
*/
function pptg_handleFormSubmit(e) {
e.preventDefault();
const id = document.getElementById("pptg-project-id").value;
const projectData = {
name: document.getElementById("pptg-project-name").value,
client: document.getElementById("pptg-client-name").value,
status: document.getElementById("pptg-status").value,
dueDate: document.getElementById("pptg-due-date").value,
budget: parseFloat(document.getElementById("pptg-budget").value) || 0,
notes: document.getElementById("pptg-notes").value,
};
if (id) { // Update existing
const index = projects.findIndex(p => p.id == id);
if (index > -1) {
projects[index] = { ...projects[index], ...projectData };
}
} else { // Add new
projectData.id = projects.length > 0 ? Math.max(...projects.map(p => p.id)) + 1 : 1;
projects.push(projectData);
}
pptg_renderDashboard();
pptg_renderReport();
projectForm.reset();
formTitle.textContent = "Add New Project";
cancelBtn.style.display = "none";
document.querySelector('.pptg-tab-link[onclick*="pptg-tab-1"]').click(); // Go back to dashboard
}
/**
* Handles clicks on Edit or Delete buttons on project cards.
* @param {Event} e The click event.
*/
function pptg_handleCardActions(e) {
if (!e.target.dataset.id) return;
const id = parseInt(e.target.dataset.id, 10);
const action = e.target.dataset.action;
if (action === "edit") {
const project = projects.find(p => p.id === id);
if (!project) return;
formTitle.textContent = "Edit Project";
document.getElementById("pptg-project-id").value = project.id;
document.getElementById("pptg-project-name").value = project.name;
document.getElementById("pptg-client-name").value = project.client;
document.getElementById("pptg-status").value = project.status;
document.getElementById("pptg-due-date").value = project.dueDate;
document.getElementById("pptg-budget").value = project.budget;
document.getElementById("pptg-notes").value = project.notes;
cancelBtn.style.display = "inline-block";
document.querySelector('.pptg-tab-link[onclick*="pptg-tab-3"]').click(); // Switch to form tab
}
if (action === "delete") {
projects = projects.filter(p => p.id !== id);
pptg_renderDashboard();
pptg_renderReport();
}
}
/**
* Resets the form to its "Add New" state.
*/
function pptg_resetForm() {
projectForm.reset();
formTitle.textContent = "Add New Project";
document.getElementById("pptg-project-id").value = "";
cancelBtn.style.display = "none";
}
/**
* Generates and triggers a download for a PDF of the report. (Spec II.C)
*/
function pptg_downloadPdf() {
if (typeof jspdf === 'undefined' || typeof jspdf.plugin.autotable === 'undefined') {
alert('Error: PDF library is missing. Please refresh.');
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
const head = [['Project Name', 'Client', 'Status', 'Due Date', 'Budget ($)']];
const body = projects.map(p => [p.name, p.client, p.status, p.dueDate, p.budget.toLocaleString()]);
doc.setFontSize(18);
doc.text("Portfolio Project Report", 14, 22);
doc.autoTable({
startY: 30,
head: head,
body: body,
theme: 'striped',
headStyles: { fillColor: '#3498db' }
});
doc.save("Project-Report.pdf");
}
// --- Event Listener Attachment ---
projectForm.addEventListener("submit", pptg_handleFormSubmit);
projectList.addEventListener("click", pptg_handleCardActions);
cancelBtn.addEventListener("click", () => {
pptg_resetForm();
document.querySelector('.pptg-tab-link[onclick*="pptg-tab-1"]').click();
});
addNewBtn.addEventListener("click", () => {
pptg_resetForm();
document.querySelector('.pptg-tab-link[onclick*="pptg-tab-3"]').click();
});
document.getElementById("pptg-download-pdf").addEventListener("click", pptg_downloadPdf);
// Initial Render
pptg_renderDashboard();
pptg_renderReport();
});
// --- Global Functions for inline onclick (Spec IV.C) ---
function pptg_openTab(evt, tabName) {
const container = document.getElementById("pptg-container");
if (!container) return;
const tabcontent = container.getElementsByClassName("pptg-tab-content");
for (let i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
tabcontent[i].classList.remove("pptg-active");
}
const tablinks = container.getElementsByClassName("pptg-tab-link");
for (let i = 0; i < tablinks.length; i++) {
tablinks[i].classList.remove("pptg-active");
}
const activeTab = document.getElementById(tabName);
if(activeTab) activeTab.style.display = "block";
if(activeTab) activeTab.classList.add("pptg-active");
if(evt.currentTarget) evt.currentTarget.classList.add("pptg-active");
}
function pptg_navTab(direction) {
const container = document.getElementById("pptg-container");
if (!container) return;
const tabs = container.querySelectorAll(".pptg-tab-link");
let currentIndex = -1;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].classList.contains("pptg-active")) {
currentIndex = i;
break;
}
}
if (currentIndex === -1) return;
let newIndex = currentIndex + direction;
if (newIndex >= 0 && newIndex < tabs.length) {
tabs[newIndex].click();
}
}