diff --git a/src/App.tsx b/src/App.tsx index 62ea014..17216cd 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { BrowserRouter, Routes, Route, useNavigate } from 'react-router-dom'; +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'; @@ -21,6 +21,9 @@ import { Settings } from '@/pages/Settings'; import { Notifications } from '@/pages/Notifications'; import { DetailedReports } from '@/pages/DetailedReports'; import { Admin } from '@/pages/Admin'; +import { AdminTemplatesList } from '@/pages/Admin/Templates/AdminTemplatesList'; +import { CreateTemplate } from '@/pages/Admin/Templates/CreateTemplate'; +import { CreateAdminRequest } from '@/pages/CreateAdminRequest/CreateAdminRequest'; import { ApprovalActionModal } from '@/components/modals/ApprovalActionModal'; import { Toaster } from '@/components/ui/sonner'; import { toast } from 'sonner'; @@ -40,7 +43,7 @@ interface AppProps { 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) @@ -152,7 +155,7 @@ function AppRoutes({ onLogout }: AppProps) { 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, @@ -180,18 +183,18 @@ function AppRoutes({ onLogout }: AppProps) { } 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(Object.keys(CUSTOM_REQUEST_DATABASE).length + dynamicRequests.length + 1).padStart(3, '0')}`; - + // Create full custom request object const newCustomRequest = { id: requestId, @@ -219,21 +222,21 @@ function AppRoutes({ onLogout }: AppProps) { avatar: 'CU' }, department: requestData.department || 'General', - createdAt: new Date().toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', + createdAt: new Date().toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', minute: 'numeric', - hour12: true + hour12: true }), - updatedAt: new Date().toLocaleDateString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', + updatedAt: new Date().toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', minute: 'numeric', - hour12: true + hour12: true }), dueDate: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(), submittedDate: new Date().toISOString(), @@ -243,7 +246,7 @@ function AppRoutes({ onLogout }: AppProps) { // 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})` : ''}`, @@ -268,32 +271,28 @@ function AppRoutes({ onLogout }: AppProps) { }; }), auditTrail: [ - { - type: 'created', - action: 'Request Created', - details: `Custom request "${requestData.title}" created`, - user: 'Current User', + { + 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', + { + 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) => { @@ -310,7 +309,7 @@ function AppRoutes({ onLogout }: AppProps) { duration: 5000, }); } - + setApprovalAction(null); resolve(true); }, 1000); @@ -343,7 +342,7 @@ function AppRoutes({ onLogout }: AppProps) { // 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'); @@ -377,11 +376,11 @@ function AppRoutes({ onLogout }: AppProps) { } } 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({ @@ -392,7 +391,7 @@ function AppRoutes({ onLogout }: AppProps) { setManagerModalOpen(true); return; } - + if (errorCode === 'MULTIPLE_MANAGERS_FOUND') { // Show modal with manager list for selection const managers = errorData?.managers || errorData?.error?.managers || []; @@ -405,20 +404,20 @@ function AppRoutes({ onLogout }: AppProps) { 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, }); } - + // Keep the old code below for backward compatibility (local storage fallback) // This can be removed once API integration is fully tested /* // Generate unique ID for the new claim request const requestId = `RE-REQ-2024-CM-${String(dynamicRequests.length + 2).padStart(3, '0')}`; - + // Create full request object const newRequest = { id: requestId, @@ -445,21 +444,21 @@ function AppRoutes({ onLogout }: AppProps) { avatar: 'CU' }, department: 'Marketing', - createdAt: new Date().toLocaleString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', + createdAt: new Date().toLocaleString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', minute: 'numeric', - hour12: true + hour12: true }), - updatedAt: new Date().toLocaleString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', + updatedAt: new Date().toLocaleString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', minute: 'numeric', - hour12: true + hour12: true }), dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), conclusionRemark: '', @@ -579,30 +578,30 @@ function AppRoutes({ onLogout }: AppProps) { documents: [], spectators: [], auditTrail: [ - { - type: 'created', - action: 'Request Created', - details: `Claim request for ${claimData.activityName} created`, - user: 'Current User', - timestamp: new Date().toLocaleString('en-US', { - month: 'short', - day: 'numeric', - year: 'numeric', - hour: 'numeric', + { + type: 'created', + action: 'Request Created', + details: `Claim request for ${claimData.activityName} created`, + user: 'Current User', + timestamp: new Date().toLocaleString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: 'numeric', minute: 'numeric', - hour12: true + hour12: true }) } ], tags: ['claim-management', 'new-request', claimData.activityType?.toLowerCase().replace(/\s+/g, '-')] }; - + // Add to dynamic requests setDynamicRequests(prev => [...prev, newRequest]); - + // Also add to REQUEST_DATABASE for immediate viewing (REQUEST_DATABASE as any)[requestId] = newRequest; - + toast.success('Claim Request Submitted', { description: 'Your claim management request has been created successfully.', }); @@ -614,134 +613,194 @@ function AppRoutes({ onLogout }: AppProps) {
RE Flow
@@ -248,28 +249,31 @@ export function PageLayout({ children, currentPage = 'dashboard', onNavigate, on- Start with a pre-built template for faster approvals, or create a custom request tailored to your needs. + {viewMode === 'main' + ? 'Start with a pre-built template for faster approvals, or create a custom request tailored to your needs.' + : 'Select a pre-configured workflow template defined by your organization.'}
No admin templates available yet.
+handleTemplateClick(template) : undefined} + data-testid={`template-card-${template.id}-clickable`} > - {template.description} -
-+ {template.description} +
+ + {!isCategoryCard && ( + <> ++ Click to browse templates → +
+{selectedTemplate.suggestedSLA} hours
{selectedTemplate.estimatedTime}
+Manage workflow templates for your organization
++ {searchQuery ? 'Try adjusting your search terms' : 'Get started by creating your first workflow template'} +
+ {!searchQuery && ( + + )} ++ {isEditing ? 'Update existing workflow configuration' : 'Define a new standardized request workflow'} +
++ The requested template could not be loaded. It may have been deleted or you do not have permission to view it. +
+{template.name}
+{template.description}
++ Explain what you need approval for, why it's needed, and any relevant details. +
+Click to upload files
+PDF, Excel, Images (Max 10MB)
+ +{file.name}
+{(file.size / 1024 / 1024).toFixed(2)} MB
++ Please review the details below. This request will follow the standardized approval workflow defined by the administrator. +
+{formData.title}
+Level {approver.level} Approver
+{approver.email}
+