Merged PR 255: 画面実装(パートナーアカウント追加PU)

## 概要
[Task2156: 画面実装(パートナーアカウント追加PU)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2156)

- 何をどう変更したか、追加したライブラリなど
パートナー画面のAdd Partner押下時のポップアップ画面の実装を行いました。

- このPull Requestでの対象/対象外
呼び出し元の画面であるパートナー一覧画面は仮実装なので、対象外
- 影響範囲(他の機能にも影響があるか)
新規のため、なし

## レビューポイント
- 特にレビューしてほしい箇所
デザイナさんのデザインと相違がないか。
ローディング中の処理など、他機能からの取りこぼしがないか。

## UIの変更
- Before/Afterのスクショなど
- スクショ置き場
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/Task2156?csf=1&web=1&e=JdT2Bx

## 動作確認状況
- ローカルで表示確認
先にDONEのAPIと合わせてローカル環境で、パートナー追加の一連の流れが行えることを確認。

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
maruyama.t 2023-07-19 09:25:24 +00:00
parent 478d454259
commit 28a88a1b3d
13 changed files with 718 additions and 53 deletions

View File

@ -336,6 +336,37 @@ export interface CreateOrdersRequest {
*/
'orderCount': number;
}
/**
*
* @export
* @interface CreatePartnerAccountRequest
*/
export interface CreatePartnerAccountRequest {
/**
*
* @type {string}
* @memberof CreatePartnerAccountRequest
*/
'companyName': string;
/**
* (ISO 3166-1 alpha-2)
* @type {string}
* @memberof CreatePartnerAccountRequest
*/
'country': string;
/**
*
* @type {string}
* @memberof CreatePartnerAccountRequest
*/
'adminName': string;
/**
*
* @type {string}
* @memberof CreatePartnerAccountRequest
*/
'email'?: string;
}
/**
*
* @export
@ -1119,6 +1150,46 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat
options: localVarRequestOptions,
};
},
/**
*
* @summary
* @param {CreatePartnerAccountRequest} createPartnerAccountRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
createPartnerAccount: async (createPartnerAccountRequest: CreatePartnerAccountRequest, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
// verify required parameter 'createPartnerAccountRequest' is not null or undefined
assertParamExists('createPartnerAccount', 'createPartnerAccountRequest', createPartnerAccountRequest)
const localVarPath = `/accounts/partner`;
// 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(createPartnerAccountRequest, localVarRequestOptions, configuration)
return {
url: toPathString(localVarUrlObj),
options: localVarRequestOptions,
};
},
/**
*
* @summary
@ -1282,6 +1353,17 @@ export const AccountsApiFp = function(configuration?: Configuration) {
const localVarAxiosArgs = await localVarAxiosParamCreator.createAccount(createAccountRequest, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @summary
* @param {CreatePartnerAccountRequest} createPartnerAccountRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
async createPartnerAccount(createPartnerAccountRequest: CreatePartnerAccountRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<object>> {
const localVarAxiosArgs = await localVarAxiosParamCreator.createPartnerAccount(createPartnerAccountRequest, options);
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
},
/**
*
* @summary
@ -1343,6 +1425,16 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP
createAccount(createAccountRequest: CreateAccountRequest, options?: any): AxiosPromise<object> {
return localVarFp.createAccount(createAccountRequest, options).then((request) => request(axios, basePath));
},
/**
*
* @summary
* @param {CreatePartnerAccountRequest} createPartnerAccountRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
*/
createPartnerAccount(createPartnerAccountRequest: CreatePartnerAccountRequest, options?: any): AxiosPromise<object> {
return localVarFp.createPartnerAccount(createPartnerAccountRequest, options).then((request) => request(axios, basePath));
},
/**
*
* @summary
@ -1402,6 +1494,18 @@ export class AccountsApi extends BaseAPI {
return AccountsApiFp(this.configuration).createAccount(createAccountRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary
* @param {CreatePartnerAccountRequest} createPartnerAccountRequest
* @param {*} [options] Override http request option.
* @throws {RequiredError}
* @memberof AccountsApi
*/
public createPartnerAccount(createPartnerAccountRequest: CreatePartnerAccountRequest, options?: AxiosRequestConfig) {
return AccountsApiFp(this.configuration).createPartnerAccount(createPartnerAccountRequest, options).then((request) => request(this.axios, this.basePath));
}
/**
*
* @summary

View File

@ -10,6 +10,7 @@ import licenseCardIssue from "features/license/licenseCardIssue/licenseCardIssue
import licenseCardActivate from "features/license/licenseCardActivate/licenseCardActivateSlice";
import licenseSummary from "features/license/licenseSummary/licenseSummarySlice";
import dictation from "features/dictation/dictationSlice";
import partner from "features/partner/partnerSlice";
export const store = configureStore({
reducer: {
@ -24,6 +25,7 @@ export const store = configureStore({
licenseCardActivate,
licenseSummary,
dictation,
partner,
},
});

View File

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

View File

@ -0,0 +1,64 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import type { RootState } from "app/store";
import { ErrorObject, createErrorObject } from "common/errors";
import { getTranslationID } from "translation";
import { openSnackbar } from "features/ui/uiSlice";
import { AccountsApi, CreatePartnerAccountRequest } from "../../api/api";
import { Configuration } from "../../api/configuration";
export const createPartnerAccountAsync = createAsyncThunk<
{
/* Empty Object */
},
CreatePartnerAccountRequest,
{
// rejectした時の返却値の型
rejectValue: {
error: ErrorObject;
};
}
>("partner/createPartnerAccountAsync", async (args, thunkApi) => {
const createPartnerAccountRequest = args;
// apiのConfigurationを取得する
const { getState } = thunkApi;
const state = getState() as RootState;
const { configuration, accessToken } = state.auth;
const config = new Configuration(configuration);
const accountApi = new AccountsApi(config);
try {
await accountApi.createPartnerAccount(createPartnerAccountRequest, {
headers: { authorization: `Bearer ${accessToken}` },
});
thunkApi.dispatch(
openSnackbar({
level: "info",
message: getTranslationID(
"addPartnerAccountPopupPage.message.addAccountSuccess"
),
})
);
return {};
} catch (e) {
const error = createErrorObject(e);
if (error.code === "E010301") {
thunkApi.dispatch(
openSnackbar({
level: "error",
message: getTranslationID(
"addPartnerAccountPopupPage.message.emailConflictError"
),
})
);
} else {
thunkApi.dispatch(
openSnackbar({
level: "error",
message: getTranslationID("common.message.internalServerError"),
})
);
}
return thunkApi.rejectWithValue({ error });
}
});

View File

@ -0,0 +1,60 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { PartnerState } from "./state";
import { createPartnerAccountAsync } from "./operations";
const initialState: PartnerState = {
apps: {
addPartner: {
companyName: "",
country: "",
adminName: "",
email: "",
},
isLoading: false,
},
};
export const partnerSlice = createSlice({
name: "partner",
initialState,
reducers: {
changeCompany: (state, action: PayloadAction<{ company: string }>) => {
const { company } = action.payload;
state.apps.addPartner.companyName = company;
},
changeCountry: (state, action: PayloadAction<{ country: string }>) => {
const { country } = action.payload;
state.apps.addPartner.country = country;
},
changeAdminName: (state, action: PayloadAction<{ adminName: string }>) => {
const { adminName } = action.payload;
state.apps.addPartner.adminName = adminName;
},
changeEmail: (state, action: PayloadAction<{ email: string }>) => {
const { email } = action.payload;
state.apps.addPartner.email = email;
},
cleanupAddPartner: (state) => {
state.apps.addPartner = initialState.apps.addPartner;
},
},
extraReducers: (builder) => {
builder.addCase(createPartnerAccountAsync.pending, (state) => {
state.apps.isLoading = true;
});
builder.addCase(createPartnerAccountAsync.fulfilled, (state) => {
state.apps.isLoading = false;
});
builder.addCase(createPartnerAccountAsync.rejected, (state) => {
state.apps.isLoading = false;
});
},
});
export const {
changeEmail,
changeAdminName,
changeCompany,
changeCountry,
cleanupAddPartner,
} = partnerSlice.actions;
export default partnerSlice.reducer;

View File

@ -0,0 +1,35 @@
import { RootState } from "app/store";
export const selectInputValidationErrors = (state: RootState) => {
// 必須項目のチェック
const hasErrorEmptyCompany = state.partner.apps.addPartner.companyName === "";
const hasErrorEmptyCountry = state.partner.apps.addPartner.country === "";
const hasErrorEmptyAdminName = state.partner.apps.addPartner.adminName === "";
const hasErrorEmptyEmail = state.partner.apps.addPartner.email === "";
const hasErrorIncorrectEmail =
(state.partner.apps.addPartner.email as string).match(/^[^@]+@[^@]+$/)
?.length !== 1;
return {
hasErrorEmptyCompany,
hasErrorEmptyCountry,
hasErrorEmptyAdminName,
hasErrorEmptyEmail,
hasErrorIncorrectEmail,
};
};
// Account Info
export const selectCompany = (state: RootState) =>
state.partner.apps.addPartner.companyName;
export const selectCountry = (state: RootState) =>
state.partner.apps.addPartner.country;
// Admin Info
export const selectAdminName = (state: RootState) =>
state.partner.apps.addPartner.adminName;
export const selectEmail = (state: RootState) =>
state.partner.apps.addPartner.email;
export const selectIsLoading = (state: RootState) =>
state.partner.apps.isLoading;

View File

@ -0,0 +1,10 @@
import { CreatePartnerAccountRequest } from "../../api/api";
export interface PartnerState {
apps: Apps;
}
export interface Apps {
addPartner: CreatePartnerAccountRequest;
isLoading: boolean;
}

View File

@ -0,0 +1,291 @@
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 {
selectAdminName,
selectCompany,
selectCountry,
selectEmail,
selectInputValidationErrors,
selectIsLoading,
} from "features/partner/selectors";
import {
changeAdminName,
changeCompany,
changeCountry,
changeEmail,
cleanupAddPartner,
} from "features/partner/partnerSlice";
import { createPartnerAccountAsync } from "features/partner";
import close from "../../assets/images/close.svg";
import progress_activit from "../../assets/images/progress_activit.svg";
import { COUNTRY_LIST } from "../SignupPage/constants";
interface AddPartnerAccountPopup {
isOpen: boolean;
onClose: () => void;
}
export const AddPartnerAccountPopup: React.FC<AddPartnerAccountPopup> = (
props
) => {
const { isOpen, onClose } = props;
const dispatch: AppDispatch = useDispatch();
const { t } = useTranslation();
const {
hasErrorEmptyCompany,
hasErrorEmptyCountry,
hasErrorEmptyAdminName,
hasErrorEmptyEmail,
hasErrorIncorrectEmail,
} = useSelector(selectInputValidationErrors);
const [isPushCreateButton, setIsPushCreateButton] = useState<boolean>(false);
const companyName = useSelector(selectCompany);
const country = useSelector(selectCountry);
const adminName = useSelector(selectAdminName);
const email = useSelector(selectEmail);
const isLoading = useSelector(selectIsLoading);
// ブラウザのウィンドウが閉じられようとしている場合に発火するイベントハンドラ
const handleBeforeUnload = (e: BeforeUnloadEvent) => {
// isLoadingがtrueの場合は確認ダイアログを表示する
if (isLoading) {
e.preventDefault();
// ChromeではreturnValueが必要
e.returnValue = "";
}
};
// コンポーネントがマウントされた時にイベントハンドラを登録する
useEffect(() => {
window.addEventListener("beforeunload", handleBeforeUnload);
// コンポーネントがアンマウントされるときにイベントハンドラを解除する
return () => {
window.removeEventListener("beforeunload", handleBeforeUnload);
};
});
const closePopup = useCallback(() => {
if (isLoading) {
return;
}
setIsPushCreateButton(false);
dispatch(cleanupAddPartner());
onClose();
}, [isLoading, onClose, dispatch]);
const onAddPartner = useCallback(async () => {
setIsPushCreateButton(true);
if (
hasErrorEmptyCompany ||
hasErrorEmptyCountry ||
hasErrorEmptyAdminName ||
hasErrorEmptyEmail ||
hasErrorIncorrectEmail
) {
return;
}
const { meta } = await dispatch(
createPartnerAccountAsync({
companyName,
country,
adminName,
email,
})
);
setIsPushCreateButton(false);
if (meta.requestStatus === "fulfilled") {
closePopup();
}
}, [
dispatch,
closePopup,
hasErrorEmptyCompany,
hasErrorEmptyCountry,
hasErrorEmptyAdminName,
hasErrorEmptyEmail,
hasErrorIncorrectEmail,
companyName,
country,
adminName,
email,
]);
return (
<div className={`${styles.modal} ${isOpen ? styles.isShow : ""}`}>
<div className={styles.modalBox}>
<p className={styles.modalTitle}>
{t(getTranslationID("addPartnerAccountPopupPage.text.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} ${styles.marginBtm0}`}>
{t(
getTranslationID(
"addPartnerAccountPopupPage.text.accountInfoTitle"
)
)}
</dt>
<dt>
{t(getTranslationID("addPartnerAccountPopupPage.label.company"))}
</dt>
<dd>
<input
type="text"
size={64}
className={`${styles.formInput} ${
isPushCreateButton && hasErrorEmptyCompany && styles.isError
}`}
onChange={(e) => {
dispatch(changeCompany({ company: e.target.value }));
}}
value={companyName}
/>
{isPushCreateButton && hasErrorEmptyCompany && (
<span className={styles.formError}>
{t(
getTranslationID(
"addPartnerAccountPopupPage.message.inputEmptyError"
)
)}
</span>
)}
</dd>
<dt>
{t(getTranslationID("addPartnerAccountPopupPage.label.country"))}
</dt>
<dd>
<select
className={`${styles.formInput} ${
isPushCreateButton && hasErrorEmptyCountry && styles.isError
}`}
onChange={(event) => {
dispatch(changeCountry({ country: event.target.value }));
}}
value={
COUNTRY_LIST.find((x) => x.value === country)?.value ?? ""
}
>
{COUNTRY_LIST.map((x) => (
<option key={x.value} value={x.value}>
{x.label}
</option>
))}
</select>
{isPushCreateButton && hasErrorEmptyCountry && (
<span className={styles.formError}>
{t(
getTranslationID(
"addPartnerAccountPopupPage.message.inputEmptyError"
)
)}
</span>
)}
</dd>
<dt className={` ${styles.formTitle} ${styles.marginBtm0}`}>
{t(
getTranslationID(
"addPartnerAccountPopupPage.text.adminInfoTitle"
)
)}
</dt>
<dt>
{t(
getTranslationID("addPartnerAccountPopupPage.label.adminName")
)}
</dt>
<dd>
<input
type="text"
size={256}
className={`${styles.formInput} ${
isPushCreateButton && hasErrorEmptyAdminName && styles.isError
}`}
onChange={(e) => {
dispatch(changeAdminName({ adminName: e.target.value }));
}}
value={adminName}
/>
{isPushCreateButton && hasErrorEmptyAdminName && (
<span className={styles.formError}>
{t(
getTranslationID(
"addPartnerAccountPopupPage.message.inputEmptyError"
)
)}
</span>
)}
</dd>
<dt>
{t(getTranslationID("addPartnerAccountPopupPage.label.email"))}
</dt>
<dd>
<input
type="text"
size={64}
className={`${styles.formInput}
${
isPushCreateButton &&
(hasErrorEmptyEmail || hasErrorIncorrectEmail) &&
styles.isError
}`}
onChange={(e) => {
dispatch(changeEmail({ email: e.target.value }));
}}
value={email}
/>
{isPushCreateButton && hasErrorEmptyEmail && (
<span className={styles.formError}>
{t(
getTranslationID(
"addPartnerAccountPopupPage.message.inputEmptyError"
)
)}
</span>
)}
{isPushCreateButton && hasErrorIncorrectEmail && (
<span className={styles.formError}>
{t(
getTranslationID(
"addPartnerAccountPopupPage.message.emailIncorrectError"
)
)}
</span>
)}
</dd>
<dd className={`${styles.full} ${styles.alignCenter}`}>
<input
type="button"
name="submit"
value={t(
getTranslationID(
"addPartnerAccountPopupPage.label.createAccountButton"
)
)}
className={`${styles.formSubmit} ${styles.marginBtm1} ${
!isLoading ? styles.isActive : ""
}`}
onClick={onAddPartner}
/>
<img
style={{ display: isLoading ? "inline" : "none" }}
src={progress_activit}
className={styles.icLoading}
alt="Loading"
/>
</dd>
</dl>
</form>
</div>
</div>
);
};

View File

@ -4,19 +4,21 @@ 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 React, { useCallback, useState } from "react";
import { useDispatch } from "react-redux";
import styles from "styles/app.module.scss";
import { loadAccessToken, isApproveTier } from "features/auth/utils";
import postAdd from "../../assets/images/post_add.svg";
import { decodeToken } from "../../common/decodeToken";
import { TIERS } from "../../components/auth/constants";
import { AddPartnerAccountPopup } from "./addPartnerAccountPopup";
const PartnerPage: React.FC = (): JSX.Element => {
const { instance } = useMsal();
const dispatch: AppDispatch = useDispatch();
const [isPopupOpen, setIsPopupOpen] = useState(false);
/*
/* XXX
POデモ時に階層情報を表示するための実装です */
const getUserTier = () => {
const jwt = loadAccessToken(); // トークンを取得
@ -29,64 +31,73 @@ const PartnerPage: React.FC = (): JSX.Element => {
return "error!"; // 階層情報が見つからない場合はerror!を返す
};
/*
/* XXX
*/
const userTier = getUserTier();
// 第13階層にボタンを表示する
const isVisible = isApproveTier([TIERS.TIER1, TIERS.TIER2, TIERS.TIER3]);
const onOpen = useCallback(() => {
setIsPopupOpen(true);
}, [setIsPopupOpen]);
// HTML
return (
<div className={styles.wrap}>
<Header userName="XXXXXX" />
<UpdateTokenTimer />
<main className={styles.main}>
<ul className={styles.menuAction}>
<li>
{/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */}
{isVisible && (
<a
className={`${styles.menuLink} ${styles.isActive}`}
// onClick={}
>
<img src={postAdd} alt="" className={styles.menuIcon} />
Add Account
</a>
)}
</li>
</ul>
<form className={styles.form}>
<dl className={`${styles.formList} ${styles.hasbg}`}>
<dt className={styles.formTitle} />
<dt />
<dd className="">
<input
type="text"
size={40}
name=""
value={`Tier:${userTier}`}
maxLength={20}
className={styles.formInput}
/>
</dd>
</dl>
</form>
</main>
<div>
<button
type="button"
className={styles.buttonText}
onClick={() => {
instance.logout({ postLogoutRedirectUri: "/" });
dispatch(clearToken());
}}
>
sign out
</button>
<>
<AddPartnerAccountPopup
isOpen={isPopupOpen}
onClose={() => {
setIsPopupOpen(false);
}}
/>
<div className={styles.wrap}>
<Header userName="XXXXXX" />
<UpdateTokenTimer />
<main className={styles.main}>
<ul className={styles.menuAction}>
<li>
{isVisible && (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<a
className={`${styles.menuLink} ${styles.isActive}`}
onClick={onOpen}
>
<img src={postAdd} alt="" className={styles.menuIcon} />
Add Account
</a>
)}
</li>
</ul>
<form className={styles.form}>
<dl className={`${styles.formList} ${styles.hasbg}`}>
<dt className={styles.formTitle} />
<dt />
<dd className="">
<input
type="text"
size={40}
name=""
value={`Tier:${userTier}`}
maxLength={20}
className={styles.formInput}
/>
</dd>
</dl>
</form>
</main>
<div>
<button
type="button"
className={styles.buttonText}
onClick={() => {
instance.logout({ postLogoutRedirectUri: "/" });
dispatch(clearToken());
}}
>
sign out
</button>
</div>
<Footer />
</div>
<Footer />
</div>
</>
);
};
export default PartnerPage;

View File

@ -250,5 +250,26 @@
"LicenseKeyNotExistError": "(de)入力されたライセンスキーは存在しません。ライセンスキーを再度お確かめください。",
"LicenseKeyAlreadyActivatedError": "(de)入力されたライセンスキーは、既に有効化されています。ライセンスキーを再度お確かめください。"
}
},
"addPartnerAccountPopupPage": {
"message": {
"addAccountSuccess": "(de)メールアドレス宛に認証用メールを送信しました。",
"inputEmptyError": "(de)この項目の入力は必須です。入力してください。",
"emailIncorrectError": "(de)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"emailConflictError": "(de)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
},
"text": {
"title": "(de)Add account",
"accountInfoTitle": "(de)Register account information",
"countryExplanation": "(de)Please select country or the nearest country.",
"adminInfoTitle": "(de)Register primary administrator's information"
},
"label": {
"company": "(de)Company Name",
"country": "(de)Country",
"adminName": "(de)Admin Name",
"email": "(de)Email",
"createAccountButton": "(de)Add account"
}
}
}

View File

@ -250,5 +250,26 @@
"LicenseKeyNotExistError": "入力されたライセンスキーは存在しません。ライセンスキーを再度お確かめください。",
"LicenseKeyAlreadyActivatedError": "入力されたライセンスキーは、既に有効化されています。ライセンスキーを再度お確かめください。"
}
},
"addPartnerAccountPopupPage": {
"message": {
"addAccountSuccess": "メールアドレス宛に認証用メールを送信しました。",
"inputEmptyError": "この項目の入力は必須です。入力してください。",
"emailIncorrectError": "メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"emailConflictError": "このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
},
"text": {
"title": "Add account",
"accountInfoTitle": "Register account information",
"countryExplanation": "Please select country or the nearest country.",
"adminInfoTitle": "Register primary administrator's information"
},
"label": {
"company": "Company Name",
"country": "Country",
"adminName": "Admin Name",
"email": "Email",
"createAccountButton": "Add account"
}
}
}

View File

@ -250,5 +250,26 @@
"LicenseKeyNotExistError": "(es)入力されたライセンスキーは存在しません。ライセンスキーを再度お確かめください。",
"LicenseKeyAlreadyActivatedError": "(es)入力されたライセンスキーは、既に有効化されています。ライセンスキーを再度お確かめください。"
}
},
"addPartnerAccountPopupPage": {
"message": {
"addAccountSuccess": "(es)メールアドレス宛に認証用メールを送信しました。",
"inputEmptyError": "(es)この項目の入力は必須です。入力してください。",
"emailIncorrectError": "(es)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"emailConflictError": "(es)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
},
"text": {
"title": "(es)Add account",
"accountInfoTitle": "(es)Register account information",
"countryExplanation": "(es)Please select country or the nearest country.",
"adminInfoTitle": "(es)Register primary administrator's information"
},
"label": {
"company": "(es)Company Name",
"country": "(es)Country",
"adminName": "(es)Admin Name",
"email": "(es)Email",
"createAccountButton": "(es)Add account"
}
}
}

View File

@ -250,5 +250,26 @@
"LicenseKeyNotExistError": "(fr)入力されたライセンスキーは存在しません。ライセンスキーを再度お確かめください。",
"LicenseKeyAlreadyActivatedError": "(fr)入力されたライセンスキーは、既に有効化されています。ライセンスキーを再度お確かめください。"
}
},
"addPartnerAccountPopupPage": {
"message": {
"addAccountSuccess": "(fr)メールアドレス宛に認証用メールを送信しました。",
"inputEmptyError": "(fr)この項目の入力は必須です。入力してください。",
"emailIncorrectError": "(fr)メールアドレスの形式が不正です。正しいメールアドレスの形式で入力してください。",
"emailConflictError": "(fr)このメールアドレスは既に登録されています。他のメールアドレスで登録してください。"
},
"text": {
"title": "(fr)Add account",
"accountInfoTitle": "(fr)Register account information",
"countryExplanation": "(fr)Please select country or the nearest country.",
"adminInfoTitle": "(fr)Register primary administrator's information"
},
"label": {
"company": "(fr)Company Name",
"country": "(fr)Country",
"adminName": "(fr)Admin Name",
"email": "(fr)Email",
"createAccountButton": "(fr)Add account"
}
}
}