Remove mock data from Overview; fix Settings save button
Some checks failed
Beta CI/CD Pipeline / build-and-test (push) Failing after 44s
Beta CI/CD Pipeline / deploy-beta (push) Has been skipped

This commit is contained in:
vickytechkey 2026-08-21 11:57:14 +05:30
parent 114fa4706c
commit 91971379c5
2 changed files with 275 additions and 134 deletions

View file

@ -1,28 +1,72 @@
import React from 'react';
import React, { useMemo } from 'react';
import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
import { useSeller } from '../../../context/SellerContext';
const data = [
{ name: 'Jan', sales: 4000 },
{ name: 'Feb', sales: 3000 },
{ name: 'Mar', sales: 5000 },
{ name: 'Apr', sales: 2780 },
{ name: 'May', sales: 8890 },
{ name: 'Jun', sales: 14500 },
];
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 metrics = computeRealtimeMetrics();
// Group orders by month for chart
const chartData = useMemo(() => {
const monthlySales: Record<number, number> = {};
orders
.filter((o: any) => o.status !== 'Cancelled' && o.status !== 'Rejected')
.forEach((o: any) => {
const date = new Date(o.date);
if (!isNaN(date.getTime())) {
const month = date.getMonth();
monthlySales[month] = (monthlySales[month] || 0) + Number(o.total);
}
});
// Show last 6 months
const currentMonth = new Date().getMonth();
const months = [];
for (let i = 5; i >= 0; i--) {
const idx = (currentMonth - i + 12) % 12;
months.push({ name: MONTH_NAMES[idx], sales: monthlySales[idx] || 0 });
}
return months;
}, [orders]);
const activeOrderCount = orders.filter((o: any) =>
!['Cancelled', 'Rejected', 'Delivered'].includes(o.status)
).length;
const recentActivity = useMemo(() => {
return [...orders]
.sort((a: any, b: any) => new Date(b.date).getTime() - new Date(a.date).getTime())
.slice(0, 5);
}, [orders]);
const formatDate = (dateStr: string) => {
const d = new Date(dateStr);
if (isNaN(d.getTime())) return dateStr;
return d.toLocaleDateString('en-IN', { day: '2-digit', month: 'short', year: 'numeric' });
};
const statusTagClass = (status: string) => {
switch (status) {
case 'Delivered': return 'is-success is-light';
case 'Shipped': return 'is-info is-light';
case 'Ready to Ship': return 'is-primary is-light';
case 'Pending Acceptance': return 'is-warning is-light';
case 'Rejected':
case 'Cancelled': return 'is-danger is-light';
default: return 'is-light';
}
};
const handleDownloadCsv = () => {
// Generate CSV string
const header = "Month,Sales\n";
const rows = data.map(row => `${row.name},${row.sales}`).join("\n");
const csvContent = header + rows;
// Create Blob and trigger download
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
const header = 'Month,Sales\n';
const rows = chartData.map(row => `${row.name},${row.sales}`).join('\n');
const blob = new Blob([header + rows], { type: 'text/csv;charset=utf-8;' });
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.setAttribute("href", url);
link.setAttribute("download", "sales_report.csv");
const link = document.createElement('a');
link.setAttribute('href', url);
link.setAttribute('download', 'sales_report.csv');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
@ -42,74 +86,90 @@ export default function OverviewTab() {
<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">$14,500</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">23</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">Page Views</p>
<p className="title is-2 has-text-dark">1,204</p>
<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>
{/* Sales Chart */}
<div className="box mt-4">
<h2 className="title is-5 has-text-dark mb-4">Sales Performance (YTD)</h2>
<div style={{ width: '100%', height: 300 }}>
<ResponsiveContainer>
<AreaChart
data={data}
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={(value) => `$${value}`} />
<Tooltip formatter={(value) => [`$${value}`, 'Sales']} />
<Area type="monotone" dataKey="sales" stroke="var(--bulma-primary)" fillOpacity={1} fill="url(#colorSales)" />
</AreaChart>
</ResponsiveContainer>
</div>
<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>
{/* Recent Orders */}
<div className="box mt-5">
<h2 className="title is-5 has-text-dark">Recent Activity</h2>
<table className="table is-fullwidth is-hoverable">
<thead>
<tr>
<th>Date</th>
<th>Customer</th>
<th>Action</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>Oct 24, 2023</td>
<td>Jane Doe</td>
<td>Placed Order #1045</td>
<td><span className="tag is-info is-light">Processing</span></td>
</tr>
<tr>
<td>Oct 23, 2023</td>
<td>John Smith</td>
<td>Left a 5-star review</td>
<td><span className="tag is-success is-light">Completed</span></td>
</tr>
</tbody>
</table>
<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>
) : (
<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>
)}
</div>
</div>
);

View file

@ -1,90 +1,171 @@
import React from 'react';
import React, { useState } from 'react';
import { useSeller } from '../../../context/SellerContext';
export default function SettingsTab() {
const { storeSlug, supportEmail, supportPhone } = useSeller();
const {
storeName, setStoreName,
storeSlug,
businessBio, setBusinessBio,
supportEmail, setSupportEmail,
supportPhone, setSupportPhone,
saveProfileBackend,
uploadDocument,
} = useSeller();
const [isSaving, setIsSaving] = useState(false);
const [saveSuccess, setSaveSuccess] = useState(false);
const [saveError, setSaveError] = useState('');
const handleSave = async (e: React.FormEvent) => {
e.preventDefault();
setIsSaving(true);
setSaveSuccess(false);
setSaveError('');
try {
await saveProfileBackend(true);
setSaveSuccess(true);
setTimeout(() => setSaveSuccess(false), 3000);
} catch (err: any) {
setSaveError(err.message || 'Failed to save settings.');
} finally {
setIsSaving(false);
}
};
return (
<div className="columns is-centered">
<div className="column is-8">
<h1 className="title is-3 has-text-dark mb-5" style={{ fontFamily: 'Libre Caslon Text, serif' }}>Settings</h1>
<div className="box mb-5">
<h2 className="title is-5 has-text-primary border-bottom pb-2" style={{ borderBottom: '1px solid #eee' }}>Store Profile</h2>
<div className="field">
<label className="label">Store Name</label>
<div className="control">
<input className="input" type="text" defaultValue="My Artisan Store" />
</div>
</div>
<div className="field">
<label className="label">Store URL Slug</label>
<div className="control">
<input className="input" type="text" value={storeSlug} readOnly />
</div>
<p className="help">Contact support to change your store URL.</p>
</div>
<div className="field">
<label className="label">Store Description</label>
<div className="control">
<textarea className="textarea" placeholder="Tell buyers about your craft..."></textarea>
</div>
{saveSuccess && (
<div className="notification is-success is-light mb-4">
<button className="delete" onClick={() => setSaveSuccess(false)} />
Settings saved successfully!
</div>
</div>
)}
{saveError && (
<div className="notification is-danger is-light mb-4">
<button className="delete" onClick={() => setSaveError('')} />
{saveError}
</div>
)}
<div className="box mb-5">
<h2 className="title is-5 has-text-primary border-bottom pb-2" style={{ borderBottom: '1px solid #eee' }}>Contact Information</h2>
<div className="columns">
<div className="column is-half">
<div className="field">
<label className="label">Support Email</label>
<div className="control">
<input className="input" type="email" defaultValue={supportEmail} />
</div>
<form onSubmit={handleSave}>
<div className="box mb-5">
<h2 className="title is-5 has-text-primary pb-2" style={{ borderBottom: '1px solid #eee' }}>Store Profile</h2>
<div className="field">
<label className="label">Store Name</label>
<div className="control">
<input
className="input"
type="text"
value={storeName}
onChange={e => setStoreName(e.target.value)}
placeholder="Your store name"
/>
</div>
</div>
<div className="column is-half">
<div className="field">
<label className="label">Support Phone</label>
<div className="control">
<input className="input" type="tel" defaultValue={supportPhone} />
<div className="field">
<label className="label">Store URL Slug</label>
<div className="control">
<input className="input" type="text" value={storeSlug} readOnly />
</div>
<p className="help">Contact support to change your store URL.</p>
</div>
<div className="field">
<label className="label">Store Description</label>
<div className="control">
<textarea
className="textarea"
placeholder="Tell buyers about your craft..."
value={businessBio}
onChange={e => setBusinessBio(e.target.value)}
/>
</div>
</div>
</div>
<div className="box mb-5">
<h2 className="title is-5 has-text-primary pb-2" style={{ borderBottom: '1px solid #eee' }}>Contact Information</h2>
<div className="columns">
<div className="column is-half">
<div className="field">
<label className="label">Support Email</label>
<div className="control">
<input
className="input"
type="email"
value={supportEmail}
onChange={e => setSupportEmail(e.target.value)}
placeholder="support@example.com"
/>
</div>
</div>
</div>
<div className="column is-half">
<div className="field">
<label className="label">Support Phone</label>
<div className="control">
<input
className="input"
type="tel"
value={supportPhone}
onChange={e => setSupportPhone(e.target.value)}
placeholder="+91 XXXXX XXXXX"
/>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="box mb-5">
<h2 className="title is-5 has-text-primary border-bottom pb-2" style={{ borderBottom: '1px solid #eee' }}>Business Documents</h2>
<p className="has-text-grey-dark mb-4">Upload any additional documents (like business licenses, identity proof, or craft certifications).</p>
<div className="field">
<div className="control">
<div className="file is-boxed">
<label className="file-label is-flex-direction-column is-align-items-center">
<input className="file-input" type="file" name="business_doc" />
<span className="file-cta is-flex is-flex-direction-column is-align-items-center py-4 px-6 has-background-white" style={{ border: '1px solid var(--bulma-primary)' }}>
<span className="file-icon mb-2 has-text-primary">
<span className="material-symbols-outlined is-size-3">upload_file</span>
<div className="box mb-5">
<h2 className="title is-5 has-text-primary pb-2" style={{ borderBottom: '1px solid #eee' }}>Business Documents</h2>
<p className="has-text-grey-dark mb-4">Upload any additional documents (like business licenses, identity proof, or craft certifications).</p>
<div className="field">
<div className="control">
<div className="file is-boxed">
<label className="file-label is-flex-direction-column is-align-items-center">
<input
className="file-input"
type="file"
name="business_doc"
accept=".pdf,.jpg,.jpeg,.png"
onChange={e => {
if (e.target.files?.[0]) {
uploadDocument(e.target.files[0], 'document');
}
}}
/>
<span className="file-cta is-flex is-flex-direction-column is-align-items-center py-4 px-6 has-background-white" style={{ border: '1px solid var(--bulma-primary)' }}>
<span className="file-icon mb-2 has-text-primary">
<span className="material-symbols-outlined is-size-3">upload_file</span>
</span>
<span className="file-label has-text-primary">
Choose a document
</span>
</span>
<span className="file-label has-text-primary">
Choose a document
</span>
</span>
</label>
</label>
</div>
</div>
</div>
</div>
</div>
<div className="is-flex is-justify-content-flex-end">
<button className="button is-primary is-outlined">Save Changes</button>
</div>
<div className="is-flex is-justify-content-flex-end">
<button
type="submit"
className={`button is-primary is-outlined ${isSaving ? 'is-loading' : ''}`}
disabled={isSaving}
>
Save Changes
</button>
</div>
</form>
</div>
</div>
);