Jira 26
This commit is contained in:
@@ -1349,7 +1349,20 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
<?php foreach ($allJiraFilters as $filter): ?>
|
<?php foreach ($allJiraFilters as $filter): ?>
|
||||||
<?php
|
<?php
|
||||||
$fCodeNorm = strtoupper(trim($filter['filter_code'] ?? ''));
|
$fCodeNorm = strtoupper(trim($filter['filter_code'] ?? ''));
|
||||||
$assignedColleagues = $userNamesByFilter[$fCodeNorm] ?? ($userNamesByFilter[$filter['filter_code'] ?? ''] ?? []);
|
$fProjNorm = strtoupper(trim($filter['project_key'] ?? ''));
|
||||||
|
|
||||||
|
// Verzamel toegewezen collega's op basis van filter code én project key (case-insensitief)
|
||||||
|
$assignedColleagues = [];
|
||||||
|
if (!empty($userNamesByFilter[$fCodeNorm])) {
|
||||||
|
foreach ($userNamesByFilter[$fCodeNorm] as $cn) {
|
||||||
|
if (!in_array($cn, $assignedColleagues, true)) $assignedColleagues[] = $cn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($fProjNorm !== '' && !empty($userNamesByFilter[$fProjNorm])) {
|
||||||
|
foreach ($userNamesByFilter[$fProjNorm] as $cn) {
|
||||||
|
if (!in_array($cn, $assignedColleagues, true)) $assignedColleagues[] = $cn;
|
||||||
|
}
|
||||||
|
}
|
||||||
$filterJsonSafe = htmlspecialchars(json_encode($filter), ENT_QUOTES, 'UTF-8');
|
$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">
|
<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">
|
||||||
@@ -1495,10 +1508,14 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
</td>
|
</td>
|
||||||
<td class="py-3 px-4">
|
<td class="py-3 px-4">
|
||||||
<div class="flex flex-wrap gap-1.5 items-center">
|
<div class="flex flex-wrap gap-1.5 items-center">
|
||||||
<?php foreach ($allJiraFilters as $f):
|
<?php
|
||||||
|
$userSelectedNorm = array_map(function($c) { return strtoupper(trim($c)); }, $userSelectedCodes);
|
||||||
|
foreach ($allJiraFilters as $f):
|
||||||
if (!$f['is_active']) continue;
|
if (!$f['is_active']) continue;
|
||||||
$code = $f['filter_code'];
|
$code = $f['filter_code'];
|
||||||
$isChecked = in_array($code, $userSelectedCodes, true);
|
$cNorm = strtoupper(trim($code));
|
||||||
|
$pNorm = strtoupper(trim($f['project_key'] ?? ''));
|
||||||
|
$isChecked = in_array($cNorm, $userSelectedNorm, true) || ($pNorm !== '' && in_array($pNorm, $userSelectedNorm, true));
|
||||||
?>
|
?>
|
||||||
<label class="inline-flex items-center space-x-1.5 px-2.5 py-1 rounded-lg border text-xs font-semibold cursor-pointer transition select-none <?= $isChecked ? 'bg-indigo-50 border-indigo-300 text-indigo-700 dark:bg-indigo-950/60 dark:border-indigo-700 dark:text-indigo-300' : 'bg-slate-50 border-slate-200 text-slate-600 dark:bg-slate-900/40 dark:border-slate-800 dark:text-slate-400 hover:border-slate-300 dark:hover:border-slate-700' ?>">
|
<label class="inline-flex items-center space-x-1.5 px-2.5 py-1 rounded-lg border text-xs font-semibold cursor-pointer transition select-none <?= $isChecked ? 'bg-indigo-50 border-indigo-300 text-indigo-700 dark:bg-indigo-950/60 dark:border-indigo-700 dark:text-indigo-300' : 'bg-slate-50 border-slate-200 text-slate-600 dark:bg-slate-900/40 dark:border-slate-800 dark:text-slate-400 hover:border-slate-300 dark:hover:border-slate-700' ?>">
|
||||||
<input type="checkbox" name="user_filters[<?= (int)$usr['id'] ?>][]" value="<?= htmlspecialchars($code) ?>" <?= $isChecked ? 'checked' : '' ?> class="w-3.5 h-3.5 rounded text-indigo-600 border-slate-300 dark:border-slate-700 focus:ring-indigo-500 cursor-pointer">
|
<input type="checkbox" name="user_filters[<?= (int)$usr['id'] ?>][]" value="<?= htmlspecialchars($code) ?>" <?= $isChecked ? 'checked' : '' ?> class="w-3.5 h-3.5 rounded text-indigo-600 border-slate-300 dark:border-slate-700 focus:ring-indigo-500 cursor-pointer">
|
||||||
|
|||||||
+2
-2
@@ -972,7 +972,7 @@ function get_logged_in_user(): ?array {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$stmt = $db->prepare("SELECT id, name, email, role, two_factor_enabled, avatar_url, theme, notification_settings, default_jira_filter_code, fociz_widget_layout, created_at FROM users WHERE id = ?");
|
$stmt = $db->prepare("SELECT id, name, email, role, two_factor_enabled, avatar_url, theme, notification_settings, default_jira_filter_code, default_jira_filter_codes, fociz_widget_layout, created_at FROM users WHERE id = ?");
|
||||||
$stmt->execute([$_SESSION['user_id']]);
|
$stmt->execute([$_SESSION['user_id']]);
|
||||||
$user = $stmt->fetch();
|
$user = $stmt->fetch();
|
||||||
if ($user) {
|
if ($user) {
|
||||||
@@ -1022,7 +1022,7 @@ function get_logged_in_user(): ?array {
|
|||||||
// 3. Fallback dagsessie-token: herstelt sessie na browserherstart (uitsluitend vandaag én mits niet inactief!)
|
// 3. Fallback dagsessie-token: herstelt sessie na browserherstart (uitsluitend vandaag én mits niet inactief!)
|
||||||
$dayToken = $_COOKIE['fociz_day_session'] ?? ($_COOKIE['fociz_remember'] ?? null);
|
$dayToken = $_COOKIE['fociz_day_session'] ?? ($_COOKIE['fociz_remember'] ?? null);
|
||||||
if (!empty($dayToken) && is_string($dayToken)) {
|
if (!empty($dayToken) && is_string($dayToken)) {
|
||||||
$stmt = $db->prepare("SELECT id, name, email, role, two_factor_enabled, avatar_url, theme, notification_settings, default_jira_filter_code, fociz_widget_layout, last_activity_at, last_login_at, created_at FROM users WHERE remember_token = ? AND remember_token_expires_at >= NOW() AND DATE(remember_token_expires_at) = ?");
|
$stmt = $db->prepare("SELECT id, name, email, role, two_factor_enabled, avatar_url, theme, notification_settings, default_jira_filter_code, default_jira_filter_codes, fociz_widget_layout, last_activity_at, last_login_at, created_at FROM users WHERE remember_token = ? AND remember_token_expires_at >= NOW() AND DATE(remember_token_expires_at) = ?");
|
||||||
$stmt->execute([$dayToken, $today]);
|
$stmt->execute([$dayToken, $today]);
|
||||||
$user = $stmt->fetch();
|
$user = $stmt->fetch();
|
||||||
if ($user) {
|
if ($user) {
|
||||||
|
|||||||
+170
-68
@@ -435,71 +435,136 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri
|
|||||||
'customfield_10028', // Story Points (alternatief veld sommige instanties)
|
'customfield_10028', // Story Points (alternatief veld sommige instanties)
|
||||||
];
|
];
|
||||||
|
|
||||||
// 1. Moderne Atlassian Jira Cloud endpoint (/rest/api/3/search/jql) conform CHANGE-2046 via POST
|
$allRawIssues = [];
|
||||||
$url = $cleanDomain . '/rest/api/3/search/jql';
|
$nextPageToken = null;
|
||||||
$payload = [
|
$startAt = 0;
|
||||||
'jql' => $jql,
|
$pageSize = 100;
|
||||||
'maxResults' => min($maxResults, 100),
|
$maxPages = (int)ceil($maxResults / $pageSize);
|
||||||
'fields' => $fieldsList,
|
$useMethod = 'v3_post'; // 'v3_post', 'v3_get', 'v2_get'
|
||||||
];
|
|
||||||
|
|
||||||
$response = fociz_http_request($url, [
|
for ($page = 0; $page < $maxPages; $page++) {
|
||||||
'method' => 'POST',
|
$needed = $maxResults - count($allRawIssues);
|
||||||
'headers' => [
|
if ($needed <= 0) break;
|
||||||
'Authorization' => $authHeader,
|
$currentPageSize = min($pageSize, $needed);
|
||||||
'Accept' => 'application/json',
|
|
||||||
'Content-Type' => 'application/json',
|
|
||||||
],
|
|
||||||
'body' => $payload,
|
|
||||||
'timeout' => 12,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// 2. Fallback: GET /rest/api/3/search/jql met query parameters indien de instantie GET vereist
|
$response = null;
|
||||||
if ($response['status'] === 405 || $response['status'] === 400) {
|
|
||||||
$getParams = [
|
// 1. Moderne Atlassian Jira Cloud endpoint (/rest/api/3/search/jql) conform CHANGE-2046 via POST
|
||||||
'jql' => $jql,
|
if ($useMethod === 'v3_post') {
|
||||||
'maxResults' => min($maxResults, 100),
|
$url = $cleanDomain . '/rest/api/3/search/jql';
|
||||||
'fields' => implode(',', $fieldsList),
|
$payload = [
|
||||||
];
|
'jql' => $jql,
|
||||||
$urlGet = $cleanDomain . '/rest/api/3/search/jql?' . http_build_query($getParams);
|
'maxResults' => $currentPageSize,
|
||||||
$response = fociz_http_request($urlGet, [
|
'fields' => $fieldsList,
|
||||||
'method' => 'GET',
|
];
|
||||||
'headers' => [
|
if (!empty($nextPageToken)) {
|
||||||
'Authorization' => $authHeader,
|
$payload['nextPageToken'] = $nextPageToken;
|
||||||
'Accept' => 'application/json',
|
}
|
||||||
],
|
|
||||||
'timeout' => 12,
|
$response = fociz_http_request($url, [
|
||||||
]);
|
'method' => 'POST',
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => $authHeader,
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
'Content-Type' => 'application/json',
|
||||||
|
],
|
||||||
|
'body' => $payload,
|
||||||
|
'timeout' => 12,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($response['status'] === 405 || $response['status'] === 400) {
|
||||||
|
$useMethod = 'v3_get';
|
||||||
|
} elseif ($response['status'] === 404) {
|
||||||
|
$useMethod = 'v2_get';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Fallback: GET /rest/api/3/search/jql met query parameters indien de instantie GET vereist
|
||||||
|
if ($useMethod === 'v3_get') {
|
||||||
|
$getParams = [
|
||||||
|
'jql' => $jql,
|
||||||
|
'maxResults' => $currentPageSize,
|
||||||
|
'fields' => implode(',', $fieldsList),
|
||||||
|
];
|
||||||
|
if (!empty($nextPageToken)) {
|
||||||
|
$getParams['nextPageToken'] = $nextPageToken;
|
||||||
|
} elseif ($startAt > 0) {
|
||||||
|
$getParams['startAt'] = $startAt;
|
||||||
|
}
|
||||||
|
$urlGet = $cleanDomain . '/rest/api/3/search/jql?' . http_build_query($getParams);
|
||||||
|
$response = fociz_http_request($urlGet, [
|
||||||
|
'method' => 'GET',
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => $authHeader,
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
],
|
||||||
|
'timeout' => 12,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if ($response['status'] === 404) {
|
||||||
|
$useMethod = 'v2_get';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Fallback naar API v2 voor on-premise Jira Server / Data Center installaties
|
||||||
|
if ($useMethod === 'v2_get') {
|
||||||
|
$getParams = [
|
||||||
|
'jql' => $jql,
|
||||||
|
'maxResults' => $currentPageSize,
|
||||||
|
'startAt' => $startAt,
|
||||||
|
'fields' => implode(',', $fieldsList),
|
||||||
|
];
|
||||||
|
$urlV2 = $cleanDomain . '/rest/api/2/search?' . http_build_query($getParams);
|
||||||
|
$response = fociz_http_request($urlV2, [
|
||||||
|
'method' => 'GET',
|
||||||
|
'headers' => [
|
||||||
|
'Authorization' => $authHeader,
|
||||||
|
'Accept' => 'application/json',
|
||||||
|
],
|
||||||
|
'timeout' => 12,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$response || $response['status'] !== 200 || !isset($response['json']['issues']) || !is_array($response['json']['issues'])) {
|
||||||
|
if ($page === 0) {
|
||||||
|
$errorMsg = $response['json']['errorMessages'][0] ?? ($response['json']['message'] ?? ($response['error'] ?? 'Kan Jira tickets niet ophalen'));
|
||||||
|
return [
|
||||||
|
'success' => false,
|
||||||
|
'error' => $errorMsg,
|
||||||
|
'issues' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pageIssues = $response['json']['issues'];
|
||||||
|
if (empty($pageIssues)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($pageIssues as $pi) {
|
||||||
|
$allRawIssues[] = $pi;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nextPageToken = $response['json']['nextPageToken'] ?? null;
|
||||||
|
$totalInApi = (int)($response['json']['total'] ?? 0);
|
||||||
|
$startAt += count($pageIssues);
|
||||||
|
|
||||||
|
// Geen volgende pagina meer
|
||||||
|
if (empty($nextPageToken)) {
|
||||||
|
if ($totalInApi > 0 && $startAt >= $totalInApi) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ($useMethod === 'v3_post') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (count($pageIssues) < $currentPageSize) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Fallback naar API v2 voor on-premise Jira Server / Data Center installaties (waar /rest/api/2/search actief is)
|
$rawIssues = $allRawIssues;
|
||||||
if ($response['status'] === 404) {
|
|
||||||
$getParams = [
|
|
||||||
'jql' => $jql,
|
|
||||||
'maxResults' => min($maxResults, 100),
|
|
||||||
'fields' => implode(',', $fieldsList),
|
|
||||||
];
|
|
||||||
$urlV2 = $cleanDomain . '/rest/api/2/search?' . http_build_query($getParams);
|
|
||||||
$response = fociz_http_request($urlV2, [
|
|
||||||
'method' => 'GET',
|
|
||||||
'headers' => [
|
|
||||||
'Authorization' => $authHeader,
|
|
||||||
'Accept' => 'application/json',
|
|
||||||
],
|
|
||||||
'timeout' => 12,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($response['status'] !== 200 || !isset($response['json']['issues'])) {
|
|
||||||
$errorMsg = $response['json']['errorMessages'][0] ?? ($response['json']['message'] ?? ($response['error'] ?? 'Kan Jira tickets niet ophalen'));
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'error' => $errorMsg,
|
|
||||||
'issues' => [],
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$rawIssues = $response['json']['issues'];
|
|
||||||
$issues = [];
|
$issues = [];
|
||||||
|
|
||||||
foreach ($rawIssues as $item) {
|
foreach ($rawIssues as $item) {
|
||||||
@@ -631,7 +696,7 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri
|
|||||||
/**
|
/**
|
||||||
* Haalt alle actieve issues op die zijn toegewezen aan de ingelogde Jira gebruiker
|
* Haalt alle actieve issues op die zijn toegewezen aan de ingelogde Jira gebruiker
|
||||||
*/
|
*/
|
||||||
function jira_fetch_assigned_issues(string $domain, string $email, string $apiToken, int $maxResults = 100): array {
|
function jira_fetch_assigned_issues(string $domain, string $email, string $apiToken, int $maxResults = 250): array {
|
||||||
$jql = 'assignee = currentUser() ORDER BY updated DESC';
|
$jql = 'assignee = currentUser() ORDER BY updated DESC';
|
||||||
return jira_fetch_issues($domain, $email, $apiToken, $jql, $maxResults);
|
return jira_fetch_issues($domain, $email, $apiToken, $jql, $maxResults);
|
||||||
}
|
}
|
||||||
@@ -639,8 +704,18 @@ function jira_fetch_assigned_issues(string $domain, string $email, string $apiTo
|
|||||||
/**
|
/**
|
||||||
* Haalt openstaande team issues op die horen bij de projecten van de gebruiker
|
* Haalt openstaande team issues op die horen bij de projecten van de gebruiker
|
||||||
*/
|
*/
|
||||||
function jira_fetch_team_issues(string $domain, string $email, string $apiToken, int $maxResults = 100): array {
|
function jira_fetch_team_issues(string $domain, string $email, string $apiToken, $projectKeys = [], int $maxResults = 500): array {
|
||||||
$jql = 'assignee != currentUser() AND statusCategory != Done ORDER BY updated DESC';
|
if (is_int($projectKeys)) {
|
||||||
|
$maxResults = $projectKeys;
|
||||||
|
$projectKeys = [];
|
||||||
|
}
|
||||||
|
$cleanKeys = array_values(array_filter(array_map('strtoupper', array_map('trim', (array)$projectKeys))));
|
||||||
|
$projClause = '';
|
||||||
|
if (!empty($cleanKeys)) {
|
||||||
|
$escaped = array_map(function($k) { return '"' . addslashes($k) . '"'; }, $cleanKeys);
|
||||||
|
$projClause = 'project in (' . implode(', ', $escaped) . ') AND ';
|
||||||
|
}
|
||||||
|
$jql = $projClause . 'statusCategory != Done ORDER BY updated DESC';
|
||||||
return jira_fetch_issues($domain, $email, $apiToken, $jql, $maxResults);
|
return jira_fetch_issues($domain, $email, $apiToken, $jql, $maxResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1126,10 +1201,37 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ophalen van toegewezen taken (max 100)
|
// Ophalen van toegewezen taken (max 250)
|
||||||
$assignedRes = jira_fetch_assigned_issues($domain, $email, $apiToken, 100);
|
$assignedRes = jira_fetch_assigned_issues($domain, $email, $apiToken, 250);
|
||||||
// Ophalen van teamtaken (max 100)
|
|
||||||
$teamRes = jira_fetch_team_issues($domain, $email, $apiToken, 100);
|
// Bepaal de relevante projecten van de gebruiker om gericht en compleet op te halen
|
||||||
|
$targetProjKeys = [];
|
||||||
|
if (!empty($userQueuePrefs)) {
|
||||||
|
foreach ($userQueuePrefs as $code) {
|
||||||
|
$c = strtoupper(trim($code));
|
||||||
|
if ($c === 'DX') {
|
||||||
|
$targetProjKeys['SD'] = true;
|
||||||
|
} elseif (!in_array($c, ['MY_QUEUES'], true)) {
|
||||||
|
$foundProj = false;
|
||||||
|
foreach ($queueFilters as $qf) {
|
||||||
|
if (strtoupper(trim($qf['filter_code'] ?? '')) === $c && !empty($qf['project_key'])) {
|
||||||
|
$targetProjKeys[strtoupper(trim($qf['project_key']))] = true;
|
||||||
|
$foundProj = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$foundProj && strlen($c) <= 10) {
|
||||||
|
$targetProjKeys[$c] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($targetProjKeys)) {
|
||||||
|
$targetProjKeys = array_fill_keys(['SD', 'IN', 'AAN', 'GEN'], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ophalen van teamtaken binnen de relevante projecten (met paginatie tot max 500)
|
||||||
|
$teamRes = jira_fetch_team_issues($domain, $email, $apiToken, array_keys($targetProjKeys), 500);
|
||||||
// Ophalen van Jira statussen
|
// Ophalen van Jira statussen
|
||||||
$availableStatuses = jira_fetch_statuses($domain, $email, $apiToken);
|
$availableStatuses = jira_fetch_statuses($domain, $email, $apiToken);
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,16 @@ $syncError = $jiraData['error'] ?? null;
|
|||||||
$assignedIssues = $jiraData['assigned_issues'] ?? [];
|
$assignedIssues = $jiraData['assigned_issues'] ?? [];
|
||||||
$teamIssues = $jiraData['team_issues'] ?? [];
|
$teamIssues = $jiraData['team_issues'] ?? [];
|
||||||
|
|
||||||
|
// Markeer expliciet welke tickets zijn toegewezen aan de ingelogde gebruiker
|
||||||
|
foreach ($assignedIssues as &$ai) {
|
||||||
|
$ai['is_assigned_to_me'] = true;
|
||||||
|
}
|
||||||
|
unset($ai);
|
||||||
|
$jiraData['assigned_issues'] = $assignedIssues;
|
||||||
|
|
||||||
|
$currentUserName = $currentUser['name'] ?? ($accountName ?: '');
|
||||||
|
$currentUserEmail = $currentUser['email'] ?? ($email ?: '');
|
||||||
|
|
||||||
// Haal borden en kolommen op voor het "Naar Bord" modal
|
// Haal borden en kolommen op voor het "Naar Bord" modal
|
||||||
$db = get_db_connection();
|
$db = get_db_connection();
|
||||||
if ($isAdmin) {
|
if ($isAdmin) {
|
||||||
@@ -306,6 +316,8 @@ $jiraDataJson = json_encode($jiraData, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_Q
|
|||||||
$boardsJson = json_encode($boards, 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);
|
$projectStatusesJson = json_encode($projectStatusesData, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
||||||
$statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
$statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
||||||
|
$currentUserNameJson = json_encode($currentUserName, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
||||||
|
$currentUserEmailJson = json_encode($currentUserEmail, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<div class="space-y-6 w-full">
|
<div class="space-y-6 w-full">
|
||||||
@@ -615,6 +627,17 @@ $statusesJson = json_encode($allStatusesList, JSON_HEX_TAG | JSON_HEX_APOS | JSO
|
|||||||
<div id="projectStatusDropdownsWrapper" class="flex flex-wrap items-center gap-2">
|
<div id="projectStatusDropdownsWrapper" class="flex flex-wrap items-center gap-2">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Snelle optie: Toegewezen aan mij (uitvoerder) per geselecteerd project -->
|
||||||
|
<button type="button"
|
||||||
|
id="btnToggleAssignedToMePill"
|
||||||
|
onclick="toggleCurrentProjectOnlyAssigned()"
|
||||||
|
class="flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-semibold border transition cursor-pointer shadow-2xs flex-shrink-0 bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:border-slate-300 dark:hover:border-slate-600"
|
||||||
|
title="Filter op taken toegewezen aan mijzelf (uitvoerder)">
|
||||||
|
<i data-lucide="user-check" class="w-3.5 h-3.5 text-indigo-500 flex-shrink-0"></i>
|
||||||
|
<span id="labelAssignedToMePill">Toegewezen aan mij <span class="hidden sm:inline">(uitvoerder)</span></span>
|
||||||
|
<span id="badgeAssignedToMeCount" 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">0</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- Actieve filters reset knop als er iets gefilterd is -->
|
<!-- Actieve filters reset knop als er iets gefilterd is -->
|
||||||
<button type="button" onclick="resetAllFilters()" id="btnResetFiltersInline" class="hidden flex items-center space-x-1 px-2.5 py-1.5 rounded-xl text-xs font-semibold text-slate-500 hover:text-slate-800 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition cursor-pointer">
|
<button type="button" onclick="resetAllFilters()" id="btnResetFiltersInline" class="hidden flex items-center space-x-1 px-2.5 py-1.5 rounded-xl text-xs font-semibold text-slate-500 hover:text-slate-800 dark:hover:text-slate-200 hover:bg-slate-100 dark:hover:bg-slate-800 transition cursor-pointer">
|
||||||
<i data-lucide="rotate-ccw" class="w-3 h-3"></i>
|
<i data-lucide="rotate-ccw" class="w-3 h-3"></i>
|
||||||
@@ -991,6 +1014,8 @@ const jiraData = <?= $jiraDataJson ?>;
|
|||||||
const availableBoards = <?= $boardsJson ?>;
|
const availableBoards = <?= $boardsJson ?>;
|
||||||
const projectStatusesData = <?= $projectStatusesJson ?>;
|
const projectStatusesData = <?= $projectStatusesJson ?>;
|
||||||
const allJiraStatuses = <?= $statusesJson ?>;
|
const allJiraStatuses = <?= $statusesJson ?>;
|
||||||
|
const currentUserName = <?= $currentUserNameJson ?>;
|
||||||
|
const currentUserEmail = <?= $currentUserEmailJson ?>;
|
||||||
|
|
||||||
let currentProject = '';
|
let currentProject = '';
|
||||||
let currentQueue = '';
|
let currentQueue = '';
|
||||||
@@ -999,6 +1024,134 @@ let activeUrgentOnly = false;
|
|||||||
let expandedKeys = new Set();
|
let expandedKeys = new Set();
|
||||||
const ticketStore = new Map();
|
const ticketStore = new Map();
|
||||||
|
|
||||||
|
// Filter per project: 'Toegewezen aan mij (uitvoerder)'
|
||||||
|
let projectOnlyAssigned = new Set();
|
||||||
|
try {
|
||||||
|
const savedAssigned = localStorage.getItem('fociz_jira_project_only_assigned');
|
||||||
|
if (savedAssigned) {
|
||||||
|
const parsed = JSON.parse(savedAssigned);
|
||||||
|
if (Array.isArray(parsed)) {
|
||||||
|
parsed.forEach(p => projectOnlyAssigned.add(String(p).toUpperCase().trim()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
// Set van alle aan mij toegewezen issue keys
|
||||||
|
const assignedIssueKeys = new Set((jiraData.assigned_issues || []).map(i => i.key));
|
||||||
|
|
||||||
|
function isIssueAssignedToMe(iss) {
|
||||||
|
if (!iss) return false;
|
||||||
|
if (iss.is_assigned_to_me === true) return true;
|
||||||
|
if (iss.key && assignedIssueKeys.has(iss.key)) return true;
|
||||||
|
const ass = (iss.assignee_name || '').toLowerCase().trim();
|
||||||
|
if (!ass || ass === 'niet toegewezen' || ass === 'unassigned') return false;
|
||||||
|
if (ass === 'mijzelf' || ass === 'myself' || ass === 'me') return true;
|
||||||
|
|
||||||
|
const myName = (currentUserName || jiraData.account_name || '').toLowerCase().trim();
|
||||||
|
const myEmail = (currentUserEmail || jiraData.email || '').toLowerCase().trim();
|
||||||
|
if (myName && (ass === myName || ass.includes(myName) || myName.includes(ass))) return true;
|
||||||
|
if (myEmail && (ass.includes(myEmail) || (iss.assignee_email && String(iss.assignee_email).toLowerCase().includes(myEmail)))) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isProjectOnlyAssignedActive(projectVal, issPKey) {
|
||||||
|
if (projectOnlyAssigned.has('ALL')) return true;
|
||||||
|
const pNorm = (projectVal || '').toUpperCase().trim();
|
||||||
|
const issPNorm = (issPKey || '').toUpperCase().trim();
|
||||||
|
if (pNorm && projectOnlyAssigned.has(pNorm)) return true;
|
||||||
|
if (!pNorm && issPNorm && projectOnlyAssigned.has(issPNorm)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleProjectOnlyAssigned(pKey, forceVal) {
|
||||||
|
const key = (pKey || 'ALL').toUpperCase().trim();
|
||||||
|
const isCurrently = projectOnlyAssigned.has(key);
|
||||||
|
const willSet = (typeof forceVal === 'boolean') ? forceVal : !isCurrently;
|
||||||
|
|
||||||
|
if (willSet) {
|
||||||
|
projectOnlyAssigned.add(key);
|
||||||
|
} else {
|
||||||
|
projectOnlyAssigned.delete(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
localStorage.setItem('fociz_jira_project_only_assigned', JSON.stringify([...projectOnlyAssigned]));
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
updateAssignedPillState();
|
||||||
|
updateStatusDropdownLabel();
|
||||||
|
updateStatusCounts();
|
||||||
|
applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleCurrentProjectOnlyAssigned() {
|
||||||
|
const targetKey = (currentProject || 'ALL').toUpperCase().trim();
|
||||||
|
toggleProjectOnlyAssigned(targetKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateAssignedPillState() {
|
||||||
|
const targetKey = (currentProject || 'ALL').toUpperCase().trim();
|
||||||
|
const isAssigned = projectOnlyAssigned.has(targetKey) || (currentProject === '' && projectOnlyAssigned.has('ALL'));
|
||||||
|
const btn = document.getElementById('btnToggleAssignedToMePill');
|
||||||
|
const badge = document.getElementById('badgeAssignedToMeCount');
|
||||||
|
const label = document.getElementById('labelAssignedToMePill');
|
||||||
|
if (!btn) return;
|
||||||
|
|
||||||
|
if (isAssigned) {
|
||||||
|
btn.className = "flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-bold border border-indigo-600 bg-indigo-600 text-white shadow-xs flex-shrink-0 cursor-pointer";
|
||||||
|
if (badge) {
|
||||||
|
badge.className = "px-1.5 py-0.5 rounded-full text-[10px] font-black bg-white/20 text-white";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
btn.className = "flex items-center space-x-1.5 px-2.5 sm:px-3 py-1.5 rounded-xl text-xs font-semibold border transition cursor-pointer shadow-2xs flex-shrink-0 bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-700 text-slate-700 dark:text-slate-300 hover:border-slate-300 dark:hover:border-slate-600";
|
||||||
|
if (badge) {
|
||||||
|
badge.className = "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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label) {
|
||||||
|
if (currentProject) {
|
||||||
|
label.innerHTML = `Toegewezen aan mij <span class="text-[10px] opacity-80 uppercase font-mono">(${escapeHtml(currentProject)})</span>`;
|
||||||
|
} else {
|
||||||
|
label.innerHTML = `Toegewezen aan mij <span class="hidden sm:inline">(uitvoerder)</span>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const all = getDeduplicatedIssues();
|
||||||
|
let targetList = all;
|
||||||
|
if (currentProject) {
|
||||||
|
targetList = all.filter(iss => isIssueInQueue(iss, currentProject));
|
||||||
|
}
|
||||||
|
const myCount = targetList.filter(iss => isIssueAssignedToMe(iss)).length;
|
||||||
|
if (badge) {
|
||||||
|
badge.innerText = myCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
(projectStatusesData || []).forEach(proj => {
|
||||||
|
const pKey = (proj.key || '').toUpperCase().trim();
|
||||||
|
const chk = document.getElementById(`chkOnlyAssigned_${pKey}`);
|
||||||
|
if (chk) {
|
||||||
|
chk.checked = projectOnlyAssigned.has(pKey) || projectOnlyAssigned.has('ALL');
|
||||||
|
}
|
||||||
|
const cCnt = all.filter(iss => {
|
||||||
|
const k = getIssueProjectKey(iss);
|
||||||
|
return (k === pKey || (!k && (iss.project || '').toUpperCase().includes(pKey))) && isIssueAssignedToMe(iss);
|
||||||
|
}).length;
|
||||||
|
const cntEl = document.getElementById(`projAssignedCount_${pKey}`);
|
||||||
|
if (cntEl) cntEl.innerText = cCnt;
|
||||||
|
|
||||||
|
const btnMijn = document.getElementById(`btnDropdownMijn_${pKey}`);
|
||||||
|
if (btnMijn) {
|
||||||
|
const isPActive = projectOnlyAssigned.has(pKey) || projectOnlyAssigned.has('ALL');
|
||||||
|
if (isPActive) {
|
||||||
|
btnMijn.className = "px-1.5 py-0.5 rounded font-bold bg-indigo-600 text-white transition cursor-pointer";
|
||||||
|
} else {
|
||||||
|
btnMijn.className = "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";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Hulpfunctie voor deduplicatie van tickets (assigned + team)
|
// Hulpfunctie voor deduplicatie van tickets (assigned + team)
|
||||||
function getDeduplicatedIssues() {
|
function getDeduplicatedIssues() {
|
||||||
const assigned = jiraData.assigned_issues || [];
|
const assigned = jiraData.assigned_issues || [];
|
||||||
@@ -1274,12 +1427,39 @@ function renderStatusCheckboxes() {
|
|||||||
Actief
|
Actief
|
||||||
</button>
|
</button>
|
||||||
<span class="text-slate-300 dark:text-slate-700">•</span>
|
<span class="text-slate-300 dark:text-slate-700">•</span>
|
||||||
|
<button type="button" id="btnDropdownMijn_${escapeHtml(pKey)}" onclick="toggleProjectOnlyAssigned('${escapeHtml(pKey)}')" class="px-1.5 py-0.5 rounded font-bold ${projectOnlyAssigned.has(pKey) || projectOnlyAssigned.has('ALL') ? 'bg-indigo-600 text-white' : 'text-indigo-600 dark:text-indigo-400 hover:bg-indigo-50 dark:hover:bg-indigo-950/60'} transition cursor-pointer" title="Toegewezen aan mij (uitvoerder)">
|
||||||
|
Mijn
|
||||||
|
</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">
|
<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
|
Geen
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Toegewezen aan mij (uitvoerder) toggle card -->
|
||||||
|
<div class="px-3 py-2 -mx-3.5 bg-slate-50/80 dark:bg-slate-800/50 border-b border-slate-100 dark:border-slate-800/80 flex items-center justify-between">
|
||||||
|
<label for="chkOnlyAssigned_${escapeHtml(pKey)}" class="flex items-center space-x-2 cursor-pointer select-none min-w-0 flex-1 group">
|
||||||
|
<div class="w-6 h-6 rounded-lg ${projectOnlyAssigned.has(pKey) || projectOnlyAssigned.has('ALL') ? 'bg-indigo-600 text-white' : 'bg-slate-200 dark:bg-slate-700 text-slate-600 dark:text-slate-300'} flex items-center justify-center flex-shrink-0 transition">
|
||||||
|
<i data-lucide="user-check" class="w-3.5 h-3.5"></i>
|
||||||
|
</div>
|
||||||
|
<div class="min-w-0 flex-1">
|
||||||
|
<div class="text-xs font-bold text-slate-800 dark:text-slate-200 group-hover:text-indigo-600 dark:group-hover:text-indigo-400 transition truncate">Toegewezen aan mij (uitvoerder)</div>
|
||||||
|
<div class="text-[10px] text-slate-400">Alleen mijn taken in ${escapeHtml(pKey)}</div>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
<div class="flex items-center space-x-2 flex-shrink-0 ml-2">
|
||||||
|
<span id="projAssignedCount_${escapeHtml(pKey)}" class="px-1.5 py-0.5 rounded-full text-[10px] font-black bg-indigo-100 dark:bg-indigo-950/70 text-indigo-700 dark:text-indigo-300">0</span>
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
id="chkOnlyAssigned_${escapeHtml(pKey)}"
|
||||||
|
${projectOnlyAssigned.has(pKey) || projectOnlyAssigned.has('ALL') ? 'checked' : ''}
|
||||||
|
onchange="toggleProjectOnlyAssigned('${escapeHtml(pKey)}', this.checked)"
|
||||||
|
class="w-4 h-4 rounded text-indigo-600 border-slate-300 dark:border-slate-700 focus:ring-indigo-500 cursor-pointer"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="max-h-60 overflow-y-auto space-y-1 pr-1 custom-scrollbar" id="statusCheckboxList_${escapeHtml(pKey)}">
|
<div class="max-h-60 overflow-y-auto space-y-1 pr-1 custom-scrollbar" id="statusCheckboxList_${escapeHtml(pKey)}">
|
||||||
${statusRowsHtml}
|
${statusRowsHtml}
|
||||||
</div>
|
</div>
|
||||||
@@ -1293,6 +1473,7 @@ function renderStatusCheckboxes() {
|
|||||||
updateStatusCounts();
|
updateStatusCounts();
|
||||||
updateStatusDropdownLabel();
|
updateStatusDropdownLabel();
|
||||||
updateProjectStatusDropdownVisibility();
|
updateProjectStatusDropdownVisibility();
|
||||||
|
updateAssignedPillState();
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateProjectStatusDropdownVisibility() {
|
function updateProjectStatusDropdownVisibility() {
|
||||||
@@ -1386,6 +1567,11 @@ function updateStatusCounts() {
|
|||||||
projIssues = projIssues.filter(iss => isIssueInQueue(iss, currentProject));
|
projIssues = projIssues.filter(iss => isIssueInQueue(iss, currentProject));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toegewezen aan mij filter per project meenemen in de status tellingen
|
||||||
|
if (isProjectOnlyAssignedActive(currentProject, pKey)) {
|
||||||
|
projIssues = projIssues.filter(iss => isIssueAssignedToMe(iss));
|
||||||
|
}
|
||||||
|
|
||||||
const projTotalEl = document.getElementById(`projTotal_${pKey}`);
|
const projTotalEl = document.getElementById(`projTotal_${pKey}`);
|
||||||
if (projTotalEl) {
|
if (projTotalEl) {
|
||||||
projTotalEl.innerText = `(${projIssues.length})`;
|
projTotalEl.innerText = `(${projIssues.length})`;
|
||||||
@@ -1401,6 +1587,8 @@ function updateStatusCounts() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
updateAssignedPillState();
|
||||||
}
|
}
|
||||||
|
|
||||||
function onStatusCheckboxChange(projectKey) {
|
function onStatusCheckboxChange(projectKey) {
|
||||||
@@ -1556,11 +1744,18 @@ function updateStatusDropdownLabel() {
|
|||||||
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";
|
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";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toon indicator indien Toegewezen aan mij actief is voor dit project
|
||||||
|
const isAssignedOnly = projectOnlyAssigned.has(pKey) || projectOnlyAssigned.has('ALL');
|
||||||
|
if (isAssignedOnly) {
|
||||||
|
hasAnyCustomFilter = true;
|
||||||
|
labelEl.innerText += " • Mijn";
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const btnReset = document.getElementById('btnResetFiltersInline');
|
const btnReset = document.getElementById('btnResetFiltersInline');
|
||||||
if (btnReset) {
|
if (btnReset) {
|
||||||
const isCustom = hasAnyCustomFilter || currentProject !== '' || document.getElementById('jiraSearchInput')?.value;
|
const isCustom = hasAnyCustomFilter || currentProject !== '' || projectOnlyAssigned.size > 0 || document.getElementById('jiraSearchInput')?.value;
|
||||||
if (isCustom) btnReset.classList.remove('hidden');
|
if (isCustom) btnReset.classList.remove('hidden');
|
||||||
else btnReset.classList.add('hidden');
|
else btnReset.classList.add('hidden');
|
||||||
}
|
}
|
||||||
@@ -1628,6 +1823,7 @@ function selectProject(proj) {
|
|||||||
closeAllProjectStatusDropdowns();
|
closeAllProjectStatusDropdowns();
|
||||||
updateProjectPills();
|
updateProjectPills();
|
||||||
updateProjectStatusDropdownVisibility();
|
updateProjectStatusDropdownVisibility();
|
||||||
|
updateAssignedPillState();
|
||||||
updateStatusCounts();
|
updateStatusCounts();
|
||||||
applyFilters();
|
applyFilters();
|
||||||
}
|
}
|
||||||
@@ -1942,7 +2138,13 @@ function updateProjectCounts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const elAll = document.getElementById('projCount_all');
|
const elAll = document.getElementById('projCount_all');
|
||||||
if (elAll) elAll.innerText = currentList.length;
|
if (elAll) {
|
||||||
|
let allFiltered = currentList;
|
||||||
|
if (projectOnlyAssigned.has('ALL')) {
|
||||||
|
allFiltered = allFiltered.filter(iss => isIssueAssignedToMe(iss));
|
||||||
|
}
|
||||||
|
elAll.innerText = allFiltered.length;
|
||||||
|
}
|
||||||
|
|
||||||
const pills = document.querySelectorAll('.project-pill');
|
const pills = document.querySelectorAll('.project-pill');
|
||||||
pills.forEach(p => {
|
pills.forEach(p => {
|
||||||
@@ -1950,7 +2152,11 @@ function updateProjectCounts() {
|
|||||||
if (!pKey) return;
|
if (!pKey) return;
|
||||||
const el = document.getElementById(`projCount_${pKey}`);
|
const el = document.getElementById(`projCount_${pKey}`);
|
||||||
if (el) {
|
if (el) {
|
||||||
el.innerText = currentList.filter(iss => isIssueInQueue(iss, pKey)).length;
|
let pList = currentList.filter(iss => isIssueInQueue(iss, pKey));
|
||||||
|
if (projectOnlyAssigned.has(pKey.toUpperCase()) || projectOnlyAssigned.has('ALL')) {
|
||||||
|
pList = pList.filter(iss => isIssueAssignedToMe(iss));
|
||||||
|
}
|
||||||
|
el.innerText = pList.length;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1995,6 +2201,10 @@ function clearSearch() {
|
|||||||
function resetAllFilters() {
|
function resetAllFilters() {
|
||||||
currentProject = '';
|
currentProject = '';
|
||||||
currentQueue = '';
|
currentQueue = '';
|
||||||
|
projectOnlyAssigned.clear();
|
||||||
|
try {
|
||||||
|
localStorage.removeItem('fociz_jira_project_only_assigned');
|
||||||
|
} catch (e) {}
|
||||||
closeAllProjectStatusDropdowns();
|
closeAllProjectStatusDropdowns();
|
||||||
selectAllStatuses(true);
|
selectAllStatuses(true);
|
||||||
|
|
||||||
@@ -2009,6 +2219,7 @@ function resetAllFilters() {
|
|||||||
document.getElementById('clearSearchBtn')?.classList.add('hidden');
|
document.getElementById('clearSearchBtn')?.classList.add('hidden');
|
||||||
updateProjectPills();
|
updateProjectPills();
|
||||||
updateProjectStatusDropdownVisibility();
|
updateProjectStatusDropdownVisibility();
|
||||||
|
updateAssignedPillState();
|
||||||
updateStatusCounts();
|
updateStatusCounts();
|
||||||
applyFilters();
|
applyFilters();
|
||||||
}
|
}
|
||||||
@@ -2048,6 +2259,12 @@ function applyFilters() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 3. Toegewezen aan mij (uitvoerder) per project filter
|
||||||
|
const isAssignedFilterActive = isProjectOnlyAssignedActive(projectVal, issPKey);
|
||||||
|
if (isAssignedFilterActive && !isIssueAssignedToMe(iss)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// 4. Type filter (uit drawer)
|
// 4. Type filter (uit drawer)
|
||||||
if (typeVal && (iss.issue_type || '').toLowerCase() !== typeVal) {
|
if (typeVal && (iss.issue_type || '').toLowerCase() !== typeVal) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user