function renderProjects(projects) { const projectsList = document.getElementById('projects-list'); projectsList.innerHTML = ''; if (projects.length === 0) { projectsList.innerHTML = '
No projects found
'; return; } for (const project of projects) { const labelCounts = project.labelCounts || {}; const card = document.createElement('div'); card.className = 'card'; card.style.background = '#f5f5f5'; card.style.borderRadius = '12px'; card.style.overflow = 'hidden'; card.style.boxShadow = '0 2px 8px rgba(0,0,0,0)'; card.style.display = 'flex'; card.style.background = 'white'; card.style.cursor = 'pointer'; card.tabIndex = 0; card.setAttribute('role', 'button'); card.setAttribute('aria-label', `Open project ${project.title || project.id}`); card.style.position = 'relative'; // For absolute positioning of delete button card.addEventListener('click', (e) => { // Prevent click if delete button is pressed if (e.target.classList.contains('delete-btn')) return; if (project.hasTraining) { window.location.href = `/overview-training.html?id=${project.id}`; } else if (project.hasDetails) { // Find details for this project const detailsEntry = window._trainingProjectDetails?.find(d => d.project_id == project.id); if (detailsEntry && Array.isArray(detailsEntry.class_map) && detailsEntry.class_map.length > 0) { // If classes are assigned, skip to start-training.html window.location.href = `/edit-training.html?id=${project.id}`; } else { window.location.href = `/setup-training-project.html?id=${project.id}`; } } else { window.location.href = `/project-details.html?id=${project.id}`; } }); // Image let imageHTML = ''; if (project.project_image) { imageHTML = `img`; } const imgContainer = document.createElement('div'); imgContainer.className = 'img-container'; imgContainer.style.background = '#009eac2d' imgContainer.style.flex = '0 0 120px'; imgContainer.style.display = 'flex'; imgContainer.style.alignItems = 'center'; imgContainer.style.justifyContent = 'center'; imgContainer.innerHTML = imageHTML; // Info const infoDiv = document.createElement('div'); infoDiv.className = 'info'; infoDiv.style.background = '#009eac2d' infoDiv.style.flex = '1'; infoDiv.style.padding = '16px'; infoDiv.innerHTML = `

${project.id ?? 'N/A'}     ${project.title || 'Untitled'}

${getClassesAsParagraphs(project, labelCounts)}
`; // Delete button const deleteBtn = document.createElement('button'); deleteBtn.textContent = 'Delete'; deleteBtn.style.width = '70px'; deleteBtn.style.height = '28px'; deleteBtn.className = 'button-red delete-btn'; deleteBtn.style.position = 'absolute'; deleteBtn.style.bottom = '0px'; deleteBtn.style.right = '15px'; deleteBtn.style.zIndex = '2'; deleteBtn.style.fontSize = '14px'; deleteBtn.style.padding = '0'; deleteBtn.style.borderRadius = '6px'; deleteBtn.style.boxShadow = '0 2px 8px rgba(0,0,0,0.08)'; deleteBtn.style.display = 'flex'; deleteBtn.style.alignItems = 'center'; deleteBtn.style.justifyContent = 'center'; deleteBtn.addEventListener('click', function(e) { e.stopPropagation(); if (confirm('Are you sure you want to delete this training project?')) { fetch(`/api/training-projects/${project.id}`, { method: 'DELETE' }) .then(res => { if (res.ok) { card.remove(); } else { alert('Failed to delete project.'); } }) .catch(() => alert('Failed to delete project.')); } }); card.appendChild(imgContainer); card.appendChild(infoDiv); card.appendChild(deleteBtn); projectsList.appendChild(card); } } // Helper to render classes as

elements function getClassesAsParagraphs(project, labelCounts) { let classes = []; let labelConfig = project.parsed_label_config; if (typeof labelConfig === 'string') { try { labelConfig = JSON.parse(labelConfig); } catch { labelConfig = null; } } if (labelConfig) { Object.values(labelConfig).forEach(cfg => { if (cfg.labels && Array.isArray(cfg.labels)) { cfg.labels.forEach(label => { classes.push(label); }); } }); } if (classes.length === 0 && project.prompts && project.prompts.length > 0) { const prompt = project.prompts[0]; if (prompt.output_classes && prompt.output_classes.length > 0) { classes = prompt.output_classes; } } if (classes.length === 0 && Object.keys(labelCounts).length > 0) { classes = Object.keys(labelCounts); } return classes.map(cls => `

${cls}${labelCounts && labelCounts[cls] !== undefined ? ' ' : ''}

`).join(''); } // Fetch and render TrainingProjects from the backend window.addEventListener('DOMContentLoaded', () => { Promise.all([ fetch('/api/training-projects').then(res => res.json()), fetch('/api/training-project-details').then(res => res.json()), fetch('/api/trainings').then(res => res.json()) ]).then(([projects, details, trainings]) => { window._trainingProjectDetails = details; // Store globally for click handler // Build a set of project IDs that have details const detailsProjectIds = new Set(details.map(d => d.project_id)); // Build a set of project IDs that have trainings const detailsIdToProjectId = {}; details.forEach(d => { detailsIdToProjectId[d.id] = d.project_id; }); const trainingProjectIds = new Set(trainings.map(t => detailsIdToProjectId[t.project_details_id])); // Map project_id to id for frontend compatibility projects.forEach(project => { if (project.project_id !== undefined) project.id = project.project_id; if (Array.isArray(project.classes)) { project.labelCounts = {}; project.classes.forEach(cls => project.labelCounts[cls] = 0); } // Attach a flag for details existence project.hasDetails = detailsProjectIds.has(project.id); // Attach a flag for training existence project.hasTraining = trainingProjectIds.has(project.id); }); renderProjects(projects); }).catch(err => { document.getElementById('projects-list').innerHTML = '
Error loading projects
'; }); });