소스 검색

パスワード設定機能 追加

develop
sosuke.iwabuchi 2 년 전
부모
커밋
3abcceec81
9개의 변경된 파일301개의 추가작업 그리고 3개의 파일을 삭제
  1. +32
    -1
      src/api/auth.ts
  2. +3
    -0
      src/api/index.ts
  3. +2
    -0
      src/api/url.ts
  4. +8
    -1
      src/pages/auth/login.tsx
  5. +120
    -0
      src/pages/auth/password-setting-start.tsx
  6. +117
    -0
      src/pages/auth/password-setting-verify.tsx
  7. +2
    -0
      src/pages/index.ts
  8. +14
    -1
      src/routes/index.tsx
  9. +3
    -0
      src/routes/path.ts

+ 32
- 1
src/api/auth.ts 파일 보기

@@ -1,4 +1,4 @@
import { APICommonResponse, ApiId, HttpMethod, request } from ".";
import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from ".";
import { getUrl } from "./url";

export type Me = {
@@ -41,3 +41,34 @@ export const logout = async () => {
});
return res;
};

// -------パスワード設定開始---------------
export type StartPasswordSettingRequest = {
email: string;
};
export const startPasswordSetting = async (
param: StartPasswordSettingRequest
) => {
const res = await request({
url: getUrl(ApiId.PASSWORD_SETTING_START),
method: HttpMethod.POST,
data: makeParam(param),
});
return res;
};

// -------パスワード設定認証---------------
export type VerifyPasswordSettingRequest = {
token: string;
password: string;
};
export const verifyPasswordSetting = async (
param: VerifyPasswordSettingRequest
) => {
const res = await request({
url: getUrl(ApiId.PASSWORD_SETTING_VERIFY),
method: HttpMethod.POST,
data: makeParam(param),
});
return res;
};

+ 3
- 0
src/api/index.ts 파일 보기

@@ -14,6 +14,9 @@ export const ApiId = {
LOGIN: id++,
LOGOUT: id++,

PASSWORD_SETTING_START: id++,
PASSWORD_SETTING_VERIFY: id++,

SEASON_TICKET_CONTRACTS: id++,
PAYMENT_PLANS: id++,



+ 2
- 0
src/api/url.ts 파일 보기

@@ -6,6 +6,8 @@ const urls = {
[A.ME]: "me",
[A.LOGIN]: "login",
[A.LOGOUT]: "logout",
[A.PASSWORD_SETTING_START]: "password/setting/start",
[A.PASSWORD_SETTING_VERIFY]: "password/setting/verify",
[A.SEASON_TICKET_CONTRACTS]: "season-ticket-contracts",
[A.PAYMENT_PLANS]: "season-ticket-contract/payment-plans",
[A.STICKER_RE_ORDER]: "season-ticket-contract/sticker-re-order",


+ 8
- 1
src/pages/auth/login.tsx 파일 보기

@@ -73,7 +73,14 @@ export default function Login() {
<LoadingButton loading={sending} type="submit" variant="contained">
ログイン
</LoadingButton>
<Button variant="outlined">パスワードを忘れた</Button>
<Button
variant="outlined"
onClick={() => {
navigateWhenChanged(getPath(PageID.USER_SETTING_PASSWORD_START));
}}
>
パスワードを忘れた/設定したい
</Button>
</Stack>
</Box>
</FormProvider>


+ 120
- 0
src/pages/auth/password-setting-start.tsx 파일 보기

@@ -0,0 +1,120 @@
import { yupResolver } from "@hookform/resolvers/yup";
import { Box, Button, Stack, Typography } from "@mui/material";
import { HasChildren } from "@types";
import { startPasswordSetting } from "api/auth";
import RequireChip from "components/chip/RequireChip";
import { FormProvider, RHFTextField } from "components/hook-form";
import StackRow from "components/stack/StackRow";
import useAPICall from "hooks/useAPICall";
import useNavigateCustom from "hooks/useNavigateCustom";
import { PageID } from "pages";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";
import { object as YupObject, string as YupString } 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 = {
email: string;
retype_email: string;
};

export default function PasswordSettingStart() {
const { navigateWhenChanged } = useNavigateCustom();

const [done, setDone] = useState<boolean | null>(null);

const form = useForm<FormProps>({
defaultValues: {
email: "",
retype_email: "",
},
resolver: yupResolver(
YupObject().shape({
email: YupString().required("必須項目です"),
retype_email: YupString()
.required("必須項目です")
.test("retype", "一致しません", function (value) {
return value === this.parent.email;
}),
})
),
});

const { callAPI: callStartPasswordSetting } = useAPICall({
apiMethod: startPasswordSetting,
backDrop: true,
form,
onSuccess: () => {
setDone(true);
},
onFailed: () => {},
});

const handleSubmit = (data: FormProps) => {
callStartPasswordSetting({ ...data });
};

const toLogin = () => {
navigateWhenChanged(getPath(PageID.LOGIN));
};

if (done === true) {
return (
<Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center">
<Stack spacing={2}>
<Box>
<Typography>
メールアドレスに設定手続きのご案内を送信しました。
</Typography>
<Typography>ご確認ください。</Typography>
</Box>
<Box>
<Button onClick={toLogin}>ログインへ</Button>
</Box>
</Stack>
</Box>
);
}

return (
<Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center">
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Stack spacing={2}>
<Box>
<Button onClick={toLogin}>戻る</Button>
</Box>
<Box>
<Typography>
登録済みのメールアドレスへ変更用のURLを送信します。
</Typography>
</Box>
<AreaBox label="Email" require={true}>
<RHFTextField name="email" />
</AreaBox>
<AreaBox label="Email (確認用)" require={true}>
<RHFTextField name="retype_email" />
</AreaBox>
<Box>
<Button type="submit">送信</Button>
</Box>
</Stack>
</FormProvider>
</Box>
);
}

+ 117
- 0
src/pages/auth/password-setting-verify.tsx 파일 보기

@@ -0,0 +1,117 @@
import { yupResolver } from "@hookform/resolvers/yup";
import { Box, Button, Stack, Typography } from "@mui/material";
import { HasChildren } from "@types";
import { verifyPasswordSetting } from "api/auth";
import RequireChip from "components/chip/RequireChip";
import { FormProvider, RHFTextField } from "components/hook-form";
import StackRow from "components/stack/StackRow";
import useAPICall from "hooks/useAPICall";
import useNavigateCustom from "hooks/useNavigateCustom";
import { PageID } from "pages";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { useParams } from "react-router-dom";
import { getPath } from "routes/path";
import { object as YupObject, string as YupString } 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 = {
password: string;
retype_password: string;
};

export default function PasswordSettingVerify() {
const { navigateWhenChanged } = useNavigateCustom();
const { token: paramToken } = useParams();

const [done, setDone] = useState<boolean | null>(null);

const form = useForm<FormProps>({
defaultValues: {
password: "",
retype_password: "",
},
resolver: yupResolver(
YupObject().shape({
password: YupString().required("必須項目です"),
retype_password: YupString()
.required("必須項目です")
.test("retype", "一致しません", function (value) {
return value === this.parent.password;
}),
})
),
});

const { callAPI: callVerifyPasswordSetting } = useAPICall({
apiMethod: verifyPasswordSetting,
backDrop: true,
form,
onSuccess: () => {
setDone(true);
},
onFailed: () => {},
});

const handleSubmit = (data: FormProps) => {
if (!paramToken) return;
callVerifyPasswordSetting({ ...data, token: paramToken });
};

const toLogin = () => {
navigateWhenChanged(getPath(PageID.LOGIN));
};

if (done === true) {
return (
<Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center">
<Stack spacing={2}>
<Box>
<Typography>パスワードを設定しました</Typography>
</Box>
<Box>
<Button onClick={toLogin}>ログインへ</Button>
</Box>
</Stack>
</Box>
);
}

return (
<Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center">
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Stack spacing={2}>
<Box>
<Typography>
新たに設定するログインパスワードを入力してください
</Typography>
</Box>
<AreaBox label="新しいログインパスワード" require={true}>
<RHFTextField name="password" type="password" />
</AreaBox>
<AreaBox label="新しいログインパスワード (再入力)" require={true}>
<RHFTextField name="retype_password" type="password" />
</AreaBox>
<Box>
<Button type="submit">送信</Button>
</Box>
</Stack>
</FormProvider>
</Box>
);
}

+ 2
- 0
src/pages/index.ts 파일 보기

@@ -5,6 +5,8 @@ export const PageID = {
LOGIN: id++,
LOGOUT: id++,
USER_CHANGE_EMAIL_VERIFY: id++,
USER_SETTING_PASSWORD_START: id++,
USER_SETTING_PASSWORD_VERIFY: id++,

DASHBOARD_OVERVIEW: id++,



+ 14
- 1
src/routes/index.tsx 파일 보기

@@ -35,7 +35,14 @@ const AuthRoutes = (): RouteObject => ({
path: getRoute(PageID.USER_CHANGE_EMAIL_VERIFY),
element: <ChangeEmailVerify />,
},
{},
{
path: getRoute(PageID.USER_SETTING_PASSWORD_START),
element: <PasswordSettingStart />,
},
{
path: getRoute(PageID.USER_SETTING_PASSWORD_VERIFY),
element: <PasswordSettingVerify />,
},
],
});

@@ -63,6 +70,12 @@ const Logout = Loadable(lazy(() => import("pages/auth/logout")));
const ChangeEmailVerify = Loadable(
lazy(() => import("pages/auth/change-email-verify"))
);
const PasswordSettingStart = Loadable(
lazy(() => import("pages/auth/password-setting-start"))
);
const PasswordSettingVerify = Loadable(
lazy(() => import("pages/auth/password-setting-verify"))
);

// その他 ---------------------------------



+ 3
- 0
src/routes/path.ts 파일 보기

@@ -66,6 +66,9 @@ const PATHS = {
[makePathKey(PageID.LOGIN)]: "/login",
[makePathKey(PageID.LOGOUT)]: "/logout",
[makePathKey(PageID.USER_CHANGE_EMAIL_VERIFY)]: "/change/email/verify/:token",
[makePathKey(PageID.USER_SETTING_PASSWORD_START)]: "/setting/password/start",
[makePathKey(PageID.USER_SETTING_PASSWORD_VERIFY)]:
"/setting/password/verify/:token",

// ダッシュボード----------------
...PATHS_DASHBOARD,


Loading…
취소
저장