ui changes to made to show the claim amount and rlated invoice info

This commit is contained in:
laxman h 2026-03-26 14:35:38 +05:30
parent aa2f2b88a3
commit cd329f292a
5 changed files with 191 additions and 105 deletions

View File

@ -2137,8 +2137,9 @@ export function DealerClaimWorkflowTab({
const isDeptLeadStep =
(stepLevelName && stepLevelName.toLowerCase().includes('department lead')) ||
(step.title && step.title.toLowerCase().includes('department lead'));
// Only show when step is approved and has IO details
return isDeptLeadStep && step.status === 'approved' && step.ioDetails && step.ioDetails.ioNumber && (
// Only show when step is approved and has IO details.
// Hide this section for dealer users.
return isDeptLeadStep && !isDealer && step.status === 'approved' && step.ioDetails && step.ioDetails.ioNumber && (
<div className="mt-3 p-3 bg-blue-50 rounded-lg border border-blue-200">
<div className="flex items-center gap-2 mb-2">
<Receipt className="w-4 h-4 text-blue-600" />

View File

@ -23,6 +23,8 @@ interface IODetails {
interface DMSDetails {
dmsNumber?: string;
eInvoiceNumber?: string;
eInvoiceDate?: string;
remarks?: string;
createdByName?: string;
createdAt?: string;
@ -66,7 +68,6 @@ interface ProcessDetailsCardProps {
}
export function ProcessDetailsCard({
ioDetails,
dmsDetails,
claimAmount,
estimatedBudgetBreakdown,
@ -96,9 +97,18 @@ export function ProcessDetailsCard({
return items.reduce((sum, item) => sum + (item.totalAmt ?? (item.amount + (item.gstAmt ?? 0))), 0);
};
const hasInvoiceDetails = Boolean(
dmsDetails?.eInvoiceNumber ||
dmsDetails?.ackNo ||
dmsDetails?.eInvoiceDate ||
dmsDetails?.dmsNumber ||
dmsDetails?.ackDate ||
dmsDetails?.irn ||
dmsDetails?.signedInvoiceUrl
);
// Don't render if nothing to show
const hasContent =
(visibility.showIODetails && ioDetails) ||
(visibility.showDMSDetails && dmsDetails) ||
(dmsDetails?.creditNoteWfmData && dmsDetails.creditNoteWfmData.length > 0) ||
(visibility.showClaimAmount && claimAmount && claimAmount.amount !== undefined && claimAmount.amount !== null) ||
@ -119,63 +129,8 @@ export function ProcessDetailsCard({
<CardDescription>Workflow reference numbers</CardDescription>
</CardHeader>
<CardContent className="space-y-3">
{/* IO Details - Only visible to internal RE users */}
{visibility.showIODetails && ioDetails && (
<div className="bg-white rounded-lg p-3 border border-blue-200">
<div className="flex items-center gap-2 mb-2">
<Receipt className="w-4 h-4 text-blue-600" />
<Label className="text-xs font-semibold text-blue-900 uppercase tracking-wide">
IO Number
</Label>
</div>
<p className="font-bold text-gray-900 mb-2">{ioDetails.ioNumber}</p>
{ioDetails.remarks && (
<div className="pt-2 border-t border-blue-100">
<p className="text-xs text-gray-600 mb-1">Remark:</p>
<p className="text-xs text-gray-900">{ioDetails.remarks}</p>
</div>
)}
{/* Budget Details */}
{(ioDetails.availableBalance !== undefined || ioDetails.blockedAmount !== undefined) && (
<div className="pt-2 border-t border-blue-100 mt-2 space-y-1">
{ioDetails.availableBalance !== undefined && (
<div className="flex justify-between text-xs">
<span className="text-gray-600">Available Balance:</span>
<span className="font-medium text-gray-900">
{formatCurrency(ioDetails.availableBalance)}
</span>
</div>
)}
{ioDetails.blockedAmount !== undefined && (
<div className="flex justify-between text-xs">
<span className="text-gray-600">Blocked Amount:</span>
<span className="font-medium text-blue-700">
{formatCurrency(ioDetails.blockedAmount)}
</span>
</div>
)}
{ioDetails.remainingBalance !== undefined && (
<div className="flex justify-between text-xs">
<span className="text-gray-600">Remaining Balance:</span>
<span className="font-medium text-green-700">
{formatCurrency(ioDetails.remainingBalance)}
</span>
</div>
)}
</div>
)}
<div className="pt-2 border-t border-blue-100 mt-2">
<p className="text-xs text-gray-500">By {ioDetails.blockedByName}</p>
<p className="text-xs text-gray-500">{formatDate(ioDetails.blockedAt)}</p>
</div>
</div>
)}
{/* E-Invoice Details */}
{visibility.showDMSDetails && dmsDetails && (
{visibility.showDMSDetails && dmsDetails && hasInvoiceDetails && (
<div className="bg-white rounded-lg p-3 border border-purple-200">
<div className="flex items-center gap-2 mb-2">
<Activity className="w-4 h-4 text-purple-600" />
@ -185,11 +140,34 @@ export function ProcessDetailsCard({
</div>
<div className="grid grid-cols-2 gap-3 mb-2">
{dmsDetails.ackNo && (
{(dmsDetails.eInvoiceNumber || dmsDetails.ackNo) && (
<div>
<p className="text-[10px] text-gray-500 uppercase">Invoice Number</p>
<p className="font-bold text-sm text-purple-700">{dmsDetails.eInvoiceNumber || dmsDetails.ackNo }</p>
</div>
)}
{dmsDetails.eInvoiceDate && (
<div>
<p className="text-[10px] text-gray-500 uppercase">Invoice Date</p>
<p className="font-semibold text-xs text-gray-700">{formatDate(dmsDetails.eInvoiceDate)}</p>
</div>
)}
{dmsDetails.dmsNumber && (
<div>
<p className="text-[10px] text-gray-500 uppercase">DMS Number</p>
<p className="font-semibold text-xs text-gray-700">{dmsDetails.dmsNumber}</p>
</div>
)}
{dmsDetails.ackDate && (
<div>
<p className="text-[10px] text-gray-500 uppercase">Ack Date</p>
<p className="font-semibold text-xs text-gray-700">{formatDate(dmsDetails.ackDate)}</p>
</div>
)}
{dmsDetails.ackNo && dmsDetails.eInvoiceNumber && (
<div>
<p className="text-[10px] text-gray-500 uppercase">Ack No</p>
<p className="font-bold text-sm text-purple-700">{dmsDetails.ackNo}</p>
<p className="font-semibold text-xs text-gray-700">{dmsDetails.ackNo}</p>
</div>
)}
</div>
@ -223,32 +201,69 @@ export function ProcessDetailsCard({
)}
<div className="pt-2 border-t border-purple-100 mt-2">
<p className="text-[10px] text-gray-500">By {dmsDetails.createdByName}</p>
<p className="text-[10px] text-gray-500">By {dmsDetails.createdByName || 'System'}</p>
<p className="text-[10px] text-gray-500">{formatDate(dmsDetails.createdAt)}</p>
</div>
</div>
)}
{/* Credit Note Validation */}
{/* Credit Note Details */}
{dmsDetails?.creditNoteWfmData && dmsDetails.creditNoteWfmData.length > 0 && (
<div className="bg-white rounded-lg p-3 border border-pink-200">
<div className="flex items-center gap-2 mb-2">
<Receipt className="w-4 h-4 text-pink-600" />
<Label className="text-xs font-semibold text-pink-900 uppercase tracking-wide">
Credit Note Validation
Credit Note Details
</Label>
</div>
<div className="space-y-2">
{dmsDetails.creditNoteWfmData.map((cn: any, idx: number) => (
<div key={idx} className="bg-pink-50 p-2 rounded border border-pink-100 text-[10px]">
<div className="flex justify-between mb-1 text-pink-900">
<span className="font-semibold break-words flex-1 pr-2">Credit Note No: {cn.DOC_NO || 'N/A'}</span>
<div className="flex justify-between mb-1.5 text-pink-900">
<span className="font-semibold break-words flex-1 pr-2 uppercase">CN No: {cn.DOC_NO || 'N/A'}</span>
<span className={`px-1.5 py-0.5 rounded-sm text-white font-medium self-start whitespace-nowrap ${cn.MSG_TYP === 'E' ? 'bg-red-500' : 'bg-green-500'}`}>
{cn.MSG_TYP === 'E' ? 'Error' : (cn.MSG_TYP === 'S' ? 'Success' : cn.MSG_TYP || 'Unknown')}
{cn.MSG_TYP === 'E' ? 'Error' : (cn.MSG_TYP === 'S' ? 'Success' : cn.MSG_TYP || 'Processed')}
</span>
</div>
<div className="text-gray-500 mb-0.5">Txn No: {cn.TRNS_UNIQ_NO || 'N/A'}</div>
<div className="text-gray-700 break-words font-medium">{cn.MESSAGE || 'No Message'}</div>
<div className="grid grid-cols-2 gap-x-4 gap-y-1 mb-1.5 text-gray-600">
<div className="flex justify-between">
<span>Txn No:</span>
<span className="font-medium">{cn.TRNS_UNIQ_NO || 'N/A'}</span>
</div>
{cn.TDS_AMT !== undefined &&
cn.TDS_AMT !== null &&
cn.TDS_AMT !== '' &&
(Number.isNaN(Number(cn.TDS_AMT)) || Number(cn.TDS_AMT) !== 0) && (
<div className="flex justify-between">
<span>TDS:</span>
<span className="font-medium text-red-600">
{Number.isNaN(Number(cn.TDS_AMT))
? String(cn.TDS_AMT)
: formatCurrency(Number(cn.TDS_AMT))}
</span>
</div>
)}
{cn.CREDITED_TOTAL_AMT !== undefined &&
cn.CREDITED_TOTAL_AMT !== null &&
cn.CREDITED_TOTAL_AMT !== '' &&
(Number.isNaN(Number(cn.CREDITED_TOTAL_AMT)) || Number(cn.CREDITED_TOTAL_AMT) !== 0) && (
<div className="flex justify-between col-span-2 pt-1 border-t border-pink-100">
<span className="font-semibold">Credit Amount:</span>
<span className="font-bold text-pink-700">
{Number.isNaN(Number(cn.CREDITED_TOTAL_AMT))
? String(cn.CREDITED_TOTAL_AMT)
: formatCurrency(Number(cn.CREDITED_TOTAL_AMT))}
</span>
</div>
)}
</div>
{cn.MESSAGE && (
<div className="text-gray-700 italic border-l-2 border-pink-200 pl-1.5 mt-1 leading-tight">
{cn.MESSAGE}
</div>
)}
</div>
))}
</div>

View File

@ -303,6 +303,7 @@ export function useRequestDetails(
id: wf.requestNumber || wf.requestId,
requestId: wf.requestId, // UUID for API calls
requestNumber: wf.requestNumber, // Human-readable number for display
initiatorId: wf.initiatorId || wf.initiator_id,
title: wf.title,
description: wf.description,
status: statusMap(wf.status),
@ -318,6 +319,7 @@ export function useRequestDetails(
// Backend provides full SLA in summary.sla with all required fields
sla: summary?.sla || wf.sla || null,
initiator: {
userId: wf.initiator?.userId || wf.initiator?.user_id || wf.initiatorId || wf.initiator_id,
name: wf.initiator?.displayName || wf.initiator?.email,
role: wf.initiator?.designation || undefined,
department: wf.initiator?.department || undefined,

View File

@ -231,7 +231,25 @@ export function mapToClaimManagementRequest(
// Map proposal details
const expectedCompletionDate = proposalDetails?.expectedCompletionDate || proposalDetails?.expected_completion_date;
const proposal = proposalDetails ? {
const hasProposalData = Boolean(
(proposalDetails && Object.keys(proposalDetails).length > 0) &&
(
(Array.isArray(proposalDetails.costItems || proposalDetails.cost_items) &&
(proposalDetails.costItems || proposalDetails.cost_items).length > 0) ||
(Array.isArray(proposalDetails.costBreakup || proposalDetails.cost_breakup) &&
(proposalDetails.costBreakup || proposalDetails.cost_breakup).length > 0) ||
proposalDetails.proposalDocumentUrl ||
proposalDetails.proposal_document_url ||
proposalDetails.totalEstimatedBudget ||
proposalDetails.total_estimated_budget ||
proposalDetails.expectedCompletionDate ||
proposalDetails.expected_completion_date ||
proposalDetails.submittedAt ||
proposalDetails.submitted_at
)
);
const proposal = hasProposalData ? {
proposalDocumentUrl: proposalDetails.proposalDocumentUrl || proposalDetails.proposal_document_url,
costBreakup: Array.isArray(proposalDetails.costItems || proposalDetails.cost_items)
? (proposalDetails.costItems || proposalDetails.cost_items).map((item: any) => ({
@ -259,9 +277,10 @@ export function mapToClaimManagementRequest(
totalEstimatedBudget: proposalDetails.totalEstimatedBudget || proposalDetails.total_estimated_budget || 0,
timelineMode: proposalDetails.timelineMode || proposalDetails.timeline_mode,
expectedCompletionDate: expectedCompletionDate,
timelineForClosure: expectedCompletionDate,
expectedCompletionDays: proposalDetails.expectedCompletionDays || proposalDetails.expected_completion_days,
timelineForClosure: expectedCompletionDate, // Map expectedCompletionDate to timelineForClosure for ProposalDetailsCard
dealerComments: proposalDetails.dealerComments || proposalDetails.dealer_comments,
submittedAt: proposalDetails.submittedAt || proposalDetails.submitted_at || proposalDetails.submittedOn,
submittedOn: proposalDetails.submittedAt || proposalDetails.submitted_at || proposalDetails.submittedOn,
} : undefined;
@ -277,29 +296,47 @@ export function mapToClaimManagementRequest(
};
// Map DMS details from new invoice and credit note tables
// Invoice could be top-level or nested in some backend responses
const inv = apiRequest.invoice || apiRequest.claimInvoice || apiRequest.claim_invoice ||
apiRequest.claimRequest?.invoice || apiRequest.request?.invoice || invoice || {};
const claimDetailsObj = claimDetails || {};
const dmsDetails = {
eInvoiceNumber: invoice.invoiceNumber || invoice.invoice_number ||
claimDetails.eInvoiceNumber || claimDetails.e_invoice_number,
eInvoiceDate: invoice.invoiceDate || invoice.invoice_date ||
claimDetails.eInvoiceDate || claimDetails.e_invoice_date,
dmsNumber: invoice.dmsNumber || invoice.dms_number ||
claimDetails.dmsNumber || claimDetails.dms_number,
eInvoiceNumber: inv.invoiceNumber || inv.invoice_number ||
claimDetailsObj.eInvoiceNumber || claimDetailsObj.e_invoice_number ||
inv.dmsNumber || inv.dms_number,
eInvoiceDate: inv.invoiceDate || inv.invoice_date ||
claimDetailsObj.eInvoiceDate || claimDetailsObj.e_invoice_date,
dmsNumber: inv.dmsNumber || inv.dms_number ||
claimDetailsObj.dmsNumber || claimDetailsObj.dms_number,
creditNoteNumber: creditNote.creditNoteNumber || creditNote.credit_note_number ||
claimDetails.creditNoteNumber || claimDetails.credit_note_number,
claimDetailsObj.creditNoteNumber || claimDetailsObj.credit_note_number,
creditNoteDate: creditNote.creditNoteDate || creditNote.credit_note_date ||
claimDetails.creditNoteDate || claimDetails.credit_note_date,
creditNoteAmount: creditNote.creditNoteAmount ? Number(creditNote.creditNoteAmount) :
(creditNote.credit_note_amount ? Number(creditNote.credit_note_amount) :
(creditNote.creditNoteAmount ? Number(creditNote.creditNoteAmount) :
(claimDetails.creditNoteAmount ? Number(claimDetails.creditNoteAmount) :
(claimDetails.credit_note_amount ? Number(claimDetails.credit_note_amount) : undefined)))),
claimDetailsObj.creditNoteDate || claimDetailsObj.credit_note_date,
creditNoteAmount:
creditNote.creditNoteAmount !== undefined && creditNote.creditNoteAmount !== null
? Number(creditNote.creditNoteAmount)
: (creditNote.credit_note_amount !== undefined && creditNote.credit_note_amount !== null
? Number(creditNote.credit_note_amount)
: (claimDetailsObj.creditNoteAmount !== undefined && claimDetailsObj.creditNoteAmount !== null
? Number(claimDetailsObj.creditNoteAmount)
: (claimDetailsObj.credit_note_amount !== undefined && claimDetailsObj.credit_note_amount !== null
? Number(claimDetailsObj.credit_note_amount)
: undefined))),
// PWC fields
irn: invoice.irn || claimDetails.irn,
ackNo: invoice.ackNo || claimDetails.ackNo,
ackDate: invoice.ackDate || claimDetails.ackDate,
signedInvoiceUrl: invoice.signedInvoiceUrl || claimDetails.signedInvoiceUrl,
taxBreakdown: invoice.taxBreakdown || claimDetails.taxBreakdown,
creditNoteWfmData: apiRequest.creditNoteWfmData || claimDetails.creditNoteWfmData || [],
irn: inv.irn || claimDetailsObj.irn,
ackNo: inv.ackNo || inv.ack_no || claimDetailsObj.ackNo || claimDetailsObj.ack_no,
ackDate: inv.ackDate || inv.ack_date || claimDetailsObj.ackDate || claimDetailsObj.ack_date,
signedInvoiceUrl: inv.signedInvoiceUrl || inv.signed_invoice_url || claimDetailsObj.signedInvoiceUrl || claimDetailsObj.signed_invoice_url,
taxBreakdown: inv.taxBreakdown || claimDetailsObj.taxBreakdown,
creditNoteWfmData: (apiRequest.creditNote?.items || apiRequest.creditNoteWfmData || claimDetails.creditNoteWfmData || []).map((item: any) => ({
...item,
DOC_NO: item.creditNoteNumber || apiRequest.creditNote?.creditNoteNumber || item.DOC_NO,
TRNS_UNIQ_NO: item.transactionNo || item.TRNS_UNIQ_NO,
MESSAGE: item.description || item.MESSAGE,
TDS_AMT: item.tdsAmount || item.TDS_AMT,
CREDITED_TOTAL_AMT: item.creditAmount || item.amount || item.CREDITED_TOTAL_AMT,
})),
};
// Map claim amounts
@ -327,18 +364,43 @@ export function mapToClaimManagementRequest(
*/
export function determineUserRole(apiRequest: any, currentUserId: string): RequestRole {
try {
const normalize = (v: any) => (v || '').toString().trim().toLowerCase();
const participants = Array.isArray(apiRequest.participants) ? apiRequest.participants : [];
const currentParticipant = participants.find((p: any) =>
(p.userId === currentUserId || p.user_id === currentUserId || p.user?.userId === currentUserId)
);
const currentUserEmail = normalize(
currentParticipant?.userEmail ||
currentParticipant?.user_email ||
currentParticipant?.email ||
currentParticipant?.user?.email ||
(apiRequest.initiatorId === currentUserId ? apiRequest.initiator?.email : '')
);
const dealerEmail = normalize(
apiRequest?.claimDetails?.dealerEmail ||
apiRequest?.claimDetails?.dealer_email ||
apiRequest?.dealerEmail ||
apiRequest?.dealer?.email
);
// Dealer detection first: if current user's email matches dealer email, treat as DEALER.
if (dealerEmail && currentUserEmail && currentUserEmail === dealerEmail) {
return 'DEALER';
}
// Check if user is the initiator
if (apiRequest.initiatorId === currentUserId ||
apiRequest.initiator_id === currentUserId ||
apiRequest.initiator?.userId === currentUserId ||
apiRequest.initiator?.user_id === currentUserId ||
apiRequest.requestedBy?.userId === currentUserId) {
return 'INITIATOR';
}
// Check if user is a dealer (participant with DEALER type)
const participants = apiRequest.participants || [];
const dealerParticipant = participants.find((p: any) =>
(p.userId === currentUserId || p.user?.userId === currentUserId) &&
(p.participantType === 'DEALER' || p.type === 'DEALER')
(p.userId === currentUserId || p.user_id === currentUserId || p.user?.userId === currentUserId || p.user?.user_id === currentUserId) &&
((p.participantType || p.participant_type || p.type || '').toString().toUpperCase() === 'DEALER')
);
if (dealerParticipant) {
return 'DEALER';
@ -391,7 +453,7 @@ export function getRoleBasedVisibility(role: RequestRole): RoleVisibility {
showDealerInfo: true,
showProposalDetails: true,
showIODetails: false,
showDMSDetails: false,
showDMSDetails: true,
showClaimAmount: true,
canEditClaimAmount: false,
};
@ -419,10 +481,10 @@ export function getRoleBasedVisibility(role: RequestRole): RoleVisibility {
case 'SPECTATOR':
default:
return {
showDealerInfo: false,
showProposalDetails: false,
showDealerInfo: true,
showProposalDetails: true,
showIODetails: false,
showDMSDetails: false,
showDMSDetails: true,
showClaimAmount: false,
canEditClaimAmount: false,
};

View File

@ -10,18 +10,21 @@
export function isClaimManagementRequest(request: any): boolean {
if (!request) return false;
// Handle case where request is the full response object { request, claimDetails, ... }
const req = request.request || request;
// New format: Check workflowType
if (request.workflowType === 'CLAIM_MANAGEMENT') {
if (req.workflowType === 'CLAIM_MANAGEMENT') {
return true;
}
// Old format: Check templateType (for backward compatibility)
if (request.templateType === 'claim-management' || request.template === 'claim-management') {
if (req.templateType === 'claim-management' || req.template === 'claim-management') {
return true;
}
// Check template name/code
if (request.templateName === 'Claim Management' || request.templateCode === 'CLAIM_MANAGEMENT') {
if (req.templateName === 'Claim Management' || req.templateCode === 'CLAIM_MANAGEMENT') {
return true;
}
@ -35,13 +38,16 @@ export function isClaimManagementRequest(request: any): boolean {
export function getWorkflowType(request: any): string {
if (!request) return 'NON_TEMPLATIZED';
// Handle wrapped response
const req = request.request || request;
// New format
if (request.workflowType) {
return request.workflowType;
if (req.workflowType) {
return req.workflowType;
}
// Old format: Map templateType to workflowType
if (request.templateType === 'claim-management' || request.template === 'claim-management') {
if (req.templateType === 'claim-management' || req.template === 'claim-management') {
return 'CLAIM_MANAGEMENT';
}