first commit

This commit is contained in:
AnuragJangid18 2026-09-05 18:52:26 +05:30
parent e7365b06ac
commit a9e21cbe3c
9 changed files with 874 additions and 320 deletions

6
.env
View file

@ -20,4 +20,8 @@ DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWORD=Z41kfdw8e8@sql
DB_NAME=roadrentals
DB_NAME=roadrentals
# Admin Credentials
ADMIN_ID=Himanshu Jaiswal
ADMIN_PASSWORD=Himan@1314

View file

@ -114,214 +114,7 @@ app.post("/api/bookings", async (req, res) => {
}
});
// Pricing Engine & Car Data
const VEHICLES = [
// LUXURY TIER
{
id: 1,
name: "Audi A6 Premium",
type: "LUXURY",
seats: "4+1",
luggage: "2 Bags",
image: "audi_a6",
baseFarePerDay: 12000,
driverAllowancePerDay: 600,
extraKmRate: 45,
includedKmPerDay: 250,
badge: "Executive",
features: ["Leather Interior", "Dual Climate", "Premium Audio", "Sunroof"]
},
{
id: 2,
name: "BMW 7 Series",
type: "LUXURY",
seats: "4+1",
luggage: "3 Bags",
image: "bmw_7_series",
baseFarePerDay: 25000,
driverAllowancePerDay: 800,
extraKmRate: 85,
includedKmPerDay: 200,
badge: "VVIP Choice",
features: ["Rear-seat Entertainment", "Massage Seats", "Quiet Cabin", "Chauffeur Driven"]
},
{
id: 3,
name: "Mercedes S-Class",
type: "LUXURY",
seats: "4+1",
luggage: "2 Bags",
image: "mercedes_s_class",
baseFarePerDay: 28000,
driverAllowancePerDay: 800,
extraKmRate: 90,
includedKmPerDay: 200,
badge: "Ultimate Luxury",
features: ["Ambient Lighting", "Burmester Sound", "Premium Chauffeur"]
},
// SUV/MUV TIER
{
id: 4,
name: "Toyota Fortuner 4x4",
type: "SUV",
seats: "6+1",
luggage: "4 Bags",
image: "fortuner",
baseFarePerDay: 6500,
driverAllowancePerDay: 500,
extraKmRate: 25,
includedKmPerDay: 300,
badge: "Advanture King",
features: ["Off-road capable", "Powerful AC", "Spacious", "Rugged Built"]
},
{
id: 5,
name: "Innova Crysta",
type: "MUV",
seats: "7+1",
luggage: "3 Bags",
image: "innova_crysta",
baseFarePerDay: 4500,
driverAllowancePerDay: 400,
extraKmRate: 18,
includedKmPerDay: 300,
badge: "Best Seller",
features: ["Clean Interiors", "Captain Seats", "Smooth Ride", "Carrier Available"]
},
{
id: 6,
name: "Toyota Innova",
type: "MUV",
seats: "7+1",
luggage: "2 Bags",
image: "innova",
baseFarePerDay: 3800,
driverAllowancePerDay: 400,
extraKmRate: 16,
includedKmPerDay: 300,
badge: "Value MUV",
features: ["Comfortable", "Spacious", "Economical for Family"]
},
// SEDAN TIER
{
id: 7,
name: "Toyota Camry",
type: "SEDAN",
seats: "4+1",
luggage: "2 Bags",
image: "camry",
baseFarePerDay: 4200,
driverAllowancePerDay: 400,
extraKmRate: 18,
includedKmPerDay: 300,
badge: "Corporate Elite",
features: ["Hybrid Tech", "Quiet Ride", "Executive Space"]
},
{
id: 8,
name: "Toyota Etios",
type: "SEDAN",
seats: "4+1",
luggage: "2 Bags",
image: "etios",
baseFarePerDay: 2800,
driverAllowancePerDay: 400,
extraKmRate: 13,
includedKmPerDay: 300,
badge: "Economic",
features: ["Spacious Boot", "Good Legroom", "Standard AC"]
},
{
id: 9,
name: "Swift Dzire",
type: "SEDAN",
seats: "4+1",
luggage: "2 Bags",
image: "dzire",
baseFarePerDay: 2600,
driverAllowancePerDay: 400,
extraKmRate: 13,
includedKmPerDay: 300,
badge: "Economic Choice",
features: ["Popular", "Quick Maneuver", "Efficient AC"]
},
// GROUP TRAVEL
{
id: 11,
name: "Audi Q7 Luxury SUV",
type: "SUV",
seats: "6+1",
luggage: "4 Bags",
image: "audi_q7",
baseFarePerDay: 15000,
driverAllowancePerDay: 700,
extraKmRate: 55,
includedKmPerDay: 250,
badge: "Luxury SUV",
features: ["Quattro 4x4", "Panoramic Sunroof", "Premium Audio", "Heated Seats"]
},
{
id: 12,
name: "BMW 5 Series",
type: "LUXURY",
seats: "4+1",
luggage: "2 Bags",
image: "bmw_5_series",
baseFarePerDay: 14000,
driverAllowancePerDay: 600,
extraKmRate: 50,
includedKmPerDay: 250,
badge: "Business Class",
features: ["Dynamic Drive", "Luxury Interiors", "Silent Hybrid", "Touchscreen Control"]
},
{
id: 13,
name: "Mercedes E-Class",
type: "LUXURY",
seats: "4+1",
luggage: "2 Bags",
image: "mercedes_e_class",
baseFarePerDay: 14500,
driverAllowancePerDay: 600,
extraKmRate: 52,
includedKmPerDay: 250,
badge: "Timeless Elegance",
features: ["Soft Close Doors", "Ambient Light", "Professional Driver", "WIFI Enabled"]
},
{
id: 14,
name: "Toyota Corolla Altis",
type: "SEDAN",
seats: "4+1",
luggage: "2 Bags",
image: "corolla_altis",
baseFarePerDay: 3800,
driverAllowancePerDay: 400,
extraKmRate: 16,
includedKmPerDay: 300,
badge: "Executive Sedan",
features: ["Durable", "Comfortable Suspension", "Clean Air Filter"]
}
];
// Lead Capture
app.post("/api/leads", async (req, res) => {
try {
const { name, mobile_number, tripType, pickupLocation, dropLocation } = req.body;
if (!name || !mobile_number) return res.status(400).json({ detail: "Name and Mobile are required" });
await pool.execute(
`INSERT INTO Leads (name, mobile_number, trip_type, pickup_location, drop_location) VALUES (?, ?, ?, ?, ?)`,
[name, mobile_number, tripType || null, pickupLocation || null, dropLocation || null]
);
res.status(201).json({ status: 1, message: "Lead captured" });
} catch (error) {
console.error("Lead Error:", error);
res.status(500).json({ detail: "Failed to capture lead" });
}
});
// Centralized Pricing Calculator
// --- DYNAMIC PRICING ENGINE (DB DRIVEN) ---
app.post("/api/pricing/calculate", async (req, res) => {
try {
const { tripType, pickupDate, returnDate, packageType } = req.body;
@ -331,7 +124,12 @@ app.post("/api/pricing/calculate", async (req, res) => {
const diffTime = Math.abs(end - start);
const durationDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)) || 1;
const results = VEHICLES.map(vehicle => {
// Fetch ACTIVE vehicles from DB
const [rows] = await pool.execute("SELECT * FROM Vehicles WHERE isAvailable = TRUE");
const results = rows.map(vehicle => {
const features = typeof vehicle.features === 'string' ? JSON.parse(vehicle.features) : vehicle.features;
let totalBaseFare = vehicle.baseFarePerDay * durationDays;
let totalDriverAllowance = vehicle.driverAllowancePerDay * durationDays;
@ -343,11 +141,11 @@ app.post("/api/pricing/calculate", async (req, res) => {
totalDriverAllowance = 0;
}
// Special Case: Airport Transfers (Flat Rates)
// Special Case: Airport Transfers
if (tripType === 'airport') {
if (vehicle.type === 'SEDAN') totalBaseFare = 1200;
else if (vehicle.type === 'SUV') totalBaseFare = 2200;
else totalBaseFare = 1800; // MUV/Other
else totalBaseFare = 1800;
totalDriverAllowance = 0;
}
@ -356,18 +154,19 @@ app.post("/api/pricing/calculate", async (req, res) => {
return {
...vehicle,
features,
calc: {
days: durationDays,
baseFare: totalBaseFare,
driverAllowance: totalDriverAllowance,
totalKmIncluded: vehicle.includedKmPerDay * (tripType === 'airport' ? 0.2 : durationDays), // Approx for airport
totalKmIncluded: vehicle.includedKmPerDay * (tripType === 'airport' ? 0.2 : durationDays),
grandTotal,
advancePayment,
math: tripType === 'airport' || tripType === 'local'
? `Fixed ${tripType.charAt(0).toUpperCase() + tripType.slice(1)} Rate`
: `(₹${vehicle.baseFarePerDay} x ${durationDays} Days) + ₹${totalDriverAllowance} Driver`
},
inclusions: ["AC", "Professional Driver", tripType === 'airport' ? "Airport Toll Included" : "First " + (vehicle.includedKmPerDay * durationDays) + " Kms"],
inclusions: ["AC", "Professional Driver", tripType === 'airport' ? "Airport Toll Included" : "First " + (vehicle.includedKmPerDay * (tripType === 'airport' ? 1 : durationDays)) + " Kms"],
exclusions: ["Parking Charges", "Extra Waiting Time", "Inter-state Permits"]
};
});
@ -379,6 +178,86 @@ app.post("/api/pricing/calculate", async (req, res) => {
}
});
// --- ADMIN API ENDPOINTS ---
// Secure Admin Login
app.post("/api/admin/login", async (req, res) => {
const { adminId, password } = req.body;
// Environment-based credentials for security
const VALID_ADMIN_ID = process.env.ADMIN_ID || "admin";
const VALID_ADMIN_PASS = process.env.ADMIN_PASSWORD || "admin123";
if (adminId === VALID_ADMIN_ID && password === VALID_ADMIN_PASS) {
res.json({ success: true, token: "secure_session_token_xyz_987" });
} else {
res.status(401).json({ detail: "Invalid Admin ID or Password" });
}
});
// Get Full Fleet for Management
app.get("/api/admin/vehicles", async (req, res) => {
try {
const [rows] = await pool.execute("SELECT * FROM Vehicles ORDER BY id DESC");
const cars = rows.map(v => ({
...v,
features: typeof v.features === 'string' ? JSON.parse(v.features) : v.features
}));
res.json(cars);
} catch (err) {
res.status(500).json({ detail: "Fail to fetch fleet" });
}
});
// Update Availability or Pricing
app.patch("/api/admin/vehicles/:id", async (req, res) => {
try {
const { id } = req.params;
const { baseFarePerDay, isAvailable } = req.body;
if (baseFarePerDay !== undefined) {
await pool.execute("UPDATE Vehicles SET baseFarePerDay = ? WHERE id = ?", [baseFarePerDay, id]);
}
if (isAvailable !== undefined) {
await pool.execute("UPDATE Vehicles SET isAvailable = ? WHERE id = ?", [isAvailable, id]);
}
res.json({ success: true, message: "Vehicle updated successfully" });
} catch (err) {
res.status(500).json({ detail: "Failed to update vehicle" });
}
});
// Add New Cab
app.post("/api/admin/vehicles", async (req, res) => {
try {
const v = req.body;
const features = JSON.stringify(v.features || []);
await pool.execute(
`INSERT INTO Vehicles (name, type, seats, luggage, image, baseFarePerDay, driverAllowancePerDay, extraKmRate, includedKmPerDay, badge, features)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[v.name, v.type, v.seats, v.luggage, v.image, v.baseFarePerDay, v.driverAllowancePerDay, v.extraKmRate, v.includedKmPerDay, v.badge, features]
);
res.status(201).json({ success: true, message: "New vehicle added" });
} catch (err) {
res.status(500).json({ detail: "Failed to add new vehicle" });
}
});
// Delete Cab
app.delete("/api/admin/vehicles/:id", async (req, res) => {
try {
const { id } = req.params;
await pool.execute("DELETE FROM Vehicles WHERE id = ?", [id]);
res.json({ success: true, message: "Vehicle removed" });
} catch (err) {
res.status(500).json({ detail: "Failed to delete vehicle" });
}
});
const PORT = 8000;
app.listen(PORT, () => {

235
scripts/migrate_vehicles.js Normal file
View file

@ -0,0 +1,235 @@
import "dotenv/config";
import mysql from "mysql2/promise";
const vehicles = [
{
name: "Audi A6 Premium",
type: "LUXURY",
seats: "4+1",
luggage: "2 Bags",
image: "audi_a6",
baseFarePerDay: 12000,
driverAllowancePerDay: 600,
extraKmRate: 45,
includedKmPerDay: 250,
badge: "Executive",
features: JSON.stringify(["Leather Interior", "Dual Climate", "Premium Audio", "Sunroof"])
},
{
name: "BMW 7 Series",
type: "LUXURY",
seats: "4+1",
luggage: "3 Bags",
image: "bmw_7_series",
baseFarePerDay: 25000,
driverAllowancePerDay: 800,
extraKmRate: 85,
includedKmPerDay: 200,
badge: "VVIP Choice",
features: JSON.stringify(["Rear-seat Entertainment", "Massage Seats", "Quiet Cabin", "Chauffeur Driven"])
},
{
name: "Mercedes S-Class",
type: "LUXURY",
seats: "4+1",
luggage: "2 Bags",
image: "mercedes_s_class",
baseFarePerDay: 28000,
driverAllowancePerDay: 800,
extraKmRate: 90,
includedKmPerDay: 200,
badge: "Ultimate Luxury",
features: JSON.stringify(["Ambient Lighting", "Burmester Sound", "Premium Chauffeur"])
},
{
name: "Toyota Fortuner 4x4",
type: "SUV",
seats: "6+1",
luggage: "4 Bags",
image: "fortuner",
baseFarePerDay: 6500,
driverAllowancePerDay: 500,
extraKmRate: 25,
includedKmPerDay: 300,
badge: "Advanture King",
features: JSON.stringify(["Off-road capable", "Powerful AC", "Spacious", "Rugged Built"])
},
{
name: "Innova Crysta",
type: "MUV",
seats: "7+1",
luggage: "3 Bags",
image: "innova_crysta",
baseFarePerDay: 4500,
driverAllowancePerDay: 400,
extraKmRate: 18,
includedKmPerDay: 300,
badge: "Best Seller",
features: JSON.stringify(["Clean Interiors", "Captain Seats", "Smooth Ride", "Carrier Available"])
},
{
name: "Toyota Innova",
type: "MUV",
seats: "7+1",
luggage: "2 Bags",
image: "innova",
baseFarePerDay: 3800,
driverAllowancePerDay: 400,
extraKmRate: 16,
includedKmPerDay: 300,
badge: "Value MUV",
features: JSON.stringify(["Comfortable", "Spacious", "Economical for Family"])
},
{
name: "Toyota Camry",
type: "SEDAN",
seats: "4+1",
luggage: "2 Bags",
image: "camry",
baseFarePerDay: 4200,
driverAllowancePerDay: 400,
extraKmRate: 18,
includedKmPerDay: 300,
badge: "Corporate Elite",
features: JSON.stringify(["Hybrid Tech", "Quiet Ride", "Executive Space"])
},
{
name: "Toyota Etios",
type: "SEDAN",
seats: "4+1",
luggage: "2 Bags",
image: "etios",
baseFarePerDay: 2800,
driverAllowancePerDay: 400,
extraKmRate: 13,
includedKmPerDay: 300,
badge: "Economic",
features: JSON.stringify(["Spacious Boot", "Good Legroom", "Standard AC"])
},
{
name: "Swift Dzire",
type: "SEDAN",
seats: "4+1",
luggage: "2 Bags",
image: "dzire",
baseFarePerDay: 2600,
driverAllowancePerDay: 400,
extraKmRate: 13,
includedKmPerDay: 300,
badge: "Economic Choice",
features: JSON.stringify(["Popular", "Quick Maneuver", "Efficient AC"])
},
{
name: "Audi Q7 Luxury SUV",
type: "SUV",
seats: "6+1",
luggage: "4 Bags",
image: "audi_q7",
baseFarePerDay: 15000,
driverAllowancePerDay: 700,
extraKmRate: 55,
includedKmPerDay: 250,
badge: "Luxury SUV",
features: JSON.stringify(["Quattro 4x4", "Panoramic Sunroof", "Premium Audio", "Heated Seats"])
},
{
name: "BMW 5 Series",
type: "LUXURY",
seats: "4+1",
luggage: "2 Bags",
image: "bmw_5_series",
baseFarePerDay: 14000,
driverAllowancePerDay: 600,
extraKmRate: 50,
includedKmPerDay: 250,
badge: "Business Class",
features: JSON.stringify(["Dynamic Drive", "Luxury Interiors", "Silent Hybrid", "Touchscreen Control"])
},
{
name: "Mercedes E-Class",
type: "LUXURY",
seats: "4+1",
luggage: "2 Bags",
image: "mercedes_e_class",
baseFarePerDay: 14500,
driverAllowancePerDay: 600,
extraKmRate: 52,
includedKmPerDay: 250,
badge: "Timeless Elegance",
features: JSON.stringify(["Soft Close Doors", "Ambient Light", "Professional Driver", "WIFI Enabled"])
},
{
name: "Toyota Corolla Altis",
type: "SEDAN",
seats: "4+1",
luggage: "2 Bags",
image: "corolla_altis",
baseFarePerDay: 3800,
driverAllowancePerDay: 400,
extraKmRate: 16,
includedKmPerDay: 300,
badge: "Executive Sedan",
features: JSON.stringify(["Durable", "Comfortable Suspension", "Clean Air Filter"])
},
{
name: "Toyota Commuter",
type: "VAN",
seats: "12+1",
luggage: "6 Bags",
image: "toyota_commuter",
baseFarePerDay: 8500,
driverAllowancePerDay: 600,
extraKmRate: 28,
includedKmPerDay: 300,
badge: "Large Group",
features: JSON.stringify(["12 Seater", "High Ceiling", "Multiple Bags Space"])
}
];
async function runMigration() {
const pool = mysql.createPool({
host: process.env.DB_HOST || "localhost",
user: process.env.DB_USER || "root",
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || "roadrentals",
});
try {
console.log("Creating Vehicles table...");
await pool.execute(`
CREATE TABLE IF NOT EXISTS Vehicles (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
type VARCHAR(50),
seats VARCHAR(20),
luggage VARCHAR(50),
image VARCHAR(255),
baseFarePerDay INT,
driverAllowancePerDay INT,
extraKmRate INT,
includedKmPerDay INT,
badge VARCHAR(100),
features TEXT,
isAvailable BOOLEAN DEFAULT TRUE,
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`);
console.log("Seeding vehicles...");
for (const v of vehicles) {
await pool.execute(
`INSERT INTO Vehicles (name, type, seats, luggage, image, baseFarePerDay, driverAllowancePerDay, extraKmRate, includedKmPerDay, badge, features)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[v.name, v.type, v.seats, v.luggage, v.image, v.baseFarePerDay, v.driverAllowancePerDay, v.extraKmRate, v.includedKmPerDay, v.badge, v.features]
);
}
console.log("Migration successful!");
} catch (error) {
console.error("Migration failed:", error);
} finally {
await pool.end();
}
}
runMigration();

View file

@ -2,6 +2,8 @@ import { useState } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import HomePage from "./pages/HomePage";
import SearchPage from "./pages/SearchPage";
import AdminLogin from "./pages/Admin/AdminLogin";
import AdminDashboard from "./pages/Admin/AdminDashboard";
import AuthModal from "./components/Auth/AuthModal";
import "./index.css";
@ -13,29 +15,32 @@ function App() {
<Router>
<main className="min-h-screen">
<Routes>
<Route
path="/"
<Route
path="/"
element={
<HomePage
onLoginClick={() => setIsAuthModalOpen(true)}
user={user}
<HomePage
onLoginClick={() => setIsAuthModalOpen(true)}
user={user}
/>
}
}
/>
<Route
path="/search"
<Route
path="/search"
element={
<SearchPage
onLoginClick={() => setIsAuthModalOpen(true)}
user={user}
<SearchPage
onLoginClick={() => setIsAuthModalOpen(true)}
user={user}
/>
}
}
/>
<Route path="/admin" element={<AdminLogin />} />
<Route path="/admin/login" element={<AdminLogin />} />
<Route path="/admin/dashboard" element={<AdminDashboard />} />
</Routes>
<AuthModal
isOpen={isAuthModalOpen}
onClose={() => setIsAuthModalOpen(false)}
<AuthModal
isOpen={isAuthModalOpen}
onClose={() => setIsAuthModalOpen(false)}
user={user}
setUser={setUser}
/>

View file

@ -1,3 +1,4 @@
import { Link } from "react-router-dom";
import Logo from "../Logo/Logo";
const Footer = () => {
@ -81,6 +82,11 @@ const Footer = () => {
<p className="text-gray-500 text-sm">
Copyright © 2018-2025 road.rentals Tours & Travels. All Rights Reserved.
</p>
<div className="flex items-center gap-4">
<Link to="/admin" className="text-xs text-gray-600 hover:text-indigo-400 opacity-60 hover:opacity-100 transition-all font-medium uppercase tracking-tighter">
Owner Login
</Link>
</div>
</div>
</div>
</footer>

View file

@ -0,0 +1,316 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Car, CreditCard, Power, Plus, Trash2, LayoutDashboard, Settings, LogOut, Search, MoreVertical, X, Check } from 'lucide-react';
const AdminDashboard = () => {
const [vehicles, setVehicles] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [showAddModal, setShowAddModal] = useState(false);
const [editingId, setEditingId] = useState(null);
const [newCar, setNewCar] = useState({
name: '', type: 'SEDAN', seats: '4+1', luggage: '2 Bags', image: 'dzire',
baseFarePerDay: 2500, driverAllowancePerDay: 400, extraKmRate: 13,
includedKmPerDay: 300, badge: 'Standard', features: []
});
const navigate = useNavigate();
useEffect(() => {
const token = localStorage.getItem('adminToken');
if (!token) navigate('/admin/login');
else fetchVehicles();
}, []);
const fetchVehicles = async () => {
try {
const res = await fetch('/api/admin/vehicles');
const data = await res.json();
setVehicles(data);
} catch (err) {
console.error(err);
} finally {
setIsLoading(false);
}
};
const toggleAvailability = async (id, currentStatus) => {
try {
await fetch(`/api/admin/vehicles/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ isAvailable: !currentStatus })
});
fetchVehicles();
} catch (err) {
console.error(err);
}
};
const updatePrice = async (id, newPrice) => {
try {
await fetch(`/api/admin/vehicles/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ baseFarePerDay: parseInt(newPrice) })
});
setEditingId(null);
fetchVehicles();
} catch (err) {
console.error(err);
}
};
const deleteVehicle = async (id) => {
if (!window.confirm('Delete this vehicle from fleet?')) return;
try {
await fetch(`/api/admin/vehicles/${id}`, { method: 'DELETE' });
fetchVehicles();
} catch (err) {
console.error(err);
}
};
const handleAddVehicle = async (e) => {
e.preventDefault();
try {
await fetch('/api/admin/vehicles', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newCar)
});
setShowAddModal(false);
fetchVehicles();
} catch (err) {
console.error(err);
}
};
const logout = () => {
localStorage.removeItem('adminToken');
navigate('/admin/login');
};
return (
<div className="min-h-screen bg-[#f8fafc] flex">
{/* Sidebar */}
<div className="w-64 bg-slate-900 text-white flex flex-col">
<div className="p-6 border-b border-white/10">
<h1 className="text-xl font-bold flex items-center gap-2">
<div className="w-8 h-8 bg-indigo-500 rounded-lg flex items-center justify-center">
<Car className="w-5 h-5" />
</div>
road.rentals
</h1>
<p className="text-slate-400 text-xs mt-1">Admin Panel v2.0</p>
</div>
<nav className="flex-1 p-4 space-y-2">
<button className="w-full flex items-center gap-3 px-4 py-3 bg-indigo-600 rounded-xl text-sm font-semibold text-white">
<LayoutDashboard className="w-5 h-5" />
Fleet Control
</button>
<button className="w-full flex items-center gap-3 px-4 py-3 text-slate-400 hover:bg-white/5 rounded-xl text-sm font-semibold transition-all">
<CreditCard className="w-5 h-5" />
Booking Reports
</button>
<button className="w-full flex items-center gap-3 px-4 py-3 text-slate-400 hover:bg-white/5 rounded-xl text-sm font-semibold transition-all">
<Settings className="w-5 h-5" />
System Config
</button>
</nav>
<div className="p-4 mt-auto">
<button
onClick={logout}
className="w-full flex items-center gap-3 px-4 py-3 text-red-400 hover:bg-red-500/10 rounded-xl text-sm font-semibold transition-all"
>
<LogOut className="w-5 h-5" />
Logout
</button>
</div>
</div>
{/* Main Content */}
<div className="flex-1 flex flex-col max-h-screen overflow-hidden">
<header className="bg-white border-b border-slate-200 px-8 py-4 flex items-center justify-between">
<div>
<h2 className="text-xl font-bold text-slate-800">Fleet Management</h2>
<p className="text-sm text-slate-500">Inventory control and dynamic pricing</p>
</div>
<button
onClick={() => setShowAddModal(true)}
className="bg-indigo-600 hover:bg-indigo-700 text-white px-5 py-2.5 rounded-xl text-sm font-bold shadow-lg shadow-indigo-100 flex items-center gap-2 transition-all"
>
<Plus className="w-5 h-5" />
Add New Vehicle
</button>
</header>
<main className="flex-1 p-8 overflow-y-auto">
{/* Stats */}
<div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm">
<p className="text-sm text-slate-500 font-semibold mb-1">Total Fleet</p>
<h3 className="text-28 font-bold text-slate-900">{vehicles.length} Cars</h3>
</div>
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm">
<p className="text-sm text-slate-400 font-semibold mb-1">Active Now</p>
<h3 className="text-28 font-bold text-green-600">{vehicles.filter(v => v.isAvailable).length} Cars</h3>
</div>
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm">
<p className="text-sm text-slate-400 font-semibold mb-1">Maintenance</p>
<h3 className="text-28 font-bold text-red-500">{vehicles.filter(v => !v.isAvailable).length} Cars</h3>
</div>
<div className="bg-indigo-600 p-6 rounded-2xl shadow-xl shadow-indigo-100 text-white">
<p className="text-sm text-white/70 font-semibold mb-1">Fleet Revenue</p>
<h3 className="text-28 font-bold">Dynamic</h3>
</div>
</div>
{/* Table */}
<div className="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<table className="w-full text-left border-collapse">
<thead>
<tr className="bg-slate-50 border-b border-slate-200">
<th className="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider">Vehicle Details</th>
<th className="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider">Type / Capacity</th>
<th className="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider">Base Price / Day</th>
<th className="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider">Status</th>
<th className="px-6 py-4 text-xs font-bold text-slate-500 uppercase tracking-wider text-right">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{isLoading ? (
<tr><td colSpan="5" className="p-10 text-center text-slate-400">Loading fleet data...</td></tr>
) : vehicles.map(vehicle => (
<tr key={vehicle.id} className="hover:bg-slate-50 transition-colors">
<td className="px-6 py-4">
<div className="flex items-center gap-3">
<div className="w-12 h-12 bg-slate-100 rounded-lg overflow-hidden flex items-center justify-center">
<Car className="w-6 h-6 text-slate-400" />
</div>
<div>
<p className="font-bold text-slate-900">{vehicle.name}</p>
<p className="text-xs text-slate-500">{vehicle.badge || 'Standard'}</p>
</div>
</div>
</td>
<td className="px-6 py-4">
<span className="text-xs font-bold px-2 py-1 bg-slate-100 rounded-md text-slate-600 mr-2">{vehicle.type}</span>
<span className="text-sm text-slate-500 font-medium">{vehicle.seats} Seater</span>
</td>
<td className="px-6 py-4">
{editingId === vehicle.id ? (
<div className="flex items-center gap-2">
<input
type="number"
defaultValue={vehicle.baseFarePerDay}
className="w-20 px-2 py-1 border border-indigo-300 rounded outline-none"
onKeyDown={(e) => {
if (e.key === 'Enter') updatePrice(vehicle.id, e.target.value);
if (e.key === 'Escape') setEditingId(null);
}}
/>
</div>
) : (
<div
onClick={() => setEditingId(vehicle.id)}
className="font-bold text-slate-900 cursor-pointer hover:text-indigo-600"
>
{vehicle.baseFarePerDay}
</div>
)}
</td>
<td className="px-6 py-4">
<button
onClick={() => toggleAvailability(vehicle.id, vehicle.isAvailable)}
className={`flex items-center gap-2 text-xs font-bold px-3 py-1.5 rounded-full transition-all ${
vehicle.isAvailable
? 'bg-green-50 text-green-600 border border-green-100 hover:bg-green-100'
: 'bg-red-50 text-red-600 border border-red-100 hover:bg-red-100'
}`}
>
<Power className="w-3 h-3" />
{vehicle.isAvailable ? 'AVAILABLE' : 'OFF DUTY'}
</button>
</td>
<td className="px-6 py-4 text-right">
<button
onClick={() => deleteVehicle(vehicle.id)}
className="p-2 text-slate-400 hover:text-red-500 hover:bg-red-50 rounded-lg transition-all"
>
<Trash2 className="w-5 h-5" />
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</main>
</div>
{/* Add Cab Modal */}
{showAddModal && (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/40 backdrop-blur-sm">
<div className="bg-white w-full max-w-xl rounded-2xl shadow-2xl overflow-hidden border border-slate-200">
<header className="px-8 py-6 bg-slate-50 border-b border-slate-200 flex items-center justify-between">
<h3 className="text-xl font-bold text-slate-900">Add New Fleet Vehicle</h3>
<button onClick={() => setShowAddModal(false)} className="text-slate-400 hover:text-slate-600">
<X className="w-6 h-6" />
</button>
</header>
<form onSubmit={handleAddVehicle} className="p-8 space-y-6">
<div className="grid grid-cols-2 gap-6">
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Vehicle Name</label>
<input
required
className="w-full px-4 py-2.5 rounded-xl border border-slate-200 outline-none"
placeholder="e.g. Audi A8 L"
onChange={e => setNewCar({...newCar, name: e.target.value})}
/>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Category</label>
<select
className="w-full px-4 py-2.5 rounded-xl border border-slate-200 outline-none"
onChange={e => setNewCar({...newCar, type: e.target.value})}
>
<option value="LUXURY">Luxury</option>
<option value="SUV">SUV</option>
<option value="MUV">MUV</option>
<option value="SEDAN">Sedan</option>
<option value="VAN">Van</option>
</select>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Seats</label>
<input
className="w-full px-4 py-2.5 rounded-xl border border-slate-200 outline-none"
placeholder="e.g. 4+1"
onChange={e => setNewCar({...newCar, seats: e.target.value})}
/>
</div>
<div>
<label className="block text-sm font-bold text-slate-700 mb-2">Base Fare</label>
<input
type="number"
className="w-full px-4 py-2.5 rounded-xl border border-slate-200 outline-none"
placeholder="₹ per day"
onChange={e => setNewCar({...newCar, baseFarePerDay: parseInt(e.target.value)})}
/>
</div>
</div>
<button className="w-full bg-indigo-600 hover:bg-fuchsia-600 text-white font-bold py-4 rounded-xl shadow-lg transition-all">
Confirm & Add to Fleet
</button>
</form>
</div>
</div>
)}
</div>
);
};
export default AdminDashboard;

View file

@ -0,0 +1,109 @@
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Lock, User, ShieldCheck, AlertCircle } from 'lucide-react';
const AdminLogin = () => {
const [adminId, setAdminId] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();
const handleLogin = async (e) => {
e.preventDefault();
setIsLoading(true);
setError('');
try {
const response = await fetch('/api/admin/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ adminId, password })
});
const data = await response.json();
if (response.ok) {
localStorage.setItem('adminToken', data.token);
navigate('/admin/dashboard');
} else {
setError(data.detail || 'Invalid credentials');
}
} catch (err) {
setError('Connection failed. Please try again.');
} finally {
setIsLoading(false);
}
};
return (
<div className="min-h-screen bg-slate-50 flex items-center justify-center p-4">
<div className="max-w-md w-full bg-white rounded-2xl shadow-xl p-8 border border-slate-100">
<div className="text-center mb-8">
<div className="w-16 h-16 bg-indigo-600 rounded-2xl flex items-center justify-center mx-auto mb-4 shadow-lg shadow-indigo-200">
<ShieldCheck className="w-8 h-8 text-white" />
</div>
<h1 className="text-24 font-bold text-slate-900">Admin Portal</h1>
<p className="text-slate-500 mt-2">Secure access for road.rentals owner</p>
</div>
{error && (
<div className="mb-6 p-4 bg-red-50 border border-red-100 rounded-xl flex items-center gap-3 text-red-600 text-sm">
<AlertCircle className="w-5 h-5 flex-shrink-0" />
{error}
</div>
)}
<form onSubmit={handleLogin} className="space-y-6">
<div>
<label className="block text-sm font-semibold text-slate-700 mb-2">Admin ID</label>
<div className="relative">
<User className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-slate-400" />
<input
type="text"
value={adminId}
onChange={(e) => setAdminId(e.target.value)}
className="w-full pl-10 pr-4 py-3 rounded-xl border border-slate-200 focus:border-indigo-500 focus:ring-4 focus:ring-indigo-50/50 outline-none transition-all"
placeholder="Enter your Admin ID"
required
/>
</div>
</div>
<div>
<label className="block text-sm font-semibold text-slate-700 mb-2">Password</label>
<div className="relative">
<Lock className="absolute left-3 top-1/2 -translate-y-1/2 w-5 h-5 text-slate-400" />
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="w-full pl-10 pr-4 py-3 rounded-xl border border-slate-200 focus:border-indigo-500 focus:ring-4 focus:ring-indigo-50/50 outline-none transition-all"
placeholder="••••••••"
required
/>
</div>
</div>
<button
type="submit"
disabled={isLoading}
className="w-full bg-indigo-600 hover:bg-indigo-700 text-white font-bold py-4 rounded-xl transition-all shadow-lg shadow-indigo-200 flex items-center justify-center gap-2"
>
{isLoading ? (
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
) : (
'Sign In to Dashboard'
)}
</button>
</form>
<p className="text-center text-slate-400 text-xs mt-8 font-medium uppercase tracking-wider">
Road.Rentals Official Admin System
</p>
</div>
</div>
);
};
export default AdminLogin;

View file

@ -12,9 +12,9 @@ import Footer from "../components/Footer/Footer";
const HomePage = ({ onLoginClick, user }) => {
return (
<>
<Navbar
user={user}
onLoginClick={onLoginClick}
<Navbar
user={user}
onLoginClick={onLoginClick}
/>
<HeroSection />
<CabsSection />

View file

@ -1,9 +1,9 @@
import React, { useMemo, useState, useEffect } from "react";
import { useSearchParams, Link } from "react-router-dom";
import {
Calendar, MapPin, Users, Luggage, ShieldCheck,
ArrowLeft, Info, CheckCircle2, Loader2, X,
ChevronRight, ArrowRight, User, Phone
import {
Calendar, MapPin, Users, Luggage, ShieldCheck,
ArrowLeft, Info, CheckCircle2, Loader2, X,
ChevronRight, ArrowRight, User, Phone
} from "lucide-react";
import Navbar from "../components/Navbar/Navbar";
import Footer from "../components/Footer/Footer";
@ -13,8 +13,8 @@ import { api } from "../lib/api";
import innovaCrystaImg from "../assets/vehicles/innova_crysta.png";
import innovaImg from "../assets/vehicles/innova.png";
import fortunerImg from "../assets/vehicles/fortuner.png";
import dzireImg from "../assets/vehicles/dzire.png";
import etiosImg from "../assets/vehicles/etios.png";
import dzireImg from "../assets/vehicles/dzire.png";
import etiosImg from "../assets/vehicles/etios.png";
import audiA6Img from "../assets/vehicles/audi_a6.png";
import audiQ7Img from "../assets/vehicles/audi_q7.png";
import bmw5Img from "../assets/vehicles/bmw_5_series.png";
@ -105,7 +105,7 @@ const LeadCaptureModal = ({ isOpen, onSubmit, searchData }) => {
onChange={(e) => setMobile(e.target.value)}
/>
</div>
<button
type="submit"
disabled={isSubmitting}
@ -113,13 +113,13 @@ const LeadCaptureModal = ({ isOpen, onSubmit, searchData }) => {
>
{isSubmitting ? <Loader2 className="w-5 h-5 animate-spin" /> : (
<>
View Deals
View Deals
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</>
)}
</button>
</form>
<p className="text-center text-[10px] text-gray-400 mt-6 font-bold uppercase tracking-widest">
No spam. Only trip updates.
</p>
@ -135,7 +135,7 @@ const SearchPage = ({ onLoginClick, user }) => {
const [showLeadModal, setShowLeadModal] = useState(!user); // Don't show if user logged in
const [cars, setCars] = useState([]);
const [isLoading, setIsLoading] = useState(true);
// Extract search details
const tripType = searchParams.get("tripType") || "round";
const pickupLocation = searchParams.get("pickup") || "Not Specified";
@ -199,16 +199,16 @@ const SearchPage = ({ onLoginClick, user }) => {
return (
<div className="min-h-screen bg-gray-50 flex flex-col">
<Navbar onLoginClick={onLoginClick} user={user} />
{/* Lead Capture Modal */}
<LeadCaptureModal
isOpen={showLeadModal}
<LeadCaptureModal
isOpen={showLeadModal}
onSubmit={() => setShowLeadModal(false)}
searchData={{ tripType, pickup: pickupLocation, drop: dropLocation }}
/>
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-10 pt-28 flex-grow">
{/* Search Summary Header */}
<div className="bg-white rounded-2xl shadow-sm border border-gray-100 p-6 mb-8 flex flex-col md:flex-row md:items-center justify-between gap-6 animate-fadeIn">
<div className="flex flex-col gap-4">
@ -254,7 +254,7 @@ const SearchPage = ({ onLoginClick, user }) => {
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8 pb-20">
{/* Main Listings */}
<div className="lg:col-span-2 space-y-6">
<div className="flex items-center justify-between mb-2">
@ -263,7 +263,7 @@ const SearchPage = ({ onLoginClick, user }) => {
Found {cars.length} Options
</p>
</div>
{isLoading ? (
<div className="bg-white rounded-3xl p-20 flex flex-col items-center justify-center shadow-sm border border-gray-100">
<Loader2 className="w-10 h-10 text-primary-600 animate-spin mb-4" />
@ -275,14 +275,14 @@ const SearchPage = ({ onLoginClick, user }) => {
{/* Car Image Wrapper */}
<div className="xl:w-[280px] bg-gray-50 rounded-2xl flex items-center justify-center p-6 relative overflow-hidden group-hover:bg-primary-50/30 transition-colors">
<img src={carImages[car.image]} alt={car.name} className="w-full h-auto object-contain transform group-hover:scale-110 transition-transform duration-700 drop-shadow-xl" />
{/* Badge */}
{car.badge && (
<div className="absolute top-4 left-4 bg-primary-600 text-white text-[9px] font-black px-3 py-1.5 rounded-full shadow-lg uppercase tracking-widest animate-bounceIn">
{car.badge}
</div>
)}
<div className="absolute bottom-4 right-4 bg-white/90 backdrop-blur-sm px-2.5 py-1.5 rounded-xl border border-white flex items-center gap-1.5 shadow-sm">
<span className="text-yellow-500 font-black tracking-tighter"></span>
<span className="text-[10px] font-black text-dark-900">Top Rated</span>
@ -298,14 +298,14 @@ const SearchPage = ({ onLoginClick, user }) => {
<div className="flex items-center gap-4 mt-1.5">
<span className="text-[10px] font-black text-primary-600 bg-primary-50 px-2.5 py-1 rounded-lg uppercase tracking-widest">{car.type}</span>
<div className="flex items-center gap-3 text-gray-400">
<div className="flex items-center gap-1.5">
<Users className="w-3.5 h-3.5" />
<span className="text-[11px] font-bold">{car.seats}</span>
</div>
<div className="flex items-center gap-1.5">
<Luggage className="w-3.5 h-3.5" />
<span className="text-[11px] font-bold">{car.luggage}</span>
</div>
<div className="flex items-center gap-1.5">
<Users className="w-3.5 h-3.5" />
<span className="text-[11px] font-bold">{car.seats}</span>
</div>
<div className="flex items-center gap-1.5">
<Luggage className="w-3.5 h-3.5" />
<span className="text-[11px] font-bold">{car.luggage}</span>
</div>
</div>
</div>
</div>
@ -318,15 +318,15 @@ const SearchPage = ({ onLoginClick, user }) => {
{/* Transparency Math Block */}
<div className="bg-primary-50/50 rounded-2xl p-4 mb-6 border border-primary-100/30">
<div className="flex items-center justify-between mb-2">
<span className="text-[10px] font-black text-primary-900 uppercase tracking-widest flex items-center gap-1.5">
<Info className="w-3.5 h-3.5" />
Transparent Breakdown
</span>
<span className="text-[11px] font-black text-primary-600">{car.calc.math}</span>
<span className="text-[10px] font-black text-primary-900 uppercase tracking-widest flex items-center gap-1.5">
<Info className="w-3.5 h-3.5" />
Transparent Breakdown
</span>
<span className="text-[11px] font-black text-primary-600">{car.calc.math}</span>
</div>
<div className="flex items-center justify-between">
<span className="text-[10px] font-bold text-gray-500 uppercase tracking-widest font-mono">20% Booking Advance</span>
<span className="text-sm font-black text-primary-600">Pay {car.calc.advancePayment} to Confirm</span>
<span className="text-[10px] font-bold text-gray-500 uppercase tracking-widest font-mono">20% Booking Advance</span>
<span className="text-sm font-black text-primary-600">Pay {car.calc.advancePayment} to Confirm</span>
</div>
</div>
@ -334,7 +334,7 @@ const SearchPage = ({ onLoginClick, user }) => {
<div className="grid grid-cols-2 gap-4 pb-6 border-b border-gray-50">
<div>
<p className="text-[10px] font-black text-green-600 uppercase tracking-widest mb-2 flex items-center gap-1.5">
<CheckCircle2 className="w-3 h-3" /> Included
<CheckCircle2 className="w-3 h-3" /> Included
</p>
<ul className="space-y-1.5">
{car.inclusions.map((inc, i) => (
@ -346,7 +346,7 @@ const SearchPage = ({ onLoginClick, user }) => {
</div>
<div>
<p className="text-[10px] font-black text-red-500 uppercase tracking-widest mb-2 flex items-center gap-1.5">
<X className="w-3 h-3" /> Excluded
<X className="w-3 h-3" /> Excluded
</p>
<ul className="space-y-1.5">
{car.exclusions.map((exc, i) => (
@ -364,7 +364,7 @@ const SearchPage = ({ onLoginClick, user }) => {
<span className="text-[10px] font-black text-gray-400 uppercase tracking-widest">Available Distance</span>
<span className="text-sm font-black text-dark-900">{car.calc.totalKmIncluded} Km Included</span>
</div>
<button
<button
onClick={() => handleBookNow(car)}
disabled={isBooking}
className="bg-dark-900 hover:bg-primary-600 text-white font-black text-xs px-8 py-4 rounded-2xl transition-all duration-300 shadow-xl shadow-dark-900/10 hover:shadow-primary-600/30 disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2 group"
@ -385,61 +385,61 @@ const SearchPage = ({ onLoginClick, user }) => {
{/* Pricing Summary Sidebar */}
<div className="lg:col-span-1">
<div className="sticky top-28 space-y-6">
<div className="bg-white rounded-3xl shadow-sm border border-gray-100 p-8 overflow-hidden relative group">
{/* Decorative element */}
<div className="absolute -top-10 -right-10 w-32 h-32 bg-primary-50 rounded-full opacity-50 blur-3xl group-hover:bg-primary-100 transition-colors duration-500" />
<h2 className="text-xl font-black text-dark-900 mb-8 flex items-center gap-3 relative z-10">
Terms & Policy
< ShieldCheck className="w-5 h-5 text-green-500" />
</h2>
<div className="bg-white rounded-3xl shadow-sm border border-gray-100 p-8 overflow-hidden relative group">
{/* Decorative element */}
<div className="absolute -top-10 -right-10 w-32 h-32 bg-primary-50 rounded-full opacity-50 blur-3xl group-hover:bg-primary-100 transition-colors duration-500" />
<div className="space-y-5 relative z-10">
<div className="flex items-start gap-3">
<div className="mt-1 w-5 h-5 bg-green-50 rounded-lg flex items-center justify-center flex-shrink-0">
<CheckCircle2 className="w-3 h-3 text-green-600" />
</div>
<div>
<p className="text-xs font-black text-dark-900 mb-0.5">Toll & State Permits</p>
<p className="text-[11px] font-bold text-gray-500 leading-relaxed">To be paid by traveller as per actuals during the trip.</p>
</div>
<h2 className="text-xl font-black text-dark-900 mb-8 flex items-center gap-3 relative z-10">
Terms & Policy
< ShieldCheck className="w-5 h-5 text-green-500" />
</h2>
<div className="space-y-5 relative z-10">
<div className="flex items-start gap-3">
<div className="mt-1 w-5 h-5 bg-green-50 rounded-lg flex items-center justify-center flex-shrink-0">
<CheckCircle2 className="w-3 h-3 text-green-600" />
</div>
<div className="flex items-start gap-3">
<div className="mt-1 w-5 h-5 bg-primary-50 rounded-lg flex items-center justify-center flex-shrink-0">
<Info className="w-3 h-3 text-primary-600" />
</div>
<div>
<p className="text-xs font-black text-dark-900 mb-0.5">Extra Kilimoteres</p>
<p className="text-[11px] font-bold text-gray-500 leading-relaxed">Charged at standard base rates mentioned on the car cards.</p>
</div>
</div>
<div className="flex items-start gap-3">
<div className="mt-1 w-5 h-5 bg-orange-50 rounded-lg flex items-center justify-center flex-shrink-0">
<Calendar className="w-3 h-3 text-orange-600" />
</div>
<div>
<p className="text-xs font-black text-dark-900 mb-0.5">Night Charge</p>
<p className="text-[11px] font-bold text-gray-500 leading-relaxed">Driver night allowance applicable after 10:00 PM.</p>
</div>
<div>
<p className="text-xs font-black text-dark-900 mb-0.5">Toll & State Permits</p>
<p className="text-[11px] font-bold text-gray-500 leading-relaxed">To be paid by traveller as per actuals during the trip.</p>
</div>
</div>
<div className="mt-10 p-5 bg-gray-50 rounded-2xl border border-gray-100 text-center">
<p className="text-[10px] font-black text-gray-400 uppercase tracking-widest mb-1.5">Need Custom Help?</p>
<a href="tel:+917975942520" className="text-sm font-black text-dark-900 hover:text-primary-600 transition-colors">+91 7975942520</a>
<div className="flex items-start gap-3">
<div className="mt-1 w-5 h-5 bg-primary-50 rounded-lg flex items-center justify-center flex-shrink-0">
<Info className="w-3 h-3 text-primary-600" />
</div>
<div>
<p className="text-xs font-black text-dark-900 mb-0.5">Extra Kilimoteres</p>
<p className="text-[11px] font-bold text-gray-500 leading-relaxed">Charged at standard base rates mentioned on the car cards.</p>
</div>
</div>
</div>
<div className="bg-dark-900 rounded-3xl p-8 text-white relative overflow-hidden shadow-2xl">
<div className="absolute top-0 right-0 w-32 h-32 bg-primary-600/20 rounded-full blur-3xl" />
<h3 className="text-lg font-black mb-4 flex items-center gap-2">
<ShieldCheck className="w-5 h-5 text-primary-400" />
Refund Guarantee
</h3>
<p className="text-xs font-bold text-gray-400 leading-relaxed">
Cancel up to 24 hours in advance and receive a full refund of your 20% booking deposit.
</p>
</div>
<div className="flex items-start gap-3">
<div className="mt-1 w-5 h-5 bg-orange-50 rounded-lg flex items-center justify-center flex-shrink-0">
<Calendar className="w-3 h-3 text-orange-600" />
</div>
<div>
<p className="text-xs font-black text-dark-900 mb-0.5">Night Charge</p>
<p className="text-[11px] font-bold text-gray-500 leading-relaxed">Driver night allowance applicable after 10:00 PM.</p>
</div>
</div>
</div>
<div className="mt-10 p-5 bg-gray-50 rounded-2xl border border-gray-100 text-center">
<p className="text-[10px] font-black text-gray-400 uppercase tracking-widest mb-1.5">Need Custom Help?</p>
<a href="tel:+917975942520" className="text-sm font-black text-dark-900 hover:text-primary-600 transition-colors">+91 7975942520</a>
</div>
</div>
<div className="bg-dark-900 rounded-3xl p-8 text-white relative overflow-hidden shadow-2xl">
<div className="absolute top-0 right-0 w-32 h-32 bg-primary-600/20 rounded-full blur-3xl" />
<h3 className="text-lg font-black mb-4 flex items-center gap-2">
<ShieldCheck className="w-5 h-5 text-primary-400" />
Refund Guarantee
</h3>
<p className="text-xs font-bold text-gray-400 leading-relaxed">
Cancel up to 24 hours in advance and receive a full refund of your 20% booking deposit.
</p>
</div>
</div>
</div>
</div>
@ -460,7 +460,7 @@ const SearchPage = ({ onLoginClick, user }) => {
<p className="text-[10px] font-black text-gray-400 uppercase tracking-widest mb-2">Booking Transaction ID</p>
<p className="text-2xl font-black text-dark-900 tracking-tighter">#RR-{bookingSuccess}</p>
</div>
<button
<button
onClick={() => setBookingSuccess(null)}
className="w-full bg-dark-900 hover:bg-black text-white font-black py-5 rounded-2xl transition-all shadow-xl shadow-dark-900/20 active:scale-95"
>
@ -469,7 +469,7 @@ const SearchPage = ({ onLoginClick, user }) => {
</div>
</div>
)}
<Footer />
</div>
);