172 lines
6.8 KiB
JavaScript
172 lines
6.8 KiB
JavaScript
function renderProjects(projects) {
|
|
const projectsList = document.getElementById('projects-list');
|
|
projectsList.innerHTML = '';
|
|
|
|
if (projects.length === 0) {
|
|
projectsList.innerHTML = '<div>No projects found</div>';
|
|
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 src="${project.project_image}" alt="img" style="width:120px;height:120px;object-fit:cover;display:block;" />`;
|
|
}
|
|
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 = `
|
|
<h3 style="margin:0 0 8px 0;font-size:1.5em;font-weight:bold;">${project.id ?? 'N/A'}     ${project.title || 'Untitled'}</h3>
|
|
<div class="label-classes" style="font-size:1em;">
|
|
${getClassesAsParagraphs(project, labelCounts)}
|
|
</div>
|
|
`;
|
|
|
|
// 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 <p> 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 => `<p>${cls}${labelCounts && labelCounts[cls] !== undefined ? ' ' : ''}</p>`).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 = '<div>Error loading projects</div>';
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|