Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bb6db4fc41 | |||
| 99284eb4a9 | |||
| 952d3519f6 | |||
| 5e883d6c34 | |||
| a1ce5bf0cd | |||
| 809b6e5db4 | |||
| 1eb94c2c2a |
@@ -1,141 +1,438 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
import { useState, useEffect } from "react";
|
||||||
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
|
import { useRouter } from "next/navigation";
|
||||||
import FooterLogoReveal from '@/components/sections/footer/FooterLogoReveal';
|
import Link from "next/link";
|
||||||
import Link from 'next/link';
|
import { LogOut, Plus, Download, Filter, ChevronDown, TrendingUp, Clock, AlertCircle } from "lucide-react";
|
||||||
|
|
||||||
|
interface TransportAction {
|
||||||
|
id: string;
|
||||||
|
vehicleId: string;
|
||||||
|
vehiclePlate: string;
|
||||||
|
driverName: string;
|
||||||
|
actionType: "entry" | "exit";
|
||||||
|
timestamp: string;
|
||||||
|
location: string;
|
||||||
|
notes: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default function DashboardPage() {
|
export default function DashboardPage() {
|
||||||
|
const router = useRouter();
|
||||||
|
const [isLoggedIn, setIsLoggedIn] = useState(false);
|
||||||
|
const [isLoading, setIsLoading] = useState(true);
|
||||||
|
const [actions, setActions] = useState<TransportAction[]>([]);
|
||||||
|
const [filteredActions, setFilteredActions] = useState<TransportAction[]>([]);
|
||||||
|
const [filterType, setFilterType] = useState<"all" | "entry" | "exit">("all");
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
const [showEntryForm, setShowEntryForm] = useState(false);
|
||||||
|
const [showExitForm, setShowExitForm] = useState(false);
|
||||||
|
const [formData, setFormData] = useState({
|
||||||
|
vehicleId: "", vehiclePlate: "", driverName: "", location: "", notes: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check authentication
|
||||||
|
useEffect(() => {
|
||||||
|
const user = localStorage.getItem("user");
|
||||||
|
if (!user) {
|
||||||
|
router.push("/login");
|
||||||
|
} else {
|
||||||
|
setIsLoggedIn(true);
|
||||||
|
setIsLoading(false);
|
||||||
|
loadActions();
|
||||||
|
}
|
||||||
|
}, [router]);
|
||||||
|
|
||||||
|
const loadActions = () => {
|
||||||
|
const savedActions = localStorage.getItem("transportActions");
|
||||||
|
const parsed = savedActions ? JSON.parse(savedActions) : [];
|
||||||
|
setActions(parsed);
|
||||||
|
filterActions(parsed);
|
||||||
|
};
|
||||||
|
|
||||||
|
const filterActions = (acts: TransportAction[], type: "all" | "entry" | "exit" = filterType, search: string = searchTerm) => {
|
||||||
|
let filtered = acts;
|
||||||
|
|
||||||
|
if (type !== "all") {
|
||||||
|
filtered = filtered.filter((a) => a.actionType === type);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
filtered = filtered.filter(
|
||||||
|
(a) =>
|
||||||
|
a.vehiclePlate.toLowerCase().includes(search.toLowerCase()) ||
|
||||||
|
a.driverName.toLowerCase().includes(search.toLowerCase()) ||
|
||||||
|
a.vehicleId.toLowerCase().includes(search.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredActions(filtered);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFilterChange = (type: "all" | "entry" | "exit") => {
|
||||||
|
setFilterType(type);
|
||||||
|
filterActions(actions, type, searchTerm);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSearchChange = (term: string) => {
|
||||||
|
setSearchTerm(term);
|
||||||
|
filterActions(actions, filterType, term);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddEntry = () => {
|
||||||
|
if (!formData.vehiclePlate || !formData.driverName) {
|
||||||
|
alert("Please fill in vehicle plate and driver name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAction: TransportAction = {
|
||||||
|
id: Date.now().toString(),
|
||||||
|
vehicleId: formData.vehicleId,
|
||||||
|
vehiclePlate: formData.vehiclePlate,
|
||||||
|
driverName: formData.driverName,
|
||||||
|
actionType: "entry", timestamp: new Date().toISOString(),
|
||||||
|
location: formData.location,
|
||||||
|
notes: formData.notes
|
||||||
|
};
|
||||||
|
|
||||||
|
const updated = [newAction, ...actions];
|
||||||
|
setActions(updated);
|
||||||
|
localStorage.setItem("transportActions", JSON.stringify(updated));
|
||||||
|
filterActions(updated);
|
||||||
|
setFormData({ vehicleId: "", vehiclePlate: "", driverName: "", location: "", notes: "" });
|
||||||
|
setShowEntryForm(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAddExit = () => {
|
||||||
|
if (!formData.vehiclePlate || !formData.driverName) {
|
||||||
|
alert("Please fill in vehicle plate and driver name");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newAction: TransportAction = {
|
||||||
|
id: Date.now().toString(),
|
||||||
|
vehicleId: formData.vehicleId,
|
||||||
|
vehiclePlate: formData.vehiclePlate,
|
||||||
|
driverName: formData.driverName,
|
||||||
|
actionType: "exit", timestamp: new Date().toISOString(),
|
||||||
|
location: formData.location,
|
||||||
|
notes: formData.notes
|
||||||
|
};
|
||||||
|
|
||||||
|
const updated = [newAction, ...actions];
|
||||||
|
setActions(updated);
|
||||||
|
localStorage.setItem("transportActions", JSON.stringify(updated));
|
||||||
|
filterActions(updated);
|
||||||
|
setFormData({ vehicleId: "", vehiclePlate: "", driverName: "", location: "", notes: "" });
|
||||||
|
setShowExitForm(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const downloadReport = () => {
|
||||||
|
if (filteredActions.length === 0) {
|
||||||
|
alert("No data to download");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const csv = [
|
||||||
|
["ID", "Vehicle ID", "Vehicle Plate", "Driver Name", "Action Type", "Timestamp", "Location", "Notes"],
|
||||||
|
...filteredActions.map((a) => [
|
||||||
|
a.id,
|
||||||
|
a.vehicleId,
|
||||||
|
a.vehiclePlate,
|
||||||
|
a.driverName,
|
||||||
|
a.actionType,
|
||||||
|
new Date(a.timestamp).toLocaleString(),
|
||||||
|
a.location,
|
||||||
|
a.notes
|
||||||
|
])
|
||||||
|
]
|
||||||
|
.map((row) => row.map((cell) => `"${cell}"`).join(","))
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
const blob = new Blob([csv], { type: "text/csv" });
|
||||||
|
const url = window.URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
a.href = url;
|
||||||
|
a.download = `transport-log-${new Date().toISOString().split("T")[0]}.csv`;
|
||||||
|
a.click();
|
||||||
|
window.URL.revokeObjectURL(url);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleLogout = () => {
|
||||||
|
localStorage.removeItem("user");
|
||||||
|
router.push("/login");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="inline-block animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
||||||
|
<p className="mt-4 text-gray-600">Loading dashboard...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isLoggedIn) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const entryCount = actions.filter((a) => a.actionType === "entry").length;
|
||||||
|
const exitCount = actions.filter((a) => a.actionType === "exit").length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
||||||
defaultButtonVariant="bounce-effect"
|
{/* Header */}
|
||||||
defaultTextAnimation="entrance-slide"
|
<header className="bg-white shadow-sm sticky top-0 z-50">
|
||||||
borderRadius="soft"
|
<div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
|
||||||
contentWidth="smallMedium"
|
<Link href="/" className="flex items-center space-x-2">
|
||||||
sizing="largeSmallSizeMediumTitles"
|
<div className="text-2xl font-bold text-blue-600">TransportLog</div>
|
||||||
background="fluid"
|
</Link>
|
||||||
cardStyle="gradient-mesh"
|
<button
|
||||||
primaryButtonStyle="double-inset"
|
onClick={handleLogout}
|
||||||
secondaryButtonStyle="glass"
|
className="flex items-center space-x-2 text-red-600 hover:text-red-700 font-semibold"
|
||||||
headingFontWeight="light"
|
>
|
||||||
>
|
<LogOut className="w-5 h-5" />
|
||||||
<div id="nav" data-section="nav">
|
<span>Logout</span>
|
||||||
<NavbarStyleCentered
|
</button>
|
||||||
navItems={[
|
</div>
|
||||||
{ name: "Home", id: "home" },
|
</header>
|
||||||
{ name: "Features", id: "features" },
|
|
||||||
{ name: "How It Works", id: "how-it-works" },
|
|
||||||
{ name: "About", id: "about" },
|
|
||||||
{ name: "Contact", id: "contact" }
|
|
||||||
]}
|
|
||||||
button={{ text: "Login", href: "/login" }}
|
|
||||||
brandName="TransportLog"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="dashboard-hero" data-section="dashboard-hero" className="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-50 to-gray-100 px-4">
|
<div className="max-w-7xl mx-auto px-4 py-8">
|
||||||
<div className="text-center max-w-2xl mx-auto">
|
{/* Stats */}
|
||||||
<h1 className="text-5xl md:text-6xl font-light text-foreground mb-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||||
Your Transport Dashboard
|
<div className="bg-white rounded-lg shadow p-6">
|
||||||
</h1>
|
<div className="flex items-center justify-between">
|
||||||
<p className="text-xl text-gray-600 mb-8">
|
<div>
|
||||||
Real-time monitoring of all vehicle entries and exits. Track movements, access logs, and generate reports from a single unified dashboard.
|
<p className="text-gray-600 text-sm font-medium">Total Entries</p>
|
||||||
</p>
|
<p className="text-3xl font-bold text-blue-600 mt-2">{entryCount}</p>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12">
|
</div>
|
||||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
<Plus className="w-12 h-12 text-blue-100" />
|
||||||
<h3 className="text-2xl font-bold text-accent mb-2">150+</h3>
|
|
||||||
<p className="text-gray-600">Vehicles Tracked</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
</div>
|
||||||
<h3 className="text-2xl font-bold text-accent mb-2">99.9%</h3>
|
|
||||||
<p className="text-gray-600">System Uptime</p>
|
<div className="bg-white rounded-lg shadow p-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-gray-600 text-sm font-medium">Total Exits</p>
|
||||||
|
<p className="text-3xl font-bold text-green-600 mt-2">{exitCount}</p>
|
||||||
|
</div>
|
||||||
|
<ChevronDown className="w-12 h-12 text-green-100" />
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
</div>
|
||||||
<h3 className="text-2xl font-bold text-accent mb-2">24/7</h3>
|
|
||||||
<p className="text-gray-600">Real-Time Monitoring</p>
|
<div className="bg-white rounded-lg shadow p-6">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<p className="text-gray-600 text-sm font-medium">Total Records</p>
|
||||||
|
<p className="text-3xl font-bold text-indigo-600 mt-2">{actions.length}</p>
|
||||||
|
</div>
|
||||||
|
<TrendingUp className="w-12 h-12 text-indigo-100" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="dashboard-features" data-section="dashboard-features" className="py-20 px-4 bg-white">
|
{/* Action Buttons */}
|
||||||
<div className="max-w-4xl mx-auto">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||||
<h2 className="text-4xl font-light text-foreground mb-12 text-center">
|
<button
|
||||||
Dashboard Features
|
onClick={() => setShowEntryForm(!showEntryForm)}
|
||||||
</h2>
|
className="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-3 px-4 rounded-lg flex items-center justify-center space-x-2 transition"
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
>
|
||||||
<div className="bg-gradient-to-br from-blue-50 to-gray-50 rounded-lg p-8 border border-gray-200">
|
<Plus className="w-5 h-5" />
|
||||||
<div className="text-accent text-3xl mb-4">📊</div>
|
<span>Record Entry</span>
|
||||||
<h3 className="text-2xl font-semibold text-foreground mb-3">Live Vehicle Tracking</h3>
|
</button>
|
||||||
<p className="text-gray-600">
|
|
||||||
See all vehicle movements in real-time. Know exactly when each vehicle enters and exits your facility with precise timestamps and location data.
|
<button
|
||||||
</p>
|
onClick={() => setShowExitForm(!showExitForm)}
|
||||||
|
className="bg-green-600 hover:bg-green-700 text-white font-semibold py-3 px-4 rounded-lg flex items-center justify-center space-x-2 transition"
|
||||||
|
>
|
||||||
|
<ChevronDown className="w-5 h-5" />
|
||||||
|
<span>Record Exit</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={downloadReport}
|
||||||
|
className="bg-indigo-600 hover:bg-indigo-700 text-white font-semibold py-3 px-4 rounded-lg flex items-center justify-center space-x-2 transition"
|
||||||
|
>
|
||||||
|
<Download className="w-5 h-5" />
|
||||||
|
<span>Download Report</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Forms */}
|
||||||
|
{(showEntryForm || showExitForm) && (
|
||||||
|
<div className="bg-white rounded-lg shadow p-6 mb-8">
|
||||||
|
<h3 className="text-xl font-bold text-gray-900 mb-4">
|
||||||
|
{showEntryForm ? "Record Vehicle Entry" : "Record Vehicle Exit"}
|
||||||
|
</h3>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Vehicle ID"
|
||||||
|
value={formData.vehicleId}
|
||||||
|
onChange={(e) => setFormData({ ...formData, vehicleId: e.target.value })}
|
||||||
|
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Vehicle Plate (e.g., ABC-1234)"
|
||||||
|
value={formData.vehiclePlate}
|
||||||
|
onChange={(e) => setFormData({ ...formData, vehiclePlate: e.target.value })}
|
||||||
|
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Driver Name"
|
||||||
|
value={formData.driverName}
|
||||||
|
onChange={(e) => setFormData({ ...formData, driverName: e.target.value })}
|
||||||
|
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
|
||||||
|
/>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Location"
|
||||||
|
value={formData.location}
|
||||||
|
onChange={(e) => setFormData({ ...formData, location: e.target.value })}
|
||||||
|
className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gradient-to-br from-blue-50 to-gray-50 rounded-lg p-8 border border-gray-200">
|
<textarea
|
||||||
<div className="text-accent text-3xl mb-4">📝</div>
|
placeholder="Additional notes (optional)"
|
||||||
<h3 className="text-2xl font-semibold text-foreground mb-3">Complete Audit Trail</h3>
|
value={formData.notes}
|
||||||
<p className="text-gray-600">
|
onChange={(e) => setFormData({ ...formData, notes: e.target.value })}
|
||||||
Every action is logged with driver information, cargo details, and system metadata. Perfect for compliance and historical analysis.
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none mb-4"
|
||||||
</p>
|
rows={3}
|
||||||
|
/>
|
||||||
|
<div className="flex space-x-3">
|
||||||
|
{showEntryForm && (
|
||||||
|
<button
|
||||||
|
onClick={handleAddEntry}
|
||||||
|
className="bg-blue-600 hover:bg-blue-700 text-white font-semibold py-2 px-6 rounded-lg transition"
|
||||||
|
>
|
||||||
|
Save Entry
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
{showExitForm && (
|
||||||
|
<button
|
||||||
|
onClick={handleAddExit}
|
||||||
|
className="bg-green-600 hover:bg-green-700 text-white font-semibold py-2 px-6 rounded-lg transition"
|
||||||
|
>
|
||||||
|
Save Exit
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setShowEntryForm(false);
|
||||||
|
setShowExitForm(false);
|
||||||
|
setFormData({ vehicleId: "", vehiclePlate: "", driverName: "", location: "", notes: "" });
|
||||||
|
}}
|
||||||
|
className="bg-gray-300 hover:bg-gray-400 text-gray-800 font-semibold py-2 px-6 rounded-lg transition"
|
||||||
|
>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gradient-to-br from-blue-50 to-gray-50 rounded-lg p-8 border border-gray-200">
|
</div>
|
||||||
<div className="text-accent text-3xl mb-4">📥</div>
|
)}
|
||||||
<h3 className="text-2xl font-semibold text-foreground mb-3">Entry Management</h3>
|
|
||||||
<p className="text-gray-600">
|
{/* Filters & Search */}
|
||||||
Quick entry recording with automatic timestamp, vehicle recognition, and anomaly detection for unusual patterns.
|
<div className="bg-white rounded-lg shadow p-6 mb-8">
|
||||||
</p>
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-end">
|
||||||
|
<div>
|
||||||
|
<label className="block text-sm font-medium text-gray-700 mb-2">Search</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search by vehicle plate, driver name, or ID..."
|
||||||
|
value={searchTerm}
|
||||||
|
onChange={(e) => handleSearchChange(e.target.value)}
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gradient-to-br from-blue-50 to-gray-50 rounded-lg p-8 border border-gray-200">
|
<div>
|
||||||
<div className="text-accent text-3xl mb-4">📤</div>
|
<label className="block text-sm font-medium text-gray-700 mb-2">Filter by Type</label>
|
||||||
<h3 className="text-2xl font-semibold text-foreground mb-3">Exit Documentation</h3>
|
<div className="flex space-x-2">
|
||||||
<p className="text-gray-600">
|
<button
|
||||||
Capture exit details including cargo status, fuel levels, and maintenance notes for complete vehicle history tracking.
|
onClick={() => handleFilterChange("all")}
|
||||||
</p>
|
className={`px-4 py-2 rounded-lg font-semibold transition ${
|
||||||
</div>
|
filterType === "all" ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
||||||
<div className="bg-gradient-to-br from-blue-50 to-gray-50 rounded-lg p-8 border border-gray-200">
|
}`}
|
||||||
<div className="text-accent text-3xl mb-4">📊</div>
|
>
|
||||||
<h3 className="text-2xl font-semibold text-foreground mb-3">Advanced Analytics</h3>
|
All
|
||||||
<p className="text-gray-600">
|
</button>
|
||||||
Visualize trends, patterns, and performance metrics. Identify peak times, vehicle utilization, and operational efficiency opportunities.
|
<button
|
||||||
</p>
|
onClick={() => handleFilterChange("entry")}
|
||||||
</div>
|
className={`px-4 py-2 rounded-lg font-semibold transition ${
|
||||||
<div className="bg-gradient-to-br from-blue-50 to-gray-50 rounded-lg p-8 border border-gray-200">
|
filterType === "entry" ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
||||||
<div className="text-accent text-3xl mb-4">🔐</div>
|
}`}
|
||||||
<h3 className="text-2xl font-semibold text-foreground mb-3">Secure Access Control</h3>
|
>
|
||||||
<p className="text-gray-600">
|
Entry
|
||||||
Role-based permissions ensure only authorized personnel can view specific data. Multi-factor authentication protects sensitive information.
|
</button>
|
||||||
</p>
|
<button
|
||||||
|
onClick={() => handleFilterChange("exit")}
|
||||||
|
className={`px-4 py-2 rounded-lg font-semibold transition ${
|
||||||
|
filterType === "exit" ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
Exit
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="dashboard-cta" data-section="dashboard-cta" className="py-20 px-4 bg-gradient-to-r from-accent to-blue-900">
|
{/* Data Table */}
|
||||||
<div className="max-w-3xl mx-auto text-center">
|
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||||
<h2 className="text-4xl font-light text-white mb-6">
|
{filteredActions.length === 0 ? (
|
||||||
Ready to Access Your Dashboard?
|
<div className="p-12 text-center">
|
||||||
</h2>
|
<AlertCircle className="w-12 h-12 text-gray-300 mx-auto mb-4" />
|
||||||
<p className="text-xl text-blue-100 mb-8">
|
<p className="text-gray-500 text-lg">No transport records found</p>
|
||||||
Log in now to start monitoring your transport operations with real-time precision.
|
<p className="text-gray-400 text-sm mt-1">Start by recording vehicle entries or exits</p>
|
||||||
</p>
|
</div>
|
||||||
<div className="flex flex-col sm:flex-row gap-4 justify-center">
|
) : (
|
||||||
<Link href="/login" className="bg-white text-accent px-8 py-3 rounded-lg font-semibold hover:bg-gray-100 transition">
|
<div className="overflow-x-auto">
|
||||||
Log In Now
|
<table className="w-full">
|
||||||
</Link>
|
<thead className="bg-gray-50 border-b border-gray-200">
|
||||||
<Link href="/" className="bg-transparent text-white border-2 border-white px-8 py-3 rounded-lg font-semibold hover:bg-white hover:bg-opacity-10 transition">
|
<tr>
|
||||||
Back to Home
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Vehicle Plate</th>
|
||||||
</Link>
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Driver Name</th>
|
||||||
</div>
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Action Type</th>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Timestamp</th>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Location</th>
|
||||||
|
<th className="px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Notes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody className="divide-y divide-gray-200">
|
||||||
|
{filteredActions.map((action) => (
|
||||||
|
<tr key={action.id} className="hover:bg-gray-50 transition">
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">{action.vehiclePlate}</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-600">{action.driverName}</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm">
|
||||||
|
<span
|
||||||
|
className={`px-3 py-1 rounded-full text-xs font-semibold ${
|
||||||
|
action.actionType === "entry"
|
||||||
|
? "bg-blue-100 text-blue-800"
|
||||||
|
: "bg-green-100 text-green-800"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{action.actionType.charAt(0).toUpperCase() + action.actionType.slice(1)}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-600">
|
||||||
|
{new Date(action.timestamp).toLocaleString()}
|
||||||
|
</td>
|
||||||
|
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-600">{action.location}</td>
|
||||||
|
<td className="px-6 py-4 text-sm text-gray-600 max-w-xs truncate">{action.notes}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pagination Info */}
|
||||||
|
<div className="mt-4 text-center text-sm text-gray-600">
|
||||||
|
Showing {filteredActions.length} of {actions.length} records
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div id="footer" data-section="footer">
|
|
||||||
<FooterLogoReveal
|
|
||||||
logoText="TransportLog"
|
|
||||||
leftLink={{ text: "Privacy Policy", href: "/" }}
|
|
||||||
rightLink={{ text: "Terms of Service", href: "/" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,55 +1,20 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
import { Inter } from "next/font/google";
|
import { Inter } from "next/font/google";
|
||||||
import { Open_Sans } from "next/font/google";
|
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import { ServiceWrapper } from "@/components/ServiceWrapper";
|
|
||||||
import Tag from "@/tag/Tag";
|
|
||||||
|
|
||||||
const inter = Inter({
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
variable: "--font-inter",
|
|
||||||
subsets: ["latin"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const openSans = Open_Sans({
|
|
||||||
variable: "--font-open-sans",
|
|
||||||
subsets: ["latin"],
|
|
||||||
});
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "TransportLog - Real-Time Transport Action Tracking",
|
title: "TransportLog - Transport Action Recording System", description: "Record vehicle entries and exits. Track transport movements with real-time monitoring and downloadable reports."};
|
||||||
description: "Record vehicle entries and exits instantly. Secure authentication, real-time dashboards, downloadable reports for complete transport visibility.",
|
|
||||||
keywords: "transport tracking, vehicle logging, fleet management, entry exit logs, logistics software, downloadable reports",
|
|
||||||
robots: {
|
|
||||||
index: true,
|
|
||||||
follow: true,
|
|
||||||
},
|
|
||||||
openGraph: {
|
|
||||||
title: "TransportLog - Real-Time Transport Action Tracking",
|
|
||||||
description: "Track every transport movement with precision. Entry/exit logging, secure access, downloadable reports.",
|
|
||||||
type: "website",
|
|
||||||
siteName: "TransportLog",
|
|
||||||
},
|
|
||||||
twitter: {
|
|
||||||
card: "summary_large_image",
|
|
||||||
title: "TransportLog - Real-Time Transport Action Tracking",
|
|
||||||
description: "Track every transport movement with precision. Entry/exit logging, secure access, downloadable reports.",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="en" suppressHydrationWarning>
|
<html lang="en">
|
||||||
<ServiceWrapper>
|
<body className={inter.className}>{children}
|
||||||
<body
|
|
||||||
className={`${inter.variable} ${openSans.variable} antialiased`}
|
|
||||||
>
|
|
||||||
<Tag />
|
|
||||||
{children}
|
|
||||||
|
|
||||||
<script
|
<script
|
||||||
dangerouslySetInnerHTML={{
|
dangerouslySetInnerHTML={{
|
||||||
__html: `
|
__html: `
|
||||||
@@ -1417,7 +1382,6 @@ export default function RootLayout({
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</body>
|
</body>
|
||||||
</ServiceWrapper>
|
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,166 +1,149 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
import { useState } from "react";
|
||||||
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
|
import { useRouter } from "next/navigation";
|
||||||
import FooterLogoReveal from '@/components/sections/footer/FooterLogoReveal';
|
import Link from "next/link";
|
||||||
import Link from 'next/link';
|
import { LogIn, Eye, EyeOff, AlertCircle } from "lucide-react";
|
||||||
import { Lock, Mail } from 'lucide-react';
|
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
export default function LoginPage() {
|
export default function LoginPage() {
|
||||||
const [email, setEmail] = useState('');
|
const router = useRouter();
|
||||||
const [password, setPassword] = useState('');
|
const [email, setEmail] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [showPassword, setShowPassword] = useState(false);
|
||||||
|
const [error, setError] = useState("");
|
||||||
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
|
const handleSubmit = async (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
setError("");
|
||||||
|
setIsLoading(true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Simulate login - in production, call your API
|
||||||
|
if (!email || !password) {
|
||||||
|
setError("Please enter both email and password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||||
|
setError("Please enter a valid email address");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password.length < 6) {
|
||||||
|
setError("Password must be at least 6 characters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simulate successful login
|
||||||
|
localStorage.setItem("user", JSON.stringify({ email, isLoggedIn: true }));
|
||||||
|
router.push("/dashboard");
|
||||||
|
} catch (err) {
|
||||||
|
setError("Login failed. Please try again.");
|
||||||
|
} finally {
|
||||||
|
setIsLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider
|
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
|
||||||
defaultButtonVariant="bounce-effect"
|
<div className="w-full max-w-md">
|
||||||
defaultTextAnimation="entrance-slide"
|
<div className="bg-white rounded-lg shadow-xl p-8">
|
||||||
borderRadius="soft"
|
{/* Header */}
|
||||||
contentWidth="smallMedium"
|
<div className="flex items-center justify-center mb-8">
|
||||||
sizing="largeSmallSizeMediumTitles"
|
<LogIn className="w-8 h-8 text-blue-600 mr-3" />
|
||||||
background="fluid"
|
<h1 className="text-3xl font-bold text-gray-900">TransportLog</h1>
|
||||||
cardStyle="gradient-mesh"
|
|
||||||
primaryButtonStyle="double-inset"
|
|
||||||
secondaryButtonStyle="glass"
|
|
||||||
headingFontWeight="light"
|
|
||||||
>
|
|
||||||
<div id="nav" data-section="nav">
|
|
||||||
<NavbarStyleCentered
|
|
||||||
navItems={[
|
|
||||||
{ name: "Home", id: "home" },
|
|
||||||
{ name: "Features", id: "features" },
|
|
||||||
{ name: "How It Works", id: "how-it-works" },
|
|
||||||
{ name: "About", id: "about" },
|
|
||||||
{ name: "Contact", id: "contact" }
|
|
||||||
]}
|
|
||||||
button={{ text: "Login", href: "/login" }}
|
|
||||||
brandName="TransportLog"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="login-hero" data-section="login-hero" className="min-h-screen flex items-center justify-center bg-gradient-to-br from-gray-50 to-gray-100 px-4 py-20">
|
|
||||||
<div className="w-full max-w-md">
|
|
||||||
<div className="bg-white rounded-lg shadow-lg border border-gray-200 p-8">
|
|
||||||
<div className="text-center mb-8">
|
|
||||||
<h1 className="text-3xl font-light text-foreground mb-2">
|
|
||||||
Welcome Back
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Sign in to access your transport dashboard
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-foreground mb-2">
|
|
||||||
Email Address
|
|
||||||
</label>
|
|
||||||
<div className="relative">
|
|
||||||
<Mail className="absolute left-3 top-3.5 text-gray-400 h-5 w-5" />
|
|
||||||
<input
|
|
||||||
type="email"
|
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
placeholder="you@example.com"
|
|
||||||
className="w-full pl-10 pr-4 py-2.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<label className="block text-sm font-medium text-foreground mb-2">
|
|
||||||
Password
|
|
||||||
</label>
|
|
||||||
<div className="relative">
|
|
||||||
<Lock className="absolute left-3 top-3.5 text-gray-400 h-5 w-5" />
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
value={password}
|
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
placeholder="••••••••"
|
|
||||||
className="w-full pl-10 pr-4 py-2.5 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<label className="flex items-center">
|
|
||||||
<input type="checkbox" className="w-4 h-4 text-accent border-gray-300 rounded" />
|
|
||||||
<span className="ml-2 text-sm text-gray-600">Remember me</span>
|
|
||||||
</label>
|
|
||||||
<Link href="#" className="text-sm text-accent hover:underline">
|
|
||||||
Forgot password?
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
className="w-full bg-accent text-white py-2.5 rounded-lg font-semibold hover:bg-blue-900 transition"
|
|
||||||
>
|
|
||||||
Sign In
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div className="mt-6 pt-6 border-t border-gray-200 text-center">
|
|
||||||
<p className="text-gray-600 text-sm">
|
|
||||||
Don't have an account?{" "}
|
|
||||||
<Link href="/" className="text-accent font-semibold hover:underline">
|
|
||||||
Start free trial
|
|
||||||
</Link>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-8 bg-blue-50 rounded-lg p-6 border border-blue-200">
|
<h2 className="text-2xl font-bold text-gray-900 mb-2 text-center">Welcome Back</h2>
|
||||||
<h3 className="font-semibold text-foreground mb-3">Demo Credentials:</h3>
|
<p className="text-gray-600 text-center mb-8">Sign in to your account to continue</p>
|
||||||
<p className="text-sm text-gray-700 mb-2">
|
|
||||||
<strong>Email:</strong> demo@transportlog.com
|
{/* Error Alert */}
|
||||||
</p>
|
{error && (
|
||||||
<p className="text-sm text-gray-700">
|
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg flex items-start">
|
||||||
<strong>Password:</strong> demo123456
|
<AlertCircle className="w-5 h-5 text-red-600 mr-3 flex-shrink-0 mt-0.5" />
|
||||||
</p>
|
<p className="text-red-700 text-sm">{error}</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Form */}
|
||||||
|
<form onSubmit={handleSubmit} className="space-y-6">
|
||||||
|
{/* Email */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Email Address
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="email"
|
||||||
|
type="email"
|
||||||
|
value={email}
|
||||||
|
onChange={(e) => setEmail(e.target.value)}
|
||||||
|
placeholder="you@example.com"
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Password */}
|
||||||
|
<div>
|
||||||
|
<label htmlFor="password" className="block text-sm font-medium text-gray-700 mb-2">
|
||||||
|
Password
|
||||||
|
</label>
|
||||||
|
<div className="relative">
|
||||||
|
<input
|
||||||
|
id="password"
|
||||||
|
type={showPassword ? "text" : "password"}
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
placeholder="••••••••"
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent outline-none transition"
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setShowPassword(!showPassword)}
|
||||||
|
className="absolute right-3 top-2.5 text-gray-500 hover:text-gray-700"
|
||||||
|
>
|
||||||
|
{showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Remember & Forgot */}
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<label className="flex items-center">
|
||||||
|
<input type="checkbox" className="w-4 h-4 text-blue-600 rounded" />
|
||||||
|
<span className="ml-2 text-sm text-gray-700">Remember me</span>
|
||||||
|
</label>
|
||||||
|
<Link href="#" className="text-sm text-blue-600 hover:text-blue-700">
|
||||||
|
Forgot password?
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Submit Button */}
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={isLoading}
|
||||||
|
className="w-full bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-semibold py-2 px-4 rounded-lg transition duration-200"
|
||||||
|
>
|
||||||
|
{isLoading ? "Signing in..." : "Sign In"}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{/* Demo Credentials */}
|
||||||
|
<div className="mt-8 p-4 bg-blue-50 rounded-lg border border-blue-200">
|
||||||
|
<p className="text-sm font-semibold text-blue-900 mb-2">Demo Credentials:</p>
|
||||||
|
<p className="text-sm text-blue-800">Email: <span className="font-mono">demo@transportlog.com</span></p>
|
||||||
|
<p className="text-sm text-blue-800">Password: <span className="font-mono">demo123456</span></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<p className="mt-8 text-center text-gray-600 text-sm">
|
||||||
|
New to TransportLog?{" "}
|
||||||
|
<Link href="/" className="text-blue-600 hover:text-blue-700 font-semibold">
|
||||||
|
Sign up
|
||||||
|
</Link>
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<div id="login-features" data-section="login-features" className="py-16 px-4 bg-white">
|
|
||||||
<div className="max-w-4xl mx-auto">
|
|
||||||
<h2 className="text-3xl font-light text-foreground mb-12 text-center">
|
|
||||||
Why Transport Companies Trust TransportLog
|
|
||||||
</h2>
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-4xl mb-4">🔒</div>
|
|
||||||
<h3 className="text-xl font-semibold text-foreground mb-2">Enterprise Security</h3>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Military-grade encryption protects your data with multi-factor authentication
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-4xl mb-4">⚡</div>
|
|
||||||
<h3 className="text-xl font-semibold text-foreground mb-2">Lightning Fast</h3>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Real-time updates and instant report generation for immediate insights
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="text-4xl mb-4">📱</div>
|
|
||||||
<h3 className="text-xl font-semibold text-foreground mb-2">Mobile Ready</h3>
|
|
||||||
<p className="text-gray-600">
|
|
||||||
Access your dashboard anywhere, anytime with full mobile support
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="footer" data-section="footer">
|
|
||||||
<FooterLogoReveal
|
|
||||||
logoText="TransportLog"
|
|
||||||
leftLink={{ text: "Privacy Policy", href: "/" }}
|
|
||||||
rightLink={{ text: "Terms of Service", href: "/" }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -28,7 +28,7 @@ export default function HomePage() {
|
|||||||
<div id="nav" data-section="nav">
|
<div id="nav" data-section="nav">
|
||||||
<NavbarStyleCentered
|
<NavbarStyleCentered
|
||||||
navItems={[
|
navItems={[
|
||||||
{ name: "Home", id: "home" },
|
{ name: "Home", id: "/" },
|
||||||
{ name: "Features", id: "features" },
|
{ name: "Features", id: "features" },
|
||||||
{ name: "How It Works", id: "how-it-works" },
|
{ name: "How It Works", id: "how-it-works" },
|
||||||
{ name: "About", id: "about" },
|
{ name: "About", id: "about" },
|
||||||
|
|||||||
Reference in New Issue
Block a user