| @@ -1,4 +1,4 @@ | |||
| import { ApiId, HttpMethod, makeParam, request } from ".."; | |||
| import { ApiId, HttpMethod, TimestampRequest, makeParam, request } from ".."; | |||
| import { getUrl } from "../url"; | |||
| export type AppReceiptIssuingOrder = { | |||
| @@ -56,3 +56,18 @@ export const mailRequest = async (data: MailOrderRequest) => { | |||
| }); | |||
| return res; | |||
| }; | |||
| // 領収証Email送付依頼 ------------------------------- | |||
| export type EmailOrderRequest = { | |||
| access_token: string; | |||
| email: string; | |||
| } & TimestampRequest; | |||
| export const emailRequest = async (data: EmailOrderRequest) => { | |||
| const res = await request({ | |||
| url: getUrl(ApiId.RECEIPT_ISSUING_ORDER_EMAIL_ORDER), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(data), | |||
| }); | |||
| return res; | |||
| }; | |||
| @@ -19,6 +19,7 @@ export const ApiId = { | |||
| RECEIPT_ISSUING_ORDER_CONFIRM: id++, | |||
| DOWNLOAD_RECEIPT: id++, | |||
| RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, | |||
| RECEIPT_ISSUING_ORDER_EMAIL_ORDER: id++, | |||
| // DASHBOARD向け------------------------------- | |||
| CHANGE_CONTRACT: id++, | |||
| @@ -15,6 +15,7 @@ const urls = { | |||
| [A.RECEIPT_ISSUING_ORDER_MAIL_ORDER]: "receipt-issuing-order/mail-order", | |||
| [A.RECEIPT_ISSUING_ORDER_MAIL_POST_COMPLETE]: | |||
| "receipt-issuing-order/mail-complete", | |||
| [A.RECEIPT_ISSUING_ORDER_EMAIL_ORDER]: "receipt-issuing-order/email-order", | |||
| [A.RECEIPT_ISSUING_ORDER_HANDELRS]: "receipt-issuing-order/handlers", | |||
| [A.RECEIPT_ISSUING_ORDER_CHANGE_HANDELR]: | |||
| "receipt-issuing-order/change-handler", | |||
| @@ -8,6 +8,7 @@ export const PageID = { | |||
| APP_RECEIPT_ISSUING_ORDER_INDEX: id++, | |||
| APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, | |||
| APP_RECEIPT_ISSUING_ORDER_EMAIL_ORDER: id++, | |||
| DASHBOARD_OVERVIEW: id++, | |||
| @@ -0,0 +1,171 @@ | |||
| import { | |||
| Box, | |||
| Button, | |||
| Paper, | |||
| Stack, | |||
| Step, | |||
| StepLabel, | |||
| Stepper, | |||
| Table, | |||
| TableBody, | |||
| TableCell, | |||
| TableRow, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { HasChildren } from "@types"; | |||
| import { emailRequest } from "api/app/receipt-issuing-order"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useApp from "hooks/useApp"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { useMemo, useState } from "react"; | |||
| import useInputEmailStep from "./hooks/useInputEmailStep"; | |||
| type TableRowCustomProps = { | |||
| title: string; | |||
| value: string; | |||
| }; | |||
| const TableRowCustom = ({ title, value }: TableRowCustomProps) => { | |||
| return ( | |||
| <TableRow> | |||
| <TableCell sx={{ borderRight: "1px solid rgba(224, 224, 224, 1)" }}> | |||
| {title} | |||
| </TableCell> | |||
| <TableCell>{value}</TableCell> | |||
| </TableRow> | |||
| ); | |||
| }; | |||
| type SectionProps = { | |||
| title: string; | |||
| subtitle?: string; | |||
| } & HasChildren; | |||
| const Section = ({ title, subtitle, children }: SectionProps) => { | |||
| return ( | |||
| <Paper | |||
| sx={{ py: 2, border: "1px solid rgba(224, 224, 224, 1)" }} | |||
| elevation={0} | |||
| > | |||
| <Box> | |||
| <Typography variant="subtitle1">{title}</Typography> | |||
| {subtitle && <Typography variant="body2">{subtitle}</Typography>} | |||
| </Box> | |||
| <Box sx={{ mt: 2 }}>{children}</Box> | |||
| </Paper> | |||
| ); | |||
| }; | |||
| export default function EmailOrder() { | |||
| const { | |||
| tokenResult, | |||
| token, | |||
| navigateToHome, | |||
| fetch, | |||
| receiptIssuingOrder: order, | |||
| } = useApp(); | |||
| const { success, error } = useSnackbarCustom(); | |||
| const { navigate } = useNavigateCustom(); | |||
| const [mode, setMode] = useState<"input" | "confirm" | "done">("input"); | |||
| const input = useInputEmailStep({ | |||
| onNext: () => { | |||
| setMode("confirm"); | |||
| }, | |||
| onPrev: () => { | |||
| navigate(-1); | |||
| }, | |||
| }); | |||
| const requestEmailAPI = useAPICall({ | |||
| apiMethod: emailRequest, | |||
| backDrop: true, | |||
| onSuccess: () => { | |||
| fetch(); | |||
| setMode("done"); | |||
| }, | |||
| onFailed: () => { | |||
| error("登録失敗"); | |||
| }, | |||
| }); | |||
| const currentStep = useMemo(() => { | |||
| if (mode === "input") return 0; | |||
| if (mode === "confirm") return 1; | |||
| if (mode === "done") return 2; | |||
| }, [mode]); | |||
| const handleConfirm = () => { | |||
| if (!order) return; | |||
| requestEmailAPI.callAPI({ | |||
| access_token: token, | |||
| timestamp: order.updated_at, | |||
| ...input.values(), | |||
| }); | |||
| }; | |||
| if (tokenResult !== "ok") { | |||
| return null; | |||
| } | |||
| return ( | |||
| <> | |||
| <Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center"> | |||
| <Stack spacing={3}> | |||
| <Box> | |||
| <Typography variant="h5">領収証Email送付依頼</Typography> | |||
| </Box> | |||
| <Section title=""> | |||
| <Stepper activeStep={currentStep}> | |||
| <Step> | |||
| <StepLabel>Email送信先入力</StepLabel> | |||
| </Step> | |||
| <Step> | |||
| <StepLabel>確認</StepLabel> | |||
| </Step> | |||
| <Step> | |||
| <StepLabel>完了</StepLabel> | |||
| </Step> | |||
| </Stepper> | |||
| {mode === "input" && input.element} | |||
| {mode === "confirm" && ( | |||
| <Stack spacing={2} sx={{ p: 1, py: 3, m: 1 }}> | |||
| <Typography variant="h5">Email送信先情報確認</Typography> | |||
| <Table> | |||
| <TableBody> | |||
| <TableRowCustom | |||
| title="Email" | |||
| value={input.values("email")} | |||
| /> | |||
| </TableBody> | |||
| </Table> | |||
| <Stack direction="row" spacing={2}> | |||
| <Button | |||
| variant="text" | |||
| onClick={() => { | |||
| setMode("input"); | |||
| }} | |||
| > | |||
| 戻る | |||
| </Button> | |||
| <Button variant="contained" onClick={handleConfirm}> | |||
| 確定 | |||
| </Button> | |||
| </Stack> | |||
| </Stack> | |||
| )} | |||
| {mode === "done" && ( | |||
| <Stack spacing={2} sx={{ p: 1, py: 3, m: 1 }}> | |||
| <Box>受付いたしました。</Box> | |||
| <Button onClick={navigateToHome}>戻る</Button> | |||
| </Stack> | |||
| )} | |||
| </Section> | |||
| </Stack> | |||
| </Box> | |||
| </> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,72 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { Box, Button, Divider, Stack, Typography } from "@mui/material"; | |||
| import { HasChildren } from "@types"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import { useForm } from "react-hook-form"; | |||
| import * as Yup from "yup"; | |||
| type AreaBoxProps = { | |||
| title: string; | |||
| } & HasChildren; | |||
| function AreaBox({ title, children }: AreaBoxProps) { | |||
| return ( | |||
| <Box sx={{ maxWidth: 500 }}> | |||
| <Typography variant="subtitle1">{title}</Typography> | |||
| {children} | |||
| </Box> | |||
| ); | |||
| } | |||
| type FormProps = { | |||
| email: string; | |||
| }; | |||
| type Props = { | |||
| onNext?: VoidFunction; | |||
| onPrev?: VoidFunction; | |||
| }; | |||
| export default function useInputEmailStep({ onNext, onPrev }: Props) { | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| email: "", | |||
| }, | |||
| resolver: yupResolver( | |||
| Yup.object().shape({ | |||
| email: Yup.string().required("必須項目です"), | |||
| }) | |||
| ), | |||
| }); | |||
| const handleSubmit = () => { | |||
| if (onNext) { | |||
| onNext(); | |||
| } | |||
| }; | |||
| const handlePrev = () => { | |||
| if (onPrev) { | |||
| onPrev(); | |||
| } | |||
| }; | |||
| const element = ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Stack spacing={2} sx={{ p: 1, m: 1 }} textAlign="left"> | |||
| <AreaBox title="Email"> | |||
| <RHFTextField name="email" /> | |||
| </AreaBox> | |||
| <Divider /> | |||
| <Stack direction="row" spacing={2}> | |||
| <Button variant="text" onClick={handlePrev}> | |||
| 戻る | |||
| </Button> | |||
| <Button variant="contained" type="submit"> | |||
| 次へ | |||
| </Button> | |||
| </Stack> | |||
| </Stack> | |||
| </FormProvider> | |||
| ); | |||
| return { element, values: form.getValues, setValue: form.setValue }; | |||
| } | |||
| @@ -22,6 +22,9 @@ export default function useReceiptIssuingOrderSelectHowToGet() { | |||
| const handleClickMail = () => { | |||
| navigateWhenChanged(getPath(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER)); | |||
| }; | |||
| const handleClickEmail = () => { | |||
| navigateWhenChanged(getPath(PageID.APP_RECEIPT_ISSUING_ORDER_EMAIL_ORDER)); | |||
| }; | |||
| const element = ( | |||
| <Stack spacing={1}> | |||
| @@ -31,7 +34,9 @@ export default function useReceiptIssuingOrderSelectHowToGet() { | |||
| </Button> | |||
| </Box> | |||
| <Box> | |||
| <Button variant="contained">メール送信依頼</Button> | |||
| <Button variant="contained" onClick={handleClickEmail}> | |||
| メール送信依頼 | |||
| </Button> | |||
| </Box> | |||
| {!order?.status_order_mail_datetime && ( | |||
| <Box> | |||
| @@ -50,6 +50,10 @@ const AppRoutes = (): RouteObject => ({ | |||
| path: getRoute(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER), | |||
| element: <MailOrder />, | |||
| }, | |||
| { | |||
| path: getRoute(PageID.APP_RECEIPT_ISSUING_ORDER_EMAIL_ORDER), | |||
| element: <EmailOrder />, | |||
| }, | |||
| ], | |||
| }); | |||
| @@ -149,6 +153,7 @@ const ReceiptIssuingOrder = Loadable( | |||
| lazy(() => import("pages/app/ReceiptIssuingOrder")) | |||
| ); | |||
| const MailOrder = Loadable(lazy(() => import("pages/app/MailOrder"))); | |||
| const EmailOrder = Loadable(lazy(() => import("pages/app/EmailOrder"))); | |||
| //ダッシュボード ---------------------------- | |||
| const Dashboard = Loadable(lazy(() => import("pages/dashboard"))); | |||
| @@ -43,6 +43,8 @@ const PATHS = { | |||
| "/app/receipt-issuing-order/:token", | |||
| [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER)]: | |||
| "/app/receipt-issuing-order/mail", | |||
| [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_EMAIL_ORDER)]: | |||
| "/app/receipt-issuing-order/email", | |||
| // 契約関連 | |||
| [makePathKey(PageID.DASHBOARD_CONTRACT_LIST)]: | |||