feat: Fill missing dates in charts + no-data states

## Task 1: Fill Missing Dates in Chart Data 

**Issue:** Charts only show dates with actual data, causing:
- Gaps in timeline
- Tight/crowded lines on mobile
- Inconsistent X-axis

**Solution:** Backend now fills ALL dates in range with zeros

**Files Updated:**
- `includes/Api/AnalyticsController.php`
  - `calculate_revenue_metrics()` - Revenue chart
  - `calculate_orders_metrics()` - Orders chart
  - `calculate_coupons_metrics()` - Coupons chart

**Implementation:**
```php
// Create data map from query results
$data_map = [];
foreach ($chart_data_raw as $row) {
    $data_map[$row->date] = [...];
}

// Fill ALL dates in range
for ($i = $days - 1; $i >= 0; $i--) {
    $date = date('Y-m-d', strtotime("-{$i} days"));
    if (isset($data_map[$date])) {
        // Use real data
    } else {
        // Fill with zeros
    }
}
```

**Result:**
-  Consistent X-axis with all dates
-  No gaps in timeline
-  Better mobile display (evenly spaced)

---

## Task 2: No-Data States for Charts 

**Issue:** Charts show broken/empty state when no data

**Solution:** Show friendly message like Overview does

**Files Updated:**
- `admin-spa/src/routes/Dashboard/Revenue.tsx`
- `admin-spa/src/routes/Dashboard/Orders.tsx`
- `admin-spa/src/routes/Dashboard/Coupons.tsx`

**Implementation:**
```tsx
{chartData.length === 0 || chartData.every(d => d.value === 0) ? (
  <div className="flex items-center justify-center h-[300px]">
    <div className="text-center">
      <Package className="w-12 h-12 text-muted-foreground mx-auto mb-3" />
      <p className="text-muted-foreground font-medium">
        No {type} data available
      </p>
      <p className="text-sm text-muted-foreground mt-1">
        Data will appear once you have {action}
      </p>
    </div>
  </div>
) : (
  <ResponsiveContainer>...</ResponsiveContainer>
)}
```

**Result:**
-  Revenue: "No revenue data available"
-  Orders: "No orders data available"
-  Coupons: "No coupon usage data available"
-  Consistent with Overview page
-  User-friendly empty states

---

## Summary

 **Backend:** All dates filled in chart data
 **Frontend:** No-data states added to 3 charts
 **UX:** Consistent, professional empty states

**Next:** VIP customer settings + mobile chart optimization
This commit is contained in:
dwindown
2025-11-11 00:37:22 +07:00
parent 0aafb65ec0
commit 8fd3691975
4 changed files with 115 additions and 21 deletions

View File

@@ -1,6 +1,6 @@
import React, { useState, useMemo } from 'react';
import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts';
import { Tag, DollarSign, TrendingUp, ShoppingCart } from 'lucide-react';
import { Tag, DollarSign, TrendingUp, ShoppingCart, Package } from 'lucide-react';
import { __ } from '@/lib/i18n';
import { formatMoney, getStoreCurrency } from '@/lib/currency';
import { useDashboardPeriod } from '@/hooks/useDashboardPeriod';
@@ -223,8 +223,19 @@ export default function CouponsReport() {
title={__('Coupon Usage Over Time')}
description={__('Daily coupon usage and discount amount')}
>
<ResponsiveContainer width="100%" height={300}>
<LineChart data={chartData}>
{chartData.length === 0 || chartData.every((d: any) => d.uses === 0) ? (
<div className="flex items-center justify-center h-[300px]">
<div className="text-center">
<Package className="w-12 h-12 text-muted-foreground mx-auto mb-3" />
<p className="text-muted-foreground font-medium">{__('No coupon usage data available')}</p>
<p className="text-sm text-muted-foreground mt-1">
{__('Coupon data will appear once customers use coupons')}
</p>
</div>
</div>
) : (
<ResponsiveContainer width="100%" height={300}>
<LineChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" className="stroke-muted" />
<XAxis
dataKey="date"
@@ -283,6 +294,7 @@ export default function CouponsReport() {
/>
</LineChart>
</ResponsiveContainer>
)}
</ChartCard>
<ChartCard

View File

@@ -206,8 +206,19 @@ export default function OrdersAnalytics() {
title={__('Orders Over Time')}
description={__('Daily order count and status breakdown')}
>
<ResponsiveContainer width="100%" height={300}>
<ComposedChart data={chartData}>
{chartData.length === 0 || chartData.every((d: any) => d.orders === 0) ? (
<div className="flex items-center justify-center h-[300px]">
<div className="text-center">
<Package className="w-12 h-12 text-muted-foreground mx-auto mb-3" />
<p className="text-muted-foreground font-medium">{__('No orders data available')}</p>
<p className="text-sm text-muted-foreground mt-1">
{__('Order data will appear once you have orders')}
</p>
</div>
</div>
) : (
<ResponsiveContainer width="100%" height={300}>
<ComposedChart data={chartData}>
<defs>
<linearGradient id="totalOrdersGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3}/>
@@ -287,6 +298,7 @@ export default function OrdersAnalytics() {
/>
</ComposedChart>
</ResponsiveContainer>
)}
</ChartCard>
{/* Two Column Layout */}

View File

@@ -1,6 +1,6 @@
import React, { useState, useMemo } from 'react';
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend } from 'recharts';
import { DollarSign, TrendingUp, TrendingDown, CreditCard, Truck, RefreshCw } from 'lucide-react';
import { DollarSign, TrendingUp, TrendingDown, CreditCard, Truck, RefreshCw, Package } from 'lucide-react';
import { __ } from '@/lib/i18n';
import { formatMoney, getStoreCurrency } from '@/lib/currency';
import { useDashboardPeriod } from '@/hooks/useDashboardPeriod';
@@ -396,8 +396,19 @@ export default function RevenueAnalytics() {
</Select>
}
>
<ResponsiveContainer width="100%" height={300}>
<AreaChart data={chartData}>
{chartData.length === 0 || chartData.every((d: any) => d.gross === 0 && d.net === 0) ? (
<div className="flex items-center justify-center h-[300px]">
<div className="text-center">
<Package className="w-12 h-12 text-muted-foreground mx-auto mb-3" />
<p className="text-muted-foreground font-medium">{__('No revenue data available')}</p>
<p className="text-sm text-muted-foreground mt-1">
{__('Revenue data will appear once you have completed orders')}
</p>
</div>
</div>
) : (
<ResponsiveContainer width="100%" height={300}>
<AreaChart data={chartData}>
<defs>
<linearGradient id="colorGross" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3}/>
@@ -467,6 +478,7 @@ export default function RevenueAnalytics() {
/>
</AreaChart>
</ResponsiveContainer>
)}
</ChartCard>
{/* Revenue Breakdown Tables */}