From 3f4d4ec43612a0e19d16520a2c218d46cf467597 Mon Sep 17 00:00:00 2001 From: "saito.k" Date: Fri, 22 Sep 2023 07:36:23 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20427:=20=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E5=AE=9F=E8=A3=85=EF=BC=88=E3=83=86=E3=83=B3=E3=83=97=E3=83=AC?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=83=95=E3=82=A1=E3=82=A4=E3=83=AB=E4=B8=80?= =?UTF-8?q?=E8=A6=A7=E7=94=BB=E9=9D=A2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2651: 画面実装(テンプレートファイル一覧画面)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2651) - テンプレートファイル一覧画面を実装 ## レビューポイント - 取得方法や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/Task2651?csf=1&web=1&e=MAaOJd ## 動作確認状況 - ローカルで確認 ## 補足 - 相談、参考資料などがあれば --- dictation_client/src/AppRouter.tsx | 5 + dictation_client/src/api/api.ts | 406 +++++++++++++++++- dictation_client/src/app/store.ts | 2 + .../src/assets/images/template_add.svg | 10 + .../src/features/account/operations.ts | 2 +- .../src/features/workflow/template/index.ts | 4 + .../features/workflow/template/operations.ts | 42 ++ .../features/workflow/template/selectors.ts | 7 + .../src/features/workflow/template/state.ts | 14 + .../workflow/template/templateSlice.ts | 34 ++ .../src/pages/TemplateFilePage/index.tsx | 109 +++++ .../src/pages/WorkflowPage/index.tsx | 5 + dictation_client/src/translation/de.json | 16 + dictation_client/src/translation/en.json | 16 + dictation_client/src/translation/es.json | 16 + dictation_client/src/translation/fr.json | 16 + 16 files changed, 695 insertions(+), 9 deletions(-) create mode 100644 dictation_client/src/assets/images/template_add.svg create mode 100644 dictation_client/src/features/workflow/template/index.ts create mode 100644 dictation_client/src/features/workflow/template/operations.ts create mode 100644 dictation_client/src/features/workflow/template/selectors.ts create mode 100644 dictation_client/src/features/workflow/template/state.ts create mode 100644 dictation_client/src/features/workflow/template/templateSlice.ts create mode 100644 dictation_client/src/pages/TemplateFilePage/index.tsx diff --git a/dictation_client/src/AppRouter.tsx b/dictation_client/src/AppRouter.tsx index 1cb6298..8cfed31 100644 --- a/dictation_client/src/AppRouter.tsx +++ b/dictation_client/src/AppRouter.tsx @@ -20,6 +20,7 @@ import WorkflowPage from "pages/WorkflowPage"; import TypistGroupSettingPage from "pages/TypistGroupSettingPage"; import WorktypeIdSettingPage from "pages/WorkTypeIdSettingPage"; import AccountPage from "pages/AccountPage"; +import { TemplateFilePage } from "pages/TemplateFilePage"; const AppRouter: React.FC = () => ( @@ -72,6 +73,10 @@ const AppRouter: React.FC = () => ( path="/workflow/worktype-id" element={} />} /> + } />} + /> } />} diff --git a/dictation_client/src/api/api.ts b/dictation_client/src/api/api.ts index 2c44b9f..eb4d4e2 100644 --- a/dictation_client/src/api/api.ts +++ b/dictation_client/src/api/api.ts @@ -561,6 +561,19 @@ export interface DeallocateLicenseRequest { */ 'userId': number; } +/** + * + * @export + * @interface DeleteAccountRequest + */ +export interface DeleteAccountRequest { + /** + * アカウントID + * @type {number} + * @memberof DeleteAccountRequest + */ + 'accountId': number; +} /** * * @export @@ -905,6 +918,19 @@ export interface GetSortCriteriaResponse { */ 'paramName': string; } +/** + * + * @export + * @interface GetTemplatesResponse + */ +export interface GetTemplatesResponse { + /** + * テンプレートファイルの一覧 + * @type {Array} + * @memberof GetTemplatesResponse + */ + 'templates': Array; +} /** * * @export @@ -1631,6 +1657,57 @@ export interface TemplateDownloadLocationResponse { */ 'url': string; } +/** + * + * @export + * @interface TemplateFile + */ +export interface TemplateFile { + /** + * テンプレートファイルのID + * @type {number} + * @memberof TemplateFile + */ + 'id': number; + /** + * テンプレートファイルのファイル名 + * @type {string} + * @memberof TemplateFile + */ + 'name': string; +} +/** + * + * @export + * @interface TemplateUploadFinishedRequest + */ +export interface TemplateUploadFinishedRequest { + /** + * テンプレートファイルのファイル名 + * @type {string} + * @memberof TemplateUploadFinishedRequest + */ + 'name': string; + /** + * テンプレートファイルのアップロード先URL + * @type {string} + * @memberof TemplateUploadFinishedRequest + */ + 'url': string; +} +/** + * + * @export + * @interface TemplateUploadLocationResponse + */ +export interface TemplateUploadLocationResponse { + /** + * + * @type {string} + * @memberof TemplateUploadLocationResponse + */ + 'url': string; +} /** * * @export @@ -2154,6 +2231,46 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat options: localVarRequestOptions, }; }, + /** + * + * @summary + * @param {DeleteAccountRequest} deleteAccountRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteAccount: async (deleteAccountRequest: DeleteAccountRequest, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'deleteAccountRequest' is not null or undefined + assertParamExists('deleteAccount', 'deleteAccountRequest', deleteAccountRequest) + const localVarPath = `/accounts/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(deleteAccountRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * * @summary @@ -2611,9 +2728,9 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat * @param {*} [options] Override http request option. * @throws {RequiredError} */ - me: async (updateAccountInfoRequest: UpdateAccountInfoRequest, options: AxiosRequestConfig = {}): Promise => { + updateAccountInfo: async (updateAccountInfoRequest: UpdateAccountInfoRequest, options: AxiosRequestConfig = {}): Promise => { // verify required parameter 'updateAccountInfoRequest' is not null or undefined - assertParamExists('me', 'updateAccountInfoRequest', updateAccountInfoRequest) + assertParamExists('updateAccountInfo', 'updateAccountInfoRequest', updateAccountInfoRequest) const localVarPath = `/accounts/me`; // use dummy base URL string because the URL constructor only accepts absolute URLs. const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL); @@ -2852,6 +2969,17 @@ export const AccountsApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.createWorktype(createWorktypesRequest, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @summary + * @param {DeleteAccountRequest} deleteAccountRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async deleteAccount(deleteAccountRequest: DeleteAccountRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.deleteAccount(deleteAccountRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * * @summary @@ -2987,8 +3115,8 @@ export const AccountsApiFp = function(configuration?: Configuration) { * @param {*} [options] Override http request option. * @throws {RequiredError} */ - async me(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { - const localVarAxiosArgs = await localVarAxiosParamCreator.me(updateAccountInfoRequest, options); + async updateAccountInfo(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.updateAccountInfo(updateAccountInfoRequest, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, /** @@ -3097,6 +3225,16 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP createWorktype(createWorktypesRequest: CreateWorktypesRequest, options?: any): AxiosPromise { return localVarFp.createWorktype(createWorktypesRequest, options).then((request) => request(axios, basePath)); }, + /** + * + * @summary + * @param {DeleteAccountRequest} deleteAccountRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + deleteAccount(deleteAccountRequest: DeleteAccountRequest, options?: any): AxiosPromise { + return localVarFp.deleteAccount(deleteAccountRequest, options).then((request) => request(axios, basePath)); + }, /** * * @summary @@ -3220,8 +3358,8 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP * @param {*} [options] Override http request option. * @throws {RequiredError} */ - me(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: any): AxiosPromise { - return localVarFp.me(updateAccountInfoRequest, options).then((request) => request(axios, basePath)); + updateAccountInfo(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: any): AxiosPromise { + return localVarFp.updateAccountInfo(updateAccountInfoRequest, options).then((request) => request(axios, basePath)); }, /** * @@ -3338,6 +3476,18 @@ export class AccountsApi extends BaseAPI { return AccountsApiFp(this.configuration).createWorktype(createWorktypesRequest, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary + * @param {DeleteAccountRequest} deleteAccountRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AccountsApi + */ + public deleteAccount(deleteAccountRequest: DeleteAccountRequest, options?: AxiosRequestConfig) { + return AccountsApiFp(this.configuration).deleteAccount(deleteAccountRequest, options).then((request) => request(this.axios, this.basePath)); + } + /** * * @summary @@ -3486,8 +3636,8 @@ export class AccountsApi extends BaseAPI { * @throws {RequiredError} * @memberof AccountsApi */ - public me(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: AxiosRequestConfig) { - return AccountsApiFp(this.configuration).me(updateAccountInfoRequest, options).then((request) => request(this.axios, this.basePath)); + public updateAccountInfo(updateAccountInfoRequest: UpdateAccountInfoRequest, options?: AxiosRequestConfig) { + return AccountsApiFp(this.configuration).updateAccountInfo(updateAccountInfoRequest, options).then((request) => request(this.axios, this.basePath)); } /** @@ -3956,6 +4106,80 @@ export const FilesApiAxiosParamCreator = function (configuration?: Configuration + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * アップロードが完了したテンプレートファイルの情報を登録します + * @summary + * @param {TemplateUploadFinishedRequest} templateUploadFinishedRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadTemplateFinished: async (templateUploadFinishedRequest: TemplateUploadFinishedRequest, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'templateUploadFinishedRequest' is not null or undefined + assertParamExists('uploadTemplateFinished', 'templateUploadFinishedRequest', templateUploadFinishedRequest) + const localVarPath = `/files/template/upload-finished`; + // 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(templateUploadFinishedRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + /** + * ログイン中ユーザー用のBlob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadTemplateLocation: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/files/template/upload-location`; + // 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: 'GET', ...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}; @@ -4018,6 +4242,27 @@ export const FilesApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.uploadLocation(options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * アップロードが完了したテンプレートファイルの情報を登録します + * @summary + * @param {TemplateUploadFinishedRequest} templateUploadFinishedRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async uploadTemplateFinished(templateUploadFinishedRequest: TemplateUploadFinishedRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.uploadTemplateFinished(templateUploadFinishedRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + /** + * ログイン中ユーザー用のBlob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async uploadTemplateLocation(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.uploadTemplateLocation(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, } }; @@ -4067,6 +4312,25 @@ export const FilesApiFactory = function (configuration?: Configuration, basePath uploadLocation(options?: any): AxiosPromise { return localVarFp.uploadLocation(options).then((request) => request(axios, basePath)); }, + /** + * アップロードが完了したテンプレートファイルの情報を登録します + * @summary + * @param {TemplateUploadFinishedRequest} templateUploadFinishedRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadTemplateFinished(templateUploadFinishedRequest: TemplateUploadFinishedRequest, options?: any): AxiosPromise { + return localVarFp.uploadTemplateFinished(templateUploadFinishedRequest, options).then((request) => request(axios, basePath)); + }, + /** + * ログイン中ユーザー用のBlob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + uploadTemplateLocation(options?: any): AxiosPromise { + return localVarFp.uploadTemplateLocation(options).then((request) => request(axios, basePath)); + }, }; }; @@ -4123,6 +4387,29 @@ export class FilesApi extends BaseAPI { public uploadLocation(options?: AxiosRequestConfig) { return FilesApiFp(this.configuration).uploadLocation(options).then((request) => request(this.axios, this.basePath)); } + + /** + * アップロードが完了したテンプレートファイルの情報を登録します + * @summary + * @param {TemplateUploadFinishedRequest} templateUploadFinishedRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof FilesApi + */ + public uploadTemplateFinished(templateUploadFinishedRequest: TemplateUploadFinishedRequest, options?: AxiosRequestConfig) { + return FilesApiFp(this.configuration).uploadTemplateFinished(templateUploadFinishedRequest, options).then((request) => request(this.axios, this.basePath)); + } + + /** + * ログイン中ユーザー用のBlob Storage上のテンプレートファイルのアップロード先アクセスURLを取得します + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof FilesApi + */ + public uploadTemplateLocation(options?: AxiosRequestConfig) { + return FilesApiFp(this.configuration).uploadTemplateLocation(options).then((request) => request(this.axios, this.basePath)); + } } @@ -5357,6 +5644,109 @@ export class TasksApi extends BaseAPI { +/** + * TemplatesApi - axios parameter creator + * @export + */ +export const TemplatesApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * アカウント内のテンプレートファイルの一覧を取得します + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTemplates: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/templates`; + // 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: 'GET', ...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, + }; + }, + } +}; + +/** + * TemplatesApi - functional programming interface + * @export + */ +export const TemplatesApiFp = function(configuration?: Configuration) { + const localVarAxiosParamCreator = TemplatesApiAxiosParamCreator(configuration) + return { + /** + * アカウント内のテンプレートファイルの一覧を取得します + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getTemplates(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getTemplates(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + } +}; + +/** + * TemplatesApi - factory interface + * @export + */ +export const TemplatesApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = TemplatesApiFp(configuration) + return { + /** + * アカウント内のテンプレートファイルの一覧を取得します + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getTemplates(options?: any): AxiosPromise { + return localVarFp.getTemplates(options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * TemplatesApi - object-oriented interface + * @export + * @class TemplatesApi + * @extends {BaseAPI} + */ +export class TemplatesApi extends BaseAPI { + /** + * アカウント内のテンプレートファイルの一覧を取得します + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof TemplatesApi + */ + public getTemplates(options?: AxiosRequestConfig) { + return TemplatesApiFp(this.configuration).getTemplates(options).then((request) => request(this.axios, this.basePath)); + } +} + + + /** * UsersApi - axios parameter creator * @export diff --git a/dictation_client/src/app/store.ts b/dictation_client/src/app/store.ts index c313325..d42c776 100644 --- a/dictation_client/src/app/store.ts +++ b/dictation_client/src/app/store.ts @@ -16,6 +16,7 @@ import licenseOrderHistory from "features/license/licenseOrderHistory/licenseOrd import typistGroup from "features/workflow/typistGroup/typistGroupSlice"; import worktype from "features/workflow/worktype/worktypeSlice"; import account from "features/account/accountSlice"; +import template from "features/workflow/template/templateSlice"; export const store = configureStore({ reducer: { @@ -36,6 +37,7 @@ export const store = configureStore({ typistGroup, worktype, account, + template, }, }); diff --git a/dictation_client/src/assets/images/template_add.svg b/dictation_client/src/assets/images/template_add.svg new file mode 100644 index 0000000..562230b --- /dev/null +++ b/dictation_client/src/assets/images/template_add.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/dictation_client/src/features/account/operations.ts b/dictation_client/src/features/account/operations.ts index 93b092b..59b70e0 100644 --- a/dictation_client/src/features/account/operations.ts +++ b/dictation_client/src/features/account/operations.ts @@ -72,7 +72,7 @@ export const updateAccountInfoAsync = createAsyncThunk< const accountApi = new AccountsApi(config); try { - await accountApi.me(args, { + await accountApi.updateAccountInfo(args, { headers: { authorization: `Bearer ${accessToken}` }, }); thunkApi.dispatch( diff --git a/dictation_client/src/features/workflow/template/index.ts b/dictation_client/src/features/workflow/template/index.ts new file mode 100644 index 0000000..64902e8 --- /dev/null +++ b/dictation_client/src/features/workflow/template/index.ts @@ -0,0 +1,4 @@ +export * from "./templateSlice"; +export * from "./state"; +export * from "./operations"; +export * from "./selectors"; diff --git a/dictation_client/src/features/workflow/template/operations.ts b/dictation_client/src/features/workflow/template/operations.ts new file mode 100644 index 0000000..ddc556e --- /dev/null +++ b/dictation_client/src/features/workflow/template/operations.ts @@ -0,0 +1,42 @@ +import { createAsyncThunk } from "@reduxjs/toolkit"; +import { Configuration, GetTemplatesResponse, TemplatesApi } from "api"; +import type { RootState } from "app/store"; +import { ErrorObject, createErrorObject } from "common/errors"; +import { openSnackbar } from "features/ui/uiSlice"; +import { getTranslationID } from "translation"; + +export const listTemplateAsync = createAsyncThunk< + GetTemplatesResponse, + void, + { + // rejectした時の返却値の型 + rejectValue: { + error: ErrorObject; + }; + } +>("workflow/listTemplateAsync", async (args, thunkApi) => { + // apiのConfigurationを取得する + const { getState } = thunkApi; + const state = getState() as RootState; + const { configuration, accessToken } = state.auth; + const config = new Configuration(configuration); + const templateApi = new TemplatesApi(config); + + try { + const { data } = await templateApi.getTemplates({ + headers: { authorization: `Bearer ${accessToken}` }, + }); + + return data; + } catch (e) { + // e ⇒ errorObjectに変換" + const error = createErrorObject(e); + thunkApi.dispatch( + openSnackbar({ + level: "error", + message: getTranslationID("common.message.internalServerError"), + }) + ); + return thunkApi.rejectWithValue({ error }); + } +}); diff --git a/dictation_client/src/features/workflow/template/selectors.ts b/dictation_client/src/features/workflow/template/selectors.ts new file mode 100644 index 0000000..45c3ccd --- /dev/null +++ b/dictation_client/src/features/workflow/template/selectors.ts @@ -0,0 +1,7 @@ +import { RootState } from "app/store"; + +export const selectTemplates = (state: RootState) => + state.template.domain.templates; + +export const selectIsLoading = (state: RootState) => + state.template.apps.isLoading; diff --git a/dictation_client/src/features/workflow/template/state.ts b/dictation_client/src/features/workflow/template/state.ts new file mode 100644 index 0000000..8805a9a --- /dev/null +++ b/dictation_client/src/features/workflow/template/state.ts @@ -0,0 +1,14 @@ +import { TemplateFile } from "api"; + +export interface TemplateState { + apps: Apps; + domain: Domain; +} + +export interface Apps { + isLoading: boolean; +} + +export interface Domain { + templates?: TemplateFile[]; +} diff --git a/dictation_client/src/features/workflow/template/templateSlice.ts b/dictation_client/src/features/workflow/template/templateSlice.ts new file mode 100644 index 0000000..9bbcab2 --- /dev/null +++ b/dictation_client/src/features/workflow/template/templateSlice.ts @@ -0,0 +1,34 @@ +import { createSlice } from "@reduxjs/toolkit"; +import { TemplateState } from "./state"; +import { listTemplateAsync } from "./operations"; + +const initialState: TemplateState = { + apps: { + isLoading: false, + }, + domain: { + templates: undefined, + }, +}; + +export const templateSlice = createSlice({ + name: "template", + initialState, + reducers: {}, + extraReducers: (builder) => { + builder.addCase(listTemplateAsync.pending, (state) => { + state.apps.isLoading = true; + }); + builder.addCase(listTemplateAsync.fulfilled, (state, action) => { + const { templates } = action.payload; + + state.domain.templates = templates; + state.apps.isLoading = false; + }); + builder.addCase(listTemplateAsync.rejected, (state) => { + state.apps.isLoading = false; + }); + }, +}); + +export default templateSlice.reducer; diff --git a/dictation_client/src/pages/TemplateFilePage/index.tsx b/dictation_client/src/pages/TemplateFilePage/index.tsx new file mode 100644 index 0000000..f0783ab --- /dev/null +++ b/dictation_client/src/pages/TemplateFilePage/index.tsx @@ -0,0 +1,109 @@ +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { AppDispatch } from "app/store"; +import Header from "components/header"; +import { UpdateTokenTimer } from "components/auth/updateTokenTimer"; +import { useTranslation } from "react-i18next"; +import { getTranslationID } from "translation"; +import undo from "assets/images/undo.svg"; +import styles from "styles/app.module.scss"; +import { + listTemplateAsync, + selectIsLoading, + selectTemplates, +} from "features/workflow/template"; +import addTemplate from "assets/images/template_add.svg"; +import progress_activit from "assets/images/progress_activit.svg"; + +export const TemplateFilePage: React.FC = () => { + const dispatch: AppDispatch = useDispatch(); + const [t] = useTranslation(); + const templates = useSelector(selectTemplates); + const isLoading = useSelector(selectIsLoading); + + useEffect(() => { + dispatch(listTemplateAsync()); + }, [dispatch]); + + return ( +
+
+ +
+
+
+

+ {t(getTranslationID("workflowPage.label.title"))} +

+

+ {t(getTranslationID("templateFilePage.label.title"))} +

+
+
+
+
+ + + + + + + {templates?.map((template) => ( + + + + + ))} + {!isLoading && templates?.length === 0 && ( +

+ {t(getTranslationID("common.message.listEmpty"))} +

+ )} + {isLoading && ( + Loading + )} +
+ {t(getTranslationID("templateFilePage.label.fileName"))} + {/** empty th */}
{template.name} + +
+
+
+
+
+ ); +}; diff --git a/dictation_client/src/pages/WorkflowPage/index.tsx b/dictation_client/src/pages/WorkflowPage/index.tsx index 6d57be9..0df685b 100644 --- a/dictation_client/src/pages/WorkflowPage/index.tsx +++ b/dictation_client/src/pages/WorkflowPage/index.tsx @@ -20,6 +20,11 @@ const WorkflowPage: React.FC = (): JSX.Element => ( Worktype ID Setting + + + Template File + +