diff --git a/app/api/v1/admin/orders.py b/app/api/v1/admin/orders.py
new file mode 100644
index 00000000..9401a7f5
--- /dev/null
+++ b/app/api/v1/admin/orders.py
@@ -0,0 +1,138 @@
+# app/api/v1/admin/orders.py
+"""
+Admin order management endpoints.
+
+Provides order management capabilities for administrators:
+- View orders across all vendors
+- View vendor-specific orders
+- Update order status on behalf of vendors
+- Order statistics and reporting
+
+Admin Context: Uses admin JWT authentication.
+Vendor selection is passed as a request parameter.
+"""
+
+import logging
+
+from fastapi import APIRouter, Depends, Query
+from sqlalchemy.orm import Session
+
+from app.api.deps import get_current_admin_api
+from app.core.database import get_db
+from app.services.order_service import order_service
+from models.database.user import User
+from models.schema.order import (
+ AdminOrderItem,
+ AdminOrderListResponse,
+ AdminOrderStats,
+ AdminOrderStatusUpdate,
+ AdminVendorsWithOrdersResponse,
+ OrderDetailResponse,
+)
+
+router = APIRouter(prefix="/orders")
+logger = logging.getLogger(__name__)
+
+
+# ============================================================================
+# List & Statistics Endpoints
+# ============================================================================
+
+
+@router.get("", response_model=AdminOrderListResponse)
+def get_all_orders(
+ skip: int = Query(0, ge=0),
+ limit: int = Query(50, ge=1, le=500),
+ vendor_id: int | None = Query(None, description="Filter by vendor"),
+ status: str | None = Query(None, description="Filter by status"),
+ channel: str | None = Query(None, description="Filter by channel"),
+ search: str | None = Query(None, description="Search by order number or customer"),
+ db: Session = Depends(get_db),
+ current_admin: User = Depends(get_current_admin_api),
+):
+ """
+ Get orders across all vendors with filtering.
+
+ Allows admins to view and filter orders across the platform.
+ """
+ orders, total = order_service.get_all_orders_admin(
+ db=db,
+ skip=skip,
+ limit=limit,
+ vendor_id=vendor_id,
+ status=status,
+ channel=channel,
+ search=search,
+ )
+
+ return AdminOrderListResponse(
+ orders=[AdminOrderItem(**order) for order in orders],
+ total=total,
+ skip=skip,
+ limit=limit,
+ )
+
+
+@router.get("/stats", response_model=AdminOrderStats)
+def get_order_stats(
+ db: Session = Depends(get_db),
+ current_admin: User = Depends(get_current_admin_api),
+):
+ """Get platform-wide order statistics."""
+ return order_service.get_order_stats_admin(db)
+
+
+@router.get("/vendors", response_model=AdminVendorsWithOrdersResponse)
+def get_vendors_with_orders(
+ db: Session = Depends(get_db),
+ current_admin: User = Depends(get_current_admin_api),
+):
+ """Get list of vendors that have orders."""
+ vendors = order_service.get_vendors_with_orders_admin(db)
+ return AdminVendorsWithOrdersResponse(vendors=vendors)
+
+
+# ============================================================================
+# Order Detail & Update Endpoints
+# ============================================================================
+
+
+@router.get("/{order_id}", response_model=OrderDetailResponse)
+def get_order_detail(
+ order_id: int,
+ db: Session = Depends(get_db),
+ current_admin: User = Depends(get_current_admin_api),
+):
+ """Get order details including items and addresses."""
+ order = order_service.get_order_by_id_admin(db, order_id)
+ return order
+
+
+@router.patch("/{order_id}/status", response_model=OrderDetailResponse)
+def update_order_status(
+ order_id: int,
+ status_update: AdminOrderStatusUpdate,
+ db: Session = Depends(get_db),
+ current_admin: User = Depends(get_current_admin_api),
+):
+ """
+ Update order status.
+
+ Admin can update status and add tracking number.
+ Status changes are logged with optional reason.
+ """
+ order = order_service.update_order_status_admin(
+ db=db,
+ order_id=order_id,
+ status=status_update.status,
+ tracking_number=status_update.tracking_number,
+ reason=status_update.reason,
+ )
+
+ logger.info(
+ f"Admin {current_admin.email} updated order {order.order_number} "
+ f"status to {status_update.status}"
+ )
+
+ db.commit()
+ return order
diff --git a/app/services/order_service.py b/app/services/order_service.py
index 66bf9103..05988ae5 100644
--- a/app/services/order_service.py
+++ b/app/services/order_service.py
@@ -366,5 +366,251 @@ class OrderService:
raise ValidationException(f"Failed to update order: {str(e)}")
+ # =========================================================================
+ # Admin Methods (cross-vendor)
+ # =========================================================================
+
+ def get_all_orders_admin(
+ self,
+ db: Session,
+ skip: int = 0,
+ limit: int = 50,
+ vendor_id: int | None = None,
+ status: str | None = None,
+ channel: str | None = None,
+ search: str | None = None,
+ ) -> tuple[list[dict], int]:
+ """
+ Get orders across all vendors for admin.
+
+ Args:
+ db: Database session
+ skip: Pagination offset
+ limit: Pagination limit
+ vendor_id: Filter by vendor
+ status: Filter by status
+ channel: Filter by channel
+ search: Search by order number or customer
+
+ Returns:
+ Tuple of (orders with vendor info, total_count)
+ """
+ from models.database.customer import Customer
+ from models.database.vendor import Vendor
+
+ query = db.query(Order).join(Vendor).join(Customer)
+
+ if vendor_id:
+ query = query.filter(Order.vendor_id == vendor_id)
+
+ if status:
+ query = query.filter(Order.status == status)
+
+ if channel:
+ query = query.filter(Order.channel == channel)
+
+ if search:
+ search_term = f"%{search}%"
+ query = query.filter(
+ (Order.order_number.ilike(search_term))
+ | (Customer.email.ilike(search_term))
+ | (Customer.first_name.ilike(search_term))
+ | (Customer.last_name.ilike(search_term))
+ )
+
+ # Order by most recent first
+ query = query.order_by(Order.created_at.desc())
+
+ total = query.count()
+ orders = query.offset(skip).limit(limit).all()
+
+ # Build response with vendor/customer info
+ result = []
+ for order in orders:
+ item_count = len(order.items) if order.items else 0
+ customer_name = None
+ customer_email = None
+
+ if order.customer:
+ customer_name = (
+ f"{order.customer.first_name} {order.customer.last_name}".strip()
+ )
+ customer_email = order.customer.email
+
+ result.append(
+ {
+ "id": order.id,
+ "vendor_id": order.vendor_id,
+ "vendor_name": order.vendor.name if order.vendor else None,
+ "vendor_code": order.vendor.vendor_code if order.vendor else None,
+ "customer_id": order.customer_id,
+ "customer_name": customer_name,
+ "customer_email": customer_email,
+ "order_number": order.order_number,
+ "channel": order.channel,
+ "status": order.status,
+ "subtotal": order.subtotal,
+ "tax_amount": order.tax_amount,
+ "shipping_amount": order.shipping_amount,
+ "discount_amount": order.discount_amount,
+ "total_amount": order.total_amount,
+ "currency": order.currency,
+ "shipping_method": order.shipping_method,
+ "tracking_number": order.tracking_number,
+ "item_count": item_count,
+ "created_at": order.created_at,
+ "updated_at": order.updated_at,
+ "paid_at": order.paid_at,
+ "shipped_at": order.shipped_at,
+ "delivered_at": order.delivered_at,
+ "cancelled_at": order.cancelled_at,
+ }
+ )
+
+ return result, total
+
+ def get_order_stats_admin(self, db: Session) -> dict:
+ """Get platform-wide order statistics."""
+ from sqlalchemy import func
+
+ from models.database.vendor import Vendor
+
+ # Get status counts
+ status_counts = (
+ db.query(Order.status, func.count(Order.id)).group_by(Order.status).all()
+ )
+
+ stats = {
+ "total_orders": 0,
+ "pending_orders": 0,
+ "processing_orders": 0,
+ "shipped_orders": 0,
+ "delivered_orders": 0,
+ "cancelled_orders": 0,
+ "refunded_orders": 0,
+ "total_revenue": 0.0,
+ "vendors_with_orders": 0,
+ }
+
+ for status, count in status_counts:
+ stats["total_orders"] += count
+ key = f"{status}_orders"
+ if key in stats:
+ stats[key] = count
+
+ # Get total revenue (from delivered orders only)
+ revenue = (
+ db.query(func.sum(Order.total_amount))
+ .filter(Order.status == "delivered")
+ .scalar()
+ )
+ stats["total_revenue"] = float(revenue) if revenue else 0.0
+
+ # Count vendors with orders
+ vendors_count = (
+ db.query(func.count(func.distinct(Order.vendor_id))).scalar() or 0
+ )
+ stats["vendors_with_orders"] = vendors_count
+
+ return stats
+
+ def get_vendors_with_orders_admin(self, db: Session) -> list[dict]:
+ """Get list of vendors that have orders."""
+ from sqlalchemy import func
+
+ from models.database.vendor import Vendor
+
+ results = (
+ db.query(
+ Vendor.id,
+ Vendor.name,
+ Vendor.vendor_code,
+ func.count(Order.id).label("order_count"),
+ )
+ .join(Order)
+ .group_by(Vendor.id, Vendor.name, Vendor.vendor_code)
+ .order_by(Vendor.name)
+ .all()
+ )
+
+ return [
+ {
+ "id": r.id,
+ "name": r.name,
+ "vendor_code": r.vendor_code,
+ "order_count": r.order_count,
+ }
+ for r in results
+ ]
+
+ def get_order_by_id_admin(self, db: Session, order_id: int) -> Order:
+ """Get order by ID without vendor scope (admin only)."""
+ order = db.query(Order).filter(Order.id == order_id).first()
+
+ if not order:
+ raise OrderNotFoundException(str(order_id))
+
+ return order
+
+ def update_order_status_admin(
+ self,
+ db: Session,
+ order_id: int,
+ status: str,
+ tracking_number: str | None = None,
+ reason: str | None = None,
+ ) -> Order:
+ """
+ Update order status as admin.
+
+ Args:
+ db: Database session
+ order_id: Order ID
+ status: New status
+ tracking_number: Optional tracking number
+ reason: Optional reason for change
+
+ Returns:
+ Updated Order object
+ """
+ order = self.get_order_by_id_admin(db, order_id)
+
+ # Update status with timestamps
+ old_status = order.status
+ order.status = status
+
+ now = datetime.now(UTC)
+ if status == "shipped" and not order.shipped_at:
+ order.shipped_at = now
+ elif status == "delivered" and not order.delivered_at:
+ order.delivered_at = now
+ elif status == "cancelled" and not order.cancelled_at:
+ order.cancelled_at = now
+
+ # Update tracking number
+ if tracking_number:
+ order.tracking_number = tracking_number
+
+ # Add reason to internal notes if provided
+ if reason:
+ note = f"[{now.isoformat()}] Status changed from {old_status} to {status}: {reason}"
+ if order.internal_notes:
+ order.internal_notes = f"{order.internal_notes}\n{note}"
+ else:
+ order.internal_notes = note
+
+ order.updated_at = now
+ db.flush()
+ db.refresh(order)
+
+ logger.info(
+ f"Admin updated order {order.order_number}: "
+ f"{old_status} -> {status}"
+ f"{f' (reason: {reason})' if reason else ''}"
+ )
+
+ return order
+
+
# Create service instance
order_service = OrderService()
diff --git a/app/templates/admin/orders.html b/app/templates/admin/orders.html
new file mode 100644
index 00000000..7b951588
--- /dev/null
+++ b/app/templates/admin/orders.html
@@ -0,0 +1,440 @@
+{# app/templates/admin/orders.html #}
+{% extends "admin/base.html" %}
+{% from 'shared/macros/pagination.html' import pagination %}
+{% from 'shared/macros/headers.html' import page_header %}
+{% from 'shared/macros/alerts.html' import loading_state, error_state %}
+{% from 'shared/macros/tables.html' import table_wrapper %}
+{% from 'shared/macros/modals.html' import modal_simple %}
+{% from 'shared/macros/inputs.html' import vendor_selector %}
+
+{% block title %}Orders{% endblock %}
+
+{% block alpine_data %}adminOrders(){% endblock %}
+
+{% block content %}
+{{ page_header('Orders', subtitle='Manage orders across all vendors') }}
+
+{{ loading_state('Loading orders...') }}
+
+{{ error_state('Error loading orders') }}
+
+
+
+
+
+
+
+
+
+
+ Total Orders
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Processing
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+ Total Revenue
+
+
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ vendor_selector(
+ ref_name='vendorSelect',
+ id='orders-vendor-select',
+ placeholder='Filter by vendor...',
+ width='w-64'
+ ) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% call table_wrapper() %}
+
+
+ | Order |
+ Customer |
+ Vendor |
+ Channel |
+ Total |
+ Status |
+ Date |
+ Actions |
+
+
+
+
+
+
+ |
+
+ |
+
+
+
+
+
+
+
+ |
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+ |
+
+
+
+ {% endcall %}
+
+ {{ pagination(show_condition="!loading && pagination.total > 0") }}
+
+
+
+{% call modal_simple('updateStatusModal', 'Update Order Status', show_var='showStatusModal', size='sm') %}
+
+
+
Order:
+
Current Status:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endcall %}
+
+
+{% call modal_simple('orderDetailModal', 'Order Details', show_var='showDetailModal', size='lg') %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Subtotal
+
+
+
+ Tax
+
+
+
+ Shipping
+
+
+
+
+ Discount
+
+
+
+
+ Total
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endcall %}
+{% endblock %}
+
+{% block extra_scripts %}
+
+{% endblock %}
diff --git a/models/schema/order.py b/models/schema/order.py
index f64db5b2..b28bc87c 100644
--- a/models/schema/order.py
+++ b/models/schema/order.py
@@ -162,3 +162,96 @@ class OrderListResponse(BaseModel):
total: int
skip: int
limit: int
+
+
+# ============================================================================
+# Admin Order Schemas
+# ============================================================================
+
+
+class AdminOrderItem(BaseModel):
+ """Order item with vendor info for admin list view."""
+
+ model_config = ConfigDict(from_attributes=True)
+
+ id: int
+ vendor_id: int
+ vendor_name: str | None = None
+ vendor_code: str | None = None
+ customer_id: int
+ customer_name: str | None = None
+ customer_email: str | None = None
+ order_number: str
+ channel: str
+ status: str
+
+ # Financial
+ subtotal: float
+ tax_amount: float
+ shipping_amount: float
+ discount_amount: float
+ total_amount: float
+ currency: str
+
+ # Shipping
+ shipping_method: str | None
+ tracking_number: str | None
+
+ # Item count
+ item_count: int = 0
+
+ # Timestamps
+ created_at: datetime
+ updated_at: datetime
+ paid_at: datetime | None
+ shipped_at: datetime | None
+ delivered_at: datetime | None
+ cancelled_at: datetime | None
+
+
+class AdminOrderListResponse(BaseModel):
+ """Cross-vendor order list for admin."""
+
+ orders: list[AdminOrderItem]
+ total: int
+ skip: int
+ limit: int
+
+
+class AdminOrderStats(BaseModel):
+ """Order statistics for admin dashboard."""
+
+ total_orders: int
+ pending_orders: int
+ processing_orders: int
+ shipped_orders: int
+ delivered_orders: int
+ cancelled_orders: int
+ refunded_orders: int
+ total_revenue: float
+ vendors_with_orders: int
+
+
+class AdminOrderStatusUpdate(BaseModel):
+ """Admin version of status update with reason."""
+
+ status: str = Field(
+ ..., pattern="^(pending|processing|shipped|delivered|cancelled|refunded)$"
+ )
+ tracking_number: str | None = None
+ reason: str | None = Field(None, description="Reason for status change")
+
+
+class AdminVendorWithOrders(BaseModel):
+ """Vendor with order count."""
+
+ id: int
+ name: str
+ vendor_code: str
+ order_count: int = 0
+
+
+class AdminVendorsWithOrdersResponse(BaseModel):
+ """Response for vendors with orders list."""
+
+ vendors: list[AdminVendorWithOrders]
diff --git a/static/admin/js/orders.js b/static/admin/js/orders.js
new file mode 100644
index 00000000..8056bc9e
--- /dev/null
+++ b/static/admin/js/orders.js
@@ -0,0 +1,392 @@
+// static/admin/js/orders.js
+/**
+ * Admin orders management page logic
+ * View and manage orders across all vendors
+ */
+
+const adminOrdersLog = window.LogConfig.loggers.adminOrders ||
+ window.LogConfig.createLogger('adminOrders', false);
+
+adminOrdersLog.info('Loading...');
+
+function adminOrders() {
+ adminOrdersLog.info('adminOrders() called');
+
+ return {
+ // Inherit base layout state
+ ...data(),
+
+ // Set page identifier
+ currentPage: 'orders',
+
+ // Loading states
+ loading: true,
+ error: '',
+ saving: false,
+
+ // Orders data
+ orders: [],
+ stats: {
+ total_orders: 0,
+ pending_orders: 0,
+ processing_orders: 0,
+ shipped_orders: 0,
+ delivered_orders: 0,
+ cancelled_orders: 0,
+ refunded_orders: 0,
+ total_revenue: 0,
+ vendors_with_orders: 0
+ },
+
+ // Filters
+ filters: {
+ search: '',
+ vendor_id: '',
+ status: '',
+ channel: ''
+ },
+
+ // Available vendors for filter dropdown
+ vendors: [],
+
+ // Pagination
+ pagination: {
+ page: 1,
+ per_page: 50,
+ total: 0,
+ pages: 0
+ },
+
+ // Modal states
+ showStatusModal: false,
+ showDetailModal: false,
+ selectedOrder: null,
+ selectedOrderDetail: null,
+
+ // Status update form
+ statusForm: {
+ status: '',
+ tracking_number: '',
+ reason: ''
+ },
+
+ // Debounce timer
+ searchTimeout: null,
+
+ // Computed: Total pages
+ get totalPages() {
+ return this.pagination.pages;
+ },
+
+ // Computed: Start index for pagination display
+ get startIndex() {
+ if (this.pagination.total === 0) return 0;
+ return (this.pagination.page - 1) * this.pagination.per_page + 1;
+ },
+
+ // Computed: End index for pagination display
+ get endIndex() {
+ const end = this.pagination.page * this.pagination.per_page;
+ return end > this.pagination.total ? this.pagination.total : end;
+ },
+
+ // Computed: Page numbers for pagination
+ get pageNumbers() {
+ const pages = [];
+ const totalPages = this.totalPages;
+ const current = this.pagination.page;
+
+ if (totalPages <= 7) {
+ for (let i = 1; i <= totalPages; i++) {
+ pages.push(i);
+ }
+ } else {
+ pages.push(1);
+ if (current > 3) {
+ pages.push('...');
+ }
+ const start = Math.max(2, current - 1);
+ const end = Math.min(totalPages - 1, current + 1);
+ for (let i = start; i <= end; i++) {
+ pages.push(i);
+ }
+ if (current < totalPages - 2) {
+ pages.push('...');
+ }
+ pages.push(totalPages);
+ }
+ return pages;
+ },
+
+ async init() {
+ adminOrdersLog.info('Orders init() called');
+
+ // Guard against multiple initialization
+ if (window._adminOrdersInitialized) {
+ adminOrdersLog.warn('Already initialized, skipping');
+ return;
+ }
+ window._adminOrdersInitialized = true;
+
+ // Load data in parallel
+ await Promise.all([
+ this.loadStats(),
+ this.loadVendors(),
+ this.loadOrders()
+ ]);
+
+ adminOrdersLog.info('Orders initialization complete');
+ },
+
+ /**
+ * Load order statistics
+ */
+ async loadStats() {
+ try {
+ const response = await apiClient.get('/admin/orders/stats');
+ this.stats = response;
+ adminOrdersLog.info('Loaded stats:', this.stats);
+ } catch (error) {
+ adminOrdersLog.error('Failed to load stats:', error);
+ }
+ },
+
+ /**
+ * Load available vendors for filter
+ */
+ async loadVendors() {
+ try {
+ const response = await apiClient.get('/admin/orders/vendors');
+ this.vendors = response.vendors || [];
+ adminOrdersLog.info('Loaded vendors:', this.vendors.length);
+ } catch (error) {
+ adminOrdersLog.error('Failed to load vendors:', error);
+ }
+ },
+
+ /**
+ * Load orders with filtering and pagination
+ */
+ async loadOrders() {
+ this.loading = true;
+ this.error = '';
+
+ try {
+ const params = new URLSearchParams({
+ skip: (this.pagination.page - 1) * this.pagination.per_page,
+ limit: this.pagination.per_page
+ });
+
+ // Add filters
+ if (this.filters.search) {
+ params.append('search', this.filters.search);
+ }
+ if (this.filters.vendor_id) {
+ params.append('vendor_id', this.filters.vendor_id);
+ }
+ if (this.filters.status) {
+ params.append('status', this.filters.status);
+ }
+ if (this.filters.channel) {
+ params.append('channel', this.filters.channel);
+ }
+
+ const response = await apiClient.get(`/admin/orders?${params.toString()}`);
+
+ this.orders = response.orders || [];
+ this.pagination.total = response.total || 0;
+ this.pagination.pages = Math.ceil(this.pagination.total / this.pagination.per_page);
+
+ adminOrdersLog.info('Loaded orders:', this.orders.length, 'of', this.pagination.total);
+ } catch (error) {
+ adminOrdersLog.error('Failed to load orders:', error);
+ this.error = error.message || 'Failed to load orders';
+ } finally {
+ this.loading = false;
+ }
+ },
+
+ /**
+ * Debounced search handler
+ */
+ debouncedSearch() {
+ clearTimeout(this.searchTimeout);
+ this.searchTimeout = setTimeout(() => {
+ this.pagination.page = 1;
+ this.loadOrders();
+ }, 300);
+ },
+
+ /**
+ * Refresh orders list
+ */
+ async refresh() {
+ await Promise.all([
+ this.loadStats(),
+ this.loadVendors(),
+ this.loadOrders()
+ ]);
+ },
+
+ /**
+ * View order details
+ */
+ async viewOrder(order) {
+ try {
+ const response = await apiClient.get(`/admin/orders/${order.id}`);
+ this.selectedOrderDetail = response;
+ this.showDetailModal = true;
+ } catch (error) {
+ adminOrdersLog.error('Failed to load order details:', error);
+ Utils.showToast('Failed to load order details.', 'error');
+ }
+ },
+
+ /**
+ * Open status update modal
+ */
+ openStatusModal(order) {
+ this.selectedOrder = order;
+ this.statusForm = {
+ status: order.status,
+ tracking_number: order.tracking_number || '',
+ reason: ''
+ };
+ this.showStatusModal = true;
+ },
+
+ /**
+ * Update order status
+ */
+ async updateStatus() {
+ if (!this.selectedOrder || this.statusForm.status === this.selectedOrder.status) return;
+
+ this.saving = true;
+ try {
+ const payload = {
+ status: this.statusForm.status
+ };
+
+ if (this.statusForm.tracking_number) {
+ payload.tracking_number = this.statusForm.tracking_number;
+ }
+
+ if (this.statusForm.reason) {
+ payload.reason = this.statusForm.reason;
+ }
+
+ await apiClient.patch(`/admin/orders/${this.selectedOrder.id}/status`, payload);
+
+ adminOrdersLog.info('Updated order status:', this.selectedOrder.id);
+
+ this.showStatusModal = false;
+ this.selectedOrder = null;
+
+ Utils.showToast('Order status updated successfully.', 'success');
+
+ await this.refresh();
+ } catch (error) {
+ adminOrdersLog.error('Failed to update order status:', error);
+ Utils.showToast(error.message || 'Failed to update status.', 'error');
+ } finally {
+ this.saving = false;
+ }
+ },
+
+ /**
+ * Get CSS class for status badge
+ */
+ getStatusClass(status) {
+ const classes = {
+ pending: 'text-orange-700 bg-orange-100 dark:bg-orange-700 dark:text-orange-100',
+ processing: 'text-blue-700 bg-blue-100 dark:bg-blue-700 dark:text-blue-100',
+ shipped: 'text-purple-700 bg-purple-100 dark:bg-purple-700 dark:text-purple-100',
+ delivered: 'text-green-700 bg-green-100 dark:bg-green-700 dark:text-green-100',
+ cancelled: 'text-red-700 bg-red-100 dark:bg-red-700 dark:text-red-100',
+ refunded: 'text-gray-700 bg-gray-100 dark:bg-gray-700 dark:text-gray-100'
+ };
+ return classes[status] || 'text-gray-700 bg-gray-100 dark:bg-gray-700 dark:text-gray-100';
+ },
+
+ /**
+ * Format price for display
+ */
+ formatPrice(price, currency = 'EUR') {
+ if (price === null || price === undefined) return '-';
+ return new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: currency || 'EUR'
+ }).format(price);
+ },
+
+ /**
+ * Format date for display
+ */
+ formatDate(dateString) {
+ if (!dateString) return '-';
+ const date = new Date(dateString);
+ return date.toLocaleDateString('en-GB', {
+ day: '2-digit',
+ month: 'short',
+ year: 'numeric'
+ });
+ },
+
+ /**
+ * Format time for display
+ */
+ formatTime(dateString) {
+ if (!dateString) return '';
+ const date = new Date(dateString);
+ return date.toLocaleTimeString('en-GB', {
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+ },
+
+ /**
+ * Format full date and time
+ */
+ formatDateTime(dateString) {
+ if (!dateString) return '-';
+ const date = new Date(dateString);
+ return date.toLocaleString('en-GB', {
+ day: '2-digit',
+ month: 'short',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ });
+ },
+
+ /**
+ * Pagination: Previous page
+ */
+ previousPage() {
+ if (this.pagination.page > 1) {
+ this.pagination.page--;
+ this.loadOrders();
+ }
+ },
+
+ /**
+ * Pagination: Next page
+ */
+ nextPage() {
+ if (this.pagination.page < this.totalPages) {
+ this.pagination.page++;
+ this.loadOrders();
+ }
+ },
+
+ /**
+ * Pagination: Go to specific page
+ */
+ goToPage(pageNum) {
+ if (pageNum !== '...' && pageNum >= 1 && pageNum <= this.totalPages) {
+ this.pagination.page = pageNum;
+ this.loadOrders();
+ }
+ }
+ };
+}