diff --git a/admin.php b/admin.php
index d96e99e..1dcb0b1 100644
--- a/admin.php
+++ b/admin.php
@@ -93,6 +93,74 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$messageType = 'success';
}
+ // 2b. Jira Wachtrijfilter toevoegen of bewerken
+ elseif ($action === 'save_jira_queue_filter') {
+ $filterId = (int)($_POST['filter_id'] ?? 0);
+ $filterCode = strtoupper(trim($_POST['filter_code'] ?? ''));
+ $filterName = trim($_POST['filter_name'] ?? '');
+ $projectKey = strtoupper(trim($_POST['project_key'] ?? ''));
+ $jqlClause = trim($_POST['jql_clause'] ?? '');
+ $description = trim($_POST['description'] ?? '');
+ $isActive = isset($_POST['is_active']) ? 1 : 0;
+ $sortOrder = (int)($_POST['sort_order'] ?? 0);
+
+ if (empty($filterCode) || empty($filterName) || empty($jqlClause)) {
+ $message = "Filtercode, naam en JQL-clausule zijn verplicht.";
+ $messageType = 'error';
+ } else {
+ if ($filterId > 0) {
+ $stmt = $db->prepare("
+ UPDATE jira_queue_filters
+ SET filter_code = ?, filter_name = ?, project_key = ?, jql_clause = ?, description = ?, is_active = ?, sort_order = ?, updated_at = NOW()
+ WHERE id = ?
+ ");
+ $stmt->execute([$filterCode, $filterName, $projectKey ?: null, $jqlClause, $description, $isActive, $sortOrder, $filterId]);
+ $message = "Wachtrijfilter '{$filterName}' is succesvol bijgewerkt.";
+ } else {
+ $stmt = $db->prepare("
+ INSERT INTO jira_queue_filters (filter_code, filter_name, project_key, jql_clause, description, is_active, sort_order)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE
+ filter_name = VALUES(filter_name),
+ project_key = VALUES(project_key),
+ jql_clause = VALUES(jql_clause),
+ description = VALUES(description),
+ is_active = VALUES(is_active),
+ sort_order = VALUES(sort_order),
+ updated_at = NOW()
+ ");
+ $stmt->execute([$filterCode, $filterName, $projectKey ?: null, $jqlClause, $description, $isActive, $sortOrder]);
+ $message = "Nieuw wachtrijfilter '{$filterName}' is succesvol toegevoegd.";
+ }
+ $messageType = 'success';
+ }
+ }
+
+ // 2c. Jira Wachtrijfilter verwijderen
+ elseif ($action === 'delete_jira_queue_filter') {
+ $filterId = (int)($_POST['filter_id'] ?? 0);
+ if ($filterId > 0) {
+ $stmt = $db->prepare("DELETE FROM jira_queue_filters WHERE id = ?");
+ $stmt->execute([$filterId]);
+ $message = "Wachtrijfilter is succesvol verwijderd.";
+ $messageType = 'success';
+ }
+ }
+
+ // 2d. Teamleden toewijzen aan Jira Wachtrijfilter
+ elseif ($action === 'save_user_jira_assignments') {
+ $assignments = $_POST['user_filter'] ?? [];
+ if (is_array($assignments)) {
+ $upd = $db->prepare("UPDATE users SET default_jira_filter_code = ? WHERE id = ?");
+ foreach ($assignments as $uId => $fCode) {
+ $fCode = trim($fCode);
+ $upd->execute([$fCode !== '' ? $fCode : null, (int)$uId]);
+ }
+ $message = "Standaard Jira wachtrijtoewijzingen voor het team zijn succesvol opgeslagen.";
+ $messageType = 'success';
+ }
+ }
+
// 3. Organisatie Werkdag Standaarden opslaan (Release v1.4)
elseif ($action === 'save_workday_settings') {
$workdayStart = trim($_POST['workday_start'] ?? '08:30');
@@ -215,6 +283,15 @@ try {
error_log("Fout bij ophalen recente gebruikers: " . $e->getMessage());
}
+$allUsersForJira = [];
+$allJiraFilters = [];
+try {
+ $allUsersForJira = $db->query("SELECT id, name, email, role, avatar_url, default_jira_filter_code FROM users ORDER BY name ASC")->fetchAll() ?: [];
+ $allJiraFilters = $db->query("SELECT * FROM jira_queue_filters ORDER BY sort_order ASC, id ASC")->fetchAll() ?: [];
+} catch (Throwable $e) {
+ error_log("Fout bij ophalen jira filters/users: " . $e->getMessage());
+}
+
$pageTitle = 'Systeembeheer & Integraties — Fociz';
require_once __DIR__ . '/includes/header.php';
?>
@@ -497,6 +574,286 @@ require_once __DIR__ . '/includes/header.php';
+
+
@@ -860,6 +1217,49 @@ function toggleAdminGuide() {
arrow.classList.toggle('rotate-180');
}
}
+
+// Jira Wachtrijfilter Modalfuncties
+function openJiraFilterModal() {
+ document.getElementById('jiraFilterModalTitle').innerText = 'Nieuw Wachtrijfilter Toevoegen';
+ document.getElementById('filterModal_id').value = '0';
+ document.getElementById('filterModal_code').value = '';
+ document.getElementById('filterModal_name').value = '';
+ document.getElementById('filterModal_project').value = 'SD';
+ document.getElementById('filterModal_jql').value = '';
+ document.getElementById('filterModal_desc').value = '';
+ document.getElementById('filterModal_active').checked = true;
+
+ const modal = document.getElementById('jiraFilterModal');
+ modal.classList.remove('hidden');
+ modal.classList.add('flex');
+}
+
+function editJiraFilter(filter) {
+ document.getElementById('jiraFilterModalTitle').innerText = `Wachtrijfilter '${filter.filter_name}' Bewerken`;
+ document.getElementById('filterModal_id').value = filter.id || '0';
+ document.getElementById('filterModal_code').value = filter.filter_code || '';
+ document.getElementById('filterModal_name').value = filter.filter_name || '';
+ document.getElementById('filterModal_project').value = filter.project_key || '';
+ document.getElementById('filterModal_jql').value = filter.jql_clause || '';
+ document.getElementById('filterModal_desc').value = filter.description || '';
+ document.getElementById('filterModal_active').checked = (filter.is_active != 0);
+
+ const modal = document.getElementById('jiraFilterModal');
+ modal.classList.remove('hidden');
+ modal.classList.add('flex');
+}
+
+function closeJiraFilterModal() {
+ const modal = document.getElementById('jiraFilterModal');
+ modal.classList.add('hidden');
+ modal.classList.remove('flex');
+}
+
+document.addEventListener('keydown', function(e) {
+ if (e.key === 'Escape') {
+ closeJiraFilterModal();
+ }
+});
diff --git a/api/integrations.php b/api/integrations.php
index b8f78e7..7c8656c 100644
--- a/api/integrations.php
+++ b/api/integrations.php
@@ -272,6 +272,107 @@ try {
exit;
}
+ // 5a. Jira Wachtrijfilters Ophalen
+ if ($action === 'get_jira_queue_filters') {
+ $onlyActive = !isset($_GET['all']) && !isset($_POST['all']);
+ $filters = jira_get_queue_filters($onlyActive);
+ echo json_encode(['success' => true, 'filters' => $filters]);
+ exit;
+ }
+
+ // 5b. Jira Wachtrijfilter Opslaan / Wijzigen (Alleen Beheerders)
+ if ($action === 'save_jira_queue_filter') {
+ if (($user['role'] ?? '') !== 'ADMIN') {
+ echo json_encode(['error' => 'Alleen beheerders mogen wachtrijfilters aanpassen.']);
+ exit;
+ }
+
+ $input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
+ $id = (int)($input['id'] ?? 0);
+ $filterCode = strtoupper(trim($input['filter_code'] ?? ''));
+ $filterName = trim($input['filter_name'] ?? '');
+ $projectKey = strtoupper(trim($input['project_key'] ?? ''));
+ $jqlClause = trim($input['jql_clause'] ?? '');
+ $description = trim($input['description'] ?? '');
+ $isActive = isset($input['is_active']) ? (int)$input['is_active'] : 1;
+ $sortOrder = (int)($input['sort_order'] ?? 0);
+
+ if (empty($filterCode) || empty($filterName) || empty($jqlClause)) {
+ echo json_encode(['error' => 'Code, naam en JQL-clausule zijn verplicht.']);
+ exit;
+ }
+
+ if ($id > 0) {
+ $stmt = $db->prepare("
+ UPDATE jira_queue_filters
+ SET filter_code = ?, filter_name = ?, project_key = ?, jql_clause = ?, description = ?, is_active = ?, sort_order = ?, updated_at = NOW()
+ WHERE id = ?
+ ");
+ $stmt->execute([$filterCode, $filterName, $projectKey ?: null, $jqlClause, $description, $isActive, $sortOrder, $id]);
+ } else {
+ $stmt = $db->prepare("
+ INSERT INTO jira_queue_filters (filter_code, filter_name, project_key, jql_clause, description, is_active, sort_order)
+ VALUES (?, ?, ?, ?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE
+ filter_name = VALUES(filter_name),
+ project_key = VALUES(project_key),
+ jql_clause = VALUES(jql_clause),
+ description = VALUES(description),
+ is_active = VALUES(is_active),
+ sort_order = VALUES(sort_order),
+ updated_at = NOW()
+ ");
+ $stmt->execute([$filterCode, $filterName, $projectKey ?: null, $jqlClause, $description, $isActive, $sortOrder]);
+ }
+
+ echo json_encode(['success' => true, 'message' => "Wachtrijfilter '{$filterName}' is succesvol opgeslagen."]);
+ exit;
+ }
+
+ // 5c. Jira Wachtrijfilter Verwijderen (Alleen Beheerders)
+ if ($action === 'delete_jira_queue_filter') {
+ if (($user['role'] ?? '') !== 'ADMIN') {
+ echo json_encode(['error' => 'Alleen beheerders mogen wachtrijfilters verwijderen.']);
+ exit;
+ }
+
+ $input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
+ $id = (int)($input['id'] ?? 0);
+ $filterCode = trim($input['filter_code'] ?? '');
+
+ if ($id > 0) {
+ $stmt = $db->prepare("DELETE FROM jira_queue_filters WHERE id = ?");
+ $stmt->execute([$id]);
+ } elseif (!empty($filterCode)) {
+ $stmt = $db->prepare("DELETE FROM jira_queue_filters WHERE filter_code = ?");
+ $stmt->execute([$filterCode]);
+ } else {
+ echo json_encode(['error' => 'Filter ID of code ontbreekt.']);
+ exit;
+ }
+
+ echo json_encode(['success' => true, 'message' => 'Wachtrijfilter is verwijderd.']);
+ exit;
+ }
+
+ // 5d. Standaard Wachtrijfilter Toewijzen aan Gebruiker (Beheerders of Gebruiker zelf)
+ if ($action === 'set_user_jira_filter') {
+ $input = json_decode(file_get_contents('php://input'), true) ?: $_POST;
+ $targetUserId = (int)($input['user_id'] ?? $user['id']);
+ $filterCode = trim($input['filter_code'] ?? '');
+
+ if ($targetUserId !== (int)$user['id'] && ($user['role'] ?? '') !== 'ADMIN') {
+ echo json_encode(['error' => 'Geen toestemming om instellingen van andere gebruikers te wijzigen.']);
+ exit;
+ }
+
+ $stmt = $db->prepare("UPDATE users SET default_jira_filter_code = ? WHERE id = ?");
+ $stmt->execute([$filterCode ?: null, $targetUserId]);
+
+ echo json_encode(['success' => true, 'message' => 'Wachtrijvoorkeur is succesvol bijgewerkt.']);
+ exit;
+ }
+
echo json_encode(['error' => 'Onbekende actie']);
} catch (Exception $e) {
http_response_code(500);
diff --git a/config/database.php b/config/database.php
index 77d4cd4..88ea16d 100644
--- a/config/database.php
+++ b/config/database.php
@@ -147,8 +147,10 @@ function ensure_tables_exist(PDO $pdo): void {
"ALTER TABLE users ADD COLUMN theme VARCHAR(20) NOT NULL DEFAULT 'dark' AFTER notification_settings",
"ALTER TABLE users ADD COLUMN avatar_url VARCHAR(255) NULL AFTER theme",
"ALTER TABLE users ADD COLUMN backup_codes TEXT NULL AFTER two_factor_enabled",
+ "ALTER TABLE users ADD COLUMN default_jira_filter_code VARCHAR(50) NULL AFTER avatar_url",
"ALTER TABLE board_card_schedules ADD UNIQUE KEY uk_sched_card (card_id)",
"CREATE TABLE IF NOT EXISTS `password_resets` (`id` INT AUTO_INCREMENT PRIMARY KEY, `email` VARCHAR(191) NOT NULL, `token` VARCHAR(64) NOT NULL UNIQUE, `expires_at` DATETIME NOT NULL, `used_at` DATETIME NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, INDEX (`email`), INDEX (`token`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
+ "CREATE TABLE IF NOT EXISTS `jira_queue_filters` (`id` INT AUTO_INCREMENT PRIMARY KEY, `filter_code` VARCHAR(50) NOT NULL UNIQUE, `filter_name` VARCHAR(120) NOT NULL, `project_key` VARCHAR(50) NULL, `jql_clause` TEXT NOT NULL, `description` VARCHAR(255) NULL, `is_active` TINYINT(1) NOT NULL DEFAULT 1, `sort_order` INT NOT NULL DEFAULT 0, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci",
];
foreach ($colUpgrades as $sql) {
try {
@@ -158,6 +160,35 @@ function ensure_tables_exist(PDO $pdo): void {
}
}
+ // Zorg dat de standaard Jira Servicedesk wachtrijfilters (DX en SD) aanwezig zijn
+ try {
+ $filterCount = (int)$pdo->query("SELECT COUNT(*) FROM jira_queue_filters")->fetchColumn();
+ if ($filterCount === 0) {
+ $insertFilterStmt = $pdo->prepare("
+ INSERT INTO jira_queue_filters (filter_code, filter_name, project_key, jql_clause, description, is_active, sort_order)
+ VALUES (?, ?, ?, ?, ?, 1, ?)
+ ");
+ $insertFilterStmt->execute([
+ 'DX',
+ 'DX — Koppelingen, SSO & Migratie',
+ 'SD',
+ '"Onderwerp logging[dropdown]" in (Koppelingen, SSO-koppeling, Rest-endpoints, Datadump, UWV-koppeling, Migratie)',
+ 'Servicedesk tickets met betrekking tot koppelingen, endpoints en datamigraties.',
+ 1
+ ]);
+ $insertFilterStmt->execute([
+ 'SD',
+ 'SD — Regulier Servicedesk',
+ 'SD',
+ '("Onderwerp logging[Dropdown]" = EMPTY OR "Onderwerp logging[Dropdown]" != Koppelingen AND "Onderwerp logging[Dropdown]" != Migratie AND "Onderwerp logging[Dropdown]" != SSO-koppeling AND "Onderwerp logging[Dropdown]" != UWV-koppeling AND "Onderwerp logging[Dropdown]" != Rest-endpoints AND "Onderwerp logging[Dropdown]" != Datadump)',
+ 'Reguliere servicedesk incidenten, algemene meldingen en gebruikersvragen.',
+ 2
+ ]);
+ }
+ } catch (Throwable $e) {
+ // Stil negeren indien tabel nog in opbouw is
+ }
+
// Zorg dat er ten minste één standaard bord met standaard lanes aanwezig is
try {
$boardCount = (int)$pdo->query("SELECT COUNT(*) FROM boards")->fetchColumn();
diff --git a/config/schema.sql b/config/schema.sql
index d49f3c2..e855a8c 100644
--- a/config/schema.sql
+++ b/config/schema.sql
@@ -14,6 +14,7 @@ CREATE TABLE IF NOT EXISTS `users` (
`remember_token_expires_at` DATETIME NULL,
`notification_settings` TEXT NULL,
`avatar_url` VARCHAR(255) NULL,
+ `default_jira_filter_code` VARCHAR(50) NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -218,3 +219,16 @@ CREATE TABLE IF NOT EXISTS `board_card_schedules` (
UNIQUE KEY `uk_sched_card` (`card_id`),
CONSTRAINT `fk_bsched_card` FOREIGN KEY (`card_id`) REFERENCES `board_cards` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+CREATE TABLE IF NOT EXISTS `jira_queue_filters` (
+ `id` INT AUTO_INCREMENT PRIMARY KEY,
+ `filter_code` VARCHAR(50) NOT NULL UNIQUE,
+ `filter_name` VARCHAR(120) NOT NULL,
+ `project_key` VARCHAR(50) NULL,
+ `jql_clause` TEXT NOT NULL,
+ `description` VARCHAR(255) NULL,
+ `is_active` TINYINT(1) NOT NULL DEFAULT 1,
+ `sort_order` INT NOT NULL DEFAULT 0,
+ `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/includes/integrations/jira.php b/includes/integrations/jira.php
index 46dedf1..c9caade 100644
--- a/includes/integrations/jira.php
+++ b/includes/integrations/jira.php
@@ -142,6 +142,76 @@ function jira_test_connection(string $domain, string $email, string $apiToken):
/**
* Haalt alle actieve issues op die zijn toegewezen aan de ingelogde Jira gebruiker
*/
+/**
+ * Converteert Atlassian Document Format (ADF) of tekst naar platte leesbare tekst
+ */
+function jira_adf_to_text($adf): string {
+ if (empty($adf)) {
+ return '';
+ }
+ if (is_string($adf)) {
+ return trim($adf);
+ }
+ if (!is_array($adf)) {
+ return '';
+ }
+
+ $text = '';
+ $type = $adf['type'] ?? '';
+
+ if ($type === 'text') {
+ return $adf['text'] ?? '';
+ }
+
+ if (!empty($adf['content']) && is_array($adf['content'])) {
+ foreach ($adf['content'] as $child) {
+ $childText = jira_adf_to_text($child);
+ $childType = $child['type'] ?? '';
+
+ if ($childType === 'paragraph' || $childType === 'heading') {
+ $text .= $childText . "\n\n";
+ } elseif ($childType === 'listItem') {
+ $text .= "• " . trim($childText) . "\n";
+ } elseif ($childType === 'codeBlock') {
+ $text .= "\n" . trim($childText) . "\n\n";
+ } else {
+ $text .= $childText;
+ }
+ }
+ }
+
+ return trim($text);
+}
+
+/**
+ * Haalt alle geconfigureerde Jira wachtrijfilters op uit de database
+ */
+function jira_get_queue_filters(bool $onlyActive = true): array {
+ try {
+ $db = get_db_connection();
+ $sql = "SELECT * FROM jira_queue_filters " . ($onlyActive ? "WHERE is_active = 1 " : "") . "ORDER BY sort_order ASC, id ASC";
+ return $db->query($sql)->fetchAll() ?: [];
+ } catch (Throwable $e) {
+ error_log("jira_get_queue_filters error: " . $e->getMessage());
+ return [];
+ }
+}
+
+/**
+ * Haalt de standaard wachtrijfilter voorkeur van een gebruiker op
+ */
+function jira_get_user_queue_preference(int $userId): ?string {
+ try {
+ $db = get_db_connection();
+ $stmt = $db->prepare("SELECT default_jira_filter_code FROM users WHERE id = ?");
+ $stmt->execute([$userId]);
+ $res = $stmt->fetchColumn();
+ return $res ? (string)$res : null;
+ } catch (Throwable $e) {
+ return null;
+ }
+}
+
/**
* Haalt issues op via een flexibele JQL query via Atlassian REST API
*/
@@ -159,7 +229,9 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri
'assignee',
'reporter',
'duedate',
- 'updated'
+ 'updated',
+ 'created',
+ 'description',
];
// 1. Moderne Atlassian Jira Cloud endpoint (/rest/api/3/search/jql) conform CHANGE-2046 via POST
@@ -242,26 +314,60 @@ function jira_fetch_issues(string $domain, string $email, string $apiToken, stri
$projectKey = $fields['project']['key'] ?? '';
$dueDate = $fields['duedate'] ?? null;
$updated = $fields['updated'] ?? null;
+ $created = $fields['created'] ?? null;
$assigneeName = $fields['assignee']['displayName'] ?? 'Niet toegewezen';
$assigneeAvatar = $fields['assignee']['avatarUrls']['48x48'] ?? ($fields['assignee']['avatarUrls']['32x32'] ?? '');
+ $reporterName = $fields['reporter']['displayName'] ?? 'Onbekend';
+ $reporterAvatar = $fields['reporter']['avatarUrls']['48x48'] ?? ($fields['reporter']['avatarUrls']['32x32'] ?? '');
+
+ $descriptionText = jira_adf_to_text($fields['description'] ?? '');
+
+ // Detectie van custom field "Onderwerp logging"
+ $onderwerpLogging = '';
+ foreach ($fields as $fKey => $fVal) {
+ if (str_starts_with($fKey, 'customfield_')) {
+ if (is_array($fVal) && isset($fVal['value']) && is_string($fVal['value'])) {
+ $valStr = trim($fVal['value']);
+ $known = ['koppelingen', 'sso-koppeling', 'rest-endpoints', 'datadump', 'uwv-koppeling', 'migratie', 'incident', 'vraag', 'algemeen'];
+ if (in_array(strtolower($valStr), $known)) {
+ $onderwerpLogging = $valStr;
+ }
+ } elseif (is_string($fVal) && !empty($fVal)) {
+ $valStr = trim($fVal);
+ $known = ['koppelingen', 'sso-koppeling', 'rest-endpoints', 'datadump', 'uwv-koppeling', 'migratie'];
+ if (in_array(strtolower($valStr), $known)) {
+ $onderwerpLogging = $valStr;
+ }
+ }
+ }
+ }
+ if (empty($onderwerpLogging) && !empty($fields['onderwerp_logging'])) {
+ $onderwerpLogging = is_string($fields['onderwerp_logging']) ? $fields['onderwerp_logging'] : ($fields['onderwerp_logging']['value'] ?? '');
+ }
+
$issues[] = [
- 'key' => $key,
- 'summary' => $summary,
- 'status' => $statusName,
- 'status_category' => $statusCategory,
- 'priority' => $priorityName,
- 'issue_type' => $issueTypeName,
- 'issue_type_icon' => $issueTypeIcon,
- 'project' => $projectName,
- 'project_key' => $projectKey,
- 'assignee_name' => $assigneeName,
- 'assignee_avatar' => $assigneeAvatar,
- 'due_date' => $dueDate,
- 'updated' => $updated,
- 'url' => "{$cleanDomain}/browse/{$key}",
- 'source' => 'JIRA_LIVE',
+ 'key' => $key,
+ 'summary' => $summary,
+ 'description' => $descriptionText,
+ 'status' => $statusName,
+ 'status_category' => $statusCategory,
+ 'priority' => $priorityName,
+ 'issue_type' => $issueTypeName,
+ 'issue_type_icon' => $issueTypeIcon,
+ 'project' => $projectName,
+ 'project_key' => $projectKey,
+ 'assignee_name' => $assigneeName,
+ 'assignee_avatar' => $assigneeAvatar,
+ 'reporter_name' => $reporterName,
+ 'reporter_avatar' => $reporterAvatar,
+ 'onderwerp_logging' => $onderwerpLogging,
+ 'due_date' => $dueDate,
+ 'created' => $created,
+ 'updated' => $updated,
+ 'url' => "{$cleanDomain}/browse/{$key}",
+ 'source' => 'JIRA_LIVE',
];
}
@@ -297,247 +403,407 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array
$stmt->execute([$userId]);
$integration = $stmt->fetch();
+ // Ophalen van actieve wachtrijfilters en voorkeur van gebruiker
+ $queueFilters = jira_get_queue_filters(true);
+ $userQueuePref = jira_get_user_queue_preference($userId);
+
// Sandbox / Demo dataset indien niet gekoppeld of expliciet in demomodus
if (!$integration || !empty($integration['is_demo_mode'])) {
$assigned = [
[
- 'key' => 'FOC-101',
- 'summary' => 'Tweestapsverificatie (2FA TOTP) flow implementeren',
- 'status' => 'In Progress',
- 'status_category' => 'indeterminate',
- 'priority' => 'High',
- 'issue_type' => 'Story',
- 'project' => 'Fociz Platform',
- 'project_key' => 'FOC',
- 'assignee_name' => 'Mijzelf',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+2 days')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-2 hours')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-101',
- 'source' => 'SANDBOX',
+ 'key' => 'SD-401',
+ 'summary' => 'AFAS Profit SSO-koppeling geeft 401 fout bij token refresh',
+ 'description' => "Gebruikers die via AFAS SSO inloggen krijgen periodiek een HTTP 401 foutcode bij de geautomatiseerde token verversing.\n\nHet endpoint `/oauth/token` reageert met `invalid_grant` zodra de refresh token ouder is dan 24 uur. Er dient een retry-mechanisme met fallback authenticatie ingebouwd te worden.",
+ 'status' => 'In Progress',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'Highest',
+ 'issue_type' => 'Bug',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Mijzelf',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Kees van de Berg (AFAS Beheer)',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'SSO-koppeling',
+ 'due_date' => date('Y-m-d', strtotime('+1 day')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-1 day')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-45 minutes')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-401',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-102',
- 'summary' => 'Interactief whiteboard canvas voor brainstormsessies',
- 'status' => 'To Do',
- 'status_category' => 'new',
- 'priority' => 'High',
+ 'key' => 'SD-405',
+ 'summary' => 'REST-endpoints voor medewerker synchronisatie beveiligen met rate limiting',
+ 'description' => "De openbare REST-endpoints voor medewerkerssynchronisatie dienen conform security richtlijnen te worden voorzien van rate limiting (max 120 calls per minuut per IP).\n\nBij overschrijding moet een duidelijke HTTP 429 Retry-After header worden geretourneerd.",
+ 'status' => 'To Do',
+ 'status_category' => 'new',
+ 'priority' => 'High',
+ 'issue_type' => 'Task',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Mijzelf',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Security Team',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'Rest-endpoints',
+ 'due_date' => date('Y-m-d', strtotime('+3 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-2 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-2 hours')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-405',
+ 'source' => 'SANDBOX',
+ ],
+ [
+ 'key' => 'SD-380',
+ 'summary' => 'Wachtwoord reset e-mail komt niet aan bij nieuwe medewerker',
+ 'description' => "Een nieuwe medewerker ontvangt geen welkomstmail en wachtwoord reset via het selfservice portaal.\n\nControleer de SMTP-verbinding van de mailserver mail.fociz.nl en controleer of het mailadres in de spamfilter is beland.",
+ 'status' => 'In Progress',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'High',
+ 'issue_type' => 'Task',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Mijzelf',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'HR Servicedesk',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'EMPTY',
+ 'due_date' => date('Y-m-d'),
+ 'created' => date('Y-m-d H:i:s', strtotime('-3 hours')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-1 hour')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-380',
+ 'source' => 'SANDBOX',
+ ],
+ [
+ 'key' => 'SD-385',
+ 'summary' => 'Rechten aanpassen voor rol Projectleider in urenregistratie',
+ 'description' => "Projectleiders hebben momenteel alleen leesrechten op ingediende urenstaten. Graag de rol uitbreiden zodat zij declaraties direct kunnen goedkeuren of afkeuren.",
+ 'status' => 'To Do',
+ 'status_category' => 'new',
+ 'priority' => 'Medium',
+ 'issue_type' => 'Task',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Mijzelf',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Marianne Willems',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'Algemeen',
+ 'due_date' => date('Y-m-d', strtotime('+5 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-3 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-1 day')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-385',
+ 'source' => 'SANDBOX',
+ ],
+ [
+ 'key' => 'FOC-101',
+ 'summary' => 'Tweestapsverificatie (2FA TOTP) flow implementeren',
+ 'description' => "Ontwikkeling van een complete Google Authenticator / Microsoft Authenticator TOTP verificatie flow inclusief 8 cijferige noodcodes en QR-code weergave in het gebruikersprofiel.",
+ 'status' => 'In Progress',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'High',
+ 'issue_type' => 'Story',
+ 'project' => 'Fociz Platform',
+ 'project_key' => 'FOC',
+ 'assignee_name' => 'Mijzelf',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Product Owner',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'Beveiliging',
+ 'due_date' => date('Y-m-d', strtotime('+2 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-4 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-2 hours')),
+ 'url' => 'https://jira.atlassian.net/browse/FOC-101',
+ 'source' => 'SANDBOX',
+ ],
+ [
+ 'key' => 'FOC-102',
+ 'summary' => 'Interactief whiteboard canvas voor brainstormsessies',
+ 'description' => "Gebruikers moeten live post-its kunnen slepen, kleuren en stemmen tijdens kwartaal brainstormsessies.",
+ 'status' => 'To Do',
+ 'status_category' => 'new',
+ 'priority' => 'High',
'issue_type' => 'Feature',
'project' => 'Fociz Platform',
'project_key' => 'FOC',
'assignee_name' => 'Mijzelf',
'assignee_avatar' => '',
+ 'reporter_name' => 'Scrum Master',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => '',
'due_date' => date('Y-m-d', strtotime('+4 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-5 days')),
'updated' => date('Y-m-d H:i:s', strtotime('-1 day')),
'url' => 'https://jira.atlassian.net/browse/FOC-102',
'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-103',
- 'summary' => 'Koppeling met Microsoft Outlook Calendar & Teams Graph API',
- 'status' => 'In Review',
- 'status_category' => 'indeterminate',
- 'priority' => 'Medium',
+ 'key' => 'FOC-103',
+ 'summary' => 'Koppeling met Microsoft Outlook Calendar & Teams Graph API',
+ 'description' => "Synchronisatie van dagplanning blokken naar Microsoft 365 Exchange Online agenda met real-time conflictcontrole.",
+ 'status' => 'In Review',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'Medium',
'issue_type' => 'Task',
'project' => 'Integrations Hub',
'project_key' => 'INT',
'assignee_name' => 'Mijzelf',
'assignee_avatar' => '',
+ 'reporter_name' => 'Architect',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'SSO-koppeling',
'due_date' => date('Y-m-d'),
+ 'created' => date('Y-m-d H:i:s', strtotime('-6 days')),
'updated' => date('Y-m-d H:i:s', strtotime('-4 hours')),
'url' => 'https://jira.atlassian.net/browse/FOC-103',
'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-104',
- 'summary' => 'Roadmap kwartaalweergave en voortgangsindicatoren',
- 'status' => 'To Do',
- 'status_category' => 'new',
- 'priority' => 'Medium',
- 'issue_type' => 'Story',
- 'project' => 'Roadmap Module',
- 'project_key' => 'ROAD',
- 'assignee_name' => 'Mijzelf',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+7 days')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-3 days')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-104',
- 'source' => 'SANDBOX',
+ 'key' => 'FOC-108',
+ 'summary' => 'CSS scrollbar glitch oplossen in Kanban lane op Firefox',
+ 'description' => "Op Windows Firefox versies 128+ verspringt de horizontale scrollbalk bij het slepen van kaarten tussen kolommen.",
+ 'status' => 'In Progress',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'Highest',
+ 'issue_type' => 'Bug',
+ 'project' => 'UI/UX Redesign',
+ 'project_key' => 'UI',
+ 'assignee_name' => 'Mijzelf',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'QA Lead',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => '',
+ 'due_date' => date('Y-m-d', strtotime('+1 day')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-2 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-30 minutes')),
+ 'url' => 'https://jira.atlassian.net/browse/FOC-108',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-108',
- 'summary' => 'CSS scrollbar glitch oplossen in Kanban lane op Firefox',
- 'status' => 'In Progress',
- 'status_category' => 'indeterminate',
- 'priority' => 'Highest',
- 'issue_type' => 'Bug',
- 'project' => 'UI/UX Redesign',
- 'project_key' => 'UI',
- 'assignee_name' => 'Mijzelf',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+1 day')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-30 minutes')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-108',
- 'source' => 'SANDBOX',
- ],
- [
- 'key' => 'FOC-112',
- 'summary' => 'Avatar upload beveiliging en compressie optimalisatie',
- 'status' => 'Done',
- 'status_category' => 'done',
- 'priority' => 'Medium',
- 'issue_type' => 'Task',
- 'project' => 'Security',
- 'project_key' => 'SEC',
- 'assignee_name' => 'Mijzelf',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('-1 day')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-5 hours')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-112',
- 'source' => 'SANDBOX',
+ 'key' => 'FOC-112',
+ 'summary' => 'Avatar upload beveiliging en compressie optimalisatie',
+ 'description' => "Server-side image re-encoding en EXIF stripping voor geüploade gebruikersavatars.",
+ 'status' => 'Done',
+ 'status_category' => 'done',
+ 'priority' => 'Medium',
+ 'issue_type' => 'Task',
+ 'project' => 'Security',
+ 'project_key' => 'SEC',
+ 'assignee_name' => 'Mijzelf',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'SecOps',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => '',
+ 'due_date' => date('Y-m-d', strtotime('-1 day')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-7 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-5 hours')),
+ 'url' => 'https://jira.atlassian.net/browse/FOC-112',
+ 'source' => 'SANDBOX',
],
];
$team = [
[
- 'key' => 'FOC-95',
- 'summary' => 'Sessie timeout notificatie verschijnt niet tijdig bij inactieve tab',
- 'status' => 'In Progress',
- 'status_category' => 'indeterminate',
- 'priority' => 'Highest',
- 'issue_type' => 'Bug',
- 'project' => 'Fociz Platform',
- 'project_key' => 'FOC',
- 'assignee_name' => 'Lisa Bakker',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+1 day')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-1 hour')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-95',
- 'source' => 'SANDBOX',
+ 'key' => 'SD-408',
+ 'summary' => 'UWV-koppeling polisadministratie import loopt vast op datumformaat',
+ 'description' => "De XML batch import vanuit het UWV portaal faalt op records met afwijkende geboortedatumnotatie.\n\nDe parser dient flexibel zowel ISO-8601 (YYYY-MM-DD) als traditioneel formaat (DD-MM-YYYY) te accepteren.",
+ 'status' => 'In Progress',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'High',
+ 'issue_type' => 'Bug',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Lisa Bakker',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Servicedesk L2',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'UWV-koppeling',
+ 'due_date' => date('Y-m-d', strtotime('+2 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-2 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-1 hour')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-408',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-98',
- 'summary' => 'Automatische e-mail triggers voor taakdeadlines en uitnodigingen',
- 'status' => 'To Do',
- 'status_category' => 'new',
- 'priority' => 'High',
- 'issue_type' => 'Story',
- 'project' => 'Notifications',
- 'project_key' => 'NOTIF',
- 'assignee_name' => 'Daan van Dijk',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+3 days')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-2 days')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-98',
- 'source' => 'SANDBOX',
+ 'key' => 'SD-412',
+ 'summary' => 'Datadump archief export voor externe accountant audit 2025',
+ 'description' => "Genereer een beveiligde, versleutelde datadump van alle audit-logs en urenstaten voor de periodieke controle.",
+ 'status' => 'To Do',
+ 'status_category' => 'new',
+ 'priority' => 'Medium',
+ 'issue_type' => 'Story',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Daan van Dijk',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Compliance Officer',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'Datadump',
+ 'due_date' => date('Y-m-d', strtotime('+6 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-3 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-4 hours')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-412',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-105',
- 'summary' => 'Database indexering optimaliseren voor grote audit logs',
- 'status' => 'In Review',
- 'status_category' => 'indeterminate',
- 'priority' => 'Medium',
- 'issue_type' => 'Task',
- 'project' => 'Database & Core',
- 'project_key' => 'CORE',
- 'assignee_name' => 'Sophie de Vries',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+5 days')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-1 day')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-105',
- 'source' => 'SANDBOX',
+ 'key' => 'SD-416',
+ 'summary' => 'Migratie van oude klantportaal database naar MariaDB 11 cluster',
+ 'description' => "Voorbereiden en uitvoeren van de dataschemamigratie van het oude portaal naar de nieuwe MariaDB 11 clusteromgeving inclusief regressietests.",
+ 'status' => 'To Do',
+ 'status_category' => 'new',
+ 'priority' => 'High',
+ 'issue_type' => 'Story',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Bram Meijer',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Lead Engineer',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'Migratie',
+ 'due_date' => date('Y-m-d', strtotime('+8 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-4 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-6 hours')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-416',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-107',
- 'summary' => 'Exporteer wekelijkse team rapportage naar PDF en Excel spreadsheet',
- 'status' => 'To Do',
- 'status_category' => 'new',
- 'priority' => 'Low',
- 'issue_type' => 'Feature',
- 'project' => 'Reporting',
- 'project_key' => 'REP',
- 'assignee_name' => 'Thomas Jansen',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+10 days')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-4 days')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-107',
- 'source' => 'SANDBOX',
+ 'key' => 'SD-420',
+ 'summary' => 'Koppelingen API webhook notificaties afleveren met retry-mechanisme',
+ 'description' => "Webhooks die naar externe partnersystemen worden verzonden moeten bij een tijdelijke 5xx fout automatisch 3x opnieuw worden aangeboden (exponential backoff).",
+ 'status' => 'In Review',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'Medium',
+ 'issue_type' => 'Task',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Sophie de Vries',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Integratie Specialist',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'Koppelingen',
+ 'due_date' => date('Y-m-d', strtotime('+4 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-5 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-3 hours')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-420',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-110',
- 'summary' => 'Real-time websocket synchronisatie voor gelijktijdig bewerken van borden',
- 'status' => 'To Do',
- 'status_category' => 'new',
- 'priority' => 'High',
- 'issue_type' => 'Story',
- 'project' => 'Realtime Engine',
- 'project_key' => 'RT',
- 'assignee_name' => 'Bram Meijer',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+6 days')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-6 hours')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-110',
- 'source' => 'SANDBOX',
+ 'key' => 'SD-392',
+ 'summary' => 'Outlook Agenda synchronisatie toont dubbele afspraken op mobiel',
+ 'description' => "Bij enkele gebruikers met de iOS Outlook app worden terugkerende agenda-afspraken tweemaal weergegeven na het synchroniseren.",
+ 'status' => 'In Progress',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'Medium',
+ 'issue_type' => 'Bug',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Lisa Bakker',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Servicedesk Support',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'Incident',
+ 'due_date' => date('Y-m-d', strtotime('+3 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-3 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-2 hours')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-392',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-114',
- 'summary' => 'Datum picker springt terug naar 1970 bij lege invoer',
- 'status' => 'To Do',
- 'status_category' => 'new',
- 'priority' => 'Medium',
- 'issue_type' => 'Bug',
- 'project' => 'UI/UX Redesign',
- 'project_key' => 'UI',
- 'assignee_name' => 'Niet toegewezen',
- 'assignee_avatar' => '',
- 'due_date' => null,
- 'updated' => date('Y-m-d H:i:s', strtotime('-2 days')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-114',
- 'source' => 'SANDBOX',
+ 'key' => 'SD-399',
+ 'summary' => 'Vraag over exportmogelijkheden naar Excel rapportage',
+ 'description' => "Klant informeert of het mogelijk is om een wekelijkse CSV / Excel export van de urenstaten automatisch per mail te ontvangen.",
+ 'status' => 'To Do',
+ 'status_category' => 'new',
+ 'priority' => 'Low',
+ 'issue_type' => 'Task',
+ 'project' => 'Servicedesk',
+ 'project_key' => 'SD',
+ 'assignee_name' => 'Thomas Jansen',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Financieel Directeur',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => 'Vraag',
+ 'due_date' => date('Y-m-d', strtotime('+10 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-5 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-1 day')),
+ 'url' => 'https://jira.atlassian.net/browse/SD-399',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-89',
- 'summary' => 'GDPR / AVG compliancy en geautomatiseerd data retentiebeleid',
- 'status' => 'In Progress',
- 'status_category' => 'indeterminate',
- 'priority' => 'Medium',
- 'issue_type' => 'Epic',
- 'project' => 'Security',
- 'project_key' => 'SEC',
- 'assignee_name' => 'Lisa Bakker',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('+14 days')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-3 hours')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-89',
- 'source' => 'SANDBOX',
+ 'key' => 'FOC-95',
+ 'summary' => 'Sessie timeout notificatie verschijnt niet tijdig bij inactieve tab',
+ 'description' => "Wanneer een gebruiker de browser 2 uur niet aanraakt, vervalt de PHP sessie zonder dat het login-dialoogvenster netjes openspringt.",
+ 'status' => 'In Progress',
+ 'status_category' => 'indeterminate',
+ 'priority' => 'Highest',
+ 'issue_type' => 'Bug',
+ 'project' => 'Fociz Platform',
+ 'project_key' => 'FOC',
+ 'assignee_name' => 'Lisa Bakker',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Product QA',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => '',
+ 'due_date' => date('Y-m-d', strtotime('+1 day')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-2 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-1 hour')),
+ 'url' => 'https://jira.atlassian.net/browse/FOC-95',
+ 'source' => 'SANDBOX',
],
[
- 'key' => 'FOC-82',
- 'summary' => 'Performance optimalisatie van vector avatar rendering in navigatiebalk',
- 'status' => 'Done',
- 'status_category' => 'done',
- 'priority' => 'Low',
- 'issue_type' => 'Task',
- 'project' => 'UI/UX Redesign',
- 'project_key' => 'UI',
- 'assignee_name' => 'Sophie de Vries',
- 'assignee_avatar' => '',
- 'due_date' => date('Y-m-d', strtotime('-3 days')),
- 'updated' => date('Y-m-d H:i:s', strtotime('-2 days')),
- 'url' => 'https://jira.atlassian.net/browse/FOC-82',
- 'source' => 'SANDBOX',
+ 'key' => 'FOC-98',
+ 'summary' => 'Automatische e-mail triggers voor taakdeadlines en uitnodigingen',
+ 'description' => "Cronjob instellen die 24 uur voor de deadline van een taak een herinneringsmail stuurt via SMTP.",
+ 'status' => 'To Do',
+ 'status_category' => 'new',
+ 'priority' => 'High',
+ 'issue_type' => 'Story',
+ 'project' => 'Notifications',
+ 'project_key' => 'NOTIF',
+ 'assignee_name' => 'Daan van Dijk',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Operations',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => '',
+ 'due_date' => date('Y-m-d', strtotime('+3 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-3 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-2 days')),
+ 'url' => 'https://jira.atlassian.net/browse/FOC-98',
+ 'source' => 'SANDBOX',
+ ],
+ [
+ 'key' => 'FOC-110',
+ 'summary' => 'Real-time websocket synchronisatie voor gelijktijdig bewerken van borden',
+ 'description' => "Voorkom overschrijfconflicten wanneer meerdere collega's gelijktijdig kaarten verplaatsen op hetzelfde Kanban bord.",
+ 'status' => 'To Do',
+ 'status_category' => 'new',
+ 'priority' => 'High',
+ 'issue_type' => 'Story',
+ 'project' => 'Realtime Engine',
+ 'project_key' => 'RT',
+ 'assignee_name' => 'Bram Meijer',
+ 'assignee_avatar' => '',
+ 'reporter_name' => 'Architect',
+ 'reporter_avatar' => '',
+ 'onderwerp_logging' => '',
+ 'due_date' => date('Y-m-d', strtotime('+6 days')),
+ 'created' => date('Y-m-d H:i:s', strtotime('-4 days')),
+ 'updated' => date('Y-m-d H:i:s', strtotime('-6 hours')),
+ 'url' => 'https://jira.atlassian.net/browse/FOC-110',
+ 'source' => 'SANDBOX',
],
];
return [
- 'is_demo' => true,
- 'success' => true,
- 'domain' => 'sandbox.atlassian.net',
- 'email' => 'demo@fociz.nl',
- 'account_name' => 'Demo Account',
- 'last_sync_at' => date('Y-m-d H:i:s'),
- 'assigned_issues' => $assigned,
- 'team_issues' => $team,
+ 'is_demo' => true,
+ 'success' => true,
+ 'domain' => 'sandbox.atlassian.net',
+ 'email' => 'demo@fociz.nl',
+ 'account_name' => 'Demo Account',
+ 'last_sync_at' => date('Y-m-d H:i:s'),
+ 'assigned_issues' => $assigned,
+ 'team_issues' => $team,
+ 'queue_filters' => $queueFilters,
+ 'default_queue_filter' => $userQueuePref,
];
}
@@ -587,14 +853,16 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array
$teamIssues = $teamRes['success'] ? $teamRes['issues'] : [];
$data = [
- 'is_demo' => false,
- 'success' => true,
- 'domain' => $domain,
- 'email' => $email,
- 'account_name' => $accName,
- 'last_sync_at' => date('Y-m-d H:i:s'),
- 'assigned_issues' => $assignedIssues,
- 'team_issues' => $teamIssues,
+ 'is_demo' => false,
+ 'success' => true,
+ 'domain' => $domain,
+ 'email' => $email,
+ 'account_name' => $accName,
+ 'last_sync_at' => date('Y-m-d H:i:s'),
+ 'assigned_issues' => $assignedIssues,
+ 'team_issues' => $teamIssues,
+ 'queue_filters' => $queueFilters,
+ 'default_queue_filter' => $userQueuePref,
];
$_SESSION[$cacheKey] = [
diff --git a/jira.php b/jira.php
index f4d1648..d63d25e 100644
--- a/jira.php
+++ b/jira.php
@@ -56,16 +56,22 @@ foreach ($boards as &$b) {
}
unset($b);
-// Verzamel unieke projecten en types voor filter dropdowns
+// Verzamel unieke projecten en types voor filter dropdowns en project switcher
$allIssues = array_merge($assignedIssues, $teamIssues);
$allProjects = [];
$allTypes = [];
$allPriorities = ['Highest', 'High', 'Medium', 'Low'];
$allStatuses = [];
+$projectCounts = [];
foreach ($allIssues as $iss) {
if (!empty($iss['project'])) {
- $allProjects[$iss['project']] = $iss['project'];
+ $p = $iss['project'];
+ $allProjects[$p] = [
+ 'name' => $p,
+ 'key' => $iss['project_key'] ?? '',
+ ];
+ $projectCounts[$p] = ($projectCounts[$p] ?? 0) + 1;
}
if (!empty($iss['issue_type'])) {
$allTypes[$iss['issue_type']] = $iss['issue_type'];
@@ -78,6 +84,9 @@ ksort($allProjects);
ksort($allTypes);
ksort($allStatuses);
+$queueFilters = $jiraData['queue_filters'] ?? jira_get_queue_filters(true);
+$defaultQueueFilter = $jiraData['default_queue_filter'] ?? jira_get_user_queue_preference($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);
?>
@@ -229,6 +238,97 @@ $boardsJson = json_encode($boards, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT
+
+