| @@ -54,6 +54,8 @@ export const ApiId = { | |||
| QRサービス券取得用トークン取得: id++, | |||
| QRサービス券取得用トークンリフレッシュ: id++, | |||
| QRサービス券取得: id++, | |||
| QRサービス券承認チェック: id++, | |||
| QRサービス券承認: id++, | |||
| } as const; | |||
| export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | |||
| @@ -1,7 +1,7 @@ | |||
| import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api"; | |||
| import { getUrl } from "./url"; | |||
| import { string } from "yup"; | |||
| import { 駐車場マスタ } from "./parking"; | |||
| import { サービス券マスタ, 駐車場マスタ } from "./parking"; | |||
| export type QRサービス券駐車場グループ = { | |||
| id: string; | |||
| @@ -135,3 +135,49 @@ export const QRサービス券取得用トークンリフレッシュ = async ( | |||
| }); | |||
| return res; | |||
| }; | |||
| export type QRサービス券承認チェックReturn = { | |||
| parking: { | |||
| parking_name: string; | |||
| parking_management_code: string; | |||
| publishing_terminal_code: string; | |||
| publishing_date: Date; | |||
| publishing_no: number; | |||
| }; | |||
| discount_tickets: サービス券マスタ[]; | |||
| }; | |||
| // -------QRサービス券承認チェック--------------- | |||
| export type QRサービス券承認チェックRequest = { | |||
| data: string; | |||
| }; | |||
| export type QRサービス券承認チェックResponse = { | |||
| data: QRサービス券承認チェックReturn; | |||
| } & APICommonResponse; | |||
| export const QRサービス券承認チェック = async ( | |||
| param: QRサービス券承認チェックRequest | |||
| ) => { | |||
| const res = await request<QRサービス券承認チェックResponse>({ | |||
| url: getUrl(ApiId.QRサービス券承認チェック), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(param), | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------QRサービス券承認--------------- | |||
| export type QRサービス券承認Request = { | |||
| parking_management_code: string; | |||
| publishing_terminal_code: string; | |||
| publishing_date: Date; | |||
| publishing_no: string; | |||
| discount_ticket_code: string; | |||
| }; | |||
| export type QRサービス券承認Response = {} & APICommonResponse; | |||
| export const QRサービス券承認 = async (param: QRサービス券承認Request) => { | |||
| const res = await request<QRサービス券承認Response>({ | |||
| url: getUrl(ApiId.QRサービス券承認), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(param), | |||
| }); | |||
| return res; | |||
| }; | |||
| @@ -53,6 +53,8 @@ const urls = { | |||
| [A.QRサービス券取得用トークンリフレッシュ]: | |||
| "qr-service/acquisition/token/refresh", | |||
| [A.QRサービス券取得]: "qr-service/get-ticket", | |||
| [A.QRサービス券承認チェック]: "qr-service/certification/check-data-format", | |||
| [A.QRサービス券承認]: "qr-service/certification", | |||
| }; | |||
| const prefixs = { | |||
| @@ -34,6 +34,7 @@ const 認可別許可ルート: { | |||
| ...認証後共通ルート, | |||
| P.サービス券発行用QRコード, | |||
| P.サービス券利用履歴, | |||
| P.QRサービス券承認, | |||
| ], | |||
| }; | |||
| @@ -51,7 +51,6 @@ export default function サービス券発行用QRコード() { | |||
| useEffect(() => { | |||
| if (url) { | |||
| console.log({ url }); | |||
| QRCode.toDataURL(url, { | |||
| errorCorrectionLevel: "H", | |||
| }).then((data: string) => { | |||
| @@ -76,6 +75,8 @@ export default function サービス券発行用QRコード() { | |||
| <Box mx="auto"> | |||
| {!!qr && <img src={qr} width={size} height={size}></img>} | |||
| </Box> | |||
| <Box mx="auto">{url}</Box> | |||
| <Box mx="auto"> | |||
| <RefreshButton fetch={fetch}>QRコードリフレッシュ</RefreshButton> | |||
| </Box> | |||
| @@ -32,6 +32,7 @@ export const PageID = { | |||
| サービス券利用履歴: id++, | |||
| QRサービス券発行申請: id++, | |||
| QRサービス券承認: id++, | |||
| // ダッシュボード系 END ---------------------------------- | |||
| PAGE_403: id++, | |||
| @@ -0,0 +1,147 @@ | |||
| import { Box, Button, Paper, Stack, Typography } from "@mui/material"; | |||
| import { サービス券マスタ } from "api/parking"; | |||
| import { | |||
| QRサービス券承認, | |||
| QRサービス券承認チェック, | |||
| QRサービス券承認チェックReturn, | |||
| } from "api/qr-service"; | |||
| import { FormProvider, RHFAutoComplete } from "components/hook-form"; | |||
| import { | |||
| AutoCompleteOption, | |||
| AutoCompleteOptionType, | |||
| getValue, | |||
| } from "components/hook-form/RHFAutoComplete"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { useEffect, useMemo, useState } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { useParams } from "react-router-dom"; | |||
| import { sprintf } from "sprintf-js"; | |||
| type FormProps = { | |||
| discount_ticket_code: AutoCompleteOptionType; | |||
| }; | |||
| export default function Main() { | |||
| const { data: paramData } = useParams(); | |||
| const { error } = useSnackbarCustom(); | |||
| const [isFailed, setIsfailed] = useState(false); | |||
| const [done, setDone] = useState(false); | |||
| const [checkData, setCheckData] = useState< | |||
| QRサービス券承認チェックReturn | undefined | |||
| >(undefined); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| discount_ticket_code: null, | |||
| }, | |||
| }); | |||
| const { callAPI: callQRサービス券承認チェック, generalErrorMessage } = | |||
| useAPICall({ | |||
| apiMethod: QRサービス券承認チェック, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| setCheckData(data); | |||
| }, | |||
| onFailed: () => { | |||
| setIsfailed(true); | |||
| }, | |||
| }); | |||
| const { callAPI: callQRサービス券承認 } = useAPICall({ | |||
| apiMethod: QRサービス券承認, | |||
| backDrop: true, | |||
| form, | |||
| onSuccess: () => { | |||
| setDone(true); | |||
| }, | |||
| onFailed: (res) => { | |||
| error(sprintf("失敗しました。(%s)", res?.messages.general ?? "")); | |||
| }, | |||
| }); | |||
| const options: AutoCompleteOption[] = useMemo(() => { | |||
| if (checkData === undefined) return []; | |||
| return checkData.discount_tickets.map((ele) => ({ | |||
| label: ele.ticket_name, | |||
| value: String(ele.discount_ticket_code), | |||
| })); | |||
| }, [checkData]); | |||
| const handleSubmit = (data: FormProps) => { | |||
| if (checkData === undefined) return; | |||
| const { publishing_no } = checkData.parking; | |||
| callQRサービス券承認({ | |||
| ...checkData.parking, | |||
| publishing_no: String(publishing_no), | |||
| discount_ticket_code: getValue(data.discount_ticket_code), | |||
| }); | |||
| }; | |||
| useEffect(() => { | |||
| if (paramData) { | |||
| callQRサービス券承認チェック({ | |||
| data: paramData, | |||
| }); | |||
| } | |||
| }, [paramData]); | |||
| if (isFailed === true) { | |||
| return ( | |||
| <Box> | |||
| <Stack sx={{ p: 2 }}> | |||
| <Box mx="auto"> | |||
| <Typography>無効なURLもしくは割引できない駐車場です。</Typography> | |||
| <Typography>({generalErrorMessage})</Typography> | |||
| </Box> | |||
| </Stack> | |||
| </Box> | |||
| ); | |||
| } | |||
| if (done === true) { | |||
| return ( | |||
| <Box> | |||
| <Stack sx={{ p: 2 }}> | |||
| <Box mx="auto"> | |||
| <Typography>認証しました</Typography> | |||
| </Box> | |||
| </Stack> | |||
| </Box> | |||
| ); | |||
| } | |||
| if (checkData === undefined) return null; | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Box> | |||
| <Stack spacing={2}> | |||
| <Paper sx={{ p: 2 }}> | |||
| <Stack spacing={2} mx="auto" maxWidth={500}> | |||
| <Box> | |||
| <Typography>駐車場名</Typography> | |||
| <Typography variant="subtitle1"> | |||
| <strong>{checkData.parking.parking_name}</strong> | |||
| </Typography> | |||
| </Box> | |||
| <Box> | |||
| <Typography>サービス券の選択</Typography> | |||
| <RHFAutoComplete | |||
| name="discount_ticket_code" | |||
| options={options} | |||
| size="small" | |||
| /> | |||
| </Box> | |||
| <Box> | |||
| <Button type="submit">承認</Button> | |||
| </Box> | |||
| </Stack> | |||
| </Paper> | |||
| </Stack> | |||
| </Box> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| @@ -48,6 +48,19 @@ const QRサービス券Routes = (): RouteObject => ({ | |||
| }, | |||
| ], | |||
| }); | |||
| const 認証後QRサービス券Routes = (): RouteObject => ({ | |||
| element: ( | |||
| <AuthGuard> | |||
| <QRServiceSimpleLayout /> | |||
| </AuthGuard> | |||
| ), | |||
| children: [ | |||
| { | |||
| path: getRoute(PageID.QRサービス券承認), | |||
| element: <QRサービス券承認 />, | |||
| }, | |||
| ], | |||
| }); | |||
| export function Routes() { | |||
| const { initialized } = useAuth(); | |||
| @@ -56,6 +69,7 @@ export function Routes() { | |||
| CommonRoutes(), | |||
| AuthRoutes(), | |||
| QRサービス券Routes(), | |||
| 認証後QRサービス券Routes(), | |||
| ...DashboardRoutes(), | |||
| { | |||
| path: "403", | |||
| @@ -87,6 +101,9 @@ const Logout = Loadable(lazy(() => import("pages/auth/logout"))); | |||
| const QRサービス券発行申請 = Loadable( | |||
| lazy(() => import("pages/qr-service/QRサービス券発行申請")) | |||
| ); | |||
| const QRサービス券承認 = Loadable( | |||
| lazy(() => import("pages/qr-service/QRサービス券承認")) | |||
| ); | |||
| // その他 --------------------------------- | |||
| @@ -64,7 +64,8 @@ const PATHS_DASHBOARD = { | |||
| [makePathKey([PageID.店舗詳細, TabID.店舗詳細_QR取得設定])]: | |||
| "/dashboard/shop/detail/setting/qr/acquisition/:shopId", | |||
| [makePathKey(PageID.サービス券発行用QRコード)]: "/dashboard/qrcode/generate", | |||
| [makePathKey(PageID.サービス券発行用QRコード)]: | |||
| "/dashboard/qr-service/acquisition/generate", | |||
| [makePathKey(PageID.サービス券利用履歴)]: "/dashboard/qrcode/history", | |||
| }; | |||
| @@ -78,6 +79,7 @@ const PATHS = { | |||
| [makePathKey(PageID.成り代わり終了)]: "/role/switch/end", | |||
| [makePathKey(PageID.QRサービス券発行申請)]: "/qr-service/acquitision/:token", | |||
| [makePathKey(PageID.QRサービス券承認)]: "/qr-service/certificate/:data", | |||
| // ダッシュボード---------------- | |||
| ...PATHS_DASHBOARD, | |||