fix in auto save

This commit is contained in:
Malini 2026-03-09 11:19:12 +05:30
parent 9653ca7395
commit a59f32db26
4 changed files with 157 additions and 12 deletions

View File

@ -214,6 +214,7 @@ const handleResubmit = async (submission) => {
}
};
const handleEdit = async (submission) => {
localStorage.removeItem('autoSaveData');
if (submission.status.toLowerCase() === 'draft') {
try {
const submissionData = await fetchSubmissionDetail(submission.id);

View File

@ -653,7 +653,7 @@ React.useEffect(() => {
</Card>
<Card className="mt-10">
<h3 className="text-sm font-semibold text-gray-900 mb-4">Productss</h3>
<h3 className="text-sm font-semibold text-gray-900 mb-4">Products</h3>
{loadingProducts ? (
<div className="flex justify-center py-4">
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-[#92722A]" />

View File

@ -563,6 +563,26 @@ const ProductData = ({
persistLocalDraft(latestDraftRef.current);
}, [persistLocalDraft]);
// Function to synchronize draft products with current establishment products
const synchronizeDraftWithEstablishmentProducts = React.useCallback((draftProducts, currentEstablishmentProducts) => {
if (!Array.isArray(draftProducts) || !Array.isArray(currentEstablishmentProducts)) {
return draftProducts;
}
// Create a Set of valid product IDs from current establishment products
const validProductIds = new Set(
currentEstablishmentProducts.map(p => p.value || p.id || p.product_id || p.productId)
);
// Filter draft products to only include those that are still valid
const synchronizedProducts = draftProducts.filter(draftProduct => {
const productId = draftProduct.product || draftProduct.productId || draftProduct.product_id;
return productId && validProductIds.has(productId.toString());
});
return synchronizedProducts;
}, []);
const handleRemarksChange = (e) => {
const value = e.target.value;
setRemarks(value);
@ -621,16 +641,81 @@ const ProductData = ({
if (restoredDraftKeyRef.current === key && restoredDraftSignatureRef.current === parsedSignature) {
return;
}
onProductsChange(parsed.products);
if (Array.isArray(parsed.deletedProducts)) setDeletedProducts(parsed.deletedProducts);
if (typeof parsed.remarks === 'string') setRemarks(parsed.remarks);
restoredDraftKeyRef.current = key;
restoredDraftSignatureRef.current = parsedSignature;
// Synchronize draft products with current establishment products
const synchronizedProducts = synchronizeDraftWithEstablishmentProducts(parsed.products, productOptions);
// Only restore if we have valid products after synchronization
if (synchronizedProducts.length > 0) {
onProductsChange(synchronizedProducts);
if (Array.isArray(parsed.deletedProducts)) setDeletedProducts(parsed.deletedProducts);
if (typeof parsed.remarks === 'string') setRemarks(parsed.remarks);
restoredDraftKeyRef.current = key;
restoredDraftSignatureRef.current = JSON.stringify({
products: synchronizedProducts,
deletedProducts: Array.isArray(parsed.deletedProducts) ? parsed.deletedProducts : [],
remarks: typeof parsed.remarks === 'string' ? parsed.remarks : '',
});
// Update the localStorage with synchronized data to prevent future issues
persistLocalDraft({
products: synchronizedProducts,
deletedProducts: Array.isArray(parsed.deletedProducts) ? parsed.deletedProducts : [],
remarks: typeof parsed.remarks === 'string' ? parsed.remarks : '',
});
} else {
// If no valid products remain, clear the draft and start fresh
onProductsChange([{ id: Date.now() }]);
// Clear the invalid draft from localStorage
if (typeof window !== 'undefined') {
localStorage.removeItem(key);
}
}
}
} catch (e) {
// ignore parse errors
}
}, [getLocalDraftKey, onProductsChange, products]);
}, [getLocalDraftKey, onProductsChange, products, synchronizeDraftWithEstablishmentProducts, productOptions]);
// Synchronize existing products when establishment products change
React.useEffect(() => {
if (productOptions.length === 0) return;
// Check if current products have any that are no longer valid
const validProductIds = new Set(
productOptions.map(p => p.value || p.id || p.product_id || p.productId)
);
const hasInvalidProducts = products.some(product => {
const productId = product.product || product.productId || product.product_id;
return productId && !validProductIds.has(productId.toString());
});
if (hasInvalidProducts) {
// Filter out invalid products
const validProducts = products.filter(product => {
const productId = product.product || product.productId || product.product_id;
return !productId || validProductIds.has(productId.toString());
});
// Update products state
onProductsChange(validProducts);
// Update the draft with filtered products
persistLocalDraft({
products: validProducts,
deletedProducts,
remarks,
});
// Update the latest draft ref
latestDraftRef.current = {
products: validProducts,
deletedProducts,
remarks,
};
}
}, [productOptions, products, onProductsChange, deletedProducts, remarks, persistLocalDraft]);
// Debounced autosave to localStorage whenever user changes anything
React.useEffect(() => {
@ -959,11 +1044,69 @@ const existingProduct = currentSubmission.products?.find(p =>
other_variation_reason: product.otherVariationReason || '',
zero_target_reason_master_id: product.zeroTargetReason === null ? null : (parseInt(product.zeroTargetReason) || 0),
other_zero_target_reason: product.otherZeroTargetReason || '',
remarks: product.remarks || remarks || '' // Use product.remarks if available, otherwise use the component's remarks state
remarks: product.remarks || remarks || '', // Use product.remarks if available, otherwise use the component's remarks state
is_active: true // Mark current products as active
};
});
// Add deleted products with is_active: false if we're in draft edit mode
// Get all products from current submission to identify removed ones
const currentSubmissionProducts = currentSubmission.products || [];
const currentProductIds = new Set(
products.map(p => p.product || p.productId || p.product_id)
);
// Find products that are in current submission but not in current products (removed products)
const removedProducts = currentSubmissionProducts.filter(submissionProduct => {
const productId = submissionProduct.product_id || submissionProduct.product?.id;
return productId && !currentProductIds.has(productId.toString());
});
// Add removed products with is_active: false
if (removedProducts.length > 0) {
const removedFormattedProducts = removedProducts.map(product => {
return {
id: product.id || 0,
product_id: product.product_id || product.product?.id || null,
unit_id: product.unit_id?.toString() || '0',
annual_installed_capacity: product.annual_installed_capacity || '0',
previous_quantity_period_one: product.previous_quantity_period_one || '0',
previous_quantity_period_two: product.previous_quantity_period_two || '0',
previous_quantity_period_three: product.previous_quantity_period_three || '0',
previous_cost_period_one: product.previous_cost_period_one || '0',
previous_cost_period_two: product.previous_cost_period_two || '0',
previous_cost_period_three: product.previous_cost_period_three || '0',
current_quantity_period_one: product.current_quantity_period_one || '0',
current_quantity_period_two: product.current_quantity_period_two || '0',
current_quantity_period_three: product.current_quantity_period_three || '0',
current_cost_period_one: product.current_cost_period_one || '0',
current_cost_period_two: product.current_cost_period_two || '0',
current_cost_period_three: product.current_cost_period_three || '0',
forecast_quantity_period_one: product.forecast_quantity_period_one || '0',
forecast_quantity_period_two: product.forecast_quantity_period_two || '0',
forecast_quantity_period_three: product.forecast_quantity_period_three || '0',
forecast_cost_period_one: product.forecast_cost_period_one || '0',
forecast_cost_period_two: product.forecast_cost_period_two || '0',
forecast_cost_period_three: product.forecast_cost_period_three || '0',
previous_quantity: product.previous_quantity || '0',
previous_cost: product.previous_cost || '0',
current_quantity: product.current_quantity || '0',
current_cost: product.current_cost || '0',
forecast_quantity: product.forecast_quantity || '0',
forecast_cost: product.forecast_cost || '0',
variation_reason_master_id: product.variation_reason_master_id || null,
other_variation_reason: product.other_variation_reason || '',
zero_target_reason_master_id: product.zero_target_reason_master_id || null,
other_zero_target_reason: product.other_zero_target_reason || '',
remarks: product.remarks || '',
is_active: false // Mark removed products as inactive
};
});
// Combine active and removed products
formattedProducts.push(...removedFormattedProducts);
}
// Also add deleted products from local state if we're in edit mode
if (location.state?.isEdit && deletedProducts.length > 0) {
const deletedFormattedProducts = deletedProducts.map(product => {
const selectedProduct = productOptions.find(p => p.value === product.product);
@ -1345,17 +1488,17 @@ useEffect(() => {
const [lastAutoSave, setLastAutoSave] = React.useState(null);
const autoSaveInterval = React.useRef(null);
// Auto-save every 2 minutes
// Auto-save every 1 second
React.useEffect(() => {
// Clear any existing interval
if (autoSaveInterval.current) {
clearInterval(autoSaveInterval.current);
}
// Set up new interval for auto-save every 2 minutes (120000 ms)
// Set up new interval for auto-save every 1 second (1000 ms)
autoSaveInterval.current = setInterval(() => {
autoSaveDraft();
}, 120000); // 2 minutes = 120000 ms
}, 1000); // 1 second = 1000 ms
// Cleanup interval on unmount
return () => {

View File

@ -178,6 +178,7 @@ const showToast = (message, type = 'success') => {
onClick={async (e) => {
e.preventDefault();
e.stopPropagation();
localStorage.removeItem('autoSaveData');
try {
const submissionData = await fetchSubmissionDetail(record.id);
navigate('/survey', {