Selaa lähdekoodia

申請 整備

develop
sosuke.iwabuchi 2 vuotta sitten
vanhempi
commit
2b33471154
7 muutettua tiedostoa jossa 443 lisäystä ja 66 poistoa
  1. +3
    -0
      src/api/auth.ts
  2. +13
    -3
      src/api/season-ticket-contract.ts
  3. +10
    -5
      src/components/form/InputAlert.tsx
  4. +24
    -16
      src/components/hook-form/RHFCheckbox.tsx
  5. +97
    -9
      src/pages/dashboard/contract/season-ticket-re-order.tsx
  6. +160
    -23
      src/pages/dashboard/contract/terminate-order.tsx
  7. +136
    -10
      src/pages/dashboard/contract/update-vehicle-info-order.tsx

+ 3
- 0
src/api/auth.ts Näytä tiedosto

@@ -4,6 +4,9 @@ import { getUrl } from "./url";
export type Me = {
customer_name: string;
email: string;
zip_code: string;
address: string;
phone_no: string;
};

type MeResponse = {


+ 13
- 3
src/api/season-ticket-contract.ts Näytä tiedosto

@@ -14,6 +14,7 @@ export type SeasonTicketContract = {
room_no: string | null;
season_ticket_seq_no: string | null;
vehicle_no: string | null;
register_no: string | null;
vehicle_type: string | null;
contract_start_date: string | null;
contract_end_date: string | null;
@@ -78,10 +79,13 @@ export const reOrderSticker = async (data: StickerReOrderRequest) => {
// -------定期券再発行依頼------------------
type SeasonTicketReOrderRequest = {
season_ticket_contract_record_no: string;
parking_name: string;
reason: string;
memo?: string;
};
export const reOrderSeasonTicket = async (data: SeasonTicketReOrderRequest) => {
const res = await request({
url: getUrl(ApiId.SEASON_TICKET_CONTRACT_STICKER_RE_ORDER),
url: getUrl(ApiId.SEASON_TICKET_CONTRACT_SEASON_TICKET_RE_ORDER),
method: HttpMethod.POST,
data: makeParam(data),
});
@@ -107,9 +111,10 @@ export const orderParkingCertificate = async (
// -------解約依頼------------------
type TerminateOrderRequest = {
season_ticket_contract_record_no: string;
date: string;
parking_name: string;
terminate_date: string;
reason: string;
other_reason: string;
reason_detail: string;
opinion: string;
memo: string;
};
@@ -146,6 +151,11 @@ export const getSeasonTicketContractTerminateOptions = async (
// -------車両情報変更依頼------------------
type UpdateVehicleInfoOrderRequest = {
season_ticket_contract_record_no: string;
parking_name: string;
change_date: string;
vehicle_no?: string;
register_no?: string;
memo?: string;
};
export const orderUpdateVehicleInfo = async (
data: UpdateVehicleInfoOrderRequest


+ 10
- 5
src/components/form/InputAlert.tsx Näytä tiedosto

@@ -1,13 +1,15 @@
import { Alert, SxProps } from "@mui/material";
import { APIErrorType } from "hooks/useAPICall";
import React, { useEffect, useMemo, useRef } from "react";
import { FieldErrors, FieldValues, FormState } from "react-hook-form";

type Props = {
error: APIErrorType;
type Props<T extends FieldValues> = {
error?: APIErrorType;
sx?: SxProps;
getMessage?: (errpr: APIErrorType) => string | null;
message?: string;
errorScroll?: boolean;
formState?: FormState<T>;
};
const InputAlert = ({
error,
@@ -15,26 +17,29 @@ const InputAlert = ({
getMessage,
message: errorMessage,
errorScroll,
}: Props) => {
formState,
}: Props<FieldValues>) => {
const ref = useRef<HTMLDivElement>(null);

const message = useMemo(() => {
if (errorMessage) {
return errorMessage;
}
if (getMessage) {
if (getMessage && error) {
const m = getMessage(error);
if (m !== null) {
return m;
}
}
if (!!formState && formState.isSubmitted && !formState.isValid)
return "入力項目を確認してください。";
if (error === APIErrorType.INPUT) return "入力項目を確認してください。";
if (error === APIErrorType.SERVER)
return "エラーが発生しております。しばらくお待ちください。";
if (error === APIErrorType.EXCLUSIVE)
return "ページの期限が切れています。再度読込を行ってください";
return "";
}, [error, errorMessage, getMessage]);
}, [error, errorMessage, getMessage, formState]);

// エラー時に自動的にスクロール制御
useEffect(() => {


+ 24
- 16
src/components/hook-form/RHFCheckbox.tsx Näytä tiedosto

@@ -7,6 +7,8 @@ import {
FormGroup,
FormControlLabelProps,
Typography,
Stack,
Alert,
} from "@mui/material";
import { useMemo } from "react";

@@ -100,28 +102,34 @@ export function RHFMultiCheckbox({
<Controller
name={name}
control={control}
render={({ field }) => {
render={({ field, fieldState: { error } }) => {
const onSelected = (option: string) =>
field.value.includes(option)
? field.value.filter((value: string) => value !== option)
: [...field.value, option];

return (
<FormGroup>
{options.map((option) => (
<FormControlLabel
key={option.value}
control={
<Checkbox
checked={field.value.includes(option.value)}
onChange={() => field.onChange(onSelected(option.value))}
/>
}
label={<Typography variant="body2">{option.label}</Typography>}
{...other}
/>
))}
</FormGroup>
<Stack>
{!!error && <Alert severity="warning">{error.message}</Alert>}
<FormGroup>
{options.map((option) => (
<FormControlLabel
key={option.value}
control={
<Checkbox
checked={field.value.includes(option.value)}
onChange={() => field.onChange(onSelected(option.value))}
color="error"
/>
}
label={
<Typography variant="body2">{option.label}</Typography>
}
{...other}
/>
))}
</FormGroup>
</Stack>
);
}}
/>


+ 97
- 9
src/pages/dashboard/contract/season-ticket-re-order.tsx Näytä tiedosto

@@ -1,10 +1,22 @@
import { Box, Button, Stack, Typography } from "@mui/material";
import { yupResolver } from "@hookform/resolvers/yup";
import {
Box,
Button,
Stack,
Table,
TableBody,
TableCell,
TableRow,
Typography,
} from "@mui/material";
import { HasChildren } from "@types";
import {
reOrderSeasonTicket,
reOrderSticker,
} from "api/season-ticket-contract";
import TextFieldEx from "components/form/TextFieldEx";
import { FormProvider, RHFTextField } from "components/hook-form";
import StackRow from "components/stack/StackRow";
import { useSeasonTicketContractContext } from "contexts/dashboard/SeasonTicketContractContext";
import useAPICall from "hooks/useAPICall";
import useDashboard from "hooks/useDashBoard";
@@ -14,6 +26,7 @@ import { PageID, TabID } from "pages";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";
import { object, string } from "yup";

type AreaBoxProps = {
label: string;
@@ -28,7 +41,8 @@ function AreaBox({ label, children }: AreaBoxProps) {
}

type FormProps = {
upload1: File[];
reason: string;
memo: string;
};

export default function SeasonTicketReOrder() {
@@ -39,35 +53,48 @@ export default function SeasonTicketReOrder() {

const form = useForm<FormProps>({
defaultValues: {
upload1: [],
reason: "",
memo: "",
},
resolver: yupResolver(
object().shape({
reason: string().required("入力してください"),
})
),
});

const { navigateWhenChanged, navigate } = useNavigateCustom();
const { navigateWhenChanged } = useNavigateCustom();

const { error } = useSnackbarCustom();

const { selectedseasonTicketContract, backToDetailHome, initialized } =
useSeasonTicketContractContext();

const [done, setDone] = useState(false);
const [mode, setMode] = useState<"input" | "confirm" | "done">("input");

const { callAPI: callReOrderSeasonTicket } = useAPICall({
apiMethod: reOrderSeasonTicket,
backDrop: true,
onSuccess: () => {
setDone(true);
setMode("done");
},
onFailed: () => {
error("依頼失敗しました");
setMode("input");
},
});

const handleSubmit = (data: FormProps) => {
const handleSubmit = () => {
setMode("confirm");
};

const send = () => {
if (selectedseasonTicketContract === null) return;
callReOrderSeasonTicket({
season_ticket_contract_record_no:
selectedseasonTicketContract?.season_ticekt_contract_record_no ?? "",
...data,
parking_name: selectedseasonTicketContract.parking_name ?? "",
...form.getValues(),
});
};

@@ -87,7 +114,7 @@ export default function SeasonTicketReOrder() {
if (selectedseasonTicketContract === null) {
return null;
}
if (done) {
if (mode === "done") {
return (
<Box sx={{ mt: 1 }}>
<Stack spacing={2}>
@@ -100,6 +127,58 @@ export default function SeasonTicketReOrder() {
);
}

if (mode === "confirm") {
return (
<Box sx={{ mt: 1 }}>
<Stack spacing={2}>
<Box>
<Typography>下記内容で申請を行います。</Typography>
</Box>
<Box>
<Table size="small">
<TableBody>
<TableRow>
<TableCell>理由</TableCell>
<TableCell>
<TextFieldEx
multiline
value={form.getValues("reason")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>備考</TableCell>
<TableCell>
<TextFieldEx
multiline
value={form.getValues("memo")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
</Box>
<StackRow spacing={2}>
<Button
onClick={() => {
setMode("input");
}}
>
戻る
</Button>
<Button onClick={send} variant="contained">
送信
</Button>
</StackRow>
</Stack>
</Box>
);
}

return (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Box sx={{ mt: 1 }}>
@@ -117,6 +196,15 @@ export default function SeasonTicketReOrder() {
maxRows={10}
/>
</AreaBox>
<AreaBox label="備考">
<RHFTextField
name="memo"
size="small"
multiline
minRows={3}
maxRows={10}
/>
</AreaBox>

<Box>
<Button variant="contained" type="submit">


+ 160
- 23
src/pages/dashboard/contract/terminate-order.tsx Näytä tiedosto

@@ -1,4 +1,14 @@
import { Box, Button, Stack, Typography } from "@mui/material";
import { yupResolver } from "@hookform/resolvers/yup";
import {
Box,
Button,
Stack,
Table,
TableBody,
TableCell,
TableRow,
Typography,
} from "@mui/material";
import { HasChildren } from "@types";
import {
getSeasonTicketContractTerminateOptions,
@@ -6,6 +16,8 @@ import {
reOrderSticker,
} from "api/season-ticket-contract";
import RequireChip from "components/chip/RequireChip";
import InputAlert from "components/form/InputAlert";
import TextFieldEx from "components/form/TextFieldEx";
import {
FormProvider,
RHFCheckbox,
@@ -25,6 +37,8 @@ import { PageID, TabID } from "pages";
import { useEffect, useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";
import { scrollToTop } from "utils/page";
import { array, object, string } from "yup";

type AreaBoxProps = {
label: string;
@@ -43,9 +57,9 @@ function AreaBox({ label, children, require }: AreaBoxProps) {
}

type FormProps = {
date: string;
terminate_date: string;
reason: string[];
other_reason: string;
reason_detail: string;
opinion: string;
memo: string;
};
@@ -58,12 +72,18 @@ export default function TerminateOrder() {

const form = useForm<FormProps>({
defaultValues: {
date: "",
terminate_date: "",
reason: [],
other_reason: "",
reason_detail: "",
opinion: "",
memo: "",
},
resolver: yupResolver(
object().shape({
terminate_date: string().required("入力してください"),
reason: array().min(1, "選択してください"),
})
),
});

const { navigateWhenChanged, navigate } = useNavigateCustom();
@@ -73,6 +93,8 @@ export default function TerminateOrder() {
const { selectedseasonTicketContract, backToDetailHome } =
useSeasonTicketContractContext();

const [mode, setMode] = useState<"input" | "confirm" | "done">("input");

const [monthes, setMonthes] = useState<string[]>([]);

const monthOptions: SelectOptionProps[] = useMemo(() => {
@@ -82,35 +104,54 @@ export default function TerminateOrder() {
}));
}, [monthes]);

const [done, setDone] = useState(false);

const { callAPI: callOrderSeasonTicketContractTerminate } = useAPICall({
apiMethod: orderSeasonTicketContractTerminate,
backDrop: true,
onSuccess: () => {
setDone(true);
setMode("done");
},
onFailed: () => {
error("依頼失敗しました");
setMode("input");
},
});

const { callAPI: callGetSeasonTicketContractTerminateOptions } = useAPICall({
apiMethod: getSeasonTicketContractTerminateOptions,
backDrop: true,
onSuccess: ({ data }) => {
setMonthes(data.monthes);
},
});
const { callAPI: callGetSeasonTicketContractTerminateOptions, errorMode } =
useAPICall({
apiMethod: getSeasonTicketContractTerminateOptions,
backDrop: true,
onSuccess: ({ data }) => {
setMonthes(data.monthes);
},
});

const handleSubmit = () => {
// form.clearErrors();

const handleSubmit = (data: FormProps) => {
// if (form.getValues("reason").length === 0) {
// form.setError("reason", {
// message: "選択してください",
// type: "required",
// });
// }

// if (!form.formState.isValid) {
// scrollToTop();
// return;
// }

setMode("confirm");
};

const send = () => {
if (selectedseasonTicketContract === null) return;

callOrderSeasonTicketContractTerminate({
...data,
...form.getValues(),
season_ticket_contract_record_no:
selectedseasonTicketContract.season_ticekt_contract_record_no ?? "",
reason: data.reason.join(","),
parking_name: selectedseasonTicketContract.parking_name ?? "",
reason: form.getValues("reason").join(","),
});
};

@@ -151,7 +192,8 @@ export default function TerminateOrder() {
if (selectedseasonTicketContract === null) {
return null;
}
if (done) {

if (mode === "done") {
return (
<Box sx={{ mt: 1 }}>
<Stack spacing={2}>
@@ -164,22 +206,117 @@ export default function TerminateOrder() {
);
}

if (mode === "confirm") {
return (
<Box sx={{ mt: 1 }}>
<Stack spacing={2}>
<Box>
<Typography>下記内容で申請を行います。</Typography>
</Box>
<Box>
<Table size="small">
<TableBody>
<TableRow>
<TableCell>解約希望日</TableCell>
<TableCell>
<TextFieldEx
multiline
value={form.getValues("terminate_date")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>解約理由</TableCell>
<TableCell>
<TextFieldEx
multiline
value={form.getValues("reason").join("\n")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>解約理由詳細</TableCell>
<TableCell>
<TextFieldEx
multiline
value={form.getValues("reason_detail")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>ご意見</TableCell>
<TableCell>
<TextFieldEx
multiline
value={form.getValues("opinion")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>備考</TableCell>
<TableCell>
<TextFieldEx
multiline
value={form.getValues("memo")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
</Box>
<StackRow spacing={2}>
<Button
onClick={() => {
setMode("input");
}}
>
戻る
</Button>
<Button onClick={send} variant="contained">
送信
</Button>
</StackRow>
</Stack>
</Box>
);
}

return (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<FormProvider
methods={form}
onSubmit={form.handleSubmit(handleSubmit, (aaa) => {
scrollToTop();
})}
>
<Box sx={{ mt: 1 }}>
<Stack spacing={2}>
<Box>
<Button onClick={backToDetailHome}>戻る</Button>
</Box>

<InputAlert formState={form.formState} error={errorMode} />

<AreaBox label="解約希望日" require>
<RHFSelect name="date" size="small" options={monthOptions} />
<RHFSelect
name="terminate_date"
size="small"
options={monthOptions}
/>
</AreaBox>
<AreaBox label="解約理由(複数選択可)" require>
<Typography variant="body2">
解約の理由についてお聞かせ下さい(複数選択可)
</Typography>

<RHFMultiCheckbox name="reason" options={reasons} />
</AreaBox>
<AreaBox label="解約理由詳細">
@@ -187,7 +324,7 @@ export default function TerminateOrder() {
前質問 7・8を選択された方 詳細を詳しくお聞かせください。
</Typography>
<RHFTextField
name="reason_other"
name="reason_detail"
size="small"
multiline
minRows={3}


+ 136
- 10
src/pages/dashboard/contract/update-vehicle-info-order.tsx Näytä tiedosto

@@ -1,10 +1,23 @@
import { Box, Button, Stack, Typography } from "@mui/material";
import { yupResolver } from "@hookform/resolvers/yup";
import {
Box,
Button,
Stack,
Table,
TableBody,
TableCell,
TableRow,
Typography,
} from "@mui/material";
import { HasChildren } from "@types";
import {
orderUpdateVehicleInfo,
reOrderSticker,
} from "api/season-ticket-contract";
import TextFieldEx from "components/form/TextFieldEx";
import { FormProvider, RHFTextField } from "components/hook-form";
import RHFDatePicker from "components/hook-form/RHFDatePicker";
import StackRow from "components/stack/StackRow";
import { useSeasonTicketContractContext } from "contexts/dashboard/SeasonTicketContractContext";
import useAPICall from "hooks/useAPICall";
import useDashboard from "hooks/useDashBoard";
@@ -14,6 +27,8 @@ import { PageID, TabID } from "pages";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
import { getPath } from "routes/path";
import { formatDateStr } from "utils/datetime";
import { date, object } from "yup";

type AreaBoxProps = {
label: string;
@@ -28,7 +43,10 @@ function AreaBox({ label, children }: AreaBoxProps) {
}

type FormProps = {
upload1: File[];
change_date: Date | null;
vehicle_no: string;
register_no: string;
memo: string;
};

export default function UpdateVehicleInfoOrder() {
@@ -39,8 +57,16 @@ export default function UpdateVehicleInfoOrder() {

const form = useForm<FormProps>({
defaultValues: {
upload1: [],
change_date: null,
vehicle_no: "",
register_no: "",
memo: "",
},
resolver: yupResolver(
object().shape({
change_date: date().required("入力してください"),
})
),
});

const { navigateWhenChanged, navigate } = useNavigateCustom();
@@ -50,25 +76,33 @@ export default function UpdateVehicleInfoOrder() {
const { selectedseasonTicketContract, backToDetailHome } =
useSeasonTicketContractContext();

const [done, setDone] = useState(false);
const [mode, setMode] = useState<"input" | "confirm" | "done">("input");

const { callAPI: callOrderUpdateVehicleInfo } = useAPICall({
apiMethod: orderUpdateVehicleInfo,
backDrop: true,
form,
onSuccess: () => {
setDone(true);
setMode("done");
},
onFailed: () => {
error("依頼失敗しました");
setMode("input");
},
});

const handleSubmit = (data: FormProps) => {
const handleSubmit = () => {
setMode("confirm");
};

const send = () => {
if (selectedseasonTicketContract === null) return;
callOrderUpdateVehicleInfo({
season_ticket_contract_record_no:
selectedseasonTicketContract?.season_ticekt_contract_record_no ?? "",
...data,
parking_name: selectedseasonTicketContract.parking_name ?? "",
...form.getValues(),
change_date: formatDateStr(form.getValues("change_date")),
});
};

@@ -82,13 +116,22 @@ export default function UpdateVehicleInfoOrder() {
navigateWhenChanged(
getPath(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST)
);
} else {
form.setValue(
"vehicle_no",
selectedseasonTicketContract.vehicle_no ?? ""
);
form.setValue(
"register_no",
selectedseasonTicketContract.register_no ?? ""
);
}
}, [selectedseasonTicketContract]);

if (selectedseasonTicketContract === null) {
return null;
}
if (done) {
if (mode === "done") {
return (
<Box sx={{ mt: 1 }}>
<Stack spacing={2}>
@@ -101,6 +144,77 @@ export default function UpdateVehicleInfoOrder() {
);
}

if (mode === "confirm") {
return (
<Box sx={{ mt: 1 }}>
<Stack spacing={2}>
<Box>
<Typography>下記内容で申請を行います。</Typography>
</Box>
<Box>
<Table size="small">
<TableBody>
<TableRow>
<TableCell>変更日</TableCell>
<TableCell>
<TextFieldEx
value={formatDateStr(form.getValues("change_date"))}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>車両番号</TableCell>
<TableCell>
<TextFieldEx
value={form.getValues("vehicle_no")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>防犯登録番号</TableCell>
<TableCell>
<TextFieldEx
value={form.getValues("register_no")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
<TableRow>
<TableCell>備考</TableCell>
<TableCell>
<TextFieldEx
multiline
value={form.getValues("memo")}
readOnly
fullWidth
/>
</TableCell>
</TableRow>
</TableBody>
</Table>
</Box>
<StackRow spacing={2}>
<Button
onClick={() => {
setMode("input");
}}
>
戻る
</Button>
<Button onClick={send} variant="contained">
送信
</Button>
</StackRow>
</Stack>
</Box>
);
}

return (
<FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}>
<Box sx={{ mt: 1 }}>
@@ -109,11 +223,23 @@ export default function UpdateVehicleInfoOrder() {
<Button onClick={backToDetailHome}>戻る</Button>
</Box>

<AreaBox label="変更日">
<RHFDatePicker name="change_date" />
</AreaBox>
<AreaBox label="車両番号">
<RHFTextField name="vehicle_no" />
</AreaBox>
<AreaBox label="車体番号">
<RHFTextField name="chassis_no" />
<AreaBox label="防犯登録番号">
<RHFTextField name="register_no" />
</AreaBox>
<AreaBox label="備考">
<RHFTextField
name="memo"
size="small"
multiline
minRows={3}
maxRows={10}
/>
</AreaBox>

<Box>


Loading…
Peruuta
Tallenna