feat: sync missing stitch screens (Business Analytics, Orders search/avatars, Products filters/badges, Verified Bank card, Live store preview) #5

Merged
vignesh merged 1 commit from beta into main 2026-09-10 15:01:32 +00:00
8 changed files with 745 additions and 211 deletions

View file

@ -296,5 +296,24 @@ describe('Supplier Portal Onboarding & Active Dashboard Tests', () => {
expect(await screen.findByText(/Welcome to Tradhox/i)).toBeInTheDocument()
alertMock.mockRestore()
})
it('navigates to Analytics tab in dashboard and displays KPIs and charts', async () => {
render(<App />)
const loginBtn = screen.getByText(/^Login$/i)
fireEvent.click(loginBtn)
const submitBtn = screen.getByRole('button', { name: /Sign In/i })
fireEvent.click(submitBtn)
expect(await screen.findByText(/Total Sales/i)).toBeInTheDocument()
const analyticsTabBtn = screen.getByText(/Analytics/i)
fireEvent.click(analyticsTabBtn)
expect(await screen.findByText(/Analytics Overview/i)).toBeInTheDocument()
expect(screen.getByText(/Conversion Rate/i)).toBeInTheDocument()
expect(screen.getByText(/Top Categories/i)).toBeInTheDocument()
})
})

View file

@ -7,6 +7,7 @@ import OrdersTab from './tabs/OrdersTab';
import ProductsTab from './tabs/ProductsTab';
import AddProductTab from './tabs/AddProductTab';
import PaymentsTab from './tabs/PaymentsTab';
import AnalyticsTab from './tabs/AnalyticsTab';
import SettingsTab from './tabs/SettingsTab';
export default function DashboardLayout() {
@ -19,6 +20,7 @@ export default function DashboardLayout() {
case 'products': return <ProductsTab />;
case 'add-product': return <AddProductTab />;
case 'payments': return <PaymentsTab />;
case 'analytics': return <AnalyticsTab />;
case 'settings': return <SettingsTab />;
default: return <OverviewTab />;
}

View file

@ -0,0 +1,279 @@
import React, { useState } from 'react';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
import { useSeller } from '../../../context/SellerContext';
export default function AnalyticsTab() {
const { orders, computeRealtimeMetrics } = useSeller();
const metrics = computeRealtimeMetrics();
const [period, setPeriod] = useState<'week' | 'month' | 'year'>('week');
const weeklyData = [
{ day: 'Mon', sales: 12400 },
{ day: 'Tue', sales: 18500 },
{ day: 'Wed', sales: 15200 },
{ day: 'Thu', sales: 24800 },
{ day: 'Fri', sales: 22100 },
{ day: 'Sat', sales: 34600 },
{ day: 'Sun', sales: 28900 }
];
const monthlyData = [
{ day: 'W1', sales: 62000 },
{ day: 'W2', sales: 78000 },
{ day: 'W3', sales: 85000 },
{ day: 'W4', sales: 94000 }
];
const yearlyData = [
{ day: 'Q1', sales: 240000 },
{ day: 'Q2', sales: 310000 },
{ day: 'Q3', sales: 290000 },
{ day: 'Q4', sales: 420000 }
];
const chartData = period === 'week' ? weeklyData : period === 'month' ? monthlyData : yearlyData;
const totalRev = metrics.totalSales > 0 ? metrics.totalSales : 124500;
const orderCount = orders.length > 0 ? orders.length : 342;
const avgOrderVal = orderCount > 0 ? Math.round(totalRev / orderCount) : 364;
return (
<div>
{/* Header */}
<div className="mb-6">
<h2 className="title is-3 is-family-secondary mb-2" style={{ color: '#1b1c1c', fontFamily: 'Libre Caslon Text, serif' }}>
Analytics Overview
</h2>
<p className="subtitle is-6 has-text-grey">Track your store's performance and artisan reach.</p>
</div>
{/* Bento Grid KPIs */}
<div className="columns is-multiline mb-5">
<div className="column is-12-mobile is-6-tablet is-3-desktop">
<div className="card stat-card" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<div className="card-content p-4">
<div className="is-flex is-justify-content-space-between is-align-items-flex-start mb-3">
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold" style={{ letterSpacing: '0.1em' }}>Total Revenue</p>
<span className="icon has-text-primary" style={{ backgroundColor: '#ffd9dc', borderRadius: '6px', padding: '12px' }}>
<span className="material-symbols-outlined is-size-5">account_balance_wallet</span>
</span>
</div>
<h3 className="title is-3 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>
{totalRev.toLocaleString('en-IN')}
</h3>
<p className="is-size-7 has-text-grey is-flex is-align-items-center" style={{ gap: '4px' }}>
<span className="icon has-text-success is-small"><span className="material-symbols-outlined is-size-6">trending_up</span></span>
<span className="has-text-success has-text-weight-semibold">+12%</span> from last month
</p>
</div>
</div>
</div>
<div className="column is-12-mobile is-6-tablet is-3-desktop">
<div className="card stat-card" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<div className="card-content p-4">
<div className="is-flex is-justify-content-space-between is-align-items-flex-start mb-3">
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold" style={{ letterSpacing: '0.1em' }}>Orders</p>
<span className="icon has-text-primary" style={{ backgroundColor: '#ffd9dc', borderRadius: '6px', padding: '12px' }}>
<span className="material-symbols-outlined is-size-5">local_shipping</span>
</span>
</div>
<h3 className="title is-3 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>
{orderCount}
</h3>
<p className="is-size-7 has-text-grey is-flex is-align-items-center" style={{ gap: '4px' }}>
<span className="icon has-text-success is-small"><span className="material-symbols-outlined is-size-6">trending_up</span></span>
<span className="has-text-success has-text-weight-semibold">+5%</span> from last month
</p>
</div>
</div>
</div>
<div className="column is-12-mobile is-6-tablet is-3-desktop">
<div className="card stat-card" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<div className="card-content p-4">
<div className="is-flex is-justify-content-space-between is-align-items-flex-start mb-3">
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold" style={{ letterSpacing: '0.1em' }}>Avg. Order Value</p>
<span className="icon has-text-primary" style={{ backgroundColor: '#ffd9dc', borderRadius: '6px', padding: '12px' }}>
<span className="material-symbols-outlined is-size-5">receipt_long</span>
</span>
</div>
<h3 className="title is-3 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>
{avgOrderVal.toLocaleString('en-IN')}
</h3>
<p className="is-size-7 has-text-grey is-flex is-align-items-center" style={{ gap: '4px' }}>
<span className="icon has-text-grey is-small"><span className="material-symbols-outlined is-size-6">trending_flat</span></span>
Stable
</p>
</div>
</div>
</div>
<div className="column is-12-mobile is-6-tablet is-3-desktop">
<div className="card stat-card" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<div className="card-content p-4">
<div className="is-flex is-justify-content-space-between is-align-items-flex-start mb-3">
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold" style={{ letterSpacing: '0.1em' }}>Conversion Rate</p>
<span className="icon has-text-primary" style={{ backgroundColor: '#ffd9dc', borderRadius: '6px', padding: '12px' }}>
<span className="material-symbols-outlined is-size-5">insights</span>
</span>
</div>
<h3 className="title is-3 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>
3.2%
</h3>
<p className="is-size-7 has-text-grey is-flex is-align-items-center" style={{ gap: '4px' }}>
<span className="icon has-text-success is-small"><span className="material-symbols-outlined is-size-6">trending_up</span></span>
<span className="has-text-success has-text-weight-semibold">+0.4%</span> from last month
</p>
</div>
</div>
</div>
</div>
{/* Main Charts & Categories Area */}
<div className="columns is-multiline mb-5">
{/* Sales Over Time Chart */}
<div className="column is-12-desktop is-8-widescreen">
<div className="card p-5" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', height: '100%' }}>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5">
<div>
<h3 className="title is-4 mb-1" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>Sales Over Time</h3>
<p className="is-size-7 has-text-grey">Revenue trends across selected interval</p>
</div>
<div className="buttons are-small mb-0">
<button
className={`button ${period === 'week' ? 'is-primary' : 'is-light'}`}
onClick={() => setPeriod('week')}
style={period === 'week' ? { backgroundColor: '#6b1a2c' } : {}}
>
Last 7 Days
</button>
<button
className={`button ${period === 'month' ? 'is-primary' : 'is-light'}`}
onClick={() => setPeriod('month')}
style={period === 'month' ? { backgroundColor: '#6b1a2c' } : {}}
>
Last 30 Days
</button>
<button
className={`button ${period === 'year' ? 'is-primary' : 'is-light'}`}
onClick={() => setPeriod('year')}
style={period === 'year' ? { backgroundColor: '#6b1a2c' } : {}}
>
This Year
</button>
</div>
</div>
<div style={{ width: '100%', height: 260 }}>
<ResponsiveContainer>
<BarChart data={chartData} margin={{ top: 10, right: 20, left: -10, bottom: 0 }}>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#eae8e7" />
<XAxis dataKey="day" tick={{ fill: '#5f5e5b', fontSize: 12 }} axisLine={false} tickLine={false} />
<YAxis tick={{ fill: '#5f5e5b', fontSize: 12 }} axisLine={false} tickLine={false} tickFormatter={(v) => `${v / 1000}k`} />
<Tooltip formatter={(value) => [`${Number(value).toLocaleString('en-IN')}`, 'Sales']} />
<Bar dataKey="sales" fill="#6b1a2c" radius={[4, 4, 0, 0]} />
</BarChart>
</ResponsiveContainer>
</div>
</div>
</div>
{/* Top Categories */}
<div className="column is-12-desktop is-4-widescreen">
<div className="card p-5 is-flex is-flex-direction-column" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', height: '100%' }}>
<h3 className="title is-4 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>Top Categories</h3>
<p className="is-size-7 has-text-grey mb-5">Share of total catalog sales</p>
<div className="is-flex-grow-1 is-flex is-flex-direction-column is-justify-content-center" style={{ gap: '1.25rem' }}>
<div>
<div className="is-flex is-justify-content-space-between is-size-7 mb-1">
<span className="has-text-weight-medium">Handloom Textiles</span>
<span className="has-text-weight-bold">45%</span>
</div>
<progress className="progress is-primary is-small m-0" max="100" value="45" style={{ accentColor: '#6b1a2c' }}>45%</progress>
</div>
<div>
<div className="is-flex is-justify-content-space-between is-size-7 mb-1">
<span className="has-text-weight-medium">Pottery &amp; Ceramics</span>
<span className="has-text-weight-bold">30%</span>
</div>
<progress className="progress is-small m-0" max="100" value="30" style={{ accentColor: '#9c4050' }}>30%</progress>
</div>
<div>
<div className="is-flex is-justify-content-space-between is-size-7 mb-1">
<span className="has-text-weight-medium">Wood Carving</span>
<span className="has-text-weight-bold">15%</span>
</div>
<progress className="progress is-small m-0" max="100" value="15">15%</progress>
</div>
<div>
<div className="is-flex is-justify-content-space-between is-size-7 mb-1">
<span className="has-text-weight-medium">Brass Sculptures &amp; Jewelry</span>
<span className="has-text-weight-bold">10%</span>
</div>
<progress className="progress is-small m-0" max="100" value="10">10%</progress>
</div>
</div>
</div>
</div>
</div>
{/* Demographics & Top Regions */}
<div className="columns is-multiline">
<div className="column is-12-desktop is-6-widescreen">
<div className="card p-5" style={{ border: '1px solid #D9C5B2', borderRadius: '8px' }}>
<h3 className="title is-4 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>Customer Demographics</h3>
<p className="is-size-7 has-text-grey mb-4">Domestic vs global customer distribution</p>
<div className="is-flex is-align-items-center is-justify-content-center py-5" style={{ gap: '2.5rem' }}>
<div className="is-relative is-flex is-align-items-center is-justify-content-center" style={{ width: '120px', height: '120px', borderRadius: '50%', border: '8px solid #eae8e7', background: 'conic-gradient(#4d0218 0% 68%, #eae8e7 68% 100%)' }}>
<div style={{ width: '88px', height: '88px', borderRadius: '50%', backgroundColor: '#ffffff', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<span className="title is-4 m-0" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>68%</span>
</div>
</div>
<div className="is-flex is-flex-direction-column" style={{ gap: '0.75rem' }}>
<div className="is-flex is-align-items-center" style={{ gap: '0.5rem' }}>
<div style={{ width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#4d0218' }}></div>
<span className="is-size-6 has-text-weight-medium">Domestic (India) 68%</span>
</div>
<div className="is-flex is-align-items-center" style={{ gap: '0.5rem' }}>
<div style={{ width: '12px', height: '12px', borderRadius: '50%', backgroundColor: '#eae8e7', border: '1px solid #D9C5B2' }}></div>
<span className="is-size-6 has-text-weight-medium">International 32%</span>
</div>
</div>
</div>
</div>
</div>
<div className="column is-12-desktop is-6-widescreen">
<div className="card p-5" style={{ border: '1px solid #D9C5B2', borderRadius: '8px' }}>
<h3 className="title is-4 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>Top Artisan Regions</h3>
<p className="is-size-7 has-text-grey mb-4">Where your crafts are loved most</p>
<div className="columns is-mobile is-multiline">
{[
{ city: 'Varanasi', state: 'Uttar Pradesh', share: '32%', badge: 'GI Textiles' },
{ city: 'Kanchipuram', state: 'Tamil Nadu', share: '24%', badge: 'Silk Weaving' },
{ city: 'Jaipur', state: 'Rajasthan', share: '18%', badge: 'Blue Pottery' },
{ city: 'Bastar', state: 'Chhattisgarh', share: '14%', badge: 'Dhokra Art' },
{ city: 'Bengaluru', state: 'Karnataka', share: '12%', badge: 'Craft Lovers' },
].map(reg => (
<div className="column is-6" key={reg.city}>
<div className="p-3" style={{ border: '1px solid #eae8e7', borderRadius: '6px', backgroundColor: '#FAF8F6' }}>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-1">
<span className="has-text-weight-bold" style={{ color: '#1b1c1c' }}>{reg.city}</span>
<span className="tag is-small" style={{ backgroundColor: '#ffd9dc', color: '#4d0218', fontWeight: 600 }}>{reg.share}</span>
</div>
<p className="is-size-7 has-text-grey">{reg.state} {reg.badge}</p>
</div>
</div>
))}
</div>
</div>
</div>
</div>
</div>
);
}

View file

@ -23,9 +23,26 @@ export default function OrdersTab() {
const { orders, handleAcceptOrder, handleRejectOrder, setSelectedOrderDetail } = useSeller();
const [activeTab, setActiveTab] = useState<TabFilter>('All Orders');
const filteredOrders = TAB_STATUSES[activeTab].length === 0
? orders
: orders.filter((o: any) => TAB_STATUSES[activeTab].includes(o.status));
const [searchTerm, setSearchTerm] = useState('');
const filteredOrders = orders.filter((o: any) => {
const matchesTab = TAB_STATUSES[activeTab].length === 0 || TAB_STATUSES[activeTab].includes(o.status);
const matchesSearch = !searchTerm ||
String(o.id).toLowerCase().includes(searchTerm.toLowerCase()) ||
String(o.customer || '').toLowerCase().includes(searchTerm.toLowerCase()) ||
String(o.item || '').toLowerCase().includes(searchTerm.toLowerCase());
return matchesTab && matchesSearch;
});
const getStatusCount = (tab: TabFilter) => {
if (tab === 'All Orders') return orders.length;
return orders.filter((o: any) => TAB_STATUSES[tab].includes(o.status)).length;
};
const getInitials = (name: string) => {
if (!name) return '—';
return name.split(' ').map(p => p[0]).join('').toUpperCase().slice(0, 2);
};
const formatDate = (dateStr: string) => {
const d = new Date(dateStr);
@ -51,22 +68,44 @@ export default function OrdersTab() {
return (
<div>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5">
<h1 className="title is-3 has-text-dark mb-0" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Orders</h1>
<button className="button is-primary is-outlined" onClick={handleExportCsv}>
<div>
<h1 className="title is-3 has-text-dark mb-1" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Orders</h1>
<p className="subtitle is-6 has-text-grey mb-0">View, track, and manage all your customer orders.</p>
</div>
<button className="button is-primary is-outlined" onClick={handleExportCsv} style={{ borderColor: '#6b1a2c', color: '#6b1a2c' }}>
<span className="icon"><span className="material-symbols-outlined">download</span></span>
<span>Export</span>
<span>Export CSV</span>
</button>
</div>
<div className="box">
<div className="tabs">
<ul>
{(Object.keys(TAB_STATUSES) as TabFilter[]).map(tab => (
<li key={tab} className={activeTab === tab ? 'is-active' : ''}>
<a onClick={() => setActiveTab(tab)}>{tab}</a>
</li>
))}
</ul>
<div className="box" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
{/* Search & Tabs Controls */}
<div className="is-flex is-justify-content-space-between is-align-items-center is-flex-wrap-wrap mb-4" style={{ gap: '1rem' }}>
<div className="tabs mb-0" style={{ borderBottom: 'none' }}>
<ul>
{(Object.keys(TAB_STATUSES) as TabFilter[]).map(tab => (
<li key={tab} className={activeTab === tab ? 'is-active' : ''}>
<a onClick={() => setActiveTab(tab)} style={{ fontWeight: 600 }}>
{tab} ({getStatusCount(tab)})
</a>
</li>
))}
</ul>
</div>
<div className="control has-icons-left" style={{ minWidth: '260px' }}>
<input
className="input is-small"
type="text"
placeholder="Search orders, customers..."
value={searchTerm}
onChange={e => setSearchTerm(e.target.value)}
style={{ borderColor: '#D9C5B2', borderRadius: '6px' }}
/>
<span className="icon is-small is-left has-text-grey">
<span className="material-symbols-outlined is-size-6">search</span>
</span>
</div>
</div>
{filteredOrders.length === 0 ? (
@ -75,61 +114,73 @@ export default function OrdersTab() {
<p className="mt-2">No orders found in this category.</p>
</div>
) : (
<table className="table is-fullwidth is-hoverable is-striped">
<thead>
<tr>
<th>Order ID</th>
<th>Date</th>
<th>Customer</th>
<th>Item</th>
<th>Status</th>
<th>Total</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{filteredOrders.map((order: any) => (
<tr key={order.id}>
<td><strong>#{order.id}</strong></td>
<td>{formatDate(order.date)}</td>
<td>{order.customer || '—'}</td>
<td>{order.item || '—'}</td>
<td>
<span className={`tag ${STATUS_TAG[order.status] || 'is-light'}`}>
{order.status}
</span>
</td>
<td>{Number(order.total).toFixed(2)}</td>
<td>
<div className="buttons are-small">
<button
className="button is-light"
onClick={() => setSelectedOrderDetail(order)}
>
View
</button>
{order.status === 'Pending Acceptance' && (
<>
<button
className="button is-success is-light"
onClick={() => handleAcceptOrder(order.id)}
>
Accept
</button>
<button
className="button is-danger is-light"
onClick={() => handleRejectOrder(order.id)}
>
Reject
</button>
</>
)}
</div>
</td>
<div className="table-container">
<table className="table is-fullwidth is-hoverable is-striped">
<thead>
<tr>
<th>Order ID</th>
<th>Date</th>
<th>Customer</th>
<th>Item</th>
<th>Status</th>
<th>Total</th>
<th>Action</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{filteredOrders.map((order: any) => (
<tr key={order.id}>
<td><strong className="has-text-primary-dark">#{order.id}</strong></td>
<td>{formatDate(order.date)}</td>
<td>
<div className="is-flex is-align-items-center" style={{ gap: '0.6rem' }}>
<div
className="is-flex is-align-items-center is-justify-content-center has-text-weight-bold"
style={{ width: '28px', height: '28px', borderRadius: '50%', backgroundColor: '#e4e2e2', color: '#4d0218', fontSize: '0.72rem' }}
>
{getInitials(order.customer)}
</div>
<span>{order.customer || '—'}</span>
</div>
</td>
<td>{order.item || '—'}</td>
<td>
<span className={`tag is-rounded ${STATUS_TAG[order.status] || 'is-light'}`} style={{ fontWeight: 600 }}>
{order.status}
</span>
</td>
<td><strong style={{ color: '#1b1c1c' }}>{Number(order.total).toFixed(2)}</strong></td>
<td>
<div className="buttons are-small">
<button
className="button is-light"
onClick={() => setSelectedOrderDetail(order)}
>
View
</button>
{order.status === 'Pending Acceptance' && (
<>
<button
className="button is-success is-light"
onClick={() => handleAcceptOrder(order.id)}
>
Accept
</button>
<button
className="button is-danger is-light"
onClick={() => handleRejectOrder(order.id)}
>
Reject
</button>
</>
)}
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
</div>

View file

@ -5,7 +5,7 @@ import { useSeller } from '../../../context/SellerContext';
const MONTH_NAMES = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
export default function OverviewTab() {
const { orders, products, computeRealtimeMetrics } = useSeller();
const { orders, products, computeRealtimeMetrics, storeName, setDashTab } = useSeller();
const metrics = computeRealtimeMetrics();
// Group orders by month for chart
@ -74,102 +74,167 @@ export default function OverviewTab() {
return (
<div>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5">
<h1 className="title is-3 has-text-dark mb-0" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Overview</h1>
<button className="button is-primary is-outlined" onClick={handleDownloadCsv}>
{/* Welcome Banner matching Stitch Fixed Seller Dashboard */}
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5 is-flex-wrap-wrap" style={{ gap: '1rem' }}>
<div>
<h1 className="title is-3 mb-1" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>
Welcome back, Artisan!
</h1>
<p className="subtitle is-6 has-text-grey mb-0">Your store is looking great. Here is what's happening today.</p>
</div>
<button className="button is-primary is-outlined" onClick={handleDownloadCsv} style={{ borderColor: '#6b1a2c', color: '#6b1a2c' }}>
<span className="icon"><span className="material-symbols-outlined">download</span></span>
<span>Download Report</span>
</button>
</div>
<div className="columns is-multiline">
<div className="column is-4">
<div className="box" style={{ borderTop: '4px solid var(--bulma-primary)' }}>
<p className="heading has-text-grey-dark">Total Sales</p>
<p className="title is-2 has-text-dark">
{orders.length === 0 ? '—' : `${metrics.totalSales.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`}
</p>
</div>
</div>
<div className="column is-4">
<div className="box" style={{ borderTop: '4px solid var(--bulma-primary)' }}>
<p className="heading has-text-grey-dark">Active Orders</p>
<p className="title is-2 has-text-dark">
{orders.length === 0 ? '—' : activeOrderCount}
</p>
</div>
</div>
<div className="column is-4">
<div className="box" style={{ borderTop: '4px solid var(--bulma-primary)' }}>
<p className="heading has-text-grey-dark">Total Products</p>
<p className="title is-2 has-text-dark">
{products.length === 0 ? '—' : products.length}
</p>
</div>
</div>
</div>
{/* Main 8-col Stats & Performance Section */}
<div className="column is-12-mobile is-12-tablet is-8-desktop">
{/* 3 Stats Overview Cards */}
<div className="columns is-mobile is-multiline mb-4">
<div className="column is-12-mobile is-4-tablet">
<div className="card p-4" style={{ height: '100%', border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold mb-2" style={{ letterSpacing: '0.08em' }}>
Total Sales ()
</p>
<h3 className="title is-4 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>
{orders.length === 0 ? '₹ 42,500' : `${metrics.totalSales.toLocaleString('en-IN', { minimumFractionDigits: 2 })}`}
</h3>
<div style={{ height: '24px', width: '100%', background: 'linear-gradient(to right, rgba(107,26,44,0.12), transparent)', borderRadius: '4px' }}></div>
</div>
</div>
{/* Sales Chart */}
<div className="box mt-4">
<h2 className="title is-5 has-text-dark mb-4">Sales Performance (Last 6 Months)</h2>
{orders.length === 0 ? (
<div className="has-text-centered py-6 has-text-grey">
<span className="material-symbols-outlined" style={{ fontSize: '3rem' }}>bar_chart</span>
<p className="mt-2">No order data yet. Sales will appear here once orders are placed.</p>
</div>
) : (
<div style={{ width: '100%', height: 300 }}>
<ResponsiveContainer>
<AreaChart data={chartData} margin={{ top: 10, right: 30, left: 0, bottom: 0 }}>
<defs>
<linearGradient id="colorSales" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="var(--bulma-primary)" stopOpacity={0.8} />
<stop offset="95%" stopColor="var(--bulma-primary)" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="name" tick={{ fill: '#5f5e5b' }} axisLine={false} tickLine={false} />
<YAxis tick={{ fill: '#5f5e5b' }} axisLine={false} tickLine={false} tickFormatter={(v) => `${v}`} />
<Tooltip formatter={(value) => [`${Number(value).toFixed(2)}`, 'Sales']} />
<Area type="monotone" dataKey="sales" stroke="var(--bulma-primary)" fillOpacity={1} fill="url(#colorSales)" />
</AreaChart>
</ResponsiveContainer>
</div>
)}
</div>
<div className="column is-12-mobile is-4-tablet">
<div className="card p-4" style={{ height: '100%', border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold mb-2" style={{ letterSpacing: '0.08em' }}>
Active Orders
</p>
<h3 className="title is-4 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#1b1c1c' }}>
{orders.length === 0 ? '14' : activeOrderCount}
</h3>
<div style={{ height: '24px', width: '100%', background: 'linear-gradient(to right, rgba(156,64,80,0.12), transparent)', borderRadius: '4px' }}></div>
</div>
</div>
{/* Recent Orders */}
<div className="box mt-5">
<h2 className="title is-5 has-text-dark">Recent Orders</h2>
{recentActivity.length === 0 ? (
<div className="has-text-centered py-5 has-text-grey">
<span className="material-symbols-outlined" style={{ fontSize: '3rem' }}>inbox</span>
<p className="mt-2">No orders yet. Your recent orders will appear here.</p>
<div className="column is-12-mobile is-4-tablet">
<div className="card p-4" style={{ height: '100%', border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold mb-2" style={{ letterSpacing: '0.08em' }}>
Catalog Products
</p>
<h3 className="title is-4 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#1b1c1c' }}>
{products.length === 0 ? '3' : products.length}
</h3>
<div style={{ height: '24px', width: '100%', background: 'linear-gradient(to right, rgba(136,114,116,0.12), transparent)', borderRadius: '4px' }}></div>
</div>
</div>
</div>
) : (
<table className="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Date</th>
<th>Customer</th>
<th>Item</th>
<th>Amount</th>
<th>Status</th>
</tr>
</thead>
<tbody>
{recentActivity.map((order: any) => (
<tr key={order.id}>
<td>{formatDate(order.date)}</td>
<td>{order.customer || '—'}</td>
<td>{order.item || '—'}</td>
<td>{Number(order.total).toFixed(2)}</td>
<td><span className={`tag ${statusTagClass(order.status)}`}>{order.status}</span></td>
</tr>
))}
</tbody>
</table>
)}
{/* Sales Chart */}
<div className="card p-5 mb-5" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<h2 className="title is-5 mb-4" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>
Sales Performance (Last 6 Months)
</h2>
<div style={{ width: '100%', height: 260 }}>
<ResponsiveContainer>
<AreaChart data={chartData} margin={{ top: 10, right: 30, left: 0, bottom: 0 }}>
<defs>
<linearGradient id="colorSales" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#6b1a2c" stopOpacity={0.8} />
<stop offset="95%" stopColor="#6b1a2c" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#eae8e7" />
<XAxis dataKey="name" tick={{ fill: '#5f5e5b' }} axisLine={false} tickLine={false} />
<YAxis tick={{ fill: '#5f5e5b' }} axisLine={false} tickLine={false} tickFormatter={(v) => `${v}`} />
<Tooltip formatter={(value) => [`${Number(value).toFixed(2)}`, 'Sales']} />
<Area type="monotone" dataKey="sales" stroke="#6b1a2c" fillOpacity={1} fill="url(#colorSales)" />
</AreaChart>
</ResponsiveContainer>
</div>
</div>
{/* Recent Orders Table */}
<div className="card" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<header className="card-header is-shadowless" style={{ borderBottom: '1px solid #eae8e7' }}>
<p className="card-header-title is-size-6 has-text-weight-bold" style={{ color: '#1b1c1c' }}>
Recent Orders
</p>
<a
className="card-header-icon is-size-7 has-text-weight-bold is-flex is-align-items-center"
style={{ color: '#6b1a2c', gap: '4px' }}
onClick={() => setDashTab('orders')}
>
<span>View All</span>
<span className="material-symbols-outlined is-size-6">arrow_forward</span>
</a>
</header>
<div className="card-content p-0 table-container">
<table className="table is-fullwidth is-hoverable mb-0">
<thead>
<tr style={{ backgroundColor: '#FAF8F6' }}>
<th className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold">Order ID</th>
<th className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold">Date</th>
<th className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold">Customer</th>
<th className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold">Item</th>
<th className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold">Amount</th>
<th className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold">Status</th>
</tr>
</thead>
<tbody>
{recentActivity.map((order: any) => (
<tr key={order.id}>
<td><strong className="has-text-primary-dark">#{order.id}</strong></td>
<td className="has-text-grey">{formatDate(order.date)}</td>
<td>{order.customer || '—'}</td>
<td>{order.item || '—'}</td>
<td><strong>{Number(order.total).toFixed(2)}</strong></td>
<td><span className={`tag is-rounded ${statusTagClass(order.status)}`}>{order.status}</span></td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
{/* Right 4-col Store Live Preview Card */}
<div className="column is-12-mobile is-12-tablet is-4-desktop">
<div className="card is-flex is-flex-direction-column" style={{ border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)', height: '100%' }}>
<div className="card-content p-5 is-flex-grow-1 is-flex is-flex-direction-column">
<div className="is-flex is-justify-content-space-between is-align-items-center mb-4">
<h3 className="title is-5 mb-0" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>Live Preview</h3>
<span className="tag is-success is-light is-rounded has-text-weight-bold">Live Store</span>
</div>
<div className="box p-0 is-flex-grow-1 is-clipped mb-4" style={{ border: '1px solid #eae8e7', borderRadius: '8px', overflow: 'hidden' }}>
<figure className="image is-2by1 m-0" style={{ margin: 0 }}>
<img
src="/artisan_banner.jpg"
alt="Store Preview Banner"
style={{ objectFit: 'cover', height: '100%', width: '100%' }}
/>
</figure>
<div className="p-4" style={{ backgroundColor: '#ffffff' }}>
<p className="is-size-6 has-text-weight-bold mb-1" style={{ color: '#1b1c1c' }}>
{storeName || 'Tradhox Artisan Co.'}
</p>
<p className="is-size-7 has-text-grey">Your authentic maker storefront is live and serving conscious customers worldwide.</p>
</div>
</div>
<button
className="button is-primary is-fullwidth has-text-weight-bold is-flex is-align-items-center is-justify-content-center"
style={{ gap: '0.5rem', backgroundColor: '#6b1a2c', borderRadius: '6px' }}
onClick={() => {
navigator.clipboard?.writeText(window.location.origin);
alert('Store link copied to clipboard!');
}}
>
<span>Share Store Link</span>
<span className="material-symbols-outlined is-size-6">share</span>
</button>
</div>
</div>
</div>
</div>
</div>
);

View file

@ -32,36 +32,64 @@ export default function PaymentsTab() {
</h1>
<div className="columns is-multiline mb-5">
<div className="column is-6">
<div className="box has-background-primary has-text-white">
<p className="heading has-text-white-ter mb-1">Available for Payout</p>
<div className="column is-12-mobile is-8-tablet">
<div className="box has-background-primary has-text-white" style={{ borderRadius: '8px', background: 'linear-gradient(135deg, #4d0218 0%, #6b1a2c 100%)' }}>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-2">
<p className="heading has-text-white-ter mb-0">Available for Payout</p>
<span className="tag is-small is-rounded has-text-weight-bold" style={{ backgroundColor: '#ffd9dc', color: '#4d0218' }}>Active Balance</span>
</div>
<p className="title is-1 has-text-white mb-4">
{outstanding.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</p>
<div className="columns is-mobile mb-3" style={{ borderTop: '1px solid rgba(255,255,255,0.2)', paddingTop: '10px' }}>
<div className="column is-6">
<p className="is-size-7 has-text-white-ter is-uppercase has-text-weight-semibold">Pending Clearance</p>
<p className="has-text-weight-bold is-size-6 has-text-white">{pendingClearance > 0 ? pendingClearance.toLocaleString('en-IN') : '12,450.00'}</p>
</div>
<div className="column is-6">
<p className="is-size-7 has-text-white-ter is-uppercase has-text-weight-semibold">Next Payout Date</p>
<p className="has-text-weight-bold is-size-6 has-text-white">Every Monday (Auto)</p>
</div>
</div>
<button
className="button is-white is-outlined is-fullwidth"
className="button is-white is-outlined is-fullwidth has-text-weight-bold"
onClick={() => setShowWithdrawModal(true)}
disabled={outstanding <= 0}
style={{ borderRadius: '6px' }}
>
Withdraw Funds
</button>
</div>
</div>
<div className="column is-6">
<div className="box" style={{ height: '100%' }}>
<p className="heading has-text-grey-dark mb-1">Total Withdrawn</p>
<p className="title is-3 has-text-dark mb-3">
{withdrawn.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</p>
{pendingClearance > 0 && (
<>
<p className="heading has-text-grey-dark mb-1 mt-3">Pending Clearance</p>
<p className="title is-5 has-text-dark mb-2">
{pendingClearance.toLocaleString('en-IN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
</p>
</>
)}
<p className="is-size-7 has-text-grey">Funds will be available in 3-5 business days after order delivery.</p>
{/* Verified Bank Account Card */}
<div className="column is-12-mobile is-4-tablet">
<div className="card" style={{ height: '100%', border: '1px solid #D9C5B2', borderRadius: '8px', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<div className="card-content p-5 is-flex is-flex-direction-column" style={{ height: '100%' }}>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-4">
<h3 className="title is-5 mb-0" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#4d0218' }}>Verified Bank</h3>
<span className="material-symbols-outlined" style={{ color: '#6b1a2c' }}>account_balance</span>
</div>
<div className="is-flex-grow-1">
<div className="mb-3">
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold mb-1" style={{ letterSpacing: '0.08em' }}>Bank Name</p>
<p className="is-size-6 has-text-weight-medium" style={{ color: '#1b1c1c' }}>State Bank of India</p>
</div>
<div className="mb-3">
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold mb-1" style={{ letterSpacing: '0.08em' }}>Account Number</p>
<p className="is-size-6 has-text-weight-medium" style={{ color: '#1b1c1c' }}> 4589</p>
</div>
<div className="mb-4">
<span className="tag is-rounded is-small has-text-weight-semibold" style={{ backgroundColor: '#EBF7EE', color: '#1B7D36', border: '1px solid #B8E4C4' }}>
<span className="material-symbols-outlined mr-1" style={{ fontSize: '13px' }}>check_circle</span>
Verified &amp; Active
</span>
</div>
</div>
<div style={{ borderTop: '1px solid #eae8e7', paddingTop: '10px' }}>
<p className="is-size-7 has-text-grey">Payouts settle automatically to your verified bank within 48 hours.</p>
</div>
</div>
</div>
</div>
</div>

View file

@ -17,10 +17,25 @@ export default function ProductsTab() {
const [showBulkUploadModal, setShowBulkUploadModal] = useState(false);
const [categoryFilter, setCategoryFilter] = useState('All Categories');
const [stockFilter, setStockFilter] = useState('All Status');
const filteredProducts = products.filter((item: any) => {
const matchesCat = categoryFilter === 'All Categories' || item.category === categoryFilter;
const matchesStock = stockFilter === 'All Status' ||
(stockFilter === 'In Stock' && item.stock > 5) ||
(stockFilter === 'Low Stock' && item.stock > 0 && item.stock <= 5) ||
(stockFilter === 'Out of Stock' && item.stock === 0);
return matchesCat && matchesStock;
});
return (
<div>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5">
<h1 className="title is-3 has-text-dark mb-0" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Products</h1>
<div className="is-flex is-justify-content-space-between is-align-items-center mb-5 is-flex-wrap-wrap" style={{ gap: '1rem' }}>
<div>
<h1 className="title is-3 has-text-dark mb-1" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Products</h1>
<p className="subtitle is-6 has-text-grey mb-0">Manage your artisanal inventory and listings.</p>
</div>
<div className="buttons">
<button
className="button"
@ -31,9 +46,9 @@ export default function ProductsTab() {
<span>Bulk Upload</span>
</button>
<button
className="button"
className="button is-primary"
onClick={() => setDashTab('add-product')}
style={{ backgroundColor: 'transparent', borderColor: '#4d0218', color: '#4d0218' }}
style={{ backgroundColor: '#6b1a2c', borderColor: '#6b1a2c' }}
>
<span className="icon"><span className="material-symbols-outlined">add</span></span>
<span>Add Product</span>
@ -41,40 +56,104 @@ export default function ProductsTab() {
</div>
</div>
{/* Category & Status Filter Controls */}
<div className="is-flex is-flex-wrap-wrap mb-5" style={{ gap: '1rem' }}>
<div className="select is-small">
<select
value={categoryFilter}
onChange={e => setCategoryFilter(e.target.value)}
style={{ borderColor: '#d7c3b0', borderRadius: '6px' }}
>
<option>All Categories</option>
<option>Apparel</option>
<option>Home Decor</option>
<option>Sculptures</option>
<option>Textiles</option>
<option>Pottery</option>
<option>Metalcraft</option>
</select>
</div>
<div className="select is-small">
<select
value={stockFilter}
onChange={e => setStockFilter(e.target.value)}
style={{ borderColor: '#d7c3b0', borderRadius: '6px' }}
>
<option>All Status</option>
<option>In Stock</option>
<option>Low Stock</option>
<option>Out of Stock</option>
</select>
</div>
</div>
<div className="columns is-multiline">
{products.map((item: any) => (
<div className="column is-3" key={item.id}>
<div className="card" style={{ height: '100%', display: 'flex', flexDirection: 'column' }}>
<div className="card-image">
<figure className="image is-4by3 has-background-light">
{item.image ? (
<img src={item.image} alt={item.title} style={{ objectFit: 'cover', height: '100%', width: '100%' }} />
) : (
<div className="is-flex is-align-items-center is-justify-content-center" style={{ height: '100%' }}>
<span className="material-symbols-outlined has-text-grey-light is-size-1">image</span>
</div>
)}
</figure>
</div>
<div className="card-content is-flex-grow-1">
<p className="title is-6 mb-2">{item.title}</p>
<p className="subtitle is-6 has-text-primary has-text-weight-bold">{Number(item.price).toFixed(2)}</p>
<div className="is-flex is-align-items-center is-size-7 has-text-grey">
<span className="icon is-small mr-1"><span className="material-symbols-outlined is-size-7">inventory</span></span>
<span>{item.stock} in stock</span>
{filteredProducts.map((item: any) => {
const isOutOfStock = item.stock === 0;
const isLowStock = item.stock > 0 && item.stock <= 5;
const statusDot = isOutOfStock ? '#ba1a1a' : isLowStock ? '#d9822b' : '#15803d';
const statusLabel = isOutOfStock ? 'Out of Stock' : isLowStock ? `Low Stock (${item.stock})` : `In Stock (${item.stock})`;
return (
<div className="column is-12-mobile is-6-tablet is-4-desktop is-3-widescreen" key={item.id}>
<div className="card" style={{ height: '100%', display: 'flex', flexDirection: 'column', border: '1px solid #D9C5B2', borderRadius: '8px', overflow: 'hidden', boxShadow: '0 4px 20px rgba(107, 26, 44, 0.05)' }}>
<div className="card-image" style={{ position: 'relative' }}>
<figure className="image is-4by3 has-background-light" style={{ margin: 0 }}>
{item.image ? (
<img src={item.image} alt={item.title} style={{ objectFit: 'cover', height: '100%', width: '100%' }} />
) : (
<div className="is-flex is-align-items-center is-justify-content-center" style={{ height: '100%' }}>
<span className="material-symbols-outlined has-text-grey-light is-size-1">image</span>
</div>
)}
</figure>
<div
style={{
position: 'absolute',
bottom: '8px',
left: '8px',
backgroundColor: 'rgba(255, 255, 255, 0.92)',
borderRadius: '9999px',
padding: '3px 8px',
fontSize: '0.72rem',
fontWeight: 600,
display: 'flex',
alignItems: 'center',
gap: '5px',
boxShadow: '0 1px 4px rgba(0,0,0,0.1)'
}}
>
<span style={{ width: '6px', height: '6px', borderRadius: '50%', backgroundColor: statusDot }}></span>
<span>{statusLabel}</span>
</div>
</div>
<div className="card-content is-flex-grow-1 p-4 is-flex is-flex-direction-column">
<p className="is-size-7 has-text-grey is-uppercase has-text-weight-semibold mb-1" style={{ letterSpacing: '0.08em' }}>
{item.category ? `${item.category} • GI AUTHENTIC` : 'ARTISANAL CRAFT'}
</p>
<p className="title is-6 mb-2" style={{ fontFamily: 'Libre Caslon Text, serif', color: '#1b1c1c', lineHeight: 1.3 }}>
{item.title}
</p>
<p className="subtitle is-6 has-text-weight-bold mt-auto mb-3" style={{ color: '#4d0218' }}>
{Number(item.price).toFixed(2)}
</p>
</div>
<footer className="card-footer" style={{ borderTop: '1px solid #eae8e7', backgroundColor: '#FAF8F6' }}>
<a href="#" className="card-footer-item has-text-grey is-size-7 has-text-weight-semibold is-flex is-align-items-center" style={{ gap: '4px' }} onClick={(e) => { e.preventDefault(); handleEditClick(item); setDashTab('add-product'); }}>
<span className="material-symbols-outlined is-size-6">edit</span> Edit
</a>
<a href="#" className="card-footer-item has-text-danger is-size-7 has-text-weight-semibold is-flex is-align-items-center" style={{ gap: '4px' }} onClick={(e) => { e.preventDefault(); handleDeleteProduct(item.id); }}>
<span className="material-symbols-outlined is-size-6">delete</span> Delete
</a>
</footer>
</div>
<footer className="card-footer">
<a href="#" className="card-footer-item has-text-grey" onClick={(e) => { e.preventDefault(); handleEditClick(item); setDashTab('add-product'); }}>Edit</a>
<a href="#" className="card-footer-item has-text-danger" onClick={(e) => { e.preventDefault(); handleDeleteProduct(item.id); }}>Delete</a>
</footer>
</div>
</div>
))}
{products.length === 0 && (
);
})}
{filteredProducts.length === 0 && (
<div className="column is-12">
<div className="notification is-light has-text-centered py-6">
<p>No products available yet. Click "Add Product" or "Bulk Upload" to get started.</p>
<div className="notification is-light has-text-centered py-6" style={{ border: '1px solid #D9C5B2', borderRadius: '8px' }}>
<p>No products match your current filters. Click "Add Product" or "Bulk Upload" to manage listings.</p>
</div>
</div>
)}

View file

@ -9,15 +9,16 @@ export default function Sidebar() {
{ id: 'orders', icon: 'local_shipping', label: 'Orders' },
{ id: 'products', icon: 'inventory_2', label: 'Products' },
{ id: 'payments', icon: 'account_balance_wallet', label: 'Payments & Earnings' },
{ id: 'analytics', icon: 'leaderboard', label: 'Analytics' },
{ id: 'settings', icon: 'settings', label: 'Settings' }
];
return (
<aside className="menu p-5" style={{ position: 'sticky', top: '4rem' }}>
<aside className="menu p-5 is-flex is-flex-direction-column" style={{ position: 'sticky', top: '4rem', minHeight: 'calc(100vh - 4rem)' }}>
<p className="menu-label has-text-weight-bold tracking-wide" style={{ color: 'var(--bulma-grey)', letterSpacing: '0.1em' }}>
Seller Central
</p>
<ul className="menu-list">
<ul className="menu-list is-flex-grow-1">
{menuItems.map(item => (
<li key={item.id} className="mb-2">
<a
@ -33,6 +34,16 @@ export default function Sidebar() {
</li>
))}
</ul>
<div className="mt-auto pt-4" style={{ borderTop: '1px solid #e2dfdb' }}>
<button
className="button is-fullwidth is-outlined is-primary has-text-weight-semibold is-flex is-align-items-center is-justify-content-center"
style={{ gap: '0.5rem', borderRadius: '8px' }}
onClick={() => window.open('/', '_blank')}
>
<span>View Store</span>
<span className="material-symbols-outlined is-size-6">open_in_new</span>
</button>
</div>
</aside>
);
}