docs: Update PROGRESS_NOTE with complete dashboard analytics implementation and cleanup temporary docs
This commit is contained in:
@@ -1,268 +0,0 @@
|
||||
# 👥 Customer Analytics - Data Logic Documentation
|
||||
|
||||
**Last Updated:** Nov 4, 2025 12:48 AM (GMT+7)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
This document defines the business logic for Customer Analytics metrics, clarifying which data is **period-based** vs **store-level**.
|
||||
|
||||
---
|
||||
|
||||
## 📊 Stat Cards Layout
|
||||
|
||||
### Row 1: Period-Based Metrics (with comparisons)
|
||||
```
|
||||
[New Customers] [Retention Rate] [Avg Orders/Customer] [Avg Lifetime Value]
|
||||
```
|
||||
|
||||
### Row 2: Store-Level + Segment Data
|
||||
```
|
||||
[Total Customers] [Returning] [VIP Customers] [At Risk]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Metric Definitions
|
||||
|
||||
### 1. **New Customers** ✅ Period-Based
|
||||
- **Definition:** Number of customers who made their first purchase in the selected period
|
||||
- **Affected by Period:** YES
|
||||
- **Has Comparison:** YES (vs previous period)
|
||||
- **Logic:**
|
||||
```typescript
|
||||
new_customers = sum(acquisition_chart[period].new_customers)
|
||||
change = ((current - previous) / previous) × 100
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. **Retention Rate** ✅ Period-Based
|
||||
- **Definition:** Percentage of customers who returned in the selected period
|
||||
- **Affected by Period:** YES
|
||||
- **Has Comparison:** YES (vs previous period)
|
||||
- **Logic:**
|
||||
```typescript
|
||||
retention_rate = (returning_customers / total_in_period) × 100
|
||||
total_in_period = new_customers + returning_customers
|
||||
```
|
||||
- **Previous Implementation:** ❌ Was store-level (global retention)
|
||||
- **Fixed:** ✅ Now calculates from period data
|
||||
|
||||
---
|
||||
|
||||
### 3. **Avg Orders/Customer** ❌ Store-Level
|
||||
- **Definition:** Average number of orders per customer (all-time)
|
||||
- **Affected by Period:** NO
|
||||
- **Has Comparison:** NO
|
||||
- **Logic:**
|
||||
```typescript
|
||||
avg_orders_per_customer = total_orders / total_customers
|
||||
```
|
||||
- **Rationale:** This is a ratio metric representing customer behavior patterns, not a time-based sum
|
||||
|
||||
---
|
||||
|
||||
### 4. **Avg Lifetime Value** ❌ Store-Level
|
||||
- **Definition:** Average total revenue generated by a customer over their entire lifetime
|
||||
- **Affected by Period:** NO
|
||||
- **Has Comparison:** NO
|
||||
- **Logic:**
|
||||
```typescript
|
||||
avg_ltv = total_revenue_all_time / total_customers
|
||||
```
|
||||
- **Previous Implementation:** ❌ Was scaled by period factor
|
||||
- **Fixed:** ✅ Now always shows store-level LTV
|
||||
- **Rationale:** LTV is cumulative by definition - scaling it by period makes no business sense
|
||||
|
||||
---
|
||||
|
||||
### 5. **Total Customers** ❌ Store-Level
|
||||
- **Definition:** Total number of customers who have ever placed an order
|
||||
- **Affected by Period:** NO
|
||||
- **Has Comparison:** NO
|
||||
- **Display:** Shows "All-time total" subtitle
|
||||
- **Logic:**
|
||||
```typescript
|
||||
total_customers = data.overview.total_customers
|
||||
```
|
||||
- **Previous Implementation:** ❌ Was calculated from period data
|
||||
- **Fixed:** ✅ Now shows all-time total
|
||||
- **Rationale:** Represents store's total customer base, not acquisitions in period
|
||||
|
||||
---
|
||||
|
||||
### 6. **Returning Customers** ✅ Period-Based
|
||||
- **Definition:** Number of existing customers who made repeat purchases in the selected period
|
||||
- **Affected by Period:** YES
|
||||
- **Has Comparison:** NO (shown as segment card)
|
||||
- **Display:** Shows "In selected period" subtitle
|
||||
- **Logic:**
|
||||
```typescript
|
||||
returning_customers = sum(acquisition_chart[period].returning_customers)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. **VIP Customers** ❌ Store-Level
|
||||
- **Definition:** Customers who qualify as VIP based on lifetime criteria
|
||||
- **Qualification:** 10+ orders OR lifetime value > Rp5,000,000
|
||||
- **Affected by Period:** NO
|
||||
- **Has Comparison:** NO
|
||||
- **Logic:**
|
||||
```typescript
|
||||
vip_customers = data.segments.vip
|
||||
```
|
||||
- **Rationale:** VIP status is based on cumulative lifetime behavior, not period activity
|
||||
|
||||
---
|
||||
|
||||
### 8. **At Risk Customers** ❌ Store-Level
|
||||
- **Definition:** Customers with no orders in the last 90 days
|
||||
- **Affected by Period:** NO
|
||||
- **Has Comparison:** NO
|
||||
- **Logic:**
|
||||
```typescript
|
||||
at_risk = data.segments.at_risk
|
||||
```
|
||||
- **Rationale:** At-risk status is a current state classification, not a time-based metric
|
||||
|
||||
---
|
||||
|
||||
## 📊 Charts & Tables
|
||||
|
||||
### Customer Acquisition Chart ✅ Period-Based
|
||||
- **Data:** New vs Returning customers over time
|
||||
- **Filtered by Period:** YES
|
||||
- **Logic:**
|
||||
```typescript
|
||||
chartData = period === 'all'
|
||||
? data.acquisition_chart
|
||||
: data.acquisition_chart.slice(-parseInt(period))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Lifetime Value Distribution ❌ Store-Level
|
||||
- **Data:** Distribution of customers across LTV ranges
|
||||
- **Filtered by Period:** NO
|
||||
- **Logic:**
|
||||
```typescript
|
||||
ltv_distribution = data.ltv_distribution // Always all-time
|
||||
```
|
||||
- **Rationale:** LTV is cumulative, distribution shows overall customer value spread
|
||||
|
||||
---
|
||||
|
||||
### Top Customers Table ✅ Period-Based
|
||||
- **Data:** Customers with highest spending in selected period
|
||||
- **Filtered by Period:** YES
|
||||
- **Logic:**
|
||||
```typescript
|
||||
filteredTopCustomers = period === 'all'
|
||||
? data.top_customers
|
||||
: data.top_customers.map(c => ({
|
||||
...c,
|
||||
total_spent: c.total_spent * (period / 30),
|
||||
orders: c.orders * (period / 30)
|
||||
}))
|
||||
```
|
||||
- **Previous Implementation:** ❌ Was always all-time
|
||||
- **Fixed:** ✅ Now respects period selection
|
||||
- **Note:** Uses global period selector (no individual toggle needed)
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Comparison Logic
|
||||
|
||||
### When Comparisons Are Shown:
|
||||
- Period is **7, 14, or 30 days**
|
||||
- Metric is **period-based**
|
||||
- Compares current period vs previous period of same length
|
||||
|
||||
### When Comparisons Are Hidden:
|
||||
- Period is **"All Time"** (no previous period to compare)
|
||||
- Metric is **store-level** (not time-based)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Summary Table
|
||||
|
||||
| Metric | Type | Period-Based? | Has Comparison? | Notes |
|
||||
|--------|------|---------------|-----------------|-------|
|
||||
| New Customers | Period | ✅ YES | ✅ YES | Acquisitions in period |
|
||||
| Retention Rate | Period | ✅ YES | ✅ YES | **FIXED** - Now period-based |
|
||||
| Avg Orders/Customer | Store | ❌ NO | ❌ NO | Ratio, not sum |
|
||||
| Avg Lifetime Value | Store | ❌ NO | ❌ NO | **FIXED** - Now store-level |
|
||||
| Total Customers | Store | ❌ NO | ❌ NO | **FIXED** - Now all-time total |
|
||||
| Returning Customers | Period | ✅ YES | ❌ NO | Segment card |
|
||||
| VIP Customers | Store | ❌ NO | ❌ NO | Lifetime qualification |
|
||||
| At Risk | Store | ❌ NO | ❌ NO | Current state |
|
||||
| Acquisition Chart | Period | ✅ YES | - | Filtered by period |
|
||||
| LTV Distribution | Store | ❌ NO | - | All-time distribution |
|
||||
| Top Customers Table | Period | ✅ YES | - | **FIXED** - Now filtered |
|
||||
|
||||
---
|
||||
|
||||
## ✅ Changes Made
|
||||
|
||||
### 1. **Total Customers**
|
||||
- **Before:** Calculated from period data (new + returning)
|
||||
- **After:** Shows all-time total from `data.overview.total_customers`
|
||||
- **Reason:** Represents store's customer base, not period acquisitions
|
||||
|
||||
### 2. **Avg Lifetime Value**
|
||||
- **Before:** Scaled by period factor `avg_ltv * (period / 30)`
|
||||
- **After:** Always shows store-level `data.overview.avg_ltv`
|
||||
- **Reason:** LTV is cumulative by definition, cannot be period-based
|
||||
|
||||
### 3. **Retention Rate**
|
||||
- **Before:** Store-level `data.overview.retention_rate`
|
||||
- **After:** Calculated from period data `(returning / total_in_period) × 100`
|
||||
- **Reason:** More useful to see retention in specific periods
|
||||
|
||||
### 4. **Top Customers Table**
|
||||
- **Before:** Always showed all-time data
|
||||
- **After:** Filtered by selected period
|
||||
- **Reason:** Useful to see top spenders in specific timeframes
|
||||
|
||||
### 5. **Card Layout Reordered**
|
||||
- **Row 1:** Period-based metrics with comparisons
|
||||
- **Row 2:** Store-level + segment data
|
||||
- **Reason:** Better visual grouping and user understanding
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Business Value
|
||||
|
||||
### Period-Based Metrics Answer:
|
||||
- "How many new customers did we acquire this week?"
|
||||
- "What's our retention rate for the last 30 days?"
|
||||
- "Who are our top spenders this month?"
|
||||
|
||||
### Store-Level Metrics Answer:
|
||||
- "How many total customers do we have?"
|
||||
- "What's the average lifetime value of our customers?"
|
||||
- "How many VIP customers do we have?"
|
||||
- "How many customers are at risk of churning?"
|
||||
|
||||
---
|
||||
|
||||
## 🔮 Future Enhancements
|
||||
|
||||
### Custom Date Range (Planned)
|
||||
When custom date range is implemented:
|
||||
- Period-based metrics will calculate from custom range
|
||||
- Store-level metrics remain unchanged
|
||||
- Comparisons will be hidden (no "previous custom range")
|
||||
|
||||
### Real API Integration
|
||||
Current implementation uses dummy data with period scaling. Real API will:
|
||||
- Fetch period-specific data from backend
|
||||
- Calculate metrics server-side
|
||||
- Return proper comparison data
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ Complete - All customer analytics metrics now have correct business logic!
|
||||
@@ -1,274 +0,0 @@
|
||||
# 📊 Dashboard API Implementation Guide
|
||||
|
||||
**Last Updated:** Nov 4, 2025 10:50 AM (GMT+7)
|
||||
|
||||
---
|
||||
|
||||
## ✅ Frontend Implementation Complete
|
||||
|
||||
### **Implemented Pages (6/7):**
|
||||
|
||||
1. ✅ **Customers.tsx** - Full API integration
|
||||
2. ✅ **Revenue.tsx** - Full API integration
|
||||
3. ✅ **Orders.tsx** - Full API integration
|
||||
4. ✅ **Products.tsx** - Full API integration
|
||||
5. ✅ **Coupons.tsx** - Full API integration
|
||||
6. ✅ **Taxes.tsx** - Full API integration
|
||||
7. ⚠️ **Dashboard/index.tsx** - Partial (has syntax issues, but builds)
|
||||
|
||||
### **Features Implemented:**
|
||||
- ✅ API integration via `useAnalytics` hook
|
||||
- ✅ Loading states with spinner
|
||||
- ✅ Error states with `ErrorCard` and retry functionality
|
||||
- ✅ Dummy data toggle (works seamlessly)
|
||||
- ✅ TypeScript type safety
|
||||
- ✅ React Query caching
|
||||
- ✅ Proper React Hooks ordering
|
||||
|
||||
---
|
||||
|
||||
## 🔌 Backend API Structure
|
||||
|
||||
### **Created Files:**
|
||||
|
||||
#### `/includes/Api/AnalyticsController.php`
|
||||
Main controller handling all analytics endpoints.
|
||||
|
||||
**Registered Endpoints:**
|
||||
```
|
||||
GET /wp-json/woonoow/v1/analytics/overview
|
||||
GET /wp-json/woonoow/v1/analytics/revenue?granularity=day
|
||||
GET /wp-json/woonoow/v1/analytics/orders
|
||||
GET /wp-json/woonoow/v1/analytics/products
|
||||
GET /wp-json/woonoow/v1/analytics/customers
|
||||
GET /wp-json/woonoow/v1/analytics/coupons
|
||||
GET /wp-json/woonoow/v1/analytics/taxes
|
||||
```
|
||||
|
||||
**Current Status:**
|
||||
- All endpoints return `501 Not Implemented` error
|
||||
- This triggers frontend to use dummy data
|
||||
- Ready for actual implementation
|
||||
|
||||
#### `/includes/Api/Routes.php`
|
||||
Updated to register `AnalyticsController::register_routes()`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps: Backend Implementation
|
||||
|
||||
### **Phase 1: Revenue Analytics** (Highest Priority)
|
||||
|
||||
**Endpoint:** `GET /analytics/revenue`
|
||||
|
||||
**Query Strategy:**
|
||||
```php
|
||||
// Use WooCommerce HPOS tables
|
||||
global $wpdb;
|
||||
|
||||
// Query wp_wc_orders table
|
||||
$orders = $wpdb->get_results("
|
||||
SELECT
|
||||
DATE(date_created_gmt) as date,
|
||||
SUM(total_amount) as gross,
|
||||
SUM(total_amount - tax_amount) as net,
|
||||
SUM(tax_amount) as tax,
|
||||
COUNT(*) as orders
|
||||
FROM {$wpdb->prefix}wc_orders
|
||||
WHERE status IN ('wc-completed', 'wc-processing')
|
||||
AND date_created_gmt >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
||||
GROUP BY DATE(date_created_gmt)
|
||||
ORDER BY date ASC
|
||||
");
|
||||
```
|
||||
|
||||
**Expected Response Format:**
|
||||
```json
|
||||
{
|
||||
"overview": {
|
||||
"gross_revenue": 125000000,
|
||||
"net_revenue": 112500000,
|
||||
"tax": 12500000,
|
||||
"refunds": 2500000,
|
||||
"avg_order_value": 250000
|
||||
},
|
||||
"chart_data": [
|
||||
{
|
||||
"date": "2025-10-05",
|
||||
"gross": 4500000,
|
||||
"net": 4050000,
|
||||
"tax": 450000,
|
||||
"refunds": 100000,
|
||||
"shipping": 50000
|
||||
}
|
||||
],
|
||||
"by_product": [...],
|
||||
"by_category": [...],
|
||||
"by_payment_method": [...],
|
||||
"by_shipping_method": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### **Phase 2: Orders Analytics**
|
||||
|
||||
**Key Metrics to Calculate:**
|
||||
- Total orders by status
|
||||
- Fulfillment rate
|
||||
- Cancellation rate
|
||||
- Average processing time
|
||||
- Orders by day of week
|
||||
- Orders by hour
|
||||
|
||||
**HPOS Tables:**
|
||||
- `wp_wc_orders` - Main orders table
|
||||
- `wp_wc_order_operational_data` - Status changes, timestamps
|
||||
|
||||
### **Phase 3: Customers Analytics**
|
||||
|
||||
**Key Metrics:**
|
||||
- New vs returning customers
|
||||
- Customer retention rate
|
||||
- Average orders per customer
|
||||
- Customer lifetime value (LTV)
|
||||
- VIP customers (high spenders)
|
||||
- At-risk customers (inactive)
|
||||
|
||||
**Data Sources:**
|
||||
- `wp_wc_orders` - Order history
|
||||
- `wp_wc_customer_lookup` - Customer aggregates (if using WC Analytics)
|
||||
- Custom queries for LTV calculation
|
||||
|
||||
### **Phase 4: Products Analytics**
|
||||
|
||||
**Key Metrics:**
|
||||
- Top selling products
|
||||
- Revenue by product
|
||||
- Revenue by category
|
||||
- Stock analysis (low stock, out of stock)
|
||||
- Product performance trends
|
||||
|
||||
**Data Sources:**
|
||||
- `wp_wc_order_product_lookup` - Product sales data
|
||||
- `wp_posts` + `wp_postmeta` - Product data
|
||||
- `wp_term_relationships` - Categories
|
||||
|
||||
### **Phase 5: Coupons & Taxes**
|
||||
|
||||
**Coupons:**
|
||||
- Usage statistics
|
||||
- Discount amounts
|
||||
- Revenue generated with coupons
|
||||
- Top performing coupons
|
||||
|
||||
**Taxes:**
|
||||
- Tax collected by rate
|
||||
- Tax by location
|
||||
- Orders with tax
|
||||
|
||||
---
|
||||
|
||||
## 📝 Implementation Checklist
|
||||
|
||||
### **For Each Endpoint:**
|
||||
|
||||
- [ ] Write HPOS-compatible queries
|
||||
- [ ] Add date range filtering
|
||||
- [ ] Implement caching (transients)
|
||||
- [ ] Add error handling
|
||||
- [ ] Test with real WooCommerce data
|
||||
- [ ] Optimize query performance
|
||||
- [ ] Add query result pagination if needed
|
||||
- [ ] Document response format
|
||||
|
||||
### **Performance Considerations:**
|
||||
|
||||
1. **Use Transients for Caching:**
|
||||
```php
|
||||
$cache_key = 'woonoow_revenue_' . md5(serialize($params));
|
||||
$data = get_transient($cache_key);
|
||||
|
||||
if (false === $data) {
|
||||
$data = self::calculate_revenue_metrics($params);
|
||||
set_transient($cache_key, $data, HOUR_IN_SECONDS);
|
||||
}
|
||||
```
|
||||
|
||||
2. **Limit Date Ranges:**
|
||||
- Default to last 30 days
|
||||
- Max 1 year for performance
|
||||
|
||||
3. **Use Indexes:**
|
||||
- Ensure HPOS tables have proper indexes
|
||||
- Add custom indexes if needed
|
||||
|
||||
4. **Async Processing:**
|
||||
- For heavy calculations, use Action Scheduler
|
||||
- Pre-calculate daily aggregates
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Testing Strategy
|
||||
|
||||
### **Manual Testing:**
|
||||
1. Toggle dummy data OFF in dashboard
|
||||
2. Verify loading states appear
|
||||
3. Check error messages are clear
|
||||
4. Test retry functionality
|
||||
5. Verify data displays correctly
|
||||
|
||||
### **API Testing:**
|
||||
```bash
|
||||
# Test endpoint
|
||||
curl -X GET "http://woonoow.local/wp-json/woonoow/v1/analytics/revenue" \
|
||||
-H "Authorization: Bearer YOUR_TOKEN"
|
||||
|
||||
# Expected: 501 error (not implemented)
|
||||
# After implementation: 200 with data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Reference Files
|
||||
|
||||
### **Frontend:**
|
||||
- `admin-spa/src/hooks/useAnalytics.ts` - Data fetching hook
|
||||
- `admin-spa/src/lib/analyticsApi.ts` - API endpoint definitions
|
||||
- `admin-spa/src/routes/Dashboard/Customers.tsx` - Reference implementation
|
||||
|
||||
### **Backend:**
|
||||
- `includes/Api/AnalyticsController.php` - Main controller
|
||||
- `includes/Api/Routes.php` - Route registration
|
||||
- `includes/Api/Permissions.php` - Permission checks
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Success Criteria
|
||||
|
||||
✅ **Frontend:**
|
||||
- All pages load without errors
|
||||
- Dummy data toggle works smoothly
|
||||
- Loading states are clear
|
||||
- Error messages are helpful
|
||||
- Build succeeds without TypeScript errors
|
||||
|
||||
✅ **Backend (To Do):**
|
||||
- All endpoints return real data
|
||||
- Queries are performant (<1s response time)
|
||||
- Data matches frontend expectations
|
||||
- Caching reduces database load
|
||||
- Error handling is robust
|
||||
|
||||
---
|
||||
|
||||
## 📊 Current Build Status
|
||||
|
||||
```
|
||||
✓ built in 3.71s
|
||||
Exit code: 0
|
||||
```
|
||||
|
||||
**All dashboard pages are production-ready with dummy data fallback!**
|
||||
|
||||
---
|
||||
|
||||
**Next Action:** Start implementing `AnalyticsController::get_revenue()` method with real HPOS queries.
|
||||
@@ -1,372 +0,0 @@
|
||||
# 🔌 Dashboard Analytics - API Integration Guide
|
||||
|
||||
**Created:** Nov 4, 2025 9:21 AM (GMT+7)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
Dashboard now supports **real data from API** with a toggle to switch between real and dummy data for development/testing.
|
||||
|
||||
**Default:** Real data (dummy data toggle OFF)
|
||||
|
||||
---
|
||||
|
||||
## 📁 Files Created
|
||||
|
||||
### 1. **Analytics API Module**
|
||||
**File:** `/admin-spa/src/lib/analyticsApi.ts`
|
||||
|
||||
Defines all analytics endpoints:
|
||||
```typescript
|
||||
export const AnalyticsApi = {
|
||||
overview: (params?: AnalyticsParams) => api.get('/woonoow/v1/analytics/overview', params),
|
||||
revenue: (params?: AnalyticsParams) => api.get('/woonoow/v1/analytics/revenue', params),
|
||||
orders: (params?: AnalyticsParams) => api.get('/woonoow/v1/analytics/orders', params),
|
||||
products: (params?: AnalyticsParams) => api.get('/woonoow/v1/analytics/products', params),
|
||||
customers: (params?: AnalyticsParams) => api.get('/woonoow/v1/analytics/customers', params),
|
||||
coupons: (params?: AnalyticsParams) => api.get('/woonoow/v1/analytics/coupons', params),
|
||||
taxes: (params?: AnalyticsParams) => api.get('/woonoow/v1/analytics/taxes', params),
|
||||
};
|
||||
```
|
||||
|
||||
### 2. **Analytics Hooks**
|
||||
**File:** `/admin-spa/src/hooks/useAnalytics.ts`
|
||||
|
||||
React Query hooks for each endpoint:
|
||||
```typescript
|
||||
// Generic hook
|
||||
useAnalytics(endpoint, dummyData, additionalParams)
|
||||
|
||||
// Specific hooks
|
||||
useRevenueAnalytics(dummyData, granularity?)
|
||||
useOrdersAnalytics(dummyData)
|
||||
useProductsAnalytics(dummyData)
|
||||
useCustomersAnalytics(dummyData)
|
||||
useCouponsAnalytics(dummyData)
|
||||
useTaxesAnalytics(dummyData)
|
||||
useOverviewAnalytics(dummyData)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔄 How It Works
|
||||
|
||||
### 1. **Context State**
|
||||
```typescript
|
||||
// DashboardContext.tsx
|
||||
const [useDummyData, setUseDummyData] = useState(false); // Default: real data
|
||||
```
|
||||
|
||||
### 2. **Hook Logic**
|
||||
```typescript
|
||||
// useAnalytics.ts
|
||||
const { data, isLoading, error } = useQuery({
|
||||
queryKey: ['analytics', endpoint, period, additionalParams],
|
||||
queryFn: async () => {
|
||||
const params = { period: period === 'all' ? undefined : period, ...additionalParams };
|
||||
return await AnalyticsApi[endpoint](params);
|
||||
},
|
||||
enabled: !useDummy, // Only fetch when NOT using dummy data
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
});
|
||||
|
||||
// Return dummy data if toggle is on, otherwise return API data
|
||||
return {
|
||||
data: useDummy ? dummyData : (data || dummyData),
|
||||
isLoading: useDummy ? false : isLoading,
|
||||
error: useDummy ? null : error,
|
||||
};
|
||||
```
|
||||
|
||||
### 3. **Component Usage**
|
||||
```typescript
|
||||
// Before (old way)
|
||||
const { period, useDummy } = useDashboardPeriod();
|
||||
const data = useDummy ? DUMMY_DATA : DUMMY_DATA; // Always dummy!
|
||||
|
||||
// After (new way)
|
||||
const { period } = useDashboardPeriod();
|
||||
const { data, isLoading, error } = useCustomersAnalytics(DUMMY_CUSTOMERS_DATA);
|
||||
|
||||
// Loading state
|
||||
if (isLoading) return <LoadingSpinner />;
|
||||
|
||||
// Error state
|
||||
if (error) return <ErrorMessage error={error} />;
|
||||
|
||||
// Use data normally
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 API Endpoints Required
|
||||
|
||||
### Backend PHP REST API Routes
|
||||
|
||||
All endpoints should be registered under `/woonoow/v1/analytics/`:
|
||||
|
||||
#### 1. **Overview** - `GET /woonoow/v1/analytics/overview`
|
||||
**Query Params:**
|
||||
- `period`: '7', '14', '30', or omit for all-time
|
||||
- `start_date`: ISO date (for custom range)
|
||||
- `end_date`: ISO date (for custom range)
|
||||
|
||||
**Response:** Same structure as `DUMMY_DATA` in `Dashboard/index.tsx`
|
||||
|
||||
---
|
||||
|
||||
#### 2. **Revenue** - `GET /woonoow/v1/analytics/revenue`
|
||||
**Query Params:**
|
||||
- `period`: '7', '14', '30', or omit for all-time
|
||||
- `granularity`: 'day', 'week', 'month'
|
||||
|
||||
**Response:** Same structure as `DUMMY_REVENUE_DATA`
|
||||
|
||||
---
|
||||
|
||||
#### 3. **Orders** - `GET /woonoow/v1/analytics/orders`
|
||||
**Query Params:**
|
||||
- `period`: '7', '14', '30', or omit for all-time
|
||||
|
||||
**Response:** Same structure as `DUMMY_ORDERS_DATA`
|
||||
|
||||
---
|
||||
|
||||
#### 4. **Products** - `GET /woonoow/v1/analytics/products`
|
||||
**Query Params:**
|
||||
- `period`: '7', '14', '30', or omit for all-time
|
||||
|
||||
**Response:** Same structure as `DUMMY_PRODUCTS_DATA`
|
||||
|
||||
---
|
||||
|
||||
#### 5. **Customers** - `GET /woonoow/v1/analytics/customers`
|
||||
**Query Params:**
|
||||
- `period`: '7', '14', '30', or omit for all-time
|
||||
|
||||
**Response:** Same structure as `DUMMY_CUSTOMERS_DATA`
|
||||
|
||||
---
|
||||
|
||||
#### 6. **Coupons** - `GET /woonoow/v1/analytics/coupons`
|
||||
**Query Params:**
|
||||
- `period`: '7', '14', '30', or omit for all-time
|
||||
|
||||
**Response:** Same structure as `DUMMY_COUPONS_DATA`
|
||||
|
||||
---
|
||||
|
||||
#### 7. **Taxes** - `GET /woonoow/v1/analytics/taxes`
|
||||
**Query Params:**
|
||||
- `period`: '7', '14', '30', or omit for all-time
|
||||
|
||||
**Response:** Same structure as `DUMMY_TAXES_DATA`
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Backend Implementation Guide
|
||||
|
||||
### Step 1: Register REST Routes
|
||||
|
||||
```php
|
||||
// includes/Admin/Analytics/AnalyticsController.php
|
||||
|
||||
namespace WooNooW\Admin\Analytics;
|
||||
|
||||
class AnalyticsController {
|
||||
public function register_routes() {
|
||||
register_rest_route('woonoow/v1', '/analytics/overview', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'get_overview'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
register_rest_route('woonoow/v1', '/analytics/revenue', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'get_revenue'],
|
||||
'permission_callback' => [$this, 'check_permission'],
|
||||
]);
|
||||
|
||||
// ... register other endpoints
|
||||
}
|
||||
|
||||
public function check_permission() {
|
||||
return current_user_can('manage_woocommerce');
|
||||
}
|
||||
|
||||
public function get_overview(\WP_REST_Request $request) {
|
||||
$period = $request->get_param('period');
|
||||
$start_date = $request->get_param('start_date');
|
||||
$end_date = $request->get_param('end_date');
|
||||
|
||||
// Calculate date range
|
||||
$dates = $this->calculate_date_range($period, $start_date, $end_date);
|
||||
|
||||
// Fetch data from WooCommerce
|
||||
$data = [
|
||||
'metrics' => $this->get_overview_metrics($dates),
|
||||
'salesChart' => $this->get_sales_chart($dates),
|
||||
'orderStatusDistribution' => $this->get_order_status_distribution($dates),
|
||||
'lowStock' => $this->get_low_stock_products(),
|
||||
];
|
||||
|
||||
return rest_ensure_response($data);
|
||||
}
|
||||
|
||||
private function calculate_date_range($period, $start_date, $end_date) {
|
||||
if ($start_date && $end_date) {
|
||||
return ['start' => $start_date, 'end' => $end_date];
|
||||
}
|
||||
|
||||
if (!$period) {
|
||||
// All time
|
||||
return ['start' => null, 'end' => null];
|
||||
}
|
||||
|
||||
$days = intval($period);
|
||||
$end = current_time('Y-m-d');
|
||||
$start = date('Y-m-d', strtotime("-{$days} days"));
|
||||
|
||||
return ['start' => $start, 'end' => $end];
|
||||
}
|
||||
|
||||
// ... implement other methods
|
||||
}
|
||||
```
|
||||
|
||||
### Step 2: Query WooCommerce Data
|
||||
|
||||
```php
|
||||
private function get_overview_metrics($dates) {
|
||||
global $wpdb;
|
||||
|
||||
$where = $this->build_date_where_clause($dates);
|
||||
|
||||
// Use HPOS tables
|
||||
$orders_table = $wpdb->prefix . 'wc_orders';
|
||||
|
||||
$query = "
|
||||
SELECT
|
||||
COUNT(*) as total_orders,
|
||||
SUM(total_amount) as total_revenue,
|
||||
AVG(total_amount) as avg_order_value
|
||||
FROM {$orders_table}
|
||||
WHERE status IN ('wc-completed', 'wc-processing')
|
||||
{$where}
|
||||
";
|
||||
|
||||
$results = $wpdb->get_row($query);
|
||||
|
||||
// Calculate comparison with previous period
|
||||
$previous_metrics = $this->get_previous_period_metrics($dates);
|
||||
|
||||
return [
|
||||
'revenue' => [
|
||||
'today' => floatval($results->total_revenue),
|
||||
'yesterday' => floatval($previous_metrics->total_revenue),
|
||||
'change' => $this->calculate_change_percent(
|
||||
$results->total_revenue,
|
||||
$previous_metrics->total_revenue
|
||||
),
|
||||
],
|
||||
// ... other metrics
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎨 Frontend Implementation
|
||||
|
||||
### Example: Update Revenue.tsx
|
||||
|
||||
```typescript
|
||||
import { useRevenueAnalytics } from '@/hooks/useAnalytics';
|
||||
import { DUMMY_REVENUE_DATA } from './data/dummyRevenue';
|
||||
|
||||
export default function RevenueAnalytics() {
|
||||
const { period } = useDashboardPeriod();
|
||||
const [granularity, setGranularity] = useState<'day' | 'week' | 'month'>('day');
|
||||
|
||||
// Fetch real data or use dummy data
|
||||
const { data, isLoading, error } = useRevenueAnalytics(DUMMY_REVENUE_DATA, granularity);
|
||||
|
||||
if (isLoading) return <LoadingSpinner />;
|
||||
if (error) return <ErrorMessage error={error} />;
|
||||
|
||||
// Use data normally...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔀 Toggle Behavior
|
||||
|
||||
### When Dummy Data Toggle is OFF (default):
|
||||
1. ✅ Fetches real data from API
|
||||
2. ✅ Shows loading spinner while fetching
|
||||
3. ✅ Shows error message if API fails
|
||||
4. ✅ Caches data for 5 minutes (React Query)
|
||||
5. ✅ Automatically refetches when period changes
|
||||
|
||||
### When Dummy Data Toggle is ON:
|
||||
1. ✅ Uses dummy data immediately (no API call)
|
||||
2. ✅ No loading state
|
||||
3. ✅ No error state
|
||||
4. ✅ Perfect for development/testing
|
||||
|
||||
---
|
||||
|
||||
## 📋 Migration Checklist
|
||||
|
||||
### Frontend (React):
|
||||
- [x] Create `analyticsApi.ts` with all endpoints
|
||||
- [x] Create `useAnalytics.ts` hooks
|
||||
- [x] Update `DashboardContext` default to `false`
|
||||
- [x] Update `Customers.tsx` as example
|
||||
- [ ] Update `Revenue.tsx`
|
||||
- [ ] Update `Orders.tsx`
|
||||
- [ ] Update `Products.tsx`
|
||||
- [ ] Update `Coupons.tsx`
|
||||
- [ ] Update `Taxes.tsx`
|
||||
- [ ] Update `Dashboard/index.tsx` (overview)
|
||||
|
||||
### Backend (PHP):
|
||||
- [ ] Create `AnalyticsController.php`
|
||||
- [ ] Register REST routes
|
||||
- [ ] Implement `/analytics/overview`
|
||||
- [ ] Implement `/analytics/revenue`
|
||||
- [ ] Implement `/analytics/orders`
|
||||
- [ ] Implement `/analytics/products`
|
||||
- [ ] Implement `/analytics/customers`
|
||||
- [ ] Implement `/analytics/coupons`
|
||||
- [ ] Implement `/analytics/taxes`
|
||||
- [ ] Add permission checks
|
||||
- [ ] Add data caching (transients)
|
||||
- [ ] Add error handling
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Benefits
|
||||
|
||||
1. **Real-time Data**: Dashboard shows actual store data
|
||||
2. **Development Friendly**: Toggle to dummy data for testing
|
||||
3. **Performance**: React Query caching reduces API calls
|
||||
4. **Error Handling**: Graceful fallback to dummy data
|
||||
5. **Type Safety**: TypeScript interfaces match API responses
|
||||
6. **Maintainable**: Single source of truth for API endpoints
|
||||
|
||||
---
|
||||
|
||||
## 🔮 Future Enhancements
|
||||
|
||||
1. **Custom Date Range**: Add date picker for custom ranges
|
||||
2. **Export Data**: Download analytics as CSV/PDF
|
||||
3. **Real-time Updates**: WebSocket for live data
|
||||
4. **Comparison Mode**: Compare multiple periods side-by-side
|
||||
5. **Scheduled Reports**: Email reports automatically
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ Frontend ready - Backend implementation needed!
|
||||
@@ -1,507 +0,0 @@
|
||||
# 📊 Dashboard Implementation Guide
|
||||
|
||||
**Last updated:** 2025-11-03 14:50 GMT+7
|
||||
**Status:** In Progress
|
||||
**Reference:** DASHBOARD_PLAN.md
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Overview
|
||||
|
||||
This document tracks the implementation of the WooNooW Dashboard module with all submenus as planned in DASHBOARD_PLAN.md. We're implementing a **dummy data toggle system** to allow visualization of charts even when stores have no data yet.
|
||||
|
||||
---
|
||||
|
||||
## ✅ Completed
|
||||
|
||||
### 1. Main Dashboard (`/dashboard`) ✅
|
||||
**Status:** Complete with dummy data
|
||||
**File:** `admin-spa/src/routes/Dashboard/index.tsx`
|
||||
|
||||
**Features:**
|
||||
- ✅ 4 metric cards (Revenue, Orders, Avg Order Value, Conversion Rate)
|
||||
- ✅ Unified period selector (7/14/30 days)
|
||||
- ✅ Interactive Sales Overview chart (Revenue/Orders/Both)
|
||||
- ✅ Interactive Order Status pie chart with dropdown
|
||||
- ✅ Top Products & Customers (tabbed)
|
||||
- ✅ Low Stock Alert banner (edge-to-edge)
|
||||
- ✅ Fully responsive (desktop/tablet/mobile)
|
||||
- ✅ Dark mode support
|
||||
- ✅ Proper currency formatting
|
||||
|
||||
### 2. Dummy Data Toggle System ✅
|
||||
**Status:** Complete
|
||||
**Files:**
|
||||
- `admin-spa/src/lib/useDummyData.ts` - Zustand store with persistence
|
||||
- `admin-spa/src/components/DummyDataToggle.tsx` - Toggle button component
|
||||
|
||||
**Features:**
|
||||
- ✅ Global state management with Zustand
|
||||
- ✅ LocalStorage persistence
|
||||
- ✅ Toggle button in dashboard header
|
||||
- ✅ Visual indicator (Database icon vs DatabaseZap icon)
|
||||
- ✅ Works across all dashboard pages
|
||||
|
||||
**Usage:**
|
||||
```typescript
|
||||
import { useDummyData } from '@/lib/useDummyData';
|
||||
|
||||
function MyComponent() {
|
||||
const useDummy = useDummyData();
|
||||
|
||||
const data = useDummy ? DUMMY_DATA : realData;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚧 In Progress
|
||||
|
||||
### Shared Components
|
||||
Creating reusable components for all dashboard pages:
|
||||
|
||||
#### Components to Create:
|
||||
- [ ] `StatCard.tsx` - Metric card with trend indicator
|
||||
- [ ] `ChartCard.tsx` - Chart container with title and filters
|
||||
- [ ] `DataTable.tsx` - Sortable, searchable table
|
||||
- [ ] `DateRangePicker.tsx` - Custom date range selector
|
||||
- [ ] `ComparisonToggle.tsx` - Compare with previous period
|
||||
- [ ] `ExportButton.tsx` - CSV/PDF export functionality
|
||||
- [ ] `LoadingSkeleton.tsx` - Loading states for charts/tables
|
||||
- [ ] `EmptyState.tsx` - No data messages
|
||||
|
||||
---
|
||||
|
||||
## 📋 Pending Implementation
|
||||
|
||||
### 1. Revenue Report (`/dashboard/revenue`)
|
||||
**Priority:** High
|
||||
**Estimated Time:** 2-3 days
|
||||
|
||||
**Features to Implement:**
|
||||
- [ ] Revenue chart with granularity selector (daily/weekly/monthly)
|
||||
- [ ] Gross vs Net revenue comparison
|
||||
- [ ] Revenue breakdown tables:
|
||||
- [ ] By Product
|
||||
- [ ] By Category
|
||||
- [ ] By Payment Method
|
||||
- [ ] By Shipping Method
|
||||
- [ ] Tax collected display
|
||||
- [ ] Refunds tracking
|
||||
- [ ] Comparison mode (vs previous period)
|
||||
- [ ] Export functionality
|
||||
|
||||
**Dummy Data Structure:**
|
||||
```typescript
|
||||
{
|
||||
overview: {
|
||||
gross_revenue: number,
|
||||
net_revenue: number,
|
||||
tax: number,
|
||||
shipping: number,
|
||||
refunds: number,
|
||||
change_percent: number
|
||||
},
|
||||
chart_data: Array<{
|
||||
date: string,
|
||||
gross: number,
|
||||
net: number,
|
||||
refunds: number
|
||||
}>,
|
||||
by_product: Array<{
|
||||
id: number,
|
||||
name: string,
|
||||
revenue: number,
|
||||
orders: number,
|
||||
refunds: number
|
||||
}>,
|
||||
by_category: Array<{
|
||||
id: number,
|
||||
name: string,
|
||||
revenue: number,
|
||||
percentage: number
|
||||
}>,
|
||||
by_payment_method: Array<{
|
||||
method: string,
|
||||
orders: number,
|
||||
revenue: number
|
||||
}>,
|
||||
by_shipping_method: Array<{
|
||||
method: string,
|
||||
orders: number,
|
||||
revenue: number
|
||||
}>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. Orders Analytics (`/dashboard/orders`)
|
||||
**Priority:** High
|
||||
**Estimated Time:** 2-3 days
|
||||
|
||||
**Features to Implement:**
|
||||
- [ ] Orders timeline chart
|
||||
- [ ] Status breakdown pie chart
|
||||
- [ ] Orders by hour heatmap
|
||||
- [ ] Orders by day of week chart
|
||||
- [ ] Average processing time
|
||||
- [ ] Fulfillment rate metric
|
||||
- [ ] Cancellation rate metric
|
||||
- [ ] Filters (status, payment method, date range)
|
||||
|
||||
**Dummy Data Structure:**
|
||||
```typescript
|
||||
{
|
||||
overview: {
|
||||
total_orders: number,
|
||||
avg_order_value: number,
|
||||
fulfillment_rate: number,
|
||||
cancellation_rate: number,
|
||||
avg_processing_time: string
|
||||
},
|
||||
chart_data: Array<{
|
||||
date: string,
|
||||
orders: number,
|
||||
completed: number,
|
||||
cancelled: number
|
||||
}>,
|
||||
by_status: Array<{
|
||||
status: string,
|
||||
count: number,
|
||||
percentage: number,
|
||||
color: string
|
||||
}>,
|
||||
by_hour: Array<{
|
||||
hour: number,
|
||||
orders: number
|
||||
}>,
|
||||
by_day_of_week: Array<{
|
||||
day: string,
|
||||
orders: number
|
||||
}>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. Products Performance (`/dashboard/products`)
|
||||
**Priority:** Medium
|
||||
**Estimated Time:** 3-4 days
|
||||
|
||||
**Features to Implement:**
|
||||
- [ ] Top products table (sortable by revenue/quantity/views)
|
||||
- [ ] Category performance breakdown
|
||||
- [ ] Product trends chart (multi-select products)
|
||||
- [ ] Stock analysis:
|
||||
- [ ] Low stock items
|
||||
- [ ] Out of stock items
|
||||
- [ ] Slow movers (overstocked)
|
||||
- [ ] Search and filters
|
||||
- [ ] Export functionality
|
||||
|
||||
**Dummy Data Structure:**
|
||||
```typescript
|
||||
{
|
||||
overview: {
|
||||
items_sold: number,
|
||||
revenue: number,
|
||||
avg_price: number,
|
||||
low_stock_count: number,
|
||||
out_of_stock_count: number
|
||||
},
|
||||
top_products: Array<{
|
||||
id: number,
|
||||
name: string,
|
||||
image: string,
|
||||
items_sold: number,
|
||||
revenue: number,
|
||||
stock: number,
|
||||
status: string,
|
||||
views: number
|
||||
}>,
|
||||
by_category: Array<{
|
||||
id: number,
|
||||
name: string,
|
||||
products_count: number,
|
||||
revenue: number,
|
||||
items_sold: number
|
||||
}>,
|
||||
stock_analysis: {
|
||||
low_stock: Array<Product>,
|
||||
out_of_stock: Array<Product>,
|
||||
slow_movers: Array<Product>
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. Customers Analytics (`/dashboard/customers`)
|
||||
**Priority:** Medium
|
||||
**Estimated Time:** 3-4 days
|
||||
|
||||
**Features to Implement:**
|
||||
- [ ] Customer segments (New, Returning, VIP, At-Risk)
|
||||
- [ ] Top customers table
|
||||
- [ ] Customer acquisition chart
|
||||
- [ ] Lifetime value analysis
|
||||
- [ ] Retention rate metric
|
||||
- [ ] Average orders per customer
|
||||
- [ ] Search and filters
|
||||
|
||||
**Dummy Data Structure:**
|
||||
```typescript
|
||||
{
|
||||
overview: {
|
||||
total_customers: number,
|
||||
new_customers: number,
|
||||
returning_customers: number,
|
||||
avg_ltv: number,
|
||||
retention_rate: number,
|
||||
avg_orders_per_customer: number
|
||||
},
|
||||
segments: {
|
||||
new: number,
|
||||
returning: number,
|
||||
vip: number,
|
||||
at_risk: number
|
||||
},
|
||||
top_customers: Array<{
|
||||
id: number,
|
||||
name: string,
|
||||
email: string,
|
||||
orders: number,
|
||||
total_spent: number,
|
||||
avg_order_value: number,
|
||||
last_order_date: string,
|
||||
segment: string
|
||||
}>,
|
||||
acquisition_chart: Array<{
|
||||
date: string,
|
||||
new_customers: number,
|
||||
returning_customers: number
|
||||
}>,
|
||||
ltv_distribution: Array<{
|
||||
range: string,
|
||||
count: number
|
||||
}>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. Coupons Report (`/dashboard/coupons`)
|
||||
**Priority:** Low
|
||||
**Estimated Time:** 2 days
|
||||
|
||||
**Features to Implement:**
|
||||
- [ ] Coupon performance table
|
||||
- [ ] Usage chart over time
|
||||
- [ ] ROI calculation
|
||||
- [ ] Top coupons (most used, highest revenue, best ROI)
|
||||
- [ ] Filters and search
|
||||
|
||||
**Dummy Data Structure:**
|
||||
```typescript
|
||||
{
|
||||
overview: {
|
||||
total_discount: number,
|
||||
coupons_used: number,
|
||||
revenue_with_coupons: number,
|
||||
avg_discount_per_order: number
|
||||
},
|
||||
coupons: Array<{
|
||||
id: number,
|
||||
code: string,
|
||||
type: string,
|
||||
amount: number,
|
||||
uses: number,
|
||||
discount_amount: number,
|
||||
revenue_generated: number,
|
||||
roi: number
|
||||
}>,
|
||||
usage_chart: Array<{
|
||||
date: string,
|
||||
uses: number,
|
||||
discount: number
|
||||
}>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 6. Taxes Report (`/dashboard/taxes`)
|
||||
**Priority:** Low
|
||||
**Estimated Time:** 1-2 days
|
||||
|
||||
**Features to Implement:**
|
||||
- [ ] Tax summary (total collected)
|
||||
- [ ] Tax by rate breakdown
|
||||
- [ ] Tax by location (country/state)
|
||||
- [ ] Tax collection chart
|
||||
- [ ] Export for accounting
|
||||
|
||||
**Dummy Data Structure:**
|
||||
```typescript
|
||||
{
|
||||
overview: {
|
||||
total_tax: number,
|
||||
avg_tax_per_order: number,
|
||||
orders_with_tax: number
|
||||
},
|
||||
by_rate: Array<{
|
||||
rate: string,
|
||||
percentage: number,
|
||||
orders: number,
|
||||
tax_amount: number
|
||||
}>,
|
||||
by_location: Array<{
|
||||
country: string,
|
||||
state: string,
|
||||
orders: number,
|
||||
tax_amount: number
|
||||
}>,
|
||||
chart_data: Array<{
|
||||
date: string,
|
||||
tax: number
|
||||
}>
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗂️ File Structure
|
||||
|
||||
```
|
||||
admin-spa/src/
|
||||
├── routes/
|
||||
│ └── Dashboard/
|
||||
│ ├── index.tsx ✅ Main overview (complete)
|
||||
│ ├── Revenue.tsx ⏳ Revenue report (pending)
|
||||
│ ├── Orders.tsx ⏳ Orders analytics (pending)
|
||||
│ ├── Products.tsx ⏳ Product performance (pending)
|
||||
│ ├── Customers.tsx ⏳ Customer analytics (pending)
|
||||
│ ├── Coupons.tsx ⏳ Coupon reports (pending)
|
||||
│ ├── Taxes.tsx ⏳ Tax reports (pending)
|
||||
│ ├── components/
|
||||
│ │ ├── StatCard.tsx ⏳ Metric card (pending)
|
||||
│ │ ├── ChartCard.tsx ⏳ Chart container (pending)
|
||||
│ │ ├── DataTable.tsx ⏳ Sortable table (pending)
|
||||
│ │ ├── DateRangePicker.tsx ⏳ Date selector (pending)
|
||||
│ │ ├── ComparisonToggle.tsx ⏳ Compare mode (pending)
|
||||
│ │ └── ExportButton.tsx ⏳ Export (pending)
|
||||
│ └── data/
|
||||
│ ├── dummyRevenue.ts ⏳ Revenue dummy data (pending)
|
||||
│ ├── dummyOrders.ts ⏳ Orders dummy data (pending)
|
||||
│ ├── dummyProducts.ts ⏳ Products dummy data (pending)
|
||||
│ ├── dummyCustomers.ts ⏳ Customers dummy data (pending)
|
||||
│ ├── dummyCoupons.ts ⏳ Coupons dummy data (pending)
|
||||
│ └── dummyTaxes.ts ⏳ Taxes dummy data (pending)
|
||||
├── components/
|
||||
│ ├── DummyDataToggle.tsx ✅ Toggle button (complete)
|
||||
│ └── ui/
|
||||
│ ├── tabs.tsx ✅ Tabs component (complete)
|
||||
│ └── tooltip.tsx ⏳ Tooltip (needs @radix-ui package)
|
||||
└── lib/
|
||||
└── useDummyData.ts ✅ Dummy data store (complete)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Technical Stack
|
||||
|
||||
**Frontend:**
|
||||
- React 18 + TypeScript
|
||||
- Recharts 3.3.0 (charts)
|
||||
- TanStack Query (data fetching)
|
||||
- Zustand (state management)
|
||||
- Shadcn UI (components)
|
||||
- Tailwind CSS (styling)
|
||||
|
||||
**Backend (Future):**
|
||||
- REST API endpoints (`/woonoow/v1/analytics/*`)
|
||||
- HPOS tables integration
|
||||
- Query optimization with caching
|
||||
- Transients for expensive queries
|
||||
|
||||
---
|
||||
|
||||
## 📅 Implementation Timeline
|
||||
|
||||
### Week 1: Foundation ✅
|
||||
- [x] Main Dashboard with dummy data
|
||||
- [x] Dummy data toggle system
|
||||
- [x] Shared component planning
|
||||
|
||||
### Week 2: Shared Components (Current)
|
||||
- [ ] Create all shared components
|
||||
- [ ] Create dummy data files
|
||||
- [ ] Set up routing for submenus
|
||||
|
||||
### Week 3: Revenue & Orders
|
||||
- [ ] Revenue report page
|
||||
- [ ] Orders analytics page
|
||||
- [ ] Export functionality
|
||||
|
||||
### Week 4: Products & Customers
|
||||
- [ ] Products performance page
|
||||
- [ ] Customers analytics page
|
||||
- [ ] Advanced filters
|
||||
|
||||
### Week 5: Coupons & Taxes
|
||||
- [ ] Coupons report page
|
||||
- [ ] Taxes report page
|
||||
- [ ] Final polish
|
||||
|
||||
### Week 6: Real Data Integration
|
||||
- [ ] Create backend API endpoints
|
||||
- [ ] Wire all pages to real data
|
||||
- [ ] Keep dummy data toggle for demos
|
||||
- [ ] Performance optimization
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps
|
||||
|
||||
### Immediate (This Week):
|
||||
1. ✅ Create dummy data toggle system
|
||||
2. ⏳ Create shared components (StatCard, ChartCard, DataTable)
|
||||
3. ⏳ Set up routing for all dashboard submenus
|
||||
4. ⏳ Create dummy data files for each page
|
||||
|
||||
### Short Term (Next 2 Weeks):
|
||||
1. Implement Revenue report page
|
||||
2. Implement Orders analytics page
|
||||
3. Add export functionality
|
||||
4. Add comparison mode
|
||||
|
||||
### Long Term (Month 2):
|
||||
1. Implement remaining pages (Products, Customers, Coupons, Taxes)
|
||||
2. Create backend API endpoints
|
||||
3. Wire to real data
|
||||
4. Performance optimization
|
||||
5. User testing
|
||||
|
||||
---
|
||||
|
||||
## 📝 Notes
|
||||
|
||||
### Dummy Data Toggle Benefits:
|
||||
1. **Development:** Easy to test UI without real data
|
||||
2. **Demos:** Show potential to clients/stakeholders
|
||||
3. **New Stores:** Visualize what analytics will look like
|
||||
4. **Testing:** Consistent data for testing edge cases
|
||||
|
||||
### Design Principles:
|
||||
1. **Consistency:** All pages follow same design language
|
||||
2. **Performance:** Lazy load routes, optimize queries
|
||||
3. **Accessibility:** Keyboard navigation, screen readers
|
||||
4. **Responsiveness:** Mobile-first approach
|
||||
5. **UX:** Clear loading states, helpful empty states
|
||||
|
||||
---
|
||||
|
||||
**Status:** Ready to proceed with shared components and submenu pages!
|
||||
**Next Action:** Create shared components (StatCard, ChartCard, DataTable)
|
||||
@@ -1,207 +0,0 @@
|
||||
# 📊 Dashboard Stat Cards & Tables Audit
|
||||
|
||||
**Generated:** Nov 4, 2025 12:03 AM (GMT+7)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Rules for Period-Based Data:
|
||||
|
||||
### ✅ Should Have Comparison (change prop):
|
||||
- Period is NOT "all"
|
||||
- Period is NOT custom date range (future)
|
||||
- Data is time-based (affected by period)
|
||||
|
||||
### ❌ Should NOT Have Comparison:
|
||||
- Period is "all" (no previous period)
|
||||
- Period is custom date range (future)
|
||||
- Data is global/store-level (not time-based)
|
||||
|
||||
---
|
||||
|
||||
## 📄 Page 1: Dashboard (index.tsx)
|
||||
|
||||
### Stat Cards:
|
||||
| # | Title | Value Source | Affected by Period? | Has Comparison? | Status |
|
||||
|---|-------|--------------|---------------------|-----------------|--------|
|
||||
| 1 | Revenue | `periodMetrics.revenue.current` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Orders | `periodMetrics.orders.current` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 3 | Avg Order Value | `periodMetrics.avgOrderValue.current` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 4 | Conversion Rate | `DUMMY_DATA.metrics.conversionRate.today` | ✅ YES | ✅ YES | ⚠️ NEEDS FIX - Not using periodMetrics |
|
||||
|
||||
### Other Metrics:
|
||||
- **Low Stock Alert**: ❌ NOT period-based (global inventory)
|
||||
|
||||
---
|
||||
|
||||
## 📄 Page 2: Revenue Analytics (Revenue.tsx)
|
||||
|
||||
### Stat Cards:
|
||||
| # | Title | Value Source | Affected by Period? | Has Comparison? | Status |
|
||||
|---|-------|--------------|---------------------|-----------------|--------|
|
||||
| 1 | Gross Revenue | `periodMetrics.gross_revenue` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Net Revenue | `periodMetrics.net_revenue` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 3 | Tax Collected | `periodMetrics.tax` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
| 4 | Refunds | `periodMetrics.refunds` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
|
||||
### Tables:
|
||||
| # | Title | Data Source | Affected by Period? | Status |
|
||||
|---|-------|-------------|---------------------|--------|
|
||||
| 1 | Top Products | `filteredProducts` | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Revenue by Category | `filteredCategories` | ✅ YES | ✅ CORRECT |
|
||||
| 3 | Payment Methods | `filteredPaymentMethods` | ✅ YES | ✅ CORRECT |
|
||||
| 4 | Shipping Methods | `filteredShippingMethods` | ✅ YES | ✅ CORRECT |
|
||||
|
||||
---
|
||||
|
||||
## 📄 Page 3: Orders Analytics (Orders.tsx)
|
||||
|
||||
### Stat Cards:
|
||||
| # | Title | Value Source | Affected by Period? | Has Comparison? | Status |
|
||||
|---|-------|--------------|---------------------|-----------------|--------|
|
||||
| 1 | Total Orders | `periodMetrics.total_orders` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Avg Order Value | `periodMetrics.avg_order_value` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
| 3 | Fulfillment Rate | `periodMetrics.fulfillment_rate` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
| 4 | Cancellation Rate | `periodMetrics.cancellation_rate` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
|
||||
### Other Metrics:
|
||||
- **Avg Processing Time**: ✅ YES (period-based average) - ⚠️ NEEDS comparison
|
||||
- **Performance Summary**: ✅ YES (period-based) - Already has text summary
|
||||
|
||||
---
|
||||
|
||||
## 📄 Page 4: Products Performance (Products.tsx)
|
||||
|
||||
### Stat Cards:
|
||||
| # | Title | Value Source | Affected by Period? | Has Comparison? | Status |
|
||||
|---|-------|--------------|---------------------|-----------------|--------|
|
||||
| 1 | Items Sold | `periodMetrics.items_sold` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Revenue | `periodMetrics.revenue` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 3 | Low Stock | `data.overview.low_stock_count` | ❌ NO (Global) | ❌ NO | ✅ CORRECT |
|
||||
| 4 | Out of Stock | `data.overview.out_of_stock_count` | ❌ NO (Global) | ❌ NO | ✅ CORRECT |
|
||||
|
||||
### Tables:
|
||||
| # | Title | Data Source | Affected by Period? | Status |
|
||||
|---|-------|-------------|---------------------|--------|
|
||||
| 1 | Top Products | `filteredProducts` | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Products by Category | `filteredCategories` | ✅ YES | ✅ CORRECT |
|
||||
| 3 | Stock Analysis | `data.stock_analysis` | ❌ NO (Global) | ✅ CORRECT |
|
||||
|
||||
---
|
||||
|
||||
## 📄 Page 5: Customers Analytics (Customers.tsx)
|
||||
|
||||
### Stat Cards:
|
||||
| # | Title | Value Source | Affected by Period? | Has Comparison? | Status |
|
||||
|---|-------|--------------|---------------------|-----------------|--------|
|
||||
| 1 | Total Customers | `periodMetrics.total_customers` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Avg Lifetime Value | `periodMetrics.avg_ltv` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
| 3 | Retention Rate | `periodMetrics.retention_rate` | ❌ NO (Percentage) | ❌ NO | ✅ CORRECT |
|
||||
| 4 | Avg Orders/Customer | `periodMetrics.avg_orders_per_customer` | ❌ NO (Average) | ❌ NO | ✅ CORRECT |
|
||||
|
||||
### Segment Cards:
|
||||
| # | Title | Value Source | Affected by Period? | Status |
|
||||
|---|-------|--------------|---------------------|--------|
|
||||
| 1 | New Customers | `periodMetrics.new_customers` | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Returning Customers | `periodMetrics.returning_customers` | ✅ YES | ✅ CORRECT |
|
||||
| 3 | VIP Customers | `data.segments.vip` | ❌ NO (Global) | ✅ CORRECT |
|
||||
| 4 | At Risk | `data.segments.at_risk` | ❌ NO (Global) | ✅ CORRECT |
|
||||
|
||||
### Tables:
|
||||
| # | Title | Data Source | Affected by Period? | Status |
|
||||
|---|-------|-------------|---------------------|--------|
|
||||
| 1 | Top Customers | `data.top_customers` | ❌ NO (Global LTV) | ✅ CORRECT |
|
||||
|
||||
---
|
||||
|
||||
## 📄 Page 6: Coupons Report (Coupons.tsx)
|
||||
|
||||
### Stat Cards:
|
||||
| # | Title | Value Source | Affected by Period? | Has Comparison? | Status |
|
||||
|---|-------|--------------|---------------------|-----------------|--------|
|
||||
| 1 | Total Discount | `periodMetrics.total_discount` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Coupons Used | `periodMetrics.coupons_used` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 3 | Revenue with Coupons | `periodMetrics.revenue_with_coupons` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
| 4 | Avg Discount/Order | `periodMetrics.avg_discount_per_order` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
|
||||
### Tables:
|
||||
| # | Title | Data Source | Affected by Period? | Status |
|
||||
|---|-------|-------------|---------------------|--------|
|
||||
| 1 | Coupon Performance | `filteredCoupons` | ✅ YES | ✅ CORRECT |
|
||||
|
||||
---
|
||||
|
||||
## 📄 Page 7: Taxes Report (Taxes.tsx)
|
||||
|
||||
### Stat Cards:
|
||||
| # | Title | Value Source | Affected by Period? | Has Comparison? | Status |
|
||||
|---|-------|--------------|---------------------|-----------------|--------|
|
||||
| 1 | Total Tax Collected | `periodMetrics.total_tax` | ✅ YES | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Avg Tax per Order | `periodMetrics.avg_tax_per_order` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
| 3 | Orders with Tax | `periodMetrics.orders_with_tax` | ✅ YES | ❌ NO | ⚠️ NEEDS FIX - Should have comparison |
|
||||
|
||||
### Tables:
|
||||
| # | Title | Data Source | Affected by Period? | Status |
|
||||
|---|-------|-------------|---------------------|--------|
|
||||
| 1 | Tax by Rate | `filteredByRate` | ✅ YES | ✅ CORRECT |
|
||||
| 2 | Tax by Location | `filteredByLocation` | ✅ YES | ✅ CORRECT |
|
||||
|
||||
---
|
||||
|
||||
## 📋 Summary - ALL ISSUES FIXED! ✅
|
||||
|
||||
### ✅ FIXED (13 items):
|
||||
|
||||
**Dashboard (index.tsx):**
|
||||
1. ✅ Conversion Rate - Now using periodMetrics with proper comparison
|
||||
|
||||
**Revenue.tsx:**
|
||||
2. ✅ Tax Collected - Added comparison (`tax_change`)
|
||||
3. ✅ Refunds - Added comparison (`refunds_change`)
|
||||
|
||||
**Orders.tsx:**
|
||||
4. ✅ Avg Order Value - Added comparison (`avg_order_value_change`)
|
||||
5. ✅ Fulfillment Rate - Added comparison (`fulfillment_rate_change`)
|
||||
6. ✅ Cancellation Rate - Added comparison (`cancellation_rate_change`)
|
||||
7. ✅ Avg Processing Time - Displayed in card (not StatCard, no change needed)
|
||||
|
||||
**Customers.tsx:**
|
||||
8. ✅ Avg Lifetime Value - Added comparison (`avg_ltv_change`)
|
||||
|
||||
**Coupons.tsx:**
|
||||
9. ✅ Revenue with Coupons - Added comparison (`revenue_with_coupons_change`)
|
||||
10. ✅ Avg Discount/Order - Added comparison (`avg_discount_per_order_change`)
|
||||
|
||||
**Taxes.tsx:**
|
||||
11. ✅ Avg Tax per Order - Added comparison (`avg_tax_per_order_change`)
|
||||
12. ✅ Orders with Tax - Added comparison (`orders_with_tax_change`)
|
||||
|
||||
---
|
||||
|
||||
## ✅ Correct Implementation (41 items total):
|
||||
|
||||
- ✅ All 13 stat cards now have proper period comparisons
|
||||
- ✅ All tables are correctly filtered by period
|
||||
- ✅ Global/store-level data correctly excluded from period filtering
|
||||
- ✅ All primary metrics have proper comparisons
|
||||
- ✅ Stock data remains global (correct)
|
||||
- ✅ Customer segments (VIP/At Risk) remain global (correct)
|
||||
- ✅ "All Time" period correctly shows no comparison (undefined)
|
||||
- ✅ Build successful with no errors
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Comparison Logic Implemented:
|
||||
|
||||
**For period-based data (7/14/30 days):**
|
||||
- Current period data vs. previous period data
|
||||
- Example: 7 days compares last 7 days vs. previous 7 days
|
||||
- Percentage change calculated and displayed with trend indicator
|
||||
|
||||
**For "All Time" period:**
|
||||
- No comparison shown (change = undefined)
|
||||
- StatCard component handles undefined gracefully
|
||||
- No "vs previous period" text displayed
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ COMPLETE - All dashboard stat cards now have consistent comparison logic!
|
||||
@@ -1,37 +0,0 @@
|
||||
# WooNooW Keyboard Shortcut Plan
|
||||
|
||||
This document lists all keyboard shortcuts planned for the WooNooW admin SPA.
|
||||
Each item includes its purpose, proposed key binding, and implementation status.
|
||||
|
||||
## Global Shortcuts
|
||||
- [ ] **Toggle Fullscreen Mode** — `Ctrl + Shift + F` or `Cmd + Shift + F`
|
||||
- Focus: Switch between fullscreen and normal layout
|
||||
- Implementation target: useFullscreen() hook
|
||||
|
||||
- [ ] **Quick Search** — `/`
|
||||
- Focus: Focus on global search bar (future top search input)
|
||||
|
||||
- [ ] **Navigate to Dashboard** — `D`
|
||||
- Focus: Jump to Dashboard route
|
||||
|
||||
- [ ] **Navigate to Orders** — `O`
|
||||
- Focus: Jump to Orders route
|
||||
|
||||
- [ ] **Refresh Current View** — `R`
|
||||
- Focus: Soft refresh current SPA route (refetch query)
|
||||
|
||||
- [ ] **Open Command Palette** — `Ctrl + K` or `Cmd + K`
|
||||
- Focus: Open a unified command palette for navigation/actions
|
||||
|
||||
## Page-Level Shortcuts
|
||||
- [ ] **Orders Page – New Order** — `N`
|
||||
- Focus: Trigger order creation modal (future enhancement)
|
||||
|
||||
- [ ] **Orders Page – Filter** — `F`
|
||||
- Focus: Focus on filter dropdown
|
||||
|
||||
- [ ] **Dashboard – Toggle Stats Range** — `T`
|
||||
- Focus: Switch dashboard stats range (Today / Week / Month)
|
||||
|
||||
---
|
||||
✅ *This checklist will be updated as each shortcut is implemented.*
|
||||
274
PROGRESS_NOTE.md
274
PROGRESS_NOTE.md
@@ -1789,4 +1789,276 @@ const data = useDummy ? DUMMY_DATA : realApiData;
|
||||
---
|
||||
|
||||
**Last synced:** 2025‑11‑03 21:05 GMT+7
|
||||
**Next milestone:** Wire Dashboard to real data OR Products module.
|
||||
**Next milestone:** Wire Dashboard to real data OR Products module.# 📊 Dashboard Analytics Implementation — November 4, 2025
|
||||
|
||||
## ✅ COMPLETE - All 7 Analytics Pages with Real Data
|
||||
|
||||
**Status:** Production Ready
|
||||
**Implementation:** Full HPOS integration with 5-minute caching
|
||||
**Total Lines:** ~1200 lines (AnalyticsController.php)
|
||||
|
||||
### 🎯 Implemented Pages
|
||||
|
||||
#### **1. Overview** (`/analytics/overview`)
|
||||
- ✅ Sales chart (revenue + orders over time) with **filled dates**
|
||||
- ✅ Top 5 products by revenue
|
||||
- ✅ Top 5 customers by spending
|
||||
- ✅ Order status distribution (pie chart with sorting)
|
||||
- ✅ Key metrics: Revenue, Orders, Avg Order Value, **Conversion Rate**
|
||||
|
||||
#### **2. Revenue** (`/analytics/revenue`)
|
||||
- ✅ Revenue chart (gross, net, tax, refunds, shipping)
|
||||
- ✅ Top 10 products by revenue
|
||||
- 📋 Revenue by category (TODO)
|
||||
- 📋 Revenue by payment method (TODO)
|
||||
- 📋 Revenue by shipping method (TODO)
|
||||
|
||||
#### **3. Orders** (`/analytics/orders`)
|
||||
- ✅ Orders over time (total + by status)
|
||||
- ✅ Orders by status (sorted by importance)
|
||||
- ✅ Orders by hour of day (24h breakdown)
|
||||
- ✅ Orders by day of week
|
||||
- ✅ Average processing time (human-readable)
|
||||
- ✅ Fulfillment rate & Cancellation rate
|
||||
|
||||
#### **4. Products** (`/analytics/products`)
|
||||
- ✅ Top 20 products by revenue
|
||||
- ✅ Stock analysis (low stock, out of stock counts)
|
||||
- ✅ Average price calculation
|
||||
- 📋 Conversion rate placeholder (0.00)
|
||||
|
||||
#### **5. Customers** (`/analytics/customers`)
|
||||
- ✅ Top 20 customers by spending
|
||||
- ✅ New vs Returning customers
|
||||
- ✅ Customer segments
|
||||
- ✅ Average LTV (Lifetime Value)
|
||||
- ✅ Average orders per customer
|
||||
|
||||
#### **6. Coupons** (`/analytics/coupons`)
|
||||
- ✅ Coupon usage chart over time
|
||||
- ✅ Top coupons by discount amount
|
||||
- ✅ **ROI calculation** (Revenue Generated / Discount Given)
|
||||
- ✅ Coupon performance metrics
|
||||
|
||||
#### **7. Taxes** (`/analytics/taxes`)
|
||||
- ✅ Tax chart over time
|
||||
- ✅ Total tax collected
|
||||
- ✅ Average tax per order
|
||||
- ✅ Orders with tax count
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Key Features Implemented
|
||||
|
||||
### **1. Conversion Rate Calculation**
|
||||
**Formula:** `(Completed Orders / Total Orders) × 100`
|
||||
|
||||
**Example:**
|
||||
- 10 orders total
|
||||
- 3 completed
|
||||
- Conversion Rate = 30.00%
|
||||
|
||||
**Location:** `AnalyticsController.php` lines 383-406
|
||||
|
||||
```php
|
||||
$total_all_orders = 0;
|
||||
$completed_orders = 0;
|
||||
|
||||
foreach ($orderStatusDistribution as $status) {
|
||||
$total_all_orders += $count;
|
||||
if ($status->status === 'wc-completed') {
|
||||
$completed_orders = $count;
|
||||
}
|
||||
}
|
||||
|
||||
$conversion_rate = $total_all_orders > 0
|
||||
? round(($completed_orders / $total_all_orders) * 100, 2)
|
||||
: 0.00;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **2. Fill All Dates in Charts**
|
||||
**Best Practice:** Show all dates in range, even with no data
|
||||
|
||||
**Implementation:** `AnalyticsController.php` lines 324-358
|
||||
|
||||
```php
|
||||
// Create a map of existing data
|
||||
$data_map = [];
|
||||
foreach ($salesChart as $row) {
|
||||
$data_map[$row->date] = [
|
||||
'revenue' => round(floatval($row->revenue), 2),
|
||||
'orders' => intval($row->orders),
|
||||
];
|
||||
}
|
||||
|
||||
// Fill in ALL dates in the range
|
||||
for ($i = $days - 1; $i >= 0; $i--) {
|
||||
$date = date('Y-m-d', strtotime("-{$i} days"));
|
||||
|
||||
if (isset($data_map[$date])) {
|
||||
$revenue = $data_map[$date]['revenue'];
|
||||
$orders = $data_map[$date]['orders'];
|
||||
} else {
|
||||
// No data for this date, fill with zeros
|
||||
$revenue = 0.00;
|
||||
$orders = 0;
|
||||
}
|
||||
|
||||
$formatted_sales[] = [
|
||||
'date' => $date,
|
||||
'revenue' => $revenue,
|
||||
'orders' => $orders,
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- ✅ Shows complete timeline (no gaps)
|
||||
- ✅ Weekends/holidays with no orders are visible
|
||||
- ✅ Accurate trend visualization
|
||||
- ✅ Matches Google Analytics, Shopify standards
|
||||
|
||||
---
|
||||
|
||||
### **3. Frontend Improvements**
|
||||
|
||||
#### **Conversion Rate Display**
|
||||
- ✅ Uses real API data (no dummy fallback)
|
||||
- ✅ Formatted as percentage with 2 decimals
|
||||
- ✅ Shows comparison for non-"all time" periods
|
||||
|
||||
#### **Low Stock Alert**
|
||||
- ✅ Hides when count is zero
|
||||
- ✅ Shows actual count from API
|
||||
- ✅ No dummy data fallback
|
||||
|
||||
**Location:** `admin-spa/src/routes/Dashboard/index.tsx`
|
||||
|
||||
```typescript
|
||||
// Conversion rate from real data
|
||||
const currentConversionRate = data?.metrics?.conversionRate?.today ?? 0;
|
||||
|
||||
// Low stock alert - hide if zero
|
||||
{(data?.lowStock?.length ?? 0) > 0 && (
|
||||
<div className="alert">
|
||||
{data?.lowStock?.length ?? 0} products need attention
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### **4. Chart Visualization**
|
||||
|
||||
**Sales Overview Chart:**
|
||||
- ✅ Area chart for revenue (gradient fill)
|
||||
- ✅ Line chart with dots for orders
|
||||
- ✅ Balanced visual hierarchy
|
||||
- ✅ Professional appearance
|
||||
|
||||
**Order Status Pie Chart:**
|
||||
- ✅ Sorted by importance (completed first)
|
||||
- ✅ Auto-selection of first status
|
||||
- ✅ Interactive hover states
|
||||
- ✅ Color-coded by status
|
||||
|
||||
---
|
||||
|
||||
## 📊 API Endpoints
|
||||
|
||||
All endpoints support caching (5 minutes):
|
||||
|
||||
1. `GET /woonoow/v1/analytics/overview?period=30`
|
||||
2. `GET /woonoow/v1/analytics/revenue?period=30&granularity=day`
|
||||
3. `GET /woonoow/v1/analytics/orders?period=30`
|
||||
4. `GET /woonoow/v1/analytics/products?period=30`
|
||||
5. `GET /woonoow/v1/analytics/customers?period=30`
|
||||
6. `GET /woonoow/v1/analytics/coupons?period=30`
|
||||
7. `GET /woonoow/v1/analytics/taxes?period=30`
|
||||
|
||||
**Period Options:** `7`, `14`, `30`, `all`
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI/UX Features
|
||||
|
||||
- ✅ Period selector (Last 7/14/30 days, All time)
|
||||
- ✅ Real Data toggle (switches between real and dummy data)
|
||||
- ✅ Responsive design (mobile-first)
|
||||
- ✅ Dark mode support
|
||||
- ✅ Loading states
|
||||
- ✅ Error handling
|
||||
- ✅ Empty states
|
||||
- ✅ Metric cards with comparison
|
||||
- ✅ Professional charts (Recharts)
|
||||
- ✅ Consistent styling (Shadcn UI)
|
||||
|
||||
---
|
||||
|
||||
## 📚 Files Changed
|
||||
|
||||
### Backend (PHP)
|
||||
- `includes/Api/AnalyticsController.php` - Complete implementation (~1200 lines)
|
||||
- `includes/Api/Routes.php` - 7 new endpoints
|
||||
|
||||
### Frontend (React/TypeScript)
|
||||
- `admin-spa/src/routes/Dashboard/index.tsx` - Overview page
|
||||
- `admin-spa/src/routes/Dashboard/Revenue.tsx` - Revenue page
|
||||
- `admin-spa/src/routes/Dashboard/Orders.tsx` - Orders analytics
|
||||
- `admin-spa/src/routes/Dashboard/Products.tsx` - Products analytics
|
||||
- `admin-spa/src/routes/Dashboard/Customers.tsx` - Customers analytics
|
||||
- `admin-spa/src/routes/Dashboard/Coupons.tsx` - Coupons analytics
|
||||
- `admin-spa/src/routes/Dashboard/Taxes.tsx` - Taxes analytics
|
||||
- `admin-spa/src/hooks/useAnalytics.ts` - Shared analytics hook
|
||||
|
||||
---
|
||||
|
||||
## 🐛 Fixes Applied
|
||||
|
||||
1. ✅ **Recharts prop warning** - Changed from function to string-based `dataKey`/`nameKey`
|
||||
2. ✅ **Conversion rate dummy data** - Now uses real API data
|
||||
3. ✅ **Low stock alert** - Hides when zero
|
||||
4. ✅ **Date gaps in charts** - All dates filled with zeros
|
||||
5. ✅ **"All time" comparison** - Suppressed for all time period
|
||||
6. ✅ **Percentage formatting** - Consistent 2 decimal places
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Next Steps (Optional Enhancements)
|
||||
|
||||
1. **Revenue by Category** - Group products by category
|
||||
2. **Revenue by Payment Method** - Breakdown by gateway
|
||||
3. **Revenue by Shipping Method** - Breakdown by shipping
|
||||
4. **Product Conversion Rate** - Track views → purchases
|
||||
5. **Customer Retention Rate** - Calculate repeat purchase rate
|
||||
6. **Previous Period Comparison** - Calculate "yesterday" metrics
|
||||
7. **Export to CSV** - Download analytics data
|
||||
8. **Date Range Picker** - Custom date selection
|
||||
9. **Real-time Updates** - WebSocket or polling
|
||||
10. **Dashboard Widgets** - Customizable widget system
|
||||
|
||||
---
|
||||
|
||||
## ✅ Success Criteria - ALL MET
|
||||
|
||||
- [x] 7 analytics pages implemented
|
||||
- [x] Real HPOS data integration
|
||||
- [x] Caching (5 minutes)
|
||||
- [x] Conversion rate calculation
|
||||
- [x] Fill all dates in charts
|
||||
- [x] ROI calculation for coupons
|
||||
- [x] Responsive design
|
||||
- [x] Dark mode support
|
||||
- [x] Error handling
|
||||
- [x] Loading states
|
||||
- [x] No dummy data fallbacks in Real Data mode
|
||||
- [x] Professional UI/UX
|
||||
|
||||
---
|
||||
|
||||
**Implementation Date:** November 4, 2025
|
||||
**Total Development Time:** ~6 hours
|
||||
**Status:** ✅ Production Ready
|
||||
**Next Milestone:** Products module OR Settings module
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
## Catatan Tambahan
|
||||
|
||||
Jika kamu ingin hanya isi plugin (tanpa folder dist, scripts, dsb.), jalankan perintah ini dari root project dan ganti argumen zip:
|
||||
```js
|
||||
execSync('zip -r dist/woonoow.zip woonoow.php includes admin-spa customer-spa composer.json package.json phpcs.xml README.md', { stdio: 'inherit' });
|
||||
```
|
||||
|
||||
Coba ganti isi file scripts/package-zip.mjs dengan versi di atas, lalu jalankan:
|
||||
```bash
|
||||
node scripts/package-zip.mjs
|
||||
```
|
||||
|
||||
Kalau sukses, kamu akan melihat log:
|
||||
```
|
||||
✅ Packed: dist/woonoow.zip
|
||||
```
|
||||
Reference in New Issue
Block a user