Procházet zdrojové kódy

申込とキャンセルの対応

develop
sosuke.iwabuchi před 2 roky
rodič
revize
a21d8d6522
9 změnil soubory, kde provedl 495 přidání a 1 odebrání
  1. +7
    -1
      src/api/index.ts
  2. +48
    -0
      src/api/season-ticket-contract-entry.ts
  3. +43
    -0
      src/api/season-ticket-contract-selection.ts
  4. +9
    -0
      src/api/url.ts
  5. +3
    -0
      src/pages/index.ts
  6. +130
    -0
      src/pages/season-ticket-contract-selection/cancel.tsx
  7. +227
    -0
      src/pages/season-ticket-contract-selection/entry.tsx
  8. +22
    -0
      src/routes/index.tsx
  9. +6
    -0
      src/routes/path.ts

+ 7
- 1
src/api/index.ts Zobrazit soubor

@@ -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++,


+ 48
- 0
src/api/season-ticket-contract-entry.ts Zobrazit soubor

@@ -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<SeasonTicketContractEntryResponse>({
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;
};

+ 43
- 0
src/api/season-ticket-contract-selection.ts Zobrazit soubor

@@ -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<SeasonTicketContractEntryResponse>({
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;
};

+ 9
- 0
src/api/url.ts Zobrazit soubor

@@ -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",


+ 3
- 0
src/pages/index.ts Zobrazit soubor

@@ -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;


+ 130
- 0
src/pages/season-ticket-contract-selection/cancel.tsx Zobrazit soubor

@@ -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 (
<Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center">
<Stack spacing={3}>
<Box>{generalErrorMessage ? generalErrorMessage : "認証失敗"}</Box>
</Stack>
</Box>
);
}

if (mode === "confirm") {
return (
<Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center">
<Stack spacing={3}>
<Typography variant="h5">申込キャンセル</Typography>
<Box>下記内容で申込を承っております。</Box>
<Box>
<Table size="small">
<TableBody>
<TableRow>
<TableCell>駐車場</TableCell>
<TableCell>
<TextFieldEx value={parkingName} readOnly fullWidth />
</TableCell>
</TableRow>
<TableRow>
<TableCell>プラン</TableCell>
<TableCell>
<TextFieldEx value={planName} readOnly fullWidth />
</TableCell>
</TableRow>
<TableRow>
<TableCell>申込日時</TableCell>
<TableCell>
<TextFieldEx value={entryDatetime} readOnly fullWidth />
</TableCell>
</TableRow>
</TableBody>
</Table>
</Box>
<Button variant="contained" onClick={handleSubmit}>
キャンセルする
</Button>
</Stack>
</Box>
);
}
if (mode === "done") {
return (
<Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center">
<Stack spacing={3}>
<Typography variant="h5">申込キャンセル</Typography>
<Box>キャンセルを承りました。</Box>
</Stack>
</Box>
);
}
return null;
}

+ 227
- 0
src/pages/season-ticket-contract-selection/entry.tsx Zobrazit soubor

@@ -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 (
<Box>
<StackRow>
<Typography variant="subtitle1">〇{label}</Typography>
<RequireChip require={require ?? false} />
</StackRow>
{children}
</Box>
);
}

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<SeasonTicketContractEntry | null>(null);

const form = useForm<FormProps>({
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 (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center">
<Stack spacing={3}>
<Typography variant="h5">申込</Typography>
{mode === "initialize" && <Typography>読込中</Typography>}
{mode === "failed" && (
<Typography>{generalErrorMessage ?? "認証失敗"}</Typography>
)}
{mode === "input" && (
<>
<InputAlert formState={form.formState} />
<AreaBox label="駐車場名">
<RHFTextField name="parking_name" readOnly />
</AreaBox>
<AreaBox label="プラン名">
<RHFTextField name="plan_name" readOnly />
</AreaBox>
<AreaBox label="台数">
<RHFTextField name="car_amount" readOnly />
</AreaBox>
<AreaBox label="住所" require>
<RHFTextField name="address" />
</AreaBox>
<AreaBox label="電話番号" require>
<RHFTextField name="phone_no" />
</AreaBox>
<Button
variant="contained"
onClick={() => {
setMode("confirm");
}}
>
次へ
</Button>
</>
)}
{mode === "confirm" && (
<>
<Box>
<Table size="small">
<TableBody>
<TableRow>
<TableCell>駐車場名</TableCell>
<TableCell>
<TextFieldEx
value={form.getValues("parking_name")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>プラン名</TableCell>
<TableCell>
<TextFieldEx
value={form.getValues("plan_name")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>住所</TableCell>
<TableCell>
<TextFieldEx
value={form.getValues("address")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>電話番号</TableCell>
<TableCell>
<TextFieldEx
value={form.getValues("phone_no")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
</Box>
<Button variant="contained" type="submit">
確定
</Button>
</>
)}
{mode === "done" && <Box>申込受付完了しました</Box>}
</Stack>
</Box>
</FormProvider>
);
}

+ 22
- 0
src/routes/index.tsx Zobrazit soubor

@@ -45,6 +45,19 @@ const AuthRoutes = (): RouteObject => ({
},
],
});
const SeasonTicketEntryRoutes = (): RouteObject => ({
element: <SimpleLayout />,
children: [
{
path: getRoute(PageID.SEASON_TICKET_CONTRACT_SELECTION_ENTRY),
element: <SeasonTicketContractSelectionEntry />,
},
{
path: getRoute(PageID.SEASON_TICKET_CONTRACT_ENTRY_CANCEL),
element: <SeasonTicketContractEntryCancel />,
},
],
});

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")));


+ 6
- 0
src/routes/path.ts Zobrazit soubor

@@ -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,



Načítá se…
Zrušit
Uložit