prepare(" SELECT * FROM projects WHERE id = ? AND client_id = ? "); $stmt->execute([$project_id, $_SESSION['client_id']]); $project = $stmt->fetch(); if (!$project) { header('Location: projects-list.php'); exit; } // Fetch selections for this project try { $stmt = $pdo->prepare(" SELECT ps.*, COUNT(DISTINCT sm.user_id) as actual_selected, c.company_name as created_by_name, SUM(CASE WHEN sm.sample_status = 'pending' THEN 1 ELSE 0 END) as cnt_pending, SUM(CASE WHEN sm.sample_status = 'complete' THEN 1 ELSE 0 END) as cnt_complete, SUM(CASE WHEN sm.sample_status = 'screen_out' THEN 1 ELSE 0 END) as cnt_screen_out, SUM(CASE WHEN sm.sample_status = 'quota_full' THEN 1 ELSE 0 END) as cnt_quota_full, SUM(CASE WHEN sm.sample_status = 'over_quota' THEN 1 ELSE 0 END) as cnt_over_quota FROM project_selections ps LEFT JOIN selection_members sm ON ps.id = sm.selection_id LEFT JOIN clients c ON ps.created_by = c.id WHERE ps.project_id = ? AND ps.client_id = ? GROUP BY ps.id ORDER BY ps.created_at DESC "); $stmt->execute([$project_id, $_SESSION['client_id']]); $selections = $stmt->fetchAll(); // Get count of URLs sent per selection (via sent_to_user_id matching selection_members) $selectionUrlCounts = []; $selectionLoiStats = []; $projectLoiStats = ['avg_loi' => 0, 'min_loi' => 0, 'max_loi' => 0, 'total_completes' => 0, 'speedsters' => 0, 'ip_dups' => 0]; if (!empty($selections)) { $selIds = array_column($selections, 'id'); $placeholders = implode(',', array_fill(0, count($selIds), '?')); $stmt = $pdo->prepare(" SELECT sm.selection_id, COUNT(DISTINCT su.id) as urls_sent, SUM(CASE WHEN su.status = 'complete' THEN 1 ELSE 0 END) as urls_complete, SUM(CASE WHEN su.status = 'partial' THEN 1 ELSE 0 END) as urls_partial, SUM(CASE WHEN su.status IN ('earlyscreenout','latescreenout') THEN 1 ELSE 0 END) as urls_screenout, SUM(CASE WHEN su.status = 'timeout' THEN 1 ELSE 0 END) as urls_timeout, SUM(CASE WHEN su.status = 'quotafull' THEN 1 ELSE 0 END) as urls_quotafull FROM selection_members sm INNER JOIN survey_urls su ON su.sent_to_user_id = sm.user_id AND su.project_id = ? WHERE sm.selection_id IN ($placeholders) GROUP BY sm.selection_id "); $stmt->execute(array_merge([$project['project_id']], $selIds)); foreach ($stmt->fetchAll() as $row) { $selectionUrlCounts[$row['selection_id']] = $row; } // LOI stats per selection $stmt = $pdo->prepare(" SELECT sm.selection_id, AVG(CASE WHEN su.status = 'complete' AND su.actual_loi_seconds IS NOT NULL THEN su.actual_loi_seconds END) as avg_loi, MIN(CASE WHEN su.status = 'complete' AND su.actual_loi_seconds IS NOT NULL THEN su.actual_loi_seconds END) as min_loi, MAX(CASE WHEN su.status = 'complete' AND su.actual_loi_seconds IS NOT NULL THEN su.actual_loi_seconds END) as max_loi, SUM(CASE WHEN su.quality_flag = 'speedster' THEN 1 ELSE 0 END) as speedsters, SUM(CASE WHEN su.quality_flag = 'ip_duplicate' THEN 1 ELSE 0 END) as ip_dups FROM selection_members sm INNER JOIN survey_urls su ON su.sent_to_user_id = sm.user_id AND su.project_id = ? WHERE sm.selection_id IN ($placeholders) GROUP BY sm.selection_id "); $stmt->execute(array_merge([$project['project_id']], $selIds)); foreach ($stmt->fetchAll() as $row) { $selectionLoiStats[$row['selection_id']] = $row; } // Project-wide LOI stats $stmt = $pdo->prepare(" SELECT AVG(CASE WHEN status = 'complete' AND actual_loi_seconds IS NOT NULL THEN actual_loi_seconds END) as avg_loi, MIN(CASE WHEN status = 'complete' AND actual_loi_seconds IS NOT NULL THEN actual_loi_seconds END) as min_loi, MAX(CASE WHEN status = 'complete' AND actual_loi_seconds IS NOT NULL THEN actual_loi_seconds END) as max_loi, SUM(CASE WHEN status = 'complete' THEN 1 ELSE 0 END) as total_completes, SUM(CASE WHEN quality_flag = 'speedster' THEN 1 ELSE 0 END) as speedsters, SUM(CASE WHEN quality_flag = 'ip_duplicate' THEN 1 ELSE 0 END) as ip_dups FROM survey_urls WHERE project_id = ? "); $stmt->execute([$project['project_id']]); $pStats = $stmt->fetch(PDO::FETCH_ASSOC); if ($pStats) $projectLoiStats = $pStats; } } catch (Exception $e) { error_log("Selections fetch error: " . $e->getMessage()); $selections = []; $selectionUrlCounts = []; } $page_title = 'View Project'; $is_live = $project['status'] === 'Live'; // Status colors $status_colors = [ 'Created' => '#0066cc', 'Targeted' => '#ffc107', 'Live' => '#28a745', 'On hold' => '#dc2626', 'Closed' => '#666' ]; $status_color = $status_colors[$project['status']] ?? '#666'; // Define redirect statuses $redirect_statuses = [ 'complete' => ['name' => 'Complete', 'color' => '#28a745'], 'partial' => ['name' => 'Partial Complete', 'color' => '#0066cc'], 'earlyscreenout' => ['name' => 'Early Screen Out', 'color' => '#dc2626'], 'latescreenout' => ['name' => 'Late Screen Out', 'color' => '#ffc107'], 'timeout' => ['name' => 'Timed Out', 'color' => '#666'], 'quotafull' => ['name' => 'Quota Full', 'color' => '#ff6b6b'] ]; $base_url = 'https://relevantreflex.com/r/'; include 'client-portal-header.php'; ?>

Edit Project Close Project Reopen Pending Admin Approval Closure Pending Admin Review Back to Projects
Status
Project ID
Country
Created

🔗 Redirect URLs

$info): ?>

👥 Panel Member Selections ()

+ Create Selection
0): ?>
Project aLOI: min avg (min range)  |  ELOI: min  |  Speedster Threshold: <min (% of ELOI = min)
Completes: 0): ?>  |  Possible Speedster 1 ? 's' : ''; ?> 0): ?>  |  🔗 Possible IP Duplication 1 ? 's' : ''; ?>
📋
No Selections Created Yet
Create your first selection to start recruiting panel members for this project.
+ Create Your First Selection
Selection ID Name Status Required Selected Sample Status URLs Sent Avg LOI Quality Flags Incidence Created Actions
0): ?> Pending: 0): ?> Complete: 0): ?> Screen Out: 0): ?> Quota Full: 0): ?> Over Quota: No members
0): ?>
Sent: 0): ?> 0): ?> SO: 0): ?> TO:
None
m - 0 || $ipDups > 0): ?>
0): ?> Speedster 1 ? 's' : ''; ?> 0): ?> 🔗 IP Dup 1 ? 's' : ''; ?>
Clean
%
View Edit Edit Delete

📥 Export URL Data

📋 Export Complete Report
prepare(" SELECT batch_number, total_urls, sent_count, upload_type, created_at FROM survey_url_batches WHERE project_id = ? ORDER BY batch_number DESC "); $stmt->execute([$project['project_id']]); $url_batches = $stmt->fetchAll(PDO::FETCH_ASSOC); // Query 2: Get status counts per batch in one query $batch_status_counts = []; if (!empty($url_batches)) { $stmt = $pdo->prepare(" SELECT batch_number, status, COUNT(*) as cnt FROM survey_urls WHERE project_id = ? GROUP BY batch_number, status "); $stmt->execute([$project['project_id']]); $status_rows = $stmt->fetchAll(PDO::FETCH_ASSOC); foreach ($status_rows as $row) { $bn = $row['batch_number']; $st = $row['status']; if (!isset($batch_status_counts[$bn])) { $batch_status_counts[$bn] = []; } $batch_status_counts[$bn][$st] = (int)$row['cnt']; } } } catch (Exception $e) { $url_batches = []; $batch_status_counts = []; error_log("Error fetching URL batches: " . $e->getMessage()); } ?>
🔗
No Survey URLs Yet
Add survey URLs to track panel member responses and manage redirects.
+ Add Your First Batch
Batch # Type Total Status Breakdown Created Actions
Batch # ['label' => 'Available', 'class' => 'pill-available'], 'sent' => ['label' => 'Sent', 'class' => 'pill-sent'], 'clicked' => ['label' => 'Clicked', 'class' => 'pill-clicked'], 'complete' => ['label' => 'Complete', 'class' => 'pill-complete'], 'partial' => ['label' => 'Partial', 'class' => 'pill-partial'], 'earlyscreenout' => ['label' => 'Early SO', 'class' => 'pill-earlyscreenout'], 'latescreenout' => ['label' => 'Late SO', 'class' => 'pill-latescreenout'], 'timeout' => ['label' => 'Timeout', 'class' => 'pill-timeout'], 'quotafull' => ['label' => 'Quota Full', 'class' => 'pill-quotafull'], ]; ?>
$status_info): ?> 0): ?> : No activity yet

🔍 Note: URLs that remain without a result for over 2 hours are automatically marked as Timeout.