The merchant dashboard was showing subscription count as "Total Stores". Add get_merchant_metrics() to MetricsProviderProtocol and implement it in tenancy, billing, and customer providers. Dashboard now fetches real stats from a new /merchants/core/dashboard/stats endpoint and displays 4 cards: active subscriptions, total stores, customers, team members. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
263 lines
8.1 KiB
Python
263 lines
8.1 KiB
Python
# app/modules/core/schemas/dashboard.py
|
|
"""
|
|
Dashboard schemas for core module.
|
|
|
|
These schemas define the response structures for store and admin dashboards.
|
|
They're located in core because dashboards are core functionality that should
|
|
always be available, regardless of which optional modules are enabled.
|
|
|
|
The analytics module can extend these with additional functionality (trends,
|
|
reports, exports) but the base dashboard schemas live here.
|
|
"""
|
|
|
|
from typing import Any
|
|
|
|
from pydantic import BaseModel, Field
|
|
|
|
# ============================================================================
|
|
# User Statistics
|
|
# ============================================================================
|
|
|
|
|
|
class UserStatsResponse(BaseModel):
|
|
"""User statistics response schema.
|
|
|
|
Used by: Platform statistics endpoints
|
|
"""
|
|
|
|
total_users: int = Field(..., description="Total number of users")
|
|
active_users: int = Field(..., description="Number of active users")
|
|
inactive_users: int = Field(..., description="Number of inactive users")
|
|
admin_users: int = Field(..., description="Number of admin users")
|
|
activation_rate: float = Field(..., description="Percentage of active users")
|
|
|
|
|
|
# ============================================================================
|
|
# Store Statistics (Admin)
|
|
# ============================================================================
|
|
|
|
|
|
class StoreStatsResponse(BaseModel):
|
|
"""Store statistics response schema for admin dashboard.
|
|
|
|
Used by: GET /api/v1/admin/stores/stats
|
|
"""
|
|
|
|
total: int = Field(..., description="Total number of stores")
|
|
verified: int = Field(..., description="Number of verified stores")
|
|
pending: int = Field(..., description="Number of pending verification stores")
|
|
inactive: int = Field(..., description="Number of inactive stores")
|
|
|
|
|
|
# ============================================================================
|
|
# Product Statistics
|
|
# ============================================================================
|
|
|
|
|
|
class ProductStatsResponse(BaseModel):
|
|
"""Product statistics response schema.
|
|
|
|
Used by: Platform statistics endpoints
|
|
"""
|
|
|
|
total_products: int = Field(0, description="Total number of products")
|
|
active_products: int = Field(0, description="Number of active products")
|
|
out_of_stock: int = Field(0, description="Number of out-of-stock products")
|
|
|
|
|
|
# ============================================================================
|
|
# Order Statistics
|
|
# ============================================================================
|
|
|
|
|
|
class OrderStatsBasicResponse(BaseModel):
|
|
"""Basic order statistics (stub until Order model is fully implemented).
|
|
|
|
Used by: Platform statistics endpoints
|
|
"""
|
|
|
|
total_orders: int = Field(0, description="Total number of orders")
|
|
pending_orders: int = Field(0, description="Number of pending orders")
|
|
completed_orders: int = Field(0, description="Number of completed orders")
|
|
|
|
|
|
# ============================================================================
|
|
# Import Statistics
|
|
# ============================================================================
|
|
|
|
|
|
class ImportStatsResponse(BaseModel):
|
|
"""Import job statistics response schema.
|
|
|
|
Used by: GET /api/v1/admin/marketplace-import-jobs/stats
|
|
"""
|
|
|
|
total: int = Field(..., description="Total number of import jobs")
|
|
pending: int = Field(..., description="Jobs waiting to start")
|
|
processing: int = Field(..., description="Jobs currently running")
|
|
completed: int = Field(..., description="Successfully completed jobs")
|
|
failed: int = Field(..., description="Failed jobs")
|
|
success_rate: float = Field(..., description="Percentage of successful imports")
|
|
|
|
|
|
# ============================================================================
|
|
# Comprehensive Stats
|
|
# ============================================================================
|
|
|
|
|
|
class StatsResponse(BaseModel):
|
|
"""Comprehensive platform statistics response schema."""
|
|
|
|
total_products: int
|
|
unique_brands: int
|
|
unique_categories: int
|
|
unique_marketplaces: int = 0
|
|
unique_stores: int = 0
|
|
total_inventory_entries: int = 0
|
|
total_inventory_quantity: int = 0
|
|
|
|
|
|
class MarketplaceStatsResponse(BaseModel):
|
|
"""Statistics per marketplace response schema."""
|
|
|
|
marketplace: str
|
|
total_products: int
|
|
unique_stores: int
|
|
unique_brands: int
|
|
|
|
|
|
# ============================================================================
|
|
# Platform Statistics (Combined)
|
|
# ============================================================================
|
|
|
|
|
|
class PlatformStatsResponse(BaseModel):
|
|
"""Combined platform statistics response schema.
|
|
|
|
Used by: GET /api/v1/admin/dashboard/stats/platform
|
|
"""
|
|
|
|
users: UserStatsResponse
|
|
stores: StoreStatsResponse
|
|
products: ProductStatsResponse
|
|
orders: OrderStatsBasicResponse
|
|
imports: ImportStatsResponse
|
|
|
|
|
|
# ============================================================================
|
|
# Admin Dashboard Response
|
|
# ============================================================================
|
|
|
|
|
|
class AdminDashboardResponse(BaseModel):
|
|
"""Admin dashboard response schema.
|
|
|
|
Used by: GET /api/v1/admin/dashboard
|
|
"""
|
|
|
|
platform: dict[str, Any] = Field(..., description="Platform information")
|
|
users: UserStatsResponse
|
|
stores: StoreStatsResponse
|
|
recent_stores: list[dict[str, Any]] = Field(
|
|
default_factory=list, description="Recent stores"
|
|
)
|
|
recent_imports: list[dict[str, Any]] = Field(
|
|
default_factory=list, description="Recent import jobs"
|
|
)
|
|
|
|
|
|
# ============================================================================
|
|
# Store Dashboard Statistics
|
|
# ============================================================================
|
|
|
|
|
|
class StoreProductStats(BaseModel):
|
|
"""Store product statistics."""
|
|
|
|
total: int = Field(0, description="Total products in catalog")
|
|
active: int = Field(0, description="Active products")
|
|
|
|
|
|
class StoreOrderStats(BaseModel):
|
|
"""Store order statistics."""
|
|
|
|
total: int = Field(0, description="Total orders")
|
|
pending: int = Field(0, description="Pending orders")
|
|
completed: int = Field(0, description="Completed orders")
|
|
|
|
|
|
class StoreCustomerStats(BaseModel):
|
|
"""Store customer statistics."""
|
|
|
|
total: int = Field(0, description="Total customers")
|
|
active: int = Field(0, description="Active customers")
|
|
|
|
|
|
class StoreRevenueStats(BaseModel):
|
|
"""Store revenue statistics."""
|
|
|
|
total: float = Field(0, description="Total revenue")
|
|
this_month: float = Field(0, description="Revenue this month")
|
|
|
|
|
|
class StoreInfo(BaseModel):
|
|
"""Store basic info for dashboard."""
|
|
|
|
id: int
|
|
name: str
|
|
store_code: str
|
|
|
|
|
|
class StoreDashboardStatsResponse(BaseModel):
|
|
"""Store dashboard statistics response schema.
|
|
|
|
Used by: GET /api/v1/store/dashboard/stats
|
|
"""
|
|
|
|
store: StoreInfo
|
|
products: StoreProductStats
|
|
orders: StoreOrderStats
|
|
customers: StoreCustomerStats
|
|
revenue: StoreRevenueStats
|
|
|
|
|
|
# ============================================================================
|
|
# Merchant Dashboard Statistics
|
|
# ============================================================================
|
|
|
|
|
|
class MerchantDashboardStatsResponse(BaseModel):
|
|
"""Merchant dashboard statistics response schema.
|
|
|
|
Used by: GET /api/v1/merchants/core/dashboard/stats
|
|
"""
|
|
|
|
active_subscriptions: int = Field(0, description="Active or trial subscriptions")
|
|
total_stores: int = Field(0, description="Total stores owned by this merchant")
|
|
total_customers: int = Field(0, description="Total customers across all stores")
|
|
team_members: int = Field(0, description="Distinct active team members across stores")
|
|
|
|
|
|
__all__ = [
|
|
# Stats responses
|
|
"StatsResponse",
|
|
"MarketplaceStatsResponse",
|
|
"ImportStatsResponse",
|
|
"UserStatsResponse",
|
|
"StoreStatsResponse",
|
|
"ProductStatsResponse",
|
|
"PlatformStatsResponse",
|
|
"OrderStatsBasicResponse",
|
|
# Admin dashboard
|
|
"AdminDashboardResponse",
|
|
# Store dashboard
|
|
"StoreProductStats",
|
|
"StoreOrderStats",
|
|
"StoreCustomerStats",
|
|
"StoreRevenueStats",
|
|
"StoreInfo",
|
|
"StoreDashboardStatsResponse",
|
|
# Merchant dashboard
|
|
"MerchantDashboardStatsResponse",
|
|
]
|