fix in auto save
This commit is contained in:
parent
9653ca7395
commit
a59f32db26
@ -214,6 +214,7 @@ const handleResubmit = async (submission) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
const handleEdit = async (submission) => {
|
const handleEdit = async (submission) => {
|
||||||
|
localStorage.removeItem('autoSaveData');
|
||||||
if (submission.status.toLowerCase() === 'draft') {
|
if (submission.status.toLowerCase() === 'draft') {
|
||||||
try {
|
try {
|
||||||
const submissionData = await fetchSubmissionDetail(submission.id);
|
const submissionData = await fetchSubmissionDetail(submission.id);
|
||||||
|
|||||||
@ -653,7 +653,7 @@ React.useEffect(() => {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card className="mt-10">
|
<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 ? (
|
{loadingProducts ? (
|
||||||
<div className="flex justify-center py-4">
|
<div className="flex justify-center py-4">
|
||||||
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-[#92722A]" />
|
<div className="h-6 w-6 animate-spin rounded-full border-b-2 border-[#92722A]" />
|
||||||
|
|||||||
@ -563,6 +563,26 @@ const ProductData = ({
|
|||||||
persistLocalDraft(latestDraftRef.current);
|
persistLocalDraft(latestDraftRef.current);
|
||||||
}, [persistLocalDraft]);
|
}, [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 handleRemarksChange = (e) => {
|
||||||
const value = e.target.value;
|
const value = e.target.value;
|
||||||
setRemarks(value);
|
setRemarks(value);
|
||||||
@ -621,16 +641,81 @@ const ProductData = ({
|
|||||||
if (restoredDraftKeyRef.current === key && restoredDraftSignatureRef.current === parsedSignature) {
|
if (restoredDraftKeyRef.current === key && restoredDraftSignatureRef.current === parsedSignature) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
onProductsChange(parsed.products);
|
|
||||||
if (Array.isArray(parsed.deletedProducts)) setDeletedProducts(parsed.deletedProducts);
|
// Synchronize draft products with current establishment products
|
||||||
if (typeof parsed.remarks === 'string') setRemarks(parsed.remarks);
|
const synchronizedProducts = synchronizeDraftWithEstablishmentProducts(parsed.products, productOptions);
|
||||||
restoredDraftKeyRef.current = key;
|
|
||||||
restoredDraftSignatureRef.current = parsedSignature;
|
// 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) {
|
} catch (e) {
|
||||||
// ignore parse errors
|
// 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
|
// Debounced autosave to localStorage whenever user changes anything
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -959,11 +1044,69 @@ const existingProduct = currentSubmission.products?.find(p =>
|
|||||||
other_variation_reason: product.otherVariationReason || '',
|
other_variation_reason: product.otherVariationReason || '',
|
||||||
zero_target_reason_master_id: product.zeroTargetReason === null ? null : (parseInt(product.zeroTargetReason) || 0),
|
zero_target_reason_master_id: product.zeroTargetReason === null ? null : (parseInt(product.zeroTargetReason) || 0),
|
||||||
other_zero_target_reason: product.otherZeroTargetReason || '',
|
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) {
|
if (location.state?.isEdit && deletedProducts.length > 0) {
|
||||||
const deletedFormattedProducts = deletedProducts.map(product => {
|
const deletedFormattedProducts = deletedProducts.map(product => {
|
||||||
const selectedProduct = productOptions.find(p => p.value === product.product);
|
const selectedProduct = productOptions.find(p => p.value === product.product);
|
||||||
@ -1345,17 +1488,17 @@ useEffect(() => {
|
|||||||
const [lastAutoSave, setLastAutoSave] = React.useState(null);
|
const [lastAutoSave, setLastAutoSave] = React.useState(null);
|
||||||
const autoSaveInterval = React.useRef(null);
|
const autoSaveInterval = React.useRef(null);
|
||||||
|
|
||||||
// Auto-save every 2 minutes
|
// Auto-save every 1 second
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// Clear any existing interval
|
// Clear any existing interval
|
||||||
if (autoSaveInterval.current) {
|
if (autoSaveInterval.current) {
|
||||||
clearInterval(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(() => {
|
autoSaveInterval.current = setInterval(() => {
|
||||||
autoSaveDraft();
|
autoSaveDraft();
|
||||||
}, 120000); // 2 minutes = 120000 ms
|
}, 1000); // 1 second = 1000 ms
|
||||||
|
|
||||||
// Cleanup interval on unmount
|
// Cleanup interval on unmount
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@ -178,6 +178,7 @@ const showToast = (message, type = 'success') => {
|
|||||||
onClick={async (e) => {
|
onClick={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
localStorage.removeItem('autoSaveData');
|
||||||
try {
|
try {
|
||||||
const submissionData = await fetchSubmissionDetail(record.id);
|
const submissionData = await fetchSubmissionDetail(record.id);
|
||||||
navigate('/survey', {
|
navigate('/survey', {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user