rtrips-online/src/app/trips/page.tsx

132 lines
4.9 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { AppSwitcher } from '@/components/AppSwitcher';
import { format } from 'date-fns';
import { apiUrl } from '@/lib/api';
interface TripSummary {
id: string;
title: string;
slug: string;
description: string | null;
startDate: string | null;
endDate: string | null;
budgetTotal: number | null;
budgetCurrency: string;
status: string;
destinations: { name: string; country: string | null }[];
_count: {
itineraryItems: number;
bookings: number;
expenses: number;
packingItems: number;
};
updatedAt: string;
}
const STATUS_COLORS: Record<string, string> = {
PLANNING: 'bg-teal-500/20 text-teal-300',
BOOKED: 'bg-blue-500/20 text-blue-300',
IN_PROGRESS: 'bg-amber-500/20 text-amber-300',
COMPLETED: 'bg-emerald-500/20 text-emerald-300',
CANCELLED: 'bg-slate-500/20 text-slate-400',
};
export default function TripsPage() {
const [trips, setTrips] = useState<TripSummary[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch(apiUrl('/api/trips'))
.then((res) => res.json())
.then(setTrips)
.catch(console.error)
.finally(() => setLoading(false));
}, []);
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 text-white">
{/* Nav */}
<nav className="border-b border-slate-700/50 backdrop-blur-sm">
<div className="max-w-6xl mx-auto px-6 py-4 flex items-center justify-between">
<div className="flex items-center gap-3">
<AppSwitcher current="trips" />
<Link href="/" className="flex items-center gap-2">
<div className="w-8 h-8 bg-gradient-to-br from-teal-400 to-cyan-500 rounded-lg flex items-center justify-center font-bold text-slate-900 text-sm">
rT
</div>
<span className="font-semibold text-lg">rTrips</span>
</Link>
</div>
<Link
href="/trips/new"
className="text-sm px-4 py-2 bg-teal-600 hover:bg-teal-500 rounded-lg transition-colors font-medium"
>
Plan a Trip
</Link>
</div>
</nav>
<div className="max-w-4xl mx-auto px-6 py-12">
<h1 className="text-3xl font-bold mb-8">My Trips</h1>
{loading ? (
<div className="flex items-center justify-center py-20">
<svg className="animate-spin h-8 w-8 text-teal-400" viewBox="0 0 24 24">
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" fill="none" />
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" />
</svg>
</div>
) : trips.length === 0 ? (
<div className="text-center py-20">
<p className="text-slate-400 mb-4">No trips yet. Start planning your first adventure!</p>
<Link
href="/trips/new"
className="inline-block px-6 py-3 bg-teal-600 hover:bg-teal-500 rounded-xl font-medium transition-all"
>
Plan a Trip
</Link>
</div>
) : (
<div className="space-y-4">
{trips.map((trip) => (
<Link
key={trip.id}
href={`/trips/${trip.id}`}
className="block bg-slate-800/50 rounded-xl p-5 border border-slate-700/50 hover:border-teal-500/30 transition-all group"
>
<div className="flex items-start justify-between mb-3">
<div>
<h2 className="text-lg font-semibold group-hover:text-teal-300 transition-colors">{trip.title}</h2>
{trip.destinations.length > 0 && (
<p className="text-sm text-slate-400 mt-0.5">
{trip.destinations.map((d) => d.name).join(' → ')}
</p>
)}
</div>
<span className={`text-xs px-2 py-1 rounded-full ${STATUS_COLORS[trip.status] || STATUS_COLORS.PLANNING}`}>
{trip.status.replace('_', ' ')}
</span>
</div>
<div className="flex items-center gap-4 text-xs text-slate-500">
{trip.startDate && (
<span>{format(new Date(trip.startDate), 'MMM d, yyyy')}</span>
)}
{trip.budgetTotal && (
<span>{trip.budgetCurrency} {trip.budgetTotal.toLocaleString()}</span>
)}
<span>{trip._count.itineraryItems} items</span>
<span>{trip._count.bookings} bookings</span>
</div>
</Link>
))}
</div>
)}
</div>
</div>
);
}