Jira 24
This commit is contained in:
@@ -1322,15 +1322,34 @@ require_once __DIR__ . '/includes/header.php';
|
||||
<?php
|
||||
$userNamesByFilter = [];
|
||||
foreach ($allUsersForJira as $u) {
|
||||
if (!empty($u['default_jira_filter_code'])) {
|
||||
$userNamesByFilter[$u['default_jira_filter_code']][] = $u['name'];
|
||||
$userCodes = [];
|
||||
if (!empty($u['default_jira_filter_codes'])) {
|
||||
$decoded = json_decode($u['default_jira_filter_codes'], true);
|
||||
if (is_array($decoded)) {
|
||||
$userCodes = $decoded;
|
||||
}
|
||||
}
|
||||
if (empty($userCodes) && !empty($u['default_jira_filter_code'])) {
|
||||
$userCodes = [$u['default_jira_filter_code']];
|
||||
}
|
||||
foreach ($userCodes as $code) {
|
||||
$codeClean = strtoupper(trim($code));
|
||||
if ($codeClean !== '') {
|
||||
if (!isset($userNamesByFilter[$codeClean])) {
|
||||
$userNamesByFilter[$codeClean] = [];
|
||||
}
|
||||
if (!in_array($u['name'], $userNamesByFilter[$codeClean], true)) {
|
||||
$userNamesByFilter[$codeClean][] = $u['name'];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="grid grid-cols-1 gap-3.5">
|
||||
<?php foreach ($allJiraFilters as $filter): ?>
|
||||
<?php
|
||||
$assignedColleagues = $userNamesByFilter[$filter['filter_code']] ?? [];
|
||||
$fCodeNorm = strtoupper(trim($filter['filter_code'] ?? ''));
|
||||
$assignedColleagues = $userNamesByFilter[$fCodeNorm] ?? ($userNamesByFilter[$filter['filter_code'] ?? ''] ?? []);
|
||||
$filterJsonSafe = htmlspecialchars(json_encode($filter), ENT_QUOTES, 'UTF-8');
|
||||
?>
|
||||
<div class="p-4 rounded-2xl bg-slate-50 dark:bg-[#0c0f17] border border-slate-200 dark:border-slate-800 hover:border-indigo-500/40 transition flex flex-col md:flex-row md:items-center justify-between gap-4">
|
||||
|
||||
@@ -56,7 +56,97 @@ foreach ($boards as &$b) {
|
||||
}
|
||||
unset($b);
|
||||
|
||||
// Verzamel unieke projecten en types voor filter dropdowns en project switcher
|
||||
// Ophalen van actieve wachtrijfilters en toewijzingen van de ingelogde gebruiker
|
||||
$queueFilters = $jiraData['queue_filters'] ?? jira_get_queue_filters(true);
|
||||
$defaultQueueFilter = $jiraData['default_queue_filter'] ?? jira_get_user_queue_preference($userId);
|
||||
$userQueueFilters = $jiraData['user_queue_filters'] ?? jira_get_user_queue_preferences($userId);
|
||||
$userQueueFilters = array_values(array_filter(array_map('strtoupper', array_map('trim', $userQueueFilters ?? []))));
|
||||
$jiraData['user_queue_filters'] = $userQueueFilters;
|
||||
|
||||
// Hulpfunctie om te controleren of een ticket behoort tot een wachtrij / project
|
||||
$issueMatchesQueue = function(array $iss, string $code) use ($queueFilters): bool {
|
||||
$code = strtoupper(trim($code));
|
||||
if (empty($code)) return true;
|
||||
|
||||
$pKey = strtoupper(trim($iss['project_key'] ?? ''));
|
||||
if (empty($pKey) && !empty($iss['key']) && preg_match('/^([A-Z0-9]+)-/i', $iss['key'], $m)) {
|
||||
$pKey = strtoupper($m[1]);
|
||||
}
|
||||
$pName = strtolower(trim($iss['project'] ?? ''));
|
||||
$ol = strtolower(trim($iss['onderwerp_logging'] ?? ''));
|
||||
$sum = strtolower(trim($iss['summary'] ?? ''));
|
||||
|
||||
// 1. Zoek geconfigureerd filter in database
|
||||
foreach ($queueFilters as $qf) {
|
||||
if (strtoupper(trim($qf['filter_code'] ?? '')) === $code) {
|
||||
if (!empty($qf['project_key'])) {
|
||||
$filterPKey = strtoupper(trim($qf['project_key']));
|
||||
if ($pKey) {
|
||||
if ($pKey !== $filterPKey) return false;
|
||||
} elseif (strpos($pName, strtolower($filterPKey)) === false) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!empty($qf['jql_clause'])) {
|
||||
$jql = $qf['jql_clause'];
|
||||
if (preg_match('/onderwerp\s+logging.*?in\s*\(([^)]+)\)/i', $jql, $jm)) {
|
||||
$terms = array_map(function($t) { return trim($t, " '\"\t\n\r\0\x0B"); }, explode(',', strtolower($jm[1])));
|
||||
$found = false;
|
||||
foreach ($terms as $t) {
|
||||
if ($t !== '' && (strpos($ol, $t) !== false || strpos($sum, $t) !== false)) {
|
||||
$found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$found) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Fallbacks voor standaard wachtrijen/projecten
|
||||
if ($code === 'DX') {
|
||||
$isSd = ($pKey === 'SD') || strpos($pName, 'servicedesk') !== false;
|
||||
if (!$isSd) return false;
|
||||
$dxTerms = ['koppeling', 'sso', 'endpoint', 'rest', 'datadump', 'uwv', 'migratie'];
|
||||
foreach ($dxTerms as $t) {
|
||||
if (strpos($ol, $t) !== false || strpos($sum, $t) !== false) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if ($code === 'SD') {
|
||||
return ($pKey === 'SD') || strpos($pName, 'servicedesk') !== false;
|
||||
}
|
||||
if ($code === 'IN') {
|
||||
return ($pKey === 'IN') || strpos($pName, 'interface') !== false;
|
||||
}
|
||||
if ($code === 'AAN') {
|
||||
return ($pKey === 'AAN') || strpos($pName, 'aanvraag') !== false;
|
||||
}
|
||||
if ($code === 'GEN') {
|
||||
return ($pKey === 'GEN') || strpos($pName, 'general') !== false || strpos($pName, 'algemeen') !== false;
|
||||
}
|
||||
|
||||
return ($pKey === $code);
|
||||
};
|
||||
|
||||
// Als er specifieke projecten aan de gebruiker zijn toegewezen: verberg tickets van niet-toegewezen projecten
|
||||
if (!empty($userQueueFilters)) {
|
||||
$filterByAssigned = function(array $issues) use ($userQueueFilters, $issueMatchesQueue): array {
|
||||
return array_values(array_filter($issues, function($iss) use ($userQueueFilters, $issueMatchesQueue) {
|
||||
foreach ($userQueueFilters as $qCode) {
|
||||
if ($issueMatchesQueue($iss, $qCode)) return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
};
|
||||
$assignedIssues = $filterByAssigned($assignedIssues);
|
||||
$teamIssues = $filterByAssigned($teamIssues);
|
||||
$jiraData['assigned_issues'] = $assignedIssues;
|
||||
$jiraData['team_issues'] = $teamIssues;
|
||||
}
|
||||
|
||||
// Verzamel unieke projecten en types voor filter dropdowns en project switcher
|
||||
$allIssues = array_merge($assignedIssues, $teamIssues);
|
||||
$allProjects = [];
|
||||
@@ -198,10 +288,6 @@ $allStatusesList = array_values($allStatusesMap);
|
||||
ksort($allProjects);
|
||||
ksort($allTypes);
|
||||
|
||||
$queueFilters = $jiraData['queue_filters'] ?? jira_get_queue_filters(true);
|
||||
$defaultQueueFilter = $jiraData['default_queue_filter'] ?? jira_get_user_queue_preference($userId);
|
||||
$userQueueFilters = $jiraData['user_queue_filters'] ?? jira_get_user_queue_preferences($userId);
|
||||
|
||||
$jiraDataJson = json_encode($jiraData, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
||||
$boardsJson = json_encode($boards, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
||||
$projectStatusesJson = json_encode($projectStatusesData, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
||||
@@ -408,7 +494,7 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
|
||||
<!-- Hoofdbalk: Project Switcher, Wachtrijen, Tabs & Status Filtering -->
|
||||
<div class="fociz-card relative z-30 p-4 sm:p-5 space-y-4 shadow-sm border border-slate-200/80 dark:border-slate-800 overflow-visible">
|
||||
|
||||
<!-- Rij 1: Prominente Project Switcher (SD, IN, AAN, GEN, Alle) -->
|
||||
<!-- Prominente Project Switcher (Toegewezen Projecten: SD, GEN, IN, etc.) -->
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-3 pb-3 border-b border-slate-100 dark:border-slate-800/80">
|
||||
<div class="flex items-center space-x-2.5">
|
||||
<div class="w-7 h-7 rounded-lg bg-indigo-50 dark:bg-indigo-950/60 text-indigo-600 dark:text-indigo-400 flex items-center justify-center font-bold">
|
||||
@@ -416,7 +502,7 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-xs font-black uppercase tracking-wider text-slate-900 dark:text-white">Projecten</span>
|
||||
<p class="text-[10px] text-slate-400">Direct filteren op projectcode</p>
|
||||
<p class="text-[10px] text-slate-400">Direct schakelen tussen jouw toegewezen projecten</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -427,108 +513,74 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
|
||||
<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>
|
||||
|
||||
<?php
|
||||
$hasUserAssignments = !empty($userQueueFilters);
|
||||
$isCodeVisible = function(string $code) use ($hasUserAssignments, $userQueueFilters): bool {
|
||||
if (!$hasUserAssignments) return true;
|
||||
return in_array(strtoupper(trim($code)), $userQueueFilters, true);
|
||||
};
|
||||
?>
|
||||
|
||||
<!-- SD: Servicedesk -->
|
||||
<?php if ($isCodeVisible('SD')): ?>
|
||||
<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 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-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 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-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 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>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- GEN: General -->
|
||||
<?php if ($isCodeVisible('GEN')): ?>
|
||||
<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 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>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php foreach ($allProjects as $pKey => $pData):
|
||||
$pKeyNorm = strtoupper($pKey);
|
||||
$pDataKeyNorm = strtoupper($pData['key'] ?? '');
|
||||
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-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 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; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rij 2: Wachtrijen & Multi-queue voorkeur -->
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-3 pb-3 border-b border-slate-100 dark:border-slate-800/80">
|
||||
<div class="flex items-center space-x-2.5">
|
||||
<div class="w-7 h-7 rounded-lg bg-indigo-50 dark:bg-indigo-950/60 text-indigo-600 dark:text-indigo-400 flex items-center justify-center font-bold">
|
||||
<i data-lucide="filter" class="w-4 h-4"></i>
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-xs font-black uppercase tracking-wider text-slate-900 dark:text-white">Wachtrij</span>
|
||||
<p class="text-[10px] text-slate-400">Filter op gekoppelde wachtrijen</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-wrap items-center gap-1.5" id="queueButtonsContainer">
|
||||
<button type="button" onclick="selectQueueFilter('')" data-queue="" class="queue-pill flex items-center space-x-1 px-2.5 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-slate-200/80 dark:bg-slate-800 text-slate-700 dark:text-slate-300 flex-shrink-0">
|
||||
<i data-lucide="list" class="w-3 h-3"></i>
|
||||
<span>Alle</span>
|
||||
<span id="queueCount_all" class="px-1.5 py-0.5 rounded-full text-[10px] bg-slate-300 dark:bg-slate-700 font-bold">0</span>
|
||||
</button>
|
||||
|
||||
<?php if (!empty($userQueueFilters)): ?>
|
||||
<button type="button" onclick="selectQueueFilter('MY_QUEUES')" data-queue="MY_QUEUES" class="queue-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/50 text-amber-800 dark:text-amber-200 border border-amber-300 dark:border-amber-700/80 flex-shrink-0" title="Alleen tickets uit jouw gekoppelde wachtrijen (<?= htmlspecialchars(implode(', ', $userQueueFilters)) ?>)">
|
||||
<i data-lucide="star" class="w-3.5 h-3.5 text-amber-500 fill-amber-500"></i>
|
||||
<span>Mijn</span>
|
||||
<span id="queueCount_MY_QUEUES" class="px-1.5 py-0.5 rounded-full text-[10px] bg-amber-200 dark:bg-amber-800 font-bold">0</span>
|
||||
<!-- IN: Interfaces -->
|
||||
<?php if ($isCodeVisible('IN')): ?>
|
||||
<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 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>
|
||||
<?php endif; ?>
|
||||
|
||||
<button type="button" onclick="selectQueueFilter('SD')" data-queue="SD" class="queue-pill flex items-center space-x-1.5 px-2.5 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800/80 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0">
|
||||
<span class="w-2 h-2 rounded-full bg-sky-500"></span>
|
||||
<span>SD</span>
|
||||
<span id="queueCount_SD" class="px-1.5 py-0.5 rounded-full text-[10px] bg-sky-100 dark:bg-sky-950/60 text-sky-800 dark:text-sky-300 font-bold">0</span>
|
||||
<!-- AAN: Aanvragen -->
|
||||
<?php if ($isCodeVisible('AAN')): ?>
|
||||
<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 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>
|
||||
<?php endif; ?>
|
||||
|
||||
<button type="button" onclick="selectQueueFilter('IN')" data-queue="IN" class="queue-pill flex items-center space-x-1.5 px-2.5 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800/80 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0">
|
||||
<span class="w-2 h-2 rounded-full bg-amber-500"></span>
|
||||
<span>IN</span>
|
||||
<span id="queueCount_IN" class="px-1.5 py-0.5 rounded-full text-[10px] bg-amber-100 dark:bg-amber-950/60 text-amber-800 dark:text-amber-300 font-bold">0</span>
|
||||
</button>
|
||||
|
||||
<button type="button" onclick="selectQueueFilter('AAN')" data-queue="AAN" class="queue-pill flex items-center space-x-1.5 px-2.5 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800/80 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0">
|
||||
<span class="w-2 h-2 rounded-full bg-emerald-500"></span>
|
||||
<span>AAN</span>
|
||||
<span id="queueCount_AAN" class="px-1.5 py-0.5 rounded-full text-[10px] bg-emerald-100 dark:bg-emerald-950/60 text-emerald-800 dark:text-emerald-300 font-bold">0</span>
|
||||
</button>
|
||||
|
||||
<button type="button" onclick="selectQueueFilter('GEN')" data-queue="GEN" class="queue-pill flex items-center space-x-1.5 px-2.5 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800/80 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0">
|
||||
<span class="w-2 h-2 rounded-full bg-indigo-500"></span>
|
||||
<span>GEN</span>
|
||||
<span id="queueCount_GEN" class="px-1.5 py-0.5 rounded-full text-[10px] bg-indigo-100 dark:bg-indigo-950/60 text-indigo-800 dark:text-indigo-300 font-bold">0</span>
|
||||
</button>
|
||||
|
||||
<button type="button" onclick="selectQueueFilter('DX')" data-queue="DX" class="queue-pill flex items-center space-x-1.5 px-2.5 py-1.5 rounded-xl text-xs font-bold transition cursor-pointer bg-white dark:bg-slate-800/80 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 border border-slate-200 dark:border-slate-700 flex-shrink-0">
|
||||
<i data-lucide="zap" class="w-3 h-3 text-amber-500"></i>
|
||||
<!-- DX: DX Koppelingen -->
|
||||
<?php if ($isCodeVisible('DX')): ?>
|
||||
<button type="button" onclick="selectProject('DX')" data-project="DX" 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">
|
||||
<i data-lucide="zap" class="w-3.5 h-3.5 text-amber-500"></i>
|
||||
<span>DX</span>
|
||||
<span id="queueCount_DX" class="px-1.5 py-0.5 rounded-full text-[10px] bg-amber-100 dark:bg-amber-950/60 text-amber-800 dark:text-amber-300 font-bold">0</span>
|
||||
<span id="projCount_DX" 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>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Overige geconfigureerde projectfilters -->
|
||||
<?php foreach ($queueFilters as $qf):
|
||||
$qCode = strtoupper(trim($qf['filter_code'] ?? ''));
|
||||
if (in_array($qCode, ['SD', 'IN', 'AAN', 'GEN', 'DX'], true)) continue;
|
||||
if (!$isCodeVisible($qCode)) continue;
|
||||
?>
|
||||
<button type="button" onclick="selectProject('<?= htmlspecialchars(addslashes($qCode)) ?>')" data-project="<?= htmlspecialchars($qCode) ?>" 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($qCode) ?></span>
|
||||
<span class="hidden sm:inline"><?= htmlspecialchars($qf['filter_name']) ?></span>
|
||||
<span id="projCount_<?= htmlspecialchars($qCode) ?>" 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">0</span>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if ($isAdmin): ?>
|
||||
<a href="/admin.php#projects" title="Wachtrijfilters beheren in Beheer" class="p-1.5 rounded-lg text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition cursor-pointer flex-shrink-0">
|
||||
<a href="/admin.php#projects" title="Wachtrijen & Projecten beheren in Beheer" class="p-1.5 rounded-lg text-slate-400 hover:text-slate-700 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition cursor-pointer flex-shrink-0">
|
||||
<i data-lucide="settings" class="w-3.5 h-3.5"></i>
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
@@ -963,7 +1015,7 @@ const projectStatusesData = <?= $projectStatusesJson ?>;
|
||||
const allJiraStatuses = <?= $statusesJson ?>;
|
||||
|
||||
let currentProject = '';
|
||||
let currentQueue = (jiraData.user_queue_filters && jiraData.user_queue_filters.length > 0) ? 'MY_QUEUES' : (jiraData.default_queue_filter || '');
|
||||
let currentQueue = '';
|
||||
let selectedStatuses = new Set();
|
||||
let activeUrgentOnly = false;
|
||||
let expandedKeys = new Set();
|
||||
@@ -975,9 +1027,18 @@ function getDeduplicatedIssues() {
|
||||
const team = jiraData.team_issues || [];
|
||||
const seen = new Set();
|
||||
const list = [];
|
||||
const myQueues = (jiraData.user_queue_filters || []).map(q => String(q).toUpperCase().trim());
|
||||
const hasAssignments = myQueues.length > 0;
|
||||
|
||||
[...assigned, ...team].forEach(iss => {
|
||||
if (iss && iss.key && !seen.has(iss.key)) {
|
||||
seen.add(iss.key);
|
||||
// Als er specifieke projecten aan de gebruiker zijn toegewezen:
|
||||
// verberg tickets die niet tot minstens één van de toegewezen projecten behoren
|
||||
if (hasAssignments) {
|
||||
const isAllowed = myQueues.some(q => isIssueInQueue(iss, q));
|
||||
if (!isAllowed) return;
|
||||
}
|
||||
list.push(iss);
|
||||
}
|
||||
});
|
||||
@@ -1009,8 +1070,15 @@ function isInProgressStatus(st, sc) {
|
||||
// Bereken statussen en KPI statistieken voor de drawer
|
||||
function calculateKpis() {
|
||||
const all = getDeduplicatedIssues();
|
||||
const assigned = jiraData.assigned_issues || [];
|
||||
const team = jiraData.team_issues || [];
|
||||
const myQueues = (jiraData.user_queue_filters || []).map(q => String(q).toUpperCase().trim());
|
||||
const hasAssignments = myQueues.length > 0;
|
||||
|
||||
let assigned = jiraData.assigned_issues || [];
|
||||
let team = jiraData.team_issues || [];
|
||||
if (hasAssignments) {
|
||||
assigned = assigned.filter(iss => myQueues.some(q => isIssueInQueue(iss, q)));
|
||||
team = team.filter(iss => myQueues.some(q => isIssueInQueue(iss, q)));
|
||||
}
|
||||
|
||||
let inProgressCount = 0;
|
||||
let highPriorityCount = 0;
|
||||
@@ -1039,7 +1107,6 @@ function calculateKpis() {
|
||||
if (elStatHigh) elStatHigh.innerText = highPriorityCount;
|
||||
|
||||
updateProjectCounts();
|
||||
updateQueueCounts();
|
||||
}
|
||||
|
||||
// Drawer open/dicht
|
||||
@@ -1067,7 +1134,6 @@ function updateActiveFilterBadge() {
|
||||
|
||||
let activeCount = 0;
|
||||
if (currentProject) activeCount++;
|
||||
if (currentQueue && currentQueue !== 'MY_QUEUES') activeCount++;
|
||||
if (document.getElementById('filterType')?.value) activeCount++;
|
||||
if (document.getElementById('filterPriority')?.value) activeCount++;
|
||||
const total = getTotalStatusCount();
|
||||
@@ -1233,9 +1299,9 @@ function updateStatusCounts() {
|
||||
return pn.includes(pKey);
|
||||
});
|
||||
|
||||
// Wachtrij filter meenemen indien actief
|
||||
if (currentQueue) {
|
||||
projIssues = projIssues.filter(iss => isIssueInQueue(iss, currentQueue));
|
||||
// Project filter meenemen indien actief
|
||||
if (currentProject) {
|
||||
projIssues = projIssues.filter(iss => isIssueInQueue(iss, currentProject));
|
||||
}
|
||||
|
||||
const projTotalEl = document.getElementById(`projTotal_${pKey}`);
|
||||
@@ -1383,7 +1449,7 @@ function updateStatusDropdownLabel() {
|
||||
|
||||
const btnReset = document.getElementById('btnResetFiltersInline');
|
||||
if (btnReset) {
|
||||
const isCustom = selected < total || currentProject !== '' || (currentQueue !== '' && currentQueue !== 'MY_QUEUES') || document.getElementById('jiraSearchInput')?.value;
|
||||
const isCustom = selected < total || currentProject !== '' || document.getElementById('jiraSearchInput')?.value;
|
||||
if (isCustom) btnReset.classList.remove('hidden');
|
||||
else btnReset.classList.add('hidden');
|
||||
}
|
||||
@@ -1480,6 +1546,10 @@ function updateProjectPills() {
|
||||
p.className = isSelected
|
||||
? "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 if (pKey === 'DX') {
|
||||
p.className = isSelected
|
||||
? "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 {
|
||||
p.className = isSelected
|
||||
? "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"
|
||||
@@ -1742,22 +1812,11 @@ function isIssueInQueue(iss, qCode) {
|
||||
}
|
||||
|
||||
function selectQueueFilter(code) {
|
||||
currentQueue = code;
|
||||
updateQueuePills();
|
||||
applyFilters();
|
||||
selectProject(code);
|
||||
}
|
||||
|
||||
function updateQueuePills() {
|
||||
const pills = document.querySelectorAll('.queue-pill');
|
||||
pills.forEach(p => {
|
||||
const qCode = p.getAttribute('data-queue') || '';
|
||||
const isSelected = (qCode === currentQueue);
|
||||
if (isSelected) {
|
||||
p.className = "queue-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-sm shadow-indigo-600/20 flex-shrink-0";
|
||||
} else {
|
||||
p.className = "queue-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/80 text-slate-700 dark:text-slate-300 hover:bg-slate-100 dark:hover:bg-slate-700 border border-slate-200/80 dark:border-slate-700/80 flex-shrink-0";
|
||||
}
|
||||
});
|
||||
updateProjectPills();
|
||||
}
|
||||
|
||||
function updateProjectCounts() {
|
||||
@@ -1772,66 +1831,19 @@ function updateProjectCounts() {
|
||||
const elAll = document.getElementById('projCount_all');
|
||||
if (elAll) elAll.innerText = currentList.length;
|
||||
|
||||
['SD', 'IN', 'AAN', 'GEN'].forEach(key => {
|
||||
const el = document.getElementById(`projCount_${key}`);
|
||||
const pills = document.querySelectorAll('.project-pill');
|
||||
pills.forEach(p => {
|
||||
const pKey = p.getAttribute('data-project');
|
||||
if (!pKey) return;
|
||||
const el = document.getElementById(`projCount_${pKey}`);
|
||||
if (el) {
|
||||
const cnt = currentList.filter(iss => {
|
||||
const pk = getIssueProjectKey(iss);
|
||||
const pn = (iss.project || '').toLowerCase().trim();
|
||||
if (pk) return pk === key;
|
||||
if (key === 'SD' && pn.includes('servicedesk')) return true;
|
||||
if (key === 'IN' && pn.includes('interface')) return true;
|
||||
if (key === 'AAN' && pn.includes('aanvraag')) return true;
|
||||
if (key === 'GEN' && (pn.includes('general') || pn.includes('algemeen'))) return true;
|
||||
return false;
|
||||
}).length;
|
||||
el.innerText = cnt;
|
||||
el.innerText = currentList.filter(iss => isIssueInQueue(iss, pKey)).length;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateQueueCounts() {
|
||||
const all = getDeduplicatedIssues();
|
||||
const total = getTotalStatusCount();
|
||||
let currentList = all;
|
||||
|
||||
if (selectedStatuses.size > 0 && selectedStatuses.size < total) {
|
||||
currentList = currentList.filter(iss => isIssueStatusSelected(iss));
|
||||
}
|
||||
|
||||
if (currentProject) {
|
||||
const pNorm = currentProject.trim().toUpperCase();
|
||||
currentList = currentList.filter(iss => {
|
||||
const pk = getIssueProjectKey(iss);
|
||||
const pn = (iss.project || '').trim().toLowerCase();
|
||||
if (pk) return pk === pNorm;
|
||||
return pn.includes(pNorm.toLowerCase());
|
||||
});
|
||||
}
|
||||
|
||||
const elAll = document.getElementById('queueCount_all');
|
||||
if (elAll) elAll.innerText = currentList.length;
|
||||
|
||||
const elMy = document.getElementById('queueCount_MY_QUEUES');
|
||||
if (elMy) {
|
||||
elMy.innerText = currentList.filter(iss => isIssueInQueue(iss, 'MY_QUEUES')).length;
|
||||
}
|
||||
|
||||
['SD', 'IN', 'AAN', 'GEN', 'DX'].forEach(code => {
|
||||
const el = document.getElementById(`queueCount_${code}`);
|
||||
if (el) {
|
||||
el.innerText = currentList.filter(iss => isIssueInQueue(iss, code)).length;
|
||||
}
|
||||
});
|
||||
|
||||
(jiraData.queue_filters || []).forEach(f => {
|
||||
if (!['SD', 'IN', 'AAN', 'GEN', 'DX'].includes(f.filter_code)) {
|
||||
const el = document.getElementById(`queueCount_${f.filter_code}`);
|
||||
if (el) {
|
||||
el.innerText = currentList.filter(iss => isIssueInQueue(iss, f.filter_code)).length;
|
||||
}
|
||||
}
|
||||
});
|
||||
updateProjectCounts();
|
||||
}
|
||||
|
||||
function filterByMyTickets() {
|
||||
@@ -1914,24 +1926,9 @@ function applyFilters() {
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Project Filter (SD, IN, AAN, GEN of ander)
|
||||
// 2. Project Filter (SD, GEN, IN, AAN, DX of ander)
|
||||
if (projectVal) {
|
||||
if (issPKey) {
|
||||
if (issPKey !== projectVal) return false;
|
||||
} else {
|
||||
let matchesProj = false;
|
||||
if (projectVal === 'SD' && issPName.includes('servicedesk')) matchesProj = true;
|
||||
else if (projectVal === 'IN' && issPName.includes('interface')) matchesProj = true;
|
||||
else if (projectVal === 'AAN' && issPName.includes('aanvraag')) matchesProj = true;
|
||||
else if (projectVal === 'GEN' && (issPName.includes('general') || issPName.includes('algemeen'))) matchesProj = true;
|
||||
else if (issPName === projectVal.toLowerCase()) matchesProj = true;
|
||||
if (!matchesProj) return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Wachtrijfilter (MY_QUEUES, SD, IN, AAN, GEN, DX)
|
||||
if (currentQueue) {
|
||||
if (!isIssueInQueue(iss, currentQueue)) {
|
||||
if (!isIssueInQueue(iss, projectVal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2835,10 +2832,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
initStatusSelection();
|
||||
calculateKpis();
|
||||
updateProjectPills();
|
||||
updateQueueCounts();
|
||||
if (currentQueue) {
|
||||
updateQueuePills();
|
||||
}
|
||||
applyFilters();
|
||||
|
||||
// Luister naar live events voor nieuwe/gewijzigde Jira tickets
|
||||
|
||||
Reference in New Issue
Block a user