exec("SET time_zone = '+05:30'"); // Get selection details with project info $stmt = $pdo->prepare(" SELECT ps.*, p.project_name, p.project_id as project_code, p.eloi, p.status as project_status FROM project_selections ps JOIN projects p ON ps.project_id = p.id WHERE ps.id = ? AND ps.client_id = ? "); $stmt->execute([$selection_id, $_SESSION['client_id']]); $selection = $stmt->fetch(); if (!$selection) { header('Location: projects-list.php'); exit; } // Get selection criteria $stmt = $pdo->prepare("SELECT * FROM selection_criteria WHERE selection_id = ? ORDER BY id"); $stmt->execute([$selection_id]); $criteria = $stmt->fetchAll(); // Get member count $stmt = $pdo->prepare("SELECT COUNT(*) FROM selection_members WHERE selection_id = ?"); $stmt->execute([$selection_id]); $member_count = $stmt->fetchColumn(); // Get assigned URL counts from survey_urls table $stmt = $pdo->prepare(" SELECT COUNT(*) FROM survey_urls WHERE assigned_to_selection_id = ? "); $stmt->execute([$selection_id]); $urls_assigned = (int)$stmt->fetchColumn(); // Total available URLs for the project $stmt = $pdo->prepare(" SELECT COUNT(*) FROM survey_urls WHERE project_id = (SELECT project_id FROM projects WHERE id = ?) AND status = 'available' "); $stmt->execute([$selection['project_id']]); $urls_available = (int)$stmt->fetchColumn(); $fully_assigned = ($member_count > 0 && $urls_assigned >= $member_count); $shortfall = max(0, $member_count - $urls_assigned); // Calculate default incentive: Rs.10 base for 5 min, +Rs.1 per min beyond $eloi = (int)($selection['eloi'] ?? 10); $default_incentive = max(10, 10 + max(0, $eloi - 5)); // URL status breakdown for tracking table $status_filter = $_GET['url_status'] ?? 'all'; $page = max(1, intval($_GET['page'] ?? 1)); $per_page = 25; $offset = ($page - 1) * $per_page; // Get status counts $stmt = $pdo->prepare(" SELECT status, COUNT(*) as cnt FROM survey_urls WHERE assigned_to_selection_id = ? GROUP BY status "); $stmt->execute([$selection_id]); $status_counts = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); $total_urls = array_sum($status_counts); // Get paginated URLs $url_where = "assigned_to_selection_id = ?"; $url_params = [$selection_id]; if ($status_filter !== 'all') { $url_where .= " AND status = ?"; $url_params[] = $status_filter; } $stmt = $pdo->prepare("SELECT COUNT(*) FROM survey_urls WHERE $url_where"); $stmt->execute($url_params); $filtered_total = (int)$stmt->fetchColumn(); $total_pages = max(1, ceil($filtered_total / $per_page)); $stmt = $pdo->prepare(" SELECT su.*, sm.user_id FROM survey_urls su LEFT JOIN selection_members sm ON su.sent_to_user_id = sm.user_id AND sm.selection_id = su.assigned_to_selection_id WHERE su.$url_where ORDER BY su.id ASC LIMIT $per_page OFFSET $offset "); $stmt->execute($url_params); $url_rows = $stmt->fetchAll(); // Detect duplicate IPs within this selection $stmt = $pdo->prepare(" SELECT respondent_ip, COUNT(*) as ip_count FROM survey_urls WHERE assigned_to_selection_id = ? AND respondent_ip IS NOT NULL AND respondent_ip != '' GROUP BY respondent_ip HAVING COUNT(*) > 1 "); $stmt->execute([$selection_id]); $duplicate_ips = $stmt->fetchAll(PDO::FETCH_KEY_PAIR); // Check if invitations already sent $invitations_sent = !empty($selection['invitations_sent_at']); // ===== RESEND: Compute dispatch state ===== $dispatch_count = (int)($selection['mail_dispatch_count'] ?? 0); $first_sent_ts = $invitations_sent ? strtotime($selection['invitations_sent_at']) : null; $second_sent_ts = !empty($selection['second_dispatch_at']) ? strtotime($selection['second_dispatch_at']) : null; $third_sent_ts = !empty($selection['third_dispatch_at']) ? strtotime($selection['third_dispatch_at']) : null; // Count pending (still in 'sent' status = not taken) for resend info $pending_resend_count = 0; if ($invitations_sent) { $stmt = $pdo->prepare("SELECT COUNT(*) FROM survey_urls WHERE assigned_to_selection_id = ? AND status = 'sent'"); $stmt->execute([$selection_id]); $pending_resend_count = (int)$stmt->fetchColumn(); } // Determine if resend is possible and when $can_resend = false; $resend_blocked_reason = ''; $next_resend_available_at = null; if ($invitations_sent && $dispatch_count < 3 && in_array($selection['status'], ['live', 'hold'])) { $now = time(); if ($dispatch_count == 1) { $min_time = $first_sent_ts + (24 * 3600); if ($now >= $min_time) { $can_resend = ($pending_resend_count > 0); if ($pending_resend_count == 0) { $resend_blocked_reason = 'All members have already taken the survey. No pending recipients to resend to.'; } } else { $next_resend_available_at = $min_time; $remaining = $min_time - $now; $resend_blocked_reason = 'The 2nd dispatch can only be sent 24 hours after the first send. Available in ' . floor($remaining / 3600) . 'h ' . ceil(($remaining % 3600) / 60) . 'm.'; } } elseif ($dispatch_count == 2) { $min_time = $second_sent_ts + (48 * 3600); if ($now >= $min_time) { $can_resend = ($pending_resend_count > 0); if ($pending_resend_count == 0) { $resend_blocked_reason = 'All members have already taken the survey. No pending recipients to resend to.'; } } else { $next_resend_available_at = $min_time; $remaining = $min_time - $now; $resend_blocked_reason = 'The 3rd dispatch can only be sent 48 hours after the 2nd send. Available in ' . floor($remaining / 3600) . 'h ' . ceil(($remaining % 3600) / 60) . 'm.'; } } } elseif ($dispatch_count >= 3) { $resend_blocked_reason = 'Maximum dispatches reached (3 of 3). No further resends allowed.'; } $page_title = 'View Selection'; include 'client-portal-header.php'; ?>
← Back to Project

Selection ID: | Project:

Status
Required Samples
Members Assigned
URLs Assigned /
Incidence Rate %
Est. LOI min
Created

Description

🚀 Actions

Members:
URLs Assigned: /
Available URLs:
0): ?>
Shortfall:
No members in this selection. Create selection criteria first.
All members have URLs assigned! You can now send invitations.
✅ Invitations sent on | Incentive: ₹
📨 Mail Dispatch History (/3 dispatches used)
1 Original Invitation
= 2): ?> 2 1st Reminder 2 1st Reminder — Not sent yet
= 3): ?> 3 2nd Reminder (Final) 3 2nd Reminder (Final) — Not sent yet
0 && $dispatch_count < 3): ?>
member(s) have not taken the survey yet and will receive the reminder.
ⓘ Resend Rules:
  • Maximum 3 dispatches total (1 original + 2 reminders).
  • 2nd dispatch: Only allowed 24 hours after the 1st send.
  • 3rd dispatch: Only allowed 48 hours after the 2nd send.
= 3): ?>
All 3 dispatches have been completed. No further resends are available for this selection.
Sending reminders... Please wait.
Selection Status
⚠ Project is — overrides selection
⚠ Not Ready: All members must have URLs assigned before you can send invitations. Currently assigned: /. 0): ?>
Upload more URL(s) and assign them to proceed.
💰 Confirm Incentive & Send
Estimated LOI
min
Default Incentive
Minimum: ₹ (cannot be reduced). You can increase this amount.
Sending invitations... Please wait.

🔗 Assigned URLs & Status Tracking ()

0): ?> 📥 Export CSV
'Assigned', 'sent' => 'Sent', 'clicked' => 'Clicked', 'complete' => 'Complete', 'partial' => 'Partial', 'earlyscreenout' => 'Early SO', 'latescreenout' => 'Late SO', 'quotafull' => 'Quota Full', 'timeout' => 'Timeout' ]; foreach ($all_statuses_map as $sk => $sl): if (isset($status_counts[$sk]) && $status_counts[$sk] > 0): ?> :
$row): $loi_seconds = $row['actual_loi_seconds'] ?? null; $loi_display = '-'; $loi_class = ''; if ($loi_seconds !== null) { $loi_min = round($loi_seconds / 60, 1); $loi_display = $loi_min . 'm'; $eloi_val = (int)($selection['eloi'] ?? 10); $threshold_pct = 33.33; $threshold_sec = $eloi_val * 60 * ($threshold_pct / 100); $loi_class = ($loi_seconds < $threshold_sec) ? 'vs-loi-speedster' : 'vs-loi-normal'; } $ip = $row['respondent_ip'] ?? ''; $is_ip_dup = !empty($ip) && isset($duplicate_ips[$ip]); $quality = $row['quality_flag'] ?? 'valid'; $wave = $row['mail_dispatch_wave'] ?? null; ?>
# Proxy URL Status Wave Member ID Sent At Start Time End Time LOI IP Address Quality
DUP () - Valid
1): ?>
1): ?> « Prev Next »
📋

No URLs yet.

Selection Criteria

No specific criteria applied

()