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) {} ?> -
+
+ Groups: = implode(', ', array_map(fn($g) => htmlspecialchars($g['name']), $user_groups)) ?> +
+No one else has set a mood yet.
+ +No activity yet.
+ +