768 lines
28 KiB
TypeScript
768 lines
28 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { BrowserRouter, Routes, Route, useNavigate } 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 { RequestDetailTemplated } from '@/pages/RequestDetail/RequestDetailTemplated';
|
|
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 '@/components/workflow/ClaimManagementWizard';
|
|
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 { Notifications } from '@/pages/Notifications';
|
|
import { DetailedReports } from '@/pages/DetailedReports';
|
|
import { Admin } from '@/pages/Admin';
|
|
import { ApprovalActionModal } from '@/components/modals/ApprovalActionModal';
|
|
import { Toaster } from '@/components/ui/sonner';
|
|
import { toast } from 'sonner';
|
|
import { CUSTOM_REQUEST_DATABASE } from '@/utils/customRequestDatabase';
|
|
import { CLAIM_MANAGEMENT_DATABASE } from '@/utils/claimManagementDatabase';
|
|
import { AuthCallback } from '@/pages/Auth/AuthCallback';
|
|
import { mockApi } from '@/services/mockApi';
|
|
|
|
// Combined Request Database for backward compatibility
|
|
// This combines both custom and claim management requests
|
|
export const REQUEST_DATABASE: any = {
|
|
...CUSTOM_REQUEST_DATABASE,
|
|
...CLAIM_MANAGEMENT_DATABASE
|
|
};
|
|
|
|
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} />;
|
|
}
|
|
}
|
|
|
|
// Main Application Routes Component
|
|
function AppRoutes({ onLogout }: AppProps) {
|
|
const navigate = useNavigate();
|
|
const { user } = useAuth(); // Add user from useAuth hook
|
|
const [approvalAction, setApprovalAction] = useState<'approve' | 'reject' | null>(null);
|
|
const [dynamicRequests, setDynamicRequests] = useState<any[]>([]);
|
|
const [selectedRequestId, setSelectedRequestId] = useState<string>('');
|
|
const [selectedRequestTitle, setSelectedRequestTitle] = useState<string>('');
|
|
|
|
// 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 = async (requestId: string, requestTitle?: string, status?: string) => {
|
|
setSelectedRequestId(requestId);
|
|
setSelectedRequestTitle(requestTitle || 'Unknown Request');
|
|
|
|
// Check if request is a draft - if so, route to edit form instead of detail view
|
|
const isDraft = status?.toLowerCase() === 'draft' || status === 'DRAFT';
|
|
if (isDraft) {
|
|
navigate(`/edit-request/${requestId}`);
|
|
} else {
|
|
navigate(`/request/${requestId}`);
|
|
}
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
// Regular custom request submission
|
|
// Generate unique ID for the new custom request
|
|
const requestId = `RE-REQ-2024-${String(Object.keys(CUSTOM_REQUEST_DATABASE).length + 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');
|
|
toast.success('Request Submitted Successfully!', {
|
|
description: `Your request "${requestData.title}" (${requestId}) has been created and sent for approval.`,
|
|
duration: 5000,
|
|
});
|
|
};
|
|
|
|
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) => {
|
|
// Generate unique ID
|
|
const year = new Date().getFullYear();
|
|
const requestsResponse = await mockApi.getAllRequests();
|
|
const requests = requestsResponse.success ? (requestsResponse.data || []) : [];
|
|
const existingIds = requests
|
|
.filter((r: any) => r.requestId?.includes(`${year}-CM`))
|
|
.map((r: any) => {
|
|
const match = r.requestId?.match(/-(\d+)$/);
|
|
return match ? parseInt(match[1]) : 0;
|
|
});
|
|
const nextNumber = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 1;
|
|
const requestId = `RE-REQ-${year}-CM-${nextNumber.toString().padStart(3, '0')}`;
|
|
|
|
const now = new Date().toISOString();
|
|
const dueDate = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
|
|
// Create full request object matching DatabaseRequest interface
|
|
const newRequest = {
|
|
id: requestId,
|
|
requestId: requestId,
|
|
requestNumber: requestId,
|
|
title: `${claimData.activityName} - Claim Request`,
|
|
description: claimData.requestDescription,
|
|
category: 'claim-management',
|
|
subcategory: 'Claim Management',
|
|
type: 'dealer-claim',
|
|
status: 'pending',
|
|
priority: 'standard',
|
|
amount: claimData.estimatedBudget ? parseFloat(claimData.estimatedBudget.replace(/[₹,]/g, '')) || 0 : 0,
|
|
claimAmount: claimData.estimatedBudget ? parseFloat(claimData.estimatedBudget.replace(/[₹,]/g, '')) || 0 : 0,
|
|
slaProgress: 0,
|
|
slaRemaining: '7 days',
|
|
slaEndDate: dueDate,
|
|
currentStep: 1,
|
|
totalSteps: 8,
|
|
template: 'claim-management',
|
|
templateName: 'Claim Management',
|
|
initiator: {
|
|
userId: (user as any)?.userId || 'user-123',
|
|
name: (user as any)?.name || 'Current User',
|
|
email: (user as any)?.email || 'current.user@royalenfield.com',
|
|
role: (user as any)?.role || 'Regional Marketing Coordinator',
|
|
department: (user as any)?.department || 'Marketing',
|
|
phone: (user as any)?.phone || '+91 98765 43290',
|
|
avatar: (user as any)?.name?.split(' ').map((n: string) => n[0]).join('').toUpperCase() || 'CU'
|
|
},
|
|
department: (user as any)?.department || 'Marketing',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
dueDate: dueDate,
|
|
conclusionRemark: '',
|
|
claimDetails: {
|
|
activityName: claimData.activityName,
|
|
activityType: claimData.activityType,
|
|
activityDate: claimData.activityDate ? new Date(claimData.activityDate).toISOString() : now,
|
|
location: claimData.location,
|
|
dealerCode: claimData.dealerCode,
|
|
dealerName: claimData.dealerName,
|
|
dealerEmail: claimData.dealerEmail || 'N/A',
|
|
dealerPhone: claimData.dealerPhone || 'N/A',
|
|
dealerAddress: claimData.dealerAddress || 'N/A',
|
|
requestDescription: claimData.requestDescription,
|
|
estimatedBudget: claimData.estimatedBudget || 'TBD',
|
|
periodStart: claimData.periodStartDate ? new Date(claimData.periodStartDate).toISOString() : now,
|
|
periodEnd: claimData.periodEndDate ? new Date(claimData.periodEndDate).toISOString() : dueDate
|
|
},
|
|
// Also add dealerInfo for compatibility
|
|
dealerInfo: {
|
|
name: claimData.dealerName,
|
|
code: claimData.dealerCode,
|
|
email: claimData.dealerEmail || 'N/A',
|
|
phone: claimData.dealerPhone || 'N/A',
|
|
address: claimData.dealerAddress || 'N/A',
|
|
},
|
|
// Add activityInfo for compatibility
|
|
activityInfo: {
|
|
activityName: claimData.activityName,
|
|
activityType: claimData.activityType,
|
|
activityDate: claimData.activityDate ? new Date(claimData.activityDate).toISOString() : now,
|
|
location: claimData.location,
|
|
},
|
|
tags: ['claim-management', 'new-request', claimData.activityType?.toLowerCase().replace(/\s+/g, '-')]
|
|
};
|
|
|
|
// Save to mock API
|
|
try {
|
|
console.log('[Claim Management] Creating request:', requestId);
|
|
const createResponse = await mockApi.createRequest(newRequest);
|
|
if (!createResponse.success) {
|
|
throw new Error(createResponse.error?.message || 'Failed to create request');
|
|
}
|
|
const savedRequest = createResponse.data;
|
|
console.log('[Claim Management] Request created successfully:', savedRequest.requestId);
|
|
|
|
// Create approval flow steps for dealer claim (8-step workflow)
|
|
const initiatorName = (user as any)?.name || 'Current User';
|
|
const approvalFlowSteps = [
|
|
{
|
|
step: 1,
|
|
approver: `${claimData.dealerName} (Dealer)`,
|
|
role: 'Dealer - Proposal Submission',
|
|
status: 'pending' as const,
|
|
tatHours: 72,
|
|
levelId: 'level-1',
|
|
description: 'Dealer submits the proposal for the activity with comments including proposal document with requested details, cost break-up, timeline for closure, and other requests'
|
|
},
|
|
{
|
|
step: 2,
|
|
approver: `${initiatorName} (Requestor)`,
|
|
role: 'Requestor Evaluation & Confirmation',
|
|
status: 'waiting' as const,
|
|
tatHours: 48,
|
|
levelId: 'level-2',
|
|
description: 'Requestor evaluates the request and confirms with comments. Decision point: Confirms? (YES → Continue to Dept Lead / NO → Request is cancelled)'
|
|
},
|
|
{
|
|
step: 3,
|
|
approver: 'Department Lead',
|
|
role: 'Dept Lead Approval',
|
|
status: 'waiting' as const,
|
|
tatHours: 72,
|
|
levelId: 'level-3',
|
|
description: 'Department Lead approval. Decision point: Approved? (YES → Budget is blocked in the respective IO for the activity / NO → More clarification required → Request is cancelled)'
|
|
},
|
|
{
|
|
step: 4,
|
|
approver: 'System Auto-Process',
|
|
role: 'Activity Creation',
|
|
status: 'waiting' as const,
|
|
tatHours: 1,
|
|
levelId: 'level-4',
|
|
description: 'Activity is created. Activity confirmation email is auto-triggered to dealer / requestor / Lead. IO confirmation to be made.'
|
|
},
|
|
{
|
|
step: 5,
|
|
approver: `${claimData.dealerName} (Dealer)`,
|
|
role: 'Dealer - Completion Documents',
|
|
status: 'waiting' as const,
|
|
tatHours: 120,
|
|
levelId: 'level-5',
|
|
description: 'Dealer submits the necessary documents upon completion of the activity including document attachments (Zip Folder) and brief description'
|
|
},
|
|
{
|
|
step: 6,
|
|
approver: `${initiatorName} (Requestor)`,
|
|
role: 'Requestor - Claim Approval',
|
|
status: 'waiting' as const,
|
|
tatHours: 48,
|
|
levelId: 'level-6',
|
|
description: 'Requestor approves the claim in full or can modify the amount. If more information is required, can request additional details from dealer.'
|
|
},
|
|
{
|
|
step: 7,
|
|
approver: 'System Auto-Process',
|
|
role: 'E-Invoice Generation',
|
|
status: 'waiting' as const,
|
|
tatHours: 1,
|
|
levelId: 'level-7',
|
|
description: 'E-invoice will be generated through DMS.'
|
|
},
|
|
{
|
|
step: 8,
|
|
approver: 'Finance Team',
|
|
role: 'Credit Note from SAP',
|
|
status: 'waiting' as const,
|
|
tatHours: 48,
|
|
levelId: 'level-8',
|
|
description: 'Got credit note from SAP. Review and send to dealer to complete the claim management process.'
|
|
},
|
|
];
|
|
|
|
// Create approval flow steps
|
|
console.log('[Claim Management] Creating approval flows...');
|
|
for (const step of approvalFlowSteps) {
|
|
const flowResponse = await mockApi.createApprovalFlow(savedRequest.requestId, {
|
|
step: step.step,
|
|
levelId: step.levelId || `level-${step.step}`,
|
|
approver: step.approver,
|
|
role: step.role,
|
|
status: step.status,
|
|
tatHours: step.tatHours,
|
|
assignedAt: step.status === 'pending' ? now : undefined,
|
|
description: step.description,
|
|
});
|
|
if (!flowResponse.success) {
|
|
console.error(`[Claim Management] Failed to create approval flow step ${step.step}:`, flowResponse.error);
|
|
} else {
|
|
console.log(`[Claim Management] Created approval flow Step ${step.step}:`, flowResponse.data);
|
|
}
|
|
}
|
|
console.log('[Claim Management] All approval flows created');
|
|
|
|
// Create initial activity
|
|
const activityResponse = await mockApi.createActivity(savedRequest.requestId, {
|
|
id: `act-${Date.now()}`,
|
|
type: 'created',
|
|
action: 'Request Created',
|
|
details: `Claim request for ${claimData.activityName} created`,
|
|
user: savedRequest.initiator.name,
|
|
message: 'Request created',
|
|
});
|
|
if (!activityResponse.success) {
|
|
console.error('[Claim Management] Failed to create initial activity:', activityResponse.error);
|
|
}
|
|
|
|
// Add to dynamic requests for immediate UI update
|
|
setDynamicRequests(prev => [...prev, savedRequest]);
|
|
|
|
// Also add to REQUEST_DATABASE for backward compatibility
|
|
(REQUEST_DATABASE as any)[requestId] = savedRequest;
|
|
|
|
console.log('[Claim Management] Request fully created. Navigating to:', `/request/${requestId}`);
|
|
|
|
// Show success message with more details
|
|
toast.success('Claim Request Submitted Successfully!', {
|
|
description: `Request ${requestId} has been created and is ready for dealer proposal submission.`,
|
|
duration: 5000,
|
|
});
|
|
|
|
// Small delay to ensure toast is visible before navigation
|
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
|
// Navigate to the demo request detail page
|
|
navigate(`/demo/request-detail/${requestId}`, {
|
|
replace: false,
|
|
state: {
|
|
fromWizard: true,
|
|
requestId: requestId
|
|
}
|
|
});
|
|
} catch (error: any) {
|
|
console.error('[Claim Management] Failed to create request:', error);
|
|
toast.error('Failed to Submit Request', {
|
|
description: error.message || 'An error occurred while creating the request. Please try again.',
|
|
duration: 6000,
|
|
});
|
|
throw error; // Re-throw to allow component to handle it
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen h-screen flex flex-col overflow-hidden bg-background">
|
|
<Routes>
|
|
{/* Auth Callback - Must be before other routes */}
|
|
<Route
|
|
path="/login/callback"
|
|
element={<AuthCallback />}
|
|
/>
|
|
|
|
{/* Dashboard */}
|
|
<Route
|
|
path="/"
|
|
element={
|
|
<PageLayout currentPage="dashboard" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
|
<Dashboard onNavigate={handleNavigate} onNewRequest={handleNewRequest} />
|
|
</PageLayout>
|
|
}
|
|
/>
|
|
|
|
<Route
|
|
path="/dashboard"
|
|
element={
|
|
<PageLayout currentPage="dashboard" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
|
<Dashboard onNavigate={handleNavigate} onNewRequest={handleNewRequest} />
|
|
</PageLayout>
|
|
}
|
|
/>
|
|
|
|
{/* 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>
|
|
}
|
|
/>
|
|
|
|
{/* 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>
|
|
}
|
|
/>
|
|
|
|
{/* Demo Request Detail - Template System Preview */}
|
|
<Route
|
|
path="/demo/request-detail/:requestId"
|
|
element={
|
|
<RequestDetailTemplated
|
|
requestId=""
|
|
onBack={handleBack}
|
|
dynamicRequests={dynamicRequests}
|
|
/>
|
|
}
|
|
/>
|
|
|
|
{/* 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>
|
|
}
|
|
/>
|
|
|
|
{/* 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>
|
|
}
|
|
/>
|
|
|
|
{/* Admin Control Panel */}
|
|
<Route
|
|
path="/admin"
|
|
element={
|
|
<PageLayout currentPage="admin" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
|
<Admin />
|
|
</PageLayout>
|
|
}
|
|
/>
|
|
</Routes>
|
|
|
|
<Toaster
|
|
position="top-right"
|
|
toastOptions={{
|
|
style: {
|
|
background: 'var(--card)',
|
|
color: 'var(--card-foreground)',
|
|
border: '1px solid var(--border)',
|
|
},
|
|
}}
|
|
/>
|
|
|
|
{/* 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>
|
|
);
|
|
}
|