feat: подсветка незаполненных полей в заявке в отдел 1С
Проверки обязательных полей вынесены в validation.ts и показываются под полями через FieldIssuesProvider, как в приёме, подборе и отпуске: форма прокручивается к первому полю, над кнопками — кликабельный список. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@ import { useState, useMemo, useCallback, useEffect } from 'react';
|
|||||||
import styles from './Ticket1CForm.module.scss';
|
import styles from './Ticket1CForm.module.scss';
|
||||||
import {
|
import {
|
||||||
Alert,
|
Alert,
|
||||||
|
FieldIssuesProvider,
|
||||||
|
issueLabels,
|
||||||
FormCard,
|
FormCard,
|
||||||
InitiatorLine,
|
InitiatorLine,
|
||||||
FormModeProvider,
|
FormModeProvider,
|
||||||
@@ -49,7 +51,8 @@ import {
|
|||||||
dataListTitle,
|
dataListTitle,
|
||||||
dataListTitleForListId
|
dataListTitleForListId
|
||||||
} from '../../shared/listTitles';
|
} from '../../shared/listTitles';
|
||||||
import { FORM_ID, IFormData, EMPTY_DATA, SetField, hasOtherDatabase } from './model';
|
import { FORM_ID, IFormData, EMPTY_DATA, SetField } from './model';
|
||||||
|
import { formIssues } from './validation';
|
||||||
import { saveTicketRequest, ticket1cApprovalService } from './submit';
|
import { saveTicketRequest, ticket1cApprovalService } from './submit';
|
||||||
import { loadTicketRequest } from './load';
|
import { loadTicketRequest } from './load';
|
||||||
import { getApplicantProfile } from './services/profile.service';
|
import { getApplicantProfile } from './services/profile.service';
|
||||||
@@ -240,20 +243,11 @@ const Ticket1CForm: React.FC<IRequestFormProps> = ({
|
|||||||
);
|
);
|
||||||
const matrixEditor = useMemo(() => ({ ...matrix, rows: matrixRows }), [matrix, matrixRows]);
|
const matrixEditor = useMemo(() => ({ ...matrix, rows: matrixRows }), [matrix, matrixRows]);
|
||||||
|
|
||||||
const isOtherDatabaseMissing = hasOtherDatabase(data) && !data.DB1COther.trim();
|
// Проблемы полей (см. validation.ts) подсвечиваются под полями через FieldIssuesProvider.
|
||||||
const areRequiredFilled = !!(
|
const issues = formIssues(data);
|
||||||
data.TaskName.trim() &&
|
const isValid = issues.length === 0;
|
||||||
data.WorkType &&
|
|
||||||
data.Organization &&
|
|
||||||
data.DB1C.length > 0 &&
|
|
||||||
data.Question.trim()
|
|
||||||
);
|
|
||||||
const isValid = areRequiredFilled && !isOtherDatabaseMissing;
|
|
||||||
const validate = (): string | undefined => {
|
const validate = (): string | undefined => {
|
||||||
if (!areRequiredFilled) {
|
if (!isValid) return `Заполните или исправьте поля: ${issueLabels(issues)}.`;
|
||||||
return 'Заполните обязательные поля: наименование задачи, вид работ, организация, база 1С, вопрос.';
|
|
||||||
}
|
|
||||||
if (isOtherDatabaseMissing) return 'Выбрана база 1С «Другое» — укажите её название.';
|
|
||||||
if (requestTypeIdState.error) return `Не удалось определить тип заявки: ${requestTypeIdState.error}`;
|
if (requestTypeIdState.error) return `Не удалось определить тип заявки: ${requestTypeIdState.error}`;
|
||||||
if (!requestTypeId) return 'Тип заявки ещё определяется — подождите немного и повторите.';
|
if (!requestTypeId) return 'Тип заявки ещё определяется — подождите немного и повторите.';
|
||||||
return undefined;
|
return undefined;
|
||||||
@@ -391,7 +385,7 @@ const Ticket1CForm: React.FC<IRequestFormProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<FieldIssuesProvider draftIssues={issues} submitIssues={issues}>
|
||||||
<button type="button" className={styles.back} onClick={onCancel}>
|
<button type="button" className={styles.back} onClick={onCancel}>
|
||||||
<Icon name="arrow-left" size="sm" />
|
<Icon name="arrow-left" size="sm" />
|
||||||
<span>К списку заявок</span>
|
<span>К списку заявок</span>
|
||||||
@@ -486,7 +480,7 @@ const Ticket1CForm: React.FC<IRequestFormProps> = ({
|
|||||||
submitLabel="Отправить"
|
submitLabel="Отправить"
|
||||||
/>
|
/>
|
||||||
</FormCard>
|
</FormCard>
|
||||||
</div>
|
</FieldIssuesProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import type { IFieldIssue } from '../../shared/ui';
|
||||||
|
import { IFormData, hasOtherDatabase } from './model';
|
||||||
|
|
||||||
|
/** Название базы вручную обязательно, только если в «База 1С» отмечено «Другое». */
|
||||||
|
function otherDatabaseIssue(data: IFormData): IFieldIssue | undefined {
|
||||||
|
if (!hasOtherDatabase(data) || data.DB1COther.trim()) return undefined;
|
||||||
|
return { field: 'DB1COther', label: 'Другая база 1С', message: 'выбрано «Другое» — укажите название базы' };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Всё, что мешает сохранить или отправить заявку, в порядке формы (пусто — можно).
|
||||||
|
* field — ключ FormField (htmlFor/fieldKey): по нему поле подсвечивается и к нему
|
||||||
|
* прокручивается форма.
|
||||||
|
*/
|
||||||
|
export function formIssues(data: IFormData): IFieldIssue[] {
|
||||||
|
const issues: Array<IFieldIssue | undefined> = [
|
||||||
|
data.TaskName.trim() ? undefined : { field: 'TaskName', label: 'Наименование задачи' },
|
||||||
|
data.WorkType ? undefined : { field: 'WorkType', label: 'Виды работ' },
|
||||||
|
data.Organization ? undefined : { field: 'Organization', label: 'Организация' },
|
||||||
|
data.DB1C.length > 0 ? undefined : { field: 'DB1C', label: 'База 1С' },
|
||||||
|
otherDatabaseIssue(data),
|
||||||
|
data.Question.trim() ? undefined : { field: 'Question', label: 'Вопрос' }
|
||||||
|
];
|
||||||
|
return issues.filter((issue): issue is IFieldIssue => issue !== undefined);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user