fix: changed resolve endpoint contract form get to post
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
@import '../../../../styles/design-tokens';
|
||||
|
||||
.state {
|
||||
padding: $sp-8;
|
||||
color: $gray-500;
|
||||
font-size: $fs-sm;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
import * as React from 'react';
|
||||
import { useState, useEffect, useMemo } from 'react';
|
||||
import type { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
import styles from './RecruitingHireView.module.scss';
|
||||
import { Alert, FormCard, FormModeProvider } from '../../shared/ui';
|
||||
import { ApproverService, DecisionPanel } from '../../shared/approval';
|
||||
import { IFormData, EMPTY_DATA, SetField } from './model';
|
||||
import { loadHireRequest } from './load';
|
||||
import { hireApprovalService, DATA_LIST_TITLE } from './submit';
|
||||
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;
|
||||
itemId: number;
|
||||
}
|
||||
|
||||
// В режиме чтения поля не редактируются, поэтому обновление — пустышка.
|
||||
const NOOP_SET: SetField = () => undefined;
|
||||
|
||||
// Заявка «на прием» только для просмотра (режим согласующего).
|
||||
// Переиспользует те же секции, что и форма подачи; редактирование выключено
|
||||
// через <FormModeProvider readOnly> — поля сами становятся неизменяемыми.
|
||||
const RecruitingHireView: React.FC<IRecruitingHireViewProps> = ({ context, itemId }) => {
|
||||
const [data, setData] = useState<IFormData>(EMPTY_DATA);
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
// Сервис решений согласующего: список согласования + список данных этой формы.
|
||||
const approverService = useMemo(
|
||||
() => new ApproverService(hireApprovalService(), DATA_LIST_TITLE),
|
||||
[]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
setLoading(true);
|
||||
loadHireRequest(itemId)
|
||||
.then((loaded) => {
|
||||
if (alive) {
|
||||
setData(loaded);
|
||||
setError(null);
|
||||
setLoading(false);
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
if (alive) {
|
||||
setError(`Не удалось загрузить заявку №${itemId}: ${(e as Error).message}`);
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
alive = false;
|
||||
};
|
||||
}, [itemId]);
|
||||
|
||||
if (loading) {
|
||||
return <div className={styles.state}>Загрузка заявки…</div>;
|
||||
}
|
||||
if (error) {
|
||||
return (
|
||||
<div className={styles.state}>
|
||||
<Alert variant="err">{error}</Alert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const sectionProps = { data, set: NOOP_SET, context };
|
||||
|
||||
return (
|
||||
<div>
|
||||
<FormCard title="Заявка на прием" subtitle={`№${itemId} · режим согласования`}>
|
||||
<FormModeProvider readOnly>
|
||||
<ApplicationSection {...sectionProps} />
|
||||
<CandidateSection {...sectionProps} />
|
||||
<PpeSection {...sectionProps} />
|
||||
<ConditionsSection {...sectionProps} />
|
||||
<ResponsibleSection {...sectionProps} />
|
||||
</FormModeProvider>
|
||||
</FormCard>
|
||||
|
||||
{/* Панель решения — ВНЕ FormModeProvider, чтобы комментарий был редактируемым. */}
|
||||
<DecisionPanel service={approverService} requestId={itemId} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RecruitingHireView;
|
||||
@@ -0,0 +1,47 @@
|
||||
import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { IFormData, EMPTY_DATA } from './model';
|
||||
|
||||
const LIST_TITLE = 'Прием на работу — данные';
|
||||
|
||||
// Скалярные поля (текст/выбор/число/дата) — читаем как есть, приводим к строке.
|
||||
const SCALAR_FIELDS: (keyof IFormData)[] = [
|
||||
'Phone', 'HireType', 'ReplacedEmployee', 'CandidateName', 'HireDate', 'Position', 'WorkKind',
|
||||
'SiteName', 'SiteLocation', 'PpeHeight', 'PpeClothingSize', 'PpeShoeSize', 'SalaryProbation',
|
||||
'SalaryAfterProbation', 'WorkSchedule', 'EmploymentForm'
|
||||
];
|
||||
|
||||
/** Прочитать заявку «на прием» по id и разложить в модель формы (для режима чтения). */
|
||||
export async function loadHireRequest(itemId: number): Promise<IFormData> {
|
||||
const sp = getSP();
|
||||
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>;
|
||||
|
||||
const data: IFormData = { ...EMPTY_DATA };
|
||||
const writable = data as unknown as Record<string, string>;
|
||||
|
||||
SCALAR_FIELDS.forEach((key) => {
|
||||
const value = item[key];
|
||||
if (value === null || value === undefined) {
|
||||
writable[key] = '';
|
||||
} else if (key === 'HireDate' && typeof value === 'string') {
|
||||
writable[key] = value.slice(0, 10); // ISO → YYYY-MM-DD для input type=date
|
||||
} else {
|
||||
writable[key] = String(value);
|
||||
}
|
||||
});
|
||||
|
||||
// Люди: из 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;
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import { getSP } from '../../shared/pnp/pnpjs';
|
||||
import { ApprovalListService, IApproverToWrite, writeApprovers } from '../../shared/approval';
|
||||
import { IFormData } from './model';
|
||||
|
||||
const LIST_TITLE = 'Прием на работу — данные';
|
||||
export const DATA_LIST_TITLE = 'Прием на работу — данные';
|
||||
export const APPROVAL_LIST_TITLE = 'Прием на работу — согласование';
|
||||
// Тип заявки «Заявка на прием» во внешнем approval-route API
|
||||
// (GET /api/request-types → id 5). Захардкожен, как «отпуск = 1» в pto-tracker.
|
||||
@@ -54,7 +54,7 @@ async function writeContent(data: IFormData, status: string): Promise<number> {
|
||||
}
|
||||
}
|
||||
|
||||
const added = await getSP().web.lists.getByTitle(LIST_TITLE).items.add(item);
|
||||
const added = await getSP().web.lists.getByTitle(DATA_LIST_TITLE).items.add(item);
|
||||
return (added as { Id: number }).Id;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import * as React from 'react';
|
||||
import type { WebPartContext } from '@microsoft/sp-webpart-base';
|
||||
|
||||
export interface IApprovalViewProps {
|
||||
context: WebPartContext;
|
||||
itemId: number;
|
||||
}
|
||||
|
||||
type ViewComponent = React.ComponentType<IApprovalViewProps>;
|
||||
|
||||
/**
|
||||
* Реестр форм «только для чтения» (режим согласующего): ключ формы → ленивый просмотр.
|
||||
* Добавить просмотр = создать <Форма>View.tsx + строка здесь.
|
||||
*/
|
||||
const viewRegistry: Record<string, React.LazyExoticComponent<ViewComponent>> = {
|
||||
'recruiting-hire': React.lazy(
|
||||
() => import(/* webpackChunkName: 'view-recruiting-hire' */ './RecruitingHire/RecruitingHireView')
|
||||
)
|
||||
};
|
||||
|
||||
export function getViewComponent(
|
||||
formKey: string
|
||||
): React.LazyExoticComponent<ViewComponent> | undefined {
|
||||
return viewRegistry[formKey];
|
||||
}
|
||||
Reference in New Issue
Block a user