diff --git a/views/admin.php b/views/admin.php index a0fec52..5cae6fd 100644 --- a/views/admin.php +++ b/views/admin.php @@ -256,7 +256,7 @@ $active_tab = $_GET['tab'] ?? 'overview'; -
+
@@ -443,10 +443,10 @@ $active_tab = $_GET['tab'] ?? 'overview';

Danger Zone

These actions are irreversible. Proceed with extreme caution.

- -
diff --git a/views/dashboard.php b/views/dashboard.php index c9599e0..eba04a5 100644 --- a/views/dashboard.php +++ b/views/dashboard.php @@ -52,61 +52,69 @@ try { } catch(Exception $e) {} // ── Current user mood ───────────────────────────────────────────────────────── -$my_mood = $current_user['mood'] ?? null; +$me = query("SELECT username, profile_pic, mood, mood_updated_at FROM users WHERE id=?", [$user_id])->fetch(); +$my_mood = $me['mood'] ?? null; // ── Team moods (all users + their mood) ────────────────────────────────────── -$team_moods = query("SELECT id, username, mood, mood_updated_at FROM users WHERE mood IS NOT NULL AND id != ? ORDER BY mood_updated_at DESC", [$user_id])->fetchAll(); +$team_moods = query("SELECT id, username, mood, mood_updated_at, profile_pic FROM users WHERE mood IS NOT NULL AND id != ? ORDER BY mood_updated_at DESC", [$user_id])->fetchAll(); // ── Activity log ────────────────────────────────────────────────────────────── -// Admin: sees ALL activity across all users & groups -// Standard user: sees own activity + group members activity +// Admin: No longer sees all activity by default. +// Standard user/Admin: sees own activity + group members activity, BUT we hide admin activity from non-self. $is_admin_user = !empty($_SESSION['is_admin']); + +$peer_ids = [$user_id]; +if ($my_group_ids) { + $gin = implode(',', array_fill(0, count($my_group_ids), '?')); + $peers = $pdo->prepare("SELECT DISTINCT user_id FROM user_groups WHERE group_id IN ($gin)"); + $peers->execute($my_group_ids); + $peer_ids = array_unique(array_merge($peer_ids, array_column($peers->fetchAll(), 'user_id'))); +} + +// Ensure at least the user themselves is present +if(empty($peer_ids)) { $peer_ids = [$user_id]; } + +// Get all admins except the current user +$other_admins = []; +try { + $other_admins = $pdo->prepare("SELECT id FROM users WHERE is_admin = 1 AND id != ?"); + $other_admins->execute([$user_id]); + $other_admins = $other_admins->fetchAll(PDO::FETCH_COLUMN); +} catch(Exception $e) {} + +// Filter out other admins from peer ids +$peer_ids = array_filter($peer_ids, function($id) use ($other_admins) { + return !in_array($id, $other_admins); +}); + +$uid_in = implode(',', array_fill(0, count($peer_ids), '?')); $recent_logs = []; try { - if ($is_admin_user) { - // Admin: fetch all activity - $stmt = $pdo->prepare("SELECT al.*, u.username FROM activity_log al LEFT JOIN users u ON al.user_id=u.id ORDER BY al.created_at DESC LIMIT 40"); - $stmt->execute(); - } else { - // Standard: only own + group peers - $peer_ids = [$user_id]; - if ($my_group_ids) { - $gin = implode(',', array_fill(0, count($my_group_ids), '?')); - $peers = $pdo->prepare("SELECT DISTINCT user_id FROM user_groups WHERE group_id IN ($gin)"); - $peers->execute($my_group_ids); - $peer_ids = array_unique(array_merge($peer_ids, array_column($peers->fetchAll(), 'user_id'))); - } - $uid_in = implode(',', array_fill(0, count($peer_ids), '?')); - $stmt = $pdo->prepare("SELECT al.*, u.username FROM activity_log al LEFT JOIN users u ON al.user_id=u.id WHERE al.user_id IN ($uid_in) ORDER BY al.created_at DESC LIMIT 20"); - $stmt->execute($peer_ids); - } + $stmt = $pdo->prepare("SELECT al.*, u.username FROM activity_log al LEFT JOIN users u ON al.user_id=u.id WHERE al.user_id IN ($uid_in) ORDER BY al.created_at DESC LIMIT 30"); + $stmt->execute(array_values($peer_ids)); $recent_logs = $stmt->fetchAll(); } catch(Exception $e) {} // ── Task logs ───────────────────────────────────────────────────────────────── $task_logs = []; try { - if ($is_admin_user) { - // Admin: all recent task status changes - $stmt = $pdo->prepare("SELECT t.title, t.status, t.status_updated_at, u.username as assignor - FROM tasks t LEFT JOIN users u ON t.assignor_id=u.id - WHERE t.status_updated_at IS NOT NULL - ORDER BY t.status_updated_at DESC LIMIT 40"); - $stmt->execute(); - } else { - $group_in_tl = $my_group_ids ? implode(',', array_fill(0, count($my_group_ids), '?')) : '0'; - $tl_params = array_merge([$user_id, $user_id], $my_group_ids, $my_group_ids); - $stmt = $pdo->prepare("SELECT t.title, t.status, t.status_updated_at, u.username as assignor - FROM tasks t LEFT JOIN users u ON t.assignor_id=u.id - WHERE t.status_updated_at IS NOT NULL - AND ( - t.assignee_id=? OR t.assignor_id=? - OR (t.assignee_type='group' AND t.assignee_id IN ($group_in_tl)) - OR (t.assignor_type='group' AND t.assignor_group_id IN ($group_in_tl)) - ) - ORDER BY t.status_updated_at DESC LIMIT 30"); - $stmt->execute($tl_params); - } + $group_in_tl = $my_group_ids ? implode(',', array_fill(0, count($my_group_ids), '?')) : '0'; + $tl_params = array_merge([$user_id, $user_id], $my_group_ids, $my_group_ids); + + $admin_excl = $other_admins ? "AND t.assignor_id NOT IN (" . implode(',', array_fill(0, count($other_admins), '?')) . ")" : ""; + if($other_admins) { $tl_params = array_merge($tl_params, $other_admins); } + + $stmt = $pdo->prepare("SELECT t.title, t.status, t.status_updated_at, u.username as assignor + FROM tasks t LEFT JOIN users u ON t.assignor_id=u.id + WHERE t.status_updated_at IS NOT NULL + AND ( + t.assignee_id=? OR t.assignor_id=? + OR (t.assignee_type='group' AND t.assignee_id IN ($group_in_tl)) + OR (t.assignor_type='group' AND t.assignor_group_id IN ($group_in_tl)) + ) + $admin_excl + ORDER BY t.status_updated_at DESC LIMIT 30"); + $stmt->execute($tl_params); $task_logs = $stmt->fetchAll(); } catch(Exception $e) {} ?> @@ -293,6 +301,19 @@ if (empty($_SESSION['dash_logged_today'])) { + + + +
+ + Tools +
+
3
+
+ 🛠 Utilities +
+ +
@@ -301,19 +322,46 @@ if (empty($_SESSION['dash_logged_today'])) {

👥 Team Mood

Live from colleagues - -

No one else has set a mood yet.

+ +

No team mood activity yet.

+
+
+ + + + + +
+
+ +
+
+ You +
+
+ + +
-
- +
+ + + + +
@@ -325,11 +373,6 @@ if (empty($_SESSION['dash_logged_today'])) {
- -
- Your mood is visible to your team: -
-
@@ -337,12 +380,9 @@ if (empty($_SESSION['dash_logged_today'])) {

🕓 Recent Activity

- - All Users -
- + You & your groups
t.classList.remove('show'), 3000); } + // ── Custom Alert / Confirm ───────────────────────────────────────────── + function appAlert(msg) { + return new Promise(resolve => { + let modal = document.getElementById('app-alert-modal'); + if(!modal) { + modal = document.createElement('div'); + modal.id = 'app-alert-modal'; + modal.className = 'modal-overlay'; + modal.style.zIndex = '10005'; + modal.innerHTML = ` + `; + document.body.appendChild(modal); + } + document.getElementById('app-alert-msg').textContent = msg; + modal.classList.add('active'); + document.getElementById('app-alert-ok').onclick = () => { modal.classList.remove('active'); resolve(true); }; + }); + } + window.alert = function(msg) { appAlert(msg); }; + + function appConfirm(msg) { + return new Promise(resolve => { + let modal = document.getElementById('app-confirm-modal'); + if(!modal) { + modal = document.createElement('div'); + modal.id = 'app-confirm-modal'; + modal.className = 'modal-overlay'; + modal.style.zIndex = '10005'; + modal.innerHTML = ` + `; + document.body.appendChild(modal); + } + document.getElementById('app-confirm-msg').textContent = msg; + modal.classList.add('active'); + document.getElementById('app-confirm-no').onclick = () => { modal.classList.remove('active'); resolve(false); }; + document.getElementById('app-confirm-yes').onclick = () => { modal.classList.remove('active'); resolve(true); }; + }); + } + + function appConfirmUrl(event, url, msg) { + event.preventDefault(); + appConfirm(msg).then(yes => { if(yes) window.location.href = url; }); + } + // ── Sidebar Collapse ────────────────────────────────────────────────── function toggleSidebar() { const collapsed = document.body.classList.toggle('sidebar-collapsed'); @@ -87,6 +142,19 @@ document.getElementById('change-pw-modal').classList.add('active'); }); + + // ── Tagify globally ─────────────────────────────────────────────────── + document.addEventListener('DOMContentLoaded', () => { + if(typeof Tagify !== 'undefined' && window.APP_GLOBAL_TAGS) { + document.querySelectorAll('.tagify-input').forEach(el => { + new Tagify(el, { + whitelist: window.APP_GLOBAL_TAGS, + dropdown: { maxItems: 20, classname: "tags-look", enabled: 0, closeOnSelect: false }, + originalInputValueFormat: valuesArr => valuesArr.map(item => item.value).join(',') + }); + }); + } + }); diff --git a/views/layout_top.php b/views/layout_top.php index ea32245..2f7adfe 100644 --- a/views/layout_top.php +++ b/views/layout_top.php @@ -7,6 +7,26 @@ + + + + query("SELECT label_name as tag FROM notes WHERE label_name IS NOT NULL AND label_name != ''")->fetchAll(); + foreach($rows as $r) { foreach(explode(',', $r['tag']) as $p) { $all_app_tags[trim($p)] = 1; } } + $rows = $pdo->query("SELECT tags as tag FROM tasks WHERE tags IS NOT NULL AND tags != ''")->fetchAll(); + foreach($rows as $r) { foreach(explode(',', $r['tag']) as $p) { $all_app_tags[trim($p)] = 1; } } + } catch(Exception $e) {} + $global_tags = array_keys($all_app_tags); + ?> +