Заглушка для департамента (#8)
Tests / frontend-test (push) Successful in 43s
Tests / backend-test (push) Successful in 2m57s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/8
This commit was merged in pull request #8.
This commit is contained in:
2026-08-07 10:36:53 +00:00
parent 0ffa032ade
commit 1d45780294
14 changed files with 369 additions and 14 deletions
@@ -8,6 +8,7 @@ import { routeStepStatusLabels, texts } from '../../texts';
import { useRequestTypes } from '../../hooks/useRequestTypes';
import { useResolveRoute } from '../../hooks/useResolveRoute';
import { usePortalEmployees } from '../../hooks/usePortalEmployees';
import { usePortalDictionaries } from '../../hooks/usePortalDictionaries';
import { useSharePointFields } from '../../hooks/useSharePointFields';
import { useLocalStorageState } from '../../hooks/useLocalStorageState';
import { findEmployeeByEmail, toEmployeeOption } from '../../utils/employee';
@@ -56,6 +57,7 @@ interface IResolveFormState {
projectLabel: string;
status: RouteStepStatus;
fieldValues: ResolveFieldValue[];
departmentId: string;
}
const EMPTY_RESOLVE_FORM: IResolveFormState = {
@@ -64,13 +66,14 @@ const EMPTY_RESOLVE_FORM: IResolveFormState = {
projectId: '',
projectLabel: '',
status: RouteStepStatus.Approval,
fieldValues: []
fieldValues: [],
departmentId: ''
};
export const ResolveScreen: React.FC<IResolveScreenProps> = ({ api, dictionary, sharePoint, currentUserEmail, route }) => {
const requestTypes = useRequestTypes(api);
const [form, setForm] = useLocalStorageState<IResolveFormState>(RESOLVE_FORM_STORAGE_KEY, EMPTY_RESOLVE_FORM);
const { requestTypeId, userEmail, projectId, projectLabel, status, fieldValues = [] } = form;
const { requestTypeId, userEmail, projectId, projectLabel, status, fieldValues = [], departmentId } = form;
// Открыли по глубокой ссылке (aartype и т.д. в URL) — параметры оттуда
// перекрывают то, что было в localStorage. Только на монтировании: route
@@ -85,7 +88,8 @@ export const ResolveScreen: React.FC<IResolveScreenProps> = ({ api, dictionary,
projectId: route.projectId,
projectLabel: route.projectLabel,
status: route.status,
fieldValues: route.fieldValues
fieldValues: route.fieldValues,
departmentId: route.departmentId
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
@@ -94,8 +98,11 @@ export const ResolveScreen: React.FC<IResolveScreenProps> = ({ api, dictionary,
// которую можно скопировать/сохранить. replaceRoute, а не navigate: иначе
// каждый введённый символ добавлял бы шаг в историю браузера.
React.useEffect(() => {
replaceRoute({ tab: 'resolve', resolve: { requestTypeId, userEmail, projectId, projectLabel, status, fieldValues } });
}, [requestTypeId, userEmail, projectId, projectLabel, status, fieldValues]);
replaceRoute({
tab: 'resolve',
resolve: { requestTypeId, userEmail, projectId, projectLabel, status, fieldValues, departmentId }
});
}, [requestTypeId, userEmail, projectId, projectLabel, status, fieldValues, departmentId]);
const requestType = requestTypes.items.find(rt => rt.id === Number(requestTypeId));
const hasListId = Boolean(requestType?.listId);
@@ -104,6 +111,9 @@ export const ResolveScreen: React.FC<IResolveScreenProps> = ({ api, dictionary,
const resolved = useResolveRoute(api);
const employees = usePortalEmployees(dictionary);
// ~700 отделов — то же соображение, что и в RouteFormModal: дешевле грузить
// список один раз и искать по нему на клиенте, чем ходить в сеть на каждый символ.
const dictionaries = usePortalDictionaries(dictionary);
const searchProjects = React.useCallback(
(query: string, page: number) => {
@@ -143,7 +153,8 @@ export const ResolveScreen: React.FC<IResolveScreenProps> = ({ api, dictionary,
userEmail: userEmail.trim() || undefined,
projectId: projectId.trim() || undefined,
status,
fieldValues: conditions.length > 0 ? conditions : undefined
fieldValues: conditions.length > 0 ? conditions : undefined,
departmentId: departmentId.trim() || undefined
});
};
@@ -197,6 +208,19 @@ export const ResolveScreen: React.FC<IResolveScreenProps> = ({ api, dictionary,
onChange={(nextId, item) => setForm({ ...form, projectId: nextId, projectLabel: item ? item.name : '' })}
/>
</Field>
<Field label={texts.resolve.departmentLabel} hint={texts.resolve.departmentHint}>
<SearchableSelect
options={dictionaries.departments.map(d => ({ value: d.id, label: d.name, sublabel: d.company }))}
value={departmentId}
loading={dictionaries.isLoading}
allowClear
clearLabel={texts.resolve.departmentClear}
placeholder={texts.resolve.departmentPlaceholder}
searchPlaceholder={texts.resolve.departmentSearchPlaceholder}
emptyMessage={texts.resolve.departmentEmpty}
onChange={departmentId => setForm({ ...form, departmentId })}
/>
</Field>
<Field label={texts.resolve.statusLabel} required>
<Select
options={STATUS_OPTIONS}