feat: replace native alerts with react-hot-toast notifications and update rider plan constants

This commit is contained in:
sazzadulalambd
2026-05-05 02:44:32 +06:00
parent 4445624638
commit 111b242bf8
3 changed files with 74 additions and 70 deletions

View File

@@ -9,10 +9,11 @@ import {
CheckCircle, XCircle, ArrowLeft, Save, Printer, Send, CheckCircle, XCircle, ArrowLeft, Save, Printer, Send,
MessageSquare, Edit, UserCheck, Wallet, Store, Globe, Calendar, Briefcase, Plus, Upload MessageSquare, Edit, UserCheck, Wallet, Store, Globe, Calendar, Briefcase, Plus, Upload
} from 'lucide-react'; } from 'lucide-react';
import toast from 'react-hot-toast';
type ApplicationSource = 'app' | 'web' | 'walkin' | 'referral'; type ApplicationSource = 'app' | 'web' | 'walkin' | 'referral';
type KYCType = 'biker' | 'investor' | 'swapstation' | 'merchant' | 'general'; type KYCType = 'biker' | 'investor' | 'swapstation' | 'merchant' | 'general';
type RiderPlan = 'daily_rent' | 'weekly_rent' | 'monthly_rent' | 'rent_to_own' | 'share_ev'; type RiderPlan = 'single_rent' | 'rent_to_own' | 'share_ev';
type VerificationStage = 'application' | 'document_collection' | 'risk_check' | 'plan_selection' | 'payment' | 'agreement' | 'allocated' | 'active'; type VerificationStage = 'application' | 'document_collection' | 'risk_check' | 'plan_selection' | 'payment' | 'agreement' | 'allocated' | 'active';
interface Document { interface Document {
@@ -310,7 +311,7 @@ export default function KYCDetailPage() {
} : null); } : null);
setNewMessageText(''); setNewMessageText('');
setShowMessageModal(false); setShowMessageModal(false);
alert(`Message sent to ${request.phone}`); toast.success(`SMS sent to ${request.phone}`);
}; };
const handleAddDocument = () => { const handleAddDocument = () => {

View File

@@ -9,10 +9,11 @@ import {
Upload, CheckCircle, XCircle, Camera, AlertTriangle, Edit, Globe, Wallet, Calendar, Upload, CheckCircle, XCircle, Camera, AlertTriangle, Edit, Globe, Wallet, Calendar,
CreditCard, FileSignature, MapPinned, Key, BatteryCharging, Briefcase, Plus CreditCard, FileSignature, MapPinned, Key, BatteryCharging, Briefcase, Plus
} from 'lucide-react'; } from 'lucide-react';
import toast from 'react-hot-toast';
export type ApplicationSource = 'app' | 'web' | 'walkin' | 'referral'; export type ApplicationSource = 'app' | 'web' | 'walkin' | 'referral';
export type KYCType = 'biker' | 'investor' | 'swapstation' | 'merchant' | 'general'; export type KYCType = 'biker' | 'investor' | 'swapstation' | 'merchant' | 'general';
export type RiderPlan = 'daily_rent' | 'weekly_rent' | 'monthly_rent' | 'rent_to_own' | 'share_ev'; export type RiderPlan = 'single_rent' | 'rent_to_own' | 'share_ev';
export type VerificationStage = 'application' | 'document_collection' | 'risk_check' | 'plan_selection' | 'payment' | 'agreement' | 'allocated' | 'active'; export type VerificationStage = 'application' | 'document_collection' | 'risk_check' | 'plan_selection' | 'payment' | 'agreement' | 'allocated' | 'active';
interface NomineeDetails { interface NomineeDetails {
@@ -352,12 +353,12 @@ export default function RequestsPage() {
return req; return req;
}); });
setRequests(updatedRequests); setRequests(updatedRequests);
alert('Document approved!'); toast.success('Document approved!');
}; };
const handleRejectDocument = (docId: string, reason: string) => { const handleRejectDocument = (docId: string, reason: string) => {
if (!selectedRequest || !reason.trim()) { if (!selectedRequest || !reason.trim()) {
alert('Please enter a reason for rejection'); toast.error('Please enter a reason for rejection');
return; return;
} }
const updatedRequests = requests.map(req => { const updatedRequests = requests.map(req => {
@@ -373,7 +374,7 @@ export default function RequestsPage() {
}); });
setRequests(updatedRequests); setRequests(updatedRequests);
setRejectReason(''); setRejectReason('');
alert('Document rejected!'); toast.error('Document rejected!');
}; };
const filteredRequests = requests.filter(req => { const filteredRequests = requests.filter(req => {
@@ -410,7 +411,7 @@ export default function RequestsPage() {
setRequests(updatedRequests); setRequests(updatedRequests);
setShowMessageModal(false); setShowMessageModal(false);
setMessageText(''); setMessageText('');
alert(`Message sent to ${selectedRequest.phone}: "${messageText}"`); toast.success(`SMS sent to ${selectedRequest.phone}`);
} }
}; };
@@ -444,7 +445,7 @@ export default function RequestsPage() {
); );
setRequests(updatedRequests); setRequests(updatedRequests);
setShowActionModal(false); setShowActionModal(false);
alert(successMessage); toast.success(successMessage);
}; };
return ( return (
@@ -1099,7 +1100,7 @@ export default function RequestsPage() {
}; };
setRequests([newRequest, ...requests]); setRequests([newRequest, ...requests]);
setShowNewApplicationModal(false); setShowNewApplicationModal(false);
alert('Application created successfully!'); toast.success('Application created successfully!');
}} }}
/> />
</div> </div>
@@ -1674,68 +1675,68 @@ function NewApplicationModal({ isOpen, onClose, onSave }: { isOpen: boolean; onC
</div> </div>
{formData.type === 'biker' && ( {formData.type === 'biker' && (
<div className="bg-amber-50/80 p-4 rounded-xl border border-amber-100"> <div className="bg-amber-50/80 p-4 rounded-xl border border-amber-100">
<h4 className="font-semibold text-amber-900 mb-3 flex items-center gap-2 text-sm"> <h4 className="font-semibold text-amber-900 mb-3 flex items-center gap-2 text-sm">
<Key className="w-4 h-4" /> Driving License <Key className="w-4 h-4" /> Driving License
</h4> </h4>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
<div> <div>
<label className="text-xs font-medium text-slate-600 mb-1 block">License Number *</label> <label className="text-xs font-medium text-slate-600 mb-1 block">License Number *</label>
<input <input
type="text" type="text"
value={formData.drivingLicense.number} value={formData.drivingLicense.number}
onChange={(e) => updateField('drivingLicense.number', e.target.value)} onChange={(e) => updateField('drivingLicense.number', 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"
placeholder="License number" placeholder="License number"
/> />
</div> </div>
<div> <div>
<label className="text-xs font-medium text-slate-600 mb-1 block">Class *</label> <label className="text-xs font-medium text-slate-600 mb-1 block">Class *</label>
<select <select
value={formData.drivingLicense.class} value={formData.drivingLicense.class}
onChange={(e) => updateField('drivingLicense.class', e.target.value)} onChange={(e) => updateField('drivingLicense.class', e.target.value)}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
> >
<option value="">Select Class</option> <option value="">Select Class</option>
<option value="C">Class C</option> <option value="C">Class C</option>
<option value="D">Class D</option> <option value="D">Class D</option>
<option value="E">Class E</option> <option value="E">Class E</option>
<option value="F">Class F</option> <option value="F">Class F</option>
</select> </select>
</div> </div>
<div> <div>
<label className="text-xs font-medium text-slate-600 mb-1 block">Status *</label> <label className="text-xs font-medium text-slate-600 mb-1 block">Status *</label>
<select <select
value={formData.drivingLicense.status} value={formData.drivingLicense.status}
onChange={(e) => updateField('drivingLicense.status', e.target.value)} onChange={(e) => updateField('drivingLicense.status', e.target.value)}
className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white" className="w-full px-3 py-2 border border-slate-200 rounded-lg text-sm bg-white"
> >
<option value="not_applied">Not Applied</option> <option value="not_applied">Not Applied</option>
<option value="applied">Applied</option> <option value="applied">Applied</option>
<option value="issued">Issued</option> <option value="issued">Issued</option>
<option value="expired">Expired</option> <option value="expired">Expired</option>
</select> </select>
</div> </div>
<div> <div>
<label className="text-xs font-medium text-slate-600 mb-1 block">Issue Date *</label> <label className="text-xs font-medium text-slate-600 mb-1 block">Issue Date *</label>
<input <input
type="date" type="date"
value={formData.drivingLicense.issueDate} value={formData.drivingLicense.issueDate}
onChange={(e) => updateField('drivingLicense.issueDate', e.target.value)} onChange={(e) => updateField('drivingLicense.issueDate', 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>
<div> <div>
<label className="text-xs font-medium text-slate-600 mb-1 block">Expiry Date *</label> <label className="text-xs font-medium text-slate-600 mb-1 block">Expiry Date *</label>
<input <input
type="date" type="date"
value={formData.drivingLicense.expiryDate} value={formData.drivingLicense.expiryDate}
onChange={(e) => updateField('drivingLicense.expiryDate', e.target.value)} onChange={(e) => updateField('drivingLicense.expiryDate', 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> </div>
</div> </div>
</div>
)} )}
</div> </div>
)} )}
@@ -2217,7 +2218,7 @@ function NewApplicationModal({ isOpen, onClose, onSave }: { isOpen: boolean; onC
</h4> </h4>
<div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4"> <div className="grid grid-cols-1 sm:grid-cols-3 gap-3 mb-4">
{[ {[
{ v: 'daily_rent', l: 'Single Rent', d: 'Single person rental plan', icon: Bike }, { v: 'single_rent', l: 'Single Rent', d: 'Single person rental plan', icon: Bike },
{ v: 'rent_to_own', l: 'Rent to Own', d: 'Own after X months', icon: Key }, { v: 'rent_to_own', l: 'Rent to Own', d: 'Own after X months', icon: Key },
{ v: 'share_ev', l: 'Share EV', d: 'Shared bike', icon: Users }, { v: 'share_ev', l: 'Share EV', d: 'Shared bike', icon: Users },
].map(plan => ( ].map(plan => (

View File

@@ -2,6 +2,7 @@ import type { Metadata } from "next";
import { Inter } from "next/font/google"; import { Inter } from "next/font/google";
import "./globals.css"; import "./globals.css";
import Sidebar from "@/components/Sidebar"; import Sidebar from "@/components/Sidebar";
import { Toaster } from "react-hot-toast";
const inter = Inter({ const inter = Inter({
variable: "--font-inter", variable: "--font-inter",
@@ -25,6 +26,7 @@ export default function RootLayout({
<main className="lg:ml-64 min-h-screen"> <main className="lg:ml-64 min-h-screen">
{children} {children}
</main> </main>
<Toaster position="top-right" />
</body> </body>
</html> </html>
); );