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 && (
)}
);
};