Browse Source

Seperate Consultants

Benjamin Harris 2 months ago
parent
commit
356985b9ce

+ 351 - 0
dashboard/admin/consultant-clients.php

@@ -0,0 +1,351 @@
+<?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'; ?>

+ 2 - 2
dashboard/consultant/client.php

@@ -23,7 +23,7 @@ if (session_status() === PHP_SESSION_NONE) {
     session_start();
 }
 
-requireLogin();
+requireConsultant();
 
 $pdo      = getDBConnection();
 $userId   = (int) getCurrentUserId();
@@ -183,7 +183,7 @@ include __DIR__ . '/../../layouts/navbar.php';
 
 <div id="layoutSidenav">
     <div id="layoutSidenav_nav">
-        <?php include __DIR__ . '/../../layouts/sidebar.php'; ?>
+        <?php include __DIR__ . '/../../layouts/consultant-sidebar.php'; ?>
     </div>
     <div id="layoutSidenav_content">
         <main>

+ 2 - 2
dashboard/consultant/index.php

@@ -14,7 +14,7 @@ if (session_status() === PHP_SESSION_NONE) {
     session_start();
 }
 
-requireLogin();
+requireConsultant();
 
 $pageTitle  = 'Consultant Dashboard';
 $siteName   = 'Crop Monitor';
@@ -37,7 +37,7 @@ include __DIR__ . '/../../layouts/navbar.php';
 
 <div id="layoutSidenav">
     <div id="layoutSidenav_nav">
-        <?php include __DIR__ . '/../../layouts/sidebar.php'; ?>
+        <?php include __DIR__ . '/../../layouts/consultant-sidebar.php'; ?>
     </div>
     <div id="layoutSidenav_content">
         <main>

+ 24 - 0
database/migrations/002_consultant_roles.sql

@@ -0,0 +1,24 @@
+-- ============================================================
+-- Migration 002: Consultant roles + client assignment
+-- Run once against the cropmonitor database
+-- ============================================================
+
+-- 1. Add user_type column to users table
+--    Values: 'client' (default), 'consultant', 'admin'
+ALTER TABLE `users`
+    ADD COLUMN `user_type` ENUM('client','consultant','admin')
+        NOT NULL DEFAULT 'client'
+        AFTER `role`;
+
+-- 2. Junction table: maps consultants → the client_records they manage
+CREATE TABLE IF NOT EXISTS `consultant_clients` (
+    `id`            INT UNSIGNED NOT NULL AUTO_INCREMENT,
+    `consultant_id` INT UNSIGNED NOT NULL,   -- users.id  (user_type = 'consultant')
+    `client_id`     INT          NOT NULL,   -- client_records.id
+    `assigned_at`   DATETIME     NOT NULL DEFAULT CURRENT_TIMESTAMP,
+    `assigned_by`   INT UNSIGNED DEFAULT NULL, -- users.id of admin who made the assignment
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uq_consultant_client` (`consultant_id`, `client_id`),
+    KEY `idx_cc_consultant` (`consultant_id`),
+    KEY `idx_cc_client`     (`client_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

+ 64 - 0
layouts/consultant-sidebar.php

@@ -0,0 +1,64 @@
+<?php
+/**
+ * layouts/consultant-sidebar.php
+ *
+ * Sidebar navigation for the Consultant Dashboard area.
+ * Separate from the main client sidebar — consultants see
+ * their own client list and admin tools only.
+ */
+$currentUser = getCurrentUser() ?? ['fullname' => '', 'user_type' => 'consultant'];
+$currentPath = parse_url($_SERVER['REQUEST_URI'] ?? '', PHP_URL_PATH);
+$isActive    = fn(string $href): string => str_starts_with($currentPath, $href) ? ' active' : '';
+?>
+
+<nav id="sidenavAccordion" class="sb-sidenav accordion sb-sidenav-dark">
+    <div class="sb-sidenav-menu">
+        <div class="nav">
+
+            <!-- Brand / home -->
+            <div class="sb-sidenav-menu-heading">Consultant Portal</div>
+
+            <a href="/dashboard/consultant/index.php"
+               class="nav-link<?= $isActive('/dashboard/consultant/index.php') ?>">
+                <div class="sb-nav-link-icon">
+                    <i class="fas fa-th-large nav_icon"></i>
+                </div>
+                Client Overview
+            </a>
+
+            <!-- Admin section (visible to admins only) -->
+            <?php if (isAdmin()): ?>
+            <div class="sb-sidenav-menu-heading">Admin</div>
+
+            <a href="/dashboard/admin/consultant-clients.php"
+               class="nav-link<?= $isActive('/dashboard/admin/') ?>">
+                <div class="sb-nav-link-icon">
+                    <i class="fas fa-user-cog nav_icon"></i>
+                </div>
+                Manage Consultants
+            </a>
+            <?php endif; ?>
+
+            <!-- Divider back to main app -->
+            <div class="sb-sidenav-menu-heading">Client App</div>
+
+            <a href="/dashboard/dashboard.php" class="nav-link">
+                <div class="sb-nav-link-icon">
+                    <i class="fas fa-arrow-left nav_icon"></i>
+                </div>
+                Main Dashboard
+            </a>
+
+        </div>
+    </div>
+
+    <div class="sb-sidenav-footer">
+        <div class="small">Logged in as:</div>
+        <?= htmlspecialchars($currentUser['fullname'], ENT_QUOTES, 'UTF-8') ?>
+        <div class="small text-muted mt-1">
+            <span class="badge bg-info bg-opacity-50">
+                <?= htmlspecialchars(ucfirst($currentUser['user_type'] ?? 'consultant'), ENT_QUOTES, 'UTF-8') ?>
+            </span>
+        </div>
+    </div>
+</nav>

+ 0 - 9
layouts/sidebar.php

@@ -50,15 +50,6 @@ $settingsChildren  = [
     <div class="sb-sidenav-menu">
         <div class="nav">
 
-            <!-- Consultant Dashboard -->
-            <a href="/dashboard/consultant/index.php"
-               class="nav-link<?= $isActive('/dashboard/consultant/') ?>">
-                <div class="sb-nav-link-icon">
-                    <i class="fas fa-chart-line nav_icon"></i>
-                </div>
-                Consultant Dashboard
-            </a>
-
             <!-- Planning Calendar -->
             <a href="/dashboard/planning-calendar.php"
                class="nav-link<?= $isActive('/dashboard/planning-calendar.php') ?>">

+ 39 - 5
lib/auth.php

@@ -30,12 +30,28 @@ function getCurrentUser(): ?array
         return null;
     }
     return [
-        'id'       => (int) $_SESSION['user_id'],
-        'fullname' => $_SESSION['user_name']  ?? '',
-        'email'    => $_SESSION['user_email'] ?? '',
+        'id'        => (int) $_SESSION['user_id'],
+        'fullname'  => $_SESSION['user_name']  ?? '',
+        'email'     => $_SESSION['user_email'] ?? '',
+        'user_type' => $_SESSION['user_type']  ?? 'client',
     ];
 }
 
+function getCurrentUserType(): string
+{
+    return $_SESSION['user_type'] ?? 'client';
+}
+
+function isConsultant(): bool
+{
+    return isLoggedIn() && in_array($_SESSION['user_type'] ?? '', ['consultant', 'admin'], true);
+}
+
+function isAdmin(): bool
+{
+    return isLoggedIn() && ($_SESSION['user_type'] ?? '') === 'admin';
+}
+
 function requireLogin(): void
 {
     if (!isLoggedIn()) {
@@ -46,9 +62,26 @@ function requireLogin(): void
     }
 }
 
+function requireConsultant(): void
+{
+    requireLogin();
+    if (!isConsultant()) {
+        http_response_code(403);
+        die('Access denied. Consultant account required.');
+    }
+}
+
+function requireAdmin(): void
+{
+    requireLogin();
+    if (!isAdmin()) {
+        http_response_code(403);
+        die('Access denied. Administrator account required.');
+    }
+}
+
 function hasPermission(string $permission): bool
 {
-    // Stub — extend with role checks when roles are introduced
     return isLoggedIn();
 }
 
@@ -64,7 +97,7 @@ function loginUser(string $email, string $password): ?array
 {
     $pdo  = getDBConnection();
     $stmt = $pdo->prepare(
-        'SELECT id, fullname, email, password FROM users WHERE email = ? AND active = 1 LIMIT 1'
+        'SELECT id, fullname, email, password, user_type FROM users WHERE email = ? AND active = 1 LIMIT 1'
     );
     $stmt->execute([strtolower(trim($email))]);
     $user = $stmt->fetch();
@@ -83,6 +116,7 @@ function loginUser(string $email, string $password): ?array
     $_SESSION['user_id']    = $user['id'];
     $_SESSION['user_name']  = $user['fullname'];
     $_SESSION['user_email'] = $user['email'];
+    $_SESSION['user_type']  = $user['user_type'] ?? 'client';
 
     return $user;
 }

+ 3 - 3
lib/consultant.php

@@ -68,18 +68,18 @@ function getConsultantClients(PDO $pdo, int $userId): array
             MAX(pr.date_sampled)   AS last_plant_date,
             MAX(wr.date_sampled)   AS last_water_date,
 
-            -- Most recent activity across all test types
             GREATEST(
                 COALESCE(MAX(sr.date_sampled), '1970-01-01'),
                 COALESCE(MAX(pr.date_sampled), '1970-01-01'),
                 COALESCE(MAX(wr.date_sampled), '1970-01-01')
             ) AS last_activity
 
-        FROM client_records cr
+        FROM consultant_clients cc
+        JOIN client_records cr ON cr.id = cc.client_id
         LEFT JOIN soil_records  sr ON CAST(sr.client_records_id AS UNSIGNED) = cr.id
         LEFT JOIN plant_records pr ON pr.client_records_id = cr.id
         LEFT JOIN water_records wr ON wr.client_records_id = cr.id
-        WHERE cr.modx_user_id = ?
+        WHERE cc.consultant_id = ?
         GROUP BY cr.id
         ORDER BY last_activity DESC, cr.client ASC
     ";

+ 18 - 6
login/login.php

@@ -3,9 +3,21 @@ require_once __DIR__ . '/../config/database.php';
 require_once __DIR__ . '/../lib/auth.php';
 require_once __DIR__ . '/../lib/csrf.php';
 
-// Already logged in → go to dashboard
+/**
+ * Return the default landing page for a user based on their type.
+ * Consultants and admins go to the consultant dashboard.
+ */
+function defaultDashboard(): string
+{
+    return match (getCurrentUserType()) {
+        'consultant', 'admin' => '/dashboard/consultant/index.php',
+        default               => '/dashboard/dashboard.php',
+    };
+}
+
+// Already logged in → go to appropriate dashboard
 if (isLoggedIn()) {
-    header('Location: /dashboard/dashboard.php');
+    header('Location: ' . defaultDashboard());
     exit;
 }
 
@@ -23,10 +35,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
         } else {
             $user = loginUser($email, $password);
             if ($user) {
-                $redirect = $_GET['redirect'] ?? '/dashboard/dashboard.php';
-                // Sanitise redirect to prevent open redirect
-                if (!str_starts_with($redirect, '/')) {
-                    $redirect = '/dashboard/dashboard.php';
+                // If a specific redirect was requested honour it (internal paths only)
+                $redirect = $_GET['redirect'] ?? '';
+                if ($redirect === '' || !str_starts_with($redirect, '/')) {
+                    $redirect = defaultDashboard();
                 }
                 header('Location: ' . $redirect);
                 exit;