oonclusin remark fallback added
This commit is contained in:
parent
e8caafa7a1
commit
fc46f32282
@ -197,6 +197,9 @@ function CustomRequestDetailInner({ requestId: propRequestId, onBack, dynamicReq
|
|||||||
aiGenerated,
|
aiGenerated,
|
||||||
handleGenerateConclusion,
|
handleGenerateConclusion,
|
||||||
handleFinalizeConclusion,
|
handleFinalizeConclusion,
|
||||||
|
generationAttempts,
|
||||||
|
generationFailed,
|
||||||
|
maxAttemptsReached,
|
||||||
} = useConclusionRemark(request, requestIdentifier, isInitiator, refreshDetails, onBack, setActionStatus, setShowActionStatusModal);
|
} = useConclusionRemark(request, requestIdentifier, isInitiator, refreshDetails, onBack, setActionStatus, setShowActionStatusModal);
|
||||||
|
|
||||||
// Load system policy on mount
|
// Load system policy on mount
|
||||||
@ -510,6 +513,9 @@ function CustomRequestDetailInner({ requestId: propRequestId, onBack, dynamicReq
|
|||||||
currentUserIsApprover={!!currentApprovalLevel}
|
currentUserIsApprover={!!currentApprovalLevel}
|
||||||
pausedByUserId={request?.pauseInfo?.pausedBy?.userId}
|
pausedByUserId={request?.pauseInfo?.pausedBy?.userId}
|
||||||
currentUserId={(user as any)?.userId}
|
currentUserId={(user as any)?.userId}
|
||||||
|
generationAttempts={generationAttempts}
|
||||||
|
generationFailed={generationFailed}
|
||||||
|
maxAttemptsReached={maxAttemptsReached}
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
|
|||||||
@ -41,6 +41,9 @@ interface ClaimManagementOverviewTabProps {
|
|||||||
aiGenerated?: boolean;
|
aiGenerated?: boolean;
|
||||||
handleGenerateConclusion?: () => void;
|
handleGenerateConclusion?: () => void;
|
||||||
handleFinalizeConclusion?: () => void;
|
handleFinalizeConclusion?: () => void;
|
||||||
|
generationAttempts?: number;
|
||||||
|
generationFailed?: boolean;
|
||||||
|
maxAttemptsReached?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ClaimManagementOverviewTab({
|
export function ClaimManagementOverviewTab({
|
||||||
@ -58,6 +61,9 @@ export function ClaimManagementOverviewTab({
|
|||||||
aiGenerated = false,
|
aiGenerated = false,
|
||||||
handleGenerateConclusion,
|
handleGenerateConclusion,
|
||||||
handleFinalizeConclusion,
|
handleFinalizeConclusion,
|
||||||
|
generationAttempts = 0,
|
||||||
|
generationFailed = false,
|
||||||
|
maxAttemptsReached = false,
|
||||||
}: ClaimManagementOverviewTabProps) {
|
}: ClaimManagementOverviewTabProps) {
|
||||||
// Check if this is a claim management request
|
// Check if this is a claim management request
|
||||||
if (!isClaimManagementRequest(apiRequest)) {
|
if (!isClaimManagementRequest(apiRequest)) {
|
||||||
@ -182,17 +188,24 @@ export function ClaimManagementOverviewTab({
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
{handleGenerateConclusion && (
|
{handleGenerateConclusion && (
|
||||||
<Button
|
<div className="flex flex-col items-end gap-1.5">
|
||||||
variant="outline"
|
<Button
|
||||||
size="sm"
|
variant="outline"
|
||||||
onClick={handleGenerateConclusion}
|
size="sm"
|
||||||
disabled={conclusionLoading}
|
onClick={handleGenerateConclusion}
|
||||||
className="gap-2 shrink-0"
|
disabled={conclusionLoading || maxAttemptsReached}
|
||||||
data-testid="generate-ai-conclusion-button"
|
className="gap-2 shrink-0 h-9"
|
||||||
>
|
data-testid="generate-ai-conclusion-button"
|
||||||
<RefreshCw className={`w-3.5 h-3.5 ${conclusionLoading ? 'animate-spin' : ''}`} />
|
>
|
||||||
{aiGenerated ? 'Regenerate' : 'Generate with AI'}
|
<RefreshCw className={`w-3.5 h-3.5 ${conclusionLoading ? 'animate-spin' : ''}`} />
|
||||||
</Button>
|
{aiGenerated ? 'Regenerate' : 'Generate with AI'}
|
||||||
|
</Button>
|
||||||
|
{aiGenerated && !maxAttemptsReached && !generationFailed && (
|
||||||
|
<span className="text-[10px] text-gray-500 font-medium px-1">
|
||||||
|
{2 - generationAttempts} attempts remaining
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
|
|||||||
@ -230,6 +230,9 @@ function DealerClaimRequestDetailInner({ requestId: propRequestId, onBack, dynam
|
|||||||
aiGenerated,
|
aiGenerated,
|
||||||
handleGenerateConclusion,
|
handleGenerateConclusion,
|
||||||
handleFinalizeConclusion,
|
handleFinalizeConclusion,
|
||||||
|
generationAttempts,
|
||||||
|
generationFailed,
|
||||||
|
maxAttemptsReached,
|
||||||
} = useConclusionRemark(
|
} = useConclusionRemark(
|
||||||
request,
|
request,
|
||||||
requestIdentifier,
|
requestIdentifier,
|
||||||
@ -587,6 +590,9 @@ function DealerClaimRequestDetailInner({ requestId: propRequestId, onBack, dynam
|
|||||||
aiGenerated={aiGenerated}
|
aiGenerated={aiGenerated}
|
||||||
handleGenerateConclusion={handleGenerateConclusion}
|
handleGenerateConclusion={handleGenerateConclusion}
|
||||||
handleFinalizeConclusion={handleFinalizeConclusion}
|
handleFinalizeConclusion={handleFinalizeConclusion}
|
||||||
|
generationAttempts={generationAttempts}
|
||||||
|
generationFailed={generationFailed}
|
||||||
|
maxAttemptsReached={maxAttemptsReached}
|
||||||
/>
|
/>
|
||||||
</TabsContent>
|
</TabsContent>
|
||||||
|
|
||||||
|
|||||||
@ -42,6 +42,18 @@ export function useConclusionRemark(
|
|||||||
// State: Tracks if current conclusion was AI-generated (shows badge in UI)
|
// State: Tracks if current conclusion was AI-generated (shows badge in UI)
|
||||||
const [aiGenerated, setAiGenerated] = useState(false);
|
const [aiGenerated, setAiGenerated] = useState(false);
|
||||||
|
|
||||||
|
// State: Tracks number of AI generation attempts
|
||||||
|
const [generationAttempts, setGenerationAttempts] = useState(0);
|
||||||
|
|
||||||
|
// State: Tracks if AI generation failed (unable to generate)
|
||||||
|
const [generationFailed, setGenerationFailed] = useState(false);
|
||||||
|
|
||||||
|
// State: Tracks if max attempts (3 for success, 1 for fail) reached
|
||||||
|
const [maxAttemptsReached, setMaxAttemptsReached] = useState(false);
|
||||||
|
|
||||||
|
// State: Tracks number of AI generation failures
|
||||||
|
const [failureAttempts, setFailureAttempts] = useState(0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function: fetchExistingConclusion
|
* Function: fetchExistingConclusion
|
||||||
*
|
*
|
||||||
@ -113,8 +125,12 @@ export function useConclusionRemark(
|
|||||||
* 5. Handle errors silently (user can type manually)
|
* 5. Handle errors silently (user can type manually)
|
||||||
*/
|
*/
|
||||||
const handleGenerateConclusion = async () => {
|
const handleGenerateConclusion = async () => {
|
||||||
|
// Safety check: Prevent generation if max attempts already reached
|
||||||
|
if (maxAttemptsReached) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setConclusionLoading(true);
|
setConclusionLoading(true);
|
||||||
|
setGenerationFailed(false);
|
||||||
|
|
||||||
// Lazy load: Import conclusion API
|
// Lazy load: Import conclusion API
|
||||||
const { generateConclusion } = await import('@/services/conclusionApi');
|
const { generateConclusion } = await import('@/services/conclusionApi');
|
||||||
@ -122,14 +138,74 @@ export function useConclusionRemark(
|
|||||||
// API Call: Generate AI conclusion based on request data
|
// API Call: Generate AI conclusion based on request data
|
||||||
const result = await generateConclusion(request.requestId || requestIdentifier);
|
const result = await generateConclusion(request.requestId || requestIdentifier);
|
||||||
|
|
||||||
|
const newAttempts = generationAttempts + 1;
|
||||||
|
setGenerationAttempts(newAttempts);
|
||||||
|
|
||||||
|
// Check for "unable to generate" or similar keywords in proper response
|
||||||
|
const isUnableToGenerate = !result?.aiGeneratedRemark ||
|
||||||
|
result.aiGeneratedRemark.toLowerCase().includes('unable to generate') ||
|
||||||
|
result.aiGeneratedRemark.toLowerCase().includes('sorry');
|
||||||
|
|
||||||
|
if (isUnableToGenerate) {
|
||||||
|
const newFailures = failureAttempts + 1;
|
||||||
|
setFailureAttempts(newFailures);
|
||||||
|
|
||||||
|
if (newFailures >= 2) {
|
||||||
|
setMaxAttemptsReached(true);
|
||||||
|
setActionStatus?.({
|
||||||
|
success: false,
|
||||||
|
title: 'AI Generation Limit Reached',
|
||||||
|
message: "We're unable to process a conclusion remark at this time after 2 attempts. Please proceed with a manual approach using the editor below."
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setActionStatus?.({
|
||||||
|
success: false,
|
||||||
|
title: 'System Note',
|
||||||
|
message: "We're unable to process a conclusion remark at the moment. You have one more attempt remaining, or you can proceed manually."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setShowActionStatusModal?.(true);
|
||||||
|
setConclusionRemark(result?.aiGeneratedRemark || '');
|
||||||
|
setAiGenerated(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Success: Load AI-generated remark
|
// Success: Load AI-generated remark
|
||||||
setConclusionRemark(result.aiGeneratedRemark);
|
setConclusionRemark(result.aiGeneratedRemark);
|
||||||
setAiGenerated(true);
|
setAiGenerated(true);
|
||||||
|
setFailureAttempts(0); // Reset failures on success
|
||||||
|
|
||||||
|
// Limit to 2 successful attempts
|
||||||
|
if (newAttempts >= 2) {
|
||||||
|
setMaxAttemptsReached(true);
|
||||||
|
setActionStatus?.({
|
||||||
|
success: true,
|
||||||
|
title: 'Maximum Attempts Reached',
|
||||||
|
message: "You've reached the maximum of 2 regeneration attempts. Feel free to manually edit the current suggestion to fit your specific needs."
|
||||||
|
});
|
||||||
|
setShowActionStatusModal?.(true);
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Fail silently: User can write conclusion manually
|
|
||||||
console.error('[useConclusionRemark] AI generation failed:', err);
|
console.error('[useConclusionRemark] AI generation failed:', err);
|
||||||
setConclusionRemark('');
|
const newFailures = failureAttempts + 1;
|
||||||
|
setFailureAttempts(newFailures);
|
||||||
setAiGenerated(false);
|
setAiGenerated(false);
|
||||||
|
|
||||||
|
if (newFailures >= 2) {
|
||||||
|
setMaxAttemptsReached(true);
|
||||||
|
setActionStatus?.({
|
||||||
|
success: false,
|
||||||
|
title: 'System Note',
|
||||||
|
message: "We're unable to process your request at the moment. Since the maximum of 2 attempts is reached, please proceed with a manual approach."
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setActionStatus?.({
|
||||||
|
success: false,
|
||||||
|
title: 'System Note',
|
||||||
|
message: "We're unable to process your request at the moment. You have one more attempt remaining, or you can proceed manually."
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setShowActionStatusModal?.(true);
|
||||||
} finally {
|
} finally {
|
||||||
setConclusionLoading(false);
|
setConclusionLoading(false);
|
||||||
}
|
}
|
||||||
@ -276,7 +352,10 @@ export function useConclusionRemark(
|
|||||||
conclusionSubmitting,
|
conclusionSubmitting,
|
||||||
aiGenerated,
|
aiGenerated,
|
||||||
handleGenerateConclusion,
|
handleGenerateConclusion,
|
||||||
handleFinalizeConclusion
|
handleFinalizeConclusion,
|
||||||
|
generationAttempts,
|
||||||
|
generationFailed,
|
||||||
|
maxAttemptsReached
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,9 @@ interface OverviewTabProps {
|
|||||||
currentUserIsApprover?: boolean;
|
currentUserIsApprover?: boolean;
|
||||||
pausedByUserId?: string;
|
pausedByUserId?: string;
|
||||||
currentUserId?: string;
|
currentUserId?: string;
|
||||||
|
generationAttempts?: number;
|
||||||
|
generationFailed?: boolean;
|
||||||
|
maxAttemptsReached?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OverviewTab({
|
export function OverviewTab({
|
||||||
@ -51,6 +54,9 @@ export function OverviewTab({
|
|||||||
currentUserIsApprover = false,
|
currentUserIsApprover = false,
|
||||||
pausedByUserId: _pausedByUserId,
|
pausedByUserId: _pausedByUserId,
|
||||||
currentUserId: _currentUserId,
|
currentUserId: _currentUserId,
|
||||||
|
generationAttempts = 0,
|
||||||
|
generationFailed = false,
|
||||||
|
maxAttemptsReached = false,
|
||||||
}: OverviewTabProps) {
|
}: OverviewTabProps) {
|
||||||
void _onPause; // Marked as intentionally unused - available for future use
|
void _onPause; // Marked as intentionally unused - available for future use
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
@ -64,6 +70,7 @@ export function OverviewTab({
|
|||||||
|
|
||||||
// Retrigger: Only for initiator when approver paused (initiator asks approver to resume)
|
// Retrigger: Only for initiator when approver paused (initiator asks approver to resume)
|
||||||
const canRetrigger = isPaused && isInitiator && pausedByUserId && pausedByUserId !== currentUserId && onRetrigger;
|
const canRetrigger = isPaused && isInitiator && pausedByUserId && pausedByUserId !== currentUserId && onRetrigger;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-4 sm:space-y-6" data-testid="overview-tab-content">
|
<div className="space-y-4 sm:space-y-6" data-testid="overview-tab-content">
|
||||||
{/* Request Initiator Card */}
|
{/* Request Initiator Card */}
|
||||||
@ -345,17 +352,24 @@ export function OverviewTab({
|
|||||||
: 'All approvals are complete. Please review and finalize the conclusion to close this request.'}
|
: 'All approvals are complete. Please review and finalize the conclusion to close this request.'}
|
||||||
</CardDescription>
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<div className="flex flex-col items-end gap-1.5">
|
||||||
variant="outline"
|
<Button
|
||||||
size="sm"
|
variant="outline"
|
||||||
onClick={handleGenerateConclusion}
|
size="sm"
|
||||||
disabled={conclusionLoading}
|
onClick={handleGenerateConclusion}
|
||||||
className="gap-2 shrink-0"
|
disabled={conclusionLoading || maxAttemptsReached}
|
||||||
data-testid="generate-ai-conclusion-button"
|
className="gap-2 shrink-0 h-9"
|
||||||
>
|
data-testid="generate-ai-conclusion-button"
|
||||||
<RefreshCw className={`w-3.5 h-3.5 ${conclusionLoading ? 'animate-spin' : ''}`} />
|
>
|
||||||
{aiGenerated ? 'Regenerate' : 'Generate with AI'}
|
<RefreshCw className={`w-3.5 h-3.5 ${conclusionLoading ? 'animate-spin' : ''}`} />
|
||||||
</Button>
|
{aiGenerated ? 'Regenerate' : 'Generate with AI'}
|
||||||
|
</Button>
|
||||||
|
{aiGenerated && !maxAttemptsReached && !generationFailed && (
|
||||||
|
<span className="text-[10px] text-gray-500 font-medium px-1">
|
||||||
|
{2 - generationAttempts} attempts remaining
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="pt-4">
|
<CardContent className="pt-4">
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user