Merge version_2 into main #4
@@ -1,141 +1,438 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
|
||||
import FooterLogoReveal from '@/components/sections/footer/FooterLogoReveal';
|
||||
import Link from 'next/link';
|
||||
import { useState, useEffect } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
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() {
|
||||
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 (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="bounce-effect"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="soft"
|
||||
contentWidth="smallMedium"
|
||||
sizing="largeSmallSizeMediumTitles"
|
||||
background="fluid"
|
||||
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 className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
|
||||
{/* Header */}
|
||||
<header className="bg-white shadow-sm sticky top-0 z-50">
|
||||
<div className="max-w-7xl mx-auto px-4 py-4 flex items-center justify-between">
|
||||
<Link href="/" className="flex items-center space-x-2">
|
||||
<div className="text-2xl font-bold text-blue-600">TransportLog</div>
|
||||
</Link>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex items-center space-x-2 text-red-600 hover:text-red-700 font-semibold"
|
||||
>
|
||||
<LogOut className="w-5 h-5" />
|
||||
<span>Logout</span>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<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="text-center max-w-2xl mx-auto">
|
||||
<h1 className="text-5xl md:text-6xl font-light text-foreground mb-6">
|
||||
Your Transport Dashboard
|
||||
</h1>
|
||||
<p className="text-xl text-gray-600 mb-8">
|
||||
Real-time monitoring of all vehicle entries and exits. Track movements, access logs, and generate reports from a single unified dashboard.
|
||||
</p>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-12">
|
||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h3 className="text-2xl font-bold text-accent mb-2">150+</h3>
|
||||
<p className="text-gray-600">Vehicles Tracked</p>
|
||||
<div className="max-w-7xl mx-auto px-4 py-8">
|
||||
{/* Stats */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||
<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 Entries</p>
|
||||
<p className="text-3xl font-bold text-blue-600 mt-2">{entryCount}</p>
|
||||
</div>
|
||||
<Plus className="w-12 h-12 text-blue-100" />
|
||||
</div>
|
||||
<div className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h3 className="text-2xl font-bold text-accent mb-2">99.9%</h3>
|
||||
<p className="text-gray-600">System Uptime</p>
|
||||
</div>
|
||||
|
||||
<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 className="bg-white rounded-lg p-6 shadow-sm border border-gray-200">
|
||||
<h3 className="text-2xl font-bold text-accent mb-2">24/7</h3>
|
||||
<p className="text-gray-600">Real-Time Monitoring</p>
|
||||
</div>
|
||||
|
||||
<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 id="dashboard-features" data-section="dashboard-features" className="py-20 px-4 bg-white">
|
||||
<div className="max-w-4xl mx-auto">
|
||||
<h2 className="text-4xl font-light text-foreground mb-12 text-center">
|
||||
Dashboard Features
|
||||
</h2>
|
||||
<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">
|
||||
<div className="text-accent text-3xl mb-4">📊</div>
|
||||
<h3 className="text-2xl font-semibold text-foreground mb-3">Live Vehicle Tracking</h3>
|
||||
<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.
|
||||
</p>
|
||||
{/* Action Buttons */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8">
|
||||
<button
|
||||
onClick={() => setShowEntryForm(!showEntryForm)}
|
||||
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"
|
||||
>
|
||||
<Plus className="w-5 h-5" />
|
||||
<span>Record Entry</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
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 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">Complete Audit Trail</h3>
|
||||
<p className="text-gray-600">
|
||||
Every action is logged with driver information, cargo details, and system metadata. Perfect for compliance and historical analysis.
|
||||
</p>
|
||||
<textarea
|
||||
placeholder="Additional notes (optional)"
|
||||
value={formData.notes}
|
||||
onChange={(e) => setFormData({ ...formData, notes: 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 mb-4"
|
||||
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 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">Entry Management</h3>
|
||||
<p className="text-gray-600">
|
||||
Quick entry recording with automatic timestamp, vehicle recognition, and anomaly detection for unusual patterns.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Filters & Search */}
|
||||
<div className="bg-white rounded-lg shadow p-6 mb-8">
|
||||
<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 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">Exit Documentation</h3>
|
||||
<p className="text-gray-600">
|
||||
Capture exit details including cargo status, fuel levels, and maintenance notes for complete vehicle history tracking.
|
||||
</p>
|
||||
</div>
|
||||
<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>
|
||||
<p className="text-gray-600">
|
||||
Visualize trends, patterns, and performance metrics. Identify peak times, vehicle utilization, and operational efficiency opportunities.
|
||||
</p>
|
||||
</div>
|
||||
<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">Secure Access Control</h3>
|
||||
<p className="text-gray-600">
|
||||
Role-based permissions ensure only authorized personnel can view specific data. Multi-factor authentication protects sensitive information.
|
||||
</p>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-2">Filter by Type</label>
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={() => handleFilterChange("all")}
|
||||
className={`px-4 py-2 rounded-lg font-semibold transition ${
|
||||
filterType === "all" ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
||||
}`}
|
||||
>
|
||||
All
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleFilterChange("entry")}
|
||||
className={`px-4 py-2 rounded-lg font-semibold transition ${
|
||||
filterType === "entry" ? "bg-blue-600 text-white" : "bg-gray-200 text-gray-700 hover:bg-gray-300"
|
||||
}`}
|
||||
>
|
||||
Entry
|
||||
</button>
|
||||
<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 id="dashboard-cta" data-section="dashboard-cta" className="py-20 px-4 bg-gradient-to-r from-accent to-blue-900">
|
||||
<div className="max-w-3xl mx-auto text-center">
|
||||
<h2 className="text-4xl font-light text-white mb-6">
|
||||
Ready to Access Your Dashboard?
|
||||
</h2>
|
||||
<p className="text-xl text-blue-100 mb-8">
|
||||
Log in now to start monitoring your transport operations with real-time precision.
|
||||
</p>
|
||||
<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">
|
||||
Log In Now
|
||||
</Link>
|
||||
<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">
|
||||
Back to Home
|
||||
</Link>
|
||||
</div>
|
||||
{/* Data Table */}
|
||||
<div className="bg-white rounded-lg shadow overflow-hidden">
|
||||
{filteredActions.length === 0 ? (
|
||||
<div className="p-12 text-center">
|
||||
<AlertCircle className="w-12 h-12 text-gray-300 mx-auto mb-4" />
|
||||
<p className="text-gray-500 text-lg">No transport records found</p>
|
||||
<p className="text-gray-400 text-sm mt-1">Start by recording vehicle entries or exits</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
<table className="w-full">
|
||||
<thead className="bg-gray-50 border-b border-gray-200">
|
||||
<tr>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Vehicle Plate</th>
|
||||
<th className="px-6 py-3 text-left text-xs font-medium text-gray-700 uppercase tracking-wider">Driver Name</th>
|
||||
<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 id="footer" data-section="footer">
|
||||
<FooterLogoReveal
|
||||
logoText="TransportLog"
|
||||
leftLink={{ text: "Privacy Policy", href: "/" }}
|
||||
rightLink={{ text: "Terms of Service", href: "/" }}
|
||||
/>
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,20 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Inter } from "next/font/google";
|
||||
import { Open_Sans } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { ServiceWrapper } from "@/components/ServiceWrapper";
|
||||
import Tag from "@/tag/Tag";
|
||||
|
||||
const inter = Inter({
|
||||
variable: "--font-inter",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const openSans = Open_Sans({
|
||||
variable: "--font-open-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "TransportLog - Real-Time Transport Action Tracking",
|
||||
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.",
|
||||
},
|
||||
};
|
||||
title: "TransportLog - Transport Action Recording System", description: "Record vehicle entries and exits. Track transport movements with real-time monitoring and downloadable reports."};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
}) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<ServiceWrapper>
|
||||
<body
|
||||
className={`${inter.variable} ${openSans.variable} antialiased`}
|
||||
>
|
||||
<Tag />
|
||||
{children}
|
||||
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}
|
||||
<script
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
@@ -1417,7 +1382,6 @@ export default function RootLayout({
|
||||
}}
|
||||
/>
|
||||
</body>
|
||||
</ServiceWrapper>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,166 +1,149 @@
|
||||
"use client";
|
||||
|
||||
import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider";
|
||||
import NavbarStyleCentered from '@/components/navbar/NavbarStyleCentered/NavbarStyleCentered';
|
||||
import FooterLogoReveal from '@/components/sections/footer/FooterLogoReveal';
|
||||
import Link from 'next/link';
|
||||
import { Lock, Mail } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { LogIn, Eye, EyeOff, AlertCircle } from "lucide-react";
|
||||
|
||||
export default function LoginPage() {
|
||||
const [email, setEmail] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const router = useRouter();
|
||||
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 (
|
||||
<ThemeProvider
|
||||
defaultButtonVariant="bounce-effect"
|
||||
defaultTextAnimation="entrance-slide"
|
||||
borderRadius="soft"
|
||||
contentWidth="smallMedium"
|
||||
sizing="largeSmallSizeMediumTitles"
|
||||
background="fluid"
|
||||
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 className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100 flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-md">
|
||||
<div className="bg-white rounded-lg shadow-xl p-8">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-center mb-8">
|
||||
<LogIn className="w-8 h-8 text-blue-600 mr-3" />
|
||||
<h1 className="text-3xl font-bold text-gray-900">TransportLog</h1>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 bg-blue-50 rounded-lg p-6 border border-blue-200">
|
||||
<h3 className="font-semibold text-foreground mb-3">Demo Credentials:</h3>
|
||||
<p className="text-sm text-gray-700 mb-2">
|
||||
<strong>Email:</strong> demo@transportlog.com
|
||||
</p>
|
||||
<p className="text-sm text-gray-700">
|
||||
<strong>Password:</strong> demo123456
|
||||
</p>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-2 text-center">Welcome Back</h2>
|
||||
<p className="text-gray-600 text-center mb-8">Sign in to your account to continue</p>
|
||||
|
||||
{/* Error Alert */}
|
||||
{error && (
|
||||
<div className="mb-6 p-4 bg-red-50 border border-red-200 rounded-lg flex items-start">
|
||||
<AlertCircle className="w-5 h-5 text-red-600 mr-3 flex-shrink-0 mt-0.5" />
|
||||
<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>
|
||||
|
||||
{/* 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 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>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ export default function HomePage() {
|
||||
<div id="nav" data-section="nav">
|
||||
<NavbarStyleCentered
|
||||
navItems={[
|
||||
{ name: "Home", id: "home" },
|
||||
{ name: "Home", id: "/" },
|
||||
{ name: "Features", id: "features" },
|
||||
{ name: "How It Works", id: "how-it-works" },
|
||||
{ name: "About", id: "about" },
|
||||
@@ -208,4 +208,4 @@ export default function HomePage() {
|
||||
</div>
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user