| @@ -1,4 +1,4 @@ | |||||
| import { ApiId, HttpMethod, makeParam, request } from ".."; | |||||
| import { ApiId, HttpMethod, TimestampRequest, makeParam, request } from ".."; | |||||
| import { getUrl } from "../url"; | import { getUrl } from "../url"; | ||||
| export type AppReceiptIssuingOrder = { | export type AppReceiptIssuingOrder = { | ||||
| @@ -56,3 +56,18 @@ export const mailRequest = async (data: MailOrderRequest) => { | |||||
| }); | }); | ||||
| return res; | 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++, | RECEIPT_ISSUING_ORDER_CONFIRM: id++, | ||||
| DOWNLOAD_RECEIPT: id++, | DOWNLOAD_RECEIPT: id++, | ||||
| RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, | RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, | ||||
| RECEIPT_ISSUING_ORDER_EMAIL_ORDER: id++, | |||||
| // DASHBOARD向け------------------------------- | // DASHBOARD向け------------------------------- | ||||
| CHANGE_CONTRACT: id++, | 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_ORDER]: "receipt-issuing-order/mail-order", | ||||
| [A.RECEIPT_ISSUING_ORDER_MAIL_POST_COMPLETE]: | [A.RECEIPT_ISSUING_ORDER_MAIL_POST_COMPLETE]: | ||||
| "receipt-issuing-order/mail-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_HANDELRS]: "receipt-issuing-order/handlers", | ||||
| [A.RECEIPT_ISSUING_ORDER_CHANGE_HANDELR]: | [A.RECEIPT_ISSUING_ORDER_CHANGE_HANDELR]: | ||||
| "receipt-issuing-order/change-handler", | "receipt-issuing-order/change-handler", | ||||
| @@ -8,6 +8,7 @@ export const PageID = { | |||||
| APP_RECEIPT_ISSUING_ORDER_INDEX: id++, | APP_RECEIPT_ISSUING_ORDER_INDEX: id++, | ||||
| APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, | APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, | ||||
| APP_RECEIPT_ISSUING_ORDER_EMAIL_ORDER: id++, | |||||
| DASHBOARD_OVERVIEW: 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 = () => { | const handleClickMail = () => { | ||||
| navigateWhenChanged(getPath(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER)); | navigateWhenChanged(getPath(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER)); | ||||
| }; | }; | ||||
| const handleClickEmail = () => { | |||||
| navigateWhenChanged(getPath(PageID.APP_RECEIPT_ISSUING_ORDER_EMAIL_ORDER)); | |||||
| }; | |||||
| const element = ( | const element = ( | ||||
| <Stack spacing={1}> | <Stack spacing={1}> | ||||
| @@ -31,7 +34,9 @@ export default function useReceiptIssuingOrderSelectHowToGet() { | |||||
| </Button> | </Button> | ||||
| </Box> | </Box> | ||||
| <Box> | <Box> | ||||
| <Button variant="contained">メール送信依頼</Button> | |||||
| <Button variant="contained" onClick={handleClickEmail}> | |||||
| メール送信依頼 | |||||
| </Button> | |||||
| </Box> | </Box> | ||||
| {!order?.status_order_mail_datetime && ( | {!order?.status_order_mail_datetime && ( | ||||
| <Box> | <Box> | ||||
| @@ -50,6 +50,10 @@ const AppRoutes = (): RouteObject => ({ | |||||
| path: getRoute(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER), | path: getRoute(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER), | ||||
| element: <MailOrder />, | 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")) | lazy(() => import("pages/app/ReceiptIssuingOrder")) | ||||
| ); | ); | ||||
| const MailOrder = Loadable(lazy(() => import("pages/app/MailOrder"))); | const MailOrder = Loadable(lazy(() => import("pages/app/MailOrder"))); | ||||
| const EmailOrder = Loadable(lazy(() => import("pages/app/EmailOrder"))); | |||||
| //ダッシュボード ---------------------------- | //ダッシュボード ---------------------------- | ||||
| const Dashboard = Loadable(lazy(() => import("pages/dashboard"))); | const Dashboard = Loadable(lazy(() => import("pages/dashboard"))); | ||||
| @@ -43,6 +43,8 @@ const PATHS = { | |||||
| "/app/receipt-issuing-order/:token", | "/app/receipt-issuing-order/:token", | ||||
| [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER)]: | [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER)]: | ||||
| "/app/receipt-issuing-order/mail", | "/app/receipt-issuing-order/mail", | ||||
| [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_EMAIL_ORDER)]: | |||||
| "/app/receipt-issuing-order/email", | |||||
| // 契約関連 | // 契約関連 | ||||
| [makePathKey(PageID.DASHBOARD_CONTRACT_LIST)]: | [makePathKey(PageID.DASHBOARD_CONTRACT_LIST)]: | ||||