wfm file push indicator re-added in workflow step

This commit is contained in:
laxmanhalaki 2026-03-11 12:31:04 +05:30
parent a4662c99f3
commit 8940a8981e
6 changed files with 84 additions and 35 deletions

View File

@ -658,6 +658,7 @@ function CustomRequestDetailInner({ requestId: propRequestId, onBack, dynamicReq
pausedByUserId={request?.pauseInfo?.pausedBy?.userId} pausedByUserId={request?.pauseInfo?.pausedBy?.userId}
currentUserId={(user as any)?.userId} currentUserId={(user as any)?.userId}
apiRequest={apiRequest} apiRequest={apiRequest}
hideApproveReject={isDealer}
/> />
</div> </div>
)} )}

View File

@ -10,7 +10,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
import { Badge } from '@/components/ui/badge'; import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button'; import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress'; import { Progress } from '@/components/ui/progress';
import { TrendingUp, Clock, CheckCircle, CircleCheckBig, Upload, Mail, Download, Receipt, Activity, AlertTriangle, AlertOctagon, XCircle, History, ChevronDown, ChevronUp, RefreshCw, RotateCw, Eye, FileSpreadsheet, X, Loader2 } from 'lucide-react'; import { TrendingUp, Clock, CheckCircle, CheckCircle2, CircleCheckBig, Upload, Mail, Download, Receipt, Activity, AlertTriangle, AlertOctagon, XCircle, History, ChevronDown, ChevronUp, RefreshCw, RotateCw, Eye, FileSpreadsheet, X, Loader2 } from 'lucide-react';
import { formatDateTime, formatDateDDMMYYYY } from '@/utils/dateFormatter'; import { formatDateTime, formatDateDDMMYYYY } from '@/utils/dateFormatter';
import { formatHoursMinutes } from '@/utils/slaTracker'; import { formatHoursMinutes } from '@/utils/slaTracker';
import { import {
@ -26,7 +26,7 @@ import {
// InitiatorActionModal - Removed, using direct buttons instead // InitiatorActionModal - Removed, using direct buttons instead
} from './modals'; } from './modals';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { submitProposal, updateIODetails, submitCompletion, updateEInvoice, sendCreditNoteToDealer } from '@/services/dealerClaimApi'; import { submitProposal, updateIODetails, submitCompletion, updateEInvoice, sendCreditNoteToDealer, retriggerWFMPush } from '@/services/dealerClaimApi';
import { getWorkflowDetails, approveLevel, rejectLevel, handleInitiatorAction, getWorkflowHistory } from '@/services/workflowApi'; import { getWorkflowDetails, approveLevel, rejectLevel, handleInitiatorAction, getWorkflowHistory } from '@/services/workflowApi';
import { uploadDocument } from '@/services/documentApi'; import { uploadDocument } from '@/services/documentApi';
import { TokenManager } from '@/utils/tokenManager'; import { TokenManager } from '@/utils/tokenManager';
@ -1510,6 +1510,25 @@ export function DealerClaimWorkflowTab({
loadCompletionDocuments(); loadCompletionDocuments();
}, [request]); }, [request]);
const handleRetrigger = async () => {
try {
toast.loading('Retriggering WFM push...', { id: 'wfm-retrigger' });
await retriggerWFMPush(request.id);
toast.success('WFM push re-triggered successfully', { id: 'wfm-retrigger' });
// Refresh the request data if onRefresh is provided
if (onRefresh) {
onRefresh();
} else {
// Fallback or full page refresh
window.location.reload();
}
} catch (error: any) {
console.error('Error retriggering WFM push:', error);
toast.error(error.message || 'Failed to re-trigger WFM push', { id: 'wfm-retrigger' });
}
};
const handleDownloadCSV = async () => { const handleDownloadCSV = async () => {
try { try {
const requestId = request.id || request.requestId; const requestId = request.id || request.requestId;
@ -1764,21 +1783,61 @@ export function DealerClaimWorkflowTab({
</Button> </Button>
); );
})()} })()}
{/* CSV Export Button (Requestor Claim Approval) */} {/* CSV Export & WFM Push Status (Requestor Claim Approval) */}
{(() => { {(() => {
const isRequestorClaimStep = (step.levelName || step.title || '').toLowerCase().includes('requestor claim') || const isRequestorClaimStep = (step.levelName || step.title || '').toLowerCase().includes('requestor claim') ||
(step.levelName || step.title || '').toLowerCase().includes('requestor - claim'); (step.levelName || step.title || '').toLowerCase().includes('requestor - claim');
const hasInvoice = request?.invoice || (request?.irn && step.status === 'approved'); const hasInvoice = request?.invoice || (request?.irn && step.status === 'approved');
const wfmStatus = request?.invoice?.wfmPushStatus || (request?.invoice as any)?.wfmPushStatus;
const wfmError = request?.invoice?.wfmPushError || (request?.invoice as any)?.wfmPushError;
return isRequestorClaimStep && hasInvoice && ( return isRequestorClaimStep && hasInvoice && (
<div className="flex items-center gap-1 ml-1">
<Button <Button
variant="ghost" variant="ghost"
size="sm" size="sm"
className="h-6 w-6 p-0 hover:bg-emerald-100 ml-1" className="h-6 w-6 p-0 hover:bg-emerald-100"
title="Export CSV" title="Export CSV"
onClick={handleDownloadCSV} onClick={handleDownloadCSV}
> >
<FileSpreadsheet className="w-3.5 h-3.5 text-emerald-600" /> <FileSpreadsheet className="w-3.5 h-3.5 text-emerald-600" />
</Button> </Button>
{/* WFM Push Status Indication */}
{wfmStatus === 'SUCCESS' ? (
<div title="Pushed to WFM successfully">
<CheckCircle2 className="w-3.5 h-3.5 text-green-500" />
</div>
) : wfmStatus === 'FAILED' ? (
<div className="flex items-center gap-1">
<div title={`WFM Push Failed: ${wfmError || 'Unknown error'}`}>
<XCircle className="w-3.5 h-3.5 text-red-500" />
</div>
<Button
variant="ghost"
size="sm"
className="h-5 px-1.5 text-[10px] text-red-600 hover:text-red-700 hover:bg-red-50 border border-red-200 h-auto py-0"
onClick={handleRetrigger}
>
Retry Push
</Button>
</div>
) : (
<div className="flex items-center gap-1">
<div title="WFM Push Pending">
<Clock className="w-3.5 h-3.5 text-amber-500" />
</div>
<Button
variant="ghost"
size="sm"
className="h-5 px-1.5 text-[10px] text-amber-600 hover:text-amber-700 hover:bg-amber-50 border border-amber-200 h-auto py-0"
onClick={handleRetrigger}
>
Push Now
</Button>
</div>
)}
</div>
); );
})()} })()}
</div> </div>

View File

@ -230,13 +230,6 @@ export function DealerDashboard({ onNavigate, onNewRequest: _onNewRequest }: Das
</div> </div>
</div> </div>
<div className="flex flex-wrap gap-4 mt-8"> <div className="flex flex-wrap gap-4 mt-8">
<Button
onClick={() => onNavigate?.('/new-request')}
className="bg-blue-600 hover:bg-blue-700 text-white border-0 shadow-lg hover:shadow-xl transition-all duration-200"
>
<FileText className="w-5 h-5 mr-2" />
Create New Claim
</Button>
<Button <Button
onClick={() => { onClick={() => {
setRefreshing(true); setRefreshing(true);
@ -266,13 +259,6 @@ export function DealerDashboard({ onNavigate, onNewRequest: _onNewRequest }: Das
You don't have any claims data yet. Once you create and submit claim requests, your analytics will appear here. You don't have any claims data yet. Once you create and submit claim requests, your analytics will appear here.
</p> </p>
<div className="flex flex-col sm:flex-row gap-4"> <div className="flex flex-col sm:flex-row gap-4">
<Button
onClick={() => onNavigate?.('/new-request')}
className="bg-blue-600 hover:bg-blue-700 text-white"
>
<FileText className="w-5 h-5 mr-2" />
Create Your First Claim
</Button>
<Button <Button
onClick={() => { onClick={() => {
setRefreshing(true); setRefreshing(true);

View File

@ -690,6 +690,7 @@ function DealerClaimRequestDetailInner({ requestId: propRequestId, onBack, dynam
pausedByUserId={request?.pauseInfo?.pausedBy?.userId} pausedByUserId={request?.pauseInfo?.pausedBy?.userId}
currentUserId={currentUserId} currentUserId={currentUserId}
apiRequest={apiRequest} apiRequest={apiRequest}
hideApproveReject={isDealer}
/> />
)} )}
</div> </div>

View File

@ -331,7 +331,7 @@ export function useRequestDetails(
internalOrders: internalOrders || [], internalOrders: internalOrders || [],
// New normalized tables (also available via claimDetails for backward compatibility) // New normalized tables (also available via claimDetails for backward compatibility)
budgetTracking: (claimDetails as any)?.budgetTracking || null, budgetTracking: (claimDetails as any)?.budgetTracking || null,
invoice: (claimDetails as any)?.invoice || null, invoice: claimDetails?.invoice || (claimDetails as any)?.invoice || null,
creditNote: (claimDetails as any)?.creditNote || null, creditNote: (claimDetails as any)?.creditNote || null,
completionExpenses: (claimDetails as any)?.completionExpenses || null, completionExpenses: (claimDetails as any)?.completionExpenses || null,
templateType: wf.templateType || wf.template_type, templateType: wf.templateType || wf.template_type,

View File

@ -32,6 +32,7 @@ interface QuickActionsSidebarProps {
currentUserId?: string; // Current user's ID (kept for backwards compatibility) currentUserId?: string; // Current user's ID (kept for backwards compatibility)
apiRequest?: any; apiRequest?: any;
onEditClaimAmount?: () => void; onEditClaimAmount?: () => void;
hideApproveReject?: boolean;
} }
export function QuickActionsSidebar({ export function QuickActionsSidebar({
@ -52,6 +53,7 @@ export function QuickActionsSidebar({
currentUserId: currentUserIdProp, currentUserId: currentUserIdProp,
apiRequest, apiRequest,
onEditClaimAmount, onEditClaimAmount,
hideApproveReject = false,
}: QuickActionsSidebarProps) { }: QuickActionsSidebarProps) {
const { user } = useAuth(); const { user } = useAuth();
const [sharedRecipients, setSharedRecipients] = useState<SharedRecipient[]>([]); const [sharedRecipients, setSharedRecipients] = useState<SharedRecipient[]>([]);
@ -210,7 +212,7 @@ export function QuickActionsSidebar({
{/* Approve/Reject Buttons */} {/* Approve/Reject Buttons */}
<div className="pt-3 sm:pt-4 space-y-2"> <div className="pt-3 sm:pt-4 space-y-2">
{currentApprovalLevel && !isPaused && ( {currentApprovalLevel && !isPaused && !hideApproveReject && (
<> <>
<Button <Button
className="w-full bg-green-600 hover:bg-green-700 text-white h-9 sm:h-10 text-xs sm:text-sm" className="w-full bg-green-600 hover:bg-green-700 text-white h-9 sm:h-10 text-xs sm:text-sm"