feat: build Order Management & Analytics (F2.13-F2.16)
Components: - OrderList: table with filters (status, date, search, pagination) - OrderListItem: single order row with status badge - OrderDetail: full order view with status update, items, customer info - OrderTimeline: status change history with visual progress - AnalyticsDashboard: stats cards (orders, revenue, completed, pending) Features: - Order list with keyword search, status filter, date range - Pagination support - Status change workflow with immediate update - Order detail with items breakdown and customer info - Visual timeline progress indicator - Analytics dashboard with key metrics - Placeholder charts for future implementation Updated Orders page to use new components with list/detail navigation
This commit is contained in:
128
src/admin/components/dashboard/AnalyticsDashboard.css
Normal file
128
src/admin/components/dashboard/AnalyticsDashboard.css
Normal file
@@ -0,0 +1,128 @@
|
||||
.formipay-analytics-dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.formipay-dashboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.formipay-dashboard-header h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.formipay-dashboard-header svg {
|
||||
fill: #1e1e1e;
|
||||
}
|
||||
|
||||
.formipay-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
background: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f0f0f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.stat-icon svg {
|
||||
fill: #1e1e1e;
|
||||
}
|
||||
|
||||
.stat-icon.completed {
|
||||
background: #e7f7ed;
|
||||
}
|
||||
|
||||
.stat-icon.completed span {
|
||||
font-size: 24px;
|
||||
color: #28a745;
|
||||
}
|
||||
|
||||
.stat-icon.pending {
|
||||
background: #fff8e5;
|
||||
}
|
||||
|
||||
.stat-icon.pending span {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #646970;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
color: #1e1e1e;
|
||||
}
|
||||
|
||||
.formipay-dashboard-charts {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.chart-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.chart-card h3 {
|
||||
margin: 0 0 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1e1e1e;
|
||||
}
|
||||
|
||||
.chart-placeholder {
|
||||
height: 200px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #f6f7f7;
|
||||
border: 1px dashed #c3c4c7;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.chart-placeholder p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: #646970;
|
||||
text-align: center;
|
||||
}
|
||||
170
src/admin/components/dashboard/AnalyticsDashboard.js
Normal file
170
src/admin/components/dashboard/AnalyticsDashboard.js
Normal file
@@ -0,0 +1,170 @@
|
||||
/**
|
||||
* Analytics Dashboard - Order statistics and charts
|
||||
*/
|
||||
|
||||
import { __ } from '@wordpress/i18n';
|
||||
import { useState, useCallback, useEffect } from '@wordpress/element';
|
||||
import { SelectControl } from '@wordpress/components';
|
||||
import { Icon, chartLine, money, shoppingCart } from '@wordpress/icons';
|
||||
import { ordersApi } from '../../api/client';
|
||||
import './AnalyticsDashboard.css';
|
||||
|
||||
export default function AnalyticsDashboard() {
|
||||
const [stats, setStats] = useState({
|
||||
totalOrders: 0,
|
||||
totalRevenue: 0,
|
||||
completedOrders: 0,
|
||||
pendingOrders: 0,
|
||||
});
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [dateRange, setDateRange] = useState('30');
|
||||
|
||||
const loadStats = useCallback(() => {
|
||||
setLoading(true);
|
||||
// This would be a dedicated stats API endpoint
|
||||
// For now, we'll fetch orders and calculate
|
||||
ordersApi.list({ limit: 1000 })
|
||||
.then(result => {
|
||||
if (result.data) {
|
||||
const orders = result.data.results || [];
|
||||
const totalRevenue = orders.reduce((sum, order) => {
|
||||
if (order.status === 'completed') {
|
||||
return sum + parseFloat(order.total || 0);
|
||||
}
|
||||
return sum;
|
||||
}, 0);
|
||||
|
||||
setStats({
|
||||
totalOrders: orders.length,
|
||||
totalRevenue: totalRevenue,
|
||||
completedOrders: orders.filter(o => o.status === 'completed').length,
|
||||
pendingOrders: orders.filter(o => ['on-hold', 'payment-confirm', 'in-progress'].includes(o.status)).length,
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Load stats error:', error);
|
||||
})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
}, [dateRange]);
|
||||
|
||||
useEffect(() => {
|
||||
loadStats();
|
||||
}, [loadStats]);
|
||||
|
||||
return (
|
||||
<div className="formipay-analytics-dashboard">
|
||||
<div className="formipay-dashboard-header">
|
||||
<h2>
|
||||
<Icon icon={chartLine} />
|
||||
{ __('Dashboard', 'formipay') }
|
||||
</h2>
|
||||
<SelectControl
|
||||
value={dateRange}
|
||||
options={[
|
||||
{ value: '7', label: __('Last 7 days', 'formipay') },
|
||||
{ value: '30', label: __('Last 30 days', 'formipay') },
|
||||
{ value: '90', label: __('Last 90 days', 'formipay') },
|
||||
{ value: '365', label: __('Last year', 'formipay') },
|
||||
]}
|
||||
onChange={setDateRange}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{loading ? (
|
||||
<div className="formipay-loading">
|
||||
<span className="spinner is-active" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="formipay-stats-grid">
|
||||
<div className="stat-card">
|
||||
<div className="stat-icon">
|
||||
<Icon icon={shoppingCart} size={24} />
|
||||
</div>
|
||||
<div className="stat-content">
|
||||
<span className="stat-label">
|
||||
{ __('Total Orders', 'formipay') }
|
||||
</span>
|
||||
<span className="stat-value">
|
||||
{ stats.totalOrders }
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat-card">
|
||||
<div className="stat-icon">
|
||||
<Icon icon={money} size={24} />
|
||||
</div>
|
||||
<div className="stat-content">
|
||||
<span className="stat-label">
|
||||
{ __('Total Revenue', 'formipay') }
|
||||
</span>
|
||||
<span className="stat-value">
|
||||
{ stats.totalRevenue.toLocaleString('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
}) }
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat-card">
|
||||
<div className="stat-icon completed">
|
||||
<span>✓</span>
|
||||
</div>
|
||||
<div className="stat-content">
|
||||
<span className="stat-label">
|
||||
{ __('Completed', 'formipay') }
|
||||
</span>
|
||||
<span className="stat-value">
|
||||
{ stats.completedOrders }
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="stat-card">
|
||||
<div className="stat-icon pending">
|
||||
<span>⏱</span>
|
||||
</div>
|
||||
<div className="stat-content">
|
||||
<span className="stat-label">
|
||||
{ __('Pending', 'formipay') }
|
||||
</span>
|
||||
<span className="stat-value">
|
||||
{ stats.pendingOrders }
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="formipay-dashboard-charts">
|
||||
<div className="chart-card">
|
||||
<h3>
|
||||
{ __('Revenue Over Time', 'formipay') }
|
||||
</h3>
|
||||
<div className="chart-placeholder">
|
||||
<p>
|
||||
{ __('Chart placeholder - would use a library like Chart.js or Recharts', 'formipay') }
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="chart-card">
|
||||
<h3>
|
||||
{ __('Orders by Status', 'formipay') }
|
||||
</h3>
|
||||
<div className="chart-placeholder">
|
||||
<p>
|
||||
{ __('Chart placeholder - would use a library like Chart.js or Recharts', 'formipay') }
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user