gamtest / resources /views /dashboard.blade.php
veela4's picture
Upload folder using huggingface_hub
70ba896 verified
<x-app-layout>
<x-slot name="header">
<div class="flex items-center justify-between">
<div>
<h1 class="heading-2 mb-2">Analytics Dashboard</h1>
<p class="text-muted">Real-time insights and performance metrics</p>
</div>
<div class="text-right">
<div class="text-sm text-muted">{{ date('l, F j, Y') }}</div>
<div class="text-lg font-semibold text-white">{{ date('g:i A') }}</div>
</div>
</div>
</x-slot>
<div class="py-8">
<div class="container-custom space-y-8">
<!-- Time Period Filter -->
<div class="flex items-center justify-between mb-8">
<div>
<h2 class="text-2xl font-bold text-white mb-2">Performance Overview</h2>
<p class="text-white/60">Track your store's key metrics and trends</p>
</div>
<div class="flex items-center space-x-4">
<div class="flex items-center space-x-2 text-white/60">
<div class="w-2 h-2 bg-emerald-400 rounded-full animate-pulse"></div>
<span class="text-sm">Real-time</span>
</div>
<!-- Simple Time Filter -->
<div class="bg-white/5 backdrop-blur-2xl border border-white/10 rounded-2xl p-2 shadow-2xl">
<div class="flex space-x-1">
<button onclick="switchPeriod('daily')" id="btn-daily" data-period="daily" class="time-filter-btn active px-4 py-2 rounded-xl text-sm font-semibold">
Daily
</button>
<button onclick="switchPeriod('weekly')" id="btn-weekly" data-period="weekly" class="time-filter-btn px-4 py-2 rounded-xl text-sm font-semibold">
Weekly
</button>
<button onclick="switchPeriod('monthly')" id="btn-monthly" data-period="monthly" class="time-filter-btn px-4 py-2 rounded-xl text-sm font-semibold">
Monthly
</button>
</div>
</div>
</div>
</div>
<!-- Metric Cards Grid -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
<!-- Revenue Card -->
<div class="bg-white/5 backdrop-blur-2xl border border-white/10 rounded-2xl p-6 shadow-2xl">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-gradient-to-br from-emerald-400/20 to-emerald-600/10 rounded-xl flex items-center justify-center border border-white/20">
<i class="fas fa-dollar-sign text-emerald-400 text-lg"></i>
</div>
<h3 class="text-white text-sm font-semibold uppercase tracking-wider" id="revenue-title">Today's Revenue</h3>
</div>
</div>
<div class="text-3xl font-bold text-emerald-400 mb-1" id="revenue-value">฿0</div>
<p class="text-white/60 text-xs" id="revenue-previous">Previous: ฿0</p>
</div>
<!-- Sales Card -->
<div class="bg-white/5 backdrop-blur-2xl border border-white/10 rounded-2xl p-6 shadow-2xl">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-gradient-to-br from-blue-400/20 to-blue-600/10 rounded-xl flex items-center justify-center border border-white/20">
<i class="fas fa-shopping-bag text-blue-400 text-lg"></i>
</div>
<h3 class="text-white text-sm font-semibold uppercase tracking-wider" id="sales-title">Orders Today</h3>
</div>
</div>
<div class="text-3xl font-bold text-blue-400 mb-1" id="sales-value">0</div>
<p class="text-white/60 text-xs" id="sales-previous">Previous: 0</p>
</div>
<!-- Customers Card -->
<div class="bg-white/5 backdrop-blur-2xl border border-white/10 rounded-2xl p-6 shadow-2xl">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-gradient-to-br from-orange-400/20 to-orange-600/10 rounded-xl flex items-center justify-center border border-white/20">
<i class="fas fa-users text-orange-400 text-lg"></i>
</div>
<h3 class="text-white text-sm font-semibold uppercase tracking-wider" id="customers-title">Customers Today</h3>
</div>
</div>
<div class="text-3xl font-bold text-orange-400 mb-1" id="customers-value">0</div>
<p class="text-white/60 text-xs" id="customers-previous">Previous: 0</p>
</div>
<!-- Products Card -->
<div class="bg-white/5 backdrop-blur-2xl border border-white/10 rounded-2xl p-6 shadow-2xl">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 bg-gradient-to-br from-purple-400/20 to-purple-600/10 rounded-xl flex items-center justify-center border border-white/20">
<i class="fas fa-box text-purple-400 text-lg"></i>
</div>
<h3 class="text-white text-sm font-semibold uppercase tracking-wider">Total Products</h3>
</div>
</div>
<div class="text-3xl font-bold text-purple-400 mb-1" id="products-value">0</div>
<p class="text-white/60 text-xs">Active products</p>
</div>
</div>
<!-- Charts Grid -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8 mb-8">
<!-- Revenue Chart -->
<div class="bg-white/5 backdrop-blur-2xl border border-white/10 rounded-2xl p-6 shadow-2xl hover:bg-white/10 transition-all duration-300 cursor-pointer" onclick="openRevenueDetails()">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-bold text-white">Revenue Trend</h3>
<div class="flex items-center space-x-2">
<div class="w-3 h-3 bg-emerald-400 rounded-full"></div>
<span class="text-sm text-white/60">Revenue</span>
<i class="fas fa-expand-alt text-white/40 ml-2"></i>
</div>
</div>
<div class="relative h-80">
<canvas id="revenueChart"></canvas>
</div>
</div>
<!-- Top Products Chart -->
<div class="bg-white/5 backdrop-blur-2xl border border-white/10 rounded-2xl p-6 shadow-2xl hover:bg-white/10 transition-all duration-300 cursor-pointer" onclick="openProductsDetails()">
<div class="flex items-center justify-between mb-6">
<h3 class="text-xl font-bold text-white">Top Products</h3>
<div class="flex items-center space-x-2">
<span class="text-sm text-white/60">By Revenue</span>
<i class="fas fa-expand-alt text-white/40 ml-2"></i>
</div>
</div>
<div class="relative h-80">
<canvas id="productsChart"></canvas>
</div>
</div>
</div>
<!-- Modal for Chart Details -->
<div id="chartModal" class="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 hidden opacity-0 transition-all duration-500">
<div class="flex items-center justify-center min-h-screen p-4">
<div id="modalContainer" class="bg-gray-900/95 backdrop-blur-2xl border border-white/10 rounded-2xl p-6 max-w-6xl w-full max-h-[90vh] shadow-2xl flex flex-col transform scale-75 transition-all duration-500">
<div class="flex items-center justify-between mb-6 flex-shrink-0">
<h2 id="modalTitle" class="text-2xl font-bold text-white flex items-center">
<i id="modalIcon" class="fas fa-chart-line mr-3 text-emerald-400"></i>
<span id="modalTitleText">Chart Details</span>
</h2>
<button onclick="closeModal()" class="text-white/60 hover:text-white text-2xl hover:rotate-90 transition-all duration-300">
<i class="fas fa-times"></i>
</button>
</div>
<div id="modalContent" class="text-white overflow-y-auto flex-1 space-y-6">
<!-- Dynamic content will be loaded here -->
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="bg-white/5 backdrop-blur-2xl border border-white/10 rounded-2xl p-6 shadow-2xl">
<h3 class="text-xl font-bold text-white mb-6">
<i class="fas fa-rocket mr-3 text-purple-400"></i>Quick Actions
</h3>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<a href="{{ route('products.listOFproduct') }}" class="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white font-semibold py-3 px-6 rounded-xl transition-all duration-200 flex items-center justify-center space-x-2 shadow-lg hover:shadow-xl transform hover:scale-105">
<i class="fas fa-cogs"></i>
<span>Manage Products</span>
</a>
<a href="{{ route('orders.index') }}" class="bg-gradient-to-r from-emerald-600 to-teal-600 hover:from-emerald-700 hover:to-teal-700 text-white font-semibold py-3 px-6 rounded-xl transition-all duration-200 flex items-center justify-center space-x-2 shadow-lg hover:shadow-xl transform hover:scale-105">
<i class="fas fa-shopping-bag"></i>
<span>View Orders</span>
</a>
<a href="{{ route('categories') }}" class="bg-gradient-to-r from-orange-600 to-red-600 hover:from-orange-700 hover:to-red-700 text-white font-semibold py-3 px-6 rounded-xl transition-all duration-200 flex items-center justify-center space-x-2 shadow-lg hover:shadow-xl transform hover:scale-105">
<i class="fas fa-store"></i>
<span>Visit Store</span>
</a>
<a href="{{ route('profile.edit') }}" class="bg-gradient-to-r from-purple-600 to-pink-600 hover:from-purple-700 hover:to-pink-700 text-white font-semibold py-3 px-6 rounded-xl transition-all duration-200 flex items-center justify-center space-x-2 shadow-lg hover:shadow-xl transform hover:scale-105">
<i class="fas fa-user-edit"></i>
<span>Edit Profile</span>
</a>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
.time-filter-btn {
background: rgba(255, 255, 255, 0.05);
color: rgba(255, 255, 255, 0.7);
border: 1px solid rgba(255, 255, 255, 0.1);
transition: all 0.3s ease;
}
.time-filter-btn:hover {
background: rgba(255, 255, 255, 0.1);
color: rgba(255, 255, 255, 0.9);
border-color: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 20px rgba(255, 255, 255, 0.1);
}
.time-filter-btn.active {
background: linear-gradient(135deg, rgba(59, 130, 246, 0.3), rgba(147, 51, 234, 0.3));
color: white;
border-color: rgba(59, 130, 246, 0.4);
box-shadow: 0 0 25px rgba(59, 130, 246, 0.2), inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
</style>
<script>
let currentPeriod = 'daily';
let revenueChart = null;
let productsChart = null;
// Simple period switching
function switchPeriod(period) {
currentPeriod = period;
// Update button states
document.querySelectorAll('.time-filter-btn').forEach(btn => btn.classList.remove('active'));
document.getElementById(`btn-${period}`).classList.add('active');
// Update titles
updateTitles(period);
// Load new data
loadDashboardData();
}
function updateTitles(period) {
const titles = {
daily: {
revenue: "Today's Revenue",
sales: "Orders Today",
customers: "Customers Today"
},
weekly: {
revenue: "This Week's Revenue",
sales: "Orders This Week",
customers: "Customers This Week"
},
monthly: {
revenue: "This Month's Revenue",
sales: "Orders This Month",
customers: "Customers This Month"
}
};
document.getElementById('revenue-title').textContent = titles[period].revenue;
document.getElementById('sales-title').textContent = titles[period].sales;
document.getElementById('customers-title').textContent = titles[period].customers;
}
async function loadDashboardData() {
try {
// Load summary data
const response = await fetch(`/api/dashboard/summary?period=${currentPeriod}`);
const data = await response.json();
if (data.success) {
updateMetricCards(data.data);
}
// Load charts
await loadCharts();
} catch (error) {
console.error('Error loading dashboard data:', error);
}
}
function updateMetricCards(data) {
// Update revenue
document.getElementById('revenue-value').textContent = `฿${data.revenue.current.toLocaleString()}`;
document.getElementById('revenue-previous').textContent = `Previous: ฿${data.revenue.previous.toLocaleString()}`;
// Update sales
document.getElementById('sales-value').textContent = data.orders.current.toLocaleString();
document.getElementById('sales-previous').textContent = `Previous: ${data.orders.previous.toLocaleString()}`;
// Update customers
document.getElementById('customers-value').textContent = data.customers.total_customers.toLocaleString();
document.getElementById('customers-previous').textContent = `Active: ${data.customers.active_today || 0}`;
// Update products
document.getElementById('products-value').textContent = data.products.total_products.toLocaleString();
}
async function loadCharts() {
await Promise.all([
loadRevenueChart(),
loadProductsChart()
]);
}
async function loadRevenueChart() {
try {
const response = await fetch(`/api/dashboard/revenue?period=${currentPeriod}`);
const result = await response.json();
const ctx = document.getElementById('revenueChart');
// Destroy existing chart
if (revenueChart) {
revenueChart.destroy();
}
if (result.success && result.data.chart_data) {
const chartData = result.data.chart_data;
const labels = chartData.map(item => {
if (item.hour !== undefined) return `${item.hour}:00`;
if (item.day_name) return item.day_name;
return item.date;
});
const data = chartData.map(item => item.revenue);
revenueChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Revenue (฿)',
data: data,
borderColor: 'rgba(52, 211, 153, 0.8)',
backgroundColor: 'rgba(52, 211, 153, 0.1)',
borderWidth: 3,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
labels: {
color: 'rgba(255, 255, 255, 0.8)'
}
}
},
scales: {
x: {
ticks: {
color: 'rgba(255, 255, 255, 0.6)'
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
},
y: {
ticks: {
color: 'rgba(255, 255, 255, 0.6)'
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
}
}
}
});
}
} catch (error) {
console.error('Error loading revenue chart:', error);
}
}
async function loadProductsChart() {
try {
const response = await fetch('/api/dashboard/sales');
const result = await response.json();
const ctx = document.getElementById('productsChart');
// Destroy existing chart
if (productsChart) {
productsChart.destroy();
}
if (result.success && result.data.top_products) {
const products = result.data.top_products;
const labels = products.map(p => p.product_name);
const data = products.map(p => parseFloat(p.total_revenue));
productsChart = new Chart(ctx, {
type: 'doughnut',
data: {
labels: labels,
datasets: [{
data: data,
backgroundColor: [
'rgba(52, 211, 153, 0.8)',
'rgba(168, 85, 247, 0.8)',
'rgba(59, 130, 246, 0.8)',
'rgba(251, 146, 60, 0.8)',
'rgba(236, 72, 153, 0.8)'
],
borderWidth: 2,
borderColor: 'rgba(255, 255, 255, 0.1)'
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right',
labels: {
color: 'rgba(255, 255, 255, 0.8)',
usePointStyle: true,
padding: 20
}
}
}
}
});
}
} catch (error) {
console.error('Error loading products chart:', error);
}
}
// Chart detail functions
async function openRevenueDetails() {
showModal('Revenue Analytics', 'fas fa-chart-line', 'emerald');
try {
const response = await fetch(`/api/dashboard/revenue?period=${currentPeriod}&detailed=true`);
const result = await response.json();
if (result.success) {
const data = result.data;
const peak = data.chart_data.reduce((max, item) => item.revenue > max.revenue ? item : max, data.chart_data[0]);
const peakLabel = peak.hour !== undefined ? `${peak.hour}:00` : (peak.day_name || peak.date);
const avgRevenue = data.summary.current / data.chart_data.length;
const content = `
<!-- Key Metrics Cards -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<div class="bg-gradient-to-br from-emerald-500/20 to-emerald-600/10 rounded-xl p-4 border border-emerald-400/20">
<div class="flex items-center justify-between mb-2">
<i class="fas fa-dollar-sign text-emerald-400 text-xl"></i>
<span class="text-xs text-emerald-400 font-semibold">TOTAL</span>
</div>
<div class="text-2xl font-bold text-white">฿${data.summary.current.toLocaleString()}</div>
<div class="text-sm text-white/60">Revenue</div>
</div>
<div class="bg-gradient-to-br from-purple-500/20 to-purple-600/10 rounded-xl p-4 border border-purple-400/20">
<div class="flex items-center justify-between mb-2">
<i class="fas fa-trending-up text-purple-400 text-xl"></i>
<span class="text-xs text-purple-400 font-semibold">GROWTH</span>
</div>
<div class="text-2xl font-bold ${data.summary.percentage_change >= 0 ? 'text-emerald-400' : 'text-red-400'}">
${data.summary.percentage_change >= 0 ? '+' : ''}${data.summary.percentage_change.toFixed(1)}%
</div>
<div class="text-sm text-white/60">vs Previous</div>
</div>
<div class="bg-gradient-to-br from-blue-500/20 to-blue-600/10 rounded-xl p-4 border border-blue-400/20">
<div class="flex items-center justify-between mb-2">
<i class="fas fa-chart-bar text-blue-400 text-xl"></i>
<span class="text-xs text-blue-400 font-semibold">AVERAGE</span>
</div>
<div class="text-2xl font-bold text-white">฿${avgRevenue.toFixed(0)}</div>
<div class="text-sm text-white/60">Per ${currentPeriod === 'daily' ? 'Hour' : 'Day'}</div>
</div>
<div class="bg-gradient-to-br from-orange-500/20 to-orange-600/10 rounded-xl p-4 border border-orange-400/20">
<div class="flex items-center justify-between mb-2">
<i class="fas fa-crown text-orange-400 text-xl"></i>
<span class="text-xs text-orange-400 font-semibold">PEAK</span>
</div>
<div class="text-2xl font-bold text-white">฿${peak.revenue.toLocaleString()}</div>
<div class="text-sm text-white/60">${peakLabel}</div>
</div>
</div>
<!-- Chart and Top Periods -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<div class="bg-white/5 rounded-xl p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<i class="fas fa-chart-area text-emerald-400 mr-2"></i>
Revenue Trend
</h3>
<div class="relative h-64">
<canvas id="detailedRevenueChart"></canvas>
</div>
</div>
<div class="bg-white/5 rounded-xl p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<i class="fas fa-trophy text-orange-400 mr-2"></i>
Top 5 Periods
</h3>
<div class="space-y-3">
${generateTopRevenuePeriods(data.chart_data, data.summary.current)}
</div>
</div>
</div>
<!-- Detailed Revenue Table -->
<div class="bg-white/5 rounded-xl p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<i class="fas fa-table text-blue-400 mr-2"></i>
Detailed Breakdown
</h3>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-white/20">
<th class="text-left py-3 px-2 text-white/80 font-semibold">Period</th>
<th class="text-right py-3 px-2 text-white/80 font-semibold">Revenue</th>
<th class="text-right py-3 px-2 text-white/80 font-semibold">Orders</th>
<th class="text-right py-3 px-2 text-white/80 font-semibold">Avg Order</th>
<th class="text-right py-3 px-2 text-white/80 font-semibold">% of Total</th>
<th class="text-center py-3 px-2 text-white/80 font-semibold">Performance</th>
</tr>
</thead>
<tbody>
${generateRevenueTable(data.chart_data, data.summary.current, avgRevenue)}
</tbody>
</table>
</div>
</div>
`;
document.getElementById('modalContent').innerHTML = content;
// Create detailed chart with animation
setTimeout(() => {
createDetailedRevenueChart(data.chart_data);
}, 300);
}
} catch (error) {
console.error('Error loading revenue details:', error);
document.getElementById('modalContent').innerHTML = '<p class="text-red-400">Error loading revenue details</p>';
}
}
async function openProductsDetails() {
showModal('Product Performance', 'fas fa-box', 'purple');
try {
const response = await fetch('/api/dashboard/sales?detailed=true');
const result = await response.json();
if (result.success && result.data.top_products) {
const products = result.data.top_products.slice(0, 8); // Show top 8 products
const totalRevenue = products.reduce((sum, p) => sum + parseFloat(p.total_revenue), 0);
const totalQuantity = products.reduce((sum, p) => sum + parseInt(p.total_quantity), 0);
const avgPrice = totalRevenue / totalQuantity;
const content = `
<!-- Summary Cards -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-6">
<div class="bg-gradient-to-br from-purple-500/20 to-purple-600/10 rounded-xl p-4 border border-purple-400/20">
<div class="flex items-center justify-between mb-2">
<i class="fas fa-box text-purple-400 text-xl"></i>
<span class="text-xs text-purple-400 font-semibold">PRODUCTS</span>
</div>
<div class="text-2xl font-bold text-white">${products.length}</div>
<div class="text-sm text-white/60">Top Sellers</div>
</div>
<div class="bg-gradient-to-br from-emerald-500/20 to-emerald-600/10 rounded-xl p-4 border border-emerald-400/20">
<div class="flex items-center justify-between mb-2">
<i class="fas fa-dollar-sign text-emerald-400 text-xl"></i>
<span class="text-xs text-emerald-400 font-semibold">REVENUE</span>
</div>
<div class="text-2xl font-bold text-white">฿${totalRevenue.toLocaleString()}</div>
<div class="text-sm text-white/60">Total Sales</div>
</div>
<div class="bg-gradient-to-br from-blue-500/20 to-blue-600/10 rounded-xl p-4 border border-blue-400/20">
<div class="flex items-center justify-between mb-2">
<i class="fas fa-shopping-cart text-blue-400 text-xl"></i>
<span class="text-xs text-blue-400 font-semibold">QUANTITY</span>
</div>
<div class="text-2xl font-bold text-white">${totalQuantity.toLocaleString()}</div>
<div class="text-sm text-white/60">Units Sold</div>
</div>
<div class="bg-gradient-to-br from-orange-500/20 to-orange-600/10 rounded-xl p-4 border border-orange-400/20">
<div class="flex items-center justify-between mb-2">
<i class="fas fa-tag text-orange-400 text-xl"></i>
<span class="text-xs text-orange-400 font-semibold">AVG PRICE</span>
</div>
<div class="text-2xl font-bold text-white">฿${avgPrice.toFixed(0)}</div>
<div class="text-sm text-white/60">Per Unit</div>
</div>
</div>
<!-- Chart and Top Products -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
<div class="bg-white/5 rounded-xl p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<i class="fas fa-chart-pie text-purple-400 mr-2"></i>
Market Share
</h3>
<div class="relative h-64">
<canvas id="detailedProductsChart"></canvas>
</div>
</div>
<div class="bg-white/5 rounded-xl p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<i class="fas fa-medal text-orange-400 mr-2"></i>
Top Performers
</h3>
<div class="space-y-3 max-h-64 overflow-y-auto">
${generateProductRankings(products, totalRevenue)}
</div>
</div>
</div>
<!-- Detailed Product Table -->
<div class="bg-white/5 rounded-xl p-6">
<h3 class="text-lg font-semibold mb-4 flex items-center">
<i class="fas fa-table text-blue-400 mr-2"></i>
Product Analysis
</h3>
<div class="overflow-x-auto">
<table class="w-full text-sm">
<thead>
<tr class="border-b border-white/20">
<th class="text-left py-3 px-2 text-white/80 font-semibold">Rank</th>
<th class="text-left py-3 px-2 text-white/80 font-semibold">Product</th>
<th class="text-right py-3 px-2 text-white/80 font-semibold">Revenue</th>
<th class="text-right py-3 px-2 text-white/80 font-semibold">Quantity</th>
<th class="text-right py-3 px-2 text-white/80 font-semibold">Avg Price</th>
<th class="text-right py-3 px-2 text-white/80 font-semibold">Market Share</th>
<th class="text-center py-3 px-2 text-white/80 font-semibold">Performance</th>
</tr>
</thead>
<tbody>
${generateProductTable(products, totalRevenue)}
</tbody>
</table>
</div>
</div>
`;
document.getElementById('modalContent').innerHTML = content;
// Create detailed chart with animation
setTimeout(() => {
createDetailedProductsChart(products);
}, 300);
}
} catch (error) {
console.error('Error loading product details:', error);
document.getElementById('modalContent').innerHTML = '<p class="text-red-400">Error loading product details</p>';
}
}
function showModal(title, icon, color) {
document.getElementById('modalTitleText').textContent = title;
document.getElementById('modalIcon').className = `${icon} mr-3 text-${color}-400`;
const modal = document.getElementById('chartModal');
const container = document.getElementById('modalContainer');
modal.classList.remove('hidden');
// Animate modal appearance
setTimeout(() => {
modal.classList.remove('opacity-0');
container.classList.remove('scale-75');
container.classList.add('scale-100');
}, 10);
}
function closeModal() {
const modal = document.getElementById('chartModal');
const container = document.getElementById('modalContainer');
// Animate modal disappearance
modal.classList.add('opacity-0');
container.classList.remove('scale-100');
container.classList.add('scale-75');
setTimeout(() => {
modal.classList.add('hidden');
}, 500);
}
// Helper functions for generating content
function getPeakPeriod(chartData) {
const peak = chartData.reduce((max, item) => item.revenue > max.revenue ? item : max, chartData[0]);
if (peak.hour !== undefined) return `${peak.hour}:00`;
if (peak.day_name) return peak.day_name;
return peak.date;
}
function generateRevenueDistribution(chartData) {
const total = chartData.reduce((sum, item) => sum + item.revenue, 0);
const sorted = chartData.sort((a, b) => b.revenue - a.revenue).slice(0, 5);
return sorted.map(item => {
const percentage = ((item.revenue / total) * 100).toFixed(1);
const label = item.hour !== undefined ? `${item.hour}:00` : (item.day_name || item.date);
return `
<div class="flex items-center justify-between">
<span class="text-white/70">${label}:</span>
<div class="flex items-center space-x-2">
<div class="w-20 bg-white/10 rounded-full h-2">
<div class="bg-emerald-400 h-2 rounded-full" style="width: ${percentage}%"></div>
</div>
<span class="text-sm font-medium">${percentage}%</span>
</div>
</div>
`;
}).join('');
}
function generateRevenueTable(chartData, totalRevenue) {
return chartData.map(item => {
const percentage = ((item.revenue / totalRevenue) * 100).toFixed(1);
const label = item.hour !== undefined ? `${item.hour}:00` : (item.day_name || item.date);
const avgOrder = item.orders > 0 ? (item.revenue / item.orders).toFixed(2) : '0.00';
return `
<tr class="border-b border-white/5">
<td class="py-2">${label}</td>
<td class="py-2 text-right font-medium">฿${item.revenue.toLocaleString()}</td>
<td class="py-2 text-right">${item.orders}</td>
<td class="py-2 text-right">฿${avgOrder}</td>
<td class="py-2 text-right">${percentage}%</td>
</tr>
`;
}).join('');
}
function generateProductCards(products, totalRevenue) {
return products.slice(0, 5).map((product, index) => {
const percentage = ((parseFloat(product.total_revenue) / totalRevenue) * 100).toFixed(1);
const colors = ['emerald', 'purple', 'blue', 'orange', 'pink'];
const color = colors[index] || 'gray';
return `
<div class="flex items-center space-x-3 p-3 bg-white/5 rounded-lg">
<div class="w-10 h-10 bg-${color}-400/20 rounded-lg flex items-center justify-center">
<span class="text-${color}-400 font-bold">#${index + 1}</span>
</div>
<div class="flex-1">
<div class="font-medium">${product.product_name}</div>
<div class="text-sm text-white/60">฿${parseFloat(product.total_revenue).toLocaleString()} (${percentage}%)</div>
</div>
</div>
`;
}).join('');
}
function generateDetailedProductCards(products, totalRevenue) {
return products.map((product, index) => {
const percentage = ((parseFloat(product.total_revenue) / totalRevenue) * 100).toFixed(1);
const avgPrice = product.total_quantity > 0 ? (parseFloat(product.total_revenue) / product.total_quantity).toFixed(2) : '0.00';
return `
<div class="bg-white/5 rounded-xl p-6">
<div class="flex items-center space-x-3 mb-4">
<div class="w-12 h-12 bg-gradient-to-br from-purple-400/20 to-purple-600/10 rounded-xl flex items-center justify-center">
<i class="fas fa-box text-purple-400"></i>
</div>
<div>
<h4 class="font-semibold">${product.product_name}</h4>
<p class="text-sm text-white/60">Rank #${index + 1}</p>
</div>
</div>
<div class="space-y-3">
<div class="flex justify-between">
<span class="text-white/70">Revenue:</span>
<span class="font-bold text-emerald-400">฿${parseFloat(product.total_revenue).toLocaleString()}</span>
</div>
<div class="flex justify-between">
<span class="text-white/70">Quantity:</span>
<span class="font-medium">${product.total_quantity}</span>
</div>
<div class="flex justify-between">
<span class="text-white/70">Avg Price:</span>
<span class="font-medium">฿${avgPrice}</span>
</div>
<div class="flex justify-between">
<span class="text-white/70">Market Share:</span>
<span class="font-medium">${percentage}%</span>
</div>
</div>
<div class="mt-4">
<div class="w-full bg-white/10 rounded-full h-2">
<div class="bg-gradient-to-r from-purple-400 to-purple-600 h-2 rounded-full" style="width: ${percentage}%"></div>
</div>
</div>
</div>
`;
}).join('');
}
function generateProductTable(products, totalRevenue) {
return products.map((product, index) => {
const percentage = ((parseFloat(product.total_revenue) / totalRevenue) * 100).toFixed(1);
const avgPrice = product.total_quantity > 0 ? (parseFloat(product.total_revenue) / product.total_quantity).toFixed(2) : '0.00';
const performance = parseFloat(percentage) > 20 ? 'Excellent' : parseFloat(percentage) > 10 ? 'Good' : 'Average';
const performanceColor = parseFloat(percentage) > 20 ? 'text-emerald-400' : parseFloat(percentage) > 10 ? 'text-blue-400' : 'text-orange-400';
return `
<tr class="border-b border-white/5">
<td class="py-3">
<div class="flex items-center space-x-2">
<span class="w-6 h-6 bg-purple-400/20 rounded flex items-center justify-center text-xs">${index + 1}</span>
<span>${product.product_name}</span>
</div>
</td>
<td class="py-3 text-right font-medium">฿${parseFloat(product.total_revenue).toLocaleString()}</td>
<td class="py-3 text-right">${product.total_quantity}</td>
<td class="py-3 text-right">฿${avgPrice}</td>
<td class="py-3 text-right">${percentage}%</td>
<td class="py-3 text-right ${performanceColor}">${performance}</td>
</tr>
`;
}).join('');
}
function createDetailedRevenueChart(chartData) {
const ctx = document.getElementById('detailedRevenueChart');
if (!ctx) return;
const labels = chartData.map(item => {
if (item.hour !== undefined) return `${item.hour}:00`;
if (item.day_name) return item.day_name;
return item.date;
});
const data = chartData.map(item => item.revenue);
new Chart(ctx, {
type: 'bar',
data: {
labels: labels,
datasets: [{
label: 'Revenue (฿)',
data: data,
backgroundColor: 'rgba(52, 211, 153, 0.6)',
borderColor: 'rgba(52, 211, 153, 0.8)',
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
labels: {
color: 'rgba(255, 255, 255, 0.8)'
}
}
},
scales: {
x: {
ticks: {
color: 'rgba(255, 255, 255, 0.6)'
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
},
y: {
ticks: {
color: 'rgba(255, 255, 255, 0.6)'
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
}
}
}
});
}
function createDetailedProductsChart(products) {
const ctx = document.getElementById('detailedProductsChart');
if (!ctx) return;
const labels = products.map(p => p.product_name);
const data = products.map(p => parseFloat(p.total_revenue));
new Chart(ctx, {
type: 'polarArea',
data: {
labels: labels,
datasets: [{
data: data,
backgroundColor: [
'rgba(52, 211, 153, 0.6)',
'rgba(168, 85, 247, 0.6)',
'rgba(59, 130, 246, 0.6)',
'rgba(251, 146, 60, 0.6)',
'rgba(236, 72, 153, 0.6)'
],
borderColor: [
'rgba(52, 211, 153, 0.8)',
'rgba(168, 85, 247, 0.8)',
'rgba(59, 130, 246, 0.8)',
'rgba(251, 146, 60, 0.8)',
'rgba(236, 72, 153, 0.8)'
],
borderWidth: 2
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'bottom',
labels: {
color: 'rgba(255, 255, 255, 0.8)',
usePointStyle: true,
padding: 20
}
}
},
scales: {
r: {
ticks: {
color: 'rgba(255, 255, 255, 0.6)'
},
grid: {
color: 'rgba(255, 255, 255, 0.1)'
}
}
}
}
});
}
// Close modal when clicking outside
document.addEventListener('click', function(event) {
const modal = document.getElementById('chartModal');
if (event.target === modal) {
closeModal();
}
});
// Helper functions for generating content
function generateTopRevenuePeriods(chartData, totalRevenue) {
const sorted = [...chartData].sort((a, b) => b.revenue - a.revenue).slice(0, 5);
return sorted.map((item, index) => {
const percentage = ((item.revenue / totalRevenue) * 100).toFixed(1);
const label = item.hour !== undefined ? `${item.hour}:00` : (item.day_name || item.date);
const colors = ['emerald', 'purple', 'blue', 'orange', 'pink'];
const color = colors[index];
return `
<div class="flex items-center justify-between p-3 bg-gradient-to-r from-${color}-500/10 to-${color}-600/5 rounded-lg border border-${color}-400/20 hover:border-${color}-400/40 transition-all duration-300">
<div class="flex items-center space-x-3">
<div class="w-8 h-8 bg-${color}-400/20 rounded-lg flex items-center justify-center">
<span class="text-sm font-bold text-${color}-400">${index + 1}</span>
</div>
<div>
<div class="font-medium text-white">${label}</div>
<div class="text-xs text-white/60">${item.orders || 0} orders</div>
</div>
</div>
<div class="text-right">
<div class="font-bold text-emerald-400">฿${item.revenue.toLocaleString()}</div>
<div class="text-xs text-white/60">${percentage}%</div>
</div>
</div>
`;
}).join('');
}
function generateRevenueTable(chartData, totalRevenue, avgRevenue) {
return chartData.map((item, index) => {
const percentage = ((item.revenue / totalRevenue) * 100).toFixed(1);
const label = item.hour !== undefined ? `${item.hour}:00` : (item.day_name || item.date);
const avgOrder = item.orders > 0 ? (item.revenue / item.orders).toFixed(0) : '0';
const performance = item.revenue > avgRevenue * 1.2 ? 'Excellent' : item.revenue > avgRevenue ? 'Good' : 'Average';
const performanceColor = item.revenue > avgRevenue * 1.2 ? 'text-emerald-400' : item.revenue > avgRevenue ? 'text-blue-400' : 'text-orange-400';
const performanceIcon = item.revenue > avgRevenue * 1.2 ? 'fas fa-star' : item.revenue > avgRevenue ? 'fas fa-thumbs-up' : 'fas fa-minus';
return `
<tr class="border-b border-white/10 hover:bg-white/5 transition-colors duration-200">
<td class="py-3 px-2 font-medium">${label}</td>
<td class="py-3 px-2 text-right font-bold text-emerald-400">฿${item.revenue.toLocaleString()}</td>
<td class="py-3 px-2 text-right">${item.orders || 0}</td>
<td class="py-3 px-2 text-right">฿${avgOrder}</td>
<td class="py-3 px-2 text-right font-medium">${percentage}%</td>
<td class="py-3 px-2 text-center">
<span class="${performanceColor} flex items-center justify-center">
<i class="${performanceIcon} mr-1 text-xs"></i>
<span class="text-xs font-medium">${performance}</span>
</span>
</td>
</tr>
`;
}).join('');
}
function generateProductRankings(products, totalRevenue) {
return products.map((product, index) => {
const percentage = ((parseFloat(product.total_revenue) / totalRevenue) * 100).toFixed(1);
const colors = ['emerald', 'purple', 'blue', 'orange', 'pink', 'indigo', 'teal', 'rose'];
const color = colors[index] || 'gray';
const avgPrice = product.total_quantity > 0 ? (parseFloat(product.total_revenue) / product.total_quantity).toFixed(0) : '0';
return `
<div class="flex items-center justify-between p-3 bg-gradient-to-r from-${color}-500/10 to-${color}-600/5 rounded-lg border border-${color}-400/20 hover:border-${color}-400/40 transition-all duration-300">
<div class="flex items-center space-x-3">
<div class="w-8 h-8 bg-${color}-400/20 rounded-lg flex items-center justify-center">
<span class="text-sm font-bold text-${color}-400">${index + 1}</span>
</div>
<div>
<div class="font-medium text-white">${product.product_name}</div>
<div class="text-xs text-white/60">${product.total_quantity} units • ฿${avgPrice} avg</div>
</div>
</div>
<div class="text-right">
<div class="font-bold text-emerald-400">฿${parseFloat(product.total_revenue).toLocaleString()}</div>
<div class="text-xs text-white/60">${percentage}%</div>
</div>
</div>
`;
}).join('');
}
function generateProductTable(products, totalRevenue) {
return products.map((product, index) => {
const percentage = ((parseFloat(product.total_revenue) / totalRevenue) * 100).toFixed(1);
const avgPrice = product.total_quantity > 0 ? (parseFloat(product.total_revenue) / product.total_quantity).toFixed(0) : '0';
const performance = parseFloat(percentage) > 20 ? 'Excellent' : parseFloat(percentage) > 10 ? 'Good' : parseFloat(percentage) > 5 ? 'Average' : 'Low';
const performanceColor = parseFloat(percentage) > 20 ? 'text-emerald-400' : parseFloat(percentage) > 10 ? 'text-blue-400' : parseFloat(percentage) > 5 ? 'text-orange-400' : 'text-red-400';
const performanceIcon = parseFloat(percentage) > 20 ? 'fas fa-star' : parseFloat(percentage) > 10 ? 'fas fa-thumbs-up' : parseFloat(percentage) > 5 ? 'fas fa-minus' : 'fas fa-arrow-down';
return `
<tr class="border-b border-white/10 hover:bg-white/5 transition-colors duration-200">
<td class="py-3 px-2">
<div class="w-6 h-6 bg-purple-400/20 rounded flex items-center justify-center">
<span class="text-xs font-bold text-purple-400">${index + 1}</span>
</div>
</td>
<td class="py-3 px-2 font-medium">${product.product_name}</td>
<td class="py-3 px-2 text-right font-bold text-emerald-400">฿${parseFloat(product.total_revenue).toLocaleString()}</td>
<td class="py-3 px-2 text-right">${product.total_quantity}</td>
<td class="py-3 px-2 text-right">฿${avgPrice}</td>
<td class="py-3 px-2 text-right font-medium">${percentage}%</td>
<td class="py-3 px-2 text-center">
<span class="${performanceColor} flex items-center justify-center">
<i class="${performanceIcon} mr-1 text-xs"></i>
<span class="text-xs font-medium">${performance}</span>
</span>
</td>
</tr>
`;
}).join('');
}
// Initialize dashboard
document.addEventListener('DOMContentLoaded', function() {
loadDashboardData();
// Auto-refresh every 30 seconds
setInterval(loadDashboardData, 30000);
});
</script>
<style>
.time-filter-btn {
@apply text-white/60 hover:text-white/90 transition-all duration-300;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.05);
}
.time-filter-btn:hover {
background: rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.15);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.time-filter-btn.active {
@apply text-white;
background: linear-gradient(135deg, rgba(59, 130, 246, 0.15), rgba(99, 102, 241, 0.10));
border: 1px solid rgba(59, 130, 246, 0.25);
box-shadow:
0 0 25px rgba(59, 130, 246, 0.15),
0 4px 15px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
/* Different colors for each button when active */
.time-filter-btn.active[data-period="daily"] {
background: linear-gradient(135deg, rgba(16, 185, 129, 0.15), rgba(5, 150, 105, 0.10));
border: 1px solid rgba(16, 185, 129, 0.25);
box-shadow:
0 0 25px rgba(16, 185, 129, 0.15),
0 4px 15px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.time-filter-btn.active[data-period="weekly"] {
background: linear-gradient(135deg, rgba(168, 85, 247, 0.15), rgba(147, 51, 234, 0.10));
border: 1px solid rgba(168, 85, 247, 0.25);
box-shadow:
0 0 25px rgba(168, 85, 247, 0.15),
0 4px 15px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
.time-filter-btn.active[data-period="monthly"] {
background: linear-gradient(135deg, rgba(245, 158, 11, 0.15), rgba(217, 119, 6, 0.10));
border: 1px solid rgba(245, 158, 11, 0.25);
box-shadow:
0 0 25px rgba(245, 158, 11, 0.15),
0 4px 15px rgba(0, 0, 0, 0.2),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
}
</style>
</x-app-layout>