diff --git a/dictation_client/src/api/api.ts b/dictation_client/src/api/api.ts index 1e8b108..c28f007 100644 --- a/dictation_client/src/api/api.ts +++ b/dictation_client/src/api/api.ts @@ -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 => { + // 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> { + 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 { 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 { + 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 diff --git a/dictation_client/src/app/store.ts b/dictation_client/src/app/store.ts index 4cc9a10..632c5bb 100644 --- a/dictation_client/src/app/store.ts +++ b/dictation_client/src/app/store.ts @@ -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, }, }); diff --git a/dictation_client/src/features/partner/index.ts b/dictation_client/src/features/partner/index.ts new file mode 100644 index 0000000..3de17ae --- /dev/null +++ b/dictation_client/src/features/partner/index.ts @@ -0,0 +1,4 @@ +export * from "./state"; +export * from "./operations"; +export * from "./selectors"; +export * from "./partnerSlice"; diff --git a/dictation_client/src/features/partner/operations.ts b/dictation_client/src/features/partner/operations.ts new file mode 100644 index 0000000..abb662b --- /dev/null +++ b/dictation_client/src/features/partner/operations.ts @@ -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 }); + } +}); diff --git a/dictation_client/src/features/partner/partnerSlice.ts b/dictation_client/src/features/partner/partnerSlice.ts new file mode 100644 index 0000000..6f73872 --- /dev/null +++ b/dictation_client/src/features/partner/partnerSlice.ts @@ -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; diff --git a/dictation_client/src/features/partner/selectors.ts b/dictation_client/src/features/partner/selectors.ts new file mode 100644 index 0000000..00d1cda --- /dev/null +++ b/dictation_client/src/features/partner/selectors.ts @@ -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; diff --git a/dictation_client/src/features/partner/state.ts b/dictation_client/src/features/partner/state.ts new file mode 100644 index 0000000..6cc9844 --- /dev/null +++ b/dictation_client/src/features/partner/state.ts @@ -0,0 +1,10 @@ +import { CreatePartnerAccountRequest } from "../../api/api"; + +export interface PartnerState { + apps: Apps; +} + +export interface Apps { + addPartner: CreatePartnerAccountRequest; + isLoading: boolean; +} diff --git a/dictation_client/src/pages/PartnerPage/addPartnerAccountPopup.tsx b/dictation_client/src/pages/PartnerPage/addPartnerAccountPopup.tsx new file mode 100644 index 0000000..565bfe7 --- /dev/null +++ b/dictation_client/src/pages/PartnerPage/addPartnerAccountPopup.tsx @@ -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 = ( + props +) => { + const { isOpen, onClose } = props; + const dispatch: AppDispatch = useDispatch(); + const { t } = useTranslation(); + const { + hasErrorEmptyCompany, + hasErrorEmptyCountry, + hasErrorEmptyAdminName, + hasErrorEmptyEmail, + hasErrorIncorrectEmail, + } = useSelector(selectInputValidationErrors); + + const [isPushCreateButton, setIsPushCreateButton] = useState(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 ( +
+
+

+ {t(getTranslationID("addPartnerAccountPopupPage.text.title"))} + +

+
+
+
+ {t( + getTranslationID( + "addPartnerAccountPopupPage.text.accountInfoTitle" + ) + )} +
+
+ {t(getTranslationID("addPartnerAccountPopupPage.label.company"))} +
+
+ { + dispatch(changeCompany({ company: e.target.value })); + }} + value={companyName} + /> + {isPushCreateButton && hasErrorEmptyCompany && ( + + {t( + getTranslationID( + "addPartnerAccountPopupPage.message.inputEmptyError" + ) + )} + + )} +
+
+ {t(getTranslationID("addPartnerAccountPopupPage.label.country"))} +
+
+ + {isPushCreateButton && hasErrorEmptyCountry && ( + + {t( + getTranslationID( + "addPartnerAccountPopupPage.message.inputEmptyError" + ) + )} + + )} +
+
+ {t( + getTranslationID( + "addPartnerAccountPopupPage.text.adminInfoTitle" + ) + )} +
+
+ {t( + getTranslationID("addPartnerAccountPopupPage.label.adminName") + )} +
+
+ { + dispatch(changeAdminName({ adminName: e.target.value })); + }} + value={adminName} + /> + {isPushCreateButton && hasErrorEmptyAdminName && ( + + {t( + getTranslationID( + "addPartnerAccountPopupPage.message.inputEmptyError" + ) + )} + + )} +
+
+ {t(getTranslationID("addPartnerAccountPopupPage.label.email"))} +
+
+ { + dispatch(changeEmail({ email: e.target.value })); + }} + value={email} + /> + {isPushCreateButton && hasErrorEmptyEmail && ( + + {t( + getTranslationID( + "addPartnerAccountPopupPage.message.inputEmptyError" + ) + )} + + )} + {isPushCreateButton && hasErrorIncorrectEmail && ( + + {t( + getTranslationID( + "addPartnerAccountPopupPage.message.emailIncorrectError" + ) + )} + + )} +
+
+ + Loading +
+
+
+
+
+ ); +}; diff --git a/dictation_client/src/pages/PartnerPage/index.tsx b/dictation_client/src/pages/PartnerPage/index.tsx index 1b81e82..e7efb50 100644 --- a/dictation_client/src/pages/PartnerPage/index.tsx +++ b/dictation_client/src/pages/PartnerPage/index.tsx @@ -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(); // 第1~3階層にボタンを表示する const isVisible = isApproveTier([TIERS.TIER1, TIERS.TIER2, TIERS.TIER3]); - + const onOpen = useCallback(() => { + setIsPopupOpen(true); + }, [setIsPopupOpen]); // HTML return ( -
-
- -
-
    -
  • - {/* eslint-disable-next-line jsx-a11y/click-events-have-key-events,jsx-a11y/no-static-element-interactions */} - {isVisible && ( - - - Add Account - - )} -
  • -
-
-
-
-
-
- -
-
-
-
-
- + <> + { + setIsPopupOpen(false); + }} + /> +
+
+ +
+
    +
  • + {isVisible && ( + // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions + + + Add Account + + )} +
  • +
+
+
+
+
+
+ +
+
+
+
+
+ +
+
-
+ ); }; - export default PartnerPage; diff --git a/dictation_client/src/translation/de.json b/dictation_client/src/translation/de.json index 06e3583..df0acc1 100644 --- a/dictation_client/src/translation/de.json +++ b/dictation_client/src/translation/de.json @@ -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" + } } } diff --git a/dictation_client/src/translation/en.json b/dictation_client/src/translation/en.json index b2b4e35..ee1bcd5 100644 --- a/dictation_client/src/translation/en.json +++ b/dictation_client/src/translation/en.json @@ -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" + } } } diff --git a/dictation_client/src/translation/es.json b/dictation_client/src/translation/es.json index a575daf..73c514a 100644 --- a/dictation_client/src/translation/es.json +++ b/dictation_client/src/translation/es.json @@ -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" + } } } diff --git a/dictation_client/src/translation/fr.json b/dictation_client/src/translation/fr.json index 9a19b52..f2ea336 100644 --- a/dictation_client/src/translation/fr.json +++ b/dictation_client/src/translation/fr.json @@ -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" + } } }