198 lines
5.8 KiB
TypeScript
198 lines
5.8 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import {
|
|
IonButton,
|
|
IonButtons,
|
|
IonContent,
|
|
IonHeader,
|
|
IonIcon,
|
|
IonList,
|
|
IonPage,
|
|
IonRefresher,
|
|
IonRefresherContent,
|
|
IonText,
|
|
IonToolbar,
|
|
useIonViewWillEnter,
|
|
} from '@ionic/react';
|
|
import {
|
|
chevronBackOutline,
|
|
chevronForwardOutline,
|
|
receiptOutline,
|
|
} from 'ionicons/icons';
|
|
import { RefresherEventDetail } from '@ionic/core';
|
|
import { useHistory } from 'react-router-dom';
|
|
import { supabase } from '../supabase';
|
|
import type { Tables } from '../database.types';
|
|
import { useAuth } from '../context/AuthContext';
|
|
import { setStatusBarStyle, Style } from '../utils/statusBar';
|
|
|
|
type Order = Tables<'orders'>;
|
|
const statusCopy: Record<string, string> = {
|
|
confirmed: 'Confirmed',
|
|
packed: 'Packed',
|
|
out_for_delivery: 'Out for delivery',
|
|
delivered: 'Delivered',
|
|
};
|
|
|
|
const MyOrders: React.FC = () => {
|
|
const history = useHistory();
|
|
const { user } = useAuth();
|
|
const [orders, setOrders] = useState<Order[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [errorMessage, setErrorMessage] = useState('');
|
|
|
|
useIonViewWillEnter(() => {
|
|
void setStatusBarStyle(Style.Dark);
|
|
});
|
|
|
|
const loadOrders = async () => {
|
|
if (!user) {
|
|
setOrders([]);
|
|
setErrorMessage('');
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
setErrorMessage('');
|
|
|
|
const { data, error } = await supabase
|
|
.from('orders')
|
|
.select('*')
|
|
.eq('user_id', user.id)
|
|
.order('created_at', { ascending: false });
|
|
|
|
if (error) {
|
|
throw error;
|
|
}
|
|
|
|
setOrders(data ?? []);
|
|
} catch (error) {
|
|
console.error('Failed to load orders', error);
|
|
setOrders([]);
|
|
setErrorMessage('We could not load your orders right now.');
|
|
window.setTimeout(() => setErrorMessage(''), 4000);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (!user) {
|
|
setOrders([]);
|
|
setErrorMessage('');
|
|
setIsLoading(false);
|
|
return;
|
|
}
|
|
|
|
setIsLoading(true);
|
|
void loadOrders();
|
|
}, [user?.id]);
|
|
|
|
const handleRefresh = async (event: CustomEvent<RefresherEventDetail>) => {
|
|
await loadOrders();
|
|
event.detail.complete();
|
|
};
|
|
|
|
return (
|
|
<IonPage>
|
|
<IonHeader className="ion-no-border wallet-header wallet-header-modern">
|
|
<IonToolbar>
|
|
<div className="wallet-toolbar-inner wallet-toolbar-inner-single-action">
|
|
<IonButton
|
|
fill="clear"
|
|
className="wallet-back-button wallet-back-button-modern"
|
|
aria-label="Back to home"
|
|
onClick={() => history.replace('/home')}
|
|
>
|
|
<IonIcon icon={chevronBackOutline} slot="icon-only" />
|
|
</IonButton>
|
|
<div className="wallet-toolbar-title-block">
|
|
<span>Tracking</span>
|
|
<strong>My Orders</strong>
|
|
</div>
|
|
<span className="wallet-toolbar-placeholder" />
|
|
</div>
|
|
</IonToolbar>
|
|
</IonHeader>
|
|
<IonContent className="ion-padding">
|
|
<IonRefresher slot="fixed" onIonRefresh={handleRefresh}>
|
|
<IonRefresherContent />
|
|
</IonRefresher>
|
|
{errorMessage && (
|
|
<IonText color="danger">
|
|
<p>{errorMessage}</p>
|
|
</IonText>
|
|
)}
|
|
{!user ? (
|
|
<div className="empty-state cart-empty">
|
|
<IonIcon icon={receiptOutline} />
|
|
<h3>No tracked orders yet</h3>
|
|
<p className="muted-text">
|
|
Sign in later to sync orders, or place an order now and track it
|
|
from the confirmation screen.
|
|
</p>
|
|
<IonButton onClick={() => history.push('/home')}>
|
|
Continue shopping
|
|
</IonButton>
|
|
</div>
|
|
) : isLoading ? (
|
|
<div className="empty-state cart-empty">
|
|
<IonIcon icon={receiptOutline} />
|
|
<h3>Loading your orders…</h3>
|
|
<p className="muted-text">
|
|
We are fetching your latest order updates.
|
|
</p>
|
|
</div>
|
|
) : orders.length === 0 ? (
|
|
<div className="empty-state cart-empty">
|
|
<IonIcon icon={receiptOutline} />
|
|
<h3>No orders yet</h3>
|
|
<p className="muted-text">
|
|
Your placed orders will appear here so you can track their status.
|
|
</p>
|
|
<IonButton onClick={() => history.push('/home')}>
|
|
Start shopping
|
|
</IonButton>
|
|
</div>
|
|
) : (
|
|
<IonList lines="none" className="orders-list">
|
|
{orders.map((order) => (
|
|
<button
|
|
key={order.id}
|
|
type="button"
|
|
className="order-card"
|
|
onClick={() => history.push(`/orders/${order.id}`)}
|
|
>
|
|
<div className="order-card-top">
|
|
<div>
|
|
<span className="order-label">Order</span>
|
|
<h3>{order.order_number}</h3>
|
|
</div>
|
|
<IonIcon icon={chevronForwardOutline} />
|
|
</div>
|
|
<div className="order-card-row">
|
|
<span>Status</span>
|
|
<strong>{statusCopy[order.status] ?? order.status}</strong>
|
|
</div>
|
|
<div className="order-card-row">
|
|
<span>Items</span>
|
|
<strong>{order.item_count}</strong>
|
|
</div>
|
|
<div className="order-card-row">
|
|
<span>Total</span>
|
|
<strong>${Number(order.total).toFixed(2)}</strong>
|
|
</div>
|
|
<div className="order-card-date">
|
|
{new Date(order.created_at).toLocaleString()}
|
|
</div>
|
|
</button>
|
|
))}
|
|
</IonList>
|
|
)}
|
|
</IonContent>
|
|
</IonPage>
|
|
);
|
|
};
|
|
|
|
export default MyOrders;
|