diff --git a/src/admin/components/dashboard/AnalyticsDashboard.css b/src/admin/components/dashboard/AnalyticsDashboard.css new file mode 100644 index 000000000..039c2d1a9 --- /dev/null +++ b/src/admin/components/dashboard/AnalyticsDashboard.css @@ -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; +} diff --git a/src/admin/components/dashboard/AnalyticsDashboard.js b/src/admin/components/dashboard/AnalyticsDashboard.js new file mode 100644 index 000000000..4d835aeae --- /dev/null +++ b/src/admin/components/dashboard/AnalyticsDashboard.js @@ -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 ( +
+
+

+ + { __('Dashboard', 'formipay') } +

+ +
+ + {loading ? ( +
+ +
+ ) : ( + <> +
+
+
+ +
+
+ + { __('Total Orders', 'formipay') } + + + { stats.totalOrders } + +
+
+ +
+
+ +
+
+ + { __('Total Revenue', 'formipay') } + + + { stats.totalRevenue.toLocaleString('en-US', { + style: 'currency', + currency: 'USD', + }) } + +
+
+ +
+
+ +
+
+ + { __('Completed', 'formipay') } + + + { stats.completedOrders } + +
+
+ +
+
+ +
+
+ + { __('Pending', 'formipay') } + + + { stats.pendingOrders } + +
+
+
+ +
+
+

+ { __('Revenue Over Time', 'formipay') } +

+
+

+ { __('Chart placeholder - would use a library like Chart.js or Recharts', 'formipay') } +

+
+
+ +
+

+ { __('Orders by Status', 'formipay') } +

+
+

+ { __('Chart placeholder - would use a library like Chart.js or Recharts', 'formipay') } +

+
+
+
+ + )} +
+ ); +} diff --git a/src/admin/components/orders/OrderDetail.css b/src/admin/components/orders/OrderDetail.css new file mode 100644 index 000000000..5ad3e82da --- /dev/null +++ b/src/admin/components/orders/OrderDetail.css @@ -0,0 +1,165 @@ +.formipay-order-detail { + display: flex; + flex-direction: column; + height: 100%; + background: #f6f7f7; +} + +.formipay-detail-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 20px; + background: #fff; + border-bottom: 1px solid #e0e0e0; + gap: 16px; +} + +.formipay-detail-header h1 { + margin: 0; + font-size: 20px; + font-weight: 600; + flex: 1; +} + +.header-actions { + display: flex; + gap: 8px; +} + +.formipay-detail-content { + display: grid; + grid-template-columns: 2fr 1fr; + gap: 20px; + padding: 20px; + overflow-y: auto; +} + +.formipay-detail-main, +.formipay-detail-sidebar { + display: flex; + flex-direction: column; + gap: 20px; +} + +.formipay-detail-card { + background: #fff; + border: 1px solid #e0e0e0; + border-radius: 4px; + padding: 20px; +} + +.formipay-detail-card h3 { + margin: 0 0 16px; + font-size: 16px; + font-weight: 600; + color: #1e1e1e; +} + +.detail-list { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; +} + +.detail-list > div { + display: flex; + flex-direction: column; +} + +.detail-list dt { + font-size: 12px; + font-weight: 600; + color: #646970; + margin-bottom: 4px; +} + +.detail-list dd { + font-size: 14px; + color: #1e1e1e; + display: flex; + align-items: center; + gap: 8px; +} + +.detail-list dd .components-select-control { + flex: 1; +} + +.items-table { + width: 100%; + border-collapse: collapse; +} + +.items-table th, +.items-table td { + padding: 10px; + text-align: left; + border-bottom: 1px solid #f0f0f1; +} + +.items-table th { + font-size: 12px; + font-weight: 600; + color: #646970; + text-transform: uppercase; +} + +.items-table td { + font-size: 13px; +} + +.items-table small { + display: block; + color: #646970; + font-size: 11px; +} + +.items-table tfoot td { + border-top: 2px solid #1e1e1e; + border-bottom: none; + padding-top: 16px; +} + +.customer-info { + display: grid; + grid-template-columns: 1fr; + gap: 12px; +} + +.customer-info > div { + display: flex; + flex-direction: column; +} + +.customer-info dt { + font-size: 11px; + font-weight: 600; + color: #646970; + margin-bottom: 2px; +} + +.customer-info dd { + font-size: 13px; + color: #1e1e1e; +} + +.no-data { + color: #646970; + font-size: 13px; +} + +.formipay-error, +.formipay-loading { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + gap: 16px; +} + +.formipay-error p { + color: #646970; + margin: 0; +} diff --git a/src/admin/components/orders/OrderDetail.js b/src/admin/components/orders/OrderDetail.js new file mode 100644 index 000000000..df6bda002 --- /dev/null +++ b/src/admin/components/orders/OrderDetail.js @@ -0,0 +1,251 @@ +/** + * Order Detail - Complete order information view + */ + +import { __ } from '@wordpress/i18n'; +import { useState, useCallback, useEffect } from '@wordpress/element'; +import { Button, SelectControl } from '@wordpress/components'; +import { Icon, arrowLeft, trash } from '@wordpress/icons'; +import { ordersApi } from '../../api/client'; +import OrderTimeline from './OrderTimeline'; +import './OrderDetail.css'; + +const STATUS_OPTIONS = [ + { value: 'on-hold', label: __('On Hold', 'formipay') }, + { value: 'payment-confirm', label: __('Payment Confirmed', 'formipay') }, + { value: 'in-progress', label: __('In Progress', 'formipay') }, + { value: 'shipping', label: __('Shipping', 'formipay') }, + { value: 'completed', label: __('Completed', 'formipay') }, + { value: 'failed', label: __('Failed', 'formipay') }, + { value: 'refunded', label: __('Refunded', 'formipay') }, + { value: 'cancelled', label: __('Cancelled', 'formipay') }, +]; + +export default function OrderDetail({ orderId, onBack }) { + const [order, setOrder] = useState(null); + const [loading, setLoading] = useState(true); + const [updating, setUpdating] = useState(false); + const [newStatus, setNewStatus] = useState(''); + + const loadOrder = useCallback(() => { + setLoading(true); + ordersApi.get(orderId) + .then(result => { + if (result.data) { + setOrder(result.data); + setNewStatus(result.data.status); + } + }) + .catch(error => { + console.error('Load order error:', error); + }) + .finally(() => { + setLoading(false); + }); + }, [orderId]); + + useEffect(() => { + loadOrder(); + }, [loadOrder]); + + const handleStatusChange = () => { + if (!newStatus || newStatus === order.status) return; + + setUpdating(true); + ordersApi.updateStatus(orderId, newStatus) + .then(result => { + if (result.success || result.data?.valid) { + loadOrder(); + } + }) + .catch(error => { + console.error('Update status error:', error); + }) + .finally(() => { + setUpdating(false); + }); + }; + + const handleDelete = () => { + if (!confirm(__('Are you sure you want to delete this order?', 'formipay'))) { + return; + } + + ordersApi.delete([orderId]) + .then(result => { + if (result.success) { + onBack?.(); + } + }) + .catch(error => { + console.error('Delete order error:', error); + }); + }; + + if (loading) { + return ( +
+
+ +
+
+ ); + } + + if (!order) { + return ( +
+
+

{ __('Order not found', 'formipay') }

+ +
+
+ ); + } + + const formatDate = (dateString) => { + if (!dateString) return '-'; + return new Date(dateString).toLocaleString(); + }; + + return ( +
+
+ +

+ { __('Order', 'formipay') } #{ order.id } +

+
+ +
+
+ +
+
+
+

{ __('Order Details', 'formipay') }

+
+
+
{ __('Status', 'formipay') }
+
+ + {newStatus !== order.status && ( + + )} +
+
+
+
{ __('Date Created', 'formipay') }
+
{ formatDate(order.created_date) }
+
+
+
{ __('Form ID', 'formipay') }
+
{ order.form_id }
+
+
+
{ __('Payment Gateway', 'formipay') }
+
{ order.payment_gateway || '-' }
+
+
+
+ +
+

{ __('Items', 'formipay') }

+ + + + + + + + + + {order.items?.map((item, index) => ( + + + + + + )) || ( + + + + )} + + + + + + + +
{ __('Item', 'formipay') }{ __('Qty', 'formipay') }{ __('Subtotal', 'formipay') }
+ { item.item } + {item.description && ( + { item.description } + )} + { item.qty || 1 }{ item.subtotal_formatted || item.subtotal }
+ { __('No items', 'formipay') } +
{ __('Total', 'formipay') }{ order.total_formatted || order.total }
+
+
+ +
+
+

{ __('Customer Information', 'formipay') }

+ {order.form_data ? ( +
+ {Object.entries(order.form_data).map(([key, value]) => { + if (['payment', 'payment_gateway', 'coupon_code', 'qty'].includes(key)) { + return null; + } + return ( +
+
{ key.replace(/_/g, ' ') }
+
{ value?.value || value || '-' }
+
+ ); + })} +
+ ) : ( +

{ __('No customer data available', 'formipay') }

+ )} +
+ + +
+
+
+ ); +} diff --git a/src/admin/components/orders/OrderList.css b/src/admin/components/orders/OrderList.css new file mode 100644 index 000000000..c1fd2bdd0 --- /dev/null +++ b/src/admin/components/orders/OrderList.css @@ -0,0 +1,111 @@ +.formipay-order-list { + display: flex; + flex-direction: column; + height: 100%; +} + +.formipay-orders-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 16px 20px; + background: #fff; + border-bottom: 1px solid #e0e0e0; +} + +.formipay-orders-header h2 { + margin: 0; + font-size: 18px; + font-weight: 600; + display: flex; + align-items: center; + gap: 10px; +} + +.formipay-orders-header svg { + fill: #1e1e1e; +} + +.order-count { + font-size: 13px; + color: #646970; +} + +.formipay-orders-filters { + display: flex; + gap: 12px; + padding: 16px 20px; + background: #f6f7f7; + border-bottom: 1px solid #e0e0e0; + flex-wrap: wrap; + align-items: flex-end; +} + +.formipay-orders-filters .components-base-control { + flex: 1; + min-width: 200px; + margin: 0; +} + +.formipay-date-input { + padding: 6px 8px; + font-size: 13px; + border: 1px solid #8c8f94; + border-radius: 2px; + height: 30px; +} + +.formipay-orders-table-wrapper { + flex: 1; + overflow-y: auto; + background: #fff; +} + +.formipay-loading { + display: flex; + align-items: center; + justify-content: center; + padding: 60px 20px; +} + +.formipay-no-results { + display: flex; + align-items: center; + justify-content: center; + padding: 60px 20px; + color: #646970; +} + +.formipay-orders-table { + width: 100%; + border-collapse: collapse; +} + +.formipay-orders-table thead th { + padding: 12px 16px; + text-align: left; + font-size: 13px; + font-weight: 600; + color: #1e1e1e; + border-bottom: 1px solid #e0e0e0; +} + +.formipay-orders-table tbody td { + padding: 12px 16px; + font-size: 13px; + border-bottom: 1px solid #f0f0f1; +} + +.formipay-pagination { + display: flex; + justify-content: center; + align-items: center; + gap: 16px; + padding: 16px; + border-top: 1px solid #e0e0e0; +} + +.pagination-info { + font-size: 13px; + color: #646970; +} diff --git a/src/admin/components/orders/OrderList.js b/src/admin/components/orders/OrderList.js new file mode 100644 index 000000000..8dee7e3e5 --- /dev/null +++ b/src/admin/components/orders/OrderList.js @@ -0,0 +1,196 @@ +/** + * Order List - Main orders table with filters + */ + +import { __ } from '@wordpress/i18n'; +import { useState, useCallback, useEffect } from '@wordpress/element'; +import { SearchControl, SelectControl, Button } from '@wordpress/components'; +import { Icon, list } from '@wordpress/icons'; +import { ordersApi } from '../../api/client'; +import OrderListItem from './OrderListItem'; +import './OrderList.css'; + +export default function OrderList({ onSelectOrder }) { + const [orders, setOrders] = useState([]); + const [loading, setLoading] = useState(true); + const [total, setTotal] = useState(0); + const [filters, setFilters] = useState({ + keyword: '', + status: '', + date_from: '', + date_to: '', + }); + const [pagination, setPagination] = useState({ + limit: 20, + offset: 0, + }); + + const loadOrders = useCallback(() => { + setLoading(true); + ordersApi.list({ + keyword: filters.keyword, + status: filters.status, + date_from: filters.date_from, + date_to: filters.date_to, + limit: pagination.limit, + offset: pagination.offset, + }) + .then(result => { + if (result.data) { + setOrders(result.data.results || []); + setTotal(result.data.total || 0); + } + }) + .catch(error => { + console.error('Load orders error:', error); + }) + .finally(() => { + setLoading(false); + }); + }, [filters, pagination]); + + useEffect(() => { + loadOrders(); + }, [loadOrders]); + + const handleFilterChange = (key, value) => { + setFilters({ ...filters, [key]: value }); + setPagination({ ...pagination, offset: 0 }); + }; + + const handleSearch = (value) => { + handleFilterChange('keyword', value); + }; + + const handlePageChange = (newOffset) => { + setPagination({ ...pagination, offset: newOffset }); + }; + + const statusOptions = { + '': __('All Statuses', 'formipay'), + 'on-hold': __('On Hold', 'formipay'), + 'payment-confirm': __('Payment Confirmed', 'formipay'), + 'in-progress': __('In Progress', 'formipay'), + 'shipping': __('Shipping', 'formipay'), + 'completed': __('Completed', 'formipay'), + 'failed': __('Failed', 'formipay'), + 'refunded': __('Refunded', 'formipay'), + 'cancelled': __('Cancelled', 'formipay'), + }; + + const totalPages = Math.ceil(total / pagination.limit); + const currentPage = Math.floor(pagination.offset / pagination.limit) + 1; + + return ( +
+
+

+ + { __('Orders', 'formipay') } +

+ + { total } { __('orders', 'formipay') } + +
+ +
+ + + ({ value, label }))} + onChange={(value) => handleFilterChange('status', value)} + label={ __('Status', 'formipay')} + /> + + handleFilterChange('date_from', e.target.value)} + className="formipay-date-input" + /> + + handleFilterChange('date_to', e.target.value)} + className="formipay-date-input" + /> + + {(filters.keyword || filters.status || filters.date_from || filters.date_to) && ( + + )} +
+ +
+ {loading ? ( +
+ +
+ ) : orders.length === 0 ? ( +
+

{ __('No orders found', 'formipay') }

+
+ ) : ( + <> + + + + + + + + + + + + + {orders.map(order => ( + onSelectOrder?.(order.id)} + /> + ))} + +
{ __('ID', 'formipay') }{ __('Date', 'formipay') }{ __('Customer', 'formipay') }{ __('Total', 'formipay') }{ __('Status', 'formipay') }{ __('Actions', 'formipay') }
+ + {totalPages > 1 && ( +
+ + + { __('Page', 'formipay') } { currentPage } { __('of', 'formipay') } { totalPages } + + +
+ )} + + )} +
+
+ ); +} diff --git a/src/admin/components/orders/OrderListItem.css b/src/admin/components/orders/OrderListItem.css new file mode 100644 index 000000000..b05a58832 --- /dev/null +++ b/src/admin/components/orders/OrderListItem.css @@ -0,0 +1,27 @@ +.formipay-order-item { + transition: background 0.2s; +} + +.formipay-order-item:hover { + background: #f6f7f7; +} + +.status-badge { + display: inline-block; + padding: 4px 10px; + font-size: 11px; + font-weight: 600; + color: #fff; + border-radius: 12px; + text-transform: uppercase; +} + +.formipay-order-item .button { + display: inline-flex; + align-items: center; + gap: 6px; +} + +.formipay-order-item .button svg { + fill: currentColor; +} diff --git a/src/admin/components/orders/OrderListItem.js b/src/admin/components/orders/OrderListItem.js new file mode 100644 index 000000000..2aee08518 --- /dev/null +++ b/src/admin/components/orders/OrderListItem.js @@ -0,0 +1,87 @@ +/** + * Order List Item - Single row in orders table + */ + +import { __ } from '@wordpress/i18n'; +import { Icon, seen } from '@wordpress/icons'; +import './OrderListItem.css'; + +const STATUS_COLORS = { + 'on-hold': '#f0ad4e', + 'payment-confirm': '#17a2b8', + 'in-progress': '#17a2b8', + 'shipping': '#6c757d', + 'completed': '#28a745', + 'failed': '#dc3545', + 'refunded': '#6c757d', + 'cancelled': '#dc3545', +}; + +const STATUS_LABELS = { + 'on-hold': __('On Hold', 'formipay'), + 'payment-confirm': __('Payment Confirmed', 'formipay'), + 'in-progress': __('In Progress', 'formipay'), + 'shipping': __('Shipping', 'formipay'), + 'completed': __('Completed', 'formipay'), + 'failed': __('Failed', 'formipay'), + 'refunded': __('Refunded', 'formipay'), + 'cancelled': __('Cancelled', 'formipay'), +}; + +export default function OrderListItem({ order, onSelect }) { + const statusColor = STATUS_COLORS[order.status] || '#6c757d'; + const statusLabel = STATUS_LABELS[order.status] || order.status; + + const formatDate = (dateString) => { + if (!dateString) return '-'; + const date = new Date(dateString); + return date.toLocaleDateString(); + }; + + const getCustomerName = (order) => { + if (order.form_data) { + const nameField = Object.values(order.form_data).find( + field => field.name && field.name.includes('name') + ); + return nameField?.value || '-'; + } + return '-'; + }; + + const customerName = getCustomerName(order); + + return ( + + + #{ order.id } + + + { formatDate(order.created_date) } + + + { customerName !== '-' ? customerName : Unknown } + + + { order.total_formatted || order.total } + + + + { statusLabel } + + + + + + + ); +} diff --git a/src/admin/components/orders/OrderTimeline.css b/src/admin/components/orders/OrderTimeline.css new file mode 100644 index 000000000..26b6d2270 --- /dev/null +++ b/src/admin/components/orders/OrderTimeline.css @@ -0,0 +1,116 @@ +.formipay-order-timeline h3 { + margin: 0 0 16px; + font-size: 16px; + font-weight: 600; + color: #1e1e1e; +} + +.timeline-progress { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 24px; + position: relative; +} + +.timeline-step { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + position: relative; + z-index: 1; + flex: 1; +} + +.timeline-dot { + width: 24px; + height: 24px; + background: #e0e0e0; + border: 2px solid #c3c4c7; + border-radius: 50%; + position: relative; +} + +.timeline-step.completed .timeline-dot { + background: #2271b1; + border-color: #2271b1; +} + +.timeline-step.completed .timeline-dot::after { + content: ''; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 8px; + height: 8px; + background: #fff; + border-radius: 50%; +} + +.timeline-line { + position: absolute; + top: 12px; + left: 50%; + width: 100%; + height: 2px; + background: #e0e0e0; + z-index: -1; +} + +.timeline-step.completed .timeline-line { + background: #2271b1; +} + +.timeline-label { + font-size: 10px; + font-weight: 600; + color: #646970; + text-align: center; + text-transform: uppercase; +} + +.timeline-events ul { + list-style: none; + margin: 0; + padding: 0; +} + +.timeline-events li { + display: grid; + grid-template-columns: 1fr auto; + gap: 8px; + padding: 12px 0; + border-bottom: 1px solid #f0f0f1; +} + +.timeline-events li:last-child { + border-bottom: none; +} + +.event-status { + font-size: 13px; + font-weight: 600; + color: #1e1e1e; +} + +.event-date { + font-size: 11px; + color: #646970; + text-align: right; +} + +.event-note { + grid-column: 1 / -1; + font-size: 12px; + color: #646970; + margin-top: 4px; +} + +.no-events { + color: #646970; + font-size: 13px; + text-align: center; + padding: 20px 0; +} diff --git a/src/admin/components/orders/OrderTimeline.js b/src/admin/components/orders/OrderTimeline.js new file mode 100644 index 000000000..580788e23 --- /dev/null +++ b/src/admin/components/orders/OrderTimeline.js @@ -0,0 +1,92 @@ +/** + * Order Timeline - Status change history + */ + +import { __ } from '@wordpress/i18n'; +import { useState, useEffect } from '@wordpress/element'; +import './OrderTimeline.css'; + +const STATUS_FLOW = [ + 'on-hold', + 'payment-confirm', + 'in-progress', + 'shipping', + 'completed', +]; + +const STATUS_LABELS = { + 'on-hold': __('On Hold', 'formipay'), + 'payment-confirm': __('Payment Confirmed', 'formipay'), + 'in-progress': __('In Progress', 'formipay'), + 'shipping': __('Shipping', 'formipay'), + 'completed': __('Completed', 'formipay'), + 'failed': __('Failed', 'formipay'), + 'refunded': __('Refunded', 'formipay'), + 'cancelled': __('Cancelled', 'formipay'), +}; + +export default function OrderTimeline({ orderId }) { + const [timeline, setTimeline] = useState([]); + + useEffect(() => { + // Load timeline data - this would be from an API + // For now, we'll create a mock timeline based on status + const mockTimeline = [ + { + status: 'on-hold', + date: new Date().toISOString(), + note: __('Order placed', 'formipay'), + }, + ]; + setTimeline(mockTimeline); + }, [orderId]); + + return ( +
+

{ __('Order Timeline', 'formipay') }

+ +
+ {STATUS_FLOW.map((status, index) => ( +
+
+ {index < STATUS_FLOW.length - 1 && ( +
+ )} + + { STATUS_LABELS[status] } + +
+ ))} +
+ +
+ {timeline.length === 0 ? ( +

+ { __('No timeline events yet', 'formipay') } +

+ ) : ( +
    + {timeline.map((event, index) => ( +
  • + + { STATUS_LABELS[event.status] || event.status } + + + { new Date(event.date).toLocaleString() } + + {event.note && ( + + { event.note } + + )} +
  • + ))} +
+ )} +
+
+ ); +} diff --git a/src/admin/pages/Orders.js b/src/admin/pages/Orders.js index 77b5eccfc..3cac5211d 100644 --- a/src/admin/pages/Orders.js +++ b/src/admin/pages/Orders.js @@ -1,14 +1,27 @@ /** - * Orders Page - Placeholder + * Orders Page - List and detail view */ import { __ } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; +import OrderList from '../components/orders/OrderList'; +import OrderDetail from '../components/orders/OrderDetail'; export default function OrdersPage({ initialData }) { + const [selectedOrderId, setSelectedOrderId] = useState(null); + + if (selectedOrderId) { + return ( + setSelectedOrderId(null)} + /> + ); + } + return ( -
-

{ __('Orders', 'formipay') }

-

{ __('Page content coming soon...', 'formipay') }

-
+ setSelectedOrderId(orderId)} + /> ); }