69 lines
2.6 KiB
JavaScript
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>}
|
|
</>
|
|
)
|
|
}
|