Files
Abschluss-Projekt/js/dashboard.js
2025-11-28 09:35:02 +01:00

172 lines
6.6 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'} &nbsp&nbsp&nbsp ${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>';
});
});