bug fixed in products
This commit is contained in:
parent
0d4ae03789
commit
52df438da4
@ -102,6 +102,7 @@ const EditCompanyProfile = () => {
|
||||
const [productSearchTerm, setProductSearchTerm] = useState('');
|
||||
const [isLoadingProducts, setIsLoadingProducts] = useState(false);
|
||||
const [productError, setProductError] = useState("");
|
||||
const [productLimitMessage, setProductLimitMessage] = useState("");
|
||||
const [isLoadingCities, setIsLoadingCities] = useState(false);
|
||||
const [visibleProductCount, setVisibleProductCount] = useState(14);
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
||||
@ -157,8 +158,9 @@ const EditCompanyProfile = () => {
|
||||
|
||||
// Special validation for Products step
|
||||
if (stepIndex === 3) {
|
||||
if (selectedProducts.length === 0) {
|
||||
setProductError("At least one product is required.");
|
||||
const checkedCount = selectedProducts.filter(p => checkedProducts.has(p.value || p.id || p.product_id)).length;
|
||||
if (checkedCount === 0) {
|
||||
setProductError("At least one product must be checked.");
|
||||
return false;
|
||||
} else {
|
||||
setProductError("");
|
||||
@ -181,7 +183,7 @@ const EditCompanyProfile = () => {
|
||||
return nextErrors;
|
||||
});
|
||||
return valid;
|
||||
}, [form, requiredFieldsByStep, selectedProducts]);
|
||||
}, [form, requiredFieldsByStep, selectedProducts, checkedProducts]);
|
||||
|
||||
const handleFormChange = useCallback((field, value) => {
|
||||
// Clear remarks if either industry code field is changed
|
||||
@ -408,15 +410,47 @@ const EditCompanyProfile = () => {
|
||||
}
|
||||
return; // Don't add duplicate, don't modify available products
|
||||
}
|
||||
|
||||
// Count current newly added products (excluding original products)
|
||||
const currentNewlyAdded = selectedProducts.filter(p => newlyAddedProductIds.has(p.value || p.id || p.product_id));
|
||||
|
||||
// Allow up to 4 newly added products
|
||||
if (currentNewlyAdded.length >= 4) {
|
||||
alert('Maximum of 4 replacement products can be added.');
|
||||
return; // Don't add product, don't modify available products
|
||||
|
||||
const MAX_TOTAL_SELECTED_PRODUCTS = 15;
|
||||
const MAX_NEW_PRODUCTS_PER_ACTION = 4;
|
||||
|
||||
const checkedCountTotal = selectedProducts.filter(p => checkedProducts.has(p.value || p.id || p.product_id)).length;
|
||||
if (checkedCountTotal >= MAX_TOTAL_SELECTED_PRODUCTS) {
|
||||
setProductLimitMessage(`Maximum of ${MAX_TOTAL_SELECTED_PRODUCTS} products can be selected.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Count newly added products (current action) that are selected/checked
|
||||
const newlyAddedCheckedCount = selectedProducts.filter(p => {
|
||||
const productId = p.value || p.id || p.product_id;
|
||||
return !p.isExisting && checkedProducts.has(productId);
|
||||
}).length;
|
||||
|
||||
// Base count should represent selected products excluding the newly added ones
|
||||
const baseSelectedCount = checkedCountTotal - newlyAddedCheckedCount;
|
||||
|
||||
// Allowed additions are capped by both per-action limit and remaining total slots (<= logic)
|
||||
const allowedAddCount = Math.min(
|
||||
MAX_NEW_PRODUCTS_PER_ACTION,
|
||||
Math.max(0, MAX_TOTAL_SELECTED_PRODUCTS - baseSelectedCount)
|
||||
);
|
||||
|
||||
if (newlyAddedCheckedCount >= allowedAddCount) {
|
||||
if (allowedAddCount === 0) {
|
||||
setProductLimitMessage(`Maximum of ${MAX_TOTAL_SELECTED_PRODUCTS} products can be selected.`);
|
||||
} else {
|
||||
setProductLimitMessage(`You can add up to ${allowedAddCount} new product${allowedAddCount !== 1 ? 's' : ''}.`);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (baseSelectedCount + newlyAddedCheckedCount + 1 > MAX_TOTAL_SELECTED_PRODUCTS) {
|
||||
setProductLimitMessage(`Maximum of ${MAX_TOTAL_SELECTED_PRODUCTS} products can be selected.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear any previous limit message when product can be added
|
||||
setProductLimitMessage("");
|
||||
|
||||
// Only proceed with adding the product if we pass all checks
|
||||
setSelectedProducts(prev => [...prev, { ...product, isExisting: false }]);
|
||||
@ -473,6 +507,9 @@ const EditCompanyProfile = () => {
|
||||
return newIds;
|
||||
});
|
||||
|
||||
// Clear limit message when product is removed
|
||||
setProductLimitMessage("");
|
||||
|
||||
// Always add back to available products
|
||||
setAvailableProducts(prev => {
|
||||
// Check if already in available products
|
||||
@ -1106,6 +1143,53 @@ const EditCompanyProfile = () => {
|
||||
<div className="bg-white rounded-lg border border-[#E5E7EB] overflow-hidden">
|
||||
<div className="bg-[#F9FAFB] px-4 py-3 border-b border-[#E5E7EB]">
|
||||
<h5 className="text-sm font-medium text-[#111827]">Available Products</h5>
|
||||
{(() => {
|
||||
const MAX_TOTAL_SELECTED_PRODUCTS = 15;
|
||||
const MAX_NEW_PRODUCTS_PER_ACTION = 4;
|
||||
|
||||
const checkedCountTotal = selectedProducts.filter(p => checkedProducts.has(p.value || p.id || p.product_id)).length;
|
||||
const newlyAddedCheckedCount = selectedProducts.filter(p => {
|
||||
const productId = p.value || p.id || p.product_id;
|
||||
return !p.isExisting && checkedProducts.has(productId);
|
||||
}).length;
|
||||
const baseSelectedCount = checkedCountTotal - newlyAddedCheckedCount;
|
||||
const allowedAddCount = Math.min(
|
||||
MAX_NEW_PRODUCTS_PER_ACTION,
|
||||
Math.max(0, MAX_TOTAL_SELECTED_PRODUCTS - baseSelectedCount)
|
||||
);
|
||||
const remainingToAdd = Math.max(0, allowedAddCount - newlyAddedCheckedCount);
|
||||
|
||||
if (productLimitMessage) return null;
|
||||
|
||||
if (checkedCountTotal >= MAX_TOTAL_SELECTED_PRODUCTS || allowedAddCount === 0) {
|
||||
return (
|
||||
<p className="text-xs text-[#B91C1C] mt-1">Maximum of {MAX_TOTAL_SELECTED_PRODUCTS} products can be selected.</p>
|
||||
);
|
||||
}
|
||||
|
||||
if (newlyAddedCheckedCount >= allowedAddCount) {
|
||||
return (
|
||||
<p className="text-xs text-[#B91C1C] mt-1">You can add up to {allowedAddCount} new product{allowedAddCount !== 1 ? 's' : ''}.</p>
|
||||
);
|
||||
}
|
||||
|
||||
if (remainingToAdd < MAX_NEW_PRODUCTS_PER_ACTION) {
|
||||
return (
|
||||
<p className="text-xs text-[#B91C1C] mt-1">You can add {remainingToAdd} more product{remainingToAdd !== 1 ? 's' : ''}.</p>
|
||||
);
|
||||
}
|
||||
|
||||
if (allowedAddCount < MAX_NEW_PRODUCTS_PER_ACTION) {
|
||||
return (
|
||||
<p className="text-xs text-[#B91C1C] mt-1">You can add up to {allowedAddCount} new product{allowedAddCount !== 1 ? 's' : ''}.</p>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
})()}
|
||||
{productLimitMessage && (
|
||||
<p className="text-xs text-[#B91C1C] mt-1">{productLimitMessage}</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<div className="relative mb-4">
|
||||
@ -1149,7 +1233,39 @@ const EditCompanyProfile = () => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleAddProduct(product)}
|
||||
className="text-[#92722A] hover:text-[#7A5F1E] text-sm font-medium"
|
||||
disabled={(() => {
|
||||
const MAX_TOTAL_SELECTED_PRODUCTS = 15;
|
||||
const MAX_NEW_PRODUCTS_PER_ACTION = 4;
|
||||
const checkedCountTotal = selectedProducts.filter(p => checkedProducts.has(p.value || p.id || p.product_id)).length;
|
||||
const newlyAddedCheckedCount = selectedProducts.filter(p => {
|
||||
const productId = p.value || p.id || p.product_id;
|
||||
return !p.isExisting && checkedProducts.has(productId);
|
||||
}).length;
|
||||
const baseSelectedCount = checkedCountTotal - newlyAddedCheckedCount;
|
||||
const allowedAddCount = Math.min(
|
||||
MAX_NEW_PRODUCTS_PER_ACTION,
|
||||
Math.max(0, MAX_TOTAL_SELECTED_PRODUCTS - baseSelectedCount)
|
||||
);
|
||||
return checkedCountTotal >= MAX_TOTAL_SELECTED_PRODUCTS || newlyAddedCheckedCount >= allowedAddCount;
|
||||
})()}
|
||||
className={`text-sm font-medium ${(() => {
|
||||
const MAX_TOTAL_SELECTED_PRODUCTS = 15;
|
||||
const MAX_NEW_PRODUCTS_PER_ACTION = 4;
|
||||
const checkedCountTotal = selectedProducts.filter(p => checkedProducts.has(p.value || p.id || p.product_id)).length;
|
||||
const newlyAddedCheckedCount = selectedProducts.filter(p => {
|
||||
const productId = p.value || p.id || p.product_id;
|
||||
return !p.isExisting && checkedProducts.has(productId);
|
||||
}).length;
|
||||
const baseSelectedCount = checkedCountTotal - newlyAddedCheckedCount;
|
||||
const allowedAddCount = Math.min(
|
||||
MAX_NEW_PRODUCTS_PER_ACTION,
|
||||
Math.max(0, MAX_TOTAL_SELECTED_PRODUCTS - baseSelectedCount)
|
||||
);
|
||||
const disabled = checkedCountTotal >= MAX_TOTAL_SELECTED_PRODUCTS || newlyAddedCheckedCount >= allowedAddCount;
|
||||
return disabled
|
||||
? 'text-gray-400 cursor-not-allowed'
|
||||
: 'text-[#92722A] hover:text-[#7A5F1E]';
|
||||
})()}`}
|
||||
>
|
||||
Add
|
||||
</button>
|
||||
@ -1170,94 +1286,93 @@ const EditCompanyProfile = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
{/* Selected Products Panel */}
|
||||
<div className="bg-white rounded-lg border border-[#E5E7EB] overflow-hidden">
|
||||
<div className="bg-[#F9FAFB] px-4 py-3 border-b border-[#E5E7EB] flex justify-between items-center">
|
||||
<h5 className="text-sm font-medium text-[#111827]">Selected Products</h5>
|
||||
<div className="bg-white rounded-lg border border-[#E5E7EB] overflow-hidden">
|
||||
<div className="bg-[#F9FAFB] px-4 py-3 border-b border-[#E5E7EB] flex justify-between items-center">
|
||||
<h5 className="text-sm font-medium text-[#111827]">Selected Products</h5>
|
||||
|
||||
{selectedProducts.length > 0 && (
|
||||
<span className="bg-[#FEF3C7] text-[#92400E] text-xs font-medium px-2 py-0.5 rounded-full">
|
||||
{selectedProducts.length} selected
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="p-4">
|
||||
{selectedProducts.length > 0 ? (
|
||||
<ul className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto">
|
||||
{selectedProducts.map((product) => {
|
||||
const isNewlyAdded = newlyAddedProductIds.has(product.value);
|
||||
|
||||
// extract hs + name
|
||||
const hsCode = product.label?.split(" - ")[0] ?? "";
|
||||
const productName = product.label?.split(" - ")[1] ?? product.label;
|
||||
|
||||
return (
|
||||
<li
|
||||
key={product.value}
|
||||
className="p-3 hover:bg-[#F9FAFB] flex justify-between items-center"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{!isNewlyAdded && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCheckboxToggle(product.value)}
|
||||
className="flex-shrink-0 w-5 h-5"
|
||||
>
|
||||
{checkedProducts.has(product.value) ? (
|
||||
<IoMdCheckbox className="text-[#92722A] text-lg" />
|
||||
) : (
|
||||
<MdOutlineCheckBoxOutlineBlank className="text-[#92722A] text-lg" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{product.action_done_by === "admin_users" ? (
|
||||
<MdAdminPanelSettings
|
||||
className="text-[#92722A] text-lg flex-shrink-0 w-6 h-6"
|
||||
title="Added by Admin"
|
||||
/>
|
||||
) : (
|
||||
<FaBuilding
|
||||
className="text-[#92722A] flex-shrink-0 w-6 h-5"
|
||||
title="Added by Establishment"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div className="text-sm font-medium text-[#111827]">
|
||||
{productName}
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
HS Code: {hsCode}
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="bg-[#FEF3C7] text-[#92400E] text-xs font-medium px-2 py-0.5 rounded-full">
|
||||
{selectedProducts.filter(p => checkedProducts.has(p.value || p.id || p.product_id)).length}/15 selected
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="p-4">
|
||||
{selectedProducts.length > 0 ? (
|
||||
<ul className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto">
|
||||
{selectedProducts.map((product) => {
|
||||
const isNewlyAdded = newlyAddedProductIds.has(product.value);
|
||||
|
||||
// extract hs + name
|
||||
const hsCode = product.label?.split(" - ")[0] ?? "";
|
||||
const productName = product.label?.split(" - ")[1] ?? product.label;
|
||||
|
||||
return (
|
||||
<li
|
||||
key={product.value}
|
||||
className="p-3 hover:bg-[#F9FAFB] flex justify-between items-center"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{!isNewlyAdded && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleCheckboxToggle(product.value)}
|
||||
className="flex-shrink-0 w-5 h-5"
|
||||
>
|
||||
{checkedProducts.has(product.value) ? (
|
||||
<IoMdCheckbox className="text-[#92722A] text-lg" />
|
||||
) : (
|
||||
<MdOutlineCheckBoxOutlineBlank className="text-[#92722A] text-lg" />
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{product.action_done_by === "admin_users" ? (
|
||||
<MdAdminPanelSettings
|
||||
className="text-[#92722A] text-lg flex-shrink-0 w-6 h-6"
|
||||
title="Added by Admin"
|
||||
/>
|
||||
) : (
|
||||
<FaBuilding
|
||||
className="text-[#92722A] flex-shrink-0 w-6 h-5"
|
||||
title="Added by Establishment"
|
||||
/>
|
||||
)}
|
||||
|
||||
<div>
|
||||
<div className="text-sm font-medium text-[#111827]">
|
||||
{productName}
|
||||
</div>
|
||||
|
||||
<div className="text-xs text-gray-500 mt-1">
|
||||
HS Code: {hsCode}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isNewlyAdded && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveProduct(product)}
|
||||
className="text-[#EF4444] hover:text-[#DC2626] text-sm font-medium"
|
||||
title="Remove product"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<div className="p-4 text-center text-sm text-[#6B7280] border border-[#E5E7EB] rounded-md">
|
||||
No products selected
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isNewlyAdded && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleRemoveProduct(product)}
|
||||
className="text-[#EF4444] hover:text-[#DC2626] text-sm font-medium"
|
||||
title="Remove product"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
) : (
|
||||
<div className="p-4 text-center text-sm text-[#6B7280] border border-[#E5E7EB] rounded-md">
|
||||
No products selected
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user