Astronaut Career Guide

Astronaut Career Guide

My Career Dashboard

Track your progress against common astronaut requirements. All data here is customizable for your personal use.

My Profile

My Requirements Checklist

This checklist is generated from the data in the "Data Configuration" tab.

Career Pathways

Astronaut roles have evolved. Modern space agencies primarily recruit for these two categories.

Core Requirements (NASA Example)

While specific requirements vary by agency (e.g., NASA, ESA) and mission, these are the typical standards.

Data Configuration

Edit the content displayed on the other tabs. All changes are saved automatically as you type. (Spec 4)

Edit Career Pathways

Edit Core Requirements

Edit Dashboard Checklist Items

${escapeHTML(path.desc)}

`).join(''); // 2. Render Requirements (Tab 3) acgRequirementsContent.innerHTML = acgData.requirements.map(req => `

${escapeHTML(req.title)}

    ${req.desc.split('\n').map(item => `
  • ${escapeHTML(item.replace(/^\*/, ''))}
  • `).join('')}
`).join(''); // 3. Render Dashboard Checklist (Tab 1) acgDashboardChecklist.innerHTML = acgData.checklist.map(item => `
  • ${escapeHTML(item)}
  • `).join(''); // 4. Populate Config Tab (Tab 4) // Pathways if (acgData.pathways[0]) { acgCfgPath1Title.value = acgData.pathways[0].title; acgCfgPath1Desc.value = acgData.pathways[0].desc; } if (acgData.pathways[1]) { acgCfgPath2Title.value = acgData.pathways[1].title; acgCfgPath2Desc.value = acgData.pathways[1].desc; } // Requirements if (acgData.requirements[0]) { acgCfgReq1Title.value = acgData.requirements[0].title; acgCfgReq1Desc.value = acgData.requirements[0].desc; } if (acgData.requirements[1]) { acgCfgReq2Title.value = acgData.requirements[1].title; acgCfgReq2Desc.value = acgData.requirements[1].desc; } if (acgData.requirements[2]) { acgCfgReq3Title.value = acgData.requirements[2].title; acgCfgReq3Desc.value = acgData.requirements[2].desc; } // 5. Populate Config Checklist (Tab 4) acgCfgChecklistList.innerHTML = acgData.checklist.map((item, index) => `
    ${escapeHTML(item)}
    `).join(''); // Re-attach listeners for dynamically created 'Remove' buttons acgContainer.querySelectorAll('.acg-cfg-checklist-remove').forEach(button => { button.addEventListener('click', (e) => { const indexToRemove = parseInt(e.target.dataset.index, 10); acgData.checklist.splice(indexToRemove, 1); renderAcgContent(); // Re-render all content }); }); } /** * Sets up event listeners for the Data Configuration tab (Spec 4, 6) */ function setupConfigListeners() { // Helper function to update data and re-render const createInputListener = (element, dataPath, key) => { if (element) { element.addEventListener('input', () => { dataPath[key] = element.value; renderAcgContent(); // Re-render tabs with new data }); } }; // Pathways createInputListener(acgCfgPath1Title, acgData.pathways[0], 'title'); createInputListener(acgCfgPath1Desc, acgData.pathways[0], 'desc'); createInputListener(acgCfgPath2Title, acgData.pathways[1], 'title'); createInputListener(acgCfgPath2Desc, acgData.pathways[1], 'desc'); // Requirements createInputListener(acgCfgReq1Title, acgData.requirements[0], 'title'); createInputListener(acgCfgReq1Desc, acgData.requirements[0], 'desc'); createInputListener(acgCfgReq2Title, acgData.requirements[1], 'title'); createInputListener(acgCfgReq2Desc, acgData.requirements[1], 'desc'); createInputListener(acgCfgReq3Title, acgData.requirements[2], 'title'); createInputListener(acgCfgReq3Desc, acgData.requirements[2], 'desc'); // Checklist 'Add' button acgCfgChecklistAdd.addEventListener('click', () => { const newItem = acgCfgChecklistNew.value.trim(); if (newItem) { acgData.checklist.push(newItem); acgCfgChecklistNew.value = ''; // Clear input renderAcgContent(); // Re-render } }); } /** * Handles the PDF Download functionality (Spec C, Spec 9) */ async function downloadAcgDashboard() { // Check if libraries are loaded if (typeof jspdf === 'undefined' || typeof html2canvas === 'undefined') { alert('PDF libraries are still loading. Please try again in a moment.'); return; } const { jsPDF } = jspdf; const dashboardContent = acgContainer.querySelector('#acg-dashboard-content'); const container = acgContainer.querySelector('.acg-container'); // Add a class to the container to apply print-friendly styles (Spec 9) container.classList.add('acg-pdf-view'); acgPdfDownloadBtn.style.display = 'none'; // Hide button itself try { // Use html2canvas to capture the styled element const canvas = await html2canvas(dashboardContent, { scale: 2, // Higher resolution useCORS: true, logging: false, onclone: (doc) => { // This runs on a clone of the document // Convert selects to show the selected value clearly const selects = doc.querySelectorAll('select'); selects.forEach(select => { const selectedOption = select.options[select.selectedIndex]; if (selectedOption) { const staticText = document.createElement('div'); staticText.textContent = selectedOption.text; staticText.style.padding = '12px 0'; // Match input padding staticText.style.fontSize = '16px'; staticText.style.fontWeight = 'normal'; staticText.style.color = '#000'; select.parentNode.replaceChild(staticText, select); } }); // Convert inputs to static text const inputs = doc.querySelectorAll('input[type="text"], input[type="number"]'); inputs.forEach(input => { const staticText = document.createElement('div'); staticText.textContent = input.value || 'N/A'; staticText.style.padding = '12px 0'; staticText.style.fontSize = '16px'; staticText.style.fontWeight = 'normal'; staticText.style.color = '#000'; input.parentNode.replaceChild(staticText, input); }); } }); // Create PDF const imgData = canvas.toDataURL('image/png'); const pdf = new jsPDF('p', 'mm', 'a4'); const pdfWidth = pdf.internal.pageSize.getWidth(); const pdfHeight = (canvas.height * pdfWidth) / canvas.width; const pageMargin = 15; const contentWidth = pdfWidth - (pageMargin * 2); const contentHeight = (canvas.height * contentWidth) / canvas.width; // Add title pdf.setFont("helvetica", "bold"); pdf.setFontSize(18); pdf.setTextColor(acgData.primaryColor); // Use root variable pdf.text("Astronaut Career Dashboard", pageMargin, pageMargin + 5); // Add image pdf.addImage(imgData, 'PNG', pageMargin, pageMargin + 15, contentWidth, contentHeight); pdf.save('Astronaut_Career_Dashboard.pdf'); } catch (error) { console.error("ACG PDF Error:", error); alert("An error occurred while generating the PDF."); } finally { // Clean up: remove the class and show the button again container.classList.remove('acg-pdf-view'); acgPdfDownloadBtn.style.display = 'block'; } } /** * Simple HTML escaper to prevent XSS from user-configured data */ function escapeHTML(str) { if (typeof str !== 'string') return ''; return str.replace(/[&<>"']/g, function(m) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[m]; }); } // --- 4. EVENT BINDING (Spec IV.C) --- // Tab buttons acgTabButtons.forEach((button, index) => { button.addEventListener('click', () => { showAcgTab(index); }); }); // Navigation buttons acgNavNext.addEventListener('click', () => { showAcgTab(currentAcgTabIndex + 1); }); acgNavPrev.addEventListener('click', () => { showAcgTab(currentAcgTabIndex - 1); }); // PDF Download Button acgPdfDownloadBtn.addEventListener('click', downloadAcgDashboard); // --- 5. INITIALIZATION --- renderAcgContent(); // Populate the tool with default data setupConfigListeners(); // Enable editing in Tab 4 showAcgTab(0); // Show the first tab });