Merged PR 305: ユーザー編集ポップアップ実装
## 概要 [Task2315: ユーザー編集ポップアップ実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2315) - ユーザー編集ポップアップを実装しました。 ## レビューポイント - デザイン判定は適切か - 表示文言は適切か - エラーチェックは適切か ## UIの変更 - [Task2315](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/Task2315?csf=1&web=1&e=AP7b9M) ## 動作確認状況 - ローカルで確認
This commit is contained in:
parent
712efb8bb7
commit
ef83477533
@ -20,13 +20,26 @@ export const errorCodes = [
|
||||
"E000104", // トークン署名エラー
|
||||
"E000105", // トークン発行元エラー
|
||||
"E000106", // トークンアルゴリズムエラー
|
||||
"E000107", // トークン不足エラー
|
||||
"E000108", // トークン権限エラー
|
||||
"E000301", // ADB2Cへのリクエスト上限超過エラー
|
||||
"E010001", // パラメータ形式不正エラー
|
||||
"E010201", // 未認証ユーザエラー
|
||||
"E010202", // 認証済ユーザエラー
|
||||
"E010203", // 管理ユーザ権限エラー
|
||||
"E010204", // ユーザ不在エラー
|
||||
"E010205", // DBのRoleが想定外の値エラー
|
||||
"E010206", // DBのTierが想定外の値エラー
|
||||
"E010207", // ユーザーのRole変更不可エラー
|
||||
"E010208", // ユーザーの暗号化パスワード不足エラー
|
||||
"E010301", // メールアドレス登録済みエラー
|
||||
"E010302", // authorId重複エラー
|
||||
"E010401", // PONumber重複エラー
|
||||
"E010501", // アカウント不在エラー
|
||||
"E010601", // タスク変更不可エラー(タスクが変更できる状態でない、またはタスクが存在しない)
|
||||
"E010602", // タスク変更権限不足エラー
|
||||
"E010603", // タスク不在エラー
|
||||
"E010701", // Blobファイル不在エラー
|
||||
"E010801", // ライセンス不在エラー
|
||||
"E010802", // ライセンス取り込み済みエラー
|
||||
] as const;
|
||||
|
||||
@ -105,3 +105,86 @@ export const addUserAsync = createAsyncThunk<
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
|
||||
export const updateUserAsync = createAsyncThunk<
|
||||
{
|
||||
/* Empty Object */
|
||||
},
|
||||
void,
|
||||
{
|
||||
// rejectした時の返却値の型
|
||||
rejectValue: {
|
||||
error: ErrorObject;
|
||||
};
|
||||
}
|
||||
>("users/updateUserAsync", async (args, thunkApi) => {
|
||||
// apiのConfigurationを取得する
|
||||
const { getState } = thunkApi;
|
||||
const state = getState() as RootState;
|
||||
const { configuration, accessToken } = state.auth;
|
||||
const config = new Configuration(configuration);
|
||||
const usersApi = new UsersApi(config);
|
||||
const { updateUser } = state.user.apps;
|
||||
|
||||
const authorId =
|
||||
updateUser.role === USER_ROLES.AUTHOR ? updateUser.authorId : undefined;
|
||||
const encryption =
|
||||
updateUser.role === USER_ROLES.AUTHOR ? updateUser.encryption : undefined;
|
||||
const encryptionPassword =
|
||||
updateUser.role === USER_ROLES.AUTHOR
|
||||
? updateUser.encryptionPassword
|
||||
: undefined;
|
||||
const prompt =
|
||||
updateUser.role === USER_ROLES.AUTHOR ? updateUser.prompt : undefined;
|
||||
|
||||
try {
|
||||
await usersApi.updateUser(
|
||||
{
|
||||
id: updateUser.id,
|
||||
role: updateUser.role,
|
||||
authorId,
|
||||
encryption,
|
||||
encryptionPassword,
|
||||
prompt,
|
||||
autoRenew: updateUser.autoRenew,
|
||||
licenseAlart: updateUser.licenseAlert,
|
||||
notification: updateUser.notification,
|
||||
},
|
||||
{
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
}
|
||||
);
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
message: getTranslationID("common.message.success"),
|
||||
})
|
||||
);
|
||||
return {};
|
||||
} catch (e) {
|
||||
// e ⇒ errorObjectに変換"z
|
||||
const error = createErrorObject(e);
|
||||
|
||||
let errorMessage = getTranslationID("common.message.internalServerError");
|
||||
|
||||
// Roleが変更できない
|
||||
if (error.code === "E010207") {
|
||||
errorMessage = getTranslationID("userListPage.message.roleChangeError");
|
||||
}
|
||||
// AuthorIdが重複している
|
||||
if (error.code === "E010302") {
|
||||
errorMessage = getTranslationID(
|
||||
"userListPage.message.authorIdConflictError"
|
||||
);
|
||||
}
|
||||
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
})
|
||||
);
|
||||
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
|
||||
@ -40,6 +40,47 @@ export const selectInputValidationErrors = (state: RootState) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const selectUpdateValidationErrors = (state: RootState) => {
|
||||
const { role, authorId, encryption, encryptionPassword } =
|
||||
state.user.apps.updateUser;
|
||||
const { encryption: initEncryption } = state.user.apps.selectedUser;
|
||||
|
||||
// Authorの場合、AuthorIDが必須(空文字,undefinedは不可)
|
||||
const hasErrorEmptyAuthorId =
|
||||
role === USER_ROLES.AUTHOR && (authorId === "" || !authorId);
|
||||
|
||||
const hasErrorIncorrectAuthorId = checkErrorIncorrectAuthorId(
|
||||
authorId ?? undefined,
|
||||
role
|
||||
);
|
||||
|
||||
let hasErrorIncorrectEncryptionPassword = false;
|
||||
|
||||
const passwordError = checkErrorIncorrectEncryptionPassword(
|
||||
encryptionPassword,
|
||||
role,
|
||||
encryption
|
||||
);
|
||||
|
||||
if (passwordError) {
|
||||
// 最初にEncryptionがfasleで、Encryptionがtrueに変更された場合、EncryptionPasswordが必須
|
||||
if (!initEncryption) {
|
||||
hasErrorIncorrectEncryptionPassword = true;
|
||||
// Encryptionがある状態で変更がある場合、EncryptionPasswordが空でもエラーにしない
|
||||
} else if (!encryptionPassword || encryptionPassword === "") {
|
||||
hasErrorIncorrectEncryptionPassword = false;
|
||||
} else {
|
||||
hasErrorIncorrectEncryptionPassword = true;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hasErrorEmptyAuthorId,
|
||||
hasErrorIncorrectAuthorId,
|
||||
hasErrorIncorrectEncryptionPassword,
|
||||
};
|
||||
};
|
||||
|
||||
// encreyptionPasswordのチェック
|
||||
const checkErrorIncorrectEncryptionPassword = (
|
||||
encryptionPassword: string | undefined,
|
||||
@ -180,3 +221,9 @@ const convertValueBasedOnRole = (
|
||||
typistGroupName: "-",
|
||||
};
|
||||
};
|
||||
|
||||
export const selectUpdateUser = (state: RootState) =>
|
||||
state.user.apps.updateUser;
|
||||
|
||||
export const selectHasPasswordMask = (state: RootState) =>
|
||||
state.user.apps.hasPasswordMask;
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { User } from "../../api/api";
|
||||
import { AddUser } from "./types";
|
||||
import { AddUser, UpdateUser } from "./types";
|
||||
|
||||
export interface UsersState {
|
||||
domain: Domain;
|
||||
@ -12,5 +12,8 @@ export interface Domain {
|
||||
|
||||
export interface Apps {
|
||||
addUser: AddUser;
|
||||
selectedUser: UpdateUser;
|
||||
updateUser: UpdateUser;
|
||||
hasPasswordMask: boolean;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
@ -36,6 +36,20 @@ export interface AddUser {
|
||||
prompt?: boolean;
|
||||
}
|
||||
|
||||
export interface UpdateUser {
|
||||
id: number;
|
||||
name: string;
|
||||
email: string;
|
||||
role: RoleType;
|
||||
authorId?: string | undefined;
|
||||
encryption?: boolean | undefined;
|
||||
encryptionPassword?: string | undefined;
|
||||
prompt?: boolean | undefined;
|
||||
autoRenew: boolean;
|
||||
licenseAlert: boolean;
|
||||
notification: boolean;
|
||||
}
|
||||
|
||||
export type RoleType = typeof USER_ROLES[keyof typeof USER_ROLES];
|
||||
|
||||
// 受け取った値がUSER_ROLESの型であるかどうかを判定する
|
||||
|
||||
@ -1,12 +1,38 @@
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
import { USER_ROLES } from "components/auth/constants";
|
||||
import { UsersState } from "./state";
|
||||
import { addUserAsync, listUsersAsync } from "./operations";
|
||||
import { addUserAsync, listUsersAsync, updateUserAsync } from "./operations";
|
||||
import { RoleType } from "./types";
|
||||
|
||||
const initialState: UsersState = {
|
||||
domain: { users: [] },
|
||||
apps: {
|
||||
updateUser: {
|
||||
id: 0,
|
||||
name: "",
|
||||
email: "",
|
||||
role: USER_ROLES.NONE,
|
||||
authorId: undefined,
|
||||
encryption: undefined,
|
||||
encryptionPassword: undefined,
|
||||
prompt: undefined,
|
||||
autoRenew: true,
|
||||
licenseAlert: true,
|
||||
notification: true,
|
||||
},
|
||||
selectedUser: {
|
||||
id: 0,
|
||||
name: "",
|
||||
email: "",
|
||||
role: USER_ROLES.NONE,
|
||||
authorId: undefined,
|
||||
encryption: undefined,
|
||||
encryptionPassword: undefined,
|
||||
prompt: undefined,
|
||||
autoRenew: true,
|
||||
licenseAlert: true,
|
||||
notification: true,
|
||||
},
|
||||
addUser: {
|
||||
name: "",
|
||||
role: USER_ROLES.NONE,
|
||||
@ -19,6 +45,7 @@ const initialState: UsersState = {
|
||||
prompt: false,
|
||||
encryptionPassword: "",
|
||||
},
|
||||
hasPasswordMask: false,
|
||||
isLoading: false,
|
||||
},
|
||||
};
|
||||
@ -85,6 +112,108 @@ export const userSlice = createSlice({
|
||||
cleanupAddUser: (state) => {
|
||||
state.apps.addUser = initialState.apps.addUser;
|
||||
},
|
||||
changeUpdateUser: (state, action: PayloadAction<{ id: number }>) => {
|
||||
const { id } = action.payload;
|
||||
|
||||
const user = state.domain.users.find((x) => x.id === id);
|
||||
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
|
||||
state.apps.updateUser.id = user.id;
|
||||
state.apps.updateUser.name = user.name;
|
||||
state.apps.updateUser.email = user.email;
|
||||
state.apps.updateUser.role = user.role as RoleType;
|
||||
state.apps.updateUser.authorId = user.authorId;
|
||||
state.apps.updateUser.encryption = user.encryption;
|
||||
state.apps.updateUser.encryptionPassword = undefined;
|
||||
state.apps.updateUser.prompt = user.prompt;
|
||||
state.apps.updateUser.autoRenew = user.autoRenew;
|
||||
state.apps.updateUser.licenseAlert = user.licenseAlert;
|
||||
state.apps.updateUser.notification = user.notification;
|
||||
|
||||
state.apps.selectedUser.id = user.id;
|
||||
state.apps.selectedUser.name = user.name;
|
||||
state.apps.selectedUser.email = user.email;
|
||||
state.apps.selectedUser.role = user.role as RoleType;
|
||||
state.apps.selectedUser.authorId = user.authorId;
|
||||
state.apps.selectedUser.encryption = user.encryption;
|
||||
state.apps.selectedUser.encryptionPassword = undefined;
|
||||
state.apps.selectedUser.prompt = user.prompt;
|
||||
state.apps.selectedUser.autoRenew = user.autoRenew;
|
||||
state.apps.selectedUser.licenseAlert = user.licenseAlert;
|
||||
state.apps.selectedUser.notification = user.notification;
|
||||
|
||||
state.apps.hasPasswordMask = user.encryption;
|
||||
},
|
||||
changeUpdateRole: (state, action: PayloadAction<{ role: RoleType }>) => {
|
||||
const { role } = action.payload;
|
||||
state.apps.updateUser.role = role;
|
||||
},
|
||||
changeUpdateAuthorId: (
|
||||
state,
|
||||
action: PayloadAction<{ authorId: string }>
|
||||
) => {
|
||||
const { authorId } = action.payload;
|
||||
state.apps.updateUser.authorId = authorId;
|
||||
},
|
||||
changeUpdateEncryption: (
|
||||
state,
|
||||
action: PayloadAction<{ encryption: boolean }>
|
||||
) => {
|
||||
const { encryption } = action.payload;
|
||||
state.apps.updateUser.encryption = encryption;
|
||||
const initEncryption = state.apps.selectedUser.encryption;
|
||||
const password = state.apps.updateUser.encryptionPassword;
|
||||
|
||||
if (initEncryption && encryption && !password) {
|
||||
state.apps.hasPasswordMask = true;
|
||||
}
|
||||
},
|
||||
changeUpdateEncryptionPassword: (
|
||||
state,
|
||||
action: PayloadAction<{ encryptionPassword: string }>
|
||||
) => {
|
||||
const { encryptionPassword } = action.payload;
|
||||
state.apps.updateUser.encryptionPassword =
|
||||
encryptionPassword === "" ? undefined : encryptionPassword;
|
||||
},
|
||||
changeUpdatePrompt: (state, action: PayloadAction<{ prompt: boolean }>) => {
|
||||
const { prompt } = action.payload;
|
||||
state.apps.updateUser.prompt = prompt;
|
||||
},
|
||||
changeUpdateAutoRenew: (
|
||||
state,
|
||||
action: PayloadAction<{ autoRenew: boolean }>
|
||||
) => {
|
||||
const { autoRenew } = action.payload;
|
||||
state.apps.updateUser.autoRenew = autoRenew;
|
||||
},
|
||||
changeUpdateLicenseAlert: (
|
||||
state,
|
||||
action: PayloadAction<{ licenseAlert: boolean }>
|
||||
) => {
|
||||
const { licenseAlert } = action.payload;
|
||||
state.apps.updateUser.licenseAlert = licenseAlert;
|
||||
},
|
||||
changeUpdateNotification: (
|
||||
state,
|
||||
action: PayloadAction<{ notification: boolean }>
|
||||
) => {
|
||||
const { notification } = action.payload;
|
||||
state.apps.updateUser.notification = notification;
|
||||
},
|
||||
changeHasPasswordMask: (
|
||||
state,
|
||||
action: PayloadAction<{ hasPasswordMask: boolean }>
|
||||
) => {
|
||||
const { hasPasswordMask } = action.payload;
|
||||
state.apps.hasPasswordMask = hasPasswordMask;
|
||||
},
|
||||
cleanupUpdateUser: (state) => {
|
||||
state.apps.updateUser = initialState.apps.updateUser;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(listUsersAsync.pending, (state) => {
|
||||
@ -106,6 +235,15 @@ export const userSlice = createSlice({
|
||||
builder.addCase(addUserAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(updateUserAsync.pending, (state) => {
|
||||
state.apps.isLoading = true;
|
||||
});
|
||||
builder.addCase(updateUserAsync.fulfilled, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(updateUserAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@ -118,9 +256,20 @@ export const {
|
||||
changeLicenseAlert,
|
||||
changeNotification,
|
||||
cleanupAddUser,
|
||||
changeUpdateUser,
|
||||
changeUpdateRole,
|
||||
changeUpdateAuthorId,
|
||||
changeUpdateEncryption,
|
||||
changeUpdateEncryptionPassword,
|
||||
changeUpdatePrompt,
|
||||
changeUpdateAutoRenew,
|
||||
changeUpdateLicenseAlert,
|
||||
changeUpdateNotification,
|
||||
cleanupUpdateUser,
|
||||
changeEncryption,
|
||||
changePrompt,
|
||||
changeEncryptionPassword,
|
||||
changeHasPasswordMask,
|
||||
} = userSlice.actions;
|
||||
|
||||
export default userSlice.reducer;
|
||||
|
||||
@ -16,22 +16,33 @@ import { isLicenseStatusType } from "features/user/types";
|
||||
import { LICENSE_STATUS } from "features/user/constants";
|
||||
import { isApproveTier } from "features/auth/utils";
|
||||
import { TIERS } from "components/auth/constants";
|
||||
import { changeUpdateUser } from "features/user/userSlice";
|
||||
import personAdd from "../../assets/images/person_add.svg";
|
||||
import checkFill from "../../assets/images/check_fill.svg";
|
||||
import checkOutline from "../../assets/images/check_outline.svg";
|
||||
import progress_activit from "../../assets/images/progress_activit.svg";
|
||||
import { UserAddPopup } from "./popup";
|
||||
import { UserUpdatePopup } from "./updatePopup";
|
||||
|
||||
const UserListPage: React.FC = (): JSX.Element => {
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const [isPopupOpen, setIsPopupOpen] = useState(false);
|
||||
const [isUpdatePopupOpen, setIsUpdatePopupOpen] = useState(false);
|
||||
|
||||
const onOpen = useCallback(() => {
|
||||
setIsPopupOpen(true);
|
||||
}, [setIsPopupOpen]);
|
||||
|
||||
const onUpdateOpen = useCallback(
|
||||
(id: number) => {
|
||||
setIsUpdatePopupOpen(true);
|
||||
dispatch(changeUpdateUser({ id }));
|
||||
},
|
||||
[setIsUpdatePopupOpen, dispatch]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// ユーザ一覧取得処理を呼び出す
|
||||
dispatch(listUsersAsync());
|
||||
@ -44,6 +55,13 @@ const UserListPage: React.FC = (): JSX.Element => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<UserUpdatePopup
|
||||
isOpen={isUpdatePopupOpen}
|
||||
onClose={() => {
|
||||
setIsUpdatePopupOpen(false);
|
||||
dispatch(listUsersAsync());
|
||||
}}
|
||||
/>
|
||||
<UserAddPopup
|
||||
isOpen={isPopupOpen}
|
||||
onClose={() => {
|
||||
@ -130,7 +148,12 @@ const UserListPage: React.FC = (): JSX.Element => {
|
||||
<td className={styles.clm0}>
|
||||
<ul className={styles.menuInTable}>
|
||||
<li>
|
||||
<a href="">
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
||||
<a
|
||||
onClick={() => {
|
||||
onUpdateOpen(user.id);
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"userListPage.label.editUser"
|
||||
|
||||
389
dictation_client/src/pages/UserListPage/updatePopup.tsx
Normal file
389
dictation_client/src/pages/UserListPage/updatePopup.tsx
Normal file
@ -0,0 +1,389 @@
|
||||
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,
|
||||
changeUpdateLicenseAlert,
|
||||
changeUpdateNotification,
|
||||
changeUpdatePrompt,
|
||||
changeUpdateRole,
|
||||
changeHasPasswordMask,
|
||||
cleanupUpdateUser,
|
||||
selectHasPasswordMask,
|
||||
selectIsLoading,
|
||||
selectUpdateUser,
|
||||
selectUpdateValidationErrors,
|
||||
updateUserAsync,
|
||||
} 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;
|
||||
}
|
||||
|
||||
export const UserUpdatePopup: React.FC<UserUpdatePopupProps> = (props) => {
|
||||
const { isOpen, onClose } = 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();
|
||||
}
|
||||
}, [
|
||||
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={USER_ROLES.AUTHOR}>
|
||||
<input
|
||||
id={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={USER_ROLES.TYPIST}>
|
||||
<input
|
||||
id={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={USER_ROLES.NONE}>
|
||||
<input
|
||||
id={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 })
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{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="Auto renew">
|
||||
<input
|
||||
type="checkbox"
|
||||
className={styles.formCheck}
|
||||
checked={user.autoRenew}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
changeUpdateAutoRenew({
|
||||
autoRenew: e.target.checked,
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{t(getTranslationID("userListPage.label.autoRenew"))}
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label htmlFor="License Alert">
|
||||
<input
|
||||
type="checkbox"
|
||||
className={styles.formCheck}
|
||||
checked={user.licenseAlert}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
changeUpdateLicenseAlert({
|
||||
licenseAlert: e.target.checked,
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{t(getTranslationID("userListPage.label.licenseAlert"))}
|
||||
</label>
|
||||
</p>
|
||||
<p>
|
||||
<label htmlFor="Notification">
|
||||
<input
|
||||
type="checkbox"
|
||||
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>
|
||||
);
|
||||
};
|
||||
@ -111,6 +111,7 @@
|
||||
"addUserSuccess": "(de)メールアドレス宛に認証用メールを送信しました。",
|
||||
"authorIdConflictError": "(de)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
|
||||
"authorIdIncorrectError": "(de)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
|
||||
"roleChangeError": "(de)Roleの変更に失敗しました。画面を更新して再度ユーザー情報を取得してください。",
|
||||
"encryptionPasswordCorrectError": "(de)EncryptionPasswordがルールを満たしていません。"
|
||||
},
|
||||
"label": {
|
||||
@ -309,7 +310,6 @@
|
||||
"orderHistoriesPage": {
|
||||
"label": {
|
||||
"title": "(de)License",
|
||||
"subTitle": "(de)EFGI Legal",
|
||||
"orderHistory": "(de)Order History",
|
||||
"return": "(de)Return",
|
||||
"orderDate": "(de)Order date",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"addUserSuccess": "メールアドレス宛に認証用メールを送信しました。",
|
||||
"authorIdConflictError": "このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
|
||||
"authorIdIncorrectError": "Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
|
||||
"roleChangeError": "Roleの変更に失敗しました。画面を更新して再度ユーザー情報を取得してください。",
|
||||
"encryptionPasswordCorrectError": "EncryptionPasswordがルールを満たしていません。"
|
||||
},
|
||||
"label": {
|
||||
@ -309,7 +310,6 @@
|
||||
"orderHistoriesPage": {
|
||||
"label": {
|
||||
"title": "License",
|
||||
"subTitle": "EFGI Legal",
|
||||
"orderHistory": "Order History",
|
||||
"return": "Return",
|
||||
"orderDate": "Order date",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"addUserSuccess": "(es)メールアドレス宛に認証用メールを送信しました。",
|
||||
"authorIdConflictError": "(es)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
|
||||
"authorIdIncorrectError": "(es)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
|
||||
"roleChangeError": "(es)Roleの変更に失敗しました。画面を更新して再度ユーザー情報を取得してください。",
|
||||
"encryptionPasswordCorrectError": "(es)EncryptionPasswordがルールを満たしていません。"
|
||||
},
|
||||
"label": {
|
||||
@ -309,7 +310,6 @@
|
||||
"orderHistoriesPage": {
|
||||
"label": {
|
||||
"title": "(es)License",
|
||||
"subTitle": "(es)EFGI Legal",
|
||||
"orderHistory": "(es)Order History",
|
||||
"return": "(es)Return",
|
||||
"orderDate": "(es)Order date",
|
||||
|
||||
@ -111,6 +111,7 @@
|
||||
"addUserSuccess": "(fr)メールアドレス宛に認証用メールを送信しました。",
|
||||
"authorIdConflictError": "(fr)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
|
||||
"authorIdIncorrectError": "(fr)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
|
||||
"roleChangeError": "(fr)Roleの変更に失敗しました。画面を更新して再度ユーザー情報を取得してください。",
|
||||
"encryptionPasswordCorrectError": "(fr)EncryptionPasswordがルールを満たしていません。"
|
||||
},
|
||||
"label": {
|
||||
@ -309,7 +310,6 @@
|
||||
"orderHistoriesPage": {
|
||||
"label": {
|
||||
"title": "(fr)License",
|
||||
"subTitle": "(fr)EFGI Legal",
|
||||
"orderHistory": "(fr)Order History",
|
||||
"return": "(fr)Return",
|
||||
"orderDate": "(fr)Order date",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user