added pdf link for products
This commit is contained in:
parent
49b14861aa
commit
d3d9167de2
@ -524,6 +524,9 @@ const CompanyProfile = () => {
|
||||
const [currentUser, setCurrentUser] = React.useState(null);
|
||||
const [products, setProducts] = React.useState([]);
|
||||
const [availableProducts, setAvailableProducts] = React.useState([]);
|
||||
const [visibleProductCount, setVisibleProductCount] = React.useState(14);
|
||||
const [isLoadingMore, setIsLoadingMore] = React.useState(false);
|
||||
const productListRef = React.useRef(null);
|
||||
const [selectedProducts, setSelectedProducts] = React.useState([]);
|
||||
const [productSearchTerm, setProductSearchTerm] = React.useState('');
|
||||
const [isLoadingProducts, setIsLoadingProducts] = React.useState(false);
|
||||
@ -547,6 +550,30 @@ const CompanyProfile = () => {
|
||||
const filteredAvailableProducts = availableProducts.filter(
|
||||
(product) => !selectedProducts.some((sp) => sp.id === product.id)
|
||||
);
|
||||
|
||||
// Reset visible count when products or search term changes
|
||||
React.useEffect(() => {
|
||||
setVisibleProductCount(14);
|
||||
}, [availableProducts.length, productSearchTerm]);
|
||||
|
||||
// Handle scroll for infinite loading
|
||||
const handleScroll = React.useCallback((e) => {
|
||||
if (isLoadingMore || visibleProductCount >= availableProducts.length) return;
|
||||
|
||||
const list = e.target;
|
||||
const { scrollTop, scrollHeight, clientHeight } = list;
|
||||
const isNearBottom = scrollTop + clientHeight >= scrollHeight - 100; // 100px from bottom
|
||||
|
||||
if (isNearBottom) {
|
||||
setIsLoadingMore(true);
|
||||
// Simulate loading delay for better UX
|
||||
setTimeout(() => {
|
||||
setVisibleProductCount(prev => Math.min(prev + 10, availableProducts.length));
|
||||
setIsLoadingMore(false);
|
||||
}, 300);
|
||||
}
|
||||
}, [isLoadingMore, visibleProductCount, availableProducts.length]);
|
||||
|
||||
// Load current user from session storage
|
||||
React.useEffect(() => {
|
||||
try {
|
||||
@ -1213,7 +1240,17 @@ const CompanyProfile = () => {
|
||||
{productError}
|
||||
</div>
|
||||
)}
|
||||
<h4 className="text-[16px] font-semibold text-[#232528]">Products <span className="text-red-500">*</span></h4>
|
||||
<div className="flex items-baseline">
|
||||
<h4 className="text-[16px] font-semibold text-[#232528] whitespace-nowrap">Products <span className="text-red-500">*</span></h4>
|
||||
<a
|
||||
href="/assets/images/HS-Code UAE (002).pdf"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="ml-3 text-sm text-black-600 hover:underline"
|
||||
>
|
||||
Please click here to view the list of HS codes
|
||||
</a>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* Available Products Panel */}
|
||||
<div className="bg-white rounded-lg border border-[#E5E7EB] overflow-hidden">
|
||||
@ -1241,8 +1278,12 @@ const CompanyProfile = () => {
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-[#92722A]"></div>
|
||||
</div>
|
||||
) : availableProducts.length > 0 ? (
|
||||
<ul className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto">
|
||||
{filteredAvailableProducts.map((product) => (
|
||||
<ul
|
||||
ref={productListRef}
|
||||
onScroll={handleScroll}
|
||||
className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto"
|
||||
>
|
||||
{filteredAvailableProducts.slice(0, visibleProductCount).map((product) => (
|
||||
<li key={product.id} className="p-3 hover:bg-[#F9FAFB] flex justify-between items-center">
|
||||
<div className="text-sm font-medium text-[#111827]">
|
||||
{product.hsCode || product.hs_code || ''}
|
||||
@ -1262,6 +1303,11 @@ const CompanyProfile = () => {
|
||||
|
||||
</li>
|
||||
))}
|
||||
{isLoadingMore && (
|
||||
<div className="flex justify-center p-2">
|
||||
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-[#92722A]"></div>
|
||||
</div>
|
||||
)}
|
||||
</ul>
|
||||
|
||||
) : (
|
||||
|
||||
@ -99,7 +99,10 @@ const EditCompanyProfile = () => {
|
||||
const [isLoadingProducts, setIsLoadingProducts] = useState(false);
|
||||
const [productError, setProductError] = useState("");
|
||||
const [isLoadingCities, setIsLoadingCities] = useState(false);
|
||||
const [visibleProductCount, setVisibleProductCount] = useState(14);
|
||||
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
||||
const initialSnapshotRef = useRef(null);
|
||||
const productListRef = useRef(null);
|
||||
|
||||
const formSteps = React.useMemo(
|
||||
() => [
|
||||
@ -268,21 +271,35 @@ const EditCompanyProfile = () => {
|
||||
const handleProductSearch = (e) => {
|
||||
const searchTerm = e.target.value.toLowerCase();
|
||||
setProductSearchTerm(searchTerm);
|
||||
|
||||
if (!searchTerm) {
|
||||
setAvailableProducts(prev => prev.filter(p =>
|
||||
!selectedProducts.some(sp => sp.value === p.value)
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
const filtered = availableProducts.filter(product =>
|
||||
(product.label?.toLowerCase().includes(searchTerm) ||
|
||||
product.hs_code?.toLowerCase().includes(searchTerm)) &&
|
||||
!selectedProducts.some(sp => sp.value === product.value)
|
||||
);
|
||||
|
||||
setAvailableProducts(filtered);
|
||||
|
||||
// Get the full list of products from the API
|
||||
const loadAndFilterProducts = async () => {
|
||||
try {
|
||||
setIsLoadingProducts(true);
|
||||
const allProducts = await fetchProducts();
|
||||
|
||||
if (!searchTerm) {
|
||||
// If search is cleared, show all products except selected ones
|
||||
setAvailableProducts(allProducts.filter(p =>
|
||||
!selectedProducts.some(sp => sp.value === p.value)
|
||||
));
|
||||
} else {
|
||||
// Filter products based on search term
|
||||
const filtered = allProducts.filter(product =>
|
||||
(product.label?.toLowerCase().includes(searchTerm) ||
|
||||
product.hs_code?.toLowerCase().includes(searchTerm)) &&
|
||||
!selectedProducts.some(sp => sp.value === product.value)
|
||||
);
|
||||
setAvailableProducts(filtered);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error searching products:', error);
|
||||
} finally {
|
||||
setIsLoadingProducts(false);
|
||||
}
|
||||
};
|
||||
|
||||
loadAndFilterProducts();
|
||||
};
|
||||
|
||||
// Add product handler
|
||||
@ -323,6 +340,31 @@ const EditCompanyProfile = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// Reset visible count when products change or search is performed
|
||||
useEffect(() => {
|
||||
setVisibleProductCount(14);
|
||||
}, [availableProducts.length, productSearchTerm]);
|
||||
|
||||
// Handle scroll event for infinite loading
|
||||
const handleScroll = useCallback(() => {
|
||||
if (isLoadingMore || visibleProductCount >= availableProducts.length) return;
|
||||
|
||||
const list = productListRef.current;
|
||||
if (!list) return;
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = list;
|
||||
const isNearBottom = scrollTop + clientHeight >= scrollHeight - 100; // 100px from bottom
|
||||
|
||||
if (isNearBottom) {
|
||||
setIsLoadingMore(true);
|
||||
// Simulate loading delay for better UX
|
||||
setTimeout(() => {
|
||||
setVisibleProductCount(prev => Math.min(prev + 10, availableProducts.length));
|
||||
setIsLoadingMore(false);
|
||||
}, 300);
|
||||
}
|
||||
}, [isLoadingMore, visibleProductCount, availableProducts.length]);
|
||||
|
||||
// Load emirates on component mount using your service function
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
@ -809,21 +851,31 @@ const EditCompanyProfile = () => {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
{/* Left side: Products Header */}
|
||||
<h4 className="text-[16px] font-semibold text-[#232528]">Products</h4>
|
||||
|
||||
{/* Right side: Icons */}
|
||||
<div className="flex items-center gap-4 text-sm text-medium-600">
|
||||
<div className="flex items-center gap-1">
|
||||
<MdAdminPanelSettings className="text-[#92722A] text-lg" />
|
||||
<span>Admin User</span>
|
||||
{/* Left side: Products Header */}
|
||||
<div className="flex items-baseline">
|
||||
<h4 className="text-[16px] font-semibold text-[#232528] whitespace-nowrap">Products</h4>
|
||||
<a
|
||||
href="/assets/images/HS-Code UAE (002).pdf"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="ml-3 text-sm text-black-600 hover:underline"
|
||||
>
|
||||
Please click here to view the list of HS codes
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<FaBuilding className="text-[#92722A]" />
|
||||
<span>Establishment User</span>
|
||||
{/* Right side: Icons */}
|
||||
<div className="flex items-center gap-4 text-sm text-medium-600">
|
||||
<div className="flex items-center gap-1">
|
||||
<MdAdminPanelSettings className="text-[#92722A] text-lg" />
|
||||
<span>Admin User</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1">
|
||||
<FaBuilding className="text-[#92722A]" />
|
||||
<span>Establishment User</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* </div> */}
|
||||
|
||||
{productError && (
|
||||
@ -860,8 +912,12 @@ const EditCompanyProfile = () => {
|
||||
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-[#92722A]"></div>
|
||||
</div>
|
||||
) : availableProducts.length > 0 ? (
|
||||
<ul className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto">
|
||||
{availableProducts.map((product) => (
|
||||
<ul
|
||||
ref={productListRef}
|
||||
onScroll={handleScroll}
|
||||
className="divide-y divide-[#E5E7EB] max-h-96 overflow-y-auto"
|
||||
>
|
||||
{availableProducts.slice(0, visibleProductCount).map((product) => (
|
||||
<li key={product.value} className="p-3 hover:bg-[#F9FAFB] flex justify-between items-center">
|
||||
<div>
|
||||
<div className="text-sm font-medium text-[#111827]">
|
||||
@ -877,6 +933,11 @@ const EditCompanyProfile = () => {
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
{isLoadingMore && (
|
||||
<div className="flex justify-center p-2">
|
||||
<div className="animate-spin rounded-full h-5 w-5 border-b-2 border-[#92722A]"></div>
|
||||
</div>
|
||||
)}
|
||||
</ul>
|
||||
) : (
|
||||
<div className="p-4 text-center text-sm text-[#6B7280]">
|
||||
|
||||
@ -240,7 +240,19 @@ const Login = () => {
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Login failed', err);
|
||||
|
||||
|
||||
// Get the error message from the API response
|
||||
const errorMessage = err.response?.data?.message ||
|
||||
err.message ||
|
||||
'An error occurred during login. Please try again.';
|
||||
|
||||
// Show the exact error message for inactive users
|
||||
if (errorMessage.toLowerCase().includes('inactive')) {
|
||||
showToast('error', errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// For other errors, show the generic message and update login attempts
|
||||
const storedAttempts = JSON.parse(localStorage.getItem('login_attempts') || '{}');
|
||||
const attempts = (storedAttempts[email]?.attempts || 0) + 1;
|
||||
|
||||
@ -290,14 +302,14 @@ const Login = () => {
|
||||
{toast && (
|
||||
<div className="fixed top-6 inset-x-0 z-50 flex justify-center px-4">
|
||||
<div
|
||||
className={`text-sm rounded-lg px-4 py-3 border flex items-start justify-between gap-3 shadow-lg max-w-md ${
|
||||
className={`text-sm rounded-lg px-4 py-3 border flex items-center justify-between gap-3 shadow-lg max-w-md ${
|
||||
toast.type === 'success'
|
||||
? 'bg-[#F3FAF4] border-[#C6E7D8] text-[#2F663C]'
|
||||
: 'bg-[#FEF2F2] border-[#FECACA] text-[#B91C1C]'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start gap-2">
|
||||
{toast.type === 'error' && <AlertCircle className="h-4 w-4 mt-0.5 flex-shrink-0" />}
|
||||
<div className="flex items-center gap-2">
|
||||
{toast.type === 'error' && <AlertCircle className="h-4 w-4 flex-shrink-0" />}
|
||||
<span>{toast.message}</span>
|
||||
</div>
|
||||
<button
|
||||
|
||||
Loading…
Reference in New Issue
Block a user