-------------------- END OF FILE --------------------
FILE: RR SHOP/config.php
TYPE: PHP
SIZE: 5.95 KB
------------------------------------------------------------
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
$pdo->exec("SET time_zone = '+05:30'");
return $pdo;
} catch (PDOException $e) {
error_log("Connection failed: " . $e->getMessage());
die("Database connection failed. Please try again later.");
}
}
// Create connection for Panel Database (Customer Portal)
function getPanelDBConnection() {
try {
$pdo = new PDO(
"mysql:host=" . DB_HOST . ";dbname=" . PANEL_DB_NAME . ";charset=utf8mb4",
PANEL_DB_USER, // Use the panel-specific user
DB_PASS, // Assuming same password
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
$pdo->exec("SET time_zone = '+05:30'");
return $pdo;
} catch (PDOException $e) {
error_log("Panel DB Connection failed: " . $e->getMessage());
die("Panel database connection failed. Please try again later.");
}
}
// Helper function to check if user is logged in
function isLoggedIn() {
return isset($_SESSION['admin_id']) && isset($_SESSION['admin_role']);
}
// Helper function to check if user is admin
function isAdmin() {
return isset($_SESSION['admin_role']) && $_SESSION['admin_role'] === 'admin';
}
// Helper function to check if user can access page
function canAccessPage($page) {
if (!isLoggedIn()) {
return false;
}
// Admin can access everything
if (isAdmin()) {
return true;
}
// Manager cannot access users page
if ($page === 'users' && $_SESSION['admin_role'] === 'manager') {
return false;
}
return true;
}
// Helper function to redirect if not authorized
function requireLogin() {
if (!isLoggedIn()) {
header('Location: index.php');
exit;
}
}
// Helper function to require admin role
function requireAdmin() {
requireLogin();
if (!isAdmin()) {
header('Location: index.php');
exit;
}
}
// Helper function to log admin activity
function logActivity($admin_id, $action, $description, $resource_type = null, $resource_id = null) {
try {
$pdo = getDBConnection();
$stmt = $pdo->prepare("
INSERT INTO admin_activity_log (admin_id, action, resource_type, resource_id, description, ip_address)
VALUES (?, ?, ?, ?, ?, ?)
");
$stmt->execute([
$admin_id,
$action,
$resource_type,
$resource_id,
$description,
$_SERVER['REMOTE_ADDR'] ?? null
]);
} catch (Exception $e) {
error_log("Activity log error: " . $e->getMessage());
}
}
// =========================================================
// COMPANY SETTINGS HELPERS
// =========================================================
/**
* Read a value from the company_settings key-value table.
* Caches results in a static array so the table is queried at most once per request.
*/
function getCompanySetting($key, $default = '') {
static $cache = null;
if ($cache === null) {
$cache = [];
try {
$pdo = getDBConnection();
$stmt = $pdo->query("SELECT setting_key, setting_value FROM company_settings");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
$cache[$row['setting_key']] = $row['setting_value'];
}
} catch (Exception $e) {
// Leave cache empty; defaults will be returned
}
}
return $cache[$key] ?? $default;
}
/**
* Mask an email address for privacy display.
* "john.doe@gmail.com" -> "jo*****@gmail.com"
* Keeps first 2 chars of local part, masks the rest, leaves domain visible.
* Short local parts (<=2 chars) are masked to "j***" form.
*/
function maskEmail($email) {
if (empty($email) || !is_string($email) || strpos($email, '@') === false) {
return $email;
}
list($local, $domain) = explode('@', $email, 2);
$localLen = strlen($local);
if ($localLen <= 2) {
$maskedLocal = substr($local, 0, 1) . '***';
} else {
$maskedLocal = substr($local, 0, 2) . str_repeat('*', max(3, $localLen - 2));
}
return $maskedLocal . '@' . $domain;
}
/**
* Convenience: returns email masked or unmasked based on the
* `email_mask_enabled` company setting (set via settings.php).
* - "1" or "on" or true -> masked
* - anything else -> raw email
*/
function displayEmail($email) {
$enabled = getCompanySetting('email_mask_enabled', '0');
if ($enabled === '1' || $enabled === 'on' || $enabled === 'true' || $enabled === 1 || $enabled === true) {
return maskEmail($email);
}
return $email;
}
?>
-------------------- END OF FILE --------------------
FILE: RR SHOP/demand.php
TYPE: PHP
SIZE: 10.75 KB
------------------------------------------------------------
8.7,
'growth_rate' => 15.3,
'market_score' => 92,
'forecast_accuracy' => 87.5
];
$demand_trends = [
['category' => 'Technology Panels', 'demand_level' => 'High', 'growth' => '+23%', 'priority' => 'High'],
['category' => 'Healthcare Surveys', 'demand_level' => 'Medium', 'growth' => '+12%', 'priority' => 'Medium'],
['category' => 'Financial Services', 'demand_level' => 'High', 'growth' => '+18%', 'priority' => 'High'],
['category' => 'Retail Analytics', 'demand_level' => 'Low', 'growth' => '-5%', 'priority' => 'Low'],
['category' => 'Education Research', 'demand_level' => 'Medium', 'growth' => '+8%', 'priority' => 'Medium']
];
$market_insights = [
['insight' => 'Technology sector showing strongest demand growth', 'impact' => 'High', 'action' => 'Expand tech panel offerings'],
['insight' => 'Q3 demand exceeded forecasts by 15%', 'impact' => 'Medium', 'action' => 'Adjust Q4 capacity planning'],
['insight' => 'Mobile panel participation up 34%', 'impact' => 'High', 'action' => 'Optimize mobile experience'],
['insight' => 'Weekend response rates declining', 'impact' => 'Low', 'action' => 'Review scheduling strategy']
];
include 'includes/header.php';
?>
Demand Analysis
Market insights and demand forecasting for strategic decisions
📈
Demand Index
+0.8 vs last month
🚀
%
Growth Rate
+2.3% vs Q2
🎯
Market Score
Excellent rating
🔮
%
Forecast Accuracy
+5.2% improvement
Demand Trends
Current Demand
Forecast
Category Performance
Key Market Insights
$insight): ?>
Impact
Recommended Action:
-------------------- END OF FILE --------------------
FILE: RR SHOP/error-404.html
TYPE: HTML
SIZE: 6.9 KB
------------------------------------------------------------
Page Not Found - Relevant Reflex
RR
404
Page Not Found
Sorry, the page you are looking for doesn't exist or has been moved.
Let's get you back on track with our panel management system.
-------------------- END OF FILE --------------------
FILE: RR SHOP/error-500.html
TYPE: HTML
SIZE: 8.81 KB
------------------------------------------------------------
Server Error - Relevant Reflex
RR
500
Internal Server Error
We're experiencing technical difficulties with our panel management system.
Our team has been notified and is working to resolve this issue.
Test your pricing by entering an LOI. Uses the saved rates above and live exchange rates.
INR Price
—
USD Price
—
EUR Price
—
query("SELECT p.id, p.project_id as project_code, p.client_id FROM projects p LEFT JOIN invoices i ON i.project_id=p.id WHERE p.status='Closed' AND i.id IS NULL");
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $m) {
if (function_exists('generateProjectInvoice')) generateProjectInvoice($pdo, $m['id'], $m['project_code'], $m['client_id']);
}
} catch (Exception $e) {}
// Update statuses
if (function_exists('updateInvoiceStatuses')) updateInvoiceStatuses($pdo);
// Handle invoice POST actions
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$invAction = $_POST['action'];
$invId = intval($_POST['invoice_id'] ?? 0);
if ($invAction === 'mark_paid' && $invId) {
$pdo->prepare("UPDATE invoices SET status='paid', paid_date=?, payment_reference=?, remarks=?, updated_at=NOW() WHERE id=?")->execute([$_POST['paid_date'] ?? date('Y-m-d'), trim($_POST['payment_reference'] ?? ''), trim($_POST['remarks'] ?? ''), $invId]);
$success = 'Invoice marked as paid.';
logActivity($_SESSION['admin_id'], 'update', "Marked invoice #$invId as paid", 'invoice', $invId);
}
if ($invAction === 'save_remarks' && $invId) {
$pdo->prepare("UPDATE invoices SET remarks=?, updated_at=NOW() WHERE id=?")->execute([trim($_POST['remarks'] ?? ''), $invId]);
$success = 'Remarks saved.';
}
if ($invAction === 'revert_unpaid' && $invId) {
$pdo->prepare("UPDATE invoices SET status='invoiced', paid_date=NULL, payment_reference=NULL, updated_at=NOW() WHERE id=?")->execute([$invId]);
if (function_exists('updateInvoiceStatuses')) updateInvoiceStatuses($pdo);
$success = 'Invoice reverted to unpaid.';
}
}
// Ban enforcement
try {
$bannedClients = $pdo->query("SELECT DISTINCT client_id FROM invoices WHERE status != 'paid' AND due_date < DATE_SUB(CURDATE(), INTERVAL 90 DAY)")->fetchAll(PDO::FETCH_COLUMN);
if (!empty($bannedClients)) {
$ph = implode(',', array_fill(0, count($bannedClients), '?'));
$pdo->prepare("UPDATE projects SET status='On hold' WHERE client_id IN ($ph) AND status='Live'")->execute($bannedClients);
}
} catch (Exception $e) {}
// Invoice filters
$fStatus=$_GET['inv_status']??''; $fClient=$_GET['inv_client']??''; $fDateFrom=$_GET['inv_from']??'';
$fDateTo=$_GET['inv_to']??''; $fSearch=trim($_GET['inv_search']??''); $fCurrency=$_GET['inv_currency']??'';
$fDueFrom=$_GET['inv_due_from']??''; $fDueTo=$_GET['inv_due_to']??'';
$invSort=$_GET['inv_sort']??'invoice_date'; $invDir=($_GET['inv_dir']??'desc')==='asc'?'ASC':'DESC';
$invAllowed=['invoice_number','invoice_date','due_date','total_amount','status','project_code','client_name'];
if (!in_array($invSort,$invAllowed)) $invSort='invoice_date';
$invWhere=["1=1"]; $invParams=[];
if($fStatus){$invWhere[]="i.status=?";$invParams[]=$fStatus;}
if($fClient){$invWhere[]="i.client_id=?";$invParams[]=(int)$fClient;}
if($fCurrency){$invWhere[]="i.currency=?";$invParams[]=$fCurrency;}
if($fDateFrom){$invWhere[]="i.invoice_date>=?";$invParams[]=$fDateFrom;}
if($fDateTo){$invWhere[]="i.invoice_date<=?";$invParams[]=$fDateTo;}
if($fDueFrom){$invWhere[]="i.due_date>=?";$invParams[]=$fDueFrom;}
if($fDueTo){$invWhere[]="i.due_date<=?";$invParams[]=$fDueTo;}
if($fSearch){$invWhere[]="(i.invoice_number LIKE ? OR p.project_name LIKE ? OR i.project_code LIKE ? OR c.company_name LIKE ?)";$s="%$fSearch%";$invParams=array_merge($invParams,[$s,$s,$s,$s]);}
$invWc=implode(' AND ',$invWhere);
$stmt=$pdo->prepare("SELECT i.*,p.project_name,p.industry,c.company_name as client_company,c.contact_person FROM invoices i INNER JOIN projects p ON i.project_id=p.id INNER JOIN clients c ON i.client_id=c.id WHERE $invWc ORDER BY $invSort $invDir");
$stmt->execute($invParams);
$allInvoices=$stmt->fetchAll(PDO::FETCH_ASSOC);
$invSumAll=$invSumPaid=$invSumOut=0;
$invSc=['invoiced'=>0,'paid'=>0,'due'=>0,'overdue'=>0,'critical'=>0,'banned'=>0];
foreach($allInvoices as $inv){
$invSumAll+=$inv['total_amount'];
if($inv['status']==='paid')$invSumPaid+=$inv['total_amount'];else $invSumOut+=$inv['total_amount'];
$invSc[$inv['status']]=($invSc[$inv['status']]??0)+1;
}
$allClients=$pdo->query("SELECT id,company_name FROM clients ORDER BY company_name")->fetchAll(PDO::FETCH_ASSOC);
?>
Admin Users: activeAdmin DB: u752449863_rrshopPanel DB: u752449863_rrpanelPHP:Server Time (IST):Timezone:
-------------------- END OF FILE --------------------
FILE: RR SHOP/letterhead-download.php
TYPE: PHP
SIZE: 7.21 KB
------------------------------------------------------------
TCPDF not found
Please ensure TCPDF is installed in the /tcpdf directory.
Default: every member earns ₹5 per verified-email referral. Enable below to give this member tiered bonuses instead. Tier 1 (first 100) is always ₹5 and cannot be changed.
Current verified referrals by this member:
·
Next referral falls in:
Account Information
Email Verified
✓ Yes' : '✗ No'; ?>
Onboarding Completed
✓ Yes' : '⏳ No'; ?>
Member Since
Last Updated
-------------------- END OF FILE --------------------
FILE: RR SHOP/member_view.php
TYPE: PHP
SIZE: 91.14 KB
------------------------------------------------------------
prepare("
SELECT
u.*,
up.points as current_points,
up.total_earned,
up.total_redeemed,
mv.mobile_number,
mv.is_verified as mobile_verified,
mv.verified_at as mobile_verified_at
FROM users u
LEFT JOIN user_points up ON u.id = up.user_id
LEFT JOIN mobile_verifications mv ON u.id = mv.user_id
WHERE u.id = ?
");
$stmt->execute([$member_id]);
$member = $stmt->fetch();
if (!$member) {
header('Location: panel.php');
exit;
}
// Fetch profiler completion
$completionStmt = $panelPdo->prepare("
SELECT * FROM profiler_completion WHERE user_id = ?
");
$completionStmt->execute([$member_id]);
$completionData = $completionStmt->fetchAll();
// Fetch actual profiler responses for this member
$profilerResponses = [];
try {
$respStmt = $panelPdo->prepare("SELECT section, question_id, response FROM user_profiler WHERE user_id = ? ORDER BY section, question_id");
$respStmt->execute([$member_id]);
while ($row = $respStmt->fetch()) {
if (!isset($profilerResponses[$row['section']])) $profilerResponses[$row['section']] = [];
$profilerResponses[$row['section']][$row['question_id']] = json_decode($row['response'], true);
}
} catch (Exception $e) { $profilerResponses = []; }
// Extract PAN & UPI details from profile section
$memberPanNumber = $profilerResponses['profile']['pan_number'] ?? '';
$memberPanName = $profilerResponses['profile']['pan_name'] ?? '';
$memberPanStatus = $profilerResponses['profile']['pan_status'] ?? '';
if (!empty($memberPanNumber) && empty($memberPanStatus)) $memberPanStatus = 'pending';
$memberUpiId = $profilerResponses['profile']['upi_id'] ?? '';
// Profiler question definitions for human-readable labels
$profilerDefs = [
'personal_background' => [
'name' => 'Personal Background',
'questions' => [
'education_level' => ['q'=>'Highest level of education','opts'=>['below_10th'=>'Below 10th Standard','10th_pass'=>'10th Standard','12th_pass'=>'12th Standard/Intermediate','diploma'=>'Diploma/ITI','graduation'=>'Graduation','post_graduation'=>'Post Graduation','professional'=>'Professional Degree','doctorate'=>'Doctorate/PhD']],
'employment_status' => ['q'=>'Current employment status','opts'=>['student'=>'Student','employed_private'=>'Employed - Private Sector','employed_government'=>'Employed - Government/Public Sector','self_employed'=>'Self Employed/Business Owner','freelancer'=>'Freelancer/Consultant','homemaker'=>'Homemaker','retired'=>'Retired','unemployed'=>'Currently Unemployed']],
'occupation_sector' => ['q'=>'Work sector','opts'=>['it_software'=>'IT/Software','banking_finance'=>'Banking & Finance','healthcare'=>'Healthcare','education'=>'Education','manufacturing'=>'Manufacturing','retail'=>'Retail','media'=>'Media & Entertainment','government'=>'Government','telecom'=>'Telecommunications','fmcg'=>'FMCG','auto'=>'Automotive','real_estate'=>'Real Estate','agriculture'=>'Agriculture','legal'=>'Legal','other'=>'Other']],
'monthly_income' => ['q'=>'Monthly household income range','opts'=>['below_15000'=>'Below ₹15,000','15000_25000'=>'₹15,000 - ₹25,000','25000_50000'=>'₹25,000 - ₹50,000','50000_75000'=>'₹50,000 - ₹75,000','75000_100000'=>'₹75,000 - ₹1,00,000','100000_200000'=>'₹1,00,000 - ₹2,00,000','200000_500000'=>'₹2,00,000 - ₹5,00,000','above_500000'=>'Above ₹5,00,000','prefer_not_to_say'=>'Prefer not to say']],
'marital_status' => ['q'=>'Marital status','opts'=>['single'=>'Single','married'=>'Married','divorced'=>'Divorced','widowed'=>'Widowed','separated'=>'Separated','live_in'=>'Live-in Relationship']],
'language_primary' => ['q'=>'Primary language spoken at home','opts'=>['hindi'=>'Hindi','english'=>'English','tamil'=>'Tamil','telugu'=>'Telugu','marathi'=>'Marathi','bengali'=>'Bengali','gujarati'=>'Gujarati','kannada'=>'Kannada','malayalam'=>'Malayalam','punjabi'=>'Punjabi','odia'=>'Odia','urdu'=>'Urdu','assamese'=>'Assamese','other'=>'Other']],
'languages_known' => ['q'=>'Languages you can read/write fluently','opts'=>['hindi'=>'Hindi','english'=>'English','tamil'=>'Tamil','telugu'=>'Telugu','marathi'=>'Marathi','bengali'=>'Bengali','gujarati'=>'Gujarati','kannada'=>'Kannada','malayalam'=>'Malayalam','punjabi'=>'Punjabi','odia'=>'Odia','urdu'=>'Urdu','assamese'=>'Assamese','other'=>'Other']],
'area_type' => ['q'=>'Area type','opts'=>['metro'=>'Metro City','tier1'=>'Tier 1 City','tier2'=>'Tier 2 City','tier3'=>'Tier 3/Small Town','semi_urban'=>'Semi-Urban','rural'=>'Rural/Village']],
]
],
'household_family' => [
'name' => 'Household & Family',
'questions' => [
'household_size' => ['q'=>'People living in household','opts'=>['1'=>'Just me','2'=>'2 people','3'=>'3 people','4'=>'4 people','5'=>'5 people','6_plus'=>'6 or more']],
'children_count' => ['q'=>'Number of children','opts'=>['none'=>'No children','1'=>'1 child','2'=>'2 children','3'=>'3 children','4_plus'=>'4 or more']],
'children_age_group' => ['q'=>'Age groups of children','opts'=>['infant'=>'0-2 years (Infant)','toddler'=>'3-5 years (Toddler)','child'=>'6-12 years (Child)','teen'=>'13-17 years (Teen)','adult'=>'18+ years (Adult)','not_applicable'=>'Not Applicable']],
'home_ownership' => ['q'=>'Home ownership status','opts'=>['own_house'=>'Own House','rented'=>'Rented','family_owned'=>'Family Owned','company_provided'=>'Company Provided','paying_guest'=>'Paying Guest/Hostel']],
'vehicle_ownership' => ['q'=>'Vehicles owned','opts'=>['two_wheeler'=>'Two Wheeler (Bike/Scooter)','car_hatchback'=>'Car - Hatchback','car_sedan'=>'Car - Sedan','car_suv'=>'Car - SUV/MUV','ev_two_wheeler'=>'EV Two Wheeler','ev_car'=>'Electric Car','bicycle'=>'Bicycle','none'=>'No Vehicle','commercial'=>'Commercial Vehicle']],
'domestic_help' => ['q'=>'Domestic help employed','opts'=>['full_time'=>'Full-time','part_time'=>'Part-time','cook'=>'Cook only','driver'=>'Driver','none'=>'No domestic help']],
'decision_maker' => ['q'=>'Primary decision maker for purchases','opts'=>['self'=>'Self','spouse'=>'Spouse/Partner','parents'=>'Parents','joint'=>'Joint Decision','other'=>'Other Family Member']],
'pet_ownership' => ['q'=>'Pets owned','opts'=>['dog'=>'Dog','cat'=>'Cat','bird'=>'Bird','fish'=>'Fish','other_pet'=>'Other','no_pets'=>'No Pets']],
'household_income' => ['q'=>'Total monthly household income','opts'=>['below_15k'=>'Below ₹15,000','15k_25k'=>'₹15,000 - ₹25,000','25k_50k'=>'₹25,000 - ₹50,000','50k_75k'=>'₹50,000 - ₹75,000','75k_1l'=>'₹75,000 - ₹1,00,000','1l_2l'=>'₹1,00,000 - ₹2,00,000','2l_5l'=>'₹2,00,000 - ₹5,00,000','above_5l'=>'Above ₹5,00,000','prefer_not'=>'Prefer not to say']],
]
],
'shopping_lifestyle' => [
'name' => 'Shopping & Lifestyle',
'questions' => [
'online_shopping_frequency' => ['q'=>'Online shopping frequency','opts'=>['daily'=>'Almost daily','weekly'=>'Weekly','biweekly'=>'Every 2 weeks','monthly'=>'Monthly','quarterly'=>'Every few months','rarely'=>'Rarely','never'=>'Never']],
'preferred_ecommerce' => ['q'=>'Preferred e-commerce platforms','opts'=>['amazon'=>'Amazon','flipkart'=>'Flipkart','myntra'=>'Myntra','meesho'=>'Meesho','ajio'=>'AJIO','nykaa'=>'Nykaa','tatacliq'=>'Tata CLiQ','jiomart'=>'JioMart','snapdeal'=>'Snapdeal','bigbasket'=>'BigBasket','other'=>'Other']],
'monthly_online_spend' => ['q'=>'Monthly online shopping spend','opts'=>['below_500'=>'Below ₹500','500_2000'=>'₹500 - ₹2,000','2000_5000'=>'₹2,000 - ₹5,000','5000_10000'=>'₹5,000 - ₹10,000','10000_25000'=>'₹10,000 - ₹25,000','above_25000'=>'Above ₹25,000']],
'shopping_categories' => ['q'=>'Frequently purchased categories','opts'=>['electronics'=>'Electronics & Gadgets','fashion'=>'Fashion & Clothing','beauty'=>'Beauty & Personal Care','home_kitchen'=>'Home & Kitchen','grocery'=>'Groceries & Essentials','books'=>'Books & Stationery','sports'=>'Sports & Fitness','baby'=>'Baby & Kids','health'=>'Health & Wellness','jewelry'=>'Jewelry & Accessories','automotive'=>'Automotive Accessories']],
'brand_preference' => ['q'=>'Brand preference','opts'=>['premium'=>'Prefer premium brands','mid_range'=>'Prefer mid-range brands','value'=>'Prefer value/budget brands','no_preference'=>'No brand preference','mix'=>'Mix of all']],
'deal_influence' => ['q'=>'Influence of deals on purchases','opts'=>['always'=>'Always wait for deals','often'=>'Often look for deals','sometimes'=>'Sometimes check','rarely'=>'Rarely care about deals','never'=>'Never influenced by deals']],
]
],
'technology_digital' => [
'name' => 'Technology & Digital',
'questions' => [
'smartphone_brand' => ['q'=>'Current smartphone brand','opts'=>['samsung'=>'Samsung','apple'=>'Apple','xiaomi'=>'Xiaomi/Redmi','realme'=>'Realme','oppo'=>'OPPO','vivo'=>'Vivo','oneplus'=>'OnePlus','nothing'=>'Nothing','google'=>'Google Pixel','motorola'=>'Motorola','other'=>'Other']],
'smartphone_price' => ['q'=>'Smartphone price range','opts'=>['below_10000'=>'Below ₹10,000','10000_15000'=>'₹10,000 - ₹15,000','15000_25000'=>'₹15,000 - ₹25,000','25000_40000'=>'₹25,000 - ₹40,000','40000_60000'=>'₹40,000 - ₹60,000','above_60000'=>'Above ₹60,000']],
'internet_provider' => ['q'=>'Primary internet provider','opts'=>['jio'=>'Jio','airtel'=>'Airtel','vi'=>'Vi (Vodafone Idea)','bsnl'=>'BSNL','act'=>'ACT Fibernet','tataplay'=>'Tata Play Fiber','local_isp'=>'Local ISP','other'=>'Other']],
'daily_internet_hours' => ['q'=>'Daily internet usage hours','opts'=>['less_1'=>'Less than 1 hour','1_3'=>'1-3 hours','3_5'=>'3-5 hours','5_8'=>'5-8 hours','above_8'=>'More than 8 hours']],
'social_media_usage' => ['q'=>'Social media platforms used regularly','opts'=>['whatsapp'=>'WhatsApp','instagram'=>'Instagram','facebook'=>'Facebook','youtube'=>'YouTube','twitter'=>'Twitter/X','linkedin'=>'LinkedIn','snapchat'=>'Snapchat','telegram'=>'Telegram','koo'=>'Koo','pinterest'=>'Pinterest','reddit'=>'Reddit','threads'=>'Threads']],
'smart_devices' => ['q'=>'Smart devices owned','opts'=>['smart_tv'=>'Smart TV','smart_watch'=>'Smartwatch/Band','smart_speaker'=>'Smart Speaker','tablet'=>'Tablet/iPad','laptop'=>'Laptop','desktop'=>'Desktop PC','gaming_console'=>'Gaming Console','smart_home'=>'Smart Home Devices','none'=>'None of these']],
'streaming_services' => ['q'=>'Streaming services subscribed to','opts'=>['netflix'=>'Netflix','prime_video'=>'Amazon Prime Video','hotstar'=>'Disney+ Hotstar','sonyliv'=>'SonyLIV','zee5'=>'ZEE5','jiocinema'=>'JioCinema','youtube_premium'=>'YouTube Premium','spotify'=>'Spotify','apple_music'=>'Apple Music','none'=>'None']],
]
],
'travel_transportation' => [
'name' => 'Travel & Transportation',
'questions' => [
'daily_commute' => ['q'=>'Primary daily commute mode','opts'=>['own_car'=>'Own Car','own_bike'=>'Own Bike/Scooter','public_bus'=>'Public Bus','metro_train'=>'Metro/Local Train','auto_rickshaw'=>'Auto Rickshaw','cab_ola_uber'=>'Cab (Ola/Uber)','bicycle'=>'Bicycle','walk'=>'Walk','work_from_home'=>'Work from Home']],
'travel_frequency' => ['q'=>'Domestic travel frequency (per year)','opts'=>['never'=>'Rarely/Never','1_2'=>'1-2 times','3_5'=>'3-5 times','6_10'=>'6-10 times','above_10'=>'More than 10 times']],
'international_travel' => ['q'=>'International travel experience','opts'=>['never'=>'Never traveled abroad','once'=>'Once','2_5'=>'2-5 times','6_plus'=>'More than 5 times','frequent'=>'Frequent international traveler']],
'travel_booking' => ['q'=>'Travel booking platforms used','opts'=>['makemytrip'=>'MakeMyTrip','goibibo'=>'Goibibo','irctc'=>'IRCTC','booking_com'=>'Booking.com','cleartrip'=>'Cleartrip','yatra'=>'Yatra','airbnb'=>'Airbnb','oyo'=>'OYO','travel_agent'=>'Travel Agent','direct'=>'Direct with airline/hotel']],
'travel_preference' => ['q'=>'Travel accommodation preference','opts'=>['budget'=>'Budget Hotels','mid_range'=>'Mid-range Hotels','luxury'=>'Luxury Hotels','homestay'=>'Homestays','hostel'=>'Hostels/Backpacker','resort'=>'Resorts','relative'=>"Relative's/Friend's place"]],
'cab_usage' => ['q'=>'Ride-hailing usage frequency','opts'=>['daily'=>'Daily','weekly'=>'Few times a week','monthly'=>'Few times a month','rarely'=>'Rarely','never'=>'Never use']],
]
],
'health_fitness' => [
'name' => 'Health & Fitness',
'questions' => [
'exercise_frequency' => ['q'=>'Exercise frequency','opts'=>['daily'=>'Daily','4_6'=>'4-6 times a week','2_3'=>'2-3 times a week','once_week'=>'Once a week','rarely'=>'Rarely','never'=>'Never']],
'fitness_activities' => ['q'=>'Fitness activities','opts'=>['gym'=>'Gym/Weight Training','yoga'=>'Yoga','running'=>'Running/Jogging','walking'=>'Walking','swimming'=>'Swimming','cycling'=>'Cycling','sports'=>'Sports','dance'=>'Dance/Zumba','home_workout'=>'Home Workout','martial_arts'=>'Martial Arts','none'=>'None']],
'health_insurance' => ['q'=>'Health insurance coverage','opts'=>['employer'=>'Employer-provided','individual'=>'Individual Policy','family_floater'=>'Family Floater','government'=>'Government Scheme','no_insurance'=>'No Health Insurance']],
'diet_type' => ['q'=>'Dietary preference','opts'=>['vegetarian'=>'Vegetarian','non_vegetarian'=>'Non-Vegetarian','eggetarian'=>'Eggetarian','vegan'=>'Vegan','jain'=>'Jain','flexitarian'=>'Flexitarian']],
'health_conditions' => ['q'=>'Health conditions managed','opts'=>['diabetes'=>'Diabetes','hypertension'=>'Hypertension/BP','thyroid'=>'Thyroid','heart'=>'Heart Condition','asthma'=>'Asthma/Respiratory','allergies'=>'Allergies','obesity'=>'Obesity','none'=>'None','prefer_not'=>'Prefer not to say']],
'wellness_spend' => ['q'=>'Monthly health/wellness spend','opts'=>['below_500'=>'Below ₹500','500_2000'=>'₹500 - ₹2,000','2000_5000'=>'₹2,000 - ₹5,000','5000_10000'=>'₹5,000 - ₹10,000','above_10000'=>'Above ₹10,000']],
'health_tracking' => ['q'=>'Health/fitness tracking tools used','opts'=>['fitness_band'=>'Fitness Band/Smartwatch','phone_app'=>'Phone App','manual'=>'Manual Tracking','health_checkup'=>'Regular Health Checkups','none'=>'None']],
]
],
'entertainment_media' => [
'name' => 'Entertainment & Media',
'questions' => [
'content_language' => ['q'=>'Content consumption languages','opts'=>['hindi'=>'Hindi','english'=>'English','tamil'=>'Tamil','telugu'=>'Telugu','malayalam'=>'Malayalam','kannada'=>'Kannada','bengali'=>'Bengali','marathi'=>'Marathi','punjabi'=>'Punjabi','other'=>'Other']],
'entertainment_preference' => ['q'=>'Entertainment preferences','opts'=>['movies'=>'Movies','web_series'=>'Web Series','tv_shows'=>'TV Shows','music'=>'Music','podcasts'=>'Podcasts','gaming'=>'Gaming','reading'=>'Reading/Books','live_events'=>'Live Events/Concerts','sports_viewing'=>'Watching Sports']],
'news_source' => ['q'=>'Primary news sources','opts'=>['tv_news'=>'TV News Channels','newspaper'=>'Newspaper','news_apps'=>'News Apps','social_media'=>'Social Media','youtube'=>'YouTube','radio'=>'Radio','word_of_mouth'=>'Word of Mouth']],
'gaming_platform' => ['q'=>'Gaming platforms used','opts'=>['mobile'=>'Mobile Gaming','pc'=>'PC Gaming','playstation'=>'PlayStation','xbox'=>'Xbox','nintendo'=>'Nintendo','no_gaming'=>'Do not play games']],
'music_preference' => ['q'=>'Music listening platforms','opts'=>['spotify'=>'Spotify','youtube_music'=>'YouTube Music','jiosaavn'=>'JioSaavn','gaana'=>'Gaana','apple_music'=>'Apple Music','wynk'=>'Wynk','amazon_music'=>'Amazon Music','radio'=>'FM Radio','none'=>'None']],
'cinema_frequency' => ['q'=>'Movie theatre visits per month','opts'=>['never'=>'Never','once'=>'Once a month','2_3'=>'2-3 times a month','weekly'=>'Weekly','more'=>'More than weekly']],
]
],
'food_dining' => [
'name' => 'Food & Dining',
'questions' => [
'dining_out_frequency' => ['q'=>'Dining out/ordering frequency','opts'=>['daily'=>'Almost Daily','3_5_week'=>'3-5 times a week','1_2_week'=>'1-2 times a week','few_month'=>'Few times a month','rarely'=>'Rarely','never'=>'Never']],
'food_delivery_apps' => ['q'=>'Food delivery apps used','opts'=>['zomato'=>'Zomato','swiggy'=>'Swiggy','eatsure'=>'EatSure','dominos'=>'Domino\'s App','mcdonalds'=>'McDonald\'s App','other'=>'Other App','none'=>'Don\'t use delivery apps']],
'cuisine_preference' => ['q'=>'Favourite cuisines','opts'=>['north_indian'=>'North Indian','south_indian'=>'South Indian','chinese'=>'Chinese/Indo-Chinese','italian'=>'Italian/Pizza/Pasta','mughlai'=>'Mughlai/Biryani','street_food'=>'Street Food','continental'=>'Continental','japanese'=>'Japanese/Sushi','thai'=>'Thai','fast_food'=>'Fast Food/Burgers','healthy'=>'Healthy/Salads']],
'cooking_frequency' => ['q'=>'Home cooking frequency','opts'=>['all_meals'=>'All meals at home','mostly'=>'Most meals at home','half'=>'About half','rarely'=>'Rarely cook','never'=>'Never cook']],
'monthly_food_spend' => ['q'=>'Monthly food/dining spend','opts'=>['below_2000'=>'Below ₹2,000','2000_5000'=>'₹2,000 - ₹5,000','5000_10000'=>'₹5,000 - ₹10,000','10000_20000'=>'₹10,000 - ₹20,000','above_20000'=>'Above ₹20,000']],
'beverage_preference' => ['q'=>'Daily beverages','opts'=>['tea'=>'Tea (Chai)','coffee'=>'Coffee','green_tea'=>'Green Tea','juice'=>'Fresh Juice','soft_drinks'=>'Soft Drinks/Cola','energy_drinks'=>'Energy Drinks','water_only'=>'Plain Water Only','milk'=>'Milk/Flavored Milk','alcohol'=>'Alcoholic Beverages']],
]
],
'financial_services' => [
'name' => 'Financial Services',
'questions' => [
'bank_account' => ['q'=>'Primary bank','opts'=>['sbi'=>'SBI','hdfc'=>'HDFC Bank','icici'=>'ICICI Bank','axis'=>'Axis Bank','kotak'=>'Kotak Mahindra','pnb'=>'PNB','bob'=>'Bank of Baroda','idfc'=>'IDFC First','yes'=>'Yes Bank','indusind'=>'IndusInd','other_private'=>'Other Private Bank','other_psu'=>'Other PSU Bank','payment_bank'=>'Payment Bank (Paytm/Airtel)']],
'credit_card' => ['q'=>'Credit card ownership','opts'=>['yes_multiple'=>'Yes, multiple cards','yes_one'=>'Yes, one card','no_debit'=>'No, use debit card only','no_card'=>'No cards']],
'credit_products' => ['q'=>'Credit products used','opts'=>['credit_card'=>'Credit card','personal_loan'=>'Personal loan','home_loan'=>'Home loan','car_loan'=>'Car loan','education_loan'=>'Education loan','bnpl'=>'Buy Now Pay Later','gold_loan'=>'Gold loan','none'=>'None']],
'investment_types' => ['q'=>'Investment instruments used','opts'=>['fd'=>'Fixed Deposits','mutual_funds'=>'Mutual Funds','stocks'=>'Direct Stocks','ppf'=>'PPF/EPF','gold'=>'Gold/Gold Bonds','real_estate'=>'Real Estate','nps'=>'NPS','crypto'=>'Cryptocurrency','insurance'=>'Insurance Plans','none'=>'None']],
'upi_usage' => ['q'=>'UPI/Digital payment usage','opts'=>['primary'=>'Primary payment method','frequent'=>'Frequently use','sometimes'=>'Sometimes use','rarely'=>'Rarely use','never'=>'Never use']],
'upi_apps' => ['q'=>'UPI apps used','opts'=>['gpay'=>'Google Pay','phonepe'=>'PhonePe','paytm'=>'Paytm','amazon_pay'=>'Amazon Pay','bhim'=>'BHIM','cred'=>'CRED','whatsapp_pay'=>'WhatsApp Pay','bank_app'=>'Bank\'s own app','other'=>'Other']],
]
],
'communication_payments' => [
'name' => 'Communication & Payments',
'questions' => [
'mobile_operator' => ['q'=>'Primary mobile operator','opts'=>['jio'=>'Jio','airtel'=>'Airtel','vi'=>'Vi (Vodafone Idea)','bsnl'=>'BSNL','mtnl'=>'MTNL','other'=>'Other']],
'mobile_plan_spend' => ['q'=>'Monthly mobile plan spend','opts'=>['below_200'=>'Below ₹200','200_500'=>'₹200 - ₹500','500_1000'=>'₹500 - ₹1,000','1000_2000'=>'₹1,000 - ₹2,000','above_2000'=>'Above ₹2,000']],
'sim_type' => ['q'=>'SIM type','opts'=>['prepaid'=>'Prepaid','postpaid'=>'Postpaid','both'=>'Both']],
'communication_apps' => ['q'=>'Communication apps used daily','opts'=>['whatsapp'=>'WhatsApp','telegram'=>'Telegram','signal'=>'Signal','messenger'=>'Facebook Messenger','teams'=>'Microsoft Teams','zoom'=>'Zoom','google_meet'=>'Google Meet','discord'=>'Discord','slack'=>'Slack']],
'digital_wallet_balance' => ['q'=>'Average digital wallet balance maintained','opts'=>['below_500'=>'Below ₹500','500_2000'=>'₹500 - ₹2,000','2000_5000'=>'₹2,000 - ₹5,000','above_5000'=>'Above ₹5,000','dont_maintain'=>'Don\'t maintain balance']],
'online_purchase_category' => ['q'=>'Categories of online purchases in last 3 months','opts'=>['electronics'=>'Electronics','fashion'=>'Fashion & Clothing','grocery'=>'Groceries','food_delivery'=>'Food Delivery','travel'=>'Travel/Booking','entertainment'=>'Entertainment/Subscriptions','education'=>'Education/Courses','health'=>'Health/Pharmacy','financial'=>'Financial/Insurance','products'=>'Product reviews and feedback','none'=>'None of these']],
]
],
'household_classification' => [
'name' => 'Household Classification (ISEC)',
'questions' => [
'cwe_occupation' => ['q'=>'Occupation of Chief Wage Earner (CWE)','opts'=>['unskilled'=>'Unskilled Worker','skilled'=>'Skilled Worker','petty_trader'=>'Petty Trader','shop_owner'=>'Shop Owner / Businessman (No employees)','businessman_employer'=>'Businessman / Industrialist (1+ employees)','self_employed'=>'Self Employed Professional (Doctor, Lawyer, CA etc.)']],
'male_education_21plus' => ['q'=>'Education of highest educated male aged 21+','opts'=>['illiterate'=>'Illiterate','literate_no_formal'=>'Literate but no formal schooling','school_5_9'=>'School — 5th to 9th Standard','ssc_hsc'=>'SSC / HSC (10th–12th Standard)','some_college'=>'Some College (incl. Diploma) but not Graduate','graduate'=>'Graduate: General','graduate_professional'=>'Graduate: Professional / Post Graduate']],
'female_education_21plus' => ['q'=>'Education of highest educated female aged 21+','opts'=>['illiterate'=>'Illiterate','literate_no_formal'=>'Literate but no formal schooling','school_5_9'=>'School — 5th to 9th Standard','ssc_hsc'=>'SSC / HSC (10th–12th Standard)','some_college'=>'Some College (incl. Diploma) but not Graduate','graduate'=>'Graduate: General','graduate_professional'=>'Graduate: Professional / Post Graduate']],
]
]
];
// Fetch point transactions
$transactionsStmt = $panelPdo->prepare("
SELECT * FROM point_transactions
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT 50
");
$transactionsStmt->execute([$member_id]);
$transactions = $transactionsStmt->fetchAll();
// Fetch redemption requests
$redemptionsStmt = $panelPdo->prepare("
SELECT * FROM redemption_requests
WHERE user_id = ?
ORDER BY created_at DESC
");
$redemptionsStmt->execute([$member_id]);
$redemptions = $redemptionsStmt->fetchAll();
// Fetch support tickets
$ticketsStmt = $panelPdo->prepare("
SELECT * FROM support_tickets
WHERE user_id = ?
ORDER BY created_at DESC
LIMIT 10
");
$ticketsStmt->execute([$member_id]);
$tickets = $ticketsStmt->fetchAll();
// Fetch survey data for this member from admin DB
$adminPdo = getDBConnection();
// 1. Selected: in selection_members but NO survey URL assigned for that project
$selectedStmt = $adminPdo->prepare("
SELECT sm.selection_id, sm.sample_status, sm.assigned_at,
ps.selection_name, ps.status as selection_status, ps.incentive_amount, ps.reward_per_complete,
p.project_id, p.project_name, p.status as project_status, p.eloi,
c.company_name as client_name
FROM selection_members sm
JOIN project_selections ps ON sm.selection_id = ps.id
JOIN projects p ON ps.project_id = p.id
JOIN clients c ON ps.client_id = c.id
LEFT JOIN survey_urls su ON su.sent_to_user_id = sm.user_id AND su.project_id = p.project_id
WHERE sm.user_id = ? AND su.id IS NULL
ORDER BY sm.assigned_at DESC
");
$selectedStmt->execute([$member_id]);
$selectedSurveys = $selectedStmt->fetchAll();
// 2. Assigned: URL assigned but NOT yet sent (is_sent=0 and status='assigned')
$assignedStmt = $adminPdo->prepare("
SELECT su.id as url_id, su.unique_identifier, su.status as url_status, su.created_at as url_created,
ps.selection_name, ps.status as selection_status,
p.project_id, p.project_name, p.status as project_status, p.eloi,
c.company_name as client_name
FROM survey_urls su
JOIN projects p ON su.project_id = p.project_id
JOIN clients c ON p.client_id = c.id
LEFT JOIN project_selections ps ON su.assigned_to_selection_id = ps.id
WHERE su.sent_to_user_id = ? AND su.is_sent = 0 AND su.status = 'assigned'
ORDER BY su.created_at DESC
");
$assignedStmt->execute([$member_id]);
$assignedSurveys = $assignedStmt->fetchAll();
// 3. Sent: URL was sent (is_sent=1) but member has NOT interacted (no click/terminal status)
$sentStmt = $adminPdo->prepare("
SELECT su.id as url_id, su.unique_identifier, su.status as url_status, su.sent_at,
ps.selection_name, ps.status as selection_status,
p.project_id, p.project_name, p.status as project_status, p.eloi,
c.company_name as client_name
FROM survey_urls su
JOIN projects p ON su.project_id = p.project_id
JOIN clients c ON p.client_id = c.id
LEFT JOIN project_selections ps ON su.assigned_to_selection_id = ps.id
WHERE su.sent_to_user_id = ? AND su.is_sent = 1
AND su.status NOT IN ('clicked','complete','partial','earlyscreenout','latescreenout','quotafull','timeout')
ORDER BY su.sent_at DESC
");
$sentStmt->execute([$member_id]);
$sentSurveys = $sentStmt->fetchAll();
// 4. Taken: member actually interacted (clicked or has a terminal survey status)
// Differentiate: "From Mail" (is_sent=1) vs "From Portal" (is_sent=0, took via Take Survey button)
$takenStmt = $adminPdo->prepare("
SELECT su.id as url_id, su.unique_identifier, su.status as url_status, su.is_sent,
su.clicked_at, su.completed_at, su.actual_loi_seconds, su.quality_flag, su.quality_notes,
CASE WHEN su.is_sent = 1 THEN 'mail' ELSE 'portal' END as taken_source,
ps.selection_name, ps.status as selection_status, ps.reward_per_complete,
p.project_id, p.project_name, p.status as project_status, p.eloi,
c.company_name as client_name
FROM survey_urls su
JOIN projects p ON su.project_id = p.project_id
JOIN clients c ON p.client_id = c.id
LEFT JOIN project_selections ps ON su.assigned_to_selection_id = ps.id
WHERE su.sent_to_user_id = ? AND su.status IN ('clicked','complete','partial','earlyscreenout','latescreenout','quotafull','timeout')
ORDER BY su.clicked_at DESC
");
$takenStmt->execute([$member_id]);
$takenSurveys = $takenStmt->fetchAll();
// Build taken status breakdown
$takenByStatus = [];
$takenBySource = ['mail' => 0, 'portal' => 0];
foreach ($takenSurveys as $t) {
$s = $t['url_status'];
if (!isset($takenByStatus[$s])) $takenByStatus[$s] = [];
$takenByStatus[$s][] = $t;
$takenBySource[$t['taken_source']]++;
}
} catch (Exception $e) {
error_log("Member view error: " . $e->getMessage());
header('Location: panel.php');
exit;
}
include 'includes/header.php';
?>
-------------------- END OF FILE --------------------
FILE: RR SHOP/members.php
TYPE: PHP
SIZE: 29.45 KB
------------------------------------------------------------
0) {
try {
$panelPdo = getPanelDBConnection();
$panelPdo->beginTransaction();
// Delete related records first
$panelPdo->prepare("DELETE FROM point_transactions WHERE user_id = ?")->execute([$member_id]);
$panelPdo->prepare("DELETE FROM user_points WHERE user_id = ?")->execute([$member_id]);
$panelPdo->prepare("DELETE FROM redemption_requests WHERE user_id = ?")->execute([$member_id]);
$panelPdo->prepare("DELETE FROM support_tickets WHERE user_id = ?")->execute([$member_id]);
$panelPdo->prepare("DELETE FROM support_messages WHERE sender_type = 'user' AND sender_id = ?")->execute([$member_id]);
$panelPdo->prepare("DELETE FROM user_profiler WHERE user_id = ?")->execute([$member_id]);
$panelPdo->prepare("DELETE FROM profiler_completion WHERE user_id = ?")->execute([$member_id]);
$panelPdo->prepare("DELETE FROM mobile_verifications WHERE user_id = ?")->execute([$member_id]);
$panelPdo->prepare("DELETE FROM email_verifications WHERE user_id = ?")->execute([$member_id]);
// Delete the user
$panelPdo->prepare("DELETE FROM users WHERE id = ?")->execute([$member_id]);
$panelPdo->commit();
logActivity($_SESSION['admin_id'], 'delete_member', "Deleted member #$member_id", 'member', $member_id);
$success_message = 'Member deleted successfully!';
} catch (Exception $e) {
$panelPdo->rollBack();
error_log("Delete member error: " . $e->getMessage());
$error_message = 'Error deleting member. Please try again.';
}
}
}
// Fetch all panel members with their points
try {
$panelPdo = getPanelDBConnection();
$stmt = $panelPdo->query("
SELECT
u.*,
up.points as current_points,
up.total_earned,
up.total_redeemed,
mv.mobile_number,
mv.is_verified as mobile_verified,
ev.token as verify_token
FROM users u
LEFT JOIN user_points up ON u.id = up.user_id
LEFT JOIN mobile_verifications mv ON u.id = mv.user_id
LEFT JOIN email_verifications ev ON ev.id = (
SELECT id FROM email_verifications WHERE user_id = u.id ORDER BY created_at DESC LIMIT 1
)
ORDER BY u.created_at DESC
");
$members = $stmt->fetchAll();
} catch (Exception $e) {
$members = [];
error_log("Fetch members error: " . $e->getMessage());
}
include 'includes/header.php';
?>
✅
⚠
Panel Members
View and manage all registered survey panel members
';
$payload = [
'personalizations' => [['to' => [['email' => $user['email']]], 'subject' => $subject]],
'from' => ['email' => SHOP_SENDER_EMAIL, 'name' => SHOP_SENDER_NAME],
'content' => [['type' => 'text/html', 'value' => $htmlBody]]
];
$ch = curl_init('https://api.sendgrid.com/v3/mail/send');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode($payload),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . SHOP_SENDGRID_API_KEY,
'Content-Type: application/json'
],
CURLOPT_TIMEOUT => 20,
CURLOPT_SSL_VERIFYPEER => true
]);
$response = curl_exec($ch);
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($statusCode === 202) {
logActivity($_SESSION['admin_id'], 'resend_verification', "Resent verification email to member #$member_id ({$user['email']})");
echo json_encode(['success' => true, 'message' => 'Verification email sent to ' . $user['email']]);
} else {
$decoded = json_decode($response, true);
$msg = $decoded['errors'][0]['message'] ?? $decoded['message'] ?? $response;
error_log("Resend verification failed for {$user['email']}: HTTP $statusCode — $response");
echo json_encode(['success' => false, 'message' => "Failed (HTTP $statusCode): $msg — (raw: " . substr($response, 0, 200) . ")"]);
}
} catch (Exception $e) {
error_log("Resend verification error: " . $e->getMessage());
echo json_encode(['success' => false, 'message' => 'Server error: ' . $e->getMessage()]);
}
exit;
}
try {
$panelPdo = getPanelDBConnection();
$stmt = $panelPdo->query("
SELECT
u.*,
up.points as current_points,
up.total_earned,
up.total_redeemed,
mv.mobile_number,
mv.is_verified as mobile_verified,
ev.token as verify_token
FROM users u
LEFT JOIN user_points up ON u.id = up.user_id
LEFT JOIN mobile_verifications mv ON u.id = mv.user_id
LEFT JOIN email_verifications ev ON ev.id = (
SELECT id FROM email_verifications WHERE user_id = u.id ORDER BY created_at DESC LIMIT 1
)
ORDER BY u.created_at DESC
");
$members = $stmt->fetchAll();
} catch (Exception $e) {
$members = [];
error_log("Fetch members error: " . $e->getMessage());
}
// =========================================================
// REFERRAL COUNTS (for stats cards)
// - Member referrals: rrpanel.member_referral_clicks (panel DB)
// - Affiliate referrals: rrshop.affiliate_signups (shop DB)
// Both filtered by email_verified = 1 (signup completed AND email confirmed)
// =========================================================
$referrals_by_members_verified = 0;
$referrals_by_affiliates_verified = 0;
try {
if (isset($panelPdo) && $panelPdo) {
$stmt = $panelPdo->query("
SELECT COUNT(*) FROM member_referral_clicks
WHERE email_verified = 1
");
$referrals_by_members_verified = (int)$stmt->fetchColumn();
}
} catch (Exception $e) {
error_log("Member referral count error: " . $e->getMessage());
}
try {
$shopPdo = getDBConnection();
$stmt = $shopPdo->query("
SELECT COUNT(*) FROM affiliate_signups
WHERE email_verified = 1
");
$referrals_by_affiliates_verified = (int)$stmt->fetchColumn();
} catch (Exception $e) {
error_log("Affiliate referral count error: " . $e->getMessage());
}
include 'includes/header.php';
?>
✅
⚠
ℹ
Panel Management
Manage all registered survey panel members
Total Members
$m['status'] === 'active')); ?>
Active Members
$m['email_verified'] == 1)); ?>
Email Verified
$m['mobile_verified'] == 1)); ?>
Mobile Verified
$m['onboarding_completed'] == 1)); ?>
Onboarded
!empty($m['isec_class']))); ?>
SEC Classified
Total Points
Referrals by Members (Email Verified)
Referrals by Affiliates (Email Verified)
✉ Send Verification Emails to Unverified Members
Sends a fresh 48-hour verification link to all members who registered in the selected date range and have not yet verified their email.
Send WhatsApp Verification Reminders to Unverified Members
Sends a fresh 48-hour verification link via WhatsApp (AiSensy template account_verification) to all members who registered in the selected date range, have not yet verified their email, and have confirmed their phone is a WhatsApp number. Members without a WhatsApp number, or who unchecked the "this is my WhatsApp" box at signup, are skipped automatically.
This action cannot be undone and will permanently delete:
Member profile and account data
All points and transaction history
Redemption requests
Support tickets and messages
Profiler data
✉
Confirm Send Verification Emails
You are about to send verification emails to all unverified members who registered between:
📅
Each member will receive a fresh verification link valid for 48 hours. Members who are already verified will not receive an email.
0 / …
emails sent
Do not close this window.
Confirm Send WhatsApp Reminders
You are about to send WhatsApp verification reminders to all unverified members with a WhatsApp number who registered between:
📅
Each member will receive a fresh verification link valid for 48 hours via WhatsApp using the account_verification template. Members already verified or without a WhatsApp number will be skipped.
0 / …
WhatsApp messages sent
Do not close this window.
-------------------- END OF FILE --------------------
FILE: RR SHOP/panelbook-generate.php
TYPE: PHP
SIZE: 35.82 KB
------------------------------------------------------------
TCPDF not found');
}
require_once $tcpdfPath;
// ═══════════════════════════════════════════════
// 1. COLLECT ALL DATA
// ═══════════════════════════════════════════════
try {
$panelPdo = new PDO(
'mysql:host=localhost;dbname=u752449863_rrpanel;charset=utf8mb4',
'u752449863_rrpaneladmin', 'S@n@h2016',
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC]
);
$shopPdo = getDBConnection();
} catch (Exception $e) {
error_log("PanelBook DB: " . $e->getMessage());
header('Location: index.php?error=panelbook_db'); exit;
}
$d = [];
$d['ts'] = date('F d, Y \a\t h:i A T');
$yr = date('Y');
$d['total'] = (int)$panelPdo->query("SELECT COUNT(*) FROM users")->fetchColumn();
$d['active'] = (int)$panelPdo->query("SELECT COUNT(*) FROM users WHERE status='active'")->fetchColumn();
$d['verified'] = (int)$panelPdo->query("SELECT COUNT(*) FROM users WHERE email_verified=1 AND status='active'")->fetchColumn();
$d['gender'] = $panelPdo->query("SELECT gender, COUNT(*) as c FROM users WHERE status='active' AND email_verified=1 GROUP BY gender ORDER BY c DESC")->fetchAll();
$s = $panelPdo->prepare("
SELECT CASE
WHEN (?-YEAR(date_of_birth)) BETWEEN 18 AND 24 THEN '18-24'
WHEN (?-YEAR(date_of_birth)) BETWEEN 25 AND 34 THEN '25-34'
WHEN (?-YEAR(date_of_birth)) BETWEEN 35 AND 44 THEN '35-44'
WHEN (?-YEAR(date_of_birth)) BETWEEN 45 AND 54 THEN '45-54'
WHEN (?-YEAR(date_of_birth)) BETWEEN 55 AND 64 THEN '55-64'
WHEN (?-YEAR(date_of_birth)) >= 65 THEN '65+'
ELSE 'Unknown' END as ag, COUNT(*) as c
FROM users WHERE status='active' AND email_verified=1 AND date_of_birth IS NOT NULL
GROUP BY ag ORDER BY MIN(?-YEAR(date_of_birth))
");
$s->execute(array_fill(0, 7, $yr));
$d['age'] = $s->fetchAll();
// SEC Class (ISEC) distribution
$d['sec'] = [];
try {
$d['sec'] = $panelPdo->query("
SELECT COALESCE(isec_class, 'Unknown') as sec_class, COUNT(*) as c
FROM users WHERE status='active' AND email_verified=1
GROUP BY sec_class
ORDER BY FIELD(COALESCE(isec_class, 'Unknown'), 'A','B','C','D','E','Unknown')
")->fetchAll();
} catch(Exception $e){}
// Geographic: Map PIN codes to Indian states
$PIN_TO_STATE = [
// Delhi
'110'=>'Delhi',
// Haryana
'12'=>'Haryana','13'=>'Haryana',
// Punjab
'14'=>'Punjab','15'=>'Punjab','16'=>'Punjab',
// Chandigarh (override 3-digit)
'160'=>'Chandigarh',
// Himachal Pradesh
'17'=>'Himachal Pradesh',
// Jammu & Kashmir / Ladakh
'18'=>'Jammu & Kashmir','19'=>'Jammu & Kashmir',
'194'=>'Ladakh',
// Uttar Pradesh
'20'=>'Uttar Pradesh','21'=>'Uttar Pradesh','22'=>'Uttar Pradesh','23'=>'Uttar Pradesh',
'24'=>'Uttar Pradesh','25'=>'Uttar Pradesh','27'=>'Uttar Pradesh','28'=>'Uttar Pradesh',
// Uttarakhand
'244'=>'Uttarakhand','245'=>'Uttarakhand','246'=>'Uttarakhand','247'=>'Uttarakhand',
'248'=>'Uttarakhand','249'=>'Uttarakhand','26'=>'Uttarakhand',
// Rajasthan
'30'=>'Rajasthan','31'=>'Rajasthan','32'=>'Rajasthan','33'=>'Rajasthan','34'=>'Rajasthan',
// Gujarat
'36'=>'Gujarat','37'=>'Gujarat','38'=>'Gujarat','39'=>'Gujarat',
// Goa
'403'=>'Goa',
// Maharashtra
'40'=>'Maharashtra','41'=>'Maharashtra','42'=>'Maharashtra','43'=>'Maharashtra',
'44'=>'Maharashtra','45'=>'Maharashtra',
// Madhya Pradesh
'46'=>'Madhya Pradesh','47'=>'Madhya Pradesh','48'=>'Madhya Pradesh',
// Chhattisgarh
'49'=>'Chhattisgarh',
// Telangana
'50'=>'Telangana','51'=>'Telangana',
// Andhra Pradesh
'52'=>'Andhra Pradesh','53'=>'Andhra Pradesh',
// Karnataka
'56'=>'Karnataka','57'=>'Karnataka','58'=>'Karnataka','59'=>'Karnataka',
// Tamil Nadu
'60'=>'Tamil Nadu','61'=>'Tamil Nadu','62'=>'Tamil Nadu','63'=>'Tamil Nadu','64'=>'Tamil Nadu',
// Puducherry
'605'=>'Puducherry',
// Kerala
'67'=>'Kerala','68'=>'Kerala','69'=>'Kerala',
// West Bengal
'70'=>'West Bengal','71'=>'West Bengal','72'=>'West Bengal','73'=>'West Bengal','74'=>'West Bengal',
// Odisha
'75'=>'Odisha','76'=>'Odisha',
// Assam
'78'=>'Assam',
// NE States
'790'=>'Manipur','791'=>'Manipur','795'=>'Manipur',
'793'=>'Meghalaya',
'796'=>'Mizoram',
'797'=>'Nagaland',
'799'=>'Tripura',
'791'=>'Arunachal Pradesh','792'=>'Arunachal Pradesh',
'737'=>'Sikkim',
'77'=>'Assam',
// Bihar
'80'=>'Bihar','81'=>'Bihar','84'=>'Bihar','85'=>'Bihar',
// Jharkhand
'82'=>'Jharkhand','83'=>'Jharkhand',
// Andaman & Nicobar
'744'=>'Andaman & Nicobar',
];
function pinToState($postcode, $map) {
$pc = preg_replace('/\s+/', '', $postcode);
if (strlen($pc) < 2) return 'Unknown';
// Try 3-digit match first (more specific), then 2-digit
$p3 = substr($pc, 0, 3);
$p2 = substr($pc, 0, 2);
if (isset($map[$p3])) return $map[$p3];
if (isset($map[$p2])) return $map[$p2];
return 'Other';
}
// Fetch all postcodes and aggregate by state
$geoStmt = $panelPdo->query("
SELECT postcode FROM users
WHERE status='active' AND email_verified=1 AND postcode IS NOT NULL AND postcode!=''
");
$stateCount = [];
while ($row = $geoStmt->fetch()) {
$state = pinToState($row['postcode'], $PIN_TO_STATE);
$stateCount[$state] = ($stateCount[$state] ?? 0) + 1;
}
arsort($stateCount);
$d['geo'] = [];
foreach ($stateCount as $state => $cnt) {
$d['geo'][] = ['r' => $state, 'c' => $cnt];
}
$d['mob'] = 0;
try { $d['mob'] = (int)$panelPdo->query("SELECT COUNT(*) FROM mobile_verifications WHERE is_verified=1")->fetchColumn(); } catch(Exception $e){}
$d['avgp'] = 0;
try { $d['avgp'] = round((float)$panelPdo->query("SELECT AVG(completion_percentage) FROM profiler_completion")->fetchColumn(),1); } catch(Exception $e){}
$d['pan'] = 0;
try { $d['pan'] = (int)$panelPdo->query("SELECT COUNT(DISTINCT user_id) FROM user_profiler WHERE section='profile' AND question_id='pan_status' AND response='\"approved\"'")->fetchColumn(); } catch(Exception $e){}
$d['mwp'] = 0;
try { $d['mwp'] = (int)$panelPdo->query("SELECT COUNT(DISTINCT user_id) FROM profiler_completion WHERE is_completed=1")->fetchColumn(); } catch(Exception $e){}
$d['proj']=0; $d['sent']=0; $d['comp']=0;
try {
$d['proj'] = (int)$shopPdo->query("SELECT COUNT(*) FROM projects")->fetchColumn();
$d['sent'] = (int)$shopPdo->query("SELECT COUNT(*) FROM survey_urls WHERE is_sent=1")->fetchColumn();
$d['comp'] = (int)$shopPdo->query("SELECT COUNT(*) FROM survey_urls WHERE status='complete'")->fetchColumn();
} catch(Exception $e){}
$SECTIONS = [
'personal_background'=>'Personal Background','household_family'=>'Household & Family',
'shopping_lifestyle'=>'Shopping & Lifestyle','technology_digital'=>'Technology & Digital',
'travel_transportation'=>'Travel & Transportation','health_fitness'=>'Health & Fitness',
'entertainment_media'=>'Entertainment & Media','food_dining'=>'Food & Dining',
'financial_services'=>'Financial Services','communication_payments'=>'Communication & Payments',
'household_classification'=>'Household Classification (ISEC)'
];
$pcomp = [];
foreach ($SECTIONS as $k => $l) {
$s1 = $panelPdo->prepare("SELECT COUNT(DISTINCT user_id) FROM profiler_completion WHERE section=? AND is_completed=1"); $s1->execute([$k]);
$s2 = $panelPdo->prepare("SELECT COUNT(DISTINCT user_id) FROM user_profiler WHERE section=?"); $s2->execute([$k]);
$pcomp[$k] = ['done'=>(int)$s1->fetchColumn(), 'start'=>(int)$s2->fetchColumn()];
}
$pdata = [];
$qs = $panelPdo->query("SELECT DISTINCT section, question_id FROM user_profiler ORDER BY section, question_id")->fetchAll();
foreach ($qs as $q) {
$sec = $q['section']; $qid = $q['question_id'];
$rs = $panelPdo->prepare("SELECT response, COUNT(*) as c FROM user_profiler WHERE section=? AND question_id=? GROUP BY response ORDER BY c DESC");
$rs->execute([$sec, $qid]);
$agg = [];
while ($r = $rs->fetch()) {
$dec = json_decode($r['response'], true);
$c = (int)$r['c'];
if (is_array($dec)) { foreach ($dec as $v) { $v=trim($v); if($v!=='') $agg[$v]=($agg[$v]??0)+$c; } }
else { $v=trim($r['response'],'" '); if($v!=='') $agg[$v]=($agg[$v]??0)+$c; }
}
arsort($agg);
$dist=[]; $i=0; $oth=0;
foreach ($agg as $lb=>$ct) { if($i<12){$dist[]=['l'=>$lb,'c'=>$ct];$i++;}else{$oth+=$ct;} }
if ($oth>0) $dist[]=['l'=>'Others','c'=>$oth];
$uc = $panelPdo->prepare("SELECT COUNT(DISTINCT user_id) FROM user_profiler WHERE section=? AND question_id=?");
$uc->execute([$sec,$qid]);
if (!isset($pdata[$sec])) $pdata[$sec]=[];
$pdata[$sec][$qid] = ['dist'=>$dist, 'n'=>(int)$uc->fetchColumn()];
}
// ═══════════════════════════════════════════════
// 2. FIND TTF FONT + GD CHART FUNCTIONS
// ═══════════════════════════════════════════════
// Find a usable TrueType font — auto-download if needed
function findFont($bold = false) {
$tcpdfFonts = __DIR__ . '/tcpdf/fonts';
$cacheDir = __DIR__ . '/tcpdf/fonts';
$targetFile = $bold
? $cacheDir . '/DejaVuSans-Bold.ttf'
: $cacheDir . '/DejaVuSans.ttf';
// 1. Check if we already have the font
if (file_exists($targetFile) && filesize($targetFile) > 10000) {
return $targetFile;
}
// 2. Search common system paths
$systemPaths = $bold ? [
'/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf',
'/usr/share/fonts/dejavu-sans-fonts/DejaVuSans-Bold.ttf',
'/usr/share/fonts/truetype/freefont/FreeSansBold.ttf',
] : [
'/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
'/usr/share/fonts/dejavu-sans-fonts/DejaVuSans.ttf',
'/usr/share/fonts/truetype/freefont/FreeSans.ttf',
];
foreach ($systemPaths as $p) {
if (file_exists($p)) return $p;
}
// 3. Scan TCPDF fonts dir for any .ttf
if (is_dir($tcpdfFonts)) {
$files = glob($tcpdfFonts . '/*.ttf');
if (!empty($files)) return $files[0];
}
// 4. Auto-download DejaVu Sans from GitHub (one-time)
$url = $bold
? 'https://github.com/dejavu-fonts/dejavu-fonts/raw/master/ttf/DejaVuSans-Bold.ttf'
: 'https://github.com/dejavu-fonts/dejavu-fonts/raw/master/ttf/DejaVuSans.ttf';
$ctx = stream_context_create(['http' => [
'timeout' => 15,
'follow_location' => true,
'user_agent' => 'Mozilla/5.0'
]]);
$fontData = @file_get_contents($url, false, $ctx);
if ($fontData && strlen($fontData) > 10000) {
if (is_writable($cacheDir)) {
@file_put_contents($targetFile, $fontData);
if (file_exists($targetFile)) return $targetFile;
}
// If cache dir not writable, write to /tmp
$tmpFont = sys_get_temp_dir() . '/' . ($bold ? 'DejaVuSans-Bold.ttf' : 'DejaVuSans.ttf');
@file_put_contents($tmpFont, $fontData);
if (file_exists($tmpFont)) return $tmpFont;
}
return null; // Will trigger fallback to built-in fonts
}
$FONT = findFont(false);
$FONTB = findFont(true) ?: $FONT;
// Wrapper: draws text using TTF if available, falls back to imagestring
function drawText($img, $size, $x, $y, $text, $color, $font=null) {
if ($font && function_exists('imagettftext')) {
// imagettftext y is baseline, so offset
imagettftext($img, $size, 0, $x, $y + $size + 2, $color, $font, $text);
} else {
// Fallback: use largest built-in font
imagestring($img, 5, $x, $y, $text, $color);
}
}
// Measure text width
function textWidth($size, $text, $font=null) {
if ($font && function_exists('imagettfbbox')) {
$box = imagettfbbox($size, 0, $font, $text);
return abs($box[2] - $box[0]);
}
return strlen($text) * 9; // approx for built-in font 5
}
$CLRS = [
[5,150,105],[13,148,136],[8,145,178],[2,132,199],[79,70,229],
[124,58,237],[192,38,211],[225,29,72],[234,88,12],[217,119,6],
[101,163,13],[22,163,74],[148,163,184],[100,116,139],[71,85,105]
];
function gd_hbar($labels, $values, $clrs, $bcolor=null, $w=2000) {
global $FONT, $FONTB;
$n = count($labels); if(!$n) return null;
$fontSize = 26; // label text size — larger to match pie legend
$valSize = 24; // value text size
$bH = 64; // bar height
$gap = 16; // gap between bars
$lPad = 520; // left padding for labels
$rPad = 320; // right padding for values
$tPad = 20;
$botP = 14;
$h = $tPad + $n*($bH+$gap) + $botP;
$cW = $w-$lPad-$rPad;
$mx = max($values)?:1;
$tot = array_sum($values)?:1;
$img = imagecreatetruecolor($w, $h);
$wh = imagecolorallocate($img, 255, 255, 255);
$tc = imagecolorallocate($img, 35, 45, 60);
$mc = imagecolorallocate($img, 75, 90, 110);
imagefill($img, 0, 0, $wh);
$bc = $bcolor ?: $clrs[0];
$bar = imagecolorallocate($img, $bc[0], $bc[1], $bc[2]);
for($i=0; $i<$n; $i++){
$y = $tPad + $i*($bH+$gap);
$lb = mb_strlen($labels[$i])>32 ? mb_substr($labels[$i],0,30).'..' : $labels[$i];
// Label (right-aligned to left padding)
$tw = textWidth($fontSize, $lb, $FONTB);
drawText($img, $fontSize, max(10, $lPad - $tw - 20), $y + (int)(($bH-$fontSize)/2) - 4, $lb, $tc, $FONTB);
// Bar
$bW = max(5, (int)($values[$i]/$mx*$cW));
imagefilledrectangle($img, $lPad, $y+8, $lPad+$bW, $y+$bH-8, $bar);
// Value
$pct = round($values[$i]/$tot*100,1);
$valText = number_format($values[$i])." ({$pct}%)";
drawText($img, $valSize, $lPad+$bW+14, $y + (int)(($bH-$valSize)/2) - 2, $valText, $mc, $FONT);
}
ob_start(); imagepng($img); $data=ob_get_clean(); imagedestroy($img);
return $data;
}
function gd_vbar($labels, $values, $clrs, $w=1800, $h=600) {
global $FONT, $FONTB;
$n = count($labels); if(!$n) return null;
$fontSize = 26;
$valSize = 24;
$lP=110; $rP=50; $tP=45; $bP=80;
$cW=$w-$lP-$rP; $cH=$h-$tP-$bP; $mx=max($values)?:1;
$bW=max(40, (int)($cW/$n*0.55));
$gap=(int)(($cW-$bW*$n)/($n+1));
$img = imagecreatetruecolor($w, $h);
$wh = imagecolorallocate($img, 255, 255, 255); imagefill($img, 0, 0, $wh);
$tc = imagecolorallocate($img, 35, 45, 60);
$mc = imagecolorallocate($img, 130, 140, 160);
$lc = imagecolorallocate($img, 226, 232, 240);
$c = $clrs[0]; $bar = imagecolorallocate($img, $c[0], $c[1], $c[2]);
// Grid lines
for($g=0; $g<=4; $g++){
$gy = $tP + (int)($cH*(1-$g/4));
imageline($img, $lP, $gy, $w-$rP, $gy, $lc);
$gLabel = number_format((int)($mx*$g/4));
drawText($img, 20, 8, $gy-14, $gLabel, $mc, $FONT);
}
for($i=0; $i<$n; $i++){
$x = $lP + $gap + $i*($bW+$gap);
$bH2 = max(3, (int)($values[$i]/$mx*$cH));
$y = $tP+$cH-$bH2;
imagefilledrectangle($img, $x, $y, $x+$bW, $tP+$cH, $bar);
// Value on top
$vS = number_format($values[$i]);
$tw = textWidth($valSize, $vS, $FONTB);
drawText($img, $valSize, $x+(int)($bW/2)-(int)($tw/2), $y-28, $vS, $tc, $FONTB);
// Label below
$lw = textWidth($fontSize, $labels[$i], $FONT);
drawText($img, $fontSize, $x+(int)($bW/2)-(int)($lw/2), $tP+$cH+12, $labels[$i], $tc, $FONT);
}
ob_start(); imagepng($img); $data=ob_get_clean(); imagedestroy($img);
return $data;
}
function gd_pie($labels, $values, $clrs, $sz=600) {
global $FONT, $FONTB;
$n = count($labels); if(!$n) return null;
$tot = array_sum($values)?:1;
$fontSize = 24;
$legItemH = 58; // legend row height
$legW = 780;
$w = $sz + $legW + 70;
$h = max($sz, $n*$legItemH + 60);
$cx = (int)($sz/2)+30;
$cy = (int)($h/2);
$r = (int)($sz*0.42);
$ir = (int)($r*0.52);
$img = imagecreatetruecolor($w, $h);
$wh = imagecolorallocate($img, 255, 255, 255); imagefill($img, 0, 0, $wh);
$tc = imagecolorallocate($img, 35, 45, 60);
imageantialias($img, true);
// Draw pie slices
$sa = -90; $sc = [];
for($i=0; $i<$n; $i++){
$c = $clrs[$i % count($clrs)];
$sc[$i] = imagecolorallocate($img, $c[0], $c[1], $c[2]);
$ang = ($values[$i]/$tot)*360;
if($ang > 0.5) imagefilledarc($img, $cx, $cy, $r*2, $r*2, (int)round($sa), (int)round($sa+$ang), $sc[$i], IMG_ARC_PIE);
$sa += $ang;
}
// Donut hole
imagefilledellipse($img, $cx, $cy, $ir*2, $ir*2, $wh);
// Legend
$lx = $sz + 60;
for($i=0; $i<$n; $i++){
$ly = 40 + $i*$legItemH;
imagefilledrectangle($img, $lx, $ly, $lx+28, $ly+28, $sc[$i]);
$pct = round($values[$i]/$tot*100, 1);
$legText = $labels[$i]." (".number_format($values[$i])." - {$pct}%)";
drawText($img, $fontSize, $lx+40, $ly-2, $legText, $tc, $FONT);
}
ob_start(); imagepng($img); $data=ob_get_clean(); imagedestroy($img);
return $data;
}
function fmtQ($q) { return ucwords(str_replace('_',' ',$q)); }
// ═══════════════════════════════════════════════
// 3. BUILD PDF — 16:9 WIDESCREEN SLIDE
// ═══════════════════════════════════════════════
define('SLIDE_W', 338.67);
define('SLIDE_H', 190.5);
class RRPDF extends TCPDF {
public $isCover = true;
public function Header() {
if ($this->isCover) return;
$pw = SLIDE_W;
$this->SetY(6);
$this->SetFont('helvetica','B',11);
$this->SetTextColor(6,78,59);
$this->SetXY(18, 6);
$this->Cell(160, 8, 'RELEVANT REFLEX CONSULTING', 0, 0, 'L');
$this->SetFont('helvetica','',10);
$this->SetTextColor(100,116,139);
$this->SetXY($pw-18-120, 6);
$this->Cell(120, 8, 'India Panel Book', 0, 0, 'R');
$this->SetDrawColor(5,150,105);
$this->SetLineWidth(0.7);
$this->Line(18, 16, $pw-18, 16);
}
public function Footer() {
if ($this->isCover) return;
$pw = SLIDE_W;
$this->SetY(-14);
$this->SetDrawColor(180,190,200);
$this->SetLineWidth(0.25);
$this->Line(18, $this->GetY(), $pw-18, $this->GetY());
$this->SetFont('helvetica','',8);
$this->SetTextColor(100,116,139);
$this->SetY(-12);
$hw = ($pw-36)/2;
$this->Cell($hw, 5, 'Confidential — Relevant Reflex Consulting', 0, 0, 'L');
$this->Cell($hw, 5, 'Page '.$this->getAliasNumPage(), 0, 0, 'R');
}
}
$pdf = new RRPDF('L', 'mm', array(SLIDE_W, SLIDE_H), true, 'UTF-8', false);
$pdf->SetCreator('Relevant Reflex Consulting');
$pdf->SetAuthor('Relevant Reflex Consulting');
$pdf->SetTitle('Relevant Reflex Consulting — India Panel Book');
$pdf->SetMargins(22, 22, 22);
$pdf->SetAutoPageBreak(true, 18);
$PW = SLIDE_W - 44;
$PH = SLIDE_H - 40;
$LM = 22;
$CX = SLIDE_W / 2;
function insertChart($pdf, $chartData, $x, $y, $w, $h=0) {
if (!$chartData) return;
$tmp = tempnam(sys_get_temp_dir(),'rrchart_').'.png';
file_put_contents($tmp, $chartData);
$pdf->Image($tmp, $x, $y, $w, $h, 'PNG');
@unlink($tmp);
}
// ══════════════════════════════════════════
// COVER PAGE
// ══════════════════════════════════════════
$pdf->isCover = true;
$pdf->AddPage();
$pdf->Ln(18);
$pdf->SetFillColor(5,150,105);
$pdf->RoundedRect($CX-24, $pdf->GetY(), 48, 30, 6, '1111', 'F');
$pdf->SetFont('helvetica','B',26);
$pdf->SetTextColor(255,255,255);
$pdf->SetXY($CX-24, $pdf->GetY()+3);
$pdf->Cell(48, 24, 'RR', 0, 0, 'C');
$pdf->Ln(38);
$pdf->SetFont('helvetica','B',38);
$pdf->SetTextColor(6,78,59);
$pdf->Cell(0, 15, 'Relevant Reflex Consulting', 0, 1, 'C');
$pdf->Ln(1);
$pdf->SetFont('helvetica','',26);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 11, 'India Panel Book', 0, 1, 'C');
$pdf->Ln(3);
$pdf->SetFillColor(5,150,105);
$pdf->RoundedRect($CX-22, $pdf->GetY(), 44, 1.5, 0.75, '1111', 'F');
$pdf->Ln(8);
$pdf->SetFont('helvetica','',13);
$pdf->SetTextColor(51,65,85);
$pdf->Cell(0, 7, 'Generated on '.$d['ts'], 0, 1, 'C');
$pdf->Ln(10);
$pdf->SetFillColor(248,250,252);
$pdf->SetFont('helvetica','',11);
$pdf->SetTextColor(51,65,85);
$disc = "All the data in this panel book are 100% based on the actual counts of the panel and not added/edited by human. This is a real-time snapshot generated at the date and time mentioned above.";
$pdf->SetX(55);
$pdf->MultiCell(SLIDE_W - 110, 6.5, $disc, 0, 'C', true);
$pdf->Ln(10);
$pdf->SetFont('helvetica','',11);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 5, 'www.relevantreflex.com', 0, 1, 'C');
// ══════════════════════════════════════════
// PANEL OVERVIEW
// ══════════════════════════════════════════
$pdf->isCover = false;
$pdf->AddPage();
$pdf->SetFont('helvetica','B',24);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 11, 'Panel Overview', 0, 1, 'L');
$pdf->SetFont('helvetica','',12);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 6, 'Key quality metrics and panel health indicators — India panel.', 0, 1, 'L');
$pdf->Ln(6);
$cw = $PW/3;
$panPct = $d['verified'] > 0 ? round($d['pan'] / $d['verified'] * 100, 1) : 0;
$mets = [
[[number_format($d['total']),'Total Registered'],[number_format($d['active']),'Active Members'],[number_format($d['verified']),'Email Verified']],
[[number_format($d['mwp']),'Profiler Completed'],[number_format($d['mob']),'Mobile Verified'],[$panPct.'%','PAN Verified ('.number_format($d['pan']).')']],
];
foreach ($mets as $row) {
$y0=$pdf->GetY();
foreach ($row as $ci=>$m) {
if ($m[0] === '' && $m[1] === '') continue;
$x=$LM+$ci*$cw;
$pdf->SetFillColor(248,250,252);
$pdf->RoundedRect($x, $y0, $cw-4, 28, 3, '1111', 'DF');
$pdf->SetFont('helvetica','B',28);
$pdf->SetTextColor(5,150,105);
$pdf->SetXY($x, $y0+1);
$pdf->Cell($cw-4, 15, $m[0], 0, 0, 'C');
$pdf->SetFont('helvetica','',11);
$pdf->SetTextColor(100,116,139);
$pdf->SetXY($x, $y0+16);
$pdf->Cell($cw-4, 9, $m[1], 0, 0, 'C');
}
$pdf->SetY($y0+32);
}
$pdf->Ln(3);
if ($d['proj']>0) {
$pdf->SetFont('helvetica','B',15);
$pdf->SetTextColor(6,78,59);
$pdf->Cell(0, 8, 'Research Activity', 0, 1, 'L');
$pdf->Ln(2);
$pdf->SetFillColor(6,78,59);
$pdf->SetTextColor(255,255,255);
$pdf->SetFont('helvetica','B',11);
$pdf->Cell($cw, 9, 'Total Projects', 1, 0, 'C', true);
$pdf->Cell($cw, 9, 'Invitations Sent', 1, 0, 'C', true);
$pdf->Cell($cw, 9, 'Completed Surveys', 1, 1, 'C', true);
$pdf->SetTextColor(51,65,85);
$pdf->SetFont('helvetica','B',14);
$pdf->Cell($cw, 11, number_format($d['proj']), 1, 0, 'C');
$pdf->Cell($cw, 11, number_format($d['sent']), 1, 0, 'C');
$pdf->Cell($cw, 11, number_format($d['comp']), 1, 1, 'C');
}
// ══════════════════════════════════════════
// PROFILER COMPLETION RATES
// ══════════════════════════════════════════
$pdf->AddPage();
$pdf->SetFont('helvetica','B',24);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 11, 'Profiler Completion Rates', 0, 1, 'L');
$pdf->SetFont('helvetica','',12);
$pdf->SetTextColor(100,116,139);
$popBase = $d['verified'] > 0 ? $d['verified'] : $d['active'];
$pdf->Cell(0, 6, 'Percentage based on '.number_format($popBase).' verified active panel members.', 0, 1, 'L');
$pdf->Ln(5);
$pdf->SetFillColor(6,78,59);
$pdf->SetTextColor(255,255,255);
$pdf->SetFont('helvetica','B',12);
$pdf->Cell($PW*0.46, 10, ' Section', 1, 0, 'L', true);
$pdf->Cell($PW*0.18, 10, 'Completed', 1, 0, 'C', true);
$pdf->Cell($PW*0.18, 10, '% of Panel', 1, 0, 'C', true);
$pdf->Cell($PW*0.18, 10, 'Status', 1, 1, 'C', true);
$pdf->SetFont('helvetica','',11);
$pdf->SetTextColor(51,65,85);
$ev=false;
foreach ($SECTIONS as $k=>$lb) {
$pc=$pcomp[$k];
$pct = $popBase > 0 ? round($pc['done'] / $popBase * 100, 1) : 0;
$status = $pct >= 50 ? 'Strong' : ($pct >= 20 ? 'Growing' : ($pct > 0 ? 'Emerging' : '—'));
if($ev) $pdf->SetFillColor(248,250,252); else $pdf->SetFillColor(255,255,255);
$pdf->Cell($PW*0.46, 8, ' '.$lb, 'LR', 0, 'L', true);
$pdf->Cell($PW*0.18, 8, number_format($pc['done']), 'LR', 0, 'C', true);
$pdf->SetFont('helvetica','B',11);
if ($pct >= 50) $pdf->SetTextColor(5,150,105);
elseif ($pct >= 20) $pdf->SetTextColor(217,119,6);
else $pdf->SetTextColor(100,116,139);
$pdf->Cell($PW*0.18, 8, $pct.'%', 'LR', 0, 'C', true);
$pdf->SetFont('helvetica','',11);
$pdf->SetTextColor(51,65,85);
$pdf->Cell($PW*0.18, 8, $status, 'LR', 1, 'C', true);
$ev=!$ev;
}
$pdf->SetDrawColor(203,213,225);
$pdf->Line($LM, $pdf->GetY(), $LM+$PW, $pdf->GetY());
// ══════════════════════════════════════════
// DEMOGRAPHICS
// ══════════════════════════════════════════
$pdf->AddPage();
$pdf->SetFont('helvetica','B',24);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 11, 'Demographics', 0, 1, 'L');
$pdf->SetFont('helvetica','',12);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 6, 'Distribution of '.number_format($d['verified']).' active, verified panel members across India.', 0, 1, 'L');
$pdf->Ln(4);
if (!empty($d['gender'])) {
$pdf->SetFont('helvetica','B',15);
$pdf->SetTextColor(6,78,59);
$pdf->Cell(0, 8, 'Gender Distribution', 0, 1, 'L');
$pdf->Ln(2);
$lb=array_column($d['gender'],'gender'); $vl=array_map('intval',array_column($d['gender'],'c'));
insertChart($pdf, gd_pie($lb,$vl,$CLRS,560), 30, $pdf->GetY(), 220);
$pdf->Ln(72);
}
if (!empty($d['age'])) {
$pdf->AddPage();
$pdf->SetFont('helvetica','B',24);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 11, 'Age Distribution', 0, 1, 'L');
$pdf->SetFont('helvetica','',12);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 6, 'Age brackets of active verified panel members.', 0, 1, 'L');
$pdf->Ln(3);
$lb=array_column($d['age'],'ag'); $vl=array_map('intval',array_column($d['age'],'c'));
insertChart($pdf, gd_vbar($lb,$vl,$CLRS,1800,560), $LM, $pdf->GetY(), $PW);
$pdf->Ln(70);
}
// ══════════════════════════════════════════
// GEOGRAPHY
// ══════════════════════════════════════════
if (!empty($d['geo'])) {
$pdf->AddPage();
$pdf->SetFont('helvetica','B',24);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 11, 'Geographic Distribution', 0, 1, 'L');
$pdf->SetFont('helvetica','',12);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 6, 'Panel member distribution across '.count($d['geo']).' Indian states & territories.', 0, 1, 'L');
$pdf->Ln(3);
$allLabels = array_column($d['geo'],'r');
$allValues = array_map('intval', array_column($d['geo'],'c'));
$total = count($allLabels);
if ($total <= 15) {
// Fits on one page
$ch = gd_hbar($allLabels, $allValues, $CLRS, [13,148,136], 2000);
$imgH = min($total*9.5+8, $PH-22);
insertChart($pdf, $ch, $LM, $pdf->GetY(), $PW, $imgH);
} else {
// Split across two pages
$half = (int)ceil($total/2);
$lb1 = array_slice($allLabels, 0, $half);
$vl1 = array_slice($allValues, 0, $half);
$ch1 = gd_hbar($lb1, $vl1, $CLRS, [13,148,136], 2000);
$imgH1 = min(count($lb1)*9.5+8, $PH-22);
insertChart($pdf, $ch1, $LM, $pdf->GetY(), $PW, $imgH1);
$lb2 = array_slice($allLabels, $half);
$vl2 = array_slice($allValues, $half);
if (!empty($lb2)) {
$pdf->AddPage();
$pdf->SetFont('helvetica','B',18);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 9, 'Geographic Distribution (continued)', 0, 1, 'L');
$pdf->Ln(3);
$ch2 = gd_hbar($lb2, $vl2, $CLRS, [13,148,136], 2000);
$imgH2 = min(count($lb2)*9.5+8, $PH-22);
insertChart($pdf, $ch2, $LM, $pdf->GetY(), $PW, $imgH2);
}
}
}
// SEC Class (ISEC) Distribution
if (!empty($d['sec'])) {
$pdf->AddPage();
$pdf->SetFont('helvetica','B',24);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 11, 'SEC Classification (ISEC)', 0, 1, 'L');
$pdf->SetFont('helvetica','',12);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 6, 'Socio-Economic Classification based on MRSI ISEC standard (2024).', 0, 1, 'L');
$pdf->Ln(5);
$secLabels = ['A'=>'SEC A — High','B'=>'SEC B — Upper Middle','C'=>'SEC C — Middle','D'=>'SEC D — Lower Middle','E'=>'SEC E — Low','Unknown'=>'Not Classified'];
$secColorsMap = ['A'=>[5,150,105],'B'=>[13,148,136],'C'=>[37,99,235],'D'=>[217,119,6],'E'=>[220,38,38],'Unknown'=>[148,163,184]];
// Table
$pdf->SetFillColor(6,78,59);
$pdf->SetTextColor(255,255,255);
$pdf->SetFont('helvetica','B',12);
$pdf->Cell($PW*0.40, 10, ' SEC Class', 1, 0, 'L', true);
$pdf->Cell($PW*0.20, 10, 'Members', 1, 0, 'C', true);
$pdf->Cell($PW*0.20, 10, '% of Panel', 1, 0, 'C', true);
$pdf->Cell($PW*0.20, 10, 'ISEC Tiers', 1, 1, 'C', true);
$secTierMap = ['A'=>'G1–G6','B'=>'G7–G8','C'=>'G9–G10','D'=>'G11','E'=>'G12','Unknown'=>'—'];
$secTotal = array_sum(array_column($d['sec'], 'c'));
$ev = false;
$pdf->SetFont('helvetica','',11);
foreach ($d['sec'] as $row) {
$cls = $row['sec_class'];
$cnt = (int)$row['c'];
$pct = $secTotal > 0 ? round($cnt / $secTotal * 100, 1) : 0;
$label = $secLabels[$cls] ?? 'SEC '.$cls;
$tiers = $secTierMap[$cls] ?? '—';
if($ev) $pdf->SetFillColor(248,250,252); else $pdf->SetFillColor(255,255,255);
$pdf->SetTextColor(51,65,85);
$pdf->Cell($PW*0.40, 8, ' '.$label, 'LR', 0, 'L', true);
$pdf->Cell($PW*0.20, 8, number_format($cnt), 'LR', 0, 'C', true);
$pdf->SetFont('helvetica','B',11);
$c = $secColorsMap[$cls] ?? [100,116,139];
$pdf->SetTextColor($c[0],$c[1],$c[2]);
$pdf->Cell($PW*0.20, 8, $pct.'%', 'LR', 0, 'C', true);
$pdf->SetFont('helvetica','',11);
$pdf->SetTextColor(100,116,139);
$pdf->Cell($PW*0.20, 8, $tiers, 'LR', 1, 'C', true);
$ev=!$ev;
}
$pdf->SetDrawColor(203,213,225);
$pdf->Line($LM, $pdf->GetY(), $LM+$PW, $pdf->GetY());
// Bar chart
$pdf->Ln(10);
$secLb = []; $secVl = [];
foreach ($d['sec'] as $row) {
$secLb[] = ($secLabels[$row['sec_class']] ?? $row['sec_class']);
$secVl[] = (int)$row['c'];
}
if (function_exists('gd_hbar')) {
$ch = gd_hbar($secLb, $secVl, $CLRS, [5,150,105], 2000);
$imgH = min(count($secLb)*9.5+8, $PH-22);
insertChart($pdf, $ch, $LM, $pdf->GetY(), $PW, $imgH);
}
}
// ══════════════════════════════════════════
// PROFILER SECTIONS
// ══════════════════════════════════════════
$sci=0;
foreach ($SECTIONS as $sk=>$sl) {
if (!isset($pdata[$sk]) || empty($pdata[$sk])) continue;
$qd=$pdata[$sk]; $scl=$CLRS[$sci%count($CLRS)]; $sci++;
$pc=$pcomp[$sk]??['done'=>0,'start'=>0];
$pdf->AddPage();
$pdf->SetFont('helvetica','B',24);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 11, $sl, 0, 1, 'L');
$pdf->SetFont('helvetica','',12);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 6, count($qd).' question'.(count($qd)!=1?'s':'').' — '.number_format($pc['done']).' members completed this section.', 0, 1, 'L');
$pdf->Ln(3);
foreach ($qd as $qid=>$qi) {
$dist=$qi['dist']; $rn=$qi['n'];
if(empty($dist)) continue;
$lb=array_column($dist,'l'); $vl=array_map('intval',array_column($dist,'c'));
$nc=count($dist);
$need = ($nc<=5) ? 78 : ($nc*8.5+24);
if ($pdf->GetY()+$need > (SLIDE_H - 22)) $pdf->AddPage();
$pdf->SetFont('helvetica','B',15);
$pdf->SetTextColor(15,23,42);
$pdf->Cell(0, 8, fmtQ($qid), 0, 1, 'L');
$pdf->SetFont('helvetica','',11);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 5, number_format($rn).' respondents', 0, 1, 'L');
$pdf->Ln(1);
if ($nc<=5 && $nc>=2) {
insertChart($pdf, gd_pie($lb,$vl,$CLRS,520), 35, $pdf->GetY(), 210);
$pdf->Ln(68);
} else {
$ch=gd_hbar($lb,$vl,$CLRS,$scl,2000);
$imgH=min($nc*8.5+5, 120);
insertChart($pdf, $ch, $LM, $pdf->GetY(), $PW, $imgH);
$pdf->Ln($imgH+3);
}
$pdf->Ln(3);
}
}
// ══════════════════════════════════════════
// CONTACT PAGE
// ══════════════════════════════════════════
$pdf->AddPage();
$pdf->Ln(14);
$pdf->SetFillColor(5,150,105);
$pdf->RoundedRect($CX-20, $pdf->GetY(), 40, 26, 5, '1111', 'F');
$pdf->SetFont('helvetica','B',22);
$pdf->SetTextColor(255,255,255);
$pdf->SetXY($CX-20, $pdf->GetY()+3);
$pdf->Cell(40, 20, 'RR', 0, 0, 'C');
$pdf->Ln(34);
$pdf->SetFont('helvetica','B',28);
$pdf->SetTextColor(6,78,59);
$pdf->Cell(0, 11, 'Relevant Reflex Consulting', 0, 1, 'C');
$pdf->Ln(1);
$pdf->SetFont('helvetica','I',14);
$pdf->SetTextColor(100,116,139);
$pdf->Cell(0, 7, "India's Transparent Consumer Access Panel for Market Research", 0, 1, 'C');
$pdf->Ln(14);
$ctc = [
['Email','sridhar.mani@relevantreflex.com'],
['Phone','+91 80565 26579'],
['Web','www.relevantreflex.com'],
];
foreach ($ctc as $c) {
$half = (SLIDE_W)/2;
$pdf->SetFont('helvetica','',12);
$pdf->SetTextColor(100,116,139);
$pdf->Cell($half, 9, $c[0], 0, 0, 'R');
$pdf->SetFont('helvetica','B',13);
$pdf->SetTextColor(15,23,42);
$pdf->Cell($half, 9, ' '.$c[1], 0, 1, 'L');
}
$pdf->Ln(14);
$pdf->SetDrawColor(203,213,225);
$pdf->Line($CX-40, $pdf->GetY(), $CX+40, $pdf->GetY());
$pdf->Ln(6);
$pdf->SetFont('helvetica','',10);
$pdf->SetTextColor(100,116,139);
$pdf->MultiCell(0, 5, 'For panel inquiries, project feasibility, or partnership opportunities, please reach out to our team.', 0, 'C');
$pdf->Ln(4);
$pdf->SetFont('helvetica','',9);
$pdf->Cell(0, 4, 'This document was generated on '.$d['ts'].'.', 0, 1, 'C');
// ═══════════════════════════════════════════════
// 4. SERVE PDF
// ═══════════════════════════════════════════════
logActivity($_SESSION['admin_id'], 'panelbook_download', 'Panel Book PDF generated');
$pdf->Output('RRC_India_Panel_Book_'.date('Y-m-d_His').'.pdf', 'D');
exit;
-------------------- END OF FILE --------------------
FILE: RR SHOP/projects.php
TYPE: PHP
SIZE: 29.16 KB
------------------------------------------------------------
exec("SET time_zone = '+05:30'");
} catch (Exception $e) { error_log("Projects page DB error: ".$e->getMessage()); }
// ===== Snapshot KPIs =====
$kpi = [
'total' => 0, 'live' => 0, 'closed' => 0, 'on_hold' => 0,
'created' => 0, 'targeted' => 0,
'sample_target' => 0, 'completes_total' => 0, 'completes_valid' => 0,
'avg_eloi' => 0, 'avg_actual_loi_min' => 0,
'revenue_inr' => 0, 'active_clients' => 0,
'urls_sent_total' => 0, 'urls_screenout_total' => 0,
];
$forexRates = ['INR' => 1.0, 'USD' => 86.50, 'EUR' => 93.00];
if ($pdo) {
// Live forex (best-effort)
try {
$ctx = stream_context_create(['http' => ['timeout' => 3]]);
$rateJson = @file_get_contents('https://api.exchangerate-api.com/v4/latest/USD', false, $ctx);
if ($rateJson) {
$rateData = json_decode($rateJson, true);
if (!empty($rateData['rates']['INR'])) {
$usdToInr = (float)$rateData['rates']['INR'];
$forexRates['USD'] = $usdToInr;
if (!empty($rateData['rates']['EUR'])) {
$forexRates['EUR'] = $usdToInr / (float)$rateData['rates']['EUR'];
}
}
}
} catch (Exception $e) { /* fallback rates */ }
try {
$stmt = $pdo->query("SELECT
COUNT(*) as total,
SUM(CASE WHEN status='Live' THEN 1 ELSE 0 END) as live_cnt,
SUM(CASE WHEN status='Closed' THEN 1 ELSE 0 END) as closed_cnt,
SUM(CASE WHEN status='On hold' THEN 1 ELSE 0 END) as hold_cnt,
SUM(CASE WHEN status='Created' THEN 1 ELSE 0 END) as created_cnt,
SUM(CASE WHEN status='Targeted' THEN 1 ELSE 0 END) as targeted_cnt,
COALESCE(SUM(sample_size),0) as sample_target,
AVG(eloi) as avg_eloi,
COUNT(DISTINCT client_id) as active_clients
FROM projects");
$row = $stmt->fetch();
if ($row) {
$kpi['total'] = (int)$row['total'];
$kpi['live'] = (int)$row['live_cnt'];
$kpi['closed'] = (int)$row['closed_cnt'];
$kpi['on_hold'] = (int)$row['hold_cnt'];
$kpi['created'] = (int)$row['created_cnt'];
$kpi['targeted'] = (int)$row['targeted_cnt'];
$kpi['sample_target'] = (int)$row['sample_target'];
$kpi['avg_eloi'] = round((float)($row['avg_eloi'] ?? 0), 1);
$kpi['active_clients'] = (int)$row['active_clients'];
}
} catch (Exception $e) { error_log("Projects KPI agg error: ".$e->getMessage()); }
try {
$stmt = $pdo->query("SELECT
SUM(CASE WHEN status='complete' THEN 1 ELSE 0 END) as completes_total,
SUM(CASE WHEN status='complete' AND quality_flag='valid' THEN 1 ELSE 0 END) as completes_valid,
SUM(CASE WHEN status='sent' THEN 1 ELSE 0 END) as sent_cnt,
SUM(CASE WHEN status IN ('earlyscreenout','latescreenout') THEN 1 ELSE 0 END) as so_cnt,
AVG(CASE WHEN status='complete' AND quality_flag='valid' AND actual_loi_seconds IS NOT NULL THEN actual_loi_seconds END) as avg_loi_sec
FROM survey_urls");
$row = $stmt->fetch();
if ($row) {
$kpi['completes_total'] = (int)$row['completes_total'];
$kpi['completes_valid'] = (int)$row['completes_valid'];
$kpi['urls_sent_total'] = (int)$row['sent_cnt'];
$kpi['urls_screenout_total'] = (int)$row['so_cnt'];
$kpi['avg_actual_loi_min'] = round((float)($row['avg_loi_sec'] ?? 0) / 60, 1);
}
} catch (Exception $e) { /* */ }
try {
$stmt = $pdo->query("SELECT currency, SUM(total_amount) as amt FROM invoices GROUP BY currency");
foreach ($stmt->fetchAll() as $r) {
$rate = $forexRates[$r['currency'] ?? 'INR'] ?? 1.0;
$kpi['revenue_inr'] += (float)$r['amt'] * $rate;
}
} catch (Exception $e) { /* */ }
}
// ===== Build WHERE for filtered list =====
$where = ['1=1'];
$params = [];
if ($f_status !== '') {
$where[] = "p.status = ?";
$params[] = $f_status;
}
if ($f_client > 0) {
$where[] = "p.client_id = ?";
$params[] = $f_client;
}
if ($f_search !== '') {
$where[] = "(p.project_id LIKE ? OR p.project_name LIKE ? OR p.client_reference LIKE ?)";
$params[] = '%'.$f_search.'%';
$params[] = '%'.$f_search.'%';
$params[] = '%'.$f_search.'%';
}
switch ($f_sort) {
case 'newest': $orderBy = 'p.created_at DESC'; break;
case 'oldest': $orderBy = 'p.created_at ASC'; break;
case 'sample': $orderBy = 'p.sample_size DESC'; break;
case 'completes': $orderBy = 'urls_valid DESC'; break;
case 'name': $orderBy = 'p.project_name ASC'; break;
case 'client': $orderBy = 'c.company_name ASC, p.created_at DESC'; break;
default: $orderBy = 'p.updated_at DESC'; // 'recent'
}
// ===== Fetch project list =====
$projects = [];
if ($pdo) {
try {
$sql = "
SELECT
p.*,
c.company_name as client_name,
c.country as client_country,
c.currency as client_currency,
(SELECT COUNT(*) FROM project_selections WHERE project_id = p.id) as selections_count,
(SELECT COUNT(*) FROM survey_urls WHERE project_id = p.project_id) as urls_total,
(SELECT COUNT(*) FROM survey_urls WHERE project_id = p.project_id AND status='available') as urls_available,
(SELECT COUNT(*) FROM survey_urls WHERE project_id = p.project_id AND status='sent') as urls_sent,
(SELECT COUNT(*) FROM survey_urls WHERE project_id = p.project_id AND status='complete') as urls_complete,
(SELECT COUNT(*) FROM survey_urls WHERE project_id = p.project_id AND status='complete' AND quality_flag='valid') as urls_valid,
(SELECT COUNT(*) FROM survey_urls WHERE project_id = p.project_id AND status IN ('earlyscreenout','latescreenout')) as urls_screenout,
(SELECT COUNT(*) FROM survey_urls WHERE project_id = p.project_id AND status='quotafull') as urls_quotafull,
(SELECT AVG(actual_loi_seconds) FROM survey_urls WHERE project_id = p.project_id AND status='complete' AND quality_flag='valid' AND actual_loi_seconds IS NOT NULL) as avg_loi_seconds,
(SELECT SUM(total_amount) FROM invoices WHERE project_id = p.id) as invoice_total,
(SELECT currency FROM invoices WHERE project_id = p.id LIMIT 1) as invoice_currency,
(SELECT status FROM invoices WHERE project_id = p.id LIMIT 1) as invoice_status
FROM projects p
LEFT JOIN clients c ON p.client_id = c.id
WHERE ".implode(' AND ', $where)."
ORDER BY $orderBy
";
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
$projects = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) { error_log("Projects fetch error: ".$e->getMessage()); }
}
// Client dropdown
$clients_dd = [];
if ($pdo) {
try {
$stmt = $pdo->query("SELECT id, company_name FROM clients ORDER BY company_name ASC");
$clients_dd = $stmt->fetchAll(PDO::FETCH_ASSOC);
} catch (Exception $e) { /* */ }
}
// Helpers
if (!function_exists('fmtIST')) {
function fmtIST($dt, $fmt = 'd M Y, h:i A') {
if (!$dt || $dt === '0000-00-00 00:00:00') return '—';
try {
$date = new DateTime($dt, new DateTimeZone('Asia/Kolkata'));
return $date->format($fmt);
} catch (Exception $e) { return date($fmt, strtotime($dt)); }
}
}
if (!function_exists('fmtINR')) { function fmtINR($v) { return '₹' . number_format((float)$v, 2); } }
if (!function_exists('fmtNum')) { function fmtNum($v) { return number_format((int)$v); } }
include 'includes/header.php';
?>
Projects
All projects across all clients · live + closed + draft
-------------------- END OF FILE --------------------
FILE: RR SHOP/README.md
TYPE: MD
SIZE: 8.86 KB
------------------------------------------------------------
# Relevant Reflex Panel Management System
A comprehensive, mobile-responsive panel management system built with PHP, MySQL, and modern web technologies. Designed for fast loading on shared hosting environments like Hostinger.
## 🚀 Features
- **Complete Panel Management**: Create, manage, and analyze survey panels
- **User Management**: Role-based access control with admin, manager, and user roles
- **Supply Chain Management**: Track inventory, suppliers, and procurement
- **Demand Analytics**: Market analysis and forecasting tools
- **Financial Management**: Revenue tracking and expense management
- **Support System**: Built-in ticketing and help center
- **Real-time Dashboard**: Live statistics and performance metrics
- **Mobile Responsive**: 100% mobile-friendly design
- **SEO Optimized**: Search engine friendly structure
- **Fast Loading**: Optimized for shared hosting environments
## 📁 File Structure
```
relevant-reflex/
├── index.php # Dashboard home page
├── users.php # User management
├── panel.php # Panel management
├── supply.php # Supply management
├── demand.php # Demand analysis
├── finance.php # Financial management
├── support.php # Support center
├── settings.php # System settings
├── config.php # Database configuration
├── .htaccess # Apache configuration
├── database_schema.sql # Database structure
├── robots.txt # Search engine directives
├── sitemap.xml # Site structure for SEO
├── includes/
│ ├── header.php # Site header
│ ├── footer.php # SEO footer
│ └── navigation.php # Navigation menu
├── assets/
│ ├── css/
│ │ ├── main.css # Main stylesheet
│ │ ├── responsive.css # Mobile responsiveness
│ │ └── dashboard.css # Dashboard styles
│ ├── js/
│ │ ├── main.js # Core JavaScript
│ │ └── dashboard.js # Dashboard functionality
│ └── images/
│ └── (your images here)
└── error-pages/
├── 404.html # Page not found
├── 500.html # Server error
└── maintenance.html # Maintenance mode
```
## 🛠️ Installation Instructions
### Prerequisites
- **Web Hosting**: Shared hosting account (Hostinger, cPanel, etc.)
- **PHP**: Version 7.4 or higher
- **MySQL**: Version 5.7 or higher
- **Apache**: With mod_rewrite enabled
### Step 1: Download and Extract
1. Download all the files provided in the artifacts
2. Create a new folder named `relevant-reflex` on your computer
3. Copy all files into this folder maintaining the directory structure
### Step 2: Database Setup
1. **Login to phpMyAdmin** via your hosting control panel
2. **Create a new database**:
- Database name: `relevant_reflex_db` (or your preferred name)
- Collation: `utf8mb4_unicode_ci`
3. **Import the schema**:
- Click on your database
- Go to "Import" tab
- Choose the `database_schema.sql` file
- Click "Go" to execute
### Step 3: Configuration
1. **Edit config.php**:
```php
define('DB_HOST', 'localhost');
define('DB_USER', 'your_db_username'); // From your hosting panel
define('DB_PASS', 'your_db_password'); // From your hosting panel
define('DB_NAME', 'relevant_reflex_db'); // Your database name
define('SITE_URL', 'https://yourdomain.com'); // Your actual domain
```
2. **Update site settings** in other files if needed
### Step 4: File Upload
1. **Connect via FTP/File Manager**:
- Use your hosting panel's file manager or FTP client
- Navigate to `public_html` directory (or your domain's root)
2. **Upload files**:
- Upload all files maintaining the folder structure
- Ensure permissions are set correctly:
- Files: 644
- Directories: 755
- config.php: 600 (more secure)
### Step 5: Testing
1. **Visit your website**: `https://yourdomain.com`
2. **Default admin login**:
- Username: `admin`
- Email: `admin@relevantreflex.com`
- Password: `admin123`
- **⚠️ Change this immediately after first login!**
3. **Test all features**:
- Dashboard loading
- User management
- Panel creation
- Mobile responsiveness
### Step 6: Security Hardening
1. **Change default admin password**
2. **Update config.php** with strong database credentials
3. **Enable SSL certificate** (usually free with hosting)
4. **Uncomment HTTPS redirect** in .htaccess:
```apache
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
```
## 🎨 Customization
### Changing Colors and Theme
All colors are centralized in `assets/css/main.css` at the top:
```css
:root {
--primary-color: #0066cc; /* Change main brand color */
--primary-hover: #0052a3; /* Hover state */
--success-color: #28a745; /* Success messages */
--warning-color: #ffc107; /* Warnings */
--danger-color: #dc3545; /* Errors */
/* ... more color variables */
}
```
Simply update these values to match your brand colors.
### Adding Your Logo
1. **Replace the RR logo**:
- Update the `.logo` content in `includes/navigation.php`
- Or replace with an image: ``
2. **Update favicon**:
- Add your `favicon.ico` to the root directory
- Update the reference in `includes/header.php`
### Custom Styling
- **Main styles**: `assets/css/main.css`
- **Mobile styles**: `assets/css/responsive.css`
- **Dashboard styles**: `assets/css/dashboard.css`
## 📱 Mobile Optimization
The system is built mobile-first with:
- **Responsive Grid System**: Adapts to all screen sizes
- **Touch-Friendly Interface**: 44px minimum touch targets
- **Optimized Navigation**: Hamburger menu on mobile
- **Fast Loading**: Optimized assets and caching
- **Progressive Enhancement**: Works without JavaScript
## 🔧 Maintenance
### Regular Tasks
1. **Database Backups**: Weekly automated backups recommended
2. **Update Dependencies**: Keep PHP and MySQL updated
3. **Monitor Performance**: Check load times and optimize
4. **Security Updates**: Regular security audits
5. **Content Updates**: Keep information current
### Performance Optimization
1. **Enable Gzip Compression** (included in .htaccess)
2. **Optimize Images**: Use WebP format when possible
3. **Monitor Database**: Run `OPTIMIZE TABLE` monthly
4. **Cache Headers**: Properly configured in .htaccess
5. **CDN Integration**: Consider using a CDN for static assets
## 🔐 Security Features
- **SQL Injection Protection**: PDO prepared statements
- **XSS Prevention**: Input sanitization and CSP headers
- **CSRF Protection**: Session-based token validation
- **Secure Headers**: Comprehensive security headers
- **File Upload Security**: Restricted file types and locations
- **Access Control**: Role-based permissions
## 🆘 Troubleshooting
### Common Issues
**1. Database Connection Error**
```
Solution: Check config.php credentials and database server status
```
**2. Page Not Found (404)**
```
Solution: Verify .htaccess file is uploaded and mod_rewrite is enabled
```
**3. Slow Loading**
```
Solution: Enable compression, check hosting performance, optimize images
```
**4. Mobile Display Issues**
```
Solution: Clear browser cache, check responsive.css is loaded
```
**5. JavaScript Not Working**
```
Solution: Check browser console for errors, verify JS files are accessible
```
### Getting Help
1. **Check Error Logs**: In your hosting control panel
2. **Browser Console**: F12 to check for JavaScript errors
3. **PHP Error Display**: Temporarily enable in config.php for debugging
4. **Hosting Support**: Contact your hosting provider for server issues
## 📞 Support
For technical support and customization services:
- **Email**: support@relevantreflex.com
- **Documentation**: Check inline comments in code files
- **Updates**: Monitor for system updates and security patches
## 📝 License
This system is proprietary software developed for Relevant Reflex. All rights reserved.
## 🚀 Quick Start Checklist
- [ ] Create database in phpMyAdmin
- [ ] Import database_schema.sql
- [ ] Update config.php with database credentials
- [ ] Upload all files to web server
- [ ] Set correct file permissions
- [ ] Test login with admin/admin123
- [ ] Change default admin password
- [ ] Customize colors and branding
- [ ] Enable SSL and HTTPS redirect
- [ ] Test all functionality
- [ ] Setup regular backups
## Version Information
- **Version**: 1.0.0
- **Release Date**: September 2025
- **PHP Compatibility**: 7.4+
- **MySQL Compatibility**: 5.7+
- **Browser Support**: All modern browsers, IE11+
---
**Important**: Always backup your database and files before making changes or updates.
-------------------- END OF FILE --------------------
FILE: RR SHOP/referral-audit.php
TYPE: PHP
SIZE: 44.13 KB
------------------------------------------------------------
prepare("
SELECT id, email, first_name, last_name, gender, date_of_birth, postcode,
status, created_at, email_verified, onboarding_completed
FROM users WHERE email = ? LIMIT 1
");
$stmt->execute([$searchEmail]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$user) $searchError = 'No member found with that email.';
} catch (Exception $e) { $searchError = 'Database error: ' . $e->getMessage(); }
}
// ─── If member found, run the full audit ───
if ($user) {
$userId = (int)$user['id'];
$report = [];
try {
$panelPdo = getPanelDBConnection();
// Tier configuration
$report['tier_cfg'] = null;
try {
$st = $panelPdo->prepare("SELECT * FROM member_referral_tiers WHERE user_id = ?");
$st->execute([$userId]);
$report['tier_cfg'] = $st->fetch(PDO::FETCH_ASSOC) ?: null;
} catch (Exception $e) { /* table may not exist */ }
// Aggregate referral stats
$st = $panelPdo->prepare("
SELECT
COUNT(*) AS total_clicks,
COALESCE(SUM(signup_completed), 0) AS signups,
COALESCE(SUM(email_verified), 0) AS verified,
COUNT(DISTINCT ip_address) AS unique_ips,
COUNT(DISTINCT referee_user_id) AS unique_referees,
COUNT(DISTINCT user_agent) AS unique_uas,
MIN(clicked_at) AS first_click,
MAX(clicked_at) AS last_click
FROM member_referral_clicks WHERE referrer_user_id = ?
");
$st->execute([$userId]);
$report['aggregate'] = $st->fetch(PDO::FETCH_ASSOC) ?: [];
// Engagement (mobile + profilers)
$st = $panelPdo->prepare("
SELECT
COUNT(*) AS verified_referees,
SUM(CASE WHEN EXISTS(SELECT 1 FROM mobile_verifications mv
WHERE mv.user_id = u.id AND mv.is_verified = 1)
THEN 1 ELSE 0 END) AS mobile_verified,
SUM(CASE WHEN (SELECT COUNT(*) FROM profiler_completion
WHERE user_id = u.id AND is_completed = 1) > 0
THEN 1 ELSE 0 END) AS profilers_started
FROM users u
JOIN member_referral_clicks mrc ON mrc.referee_user_id = u.id
WHERE mrc.referrer_user_id = ? AND mrc.email_verified = 1
");
$st->execute([$userId]);
$report['engagement'] = $st->fetch(PDO::FETCH_ASSOC) ?: [];
// Timing analysis
$st = $panelPdo->prepare("
SELECT
COUNT(*) AS verified_count,
MIN(TIMESTAMPDIFF(SECOND, clicked_at, signed_up_at)) AS click_to_signup_min_s,
ROUND(AVG(TIMESTAMPDIFF(SECOND, clicked_at, signed_up_at))) AS click_to_signup_avg_s,
MAX(TIMESTAMPDIFF(SECOND, clicked_at, signed_up_at)) AS click_to_signup_max_s,
MIN(TIMESTAMPDIFF(SECOND, signed_up_at, verified_at)) AS signup_to_verify_min_s,
ROUND(AVG(TIMESTAMPDIFF(SECOND, signed_up_at, verified_at))) AS signup_to_verify_avg_s,
MAX(TIMESTAMPDIFF(SECOND, signed_up_at, verified_at)) AS signup_to_verify_max_s,
SUM(CASE WHEN TIMESTAMPDIFF(SECOND, clicked_at, signed_up_at) < 15 THEN 1 ELSE 0 END) AS fast_signups_under_15s,
SUM(CASE WHEN TIMESTAMPDIFF(SECOND, signed_up_at, verified_at) < 30 THEN 1 ELSE 0 END) AS fast_verifies_under_30s
FROM member_referral_clicks
WHERE referrer_user_id = ? AND email_verified = 1
AND clicked_at IS NOT NULL AND signed_up_at IS NOT NULL AND verified_at IS NOT NULL
");
$st->execute([$userId]);
$report['timing'] = $st->fetch(PDO::FETCH_ASSOC) ?: [];
// Hour-of-day distribution (IST)
$st = $panelPdo->prepare("
SELECT
HOUR(CONVERT_TZ(signed_up_at, '+00:00', '+05:30')) AS ist_hour,
COUNT(*) AS signups
FROM member_referral_clicks
WHERE referrer_user_id = ? AND email_verified = 1
GROUP BY ist_hour ORDER BY ist_hour
");
$st->execute([$userId]);
$report['hours'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
// Burst detection (with IP diversity)
$st = $panelPdo->prepare("
SELECT
DATE_FORMAT(CONVERT_TZ(signed_up_at, '+00:00', '+05:30'), '%Y-%m-%d %H:00') AS hour_window_ist,
COUNT(*) AS signups,
COUNT(DISTINCT ip_address) AS unique_ips,
ROUND(COUNT(DISTINCT ip_address) / COUNT(*), 2) AS ip_diversity
FROM member_referral_clicks
WHERE referrer_user_id = ? AND email_verified = 1 AND signed_up_at IS NOT NULL
GROUP BY hour_window_ist HAVING signups >= 3
ORDER BY signups DESC LIMIT 20
");
$st->execute([$userId]);
$report['bursts'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
// Top IPs (any IP feeding multiple signups)
$st = $panelPdo->prepare("
SELECT
ip_address,
COUNT(*) AS hits,
SUM(signup_completed) AS signups,
SUM(email_verified) AS verified
FROM member_referral_clicks
WHERE referrer_user_id = ?
GROUP BY ip_address
HAVING hits > 1
ORDER BY hits DESC LIMIT 15
");
$st->execute([$userId]);
$report['top_ips'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
// Email pattern duplicates (Gmail dot/plus tricks)
$st = $panelPdo->prepare("
SELECT
REPLACE(SUBSTRING_INDEX(SUBSTRING_INDEX(u.email, '@', 1), '+', 1), '.', '') AS norm_local,
SUBSTRING_INDEX(u.email, '@', -1) AS domain,
COUNT(*) AS dupes,
GROUP_CONCAT(u.email ORDER BY u.email SEPARATOR ' | ') AS emails
FROM member_referral_clicks mrc
JOIN users u ON u.id = mrc.referee_user_id
WHERE mrc.referrer_user_id = ?
GROUP BY norm_local, domain HAVING dupes > 1
ORDER BY dupes DESC LIMIT 10
");
$st->execute([$userId]);
$report['email_dupes'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
// Geographic — region (first digit)
$st = $panelPdo->prepare("
SELECT
LEFT(u.postcode, 1) AS region_code,
CASE LEFT(u.postcode, 1)
WHEN '1' THEN 'Delhi / Haryana / Punjab / HP / J&K'
WHEN '2' THEN 'UP / Uttarakhand'
WHEN '3' THEN 'Rajasthan / Gujarat'
WHEN '4' THEN 'Maharashtra / MP / Goa / Chhattisgarh'
WHEN '5' THEN 'Telangana / AP / Karnataka'
WHEN '6' THEN 'Tamil Nadu / Kerala / Puducherry'
WHEN '7' THEN 'West Bengal / Odisha / NE States'
WHEN '8' THEN 'Bihar / Jharkhand'
ELSE 'Other / Unknown'
END AS region,
COUNT(*) AS members
FROM users u
JOIN member_referral_clicks mrc ON mrc.referee_user_id = u.id
WHERE mrc.referrer_user_id = ? AND mrc.email_verified = 1
AND u.postcode IS NOT NULL AND u.postcode != ''
GROUP BY region_code, region ORDER BY members DESC
");
$st->execute([$userId]);
$report['regions'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
// Geographic — district (first 3 digits)
$st = $panelPdo->prepare("
SELECT
LEFT(u.postcode, 3) AS prefix,
COUNT(*) AS members,
GROUP_CONCAT(DISTINCT LEFT(u.postcode, 6) ORDER BY u.postcode SEPARATOR ', ') AS sample_pins
FROM users u
JOIN member_referral_clicks mrc ON mrc.referee_user_id = u.id
WHERE mrc.referrer_user_id = ? AND mrc.email_verified = 1
AND u.postcode IS NOT NULL AND u.postcode != ''
GROUP BY prefix ORDER BY members DESC LIMIT 25
");
$st->execute([$userId]);
$report['districts'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
// Get referee user IDs for cross-DB survey lookup
$st = $panelPdo->prepare("
SELECT referee_user_id FROM member_referral_clicks
WHERE referrer_user_id = ? AND referee_user_id IS NOT NULL AND email_verified = 1
");
$st->execute([$userId]);
$refIds = array_map('intval', array_column($st->fetchAll(PDO::FETCH_ASSOC), 'referee_user_id'));
// Survey activity (cross-DB → rrshop)
$report['surveys'] = ['referees_with_invites' => 0, 'total_invites' => 0,
'total_completes' => 0, 'unique_with_completes' => 0,
'bad_quality' => 0];
if (!empty($refIds)) {
try {
$shopPdo = getDBConnection();
$placeholders = implode(',', array_fill(0, count($refIds), '?'));
$st = $shopPdo->prepare("
SELECT
COUNT(DISTINCT sent_to_user_id) AS referees_with_invites,
COUNT(*) AS total_invites,
SUM(CASE WHEN status = 'complete' THEN 1 ELSE 0 END) AS total_completes,
COUNT(DISTINCT CASE WHEN status = 'complete' THEN sent_to_user_id END) AS unique_with_completes,
SUM(CASE WHEN quality_flag IN ('speedster','ip_duplicate','client_flagged') THEN 1 ELSE 0 END) AS bad_quality
FROM survey_urls
WHERE sent_to_user_id IN ($placeholders)
");
$st->execute($refIds);
$report['surveys'] = $st->fetch(PDO::FETCH_ASSOC) ?: $report['surveys'];
} catch (Exception $e) { error_log("Audit survey lookup error: " . $e->getMessage()); }
}
// Recent referees detail (top 30)
$st = $panelPdo->prepare("
SELECT
mrc.id, mrc.ip_address, mrc.email AS masked_email, mrc.signup_completed, mrc.email_verified,
mrc.signup_reward_paid, mrc.clicked_at, mrc.signed_up_at, mrc.verified_at,
u.email AS full_email, u.gender, u.postcode, u.status,
EXISTS(SELECT 1 FROM mobile_verifications mv WHERE mv.user_id = u.id AND mv.is_verified = 1) AS mob_v,
(SELECT COUNT(*) FROM profiler_completion WHERE user_id = u.id AND is_completed = 1) AS prof_c
FROM member_referral_clicks mrc
LEFT JOIN users u ON u.id = mrc.referee_user_id
WHERE mrc.referrer_user_id = ?
ORDER BY mrc.clicked_at DESC LIMIT 30
");
$st->execute([$userId]);
$report['recent'] = $st->fetchAll(PDO::FETCH_ASSOC) ?: [];
} catch (Exception $e) { error_log("Referral audit error: " . $e->getMessage()); }
// ─── Compute fraud verdict score (0=clean, higher=worse) ───
$score = 0;
$signals = [];
$agg = $report['aggregate'] ?? [];
$eng = $report['engagement'] ?? [];
$tim = $report['timing'] ?? [];
$bursts = $report['bursts'] ?? [];
$verifiedN = max((int)($agg['verified'] ?? 0), 1);
// 1) Aggregate IP diversity
$aggIpRatio = (int)($agg['total_clicks'] ?? 0) > 0
? (int)($agg['unique_ips'] ?? 0) / (int)$agg['total_clicks']
: 0;
if ($aggIpRatio >= 0.5) { $signals[] = ['ok', 'IP diversity (overall): ' . round($aggIpRatio*100) . '% — healthy']; }
elseif ($aggIpRatio >= 0.3) { $score += 1; $signals[] = ['warn', 'IP diversity (overall): ' . round($aggIpRatio*100) . '% — borderline']; }
else { $score += 2; $signals[] = ['bad', 'IP diversity (overall): ' . round($aggIpRatio*100) . '% — too low']; }
// 2) Worst-burst IP diversity
$worstBurst = 1.0;
foreach ($bursts as $b) { if ((float)$b['ip_diversity'] < $worstBurst) $worstBurst = (float)$b['ip_diversity']; }
if (!empty($bursts)) {
if ($worstBurst >= 0.7) { $signals[] = ['ok', 'Worst-burst IP diversity: ' . round($worstBurst*100) . '% — healthy']; }
elseif ($worstBurst >= 0.3){ $score += 1; $signals[] = ['warn', 'Worst-burst IP diversity: ' . round($worstBurst*100) . '%']; }
else { $score += 3; $signals[] = ['bad', 'Worst-burst IP diversity: ' . round($worstBurst*100) . '% — concentrated']; }
}
// 3) Mobile verified rate
$mobRate = $verifiedN > 0 ? (int)($eng['mobile_verified'] ?? 0) / $verifiedN : 0;
if ($mobRate >= 0.30) { $signals[] = ['ok', 'Mobile-verified rate: ' . round($mobRate*100) . '%']; }
elseif ($mobRate >= 0.10) { $score += 1; $signals[] = ['warn', 'Mobile-verified rate: ' . round($mobRate*100) . '% — low']; }
else { $score += 2; $signals[] = ['bad', 'Mobile-verified rate: ' . round($mobRate*100) . '% — almost no engagement']; }
// 4) Profilers started rate
$profRate = $verifiedN > 0 ? (int)($eng['profilers_started'] ?? 0) / $verifiedN : 0;
if ($profRate >= 0.30) { $signals[] = ['ok', 'Profiler-started rate: ' . round($profRate*100) . '%']; }
elseif ($profRate >= 0.10) { $score += 1; $signals[] = ['warn', 'Profiler-started rate: ' . round($profRate*100) . '% — low']; }
else { $score += 2; $signals[] = ['bad', 'Profiler-started rate: ' . round($profRate*100) . '% — empty profiles']; }
// 5) Fast verifies (<30s) rate
$vCount = (int)($tim['verified_count'] ?? 0);
$fast30 = (int)($tim['fast_verifies_under_30s'] ?? 0);
$fastRate = $vCount > 0 ? $fast30 / $vCount : 0;
if ($fastRate < 0.20) { $signals[] = ['ok', 'Fast-verify rate (<30s): ' . round($fastRate*100) . '%']; }
elseif ($fastRate < 0.50) { $signals[] = ['info', 'Fast-verify rate (<30s): ' . round($fastRate*100) . '% — note']; }
else { $score += 1; $signals[] = ['warn', 'Fast-verify rate (<30s): ' . round($fastRate*100) . '% — high (event style or automation)']; }
// 6) Scripted-fast signups (<15s)
$fast15 = (int)($tim['fast_signups_under_15s'] ?? 0);
if ($fast15 == 0) { $signals[] = ['ok', 'Scripted-fast signups (<15s): 0']; }
elseif ($fast15 < 5) { $score += 1; $signals[] = ['warn', "Scripted-fast signups (<15s): $fast15"]; }
else { $score += 3; $signals[] = ['bad', "Scripted-fast signups (<15s): $fast15 — likely automation"]; }
// 7) Email pattern duplicates
$emailDupCount = count($report['email_dupes'] ?? []);
if ($emailDupCount == 0) { $signals[] = ['ok', 'Email pattern duplicates: none']; }
elseif ($emailDupCount <= 3) { $score += 1; $signals[] = ['warn', "Email pattern duplicates: $emailDupCount (possible Gmail tricks)"]; }
else { $score += 3; $signals[] = ['bad', "Email pattern duplicates: $emailDupCount — same person, multiple aliases"]; }
// 8) Surveys completed by referees
$sv = $report['surveys'] ?? [];
$totalRefs = max(count($refIds), 1);
$surveyEngagement = (int)($sv['unique_with_completes'] ?? 0) / $totalRefs;
if ($verifiedN >= 20) { // only meaningful at scale
if ($surveyEngagement >= 0.10) { $signals[] = ['ok', 'Survey engagement: ' . round($surveyEngagement*100) . '% of referees took surveys']; }
elseif ($surveyEngagement >= 0.03) { $score += 1; $signals[] = ['warn', 'Survey engagement: ' . round($surveyEngagement*100) . '% — low']; }
else { $score += 2; $signals[] = ['bad', 'Survey engagement: ' . round($surveyEngagement*100) . '% — referees never take surveys']; }
}
// Verdict from total score
if ($score <= 3) { $verdictLevel = 'clean'; $verdictText = 'CLEAN'; $verdictColor = '#059669'; $verdictBg = '#d1fae5'; $verdictIcon = '✓'; }
elseif ($score <= 7) { $verdictLevel = 'review'; $verdictText = 'REQUIRES REVIEW'; $verdictColor = '#d97706'; $verdictBg = '#fef3c7'; $verdictIcon = '⚠'; }
else { $verdictLevel = 'risky'; $verdictText = 'HIGH FRAUD RISK'; $verdictColor = '#dc2626'; $verdictBg = '#fee2e2'; $verdictIcon = '✗'; }
$report['verdict'] = [
'score' => $score, 'level' => $verdictLevel, 'text' => $verdictText,
'color' => $verdictColor, 'bg' => $verdictBg, 'icon' => $verdictIcon, 'signals' => $signals
];
}
// Helpers
if (!function_exists('fmtNum')) { function fmtNum($v) { return number_format((int)$v); } }
if (!function_exists('fmtIST')) {
function fmtIST($dt, $fmt = 'd M Y h:i A') {
if (!$dt || $dt === '0000-00-00 00:00:00') return '—';
try { $d = new DateTime($dt, new DateTimeZone('Asia/Kolkata')); return $d->format($fmt); }
catch (Exception $e) { return date($fmt, strtotime($dt)); }
}
}
function fmtDuration($s) {
if ($s === null || $s === '') return '—';
$s = (int)$s;
if ($s < 60) return $s . 's';
if ($s < 3600) return floor($s/60) . 'm ' . ($s%60) . 's';
if ($s < 86400) return floor($s/3600) . 'h ' . floor(($s%3600)/60) . 'm';
return floor($s/86400) . 'd ' . floor(($s%86400)/3600) . 'h';
}
include 'includes/header.php';
?>
Referral Audit Report
Generated IST · Relevant Reflex Admin · By
Referral Audit
Comprehensive fraud/legitimacy assessment for any referring panel member. Enter their email to run a full multi-signal check across IPs, timing, engagement, geographic spread, and survey activity.
IP Repeats — addresses appearing in multiple referrals
IP Address
Total Hits
Signups
Verified
A small number of repeats with 2-3 hits each is normal (people use multiple devices). Many IPs each producing 5+ verified signups is a fraud signal.
IP Repeats
No IP appears in multiple referrals — every signup came from a distinct IP. ✓
Email Pattern Duplicates — Gmail dot/plus aliases pointing to same inbox
Normalized Local
Domain
Variants
Emails
Geographic Spread — by pincode region
xxxxx —
· %
Top Districts
Pincode Prefix
Members
Sample Pincodes
Recent Referees — most recent 30 click events
Email
IP
C→S
S→V
Verified
Mob
Prof
Postcode
Clicked (IST)
✓' : '—'; ?>
✓' : '—'; ?>
0 ? '' . (int)$r['prof_c'] . '' : '—'; ?>
Report generated IST · Relevant Reflex Admin Audit · For internal review only
How this audit works: Enter the email of any panel member who has been referring others. The tool runs 8 independent checks across IP diversity, signup/verify timing patterns, post-signup engagement, geographic concentration, email-pattern tricks, burst-window analysis, and survey activity in real projects — then scores each signal and gives a verdict (CLEAN / REQUIRES REVIEW / HIGH FRAUD RISK). Click "Save as PDF" once results are shown to keep a record for fraud-related disputes or member suspensions.
-------------------- END OF FILE --------------------
FILE: RR SHOP/robots.txt
TYPE: TXT
SIZE: 2.16 KB
------------------------------------------------------------
# Relevant Reflex Panel Management System
# Robots.txt file for search engine optimization
User-agent: *
# Allow access to main pages
Allow: /
Allow: /index.php
Allow: /users.php
Allow: /panel.php
Allow: /supply.php
Allow: /demand.php
Allow: /finance.php
Allow: /support.php
Allow: /settings.php
# Allow access to static assets
Allow: /assets/css/
Allow: /assets/js/
Allow: /assets/images/
# Disallow sensitive files and directories
Disallow: /config.php
Disallow: /database_schema.sql
Disallow: /.htaccess
Disallow: /includes/
Disallow: /logs/
Disallow: /backups/
Disallow: /temp/
Disallow: /cache/
Disallow: /admin/
Disallow: /api/
Disallow: /private/
# Disallow URL parameters that might create duplicate content
Disallow: /*?*
Disallow: /*&*
Disallow: /*/search?*
Disallow: /*/filter?*
# Disallow error pages
Disallow: /error-*
Disallow: /404.html
Disallow: /500.html
Disallow: /maintenance.html
# Block access to development and testing files
Disallow: /test/
Disallow: /dev/
Disallow: /staging/
Disallow: /*.bak
Disallow: /*.tmp
Disallow: /*.log
# Block common exploits and security probes
Disallow: /wp-admin/
Disallow: /wordpress/
Disallow: /wp-content/
Disallow: /admin.php
Disallow: /administrator/
Disallow: /phpmyadmin/
Disallow: /phpMyAdmin/
# Block unwanted file types
Disallow: /*.sql$
Disallow: /*.zip$
Disallow: /*.tar.gz$
Disallow: /*.bak$
Disallow: /*.conf$
Disallow: /*.ini$
# Sitemap location
Sitemap: https://yourdomain.com/sitemap.xml
# Crawl delay for respectful crawling (optional)
# Crawl-delay: 1
# Specific rules for different bots (optional)
# Google Bot - allow everything we want indexed
User-agent: Googlebot
Allow: /
Disallow: /config.php
Disallow: /includes/
Disallow: /*?*
# Bing Bot
User-agent: Bingbot
Allow: /
Disallow: /config.php
Disallow: /includes/
# Block aggressive bots that might overload the server
User-agent: AhrefsBot
Disallow: /
User-agent: MJ12bot
Disallow: /
User-agent: SemrushBot
Disallow: /
User-agent: DotBot
Disallow: /
# Allow social media bots for link previews
User-agent: facebookexternalhit
Allow: /
User-agent: Twitterbot
Allow: /
User-agent: LinkedInBot
Allow: /
# Note: Update "yourdomain.com" with your actual domain name
-------------------- END OF FILE --------------------
FILE: RR SHOP/settings.php
TYPE: PHP
SIZE: 31.34 KB
------------------------------------------------------------
exec("
CREATE TABLE IF NOT EXISTS company_settings (
id INT AUTO_INCREMENT PRIMARY KEY,
setting_key VARCHAR(100) UNIQUE NOT NULL,
setting_value TEXT,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
)
");
} catch (Exception $e) {
// Table might already exist
}
// Handle form submission
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['save_settings'])) {
try {
$fields = [
'company_name', 'company_address', 'company_city', 'company_state',
'company_country', 'company_pincode', 'company_phone', 'company_email',
'company_website',
'tax_gst', 'tax_pan', 'tax_cin', 'tax_sac_code',
'bank_name', 'bank_account_name', 'bank_account_number', 'bank_ifsc',
'bank_branch', 'bank_swift',
'invoice_prefix', 'invoice_terms', 'invoice_notes', 'invoice_footer',
'email_mask_enabled'
];
$stmt = $pdo->prepare("
INSERT INTO company_settings (setting_key, setting_value, updated_at)
VALUES (?, ?, NOW())
ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value), updated_at = NOW()
");
foreach ($fields as $field) {
$value = trim($_POST[$field] ?? '');
$stmt->execute([$field, $value]);
}
$success = 'Company settings saved successfully!';
$mode = 'view';
logActivity($_SESSION['admin_id'], 'update', 'Updated company invoicing settings', 'settings', null);
} catch (Exception $e) {
$error = 'Error saving settings: ' . $e->getMessage();
$mode = 'edit';
}
}
// Load current settings
$settings = [];
try {
$result = $pdo->query("SELECT setting_key, setting_value FROM company_settings");
while ($row = $result->fetch()) {
$settings[$row['setting_key']] = $row['setting_value'];
}
} catch (Exception $e) {}
function getSetting($key, $default = '') {
global $settings;
return $settings[$key] ?? $default;
}
$hasData = !empty(array_filter($settings));
include 'includes/header.php';
?>
✅
⚠
Company Settings
Manage your company details used in client invoicing