feat: Mobile chart optimization + VIP customer settings
## Task 4: Mobile Chart Optimization ✅ **Problem:** Too many data points = tight/crowded lines on mobile **Solution:** Horizontal scroll container **Implementation:** - ChartCard component enhanced with mobile scroll - Calculates minimum width based on data points (40px per point) - Desktop: Full width responsive - Mobile: Fixed width chart in scrollable container ```tsx // ChartCard.tsx const mobileMinWidth = Math.max(600, dataPoints * 40); <div className="overflow-x-auto -mx-6 px-6 md:mx-0 md:px-0"> <div style={{ minWidth: `${mobileMinWidth}px` }}> {children} </div> </div> ``` **Benefits:** - ✅ All data visible (no loss) - ✅ Natural swipe gesture - ✅ Readable spacing - ✅ Works for all chart types - ✅ No data aggregation needed --- ## Task 5: VIP Customer Settings ✅ **New Feature:** Configure VIP customer qualification criteria ### Backend (PHP) **Files Created:** - `includes/Compat/CustomerSettingsProvider.php` - VIP settings management - VIP detection logic - Customer stats calculation **API Endpoints:** - `GET /store/customer-settings` - Fetch settings - `POST /store/customer-settings` - Save settings **Settings:** ```php woonoow_vip_min_spent = 1000 woonoow_vip_min_orders = 10 woonoow_vip_timeframe = 'all' | '30' | '90' | '365' woonoow_vip_require_both = true woonoow_vip_exclude_refunded = true ``` **VIP Detection:** ```php CustomerSettingsProvider::is_vip_customer($customer_id) CustomerSettingsProvider::get_vip_stats($customer_id) ``` ### Frontend (React) **Files Created:** - `admin-spa/src/routes/Settings/Customers.tsx` **Features:** - 💰 Minimum total spent (currency input) - �� Minimum order count (number input) - 📅 Timeframe selector (all-time, 30/90/365 days) - ⚙️ Require both criteria toggle - 🚫 Exclude refunded orders toggle - 👑 Live preview of VIP qualification **Navigation:** - Added to Settings menu - Route: `/settings/customers` - Position: After Tax, before Notifications --- ## Summary ✅ **Mobile Charts:** Horizontal scroll for readable spacing ✅ **VIP Settings:** Complete backend + frontend implementation **Mobile Chart Strategy:** - Minimum 600px width - 40px per data point - Smooth horizontal scroll - Desktop remains responsive **VIP Customer System:** - Flexible qualification criteria - Multiple timeframes - AND/OR logic support - Refunded order exclusion - Ready for customer list integration **All tasks complete!** 🎉
This commit is contained in:
@@ -222,6 +222,7 @@ export default function CouponsReport() {
|
||||
<ChartCard
|
||||
title={__('Coupon Usage Over Time')}
|
||||
description={__('Daily coupon usage and discount amount')}
|
||||
dataPoints={chartData.length}
|
||||
>
|
||||
{chartData.length === 0 || chartData.every((d: any) => d.uses === 0) ? (
|
||||
<div className="flex items-center justify-center h-[300px]">
|
||||
|
||||
@@ -205,6 +205,7 @@ export default function OrdersAnalytics() {
|
||||
<ChartCard
|
||||
title={__('Orders Over Time')}
|
||||
description={__('Daily order count and status breakdown')}
|
||||
dataPoints={chartData.length}
|
||||
>
|
||||
{chartData.length === 0 || chartData.every((d: any) => d.orders === 0) ? (
|
||||
<div className="flex items-center justify-center h-[300px]">
|
||||
|
||||
@@ -382,10 +382,11 @@ export default function RevenueAnalytics() {
|
||||
{/* Revenue Chart */}
|
||||
<ChartCard
|
||||
title={__('Revenue Over Time')}
|
||||
description={__('Gross revenue, net revenue, and refunds')}
|
||||
description={__('Gross and net revenue trends')}
|
||||
dataPoints={chartData.length}
|
||||
actions={
|
||||
<Select value={granularity} onValueChange={(v: any) => setGranularity(v)}>
|
||||
<SelectTrigger className="w-[120px]">
|
||||
<Select value={granularity} onValueChange={(value: any) => setGranularity(value)}>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
|
||||
@@ -8,6 +8,8 @@ interface ChartCardProps {
|
||||
actions?: ReactNode;
|
||||
loading?: boolean;
|
||||
height?: number;
|
||||
enableMobileScroll?: boolean; // Enable horizontal scroll on mobile
|
||||
dataPoints?: number; // Number of data points (for calculating mobile width)
|
||||
}
|
||||
|
||||
export function ChartCard({
|
||||
@@ -16,8 +18,14 @@ export function ChartCard({
|
||||
children,
|
||||
actions,
|
||||
loading = false,
|
||||
height = 300
|
||||
height = 300,
|
||||
enableMobileScroll = true,
|
||||
dataPoints = 30
|
||||
}: ChartCardProps) {
|
||||
// Calculate minimum width for mobile based on data points
|
||||
// Each data point needs ~40px for readability
|
||||
const mobileMinWidth = Math.max(600, dataPoints * 40);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="rounded-lg border bg-card p-6">
|
||||
@@ -46,7 +54,23 @@ export function ChartCard({
|
||||
</div>
|
||||
{actions && <div className="flex gap-2">{actions}</div>}
|
||||
</div>
|
||||
{children}
|
||||
|
||||
{/* Chart container with mobile scroll */}
|
||||
{enableMobileScroll ? (
|
||||
<div className="overflow-x-auto -mx-6 px-6 md:mx-0 md:px-0">
|
||||
<div
|
||||
className="md:w-full"
|
||||
style={{
|
||||
minWidth: `${mobileMinWidth}px`,
|
||||
width: '100%'
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user