fix: внес изменения в заявках рекрутинга от 22.09.2026
Tests / frontend-test (push) Successful in 1m19s

This commit is contained in:
Тимур Городецкий
2026-09-22 12:38:40 +05:00
parent b6708a7e72
commit 64f53b7814
35 changed files with 836 additions and 124 deletions
@@ -1,10 +1,34 @@
import * as React from 'react';
import { Button, Icon, FormActions } from '@tnsu/ui-kit';
import { Button, Icon, FormActions, Alert } from '@tnsu/ui-kit';
import { IRequestSubmit } from '../useRequestSubmit';
import { isCurrentUserTester } from './testerGroupAccess';
/**
* ВРЕМЕННАЯ ЗАМОРОЗКА (включена 2026-09-21 по просьбе Тимура Городецкого):
* приём заявок отключён на ВСЕХ формах портала — и черновик, и отправка на
* согласование заблокированы независимо от заполненности полей. Единая точка
* отключения — здесь, а не по формам, чтобы снять заморозку можно было одной
* правкой (вернуть в false) и не забыть ни одну форму.
*
* Исключение — участники SharePoint-группы «Тестирование» (см.
* testerGroupAccess.ts, добавлено 2026-09-22): для них заморозка не действует,
* чтобы можно было проверять формы, пока для всех остальных приём закрыт.
*
* TODO: снять заморозку (вернуть false), когда будет команда — см. память
* request-portal-submission-freeze.
*/
const SUBMISSION_FROZEN = true;
export interface ISubmitActionsProps {
submit: IRequestSubmit;
isValid: boolean;
/**
* Доп. условие для кнопки «Отправить», когда у отправки более строгие
* требования, чем у черновика (например, поля обязательные только при
* отправке на согласование). Не задано — отправка гейтится тем же isValid,
* что и черновик (поведение по умолчанию для всех остальных форм).
*/
submitValid?: boolean;
/** Заявка в нередактируемом статусе (На согласовании/…): сохранять нельзя. */
readOnly: boolean;
onCancel: () => void;
@@ -21,6 +45,7 @@ export interface ISubmitActionsProps {
export const SubmitActions: React.FC<ISubmitActionsProps> = ({
submit,
isValid,
submitValid,
readOnly,
onCancel,
onSaveDraft,
@@ -28,21 +53,43 @@ export const SubmitActions: React.FC<ISubmitActionsProps> = ({
submitLabel = 'Отправить на согласование',
}) => {
const { saving, kind } = submit;
const locked = saving || readOnly;
// false, пока не пришёл ответ о членстве в группе, — безопасный дефолт: не
// мигать разблокированными кнопками до подтверждения, что это тестировщик.
const [isTester, setIsTester] = React.useState<boolean>(false);
React.useEffect(() => {
if (!SUBMISSION_FROZEN) return;
let alive = true;
isCurrentUserTester()
.then((v) => {
if (alive) setIsTester(v);
})
.catch(() => undefined);
return () => {
alive = false;
};
}, []);
const frozen = SUBMISSION_FROZEN && !isTester;
const locked = saving || readOnly || frozen;
const canSubmit = submitValid ?? isValid;
return (
<FormActions>
<Button variant="secondary" onClick={onCancel} disabled={saving}>
Отмена
</Button>
<Button variant="secondary" onClick={onSaveDraft} disabled={!isValid || locked}>
<Icon name="document" size="sm" />
Сохранить черновик
</Button>
<Button variant="accent" onClick={onSubmit} disabled={!isValid || locked}>
<Icon name="send" size="sm" />
{saving && kind === 'submit' ? 'Отправка…' : submitLabel}
</Button>
</FormActions>
<>
{frozen && !readOnly && <Alert variant="warn">Подача заявок временно недоступна.</Alert>}
<FormActions>
<Button variant="secondary" onClick={onCancel} disabled={saving}>
Отмена
</Button>
<Button variant="secondary" onClick={onSaveDraft} disabled={!isValid || locked}>
<Icon name="document" size="sm" />
Сохранить черновик
</Button>
<Button variant="accent" onClick={onSubmit} disabled={!canSubmit || locked}>
<Icon name="send" size="sm" />
{saving && kind === 'submit' ? 'Отправка…' : submitLabel}
</Button>
</FormActions>
</>
);
};
@@ -0,0 +1,31 @@
import { getSP } from '../../pnp/pnpjs';
/**
* SharePoint-группа «Тестирование» на сайте данных (/sites/requests) — её
* участники исключены из портальной заморозки подачи (см. SUBMISSION_FROZEN в
* SubmitActions.tsx): могут сохранять черновик и отправлять заявки, пока для
* всех остальных это заблокировано. Id группы, а не Title — Title можно
* переименовать в SharePoint без последствий для кода.
*/
const TESTER_GROUP_ID = 124;
// Кешируем на весь сеанс — группа тестировщиков не меняется настолько часто,
// чтобы перезапрашивать её на каждый ремонт SubmitActions (переключение вкладок
// заявок и т.п.). Ошибку не кешируем — временный сбой не должен «залипнуть».
let cached: Promise<boolean> | undefined;
async function fetchIsTester(): Promise<boolean> {
const groups = await getSP().web.currentUser.groups();
return groups.some((g) => g.Id === TESTER_GROUP_ID);
}
/** Состоит ли ТЕКУЩИЙ пользователь в группе «Тестирование» (см. описание выше). */
export function isCurrentUserTester(): Promise<boolean> {
if (!cached) {
cached = fetchIsTester().catch((e) => {
cached = undefined;
throw e;
});
}
return cached;
}