Merged PR 187: 画面実装(カードライセンス発行PU)
## 概要 [Task1991: 画面実装(カードライセンス発行PU)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1991) カードライセンス発行ポップアップを新規作成。 上記ポップアップを起動するボタンを第1~4階層用のライセンスページに仮配置。 ## レビューポイント 特筆するものはなし。 ライセンスページのポップアップ起動ボタンは仮配置なので、レビュー対象外でお願いします。 ## UIの変更 https://ndstokyo.sharepoint.com/:i:/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/Task1991/%E3%82%AB%E3%83%BC%E3%83%89%E3%83%A9%E3%82%A4%E3%82%BB%E3%83%B3%E3%82%B9%E7%99%BA%E8%A1%8CPU.PNG?csf=1&web=1&e=NtuRgu ## 動作確認状況 ローカルで動作確認済み ## 補足 ・csvファイルダウンロード確認のポップアップは、現在は表示しないつくりになっています。PBIの受け入れ条件には表示するように記載があるのですが、月曜日のDSで奥澤さんに確認予定です。 ・ライセンス数入力のテキストボックスで「.」「-」「+」が入力できてしまう事象がありました。こちらですがライセンス注文ポップアップでも同様の仕様となっているため、本PBI内の別タスクで纏めての修正とさせてください。
This commit is contained in:
parent
9270dc2c0d
commit
709db0d2ec
3537
dictation_client/package-lock.json
generated
3537
dictation_client/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -554,6 +554,32 @@ export interface GetUsersResponse {
|
||||
*/
|
||||
'users': Array<User>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface IssueCardLicensesRequest
|
||||
*/
|
||||
export interface IssueCardLicensesRequest {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof IssueCardLicensesRequest
|
||||
*/
|
||||
'createCount': number;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface IssueCardLicensesResponse
|
||||
*/
|
||||
export interface IssueCardLicensesResponse {
|
||||
/**
|
||||
*
|
||||
* @type {Array<string>}
|
||||
* @memberof IssueCardLicensesResponse
|
||||
*/
|
||||
'cardLicenseKeys': Array<string>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@ -1770,14 +1796,11 @@ export const FilesApiAxiosParamCreator = function (configuration?: Configuration
|
||||
/**
|
||||
* アップロードが完了した音声ファイルの情報を登録し、文字起こしタスクを生成します
|
||||
* @summary
|
||||
* @param {string} authorization
|
||||
* @param {AudioUploadFinishedRequest} audioUploadFinishedRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
uploadFinished: async (authorization: string, audioUploadFinishedRequest: AudioUploadFinishedRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'authorization' is not null or undefined
|
||||
assertParamExists('uploadFinished', 'authorization', authorization)
|
||||
uploadFinished: async (audioUploadFinishedRequest: AudioUploadFinishedRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'audioUploadFinishedRequest' is not null or undefined
|
||||
assertParamExists('uploadFinished', 'audioUploadFinishedRequest', audioUploadFinishedRequest)
|
||||
const localVarPath = `/files/audio/upload-finished`;
|
||||
@ -1796,10 +1819,6 @@ export const FilesApiAxiosParamCreator = function (configuration?: Configuration
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (authorization != null) {
|
||||
localVarHeaderParameter['authorization'] = String(authorization);
|
||||
}
|
||||
|
||||
|
||||
|
||||
localVarHeaderParameter['Content-Type'] = 'application/json';
|
||||
@ -1817,13 +1836,10 @@ export const FilesApiAxiosParamCreator = function (configuration?: Configuration
|
||||
/**
|
||||
* ログイン中ユーザー用のBlob Storage上の音声ファイルのアップロード先アクセスURLを取得します
|
||||
* @summary
|
||||
* @param {string} authorization
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
uploadLocation: async (authorization: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'authorization' is not null or undefined
|
||||
assertParamExists('uploadLocation', 'authorization', authorization)
|
||||
uploadLocation: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/files/audio/upload-location`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
@ -1840,10 +1856,6 @@ export const FilesApiAxiosParamCreator = function (configuration?: Configuration
|
||||
// http bearer authentication required
|
||||
await setBearerAuthToObject(localVarHeaderParameter, configuration)
|
||||
|
||||
if (authorization != null) {
|
||||
localVarHeaderParameter['authorization'] = String(authorization);
|
||||
}
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter);
|
||||
@ -1890,24 +1902,22 @@ export const FilesApiFp = function(configuration?: Configuration) {
|
||||
/**
|
||||
* アップロードが完了した音声ファイルの情報を登録し、文字起こしタスクを生成します
|
||||
* @summary
|
||||
* @param {string} authorization
|
||||
* @param {AudioUploadFinishedRequest} audioUploadFinishedRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async uploadFinished(authorization: string, audioUploadFinishedRequest: AudioUploadFinishedRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AudioUploadFinishedResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFinished(authorization, audioUploadFinishedRequest, options);
|
||||
async uploadFinished(audioUploadFinishedRequest: AudioUploadFinishedRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AudioUploadFinishedResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadFinished(audioUploadFinishedRequest, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
* ログイン中ユーザー用のBlob Storage上の音声ファイルのアップロード先アクセスURLを取得します
|
||||
* @summary
|
||||
* @param {string} authorization
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async uploadLocation(authorization: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AudioUploadLocationResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadLocation(authorization, options);
|
||||
async uploadLocation(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<AudioUploadLocationResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.uploadLocation(options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
}
|
||||
@ -1943,23 +1953,21 @@ export const FilesApiFactory = function (configuration?: Configuration, basePath
|
||||
/**
|
||||
* アップロードが完了した音声ファイルの情報を登録し、文字起こしタスクを生成します
|
||||
* @summary
|
||||
* @param {string} authorization
|
||||
* @param {AudioUploadFinishedRequest} audioUploadFinishedRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
uploadFinished(authorization: string, audioUploadFinishedRequest: AudioUploadFinishedRequest, options?: any): AxiosPromise<AudioUploadFinishedResponse> {
|
||||
return localVarFp.uploadFinished(authorization, audioUploadFinishedRequest, options).then((request) => request(axios, basePath));
|
||||
uploadFinished(audioUploadFinishedRequest: AudioUploadFinishedRequest, options?: any): AxiosPromise<AudioUploadFinishedResponse> {
|
||||
return localVarFp.uploadFinished(audioUploadFinishedRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
* ログイン中ユーザー用のBlob Storage上の音声ファイルのアップロード先アクセスURLを取得します
|
||||
* @summary
|
||||
* @param {string} authorization
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
uploadLocation(authorization: string, options?: any): AxiosPromise<AudioUploadLocationResponse> {
|
||||
return localVarFp.uploadLocation(authorization, options).then((request) => request(axios, basePath));
|
||||
uploadLocation(options?: any): AxiosPromise<AudioUploadLocationResponse> {
|
||||
return localVarFp.uploadLocation(options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
@ -1998,26 +2006,24 @@ export class FilesApi extends BaseAPI {
|
||||
/**
|
||||
* アップロードが完了した音声ファイルの情報を登録し、文字起こしタスクを生成します
|
||||
* @summary
|
||||
* @param {string} authorization
|
||||
* @param {AudioUploadFinishedRequest} audioUploadFinishedRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof FilesApi
|
||||
*/
|
||||
public uploadFinished(authorization: string, audioUploadFinishedRequest: AudioUploadFinishedRequest, options?: AxiosRequestConfig) {
|
||||
return FilesApiFp(this.configuration).uploadFinished(authorization, audioUploadFinishedRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
public uploadFinished(audioUploadFinishedRequest: AudioUploadFinishedRequest, options?: AxiosRequestConfig) {
|
||||
return FilesApiFp(this.configuration).uploadFinished(audioUploadFinishedRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* ログイン中ユーザー用のBlob Storage上の音声ファイルのアップロード先アクセスURLを取得します
|
||||
* @summary
|
||||
* @param {string} authorization
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof FilesApi
|
||||
*/
|
||||
public uploadLocation(authorization: string, options?: AxiosRequestConfig) {
|
||||
return FilesApiFp(this.configuration).uploadLocation(authorization, options).then((request) => request(this.axios, this.basePath));
|
||||
public uploadLocation(options?: AxiosRequestConfig) {
|
||||
return FilesApiFp(this.configuration).uploadLocation(options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2063,6 +2069,46 @@ export const LicensesApiAxiosParamCreator = function (configuration?: Configurat
|
||||
localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
|
||||
localVarRequestOptions.data = serializeDataIfNeeded(createOrdersRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
};
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {IssueCardLicensesRequest} issueCardLicensesRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
issueCardLicenses: async (issueCardLicensesRequest: IssueCardLicensesRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
|
||||
// verify required parameter 'issueCardLicensesRequest' is not null or undefined
|
||||
assertParamExists('issueCardLicenses', 'issueCardLicensesRequest', issueCardLicensesRequest)
|
||||
const localVarPath = `/licenses/cards`;
|
||||
// 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(issueCardLicensesRequest, localVarRequestOptions, configuration)
|
||||
|
||||
return {
|
||||
url: toPathString(localVarUrlObj),
|
||||
options: localVarRequestOptions,
|
||||
@ -2089,6 +2135,17 @@ export const LicensesApiFp = function(configuration?: Configuration) {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.createOrders(createOrdersRequest, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {IssueCardLicensesRequest} issueCardLicensesRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async issueCardLicenses(issueCardLicensesRequest: IssueCardLicensesRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<IssueCardLicensesResponse>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.issueCardLicenses(issueCardLicensesRequest, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
@ -2109,6 +2166,16 @@ export const LicensesApiFactory = function (configuration?: Configuration, baseP
|
||||
createOrders(createOrdersRequest: CreateOrdersRequest, options?: any): AxiosPromise<object> {
|
||||
return localVarFp.createOrders(createOrdersRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {IssueCardLicensesRequest} issueCardLicensesRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
issueCardLicenses(issueCardLicensesRequest: IssueCardLicensesRequest, options?: any): AxiosPromise<IssueCardLicensesResponse> {
|
||||
return localVarFp.issueCardLicenses(issueCardLicensesRequest, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@ -2130,6 +2197,18 @@ export class LicensesApi extends BaseAPI {
|
||||
public createOrders(createOrdersRequest: CreateOrdersRequest, options?: AxiosRequestConfig) {
|
||||
return LicensesApiFp(this.configuration).createOrders(createOrdersRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @summary
|
||||
* @param {IssueCardLicensesRequest} issueCardLicensesRequest
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof LicensesApi
|
||||
*/
|
||||
public issueCardLicenses(issueCardLicensesRequest: IssueCardLicensesRequest, options?: AxiosRequestConfig) {
|
||||
return LicensesApiFp(this.configuration).issueCardLicenses(issueCardLicensesRequest, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import verify from "features/verify/verifySlice";
|
||||
import ui from "features/ui/uiSlice";
|
||||
import user from "features/user/userSlice";
|
||||
import license from "features/license/licenseOrder/licenseSlice";
|
||||
import licenseCardIssue from "features/license/licenseCardIssue/licenseCardIssueSlice";
|
||||
import licenseSummary from "features/license/licenseSummary/licenseSummarySlice";
|
||||
import dictation from "features/dictation/dictationSlice";
|
||||
|
||||
@ -18,6 +19,7 @@ export const store = configureStore({
|
||||
ui,
|
||||
user,
|
||||
license,
|
||||
licenseCardIssue,
|
||||
licenseSummary,
|
||||
dictation,
|
||||
},
|
||||
|
||||
@ -0,0 +1,4 @@
|
||||
export * from "./state";
|
||||
export * from "./operations";
|
||||
export * from "./selectors";
|
||||
export * from "./licenseCardIssueSlice";
|
||||
@ -0,0 +1,53 @@
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
import { LicenseCardIssuesState } from "./state";
|
||||
import { createCardLicenseAsync } from "./operations";
|
||||
|
||||
const initialState: LicenseCardIssuesState = {
|
||||
apps: {
|
||||
numberOfCreateLicenses: 0,
|
||||
},
|
||||
};
|
||||
export const licenseCardSlice = createSlice({
|
||||
name: "licenseCard",
|
||||
initialState,
|
||||
reducers: {
|
||||
changeNumberOfCreateLicenses: (
|
||||
state,
|
||||
action: PayloadAction<{ numberOfCreateLicenses: number }>
|
||||
) => {
|
||||
const { numberOfCreateLicenses } = action.payload;
|
||||
state.apps.numberOfCreateLicenses = numberOfCreateLicenses;
|
||||
},
|
||||
cleanupApps: (state) => {
|
||||
state.apps = initialState.apps;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder.addCase(createCardLicenseAsync.fulfilled, (state, action) => {
|
||||
// csvファイルダウンロード処理
|
||||
// ファイル名は「{発行日時}_licenseCard.csv」とする
|
||||
const currentDate = new Date();
|
||||
const currentDateString = currentDate
|
||||
.toISOString()
|
||||
.replace(/[^0-9]/g, "")
|
||||
.slice(0, -3);
|
||||
const filename = `${currentDateString}_licenseCard.csv`;
|
||||
|
||||
const blob = new Blob([action.payload.cardLicenseKeys.toString()], {
|
||||
type: "mime",
|
||||
});
|
||||
const blobURL = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = blobURL;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
a.parentNode?.removeChild(a);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
export const { changeNumberOfCreateLicenses, cleanupApps } =
|
||||
licenseCardSlice.actions;
|
||||
|
||||
export default licenseCardSlice.reducer;
|
||||
@ -0,0 +1,62 @@
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import { openSnackbar } from "../../ui/uiSlice";
|
||||
import type { RootState } from "../../../app/store";
|
||||
import { getTranslationID } from "../../../translation";
|
||||
import { LicensesApi, IssueCardLicensesResponse } from "../../../api/api";
|
||||
import { Configuration } from "../../../api/configuration";
|
||||
import { ErrorObject, createErrorObject } from "../../../common/errors";
|
||||
|
||||
export const createCardLicenseAsync = createAsyncThunk<
|
||||
IssueCardLicensesResponse,
|
||||
{
|
||||
// パラメータ
|
||||
createCount: number;
|
||||
},
|
||||
{
|
||||
// rejectした時の返却値の型s
|
||||
rejectValue: {
|
||||
error: ErrorObject;
|
||||
};
|
||||
}
|
||||
>("licenses/createCardLicenseAsync", async (args, thunkApi) => {
|
||||
const { createCount } = args;
|
||||
|
||||
// apiのConfigurationを取得する
|
||||
const { getState } = thunkApi;
|
||||
const state = getState() as RootState;
|
||||
const { configuration, accessToken } = state.auth;
|
||||
const config = new Configuration(configuration);
|
||||
const licensesApi = new LicensesApi(config);
|
||||
|
||||
try {
|
||||
const res = await licensesApi.issueCardLicenses(
|
||||
{
|
||||
createCount,
|
||||
},
|
||||
{
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
}
|
||||
);
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
message: getTranslationID("common.message.success"),
|
||||
})
|
||||
);
|
||||
return res.data;
|
||||
} catch (e) {
|
||||
// e ⇒ errorObjectに変換"
|
||||
const error = createErrorObject(e);
|
||||
|
||||
const errorMessage = getTranslationID("common.message.internalServerError");
|
||||
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
})
|
||||
);
|
||||
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
@ -0,0 +1,24 @@
|
||||
import { RootState } from "../../../app/store";
|
||||
|
||||
export const selectInputValidationErrors = (state: RootState) => {
|
||||
const { numberOfCreateLicenses } = state.licenseCardIssue.apps;
|
||||
const hasErrorIncorrectNumberOfCreateLicenses =
|
||||
checkErrorIncorrectNumberOfCreateLicenses(numberOfCreateLicenses);
|
||||
|
||||
return {
|
||||
hasErrorIncorrectNumberOfCreateLicenses,
|
||||
};
|
||||
};
|
||||
export const checkErrorIncorrectNumberOfCreateLicenses = (
|
||||
numberOfCreateLicenses: number
|
||||
): boolean => {
|
||||
// 0以下の場合はエラー
|
||||
if (numberOfCreateLicenses <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const selectNumberOfCreateLicenses = (state: RootState) =>
|
||||
state.licenseCardIssue.apps.numberOfCreateLicenses;
|
||||
@ -0,0 +1,7 @@
|
||||
export interface LicenseCardIssuesState {
|
||||
apps: Apps;
|
||||
}
|
||||
|
||||
export interface Apps {
|
||||
numberOfCreateLicenses: number;
|
||||
}
|
||||
150
dictation_client/src/pages/LicensePage/cardLicenseIssuePopup.tsx
Normal file
150
dictation_client/src/pages/LicensePage/cardLicenseIssuePopup.tsx
Normal file
@ -0,0 +1,150 @@
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AppDispatch } from "app/store";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import _ from "lodash";
|
||||
import styles from "../../styles/app.module.scss";
|
||||
import { getTranslationID } from "../../translation";
|
||||
import close from "../../assets/images/close.svg";
|
||||
import {
|
||||
changeNumberOfCreateLicenses,
|
||||
selectNumberOfCreateLicenses,
|
||||
selectInputValidationErrors,
|
||||
createCardLicenseAsync,
|
||||
cleanupApps,
|
||||
} from "../../features/license/licenseCardIssue/index";
|
||||
|
||||
interface CardLicenseIssuePopupProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const CardLicenseIssuePopup: React.FC<CardLicenseIssuePopupProps> = (
|
||||
props
|
||||
) => {
|
||||
const { onClose } = props;
|
||||
const { t } = useTranslation();
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
const initCount = useSelector(selectNumberOfCreateLicenses);
|
||||
const [count, setCount] = useState<number>(initCount);
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
// useEffectのreturnとしてcleanupAppsを実行することで、ポップアップのアンマウント時に初期化を行う
|
||||
dispatch(cleanupApps());
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
// ポップアップを閉じる処理
|
||||
const closePopup = useCallback(() => {
|
||||
// setIsPushIssueButton(false);
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
|
||||
// 画面からのパラメータ
|
||||
const createCount = useSelector(selectNumberOfCreateLicenses);
|
||||
|
||||
const [isPushCreateButton, setIsPushCreateButton] = useState<boolean>(false);
|
||||
|
||||
// エラー宣言
|
||||
const { hasErrorIncorrectNumberOfCreateLicenses } = useSelector(
|
||||
selectInputValidationErrors
|
||||
);
|
||||
|
||||
// 注文ボタン押下時
|
||||
const onCreateLicense = useCallback(async () => {
|
||||
setIsPushCreateButton(true);
|
||||
|
||||
// エラーチェックを実施
|
||||
if (hasErrorIncorrectNumberOfCreateLicenses) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 注文APIの呼び出し
|
||||
const { meta } = await dispatch(
|
||||
createCardLicenseAsync({
|
||||
createCount,
|
||||
})
|
||||
);
|
||||
setIsPushCreateButton(false);
|
||||
|
||||
if (meta.requestStatus === "fulfilled") {
|
||||
closePopup();
|
||||
}
|
||||
}, [
|
||||
dispatch,
|
||||
closePopup,
|
||||
hasErrorIncorrectNumberOfCreateLicenses,
|
||||
createCount,
|
||||
]);
|
||||
|
||||
// HTML
|
||||
return (
|
||||
<div className={`${styles.modal} ${styles.isShow}`}>
|
||||
<div className={styles.modalBox}>
|
||||
<p className={styles.modalTitle}>
|
||||
{t(getTranslationID("cardLicenseIssuePopupPage.label.title"))}
|
||||
<button type="button" onClick={closePopup}>
|
||||
<img src={close} className={styles.modalTitleIcon} alt="close" />
|
||||
</button>
|
||||
</p>
|
||||
<form className={styles.form}>
|
||||
<dl className={`${styles.formList} ${styles.hasbg}`}>
|
||||
<dt className={styles.formTitle}>
|
||||
{t(getTranslationID("cardLicenseIssuePopupPage.label.subTitle"))}
|
||||
</dt>
|
||||
<dt className={styles.overLine}>
|
||||
{t(getTranslationID("cardLicenseIssuePopupPage.label.number"))}
|
||||
</dt>
|
||||
<dd className="">
|
||||
<input
|
||||
type="number"
|
||||
size={40}
|
||||
name="numberOfLicense"
|
||||
value={count}
|
||||
min={1}
|
||||
max={9999}
|
||||
step={1}
|
||||
className={styles.formInput}
|
||||
onChange={(e) => {
|
||||
const input = Number(e.target.value.substring(0, 4));
|
||||
setCount(input);
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
dispatch(
|
||||
changeNumberOfCreateLicenses({
|
||||
numberOfCreateLicenses: Number(e.target.value),
|
||||
})
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{isPushCreateButton &&
|
||||
hasErrorIncorrectNumberOfCreateLicenses && (
|
||||
<span className={styles.formError}>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"cardLicenseIssuePopupPage.message.createNumberIncorrectError"
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
<dd className={`${styles.full} ${styles.alignCenter}`}>
|
||||
<input
|
||||
type="button"
|
||||
name="submit"
|
||||
value={t(
|
||||
getTranslationID(
|
||||
"cardLicenseIssuePopupPage.label.createButton"
|
||||
)
|
||||
)}
|
||||
className={`${styles.formSubmit} ${styles.marginBtm1} ${styles.isActive}`}
|
||||
onClick={onCreateLicense}
|
||||
/>
|
||||
</dd>
|
||||
</dl>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -1,5 +1,5 @@
|
||||
import React, { useCallback, useEffect, useState } from "react";
|
||||
import { useMsal } from "@azure/msal-react";
|
||||
import React from "react";
|
||||
import Footer from "components/footer";
|
||||
import Header from "components/header";
|
||||
import styles from "styles/app.module.scss";
|
||||
@ -7,37 +7,74 @@ import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
|
||||
import { clearToken } from "features/auth";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { AppDispatch } from "app/store";
|
||||
import { getTranslationID } from "translation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { CardLicenseIssuePopup } from "./cardLicenseIssuePopup";
|
||||
import postAdd from "../../assets/images/post_add.svg";
|
||||
|
||||
const PartnerLicense: React.FC = (): JSX.Element => {
|
||||
const { instance } = useMsal();
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
// popup制御関係
|
||||
const [isCardLicenseIssuePopupOpen, setIsCardLicenseIssuePopupOpen] =
|
||||
useState(false);
|
||||
|
||||
const onlicenseIssueOpen = useCallback(() => {
|
||||
setIsCardLicenseIssuePopupOpen(true);
|
||||
}, [setIsCardLicenseIssuePopupOpen]);
|
||||
|
||||
return (
|
||||
// 表示確認用の仮画面
|
||||
<div className={styles.wrap}>
|
||||
<Header userName="XXXXXX" />
|
||||
<UpdateTokenTimer />
|
||||
<main className={styles.main}>
|
||||
<div className="">
|
||||
<div className={styles.pageHeader}>
|
||||
<h1 className={styles.pageTitle}>第一~四階層です</h1>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.buttonText}
|
||||
onClick={() => {
|
||||
instance.logout({ postLogoutRedirectUri: "/" });
|
||||
dispatch(clearToken());
|
||||
<>
|
||||
{/* 表示確認用の仮画面 */}
|
||||
{/* isPopupOpenがfalseの場合はポップアップのhtmlを生成しないように対応。これによりポップアップは都度生成されて初期化の考慮が減る */}
|
||||
{isCardLicenseIssuePopupOpen && (
|
||||
<CardLicenseIssuePopup
|
||||
onClose={() => {
|
||||
setIsCardLicenseIssuePopupOpen(false);
|
||||
}}
|
||||
>
|
||||
sign out
|
||||
</button>
|
||||
</div>{" "}
|
||||
<Footer />
|
||||
</div>
|
||||
/>
|
||||
)}
|
||||
<div className={styles.wrap}>
|
||||
<Header userName="XXXXXX" />
|
||||
<UpdateTokenTimer />
|
||||
<main className={styles.main}>
|
||||
<div className="">
|
||||
<div className={styles.pageHeader}>
|
||||
<h1 className={styles.pageTitle}>第一~四階層です</h1>
|
||||
</div>
|
||||
</div>
|
||||
<ul className={styles.menuAction}>
|
||||
<li>
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
||||
<a
|
||||
className={`${styles.menuLink} ${styles.isActive}`}
|
||||
onClick={onlicenseIssueOpen}
|
||||
>
|
||||
<img src={postAdd} alt="" className={styles.menuIcon} />
|
||||
{t(
|
||||
getTranslationID("partnerLicensePage.label.cardLicenseButton")
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.buttonText}
|
||||
onClick={() => {
|
||||
instance.logout({ postLogoutRedirectUri: "/" });
|
||||
dispatch(clearToken());
|
||||
}}
|
||||
>
|
||||
sign out
|
||||
</button>
|
||||
</div>{" "}
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -219,5 +219,22 @@
|
||||
"poolTranscriptionist": "(de)Pool",
|
||||
"saveChanges": "(de)Save changes"
|
||||
}
|
||||
},
|
||||
"cardLicenseIssuePopupPage": {
|
||||
"label": {
|
||||
"title": "(de)License Card",
|
||||
"subTitle": "(de)Card License Creation",
|
||||
"number": "(de)Number of create licenses",
|
||||
"createButton": "(de)Create"
|
||||
},
|
||||
"message": {
|
||||
"createSuccess": "(de)処理に成功しました。",
|
||||
"createNumberIncorrectError": "(de)Number of create licensesには1以上の数字を入力してください。"
|
||||
}
|
||||
},
|
||||
"partnerLicensePage": {
|
||||
"label": {
|
||||
"cardLicenseButton": "(de)License Card"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,5 +219,22 @@
|
||||
"poolTranscriptionist": "Pool",
|
||||
"saveChanges": "Save changes"
|
||||
}
|
||||
},
|
||||
"cardLicenseIssuePopupPage": {
|
||||
"label": {
|
||||
"title": "License Card",
|
||||
"subTitle": "Card License Creation",
|
||||
"number": "Number of create licenses",
|
||||
"createButton": "Create"
|
||||
},
|
||||
"message": {
|
||||
"createSuccess": "処理に成功しました。",
|
||||
"createNumberIncorrectError": "Number of create licensesには1以上の数字を入力してください。"
|
||||
}
|
||||
},
|
||||
"partnerLicensePage": {
|
||||
"label": {
|
||||
"cardLicenseButton": "License Card"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,5 +219,22 @@
|
||||
"poolTranscriptionist": "(es)Pool",
|
||||
"saveChanges": "(es)Save changes"
|
||||
}
|
||||
},
|
||||
"cardLicenseIssuePopupPage": {
|
||||
"label": {
|
||||
"title": "(es)License Card",
|
||||
"subTitle": "(es)Card License Creation",
|
||||
"number": "(es)Number of create licenses",
|
||||
"createButton": "(es)Create"
|
||||
},
|
||||
"message": {
|
||||
"createSuccess": "(es)処理に成功しました。",
|
||||
"createNumberIncorrectError": "(es)Number of create licensesには1以上の数字を入力してください。"
|
||||
}
|
||||
},
|
||||
"partnerLicensePage": {
|
||||
"label": {
|
||||
"cardLicenseButton": "(es)License Card"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -219,5 +219,22 @@
|
||||
"poolTranscriptionist": "(fr)Pool",
|
||||
"saveChanges": "(fr)Save changes"
|
||||
}
|
||||
},
|
||||
"cardLicenseIssuePopupPage": {
|
||||
"label": {
|
||||
"title": "(fr)License Card",
|
||||
"subTitle": "(fr)Card License Creation",
|
||||
"number": "(fr)Number of create licenses",
|
||||
"createButton": "(fr)Create"
|
||||
},
|
||||
"message": {
|
||||
"createSuccess": "(fr)処理に成功しました。",
|
||||
"createNumberIncorrectError": "(fr)Number of create licensesには1以上の数字を入力してください。"
|
||||
}
|
||||
},
|
||||
"partnerLicensePage": {
|
||||
"label": {
|
||||
"cardLicenseButton": "(fr)License Card"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user