+ {level === "error" ? (
+

+ ) : (
+

+ )}
+
{message}
+ {level === "error" && (
+
+ )}
+
+ );
+};
+
+Snackbar.defaultProps = {
+ duration: 0,
+};
+
+export default Snackbar;
diff --git a/dictation_client/src/features/signup/operations.ts b/dictation_client/src/features/signup/operations.ts
index 109861b..dce701a 100644
--- a/dictation_client/src/features/signup/operations.ts
+++ b/dictation_client/src/features/signup/operations.ts
@@ -1,5 +1,8 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
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 { Configuration } from "../../api/configuration";
@@ -11,7 +14,7 @@ export const signupAsync = createAsyncThunk<
{
// rejectした時の返却値の型
rejectValue: {
- /* Empty Object */
+ error: ErrorObject;
};
}
>("login/signupAsync", async (args, thunkApi) => {
@@ -24,10 +27,32 @@ export const signupAsync = createAsyncThunk<
const accountApi = new AccountsApi(config);
try {
- const { data } = await accountApi.createAccount(createAccountRequest);
+ thunkApi.dispatch(closeSnackbar());
+ await accountApi.createAccount(createAccountRequest);
+
+ thunkApi.dispatch(closeSnackbar());
return {};
} catch (e) {
- return thunkApi.rejectWithValue({});
+ const error = createErrorObject(e);
+ if (error.code === "E010301") {
+ thunkApi.dispatch(
+ openSnackbar({
+ level: "error",
+ message: getTranslationID(
+ "signupConfirmPage.message.emailConflictError"
+ ),
+ })
+ );
+ } else {
+ thunkApi.dispatch(
+ openSnackbar({
+ level: "error",
+ message: getTranslationID("common.message.internalServerError"),
+ })
+ );
+ }
+
+ return thunkApi.rejectWithValue({ error });
}
});
diff --git a/dictation_client/src/features/signup/signupSlice.ts b/dictation_client/src/features/signup/signupSlice.ts
index 390a245..d75f385 100644
--- a/dictation_client/src/features/signup/signupSlice.ts
+++ b/dictation_client/src/features/signup/signupSlice.ts
@@ -51,13 +51,13 @@ export const signupSlice = createSlice({
},
},
extraReducers: (builder) => {
- builder.addCase(signupAsync.pending, (state) => {
+ builder.addCase(signupAsync.pending, () => {
//
});
builder.addCase(signupAsync.fulfilled, (state) => {
state.apps.pageState = "complete";
});
- builder.addCase(signupAsync.rejected, (state) => {
+ builder.addCase(signupAsync.rejected, () => {
//
});
},
diff --git a/dictation_client/src/features/ui/constants.ts b/dictation_client/src/features/ui/constants.ts
new file mode 100644
index 0000000..9374dea
--- /dev/null
+++ b/dictation_client/src/features/ui/constants.ts
@@ -0,0 +1,2 @@
+// 標準のスナックバー表示時間(ミリ秒)
+export const DEFAULT_SNACKBAR_DURATION = 3000;
diff --git a/dictation_client/src/features/ui/index.ts b/dictation_client/src/features/ui/index.ts
new file mode 100644
index 0000000..e1354d4
--- /dev/null
+++ b/dictation_client/src/features/ui/index.ts
@@ -0,0 +1,4 @@
+export * from "./constants";
+export * from "./selectors";
+export * from "./state";
+export * from "./uiSlice";
diff --git a/dictation_client/src/features/ui/selectors.ts b/dictation_client/src/features/ui/selectors.ts
new file mode 100644
index 0000000..4cb1543
--- /dev/null
+++ b/dictation_client/src/features/ui/selectors.ts
@@ -0,0 +1,15 @@
+import { RootState } from "app/store";
+import { SnackbarLevel } from "components/snackbar";
+
+export const selectSnackber = (
+ state: RootState
+): {
+ isOpen: boolean;
+ level: SnackbarLevel;
+ message: string;
+ duration?: number;
+} => {
+ const { isOpen, level, message, duration } = state.ui;
+
+ return { isOpen, level, message, duration };
+};
diff --git a/dictation_client/src/features/ui/state.ts b/dictation_client/src/features/ui/state.ts
new file mode 100644
index 0000000..ade4c09
--- /dev/null
+++ b/dictation_client/src/features/ui/state.ts
@@ -0,0 +1,8 @@
+import { SnackbarLevel } from "components/snackbar";
+
+export interface UIState {
+ isOpen: boolean;
+ level: SnackbarLevel;
+ message: string;
+ duration?: number;
+}
diff --git a/dictation_client/src/features/ui/uiSlice.ts b/dictation_client/src/features/ui/uiSlice.ts
new file mode 100644
index 0000000..bf82331
--- /dev/null
+++ b/dictation_client/src/features/ui/uiSlice.ts
@@ -0,0 +1,38 @@
+import { createSlice, PayloadAction } from "@reduxjs/toolkit";
+import { SnackbarLevel } from "components/snackbar";
+import { UIState } from "./state";
+import { DEFAULT_SNACKBAR_DURATION } from "./constants";
+
+const initialState: UIState = {
+ isOpen: false,
+ level: "error",
+ message: "",
+};
+
+export const uiSlice = createSlice({
+ name: "ui",
+ initialState,
+ reducers: {
+ openSnackbar: (
+ state,
+ action: PayloadAction<{
+ level: SnackbarLevel;
+ message: string;
+ duration?: number;
+ }>
+ ) => {
+ const { level, message, duration } = action.payload;
+ state.isOpen = true;
+ state.level = level;
+ state.message = message;
+ state.duration =
+ level === "error" ? undefined : duration ?? DEFAULT_SNACKBAR_DURATION;
+ },
+ closeSnackbar: (state) => {
+ state.isOpen = false;
+ },
+ },
+});
+export const { openSnackbar, closeSnackbar } = uiSlice.actions;
+
+export default uiSlice.reducer;
diff --git a/dictation_client/src/features/verify/verifySlice.ts b/dictation_client/src/features/verify/verifySlice.ts
index 6dfe7af..1039f64 100644
--- a/dictation_client/src/features/verify/verifySlice.ts
+++ b/dictation_client/src/features/verify/verifySlice.ts
@@ -23,7 +23,7 @@ export const verifySlice = createSlice({
const { payload } = action;
// メール認証済みかをエラーコードから判定
- if (payload?.error.code === "E010301") {
+ if (payload?.error.code === "E010202") {
state.apps.VerifyState = "alreadySuccess";
} else {
state.apps.VerifyState = "failed";
diff --git a/dictation_client/src/pages/SignupCompletePage/index.tsx b/dictation_client/src/pages/SignupCompletePage/index.tsx
index c9bd65e..50822c5 100644
--- a/dictation_client/src/pages/SignupCompletePage/index.tsx
+++ b/dictation_client/src/pages/SignupCompletePage/index.tsx
@@ -1,4 +1,3 @@
-import { AppDispatch } from "app/store";
import React from "react";
import { useTranslation } from "react-i18next";
import { getTranslationID } from "translation";
diff --git a/dictation_client/src/pages/SignupPage/signupInput.tsx b/dictation_client/src/pages/SignupPage/signupInput.tsx
index 59a2203..bd3bfca 100644
--- a/dictation_client/src/pages/SignupPage/signupInput.tsx
+++ b/dictation_client/src/pages/SignupPage/signupInput.tsx
@@ -105,8 +105,9 @@ const SignupInput: React.FC = (): JSX.Element => {
/>
{isPushCreateButton && hasErrorEmptyCompany && (