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,
|
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>
|
||||||
|
{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>
|
<p className="text-xs text-slate-400 capitalize">{rental.subscriptionType}</p>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
})()}
|
})()}
|
||||||
@@ -800,19 +821,40 @@ export default function RentalsPage() {
|
|||||||
{rental.depositPaid ? 'Paid' : 'Unpaid'}
|
{rental.depositPaid ? 'Paid' : 'Unpaid'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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">
|
<div className="text-right">
|
||||||
<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}</span>}
|
<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>
|
||||||
) : (
|
) : (
|
||||||
<div className="text-right">
|
<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>
|
<p className="text-xs text-slate-400 capitalize">{rental.subscriptionType}</p>
|
||||||
</div>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-1.5 pt-1">
|
<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">
|
<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>
|
<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>
|
||||||
)}
|
)}
|
||||||
@@ -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;
|
||||||
|
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>
|
||||||
|
<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>
|
||||||
|
|||||||
Reference in New Issue
Block a user