Filter changes #86

Merged
NiekRabelink merged 1 commits from dev into main 2026-09-15 14:26:22 +00:00
2 changed files with 239 additions and 36 deletions
+224 -32
View File
@@ -1473,14 +1473,193 @@ function updateProjectPills() {
}); });
} }
// Hulpfunctie: Match een issue tegen een JQL clausule
function matchJqlClause(iss, jql) {
if (!jql || typeof jql !== 'string') return true;
const trimmed = jql.trim();
if (!trimmed) return true;
const issPKey = getIssueProjectKey(iss);
const issPName = (iss.project || '').toLowerCase().trim();
const issOl = (iss.onderwerp_logging || '').toLowerCase().trim();
const issSum = (iss.summary || '').toLowerCase();
const issPriority = (iss.priority || '').toLowerCase().trim();
const issType = (iss.issue_type || '').toLowerCase().trim();
const issStatus = (iss.status || '').toLowerCase().trim();
// Normalisatie helper voor onderwerpteksten (verwijder leestekens, spaties, streepjes voor flexibele matching)
function normalizeText(str) {
if (!str) return '';
return str.toLowerCase().replace(/["']/g, '').replace(/[-_\s]/g, '').trim();
}
// Splits condities op AND (hoofdletterongevoelig)
const conditions = trimmed.split(/\s+AND\s+/i);
for (let rawCond of conditions) {
const cond = rawCond.trim();
if (!cond) continue;
// --- 1. Project condities ---
// project = "SD" of project = SD
const projEqMatch = cond.match(/^project\s*=\s*["']?([A-Z0-9_-]+)["']?$/i);
if (projEqMatch) {
const req = projEqMatch[1].toUpperCase().trim();
if (issPKey !== req && !issPName.includes(req.toLowerCase())) {
return false;
}
continue;
}
// project != "SD" of project != SD
const projNeqMatch = cond.match(/^project\s*!=\s*["']?([A-Z0-9_-]+)["']?$/i);
if (projNeqMatch) {
const notReq = projNeqMatch[1].toUpperCase().trim();
if (issPKey === notReq || issPName.includes(notReq.toLowerCase())) {
return false;
}
continue;
}
// project in ("SD", "IN")
const projInMatch = cond.match(/^project\s+in\s*\(([^)]+)\)$/i);
if (projInMatch) {
const allowed = projInMatch[1].split(',').map(s => s.replace(/["']/g, '').toUpperCase().trim());
if (!allowed.includes(issPKey) && !allowed.some(p => issPName.includes(p.toLowerCase()))) {
return false;
}
continue;
}
// project not in ("SD", "IN")
const projNotInMatch = cond.match(/^project\s+not\s+in\s*\(([^)]+)\)$/i);
if (projNotInMatch) {
const disallowed = projNotInMatch[1].split(',').map(s => s.replace(/["']/g, '').toUpperCase().trim());
if (disallowed.includes(issPKey) || disallowed.some(p => issPName.includes(p.toLowerCase()))) {
return false;
}
continue;
}
// --- 2. Onderwerp logging condities ---
// bv: "Onderwerp logging[dropdown]" in (Koppelingen, SSO-koppeling, Rest-endpoints, Datadump, UWV-koppeling, Migratie)
const olInMatch = cond.match(/["']?(?:onderwerp(?:[\w\s\[\]_-]*)?|customfield_\d+)["']?\s+in\s*\(([^)]+)\)/i);
if (olInMatch) {
const allowedTopics = olInMatch[1].split(',').map(s => normalizeText(s)).filter(Boolean);
const normOl = normalizeText(issOl);
let matched = false;
if (normOl) {
matched = allowedTopics.some(t => normOl === t || normOl.includes(t) || t.includes(normOl));
}
if (!matched && !normOl) {
// Als onderwerp_logging op het ticket leeg is, kijk in de summary
const normSum = normalizeText(issSum);
matched = allowedTopics.some(t => normSum.includes(t));
}
if (!matched) return false;
continue;
}
// "Onderwerp logging[dropdown]" not in (...)
const olNotInMatch = cond.match(/["']?(?:onderwerp(?:[\w\s\[\]_-]*)?|customfield_\d+)["']?\s+not\s+in\s*\(([^)]+)\)/i);
if (olNotInMatch) {
const forbiddenTopics = olNotInMatch[1].split(',').map(s => normalizeText(s)).filter(Boolean);
const normOl = normalizeText(issOl);
if (normOl && forbiddenTopics.some(t => normOl === t || normOl.includes(t) || t.includes(normOl))) {
return false;
}
continue;
}
// "Onderwerp logging[dropdown]" = "..."
const olEqMatch = cond.match(/["']?(?:onderwerp(?:[\w\s\[\]_-]*)?|customfield_\d+)["']?\s*=\s*["']?([^"'\)]+)["']?/i);
if (olEqMatch) {
const reqTopic = normalizeText(olEqMatch[1]);
const normOl = normalizeText(issOl);
const matched = (normOl === reqTopic || normOl.includes(reqTopic) || reqTopic.includes(normOl) || (!normOl && normalizeText(issSum).includes(reqTopic)));
if (!matched) return false;
continue;
}
// "Onderwerp logging[dropdown]" != "..."
const olNeqMatch = cond.match(/["']?(?:onderwerp(?:[\w\s\[\]_-]*)?|customfield_\d+)["']?\s*!=\s*["']?([^"'\)]+)["']?/i);
if (olNeqMatch) {
const notTopic = normalizeText(olNeqMatch[1]);
const normOl = normalizeText(issOl);
if (normOl && (normOl === notTopic || normOl.includes(notTopic))) {
return false;
}
continue;
}
// --- 3. Prioriteit condities ---
const prioInMatch = cond.match(/^priority\s+in\s*\(([^)]+)\)$/i);
if (prioInMatch) {
const allowed = prioInMatch[1].split(',').map(s => s.replace(/["']/g, '').toLowerCase().trim());
if (!allowed.includes(issPriority)) return false;
continue;
}
const prioEqMatch = cond.match(/^priority\s*=\s*["']?([^"']+)["']?$/i);
if (prioEqMatch) {
const req = prioEqMatch[1].toLowerCase().trim();
if (issPriority !== req) return false;
continue;
}
// --- 4. Type condities ---
const typeInMatch = cond.match(/^(?:issuetype|type)\s+in\s*\(([^)]+)\)$/i);
if (typeInMatch) {
const allowed = typeInMatch[1].split(',').map(s => s.replace(/["']/g, '').toLowerCase().trim());
if (!allowed.includes(issType)) return false;
continue;
}
const typeEqMatch = cond.match(/^(?:issuetype|type)\s*=\s*["']?([^"']+)["']?$/i);
if (typeEqMatch) {
const req = typeEqMatch[1].toLowerCase().trim();
if (issType !== req) return false;
continue;
}
// --- 5. StatusCategory conditie ---
if (/statusCategory\s*!=\s*Done/i.test(cond)) {
if (isDoneStatus(issStatus) || (iss.status_category || '').toLowerCase() === 'done') {
return false;
}
continue;
}
if (/statusCategory\s*=\s*Done/i.test(cond)) {
if (!isDoneStatus(issStatus) && (iss.status_category || '').toLowerCase() !== 'done') {
return false;
}
continue;
}
// --- 6. Status condities ---
const statusInMatch = cond.match(/^status\s+in\s*\(([^)]+)\)$/i);
if (statusInMatch) {
const allowed = statusInMatch[1].split(',').map(s => s.replace(/["']/g, '').toLowerCase().trim());
if (!allowed.includes(issStatus)) return false;
continue;
}
const statusEqMatch = cond.match(/^status\s*=\s*["']?([^"']+)["']?$/i);
if (statusEqMatch) {
const req = statusEqMatch[1].toLowerCase().trim();
if (issStatus !== req) return false;
continue;
}
}
return true;
}
// Hulpfunctie: Bepaal of een ticket tot een specifieke wachtrij behoort // Hulpfunctie: Bepaal of een ticket tot een specifieke wachtrij behoort
function isIssueInQueue(iss, qCode) { function isIssueInQueue(iss, qCode) {
if (!qCode) return true; if (!qCode) return true;
const code = qCode.toUpperCase().trim(); const code = qCode.toUpperCase().trim();
const pKey = getIssueProjectKey(iss);
const pName = (iss.project || '').toLowerCase().trim();
const ol = (iss.onderwerp_logging || '').toLowerCase().trim(); const ol = (iss.onderwerp_logging || '').toLowerCase().trim();
const sum = (iss.summary || '').toLowerCase(); const sum = (iss.summary || '').toLowerCase();
const pKey = (iss.project_key || '').toUpperCase().trim();
const pName = (iss.project || '').toLowerCase().trim();
// Multi-queue matching voor de ingelogde gebruiker // Multi-queue matching voor de ingelogde gebruiker
if (code === 'MY_QUEUES') { if (code === 'MY_QUEUES') {
@@ -1489,38 +1668,51 @@ function isIssueInQueue(iss, qCode) {
return myQueues.some(q => isIssueInQueue(iss, q)); return myQueues.some(q => isIssueInQueue(iss, q));
} }
const dxTerms = ['koppeling', 'sso', 'endpoint', 'rest', 'datadump', 'uwv', 'migratie', 'api', 'webhook', 'integrat']; // 1. Zoek geconfigureerd wachtrijfilter in jiraData.queue_filters (database)
const matchesDx = dxTerms.some(t => ol.includes(t) || sum.includes(t));
if (code === 'DX') {
return matchesDx;
}
if (code === 'SD') {
return (pKey === 'SD' || pName.includes('servicedesk') || !matchesDx);
}
if (code === 'IN') {
return (pKey === 'IN' || pName.includes('interface') || matchesDx || ol.includes('interface') || sum.includes('interface'));
}
if (code === 'AAN') {
return (pKey === 'AAN' || pName.includes('aanvraag') || ol.includes('aanvraag') || sum.includes('aanvraag'));
}
if (code === 'GEN') {
return (pKey === 'GEN' || pName.includes('general') || pName.includes('algemeen') || ol.includes('general') || ol.includes('algemeen'));
}
// Aangepaste wachtrijfilter uit de database
const fObj = (jiraData.queue_filters || []).find(f => f.filter_code && f.filter_code.toUpperCase() === code); const fObj = (jiraData.queue_filters || []).find(f => f.filter_code && f.filter_code.toUpperCase() === code);
if (fObj) { if (fObj) {
if (fObj.project_key && pKey && fObj.project_key.toUpperCase() !== pKey) { // Project key validatie indien expliciet in filter ingesteld
return false; if (fObj.project_key) {
const filterPKey = fObj.project_key.toUpperCase().trim();
if (pKey !== filterPKey && !pName.includes(filterPKey.toLowerCase())) {
return false;
}
} }
const clause = (fObj.jql_clause || '').toLowerCase(); // JQL clausule evaluatie
if (ol && (clause.includes(ol) || ol.includes(code.toLowerCase()))) { if (fObj.jql_clause && fObj.jql_clause.trim()) {
return true; return matchJqlClause(iss, fObj.jql_clause);
}
// Als er alleen een project_key was zonder nadere jql_clause:
if (fObj.project_key) {
const filterPKey = fObj.project_key.toUpperCase().trim();
return pKey === filterPKey || pName.includes(filterPKey.toLowerCase());
} }
if (clause.includes('koppeling') && matchesDx) return true;
if (clause.includes(pKey.toLowerCase())) return true;
} }
// 2. Fallbacks voor standaard wachtrijen (als er geen filter in de database staat)
if (code === 'DX') {
const isSd = (pKey === 'SD' || pName.includes('servicedesk'));
if (!isSd) return false;
const dxTerms = ['koppeling', 'sso', 'endpoint', 'rest', 'datadump', 'uwv', 'migratie'];
return dxTerms.some(t => ol.includes(t) || sum.includes(t));
}
if (code === 'SD') {
return (pKey === 'SD' || pName.includes('servicedesk'));
}
if (code === 'IN') {
return (pKey === 'IN' || pName.includes('interface'));
}
if (code === 'AAN') {
return (pKey === 'AAN' || pName.includes('aanvraag'));
}
if (code === 'GEN') {
return (pKey === 'GEN' || pName.includes('general') || pName.includes('algemeen'));
}
return true; return true;
} }
@@ -1561,7 +1753,7 @@ function updateProjectCounts() {
const el = document.getElementById(`projCount_${key}`); const el = document.getElementById(`projCount_${key}`);
if (el) { if (el) {
const cnt = currentList.filter(iss => { const cnt = currentList.filter(iss => {
const pk = (iss.project_key || '').toUpperCase().trim(); const pk = getIssueProjectKey(iss);
const pn = (iss.project || '').toLowerCase().trim(); const pn = (iss.project || '').toLowerCase().trim();
if (pk === key) return true; if (pk === key) return true;
if (key === 'SD' && pn.includes('servicedesk')) return true; if (key === 'SD' && pn.includes('servicedesk')) return true;
@@ -1589,7 +1781,7 @@ function updateQueueCounts() {
if (currentProject) { if (currentProject) {
const pNorm = currentProject.trim().toUpperCase(); const pNorm = currentProject.trim().toUpperCase();
currentList = currentList.filter(iss => { currentList = currentList.filter(iss => {
const pk = (iss.project_key || '').trim().toUpperCase(); const pk = getIssueProjectKey(iss);
const pn = (iss.project || '').trim().toLowerCase(); const pn = (iss.project || '').trim().toLowerCase();
return pk === pNorm || pn.includes(pNorm.toLowerCase()); return pk === pNorm || pn.includes(pNorm.toLowerCase());
}); });
@@ -1689,7 +1881,7 @@ function applyFilters() {
// Filter tickets // Filter tickets
const filtered = all.filter(iss => { const filtered = all.filter(iss => {
const issPKey = (iss.project_key || '').toUpperCase().trim(); const issPKey = getIssueProjectKey(iss);
const issPName = (iss.project || '').toLowerCase().trim(); const issPName = (iss.project || '').toLowerCase().trim();
// 1. Status Filter via Checkboxes // 1. Status Filter via Checkboxes
+15 -4
View File
@@ -4671,15 +4671,26 @@ let activePlannerJiraQueue = ''; // '': Alle, 'my': Mijn, 'DX': DX Queue, 'SD':
function isIssueInQueue(iss, qCode) { function isIssueInQueue(iss, qCode) {
if (!qCode) return true; if (!qCode) return true;
const code = qCode.toUpperCase(); const code = qCode.toUpperCase().trim();
let pKey = (iss.project_key || '').toUpperCase().trim();
if (!pKey && iss.key) {
const parts = iss.key.split('-');
if (parts.length > 0) pKey = parts[0].toUpperCase().trim();
}
const pName = (iss.project || '').toLowerCase().trim();
const isSd = (pKey === 'SD' || pName.includes('servicedesk') || !pKey);
const ol = (iss.onderwerp_logging || '').toLowerCase().trim(); const ol = (iss.onderwerp_logging || '').toLowerCase().trim();
const sum = (iss.summary || '').toLowerCase(); const sum = (iss.summary || '').toLowerCase();
const dxTerms = ['koppeling', 'sso', 'endpoint', 'rest', 'datadump', 'uwv', 'migratie', 'api', 'webhook', 'integrat']; const dxTerms = ['koppeling', 'sso', 'endpoint', 'rest', 'datadump', 'uwv', 'migratie'];
const matchesDx = dxTerms.some(t => ol.includes(t) || sum.includes(t)); const matchesDx = isSd && dxTerms.some(t => ol.includes(t) || sum.includes(t));
if (code === 'DX') return matchesDx; if (code === 'DX') return matchesDx;
if (code === 'SD') return !matchesDx; if (code === 'SD') return isSd && !matchesDx;
if (code === 'IN') return (pKey === 'IN' || pName.includes('interface'));
if (code === 'AAN') return (pKey === 'AAN' || pName.includes('aanvraag'));
if (code === 'GEN') return (pKey === 'GEN' || pName.includes('general') || pName.includes('algemeen'));
return true; return true;
} }