- Dashboard with all Gitea projects (active/archived) - Kanban board per project (Backlog → À faire → En cours → Révision → Terminé) - Drag & drop issues between columns (SortableJS) - Project notes panel - Gitea API integration (read issues, repos) - SQLite local state for board positions - Dark theme design system - Docker-ready (Dockerfile + docker-compose.yml) - FastAPI + Jinja2 + HTMX + Alpine.js stack
60 lines
2.0 KiB
JavaScript
60 lines
2.0 KiB
JavaScript
/**
|
|
* Kanbante — Kanban board drag & drop
|
|
*/
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
const columns = document.querySelectorAll('.column-cards');
|
|
|
|
columns.forEach(col => {
|
|
new Sortable(col, {
|
|
group: 'kanban',
|
|
animation: 200,
|
|
ghostClass: 'dragging',
|
|
dragClass: 'dragging',
|
|
onEnd: function(evt) {
|
|
const card = evt.item;
|
|
const toColumn = evt.to.dataset.column;
|
|
const issueNumber = card.dataset.issue;
|
|
const repoFullName = card.dataset.repo;
|
|
|
|
// Update column count
|
|
updateCounts();
|
|
|
|
// Save to backend
|
|
const formData = new FormData();
|
|
formData.append('repo_full_name', repoFullName);
|
|
formData.append('issue_number', issueNumber);
|
|
formData.append('to_column', toColumn);
|
|
|
|
fetch('/api/move', {
|
|
method: 'POST',
|
|
body: formData,
|
|
}).then(r => r.json()).then(data => {
|
|
if (data.ok) {
|
|
showToast('✅ Déplacé vers ' + toColumn.replace('_', ' '));
|
|
}
|
|
}).catch(err => {
|
|
console.error('Move failed:', err);
|
|
showToast('❌ Erreur de déplacement');
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
function updateCounts() {
|
|
document.querySelectorAll('.kanban-column').forEach(col => {
|
|
const colName = col.querySelector('.column-cards').dataset.column;
|
|
const count = col.querySelectorAll('.kanban-card').length;
|
|
const badge = col.querySelector('.column-count');
|
|
if (badge) badge.textContent = count;
|
|
});
|
|
}
|
|
|
|
function showToast(msg) {
|
|
const toast = document.createElement('div');
|
|
toast.className = 'toast';
|
|
toast.textContent = msg;
|
|
document.body.appendChild(toast);
|
|
setTimeout(() => toast.remove(), 2500);
|
|
}
|
|
});
|