oonclusin remark fallback added
This commit is contained in:
parent
e8caafa7a1
commit
fc46f32282
@ -197,6 +197,9 @@ function CustomRequestDetailInner({ requestId: propRequestId, onBack, dynamicReq
|
||||
aiGenerated,
|
||||
handleGenerateConclusion,
|
||||
handleFinalizeConclusion,
|
||||
generationAttempts,
|
||||
generationFailed,
|
||||
maxAttemptsReached,
|
||||
} = useConclusionRemark(request, requestIdentifier, isInitiator, refreshDetails, onBack, setActionStatus, setShowActionStatusModal);
|
||||
|
||||
// Load system policy on mount
|
||||
@ -510,6 +513,9 @@ function CustomRequestDetailInner({ requestId: propRequestId, onBack, dynamicReq
|
||||
currentUserIsApprover={!!currentApprovalLevel}
|
||||
pausedByUserId={request?.pauseInfo?.pausedBy?.userId}
|
||||
currentUserId={(user as any)?.userId}
|
||||
generationAttempts={generationAttempts}
|
||||
generationFailed={generationFailed}
|
||||
maxAttemptsReached={maxAttemptsReached}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
@ -41,6 +41,9 @@ interface ClaimManagementOverviewTabProps {
|
||||
aiGenerated?: boolean;
|
||||
handleGenerateConclusion?: () => void;
|
||||
handleFinalizeConclusion?: () => void;
|
||||
generationAttempts?: number;
|
||||
generationFailed?: boolean;
|
||||
maxAttemptsReached?: boolean;
|
||||
}
|
||||
|
||||
export function ClaimManagementOverviewTab({
|
||||
@ -58,6 +61,9 @@ export function ClaimManagementOverviewTab({
|
||||
aiGenerated = false,
|
||||
handleGenerateConclusion,
|
||||
handleFinalizeConclusion,
|
||||
generationAttempts = 0,
|
||||
generationFailed = false,
|
||||
maxAttemptsReached = false,
|
||||
}: ClaimManagementOverviewTabProps) {
|
||||
// Check if this is a claim management request
|
||||
if (!isClaimManagementRequest(apiRequest)) {
|
||||
@ -182,17 +188,24 @@ export function ClaimManagementOverviewTab({
|
||||
</CardDescription>
|
||||
</div>
|
||||
{handleGenerateConclusion && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleGenerateConclusion}
|
||||
disabled={conclusionLoading}
|
||||
className="gap-2 shrink-0"
|
||||
data-testid="generate-ai-conclusion-button"
|
||||
>
|
||||
<RefreshCw className={`w-3.5 h-3.5 ${conclusionLoading ? 'animate-spin' : ''}`} />
|
||||
{aiGenerated ? 'Regenerate' : 'Generate with AI'}
|
||||
</Button>
|
||||
<div className="flex flex-col items-end gap-1.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleGenerateConclusion}
|
||||
disabled={conclusionLoading || maxAttemptsReached}
|
||||
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'}
|
||||
</Button>
|
||||
{aiGenerated && !maxAttemptsReached && !generationFailed && (
|
||||
<span className="text-[10px] text-gray-500 font-medium px-1">
|
||||
{2 - generationAttempts} attempts remaining
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
@ -230,6 +230,9 @@ function DealerClaimRequestDetailInner({ requestId: propRequestId, onBack, dynam
|
||||
aiGenerated,
|
||||
handleGenerateConclusion,
|
||||
handleFinalizeConclusion,
|
||||
generationAttempts,
|
||||
generationFailed,
|
||||
maxAttemptsReached,
|
||||
} = useConclusionRemark(
|
||||
request,
|
||||
requestIdentifier,
|
||||
@ -587,6 +590,9 @@ function DealerClaimRequestDetailInner({ requestId: propRequestId, onBack, dynam
|
||||
aiGenerated={aiGenerated}
|
||||
handleGenerateConclusion={handleGenerateConclusion}
|
||||
handleFinalizeConclusion={handleFinalizeConclusion}
|
||||
generationAttempts={generationAttempts}
|
||||
generationFailed={generationFailed}
|
||||
maxAttemptsReached={maxAttemptsReached}
|
||||
/>
|
||||
</TabsContent>
|
||||
|
||||
|
||||
@ -42,6 +42,18 @@ export function useConclusionRemark(
|
||||
// State: Tracks if current conclusion was AI-generated (shows badge in UI)
|
||||
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
|
||||
*
|
||||
@ -113,8 +125,12 @@ export function useConclusionRemark(
|
||||
* 5. Handle errors silently (user can type manually)
|
||||
*/
|
||||
const handleGenerateConclusion = async () => {
|
||||
// Safety check: Prevent generation if max attempts already reached
|
||||
if (maxAttemptsReached) return;
|
||||
|
||||
try {
|
||||
setConclusionLoading(true);
|
||||
setGenerationFailed(false);
|
||||
|
||||
// Lazy load: Import conclusion API
|
||||
const { generateConclusion } = await import('@/services/conclusionApi');
|
||||
@ -122,14 +138,74 @@ export function useConclusionRemark(
|
||||
// API Call: Generate AI conclusion based on request data
|
||||
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
|
||||
setConclusionRemark(result.aiGeneratedRemark);
|
||||
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) {
|
||||
// Fail silently: User can write conclusion manually
|
||||
console.error('[useConclusionRemark] AI generation failed:', err);
|
||||
setConclusionRemark('');
|
||||
const newFailures = failureAttempts + 1;
|
||||
setFailureAttempts(newFailures);
|
||||
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 {
|
||||
setConclusionLoading(false);
|
||||
}
|
||||
@ -276,7 +352,10 @@ export function useConclusionRemark(
|
||||
conclusionSubmitting,
|
||||
aiGenerated,
|
||||
handleGenerateConclusion,
|
||||
handleFinalizeConclusion
|
||||
handleFinalizeConclusion,
|
||||
generationAttempts,
|
||||
generationFailed,
|
||||
maxAttemptsReached
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,9 @@ interface OverviewTabProps {
|
||||
currentUserIsApprover?: boolean;
|
||||
pausedByUserId?: string;
|
||||
currentUserId?: string;
|
||||
generationAttempts?: number;
|
||||
generationFailed?: boolean;
|
||||
maxAttemptsReached?: boolean;
|
||||
}
|
||||
|
||||
export function OverviewTab({
|
||||
@ -51,6 +54,9 @@ export function OverviewTab({
|
||||
currentUserIsApprover = false,
|
||||
pausedByUserId: _pausedByUserId,
|
||||
currentUserId: _currentUserId,
|
||||
generationAttempts = 0,
|
||||
generationFailed = false,
|
||||
maxAttemptsReached = false,
|
||||
}: OverviewTabProps) {
|
||||
void _onPause; // Marked as intentionally unused - available for future use
|
||||
const { user } = useAuth();
|
||||
@ -64,6 +70,7 @@ export function OverviewTab({
|
||||
|
||||
// Retrigger: Only for initiator when approver paused (initiator asks approver to resume)
|
||||
const canRetrigger = isPaused && isInitiator && pausedByUserId && pausedByUserId !== currentUserId && onRetrigger;
|
||||
|
||||
return (
|
||||
<div className="space-y-4 sm:space-y-6" data-testid="overview-tab-content">
|
||||
{/* Request Initiator Card */}
|
||||
@ -345,17 +352,24 @@ export function OverviewTab({
|
||||
: 'All approvals are complete. Please review and finalize the conclusion to close this request.'}
|
||||
</CardDescription>
|
||||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleGenerateConclusion}
|
||||
disabled={conclusionLoading}
|
||||
className="gap-2 shrink-0"
|
||||
data-testid="generate-ai-conclusion-button"
|
||||
>
|
||||
<RefreshCw className={`w-3.5 h-3.5 ${conclusionLoading ? 'animate-spin' : ''}`} />
|
||||
{aiGenerated ? 'Regenerate' : 'Generate with AI'}
|
||||
</Button>
|
||||
<div className="flex flex-col items-end gap-1.5">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleGenerateConclusion}
|
||||
disabled={conclusionLoading || maxAttemptsReached}
|
||||
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'}
|
||||
</Button>
|
||||
{aiGenerated && !maxAttemptsReached && !generationFailed && (
|
||||
<span className="text-[10px] text-gray-500 font-medium px-1">
|
||||
{2 - generationAttempts} attempts remaining
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="pt-4">
|
||||
|
||||
Loading…
Reference in New Issue
Block a user