Merged PR 710: 画面実装(削除操作)
## 概要 [Task3488: 画面実装(削除操作)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3488) - ユーザー削除の画面実装 - 確認ダイアログ - 削除API呼び出し - エラーハンドリング - 成功時のメッセージ - 成功時のユーザー一覧更新 ## レビューポイント - 特にレビューしてほしい箇所 - 軽微なものや自明なものは記載不要 - 修正範囲が大きい場合などに記載 - 全体的にや仕様を満たしているか等は本当に必要な時のみ記載 ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認 ## 補足 - API呼び出しのエラーハンドリング部分はエラーコードが採番されたら追従します
This commit is contained in:
parent
44759b1aac
commit
4be13e002d
@ -6982,6 +6982,46 @@ export const UsersApiAxiosParamCreator = function (configuration?: Configuration
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* ユーザーを削除します
|
||||
* @summary
|
||||
* @param {PostDeleteUserRequest} postDeleteUserRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
deleteUser: async (postDeleteUserRequest: PostDeleteUserRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'postDeleteUserRequest' is not null or undefined
|
||||
assertParamExists('deleteUser', 'postDeleteUserRequest', postDeleteUserRequest)
|
||||
const localVarPath = `/users/delete`;
|
||||
// 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(postDeleteUserRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
* ログインしているユーザーの情報を取得します
|
||||
* @summary
|
||||
@ -7376,6 +7416,19 @@ export const UsersApiFp = function(configuration?: Configuration) {
|
||||
const operationBasePath = operationServerMap['UsersApi.deallocateLicense']?.[index]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* ユーザーを削除します
|
||||
* @summary
|
||||
* @param {PostDeleteUserRequest} postDeleteUserRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async deleteUser(postDeleteUserRequest: PostDeleteUserRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteUser(postDeleteUserRequest, options);
|
||||
const index = configuration?.serverIndex ?? 0;
|
||||
const operationBasePath = operationServerMap['UsersApi.deleteUser']?.[index]?.url;
|
||||
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
|
||||
},
|
||||
/**
|
||||
* ログインしているユーザーの情報を取得します
|
||||
* @summary
|
||||
@ -7539,6 +7592,16 @@ export const UsersApiFactory = function (configuration?: Configuration, basePath
|
||||
deallocateLicense(deallocateLicenseRequest: DeallocateLicenseRequest, options?: any): AxiosPromise<object> {
|
||||
return localVarFp.deallocateLicense(deallocateLicenseRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* ユーザーを削除します
|
||||
* @summary
|
||||
* @param {PostDeleteUserRequest} postDeleteUserRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
deleteUser(postDeleteUserRequest: PostDeleteUserRequest, options?: any): AxiosPromise<object> {
|
||||
return localVarFp.deleteUser(postDeleteUserRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* ログインしているユーザーの情報を取得します
|
||||
* @summary
|
||||
@ -7683,6 +7746,18 @@ export class UsersApi extends BaseAPI {
|
||||
return UsersApiFp(this.configuration).deallocateLicense(deallocateLicenseRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* ユーザーを削除します
|
||||
* @summary
|
||||
* @param {PostDeleteUserRequest} postDeleteUserRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof UsersApi
|
||||
*/
|
||||
public deleteUser(postDeleteUserRequest: PostDeleteUserRequest, options?: AxiosRequestConfig) {
|
||||
return UsersApiFp(this.configuration).deleteUser(postDeleteUserRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* ログインしているユーザーの情報を取得します
|
||||
* @summary
|
||||
|
||||
@ -60,6 +60,14 @@ export const errorCodes = [
|
||||
"E011004", // ワークタイプ使用中エラー
|
||||
"E013001", // ワークフローのAuthorIDとWorktypeIDのペア重複エラー
|
||||
"E013002", // ワークフロー不在エラー
|
||||
"E014001", // ユーザー削除エラー(削除しようとしたユーザーがすでに削除済みだった)
|
||||
"E014002", // ユーザー削除エラー(削除しようとしたユーザーが管理者だった)
|
||||
"E014003", // ユーザー削除エラー(削除しようとしたAuthorのAuthorIDがWorkflowに指定されていた)
|
||||
"E014004", // ユーザー削除エラー(削除しようとしたTypistがWorkflowのTypist候補として指定されていた)
|
||||
"E014005", // ユーザー削除エラー(削除しようとしたTypistがUserGroupに所属していた)
|
||||
"E014006", // ユーザー削除エラー(削除しようとしたユーザが所有者の未完了のタスクが残っている)
|
||||
"E014007", // ユーザー削除エラー(削除しようとしたユーザーが有効なライセンスを持っていた)
|
||||
"E014009", // ユーザー削除エラー(削除しようとしたTypistが未完了のタスクのルーティングに設定されている)
|
||||
"E015001", // タイピストグループ削除済みエラー
|
||||
"E015002", // タイピストグループがワークフローに紐づいているエラー
|
||||
"E015003", // タイピストグループがルーティングされているエラー
|
||||
|
||||
@ -610,7 +610,7 @@ export const deleteTaskAsync = createAsyncThunk<
|
||||
// e ⇒ errorObjectに変換"
|
||||
const error = createErrorObject(e);
|
||||
|
||||
let message = getTranslationID("dictationPage.message.backupFailedError");
|
||||
let message = getTranslationID("common.message.internalServerError");
|
||||
|
||||
if (error.statusCode === 400) {
|
||||
if (error.code === "E010603") {
|
||||
|
||||
@ -383,3 +383,118 @@ export const deallocateLicenseAsync = createAsyncThunk<
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
|
||||
export const deleteUserAsync = createAsyncThunk<
|
||||
// 正常時の戻り値の型
|
||||
{
|
||||
/* Empty Object */
|
||||
},
|
||||
// 引数
|
||||
{
|
||||
userId: number;
|
||||
},
|
||||
{
|
||||
// rejectした時の返却値の型
|
||||
rejectValue: {
|
||||
error: ErrorObject;
|
||||
};
|
||||
}
|
||||
>("users/deleteUserAsync", async (args, thunkApi) => {
|
||||
const { userId } = args;
|
||||
|
||||
// apiのConfigurationを取得する
|
||||
const { getState } = thunkApi;
|
||||
const state = getState() as RootState;
|
||||
const { configuration } = state.auth;
|
||||
const accessToken = getAccessToken(state.auth);
|
||||
const config = new Configuration(configuration);
|
||||
const usersApi = new UsersApi(config);
|
||||
|
||||
try {
|
||||
await usersApi.deleteUser(
|
||||
{
|
||||
userId,
|
||||
},
|
||||
{
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
}
|
||||
);
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
message: getTranslationID("common.message.success"),
|
||||
})
|
||||
);
|
||||
return {};
|
||||
} catch (e) {
|
||||
// e ⇒ errorObjectに変換
|
||||
const error = createErrorObject(e);
|
||||
|
||||
let errorMessage = getTranslationID("common.message.internalServerError");
|
||||
|
||||
if (error.statusCode === 400) {
|
||||
if (error.code === "E014001") {
|
||||
// ユーザーが削除済みのため成功
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
message: getTranslationID("common.message.success"),
|
||||
})
|
||||
);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
// ユーザーに有効なライセンスが割り当たっているため削除不可
|
||||
if (error.code === "E014007") {
|
||||
errorMessage = getTranslationID(
|
||||
"userListPage.message.UserDeletionLicenseActiveError"
|
||||
);
|
||||
}
|
||||
// 管理者ユーザーため削除不可
|
||||
if (error.code === "E014002") {
|
||||
errorMessage = getTranslationID(
|
||||
"userListPage.message.AdminUserDeletionError"
|
||||
);
|
||||
}
|
||||
// タイピストユーザーで担当タスクがあるため削除不可
|
||||
if (error.code === "E014009") {
|
||||
errorMessage = getTranslationID(
|
||||
"userListPage.message.TypistUserDeletionTranscriptionTaskError"
|
||||
);
|
||||
}
|
||||
// タイピストユーザーでルーティングルールに設定されているため削除不可
|
||||
if (error.code === "E014004") {
|
||||
errorMessage = getTranslationID(
|
||||
"userListPage.message.TypistDeletionRoutingRuleError"
|
||||
);
|
||||
}
|
||||
// タイピストユーザーでTranscriptionistGroupに所属しているため削除不可
|
||||
if (error.code === "E014005") {
|
||||
errorMessage = getTranslationID(
|
||||
"userListPage.message.TypistUserDeletionTranscriptionistGroupError"
|
||||
);
|
||||
}
|
||||
// Authorユーザーで同一AuthorIDのタスクがあるため削除不可
|
||||
if (error.code === "E014006") {
|
||||
errorMessage = getTranslationID(
|
||||
"userListPage.message.AuthorUserDeletionTranscriptionTaskError"
|
||||
);
|
||||
}
|
||||
// Authorユーザーで同一AuthorIDがルーティングルールに設定されているため削除不可
|
||||
if (error.code === "E014003") {
|
||||
errorMessage = getTranslationID(
|
||||
"userListPage.message.AuthorDeletionRoutingRuleError"
|
||||
);
|
||||
}
|
||||
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
})
|
||||
);
|
||||
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
updateUserAsync,
|
||||
getAllocatableLicensesAsync,
|
||||
deallocateLicenseAsync,
|
||||
deleteUserAsync,
|
||||
} from "./operations";
|
||||
import { RoleType, UserView } from "./types";
|
||||
|
||||
@ -290,6 +291,15 @@ export const userSlice = createSlice({
|
||||
builder.addCase(deallocateLicenseAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(deleteUserAsync.pending, (state) => {
|
||||
state.apps.isLoading = true;
|
||||
});
|
||||
builder.addCase(deleteUserAsync.fulfilled, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
builder.addCase(deleteUserAsync.rejected, (state) => {
|
||||
state.apps.isLoading = false;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
selectUserViews,
|
||||
selectIsLoading,
|
||||
deallocateLicenseAsync,
|
||||
deleteUserAsync,
|
||||
} from "features/user";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { getTranslationID } from "translation";
|
||||
@ -84,6 +85,24 @@ const UserListPage: React.FC = (): JSX.Element => {
|
||||
[dispatch, t]
|
||||
);
|
||||
|
||||
const onDeleteUser = useCallback(
|
||||
async (userId: number) => {
|
||||
// ダイアログ確認
|
||||
if (
|
||||
/* eslint-disable-next-line no-alert */
|
||||
!window.confirm(t(getTranslationID("common.message.dialogConfirm")))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { meta } = await dispatch(deleteUserAsync({ userId }));
|
||||
if (meta.requestStatus === "fulfilled") {
|
||||
dispatch(listUsersAsync());
|
||||
}
|
||||
},
|
||||
[dispatch, t]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// ユーザ一覧取得処理を呼び出す
|
||||
dispatch(listUsersAsync());
|
||||
@ -244,17 +263,20 @@ const UserListPage: React.FC = (): JSX.Element => {
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
{/* ユーザー削除 CCB後回し分なので今は非表示
|
||||
<li>
|
||||
<a href="">
|
||||
{t(
|
||||
getTranslationID(
|
||||
"userListPage.label.deleteUser"
|
||||
)
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
*/}
|
||||
<li>
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
||||
<a
|
||||
onClick={() => {
|
||||
onDeleteUser(user.id);
|
||||
}}
|
||||
>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"userListPage.label.deleteUser"
|
||||
)
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<td> {user.name}</td>
|
||||
|
||||
@ -2160,7 +2160,7 @@
|
||||
},
|
||||
"/users/delete": {
|
||||
"post": {
|
||||
"operationId": "updeateUser",
|
||||
"operationId": "deleteUser",
|
||||
"summary": "",
|
||||
"description": "ユーザーを削除します",
|
||||
"parameters": [],
|
||||
|
||||
@ -940,7 +940,7 @@ export class UsersController {
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({
|
||||
operationId: 'updeateUser',
|
||||
operationId: 'deleteUser',
|
||||
description: 'ユーザーを削除します',
|
||||
})
|
||||
@ApiBearerAuth()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user