Compare commits
2 Commits
0d7b684c77
...
4ee6944f9d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ee6944f9d | ||
|
|
2e7bf23752 |
@@ -272,27 +272,52 @@ export default function RentalsPage() {
|
|||||||
const [editPermission, setEditPermission] = useState(false);
|
const [editPermission, setEditPermission] = useState(false);
|
||||||
const [lockPermission, setLockPermission] = useState(false);
|
const [lockPermission, setLockPermission] = useState(false);
|
||||||
const [unlockPermission, setUnlockPermission] = useState(false);
|
const [unlockPermission, setUnlockPermission] = useState(false);
|
||||||
|
const [planConditions, setPlanConditions] = useState<{
|
||||||
|
single: { name: string; deposit: number; dailyRate: number; weeklyRate: number; monthlyRate: number; contractMonths: number[]; evModels: string[] }[];
|
||||||
|
shared: { name: string; deposit: number; dailyRate: number; weeklyRate: number; monthlyRate: number; contractMonths: number[]; evModels: string[] }[];
|
||||||
|
'rent-to-own': { name: string; deposit: number; dailyRate: number; weeklyRate: number; monthlyRate: number; contractMonths: number[]; evModels: string[] }[];
|
||||||
|
}>({
|
||||||
|
single: [
|
||||||
|
{ name: 'Single Rent - Premium', deposit: 25000, dailyRate: 400, weeklyRate: 2800, monthlyRate: 12000, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
{ name: 'Single Rent - Standard', deposit: 20000, dailyRate: 300, weeklyRate: 2100, monthlyRate: 9000, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
{ name: 'Single Rent - Economy', deposit: 15000, dailyRate: 250, weeklyRate: 1750, monthlyRate: 7500, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
],
|
||||||
|
shared: [
|
||||||
|
{ name: 'Share an EV - Premium', deposit: 20000, dailyRate: 300, weeklyRate: 2100, monthlyRate: 8400, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
{ name: 'Share an EV - Standard', deposit: 15000, dailyRate: 200, weeklyRate: 1400, monthlyRate: 5600, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
{ name: 'Share an EV - Economy', deposit: 12000, dailyRate: 150, weeklyRate: 1050, monthlyRate: 4200, contractMonths: [1, 3, 6, 12], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
],
|
||||||
|
'rent-to-own': [
|
||||||
|
{ name: 'Rent to Own - Premium', deposit: 25000, dailyRate: 350, weeklyRate: 2450, monthlyRate: 10500, contractMonths: [12, 18, 24, 36], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
{ name: 'Rent to Own - Standard', deposit: 18000, dailyRate: 250, weeklyRate: 1750, monthlyRate: 7000, contractMonths: [12, 18, 24, 36], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
{ name: 'Rent to Own - Economy', deposit: 15000, dailyRate: 200, weeklyRate: 1400, monthlyRate: 6000, contractMonths: [12, 18, 24, 36], evModels: ['Yadea G5', 'Yadea C1S', 'Himiwa 400', 'Himiwa 500', 'Himiwa 600', 'TVS iQube', 'Bexly 1S', 'Bexly 3S', 'Bexly 5E', 'Bolan E-Bike', 'Nova E-Bike', 'Super Eco', 'Other'] },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
const [createStep, setCreateStep] = useState(1);
|
const [createStep, setCreateStep] = useState(1);
|
||||||
const [newRental, setNewRental] = useState<{
|
const [newRental, setNewRental] = useState<{
|
||||||
userId: string;
|
userId: string;
|
||||||
type: RentalType;
|
type: RentalType;
|
||||||
condition: 'Premium' | 'Standard' | 'Economy';
|
planConditionName: string;
|
||||||
|
evModel: string;
|
||||||
subscriptionType: 'daily' | 'weekly' | 'monthly';
|
subscriptionType: 'daily' | 'weekly' | 'monthly';
|
||||||
contractMonths: number;
|
contractMonths: number;
|
||||||
bikeId: string;
|
bikeId: string;
|
||||||
startDate: string;
|
startDate: string;
|
||||||
hubId: string;
|
hubId: string;
|
||||||
|
depositAmount: number;
|
||||||
depositPaymentMethod: PaymentMethod;
|
depositPaymentMethod: PaymentMethod;
|
||||||
}>({
|
}>({
|
||||||
userId: '',
|
userId: '',
|
||||||
type: 'single',
|
type: 'single',
|
||||||
condition: 'Standard',
|
planConditionName: '',
|
||||||
|
evModel: '',
|
||||||
subscriptionType: 'daily',
|
subscriptionType: 'daily',
|
||||||
contractMonths: 0,
|
contractMonths: 0,
|
||||||
bikeId: '',
|
bikeId: '',
|
||||||
startDate: new Date().toISOString().split('T')[0],
|
startDate: new Date().toISOString().split('T')[0],
|
||||||
hubId: '',
|
hubId: '',
|
||||||
|
depositAmount: 0,
|
||||||
depositPaymentMethod: 'cash',
|
depositPaymentMethod: 'cash',
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -306,6 +331,8 @@ export default function RentalsPage() {
|
|||||||
setUnlockPermission(canRentalUnlock());
|
setUnlockPermission(canRentalUnlock());
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const getStatusBadge = (status: RentalStatus) => {
|
const getStatusBadge = (status: RentalStatus) => {
|
||||||
const styles: Record<RentalStatus, string> = {
|
const styles: Record<RentalStatus, string> = {
|
||||||
active: 'bg-green-100 text-green-700',
|
active: 'bg-green-100 text-green-700',
|
||||||
@@ -393,7 +420,7 @@ export default function RentalsPage() {
|
|||||||
const eligibleUsers = mockUsers.filter(u => u.kycStatus === 'approved' && !u.hasActiveRental);
|
const eligibleUsers = mockUsers.filter(u => u.kycStatus === 'approved' && !u.hasActiveRental);
|
||||||
const availableBikes = mockBikes.filter(b => b.status === 'available');
|
const availableBikes = mockBikes.filter(b => b.status === 'available');
|
||||||
|
|
||||||
const selectedSettings = rentalSettings[newRental.type]?.[newRental.condition] || rentalSettings.single.Standard;
|
const selectedPlan = planConditions[newRental.type]?.find(p => p.name === newRental.planConditionName) || planConditions[newRental.type]?.[0];
|
||||||
|
|
||||||
const stats = {
|
const stats = {
|
||||||
active: rentals.filter(r => r.status === 'active').length,
|
active: rentals.filter(r => r.status === 'active').length,
|
||||||
@@ -411,7 +438,7 @@ export default function RentalsPage() {
|
|||||||
const bike = mockBikes.find(b => b.id === newRental.bikeId);
|
const bike = mockBikes.find(b => b.id === newRental.bikeId);
|
||||||
const user = mockUsers.find(u => u.id === newRental.userId);
|
const user = mockUsers.find(u => u.id === newRental.userId);
|
||||||
const hub = mockHubs.find(h => h.id === newRental.hubId);
|
const hub = mockHubs.find(h => h.id === newRental.hubId);
|
||||||
const settings = rentalSettings[newRental.type]?.[newRental.condition];
|
const settings = planConditions[newRental.type]?.find(p => p.name === newRental.planConditionName) || planConditions[newRental.type]?.[0];
|
||||||
|
|
||||||
const rental: Rental = {
|
const rental: Rental = {
|
||||||
id: `RNT-${String(rentals.length + 1).padStart(3, '0')}`,
|
id: `RNT-${String(rentals.length + 1).padStart(3, '0')}`,
|
||||||
@@ -454,12 +481,14 @@ export default function RentalsPage() {
|
|||||||
setNewRental({
|
setNewRental({
|
||||||
userId: '',
|
userId: '',
|
||||||
type: 'single',
|
type: 'single',
|
||||||
condition: 'Standard',
|
planConditionName: planConditions.single[1]?.name || '',
|
||||||
subscriptionType: 'daily',
|
subscriptionType: 'daily',
|
||||||
contractMonths: 0,
|
contractMonths: 0,
|
||||||
bikeId: '',
|
bikeId: '',
|
||||||
|
evModel: '',
|
||||||
startDate: new Date().toISOString().split('T')[0],
|
startDate: new Date().toISOString().split('T')[0],
|
||||||
hubId: '',
|
hubId: '',
|
||||||
|
depositAmount: 0,
|
||||||
depositPaymentMethod: 'cash',
|
depositPaymentMethod: 'cash',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -673,7 +702,7 @@ export default function RentalsPage() {
|
|||||||
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
|
||||||
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
<div className="bg-white rounded-xl shadow-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||||
<div className="p-4 border-b border-slate-100 flex justify-between items-center">
|
<div className="p-4 border-b border-slate-100 flex justify-between items-center">
|
||||||
<h3 className="font-semibold text-slate-800">Create New Rental - Step {createStep} of 5</h3>
|
<h3 className="font-semibold text-slate-800">Create New Rental - Step {createStep} of 4</h3>
|
||||||
<button onClick={() => { setShowCreateModal(false); setCreateStep(1); }} className="text-slate-400 hover:text-slate-600">
|
<button onClick={() => { setShowCreateModal(false); setCreateStep(1); }} className="text-slate-400 hover:text-slate-600">
|
||||||
<X className="w-5 h-5" />
|
<X className="w-5 h-5" />
|
||||||
</button>
|
</button>
|
||||||
@@ -681,19 +710,33 @@ export default function RentalsPage() {
|
|||||||
|
|
||||||
<div className="p-4 space-y-4">
|
<div className="p-4 space-y-4">
|
||||||
{createStep === 1 && (
|
{createStep === 1 && (
|
||||||
<div>
|
<div className="space-y-4">
|
||||||
<h4 className="font-medium text-slate-700 mb-3">Step 1: Select User</h4>
|
<h4 className="font-medium text-slate-700 mb-3">Step 1: Start Date & Hub</h4>
|
||||||
<p className="text-sm text-slate-500 mb-3">Only KYC-approved users without active rentals are shown.</p>
|
<div className="grid grid-cols-2 gap-3">
|
||||||
<select
|
<div>
|
||||||
value={newRental.userId}
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-1.5 block">Start Date</label>
|
||||||
onChange={(e) => setNewRental({ ...newRental, userId: e.target.value })}
|
<input type="date" value={newRental.startDate} onChange={(e) => setNewRental({ ...newRental, startDate: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" />
|
||||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
</div>
|
||||||
>
|
<div>
|
||||||
<option value="">Select User...</option>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-1.5 block">Hub</label>
|
||||||
{eligibleUsers.map(user => (
|
<select value={newRental.hubId} onChange={(e) => setNewRental({ ...newRental, hubId: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm">
|
||||||
<option key={user.id} value={user.id}>{user.name} ({user.phone})</option>
|
<option value="">Select Hub...</option>
|
||||||
))}
|
{mockHubs.map(hub => (
|
||||||
</select>
|
<option key={hub.id} value={hub.id}>{hub.name}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-1.5 block">Select User</label>
|
||||||
|
<p className="text-xs text-slate-400 mb-2">Only KYC-approved users without active rentals are shown.</p>
|
||||||
|
<select value={newRental.userId} onChange={(e) => setNewRental({ ...newRental, userId: e.target.value })} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm">
|
||||||
|
<option value="">Select User...</option>
|
||||||
|
{eligibleUsers.map(user => (
|
||||||
|
<option key={user.id} value={user.id}>{user.name} ({user.phone})</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -703,7 +746,7 @@ export default function RentalsPage() {
|
|||||||
<label className="text-sm text-slate-600">Rental Type</label>
|
<label className="text-sm text-slate-600">Rental Type</label>
|
||||||
<select
|
<select
|
||||||
value={newRental.type}
|
value={newRental.type}
|
||||||
onChange={(e) => setNewRental({ ...newRental, type: e.target.value as RentalType, condition: 'Standard', subscriptionType: 'daily' })}
|
onChange={(e) => setNewRental({ ...newRental, type: e.target.value as RentalType, planConditionName: planConditions[e.target.value as RentalType]?.[1]?.name || planConditions[e.target.value as RentalType]?.[0]?.name || '', subscriptionType: 'daily', contractMonths: 0 })}
|
||||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
||||||
>
|
>
|
||||||
<option value="single">Single Rent</option>
|
<option value="single">Single Rent</option>
|
||||||
@@ -712,25 +755,33 @@ export default function RentalsPage() {
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Condition</label>
|
<label className="text-sm text-slate-600">Plan Condition</label>
|
||||||
<div className="grid grid-cols-3 gap-2 mt-1">
|
<select
|
||||||
{(['Premium', 'Standard', 'Economy'] as const).map(cond => (
|
value={newRental.planConditionName}
|
||||||
<button
|
onChange={(e) => setNewRental(prev => {
|
||||||
key={cond}
|
const selectedPlan = planConditions[prev.type]?.find(p => p.name === e.target.value);
|
||||||
type="button"
|
return { ...prev, planConditionName: e.target.value, depositAmount: selectedPlan?.deposit || prev.depositAmount, contractMonths: 0, subscriptionType: 'daily' };
|
||||||
onClick={() => setNewRental({ ...newRental, condition: cond })}
|
})}
|
||||||
className={`py-2 px-3 rounded-lg text-sm border ${
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
||||||
newRental.condition === cond
|
>
|
||||||
? cond === 'Premium' ? 'bg-purple-100 border-purple-300 text-purple-700'
|
<option value="">Select Plan...</option>
|
||||||
: cond === 'Standard' ? 'bg-blue-100 border-blue-300 text-blue-700'
|
{planConditions[newRental.type]?.map(plan => (
|
||||||
: 'bg-amber-100 border-amber-300 text-amber-700'
|
<option key={plan.name} value={plan.name}>{plan.name}</option>
|
||||||
: 'border-slate-200 text-slate-600 hover:bg-slate-50'
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
{cond}
|
|
||||||
</button>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label className="text-sm text-slate-600">EV Model</label>
|
||||||
|
<select
|
||||||
|
value={newRental.evModel}
|
||||||
|
onChange={(e) => setNewRental({ ...newRental, evModel: e.target.value })}
|
||||||
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
||||||
|
>
|
||||||
|
<option value="">Select EV Model...</option>
|
||||||
|
{selectedPlan?.evModels.map(model => (
|
||||||
|
<option key={model} value={model}>{model}</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Contract Duration</label>
|
<label className="text-sm text-slate-600">Contract Duration</label>
|
||||||
@@ -740,7 +791,7 @@ export default function RentalsPage() {
|
|||||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
||||||
>
|
>
|
||||||
<option value={0}>Daily Basis</option>
|
<option value={0}>Daily Basis</option>
|
||||||
{selectedSettings.contractMonths.map(m => (
|
{selectedPlan?.contractMonths.map(m => (
|
||||||
<option key={m} value={m}>{m} Months</option>
|
<option key={m} value={m}>{m} Months</option>
|
||||||
))}
|
))}
|
||||||
</select>
|
</select>
|
||||||
@@ -770,11 +821,11 @@ export default function RentalsPage() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="bg-emerald-50 p-3 rounded-lg border border-emerald-100">
|
<div className="bg-emerald-50 p-3 rounded-lg border border-emerald-100">
|
||||||
<div className="flex items-center justify-between mb-1">
|
<div className="flex items-center justify-between mb-1">
|
||||||
<span className="text-xs text-emerald-600 font-medium uppercase tracking-wide">{newRental.condition} - {newRental.type === 'single' ? 'Single Rent' : newRental.type === 'shared' ? 'Share EV' : 'Rent to Own'}</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">৳{selectedSettings.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">
|
<p className="text-sm text-slate-600">
|
||||||
Rate: <span className="font-semibold text-slate-800">৳{newRental.subscriptionType === 'daily' ? selectedSettings.dailyRate : newRental.subscriptionType === 'weekly' ? selectedSettings.weeklyRate : selectedSettings.monthlyRate}</span>
|
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>
|
<span className="text-slate-500">/{newRental.subscriptionType === 'daily' ? 'day' : newRental.subscriptionType === 'weekly' ? 'week' : 'month'}</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -805,44 +856,26 @@ export default function RentalsPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
{createStep === 4 && (
|
{createStep === 4 && (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<h4 className="font-medium text-slate-700 mb-3">Step 4: Start Date & Hub</h4>
|
<h4 className="font-medium text-slate-700 mb-3">Step 4: Deposit Payment</h4>
|
||||||
<div>
|
|
||||||
<label className="text-sm text-slate-600">Start Date</label>
|
|
||||||
<input
|
|
||||||
type="date"
|
|
||||||
value={newRental.startDate}
|
|
||||||
onChange={(e) => setNewRental({ ...newRental, startDate: e.target.value })}
|
|
||||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<label className="text-sm text-slate-600">Hub</label>
|
|
||||||
<select
|
|
||||||
value={newRental.hubId}
|
|
||||||
onChange={(e) => setNewRental({ ...newRental, hubId: e.target.value })}
|
|
||||||
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1"
|
|
||||||
>
|
|
||||||
<option value="">Select Hub...</option>
|
|
||||||
{mockHubs.map(hub => (
|
|
||||||
<option key={hub.id} value={hub.id}>{hub.name}</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{createStep === 5 && (
|
|
||||||
<div className="space-y-4">
|
|
||||||
<h4 className="font-medium text-slate-700 mb-3">Step 5: Deposit Payment</h4>
|
|
||||||
<div className="bg-slate-50 p-3 rounded-lg">
|
<div className="bg-slate-50 p-3 rounded-lg">
|
||||||
<p className="text-sm text-slate-600">Deposit Amount: ৳{selectedSettings.deposit.toLocaleString()}</p>
|
<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">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>
|
||||||
<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>
|
||||||
</div>
|
</div>
|
||||||
{selectedSettings.deposit > 0 && (
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
{/* <div>
|
||||||
|
<label className="text-sm text-slate-600 mb-1 block">Deposit Amount (৳)</label>
|
||||||
|
<input
|
||||||
|
type="number"
|
||||||
|
value={newRental.depositAmount}
|
||||||
|
onChange={(e) => setNewRental({ ...newRental, depositAmount: Number(e.target.value) })}
|
||||||
|
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm"
|
||||||
|
/>
|
||||||
|
</div> */}
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600 mb-2 block">Payment Method</label>
|
<label className="text-sm text-slate-600 mb-2 block">Payment Method</label>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
@@ -864,34 +897,49 @@ export default function RentalsPage() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
</div>
|
||||||
{selectedSettings.deposit > 0 && (
|
{newRental.depositAmount > 0 && (
|
||||||
<div className="bg-blue-50 p-3 rounded-lg">
|
<div className="bg-white border border-slate-200 rounded-lg overflow-hidden">
|
||||||
<p className="text-sm font-medium text-blue-700 mb-2">Journal Preview</p>
|
<div className="bg-slate-100 px-4 py-2 border-b border-slate-200">
|
||||||
|
<p className="text-sm font-semibold text-slate-700">Proforma Invoice</p>
|
||||||
|
</div>
|
||||||
|
<div className="p-3 space-y-1 text-xs">
|
||||||
|
<div className="flex justify-between"><span className="text-slate-500">Invoice No:</span><span className="text-slate-700">PI-{Date.now().toString().slice(-8)}</span></div>
|
||||||
|
<div className="flex justify-between"><span className="text-slate-500">Date:</span><span className="text-slate-700">{new Date().toLocaleDateString()}</span></div>
|
||||||
|
<div className="flex justify-between"><span className="text-slate-500">Customer:</span><span className="text-slate-700">{selectedUser?.name}</span></div>
|
||||||
|
<div className="flex justify-between"><span className="text-slate-500">Bike:</span><span className="text-slate-700">{selectedBike?.model} ({selectedBike?.plate})</span></div>
|
||||||
|
</div>
|
||||||
<table className="w-full text-xs">
|
<table className="w-full text-xs">
|
||||||
<thead className="bg-blue-100">
|
<thead className="bg-slate-50 border-t border-slate-200">
|
||||||
<tr>
|
<tr>
|
||||||
<th className="px-2 py-1 text-left">Account</th>
|
<th className="px-3 py-2 text-left text-slate-500 font-medium">Description</th>
|
||||||
<th className="px-2 py-1 text-right">Debit</th>
|
<th className="px-3 py-2 text-right text-slate-500 font-medium">Debit (৳)</th>
|
||||||
<th className="px-2 py-1 text-right">Credit</th>
|
<th className="px-3 py-2 text-right text-slate-500 font-medium">Credit (৳)</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr className="border-t border-slate-100">
|
||||||
<td className="px-2 py-1">
|
<td className="px-3 py-2">
|
||||||
{newRental.depositPaymentMethod === 'cash' && '1000 - Cash'}
|
{newRental.depositPaymentMethod === 'cash' && '1000 - Cash'}
|
||||||
{newRental.depositPaymentMethod === 'bank' && '1100 - Bank'}
|
{newRental.depositPaymentMethod === 'bank' && '1100 - Bank'}
|
||||||
{newRental.depositPaymentMethod === 'wallet' && '1200 - Biker Wallet'}
|
{newRental.depositPaymentMethod === 'wallet' && '1200 - Biker Wallet'}
|
||||||
</td>
|
</td>
|
||||||
<td className="px-2 py-1 text-right">৳{selectedSettings.deposit}</td>
|
<td className="px-3 py-2 text-right">{newRental.depositAmount.toLocaleString()}</td>
|
||||||
<td className="px-2 py-1 text-right">-</td>
|
<td className="px-3 py-2 text-right">-</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr className="border-t border-slate-100">
|
||||||
<td className="px-2 py-1">2100 - Deposit Received</td>
|
<td className="px-3 py-2">2100 - Deposit Received</td>
|
||||||
<td className="px-2 py-1 text-right">-</td>
|
<td className="px-3 py-2 text-right">-</td>
|
||||||
<td className="px-2 py-1 text-right">৳{selectedSettings.deposit}</td>
|
<td className="px-3 py-2 text-right">{newRental.depositAmount.toLocaleString()}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
<tfoot className="bg-slate-50 border-t border-slate-200">
|
||||||
|
<tr>
|
||||||
|
<td className="px-3 py-2 font-semibold text-slate-700">Total</td>
|
||||||
|
<td className="px-3 py-2 text-right font-semibold">{newRental.depositAmount.toLocaleString()}</td>
|
||||||
|
<td className="px-3 py-2 text-right font-semibold">{newRental.depositAmount.toLocaleString()}</td>
|
||||||
|
</tr>
|
||||||
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -911,13 +959,12 @@ export default function RentalsPage() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{createStep < 5 ? (
|
{createStep < 4 ? (
|
||||||
<button
|
<button
|
||||||
onClick={() => setCreateStep(createStep + 1)}
|
onClick={() => setCreateStep(createStep + 1)}
|
||||||
disabled={
|
disabled={
|
||||||
(createStep === 1 && !newRental.userId) ||
|
(createStep === 1 && !newRental.userId) ||
|
||||||
(createStep === 3 && !newRental.bikeId) ||
|
(createStep === 3 && !newRental.bikeId)
|
||||||
(createStep === 4 && !newRental.hubId)
|
|
||||||
}
|
}
|
||||||
className="px-4 py-2 bg-emerald-600 text-white rounded-lg text-sm disabled:opacity-50 flex items-center gap-2"
|
className="px-4 py-2 bg-emerald-600 text-white rounded-lg text-sm disabled:opacity-50 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
@@ -925,7 +972,7 @@ export default function RentalsPage() {
|
|||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
{selectedSettings.deposit > 0 && (
|
{newRental.depositAmount > 0 && (
|
||||||
<button className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm flex items-center gap-2">
|
<button className="px-4 py-2 bg-blue-600 text-white rounded-lg text-sm flex items-center gap-2">
|
||||||
<Printer className="w-4 h-4" /> Print Invoice
|
<Printer className="w-4 h-4" /> Print Invoice
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -2461,8 +2461,8 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-5 space-y-5">
|
<div className="p-5 space-y-5">
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Name</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Condition</label>
|
||||||
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Plan Name" />
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.singleRent]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, singleRent: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Plan Condition" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
||||||
@@ -2600,8 +2600,8 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-5 space-y-5">
|
<div className="p-5 space-y-5">
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Name</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Condition</label>
|
||||||
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Plan Name" />
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.rentToOwn]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, rentToOwn: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Plan Condition" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
||||||
@@ -2739,8 +2739,8 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-5 space-y-5">
|
<div className="p-5 space-y-5">
|
||||||
<div className="grid grid-cols-3 gap-4">
|
<div className="grid grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Name</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">Plan Condition</label>
|
||||||
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Plan Name" />
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.shareEv]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, shareEv: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm" placeholder="Plan Condition" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
<label className="text-xs font-semibold text-slate-500 uppercase tracking-wide mb-2 block">EV Model Numbers</label>
|
||||||
@@ -2891,7 +2891,7 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="grid lg:grid-cols-3 gap-4">
|
<div className="grid lg:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Plan Name</label>
|
<label className="text-sm text-slate-600">Plan Condition</label>
|
||||||
<input type="text" value={newInvestName} onChange={(e) => setNewInvestName(e.target.value)} placeholder="e.g., 1 Bike Plan" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
<input type="text" value={newInvestName} onChange={(e) => setNewInvestName(e.target.value)} placeholder="e.g., 1 Bike Plan" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -2986,7 +2986,7 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="grid lg:grid-cols-3 gap-4">
|
<div className="grid lg:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Plan Name</label>
|
<label className="text-sm text-slate-600">Plan Condition</label>
|
||||||
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.investment]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, investment: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -3101,7 +3101,7 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="grid lg:grid-cols-3 gap-4">
|
<div className="grid lg:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Plan Name</label>
|
<label className="text-sm text-slate-600">Plan Condition</label>
|
||||||
<input type="text" value={newSwapName} onChange={(e) => setNewSwapName(e.target.value)} placeholder="e.g., Standard Station" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
<input type="text" value={newSwapName} onChange={(e) => setNewSwapName(e.target.value)} placeholder="e.g., Standard Station" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -3168,7 +3168,7 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="grid lg:grid-cols-3 gap-4">
|
<div className="grid lg:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Plan Name</label>
|
<label className="text-sm text-slate-600">Plan Condition</label>
|
||||||
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.swapStation]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, swapStation: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -3255,7 +3255,7 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="grid lg:grid-cols-3 gap-4">
|
<div className="grid lg:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Plan Name</label>
|
<label className="text-sm text-slate-600">Plan Condition</label>
|
||||||
<input type="text" value={newRiderName} onChange={(e) => setNewRiderName(e.target.value)} placeholder="e.g., Premium Rider Plan" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
<input type="text" value={newRiderName} onChange={(e) => setNewRiderName(e.target.value)} placeholder="e.g., Premium Rider Plan" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -3334,7 +3334,7 @@ export default function CompanySettingsPage() {
|
|||||||
<div className="p-4">
|
<div className="p-4">
|
||||||
<div className="grid lg:grid-cols-3 gap-4">
|
<div className="grid lg:grid-cols-3 gap-4">
|
||||||
<div>
|
<div>
|
||||||
<label className="text-sm text-slate-600">Plan Name</label>
|
<label className="text-sm text-slate-600">Plan Condition</label>
|
||||||
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.riderRequest]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, riderRequest: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
<input type="text" value={plan.name} onChange={(e) => { const updated = [...settings.plans.riderRequest]; updated[idx].name = e.target.value; setSettings({ ...settings, plans: { ...settings.plans, riderRequest: updated } }); }} className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm mt-1" />
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
Reference in New Issue
Block a user