Re_Figma_Code/src/hooks/useConclusionRemark.ts

231 lines
7.5 KiB
TypeScript

import { useState, useEffect } from 'react';
/**
* Custom Hook: useConclusionRemark
*
* Purpose: Manages conclusion remark generation and finalization
*
* Responsibilities:
* - Fetches existing AI-generated conclusion
* - Generates new conclusion using AI
* - Finalizes conclusion and closes request
* - Manages loading and submission states
* - Handles navigation after successful closure
*
* @param request - Current request object
* @param requestIdentifier - Request number or UUID
* @param isInitiator - Whether current user is the request initiator
* @param refreshDetails - Function to refresh request data
* @param onBack - Navigation callback
* @param setActionStatus - Function to show action status modal
* @param setShowActionStatusModal - Function to control action status modal visibility
* @returns Object with conclusion state and action handlers
*/
export function useConclusionRemark(
request: any,
requestIdentifier: string,
isInitiator: boolean,
refreshDetails: () => Promise<void>,
onBack?: () => void,
setActionStatus?: (status: { success: boolean; title: string; message: string }) => void,
setShowActionStatusModal?: (show: boolean) => void
) {
// State: The conclusion remark text (editable by user)
const [conclusionRemark, setConclusionRemark] = useState('');
// State: Indicates if AI is currently generating conclusion
const [conclusionLoading, setConclusionLoading] = useState(false);
// State: Indicates if conclusion is being submitted to backend
const [conclusionSubmitting, setConclusionSubmitting] = useState(false);
// State: Tracks if current conclusion was AI-generated (shows badge in UI)
const [aiGenerated, setAiGenerated] = useState(false);
/**
* Function: fetchExistingConclusion
*
* Purpose: Load existing AI-generated conclusion from backend
*
* Use Case: When request is approved, final approver generates conclusion.
* Initiator needs to review and finalize it before closing request.
*
* Process:
* 1. Dynamically import conclusion API service
* 2. Fetch conclusion by request ID
* 3. Load into state if exists
* 4. Mark as AI-generated if applicable
*/
const fetchExistingConclusion = async () => {
try {
// Lazy load: Import conclusion API only when needed
const { getConclusion } = await import('@/services/conclusionApi');
// API Call: Fetch existing conclusion
const result = await getConclusion(request.requestId || requestIdentifier);
if (result && result.aiGeneratedRemark) {
// Load: Set the AI-generated or final remark
setConclusionRemark(result.finalRemark || result.aiGeneratedRemark);
setAiGenerated(!!result.aiGeneratedRemark);
}
} catch (err) {
// No conclusion yet - this is expected for newly approved requests
}
};
/**
* Function: handleGenerateConclusion
*
* Purpose: Generate a new conclusion remark using AI
*
* How it works:
* 1. Sends request details to AI service
* 2. AI analyzes approval history, comments, and request data
* 3. Generates professional conclusion summarizing outcome
* 4. User can edit the AI suggestion before finalizing
*
* Process:
* 1. Set loading state
* 2. Call AI generation API
* 3. Load generated text into textarea
* 4. Mark as AI-generated (shows badge)
* 5. Handle errors silently (user can type manually)
*/
const handleGenerateConclusion = async () => {
try {
setConclusionLoading(true);
// Lazy load: Import conclusion API
const { generateConclusion } = await import('@/services/conclusionApi');
// API Call: Generate AI conclusion based on request data
const result = await generateConclusion(request.requestId || requestIdentifier);
// Success: Load AI-generated remark
setConclusionRemark(result.aiGeneratedRemark);
setAiGenerated(true);
} catch (err) {
// Fail silently: User can write conclusion manually
console.error('[useConclusionRemark] AI generation failed:', err);
setConclusionRemark('');
setAiGenerated(false);
} finally {
setConclusionLoading(false);
}
};
/**
* Function: handleFinalizeConclusion
*
* Purpose: Submit conclusion remark and close the request
*
* Business Logic:
* - Only initiators can finalize approved requests
* - Conclusion cannot be empty
* - After finalization:
* → Request status changes to CLOSED
* → All participants are notified
* → Request moves to Closed Requests
* → Conclusion is permanently saved
*
* Process:
* 1. Validate conclusion is not empty
* 2. Submit to backend
* 3. Show success modal
* 4. Refresh request data (status will be "closed")
* 5. Navigate to Closed Requests after 2 seconds
* 6. Handle errors with user-friendly messages
*/
const handleFinalizeConclusion = async () => {
// Validation: Ensure conclusion is not empty
if (!conclusionRemark.trim()) {
setActionStatus?.({
success: false,
title: 'Validation Error',
message: 'Conclusion remark cannot be empty'
});
setShowActionStatusModal?.(true);
return;
}
try {
setConclusionSubmitting(true);
// Lazy load: Import conclusion API
const { finalizeConclusion } = await import('@/services/conclusionApi');
// API Call: Submit conclusion and close request
// Backend will:
// - Update request status to CLOSED
// - Save conclusion remark
// - Send notifications to all participants
// - Record closure timestamp
await finalizeConclusion(request.requestId || requestIdentifier, conclusionRemark);
// Success feedback
setActionStatus?.({
success: true,
title: 'Request Closed with Successful Completion',
message: 'The request has been finalized and moved to Closed Requests.'
});
setShowActionStatusModal?.(true);
// Refresh: Update UI with new "closed" status
await refreshDetails();
/**
* Navigate: Redirect to Closed Requests after showing success message
* Delay allows user to see the success notification
*/
setTimeout(() => {
if (onBack) {
// Use callback navigation if provided
onBack();
// Then navigate to closed requests
setTimeout(() => {
window.location.hash = '#/closed-requests';
}, 100);
} else {
// Direct navigation
window.location.hash = '#/closed-requests';
}
}, 2000); // 2 second delay
} catch (err: any) {
// Error feedback with backend message
setActionStatus?.({
success: false,
title: 'Error',
message: err.response?.data?.error || 'Failed to finalize conclusion'
});
setShowActionStatusModal?.(true);
} finally {
setConclusionSubmitting(false);
}
};
/**
* Effect: Auto-fetch existing conclusion when request becomes approved
*
* Trigger: When request status changes to "approved" and user is initiator
* Purpose: Load any conclusion generated by final approver
*/
useEffect(() => {
if (request?.status === 'approved' && isInitiator && !conclusionRemark) {
fetchExistingConclusion();
}
}, [request?.status, isInitiator]);
return {
conclusionRemark,
setConclusionRemark,
conclusionLoading,
conclusionSubmitting,
aiGenerated,
handleGenerateConclusion,
handleFinalizeConclusion
};
}