# 👥 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!