diff --git a/dictation_client/src/api/api.ts b/dictation_client/src/api/api.ts index 180551e..36c518b 100644 --- a/dictation_client/src/api/api.ts +++ b/dictation_client/src/api/api.ts @@ -373,6 +373,31 @@ export interface CreatePartnerAccountRequest { */ 'email': string; } +/** + * + * @export + * @interface Dealer + */ +export interface Dealer { + /** + * アカウントID + * @type {number} + * @memberof Dealer + */ + 'id': number; + /** + * 会社名 + * @type {string} + * @memberof Dealer + */ + 'name': string; + /** + * 国名(ISO 3166-1 alpha-2) + * @type {string} + * @memberof Dealer + */ + 'country': string; +} /** * * @export @@ -392,6 +417,19 @@ export interface ErrorResponse { */ 'code': string; } +/** + * + * @export + * @interface GetDealersResponse + */ +export interface GetDealersResponse { + /** + * + * @type {Array} + * @memberof GetDealersResponse + */ + 'dealers': Array; +} /** * * @export @@ -1504,6 +1542,36 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat options: localVarRequestOptions, }; }, + /** + * + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getDealers: async (options: AxiosRequestConfig = {}): Promise => { + const localVarPath = `/accounts/dealers`; + // 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: 'GET', ...baseOptions, ...options}; + const localVarHeaderParameter = {} as any; + const localVarQueryParameter = {} as any; + + + + setSearchParams(localVarUrlObj, localVarQueryParameter); + let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; + localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, /** * 指定したアカウントのライセンス集計情報を取得します * @summary @@ -1798,6 +1866,16 @@ export const AccountsApiFp = function(configuration?: Configuration) { const localVarAxiosArgs = await localVarAxiosParamCreator.createPartnerAccount(createPartnerAccountRequest, options); return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); }, + /** + * + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async getDealers(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.getDealers(options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, /** * 指定したアカウントのライセンス集計情報を取得します * @summary @@ -1902,6 +1980,15 @@ export const AccountsApiFactory = function (configuration?: Configuration, baseP createPartnerAccount(createPartnerAccountRequest: CreatePartnerAccountRequest, options?: any): AxiosPromise { return localVarFp.createPartnerAccount(createPartnerAccountRequest, options).then((request) => request(axios, basePath)); }, + /** + * + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + getDealers(options?: any): AxiosPromise { + return localVarFp.getDealers(options).then((request) => request(axios, basePath)); + }, /** * 指定したアカウントのライセンス集計情報を取得します * @summary @@ -2003,6 +2090,17 @@ export class AccountsApi extends BaseAPI { return AccountsApiFp(this.configuration).createPartnerAccount(createPartnerAccountRequest, options).then((request) => request(this.axios, this.basePath)); } + /** + * + * @summary + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof AccountsApi + */ + public getDealers(options?: AxiosRequestConfig) { + return AccountsApiFp(this.configuration).getDealers(options).then((request) => request(this.axios, this.basePath)); + } + /** * 指定したアカウントのライセンス集計情報を取得します * @summary diff --git a/dictation_client/src/features/signup/operations.ts b/dictation_client/src/features/signup/operations.ts index dce701a..67e505e 100644 --- a/dictation_client/src/features/signup/operations.ts +++ b/dictation_client/src/features/signup/operations.ts @@ -3,7 +3,11 @@ import type { RootState } from "app/store"; import { ErrorObject, createErrorObject } from "common/errors"; import { getTranslationID } from "translation"; import { closeSnackbar, openSnackbar } from "features/ui/uiSlice"; -import { AccountsApi, CreateAccountRequest } from "../../api/api"; +import { + AccountsApi, + CreateAccountRequest, + GetDealersResponse, +} from "../../api/api"; import { Configuration } from "../../api/configuration"; export const signupAsync = createAsyncThunk< @@ -56,3 +60,36 @@ export const signupAsync = createAsyncThunk< return thunkApi.rejectWithValue({ error }); } }); + +export const getDealersAsync = createAsyncThunk< + GetDealersResponse, + void, + { + // rejectした時の返却値の型 + rejectValue: { + error: ErrorObject; + }; + } +>("login/getDealersAsync", async (args, thunkApi) => { + // apiのConfigurationを取得する + const { getState } = thunkApi; + const state = getState() as RootState; + const { configuration } = state.auth; + const config = new Configuration(configuration); + const accountApi = new AccountsApi(config); + + try { + const res = await accountApi.getDealers(); + return res.data; + } catch (e) { + const error = createErrorObject(e); + thunkApi.dispatch( + openSnackbar({ + level: "error", + message: getTranslationID("common.message.internalServerError"), + }) + ); + + return thunkApi.rejectWithValue({ error }); + } +}); diff --git a/dictation_client/src/features/signup/selectors.ts b/dictation_client/src/features/signup/selectors.ts index a6625d3..87c3118 100644 --- a/dictation_client/src/features/signup/selectors.ts +++ b/dictation_client/src/features/signup/selectors.ts @@ -1,3 +1,4 @@ +import { Dealer } from "api/api"; import { RootState } from "app/store"; export const selectInputValidationErrors = (state: RootState) => { @@ -56,3 +57,18 @@ export const selectPassword = (state: RootState) => state.signup.apps.password; export const selectPageState = (state: RootState) => state.signup.apps.pageState; + +export const selectAllDealers = (state: RootState) => + state.signup.domain.dealers; + +export const selectSameCountryDealers = (state: RootState) => { + const { dealers } = state.signup.domain; + const { country } = state.signup.apps; + return dealers.filter((x: Dealer) => x.country === country); +}; + +export const selectSelectedDealer = (state: RootState) => { + const { dealers } = state.signup.domain; + const { dealer } = state.signup.apps; + return dealers.find((x: Dealer) => x.id === dealer); +}; diff --git a/dictation_client/src/features/signup/signupSlice.ts b/dictation_client/src/features/signup/signupSlice.ts index d75f385..5b03e63 100644 --- a/dictation_client/src/features/signup/signupSlice.ts +++ b/dictation_client/src/features/signup/signupSlice.ts @@ -1,16 +1,20 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { SignupState } from "./state"; -import { signupAsync } from "./operations"; +import { getDealersAsync, signupAsync } from "./operations"; const initialState: SignupState = { apps: { pageState: "input", company: "", country: "", - dealer: "", + dealer: undefined, adminName: "", email: "", password: "", + dealers: [], + }, + domain: { + dealers: [], }, }; @@ -32,10 +36,11 @@ export const signupSlice = createSlice({ changeCountry: (state, action: PayloadAction<{ country: string }>) => { const { country } = action.payload; state.apps.country = country; + state.apps.dealer = undefined; }, - changeDealer: (state, action: PayloadAction<{ dealer: string }>) => { + changeDealer: (state, action: PayloadAction<{ dealer: number }>) => { const { dealer } = action.payload; - state.apps.dealer = dealer; + state.apps.dealer = Number.isNaN(dealer) ? undefined : dealer; }, changeAdminName: (state, action: PayloadAction<{ adminName: string }>) => { const { adminName } = action.payload; @@ -60,6 +65,15 @@ export const signupSlice = createSlice({ builder.addCase(signupAsync.rejected, () => { // }); + builder.addCase(getDealersAsync.pending, () => { + // + }); + builder.addCase(getDealersAsync.fulfilled, (state, action) => { + state.domain.dealers = action.payload.dealers; + }); + builder.addCase(getDealersAsync.rejected, () => { + // + }); }, }); export const { diff --git a/dictation_client/src/features/signup/state.ts b/dictation_client/src/features/signup/state.ts index 9d85fdd..164ab04 100644 --- a/dictation_client/src/features/signup/state.ts +++ b/dictation_client/src/features/signup/state.ts @@ -1,13 +1,21 @@ +import { Dealer } from "api/api"; + export interface SignupState { apps: Apps; + domain: Domain; } export interface Apps { pageState: "input" | "confirm" | "complete"; company: string; country: string; - dealer: string; + dealer?: number | undefined; adminName: string; email: string; password: string; + dealers: Dealer[]; +} + +export interface Domain { + dealers: Dealer[]; } diff --git a/dictation_client/src/pages/SignupPage/signupConfirm.tsx b/dictation_client/src/pages/SignupPage/signupConfirm.tsx index 6ffacd0..78e45f7 100644 --- a/dictation_client/src/pages/SignupPage/signupConfirm.tsx +++ b/dictation_client/src/pages/SignupPage/signupConfirm.tsx @@ -13,6 +13,7 @@ import { selectAdminName, selectEmail, selectPassword, + selectSelectedDealer, } from "../../features/signup/selectors"; import { signupAsync } from "../../features/signup/operations"; @@ -25,13 +26,14 @@ const SignupConfirm: React.FC = (): JSX.Element => { const adminName = useSelector(selectAdminName); const adminMail = useSelector(selectEmail); const adminPassword = useSelector(selectPassword); + const dealer = useSelector(selectSelectedDealer); const onSubmit = useCallback(() => { dispatch( signupAsync({ companyName, country, - dealerAccountId: 0, + dealerAccountId: dealer?.id ?? 0, adminName, adminMail, adminPassword, @@ -39,7 +41,15 @@ const SignupConfirm: React.FC = (): JSX.Element => { token: "", }) ); - }, [dispatch, companyName, country, adminName, adminMail, adminPassword]); + }, [ + dispatch, + companyName, + country, + dealer, + adminName, + adminMail, + adminPassword, + ]); return (
@@ -71,7 +81,9 @@ const SignupConfirm: React.FC = (): JSX.Element => {
{t(getTranslationID("signupConfirmPage.label.dealer"))}
-
+
+

{dealer?.name} 

+
{t(getTranslationID("signupConfirmPage.text.adminInfoTitle"))} diff --git a/dictation_client/src/pages/SignupPage/signupInput.tsx b/dictation_client/src/pages/SignupPage/signupInput.tsx index 3a0f751..4579800 100644 --- a/dictation_client/src/pages/SignupPage/signupInput.tsx +++ b/dictation_client/src/pages/SignupPage/signupInput.tsx @@ -1,30 +1,37 @@ import { AppDispatch } from "app/store"; import { selectAdminName, + selectAllDealers, selectCompany, selectCountry, + selectDealer, selectEmail, selectInputValidationErrors, + selectSameCountryDealers, } from "features/signup/selectors"; import { changeAdminName, changeCompany, changeCountry, + changeDealer, changeEmail, changePageState, changePassword, } from "features/signup/signupSlice"; -import React, { useCallback, useState } from "react"; +import React, { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useDispatch, useSelector } from "react-redux"; -import { useNavigate } from "react-router-dom"; +import { useLocation, useNavigate } from "react-router-dom"; import { getTranslationID } from "translation"; import styles from "styles/app.module.scss"; +import { getDealersAsync } from "features/signup/operations"; +import { LANGUAGE_LIST } from "features/top/constants"; import { COUNTRY_LIST } from "./constants"; const SignupInput: React.FC = (): JSX.Element => { const dispatch: AppDispatch = useDispatch(); - const [t] = useTranslation(); + const [t, i18n] = useTranslation(); + const { search } = useLocation(); const navigate = useNavigate(); const [isPasswordHide, setIsPasswordHide] = useState(true); const [isOpenPolicy, setIsOpenPolicy] = useState(false); @@ -39,6 +46,7 @@ const SignupInput: React.FC = (): JSX.Element => { hasErrorIncorrectEmail, hasErrorIncorrectPassword, } = useSelector(selectInputValidationErrors); + const onSubmit = useCallback(() => { if ( hasErrorEmptyAdminName || @@ -63,11 +71,49 @@ const SignupInput: React.FC = (): JSX.Element => { hasErrorIncorrectPassword, ]); + const allDealers = useSelector(selectAllDealers); + const dealers = useSelector(selectSameCountryDealers); + const company = useSelector(selectCompany); const country = useSelector(selectCountry); + const dealer = useSelector(selectDealer); const adminName = useSelector(selectAdminName); const email = useSelector(selectEmail); + // 入力画面の初期化時の処理 + useEffect(() => { + dispatch(getDealersAsync()); + }, [dispatch]); + + useEffect(() => { + // 外部のWebサイトからの遷移時にURLのパラメータを取得 + // 以下のようなURLで遷移してきた場合に、Dealerと言語を変更する + // https://xxx/signup?dealer=1&language=en + // dealer={account_id(第四階層のアカウントID)} でDealerを指定 + // language={language(en/de/fr/es)} で言語を指定 + const query = new URLSearchParams(search); + const dealerId = parseInt(query.get("dealer") ?? "", 10); + const language = query.get("language"); + + // URLで言語が指定されていたら言語を変更 + if (language && LANGUAGE_LIST.map((x) => x.value).includes(language)) { + i18n.changeLanguage(language); + // 既にcookieに選択言語があれば削除 + document.cookie = "language=; max-age=0"; + // cookieの期限は1年 + document.cookie = `language=${language}; max-age=31536000`; + } + + // URLでDealerが指定されていたら、そのdealerを選択(国も選択したDealerの国に変更) + if (!Number.isNaN(dealerId)) { + const urlDealer = allDealers.find((x) => x.id === dealerId); + if (urlDealer) { + dispatch(changeCountry({ country: urlDealer.country })); + dispatch(changeDealer({ dealer: urlDealer.id })); + } + } + }, [i18n, dispatch, search, allDealers]); + return (
@@ -145,9 +191,29 @@ const SignupInput: React.FC = (): JSX.Element => {
{t(getTranslationID("signupPage.label.dealer"))}
- { + dispatch( + changeDealer({ + dealer: parseInt(event.target.value, 10), + }) + ); + }} + value={dealers.find((x) => x.id === dealer)?.id ?? NaN} + > + {[ + { + id: NaN, + country: "", + name: "Select dealer", + }, + ...dealers, + ].map((x) => ( + + ))} {t(getTranslationID("signupPage.text.dealerExplanation"))}