diff --git a/admin.php b/admin.php
index 8b60250..df84755 100644
--- a/admin.php
+++ b/admin.php
@@ -471,8 +471,8 @@ require_once __DIR__ . '/includes/header.php';
Wordt veilig en versleuteld opgeslagen
-
-
@@ -1204,16 +1204,18 @@ function copyAdminRedirectUri() {
function toggleAdminSecret() {
const input = document.getElementById('adminClientSecret');
- const icon = document.getElementById('eyeSecretIcon');
- if (!input || !icon) return;
+ const btn = document.getElementById('toggleSecretBtn');
+ if (!input) return;
if (input.type === 'password') {
input.type = 'text';
- icon.setAttribute('data-lucide', 'eye-off');
+ if (btn) btn.innerHTML = '';
} else {
input.type = 'password';
- icon.setAttribute('data-lucide', 'eye');
+ if (btn) btn.innerHTML = '';
+ }
+ if (window.lucide && typeof lucide.createIcons === 'function') {
+ lucide.createIcons();
}
- if (window.lucide) lucide.createIcons();
}
function toggleAdminGuide() {
diff --git a/api/live_sync.php b/api/live_sync.php
index 0fe34b5..4afef42 100644
--- a/api/live_sync.php
+++ b/api/live_sync.php
@@ -12,6 +12,11 @@ $user = require_auth();
$userId = (int)$user['id'];
$db = get_db_connection();
+// Geef het PHP sessieslot direct vrij zodat gelijktijdige paginanavigatie nooit geblokkeerd wordt
+if (session_status() === PHP_SESSION_ACTIVE) {
+ session_write_close();
+}
+
header('Content-Type: application/json; charset=utf-8');
try {
diff --git a/api/mail.php b/api/mail.php
index 52c8571..4ae95cd 100644
--- a/api/mail.php
+++ b/api/mail.php
@@ -11,6 +11,11 @@ $user = require_auth();
$userId = (int)$user['id'];
$db = get_db_connection();
+// Geef het PHP sessieslot direct vrij zodat paginanavigatie nooit geblokkeerd wordt
+if (session_status() === PHP_SESSION_ACTIVE) {
+ session_write_close();
+}
+
header('Content-Type: application/json; charset=utf-8');
$action = $_GET['action'] ?? ($_POST['action'] ?? '');
@@ -20,8 +25,9 @@ try {
if ($action === 'get_inbox') {
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 25;
$unreadOnly = !empty($_GET['unread_only']) && $_GET['unread_only'] === '1';
+ $forceRefresh = !empty($_GET['refresh']) && $_GET['refresh'] === '1';
- $res = microsoft_fetch_inbox_messages($userId, $limit, $unreadOnly);
+ $res = microsoft_fetch_inbox_messages($userId, $limit, $unreadOnly, $forceRefresh);
echo json_encode($res);
exit;
}
diff --git a/api/notifications.php b/api/notifications.php
index 9ff490f..31f1401 100644
--- a/api/notifications.php
+++ b/api/notifications.php
@@ -9,6 +9,11 @@ require_once __DIR__ . '/../includes/integrations/microsoft.php';
$user = require_auth();
$db = get_db_connection();
+// Geef het PHP sessieslot direct vrij zodat paginanavigatie nooit geblokkeerd wordt
+if (session_status() === PHP_SESSION_ACTIVE) {
+ session_write_close();
+}
+
header('Content-Type: application/json; charset=utf-8');
$action = $_GET['action'] ?? ($_POST['action'] ?? '');
diff --git a/assets/js/notifications.js b/assets/js/notifications.js
index 96862b5..8ef2959 100644
--- a/assets/js/notifications.js
+++ b/assets/js/notifications.js
@@ -425,15 +425,21 @@
if (res.ok) {
const data = await res.json();
if (data && data.success) {
- // Update mail badge in navigatie
+ // Update mail badge in navigatie (desktop & mobiel)
const mailBadge = document.getElementById('navMailBadge');
- if (mailBadge && typeof data.mail?.unread_count !== 'undefined') {
- if (data.mail.unread_count > 0) {
- mailBadge.innerText = data.mail.unread_count;
- mailBadge.classList.remove('hidden');
- } else {
- mailBadge.classList.add('hidden');
- }
+ const mailBadgeMobile = document.getElementById('navMailBadgeMobile');
+ if (typeof data.mail?.unread_count !== 'undefined') {
+ const count = parseInt(data.mail.unread_count, 10) || 0;
+ const badgeText = count > 99 ? '99+' : count;
+ [mailBadge, mailBadgeMobile].forEach(el => {
+ if (!el) return;
+ if (count > 0) {
+ el.innerText = badgeText;
+ el.classList.remove('hidden');
+ } else {
+ el.classList.add('hidden');
+ }
+ });
}
// E-mail melding
@@ -500,18 +506,25 @@
setInterval(refreshEvents, 60000);
// Evaluatie interval elke 15 seconden voor agenda popups
setInterval(evaluateEvents, 15000);
- // Live sync interval elke 12 seconden voor instant mail, Jira & calendar updates
- setInterval(checkLiveSync, 12000);
+ // Live sync interval elke 30 seconden voor mail, Jira & calendar updates
+ setInterval(checkLiveSync, 30000);
- window.addEventListener('focus', () => {
- checkLiveSync();
- refreshEvents();
- });
+ // Intelligente throttle voor window focus en tabwisseling (max 1x per 20s)
+ let lastFocusCheck = Date.now();
+ const throttledSync = () => {
+ const now = Date.now();
+ if (now - lastFocusCheck > 20000) {
+ lastFocusCheck = now;
+ checkLiveSync();
+ refreshEvents();
+ }
+ };
+
+ window.addEventListener('focus', throttledSync);
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
- checkLiveSync();
- refreshEvents();
+ throttledSync();
}
});
diff --git a/config/schema.sql b/config/schema.sql
index afd353e..dd49ea9 100644
--- a/config/schema.sql
+++ b/config/schema.sql
@@ -235,3 +235,10 @@ CREATE TABLE IF NOT EXISTS `jira_queue_filters` (
`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;
+
+CREATE TABLE IF NOT EXISTS `app_cache` (
+ `cache_key` VARCHAR(128) NOT NULL PRIMARY KEY,
+ `cache_value` MEDIUMTEXT NOT NULL,
+ `expires_at` INT UNSIGNED NOT NULL,
+ INDEX `idx_app_cache_expires` (`expires_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/includes/auth.php b/includes/auth.php
index f2b892a..a5dc805 100644
--- a/includes/auth.php
+++ b/includes/auth.php
@@ -211,7 +211,7 @@ function get_logged_in_user(): ?array {
// 2. Controleer actieve PHP sessie (mits behorend bij vandaag)
if (!empty($_SESSION['user_id']) && !empty($_SESSION['is_2fa_verified'])) {
- $stmt = $db->prepare("SELECT id, name, email, role, two_factor_enabled, avatar_url, 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, fociz_widget_layout, created_at FROM users WHERE id = ?");
$stmt->execute([$_SESSION['user_id']]);
$user = $stmt->fetch();
if ($user) {
@@ -229,7 +229,7 @@ function get_logged_in_user(): ?array {
// 3. Fallback dagsessie-token: herstelt sessie na browserherstart of server idle timeout (uitsluitend vandaag!)
$dayToken = $_COOKIE['fociz_day_session'] ?? ($_COOKIE['fociz_remember'] ?? null);
if (!empty($dayToken) && is_string($dayToken)) {
- $stmt = $db->prepare("SELECT id, name, email, role, two_factor_enabled, avatar_url, 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, fociz_widget_layout, created_at FROM users WHERE remember_token = ? AND remember_token_expires_at >= NOW() AND DATE(remember_token_expires_at) = ?");
$stmt->execute([$dayToken, $today]);
$user = $stmt->fetch();
if ($user) {
@@ -471,10 +471,13 @@ function update_user_notification_settings(int $userId, array $settings): bool {
* Haalt het voorkeursthema op voor een gebruiker ('dark', 'light' of 'system')
*/
function get_user_theme(int $userId): string {
- $db = get_db_connection();
- ensure_theme_column($db);
+ global $currentUser;
+ if (!empty($currentUser['id']) && (int)$currentUser['id'] === $userId && !empty($currentUser['theme'])) {
+ return $currentUser['theme'];
+ }
try {
+ $db = get_db_connection();
$stmt = $db->prepare("SELECT theme FROM users WHERE id = ?");
$stmt->execute([$userId]);
$theme = $stmt->fetchColumn();
diff --git a/includes/cache.php b/includes/cache.php
new file mode 100644
index 0000000..003a620
--- /dev/null
+++ b/includes/cache.php
@@ -0,0 +1,139 @@
+exec("
+ CREATE TABLE IF NOT EXISTS `app_cache` (
+ `cache_key` VARCHAR(128) NOT NULL PRIMARY KEY,
+ `cache_value` MEDIUMTEXT NOT NULL,
+ `expires_at` INT UNSIGNED NOT NULL,
+ INDEX `idx_app_cache_expires` (`expires_at`)
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+ ");
+ $ensured = true;
+ } catch (Throwable $e) {
+ error_log("ensure_app_cache_table error: " . $e->getMessage());
+ }
+}
+
+/**
+ * Haalt een item op uit de cache.
+ * Controleert eerst het runtime statisch geheugen, vervolgens MariaDB.
+ */
+function fociz_cache_get(string $key, $default = null) {
+ static $memoryCache = [];
+
+ $now = time();
+
+ // 1. Runtime memory check (0ms)
+ if (isset($memoryCache[$key])) {
+ if ($memoryCache[$key]['expires_at'] > $now) {
+ return $memoryCache[$key]['value'];
+ }
+ unset($memoryCache[$key]);
+ }
+
+ // 2. Database check (< 0.5ms)
+ try {
+ $db = get_db_connection();
+ ensure_app_cache_table($db);
+
+ $stmt = $db->prepare("SELECT cache_value, expires_at FROM app_cache WHERE cache_key = ?");
+ $stmt->execute([$key]);
+ $row = $stmt->fetch();
+
+ if ($row) {
+ if ((int)$row['expires_at'] > $now) {
+ $decoded = json_decode($row['cache_value'], true);
+ $val = ($decoded !== null || $row['cache_value'] === 'null') ? $decoded : $row['cache_value'];
+ $memoryCache[$key] = [
+ 'value' => $val,
+ 'expires_at' => (int)$row['expires_at']
+ ];
+ return $val;
+ } else {
+ // Verlopen item direct opruimen
+ $del = $db->prepare("DELETE FROM app_cache WHERE cache_key = ?");
+ $del->execute([$key]);
+ }
+ }
+ } catch (Throwable $e) {
+ error_log("fociz_cache_get error for {$key}: " . $e->getMessage());
+ }
+
+ return $default;
+}
+
+/**
+ * Slaat een item op in de cache met een bepaalde levensduur in seconden.
+ */
+function fociz_cache_set(string $key, $value, int $ttlSeconds = 60): bool {
+ static $memoryCache = [];
+
+ $now = time();
+ $expiresAt = $now + max(1, $ttlSeconds);
+
+ // 1. Sla op in runtime memory
+ $memoryCache[$key] = [
+ 'value' => $value,
+ 'expires_at' => $expiresAt
+ ];
+
+ // 2. Sla op in database
+ try {
+ $db = get_db_connection();
+ ensure_app_cache_table($db);
+
+ $valToStore = is_string($value) ? $value : json_encode($value, JSON_UNESCAPED_UNICODE);
+
+ $stmt = $db->prepare("
+ INSERT INTO app_cache (cache_key, cache_value, expires_at)
+ VALUES (?, ?, ?)
+ ON DUPLICATE KEY UPDATE
+ cache_value = VALUES(cache_value),
+ expires_at = VALUES(expires_at)
+ ");
+ return $stmt->execute([$key, $valToStore, $expiresAt]);
+ } catch (Throwable $e) {
+ error_log("fociz_cache_set error for {$key}: " . $e->getMessage());
+ return false;
+ }
+}
+
+/**
+ * Verwijdert een specifiek item uit de cache.
+ */
+function fociz_cache_delete(string $key): bool {
+ try {
+ $db = get_db_connection();
+ ensure_app_cache_table($db);
+ $stmt = $db->prepare("DELETE FROM app_cache WHERE cache_key = ?");
+ return $stmt->execute([$key]);
+ } catch (Throwable $e) {
+ return false;
+ }
+}
+
+/**
+ * Verwijdert alle items met een bepaald voorvoegsel (bijv. 'ms_events_1_')
+ */
+function fociz_cache_delete_pattern(string $prefix): bool {
+ try {
+ $db = get_db_connection();
+ ensure_app_cache_table($db);
+ $stmt = $db->prepare("DELETE FROM app_cache WHERE cache_key LIKE ?");
+ return $stmt->execute([$prefix . '%']);
+ } catch (Throwable $e) {
+ return false;
+ }
+}
diff --git a/includes/header.php b/includes/header.php
index fc0a5e6..a52c0f6 100644
--- a/includes/header.php
+++ b/includes/header.php
@@ -165,10 +165,10 @@ require_once __DIR__ . '/env_badge.php';
Fociz
-
+
Mail
-
+
@@ -284,8 +284,11 @@ require_once __DIR__ . '/env_badge.php';
Fociz
-
-
+
+
+
+
+
Mail
diff --git a/includes/integrations/jira.php b/includes/integrations/jira.php
index c9caade..f88d5a7 100644
--- a/includes/integrations/jira.php
+++ b/includes/integrations/jira.php
@@ -6,6 +6,7 @@
require_once __DIR__ . '/../http.php';
require_once __DIR__ . '/../../config/database.php';
+require_once __DIR__ . '/../cache.php';
/**
* Normaliseert het Jira domein naar een schone URL zonder slash aan het einde
@@ -823,15 +824,18 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array
];
}
- // Sessiecache voor 60 seconden
- if (session_status() === PHP_SESSION_NONE) {
- @session_start();
- }
+ // Cache controle (60 seconden)
$cacheKey = "jira_dashboard_user_{$userId}";
- $cached = $_SESSION[$cacheKey] ?? null;
-
- if (!$forceRefresh && $cached && is_array($cached) && ($cached['timestamp'] ?? 0) > (time() - 60)) {
- return array_merge($cached['data'], ['from_cache' => true]);
+ if (!$forceRefresh) {
+ $cachedData = fociz_cache_get($cacheKey);
+ if ($cachedData !== null && is_array($cachedData)) {
+ return array_merge($cachedData, ['from_cache' => true]);
+ }
+ if (session_status() === PHP_SESSION_ACTIVE && !empty($_SESSION[$cacheKey]['data'])) {
+ if (($_SESSION[$cacheKey]['timestamp'] ?? 0) > (time() - 60)) {
+ return array_merge($_SESSION[$cacheKey]['data'], ['from_cache' => true]);
+ }
+ }
}
// Ophalen van toegewezen taken
@@ -865,10 +869,14 @@ function jira_get_dashboard_data(int $userId, bool $forceRefresh = false): array
'default_queue_filter' => $userQueuePref,
];
- $_SESSION[$cacheKey] = [
- 'timestamp' => time(),
- 'data' => $data,
- ];
+ fociz_cache_set($cacheKey, $data, 60);
+
+ if (session_status() === PHP_SESSION_ACTIVE) {
+ $_SESSION[$cacheKey] = [
+ 'timestamp' => time(),
+ 'data' => $data,
+ ];
+ }
// Werk last_sync_at bij in database
$stmtUpdate = $db->prepare("UPDATE integrations SET last_sync_at = NOW() WHERE id = ?");
@@ -902,6 +910,7 @@ function jira_disconnect(int $userId): bool {
}
unset($_SESSION["jira_issues_user_{$userId}"]);
unset($_SESSION["jira_dashboard_user_{$userId}"]);
+ fociz_cache_delete("jira_dashboard_user_{$userId}");
return $stmt->execute([$userId]);
}
diff --git a/includes/integrations/microsoft.php b/includes/integrations/microsoft.php
index 16f3a01..1f83a8c 100644
--- a/includes/integrations/microsoft.php
+++ b/includes/integrations/microsoft.php
@@ -6,6 +6,7 @@
require_once __DIR__ . '/../http.php';
require_once __DIR__ . '/../../config/database.php';
+require_once __DIR__ . '/../cache.php';
const MS_OAUTH_AUTHORIZE_URL = 'https://login.microsoftonline.com/%s/oauth2/v2.0/authorize';
const MS_OAUTH_TOKEN_URL = 'https://login.microsoftonline.com/%s/oauth2/v2.0/token';
@@ -279,7 +280,15 @@ function microsoft_get_valid_token(int $userId, bool $forceRefresh = false): ?st
/**
* Haalt agenda-afspraken van Microsoft Outlook op voor een datumbereik (bijv. een hele week)
*/
-function microsoft_fetch_events_range(int $userId, string $startDate, string $endDate): array {
+function microsoft_fetch_events_range(int $userId, string $startDate, string $endDate, bool $forceRefresh = false): array {
+ $cacheKey = "ms_events_{$userId}_{$startDate}_{$endDate}";
+ if (!$forceRefresh) {
+ $cached = fociz_cache_get($cacheKey);
+ if ($cached !== null && is_array($cached)) {
+ return $cached;
+ }
+ }
+
$token = microsoft_get_valid_token($userId);
if (!$token) {
return [];
@@ -400,6 +409,7 @@ function microsoft_fetch_events_range(int $userId, string $startDate, string $en
];
}
+ fociz_cache_set($cacheKey, $events, 60);
return $events;
}
@@ -520,6 +530,7 @@ function microsoft_create_event(int $userId, array $eventData): array {
}
if ($response['status'] === 201 && is_array($response['json'])) {
+ fociz_cache_delete_pattern("ms_events_{$userId}_");
return [
'success' => true,
'event_id' => $response['json']['id'] ?? null,
@@ -559,7 +570,11 @@ function microsoft_delete_event(int $userId, string $eventId): bool {
'timeout' => 12,
]);
- return in_array($response['status'], [200, 204, 404]);
+ $ok = in_array($response['status'], [200, 204, 404]);
+ if ($ok) {
+ fociz_cache_delete_pattern("ms_events_{$userId}_");
+ }
+ return $ok;
}
/**
@@ -628,7 +643,11 @@ function microsoft_update_event(int $userId, string $eventId, array $eventData):
}
}
- return in_array($response['status'], [200, 204]);
+ $ok = in_array($response['status'], [200, 204]);
+ if ($ok) {
+ fociz_cache_delete_pattern("ms_events_{$userId}_");
+ }
+ return $ok;
}
/**
@@ -985,7 +1004,15 @@ function microsoft_get_mail_demo_data(): array {
/**
* Haalt inbox berichten op van Microsoft Graph API (of retourneert demo data)
*/
-function microsoft_fetch_inbox_messages(int $userId, int $limit = 25, bool $unreadOnly = false): array {
+function microsoft_fetch_inbox_messages(int $userId, int $limit = 25, bool $unreadOnly = false, bool $forceRefresh = false): array {
+ $cacheKey = "ms_inbox_{$userId}_{$limit}_" . ($unreadOnly ? '1' : '0');
+ if (!$forceRefresh) {
+ $cached = fociz_cache_get($cacheKey);
+ if ($cached !== null && is_array($cached)) {
+ return $cached;
+ }
+ }
+
$db = get_db_connection();
$stmt = $db->prepare("SELECT * FROM integrations WHERE user_id = ? AND provider = 'OUTLOOK' AND is_enabled = 1");
$stmt->execute([$userId]);
@@ -1118,13 +1145,15 @@ function microsoft_fetch_inbox_messages(int $userId, int $limit = 25, bool $unre
$unreadCount = count(array_filter($messages, function($m) { return empty($m['is_read']); }));
- return [
+ $result = [
'success' => true,
'is_demo' => false,
'messages' => $messages,
'unread_count' => $unreadCount,
'total_count' => count($messages),
];
+ fociz_cache_set($cacheKey, $result, 45); // 45 seconden cache
+ return $result;
}
/**
@@ -1239,7 +1268,11 @@ function microsoft_mark_message_read(int $userId, string $messageId, bool $isRea
'timeout' => 10,
]);
- return ($res['status'] >= 200 && $res['status'] < 300);
+ $ok = ($res['status'] >= 200 && $res['status'] < 300);
+ if ($ok) {
+ fociz_cache_delete_pattern("ms_inbox_{$userId}_");
+ }
+ return $ok;
}
/**
@@ -1271,6 +1304,7 @@ function microsoft_reply_message(int $userId, string $messageId, string $comment
]);
if ($res['status'] >= 200 && $res['status'] < 300) {
+ fociz_cache_delete_pattern("ms_inbox_{$userId}_");
return ['success' => true, 'message' => 'Antwoord succesvol verzonden via Outlook.'];
}
diff --git a/includes/settings.php b/includes/settings.php
index 43bbd66..d0f6dd3 100644
--- a/includes/settings.php
+++ b/includes/settings.php
@@ -29,36 +29,42 @@ function ensure_system_settings_table(?PDO $db = null): void {
}
/**
- * Haalt een enkele instelling op uit MariaDB
+ * Haalt alle systeeminstellingen in 1 geoptimaliseerde query op en bewaart deze in het static runtime geheugen
*/
-function get_system_setting(string $key, $default = null) {
+function get_all_system_settings(bool $forceRefresh = false): array {
+ static $settingsCache = null;
+ if ($settingsCache !== null && !$forceRefresh) {
+ return $settingsCache;
+ }
+
+ $settingsCache = [];
try {
$db = get_db_connection();
ensure_system_settings_table($db);
- $stmt = $db->prepare("SELECT setting_value FROM system_settings WHERE setting_key = ?");
- $stmt->execute([$key]);
- $val = $stmt->fetchColumn();
-
- if ($val === false || $val === null) {
- return $default;
+ $stmt = $db->query("SELECT setting_key, setting_value FROM system_settings");
+ while ($row = $stmt->fetch()) {
+ $val = $row['setting_value'];
+ $decoded = json_decode($val, true);
+ $settingsCache[$row['setting_key']] = (json_last_error() === JSON_ERROR_NONE) ? $decoded : $val;
}
-
- // Decodeer JSON indien van toepassing
- $decoded = json_decode($val, true);
- if (json_last_error() === JSON_ERROR_NONE) {
- return $decoded;
- }
-
- return $val;
} catch (Throwable $e) {
- error_log("get_system_setting error for {$key}: " . $e->getMessage());
- return $default;
+ error_log("get_all_system_settings error: " . $e->getMessage());
}
+
+ return $settingsCache;
}
/**
- * Slaat een instelling op in MariaDB
+ * Haalt een enkele instelling op uit het geheugen (of fallback default)
+ */
+function get_system_setting(string $key, $default = null) {
+ $all = get_all_system_settings();
+ return array_key_exists($key, $all) ? $all[$key] : $default;
+}
+
+/**
+ * Slaat een instelling op in MariaDB en ververst direct het cachegeheugen
*/
function set_system_setting(string $key, $value): bool {
try {
@@ -72,7 +78,11 @@ function set_system_setting(string $key, $value): bool {
VALUES (?, ?)
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), updated_at = NOW()
");
- return $stmt->execute([$key, $valToStore]);
+ $ok = $stmt->execute([$key, $valToStore]);
+ if ($ok) {
+ get_all_system_settings(true);
+ }
+ return $ok;
} catch (Throwable $e) {
error_log("set_system_setting error for {$key}: " . $e->getMessage());
return false;
diff --git a/mail.php b/mail.php
index b4772e9..9577c8d 100644
--- a/mail.php
+++ b/mail.php
@@ -11,8 +11,9 @@ require_once __DIR__ . '/includes/integrations/microsoft.php';
$userId = (int)($currentUser['id'] ?? 0);
$db = get_db_connection();
-// Haal initiële inbox data op
-$inboxData = microsoft_fetch_inbox_messages($userId, 30);
+// Haal initiële inbox data op (met 45s cache, tenzij ?refresh=1)
+$forceRefresh = !empty($_GET['refresh']) && $_GET['refresh'] === '1';
+$inboxData = microsoft_fetch_inbox_messages($userId, 30, false, $forceRefresh);
$messages = $inboxData['messages'] ?? [];
$isDemo = !empty($inboxData['is_demo']);
$unreadCount = (int)($inboxData['unread_count'] ?? 0);
@@ -720,7 +721,7 @@ async function refreshMailbox() {
if (icon) icon.classList.add('animate-spin');
try {
- const res = await fetch('/api/mail.php?action=get_inbox');
+ const res = await fetch('/api/mail.php?action=get_inbox&refresh=1');
const data = await res.json();
if (data.success && data.messages) {
allMessages = data.messages;
diff --git a/planner.php b/planner.php
index 631f386..af1a02a 100644
--- a/planner.php
+++ b/planner.php
@@ -79,14 +79,24 @@ $syncIntervalSeconds = isset($outlookIntegrationSettings['sync_interval_seconds'
$weekOutlookEvents = [];
$isLiveOutlook = false;
+$forceRefresh = !empty($_GET['refresh']) && $_GET['refresh'] === '1';
+
if ($isOutlookConnected) {
try {
- // Haal afspraken op voor het volledige datumbereik van de week
- $weekOutlookEvents = microsoft_fetch_events_range($userId, $weekStart, $weekEnd);
+ // Haal afspraken op voor het volledige datumbereik van de week (met 60s cache)
+ $weekOutlookEvents = microsoft_fetch_events_range($userId, $weekStart, $weekEnd, $forceRefresh);
$isLiveOutlook = true;
// Synchroniseer / reconcilieer Fociz taken direct met actuele Outlook data
- microsoft_reconcile_tasks_with_outlook($userId, $weekStart, $weekEnd, $weekOutlookEvents);
+ // Alleen uitvoeren indien events gewijzigd zijn of bij expliciete refresh om overtollige DB load te voorkomen
+ $eventsHash = md5(json_encode($weekOutlookEvents));
+ $lastReconciledHash = $_SESSION["last_reconciled_{$userId}"] ?? '';
+ if ($eventsHash !== $lastReconciledHash || $forceRefresh) {
+ microsoft_reconcile_tasks_with_outlook($userId, $weekStart, $weekEnd, $weekOutlookEvents);
+ if (session_status() === PHP_SESSION_ACTIVE) {
+ $_SESSION["last_reconciled_{$userId}"] = $eventsHash;
+ }
+ }
} catch (Throwable $e) {
error_log("Outlook synchronisatiefout in planner: " . $e->getMessage());
$isLiveOutlook = false;
@@ -224,7 +234,7 @@ for ($i = 0; $i < 7; $i++) {
$jiraIssues = [];
$isJiraLive = false;
try {
- $jiraData = jira_get_user_issues($userId);
+ $jiraData = jira_get_user_issues($userId, $forceRefresh);
$jiraIssues = $jiraData['issues'] ?? [];
$isJiraLive = !empty($jiraData['success']) && empty($jiraData['is_demo']);
} catch (Throwable $e) {
@@ -236,7 +246,7 @@ $recentEmails = [];
$unreadEmailCount = 0;
$mailNeedsReconsent = false;
try {
- $mailData = microsoft_fetch_inbox_messages($userId, 5);
+ $mailData = microsoft_fetch_inbox_messages($userId, 5, false, $forceRefresh);
$recentEmails = $mailData['messages'] ?? [];
$unreadEmailCount = (int)($mailData['unread_count'] ?? 0);
$mailNeedsReconsent = !empty($mailData['needs_reconsent']);
diff --git a/profile.php b/profile.php
index 64bced7..3e3c637 100644
--- a/profile.php
+++ b/profile.php
@@ -840,8 +840,8 @@ require_once __DIR__ . '/includes/header.php';
-
-
+
+
@@ -1122,16 +1122,18 @@ function updateProfileThemeUI(theme) {
function toggleJiraToken() {
const input = document.getElementById('jiraApiTokenInput');
- const icon = document.getElementById('eyeJiraIcon');
- if (!input || !icon) return;
+ const btn = document.getElementById('toggleJiraTokenBtn');
+ if (!input) return;
if (input.type === 'password') {
input.type = 'text';
- icon.setAttribute('data-lucide', 'eye-off');
+ if (btn) btn.innerHTML = '';
} else {
input.type = 'password';
- icon.setAttribute('data-lucide', 'eye');
+ if (btn) btn.innerHTML = '';
+ }
+ if (window.lucide && typeof lucide.createIcons === 'function') {
+ lucide.createIcons();
}
- if (window.lucide) lucide.createIcons();
}
function previewUploadAvatar(input) {