diff --git a/index.php b/index.php index bbcd106..8ba9039 100644 --- a/index.php +++ b/index.php @@ -65,6 +65,29 @@ if ($action === 'login') { exit; } +// Handle POST actions that need a redirect BEFORE layout outputs any HTML. +// Adding new redirect-based POST keys here keeps header() calls safe. +if ($_SERVER['REQUEST_METHOD'] === 'POST' && $user_id) { + // Mood setter + if (isset($_POST['set_mood'])) { + try { $pdo->exec("ALTER TABLE users ADD COLUMN mood TEXT DEFAULT NULL"); } catch(Exception $e) {} + try { $pdo->exec("ALTER TABLE users ADD COLUMN mood_updated_at DATETIME DEFAULT NULL"); } catch(Exception $e) {} + try { $pdo->exec("CREATE TABLE IF NOT EXISTS activity_log (id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER, action TEXT NOT NULL, detail TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE)"); } catch(Exception $e) {} + $mood = trim((string)($_POST['mood'] ?? '')); + $allowed = ['๐Ÿ˜Š','๐Ÿ˜ข','๐Ÿ˜ ','๐Ÿ˜ด','๐Ÿคฉ','๐Ÿ˜Ÿ','๐Ÿ˜Ž','๐Ÿค”','๐Ÿ˜','๐Ÿฅณ']; + if (in_array($mood, $allowed) || $mood === '') { + $pdo->prepare("UPDATE users SET mood=?, mood_updated_at=CURRENT_TIMESTAMP WHERE id=?") + ->execute([$mood ?: null, $user_id]); + try { + $pdo->prepare("INSERT INTO activity_log (user_id, action, detail) VALUES (?, 'mood_set', ?)") + ->execute([$user_id, $mood ?: 'cleared']); + } catch(Exception $e) {} + } + header('Location: ?action=dashboard'); + exit; + } +} + $view_file = 'views/' . basename($action) . '.php'; if (file_exists($view_file)) { // AJAX-only POST requests must not have the HTML layout wrapped around them, diff --git a/views/dashboard.php b/views/dashboard.php index a7607b9..44ab63b 100644 --- a/views/dashboard.php +++ b/views/dashboard.php @@ -1,96 +1,451 @@ - prepare("SELECT status, COUNT(*) as count FROM tasks WHERE (assignee_type='user' AND assignee_id=?) OR (assignee_type='custom' AND assignor_id=?) GROUP BY status"); -$stmt->execute([$user_id, $user_id]); -$task_counts = ['pending' => 0, 'in_progress' => 0, 'completed' => 0]; -foreach($stmt->fetchAll() as $row) { - if(isset($task_counts[$row['status']])) $task_counts[$row['status']] = $row['count']; -} -$total_tasks = array_sum($task_counts); +// โ”€โ”€ DB Migrations (safe, idempotent) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +try { $pdo->exec("ALTER TABLE users ADD COLUMN mood TEXT DEFAULT NULL"); } catch(Exception $e) {} +try { $pdo->exec("ALTER TABLE users ADD COLUMN mood_updated_at DATETIME DEFAULT NULL"); } catch(Exception $e) {} +try { + $pdo->exec("CREATE TABLE IF NOT EXISTS activity_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER, + action TEXT NOT NULL, + detail TEXT, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE + )"); +} catch(Exception $e) {} -// Calculate Passwords count -$stmt = $pdo->prepare("SELECT COUNT(*) FROM passwords p LEFT JOIN password_shares ps ON p.id = ps.password_id WHERE p.owner_id = ? OR (ps.share_type='user' AND ps.share_id=?)"); + +// โ”€โ”€ Task Counts โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +$user_groups = get_user_groups($user_id); +$my_group_ids = array_column($user_groups, 'id'); +$group_in = implode(',', array_fill(0, max(count($my_group_ids), 1), '?')) ?: '0'; +$params_tasks = array_merge([$user_id, $user_id], $my_group_ids, $my_group_ids, [$user_id]); +$sql_tasks = "SELECT status, COUNT(*) as cnt FROM tasks WHERE + (assignee_type='user' AND assignee_id=?) + OR (assignor_type='user' AND assignor_id=?) + OR (assignee_type='group' AND assignee_id IN ($group_in)) + OR (assignor_type='group' AND assignor_group_id IN ($group_in)) + OR (assignor_id=?) + GROUP BY status"; +$stmt = $pdo->prepare($sql_tasks); +$stmt->execute($params_tasks); +$task_counts = ['pending'=>0,'in_progress'=>0,'completed'=>0,'archived'=>0]; +foreach($stmt->fetchAll() as $row) { + if (isset($task_counts[$row['status']])) $task_counts[$row['status']] = $row['cnt']; +} +$total_tasks = $task_counts['pending'] + $task_counts['in_progress'] + $task_counts['completed']; + +// โ”€โ”€ Password & Project counts โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +$stmt = $pdo->prepare("SELECT COUNT(*) FROM passwords p LEFT JOIN password_shares ps ON p.id=ps.password_id WHERE p.owner_id=? OR (ps.share_type='user' AND ps.share_id=?)"); $stmt->execute([$user_id, $user_id]); $passwords_count = $stmt->fetchColumn(); -// Calculate Projects count -$stmt = $pdo->prepare("SELECT COUNT(*) FROM projects p LEFT JOIN project_shares ps ON p.id = ps.project_id WHERE p.owner_id = ? OR (ps.share_type='user' AND ps.share_id=?)"); +$stmt = $pdo->prepare("SELECT COUNT(*) FROM projects p LEFT JOIN project_shares ps ON p.id=ps.project_id WHERE p.owner_id=? OR (ps.share_type='user' AND ps.share_id=?)"); $stmt->execute([$user_id, $user_id]); $projects_count = $stmt->fetchColumn(); +// โ”€โ”€ Notes count โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +$notes_count = 0; +try { + $stmt = $pdo->prepare("SELECT COUNT(*) FROM notes WHERE owner_id=?"); + $stmt->execute([$user_id]); + $notes_count = $stmt->fetchColumn(); +} catch(Exception $e) {} + +// โ”€โ”€ Current user mood โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +$my_mood = $current_user['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(); + +// โ”€โ”€ Activity log (user + their groups, last 20) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +$recent_logs = []; +try { + // Collect relevant user IDs: current user + members of same groups + $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); + $recent_logs = $stmt->fetchAll(); +} catch(Exception $e) {} + +// โ”€โ”€ Auto-log task changes for history (read recent task status changes) โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +$task_logs = []; +try { + // Include group-assigned tasks too + $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); + $task_logs = $stmt->fetchAll(); +} catch(Exception $e) {} ?> -
-

Dashboard

-
-
+ + +prepare("INSERT INTO activity_log (user_id, action, detail) VALUES (?, 'visit', 'Opened dashboard')")->execute([$user_id]); + } catch(Exception $e) {} + $_SESSION['dash_logged_today'] = date('Y-m-d'); +} +?> + +
+ +
-

Welcome, !

-

+

๐Ÿ‘‹ Welcome, !

+

+ +

+ Groups: htmlspecialchars($g['name']), $user_groups)) ?> +

+
-
-
Holiday Info:
-
Loading...
+ +
+
Your Mood
+
+ + +
+ + + + + + +
+
+ +
Currently:
+
-
- -
-

My Tasks Overview

-

Total

-
-
-
Pending
-
-
-
-
In Progress
-
-
-
-
Completed
-
-
+ + + + + - - - + + +
+ + +
+
+

๐Ÿ‘ฅ Team Mood

+ Live from colleagues +
+ +

No one else has set a mood yet.

+ +
+ +
+ + +
+ +
+ + +
+ Your mood is visible to your team: +
+
- -
-

My Projects & Files

-

Files/Folders

- Go to Explorer → + +
+
+

๐Ÿ•“ Recent Activity

+ You & your groups +
+ $l['created_at'], + 'user' => $l['username'] ?? 'System', + 'action' => $l['action'], + 'detail' => $l['detail'] ?? '', + ]; + } + foreach($task_logs as $l) { + $status_label = ['pending'=>'moved to Pending','in_progress'=>'started In Progress','completed'=>'marked Completed','archived'=>'archived'][$l['status']] ?? $l['status']; + $feed[] = [ + 'time' => $l['status_updated_at'], + 'user' => $l['assignor'] ?? 'Someone', + 'action' => 'task_status', + 'detail' => '"'.htmlspecialchars(mb_substr($l['title'],0,30)).'" '.$status_label, + ]; + } + usort($feed, fn($a,$b) => strtotime($b['time']) - strtotime($a['time'])); + $feed = array_slice($feed, 0, 30); + + $action_icons = [ + 'visit' => '๐ŸŒ', + 'mood_set' => '๐Ÿ˜Š', + 'task_status' => 'โœ…', + ]; + ?> + +

No activity yet.

+ +
+ +
+
+
+ + +
+
+
+ +
+
+
+
+ +