CR Bug fixed

This commit is contained in:
Malini 2026-03-02 18:56:28 +05:30
parent 2e1bd35f24
commit 281d98cf13

View File

@ -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>