Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions src/app/delivery/history/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
'use client';

import Link from 'next/link';
import { ArrowLeft, Truck, Clock } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { useDeliveryHistory } from '@/hooks/use-orders';
import { useAuth } from '@/hooks/use-auth';

export default function DeliveryHistoryPage() {
const { deliveryPersonId } = useAuth();
const { assignments, loading, error } = useDeliveryHistory(deliveryPersonId ?? '');

if (!deliveryPersonId || loading) {
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center">
<div className="text-center">
<div className="w-8 h-8 border-4 border-blue-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
<p className="text-gray-500 dark:text-gray-400">Loading history...</p>
</div>
</div>
);
}

const now = new Date();
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
const thisWeek = assignments.filter(
(a) => a.delivered_at && new Date(a.delivered_at) >= sevenDaysAgo
);
const avgPerDay = thisWeek.length > 0 ? (thisWeek.length / 7).toFixed(1) : '0';

return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
<header className="bg-blue-600 text-white">
<div className="max-w-2xl mx-auto px-4 py-4">
<div className="flex items-center gap-3">
<Link href="/delivery">
<Button variant="ghost" size="sm" className="text-white hover:bg-blue-700 -ml-2">
<ArrowLeft className="w-5 h-5" />
</Button>
</Link>
<Truck className="w-6 h-6" />
<h1 className="text-xl font-bold">Delivery History</h1>
</div>
</div>
</header>

<main className="max-w-2xl mx-auto px-4 py-6">
{error && (
<Card className="mb-6 border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950">
<CardContent className="py-4 text-center text-red-600 dark:text-red-400">
{error}
</CardContent>
</Card>
)}

{/* Stats */}
<div className="grid grid-cols-3 gap-3 mb-6">
<Card>
<CardContent className="pt-4 text-center pb-4">
<p className="text-2xl font-bold text-gray-900 dark:text-white">{assignments.length}</p>
<p className="text-xs text-muted-foreground">Completed</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4 text-center pb-4">
<p className="text-2xl font-bold text-gray-900 dark:text-white">{thisWeek.length}</p>
<p className="text-xs text-muted-foreground">This Week</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4 text-center pb-4">
<p className="text-2xl font-bold text-gray-900 dark:text-white">{avgPerDay}</p>
<p className="text-xs text-muted-foreground">Avg / Day</p>
</CardContent>
</Card>
</div>

{assignments.length === 0 ? (
<Card>
<CardContent className="py-12 text-center">
<Clock className="w-12 h-12 mx-auto text-gray-300 dark:text-gray-600 mb-4" />
<p className="text-gray-500 dark:text-gray-400">No completed deliveries yet.</p>
</CardContent>
</Card>
) : (
<div className="space-y-3">
{assignments.map((assignment) => {
const order = assignment.order;
return (
<Card key={assignment.id}>
<CardContent className="py-4">
<div className="flex items-start justify-between mb-2">
<p className="font-medium text-gray-900 dark:text-white text-sm">
#{order.id.slice(0, 8)}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
{assignment.delivered_at
? new Date(assignment.delivered_at).toLocaleString()
: '—'}
</p>
</div>
<p className="text-sm text-gray-600 dark:text-gray-300 mb-1">
<span className="font-medium">{order.vendor?.name ?? 'Vendor'}</span>
{' → '}
{order.delivery_address}
</p>
<p className="text-sm font-semibold text-blue-600 dark:text-blue-400">
${Number(order.total_amount).toFixed(2)}
</p>
</CardContent>
</Card>
);
})}
</div>
)}
</main>
</div>
);
}
24 changes: 17 additions & 7 deletions src/app/delivery/page.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client';

import { useState } from 'react';
import { Bike, Clock, MapPin, Store, Package, Truck, Home, Loader2, Check } from 'lucide-react';
import Link from 'next/link';
import { Bike, Clock, MapPin, Store, Package, Truck, Home, Loader2, Check, History } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Separator } from '@/components/ui/separator';
Expand Down Expand Up @@ -60,12 +61,20 @@ export default function DeliveryDashboard() {
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
<header className="bg-blue-600 text-white">
<div className="max-w-2xl mx-auto px-4 py-4">
<div className="flex items-center gap-3">
<Bike className="w-6 h-6" />
<div>
<h1 className="text-xl font-bold">Delivery Dashboard</h1>
<p className="text-sm text-blue-100">Alex Johnson</p>
<div className="flex items-center justify-between w-full">
<div className="flex items-center gap-3">
<Bike className="w-6 h-6" />
<div>
<h1 className="text-xl font-bold">Delivery Dashboard</h1>
<p className="text-sm text-blue-100">Alex Johnson</p>
</div>
</div>
<Link href="/delivery/history">
<Button variant="outline" size="sm" className="bg-white text-blue-600 hover:bg-blue-50 border-white">
<History className="w-4 h-4 mr-1" />
History
</Button>
</Link>
</div>
</div>
</header>
Expand Down Expand Up @@ -99,7 +108,7 @@ export default function DeliveryDashboard() {
</Card>
) : (
<div className="space-y-4">
{assignments.map((assignment) => (
{assignments.filter((a) => a.order).map((assignment) => (
<AssignmentCard
key={assignment.id}
assignment={assignment}
Expand All @@ -125,6 +134,7 @@ interface AssignmentCardProps {

function AssignmentCard({ assignment, updating, onStatusUpdate }: AssignmentCardProps) {
const order = assignment.order;
if (!order) return null;
const vendorAccepted = order.vendor_accepted;
const deliveryAccepted = assignment.status === 'accepted' || assignment.status === 'picked_up' || assignment.status === 'delivered';

Expand Down
158 changes: 158 additions & 0 deletions src/app/orders/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
'use client';

import Link from 'next/link';
import { ArrowLeft, ShoppingBag, Clock } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { OrderStatusBadge } from '@/components/order-status';
import { useCustomerOrders } from '@/hooks/use-orders';
import { useAuth } from '@/hooks/use-auth';

const ACTIVE_STATUSES = ['pending', 'confirmed', 'preparing', 'ready', 'picked_up'];
const PAST_STATUSES = ['delivered', 'cancelled'];

export default function CustomerOrdersPage() {
const { userId } = useAuth();
const { orders, loading, error } = useCustomerOrders(userId ?? '');

if (!userId || loading) {
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center">
<div className="text-center">
<div className="w-8 h-8 border-4 border-green-500 border-t-transparent rounded-full animate-spin mx-auto mb-4" />
<p className="text-gray-500 dark:text-gray-400">Loading orders...</p>
</div>
</div>
);
}

const activeOrders = orders.filter((o) => ACTIVE_STATUSES.includes(o.status));
const pastOrders = orders.filter((o) => PAST_STATUSES.includes(o.status));

const totalSpent = pastOrders
.filter((o) => o.status === 'delivered')
.reduce((sum, o) => sum + Number(o.total_amount), 0);

return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
<header className="bg-green-600 text-white">
<div className="max-w-2xl mx-auto px-4 py-4">
<div className="flex items-center gap-3">
<Link href="/vendors">
<Button variant="ghost" size="sm" className="text-white hover:bg-green-700 -ml-2">
<ArrowLeft className="w-5 h-5" />
</Button>
</Link>
<ShoppingBag className="w-6 h-6" />
<h1 className="text-xl font-bold">My Orders</h1>
</div>
</div>
</header>

<main className="max-w-2xl mx-auto px-4 py-6">
{error && (
<Card className="mb-6 border-red-200 bg-red-50 dark:border-red-800 dark:bg-red-950">
<CardContent className="py-4 text-center text-red-600 dark:text-red-400">
{error}
</CardContent>
</Card>
)}

{/* Stats */}
<div className="grid grid-cols-3 gap-3 mb-6">
<Card>
<CardContent className="pt-4 text-center pb-4">
<p className="text-2xl font-bold text-gray-900 dark:text-white">{orders.length}</p>
<p className="text-xs text-muted-foreground">Total Orders</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4 text-center pb-4">
<p className="text-2xl font-bold text-gray-900 dark:text-white">${totalSpent.toFixed(2)}</p>
<p className="text-xs text-muted-foreground">Total Spent</p>
</CardContent>
</Card>
<Card>
<CardContent className="pt-4 text-center pb-4">
<p className="text-2xl font-bold text-gray-900 dark:text-white">{activeOrders.length}</p>
<p className="text-xs text-muted-foreground">Active Orders</p>
</CardContent>
</Card>
</div>

{orders.length === 0 ? (
<Card>
<CardContent className="py-12 text-center">
<Clock className="w-12 h-12 mx-auto text-gray-300 dark:text-gray-600 mb-4" />
<p className="text-gray-500 dark:text-gray-400">No orders yet. Start ordering!</p>
</CardContent>
</Card>
) : (
<div className="space-y-6">
{activeOrders.length > 0 && (
<section>
<h2 className="text-sm font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-3">
Active
</h2>
<div className="space-y-3">
{activeOrders.map((order) => (
<Link key={order.id} href={`/orders/${order.id}`}>
<Card className="hover:shadow-md transition-shadow cursor-pointer">
<CardContent className="py-4">
<div className="flex items-start justify-between mb-2">
<div>
<p className="font-medium text-gray-900 dark:text-white">
{order.vendor?.name ?? 'Vendor'}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
{new Date(order.created_at).toLocaleDateString()} · {order.items?.length ?? 0} item{order.items?.length !== 1 ? 's' : ''}
</p>
</div>
<OrderStatusBadge status={order.status} />
</div>
<p className="text-sm font-semibold text-green-600 dark:text-green-400">
${Number(order.total_amount).toFixed(2)}
</p>
</CardContent>
</Card>
</Link>
))}
</div>
</section>
)}

{pastOrders.length > 0 && (
<section>
<h2 className="text-sm font-semibold text-gray-500 dark:text-gray-400 uppercase tracking-wide mb-3">
Past
</h2>
<div className="space-y-3">
{pastOrders.map((order) => (
<Card key={order.id}>
<CardContent className="py-4">
<div className="flex items-start justify-between mb-2">
<div>
<p className="font-medium text-gray-900 dark:text-white">
{order.vendor?.name ?? 'Vendor'}
</p>
<p className="text-xs text-gray-500 dark:text-gray-400">
{new Date(order.created_at).toLocaleDateString()} · {order.items?.length ?? 0} item{order.items?.length !== 1 ? 's' : ''}
</p>
</div>
<OrderStatusBadge status={order.status} />
</div>
<p className="text-sm font-semibold text-gray-700 dark:text-gray-300">
${Number(order.total_amount).toFixed(2)}
</p>
</CardContent>
</Card>
))}
</div>
</section>
)}
</div>
)}
</main>
</div>
);
}
Loading
Loading