fix(MultiSelect): enhance item handling and improve empty state display
- Update MultiSelect component to allow items to be optional or null, improving flexibility. - Implement safe handling of items to prevent errors when items are not provided. - Add a message to display when no items are available, enhancing user experience. - Refactor rendering logic for selected items to maintain consistent styling.
This commit is contained in:
@@ -17,7 +17,7 @@ interface Option {
|
|||||||
|
|
||||||
type MultiSelectProps<T extends FieldValues> = {
|
type MultiSelectProps<T extends FieldValues> = {
|
||||||
label: string;
|
label: string;
|
||||||
items: { value: string; label: string }[];
|
items?: { value: string; label: string }[] | null;
|
||||||
value?: string[];
|
value?: string[];
|
||||||
errors?: FieldErrors<T>;
|
errors?: FieldErrors<T>;
|
||||||
required?: boolean;
|
required?: boolean;
|
||||||
@@ -54,13 +54,15 @@ function MultiSelect<T extends FieldValues>({
|
|||||||
const trigger = useRef<HTMLDivElement | null>(null);
|
const trigger = useRef<HTMLDivElement | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const newOptions = items.map((item) => ({
|
const safeItems = Array.isArray(items) ? items : [];
|
||||||
|
|
||||||
|
const newOptions = safeItems.map((item) => ({
|
||||||
...item,
|
...item,
|
||||||
selected: value?.includes(item.value) ?? false,
|
selected: value?.includes(item.value) ?? false,
|
||||||
}));
|
}));
|
||||||
setOptions(newOptions);
|
setOptions(newOptions);
|
||||||
|
|
||||||
const newSelectedIndices = items
|
const newSelectedIndices = safeItems
|
||||||
.map((item, index) => (value?.includes(item.value) ? index : -1))
|
.map((item, index) => (value?.includes(item.value) ? index : -1))
|
||||||
.filter((index) => index !== -1);
|
.filter((index) => index !== -1);
|
||||||
setSelected(newSelectedIndices);
|
setSelected(newSelectedIndices);
|
||||||
@@ -215,40 +217,46 @@ function MultiSelect<T extends FieldValues>({
|
|||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
>
|
>
|
||||||
<div className="py-1">
|
<div className="py-1">
|
||||||
{options.map((option, index) => (
|
{options.length === 0 ? (
|
||||||
<div
|
<div className="px-3 py-2 text-sm text-dark-6 dark:text-dark-6">
|
||||||
key={index}
|
No items are available
|
||||||
className={cn(
|
|
||||||
"flex cursor-pointer items-center px-3 py-2 text-sm text-dark hover:bg-primary/5 dark:text-white dark:hover:bg-primary/5",
|
|
||||||
option.selected && "bg-primary/10 dark:bg-primary/20",
|
|
||||||
)}
|
|
||||||
onClick={() => handleSelect(index)}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"mr-3 h-4 w-4 rounded border-2",
|
|
||||||
option.selected
|
|
||||||
? "border-primary bg-primary"
|
|
||||||
: "border-stroke dark:border-dark-3",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{option.selected && (
|
|
||||||
<svg
|
|
||||||
className="h-full w-full text-white"
|
|
||||||
fill="currentColor"
|
|
||||||
viewBox="0 0 20 20"
|
|
||||||
>
|
|
||||||
<path
|
|
||||||
fillRule="evenodd"
|
|
||||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
|
||||||
clipRule="evenodd"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{option.label}
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
) : (
|
||||||
|
options.map((option, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-pointer items-center px-3 py-2 text-sm text-dark hover:bg-primary/5 dark:text-white dark:hover:bg-primary/5",
|
||||||
|
option.selected && "bg-primary/10 dark:bg-primary/20",
|
||||||
|
)}
|
||||||
|
onClick={() => handleSelect(index)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"mr-3 h-4 w-4 rounded border-2",
|
||||||
|
option.selected
|
||||||
|
? "border-primary bg-primary"
|
||||||
|
: "border-stroke dark:border-dark-3",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{option.selected && (
|
||||||
|
<svg
|
||||||
|
className="h-full w-full text-white"
|
||||||
|
fill="currentColor"
|
||||||
|
viewBox="0 0 20 20"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fillRule="evenodd"
|
||||||
|
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||||
|
clipRule="evenodd"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{option.label}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { Tooltip } from "react-tooltip";
|
|||||||
import useTenderListPresenter from "./useTenderListPresenter";
|
import useTenderListPresenter from "./useTenderListPresenter";
|
||||||
|
|
||||||
import { ExclamationIcon, EyeIcon } from "@/assets/icons";
|
import { ExclamationIcon, EyeIcon } from "@/assets/icons";
|
||||||
import EmptyListWrapper from "@/components/HOC/EmptyListWrapper";
|
|
||||||
import ListHeader from "@/components/ui/ListHeader";
|
import ListHeader from "@/components/ui/ListHeader";
|
||||||
import Modal from "@/components/ui/modal";
|
import Modal from "@/components/ui/modal";
|
||||||
import Pagination from "@/components/ui/pagination";
|
import Pagination from "@/components/ui/pagination";
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export const useCreateCompanyCategory = (successCallback?: () => void) => {
|
|||||||
queryClient.invalidateQueries({
|
queryClient.invalidateQueries({
|
||||||
queryKey: [API_ENDPOINTS.COMPANIES.CATEGORIES.READ_ALL],
|
queryKey: [API_ENDPOINTS.COMPANIES.CATEGORIES.READ_ALL],
|
||||||
});
|
});
|
||||||
router.push("/companies/categories");
|
router.push("/company-categories");
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user