Merged PR 301: ユーザー追加修正(API/画面)
## 概要 [Task2327: ユーザー追加修正(API/画面)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2327) - ユーザー追加API - リクエストにencryption,encryptionPassword,promptを追加 - リクエストからtypistGroupIdを削除 - ロールに応じてDBに保存するデータを作成する処理を追加 - リクエストパラメータのバリデーションチェックを追加 - ユーザー追加画面 - TypistGroupの選択欄を削除 - RoleがAuthorの場合、encryption,encryptionPassword,promptを追加 ## レビューポイント - 修正に不足はないか - 画面のユーザー追加処理を引数を渡さずにstoreから取得するようにしたが問題ないか - 画面に必要な値をまとめて取るようにしたが問題ないか - デザインに差異はないか ## UIの変更 - 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/Task2327?csf=1&web=1&e=uvTYlb ## 動作確認状況 - ローカルで確認 ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
338d6b88a9
commit
4565d59a51
@ -880,6 +880,67 @@ export interface PostSortCriteriaRequest {
|
||||
*/
|
||||
'paramName': string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface PostUpdateUserRequest
|
||||
*/
|
||||
export interface PostUpdateUserRequest {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'id': number;
|
||||
/**
|
||||
* none/author/typist
|
||||
* @type {string}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'role': string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'authorId'?: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'autoRenew': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'licenseAlart': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'notification': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'encryption'?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'encryptionPassword'?: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof PostUpdateUserRequest
|
||||
*/
|
||||
'prompt'?: boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@ -923,12 +984,6 @@ export interface SignupRequest {
|
||||
* @memberof SignupRequest
|
||||
*/
|
||||
'authorId'?: string;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof SignupRequest
|
||||
*/
|
||||
'typistGroupId'?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@ -953,6 +1008,24 @@ export interface SignupRequest {
|
||||
* @memberof SignupRequest
|
||||
*/
|
||||
'notification': boolean;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SignupRequest
|
||||
*/
|
||||
'encryption'?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof SignupRequest
|
||||
*/
|
||||
'encryptionPassword'?: string;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof SignupRequest
|
||||
*/
|
||||
'prompt'?: boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@ -3852,6 +3925,46 @@ export const UsersApiAxiosParamCreator = function (configuration?: Configuration
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(postSortCriteriaRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* ユーザーの情報を更新します
|
||||
* @summary
|
||||
* @param {PostUpdateUserRequest} postUpdateUserRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateUser: async (postUpdateUserRequest: PostUpdateUserRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'postUpdateUserRequest' is not null or undefined
|
||||
assertParamExists('updateUser', 'postUpdateUserRequest', postUpdateUserRequest)
|
||||
const localVarPath = `/users/update`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
let baseOptions;
|
||||
if (configuration) {
|
||||
baseOptions = configuration.baseOptions;
|
||||
}
|
||||
|
||||
const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options};
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
// authentication bearer required
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(postUpdateUserRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
@ -3941,6 +4054,17 @@ export const UsersApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.updateSortCriteria(postSortCriteriaRequest, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
* ユーザーの情報を更新します
|
||||
* @summary
|
||||
* @param {PostUpdateUserRequest} postUpdateUserRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async updateUser(postUpdateUserRequest: PostUpdateUserRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.updateUser(postUpdateUserRequest, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -4018,6 +4142,16 @@ export const UsersApiFactory = function (configuration?: Configuration, basePath
|
||||
updateSortCriteria(postSortCriteriaRequest: PostSortCriteriaRequest, options?: any): AxiosPromise<object> {
|
||||
return localVarFp.updateSortCriteria(postSortCriteriaRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* ユーザーの情報を更新します
|
||||
* @summary
|
||||
* @param {PostUpdateUserRequest} postUpdateUserRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
updateUser(postUpdateUserRequest: PostUpdateUserRequest, options?: any): AxiosPromise<object> {
|
||||
return localVarFp.updateUser(postUpdateUserRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -4108,6 +4242,18 @@ export class UsersApi extends BaseAPI {
|
||||
public updateSortCriteria(postSortCriteriaRequest: PostSortCriteriaRequest, options?: AxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).updateSortCriteria(postSortCriteriaRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* ユーザーの情報を更新します
|
||||
* @summary
|
||||
* @param {PostUpdateUserRequest} postUpdateUserRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof UsersApi
|
||||
*/
|
||||
public updateUser(postUpdateUserRequest: PostUpdateUserRequest, options?: AxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).updateUser(postUpdateUserRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import type { RootState } from "app/store";
|
||||
import { getTranslationID } from "translation";
|
||||
import { USER_ROLES } from "components/auth/constants";
|
||||
import { openSnackbar } from "features/ui/uiSlice";
|
||||
import { SignupRequest, UsersApi, GetUsersResponse } from "../../api/api";
|
||||
import { getTranslationID } from "translation";
|
||||
import { GetUsersResponse, UsersApi } from "../../api/api";
|
||||
import { Configuration } from "../../api/configuration";
|
||||
import { ErrorObject, createErrorObject } from "../../common/errors";
|
||||
|
||||
@ -43,7 +44,7 @@ export const addUserAsync = createAsyncThunk<
|
||||
{
|
||||
/* Empty Object */
|
||||
},
|
||||
SignupRequest,
|
||||
void,
|
||||
{
|
||||
// rejectした時の返却値の型
|
||||
rejectValue: {
|
||||
@ -51,40 +52,25 @@ export const addUserAsync = createAsyncThunk<
|
||||
};
|
||||
}
|
||||
>("users/addUserAsync", async (args, thunkApi) => {
|
||||
const {
|
||||
name,
|
||||
email,
|
||||
role,
|
||||
authorId,
|
||||
typistGroupId,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
} = args;
|
||||
|
||||
// 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 { addUser } = state.user.apps;
|
||||
// roleがAUTHOR以外の場合、不要なプロパティをundefinedにする
|
||||
if (addUser.role !== USER_ROLES.AUTHOR) {
|
||||
addUser.authorId = undefined;
|
||||
addUser.encryption = undefined;
|
||||
addUser.prompt = undefined;
|
||||
addUser.encryptionPassword = undefined;
|
||||
}
|
||||
|
||||
try {
|
||||
await usersApi.signup(
|
||||
{
|
||||
name,
|
||||
email,
|
||||
role,
|
||||
authorId,
|
||||
typistGroupId,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
},
|
||||
{
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
}
|
||||
);
|
||||
await usersApi.signup(addUser, {
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
});
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
import { RootState } from "app/store";
|
||||
import { USER_ROLES } from "components/auth/constants";
|
||||
import { RoleType, UserView, isLicenseStatusType, isRoleType } from "./types";
|
||||
import {
|
||||
AddUser,
|
||||
RoleType,
|
||||
UserView,
|
||||
isLicenseStatusType,
|
||||
isRoleType,
|
||||
} from "./types";
|
||||
import { LICENSE_STATUS } from "./constants";
|
||||
|
||||
export const selectInputValidationErrors = (state: RootState) => {
|
||||
const { name, email, role, authorId } = state.user.apps.addUser;
|
||||
const { name, email, role, authorId, encryption, encryptionPassword } =
|
||||
state.user.apps.addUser;
|
||||
|
||||
// 必須項目のチェック
|
||||
const hasErrorEmptyName = name === "";
|
||||
const hasErrorEmptyEmail = email === "";
|
||||
const hasErrorEmptyAuthorId = role === USER_ROLES.AUTHOR && authorId === "";
|
||||
// Authorの場合、AuthorIDが必須(空文字,undefinedは不可)
|
||||
const hasErrorEmptyAuthorId =
|
||||
role === USER_ROLES.AUTHOR && (authorId === "" || !authorId);
|
||||
|
||||
const hasErrorIncorrectAuthorId = checkErrorIncorrectAuthorId(
|
||||
authorId ?? undefined,
|
||||
@ -18,14 +27,46 @@ export const selectInputValidationErrors = (state: RootState) => {
|
||||
|
||||
const hasErrorIncorrectEmail = email.match(/^[^@]+@[^@]+$/)?.length !== 1;
|
||||
|
||||
const hasErrorIncorrectEncryptionPassword =
|
||||
checkErrorIncorrectEncryptionPassword(encryptionPassword, role, encryption);
|
||||
|
||||
return {
|
||||
hasErrorEmptyName,
|
||||
hasErrorEmptyEmail,
|
||||
hasErrorEmptyAuthorId,
|
||||
hasErrorIncorrectEmail,
|
||||
hasErrorIncorrectAuthorId,
|
||||
hasErrorIncorrectEncryptionPassword,
|
||||
};
|
||||
};
|
||||
|
||||
// encreyptionPasswordのチェック
|
||||
const checkErrorIncorrectEncryptionPassword = (
|
||||
encryptionPassword: string | undefined,
|
||||
role: RoleType,
|
||||
encryption: boolean | undefined
|
||||
): boolean => {
|
||||
// roleがAuthor以外の場合、チェックしない
|
||||
if (role !== USER_ROLES.AUTHOR) {
|
||||
return false;
|
||||
}
|
||||
// roleがAuthorかつencryptionがfalseの場合、チェックしない
|
||||
if (!encryption) {
|
||||
return false;
|
||||
}
|
||||
// encryptionPasswordがundefined,空文字の場合、エラー
|
||||
if (!encryptionPassword || encryptionPassword === "") {
|
||||
return true;
|
||||
}
|
||||
// encryptionPasswordがルールに則していない場合、エラー
|
||||
const regex = /^[!-~]{4,16}$/;
|
||||
if (!regex.test(encryptionPassword)) {
|
||||
return true;
|
||||
}
|
||||
// チェックを通ったらエラーではない
|
||||
return false;
|
||||
};
|
||||
|
||||
export const checkErrorIncorrectAuthorId = (
|
||||
authorId: string | undefined,
|
||||
role: string
|
||||
@ -46,14 +87,15 @@ export const selectEmail = (state: RootState) => state.user.apps.addUser.email;
|
||||
export const selectRole = (state: RootState) => state.user.apps.addUser.role;
|
||||
export const selectAuthorId = (state: RootState) =>
|
||||
state.user.apps.addUser.authorId;
|
||||
export const selectTypistGroupId = (state: RootState) =>
|
||||
state.user.apps.addUser.typistGroupId;
|
||||
export const selectAutoRenew = (state: RootState) =>
|
||||
state.user.apps.addUser.autoRenew;
|
||||
export const selectLicenseAlert = (state: RootState) =>
|
||||
state.user.apps.addUser.licenseAlert;
|
||||
export const selectNtotification = (state: RootState) =>
|
||||
export const selectNotification = (state: RootState) =>
|
||||
state.user.apps.addUser.notification;
|
||||
// AddUserを返却する
|
||||
export const selectAddUser = (state: RootState): AddUser =>
|
||||
state.user.apps.addUser;
|
||||
// usersからUserViewに変換して返却する
|
||||
export const selectUserViews = (state: RootState): UserView[] => {
|
||||
const { users } = state.user.domain;
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { User } from "../../api/api";
|
||||
import { AddUser } from "./types";
|
||||
|
||||
export interface UsersState {
|
||||
domain: Domain;
|
||||
@ -13,7 +14,3 @@ export interface Apps {
|
||||
addUser: AddUser;
|
||||
isLoading: boolean;
|
||||
}
|
||||
|
||||
export interface AddUser extends Omit<User, "id"> {
|
||||
typistGroupId?: number | undefined;
|
||||
}
|
||||
|
||||
@ -23,6 +23,18 @@ export interface UserView
|
||||
expiration: string;
|
||||
remaining: number | string;
|
||||
}
|
||||
export interface AddUser {
|
||||
name: string;
|
||||
role: RoleType;
|
||||
email: string;
|
||||
autoRenew: boolean;
|
||||
licenseAlert: boolean;
|
||||
notification: boolean;
|
||||
authorId?: string;
|
||||
encryption?: boolean;
|
||||
encryptionPassword?: string;
|
||||
prompt?: boolean;
|
||||
}
|
||||
|
||||
export type RoleType = typeof USER_ROLES[keyof typeof USER_ROLES];
|
||||
|
||||
|
||||
@ -10,16 +10,14 @@ const initialState: UsersState = {
|
||||
addUser: {
|
||||
name: "",
|
||||
role: USER_ROLES.NONE,
|
||||
authorId: "",
|
||||
typistGroupName: [],
|
||||
email: "",
|
||||
emailVerified: true,
|
||||
autoRenew: true,
|
||||
licenseAlert: true,
|
||||
notification: true,
|
||||
encryption: true,
|
||||
licenseStatus: "",
|
||||
authorId: "",
|
||||
encryption: false,
|
||||
prompt: false,
|
||||
encryptionPassword: "",
|
||||
},
|
||||
isLoading: false,
|
||||
},
|
||||
@ -48,13 +46,6 @@ export const userSlice = createSlice({
|
||||
const { authorId } = action.payload;
|
||||
state.apps.addUser.authorId = authorId;
|
||||
},
|
||||
changeTypistGroupId: (
|
||||
state,
|
||||
action: PayloadAction<{ typistGroupId: number | undefined }>
|
||||
) => {
|
||||
const { typistGroupId } = action.payload;
|
||||
state.apps.addUser.typistGroupId = typistGroupId;
|
||||
},
|
||||
changeAutoRenew: (state, action: PayloadAction<{ autoRenew: boolean }>) => {
|
||||
const { autoRenew } = action.payload;
|
||||
state.apps.addUser.autoRenew = autoRenew;
|
||||
@ -66,6 +57,24 @@ export const userSlice = createSlice({
|
||||
const { licenseAlert } = action.payload;
|
||||
state.apps.addUser.licenseAlert = licenseAlert;
|
||||
},
|
||||
changeEncryption: (
|
||||
state,
|
||||
action: PayloadAction<{ encryption: boolean }>
|
||||
) => {
|
||||
const { encryption } = action.payload;
|
||||
state.apps.addUser.encryption = encryption;
|
||||
},
|
||||
changePrompt: (state, action: PayloadAction<{ prompt: boolean }>) => {
|
||||
const { prompt } = action.payload;
|
||||
state.apps.addUser.prompt = prompt;
|
||||
},
|
||||
changeEncryptionPassword: (
|
||||
state,
|
||||
action: PayloadAction<{ encryptionPassword: string }>
|
||||
) => {
|
||||
const { encryptionPassword } = action.payload;
|
||||
state.apps.addUser.encryptionPassword = encryptionPassword;
|
||||
},
|
||||
changeNotification: (
|
||||
state,
|
||||
action: PayloadAction<{ notification: boolean }>
|
||||
@ -105,11 +114,13 @@ export const {
|
||||
changeEmail,
|
||||
changeRole,
|
||||
changeAuthorId,
|
||||
changeTypistGroupId,
|
||||
changeAutoRenew,
|
||||
changeLicenseAlert,
|
||||
changeNotification,
|
||||
cleanupAddUser,
|
||||
changeEncryption,
|
||||
changePrompt,
|
||||
changeEncryptionPassword,
|
||||
} = userSlice.actions;
|
||||
|
||||
export default userSlice.reducer;
|
||||
|
||||
@ -9,22 +9,17 @@ import {
|
||||
changeName,
|
||||
changeRole,
|
||||
changeAuthorId,
|
||||
changeTypistGroupId,
|
||||
changeAutoRenew,
|
||||
changeLicenseAlert,
|
||||
changeNotification,
|
||||
cleanupAddUser,
|
||||
selectName,
|
||||
selectEmail,
|
||||
selectRole,
|
||||
selectAuthorId,
|
||||
selectTypistGroupId,
|
||||
selectInputValidationErrors,
|
||||
selectAutoRenew,
|
||||
selectLicenseAlert,
|
||||
selectNtotification,
|
||||
addUserAsync,
|
||||
selectAddUser,
|
||||
selectInputValidationErrors,
|
||||
selectIsLoading,
|
||||
changePrompt,
|
||||
changeEncryption,
|
||||
changeEncryptionPassword,
|
||||
} from "features/user";
|
||||
import { USER_ROLES } from "components/auth/constants";
|
||||
import close from "../../assets/images/close.svg";
|
||||
@ -39,6 +34,9 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
const { isOpen, onClose } = props;
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const [isPasswordHide, setIsPasswordHide] = useState<boolean>(true);
|
||||
// AddUserの情報を取得
|
||||
const addUser = useSelector(selectAddUser);
|
||||
|
||||
const closePopup = useCallback(() => {
|
||||
setIsPushCreateButton(false);
|
||||
@ -52,17 +50,9 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
hasErrorEmptyAuthorId,
|
||||
hasErrorIncorrectEmail,
|
||||
hasErrorIncorrectAuthorId,
|
||||
hasErrorIncorrectEncryptionPassword,
|
||||
} = useSelector(selectInputValidationErrors);
|
||||
|
||||
const name = useSelector(selectName);
|
||||
const email = useSelector(selectEmail);
|
||||
const role = useSelector(selectRole);
|
||||
const authorId = useSelector(selectAuthorId);
|
||||
const typistGroupId = useSelector(selectTypistGroupId);
|
||||
const autoRenew = useSelector(selectAutoRenew);
|
||||
const licenseAlert = useSelector(selectLicenseAlert);
|
||||
const notification = useSelector(selectNtotification);
|
||||
|
||||
const [isPushCreateButton, setIsPushCreateButton] = useState<boolean>(false);
|
||||
|
||||
const isLoading = useSelector(selectIsLoading);
|
||||
@ -74,51 +64,27 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
hasErrorEmptyEmail ||
|
||||
hasErrorEmptyAuthorId ||
|
||||
hasErrorIncorrectEmail ||
|
||||
hasErrorIncorrectAuthorId
|
||||
hasErrorIncorrectAuthorId ||
|
||||
hasErrorIncorrectEncryptionPassword
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (role !== USER_ROLES.AUTHOR) {
|
||||
changeAuthorId({ authorId: undefined });
|
||||
}
|
||||
if (role !== USER_ROLES.TYPIST) {
|
||||
changeTypistGroupId({ typistGroupId: undefined });
|
||||
}
|
||||
|
||||
const { meta } = await dispatch(
|
||||
addUserAsync({
|
||||
name,
|
||||
email,
|
||||
role,
|
||||
authorId:
|
||||
role === USER_ROLES.AUTHOR ? authorId ?? undefined : undefined,
|
||||
typistGroupId: role === USER_ROLES.TYPIST ? typistGroupId : undefined,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
})
|
||||
);
|
||||
const { meta } = await dispatch(addUserAsync());
|
||||
setIsPushCreateButton(false);
|
||||
|
||||
if (meta.requestStatus === "fulfilled") {
|
||||
closePopup();
|
||||
}
|
||||
}, [
|
||||
dispatch,
|
||||
closePopup,
|
||||
hasErrorEmptyName,
|
||||
hasErrorEmptyEmail,
|
||||
hasErrorEmptyAuthorId,
|
||||
hasErrorIncorrectEmail,
|
||||
hasErrorIncorrectAuthorId,
|
||||
name,
|
||||
email,
|
||||
role,
|
||||
authorId,
|
||||
typistGroupId,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
hasErrorIncorrectEncryptionPassword,
|
||||
dispatch,
|
||||
closePopup,
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -142,7 +108,7 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
size={40}
|
||||
maxLength={255}
|
||||
className={styles.formInput}
|
||||
value={name}
|
||||
value={addUser.name}
|
||||
onChange={(e) => {
|
||||
dispatch(changeName({ name: e.target.value }));
|
||||
}}
|
||||
@ -160,7 +126,7 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
size={40}
|
||||
maxLength={255}
|
||||
className={styles.formInput}
|
||||
value={email}
|
||||
value={addUser.email}
|
||||
onChange={(e) => {
|
||||
dispatch(changeEmail({ email: e.target.value }));
|
||||
}}
|
||||
@ -182,10 +148,11 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
<dd>
|
||||
<label htmlFor={USER_ROLES.AUTHOR}>
|
||||
<input
|
||||
id={USER_ROLES.AUTHOR}
|
||||
type="radio"
|
||||
name="role"
|
||||
className={styles.formRadio}
|
||||
checked={role === USER_ROLES.AUTHOR}
|
||||
checked={addUser.role === USER_ROLES.AUTHOR}
|
||||
onChange={() => {
|
||||
dispatch(changeRole({ role: USER_ROLES.AUTHOR }));
|
||||
}}
|
||||
@ -194,10 +161,11 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
</label>
|
||||
<label htmlFor={USER_ROLES.TYPIST}>
|
||||
<input
|
||||
id={USER_ROLES.TYPIST}
|
||||
type="radio"
|
||||
name="role"
|
||||
className={styles.formRadio}
|
||||
checked={role === USER_ROLES.TYPIST}
|
||||
checked={addUser.role === USER_ROLES.TYPIST}
|
||||
onChange={() => {
|
||||
dispatch(changeRole({ role: USER_ROLES.TYPIST }));
|
||||
}}
|
||||
@ -206,10 +174,11 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
</label>
|
||||
<label htmlFor={USER_ROLES.NONE}>
|
||||
<input
|
||||
id={USER_ROLES.NONE}
|
||||
type="radio"
|
||||
name="role"
|
||||
className={styles.formRadio}
|
||||
checked={role === USER_ROLES.NONE}
|
||||
checked={addUser.role === USER_ROLES.NONE}
|
||||
onChange={() => {
|
||||
dispatch(changeRole({ role: USER_ROLES.NONE }));
|
||||
}}
|
||||
@ -218,7 +187,7 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
</label>
|
||||
</dd>
|
||||
{/** Author 選択時に表示 */}
|
||||
{role === USER_ROLES.AUTHOR && (
|
||||
{addUser.role === USER_ROLES.AUTHOR && (
|
||||
<div className={styles.slideSet} id="">
|
||||
<dt>{t(getTranslationID("userListPage.label.authorID"))}</dt>
|
||||
<dd>
|
||||
@ -227,7 +196,7 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
size={40}
|
||||
maxLength={16}
|
||||
className={styles.formInput}
|
||||
value={authorId ?? undefined}
|
||||
value={addUser.authorId ?? undefined}
|
||||
onChange={(e) => {
|
||||
dispatch(changeAuthorId({ authorId: e.target.value }));
|
||||
}}
|
||||
@ -249,38 +218,88 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/** Transcriptionist 選択時に表示 */}
|
||||
{role === USER_ROLES.TYPIST && (
|
||||
<div className={styles.slideSet} id="">
|
||||
<dt className={`${styles.overLine} ${styles.marginBtm0}`}>
|
||||
{t(getTranslationID("userListPage.label.addToGroup"))}
|
||||
</dt>
|
||||
<dt>{t(getTranslationID("userListPage.label.encryption"))}</dt>
|
||||
<dd>
|
||||
<select
|
||||
className={styles.formInput}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
changeTypistGroupId({
|
||||
typistGroupId: Number(e.target.value),
|
||||
})
|
||||
);
|
||||
}}
|
||||
>
|
||||
{/** XXX: タイピストグループは後ほど差し替える */}
|
||||
<option value={0}>
|
||||
{t(getTranslationID("userListPage.label.selectGroup"))}
|
||||
</option>
|
||||
<option value={1}>Tgroup A</option>
|
||||
<option value={2}>Tgroup B</option>
|
||||
<option value={3}>Tgroup C</option>
|
||||
</select>
|
||||
<label htmlFor="encryption">
|
||||
<input
|
||||
type="checkbox"
|
||||
className={styles.formCheck}
|
||||
checked={addUser.encryption}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
changeEncryption({ encryption: e.target.checked })
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
{addUser.encryption && (
|
||||
<p className={`${styles.encryptionPass} ${styles.isShow}`}>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"userListPage.label.encryptionPassword"
|
||||
)
|
||||
)}
|
||||
<input
|
||||
type={isPasswordHide ? "password" : "text"}
|
||||
size={40}
|
||||
maxLength={16}
|
||||
className={`${styles.formInput} ${styles.password}`}
|
||||
value={addUser.encryptionPassword ?? undefined}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
changeEncryptionPassword({
|
||||
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={addUser.prompt}
|
||||
onChange={(e) => {
|
||||
dispatch(changePrompt({ prompt: e.target.checked }));
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</dd>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<dt>{t(getTranslationID("userListPage.label.setting"))}</dt>
|
||||
<dd className={styles.last}>
|
||||
<p>
|
||||
@ -288,7 +307,7 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
<input
|
||||
type="checkbox"
|
||||
className={styles.formCheck}
|
||||
checked={autoRenew}
|
||||
checked={addUser.autoRenew}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
changeAutoRenew({ autoRenew: e.target.checked })
|
||||
@ -302,7 +321,7 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
<label htmlFor="License Alert">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={licenseAlert}
|
||||
checked={addUser.licenseAlert}
|
||||
className={styles.formCheck}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
@ -317,7 +336,7 @@ export const UserAddPopup: React.FC<UserAddPopupProps> = (props) => {
|
||||
<label htmlFor="Notification">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={notification}
|
||||
checked={addUser.notification}
|
||||
className={styles.formCheck}
|
||||
onChange={(e) => {
|
||||
dispatch(
|
||||
|
||||
@ -942,7 +942,7 @@ h3 .brCrumb .tlIcon {
|
||||
}
|
||||
.pageTitle {
|
||||
display: inline-block;
|
||||
padding-right: 1rem;
|
||||
padding-right: 1.5rem;
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.4rem;
|
||||
letter-spacing: 0.07rem;
|
||||
@ -950,6 +950,22 @@ h3 .brCrumb .tlIcon {
|
||||
}
|
||||
.pageTx {
|
||||
display: inline-block;
|
||||
padding-left: 2rem;
|
||||
font-size: 1.4rem;
|
||||
line-height: 1.4rem;
|
||||
letter-spacing: 0.07rem;
|
||||
font-weight: 500;
|
||||
position: relative;
|
||||
}
|
||||
.pageTx::before {
|
||||
content: "";
|
||||
border-top: 6px transparent solid;
|
||||
border-bottom: 6px transparent solid;
|
||||
border-left: 8px #ffffff solid;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 0;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.pagenation {
|
||||
@ -988,6 +1004,15 @@ h3 .brCrumb .tlIcon {
|
||||
.pagenationTotal {
|
||||
color: #999999;
|
||||
}
|
||||
.pagenation.widthMid {
|
||||
width: 1000px;
|
||||
}
|
||||
.pagenation.widthSml {
|
||||
width: 750px;
|
||||
}
|
||||
.pagenation.widthMin {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
_:-ms-lang(x)::-ms-backdrop,
|
||||
.pagenationNav a {
|
||||
@ -1262,7 +1287,8 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user > div,
|
||||
.license > div,
|
||||
.dictation > div,
|
||||
.partners > div {
|
||||
.partners > div,
|
||||
.workflow > div {
|
||||
padding: 0 2rem;
|
||||
position: relative;
|
||||
}
|
||||
@ -1270,7 +1296,8 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user > div .icLoading,
|
||||
.license > div .icLoading,
|
||||
.dictation > div .icLoading,
|
||||
.partners > div .icLoading {
|
||||
.partners > div .icLoading,
|
||||
.workflow > div .icLoading {
|
||||
top: 5.5rem;
|
||||
left: calc(50% - 25px);
|
||||
}
|
||||
@ -1278,7 +1305,8 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user .table tr.tableHeader th.clm0,
|
||||
.license .table tr.tableHeader th.clm0,
|
||||
.dictation .table tr.tableHeader th.clm0,
|
||||
.partners .table tr.tableHeader th.clm0 {
|
||||
.partners .table tr.tableHeader th.clm0,
|
||||
.workflow .table tr.tableHeader th.clm0 {
|
||||
width: 0px;
|
||||
padding: 0 0;
|
||||
}
|
||||
@ -1286,21 +1314,24 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user .table tr:not(.tableHeader),
|
||||
.license .table tr:not(.tableHeader),
|
||||
.dictation .table tr:not(.tableHeader),
|
||||
.partners .table tr:not(.tableHeader) {
|
||||
.partners .table tr:not(.tableHeader),
|
||||
.workflow .table tr:not(.tableHeader) {
|
||||
position: relative;
|
||||
}
|
||||
.account .table tr:not(.tableHeader):hover .menuInTable,
|
||||
.user .table tr:not(.tableHeader):hover .menuInTable,
|
||||
.license .table tr:not(.tableHeader):hover .menuInTable,
|
||||
.dictation .table tr:not(.tableHeader):hover .menuInTable,
|
||||
.partners .table tr:not(.tableHeader):hover .menuInTable {
|
||||
.partners .table tr:not(.tableHeader):hover .menuInTable,
|
||||
.workflow .table tr:not(.tableHeader):hover .menuInTable {
|
||||
opacity: 1;
|
||||
}
|
||||
.account .table tr:not(.tableHeader).isSelected,
|
||||
.user .table tr:not(.tableHeader).isSelected,
|
||||
.license .table tr:not(.tableHeader).isSelected,
|
||||
.dictation .table tr:not(.tableHeader).isSelected,
|
||||
.partners .table tr:not(.tableHeader).isSelected {
|
||||
.partners .table tr:not(.tableHeader).isSelected,
|
||||
.workflow .table tr:not(.tableHeader).isSelected {
|
||||
background: #0084b2;
|
||||
color: #ffffff;
|
||||
}
|
||||
@ -1308,21 +1339,24 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user .table tr:not(.tableHeader).isSelected:hover,
|
||||
.license .table tr:not(.tableHeader).isSelected:hover,
|
||||
.dictation .table tr:not(.tableHeader).isSelected:hover,
|
||||
.partners .table tr:not(.tableHeader).isSelected:hover {
|
||||
.partners .table tr:not(.tableHeader).isSelected:hover,
|
||||
.workflow .table tr:not(.tableHeader).isSelected:hover {
|
||||
color: #ffffff;
|
||||
}
|
||||
.account .table tr:not(.tableHeader).isSelected .menuInTable,
|
||||
.user .table tr:not(.tableHeader).isSelected .menuInTable,
|
||||
.license .table tr:not(.tableHeader).isSelected .menuInTable,
|
||||
.dictation .table tr:not(.tableHeader).isSelected .menuInTable,
|
||||
.partners .table tr:not(.tableHeader).isSelected .menuInTable {
|
||||
.partners .table tr:not(.tableHeader).isSelected .menuInTable,
|
||||
.workflow .table tr:not(.tableHeader).isSelected .menuInTable {
|
||||
display: block;
|
||||
}
|
||||
.account .table td,
|
||||
.user .table td,
|
||||
.license .table td,
|
||||
.dictation .table td,
|
||||
.partners .table td {
|
||||
.partners .table td,
|
||||
.workflow .table td {
|
||||
max-width: 300px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@ -1333,7 +1367,8 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user .table td.clm0,
|
||||
.license .table td.clm0,
|
||||
.dictation .table td.clm0,
|
||||
.partners .table td.clm0 {
|
||||
.partners .table td.clm0,
|
||||
.workflow .table td.clm0 {
|
||||
width: 0px;
|
||||
padding: 0 0;
|
||||
overflow: visible;
|
||||
@ -1345,14 +1380,24 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user .table.user,
|
||||
.license .table.user,
|
||||
.dictation .table.user,
|
||||
.partners .table.user {
|
||||
.partners .table.user,
|
||||
.workflow .table.user {
|
||||
margin-bottom: 5rem;
|
||||
}
|
||||
.account .table.user th::after,
|
||||
.user .table.user th::after,
|
||||
.license .table.user th::after,
|
||||
.dictation .table.user th::after,
|
||||
.partners .table.user th::after,
|
||||
.workflow .table.user th::after {
|
||||
display: none;
|
||||
}
|
||||
.account .table.user tr:not(.tableHeader) td,
|
||||
.user .table.user tr:not(.tableHeader) td,
|
||||
.license .table.user tr:not(.tableHeader) td,
|
||||
.dictation .table.user tr:not(.tableHeader) td,
|
||||
.partners .table.user tr:not(.tableHeader) td {
|
||||
.partners .table.user tr:not(.tableHeader) td,
|
||||
.workflow .table.user tr:not(.tableHeader) td {
|
||||
padding-bottom: 2rem;
|
||||
vertical-align: top;
|
||||
}
|
||||
@ -1360,21 +1405,24 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user.account .listVertical,
|
||||
.license.account .listVertical,
|
||||
.dictation.account .listVertical,
|
||||
.partners.account .listVertical {
|
||||
.partners.account .listVertical,
|
||||
.workflow.account .listVertical {
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
.account.account .listVertical dd .formInput,
|
||||
.user.account .listVertical dd .formInput,
|
||||
.license.account .listVertical dd .formInput,
|
||||
.dictation.account .listVertical dd .formInput,
|
||||
.partners.account .listVertical dd .formInput {
|
||||
.partners.account .listVertical dd .formInput,
|
||||
.workflow.account .listVertical dd .formInput {
|
||||
max-width: 100%;
|
||||
}
|
||||
.account.account .listVertical dd .formCheckToggle,
|
||||
.user.account .listVertical dd .formCheckToggle,
|
||||
.license.account .listVertical dd .formCheckToggle,
|
||||
.dictation.account .listVertical dd .formCheckToggle,
|
||||
.partners.account .listVertical dd .formCheckToggle {
|
||||
.partners.account .listVertical dd .formCheckToggle,
|
||||
.workflow.account .listVertical dd .formCheckToggle {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
@ -1382,7 +1430,8 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user.account .listVertical dd .formCheckToggle .toggleBase,
|
||||
.license.account .listVertical dd .formCheckToggle .toggleBase,
|
||||
.dictation.account .listVertical dd .formCheckToggle .toggleBase,
|
||||
.partners.account .listVertical dd .formCheckToggle .toggleBase {
|
||||
.partners.account .listVertical dd .formCheckToggle .toggleBase,
|
||||
.workflow.account .listVertical dd .formCheckToggle .toggleBase {
|
||||
display: inline-block;
|
||||
width: 2.8rem;
|
||||
height: 1.6rem;
|
||||
@ -1400,7 +1449,8 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user.account .listVertical dd .formCheckToggle .toggleBase::after,
|
||||
.license.account .listVertical dd .formCheckToggle .toggleBase::after,
|
||||
.dictation.account .listVertical dd .formCheckToggle .toggleBase::after,
|
||||
.partners.account .listVertical dd .formCheckToggle .toggleBase::after {
|
||||
.partners.account .listVertical dd .formCheckToggle .toggleBase::after,
|
||||
.workflow.account .listVertical dd .formCheckToggle .toggleBase::after {
|
||||
content: "";
|
||||
width: 1.3rem;
|
||||
height: 1.3rem;
|
||||
@ -1418,7 +1468,8 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.user.account .listVertical dd .formCheckToggle input,
|
||||
.license.account .listVertical dd .formCheckToggle input,
|
||||
.dictation.account .listVertical dd .formCheckToggle input,
|
||||
.partners.account .listVertical dd .formCheckToggle input {
|
||||
.partners.account .listVertical dd .formCheckToggle input,
|
||||
.workflow.account .listVertical dd .formCheckToggle input {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
heigh: 0;
|
||||
@ -1432,7 +1483,8 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.formCheckToggle
|
||||
input:checked
|
||||
~ .toggleBase,
|
||||
.partners.account
|
||||
.partners.account .listVertical dd .formCheckToggle input:checked ~ .toggleBase,
|
||||
.workflow.account
|
||||
.listVertical
|
||||
dd
|
||||
.formCheckToggle
|
||||
@ -1465,6 +1517,12 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
input:checked
|
||||
~ .toggleBase::after,
|
||||
.partners.account
|
||||
.listVertical
|
||||
dd
|
||||
.formCheckToggle
|
||||
input:checked
|
||||
~ .toggleBase::after,
|
||||
.workflow.account
|
||||
.listVertical
|
||||
dd
|
||||
.formCheckToggle
|
||||
@ -1482,6 +1540,9 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
display: inline-block;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
.menuAction li:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
.menuAction.inTable {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
@ -1521,9 +1582,6 @@ _:-ms-lang(x)::-ms-backdrop,
|
||||
.menuAction.inTable .colorLink.isActive:hover {
|
||||
background: rgba(0, 94, 184, 0.7);
|
||||
}
|
||||
.menuAction.alignRight {
|
||||
margin-top: -1rem;
|
||||
}
|
||||
.menuLink {
|
||||
display: block;
|
||||
padding: 0.3rem 0.5rem 0.3rem 0.3rem;
|
||||
@ -1697,6 +1755,9 @@ tr.isSelected .menuInTable li a {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.dictation .menuAction {
|
||||
margin-top: -1rem;
|
||||
}
|
||||
.dictation .displayOptions {
|
||||
display: none;
|
||||
margin-bottom: 0.6rem;
|
||||
@ -2077,6 +2138,9 @@ tr.isSelected .menuInTable li a {
|
||||
background: #282828;
|
||||
z-index: 1;
|
||||
}
|
||||
.partners .table.partner tr.tableHeader th::after {
|
||||
display: none;
|
||||
}
|
||||
.partners .table.partner td {
|
||||
padding-bottom: 2rem;
|
||||
vertical-align: top;
|
||||
@ -2098,6 +2162,113 @@ tr.isSelected .menuInTable li a {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.workflow .table {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.workflow .table.workflow {
|
||||
min-width: 100%;
|
||||
}
|
||||
.workflow .table.workflow tr {
|
||||
position: relative;
|
||||
}
|
||||
.workflow .table.workflow th::after {
|
||||
display: none;
|
||||
}
|
||||
.workflow .table.workflow td {
|
||||
padding-bottom: 2rem;
|
||||
vertical-align: top;
|
||||
}
|
||||
.workflow .table.workflow td.txWsline {
|
||||
white-space: pre;
|
||||
}
|
||||
.workflow .table.group {
|
||||
width: 600px;
|
||||
}
|
||||
.workflow .table.group td:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.formList dd.formChange {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 92%;
|
||||
padding: 0 4%;
|
||||
}
|
||||
.formList dd.formChange ul.chooseMember,
|
||||
.formList dd.formChange ul.holdMember {
|
||||
width: calc(40% - 2px);
|
||||
border: 1px #999999 solid;
|
||||
}
|
||||
.formChange ul.chooseMember,
|
||||
.formChange ul.holdMember {
|
||||
height: 250px;
|
||||
overflow-y: scroll;
|
||||
padding: 0.5rem;
|
||||
background: #ffffff;
|
||||
}
|
||||
.formChange ul.chooseMember li.changeTitle,
|
||||
.formChange ul.holdMember li.changeTitle {
|
||||
font-weight: 600;
|
||||
}
|
||||
.formChange ul.chooseMember li .formCheck,
|
||||
.formChange ul.holdMember li .formCheck {
|
||||
display: none;
|
||||
}
|
||||
.formChange ul.chooseMember li input + label,
|
||||
.formChange ul.holdMember li input + label {
|
||||
display: block;
|
||||
padding: 0.2rem 0 0.2rem 1.5rem;
|
||||
margin-right: 0;
|
||||
background: url(../assets/images/circle.svg) no-repeat left center;
|
||||
background-size: 1.3rem;
|
||||
}
|
||||
.formChange ul.chooseMember li input + label:hover,
|
||||
.formChange ul.holdMember li input + label:hover {
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_left.svg) no-repeat left
|
||||
center;
|
||||
background-size: 1.3rem;
|
||||
}
|
||||
.formChange ul.chooseMember li input:checked + label,
|
||||
.formChange ul.holdMember li input:checked + label {
|
||||
padding: 0.2rem 1rem 0.2rem 0;
|
||||
background: url(../assets/images/check_circle_fill.svg) no-repeat right center;
|
||||
background-size: 1.3rem;
|
||||
}
|
||||
.formChange ul.chooseMember li input:checked + label:hover,
|
||||
.formChange ul.holdMember li input:checked + label:hover {
|
||||
background: #e6e6e6 url(../assets/images/arrow_circle_right.svg) no-repeat
|
||||
right center;
|
||||
background-size: 1.3rem;
|
||||
}
|
||||
.formChange > p {
|
||||
width: 6%;
|
||||
height: 20px;
|
||||
background: #e6e6e6;
|
||||
position: relative;
|
||||
}
|
||||
.formChange > p::before {
|
||||
content: "";
|
||||
border-top: 20px transparent solid;
|
||||
border-right: 20px #e6e6e6 solid;
|
||||
border-bottom: 20px transparent solid;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: -15px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
.formChange > p::after {
|
||||
content: "";
|
||||
border-top: 20px transparent solid;
|
||||
border-bottom: 20px transparent solid;
|
||||
border-left: 20px #e6e6e6 solid;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: -15px;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.alignCenter {
|
||||
text-align: center;
|
||||
}
|
||||
@ -2108,6 +2279,16 @@ tr.isSelected .menuInTable li a {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.floatNone {
|
||||
float: none;
|
||||
}
|
||||
.floatLeft {
|
||||
float: left;
|
||||
}
|
||||
.floatRight {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.linkTx {
|
||||
color: #0084b2;
|
||||
text-decoration: none;
|
||||
|
||||
10
dictation_client/src/styles/app.module.scss.d.ts
vendored
10
dictation_client/src/styles/app.module.scss.d.ts
vendored
@ -67,6 +67,9 @@ declare const classNames: {
|
||||
readonly pagenation: "pagenation";
|
||||
readonly pagenationNav: "pagenationNav";
|
||||
readonly pagenationTotal: "pagenationTotal";
|
||||
readonly widthMid: "widthMid";
|
||||
readonly widthSml: "widthSml";
|
||||
readonly widthMin: "widthMin";
|
||||
readonly snackbar: "snackbar";
|
||||
readonly isAlert: "isAlert";
|
||||
readonly snackbarMessage: "snackbarMessage";
|
||||
@ -88,6 +91,7 @@ declare const classNames: {
|
||||
readonly license: "license";
|
||||
readonly dictation: "dictation";
|
||||
readonly partners: "partners";
|
||||
readonly workflow: "workflow";
|
||||
readonly clm0: "clm0";
|
||||
readonly menuInTable: "menuInTable";
|
||||
readonly isSelected: "isSelected";
|
||||
@ -97,7 +101,6 @@ declare const classNames: {
|
||||
readonly inTable: "inTable";
|
||||
readonly menuLink: "menuLink";
|
||||
readonly colorLink: "colorLink";
|
||||
readonly alignRight: "alignRight";
|
||||
readonly menuIcon: "menuIcon";
|
||||
readonly isDisable: "isDisable";
|
||||
readonly icCheckCircle: "icCheckCircle";
|
||||
@ -174,8 +177,13 @@ declare const classNames: {
|
||||
readonly holdMember: "holdMember";
|
||||
readonly changeTitle: "changeTitle";
|
||||
readonly role4: "role4";
|
||||
readonly group: "group";
|
||||
readonly alignCenter: "alignCenter";
|
||||
readonly alignLeft: "alignLeft";
|
||||
readonly alignRight: "alignRight";
|
||||
readonly floatNone: "floatNone";
|
||||
readonly floatLeft: "floatLeft";
|
||||
readonly floatRight: "floatRight";
|
||||
readonly linkTx: "linkTx";
|
||||
readonly linkBottom: "linkBottom";
|
||||
readonly borderTop: "borderTop";
|
||||
|
||||
@ -110,7 +110,8 @@
|
||||
"message": {
|
||||
"addUserSuccess": "(de)メールアドレス宛に認証用メールを送信しました。",
|
||||
"authorIdConflictError": "(de)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
|
||||
"authorIdIncorrectError": "(de)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。"
|
||||
"authorIdIncorrectError": "(de)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
|
||||
"encryptionPasswordCorrectError": "(de)EncryptionPasswordがルールを満たしていません。"
|
||||
},
|
||||
"label": {
|
||||
"title": "(de)User",
|
||||
@ -143,7 +144,9 @@
|
||||
"editUser": "(de)Edit User",
|
||||
"licenseDeallocation": "(de)License Deallocation",
|
||||
"deleteUser": "(de)Delete User",
|
||||
"none": "(de)None"
|
||||
"none": "(de)None",
|
||||
"encryptionPassword": "(de)Password:",
|
||||
"encryptionPasswordTerm": "(de)Please set your password using 4 to 16 ASCII characters.\nYour password may include letters, numbers, and symbols."
|
||||
}
|
||||
},
|
||||
"LicenseSummaryPage": {
|
||||
|
||||
@ -110,7 +110,8 @@
|
||||
"message": {
|
||||
"addUserSuccess": "メールアドレス宛に認証用メールを送信しました。",
|
||||
"authorIdConflictError": "このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
|
||||
"authorIdIncorrectError": "Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。"
|
||||
"authorIdIncorrectError": "Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
|
||||
"encryptionPasswordCorrectError": "EncryptionPasswordがルールを満たしていません。"
|
||||
},
|
||||
"label": {
|
||||
"title": "User",
|
||||
@ -143,7 +144,9 @@
|
||||
"editUser": "Edit User",
|
||||
"licenseDeallocation": "License Deallocation",
|
||||
"deleteUser": "Delete User",
|
||||
"none": "None"
|
||||
"none": "None",
|
||||
"encryptionPassword": "Password:",
|
||||
"encryptionPasswordTerm": "Please set your password using 4 to 16 ASCII characters.\nYour password may include letters, numbers, and symbols."
|
||||
}
|
||||
},
|
||||
"LicenseSummaryPage": {
|
||||
|
||||
@ -110,7 +110,8 @@
|
||||
"message": {
|
||||
"addUserSuccess": "(es)メールアドレス宛に認証用メールを送信しました。",
|
||||
"authorIdConflictError": "(es)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
|
||||
"authorIdIncorrectError": "(es)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。"
|
||||
"authorIdIncorrectError": "(es)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
|
||||
"encryptionPasswordCorrectError": "(es)EncryptionPasswordがルールを満たしていません。"
|
||||
},
|
||||
"label": {
|
||||
"title": "(es)User",
|
||||
@ -143,7 +144,9 @@
|
||||
"editUser": "(es)Edit User",
|
||||
"licenseDeallocation": "(es)License Deallocation",
|
||||
"deleteUser": "(es)Delete User",
|
||||
"none": "(es)None"
|
||||
"none": "(es)None",
|
||||
"encryptionPassword": "(es)Password:",
|
||||
"encryptionPasswordTerm": "(es)Please set your password using 4 to 16 ASCII characters.\nYour password may include letters, numbers, and symbols."
|
||||
}
|
||||
},
|
||||
"LicenseSummaryPage": {
|
||||
|
||||
@ -110,7 +110,8 @@
|
||||
"message": {
|
||||
"addUserSuccess": "(fr)メールアドレス宛に認証用メールを送信しました。",
|
||||
"authorIdConflictError": "(fr)このAuthor IDは既に登録されています。他のAuthor IDで登録してください。",
|
||||
"authorIdIncorrectError": "(fr)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。"
|
||||
"authorIdIncorrectError": "(fr)Author IDの形式が不正です。Author IDは半角英数字(大文字)と\"_\"のみ入力可能です。",
|
||||
"encryptionPasswordCorrectError": "(fr)EncryptionPasswordがルールを満たしていません。"
|
||||
},
|
||||
"label": {
|
||||
"title": "(fr)User",
|
||||
@ -143,7 +144,9 @@
|
||||
"editUser": "(fr)Edit User",
|
||||
"licenseDeallocation": "(fr)License Deallocation",
|
||||
"deleteUser": "(fr)Delete User",
|
||||
"none": "(fr)None"
|
||||
"none": "(fr)None",
|
||||
"encryptionPassword": "(fr)Password:",
|
||||
"encryptionPasswordTerm": "(fr)Please set your password using 4 to 16 ASCII characters.\nYour password may include letters, numbers, and symbols."
|
||||
}
|
||||
},
|
||||
"LicenseSummaryPage": {
|
||||
|
||||
@ -2179,11 +2179,13 @@
|
||||
"name": { "type": "string" },
|
||||
"role": { "type": "string", "description": "none/author/typist" },
|
||||
"authorId": { "type": "string" },
|
||||
"typistGroupId": { "type": "number" },
|
||||
"email": { "type": "string" },
|
||||
"autoRenew": { "type": "boolean" },
|
||||
"licenseAlert": { "type": "boolean" },
|
||||
"notification": { "type": "boolean" }
|
||||
"notification": { "type": "boolean" },
|
||||
"encryption": { "type": "boolean" },
|
||||
"encryptionPassword": { "type": "string" },
|
||||
"prompt": { "type": "boolean" }
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
|
||||
@ -6,3 +6,5 @@ import { ADMIN_ROLES, USER_ROLES } from '../../../constants';
|
||||
export type Roles =
|
||||
| (typeof ADMIN_ROLES)[keyof typeof ADMIN_ROLES]
|
||||
| (typeof USER_ROLES)[keyof typeof USER_ROLES];
|
||||
|
||||
export type UserRoles = (typeof USER_ROLES)[keyof typeof USER_ROLES];
|
||||
|
||||
@ -1,4 +1,9 @@
|
||||
import { registerDecorator, ValidationOptions } from 'class-validator';
|
||||
import {
|
||||
registerDecorator,
|
||||
ValidationArguments,
|
||||
ValidationOptions,
|
||||
} from 'class-validator';
|
||||
import { SignupRequest } from '../../features/users/types/types';
|
||||
|
||||
export const IsPasswordvalid = (validationOptions?: ValidationOptions) => {
|
||||
return (object: any, propertyName: string) => {
|
||||
@ -30,3 +35,29 @@ export const IsPasswordvalid = (validationOptions?: ValidationOptions) => {
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const IsEncryptionPasswordPresent = (
|
||||
validationOptions?: ValidationOptions,
|
||||
) => {
|
||||
return (object: SignupRequest, propertyName: string) => {
|
||||
registerDecorator({
|
||||
name: 'IsEncryptionPasswordValid',
|
||||
target: object.constructor,
|
||||
propertyName: propertyName,
|
||||
constraints: [],
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate: (value: string | undefined, args: ValidationArguments) => {
|
||||
const { encryption } = args.object as SignupRequest;
|
||||
if (encryption === true && !value) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
defaultMessage: () => {
|
||||
return 'Encryption password is required when encryption is enabled';
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import {
|
||||
registerDecorator,
|
||||
ValidationOptions,
|
||||
ValidationArguments,
|
||||
ValidationOptions,
|
||||
} from 'class-validator';
|
||||
import { USER_ROLES } from '../../constants';
|
||||
import {
|
||||
SignupRequest,
|
||||
PostUpdateUserRequest,
|
||||
SignupRequest,
|
||||
} from '../../features/users/types/types';
|
||||
import { USER_ROLES } from '../../constants';
|
||||
|
||||
export const IsRoleAuthorDataValid = <
|
||||
T extends SignupRequest | PostUpdateUserRequest,
|
||||
@ -22,7 +22,7 @@ export const IsRoleAuthorDataValid = <
|
||||
constraints: [],
|
||||
options: validationOptions,
|
||||
validator: {
|
||||
validate: (value: boolean | undefined, args: ValidationArguments) => {
|
||||
validate: (value: any, args: ValidationArguments) => {
|
||||
const request = args.object as T;
|
||||
const { role } = request;
|
||||
if (role === USER_ROLES.AUTHOR && value === undefined) {
|
||||
|
||||
@ -5,8 +5,11 @@ import {
|
||||
USER_LICENSE_STATUS,
|
||||
} from '../../../constants';
|
||||
import { USER_ROLES } from '../../../constants';
|
||||
import {
|
||||
IsEncryptionPasswordPresent,
|
||||
IsPasswordvalid,
|
||||
} from '../../../common/validators/encryptionPassword.validator';
|
||||
import { IsRoleAuthorDataValid } from '../../../common/validators/roleAuthor.validator';
|
||||
import { IsPasswordvalid } from '../../../common/validators/encryptionPassword.validator';
|
||||
|
||||
export class ConfirmRequest {
|
||||
@ApiProperty()
|
||||
@ -81,11 +84,9 @@ export class SignupRequest {
|
||||
role: string;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsRoleAuthorDataValid()
|
||||
authorId?: string | undefined;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
typistGroupId?: number | undefined;
|
||||
|
||||
@ApiProperty()
|
||||
email: string;
|
||||
|
||||
@ -97,6 +98,19 @@ export class SignupRequest {
|
||||
|
||||
@ApiProperty()
|
||||
notification: boolean;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsRoleAuthorDataValid()
|
||||
encryption?: boolean | undefined;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsPasswordvalid()
|
||||
@IsEncryptionPasswordPresent()
|
||||
encryptionPassword?: string | undefined;
|
||||
|
||||
@ApiProperty({ required: false })
|
||||
@IsRoleAuthorDataValid()
|
||||
prompt?: boolean | undefined;
|
||||
}
|
||||
|
||||
export class SignupResponse {}
|
||||
|
||||
@ -44,6 +44,7 @@ import {
|
||||
import { ADMIN_ROLES } from '../../constants';
|
||||
import { RoleGuard } from '../../common/guards/role/roleguards';
|
||||
import { makeContext } from '../../common/log';
|
||||
import { UserRoles } from '../../common/types/role';
|
||||
|
||||
@ApiTags('users')
|
||||
@Controller('users')
|
||||
@ -162,7 +163,9 @@ export class UsersController {
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
typistGroupId,
|
||||
encryption,
|
||||
encryptionPassword,
|
||||
prompt,
|
||||
} = body;
|
||||
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
@ -172,13 +175,15 @@ export class UsersController {
|
||||
await this.usersService.createUser(
|
||||
payload,
|
||||
name,
|
||||
role,
|
||||
role as UserRoles,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
typistGroupId,
|
||||
encryption,
|
||||
encryptionPassword,
|
||||
prompt,
|
||||
);
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -285,7 +285,7 @@ describe('UsersService.confirmUser', () => {
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user1';
|
||||
const role = 'None';
|
||||
const role = USER_ROLES.NONE;
|
||||
const email = 'test1@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
@ -306,7 +306,7 @@ describe('UsersService.confirmUser', () => {
|
||||
});
|
||||
|
||||
describe('UsersService.createUser', () => {
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author)', async () => {
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化あり)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
@ -321,12 +321,15 @@ describe('UsersService.createUser', () => {
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user2';
|
||||
const role = 'Author';
|
||||
const role = USER_ROLES.AUTHOR;
|
||||
const email = 'test2@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const authorId = 'testID';
|
||||
const encryption = true;
|
||||
const prompt = true;
|
||||
const encryptionPassword = 'testPassword';
|
||||
const token: AccessToken = { userId: '0001', role: '', tier: 5 };
|
||||
expect(
|
||||
await service.createUser(
|
||||
@ -338,6 +341,51 @@ describe('UsersService.createUser', () => {
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
encryption,
|
||||
encryptionPassword,
|
||||
prompt,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化無し)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const sortCriteriaRepositoryMockValue =
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user2';
|
||||
const role = USER_ROLES.AUTHOR;
|
||||
const email = 'test2@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const authorId = 'testID';
|
||||
const encryption = false;
|
||||
const prompt = true;
|
||||
const encryptionPassword = undefined;
|
||||
const token: AccessToken = { userId: '0001', role: '', tier: 5 };
|
||||
expect(
|
||||
await service.createUser(
|
||||
token,
|
||||
name,
|
||||
role,
|
||||
email,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
encryption,
|
||||
encryptionPassword,
|
||||
prompt,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
@ -357,12 +405,11 @@ describe('UsersService.createUser', () => {
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user3';
|
||||
const role = 'Transcriptioninst';
|
||||
const role = USER_ROLES.TYPIST;
|
||||
const email = 'test3@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
const notification = true;
|
||||
const typistGroupId = 111;
|
||||
const token: AccessToken = { userId: '0001', role: '', tier: 5 };
|
||||
expect(
|
||||
await service.createUser(
|
||||
@ -374,7 +421,6 @@ describe('UsersService.createUser', () => {
|
||||
licenseAlert,
|
||||
notification,
|
||||
undefined,
|
||||
typistGroupId,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
});
|
||||
@ -394,7 +440,7 @@ describe('UsersService.createUser', () => {
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user5';
|
||||
const role = 'Transcriptioninst';
|
||||
const role = USER_ROLES.TYPIST;
|
||||
const email = 'test5@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
@ -433,7 +479,7 @@ describe('UsersService.createUser', () => {
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user6';
|
||||
const role = 'Transcriptioninst';
|
||||
const role = USER_ROLES.TYPIST;
|
||||
const email = 'test6@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
@ -472,7 +518,7 @@ describe('UsersService.createUser', () => {
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user7';
|
||||
const role = 'Transcriptioninst';
|
||||
const role = USER_ROLES.TYPIST;
|
||||
const email = 'test7@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
@ -510,7 +556,7 @@ describe('UsersService.createUser', () => {
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user8';
|
||||
const role = 'Author';
|
||||
const role = USER_ROLES.AUTHOR;
|
||||
const email = 'test8@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
@ -550,7 +596,7 @@ describe('UsersService.createUser', () => {
|
||||
sortCriteriaRepositoryMockValue,
|
||||
);
|
||||
const name = 'test_user9';
|
||||
const role = 'Author';
|
||||
const role = USER_ROLES.AUTHOR;
|
||||
const email = 'test9@example.co.jp';
|
||||
const autoRenew = true;
|
||||
const licenseAlert = true;
|
||||
|
||||
@ -18,7 +18,10 @@ import {
|
||||
} from '../../gateways/adb2c/adb2c.service';
|
||||
import { SendGridService } from '../../gateways/sendgrid/sendgrid.service';
|
||||
import { SortCriteriaRepositoryService } from '../../repositories/sort_criteria/sort_criteria.repository.service';
|
||||
import { User as EntityUser } from '../../repositories/users/entity/user.entity';
|
||||
import {
|
||||
User as EntityUser,
|
||||
newUser,
|
||||
} from '../../repositories/users/entity/user.entity';
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
import { GetRelationsResponse, User } from './types/types';
|
||||
import {
|
||||
@ -31,9 +34,11 @@ import {
|
||||
import {
|
||||
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||
USER_LICENSE_STATUS,
|
||||
USER_ROLES,
|
||||
} from '../../constants';
|
||||
import { DateWithZeroTime } from '../licenses/types/types';
|
||||
import { Context } from '../../common/log';
|
||||
import { UserRoles } from '../../common/types/role';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
@ -71,7 +76,7 @@ export class UsersService {
|
||||
const userId = decodedToken.userId;
|
||||
await this.usersRepository.updateUserVerified(userId);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
this.logger.error(e);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
case EmailAlreadyVerifiedError:
|
||||
@ -87,7 +92,6 @@ export class UsersService {
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.error(`error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
@ -96,20 +100,32 @@ export class UsersService {
|
||||
}
|
||||
|
||||
/**
|
||||
* ユーザを作成する
|
||||
* @param token
|
||||
* @returns account
|
||||
* Creates user
|
||||
* @param accessToken
|
||||
* @param name
|
||||
* @param role
|
||||
* @param email
|
||||
* @param autoRenew
|
||||
* @param licenseAlert
|
||||
* @param notification
|
||||
* @param [authorId]
|
||||
* @param [encryption]
|
||||
* @param [encryptionPassword]
|
||||
* @param [prompt]
|
||||
* @returns void
|
||||
*/
|
||||
async createUser(
|
||||
accessToken: AccessToken,
|
||||
name: string,
|
||||
role: string,
|
||||
role: UserRoles,
|
||||
email: string,
|
||||
autoRenew: boolean,
|
||||
licenseAlert: boolean,
|
||||
notification: boolean,
|
||||
authorId?: string | undefined,
|
||||
groupID?: number | undefined,
|
||||
encryption?: boolean | undefined,
|
||||
encryptionPassword?: string | undefined,
|
||||
prompt?: boolean | undefined,
|
||||
): Promise<void> {
|
||||
this.logger.log(`[IN] ${this.createUser.name}`);
|
||||
|
||||
@ -120,6 +136,7 @@ export class UsersService {
|
||||
accessToken.userId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
@ -137,6 +154,7 @@ export class UsersService {
|
||||
authorId,
|
||||
);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
@ -163,7 +181,8 @@ export class UsersService {
|
||||
name,
|
||||
);
|
||||
} catch (e) {
|
||||
console.log('create externalUser failed');
|
||||
this.logger.error(`error=${e}`);
|
||||
this.logger.error('create externalUser failed');
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
@ -180,22 +199,26 @@ export class UsersService {
|
||||
|
||||
//Azure AD B2Cに登録したユーザー情報のID(sub)と受け取った情報を使ってDBにユーザーを登録する
|
||||
let newUser: EntityUser;
|
||||
// TODO [Task2246] 本来はNULLだが、テーブル定義に誤ってNOTNULLが付いているため、一時的に適当な値を設定
|
||||
const accepted_terms_version = 'xxx';
|
||||
|
||||
try {
|
||||
// ユーザ作成
|
||||
newUser = await this.usersRepository.createNormalUser(
|
||||
//roleに応じてユーザー情報を作成する
|
||||
const newUserInfo = this.createNewUserInfo(
|
||||
role,
|
||||
accountId,
|
||||
externalUser.sub,
|
||||
role,
|
||||
autoRenew,
|
||||
licenseAlert,
|
||||
notification,
|
||||
authorId,
|
||||
accepted_terms_version,
|
||||
encryption,
|
||||
encryptionPassword,
|
||||
prompt,
|
||||
);
|
||||
// ユーザ作成
|
||||
newUser = await this.usersRepository.createNormalUser(newUserInfo);
|
||||
} catch (e) {
|
||||
console.log('create user failed');
|
||||
this.logger.error(`error=${e}`);
|
||||
this.logger.error('create user failed');
|
||||
switch (e.code) {
|
||||
case 'ER_DUP_ENTRY':
|
||||
//AuthorID重複エラー
|
||||
@ -227,8 +250,9 @@ export class UsersService {
|
||||
//SendGridAPIを呼び出してメールを送信する
|
||||
await this.sendgridService.sendMail(email, from, subject, text, html);
|
||||
} catch (e) {
|
||||
console.log('create user failed');
|
||||
console.log(`[NOT IMPLEMENT] [RECOVER] delete user: ${newUser.id}`);
|
||||
this.logger.error(`error=${e}`);
|
||||
this.logger.error('create user failed');
|
||||
this.logger.error(`[NOT IMPLEMENT] [RECOVER] delete user: ${newUser.id}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
@ -237,6 +261,54 @@ export class UsersService {
|
||||
this.logger.log(`[OUT] ${this.createUser.name}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// roleを受け取って、roleに応じたnewUserを作成して返却する
|
||||
private createNewUserInfo(
|
||||
role: UserRoles,
|
||||
accountId: number,
|
||||
externalId: string,
|
||||
autoRenew: boolean,
|
||||
licenseAlert: boolean,
|
||||
notification: boolean,
|
||||
authorId?: string | undefined,
|
||||
encryption?: boolean | undefined,
|
||||
encryptionPassword?: string | undefined,
|
||||
prompt?: boolean | undefined,
|
||||
): newUser {
|
||||
switch (role) {
|
||||
case USER_ROLES.NONE:
|
||||
case USER_ROLES.TYPIST:
|
||||
return {
|
||||
account_id: accountId,
|
||||
external_id: externalId,
|
||||
auto_renew: autoRenew,
|
||||
license_alert: licenseAlert,
|
||||
notification,
|
||||
role,
|
||||
};
|
||||
case USER_ROLES.AUTHOR:
|
||||
return {
|
||||
account_id: accountId,
|
||||
external_id: externalId,
|
||||
auto_renew: autoRenew,
|
||||
license_alert: licenseAlert,
|
||||
notification,
|
||||
role,
|
||||
author_id: authorId,
|
||||
encryption,
|
||||
encryption_password: encryptionPassword,
|
||||
prompt,
|
||||
};
|
||||
default:
|
||||
//不正なroleが指定された場合はログを出力してエラーを返す
|
||||
this.logger.error(`[NOT IMPLEMENT] [RECOVER] role: ${role}`);
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* confirm User And Init Password
|
||||
* @param token ユーザ仮登録時に払いだされるトークン
|
||||
|
||||
@ -30,8 +30,8 @@ export class User {
|
||||
@Column({ nullable: true })
|
||||
author_id?: string;
|
||||
|
||||
@Column()
|
||||
accepted_terms_version: string;
|
||||
@Column({ nullable: true })
|
||||
accepted_terms_version?: string;
|
||||
|
||||
@Column()
|
||||
email_verified: boolean;
|
||||
@ -45,14 +45,14 @@ export class User {
|
||||
@Column()
|
||||
notification: boolean;
|
||||
|
||||
@Column()
|
||||
encryption: boolean;
|
||||
@Column({ nullable: true })
|
||||
encryption?: boolean;
|
||||
|
||||
@Column({ nullable: true })
|
||||
encryption_password?: string;
|
||||
|
||||
@Column()
|
||||
prompt: boolean;
|
||||
@Column({ nullable: true })
|
||||
prompt?: boolean;
|
||||
|
||||
@Column({ nullable: true })
|
||||
deleted_at?: Date;
|
||||
@ -79,3 +79,17 @@ export class User {
|
||||
@OneToMany(() => UserGroupMember, (userGroupMember) => userGroupMember.user)
|
||||
userGroupMembers?: UserGroupMember[];
|
||||
}
|
||||
|
||||
export type newUser = Omit<
|
||||
User,
|
||||
| 'id'
|
||||
| 'deleted_at'
|
||||
| 'created_at'
|
||||
| 'updated_at'
|
||||
| 'updated_by'
|
||||
| 'created_by'
|
||||
| 'account'
|
||||
| 'license'
|
||||
| 'userGroupMembers'
|
||||
| 'email_verified'
|
||||
>;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { User, newUser } from './entity/user.entity';
|
||||
import { DataSource, IsNull, Not, UpdateResult } from 'typeorm';
|
||||
import { User } from './entity/user.entity';
|
||||
import { SortCriteria } from '../sort_criteria/entity/sort_criteria.entity';
|
||||
import {
|
||||
getDirection,
|
||||
@ -45,41 +45,42 @@ export class UsersRepositoryService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 一般ユーザを作成する
|
||||
* @param account_id
|
||||
* @param external_id
|
||||
* @param role
|
||||
* @param autoRenew
|
||||
* @param licenseAlert
|
||||
* @param notification
|
||||
* @param authorId
|
||||
* 一般ユーザーを作成する
|
||||
* @param user
|
||||
* @returns User
|
||||
*/
|
||||
async createNormalUser(
|
||||
accountId: number,
|
||||
externalUserId: string,
|
||||
role: string,
|
||||
auto_renew: boolean,
|
||||
license_alert: boolean,
|
||||
notification: boolean,
|
||||
author_id: string,
|
||||
accepted_terms_version: string,
|
||||
): Promise<User> {
|
||||
const user = new User();
|
||||
async createNormalUser(user: newUser): Promise<User> {
|
||||
const {
|
||||
account_id: accountId,
|
||||
external_id: externalUserId,
|
||||
role,
|
||||
auto_renew,
|
||||
license_alert,
|
||||
notification,
|
||||
author_id,
|
||||
accepted_terms_version,
|
||||
encryption,
|
||||
encryption_password: encryptionPassword,
|
||||
prompt,
|
||||
} = user;
|
||||
const userEntity = new User();
|
||||
|
||||
user.role = role;
|
||||
user.account_id = accountId;
|
||||
user.external_id = externalUserId;
|
||||
user.auto_renew = auto_renew;
|
||||
user.license_alert = license_alert;
|
||||
user.notification = notification;
|
||||
user.author_id = author_id;
|
||||
user.accepted_terms_version = accepted_terms_version;
|
||||
userEntity.role = role;
|
||||
userEntity.account_id = accountId;
|
||||
userEntity.external_id = externalUserId;
|
||||
userEntity.auto_renew = auto_renew;
|
||||
userEntity.license_alert = license_alert;
|
||||
userEntity.notification = notification;
|
||||
userEntity.author_id = author_id;
|
||||
userEntity.accepted_terms_version = accepted_terms_version;
|
||||
userEntity.encryption = encryption;
|
||||
userEntity.encryption_password = encryptionPassword;
|
||||
userEntity.prompt = prompt;
|
||||
|
||||
const createdEntity = await this.dataSource.transaction(
|
||||
async (entityManager) => {
|
||||
const repo = entityManager.getRepository(User);
|
||||
const newUser = repo.create(user);
|
||||
const newUser = repo.create(userEntity);
|
||||
const persisted = await repo.save(newUser);
|
||||
|
||||
// ユーザーのタスクソート条件を作成
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user