feat: Implement map analytics system with effectiveness tracking

Add complete map analytics pipeline to answer: "Which maps drive the most
players? Is my rotation working?"

Backend Changes:
- Migration 005: Add map_id FK to server_stats and wipe_history tables
- Stats consumer now captures current_map_id when persisting stats
- Map analytics queries: get_map_analytics() returns performance metrics,
  effectiveness scores, and rotation health
- API endpoint: GET /api/analytics/maps?range=90d returns summary with
  best performing map and rotation effectiveness percentage

Frontend Changes:
- MapAnalyticsView.vue: Complete dashboard with performance charts,
  sortable metrics table, actionable insights, and CSV export
- ECharts bar chart comparing avg vs peak players per map
- Color-coded effectiveness scoring (green ≥80%, yellow ≥60%, red <60%)
- Time range selector: 30d/90d/all

Purpose: Enables data-driven map selection for wipe day based on player
engagement metrics. Rotation effectiveness algorithm scores maps by
(avg_players / peak_players) * 100.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-15 14:22:55 -05:00
parent dc7f41b8c5
commit cef89ade18
9 changed files with 691 additions and 4 deletions

View File

@@ -119,11 +119,21 @@ const panelRoutes: RouteRecordRaw[] = [
name: 'wipe-history',
component: () => import('@/views/admin/WipeHistoryView.vue'),
},
{
path: 'wipes/analytics',
name: 'wipe-analytics',
component: () => import('@/views/admin/WipeAnalyticsView.vue'),
},
{
path: 'maps',
name: 'maps',
component: () => import('@/views/admin/MapsView.vue'),
},
{
path: 'maps/analytics',
name: 'map-analytics',
component: () => import('@/views/admin/MapAnalyticsView.vue'),
},
{
path: 'chat',
name: 'chat',
@@ -134,6 +144,11 @@ const panelRoutes: RouteRecordRaw[] = [
name: 'analytics',
component: () => import('@/views/admin/AnalyticsView.vue'),
},
{
path: 'retention',
name: 'retention',
component: () => import('@/views/admin/PlayerRetentionView.vue'),
},
{
path: 'notifications',
name: 'notifications',