Compare commits
2 Commits
a038d25489
...
1ad383d27b
| Author | SHA1 | Date | |
|---|---|---|---|
| 1ad383d27b | |||
| 8498d82e10 |
@@ -202,6 +202,20 @@ foreach ($usedProjectKeys as $uKey => $val) {
|
||||
if (empty($activeProjectKeys)) {
|
||||
$activeProjectKeys = array_fill_keys(['SD', 'IN', 'AAN', 'GEN'], true);
|
||||
}
|
||||
if (!empty($userQueueFilters)) {
|
||||
$filteredActiveKeys = [];
|
||||
foreach ($userQueueFilters as $code) {
|
||||
$clean = strtoupper(trim($code));
|
||||
if ($clean === 'DX') {
|
||||
$filteredActiveKeys['SD'] = true;
|
||||
} elseif ($clean !== 'MY_QUEUES') {
|
||||
$filteredActiveKeys[$clean] = true;
|
||||
}
|
||||
}
|
||||
if (!empty($filteredActiveKeys)) {
|
||||
$activeProjectKeys = $filteredActiveKeys;
|
||||
}
|
||||
}
|
||||
|
||||
// Haal projectstatussen op uit jiraData
|
||||
$rawProjectStatuses = $jiraData['project_statuses'] ?? [];
|
||||
@@ -587,54 +601,18 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rij 3: Status Multi-select Dropdown & Zoekveld -->
|
||||
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 relative z-30">
|
||||
<!-- Rij 2: Status Filters PER Project (Direct onder de projectknoppen) & Zoekveld -->
|
||||
<div class="flex flex-col md:flex-row md:items-center justify-between gap-3 pt-0.5 relative z-30">
|
||||
|
||||
<!-- Linkerkant: Statussen multi-select dropdown -->
|
||||
<!-- Linkerkant: Status Dropdowns PER Project -->
|
||||
<div class="flex flex-wrap items-center gap-2 relative z-50">
|
||||
<div class="relative z-50" id="statusDropdownWrapper">
|
||||
<button
|
||||
type="button"
|
||||
onclick="toggleStatusDropdown()"
|
||||
id="statusDropdownBtn"
|
||||
class="flex items-center space-x-2 px-3.5 py-2 rounded-xl text-xs font-semibold bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 hover:border-slate-300 dark:hover:border-slate-600 text-slate-800 dark:text-slate-200 transition cursor-pointer shadow-2xs"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i data-lucide="check-square" class="w-3.5 h-3.5 text-indigo-500"></i>
|
||||
<span>Status:</span>
|
||||
<span id="statusDropdownLabel" class="font-bold text-indigo-600 dark:text-indigo-400">Alle statussen</span>
|
||||
<span id="statusDropdownBadge" class="hidden px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-indigo-100 dark:bg-indigo-950/60 text-indigo-700 dark:text-indigo-300"></span>
|
||||
<i data-lucide="chevron-down" id="statusDropdownChevron" class="w-3.5 h-3.5 text-slate-400 transition-transform duration-200"></i>
|
||||
</button>
|
||||
|
||||
<!-- Dropdown Menu met Checkboxes (Gegroepeerd per project) -->
|
||||
<div
|
||||
id="statusDropdownMenu"
|
||||
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">
|
||||
<span class="font-bold text-slate-900 dark:text-white text-[11px] uppercase tracking-wider">Statussen per Project</span>
|
||||
<div class="flex items-center space-x-1.5">
|
||||
<button type="button" onclick="selectAllStatuses(true)" class="px-2 py-0.5 rounded-md text-[10px] font-bold text-indigo-600 dark:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-950/60 transition cursor-pointer">
|
||||
Alles
|
||||
</button>
|
||||
<span class="text-slate-300 dark:text-slate-700">•</span>
|
||||
<button type="button" onclick="selectActiveStatusesOnly()" class="px-2 py-0.5 rounded-md text-[10px] font-bold text-emerald-600 dark:text-emerald-400 hover:bg-emerald-50 dark:hover:bg-emerald-950/60 transition cursor-pointer" title="Alleen actieve taken (geen gesloten)">
|
||||
Actief
|
||||
</button>
|
||||
<span class="text-slate-300 dark:text-slate-700">•</span>
|
||||
<button type="button" onclick="selectAllStatuses(false)" class="px-2 py-0.5 rounded-md text-[10px] font-bold text-slate-500 hover:bg-slate-100 dark:hover:bg-slate-800 transition cursor-pointer">
|
||||
Geen
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1.5 text-slate-400 dark:text-slate-500 mr-0.5 flex-shrink-0">
|
||||
<i data-lucide="filter" class="w-3.5 h-3.5 text-indigo-500"></i>
|
||||
<span class="text-[11px] font-bold uppercase tracking-wider">Status:</span>
|
||||
</div>
|
||||
|
||||
<!-- Status Checkboxes List (scrollable, gegroepeerd per project) -->
|
||||
<div class="max-h-80 overflow-y-auto space-y-2.5 pr-1 custom-scrollbar" id="statusCheckboxList">
|
||||
<!-- Dynamisch gevuld via renderStatusCheckboxes() -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- Hier worden de status dropdowns per project dynamisch ingeladen -->
|
||||
<div id="projectStatusDropdownsWrapper" class="flex flex-wrap items-center gap-2">
|
||||
</div>
|
||||
|
||||
<!-- Actieve filters reset knop als er iets gefilterd is -->
|
||||
@@ -645,7 +623,7 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
|
||||
</div>
|
||||
|
||||
<!-- Rechterkant: Zoekveld -->
|
||||
<div class="relative w-full sm:w-64">
|
||||
<div class="relative w-full md:w-64 flex-shrink-0">
|
||||
<i data-lucide="search" class="w-3.5 h-3.5 text-slate-400 absolute left-2.5 top-1/2 -translate-y-1/2 pointer-events-none"></i>
|
||||
<input
|
||||
type="text"
|
||||
@@ -1148,7 +1126,7 @@ function updateActiveFilterBadge() {
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// STATUS MULTI-SELECT CHECKBOX DROPDOWN LOGICA (GEGROEPEERD PER PROJECT)
|
||||
// STATUS MULTI-SELECT CHECKBOX DROPDOWN LOGICA (PER PROJECT DROPDOWNS)
|
||||
// =========================================================================
|
||||
|
||||
function getTotalStatusCount() {
|
||||
@@ -1175,63 +1153,48 @@ function isIssueStatusSelected(iss) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Renders the per-project status dropdowns into #projectStatusDropdownsWrapper
|
||||
function renderStatusCheckboxes() {
|
||||
const container = document.getElementById('statusCheckboxList');
|
||||
const container = document.getElementById('projectStatusDropdownsWrapper');
|
||||
if (!container) return;
|
||||
|
||||
if (!projectStatusesData || projectStatusesData.length === 0) {
|
||||
container.innerHTML = '<div class="py-2 text-center text-slate-400 italic text-[11px]">Geen project statussen gevonden</div>';
|
||||
container.innerHTML = '<span class="text-slate-400 italic text-[11px]">Geen project statussen gevonden</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
const colorClasses = {
|
||||
sky: {
|
||||
badge: 'bg-sky-100 dark:bg-sky-900/60 text-sky-800 dark:text-sky-200 border border-sky-300 dark:border-sky-800',
|
||||
hdrBg: 'bg-sky-50/60 dark:bg-sky-950/30'
|
||||
badge: 'bg-sky-100 text-sky-800 dark:bg-sky-950/70 dark:text-sky-300 border border-sky-300 dark:border-sky-800',
|
||||
hdrBg: 'bg-sky-50/70 dark:bg-sky-950/40'
|
||||
},
|
||||
amber: {
|
||||
badge: 'bg-amber-100 dark:bg-amber-900/60 text-amber-800 dark:text-amber-200 border border-amber-300 dark:border-amber-800',
|
||||
hdrBg: 'bg-amber-50/60 dark:bg-amber-950/30'
|
||||
badge: 'bg-amber-100 text-amber-800 dark:bg-amber-950/70 dark:text-amber-300 border border-amber-300 dark:border-amber-800',
|
||||
hdrBg: 'bg-amber-50/70 dark:bg-amber-950/40'
|
||||
},
|
||||
emerald: {
|
||||
badge: 'bg-emerald-100 dark:bg-emerald-900/60 text-emerald-800 dark:text-emerald-200 border border-emerald-300 dark:border-emerald-800',
|
||||
hdrBg: 'bg-emerald-50/60 dark:bg-emerald-950/30'
|
||||
badge: 'bg-emerald-100 text-emerald-800 dark:bg-emerald-950/70 dark:text-emerald-300 border border-emerald-300 dark:border-emerald-800',
|
||||
hdrBg: 'bg-emerald-50/70 dark:bg-emerald-950/40'
|
||||
},
|
||||
indigo: {
|
||||
badge: 'bg-indigo-100 dark:bg-indigo-900/60 text-indigo-800 dark:text-indigo-200 border border-indigo-300 dark:border-indigo-800',
|
||||
hdrBg: 'bg-indigo-50/60 dark:bg-indigo-950/30'
|
||||
badge: 'bg-indigo-100 text-indigo-800 dark:bg-indigo-950/70 dark:text-indigo-300 border border-indigo-300 dark:border-indigo-800',
|
||||
hdrBg: 'bg-indigo-50/70 dark:bg-indigo-950/40'
|
||||
},
|
||||
slate: {
|
||||
badge: 'bg-slate-100 dark:bg-slate-800 text-slate-800 dark:text-slate-200 border border-slate-300 dark:border-slate-700',
|
||||
badge: 'bg-slate-100 text-slate-800 dark:bg-slate-800 dark:text-slate-200 border border-slate-300 dark:border-slate-700',
|
||||
hdrBg: 'bg-slate-50 dark:bg-slate-800/40'
|
||||
}
|
||||
};
|
||||
|
||||
let html = '';
|
||||
projectStatusesData.forEach((proj, pIdx) => {
|
||||
const pKey = (proj.key || '').toUpperCase();
|
||||
const pKey = (proj.key || '').toUpperCase().trim();
|
||||
const pKeyLower = pKey.toLowerCase();
|
||||
const pColor = proj.color || 'slate';
|
||||
const styles = colorClasses[pColor] || colorClasses.slate;
|
||||
const statuses = proj.statuses || [];
|
||||
|
||||
html += `
|
||||
<div class="project-status-group pb-2 mb-2 border-b border-slate-100 dark:border-slate-800/80 last:border-b-0 last:pb-0 last:mb-0">
|
||||
<div class="flex items-center justify-between px-2 py-1.5 mb-1.5 rounded-lg ${styles.hdrBg}">
|
||||
<div class="flex items-center space-x-2 min-w-0">
|
||||
<span class="px-1.5 py-0.5 rounded font-mono text-[10px] font-black ${styles.badge}">${escapeHtml(pKey)}</span>
|
||||
<span class="font-bold text-slate-800 dark:text-slate-200 text-xs truncate">${escapeHtml(proj.name || pKey)}</span>
|
||||
<span id="projTotal_${pKey}" class="text-[10px] text-slate-400 font-bold">(0)</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1.5 text-[10px] flex-shrink-0">
|
||||
<button type="button" onclick="selectProjectStatuses('${escapeHtml(pKey)}', true)" class="px-1.5 py-0.5 rounded font-semibold text-indigo-600 dark:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-950/60 transition cursor-pointer">Alles</button>
|
||||
<span class="text-slate-300 dark:text-slate-700">•</span>
|
||||
<button type="button" onclick="selectProjectStatuses('${escapeHtml(pKey)}', false)" class="px-1.5 py-0.5 rounded font-semibold text-slate-500 hover:bg-slate-200 dark:hover:bg-slate-700 transition cursor-pointer">Geen</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-0.5 pl-1">
|
||||
`;
|
||||
|
||||
let statusRowsHtml = '';
|
||||
statuses.forEach((st, sIdx) => {
|
||||
const sNorm = (st.name || '').toLowerCase().trim();
|
||||
const compoundVal = `${pKeyLower}::${sNorm}`;
|
||||
@@ -1253,10 +1216,10 @@ function renderStatusCheckboxes() {
|
||||
}
|
||||
|
||||
const safeKey = `${pKey}_${sNorm.replace(/[^a-z0-9]/gi, '_')}`;
|
||||
const safeId = `chkStatus_${pIdx}_${sIdx}`;
|
||||
const safeId = `chkStatus_${pKey}_${sIdx}`;
|
||||
|
||||
html += `
|
||||
<label for="${safeId}" class="flex items-center justify-between px-2 py-1.5 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800/70 cursor-pointer transition select-none group">
|
||||
statusRowsHtml += `
|
||||
<label for="${safeId}" class="flex items-center justify-between px-2.5 py-1.5 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800/70 cursor-pointer transition select-none group">
|
||||
<div class="flex items-center space-x-2.5 min-w-0 flex-1">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -1266,8 +1229,8 @@ function renderStatusCheckboxes() {
|
||||
data-status="${escapeHtml(sNorm)}"
|
||||
data-is-done="${isDone ? 1 : 0}"
|
||||
${isChecked ? 'checked' : ''}
|
||||
onchange="onStatusCheckboxChange()"
|
||||
class="status-checkbox w-4 h-4 rounded text-indigo-600 border-slate-300 dark:border-slate-700 focus:ring-indigo-500 cursor-pointer flex-shrink-0"
|
||||
onchange="onStatusCheckboxChange('${escapeHtml(pKey)}')"
|
||||
class="status-checkbox status-checkbox-${escapeHtml(pKey)} w-4 h-4 rounded text-indigo-600 border-slate-300 dark:border-slate-700 focus:ring-indigo-500 cursor-pointer flex-shrink-0"
|
||||
>
|
||||
<span class="w-2 h-2 rounded-full ${dotColor} flex-shrink-0"></span>
|
||||
<span class="font-semibold text-slate-700 dark:text-slate-300 truncate text-xs group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition">${escapeHtml(st.name)}</span>
|
||||
@@ -1278,13 +1241,132 @@ function renderStatusCheckboxes() {
|
||||
});
|
||||
|
||||
html += `
|
||||
<div class="relative project-status-dropdown-item" data-project="${escapeHtml(pKey)}" id="statusDropdownWrapper_${escapeHtml(pKey)}">
|
||||
<button
|
||||
type="button"
|
||||
onclick="toggleProjectStatusDropdown('${escapeHtml(pKey)}')"
|
||||
id="statusDropdownBtn_${escapeHtml(pKey)}"
|
||||
class="flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-semibold bg-white dark:bg-slate-900 border border-slate-200 dark:border-slate-700 hover:border-slate-300 dark:hover:border-slate-600 text-slate-800 dark:text-slate-200 transition cursor-pointer shadow-2xs flex-shrink-0"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<span class="px-1.5 py-0.5 rounded font-mono text-[10px] font-black ${styles.badge}">${escapeHtml(pKey)}</span>
|
||||
<span id="statusDropdownLabel_${escapeHtml(pKey)}" class="font-bold text-slate-700 dark:text-slate-300 truncate max-w-[130px]">Alle</span>
|
||||
<span id="statusDropdownBadge_${escapeHtml(pKey)}" class="px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-slate-100 dark:bg-slate-800 text-slate-600 dark:text-slate-300"></span>
|
||||
<i data-lucide="chevron-down" id="statusDropdownChevron_${escapeHtml(pKey)}" class="w-3.5 h-3.5 text-slate-400 transition-transform duration-200 flex-shrink-0"></i>
|
||||
</button>
|
||||
|
||||
<div
|
||||
id="statusDropdownMenu_${escapeHtml(pKey)}"
|
||||
class="hidden absolute left-0 top-full mt-1.5 w-72 sm:w-80 max-w-[calc(100vw-2.5rem)] 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"
|
||||
>
|
||||
<div class="flex items-center justify-between pb-2 border-b border-slate-100 dark:border-slate-800 ${styles.hdrBg} -mx-3.5 -mt-3 p-3 rounded-t-2xl">
|
||||
<div class="flex items-center space-x-1.5 min-w-0">
|
||||
<span class="px-1.5 py-0.5 rounded font-mono text-[10px] font-black ${styles.badge}">${escapeHtml(pKey)}</span>
|
||||
<span class="font-bold text-slate-900 dark:text-white text-xs truncate">${escapeHtml(proj.name || pKey)}</span>
|
||||
<span id="projTotal_${escapeHtml(pKey)}" class="text-[10px] text-slate-400 font-bold">(0)</span>
|
||||
</div>
|
||||
<div class="flex items-center space-x-1 text-[10px] flex-shrink-0">
|
||||
<button type="button" onclick="selectProjectStatuses('${escapeHtml(pKey)}', true)" class="px-1.5 py-0.5 rounded font-bold text-indigo-600 dark:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-950/60 transition cursor-pointer">
|
||||
Alles
|
||||
</button>
|
||||
<span class="text-slate-300 dark:text-slate-700">•</span>
|
||||
<button type="button" onclick="selectProjectActiveStatuses('${escapeHtml(pKey)}')" class="px-1.5 py-0.5 rounded font-bold text-emerald-600 dark:text-emerald-400 hover:bg-emerald-50 dark:hover:bg-emerald-950/60 transition cursor-pointer" title="Alleen actieve taken">
|
||||
Actief
|
||||
</button>
|
||||
<span class="text-slate-300 dark:text-slate-700">•</span>
|
||||
<button type="button" onclick="selectProjectStatuses('${escapeHtml(pKey)}', false)" class="px-1.5 py-0.5 rounded font-bold text-slate-500 hover:bg-slate-200 dark:hover:bg-slate-700 transition cursor-pointer">
|
||||
Geen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="max-h-60 overflow-y-auto space-y-1 pr-1 custom-scrollbar" id="statusCheckboxList_${escapeHtml(pKey)}">
|
||||
${statusRowsHtml}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
container.innerHTML = html;
|
||||
if (typeof lucide !== 'undefined') lucide.createIcons();
|
||||
updateStatusCounts();
|
||||
updateStatusDropdownLabel();
|
||||
updateProjectStatusDropdownVisibility();
|
||||
}
|
||||
|
||||
function updateProjectStatusDropdownVisibility() {
|
||||
const items = document.querySelectorAll('.project-status-dropdown-item');
|
||||
const curNorm = (currentProject || '').toUpperCase().trim();
|
||||
items.forEach(item => {
|
||||
const pKey = (item.getAttribute('data-project') || '').toUpperCase().trim();
|
||||
if (!curNorm) {
|
||||
// "Alle Projecten": toon alle toegewezen project dropdowns
|
||||
item.classList.remove('hidden');
|
||||
} else if (curNorm === pKey) {
|
||||
item.classList.remove('hidden');
|
||||
} else if (curNorm === 'DX' && pKey === 'SD') {
|
||||
item.classList.remove('hidden');
|
||||
} else {
|
||||
item.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleProjectStatusDropdown(projectKey, force) {
|
||||
const targetKey = (projectKey || '').toUpperCase().trim();
|
||||
const menu = document.getElementById(`statusDropdownMenu_${targetKey}`);
|
||||
const chevron = document.getElementById(`statusDropdownChevron_${targetKey}`);
|
||||
const btn = document.getElementById(`statusDropdownBtn_${targetKey}`);
|
||||
if (!menu) return;
|
||||
|
||||
const willOpen = (typeof force === 'boolean') ? force : menu.classList.contains('hidden');
|
||||
|
||||
// Sluit alle andere geopende status dropdowns
|
||||
closeAllProjectStatusDropdowns(targetKey);
|
||||
|
||||
if (willOpen) {
|
||||
menu.classList.remove('hidden');
|
||||
if (chevron) chevron.classList.add('rotate-180');
|
||||
if (btn) btn.setAttribute('aria-expanded', 'true');
|
||||
updateStatusCounts();
|
||||
} else {
|
||||
menu.classList.add('hidden');
|
||||
if (chevron) chevron.classList.remove('rotate-180');
|
||||
if (btn) btn.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
}
|
||||
|
||||
function closeAllProjectStatusDropdowns(exceptKey = '') {
|
||||
const exceptNorm = (exceptKey || '').toUpperCase().trim();
|
||||
(projectStatusesData || []).forEach(proj => {
|
||||
const pKey = (proj.key || '').toUpperCase().trim();
|
||||
if (pKey === exceptNorm) return;
|
||||
const menu = document.getElementById(`statusDropdownMenu_${pKey}`);
|
||||
const chevron = document.getElementById(`statusDropdownChevron_${pKey}`);
|
||||
const btn = document.getElementById(`statusDropdownBtn_${pKey}`);
|
||||
if (menu && !menu.classList.contains('hidden')) {
|
||||
menu.classList.add('hidden');
|
||||
if (chevron) chevron.classList.remove('rotate-180');
|
||||
if (btn) btn.setAttribute('aria-expanded', 'false');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Sluit dropdowns bij klikken buiten dropdowns
|
||||
document.addEventListener('click', (e) => {
|
||||
const isInside = e.target.closest('.project-status-dropdown-item');
|
||||
if (!isInside) {
|
||||
closeAllProjectStatusDropdowns();
|
||||
}
|
||||
});
|
||||
|
||||
function toggleStatusDropdown(force) {
|
||||
if (currentProject) {
|
||||
toggleProjectStatusDropdown(currentProject, force);
|
||||
} else if (projectStatusesData && projectStatusesData.length > 0) {
|
||||
toggleProjectStatusDropdown(projectStatusesData[0].key, force);
|
||||
}
|
||||
}
|
||||
|
||||
function updateStatusCounts() {
|
||||
@@ -1321,37 +1403,18 @@ function updateStatusCounts() {
|
||||
});
|
||||
}
|
||||
|
||||
function toggleStatusDropdown(force) {
|
||||
const menu = document.getElementById('statusDropdownMenu');
|
||||
const chevron = document.getElementById('statusDropdownChevron');
|
||||
const btn = document.getElementById('statusDropdownBtn');
|
||||
if (!menu) return;
|
||||
function onStatusCheckboxChange(projectKey) {
|
||||
const pNorm = (projectKey || '').toUpperCase().trim();
|
||||
const pKeyLower = pNorm.toLowerCase();
|
||||
const chks = document.querySelectorAll(`.status-checkbox-${pNorm}`);
|
||||
|
||||
const willOpen = (typeof force === 'boolean') ? force : menu.classList.contains('hidden');
|
||||
if (willOpen) {
|
||||
menu.classList.remove('hidden');
|
||||
if (chevron) chevron.classList.add('rotate-180');
|
||||
if (btn) btn.setAttribute('aria-expanded', 'true');
|
||||
updateStatusCounts();
|
||||
chks.forEach(chk => {
|
||||
const val = chk.value.toLowerCase().trim();
|
||||
if (chk.checked) {
|
||||
selectedStatuses.add(val);
|
||||
} else {
|
||||
menu.classList.add('hidden');
|
||||
if (chevron) chevron.classList.remove('rotate-180');
|
||||
if (btn) btn.setAttribute('aria-expanded', 'false');
|
||||
selectedStatuses.delete(val);
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('click', (e) => {
|
||||
const wrapper = document.getElementById('statusDropdownWrapper');
|
||||
if (wrapper && !wrapper.contains(e.target)) {
|
||||
toggleStatusDropdown(false);
|
||||
}
|
||||
});
|
||||
|
||||
function onStatusCheckboxChange() {
|
||||
selectedStatuses.clear();
|
||||
const checked = document.querySelectorAll('.status-checkbox:checked');
|
||||
checked.forEach(chk => {
|
||||
if (chk.value) selectedStatuses.add(chk.value.toLowerCase().trim());
|
||||
});
|
||||
|
||||
try {
|
||||
@@ -1364,7 +1427,9 @@ function onStatusCheckboxChange() {
|
||||
|
||||
function selectProjectStatuses(projectKey, selectAll) {
|
||||
const targetKey = (projectKey || '').toUpperCase().trim();
|
||||
const pKeyLower = targetKey.toLowerCase();
|
||||
const chks = document.querySelectorAll(`.status-checkbox[data-project="${targetKey}"]`);
|
||||
|
||||
chks.forEach(chk => {
|
||||
chk.checked = !!selectAll;
|
||||
const val = chk.value.toLowerCase().trim();
|
||||
@@ -1383,6 +1448,29 @@ function selectProjectStatuses(projectKey, selectAll) {
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
function selectProjectActiveStatuses(projectKey) {
|
||||
const targetKey = (projectKey || '').toUpperCase().trim();
|
||||
const chks = document.querySelectorAll(`.status-checkbox[data-project="${targetKey}"]`);
|
||||
|
||||
chks.forEach(chk => {
|
||||
const isDone = chk.getAttribute('data-is-done') === '1';
|
||||
chk.checked = !isDone;
|
||||
const val = chk.value.toLowerCase().trim();
|
||||
if (!isDone) {
|
||||
selectedStatuses.add(val);
|
||||
} else {
|
||||
selectedStatuses.delete(val);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
localStorage.setItem('fociz_jira_selected_statuses', JSON.stringify([...selectedStatuses]));
|
||||
} catch (e) {}
|
||||
|
||||
updateStatusDropdownLabel();
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
function selectAllStatuses(selectAll) {
|
||||
selectedStatuses.clear();
|
||||
const chks = document.querySelectorAll('.status-checkbox');
|
||||
@@ -1401,55 +1489,78 @@ function selectAllStatuses(selectAll) {
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
function selectActiveStatusesOnly() {
|
||||
selectedStatuses.clear();
|
||||
const chks = document.querySelectorAll('.status-checkbox');
|
||||
chks.forEach(chk => {
|
||||
const isDone = chk.getAttribute('data-is-done') === '1';
|
||||
chk.checked = !isDone;
|
||||
if (!isDone && chk.value) {
|
||||
selectedStatuses.add(chk.value.toLowerCase().trim());
|
||||
function updateStatusDropdownLabel() {
|
||||
let hasAnyCustomFilter = false;
|
||||
|
||||
(projectStatusesData || []).forEach(proj => {
|
||||
const pKey = (proj.key || '').toUpperCase().trim();
|
||||
const pKeyLower = pKey.toLowerCase();
|
||||
const labelEl = document.getElementById(`statusDropdownLabel_${pKey}`);
|
||||
const badgeEl = document.getElementById(`statusDropdownBadge_${pKey}`);
|
||||
if (!labelEl) return;
|
||||
|
||||
const statuses = proj.statuses || [];
|
||||
const total = statuses.length;
|
||||
let selectedCount = 0;
|
||||
let activeTotal = 0;
|
||||
let activeSelected = 0;
|
||||
let singleStatusName = '';
|
||||
|
||||
statuses.forEach(st => {
|
||||
const sNorm = (st.name || '').toLowerCase().trim();
|
||||
const compound = `${pKeyLower}::${sNorm}`;
|
||||
const cat = (st.status_category || '').toLowerCase().trim();
|
||||
const isDone = (cat === 'done' || isDoneStatus(sNorm));
|
||||
|
||||
if (!isDone) activeTotal++;
|
||||
|
||||
if (selectedStatuses.has(compound) || selectedStatuses.has(sNorm)) {
|
||||
selectedCount++;
|
||||
singleStatusName = st.name;
|
||||
if (!isDone) activeSelected++;
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
localStorage.setItem('fociz_jira_selected_statuses', JSON.stringify([...selectedStatuses]));
|
||||
} catch (e) {}
|
||||
if (selectedCount < total) {
|
||||
hasAnyCustomFilter = true;
|
||||
}
|
||||
|
||||
updateStatusDropdownLabel();
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
function updateStatusDropdownLabel() {
|
||||
const labelEl = document.getElementById('statusDropdownLabel');
|
||||
const badgeEl = document.getElementById('statusDropdownBadge');
|
||||
if (!labelEl) return;
|
||||
|
||||
const total = getTotalStatusCount();
|
||||
const selected = selectedStatuses.size;
|
||||
|
||||
if (selected === total || (total === 0 && selected === 0)) {
|
||||
if (selectedCount === total || (total === 0 && selectedCount === 0)) {
|
||||
labelEl.innerText = "Alle statussen";
|
||||
if (badgeEl) badgeEl.classList.add('hidden');
|
||||
} else if (selected === 0) {
|
||||
labelEl.innerText = "Geen geselecteerd";
|
||||
if (badgeEl) {
|
||||
badgeEl.innerText = total;
|
||||
badgeEl.className = "px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-slate-100 dark:bg-slate-800 text-slate-500 dark:text-slate-400";
|
||||
}
|
||||
} else if (selectedCount === 0) {
|
||||
labelEl.innerText = "Geen";
|
||||
if (badgeEl) {
|
||||
badgeEl.innerText = "0";
|
||||
badgeEl.classList.remove('hidden');
|
||||
badgeEl.className = "px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-rose-100 dark:bg-rose-950/60 text-rose-700 dark:text-rose-300";
|
||||
}
|
||||
} else {
|
||||
labelEl.innerText = `${selected} statussen`;
|
||||
} else if (selectedCount === activeTotal && activeSelected === activeTotal && activeTotal > 0) {
|
||||
labelEl.innerText = "Actief";
|
||||
if (badgeEl) {
|
||||
badgeEl.innerText = `${selected}/${total}`;
|
||||
badgeEl.classList.remove('hidden');
|
||||
badgeEl.innerText = `${activeSelected}/${total}`;
|
||||
badgeEl.className = "px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-emerald-100 dark:bg-emerald-950/60 text-emerald-700 dark:text-emerald-300";
|
||||
}
|
||||
} else if (selectedCount === 1) {
|
||||
labelEl.innerText = singleStatusName;
|
||||
if (badgeEl) {
|
||||
badgeEl.innerText = `1/${total}`;
|
||||
badgeEl.className = "px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-indigo-100 dark:bg-indigo-950/60 text-indigo-700 dark:text-indigo-300";
|
||||
}
|
||||
} else {
|
||||
labelEl.innerText = `${selectedCount} statussen`;
|
||||
if (badgeEl) {
|
||||
badgeEl.innerText = `${selectedCount}/${total}`;
|
||||
badgeEl.className = "px-1.5 py-0.5 rounded-full text-[10px] font-bold bg-indigo-100 dark:bg-indigo-950/60 text-indigo-700 dark:text-indigo-300";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const btnReset = document.getElementById('btnResetFiltersInline');
|
||||
if (btnReset) {
|
||||
const isCustom = selected < total || currentProject !== '' || document.getElementById('jiraSearchInput')?.value;
|
||||
const isCustom = hasAnyCustomFilter || currentProject !== '' || document.getElementById('jiraSearchInput')?.value;
|
||||
if (isCustom) btnReset.classList.remove('hidden');
|
||||
else btnReset.classList.add('hidden');
|
||||
}
|
||||
@@ -1465,16 +1576,16 @@ function initStatusSelection() {
|
||||
|
||||
let validSaved = false;
|
||||
if (Array.isArray(saved) && saved.length > 0) {
|
||||
const hasCompound = saved.some(s => String(s).includes('::'));
|
||||
if (hasCompound) {
|
||||
saved.forEach(s => {
|
||||
if (s) selectedStatuses.add(String(s).toLowerCase().trim());
|
||||
});
|
||||
if (selectedStatuses.size > 0) {
|
||||
validSaved = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!validSaved || selectedStatuses.size === 0) {
|
||||
selectedStatuses.clear();
|
||||
if (!validSaved) {
|
||||
// Standaard alle actieve statussen selecteren, gesloten uit conform gebruikerswens
|
||||
(projectStatusesData || []).forEach(proj => {
|
||||
const pKeyLower = (proj.key || '').toLowerCase().trim();
|
||||
@@ -1509,13 +1620,15 @@ function initStatusSelection() {
|
||||
}
|
||||
|
||||
renderStatusCheckboxes();
|
||||
updateStatusDropdownLabel();
|
||||
}
|
||||
|
||||
// Project selectie via pill tabs
|
||||
function selectProject(proj) {
|
||||
currentProject = (proj || '').trim();
|
||||
closeAllProjectStatusDropdowns();
|
||||
updateProjectPills();
|
||||
updateProjectStatusDropdownVisibility();
|
||||
updateStatusCounts();
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
@@ -1882,6 +1995,7 @@ function clearSearch() {
|
||||
function resetAllFilters() {
|
||||
currentProject = '';
|
||||
currentQueue = '';
|
||||
closeAllProjectStatusDropdowns();
|
||||
selectAllStatuses(true);
|
||||
|
||||
const searchInput = document.getElementById('jiraSearchInput');
|
||||
@@ -1894,7 +2008,8 @@ function resetAllFilters() {
|
||||
|
||||
document.getElementById('clearSearchBtn')?.classList.add('hidden');
|
||||
updateProjectPills();
|
||||
updateQueuePills();
|
||||
updateProjectStatusDropdownVisibility();
|
||||
updateStatusCounts();
|
||||
applyFilters();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user