HR-ATS-Portal/frontend/src/ui/DepartmentSelect.jsx

69 lines
2.6 KiB
JavaScript

/* ============================================================
DepartmentSelect — the "From (Dept.)" / "Department" picker.
Wraps SearchSelect over GET /department/names (`{data:[{id,name}]}`) and
hands back both the department's id and its name. The server filters on `search`,
so typing is debounced and SearchSelect is told not to filter again locally.
============================================================ */
import { useEffect, useMemo, useState } from 'react'
import { keepPreviousData, useQuery } from '@tanstack/react-query'
import SearchSelect from './SearchSelect'
import { qk } from '../lib/queryKeys'
import * as departmentsApi from '../api/departments'
/**
* @param value selected department id, '' when none
* @param onChange (id, name) => void — receives ('', '') when cleared
* @param fallbackName name of the saved department, used only to label it when
* the list does not contain it (see below)
*/
export default function DepartmentSelect({ value, onChange, fallbackName = '', disabled = false, error = false }) {
const [q, setQ] = useState('')
const [term, setTerm] = useState('')
useEffect(() => {
const t = setTimeout(() => setTerm(q.trim()), 250)
return () => clearTimeout(t)
}, [q])
const query = useQuery({
queryKey: qk.departments.names(term),
queryFn: async () => departmentsApi.toRows(await departmentsApi.listNames({ search: term })),
placeholderData: keepPreviousData,
retry: false,
})
// Two ways the saved department is missing from `query.data`: it was since
// deactivated (the endpoint returns active only), or the user has typed a
// search that excludes it. Keep it in the list either way so opening the form
// — or typing and then clearing — never silently drops the selection.
const options = useMemo(() => {
const rows = query.data ?? []
const picked = String(value || '')
if (picked && !rows.some((d) => String(d.id) === picked)) {
return [{ id: picked, name: fallbackName || 'Current department' }, ...rows]
}
return rows
}, [query.data, value, fallbackName])
return (
<>
<SearchSelect
options={options}
value={value}
onChange={(id) => onChange(id, options.find((d) => String(d.id) === String(id))?.name ?? '')}
onQueryChange={setQ}
placeholder="Search departments…"
disabled={disabled}
loading={query.isPending && !query.data}
error={error}
allowEmpty
emptyLabel="No department"
/>
{query.isError && <p className="text-muted text-sm">Could not load departments.</p>}
</>
)
}