bug fixed

This commit is contained in:
Malini 2025-11-09 21:23:36 +05:30
parent b2a01bb935
commit a381b5d30b
4 changed files with 188 additions and 18 deletions

View File

@ -1,4 +1,4 @@
import React from 'react';
import React, { useRef, useEffect } from 'react';
import { NavLink, useNavigate } from 'react-router-dom';
const logoSrc = '/assets/images/FCSCLogo.svg';
@ -40,6 +40,35 @@ const AdminHeader = () => {
const [userName, setUserName] = React.useState('User');
const [userEmail, setUserEmail] = React.useState('user@example.com');
const navigate = useNavigate();
const profileRef = useRef(null);
const mobileMenuRef = useRef(null);
const profileButtonRef = useRef(null);
const mobileMenuButtonRef = useRef(null);
// Handle click outside for profile dropdown and mobile menu
useEffect(() => {
const handleClickOutside = (event) => {
// Close profile dropdown if clicked outside
if (profileOpen && profileRef.current && !profileRef.current.contains(event.target) &&
profileButtonRef.current && !profileButtonRef.current.contains(event.target)) {
setProfileOpen(false);
}
// Close mobile menu if clicked outside
if (open && mobileMenuRef.current && !mobileMenuRef.current.contains(event.target) &&
mobileMenuButtonRef.current && !mobileMenuButtonRef.current.contains(event.target)) {
setOpen(false);
}
};
// Add event listener when the component mounts
document.addEventListener('mousedown', handleClickOutside);
// Clean up the event listener when the component unmounts
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [open, profileOpen]);
React.useEffect(() => {
try {
@ -90,8 +119,9 @@ const AdminHeader = () => {
>
<img src={bellIconSrc} alt="Notifications" className="h-[18px] w-[18px]" />
</button> */}
<div className="relative">
<div className="relative" ref={profileRef}>
<button
ref={profileButtonRef}
type="button"
onClick={() => setProfileOpen((value) => !value)}
className="h-9 w-9 rounded-full bg-[#F2F2F2] grid place-items-center text-[13px] font-medium text-[#232528]"
@ -137,8 +167,9 @@ const AdminHeader = () => {
)}
</div>
</div>
<div className="ml-auto lg:hidden">
<div className="ml-auto lg:hidden" ref={mobileMenuRef}>
<button
ref={mobileMenuButtonRef}
type="button"
aria-label="Open Menu"
className="inline-flex items-center justify-center h-10 w-10 rounded-full border border-[#E5E7EB]"

View File

@ -15,6 +15,7 @@ const clockIcon = '/assets/images/mingcute_time-line.svg';
const calendarIcon = '/assets/images/Duedate.svg';
const nextIcon = '/assets/images/mdi_page-next-outline.svg';
const trashActiveSrc = '/assets/images/Trash - active.svg';
const searchIcon = '/assets/images/material-symbols_search-rounded.svg';
const Input = ({ placeholder = '', type = 'text', value = '', onChange = () => {}, required = false, error = false }) => {
const [isFocused, setIsFocused] = React.useState(false);
@ -46,6 +47,135 @@ const Input = ({ placeholder = '', type = 'text', value = '', onChange = () => {
const SearchableSelect = ({
placeholder = '',
options = [],
value = '',
onChange,
loading = false,
disabled = false,
error = false,
className = ''
}) => {
const [isOpen, setIsOpen] = React.useState(false);
const [searchTerm, setSearchTerm] = React.useState('');
const selectRef = React.useRef(null);
const inputRef = React.useRef(null);
// Close dropdown when clicking outside
React.useEffect(() => {
const handleClickOutside = (event) => {
if (selectRef.current && !selectRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
// Focus input when dropdown opens
React.useEffect(() => {
if (isOpen && inputRef.current) {
inputRef.current.focus();
}
}, [isOpen]);
// Filter options based on search term
const filteredOptions = React.useMemo(() => {
if (!searchTerm) return options;
const term = searchTerm.toLowerCase();
return options.filter(option => {
const label = option.label?.toLowerCase() || '';
const value = option.value?.toString().toLowerCase() || '';
return label.includes(term) || value.includes(term);
});
}, [options, searchTerm]);
// Get selected option label
const selectedOption = options.find(opt => opt.value === value);
const displayValue = selectedOption ? selectedOption.label : '';
// Handle option selection
const handleSelect = (option) => {
if (onChange) {
const event = { target: { value: option.value } };
onChange(event);
}
setSearchTerm('');
setIsOpen(false);
};
let borderColor = 'border-[#CBA344]';
let bgColor = 'bg-white';
if (error) {
borderColor = 'border-red-500';
bgColor = 'bg-red-50';
} else if (value || isOpen) {
borderColor = 'border-[#92722A]';
}
return (
<div className="relative w-full" ref={selectRef}>
<div
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">{displayValue || 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" />
)}
</div>
{isOpen && (
<div className="absolute z-10 w-full mt-1 bg-white rounded-md shadow-lg border border-gray-200">
<div className="p-2 border-b border-gray-200">
<div className="relative">
<input
ref={inputRef}
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Search..."
className="w-full h-9 pl-8 pr-3 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-1 focus:ring-[#92722A]"
onClick={(e) => e.stopPropagation()}
/>
<img
src={searchIcon}
alt="Search"
className="absolute left-2.5 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400"
/>
</div>
</div>
<div className="max-h-60 overflow-auto">
{filteredOptions.length > 0 ? (
filteredOptions.map((option) => (
<div
key={option.value}
className={`px-4 py-2 text-sm cursor-pointer hover:bg-gray-100 ${
value === option.value ? 'bg-[#F5F0E1]' : ''
}`}
onClick={() => handleSelect(option)}
>
{option.label}
</div>
))
) : (
<div className="px-4 py-2 text-sm text-gray-500">No options found</div>
)}
</div>
</div>
)}
</div>
);
};
// Keep the original Select component for other dropdowns
const Select = ({
placeholder = '',
options = [],
@ -847,8 +977,8 @@ const location = useLocation();
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Product (HS Code - Name) <span className="text-red-500">*</span></label>
<div>
<Select
placeholder={isLoadingProducts ? 'Loading products...' : 'Select product HS code'}
<SearchableSelect
placeholder={isLoadingProducts ? 'Loading products...' : 'Search product HS code...'}
options={productOptions}
value={p.productId || p.product || ''}
onChange={async (e) => {
@ -927,11 +1057,20 @@ const location = useLocation();
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Unit <span className="text-red-500">*</span></label>
<Select
placeholder={isLoadingUnits ? 'Loading units...' : 'Select unit'}
<SearchableSelect
placeholder={isLoadingUnits ? 'Loading units...' : 'Search unit...'}
options={unitOptions}
value={p.unit || ''}
onChange={(e) => updateProductField(p.id, 'unit', e.target.value, unitOptions)}
value={p.unit}
onChange={(e) => {
// Clear error when user selects a unit
if (formErrors[`unit_${idx}`]) {
const newErrors = { ...formErrors };
delete newErrors[`unit_${idx}`];
setFormErrors(newErrors);
}
updateProductField(p.id, 'unit', e.target.value, unitOptions);
}}
loading={isLoadingUnits}
error={!!formErrors[`unit_${idx}`]}
/>
<div className="h-5">

View File

@ -133,14 +133,14 @@ const AdminDashboard = () => {
onChange={(e) => setSelectedYear(e.target.value)}
className="border border-[#D0D5DD] rounded-md h-8 px-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#92722A]"
>
{/* <option>All</option> */}
<option>2028</option>
<option>2027</option>
<option>2026</option>
<option>2025</option>
<option>2024</option>
<option>2023</option>
<option>2022</option>
{Array.from(
{ length: new Date().getFullYear() - 2021 },
(_, i) => new Date().getFullYear() - i
).map((year) => (
<option key={year} value={year}>
{year}
</option>
))}
</select>
</div>
</div>

View File

@ -752,7 +752,7 @@ const ManageSubmissions = () => {
<p className="mt-1 text-sm text-red-600">Reason for rejection is required</p>
)}
</div>
<div className="flex justify-end gap-3">
<div className="mt-4 flex justify-end gap-3">
<button
type="button"
onClick={handleCloseRejectModal}