Files
Task-Management/views/layout_top.php

278 lines
19 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Task Management</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<link rel="stylesheet" href="public/css/style.css">
<script src="https://cdn.jsdelivr.net/npm/@yaireo/tagify"></script>
<link href="https://cdn.jsdelivr.net/npm/@yaireo/tagify/dist/tagify.css" rel="stylesheet" type="text/css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/themes/dark.css">
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<style>
.tags-look .tagify__dropdown__item{ display: inline-block; border-radius: 3px; padding: .3em .5em; border: 1px solid var(--border); background: var(--surface); margin: .2em; font-size: .85em; transition: 0s; color: var(--text); }
.tags-look .tagify__dropdown__item--active{ color: white; background: var(--primary); border-color: var(--primary); }
.tags-look .tagify__dropdown__item:hover{ background: var(--primary); color: white; border-color: var(--primary); }
/* Premium Dark Mode Tagify Sync */
.tagify { --tag-bg: rgba(var(--primary-rgb), 0.2); --tag-text-color: var(--text); --tag-hover: rgba(var(--primary-rgb), 0.3); --placeholder-color: var(--text-light); }
body.dark-mode .tagify { --tag-bg: #2d3748; --tag-text-color: #e2e8f0; --tag-hover: #4a5568; }
.tagify__tag > div { background: var(--tag-bg); color: var(--tag-text-color); border-radius: 6px; }
.tagify__tag:hover:not([readonly]) > div::before { background: var(--tag-hover); }
.tagify__dropdown { background: rgba(30, 41, 59, 0.95); backdrop-filter: blur(8px); border: 1px solid var(--border); border-radius: 8px; }
.tagify__dropdown__wrapper { background: transparent; border: none; }
</style>
<?php
date_default_timezone_set('Etc/GMT-7');
$all_app_tags = [];
try {
$rows = $pdo->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);
?>
<script>
window.APP_GLOBAL_TAGS = <?= json_encode(array_filter($global_tags)) ?>;
</script>
</head>
<body>
<script>
if(localStorage.getItem('theme') === 'dark') document.body.classList.add('dark-mode');
if(localStorage.getItem('sidebar_collapsed') === '1') document.body.classList.add('sidebar-collapsed');
</script>
<div id="toast"></div>
<!-- Profile Picture Modal -->
<div class="modal-overlay" id="profile-pic-modal">
<div class="modal-content" style="max-width:420px;">
<h3 style="margin-bottom:1rem; font-size:1.1rem;">Profile Picture</h3>
<div style="display:flex; flex-direction:column; align-items:center; gap:1rem; margin-bottom:1.5rem;">
<?php
try { $pdo->exec("ALTER TABLE users ADD COLUMN profile_pic TEXT DEFAULT NULL"); } catch(Exception $e) {}
$pic = $current_user['profile_pic'] ?? null;
?>
<?php if($pic && file_exists($pic)): ?>
<img src="<?= htmlspecialchars($pic) ?>" alt="Profile" style="width:100px; height:100px; border-radius:50%; object-fit:cover; border:3px solid var(--primary);">
<?php else: ?>
<div style="width:100px; height:100px; border-radius:50%; background:var(--primary); display:flex; align-items:center; justify-content:center; font-size:2.5rem; color:#fff; font-weight:700;">
<?= strtoupper(mb_substr($current_user['username'],0,1)) ?>
</div>
<?php endif; ?>
</div>
<form method="POST" enctype="multipart/form-data" style="margin-bottom:1rem;">
<input type="hidden" name="upload_profile_pic" value="1">
<input type="hidden" name="redirect_to" value="<?= htmlspecialchars($action) ?>">
<div class="form-group">
<label style="font-size:0.875rem; color:var(--text-light);">Upload New Picture (JPG, PNG, GIF, WebP)</label>
<input type="file" name="profile_pic" accept="image/*" class="form-control" style="margin-top:0.5rem;" required>
</div>
<button type="submit" class="btn" style="width:100%;">Upload Picture</button>
</form>
<?php if($pic && file_exists($pic)): ?>
<form method="POST">
<input type="hidden" name="delete_profile_pic" value="1">
<input type="hidden" name="redirect_to" value="<?= htmlspecialchars($action) ?>">
<button type="button" class="btn btn-danger" style="width:100%;" onclick="appConfirm('Delete profile picture?').then(y => { if(y) this.form.submit(); })">Delete Picture</button>
</form>
<?php endif; ?>
<button class="btn btn-secondary" style="width:100%; margin-top:0.5rem;" onclick="document.getElementById('profile-pic-modal').classList.remove('active')">Close</button>
</div>
</div>
<!-- Change Password Modal -->
<div class="modal-overlay" id="change-pw-modal">
<div class="modal-content" style="max-width:400px;">
<h3 style="margin-bottom:1rem; font-size:1.1rem;">Change Password</h3>
<?php
$pw_msg = $_SESSION['pw_change_msg'] ?? null;
unset($_SESSION['pw_change_msg']);
if ($pw_msg === 'success'): ?>
<div class="alert" style="background:rgba(16,185,129,0.12); color:#10B981; border:1px solid #10B981;">Password changed successfully!</div>
<?php elseif($pw_msg): ?>
<div class="alert"><?= htmlspecialchars($pw_msg) ?></div>
<?php endif; ?>
<form method="POST">
<input type="hidden" name="change_password" value="1">
<input type="hidden" name="redirect_to" value="<?= htmlspecialchars($action) ?>">
<div class="form-group">
<label>Current Password</label>
<input type="password" name="current_password" class="form-control" required>
</div>
<div class="form-group">
<label>New Password</label>
<input type="password" name="new_password" class="form-control" required minlength="6">
</div>
<div class="form-group">
<label>Confirm New Password</label>
<input type="password" name="confirm_password" class="form-control" required minlength="6">
</div>
<button type="submit" class="btn" style="width:100%;">Change Password</button>
</form>
<button class="btn btn-secondary" style="width:100%; margin-top:0.5rem;" onclick="document.getElementById('change-pw-modal').classList.remove('active')">Cancel</button>
</div>
</div>
<!-- Notifications Modal -->
<?php
// Fetch notifications for popup
$notif_list = [];
try {
$stmt_n = $pdo->prepare("SELECT * FROM notifications WHERE user_id=? ORDER BY created_at DESC LIMIT 30");
$stmt_n->execute([$user_id]);
$notif_list = $stmt_n->fetchAll();
} catch(Exception $e) {}
?>
<div class="modal-overlay" id="notifications-modal">
<div class="modal-content" style="max-width:460px; max-height:80vh; display:flex; flex-direction:column; padding:0; overflow:hidden;">
<div style="display:flex; justify-content:space-between; align-items:center; padding:1.25rem 1.5rem; border-bottom:1px solid var(--border);">
<h3 style="font-size:1.05rem; font-weight:700;"><i class="fa fa-bell" style="color:var(--primary);"></i> Notifications</h3>
<div style="display:flex; gap:0.5rem; align-items:center;">
<?php if($unread_notifications > 0): ?>
<form method="POST" id="mark-all-read-form" style="display:inline;">
<input type="hidden" name="mark_all_read" value="1">
<input type="hidden" name="redirect_to" value="<?= htmlspecialchars($action) ?>">
<button type="submit" class="btn btn-sm btn-secondary" style="font-size:0.75rem;">Mark all read</button>
</form>
<?php endif; ?>
<button onclick="document.getElementById('notifications-modal').classList.remove('active')" style="background:none;border:none;color:var(--text-light);cursor:pointer;font-size:1.2rem;padding:4px;">×</button>
</div>
</div>
<div style="overflow-y:auto; flex:1; padding:0.75rem 0;">
<?php if(empty($notif_list)): ?>
<div style="text-align:center; padding:2.5rem 1rem; color:var(--text-light);">
<i class="fa fa-bell-slash" style="font-size:2rem; display:block; margin-bottom:0.75rem;"></i>
No notifications yet.
</div>
<?php else: foreach($notif_list as $n):
$t = strtotime($n['created_at']);
$diff = time()-$t;
$ago = $diff<60?'just now':($diff<3600?floor($diff/60).'m ago':($diff<86400?floor($diff/3600).'h ago':floor($diff/86400).'d ago'));
?>
<div style="display:flex; align-items:flex-start; gap:0.75rem; padding:0.65rem 1.5rem; border-bottom:1px solid var(--border); transition:background 0.15s; <?= !$n['is_read']?'background:rgba(var(--primary-rgb),0.04);':'' ?>">
<div style="width:8px; height:8px; border-radius:50%; background:<?= !$n['is_read']?'var(--primary)':'transparent' ?>; margin-top:6px; flex-shrink:0;"></div>
<div style="flex:1; min-width:0;">
<div style="font-size:0.875rem; color:var(--text); <?= !$n['is_read']?'font-weight:500;':'' ?>"><?= htmlspecialchars($n['message']) ?></div>
<div style="font-size:0.72rem; color:var(--text-light); margin-top:3px;"><?= $ago ?></div>
</div>
</div>
<?php endforeach; endif; ?>
</div>
</div>
</div>
<div class="app-container">
<!-- Sidebar -->
<aside class="sidebar" id="sidebar">
<!-- Toggle button on the edge -->
<button class="sidebar-toggle-btn" id="sidebar-toggle-btn" onclick="toggleSidebar()" title="Collapse / Expand">
<i class="fa fa-chevron-left" id="sidebar-toggle-icon"></i>
</button>
<h2 class="sidebar-brand">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="flex-shrink:0;"><path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path><polyline points="22 4 12 14.01 9 11.01"></polyline></svg>
<span class="sidebar-label sidebar-brand-text">TaskManager</span>
</h2>
<nav style="display:flex; flex-direction:column; gap:0.25rem;">
<a href="?action=dashboard" class="nav-link <?= $action=='dashboard'?'active':'' ?>" title="Dashboard">
<i class="fa fa-gauge-high nav-icon"></i>
<span class="sidebar-label">Dashboard</span>
</a>
<a href="?action=tasks" class="nav-link <?= $action=='tasks'?'active':'' ?>" title="Tasks">
<i class="fa fa-list-check nav-icon"></i>
<span class="sidebar-label">Tasks</span>
</a>
<a href="?action=notes" class="nav-link <?= $action=='notes'?'active':'' ?>" title="Notes">
<i class="fa fa-note-sticky nav-icon"></i>
<span class="sidebar-label">Notes</span>
</a>
<a href="?action=passwords" class="nav-link <?= $action=='passwords'?'active':'' ?>" title="Passwords">
<i class="fa fa-lock nav-icon"></i>
<span class="sidebar-label">Passwords</span>
</a>
<a href="?action=projects" class="nav-link <?= $action=='projects'?'active':'' ?>" title="Files">
<i class="fa fa-folder-open nav-icon"></i>
<span class="sidebar-label">Files</span>
</a>
<a href="?action=tools" class="nav-link <?= $action=='tools'?'active':'' ?>" title="Tools">
<i class="fa fa-toolbox nav-icon"></i>
<span class="sidebar-label">Tools</span>
</a>
<?php if(!empty($_SESSION['is_admin'])): ?>
<a href="?action=admin" class="nav-link <?= $action=='admin'?'active':'' ?>" title="Admin Panel">
<i class="fa fa-shield-halved nav-icon"></i>
<span class="sidebar-label">Admin Panel</span>
</a>
<?php endif; ?>
</nav>
</aside>
<main class="main-content">
<!-- Top Bar -->
<div style="display:flex; justify-content:flex-end; align-items:center; gap:0.75rem; margin-bottom:1.5rem;">
<!-- Dark Mode Toggle -->
<button id="theme-toggle" class="btn btn-secondary" title="Toggle dark mode" style="border-radius:50%; padding:0; width:38px; height:38px; display:flex; align-items:center; justify-content:center; flex-shrink:0;">
<span id="theme-icon-light" style="display:none;"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg></span>
<span id="theme-icon-dark" style="display:none;"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg></span>
</button>
<!-- User Avatar Dropdown -->
<div class="user-dropdown-wrap" id="user-dropdown-wrap">
<!-- Avatar with notification badge -->
<div style="position:relative; display:inline-flex;">
<button class="user-avatar-btn" id="user-avatar-btn" onclick="toggleUserDropdown()" title="<?= htmlspecialchars($current_user['username']) ?>">
<?php
try { $pdo->exec("ALTER TABLE users ADD COLUMN profile_pic TEXT DEFAULT NULL"); } catch(Exception $e) {}
$pic_top = $current_user['profile_pic'] ?? null;
?>
<?php if($pic_top && file_exists($pic_top)): ?>
<img src="<?= htmlspecialchars($pic_top) ?>?v=<?= time() ?>" alt="avatar" style="width:100%; height:100%; object-fit:cover; border-radius:50%;">
<?php else: ?>
<?= strtoupper(mb_substr($current_user['username'],0,1)) ?>
<?php endif; ?>
</button>
<?php if($unread_notifications > 0): ?>
<span class="avatar-notif-badge" onclick="openNotifications()"><?= $unread_notifications > 9 ? '9+' : $unread_notifications ?></span>
<?php endif; ?>
</div>
<div class="user-dropdown-menu" id="user-dropdown-menu">
<div class="user-dropdown-header">
<div class="user-dropdown-name"><?= htmlspecialchars($current_user['username']) ?></div>
<?php if(!empty($_SESSION['is_admin'])): ?>
<div class="user-dropdown-role">Administrator</div>
<?php else: ?>
<div class="user-dropdown-role">Member</div>
<?php endif; ?>
</div>
<div class="user-dropdown-divider"></div>
<button class="user-dropdown-item" onclick="openNotifications()">
<i class="fa fa-bell"></i>
Notifications
<?php if($unread_notifications > 0): ?>
<span class="badge" style="margin-left:auto; font-size:0.65rem;"><?= $unread_notifications ?></span>
<?php endif; ?>
</button>
<div class="user-dropdown-divider"></div>
<button class="user-dropdown-item" onclick="document.getElementById('profile-pic-modal').classList.add('active'); closeUserDropdown();">
<i class="fa fa-image"></i> Set Profile Picture
</button>
<button class="user-dropdown-item" onclick="document.getElementById('change-pw-modal').classList.add('active'); closeUserDropdown();">
<i class="fa fa-key"></i> Change Password
</button>
<div class="user-dropdown-divider"></div>
<a href="?action=logout" class="user-dropdown-item user-dropdown-logout" onclick="appConfirmUrl(event, this.href, 'Logout?')">
<i class="fa fa-right-from-bracket"></i> Logout
</a>
</div>
</div>
</div>
<?php if (isset($pw_msg) && $pw_msg): ?>
<script>document.addEventListener('DOMContentLoaded',()=>{ document.getElementById('change-pw-modal').classList.add('active'); });</script>
<?php endif; ?>