feat: integrate Tagify for multi-tag support and add a new view-note modal across the application
This commit is contained in:
@@ -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'])) {
|
||||
</div>
|
||||
<div class="db-stat-link">Open Notes →</div>
|
||||
</a>
|
||||
|
||||
<!-- Tools -->
|
||||
<a href="?action=tools" class="db-stat-card" style="border-top:3px solid #EC4899;">
|
||||
<div class="db-stat-label">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 9.36l-7.1 7.1a1 1 0 0 1-1.4 0l-2.8-2.8a1 1 0 0 1 0-1.4l7.1-7.1a6 6 0 0 1 9.36-7.94z"></path></svg>
|
||||
Tools
|
||||
</div>
|
||||
<div class="db-stat-value" style="color:#EC4899;">3</div>
|
||||
<div class="db-stat-sub">
|
||||
<span style="background:rgba(236,72,153,0.12); color:#EC4899;">🛠 Utilities</span>
|
||||
</div>
|
||||
<div class="db-stat-link">Open Tools →</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- ── Team Moods ───────────────────────────────────────────── -->
|
||||
@@ -301,19 +322,46 @@ if (empty($_SESSION['dash_logged_today'])) {
|
||||
<h3 style="font-size:1rem; font-weight:700;">👥 Team Mood</h3>
|
||||
<span style="font-size:0.75rem; color:var(--text-light); font-style:italic;">Live from colleagues</span>
|
||||
</div>
|
||||
<?php if(empty($team_moods)): ?>
|
||||
<p style="color:var(--text-light); font-size:0.85rem;">No one else has set a mood yet.</p>
|
||||
<?php if(empty($team_moods) && empty($my_mood)): ?>
|
||||
<p style="color:var(--text-light); font-size:0.85rem;">No team mood activity yet.</p>
|
||||
<?php else: ?>
|
||||
<div class="team-mood-row" style="display:flex; flex-wrap:nowrap; overflow-x:auto; gap:1rem; padding-bottom:0.5rem;">
|
||||
<?php
|
||||
$avatar_colors_tm = ['#4F46E5','#10B981','#F59E0B','#EF4444','#8B5CF6','#06B6D4','#EC4899','#84CC16'];
|
||||
|
||||
if(!empty($my_mood)):
|
||||
$m_initial = strtoupper(mb_substr($me['username'], 0, 1));
|
||||
$m_color = $avatar_colors_tm[crc32($me['username']) % count($avatar_colors_tm)];
|
||||
?>
|
||||
<div style="position:relative; flex-shrink:0; cursor:pointer;" title="You (Updated: <?= date('M j, H:i', strtotime($me['mood_updated_at'])) ?>)">
|
||||
<div style="width:40px; height:40px; border-radius:50%; background:<?= $m_color ?>; color:#fff; display:flex; align-items:center; justify-content:center; font-weight:700; font-size:1rem; overflow:hidden;">
|
||||
<?php if(!empty($me['profile_pic']) && file_exists($me['profile_pic'])): ?>
|
||||
<img src="<?= htmlspecialchars($me['profile_pic']) ?>?v=<?= time() ?>" style="width:100%; height:100%; object-fit:cover;">
|
||||
<?php else: ?>
|
||||
<?= $m_initial ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div style="position:absolute; top:-4px; right:-4px; background:var(--surface); border-radius:50%; width:20px; height:20px; display:flex; align-items:center; justify-content:center; font-size:0.8rem; box-shadow:0 2px 4px rgba(0,0,0,0.1);">
|
||||
<?= $my_mood ?>
|
||||
</div>
|
||||
<div style="font-size:0.65rem; color:var(--text-light); text-align:center; margin-top:4px; max-width:40px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; font-style:italic;">
|
||||
You
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
foreach($team_moods as $tm):
|
||||
$initial = strtoupper(mb_substr($tm['username'], 0, 1));
|
||||
$color = $avatar_colors_tm[crc32($tm['username']) % count($avatar_colors_tm)];
|
||||
?>
|
||||
<div style="position:relative; flex-shrink:0; cursor:pointer;" title="<?= htmlspecialchars($tm['username']) ?> (Updated: <?= date('M j, H:i', strtotime($tm['mood_updated_at'])) ?>)">
|
||||
<div style="width:40px; height:40px; border-radius:50%; background:<?= $color ?>; color:#fff; display:flex; align-items:center; justify-content:center; font-weight:700; font-size:1rem;">
|
||||
<?= $initial ?>
|
||||
<div style="width:40px; height:40px; border-radius:50%; background:<?= $color ?>; color:#fff; display:flex; align-items:center; justify-content:center; font-weight:700; font-size:1rem; overflow:hidden;">
|
||||
<?php if(!empty($tm['profile_pic']) && file_exists($tm['profile_pic'])): ?>
|
||||
<img src="<?= htmlspecialchars($tm['profile_pic']) ?>?v=<?= time() ?>" style="width:100%; height:100%; object-fit:cover;">
|
||||
<?php else: ?>
|
||||
<?= $initial ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div style="position:absolute; top:-4px; right:-4px; background:var(--surface); border-radius:50%; width:20px; height:20px; display:flex; align-items:center; justify-content:center; font-size:0.8rem; box-shadow:0 2px 4px rgba(0,0,0,0.1);">
|
||||
<?= $tm['mood'] ?>
|
||||
@@ -325,11 +373,6 @@ if (empty($_SESSION['dash_logged_today'])) {
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if($my_mood): ?>
|
||||
<div style="margin-top:0.5rem; font-size:0.8rem; color:var(--text-light);">
|
||||
Your mood is visible to your team: <strong style="color:var(--text);"><?= $my_mood ?></strong>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<!-- ── Activity Log ───────────────────────────────────────────── -->
|
||||
@@ -337,12 +380,9 @@ if (empty($_SESSION['dash_logged_today'])) {
|
||||
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:1rem;">
|
||||
<div style="display:flex; align-items:center; gap:0.65rem;">
|
||||
<h3 style="font-size:1rem; font-weight:700;">🕓 Recent Activity</h3>
|
||||
<?php if($is_admin_user): ?>
|
||||
<span class="admin-activity-badge"><i class="fa fa-shield-halved" style="font-size:0.65rem;"></i> All Users</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<span style="font-size:0.75rem; color:var(--text-light);">
|
||||
<?= $is_admin_user ? 'All activity across platform' : 'You & your groups' ?>
|
||||
You & your groups
|
||||
</span>
|
||||
</div>
|
||||
<?php
|
||||
|
||||
Reference in New Issue
Block a user