| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351 |
- <?php
- /**
- * dashboard/admin/consultant-clients.php
- *
- * Admin tool: assign client_records to consultant accounts.
- *
- * POST actions (CSRF protected):
- * action=assign consultant_id + client_id → insert into consultant_clients
- * action=remove assignment_id → delete from consultant_clients
- * action=set_type user_id + user_type → update users.user_type
- */
- require_once __DIR__ . '/../../config/database.php';
- require_once __DIR__ . '/../../lib/auth.php';
- require_once __DIR__ . '/../../lib/csrf.php';
- if (session_status() === PHP_SESSION_NONE) {
- session_start();
- }
- requireAdmin();
- $pdo = getDBConnection();
- $flash = '';
- $flashType = 'success';
- // ── POST handler ──────────────────────────────────────────────────────────────
- if ($_SERVER['REQUEST_METHOD'] === 'POST') {
- if (!verifyCsrfToken($_POST['csrf_token'] ?? '')) {
- $flash = 'Invalid request token. Please try again.';
- $flashType = 'danger';
- } else {
- $action = $_POST['action'] ?? '';
- if ($action === 'assign') {
- $consultantId = (int) ($_POST['consultant_id'] ?? 0);
- $clientId = (int) ($_POST['client_id'] ?? 0);
- if ($consultantId && $clientId) {
- try {
- $pdo->prepare("
- INSERT IGNORE INTO consultant_clients (consultant_id, client_id, assigned_by)
- VALUES (?, ?, ?)
- ")->execute([$consultantId, $clientId, getCurrentUserId()]);
- $flash = 'Client assigned successfully.';
- } catch (PDOException $e) {
- $flash = 'Assignment failed: ' . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8');
- $flashType = 'danger';
- }
- } else {
- $flash = 'Please select both a consultant and a client.';
- $flashType = 'warning';
- }
- } elseif ($action === 'remove') {
- $assignmentId = (int) ($_POST['assignment_id'] ?? 0);
- if ($assignmentId) {
- $pdo->prepare("DELETE FROM consultant_clients WHERE id = ?")->execute([$assignmentId]);
- $flash = 'Assignment removed.';
- }
- } elseif ($action === 'set_type') {
- $targetUserId = (int) ($_POST['user_id'] ?? 0);
- $userType = $_POST['user_type'] ?? '';
- if ($targetUserId && in_array($userType, ['client', 'consultant', 'admin'], true)) {
- $pdo->prepare("UPDATE users SET user_type = ? WHERE id = ?")
- ->execute([$userType, $targetUserId]);
- $flash = 'User type updated.';
- } else {
- $flash = 'Invalid user type selection.';
- $flashType = 'warning';
- }
- }
- }
- }
- // ── Load data ─────────────────────────────────────────────────────────────────
- // All users with their type
- $allUsers = $pdo->query("
- SELECT id, fullname, email, user_type
- FROM users
- WHERE active = 1
- ORDER BY user_type, fullname
- ")->fetchAll();
- // Consultants with their assigned clients
- $consultants = $pdo->query("
- SELECT u.id, u.fullname, u.email
- FROM users u
- WHERE u.user_type IN ('consultant','admin') AND u.active = 1
- ORDER BY u.fullname
- ")->fetchAll();
- // All client records (for the assign dropdown)
- $allClients = $pdo->query("
- SELECT id, client, company
- FROM client_records
- ORDER BY client ASC
- ")->fetchAll();
- // Assignments: consultant_id → [{assignment_id, client_id, client_name, company, assigned_at}]
- $assignmentRows = $pdo->query("
- SELECT cc.id AS assignment_id, cc.consultant_id, cc.assigned_at,
- cr.id AS client_id, cr.client, cr.company
- FROM consultant_clients cc
- JOIN client_records cr ON cr.id = cc.client_id
- ORDER BY cc.consultant_id, cr.client
- ")->fetchAll();
- $assignmentsByConsultant = [];
- foreach ($assignmentRows as $row) {
- $assignmentsByConsultant[$row['consultant_id']][] = $row;
- }
- // ── Page render ───────────────────────────────────────────────────────────────
- $pageTitle = 'Manage Consultants';
- $siteName = 'Crop Monitor';
- include __DIR__ . '/../../layouts/header.php';
- include __DIR__ . '/../../layouts/navbar.php';
- ?>
- <div id="layoutSidenav">
- <div id="layoutSidenav_nav">
- <?php include __DIR__ . '/../../layouts/consultant-sidebar.php'; ?>
- </div>
- <div id="layoutSidenav_content">
- <main>
- <div class="container-fluid px-4">
- <h1 class="mt-4"><?= htmlspecialchars($pageTitle, ENT_QUOTES, 'UTF-8') ?></h1>
- <ol class="breadcrumb mb-4">
- <li class="breadcrumb-item"><a href="/dashboard/consultant/index.php">Consultant</a></li>
- <li class="breadcrumb-item active">Manage Consultants</li>
- </ol>
- <?php if ($flash): ?>
- <div class="alert alert-<?= $flashType ?> alert-dismissible fade show" role="alert">
- <?= htmlspecialchars($flash, ENT_QUOTES, 'UTF-8') ?>
- <button type="button" class="btn-close" data-bs-dismiss="alert"></button>
- </div>
- <?php endif; ?>
- <div class="row g-4">
- <!-- ══ LEFT: User roles ═══════════════════════════════════ -->
- <div class="col-xl-4">
- <div class="card shadow-sm border-0 h-100">
- <div class="card-header fw-semibold">
- <i class="fas fa-users-cog me-1"></i> User Roles
- </div>
- <div class="card-body p-0">
- <table class="table table-sm table-hover mb-0 align-middle">
- <thead class="table-light">
- <tr>
- <th>Name</th>
- <th>Email</th>
- <th>Role</th>
- <th></th>
- </tr>
- </thead>
- <tbody>
- <?php foreach ($allUsers as $u): ?>
- <tr>
- <td class="fw-semibold small">
- <?= htmlspecialchars($u['fullname'], ENT_QUOTES, 'UTF-8') ?>
- </td>
- <td class="text-muted small">
- <?= htmlspecialchars($u['email'], ENT_QUOTES, 'UTF-8') ?>
- </td>
- <td>
- <?php
- $badge = match($u['user_type']) {
- 'admin' => 'danger',
- 'consultant' => 'info',
- default => 'secondary',
- };
- ?>
- <span class="badge bg-<?= $badge ?>">
- <?= htmlspecialchars(ucfirst($u['user_type']), ENT_QUOTES, 'UTF-8') ?>
- </span>
- </td>
- <td>
- <button class="btn btn-xs btn-sm btn-outline-secondary"
- data-bs-toggle="modal"
- data-bs-target="#modalSetType"
- data-user-id="<?= (int)$u['id'] ?>"
- data-user-name="<?= htmlspecialchars($u['fullname'], ENT_QUOTES, 'UTF-8') ?>"
- data-user-type="<?= htmlspecialchars($u['user_type'], ENT_QUOTES, 'UTF-8') ?>">
- Edit
- </button>
- </td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- <!-- ══ RIGHT: Consultant assignments ══════════════════════ -->
- <div class="col-xl-8">
- <!-- Assign form -->
- <div class="card shadow-sm border-0 mb-4">
- <div class="card-header fw-semibold">
- <i class="fas fa-user-plus me-1"></i> Assign Client to Consultant
- </div>
- <div class="card-body">
- <form method="POST" class="row g-2 align-items-end">
- <input type="hidden" name="csrf_token" value="<?= generateCsrfToken() ?>">
- <input type="hidden" name="action" value="assign">
- <div class="col-md-5">
- <label class="form-label fw-semibold small">Consultant</label>
- <select name="consultant_id" class="form-select form-select-sm" required>
- <option value="">Select consultant…</option>
- <?php foreach ($consultants as $c): ?>
- <option value="<?= (int)$c['id'] ?>">
- <?= htmlspecialchars($c['fullname'], ENT_QUOTES, 'UTF-8') ?>
- </option>
- <?php endforeach; ?>
- </select>
- </div>
- <div class="col-md-5">
- <label class="form-label fw-semibold small">Client</label>
- <select name="client_id" class="form-select form-select-sm" required>
- <option value="">Select client…</option>
- <?php foreach ($allClients as $cl): ?>
- <option value="<?= (int)$cl['id'] ?>">
- <?= htmlspecialchars($cl['client'] . ($cl['company'] ? ' — ' . $cl['company'] : ''), ENT_QUOTES, 'UTF-8') ?>
- </option>
- <?php endforeach; ?>
- </select>
- </div>
- <div class="col-md-2">
- <button class="btn btn-success btn-sm w-100" type="submit">
- <i class="fas fa-plus me-1"></i>Assign
- </button>
- </div>
- </form>
- </div>
- </div>
- <!-- Current assignments per consultant -->
- <?php if (empty($consultants)): ?>
- <div class="alert alert-info">
- No consultant accounts yet. Set a user's role to "Consultant" using the panel on the left.
- </div>
- <?php else: ?>
- <?php foreach ($consultants as $con):
- $assigned = $assignmentsByConsultant[$con['id']] ?? [];
- ?>
- <div class="card shadow-sm border-0 mb-3">
- <div class="card-header d-flex justify-content-between align-items-center">
- <span class="fw-semibold">
- <i class="fas fa-user-tie me-1 text-info"></i>
- <?= htmlspecialchars($con['fullname'], ENT_QUOTES, 'UTF-8') ?>
- <span class="text-muted fw-normal small ms-1">
- <?= htmlspecialchars($con['email'], ENT_QUOTES, 'UTF-8') ?>
- </span>
- </span>
- <span class="badge bg-secondary"><?= count($assigned) ?> client<?= count($assigned) !== 1 ? 's' : '' ?></span>
- </div>
- <?php if (empty($assigned)): ?>
- <div class="card-body text-muted small">No clients assigned yet.</div>
- <?php else: ?>
- <ul class="list-group list-group-flush">
- <?php foreach ($assigned as $a): ?>
- <li class="list-group-item d-flex justify-content-between align-items-center py-2">
- <div>
- <span class="fw-semibold">
- <?= htmlspecialchars($a['client'], ENT_QUOTES, 'UTF-8') ?>
- </span>
- <?php if ($a['company']): ?>
- <span class="text-muted small ms-1">
- — <?= htmlspecialchars($a['company'], ENT_QUOTES, 'UTF-8') ?>
- </span>
- <?php endif; ?>
- <div class="text-muted" style="font-size:.72rem">
- Assigned <?= date('j M Y', strtotime($a['assigned_at'])) ?>
- </div>
- </div>
- <form method="POST" class="d-inline"
- onsubmit="return confirm('Remove this client assignment?')">
- <input type="hidden" name="csrf_token" value="<?= generateCsrfToken() ?>">
- <input type="hidden" name="action" value="remove">
- <input type="hidden" name="assignment_id" value="<?= (int)$a['assignment_id'] ?>">
- <button class="btn btn-sm btn-outline-danger" type="submit">
- <i class="fas fa-times"></i>
- </button>
- </form>
- </li>
- <?php endforeach; ?>
- </ul>
- <?php endif; ?>
- </div>
- <?php endforeach; ?>
- <?php endif; ?>
- </div>
- </div><!-- /.row -->
- </div>
- </main>
- </div>
- </div>
- <!-- ── Modal: set user type ───────────────────────────────────────────────── -->
- <div class="modal fade" id="modalSetType" tabindex="-1">
- <div class="modal-dialog modal-sm">
- <div class="modal-content">
- <form method="POST">
- <input type="hidden" name="csrf_token" value="<?= generateCsrfToken() ?>">
- <input type="hidden" name="action" value="set_type">
- <input type="hidden" name="user_id" id="modalUserId">
- <div class="modal-header">
- <h5 class="modal-title">Change Role</h5>
- <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
- </div>
- <div class="modal-body">
- <p class="mb-2 text-muted small" id="modalUserName"></p>
- <label class="form-label fw-semibold">User Type</label>
- <select name="user_type" id="modalUserType" class="form-select">
- <option value="client">Client</option>
- <option value="consultant">Consultant</option>
- <option value="admin">Admin</option>
- </select>
- </div>
- <div class="modal-footer">
- <button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
- <button type="submit" class="btn btn-primary btn-sm">Save</button>
- </div>
- </form>
- </div>
- </div>
- </div>
- <script>
- document.getElementById('modalSetType').addEventListener('show.bs.modal', function (e) {
- const btn = e.relatedTarget;
- document.getElementById('modalUserId').value = btn.dataset.userId;
- document.getElementById('modalUserName').textContent = btn.dataset.userName;
- document.getElementById('modalUserType').value = btn.dataset.userType;
- });
- </script>
- <?php include __DIR__ . '/../../layouts/footer.php'; ?>
|