makabe.t 2d569aee6d Merged PR 479: ワークフロー更新ポップアップ実装
## 概要
[Task2777: ワークフロー更新ポップアップ実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2777)

- ワークフロー編集ポップアップを実装しました。

## レビューポイント
- 表示内容は適切か
- 選択ワークフローの値取得処理は適切か

## UIの変更
- [Task2777](https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task2777?csf=1&web=1&e=RfM1Dv)

## 動作確認状況
- ローカルで確認
2023-10-12 07:42:53 +00:00

314 lines
11 KiB
TypeScript

import React, { useCallback, useEffect, useState } from "react";
import { AppDispatch } from "app/store";
import progress_activit from "assets/images/progress_activit.svg";
import {
addAssignee,
removeAssignee,
changeAuthor,
changeTemplate,
changeWorktype,
clearWorkflow,
selectIsAddLoading,
selectWorkflowAssinee,
selectWorkflowError,
selectWorkflowRelations,
selectSelectedWorkflow,
selectAuthorId,
selectWorktypeId,
setAssignees,
selectTemplateId,
} from "features/workflow";
import {
getworkflowRelationsAsync,
listWorkflowAsync,
updateWorkflowAsync,
} from "features/workflow/operations";
import { useTranslation } from "react-i18next";
import { useDispatch, useSelector } from "react-redux";
import styles from "styles/app.module.scss";
import { getTranslationID } from "translation";
import close from "../../assets/images/close.svg";
interface EditWorkflowPopupProps {
onClose: () => void;
}
export const EditWorkflowPopup: React.FC<EditWorkflowPopupProps> = (
props
): JSX.Element => {
const { onClose } = props;
const dispatch: AppDispatch = useDispatch();
const [t] = useTranslation();
// 保存ボタンを押したかどうか
const [isPushEditButton, setIsPushEditButton] = useState<boolean>(false);
const workflow = useSelector(selectSelectedWorkflow);
const authorId = useSelector(selectAuthorId);
const worktypeId = useSelector(selectWorktypeId);
const templateId = useSelector(selectTemplateId);
const workflowRelations = useSelector(selectWorkflowRelations);
const { poolAssignees, selectedAssignees } = useSelector(
selectWorkflowAssinee
);
const isLoading = useSelector(selectIsAddLoading);
const { hasAuthorIdEmptyError, hasSelectedWorkflowAssineeEmptyError } =
useSelector(selectWorkflowError);
useEffect(() => {
dispatch(getworkflowRelationsAsync());
// ポップアップのアンマウント時に初期化を行う
return () => {
dispatch(clearWorkflow());
setIsPushEditButton(false);
};
}, [dispatch]);
useEffect(() => {
dispatch(changeAuthor({ authorId: workflow?.author?.id }));
dispatch(changeWorktype({ worktypeId: workflow?.worktype?.id }));
dispatch(changeTemplate({ templateId: workflow?.template?.id }));
dispatch(setAssignees({ assignees: workflow?.typists ?? [] }));
}, [dispatch, workflow]);
const changeWorktypeId = useCallback(
(target: string) => {
// 空文字の場合はundefinedをdispatchする
if (target === "") {
dispatch(changeWorktype({ worktypeId: undefined }));
} else if (!Number.isNaN(Number(target))) {
dispatch(changeWorktype({ worktypeId: Number(target) }));
}
},
[dispatch]
);
const changeTemplateId = useCallback(
(target: string) => {
// 空文字の場合はundefinedをdispatchする
if (target === "") {
dispatch(changeTemplate({ templateId: undefined }));
} else if (!Number.isNaN(Number(target))) {
dispatch(changeTemplate({ templateId: Number(target) }));
}
},
[dispatch]
);
const changeAuthorId = useCallback(
(target: string) => {
if (!Number.isNaN(target)) {
dispatch(changeAuthor({ authorId: Number(target) }));
}
},
[dispatch]
);
// 保存ボタン押下時の処理
const handleSave = useCallback(async () => {
setIsPushEditButton(true);
// エラーチェック
if (hasAuthorIdEmptyError || hasSelectedWorkflowAssineeEmptyError) {
return;
}
const { meta } = await dispatch(updateWorkflowAsync());
if (meta.requestStatus === "fulfilled") {
onClose();
dispatch(listWorkflowAsync());
}
}, [
dispatch,
hasAuthorIdEmptyError,
hasSelectedWorkflowAssineeEmptyError,
onClose,
]);
return (
<div className={`${styles.modal} ${styles.isShow}`}>
<div className={styles.modalBox}>
<p className={styles.modalTitle}>
{t(getTranslationID("workflowPage.label.editRoutingRule"))}
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-noninteractive-element-interactions */}
<img
src={close}
className={styles.modalTitleIcon}
alt="close"
onClick={onClose}
/>
</p>
<form className={styles.form}>
<dl className={`${styles.formList} ${styles.hasbg}`}>
<dt className={styles.formTitle} />
<dt>{t(getTranslationID("workflowPage.label.authorID"))}</dt>
<dd>
<select
className={styles.formInput}
value={authorId}
onChange={(e) => {
changeAuthorId(e.target.value);
}}
>
<option value="" hidden>
{`-- ${t(
getTranslationID("workflowPage.label.selectAuthor")
)} --`}
</option>
{workflowRelations?.authors.map((author) => (
<option key={author.authorId} value={author.id}>
{author.authorId}
</option>
))}
</select>
{isPushEditButton && hasAuthorIdEmptyError && (
<span className={styles.formError}>
{t(getTranslationID("workflowPage.message.inputEmptyError"))}
</span>
)}
</dd>
<dt className={styles.overLine}>
{t(getTranslationID("workflowPage.label.worktypeOptional"))}
</dt>
<dd>
<select
className={styles.formInput}
value={worktypeId}
onChange={(e) => {
changeWorktypeId(e.target.value);
}}
>
<option value="" hidden>
{`-- ${t(
getTranslationID("workflowPage.label.selectWorktypeId")
)} --`}
</option>
<option value="">
{`-- ${t(getTranslationID("common.label.notSelected"))} --`}
</option>
{workflowRelations?.worktypes.map((worktype) => (
<option key={worktype.id} value={worktype.id}>
{worktype.worktypeId}
</option>
))}
</select>
</dd>
<dt className={styles.formTitle}>
{t(getTranslationID("typistGroupSetting.label.transcriptionist"))}
</dt>
<dd className={`${styles.formChange} ${styles.last}`}>
<ul className={styles.chooseMember}>
<li className={styles.changeTitle}>
{t(getTranslationID("workflowPage.label.selected"))}
</li>
{selectedAssignees?.map((x) => {
const key = `${x.typistName}_${
x.typistUserId ?? x.typistGroupId
}`;
return (
<li key={key}>
<input
type="checkbox"
className={styles.formCheck}
value={x.typistName}
id={key}
checked
onClick={() => {
dispatch(removeAssignee({ assignee: x }));
}}
/>
<label htmlFor={key} title="Remove">
{x.typistName}
</label>
</li>
);
})}
</ul>
<p />
<ul className={styles.holdMember}>
<li className={styles.changeTitle}>
{t(getTranslationID("workflowPage.label.pool"))}
</li>
{poolAssignees?.map((x) => {
const key = `${x.typistName}_${
x.typistUserId ?? x.typistGroupId
}`;
return (
<li key={key}>
<input
type="checkbox"
className={styles.formCheck}
value={x.typistName}
id={key}
onClick={() => dispatch(addAssignee({ assignee: x }))}
/>
<label htmlFor={key} title="Add">
{x.typistName}
</label>
</li>
);
})}
</ul>
{isPushEditButton && hasSelectedWorkflowAssineeEmptyError && (
<span
className={styles.formError}
style={{ margin: "0px 30px 0px 30px" }}
>
{t(
getTranslationID(
"workflowPage.message.selectedTypistEmptyError"
)
)}
</span>
)}
</dd>
<dt className={styles.overLine}>
{t(getTranslationID("workflowPage.label.templateOptional"))}
</dt>
<dd className={styles.last}>
<select
className={styles.formInput}
value={templateId}
onChange={(e) => {
changeTemplateId(e.target.value);
}}
>
<option value="" hidden>
{`-- ${t(
getTranslationID("workflowPage.label.selectTemplate")
)} --`}
</option>
<option value="">
{`-- ${t(getTranslationID("common.label.notSelected"))} --`}
</option>
{workflowRelations?.templates.map((template) => (
<option
key={`${template.name}_${template.id}`}
value={template.id}
>
{template.name}
</option>
))}
</select>
</dd>
<dd className={`${styles.full} ${styles.alignCenter}`}>
<input
type="button"
value={t(getTranslationID("common.label.save"))}
className={`${styles.formSubmit} ${styles.marginBtm1} ${
!isLoading ? styles.isActive : ""
}`}
onClick={handleSave}
/>
{isLoading && (
<img
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
)}
</dd>
</dl>
</form>
</div>
</div>
);
};