diff --git a/apps/web/src/components/seller/SellerDashboardView.tsx b/apps/web/src/components/seller/SellerDashboardView.tsx index ca3dcea71..e803187eb 100644 --- a/apps/web/src/components/seller/SellerDashboardView.tsx +++ b/apps/web/src/components/seller/SellerDashboardView.tsx @@ -56,6 +56,15 @@ export const SellerDashboardView: React.FC = ({ const [balance, setBalance] = useState<{ connected: boolean; available: number; pending: number } | null>(null); const [stripeConnectAvailable, setStripeConnectAvailable] = useState(null); const [transfers, setTransfers] = useState([]); + // v0.12.0 F254: Marketplace balance + const [marketplaceBalance, setMarketplaceBalance] = useState<{ + available_cents: number; + pending_cents: number; + total_earned_cents: number; + total_paid_out_cents: number; + currency: string; + } | null>(null); + const [payoutLoading, setPayoutLoading] = useState(false); const fetchData = useCallback(async () => { setLoading(true); @@ -91,6 +100,13 @@ export const SellerDashboardView: React.FC = ({ } catch { setTransfers([]); } + // v0.12.0 F254: Fetch marketplace balance + try { + const mktBalance = await marketplaceService.getSellerBalance(); + setMarketplaceBalance(mktBalance); + } catch { + setMarketplaceBalance(null); + } } catch (e) { logger.error('Error loading seller dashboard data', { error: e instanceof Error ? e.message : String(e), @@ -106,6 +122,20 @@ export const SellerDashboardView: React.FC = ({ fetchData(); }, [fetchData]); + const handleRequestPayout = useCallback(async () => { + setPayoutLoading(true); + try { + await marketplaceService.requestPayout(); + addToast('Payout requested successfully', 'success'); + fetchData(); + } catch (err: unknown) { + const msg = (err as { response?: { data?: { error?: { message?: string } } } })?.response?.data?.error?.message || 'Failed to request payout'; + addToast(msg, 'error'); + } finally { + setPayoutLoading(false); + } + }, [addToast, fetchData]); + const handleConnectPayments = useCallback(async () => { try { const { onboarding_url } = await commerceService.connectStripeOnboard(); @@ -292,6 +322,40 @@ export const SellerDashboardView: React.FC = ({ )} + {/* v0.12.0 F254: Marketplace Balance Card */} + {marketplaceBalance && ( + +
+ +
+
+ Marketplace Balance +
+
+ €{(marketplaceBalance.available_cents / 100).toFixed(2)} +
+
+ Pending: €{(marketplaceBalance.pending_cents / 100).toFixed(2)} · Total earned: €{(marketplaceBalance.total_earned_cents / 100).toFixed(2)} +
+ {marketplaceBalance.available_cents >= 10000 && ( + + )} + {marketplaceBalance.available_cents > 0 && marketplaceBalance.available_cents < 10000 && ( +
+ Min. €100.00 for manual payout · Auto-payout at €50.00 weekly +
+ )} +
+ )} +
diff --git a/apps/web/src/services/marketplaceService.ts b/apps/web/src/services/marketplaceService.ts index 0043f9ea3..02ebdae97 100644 --- a/apps/web/src/services/marketplaceService.ts +++ b/apps/web/src/services/marketplaceService.ts @@ -269,4 +269,83 @@ export const marketplaceService = { }>(`/commerce/promo/${encodeURIComponent(code.trim())}`); return response.data; }, + + // v0.12.0 F254: Seller balance and payouts + getSellerBalance: async () => { + const response = await apiClient.get<{ + available_cents: number; + pending_cents: number; + total_earned_cents: number; + total_paid_out_cents: number; + currency: string; + }>('/sell/marketplace-balance'); + return response.data; + }, + + getSellerPayouts: async (limit = 20, offset = 0) => { + const response = await apiClient.get<{ + payouts: Array<{ + id: string; + amount_cents: number; + currency: string; + status: string; + payout_method: string; + scheduled_at: string; + processed_at?: string; + created_at: string; + }>; + }>('/sell/payouts', { params: { limit, offset } }); + return response.data?.payouts ?? []; + }, + + requestPayout: async () => { + const response = await apiClient.post<{ + id: string; + amount_cents: number; + currency: string; + status: string; + scheduled_at: string; + }>('/sell/payouts/request'); + return response.data; + }, + + // Seller stats + getSellerStats: async () => { + const response = await apiClient.get<{ + revenue: number; + sales_count: number; + }>('/sell/stats'); + return response.data; + }, + + getSellerStatsEvolution: async (period = 'day') => { + const response = await apiClient.get>('/sell/stats/evolution', { params: { period } }); + return response.data ?? []; + }, + + getSellerTopProducts: async (limit = 10) => { + const response = await apiClient.get>('/sell/stats/top-products', { params: { limit } }); + return response.data ?? []; + }, + + getSellerSales: async (limit = 20) => { + const response = await apiClient.get>('/sell/sales', { params: { limit } }); + return response.data ?? []; + }, };