# RR SHOP 17FEB26 01 - REPOSITORY
================================================================================
Project Name: RR shop 17Feb26 01
Created: 2026-02-17 02:46:11
Last Updated: 2026-02-17 02:47:15
Source ZIP: rrshop.zip
Total Files: 42
Total Folders: 7
================================================================================
## FILE STRUCTURE
================================================================================
RR shop 17Feb26 01/
├── affiliate_create.php
├── finance.php
├── demand.php
├── login.php
├── support.php
├── member_view.php
├── supply.php
├── logout.php
├── affiliate_edit.php
├── users.php
├── settings.php
├── client_create.php
├── members.php
├── index.php
├── error-404.html
├── affiliate_view.php
├── client_edit.php
├── clients.php
├── config.php
├── robots.txt
├── maintenance.html
├── generate_hash.php
├── assets/
│ ├── images/
│ │ └── logo.svg
│ ├── css/
│ │ ├── member-enhancements.css
│ │ ├── dashboard.css
│ │ ├── main.css
│ │ └── responsive.css
│ └── js/
│ ├── dashboard.js
│ └── main.js
├── error-500.html
├── README.md
├── member_edit.php
├── panel.php
├── sitemap.xml
├── uploads/
│ └── clients/
│ ├── BD0B267D_1769770697_0.pdf
│ ├── A6FB1FDC_1770453927_0.pdf
│ ├── BD0B267D_1769743328_0.png
│ └── EA89781D_1769740517_0.pdf
├── client_view.php
└── includes/
├── footer.php
├── navigation.php
└── header.php
================================================================================
## FILE CONTENTS
================================================================================
### FILE 1: affiliate_create.php
- Type: PHP
- Size: 17.14 KB
- Path: .
- Name: affiliate_create.php
------------------------------------------------------------
------------------------------------------------------------
prepare("SELECT id FROM affiliates WHERE affiliate_code = ?");
$checkStmt->execute([$affiliate_code]);
} while ($checkStmt->fetch());
// Insert affiliate
$stmt = $pdo->prepare("
INSERT INTO affiliates
(affiliate_code, type, company_name, incharge_name, state, postal_code,
place_name, mobile, email, url, signup_reward, survey_reward, created_by, status, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', NOW())
");
$stmt->execute([
$affiliate_code, $type, $company_name, $incharge_name, $state, $postal_code,
$place_name, $mobile, $email, $url, $signup_reward, $survey_reward, $_SESSION['admin_id']
]);
$affiliate_id = $pdo->lastInsertId();
// Handle file uploads
if (!empty($_FILES['attachments']['name'][0])) {
$upload_dir = 'uploads/affiliates/';
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
$allowed_types = ['application/pdf', 'image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
$max_files = 10;
$max_size = 5 * 1024 * 1024; // 5MB
for ($i = 0; $i < min(count($_FILES['attachments']['name']), $max_files); $i++) {
if ($_FILES['attachments']['error'][$i] === UPLOAD_ERR_OK) {
$file_type = $_FILES['attachments']['type'][$i];
$file_size = $_FILES['attachments']['size'][$i];
if (in_array($file_type, $allowed_types) && $file_size <= $max_size) {
$file_name = $_FILES['attachments']['name'][$i];
$file_tmp = $_FILES['attachments']['tmp_name'][$i];
// Generate unique filename
$extension = pathinfo($file_name, PATHINFO_EXTENSION);
$unique_name = $affiliate_code . '_' . time() . '_' . $i . '.' . $extension;
$file_path = $upload_dir . $unique_name;
if (move_uploaded_file($file_tmp, $file_path)) {
$attachStmt = $pdo->prepare("
INSERT INTO affiliate_attachments
(affiliate_id, file_name, file_path, file_type, file_size, uploaded_at)
VALUES (?, ?, ?, ?, ?, NOW())
");
$attachStmt->execute([
$affiliate_id, $file_name, $file_path, $file_type, $file_size
]);
}
}
}
}
}
logActivity($_SESSION['admin_id'], 'create_affiliate', "Created affiliate: $affiliate_code", 'affiliate', $affiliate_id);
header('Location: supply.php?success=created&code=' . $affiliate_code);
exit;
} catch (Exception $e) {
$error = 'An error occurred. Please try again.';
error_log("Create affiliate error: " . $e->getMessage());
}
}
}
include 'includes/header.php';
?>
-------------------- END OF FILE --------------------
### FILE 2: affiliate_edit.php
- Type: PHP
- Size: 10.64 KB
- Path: .
- Name: affiliate_edit.php
------------------------------------------------------------
------------------------------------------------------------
prepare("
UPDATE affiliates
SET type = ?, company_name = ?, incharge_name = ?, state = ?,
postal_code = ?, place_name = ?, mobile = ?, email = ?,
url = ?, signup_reward = ?, survey_reward = ?, status = ?, updated_at = NOW()
WHERE id = ?
");
$stmt->execute([
$type, $company_name, $incharge_name, $state, $postal_code,
$place_name, $mobile, $email, $url, $signup_reward, $survey_reward, $status, $affiliate_id
]);
logActivity($_SESSION['admin_id'], 'update_affiliate', "Updated affiliate #$affiliate_id", 'affiliate', $affiliate_id);
$success = 'Affiliate updated successfully!';
} catch (Exception $e) {
$error = 'An error occurred. Please try again.';
error_log("Update affiliate error: " . $e->getMessage());
}
}
}
}
// Fetch affiliate details
try {
$pdo = getDBConnection();
$stmt = $pdo->prepare("SELECT * FROM affiliates WHERE id = ?");
$stmt->execute([$affiliate_id]);
$affiliate = $stmt->fetch();
if (!$affiliate) {
header('Location: supply.php');
exit;
}
} catch (Exception $e) {
error_log("Fetch affiliate error: " . $e->getMessage());
header('Location: supply.php');
exit;
}
$indian_states = [
'Andhra Pradesh', 'Arunachal Pradesh', 'Assam', 'Bihar', 'Chhattisgarh', 'Goa', 'Gujarat',
'Haryana', 'Himachal Pradesh', 'Jharkhand', 'Karnataka', 'Kerala', 'Madhya Pradesh',
'Maharashtra', 'Manipur', 'Meghalaya', 'Mizoram', 'Nagaland', 'Odisha', 'Punjab',
'Rajasthan', 'Sikkim', 'Tamil Nadu', 'Telangana', 'Tripura', 'Uttar Pradesh',
'Uttarakhand', 'West Bengal', 'Andaman and Nicobar Islands', 'Chandigarh',
'Dadra and Nagar Haveli and Daman and Diu', 'Delhi', 'Jammu and Kashmir', 'Ladakh',
'Lakshadweep', 'Puducherry'
];
include 'includes/header.php';
?>
-------------------- END OF FILE --------------------
### FILE 3: affiliate_view.php
- Type: PHP
- Size: 13.94 KB
- Path: .
- Name: affiliate_view.php
------------------------------------------------------------
------------------------------------------------------------
prepare("SELECT * FROM affiliates WHERE id = ?");
$stmt->execute([$affiliate_id]);
$affiliate = $stmt->fetch();
if (!$affiliate) {
header('Location: supply.php');
exit;
}
// Fetch attachments
$attachStmt = $pdo->prepare("SELECT * FROM affiliate_attachments WHERE affiliate_id = ?");
$attachStmt->execute([$affiliate_id]);
$attachments = $attachStmt->fetchAll();
// Fetch signups
$signupsStmt = $pdo->prepare("
SELECT * FROM affiliate_signups
WHERE affiliate_id = ?
ORDER BY clicked_at DESC
LIMIT 100
");
$signupsStmt->execute([$affiliate_id]);
$signups = $signupsStmt->fetchAll();
} catch (Exception $e) {
error_log("View affiliate error: " . $e->getMessage());
header('Location: supply.php');
exit;
}
$signup_url = 'https://relevantreflex.com/signup.php?ref=' . $affiliate['affiliate_code'];
include 'includes/header.php';
?>
Basic Information
Signup Reward
₹
₹5 email + ₹ mobile
Survey Revenue Share
₹
Per survey completed by referred members
Performance Statistics
Conversion Rate
0
? ($affiliate['total_verified_signups'] / $affiliate['total_signups'] * 100)
: 0;
echo number_format($conversion, 1);
?>%
Recent Signups (Last 100)
No signups yet
Email
Clicked At
Signed Up
Verified
Reward
Status
Not completed'; ?>
✅
Pending
✅
Not verified
₹
Pending
Complete
Awaiting Verification
Clicked Only
-------------------- END OF FILE --------------------
### FILE 4: client_create.php
- Type: PHP
- Size: 17.34 KB
- Path: .
- Name: client_create.php
------------------------------------------------------------
prepare("SELECT COUNT(*) FROM clients WHERE client_code = ?");
$stmt->execute([$client_code]);
} while ($stmt->fetchColumn() > 0);
$stmt = $pdo->prepare("
INSERT INTO clients
(client_code, company_name, industry, contact_person, email, phone,
address, city, country, postal_code, website, notes, currency, created_by, created_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())
");
$stmt->execute([
$client_code, $company_name, $industry, $contact_person, $email, $phone,
$address, $city, $country, $postal_code, $website, $notes, $currency, $_SESSION['admin_id']
]);
$client_id = $pdo->lastInsertId();
// Handle file uploads
if (!empty($_FILES['attachments']['name'][0])) {
$upload_dir = 'uploads/clients/';
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
$allowed_types = ['application/pdf', 'image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
$max_files = 10;
$max_size = 5 * 1024 * 1024; // 5MB
for ($i = 0; $i < min(count($_FILES['attachments']['name']), $max_files); $i++) {
if ($_FILES['attachments']['error'][$i] === UPLOAD_ERR_OK) {
$file_type = $_FILES['attachments']['type'][$i];
$file_size = $_FILES['attachments']['size'][$i];
if (in_array($file_type, $allowed_types) && $file_size <= $max_size) {
$file_name = $_FILES['attachments']['name'][$i];
$file_tmp = $_FILES['attachments']['tmp_name'][$i];
$extension = pathinfo($file_name, PATHINFO_EXTENSION);
$unique_name = $client_code . '_' . time() . '_' . $i . '.' . $extension;
$file_path = $upload_dir . $unique_name;
if (move_uploaded_file($file_tmp, $file_path)) {
$attachStmt = $pdo->prepare("
INSERT INTO client_attachments
(client_id, file_name, file_path, file_type, file_size, uploaded_at)
VALUES (?, ?, ?, ?, ?, NOW())
");
$attachStmt->execute([
$client_id, $file_name, $file_path, $file_type, $file_size
]);
}
}
}
}
}
logActivity($_SESSION['admin_id'], 'create_client', "Created client: $client_code", 'client', $client_id);
header('Location: clients.php?success=created&code=' . $client_code);
exit;
} catch (Exception $e) {
$error = 'An error occurred. Please try again.';
error_log("Create client error: " . $e->getMessage());
}
}
}
include 'includes/header.php';
?>
-------------------- END OF FILE --------------------
### FILE 5: client_edit.php
- Type: PHP
- Size: 19.31 KB
- Path: .
- Name: client_edit.php
------------------------------------------------------------
prepare("
UPDATE clients
SET company_name = ?, industry = ?, contact_person = ?, email = ?,
phone = ?, address = ?, city = ?, country = ?, postal_code = ?,
website = ?, notes = ?, status = ?, currency = ?, updated_at = NOW()
WHERE id = ?
");
$stmt->execute([
$company_name, $industry, $contact_person, $email, $phone,
$address, $city, $country, $postal_code, $website,
$notes, $status, $currency, $client_id
]);
// Handle file uploads
if (!empty($_FILES['attachments']['name'][0])) {
$upload_dir = 'uploads/clients/';
if (!file_exists($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
$allowed_types = ['application/pdf', 'image/jpeg', 'image/jpg', 'image/png', 'image/gif'];
$max_size = 5 * 1024 * 1024;
// Get client code
$clientStmt = $pdo->prepare("SELECT client_code FROM clients WHERE id = ?");
$clientStmt->execute([$client_id]);
$client_code = $clientStmt->fetchColumn();
for ($i = 0; $i < count($_FILES['attachments']['name']); $i++) {
if ($_FILES['attachments']['error'][$i] === UPLOAD_ERR_OK) {
$file_type = $_FILES['attachments']['type'][$i];
$file_size = $_FILES['attachments']['size'][$i];
if (in_array($file_type, $allowed_types) && $file_size <= $max_size) {
$file_name = $_FILES['attachments']['name'][$i];
$file_tmp = $_FILES['attachments']['tmp_name'][$i];
$extension = pathinfo($file_name, PATHINFO_EXTENSION);
$unique_name = $client_code . '_' . time() . '_' . $i . '.' . $extension;
$file_path = $upload_dir . $unique_name;
if (move_uploaded_file($file_tmp, $file_path)) {
$attachStmt = $pdo->prepare("
INSERT INTO client_attachments
(client_id, file_name, file_path, file_type, file_size, uploaded_at)
VALUES (?, ?, ?, ?, ?, NOW())
");
$attachStmt->execute([$client_id, $file_name, $file_path, $file_type, $file_size]);
}
}
}
}
}
logActivity($_SESSION['admin_id'], 'update_client', "Updated client #$client_id", 'client', $client_id);
$success = 'Client updated successfully!';
} catch (Exception $e) {
$error = 'An error occurred. Please try again.';
error_log("Update client error: " . $e->getMessage());
}
}
}
// Handle attachment deletion - SEPARATE ACTION
if ($_POST['action'] === 'delete_attachment') {
$attach_id = intval($_POST['attachment_id'] ?? 0);
if ($attach_id) {
try {
$pdo = getDBConnection();
$stmt = $pdo->prepare("SELECT * FROM client_attachments WHERE id = ? AND client_id = ?");
$stmt->execute([$attach_id, $client_id]);
$attach = $stmt->fetch();
if ($attach) {
if (file_exists($attach['file_path'])) {
unlink($attach['file_path']);
}
$stmt = $pdo->prepare("DELETE FROM client_attachments WHERE id = ?");
$stmt->execute([$attach_id]);
$success = 'Attachment deleted successfully!';
}
} catch (Exception $e) {
$error = 'Error deleting attachment.';
}
}
}
}
// Fetch client details
try {
$pdo = getDBConnection();
$stmt = $pdo->prepare("SELECT * FROM clients WHERE id = ?");
$stmt->execute([$client_id]);
$client = $stmt->fetch();
if (!$client) {
header('Location: clients.php');
exit;
}
// Fetch attachments
$attachStmt = $pdo->prepare("SELECT * FROM client_attachments WHERE client_id = ?");
$attachStmt->execute([$client_id]);
$attachments = $attachStmt->fetchAll();
} catch (Exception $e) {
error_log("Fetch client error: " . $e->getMessage());
header('Location: clients.php');
exit;
}
$industries = [
'Technology', 'Healthcare', 'Finance', 'Retail', 'Manufacturing',
'Education', 'Real Estate', 'Hospitality', 'Consulting',
'Marketing', 'Construction', 'Transportation', 'Other'
];
$countries = [
'India', 'United States', 'United Kingdom', 'Canada', 'Australia',
'Germany', 'France', 'Japan', 'China', 'Singapore',
'United Arab Emirates', 'Saudi Arabia', 'Malaysia', 'Thailand', 'Indonesia',
'Philippines', 'Vietnam', 'South Korea', 'Hong Kong', 'Taiwan',
'Bangladesh', 'Pakistan', 'Sri Lanka', 'Nepal', 'Bhutan',
'Afghanistan', 'Maldives', 'Myanmar', 'Cambodia', 'Laos',
'Other'
];
include 'includes/header.php';
?>
-------------------- END OF FILE --------------------
### FILE 6: client_view.php
- Type: PHP
- Size: 11.71 KB
- Path: .
- Name: client_view.php
------------------------------------------------------------
prepare("SELECT * FROM clients WHERE id = ?");
$stmt->execute([$client_id]);
$client = $stmt->fetch();
if (!$client) {
header('Location: clients.php');
exit;
}
// Fetch attachments
$attachStmt = $pdo->prepare("SELECT * FROM client_attachments WHERE client_id = ?");
$attachStmt->execute([$client_id]);
$attachments = $attachStmt->fetchAll();
} catch (Exception $e) {
error_log("View client error: " . $e->getMessage());
header('Location: clients.php');
exit;
}
include 'includes/header.php';
?>
✅
Basic Information
Company Name
Industry
Contact Person
Location Information
Address
City
Country
Postal Code
Business Information
Total Projects
Total Revenue
₹
Account Created
Last Updated
Attachments ()
📎
No documents uploaded yet
-------------------- END OF FILE --------------------
### FILE 7: clients.php
- Type: PHP
- Size: 13.34 KB
- Path: .
- Name: clients.php
------------------------------------------------------------
prepare("SELECT client_code FROM clients WHERE id = ?");
$stmt->execute([$delete_id]);
$client_code = $stmt->fetchColumn();
$stmt = $pdo->prepare("DELETE FROM clients WHERE id = ?");
$stmt->execute([$delete_id]);
logActivity($_SESSION['admin_id'], 'delete_client', "Deleted client: $client_code", 'client', $delete_id);
$success = 'Client deleted successfully!';
} catch (Exception $e) {
$error = 'Error deleting client.';
}
}
}
// Fetch clients
try {
$pdo = getDBConnection();
$stmt = $pdo->query("SELECT * FROM clients ORDER BY created_at DESC");
$clients = $stmt->fetchAll();
} catch (Exception $e) {
$clients = [];
}
$total_clients = count($clients);
$active_clients = count(array_filter($clients, fn($c) => $c['status'] === 'active'));
$total_projects = array_sum(array_column($clients, 'total_projects'));
$total_revenue = array_sum(array_column($clients, 'total_revenue'));
include 'includes/header.php';
?>
✅
ID
Client Code
Company Name
Industry
Contact Person
Location
Email & Phone
Projects
Revenue
Status
Actions
No clients found. Click "Add New Client" to create one.
#
₹
Are you sure you want to delete client ?
This action cannot be undone.
Cancel
Delete Client
-------------------- END OF FILE --------------------
### FILE 8: config.php
- Type: PHP
- Size: 3.88 KB
- Path: .
- Name: config.php
------------------------------------------------------------
PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false,
]
);
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,
]
);
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());
}
}
?>
-------------------- END OF FILE --------------------
### FILE 9: demand.php
- Type: PHP
- Size: 10.75 KB
- Path: .
- Name: demand.php
------------------------------------------------------------
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 Index
+0.8 vs last month
🚀
%
Growth Rate
+2.3% vs Q2
🎯
Market Score
Excellent rating
🔮
%
Forecast Accuracy
+5.2% improvement
-------------------- END OF FILE --------------------
### FILE 10: error-404.html
- Type: HTML
- Size: 6.9 KB
- Path: .
- Name: error-404.html
------------------------------------------------------------
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 11: error-500.html
- Type: HTML
- Size: 8.81 KB
- Path: .
- Name: error-500.html
------------------------------------------------------------
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.
What you can try:
Refresh this page in a few minutes
Clear your browser cache and cookies
Try accessing a different page
Contact our support team if the problem persists
Need Immediate Assistance?
Our technical team is available 24/7 to help resolve system issues.
-------------------- END OF FILE --------------------
### FILE 12: finance.php
- Type: PHP
- Size: 1.28 KB
- Path: .
- Name: finance.php
------------------------------------------------------------
💰
Finance
This section is being set up. Financial management, invoicing and reporting features will be available here soon.
-------------------- END OF FILE --------------------
### FILE 13: generate_hash.php
- Type: PHP
- Size: 184 B
- Path: .
- Name: generate_hash.php
------------------------------------------------------------
php
-------------------- END OF FILE --------------------
### FILE 14: index.php
- Type: PHP
- Size: 23.24 KB
- Path: .
- Name: index.php
------------------------------------------------------------
prepare("SELECT * FROM admin_users WHERE username = ? AND status = 'active'");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
$_SESSION['admin_id'] = $user['id'];
$_SESSION['admin_username'] = $user['username'];
$_SESSION['admin_email'] = $user['email'];
$_SESSION['admin_role'] = $user['role'];
$_SESSION['admin_name'] = $user['full_name'];
$updateStmt = $pdo->prepare("UPDATE admin_users SET last_login = NOW() WHERE id = ?");
$updateStmt->execute([$user['id']]);
logActivity($user['id'], 'login', 'User logged in successfully');
header('Location: index.php');
exit;
} else {
$error = 'Invalid username or password';
}
} catch (Exception $e) {
error_log("Login error: " . $e->getMessage());
$error = 'An error occurred. Please try again.';
}
}
}
// ============ SHOW LOGIN FORM ============
?>
Login - Relevant Reflex Admin
query("SELECT COUNT(*) as total FROM users");
$total_members = $memberStmt->fetchColumn();
// Active members
$activeStmt = $panelPdo->query("SELECT COUNT(*) as total FROM users WHERE status = 'active'");
$active_members = $activeStmt->fetchColumn();
// Email verified members
$verifiedStmt = $panelPdo->query("SELECT COUNT(*) as total FROM users WHERE email_verified = 1");
$verified_members = $verifiedStmt->fetchColumn();
// Total points in circulation
$pointsStmt = $panelPdo->query("SELECT SUM(points) as total FROM user_points");
$total_points = $pointsStmt->fetchColumn() ?? 0;
// Total points earned (lifetime)
$earnedStmt = $panelPdo->query("SELECT SUM(total_earned) as total FROM user_points");
$total_earned = $earnedStmt->fetchColumn() ?? 0;
// Total redemptions
$redeemedStmt = $panelPdo->query("SELECT SUM(total_redeemed) as total FROM user_points");
$total_redeemed = $redeemedStmt->fetchColumn() ?? 0;
// Pending redemption requests
$pendingRedemptionsStmt = $panelPdo->query("SELECT COUNT(*) as total FROM redemption_requests WHERE status = 'pending'");
$pending_redemptions = $pendingRedemptionsStmt->fetchColumn();
// Open support tickets
$openTicketsStmt = $panelPdo->query("SELECT COUNT(*) as total FROM support_tickets WHERE status IN ('open', 'pending')");
$open_tickets = $openTicketsStmt->fetchColumn();
// Admin users
$adminStmt = $pdo->query("SELECT COUNT(*) as total FROM admin_users WHERE status = 'active'");
$admin_count = $adminStmt->fetchColumn();
// Affiliates
$affiliateStmt = $pdo->query("SELECT COUNT(*) as total FROM affiliates WHERE status = 'active'");
$affiliate_count = $affiliateStmt->fetchColumn();
// Recent members (last 7 days)
$recentStmt = $panelPdo->query("SELECT COUNT(*) as total FROM users WHERE created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)");
$recent_members = $recentStmt->fetchColumn();
// Recent activities
$activitiesStmt = $pdo->query("
SELECT al.*, au.full_name, au.username
FROM admin_activity_log al
LEFT JOIN admin_users au ON al.admin_id = au.id
ORDER BY al.created_at DESC
LIMIT 10
");
$recent_activities = $activitiesStmt->fetchAll();
} catch (Exception $e) {
error_log("Dashboard error: " . $e->getMessage());
$total_members = $active_members = $verified_members = 0;
$total_points = $total_earned = $total_redeemed = 0;
$pending_redemptions = $open_tickets = 0;
$admin_count = $affiliate_count = $recent_members = 0;
$recent_activities = [];
}
include 'includes/header.php';
?>
👋 Welcome back, !
Here's what's happening with your panel today
📅
⏰
👤
Total Members
0): ?>
↑ + this week
No new members
Active Members
0 ? round(($active_members / $total_members) * 100) : 0; ?>% of total
Points in Circulation
Available balance
Pending Redemptions
0): ?>
⚠ Needs attention
✓ All processed
Open Support Tickets
0): ?>
⚠ Requires response
✓ All resolved
Active Affiliates
Partner network
-------------------- END OF FILE --------------------
### FILE 15: login.php
- Type: PHP
- Size: 3.92 KB
- Path: .
- Name: login.php
------------------------------------------------------------
prepare("SELECT * FROM admin_users WHERE username = ? AND status = 'active'");
$stmt->execute([$username]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password'])) {
// Set session
$_SESSION['admin_id'] = $user['id'];
$_SESSION['admin_username'] = $user['username'];
$_SESSION['admin_email'] = $user['email'];
$_SESSION['admin_role'] = $user['role'];
$_SESSION['admin_name'] = $user['full_name'];
// Update last login
$updateStmt = $pdo->prepare("UPDATE admin_users SET last_login = NOW() WHERE id = ?");
$updateStmt->execute([$user['id']]);
// Log activity
logActivity($user['id'], 'login', 'User logged in successfully');
header('Location: index.php');
exit;
} else {
$error = 'Invalid username or password';
}
} catch (Exception $e) {
error_log("Login error: " . $e->getMessage());
$error = 'An error occurred. Please try again.';
}
}
}
?>
Login - Relevant Reflex
-------------------- END OF FILE --------------------
### FILE 16: logout.php
- Type: PHP
- Size: 185 B
- Path: .
- Name: logout.php
------------------------------------------------------------
-------------------- END OF FILE --------------------
### FILE 17: maintenance.html
- Type: HTML
- Size: 0 B
- Path: .
- Name: maintenance.html
------------------------------------------------------------
-------------------- END OF FILE --------------------
### FILE 18: member_edit.php
- Type: PHP
- Size: 13.96 KB
- Path: .
- Name: member_edit.php
------------------------------------------------------------
prepare("
UPDATE users
SET gender = ?, date_of_birth = ?, postcode = ?, status = ?, updated_at = NOW()
WHERE id = ?
");
$stmt->execute([$gender, $dob, $postcode, $status, $member_id]);
logActivity($_SESSION['admin_id'], 'update_member', "Updated member #$member_id basic info", 'member', $member_id);
$success = 'Member information updated successfully!';
} catch (Exception $e) {
$error = 'An error occurred. Please try again.';
error_log("Update member error: " . $e->getMessage());
}
}
}
if ($_POST['action'] === 'update_mobile') {
$mobile = trim($_POST['mobile_number'] ?? '');
if (empty($mobile)) {
$error = 'Mobile number is required';
} else {
try {
$panelPdo = getPanelDBConnection();
// Check if mobile verification record exists
$checkStmt = $panelPdo->prepare("SELECT id FROM mobile_verifications WHERE user_id = ?");
$checkStmt->execute([$member_id]);
$exists = $checkStmt->fetch();
if ($exists) {
$stmt = $panelPdo->prepare("
UPDATE mobile_verifications
SET mobile_number = ?, updated_at = NOW()
WHERE user_id = ?
");
$stmt->execute([$mobile, $member_id]);
} else {
$stmt = $panelPdo->prepare("
INSERT INTO mobile_verifications (user_id, mobile_number)
VALUES (?, ?)
");
$stmt->execute([$member_id, $mobile]);
}
logActivity($_SESSION['admin_id'], 'update_member', "Updated member #$member_id mobile number", 'member', $member_id);
$success = 'Mobile number updated successfully!';
} catch (Exception $e) {
$error = 'An error occurred. Please try again.';
error_log("Update mobile error: " . $e->getMessage());
}
}
}
}
// Fetch member details
try {
$panelPdo = getPanelDBConnection();
$stmt = $panelPdo->prepare("
SELECT
u.*,
mv.mobile_number,
mv.is_verified as mobile_verified
FROM users u
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: members.php');
exit;
}
} catch (Exception $e) {
error_log("Fetch member error: " . $e->getMessage());
header('Location: members.php');
exit;
}
include 'includes/header.php';
?>
✅
⚠
Email Verified
✓ Yes' : '✗ No '; ?>
Onboarding Completed
✓ Yes' : '⏳ No '; ?>
-------------------- END OF FILE --------------------
### FILE 19: member_view.php
- Type: PHP
- Size: 30.18 KB
- Path: .
- Name: member_view.php
------------------------------------------------------------
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 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();
} catch (Exception $e) {
error_log("Member view error: " . $e->getMessage());
header('Location: panel.php');
exit;
}
include 'includes/header.php';
?>
💰
Current Points
Available Balance
📈
Total Earned
Lifetime Earnings
💳
Total Redeemed
Lifetime Redemptions
📧 Email Address
✓ Verified
✗ Not Verified
🎂 Date of Birth
diff($dob)->y;
echo $dob->format('F j, Y');
?>
years old
📱 Mobile Number
✓ Verified
✅ Email Verified
✓ Yes' : '✗ No '; ?>
🎯 Onboarding Status
✓ Completed
⏳ Pending
of questions answered
• Completed on
📋
No profiler data available
💎
No transaction history available
Request ID
Points
Amount (₹)
UPI ID
Status
Requested
Processed
₹
Pending';
?>
🎁
No redemption requests found
🎫
No support tickets found
-------------------- END OF FILE --------------------
### FILE 20: members.php
- Type: PHP
- Size: 21.92 KB
- Path: .
- Name: members.php
------------------------------------------------------------
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
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
ORDER BY u.created_at DESC
");
$members = $stmt->fetchAll();
} catch (Exception $e) {
$members = [];
error_log("Fetch members error: " . $e->getMessage());
}
include 'includes/header.php';
?>
✅
⚠
$m['status'] === 'active')); ?>
Active Members
$m['email_verified'] == 1)); ?>
Email Verified
$m['mobile_verified'] == 1)); ?>
Mobile Verified
ID
Email
Gender
Date of Birth
Postcode
Points
Status
Joined
Actions
#
diff($dob)->y;
?>
format('M j, Y'); ?>
years old
Are you sure you want to delete member ?
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
Cancel
Delete Member
-------------------- END OF FILE --------------------
### FILE 21: panel.php
- Type: PHP
- Size: 22 KB
- Path: .
- Name: panel.php
------------------------------------------------------------
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.';
}
}
}
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
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
ORDER BY u.created_at DESC
");
$members = $stmt->fetchAll();
} catch (Exception $e) {
$members = [];
error_log("Fetch members error: " . $e->getMessage());
}
include 'includes/header.php';
?>
✅
⚠
$m['status'] === 'active')); ?>
Active Members
$m['email_verified'] == 1)); ?>
Email Verified
$m['mobile_verified'] == 1)); ?>
Mobile Verified
$m['onboarding_completed'] == 1)); ?>
Onboarded
ID
Email
Gender
Date of Birth
Postcode
Points
Status
Joined
Actions
#
diff($dob)->y;
?>
format('M j, Y'); ?>
years old
Are you sure you want to delete member ?
This action cannot be undone and will permanently delete all member data.
Cancel
Delete Member
-------------------- END OF FILE --------------------
### FILE 22: README.md
- Type: MD
- Size: 8.86 KB
- Path: .
- Name: README.md
------------------------------------------------------------
# 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 23: robots.txt
- Type: TXT
- Size: 2.16 KB
- Path: .
- Name: robots.txt
------------------------------------------------------------
# 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 24: settings.php
- Type: PHP
- Size: 28.4 KB
- Path: .
- Name: settings.php
------------------------------------------------------------
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'
];
$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';
?>
✅
⚠
📋
No Company Details Saved Yet
Add your company information for invoicing by switching to Edit mode.
✏ Add Company Details
🏢 Company Information
Basic company details that appear on invoices and official documents.
📋 Tax & Registration
Tax identification numbers and registration details.
CIN / Registration Number
💳 Bank Details
Banking information displayed on invoices for client payments.
📄 Invoice Preferences
Default text and formatting for generated invoices.
🏢 Company Information
Basic company details that appear on invoices and official documents.
📋 Tax & Registration
Tax identification numbers and registration details for invoicing compliance.
💳 Bank Details
Banking information displayed on invoices for client payments.
📄 Invoice Preferences
Default text and formatting preferences for generated invoices.
-------------------- END OF FILE --------------------
### FILE 25: sitemap.xml
- Type: XML
- Size: 5.24 KB
- Path: .
- Name: sitemap.xml
------------------------------------------------------------
https://yourdomain.com/
2025-09-03
daily
1.0
https://yourdomain.com/users.php
2025-09-03
weekly
0.8
https://yourdomain.com/panel.php
2025-09-03
weekly
0.9
https://yourdomain.com/supply.php
2025-09-03
weekly
0.7
https://yourdomain.com/demand.php
2025-09-03
monthly
0.7
https://yourdomain.com/finance.php
2025-09-03
weekly
0.8
https://yourdomain.com/support.php
2025-09-03
monthly
0.6
https://yourdomain.com/settings.php
2025-09-03
monthly
0.5
-------------------- END OF FILE --------------------
### FILE 26: supply.php
- Type: PHP
- Size: 21.02 KB
- Path: .
- Name: supply.php
------------------------------------------------------------
prepare("SELECT affiliate_code FROM affiliates WHERE id = ?");
$stmt->execute([$delete_id]);
$affiliate_code = $stmt->fetchColumn();
// Delete affiliate (cascade will delete attachments and signups)
$stmt = $pdo->prepare("DELETE FROM affiliates WHERE id = ?");
$stmt->execute([$delete_id]);
logActivity($_SESSION['admin_id'], 'delete_affiliate', "Deleted affiliate: $affiliate_code", 'affiliate', $delete_id);
$success = 'Affiliate deleted successfully!';
} catch (Exception $e) {
$error = 'An error occurred while deleting. Please try again.';
error_log("Delete affiliate error: " . $e->getMessage());
}
}
}
}
// Fetch all affiliates
try {
$pdo = getDBConnection();
$stmt = $pdo->query("SELECT * FROM affiliates ORDER BY created_at DESC");
$affiliates = $stmt->fetchAll();
} catch (Exception $e) {
$affiliates = [];
error_log("Fetch affiliates error: " . $e->getMessage());
}
// Calculate statistics
$total_affiliates = count($affiliates);
$active_affiliates = count(array_filter($affiliates, fn($a) => $a['status'] === 'active'));
$total_signups = array_sum(array_column($affiliates, 'total_signups'));
$verified_signups = array_sum(array_column($affiliates, 'total_verified_signups'));
$company_affiliates = count(array_filter($affiliates, fn($a) => $a['type'] === 'company'));
$individual_affiliates = count(array_filter($affiliates, fn($a) => $a['type'] === 'individual'));
include 'includes/header.php';
?>
✅
⚠
ID
Affiliate Code
Company/Name
Type
In-charge
Location
Contact
Signups
Verified
Status
Actions
No affiliates found. Click "Add New Affiliate" to create one.
#
-
Are you sure you want to delete affiliate ?
This action cannot be undone and will delete all associated data including signups and attachments.
Cancel
Delete Affiliate
-------------------- END OF FILE --------------------
### FILE 27: support.php
- Type: PHP
- Size: 2.21 KB
- Path: .
- Name: support.php
------------------------------------------------------------
🛠
Support Center
Our support portal is available on the main Relevant Reflex website.
Submit tickets, track requests, and access help resources all in one place.
Open Support Portal →
Opens in a new window at relevantreflex.com
-------------------- END OF FILE --------------------
### FILE 28: users.php
- Type: PHP
- Size: 23.18 KB
- Path: .
- Name: users.php
------------------------------------------------------------
prepare("INSERT INTO admin_users (username, email, password, full_name, role, created_by) VALUES (?, ?, ?, ?, ?, ?)");
$stmt->execute([$username, $email, $hashedPassword, $full_name, $role, $_SESSION['admin_id']]);
logActivity($_SESSION['admin_id'], 'create_admin_user', "Created admin user: $username", 'admin_user', $pdo->lastInsertId());
$success = 'Admin user created successfully!';
} catch (PDOException $e) {
if ($e->getCode() == 23000) {
$error = 'Username or email already exists';
} else {
$error = 'An error occurred. Please try again.';
error_log("Create user error: " . $e->getMessage());
}
}
}
}
if ($_POST['action'] === 'toggle_status') {
$user_id = intval($_POST['user_id'] ?? 0);
$new_status = $_POST['new_status'] ?? 'active';
try {
$pdo = getDBConnection();
$stmt = $pdo->prepare("UPDATE admin_users SET status = ? WHERE id = ?");
$stmt->execute([$new_status, $user_id]);
logActivity($_SESSION['admin_id'], 'update_admin_user', "Changed user status to: $new_status", 'admin_user', $user_id);
$success = 'User status updated successfully!';
} catch (Exception $e) {
$error = 'An error occurred. Please try again.';
error_log("Status update error: " . $e->getMessage());
}
}
if ($_POST['action'] === 'delete_user' && isAdmin()) {
$user_id = intval($_POST['user_id'] ?? 0);
if ($user_id > 0 && $user_id != $_SESSION['admin_id']) {
try {
$pdo = getDBConnection();
// Get username for logging
$stmt = $pdo->prepare("SELECT username FROM admin_users WHERE id = ?");
$stmt->execute([$user_id]);
$username = $stmt->fetchColumn();
// Delete user
$stmt = $pdo->prepare("DELETE FROM admin_users WHERE id = ?");
$stmt->execute([$user_id]);
logActivity($_SESSION['admin_id'], 'delete_admin_user', "Deleted admin user: $username", 'admin_user', $user_id);
$success = 'Admin user deleted successfully!';
} catch (Exception $e) {
$error = 'An error occurred while deleting. Please try again.';
error_log("Delete user error: " . $e->getMessage());
}
}
}
}
// Fetch all users
try {
$pdo = getDBConnection();
$stmt = $pdo->query("SELECT * FROM admin_users ORDER BY created_at DESC");
$users = $stmt->fetchAll();
} catch (Exception $e) {
$users = [];
error_log("Fetch users error: " . $e->getMessage());
}
include 'includes/header.php';
?>
✅
⚠
$u['status'] === 'active')); ?>
Active Users
$u['role'] === 'admin')); ?>
Admins
$u['role'] === 'manager')); ?>
Managers
ID
User Details
Email
Role
Status
Last Login
Actions
#
Never
🗑
Current User
Are you sure you want to delete user ?
This action cannot be undone.
Cancel
Delete User
-------------------- END OF FILE --------------------
### FILE 29: assets/css/dashboard.css
- Type: CSS
- Size: 28.48 KB
- Path: assets/css
- Name: dashboard.css
------------------------------------------------------------
/* Dashboard Specific Styles for Relevant Reflex */
/* Dashboard Container */
.dashboard-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing-md);
}
/* Enhanced Stats Cards for Dashboard */
.dashboard-content .stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--spacing-xl);
margin-bottom: var(--spacing-2xl);
}
.dashboard-content .stat-card {
background: linear-gradient(135deg, var(--white) 0%, #f8fafc 100%);
border: 1px solid var(--gray-100);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
gap: var(--spacing-xl);
transition: all var(--transition-normal);
position: relative;
overflow: hidden;
}
.dashboard-content .stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--primary-color), var(--primary-dark));
}
.dashboard-content .stat-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-lg);
border-color: var(--primary-color);
}
.dashboard-content .stat-icon {
font-size: 3rem;
width: 70px;
height: 70px;
background: linear-gradient(135deg, var(--primary-light), rgba(5, 150, 105, 0.1));
border: 2px solid var(--primary-color);
border-radius: var(--border-radius-2xl);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
position: relative;
}
.dashboard-content .stat-icon::after {
content: '';
position: absolute;
inset: 2px;
background: linear-gradient(135deg, transparent, rgba(255, 255, 255, 0.1));
border-radius: calc(var(--border-radius-2xl) - 2px);
pointer-events: none;
}
.dashboard-content .stat-content h3 {
font-size: var(--font-size-4xl);
margin: 0;
color: var(--primary-color);
font-weight: var(--font-weight-bold);
line-height: 1;
}
.dashboard-content .stat-content p {
margin: var(--spacing-sm) 0 0;
color: var(--gray-600);
font-weight: var(--font-weight-semibold);
font-size: var(--font-size-base);
}
.stat-change {
display: inline-flex;
align-items: center;
gap: var(--spacing-xs);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
margin-top: var(--spacing-sm);
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-xl);
}
.stat-change.positive {
background: var(--success-light);
color: var(--success-color);
}
.stat-change.negative {
background: var(--danger-light);
color: var(--danger-color);
}
.stat-change.neutral {
background: var(--gray-100);
color: var(--gray-600);
}
.stat-change::before {
content: '↗';
font-size: var(--font-size-sm);
}
.stat-change.negative::before {
content: '↘';
}
.stat-change.neutral::before {
content: '→';
}
/* Dashboard Content Grid */
.dashboard-content {
margin-top: var(--spacing-2xl);
}
.content-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: var(--spacing-2xl);
margin-bottom: var(--spacing-2xl);
}
/* Chart Section */
.chart-section {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-xl);
padding-bottom: var(--spacing-lg);
border-bottom: 1px solid var(--gray-200);
}
.section-header h2 {
margin: 0;
color: var(--dark-color);
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
}
.chart-controls select {
padding: var(--spacing-sm) var(--spacing-md);
border: 1px solid var(--gray-300);
border-radius: var(--border-radius-lg);
background: var(--white);
font-size: var(--font-size-sm);
cursor: pointer;
}
.chart-container {
position: relative;
height: 300px;
background: var(--gray-50);
border-radius: var(--border-radius-lg);
display: flex;
align-items: center;
justify-content: center;
border: 1px dashed var(--gray-300);
}
.chart-placeholder {
color: var(--gray-500);
font-style: italic;
text-align: center;
}
.chart-legend {
display: flex;
gap: var(--spacing-lg);
margin-top: var(--spacing-lg);
justify-content: center;
}
.legend-item {
display: flex;
align-items: center;
gap: var(--spacing-sm);
font-size: var(--font-size-sm);
}
.legend-color {
width: 16px;
height: 16px;
border-radius: var(--border-radius-sm);
}
/* Recent Activity */
.recent-activity {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.view-all {
color: var(--primary-color);
text-decoration: none;
font-weight: var(--font-weight-medium);
font-size: var(--font-size-sm);
transition: color var(--transition-normal);
}
.view-all:hover {
color: var(--primary-hover);
text-decoration: underline;
}
.activity-list {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
.activity-item {
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
padding: var(--spacing-lg);
background: linear-gradient(135deg, var(--gray-50), #f1f5f9);
border-radius: var(--border-radius-lg);
border-left: 4px solid var(--primary-color);
transition: all var(--transition-normal);
position: relative;
}
.activity-item:hover {
background: linear-gradient(135deg, var(--primary-light), rgba(5, 150, 105, 0.05));
transform: translateX(4px);
box-shadow: var(--shadow-sm);
}
.activity-time {
font-size: var(--font-size-xs);
color: var(--gray-500);
font-weight: var(--font-weight-medium);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.activity-text {
color: var(--dark-color);
font-weight: var(--font-weight-normal);
line-height: 1.5;
}
.activity-footer {
margin-top: var(--spacing-lg);
text-align: center;
padding-top: var(--spacing-lg);
border-top: 1px solid var(--gray-200);
}
/* Quick Actions Section */
.quick-actions-section {
margin-top: var(--spacing-2xl);
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.quick-actions-section h2 {
margin-bottom: var(--spacing-xl);
color: var(--dark-color);
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
text-align: center;
}
.quick-actions-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-lg);
}
.quick-action-card {
background: linear-gradient(135deg, var(--white) 0%, var(--gray-50) 100%);
border: 1px solid var(--gray-200);
border-radius: var(--border-radius-xl);
padding: var(--spacing-xl);
text-align: center;
cursor: pointer;
transition: all var(--transition-normal);
text-decoration: none;
color: inherit;
}
.quick-action-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
border-color: var(--primary-color);
background: linear-gradient(135deg, var(--primary-light) 0%, rgba(5, 150, 105, 0.05) 100%);
}
.action-icon {
font-size: 2.5rem;
margin-bottom: var(--spacing-lg);
display: block;
}
.quick-action-card h3 {
margin: 0 0 var(--spacing-md);
color: var(--dark-color);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
}
.quick-action-card p {
margin: 0;
color: var(--gray-600);
font-size: var(--font-size-sm);
line-height: 1.5;
}
/* Overview Cards for Other Pages */
.overview-grid,
.user-stats .stats-row,
.supply-overview .stats-grid,
.panel-overview .overview-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--spacing-lg);
margin-bottom: var(--spacing-2xl);
}
.overview-card,
.stat-item {
background: var(--white);
border-radius: var(--border-radius-xl);
padding: var(--spacing-lg);
text-align: center;
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
transition: all var(--transition-normal);
}
.overview-card:hover,
.stat-item:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.overview-icon {
font-size: 2rem;
margin-bottom: var(--spacing-md);
display: block;
}
.overview-content h3,
.stat-number {
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
color: var(--primary-color);
margin: 0 0 var(--spacing-sm);
}
.overview-content p,
.stat-label {
color: var(--gray-600);
font-weight: var(--font-weight-medium);
margin: 0;
}
/* Login Information Styling */
.login-info {
display: flex;
flex-direction: column;
gap: 2px;
}
.login-date {
font-weight: var(--font-weight-medium);
color: var(--dark-color);
font-size: var(--font-size-sm);
}
.login-time {
font-size: var(--font-size-xs);
color: var(--gray-500);
}
.never-logged {
font-style: italic;
color: var(--gray-400);
font-size: var(--font-size-sm);
}
/* Response Information */
.response-info {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.response-info strong {
color: var(--primary-color);
font-size: var(--font-size-lg);
}
.response-info small {
color: var(--gray-500);
font-size: var(--font-size-xs);
}
/* Date Information */
.date-info {
display: flex;
flex-direction: column;
gap: var(--spacing-xs);
}
.end-date {
font-weight: var(--font-weight-medium);
color: var(--dark-color);
}
.days-left {
font-size: var(--font-size-xs);
color: var(--gray-500);
}
/* Quantity Badge */
.quantity-badge {
background: var(--primary-light);
color: var(--primary-color);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-xl);
font-weight: var(--font-weight-bold);
font-size: var(--font-size-sm);
}
/* Financial Cards */
.finance-card {
position: relative;
overflow: hidden;
}
.finance-card.revenue-card::before {
background: linear-gradient(90deg, var(--success-color), #20c997);
}
.finance-card.expense-card::before {
background: linear-gradient(90deg, var(--danger-color), #e74c3c);
}
.finance-card.profit-card::before {
background: linear-gradient(90deg, var(--primary-color), var(--primary-dark));
}
.stat-footer {
font-size: var(--font-size-xs);
color: var(--gray-500);
margin-top: var(--spacing-sm);
text-align: center;
font-weight: var(--font-weight-medium);
}
/* Trend Indicators */
.trend {
display: inline-flex;
align-items: center;
gap: var(--spacing-xs);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-bold);
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-xl);
margin-top: var(--spacing-sm);
}
.trend-up {
background: var(--success-light);
color: var(--success-color);
}
.trend-down {
background: var(--danger-light);
color: var(--danger-color);
}
.trend-up::before {
content: '↗';
}
.trend-down::before {
content: '↘';
}
/* Demand Metrics */
.demand-metrics {
margin-bottom: var(--spacing-2xl);
}
.metrics-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: var(--spacing-lg);
}
.metric-card {
background: var(--white);
border-radius: var(--border-radius-xl);
padding: var(--spacing-xl);
text-align: center;
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
transition: all var(--transition-normal);
}
.metric-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.metric-icon {
font-size: 2.5rem;
margin-bottom: var(--spacing-lg);
display: block;
}
.metric-value {
font-size: var(--font-size-4xl);
font-weight: var(--font-weight-bold);
color: var(--primary-color);
line-height: 1;
margin-bottom: var(--spacing-sm);
}
.metric-label {
color: var(--gray-600);
font-weight: var(--font-weight-medium);
margin-bottom: var(--spacing-sm);
}
.metric-change {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-xl);
}
/* Category Performance */
.category-performance {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.category-list {
display: flex;
flex-direction: column;
gap: var(--spacing-lg);
}
.category-item {
border: 1px solid var(--gray-200);
border-radius: var(--border-radius-lg);
padding: var(--spacing-lg);
transition: all var(--transition-normal);
}
.category-item:hover {
border-color: var(--primary-color);
background: var(--primary-light);
}
.category-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-md);
}
.category-header h4 {
margin: 0;
color: var(--dark-color);
font-weight: var(--font-weight-semibold);
}
.category-metrics {
display: flex;
justify-content: space-between;
align-items: center;
}
.demand-level {
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-xl);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-bold);
}
.demand-high {
background: var(--success-light);
color: var(--success-color);
}
.demand-medium {
background: var(--warning-light);
color: #856404;
}
.demand-low {
background: var(--gray-200);
color: var(--gray-600);
}
.category-growth {
font-weight: var(--font-weight-bold);
}
.category-growth.positive {
color: var(--success-color);
}
.category-growth.negative {
color: var(--danger-color);
}
/* Insights Sections */
.insights-section {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
margin-top: var(--spacing-2xl);
}
.insights-section h2 {
margin-bottom: var(--spacing-xl);
color: var(--dark-color);
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
text-align: center;
}
.insights-grid,
.insights-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: var(--spacing-lg);
}
.insight-card {
background: linear-gradient(135deg, var(--gray-50) 0%, #f8fafc 100%);
border: 1px solid var(--gray-200);
border-radius: var(--border-radius-xl);
padding: var(--spacing-xl);
transition: all var(--transition-normal);
}
.insight-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
border-color: var(--primary-color);
}
.insight-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-lg);
}
.insight-number {
width: 30px;
height: 30px;
background: var(--primary-color);
color: var(--white);
border-radius: var(--border-radius-full);
display: flex;
align-items: center;
justify-content: center;
font-weight: var(--font-weight-bold);
font-size: var(--font-size-sm);
}
.insight-impact {
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-xl);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-bold);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.impact-high {
background: var(--danger-light);
color: var(--danger-color);
}
.impact-medium {
background: var(--warning-light);
color: #856404;
}
.impact-low {
background: var(--info-light);
color: var(--info-color);
}
.insight-content h4 {
margin: 0 0 var(--spacing-md);
color: var(--dark-color);
font-weight: var(--font-weight-semibold);
line-height: 1.3;
}
.insight-content p {
margin: 0 0 var(--spacing-md);
color: var(--gray-600);
line-height: 1.6;
}
.recommended-action {
margin: 0 !important;
padding: var(--spacing-md);
background: var(--primary-light);
border-radius: var(--border-radius-lg);
border-left: 4px solid var(--primary-color);
font-size: var(--font-size-sm);
}
.recommended-action strong {
color: var(--primary-color);
}
/* Transaction Items */
.transactions-section {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.transactions-list {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
.transaction-item {
display: flex;
align-items: center;
gap: var(--spacing-lg);
padding: var(--spacing-lg);
border-radius: var(--border-radius-lg);
border: 1px solid var(--gray-200);
transition: all var(--transition-normal);
}
.transaction-item:hover {
border-color: var(--primary-color);
background: var(--primary-light);
}
.transaction-item.revenue {
border-left: 4px solid var(--success-color);
}
.transaction-item.expense {
border-left: 4px solid var(--danger-color);
}
.transaction-icon {
font-size: 1.5rem;
width: 40px;
height: 40px;
border-radius: var(--border-radius-full);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.transaction-item.revenue .transaction-icon {
background: var(--success-light);
}
.transaction-item.expense .transaction-icon {
background: var(--danger-light);
}
.transaction-details {
flex: 1;
}
.transaction-description {
font-weight: var(--font-weight-medium);
color: var(--dark-color);
margin-bottom: var(--spacing-xs);
}
.transaction-meta {
display: flex;
gap: var(--spacing-lg);
font-size: var(--font-size-sm);
color: var(--gray-500);
}
.transaction-amount {
font-size: var(--font-size-lg);
font-weight: var(--font-weight-bold);
}
.transaction-amount.positive {
color: var(--success-color);
}
.transaction-amount.negative {
color: var(--danger-color);
}
/* Support Specific Styles */
.support-overview {
margin-bottom: var(--spacing-2xl);
}
.support-card .stat-icon {
background: linear-gradient(135deg, var(--info-light), rgba(23, 162, 184, 0.1));
border: 2px solid var(--info-color);
}
.help-resources {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-lg);
}
.help-item {
display: flex;
align-items: center;
gap: var(--spacing-lg);
padding: var(--spacing-xl);
background: var(--white);
border: 1px solid var(--gray-200);
border-radius: var(--border-radius-xl);
text-decoration: none;
color: inherit;
transition: all var(--transition-normal);
}
.help-item:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
border-color: var(--primary-color);
background: var(--primary-light);
}
.help-icon {
font-size: 2rem;
width: 50px;
height: 50px;
background: var(--primary-light);
border-radius: var(--border-radius-xl);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.help-content h4 {
margin: 0 0 var(--spacing-xs);
color: var(--dark-color);
font-weight: var(--font-weight-semibold);
}
.help-content p {
margin: 0;
color: var(--gray-600);
font-size: var(--font-size-sm);
}
/* Tickets Section */
.tickets-section {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
margin-top: var(--spacing-2xl);
}
.tickets-container {
display: flex;
flex-direction: column;
gap: var(--spacing-lg);
}
.ticket-item {
border: 1px solid var(--gray-200);
border-radius: var(--border-radius-lg);
padding: var(--spacing-lg);
transition: all var(--transition-normal);
}
.ticket-item:hover {
border-color: var(--primary-color);
background: var(--primary-light);
}
.ticket-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-md);
}
.ticket-id {
font-family: var(--font-family-mono);
font-weight: var(--font-weight-bold);
color: var(--primary-color);
background: var(--primary-light);
padding: var(--spacing-xs) var(--spacing-md);
border-radius: var(--border-radius-lg);
}
.ticket-content h4 {
margin: 0 0 var(--spacing-md);
color: var(--dark-color);
font-weight: var(--font-weight-medium);
}
.ticket-meta {
display: flex;
gap: var(--spacing-lg);
align-items: center;
margin-bottom: var(--spacing-md);
}
.ticket-date {
font-size: var(--font-size-sm);
color: var(--gray-500);
}
/* FAQ Section */
.faq-section {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
margin-top: var(--spacing-2xl);
}
.faq-container {
display: flex;
flex-direction: column;
gap: var(--spacing-md);
}
.faq-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--spacing-lg);
border: 1px solid var(--gray-200);
border-radius: var(--border-radius-lg);
transition: all var(--transition-normal);
}
.faq-item:hover {
border-color: var(--primary-color);
background: var(--primary-light);
}
.faq-question h4 {
margin: 0 0 var(--spacing-xs);
color: var(--dark-color);
font-weight: var(--font-weight-medium);
}
.faq-category {
font-size: var(--font-size-xs);
color: var(--primary-color);
font-weight: var(--font-weight-bold);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.faq-toggle {
background: var(--primary-color);
color: var(--white);
border: none;
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius-lg);
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
cursor: pointer;
transition: all var(--transition-normal);
flex-shrink: 0;
}
.faq-toggle:hover {
background: var(--primary-hover);
transform: scale(1.05);
}
/* Settings Specific Styles */
.settings-section {
background: var(--white);
}
.settings-section h2 {
margin-bottom: var(--spacing-xl);
color: var(--dark-color);
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
padding-bottom: var(--spacing-lg);
border-bottom: 2px solid var(--primary-color);
}
.settings-section h3 {
margin: var(--spacing-xl) 0 var(--spacing-lg);
color: var(--gray-700);
font-size: var(--font-size-lg);
font-weight: var(--font-weight-semibold);
}
.notification-group {
background: var(--gray-50);
border-radius: var(--border-radius-lg);
padding: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
}
/* Color Picker */
.color-picker-group {
display: flex;
gap: var(--spacing-md);
align-items: center;
}
.color-picker-group input[type="color"] {
width: 60px;
height: 40px;
border-radius: var(--border-radius);
border: 1px solid var(--gray-300);
cursor: pointer;
}
.color-hex {
flex: 1;
background: var(--gray-100);
font-family: var(--font-family-mono);
}
/* Integration Cards */
.integrations-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--spacing-lg);
}
.integration-card {
background: var(--white);
border: 1px solid var(--gray-200);
border-radius: var(--border-radius-xl);
padding: var(--spacing-xl);
transition: all var(--transition-normal);
}
.integration-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
border-color: var(--primary-color);
}
.integration-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: var(--spacing-lg);
}
.integration-header h4 {
margin: 0;
color: var(--dark-color);
font-weight: var(--font-weight-semibold);
}
.integration-icon {
font-size: 1.5rem;
width: 40px;
height: 40px;
background: var(--primary-light);
border-radius: var(--border-radius-lg);
display: flex;
align-items: center;
justify-content: center;
margin-bottom: var(--spacing-md);
}
/* Account Management */
.account-section {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
margin-top: var(--spacing-2xl);
}
.account-section h2 {
margin-bottom: var(--spacing-xl);
color: var(--dark-color);
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
}
.account-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: var(--spacing-lg);
}
.account-card {
background: var(--gray-50);
border: 1px solid var(--gray-200);
border-radius: var(--border-radius-xl);
padding: var(--spacing-xl);
transition: all var(--transition-normal);
text-align: center;
}
.account-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-md);
}
.account-card.danger-card {
border-color: var(--danger-color);
background: var(--danger-light);
}
.account-card.danger-card:hover {
background: #f8d7da;
}
.account-icon {
font-size: 2rem;
margin-bottom: var(--spacing-lg);
display: block;
}
.account-content h4 {
margin: 0 0 var(--spacing-md);
color: var(--dark-color);
font-weight: var(--font-weight-semibold);
}
.account-content p {
margin: 0 0 var(--spacing-lg);
color: var(--gray-600);
font-size: var(--font-size-sm);
line-height: 1.5;
}
/* Password Strength Indicator */
.password-strength {
margin-top: var(--spacing-md);
}
.strength-bar {
height: 4px;
background: var(--gray-200);
border-radius: var(--border-radius-sm);
overflow: hidden;
margin-bottom: var(--spacing-sm);
}
.strength-fill {
height: 100%;
border-radius: var(--border-radius-sm);
transition: all var(--transition-normal);
}
.strength-fill.strength-weak {
background: var(--danger-color);
width: 25%;
}
.strength-fill.strength-fair {
background: var(--warning-color);
width: 50%;
}
.strength-fill.strength-good {
background: var(--info-color);
width: 75%;
}
.strength-fill.strength-strong {
background: var(--success-color);
width: 100%;
}
.strength-text {
font-size: var(--font-size-xs);
color: var(--gray-500);
}
/* Summary Stats */
.summary-section {
background: var(--white);
border-radius: var(--border-radius-2xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
border: 1px solid var(--gray-100);
}
.summary-stats {
display: flex;
flex-direction: column;
gap: var(--spacing-lg);
}
.summary-item {
text-align: center;
padding: var(--spacing-lg);
background: var(--gray-50);
border-radius: var(--border-radius-lg);
border: 1px solid var(--gray-200);
}
.summary-item h3 {
font-size: var(--font-size-2xl);
font-weight: var(--font-weight-bold);
color: var(--primary-color);
margin: 0 0 var(--spacing-sm);
}
.summary-item p {
margin: 0;
color: var(--gray-600);
font-weight: var(--font-weight-medium);
}
/* Responsive Adjustments for Dashboard Components */
@media (max-width: 992px) {
.content-grid {
grid-template-columns: 1fr;
}
.quick-actions-grid {
grid-template-columns: repeat(2, 1fr);
}
.metrics-grid {
grid-template-columns: repeat(2, 1fr);
}
.insights-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 576px) {
.quick-actions-grid {
grid-template-columns: 1fr;
}
.metrics-grid {
grid-template-columns: 1fr;
}
.overview-grid,
.account-grid,
.integrations-grid {
grid-template-columns: 1fr;
}
.transaction-item {
flex-direction: column;
text-align: center;
gap: var(--spacing-md);
}
.transaction-meta {
justify-content: center;
}
.help-resources {
grid-template-columns: 1fr;
}
.help-item {
flex-direction: column;
text-align: center;
gap: var(--spacing-md);
}
}
-------------------- END OF FILE --------------------
### FILE 30: assets/css/main.css
- Type: CSS
- Size: 18.91 KB
- Path: assets/css
- Name: main.css
------------------------------------------------------------
/* ===================================
CSS Variables
=================================== */
:root {
/* Colors */
--primary-color: #059669;
--primary-hover: #047857;
--secondary-color: #6c757d;
--success-color: #28a745;
--danger-color: #dc3545;
--warning-color: #ffc107;
--info-color: #17a2b8;
--light-color: #f8f9fa;
--dark-color: #212529;
/* Gray Scale */
--gray-50: #f9fafb;
--gray-100: #f3f4f6;
--gray-200: #e5e7eb;
--gray-300: #d1d5db;
--gray-400: #9ca3af;
--gray-500: #6b7280;
--gray-600: #4b5563;
--gray-700: #374151;
--gray-800: #1f2937;
--gray-900: #111827;
/* Spacing */
--spacing-xs: 4px;
--spacing-sm: 8px;
--spacing-md: 16px;
--spacing-lg: 24px;
--spacing-xl: 32px;
--spacing-2xl: 48px;
/* Font Sizes */
--font-size-xs: 12px;
--font-size-sm: 14px;
--font-size-base: 16px;
--font-size-lg: 18px;
--font-size-xl: 20px;
--font-size-2xl: 24px;
--font-size-3xl: 30px;
/* Border Radius */
--border-radius-sm: 4px;
--border-radius-md: 8px;
--border-radius-lg: 12px;
--border-radius-xl: 16px;
--border-radius-full: 9999px;
/* Shadows */
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
/* Transitions */
--transition-fast: 150ms ease-in-out;
--transition-base: 250ms ease-in-out;
--transition-slow: 350ms ease-in-out;
}
/* ===================================
Reset & Base Styles
=================================== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 16px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
font-size: var(--font-size-base);
line-height: 1.5;
color: var(--dark-color);
background: var(--gray-50);
min-height: 100vh;
}
/* ===================================
Header & Navigation
=================================== */
.main-header {
background: white;
border-bottom: 1px solid var(--gray-200);
position: sticky;
top: 0;
z-index: 100;
box-shadow: var(--shadow-sm);
}
.navbar {
padding: 0;
}
.nav-container {
max-width: 1400px;
margin: 0 auto;
padding: 0 var(--spacing-lg);
display: flex;
align-items: center;
justify-content: space-between;
min-height: 64px;
}
.nav-brand {
display: flex;
align-items: center;
gap: var(--spacing-md);
font-weight: 700;
font-size: var(--font-size-lg);
color: var(--dark-color);
}
.logo {
width: 40px;
height: 40px;
background: linear-gradient(135deg, var(--primary-color), #047857);
color: white;
border-radius: var(--border-radius-lg);
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 18px;
}
.nav-menu {
display: flex;
align-items: center;
gap: var(--spacing-sm);
flex: 1;
justify-content: flex-end;
}
.nav-link {
display: flex;
align-items: center;
gap: var(--spacing-sm);
padding: var(--spacing-sm) var(--spacing-md);
color: var(--gray-700);
text-decoration: none;
border-radius: var(--border-radius-md);
transition: all var(--transition-fast);
font-size: var(--font-size-sm);
font-weight: 500;
white-space: nowrap;
}
.nav-link:hover {
background: var(--gray-100);
color: var(--primary-color);
}
.nav-link.active {
background: var(--primary-color);
color: white;
}
.nav-icon {
font-size: 18px;
}
.mobile-toggle {
display: none;
flex-direction: column;
gap: 4px;
background: none;
border: none;
padding: var(--spacing-sm);
cursor: pointer;
}
.mobile-toggle span {
width: 24px;
height: 2px;
background: var(--dark-color);
transition: var(--transition-fast);
}
/* ===================================
Page Container & Layout
=================================== */
.page-container {
max-width: 1400px;
margin: 0 auto;
padding: var(--spacing-2xl) var(--spacing-lg);
}
.page-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: var(--spacing-2xl);
gap: var(--spacing-lg);
}
.page-header h1 {
font-size: var(--font-size-3xl);
font-weight: 700;
color: var(--dark-color);
margin-bottom: var(--spacing-xs);
}
.page-subtitle {
font-size: var(--font-size-base);
color: var(--gray-600);
margin: 0;
}
.header-actions {
display: flex;
gap: var(--spacing-md);
}
/* ===================================
Buttons
=================================== */
.btn {
display: inline-flex;
align-items: center;
gap: var(--spacing-sm);
padding: 10px 20px;
border: none;
border-radius: var(--border-radius-md);
font-size: var(--font-size-sm);
font-weight: 600;
cursor: pointer;
transition: all var(--transition-fast);
text-decoration: none;
white-space: nowrap;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-hover);
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn-secondary {
background: var(--gray-200);
color: var(--gray-700);
}
.btn-secondary:hover {
background: var(--gray-300);
}
/* ===================================
Statistics Cards
=================================== */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-lg);
margin-bottom: var(--spacing-2xl);
}
.stat-card {
background: white;
padding: var(--spacing-xl);
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
gap: var(--spacing-lg);
transition: var(--transition-base);
}
.stat-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
.stat-icon {
font-size: 48px;
opacity: 0.9;
}
.stat-content h3 {
font-size: var(--font-size-3xl);
font-weight: 700;
color: var(--dark-color);
margin-bottom: var(--spacing-xs);
}
.stat-content p {
font-size: var(--font-size-sm);
color: var(--gray-600);
margin: 0 0 var(--spacing-xs) 0;
}
.stat-change {
font-size: var(--font-size-xs);
font-weight: 600;
padding: 2px 8px;
border-radius: var(--border-radius-sm);
}
.stat-change.positive {
background: #d4edda;
color: #155724;
}
.stat-change.negative {
background: #f8d7da;
color: #721c24;
}
.stat-change.neutral {
background: var(--gray-100);
color: var(--gray-600);
}
/* ===================================
User Stats (Alternative Layout)
=================================== */
.user-stats {
background: white;
padding: var(--spacing-xl);
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow-sm);
margin-bottom: var(--spacing-2xl);
}
.stats-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: var(--spacing-xl);
}
.stat-item {
text-align: center;
}
.stat-number {
font-size: var(--font-size-3xl);
font-weight: 700;
color: var(--primary-color);
margin-bottom: var(--spacing-xs);
}
.stat-label {
font-size: var(--font-size-sm);
color: var(--gray-600);
}
/* ===================================
Table Controls (Search & Filters)
=================================== */
.table-controls {
display: flex;
gap: var(--spacing-md);
margin-bottom: var(--spacing-lg);
flex-wrap: wrap;
}
.search-container {
flex: 1;
min-width: 250px;
}
.search-input {
width: 100%;
padding: 10px 16px;
border: 1px solid var(--gray-300);
border-radius: var(--border-radius-md);
font-size: var(--font-size-sm);
transition: var(--transition-fast);
}
.search-input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.1);
}
.filter-container {
display: flex;
gap: var(--spacing-md);
flex-wrap: wrap;
}
.filter-select {
padding: 10px 16px;
border: 1px solid var(--gray-300);
border-radius: var(--border-radius-md);
font-size: var(--font-size-sm);
background: white;
cursor: pointer;
transition: var(--transition-fast);
}
.filter-select:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.1);
}
/* ===================================
Tables - FIXED LAYOUT
=================================== */
.table-container {
background: white;
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow-sm);
overflow-x: auto;
overflow-y: visible;
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: var(--font-size-sm);
table-layout: fixed;
min-width: 100%;
}
.data-table thead {
background: var(--gray-50);
border-bottom: 2px solid var(--gray-200);
}
.data-table th {
padding: 16px 12px;
text-align: left;
font-weight: 600;
color: var(--gray-700);
text-transform: uppercase;
font-size: var(--font-size-xs);
letter-spacing: 0.5px;
white-space: nowrap;
position: sticky;
top: 0;
background: var(--gray-50);
z-index: 10;
}
.data-table td {
padding: 16px 12px;
border-bottom: 1px solid var(--gray-100);
vertical-align: middle;
overflow: hidden;
text-overflow: ellipsis;
}
.data-table tbody tr {
transition: var(--transition-fast);
}
.data-table tbody tr:hover {
background: var(--gray-50);
}
.data-table tbody tr:last-child td {
border-bottom: none;
}
/* ===================================
Badges
=================================== */
.role-badge,
.status-badge {
display: inline-block;
padding: 4px 12px;
border-radius: var(--border-radius-full);
font-size: var(--font-size-xs);
font-weight: 600;
text-transform: uppercase;
white-space: nowrap;
}
/* Role Badges */
.role-badge.role-admin {
background: #e3f2fd;
color: #1976d2;
}
.role-badge.role-manager {
background: #f3e5f5;
color: #7b1fa2;
}
/* Status Badges */
.status-badge.status-active {
background: #d4edda;
color: #155724;
}
.status-badge.status-inactive {
background: #f8d7da;
color: #721c24;
}
.status-badge.status-suspended {
background: #fff3cd;
color: #856404;
}
.status-badge.status-open {
background: #d1ecf1;
color: #0c5460;
}
.status-badge.status-pending {
background: #fff3cd;
color: #856404;
}
.status-badge.status-resolved,
.status-badge.status-completed {
background: #d4edda;
color: #155724;
}
.status-badge.status-closed {
background: var(--gray-200);
color: var(--gray-700);
}
/* ===================================
Messages
=================================== */
.success-message,
.error-message {
padding: 16px 20px;
border-radius: var(--border-radius-md);
margin-bottom: var(--spacing-lg);
display: flex;
align-items: center;
gap: var(--spacing-md);
font-size: var(--font-size-sm);
}
.success-message {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error-message {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.message-icon {
font-size: 20px;
}
/* ===================================
Forms
=================================== */
.form-group {
margin-bottom: var(--spacing-lg);
}
.form-group label {
display: block;
margin-bottom: var(--spacing-sm);
color: var(--gray-700);
font-weight: 600;
font-size: var(--font-size-sm);
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 10px 16px;
border: 1px solid var(--gray-300);
border-radius: var(--border-radius-md);
font-size: var(--font-size-sm);
transition: var(--transition-fast);
font-family: inherit;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.1);
}
.form-group input:disabled,
.form-group select:disabled {
background: var(--gray-100);
cursor: not-allowed;
}
.form-help {
display: block;
margin-top: var(--spacing-xs);
font-size: var(--font-size-xs);
color: var(--gray-600);
}
.form-row {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--spacing-lg);
}
.form-actions {
display: flex;
gap: var(--spacing-md);
margin-top: var(--spacing-xl);
justify-content: flex-end;
}
/* ===================================
Modal
=================================== */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 1000;
align-items: center;
justify-content: center;
padding: var(--spacing-lg);
}
.modal-content {
background: white;
border-radius: var(--border-radius-xl);
padding: var(--spacing-2xl);
max-width: 500px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
box-shadow: var(--shadow-xl);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--spacing-xl);
}
.modal-header h2 {
font-size: var(--font-size-2xl);
color: var(--dark-color);
margin: 0;
}
.modal-close {
background: none;
border: none;
font-size: 28px;
color: var(--gray-500);
cursor: pointer;
padding: 0;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--border-radius-md);
transition: var(--transition-fast);
}
.modal-close:hover {
background: var(--gray-100);
color: var(--gray-700);
}
/* ===================================
Dashboard Specific
=================================== */
.dashboard-container {
/* Dashboard specific styles */
}
.quick-actions-section h2 {
font-size: var(--font-size-2xl);
font-weight: 700;
color: var(--dark-color);
margin-bottom: var(--spacing-lg);
}
.quick-actions-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-lg);
}
.quick-action-card {
background: white;
padding: var(--spacing-xl);
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow-sm);
transition: var(--transition-base);
text-align: center;
}
.quick-action-card:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-4px);
}
.action-icon {
font-size: 48px;
margin-bottom: var(--spacing-md);
}
.quick-action-card h3 {
font-size: var(--font-size-lg);
color: var(--dark-color);
margin-bottom: var(--spacing-sm);
}
.quick-action-card p {
font-size: var(--font-size-sm);
color: var(--gray-600);
margin: 0;
}
/* ===================================
Settings & Tabs
=================================== */
.settings-container {
display: grid;
grid-template-columns: 250px 1fr;
gap: var(--spacing-2xl);
}
.settings-nav {
background: white;
padding: var(--spacing-lg);
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow-sm);
height: fit-content;
position: sticky;
top: 80px;
}
.settings-tabs {
list-style: none;
}
.settings-tabs li {
margin-bottom: var(--spacing-xs);
}
.tab-link {
display: block;
padding: var(--spacing-md);
color: var(--gray-700);
text-decoration: none;
border-radius: var(--border-radius-md);
transition: var(--transition-fast);
font-size: var(--font-size-sm);
font-weight: 500;
}
.tab-link:hover {
background: var(--gray-100);
color: var(--primary-color);
}
.tab-link.active {
background: var(--primary-color);
color: white;
}
.settings-content {
background: white;
padding: var(--spacing-2xl);
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow-sm);
}
.settings-section {
margin-bottom: var(--spacing-2xl);
}
.settings-section:last-child {
margin-bottom: 0;
}
.settings-section h2 {
font-size: var(--font-size-2xl);
color: var(--dark-color);
margin-bottom: var(--spacing-lg);
padding-bottom: var(--spacing-md);
border-bottom: 2px solid var(--gray-200);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
/* ===================================
Responsive Design
=================================== */
@media (max-width: 1024px) {
.settings-container {
grid-template-columns: 1fr;
}
.settings-nav {
position: static;
}
}
@media (max-width: 768px) {
.page-container {
padding: var(--spacing-lg);
}
.page-header {
flex-direction: column;
}
.header-actions {
width: 100%;
}
.stats-grid,
.quick-actions-grid {
grid-template-columns: 1fr;
}
.stats-row {
grid-template-columns: repeat(2, 1fr);
}
.mobile-toggle {
display: flex;
}
.nav-menu {
display: none;
position: absolute;
top: 64px;
left: 0;
right: 0;
background: white;
flex-direction: column;
padding: var(--spacing-lg);
border-top: 1px solid var(--gray-200);
box-shadow: var(--shadow-lg);
}
.nav-menu.active {
display: flex;
}
.nav-link {
width: 100%;
justify-content: flex-start;
}
.table-container {
overflow-x: scroll;
}
.form-row {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.stats-row {
grid-template-columns: 1fr;
}
.table-controls {
flex-direction: column;
}
.search-container,
.filter-container {
width: 100%;
}
.filter-container {
flex-direction: column;
}
.filter-select {
width: 100%;
}
}
/* ===================================
Utility Classes
=================================== */
.text-center {
text-align: center;
}
.text-right {
text-align: right;
}
.mt-0 { margin-top: 0; }
.mt-1 { margin-top: var(--spacing-xs); }
.mt-2 { margin-top: var(--spacing-sm); }
.mt-3 { margin-top: var(--spacing-md); }
.mt-4 { margin-top: var(--spacing-lg); }
.mt-5 { margin-top: var(--spacing-xl); }
.mb-0 { margin-bottom: 0; }
.mb-1 { margin-bottom: var(--spacing-xs); }
.mb-2 { margin-bottom: var(--spacing-sm); }
.mb-3 { margin-bottom: var(--spacing-md); }
.mb-4 { margin-bottom: var(--spacing-lg); }
.mb-5 { margin-bottom: var(--spacing-xl); }
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--primary-color);
color: white;
padding: 8px;
text-decoration: none;
z-index: 100;
}
.skip-link:focus {
top: 0;
}
-------------------- END OF FILE --------------------
### FILE 31: assets/css/member-enhancements.css
- Type: CSS
- Size: 9.09 KB
- Path: assets/css
- Name: member-enhancements.css
------------------------------------------------------------
/* MEMBER MANAGEMENT ENHANCEMENTS CSS
Add this to your main.css or include separately
Improves member_view.php and member_edit.php styling */
/* Navigation Fix - Add to all member pages */
.page-cache-control {
/* Prevents caching issues with back button */
}
/* Enhanced Page Header */
.page-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 32px;
padding-bottom: 24px;
border-bottom: 2px solid #e5e7eb;
}
.page-header h1 {
font-size: 32px;
font-weight: 700;
color: #111827;
margin: 0 0 8px 0;
}
.page-subtitle {
font-size: 16px;
color: #6b7280;
margin: 0;
}
.header-actions {
display: flex;
gap: 12px;
}
/* Enhanced Buttons */
.btn {
padding: 10px 20px;
border-radius: 8px;
border: none;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 8px;
}
.btn-primary {
background: linear-gradient(135deg, #059669, #047857);
color: white;
}
.btn-primary:hover {
background: linear-gradient(135deg, #047857, #047857);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(5,150,105,0.3);
}
.btn-secondary {
background: #f3f4f6;
color: #374151;
}
.btn-secondary:hover {
background: #e5e7eb;
}
/* Statistics Cards - Member View */
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 32px;
}
.stat-card {
background: white;
border-radius: 12px;
padding: 24px;
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
transition: transform 0.2s;
}
.stat-card:hover {
transform: translateY(-4px);
box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}
.stat-icon {
width: 48px;
height: 48px;
background: #059669;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 24px;
margin-bottom: 16px;
}
.stat-content h3 {
font-size: 32px;
font-weight: 700;
color: #111827;
margin: 0 0 8px 0;
}
.stat-content p {
font-size: 14px;
color: #6b7280;
margin: 0 0 8px 0;
}
.stat-meta {
font-size: 12px;
color: #9ca3af;
}
/* Enhanced Tab Navigation */
.settings-container {
display: grid;
grid-template-columns: 240px 1fr;
gap: 24px;
margin-top: 24px;
}
.settings-nav {
background: white;
border-radius: 12px;
padding: 16px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
position: sticky;
top: 24px;
height: fit-content;
}
.settings-tabs {
list-style: none;
padding: 0;
margin: 0;
}
.settings-tabs li {
margin-bottom: 8px;
}
.tab-link {
display: block;
padding: 12px 16px;
color: #6b7280;
text-decoration: none;
border-radius: 8px;
font-weight: 500;
transition: all 0.2s;
}
.tab-link:hover {
background: #f3f4f6;
color: #111827;
}
.tab-link.active {
background: linear-gradient(135deg, #059669, #047857);
color: white;
}
.settings-content {
background: white;
border-radius: 12px;
padding: 32px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
.settings-section {
margin-bottom: 32px;
}
.settings-section h2 {
font-size: 20px;
font-weight: 700;
color: #111827;
margin-bottom: 20px;
padding-bottom: 12px;
border-bottom: 2px solid #e5e7eb;
}
/* Enhanced Form Styling */
.form-group {
margin-bottom: 20px;
}
.form-group label {
display: block;
font-size: 14px;
font-weight: 600;
color: #374151;
margin-bottom: 8px;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 12px 16px;
border: 2px solid #e5e7eb;
border-radius: 8px;
font-size: 14px;
transition: all 0.2s;
font-family: inherit;
}
.form-group input:focus,
.form-group select:focus,
.form-group textarea:focus {
outline: none;
border-color: #059669;
box-shadow: 0 0 0 3px rgba(5,150,105,0.1);
}
.form-group input:disabled {
background: #f9fafb;
color: #9ca3af;
cursor: not-allowed;
}
.form-help {
display: block;
font-size: 12px;
color: #6b7280;
margin-top: 6px;
}
.form-row {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 20px;
}
.form-actions {
margin-top: 32px;
padding-top: 20px;
border-top: 2px solid #e5e7eb;
display: flex;
gap: 12px;
}
/* Info Display Styling */
.info-item {
margin-bottom: 20px;
}
.info-item label {
display: block;
font-size: 12px;
font-weight: 600;
color: #6b7280;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 8px;
}
.info-item p {
font-size: 16px;
color: #111827;
margin: 0;
}
/* Progress Bar */
.progress-container {
display: flex;
align-items: center;
gap: 12px;
}
.progress-bar {
flex: 1;
height: 8px;
background: #e5e7eb;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(to right, #059669, #047857);
transition: width 0.3s ease;
}
.progress-text {
font-size: 14px;
font-weight: 600;
color: #6b7280;
min-width: 48px;
}
/* Transaction Item Styling */
.transactions-list {
display: flex;
flex-direction: column;
gap: 16px;
}
.transaction-item {
display: flex;
align-items: center;
gap: 16px;
padding: 16px;
border-radius: 12px;
background: #f9fafb;
border-left: 4px solid #e5e7eb;
}
.transaction-item.revenue {
background: #ecfdf5;
border-left-color: #059669;
}
.transaction-item.expense {
background: #fef2f2;
border-left-color: #dc2626;
}
.transaction-icon {
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
border-radius: 50%;
background: white;
}
.transaction-details {
flex: 1;
}
.transaction-description {
font-size: 14px;
font-weight: 600;
color: #111827;
margin-bottom: 4px;
}
.transaction-meta {
display: flex;
gap: 12px;
flex-wrap: wrap;
}
.transaction-date,
.transaction-category {
font-size: 12px;
color: #6b7280;
}
.transaction-amount {
font-size: 18px;
font-weight: 700;
}
.transaction-amount.positive {
color: #059669;
}
.transaction-amount.negative {
color: #dc2626;
}
/* Ticket Item Styling */
.tickets-container {
display: grid;
gap: 16px;
}
.ticket-item {
background: white;
border: 2px solid #e5e7eb;
border-radius: 12px;
padding: 20px;
transition: all 0.2s;
}
.ticket-item:hover {
border-color: #059669;
box-shadow: 0 4px 12px rgba(5,150,105,0.1);
}
.ticket-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.ticket-id {
font-family: monospace;
font-weight: 700;
color: #6b7280;
font-size: 14px;
}
.ticket-content h4 {
font-size: 16px;
font-weight: 600;
color: #111827;
margin: 0 0 12px 0;
}
.ticket-meta {
display: flex;
gap: 12px;
align-items: center;
}
.priority-badge {
padding: 4px 12px;
border-radius: 12px;
font-size: 11px;
font-weight: 700;
text-transform: uppercase;
}
.priority-low {
background: #dbeafe;
color: #1e40af;
}
.priority-medium {
background: #fef3c7;
color: #92400e;
}
.priority-high {
background: #fee2e2;
color: #991b1b;
}
.priority-critical {
background: #047857;
color: white;
}
.ticket-date {
font-size: 12px;
color: #6b7280;
}
/* Responsive Adjustments */
@media (max-width: 1024px) {
.settings-container {
grid-template-columns: 1fr;
}
.settings-nav {
position: static;
}
.form-row {
grid-template-columns: 1fr;
}
}
@media (max-width: 768px) {
.page-header {
flex-direction: column;
align-items: flex-start;
gap: 16px;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
.transaction-item {
flex-direction: column;
align-items: flex-start;
}
}
@media (max-width: 480px) {
.stats-grid {
grid-template-columns: 1fr;
}
.header-actions {
width: 100%;
flex-direction: column;
}
.btn {
width: 100%;
justify-content: center;
}
}
/* Back Button Prevention - Add this JavaScript */
/*
*/
-------------------- END OF FILE --------------------
### FILE 32: assets/css/responsive.css
- Type: CSS
- Size: 18.5 KB
- Path: assets/css
- Name: responsive.css
------------------------------------------------------------
/* Mobile First Responsive Design for Relevant Reflex */
/* Base Mobile Styles (up to 576px) */
@media (max-width: 576px) {
:root {
--font-size-base: 14px;
--font-size-lg: 16px;
--font-size-xl: 18px;
--font-size-2xl: 20px;
--font-size-3xl: 24px;
--spacing-md: 0.75rem;
--spacing-lg: 1rem;
--spacing-xl: 1.5rem;
}
.nav-container {
padding: 0 var(--spacing-md);
height: 60px;
}
.brand-name {
display: none;
}
.logo {
width: 35px;
height: 35px;
font-size: var(--font-size-base);
}
.nav-menu {
position: fixed;
top: 60px;
left: -100%;
width: 100%;
height: calc(100vh - 60px);
background: var(--white);
flex-direction: column;
padding: var(--spacing-xl);
gap: var(--spacing-md);
transition: left var(--transition-normal);
box-shadow: var(--shadow-xl);
z-index: var(--z-modal);
overflow-y: auto;
}
.nav-menu.active {
left: 0;
}
.mobile-toggle {
display: flex !important;
}
.nav-link {
justify-content: flex-start;
padding: var(--spacing-lg);
border-radius: var(--border-radius-xl);
font-size: var(--font-size-base);
width: 100%;
}
.nav-link::before {
display: none;
}
.nav-icon {
font-size: var(--font-size-lg);
}
.page-container {
padding: 0 var(--spacing-md);
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: var(--spacing-md);
margin-bottom: var(--spacing-xl);
}
.page-header h1 {
font-size: var(--font-size-3xl);
}
.header-actions {
width: 100%;
justify-content: flex-start;
flex-wrap: wrap;
}
.btn {
font-size: var(--font-size-sm);
padding: var(--spacing-md);
min-height: 40px;
}
.btn-small {
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-xs);
min-height: 32px;
}
.stats-grid {
grid-template-columns: 1fr;
gap: var(--spacing-md);
}
.stat-card {
padding: var(--spacing-lg);
flex-direction: row;
gap: var(--spacing-md);
}
.stat-icon {
width: 50px;
height: 50px;
font-size: 2rem;
}
.stat-content h3 {
font-size: var(--font-size-xl);
}
.table-container {
overflow-x: auto;
border-radius: var(--border-radius-lg);
}
.data-table {
min-width: 700px;
}
.data-table th,
.data-table td {
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-xs);
}
.user-info, .item-info, .panel-info {
gap: var(--spacing-sm);
}
.user-avatar {
width: 32px;
height: 32px;
font-size: var(--font-size-xs);
}
.action-buttons {
flex-direction: column;
gap: 2px;
}
.btn-edit,
.btn-delete,
.btn-view,
.btn-analytics,
.btn-track,
.btn-launch {
min-width: 28px;
height: 28px;
padding: 2px;
}
.form-row {
grid-template-columns: 1fr;
gap: var(--spacing-md);
}
.table-controls {
flex-direction: column;
align-items: stretch;
gap: var(--spacing-md);
}
.filter-container {
flex-direction: column;
gap: var(--spacing-sm);
}
.search-container {
max-width: none;
}
.modal {
padding: var(--spacing-md);
}
.modal-content {
max-height: 95vh;
}
.modal-form,
.settings-form,
.support-form {
padding: var(--spacing-lg);
}
}
/* Mobile Landscape and Small Tablets (577px - 768px) */
@media (min-width: 577px) and (max-width: 768px) {
.nav-menu {
position: fixed;
top: 70px;
left: -100%;
width: 100%;
height: calc(100vh - 70px);
background: var(--white);
flex-direction: column;
padding: var(--spacing-xl);
gap: var(--spacing-md);
transition: left var(--transition-normal);
box-shadow: var(--shadow-xl);
z-index: var(--z-modal);
}
.nav-menu.active {
left: 0;
}
.mobile-toggle {
display: flex;
}
.nav-link {
justify-content: flex-start;
padding: var(--spacing-lg);
border-radius: var(--border-radius-xl);
width: 100%;
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-lg);
}
.page-header {
flex-direction: column;
align-items: flex-start;
gap: var(--spacing-md);
}
.form-row {
grid-template-columns: 1fr;
gap: var(--spacing-md);
}
.table-controls {
flex-direction: row;
flex-wrap: wrap;
}
.filter-container {
flex-direction: row;
gap: var(--spacing-md);
}
}
/* Tablets (769px - 992px) */
@media (min-width: 769px) and (max-width: 992px) {
.nav-container {
padding: 0 var(--spacing-lg);
}
.page-container {
padding: 0 var(--spacing-lg);
}
.stats-grid {
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-lg);
}
.content-grid {
grid-template-columns: 1fr;
gap: var(--spacing-xl);
}
.dashboard-content .content-grid {
grid-template-columns: 2fr 1fr;
}
.footer-grid {
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-xl);
}
.nav-link {
padding: var(--spacing-md);
font-size: var(--font-size-sm);
}
.nav-icon {
font-size: var(--font-size-sm);
}
.nav-label {
font-size: var(--font-size-sm);
}
}
/* Desktop (993px - 1199px) */
@media (min-width: 993px) and (max-width: 1199px) {
.nav-container,
.page-container,
.footer-container {
max-width: 960px;
}
.stats-grid {
grid-template-columns: repeat(4, 1fr);
}
.content-grid {
grid-template-columns: 2fr 1fr;
}
.footer-grid {
grid-template-columns: repeat(4, 1fr);
}
}
/* Large Desktop (1200px and up) */
@media (min-width: 1200px) {
.nav-container,
.page-container,
.footer-container {
max-width: 1200px;
}
.stats-grid {
grid-template-columns: repeat(4, 1fr);
}
.content-grid {
grid-template-columns: 2fr 1fr;
gap: var(--spacing-2xl);
}
}
/* Extra Large Screens (1400px and up) */
@media (min-width: 1400px) {
.nav-container,
.page-container,
.footer-container {
max-width: 1400px;
}
:root {
--spacing-md: 1.25rem;
--spacing-lg: 2rem;
--spacing-xl: 2.5rem;
--spacing-2xl: 3.5rem;
}
}
/* Settings Page Responsive */
.settings-container {
display: grid;
grid-template-columns: 250px 1fr;
gap: var(--spacing-2xl);
margin-top: var(--spacing-xl);
}
.settings-nav {
background: var(--white);
border-radius: var(--border-radius-xl);
padding: var(--spacing-lg);
box-shadow: var(--shadow-sm);
height: fit-content;
position: sticky;
top: calc(70px + var(--spacing-lg));
}
.settings-tabs {
list-style: none;
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
}
.tab-link {
display: block;
padding: var(--spacing-md);
text-decoration: none;
color: var(--gray-600);
border-radius: var(--border-radius-lg);
transition: all var(--transition-normal);
font-weight: var(--font-weight-medium);
}
.tab-link:hover {
background: var(--primary-light);
color: var(--primary-color);
}
.tab-link.active {
background: var(--primary-color);
color: var(--white);
}
.settings-content {
background: var(--white);
border-radius: var(--border-radius-xl);
box-shadow: var(--shadow-sm);
}
.tab-content {
display: none;
padding: var(--spacing-2xl);
}
.tab-content.active {
display: block;
}
@media (max-width: 768px) {
.settings-container {
grid-template-columns: 1fr;
gap: var(--spacing-lg);
}
.settings-nav {
position: static;
order: -1;
}
.settings-tabs {
flex-direction: row;
overflow-x: auto;
gap: var(--spacing-xs);
padding-bottom: var(--spacing-sm);
}
.tab-link {
white-space: nowrap;
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-sm);
}
}
/* Dashboard Responsive Specific */
.dashboard-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing-md);
}
@media (max-width: 768px) {
.dashboard-container {
padding: 0 var(--spacing-md);
}
.content-grid {
grid-template-columns: 1fr;
gap: var(--spacing-lg);
}
.quick-actions-grid {
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-md);
}
}
@media (max-width: 480px) {
.quick-actions-grid {
grid-template-columns: 1fr;
}
}
/* Table Responsive Behavior */
@media (max-width: 768px) {
.table-container {
border-radius: var(--border-radius-lg);
}
.data-table th:first-child,
.data-table td:first-child {
position: sticky;
left: 0;
background: var(--white);
z-index: 10;
}
.data-table th {
background: var(--gray-50);
}
.data-table tbody tr:hover th:first-child,
.data-table tbody tr:hover td:first-child {
background: var(--gray-50);
}
}
/* Modal Responsive */
@media (max-width: 768px) {
.modal {
padding: var(--spacing-md);
align-items: flex-start;
padding-top: 10vh;
}
.modal-content {
max-height: 85vh;
width: 100%;
margin: 0;
}
.modal-header {
padding: var(--spacing-lg);
}
.modal-form,
.settings-form,
.support-form {
padding: var(--spacing-lg);
}
.modal-large {
max-width: none;
}
}
/* Form Responsive */
@media (max-width: 576px) {
.form-row {
grid-template-columns: 1fr;
gap: var(--spacing-md);
}
.form-actions {
flex-direction: column;
gap: var(--spacing-md);
}
.form-actions .btn {
width: 100%;
justify-content: center;
}
.table-controls {
flex-direction: column;
align-items: stretch;
}
.filter-container {
flex-direction: column;
gap: var(--spacing-sm);
}
.search-input,
.filter-select {
width: 100%;
}
}
/* Stats Grid Responsive */
@media (max-width: 1200px) {
.stats-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 768px) {
.stats-grid {
grid-template-columns: 1fr;
}
}
/* Footer Responsive */
@media (max-width: 992px) {
.footer-grid {
grid-template-columns: repeat(2, 1fr);
gap: var(--spacing-xl);
}
}
@media (max-width: 576px) {
.footer-container {
padding: var(--spacing-xl) var(--spacing-md) var(--spacing-md);
}
.footer-grid {
grid-template-columns: 1fr;
gap: var(--spacing-lg);
}
.footer-bottom {
flex-direction: column;
text-align: center;
gap: var(--spacing-md);
}
.legal-links {
flex-direction: column;
align-items: center;
gap: var(--spacing-sm);
}
.social-links {
justify-content: center;
}
}
/* Chart Responsive */
@media (max-width: 768px) {
.chart-container canvas {
max-width: 100%;
height: auto;
}
.chart-section {
padding: var(--spacing-md);
}
}
/* Touch Device Optimizations */
@media (hover: none) and (pointer: coarse) {
.btn {
min-height: 44px; /* Apple's recommended minimum */
padding: var(--spacing-md) var(--spacing-lg);
}
.btn-edit,
.btn-delete,
.btn-view,
.btn-analytics {
min-width: 44px;
min-height: 44px;
padding: var(--spacing-md);
}
.nav-link {
min-height: 44px;
padding: var(--spacing-md) var(--spacing-lg);
}
.mobile-toggle {
min-width: 44px;
min-height: 44px;
}
/* Remove hover effects on touch devices */
.stat-card:hover {
transform: none;
box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
transform: none;
}
}
/* High DPI Displays */
@media (-webkit-min-device-pixel-ratio: 2), (min-resolution: 192dpi) {
.logo {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
}
/* Landscape Phone Orientation */
@media (max-height: 500px) and (orientation: landscape) {
.nav-menu {
height: calc(100vh - 70px);
padding: var(--spacing-lg);
}
.modal {
padding-top: 2vh;
}
.page-header {
margin-bottom: var(--spacing-lg);
}
main {
padding: var(--spacing-lg) 0;
}
}
/* Print Optimizations */
@media print {
.main-header,
.main-footer,
.btn,
.mobile-toggle,
.action-buttons,
.search-input,
.filter-select,
.modal {
display: none !important;
}
.page-container {
max-width: none;
padding: 0;
margin: 0;
}
.stats-grid {
grid-template-columns: repeat(4, 1fr);
gap: var(--spacing-md);
break-inside: avoid;
}
.stat-card {
border: 1px solid var(--gray-300);
box-shadow: none;
break-inside: avoid;
}
.table-container {
border: 1px solid var(--gray-300);
box-shadow: none;
}
.data-table th {
background: var(--gray-100) !important;
color: var(--dark-color) !important;
}
body {
font-size: 11pt;
line-height: 1.4;
color: black !important;
background: white !important;
}
h1, h2, h3, h4 {
color: black !important;
}
.page-header h1 {
font-size: 18pt;
margin-bottom: 12pt;
}
}
/* High Contrast Mode Support */
@media (prefers-contrast: high) {
:root {
--primary-color: #047857;
--secondary-color: #495057;
--gray-300: #999999;
--gray-600: #333333;
}
.nav-link {
border: 1px solid transparent;
}
.nav-link.active {
border-color: var(--white);
}
.btn {
border: 2px solid currentColor;
}
.stat-card {
border: 1px solid var(--gray-300);
}
}
/* Reduced Motion Support */
@media (prefers-reduced-motion: reduce) {
*,
*::before,
*::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
.modal-content {
animation: none;
}
}
/* Dark Mode Support */
@media (prefers-color-scheme: dark) {
:root {
--white: #1a1d20;
--light-color: #212529;
--gray-50: #2d3436;
--gray-100: #495057;
--gray-200: #6c757d;
--gray-300: #adb5bd;
--gray-400: #ced4da;
--gray-500: #dee2e6;
--gray-600: #e9ecef;
--gray-700: #f1f3f4;
--gray-800: #f8f9fa;
--dark-color: #ffffff;
--primary-light: rgba(5, 150, 105, 0.1);
}
body {
background-color: #0d1117;
color: var(--dark-color);
}
.main-header {
background: #161b22;
border-bottom: 1px solid var(--gray-700);
}
.nav-link:hover {
background: rgba(5, 150, 105, 0.1);
}
.table-container,
.stat-card,
.modal-content,
.settings-content {
background: #161b22;
border: 1px solid var(--gray-700);
}
.data-table th {
background: var(--gray-800);
}
.search-input,
.filter-select,
input,
textarea,
select {
background: #0d1117;
border-color: var(--gray-700);
color: var(--dark-color);
}
.search-input:focus,
input:focus,
textarea:focus,
select:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(5, 150, 105, 0.1);
}
}
/* Pagination Responsive */
.pagination-container {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: var(--spacing-lg);
padding: var(--spacing-lg);
background: var(--white);
border-radius: var(--border-radius-lg);
box-shadow: var(--shadow-sm);
}
.pagination-controls {
display: flex;
gap: var(--spacing-sm);
align-items: center;
}
.page-number {
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
background: var(--gray-100);
color: var(--gray-700);
font-weight: var(--font-weight-medium);
}
.page-number.active {
background: var(--primary-color);
color: var(--white);
}
@media (max-width: 576px) {
.pagination-container {
flex-direction: column;
gap: var(--spacing-md);
text-align: center;
}
.pagination-info {
order: 2;
font-size: var(--font-size-sm);
}
.pagination-controls {
order: 1;
}
}
/* Loading States Responsive */
@media (max-width: 576px) {
.loading::after {
animation-duration: 2s;
}
}
/* Support Page Responsive */
.support-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-2xl);
margin-bottom: var(--spacing-2xl);
}
@media (max-width: 992px) {
.support-grid {
grid-template-columns: 1fr;
gap: var(--spacing-xl);
}
}
.help-resources {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-lg);
}
@media (max-width: 576px) {
.help-resources {
grid-template-columns: 1fr;
}
}
/* Finance Page Responsive */
.finance-overview {
margin-bottom: var(--spacing-2xl);
}
.finance-content .content-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: var(--spacing-2xl);
}
@media (max-width: 992px) {
.finance-content .content-grid {
grid-template-columns: 1fr;
gap: var(--spacing-xl);
}
}
-------------------- END OF FILE --------------------
### FILE 33: assets/images/logo.svg
- Type: SVG
- Size: 0 B
- Path: assets/images
- Name: logo.svg
------------------------------------------------------------
[IMAGE FILE: SVG - Content not displayed]
-------------------- END OF FILE --------------------
### FILE 34: assets/js/dashboard.js
- Type: JS
- Size: 26.44 KB
- Path: assets/js
- Name: dashboard.js
------------------------------------------------------------
// Dashboard specific functionality for Relevant Reflex
(function() {
'use strict';
// Dashboard module
window.RR = window.RR || {};
RR.dashboard = {
charts: {},
updateInterval: null,
refreshRate: 30000, // 30 seconds
init: function() {
this.initializeCharts();
this.setupQuickActions();
this.setupRealTimeUpdates();
this.setupInteractions();
this.loadDashboardData();
},
initializeCharts: function() {
// Performance Chart
const performanceCanvas = document.getElementById('performanceChart');
if (performanceCanvas) {
this.charts.performance = this.createPerformanceChart(performanceCanvas);
}
// Revenue Chart (for finance page)
const revenueCanvas = document.getElementById('revenueChart');
if (revenueCanvas) {
this.charts.revenue = this.createRevenueChart(revenueCanvas);
}
// Demand Chart (for demand page)
const demandCanvas = document.getElementById('demandChart');
if (demandCanvas) {
this.charts.demand = this.createDemandChart(demandCanvas);
}
// Setup chart period controls
this.setupChartControls();
},
createPerformanceChart: function(canvas) {
const ctx = canvas.getContext('2d');
// Sample performance data - replace with real API data
const data = {
labels: ['Week 1', 'Week 2', 'Week 3', 'Week 4', 'Week 5', 'Week 6'],
datasets: [{
label: 'Panel Responses',
data: [320, 450, 380, 520, 430, 580],
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(),
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim() + '20',
borderWidth: 3,
tension: 0.4,
fill: true
}, {
label: 'Active Users',
data: [280, 390, 420, 480, 510, 540],
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--success-color').trim(),
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--success-color').trim() + '20',
borderWidth: 3,
tension: 0.4,
fill: true
}]
};
return this.drawChart(ctx, canvas, data, 'line');
},
createRevenueChart: function(canvas) {
const ctx = canvas.getContext('2d');
// Sample revenue data for last 12 months
const data = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [{
label: 'Revenue',
data: [28500, 31200, 29800, 33500, 35200, 38900, 42100, 39800, 43500, 45230, 41800, 44600],
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(),
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(),
borderWidth: 1
}]
};
return this.drawChart(ctx, canvas, data, 'bar');
},
createDemandChart: function(canvas) {
const ctx = canvas.getContext('2d');
// Sample demand trend data
const currentData = [6.2, 6.8, 7.1, 7.5, 8.2, 8.0, 8.5, 8.7, 9.1, 8.9, 8.7, 9.2];
const forecastData = [9.0, 9.2, 9.5, 9.7, 9.9, 10.2];
const data = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec', 'Jan+', 'Feb+', 'Mar+', 'Apr+', 'May+', 'Jun+'],
datasets: [{
label: 'Current Demand',
data: [...currentData, ...Array(6).fill(null)],
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim(),
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--primary-color').trim() + '30',
borderWidth: 3,
tension: 0.4,
fill: false
}, {
label: 'Forecast',
data: [...Array(11).fill(null), currentData[11], ...forecastData],
borderColor: getComputedStyle(document.documentElement).getPropertyValue('--success-color').trim(),
backgroundColor: getComputedStyle(document.documentElement).getPropertyValue('--success-color').trim() + '30',
borderWidth: 2,
borderDash: [5, 5],
tension: 0.4,
fill: false
}]
};
return this.drawChart(ctx, canvas, data, 'line');
},
drawChart: function(ctx, canvas, data, type) {
// Simple chart implementation (replace with Chart.js for production)
const chart = {
data: data,
type: type,
canvas: canvas,
ctx: ctx
};
this.renderChart(chart);
return chart;
},
renderChart: function(chart) {
const { ctx, canvas, data, type } = chart;
const padding = 40;
const chartWidth = canvas.width - 2 * padding;
const chartHeight = canvas.height - 2 * padding;
// Clear canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
if (type === 'line') {
this.renderLineChart(ctx, canvas, data, padding, chartWidth, chartHeight);
} else if (type === 'bar') {
this.renderBarChart(ctx, canvas, data, padding, chartWidth, chartHeight);
}
},
renderLineChart: function(ctx, canvas, data, padding, chartWidth, chartHeight) {
const datasets = data.datasets;
const labels = data.labels;
// Find max and min values
const allValues = datasets.flatMap(d => d.data.filter(v => v !== null));
const maxValue = Math.max(...allValues);
const minValue = Math.min(...allValues);
const range = maxValue - minValue || 1;
// Draw axes
ctx.strokeStyle = '#e9ecef';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, canvas.height - padding);
ctx.lineTo(canvas.width - padding, canvas.height - padding);
ctx.stroke();
// Draw grid lines
ctx.strokeStyle = '#f1f3f4';
ctx.lineWidth = 1;
for (let i = 1; i < 5; i++) {
const y = padding + (i / 5) * chartHeight;
ctx.beginPath();
ctx.moveTo(padding, y);
ctx.lineTo(canvas.width - padding, y);
ctx.stroke();
}
// Draw labels
ctx.fillStyle = '#666';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
labels.forEach((label, index) => {
const x = padding + (index / (labels.length - 1)) * chartWidth;
ctx.fillText(label, x, canvas.height - padding + 20);
});
// Draw datasets
datasets.forEach(dataset => {
ctx.strokeStyle = dataset.borderColor;
ctx.lineWidth = dataset.borderWidth || 2;
if (dataset.borderDash) {
ctx.setLineDash(dataset.borderDash);
} else {
ctx.setLineDash([]);
}
ctx.beginPath();
let firstPoint = true;
dataset.data.forEach((value, index) => {
if (value !== null) {
const x = padding + (index / (labels.length - 1)) * chartWidth;
const y = canvas.height - padding - ((value - minValue) / range) * chartHeight;
if (firstPoint) {
ctx.moveTo(x, y);
firstPoint = false;
} else {
ctx.lineTo(x, y);
}
// Draw data points
ctx.save();
ctx.fillStyle = dataset.borderColor;
ctx.beginPath();
ctx.arc(x, y, 3, 0, 2 * Math.PI);
ctx.fill();
ctx.restore();
}
});
ctx.stroke();
});
},
renderBarChart: function(ctx, canvas, data, padding, chartWidth, chartHeight) {
const dataset = data.datasets[0];
const labels = data.labels;
const values = dataset.data;
const maxValue = Math.max(...values);
const barWidth = chartWidth / values.length * 0.8;
// Draw axes
ctx.strokeStyle = '#e9ecef';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(padding, padding);
ctx.lineTo(padding, canvas.height - padding);
ctx.lineTo(canvas.width - padding, canvas.height - padding);
ctx.stroke();
// Draw bars
ctx.fillStyle = dataset.backgroundColor;
values.forEach((value, index) => {
const barHeight = (value / maxValue) * chartHeight;
const x = padding + (index / values.length) * chartWidth + (chartWidth / values.length - barWidth) / 2;
const y = canvas.height - padding - barHeight;
ctx.fillRect(x, y, barWidth, barHeight);
// Draw value labels
ctx.fillStyle = '#666';
ctx.font = '12px Arial';
ctx.textAlign = 'center';
ctx.fillText('$' + (value / 1000).toFixed(0) + 'k', x + barWidth / 2, y - 5);
// Draw month labels
ctx.fillText(labels[index], x + barWidth / 2, canvas.height - padding + 15);
ctx.fillStyle = dataset.backgroundColor;
});
},
setupChartControls: function() {
// Chart period selectors
const periodSelects = document.querySelectorAll('#chartPeriod, #trendPeriod, #revenuePeriod');
periodSelects.forEach(select => {
select.addEventListener('change', function() {
const chartId = this.id.replace('Period', 'Chart');
RR.dashboard.updateChartPeriod(chartId, this.value);
});
});
},
updateChartPeriod: function(chartId, period) {
const chart = this.charts[chartId.replace('Chart', '')];
if (!chart) return;
// Update chart data based on period
// This would typically fetch new data from API
console.log(`Updating ${chartId} for period: ${period}`);
// Show loading state
const canvas = chart.canvas;
const ctx = chart.ctx;
ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = '#666';
ctx.font = '16px Arial';
ctx.textAlign = 'center';
ctx.fillText('Loading...', canvas.width / 2, canvas.height / 2);
// Simulate API call
setTimeout(() => {
this.renderChart(chart);
RR.toast.show('Chart updated successfully', 'success');
}, 800);
},
setupQuickActions: function() {
// Quick action buttons
const quickActions = {
createPanel: () => {
window.location.href = 'panel.php?action=create';
},
manageUsers: () => {
window.location.href = 'users.php';
},
viewReports: () => {
window.location.href = 'finance.php?tab=reports';
},
systemSettings: () => {
window.location.href = 'settings.php';
}
};
// Expose to global scope
window.quickActions = quickActions;
// Setup quick action cards
document.querySelectorAll('.quick-action-card').forEach(card => {
card.addEventListener('click', function() {
const action = this.getAttribute('data-action');
if (action && quickActions[action]) {
quickActions[action]();
}
});
});
// Setup header action buttons
document.getElementById('addUserBtn')?.addEventListener('click', function() {
RR.modals.open('addUserModal');
});
document.getElementById('createPanelBtn')?.addEventListener('click', function() {
RR.modals.open('createPanelModal');
});
document.getElementById('addSupplyBtn')?.addEventListener('click', function() {
RR.modals.open('addSupplyModal');
});
document.getElementById('addTransactionBtn')?.addEventListener('click', function() {
RR.modals.open('addTransactionModal');
});
},
setupRealTimeUpdates: function() {
// Auto-refresh dashboard data
this.updateInterval = setInterval(() => {
this.updateRealTimeData();
}, this.refreshRate);
// Pause updates when page is not visible
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
clearInterval(this.updateInterval);
} else {
this.updateInterval = setInterval(() => {
this.updateRealTimeData();
}, this.refreshRate);
}
});
},
updateRealTimeData: function() {
// Update stat cards with simulated real-time data
const statCards = document.querySelectorAll('.stat-card');
statCards.forEach(card => {
const valueElement = card.querySelector('h3');
if (!valueElement) return;
const currentText = valueElement.textContent;
// Update revenue values
if (currentText.includes('$')) {
const currentValue = parseInt(currentText.replace(/[^0-9]/g, ''));
const variation = Math.floor(Math.random() * 200) - 100; // -100 to +100
const newValue = Math.max(0, currentValue + variation);
valueElement.textContent = '$' + RR.utils.formatNumber(newValue);
// Add pulse animation
card.style.transform = 'scale(1.02)';
setTimeout(() => {
card.style.transform = '';
}, 200);
}
// Update numeric values
else if (/^\d+$/.test(currentText)) {
const currentValue = parseInt(currentText);
const variation = Math.floor(Math.random() * 6) - 3; // -3 to +3
const newValue = Math.max(0, currentValue + variation);
valueElement.textContent = newValue.toString();
// Add subtle highlight
card.classList.add('updated');
setTimeout(() => {
card.classList.remove('updated');
}, 1000);
}
});
// Update charts
Object.keys(this.charts).forEach(chartKey => {
if (Math.random() > 0.7) { // 30% chance to update each chart
this.updateChartData(this.charts[chartKey]);
}
});
},
updateChartData: function(chart) {
if (!chart || !chart.data) return;
// Add new data point and remove oldest
chart.data.datasets.forEach(dataset => {
if (dataset.data.length > 0) {
// Generate new data point based on last value
const lastValue = dataset.data[dataset.data.length - 1];
const variation = (Math.random() - 0.5) * (lastValue * 0.1);
const newValue = Math.max(0, lastValue + variation);
dataset.data.push(Math.round(newValue));
// Remove oldest point if we have too many
if (dataset.data.length > 12) {
dataset.data.shift();
}
}
});
this.renderChart(chart);
},
setupInteractions: function() {
// Activity item interactions
document.querySelectorAll('.activity-item').forEach(item => {
item.addEventListener('click', function() {
this.classList.toggle('expanded');
});
});
// Stat card interactions
document.querySelectorAll('.stat-card').forEach(card => {
card.addEventListener('click', function() {
const cardType = this.querySelector('p').textContent.toLowerCase();
RR.dashboard.navigateToDetails(cardType);
});
});
// Export functionality
document.querySelectorAll('[id$="ExportBtn"], [id$="exportBtn"]').forEach(btn => {
btn.addEventListener('click', function() {
RR.dashboard.exportData(this.id);
});
});
// Generate report functionality
document.getElementById('generateReportBtn')?.addEventListener('click', function() {
RR.dashboard.generateReport();
});
},
navigateToDetails: function(cardType) {
const navigationMap = {
'total users': 'users.php',
'active panels': 'panel.php',
'pending supplies': 'supply.php',
'monthly revenue': 'finance.php',
'total revenue': 'finance.php',
'net profit': 'finance.php'
};
const url = navigationMap[cardType];
if (url) {
window.location.href = url;
}
},
exportData: function(buttonId) {
const button = document.getElementById(buttonId);
if (!button) return;
// Show loading state
const originalText = button.textContent;
button.textContent = 'Exporting...';
button.disabled = true;
// Simulate export process
setTimeout(() => {
// Reset button
button.textContent = originalText;
button.disabled = false;
// Show success message
RR.toast.show('Data exported successfully!', 'success');
// Simulate file download
const link = document.createElement('a');
link.href = 'data:text/csv;charset=utf-8,Sample Export Data\nColumn1,Column2,Column3\nValue1,Value2,Value3';
link.download = `export-${new Date().toISOString().split('T')[0]}.csv`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}, 2000);
},
generateReport: function() {
const button = document.getElementById('generateReportBtn');
if (!button) return;
// Show loading state
button.innerHTML = '⏳ Generating...';
button.disabled = true;
// Simulate report generation
setTimeout(() => {
// Reset button
button.innerHTML = '📊 Generate Report';
button.disabled = false;
// Show success and open modal with report preview
RR.toast.show('Report generated successfully!', 'success');
// Create and show report preview modal
RR.dashboard.showReportPreview();
}, 3000);
},
showReportPreview: function() {
const modal = document.createElement('div');
modal.className = 'modal';
modal.id = 'reportPreviewModal';
modal.innerHTML = `
Performance Summary Report
Generated: ${new Date().toLocaleString()}
Key Metrics
Total Users: 1,248 (+12% from last month)
Active Panels: 23 (+3 new this month)
Revenue Growth: 15.3% year-over-year
Customer Satisfaction: 98.5%
Print Report
Close
`;
document.body.appendChild(modal);
RR.modals.open('reportPreviewModal');
},
loadDashboardData: function() {
// Load initial dashboard data
console.log('Loading dashboard data...');
// This would typically make API calls to load real data
// For now, we'll simulate data loading with a timeout
setTimeout(() => {
console.log('Dashboard data loaded successfully');
}, 1000);
},
// Keyboard shortcuts
setupKeyboardShortcuts: function() {
document.addEventListener('keydown', function(e) {
// Only trigger shortcuts if not typing in input fields
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
return;
}
if (e.ctrlKey || e.metaKey) {
switch(e.key) {
case '1':
e.preventDefault();
window.location.href = 'users.php';
break;
case '2':
e.preventDefault();
window.location.href = 'panel.php';
break;
case '3':
e.preventDefault();
window.location.href = 'supply.php';
break;
case '4':
e.preventDefault();
window.location.href = 'demand.php';
break;
case '5':
e.preventDefault();
window.location.href = 'finance.php';
break;
case 'h':
e.preventDefault();
window.location.href = 'index.php';
break;
}
}
});
},
// Cleanup function
destroy: function() {
if (this.updateInterval) {
clearInterval(this.updateInterval);
}
}
};
// Initialize dashboard when DOM is ready
document.addEventListener('DOMContentLoaded', function() {
// Only initialize on dashboard-related pages
const isDashboardPage = document.querySelector('.dashboard-container, .stats-grid, .chart-section');
if (isDashboardPage) {
RR.dashboard.init();
RR.dashboard.setupKeyboardShortcuts();
}
});
// Initialize dashboard function for other pages to call
window.initDashboard = function() {
if (RR.dashboard) {
RR.dashboard.init();
}
};
// Cleanup on page unload
window.addEventListener('beforeunload', function() {
if (RR.dashboard) {
RR.dashboard.destroy();
}
});
// Add CSS for updated state
const style = document.createElement('style');
style.textContent = `
.stat-card.updated {
background: linear-gradient(135deg, var(--primary-light) 0%, rgba(5, 150, 105, 0.05) 100%);
border-color: var(--primary-color);
transform: translateY(-1px);
}
.report-preview h3 {
color: var(--primary-color);
margin-bottom: var(--spacing-lg);
}
.report-preview h4 {
color: var(--gray-700);
margin: var(--spacing-lg) 0 var(--spacing-md);
}
.report-preview ul {
list-style: none;
padding-left: 0;
}
.report-preview li {
padding: var(--spacing-sm) 0;
border-bottom: 1px solid var(--gray-200);
}
.report-preview li:last-child {
border-bottom: none;
}
`;
document.head.appendChild(style);
})();
-------------------- END OF FILE --------------------
### FILE 35: assets/js/main.js
- Type: JS
- Size: 33.12 KB
- Path: assets/js
- Name: main.js
------------------------------------------------------------
// Relevant Reflex - Main JavaScript
(function() {
'use strict';
// Global app object
window.RR = window.RR || {};
// App configuration
RR.config = {
version: '1.0.0',
apiUrl: '/api/',
debounceDelay: 300,
toastDuration: 3000,
animationDuration: 300
};
// Initialize app when DOM is ready
document.addEventListener('DOMContentLoaded', function() {
RR.init();
});
// Main initialization
RR.init = function() {
console.log(`Relevant Reflex Panel Management System v${RR.config.version} initialized`);
// Initialize core modules
RR.navigation.init();
RR.forms.init();
RR.tables.init();
RR.modals.init();
RR.tooltips.init();
RR.lazyLoad.init();
RR.performance.init();
// Initialize page-specific modules
if (typeof window.initDashboard === 'function') {
window.initDashboard();
}
};
// Navigation Module
RR.navigation = {
init: function() {
this.setupMobileToggle();
this.setupSmoothScrolling();
this.setupActiveLinks();
},
setupMobileToggle: function() {
const mobileToggle = document.querySelector('.mobile-toggle');
const navMenu = document.querySelector('.nav-menu');
if (!mobileToggle || !navMenu) return;
// Toggle mobile menu
mobileToggle.addEventListener('click', function(e) {
e.preventDefault();
const isActive = mobileToggle.classList.contains('active');
if (isActive) {
RR.navigation.closeMobileMenu();
} else {
RR.navigation.openMobileMenu();
}
});
// Close menu when clicking nav links
navMenu.querySelectorAll('.nav-link').forEach(link => {
link.addEventListener('click', function() {
RR.navigation.closeMobileMenu();
});
});
// Close menu when clicking outside
document.addEventListener('click', function(e) {
const isClickInsideNav = navMenu.contains(e.target) || mobileToggle.contains(e.target);
if (!isClickInsideNav && mobileToggle.classList.contains('active')) {
RR.navigation.closeMobileMenu();
}
});
// Close menu on escape key
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && mobileToggle.classList.contains('active')) {
RR.navigation.closeMobileMenu();
}
});
},
openMobileMenu: function() {
const mobileToggle = document.querySelector('.mobile-toggle');
const navMenu = document.querySelector('.nav-menu');
mobileToggle.classList.add('active');
navMenu.classList.add('active');
mobileToggle.setAttribute('aria-expanded', 'true');
// Prevent body scroll
document.body.style.overflow = 'hidden';
},
closeMobileMenu: function() {
const mobileToggle = document.querySelector('.mobile-toggle');
const navMenu = document.querySelector('.nav-menu');
mobileToggle.classList.remove('active');
navMenu.classList.remove('active');
mobileToggle.setAttribute('aria-expanded', 'false');
// Restore body scroll
document.body.style.overflow = '';
},
setupSmoothScrolling: function() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
const href = this.getAttribute('href');
if (href === '#') {
e.preventDefault();
return;
}
const target = document.querySelector(href);
if (target) {
e.preventDefault();
const headerHeight = document.querySelector('.main-header')?.offsetHeight || 0;
const targetPosition = target.offsetTop - headerHeight - 20;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
},
setupActiveLinks: function() {
const currentPage = window.location.pathname;
const navLinks = document.querySelectorAll('.nav-link');
navLinks.forEach(link => {
const linkPath = new URL(link.href).pathname;
if (linkPath === currentPage) {
link.classList.add('active');
} else {
link.classList.remove('active');
}
});
}
};
// Forms Module
RR.forms = {
init: function() {
this.setupValidation();
this.setupFileUploads();
this.setupFormSubmissions();
this.setupRealTimeValidation();
},
setupValidation: function() {
const forms = document.querySelectorAll('form[data-validate]');
forms.forEach(form => {
form.addEventListener('submit', function(e) {
if (!RR.forms.validateForm(this)) {
e.preventDefault();
}
});
});
},
validateForm: function(form) {
const requiredFields = form.querySelectorAll('[required]');
let isValid = true;
// Clear previous errors
form.querySelectorAll('.error').forEach(el => el.classList.remove('error'));
form.querySelectorAll('.error-message').forEach(el => el.remove());
requiredFields.forEach(field => {
if (!this.validateField(field)) {
isValid = false;
}
});
// Email validation
const emailFields = form.querySelectorAll('input[type="email"]');
emailFields.forEach(field => {
if (field.value && !this.isValidEmail(field.value)) {
this.showFieldError(field, 'Please enter a valid email address');
isValid = false;
}
});
// Password confirmation
const passwordField = form.querySelector('input[name="password"]');
const confirmField = form.querySelector('input[name="confirm_password"]');
if (passwordField && confirmField && passwordField.value !== confirmField.value) {
this.showFieldError(confirmField, 'Passwords do not match');
isValid = false;
}
return isValid;
},
validateField: function(field) {
const value = field.value.trim();
if (!value) {
this.showFieldError(field, 'This field is required');
return false;
}
// Minimum length validation
const minLength = field.getAttribute('minlength');
if (minLength && value.length < parseInt(minLength)) {
this.showFieldError(field, `Minimum ${minLength} characters required`);
return false;
}
field.classList.remove('error');
return true;
},
showFieldError: function(field, message) {
field.classList.add('error');
// Remove existing error message
const existingError = field.parentNode.querySelector('.error-message');
if (existingError) {
existingError.remove();
}
// Add new error message
const errorEl = document.createElement('small');
errorEl.className = 'error-message';
errorEl.textContent = message;
errorEl.style.color = 'var(--danger-color)';
errorEl.style.display = 'block';
errorEl.style.marginTop = 'var(--spacing-xs)';
field.parentNode.insertBefore(errorEl, field.nextSibling);
},
isValidEmail: function(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
},
setupFileUploads: function() {
const fileInputs = document.querySelectorAll('input[type="file"]');
fileInputs.forEach(input => {
input.addEventListener('change', function() {
const file = this.files[0];
if (file) {
// Validate file size (5MB limit)
if (file.size > 5 * 1024 * 1024) {
RR.toast.show('File size must be less than 5MB', 'error');
this.value = '';
return;
}
// Show file name
const label = this.nextElementSibling;
if (label) {
label.textContent = file.name;
}
}
});
});
},
setupFormSubmissions: function() {
const submitButtons = document.querySelectorAll('.btn[type="submit"], .btn-primary');
submitButtons.forEach(btn => {
btn.addEventListener('click', function(e) {
const form = this.closest('form');
if (form && this.type === 'submit') {
RR.forms.addLoadingState(this);
// Remove loading state after form submission
setTimeout(() => {
RR.forms.removeLoadingState(this);
}, 2000);
}
});
});
},
setupRealTimeValidation: function() {
const inputs = document.querySelectorAll('input[required], textarea[required]');
inputs.forEach(input => {
input.addEventListener('blur', function() {
if (this.value.trim()) {
RR.forms.validateField(this);
}
});
input.addEventListener('input', function() {
if (this.classList.contains('error') && this.value.trim()) {
RR.forms.validateField(this);
}
});
});
},
addLoadingState: function(button) {
if (button.classList.contains('loading')) return;
const originalText = button.textContent;
button.setAttribute('data-original-text', originalText);
button.textContent = 'Loading...';
button.disabled = true;
button.classList.add('loading');
},
removeLoadingState: function(button) {
const originalText = button.getAttribute('data-original-text');
if (originalText) {
button.textContent = originalText;
button.removeAttribute('data-original-text');
}
button.disabled = false;
button.classList.remove('loading');
}
};
// Tables Module
RR.tables = {
init: function() {
this.setupSearch();
this.setupSorting();
this.setupActions();
this.setupFilters();
},
setupSearch: function() {
const searchInputs = document.querySelectorAll('.search-input');
searchInputs.forEach(input => {
let timeout;
input.addEventListener('input', function() {
clearTimeout(timeout);
timeout = setTimeout(() => {
const query = this.value.toLowerCase().trim();
RR.tables.filterTable(query);
}, RR.config.debounceDelay);
});
});
},
filterTable: function(query) {
const table = document.querySelector('.data-table');
if (!table) return;
const rows = table.querySelectorAll('tbody tr');
let visibleCount = 0;
rows.forEach(row => {
const text = row.textContent.toLowerCase();
const shouldShow = !query || text.includes(query);
row.style.display = shouldShow ? '' : 'none';
if (shouldShow) visibleCount++;
});
// Update pagination info if exists
const paginationInfo = document.querySelector('.pagination-info');
if (paginationInfo && query) {
paginationInfo.textContent = `Showing ${visibleCount} of ${rows.length} results`;
}
},
setupSorting: function() {
const headers = document.querySelectorAll('.data-table th[data-sort]');
headers.forEach(header => {
header.style.cursor = 'pointer';
header.addEventListener('click', function() {
const sortKey = this.getAttribute('data-sort');
const isAscending = !this.classList.contains('sort-asc');
// Remove sort classes from all headers
headers.forEach(h => h.classList.remove('sort-asc', 'sort-desc'));
// Add sort class to current header
this.classList.add(isAscending ? 'sort-asc' : 'sort-desc');
RR.tables.sortTable(sortKey, isAscending);
});
});
},
sortTable: function(sortKey, ascending) {
const table = document.querySelector('.data-table tbody');
if (!table) return;
const rows = Array.from(table.querySelectorAll('tr'));
const sortIndex = Array.from(table.parentNode.querySelectorAll('th')).findIndex(th =>
th.getAttribute('data-sort') === sortKey
);
if (sortIndex === -1) return;
rows.sort((a, b) => {
const aText = a.cells[sortIndex]?.textContent.trim() || '';
const bText = b.cells[sortIndex]?.textContent.trim() || '';
// Try numeric sort first
const aNum = parseFloat(aText.replace(/[^0-9.-]/g, ''));
const bNum = parseFloat(bText.replace(/[^0-9.-]/g, ''));
if (!isNaN(aNum) && !isNaN(bNum)) {
return ascending ? aNum - bNum : bNum - aNum;
}
// Fall back to string sort
return ascending
? aText.localeCompare(bText)
: bText.localeCompare(aText);
});
// Re-append sorted rows
rows.forEach(row => table.appendChild(row));
},
setupActions: function() {
// Edit buttons
document.addEventListener('click', function(e) {
if (e.target.closest('.btn-edit')) {
e.preventDefault();
const id = e.target.closest('.btn-edit').getAttribute('data-id');
RR.tables.handleEdit(id);
}
if (e.target.closest('.btn-delete')) {
e.preventDefault();
const id = e.target.closest('.btn-delete').getAttribute('data-id');
RR.tables.handleDelete(id);
}
if (e.target.closest('.btn-view')) {
e.preventDefault();
const id = e.target.closest('.btn-view').getAttribute('data-id');
RR.tables.handleView(id);
}
});
},
handleEdit: function(id) {
RR.toast.show(`Edit functionality for ID: ${id}`, 'info');
// Implement edit modal or redirect
},
handleDelete: function(id) {
if (confirm('Are you sure you want to delete this item? This action cannot be undone.')) {
// Show loading state
const button = document.querySelector(`[data-id="${id}"].btn-delete`);
if (button) {
button.disabled = true;
button.innerHTML = '⏳ ';
}
// Simulate API call
setTimeout(() => {
const row = button?.closest('tr');
if (row) {
row.style.transition = 'all 0.3s ease';
row.style.opacity = '0';
row.style.transform = 'translateX(-20px)';
setTimeout(() => {
row.remove();
RR.toast.show('Item deleted successfully', 'success');
}, 300);
}
}, 1000);
}
},
handleView: function(id) {
RR.toast.show(`View details for ID: ${id}`, 'info');
// Implement view modal or redirect
},
setupFilters: function() {
const filterSelects = document.querySelectorAll('.filter-select');
filterSelects.forEach(select => {
select.addEventListener('change', function() {
RR.tables.applyFilters();
});
});
},
applyFilters: function() {
const table = document.querySelector('.data-table');
if (!table) return;
const rows = table.querySelectorAll('tbody tr');
const filters = {};
// Get all filter values
document.querySelectorAll('.filter-select').forEach(select => {
if (select.value) {
filters[select.id] = select.value.toLowerCase();
}
});
// Apply filters
rows.forEach(row => {
let shouldShow = true;
Object.keys(filters).forEach(filterId => {
const filterValue = filters[filterId];
let cellText = '';
// Get cell text based on filter type
if (filterId.includes('role')) {
const roleElement = row.querySelector('.role-badge');
cellText = roleElement ? roleElement.textContent.toLowerCase() : '';
} else if (filterId.includes('status')) {
const statusElement = row.querySelector('.status-badge');
cellText = statusElement ? statusElement.textContent.toLowerCase() : '';
}
if (cellText && !cellText.includes(filterValue)) {
shouldShow = false;
}
});
row.style.display = shouldShow ? '' : 'none';
});
}
};
// Modals Module
RR.modals = {
init: function() {
this.setupModalTriggers();
this.setupModalClosing();
this.setupKeyboardNavigation();
},
setupModalTriggers: function() {
// Generic modal triggers
document.addEventListener('click', function(e) {
const trigger = e.target.closest('[data-modal]');
if (trigger) {
e.preventDefault();
const modalId = trigger.getAttribute('data-modal');
RR.modals.open(modalId);
}
});
},
setupModalClosing: function() {
document.addEventListener('click', function(e) {
// Close button
if (e.target.closest('.modal-close')) {
const modal = e.target.closest('.modal');
if (modal) RR.modals.close(modal.id);
}
// Backdrop click
if (e.target.classList.contains('modal')) {
RR.modals.close(e.target.id);
}
});
},
setupKeyboardNavigation: function() {
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
const openModal = document.querySelector('.modal[style*="flex"]');
if (openModal) {
RR.modals.close(openModal.id);
}
}
});
},
open: function(modalId) {
const modal = document.getElementById(modalId);
if (!modal) return;
modal.style.display = 'flex';
document.body.style.overflow = 'hidden';
// Focus first input
const firstInput = modal.querySelector('input, textarea, select');
if (firstInput) {
setTimeout(() => firstInput.focus(), 100);
}
// Add animation class if needed
const modalContent = modal.querySelector('.modal-content');
if (modalContent) {
modalContent.style.animation = 'modalAppear 0.3s ease-out';
}
},
close: function(modalId) {
const modal = document.getElementById(modalId);
if (!modal) return;
modal.style.display = 'none';
document.body.style.overflow = '';
// Reset form if exists
const form = modal.querySelector('form');
if (form) {
form.reset();
form.querySelectorAll('.error').forEach(el => el.classList.remove('error'));
form.querySelectorAll('.error-message').forEach(el => el.remove());
}
}
};
// Toast Notifications
RR.toast = {
show: function(message, type = 'info') {
// Remove existing toasts
document.querySelectorAll('.toast').forEach(toast => toast.remove());
const toast = document.createElement('div');
toast.className = `toast toast-${type}`;
toast.textContent = message;
toast.setAttribute('role', 'alert');
toast.setAttribute('aria-live', 'assertive');
document.body.appendChild(toast);
// Show with animation
setTimeout(() => toast.classList.add('show'), 100);
// Auto hide
setTimeout(() => {
toast.classList.remove('show');
setTimeout(() => {
if (toast.parentNode) {
toast.parentNode.removeChild(toast);
}
}, RR.config.animationDuration);
}, RR.config.toastDuration);
}
};
// Tooltips Module
RR.tooltips = {
init: function() {
this.createTooltips();
},
createTooltips: function() {
const elements = document.querySelectorAll('[title]');
elements.forEach(el => {
const title = el.getAttribute('title');
el.removeAttribute('title'); // Prevent default tooltip
el.addEventListener('mouseenter', () => {
RR.tooltips.show(el, title);
});
el.addEventListener('mouseleave', () => {
RR.tooltips.hide();
});
});
},
show: function(element, text) {
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.textContent = text;
tooltip.style.cssText = `
position: absolute;
background: var(--dark-color);
color: var(--white);
padding: var(--spacing-sm) var(--spacing-md);
border-radius: var(--border-radius);
font-size: var(--font-size-sm);
z-index: var(--z-tooltip);
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
white-space: nowrap;
`;
document.body.appendChild(tooltip);
const rect = element.getBoundingClientRect();
const tooltipRect = tooltip.getBoundingClientRect();
tooltip.style.left = rect.left + (rect.width - tooltipRect.width) / 2 + 'px';
tooltip.style.top = rect.top - tooltipRect.height - 8 + 'px';
setTimeout(() => tooltip.style.opacity = '1', 10);
},
hide: function() {
const tooltips = document.querySelectorAll('.tooltip');
tooltips.forEach(tooltip => {
tooltip.style.opacity = '0';
setTimeout(() => {
if (tooltip.parentNode) {
tooltip.parentNode.removeChild(tooltip);
}
}, 200);
});
}
};
// Lazy Loading Module
RR.lazyLoad = {
init: function() {
if ('IntersectionObserver' in window) {
this.setupImageLazyLoading();
this.setupContentLazyLoading();
}
},
setupImageLazyLoading: function() {
const images = document.querySelectorAll('img[data-src]');
if (images.length === 0) return;
const imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
imageObserver.unobserve(img);
}
});
});
images.forEach(img => imageObserver.observe(img));
},
setupContentLazyLoading: function() {
const lazyElements = document.querySelectorAll('.lazy-load');
if (lazyElements.length === 0) return;
const contentObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('loaded');
contentObserver.unobserve(entry.target);
}
});
});
lazyElements.forEach(el => contentObserver.observe(el));
}
};
// Performance Module
RR.performance = {
init: function() {
this.monitorPageLoad();
this.setupLinkPrefetching();
},
monitorPageLoad: function() {
window.addEventListener('load', function() {
if ('performance' in window) {
const loadTime = Math.round(performance.now());
console.log(`Page loaded in ${loadTime}ms`);
// Warn if load time is slow
if (loadTime > 3000) {
console.warn('Page load time is above 3 seconds. Consider optimization.');
}
// Send to analytics if available
if (typeof gtag !== 'undefined') {
gtag('event', 'timing_complete', {
name: 'load',
value: loadTime
});
}
}
});
},
setupLinkPrefetching: function() {
// Prefetch important pages on hover
const importantLinks = document.querySelectorAll('.nav-link, .btn-primary');
importantLinks.forEach(link => {
link.addEventListener('mouseenter', function() {
const url = this.href;
if (url && url.startsWith(window.location.origin)) {
RR.performance.prefetchPage(url);
}
});
});
},
prefetchPage: function(url) {
// Check if already prefetched
if (document.querySelector(`link[href="${url}"]`)) return;
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = url;
document.head.appendChild(link);
}
};
// Utility Functions
RR.utils = {
// Debounce function
debounce: function(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
},
// Throttle function
throttle: function(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
},
// Format numbers with commas
formatNumber: function(num) {
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
},
// Format currency
formatCurrency: function(amount, currency = 'USD') {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currency
}).format(amount);
},
// Format date
formatDate: function(date, options = {}) {
const defaultOptions = {
year: 'numeric',
month: 'short',
day: 'numeric'
};
return new Date(date).toLocaleDateString('en-US', {
...defaultOptions,
...options
});
},
// Get relative time
getRelativeTime: function(date) {
const rtf = new Intl.RelativeTimeFormat('en', { numeric: 'auto' });
const now = new Date();
const targetDate = new Date(date);
const diffInMs = targetDate - now;
const diffInDays = Math.floor(diffInMs / (1000 * 60 * 60 * 24));
if (Math.abs(diffInDays) < 1) {
const diffInHours = Math.floor(diffInMs / (1000 * 60 * 60));
return rtf.format(diffInHours, 'hour');
} else {
return rtf.format(diffInDays, 'day');
}
},
// API helper
api: async function(url, options = {}) {
const defaultOptions = {
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
}
};
try {
const response = await fetch(RR.config.apiUrl + url, {
...defaultOptions,
...options,
headers: { ...defaultOptions.headers, ...options.headers }
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error('API call failed:', error);
RR.toast.show('An error occurred. Please try again.', 'error');
throw error;
}
},
// Local storage helpers (with fallbacks)
storage: {
get: function(key) {
try {
return JSON.parse(localStorage.getItem(key));
} catch {
return null;
}
},
set: function(key, value) {
try {
localStorage.setItem(key, JSON.stringify(value));
return true;
} catch {
return false;
}
},
remove: function(key) {
try {
localStorage.removeItem(key);
return true;
} catch {
return false;
}
}
}
};
// Expose utilities globally
window.utils = RR.utils;
window.showToast = RR.toast.show;
})();
-------------------- END OF FILE --------------------
### FILE 36: includes/footer.php
- Type: PHP
- Size: 3.88 KB
- Path: includes
- Name: footer.php
------------------------------------------------------------
Relevant Reflex
Professional panel management and survey solutions for businesses worldwide.
Contact
Email: support@relevantreflex.com
Business Hours:
Monday - Friday: 9:00 AM - 6:00 PM
© Relevant Reflex. All rights reserved.