From 13bc5f90781983b95e453bffada0beb1e1bb630d Mon Sep 17 00:00:00 2001 From: vickytechkey Date: Sun, 16 Aug 2026 10:24:53 +0530 Subject: [PATCH] feat: integrate API-backed product list and dashboard statistics with authentication headers --- src/pages/Dashboard.jsx | 55 +++++++-- src/pages/products/ProductList.jsx | 185 ++++++++++++++++++----------- 2 files changed, 164 insertions(+), 76 deletions(-) diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index c43b3e7..59c1e95 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -1,16 +1,49 @@ -import React from 'react'; +import React, { useState, useEffect } from 'react'; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend } from 'chart.js'; import { Line } from 'react-chartjs-2'; +import { API_BASE_URL } from '../config'; +import { useAuth } from '../context/AuthContext'; ChartJS.register(CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend); const Dashboard = () => { + const { token } = useAuth(); + const [stats, setStats] = useState({ + total_sellers: 0, + pending_approvals: 0, + total_revenue: 0, + active_products: 0, + refunds_issued: 0 + }); + const [loading, setLoading] = useState(true); + + useEffect(() => { + const fetchStats = async () => { + try { + const res = await fetch(`${API_BASE_URL}/dashboard/`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + if (res.ok) { + const data = await res.json(); + setStats(data); + } + } catch (err) { + console.error("Failed to load dashboard stats:", err); + } finally { + setLoading(false); + } + }; + fetchStats(); + }, [token]); + const statCards = [ - { label: 'Total Sellers', value: '142', change: '+12% this month', icon: '👤' }, - { label: 'Pending Approvals', value: '8', change: 'Require review', icon: '⏳' }, - { label: 'Total Revenue', value: '₹12,45,800', change: '+18% this month', icon: '💰' }, - { label: 'Active Products', value: '1,840', change: '+5% this week', icon: '📦' }, - { label: 'Refunds Issued', value: '₹42,500', change: '8 processed', icon: '🔄' }, + { label: 'Total Sellers', value: stats.total_sellers.toString(), change: 'Live from DB', icon: '👤' }, + { label: 'Pending Approvals', value: stats.pending_approvals.toString(), change: stats.pending_approvals > 0 ? 'Action required' : 'Up to date', icon: '⏳' }, + { label: 'Total Revenue', value: `₹${stats.total_revenue.toLocaleString('en-IN')}`, change: 'Total completed sales', icon: '💰' }, + { label: 'Active Products', value: stats.active_products.toString(), change: 'Live in catalog', icon: '📦' }, + { label: 'Refunds Issued', value: `₹${stats.refunds_issued.toLocaleString('en-IN')}`, change: 'Processed refunds', icon: '🔄' }, ]; const chartData = { @@ -18,7 +51,7 @@ const Dashboard = () => { datasets: [ { label: 'Platform Sales (₹ Lakhs)', - data: [2.5, 3.8, 3.2, 5.4, 6.1, 7.8, 9.2, 12.45], + data: [2.5, 3.8, 3.2, 5.4, 6.1, 7.8, 9.2, stats.total_revenue / 100000], borderColor: '#8b5cf6', backgroundColor: 'rgba(139, 92, 246, 0.2)', tension: 0.4, @@ -49,6 +82,10 @@ const Dashboard = () => { }, }; + if (loading) { + return
Loading dashboard metrics...
; + } + return (

Dashboard Overview

@@ -85,7 +122,7 @@ const Dashboard = () => { borderRadius: 'var(--border-radius)', fontSize: '14px' }}> -
Validate 8 New Sellers
+
Validate New Sellers

Verify uploaded KYC Aadhar and PAN records.

{ fontSize: '14px' }}>
Shadowfax Reverse Log
-

2 returned items failed doorstep quality check (QC).

+

Check failed doorstep quality check (QC) status.

diff --git a/src/pages/products/ProductList.jsx b/src/pages/products/ProductList.jsx index 3837b36..10cc6be 100644 --- a/src/pages/products/ProductList.jsx +++ b/src/pages/products/ProductList.jsx @@ -1,30 +1,75 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import StatusBadge from '../../components/StatusBadge'; +import { API_BASE_URL } from '../../config'; +import { useAuth } from '../../context/AuthContext'; const ProductList = () => { + const { token } = useAuth(); const [searchTerm, setSearchTerm] = useState(''); - const [products, setProducts] = useState([ - { id: 101, name: 'Ultra Wireless Headset 5.0', seller: 'ElectroHub Retailers', price: '₹2,499', stock: 45, status: 'pending_approval' }, - { id: 102, name: 'ElectroHub Mechanical Keyboard', seller: 'ElectroHub Retailers', price: '₹4,999', stock: 12, status: 'approved' }, - { id: 201, name: 'Pure Organic Honey (500g)', seller: 'A1 Spices Ltd', price: '₹350', stock: 150, status: 'approved' }, - { id: 301, name: 'Air Running Shoes (Red)', seller: 'Apex Footwear', price: '₹1,899', stock: 8, status: 'on_hold' }, - ]); + const [products, setProducts] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); - const handleStatusChange = (prodId, newStatus) => { - setProducts(products.map(p => p.id === prodId ? { ...p, status: newStatus } : p)); - alert(`Product status changed to ${newStatus}`); + const fetchProducts = async () => { + try { + setLoading(true); + const res = await fetch(`${API_BASE_URL}/products/`, { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + if (!res.ok) throw new Error('Failed to fetch products'); + const data = await res.json(); + setProducts(data); + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + } }; - const handleRemove = (prodId) => { + useEffect(() => { + fetchProducts(); + }, [token]); + + const handleStatusChange = async (prodId, action) => { + try { + const res = await fetch(`${API_BASE_URL}/products/${prodId}/${action}/`, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + } + }); + if (!res.ok) throw new Error(`Failed to perform action: ${action}`); + alert(`Product action '${action}' completed successfully`); + fetchProducts(); + } catch (err) { + alert(err.message); + } + }; + + const handleRemove = async (prodId) => { if (window.confirm('Are you sure you want to permanently remove this product?')) { - setProducts(products.filter(p => p.id !== prodId)); - alert('Product permanently deleted'); + try { + const res = await fetch(`${API_BASE_URL}/products/${prodId}/`, { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${token}` + } + }); + if (!res.ok) throw new Error('Failed to delete product'); + alert('Product permanently deleted'); + fetchProducts(); + } catch (err) { + alert(err.message); + } } }; const filteredProducts = products.filter(p => p.name.toLowerCase().includes(searchTerm.toLowerCase()) || - p.seller.toLowerCase().includes(searchTerm.toLowerCase()) + (p.supplier_name && p.supplier_name.toLowerCase().includes(searchTerm.toLowerCase())) ); return ( @@ -42,60 +87,66 @@ const ProductList = () => { /> -
- - - - - - - - - - - - - - {filteredProducts.map(prod => ( - - - - - - - - + {loading ? ( +
Loading products catalog...
+ ) : error ? ( +
Error: {error}
+ ) : ( +
+
IDProduct TitleSellerPriceStockStatusActions
#{prod.id}{prod.name}{prod.seller}{prod.price}{prod.stock} items -
- {prod.status === 'pending_approval' && ( - <> - - - - )} - {prod.status === 'approved' && ( - - )} - {prod.status === 'on_hold' && ( - - )} - -
-
+ + + + + + + + + - ))} - -
IDProduct TitleSellerPriceStockStatusActions
-
+ + + {filteredProducts.map(prod => ( + + #{prod.id} + {prod.name} + {prod.supplier_name || 'N/A'} + ₹{parseFloat(prod.price).toLocaleString('en-IN')} + {prod.stock} items + + +
+ {prod.status === 'pending_approval' && ( + <> + + + + )} + {prod.status === 'approved' && ( + + )} + {prod.status === 'on_hold' && ( + + )} + +
+ + + ))} + + + + )} );