| @@ -1,4 +1,4 @@ | |||
| import { ApiId, HttpMethod, makeFormData, request } from "api"; | |||
| import { ApiId, HttpMethod, makeFormData, makeParam, request } from "api"; | |||
| import { getUrl } from "./url"; | |||
| // -------学生証アップロード--------------- | |||
| @@ -34,3 +34,31 @@ export const uploadOtherLicenseImages = async ( | |||
| }); | |||
| 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_OTHER_LICENSE_IMAGES: id++, | |||
| START_CHANGE_EMAIL: id++, | |||
| VERIFY_CHANGE_EMAIL: id++, | |||
| } as const; | |||
| export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | |||
| @@ -20,6 +20,8 @@ const urls = { | |||
| [A.ASK]: "ask", | |||
| [A.UPLOAD_STUDENT_LICENSE_IMAGES]: "upload/student-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 = { | |||
| @@ -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(() => { | |||
| if (!initialized) return; | |||
| if (authenticated) { | |||
| console.log("404 to dashboard"); | |||
| navigateWhenChanged(getPath(PageID.DASHBOARD_OVERVIEW)); | |||
| return; | |||
| } else { | |||
| console.log("404 to login"); | |||
| navigateWhenChanged(getPath(PageID.LOGIN)); | |||
| 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 ( | |||
| <Stack> | |||
| <Button>ユーザー情報変更</Button> | |||
| <Button | |||
| onClick={() => { | |||
| navigateWhenChanged( | |||
| getPath(PageID.DASHBOARD_USER_CHANGE_EMAIL_START) | |||
| ); | |||
| }} | |||
| > | |||
| Email変更 | |||
| </Button> | |||
| <Button>口座情報変更</Button> | |||
| <Button | |||
| onClick={() => { | |||
| @@ -4,6 +4,7 @@ export const PageID = { | |||
| LOGIN: id++, | |||
| LOGOUT: id++, | |||
| USER_CHANGE_EMAIL_VERIFY: id++, | |||
| DASHBOARD_OVERVIEW: id++, | |||
| @@ -22,6 +23,7 @@ export const PageID = { | |||
| DASHBOARD_USER_OTHER_LICENSE_IMAGES_UPLOAD: id++, | |||
| DASHBOARD_ASK: id++, | |||
| DASHBOARD_USER_CHANGE_EMAIL_START: id++, | |||
| PAGE_403: id++, | |||
| PAGE_404: id++, | |||
| @@ -31,6 +31,10 @@ const AuthRoutes = (): RouteObject => ({ | |||
| path: getRoute(PageID.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 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)]: | |||
| "/dashboard/user/upload/other-license", | |||
| [makePathKey(PageID.DASHBOARD_ASK)]: "/dashboard/ask", | |||
| [makePathKey(PageID.DASHBOARD_USER_CHANGE_EMAIL_START)]: | |||
| "/dashboard/change/email/start", | |||
| }; | |||
| const PATHS = { | |||
| @@ -63,6 +65,7 @@ const PATHS = { | |||
| // 認証 | |||
| [makePathKey(PageID.LOGIN)]: "/login", | |||
| [makePathKey(PageID.LOGOUT)]: "/logout", | |||
| [makePathKey(PageID.USER_CHANGE_EMAIL_VERIFY)]: "/change/email/verify/:token", | |||
| // ダッシュボード---------------- | |||
| ...PATHS_DASHBOARD, | |||
| @@ -30,6 +30,9 @@ export default function DashboardRoutes(): RouteObject[] { | |||
| lazy(() => import("pages/dashboard/user/upload-other-license-images")) | |||
| ); | |||
| const Ask = Loadable(lazy(() => import("pages/dashboard/other/ask"))); | |||
| const ChangeEmailStart = Loadable( | |||
| lazy(() => import("pages/dashboard/user/change-email-start")) | |||
| ); | |||
| const allChildren = [ | |||
| { | |||
| @@ -60,6 +63,10 @@ export default function DashboardRoutes(): RouteObject[] { | |||
| pageId: PageID.DASHBOARD_ASK, | |||
| element: <Ask />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_USER_CHANGE_EMAIL_START, | |||
| element: <ChangeEmailStart />, | |||
| }, | |||
| ]; | |||
| return allChildren.map(({ pageId, ...others }) => ({ | |||
| ...others, | |||