| Server IP : 69.72.244.102 / Your IP : 216.73.216.164 Web Server : LiteSpeed System : Linux s3434.fra1.stableserver.net 4.18.0-513.24.1.lve.2.el8.x86_64 #1 SMP Fri May 24 12:42:50 UTC 2024 x86_64 User : konzalta ( 1271) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/konzalta/constituency.work/ |
Upload File : |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ConstituencyOS β Staff Management</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
:root{
--green:#1a7a4a;--green-light:#e8f5ee;--green-mid:#2d9e63;
--red:#dc2626;--red-light:#fdecea;
--amber:#f59e0b;--amber-light:#fffbeb;
--blue:#2563eb;--blue-light:#eff6ff;
--purple:#7c3aed;--purple-light:#f5f3ff;
--teal:#0891b2;--teal-light:#ecfeff;
--text:#111827;--muted:#6b7280;--light:#9ca3af;
--border:#e5e7eb;--bg:#f3f4f6;--white:#ffffff;
--sidebar-w:216px;--radius:10px;--radius-sm:6px;
--shadow:0 1px 3px rgba(0,0,0,.08);
}
html,body{height:100%;margin:0;padding:0;}
body{font-family:'Inter',sans-serif;font-size:13px;color:var(--text);background:var(--bg);}
.sidebar{position:fixed;top:0;left:0;bottom:0;width:var(--sidebar-w);background:var(--white);border-right:1px solid var(--border);display:flex;flex-direction:column;overflow-y:auto;overflow-x:hidden;z-index:40;}
.sidebar-logo{display:flex;align-items:center;gap:9px;padding:14px 14px 12px;border-bottom:1px solid var(--border);flex-shrink:0;}
.logo-icon{width:38px;height:38px;background:linear-gradient(135deg,#b91c1c,#1a7a4a);border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:17px;flex-shrink:0;}
.logo-text strong{display:block;font-size:12.5px;font-weight:700;color:var(--green);}
.logo-text small{font-size:10px;color:var(--muted);line-height:1.3;}
.sidebar-profile{display:flex;align-items:center;gap:9px;padding:11px 14px;border-bottom:1px solid var(--border);flex-shrink:0;}
.s-av{width:36px;height:36px;border-radius:50%;background:#374151;color:white;font-weight:600;font-size:13px;display:flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;}
.s-av .dot{position:absolute;bottom:1px;right:1px;width:8px;height:8px;border-radius:50%;background:var(--green-mid);border:2px solid white;}
.pname{font-size:11.5px;font-weight:600;}.psub{font-size:10.5px;color:var(--muted);}
.sidebar-nav{flex:1;padding:6px 0;}
.nav-item{display:flex;align-items:center;gap:9px;padding:8px 14px;font-size:12px;font-weight:500;color:var(--muted);cursor:pointer;position:relative;transition:background .12s,color .12s;white-space:nowrap;}
.nav-item:hover{background:var(--bg);color:var(--text);}
.nav-item.active{background:var(--green-light);color:var(--green);font-weight:600;}
.nav-item.active::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;background:var(--green);border-radius:0 2px 2px 0;}
.nav-icon{font-size:14px;width:16px;text-align:center;flex-shrink:0;}
.nav-badge{margin-left:auto;background:var(--red);color:white;font-size:9.5px;font-weight:700;padding:1px 5px;border-radius:10px;}
.nav-new{margin-left:auto;background:var(--green);color:white;font-size:9px;font-weight:700;padding:2px 5px;border-radius:4px;}
.nav-chev{margin-left:auto;font-size:10px;color:var(--light);}
.sidebar-footer{padding:12px 14px;border-top:1px solid var(--border);flex-shrink:0;}
.right-panel{margin-left:var(--sidebar-w);display:flex;flex-direction:column;min-height:100vh;}
.topbar{position:sticky;top:0;z-index:30;height:52px;background:var(--white);border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;padding:0 18px;}
.menu-btn{font-size:17px;cursor:pointer;color:var(--muted);}
.const-sel{display:flex;align-items:center;gap:6px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:5px 10px;cursor:pointer;font-size:12px;font-weight:500;}
.srch{flex:1;max-width:360px;display:flex;align-items:center;gap:7px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:6px 11px;color:var(--muted);font-size:12px;}
.srch input{border:none;background:transparent;outline:none;width:100%;font-size:12px;font-family:inherit;}
.srch kbd{margin-left:auto;font-size:9.5px;background:var(--border);padding:1px 5px;border-radius:3px;font-family:inherit;}
.tbr{margin-left:auto;display:flex;align-items:center;gap:8px;}
.ibt{position:relative;width:32px;height:32px;border-radius:50%;background:var(--bg);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:15px;}
.ibt .bdg{position:absolute;top:-3px;right:-3px;background:var(--red);color:white;font-size:9px;font-weight:700;width:15px;height:15px;border-radius:50%;display:flex;align-items:center;justify-content:center;border:1.5px solid white;}
.ibt .bdg.grn{background:var(--green-mid);}
.upill{display:flex;align-items:center;gap:7px;border:1px solid var(--border);border-radius:var(--radius-sm);padding:3px 8px;cursor:pointer;font-size:12px;font-weight:500;}
.content{padding:18px 18px 40px;display:flex;flex-direction:column;gap:14px;}
.btn{display:inline-flex;align-items:center;gap:6px;padding:7px 14px;border-radius:var(--radius-sm);font-size:12px;font-weight:600;cursor:pointer;border:none;transition:background .15s;}
.btn-primary{background:var(--green);color:white;}.btn-primary:hover{background:var(--green-mid);}
.btn-ghost{background:var(--white);color:var(--text);border:1px solid var(--border);}.btn-ghost:hover{background:var(--bg);}
.page-hdr{display:flex;align-items:flex-start;justify-content:space-between;}
.page-title{font-size:19px;font-weight:700;}
.page-sub{font-size:12px;color:var(--muted);margin-top:2px;}
/* METRICS */
.metrics{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;}
.mc{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:13px 15px;cursor:pointer;transition:box-shadow .15s;}
.mc:hover,.mc.active{box-shadow:0 0 0 2px var(--green-light);border-color:var(--green);}
.mc-val{font-size:22px;font-weight:700;line-height:1.1;}
.mc-lbl{font-size:11px;color:var(--muted);margin-top:2px;}
/* TOOLBAR */
.toolbar{display:flex;align-items:center;gap:10px;background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;flex-wrap:wrap;}
.sb{flex:1;min-width:180px;display:flex;align-items:center;gap:7px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:7px 11px;}
.sb input{border:none;background:transparent;outline:none;width:100%;font-size:12px;font-family:inherit;color:var(--text);}
.fsel{display:flex;align-items:center;gap:5px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:6px 10px;font-size:12px;white-space:nowrap;}
.fsel select{border:none;background:transparent;outline:none;font-size:12px;font-family:inherit;color:var(--text);cursor:pointer;}
.view-toggle{display:flex;gap:2px;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius-sm);padding:2px;}
.vt{width:28px;height:26px;border-radius:4px;display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:13px;color:var(--muted);}
.vt.active{background:var(--white);color:var(--text);box-shadow:var(--shadow);}
/* ORG CHART */
.org-card{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;}
.org-hdr{padding:13px 16px;border-bottom:1px solid var(--border);font-size:13px;font-weight:600;display:flex;justify-content:space-between;align-items:center;}
.vlink{font-size:11px;color:var(--green);cursor:pointer;font-weight:500;}
.org-tree{padding:24px;overflow-x:auto;}
.org-level{display:flex;justify-content:center;gap:24px;margin-bottom:28px;position:relative;}
.org-level:not(:last-child)::after{content:'';position:absolute;bottom:-14px;left:50%;width:2px;height:14px;background:var(--border);}
.org-node{background:var(--white);border:2px solid var(--border);border-radius:var(--radius-sm);padding:10px 14px;text-align:center;min-width:130px;cursor:pointer;transition:box-shadow .15s,border-color .15s;position:relative;}
.org-node:hover{box-shadow:0 4px 12px rgba(0,0,0,.1);border-color:var(--green-mid);}
.org-node.mp{border-color:var(--green);background:var(--green-light);}
.org-av{width:38px;height:38px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;color:white;margin:0 auto 6px;}
.org-name{font-size:11.5px;font-weight:700;}
.org-role{font-size:10px;color:var(--muted);margin-top:1px;}
.org-row{display:flex;justify-content:center;gap:18px;flex-wrap:wrap;}
/* GRID VIEW */
.staff-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;}
.staff-card{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);padding:16px;cursor:pointer;transition:box-shadow .15s;display:flex;flex-direction:column;gap:10px;}
.staff-card:hover{box-shadow:0 4px 16px rgba(0,0,0,.1);border-color:var(--green-mid);}
.sc-top{display:flex;align-items:flex-start;gap:10px;}
.sc-av{width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:17px;font-weight:700;color:white;flex-shrink:0;position:relative;}
.sc-status-dot{position:absolute;bottom:0;right:0;width:11px;height:11px;border-radius:50%;border:2px solid white;}
.sc-name{font-size:13px;font-weight:700;}
.sc-role{font-size:11px;color:var(--muted);margin-top:1px;}
.sc-dept{display:inline-flex;align-items:center;gap:4px;font-size:10.5px;background:var(--bg);padding:2px 8px;border-radius:20px;font-weight:600;margin-top:5px;}
.sc-meta{display:flex;flex-direction:column;gap:4px;}
.sc-meta-row{display:flex;align-items:center;gap:6px;font-size:11px;color:var(--muted);}
.sc-footer{display:flex;align-items:center;justify-content:space-between;padding-top:8px;border-top:1px solid var(--border);}
.sc-phone{font-size:11px;color:var(--muted);}
.sc-action{font-size:11px;color:var(--green);font-weight:600;}
/* LIST VIEW */
.list-card{background:var(--white);border:1px solid var(--border);border-radius:var(--radius);overflow:hidden;display:none;}
table{width:100%;border-collapse:collapse;}
thead tr{background:var(--bg);border-bottom:1px solid var(--border);}
th{padding:9px 14px;font-size:11px;font-weight:600;color:var(--muted);text-align:left;white-space:nowrap;cursor:pointer;}
th:hover{color:var(--text);}
tbody tr{border-bottom:1px solid var(--border);transition:background .1s;cursor:pointer;}
tbody tr:last-child{border-bottom:none;}
tbody tr:hover{background:#f9fafb;}
td{padding:9px 14px;font-size:12px;vertical-align:middle;}
.status-pill{display:inline-flex;align-items:center;gap:4px;font-size:10px;font-weight:600;padding:3px 8px;border-radius:20px;}
.status-pill.active{background:#ecfdf5;color:#065f46;}
.status-pill.leave{background:var(--amber-light);color:#92400e;}
.status-pill.inactive{background:var(--bg);color:var(--muted);}
/* PAGINATION */
.pgn{display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border-top:1px solid var(--border);background:var(--white);border-radius:0 0 var(--radius) var(--radius);}
.pg-info{font-size:11.5px;color:var(--muted);}
.pg-btns{display:flex;gap:4px;}
.pg-btn{width:26px;height:26px;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--white);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:11.5px;font-weight:500;}
.pg-btn.active{background:var(--green);color:white;border-color:var(--green);}
.pg-btn:hover:not(.active){background:var(--bg);}
/* DETAIL PANEL */
.ov{position:fixed;inset:0;background:rgba(0,0,0,.35);z-index:100;display:none;}
.ov.open{display:flex;align-items:flex-start;justify-content:flex-end;}
.dp{width:430px;height:100%;background:var(--white);box-shadow:-4px 0 24px rgba(0,0,0,.12);display:flex;flex-direction:column;overflow:hidden;}
.dp-hdr{padding:18px;border-bottom:1px solid var(--border);flex-shrink:0;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;}
.dp-close{position:absolute;top:14px;right:14px;width:28px;height:28px;border-radius:50%;background:var(--bg);border:1px solid var(--border);display:flex;align-items:center;justify-content:center;cursor:pointer;font-size:14px;}
.dp-av{width:64px;height:64px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;color:white;margin-bottom:8px;}
.dp-name{font-size:16px;font-weight:700;}
.dp-role{font-size:12px;color:var(--muted);margin-top:2px;}
.dp-body{flex:1;overflow-y:auto;padding:18px;display:flex;flex-direction:column;gap:16px;}
.dp-sect{font-size:11px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:8px;}
.dp-row{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--border);font-size:12px;}
.dp-row:last-child{border-bottom:none;}
.dp-key{color:var(--muted);}.dp-val{font-weight:500;text-align:right;}
.task-item{display:flex;align-items:center;gap:8px;padding:8px 10px;background:var(--bg);border-radius:var(--radius-sm);font-size:11.5px;margin-bottom:5px;}
.task-dot{width:7px;height:7px;border-radius:50%;flex-shrink:0;}
.dp-footer{padding:14px 18px;border-top:1px solid var(--border);display:flex;gap:8px;flex-shrink:0;}
.dp-footer .btn{flex:1;justify-content:center;}
</style>
</head>
<body>
<aside class="sidebar">
<div class="sidebar-logo"><div class="logo-icon">ποΈ</div><div class="logo-text"><strong>ConstituencyOS</strong><small>Everything in your constituency,<br>in one place.</small></div></div>
<div class="sidebar-profile"><div class="s-av">DM<div class="dot"></div></div><div><div class="pname">Hon. David Mwangi, MP</div><div class="psub">Embakasi South</div></div></div>
<nav class="sidebar-nav">
<div class="nav-item" onclick="location.href='index.html'"><span class="nav-icon">π </span>Dashboard</div>
<div class="nav-item" onclick="location.href='constituents.html'"><span class="nav-icon">π₯</span>Constituents</div>
<div class="nav-item" onclick="location.href='complaints.html'"><span class="nav-icon">π</span>Complaints<span class="nav-badge">9</span></div>
<div class="nav-item" onclick="location.href='projects.html'"><span class="nav-icon">π</span>Projects</div>
<div class="nav-item" onclick="location.href='bursaries.html'"><span class="nav-icon">π</span>Bursaries</div>
<div class="nav-item" onclick="location.href='events.html'"><span class="nav-icon">π
</span>Events</div>
<div class="nav-item" onclick="location.href='social_protection.html'"><span class="nav-icon">π‘οΈ</span>Social Protection</div>
<div class="nav-item" onclick="location.href='ward_management.html'"><span class="nav-icon">ποΈ</span>Ward Management</div>
<div class="nav-item" onclick="location.href='finance.html'"><span class="nav-icon">π°</span>Finance</div>
<div class="nav-item" onclick="location.href='documents.html'"><span class="nav-icon">π</span>Documents</div>
<div class="nav-item" onclick="location.href='staff_management.html'"><span class="nav-icon">π€</span>Staff Management</div>
<div class="nav-item" onclick="location.href='reports_analytics.html'"><span class="nav-icon">π</span>Reports & Analytics</div>
<div class="nav-item active"><span class="nav-icon">π€</span>AI Assistant<span class="nav-new">New</span></div>
<div class="nav-item" onclick="location.href='settings.html'"><span class="nav-icon">βοΈ</span>Settings</div>
<div class="nav-item" onclick="location.href='public_portal.html'"><span class="nav-icon">π</span>Public Portal</div>
</nav>
<div class="sidebar-footer"><div style="display:flex;align-items:center;gap:9px;cursor:pointer"><span>β</span><div><div style="font-size:11.5px;font-weight:600">Need Help?</div><div style="font-size:10.5px;color:var(--muted)">Contact Support</div></div></div></div>
</aside>
<div class="right-panel">
<header class="topbar">
<span class="menu-btn">β°</span>
<div class="const-sel"><span style="font-size:13px">ποΈ</span>Embakasi South Constituency<span style="font-size:10px;color:var(--muted)">βΌ</span></div>
<div class="srch"><span>π</span><input type="text" placeholder="Search for anythingβ¦"><kbd>βK</kbd></div>
<div class="tbr">
<div class="ibt">π<div class="bdg">8</div></div>
<div class="ibt">βοΈ<div class="bdg grn">3</div></div>
<div class="upill"><div class="s-av" style="width:26px;height:26px;font-size:10px">DM</div>Hon. David Mwangi<span style="font-size:10px;color:var(--muted)">βΌ</span></div>
</div>
</header>
<main class="content">
<div class="page-hdr">
<div><div class="page-title">Staff Management</div><div class="page-sub">Office staff, ward administrators, and field officers across Embakasi South</div></div>
<div style="display:flex;gap:8px;">
<button class="btn btn-ghost">β¬ Export</button>
<button class="btn btn-primary">οΌ Add Staff Member</button>
</div>
</div>
<!-- Metrics -->
<div class="metrics">
<div class="mc active" onclick="filterDept('all',this)">
<div class="mc-val" id="cnt-all">0</div>
<div class="mc-lbl">Total Staff</div>
</div>
<div class="mc" onclick="filterDept('Administration',this)">
<div class="mc-val" style="color:var(--blue)" id="cnt-admin">0</div>
<div class="mc-lbl">Administration</div>
</div>
<div class="mc" onclick="filterDept('Field Operations',this)">
<div class="mc-val" style="color:var(--green)" id="cnt-field">0</div>
<div class="mc-lbl">Field Operations</div>
</div>
<div class="mc" onclick="filterDept('Social Services',this)">
<div class="mc-val" style="color:var(--purple)" id="cnt-social">0</div>
<div class="mc-lbl">Social Services</div>
</div>
<div class="mc" onclick="filterDept('Technical',this)">
<div class="mc-val" style="color:var(--amber)" id="cnt-tech">0</div>
<div class="mc-lbl">Technical / Engineering</div>
</div>
</div>
<!-- Org Chart -->
<div class="org-card">
<div class="org-hdr"><span>Organisation Structure</span><span class="vlink">Edit hierarchy</span></div>
<div class="org-tree">
<div class="org-level">
<div class="org-node mp" onclick="alert('Hon. David Mwangi, MP')">
<div class="org-av" style="background:#1a7a4a">DM</div>
<div class="org-name">Hon. David Mwangi</div>
<div class="org-role">Member of Parliament</div>
</div>
</div>
<div class="org-level">
<div class="org-row">
<div class="org-node" onclick="openDp('ST-001')">
<div class="org-av" style="background:#2563eb">CO</div>
<div class="org-name">Catherine Omondi</div>
<div class="org-role">Chief of Staff</div>
</div>
<div class="org-node" onclick="openDp('ST-002')">
<div class="org-av" style="background:#7c3aed">FM</div>
<div class="org-name">Felix Mutiso</div>
<div class="org-role">Finance Manager</div>
</div>
<div class="org-node" onclick="openDp('ST-003')">
<div class="org-av" style="background:#0891b2">EM</div>
<div class="org-name">Eng. Peter Mwangi</div>
<div class="org-role">Technical Lead</div>
</div>
</div>
</div>
<div class="org-level" style="margin-bottom:0;">
<div class="org-row">
<div class="org-node" onclick="openDp('ST-004')"><div class="org-av" style="background:#16a34a">JK</div><div class="org-name">James Kamau</div><div class="org-role">Pipeline Ward Admin</div></div>
<div class="org-node" onclick="openDp('ST-005')"><div class="org-av" style="background:#dc2626">SM</div><div class="org-name">Susan Muthoni</div><div class="org-role">Imara Daima Ward Admin</div></div>
<div class="org-node" onclick="openDp('ST-006')"><div class="org-av" style="background:#f59e0b">PN</div><div class="org-name">Peter Njoroge</div><div class="org-role">Mabatini Ward Admin</div></div>
<div class="org-node" onclick="openDp('ST-007')"><div class="org-av" style="background:#7c3aed">GW</div><div class="org-name">Grace Wambua</div><div class="org-role">Umoja Ward Admin</div></div>
<div class="org-node" onclick="openDp('ST-008')"><div class="org-av" style="background:#0891b2">JO</div><div class="org-name">Joseph Odhiambo</div><div class="org-role">Kwa Njenga Ward Admin</div></div>
<div class="org-node" onclick="openDp('ST-009')"><div class="org-av" style="background:#1a7a4a">AN</div><div class="org-name">Alice Njoroge</div><div class="org-role">Mihango Ward Admin</div></div>
</div>
</div>
</div>
</div>
<!-- Toolbar -->
<div class="toolbar">
<div class="sb"><span>π</span><input type="text" id="searchInput" placeholder="Search by name, role, wardβ¦" oninput="applyFilters()"></div>
<div class="fsel">ποΈ <select id="wardFilter" onchange="applyFilters()">
<option value="">All Wards / Offices</option>
<option>Pipeline Ward</option><option>Imara Daima</option><option>Mabatini Ward</option>
<option>Umoja Ward</option><option>Kwa Njenga Ward</option><option>Mihango Ward</option><option>Constituency Office</option>
</select></div>
<div class="fsel">π <select id="statusFilter" onchange="applyFilters()">
<option value="">All Statuses</option>
<option>Active</option><option>On Leave</option><option>Inactive</option>
</select></div>
<div class="view-toggle">
<div class="vt active" id="vt-grid" onclick="setView('grid')">β</div>
<div class="vt" id="vt-list" onclick="setView('list')">β°</div>
</div>
</div>
<div id="staffLabel" style="font-size:14px;font-weight:700;"></div>
<!-- Grid -->
<div class="staff-grid" id="staffGrid"></div>
<!-- List -->
<div class="list-card" id="staffListCard">
<table>
<thead><tr>
<th onclick="sortBy('name')">Name β</th><th onclick="sortBy('role')">Role β</th>
<th onclick="sortBy('department')">Department β</th><th>Ward / Office</th>
<th>Phone</th><th onclick="sortBy('status')">Status β</th>
</tr></thead>
<tbody id="staffListBody"></tbody>
</table>
</div>
<div class="pgn">
<div class="pg-info" id="pgInfo"></div>
<div class="pg-btns" id="pgBtns"></div>
</div>
</main>
</div>
<!-- DETAIL PANEL -->
<div class="ov" id="ov" onclick="closeOv(event)">
<div class="dp">
<div class="dp-hdr">
<div class="dp-close" onclick="closeDp()">β</div>
<div class="dp-av" id="dp-av"></div>
<div class="dp-name" id="dp-name"></div>
<div class="dp-role" id="dp-role"></div>
<div style="margin-top:8px" id="dp-status"></div>
</div>
<div class="dp-body">
<div><div class="dp-sect">Contact Details</div>
<div class="dp-row"><span class="dp-key">Phone</span><span class="dp-val" id="dp-phone"></span></div>
<div class="dp-row"><span class="dp-key">Email</span><span class="dp-val" id="dp-email"></span></div>
<div class="dp-row"><span class="dp-key">Office</span><span class="dp-val" id="dp-office"></span></div>
</div>
<div><div class="dp-sect">Employment Details</div>
<div class="dp-row"><span class="dp-key">Department</span><span class="dp-val" id="dp-dept"></span></div>
<div class="dp-row"><span class="dp-key">Reports To</span><span class="dp-val" id="dp-reports"></span></div>
<div class="dp-row"><span class="dp-key">Date Joined</span><span class="dp-val" id="dp-joined"></span></div>
<div class="dp-row"><span class="dp-key">Contract Type</span><span class="dp-val" id="dp-contract"></span></div>
</div>
<div><div class="dp-sect">Current Tasks</div>
<div id="dp-tasks"></div>
</div>
<div><div class="dp-sect">Notes</div>
<textarea style="width:100%;border:1px solid var(--border);border-radius:var(--radius-sm);padding:8px 10px;font-size:12px;font-family:inherit;resize:none;height:64px;outline:none;" placeholder="Add a noteβ¦" onfocus="this.style.borderColor='var(--green)'" onblur="this.style.borderColor='var(--border)'"></textarea>
</div>
</div>
<div class="dp-footer">
<button class="btn btn-ghost" style="flex:1;justify-content:center;" onclick="closeDp()">Close</button>
<button class="btn btn-ghost" style="flex:1;justify-content:center;">π Assign Task</button>
<button class="btn btn-primary" style="flex:1;justify-content:center;">βοΈ Edit</button>
</div>
</div>
</div>
<script>
const DEPT_COLORS = {'Administration':'#2563eb','Field Operations':'#1a7a4a','Social Services':'#7c3aed','Technical':'#f59e0b'};
const STAFF = [
{id:'ST-001',name:'Catherine Omondi',role:'Chief of Staff',department:'Administration',ward:'Constituency Office',phone:'0712 100 001',email:'c.omondi@embakasisouth.go.ke',status:'Active',joined:'Jan 2022',contract:'Permanent',reports:'Hon. David Mwangi, MP',tasks:[{t:'Prepare Q2 staff briefing',c:'#2563eb'},{t:'Review CDFC meeting agenda',c:'#f59e0b'}]},
{id:'ST-002',name:'Felix Mutiso',role:'Finance Manager',department:'Administration',ward:'Constituency Office',phone:'0712 100 002',email:'f.mutiso@embakasisouth.go.ke',status:'Active',joined:'Mar 2022',contract:'Permanent',reports:'Hon. David Mwangi, MP',tasks:[{t:'Finalise Q3 financial statement',c:'#dc2626'},{t:'Reconcile bursary disbursements',c:'#f59e0b'}]},
{id:'ST-003',name:'Eng. Peter Mwangi',role:'Technical Lead',department:'Technical',ward:'Constituency Office',phone:'0712 100 003',email:'p.mwangi@embakasisouth.go.ke',status:'Active',joined:'Jun 2022',contract:'Permanent',reports:'Hon. David Mwangi, MP',tasks:[{t:'Site inspection β Pipeline Road',c:'#dc2626'},{t:'Review BOQ for Kwa Njenga pipeline',c:'#2563eb'}]},
{id:'ST-004',name:'James Kamau',role:'Ward Administrator',department:'Administration',ward:'Pipeline Ward',phone:'0712 300 001',email:'j.kamau@embakasisouth.go.ke',status:'Active',joined:'Feb 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'Organise Pipeline Ward baraza',c:'#16a34a'},{t:'Compile complaints register',c:'#f59e0b'}]},
{id:'ST-005',name:'Susan Muthoni',role:'Ward Administrator',department:'Administration',ward:'Imara Daima',phone:'0723 400 001',email:'s.muthoni@embakasisouth.go.ke',status:'Active',joined:'Apr 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'Dispensary handover coordination',c:'#2563eb'}]},
{id:'ST-006',name:'Peter Njoroge',role:'Ward Administrator',department:'Administration',ward:'Mabatini Ward',phone:'0734 500 001',email:'p.njoroge@embakasisouth.go.ke',status:'On Leave',joined:'May 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[]},
{id:'ST-007',name:'Grace Wambua',role:'Ward Administrator',department:'Administration',ward:'Umoja Ward',phone:'0745 600 001',email:'g.wambua@embakasisouth.go.ke',status:'Active',joined:'Mar 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'Sports complex site visit',c:'#16a34a'}]},
{id:'ST-008',name:'Joseph Odhiambo',role:'Ward Administrator',department:'Administration',ward:'Kwa Njenga Ward',phone:'0756 700 001',email:'j.odhiambo@embakasisouth.go.ke',status:'Active',joined:'Jul 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'Drainage project follow-up',c:'#dc2626'}]},
{id:'ST-009',name:'Alice Njoroge',role:'Ward Administrator',department:'Administration',ward:'Mihango Ward',phone:'0767 800 001',email:'a.njoroge@embakasisouth.go.ke',status:'Active',joined:'Aug 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[]},
{id:'ST-010',name:'Eng. Grace Kamau',role:'Ward Engineer',department:'Technical',ward:'Mabatini Ward',phone:'0734 500 002',email:'g.kamau@embakasisouth.go.ke',status:'Active',joined:'Sep 2022',contract:'Contract',reports:'Eng. Peter Mwangi',tasks:[{t:'School roof repair inspection',c:'#f59e0b'}]},
{id:'ST-011',name:'Eng. Rose Njeri',role:'Ward Engineer',department:'Technical',ward:'Kwa Njenga Ward',phone:'0756 700 002',email:'r.njeri@embakasisouth.go.ke',status:'Active',joined:'Oct 2022',contract:'Contract',reports:'Eng. Peter Mwangi',tasks:[{t:'Water pipeline progress review',c:'#2563eb'}]},
{id:'ST-012',name:'Joyce Wanjiku',role:'Social Worker',department:'Social Services',ward:'Pipeline Ward',phone:'0712 300 003',email:'j.wanjiku@embakasisouth.go.ke',status:'Active',joined:'Jan 2023',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'Home visit β Elijah Rono (SP-005)',c:'#7c3aed'}]},
{id:'ST-013',name:'Faith Omondi',role:'Social Worker',department:'Social Services',ward:'Imara Daima',phone:'0723 400 003',email:'f.omondi@embakasisouth.go.ke',status:'Active',joined:'Feb 2023',contract:'Permanent',reports:'Catherine Omondi',tasks:[]},
{id:'ST-014',name:'Grace Muthoni',role:'Social Worker',department:'Social Services',ward:'Kwa Njenga Ward',phone:'0756 789 012',email:'g.muthoni2@embakasisouth.go.ke',status:'Active',joined:'Mar 2023',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'OVC enrolment review β Q2',c:'#dc2626'}]},
{id:'ST-015',name:'Jane Wambua',role:'Social Worker',department:'Social Services',ward:'Umoja Ward',phone:'0745 600 004',email:'j.wambua@embakasisouth.go.ke',status:'Active',joined:'Apr 2023',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'Elderly support stipend audit',c:'#f59e0b'}]},
{id:'ST-016',name:'Hassan Abdi',role:'Ward Security Officer',department:'Field Operations',ward:'Pipeline Ward',phone:'0712 300 004',email:'h.abdi@embakasisouth.go.ke',status:'Active',joined:'May 2022',contract:'Permanent',reports:'James Kamau',tasks:[]},
{id:'ST-017',name:'Kevin Mwangi',role:'Community Liaison',department:'Field Operations',ward:'Kwa Njenga Ward',phone:'0756 700 003',email:'k.mwangi@embakasisouth.go.ke',status:'Active',joined:'Jun 2022',contract:'Permanent',reports:'Joseph Odhiambo',tasks:[{t:'Community meeting prep β Kware',c:'#16a34a'}]},
{id:'ST-018',name:'Coach Samuel Karuri',role:'Youth Coordinator',department:'Field Operations',ward:'Umoja Ward',phone:'0745 600 002',email:'s.karuri@embakasisouth.go.ke',status:'Active',joined:'Jul 2022',contract:'Contract',reports:'Grace Wambua',tasks:[{t:'Football tournament logistics',c:'#7c3aed'}]},
{id:'ST-019',name:'Headmaster Paul Oduya',role:'Education Coordinator',department:'Field Operations',ward:'Mihango Ward',phone:'0767 800 002',email:'p.oduya@embakasisouth.go.ke',status:'Active',joined:'Aug 2022',contract:'Contract',reports:'Alice Njoroge',tasks:[]},
{id:'ST-020',name:'Dr. Charles Achieng',role:'Health Coordinator',department:'Field Operations',ward:'Imara Daima',phone:'0723 400 002',email:'c.achieng@embakasisouth.go.ke',status:'Active',joined:'Sep 2022',contract:'Contract',reports:'Susan Muthoni',tasks:[{t:'Health camp planning β Kwa Njenga',c:'#dc2626'}]},
{id:'ST-021',name:'Mary Chebet',role:'Social Worker',department:'Social Services',ward:'Mihango Ward',phone:'0767 800 003',email:'m.chebet@embakasisouth.go.ke',status:'On Leave',joined:'Oct 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[]},
{id:'ST-022',name:'Patricia Akinyi',role:'Social Worker',department:'Social Services',ward:'Umoja Ward',phone:'0745 600 003',email:'p.akinyi@embakasisouth.go.ke',status:'Active',joined:'Nov 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'Bursary verification β Umoja',c:'#f59e0b'}]},
{id:'ST-023',name:'Ann Cherotich',role:'Social Worker',department:'Social Services',ward:'Mabatini Ward',phone:'0734 500 003',email:'a.cherotich@embakasisouth.go.ke',status:'Inactive',joined:'Dec 2021',contract:'Permanent',reports:'Catherine Omondi',tasks:[]},
{id:'ST-024',name:'Office Secretary - Lilian Wairimu',role:'Office Secretary',department:'Administration',ward:'Constituency Office',phone:'0712 100 005',email:'l.wairimu@embakasisouth.go.ke',status:'Active',joined:'Feb 2022',contract:'Permanent',reports:'Catherine Omondi',tasks:[{t:'Compile May staff meeting minutes',c:'#2563eb'}]},
];
const avatarPalette=['#1a7a4a','#2563eb','#7c3aed','#dc2626','#d97706','#0891b2','#be185d'];
function initials(n){return n.split(' ').slice(-2).map(x=>x[0]).join('').toUpperCase();}
function pal(n){let h=0;for(let c of n)h=(h*31+c.charCodeAt(0))%avatarPalette.length;return avatarPalette[h];}
let filtered=[...STAFF];
let activeDept='all';
let currentView='grid';
let sortKey='name';
let currentPage=1;
const PAGE_SIZE=12;
let current=null;
function statusPill(s){
const cls=s==='Active'?'active':s==='On Leave'?'leave':'inactive';
const icon=s==='Active'?'β':s==='On Leave'?'π΄':'β';
return `<span class="status-pill ${cls}">${icon} ${s}</span>`;
}
function updateCounts(){
document.getElementById('cnt-all').textContent=STAFF.length;
document.getElementById('cnt-admin').textContent=STAFF.filter(s=>s.department==='Administration').length;
document.getElementById('cnt-field').textContent=STAFF.filter(s=>s.department==='Field Operations').length;
document.getElementById('cnt-social').textContent=STAFF.filter(s=>s.department==='Social Services').length;
document.getElementById('cnt-tech').textContent=STAFF.filter(s=>s.department==='Technical').length;
}
function filterDept(d, el){
activeDept=d;
document.querySelectorAll('.mc').forEach(m=>m.classList.remove('active'));
el.classList.add('active');
applyFilters();
}
function applyFilters(){
const q=document.getElementById('searchInput').value.toLowerCase();
const ward=document.getElementById('wardFilter').value;
const status=document.getElementById('statusFilter').value;
filtered=STAFF.filter(s=>{
if(activeDept!=='all'&&s.department!==activeDept)return false;
if(ward&&s.ward!==ward)return false;
if(status&&s.status!==status)return false;
if(q&&!`${s.name} ${s.role} ${s.ward} ${s.department}`.toLowerCase().includes(q))return false;
return true;
});
filtered.sort((a,b)=>a.name.localeCompare(b.name));
currentPage=1;
document.getElementById('staffLabel').textContent = `Staff Directory (${filtered.length})`;
render();
}
function sortBy(key){
filtered.sort((a,b)=>{let av=a[key],bv=b[key];return av>bv?1:av<bv?-1:0;});
render();
}
function setView(v){
currentView=v;
document.getElementById('vt-grid').classList.toggle('active', v==='grid');
document.getElementById('vt-list').classList.toggle('active', v==='list');
document.getElementById('staffGrid').style.display = v==='grid'?'grid':'none';
document.getElementById('staffListCard').style.display = v==='list'?'block':'none';
}
function render(){
const start=(currentPage-1)*PAGE_SIZE, end=start+PAGE_SIZE;
const page=filtered.slice(start,end);
document.getElementById('staffGrid').innerHTML = page.map(s=>{
const color=pal(s.name);
const statusColor = s.status==='Active'?'#22c55e':s.status==='On Leave'?'#f59e0b':'#9ca3af';
return `<div class="staff-card" onclick="openDp('${s.id}')">
<div class="sc-top">
<div class="sc-av" style="background:${color}">${initials(s.name)}<div class="sc-status-dot" style="background:${statusColor}"></div></div>
<div style="flex:1;min-width:0;">
<div class="sc-name">${s.name}</div>
<div class="sc-role">${s.role}</div>
<span class="sc-dept" style="color:${DEPT_COLORS[s.department]}">${s.department}</span>
</div>
</div>
<div class="sc-meta">
<div class="sc-meta-row">ποΈ ${s.ward}</div>
<div class="sc-meta-row">π
Joined ${s.joined}</div>
</div>
<div class="sc-footer">
<span class="sc-phone">π ${s.phone}</span>
<span class="sc-action">View β</span>
</div>
</div>`;
}).join('');
document.getElementById('staffListBody').innerHTML = page.map(s=>{
const color=pal(s.name);
return `<tr onclick="openDp('${s.id}')">
<td><div style="display:flex;align-items:center;gap:8px;"><div style="width:28px;height:28px;border-radius:50%;background:${color};color:white;font-size:10.5px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;">${initials(s.name)}</div>${s.name}</div></td>
<td>${s.role}</td>
<td style="color:${DEPT_COLORS[s.department]};font-weight:600;">${s.department}</td>
<td style="color:var(--muted)">${s.ward}</td>
<td style="color:var(--muted)">${s.phone}</td>
<td>${statusPill(s.status)}</td>
</tr>`;
}).join('');
const total=filtered.length, pages=Math.ceil(total/PAGE_SIZE);
document.getElementById('pgInfo').textContent = `Showing ${Math.min(start+1,total)}β${Math.min(end,total)} of ${total} staff`;
const pb=document.getElementById('pgBtns'); pb.innerHTML='';
const add=(l,p,a)=>{const b=document.createElement('div');b.className='pg-btn'+(a?' active':'');b.textContent=l;b.onclick=()=>{currentPage=p;render();};pb.appendChild(b);};
if(currentPage>1) add('βΉ',currentPage-1,false);
for(let i=1;i<=Math.min(pages,7);i++) add(i,i,i===currentPage);
if(currentPage<pages) add('βΊ',currentPage+1,false);
}
function openDp(id){
current=STAFF.find(s=>s.id===id);
const s=current, color=pal(s.name);
document.getElementById('dp-av').textContent=initials(s.name);
document.getElementById('dp-av').style.background=color;
document.getElementById('dp-name').textContent=s.name;
document.getElementById('dp-role').textContent=s.role;
document.getElementById('dp-status').innerHTML=statusPill(s.status);
document.getElementById('dp-phone').textContent=s.phone;
document.getElementById('dp-email').textContent=s.email;
document.getElementById('dp-office').textContent=s.ward;
document.getElementById('dp-dept').innerHTML=`<span style="color:${DEPT_COLORS[s.department]};font-weight:700;">${s.department}</span>`;
document.getElementById('dp-reports').textContent=s.reports;
document.getElementById('dp-joined').textContent=s.joined;
document.getElementById('dp-contract').textContent=s.contract;
document.getElementById('dp-tasks').innerHTML = s.tasks.length ? s.tasks.map(t=>`
<div class="task-item"><div class="task-dot" style="background:${t.c}"></div>${t.t}</div>
`).join('') : '<div style="font-size:11.5px;color:var(--muted);padding:8px 10px;">No active tasks assigned</div>';
document.getElementById('ov').classList.add('open');
}
function closeOv(e){ if(e.target===document.getElementById('ov')) closeDp(); }
function closeDp(){ document.getElementById('ov').classList.remove('open'); }
updateCounts();
applyFilters();
</script>
</body>
</html>