diff --git a/src/api/index.ts b/src/api/index.ts index cef86bc..c0c291e 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -17,7 +17,7 @@ export const ApiId = { PASSWORD_SETTING_START: id++, PASSWORD_SETTING_VERIFY: id++, - // 定期券連 --------------------------------- + // 定期関連--------------------------------- SEASON_TICKET_CONTRACTS: id++, PAYMENT_PLANS: id++, SEASON_TICKET_CONTRACT_STICKER_RE_ORDER: id++, @@ -30,6 +30,12 @@ export const ApiId = { SEASON_TICKET_CONTRACT_UPLOAD_STUDENT_LICENSE_IMAGES: id++, SEASON_TICKET_CONTRACT_UPLOAD_OTHER_LICENSE_IMAGES: id++, + // 定期申込関連--------------------------------- + SEASON_TICKET_CONTRACT_ENTRY_INFO: id++, + SEASON_TICKET_CONTRACT_ENTRY_CANCEL: id++, + SEASON_TICKET_CONTRACT_SELECTION_INFO: id++, + SEASON_TICKET_CONTRACT_SELECTION_ENTRY: id++, + // 問い合わせ関連------------------------------- FAQ: id++, FAQ_GENRES: id++, diff --git a/src/api/season-ticket-contract-entry.ts b/src/api/season-ticket-contract-entry.ts new file mode 100644 index 0000000..7f5cff9 --- /dev/null +++ b/src/api/season-ticket-contract-entry.ts @@ -0,0 +1,48 @@ +import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api"; +import { getUrl } from "./url"; + +export type SeasonTicketContractEntry = { + customer_name: string | null; + parking_name: string | null; + entry_datetime: string | null; + plan_name: string | null; + address: string | null; + phone_no: string | null; + car_amount: string | null; + revision: number; +}; + +// -------申込内容取得--------------- +type SeasonTicketContractEntryRequest = { + record_no: string; + fs: string; // ハッシュパスワード +}; +type SeasonTicketContractEntryResponse = { + data: SeasonTicketContractEntry; +} & APICommonResponse; +export const getSeasonTicketContractEntry = async ( + data: SeasonTicketContractEntryRequest +) => { + const res = await request({ + url: getUrl(ApiId.SEASON_TICKET_CONTRACT_ENTRY_INFO), + method: HttpMethod.GET, + data: new URLSearchParams(data), + }); + return res; +}; + +// -------申込キャンセル--------------- +type SeasonTicketContractEntryCancelRequest = { + record_no: string; + fs: string; // ハッシュパスワード +}; +export const cancelSeasonTicketContractEntry = async ( + data: SeasonTicketContractEntryCancelRequest +) => { + const res = await request({ + url: getUrl(ApiId.SEASON_TICKET_CONTRACT_ENTRY_CANCEL), + method: HttpMethod.POST, + data: makeParam(data), + }); + return res; +}; diff --git a/src/api/season-ticket-contract-selection.ts b/src/api/season-ticket-contract-selection.ts new file mode 100644 index 0000000..f236f50 --- /dev/null +++ b/src/api/season-ticket-contract-selection.ts @@ -0,0 +1,43 @@ +import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "api"; +import { getUrl } from "./url"; +import { SeasonTicketContractEntry } from "./season-ticket-contract-entry"; + +// -------申込内容取得--------------- +type SeasonTicketContractEntryRequest = { + selection_record_no: string; + entry_record_no: string; + fs: string; // ハッシュパスワード +}; +type SeasonTicketContractEntryResponse = { + data: SeasonTicketContractEntry; +} & APICommonResponse; +export const getSeasonTicketContractSelection = async ( + data: SeasonTicketContractEntryRequest +) => { + const res = await request({ + url: getUrl(ApiId.SEASON_TICKET_CONTRACT_SELECTION_INFO), + method: HttpMethod.GET, + data: new URLSearchParams(data), + }); + return res; +}; + +// -------選考申込--------------- +type SeasonTicketContractSelectionEntryRequest = { + selection_record_no: string; + entry_record_no: string; + fs: string; // ハッシュパスワード + + address: string; + phone_no: string; +}; +export const entrySeasonTicketContractSelection = async ( + data: SeasonTicketContractSelectionEntryRequest +) => { + const res = await request({ + url: getUrl(ApiId.SEASON_TICKET_CONTRACT_SELECTION_ENTRY), + method: HttpMethod.POST, + data: makeParam(data), + }); + return res; +}; diff --git a/src/api/url.ts b/src/api/url.ts index d490f58..ae4b357 100644 --- a/src/api/url.ts +++ b/src/api/url.ts @@ -31,6 +31,15 @@ const urls = { "upload/student-license-images", [A.SEASON_TICKET_CONTRACT_UPLOAD_OTHER_LICENSE_IMAGES]: "upload/other-license-images", + + [A.SEASON_TICKET_CONTRACT_ENTRY_INFO]: "season-ticket-contract/entry/info", + [A.SEASON_TICKET_CONTRACT_ENTRY_CANCEL]: + "season-ticket-contract/entry/cancel", + [A.SEASON_TICKET_CONTRACT_SELECTION_INFO]: + "season-ticket-contract/selection/info", + [A.SEASON_TICKET_CONTRACT_SELECTION_ENTRY]: + "season-ticket-contract/selection/entry", + [A.START_CHANGE_EMAIL]: "email/change/start", [A.VERIFY_CHANGE_EMAIL]: "email/change/verify", [A.CUSTOMER_UPDATE_INFO_ORDER]: "customer/update-info-order", diff --git a/src/pages/index.ts b/src/pages/index.ts index f6061d7..228f8b6 100644 --- a/src/pages/index.ts +++ b/src/pages/index.ts @@ -31,6 +31,9 @@ export const PageID = { DASHBOARD_USER_UPDATE_USER_INFO: id++, DASHBOARD_USER_BANK_REGISTER: id++, + SEASON_TICKET_CONTRACT_SELECTION_ENTRY: id++, + SEASON_TICKET_CONTRACT_ENTRY_CANCEL: id++, + PAGE_403: id++, PAGE_404: id++, } as const; diff --git a/src/pages/season-ticket-contract-selection/cancel.tsx b/src/pages/season-ticket-contract-selection/cancel.tsx new file mode 100644 index 0000000..5e85afd --- /dev/null +++ b/src/pages/season-ticket-contract-selection/cancel.tsx @@ -0,0 +1,130 @@ +import { + Box, + Button, + Stack, + Table, + TableBody, + TableCell, + TableRow, + Typography, +} from "@mui/material"; +import { + cancelSeasonTicketContractEntry, + getSeasonTicketContractEntry, +} from "api/season-ticket-contract-entry"; +import TextFieldEx from "components/form/TextFieldEx"; +import useAPICall from "hooks/useAPICall"; +import { useEffect, useState } from "react"; +import { useParams } from "react-router-dom"; + +export default function Cancel() { + const { entryRecordNo: paramEntryRecordNo, fs: paramFs } = useParams(); + + const [mode, setMode] = useState< + "initialize" | "failed" | "confirm" | "done" + >("initialize"); + + const [parkingName, setParkingName] = useState(""); + const [planName, setPlanName] = useState(""); + const [entryDatetime, setEntryDatetime] = useState(""); + + const { callAPI: callGetSeasonTicketContractEntry, generalErrorMessage } = + useAPICall({ + apiMethod: getSeasonTicketContractEntry, + backDrop: true, + onSuccess: ({ data }) => { + setParkingName(data.parking_name ?? "-"); + setPlanName(data.plan_name ?? "-"); + setEntryDatetime(data.entry_datetime ?? "-"); + setMode("confirm"); + }, + onFailed: () => { + setMode("failed"); + }, + }); + const { callAPI: callCancelSeasonTicketContractEntry } = useAPICall({ + apiMethod: cancelSeasonTicketContractEntry, + backDrop: true, + onSuccess: () => { + setMode("done"); + }, + onFailed: () => { + setMode("failed"); + }, + }); + + const handleSubmit = () => { + callCancelSeasonTicketContractEntry({ + record_no: paramEntryRecordNo ?? "", + fs: paramFs ?? "", + }); + }; + + useEffect(() => { + if (paramEntryRecordNo && paramFs) { + callGetSeasonTicketContractEntry({ + record_no: paramEntryRecordNo, + fs: paramFs, + }); + } + }, [paramEntryRecordNo, paramFs]); + + if (mode === "failed") { + return ( + + + {generalErrorMessage ? generalErrorMessage : "認証失敗"} + + + ); + } + + if (mode === "confirm") { + return ( + + + 申込キャンセル + 下記内容で申込を承っております。 + + + + + 駐車場 + + + + + + プラン + + + + + + 申込日時 + + + + + +
+
+ +
+
+ ); + } + if (mode === "done") { + return ( + + + 申込キャンセル + キャンセルを承りました。 + + + ); + } + return null; +} diff --git a/src/pages/season-ticket-contract-selection/entry.tsx b/src/pages/season-ticket-contract-selection/entry.tsx new file mode 100644 index 0000000..e8b5684 --- /dev/null +++ b/src/pages/season-ticket-contract-selection/entry.tsx @@ -0,0 +1,227 @@ +import { + Box, + Button, + Stack, + Table, + TableBody, + TableCell, + TableRow, + Typography, +} from "@mui/material"; +import { HasChildren } from "@types"; +import { SeasonTicketContractEntry } from "api/season-ticket-contract-entry"; +import { + entrySeasonTicketContractSelection, + getSeasonTicketContractSelection, +} from "api/season-ticket-contract-selection"; +import RequireChip from "components/chip/RequireChip"; +import InputAlert from "components/form/InputAlert"; +import TextFieldEx from "components/form/TextFieldEx"; +import { FormProvider, RHFTextField } from "components/hook-form"; +import StackRow from "components/stack/StackRow"; +import useAPICall from "hooks/useAPICall"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { useParams } from "react-router-dom"; +import { scrollToTop } from "utils/page"; + +type AreaBoxProps = { + label: string; + require?: boolean; +} & HasChildren; +function AreaBox({ label, children, require }: AreaBoxProps) { + return ( + + + 〇{label} + + + {children} + + ); +} + +type FormProps = { + address: string; + phone_no: string; + parking_name: string; + plan_name: string; + car_amount: string; +}; +export default function Entry() { + const { + selectionRecordNo: paramSelectionRecordNo, + entryRecordNo: paramEntryRecordNo, + fs: paramFs, + } = useParams(); + + const [mode, setMode] = useState< + "initialize" | "failed" | "input" | "confirm" | "done" + >("initialize"); + + const [entry, setEntry] = useState(null); + + const form = useForm({ + defaultValues: { + address: "", + phone_no: "", + parking_name: "", + plan_name: "", + car_amount: "", + }, + }); + + const { callAPI: callGetSeasonTicketContractSelection, generalErrorMessage } = + useAPICall({ + apiMethod: getSeasonTicketContractSelection, + backDrop: true, + form, + onSuccess: ({ data }) => { + setMode("input"); + setEntry(data); + }, + onFailed: () => { + setMode("failed"); + }, + }); + const { callAPI: callEntrySeasonTicketContractSelection } = useAPICall({ + apiMethod: entrySeasonTicketContractSelection, + backDrop: true, + form, + onSuccess: () => { + setMode("done"); + }, + onFailed: () => { + setMode("input"); + }, + }); + + const handleSubmit = (data: FormProps) => { + callEntrySeasonTicketContractSelection({ + ...data, + selection_record_no: paramSelectionRecordNo ?? "", + entry_record_no: paramEntryRecordNo ?? "", + fs: paramFs ?? "", + }); + }; + + useEffect(() => { + if (paramSelectionRecordNo && paramEntryRecordNo && paramFs) { + callGetSeasonTicketContractSelection({ + selection_record_no: paramSelectionRecordNo, + entry_record_no: paramEntryRecordNo, + fs: paramFs, + }); + } + }, [paramSelectionRecordNo, paramEntryRecordNo, paramFs]); + + useEffect(() => { + if (entry) { + form.setValue("address", entry.address ?? ""); + form.setValue("phone_no", entry.phone_no ?? ""); + form.setValue("parking_name", entry.parking_name ?? ""); + form.setValue("plan_name", entry.plan_name ?? ""); + form.setValue("car_amount", (entry.car_amount ?? "0") + "台"); + } + }, [entry]); + + useEffect(() => { + scrollToTop("auto"); + }, [mode]); + + return ( + + + + 申込 + {mode === "initialize" && 読込中} + {mode === "failed" && ( + {generalErrorMessage ?? "認証失敗"} + )} + {mode === "input" && ( + <> + + + + + + + + + + + + + + + + + + + )} + {mode === "confirm" && ( + <> + + + + + 駐車場名 + + + + + + プラン名 + + + + + + 住所 + + + + + + 電話番号 + + + + + +
+
+ + + )} + {mode === "done" && 申込受付完了しました} +
+
+
+ ); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index dfbafad..6824837 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -45,6 +45,19 @@ const AuthRoutes = (): RouteObject => ({ }, ], }); +const SeasonTicketEntryRoutes = (): RouteObject => ({ + element: , + children: [ + { + path: getRoute(PageID.SEASON_TICKET_CONTRACT_SELECTION_ENTRY), + element: , + }, + { + path: getRoute(PageID.SEASON_TICKET_CONTRACT_ENTRY_CANCEL), + element: , + }, + ], +}); export function Routes() { const { initialized } = useAuth(); @@ -52,6 +65,7 @@ export function Routes() { return useRoutes([ CommonRoutes(), AuthRoutes(), + SeasonTicketEntryRoutes(), ...DashboardRoutes(), { path: "403", @@ -77,6 +91,14 @@ const PasswordSettingVerify = Loadable( lazy(() => import("pages/auth/password-setting-verify")) ); +// 申込関連 +const SeasonTicketContractSelectionEntry = Loadable( + lazy(() => import("pages/season-ticket-contract-selection/entry")) +); +const SeasonTicketContractEntryCancel = Loadable( + lazy(() => import("pages/season-ticket-contract-selection/cancel")) +); + // その他 --------------------------------- const Page403 = Loadable(lazy(() => import("pages/common/Page403"))); diff --git a/src/routes/path.ts b/src/routes/path.ts index 2102069..de20bb3 100644 --- a/src/routes/path.ts +++ b/src/routes/path.ts @@ -79,6 +79,12 @@ const PATHS = { [makePathKey(PageID.USER_SETTING_PASSWORD_VERIFY)]: "/setting/password/verify/:token", + // 申込/選考 + [makePathKey(PageID.SEASON_TICKET_CONTRACT_SELECTION_ENTRY)]: + "/season-ticket-contract/selection/entry/:selectionRecordNo/:entryRecordNo/:fs", + [makePathKey(PageID.SEASON_TICKET_CONTRACT_ENTRY_CANCEL)]: + "/season-ticket-contract/entry/cancel/:entryRecordNo/:fs", + // ダッシュボード---------------- ...PATHS_DASHBOARD,