refactor: remove battery manual entry fields in biker profile and add battery rental history table to rental details

This commit is contained in:
sazzadulalambd
2026-05-16 14:06:33 +06:00
parent 41530a4691
commit 1882cfbb91
3 changed files with 175 additions and 71 deletions

View File

@@ -33,6 +33,9 @@ interface Rental {
bikeModel: string;
bikePlate: string;
bikeBattery: number;
batteryId?: string;
batteryName?: string;
batteryRent?: number;
type: RentalType;
status: RentalStatus;
startDate: string;
@@ -63,6 +66,15 @@ interface Rental {
createdAt: string;
acceptedAt?: string;
activatedAt?: string;
batteryHistory?: {
id: string;
batteryId: string;
batteryName: string;
assignedAt: string;
returnedAt?: string;
monthlyRent: number;
status: 'active' | 'returned';
}[];
}
interface Bike {
@@ -103,6 +115,23 @@ const mockHubs = [
{ id: 'HUB-004', name: 'Mirpur Hub' },
];
interface Battery {
id: string;
brand: string;
model: string;
soc: number;
monthlyRent: number;
status: 'available' | 'in-use' | 'maintenance';
}
const mockBatteries: Battery[] = [
{ id: 'BAT-DH-001', brand: 'Galaxy', model: '72V 45Ah', soc: 85, monthlyRent: 1500, status: 'available' },
{ id: 'BAT-DH-002', brand: 'Titan', model: '72V 50Ah', soc: 92, monthlyRent: 1800, status: 'available' },
{ id: 'BAT-DH-003', brand: 'PowerMax', model: '60V 40Ah', soc: 78, monthlyRent: 1200, status: 'available' },
{ id: 'BAT-DH-004', brand: 'UltraCell', model: '72V 55Ah', soc: 88, monthlyRent: 2000, status: 'available' },
{ id: 'BAT-DH-005', brand: 'EcoVolt', model: '48V 30Ah', soc: 65, monthlyRent: 800, status: 'available' },
];
const rentalSettings = {
single: {
Premium: { deposit: 5000, contractMonths: [1, 3, 6, 12], dailyRate: 200, weeklyRate: 1200, monthlyRate: 5000 },
@@ -304,6 +333,8 @@ export default function RentalsPage() {
subscriptionType: 'daily' | 'weekly' | 'monthly';
contractMonths: number;
bikeId: string;
batteryId: string;
batteryRent: number;
startDate: string;
hubId: string;
depositAmount: number;
@@ -316,12 +347,16 @@ export default function RentalsPage() {
subscriptionType: 'daily',
contractMonths: 0,
bikeId: '',
batteryId: '',
batteryRent: 0,
startDate: new Date().toISOString().split('T')[0],
hubId: '',
depositAmount: 0,
depositPaymentMethod: 'cash',
});
const availableBatteries = mockBatteries.filter(b => b.status === 'available');
const [showJournalPreview, setShowJournalPreview] = useState(false);
useEffect(() => {
@@ -439,6 +474,7 @@ export default function RentalsPage() {
const bike = mockBikes.find(b => b.id === newRental.bikeId);
const user = mockUsers.find(u => u.id === newRental.userId);
const hub = mockHubs.find(h => h.id === newRental.hubId);
const battery = mockBatteries.find(b => b.id === newRental.batteryId);
const settings = planConditions[newRental.type]?.find(p => p.name === newRental.planConditionName) || planConditions[newRental.type]?.[0];
const rental: Rental = {
@@ -450,6 +486,9 @@ export default function RentalsPage() {
bikeModel: bike?.model || '',
bikePlate: bike?.plate || '',
bikeBattery: bike?.battery || 0,
batteryId: newRental.batteryId || undefined,
batteryName: battery ? `${battery.brand} ${battery.model}` : undefined,
batteryRent: newRental.batteryRent || undefined,
type: newRental.type,
status: 'pending',
startDate: newRental.startDate,
@@ -473,6 +512,14 @@ export default function RentalsPage() {
hubName: hub?.name || '',
imagesApproved: false,
createdAt: new Date().toISOString().split('T')[0],
batteryHistory: newRental.batteryId ? [{
id: `BAT-RENT-${Date.now()}`,
batteryId: newRental.batteryId,
batteryName: battery ? `${battery.brand} ${battery.model}` : '',
assignedAt: new Date().toISOString().split('T')[0],
monthlyRent: newRental.batteryRent,
status: 'active' as const,
}] : undefined,
};
setRentals([...rentals, rental]);
@@ -486,6 +533,8 @@ export default function RentalsPage() {
subscriptionType: 'daily',
contractMonths: 0,
bikeId: '',
batteryId: '',
batteryRent: 0,
evModel: '',
startDate: new Date().toISOString().split('T')[0],
hubId: '',
@@ -927,26 +976,52 @@ export default function RentalsPage() {
)}
{createStep === 3 && (
<div>
<h4 className="font-medium text-slate-700 mb-3">Step 3: Select Bike</h4>
<select
value={newRental.bikeId}
onChange={(e) => setNewRental({ ...newRental, bikeId: e.target.value })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
<option value="">Select Bike...</option>
{availableBikes.map(bike => (
<option key={bike.id} value={bike.id}>{bike.model} - {bike.plate}</option>
))}
</select>
{selectedBike && (
<div className="mt-3 flex items-center gap-2">
<span className="text-sm text-slate-600">Battery:</span>
<span className={`text-sm px-2 py-1 rounded-full ${selectedBike.battery > 70 ? 'bg-green-100 text-green-700' : selectedBike.battery > 40 ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700'}`}>
{selectedBike.battery}%
</span>
</div>
)}
<div className="space-y-4">
<h4 className="font-medium text-slate-700 mb-3">Step 3: Select Bike & Battery</h4>
<div>
<label className="text-sm text-slate-600 mb-1 block">Select Bike</label>
<select
value={newRental.bikeId}
onChange={(e) => setNewRental({ ...newRental, bikeId: e.target.value })}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
<option value="">Select Bike...</option>
{availableBikes.map(bike => (
<option key={bike.id} value={bike.id}>{bike.model} - {bike.plate}</option>
))}
</select>
{selectedBike && (
<div className="mt-2 flex items-center gap-2">
<span className="text-sm text-slate-600">Battery:</span>
<span className={`text-sm px-2 py-1 rounded-full ${selectedBike.battery > 70 ? 'bg-green-100 text-green-700' : selectedBike.battery > 40 ? 'bg-amber-100 text-amber-700' : 'bg-red-100 text-red-700'}`}>
{selectedBike.battery}%
</span>
</div>
)}
</div>
<div>
<label className="text-sm text-slate-600 mb-1 block">Select Battery (Optional)</label>
<select
value={newRental.batteryId}
onChange={(e) => {
const bat = availableBatteries.find(b => b.id === e.target.value);
setNewRental({ ...newRental, batteryId: e.target.value, batteryRent: bat?.monthlyRent || 0 });
}}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
>
<option value="">No Battery - Use Bike's Battery</option>
{availableBatteries.map(bat => (
<option key={bat.id} value={bat.id}>{bat.brand} {bat.model} - SOC: {bat.soc}% - Rent: ৳{bat.monthlyRent}/month</option>
))}
</select>
{newRental.batteryId && (
<div className="mt-2 p-2 bg-amber-50 border border-amber-200 rounded-lg">
<p className="text-sm text-amber-700">Battery Monthly Rent: <span className="font-bold">৳{newRental.batteryRent}/month</span></p>
</div>
)}
</div>
</div>
)}
@@ -958,7 +1033,18 @@ export default function RentalsPage() {
<p className="text-sm text-slate-600">Deposit Amount: ৳{selectedPlan?.deposit.toLocaleString()}</p>
<p className="text-sm text-slate-600">User: {selectedUser?.name}</p>
<p className="text-sm text-slate-600">Bike: {selectedBike?.model} ({selectedBike?.plate})</p>
{newRental.batteryId && (
<p className="text-sm text-slate-600">Battery: {availableBatteries.find(b => b.id === newRental.batteryId)?.brand} {availableBatteries.find(b => b.id === newRental.batteryId)?.model} (Rent: ৳{newRental.batteryRent}/month)</p>
)}
<p className="text-sm text-slate-600">Hub: {mockHubs.find(h => h.id === newRental.hubId)?.name}</p>
{newRental.batteryId && (
<div className="mt-2 pt-2 border-t border-slate-200">
<p className="text-sm font-medium text-emerald-700">
Combined Monthly Rent: ৳{(newRental.subscriptionType === 'daily' ? selectedPlan?.dailyRate : newRental.subscriptionType === 'weekly' ? selectedPlan?.weeklyRate : selectedPlan?.monthlyRate) + newRental.batteryRent}/month
<span className="text-xs text-slate-500 ml-1">(Bike: ৳{newRental.subscriptionType === 'daily' ? selectedPlan?.dailyRate : newRental.subscriptionType === 'weekly' ? selectedPlan?.weeklyRate : selectedPlan?.monthlyRate} + Battery: ৳{newRental.batteryRent})</span>
</p>
</div>
)}
</div>
<div className="grid grid-cols-2 gap-4">