| @@ -1,4 +1,4 @@ | |||||
| import { ApiId, HttpMethod, makeFormData, request } from "api"; | |||||
| import { ApiId, HttpMethod, makeFormData, makeParam, request } from "api"; | |||||
| import { getUrl } from "./url"; | import { getUrl } from "./url"; | ||||
| // -------学生証アップロード--------------- | // -------学生証アップロード--------------- | ||||
| @@ -34,3 +34,31 @@ export const uploadOtherLicenseImages = async ( | |||||
| }); | }); | ||||
| return res; | return res; | ||||
| }; | }; | ||||
| // -------Email変更手続き開始--------------- | |||||
| export type StartChangeEmailRequest = { | |||||
| new_email: string; | |||||
| }; | |||||
| export const startChangeEmail = async (param: StartChangeEmailRequest) => { | |||||
| const sendData = makeParam(param); | |||||
| const res = await request({ | |||||
| url: getUrl(ApiId.START_CHANGE_EMAIL), | |||||
| method: HttpMethod.POST, | |||||
| data: sendData, | |||||
| }); | |||||
| return res; | |||||
| }; | |||||
| // -------Email変更手続き認証--------------- | |||||
| export type VerifyChangeEmailRequest = { | |||||
| token: string; | |||||
| }; | |||||
| export const verifyChangeEmail = async (param: VerifyChangeEmailRequest) => { | |||||
| const sendData = makeParam(param); | |||||
| const res = await request({ | |||||
| url: getUrl(ApiId.VERIFY_CHANGE_EMAIL), | |||||
| method: HttpMethod.POST, | |||||
| data: sendData, | |||||
| }); | |||||
| return res; | |||||
| }; | |||||
| @@ -28,6 +28,9 @@ export const ApiId = { | |||||
| UPLOAD_STUDENT_LICENSE_IMAGES: id++, | UPLOAD_STUDENT_LICENSE_IMAGES: id++, | ||||
| UPLOAD_OTHER_LICENSE_IMAGES: id++, | UPLOAD_OTHER_LICENSE_IMAGES: id++, | ||||
| START_CHANGE_EMAIL: id++, | |||||
| VERIFY_CHANGE_EMAIL: id++, | |||||
| } as const; | } as const; | ||||
| export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | ||||
| @@ -20,6 +20,8 @@ const urls = { | |||||
| [A.ASK]: "ask", | [A.ASK]: "ask", | ||||
| [A.UPLOAD_STUDENT_LICENSE_IMAGES]: "upload/student-license-images", | [A.UPLOAD_STUDENT_LICENSE_IMAGES]: "upload/student-license-images", | ||||
| [A.UPLOAD_OTHER_LICENSE_IMAGES]: "upload/other-license-images", | [A.UPLOAD_OTHER_LICENSE_IMAGES]: "upload/other-license-images", | ||||
| [A.START_CHANGE_EMAIL]: "email/change/start", | |||||
| [A.VERIFY_CHANGE_EMAIL]: "email/change/verify", | |||||
| }; | }; | ||||
| const prefixs = { | const prefixs = { | ||||
| @@ -0,0 +1,60 @@ | |||||
| import { Box, Button, Stack } from "@mui/material"; | |||||
| import { verifyChangeEmail } from "api/customer"; | |||||
| import useAPICall from "hooks/useAPICall"; | |||||
| import useAuth from "hooks/useAuth"; | |||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||||
| import { PageID } from "pages"; | |||||
| import { useEffect, useState } from "react"; | |||||
| import { useParams } from "react-router-dom"; | |||||
| import { getPath } from "routes/path"; | |||||
| export default function ChangeEmailStart() { | |||||
| const { logout, initialized } = useAuth(); | |||||
| const { navigateWhenChanged } = useNavigateCustom(); | |||||
| const { token: paramToken } = useParams(); | |||||
| const [done, setDone] = useState<boolean | null>(null); | |||||
| const { callAPI: callVerifyChangeEmail } = useAPICall({ | |||||
| apiMethod: verifyChangeEmail, | |||||
| backDrop: true, | |||||
| onSuccess: () => { | |||||
| setDone(true); | |||||
| logout(); | |||||
| }, | |||||
| onFailed: () => { | |||||
| setDone(false); | |||||
| }, | |||||
| }); | |||||
| const toLogin = () => { | |||||
| navigateWhenChanged(getPath(PageID.LOGIN)); | |||||
| }; | |||||
| useEffect(() => { | |||||
| if (!paramToken) { | |||||
| setDone(false); | |||||
| return; | |||||
| } | |||||
| if (initialized) { | |||||
| callVerifyChangeEmail({ token: paramToken }); | |||||
| } | |||||
| }, [paramToken, initialized]); | |||||
| if (done === true) { | |||||
| return ( | |||||
| <Stack spacing={2}> | |||||
| <Box> | |||||
| 新規メールアドレスに変更手続きのご案内を送信しました。ご確認ください。 | |||||
| </Box> | |||||
| <Box> | |||||
| <Button onClick={toLogin}>ログインへ</Button> | |||||
| </Box> | |||||
| </Stack> | |||||
| ); | |||||
| } else if (done === false) { | |||||
| return <Box>認証に失敗しました。再度お試しください。</Box>; | |||||
| } | |||||
| return null; | |||||
| } | |||||
| @@ -12,9 +12,11 @@ export default function Page404() { | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (!initialized) return; | if (!initialized) return; | ||||
| if (authenticated) { | if (authenticated) { | ||||
| console.log("404 to dashboard"); | |||||
| navigateWhenChanged(getPath(PageID.DASHBOARD_OVERVIEW)); | navigateWhenChanged(getPath(PageID.DASHBOARD_OVERVIEW)); | ||||
| return; | return; | ||||
| } else { | } else { | ||||
| console.log("404 to login"); | |||||
| navigateWhenChanged(getPath(PageID.LOGIN)); | navigateWhenChanged(getPath(PageID.LOGIN)); | ||||
| return; | return; | ||||
| } | } | ||||
| @@ -0,0 +1,121 @@ | |||||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||||
| import { Box, Button, Stack, Typography } from "@mui/material"; | |||||
| import { HasChildren } from "@types"; | |||||
| import { startChangeEmail } from "api/customer"; | |||||
| import RequireChip from "components/chip/RequireChip"; | |||||
| import InputAlert from "components/form/InputAlert"; | |||||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||||
| import StackRow from "components/stack/StackRow"; | |||||
| import useAPICall from "hooks/useAPICall"; | |||||
| import useAuth from "hooks/useAuth"; | |||||
| import useDashboard from "hooks/useDashBoard"; | |||||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||||
| import { PageID, TabID } from "pages"; | |||||
| import { useEffect, useState } from "react"; | |||||
| import { useForm } from "react-hook-form"; | |||||
| import * as Yup from "yup"; | |||||
| type AreaBoxProps = { | |||||
| label: string; | |||||
| require?: boolean; | |||||
| } & HasChildren; | |||||
| function AreaBox({ label, children, require }: AreaBoxProps) { | |||||
| return ( | |||||
| <Box> | |||||
| <StackRow> | |||||
| <Typography variant="subtitle1">〇{label}</Typography> | |||||
| <RequireChip require={require ?? false} /> | |||||
| </StackRow> | |||||
| {children} | |||||
| </Box> | |||||
| ); | |||||
| } | |||||
| type FormProps = { | |||||
| new_email: string; | |||||
| email_retype: string; | |||||
| }; | |||||
| export default function ChangeEmailStart() { | |||||
| const { setHeaderTitle, setTabs } = useDashboard( | |||||
| PageID.DASHBOARD_USER_CHANGE_EMAIL_START, | |||||
| TabID.NONE | |||||
| ); | |||||
| const { user } = useAuth(); | |||||
| const [done, setDone] = useState(false); | |||||
| const { error } = useSnackbarCustom(); | |||||
| const form = useForm<FormProps>({ | |||||
| defaultValues: { | |||||
| new_email: "", | |||||
| email_retype: "", | |||||
| }, | |||||
| resolver: yupResolver( | |||||
| Yup.object().shape({ | |||||
| new_email: Yup.string().required("必須項目です"), | |||||
| email_retype: Yup.string() | |||||
| .required("必須項目です") | |||||
| .test("retype", "一致しません", function (value) { | |||||
| return value === this.parent.new_email; | |||||
| }), | |||||
| }) | |||||
| ), | |||||
| }); | |||||
| const { | |||||
| callAPI: callStartChangeEmail, | |||||
| errorMode, | |||||
| generalErrorMessage, | |||||
| } = useAPICall({ | |||||
| apiMethod: startChangeEmail, | |||||
| backDrop: true, | |||||
| form, | |||||
| onSuccess: () => { | |||||
| setDone(true); | |||||
| }, | |||||
| onFailed: () => { | |||||
| error("失敗しました"); | |||||
| }, | |||||
| }); | |||||
| const handleSubmit = (data: FormProps) => { | |||||
| callStartChangeEmail({ new_email: data.new_email }); | |||||
| }; | |||||
| useEffect(() => { | |||||
| setHeaderTitle("メールアドレス変更"); | |||||
| setTabs(null); | |||||
| }, []); | |||||
| if (done) { | |||||
| return ( | |||||
| <Box> | |||||
| 新規メールアドレスに変更手続きのご案内を送信しました。ご確認ください。 | |||||
| </Box> | |||||
| ); | |||||
| } | |||||
| return ( | |||||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||||
| <Stack spacing={2}> | |||||
| <InputAlert error={errorMode} message={generalErrorMessage} /> | |||||
| <AreaBox label="新規Email" require={true}> | |||||
| <RHFTextField name="new_email" /> | |||||
| </AreaBox> | |||||
| <AreaBox label="確認用Email" require={true}> | |||||
| <> | |||||
| <Typography>確認のため再度入力してください</Typography> | |||||
| <RHFTextField name="email_retype" /> | |||||
| </> | |||||
| </AreaBox> | |||||
| <Box> | |||||
| <Button type="submit" variant="contained"> | |||||
| 送信 | |||||
| </Button> | |||||
| </Box> | |||||
| </Stack> | |||||
| </FormProvider> | |||||
| ); | |||||
| } | |||||
| @@ -21,6 +21,15 @@ export default function UserDetail() { | |||||
| return ( | return ( | ||||
| <Stack> | <Stack> | ||||
| <Button>ユーザー情報変更</Button> | <Button>ユーザー情報変更</Button> | ||||
| <Button | |||||
| onClick={() => { | |||||
| navigateWhenChanged( | |||||
| getPath(PageID.DASHBOARD_USER_CHANGE_EMAIL_START) | |||||
| ); | |||||
| }} | |||||
| > | |||||
| Email変更 | |||||
| </Button> | |||||
| <Button>口座情報変更</Button> | <Button>口座情報変更</Button> | ||||
| <Button | <Button | ||||
| onClick={() => { | onClick={() => { | ||||
| @@ -4,6 +4,7 @@ export const PageID = { | |||||
| LOGIN: id++, | LOGIN: id++, | ||||
| LOGOUT: id++, | LOGOUT: id++, | ||||
| USER_CHANGE_EMAIL_VERIFY: id++, | |||||
| DASHBOARD_OVERVIEW: id++, | DASHBOARD_OVERVIEW: id++, | ||||
| @@ -22,6 +23,7 @@ export const PageID = { | |||||
| DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD: id++, | DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD: id++, | ||||
| DASHBOARD_ASK: id++, | DASHBOARD_ASK: id++, | ||||
| DASHBOARD_USER_CHANGE_EMAIL_START: id++, | |||||
| PAGE_403: id++, | PAGE_403: id++, | ||||
| PAGE_404: id++, | PAGE_404: id++, | ||||
| @@ -31,6 +31,10 @@ const AuthRoutes = (): RouteObject => ({ | |||||
| path: getRoute(PageID.LOGOUT), | path: getRoute(PageID.LOGOUT), | ||||
| element: <Logout />, | element: <Logout />, | ||||
| }, | }, | ||||
| { | |||||
| path: getRoute(PageID.USER_CHANGE_EMAIL_VERIFY), | |||||
| element: <ChangeEmailVerify />, | |||||
| }, | |||||
| {}, | {}, | ||||
| ], | ], | ||||
| }); | }); | ||||
| @@ -56,6 +60,9 @@ export function Routes() { | |||||
| // 認証関連 ------------------------------- | // 認証関連 ------------------------------- | ||||
| const Login = Loadable(lazy(() => import("pages/auth/login"))); | const Login = Loadable(lazy(() => import("pages/auth/login"))); | ||||
| const Logout = Loadable(lazy(() => import("pages/auth/logout"))); | const Logout = Loadable(lazy(() => import("pages/auth/logout"))); | ||||
| const ChangeEmailVerify = Loadable( | |||||
| lazy(() => import("pages/auth/change-email-verify")) | |||||
| ); | |||||
| // その他 --------------------------------- | // その他 --------------------------------- | ||||
| @@ -55,6 +55,8 @@ const PATHS_DASHBOARD = { | |||||
| [makePathKey(PageID.DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD)]: | [makePathKey(PageID.DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD)]: | ||||
| "/dashboard/user/upload/other-license", | "/dashboard/user/upload/other-license", | ||||
| [makePathKey(PageID.DASHBOARD_ASK)]: "/dashboard/ask", | [makePathKey(PageID.DASHBOARD_ASK)]: "/dashboard/ask", | ||||
| [makePathKey(PageID.DASHBOARD_USER_CHANGE_EMAIL_START)]: | |||||
| "/dashboard/change/email/start", | |||||
| }; | }; | ||||
| const PATHS = { | const PATHS = { | ||||
| @@ -63,6 +65,7 @@ const PATHS = { | |||||
| // 認証 | // 認証 | ||||
| [makePathKey(PageID.LOGIN)]: "/login", | [makePathKey(PageID.LOGIN)]: "/login", | ||||
| [makePathKey(PageID.LOGOUT)]: "/logout", | [makePathKey(PageID.LOGOUT)]: "/logout", | ||||
| [makePathKey(PageID.USER_CHANGE_EMAIL_VERIFY)]: "/change/email/verify/:token", | |||||
| // ダッシュボード---------------- | // ダッシュボード---------------- | ||||
| ...PATHS_DASHBOARD, | ...PATHS_DASHBOARD, | ||||
| @@ -30,6 +30,9 @@ export default function DashboardRoutes(): RouteObject[] { | |||||
| lazy(() => import("pages/dashboard/user/upload-other-license-images")) | lazy(() => import("pages/dashboard/user/upload-other-license-images")) | ||||
| ); | ); | ||||
| const Ask = Loadable(lazy(() => import("pages/dashboard/other/ask"))); | const Ask = Loadable(lazy(() => import("pages/dashboard/other/ask"))); | ||||
| const ChangeEmailStart = Loadable( | |||||
| lazy(() => import("pages/dashboard/user/change-email-start")) | |||||
| ); | |||||
| const allChildren = [ | const allChildren = [ | ||||
| { | { | ||||
| @@ -60,6 +63,10 @@ export default function DashboardRoutes(): RouteObject[] { | |||||
| pageId: PageID.DASHBOARD_ASK, | pageId: PageID.DASHBOARD_ASK, | ||||
| element: <Ask />, | element: <Ask />, | ||||
| }, | }, | ||||
| { | |||||
| pageId: PageID.DASHBOARD_USER_CHANGE_EMAIL_START, | |||||
| element: <ChangeEmailStart />, | |||||
| }, | |||||
| ]; | ]; | ||||
| return allChildren.map(({ pageId, ...others }) => ({ | return allChildren.map(({ pageId, ...others }) => ({ | ||||
| ...others, | ...others, | ||||