diff --git a/frontend/src/webparts/approvalAdmin/components/screens/RoutesScreen.tsx b/frontend/src/webparts/approvalAdmin/components/screens/RoutesScreen.tsx index a92f5b9..ded5d1b 100644 --- a/frontend/src/webparts/approvalAdmin/components/screens/RoutesScreen.tsx +++ b/frontend/src/webparts/approvalAdmin/components/screens/RoutesScreen.tsx @@ -328,6 +328,7 @@ export const RoutesScreen: React.FC = ({ api, dictionary, on clearLabel="Без отдела (маршрут по умолчанию)" placeholder="Без отдела (маршрут по умолчанию)" searchPlaceholder="Поиск по названию отдела..." + emptyMessage="У выбранной компании нет отделов в справочнике" onChange={departmentId => setForm({ ...form, departmentId })} /> diff --git a/frontend/src/webparts/approvalAdmin/components/ui/SearchableSelect.tsx b/frontend/src/webparts/approvalAdmin/components/ui/SearchableSelect.tsx index e476709..d4f0685 100644 --- a/frontend/src/webparts/approvalAdmin/components/ui/SearchableSelect.tsx +++ b/frontend/src/webparts/approvalAdmin/components/ui/SearchableSelect.tsx @@ -20,6 +20,8 @@ export interface ISearchableSelectProps { /** Показывать пункт «очистить» — для необязательных полей. */ allowClear?: boolean; clearLabel?: string; + /** Текст, когда список опций пуст (не результат поиска, а вообще нечего выбирать). */ + emptyMessage?: string; } export const SearchableSelect: React.FC = ({ @@ -31,25 +33,35 @@ export const SearchableSelect: React.FC = ({ disabled, loading, allowClear, - clearLabel = 'Не выбрано' + clearLabel = 'Не выбрано', + emptyMessage = 'Ничего не найдено' }) => { const [open, setOpen] = React.useState(false); const [query, setQuery] = React.useState(''); const rootRef = React.useRef(null); const searchRef = React.useRef(null); - const selected = options.filter(o => o.value === value)[0]; + // Сохранённое значение может отсутствовать в справочнике (запись удалили в 1С + // или она не проходит текущий фильтр). Показать в этом случае плейсхолдер — + // значит соврать, будто поле пустое, и затереть значение при сохранении. + // Поэтому подмешиваем недостающую опцию явным образом. + const effectiveOptions = React.useMemo(() => { + if (!value || options.some(o => o.value === value)) return options; + return [{ value, label: value, sublabel: 'Нет в справочнике' }, ...options]; + }, [options, value]); + + const selected = effectiveOptions.filter(o => o.value === value)[0]; // Поиск на клиенте: справочник целиком уже загружен, отделов ~700 — // фильтровать в памяти дешевле, чем ходить на сервер на каждый символ. const filtered = React.useMemo(() => { const q = query.trim().toLowerCase(); - if (!q) return options; - return options.filter(o => + if (!q) return effectiveOptions; + return effectiveOptions.filter(o => o.label.toLowerCase().indexOf(q) !== -1 || (o.sublabel ? o.sublabel.toLowerCase().indexOf(q) !== -1 : false) ); - }, [options, query]); + }, [effectiveOptions, query]); React.useEffect(() => { if (!open) return; @@ -110,7 +122,11 @@ export const SearchableSelect: React.FC = ({ onChange={e => setQuery(e.target.value)} />
- {filtered.length === 0 &&
Ничего не найдено
} + {filtered.length === 0 && ( +
+ {effectiveOptions.length === 0 ? emptyMessage : 'Ничего не найдено'} +
+ )} {filtered.map(opt => (