786 lines
28 KiB
TypeScript
786 lines
28 KiB
TypeScript
import { useState, useEffect } from 'react';
|
||
import { BrowserRouter, Routes, Route, useNavigate, Outlet } from 'react-router-dom';
|
||
import { PageLayout } from '@/components/layout/PageLayout';
|
||
import { Dashboard } from '@/pages/Dashboard';
|
||
import { OpenRequests } from '@/pages/OpenRequests';
|
||
import { ClosedRequests } from '@/pages/ClosedRequests';
|
||
import { RequestDetail } from '@/pages/RequestDetail';
|
||
import { SharedSummaries } from '@/pages/SharedSummaries/SharedSummaries';
|
||
import { SharedSummaryDetail } from '@/pages/SharedSummaries/SharedSummaryDetail';
|
||
import { WorkNotes } from '@/pages/WorkNotes';
|
||
import { CreateRequest } from '@/pages/CreateRequest';
|
||
import { ClaimManagementWizard } from '@/dealer-claim/components/request-creation/ClaimManagementWizard';
|
||
import { DealerDashboard } from '@/dealer-claim/pages/Dashboard';
|
||
import { MyRequests } from '@/pages/MyRequests';
|
||
import { Requests } from '@/pages/Requests/Requests';
|
||
import { UserAllRequests } from '@/pages/Requests/UserAllRequests';
|
||
import { useAuth, hasManagementAccess } from '@/contexts/AuthContext';
|
||
import { ApproverPerformance } from '@/pages/ApproverPerformance/ApproverPerformance';
|
||
import { Profile } from '@/pages/Profile';
|
||
import { Settings } from '@/pages/Settings';
|
||
import { SecuritySettings } from '@/pages/Settings/SecuritySettings';
|
||
import { Notifications } from '@/pages/Notifications';
|
||
import { DetailedReports } from '@/pages/DetailedReports';
|
||
|
||
import { AdminTemplatesList } from '@/pages/Admin/Templates/AdminTemplatesList';
|
||
import { CreateTemplate } from '@/pages/Admin/Templates/CreateTemplate';
|
||
import { CreateAdminRequest } from '@/pages/CreateAdminRequest/CreateAdminRequest';
|
||
import { Admin } from '@/pages/Admin/Admin';
|
||
import { Form16CreditNotes } from '@/pages/Form16/Form16CreditNotes';
|
||
import { Form16CreditNoteDetail } from '@/pages/Form16/Form16CreditNoteDetail';
|
||
import { Form16Submit } from '@/pages/Form16/Form16Submit';
|
||
import { Form16SubmissionResult } from '@/pages/Form16/Form16SubmissionResult';
|
||
import { Form16_26AS } from '@/pages/Form16/Form16_26AS';
|
||
import { Form16NonSubmittedDealers } from '@/pages/Form16/Form16NonSubmittedDealers';
|
||
import { Form16PendingSubmissions } from '@/pages/Form16/Form16PendingSubmissions';
|
||
import { ApprovalActionModal } from '@/components/modals/ApprovalActionModal';
|
||
import { Toaster } from '@/components/ui/sonner';
|
||
import { toast } from 'sonner';
|
||
import { AuthCallback } from '@/pages/Auth/AuthCallback';
|
||
import { createClaimRequest } from '@/services/dealerClaimApi';
|
||
import { ManagerSelectionModal } from '@/components/modals/ManagerSelectionModal';
|
||
import { navigateToRequest } from '@/utils/requestNavigation';
|
||
import { TokenManager } from '@/utils/tokenManager';
|
||
|
||
interface AppProps {
|
||
onLogout?: () => void;
|
||
}
|
||
|
||
// Component to conditionally render Admin or User All Requests screen
|
||
// This ensures that when navigating from the sidebar, the correct screen is shown based on user role
|
||
function RequestsRoute({ onViewRequest }: { onViewRequest: (requestId: string) => void }) {
|
||
const { user } = useAuth();
|
||
const isAdmin = hasManagementAccess(user);
|
||
|
||
// Render separate screens based on user role
|
||
// Admin/Management users see all organization requests
|
||
// Regular users see only their participant requests (approver/spectator, NOT initiator)
|
||
if (isAdmin) {
|
||
return <Requests onViewRequest={onViewRequest} />;
|
||
} else {
|
||
return <UserAllRequests onViewRequest={onViewRequest} />;
|
||
}
|
||
}
|
||
|
||
// Component to conditionally render Dashboard or DealerDashboard based on user job title
|
||
function DashboardRoute({ onNavigate, onNewRequest }: { onNavigate?: (page: string) => void; onNewRequest?: () => void }) {
|
||
const [isDealer, setIsDealer] = useState<boolean>(false);
|
||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||
|
||
useEffect(() => {
|
||
try {
|
||
const userData = TokenManager.getUserData();
|
||
setIsDealer(userData?.jobTitle === 'Dealer');
|
||
} catch (error) {
|
||
console.error('[App] Error checking dealer status:', error);
|
||
setIsDealer(false);
|
||
} finally {
|
||
setIsLoading(false);
|
||
}
|
||
}, []);
|
||
|
||
if (isLoading) {
|
||
return (
|
||
<div className="flex items-center justify-center h-screen">
|
||
<div className="flex flex-col items-center gap-4">
|
||
<div className="w-8 h-8 animate-spin rounded-full border-4 border-blue-600 border-t-transparent" />
|
||
<p className="text-muted-foreground">Loading...</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Render dealer-specific dashboard if user is a dealer
|
||
if (isDealer) {
|
||
return <DealerDashboard onNavigate={onNavigate} onNewRequest={onNewRequest} />;
|
||
}
|
||
|
||
// Render regular dashboard for all other users
|
||
return <Dashboard onNavigate={onNavigate} onNewRequest={onNewRequest} />;
|
||
}
|
||
|
||
// Main Application Routes Component
|
||
function AppRoutes({ onLogout }: AppProps) {
|
||
const navigate = useNavigate();
|
||
const [approvalAction, setApprovalAction] = useState<'approve' | 'reject' | null>(null);
|
||
const [dynamicRequests, setDynamicRequests] = useState<any[]>([]);
|
||
const [selectedRequestId, setSelectedRequestId] = useState<string>('');
|
||
const [selectedRequestTitle, setSelectedRequestTitle] = useState<string>('');
|
||
const [managerModalOpen, setManagerModalOpen] = useState(false);
|
||
const [managerModalData, setManagerModalData] = useState<{
|
||
errorType: 'NO_MANAGER_FOUND' | 'MULTIPLE_MANAGERS_FOUND';
|
||
managers?: Array<{
|
||
userId: string;
|
||
email: string;
|
||
displayName: string;
|
||
firstName?: string;
|
||
lastName?: string;
|
||
department?: string;
|
||
}>;
|
||
message?: string;
|
||
pendingClaimData?: any;
|
||
} | null>(null);
|
||
|
||
// Retrieve dynamic requests from localStorage on mount
|
||
useEffect(() => {
|
||
const storedRequests = localStorage.getItem('dynamicRequests');
|
||
if (storedRequests) {
|
||
try {
|
||
const parsed = JSON.parse(storedRequests);
|
||
setDynamicRequests(parsed);
|
||
} catch (error) {
|
||
console.error('Error parsing dynamic requests:', error);
|
||
}
|
||
}
|
||
}, []);
|
||
|
||
// Sync dynamic requests to localStorage whenever they change
|
||
useEffect(() => {
|
||
if (dynamicRequests.length > 0) {
|
||
localStorage.setItem('dynamicRequests', JSON.stringify(dynamicRequests));
|
||
}
|
||
}, [dynamicRequests]);
|
||
|
||
const handleNavigate = (page: string) => {
|
||
// Handle special routes
|
||
if (page === 'profile') {
|
||
navigate('/profile');
|
||
return;
|
||
}
|
||
if (page === 'settings') {
|
||
navigate('/settings');
|
||
return;
|
||
}
|
||
// If page already starts with '/', use it directly (e.g., '/requests?status=approved')
|
||
// Otherwise, add leading slash (e.g., 'open-requests' -> '/open-requests')
|
||
if (page.startsWith('/')) {
|
||
navigate(page);
|
||
} else {
|
||
navigate(`/${page}`);
|
||
}
|
||
};
|
||
|
||
const handleViewRequest = (requestId: string, requestTitle?: string, status?: string, request?: any) => {
|
||
setSelectedRequestId(requestId);
|
||
setSelectedRequestTitle(requestTitle || 'Unknown Request');
|
||
|
||
// Use global navigation utility for consistent routing
|
||
navigateToRequest({
|
||
requestId,
|
||
requestTitle,
|
||
status,
|
||
request,
|
||
navigate,
|
||
});
|
||
};
|
||
|
||
const handleBack = () => {
|
||
navigate(-1);
|
||
};
|
||
|
||
const handleNewRequest = () => {
|
||
navigate('/new-request');
|
||
};
|
||
|
||
const handleNewRequestSubmit = (requestData: any) => {
|
||
// Check if this is a template selection (from Existing Template button)
|
||
if (requestData.templateType) {
|
||
// Navigate to the specific template wizard
|
||
if (requestData.templateType === 'claim-management') {
|
||
navigate('/claim-management');
|
||
}
|
||
return;
|
||
}
|
||
|
||
// If requestData has backendId, it means it came from the API flow (CreateRequest component)
|
||
// The hook already shows the toast, so we just navigate
|
||
if (requestData.backendId) {
|
||
navigate('/my-requests');
|
||
return;
|
||
}
|
||
|
||
// Regular custom request submission (old flow without API)
|
||
// Generate unique ID for the new custom request
|
||
const requestId = `RE-REQ-2024-${String(dynamicRequests.length + 1).padStart(3, '0')}`;
|
||
|
||
// Create full custom request object
|
||
const newCustomRequest = {
|
||
id: requestId,
|
||
title: requestData.title,
|
||
description: requestData.description || '',
|
||
category: requestData.category || 'General',
|
||
subcategory: requestData.subcategory || '',
|
||
status: 'pending',
|
||
priority: requestData.priority || 'standard',
|
||
amount: requestData.budget || 'N/A',
|
||
slaProgress: 0,
|
||
slaRemaining: '5 days',
|
||
slaEndDate: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true }),
|
||
currentStep: 1,
|
||
totalSteps: requestData.approvers?.length || 1,
|
||
currentApprover: requestData.approvers?.[0]?.name || requestData.approvers?.[0]?.email?.split('@')[0] || 'Pending Assignment',
|
||
approverLevel: `1 of ${requestData.approvers?.length || 1}`,
|
||
template: 'custom',
|
||
initiator: {
|
||
name: 'Current User',
|
||
role: requestData.initiatorRole || 'Employee',
|
||
department: requestData.department || 'General',
|
||
email: 'current.user@royalenfield.com',
|
||
phone: '+91 98765 43290',
|
||
avatar: 'CU'
|
||
},
|
||
department: requestData.department || 'General',
|
||
createdAt: new Date().toLocaleDateString('en-US', {
|
||
month: 'short',
|
||
day: 'numeric',
|
||
year: 'numeric',
|
||
hour: 'numeric',
|
||
minute: 'numeric',
|
||
hour12: true
|
||
}),
|
||
updatedAt: new Date().toLocaleDateString('en-US', {
|
||
month: 'short',
|
||
day: 'numeric',
|
||
year: 'numeric',
|
||
hour: 'numeric',
|
||
minute: 'numeric',
|
||
hour12: true
|
||
}),
|
||
dueDate: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(),
|
||
submittedDate: new Date().toISOString(),
|
||
estimatedCompletion: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }),
|
||
conclusionRemark: '',
|
||
approvalFlow: (requestData.approvers || []).filter((a: any) => a).map((approver: any, index: number) => {
|
||
// Extract name from email if name is not available
|
||
const approverName = approver?.name || approver?.email?.split('@')[0] || `Approver ${index + 1}`;
|
||
const approverEmail = approver?.email || '';
|
||
|
||
return {
|
||
step: index + 1,
|
||
approver: `${approverName}${approverEmail ? ` (${approverEmail})` : ''}`,
|
||
role: approver?.role || `Level ${approver?.level || index + 1} Approver`,
|
||
status: index === 0 ? 'pending' : 'waiting',
|
||
tatHours: approver?.tat ? (typeof approver.tat === 'string' ? parseInt(approver.tat) : approver.tat) : 48,
|
||
elapsedHours: index === 0 ? 0 : 0,
|
||
assignedAt: index === 0 ? new Date().toISOString() : null,
|
||
comment: null,
|
||
timestamp: null
|
||
};
|
||
}),
|
||
documents: [],
|
||
spectators: (requestData.spectators || [])
|
||
.filter((s: any) => s && (s.name || s.email))
|
||
.map((spectator: any) => {
|
||
const name = spectator?.name || spectator?.email?.split('@')[0] || 'Observer';
|
||
return {
|
||
name: name,
|
||
role: spectator?.role || spectator?.department || 'Observer',
|
||
avatar: name.split(' ').map((n: string) => n[0]).join('').toUpperCase().slice(0, 2) || 'OB'
|
||
};
|
||
}),
|
||
auditTrail: [
|
||
{
|
||
type: 'created',
|
||
action: 'Request Created',
|
||
details: `Custom request "${requestData.title}" created`,
|
||
user: 'Current User',
|
||
timestamp: new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true })
|
||
},
|
||
{
|
||
type: 'assignment',
|
||
action: 'Assigned to Approver',
|
||
details: `Request assigned to ${requestData.approvers?.[0]?.name || requestData.approvers?.[0]?.email || 'first approver'}`,
|
||
user: 'System',
|
||
timestamp: new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true })
|
||
}
|
||
],
|
||
tags: requestData.tags || ['custom-request']
|
||
};
|
||
|
||
// Add to dynamic requests
|
||
setDynamicRequests([...dynamicRequests, newCustomRequest]);
|
||
|
||
navigate('/my-requests');
|
||
};
|
||
|
||
const handleApprovalSubmit = (action: 'approve' | 'reject', _comment: string) => {
|
||
return new Promise((resolve) => {
|
||
setTimeout(() => {
|
||
if (action === 'approve') {
|
||
toast.success('Request Approved', {
|
||
description: 'The request has been approved and forwarded to the next step.',
|
||
duration: 5000,
|
||
});
|
||
} else {
|
||
toast.error('Request Rejected', {
|
||
description: 'The request has been rejected and returned to the initiator.',
|
||
duration: 5000,
|
||
});
|
||
}
|
||
|
||
setApprovalAction(null);
|
||
resolve(true);
|
||
}, 1000);
|
||
});
|
||
};
|
||
|
||
const handleCloseApprovalModal = () => {
|
||
setApprovalAction(null);
|
||
};
|
||
|
||
const handleClaimManagementSubmit = async (claimData: any, _selectedManagerEmail?: string) => {
|
||
try {
|
||
// Prepare payload for API
|
||
const payload = {
|
||
activityName: claimData.activityName,
|
||
activityType: claimData.activityType,
|
||
dealerCode: claimData.dealerCode,
|
||
dealerName: claimData.dealerName,
|
||
dealerEmail: claimData.dealerEmail || undefined,
|
||
dealerPhone: claimData.dealerPhone || undefined,
|
||
dealerAddress: claimData.dealerAddress || undefined,
|
||
activityDate: claimData.activityDate ? new Date(claimData.activityDate).toISOString() : undefined,
|
||
location: claimData.location,
|
||
requestDescription: claimData.requestDescription,
|
||
periodStartDate: claimData.periodStartDate ? new Date(claimData.periodStartDate).toISOString() : undefined,
|
||
periodEndDate: claimData.periodEndDate ? new Date(claimData.periodEndDate).toISOString() : undefined,
|
||
estimatedBudget: claimData.estimatedBudget || undefined,
|
||
approvers: claimData.approvers || [], // Pass approvers array
|
||
};
|
||
|
||
// Call API to create claim request
|
||
const response = await createClaimRequest(payload);
|
||
|
||
// Validate response - ensure request was actually created successfully
|
||
if (!response || !response.request) {
|
||
throw new Error('Invalid response from server: Request object not found');
|
||
}
|
||
|
||
const createdRequest = response.request;
|
||
|
||
// Validate that we have at least one identifier (requestNumber or requestId)
|
||
if (!createdRequest.requestNumber && !createdRequest.requestId) {
|
||
throw new Error('Invalid response from server: Request identifier not found');
|
||
}
|
||
|
||
// Close manager modal if open
|
||
setManagerModalOpen(false);
|
||
setManagerModalData(null);
|
||
|
||
// Only show success toast if request was actually created successfully
|
||
toast.success('Claim Request Submitted', {
|
||
description: 'Your claim management request has been created successfully.',
|
||
});
|
||
|
||
// Navigate to the created request detail page using requestNumber
|
||
if (createdRequest.requestNumber) {
|
||
navigate(`/request/${createdRequest.requestNumber}`);
|
||
} else if (createdRequest.requestId) {
|
||
// Fallback to requestId if requestNumber is not available
|
||
navigate(`/request/${createdRequest.requestId}`);
|
||
} else {
|
||
// This should not happen due to validation above, but just in case
|
||
navigate('/my-requests');
|
||
}
|
||
} catch (error: any) {
|
||
console.error('[App] Error creating claim request:', error);
|
||
|
||
// Check for manager-related errors
|
||
const errorData = error?.response?.data;
|
||
const errorCode = errorData?.code || errorData?.error?.code;
|
||
|
||
if (errorCode === 'NO_MANAGER_FOUND') {
|
||
// Show modal for no manager found
|
||
setManagerModalData({
|
||
errorType: 'NO_MANAGER_FOUND',
|
||
message: errorData?.message || errorData?.error?.message || 'No reporting manager found. Please ensure your manager is correctly configured in the system.',
|
||
pendingClaimData: claimData,
|
||
});
|
||
setManagerModalOpen(true);
|
||
return;
|
||
}
|
||
|
||
if (errorCode === 'MULTIPLE_MANAGERS_FOUND') {
|
||
// Show modal with manager list for selection
|
||
const managers = errorData?.managers || errorData?.error?.managers || [];
|
||
setManagerModalData({
|
||
errorType: 'MULTIPLE_MANAGERS_FOUND',
|
||
managers: managers,
|
||
message: errorData?.message || errorData?.error?.message || 'Multiple managers found. Please select one.',
|
||
pendingClaimData: claimData,
|
||
});
|
||
setManagerModalOpen(true);
|
||
return;
|
||
}
|
||
|
||
// Other errors - show toast
|
||
const errorMessage = error?.response?.data?.message || error?.message || 'Failed to create claim request';
|
||
toast.error('Failed to Submit Claim Request', {
|
||
description: errorMessage,
|
||
});
|
||
}
|
||
|
||
};
|
||
|
||
return (
|
||
<div className="min-h-screen h-screen flex flex-col overflow-hidden bg-background">
|
||
<Routes>
|
||
{/* Auth Callback - Unified callback for both OKTA and Tanflow */}
|
||
<Route
|
||
path="/login/callback"
|
||
element={<AuthCallback />}
|
||
/>
|
||
|
||
{/* Dashboard - Conditionally renders DealerDashboard or regular Dashboard */}
|
||
<Route
|
||
path="/"
|
||
element={
|
||
<PageLayout currentPage="dashboard" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<DashboardRoute onNavigate={handleNavigate} onNewRequest={handleNewRequest} />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
<Route
|
||
path="/dashboard"
|
||
element={
|
||
<PageLayout currentPage="dashboard" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<DashboardRoute onNavigate={handleNavigate} onNewRequest={handleNewRequest} />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Admin Routes Group with Shared Layout */}
|
||
<Route
|
||
element={
|
||
<PageLayout currentPage="admin" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Outlet />
|
||
</PageLayout>
|
||
}
|
||
>
|
||
<Route path="/admin" element={<Admin />} />
|
||
<Route path="/admin/create-template" element={<CreateTemplate />} />
|
||
<Route path="/admin/edit-template/:templateId" element={<CreateTemplate />} />
|
||
<Route path="/admin/templates" element={<AdminTemplatesList />} />
|
||
</Route>
|
||
|
||
{/* Create Request from Admin Template (Dedicated Flow) */}
|
||
<Route
|
||
path="/create-admin-request/:templateId"
|
||
element={
|
||
<CreateAdminRequest />
|
||
}
|
||
/>
|
||
|
||
|
||
|
||
{/* Open Requests */}
|
||
<Route
|
||
path="/open-requests"
|
||
element={
|
||
<PageLayout currentPage="open-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<OpenRequests onViewRequest={handleViewRequest} />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Closed Requests */}
|
||
<Route
|
||
path="/closed-requests"
|
||
element={
|
||
<PageLayout currentPage="closed-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<ClosedRequests onViewRequest={handleViewRequest} />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Shared Summaries */}
|
||
<Route
|
||
path="/shared-summaries"
|
||
element={
|
||
<PageLayout currentPage="shared-summaries" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<SharedSummaries />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Shared Summary Detail */}
|
||
<Route
|
||
path="/shared-summaries/:sharedSummaryId"
|
||
element={
|
||
<PageLayout currentPage="shared-summaries" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<SharedSummaryDetail />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Form 16 – Credit Notes (dealer) */}
|
||
<Route
|
||
path="/form16/credit-notes"
|
||
element={
|
||
<PageLayout currentPage="form16-credit-notes" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Form16CreditNotes />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
<Route
|
||
path="/form16/credit-notes/:id"
|
||
element={
|
||
<PageLayout currentPage="form16-credit-notes" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Form16CreditNoteDetail />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Form 16 – Submit (dealer) */}
|
||
<Route
|
||
path="/form16/submit"
|
||
element={
|
||
<PageLayout currentPage="form16-submit" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Form16Submit />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Form 16 – Submission result (after dealer submits) */}
|
||
<Route
|
||
path="/form16/submit/result"
|
||
element={
|
||
<PageLayout currentPage="form16-submit" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Form16SubmissionResult />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Form 16 – 26AS (RE) */}
|
||
<Route
|
||
path="/form16/26as"
|
||
element={
|
||
<PageLayout currentPage="form16-26as" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Form16_26AS />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Form 16 – Pending Submissions (dealer) */}
|
||
<Route
|
||
path="/form16/pending-submissions"
|
||
element={
|
||
<PageLayout currentPage="form16-pending-submissions" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Form16PendingSubmissions />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Form 16 – Non-submitted dealers (RE) */}
|
||
<Route
|
||
path="/form16/non-submitted-dealers"
|
||
element={
|
||
<PageLayout currentPage="form16-non-submitted-dealers" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Form16NonSubmittedDealers />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* My Requests */}
|
||
<Route
|
||
path="/my-requests"
|
||
element={
|
||
<PageLayout currentPage="my-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<MyRequests onViewRequest={handleViewRequest} dynamicRequests={dynamicRequests} />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Requests - Separate screens for Admin and Regular Users */}
|
||
<Route
|
||
path="/requests"
|
||
element={
|
||
<PageLayout currentPage="requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<RequestsRoute onViewRequest={handleViewRequest} />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Approver Performance - Detailed Performance Analysis */}
|
||
<Route
|
||
path="/approver-performance"
|
||
element={
|
||
<PageLayout currentPage="approver-performance" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<ApproverPerformance />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Request Detail - requestId will be read from URL params */}
|
||
<Route
|
||
path="/request/:requestId"
|
||
element={
|
||
<PageLayout currentPage="request-detail" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<RequestDetail
|
||
requestId=""
|
||
onBack={handleBack}
|
||
dynamicRequests={dynamicRequests}
|
||
/>
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Work Notes - Dedicated Full-Screen Page */}
|
||
<Route
|
||
path="/work-notes/:requestId"
|
||
element={<WorkNotes />}
|
||
/>
|
||
|
||
{/* New Request (Custom) */}
|
||
<Route
|
||
path="/new-request"
|
||
element={
|
||
<CreateRequest
|
||
onBack={handleBack}
|
||
onSubmit={handleNewRequestSubmit}
|
||
/>
|
||
}
|
||
/>
|
||
|
||
{/* Edit Draft Request */}
|
||
<Route
|
||
path="/edit-request/:requestId"
|
||
element={
|
||
<CreateRequest
|
||
onBack={handleBack}
|
||
onSubmit={handleNewRequestSubmit}
|
||
requestId={undefined} // Will be read from URL params
|
||
isEditMode={true}
|
||
/>
|
||
}
|
||
/>
|
||
|
||
{/* Claim Management Wizard */}
|
||
<Route
|
||
path="/claim-management"
|
||
element={
|
||
<ClaimManagementWizard
|
||
onBack={handleBack}
|
||
onSubmit={handleClaimManagementSubmit}
|
||
/>
|
||
}
|
||
/>
|
||
|
||
{/* Profile */}
|
||
<Route
|
||
path="/profile"
|
||
element={
|
||
<PageLayout currentPage="profile" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Profile />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Settings */}
|
||
<Route
|
||
path="/settings"
|
||
element={
|
||
<PageLayout currentPage="settings" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Settings />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Security Settings */}
|
||
<Route
|
||
path="/settings/security"
|
||
element={
|
||
<PageLayout currentPage="settings" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<SecuritySettings />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Notifications */}
|
||
<Route
|
||
path="/notifications"
|
||
element={
|
||
<PageLayout currentPage="notifications" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<Notifications onNavigate={handleNavigate} />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
{/* Detailed Reports */}
|
||
<Route
|
||
path="/detailed-reports"
|
||
element={
|
||
<PageLayout currentPage="detailed-reports" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||
<DetailedReports />
|
||
</PageLayout>
|
||
}
|
||
/>
|
||
|
||
|
||
|
||
|
||
</Routes>
|
||
|
||
<Toaster
|
||
position="top-right"
|
||
toastOptions={{
|
||
style: {
|
||
background: 'var(--card)',
|
||
color: 'var(--card-foreground)',
|
||
border: '1px solid var(--border)',
|
||
},
|
||
}}
|
||
/>
|
||
|
||
{/* Manager Selection Modal */}
|
||
<ManagerSelectionModal
|
||
open={managerModalOpen}
|
||
onClose={() => {
|
||
setManagerModalOpen(false);
|
||
setManagerModalData(null);
|
||
}}
|
||
onSelect={async (managerEmail: string) => {
|
||
if (managerModalData?.pendingClaimData) {
|
||
// Retry creating claim request with selected manager
|
||
// The pendingClaimData contains all the form data from the wizard
|
||
// This preserves the entire submission state while waiting for manager selection
|
||
await handleClaimManagementSubmit(managerModalData.pendingClaimData, managerEmail);
|
||
}
|
||
}}
|
||
managers={managerModalData?.managers}
|
||
errorType={managerModalData?.errorType || 'NO_MANAGER_FOUND'}
|
||
message={managerModalData?.message}
|
||
isLoading={false} // Will be set to true during retry if needed
|
||
/>
|
||
|
||
{/* Approval Action Modal */}
|
||
{approvalAction && (
|
||
<ApprovalActionModal
|
||
isOpen={!!approvalAction}
|
||
onClose={handleCloseApprovalModal}
|
||
action={approvalAction}
|
||
requestId={selectedRequestId}
|
||
requestTitle={selectedRequestTitle}
|
||
onSubmit={handleApprovalSubmit}
|
||
/>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Main App Component with Router
|
||
interface MainAppProps {
|
||
onLogout?: () => void;
|
||
}
|
||
|
||
export default function App(props?: MainAppProps) {
|
||
const { onLogout } = props || {};
|
||
|
||
return (
|
||
<BrowserRouter>
|
||
<AppRoutes onLogout={onLogout} />
|
||
</BrowserRouter>
|
||
);
|
||
}
|