CR Bug fixed
This commit is contained in:
parent
2e1bd35f24
commit
281d98cf13
@ -92,7 +92,8 @@ const SearchableSelect = ({
|
|||||||
error = false,
|
error = false,
|
||||||
className = '',
|
className = '',
|
||||||
displayValue = undefined,
|
displayValue = undefined,
|
||||||
allowNull = false // New prop to allow null values for no selection
|
allowNull = false, // New prop to allow null values for no selection
|
||||||
|
allowClear = false // New prop to show clear button
|
||||||
}) => {
|
}) => {
|
||||||
const [isOpen, setIsOpen] = React.useState(false);
|
const [isOpen, setIsOpen] = React.useState(false);
|
||||||
const [searchTerm, setSearchTerm] = React.useState('');
|
const [searchTerm, setSearchTerm] = React.useState('');
|
||||||
@ -176,6 +177,28 @@ const SearchableSelect = ({
|
|||||||
|
|
||||||
const displayValueToShow = internalDisplayValue;
|
const displayValueToShow = internalDisplayValue;
|
||||||
|
|
||||||
|
// Handle clear functionality
|
||||||
|
const handleClear = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
if (onChange) {
|
||||||
|
const event = {
|
||||||
|
target: {
|
||||||
|
value: allowNull ? null : ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onChange(event);
|
||||||
|
}
|
||||||
|
setSearchTerm('');
|
||||||
|
setIsOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check if clear button should be shown
|
||||||
|
// Show clear button when value is not null, not undefined, and not empty string
|
||||||
|
// Note: value can be 0 (user-selected) but should still show clear button
|
||||||
|
const hasValue = value !== null && value !== undefined && value !== '';
|
||||||
|
const showClear = allowClear && hasValue && !disabled && !loading;
|
||||||
|
|
||||||
// Handle option selection
|
// Handle option selection
|
||||||
const handleSelect = (option) => {
|
const handleSelect = (option) => {
|
||||||
if (onChange) {
|
if (onChange) {
|
||||||
@ -211,13 +234,25 @@ const SearchableSelect = ({
|
|||||||
className={`flex items-center justify-between h-10 px-4 py-2 text-sm rounded-md border-2 ${borderColor} ${bgColor} ${disabled ? 'bg-gray-50 cursor-not-allowed' : 'cursor-pointer'}`}
|
className={`flex items-center justify-between h-10 px-4 py-2 text-sm rounded-md border-2 ${borderColor} ${bgColor} ${disabled ? 'bg-gray-50 cursor-not-allowed' : 'cursor-pointer'}`}
|
||||||
onClick={() => !disabled && !loading && setIsOpen(!isOpen)}
|
onClick={() => !disabled && !loading && setIsOpen(!isOpen)}
|
||||||
>
|
>
|
||||||
<span className="truncate">{displayValueToShow || placeholder}</span>
|
<span className="truncate mr-2">{displayValueToShow || placeholder}</span>
|
||||||
{loading ? (
|
<div className="flex items-center">
|
||||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-[#92722A] border-t-transparent ml-2" />
|
{showClear ? (
|
||||||
) : (
|
<button
|
||||||
<img src={caretDownSrc} alt="open" className="h-4 w-4 ml-2" />
|
type="button"
|
||||||
|
className="h-6 w-6 text-xl rounded-full text-[#9CA3AF] hover:text-[#4B5563] flex items-center justify-center ml-auto"
|
||||||
|
onClick={handleClear}
|
||||||
|
aria-label="Clear selection"
|
||||||
|
>
|
||||||
|
×
|
||||||
|
</button>
|
||||||
|
) : loading ? (
|
||||||
|
<div className="h-4 w-4 animate-spin rounded-full border-2 border-[#92722A] border-t-transparent" />
|
||||||
|
) : null}
|
||||||
|
{!showClear && !loading && (
|
||||||
|
<img src={caretDownSrc} alt="open" className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{isOpen && (
|
{isOpen && (
|
||||||
<div className="absolute z-10 w-full mt-1 bg-white rounded-md shadow-lg border border-gray-200">
|
<div className="absolute z-10 w-full mt-1 bg-white rounded-md shadow-lg border border-gray-200">
|
||||||
@ -1424,56 +1459,49 @@ useEffect(() => {
|
|||||||
const errors = {};
|
const errors = {};
|
||||||
let isValid = true;
|
let isValid = true;
|
||||||
|
|
||||||
|
// Check if all available products have been added
|
||||||
|
const availableProductsCount = productOptions.length;
|
||||||
|
const currentProductsCount = products.length;
|
||||||
|
const remainingProducts = availableProductsCount - currentProductsCount;
|
||||||
|
|
||||||
|
if (currentProductsCount < availableProductsCount) {
|
||||||
|
errors['products_general'] = `Please fill remaining ${remainingProducts} product${remainingProducts > 1 ? 's' : ''} before proceeding`;
|
||||||
|
isValid = false;
|
||||||
|
}
|
||||||
|
|
||||||
products.forEach((product, index) => {
|
products.forEach((product, index) => {
|
||||||
// Validate product selection
|
// Only validate fields that are marked as mandatory (with asterisk in UI)
|
||||||
|
// Product selection is mandatory
|
||||||
if (!product.product) {
|
if (!product.product) {
|
||||||
errors[`product_${index}`] = 'Product is required';
|
errors[`product_${index}`] = 'Product is required';
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate unit selection
|
// Unit selection is mandatory
|
||||||
if (!product.unit) {
|
if (!product.unit) {
|
||||||
errors[`unit_${index}`] = 'Unit is required';
|
errors[`unit_${index}`] = 'Unit is required';
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate capacity
|
// Annual installed capacity is mandatory
|
||||||
if (!product.capacity) {
|
if (!product.capacity || product.capacity === '0' || parseFloat(product.capacity) === 0) {
|
||||||
errors[`capacity_${index}`] = 'Capacity is required';
|
errors[`capacity_${index}`] = 'Capacity is required';
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate current quarter quantities
|
// Only quantity fields with asterisk (*) are mandatory (current and next quarter)
|
||||||
const currentQuantityFields = ['janQuantity', 'febQuantity', 'marQuantity'];
|
const mandatoryQuantityFields = ['janQuantity', 'febQuantity', 'marQuantity', 'aprQuantity', 'mayQuantity', 'junQuantity'];
|
||||||
currentQuantityFields.forEach(field => {
|
mandatoryQuantityFields.forEach(field => {
|
||||||
if (!product[field] && product[field] !== 0) {
|
if (!product[field] || product[field] === '0' || parseFloat(product[field]) === 0) {
|
||||||
errors[`${field}_${index}`] = 'Required';
|
errors[`${field}_${index}`] = 'Required';
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Validate current quarter costs
|
// Only value fields with asterisk (*) are mandatory (current and next quarter)
|
||||||
const currentCostFields = ['janCost', 'febCost', 'marCost'];
|
const mandatoryCostFields = ['janCost', 'febCost', 'marCost', 'aprCost', 'mayCost', 'junCost'];
|
||||||
currentCostFields.forEach(field => {
|
mandatoryCostFields.forEach(field => {
|
||||||
if (!product[field] && product[field] !== 0) {
|
if (!product[field] || product[field] === '0' || parseFloat(product[field]) === 0) {
|
||||||
errors[`${field}_${index}`] = 'Required';
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Validate forecast quantities
|
|
||||||
const forecastQuantityFields = ['aprQuantity', 'mayQuantity', 'junQuantity'];
|
|
||||||
forecastQuantityFields.forEach(field => {
|
|
||||||
if (!product[field] && product[field] !== 0) {
|
|
||||||
errors[`${field}_${index}`] = 'Required';
|
|
||||||
isValid = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Validate forecast costs
|
|
||||||
const forecastCostFields = ['aprCost', 'mayCost', 'junCost'];
|
|
||||||
forecastCostFields.forEach(field => {
|
|
||||||
if (!product[field] && product[field] !== 0) {
|
|
||||||
errors[`${field}_${index}`] = 'Required';
|
errors[`${field}_${index}`] = 'Required';
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
@ -2613,6 +2641,7 @@ useEffect(() => {
|
|||||||
value={p.novCost || ''}
|
value={p.novCost || ''}
|
||||||
error={!!formErrors[`novCost_${idx}`]}
|
error={!!formErrors[`novCost_${idx}`]}
|
||||||
onChange={(e) => updateProductField(p.id, 'novCost', e.target.value)}
|
onChange={(e) => updateProductField(p.id, 'novCost', e.target.value)}
|
||||||
|
allowClear={true}
|
||||||
/>
|
/>
|
||||||
{formErrors[`novCost_${idx}`] && (
|
{formErrors[`novCost_${idx}`] && (
|
||||||
<p className="mt-1 text-xs text-red-600">{formErrors[`novCost_${idx}`]}</p>
|
<p className="mt-1 text-xs text-red-600">{formErrors[`novCost_${idx}`]}</p>
|
||||||
@ -2761,6 +2790,7 @@ useEffect(() => {
|
|||||||
loading={isLoadingReasons}
|
loading={isLoadingReasons}
|
||||||
error={!!formErrors[`variationReason_${idx}`]}
|
error={!!formErrors[`variationReason_${idx}`]}
|
||||||
allowNull={true}
|
allowNull={true}
|
||||||
|
allowClear={true}
|
||||||
/>
|
/>
|
||||||
{formErrors[`variationReason_${idx}`] && (
|
{formErrors[`variationReason_${idx}`] && (
|
||||||
<p className="mt-1 text-xs text-red-600">{formErrors[`variationReason_${idx}`]}</p>
|
<p className="mt-1 text-xs text-red-600">{formErrors[`variationReason_${idx}`]}</p>
|
||||||
@ -2920,6 +2950,7 @@ useEffect(() => {
|
|||||||
onChange={(e) => updateProductField(p.id, 'zeroTargetReason', e.target.value, zeroTargetReasons)}
|
onChange={(e) => updateProductField(p.id, 'zeroTargetReason', e.target.value, zeroTargetReasons)}
|
||||||
loading={isLoadingZeroReasons}
|
loading={isLoadingZeroReasons}
|
||||||
allowNull={true}
|
allowNull={true}
|
||||||
|
allowClear={true}
|
||||||
/>
|
/>
|
||||||
{(showOtherZeroTargetReason[p.id] || (p.zeroTargetReason && (p.zeroTargetReason.toString().toLowerCase().includes('other') || (zeroTargetReasons.find(r => r.value === p.zeroTargetReason)?.name?.toLowerCase().includes('other') || zeroTargetReasons.find(r => r.value === p.zeroTargetReason)?.label?.toLowerCase().includes('other'))))) && (
|
{(showOtherZeroTargetReason[p.id] || (p.zeroTargetReason && (p.zeroTargetReason.toString().toLowerCase().includes('other') || (zeroTargetReasons.find(r => r.value === p.zeroTargetReason)?.name?.toLowerCase().includes('other') || zeroTargetReasons.find(r => r.value === p.zeroTargetReason)?.label?.toLowerCase().includes('other'))))) && (
|
||||||
<div className="mt-3 p-3 bg-gray-50 rounded-md border border-gray-200">
|
<div className="mt-3 p-3 bg-gray-50 rounded-md border border-gray-200">
|
||||||
@ -3008,6 +3039,7 @@ 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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user