diff --git a/dictation_client/openapitools.json b/dictation_client/openapitools.json index 0436938..a3883a3 100644 --- a/dictation_client/openapitools.json +++ b/dictation_client/openapitools.json @@ -2,6 +2,6 @@ "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json", "spaces": 2, "generator-cli": { - "version": "6.4.0" + "version": "6.5.0" } } diff --git a/dictation_client/src/AppRouter.tsx b/dictation_client/src/AppRouter.tsx index 80d741f..f02d082 100644 --- a/dictation_client/src/AppRouter.tsx +++ b/dictation_client/src/AppRouter.tsx @@ -6,6 +6,10 @@ import { AuthErrorPage } from "pages/ErrorPage"; import { NotFoundPage } from "pages/ErrorPage/notFound"; import { RouteAuthGuard } from "components/auth/routeAuthGuard"; import SignupPage from "pages/SignupPage"; +import VerifyPage from "pages/VerifyPage"; +import VerifySuccessPage from "pages/VerifySuccessPage"; +import VerifyFailedPage from "pages/VerifyFailedPage"; +import VerifyAlreadyExistPage from "pages/VerifyAlreadyExistPage"; import SignupCompletePage from "pages/SignupCompletePage"; const AppRouter: React.FC = () => ( @@ -18,6 +22,13 @@ const AppRouter: React.FC = () => ( element={} /> } /> + } /> + } /> + } /> + } + /> } />} diff --git a/dictation_client/src/api/.openapi-generator/VERSION b/dictation_client/src/api/.openapi-generator/VERSION index c0be8a7..4be2c72 100644 --- a/dictation_client/src/api/.openapi-generator/VERSION +++ b/dictation_client/src/api/.openapi-generator/VERSION @@ -1 +1 @@ -6.4.0 \ No newline at end of file +6.5.0 \ No newline at end of file diff --git a/dictation_client/src/api/api.ts b/dictation_client/src/api/api.ts index 65b9f0d..5235781 100644 --- a/dictation_client/src/api/api.ts +++ b/dictation_client/src/api/api.ts @@ -123,6 +123,25 @@ export interface ErrorResponse { */ 'code': string; } +/** + * + * @export + * @interface RegisterRequest + */ +export interface RegisterRequest { + /** + * wns or apns + * @type {string} + * @memberof RegisterRequest + */ + 'pns': string; + /** + * wnsのチャネルURI or apnsのデバイストークン + * @type {string} + * @memberof RegisterRequest + */ + 'handler': string; +} /** * * @export @@ -186,17 +205,17 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat baseOptions = configuration.baseOptions; } - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; - + localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; localVarRequestOptions.data = serializeDataIfNeeded(createAccountRequest, localVarRequestOptions, configuration) return { @@ -211,7 +230,7 @@ export const AccountsApiAxiosParamCreator = function (configuration?: Configurat * AccountsApi - functional programming interface * @export */ -export const AccountsApiFp = function(configuration?: Configuration) { +export const AccountsApiFp = function (configuration?: Configuration) { const localVarAxiosParamCreator = AccountsApiAxiosParamCreator(configuration) return { /** @@ -290,7 +309,7 @@ export const AuthApiAxiosParamCreator = function (configuration?: Configuration) baseOptions = configuration.baseOptions; } - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; @@ -299,10 +318,10 @@ export const AuthApiAxiosParamCreator = function (configuration?: Configuration) await setBearerAuthToObject(localVarHeaderParameter, configuration) - + setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; return { url: toPathString(localVarUrlObj), @@ -327,17 +346,17 @@ export const AuthApiAxiosParamCreator = function (configuration?: Configuration) baseOptions = configuration.baseOptions; } - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; - + localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; localVarRequestOptions.data = serializeDataIfNeeded(tokenRequest, localVarRequestOptions, configuration) return { @@ -352,7 +371,7 @@ export const AuthApiAxiosParamCreator = function (configuration?: Configuration) * AuthApi - functional programming interface * @export */ -export const AuthApiFp = function(configuration?: Configuration) { +export const AuthApiFp = function (configuration?: Configuration) { const localVarAxiosParamCreator = AuthApiAxiosParamCreator(configuration) return { /** @@ -461,15 +480,15 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati baseOptions = configuration.baseOptions; } - const localVarRequestOptions = { method: 'GET', ...baseOptions, ...options}; + 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}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; return { url: toPathString(localVarUrlObj), @@ -483,7 +502,7 @@ export const DefaultApiAxiosParamCreator = function (configuration?: Configurati * DefaultApi - functional programming interface * @export */ -export const DefaultApiFp = function(configuration?: Configuration) { +export const DefaultApiFp = function (configuration?: Configuration) { const localVarAxiosParamCreator = DefaultApiAxiosParamCreator(configuration) return { /** @@ -538,6 +557,117 @@ export class DefaultApi extends BaseAPI { } +/** + * NotificationApi - axios parameter creator + * @export + */ +export const NotificationApiAxiosParamCreator = function (configuration?: Configuration) { + return { + /** + * + * @summary + * @param {RegisterRequest} registerRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + register: async (registerRequest: RegisterRequest, options: AxiosRequestConfig = {}): Promise => { + // verify required parameter 'registerRequest' is not null or undefined + assertParamExists('register', 'registerRequest', registerRequest) + const localVarPath = `/notification/register`; + // 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(registerRequest, localVarRequestOptions, configuration) + + return { + url: toPathString(localVarUrlObj), + options: localVarRequestOptions, + }; + }, + } +}; + +/** + * NotificationApi - functional programming interface + * @export + */ +export const NotificationApiFp = function (configuration?: Configuration) { + const localVarAxiosParamCreator = NotificationApiAxiosParamCreator(configuration) + return { + /** + * + * @summary + * @param {RegisterRequest} registerRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + async register(registerRequest: RegisterRequest, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise> { + const localVarAxiosArgs = await localVarAxiosParamCreator.register(registerRequest, options); + return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration); + }, + } +}; + +/** + * NotificationApi - factory interface + * @export + */ +export const NotificationApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) { + const localVarFp = NotificationApiFp(configuration) + return { + /** + * + * @summary + * @param {RegisterRequest} registerRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + */ + register(registerRequest: RegisterRequest, options?: any): AxiosPromise { + return localVarFp.register(registerRequest, options).then((request) => request(axios, basePath)); + }, + }; +}; + +/** + * NotificationApi - object-oriented interface + * @export + * @class NotificationApi + * @extends {BaseAPI} + */ +export class NotificationApi extends BaseAPI { + /** + * + * @summary + * @param {RegisterRequest} registerRequest + * @param {*} [options] Override http request option. + * @throws {RequiredError} + * @memberof NotificationApi + */ + public register(registerRequest: RegisterRequest, options?: AxiosRequestConfig) { + return NotificationApiFp(this.configuration).register(registerRequest, options).then((request) => request(this.axios, this.basePath)); + } +} + + /** * UsersApi - axios parameter creator * @export @@ -562,17 +692,17 @@ export const UsersApiAxiosParamCreator = function (configuration?: Configuration baseOptions = configuration.baseOptions; } - const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options}; + const localVarRequestOptions = { method: 'POST', ...baseOptions, ...options }; const localVarHeaderParameter = {} as any; const localVarQueryParameter = {} as any; - + localVarHeaderParameter['Content-Type'] = 'application/json'; setSearchParams(localVarUrlObj, localVarQueryParameter); let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {}; - localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers}; + localVarRequestOptions.headers = { ...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers }; localVarRequestOptions.data = serializeDataIfNeeded(confirmRequest, localVarRequestOptions, configuration) return { @@ -587,7 +717,7 @@ export const UsersApiAxiosParamCreator = function (configuration?: Configuration * UsersApi - functional programming interface * @export */ -export const UsersApiFp = function(configuration?: Configuration) { +export const UsersApiFp = function (configuration?: Configuration) { const localVarAxiosParamCreator = UsersApiAxiosParamCreator(configuration) return { /** diff --git a/dictation_client/src/app/store.ts b/dictation_client/src/app/store.ts index f26ad7b..190d18c 100644 --- a/dictation_client/src/app/store.ts +++ b/dictation_client/src/app/store.ts @@ -2,12 +2,14 @@ import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"; import login from "features/login/loginSlice"; import auth from "features/auth/authSlice"; import signup from "features/signup/signupSlice"; +import verify from "features/verify/verifySlice"; export const store = configureStore({ reducer: { login, auth, signup, + verify, }, }); diff --git a/dictation_client/src/assets/images/report.svg b/dictation_client/src/assets/images/report.svg new file mode 100644 index 0000000..e7890c4 --- /dev/null +++ b/dictation_client/src/assets/images/report.svg @@ -0,0 +1,12 @@ + + + + + + diff --git a/dictation_client/src/common/errors/code.ts b/dictation_client/src/common/errors/code.ts new file mode 100644 index 0000000..bb24063 --- /dev/null +++ b/dictation_client/src/common/errors/code.ts @@ -0,0 +1,25 @@ +/* +エラーコード作成方針 +E+6桁(数字)で構成する。 +- 1~2桁目の値は種類(業務エラー、システムエラー...) +- 3~4桁目の値は原因箇所(トークン、DB、...) +- 5~6桁目の値は任意の重複しない値 +ex) +E00XXXX : システムエラー(通信エラーやDB接続失敗など) +E01XXXX : 業務エラー +EXX00XX : 内部エラー(内部プログラムのエラー) +EXX01XX : トークンエラー(トークン認証関連) +EXX02XX : DBエラー(DB関連) +EXX03XX : ADB2Cエラー(DB関連) +*/ +export const errorCodes = [ + "E009999", // 汎用エラー + "E000101", // トークン形式不正エラー + "E000102", // トークン有効期限切れエラー + "E000103", // トークン非アクティブエラー + "E000104", // トークン署名エラー + "E000105", // トークン発行元エラー + "E000106", // トークンアルゴリズムエラー + "E010201", // 未認証ユーザエラー + "E010301", // メールアドレス登録済みエラー +] as const; diff --git a/dictation_client/src/common/errors/index.ts b/dictation_client/src/common/errors/index.ts new file mode 100644 index 0000000..1543e4c --- /dev/null +++ b/dictation_client/src/common/errors/index.ts @@ -0,0 +1,3 @@ +export * from "./code"; +export * from "./types"; +export * from "./utils"; diff --git a/dictation_client/src/common/errors/types.ts b/dictation_client/src/common/errors/types.ts new file mode 100644 index 0000000..8cc801e --- /dev/null +++ b/dictation_client/src/common/errors/types.ts @@ -0,0 +1,9 @@ +import { errorCodes } from "./code"; + +export type ErrorObject = { + message: string; + code: ErrorCodeType; + statusCode?: number; +}; + +export type ErrorCodeType = typeof errorCodes[number]; diff --git a/dictation_client/src/common/errors/utils.ts b/dictation_client/src/common/errors/utils.ts new file mode 100644 index 0000000..8f756ca --- /dev/null +++ b/dictation_client/src/common/errors/utils.ts @@ -0,0 +1,83 @@ +import { AxiosError } from "axios"; +import { isError } from "lodash"; +import { ErrorResponse } from "../../api"; +import { errorCodes } from "./code"; +import { ErrorCodeType, ErrorObject } from "./types"; + +export const createErrorObject = (error: unknown): ErrorObject => { + // 最低限通常のエラーかを判定 + // Error以外のものがthrowされた場合 + // 基本的にないはずだがプログラム上あるので拾う + if (!isError(error)) { + return { + message: "not error type.", + code: "E009999", + }; + } + + // Axiosエラー 通信してのエラーであるかを判定 + if (!isAxiosError(error)) { + return { + message: "not axios error.", + code: "E009999", + }; + } + + const errorResponse = error.response; + if (!errorResponse) { + return { + message: error.message, + code: "E009999", + statusCode: errorResponse, + }; + } + + const { data } = errorResponse; + + // 想定しているエラーレスポンスの型か判定 + if (!isErrorResponse(data)) { + return { + message: error.message, + code: "E009999", + statusCode: errorResponse.status, + }; + } + + const { message, code } = data; + + // 想定しているエラーコードかを判定 + if (!isErrorCode(code)) { + return { + message, + code: "E009999", + statusCode: errorResponse.status, + }; + } + + return { + message, + code, + statusCode: errorResponse.status, + }; +}; + +const isAxiosError = (e: unknown): e is AxiosError => { + const error = e as AxiosError; + return error?.isAxiosError ?? false; +}; + +const isErrorResponse = (error: unknown): error is ErrorResponse => { + const errorResponse = error as ErrorResponse; + if ( + errorResponse === undefined || + errorResponse.message === undefined || + errorResponse.code === undefined + ) { + return false; + } + + return true; +}; + +const isErrorCode = (errorCode: string): errorCode is ErrorCodeType => + errorCodes.includes(errorCode as ErrorCodeType); diff --git a/dictation_client/src/features/verify/index.ts b/dictation_client/src/features/verify/index.ts new file mode 100644 index 0000000..93fa8f3 --- /dev/null +++ b/dictation_client/src/features/verify/index.ts @@ -0,0 +1,4 @@ +export * from "./verifySlice"; +export * from "./state"; +export * from "./operations"; +export * from "./selectors"; diff --git a/dictation_client/src/features/verify/operations.ts b/dictation_client/src/features/verify/operations.ts new file mode 100644 index 0000000..8ccc42d --- /dev/null +++ b/dictation_client/src/features/verify/operations.ts @@ -0,0 +1,38 @@ +import { createAsyncThunk } from "@reduxjs/toolkit"; +import type { RootState } from "app/store"; +import { UsersApi } from "../../api/api"; +import { Configuration } from "../../api/configuration"; +import { ErrorObject, createErrorObject } from "../../common/errors"; + +export const verifyAsync = createAsyncThunk< + { + /* Empty Object */ + }, + { + jwt: string; + }, + { + // rejectした時の返却値の型 + rejectValue: { + error: ErrorObject; + }; + } +>("verify/verifyAsync", async (args, thunkApi) => { + const { jwt } = args; + // apiのConfigurationを取得する + const { getState } = thunkApi; + const state = getState() as RootState; + const { configuration } = state.auth; + const config = new Configuration(configuration); + const usersApi = new UsersApi(config); + + try { + await usersApi.confirmUser({ token: jwt }); + return {}; + } catch (e) { + // e ⇒ errorObjectに変換 + const error = createErrorObject(e); + + return thunkApi.rejectWithValue({ error }); + } +}); diff --git a/dictation_client/src/features/verify/selectors.ts b/dictation_client/src/features/verify/selectors.ts new file mode 100644 index 0000000..318533d --- /dev/null +++ b/dictation_client/src/features/verify/selectors.ts @@ -0,0 +1,6 @@ +import { RootState } from "app/store"; + +export const VerifyStateSelector = ( + state: RootState +): "duringVerify" | "success" | "alreadySuccess" | "failed" => + state.verify.apps.VerifyState; diff --git a/dictation_client/src/features/verify/state.ts b/dictation_client/src/features/verify/state.ts new file mode 100644 index 0000000..747fe4b --- /dev/null +++ b/dictation_client/src/features/verify/state.ts @@ -0,0 +1,7 @@ +export interface VerifyState { + apps: Apps; +} + +export interface Apps { + VerifyState: "duringVerify" | "success" | "alreadySuccess" | "failed"; +} diff --git a/dictation_client/src/features/verify/verifySlice.ts b/dictation_client/src/features/verify/verifySlice.ts new file mode 100644 index 0000000..6dfe7af --- /dev/null +++ b/dictation_client/src/features/verify/verifySlice.ts @@ -0,0 +1,35 @@ +import { createSlice } from "@reduxjs/toolkit"; +import { VerifyState } from "./state"; +import { verifyAsync } from "./operations"; + +const initialState: VerifyState = { + apps: { + VerifyState: "duringVerify", + }, +}; + +export const verifySlice = createSlice({ + name: "verify", + initialState, + reducers: {}, + extraReducers: (builder) => { + builder.addCase(verifyAsync.pending, (state) => { + state.apps.VerifyState = "duringVerify"; + }); + builder.addCase(verifyAsync.fulfilled, (state) => { + state.apps.VerifyState = "success"; + }); + builder.addCase(verifyAsync.rejected, (state, action) => { + const { payload } = action; + + // メール認証済みかをエラーコードから判定 + if (payload?.error.code === "E010301") { + state.apps.VerifyState = "alreadySuccess"; + } else { + state.apps.VerifyState = "failed"; + } + }); + }, +}); + +export default verifySlice.reducer; diff --git a/dictation_client/src/pages/SignupPage/signupInput.tsx b/dictation_client/src/pages/SignupPage/signupInput.tsx index e3231c3..59a2203 100644 --- a/dictation_client/src/pages/SignupPage/signupInput.tsx +++ b/dictation_client/src/pages/SignupPage/signupInput.tsx @@ -246,7 +246,13 @@ const SignupInput: React.FC = (): JSX.Element => { )} )} - + {/** XXX: 改行に対応するためにここでstyleを当てている。 + * デザインを修正していただいたらもとに戻す + */} + {t(getTranslationID("signupPage.text.passwordTerms"))} diff --git a/dictation_client/src/pages/VerifyAlreadyExistPage/index.tsx b/dictation_client/src/pages/VerifyAlreadyExistPage/index.tsx new file mode 100644 index 0000000..9c194f7 --- /dev/null +++ b/dictation_client/src/pages/VerifyAlreadyExistPage/index.tsx @@ -0,0 +1,51 @@ +import Footer from "components/footer"; +import Header from "components/header"; +import styles from "styles/app.module.scss"; +import { useTranslation } from "react-i18next"; +import { getTranslationID } from "translation"; +import check_circle from "../../assets/images/check_circle.svg"; + +const VerifyPage: React.FC = (): JSX.Element => { + const [t] = useTranslation(); + + return ( +
+
+ +
+
+
+

+ {t(getTranslationID("signupVerifyPage.label.alreadySuccess"))} +

+
+ +
+
+
+
+ chedk +
+
+ {t(getTranslationID("signupVerifyPage.text.alreadySuccess"))} +
+
+ + {t(getTranslationID("signupVerifyPage.label.returnToSignIn"))} + +
+
+
+
+
+ +
+
+ ); +}; + +export default VerifyPage; diff --git a/dictation_client/src/pages/VerifyFailedPage/index.tsx b/dictation_client/src/pages/VerifyFailedPage/index.tsx new file mode 100644 index 0000000..ddbc089 --- /dev/null +++ b/dictation_client/src/pages/VerifyFailedPage/index.tsx @@ -0,0 +1,53 @@ +import React, { useEffect } from "react"; +import Header from "components/header"; +import Footer from "components/footer"; +import styles from "styles/app.module.scss"; +import { useTranslation } from "react-i18next"; +import { getTranslationID } from "translation"; +import report from "../../assets/images/report.svg"; + +const VerifyFailedPage: React.FC = (): JSX.Element => { + const [t] = useTranslation(); + + useEffect(() => { + // 初期表示時の処理を記述 + }, []); + + return ( +
+
+ +
+
+
+

+ {t(getTranslationID("signupVerifyPage.label.faild"))} +

+
+ +
+
+
+
+ chedk +
+ {/** XXX: 改行に対応するためにここでstyleを当てている。 + * デザインを修正していただいたらもとに戻す + */} +
+ {t(getTranslationID("signupVerifyPage.text.faild"))} +
+
+
+
+
+ +
+
+ ); +}; + +export default VerifyFailedPage; diff --git a/dictation_client/src/pages/VerifyPage/index.tsx b/dictation_client/src/pages/VerifyPage/index.tsx new file mode 100644 index 0000000..6087056 --- /dev/null +++ b/dictation_client/src/pages/VerifyPage/index.tsx @@ -0,0 +1,53 @@ +import { AppDispatch } from "app/store"; +import Footer from "components/footer"; +import Header from "components/header"; +import React, { useEffect } from "react"; +import { useDispatch, useSelector } from "react-redux"; +import { useLocation, useNavigate } from "react-router-dom"; +import { verifyAsync } from "features/verify"; +import { VerifyStateSelector } from "features/verify/selectors"; + +const VerifyPage: React.FC = (): JSX.Element => { + const dispatch: AppDispatch = useDispatch(); + const navigate = useNavigate(); + + const { search } = useLocation(); + const query = new URLSearchParams(search); + const jwt = query.get("verify") ?? ""; + + useEffect(() => { + dispatch(verifyAsync({ jwt })); + }, [dispatch, jwt]); + + const verifystate = useSelector(VerifyStateSelector); + + useEffect(() => { + switch (verifystate) { + case "duringVerify": + // 認証中は処理なし + break; + case "success": + navigate("/mail-confirm/success"); + break; + case "alreadySuccess": + navigate("/mail-confirm/alreadyExist"); + break; + case "failed": + navigate("/mail-confirm/failed"); + break; + default: + // verifystateが列挙型のため到達しない + break; + } + }, [verifystate, navigate]); + + return ( + <> +
+

loading ...

+