import React, { useEffect, useRef, useState } from "react"; import Label from "../Label"; import { useDebounce } from "../../../utils/appUtils"; import { useEmployeesName } from "../../../hooks/useEmployees"; import { useProjectBothName } from "../../../hooks/useProjects"; import EmployeeRepository from "../../../repositories/EmployeeRepository"; const SelectEmployeeServerSide = ({ label = "Select", placeholder = "Select Employee", required = false, value = null, onChange, valueKey = "id", isFullObject = false, isMultiple = false, projectId = null, isAllEmployee = false, }) => { const [searchText, setSearchText] = useState(""); const debounce = useDebounce(searchText, 300); const [forcedSelected, setForcedSelected] = useState(null); const { data, isLoading } = useEmployeesName( projectId, debounce, isAllEmployee ); const options = data?.data ?? []; const [open, setOpen] = useState(false); const dropdownRef = useRef(null); const getDisplayName = (emp) => { if (!emp) return ""; return `${emp.firstName || ""} ${emp.lastName || ""}`.trim(); }; let selectedSingle = null; if (!isMultiple) { if (isFullObject && value) selectedSingle = value; else if (!isFullObject && value) selectedSingle = options.find((o) => o[valueKey] === value) || forcedSelected; } let selectedList = []; if (isMultiple && Array.isArray(value)) { if (isFullObject) selectedList = value; else { selectedList = options.filter((opt) => value.includes(opt[valueKey])); } } const displayText = !isMultiple ? getDisplayName(selectedSingle) || placeholder : selectedList.length > 0 ? selectedList.map((e) => getDisplayName(e)).join(", ") : placeholder; useEffect(() => { const handleClickOutside = (e) => { if (dropdownRef.current && !dropdownRef.current.contains(e.target)) { setOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); const handleSelect = (option) => { if (!isMultiple) { if (isFullObject) onChange(option); else onChange(option[valueKey]); setOpen(false); } else { let updated = []; const exists = selectedList.some((e) => e[valueKey] === option[valueKey]); updated = exists ? selectedList.filter((e) => e[valueKey] !== option[valueKey]) : [...selectedList, option]; if (isFullObject) onChange(updated); else onChange(updated.map((x) => x[valueKey])); } }; useEffect(() => { if (!value || isFullObject) return; const exists = options.some((o) => o[valueKey] === value); if (exists) return; const loadSingleEmployee = async () => { try { const emp = await EmployeeRepository.getEmployeeName( null, null, true, value ); setForcedSelected(emp.data[0]); } catch (err) { console.error("Failed to load selected employee", err); } }; loadSingleEmployee(); }, [value, options, isFullObject, valueKey]); return (
{label && ( )} {/* MAIN BUTTON */} {open && ( )}
); }; export default SelectEmployeeServerSide; export const SelectProjectField = ({ label = "Select", placeholder = "Select Project", required = false, value = null, onChange, valueKey = "id", isFullObject = false, isMultiple = false, isAllProject = false, }) => { const [searchText, setSearchText] = useState(""); const debounce = useDebounce(searchText, 300); const { data, isLoading } = useProjectBothName(debounce); const options = data ?? []; const [open, setOpen] = useState(false); const dropdownRef = useRef(null); const getDisplayName = (project) => { if (!project) return ""; return `${project.name || ""}`.trim(); }; let selectedSingle = null; if (!isMultiple) { if (isFullObject && value) selectedSingle = value; else if (!isFullObject && value) selectedSingle = options.find((o) => o[valueKey] === value); } let selectedList = []; if (isMultiple && Array.isArray(value)) { if (isFullObject) selectedList = value; else { selectedList = options.filter((opt) => value.includes(opt[valueKey])); } } /** Main button label */ const displayText = !isMultiple ? getDisplayName(selectedSingle) || placeholder : selectedList.length > 0 ? selectedList.map((e) => getDisplayName(e)).join(", ") : placeholder; /** ----------------------------- * HANDLE OUTSIDE CLICK * ----------------------------- */ useEffect(() => { const handleClickOutside = (e) => { if (dropdownRef.current && !dropdownRef.current.contains(e.target)) { setOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); /** ----------------------------- * HANDLE SELECT * ----------------------------- */ const handleSelect = (option) => { if (!isMultiple) { // SINGLE SELECT if (isFullObject) onChange(option); else onChange(option[valueKey]); } else { // MULTIPLE SELECT let updated = []; const exists = selectedList.some((e) => e[valueKey] === option[valueKey]); if (exists) { // remove updated = selectedList.filter((e) => e[valueKey] !== option[valueKey]); } else { // add updated = [...selectedList, option]; } if (isFullObject) onChange(updated); else onChange(updated.map((x) => x[valueKey])); } }; return (
{label && ( )} {/* MAIN BUTTON */} {/* DROPDOWN */} {open && ( )}
); }; export const SelectFieldSearch = ({ label = "Select", placeholder = "Select ", required = false, value = null, onChange, valueKey = "id", labelKey = "name", disabled = false, isFullObject = false, isMultiple = false, hookParams, useFetchHook, }) => { const [searchText, setSearchText] = useState(""); const debounce = useDebounce(searchText, 300); const { data, isLoading } = useFetchHook(...hookParams, debounce); const options = data?.data ?? []; const [open, setOpen] = useState(false); const dropdownRef = useRef(null); const getDisplayName = (entity) => { if (!entity) return ""; return `${entity[labelKey] || ""}`.trim(); }; /** ----------------------------- * SELECTED OPTION (SINGLE) * ----------------------------- */ let selectedSingle = null; if (!isMultiple) { if (isFullObject && value) selectedSingle = value; else if (!isFullObject && value) selectedSingle = options.find((o) => o[valueKey] === value); } /** ----------------------------- * SELECTED OPTION (MULTIPLE) * ----------------------------- */ let selectedList = []; if (isMultiple && Array.isArray(value)) { if (isFullObject) selectedList = value; else { selectedList = options.filter((opt) => value.includes(opt[valueKey])); } } /** Main button label */ const displayText = !isMultiple ? getDisplayName(selectedSingle) || placeholder : selectedList.length > 0 ? selectedList.map((e) => getDisplayName(e)).join(", ") : placeholder; /** ----------------------------- * HANDLE OUTSIDE CLICK * ----------------------------- */ useEffect(() => { const handleClickOutside = (e) => { if (dropdownRef.current && !dropdownRef.current.contains(e.target)) { setOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); // MERGED OPTIONS TO ENSURE SELECTED VALUE APPEARS EVEN IF NOT IN SEARCH RESULT const [mergedOptions, setMergedOptions] = useState([]); useEffect(() => { let finalList = [...options]; if (!isMultiple && value && !isFullObject) { // already selected option inside options? const exists = options.some((o) => o[valueKey] === value); // if selected item not found, try to get from props (value) as fallback if (!exists && typeof value === "object") { finalList.unshift(value); } } if (isMultiple && Array.isArray(value)) { value.forEach((val) => { const id = isFullObject ? val[valueKey] : val; const exists = options.some((o) => o[valueKey] === id); if (!exists && typeof val === "object") { finalList.unshift(val); } }); } setMergedOptions(finalList); }, [options, value]); /** ----------------------------- * HANDLE SELECT * ----------------------------- */ const handleSelect = (option) => { if (!isMultiple) { // SINGLE SELECT if (isFullObject) onChange(option); else onChange(option[valueKey]); } else { // MULTIPLE SELECT let updated = []; const exists = selectedList.some((e) => e[valueKey] === option[valueKey]); if (exists) { // remove updated = selectedList.filter((e) => e[valueKey] !== option[valueKey]); } else { // add updated = [...selectedList, option]; } if (isFullObject) onChange(updated); else onChange(updated.map((x) => x[valueKey])); } }; return (
{label && ( )} {/* MAIN BUTTON */} {/* DROPDOWN */} {open && ( )}
); };