feat: add battery rent support and integrate adjusted battery costs into rental billing calculations
This commit is contained in:
@@ -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>
|
||||
<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,19 +821,40 @@ export default function RentalsPage() {
|
||||
{rental.depositPaid ? 'Paid' : 'Unpaid'}
|
||||
</span>
|
||||
</div>
|
||||
{totalDue > 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 = 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>
|
||||
<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>}
|
||||
{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">৳{rent.toLocaleString()}</span>
|
||||
<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">
|
||||
<Link href={`/admin/rentals/${rental.id}`} className="flex-1 flex items-center justify-center gap-1 p-2 bg-white hover:bg-slate-100 rounded-lg text-xs font-medium text-slate-600">
|
||||
@@ -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>
|
||||
<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>
|
||||
|
||||
Reference in New Issue
Block a user