feat: add battery rent support and integrate adjusted battery costs into rental billing calculations

This commit is contained in:
sazzadulalambd
2026-05-16 15:09:19 +06:00
parent 21c408f828
commit adbcded611

View File

@@ -171,6 +171,7 @@ const mockRentals: Rental[] = [
dailyRate: 150,
weeklyRate: 900,
monthlyRate: 3500,
batteryRent: 1500,
totalPaid: 38500,
dueRental: 0,
pendingRent: 0,
@@ -272,6 +273,7 @@ const mockRentals: Rental[] = [
dailyRate: 100,
weeklyRate: 600,
monthlyRate: 2200,
batteryRent: 1500,
totalPaid: 2600,
dueRental: 600,
pendingRent: 600,
@@ -692,23 +694,42 @@ export default function RentalsPage() {
</td> */}
<td className="px-4 py-3">
{(() => {
const rent = rental[rental.subscriptionType === 'daily' ? 'dailyRate' : rental.subscriptionType === 'weekly' ? 'weeklyRate' : 'monthlyRate'];
const due = rental.pendingRent || 0;
const bikeRent = rental[rental.subscriptionType === 'daily' ? 'dailyRate' : rental.subscriptionType === 'weekly' ? 'weeklyRate' : 'monthlyRate'];
const batteryRent = rental.batteryRent || 0;
const batteryRentAdjusted = rental.subscriptionType === 'daily'
? Math.round(batteryRent / 30)
: rental.subscriptionType === 'weekly'
? Math.round(batteryRent / 4)
: batteryRent;
const totalRent = bikeRent + batteryRentAdjusted;
const due = rental.pendingRent + rental.batteryRentPending || 0;
const penalty = rental.penaltyAmount || 0;
const totalDue = due + penalty;
const periodLabel = rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo';
if (totalDue > 0) {
return (
<div>
<div className="space-y-1">
<span className="text-sm font-medium text-amber-600">{totalDue.toLocaleString()}</span>
<p className="text-xs text-slate-400">{rental.subscriptionType} {rent}/{(rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo')}</p>
{penalty > 0 && <span className="text-xs text-red-500">+{penalty} penalty</span>}
{batteryRent > 0 ? (
<p className="text-xs text-slate-500">
{rental.subscriptionType} {bikeRent}{periodLabel} +Battery: {batteryRentAdjusted}{periodLabel}
</p>
) : (
<p className="text-xs text-red-400">{rental.subscriptionType} {totalRent}{periodLabel} +{penalty} penalty</p>
)}
</div>
);
}
return (
<div>
<span className="text-sm font-medium text-slate-700">{rent.toLocaleString()}</span>
<p className="text-xs text-slate-400 capitalize">{rental.subscriptionType}</p>
<div className="space-y-1">
<span className="text-sm font-medium text-slate-700">{totalRent.toLocaleString()}</span>
{batteryRent > 0 ? (
<p className="text-xs text-slate-500">
{rental.subscriptionType} {bikeRent}{periodLabel} +Battery: {batteryRentAdjusted}{periodLabel}
</p>
) : (
<p className="text-xs text-slate-400 capitalize">{rental.subscriptionType}</p>
)}
</div>
);
})()}
@@ -800,18 +821,39 @@ export default function RentalsPage() {
{rental.depositPaid ? 'Paid' : 'Unpaid'}
</span>
</div>
{totalDue > 0 ? (
<div className="text-right">
<span className="text-sm font-medium text-amber-600">{totalDue.toLocaleString()}</span>
<p className="text-xs text-slate-400">{rental.subscriptionType} {rent}/{(rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo')}</p>
{penalty > 0 && <span className="text-xs text-red-500">+{penalty}</span>}
</div>
) : (
<div className="text-right">
<span className="text-sm font-medium text-slate-700">{rent.toLocaleString()}</span>
<p className="text-xs text-slate-400 capitalize">{rental.subscriptionType}</p>
</div>
)}
{(() => {
const batteryRent = rental.batteryRent || 0;
const batteryRentAdjusted = rental.subscriptionType === 'daily'
? Math.round(batteryRent / 30)
: rental.subscriptionType === 'weekly'
? Math.round(batteryRent / 4)
: batteryRent;
const totalRent = rent + batteryRentAdjusted;
const periodLabel = rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo';
return totalDue > 0 ? (
<div className="text-right">
<span className="text-sm font-medium text-amber-600">{totalDue.toLocaleString()}</span>
{batteryRent > 0 ? (
<p className="text-xs text-slate-500">
{rental.subscriptionType} {rent}{periodLabel} +Battery: {batteryRentAdjusted}{periodLabel} +{penalty} penalty
</p>
) : (
<p className="text-xs text-slate-400">{rental.subscriptionType} {totalRent}{periodLabel} +{penalty} penalty</p>
)}
</div>
) : (
<div className="text-right">
<span className="text-sm font-medium text-slate-700">{totalRent.toLocaleString()}</span>
{batteryRent > 0 ? (
<p className="text-xs text-slate-500">
{rental.subscriptionType} {rent}{periodLabel} +Battery: {batteryRentAdjusted}{periodLabel}
</p>
) : (
<p className="text-xs text-slate-400 capitalize">{rental.subscriptionType}</p>
)}
</div>
);
})()}
</div>
<div className="flex items-center gap-1.5 pt-1">
@@ -967,10 +1009,21 @@ export default function RentalsPage() {
<span className="text-xs text-emerald-600 font-medium uppercase tracking-wide">{newRental.planConditionName}</span>
</div>
<p className="text-sm text-slate-600">Deposit: <span className="font-semibold text-slate-800">{selectedPlan?.deposit.toLocaleString()}</span></p>
<p className="text-sm text-slate-600">
Rate: <span className="font-semibold text-slate-800">{newRental.subscriptionType === 'daily' ? selectedPlan?.dailyRate : newRental.subscriptionType === 'weekly' ? selectedPlan?.weeklyRate : selectedPlan?.monthlyRate}</span>
<span className="text-slate-500">/{newRental.subscriptionType === 'daily' ? 'day' : newRental.subscriptionType === 'weekly' ? 'week' : 'month'}</span>
</p>
<div className="text-sm text-slate-600">
<span>Rate: </span>
{(() => {
const bikeRate = newRental.subscriptionType === 'daily' ? selectedPlan?.dailyRate : newRental.subscriptionType === 'weekly' ? selectedPlan?.weeklyRate : selectedPlan?.monthlyRate;
const batteryRent = newRental.batteryRent || 0;
const batteryRate = batteryRent > 0 ? (newRental.subscriptionType === 'daily' ? Math.round(batteryRent / 30) : newRental.subscriptionType === 'weekly' ? Math.round(batteryRent / 4) : batteryRent) : 0;
const totalRate = bikeRate + batteryRate;
return (
<span className="font-semibold text-slate-800">
{totalRate.toLocaleString()}/{newRental.subscriptionType === 'daily' ? 'day' : newRental.subscriptionType === 'weekly' ? 'week' : 'month'}
{batteryRent > 0 && <span className="text-xs text-amber-600 ml-1">(Bike: {bikeRate} + Battery: {batteryRate})</span>}
</span>
);
})()}
</div>
</div>
</div>
)}
@@ -1002,7 +1055,8 @@ export default function RentalsPage() {
</div>
<div>
<label className="text-sm text-slate-600 mb-1 block">Select Battery (Optional)</label>
<label className="text-sm text-slate-600 mb-1 block">Request Battery (Optional)</label>
<p className="text-xs text-slate-500 mb-2">Biker can request for battery. It will be added to their rental.</p>
<select
value={newRental.batteryId}
onChange={(e) => {
@@ -1017,8 +1071,15 @@ export default function RentalsPage() {
))}
</select>
{newRental.batteryId && (
<div className="mt-2 p-2 bg-amber-50 border border-amber-200 rounded-lg">
<div className="mt-2 p-3 bg-amber-50 border border-amber-200 rounded-lg space-y-1">
<p className="text-sm text-amber-700">Battery Monthly Rent: <span className="font-bold">৳{newRental.batteryRent}/month</span></p>
<div className="text-xs text-amber-600 pt-1 border-t border-amber-100">
<p>Calculated Rate by Subscription:</p>
<p>• Daily: ৳{Math.round(newRental.batteryRent / 30)}/day (৳{newRental.batteryRent}/30)</p>
<p>• Weekly: ৳{Math.round(newRental.batteryRent / 4)}/week (৳{newRental.batteryRent}/4)</p>
<p>• Monthly (30 days): ৳{newRental.batteryRent}/month</p>
<p>• Monthly (31 days): ৳{newRental.batteryRent}/month</p>
</div>
</div>
)}
</div>
@@ -1034,15 +1095,40 @@ export default function RentalsPage() {
<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">Battery: {availableBatteries.find(b => b.id === newRental.batteryId)?.brand} {availableBatteries.find(b => b.id === newRental.batteryId)?.model} (Monthly Rent: ৳{newRental.batteryRent})</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 className="mt-3 pt-3 border-t border-slate-200">
<p className="text-sm font-medium text-emerald-700 mb-2">Total Rental Payment (Bike + Battery):</p>
{(() => {
const bikeRate = newRental.subscriptionType === 'daily' ? selectedPlan?.dailyRate : newRental.subscriptionType === 'weekly' ? selectedPlan?.weeklyRate : selectedPlan?.monthlyRate;
const batteryMonthlyRent = newRental.batteryRent || 0;
const dailyRate = bikeRate + Math.round(batteryMonthlyRent / 30);
const weeklyRate = bikeRate + Math.round(batteryMonthlyRent / 4);
const monthly30Rate = bikeRate + batteryMonthlyRent;
const monthly31Rate = bikeRate + batteryMonthlyRent;
return (
<div className="space-y-1 text-xs">
<p className="text-slate-600">
<span className="font-medium">Daily:</span> ৳{dailyRate}/day
<span className="text-amber-600"> (Bike: ৳{bikeRate} + Battery: ৳{Math.round(batteryMonthlyRent / 30)})</span>
</p>
<p className="text-slate-600">
<span className="font-medium">Weekly:</span> ৳{weeklyRate}/week
<span className="text-amber-600"> (Bike: ৳{bikeRate} + Battery: ৳{Math.round(batteryMonthlyRent / 4)})</span>
</p>
<p className="text-slate-600">
<span className="font-medium">Monthly (30 days):</span> ৳{monthly30Rate}/month
<span className="text-amber-600"> (Bike: ৳{bikeRate} + Battery: ৳{batteryMonthlyRent})</span>
</p>
<p className="text-slate-600">
<span className="font-medium">Monthly (31 days):</span> ৳{monthly31Rate}/month
<span className="text-amber-600"> (Bike: ৳{bikeRate} + Battery: ৳{batteryMonthlyRent})</span>
</p>
</div>
);
})()}
</div>
)}
</div>