Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/5
This commit was merged in pull request #5.
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
import * as React from 'react';
|
||||
import {
|
||||
Employee,
|
||||
HeadType,
|
||||
ProjectRole,
|
||||
RouteStepStatus,
|
||||
StepExecutorKind,
|
||||
TemplateRoute,
|
||||
TemplateRouteStep
|
||||
} from '../../models';
|
||||
import { headTypeLabels, projectRoleLabels, routeStepStatusLabels, texts } from '../../texts';
|
||||
import { detectExecutorKind } from '../../utils/stepExecutor';
|
||||
import { toEmployeeOption } from '../../utils/employee';
|
||||
import { useAsyncSubmit } from '../../hooks/useAsyncSubmit';
|
||||
import { ApprovalRouteApi } from '../../services/api';
|
||||
import { Checkbox, Field, FormGrid, FormModal, RadioGroup, SearchableSelect, Select, TextInput } from '../ui';
|
||||
|
||||
export interface IRouteStepFormModalProps {
|
||||
api: ApprovalRouteApi;
|
||||
route: TemplateRoute;
|
||||
/** Undefined — создание нового шага, иначе редактирование существующего. */
|
||||
editing?: TemplateRouteStep;
|
||||
nextOrderNo: number;
|
||||
employees: Employee[];
|
||||
employeesLoading: boolean;
|
||||
onSaved: (savedStep: TemplateRouteStep) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
interface IFormState {
|
||||
orderNo: string;
|
||||
status: RouteStepStatus;
|
||||
kind: StepExecutorKind;
|
||||
executorEmail: string;
|
||||
position: string;
|
||||
projectRole: ProjectRole;
|
||||
headType: HeadType;
|
||||
allowReassignToOtherApprovers: boolean;
|
||||
}
|
||||
|
||||
const STATUS_OPTIONS = [RouteStepStatus.Approval, RouteStepStatus.Execution]
|
||||
.map(v => ({ value: v, label: routeStepStatusLabels[v] }));
|
||||
|
||||
const PROJECT_ROLE_OPTIONS = Object.values(ProjectRole)
|
||||
.filter(v => v !== ProjectRole.None)
|
||||
.map(v => ({ value: v, label: projectRoleLabels[v] }));
|
||||
|
||||
const HEAD_TYPE_OPTIONS = Object.values(HeadType)
|
||||
.filter(v => v !== HeadType.None)
|
||||
.map(v => ({ value: v, label: headTypeLabels[v] }));
|
||||
|
||||
function toFormState(editing: TemplateRouteStep | undefined, nextOrderNo: number): IFormState {
|
||||
if (!editing) {
|
||||
return {
|
||||
orderNo: String(nextOrderNo),
|
||||
status: RouteStepStatus.Approval,
|
||||
kind: 'email',
|
||||
executorEmail: '',
|
||||
position: '',
|
||||
projectRole: ProjectRole.ProjectManager,
|
||||
headType: HeadType.Chief,
|
||||
allowReassignToOtherApprovers: false
|
||||
};
|
||||
}
|
||||
return {
|
||||
orderNo: String(editing.orderNo),
|
||||
status: editing.status,
|
||||
kind: detectExecutorKind(editing),
|
||||
executorEmail: editing.executorEmail ?? '',
|
||||
position: editing.position ?? '',
|
||||
projectRole: editing.projectRole && editing.projectRole !== ProjectRole.None ? editing.projectRole : ProjectRole.ProjectManager,
|
||||
headType: editing.headType && editing.headType !== HeadType.None ? editing.headType : HeadType.Chief,
|
||||
allowReassignToOtherApprovers: editing.allowReassignToOtherApprovers
|
||||
};
|
||||
}
|
||||
|
||||
/** Форма создания/редактирования шага маршрута — вынесена из RouteStepsScreen. */
|
||||
export const RouteStepFormModal: React.FC<IRouteStepFormModalProps> = ({
|
||||
api,
|
||||
route,
|
||||
editing,
|
||||
nextOrderNo,
|
||||
employees,
|
||||
employeesLoading,
|
||||
onSaved,
|
||||
onClose
|
||||
}) => {
|
||||
const [form, setForm] = React.useState<IFormState>(() => toFormState(editing, nextOrderNo));
|
||||
const submit = useAsyncSubmit();
|
||||
|
||||
const submitForm = (): void => {
|
||||
const orderNo = Number(form.orderNo);
|
||||
if (!orderNo || orderNo < 1) {
|
||||
submit.fail(texts.routeSteps.validationOrderNo);
|
||||
return;
|
||||
}
|
||||
if (form.kind === 'email' && !form.executorEmail.trim()) {
|
||||
submit.fail(texts.routeSteps.validationExecutor);
|
||||
return;
|
||||
}
|
||||
if (form.kind === 'position' && !form.position.trim()) {
|
||||
submit.fail(texts.routeSteps.validationPosition);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = {
|
||||
orderNo,
|
||||
status: form.status,
|
||||
allowReassignToOtherApprovers: form.allowReassignToOtherApprovers,
|
||||
executorEmail: form.kind === 'email' ? form.executorEmail.trim() : null,
|
||||
position: form.kind === 'position' ? form.position.trim() : null,
|
||||
projectRole: form.kind === 'projectRole' ? form.projectRole : null,
|
||||
headType: form.kind === 'headType' ? form.headType : null
|
||||
};
|
||||
|
||||
const request = editing
|
||||
? api.templateRouteSteps.update(route.id, editing.id, payload)
|
||||
: api.templateRouteSteps.create(route.id, payload);
|
||||
|
||||
submit.run(() =>
|
||||
request.then(step => {
|
||||
onSaved(step);
|
||||
onClose();
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<FormModal
|
||||
title={editing ? texts.routeSteps.editTitle(editing.orderNo) : texts.routeSteps.createTitle}
|
||||
error={submit.error}
|
||||
isSaving={submit.isBusy}
|
||||
onSubmit={submitForm}
|
||||
onClose={onClose}
|
||||
>
|
||||
<FormGrid>
|
||||
<Field label={texts.routeSteps.orderNoLabel} required>
|
||||
<TextInput
|
||||
type="number"
|
||||
min={1}
|
||||
value={form.orderNo}
|
||||
onChange={e => setForm({ ...form, orderNo: e.target.value })}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={texts.routeSteps.statusLabel} required>
|
||||
<Select
|
||||
options={STATUS_OPTIONS}
|
||||
value={form.status}
|
||||
onChange={e => setForm({ ...form, status: e.target.value as RouteStepStatus })}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field label={texts.routeSteps.kindLabel} required full hint={texts.routeSteps.kindHint}>
|
||||
<RadioGroup
|
||||
name="executorKind"
|
||||
value={form.kind}
|
||||
onChange={kind => setForm({ ...form, kind })}
|
||||
options={[
|
||||
{ value: 'email', label: texts.routeSteps.kindEmail },
|
||||
{ value: 'position', label: texts.routeSteps.kindPosition },
|
||||
{
|
||||
value: 'projectRole',
|
||||
label: texts.routeSteps.kindProjectRole,
|
||||
disabled: !route.byProject,
|
||||
disabledHint: texts.routeSteps.kindProjectRoleDisabled
|
||||
},
|
||||
{ value: 'headType', label: texts.routeSteps.kindHeadType }
|
||||
]}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
{form.kind === 'email' && (
|
||||
<Field label={texts.routeSteps.executorLabel} required full>
|
||||
<SearchableSelect
|
||||
options={employees.map(toEmployeeOption)}
|
||||
value={form.executorEmail}
|
||||
loading={employeesLoading}
|
||||
placeholder={texts.routeSteps.executorPlaceholder}
|
||||
searchPlaceholder={texts.select.employeeSearchPlaceholder}
|
||||
emptyMessage={texts.select.employeesEmpty}
|
||||
onChange={executorEmail => setForm({ ...form, executorEmail })}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
{form.kind === 'position' && (
|
||||
<Field label={texts.routeSteps.positionLabel} required full hint={texts.routeSteps.positionHint}>
|
||||
<TextInput
|
||||
value={form.position}
|
||||
onChange={e => setForm({ ...form, position: e.target.value })}
|
||||
placeholder={texts.routeSteps.positionPlaceholder}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
{form.kind === 'projectRole' && (
|
||||
<Field label={texts.routeSteps.projectRoleLabel} required full>
|
||||
<Select
|
||||
options={PROJECT_ROLE_OPTIONS}
|
||||
value={form.projectRole}
|
||||
onChange={e => setForm({ ...form, projectRole: e.target.value as ProjectRole })}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
{form.kind === 'headType' && (
|
||||
<Field label={texts.routeSteps.headTypeLabel} required full>
|
||||
<Select
|
||||
options={HEAD_TYPE_OPTIONS}
|
||||
value={form.headType}
|
||||
onChange={e => setForm({ ...form, headType: e.target.value as HeadType })}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
|
||||
<Field label={texts.routeSteps.reassignLabel} full>
|
||||
<Checkbox
|
||||
label={texts.routeSteps.reassignCheckbox}
|
||||
checked={form.allowReassignToOtherApprovers}
|
||||
onChange={v => setForm({ ...form, allowReassignToOtherApprovers: v })}
|
||||
/>
|
||||
</Field>
|
||||
</FormGrid>
|
||||
</FormModal>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user