Merged PR 743: テンプレートファイル削除画面修正

## 概要
[Task3600: テンプレートファイル削除画面修正](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3600)

- テンプレートファイル削除の画面を実装しました。

## レビューポイント
- エラー処理は適切でしょうか?

## UIの変更
- [Task3600](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/Task3600?csf=1&web=1&e=E25Kf7)

## 動作確認状況
- ローカルで確認
This commit is contained in:
makabe.t 2024-02-13 02:32:17 +00:00
parent 83efd97bdf
commit bbbd3e757b
9 changed files with 204 additions and 5 deletions

View File

@ -6730,6 +6730,44 @@ export class TasksApi extends BaseAPI {
*/
export const TemplatesApiAxiosParamCreator = function (configuration?: Configuration) {
return {
/**
* IDで指定されたテンプレートファイルを削除します
* @summary
* @param {number} templateFileId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deleteTemplateFile: async (templateFileId: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'templateFileId' is not null or undefined
assertParamExists('deleteTemplateFile', 'templateFileId', templateFileId)
const localVarPath = `/templates/{templateFileId}/delete`
.replace(`{${"templateFileId"}}`, encodeURIComponent(String(templateFileId)));
// 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)
setSearchParams(localVarUrlObj, localVarQueryParameter);
let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary
@ -6774,6 +6812,19 @@ export const TemplatesApiAxiosParamCreator = function (configuration?: Configura
export const TemplatesApiFp = function(configuration?: Configuration) {
const localVarAxiosParamCreator = TemplatesApiAxiosParamCreator(configuration)
return {
/**
* IDで指定されたテンプレートファイルを削除します
* @summary
* @param {number} templateFileId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async deleteTemplateFile(templateFileId: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.deleteTemplateFile(templateFileId, options);
const index = configuration?.serverIndex ?? 0;
const operationBasePath = operationServerMap['TemplatesApi.deleteTemplateFile']?.[index]?.url;
return (axios, basePath) => createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration)(axios, operationBasePath || basePath);
},
/**
*
* @summary
@ -6796,6 +6847,16 @@ export const TemplatesApiFp = function(configuration?: Configuration) {
export const TemplatesApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
const localVarFp = TemplatesApiFp(configuration)
return {
/**
* IDで指定されたテンプレートファイルを削除します
* @summary
* @param {number} templateFileId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
deleteTemplateFile(templateFileId: number, options?: any): AxiosPromise<object> {
return localVarFp.deleteTemplateFile(templateFileId, options).then((request) => request(axios, basePath));
},
/**
*
* @summary
@ -6815,6 +6876,18 @@ export const TemplatesApiFactory = function (configuration?: Configuration, base
* @extends {BaseAPI}
*/
export class TemplatesApi extends BaseAPI {
/**
* IDで指定されたテンプレートファイルを削除します
* @summary
* @param {number} templateFileId
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof TemplatesApi
*/
public deleteTemplateFile(templateFileId: number, options?: AxiosRequestConfig) {
return TemplatesApiFp(this.configuration).deleteTemplateFile(templateFileId, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary

View File

@ -73,4 +73,7 @@ export const errorCodes = [
"E015001", // タイピストグループ削除済みエラー
"E015002", // タイピストグループがワークフローに紐づいているエラー
"E015003", // タイピストグループがルーティングされているエラー
"E016001", // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルがすでに削除済みだった)
"E016002", // テンプレートファイル削除エラー削除しようとしたテンプレートファイルがWorkflowに指定されていた
"E016003", // テンプレートファイル削除エラー(削除しようとしたテンプレートファイルが未完了のタスクに紐づいていた)
] as const;

View File

@ -115,3 +115,78 @@ export const uploadTemplateAsync = createAsyncThunk<
return thunkApi.rejectWithValue({ error });
}
});
export const deleteTemplateAsync = createAsyncThunk<
{
/* Empty Object */
},
{ templateFileId: number },
{
// rejectした時の返却値の型
rejectValue: {
error: ErrorObject;
};
}
>("workflow/deleteTemplateAsync", async (args, thunkApi) => {
const { templateFileId } = 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 templateApi = new TemplatesApi(config);
try {
// ファイルを削除する
await templateApi.deleteTemplateFile(templateFileId, {
headers: { authorization: `Bearer ${accessToken}` },
});
thunkApi.dispatch(
openSnackbar({
level: "info",
message: getTranslationID("common.message.success"),
})
);
return {};
} catch (e) {
// e ⇒ errorObjectに変換"
const error = createErrorObject(e);
if (error.code === "E016001") {
// テンプレートファイルが削除済みの場合は成功扱いとする
thunkApi.dispatch(
openSnackbar({
level: "info",
message: getTranslationID("common.message.success"),
})
);
return {};
}
let message = getTranslationID("common.message.internalServerError");
// テンプレートファイルがルーティングルールに紐づく場合はエラー
if (error.code === "E016002") {
message = getTranslationID(
"templateFilePage.message.deleteFailedWorkflowAssigned"
);
}
// テンプレートファイルが未完了のタスクに紐づく場合はエラー
if (error.code === "E016003") {
message = getTranslationID(
"templateFilePage.message.deleteFailedTaskAssigned"
);
}
thunkApi.dispatch(
openSnackbar({
level: "error",
message,
})
);
return thunkApi.rejectWithValue({ error });
}
});

View File

@ -1,6 +1,10 @@
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
import { TemplateState } from "./state";
import { listTemplateAsync, uploadTemplateAsync } from "./operations";
import {
deleteTemplateAsync,
listTemplateAsync,
uploadTemplateAsync,
} from "./operations";
const initialState: TemplateState = {
apps: {
@ -45,6 +49,15 @@ export const templateSlice = createSlice({
builder.addCase(uploadTemplateAsync.rejected, (state) => {
state.apps.isUploading = false;
});
builder.addCase(deleteTemplateAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(deleteTemplateAsync.fulfilled, (state) => {
state.apps.isLoading = false;
});
builder.addCase(deleteTemplateAsync.rejected, (state) => {
state.apps.isLoading = false;
});
},
});

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from "react";
import React, { useCallback, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AppDispatch } from "app/store";
import Header from "components/header";
@ -13,6 +13,7 @@ import {
selectTemplates,
listTemplateAsync,
selectIsLoading,
deleteTemplateAsync,
} from "features/workflow/template";
import { selectDelegationAccessToken } from "features/auth/selectors";
import { DelegationBar } from "components/delegate";
@ -35,6 +36,23 @@ export const TemplateFilePage: React.FC = () => {
dispatch(listTemplateAsync());
}, [dispatch]);
const onDeleteTemplate = useCallback(
async (templateFileId: number) => {
if (
/* eslint-disable-next-line no-alert */
!window.confirm(t(getTranslationID("common.message.dialogConfirm")))
) {
return;
}
const { meta } = await dispatch(deleteTemplateAsync({ templateFileId }));
if (meta.requestStatus === "fulfilled") {
dispatch(listTemplateAsync());
}
},
[dispatch, t]
);
return (
<>
{isShowAddPopup && (
@ -101,16 +119,17 @@ export const TemplateFilePage: React.FC = () => {
<td>{template.name}</td>
<td>
<ul className={`${styles.menuAction} ${styles.inTable}`}>
{/* CCB
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions */}
<a
href=""
className={`${styles.menuLink} ${styles.isActive}`}
onClick={() => {
onDeleteTemplate(template.id);
}}
>
{t(getTranslationID("common.label.delete"))}
</a>
</li>
*/}
</ul>
</td>
</tr>

View File

@ -475,6 +475,10 @@
"fileSizeTerms": "Die maximale Dateigröße, die gespeichert werden kann, beträgt 5 MB.",
"fileSizeError": "Die ausgewählte Dateigröße ist zu groß. Bitte wählen Sie eine Datei mit einer Größe von 5 MB oder weniger aus.",
"fileEmptyError": "Dateiauswahl ist erforderlich. Bitte wählen Sie eine Datei aus."
},
"message": {
"deleteFailedWorkflowAssigned": "(de)テンプレートファイルの削除に失敗しました。Workflow画面でルーティングルールから対象テンプレートファイルを外してください。",
"deleteFailedTaskAssigned": "(de)テンプレートファイルの削除に失敗しました。Dictation画面で対象テンプレートファイルが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。"
}
},
"partnerPage": {

View File

@ -475,6 +475,10 @@
"fileSizeTerms": "The maximum file size that can be saved is 5MB.",
"fileSizeError": "The selected file size is too large. Please select a file that is 5MB or less in size.",
"fileEmptyError": "File selection is required. Please select a file."
},
"message": {
"deleteFailedWorkflowAssigned": "テンプレートファイルの削除に失敗しました。Workflow画面でルーティングルールから対象テンプレートファイルを外してください。",
"deleteFailedTaskAssigned": "テンプレートファイルの削除に失敗しました。Dictation画面で対象テンプレートファイルが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。"
}
},
"partnerPage": {

View File

@ -475,6 +475,10 @@
"fileSizeTerms": "El tamaño máximo de archivo que se puede guardar es de 5 MB.",
"fileSizeError": "El tamaño del archivo seleccionado es demasiado grande. Seleccione un archivo que tenga un tamaño de 5 MB o menos.",
"fileEmptyError": "Se requiere selección de archivos. Por favor seleccione un archivo."
},
"message": {
"deleteFailedWorkflowAssigned": "(es)テンプレートファイルの削除に失敗しました。Workflow画面でルーティングルールから対象テンプレートファイルを外してください。",
"deleteFailedTaskAssigned": "(es)テンプレートファイルの削除に失敗しました。Dictation画面で対象テンプレートファイルが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。"
}
},
"partnerPage": {

View File

@ -475,6 +475,10 @@
"fileSizeTerms": "La taille maximale du fichier pouvant être enregistré est de 5 Mo.",
"fileSizeError": "La taille du fichier sélectionné est trop grande. Veuillez sélectionner un fichier d'une taille maximale de 5 Mo.",
"fileEmptyError": "La sélection de fichiers est requise. Veuillez sélectionner un fichier."
},
"message": {
"deleteFailedWorkflowAssigned": "(fr)テンプレートファイルの削除に失敗しました。Workflow画面でルーティングルールから対象テンプレートファイルを外してください。",
"deleteFailedTaskAssigned": "(fr)テンプレートファイルの削除に失敗しました。Dictation画面で対象テンプレートファイルが設定されているタスクの中で、文字起こしが未完了のタスクを削除またはFinishedにしてください。"
}
},
"partnerPage": {