refactor: replace revenue and geofence pages with new hub management system

This commit is contained in:
sazzadulalambd
2026-04-26 16:02:53 +06:00
parent f724a00453
commit 03062bfc48
8 changed files with 884 additions and 291 deletions

View File

@@ -0,0 +1,438 @@
'use client';
import { useState, useEffect } from 'react';
import { useParams, useRouter } from 'next/navigation';
import {
ArrowLeft, MapPin, Phone, Clock, Bike, Plus, X, Edit, Save, Trash2,
Navigation, User, Wallet, DollarSign, CheckCircle, AlertTriangle
} from 'lucide-react';
interface Hub {
id: string;
name: string;
address: string;
phone: string;
managerName?: string;
bikeCount: number;
activeRentals: number;
status: 'active' | 'inactive';
coordinates?: { lat: number; lng: number };
openTime: string;
closeTime: string;
isHeadOffice?: boolean;
}
interface BikeInfo {
id: string;
model: string;
plate: string;
status: 'available' | 'rented' | 'maintenance';
}
const mockHub: Hub = {
id: 'HUB-001',
name: 'JAIBEN Head Office',
address: 'House 12, Road 17, Gulshan 1, Dhaka',
phone: '+8801712345678',
managerName: 'Rahim Ahmed',
bikeCount: 25,
activeRentals: 12,
status: 'active',
coordinates: { lat: 23.7925, lng: 90.4174 },
openTime: '06:00',
closeTime: '23:00',
isHeadOffice: true
};
const mockHubBikes: BikeInfo[] = [
{ id: 'BIKE-001', model: 'AIMA Lightning', plate: 'Dhaka Metro Cha-9012', status: 'available' },
{ id: 'BIKE-002', model: 'Yadea DT3', plate: 'Dhaka Metro Ba-5521', status: 'rented' },
{ id: 'BIKE-003', model: 'AIMA EM5', plate: 'Dhaka Metro Ko-1234', status: 'available' },
{ id: 'BIKE-004', model: 'AIMA Lightning', plate: 'Dhaka Metro Cha-9013', status: 'rented' },
{ id: 'BIKE-005', model: 'Yadea G5', plate: 'Dhaka Metro Ha-5678', status: 'maintenance' },
];
interface RentalInfo {
id: string;
userName: string;
bike: string;
plate: string;
startDate: string;
type: string;
status: 'active' | 'pending' | 'completed';
dailyRate: number;
totalPaid: number;
}
const mockHubRentals: RentalInfo[] = [
{ id: 'RNT-001', userName: 'Rahim Ahmed', bike: 'AIMA Lightning', plate: 'Dhaka Metro Cha-9012', startDate: '2024-01-15', type: 'single', status: 'active', dailyRate: 300, totalPaid: 81900 },
{ id: 'RNT-002', userName: 'Karim Hasan', bike: 'Yadea DT3', plate: 'Dhaka Metro Ba-5521', startDate: '2024-01-20', type: 'single', status: 'active', dailyRate: 200, totalPaid: 12400 },
{ id: 'RNT-003', userName: 'Jamal Uddin', bike: 'AIMA EM5', plate: 'Dhaka Metro Ko-1234', startDate: '2024-02-01', type: 'shared', status: 'pending', dailyRate: 150, totalPaid: 450 },
];
export default function HubDetailPage() {
const params = useParams();
const router = useRouter();
const id = params.id as string;
const [hub, setHub] = useState<Hub>(mockHub);
const [bikes, setBikes] = useState<BikeInfo[]>(mockHubBikes);
const [rentals, setRentals] = useState<RentalInfo[]>(mockHubRentals);
const [editMode, setEditMode] = useState(false);
const [editForm, setEditForm] = useState(hub);
const [activeTab, setActiveTab] = useState<'overview' | 'bikes' | 'rentals'>('overview');
const handleSaveEdit = () => {
setHub(editForm);
setEditMode(false);
};
if (!id || id !== 'HUB-001') {
return (
<div className="p-6 flex items-center justify-center min-h-[50vh]">
<div className="text-center">
<MapPin className="w-16 h-16 text-slate-300 mx-auto mb-4" />
<p className="text-slate-500">Hub not found</p>
<button
onClick={() => router.push('/admin/hub')}
className="mt-4 px-4 py-2 bg-accent text-white rounded-lg text-sm"
>
Back to Hubs
</button>
</div>
</div>
);
}
return (
<div className="p-4 lg:p-6 max-w-8xl mx-auto">
<button
onClick={() => router.push('/admin/hub')}
className="flex items-center gap-2 text-slate-600 hover:text-slate-800 mb-4"
>
<ArrowLeft className="w-4 h-4" /> Back to Hubs
</button>
<div className="bg-white rounded-xl shadow-sm border border-slate-100 overflow-hidden">
<div className="p-6 border-b border-slate-100">
<div className="flex flex-col lg:flex-row lg:items-start lg:justify-between gap-4">
<div>
<div className="flex items-center gap-3">
<h1 className="text-2xl font-extrabold text-slate-800">{hub.name}</h1>
<span className={`inline-flex items-center gap-1 text-xs font-medium px-2.5 py-1 rounded-full ${hub.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'
}`}>
{hub.status}
</span>
{hub.isHeadOffice && (
<span className="text-xs font-medium px-2 py-1 rounded-full bg-purple-100 text-purple-700">
Head Office
</span>
)}
</div>
<p className="text-slate-500 mt-1">{hub.address}</p>
</div>
<div className="flex gap-2">
{editMode ? (
<>
<button onClick={handleSaveEdit} className="px-4 py-2 bg-green-600 text-white rounded-lg text-sm hover:bg-green-700 flex items-center gap-2">
<Save className="w-4 h-4" /> Save
</button>
<button onClick={() => { setEditForm(hub); setEditMode(false); }} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50">
Cancel
</button>
</>
) : (
<button onClick={() => setEditMode(true)} className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm hover:bg-slate-50 flex items-center gap-2">
<Edit className="w-4 h-4" /> Edit
</button>
)}
</div>
</div>
</div>
<div className="border-b border-slate-100">
<nav className="flex gap-6 px-6">
<button
onClick={() => setActiveTab('overview')}
className={`py-4 text-sm font-medium border-b-2 transition-colors ${activeTab === 'overview'
? 'border-accent text-accent'
: 'border-transparent text-slate-500 hover:text-slate-700'
}`}
>
Overview
</button>
<button
onClick={() => setActiveTab('bikes')}
className={`py-4 text-sm font-medium border-b-2 transition-colors ${activeTab === 'bikes'
? 'border-accent text-accent'
: 'border-transparent text-slate-500 hover:text-slate-700'
}`}
>
Bikes ({bikes.length})
</button>
<button
onClick={() => setActiveTab('rentals')}
className={`py-4 text-sm font-medium border-b-2 transition-colors ${activeTab === 'rentals'
? 'border-accent text-accent'
: 'border-transparent text-slate-500 hover:text-slate-700'
}`}
>
Rentals ({rentals.length})
</button>
</nav>
</div>
<div className="p-6">
{activeTab === 'overview' && (
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div className="space-y-4">
<div className="bg-blue-50 p-4 rounded-xl border border-blue-100">
<h3 className="font-semibold text-blue-800 mb-3 flex items-center gap-2">
<MapPin className="w-5 h-5" /> Location Info
</h3>
{editMode ? (
<div className="space-y-3">
<input
type="text"
value={editForm.address}
onChange={(e) => setEditForm({ ...editForm, address: e.target.value })}
className="w-full px-3 py-2 border border-blue-200 rounded-lg text-sm"
placeholder="Address"
/>
</div>
) : (
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-blue-600">Address</span>
<span className="text-sm font-medium text-blue-800">{hub.address}</span>
</div>
{hub.coordinates && (
<div className="flex justify-between">
<span className="text-sm text-blue-600">Coordinates</span>
<span className="text-sm font-medium text-blue-800">{hub.coordinates.lat}, {hub.coordinates.lng}</span>
</div>
)}
</div>
)}
</div>
<div className="bg-purple-50 p-4 rounded-xl border border-purple-100">
<h3 className="font-semibold text-purple-800 mb-3 flex items-center gap-2">
<Phone className="w-5 h-5" /> Contact Info
</h3>
{editMode ? (
<div className="space-y-3">
<input
type="text"
value={editForm.phone}
onChange={(e) => setEditForm({ ...editForm, phone: e.target.value })}
className="w-full px-3 py-2 border border-purple-200 rounded-lg text-sm"
placeholder="Phone"
/>
<input
type="text"
value={editForm.managerName || ''}
onChange={(e) => setEditForm({ ...editForm, managerName: e.target.value })}
className="w-full px-3 py-2 border border-purple-200 rounded-lg text-sm"
placeholder="Manager Name"
/>
</div>
) : (
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-purple-600">Phone</span>
<span className="text-sm font-medium text-purple-800">{hub.phone}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-purple-600">Manager</span>
<span className="text-sm font-medium text-purple-800">{hub.managerName || '-'}</span>
</div>
</div>
)}
</div>
</div>
<div className="space-y-4">
<div className="bg-green-50 p-4 rounded-xl border border-green-100">
<h3 className="font-semibold text-green-800 mb-3 flex items-center gap-2">
<Clock className="w-5 h-5" /> Operating Hours
</h3>
{editMode ? (
<div className="grid grid-cols-2 gap-3">
<div>
<label className="text-sm text-green-600">Open</label>
<input
type="time"
value={editForm.openTime}
onChange={(e) => setEditForm({ ...editForm, openTime: e.target.value })}
className="w-full px-3 py-2 border border-green-200 rounded-lg text-sm mt-1"
/>
</div>
<div>
<label className="text-sm text-green-600">Close</label>
<input
type="time"
value={editForm.closeTime}
onChange={(e) => setEditForm({ ...editForm, closeTime: e.target.value })}
className="w-full px-3 py-2 border border-green-200 rounded-lg text-sm mt-1"
/>
</div>
</div>
) : (
<div className="flex justify-between">
<span className="text-sm text-green-600">Hours</span>
<span className="text-sm font-medium text-green-800">{hub.openTime} - {hub.closeTime}</span>
</div>
)}
</div>
<div className="bg-amber-50 p-4 rounded-xl border border-amber-100">
<h3 className="font-semibold text-amber-800 mb-3 flex items-center gap-2">
<DollarSign className="w-5 h-5" /> Today's Earnings
</h3>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-amber-600">Rentals</span>
<span className="text-sm font-medium text-amber-800">{hub.activeRentals}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-amber-600">Revenue</span>
<span className="text-sm font-medium text-amber-800">৳{hub.activeRentals * 300}</span>
</div>
</div>
</div>
</div>
<div className="space-y-4">
<div className="bg-slate-50 p-4 rounded-xl border border-slate-100">
<h3 className="font-semibold text-slate-800 mb-3 flex items-center gap-2">
<Bike className="w-5 h-5" /> Bike Statistics
</h3>
<div className="space-y-2">
<div className="flex justify-between">
<span className="text-sm text-slate-600">Total Bikes</span>
<span className="text-sm font-medium text-slate-800">{hub.bikeCount}</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-slate-600">Available</span>
<span className="text-sm font-medium text-green-700">
{bikes.filter(b => b.status === 'available').length}
</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-slate-600">Rented</span>
<span className="text-sm font-medium text-amber-700">
{bikes.filter(b => b.status === 'rented').length}
</span>
</div>
<div className="flex justify-between">
<span className="text-sm text-slate-600">Maintenance</span>
<span className="text-sm font-medium text-red-700">
{bikes.filter(b => b.status === 'maintenance').length}
</span>
</div>
</div>
</div>
</div>
</div>
)}
{activeTab === 'bikes' && (
<div>
<div className="flex items-center justify-between mb-4">
<h3 className="font-semibold text-slate-800">Hub Bikes ({bikes.length})</h3>
<button className="px-4 py-2 bg-accent text-white rounded-lg text-sm flex items-center gap-2">
<Plus className="w-4 h-4" /> Add Bike
</button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{bikes.map(bike => (
<div key={bike.id} className="bg-slate-50 p-4 rounded-xl border border-slate-100">
<div className="flex items-center justify-between mb-2">
<Bike className="w-5 h-5 text-slate-400" />
<span className={`text-xs font-medium px-2 py-1 rounded-full ${bike.status === 'available' ? 'bg-green-100 text-green-700' :
bike.status === 'rented' ? 'bg-amber-100 text-amber-700' :
'bg-red-100 text-red-700'
}`}>
{bike.status}
</span>
</div>
<p className="font-medium text-slate-800">{bike.model}</p>
<p className="text-sm text-slate-500">{bike.plate}</p>
<p className="text-xs text-slate-400 mt-2">ID: {bike.id}</p>
</div>
))}
</div>
</div>
)}
{activeTab === 'rentals' && (
<div>
<div className="flex items-center justify-between mb-4">
<h3 className="font-semibold text-slate-800">Hub Rentals ({rentals.length})</h3>
<button className="px-4 py-2 bg-accent text-white rounded-lg text-sm flex items-center gap-2">
<Plus className="w-4 h-4" /> New Rental
</button>
</div>
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-slate-50">
<tr>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Rental ID</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">User</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Bike</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Start Date</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Type</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Daily Rate</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Total Paid</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase">Status</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-50">
{rentals.map(rental => (
<tr key={rental.id} className="hover:bg-slate-50">
<td className="px-4 py-3">
<span className="text-sm font-medium text-slate-700">{rental.id}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm text-slate-600">{rental.userName}</span>
</td>
<td className="px-4 py-3">
<div>
<span className="text-sm text-slate-600">{rental.bike}</span>
<p className="text-xs text-slate-400">{rental.plate}</p>
</div>
</td>
<td className="px-4 py-3">
<span className="text-sm text-slate-600">{rental.startDate}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm text-slate-600 capitalize">{rental.type}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm text-slate-600">৳{rental.dailyRate}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm font-medium text-green-600">৳{rental.totalPaid.toLocaleString()}</span>
</td>
<td className="px-4 py-3">
<span className={`text-xs font-medium px-2.5 py-1 rounded-full ${
rental.status === 'active' ? 'bg-green-100 text-green-700' :
rental.status === 'pending' ? 'bg-amber-100 text-amber-700' :
'bg-blue-100 text-blue-700'
}`}>
{rental.status}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
</div>
</div>
);
}

371
src/app/admin/hub/page.tsx Normal file
View File

@@ -0,0 +1,371 @@
'use client';
import { useState } from 'react';
import { MapPin, Plus, Search, Eye, Edit, Trash2, Bike, X, Navigation, Phone, Clock } from 'lucide-react';
import Link from 'next/link';
interface Hub {
id: string;
name: string;
address: string;
phone: string;
managerName?: string;
bikeCount: number;
activeRentals: number;
status: 'active' | 'inactive';
coordinates?: { lat: number; lng: number };
openTime: string;
closeTime: string;
isHeadOffice?: boolean;
}
const mockHubs: Hub[] = [
{
id: 'HUB-001',
name: 'JAIBEN Head Office',
address: 'House 12, Road 17, Gulshan 1, Dhaka',
phone: '+8801712345678',
managerName: 'Rahim Ahmed',
bikeCount: 25,
activeRentals: 12,
status: 'active',
coordinates: { lat: 23.7925, lng: 90.4174 },
openTime: '06:00',
closeTime: '23:00',
isHeadOffice: true
},
{
id: 'HUB-002',
name: 'Banani Hub',
address: 'House 5, Road 11, Banani, Dhaka',
phone: '+8801812345678',
managerName: 'Karim Hasan',
bikeCount: 18,
activeRentals: 8,
status: 'active',
coordinates: { lat: 23.7785, lng: 90.4190 },
openTime: '06:00',
closeTime: '23:00'
},
{
id: 'HUB-003',
name: 'Uttara Hub',
address: 'Sector 11, Uttara, Dhaka',
phone: '+8801912345678',
managerName: 'Jamal Uddin',
bikeCount: 30,
activeRentals: 15,
status: 'active',
coordinates: { lat: 23.8657, lng: 90.4027 },
openTime: '06:00',
closeTime: '23:00'
},
{
id: 'HUB-004',
name: 'Mirpur Hub',
address: 'Section 10, Mirpur, Dhaka',
phone: '+8801512345678',
bikeCount: 0,
activeRentals: 0,
status: 'inactive',
openTime: '06:00',
closeTime: '23:00'
}
];
export default function HubsPage() {
const [hubs, setHubs] = useState<Hub[]>(mockHubs);
const [search, setSearch] = useState('');
const [showCreateModal, setShowCreateModal] = useState(false);
const [editingHub, setEditingHub] = useState<Hub | null>(null);
const [formData, setFormData] = useState({
name: '',
address: '',
phone: '',
managerName: '',
openTime: '06:00',
closeTime: '23:00',
isHeadOffice: false,
});
const filteredHubs = hubs.filter(h =>
h.name.toLowerCase().includes(search.toLowerCase()) ||
h.address.toLowerCase().includes(search.toLowerCase())
);
const handleSave = () => {
if (!formData.name || !formData.address) return;
if (editingHub) {
setHubs(hubs.map(h => h.id === editingHub.id ? {
...h,
...formData,
status: editingHub.status,
bikeCount: editingHub.bikeCount,
activeRentals: editingHub.activeRentals,
} : h));
} else {
const newHub: Hub = {
id: `HUB-${String(hubs.length + 1).padStart(3, '0')}`,
...formData,
bikeCount: 0,
activeRentals: 0,
status: 'active',
};
setHubs([...hubs, newHub]);
}
setShowCreateModal(false);
setEditingHub(null);
setFormData({ name: '', address: '', phone: '', managerName: '', openTime: '06:00', closeTime: '23:00', isHeadOffice: false });
};
const handleDelete = (id: string) => {
if (confirm('Are you sure you want to delete this hub?')) {
setHubs(hubs.filter(h => h.id !== id));
}
};
const openEdit = (hub: Hub) => {
setEditingHub(hub);
setFormData({
name: hub.name,
address: hub.address,
phone: hub.phone,
managerName: hub.managerName || '',
openTime: hub.openTime,
closeTime: hub.closeTime,
isHeadOffice: hub.isHeadOffice || false,
});
setShowCreateModal(true);
};
return (
<div className="p-4 lg:p-6">
<div className="flex flex-col lg:flex-row lg:items-center lg:justify-between gap-4 mb-6">
<div>
<h1 className="text-2xl lg:text-3xl font-extrabold text-slate-800">Hubs</h1>
<p className="text-sm text-slate-500 mt-1">Manage hub locations and branches</p>
</div>
<button
onClick={() => {
setEditingHub(null);
setFormData({ name: '', address: '', phone: '', managerName: '', openTime: '06:00', closeTime: '23:00', isHeadOffice: false });
setShowCreateModal(true);
}}
className="py-2.5 px-4 bg-accent text-white rounded-lg font-semibold text-sm hover:bg-accent-dark transition-colors flex items-center gap-2"
>
<Plus className="w-4 h-4" /> Add New Hub
</button>
</div>
<div className="grid grid-cols-2 lg:grid-cols-4 gap-4 mb-6">
<div className="bg-white rounded-xl p-5 shadow-sm border border-slate-100">
<p className="text-2xl font-extrabold text-slate-800">{hubs.length}</p>
<p className="text-sm text-slate-500">Total Hubs</p>
</div>
<div className="bg-white rounded-xl p-5 shadow-sm border border-slate-100">
<p className="text-2xl font-extrabold text-green-600">{hubs.filter(h => h.status === 'active').length}</p>
<p className="text-sm text-slate-500">Active Hubs</p>
</div>
<div className="bg-white rounded-xl p-5 shadow-sm border border-slate-100">
<p className="text-2xl font-extrabold text-blue-600">{hubs.reduce((a, h) => a + h.bikeCount, 0)}</p>
<p className="text-sm text-slate-500">Total Bikes</p>
</div>
<div className="bg-white rounded-xl p-5 shadow-sm border border-slate-100">
<p className="text-2xl font-extrabold text-amber-600">{hubs.reduce((a, h) => a + h.activeRentals, 0)}</p>
<p className="text-sm text-slate-500">Active Rentals</p>
</div>
</div>
<div className="bg-white rounded-xl shadow-sm border border-slate-100">
<div className="p-4 border-b border-slate-100">
<div className="relative max-w-md">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400" />
<input
type="text"
placeholder="Search hubs..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="w-full pl-10 pr-4 py-2 border border-slate-200 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-accent focus:border-transparent"
/>
</div>
</div>
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-slate-50">
<tr>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Hub Name</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Address</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Phone</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Manager</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Bikes</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Rentals</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Hours</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Status</th>
<th className="px-4 py-3 text-left text-xs font-semibold text-slate-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-50">
{filteredHubs.map(hub => (
<tr key={hub.id} className="hover:bg-slate-50 transition-colors">
<td className="px-4 py-3">
<Link href={`/admin/hub/${hub.id}`} className="flex items-center gap-3 hover:text-accent">
<MapPin className="w-5 h-5 text-accent" />
<span className="text-sm font-medium text-slate-700">{hub.name}</span>
</Link>
</td>
<td className="px-4 py-3">
<span className="text-sm text-slate-600 max-w-[200px] block truncate">{hub.address}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm text-slate-600">{hub.phone}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm text-slate-600">{hub.managerName || '-'}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm font-medium text-slate-700">{hub.bikeCount}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm font-medium text-amber-700">{hub.activeRentals}</span>
</td>
<td className="px-4 py-3">
<span className="text-sm text-slate-600">{hub.openTime} - {hub.closeTime}</span>
</td>
<td className="px-4 py-3">
<span className={`text-xs font-medium px-2.5 py-1 rounded-full ${
hub.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'
}`}>
{hub.status}
</span>
{hub.isHeadOffice && (
<span className="ml-2 text-xs font-medium px-2 py-1 rounded-full bg-purple-100 text-purple-700">
Head Office
</span>
)}
</td>
<td className="px-4 py-3">
<div className="flex items-center gap-1">
<Link href={`/admin/hub/${hub.id}`} className="p-2 hover:bg-slate-100 rounded-lg">
<Eye className="w-4 h-4 text-slate-400" />
</Link>
<button onClick={() => openEdit(hub)} className="p-2 hover:bg-slate-100 rounded-lg">
<Edit className="w-4 h-4 text-slate-400" />
</button>
<button onClick={() => handleDelete(hub.id)} className="p-2 hover:bg-red-50 rounded-lg">
<Trash2 className="w-4 h-4 text-red-400" />
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
{showCreateModal && (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-xl shadow-xl w-full max-w-md">
<div className="p-4 border-b border-slate-100 flex justify-between items-center">
<h3 className="font-semibold text-slate-800">{editingHub ? 'Edit Hub' : 'Add New Hub'}</h3>
<button onClick={() => setShowCreateModal(false)} className="text-slate-400 hover:text-slate-600">
<X className="w-5 h-5" />
</button>
</div>
<div className="p-4 space-y-4">
<div>
<label className="text-sm text-slate-600">Hub Name *</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
placeholder="Gulshan Hub"
/>
</div>
<div>
<label className="text-sm text-slate-600">Address *</label>
<textarea
value={formData.address}
onChange={(e) => setFormData({ ...formData, address: e.target.value })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
rows={2}
placeholder="Full address"
/>
</div>
<div>
<label className="text-sm text-slate-600">Phone</label>
<input
type="text"
value={formData.phone}
onChange={(e) => setFormData({ ...formData, phone: e.target.value })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
placeholder="+8801xxxxxxxxx"
/>
</div>
<div>
<label className="text-sm text-slate-600">Manager Name</label>
<input
type="text"
value={formData.managerName}
onChange={(e) => setFormData({ ...formData, managerName: e.target.value })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
placeholder="Manager name"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-sm text-slate-600">Open Time</label>
<input
type="time"
value={formData.openTime}
onChange={(e) => setFormData({ ...formData, openTime: e.target.value })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
/>
</div>
<div>
<label className="text-sm text-slate-600">Close Time</label>
<input
type="time"
value={formData.closeTime}
onChange={(e) => setFormData({ ...formData, closeTime: e.target.value })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
/>
</div>
<div className="flex items-center gap-2 mt-3">
<input
type="checkbox"
id="isHeadOffice"
checked={formData.isHeadOffice}
onChange={(e) => setFormData({ ...formData, isHeadOffice: e.target.checked })}
className="w-4 h-4 text-accent rounded"
/>
<label htmlFor="isHeadOffice" className="text-sm text-slate-600">Set as Head Office</label>
</div>
</div>
</div>
<div className="p-4 border-t border-slate-100 flex justify-end gap-2">
<button
onClick={() => setShowCreateModal(false)}
className="px-4 py-2 border border-slate-200 text-slate-600 rounded-lg text-sm"
>
Cancel
</button>
<button
onClick={handleSave}
disabled={!formData.name || !formData.address}
className="px-4 py-2 bg-accent text-white rounded-lg text-sm disabled:opacity-50"
>
{editingHub ? 'Update' : 'Create'}
</button>
</div>
</div>
</div>
)}
</div>
);
}