Auto save fixed
This commit is contained in:
parent
3424868c57
commit
302ddebfe5
@ -15,13 +15,13 @@ const CustomToast = ({ message, type = 'success', onClose, duration = 4000 }) =>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`fixed top-4 left-1/2 -translate-x-1/2 flex items-start gap-3 p-4 rounded-lg shadow-md ${bgColor} z-[10000] min-w-[320px] max-w-[90%] animate-[fadeIn_0.3s_ease-out]`}
|
className={`fixed top-4 left-1/2 -translate-x-1/2 flex items-center gap-3 p-4 rounded-lg shadow-md ${bgColor} z-[10000] min-w-[320px] max-w-[90%] animate-[fadeIn_0.3s_ease-out]`}
|
||||||
>
|
>
|
||||||
<Icon className="w-5 h-5 mt-0.5 flex-shrink-0" />
|
<Icon className="w-5 h-5 flex-shrink-0" />
|
||||||
<div className="flex-1 text-sm font-medium">{message}</div>
|
<div className="flex-1 text-sm font-medium">{message}</div>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="text-gray-500 hover:text-gray-700 ml-2"
|
className="text-gray-500 hover:text-gray-700 ml-2 flex items-center justify-center"
|
||||||
aria-label="Close"
|
aria-label="Close"
|
||||||
>
|
>
|
||||||
×
|
×
|
||||||
|
|||||||
@ -72,6 +72,7 @@ const SurveyCarousel = ({
|
|||||||
try {
|
try {
|
||||||
const submissionData = await fetchSubmissionDetail(current.draftId);
|
const submissionData = await fetchSubmissionDetail(current.draftId);
|
||||||
localStorage.removeItem('currentSubmission');
|
localStorage.removeItem('currentSubmission');
|
||||||
|
|
||||||
navigate('/survey', {
|
navigate('/survey', {
|
||||||
state: {
|
state: {
|
||||||
submission: submissionData,
|
submission: submissionData,
|
||||||
@ -84,6 +85,7 @@ const SurveyCarousel = ({
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem('currentSubmission');
|
localStorage.removeItem('currentSubmission');
|
||||||
|
localStorage.removeItem('autoSaveData');
|
||||||
onStartSurvey?.(current);
|
onStartSurvey?.(current);
|
||||||
navigate('/survey', { state: { survey: current } });
|
navigate('/survey', { state: { survey: current } });
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import {
|
|||||||
} from '@/services/masters/masterService.js';
|
} from '@/services/masters/masterService.js';
|
||||||
// import { fetchEstablishmentDetail } from '@/services/establishments/establishmentService';
|
// import { fetchEstablishmentDetail } from '@/services/establishments/establishmentService';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import CustomToast from '@/components/common/CustomToast';
|
||||||
|
|
||||||
const caretDownSrc = '/assets/images/CaretDown-black.svg';
|
const caretDownSrc = '/assets/images/CaretDown-black.svg';
|
||||||
const clockIcon = '/assets/images/mingcute_time-line.svg';
|
const clockIcon = '/assets/images/mingcute_time-line.svg';
|
||||||
@ -480,6 +481,8 @@ const ProductData = ({
|
|||||||
const [isLoadingUnits, setIsLoadingUnits] = React.useState(false);
|
const [isLoadingUnits, setIsLoadingUnits] = React.useState(false);
|
||||||
const [unitsError, setUnitsError] = React.useState(null);
|
const [unitsError, setUnitsError] = React.useState(null);
|
||||||
const [isSavingDraft, setIsSavingDraft] = React.useState(false);
|
const [isSavingDraft, setIsSavingDraft] = React.useState(false);
|
||||||
|
const [showValidationErrorToast, setShowValidationErrorToast] = React.useState(false);
|
||||||
|
const [validationErrorMessage, setValidationErrorMessage] = React.useState('');
|
||||||
|
|
||||||
const latestDraftRef = React.useRef({ products: [], deletedProducts: [], remarks: '' });
|
const latestDraftRef = React.useRef({ products: [], deletedProducts: [], remarks: '' });
|
||||||
const draftKeyRef = React.useRef('');
|
const draftKeyRef = React.useRef('');
|
||||||
@ -1621,7 +1624,7 @@ useEffect(() => {
|
|||||||
const remainingProducts = availableProductsCount - currentProductsCount;
|
const remainingProducts = availableProductsCount - currentProductsCount;
|
||||||
|
|
||||||
if (currentProductsCount < availableProductsCount) {
|
if (currentProductsCount < availableProductsCount) {
|
||||||
errors['products_general'] = `Please fill remaining ${remainingProducts} product${remainingProducts > 1 ? 's' : ''} before proceeding`;
|
errors['products_general'] = 'Please complete all products to continue.';
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1740,11 +1743,32 @@ useEffect(() => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleNext = () => {
|
const handleNext = () => {
|
||||||
if (validateForm()) {
|
// First run validation to get errors
|
||||||
|
const errors = {};
|
||||||
|
let isValid = true;
|
||||||
|
|
||||||
|
// Check if all available products have been added
|
||||||
|
const availableProductsCount = productOptions.length;
|
||||||
|
const currentProductsCount = products.length;
|
||||||
|
|
||||||
|
if (currentProductsCount < availableProductsCount) {
|
||||||
|
errors['products_general'] = 'Please complete all products to continue.';
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update formErrors state
|
||||||
|
setFormErrors(errors);
|
||||||
|
|
||||||
|
if (isValid) {
|
||||||
onNext();
|
onNext();
|
||||||
} else {
|
} else {
|
||||||
|
// Show toast notification for validation error
|
||||||
|
if (errors['products_general']) {
|
||||||
|
setValidationErrorMessage(errors['products_general']);
|
||||||
|
setShowValidationErrorToast(true);
|
||||||
|
}
|
||||||
// Scroll to first error
|
// Scroll to first error
|
||||||
const firstErrorKey = Object.keys(formErrors)[0];
|
const firstErrorKey = Object.keys(errors)[0];
|
||||||
if (firstErrorKey) {
|
if (firstErrorKey) {
|
||||||
const element = document.querySelector(`[data-field="${firstErrorKey}"]`);
|
const element = document.querySelector(`[data-field="${firstErrorKey}"]`);
|
||||||
if (element) {
|
if (element) {
|
||||||
@ -2494,16 +2518,6 @@ useEffect(() => {
|
|||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<p className="text-sm font-semibold text-gray-900">Product #{idx + 1}</p>
|
<p className="text-sm font-semibold text-gray-900">Product #{idx + 1}</p>
|
||||||
{products.length > 1 && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => removeProduct(p.id)}
|
|
||||||
className="p-1.5 rounded-md hover:bg-[#92722A]/10"
|
|
||||||
title="Remove product"
|
|
||||||
>
|
|
||||||
<img src={trashActiveSrc} alt="Delete" className="h-5 w-5" />
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
<div data-field={`product_${idx}`}>
|
<div data-field={`product_${idx}`}>
|
||||||
@ -3204,7 +3218,6 @@ useEffect(() => {
|
|||||||
{productsError && <p>{productsError}</p>}
|
{productsError && <p>{productsError}</p>}
|
||||||
{unitsError && <p>{unitsError}</p>}
|
{unitsError && <p>{unitsError}</p>}
|
||||||
{formErrors['draft_general'] && <p>{formErrors['draft_general']}</p>}
|
{formErrors['draft_general'] && <p>{formErrors['draft_general']}</p>}
|
||||||
{formErrors['products_general'] && <p>{formErrors['products_general']}</p>}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -3287,6 +3300,16 @@ useEffect(() => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Validation Error Toast */}
|
||||||
|
{showValidationErrorToast && (
|
||||||
|
<CustomToast
|
||||||
|
message={validationErrorMessage}
|
||||||
|
type="error"
|
||||||
|
onClose={() => setShowValidationErrorToast(false)}
|
||||||
|
duration={5000}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user