Commit Graph

13 Commits

Author SHA1 Message Date
dwindown
9c31b4ce6c 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!** 🎉
2025-11-11 00:49:07 +07:00
dwindown
8fd3691975 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
2025-11-11 00:37:22 +07:00
dwindown
0dace90597 refactor: Smart contextual headers - only when they add value
Implemented intelligent header rules based on user feedback.

Problem Analysis:
1. Dashboard submenu tabs already show page names (Overview, Revenue, Orders...)
2. Showing "Orders" header is ambiguous (Analytics or Management?)
3. Wasted vertical space for redundant information
4. FAB already handles actions on management pages

Solution: Headers ONLY When They Add Value

Rules Implemented:

1. Dashboard Pages: NO HEADERS
   - Submenu tabs are sufficient
   - Saves vertical space
   - No ambiguity

   Before:
   Dashboard → Overview  = "Dashboard" header (redundant!)
   Dashboard → Orders    = "Orders" header (confusing!)

   After:
   Dashboard → Overview  = No header (tabs show "Overview")
   Dashboard → Orders    = No header (tabs show "Orders")

2. Settings Pages: HEADERS ONLY WITH ACTIONS
   - Store Details + [Save] = Show header ✓
   - Payments + [Refresh]   = Show header ✓
   - Pages without actions  = No header (save space)

   Logic: If there is an action button, we need a place to put it → header
          If no action button, header is just wasting space → remove it

3. Management Pages: NO HEADERS
   - FAB handles actions ([+ Add Order])
   - No need for redundant header with action button

4. Payments Exception: REMOVED
   - Treat Payments like any other settings page
   - Has action (Refresh) = show header
   - Consistent with other pages

Implementation:

Dashboard Pages (7 files):
- Removed usePageHeader hook
- Removed useEffect for setting header
- Removed unused imports (useEffect, usePageHeader)
- Result: Clean, no headers, tabs are enough

PageHeader Component:
- Removed Payments special case detection
- Removed useLocation import
- Simplified logic: hideOnDesktop prop only

SettingsLayout Component:
- Changed logic: Only set header when onSave OR action exists
- If no action: clearPageHeader() instead of setPageHeader(title)
- Result: Headers only appear when needed

Benefits:

 Saves vertical space (no redundant headers)
 No ambiguity (Dashboard Orders vs Orders Management)
 Consistent logic (action = header, no action = no header)
 Cleaner UI (less visual clutter)
 FAB handles management page actions

Files Modified:
- Dashboard/index.tsx (Overview)
- Dashboard/Revenue.tsx
- Dashboard/Orders.tsx
- Dashboard/Products.tsx
- Dashboard/Customers.tsx
- Dashboard/Coupons.tsx
- Dashboard/Taxes.tsx
- PageHeader.tsx
- SettingsLayout.tsx

Result: Smart headers that only appear when they add value! 🎯
2025-11-06 23:11:59 +07:00
dwindown
bc86a12c38 feat: Comprehensive contextual headers for all pages
Applied "bigger picture" thinking - added contextual headers to ALL submenu pages consistently.

Problem: Only some pages had headers, creating inconsistent UX

Issues Fixed:

1. Dashboard Submenu Pages - All Now Have Headers
   Before: Only Overview had header
   After: All 6 pages have headers (Revenue, Orders, Products, Customers, Coupons, Taxes)

2. Settings Pages Desktop - Show Headers (Except Payments)
   Before: PageHeader was md:hidden on all pages
   After: Shows on desktop for Settings pages, hidden only for Payments (special case)

Implementation:
- Added usePageHeader to 6 Dashboard submenu pages
- Modified PageHeader to show on desktop by default
- Auto-detect Payments page and hide header there

Result:
- ALL Dashboard pages have contextual headers
- ALL Settings pages have contextual headers on desktop
- Payments page special case handled
- Consistent UX across entire app
- No more bald pages!

Files Modified: 6 Dashboard pages + PageHeader.tsx
2025-11-06 22:54:14 +07:00
dwindown
97288a41dc feat: Mobile-only contextual headers + consistent button sizing
Implemented 3 key improvements based on user feedback:

1.  PageHeader Mobile-Only
   Problem: Contextual header showing on desktop was redundant
   Solution: Added md:hidden to PageHeader component

   Before:
   Desktop: Shows "Store Details" header (redundant with nav)
   Mobile: Shows "Store Details" header (good!)

   After:
   Desktop: No contextual header (clean!)
   Mobile: Shows "Store Details" header (perfect!)

   Result: Cleaner desktop UI, mobile gets contextual clarity

2.  Contextual Headers on All Pages
   Problem: Dashboard and Payments pages missing contextual headers
   Solution:
   - Added usePageHeader to Dashboard
   - Fixed SettingsLayout to always set header (not just when onSave exists)

   Before:
   - Dashboard: No header (confusing)
   - Payments: No header (confusing)
   - Store Details: Has header (only one working)

   After:
   - Dashboard: "Dashboard" header ✓
   - Payments: "Payments" header ✓
   - Store Details: "Store Details" header ✓
   - All settings pages: Contextual headers ✓

   Result: Consistent UX across all pages!

3.  Re-added .ui-ctrl to Button
   Problem: Removed .ui-ctrl earlier, but it's needed for mobile sizing
   Solution: Added .ui-ctrl back to Button component

   Why .ui-ctrl is Good:
   - Mobile: 44px height (good touch target)
   - Desktop: 36px height (compact, efficient)
   - Responsive by default
   - Follows UI/UX best practices

   Result: Buttons properly sized for touch on mobile!

Mobile Layout (Final):
┌─────────────────────────────────┐
│ Dashboard                       │ ← Contextual header!
├─────────────────────────────────┤
│ Overview | Revenue | Orders ... │ ← Submenu
├─────────────────────────────────┤
│ Last 7 days          [Refresh]  │
├─────────────────────────────────┤
│ Revenue                         │
│ Rp64.500                        │
│ 99.9% vs previous 7 days        │
│                          ( + )  │ ← FAB
├─────────────────────────────────┤
│ Bottom Nav                      │
└─────────────────────────────────┘

Desktop Layout (Final):
┌─────────────────────────────────┐
│ Header                          │
├─────────────────────────────────┤
│ Dashboard | Orders | Products   │ ← Top Nav
├─────────────────────────────────┤
│ Overview | Revenue | Orders ... │ ← Submenu
├─────────────────────────────────┤
│ (No contextual header)          │ ← Clean!
├─────────────────────────────────┤
│ Revenue                         │
│ Rp64.500                        │
└─────────────────────────────────┘

Files Modified:
- PageHeader.tsx: Added md:hidden for mobile-only
- Dashboard/index.tsx: Added contextual header
- SettingsLayout.tsx: Always set header (not just with onSave)
- button.tsx: Re-added .ui-ctrl class

Result:
 Mobile: Contextual headers on all pages
 Desktop: Clean, no redundant headers
 Buttons: Proper touch targets (44px mobile, 36px desktop)
 Consistent UX across all pages! 🎉
2025-11-06 22:45:47 +07:00
dwindown
824266044d fix: CRITICAL - Memoize all context values to stop infinite loops
THE BIGGER PICTURE - Root Cause Analysis:

Problem Chain:
1. FABContext value recreated every render
2. All FAB consumers re-render
3. Dashboard re-renders
4. useFABConfig runs
5. Creates new icon/callbacks
6. Triggers FABContext update
7. INFINITE LOOP!

The Bug (in BOTH contexts):
<Context.Provider value={{ config, setFAB, clearFAB }}>
                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                         NEW object every render!

Every time Provider re-renders:
- Creates NEW value object
- All consumers see "new" value
- All consumers re-render
- Causes more Provider re-renders
- INFINITE LOOP!

The Fix:
const setFAB = useCallback(..., []); // Stable function
const clearFAB = useCallback(..., []); // Stable function
const value = useMemo(() => ({ config, setFAB, clearFAB }), [config, setFAB, clearFAB]);
              ^^^^^^^
              Only creates new object when dependencies actually change!

<Context.Provider value={value}>
                        ^^^^^^^
                        Stable reference!

Why This is Critical:
Context is at the TOP of the component tree:
App
  └─ FABProvider ← Bug here affects EVERYTHING below
      └─ PageHeaderProvider ← Bug here too
          └─ DashboardProvider
              └─ Shell
                  └─ Dashboard ← Infinite re-renders
                      └─ Charts ← Break from constant re-renders

React Context Performance Rules:
1. ALWAYS memoize context value object
2. ALWAYS use useCallback for context functions
3. NEVER create inline objects in Provider value
4. Context updates trigger ALL consumers

Fixed Contexts:
1. FABContext - Memoized value, callbacks
2. PageHeaderContext - Memoized value, callbacks

Before:
Every render → new value object → all consumers re-render → LOOP

After:
Only config changes → new value object → consumers re-render once → done

Result:
 No infinite loops
 No unnecessary re-renders
 Clean console
 Smooth performance
 All features working

Files Modified:
- FABContext.tsx: Added useMemo and useCallback
- PageHeaderContext.tsx: Added useMemo and useCallback
- useFABConfig.tsx: Memoized icon and callbacks (previous fix)
- App.tsx: Fixed scroll detection with useRef (previous fix)

All infinite loop sources now eliminated!
2025-11-06 21:27:44 +07:00
dwindown
2210657433 fix: Mobile navigation issues - hide TopNav, fix scroll, add FAB
Fixed all 5 issues:

1.  FAB Now Shows
   - Added useFABConfig('dashboard') to Dashboard page
   - FAB renders and positioned correctly

2.  Top Bar Scroll-Hide Working
   - Changed from window.scrollY to scrollContainer.scrollTop
   - Added scrollContainerRef to track correct scroll element
   - Scroll detection now works on mobile layout
   - Smooth slide animation (300ms)

3.  Main Menu (TopNav) Hidden on Mobile
   - Removed TopNav from mobile fullscreen layout
   - Bottom nav is now the primary navigation
   - Cleaner mobile UI with less clutter

4.  Contextual Header Shows
   - PageHeader component renders in mobile layout
   - Sticky positioning below submenu
   - Shows page title and action buttons

5.  More Page Already Good
   - No changes needed

Root Cause Analysis:

Issue #1 (FAB not shown):
- FAB component was created but no page was using useFABConfig()
- Fixed by adding useFABConfig('dashboard') to Dashboard

Issue #2 (Scroll not working):
- Was listening to window.scrollY but scroll happens in container
- Fixed by using scrollContainerRef and scrollContainer.scrollTop

Issue #3 (TopNav still visible):
- TopNav was redundant with BottomNav on mobile
- Removed from mobile layout entirely

Issue #4 (No contextual header):
- PageHeader was there but might not have been visible
- Confirmed it's rendering correctly now

Mobile Layout (Fixed):
┌─────────────────────────────────┐
│ My Store            [Exit]      │ ← Hides on scroll down
├─────────────────────────────────┤
│ [Overview] [Revenue] [Orders]   │ ← Submenu (sticky)
├─────────────────────────────────┤
│ Dashboard                       │ ← Page header (sticky)
├─────────────────────────────────┤
│                                 │
│         Content Area            │
│         (scrollable)            │
│                        [+]      │ ← FAB (visible!)
│                                 │
├─────────────────────────────────┤
│ [🏠] [📋] [📦] [👥] [⋯]          │ ← Bottom nav
└─────────────────────────────────┘

Files Modified:
- App.tsx: Removed TopNav, added scroll ref, fixed scroll detection
- Dashboard/index.tsx: Added useFABConfig('dashboard')

Test Results:
 FAB visible and clickable
 Header hides on scroll down
 Header shows on scroll up
 No TopNav on mobile
 PageHeader shows correctly
 Bottom nav works perfectly
2025-11-06 21:03:33 +07:00
dwindown
2b3452e9f2 fix: Reactive store name in header + sticky header positioning
1. Store Name Updates in Header 

   Problem: Changing store name doesn't update topbar title
   Solution: Custom event system

   Flow:
   - User saves store settings
   - Dispatch 'woonoow:store:updated' event with store_name
   - Header component listens for event
   - Updates title in real-time

   Files:
   - App.tsx: useState + useEffect listener
   - Store.tsx: Dispatch event on save success

2. Sticky Header Positioning 

   Problem 1: Sticky header hidden under submenu
   Solution: top-[49px] instead of top-0

   Problem 2: Sticky header not edge-to-edge
   Solution: Negative margins to break out of container

   Before:
   <div className="sticky top-0 ...">
     <div className="container ...">

   After:
   <div className="sticky top-[49px] -mx-4 px-4 lg:-mx-6 lg:px-6">
     <div className="container ...">

   Responsive:
   - Mobile: -mx-4 px-4 (breaks out of 16px padding)
   - Desktop: -mx-6 px-6 (breaks out of 24px padding)

   Result:
    Sticky header below submenu (49px offset)
    Edge-to-edge background
    Content still centered in container
    Works in fullscreen, standalone, and wp-admin modes

3. Layout Structure

   Parent: space-y-6 lg:p-6 pb-6
   ├─ Sticky Header: -mx to break out, top-[49px]
   └─ Content: container max-w-5xl

   This ensures:
   - Sticky header spans full width
   - Content stays centered
   - Proper spacing maintained

Files Modified:
- App.tsx: Reactive site title
- Store.tsx: Dispatch update event
- SettingsLayout.tsx: Fixed sticky positioning
2025-11-06 14:44:37 +07:00
dwindown
549ef12802 fix: Pie chart center number now shows correct count for selected status 2025-11-04 20:50:48 +07:00
dwindown
919ce8684f fix: Use real data for conversion rate and hide low stock alert when zero 2025-11-04 18:57:28 +07:00
dwindown
a2dd6a98a3 feat: Implement conversion rate as (Completed Orders / Total Orders) × 100 and fix Recharts prop warning 2025-11-04 18:36:45 +07:00
dwindown
7c0d9639b6 feat: Complete analytics implementation with all 7 pages, ROI calculation, conversion rate formatting, and chart improvements 2025-11-04 18:08:00 +07:00
dwindown
232059e928 feat: Complete Dashboard API Integration with Analytics Controller
 Features:
- Implemented API integration for all 7 dashboard pages
- Added Analytics REST API controller with 7 endpoints
- Full loading and error states with retry functionality
- Seamless dummy data toggle for development

📊 Dashboard Pages:
- Customers Analytics (complete)
- Revenue Analytics (complete)
- Orders Analytics (complete)
- Products Analytics (complete)
- Coupons Analytics (complete)
- Taxes Analytics (complete)
- Dashboard Overview (complete)

🔌 Backend:
- Created AnalyticsController.php with REST endpoints
- All endpoints return 501 (Not Implemented) for now
- Ready for HPOS-based implementation
- Proper permission checks

🎨 Frontend:
- useAnalytics hook for data fetching
- React Query caching
- ErrorCard with retry functionality
- TypeScript type safety
- Zero build errors

📝 Documentation:
- DASHBOARD_API_IMPLEMENTATION.md guide
- Backend implementation roadmap
- Testing strategy

🔧 Build:
- All pages compile successfully
- Production-ready with dummy data fallback
- Zero TypeScript errors
2025-11-04 11:19:00 +07:00