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:
oura.a 2023-07-03 05:35:19 +00:00
parent 9270dc2c0d
commit 709db0d2ec
14 changed files with 3038 additions and 1103 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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));
}
}

View File

@ -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,
},

View File

@ -0,0 +1,4 @@
export * from "./state";
export * from "./operations";
export * from "./selectors";
export * from "./licenseCardIssueSlice";

View File

@ -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;

View File

@ -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 });
}
});

View File

@ -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;

View File

@ -0,0 +1,7 @@
export interface LicenseCardIssuesState {
apps: Apps;
}
export interface Apps {
numberOfCreateLicenses: number;
}

View 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>
);
};

View File

@ -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>
</>
);
};

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}

View File

@ -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"
}
}
}