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