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 = () => {
/>
-
-
-
-
- | ID |
- Product Title |
- Seller |
- Price |
- Stock |
- Status |
- Actions |
-
-
-
- {filteredProducts.map(prod => (
-
- | #{prod.id} |
- {prod.name} |
- {prod.seller} |
- {prod.price} |
- {prod.stock} items |
- |
-
-
- {prod.status === 'pending_approval' && (
- <>
-
-
- >
- )}
- {prod.status === 'approved' && (
-
- )}
- {prod.status === 'on_hold' && (
-
- )}
-
-
- |
+ {loading ? (
+ Loading products catalog...
+ ) : error ? (
+ Error: {error}
+ ) : (
+
+
+
+
+ | ID |
+ Product Title |
+ Seller |
+ Price |
+ Stock |
+ Status |
+ Actions |
- ))}
-
-
-
+
+
+ {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' && (
+
+ )}
+
+
+ |
+
+ ))}
+
+
+
+ )}
);