feat: Add story points support and AI daily briefing feature

- Enhanced JIRA integration to fetch story points from multiple custom fields.
- Updated task display to show story points and their estimated duration.
- Introduced AI daily briefing banner with dynamic content loading.
- Implemented subtasks functionality with add, toggle, and delete capabilities.
- Improved task management UI with better accessibility and interaction.
This commit is contained in:
2026-09-10 22:38:06 +02:00
parent c0e0dd5947
commit 5ce08443cc
8 changed files with 780 additions and 69 deletions
+31
View File
@@ -494,10 +494,15 @@ $boardsJson = json_encode($boards, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT
<div class="flex items-center space-x-2">
<span id="planTicketKeyBadge" class="font-mono text-xs font-bold text-indigo-600 dark:text-indigo-400">FOC-101</span>
<span id="planTicketProject" class="text-[11px] text-slate-400">• Fociz Platform</span>
<span id="planSpBadge" class="hidden ml-auto text-[10px] font-bold px-2 py-0.5 rounded-full bg-violet-50 dark:bg-violet-500/10 text-violet-700 dark:text-violet-300 border border-violet-200 dark:border-violet-500/20"></span>
</div>
<div id="planTicketSummaryText" class="text-xs font-semibold text-slate-800 dark:text-slate-200 mt-1 line-clamp-2">
Tweestapsverificatie implementeren
</div>
<div id="planSpHint" class="hidden mt-1.5 text-[11px] text-violet-600 dark:text-violet-400 flex items-center space-x-1">
<i data-lucide="zap" class="w-3 h-3"></i>
<span id="planSpHintText"></span>
</div>
</div>
<!-- Datum Kiezer -->
@@ -1408,6 +1413,31 @@ function openPlanModal(item) {
document.getElementById('planTicketProject').innerText = '• ' + (ticket.project || 'Project');
document.getElementById('planTicketSummaryText').innerText = ticket.summary;
// Story Points → Duur conversie (1 SP = 30 minuten, configurabel)
const spBadge = document.getElementById('planSpBadge');
const spHint = document.getElementById('planSpHint');
const spHintText = document.getElementById('planSpHintText');
const sp = parseFloat(ticket.story_points);
if (!isNaN(sp) && sp > 0) {
const spMinutes = Math.round(sp * 30); // 1 SP = 30 minuten
const closestDuration = [30, 45, 60, 90, 120].reduce((prev, curr) =>
Math.abs(curr - spMinutes) < Math.abs(prev - spMinutes) ? curr : prev
);
const durationSelect = document.getElementById('planDurationInput');
if (durationSelect) {
const opt = durationSelect.querySelector(`option[value="${closestDuration}"]`);
if (opt) opt.selected = true;
}
spBadge.textContent = `${sp} SP`;
spBadge.classList.remove('hidden');
spHintText.textContent = `${sp} story ${sp === 1 ? 'point' : 'points'} ≈ ${spMinutes} min → automatisch ${closestDuration} min ingesteld`;
spHint.classList.remove('hidden');
spHint.style.display = 'flex';
} else {
spBadge.classList.add('hidden');
spHint.classList.add('hidden');
}
// Suggesteer prioriteit op basis van Jira prio
let focizPrio = 'P2';
const jp = (ticket.priority || '').toLowerCase();
@@ -1422,6 +1452,7 @@ function openPlanModal(item) {
const modal = document.getElementById('planTaskModal');
modal.classList.remove('hidden');
modal.classList.add('flex');
if (typeof lucide !== 'undefined') lucide.createIcons();
}
function closePlanModal() {