// Все пользовательские тексты интерфейса — в одном месте. // Так их видно целиком (легко вычитать формулировки) и это задел под локализацию: // компонентам не нужно знать, откуда пришла строка. // // Строки с подстановкой оформлены функциями, чтобы шаблон и данные не разъезжались. import { ComparisonOperator, HeadType, ProjectRole, RouteStepStatus } from './models'; import { pluralRu } from './utils/plural'; // ---- Подписи enum'ов ---- // Record гарантирует, что при добавлении значения в enum // компилятор потребует и подпись к нему. export const routeStepStatusLabels: Record = { [RouteStepStatus.None]: 'Не задан', [RouteStepStatus.Approval]: 'Согласование', [RouteStepStatus.Execution]: 'Исполнение' }; export const projectRoleLabels: Record = { [ProjectRole.None]: '—', [ProjectRole.ProjectManager]: 'Руководитель проекта', [ProjectRole.Planner]: 'Планировщик', [ProjectRole.ManagerHSE]: 'Менеджер HSE', [ProjectRole.Security]: 'Безопасность' }; export const headTypeLabels: Record = { [HeadType.None]: '—', [HeadType.Chief]: 'Непосредственный руководитель', [HeadType.DirectionHead]: 'Руководитель направления', [HeadType.DirectorTansu]: 'Директор Tansu' }; export const comparisonOperatorLabels: Record = { [ComparisonOperator.None]: '—', [ComparisonOperator.GreaterThan]: 'Больше (>)', [ComparisonOperator.LessThan]: 'Меньше (<)', [ComparisonOperator.Equal]: 'Равно (=)' }; export const comparisonOperatorSymbols: Record = { [ComparisonOperator.None]: '', [ComparisonOperator.GreaterThan]: '>', [ComparisonOperator.LessThan]: '<', [ComparisonOperator.Equal]: '=' }; export const texts = { common: { home: 'Главная', cancel: 'Отмена', save: 'Сохранить', saving: 'Сохранение...', remove: 'Удалить', edit: 'Редактировать', close: 'Закрыть', loading: 'Загрузка...', yes: 'Да', no: 'Нет', /** Прочерк вместо пустого значения — чтобы ячейка таблицы не выглядела сломанной. */ empty: '—' }, nav: { /** Буква в золотом квадрате логотипа — не зависит от заголовка панели. */ markLetter: 'T', title: 'Панель настройки маршрутов', dashboard: 'Дашборд', requestTypes: 'Типы заявок', routes: 'Маршруты', resolve: 'Проверка маршрута' }, app: { connecting: 'Подключение к API...', connectFailed: (message: string): string => `Не удалось подключиться к API: ${message}`, connectHint: 'Проверьте свойства веб-части (Api Base Url, режим авторизации) в панели настроек.' }, dashboard: { requestTypesStat: 'Типов заявок', routesStat: 'Маршрутов согласования', sections: 'Разделы', requestTypesCard: { title: 'Типы заявок', description: 'Справочник типов заявок и их страниц' }, routesCard: { title: 'Маршруты согласования', description: 'Маршруты, шаги и исполнители' }, resolveCard: { title: 'Проверка маршрута', description: 'Как разрешится маршрут для конкретного пользователя' } }, requestTypes: { breadcrumb: 'Типы заявок', subtitle: 'Справочник', title: 'Типы заявок', add: 'Добавить тип', emptyList: 'Типов заявок пока нет.', columnName: 'Название', columnPageUrl: 'Url страницы', columnSection: 'Раздел', columnListName: 'Список SharePoint', columnSiteUrl: 'Сайт SharePoint', createTitle: 'Новый тип заявки', editTitle: 'Редактировать тип заявки', nameLabel: 'Название', namePlaceholder: 'Например, Заявка на командировку', pageUrlLabel: 'Url страницы', pageUrlHint: 'Страница формы заявки на портале.', pageUrlPlaceholder: '/pages/business-trip', sectionLabel: 'Раздел', sectionPlaceholder: 'Например, Кадры', siteUrlLabel: 'Сайт SharePoint', siteUrlHint: 'Сайт, где живёт список — например, https://tnsukz.sharepoint.com/sites/hrm.', siteUrlPlaceholder: 'https://tnsukz.sharepoint.com/sites/hrm', siteUrlSearchPlaceholder: 'Поиск сайта по названию...', listNameLabel: 'Список SharePoint', listNameHint: 'Источник полей (field_name) для условий в шагах маршрута.', listNamePlaceholder: 'Сначала укажите сайт SharePoint', listNameLoading: 'Загрузка списков сайта...', listNameEmpty: 'На этом сайте нет доступных списков', listNameError: (message: string): string => `Не удалось получить списки сайта: ${message}`, validationRequired: 'Заполните имя, Url страницы и раздел.', validationListReference: 'Сайт SharePoint и список должны быть заполнены оба вместе или не заполнены вовсе.', deleteTitle: 'Удалить тип заявки?', deleteText: (name: string): string => `Тип «${name}» будет удалён безвозвратно. Связанные маршруты могут перестать разрешаться.` }, routes: { breadcrumb: 'Маршруты согласования', subtitle: 'Настройка согласований', title: 'Маршруты согласования', add: 'Добавить маршрут', filterLabel: 'Фильтр по типу заявки', filterAll: 'Все типы заявок', dictionaryFailed: (message: string): string => `Справочник компаний и отделов недоступен: ${message}`, emptyList: 'Маршрутов пока нет.', columnRequestType: 'Тип заявки', columnCompany: 'Компания', columnDepartment: 'Отдел', columnByProject: 'По проекту', columnControl: 'Контроль', controlRequired: 'Требуется', openSteps: 'Шаги', createTitle: 'Новый маршрут', editTitle: (routeId: number): string => `Маршрут #${routeId}`, requestTypeLabel: 'Тип заявки', requestTypePlaceholder: 'Выберите тип заявки', companyLabel: 'Компания', companyHint: 'Справочник 1С (api.tnsu.kz).', companyPlaceholder: 'Выберите компанию', companySearchPlaceholder: 'Поиск по названию компании...', departmentLabel: 'Отдел', departmentHint: 'Необязательно — пусто означает маршрут «по умолчанию» для всей компании.', departmentHintNoCompany: 'Сначала выберите компанию.', departmentPlaceholder: 'Без отдела (маршрут по умолчанию)', departmentSearchPlaceholder: 'Поиск по названию отдела...', departmentEmpty: 'У выбранной компании нет отделов в справочнике', byProjectLabel: 'По проекту', byProjectCheckbox: 'Маршрут учитывает конкретный проект', controlLabel: 'Контроль', controlCheckbox: 'Требуется отдельный контроль исполнения', validationRequestType: 'Выберите тип заявки.', validationCompany: 'Компания обязательна (Guid из справочника api.tnsu.kz).', deleteTitle: 'Удалить маршрут?', deleteText: (routeId: number): string => `Маршрут #${routeId} и все его шаги будут удалены безвозвратно.` }, routeSteps: { breadcrumb: (routeId: number): string => `Шаги маршрута #${routeId}`, title: (routeId: number): string => `Маршрут #${routeId}`, subtitle: (requestTypeName: string): string => requestTypeName, back: 'К маршрутам', add: 'Добавить шаг', emptyList: 'Шагов пока нет — добавьте первый.', columnOrderNo: '№', columnExecutor: 'Исполнитель', columnStatus: 'Статус', columnReassign: 'Переназначение', columnCondition: 'Условие', reassignAllowed: 'Разрешено', executorRole: (roleLabel: string): string => `Роль: ${roleLabel}`, executorHead: (headLabel: string): string => `Руководитель: ${headLabel}`, createTitle: 'Новый шаг маршрута', editTitle: (orderNo: number): string => `Шаг #${orderNo}`, orderNoLabel: 'Порядковый номер', statusLabel: 'Статус шага', kindLabel: 'Кто исполнитель', kindHint: 'Должно быть заполнено ровно одно из полей.', kindEmail: 'Конкретный сотрудник', kindPosition: 'По должности в компании инициатора', kindProjectRole: 'По роли в проекте', kindProjectRoleDisabled: 'Недоступно: маршрут не учитывает проект (по проекту = нет).', kindHeadType: 'По типу руководителя', executorLabel: 'Сотрудник', executorPlaceholder: 'Выберите сотрудника', positionLabel: 'Должность', positionHint: 'Ищется в справочнике api.tnsu.kz по компании инициатора.', positionPlaceholder: 'Главный инженер', projectRoleLabel: 'Роль в проекте', headTypeLabel: 'Тип руководителя', reassignLabel: 'Переназначение', reassignCheckbox: 'Разрешить переназначать другим согласующим', conditionLabel: 'Условие видимости шага', conditionCheckbox: 'Показывать шаг только при выполнении условия', conditionDisabledHint: 'Недоступно: у типа заявки не указан список SharePoint (list_name).', conditionFieldNameLabel: 'Поле списка', conditionFieldNamePlaceholder: 'Выберите столбец списка', conditionFieldNameLoading: 'Загрузка столбцов списка...', conditionFieldNameEmpty: 'В списке нет доступных столбцов', conditionFieldNameError: (message: string): string => `Не удалось получить столбцы списка: ${message}`, conditionOperationLabel: 'Оператор сравнения', conditionValueLabel: 'Значение', conditionValuePlaceholder: 'Например, 1000', validationOrderNo: 'Порядковый номер шага обязателен.', validationExecutor: 'Выберите сотрудника-исполнителя.', validationPosition: 'Укажите должность.', validationCondition: 'Заполните поле списка, оператор и значение условия.', validationConditionNumber: 'Выбранное поле числовое — значение условия должно быть числом.', deleteTitle: 'Удалить шаг?', deleteText: (orderNo: number): string => `Шаг #${orderNo} будет удалён безвозвратно.` }, resolve: { breadcrumb: 'Проверка маршрута', subtitle: 'Диагностика', title: 'Проверка разрешения маршрута', requestTypeLabel: 'Тип заявки', requestTypePlaceholder: 'Выберите тип заявки', userEmailLabel: 'Инициатор', userEmailHint: (currentUserEmail: string): string => `Пусто = текущий пользователь (${currentUserEmail}).`, userEmailHintUnknown: 'Пусто = текущий пользователь из токена.', userEmailPlaceholder: 'Выберите сотрудника', userEmailClear: 'Текущий пользователь', projectLabel: 'Проект', projectHint: 'Заполните, если маршрут зависит от проекта (ByProject = true).', projectPlaceholder: 'Не выбран', projectLoadingPlaceholder: 'Справочник загружается...', projectSearchPlaceholder: 'Поиск проекта по названию...', projectClear: 'Без проекта', projectDictionaryLoading: 'Справочник проектов ещё загружается.', statusLabel: 'Статус', run: 'Проверить', running: 'Проверка...', validationRequestType: 'Выберите тип заявки.', summary: (routeId: number, requiresControl: boolean): string => `Маршрут #${routeId}${requiresControl ? ' · требуется контроль исполнения' : ''}`, unresolved: (count: number): string => `Исполнитель не определён: ${count} ${pluralRu(count, ['шаг', 'шага', 'шагов'])} — причина указана в строке.`, positionMissing: 'Должность не указана в карточке сотрудника.', columnOrderNo: '№', columnExecutor: 'Исполнитель', columnPosition: 'Должность', columnReassign: 'Переназначение', reassignAllowed: 'Разрешено', emptySteps: 'Маршрут разрешился без шагов для этого статуса.' }, // Значения по умолчанию для переиспользуемых контролов выбора. select: { placeholder: 'Выберите значение', searchPlaceholder: 'Поиск...', nothingFound: 'Ничего не найдено', notSelected: 'Не выбрано', missingInDictionary: 'Нет в справочнике', loadingDictionary: 'Загрузка справочника...', searching: 'Поиск...', employeeSearchPlaceholder: 'Поиск по ФИО или email...', employeesEmpty: 'Сотрудники не найдены в справочнике' } };