Merged PR 113: 画面実装(ライセンス注文ポップアップ)
## 概要 [Task1684: 画面実装(ライセンス注文ポップアップ)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1684) - ライセンス注文ポップアップ画面について実装を行いました - ライセンス情報画面について、ボタン押下時にポップアップを表示する実装も併せて実施しております - ポップアップの表示について、表示/非表示を制御するのではなくそもそも表示しない場合はhtmlとして生成しないよう制御を入れています。 - ポップアップ表示の対応により、ポップアップクローズ時の対応が簡易化しています。(これまでは、ポップアップを閉じる際に値を初期化しなおす必要があったのですが、生成しなおす形になるので初期生成の考慮のみでよくなる) - cssについて、5/26時点で最新のファイルを取り込みました ## レビューポイント - ポップアップ生成周りの考え方、初期化の実装内容 - images、cssについては、最新のデザイナさんのものを取り込んだ形なので、レビュー対象外でお願いします ## 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/Task1684?csf=1&web=1&e=xLRn4D ## 動作確認状況 - ローカルで確認 ## 補足 - 無し
This commit is contained in:
parent
a430bd01e5
commit
81dca16d5f
@ -2,6 +2,6 @@
|
||||
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||
"spaces": 2,
|
||||
"generator-cli": {
|
||||
"version": "6.5.0"
|
||||
"version": "6.6.0"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1 @@
|
||||
6.5.0
|
||||
6.6.0
|
||||
File diff suppressed because it is too large
Load Diff
@ -5,6 +5,7 @@ import signup from "features/signup/signupSlice";
|
||||
import verify from "features/verify/verifySlice";
|
||||
import ui from "features/ui/uiSlice";
|
||||
import user from "features/user/userSlice";
|
||||
import license from "features/license/licenseSlice";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
@ -14,6 +15,7 @@ export const store = configureStore({
|
||||
verify,
|
||||
ui,
|
||||
user,
|
||||
license,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
BIN
dictation_client/src/assets/images/ODMScloud.png
Normal file
BIN
dictation_client/src/assets/images/ODMScloud.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
13
dictation_client/src/assets/images/lock.svg
Normal file
13
dictation_client/src/assets/images/lock.svg
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 40 40" style="enable-background:new 0 0 40 40;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#282828;}
|
||||
</style>
|
||||
<path class="st0" d="M9.5,36.7c-0.8,0-1.4-0.3-2-0.8c-0.5-0.5-0.8-1.2-0.8-2V16.3c0-0.8,0.3-1.4,0.8-2c0.5-0.5,1.2-0.8,2-0.8H12V9.7
|
||||
c0-2.2,0.8-4.1,2.4-5.7c1.6-1.6,3.5-2.4,5.7-2.4c2.2,0,4.1,0.8,5.7,2.4C27.3,5.6,28,7.5,28,9.7v3.8h2.5c0.8,0,1.4,0.3,2,0.8
|
||||
c0.5,0.5,0.8,1.2,0.8,2v17.6c0,0.8-0.3,1.4-0.8,2c-0.5,0.5-1.2,0.8-2,0.8H9.5z M20,28.3c0.9,0,1.7-0.3,2.3-0.9c0.6-0.6,1-1.4,1-2.3
|
||||
c0-0.9-0.3-1.6-1-2.3c-0.6-0.7-1.4-1-2.3-1s-1.7,0.3-2.3,1c-0.6,0.7-1,1.4-1,2.3c0,0.9,0.3,1.6,1,2.2C18.3,28,19.1,28.3,20,28.3z
|
||||
M14.7,13.5h10.6V9.7c0-1.4-0.5-2.7-1.5-3.7S21.5,4.5,20,4.5S17.3,5,16.2,6s-1.5,2.3-1.5,3.7V13.5z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1017 B |
14
dictation_client/src/assets/images/lock_open.svg
Normal file
14
dictation_client/src/assets/images/lock_open.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#282828;}
|
||||
</style>
|
||||
<path class="st0" d="M11,16.3h19.5v-4.8c0-1.8-0.6-3.3-1.9-4.6C27.3,5.6,25.8,5,24,5c-1.8,0-3.3,0.6-4.6,1.9
|
||||
c-1.3,1.3-1.9,2.8-1.9,4.6h-3c0-2.6,0.9-4.9,2.8-6.7C19.1,2.9,21.4,2,24,2s4.9,0.9,6.7,2.8s2.8,4.1,2.8,6.7v4.8H37
|
||||
c0.8,0,1.5,0.3,2.1,0.9c0.6,0.6,0.9,1.3,0.9,2.1V41c0,0.8-0.3,1.5-0.9,2.1C38.5,43.7,37.8,44,37,44H11c-0.8,0-1.5-0.3-2.1-0.9
|
||||
C8.3,42.5,8,41.8,8,41V19.3c0-0.8,0.3-1.5,0.9-2.1C9.5,16.6,10.2,16.3,11,16.3z M11,41h26V19.3H11V41z M24,34c1.1,0,2-0.4,2.7-1.1
|
||||
c0.8-0.7,1.1-1.6,1.1-2.6c0-1-0.4-1.9-1.1-2.7s-1.7-1.2-2.7-1.2s-2,0.4-2.7,1.2s-1.1,1.7-1.1,2.8c0,1,0.4,1.9,1.1,2.6S22.9,34,24,34
|
||||
z M11,41V19.3V41z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
BIN
dictation_client/src/assets/images/top-bg.png
Normal file
BIN
dictation_client/src/assets/images/top-bg.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 78 KiB |
11
dictation_client/src/assets/images/undo.svg
Normal file
11
dictation_client/src/assets/images/undo.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 27.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="レイヤー_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
|
||||
y="0px" viewBox="0 0 48 48" style="enable-background:new 0 0 48 48;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#282828;}
|
||||
</style>
|
||||
<path class="st0" d="M14,38v-3h14.5c2.3,0,4.3-0.8,6-2.3c1.7-1.5,2.5-3.5,2.5-5.8s-0.8-4.2-2.5-5.8c-1.7-1.5-3.7-2.3-6-2.3H13.7
|
||||
l5.7,5.7l-2.1,2.1L8,17.3L17.3,8l2.1,2.1l-5.7,5.7h14.7c3.2,0,5.9,1.1,8.2,3.2c2.3,2.1,3.4,4.8,3.4,7.9s-1.1,5.8-3.4,7.9
|
||||
c-2.3,2.1-5,3.2-8.2,3.2H14z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 688 B |
@ -24,4 +24,5 @@ export const errorCodes = [
|
||||
"E010202", // 認証済ユーザエラー
|
||||
"E010301", // メールアドレス登録済みエラー
|
||||
"E010302", // authorId重複エラー
|
||||
"E010401", // PONumber重複エラー
|
||||
] as const;
|
||||
|
||||
4
dictation_client/src/features/license/index.ts
Normal file
4
dictation_client/src/features/license/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export * from "./state";
|
||||
export * from "./operations";
|
||||
export * from "./selectors";
|
||||
export * from "./licenseSlice";
|
||||
31
dictation_client/src/features/license/licenseSlice.ts
Normal file
31
dictation_client/src/features/license/licenseSlice.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
import { LicenseOrdersState } from "./state";
|
||||
|
||||
const initialState: LicenseOrdersState = {
|
||||
apps: {
|
||||
poNumber: "",
|
||||
newOrder: 0,
|
||||
},
|
||||
};
|
||||
export const licenseSlice = createSlice({
|
||||
name: "license",
|
||||
initialState,
|
||||
reducers: {
|
||||
changePoNumber: (state, action: PayloadAction<{ poNumber: string }>) => {
|
||||
const { poNumber } = action.payload;
|
||||
state.apps.poNumber = poNumber.toUpperCase();
|
||||
},
|
||||
changeNewOrder: (state, action: PayloadAction<{ newOrder: number }>) => {
|
||||
const { newOrder } = action.payload;
|
||||
state.apps.newOrder = newOrder;
|
||||
},
|
||||
cleanupApps: (state) => {
|
||||
state.apps = initialState.apps;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { changePoNumber, changeNewOrder, cleanupApps } =
|
||||
licenseSlice.actions;
|
||||
|
||||
export default licenseSlice.reducer;
|
||||
74
dictation_client/src/features/license/operations.ts
Normal file
74
dictation_client/src/features/license/operations.ts
Normal file
@ -0,0 +1,74 @@
|
||||
import { createAsyncThunk } from "@reduxjs/toolkit";
|
||||
import type { RootState } from "app/store";
|
||||
import { getTranslationID } from "translation";
|
||||
import { openSnackbar } from "features/ui/uiSlice";
|
||||
import { LicensesApi } from "../../api/api";
|
||||
import { Configuration } from "../../api/configuration";
|
||||
import { ErrorObject, createErrorObject } from "../../common/errors";
|
||||
|
||||
export const orderLicenseAsync = createAsyncThunk<
|
||||
{
|
||||
/* Empty Object */
|
||||
},
|
||||
{
|
||||
// パラメータ
|
||||
poNumber: string;
|
||||
orderCount: number;
|
||||
},
|
||||
{
|
||||
// rejectした時の返却値の型
|
||||
rejectValue: {
|
||||
error: ErrorObject;
|
||||
};
|
||||
}
|
||||
>("licenses/orderLicenseAsync", async (args, thunkApi) => {
|
||||
const { poNumber, orderCount } = 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 {
|
||||
await licensesApi.createOrders(
|
||||
{
|
||||
poNumber,
|
||||
orderCount,
|
||||
},
|
||||
{
|
||||
headers: { authorization: `Bearer ${accessToken}` },
|
||||
}
|
||||
);
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "info",
|
||||
message: getTranslationID(
|
||||
"licenseOrderPage.message.createOrderSuccess"
|
||||
),
|
||||
})
|
||||
);
|
||||
return {};
|
||||
} catch (e) {
|
||||
// e ⇒ errorObjectに変換"
|
||||
const error = createErrorObject(e);
|
||||
|
||||
let errorMessage = getTranslationID("common.message.internalServerError");
|
||||
|
||||
if (error.code === "E010401") {
|
||||
errorMessage = getTranslationID(
|
||||
"licenseOrderPage.message.poNumberConflictError"
|
||||
);
|
||||
}
|
||||
|
||||
thunkApi.dispatch(
|
||||
openSnackbar({
|
||||
level: "error",
|
||||
message: errorMessage,
|
||||
})
|
||||
);
|
||||
|
||||
return thunkApi.rejectWithValue({ error });
|
||||
}
|
||||
});
|
||||
36
dictation_client/src/features/license/selectors.ts
Normal file
36
dictation_client/src/features/license/selectors.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { RootState } from "app/store";
|
||||
|
||||
export const selectInputValidationErrors = (state: RootState) => {
|
||||
const { poNumber, newOrder } = state.license.apps;
|
||||
|
||||
// 必須項目のチェック
|
||||
const hasErrorEmptyPoNumber = poNumber === "";
|
||||
// newOrderは初期値が入っている+minvalueが1なので必須チェックは行わない
|
||||
|
||||
const hasErrorIncorrectPoNumber = checkErrorIncorrectPoNumber(poNumber);
|
||||
const hasErrorIncorrectNewOrder = checkErrorIncorrectNewOrder(newOrder);
|
||||
|
||||
return {
|
||||
hasErrorEmptyPoNumber,
|
||||
hasErrorIncorrectPoNumber,
|
||||
hasErrorIncorrectNewOrder,
|
||||
};
|
||||
};
|
||||
export const checkErrorIncorrectPoNumber = (poNumber: string): boolean => {
|
||||
// 大文字半角英数字で50文字まで
|
||||
const charaTypePattern = /^[A-Z0-9]{1,50}$/;
|
||||
const charaType = new RegExp(charaTypePattern).test(poNumber);
|
||||
|
||||
return !charaType;
|
||||
};
|
||||
export const checkErrorIncorrectNewOrder = (newOrder: number): boolean => {
|
||||
// 0以下の場合はエラー
|
||||
if (newOrder <= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
export const selectPoNumber = (state: RootState) => state.license.apps.poNumber;
|
||||
export const selectNewOrder = (state: RootState) => state.license.apps.newOrder;
|
||||
8
dictation_client/src/features/license/state.ts
Normal file
8
dictation_client/src/features/license/state.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface LicenseOrdersState {
|
||||
apps: Apps;
|
||||
}
|
||||
|
||||
export interface Apps {
|
||||
poNumber: string;
|
||||
newOrder: number;
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
import { useMsal } from "@azure/msal-react";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { AppDispatch } from "app/store";
|
||||
import { UpdateTokenTimer } from "components/auth/updateTokenTimer";
|
||||
import Footer from "components/footer";
|
||||
import Header from "components/header";
|
||||
import { clearToken } from "features/auth";
|
||||
import React from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import styles from "styles/app.module.scss";
|
||||
import { getTranslationID } from "translation";
|
||||
@ -12,74 +12,94 @@ import { useTranslation } from "react-i18next";
|
||||
import postAdd from "../../assets/images/post_add.svg";
|
||||
import history from "../../assets/images/history.svg";
|
||||
import key from "../../assets/images/key.svg";
|
||||
import { LicenseOrderPopup } from "./licenseOrderPopup";
|
||||
|
||||
const LicensePage: React.FC = (): JSX.Element => {
|
||||
const { instance } = useMsal();
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
const [t] = useTranslation();
|
||||
|
||||
const [islicenseOrderPopupOpen, setIslicenseOrderPopupOpen] = useState(false);
|
||||
|
||||
const onlicenseOrderOpen = useCallback(() => {
|
||||
setIslicenseOrderPopupOpen(true);
|
||||
}, [setIslicenseOrderPopupOpen]);
|
||||
|
||||
return (
|
||||
/* TODO 現状(PBI1221)はライセンス注文PUを起動するためのボタンのみ。別途ライセンス注文を実装するPBIで全般的に見直し。 */
|
||||
<div className={styles.wrap}>
|
||||
<Header userName="XXXXXX" />
|
||||
|
||||
<UpdateTokenTimer />
|
||||
<main className={styles.main}>
|
||||
<div className="">
|
||||
<div className={styles.pageHeader}>
|
||||
<h1 className={styles.pageTitle}>
|
||||
{t(getTranslationID("LicensePage.label.title"))}
|
||||
</h1>
|
||||
</div>
|
||||
<section className={styles.license}>
|
||||
<div>
|
||||
<h2 className="">
|
||||
{t(getTranslationID("LicensePage.label.subTitle"))}
|
||||
</h2>
|
||||
<ul className={styles.menuAction}>
|
||||
<li>
|
||||
{/* TODO ライセンス注文ポップアップのタスクにて起動の実装を行う(その際buttonに変更する想定) */}
|
||||
<a
|
||||
href=""
|
||||
className={`${styles.menuLink} ${styles.isActive}`}
|
||||
>
|
||||
<img src={postAdd} alt="" className={styles.menuIcon} />
|
||||
{t(getTranslationID("LicensePage.label.orderLicense"))}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" className={`${styles.menuLink}`}>
|
||||
<img src={history} alt="" className={styles.menuIcon} />
|
||||
{t(getTranslationID("LicensePage.label.orderHistory"))}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" className={`${styles.menuLink}`}>
|
||||
<img src={key} alt="" className={styles.menuIcon} />
|
||||
{t(getTranslationID("LicensePage.label.importLicenseKey"))}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* TODO 画面デザインにはないが、試験時に便利なので配置しておく。ライセンス注文を実装するPBIで正式なサインアウトのレイアウトに直す */}
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.buttonText}
|
||||
onClick={() => {
|
||||
instance.logout({ postLogoutRedirectUri: "/" });
|
||||
dispatch(clearToken());
|
||||
<>
|
||||
{/* isPopupOpenがfalseの場合はポップアップのhtmlを生成しないように対応。これによりポップアップは都度生成されて初期化の考慮が減る */}
|
||||
{islicenseOrderPopupOpen && (
|
||||
<LicenseOrderPopup
|
||||
onClose={() => {
|
||||
setIslicenseOrderPopupOpen(false);
|
||||
}}
|
||||
>
|
||||
sign out
|
||||
</button>
|
||||
/>
|
||||
)}
|
||||
{/* TODO 現状(PBI1221)はライセンス注文PUを起動するためのボタンのみ。別途ライセンス注文を実装するPBIで全般的に見直し。 */}
|
||||
<div className={styles.wrap}>
|
||||
<Header userName="XXXXXX" />
|
||||
|
||||
<UpdateTokenTimer />
|
||||
<main className={styles.main}>
|
||||
<div className="">
|
||||
<div className={styles.pageHeader}>
|
||||
<h1 className={styles.pageTitle}>
|
||||
{t(getTranslationID("LicensePage.label.title"))}
|
||||
</h1>
|
||||
</div>
|
||||
<section className={styles.license}>
|
||||
<div>
|
||||
<h2 className="">
|
||||
{t(getTranslationID("LicensePage.label.subTitle"))}
|
||||
</h2>
|
||||
<ul className={styles.menuAction}>
|
||||
<li>
|
||||
{/* TODO ライセンス注文ポップアップのタスクにて起動の実装を行う(その際buttonに変更する想定) */}
|
||||
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
|
||||
<a
|
||||
className={`${styles.menuLink} ${styles.isActive}`}
|
||||
onClick={onlicenseOrderOpen}
|
||||
>
|
||||
<img src={postAdd} alt="" className={styles.menuIcon} />
|
||||
{t(getTranslationID("LicensePage.label.orderLicense"))}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" className={`${styles.menuLink}`}>
|
||||
<img src={history} alt="" className={styles.menuIcon} />
|
||||
{t(getTranslationID("LicensePage.label.orderHistory"))}
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="" className={`${styles.menuLink}`}>
|
||||
<img src={key} alt="" className={styles.menuIcon} />
|
||||
{t(
|
||||
getTranslationID("LicensePage.label.importLicenseKey")
|
||||
)}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
{/* TODO 画面デザインにはないが、試験時に便利なので配置しておく。ライセンス注文を実装するPBIで正式なサインアウトのレイアウトに直す */}
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
className={styles.buttonText}
|
||||
onClick={() => {
|
||||
instance.logout({ postLogoutRedirectUri: "/" });
|
||||
dispatch(clearToken());
|
||||
}}
|
||||
>
|
||||
sign out
|
||||
</button>
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
<Footer />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
210
dictation_client/src/pages/LicensePage/licenseOrderPopup.tsx
Normal file
210
dictation_client/src/pages/LicensePage/licenseOrderPopup.tsx
Normal file
@ -0,0 +1,210 @@
|
||||
import { AppDispatch } from "app/store";
|
||||
import React, { useState, useCallback, useEffect } from "react";
|
||||
import styles from "styles/app.module.scss";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getTranslationID } from "translation";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
changePoNumber,
|
||||
changeNewOrder,
|
||||
selectPoNumber,
|
||||
selectNewOrder,
|
||||
selectInputValidationErrors,
|
||||
orderLicenseAsync,
|
||||
cleanupApps,
|
||||
} from "features/license";
|
||||
import close from "../../assets/images/close.svg";
|
||||
|
||||
interface LicenseOrderPopupProps {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const LicenseOrderPopup: React.FC<LicenseOrderPopupProps> = (props) => {
|
||||
const { onClose } = props;
|
||||
const dispatch: AppDispatch = useDispatch();
|
||||
const { t } = useTranslation();
|
||||
const initCount = useSelector(selectNewOrder);
|
||||
const [count, setCount] = useState<number>(initCount);
|
||||
const [poNumberInputValue, setpoNumberInputValue] = useState<string>();
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
// useEffectのreturnとしてcleanupAppsを実行することで、ポップアップのアンマウント時に初期化を行う
|
||||
dispatch(cleanupApps());
|
||||
},
|
||||
[dispatch]
|
||||
);
|
||||
|
||||
// ポップアップを閉じる処理
|
||||
const closePopup = useCallback(() => {
|
||||
setIsPushOrderButton(false);
|
||||
onClose();
|
||||
}, [onClose]);
|
||||
|
||||
// 画面からのパラメータ
|
||||
const poNumber = useSelector(selectPoNumber);
|
||||
const orderCount = useSelector(selectNewOrder);
|
||||
|
||||
const [isPushOrderButton, setIsPushOrderButton] = useState<boolean>(false);
|
||||
|
||||
// エラー宣言
|
||||
const {
|
||||
hasErrorEmptyPoNumber,
|
||||
hasErrorIncorrectPoNumber,
|
||||
hasErrorIncorrectNewOrder,
|
||||
} = useSelector(selectInputValidationErrors);
|
||||
|
||||
// 注文ボタン押下時
|
||||
const onOrderLicense = useCallback(async () => {
|
||||
setIsPushOrderButton(true);
|
||||
// エラーチェックを実施
|
||||
if (
|
||||
hasErrorEmptyPoNumber ||
|
||||
hasErrorIncorrectPoNumber ||
|
||||
hasErrorIncorrectNewOrder
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// ダイアログ確認
|
||||
if (
|
||||
/* eslint-disable-next-line no-alert */
|
||||
!window.confirm(
|
||||
t(getTranslationID("licenseOrderPage.message.confirmOrder"))
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 注文APIの呼び出し
|
||||
const { meta } = await dispatch(
|
||||
orderLicenseAsync({
|
||||
poNumber,
|
||||
orderCount,
|
||||
})
|
||||
);
|
||||
setIsPushOrderButton(false);
|
||||
|
||||
if (meta.requestStatus === "fulfilled") {
|
||||
closePopup();
|
||||
}
|
||||
}, [
|
||||
dispatch,
|
||||
closePopup,
|
||||
t,
|
||||
hasErrorEmptyPoNumber,
|
||||
hasErrorIncorrectPoNumber,
|
||||
hasErrorIncorrectNewOrder,
|
||||
poNumber,
|
||||
orderCount,
|
||||
]);
|
||||
|
||||
// HTML
|
||||
return (
|
||||
<div className={`${styles.modal} ${styles.isShow}`}>
|
||||
<div className={styles.modalBox}>
|
||||
<p className={styles.modalTitle}>
|
||||
{t(getTranslationID("licenseOrderPage.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>{t(getTranslationID("licenseOrderPage.label.licenses"))}</dt>
|
||||
<dd>
|
||||
<input
|
||||
type="text"
|
||||
size={40}
|
||||
name=""
|
||||
value={t(
|
||||
getTranslationID("licenseOrderPage.label.licenseTypeText")
|
||||
)}
|
||||
maxLength={16}
|
||||
className={styles.formInput}
|
||||
readOnly
|
||||
/>
|
||||
</dd>
|
||||
<dt>{t(getTranslationID("licenseOrderPage.label.poNumber"))}</dt>
|
||||
<dd>
|
||||
<input
|
||||
type="text"
|
||||
size={40}
|
||||
name="poNumber"
|
||||
value={poNumberInputValue}
|
||||
maxLength={50}
|
||||
className={styles.formInput}
|
||||
onChange={(e) => {
|
||||
const input = e.target.value.toUpperCase();
|
||||
setpoNumberInputValue(input);
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
dispatch(changePoNumber({ poNumber: e.target.value }));
|
||||
}}
|
||||
/>
|
||||
{isPushOrderButton && hasErrorEmptyPoNumber && (
|
||||
<span className={styles.formError}>
|
||||
{t(
|
||||
getTranslationID("licenseOrderPage.message.inputEmptyError")
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
{isPushOrderButton && hasErrorIncorrectPoNumber && (
|
||||
<span className={styles.formError}>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"licenseOrderPage.message.poNumberIncorrectError"
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
<dt>{t(getTranslationID("licenseOrderPage.label.newOrder"))}</dt>
|
||||
<dd>
|
||||
<input
|
||||
type="number"
|
||||
size={4}
|
||||
name="newOrder"
|
||||
value={count}
|
||||
min={1}
|
||||
max={9999}
|
||||
step={1}
|
||||
maxLength={4}
|
||||
className={styles.formInput}
|
||||
onChange={(e) => {
|
||||
const input = Number(e.target.value.substring(0, 4));
|
||||
setCount(input);
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
dispatch(
|
||||
changeNewOrder({ newOrder: Number(e.target.value) })
|
||||
);
|
||||
}}
|
||||
/>
|
||||
{isPushOrderButton && hasErrorIncorrectNewOrder && (
|
||||
<span className={styles.formError}>
|
||||
{t(
|
||||
getTranslationID(
|
||||
"licenseOrderPage.message.newOrderIncorrectError"
|
||||
)
|
||||
)}
|
||||
</span>
|
||||
)}
|
||||
</dd>
|
||||
<dd className={`${styles.full} ${styles.alignCenter}`}>
|
||||
<input
|
||||
type="button"
|
||||
name="submit"
|
||||
value={t(
|
||||
getTranslationID("licenseOrderPage.label.orderButton")
|
||||
)}
|
||||
className={`${styles.formSubmit} ${styles.marginBtm1} ${styles.isActive}`}
|
||||
onClick={onOrderLicense}
|
||||
/>
|
||||
</dd>
|
||||
</dl>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
103
dictation_client/src/styles/app.module.scss.d.ts
vendored
103
dictation_client/src/styles/app.module.scss.d.ts
vendored
@ -1,6 +1,5 @@
|
||||
declare const classNames: {
|
||||
readonly wrap: "wrap";
|
||||
readonly home: "home";
|
||||
readonly header: "header";
|
||||
readonly headerLogo: "headerLogo";
|
||||
readonly headerSub: "headerSub";
|
||||
@ -12,6 +11,9 @@ declare const classNames: {
|
||||
readonly mainSmall: "mainSmall";
|
||||
readonly mainLogin: "mainLogin";
|
||||
readonly footer: "footer";
|
||||
readonly brCrumb: "brCrumb";
|
||||
readonly tlIcon: "tlIcon";
|
||||
readonly brCrumbAcc: "brCrumbAcc";
|
||||
readonly buttonNormal: "buttonNormal";
|
||||
readonly small: "small";
|
||||
readonly red: "red";
|
||||
@ -37,6 +39,7 @@ declare const classNames: {
|
||||
readonly formBack: "formBack";
|
||||
readonly formButtonTx: "formButtonTx";
|
||||
readonly formDone: "formDone";
|
||||
readonly listVertical: "listVertical";
|
||||
readonly loadingBoxSpinner: "loadingBoxSpinner";
|
||||
readonly modal: "modal";
|
||||
readonly isShow: "isShow";
|
||||
@ -48,6 +51,7 @@ declare const classNames: {
|
||||
readonly slideSet: "slideSet";
|
||||
readonly pageHeader: "pageHeader";
|
||||
readonly pageTitle: "pageTitle";
|
||||
readonly pageTx: "pageTx";
|
||||
readonly pagenation: "pagenation";
|
||||
readonly pagenationNav: "pagenationNav";
|
||||
readonly pagenationTotal: "pagenationTotal";
|
||||
@ -59,16 +63,106 @@ declare const classNames: {
|
||||
readonly table: "table";
|
||||
readonly tableHeader: "tableHeader";
|
||||
readonly hasSort: "hasSort";
|
||||
readonly icCheckCircle: "icCheckCircle";
|
||||
readonly noLine: "noLine";
|
||||
readonly home: "home";
|
||||
readonly pgHome: "pgHome";
|
||||
readonly ODMSlogo: "ODMSlogo";
|
||||
readonly pgHomeLinks: "pgHomeLinks";
|
||||
readonly buttonIcon: "buttonIcon";
|
||||
readonly user: "user";
|
||||
readonly license: "license";
|
||||
readonly dictation: "dictation";
|
||||
readonly isSelected: "isSelected";
|
||||
readonly menuInTable: "menuInTable";
|
||||
readonly menuAction: "menuAction";
|
||||
readonly inTable: "inTable";
|
||||
readonly menuLink: "menuLink";
|
||||
readonly colorLink: "colorLink";
|
||||
readonly menuMore: "menuMore";
|
||||
readonly menuIcon: "menuIcon";
|
||||
readonly icCheckCircle: "icCheckCircle";
|
||||
readonly history: "history";
|
||||
readonly cardHistory: "cardHistory";
|
||||
readonly partner: "partner";
|
||||
readonly isOpen: "isOpen";
|
||||
readonly role2: "role2";
|
||||
readonly role3: "role3";
|
||||
readonly role4: "role4";
|
||||
readonly iconver: "iconver";
|
||||
readonly role5: "role5";
|
||||
readonly displayOptions: "displayOptions";
|
||||
readonly tableFilter: "tableFilter";
|
||||
readonly tableFilter2: "tableFilter2";
|
||||
readonly isDisable: "isDisable";
|
||||
readonly tableWrap: "tableWrap";
|
||||
readonly clm0: "clm0";
|
||||
readonly txWsline: "txWsline";
|
||||
readonly hidePri: "hidePri";
|
||||
readonly opPri: "opPri";
|
||||
readonly hideSize: "hideSize";
|
||||
readonly opSize: "opSize";
|
||||
readonly hideUpd: "hideUpd";
|
||||
readonly opUpd: "opUpd";
|
||||
readonly hideC1: "hideC1";
|
||||
readonly clm1: "clm1";
|
||||
readonly hideC2: "hideC2";
|
||||
readonly clm2: "clm2";
|
||||
readonly hideC3: "hideC3";
|
||||
readonly clm3: "clm3";
|
||||
readonly hideC4: "hideC4";
|
||||
readonly clm4: "clm4";
|
||||
readonly hideC5: "hideC5";
|
||||
readonly clm5: "clm5";
|
||||
readonly hideC6: "hideC6";
|
||||
readonly clm6: "clm6";
|
||||
readonly hideC7: "hideC7";
|
||||
readonly clm7: "clm7";
|
||||
readonly hideC8: "hideC8";
|
||||
readonly clm8: "clm8";
|
||||
readonly hideC9: "hideC9";
|
||||
readonly clm9: "clm9";
|
||||
readonly hideC10: "hideC10";
|
||||
readonly clm10: "clm10";
|
||||
readonly hideC11: "hideC11";
|
||||
readonly clm11: "clm11";
|
||||
readonly hideC12: "hideC12";
|
||||
readonly clm12: "clm12";
|
||||
readonly hideC13: "hideC13";
|
||||
readonly clm13: "clm13";
|
||||
readonly hideC14: "hideC14";
|
||||
readonly clm14: "clm14";
|
||||
readonly hideC15: "hideC15";
|
||||
readonly clm15: "clm15";
|
||||
readonly hideC16: "hideC16";
|
||||
readonly clm16: "clm16";
|
||||
readonly hideO1: "hideO1";
|
||||
readonly op1: "op1";
|
||||
readonly hideO2: "hideO2";
|
||||
readonly op2: "op2";
|
||||
readonly hideO3: "hideO3";
|
||||
readonly op3: "op3";
|
||||
readonly hideO4: "hideO4";
|
||||
readonly op4: "op4";
|
||||
readonly hideO5: "hideO5";
|
||||
readonly op5: "op5";
|
||||
readonly hideO6: "hideO6";
|
||||
readonly op6: "op6";
|
||||
readonly hideO7: "hideO7";
|
||||
readonly op7: "op7";
|
||||
readonly hideO8: "hideO8";
|
||||
readonly op8: "op8";
|
||||
readonly hideO9: "hideO9";
|
||||
readonly op9: "op9";
|
||||
readonly hideO10: "hideO10";
|
||||
readonly op10: "op10";
|
||||
readonly mnPlay: "mnPlay";
|
||||
readonly mnFile: "mnFile";
|
||||
readonly mnChange: "mnChange";
|
||||
readonly mnDelete: "mnDelete";
|
||||
readonly formChange: "formChange";
|
||||
readonly chooseMember: "chooseMember";
|
||||
readonly holdMember: "holdMember";
|
||||
readonly changeTitle: "changeTitle";
|
||||
readonly alignCenter: "alignCenter";
|
||||
readonly alignLeft: "alignLeft";
|
||||
readonly alignRight: "alignRight";
|
||||
@ -83,7 +177,12 @@ declare const classNames: {
|
||||
readonly marginRgt1: "marginRgt1";
|
||||
readonly marginRgt2: "marginRgt2";
|
||||
readonly marginRgt3: "marginRgt3";
|
||||
readonly paddSide0: "paddSide0";
|
||||
readonly paddSide1: "paddSide1";
|
||||
readonly paddSide2: "paddSide2";
|
||||
readonly paddSide3: "paddSide3";
|
||||
readonly txNormal: "txNormal";
|
||||
readonly txIcon: "txIcon";
|
||||
readonly txWswrap: "txWswrap";
|
||||
};
|
||||
export = classNames;
|
||||
|
||||
@ -136,5 +136,23 @@
|
||||
"orderHistory": "(de)Order History",
|
||||
"importLicenseKey": "(de)Import License Key"
|
||||
}
|
||||
},
|
||||
"licenseOrderPage": {
|
||||
"message": {
|
||||
"inputEmptyError": "(de)この項目の入力は必須です。入力してください。",
|
||||
"poNumberIncorrectError": "(de)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
|
||||
"newOrderIncorrectError": "(de)New Orderには1以上の数字を入力してください。",
|
||||
"confirmOrder": "(de)注文を行いますか?",
|
||||
"createOrderSuccess": "(de)処理に成功しました。",
|
||||
"poNumberConflictError": "(de)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
|
||||
},
|
||||
"label": {
|
||||
"title": "(de)Order License",
|
||||
"licenses": "(de)Licenses",
|
||||
"poNumber": "(de)PO Number",
|
||||
"newOrder": "(de)New Order",
|
||||
"orderButton": "(de)Order",
|
||||
"licenseTypeText": "(de)One Year"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,5 +136,23 @@
|
||||
"orderHistory": "Order History",
|
||||
"importLicenseKey": "Import License Key"
|
||||
}
|
||||
},
|
||||
"licenseOrderPage": {
|
||||
"message": {
|
||||
"inputEmptyError": "この項目の入力は必須です。入力してください。",
|
||||
"poNumberIncorrectError": "PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
|
||||
"newOrderIncorrectError": "New Orderには1以上の数字を入力してください。",
|
||||
"confirmOrder": "注文を行いますか?",
|
||||
"createOrderSuccess": "処理に成功しました。",
|
||||
"poNumberConflictError": "既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
|
||||
},
|
||||
"label": {
|
||||
"title": "Order License",
|
||||
"licenses": "Licenses",
|
||||
"poNumber": "PO Number",
|
||||
"newOrder": "New Order",
|
||||
"orderButton": "Order",
|
||||
"licenseTypeText": "One Year"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,5 +136,23 @@
|
||||
"orderHistory": "(es)Order History",
|
||||
"importLicenseKey": "(es)Import License Key"
|
||||
}
|
||||
},
|
||||
"licenseOrderPage": {
|
||||
"message": {
|
||||
"inputEmptyError": "(es)この項目の入力は必須です。入力してください。",
|
||||
"poNumberIncorrectError": "(es)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
|
||||
"newOrderIncorrectError": "(es)New Orderには1以上の数字を入力してください。",
|
||||
"confirmOrder": "(es)注文を行いますか?",
|
||||
"createOrderSuccess": "(es)処理に成功しました。",
|
||||
"poNumberConflictError": "(es)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
|
||||
},
|
||||
"label": {
|
||||
"title": "(es)Order License",
|
||||
"licenses": "(es)Licenses",
|
||||
"poNumber": "(es)PO Number",
|
||||
"newOrder": "(es)New Order",
|
||||
"orderButton": "(es)Order",
|
||||
"licenseTypeText": "(es)One Year"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -136,5 +136,23 @@
|
||||
"orderHistory": "(fr)Order History",
|
||||
"importLicenseKey": "(fr)Import License Key"
|
||||
}
|
||||
},
|
||||
"licenseOrderPage": {
|
||||
"message": {
|
||||
"inputEmptyError": "(fr)この項目の入力は必須です。入力してください。",
|
||||
"poNumberIncorrectError": "(fr)PO Numberの形式が不正です。PO Numberは半角英数字(大文字)のみ入力可能です。",
|
||||
"newOrderIncorrectError": "(fr)New Orderには1以上の数字を入力してください。",
|
||||
"confirmOrder": "(fr)注文を行いますか?",
|
||||
"createOrderSuccess": "(fr)処理に成功しました。",
|
||||
"poNumberConflictError": "(fr)既に同じPO Numberで注文済みもしくは発行済みの注文が存在しています。他のPO Numberで注文してください。"
|
||||
},
|
||||
"label": {
|
||||
"title": "(fr)Order License",
|
||||
"licenses": "(fr)Licenses",
|
||||
"poNumber": "(fr)PO Number",
|
||||
"newOrder": "(fr)New Order",
|
||||
"orderButton": "(fr)Order",
|
||||
"licenseTypeText": "(fr)One Year"
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user