Заявка на услуги (#18)
Tests / frontend-test (push) Successful in 1m15s

Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz>
Reviewed-on: http://git.tnsu.kz/timur.gorodetskiy/request-portal/pulls/18
This commit was merged in pull request #18.
This commit is contained in:
2026-09-01 12:48:41 +00:00
parent ddce6b4f25
commit cc2efd8771
56 changed files with 2557 additions and 97 deletions
@@ -38,6 +38,7 @@ import { FORM_ID, IFormData, EMPTY_DATA, SetField, MIN_NOTICE_MONTHS, IOrganizat
import { saveDismissalRequest, dismissalApprovalService } from './submit';
import { loadDismissalRequest } from './load';
import { RequestSection } from './sections/RequestSection';
import { useStableCallback } from '../../shared/useStableCallback';
const DismissalForm: React.FC<IRequestFormProps> = ({
requestType,
@@ -53,6 +54,14 @@ const DismissalForm: React.FC<IRequestFormProps> = ({
const [requestStatus, setRequestStatus] = useState<string | undefined>(undefined);
const [requestCode, setRequestCode] = useState<string | undefined>(undefined);
const submit = useRequestSubmit();
// onCodeLoaded — колбэк родителя (RequestPortal → EditFeature): его ссылка
// может меняться на каждом рендере, поэтому в зависимости эффектов идёт
// стабильная обёртка, а не сам проп — иначе заявка перезапрашивалась бы при
// каждом рендере родителя.
const notifyCode = useStableCallback(onCodeLoaded);
// submit целиком в зависимости класть нельзя (пересоздаётся при каждой смене
// saving/error), а setError стабилен — его и берём.
const { setError: setSubmitError } = submit;
const readOnly = !isEditableStatus(requestStatus);
const set: SetField = (key, value) => setData((prev) => ({ ...prev, [key]: value }));
@@ -82,19 +91,18 @@ const DismissalForm: React.FC<IRequestFormProps> = ({
setData(loaded.data);
setRequestStatus(loaded.status);
setRequestCode(loaded.code || undefined);
if (loaded.code) onCodeLoaded?.(loaded.code);
if (loaded.code) notifyCode(loaded.code);
}
})
.catch((e) => {
if (alive) {
submit.setError(`Не удалось загрузить заявку №${editId}: ${friendlyErrorMessage(e)}`);
setSubmitError(`Не удалось загрузить заявку №${editId}: ${friendlyErrorMessage(e)}`);
}
});
return () => {
alive = false;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editId, editListId]);
}, [editId, editListId, notifyCode, setSubmitError]);
/** Employee/ByEmail → таблица организаций (см. IOrganizationRecord). */
const toOrganization = (records: IEmployeeOrganization[]): IOrganizationRecord[] =>
@@ -124,8 +132,9 @@ const DismissalForm: React.FC<IRequestFormProps> = ({
return () => {
alive = false;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editId]);
// context — единственный экземпляр WebPartContext на всё время жизни
// веб-части, так что перезапуска эффекта эта зависимость не добавляет.
}, [editId, context]);
// Выбор другого сотрудника подтягивает его таблицу организаций.
const handleEmployeeChange = (login: string): void => {
@@ -29,6 +29,7 @@ import { dismissalApprovalService, updateAssetsAcceptor } from './submit';
import { loadClearanceRequest } from '../Clearance/load';
import { ensureClearanceForDismissal } from './createClearance';
import { RequestSection } from './sections/RequestSection';
import { useStableCallback } from '../../shared/useStableCallback';
/**
* Статусы обходного листа, при которых его СОГЛАСОВАНИЕ уже пройдено целиком
@@ -56,6 +57,11 @@ interface IDismissalViewProps {
// Переиспользует ту же секцию, что и форма подачи; редактирование выключено через
// <FormModeProvider readOnly> — поля сами становятся неизменяемыми.
const DismissalView: React.FC<IDismissalViewProps> = ({ context, itemId, formKey, listId, onCodeLoaded }) => {
// onCodeLoaded — колбэк родителя (RequestPortal → ApproveFeature): его ссылка
// может меняться на каждом рендере, поэтому в зависимости эффекта загрузки
// идёт стабильная обёртка, а не сам проп — иначе заявка перезапрашивалась бы
// при каждом рендере родителя.
const notifyCode = useStableCallback(onCodeLoaded);
const [data, setData] = useState<IFormData>(EMPTY_DATA);
const [status, setStatus] = useState<string>('');
const [code, setCode] = useState<string>('');
@@ -94,7 +100,7 @@ const DismissalView: React.FC<IDismissalViewProps> = ({ context, itemId, formKey
setData(loaded.data);
setStatus(loaded.status);
setCode(loaded.code);
onCodeLoaded?.(loaded.code);
notifyCode(loaded.code);
setError(null);
setLoading(false);
}
@@ -108,7 +114,7 @@ const DismissalView: React.FC<IDismissalViewProps> = ({ context, itemId, formKey
return () => {
alive = false;
};
}, [itemId, listId]);
}, [itemId, listId, notifyCode]);
// Печать заявления запрещена до согласования ПАРНОГО обходного листа: сначала
// должны отработать/подтвердиться все связанные с увольнением отметки (карты,