Explore practical examples and real-world implementations using the Crave.js backend API. From simple integrations to complex restaurant management systems.

Complete Restaurant Storefront

Frontend Implementation (React + Next.js)

Here’s a complete implementation of a restaurant ordering system using React and the Crave.js API:
import { useState, useEffect } from 'react';
import { useCart } from '../hooks/useCart';

const MenuPage = ({ locationSlug }) => {
  const [menu, setMenu] = useState([]);
  const [loading, setLoading] = useState(true);
  const { addToCart, isLoading: cartLoading } = useCart();

  useEffect(() => {
    fetchMenu();
  }, [locationSlug]);

  const fetchMenu = async () => {
    try {
      const response = await fetch(`/api/locations/${locationSlug}/menu`);
      const data = await response.json();
      setMenu(data.items || []);
    } catch (error) {
      console.error('Failed to fetch menu:', error);
    } finally {
      setLoading(false);
    }
  };

  const handleAddToCart = async (product) => {
    try {
      await addToCart(product.id, 1);
    } catch (error) {
      console.error('Failed to add to cart:', error);
    }
  };

  if (loading) return <div>Loading menu...</div>;

  return (
    <div className="menu-page">
      <h1>Our Menu</h1>
      <div className="menu-grid">
        {menu.map(item => (
          <div key={item.id} className="menu-item">
            <img src={item.image} alt={item.name} />
            <div className="item-info">
              <h3>{item.name}</h3>
              <p>{item.description}</p>
              <div className="item-footer">
                <span className="price">${item.price}</span>
                <button 
                  onClick={() => handleAddToCart(item)}
                  disabled={cartLoading || !item.available}
                >
                  {item.available ? 'Add to Cart' : 'Unavailable'}
                </button>
              </div>
            </div>
          </div>
        ))}
      </div>
    </div>
  );
};

export default MenuPage;

Multi-Location Restaurant Chain

Location Management System

// services/LocationService.js
class LocationService {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = process.env.CRAVE_API_BASE;
  }

  async getAllLocations() {
    const response = await fetch(`${this.baseUrl}/admin/locations`, {
      headers: { 'X-API-Key': this.apiKey }
    });
    return response.json();
  }

  async getLocationBySlug(slug) {
    const response = await fetch(`${this.baseUrl}/locations/${slug}`, {
      headers: { 'X-API-Key': this.apiKey }
    });
    return response.json();
  }

  async updateLocationHours(locationId, hours) {
    const response = await fetch(`${this.baseUrl}/admin/locations/${locationId}/hours`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': this.apiKey
      },
      body: JSON.stringify({ hours })
    });
    return response.json();
  }

  async getLocationAnalytics(locationId, startDate, endDate) {
    const params = new URLSearchParams({
      startDate: startDate.toISOString(),
      endDate: endDate.toISOString()
    });

    const response = await fetch(`${this.baseUrl}/admin/locations/${locationId}/analytics?${params}`, {
      headers: { 'X-API-Key': this.apiKey }
    });
    return response.json();
  }
}

// Usage in a React component
const LocationDashboard = () => {
  const [locations, setLocations] = useState([]);
  const [selectedLocation, setSelectedLocation] = useState(null);
  const [analytics, setAnalytics] = useState(null);

  const locationService = new LocationService(process.env.NEXT_PUBLIC_CRAVE_API_KEY);

  useEffect(() => {
    loadLocations();
  }, []);

  const loadLocations = async () => {
    try {
      const data = await locationService.getAllLocations();
      setLocations(data.locations || []);
    } catch (error) {
      console.error('Failed to load locations:', error);
    }
  };

  const loadAnalytics = async (locationId) => {
    try {
      const endDate = new Date();
      const startDate = new Date(endDate.getTime() - 30 * 24 * 60 * 60 * 1000); // 30 days ago
      
      const data = await locationService.getLocationAnalytics(locationId, startDate, endDate);
      setAnalytics(data);
    } catch (error) {
      console.error('Failed to load analytics:', error);
    }
  };

  return (
    <div className="location-dashboard">
      <div className="locations-list">
        <h2>Restaurant Locations</h2>
        {locations.map(location => (
          <div 
            key={location.id} 
            className={`location-card ${selectedLocation?.id === location.id ? 'selected' : ''}`}
            onClick={() => {
              setSelectedLocation(location);
              loadAnalytics(location.id);
            }}
          >
            <h3>{location.name}</h3>
            <p>{location.address}</p>
            <div className="location-stats">
              <span className={`status ${location.status}`}>
                {location.status}
              </span>
              <span className="orders-today">
                {location.todayOrders || 0} orders today
              </span>
            </div>
          </div>
        ))}
      </div>

      {selectedLocation && (
        <div className="location-details">
          <h2>{selectedLocation.name}</h2>
          {analytics && (
            <div className="analytics-grid">
              <div className="metric">
                <h3>Total Orders</h3>
                <p>{analytics.totalOrders}</p>
              </div>
              <div className="metric">
                <h3>Revenue</h3>
                <p>${analytics.totalRevenue}</p>
              </div>
              <div className="metric">
                <h3>Average Order</h3>
                <p>${analytics.averageOrderValue}</p>
              </div>
              <div className="metric">
                <h3>Popular Items</h3>
                <ul>
                  {analytics.popularItems?.map(item => (
                    <li key={item.id}>{item.name} ({item.count})</li>
                  ))}
                </ul>
              </div>
            </div>
          )}
        </div>
      )}
    </div>
  );
};

Order Management System

Real-time Order Processing

// services/OrderService.js
class OrderService {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = process.env.CRAVE_API_BASE;
  }

  async getOrders(locationId, status = 'all', page = 1, limit = 20) {
    const params = new URLSearchParams({
      status,
      page: page.toString(),
      limit: limit.toString()
    });

    const response = await fetch(`${this.baseUrl}/admin/locations/${locationId}/orders?${params}`, {
      headers: { 'X-API-Key': this.apiKey }
    });
    return response.json();
  }

  async updateOrderStatus(orderId, status, estimatedTime = null) {
    const body = { status };
    if (estimatedTime) body.estimatedTime = estimatedTime;

    const response = await fetch(`${this.baseUrl}/admin/orders/${orderId}/status`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': this.apiKey
      },
      body: JSON.stringify(body)
    });
    return response.json();
  }

  async getOrderDetails(orderId) {
    const response = await fetch(`${this.baseUrl}/admin/orders/${orderId}`, {
      headers: { 'X-API-Key': this.apiKey }
    });
    return response.json();
  }

  // WebSocket connection for real-time updates
  connectToOrderUpdates(locationId, onOrderUpdate) {
    const ws = new WebSocket(`wss://api.yourrestaurant.com/ws/orders/${locationId}`);
    
    ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      onOrderUpdate(data);
    };

    ws.onopen = () => {
      console.log('Connected to order updates');
    };

    ws.onclose = () => {
      console.log('Disconnected from order updates');
      // Reconnect logic
      setTimeout(() => this.connectToOrderUpdates(locationId, onOrderUpdate), 5000);
    };

    return ws;
  }
}

// React component for kitchen display
const KitchenDisplay = ({ locationId }) => {
  const [orders, setOrders] = useState([]);
  const [selectedOrder, setSelectedOrder] = useState(null);
  const orderService = new OrderService(process.env.CRAVE_API_KEY);

  useEffect(() => {
    loadOrders();
    
    // Connect to real-time updates
    const ws = orderService.connectToOrderUpdates(locationId, (orderUpdate) => {
      setOrders(prevOrders => {
        const updatedOrders = prevOrders.map(order => 
          order.id === orderUpdate.id ? { ...order, ...orderUpdate } : order
        );
        
        // Add new orders
        if (!prevOrders.find(o => o.id === orderUpdate.id)) {
          updatedOrders.push(orderUpdate);
        }
        
        return updatedOrders.sort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
      });
    });

    return () => ws.close();
  }, [locationId]);

  const loadOrders = async () => {
    try {
      const data = await orderService.getOrders(locationId, 'pending');
      setOrders(data.orders || []);
    } catch (error) {
      console.error('Failed to load orders:', error);
    }
  };

  const updateOrderStatus = async (orderId, newStatus, estimatedTime) => {
    try {
      await orderService.updateOrderStatus(orderId, newStatus, estimatedTime);
      // Update will come through WebSocket
    } catch (error) {
      console.error('Failed to update order status:', error);
    }
  };

  const getStatusColor = (status) => {
    const colors = {
      pending: 'orange',
      preparing: 'blue',
      ready: 'green',
      completed: 'gray',
      cancelled: 'red'
    };
    return colors[status] || 'gray';
  };

  return (
    <div className="kitchen-display">
      <h1>Kitchen Display - {orders.length} Active Orders</h1>
      
      <div className="orders-grid">
        {orders.map(order => (
          <div 
            key={order.id} 
            className={`order-card ${order.status}`}
            onClick={() => setSelectedOrder(order)}
          >
            <div className="order-header">
              <span className="order-number">#{order.orderNumber}</span>
              <span className={`status ${getStatusColor(order.status)}`}>
                {order.status}
              </span>
            </div>
            
            <div className="order-time">
              <span>Placed: {new Date(order.createdAt).toLocaleTimeString()}</span>
              {order.estimatedTime && (
                <span>ETA: {new Date(order.estimatedTime).toLocaleTimeString()}</span>
              )}
            </div>
            
            <div className="order-items">
              {order.items.map(item => (
                <div key={item.id} className="item">
                  <span className="quantity">{item.quantity}x</span>
                  <span className="name">{item.name}</span>
                  {item.modifiers && item.modifiers.length > 0 && (
                    <div className="modifiers">
                      {item.modifiers.map(mod => (
                        <span key={mod.id} className="modifier">+{mod.name}</span>
                      ))}
                    </div>
                  )}
                </div>
              ))}
            </div>
            
            <div className="order-actions">
              {order.status === 'pending' && (
                <button 
                  onClick={(e) => {
                    e.stopPropagation();
                    updateOrderStatus(order.id, 'preparing', new Date(Date.now() + 15 * 60 * 1000));
                  }}
                  className="btn-start"
                >
                  Start Preparing
                </button>
              )}
              
              {order.status === 'preparing' && (
                <button 
                  onClick={(e) => {
                    e.stopPropagation();
                    updateOrderStatus(order.id, 'ready');
                  }}
                  className="btn-ready"
                >
                  Mark Ready
                </button>
              )}
              
              {order.status === 'ready' && (
                <button 
                  onClick={(e) => {
                    e.stopPropagation();
                    updateOrderStatus(order.id, 'completed');
                  }}
                  className="btn-complete"
                >
                  Complete
                </button>
              )}
            </div>
          </div>
        ))}
      </div>

      {selectedOrder && (
        <OrderDetailsModal 
          order={selectedOrder} 
          onClose={() => setSelectedOrder(null)}
          onUpdateStatus={updateOrderStatus}
        />
      )}
    </div>
  );
};

Payment Processing Integration

Stripe Payment Flow

// services/PaymentService.js
class PaymentService {
  constructor(apiKey) {
    this.apiKey = apiKey;
    this.baseUrl = process.env.CRAVE_API_BASE;
  }

  async createPaymentIntent(cartId, amount, currency = 'usd') {
    const response = await fetch(`${this.baseUrl}/payments/create-intent`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': this.apiKey
      },
      body: JSON.stringify({
        cartId,
        amount: Math.round(amount * 100), // Convert to cents
        currency
      })
    });

    if (!response.ok) throw new Error('Failed to create payment intent');
    return response.json();
  }

  async confirmPayment(paymentIntentId, paymentMethodId) {
    const response = await fetch(`${this.baseUrl}/payments/confirm`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': this.apiKey
      },
      body: JSON.stringify({
        paymentIntentId,
        paymentMethodId
      })
    });

    if (!response.ok) throw new Error('Failed to confirm payment');
    return response.json();
  }

  async processRefund(orderId, amount, reason) {
    const response = await fetch(`${this.baseUrl}/payments/refund`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-API-Key': this.apiKey
      },
      body: JSON.stringify({
        orderId,
        amount: Math.round(amount * 100),
        reason
      })
    });

    if (!response.ok) throw new Error('Failed to process refund');
    return response.json();
  }
}

// React checkout component
const CheckoutForm = ({ cart, onPaymentSuccess }) => {
  const [stripe, setStripe] = useState(null);
  const [elements, setElements] = useState(null);
  const [clientSecret, setClientSecret] = useState('');
  const [processing, setProcessing] = useState(false);
  const [error, setError] = useState('');

  const paymentService = new PaymentService(process.env.NEXT_PUBLIC_CRAVE_API_KEY);

  useEffect(() => {
    initializeStripe();
    createPaymentIntent();
  }, []);

  const initializeStripe = async () => {
    const stripeInstance = await loadStripe(process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY);
    setStripe(stripeInstance);
  };

  const createPaymentIntent = async () => {
    try {
      const { clientSecret } = await paymentService.createPaymentIntent(
        cart.id,
        cart.total
      );
      setClientSecret(clientSecret);
    } catch (error) {
      setError('Failed to initialize payment');
    }
  };

  const handleSubmit = async (event) => {
    event.preventDefault();
    
    if (!stripe || !elements) return;

    setProcessing(true);
    setError('');

    const { error, paymentIntent } = await stripe.confirmCardPayment(clientSecret, {
      payment_method: {
        card: elements.getElement(CardElement),
        billing_details: {
          name: event.target.name.value,
          email: event.target.email.value
        }
      }
    });

    if (error) {
      setError(error.message);
      setProcessing(false);
    } else {
      onPaymentSuccess(paymentIntent);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="checkout-form">
      <div className="customer-info">
        <input name="name" placeholder="Full Name" required />
        <input name="email" type="email" placeholder="Email" required />
        <input name="phone" type="tel" placeholder="Phone" required />
      </div>

      <div className="payment-section">
        <h3>Payment Information</h3>
        <Elements stripe={stripe}>
          <CardElement 
            options={{
              style: {
                base: {
                  fontSize: '16px',
                  color: '#424770',
                  '::placeholder': {
                    color: '#aab7c4',
                  },
                },
              },
            }}
          />
        </Elements>
      </div>

      <div className="order-summary">
        <h3>Order Summary</h3>
        {cart.items.map(item => (
          <div key={item.id} className="summary-item">
            <span>{item.quantity}x {item.name}</span>
            <span>${(item.price * item.quantity).toFixed(2)}</span>
          </div>
        ))}
        <div className="total">
          <strong>Total: ${cart.total.toFixed(2)}</strong>
        </div>
      </div>

      {error && <div className="error-message">{error}</div>}

      <button 
        type="submit" 
        disabled={!stripe || processing}
        className="pay-button"
      >
        {processing ? 'Processing...' : `Pay $${cart.total.toFixed(2)}`}
      </button>
    </form>
  );
};

Mobile App Integration

React Native Implementation

// services/CraveApiClient.js
class CraveApiClient {
  constructor(apiKey, baseUrl) {
    this.apiKey = apiKey;
    this.baseUrl = baseUrl;
  }

  async request(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    const config = {
      ...options,
      headers: {
        'X-API-Key': this.apiKey,
        'Content-Type': 'application/json',
        ...options.headers
      }
    };

    try {
      const response = await fetch(url, config);
      
      if (!response.ok) {
        throw new Error(`HTTP ${response.status}: ${response.statusText}`);
      }
      
      return await response.json();
    } catch (error) {
      console.error('API Request failed:', error);
      throw error;
    }
  }

  // Location methods
  async getLocations() {
    return this.request('/locations');
  }

  async getLocationMenu(locationSlug) {
    return this.request(`/locations/${locationSlug}/menu`);
  }

  // Cart methods
  async createCart(locationSlug) {
    return this.request(`/locations/${locationSlug}/carts`, {
      method: 'POST',
      body: JSON.stringify({ marketplaceId: 'stripe' })
    });
  }

  async addToCart(locationSlug, cartId, productId, quantity) {
    return this.request(`/locations/${locationSlug}/carts/${cartId}/cart-item`, {
      method: 'POST',
      body: JSON.stringify({ id: productId, quantity })
    });
  }

  async getCart(locationSlug, cartId) {
    return this.request(`/locations/${locationSlug}/carts/${cartId}`);
  }

  // Order methods
  async createOrder(cartId, customerInfo) {
    return this.request('/orders', {
      method: 'POST',
      body: JSON.stringify({
        cartId,
        customer: customerInfo
      })
    });
  }

  async getOrder(orderId) {
    return this.request(`/orders/${orderId}`);
  }
}

// React Native component
import React, { useState, useEffect } from 'react';
import { 
  View, 
  Text, 
  FlatList, 
  TouchableOpacity, 
  Image, 
  StyleSheet, 
  Alert 
} from 'react-native';

const MenuScreen = ({ navigation, route }) => {
  const { locationSlug } = route.params;
  const [menuItems, setMenuItems] = useState([]);
  const [loading, setLoading] = useState(true);
  const [cart, setCart] = useState(null);

  const apiClient = new CraveApiClient(
    'your-api-key',
    'https://api.yourrestaurant.com/api/v1'
  );

  useEffect(() => {
    loadMenuAndCart();
  }, []);

  const loadMenuAndCart = async () => {
    try {
      const [menuData, cartData] = await Promise.all([
        apiClient.getLocationMenu(locationSlug),
        apiClient.createCart(locationSlug)
      ]);

      setMenuItems(menuData.items || []);
      setCart(cartData);
    } catch (error) {
      Alert.alert('Error', 'Failed to load menu');
    } finally {
      setLoading(false);
    }
  };

  const handleAddToCart = async (item) => {
    try {
      await apiClient.addToCart(locationSlug, cart.id, item.id, 1);
      Alert.alert('Success', `${item.name} added to cart`);
    } catch (error) {
      Alert.alert('Error', 'Failed to add item to cart');
    }
  };

  const renderMenuItem = ({ item }) => (
    <View style={styles.menuItem}>
      <Image source={{ uri: item.image }} style={styles.itemImage} />
      <View style={styles.itemInfo}>
        <Text style={styles.itemName}>{item.name}</Text>
        <Text style={styles.itemDescription}>{item.description}</Text>
        <View style={styles.itemFooter}>
          <Text style={styles.itemPrice}>${item.price}</Text>
          <TouchableOpacity 
            style={[styles.addButton, !item.available && styles.disabledButton]}
            onPress={() => handleAddToCart(item)}
            disabled={!item.available}
          >
            <Text style={styles.buttonText}>
              {item.available ? 'Add to Cart' : 'Unavailable'}
            </Text>
          </TouchableOpacity>
        </View>
      </View>
    </View>
  );

  if (loading) {
    return (
      <View style={styles.loading}>
        <Text>Loading menu...</Text>
      </View>
    );
  }

  return (
    <View style={styles.container}>
      <FlatList
        data={menuItems}
        renderItem={renderMenuItem}
        keyExtractor={(item) => item.id}
        contentContainerStyle={styles.menuList}
      />
      
      <TouchableOpacity 
        style={styles.cartButton}
        onPress={() => navigation.navigate('Cart', { cart })}
      >
        <Text style={styles.cartButtonText}>View Cart</Text>
      </TouchableOpacity>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#f5f5f5'
  },
  loading: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center'
  },
  menuList: {
    padding: 16
  },
  menuItem: {
    backgroundColor: 'white',
    borderRadius: 8,
    marginBottom: 16,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 2 },
    shadowOpacity: 0.1,
    shadowRadius: 4,
    elevation: 3
  },
  itemImage: {
    width: '100%',
    height: 200,
    borderTopLeftRadius: 8,
    borderTopRightRadius: 8
  },
  itemInfo: {
    padding: 16
  },
  itemName: {
    fontSize: 18,
    fontWeight: 'bold',
    marginBottom: 8
  },
  itemDescription: {
    fontSize: 14,
    color: '#666',
    marginBottom: 12
  },
  itemFooter: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center'
  },
  itemPrice: {
    fontSize: 16,
    fontWeight: 'bold',
    color: '#2196F3'
  },
  addButton: {
    backgroundColor: '#2196F3',
    paddingHorizontal: 16,
    paddingVertical: 8,
    borderRadius: 4
  },
  disabledButton: {
    backgroundColor: '#ccc'
  },
  buttonText: {
    color: 'white',
    fontWeight: 'bold'
  },
  cartButton: {
    backgroundColor: '#2196F3',
    margin: 16,
    padding: 16,
    borderRadius: 8,
    alignItems: 'center'
  },
  cartButtonText: {
    color: 'white',
    fontSize: 18,
    fontWeight: 'bold'
  }
});

export default MenuScreen;

Analytics Dashboard

Restaurant Performance Dashboard

// components/AnalyticsDashboard.jsx
import React, { useState, useEffect } from 'react';
import { Line, Bar, Doughnut } from 'react-chartjs-2';

const AnalyticsDashboard = ({ locationId }) => {
  const [analytics, setAnalytics] = useState(null);
  const [timeRange, setTimeRange] = useState('7d');
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    loadAnalytics();
  }, [timeRange]);

  const loadAnalytics = async () => {
    try {
      const endDate = new Date();
      const startDate = new Date();
      
      switch (timeRange) {
        case '24h':
          startDate.setHours(startDate.getHours() - 24);
          break;
        case '7d':
          startDate.setDate(startDate.getDate() - 7);
          break;
        case '30d':
          startDate.setDate(startDate.getDate() - 30);
          break;
        case '90d':
          startDate.setDate(startDate.getDate() - 90);
          break;
      }

      const response = await fetch(`/api/analytics/dashboard?locationId=${locationId}&startDate=${startDate.toISOString()}&endDate=${endDate.toISOString()}`);
      const data = await response.json();
      setAnalytics(data);
    } catch (error) {
      console.error('Failed to load analytics:', error);
    } finally {
      setLoading(false);
    }
  };

  if (loading) return <div>Loading analytics...</div>;

  const revenueChartData = {
    labels: analytics.daily.map(d => new Date(d.date).toLocaleDateString()),
    datasets: [{
      label: 'Revenue',
      data: analytics.daily.map(d => d.revenue),
      borderColor: '#2196F3',
      backgroundColor: 'rgba(33, 150, 243, 0.1)',
      fill: true
    }]
  };

  const ordersChartData = {
    labels: analytics.daily.map(d => new Date(d.date).toLocaleDateString()),
    datasets: [{
      label: 'Orders',
      data: analytics.daily.map(d => d.orders),
      backgroundColor: '#4CAF50'
    }]
  };

  const popularItemsData = {
    labels: analytics.popularItems.map(item => item.name),
    datasets: [{
      data: analytics.popularItems.map(item => item.quantity),
      backgroundColor: [
        '#FF6384',
        '#36A2EB',
        '#FFCE56',
        '#4BC0C0',
        '#9966FF',
        '#FF9F40'
      ]
    }]
  };

  return (
    <div className="analytics-dashboard">
      <div className="dashboard-header">
        <h1>Restaurant Analytics</h1>
        <select 
          value={timeRange} 
          onChange={(e) => setTimeRange(e.target.value)}
          className="time-range-select"
        >
          <option value="24h">Last 24 Hours</option>
          <option value="7d">Last 7 Days</option>
          <option value="30d">Last 30 Days</option>
          <option value="90d">Last 90 Days</option>
        </select>
      </div>

      <div className="metrics-grid">
        <div className="metric-card">
          <h3>Total Revenue</h3>
          <p className="metric-value">${analytics.totalRevenue.toFixed(2)}</p>
          <p className="metric-change">
            {analytics.revenueChange > 0 ? '+' : ''}
            {analytics.revenueChange.toFixed(1)}% vs previous period
          </p>
        </div>

        <div className="metric-card">
          <h3>Total Orders</h3>
          <p className="metric-value">{analytics.totalOrders}</p>
          <p className="metric-change">
            {analytics.ordersChange > 0 ? '+' : ''}
            {analytics.ordersChange.toFixed(1)}% vs previous period
          </p>
        </div>

        <div className="metric-card">
          <h3>Average Order Value</h3>
          <p className="metric-value">${analytics.averageOrderValue.toFixed(2)}</p>
          <p className="metric-change">
            {analytics.aovChange > 0 ? '+' : ''}
            {analytics.aovChange.toFixed(1)}% vs previous period
          </p>
        </div>

        <div className="metric-card">
          <h3>Customer Satisfaction</h3>
          <p className="metric-value">{analytics.customerSatisfaction.toFixed(1)}/5</p>
          <p className="metric-change">
            Based on {analytics.reviewCount} reviews
          </p>
        </div>
      </div>

      <div className="charts-grid">
        <div className="chart-container">
          <h3>Revenue Trend</h3>
          <Line data={revenueChartData} />
        </div>

        <div className="chart-container">
          <h3>Daily Orders</h3>
          <Bar data={ordersChartData} />
        </div>

        <div className="chart-container">
          <h3>Popular Items</h3>
          <Doughnut data={popularItemsData} />
        </div>

        <div className="chart-container">
          <h3>Order Status Distribution</h3>
          <div className="status-breakdown">
            {analytics.orderStatuses.map(status => (
              <div key={status.status} className="status-item">
                <span className={`status-indicator ${status.status}`}></span>
                <span className="status-label">{status.status}</span>
                <span className="status-count">{status.count}</span>
              </div>
            ))}
          </div>
        </div>
      </div>

      <div className="insights-section">
        <h3>AI-Powered Insights</h3>
        <div className="insights-grid">
          {analytics.insights.map((insight, index) => (
            <div key={index} className="insight-card">
              <div className={`insight-icon ${insight.type}`}>
                {insight.icon}
              </div>
              <div className="insight-content">
                <h4>{insight.title}</h4>
                <p>{insight.description}</p>
                {insight.action && (
                  <button className="insight-action">
                    {insight.action}
                  </button>
                )}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

export default AnalyticsDashboard;

Next Steps

These examples demonstrate the flexibility and power of the Crave.js backend API. You can:
  1. Customize the Frontend - Use any framework (React, Vue, Angular, React Native)
  2. Extend Functionality - Add features like loyalty programs, promotions, reviews
  3. Integrate Third-Party Services - Connect with POS systems, delivery services, marketing tools
  4. Scale for Enterprise - Handle multiple locations, complex menus, high-volume orders

Additional Resources

All examples are available in our GitHub repository with full implementations and setup instructions.