'₹', 'USD' => '$', 'EUR' => '€', ]; // ── Initialize all stats ── $d = [ 'projects_total' => 0, 'projects_live' => 0, 'projects_targeted' => 0, 'projects_onhold' => 0, 'projects_closed' => 0, 'projects_created' => 0, 'selections_total' => 0, 'selections_draft' => 0, 'selections_targeted' => 0, 'selections_live' => 0, 'urls_total' => 0, 'urls_available' => 0, 'urls_sent' => 0, 'urls_complete' => 0, 'urls_screenout' => 0, 'urls_quotafull' => 0, 'urls_clicked' => 0, 'urls_timeout' => 0, 'inv_total' => 0, 'inv_invoiced' => 0, 'inv_paid' => 0, 'inv_due' => 0, 'inv_overdue' => 0, 'inv_critical' => 0, 'amt_total' => 0, 'amt_paid' => 0, 'amt_outstanding' => 0, 'amt_overdue' => 0, ]; $pending_invoices = []; $recent_projects = []; $recent_activity = []; try { $pdo = getClientDBConnection(); $pdo->exec("SET time_zone = '+05:30'"); // Client currency $stmt = $pdo->prepare("SELECT currency FROM clients WHERE id = ?"); $stmt->execute([$client_id]); $row = $stmt->fetch(); if ($row && !empty($row['currency'])) { $client_currency = $row['currency']; $csym = $currency_map[$client_currency] ?? '₹'; } // ── PROJECTS ── try { $stmt = $pdo->prepare("SELECT status, COUNT(*) as cnt FROM projects WHERE client_id = ? GROUP BY status"); $stmt->execute([$client_id]); foreach ($stmt->fetchAll() as $r) { $key = 'projects_' . strtolower(str_replace(' ', '', $r['status'])); if (isset($d[$key])) $d[$key] = (int)$r['cnt']; $d['projects_total'] += (int)$r['cnt']; } } catch (Exception $e) {} // ── SELECTIONS ── try { $stmt = $pdo->prepare("SELECT status, COUNT(*) as cnt FROM project_selections WHERE client_id = ? GROUP BY status"); $stmt->execute([$client_id]); foreach ($stmt->fetchAll() as $r) { $key = 'selections_' . strtolower($r['status']); if (isset($d[$key])) $d[$key] = (int)$r['cnt']; $d['selections_total'] += (int)$r['cnt']; } } catch (Exception $e) {} // ── SURVEY URLs ── try { $stmt = $pdo->prepare(" SELECT su.status, COUNT(*) as cnt FROM survey_urls su INNER JOIN projects p ON su.project_id = p.project_id WHERE p.client_id = ? GROUP BY su.status "); $stmt->execute([$client_id]); $statusMap = [ 'available' => 'urls_available', 'sent' => 'urls_sent', 'clicked' => 'urls_clicked', 'complete' => 'urls_complete', 'partial' => 'urls_complete', 'earlyscreenout' => 'urls_screenout', 'latescreenout' => 'urls_screenout', 'quotafull' => 'urls_quotafull', 'timeout' => 'urls_timeout', ]; foreach ($stmt->fetchAll() as $r) { $key = $statusMap[$r['status']] ?? null; if ($key) $d[$key] += (int)$r['cnt']; $d['urls_total'] += (int)$r['cnt']; } } catch (Exception $e) {} // ── INVOICES ── try { $stmt = $pdo->prepare(" SELECT status, COUNT(*) as cnt, SUM(total_amount) as amt FROM invoices WHERE client_id = ? GROUP BY status "); $stmt->execute([$client_id]); foreach ($stmt->fetchAll() as $r) { $s = $r['status']; $cnt = (int)$r['cnt']; $amt = (float)$r['amt']; $d['inv_total'] += $cnt; $d['amt_total'] += $amt; if ($s === 'paid') { $d['inv_paid'] += $cnt; $d['amt_paid'] += $amt; } elseif ($s === 'invoiced') { $d['inv_invoiced'] += $cnt; } elseif ($s === 'due') { $d['inv_due'] += $cnt; } elseif ($s === 'overdue') { $d['inv_overdue'] += $cnt; $d['amt_overdue'] += $amt; } elseif (in_array($s, ['critical','banned'])) { $d['inv_critical'] += $cnt; $d['amt_overdue'] += $amt; } } $d['amt_outstanding'] = $d['amt_total'] - $d['amt_paid']; } catch (Exception $e) {} // ── PENDING / UNPAID INVOICES (for prominent table) ── try { $stmt = $pdo->prepare(" SELECT i.*, p.project_name FROM invoices i INNER JOIN projects p ON i.project_id = p.id WHERE i.client_id = ? AND i.status != 'paid' ORDER BY CASE i.status WHEN 'critical' THEN 1 WHEN 'banned' THEN 1 WHEN 'overdue' THEN 2 WHEN 'due' THEN 3 ELSE 4 END, i.due_date ASC "); $stmt->execute([$client_id]); $pending_invoices = $stmt->fetchAll(); } catch (Exception $e) {} // ── RECENT PROJECTS ── try { $stmt = $pdo->prepare(" SELECT id, project_id, project_name, status, deadline_date, updated_at FROM projects WHERE client_id = ? ORDER BY updated_at DESC LIMIT 6 "); $stmt->execute([$client_id]); $recent_projects = $stmt->fetchAll(); } catch (Exception $e) {} // ── RECENT ACTIVITY ── try { $stmt = $pdo->prepare(" (SELECT 'project' as type, p.project_name as name, pal.action as action, pal.description as detail, pal.created_at as activity_date FROM project_activity_log pal INNER JOIN projects p ON pal.project_id = p.id WHERE p.client_id = ?) ORDER BY activity_date DESC LIMIT 10 "); $stmt->execute([$client_id]); $recent_activity = $stmt->fetchAll(); } catch (Exception $e) {} } catch (Exception $e) { error_log("Client dashboard error: " . $e->getMessage()); } // Helpers function fmtAmt($v, $sym) { return $sym . number_format((float)$v, 2); } function fmtN($v) { return number_format((int)$v); } function statusBadge($status) { $map = [ 'invoiced' => ['#dbeafe','#1e40af','Invoiced'], 'paid' => ['#d1fae5','#065f46','Paid'], 'due' => ['#fef3c7','#92400e','Due'], 'overdue' => ['#fed7aa','#9a3412','Overdue'], 'critical' => ['#fecaca','#991b1b','Critical'], 'banned' => ['#1f2937','#f9fafb','Banned'], 'Live' => ['#d1fae5','#065f46','Live'], 'Targeted' => ['#dbeafe','#1e40af','Targeted'], 'Created' => ['#f1f5f9','#475569','Created'], 'On hold' => ['#fef3c7','#92400e','On Hold'], 'Closed' => ['#f1f5f9','#64748b','Closed'], ]; $s = $map[$status] ?? ['#f1f5f9','#475569',$status]; return ''.$s[2].''; } function timeAgo($dt) { if (!$dt) return '—'; $diff = time() - strtotime($dt); if ($diff < 60) return 'Just now'; if ($diff < 3600) return floor($diff/60) . 'm ago'; if ($diff < 86400) return floor($diff/3600) . 'h ago'; if ($diff < 604800) return floor($diff/86400) . 'd ago'; return date('d M Y', strtotime($dt)); } // ── Sampling performance calculations (Overall = all projects combined) ── // Total survey clicks = clicked (still in progress) + all terminal statuses that come after a click $totalClicks = $d['urls_clicked'] + $d['urls_complete'] + $d['urls_screenout'] + $d['urls_quotafull'] + $d['urls_timeout']; // Total dispatched = urls still in 'sent' status + all that already progressed past sent $totalDispatched = $d['urls_sent'] + $totalClicks; // Response Rate = total clicks / total dispatched URLs $responseRate = $totalDispatched > 0 ? ($totalClicks / $totalDispatched) * 100 : 0; // Incidence = completes / total clicks $incidence = $totalClicks > 0 ? ($d['urls_complete'] / $totalClicks) * 100 : 0; // Screener Drop = screenouts / total dispatched $screenerDrop = $totalDispatched > 0 ? ($d['urls_screenout'] / $totalDispatched) * 100 : 0; // Invoice banner: only count due/overdue/critical as "unpaid" (not 'invoiced' which is not yet due) $unpaidCount = $d['inv_due'] + $d['inv_overdue'] + $d['inv_critical']; $hasUrgentInvoices = $unpaidCount > 0; include 'client-portal-header.php'; ?>

Welcome back,

IST ·

⚠ You have invoice 1 ? 's' : ''; ?> requiring attention

0) $parts[] = $d['inv_overdue'] . ' overdue'; if ($d['inv_critical'] > 0) $parts[] = $d['inv_critical'] . ' critical'; if ($d['inv_due'] > 0) $parts[] = $d['inv_due'] . ' due now'; echo implode(' · ', $parts); ?> — Please clear pending invoices to avoid service restrictions.

View Invoices →
Amount Outstanding

✓ No Due Invoices

0): ?> invoice 1 ? 's' : ''; ?> sent (not yet due). You're in good standing! No pending payments. You're in good standing — thank you!

View Invoice History →
Total Paid
Total Projects
live · closed
Total Completes
of survey URLs
Total Invoiced
invoice
Outstanding
due
📋 Pending Invoices
View All Invoices →
= 0 && $daysLeft <= 7)) { $rowClass = 'row-overdue'; $dueClass = 'due-soon'; } $sym = $currency_map[$inv['currency'] ?? 'INR'] ?? '₹'; ?>
Invoice # Project Amount Invoice Date Due Date Status
days overdue
Due today
day 1 ? 's' : ''; ?> left
📄 PDF
📊 Overall Project & Sampling Performance
0): ?>
Overall URLs Sent
via mail
Overall Survey Clicks
mail URL + portal
Overall Completes
Overall Screen Outs
early + late
%
Overall Response Rate
Clicks ÷ Sent URLs
%
Overall Incidence
Completes ÷ Clicks
%
Overall Screener Drop
Screen Outs ÷ Sent URLs
📊

No survey data yet. Performance metrics will appear once URLs are dispatched.

🗂 Project Status View All →
0): ?>
0 ? round($st[1]/$d['projects_total']*100) : 0; ?>
📋

No projects yet. Create your first project to get started.

+ Create Project
Recent Projects View All →
Project Status Updated
No projects yet
Recent Activity
No recent activity
⚡ Quick Actions
Client: Email: Currency: () Server Time: IST