// Все пользовательские тексты интерфейса — в одном месте. // Так их видно целиком (легко вычитать формулировки) и это задел под локализацию: // компонентам не нужно знать, откуда пришла строка. // // Строки с подстановкой оформлены функциями, чтобы шаблон и данные не разъезжались. 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', [HeadType.DepartmentHead]: 'Руководитель указанного отдела' }; 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: { title: 'Панель настройки маршрутов', dashboard: 'Дашборд', requestTypes: 'Типы заявок', routes: 'Маршруты', resolve: 'Проверка маршрута', manageRequests: 'Редактирование заявок' }, 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: 'Раздел', columnListId: 'Список 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: 'Поиск сайта по названию...', listIdLabel: 'Список SharePoint', listIdHint: 'Источник полей (field_name) для условий в шагах маршрута.', listIdPlaceholder: 'Сначала укажите сайт SharePoint', listIdLoading: 'Загрузка списков сайта...', listIdEmpty: 'На этом сайте нет доступных списков', listIdError: (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: 'Необязательно — пусто означает запасной маршрут, если для компании инициатора своего маршрута нет.', companyPlaceholder: 'Без компании (запасной маршрут)', companySearchPlaceholder: 'Поиск по названию компании...', companyClear: 'Без компании (запасной маршрут)', departmentLabel: 'Отдел', departmentHint: 'Необязательно — пусто означает маршрут «по умолчанию» для всей компании.', departmentHintNoCompany: 'Сначала выберите компанию.', departmentPlaceholder: 'Без отдела (маршрут по умолчанию)', departmentSearchPlaceholder: 'Поиск по названию отдела...', departmentEmpty: 'У выбранной компании нет отделов в справочнике', byProjectLabel: 'По проекту', byProjectCheckbox: 'Маршрут учитывает конкретный проект', controlLabel: 'Контроль', controlCheckbox: 'Требуется отдельный контроль исполнения', validationRequestType: 'Выберите тип заявки.', 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: 'Справочник проектов ещё загружается.', departmentLabel: 'Отдел (для шага «Руководитель указанного отдела»)', departmentHint: 'Заполните, если в маршруте есть шаг с типом руководителя «Руководитель указанного отдела».', departmentPlaceholder: 'Не выбран', departmentSearchPlaceholder: 'Поиск по названию отдела...', departmentClear: 'Без отдела', departmentEmpty: 'Отделы не найдены', statusLabel: 'Статус', conditionsLabel: 'Условия (значения полей заявки)', conditionsDisabledHint: 'Недоступно: у выбранного типа заявки не указан список SharePoint (list_name).', conditionFieldPlaceholder: 'Выберите столбец списка', conditionFieldLoading: 'Загрузка столбцов списка...', conditionValuePlaceholder: 'Значение для проверки', conditionAdd: 'Добавить условие', run: 'Проверить', running: 'Проверка...', validationRequestType: 'Выберите тип заявки.', validationConditionField: 'В каждом условии заполните и поле, и значение.', summary: (routeId: number, requiresControl: boolean): string => `Маршрут #${routeId}${requiresControl ? ' · требуется контроль исполнения' : ''}`, unresolved: (count: number): string => `Исполнитель не определён: ${count} ${pluralRu(count, ['шаг', 'шага', 'шагов'])} — причина указана в строке.`, positionMissing: 'Должность не указана в карточке сотрудника.', columnOrderNo: '№', columnExecutor: 'Исполнитель', columnPosition: 'Должность', columnReassign: 'Переназначение', reassignAllowed: 'Разрешено', emptySteps: 'Маршрут разрешился без шагов для этого статуса.' }, manageRequests: { breadcrumb: 'Редактирование заявок', subtitle: 'Правка маршрута согласования', title: 'Редактирование заявок', requestTypeLabel: 'Тип заявки', requestTypePlaceholder: 'Выберите тип заявки', requestTypeEmpty: 'Нет типов заявок с привязанным списком SharePoint.', requestLabel: 'Заявка', requestPlaceholder: 'Выберите заявку', requestSearchPlaceholder: 'Поиск по названию...', requestDisabledHint: 'Сначала выберите тип заявки.', noApprovalList: 'У этого типа заявки список согласования не найден (Title списка данных не оканчивается на «— данные»).', requestStatusLabel: 'Статус заявки', fullyClosedNotice: 'Заявка полностью закрыта — маршрут доступен только для просмотра.', approvalPhaseTitle: 'Согласование', executionPhaseTitle: 'Исполнение', columnOrder: 'Шаг', columnApprover: 'Согласующий', columnPosition: 'Должность', columnStatus: 'Статус', columnDate: 'Дата', emptyPhase: 'Шагов нет.', emptyMatrix: 'У этой заявки маршрут ещё не записан.', reassign: 'Переназначить', remove: 'Удалить шаг', notify: 'Отправить уведомление', notifyNotActive: 'Уведомить можно только текущий активный шаг (не «Ждёт очереди»)', notifySuccess: 'Уведомление отправлено', notifyError: 'Не удалось отправить уведомление', addApproverPlaceholder: 'Добавить согласующего: начните вводить ФИО…', addExecutorPlaceholder: 'Добавить исполнителя: начните вводить ФИО…', lockedHint: 'Решение уже принято — недоступно для правки.', ghostHint: 'Сохраняется…', phaseClosedHint: 'Эта фаза уже завершена — добавить в неё новый шаг нельзя.', requestRejectedHint: 'Заявка отклонена — добавить исполнителя нельзя.', moveUp: 'Шаг выше', moveDown: 'Шаг ниже', mergeWithPrev: 'Объединить с предыдущим шагом (выполнять параллельно)', splitFromPrev: 'Отделить от предыдущего шага (выполнять после него)', moveBlockedByDecision: 'Нельзя переставить — соседний шаг уже решён.', reassignTitle: 'Переназначить шаг', approverLabel: 'Сотрудник', approverPlaceholder: 'Выберите сотрудника', validationApprover: 'Выберите сотрудника.', deleteTitle: 'Удалить шаг?', deleteText: (position: string): string => `Шаг «${position || 'без должности'}» будет удалён безвозвратно.`, orderFloorHint: (order: number): string => `Шаг встанет на позицию ${order} — раньше уже принятых решений в этой фазе поставить нельзя.`, loadError: 'Не удалось загрузить заявку', saveError: 'Не удалось сохранить' }, // Значения по умолчанию для переиспользуемых контролов выбора. select: { placeholder: 'Выберите значение', searchPlaceholder: 'Поиск...', nothingFound: 'Ничего не найдено', notSelected: 'Не выбрано', missingInDictionary: 'Нет в справочнике', loadingDictionary: 'Загрузка справочника...', searching: 'Поиск...', employeeSearchPlaceholder: 'Поиск по ФИО или email...', employeesEmpty: 'Сотрудники не найдены в справочнике' } };