Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 191ee1e3dc | |||
| 79f340d44d | |||
| 2ebe01e070 | |||
| da90d43339 | |||
| 9fa6f9aeea |
289
src/app/admin/page.tsx
Normal file
289
src/app/admin/page.tsx
Normal file
@@ -0,0 +1,289 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleApple from "@/components/navbar/NavbarStyleApple/NavbarStyleApple";
|
||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||
import { useState } from "react";
|
||||
import { Plus, Edit2, Trash2, Save, X } from "lucide-react";
|
||||
|
||||
interface BusRoute {
|
||||
id: string;
|
||||
name: string;
|
||||
departure: string;
|
||||
arrival: string;
|
||||
price: number;
|
||||
capacity: number;
|
||||
status: "active" | "inactive";
|
||||
}
|
||||
|
||||
interface Ticket {
|
||||
id: string;
|
||||
routeId: string;
|
||||
passengerName: string;
|
||||
seatNumber: string;
|
||||
bookingDate: string;
|
||||
status: "booked" | "cancelled" | "completed";
|
||||
}
|
||||
|
||||
export default function AdminDashboard() {
|
||||
const [routes, setRoutes] = useState<BusRoute[]>([
|
||||
{ id: "1", name: "Cairo to Alexandria", departure: "06:00", arrival: "09:00", price: 100, capacity: 50, status: "active" },
|
||||
{ id: "2", name: "Cairo to Giza", departure: "07:30", arrival: "08:30", price: 50, capacity: 40, status: "active" },
|
||||
]);
|
||||
|
||||
const [tickets, setTickets] = useState<Ticket[]>([
|
||||
{ id: "1", routeId: "1", passengerName: "Ahmed Hassan", seatNumber: "A1", bookingDate: "2025-01-15", status: "booked" },
|
||||
{ id: "2", routeId: "1", passengerName: "Fatima Mohamed", seatNumber: "A2", bookingDate: "2025-01-15", status: "booked" },
|
||||
]);
|
||||
|
||||
const [editingRoute, setEditingRoute] = useState<BusRoute | null>(null);
|
||||
const [editingTicket, setEditingTicket] = useState<Ticket | null>(null);
|
||||
const [showRouteForm, setShowRouteForm] = useState(false);
|
||||
const [showTicketForm, setShowTicketForm] = useState(false);
|
||||
|
||||
const handleDeleteRoute = (id: string) => {
|
||||
setRoutes(routes.filter(r => r.id !== id));
|
||||
};
|
||||
|
||||
const handleDeleteTicket = (id: string) => {
|
||||
setTickets(tickets.filter(t => t.id !== id));
|
||||
};
|
||||
|
||||
const handleSaveRoute = (route: BusRoute) => {
|
||||
if (editingRoute) {
|
||||
setRoutes(routes.map(r => r.id === route.id ? route : r));
|
||||
setEditingRoute(null);
|
||||
} else {
|
||||
setRoutes([...routes, { ...route, id: String(Date.now()) }]);
|
||||
}
|
||||
setShowRouteForm(false);
|
||||
};
|
||||
|
||||
const handleSaveTicket = (ticket: Ticket) => {
|
||||
if (editingTicket) {
|
||||
setTickets(tickets.map(t => t.id === ticket.id ? ticket : t));
|
||||
setEditingTicket(null);
|
||||
} else {
|
||||
setTickets([...tickets, { ...ticket, id: String(Date.now()) }]);
|
||||
}
|
||||
setShowTicketForm(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="directional-hover"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="pill"
|
||||
contentWidth="compact"
|
||||
sizing="mediumLargeSizeLargeTitles"
|
||||
background="aurora"
|
||||
cardStyle="inset"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple
|
||||
brandName="BusTicket"
|
||||
navItems={[
|
||||
{ name: "Book Now", id: "pricing" },
|
||||
{ name: "How It Works", id: "features" },
|
||||
{ name: "FAQ", id: "faq" },
|
||||
{ name: "Contact", id: "contact" },
|
||||
{ name: "Admin", id: "admin" }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 py-12">
|
||||
<div className="max-w-7xl mx-auto px-4">
|
||||
<h1 className="text-4xl font-bold text-slate-900 mb-12">Admin Dashboard</h1>
|
||||
|
||||
{/* Bus Routes Management */}
|
||||
<div className="mb-12">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-2xl font-bold text-slate-800">Bus Routes Management</h2>
|
||||
<button
|
||||
onClick={() => {
|
||||
setEditingRoute(null);
|
||||
setShowRouteForm(true);
|
||||
}}
|
||||
className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition"
|
||||
>
|
||||
<Plus size={20} />
|
||||
Add Route
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-100 border-b">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Route Name</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Departure</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Arrival</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Price (EGP)</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Capacity</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Status</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{routes.map(route => (
|
||||
<tr key={route.id} className="hover:bg-slate-50">
|
||||
<td className="px-6 py-3 text-slate-900">{route.name}</td>
|
||||
<td className="px-6 py-3 text-slate-900">{route.departure}</td>
|
||||
<td className="px-6 py-3 text-slate-900">{route.arrival}</td>
|
||||
<td className="px-6 py-3 text-slate-900">{route.price}</td>
|
||||
<td className="px-6 py-3 text-slate-900">{route.capacity}</td>
|
||||
<td className="px-6 py-3">
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-semibold ${
|
||||
route.status === "active"
|
||||
? "bg-green-100 text-green-700"
|
||||
: "bg-red-100 text-red-700"
|
||||
}`}>
|
||||
{route.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-3 flex gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
setEditingRoute(route);
|
||||
setShowRouteForm(true);
|
||||
}}
|
||||
className="p-2 text-blue-600 hover:bg-blue-50 rounded transition"
|
||||
>
|
||||
<Edit2 size={18} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDeleteRoute(route.id)}
|
||||
className="p-2 text-red-600 hover:bg-red-50 rounded transition"
|
||||
>
|
||||
<Trash2 size={18} />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Ticket Management */}
|
||||
<div className="mb-12">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<h2 className="text-2xl font-bold text-slate-800">Ticket Management</h2>
|
||||
<button
|
||||
onClick={() => {
|
||||
setEditingTicket(null);
|
||||
setShowTicketForm(true);
|
||||
}}
|
||||
className="flex items-center gap-2 bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition"
|
||||
>
|
||||
<Plus size={20} />
|
||||
Add Ticket
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead className="bg-slate-100 border-b">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Passenger Name</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Route</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Seat Number</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Booking Date</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Status</th>
|
||||
<th className="px-6 py-3 text-left text-sm font-semibold text-slate-700">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{tickets.map(ticket => {
|
||||
const route = routes.find(r => r.id === ticket.routeId);
|
||||
return (
|
||||
<tr key={ticket.id} className="hover:bg-slate-50">
|
||||
<td className="px-6 py-3 text-slate-900">{ticket.passengerName}</td>
|
||||
<td className="px-6 py-3 text-slate-900">{route?.name || 'N/A'}</td>
|
||||
<td className="px-6 py-3 text-slate-900">{ticket.seatNumber}</td>
|
||||
<td className="px-6 py-3 text-slate-900">{ticket.bookingDate}</td>
|
||||
<td className="px-6 py-3">
|
||||
<span className={`px-3 py-1 rounded-full text-xs font-semibold ${
|
||||
ticket.status === "booked"
|
||||
? "bg-blue-100 text-blue-700"
|
||||
: ticket.status === "completed"
|
||||
? "bg-green-100 text-green-700"
|
||||
: "bg-red-100 text-red-700"
|
||||
}`}>
|
||||
{ticket.status}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-6 py-3 flex gap-2">
|
||||
<button
|
||||
onClick={() => {
|
||||
setEditingTicket(ticket);
|
||||
setShowTicketForm(true);
|
||||
}}
|
||||
className="p-2 text-blue-600 hover:bg-blue-50 rounded transition"
|
||||
>
|
||||
<Edit2 size={18} />
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDeleteTicket(ticket.id)}
|
||||
className="p-2 text-red-600 hover:bg-red-50 rounded transition"
|
||||
>
|
||||
<Trash2 size={18} />
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterBase
|
||||
logoText="BusTicket"
|
||||
copyrightText="© 2025 | BusTicket Egypt. All rights reserved."
|
||||
columns={[
|
||||
{
|
||||
title: "Quick Links", items: [
|
||||
{ label: "Book Tickets", href: "pricing" },
|
||||
{ label: "Track Bus", href: "#" },
|
||||
{ label: "Routes", href: "#" },
|
||||
{ label: "Promotions", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "#" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Contact", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Support", items: [
|
||||
{ label: "Help Center", href: "faq" },
|
||||
{ label: "Safety", href: "#" },
|
||||
{ label: "Terms & Conditions", href: "#" },
|
||||
{ label: "Privacy Policy", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Bus Company Info", items: [
|
||||
{ label: "Fleet Details", href: "#" },
|
||||
{ label: "Driver Qualifications", href: "#" },
|
||||
{ label: "Maintenance Schedule", href: "#" },
|
||||
{ label: "Safety Records", href: "#" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
391
src/app/booking/page.tsx
Normal file
391
src/app/booking/page.tsx
Normal file
@@ -0,0 +1,391 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleApple from "@/components/navbar/NavbarStyleApple/NavbarStyleApple";
|
||||
import HeroOverlay from "@/components/sections/hero/HeroOverlay";
|
||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||
import { useState } from "react";
|
||||
import { MapPin, Calendar, Users, Search, Heart, Clock, Users2 } from "lucide-react";
|
||||
|
||||
interface SearchFormData {
|
||||
departure: string;
|
||||
arrival: string;
|
||||
date: string;
|
||||
passengers: number;
|
||||
}
|
||||
|
||||
interface Bus {
|
||||
id: string;
|
||||
name: string;
|
||||
departure: string;
|
||||
arrival: string;
|
||||
departureTime: string;
|
||||
arrivalTime: string;
|
||||
duration: string;
|
||||
price: number;
|
||||
availableSeats: number;
|
||||
totalSeats: number;
|
||||
amenities: string[];
|
||||
image?: string;
|
||||
}
|
||||
|
||||
interface Seat {
|
||||
id: string;
|
||||
number: number;
|
||||
isAvailable: boolean;
|
||||
isSelected: boolean;
|
||||
}
|
||||
|
||||
const mockBuses: Bus[] = [
|
||||
{
|
||||
id: "1", name: "Premium Express", departure: "Cairo", arrival: "Alexandria", departureTime: "08:00 AM", arrivalTime: "11:00 AM", duration: "3h", price: 100,
|
||||
availableSeats: 12,
|
||||
totalSeats: 40,
|
||||
amenities: ["WiFi", "Air Conditioning", "USB Charging"],
|
||||
image: "http://img.b2bpic.net/free-photo/most-comfortable-means-transportation-business-people_329181-2791.jpg"
|
||||
},
|
||||
{
|
||||
id: "2", name: "Comfort Deluxe", departure: "Cairo", arrival: "Alexandria", departureTime: "10:30 AM", arrivalTime: "01:30 PM", duration: "3h", price: 150,
|
||||
availableSeats: 8,
|
||||
totalSeats: 30,
|
||||
amenities: ["WiFi", "Air Conditioning", "USB Charging", "Snacks"],
|
||||
image: "http://img.b2bpic.net/free-photo/empty-subway-train-barcelona-spain_1268-17854.jpg"
|
||||
},
|
||||
{
|
||||
id: "3", name: "Economy Max", departure: "Cairo", arrival: "Alexandria", departureTime: "02:00 PM", arrivalTime: "05:00 PM", duration: "3h", price: 80,
|
||||
availableSeats: 25,
|
||||
totalSeats: 50,
|
||||
amenities: ["Air Conditioning"],
|
||||
image: "http://img.b2bpic.net/free-photo/hand-with-credit-card-laptop_1232-619.jpg"
|
||||
}
|
||||
];
|
||||
|
||||
const generateSeats = (totalSeats: number): Seat[] => {
|
||||
return Array.from({ length: totalSeats }, (_, i) => ({
|
||||
id: `seat-${i + 1}`,
|
||||
number: i + 1,
|
||||
isAvailable: Math.random() > 0.3,
|
||||
isSelected: false
|
||||
}));
|
||||
};
|
||||
|
||||
export default function BookingPage() {
|
||||
const [searchData, setSearchData] = useState<SearchFormData>({
|
||||
departure: "", arrival: "", date: "", passengers: 1
|
||||
});
|
||||
const [searchResults, setSearchResults] = useState<Bus[]>([]);
|
||||
const [hasSearched, setHasSearched] = useState(false);
|
||||
const [selectedBus, setSelectedBus] = useState<Bus | null>(null);
|
||||
const [seats, setSeats] = useState<Seat[]>([]);
|
||||
const [selectedSeats, setSelectedSeats] = useState<string[]>([]);
|
||||
|
||||
const handleSearch = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (searchData.departure && searchData.arrival && searchData.date) {
|
||||
setSearchResults(mockBuses);
|
||||
setHasSearched(true);
|
||||
setSelectedBus(null);
|
||||
setSeats([]);
|
||||
setSelectedSeats([]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSelectBus = (bus: Bus) => {
|
||||
setSelectedBus(bus);
|
||||
setSeats(generateSeats(bus.totalSeats));
|
||||
setSelectedSeats([]);
|
||||
};
|
||||
|
||||
const handleSeatToggle = (seatId: string) => {
|
||||
const seat = seats.find(s => s.id === seatId);
|
||||
if (!seat || !seat.isAvailable) return;
|
||||
|
||||
if (selectedSeats.includes(seatId)) {
|
||||
setSelectedSeats(selectedSeats.filter(s => s !== seatId));
|
||||
} else if (selectedSeats.length < searchData.passengers) {
|
||||
setSelectedSeats([...selectedSeats, seatId]);
|
||||
}
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setSearchData(prev => ({
|
||||
...prev,
|
||||
[name]: name === "passengers" ? parseInt(value) : value
|
||||
}));
|
||||
};
|
||||
|
||||
const totalPrice = selectedBus ? selectedBus.price * selectedSeats.length : 0;
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="directional-hover"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="pill"
|
||||
contentWidth="compact"
|
||||
sizing="mediumLargeSizeLargeTitles"
|
||||
background="aurora"
|
||||
cardStyle="inset"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple
|
||||
brandName="BusTicket"
|
||||
navItems={[
|
||||
{ name: "Book Now", id: "/booking" },
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "FAQ", id: "faq" },
|
||||
{ name: "Contact", id: "contact" }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="hero" data-section="hero">
|
||||
<HeroOverlay
|
||||
title="Find & Book Your Perfect Bus"
|
||||
description="Search from hundreds of buses, select your seats, and book instantly"
|
||||
tag="Easy Booking"
|
||||
buttons={[
|
||||
{ text: "Back Home", href: "/" }
|
||||
]}
|
||||
imageSrc="http://img.b2bpic.net/free-photo/most-comfortable-means-transportation-business-people_329181-2791.jpg"
|
||||
imageAlt="Bus booking platform"
|
||||
showBlur={true}
|
||||
showDimOverlay={false}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div id="search" data-section="search" className="w-full bg-gradient-to-b from-transparent via-transparent to-background py-12 px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<form onSubmit={handleSearch} className="bg-card rounded-2xl p-8 shadow-lg border border-border/20">
|
||||
<h2 className="text-2xl font-bold mb-6 text-foreground">Search Buses</h2>
|
||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-4 mb-6">
|
||||
<div className="flex flex-col">
|
||||
<label className="text-sm font-medium text-foreground/70 mb-2">From</label>
|
||||
<input
|
||||
type="text"
|
||||
name="departure"
|
||||
value={searchData.departure}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Departure city"
|
||||
className="px-4 py-2 rounded-lg bg-background border border-border/20 text-foreground placeholder-foreground/50 focus:outline-none focus:border-primary-cta"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<label className="text-sm font-medium text-foreground/70 mb-2">To</label>
|
||||
<input
|
||||
type="text"
|
||||
name="arrival"
|
||||
value={searchData.arrival}
|
||||
onChange={handleInputChange}
|
||||
placeholder="Arrival city"
|
||||
className="px-4 py-2 rounded-lg bg-background border border-border/20 text-foreground placeholder-foreground/50 focus:outline-none focus:border-primary-cta"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<label className="text-sm font-medium text-foreground/70 mb-2">Date</label>
|
||||
<input
|
||||
type="date"
|
||||
name="date"
|
||||
value={searchData.date}
|
||||
onChange={handleInputChange}
|
||||
className="px-4 py-2 rounded-lg bg-background border border-border/20 text-foreground focus:outline-none focus:border-primary-cta"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<label className="text-sm font-medium text-foreground/70 mb-2">Passengers</label>
|
||||
<select
|
||||
name="passengers"
|
||||
value={searchData.passengers}
|
||||
onChange={handleInputChange}
|
||||
className="px-4 py-2 rounded-lg bg-background border border-border/20 text-foreground focus:outline-none focus:border-primary-cta"
|
||||
>
|
||||
{[1, 2, 3, 4, 5, 6].map(n => <option key={n} value={n}>{n}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div className="flex flex-col justify-end">
|
||||
<button
|
||||
type="submit"
|
||||
className="px-6 py-2 bg-primary-cta text-white rounded-lg hover:bg-primary-cta/90 transition-colors flex items-center justify-center gap-2 font-medium"
|
||||
>
|
||||
<Search size={18} />
|
||||
Search
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasSearched && !selectedBus && (
|
||||
<div id="bus-list" data-section="bus-list" className="w-full py-12 px-4">
|
||||
<div className="max-w-6xl mx-auto">
|
||||
<h2 className="text-2xl font-bold mb-8 text-foreground">Available Buses</h2>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
{searchResults.map(bus => (
|
||||
<div
|
||||
key={bus.id}
|
||||
className="bg-card rounded-xl p-6 border border-border/20 hover:shadow-lg transition-shadow cursor-pointer"
|
||||
onClick={() => handleSelectBus(bus)}
|
||||
>
|
||||
<div className="grid grid-cols-1 md:grid-cols-5 gap-6 items-center">
|
||||
<div>
|
||||
<h3 className="font-bold text-lg text-foreground mb-1">{bus.name}</h3>
|
||||
<p className="text-sm text-foreground/60">{bus.departure} → {bus.arrival}</p>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-2xl font-bold text-foreground">{bus.departureTime}</p>
|
||||
<p className="text-xs text-foreground/60">{bus.duration}</p>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-2xl font-bold text-foreground">{bus.arrivalTime}</p>
|
||||
<p className="text-xs text-foreground/60">Arrival</p>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<p className="text-sm text-foreground/70">Seats Available</p>
|
||||
<p className="text-xl font-bold text-accent">{bus.availableSeats}/{bus.totalSeats}</p>
|
||||
<div className="flex gap-1 mt-2">
|
||||
{bus.amenities.map((amenity, i) => (
|
||||
<span key={i} className="text-xs bg-primary-cta/10 text-primary-cta px-2 py-1 rounded">
|
||||
{amenity}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-3xl font-bold text-primary-cta">{bus.price} EGP</p>
|
||||
<p className="text-sm text-foreground/60 mt-2">per seat</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{selectedBus && (
|
||||
<div id="seat-selection" data-section="seat-selection" className="w-full py-12 px-4">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<button
|
||||
onClick={() => setSelectedBus(null)}
|
||||
className="mb-6 px-4 py-2 bg-secondary-cta text-foreground rounded-lg hover:bg-secondary-cta/80 transition-colors"
|
||||
>
|
||||
← Back to Buses
|
||||
</button>
|
||||
|
||||
<div className="bg-card rounded-xl p-8 border border-border/20">
|
||||
<div className="mb-8">
|
||||
<h2 className="text-2xl font-bold text-foreground mb-2">{selectedBus.name}</h2>
|
||||
<p className="text-foreground/60">{selectedBus.departure} → {selectedBus.arrival} | {selectedBus.departureTime}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-8">
|
||||
<h3 className="text-lg font-bold text-foreground mb-4">Select Your Seats</h3>
|
||||
<div className="bg-background rounded-lg p-6 mb-4">
|
||||
<p className="text-sm text-foreground/60 mb-4 text-center">Select {searchData.passengers} seat{searchData.passengers !== 1 ? 's' : ''}</p>
|
||||
|
||||
<div className="grid grid-cols-6 md:grid-cols-10 gap-2 justify-center max-w-md mx-auto">
|
||||
{seats.map(seat => (
|
||||
<button
|
||||
key={seat.id}
|
||||
onClick={() => handleSeatToggle(seat.id)}
|
||||
disabled={!seat.isAvailable}
|
||||
className={`aspect-square rounded-lg text-xs font-semibold transition-colors ${
|
||||
!seat.isAvailable
|
||||
? "bg-foreground/20 text-foreground/40 cursor-not-allowed"
|
||||
: selectedSeats.includes(seat.id)
|
||||
? "bg-primary-cta text-white"
|
||||
: "bg-accent/20 text-foreground hover:bg-accent/40"
|
||||
}`}
|
||||
>
|
||||
{seat.number}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-6 justify-center text-sm">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-4 h-4 rounded bg-accent/20"></div>
|
||||
<span className="text-foreground/70">Available</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-4 h-4 rounded bg-primary-cta"></div>
|
||||
<span className="text-foreground/70">Selected</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-4 h-4 rounded bg-foreground/20"></div>
|
||||
<span className="text-foreground/70">Booked</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{selectedSeats.length > 0 && (
|
||||
<div className="border-t border-border/20 pt-6">
|
||||
<div className="mb-4">
|
||||
<p className="text-foreground/70 mb-2">Selected Seats:</p>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
{selectedSeats.map(seatId => {
|
||||
const seat = seats.find(s => s.id === seatId);
|
||||
return (
|
||||
<span key={seatId} className="bg-primary-cta/20 text-primary-cta px-3 py-1 rounded-lg text-sm font-medium">
|
||||
Seat {seat?.number}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-between items-center pt-4 border-t border-border/20">
|
||||
<div>
|
||||
<p className="text-foreground/70 text-sm">Total Price:</p>
|
||||
<p className="text-3xl font-bold text-primary-cta">{totalPrice} EGP</p>
|
||||
</div>
|
||||
<button className="px-8 py-3 bg-primary-cta text-white rounded-lg hover:bg-primary-cta/90 transition-colors font-bold">
|
||||
Proceed to Payment
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterBase
|
||||
logoText="BusTicket"
|
||||
copyrightText="© 2025 | BusTicket Egypt. All rights reserved."
|
||||
columns={[
|
||||
{
|
||||
title: "Quick Links", items: [
|
||||
{ label: "Book Tickets", href: "/booking" },
|
||||
{ label: "Track Bus", href: "#" },
|
||||
{ label: "Routes", href: "#" },
|
||||
{ label: "Promotions", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "#" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Contact", href: "contact" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Support", items: [
|
||||
{ label: "Help Center", href: "faq" },
|
||||
{ label: "Safety", href: "#" },
|
||||
{ label: "Terms & Conditions", href: "#" },
|
||||
{ label: "Privacy Policy", href: "#" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
400
src/app/bookings/page.tsx
Normal file
400
src/app/bookings/page.tsx
Normal file
@@ -0,0 +1,400 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleApple from "@/components/navbar/NavbarStyleApple/NavbarStyleApple";
|
||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||
import { useState } from "react";
|
||||
import { Trash2, MapPin, Calendar, Users, CreditCard, CheckCircle } from "lucide-react";
|
||||
|
||||
interface Booking {
|
||||
id: string;
|
||||
route: string;
|
||||
date: string;
|
||||
passengers: number;
|
||||
price: number;
|
||||
status: "confirmed" | "pending" | "completed";
|
||||
seatNumbers: string[];
|
||||
paymentMethod: string;
|
||||
bookingDate: string;
|
||||
}
|
||||
|
||||
interface ConfirmationData {
|
||||
bookingId: string;
|
||||
route: string;
|
||||
date: string;
|
||||
passengers: number;
|
||||
totalPrice: number;
|
||||
seatNumbers: string[];
|
||||
}
|
||||
|
||||
export default function BookingsPage() {
|
||||
const [bookings, setBookings] = useState<Booking[]>([
|
||||
{
|
||||
id: "BT-2025-001", route: "Cairo to Alexandria", date: "2025-02-15", passengers: 2,
|
||||
price: 200,
|
||||
status: "confirmed", seatNumbers: ["A5", "A6"],
|
||||
paymentMethod: "Credit Card", bookingDate: "2025-01-20"
|
||||
},
|
||||
{
|
||||
id: "BT-2025-002", route: "Giza to Aswan", date: "2025-02-20", passengers: 1,
|
||||
price: 150,
|
||||
status: "pending", seatNumbers: ["B12"],
|
||||
paymentMethod: "Fawry", bookingDate: "2025-01-22"
|
||||
},
|
||||
{
|
||||
id: "BT-2025-003", route: "Mansoura to Cairo", date: "2025-01-25", passengers: 3,
|
||||
price: 300,
|
||||
status: "completed", seatNumbers: ["C1", "C2", "C3"],
|
||||
paymentMethod: "Debit Card", bookingDate: "2025-01-18"
|
||||
}
|
||||
]);
|
||||
|
||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||
const [confirmationData, setConfirmationData] = useState<ConfirmationData | null>(null);
|
||||
const [showPaymentModal, setShowPaymentModal] = useState(false);
|
||||
const [selectedBooking, setSelectedBooking] = useState<Booking | null>(null);
|
||||
const [paymentMethod, setPaymentMethod] = useState("card");
|
||||
|
||||
const handleBooking = (booking: Booking) => {
|
||||
setConfirmationData({
|
||||
bookingId: booking.id,
|
||||
route: booking.route,
|
||||
date: booking.date,
|
||||
passengers: booking.passengers,
|
||||
totalPrice: booking.price,
|
||||
seatNumbers: booking.seatNumbers
|
||||
});
|
||||
setShowConfirmation(true);
|
||||
};
|
||||
|
||||
const handlePayment = (booking: Booking) => {
|
||||
setSelectedBooking(booking);
|
||||
setShowPaymentModal(true);
|
||||
};
|
||||
|
||||
const processPayment = () => {
|
||||
if (selectedBooking) {
|
||||
setBookings(
|
||||
bookings.map((b) =>
|
||||
b.id === selectedBooking.id ? { ...b, status: "confirmed" as const } : b
|
||||
)
|
||||
);
|
||||
alert(`Payment of ${selectedBooking.price} EGP processed successfully!`);
|
||||
setShowPaymentModal(false);
|
||||
setSelectedBooking(null);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancelBooking = (bookingId: string) => {
|
||||
setBookings(bookings.filter((b) => b.id !== bookingId));
|
||||
};
|
||||
|
||||
const getStatusBadgeColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "confirmed":
|
||||
return "bg-green-100 text-green-800";
|
||||
case "pending":
|
||||
return "bg-yellow-100 text-yellow-800";
|
||||
case "completed":
|
||||
return "bg-blue-100 text-blue-800";
|
||||
default:
|
||||
return "bg-gray-100 text-gray-800";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="directional-hover"
|
||||
defaultTextAnimation="reveal-blur"
|
||||
borderRadius="pill"
|
||||
contentWidth="compact"
|
||||
sizing="mediumLargeSizeLargeTitles"
|
||||
background="aurora"
|
||||
cardStyle="inset"
|
||||
primaryButtonStyle="gradient"
|
||||
secondaryButtonStyle="glass"
|
||||
headingFontWeight="medium"
|
||||
>
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleApple
|
||||
brandName="BusTicket"
|
||||
navItems={[
|
||||
{ name: "Book Now", id: "pricing" },
|
||||
{ name: "How It Works", id: "features" },
|
||||
{ name: "FAQ", id: "faq" },
|
||||
{ name: "Bookings", id: "/bookings" },
|
||||
{ name: "Contact", id: "contact" }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<main className="min-h-screen bg-gradient-to-b from-background to-card pt-32 pb-20">
|
||||
<div className="max-w-4xl mx-auto px-4">
|
||||
<div className="text-center mb-12">
|
||||
<h1 className="text-4xl md:text-5xl font-bold mb-4 text-foreground">My Bookings</h1>
|
||||
<p className="text-lg text-foreground/70">Manage and view all your bus ticket bookings</p>
|
||||
</div>
|
||||
|
||||
{bookings.length === 0 ? (
|
||||
<div className="bg-card rounded-lg p-12 text-center border border-background-accent">
|
||||
<p className="text-foreground/70 mb-6">No bookings found. Start by booking your next trip!</p>
|
||||
<a
|
||||
href="/"
|
||||
className="inline-block px-6 py-3 bg-primary-cta text-white rounded-lg hover:opacity-90 transition"
|
||||
>
|
||||
Book a Ticket
|
||||
</a>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
{bookings.map((booking) => (
|
||||
<div
|
||||
key={booking.id}
|
||||
className="bg-card rounded-lg border border-background-accent overflow-hidden hover:shadow-lg transition"
|
||||
>
|
||||
<div className="p-6">
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold text-foreground mb-2">Booking {booking.id}</h3>
|
||||
<span
|
||||
className={`inline-block px-4 py-1 rounded-full text-sm font-medium ${
|
||||
getStatusBadgeColor(booking.status)
|
||||
}`}
|
||||
>
|
||||
{booking.status.charAt(0).toUpperCase() + booking.status.slice(1)}
|
||||
</span>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-2xl font-bold text-primary-cta">{booking.price} EGP</p>
|
||||
<p className="text-sm text-foreground/60">Total Price</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
|
||||
<div className="flex items-start gap-3">
|
||||
<MapPin className="w-5 h-5 text-secondary-cta mt-1" />
|
||||
<div>
|
||||
<p className="text-sm text-foreground/60">Route</p>
|
||||
<p className="text-foreground font-medium">{booking.route}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Calendar className="w-5 h-5 text-secondary-cta mt-1" />
|
||||
<div>
|
||||
<p className="text-sm text-foreground/60">Departure Date</p>
|
||||
<p className="text-foreground font-medium">{new Date(booking.date).toLocaleDateString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<Users className="w-5 h-5 text-secondary-cta mt-1" />
|
||||
<div>
|
||||
<p className="text-sm text-foreground/60">Passengers & Seats</p>
|
||||
<p className="text-foreground font-medium">{booking.passengers} × {booking.seatNumbers.join(", ")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3">
|
||||
<CreditCard className="w-5 h-5 text-secondary-cta mt-1" />
|
||||
<div>
|
||||
<p className="text-sm text-foreground/60">Payment Method</p>
|
||||
<p className="text-foreground font-medium">{booking.paymentMethod}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-3 justify-end">
|
||||
{booking.status === "confirmed" && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => handleBooking(booking)}
|
||||
className="px-4 py-2 border border-primary-cta text-primary-cta rounded-lg hover:bg-primary-cta hover:text-white transition"
|
||||
>
|
||||
View Confirmation
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleCancelBooking(booking.id)}
|
||||
className="px-4 py-2 border border-red-500 text-red-500 rounded-lg hover:bg-red-500 hover:text-white transition flex items-center gap-2"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
Cancel
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{booking.status === "pending" && (
|
||||
<>
|
||||
<button
|
||||
onClick={() => handlePayment(booking)}
|
||||
className="px-6 py-2 bg-primary-cta text-white rounded-lg hover:opacity-90 transition"
|
||||
>
|
||||
Complete Payment
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleCancelBooking(booking.id)}
|
||||
className="px-4 py-2 border border-red-500 text-red-500 rounded-lg hover:bg-red-500 hover:text-white transition flex items-center gap-2"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
Cancel
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
{booking.status === "completed" && (
|
||||
<button
|
||||
onClick={() => handleBooking(booking)}
|
||||
className="px-4 py-2 border border-primary-cta text-primary-cta rounded-lg hover:bg-primary-cta hover:text-white transition flex items-center gap-2"
|
||||
>
|
||||
<CheckCircle className="w-4 h-4" />
|
||||
View Details
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* Confirmation Modal */}
|
||||
{showConfirmation && confirmationData && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card rounded-lg max-w-md w-full p-8 border border-background-accent">
|
||||
<div className="text-center mb-6">
|
||||
<CheckCircle className="w-12 h-12 text-green-500 mx-auto mb-4" />
|
||||
<h2 className="text-2xl font-bold text-foreground mb-2">Booking Confirmed</h2>
|
||||
<p className="text-foreground/70">Your ticket has been successfully booked</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4 mb-8 p-4 bg-background rounded-lg">
|
||||
<div className="flex justify-between">
|
||||
<span className="text-foreground/70">Booking ID:</span>
|
||||
<span className="font-semibold text-foreground">{confirmationData.bookingId}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-foreground/70">Route:</span>
|
||||
<span className="font-semibold text-foreground">{confirmationData.route}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-foreground/70">Date:</span>
|
||||
<span className="font-semibold text-foreground">{new Date(confirmationData.date).toLocaleDateString()}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-foreground/70">Passengers:</span>
|
||||
<span className="font-semibold text-foreground">{confirmationData.passengers}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-foreground/70">Seats:</span>
|
||||
<span className="font-semibold text-foreground">{confirmationData.seatNumbers.join(", ")}</span>
|
||||
</div>
|
||||
<div className="border-t border-background-accent pt-4 flex justify-between">
|
||||
<span className="text-foreground/70 font-semibold">Total:</span>
|
||||
<span className="text-xl font-bold text-primary-cta">{confirmationData.totalPrice} EGP</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onClick={() => setShowConfirmation(false)}
|
||||
className="w-full px-4 py-3 bg-primary-cta text-white rounded-lg hover:opacity-90 transition font-semibold"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Payment Modal */}
|
||||
{showPaymentModal && selectedBooking && (
|
||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-card rounded-lg max-w-md w-full p-8 border border-background-accent">
|
||||
<h2 className="text-2xl font-bold text-foreground mb-6">Payment Details</h2>
|
||||
|
||||
<div className="mb-6 p-4 bg-background rounded-lg">
|
||||
<div className="flex justify-between mb-2">
|
||||
<span className="text-foreground/70">Booking:</span>
|
||||
<span className="font-semibold text-foreground">{selectedBooking.id}</span>
|
||||
</div>
|
||||
<div className="flex justify-between">
|
||||
<span className="text-foreground/70">Route:</span>
|
||||
<span className="font-semibold text-foreground">{selectedBooking.route}</span>
|
||||
</div>
|
||||
<div className="border-t border-background-accent pt-4 mt-4 flex justify-between">
|
||||
<span className="text-foreground/70 font-semibold">Amount Due:</span>
|
||||
<span className="text-xl font-bold text-primary-cta">{selectedBooking.price} EGP</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-6">
|
||||
<label className="block text-sm font-semibold text-foreground mb-3">Payment Method</label>
|
||||
<div className="space-y-2">
|
||||
{[
|
||||
{ id: "card", label: "Credit/Debit Card" },
|
||||
{ id: "fawry", label: "Fawry" },
|
||||
{ id: "wallet", label: "Mobile Wallet" }
|
||||
].map((method) => (
|
||||
<label key={method.id} className="flex items-center gap-3 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
name="payment-method"
|
||||
value={method.id}
|
||||
checked={paymentMethod === method.id}
|
||||
onChange={(e) => setPaymentMethod(e.target.value)}
|
||||
className="w-4 h-4"
|
||||
/>
|
||||
<span className="text-foreground">{method.label}</span>
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-3">
|
||||
<button
|
||||
onClick={() => setShowPaymentModal(false)}
|
||||
className="flex-1 px-4 py-3 border border-foreground/20 text-foreground rounded-lg hover:bg-background transition"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={processPayment}
|
||||
className="flex-1 px-4 py-3 bg-primary-cta text-white rounded-lg hover:opacity-90 transition font-semibold"
|
||||
>
|
||||
Pay {selectedBooking.price} EGP
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div id="footer" data-section="footer">
|
||||
<FooterBase
|
||||
logoText="BusTicket"
|
||||
copyrightText="© 2025 | BusTicket Egypt. All rights reserved."
|
||||
columns={[
|
||||
{
|
||||
title: "Quick Links", items: [
|
||||
{ label: "Book Tickets", href: "/" },
|
||||
{ label: "Track Bus", href: "#" },
|
||||
{ label: "Routes", href: "#" },
|
||||
{ label: "Promotions", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Company", items: [
|
||||
{ label: "About Us", href: "#" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Contact", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Support", items: [
|
||||
{ label: "Help Center", href: "#" },
|
||||
{ label: "Safety", href: "#" },
|
||||
{ label: "Terms & Conditions", href: "#" },
|
||||
{ label: "Privacy Policy", href: "#" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
"use client"
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleApple from '@/components/navbar/NavbarStyleApple/NavbarStyleApple';
|
||||
import HeroOverlay from '@/components/sections/hero/HeroOverlay';
|
||||
import FeatureCardTwentySix from '@/components/sections/feature/FeatureCardTwentySix';
|
||||
import PricingCardFive from '@/components/sections/pricing/PricingCardFive';
|
||||
import MetricCardSeven from '@/components/sections/metrics/MetricCardSeven';
|
||||
import SocialProofOne from '@/components/sections/socialProof/SocialProofOne';
|
||||
import TestimonialCardFive from '@/components/sections/testimonial/TestimonialCardFive';
|
||||
import FaqBase from '@/components/sections/faq/FaqBase';
|
||||
import FooterBase from '@/components/sections/footer/FooterBase';
|
||||
import { Armchair, Lock, MapPin, Smartphone } from 'lucide-react';
|
||||
import NavbarStyleApple from "@/components/navbar/NavbarStyleApple/NavbarStyleApple";
|
||||
import HeroOverlay from "@/components/sections/hero/HeroOverlay";
|
||||
import FeatureCardTwentySix from "@/components/sections/feature/FeatureCardTwentySix";
|
||||
import PricingCardFive from "@/components/sections/pricing/PricingCardFive";
|
||||
import MetricCardSeven from "@/components/sections/metrics/MetricCardSeven";
|
||||
import SocialProofOne from "@/components/sections/socialProof/SocialProofOne";
|
||||
import TestimonialCardFive from "@/components/sections/testimonial/TestimonialCardFive";
|
||||
import FaqBase from "@/components/sections/faq/FaqBase";
|
||||
import FooterBase from "@/components/sections/footer/FooterBase";
|
||||
import { Armchair, MapPin, Lock, Smartphone } from "lucide-react";
|
||||
|
||||
export default function LandingPage() {
|
||||
return (
|
||||
@@ -33,7 +33,8 @@ export default function LandingPage() {
|
||||
{ name: "Book Now", id: "pricing" },
|
||||
{ name: "How It Works", id: "features" },
|
||||
{ name: "FAQ", id: "faq" },
|
||||
{ name: "Contact", id: "contact" }
|
||||
{ name: "Contact", id: "contact" },
|
||||
{ name: "Admin", id: "admin" }
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
@@ -47,7 +48,7 @@ export default function LandingPage() {
|
||||
{ text: "Book Now", href: "pricing" },
|
||||
{ text: "Learn More", href: "features" }
|
||||
]}
|
||||
imageSrc="http://img.b2bpic.net/free-photo/full-shot-man-with-cup-outdoors_23-2148848986.jpg?_wi=1"
|
||||
imageSrc="http://img.b2bpic.net/free-photo/most-comfortable-means-transportation-business-people_329181-2791.jpg"
|
||||
imageAlt="Modern bus traveling on highway"
|
||||
showBlur={true}
|
||||
showDimOverlay={false}
|
||||
@@ -60,19 +61,19 @@ export default function LandingPage() {
|
||||
description="Experience modern travel with premium comfort, safety, and convenience"
|
||||
features={[
|
||||
{
|
||||
title: "Comfortable Seating", description: "Spacious, ergonomic seats for your comfort during long journeys", imageSrc: "http://img.b2bpic.net/free-photo/man-white-protection-suit-disinfecting-sanitizing-subway-train-interior-stop-spreading-highly-contagious-corona-virus_342744-482.jpg?_wi=1", imageAlt: "Comfortable bus interior seats", buttonIcon: Armchair,
|
||||
title: "Comfortable Seating", description: "Spacious, ergonomic seats for your comfort during long journeys", imageSrc: "http://img.b2bpic.net/free-photo/empty-subway-train-barcelona-spain_1268-17854.jpg", imageAlt: "Comfortable bus interior seats", buttonIcon: Armchair,
|
||||
buttonHref: "#"
|
||||
},
|
||||
{
|
||||
title: "Real-Time Tracking", description: "Track your bus location live from booking to arrival", imageSrc: "http://img.b2bpic.net/free-photo/closeup-smartphone-with-gps-application_1098-21636.jpg?_wi=1", imageAlt: "Live GPS tracking interface", buttonIcon: MapPin,
|
||||
title: "Real-Time Tracking", description: "Track your bus location live from booking to arrival", imageSrc: "http://img.b2bpic.net/free-vector/gradient-dark-mode-app-template_23-2150513310.jpg", imageAlt: "Live GPS tracking interface", buttonIcon: MapPin,
|
||||
buttonHref: "#"
|
||||
},
|
||||
{
|
||||
title: "Secure Payments", description: "Safe and encrypted payment processing with multiple options", imageSrc: "http://img.b2bpic.net/free-photo/close-up-female-hands-making-online-payment_1098-2337.jpg?_wi=1", imageAlt: "Secure payment processing", buttonIcon: Lock,
|
||||
title: "Secure Payments", description: "Safe and encrypted payment processing with multiple options", imageSrc: "http://img.b2bpic.net/free-photo/hand-with-credit-card-laptop_1232-619.jpg", imageAlt: "Secure payment processing", buttonIcon: Lock,
|
||||
buttonHref: "#"
|
||||
},
|
||||
{
|
||||
title: "Easy Booking", description: "Simple mobile-first interface to book in seconds", imageSrc: "http://img.b2bpic.net/free-vector/taxi-service-onboarding-app-screens_23-2148409509.jpg?_wi=1", imageAlt: "Mobile booking interface", buttonIcon: Smartphone,
|
||||
title: "Easy Booking", description: "Simple mobile-first interface to book in seconds", imageSrc: "http://img.b2bpic.net/free-vector/banking-app-interface_23-2148600205.jpg", imageAlt: "Mobile booking interface", buttonIcon: Smartphone,
|
||||
buttonHref: "#"
|
||||
}
|
||||
]}
|
||||
@@ -87,13 +88,13 @@ export default function LandingPage() {
|
||||
description="One flat rate for all routes. No hidden fees. Premium comfort included."
|
||||
plans={[
|
||||
{
|
||||
id: "economy", tag: "Standard Route", price: "100", period: "EGP/ticket", description: "Perfect for budget-conscious travelers who want comfort and safety", button: { text: "Book Now", href: "#contact" },
|
||||
id: "economy", tag: "Standard Route", price: "100", period: "EGP/ticket", description: "Perfect for budget-conscious travelers who want comfort and safety", button: { text: "Book Now", href: "pricing" },
|
||||
featuresTitle: "What's Included:", features: [
|
||||
"Comfortable air-conditioned seat", "Real-time GPS tracking", "24/7 customer support", "Free cancellation up to 2 hours"
|
||||
]
|
||||
},
|
||||
{
|
||||
id: "premium", tag: "Premium Plus", price: "150", period: "EGP/ticket", description: "Upgrade your journey with exclusive premium amenities and perks", button: { text: "Upgrade Now", href: "#contact" },
|
||||
id: "premium", tag: "Premium Plus", price: "150", period: "EGP/ticket", description: "Upgrade your journey with exclusive premium amenities and perks", button: { text: "Upgrade Now", href: "pricing" },
|
||||
featuresTitle: "Premium Includes:", features: [
|
||||
"Extra wide premium seat with footrest", "Complimentary snacks and beverages", "Priority boarding and seating", "Free travel insurance", "24/7 VIP customer support"
|
||||
]
|
||||
@@ -111,19 +112,13 @@ export default function LandingPage() {
|
||||
description="Join our growing community of satisfied travelers across Egypt"
|
||||
metrics={[
|
||||
{
|
||||
id: "1", value: "50K+", title: "Happy Passengers", items: [
|
||||
"95% customer satisfaction", "Trusted since 2020"
|
||||
]
|
||||
id: "1", value: "50K+", title: "Happy Passengers", items: ["95% customer satisfaction", "Trusted since 2020"]
|
||||
},
|
||||
{
|
||||
id: "2", value: "150+", title: "Routes Covered", items: [
|
||||
"Major cities connected", "Daily departures"
|
||||
]
|
||||
id: "2", value: "150+", title: "Routes Covered", items: ["Major cities connected", "Daily departures"]
|
||||
},
|
||||
{
|
||||
id: "3", value: "99.5%", title: "On-Time Arrival", items: [
|
||||
"Reliable scheduling", "Professional drivers"
|
||||
]
|
||||
id: "3", value: "99.5%", title: "On-Time Arrival", items: ["Reliable scheduling", "Professional drivers"]
|
||||
}
|
||||
]}
|
||||
animationType="slide-up"
|
||||
@@ -136,8 +131,11 @@ export default function LandingPage() {
|
||||
<SocialProofOne
|
||||
title="Trusted by Leading Travel Partners"
|
||||
description="Our platform is partnered with major travel agencies and tourism companies"
|
||||
names={[
|
||||
"Express Travel Co", "Journey Tours", "Travel Ventures", "Global Routes", "Adventure Lines", "Comfort Transit", "Rapid Journey"
|
||||
]}
|
||||
logos={[
|
||||
"http://img.b2bpic.net/free-vector/detailed-travel-logo_23-2148620592.jpg", "http://img.b2bpic.net/free-vector/detailed-travel-logo_23-2148611964.jpg", "http://img.b2bpic.net/free-vector/flat-pack-actual-food-truck-logos_23-2147672827.jpg", "https://webuild-dev.s3.eu-north-1.amazonaws.com/default/no-image.jpg?id=00oehk", "http://img.b2bpic.net/free-vector/flat-vintage-travel-logo_23-2148185174.jpg", "https://webuild-dev.s3.eu-north-1.amazonaws.com/default/no-image.jpg?id=2opczf", "http://img.b2bpic.net/free-vector/flat-truck-logo-template-collection_23-2148947412.jpg"
|
||||
"http://img.b2bpic.net/free-vector/logo-with-human-shape-star_1057-2127.jpg", "http://img.b2bpic.net/free-vector/detailed-travel-logo_23-2148631711.jpg", "http://img.b2bpic.net/free-vector/travel-logo-pack_23-2148614912.jpg", "http://img.b2bpic.net/free-vector/air-lines-company-logo-template_1071-18.jpg", "http://img.b2bpic.net/free-vector/detailed-travel-logo-template-with-slogan-placeholder_23-2148632798.jpg", "http://img.b2bpic.net/free-vector/logo-car-insurance_1057-2559.jpg", "http://img.b2bpic.net/free-vector/detailed-travel-logo_23-2148627268.jpg"
|
||||
]}
|
||||
textboxLayout="default"
|
||||
useInvertedBackground={false}
|
||||
@@ -152,22 +150,22 @@ export default function LandingPage() {
|
||||
description="Real experiences from thousands of satisfied customers"
|
||||
testimonials={[
|
||||
{
|
||||
id: "1", name: "Sarah Ahmed, Cairo", date: "Date: 15 January 2025", title: "Best booking experience ever!", quote: "I've used many bus booking platforms, but BusTicket is by far the easiest and most reliable. The real-time tracking gave me peace of mind, and the price was unbeatable.", tag: "Verified Traveler", avatarSrc: "http://img.b2bpic.net/free-photo/closeup-portrait-caucasian-happy-teacher-glasses_74855-9736.jpg", avatarAlt: "professional avatar portrait smiling business", imageSrc: "http://img.b2bpic.net/free-photo/full-shot-man-with-cup-outdoors_23-2148848986.jpg?_wi=2", imageAlt: "modern bus travel highway journey scenic"
|
||||
id: "1", name: "Sarah Ahmed, Cairo", date: "Date: 15 January 2025", title: "Best booking experience ever!", quote: "I've used many bus booking platforms, but BusTicket is by far the easiest and most reliable. The real-time tracking gave me peace of mind, and the price was unbeatable.", tag: "Verified Traveler", avatarSrc: "http://img.b2bpic.net/free-photo/portrait-handsome-confident-stylish-hipster-lambersexual-modelsexy-modern-man-dressed-elegant-suit-spectacles-fashion-male-posing-studio-near-blue-wall-looking-camera-glasses_158538-21056.jpg", avatarAlt: "Sarah Ahmed avatar", imageSrc: "http://img.b2bpic.net/free-photo/most-comfortable-means-transportation-business-people_329181-2791.jpg", imageAlt: "Bus travel"
|
||||
},
|
||||
{
|
||||
id: "2", name: "Mohamed Hassan, Alexandria", date: "Date: 10 January 2025", title: "Comfortable and affordable journey", quote: "Finally found a bus service that doesn't compromise on comfort. The seats are spacious, the driver was professional, and I arrived on time. Highly recommended!", tag: "Regular Customer", avatarSrc: "http://img.b2bpic.net/free-photo/medium-shot-man-wearing-suit_23-2149396254.jpg", avatarAlt: "man portrait professional avatar headshot", imageSrc: "http://img.b2bpic.net/free-photo/man-white-protection-suit-disinfecting-sanitizing-subway-train-interior-stop-spreading-highly-contagious-corona-virus_342744-482.jpg?_wi=2", imageAlt: "luxury bus interior comfortable seats amenities"
|
||||
id: "2", name: "Mohamed Hassan, Alexandria", date: "Date: 10 January 2025", title: "Comfortable and affordable journey", quote: "Finally found a bus service that doesn't compromise on comfort. The seats are spacious, the driver was professional, and I arrived on time. Highly recommended!", tag: "Regular Customer", avatarSrc: "http://img.b2bpic.net/free-photo/smiling-looking-happy-confident-friendly-offering-handshake-close-deal-cooperating_1194-632587.jpg", avatarAlt: "Mohamed Hassan avatar", imageSrc: "http://img.b2bpic.net/free-photo/empty-subway-train-barcelona-spain_1268-17854.jpg", imageAlt: "Comfortable seating"
|
||||
},
|
||||
{
|
||||
id: "3", name: "Fatima El-Din, Giza", date: "Date: 8 January 2025", title: "Transparent pricing, no surprises", quote: "I appreciate the straightforward 100 EGP pricing with no hidden fees. The booking process was quick, and the customer service team responded promptly to my questions.", tag: "First Time User", avatarSrc: "http://img.b2bpic.net/free-photo/young-beautiful-woman-smiling-posing-purple-wall_176420-2851.jpg", avatarAlt: "woman professional portrait avatar headshot", imageSrc: "http://img.b2bpic.net/free-vector/taxi-service-onboarding-app-screens_23-2148409509.jpg?_wi=2", imageAlt: "mobile app booking interface ticket reservation"
|
||||
id: "3", name: "Fatima El-Din, Giza", date: "Date: 8 January 2025", title: "Transparent pricing, no surprises", quote: "I appreciate the straightforward 100 EGP pricing with no hidden fees. The booking process was quick, and the customer service team responded promptly to my questions.", tag: "First Time User", avatarSrc: "http://img.b2bpic.net/free-photo/portrait-businesswoman_23-2148137687.jpg", avatarAlt: "Fatima El-Din avatar", imageSrc: "http://img.b2bpic.net/free-vector/banking-app-interface_23-2148600205.jpg", imageAlt: "Booking interface"
|
||||
},
|
||||
{
|
||||
id: "4", name: "Karim Yousef, Mansoura", date: "Date: 5 January 2025", title: "Safe and reliable transportation", quote: "Security and reliability are my top priorities when booking travel. BusTicket delivers on both fronts with real-time tracking and professional fleet management.", tag: "Safety Conscious", avatarSrc: "http://img.b2bpic.net/free-photo/studio-portrait-serious-bearded-male-dressed-suit_613910-5596.jpg", avatarAlt: "man professional avatar portrait headshot", imageSrc: "http://img.b2bpic.net/free-photo/close-up-female-hands-making-online-payment_1098-2337.jpg?_wi=2", imageAlt: "secure payment processing credit card safe"
|
||||
id: "4", name: "Karim Yousef, Mansoura", date: "Date: 5 January 2025", title: "Safe and reliable transportation", quote: "Security and reliability are my top priorities when booking travel. BusTicket delivers on both fronts with real-time tracking and professional fleet management.", tag: "Safety Conscious", avatarSrc: "http://img.b2bpic.net/free-photo/solar-panels-industry-technician-high-tech-factory-inspection-quality-control-production_482257-132256.jpg", avatarAlt: "Karim Yousef avatar", imageSrc: "http://img.b2bpic.net/free-photo/hand-with-credit-card-laptop_1232-619.jpg", imageAlt: "Secure payment"
|
||||
},
|
||||
{
|
||||
id: "5", name: "Layla Mohamed, Suez", date: "Date: 2 January 2025", title: "Perfect for budget travelers", quote: "100 EGP is an incredible price for the quality of service provided. I saved money on transportation and got better comfort than expected. Will definitely book again!", tag: "Budget Traveler", avatarSrc: "http://img.b2bpic.net/free-photo/closeup-portrait-caucasian-happy-teacher-glasses_74855-9736.jpg", avatarAlt: "professional avatar portrait smiling business", imageSrc: "http://img.b2bpic.net/free-photo/full-shot-man-with-cup-outdoors_23-2148848986.jpg?_wi=3", imageAlt: "modern bus travel highway journey scenic"
|
||||
id: "5", name: "Layla Mohamed, Suez", date: "Date: 2 January 2025", title: "Perfect for budget travelers", quote: "100 EGP is an incredible price for the quality of service provided. I saved money on transportation and got better comfort than expected. Will definitely book again!", tag: "Budget Traveler", avatarSrc: "http://img.b2bpic.net/free-photo/portrait-handsome-confident-stylish-hipster-lambersexual-modelsexy-modern-man-dressed-elegant-suit-spectacles-fashion-male-posing-studio-near-blue-wall-looking-camera-glasses_158538-21056.jpg", avatarAlt: "Layla Mohamed avatar", imageSrc: "http://img.b2bpic.net/free-photo/most-comfortable-means-transportation-business-people_329181-2791.jpg", imageAlt: "Bus travel"
|
||||
},
|
||||
{
|
||||
id: "6", name: "Ahmed Khalil, Aswan", date: "Date: 28 December 2024", title: "Excellent support team", quote: "Had a question about my booking and the support team responded within minutes. Their professionalism and helpfulness made me a loyal customer for life.", tag: "Excellent Service", avatarSrc: "http://img.b2bpic.net/free-photo/medium-shot-man-wearing-suit_23-2149396254.jpg", avatarAlt: "man portrait professional avatar headshot", imageSrc: "http://img.b2bpic.net/free-photo/closeup-smartphone-with-gps-application_1098-21636.jpg?_wi=2", imageAlt: "gps tracking app location real-time journey"
|
||||
id: "6", name: "Ahmed Khalil, Aswan", date: "Date: 28 December 2024", title: "Excellent support team", quote: "Had a question about my booking and the support team responded within minutes. Their professionalism and helpfulness made me a loyal customer for life.", tag: "Excellent Service", avatarSrc: "http://img.b2bpic.net/free-photo/smiling-looking-happy-confident-friendly-offering-handshake-close-deal-cooperating_1194-632587.jpg", avatarAlt: "Ahmed Khalil avatar", imageSrc: "http://img.b2bpic.net/free-vector/gradient-dark-mode-app-template_23-2150513310.jpg", imageAlt: "Real-time tracking"
|
||||
}
|
||||
]}
|
||||
textboxLayout="default"
|
||||
@@ -223,7 +221,7 @@ export default function LandingPage() {
|
||||
{ label: "About Us", href: "#" },
|
||||
{ label: "Careers", href: "#" },
|
||||
{ label: "Blog", href: "#" },
|
||||
{ label: "Contact", href: "contact" }
|
||||
{ label: "Contact", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -233,6 +231,14 @@ export default function LandingPage() {
|
||||
{ label: "Terms & Conditions", href: "#" },
|
||||
{ label: "Privacy Policy", href: "#" }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "Bus Company Info", items: [
|
||||
{ label: "Fleet Details", href: "#" },
|
||||
{ label: "Driver Qualifications", href: "#" },
|
||||
{ label: "Maintenance Schedule", href: "#" },
|
||||
{ label: "Safety Records", href: "#" }
|
||||
]
|
||||
}
|
||||
]}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user