380 lines
13 KiB
TypeScript
380 lines
13 KiB
TypeScript
import { AppDispatch } from "app/store";
|
|
import React, { useCallback, useEffect, useState } from "react";
|
|
import styles from "styles/app.module.scss";
|
|
import { useDispatch, useSelector } from "react-redux";
|
|
import { useTranslation } from "react-i18next";
|
|
import { USER_ROLES } from "components/auth/constants";
|
|
import {
|
|
changeUpdateAuthorId,
|
|
changeUpdateAutoRenew,
|
|
changeUpdateEncryption,
|
|
changeUpdateEncryptionPassword,
|
|
changeUpdateNotification,
|
|
changeUpdatePrompt,
|
|
changeUpdateRole,
|
|
changeHasPasswordMask,
|
|
cleanupUpdateUser,
|
|
selectHasPasswordMask,
|
|
selectIsLoading,
|
|
selectUpdateUser,
|
|
selectUpdateValidationErrors,
|
|
updateUserAsync,
|
|
listUsersAsync,
|
|
} from "features/user";
|
|
import { getTranslationID } from "translation";
|
|
import close from "../../assets/images/close.svg";
|
|
import progress_activit from "../../assets/images/progress_activit.svg";
|
|
|
|
interface UserUpdatePopupProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
clearUserSearchInputs: () => void;
|
|
}
|
|
|
|
export const UserUpdatePopup: React.FC<UserUpdatePopupProps> = (props) => {
|
|
const { isOpen, onClose, clearUserSearchInputs } = props;
|
|
const dispatch: AppDispatch = useDispatch();
|
|
const { t } = useTranslation();
|
|
const closePopup = useCallback(() => {
|
|
setIsPushCreateButton(false);
|
|
dispatch(cleanupUpdateUser());
|
|
onClose();
|
|
}, [onClose, dispatch]);
|
|
|
|
const {
|
|
hasErrorEmptyAuthorId,
|
|
hasErrorIncorrectAuthorId,
|
|
hasErrorIncorrectEncryptionPassword,
|
|
} = useSelector(selectUpdateValidationErrors);
|
|
|
|
const [isPasswordHide, setIsPasswordHide] = useState<boolean>(true);
|
|
const [isPushCreateButton, setIsPushCreateButton] = useState<boolean>(false);
|
|
const user = useSelector(selectUpdateUser);
|
|
|
|
const isLoading = useSelector(selectIsLoading);
|
|
|
|
const hasPasswordMask = useSelector(selectHasPasswordMask);
|
|
|
|
const [canChangeRole, setCanChangeRole] = useState(true);
|
|
|
|
// 開閉時のみ実行
|
|
useEffect(() => {
|
|
setCanChangeRole(user.role === USER_ROLES.NONE);
|
|
setIsPasswordHide(true);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [isOpen]);
|
|
|
|
const onUpdateUser = useCallback(async () => {
|
|
setIsPushCreateButton(true);
|
|
|
|
if (
|
|
hasErrorEmptyAuthorId ||
|
|
hasErrorIncorrectAuthorId ||
|
|
hasErrorIncorrectEncryptionPassword
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const { meta } = await dispatch(updateUserAsync());
|
|
setIsPushCreateButton(false);
|
|
|
|
if (meta.requestStatus === "fulfilled") {
|
|
closePopup();
|
|
clearUserSearchInputs();
|
|
dispatch(listUsersAsync());
|
|
}
|
|
}, [
|
|
dispatch,
|
|
closePopup,
|
|
hasErrorEmptyAuthorId,
|
|
hasErrorIncorrectAuthorId,
|
|
hasErrorIncorrectEncryptionPassword,
|
|
]);
|
|
|
|
return (
|
|
<div className={`${styles.modal} ${isOpen ? styles.isShow : ""}`}>
|
|
<div className={styles.modalBox}>
|
|
<p className={styles.modalTitle}>
|
|
{t(getTranslationID("userListPage.label.editUser"))}
|
|
<button type="button" onClick={closePopup}>
|
|
<img src={close} className={styles.modalTitleIcon} alt="close" />
|
|
</button>
|
|
</p>
|
|
<form className={styles.form}>
|
|
<dl className={`${styles.formList} ${styles.hasbg}`}>
|
|
<dt className={styles.formTitle}>
|
|
{t(getTranslationID("userListPage.label.personal"))}
|
|
</dt>
|
|
<dt>{t(getTranslationID("userListPage.label.name"))}</dt>
|
|
<dd>
|
|
<input
|
|
type="text"
|
|
size={40}
|
|
value={user.name}
|
|
className={styles.formInput}
|
|
readOnly
|
|
/>
|
|
</dd>
|
|
<dt>{t(getTranslationID("userListPage.label.email"))}</dt>
|
|
<dd>
|
|
<input
|
|
type="email"
|
|
size={40}
|
|
value={user.email}
|
|
className={styles.formInput}
|
|
readOnly
|
|
/>
|
|
</dd>
|
|
<dt>{t(getTranslationID("userListPage.label.role"))}</dt>
|
|
<dd>
|
|
<label htmlFor={`edit_${USER_ROLES.AUTHOR}`}>
|
|
<input
|
|
id={`edit_${USER_ROLES.AUTHOR}`}
|
|
type="radio"
|
|
name="role"
|
|
className={styles.formRadio}
|
|
checked={user.role === USER_ROLES.AUTHOR}
|
|
onChange={() => {
|
|
dispatch(changeUpdateRole({ role: USER_ROLES.AUTHOR }));
|
|
}}
|
|
disabled={!canChangeRole}
|
|
/>
|
|
{t(getTranslationID("userListPage.label.author"))}
|
|
</label>
|
|
<label htmlFor={`edit_${USER_ROLES.TYPIST}`}>
|
|
<input
|
|
id={`edit_${USER_ROLES.TYPIST}`}
|
|
type="radio"
|
|
name="role"
|
|
className={styles.formRadio}
|
|
checked={user.role === USER_ROLES.TYPIST}
|
|
onChange={() => {
|
|
dispatch(changeUpdateRole({ role: USER_ROLES.TYPIST }));
|
|
}}
|
|
disabled={!canChangeRole}
|
|
/>
|
|
{t(getTranslationID("userListPage.label.transcriptionist"))}
|
|
</label>
|
|
<label htmlFor={`edit_${USER_ROLES.NONE}`}>
|
|
<input
|
|
id={`edit_${USER_ROLES.NONE}`}
|
|
type="radio"
|
|
name="role"
|
|
className={styles.formRadio}
|
|
checked={user.role === USER_ROLES.NONE}
|
|
onChange={() => {
|
|
dispatch(changeUpdateRole({ role: USER_ROLES.NONE }));
|
|
}}
|
|
disabled={!canChangeRole}
|
|
/>
|
|
{t(getTranslationID("userListPage.label.none"))}
|
|
</label>
|
|
</dd>
|
|
|
|
{/** Author 選択時に表示 */}
|
|
{user.role === USER_ROLES.AUTHOR && (
|
|
<div className={styles.slideSet}>
|
|
<dt>{t(getTranslationID("userListPage.label.authorID"))}</dt>
|
|
<dd>
|
|
<input
|
|
type="text"
|
|
size={40}
|
|
maxLength={16}
|
|
autoComplete="off"
|
|
value={user.authorId}
|
|
className={styles.formInput}
|
|
onChange={(e) => {
|
|
dispatch(
|
|
changeUpdateAuthorId({
|
|
authorId: e.target.value.toUpperCase(),
|
|
})
|
|
);
|
|
}}
|
|
/>
|
|
{isPushCreateButton && hasErrorEmptyAuthorId && (
|
|
<span className={styles.formError}>
|
|
{t(
|
|
getTranslationID("signupPage.message.inputEmptyError")
|
|
)}
|
|
</span>
|
|
)}
|
|
{isPushCreateButton && hasErrorIncorrectAuthorId && (
|
|
<span className={styles.formError}>
|
|
{t(
|
|
getTranslationID(
|
|
"userListPage.message.authorIdIncorrectError"
|
|
)
|
|
)}
|
|
</span>
|
|
)}
|
|
</dd>
|
|
<dt>{t(getTranslationID("userListPage.label.encryption"))}</dt>
|
|
<dd>
|
|
<label htmlFor="Encryption">
|
|
<input
|
|
type="checkbox"
|
|
className={styles.formCheck}
|
|
checked={user.encryption}
|
|
onChange={(e) => {
|
|
dispatch(
|
|
changeUpdateEncryption({
|
|
encryption: e.target.checked,
|
|
})
|
|
);
|
|
}}
|
|
/>
|
|
</label>
|
|
{/** Encryptionチェックでパスワード欄表示 */}
|
|
<p
|
|
className={`${styles.encryptionPass} ${
|
|
user.encryption ? styles.isShow : ""
|
|
}`}
|
|
>
|
|
{t(
|
|
getTranslationID("userListPage.label.encryptionPassword")
|
|
)}
|
|
<input
|
|
type={isPasswordHide ? "password" : "text"}
|
|
size={40}
|
|
maxLength={16}
|
|
autoComplete="new-password"
|
|
value={
|
|
hasPasswordMask
|
|
? "********"
|
|
: user.encryptionPassword ?? ""
|
|
}
|
|
className={`${styles.formInput} ${styles.password}`}
|
|
onFocus={() => {
|
|
if (hasPasswordMask) {
|
|
dispatch(
|
|
changeHasPasswordMask({ hasPasswordMask: false })
|
|
);
|
|
dispatch(
|
|
changeUpdateEncryptionPassword({
|
|
encryptionPassword: "",
|
|
})
|
|
);
|
|
}
|
|
}}
|
|
onChange={(e) => {
|
|
if (!hasPasswordMask) {
|
|
dispatch(
|
|
changeUpdateEncryptionPassword({
|
|
encryptionPassword: e.target.value,
|
|
})
|
|
);
|
|
}
|
|
}}
|
|
/>
|
|
<span
|
|
className={styles.formIconEye}
|
|
onClick={() => {
|
|
setIsPasswordHide(!isPasswordHide);
|
|
}}
|
|
onKeyDown={() => {
|
|
setIsPasswordHide(!isPasswordHide);
|
|
}}
|
|
role="button"
|
|
tabIndex={0}
|
|
aria-label="IconEye"
|
|
/>
|
|
{isPushCreateButton &&
|
|
hasErrorIncorrectEncryptionPassword && (
|
|
<span className={styles.formError}>
|
|
{t(
|
|
getTranslationID(
|
|
"userListPage.message.encryptionPasswordCorrectError"
|
|
)
|
|
)}
|
|
</span>
|
|
)}
|
|
<span className={styles.formComment}>
|
|
{t(
|
|
getTranslationID(
|
|
"userListPage.label.encryptionPasswordTerm"
|
|
)
|
|
)}
|
|
</span>
|
|
</p>
|
|
</dd>
|
|
<dt>{t(getTranslationID("userListPage.label.prompt"))}</dt>
|
|
<dd>
|
|
<label htmlFor="Prompt">
|
|
<input
|
|
type="checkbox"
|
|
className={styles.formCheck}
|
|
checked={user.prompt}
|
|
onChange={(e) => {
|
|
dispatch(
|
|
changeUpdatePrompt({
|
|
prompt: e.target.checked,
|
|
})
|
|
);
|
|
}}
|
|
/>
|
|
</label>
|
|
</dd>
|
|
</div>
|
|
)}
|
|
|
|
<dt>{t(getTranslationID("userListPage.label.setting"))}</dt>
|
|
<dd className="last">
|
|
<p>
|
|
<label htmlFor="edit_AutoRenew">
|
|
<input
|
|
type="checkbox"
|
|
id="edit_AutoRenew"
|
|
className={styles.formCheck}
|
|
checked={user.autoRenew}
|
|
onChange={(e) => {
|
|
dispatch(
|
|
changeUpdateAutoRenew({
|
|
autoRenew: e.target.checked,
|
|
})
|
|
);
|
|
}}
|
|
/>
|
|
{t(getTranslationID("userListPage.label.autoRenew"))}
|
|
</label>
|
|
</p>
|
|
<p>
|
|
<label htmlFor="edit_Notification">
|
|
<input
|
|
type="checkbox"
|
|
id="edit_Notification"
|
|
className={styles.formCheck}
|
|
checked={user.notification}
|
|
onChange={(e) => {
|
|
dispatch(
|
|
changeUpdateNotification({
|
|
notification: e.target.checked,
|
|
})
|
|
);
|
|
}}
|
|
/>
|
|
{t(getTranslationID("userListPage.label.notification"))}
|
|
</label>
|
|
</p>
|
|
</dd>
|
|
<dd className={`${styles.full} ${styles.alignCenter}`}>
|
|
<input
|
|
type="button"
|
|
name="submit"
|
|
value={t(getTranslationID("userListPage.label.editUser"))}
|
|
className={`${styles.formSubmit} ${styles.marginBtm1} ${styles.isActive}`}
|
|
onClick={onUpdateUser}
|
|
/>
|
|
<img
|
|
style={{ display: isLoading ? "inline" : "none" }}
|
|
src={progress_activit}
|
|
className={styles.icLoading}
|
|
alt="Loading"
|
|
/>
|
|
</dd>
|
|
</dl>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|