Заявка на услуги (#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
@@ -52,6 +52,7 @@ import { DepartmentSection } from './sections/DepartmentSection';
import { VacancySection } from './sections/VacancySection';
import { HealthTripsSection } from './sections/HealthTripsSection';
import { ConditionsSection } from './sections/ConditionsSection';
import { useStableCallback } from '../../shared/useStableCallback';
const RecruitingSelectionForm: React.FC<IRequestFormProps> = ({
requestType,
@@ -69,6 +70,14 @@ const RecruitingSelectionForm: 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 }));
@@ -86,20 +95,19 @@ const RecruitingSelectionForm: 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);
setExistingAttachments(atts);
}
})
.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.company/companyId) → предзаполнение
// «1.6 Организация» (Companies/CompanyPickerMulti). companyId из Employee/ByEmail
@@ -132,8 +140,9 @@ const RecruitingSelectionForm: React.FC<IRequestFormProps> = ({
return () => {
alive = false;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editId]);
// context — единственный экземпляр WebPartContext на всё время жизни
// веб-части, так что перезапуска эффекта эта зависимость не добавляет.
}, [editId, context]);
// Выбор другого ответственного подтягивает его подразделение и организацию.
const handleResponsibleChange = (login: string): void => {
@@ -29,6 +29,7 @@ import { DepartmentSection } from './sections/DepartmentSection';
import { VacancySection } from './sections/VacancySection';
import { HealthTripsSection } from './sections/HealthTripsSection';
import { ConditionsSection } from './sections/ConditionsSection';
import { useStableCallback } from '../../shared/useStableCallback';
interface IRecruitingSelectionViewProps {
context: WebPartContext;
@@ -47,6 +48,11 @@ const NOOP_SET: SetField = () => undefined;
// Заявка «на подбор» только для просмотра (режим согласующего). Переиспользует те же
// секции, что и форма подачи; редактирование выключено через <FormModeProvider readOnly>.
const RecruitingSelectionView: React.FC<IRecruitingSelectionViewProps> = ({ 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>('');
@@ -80,7 +86,7 @@ const RecruitingSelectionView: React.FC<IRecruitingSelectionViewProps> = ({ cont
setData(loaded.data);
setStatus(loaded.status);
setCode(loaded.code);
onCodeLoaded?.(loaded.code);
notifyCode(loaded.code);
setAttachments(files);
setError(null);
setLoading(false);
@@ -95,7 +101,7 @@ const RecruitingSelectionView: React.FC<IRecruitingSelectionViewProps> = ({ cont
return () => {
alive = false;
};
}, [itemId, listId]);
}, [itemId, listId, notifyCode]);
if (loading) {
return <div className={styles.state}>Загрузка заявки…</div>;