feat: implemented workpalcesetup form
This commit is contained in:
@@ -17,7 +17,6 @@ import { ApplicationSection } from './sections/ApplicationSection';
|
||||
import { CandidateSection } from './sections/CandidateSection';
|
||||
import { PpeSection } from './sections/PpeSection';
|
||||
import { ConditionsSection } from './sections/ConditionsSection';
|
||||
import { ResponsibleSection } from './sections/ResponsibleSection';
|
||||
|
||||
type ActionStatus = 'idle' | 'saving' | 'success' | 'error';
|
||||
type ActionKind = 'draft' | 'submit';
|
||||
@@ -107,7 +106,6 @@ const RecruitingHireForm: React.FC<IRequestFormProps> = ({ requestType, context,
|
||||
<CandidateSection {...sectionProps} />
|
||||
<PpeSection {...sectionProps} />
|
||||
<ConditionsSection {...sectionProps} />
|
||||
<ResponsibleSection {...sectionProps} />
|
||||
|
||||
<CollapseSection title="Вложения" icon="upload">
|
||||
<FormField label="Файлы" full>
|
||||
|
||||
@@ -12,7 +12,6 @@ import { ApplicationSection } from './sections/ApplicationSection';
|
||||
import { CandidateSection } from './sections/CandidateSection';
|
||||
import { PpeSection } from './sections/PpeSection';
|
||||
import { ConditionsSection } from './sections/ConditionsSection';
|
||||
import { ResponsibleSection } from './sections/ResponsibleSection';
|
||||
|
||||
interface IRecruitingHireViewProps {
|
||||
context: WebPartContext;
|
||||
@@ -81,7 +80,6 @@ const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemI
|
||||
<CandidateSection {...sectionProps} />
|
||||
<PpeSection {...sectionProps} />
|
||||
<ConditionsSection {...sectionProps} />
|
||||
<ResponsibleSection {...sectionProps} />
|
||||
</FormModeProvider>
|
||||
|
||||
<CollapseSection title="Вложения" icon="upload">
|
||||
|
||||
@@ -16,12 +16,7 @@ export async function loadHireRequest(itemId: number): Promise<IFormData> {
|
||||
const item = (await sp.web.lists
|
||||
.getByTitle(LIST_TITLE)
|
||||
.items.getById(itemId)
|
||||
.select(
|
||||
...SCALAR_FIELDS,
|
||||
'SiteManager/EMail',
|
||||
'SecuritySpecialist/EMail'
|
||||
)
|
||||
.expand('SiteManager', 'SecuritySpecialist')()) as Record<string, unknown>;
|
||||
.select(...SCALAR_FIELDS)()) as Record<string, unknown>;
|
||||
|
||||
const data: IFormData = { ...EMPTY_DATA };
|
||||
const writable = data as unknown as Record<string, string>;
|
||||
@@ -37,11 +32,5 @@ export async function loadHireRequest(itemId: number): Promise<IFormData> {
|
||||
}
|
||||
});
|
||||
|
||||
// Люди: из expand берём e-mail (по нему picker покажет ФИО в режиме чтения).
|
||||
const manager = item.SiteManager as { EMail?: string } | undefined;
|
||||
const security = item.SecuritySpecialist as { EMail?: string } | undefined;
|
||||
data.SiteManager = manager?.EMail ?? '';
|
||||
data.SecuritySpecialist = security?.EMail ?? '';
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -26,9 +26,6 @@ export interface IFormData {
|
||||
SalaryAfterProbation: string;
|
||||
WorkSchedule: string;
|
||||
EmploymentForm: string;
|
||||
// Ответственные лица
|
||||
SiteManager: string;
|
||||
SecuritySpecialist: string;
|
||||
}
|
||||
|
||||
/** Обновление одного поля формы с сохранением типа значения. */
|
||||
@@ -57,9 +54,7 @@ export const EMPTY_DATA: IFormData = {
|
||||
SalaryProbation: '',
|
||||
SalaryAfterProbation: '',
|
||||
WorkSchedule: '',
|
||||
EmploymentForm: '',
|
||||
SiteManager: '',
|
||||
SecuritySpecialist: ''
|
||||
EmploymentForm: ''
|
||||
};
|
||||
|
||||
/** Значение «Тип приёма», при котором показывается поле «Взамен кого». */
|
||||
|
||||
@@ -11,8 +11,8 @@
|
||||
{ "internalName": "HireDate", "title": "Дата приёма", "type": "DateTime", "dateOnly": true },
|
||||
{ "internalName": "Position", "title": "Должность", "type": "Text", "required": true },
|
||||
{ "internalName": "WorkKind", "title": "Вид работ", "type": "Text" },
|
||||
{ "internalName": "SiteName", "title": "Наименование участка", "type": "Text" },
|
||||
{ "internalName": "SiteLocation", "title": "Местоположение участка", "type": "Text" },
|
||||
{ "internalName": "SiteName", "title": "Отдел / участок", "type": "Text" },
|
||||
{ "internalName": "SiteLocation", "title": "Местоположение", "type": "Text" },
|
||||
|
||||
{ "internalName": "PpeHeight", "title": "СИЗ: рост", "type": "Number" },
|
||||
{ "internalName": "PpeClothingSize", "title": "СИЗ: размер одежды", "type": "Text" },
|
||||
@@ -21,9 +21,6 @@
|
||||
{ "internalName": "SalaryProbation", "title": "Оклад при испытательном сроке", "type": "Number", "decimals": 2 },
|
||||
{ "internalName": "SalaryAfterProbation", "title": "Оклад по завершении ИС", "type": "Number", "decimals": 2 },
|
||||
{ "internalName": "WorkSchedule", "title": "График работы", "type": "Text" },
|
||||
{ "internalName": "EmploymentForm", "title": "Вид оформления", "type": "Choice", "choices": ["ГПХ", "Штат"] },
|
||||
|
||||
{ "internalName": "SiteManager", "title": "Руководитель участка", "type": "User", "userSelectionMode": "PeopleOnly" },
|
||||
{ "internalName": "SecuritySpecialist", "title": "Специалист СБ", "type": "User", "userSelectionMode": "PeopleOnly" }
|
||||
{ "internalName": "EmploymentForm", "title": "Вид оформления", "type": "Choice", "choices": ["ГПХ", "Штат"] }
|
||||
]
|
||||
}
|
||||
|
||||
@@ -16,10 +16,10 @@ export const CandidateSection: React.FC<ISectionProps> = ({ data, set }) => (
|
||||
<FormField label="Вид работ" htmlFor="WorkKind">
|
||||
<TextInput id="WorkKind" value={data.WorkKind} onChange={(v) => set('WorkKind', v)} />
|
||||
</FormField>
|
||||
<FormField label="Наименование участка" htmlFor="SiteName">
|
||||
<FormField label="Отдел / участок" htmlFor="SiteName">
|
||||
<TextInput id="SiteName" value={data.SiteName} onChange={(v) => set('SiteName', v)} />
|
||||
</FormField>
|
||||
<FormField label="Местоположение участка" htmlFor="SiteLocation">
|
||||
<FormField label="Местоположение" htmlFor="SiteLocation">
|
||||
<TextInput id="SiteLocation" value={data.SiteLocation} onChange={(v) => set('SiteLocation', v)} />
|
||||
</FormField>
|
||||
</CollapseSection>
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import { CollapseSection, FormField, PeopleField } from '../../../shared/ui';
|
||||
import { ISectionProps, PERSON_PLACEHOLDER } from '../model';
|
||||
|
||||
export const ResponsibleSection: React.FC<ISectionProps> = ({ data, set, context }) => (
|
||||
<CollapseSection title="Ответственные лица" icon="user-check">
|
||||
<FormField label="Руководитель участка" full>
|
||||
<PeopleField
|
||||
context={context}
|
||||
value={data.SiteManager}
|
||||
onChange={(v) => set('SiteManager', v)}
|
||||
placeholder={PERSON_PLACEHOLDER}
|
||||
/>
|
||||
</FormField>
|
||||
<FormField label="Специалист СБ" full>
|
||||
<PeopleField
|
||||
context={context}
|
||||
value={data.SecuritySpecialist}
|
||||
onChange={(v) => set('SecuritySpecialist', v)}
|
||||
placeholder={PERSON_PLACEHOLDER}
|
||||
/>
|
||||
</FormField>
|
||||
</CollapseSection>
|
||||
);
|
||||
@@ -8,8 +8,6 @@ export const APPROVAL_LIST_TITLE = 'Прием на работу — согла
|
||||
// (GET /api/request-types → id 5). Захардкожен, как «отпуск = 1» в pto-tracker.
|
||||
export const HIRE_REQUEST_TYPE_ID = 5;
|
||||
|
||||
// Поля-люди (один человек): логин → ensureUser → Id в колонку <Field>Id.
|
||||
const PERSON_FIELDS: (keyof IFormData)[] = ['SiteManager', 'SecuritySpecialist'];
|
||||
// Числовые поля: пустая строка → null, иначе Number().
|
||||
const NUMBER_FIELDS: (keyof IFormData)[] = ['PpeHeight', 'PpeShoeSize', 'SalaryProbation', 'SalaryAfterProbation'];
|
||||
|
||||
@@ -18,12 +16,6 @@ export function hireApprovalService(): ApprovalListService {
|
||||
return new ApprovalListService(APPROVAL_LIST_TITLE);
|
||||
}
|
||||
|
||||
/** Логин → числовой Id пользователя на текущем сайте. */
|
||||
async function resolveUserId(login: string): Promise<number> {
|
||||
const ensured = await getSP().web.ensureUser(login); // v4: возвращает ISiteUserInfo с .Id
|
||||
return ensured.Id;
|
||||
}
|
||||
|
||||
/** Осмысленный заголовок элемента списка (системная колонка Title). */
|
||||
function buildTitle(data: IFormData): string {
|
||||
return ['Прием', data.CandidateName, data.Position].filter(Boolean).join(' — ');
|
||||
@@ -33,11 +25,8 @@ function buildTitle(data: IFormData): string {
|
||||
async function writeContent(data: IFormData, status: string): Promise<number> {
|
||||
const item: Record<string, unknown> = { Title: buildTitle(data), Status: status };
|
||||
|
||||
// Текст/выбор/даты/числа (людей пропускаем — они ниже).
|
||||
// Текст/выбор/даты/числа.
|
||||
(Object.keys(data) as (keyof IFormData)[]).forEach((key) => {
|
||||
if (PERSON_FIELDS.indexOf(key) !== -1) {
|
||||
return;
|
||||
}
|
||||
const value = data[key];
|
||||
if (NUMBER_FIELDS.indexOf(key) !== -1) {
|
||||
item[key] = value === '' ? null : Number(value);
|
||||
@@ -46,14 +35,6 @@ async function writeContent(data: IFormData, status: string): Promise<number> {
|
||||
}
|
||||
});
|
||||
|
||||
// Один человек → <Field>Id.
|
||||
for (let i = 0; i < PERSON_FIELDS.length; i++) {
|
||||
const login = data[PERSON_FIELDS[i]] as string;
|
||||
if (login) {
|
||||
item[`${PERSON_FIELDS[i]}Id`] = await resolveUserId(login);
|
||||
}
|
||||
}
|
||||
|
||||
const added = await getSP().web.lists.getByTitle(DATA_LIST_TITLE).items.add(item);
|
||||
return (added as { Id: number }).Id;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user