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, dailyRate: 150,
weeklyRate: 900, weeklyRate: 900,
monthlyRate: 3500, monthlyRate: 3500,
batteryRent: 1500,
totalPaid: 38500, totalPaid: 38500,
dueRental: 0, dueRental: 0,
pendingRent: 0, pendingRent: 0,
@@ -272,6 +273,7 @@ const mockRentals: Rental[] = [
dailyRate: 100, dailyRate: 100,
weeklyRate: 600, weeklyRate: 600,
monthlyRate: 2200, monthlyRate: 2200,
batteryRent: 1500,
totalPaid: 2600, totalPaid: 2600,
dueRental: 600, dueRental: 600,
pendingRent: 600, pendingRent: 600,
@@ -692,23 +694,42 @@ export default function RentalsPage() {
</td> */} </td> */}
<td className="px-4 py-3"> <td className="px-4 py-3">
{(() => { {(() => {
const rent = rental[rental.subscriptionType === 'daily' ? 'dailyRate' : rental.subscriptionType === 'weekly' ? 'weeklyRate' : 'monthlyRate']; const bikeRent = rental[rental.subscriptionType === 'daily' ? 'dailyRate' : rental.subscriptionType === 'weekly' ? 'weeklyRate' : 'monthlyRate'];
const due = rental.pendingRent || 0; 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 penalty = rental.penaltyAmount || 0;
const totalDue = due + penalty; const totalDue = due + penalty;
const periodLabel = rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo';
if (totalDue > 0) { if (totalDue > 0) {
return ( return (
<div> <div className="space-y-1">
<span className="text-sm font-medium text-amber-600">{totalDue.toLocaleString()}</span> <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> {batteryRent > 0 ? (
{penalty > 0 && <span className="text-xs text-red-500">+{penalty} penalty</span>} <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> </div>
); );
} }
return ( return (
<div> <div className="space-y-1">
<span className="text-sm font-medium text-slate-700">{rent.toLocaleString()}</span> <span className="text-sm font-medium text-slate-700">{totalRent.toLocaleString()}</span>
<p className="text-xs text-slate-400 capitalize">{rental.subscriptionType}</p> {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> </div>
); );
})()} })()}
@@ -800,18 +821,39 @@ export default function RentalsPage() {
{rental.depositPaid ? 'Paid' : 'Unpaid'} {rental.depositPaid ? 'Paid' : 'Unpaid'}
</span> </span>
</div> </div>
{totalDue > 0 ? ( {(() => {
<div className="text-right"> const batteryRent = rental.batteryRent || 0;
<span className="text-sm font-medium text-amber-600">{totalDue.toLocaleString()}</span> const batteryRentAdjusted = rental.subscriptionType === 'daily'
<p className="text-xs text-slate-400">{rental.subscriptionType} {rent}/{(rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo')}</p> ? Math.round(batteryRent / 30)
{penalty > 0 && <span className="text-xs text-red-500">+{penalty}</span>} : rental.subscriptionType === 'weekly'
</div> ? Math.round(batteryRent / 4)
) : ( : batteryRent;
<div className="text-right"> const totalRent = rent + batteryRentAdjusted;
<span className="text-sm font-medium text-slate-700">{rent.toLocaleString()}</span> const periodLabel = rental.subscriptionType === 'daily' ? 'day' : rental.subscriptionType === 'weekly' ? 'wk' : 'mo';
<p className="text-xs text-slate-400 capitalize">{rental.subscriptionType}</p> return totalDue > 0 ? (
</div> <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>
<div className="flex items-center gap-1.5 pt-1"> <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> <span className="text-xs text-emerald-600 font-medium uppercase tracking-wide">{newRental.planConditionName}</span>
</div> </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">Deposit: <span className="font-semibold text-slate-800">{selectedPlan?.deposit.toLocaleString()}</span></p>
<p className="text-sm text-slate-600"> <div 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>Rate: </span>
<span className="text-slate-500">/{newRental.subscriptionType === 'daily' ? 'day' : newRental.subscriptionType === 'weekly' ? 'week' : 'month'}</span> {(() => {
</p> 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>
</div> </div>
)} )}
@@ -978,7 +1031,7 @@ export default function RentalsPage() {
{createStep === 3 && ( {createStep === 3 && (
<div className="space-y-4"> <div className="space-y-4">
<h4 className="font-medium text-slate-700 mb-3">Step 3: Select Bike & Battery</h4> <h4 className="font-medium text-slate-700 mb-3">Step 3: Select Bike & Battery</h4>
<div> <div>
<label className="text-sm text-slate-600 mb-1 block">Select Bike</label> <label className="text-sm text-slate-600 mb-1 block">Select Bike</label>
<select <select
@@ -1002,7 +1055,8 @@ export default function RentalsPage() {
</div> </div>
<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 <select
value={newRental.batteryId} value={newRental.batteryId}
onChange={(e) => { onChange={(e) => {
@@ -1017,8 +1071,15 @@ export default function RentalsPage() {
))} ))}
</select> </select>
{newRental.batteryId && ( {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> <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>
)} )}
</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">User: {selectedUser?.name}</p>
<p className="text-sm text-slate-600">Bike: {selectedBike?.model} ({selectedBike?.plate})</p> <p className="text-sm text-slate-600">Bike: {selectedBike?.model} ({selectedBike?.plate})</p>
{newRental.batteryId && ( {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> <p className="text-sm text-slate-600">Hub: {mockHubs.find(h => h.id === newRental.hubId)?.name}</p>
{newRental.batteryId && ( {newRental.batteryId && (
<div className="mt-2 pt-2 border-t border-slate-200"> <div className="mt-3 pt-3 border-t border-slate-200">
<p className="text-sm font-medium text-emerald-700"> <p className="text-sm font-medium text-emerald-700 mb-2">Total Rental Payment (Bike + Battery):</p>
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> const bikeRate = newRental.subscriptionType === 'daily' ? selectedPlan?.dailyRate : newRental.subscriptionType === 'weekly' ? selectedPlan?.weeklyRate : selectedPlan?.monthlyRate;
</p> 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>
)} )}
</div> </div>