${keyEsc}
@@ -2797,7 +2931,7 @@ function filterPlannerJira() {
class="flex items-center space-x-1 text-[11px] font-semibold text-indigo-600 dark:text-indigo-400 hover:text-indigo-700 dark:hover:text-indigo-300 bg-indigo-50 dark:bg-indigo-500/10 hover:bg-indigo-100 dark:hover:bg-indigo-500/20 border border-indigo-200 dark:border-indigo-500/20 px-2.5 py-1 rounded-lg transition flex-shrink-0 cursor-pointer"
title="Plan in als dagtaak"
>
-
+
Plan
@@ -2809,12 +2943,237 @@ function filterPlannerJira() {
}
}
-async function quickImportJiraByKey(btn, issueKey) {
+// --- Jira Ticket Detail Modal ---
+let _jiraDetailCurrentIssue = null;
+
+function openJiraTicketDetail(issueKey) {
const issue = plannerTicketStore.get(issueKey);
if (!issue) return;
- return quickImportJira(btn, issue.summary || issueKey, issue.key, issue.url || '');
+ _jiraDetailCurrentIssue = issue;
+
+ // Vul header
+ document.getElementById('jiraDetailKey').textContent = issue.key || '—';
+ document.getElementById('jiraDetailSummary').textContent = issue.summary || '—';
+
+ // Status badge
+ const statusEl = document.getElementById('jiraDetailStatusBadge');
+ const status = issue.status || '—';
+ statusEl.textContent = status;
+ statusEl.className = 'text-[10px] px-2 py-0.5 rounded-full font-bold bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400';
+
+ // Priority badge
+ const priorityEl = document.getElementById('jiraDetailPriorityBadge');
+ const prio = issue.priority || '—';
+ priorityEl.textContent = prio;
+ const prioClass = prio === 'Highest' || prio === 'High'
+ ? 'text-[10px] px-2 py-0.5 rounded-full font-bold bg-rose-50 dark:bg-rose-500/10 text-rose-600 dark:text-rose-400'
+ : prio === 'Medium'
+ ? 'text-[10px] px-2 py-0.5 rounded-full font-bold bg-amber-50 dark:bg-amber-500/10 text-amber-600 dark:text-amber-400'
+ : 'text-[10px] px-2 py-0.5 rounded-full font-bold bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-400';
+ priorityEl.className = prioClass;
+
+ // Meta velden
+ document.getElementById('jiraDetailAssignee').textContent = issue.assignee_name || issue.assignee || 'Niet toegewezen';
+ document.getElementById('jiraDetailSp').textContent = issue.story_points ? `${issue.story_points} SP` : '—';
+ document.getElementById('jiraDetailProject').textContent = issue.project_key || issue.project || (issue.key ? issue.key.split('-')[0] : '—');
+ document.getElementById('jiraDetailType').textContent = issue.issue_type || issue.issuetype || 'Story';
+
+ // Beschrijving
+ const descSection = document.getElementById('jiraDetailDescSection');
+ const descEl = document.getElementById('jiraDetailDesc');
+ if (issue.description) {
+ descEl.textContent = issue.description;
+ descSection.classList.remove('hidden');
+ } else {
+ descSection.classList.add('hidden');
+ }
+
+ // Labels
+ const labelsSection = document.getElementById('jiraDetailLabelsSection');
+ const labelsEl = document.getElementById('jiraDetailLabels');
+ if (issue.labels && issue.labels.length > 0) {
+ labelsEl.innerHTML = issue.labels.map(l =>
+ `
${escapeHtml(l)}`
+ ).join('');
+ labelsSection.classList.remove('hidden');
+ } else {
+ labelsSection.classList.add('hidden');
+ }
+
+ // Link naar Jira
+ const linkEl = document.getElementById('jiraDetailOpenLink');
+ if (issue.url) {
+ linkEl.href = issue.url;
+ linkEl.classList.remove('hidden');
+ } else {
+ linkEl.classList.add('hidden');
+ }
+
+ const modal = document.getElementById('jiraTicketDetailModal');
+ modal.classList.remove('hidden');
+ modal.classList.add('flex');
+ if (typeof lucide !== 'undefined') lucide.createIcons();
}
+function closeJiraTicketDetailModal() {
+ const modal = document.getElementById('jiraTicketDetailModal');
+ modal.classList.add('hidden');
+ modal.classList.remove('flex');
+}
+
+function openJiraPlanFromDetail() {
+ closeJiraTicketDetailModal();
+ if (_jiraDetailCurrentIssue) {
+ openJiraPlanModal(_jiraDetailCurrentIssue.key);
+ }
+}
+
+// --- Jira Plan Modal (met tijdsuggestie) ---
+let _jiraPlanCurrentIssue = null;
+
+function openJiraPlanModal(issueKey) {
+ const issue = plannerTicketStore.get(issueKey);
+ if (!issue) return;
+ _jiraPlanCurrentIssue = issue;
+
+ // Vul header
+ document.getElementById('jiraPlanModalKey').textContent = issue.key || '—';
+ document.getElementById('jiraPlanModalSummary').textContent = issue.summary || '—';
+ document.getElementById('jiraPlanModalStatus').textContent = issue.status || '—';
+ document.getElementById('jiraPlanModalPriority').textContent = issue.priority || '—';
+
+ // SP badge
+ const spBadge = document.getElementById('jiraPlanModalSpBadge');
+ if (issue.story_points) {
+ spBadge.textContent = `${issue.story_points} SP`;
+ spBadge.classList.remove('hidden');
+ } else {
+ spBadge.classList.add('hidden');
+ }
+
+ // Standaard duur: 15 min (of op basis van SP: 1 SP = 30 min)
+ let defaultDuration = 15;
+ if (issue.story_points && issue.story_points > 0) {
+ const spMin = Math.round(issue.story_points * 30);
+ defaultDuration = [15, 30, 60, 90].reduce((prev, curr) =>
+ Math.abs(curr - spMin) < Math.abs(prev - spMin) ? curr : prev
+ );
+ }
+ setJiraPlanDuration(defaultDuration);
+
+ // Prioriteit: vertaal Jira priority naar Fociz priority
+ const jiraPrio = (issue.priority || '').toLowerCase();
+ const focizPrio = (jiraPrio === 'highest' || jiraPrio === 'critical') ? 'P1'
+ : (jiraPrio === 'high') ? 'P2'
+ : 'P3';
+ document.getElementById('jiraPlanPriority').value = focizPrio;
+
+ // Slimme starttijd: stel het volgende vrije kwartier voor
+ const now = new Date();
+ const suggestTime = activePlannerDate === now.toISOString().slice(0,10)
+ ? suggestNextFreeSlot()
+ : '09:00';
+ document.getElementById('jiraPlanStartTime').value = suggestTime;
+
+ const modal = document.getElementById('jiraPlanModal');
+ modal.classList.remove('hidden');
+ modal.classList.add('flex');
+ if (typeof lucide !== 'undefined') lucide.createIcons();
+}
+
+function closeJiraPlanModal() {
+ const modal = document.getElementById('jiraPlanModal');
+ modal.classList.add('hidden');
+ modal.classList.remove('flex');
+ _jiraPlanCurrentIssue = null;
+}
+
+function setJiraPlanDuration(minutes) {
+ document.getElementById('jiraPlanDuration').value = minutes;
+ document.querySelectorAll('.jira-plan-dur-btn').forEach(btn => {
+ const dur = parseInt(btn.getAttribute('data-dur'), 10);
+ if (dur === minutes) {
+ btn.className = 'jira-plan-dur-btn px-2 py-1.5 rounded-xl border text-[11px] font-bold transition cursor-pointer bg-indigo-600 border-indigo-600 text-white';
+ } else {
+ btn.className = 'jira-plan-dur-btn px-2 py-1.5 rounded-xl border text-[11px] font-bold transition cursor-pointer bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800 text-slate-700 dark:text-slate-300 hover:border-indigo-400';
+ }
+ });
+}
+
+function suggestNextFreeSlot() {
+ // Zoek het volgende ronde kwartier na het huidige moment
+ const now = new Date();
+ let h = now.getHours();
+ let m = now.getMinutes();
+ // Rond op naar boven naar het volgende kwartier
+ m = Math.ceil((m + 1) / 15) * 15;
+ if (m >= 60) { h += 1; m = 0; }
+ // Begrenzen op werktijd 08:00-18:00
+ if (h < 8) { h = 8; m = 0; }
+ if (h >= 18) { h = 8; m = 0; } // Morgen → val terug op 08:00
+ return `${String(h).padStart(2,'0')}:${String(m).padStart(2,'0')}`;
+}
+
+async function submitJiraPlanModal() {
+ if (!_jiraPlanCurrentIssue) return;
+ const issue = _jiraPlanCurrentIssue;
+
+ const duration = parseInt(document.getElementById('jiraPlanDuration').value, 10) || 15;
+ const startTime = document.getElementById('jiraPlanStartTime').value.trim();
+ const priority = document.getElementById('jiraPlanPriority').value;
+
+ const btn = document.getElementById('btnSubmitJiraPlan');
+ const origHtml = btn.innerHTML;
+ btn.innerHTML = '
Inplannen...';
+ btn.disabled = true;
+
+ try {
+ const payload = {
+ title: `[${issue.key}] ${issue.summary || issue.key}`,
+ priority: priority,
+ source: 'JIRA',
+ external_id: issue.key,
+ external_url: issue.url || '',
+ duration_minutes: duration,
+ date: activePlannerDate
+ };
+ if (startTime) {
+ payload.time_block_start = startTime;
+ // Bereken eindtijd
+ const [hh, mm] = startTime.split(':').map(Number);
+ const endMin = hh * 60 + mm + duration;
+ payload.time_block_end = `${String(Math.floor(endMin / 60)).padStart(2,'0')}:${String(endMin % 60).padStart(2,'0')}`;
+ }
+
+ const res = await fetch('/api/planner.php?action=add_task', {
+ method: 'POST',
+ headers: {'Content-Type': 'application/json'},
+ body: JSON.stringify(payload)
+ });
+ const data = await res.json();
+
+ if (data.success) {
+ closeJiraPlanModal();
+ showWidgetToast(`
${escapeHtml(issue.key)} ingepland als dagtaak`, 'success');
+ switchPlannerDate(activePlannerDate, null, false);
+ } else {
+ alert(data.error || 'Kon ticket niet inplannen');
+ btn.innerHTML = origHtml;
+ btn.disabled = false;
+ }
+ } catch(e) {
+ alert('Netwerkfout bij inplannen');
+ btn.innerHTML = origHtml;
+ btn.disabled = false;
+ }
+}
+
+async function quickImportJiraByKey(btn, issueKey) {
+ // Plan knop → open de plan modal (niet direct importeren)
+ openJiraPlanModal(issueKey);
+}
+
+
// --- Direct E-mail Inplannen als Dagtaak ---
function openPlanEmailDirect(mailId, subject, sender, webLink) {
document.getElementById('planEmailId').value = mailId;