Sla (#11)
Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/gorbenko.pavel/tnsu-approval-route/pulls/11
This commit was merged in pull request #11.
This commit is contained in:
+63
-8
@@ -3,30 +3,59 @@ import { Employee } from '../../models';
|
||||
import { texts } from '../../texts';
|
||||
import { findEmployeeByEmail, toEmployeeOption } from '../../utils/employee';
|
||||
import { useAsyncSubmit } from '../../hooks/useAsyncSubmit';
|
||||
import { Field, FormGrid, FormModal, SearchableSelect } from '../ui';
|
||||
import {
|
||||
boolToTriState,
|
||||
isValidSlaHours,
|
||||
parseSlaHours,
|
||||
slaHoursToInput,
|
||||
triStateToBool,
|
||||
TriState,
|
||||
TRI_STATE_OPTIONS
|
||||
} from '../../utils/sla';
|
||||
import { Field, FormGrid, FormModal, SearchableSelect, Select, TextInput } from '../ui';
|
||||
|
||||
export interface IManageRequestsApproverModalProps {
|
||||
title: string;
|
||||
initialEmail?: string;
|
||||
/** SLA строки в часах на момент открытия. null — в строке не задано, работает SLA типа заявки. */
|
||||
// eslint-disable-next-line @rushstack/no-new-null -- null = «не задано», это не 0
|
||||
initialSlaHours: number | null;
|
||||
/** Опрос причины просрочки по строке. null — берётся от типа заявки. */
|
||||
// eslint-disable-next-line @rushstack/no-new-null -- см. initialSlaHours
|
||||
initialAskOverdueReason: boolean | null;
|
||||
employees: Employee[];
|
||||
employeesLoading: boolean;
|
||||
/** Email'ы (в любом регистре), уже занятые другими шагами той же фазы — этот человек не может встать в фазу второй раз. */
|
||||
excludedEmails: string[];
|
||||
onSave: (email: string, position: string) => Promise<void>;
|
||||
onSave: (
|
||||
email: string,
|
||||
position: string,
|
||||
// eslint-disable-next-line @rushstack/no-new-null -- null = «не задано»
|
||||
slaHours: number | null,
|
||||
// eslint-disable-next-line @rushstack/no-new-null -- null = «не задано»
|
||||
askOverdueReason: boolean | null
|
||||
) => Promise<void>;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Переназначение согласующего/исполнителя существующего шага (см.
|
||||
* ManageRequestsScreen). Должность НЕ вводится — берётся из справочника
|
||||
* сотрудников (api.tnsu.kz/Employee/All, см. Employee.position в
|
||||
* models/dictionaries.ts) на момент сохранения, отдельного поля для неё
|
||||
* в этой форме нет. Добавление НОВОГО шага через модалку не идёт — см.
|
||||
* Правка существующего шага: кто согласует и какой у шага SLA.
|
||||
*
|
||||
* Одна форма на оба вида правки намеренно — раньше SLA жил в отдельной модалке
|
||||
* со своей кнопкой-флажком в строке, и получалось два похожих диалога и лишняя
|
||||
* иконка в и без того плотном ряду действий. Правки применяются одним
|
||||
* сохранением (см. saveModal в ManageRequestsScreen).
|
||||
*
|
||||
* Должность НЕ вводится — берётся из справочника сотрудников
|
||||
* (api.tnsu.kz/Employee/All, см. Employee.position в models/dictionaries.ts) на
|
||||
* момент сохранения. Добавление НОВОГО шага через модалку не идёт — см.
|
||||
* плашку-пикер под таблицей в ManageRequestsPhaseTable.
|
||||
*/
|
||||
export const ManageRequestsApproverModal: React.FC<IManageRequestsApproverModalProps> = ({
|
||||
title,
|
||||
initialEmail = '',
|
||||
initialSlaHours,
|
||||
initialAskOverdueReason,
|
||||
employees,
|
||||
employeesLoading,
|
||||
excludedEmails,
|
||||
@@ -34,6 +63,10 @@ export const ManageRequestsApproverModal: React.FC<IManageRequestsApproverModalP
|
||||
onClose
|
||||
}) => {
|
||||
const [email, setEmail] = React.useState(initialEmail);
|
||||
const [slaHours, setSlaHours] = React.useState(() => slaHoursToInput(initialSlaHours));
|
||||
const [askOverdueReason, setAskOverdueReason] = React.useState<TriState>(
|
||||
() => boolToTriState(initialAskOverdueReason)
|
||||
);
|
||||
const submit = useAsyncSubmit();
|
||||
|
||||
// Тот, кто уже занят в этой фазе, исключён из списка выбора — дубликат
|
||||
@@ -51,8 +84,14 @@ export const ManageRequestsApproverModal: React.FC<IManageRequestsApproverModalP
|
||||
submit.fail(texts.manageRequests.validationDuplicateApprover);
|
||||
return;
|
||||
}
|
||||
if (!isValidSlaHours(slaHours)) {
|
||||
submit.fail(texts.manageRequests.validationSlaHours);
|
||||
return;
|
||||
}
|
||||
const position = findEmployeeByEmail(employees, trimmedEmail)?.position || '';
|
||||
submit.run(() => onSave(trimmedEmail, position));
|
||||
submit.run(() =>
|
||||
onSave(trimmedEmail, position, parseSlaHours(slaHours), triStateToBool(askOverdueReason))
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -69,6 +108,22 @@ export const ManageRequestsApproverModal: React.FC<IManageRequestsApproverModalP
|
||||
onChange={setEmail}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={texts.manageRequests.slaHoursLabel} hint={texts.manageRequests.slaHoursHint}>
|
||||
<TextInput
|
||||
type="number"
|
||||
min={0}
|
||||
value={slaHours}
|
||||
onChange={e => setSlaHours(e.target.value)}
|
||||
placeholder={texts.manageRequests.slaHoursPlaceholder}
|
||||
/>
|
||||
</Field>
|
||||
<Field label={texts.manageRequests.askOverdueReasonLabel} hint={texts.manageRequests.askOverdueReasonHint}>
|
||||
<Select
|
||||
options={TRI_STATE_OPTIONS}
|
||||
value={askOverdueReason}
|
||||
onChange={e => setAskOverdueReason(e.target.value as TriState)}
|
||||
/>
|
||||
</Field>
|
||||
</FormGrid>
|
||||
</FormModal>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user