/home/awneajlw/public_html/codestechvista.com/sale-record.php
<?php
/**
* Sale Record Page - Sales Dashboard
* This page displays sales analytics with KPI gauges and charts
* Features: Revenue tracking, expense monitoring, monthly sales data visualization
*/
// Start session if not already started
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Include required files
require_once 'config/database.php'; // Database connection configuration
require_once 'includes/auth.php'; // Authentication functions
/**
* Authentication Check
* Redirect to welcome page if user is not logged in
*/
if (!isLoggedIn()) {
header('Location: welcome.php');
exit();
}
/**
* Sub User Access Control
* Check if current user is a sub user and restrict access to sale records
*/
try {
$database = new Database();
$db = $database->getConnection();
$current_user_id = $_SESSION['user_id'] ?? null;
$check_user_query = "SELECT user_type, parent_user_id, can_access_sales FROM users WHERE id = ?";
$check_user_stmt = $db->prepare($check_user_query);
$check_user_stmt->execute([$current_user_id]);
$user_info = $check_user_stmt->fetch(PDO::FETCH_ASSOC);
// If user is a sub user and doesn't have sales access, redirect to home
if ($user_info && $user_info['user_type'] === 'sub' && $user_info['can_access_sales'] == 0) {
$_SESSION['access_denied_message'] = 'Access denied. Sub users cannot view sale records.';
header('Location: home.php');
exit();
}
} catch (Exception $e) {
error_log("Error checking user access: " . $e->getMessage());
// In case of error, allow access to avoid breaking the application
}
// Initialize variables for sales data
$total_amount = 0; // Total sales amount
$total_revenue = 0; // Total revenue calculated
$total_expense = 0; // Total expenses calculated
$monthly_data = []; // Monthly sales data for charts
$error_message = '';
// Date filter handling
$start_date = isset($_GET['start_date']) ? $_GET['start_date'] : date('Y-01-01'); // Default: start of current year
$end_date = isset($_GET['end_date']) ? $_GET['end_date'] : date('Y-12-31'); // Default: end of current year
// Format dates for display
$start_display = date('M j', strtotime($start_date));
$end_display = date('M j', strtotime($end_date));
/**
* Database Connection and Sales Data Retrieval
* Fetch sales data from orders table for analytics
*/
try {
$database = new Database();
$db = $database->getConnection();
$user_id = $_SESSION['user_id'];
// Get total amount from orders within date range
$total_query = "SELECT COALESCE(SUM(total_amount), 0) as total_amount
FROM orders
WHERE user_id = ? AND DATE(created_at) BETWEEN ? AND ?";
$stmt = $db->prepare($total_query);
$stmt->execute([$user_id, $start_date, $end_date]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
$total_amount = $result['total_amount'];
// Calculate revenue (assuming 70% of total amount is revenue)
$total_revenue = $total_amount * 0.7;
// Calculate expense (assuming 30% of total amount is expense)
$total_expense = $total_amount * 0.3;
// Get monthly data for chart within date range
$monthly_query = "SELECT
MONTH(created_at) as month,
MONTHNAME(created_at) as month_name,
COALESCE(SUM(total_amount), 0) as monthly_total
FROM orders
WHERE user_id = ? AND DATE(created_at) BETWEEN ? AND ?
GROUP BY MONTH(created_at), MONTHNAME(created_at)
ORDER BY MONTH(created_at)";
$stmt = $db->prepare($monthly_query);
$stmt->execute([$user_id, $start_date, $end_date]);
$monthly_results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// Create array with all 12 months
$months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
$monthly_data = array_fill(0, 12, 0);
// Fill actual data
foreach ($monthly_results as $row) {
$month_index = $row['month'] - 1; // Convert to 0-based index
$monthly_data[$month_index] = $row['monthly_total'];
}
} catch (Exception $e) {
$error_message = 'Error loading sales data: ' . $e->getMessage();
}
// Format numbers for display
$amount_formatted = number_format($total_amount, 0);
$revenue_formatted = number_format($total_revenue, 0);
$expense_formatted = number_format($total_expense, 0);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Sale Record - OPTI SLIP</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
background: white;
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
padding: 0;
}
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
}
.back-btn {
background: none;
border: none;
color: black;
font-size: 24px;
cursor: pointer;
padding: 10px;
transition: all 0.3s ease;
}
.back-btn:hover {
color: #169D53;
transform: translateX(-3px);
}
.logo-section {
text-align: center;
}
.logo-image {
width: 100px;
height: 100px;
object-fit: contain;
filter: brightness(0) saturate(100%);
}
.date-filter {
display: flex;
align-items: center;
gap: 12px;
font-size: 16px;
color: #666;
background: #f8f9fa;
padding: 12px 18px;
border-radius: 25px;
border: 1px solid #e9ecef;
}
.kpi-section {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
margin-bottom: 40px;
}
.kpi-card {
text-align: center;
padding: 20px;
}
.gauge-container {
position: relative;
width: 150px;
height: 150px;
margin: 0 auto 15px;
}
.gauge-circle {
width: 150px;
height: 150px;
border-radius: 50%;
position: relative;
display: flex;
align-items: center;
justify-content: center;
}
.gauge-circle::before {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background: var(--gauge-bg);
z-index: 1;
}
.gauge-circle::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background: conic-gradient(from 0deg, var(--gauge-color) 0deg, var(--gauge-color) var(--gauge-fill), transparent var(--gauge-fill));
z-index: 2;
}
.gauge-center {
width: 100px;
height: 100px;
background: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
position: relative;
z-index: 3;
}
.gauge-label {
font-size: 14px;
font-weight: 700;
color: #333;
margin-bottom: 5px;
}
.gauge-value {
font-size: 12px;
color: #666;
font-weight: 600;
}
.amount-gauge {
--gauge-color: #20c997;
--gauge-bg: #e9ecef;
--gauge-fill: <?php echo min(($total_amount / 50000) * 270, 270); ?>deg;
}
.revenue-gauge {
--gauge-color: #6f42c1;
--gauge-bg: #e9ecef;
--gauge-fill: <?php echo min(($total_revenue / 35000) * 270, 270); ?>deg;
}
.expense-gauge {
--gauge-color: #28a745;
--gauge-bg: #e9ecef;
--gauge-fill: <?php echo min(($total_expense / 15000) * 270, 270); ?>deg;
}
.chart-section {
background: white;
border-radius: 15px;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
padding: 25px;
margin-bottom: 30px;
}
.chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
}
.chart-title {
font-size: 16px;
font-weight: 600;
color: #333;
}
.chart-container {
position: relative;
height: 300px;
width: 100%;
}
.alert {
padding: 15px 20px;
border-radius: 10px;
margin-bottom: 25px;
font-size: 15px;
}
.alert-danger {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
/* Date Picker Modal Styles */
.date-picker-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.date-picker-content {
background: white;
border-radius: 15px;
padding: 25px;
max-width: 500px;
width: 90%;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}
.date-picker-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
border-bottom: 1px solid #e9ecef;
padding-bottom: 15px;
}
.date-picker-header h5 {
margin: 0;
color: #333;
font-weight: 600;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #666;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.close-btn:hover {
color: #333;
}
.date-inputs {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.date-input-group {
display: flex;
flex-direction: column;
}
.date-input-group label {
margin-bottom: 8px;
font-weight: 600;
color: #333;
font-size: 14px;
}
.date-input-group input {
padding: 12px 15px;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s ease;
}
.date-input-group input:focus {
outline: none;
border-color: #169D53;
}
.date-picker-buttons {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin-bottom: 25px;
}
.quick-btn {
padding: 8px 12px;
border: 1px solid #e9ecef;
background: white;
border-radius: 6px;
cursor: pointer;
font-size: 12px;
transition: all 0.3s ease;
color: #666;
}
.quick-btn:hover {
background: #169D53;
color: white;
border-color: #169D53;
}
.date-picker-actions {
display: flex;
justify-content: flex-end;
gap: 15px;
}
.cancel-btn, .apply-btn {
padding: 12px 25px;
border-radius: 8px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
}
.cancel-btn {
background: white;
border: 2px solid #e9ecef;
color: #666;
}
.cancel-btn:hover {
background: #f8f9fa;
border-color: #ddd;
}
.apply-btn {
background: #169D53;
border: 2px solid #169D53;
color: white;
}
.apply-btn:hover {
background: #138f48;
border-color: #138f48;
}
.date-filter {
cursor: pointer;
transition: all 0.3s ease;
}
.date-filter:hover {
background: #f0f9ff;
}
/* Mobile Responsive */
@media (max-width: 768px) {
body {
padding: 10px;
}
.header-section {
flex-direction: column;
gap: 15px;
text-align: center;
}
.back-btn {
position: absolute;
left: 10px;
top: 10px;
}
.kpi-section {
grid-template-columns: 1fr;
gap: 20px;
}
.gauge-container {
width: 120px;
height: 120px;
}
.gauge-circle {
width: 120px;
height: 120px;
}
.gauge-center {
width: 80px;
height: 80px;
}
.gauge-label {
font-size: 12px;
}
.gauge-value {
font-size: 10px;
}
.logo-image {
width: 80px;
height: 80px;
}
.date-filter {
font-size: 14px;
padding: 10px 15px;
}
.date-picker-content {
width: 95%;
padding: 20px;
}
.date-inputs {
grid-template-columns: 1fr;
gap: 15px;
}
.date-picker-buttons {
grid-template-columns: repeat(2, 1fr);
gap: 8px;
}
.quick-btn {
padding: 10px 8px;
font-size: 11px;
}
}
@media (max-width: 480px) {
.kpi-section {
gap: 15px;
}
.gauge-container {
width: 100px;
height: 100px;
}
.gauge-circle {
width: 100px;
height: 100px;
}
.gauge-center {
width: 70px;
height: 70px;
}
.chart-section {
padding: 15px;
}
.chart-container {
height: 250px;
}
}
</style>
</head>
<body>
<div class="container">
<div class="header-section">
<button class="back-btn" onclick="window.location.href='home.php'">
<i class="fas fa-arrow-left"></i>
</button>
<div class="logo-section">
<img src="assets/images/Optislipimage.png" alt="Opti Slip Logo" class="logo-image" onerror="this.style.display='none'; this.parentElement.innerHTML='<div style=\'color: black; font-size: 20px; font-weight: bold;\'>OPTI SLIP</div>'">
</div>
<div class="date-filter" onclick="toggleDatePicker()">
<i class="fas fa-calendar-alt"></i>
<span id="dateRange"><?php echo $start_display . ' To ' . $end_display; ?></span>
<i class="fas fa-chevron-down"></i>
</div>
<!-- Date Picker Modal -->
<div id="datePickerModal" class="date-picker-modal" style="display: none;">
<div class="date-picker-content">
<div class="date-picker-header">
<h5>Select Date Range</h5>
<button onclick="closeDatePicker()" class="close-btn">×</button>
</div>
<form method="GET" action="sale-record.php" class="date-picker-form">
<div class="date-inputs">
<div class="date-input-group">
<label>Start Date:</label>
<input type="date" name="start_date" value="<?php echo $start_date; ?>" required>
</div>
<div class="date-input-group">
<label>End Date:</label>
<input type="date" name="end_date" value="<?php echo $end_date; ?>" required>
</div>
</div>
<div class="date-picker-buttons">
<button type="button" onclick="setQuickRange('today')" class="quick-btn">Today</button>
<button type="button" onclick="setQuickRange('week')" class="quick-btn">This Week</button>
<button type="button" onclick="setQuickRange('month')" class="quick-btn">This Month</button>
<button type="button" onclick="setQuickRange('year')" class="quick-btn">This Year</button>
</div>
<div class="date-picker-actions">
<button type="button" onclick="closeDatePicker()" class="cancel-btn">Cancel</button>
<button type="submit" class="apply-btn">Apply Filter</button>
</div>
</form>
</div>
</div>
</div>
<?php if ($error_message): ?>
<div class="alert alert-danger">
<i class="fas fa-exclamation-triangle me-2"></i>
<?php echo htmlspecialchars($error_message); ?>
</div>
<?php endif; ?>
<div class="kpi-section">
<div class="kpi-card">
<div class="gauge-container">
<div class="gauge-circle amount-gauge">
<div class="gauge-center">
<div class="gauge-label">AMOUNT</div>
<div class="gauge-value"><?php echo $amount_formatted; ?></div>
</div>
</div>
</div>
</div>
<div class="kpi-card">
<div class="gauge-container">
<div class="gauge-circle revenue-gauge">
<div class="gauge-center">
<div class="gauge-label">REVENUE</div>
<div class="gauge-value"><?php echo $revenue_formatted; ?></div>
</div>
</div>
</div>
</div>
<div class="kpi-card">
<div class="gauge-container">
<div class="gauge-circle expense-gauge">
<div class="gauge-center">
<div class="gauge-label">EXPENSE</div>
<!--<div class="gauge-value"><?php echo $expense_formatted; ?></div>-->
</div>
</div>
</div>
</div>
</div>
<div class="chart-section">
<div class="chart-header">
<div class="chart-title">Chart Type:</div>
<div class="date-filter" onclick="toggleDatePicker()">
<i class="fas fa-calendar-alt"></i>
<span><?php echo $start_display . ' To ' . $end_display; ?></span>
<i class="fas fa-chevron-down"></i>
</div>
</div>
<div class="chart-container">
<canvas id="salesChart"></canvas>
</div>
</div>
</div>
<script>
// Date picker functionality
function toggleDatePicker() {
const modal = document.getElementById('datePickerModal');
modal.style.display = modal.style.display === 'none' ? 'flex' : 'none';
}
function closeDatePicker() {
document.getElementById('datePickerModal').style.display = 'none';
}
function setQuickRange(range) {
const today = new Date();
let startDate, endDate;
switch(range) {
case 'today':
startDate = endDate = today.toISOString().split('T')[0];
break;
case 'week':
const weekStart = new Date(today.setDate(today.getDate() - today.getDay()));
const weekEnd = new Date(today.setDate(today.getDate() - today.getDay() + 6));
startDate = weekStart.toISOString().split('T')[0];
endDate = weekEnd.toISOString().split('T')[0];
break;
case 'month':
startDate = new Date(today.getFullYear(), today.getMonth(), 1).toISOString().split('T')[0];
endDate = new Date(today.getFullYear(), today.getMonth() + 1, 0).toISOString().split('T')[0];
break;
case 'year':
startDate = new Date(today.getFullYear(), 0, 1).toISOString().split('T')[0];
endDate = new Date(today.getFullYear(), 11, 31).toISOString().split('T')[0];
break;
}
document.querySelector('input[name="start_date"]').value = startDate;
document.querySelector('input[name="end_date"]').value = endDate;
}
// Close modal when clicking outside
document.getElementById('datePickerModal').addEventListener('click', function(e) {
if (e.target === this) {
closeDatePicker();
}
});
// Chart.js configuration for sales data
const ctx = document.getElementById('salesChart').getContext('2d');
const monthlyData = <?php echo json_encode(array_values($monthly_data)); ?>;
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const salesChart = new Chart(ctx, {
type: 'line',
data: {
labels: months,
datasets: [{
label: 'Sales Amount',
data: monthlyData,
borderColor: '#28a745',
backgroundColor: 'rgba(40, 167, 69, 0.1)',
borderWidth: 3,
fill: true,
tension: 0.4,
pointBackgroundColor: '#28a745',
pointBorderColor: '#fff',
pointBorderWidth: 2,
pointRadius: 6,
pointHoverRadius: 8
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
grid: {
color: '#e9ecef'
},
ticks: {
callback: function(value) {
return value.toLocaleString();
},
color: '#666',
font: {
size: 12
}
}
},
x: {
grid: {
display: false
},
ticks: {
color: '#666',
font: {
size: 12
}
}
}
},
elements: {
point: {
hoverRadius: 8
}
}
}
});
</script>
</body>
</html>