Compare commits
No commits in common. "1391e2d2f58dd1016171209e03f2e88c2cccd3de" and "b9a8c5bf523521d0b5be4fe9a347a6dc343d7e5b" have entirely different histories.
1391e2d2f5
...
b9a8c5bf52
250
src/App.tsx
250
src/App.tsx
@ -43,7 +43,7 @@ interface AppProps {
|
|||||||
function RequestsRoute({ onViewRequest }: { onViewRequest: (requestId: string) => void }) {
|
function RequestsRoute({ onViewRequest }: { onViewRequest: (requestId: string) => void }) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const isAdmin = hasManagementAccess(user);
|
const isAdmin = hasManagementAccess(user);
|
||||||
|
|
||||||
// Render separate screens based on user role
|
// Render separate screens based on user role
|
||||||
// Admin/Management users see all organization requests
|
// Admin/Management users see all organization requests
|
||||||
// Regular users see only their participant requests (approver/spectator, NOT initiator)
|
// Regular users see only their participant requests (approver/spectator, NOT initiator)
|
||||||
@ -104,7 +104,7 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
const handleViewRequest = async (requestId: string, requestTitle?: string, status?: string) => {
|
const handleViewRequest = async (requestId: string, requestTitle?: string, status?: string) => {
|
||||||
setSelectedRequestId(requestId);
|
setSelectedRequestId(requestId);
|
||||||
setSelectedRequestTitle(requestTitle || 'Unknown Request');
|
setSelectedRequestTitle(requestTitle || 'Unknown Request');
|
||||||
|
|
||||||
// Check if request is a draft - if so, route to edit form instead of detail view
|
// Check if request is a draft - if so, route to edit form instead of detail view
|
||||||
const isDraft = status?.toLowerCase() === 'draft' || status === 'DRAFT';
|
const isDraft = status?.toLowerCase() === 'draft' || status === 'DRAFT';
|
||||||
if (isDraft) {
|
if (isDraft) {
|
||||||
@ -131,11 +131,11 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regular custom request submission
|
// Regular custom request submission
|
||||||
// Generate unique ID for the new custom request
|
// 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')}`;
|
const requestId = `RE-REQ-2024-${String(Object.keys(CUSTOM_REQUEST_DATABASE).length + dynamicRequests.length + 1).padStart(3, '0')}`;
|
||||||
|
|
||||||
// Create full custom request object
|
// Create full custom request object
|
||||||
const newCustomRequest = {
|
const newCustomRequest = {
|
||||||
id: requestId,
|
id: requestId,
|
||||||
@ -163,21 +163,21 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
avatar: 'CU'
|
avatar: 'CU'
|
||||||
},
|
},
|
||||||
department: requestData.department || 'General',
|
department: requestData.department || 'General',
|
||||||
createdAt: new Date().toLocaleDateString('en-US', {
|
createdAt: new Date().toLocaleDateString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
hour12: true
|
hour12: true
|
||||||
}),
|
}),
|
||||||
updatedAt: new Date().toLocaleDateString('en-US', {
|
updatedAt: new Date().toLocaleDateString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
hour12: true
|
hour12: true
|
||||||
}),
|
}),
|
||||||
dueDate: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(),
|
dueDate: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(),
|
||||||
submittedDate: new Date().toISOString(),
|
submittedDate: new Date().toISOString(),
|
||||||
@ -187,7 +187,7 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
// Extract name from email if name is not available
|
// Extract name from email if name is not available
|
||||||
const approverName = approver?.name || approver?.email?.split('@')[0] || `Approver ${index + 1}`;
|
const approverName = approver?.name || approver?.email?.split('@')[0] || `Approver ${index + 1}`;
|
||||||
const approverEmail = approver?.email || '';
|
const approverEmail = approver?.email || '';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
step: index + 1,
|
step: index + 1,
|
||||||
approver: `${approverName}${approverEmail ? ` (${approverEmail})` : ''}`,
|
approver: `${approverName}${approverEmail ? ` (${approverEmail})` : ''}`,
|
||||||
@ -212,28 +212,32 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
};
|
};
|
||||||
}),
|
}),
|
||||||
auditTrail: [
|
auditTrail: [
|
||||||
{
|
{
|
||||||
type: 'created',
|
type: 'created',
|
||||||
action: 'Request Created',
|
action: 'Request Created',
|
||||||
details: `Custom request "${requestData.title}" created`,
|
details: `Custom request "${requestData.title}" created`,
|
||||||
user: 'Current User',
|
user: 'Current User',
|
||||||
timestamp: new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true })
|
timestamp: new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true })
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: 'assignment',
|
type: 'assignment',
|
||||||
action: 'Assigned to Approver',
|
action: 'Assigned to Approver',
|
||||||
details: `Request assigned to ${requestData.approvers?.[0]?.name || requestData.approvers?.[0]?.email || 'first approver'}`,
|
details: `Request assigned to ${requestData.approvers?.[0]?.name || requestData.approvers?.[0]?.email || 'first approver'}`,
|
||||||
user: 'System',
|
user: 'System',
|
||||||
timestamp: new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true })
|
timestamp: new Date().toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: 'numeric', hour12: true })
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
tags: requestData.tags || ['custom-request']
|
tags: requestData.tags || ['custom-request']
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add to dynamic requests
|
// Add to dynamic requests
|
||||||
setDynamicRequests([...dynamicRequests, newCustomRequest]);
|
setDynamicRequests([...dynamicRequests, newCustomRequest]);
|
||||||
|
|
||||||
navigate('/my-requests');
|
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) => {
|
const handleApprovalSubmit = (action: 'approve' | 'reject', _comment: string) => {
|
||||||
@ -250,7 +254,7 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
duration: 5000,
|
duration: 5000,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setApprovalAction(null);
|
setApprovalAction(null);
|
||||||
resolve(true);
|
resolve(true);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
@ -264,7 +268,7 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
const handleClaimManagementSubmit = (claimData: any) => {
|
const handleClaimManagementSubmit = (claimData: any) => {
|
||||||
// Generate unique ID for the new claim request
|
// Generate unique ID for the new claim request
|
||||||
const requestId = `RE-REQ-2024-CM-${String(dynamicRequests.length + 2).padStart(3, '0')}`;
|
const requestId = `RE-REQ-2024-CM-${String(dynamicRequests.length + 2).padStart(3, '0')}`;
|
||||||
|
|
||||||
// Create full request object
|
// Create full request object
|
||||||
const newRequest = {
|
const newRequest = {
|
||||||
id: requestId,
|
id: requestId,
|
||||||
@ -291,21 +295,21 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
avatar: 'CU'
|
avatar: 'CU'
|
||||||
},
|
},
|
||||||
department: 'Marketing',
|
department: 'Marketing',
|
||||||
createdAt: new Date().toLocaleString('en-US', {
|
createdAt: new Date().toLocaleString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
hour12: true
|
hour12: true
|
||||||
}),
|
}),
|
||||||
updatedAt: new Date().toLocaleString('en-US', {
|
updatedAt: new Date().toLocaleString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
hour12: true
|
hour12: true
|
||||||
}),
|
}),
|
||||||
dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
||||||
conclusionRemark: '',
|
conclusionRemark: '',
|
||||||
@ -425,30 +429,30 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
documents: [],
|
documents: [],
|
||||||
spectators: [],
|
spectators: [],
|
||||||
auditTrail: [
|
auditTrail: [
|
||||||
{
|
{
|
||||||
type: 'created',
|
type: 'created',
|
||||||
action: 'Request Created',
|
action: 'Request Created',
|
||||||
details: `Claim request for ${claimData.activityName} created`,
|
details: `Claim request for ${claimData.activityName} created`,
|
||||||
user: 'Current User',
|
user: 'Current User',
|
||||||
timestamp: new Date().toLocaleString('en-US', {
|
timestamp: new Date().toLocaleString('en-US', {
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
hour: 'numeric',
|
hour: 'numeric',
|
||||||
minute: 'numeric',
|
minute: 'numeric',
|
||||||
hour12: true
|
hour12: true
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
tags: ['claim-management', 'new-request', claimData.activityType?.toLowerCase().replace(/\s+/g, '-')]
|
tags: ['claim-management', 'new-request', claimData.activityType?.toLowerCase().replace(/\s+/g, '-')]
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add to dynamic requests
|
// Add to dynamic requests
|
||||||
setDynamicRequests(prev => [...prev, newRequest]);
|
setDynamicRequests(prev => [...prev, newRequest]);
|
||||||
|
|
||||||
// Also add to REQUEST_DATABASE for immediate viewing
|
// Also add to REQUEST_DATABASE for immediate viewing
|
||||||
(REQUEST_DATABASE as any)[requestId] = newRequest;
|
(REQUEST_DATABASE as any)[requestId] = newRequest;
|
||||||
|
|
||||||
toast.success('Claim Request Submitted', {
|
toast.success('Claim Request Submitted', {
|
||||||
description: 'Your claim management request has been created successfully.',
|
description: 'Your claim management request has been created successfully.',
|
||||||
});
|
});
|
||||||
@ -459,134 +463,134 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
<div className="min-h-screen h-screen flex flex-col overflow-hidden bg-background">
|
<div className="min-h-screen h-screen flex flex-col overflow-hidden bg-background">
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* Auth Callback - Must be before other routes */}
|
{/* Auth Callback - Must be before other routes */}
|
||||||
<Route
|
<Route
|
||||||
path="/login/callback"
|
path="/login/callback"
|
||||||
element={<AuthCallback />}
|
element={<AuthCallback />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Dashboard */}
|
{/* Dashboard */}
|
||||||
<Route
|
<Route
|
||||||
path="/"
|
path="/"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="dashboard" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="dashboard" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<Dashboard onNavigate={handleNavigate} onNewRequest={handleNewRequest} />
|
<Dashboard onNavigate={handleNavigate} onNewRequest={handleNewRequest} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Route
|
<Route
|
||||||
path="/dashboard"
|
path="/dashboard"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="dashboard" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="dashboard" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<Dashboard onNavigate={handleNavigate} onNewRequest={handleNewRequest} />
|
<Dashboard onNavigate={handleNavigate} onNewRequest={handleNewRequest} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Open Requests */}
|
{/* Open Requests */}
|
||||||
<Route
|
<Route
|
||||||
path="/open-requests"
|
path="/open-requests"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="open-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="open-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<OpenRequests onViewRequest={handleViewRequest} />
|
<OpenRequests onViewRequest={handleViewRequest} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Closed Requests */}
|
{/* Closed Requests */}
|
||||||
<Route
|
<Route
|
||||||
path="/closed-requests"
|
path="/closed-requests"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="closed-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="closed-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<ClosedRequests onViewRequest={handleViewRequest} />
|
<ClosedRequests onViewRequest={handleViewRequest} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Shared Summaries */}
|
{/* Shared Summaries */}
|
||||||
<Route
|
<Route
|
||||||
path="/shared-summaries"
|
path="/shared-summaries"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="shared-summaries" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="shared-summaries" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<SharedSummaries />
|
<SharedSummaries />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Shared Summary Detail */}
|
{/* Shared Summary Detail */}
|
||||||
<Route
|
<Route
|
||||||
path="/shared-summaries/:sharedSummaryId"
|
path="/shared-summaries/:sharedSummaryId"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="shared-summaries" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="shared-summaries" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<SharedSummaryDetail />
|
<SharedSummaryDetail />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* My Requests */}
|
{/* My Requests */}
|
||||||
<Route
|
<Route
|
||||||
path="/my-requests"
|
path="/my-requests"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="my-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="my-requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<MyRequests onViewRequest={handleViewRequest} dynamicRequests={dynamicRequests} />
|
<MyRequests onViewRequest={handleViewRequest} dynamicRequests={dynamicRequests} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Requests - Separate screens for Admin and Regular Users */}
|
{/* Requests - Separate screens for Admin and Regular Users */}
|
||||||
<Route
|
<Route
|
||||||
path="/requests"
|
path="/requests"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="requests" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<RequestsRoute onViewRequest={handleViewRequest} />
|
<RequestsRoute onViewRequest={handleViewRequest} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Approver Performance - Detailed Performance Analysis */}
|
{/* Approver Performance - Detailed Performance Analysis */}
|
||||||
<Route
|
<Route
|
||||||
path="/approver-performance"
|
path="/approver-performance"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="approver-performance" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="approver-performance" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<ApproverPerformance />
|
<ApproverPerformance />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Request Detail - requestId will be read from URL params */}
|
{/* Request Detail - requestId will be read from URL params */}
|
||||||
<Route
|
<Route
|
||||||
path="/request/:requestId"
|
path="/request/:requestId"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="request-detail" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="request-detail" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<RequestDetail
|
<RequestDetail
|
||||||
requestId=""
|
requestId=""
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
dynamicRequests={dynamicRequests}
|
dynamicRequests={dynamicRequests}
|
||||||
/>
|
/>
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Work Notes - Dedicated Full-Screen Page */}
|
{/* Work Notes - Dedicated Full-Screen Page */}
|
||||||
<Route
|
<Route
|
||||||
path="/work-notes/:requestId"
|
path="/work-notes/:requestId"
|
||||||
element={<WorkNotes />}
|
element={<WorkNotes />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* New Request (Custom) */}
|
{/* New Request (Custom) */}
|
||||||
<Route
|
<Route
|
||||||
path="/new-request"
|
path="/new-request"
|
||||||
element={
|
element={
|
||||||
<CreateRequest
|
<CreateRequest
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
onSubmit={handleNewRequestSubmit}
|
onSubmit={handleNewRequestSubmit}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Edit Draft Request */}
|
{/* Edit Draft Request */}
|
||||||
<Route
|
<Route
|
||||||
path="/edit-request/:requestId"
|
path="/edit-request/:requestId"
|
||||||
element={
|
element={
|
||||||
<CreateRequest
|
<CreateRequest
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
@ -594,72 +598,72 @@ function AppRoutes({ onLogout }: AppProps) {
|
|||||||
requestId={undefined} // Will be read from URL params
|
requestId={undefined} // Will be read from URL params
|
||||||
isEditMode={true}
|
isEditMode={true}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Claim Management Wizard */}
|
{/* Claim Management Wizard */}
|
||||||
<Route
|
<Route
|
||||||
path="/claim-management"
|
path="/claim-management"
|
||||||
element={
|
element={
|
||||||
<ClaimManagementWizard
|
<ClaimManagementWizard
|
||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
onSubmit={handleClaimManagementSubmit}
|
onSubmit={handleClaimManagementSubmit}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Profile */}
|
{/* Profile */}
|
||||||
<Route
|
<Route
|
||||||
path="/profile"
|
path="/profile"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="profile" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="profile" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<Profile />
|
<Profile />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
<Route
|
<Route
|
||||||
path="/settings"
|
path="/settings"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="settings" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="settings" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<Settings />
|
<Settings />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Notifications */}
|
{/* Notifications */}
|
||||||
<Route
|
<Route
|
||||||
path="/notifications"
|
path="/notifications"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="notifications" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="notifications" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<Notifications onNavigate={handleNavigate} />
|
<Notifications onNavigate={handleNavigate} />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Detailed Reports */}
|
{/* Detailed Reports */}
|
||||||
<Route
|
<Route
|
||||||
path="/detailed-reports"
|
path="/detailed-reports"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="detailed-reports" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="detailed-reports" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<DetailedReports />
|
<DetailedReports />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Admin Control Panel */}
|
{/* Admin Control Panel */}
|
||||||
<Route
|
<Route
|
||||||
path="/admin"
|
path="/admin"
|
||||||
element={
|
element={
|
||||||
<PageLayout currentPage="admin" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
<PageLayout currentPage="admin" onNavigate={handleNavigate} onNewRequest={handleNewRequest} onLogout={onLogout}>
|
||||||
<Admin />
|
<Admin />
|
||||||
</PageLayout>
|
</PageLayout>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Routes>
|
||||||
|
|
||||||
<Toaster
|
<Toaster
|
||||||
position="top-right"
|
position="top-right"
|
||||||
toastOptions={{
|
toastOptions={{
|
||||||
style: {
|
style: {
|
||||||
@ -692,7 +696,7 @@ interface MainAppProps {
|
|||||||
|
|
||||||
export default function App(props?: MainAppProps) {
|
export default function App(props?: MainAppProps) {
|
||||||
const { onLogout } = props || {};
|
const { onLogout } = props || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<AppRoutes onLogout={onLogout} />
|
<AppRoutes onLogout={onLogout} />
|
||||||
|
|||||||
@ -59,29 +59,23 @@ export function TemplateSelectionStep({
|
|||||||
className="w-full max-w-6xl grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8"
|
className="w-full max-w-6xl grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 mb-8"
|
||||||
data-testid="template-selection-grid"
|
data-testid="template-selection-grid"
|
||||||
>
|
>
|
||||||
{templates.map((template) => {
|
{templates.map((template) => (
|
||||||
const isComingSoon = template.id === 'existing-template';
|
<motion.div
|
||||||
const isDisabled = isComingSoon;
|
key={template.id}
|
||||||
|
whileHover={{ scale: 1.03 }}
|
||||||
return (
|
whileTap={{ scale: 0.98 }}
|
||||||
<motion.div
|
transition={{ type: "spring", stiffness: 300, damping: 20 }}
|
||||||
key={template.id}
|
data-testid={`template-card-${template.id}`}
|
||||||
whileHover={!isDisabled ? { scale: 1.03 } : {}}
|
>
|
||||||
whileTap={!isDisabled ? { scale: 0.98 } : {}}
|
<Card
|
||||||
transition={{ type: "spring", stiffness: 300, damping: 20 }}
|
className={`cursor-pointer h-full transition-all duration-300 border-2 ${
|
||||||
data-testid={`template-card-${template.id}`}
|
selectedTemplate?.id === template.id
|
||||||
|
? 'border-blue-500 shadow-xl bg-blue-50/50 ring-2 ring-blue-200'
|
||||||
|
: 'border-gray-200 hover:border-blue-300 hover:shadow-lg'
|
||||||
|
}`}
|
||||||
|
onClick={() => onSelectTemplate(template)}
|
||||||
|
data-testid={`template-card-${template.id}-clickable`}
|
||||||
>
|
>
|
||||||
<Card
|
|
||||||
className={`h-full transition-all duration-300 border-2 ${
|
|
||||||
isDisabled
|
|
||||||
? 'border-gray-200 bg-gray-50/50 opacity-85 cursor-not-allowed'
|
|
||||||
: selectedTemplate?.id === template.id
|
|
||||||
? 'border-blue-500 shadow-xl bg-blue-50/50 ring-2 ring-blue-200 cursor-pointer'
|
|
||||||
: 'border-gray-200 hover:border-blue-300 hover:shadow-lg cursor-pointer'
|
|
||||||
}`}
|
|
||||||
onClick={!isDisabled ? () => onSelectTemplate(template) : undefined}
|
|
||||||
data-testid={`template-card-${template.id}-clickable`}
|
|
||||||
>
|
|
||||||
<CardHeader className="space-y-4 pb-4">
|
<CardHeader className="space-y-4 pb-4">
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div
|
<div
|
||||||
@ -114,20 +108,9 @@ export function TemplateSelectionStep({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<div className="flex items-start justify-between gap-2 mb-2">
|
<CardTitle className="text-xl mb-2" data-testid={`template-card-${template.id}-name`}>
|
||||||
<CardTitle className="text-xl" data-testid={`template-card-${template.id}-name`}>
|
{template.name}
|
||||||
{template.name}
|
</CardTitle>
|
||||||
</CardTitle>
|
|
||||||
{isComingSoon && (
|
|
||||||
<Badge
|
|
||||||
variant="outline"
|
|
||||||
className="text-xs bg-yellow-100 text-yellow-700 border-yellow-300 font-semibold"
|
|
||||||
data-testid={`template-card-${template.id}-coming-soon-badge`}
|
|
||||||
>
|
|
||||||
Coming Soon
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Badge variant="secondary" className="text-xs" data-testid={`template-card-${template.id}-category`}>
|
<Badge variant="secondary" className="text-xs" data-testid={`template-card-${template.id}-category`}>
|
||||||
{template.category}
|
{template.category}
|
||||||
@ -157,8 +140,7 @@ export function TemplateSelectionStep({
|
|||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
))}
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Template Details Card */}
|
{/* Template Details Card */}
|
||||||
|
|||||||
@ -3,7 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { toast } from 'sonner';
|
|
||||||
import { FormData, RequestTemplate } from '@/hooks/useCreateRequestForm';
|
import { FormData, RequestTemplate } from '@/hooks/useCreateRequestForm';
|
||||||
import {
|
import {
|
||||||
buildCreatePayload,
|
buildCreatePayload,
|
||||||
@ -73,12 +72,6 @@ export function useCreateRequestSubmission({
|
|||||||
documentsToDelete
|
documentsToDelete
|
||||||
);
|
);
|
||||||
|
|
||||||
// Show toast after backend confirmation
|
|
||||||
toast.success('Request Submitted Successfully!', {
|
|
||||||
description: `Your request "${formData.title}" has been submitted and sent for approval.`,
|
|
||||||
duration: 5000,
|
|
||||||
});
|
|
||||||
|
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
...formData,
|
...formData,
|
||||||
backendId: editRequestId,
|
backendId: editRequestId,
|
||||||
@ -94,24 +87,14 @@ export function useCreateRequestSubmission({
|
|||||||
|
|
||||||
const result = await createAndSubmitWorkflow(createPayload, documents);
|
const result = await createAndSubmitWorkflow(createPayload, documents);
|
||||||
|
|
||||||
// Show toast after backend confirmation
|
|
||||||
toast.success('Request Submitted Successfully!', {
|
|
||||||
description: `Your request "${formData.title}" has been created and sent for approval.`,
|
|
||||||
duration: 5000,
|
|
||||||
});
|
|
||||||
|
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
...formData,
|
...formData,
|
||||||
backendId: result.id,
|
backendId: result.id,
|
||||||
template: selectedTemplate,
|
template: selectedTemplate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error('Failed to submit workflow:', error);
|
console.error('Failed to submit workflow:', error);
|
||||||
toast.error('Failed to Submit Request', {
|
|
||||||
description: error?.response?.data?.message || error?.message || 'An error occurred while submitting the request.',
|
|
||||||
duration: 5000,
|
|
||||||
});
|
|
||||||
setSubmitting(false);
|
setSubmitting(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -147,11 +130,6 @@ export function useCreateRequestSubmission({
|
|||||||
documentsToDelete
|
documentsToDelete
|
||||||
);
|
);
|
||||||
|
|
||||||
toast.success('Draft Saved Successfully!', {
|
|
||||||
description: `Your request "${formData.title}" has been saved as draft.`,
|
|
||||||
duration: 5000,
|
|
||||||
});
|
|
||||||
|
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
...formData,
|
...formData,
|
||||||
backendId: editRequestId,
|
backendId: editRequestId,
|
||||||
@ -167,23 +145,14 @@ export function useCreateRequestSubmission({
|
|||||||
|
|
||||||
const result = await createWorkflow(createPayload, documents);
|
const result = await createWorkflow(createPayload, documents);
|
||||||
|
|
||||||
toast.success('Draft Saved Successfully!', {
|
|
||||||
description: `Your request "${formData.title}" has been saved as draft.`,
|
|
||||||
duration: 5000,
|
|
||||||
});
|
|
||||||
|
|
||||||
onSubmit?.({
|
onSubmit?.({
|
||||||
...formData,
|
...formData,
|
||||||
backendId: result.id,
|
backendId: result.id,
|
||||||
template: selectedTemplate,
|
template: selectedTemplate,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error) {
|
||||||
console.error('Failed to save draft:', error);
|
console.error('Failed to save draft:', error);
|
||||||
toast.error('Failed to Save Draft', {
|
|
||||||
description: error?.response?.data?.message || error?.message || 'An error occurred while saving the draft.',
|
|
||||||
duration: 5000,
|
|
||||||
});
|
|
||||||
setSavingDraft(false);
|
setSavingDraft(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user