CR Bug fixed
This commit is contained in:
parent
2e1bd35f24
commit
281d98cf13
@ -92,7 +92,8 @@ const SearchableSelect = ({
|
||||
error = false,
|
||||
className = '',
|
||||
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 [searchTerm, setSearchTerm] = React.useState('');
|
||||
@ -176,6 +177,28 @@ const SearchableSelect = ({
|
||||
|
||||
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
|
||||
const handleSelect = (option) => {
|
||||
if (onChange) {
|
||||
@ -211,12 +234,24 @@ 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'}`}
|
||||
onClick={() => !disabled && !loading && setIsOpen(!isOpen)}
|
||||
>
|
||||
<span className="truncate">{displayValueToShow || placeholder}</span>
|
||||
{loading ? (
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-[#92722A] border-t-transparent ml-2" />
|
||||
) : (
|
||||
<img src={caretDownSrc} alt="open" className="h-4 w-4 ml-2" />
|
||||
)}
|
||||
<span className="truncate mr-2">{displayValueToShow || placeholder}</span>
|
||||
<div className="flex items-center">
|
||||
{showClear ? (
|
||||
<button
|
||||
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>
|
||||
|
||||
{isOpen && (
|
||||
@ -1424,56 +1459,49 @@ useEffect(() => {
|
||||
const errors = {};
|
||||
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) => {
|
||||
// Validate product selection
|
||||
// Only validate fields that are marked as mandatory (with asterisk in UI)
|
||||
// Product selection is mandatory
|
||||
if (!product.product) {
|
||||
errors[`product_${index}`] = 'Product is required';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate unit selection
|
||||
// Unit selection is mandatory
|
||||
if (!product.unit) {
|
||||
errors[`unit_${index}`] = 'Unit is required';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate capacity
|
||||
if (!product.capacity) {
|
||||
// Annual installed capacity is mandatory
|
||||
if (!product.capacity || product.capacity === '0' || parseFloat(product.capacity) === 0) {
|
||||
errors[`capacity_${index}`] = 'Capacity is required';
|
||||
isValid = false;
|
||||
}
|
||||
|
||||
// Validate current quarter quantities
|
||||
const currentQuantityFields = ['janQuantity', 'febQuantity', 'marQuantity'];
|
||||
currentQuantityFields.forEach(field => {
|
||||
if (!product[field] && product[field] !== 0) {
|
||||
// Only quantity fields with asterisk (*) are mandatory (current and next quarter)
|
||||
const mandatoryQuantityFields = ['janQuantity', 'febQuantity', 'marQuantity', 'aprQuantity', 'mayQuantity', 'junQuantity'];
|
||||
mandatoryQuantityFields.forEach(field => {
|
||||
if (!product[field] || product[field] === '0' || parseFloat(product[field]) === 0) {
|
||||
errors[`${field}_${index}`] = 'Required';
|
||||
isValid = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Validate current quarter costs
|
||||
const currentCostFields = ['janCost', 'febCost', 'marCost'];
|
||||
currentCostFields.forEach(field => {
|
||||
if (!product[field] && 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) {
|
||||
// Only value fields with asterisk (*) are mandatory (current and next quarter)
|
||||
const mandatoryCostFields = ['janCost', 'febCost', 'marCost', 'aprCost', 'mayCost', 'junCost'];
|
||||
mandatoryCostFields.forEach(field => {
|
||||
if (!product[field] || product[field] === '0' || parseFloat(product[field]) === 0) {
|
||||
errors[`${field}_${index}`] = 'Required';
|
||||
isValid = false;
|
||||
}
|
||||
@ -2613,6 +2641,7 @@ useEffect(() => {
|
||||
value={p.novCost || ''}
|
||||
error={!!formErrors[`novCost_${idx}`]}
|
||||
onChange={(e) => updateProductField(p.id, 'novCost', e.target.value)}
|
||||
allowClear={true}
|
||||
/>
|
||||
{formErrors[`novCost_${idx}`] && (
|
||||
<p className="mt-1 text-xs text-red-600">{formErrors[`novCost_${idx}`]}</p>
|
||||
@ -2761,6 +2790,7 @@ useEffect(() => {
|
||||
loading={isLoadingReasons}
|
||||
error={!!formErrors[`variationReason_${idx}`]}
|
||||
allowNull={true}
|
||||
allowClear={true}
|
||||
/>
|
||||
{formErrors[`variationReason_${idx}`] && (
|
||||
<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)}
|
||||
loading={isLoadingZeroReasons}
|
||||
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'))))) && (
|
||||
<div className="mt-3 p-3 bg-gray-50 rounded-md border border-gray-200">
|
||||
@ -3008,6 +3039,7 @@ useEffect(() => {
|
||||
{productsError && <p>{productsError}</p>}
|
||||
{unitsError && <p>{unitsError}</p>}
|
||||
{formErrors['draft_general'] && <p>{formErrors['draft_general']}</p>}
|
||||
{formErrors['products_general'] && <p>{formErrors['products_general']}</p>}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user