| @@ -54,6 +54,8 @@ export const ApiId = { | |||||
| QRサービス券取得用トークン取得: id++, | QRサービス券取得用トークン取得: id++, | ||||
| QRサービス券取得用トークンリフレッシュ: id++, | QRサービス券取得用トークンリフレッシュ: id++, | ||||
| QRサービス券取得: id++, | QRサービス券取得: id++, | ||||
| QRサービス券承認チェック: id++, | |||||
| QRサービス券承認: id++, | |||||
| } as const; | } as const; | ||||
| export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | ||||
| @@ -1,7 +1,7 @@ | |||||
| import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api"; | import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api"; | ||||
| import { getUrl } from "./url"; | import { getUrl } from "./url"; | ||||
| import { string } from "yup"; | import { string } from "yup"; | ||||
| import { 駐車場マスタ } from "./parking"; | |||||
| import { サービス券マスタ, 駐車場マスタ } from "./parking"; | |||||
| export type QRサービス券駐車場グループ = { | export type QRサービス券駐車場グループ = { | ||||
| id: string; | id: string; | ||||
| @@ -135,3 +135,49 @@ export const QRサービス券取得用トークンリフレッシュ = async ( | |||||
| }); | }); | ||||
| return res; | 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サービス券取得用トークンリフレッシュ]: | [A.QRサービス券取得用トークンリフレッシュ]: | ||||
| "qr-service/acquisition/token/refresh", | "qr-service/acquisition/token/refresh", | ||||
| [A.QRサービス券取得]: "qr-service/get-ticket", | [A.QRサービス券取得]: "qr-service/get-ticket", | ||||
| [A.QRサービス券承認チェック]: "qr-service/certification/check-data-format", | |||||
| [A.QRサービス券承認]: "qr-service/certification", | |||||
| }; | }; | ||||
| const prefixs = { | const prefixs = { | ||||
| @@ -34,6 +34,7 @@ const 認可別許可ルート: { | |||||
| ...認証後共通ルート, | ...認証後共通ルート, | ||||
| P.サービス券発行用QRコード, | P.サービス券発行用QRコード, | ||||
| P.サービス券利用履歴, | P.サービス券利用履歴, | ||||
| P.QRサービス券承認, | |||||
| ], | ], | ||||
| }; | }; | ||||
| @@ -51,7 +51,6 @@ export default function サービス券発行用QRコード() { | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (url) { | if (url) { | ||||
| console.log({ url }); | |||||
| QRCode.toDataURL(url, { | QRCode.toDataURL(url, { | ||||
| errorCorrectionLevel: "H", | errorCorrectionLevel: "H", | ||||
| }).then((data: string) => { | }).then((data: string) => { | ||||
| @@ -76,6 +75,8 @@ export default function サービス券発行用QRコード() { | |||||
| <Box mx="auto"> | <Box mx="auto"> | ||||
| {!!qr && <img src={qr} width={size} height={size}></img>} | {!!qr && <img src={qr} width={size} height={size}></img>} | ||||
| </Box> | </Box> | ||||
| <Box mx="auto">{url}</Box> | |||||
| <Box mx="auto"> | <Box mx="auto"> | ||||
| <RefreshButton fetch={fetch}>QRコードリフレッシュ</RefreshButton> | <RefreshButton fetch={fetch}>QRコードリフレッシュ</RefreshButton> | ||||
| </Box> | </Box> | ||||
| @@ -32,6 +32,7 @@ export const PageID = { | |||||
| サービス券利用履歴: id++, | サービス券利用履歴: id++, | ||||
| QRサービス券発行申請: id++, | QRサービス券発行申請: id++, | ||||
| QRサービス券承認: id++, | |||||
| // ダッシュボード系 END ---------------------------------- | // ダッシュボード系 END ---------------------------------- | ||||
| PAGE_403: id++, | 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() { | export function Routes() { | ||||
| const { initialized } = useAuth(); | const { initialized } = useAuth(); | ||||
| @@ -56,6 +69,7 @@ export function Routes() { | |||||
| CommonRoutes(), | CommonRoutes(), | ||||
| AuthRoutes(), | AuthRoutes(), | ||||
| QRサービス券Routes(), | QRサービス券Routes(), | ||||
| 認証後QRサービス券Routes(), | |||||
| ...DashboardRoutes(), | ...DashboardRoutes(), | ||||
| { | { | ||||
| path: "403", | path: "403", | ||||
| @@ -87,6 +101,9 @@ const Logout = Loadable(lazy(() => import("pages/auth/logout"))); | |||||
| const QRサービス券発行申請 = Loadable( | const QRサービス券発行申請 = Loadable( | ||||
| lazy(() => import("pages/qr-service/QRサービス券発行申請")) | 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取得設定])]: | [makePathKey([PageID.店舗詳細, TabID.店舗詳細_QR取得設定])]: | ||||
| "/dashboard/shop/detail/setting/qr/acquisition/:shopId", | "/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", | [makePathKey(PageID.サービス券利用履歴)]: "/dashboard/qrcode/history", | ||||
| }; | }; | ||||
| @@ -78,6 +79,7 @@ const PATHS = { | |||||
| [makePathKey(PageID.成り代わり終了)]: "/role/switch/end", | [makePathKey(PageID.成り代わり終了)]: "/role/switch/end", | ||||
| [makePathKey(PageID.QRサービス券発行申請)]: "/qr-service/acquitision/:token", | [makePathKey(PageID.QRサービス券発行申請)]: "/qr-service/acquitision/:token", | ||||
| [makePathKey(PageID.QRサービス券承認)]: "/qr-service/certificate/:data", | |||||
| // ダッシュボード---------------- | // ダッシュボード---------------- | ||||
| ...PATHS_DASHBOARD, | ...PATHS_DASHBOARD, | ||||