Project: RR shop 07 Oct 2025
ZIP File: public_html.zip
Extracted on: 2025-10-07 16:35:56
Total Files: 25
================================================================================
FILE: config.php
TYPE: PHP
SIZE: 1.35 KB
------------------------------------------------------------
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.");
}
}
// Site configuration
define('SITE_NAME', 'Relevant Reflex');
define('SITE_URL', 'https://your-domain.com'); // Update with your actual domain
define('SITE_DESCRIPTION', 'Professional Panel Management System');
// Security settings
session_start();
// Timezone setting
date_default_timezone_set('UTC');
// Error reporting (disable in production)
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
?>
-------------------- END OF FILE --------------------
FILE: database_schema.sql
TYPE: SQL
SIZE: 21.6 KB
------------------------------------------------------------
-- Relevant Reflex Panel Management System Database Schema
-- Create this database structure in phpMyAdmin
-- Make sure to update the database credentials in config.php
-- Create database (run this first if database doesn't exist)
-- CREATE DATABASE relevant_reflex_db CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
-- USE relevant_reflex_db;
-- --------------------------------------------------------
-- Table structure for table `users`
-- --------------------------------------------------------
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`email` varchar(255) NOT NULL,
`password_hash` varchar(255) NOT NULL,
`first_name` varchar(100) NOT NULL,
`last_name` varchar(100) NOT NULL,
`role` enum('admin','manager','user') NOT NULL DEFAULT 'user',
`status` enum('active','inactive','suspended') NOT NULL DEFAULT 'active',
`phone` varchar(20) DEFAULT NULL,
`avatar` varchar(255) DEFAULT NULL,
`last_login` timestamp NULL DEFAULT NULL,
`login_count` int(11) DEFAULT 0,
`email_verified` tinyint(1) DEFAULT 0,
`two_factor_enabled` tinyint(1) DEFAULT 0,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `email` (`email`),
KEY `idx_role` (`role`),
KEY `idx_status` (`status`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `panels`
-- --------------------------------------------------------
CREATE TABLE `panels` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`description` text,
`type` enum('survey','feedback','research','evaluation') DEFAULT 'survey',
`status` enum('draft','active','paused','completed','archived') DEFAULT 'draft',
`created_by` int(11) NOT NULL,
`target_responses` int(11) DEFAULT 100,
`current_responses` int(11) DEFAULT 0,
`completion_rate` decimal(5,2) DEFAULT 0.00,
`start_date` date DEFAULT NULL,
`end_date` date DEFAULT NULL,
`settings` json DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_created_by` (`created_by`),
KEY `idx_status` (`status`),
KEY `idx_type` (`type`),
KEY `idx_end_date` (`end_date`),
FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `panel_responses`
-- --------------------------------------------------------
CREATE TABLE `panel_responses` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`panel_id` int(11) NOT NULL,
`respondent_email` varchar(255) NOT NULL,
`respondent_name` varchar(255) DEFAULT NULL,
`response_data` json NOT NULL,
`completion_time` int(11) DEFAULT NULL COMMENT 'Time in seconds',
`ip_address` varchar(45) DEFAULT NULL,
`user_agent` text DEFAULT NULL,
`submitted_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_panel_id` (`panel_id`),
KEY `idx_submitted_at` (`submitted_at`),
KEY `idx_respondent_email` (`respondent_email`),
FOREIGN KEY (`panel_id`) REFERENCES `panels` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `supplies`
-- --------------------------------------------------------
CREATE TABLE `supplies` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`item_name` varchar(255) NOT NULL,
`description` text DEFAULT NULL,
`category` varchar(100) DEFAULT NULL,
`quantity` int(11) NOT NULL DEFAULT 1,
`unit_cost` decimal(10,2) NOT NULL DEFAULT 0.00,
`total_cost` decimal(12,2) GENERATED ALWAYS AS (`quantity` * `unit_cost`) STORED,
`supplier` varchar(255) DEFAULT NULL,
`supplier_contact` varchar(255) DEFAULT NULL,
`status` enum('ordered','pending','received','cancelled','returned') DEFAULT 'pending',
`order_date` date DEFAULT NULL,
`expected_delivery` date DEFAULT NULL,
`actual_delivery` date DEFAULT NULL,
`notes` text DEFAULT NULL,
`created_by` int(11) DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_status` (`status`),
KEY `idx_category` (`category`),
KEY `idx_order_date` (`order_date`),
KEY `idx_created_by` (`created_by`),
FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `finances`
-- --------------------------------------------------------
CREATE TABLE `finances` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`transaction_type` enum('revenue','expense','refund','adjustment') NOT NULL,
`amount` decimal(12,2) NOT NULL,
`description` varchar(500) NOT NULL,
`category` varchar(100) DEFAULT NULL,
`reference_id` varchar(100) DEFAULT NULL COMMENT 'External reference (invoice, order, etc.)',
`related_panel_id` int(11) DEFAULT NULL,
`related_supply_id` int(11) DEFAULT NULL,
`payment_method` varchar(50) DEFAULT NULL,
`transaction_date` date NOT NULL,
`due_date` date DEFAULT NULL,
`paid_date` date DEFAULT NULL,
`status` enum('pending','completed','cancelled','failed') DEFAULT 'completed',
`notes` text DEFAULT NULL,
`created_by` int(11) DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_transaction_type` (`transaction_type`),
KEY `idx_category` (`category`),
KEY `idx_transaction_date` (`transaction_date`),
KEY `idx_status` (`status`),
KEY `idx_related_panel` (`related_panel_id`),
KEY `idx_related_supply` (`related_supply_id`),
KEY `idx_created_by` (`created_by`),
FOREIGN KEY (`related_panel_id`) REFERENCES `panels` (`id`) ON DELETE SET NULL,
FOREIGN KEY (`related_supply_id`) REFERENCES `supplies` (`id`) ON DELETE SET NULL,
FOREIGN KEY (`created_by`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `support_tickets`
-- --------------------------------------------------------
CREATE TABLE `support_tickets` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`ticket_number` varchar(20) NOT NULL,
`user_id` int(11) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`email` varchar(255) NOT NULL,
`subject` varchar(500) NOT NULL,
`message` text NOT NULL,
`priority` enum('low','medium','high','critical') DEFAULT 'medium',
`status` enum('open','in_progress','resolved','closed','reopened') DEFAULT 'open',
`category` varchar(100) DEFAULT NULL,
`assigned_to` int(11) DEFAULT NULL,
`resolution` text DEFAULT NULL,
`resolved_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `ticket_number` (`ticket_number`),
KEY `idx_status` (`status`),
KEY `idx_priority` (`priority`),
KEY `idx_user_id` (`user_id`),
KEY `idx_assigned_to` (`assigned_to`),
KEY `idx_created_at` (`created_at`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
FOREIGN KEY (`assigned_to`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `system_settings`
-- --------------------------------------------------------
CREATE TABLE `system_settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`setting_key` varchar(100) NOT NULL,
`setting_value` text DEFAULT NULL,
`setting_type` enum('string','integer','boolean','json','date') DEFAULT 'string',
`description` varchar(500) DEFAULT NULL,
`is_public` tinyint(1) DEFAULT 0,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `setting_key` (`setting_key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `activity_logs`
-- --------------------------------------------------------
CREATE TABLE `activity_logs` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`action` varchar(100) NOT NULL,
`resource_type` varchar(50) DEFAULT NULL COMMENT 'panel, user, supply, etc.',
`resource_id` int(11) DEFAULT NULL,
`description` varchar(500) NOT NULL,
`metadata` json DEFAULT NULL,
`ip_address` varchar(45) DEFAULT NULL,
`user_agent` text DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_action` (`action`),
KEY `idx_resource` (`resource_type`, `resource_id`),
KEY `idx_created_at` (`created_at`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `demand_analytics`
-- --------------------------------------------------------
CREATE TABLE `demand_analytics` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`category` varchar(100) NOT NULL,
`demand_level` enum('low','medium','high') NOT NULL,
`growth_rate` decimal(5,2) DEFAULT NULL COMMENT 'Percentage growth',
`market_score` int(11) DEFAULT NULL COMMENT '0-100 score',
`forecast_accuracy` decimal(5,2) DEFAULT NULL,
`recorded_date` date NOT NULL,
`data_source` varchar(100) DEFAULT NULL,
`metadata` json DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_category` (`category`),
KEY `idx_recorded_date` (`recorded_date`),
KEY `idx_demand_level` (`demand_level`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Table structure for table `notifications`
-- --------------------------------------------------------
CREATE TABLE `notifications` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL,
`title` varchar(255) NOT NULL,
`message` text NOT NULL,
`type` enum('info','success','warning','error') DEFAULT 'info',
`is_read` tinyint(1) DEFAULT 0,
`action_url` varchar(500) DEFAULT NULL,
`expires_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_is_read` (`is_read`),
KEY `idx_created_at` (`created_at`),
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
-- Insert default system settings
-- --------------------------------------------------------
INSERT INTO `system_settings` (`setting_key`, `setting_value`, `setting_type`, `description`, `is_public`) VALUES
('site_name', 'Relevant Reflex', 'string', 'Site name displayed throughout the application', 1),
('timezone', 'America/New_York', 'string', 'Default system timezone', 0),
('currency', 'USD', 'string', 'Default currency for financial transactions', 0),
('date_format', 'M j, Y', 'string', 'Default date format', 0),
('pagination_limit', '20', 'integer', 'Default number of items per page', 0),
('email_notifications', '1', 'boolean', 'Enable email notifications system-wide', 0),
('sms_notifications', '0', 'boolean', 'Enable SMS notifications system-wide', 0),
('session_timeout', '1800', 'integer', 'Session timeout in seconds (30 minutes)', 0),
('max_file_upload', '5242880', 'integer', 'Maximum file upload size in bytes (5MB)', 0),
('maintenance_mode', '0', 'boolean', 'Enable maintenance mode', 0);
-- --------------------------------------------------------
-- Insert sample admin user (password: admin123)
-- --------------------------------------------------------
INSERT INTO `users` (`username`, `email`, `password_hash`, `first_name`, `last_name`, `role`, `status`, `email_verified`) VALUES
('admin', 'admin@relevantreflex.com', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'System', 'Administrator', 'admin', 'active', 1);
-- --------------------------------------------------------
-- Insert sample data for demonstration
-- --------------------------------------------------------
-- Sample panels
INSERT INTO `panels` (`name`, `description`, `type`, `status`, `created_by`, `target_responses`, `current_responses`, `completion_rate`, `start_date`, `end_date`) VALUES
('Customer Satisfaction Q3 2025', 'Quarterly customer satisfaction survey to measure service quality and identify improvement areas.', 'survey', 'active', 1, 500, 245, 49.00, '2025-08-15', '2025-09-30'),
('Product Feedback Survey', 'Collecting feedback on our latest product features and user experience.', 'feedback', 'draft', 1, 300, 0, 0.00, NULL, '2025-10-15'),
('Market Research Panel Q2', 'Comprehensive market analysis for Q2 strategic planning and competitive positioning.', 'research', 'completed', 1, 1000, 1024, 102.40, '2025-07-20', '2025-08-31'),
('Employee Engagement Survey', 'Internal survey to measure employee satisfaction and engagement levels.', 'survey', 'active', 1, 150, 67, 44.67, '2025-08-28', '2025-09-15');
-- Sample supplies
INSERT INTO `supplies` (`item_name`, `description`, `category`, `quantity`, `unit_cost`, `supplier`, `status`, `order_date`, `expected_delivery`) VALUES
('Survey Tablets', 'iPad Air for field data collection', 'Equipment', 25, 299.99, 'TechCorp Solutions', 'received', '2025-08-15', '2025-08-25'),
('Data Collection Software License', 'Annual license for survey platform', 'Software', 50, 49.99, 'DataSoft Inc', 'received', '2025-08-20', '2025-08-22'),
('Mobile Hotspots', 'Portable internet connectivity devices', 'Equipment', 15, 89.99, 'ConnectTech Ltd', 'pending', '2025-09-01', '2025-09-10'),
('Office Supplies', 'General office supplies for Q3', 'Office', 100, 12.50, 'OfficeMax Pro', 'ordered', '2025-09-02', '2025-09-08'),
('Cloud Storage Subscription', 'Enterprise cloud storage solution', 'Software', 1, 199.99, 'CloudProvider Inc', 'received', '2025-07-01', '2025-07-01');
-- Sample financial transactions
INSERT INTO `finances` (`transaction_type`, `amount`, `description`, `category`, `transaction_date`, `status`) VALUES
('revenue', 2500.00, 'Panel completion payment - Technology Survey', 'Panel Revenue', '2025-09-03', 'completed'),
('expense', -450.00, 'Cloud hosting monthly subscription', 'Infrastructure', '2025-09-02', 'completed'),
('revenue', 1800.00, 'Market research project payment', 'Research Revenue', '2025-09-01', 'completed'),
('expense', -320.00, 'Software licenses renewal', 'Software', '2025-08-31', 'completed'),
('revenue', 3200.00, 'Enterprise client onboarding fee', 'New Business', '2025-08-30', 'completed'),
('expense', -125.00, 'Office supplies purchase', 'Office', '2025-08-29', 'completed'),
('revenue', 1500.00, 'Consultation services', 'Services', '2025-08-28', 'completed');
-- Sample support tickets
INSERT INTO `support_tickets` (`ticket_number`, `user_id`, `name`, `email`, `subject`, `message`, `priority`, `status`) VALUES
('TK-2025-001', 1, 'John Smith', 'john@example.com', 'Dashboard loading slowly', 'The dashboard takes too long to load, especially the charts section. This has been happening for the past few days.', 'medium', 'in_progress'),
('TK-2025-002', NULL, 'Sarah Johnson', 'sarah@external.com', 'Export function not working', 'When I try to export panel data to CSV, I get an error message. Please help resolve this issue.', 'high', 'resolved'),
('TK-2025-003', 1, 'Mike Davis', 'mike@contractor.com', 'User permissions question', 'I need clarification on user permission levels and how to assign different access rights to team members.', 'low', 'open');
-- Sample activity logs
INSERT INTO `activity_logs` (`user_id`, `action`, `resource_type`, `resource_id`, `description`, `ip_address`) VALUES
(1, 'login', NULL, NULL, 'User logged in successfully', '192.168.1.100'),
(1, 'create', 'panel', 1, 'Created new panel: Customer Satisfaction Q3 2025', '192.168.1.100'),
(1, 'update', 'user', 1, 'Updated user profile information', '192.168.1.100'),
(1, 'create', 'supply', 1, 'Added new supply item: Survey Tablets', '192.168.1.100');
-- Sample demand analytics
INSERT INTO `demand_analytics` (`category`, `demand_level`, `growth_rate`, `market_score`, `forecast_accuracy`, `recorded_date`) VALUES
('Technology Panels', 'high', 23.50, 92, 87.50, '2025-09-01'),
('Healthcare Surveys', 'medium', 12.30, 76, 82.10, '2025-09-01'),
('Financial Services', 'high', 18.70, 88, 85.30, '2025-09-01'),
('Retail Analytics', 'low', -5.20, 54, 78.90, '2025-09-01'),
('Education Research', 'medium', 8.40, 69, 80.20, '2025-09-01');
-- --------------------------------------------------------
-- Create indexes for better performance
-- --------------------------------------------------------
-- Additional composite indexes for common queries
ALTER TABLE `panel_responses` ADD INDEX `idx_panel_date` (`panel_id`, `submitted_at`);
ALTER TABLE `finances` ADD INDEX `idx_type_date` (`transaction_type`, `transaction_date`);
ALTER TABLE `activity_logs` ADD INDEX `idx_user_date` (`user_id`, `created_at`);
ALTER TABLE `supplies` ADD INDEX `idx_status_date` (`status`, `order_date`);
-- --------------------------------------------------------
-- Create views for common queries
-- --------------------------------------------------------
-- View for panel statistics
CREATE VIEW `panel_stats` AS
SELECT
p.id,
p.name,
p.status,
p.target_responses,
p.current_responses,
p.completion_rate,
COUNT(pr.id) as actual_responses,
p.created_at,
CONCAT(u.first_name, ' ', u.last_name) as created_by_name
FROM panels p
LEFT JOIN panel_responses pr ON p.id = pr.panel_id
LEFT JOIN users u ON p.created_by = u.id
GROUP BY p.id;
-- View for financial summary
CREATE VIEW `financial_summary` AS
SELECT
DATE_FORMAT(transaction_date, '%Y-%m') as month,
SUM(CASE WHEN transaction_type = 'revenue' THEN amount ELSE 0 END) as total_revenue,
SUM(CASE WHEN transaction_type = 'expense' THEN ABS(amount) ELSE 0 END) as total_expenses,
SUM(CASE WHEN transaction_type = 'revenue' THEN amount ELSE -ABS(amount) END) as net_profit,
COUNT(*) as total_transactions
FROM finances
WHERE status = 'completed'
GROUP BY DATE_FORMAT(transaction_date, '%Y-%m')
ORDER BY month DESC;
-- View for user activity summary
CREATE VIEW `user_activity_summary` AS
SELECT
u.id,
u.username,
u.email,
u.last_login,
COUNT(al.id) as total_activities,
MAX(al.created_at) as last_activity
FROM users u
LEFT JOIN activity_logs al ON u.id = al.user_id
GROUP BY u.id;
-- --------------------------------------------------------
-- Create stored procedures for common operations
-- --------------------------------------------------------
DELIMITER //
-- Procedure to update panel completion rate
CREATE PROCEDURE UpdatePanelStats(IN panel_id INT)
BEGIN
UPDATE panels
SET
current_responses = (SELECT COUNT(*) FROM panel_responses WHERE panel_id = panel_id),
completion_rate = (
(SELECT COUNT(*) FROM panel_responses WHERE panel_id = panel_id) / target_responses * 100
)
WHERE id = panel_id;
END//
-- Procedure to log user activity
CREATE PROCEDURE LogActivity(
IN user_id INT,
IN action VARCHAR(100),
IN resource_type VARCHAR(50),
IN resource_id INT,
IN description VARCHAR(500),
IN ip_address VARCHAR(45)
)
BEGIN
INSERT INTO activity_logs (user_id, action, resource_type, resource_id, description, ip_address)
VALUES (user_id, action, resource_type, resource_id, description, ip_address);
END//
DELIMITER ;
-- --------------------------------------------------------
-- Create triggers for automatic updates
-- --------------------------------------------------------
DELIMITER //
-- Trigger to update panel stats when response is added
CREATE TRIGGER update_panel_stats_after_response
AFTER INSERT ON panel_responses
FOR EACH ROW
BEGIN
CALL UpdatePanelStats(NEW.panel_id);
END//
-- Trigger to log user login
CREATE TRIGGER log_user_login
AFTER UPDATE ON users
FOR EACH ROW
BEGIN
IF NEW.last_login != OLD.last_login THEN
INSERT INTO activity_logs (user_id, action, description)
VALUES (NEW.id, 'login', 'User logged in');
END IF;
END//
DELIMITER ;
-- --------------------------------------------------------
-- Grant appropriate permissions (run as database administrator)
-- --------------------------------------------------------
-- Create application user with limited permissions
-- CREATE USER 'rr_app_user'@'localhost' IDENTIFIED BY 'secure_password_here';
-- GRANT SELECT, INSERT, UPDATE, DELETE ON relevant_reflex_db.* TO 'rr_app_user'@'localhost';
-- GRANT EXECUTE ON relevant_reflex_db.* TO 'rr_app_user'@'localhost';
-- FLUSH PRIVILEGES;
-- --------------------------------------------------------
-- Performance optimization queries
-- --------------------------------------------------------
-- Analyze table performance (run periodically)
-- ANALYZE TABLE users, panels, panel_responses, supplies, finances, support_tickets;
-- Optimize tables (run during maintenance)
-- OPTIMIZE TABLE users, panels, panel_responses, supplies, finances, support_tickets;
-- --------------------------------------------------------
-- End of schema
-- --------------------------------------------------------
-------------------- END OF FILE --------------------
FILE: demand.php
TYPE: PHP
SIZE: 10.71 KB
------------------------------------------------------------
8.7,
'growth_rate' => 15.3,
'market_score' => 92,
'forecast_accuracy' => 87.5
];
$demand_trends = [
['category' => 'Technology Panels', 'demand_level' => 'High', 'growth' => '+23%', 'priority' => 'High'],
['category' => 'Healthcare Surveys', 'demand_level' => 'Medium', 'growth' => '+12%', 'priority' => 'Medium'],
['category' => 'Financial Services', 'demand_level' => 'High', 'growth' => '+18%', 'priority' => 'High'],
['category' => 'Retail Analytics', 'demand_level' => 'Low', 'growth' => '-5%', 'priority' => 'Low'],
['category' => 'Education Research', 'demand_level' => 'Medium', 'growth' => '+8%', 'priority' => 'Medium']
];
$market_insights = [
['insight' => 'Technology sector showing strongest demand growth', 'impact' => 'High', 'action' => 'Expand tech panel offerings'],
['insight' => 'Q3 demand exceeded forecasts by 15%', 'impact' => 'Medium', 'action' => 'Adjust Q4 capacity planning'],
['insight' => 'Mobile panel participation up 34%', 'impact' => 'High', 'action' => 'Optimize mobile experience'],
['insight' => 'Weekend response rates declining', 'impact' => 'Low', 'action' => 'Review scheduling strategy']
];
include 'includes/header.php';
?>
📈
Demand 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: error-404.html
TYPE: HTML
SIZE: 6.89 KB
------------------------------------------------------------
Page Not Found - Relevant Reflex
RR
404
Page Not Found
Sorry, the page you are looking for doesn't exist or has been moved.
Let's get you back on track with our panel management system.
-------------------- END OF FILE --------------------
FILE: error-500.html
TYPE: HTML
SIZE: 8.78 KB
------------------------------------------------------------
Server Error - Relevant Reflex
RR
500
Internal Server Error
We're experiencing technical difficulties with our panel management system.
Our team has been notified and is working to resolve this issue.
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: finance.php
TYPE: PHP
SIZE: 13.13 KB
------------------------------------------------------------
125450,
'monthly_revenue' => 45230,
'total_expenses' => 32100,
'net_profit' => 93350,
'revenue_growth' => 12.5,
'expense_reduction' => -3.1,
'profit_margin' => 74.4
];
$recent_transactions = [
['id' => 1, 'type' => 'Revenue', 'amount' => 2500.00, 'description' => 'Panel completion payment - Tech Survey', 'date' => '2025-09-03', 'category' => 'Panel Revenue'],
['id' => 2, 'type' => 'Expense', 'amount' => -450.00, 'description' => 'Cloud hosting monthly fee', 'date' => '2025-09-02', 'category' => 'Infrastructure'],
['id' => 3, 'type' => 'Revenue', 'amount' => 1800.00, 'description' => 'Market research project payment', 'date' => '2025-09-01', 'category' => 'Research Revenue'],
['id' => 4, 'type' => 'Expense', 'amount' => -320.00, 'description' => 'Software licenses renewal', 'date' => '2025-08-31', 'category' => 'Software'],
['id' => 5, 'type' => 'Revenue', 'amount' => 3200.00, 'description' => 'Enterprise client onboarding', 'date' => '2025-08-30', 'category' => 'New Business']
];
include 'includes/header.php';
?>
📊
$
Monthly Revenue
+8.2% vs last month
📈
$
Net Profit
+18.7% growth
Financial Insights & Recommendations
📈
Revenue Growth Acceleration
Revenue growth has increased 23% over the past quarter, primarily driven by enterprise client acquisitions.
Action: Scale enterprise sales team
💡
Cost Optimization Opportunity
Infrastructure costs can be reduced by 20% through annual cloud service commitments.
Potential Savings: $8,400/year
🎯
Profitability Milestone
On track to exceed annual profit target by 15% if current trends continue.
Forecast: $180K+ annual profit
-------------------- END OF FILE --------------------
FILE: index.php
TYPE: PHP
SIZE: 6.32 KB
------------------------------------------------------------
1248,
'active_panels' => 23,
'pending_supplies' => 45,
'revenue_month' => 12450,
'growth_rate' => 15.8
];
$recent_activities = [
['time' => '2 hours ago', 'text' => 'New panel "Customer Satisfaction Q3" created'],
['time' => '5 hours ago', 'text' => 'Supply order #1247 completed successfully'],
['time' => '1 day ago', 'text' => 'Finance report generated for August'],
['time' => '2 days ago', 'text' => 'User permissions updated for Marketing team'],
['time' => '3 days ago', 'text' => 'New integration with analytics platform']
];
} catch (Exception $e) {
error_log("Dashboard data error: " . $e->getMessage());
$dashboard_stats = ['error' => true];
}
include 'includes/header.php';
?>
Unable to load dashboard data. Please check your database connection.
👥
Total Users
+12% this month
📦
Pending Supplies
In progress
Quick Actions
👥
Manage Users
Add, edit or remove system users
📋
Create Panel
Start a new survey or panel
📊
View Reports
Access financial and performance reports
⚙️
System Settings
Configure platform preferences
-------------------- END OF FILE --------------------
FILE: maintenance.html
TYPE: HTML
SIZE: 0 B
------------------------------------------------------------
-------------------- END OF FILE --------------------
FILE: panel.php
TYPE: PHP
SIZE: 11.53 KB
------------------------------------------------------------
1,
'name' => 'Customer Satisfaction Q3 2025',
'status' => 'Active',
'responses' => 245,
'target_responses' => 500,
'created' => '2025-08-15',
'end_date' => '2025-09-30',
'completion_rate' => 49
],
[
'id' => 2,
'name' => 'Product Feedback Survey',
'status' => 'Draft',
'responses' => 0,
'target_responses' => 300,
'created' => '2025-09-01',
'end_date' => '2025-10-15',
'completion_rate' => 0
],
[
'id' => 3,
'name' => 'Market Research Panel Q2',
'status' => 'Completed',
'responses' => 1024,
'target_responses' => 1000,
'created' => '2025-07-20',
'end_date' => '2025-08-31',
'completion_rate' => 100
],
[
'id' => 4,
'name' => 'Employee Engagement Survey',
'status' => 'Active',
'responses' => 67,
'target_responses' => 150,
'created' => '2025-08-28',
'end_date' => '2025-09-15',
'completion_rate' => 45
]
];
include 'includes/header.php';
?>
🟢
$p['status'] === 'Active')); ?>
Active Panels
Panel Details
Status
Progress
Responses
End Date
Actions
/ target
diff($now)->days;
?>
format('M j, Y'); ?>
$now ? $days_left . ' days left' : 'Expired'; ?>
👁️
✏️
📊
🚀
🗑️
-------------------- END OF FILE --------------------
FILE: README.md
TYPE: MD
SIZE: 8.86 KB
------------------------------------------------------------
# Relevant Reflex Panel Management System
A comprehensive, mobile-responsive panel management system built with PHP, MySQL, and modern web technologies. Designed for fast loading on shared hosting environments like Hostinger.
## 🚀 Features
- **Complete Panel Management**: Create, manage, and analyze survey panels
- **User Management**: Role-based access control with admin, manager, and user roles
- **Supply Chain Management**: Track inventory, suppliers, and procurement
- **Demand Analytics**: Market analysis and forecasting tools
- **Financial Management**: Revenue tracking and expense management
- **Support System**: Built-in ticketing and help center
- **Real-time Dashboard**: Live statistics and performance metrics
- **Mobile Responsive**: 100% mobile-friendly design
- **SEO Optimized**: Search engine friendly structure
- **Fast Loading**: Optimized for shared hosting environments
## 📁 File Structure
```
relevant-reflex/
├── index.php # Dashboard home page
├── users.php # User management
├── panel.php # Panel management
├── supply.php # Supply management
├── demand.php # Demand analysis
├── finance.php # Financial management
├── support.php # Support center
├── settings.php # System settings
├── config.php # Database configuration
├── .htaccess # Apache configuration
├── database_schema.sql # Database structure
├── robots.txt # Search engine directives
├── sitemap.xml # Site structure for SEO
├── includes/
│ ├── header.php # Site header
│ ├── footer.php # SEO footer
│ └── navigation.php # Navigation menu
├── assets/
│ ├── css/
│ │ ├── main.css # Main stylesheet
│ │ ├── responsive.css # Mobile responsiveness
│ │ └── dashboard.css # Dashboard styles
│ ├── js/
│ │ ├── main.js # Core JavaScript
│ │ └── dashboard.js # Dashboard functionality
│ └── images/
│ └── (your images here)
└── error-pages/
├── 404.html # Page not found
├── 500.html # Server error
└── maintenance.html # Maintenance mode
```
## 🛠️ Installation Instructions
### Prerequisites
- **Web Hosting**: Shared hosting account (Hostinger, cPanel, etc.)
- **PHP**: Version 7.4 or higher
- **MySQL**: Version 5.7 or higher
- **Apache**: With mod_rewrite enabled
### Step 1: Download and Extract
1. Download all the files provided in the artifacts
2. Create a new folder named `relevant-reflex` on your computer
3. Copy all files into this folder maintaining the directory structure
### Step 2: Database Setup
1. **Login to phpMyAdmin** via your hosting control panel
2. **Create a new database**:
- Database name: `relevant_reflex_db` (or your preferred name)
- Collation: `utf8mb4_unicode_ci`
3. **Import the schema**:
- Click on your database
- Go to "Import" tab
- Choose the `database_schema.sql` file
- Click "Go" to execute
### Step 3: Configuration
1. **Edit config.php**:
```php
define('DB_HOST', 'localhost');
define('DB_USER', 'your_db_username'); // From your hosting panel
define('DB_PASS', 'your_db_password'); // From your hosting panel
define('DB_NAME', 'relevant_reflex_db'); // Your database name
define('SITE_URL', 'https://yourdomain.com'); // Your actual domain
```
2. **Update site settings** in other files if needed
### Step 4: File Upload
1. **Connect via FTP/File Manager**:
- Use your hosting panel's file manager or FTP client
- Navigate to `public_html` directory (or your domain's root)
2. **Upload files**:
- Upload all files maintaining the folder structure
- Ensure permissions are set correctly:
- Files: 644
- Directories: 755
- config.php: 600 (more secure)
### Step 5: Testing
1. **Visit your website**: `https://yourdomain.com`
2. **Default admin login**:
- Username: `admin`
- Email: `admin@relevantreflex.com`
- Password: `admin123`
- **⚠️ Change this immediately after first login!**
3. **Test all features**:
- Dashboard loading
- User management
- Panel creation
- Mobile responsiveness
### Step 6: Security Hardening
1. **Change default admin password**
2. **Update config.php** with strong database credentials
3. **Enable SSL certificate** (usually free with hosting)
4. **Uncomment HTTPS redirect** in .htaccess:
```apache
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
```
## 🎨 Customization
### Changing Colors and Theme
All colors are centralized in `assets/css/main.css` at the top:
```css
:root {
--primary-color: #0066cc; /* Change main brand color */
--primary-hover: #0052a3; /* Hover state */
--success-color: #28a745; /* Success messages */
--warning-color: #ffc107; /* Warnings */
--danger-color: #dc3545; /* Errors */
/* ... more color variables */
}
```
Simply update these values to match your brand colors.
### Adding Your Logo
1. **Replace the RR logo**:
- Update the `.logo` content in `includes/navigation.php`
- Or replace with an image: ` `
2. **Update favicon**:
- Add your `favicon.ico` to the root directory
- Update the reference in `includes/header.php`
### Custom Styling
- **Main styles**: `assets/css/main.css`
- **Mobile styles**: `assets/css/responsive.css`
- **Dashboard styles**: `assets/css/dashboard.css`
## 📱 Mobile Optimization
The system is built mobile-first with:
- **Responsive Grid System**: Adapts to all screen sizes
- **Touch-Friendly Interface**: 44px minimum touch targets
- **Optimized Navigation**: Hamburger menu on mobile
- **Fast Loading**: Optimized assets and caching
- **Progressive Enhancement**: Works without JavaScript
## 🔧 Maintenance
### Regular Tasks
1. **Database Backups**: Weekly automated backups recommended
2. **Update Dependencies**: Keep PHP and MySQL updated
3. **Monitor Performance**: Check load times and optimize
4. **Security Updates**: Regular security audits
5. **Content Updates**: Keep information current
### Performance Optimization
1. **Enable Gzip Compression** (included in .htaccess)
2. **Optimize Images**: Use WebP format when possible
3. **Monitor Database**: Run `OPTIMIZE TABLE` monthly
4. **Cache Headers**: Properly configured in .htaccess
5. **CDN Integration**: Consider using a CDN for static assets
## 🔐 Security Features
- **SQL Injection Protection**: PDO prepared statements
- **XSS Prevention**: Input sanitization and CSP headers
- **CSRF Protection**: Session-based token validation
- **Secure Headers**: Comprehensive security headers
- **File Upload Security**: Restricted file types and locations
- **Access Control**: Role-based permissions
## 🆘 Troubleshooting
### Common Issues
**1. Database Connection Error**
```
Solution: Check config.php credentials and database server status
```
**2. Page Not Found (404)**
```
Solution: Verify .htaccess file is uploaded and mod_rewrite is enabled
```
**3. Slow Loading**
```
Solution: Enable compression, check hosting performance, optimize images
```
**4. Mobile Display Issues**
```
Solution: Clear browser cache, check responsive.css is loaded
```
**5. JavaScript Not Working**
```
Solution: Check browser console for errors, verify JS files are accessible
```
### Getting Help
1. **Check Error Logs**: In your hosting control panel
2. **Browser Console**: F12 to check for JavaScript errors
3. **PHP Error Display**: Temporarily enable in config.php for debugging
4. **Hosting Support**: Contact your hosting provider for server issues
## 📞 Support
For technical support and customization services:
- **Email**: support@relevantreflex.com
- **Documentation**: Check inline comments in code files
- **Updates**: Monitor for system updates and security patches
## 📝 License
This system is proprietary software developed for Relevant Reflex. All rights reserved.
## 🚀 Quick Start Checklist
- [ ] Create database in phpMyAdmin
- [ ] Import database_schema.sql
- [ ] Update config.php with database credentials
- [ ] Upload all files to web server
- [ ] Set correct file permissions
- [ ] Test login with admin/admin123
- [ ] Change default admin password
- [ ] Customize colors and branding
- [ ] Enable SSL and HTTPS redirect
- [ ] Test all functionality
- [ ] Setup regular backups
## Version Information
- **Version**: 1.0.0
- **Release Date**: September 2025
- **PHP Compatibility**: 7.4+
- **MySQL Compatibility**: 5.7+
- **Browser Support**: All modern browsers, IE11+
---
**Important**: Always backup your database and files before making changes or updates.
-------------------- END OF FILE --------------------
FILE: robots.txt
TYPE: TXT
SIZE: 2.16 KB
------------------------------------------------------------
# Relevant Reflex Panel Management System
# Robots.txt file for search engine optimization
User-agent: *
# Allow access to main pages
Allow: /
Allow: /index.php
Allow: /users.php
Allow: /panel.php
Allow: /supply.php
Allow: /demand.php
Allow: /finance.php
Allow: /support.php
Allow: /settings.php
# Allow access to static assets
Allow: /assets/css/
Allow: /assets/js/
Allow: /assets/images/
# Disallow sensitive files and directories
Disallow: /config.php
Disallow: /database_schema.sql
Disallow: /.htaccess
Disallow: /includes/
Disallow: /logs/
Disallow: /backups/
Disallow: /temp/
Disallow: /cache/
Disallow: /admin/
Disallow: /api/
Disallow: /private/
# Disallow URL parameters that might create duplicate content
Disallow: /*?*
Disallow: /*&*
Disallow: /*/search?*
Disallow: /*/filter?*
# Disallow error pages
Disallow: /error-*
Disallow: /404.html
Disallow: /500.html
Disallow: /maintenance.html
# Block access to development and testing files
Disallow: /test/
Disallow: /dev/
Disallow: /staging/
Disallow: /*.bak
Disallow: /*.tmp
Disallow: /*.log
# Block common exploits and security probes
Disallow: /wp-admin/
Disallow: /wordpress/
Disallow: /wp-content/
Disallow: /admin.php
Disallow: /administrator/
Disallow: /phpmyadmin/
Disallow: /phpMyAdmin/
# Block unwanted file types
Disallow: /*.sql$
Disallow: /*.zip$
Disallow: /*.tar.gz$
Disallow: /*.bak$
Disallow: /*.conf$
Disallow: /*.ini$
# Sitemap location
Sitemap: https://yourdomain.com/sitemap.xml
# Crawl delay for respectful crawling (optional)
# Crawl-delay: 1
# Specific rules for different bots (optional)
# Google Bot - allow everything we want indexed
User-agent: Googlebot
Allow: /
Disallow: /config.php
Disallow: /includes/
Disallow: /*?*
# Bing Bot
User-agent: Bingbot
Allow: /
Disallow: /config.php
Disallow: /includes/
# Block aggressive bots that might overload the server
User-agent: AhrefsBot
Disallow: /
User-agent: MJ12bot
Disallow: /
User-agent: SemrushBot
Disallow: /
User-agent: DotBot
Disallow: /
# Allow social media bots for link previews
User-agent: facebookexternalhit
Allow: /
User-agent: Twitterbot
Allow: /
User-agent: LinkedInBot
Allow: /
# Note: Update "yourdomain.com" with your actual domain name
-------------------- END OF FILE --------------------
FILE: settings.php
TYPE: PHP
SIZE: 25.81 KB
------------------------------------------------------------
'Relevant Reflex',
'timezone' => 'America/New_York',
'date_format' => 'M j, Y',
'currency' => 'USD',
'two_factor' => true,
'session_timeout' => 30,
'email_notifications' => true,
'sms_notifications' => false,
'weekly_reports' => true
];
include 'includes/header.php';
?>
✅
✅
✅
General Settings
Company Name
This appears in reports and communications
Date Format
>Sep 3, 2025
>3 Sep 2025
>2025-09-03
>09/03/2025
Save Changes
Third-Party Integrations
Track user behavior and panel performance
Configure
Sync panel participants with email campaigns
Connect
Sync customer data and panel insights
Connect
Process payments and manage subscriptions
Configure
Appearance Settings
Theme
Light
Dark
Auto (System Preference)
Navigation Position
Compact interface mode
Show more content in less space
Apply Changes
Reset to Default
Account Management
👤
Profile Information
Update your personal details and contact information
Edit Profile
📊
Usage Statistics
View your platform usage and activity history
View Stats
💾
Data Export
Download your complete data and settings backup
Export Data
⚠️
Delete Account
Permanently delete your account and all data
Delete Account
-------------------- END OF FILE --------------------
FILE: sitemap.xml
TYPE: XML
SIZE: 5.24 KB
------------------------------------------------------------
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: supply.php
TYPE: PHP
SIZE: 12.07 KB
------------------------------------------------------------
1, 'item_name' => 'Survey Tablets', 'quantity' => 25, 'unit_cost' => 299.99, 'supplier' => 'TechCorp', 'status' => 'Received', 'order_date' => '2025-08-15'],
['id' => 2, 'item_name' => 'Data Collection Software License', 'quantity' => 50, 'unit_cost' => 49.99, 'supplier' => 'DataSoft Inc', 'status' => 'Active', 'order_date' => '2025-08-20'],
['id' => 3, 'item_name' => 'Mobile Hotspots', 'quantity' => 15, 'unit_cost' => 89.99, 'supplier' => 'ConnectTech', 'status' => 'Pending', 'order_date' => '2025-09-01'],
['id' => 4, 'item_name' => 'Office Supplies', 'quantity' => 100, 'unit_cost' => 12.50, 'supplier' => 'OfficeMax', 'status' => 'Ordered', 'order_date' => '2025-09-02'],
['id' => 5, 'item_name' => 'Cloud Storage Subscription', 'quantity' => 1, 'unit_cost' => 199.99, 'supplier' => 'CloudProvider', 'status' => 'Active', 'order_date' => '2025-07-01']
];
// Calculate totals
$total_items = count($supplies);
$total_cost = array_sum(array_map(fn($item) => $item['quantity'] * $item['unit_cost'], $supplies));
$pending_items = count(array_filter($supplies, fn($item) => $item['status'] === 'Pending'));
$active_subscriptions = count(array_filter($supplies, fn($item) => $item['status'] === 'Active'));
include 'includes/header.php';
?>
📦
Total Items
Across all categories
💰
$
Total Investment
Current inventory value
⏳
Pending Orders
Awaiting delivery
🔄
Active Subscriptions
Monthly services
Item Details
Supplier
Quantity
Unit Cost
Total Cost
Status
Actions
$
$
✏️
👁️
📍
🗑️
Supply Chain Insights
📈
Cost Trend
Supply costs decreased by 8% compared to last quarter
⚡
Fastest Supplier
TechCorp delivers 2 days faster than average
💡
Recommendation
Consider bulk ordering tablets for 15% cost savings
-------------------- END OF FILE --------------------
FILE: support.php
TYPE: PHP
SIZE: 12.32 KB
------------------------------------------------------------
3,
'avg_response_time' => '2.4 hours',
'satisfaction_rate' => '98.5%',
'resolved_today' => 12
];
$recent_tickets = [
['id' => 1001, 'subject' => 'Dashboard loading slowly', 'status' => 'In Progress', 'priority' => 'Medium', 'created' => '2025-09-02'],
['id' => 1002, 'subject' => 'Export function not working', 'status' => 'Resolved', 'priority' => 'High', 'created' => '2025-09-01'],
['id' => 1003, 'subject' => 'User permissions question', 'status' => 'Open', 'priority' => 'Low', 'created' => '2025-08-31']
];
$faq_items = [
['question' => 'How do I reset my password?', 'category' => 'Account'],
['question' => 'How to create a new panel?', 'category' => 'Panels'],
['question' => 'Where can I view response analytics?', 'category' => 'Analytics'],
['question' => 'How to export data to Excel?', 'category' => 'Data Export'],
['question' => 'What are the system requirements?', 'category' => 'Technical'],
['question' => 'How to manage user permissions?', 'category' => 'Administration']
];
include 'includes/header.php';
?>
✅
Submit Support Ticket
Detailed Message *
Attachment (optional)
Max file size: 5MB. Accepted formats: JPG, PNG, PDF, DOC
📧 Submit Ticket
Frequently Asked Questions
-------------------- END OF FILE --------------------
FILE: users.php
TYPE: PHP
SIZE: 11.68 KB
------------------------------------------------------------
window.showToast && window.showToast("User created successfully!", "success");';
}
}
// Sample user data - replace with actual database queries
try {
// $pdo = getDBConnection();
// $users = $pdo->query("SELECT * FROM users ORDER BY created_at DESC")->fetchAll();
$users = [
['id' => 1, 'name' => 'John Smith', 'email' => 'john@relevantreflex.com', 'role' => 'Admin', 'status' => 'Active', 'created' => '2025-08-01', 'last_login' => '2025-09-03 10:30:00'],
['id' => 2, 'name' => 'Sarah Johnson', 'email' => 'sarah@relevantreflex.com', 'role' => 'Manager', 'status' => 'Active', 'created' => '2025-08-15', 'last_login' => '2025-09-02 14:22:00'],
['id' => 3, 'name' => 'Mike Davis', 'email' => 'mike@contractor.com', 'role' => 'User', 'status' => 'Inactive', 'created' => '2025-07-20', 'last_login' => '2025-08-28 09:15:00'],
['id' => 4, 'name' => 'Emily Chen', 'email' => 'emily@relevantreflex.com', 'role' => 'Manager', 'status' => 'Active', 'created' => '2025-08-22', 'last_login' => '2025-09-03 08:45:00'],
['id' => 5, 'name' => 'Robert Wilson', 'email' => 'robert@partner.com', 'role' => 'User', 'status' => 'Active', 'created' => '2025-09-01', 'last_login' => 'Never']
];
} catch (Exception $e) {
error_log("Users data error: " . $e->getMessage());
$users = [];
}
include 'includes/header.php';
?>
$u['status'] === 'Active')); ?>
Active Users
$u['role'] === 'Admin')); ?>
Administrators
$u['last_login'] !== 'Never' && strtotime($u['last_login']) > strtotime('-7 days'))); ?>
Active This Week
ID
Name
Email
Role
Status
Last Login
Actions
Never
✏️
👁️
🚫
✅
🗑️
-------------------- END OF FILE --------------------
FILE: assets/css/dashboard.css
TYPE: CSS
SIZE: 28.46 KB
------------------------------------------------------------
/* 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(0, 102, 204, 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(0, 102, 204, 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(0, 102, 204, 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: assets/css/main.css
TYPE: CSS
SIZE: 26.33 KB
------------------------------------------------------------
/* Relevant Reflex - Main Styles */
:root {
/* Primary Color Scheme - Easily Customizable */
--primary-color: #0066cc;
--primary-hover: #0052a3;
--primary-light: #e6f2ff;
--primary-dark: #004080;
/* Secondary Colors */
--secondary-color: #6c757d;
--secondary-light: #f8f9fa;
--success-color: #28a745;
--success-light: #d4edda;
--warning-color: #ffc107;
--warning-light: #fff3cd;
--danger-color: #dc3545;
--danger-light: #f8d7da;
--info-color: #17a2b8;
--info-light: #d1ecf1;
/* Neutral Colors */
--white: #ffffff;
--light-color: #f8f9fa;
--gray-100: #f1f3f4;
--gray-200: #e9ecef;
--gray-300: #dee2e6;
--gray-400: #ced4da;
--gray-500: #adb5bd;
--gray-600: #6c757d;
--gray-700: #495057;
--gray-800: #343a40;
--dark-color: #212529;
/* Typography */
--font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
--font-family-mono: 'Courier New', monospace;
--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: 28px;
--font-size-4xl: 32px;
/* Font Weights */
--font-weight-light: 300;
--font-weight-normal: 400;
--font-weight-medium: 500;
--font-weight-semibold: 600;
--font-weight-bold: 700;
/* Spacing Scale */
--spacing-xs: 0.25rem; /* 4px */
--spacing-sm: 0.5rem; /* 8px */
--spacing-md: 1rem; /* 16px */
--spacing-lg: 1.5rem; /* 24px */
--spacing-xl: 2rem; /* 32px */
--spacing-2xl: 3rem; /* 48px */
--spacing-3xl: 4rem; /* 64px */
/* Border Radius */
--border-radius-sm: 4px;
--border-radius: 6px;
--border-radius-lg: 8px;
--border-radius-xl: 12px;
--border-radius-2xl: 16px;
--border-radius-full: 50%;
/* Shadows */
--shadow-xs: 0 1px 2px rgba(0,0,0,0.05);
--shadow-sm: 0 1px 3px rgba(0,0,0,0.1), 0 1px 2px rgba(0,0,0,0.06);
--shadow-md: 0 4px 6px rgba(0,0,0,0.07), 0 2px 4px rgba(0,0,0,0.06);
--shadow-lg: 0 10px 15px rgba(0,0,0,0.1), 0 4px 6px rgba(0,0,0,0.05);
--shadow-xl: 0 20px 25px rgba(0,0,0,0.1), 0 10px 10px rgba(0,0,0,0.04);
/* Transitions */
--transition-fast: 0.15s ease-in-out;
--transition-normal: 0.3s ease-in-out;
--transition-slow: 0.5s ease-in-out;
/* Z-Index Scale */
--z-dropdown: 1000;
--z-sticky: 1010;
--z-fixed: 1020;
--z-modal-backdrop: 1030;
--z-modal: 1040;
--z-popover: 1050;
--z-tooltip: 1060;
}
/* CSS Reset and Base Styles */
*,
*::before,
*::after {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-size: 16px;
scroll-behavior: smooth;
}
body {
font-family: var(--font-family);
font-size: var(--font-size-base);
font-weight: var(--font-weight-normal);
line-height: 1.6;
color: var(--dark-color);
background-color: var(--light-color);
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* Accessibility */
.skip-link {
position: absolute;
top: -40px;
left: 6px;
background: var(--dark-color);
color: var(--white);
padding: var(--spacing-sm) var(--spacing-md);
text-decoration: none;
border-radius: var(--border-radius);
z-index: var(--z-tooltip);
font-weight: var(--font-weight-medium);
transition: top var(--transition-fast);
}
.skip-link:focus {
top: 6px;
}
/* Focus indicators for accessibility */
button:focus,
input:focus,
select:focus,
textarea:focus,
a:focus {
outline: 2px solid var(--primary-color);
outline-offset: 2px;
}
/* Header Styles */
.main-header {
background: var(--white);
box-shadow: var(--shadow-sm);
position: sticky;
top: 0;
z-index: var(--z-sticky);
}
.navbar {
border-bottom: 1px solid var(--gray-200);
}
.nav-container {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 var(--spacing-md);
height: 70px;
}
.nav-brand {
display: flex;
align-items: center;
gap: var(--spacing-md);
text-decoration: none;
color: var(--dark-color);
}
.logo {
width: 40px;
height: 40px;
background: linear-gradient(135deg, var(--primary-color), var(--primary-dark));
color: var(--white);
border-radius: var(--border-radius-lg);
display: flex;
align-items: center;
justify-content: center;
font-weight: var(--font-weight-bold);
font-size: var(--font-size-lg);
box-shadow: var(--shadow-sm);
transition: transform var(--transition-normal);
}
.logo:hover {
transform: scale(1.05);
}
.brand-name {
font-weight: var(--font-weight-semibold);
font-size: var(--font-size-lg);
color: var(--dark-color);
}
/* Navigation Menu */
.nav-menu {
display: flex;
gap: var(--spacing-xs);
list-style: none;
}
.nav-link {
display: flex;
align-items: center;
gap: var(--spacing-sm);
padding: var(--spacing-sm) 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);
font-size: var(--font-size-sm);
position: relative;
}
.nav-link::before {
content: '';
position: absolute;
bottom: -2px;
left: 50%;
width: 0;
height: 2px;
background: var(--primary-color);
transition: all var(--transition-normal);
transform: translateX(-50%);
}
.nav-link:hover {
background: var(--primary-light);
color: var(--primary-color);
}
.nav-link:hover::before {
width: 80%;
}
.nav-link.active {
background: var(--primary-color);
color: var(--white);
}
.nav-link.active::before {
width: 80%;
background: var(--white);
}
.nav-icon {
font-size: var(--font-size-sm);
}
/* Mobile Toggle */
.mobile-toggle {
display: none;
flex-direction: column;
background: none;
border: none;
cursor: pointer;
padding: var(--spacing-sm);
border-radius: var(--border-radius);
transition: background-color var(--transition-normal);
}
.mobile-toggle:hover {
background: var(--gray-100);
}
.mobile-toggle span {
width: 25px;
height: 3px;
background: var(--dark-color);
margin: 3px 0;
transition: var(--transition-normal);
border-radius: 2px;
}
.mobile-toggle.active span:nth-child(1) {
transform: rotate(-45deg) translate(-5px, 6px);
}
.mobile-toggle.active span:nth-child(2) {
opacity: 0;
}
.mobile-toggle.active span:nth-child(3) {
transform: rotate(45deg) translate(-5px, -6px);
}
/* Main Content */
main {
min-height: calc(100vh - 140px);
padding: var(--spacing-xl) 0;
}
.page-container {
max-width: 1200px;
margin: 0 auto;
padding: 0 var(--spacing-md);
}
.page-header {
margin-bottom: var(--spacing-2xl);
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: var(--spacing-md);
}
.page-header h1 {
font-size: var(--font-size-3xl);
font-weight: var(--font-weight-bold);
color: var(--dark-color);
margin: 0;
line-height: 1.2;
}
.page-subtitle {
color: var(--gray-600);
margin-top: var(--spacing-sm);
font-size: var(--font-size-lg);
}
.header-actions {
display: flex;
gap: var(--spacing-md);
align-items: center;
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
gap: var(--spacing-sm);
padding: var(--spacing-md) var(--spacing-lg);
border: none;
border-radius: var(--border-radius-lg);
font-weight: var(--font-weight-medium);
font-size: var(--font-size-base);
text-decoration: none;
cursor: pointer;
transition: all var(--transition-normal);
font-family: inherit;
line-height: 1;
min-height: 44px; /* Touch-friendly */
}
.btn-primary {
background: var(--primary-color);
color: var(--white);
box-shadow: var(--shadow-sm);
}
.btn-primary:hover {
background: var(--primary-hover);
box-shadow: var(--shadow-md);
transform: translateY(-1px);
}
.btn-secondary {
background: var(--white);
color: var(--gray-700);
border: 1px solid var(--gray-300);
box-shadow: var(--shadow-xs);
}
.btn-secondary:hover {
background: var(--gray-50);
border-color: var(--gray-400);
box-shadow: var(--shadow-sm);
}
.btn-danger {
background: var(--danger-color);
color: var(--white);
}
.btn-danger:hover {
background: #c82333;
transform: translateY(-1px);
}
.btn-small {
padding: var(--spacing-sm) var(--spacing-md);
font-size: var(--font-size-sm);
min-height: 36px;
}
.btn-edit {
background: var(--warning-color);
color: var(--dark-color);
padding: var(--spacing-sm);
border-radius: var(--border-radius);
border: none;
cursor: pointer;
transition: all var(--transition-fast);
min-width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-edit:hover {
background: #e0a800;
transform: scale(1.1);
}
.btn-delete {
background: var(--danger-color);
color: var(--white);
padding: var(--spacing-sm);
border-radius: var(--border-radius);
border: none;
cursor: pointer;
transition: all var(--transition-fast);
min-width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-delete:hover {
background: #c82333;
transform: scale(1.1);
}
.btn-view, .btn-analytics, .btn-track, .btn-launch, .btn-enable, .btn-disable {
background: var(--info-color);
color: var(--white);
padding: var(--spacing-sm);
border-radius: var(--border-radius);
border: none;
cursor: pointer;
transition: all var(--transition-fast);
min-width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
}
.btn-view:hover, .btn-analytics:hover, .btn-track:hover, .btn-launch:hover {
background: #138496;
transform: scale(1.1);
}
.btn-enable {
background: var(--success-color);
}
.btn-enable:hover {
background: #218838;
}
.btn-disable {
background: var(--warning-color);
color: var(--dark-color);
}
.btn-disable:hover {
background: #e0a800;
}
/* Loading States */
.btn.loading {
opacity: 0.7;
cursor: not-allowed;
pointer-events: none;
}
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
/* Tables */
.table-container {
background: var(--white);
border-radius: var(--border-radius-xl);
box-shadow: var(--shadow-sm);
overflow: hidden;
margin-bottom: var(--spacing-xl);
}
.table-header {
padding: var(--spacing-lg);
border-bottom: 1px solid var(--gray-200);
background: var(--gray-50);
}
.table-controls {
display: flex;
justify-content: space-between;
align-items: center;
gap: var(--spacing-md);
margin-bottom: var(--spacing-lg);
flex-wrap: wrap;
}
.search-container {
flex: 1;
max-width: 400px;
}
.search-input {
width: 100%;
padding: var(--spacing-md);
border: 1px solid var(--gray-300);
border-radius: var(--border-radius-lg);
font-size: var(--font-size-base);
transition: all var(--transition-normal);
}
.search-input:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px var(--primary-light);
}
.filter-container {
display: flex;
gap: var(--spacing-md);
}
.filter-select {
padding: var(--spacing-md);
border: 1px solid var(--gray-300);
border-radius: var(--border-radius-lg);
background: var(--white);
font-size: var(--font-size-base);
min-width: 120px;
cursor: pointer;
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: var(--font-size-sm);
}
.data-table th,
.data-table td {
padding: var(--spacing-md);
text-align: left;
border-bottom: 1px solid var(--gray-200);
vertical-align: middle;
}
.data-table th {
background: var(--gray-50);
font-weight: var(--font-weight-semibold);
color: var(--gray-700);
font-size: var(--font-size-sm);
text-transform: uppercase;
letter-spacing: 0.5px;
position: sticky;
top: 70px;
z-index: 10;
}
.data-table tbody tr {
transition: background-color var(--transition-fast);
}
.data-table tbody tr:hover {
background: var(--gray-50);
}
.data-table tbody tr:last-child td {
border-bottom: none;
}
/* User Info in Tables */
.user-info, .item-info, .panel-info {
display: flex;
align-items: center;
gap: var(--spacing-md);
}
.user-avatar {
width: 36px;
height: 36px;
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);
}
.user-details, .item-details {
display: flex;
flex-direction: column;
gap: 2px;
}
.user-name, .item-name, .panel-name {
font-weight: var(--font-weight-medium);
color: var(--dark-color);
}
.user-meta, .item-meta {
font-size: var(--font-size-xs);
color: var(--gray-500);
}
/* Badges */
.badge {
display: inline-block;
padding: var(--spacing-xs) var(--spacing-sm);
border-radius: var(--border-radius-xl);
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.role-badge,
.status-badge {
@extend .badge;
}
.role-admin { background: var(--danger-light); color: var(--danger-color); }
.role-manager { background: var(--warning-light); color: #856404; }
.role-user { background: var(--info-light); color: var(--info-color); }
.status-active { background: var(--success-light); color: var(--success-color); }
.status-inactive { background: var(--gray-200); color: var(--gray-600); }
.status-draft { background: var(--warning-light); color: #856404; }
.status-completed { background: var(--success-light); color: var(--success-color); }
.status-pending { background: var(--info-light); color: var(--info-color); }
.status-ordered { background: var(--primary-light); color: var(--primary-color); }
.status-received { background: var(--success-light); color: var(--success-color); }
.status-in-progress { background: var(--warning-light); color: #856404; }
.status-open { background: var(--info-light); color: var(--info-color); }
.status-resolved { background: var(--success-light); color: var(--success-color); }
/* Priority badges */
.priority-badge {
@extend .badge;
}
.priority-low { background: var(--gray-200); color: var(--gray-600); }
.priority-medium { background: var(--warning-light); color: #856404; }
.priority-high { background: var(--danger-light); color: var(--danger-color); }
.priority-critical { background: var(--danger-color); color: var(--white); }
/* Action Buttons */
.action-buttons {
display: flex;
gap: var(--spacing-xs);
align-items: center;
}
/* Stats and 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: var(--white);
border-radius: var(--border-radius-xl);
padding: var(--spacing-xl);
box-shadow: var(--shadow-sm);
display: flex;
align-items: center;
gap: var(--spacing-lg);
transition: all var(--transition-normal);
border: 1px solid var(--gray-100);
position: relative;
overflow: hidden;
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 4px;
background: linear-gradient(90deg, var(--primary-color), var(--primary-dark));
}
.stat-card:hover {
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.stat-icon {
font-size: 2.5rem;
width: 60px;
height: 60px;
background: var(--primary-light);
border-radius: var(--border-radius-xl);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.stat-content {
flex: 1;
}
.stat-content h3 {
font-size: var(--font-size-2xl);
margin: 0;
color: var(--dark-color);
font-weight: var(--font-weight-bold);
line-height: 1.2;
}
.stat-content p {
margin: var(--spacing-xs) 0 0;
color: var(--gray-600);
font-weight: var(--font-weight-medium);
font-size: var(--font-size-base);
}
.stat-change,
.stat-meta {
font-size: var(--font-size-sm);
margin-top: var(--spacing-xs);
}
.stat-change.positive {
color: var(--success-color);
}
.stat-change.negative {
color: var(--danger-color);
}
.stat-change.neutral {
color: var(--gray-500);
}
/* Forms */
.form-group {
margin-bottom: var(--spacing-lg);
}
.form-row {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--spacing-lg);
}
label {
display: block;
margin-bottom: var(--spacing-sm);
font-weight: var(--font-weight-medium);
color: var(--gray-700);
font-size: var(--font-size-sm);
}
input[type="text"],
input[type="email"],
input[type="password"],
input[type="number"],
input[type="date"],
input[type="tel"],
input[type="url"],
select,
textarea {
width: 100%;
padding: var(--spacing-md);
border: 1px solid var(--gray-300);
border-radius: var(--border-radius-lg);
font-size: var(--font-size-base);
font-family: inherit;
transition: all var(--transition-normal);
background: var(--white);
}
input:focus,
select:focus,
textarea:focus {
border-color: var(--primary-color);
box-shadow: 0 0 0 3px var(--primary-light);
}
input.error {
border-color: var(--danger-color);
box-shadow: 0 0 0 3px var(--danger-light);
}
.form-help {
display: block;
margin-top: var(--spacing-xs);
font-size: var(--font-size-xs);
color: var(--gray-500);
}
.form-actions {
display: flex;
gap: var(--spacing-md);
margin-top: var(--spacing-xl);
padding-top: var(--spacing-lg);
border-top: 1px solid var(--gray-200);
}
/* Checkbox Styling */
.checkbox-label {
display: flex !important;
align-items: center;
gap: var(--spacing-md);
margin-bottom: var(--spacing-md) !important;
cursor: pointer;
font-weight: var(--font-weight-normal) !important;
}
.checkbox-label input[type="checkbox"] {
width: auto;
margin: 0;
}
.checkmark {
width: 20px;
height: 20px;
border: 2px solid var(--gray-300);
border-radius: var(--border-radius-sm);
position: relative;
flex-shrink: 0;
}
.checkbox-label input[type="checkbox"]:checked + .checkmark {
background: var(--primary-color);
border-color: var(--primary-color);
}
.checkbox-label input[type="checkbox"]:checked + .checkmark::after {
content: '✓';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: var(--white);
font-size: 12px;
font-weight: bold;
}
/* Messages */
.success-message,
.error-message,
.info-message {
padding: var(--spacing-md) var(--spacing-lg);
border-radius: var(--border-radius-lg);
margin-bottom: var(--spacing-lg);
display: flex;
align-items: center;
gap: var(--spacing-md);
font-weight: var(--font-weight-medium);
}
.success-message {
background: var(--success-light);
color: var(--success-color);
border: 1px solid #c3e6cb;
}
.error-message {
background: var(--danger-light);
color: var(--danger-color);
border: 1px solid #f5c6cb;
}
.info-message {
background: var(--info-light);
color: var(--info-color);
border: 1px solid #bee5eb;
}
.message-icon {
font-size: var(--font-size-lg);
}
/* Progress Bars */
.progress-container {
display: flex;
align-items: center;
gap: var(--spacing-md);
}
.progress-bar {
flex: 1;
height: 8px;
background: var(--gray-200);
border-radius: var(--border-radius-sm);
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, var(--primary-color), var(--primary-dark));
border-radius: var(--border-radius-sm);
transition: width var(--transition-slow);
}
.progress-text {
font-size: var(--font-size-sm);
font-weight: var(--font-weight-medium);
color: var(--gray-600);
min-width: 40px;
}
/* Modal Styles */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: var(--z-modal);
align-items: center;
justify-content: center;
padding: var(--spacing-lg);
}
.modal-content {
background: var(--white);
border-radius: var(--border-radius-xl);
box-shadow: var(--shadow-xl);
max-width: 500px;
width: 100%;
max-height: 90vh;
overflow-y: auto;
animation: modalAppear 0.3s ease-out;
}
.modal-large {
max-width: 700px;
}
@keyframes modalAppear {
from {
opacity: 0;
transform: scale(0.9) translateY(-20px);
}
to {
opacity: 1;
transform: scale(1) translateY(0);
}
}
.modal-header {
padding: var(--spacing-lg) var(--spacing-xl);
border-bottom: 1px solid var(--gray-200);
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h2 {
margin: 0;
color: var(--dark-color);
font-size: var(--font-size-xl);
}
.modal-close {
background: none;
border: none;
font-size: var(--font-size-xl);
cursor: pointer;
color: var(--gray-500);
padding: var(--spacing-sm);
border-radius: var(--border-radius);
transition: all var(--transition-normal);
}
.modal-close:hover {
background: var(--gray-100);
color: var(--gray-700);
}
.modal-form,
.settings-form,
.support-form {
padding: var(--spacing-xl);
}
/* Footer Styles */
.main-footer {
background: var(--dark-color);
color: var(--white);
margin-top: var(--spacing-3xl);
}
.footer-container {
max-width: 1200px;
margin: 0 auto;
padding: var(--spacing-3xl) var(--spacing-md) var(--spacing-lg);
}
.footer-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: var(--spacing-2xl);
margin-bottom: var(--spacing-2xl);
}
.footer-section h3,
.footer-section h4 {
margin-bottom: var(--spacing-lg);
color: var(--white);
font-weight: var(--font-weight-semibold);
}
.footer-section p {
color: var(--gray-400);
line-height: 1.7;
margin-bottom: var(--spacing-md);
}
.footer-links {
list-style: none;
}
.footer-links li {
margin-bottom: var(--spacing-sm);
}
.footer-links a {
color: var(--gray-400);
text-decoration: none;
transition: color var(--transition-normal);
font-weight: var(--font-weight-normal);
}
.footer-links a:hover {
color: var(--white);
text-decoration: underline;
}
.social-links {
display: flex;
gap: var(--spacing-lg);
margin-bottom: var(--spacing-lg);
}
.social-links a {
color: var(--gray-400);
text-decoration: none;
transition: color var(--transition-normal);
font-weight: var(--font-weight-medium);
}
.social-links a:hover {
color: var(--primary-color);
}
.contact-info p {
color: var(--gray-400);
margin-bottom: var(--spacing-sm);
font-size: var(--font-size-sm);
}
.contact-info strong {
color: var(--white);
}
.footer-bottom {
border-top: 1px solid var(--gray-700);
padding-top: var(--spacing-lg);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: var(--spacing-md);
}
.footer-legal p {
color: var(--gray-400);
margin: 0;
font-size: var(--font-size-sm);
}
.legal-links {
display: flex;
gap: var(--spacing-lg);
}
.legal-links a {
color: var(--gray-400);
text-decoration: none;
font-size: var(--font-size-sm);
transition: color var(--transition-normal);
}
.legal-links a:hover {
color: var(--white);
text-decoration: underline;
}
/* Utility Classes */
.text-center { text-align: center; }
.text-left { text-align: left; }
.text-right { text-align: right; }
.text-muted { color: var(--gray-500); }
.text-primary { color: var(--primary-color); }
.text-success { color: var(--success-color); }
.text-warning { color: var(--warning-color); }
.text-danger { color: var(--danger-color); }
.bg-primary { background-color: var(--primary-color); }
.bg-light { background-color: var(--light-color); }
.bg-white { background-color: var(--white); }
.d-flex { display: flex; }
.d-none { display: none; }
.d-block { display: block; }
.justify-center { justify-content: center; }
.justify-between { justify-content: space-between; }
.align-center { align-items: center; }
.w-full { width: 100%; }
.h-full { height: 100%; }
/* Spacing utilities */
.m-0 { margin: 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); }
.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); }
.p-1 { padding: var(--spacing-xs); }
.p-2 { padding: var(--spacing-sm); }
.p-3 { padding: var(--spacing-md); }
.p-4 { padding: var(--spacing-lg); }
.p-5 { padding: var(--spacing-xl); }
/* Loading Animation */
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
animation: spin 1s linear infinite;
}
/* Toast Notifications */
.toast {
position: fixed;
top: 20px;
right: 20px;
padding: var(--spacing-md) var(--spacing-lg);
border-radius: var(--border-radius-lg);
color: var(--white);
font-weight: var(--font-weight-medium);
z-index: var(--z-tooltip);
opacity: 0;
transform: translateX(100px);
transition: all var(--transition-normal);
max-width: 350px;
box-shadow: var(--shadow-lg);
}
.toast.show {
opacity: 1;
transform: translateX(0);
}
.toast-success { background: var(--success-color); }
.toast-error { background: var(--danger-color); }
.toast-info { background: var(--info-color); }
.toast-warning { background: var(--warning-color); color: var(--dark-color); }
/* Print Styles */
@media print {
.main-header,
.main-footer,
.btn,
.mobile-toggle,
.action-buttons {
display: none !important;
}
.page-container {
max-width: none;
padding: 0;
}
.stats-grid {
grid-template-columns: repeat(4, 1fr);
}
body {
font-size: 12pt;
line-height: 1.4;
color: black;
background: white;
}
}
-------------------- END OF FILE --------------------
FILE: assets/css/responsive.css
TYPE: CSS
SIZE: 18.5 KB
------------------------------------------------------------
/* 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: #0052a3;
--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(0, 102, 204, 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(0, 102, 204, 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(0, 102, 204, 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: assets/images/logo.svg
TYPE: SVG
SIZE: 0 B
------------------------------------------------------------
[IMAGE FILE: SVG - Content not displayed]
-------------------- END OF FILE --------------------
FILE: assets/js/dashboard.js
TYPE: JS
SIZE: 26.43 KB
------------------------------------------------------------
// 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(0, 102, 204, 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: assets/js/main.js
TYPE: JS
SIZE: 33.11 KB
------------------------------------------------------------
// 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: includes/footer.php
TYPE: PHP
SIZE: 5.95 KB
------------------------------------------------------------