unecessary consles removed approvr performance issue card redirection issue resolved. background image added on landing screen

This commit is contained in:
laxmanhalaki 2026-01-05 19:10:51 +05:30
parent 164d576ea0
commit 985b755707
15 changed files with 220 additions and 261 deletions

View File

@ -28,6 +28,7 @@ import { CUSTOM_REQUEST_DATABASE } from '@/utils/customRequestDatabase';
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 {
@ -148,12 +149,11 @@ function AppRoutes({ onLogout }: AppProps) {
}
};
const handleViewRequest = async (requestId: string, requestTitle?: string, status?: string, request?: any) => {
const handleViewRequest = (requestId: string, requestTitle?: string, status?: string, request?: any) => {
setSelectedRequestId(requestId);
setSelectedRequestTitle(requestTitle || 'Unknown Request');
// Use global navigation utility for consistent routing
const { navigateToRequest } = await import('@/utils/requestNavigation');
navigateToRequest({
requestId,
requestTitle,

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File

@ -8,6 +8,7 @@
// Images
export { default as ReLogo } from './images/Re_Logo.png';
export { default as RoyalEnfieldLogo } from './images/royal_enfield_logo.png';
export { default as LandingPageImage } from './images/landing_page_image.jpg';
// Fonts
// Add font exports here when fonts are added to the assets/fonts folder

View File

@ -91,13 +91,7 @@ export function IOTab({ request, apiRequest, onRefresh }: IOTabProps) {
// Formula: remaining = availableBeforeBlock - blockedAmount
const expectedRemaining = availableBeforeBlock - blockedAmt;
// Log for debugging backend calculation
console.log('[IOTab] Loading existing IO block:', {
availableBeforeBlock,
blockedAmount: blockedAmt,
expectedRemaining,
backendRemaining,
});
// Loading existing IO block
// Warn if remaining balance calculation seems incorrect (for backend debugging)
if (Math.abs(backendRemaining - expectedRemaining) > 0.01) {
@ -249,15 +243,7 @@ export function IOTab({ request, apiRequest, onRefresh }: IOTabProps) {
return;
}
// Log the amount being sent to backend for debugging
console.log('[IOTab] Blocking budget:', {
ioNumber: ioNumber.trim(),
originalInput: amountToBlock,
parsedAmount: blockAmountRaw,
roundedAmount: blockAmount,
fetchedAmount,
calculatedRemaining: fetchedAmount - blockAmount,
});
// Blocking budget
setBlockingBudget(true);
try {
@ -272,7 +258,7 @@ export function IOTab({ request, apiRequest, onRefresh }: IOTabProps) {
ioRemainingBalance: fetchedAmount - blockAmount, // Calculated value (backend will use SAP's actual value)
};
console.log('[IOTab] Sending to backend:', payload);
// Sending to backend
await updateIODetails(requestId, payload);
@ -287,16 +273,7 @@ export function IOTab({ request, apiRequest, onRefresh }: IOTabProps) {
// Calculate expected remaining balance for validation/debugging
const expectedRemainingBalance = fetchedAmount - savedBlockedAmount;
// Log what was saved vs what we sent
console.log('[IOTab] Blocking result:', {
sentAmount: blockAmount,
savedBlockedAmount,
availableBalance: fetchedAmount,
expectedRemaining: expectedRemainingBalance,
backendRemaining: savedRemainingBalance,
difference: savedBlockedAmount - blockAmount,
remainingDifference: fetchedAmount - savedRemainingBalance,
});
// Blocking result processed
// Warn if the saved amount differs from what we sent
if (Math.abs(savedBlockedAmount - blockAmount) > 0.01) {

View File

@ -86,16 +86,7 @@ export function ClaimManagementOverviewTab({
);
}
// Debug: Log mapped data for troubleshooting
console.debug('[ClaimManagementOverviewTab] Mapped claim data:', {
activityInfo: claimRequest.activityInfo,
dealerInfo: claimRequest.dealerInfo,
hasProposalDetails: !!claimRequest.proposalDetails,
closedExpenses: claimRequest.activityInfo?.closedExpenses,
closedExpensesBreakdown: claimRequest.activityInfo?.closedExpensesBreakdown,
hasDealerCode: !!claimRequest.dealerInfo?.dealerCode,
hasDealerName: !!claimRequest.dealerInfo?.dealerName,
});
// Mapped claim data ready
// Determine user's role
const userRole: RequestRole = determineUserRole(apiRequest, currentUserId);
@ -103,13 +94,7 @@ export function ClaimManagementOverviewTab({
// Get visibility settings based on role
const visibility = getRoleBasedVisibility(userRole);
console.debug('[ClaimManagementOverviewTab] User role and visibility:', {
userRole,
visibility,
currentUserId,
showDealerInfo: visibility.showDealerInfo,
dealerInfoPresent: !!(claimRequest.dealerInfo?.dealerCode || claimRequest.dealerInfo?.dealerName),
});
// User role and visibility determined
// Extract initiator info from request
// The apiRequest has initiator object with displayName, email, department, phone, etc.
@ -121,20 +106,7 @@ export function ClaimManagementOverviewTab({
phone: apiRequest.initiator?.phone || apiRequest.initiator?.mobile,
};
// Debug: Log closure props to help troubleshoot
console.debug('[ClaimManagementOverviewTab] Closure setup check:', {
needsClosure,
requestStatus: apiRequest?.status,
requestStatusLower: (apiRequest?.status || '').toLowerCase(),
hasConclusionRemark: !!conclusionRemark,
conclusionRemarkLength: conclusionRemark?.length || 0,
conclusionLoading,
conclusionSubmitting,
aiGenerated,
hasHandleGenerate: !!handleGenerateConclusion,
hasHandleFinalize: !!handleFinalizeConclusion,
hasSetConclusion: !!setConclusionRemark,
});
// Closure setup check completed
return (
<div className={`space-y-6 ${className}`}>

View File

@ -4,7 +4,6 @@ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/com
import { Button } from '@/components/ui/button';
import { Badge } from '@/components/ui/badge';
import { Progress } from '@/components/ui/progress';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { PieChart, Pie, Cell, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import { getDealerDashboard, type DashboardKPIs as DashboardKPIsType, type CategoryData as CategoryDataType } from '@/services/dealerClaimApi';
import { RefreshCw } from 'lucide-react';
@ -535,13 +534,7 @@ export function DealerDashboard({ onNavigate, onNewRequest: _onNewRequest }: Das
</div>
</CardHeader>
<CardContent>
<Tabs defaultValue="overview" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="overview">Overview</TabsTrigger>
<TabsTrigger value="category-1">Top Category 1</TabsTrigger>
<TabsTrigger value="category-2">Top Category 2</TabsTrigger>
</TabsList>
<TabsContent value="overview" className="space-y-4 mt-6">
<div className="space-y-4">
<div>
<h3 className="text-lg mb-4 text-gray-900">Activity Type Value Comparison</h3>
<ResponsiveContainer width="100%" height={350}>
@ -610,16 +603,7 @@ export function DealerDashboard({ onNavigate, onNewRequest: _onNewRequest }: Das
</Card>
))}
</div>
</TabsContent>
<TabsContent value="category-1" className="space-y-4">
{/* Category 1 details */}
<p className="text-gray-600">Detailed view for top category 1</p>
</TabsContent>
<TabsContent value="category-2" className="space-y-4">
{/* Category 2 details */}
<p className="text-gray-600">Detailed view for top category 2</p>
</TabsContent>
</Tabs>
</div>
</CardContent>
</Card>

View File

@ -245,14 +245,7 @@ function DealerClaimRequestDetailInner({ requestId: propRequestId, onBack, dynam
const requestStatus = (request?.status || apiRequest?.status || '').toLowerCase();
const needsClosure = (requestStatus === 'approved' || requestStatus === 'rejected') && isInitiator;
// Debug logging
console.debug('[DealerClaimRequestDetail] Closure check:', {
requestStatus,
requestStatusRaw: request?.status,
apiRequestStatusRaw: apiRequest?.status,
isInitiator,
needsClosure,
});
// Closure check completed
const {
conclusionRemark,
setConclusionRemark,

View File

@ -50,26 +50,46 @@ export function useConclusionRemark(
* Use Case: When request is approved, final approver generates conclusion.
* Initiator needs to review and finalize it before closing request.
*
* Optimization: Check request object first before making API call
* Process:
* 1. Dynamically import conclusion API service
* 2. Fetch conclusion by request ID
* 1. Check if conclusion data is already in request object
* 2. If not available, fetch from API
* 3. Load into state if exists
* 4. Mark as AI-generated if applicable
*/
const fetchExistingConclusion = async () => {
// Optimization: Check if conclusion data is already in request object
// Request detail response includes conclusionRemark and aiGeneratedConclusion fields
const existingConclusion = request?.conclusionRemark || request?.conclusion_remark;
const existingAiConclusion = request?.aiGeneratedConclusion || request?.ai_generated_conclusion;
if (existingConclusion || existingAiConclusion) {
// Use data from request object - no API call needed
setConclusionRemark(existingConclusion || existingAiConclusion);
setAiGenerated(!!existingAiConclusion);
return;
}
// Only fetch from API if not available in request object
// This handles cases where request object might not have been refreshed yet
try {
// Lazy load: Import conclusion API only when needed
const { getConclusion } = await import('@/services/conclusionApi');
// API Call: Fetch existing conclusion
// API Call: Fetch existing conclusion (returns null if not found)
const result = await getConclusion(request.requestId || requestIdentifier);
if (result && result.aiGeneratedRemark) {
if (result && (result.aiGeneratedRemark || result.finalRemark)) {
// Load: Set the AI-generated or final remark
setConclusionRemark(result.finalRemark || result.aiGeneratedRemark);
// Handle null values by providing empty string fallback
setConclusionRemark(result.finalRemark || result.aiGeneratedRemark || '');
setAiGenerated(!!result.aiGeneratedRemark);
}
} catch (err) {
// Only log non-404 errors (404 is handled gracefully in API)
if ((err as any)?.response?.status !== 404) {
console.error('[useConclusionRemark] Error fetching conclusion:', err);
}
// No conclusion yet - this is expected for newly approved requests
}
};
@ -218,16 +238,36 @@ export function useConclusionRemark(
};
/**
* Effect: Auto-fetch existing conclusion when request becomes approved or rejected
* Effect: Auto-load existing conclusion when request becomes approved, rejected, or closed
*
* Trigger: When request status changes to "approved" or "rejected" and user is initiator
* Trigger: When request status changes to "approved", "rejected", or "closed" and user is initiator
* Purpose: Load any conclusion generated by final approver (for approved) or AI (for rejected)
*
* Optimization:
* 1. First check if conclusion data is already in request object (no API call needed)
* 2. Only fetch from API if not available in request object
*/
useEffect(() => {
if ((request?.status === 'approved' || request?.status === 'rejected') && isInitiator && !conclusionRemark) {
const status = request?.status?.toLowerCase();
const shouldLoad = (status === 'approved' || status === 'rejected' || status === 'closed')
&& isInitiator
&& !conclusionRemark;
if (!shouldLoad) return;
// Check if conclusion data is already in request object
const existingConclusion = request?.conclusionRemark || request?.conclusion_remark;
const existingAiConclusion = request?.aiGeneratedConclusion || request?.ai_generated_conclusion;
if (existingConclusion || existingAiConclusion) {
// Use data from request object - no API call needed
setConclusionRemark(existingConclusion || existingAiConclusion);
setAiGenerated(!!existingAiConclusion);
} else {
// Only fetch from API if not available in request object
fetchExistingConclusion();
}
}, [request?.status, isInitiator]);
}, [request?.status, request?.conclusionRemark, request?.aiGeneratedConclusion, isInitiator, conclusionRemark]);
return {
conclusionRemark,

View File

@ -219,15 +219,18 @@ export function useRequestDetails(
: [];
/**
* Fetch: Get pause details if request is paused
* This is needed to show resume/retrigger buttons correctly
* Fetch: Get pause details only if request is actually paused
* Use request-level isPaused field from workflow response
*/
let pauseInfo = null;
const isPaused = (wf as any).isPaused || false;
if (isPaused) {
try {
pauseInfo = await getPauseDetails(wf.requestId);
} catch (error) {
// Pause info not available or request not paused - ignore
console.debug('Pause details not available:', error);
// Pause info not available - ignore
}
}
/**
@ -240,24 +243,9 @@ export function useRequestDetails(
if (wf.workflowType === 'CLAIM_MANAGEMENT' || wf.templateType === 'claim-management') {
try {
console.debug('[useRequestDetails] Fetching claim details for requestId:', wf.requestId);
const claimResponse = await apiClient.get(`/dealer-claims/${wf.requestId}`);
console.debug('[useRequestDetails] Claim API response:', {
status: claimResponse.status,
hasData: !!claimResponse.data,
dataKeys: claimResponse.data ? Object.keys(claimResponse.data) : [],
fullResponse: claimResponse.data,
});
const claimData = claimResponse.data?.data || claimResponse.data;
console.debug('[useRequestDetails] Extracted claimData:', {
hasClaimData: !!claimData,
claimDataKeys: claimData ? Object.keys(claimData) : [],
hasClaimDetails: !!(claimData?.claimDetails || claimData?.claim_details),
hasProposalDetails: !!(claimData?.proposalDetails || claimData?.proposal_details),
hasCompletionDetails: !!(claimData?.completionDetails || claimData?.completion_details),
hasInternalOrder: !!(claimData?.internalOrder || claimData?.internal_order),
});
if (claimData) {
claimDetails = claimData.claimDetails || claimData.claim_details;
@ -278,24 +266,7 @@ export function useRequestDetails(
(claimDetails as any).completionExpenses = completionExpenses;
}
console.debug('[useRequestDetails] Extracted details:', {
claimDetails: claimDetails ? {
hasActivityName: !!(claimDetails.activityName || claimDetails.activity_name),
hasActivityType: !!(claimDetails.activityType || claimDetails.activity_type),
hasLocation: !!(claimDetails.location),
activityName: claimDetails.activityName || claimDetails.activity_name,
activityType: claimDetails.activityType || claimDetails.activity_type,
location: claimDetails.location,
allKeys: Object.keys(claimDetails),
} : null,
hasProposalDetails: !!proposalDetails,
hasCompletionDetails: !!completionDetails,
hasInternalOrder: !!internalOrder,
hasBudgetTracking: !!budgetTracking,
hasInvoice: !!invoice,
hasCreditNote: !!creditNote,
hasCompletionExpenses: Array.isArray(completionExpenses) && completionExpenses.length > 0,
});
// Extracted details processed
} else {
console.warn('[useRequestDetails] No claimData found in response');
}
@ -528,13 +499,17 @@ export function useRequestDetails(
})
: [];
// Fetch pause details
// Fetch pause details only if request is actually paused
// Use request-level isPaused field from workflow response
let pauseInfo = null;
const isPaused = (wf as any).isPaused || false;
if (isPaused) {
try {
pauseInfo = await getPauseDetails(wf.requestId);
} catch (error) {
// Pause info not available or request not paused - ignore
console.debug('Pause details not available:', error);
// Pause info not available - ignore
}
}
/**
@ -547,13 +522,7 @@ export function useRequestDetails(
if (wf.workflowType === 'CLAIM_MANAGEMENT' || wf.templateType === 'claim-management') {
try {
console.debug('[useRequestDetails] Initial load - Fetching claim details for requestId:', wf.requestId);
const claimResponse = await apiClient.get(`/dealer-claims/${wf.requestId}`);
console.debug('[useRequestDetails] Initial load - Claim API response:', {
status: claimResponse.status,
hasData: !!claimResponse.data,
dataKeys: claimResponse.data ? Object.keys(claimResponse.data) : [],
});
const claimData = claimResponse.data?.data || claimResponse.data;
if (claimData) {
@ -575,17 +544,7 @@ export function useRequestDetails(
(claimDetails as any).completionExpenses = completionExpenses;
}
console.debug('[useRequestDetails] Initial load - Extracted details:', {
hasClaimDetails: !!claimDetails,
claimDetailsKeys: claimDetails ? Object.keys(claimDetails) : [],
hasProposalDetails: !!proposalDetails,
hasCompletionDetails: !!completionDetails,
hasInternalOrder: !!internalOrder,
hasBudgetTracking: !!budgetTracking,
hasInvoice: !!invoice,
hasCreditNote: !!creditNote,
hasCompletionExpenses: Array.isArray(completionExpenses) && completionExpenses.length > 0,
});
// Initial load - Extracted details processed
}
} catch (error: any) {
// Claim details not available - request might not be fully initialized yet

View File

@ -11,6 +11,7 @@ import { Pagination } from '@/components/common/Pagination';
import { getPriorityConfig, getStatusConfig, getSLAConfig } from '../utils/configMappers';
import { formatDate, formatDateTime } from '../utils/formatters';
import { formatHoursMinutes } from '@/utils/slaTracker';
import { navigateToRequest } from '@/utils/requestNavigation';
import type { ApproverPerformanceRequest } from '../types/approverPerformance.types';
interface ApproverPerformanceRequestListProps {
@ -69,7 +70,6 @@ export function ApproverPerformanceRequestList({
key={request.requestId}
className="hover:shadow-md transition-shadow cursor-pointer"
onClick={() => {
const { navigateToRequest } = require('@/utils/requestNavigation');
navigateToRequest({
requestId: request.requestId,
requestTitle: request.title,
@ -166,7 +166,6 @@ export function ApproverPerformanceRequestList({
size="sm"
onClick={(e) => {
e.stopPropagation();
const { navigateToRequest } = require('@/utils/requestNavigation');
navigateToRequest({
requestId: request.requestId,
requestTitle: request.title,

View File

@ -1,14 +1,28 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { useAuth } from '@/contexts/AuthContext';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader } from '@/components/ui/card';
import { LogIn, Shield } from 'lucide-react';
import { ReLogo } from '@/assets';
import { ReLogo, LandingPageImage } from '@/assets';
import { initiateTanflowLogin } from '@/services/tanflowAuth';
export function Auth() {
const { login, isLoading, error } = useAuth();
const [tanflowLoading, setTanflowLoading] = useState(false);
const [imageLoaded, setImageLoaded] = useState(false);
// Preload the background image
useEffect(() => {
const img = new Image();
img.src = LandingPageImage;
img.onload = () => {
setImageLoaded(true);
};
// If image is already cached, trigger load immediately
if (img.complete) {
setImageLoaded(true);
}
}, []);
const handleOKTALogin = async () => {
// Clear any existing session data
@ -51,8 +65,24 @@ export function Auth() {
}
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-slate-50 to-slate-100 p-4">
<Card className="w-full max-w-md shadow-xl">
<div
className="min-h-screen flex items-center justify-center p-4 relative"
style={{
backgroundImage: imageLoaded ? `url(${LandingPageImage})` : 'none',
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
transition: 'background-image 0.3s ease-in-out'
}}
>
{/* Fallback background while image loads */}
{!imageLoaded && (
<div className="absolute inset-0 bg-gradient-to-br from-slate-900 to-slate-800"></div>
)}
{/* Overlay for better readability */}
<div className="absolute inset-0 bg-black/40"></div>
<Card className="w-full max-w-md shadow-xl relative z-10 bg-black backdrop-blur-sm border-gray-800">
<CardHeader className="space-y-1 text-center pb-6">
<div className="flex flex-col items-center justify-center mb-4">
<img
@ -60,13 +90,13 @@ export function Auth() {
alt="Royal Enfield Logo"
className="h-10 w-auto max-w-[168px] object-contain mb-2"
/>
<p className="text-xs text-gray-400 text-center truncate">Approval Portal</p>
<p className="text-xs text-gray-300 text-center truncate">Approval Portal</p>
</div>
</CardHeader>
<CardContent className="space-y-4">
{error && (
<div className="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded-lg">
<div className="bg-red-900/50 border border-red-700 text-red-200 px-4 py-3 rounded-lg">
<p className="text-sm font-medium">Authentication Error</p>
<p className="text-sm">{error.message}</p>
</div>
@ -96,10 +126,10 @@ export function Auth() {
<div className="relative">
<div className="absolute inset-0 flex items-center">
<span className="w-full border-t border-gray-300"></span>
<span className="w-full border-t border-gray-700"></span>
</div>
<div className="relative flex justify-center text-xs uppercase">
<span className="bg-white px-2 text-gray-500">Or</span>
<span className="bg-gray-900 px-2 text-gray-400">Or</span>
</div>
</div>
@ -125,9 +155,9 @@ export function Auth() {
</Button>
</div>
<div className="text-center text-sm text-gray-500 mt-4">
<div className="text-center text-sm text-gray-400 mt-4">
<p>Secure Single Sign-On</p>
<p className="text-xs mt-1">Choose your authentication provider</p>
<p className="text-xs mt-1 text-gray-500">Choose your authentication provider</p>
</div>
</CardContent>
</Card>

View File

@ -1,5 +1,6 @@
import { useState, useCallback } from 'react';
import { useNavigate } from 'react-router-dom';
import { navigateToRequest } from '@/utils/requestNavigation';
// Components
import { DetailedReportsHeader } from './components/DetailedReportsHeader';
@ -69,7 +70,6 @@ export function DetailedReports({ onBack }: DetailedReportsProps) {
}, [onBack, navigate]);
const handleViewRequest = useCallback((requestId: string) => {
const { navigateToRequest } = require('@/utils/requestNavigation');
navigateToRequest({
requestId,
navigate,

View File

@ -42,11 +42,15 @@ export function Notifications({ onNavigate }: NotificationsProps) {
const result = await notificationApi.list({ page, limit: ITEMS_PER_PAGE, unreadOnly });
const notifs = result.data?.notifications || [];
const total = result.data?.total || 0;
// Extract pagination data from the response
const pagination = result.data?.pagination || {};
const total = pagination.total || 0;
const totalPages = pagination.totalPages || 1;
setNotifications(notifs);
setTotalCount(total);
setTotalPages(Math.ceil(total / ITEMS_PER_PAGE));
setTotalPages(totalPages);
setCurrentPage(page); // Update current page to match what was fetched
} catch (error) {
console.error('[Notifications] Failed to fetch:', error);
} finally {
@ -56,6 +60,7 @@ export function Notifications({ onNavigate }: NotificationsProps) {
};
useEffect(() => {
setCurrentPage(1); // Reset to page 1 when filter changes
fetchNotifications(1, filter === 'unread');
}, [filter]);
@ -82,6 +87,11 @@ export function Notifications({ onNavigate }: NotificationsProps) {
navigationUrl += '?tab=worknotes';
}
// Document added notifications should open Documents tab
if (notification.notificationType === 'document_added') {
navigationUrl += '?tab=documents';
}
onNavigate(navigationUrl);
}
}
@ -131,6 +141,8 @@ export function Notifications({ onNavigate }: NotificationsProps) {
return <MessageSquare className={`${iconClass} text-blue-600`} />;
case 'worknote':
return <FileText className={`${iconClass} text-purple-600`} />;
case 'document_added':
return <FileText className={`${iconClass} text-teal-600`} />;
case 'assignment':
return <UserPlus className={`${iconClass} text-indigo-600`} />;
case 'approval':

View File

@ -57,9 +57,19 @@ export async function finalizeConclusion(requestId: string, finalRemark: string)
/**
* Get conclusion for a request
* Returns null if conclusion doesn't exist (404) instead of throwing error
*/
export async function getConclusion(requestId: string): Promise<ConclusionRemark> {
export async function getConclusion(requestId: string): Promise<ConclusionRemark | null> {
try {
const response = await apiClient.get(`/conclusions/${requestId}`);
return response.data.data;
} catch (error: any) {
// Handle 404 gracefully - conclusion doesn't exist yet, which is normal
if (error.response?.status === 404) {
return null;
}
// Re-throw other errors
throw error;
}
}

View File

@ -115,18 +115,7 @@ export function mapToClaimManagementRequest(
const creditNote = apiRequest.creditNote || apiRequest.credit_note || {};
const completionExpenses = apiRequest.completionExpenses || apiRequest.completion_expenses || [];
// Debug: Log raw claim details to help troubleshoot
console.debug('[claimDataMapper] Raw claimDetails:', claimDetails);
console.debug('[claimDataMapper] Raw apiRequest:', {
hasClaimDetails: !!apiRequest.claimDetails,
hasProposalDetails: !!apiRequest.proposalDetails,
hasCompletionDetails: !!apiRequest.completionDetails,
hasBudgetTracking: !!budgetTracking,
hasInvoice: !!invoice,
hasCreditNote: !!creditNote,
hasCompletionExpenses: Array.isArray(completionExpenses) && completionExpenses.length > 0,
workflowType: apiRequest.workflowType,
});
// Raw claim details processed
// Map activity information (matching ActivityInformationCard expectations)
// Handle both camelCase and snake_case field names from Sequelize
@ -137,14 +126,7 @@ export function mapToClaimManagementRequest(
const activityType = claimDetails.activityType || claimDetails.activity_type || '';
const location = claimDetails.location || '';
console.debug('[claimDataMapper] Mapped activity fields:', {
activityName,
activityType,
location,
hasActivityName: !!activityName,
hasActivityType: !!activityType,
hasLocation: !!location,
});
// Activity fields mapped
// Get budget values from budgetTracking table (new source of truth)
const estimatedBudget = budgetTracking.proposalEstimatedBudget ||