05cd491769
Каждый вид обращения раньше показывал одинаковый набор обязательных полей. getAppealFieldsConfig теперь описывает и видимость, и обязательность отдельно: жалоба — предложение по-прежнему можно оставить, но требовать его как для самостоятельного вида "Предложение" неверно. validateFeedback читает то же правило, что и форма, — иначе одно поле могло бы требовать то, что другое не показывает. FormCard в FeedbackForm дублировал заголовок и подзаголовок PageShell — убран (пустой title у ui-kit не создаёт строки, гэпа не остаётся). Подсказка у переключателя "Обращение анонимное" разбита на две сноски (* и **), как в исходной форме, а не слитный абзац. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
101 lines
4.4 KiB
TypeScript
101 lines
4.4 KiB
TypeScript
import type { ISelectOption } from '@tnsu/ui-kit';
|
||
|
||
/** Вид обращения. Union вместо строк, чтобы опечатку поймал компилятор. */
|
||
export type AppealKind = 'complaint' | 'suggestion' | 'other';
|
||
|
||
/** Данные формы обращения. Одна плоская структура — один источник истины. */
|
||
export interface IFeedbackData {
|
||
company: string;
|
||
/** true — автор не указывает себя и не получит ответ. */
|
||
isAnonymous: boolean;
|
||
fullName: string;
|
||
position: string;
|
||
wantsFeedback: boolean;
|
||
kind: AppealKind;
|
||
/** Заполняется только при kind === 'other'. */
|
||
kindOther: string;
|
||
situation: string;
|
||
suggestion: string;
|
||
}
|
||
|
||
/**
|
||
* Компании группы. В value — реальный Id из справочника, а не slug: именно его
|
||
* ждёт бэкенд, когда появится отправка. Порядок как в источнике (по алфавиту).
|
||
*/
|
||
export const COMPANY_OPTIONS: ISelectOption[] = [
|
||
{ value: '8028abac-ac23-8fd8-11ef-cb57631a9ab0', label: 'ТОО AFL Engineering' },
|
||
{ value: '9de064a4-44c8-88ef-11ec-831c058b1141', label: 'ТОО CL MANAGEMENT' },
|
||
{ value: '9de064a4-44c8-88ef-11ec-8287fb7737a2', label: 'ТОО KazPromService' },
|
||
{ value: 'ec3c78a7-29ef-4725-11ed-a6d8fcd0911d', label: 'ТОО QAZGEOMAX' },
|
||
{ value: '15009595-005d-00fa-11ef-11309f5e395d', label: 'ТОО Selin Invest' },
|
||
{ value: '03f4deac-f543-e8cf-11f1-84eb25329fc3', label: 'ТОО Selin Studio' },
|
||
{ value: '9de064a4-44c8-88ef-11ec-8284930fad96', label: 'ТОО TANSU Construction' },
|
||
{ value: '9de064a4-44c8-88ef-11ec-8285925d83d2', label: 'ТОО АСТЕЛ-К' },
|
||
{ value: '15009595-005d-00fa-11ef-0b8bb5ec1a88', label: 'ТОО ГеоТрек' },
|
||
{ value: '15009595-005d-00fa-11ef-0b8714483372', label: 'ТОО Монтажник-Т' },
|
||
{ value: '8028beac-ac23-8fd8-11f0-5351706e5996', label: 'ТОО ОСН Беркут' },
|
||
{ value: '8028b0ac-ac23-8fd8-11ef-dcdb24b82e02', label: 'ТОО СТ Global' },
|
||
{ value: '15009595-005d-00fa-11ef-1131801d9f3e', label: 'ТОО ТехЛаб' },
|
||
{ value: '03f4deac-f543-e8cf-11f1-84f51483f59e', label: 'Частная компания Seline Holding Ltd' }
|
||
];
|
||
|
||
export const KIND_OPTIONS: ISelectOption[] = [
|
||
{ value: 'complaint', label: 'Жалоба' },
|
||
{ value: 'suggestion', label: 'Предложение' },
|
||
{ value: 'other', label: 'Другое' }
|
||
];
|
||
|
||
/** Показывать поле или нет, и если показывать — обязательно ли оно. */
|
||
export interface IAppealFieldConfig {
|
||
visible: boolean;
|
||
required: boolean;
|
||
}
|
||
|
||
/** Настройка текстовых полей для выбранного вида обращения. */
|
||
export interface IAppealFieldsConfig {
|
||
situation: IAppealFieldConfig;
|
||
suggestion: IAppealFieldConfig;
|
||
}
|
||
|
||
/**
|
||
* Состав полей зависит от вида обращения:
|
||
* жалоба — обязательно описание, а предложение — на желание автора;
|
||
* предложение — только само предложение, «ситуации» ещё нет;
|
||
* другое — свободное описание, без предложения.
|
||
* Одно правило на форму и на валидацию, чтобы они не разошлись.
|
||
*/
|
||
export function getAppealFieldsConfig(kind: AppealKind): IAppealFieldsConfig {
|
||
switch (kind) {
|
||
case 'complaint':
|
||
return {
|
||
situation: { visible: true, required: true },
|
||
suggestion: { visible: true, required: false }
|
||
};
|
||
case 'suggestion':
|
||
return {
|
||
situation: { visible: false, required: false },
|
||
suggestion: { visible: true, required: true }
|
||
};
|
||
case 'other':
|
||
return {
|
||
situation: { visible: true, required: true },
|
||
suggestion: { visible: false, required: false }
|
||
};
|
||
}
|
||
}
|
||
|
||
export const EMPTY_FEEDBACK: IFeedbackData = {
|
||
company: '',
|
||
isAnonymous: false,
|
||
fullName: '',
|
||
position: '',
|
||
wantsFeedback: false,
|
||
kind: 'complaint',
|
||
kindOther: '',
|
||
situation: '',
|
||
suggestion: ''
|
||
};
|
||
|
||
/** Ключи полей, по которым показываем ошибку валидации. */
|
||
export type FeedbackErrors = Partial<Record<keyof IFeedbackData, string>>;
|