This commit is contained in:
2026-09-15 21:15:42 +02:00
parent 687b929170
commit b800279cd2
2 changed files with 75 additions and 67 deletions
+70 -62
View File
@@ -238,14 +238,14 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
</div>
<!-- Rechterkant: Knoppen Vernieuwen & Instellingen -->
<div class="flex items-center space-x-2.5 self-start md:self-center">
<button onclick="refreshJiraData()" id="refreshBtn" class="flex items-center space-x-2 px-3.5 py-2 rounded-xl text-xs font-semibold bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700 text-slate-700 dark:text-slate-200 border border-slate-200 dark:border-slate-700 transition cursor-pointer shadow-2xs">
<div class="flex flex-wrap items-center gap-2 self-start md:self-center">
<button onclick="refreshJiraData()" id="refreshBtn" class="flex items-center space-x-2 px-3 sm:px-3.5 py-2 rounded-xl text-xs font-semibold bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700 text-slate-700 dark:text-slate-200 border border-slate-200 dark:border-slate-700 transition cursor-pointer shadow-2xs">
<i data-lucide="refresh-cw" id="refreshIcon" class="w-3.5 h-3.5"></i>
<span>Vernieuwen</span>
</button>
<a href="/profile.php#integraties" class="flex items-center space-x-2 px-3.5 py-2 rounded-xl text-xs font-semibold bg-indigo-600 hover:bg-indigo-700 text-white transition cursor-pointer shadow-sm shadow-indigo-600/20">
<a href="/profile.php#integraties" class="flex items-center space-x-2 px-3 sm:px-3.5 py-2 rounded-xl text-xs font-semibold bg-indigo-600 hover:bg-indigo-700 text-white transition cursor-pointer shadow-sm shadow-indigo-600/20">
<i data-lucide="settings" class="w-3.5 h-3.5"></i>
<span>Jira Instellingen</span>
<span><span class="hidden sm:inline">Jira </span>Instellingen</span>
</a>
</div>
</div>
@@ -285,15 +285,15 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
<!-- Hub Voettekst: Live Status & Filters Uitklapknop (Rechtsonder) -->
<div class="mt-4 pt-3 border-t border-slate-100 dark:border-slate-800/60 flex flex-wrap items-center justify-between text-xs text-slate-500 dark:text-slate-400 gap-2">
<div class="flex items-center space-x-2">
<div class="flex items-center space-x-2 min-w-0 max-w-full">
<?php if ($isLive): ?>
<i data-lucide="check-circle-2" class="w-3.5 h-3.5 text-emerald-500"></i>
<span>Verbonden met <strong class="text-slate-700 dark:text-slate-200"><?= htmlspecialchars($domain) ?></strong> als <strong class="text-slate-700 dark:text-slate-200"><?= htmlspecialchars($accountName ?: $email) ?></strong></span>
<span class="text-slate-300 dark:text-slate-700 hidden sm:inline">•</span>
<span class="text-[11px] text-slate-400 hidden sm:inline">Sync: <?= date('d-m-Y H:i', strtotime($lastSync)) ?></span>
<i data-lucide="check-circle-2" class="w-3.5 h-3.5 text-emerald-500 flex-shrink-0"></i>
<span class="truncate">Verbonden met <strong class="text-slate-700 dark:text-slate-200"><?= htmlspecialchars($domain) ?></strong> als <strong class="text-slate-700 dark:text-slate-200"><?= htmlspecialchars($accountName ?: $email) ?></strong></span>
<span class="text-slate-300 dark:text-slate-700 hidden sm:inline flex-shrink-0">•</span>
<span class="text-[11px] text-slate-400 hidden sm:inline flex-shrink-0">Sync: <?= date('d-m-Y H:i', strtotime($lastSync)) ?></span>
<?php elseif ($isDemo): ?>
<i data-lucide="database" class="w-3.5 h-3.5 text-amber-500"></i>
<span>Demodataset actief met voorbeeldtickets</span>
<i data-lucide="database" class="w-3.5 h-3.5 text-amber-500 flex-shrink-0"></i>
<span class="truncate">Demodataset actief met voorbeeldtickets</span>
<?php endif; ?>
</div>
@@ -420,38 +420,38 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
</div>
</div>
<div class="flex items-center gap-1.5 overflow-x-auto pb-1 md:pb-0" id="projectPillsContainer">
<button type="button" onclick="selectProject('')" data-project="" class="project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-slate-900 dark:bg-white text-white dark:text-slate-900 shadow-xs flex-shrink-0">
<div class="flex flex-wrap items-center gap-1.5" id="projectPillsContainer">
<button type="button" onclick="selectProject('')" data-project="" class="project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-slate-900 dark:bg-white text-white dark:text-slate-900 shadow-xs flex-shrink-0">
<i data-lucide="grid" class="w-3.5 h-3.5"></i>
<span>Alle Projecten</span>
<span>Alle<span class="hidden sm:inline"> Projecten</span></span>
<span id="projCount_all" class="px-1.5 py-0.5 rounded-full text-[10px] bg-white/20 dark:bg-slate-900/20 font-black"><?= count($allIssues) ?></span>
</button>
<!-- SD: Servicedesk -->
<button type="button" onclick="selectProject('SD')" data-project="SD" class="project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-sky-50 dark:bg-sky-950/40 text-sky-800 dark:text-sky-300 hover:bg-sky-100 dark:hover:bg-sky-900/60 border border-sky-300 dark:border-sky-800 flex-shrink-0">
<button type="button" onclick="selectProject('SD')" data-project="SD" class="project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-sky-50 dark:bg-sky-950/40 text-sky-800 dark:text-sky-300 hover:bg-sky-100 dark:hover:bg-sky-900/60 border border-sky-300 dark:border-sky-800 flex-shrink-0">
<span class="px-1.5 py-0.5 rounded font-mono text-[10px] font-black bg-sky-200 dark:bg-sky-800 text-sky-900 dark:text-sky-100">SD</span>
<span>Servicedesk</span>
<span class="hidden sm:inline">Servicedesk</span>
<span id="projCount_SD" class="px-1.5 py-0.5 rounded-full text-[10px] bg-sky-200/80 dark:bg-sky-800/80 font-bold">0</span>
</button>
<!-- IN: Interfaces -->
<button type="button" onclick="selectProject('IN')" data-project="IN" class="project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-amber-50 dark:bg-amber-950/40 text-amber-800 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-900/60 border border-amber-300 dark:border-amber-800 flex-shrink-0">
<button type="button" onclick="selectProject('IN')" data-project="IN" class="project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-amber-50 dark:bg-amber-950/40 text-amber-800 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-900/60 border border-amber-300 dark:border-amber-800 flex-shrink-0">
<span class="px-1.5 py-0.5 rounded font-mono text-[10px] font-black bg-amber-200 dark:bg-amber-800 text-amber-900 dark:text-amber-100">IN</span>
<span>Interfaces</span>
<span class="hidden sm:inline">Interfaces</span>
<span id="projCount_IN" class="px-1.5 py-0.5 rounded-full text-[10px] bg-amber-200/80 dark:bg-amber-800/80 font-bold">0</span>
</button>
<!-- AAN: Aanvragen -->
<button type="button" onclick="selectProject('AAN')" data-project="AAN" class="project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-emerald-50 dark:bg-emerald-950/40 text-emerald-800 dark:text-emerald-300 hover:bg-emerald-100 dark:hover:bg-emerald-900/60 border border-emerald-300 dark:border-emerald-800 flex-shrink-0">
<button type="button" onclick="selectProject('AAN')" data-project="AAN" class="project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-emerald-50 dark:bg-emerald-950/40 text-emerald-800 dark:text-emerald-300 hover:bg-emerald-100 dark:hover:bg-emerald-900/60 border border-emerald-300 dark:border-emerald-800 flex-shrink-0">
<span class="px-1.5 py-0.5 rounded font-mono text-[10px] font-black bg-emerald-200 dark:bg-emerald-800 text-emerald-900 dark:text-emerald-100">AAN</span>
<span>Aanvragen</span>
<span class="hidden sm:inline">Aanvragen</span>
<span id="projCount_AAN" class="px-1.5 py-0.5 rounded-full text-[10px] bg-emerald-200/80 dark:bg-emerald-800/80 font-bold">0</span>
</button>
<!-- GEN: General -->
<button type="button" onclick="selectProject('GEN')" data-project="GEN" class="project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-indigo-50 dark:bg-indigo-950/40 text-indigo-800 dark:text-indigo-300 hover:bg-indigo-100 dark:hover:bg-indigo-900/60 border border-indigo-300 dark:border-indigo-800 flex-shrink-0">
<button type="button" onclick="selectProject('GEN')" data-project="GEN" class="project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-indigo-50 dark:bg-indigo-950/40 text-indigo-800 dark:text-indigo-300 hover:bg-indigo-100 dark:hover:bg-indigo-900/60 border border-indigo-300 dark:border-indigo-800 flex-shrink-0">
<span class="px-1.5 py-0.5 rounded font-mono text-[10px] font-black bg-indigo-200 dark:bg-indigo-800 text-indigo-900 dark:text-indigo-100">GEN</span>
<span>General</span>
<span class="hidden sm:inline">General</span>
<span id="projCount_GEN" class="px-1.5 py-0.5 rounded-full text-[10px] bg-indigo-200/80 dark:bg-indigo-800/80 font-bold">0</span>
</button>
@@ -461,9 +461,9 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
if (in_array($pKeyNorm, ['SD', 'IN', 'AAN', 'GEN']) || in_array($pDataKeyNorm, ['SD', 'IN', 'AAN', 'GEN'])) continue;
$displayKey = !empty($pData['key']) ? $pData['key'] : $pKey;
?>
<button type="button" onclick="selectProject('<?= htmlspecialchars(addslashes($displayKey)) ?>')" data-project="<?= htmlspecialchars($displayKey) ?>" class="project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0">
<button type="button" onclick="selectProject('<?= htmlspecialchars(addslashes($displayKey)) ?>')" data-project="<?= htmlspecialchars($displayKey) ?>" class="project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0">
<span class="px-1.5 py-0.5 rounded font-mono text-[10px] font-black bg-slate-200 dark:bg-slate-700"><?= htmlspecialchars($displayKey) ?></span>
<span><?= htmlspecialchars($pData['name']) ?></span>
<span class="hidden sm:inline"><?= htmlspecialchars($pData['name']) ?></span>
<span class="px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-slate-100 dark:bg-slate-700 text-slate-600 dark:text-slate-300"><?= $projectCounts[$pKey] ?? 0 ?></span>
</button>
<?php endforeach; ?>
@@ -558,7 +558,7 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
<!-- Dropdown Menu met Checkboxes (Gegroepeerd per project) -->
<div
id="statusDropdownMenu"
class="hidden absolute left-0 top-full mt-2 w-80 sm:w-96 max-w-[95vw] bg-white dark:bg-slate-900 rounded-2xl shadow-2xl border border-slate-200 dark:border-slate-800 py-3 px-3.5 z-50 text-xs space-y-2.5"
class="hidden absolute left-0 top-full mt-2 w-[calc(100vw-2.5rem)] sm:w-96 max-w-sm bg-white dark:bg-slate-900 rounded-2xl shadow-2xl border border-slate-200 dark:border-slate-800 py-3 px-3.5 z-50 text-xs space-y-2.5"
>
<!-- Quick actions header -->
<div class="flex items-center justify-between pb-2 border-b border-slate-100 dark:border-slate-800">
@@ -835,12 +835,12 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
</p>
</div>
<div class="flex items-center space-x-2.5 pt-2">
<div class="flex flex-col sm:flex-row items-stretch sm:items-center gap-2 pt-2">
<button type="button" id="btnOutsideWorkhoursNoJira" onclick="closeOutsideWorkhoursModalJira('NEE')" class="flex-1 px-4 py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-xs transition shadow-xs flex items-center justify-center space-x-1.5 cursor-pointer">
<i data-lucide="calendar-arrow-up" class="w-4 h-4"></i>
<span>Nee (Volgende werkdag)</span>
</button>
<button type="button" id="btnOutsideWorkhoursYesJira" onclick="closeOutsideWorkhoursModalJira('JA')" class="px-4 py-2.5 rounded-xl bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700 text-slate-700 dark:text-slate-200 font-semibold text-xs transition cursor-pointer">
<button type="button" id="btnOutsideWorkhoursYesJira" onclick="closeOutsideWorkhoursModalJira('JA')" class="px-4 py-2.5 rounded-xl bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700 text-slate-700 dark:text-slate-200 font-semibold text-xs transition cursor-pointer text-center">
<span>Ja, toch inplannen</span>
</button>
</div>
@@ -1462,28 +1462,28 @@ function updateProjectPills() {
if (pKey === '') {
p.className = isSelected
? "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-slate-900 dark:bg-white text-white dark:text-slate-900 shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-slate-100 dark:bg-slate-800/80 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0";
? "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-slate-900 dark:bg-white text-white dark:text-slate-900 shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-slate-100 dark:bg-slate-800/80 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0";
} else if (pKey === 'SD') {
p.className = isSelected
? "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-sky-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-sky-50 dark:bg-sky-950/40 text-sky-800 dark:text-sky-300 hover:bg-sky-100 dark:hover:bg-sky-900/60 border border-sky-300 dark:border-sky-800 flex-shrink-0";
? "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-sky-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-sky-50 dark:bg-sky-950/40 text-sky-800 dark:text-sky-300 hover:bg-sky-100 dark:hover:bg-sky-900/60 border border-sky-300 dark:border-sky-800 flex-shrink-0";
} else if (pKey === 'IN') {
p.className = isSelected
? "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-amber-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-amber-50 dark:bg-amber-950/40 text-amber-800 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-900/60 border border-amber-300 dark:border-amber-800 flex-shrink-0";
? "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-amber-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-amber-50 dark:bg-amber-950/40 text-amber-800 dark:text-amber-300 hover:bg-amber-100 dark:hover:bg-amber-900/60 border border-amber-300 dark:border-amber-800 flex-shrink-0";
} else if (pKey === 'AAN') {
p.className = isSelected
? "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-emerald-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-emerald-50 dark:bg-emerald-950/40 text-emerald-800 dark:text-emerald-300 hover:bg-emerald-100 dark:hover:bg-emerald-900/60 border border-emerald-300 dark:border-emerald-800 flex-shrink-0";
? "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-emerald-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-emerald-50 dark:bg-emerald-950/40 text-emerald-800 dark:text-emerald-300 hover:bg-emerald-100 dark:hover:bg-emerald-900/60 border border-emerald-300 dark:border-emerald-800 flex-shrink-0";
} else if (pKey === 'GEN') {
p.className = isSelected
? "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-indigo-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-indigo-50 dark:bg-indigo-950/40 text-indigo-800 dark:text-indigo-300 hover:bg-indigo-100 dark:hover:bg-indigo-900/60 border border-indigo-300 dark:border-indigo-800 flex-shrink-0";
? "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-indigo-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-indigo-50 dark:bg-indigo-950/40 text-indigo-800 dark:text-indigo-300 hover:bg-indigo-100 dark:hover:bg-indigo-900/60 border border-indigo-300 dark:border-indigo-800 flex-shrink-0";
} else {
p.className = isSelected
? "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-indigo-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0";
? "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-indigo-600 text-white shadow-xs flex-shrink-0"
: "project-pill flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800 text-slate-700 dark:text-slate-300 hover:bg-slate-200 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0";
}
});
}
@@ -2071,16 +2071,16 @@ function renderTickets(tickets) {
if (pKey === 'SD' || (!pKey && pName.includes('servicedesk'))) {
projBadgeClass = 'bg-sky-50 dark:bg-sky-950/40 text-sky-700 dark:text-sky-300 border border-sky-300 dark:border-sky-800/80';
projLabel = 'SD • Servicedesk';
projLabel = 'SD<span class="hidden sm:inline"> • Servicedesk</span>';
} else if (pKey === 'IN' || (!pKey && pName.includes('interface'))) {
projBadgeClass = 'bg-amber-50 dark:bg-amber-950/40 text-amber-700 dark:text-amber-300 border border-amber-300 dark:border-amber-800/80';
projLabel = 'IN • Interfaces';
projLabel = 'IN<span class="hidden sm:inline"> • Interfaces</span>';
} else if (pKey === 'AAN' || (!pKey && pName.includes('aanvraag'))) {
projBadgeClass = 'bg-emerald-50 dark:bg-emerald-950/40 text-emerald-700 dark:text-emerald-300 border border-emerald-300 dark:border-emerald-800/80';
projLabel = 'AAN • Aanvragen';
projLabel = 'AAN<span class="hidden sm:inline"> • Aanvragen</span>';
} else if (pKey === 'GEN' || (!pKey && (pName.includes('general') || pName.includes('algemeen')))) {
projBadgeClass = 'bg-indigo-50 dark:bg-indigo-950/40 text-indigo-700 dark:text-indigo-300 border border-indigo-300 dark:border-indigo-800/80';
projLabel = 'GEN • General';
projLabel = 'GEN<span class="hidden sm:inline"> • General</span>';
}
// Status badge styling
@@ -2172,7 +2172,7 @@ function renderTickets(tickets) {
<div class="flex-1 min-w-0 space-y-2">
<!-- Header regel met Type, Key, Project, Prioriteit & Status -->
<div class="flex flex-wrap items-center gap-2">
<div class="flex flex-wrap items-center gap-1.5 sm:gap-2">
<!-- Type Badge -->
<span class="inline-flex items-center space-x-1 px-2 py-0.5 rounded-md text-[10px] font-bold uppercase tracking-wider border ${typeBadgeClass}">
<i data-lucide="${typeIcon}" class="w-3 h-3"></i>
@@ -2180,7 +2180,7 @@ function renderTickets(tickets) {
</span>
<!-- Key Link -->
<a href="${escapeHtml(ticket.url)}" target="_blank" rel="noopener noreferrer" class="font-mono text-xs font-black text-indigo-600 dark:text-indigo-400 hover:underline flex items-center space-x-1">
<a href="${sanitizeUrl(ticket.url)}" target="_blank" rel="noopener noreferrer" class="font-mono text-xs font-black text-indigo-600 dark:text-indigo-400 hover:underline flex items-center space-x-1">
<span>${escapeHtml(ticket.key)}</span>
<i data-lucide="external-link" class="w-3 h-3 text-slate-400"></i>
</a>
@@ -2208,14 +2208,14 @@ function renderTickets(tickets) {
</div>
<!-- Ticket Titel / Summary (Klikbaar voor accordion) -->
<h3 class="text-sm sm:text-base font-bold text-slate-900 dark:text-white leading-snug cursor-pointer group" onclick="toggleTicketDetails('${escapeHtml(ticket.key)}', event)">
<h3 class="text-sm sm:text-base font-bold text-slate-900 dark:text-white leading-snug cursor-pointer group break-words" onclick="toggleTicketDetails('${escapeHtml(ticket.key)}', event)">
<span class="group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition">
${escapeHtml(ticket.summary || '(Geen titel)')}
</span>
</h3>
<!-- Sub-info: Assignee, Update timestamp & Inklap knop -->
<div class="flex flex-wrap items-center gap-4 pt-1">
<div class="flex flex-wrap items-center gap-3 sm:gap-4 pt-1">
${assigneeHtml}
${ticket.updated ? `<span class="text-[11px] text-slate-400">Gewijzigd: ${escapeHtml(ticket.updated)}</span>` : ''}
${ticket.onderwerp_logging ? onderwerpBadge : ''}
@@ -2224,23 +2224,23 @@ function renderTickets(tickets) {
</div>
<!-- Rechterkant: Snelle Actieknoppen + Uitklapknop -->
<div class="flex items-center space-x-2 pt-2 lg:pt-0 flex-shrink-0 border-t lg:border-t-0 border-slate-100 dark:border-slate-800">
<button type="button" onclick="toggleTicketDetails('${escapeHtml(ticket.key)}', event)" class="flex items-center space-x-1 px-2.5 py-2 rounded-xl text-xs font-bold text-slate-600 dark:text-slate-300 hover:text-indigo-600 dark:hover:text-indigo-400 bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700/80 transition cursor-pointer shadow-2xs">
<div class="flex flex-wrap sm:flex-nowrap items-center gap-1.5 sm:gap-2 pt-2 lg:pt-0 border-t lg:border-t-0 border-slate-100 dark:border-slate-800">
<button type="button" onclick="toggleTicketDetails('${escapeHtml(ticket.key)}', event)" class="flex items-center space-x-1 px-2.5 py-1.5 sm:py-2 rounded-xl text-xs font-bold text-slate-600 dark:text-slate-300 hover:text-indigo-600 dark:hover:text-indigo-400 bg-slate-100 hover:bg-slate-200 dark:bg-slate-800 dark:hover:bg-slate-700/80 transition cursor-pointer shadow-2xs">
<span>Details</span>
<i data-lucide="chevron-down" id="chevron_${escapeHtml(ticket.key)}" class="w-3.5 h-3.5 transition-transform duration-200 ${chevronRotateClass}"></i>
</button>
<button onclick="openPlanModal('${escapeHtml(ticket.key)}')" class="flex items-center space-x-1.5 px-3 py-2 rounded-xl text-xs font-bold bg-indigo-50 hover:bg-indigo-100 text-indigo-700 dark:bg-indigo-950/50 dark:hover:bg-indigo-900/60 dark:text-indigo-300 border border-indigo-200/80 dark:border-indigo-800/80 transition cursor-pointer shadow-2xs">
<button onclick="openPlanModal('${escapeHtml(ticket.key)}')" class="flex items-center space-x-1 sm:space-x-1.5 px-2.5 sm:px-3 py-1.5 sm:py-2 rounded-xl text-xs font-bold bg-indigo-50 hover:bg-indigo-100 text-indigo-700 dark:bg-indigo-950/50 dark:hover:bg-indigo-900/60 dark:text-indigo-300 border border-indigo-200/80 dark:border-indigo-800/80 transition cursor-pointer shadow-2xs">
<i data-lucide="calendar" class="w-3.5 h-3.5"></i>
<span>Plan in Dagplanner</span>
<span>Plan<span class="hidden sm:inline"> in Dagplanner</span></span>
</button>
<button onclick="openBoardModal('${escapeHtml(ticket.key)}')" class="flex items-center space-x-1.5 px-3 py-2 rounded-xl text-xs font-bold bg-emerald-50 hover:bg-emerald-100 text-emerald-700 dark:bg-emerald-950/50 dark:hover:bg-indigo-900/60 dark:text-emerald-300 border border-emerald-200/80 dark:border-emerald-800/80 transition cursor-pointer shadow-2xs">
<button onclick="openBoardModal('${escapeHtml(ticket.key)}')" class="flex items-center space-x-1 sm:space-x-1.5 px-2.5 sm:px-3 py-1.5 sm:py-2 rounded-xl text-xs font-bold bg-emerald-50 hover:bg-emerald-100 text-emerald-700 dark:bg-emerald-950/50 dark:hover:bg-indigo-900/60 dark:text-emerald-300 border border-emerald-200/80 dark:border-emerald-800/80 transition cursor-pointer shadow-2xs">
<i data-lucide="kanban" class="w-3.5 h-3.5"></i>
<span>Naar Bord</span>
<span><span class="hidden sm:inline">Naar </span>Bord</span>
</button>
<a href="${escapeHtml(ticket.url)}" target="_blank" rel="noopener noreferrer" title="Bekijk in Jira" class="p-2 rounded-xl text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition cursor-pointer">
<a href="${sanitizeUrl(ticket.url)}" target="_blank" rel="noopener noreferrer" title="Bekijk in Jira" class="p-1.5 sm:p-2 rounded-xl text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition cursor-pointer">
<i data-lucide="external-link" class="w-4 h-4"></i>
</a>
</div>
@@ -2256,7 +2256,7 @@ function renderTickets(tickets) {
<i data-lucide="align-left" class="w-3.5 h-3.5 text-indigo-500"></i>
<span>Volledige Ticket Omschrijving</span>
</div>
<div class="p-4 rounded-xl bg-slate-50 dark:bg-slate-900/90 border border-slate-200/80 dark:border-slate-800 text-slate-800 dark:text-slate-200 leading-relaxed whitespace-pre-wrap select-text font-normal">
<div class="p-3.5 sm:p-4 rounded-xl bg-slate-50 dark:bg-slate-900/90 border border-slate-200/80 dark:border-slate-800 text-slate-800 dark:text-slate-200 leading-relaxed whitespace-pre-wrap break-words overflow-x-hidden select-text font-normal">
${escapeHtml(ticket.description || 'Geen uitgebreide omschrijving beschikbaar in Jira.')}
</div>
</div>
@@ -2295,24 +2295,24 @@ function renderTickets(tickets) {
<!-- Snelle acties onderaan detailvak -->
<div class="flex flex-wrap items-center justify-between gap-2 pt-2 border-t border-slate-100 dark:border-slate-800/80">
<div class="flex items-center space-x-2">
<div class="flex flex-wrap items-center gap-1.5 sm:gap-2">
<button type="button" onclick='copyTicketKey("${escapeHtml(ticket.key)}")' class="flex items-center space-x-1 px-2.5 py-1.5 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 text-slate-700 dark:text-slate-300 text-xs font-semibold transition cursor-pointer">
<i data-lucide="copy" class="w-3 h-3"></i>
<span>Sleutel Kopiëren</span>
<span>Kopieer<span class="hidden sm:inline"> Sleutel</span></span>
</button>
<a href="${escapeHtml(ticket.url)}" target="_blank" rel="noopener noreferrer" class="flex items-center space-x-1 px-2.5 py-1.5 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 text-indigo-600 dark:text-indigo-400 text-xs font-semibold transition cursor-pointer">
<a href="${sanitizeUrl(ticket.url)}" target="_blank" rel="noopener noreferrer" class="flex items-center space-x-1 px-2.5 py-1.5 rounded-lg bg-slate-100 dark:bg-slate-800 hover:bg-slate-200 dark:hover:bg-slate-700 text-indigo-600 dark:text-indigo-400 text-xs font-semibold transition cursor-pointer">
<i data-lucide="external-link" class="w-3 h-3"></i>
<span>Open in Jira Cloud</span>
<span>Jira<span class="hidden sm:inline"> Cloud</span></span>
</a>
</div>
<div class="flex items-center space-x-2">
<div class="flex flex-wrap items-center gap-1.5 sm:gap-2">
<button type="button" onclick='openPlanModal("${escapeHtml(ticket.key)}")' class="flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold bg-indigo-600 hover:bg-indigo-700 text-white transition cursor-pointer shadow-xs">
<i data-lucide="calendar" class="w-3 h-3"></i>
<span>Inplannen in Dagplanner</span>
<span>Inplannen<span class="hidden sm:inline"> in Dagplanner</span></span>
</button>
<button type="button" onclick='openBoardModal("${escapeHtml(ticket.key)}")' class="flex items-center space-x-1.5 px-3 py-1.5 rounded-xl text-xs font-bold bg-emerald-600 hover:bg-emerald-700 text-white transition cursor-pointer shadow-xs">
<i data-lucide="kanban" class="w-3 h-3"></i>
<span>Naar Kanban Bord</span>
<span><span class="hidden sm:inline">Naar </span>Bord</span>
</button>
</div>
</div>
@@ -2797,6 +2797,14 @@ function escapeHtml(str) {
.replace(/'/g, '&#039;');
}
// Helper voor veilige URL sanitization (voorkomt javascript: of onveilige protocols)
function sanitizeUrl(url) {
if (!url) return '#';
const s = String(url).trim();
if (/^https?:\/\//i.test(s)) return escapeHtml(s);
return '#';
}
// Toetsenbordnavigatie voor alle modals (Escape & Enter)
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {