Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1060433d0 | |||
| cf039f4d08 |
@@ -207,28 +207,6 @@ export const LANGUAGE_LEVEL_OPTIONS: ISelectOption[] = [
|
|||||||
{ value: 'Свободный', label: 'Свободный' }
|
{ value: 'Свободный', label: 'Свободный' }
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* Программы (ПО) — ВРЕМЕННЫЙ список, пока нет справочника в API. Используется в
|
|
||||||
* мультивыборе «Необходимые знания в программах». Меняется здесь одним местом.
|
|
||||||
*/
|
|
||||||
export const SOFTWARE_OPTIONS: ISelectOption[] = [
|
|
||||||
{ value: '1С:Предприятие', label: '1С:Предприятие' },
|
|
||||||
{ value: 'MS Word', label: 'MS Word' },
|
|
||||||
{ value: 'MS Excel', label: 'MS Excel' },
|
|
||||||
{ value: 'MS PowerPoint', label: 'MS PowerPoint' },
|
|
||||||
{ value: 'MS Outlook', label: 'MS Outlook' },
|
|
||||||
{ value: 'MS Project', label: 'MS Project' },
|
|
||||||
{ value: 'AutoCAD', label: 'AutoCAD' },
|
|
||||||
{ value: 'Autodesk Forma', label: 'Autodesk Forma' },
|
|
||||||
{ value: 'BIM', label: 'BIM' },
|
|
||||||
{ value: 'Revit', label: 'Revit' },
|
|
||||||
{ value: 'ArchiCAD', label: 'ArchiCAD' },
|
|
||||||
{ value: 'Компас-3D', label: 'Компас-3D' },
|
|
||||||
{ value: 'Primavera', label: 'Primavera' },
|
|
||||||
{ value: 'SAP', label: 'SAP' },
|
|
||||||
{ value: 'Adobe Photoshop', label: 'Adobe Photoshop' }
|
|
||||||
];
|
|
||||||
|
|
||||||
/** Категории «Обеспечение рабочего места» (+ «Другое» через allowOther). */
|
/** Категории «Обеспечение рабочего места» (+ «Другое» через allowOther). */
|
||||||
export const WORKPLACE_PROVISION_OPTIONS: ISelectOption[] = [
|
export const WORKPLACE_PROVISION_OPTIONS: ISelectOption[] = [
|
||||||
{ value: 'Оргтехника', label: 'Оргтехника' },
|
{ value: 'Оргтехника', label: 'Оргтехника' },
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { CollapseSection, FormField, TextInput, TextArea, Select, MultiChoiceField } from '../../../shared/ui';
|
import { CollapseSection, FormField, TextInput, TextArea, Select, MultiChoiceField } from '../../../shared/ui';
|
||||||
import { PositionPicker } from '../../../shared/dictionary';
|
import { PositionPicker, SOFTWARE_OPTIONS } from '../../../shared/dictionary';
|
||||||
import { LanguagesField } from './LanguagesField';
|
import { LanguagesField } from './LanguagesField';
|
||||||
import {
|
import {
|
||||||
ISectionProps,
|
ISectionProps,
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
EDUCATION_OPTIONS,
|
EDUCATION_OPTIONS,
|
||||||
WORK_EXPERIENCE_OPTIONS,
|
WORK_EXPERIENCE_OPTIONS,
|
||||||
RESIDENCE_OPTIONS,
|
RESIDENCE_OPTIONS,
|
||||||
SOFTWARE_OPTIONS,
|
|
||||||
OTHER_VALUE
|
OTHER_VALUE
|
||||||
} from '../model';
|
} from '../model';
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,10 @@
|
|||||||
**первого** шага согласования и это текущий пользователь; сохраняются кнопкой
|
**первого** шага согласования и это текущий пользователь; сохраняются кнопкой
|
||||||
«Сохранить» и автоматически при «Согласовать» (`updateReviewFields`,
|
«Сохранить» и автоматически при «Согласовать» (`updateReviewFields`,
|
||||||
`DecisionPanel.onBeforeDecide`). Остальная форма при этом read-only.
|
`DecisionPanel.onBeforeDecide`). Остальная форма при этом read-only.
|
||||||
|
- **Необходимое ПО** — мультивыбор из общего списка `shared/dictionary/software.ts`
|
||||||
|
(тот же, что в подборе) + своё значение. В SP и в маршрут — по-прежнему ТЕКСТОМ
|
||||||
|
в колонке `Software` через запятую (`softwareList.ts`): условия «Software содержит
|
||||||
|
AutoCAD» и старые заявки со свободным текстом работают как раньше.
|
||||||
- **Мебель** — мультивыбор (`FURNITURE_OPTIONS`); при «Другое» появляется поле
|
- **Мебель** — мультивыбор (`FURNITURE_OPTIONS`); при «Другое» появляется поле
|
||||||
своего значения. В SP — `WorkplaceFurnitureJson` (JSON-массив) и
|
своего значения. В SP — `WorkplaceFurnitureJson` (JSON-массив) и
|
||||||
`WorkplaceFurnitureOther`. Необязательная. Уходит в маршрут как
|
`WorkplaceFurnitureOther`. Необязательная. Уходит в маршрут как
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { CollapseSection, FormField, TextArea, Toggle } from '../../../shared/ui';
|
import { CollapseSection, FormField, FormModeProvider, MultiChoiceField, TextArea, Toggle } from '../../../shared/ui';
|
||||||
|
import { SOFTWARE_OPTIONS } from '../../../shared/dictionary';
|
||||||
import { ISectionProps } from '../model';
|
import { ISectionProps } from '../model';
|
||||||
|
import { formatSoftware, parseSoftware } from '../softwareList';
|
||||||
|
|
||||||
interface IEquipmentSectionProps extends ISectionProps {
|
interface IEquipmentSectionProps extends ISectionProps {
|
||||||
/** Согласующий-руководитель на этапе согласования может править ПО и доступы,
|
/** Согласующий-руководитель на этапе согласования может править ПО и доступы,
|
||||||
@@ -13,13 +15,25 @@ export const EquipmentSection: React.FC<IEquipmentSectionProps> = ({ data, set,
|
|||||||
// берёт значение из FormMode (в форме подачи редактируемо, в просмотре — нет).
|
// берёт значение из FormMode (в форме подачи редактируемо, в просмотре — нет).
|
||||||
const fieldReadOnly = approverEditable ? false : undefined;
|
const fieldReadOnly = approverEditable ? false : undefined;
|
||||||
|
|
||||||
|
const software = (
|
||||||
|
<MultiChoiceField
|
||||||
|
value={parseSoftware(data.Software)}
|
||||||
|
onChange={(items) => set('Software', formatSoftware(items))}
|
||||||
|
options={SOFTWARE_OPTIONS}
|
||||||
|
allowOther
|
||||||
|
placeholder="Выберите программу или введите свою"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CollapseSection title="Оборудование и доступы" icon="monitor">
|
<CollapseSection title="Оборудование и доступы" icon="monitor">
|
||||||
<FormField label="Нужна ли техника" fieldKey="NeedsEquipment">
|
<FormField label="Нужна ли техника" fieldKey="NeedsEquipment">
|
||||||
<Toggle checked={data.NeedsEquipment} onChange={(v) => set('NeedsEquipment', v)} onLabel="Да" />
|
<Toggle checked={data.NeedsEquipment} onChange={(v) => set('NeedsEquipment', v)} onLabel="Да" />
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField label="Необходимое ПО" htmlFor="Software" full>
|
<FormField label="Необходимое ПО" fieldKey="Software" full>
|
||||||
<TextArea id="Software" value={data.Software} onChange={(v) => set('Software', v)} readOnly={fieldReadOnly} />
|
{/* У MultiChoiceField нет своего readOnly — право руководителя даём вложенным
|
||||||
|
провайдером режима, как в FurnitureSection. */}
|
||||||
|
{approverEditable ? <FormModeProvider readOnly={false}>{software}</FormModeProvider> : software}
|
||||||
</FormField>
|
</FormField>
|
||||||
<FormField label="Доступы" htmlFor="AccessRights" full>
|
<FormField label="Доступы" htmlFor="AccessRights" full>
|
||||||
<TextArea id="AccessRights" value={data.AccessRights} onChange={(v) => set('AccessRights', v)} readOnly={fieldReadOnly} />
|
<TextArea id="AccessRights" value={data.AccessRights} onChange={(v) => set('AccessRights', v)} readOnly={fieldReadOnly} />
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { formatSoftware, parseSoftware } from './softwareList';
|
||||||
|
|
||||||
|
describe('parseSoftware', () => {
|
||||||
|
it('разбирает сохранённый список', () => {
|
||||||
|
expect(parseSoftware('AutoCAD, MS Excel, Revit')).toEqual(['AutoCAD', 'MS Excel', 'Revit']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('пустой текст — пустой список', () => {
|
||||||
|
expect(parseSoftware('')).toEqual([]);
|
||||||
|
expect(parseSoftware(' , \n ')).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('старый свободный текст с переносами строк — тоже чипами', () => {
|
||||||
|
expect(parseSoftware('AutoCAD\nКорпоративная почта')).toEqual(['AutoCAD', 'Корпоративная почта']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('убирает повторы', () => {
|
||||||
|
expect(parseSoftware('SAP, SAP, Revit')).toEqual(['SAP', 'Revit']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('formatSoftware', () => {
|
||||||
|
it('склеивает через запятую — формат, который ждут условия маршрута', () => {
|
||||||
|
expect(formatSoftware(['AutoCAD', 'Revit'])).toBe('AutoCAD, Revit');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('туда и обратно — без потерь', () => {
|
||||||
|
const items = ['1С:Предприятие', 'MS Word', 'Своя программа'];
|
||||||
|
expect(parseSoftware(formatSoftware(items))).toEqual(items);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/**
|
||||||
|
* «Необходимое ПО» — мультивыбор на экране, но в SP и в маршруте это ТЕКСТ в
|
||||||
|
* колонке Software (условия вида «Software содержит AutoCAD» заведены по нему).
|
||||||
|
* Поэтому модель хранит строку, а раздел переводит её в список и обратно.
|
||||||
|
*/
|
||||||
|
|
||||||
|
const SEPARATOR = ', ';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Текст колонки → список для мультивыбора. Делим по запятым и переносам строк:
|
||||||
|
* у старых заявок ПО написано свободным текстом — так он тоже ляжет чипами.
|
||||||
|
* Повторы убираем: чип с тем же значением был бы дублем.
|
||||||
|
*/
|
||||||
|
export function parseSoftware(text: string): string[] {
|
||||||
|
const items = text
|
||||||
|
.split(/[,\n]/)
|
||||||
|
.map((s) => s.trim())
|
||||||
|
.filter((s) => s.length > 0);
|
||||||
|
return items.filter((s, i) => items.indexOf(s) === i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Список из мультивыбора → текст для колонки Software. */
|
||||||
|
export function formatSoftware(items: string[]): string {
|
||||||
|
return items.join(SEPARATOR);
|
||||||
|
}
|
||||||
@@ -50,3 +50,4 @@ export {
|
|||||||
parseOrganizationLine
|
parseOrganizationLine
|
||||||
} from './organizationLines';
|
} from './organizationLines';
|
||||||
export type { IOrganizationLine } from './organizationLines';
|
export type { IOrganizationLine } from './organizationLines';
|
||||||
|
export { SOFTWARE_OPTIONS } from './software';
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import { ISelectOption } from '@tnsu/ui-kit';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Программы (ПО) — ВРЕМЕННЫЙ список, пока нет справочника в API. Общий для форм:
|
||||||
|
* «Заявка на подбор» (3.8 Необходимые знания в программах) и «Организация
|
||||||
|
* рабочего места» (Необходимое ПО). Пункт добавляется здесь — появляется в обеих.
|
||||||
|
*
|
||||||
|
* Значения уходят в условия маршрута как текст («Software содержит AutoCAD»),
|
||||||
|
* поэтому существующие значения не переименовывать без правки условий в админке.
|
||||||
|
*/
|
||||||
|
export const SOFTWARE_OPTIONS: ISelectOption[] = [
|
||||||
|
{ value: '1С:Предприятие', label: '1С:Предприятие' },
|
||||||
|
{ value: 'MS Word', label: 'MS Word' },
|
||||||
|
{ value: 'MS Excel', label: 'MS Excel' },
|
||||||
|
{ value: 'MS PowerPoint', label: 'MS PowerPoint' },
|
||||||
|
{ value: 'MS Outlook', label: 'MS Outlook' },
|
||||||
|
{ value: 'MS Project', label: 'MS Project' },
|
||||||
|
{ value: 'AutoCAD', label: 'AutoCAD' },
|
||||||
|
{ value: 'Autodesk Forma', label: 'Autodesk Forma' },
|
||||||
|
{ value: 'BIM', label: 'BIM' },
|
||||||
|
{ value: 'Revit', label: 'Revit' },
|
||||||
|
{ value: 'ArchiCAD', label: 'ArchiCAD' },
|
||||||
|
{ value: 'Компас-3D', label: 'Компас-3D' },
|
||||||
|
{ value: 'Primavera', label: 'Primavera' },
|
||||||
|
{ value: 'SAP', label: 'SAP' },
|
||||||
|
{ value: 'Adobe Photoshop', label: 'Adobe Photoshop' }
|
||||||
|
];
|
||||||
Reference in New Issue
Block a user