| @@ -16,6 +16,11 @@ export const ApiId = { | |||
| SEASON_TICKET_CONTRACTS: id++, | |||
| PAYMENT_PLANS: id++, | |||
| STICKER_RE_ORDER: id++, | |||
| PARKING_CERTIFICATE_ORDER: id++, | |||
| SEASON_TICKET_CONTRACT_TERMINATE_ORDER: id++, | |||
| UPDATE_VEHICLE_INFO_ORDER: id++, | |||
| } as const; | |||
| export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | |||
| @@ -26,7 +26,6 @@ export type PaymentPlan = { | |||
| type SeasonTicketContractsResponse = { | |||
| data: SeasonTicketContract[]; | |||
| } & APICommonResponse; | |||
| export const getSeasonTicketContracts = async () => { | |||
| const res = await request<SeasonTicketContractsResponse>({ | |||
| url: getUrl(ApiId.SEASON_TICKET_CONTRACTS), | |||
| @@ -39,11 +38,9 @@ export const getSeasonTicketContracts = async () => { | |||
| type PaymentPlansRequest = { | |||
| season_ticket_contract_record_no: string; | |||
| }; | |||
| type PaymentPlansResponse = { | |||
| data: PaymentPlan[]; | |||
| } & APICommonResponse; | |||
| export const getPaymentPlans = async (data: PaymentPlansRequest) => { | |||
| const res = await request<PaymentPlansResponse>({ | |||
| url: getUrl(ApiId.PAYMENT_PLANS), | |||
| @@ -52,3 +49,71 @@ export const getPaymentPlans = async (data: PaymentPlansRequest) => { | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------シール再発行依頼------------------ | |||
| type StickerReOrderRequest = { | |||
| season_ticket_contract_record_no: string; | |||
| }; | |||
| export const reOrderSticker = async (data: StickerReOrderRequest) => { | |||
| const res = await request({ | |||
| url: getUrl(ApiId.STICKER_RE_ORDER), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(data), | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------車庫証明発行依頼------------------ | |||
| type ParkingCertificateOrderRequest = { | |||
| season_ticket_contract_record_no: string; | |||
| date: Date; | |||
| reason: string; | |||
| reson_other: string; | |||
| opinion: string; | |||
| memo: string; | |||
| }; | |||
| export const orderParkingCertificate = async ( | |||
| data: ParkingCertificateOrderRequest | |||
| ) => { | |||
| const res = await request({ | |||
| url: getUrl(ApiId.PARKING_CERTIFICATE_ORDER), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(data), | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------解約依頼------------------ | |||
| type TerminateOrderRequest = { | |||
| season_ticket_contract_record_no: string; | |||
| date: Date; | |||
| reason: string; | |||
| other_reason: string; | |||
| opinion: string; | |||
| memo: string; | |||
| }; | |||
| export const orderSeasonTicketContractTerminate = async ( | |||
| data: TerminateOrderRequest | |||
| ) => { | |||
| const res = await request({ | |||
| url: getUrl(ApiId.SEASON_TICKET_CONTRACT_TERMINATE_ORDER), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(data), | |||
| }); | |||
| return res; | |||
| }; | |||
| // -------車両情報変更依頼------------------ | |||
| type UpdateVehicleInfoOrderRequest = { | |||
| season_ticket_contract_record_no: string; | |||
| }; | |||
| export const orderUpdateVehicleInfo = async ( | |||
| data: UpdateVehicleInfoOrderRequest | |||
| ) => { | |||
| const res = await request({ | |||
| url: getUrl(ApiId.UPDATE_VEHICLE_INFO_ORDER), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(data), | |||
| }); | |||
| return res; | |||
| }; | |||
| @@ -8,6 +8,13 @@ const urls = { | |||
| [A.LOGOUT]: "logout", | |||
| [A.SEASON_TICKET_CONTRACTS]: "season-ticket-contracts", | |||
| [A.PAYMENT_PLANS]: "season-ticket-contract/payment-plans", | |||
| [A.STICKER_RE_ORDER]: "season-ticket-contract/sticker-re-order", | |||
| [A.PARKING_CERTIFICATE_ORDER]: | |||
| "season-ticket-contract/parking-certificate-order", | |||
| [A.SEASON_TICKET_CONTRACT_TERMINATE_ORDER]: | |||
| "season-ticket-contract/termination-order", | |||
| [A.UPDATE_VEHICLE_INFO_ORDER]: | |||
| "season-ticket-contract/update-vehicle-info-order", | |||
| }; | |||
| const prefixs = { | |||
| @@ -6,6 +6,7 @@ import { | |||
| FormControlLabel, | |||
| FormGroup, | |||
| FormControlLabelProps, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { useMemo } from "react"; | |||
| @@ -116,7 +117,7 @@ export function RHFMultiCheckbox({ | |||
| onChange={() => field.onChange(onSelected(option.value))} | |||
| /> | |||
| } | |||
| label={option.label} | |||
| label={<Typography variant="body2">{option.label}</Typography>} | |||
| {...other} | |||
| /> | |||
| ))} | |||
| @@ -130,15 +130,37 @@ export default function ContractDetail() { | |||
| </Paper> | |||
| <Paper sx={{ p: 2 }}> | |||
| <Typography variant="h5">各種申請</Typography> | |||
| <Stack direction="row" spacing={2} mt={2}> | |||
| <Stack spacing={2} mt={2}> | |||
| <Box> | |||
| <Button variant="contained">シール再発行申請</Button> | |||
| <Button | |||
| variant="contained" | |||
| onClick={() => { | |||
| navigateWhenChanged( | |||
| getPath( | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_STICKER_RE_ORDER | |||
| ) | |||
| ); | |||
| }} | |||
| > | |||
| シール再発行申請 | |||
| </Button> | |||
| </Box> | |||
| <Box> | |||
| <Button variant="contained">駐車証明証申請</Button> | |||
| </Box> | |||
| <Box> | |||
| <Button variant="contained">解約申請</Button> | |||
| <Button | |||
| variant="contained" | |||
| onClick={() => { | |||
| navigateWhenChanged( | |||
| getPath( | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_TERMINATE_ORDER | |||
| ) | |||
| ); | |||
| }} | |||
| > | |||
| 解約申請 | |||
| </Button> | |||
| </Box> | |||
| <Box> | |||
| <Button variant="contained">車両情報変更申請</Button> | |||
| @@ -0,0 +1,131 @@ | |||
| import { Box, Button, Stack, Typography } from "@mui/material"; | |||
| import { HasChildren } from "@types"; | |||
| import { reOrderSticker } from "api/season-ticket-contract"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import { useSeasonTicketContractContext } from "contexts/dashboard/SeasonTicketContractContext"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { useEffect, useState } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { getPath } from "routes/path"; | |||
| type AreaBoxProps = { | |||
| label: string; | |||
| } & HasChildren; | |||
| function AreaBox({ label, children }: AreaBoxProps) { | |||
| return ( | |||
| <Box> | |||
| <Typography variant="body2">{label}</Typography> | |||
| {children} | |||
| </Box> | |||
| ); | |||
| } | |||
| type FormProps = {}; | |||
| export default function StickerReOrder() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_STICKER_RE_ORDER, | |||
| TabID.NONE | |||
| ); | |||
| const form = useForm<FormProps>({}); | |||
| const { navigateWhenChanged, navigate } = useNavigateCustom(); | |||
| const { error } = useSnackbarCustom(); | |||
| const { selectedseasonTicketContract } = useSeasonTicketContractContext(); | |||
| const [done, setDone] = useState(false); | |||
| const { callAPI: callReOrderSticker } = useAPICall({ | |||
| apiMethod: reOrderSticker, | |||
| backDrop: true, | |||
| onSuccess: () => { | |||
| setDone(true); | |||
| }, | |||
| onFailed: () => { | |||
| error("依頼失敗しました"); | |||
| }, | |||
| }); | |||
| const handleSubmit = () => { | |||
| if (selectedseasonTicketContract === null) return; | |||
| callReOrderSticker({ | |||
| season_ticket_contract_record_no: | |||
| selectedseasonTicketContract.season_ticekt_contract_record_no ?? "", | |||
| }); | |||
| }; | |||
| useEffect(() => { | |||
| setHeaderTitle("シール再発行依頼"); | |||
| setTabs(null); | |||
| }, [setHeaderTitle, setTabs]); | |||
| useEffect(() => { | |||
| if (selectedseasonTicketContract === null) { | |||
| navigateWhenChanged( | |||
| getPath(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST) | |||
| ); | |||
| } | |||
| }, [selectedseasonTicketContract]); | |||
| if (selectedseasonTicketContract === null) { | |||
| return null; | |||
| } | |||
| if (done) { | |||
| return ( | |||
| <Box sx={{ mt: 1 }}> | |||
| <Stack spacing={2}> | |||
| <Box>依頼しました</Box> | |||
| <Box> | |||
| <Button | |||
| onClick={() => { | |||
| navigate(-1); | |||
| }} | |||
| > | |||
| 戻る | |||
| </Button> | |||
| </Box> | |||
| </Stack> | |||
| </Box> | |||
| ); | |||
| } | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Box sx={{ mt: 1 }}> | |||
| <Stack spacing={2}> | |||
| <Box> | |||
| <Button | |||
| onClick={() => { | |||
| navigate(-1); | |||
| }} | |||
| > | |||
| 戻る | |||
| </Button> | |||
| </Box> | |||
| <AreaBox label="申請理由"> | |||
| <RHFTextField | |||
| name="reason" | |||
| size="small" | |||
| multiline | |||
| minRows={3} | |||
| maxRows={10} | |||
| /> | |||
| </AreaBox> | |||
| <Box> | |||
| <Button variant="contained" type="submit"> | |||
| 確定 | |||
| </Button> | |||
| </Box> | |||
| </Stack> | |||
| </Box> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,224 @@ | |||
| import { Box, Button, Stack, Typography } from "@mui/material"; | |||
| import { HasChildren } from "@types"; | |||
| import { | |||
| orderSeasonTicketContractTerminate, | |||
| reOrderSticker, | |||
| } from "api/season-ticket-contract"; | |||
| import RequireChip from "components/chip/RequireChip"; | |||
| import { | |||
| FormProvider, | |||
| RHFCheckbox, | |||
| RHFMultiCheckbox, | |||
| RHFSelect, | |||
| RHFTextField, | |||
| } from "components/hook-form"; | |||
| import RHFDatePicker from "components/hook-form/RHFDatePicker"; | |||
| import { SelectOptionProps } from "components/hook-form/RHFSelect"; | |||
| import StackRow from "components/stack/StackRow"; | |||
| import { useSeasonTicketContractContext } from "contexts/dashboard/SeasonTicketContractContext"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { useEffect, useMemo, useState } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { getPath } from "routes/path"; | |||
| 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 = { | |||
| date: Date | null; | |||
| reason: string[]; | |||
| other_reason: string; | |||
| opinion: string; | |||
| memo: string; | |||
| }; | |||
| export default function TerminateOrder() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_TERMINATE_ORDER, | |||
| TabID.NONE | |||
| ); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| date: null, | |||
| reason: [], | |||
| other_reason: "", | |||
| opinion: "", | |||
| memo: "", | |||
| }, | |||
| }); | |||
| const { navigateWhenChanged, navigate } = useNavigateCustom(); | |||
| const { error } = useSnackbarCustom(); | |||
| const { selectedseasonTicketContract } = useSeasonTicketContractContext(); | |||
| const [done, setDone] = useState(false); | |||
| const { callAPI: callOrderSeasonTicketContractTerminate } = useAPICall({ | |||
| apiMethod: orderSeasonTicketContractTerminate, | |||
| backDrop: true, | |||
| onSuccess: () => { | |||
| setDone(true); | |||
| }, | |||
| onFailed: () => { | |||
| error("依頼失敗しました"); | |||
| }, | |||
| }); | |||
| const handleSubmit = (data: FormProps) => { | |||
| console.log(data); | |||
| if (selectedseasonTicketContract === null) return; | |||
| const date = data.date; | |||
| if (date === null) return; | |||
| callOrderSeasonTicketContractTerminate({ | |||
| ...data, | |||
| date, | |||
| season_ticket_contract_record_no: | |||
| selectedseasonTicketContract.season_ticekt_contract_record_no ?? "", | |||
| reason: data.reason.join(","), | |||
| }); | |||
| }; | |||
| const reasons = useMemo(() => { | |||
| return [ | |||
| "転居/転勤のため", | |||
| "就職/進学/卒業のため", | |||
| "料金が高いため", | |||
| "支払いが困難なため", | |||
| "車/バイク/自転車に乗らなくなったため", | |||
| "より良い駐車場/駐輪場が見つかったため", | |||
| "駐車場/駐輪場に不満があるため(詳細)", | |||
| "その他(詳細)", | |||
| ].map((ele, index) => ({ | |||
| label: String(index + 1) + "." + String(ele), | |||
| value: ele, | |||
| })); | |||
| }, []); | |||
| useEffect(() => { | |||
| setHeaderTitle("解約依頼申請"); | |||
| setTabs(null); | |||
| }, [setHeaderTitle, setTabs]); | |||
| useEffect(() => { | |||
| if (selectedseasonTicketContract === null) { | |||
| navigateWhenChanged( | |||
| getPath(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST) | |||
| ); | |||
| } | |||
| }, [selectedseasonTicketContract]); | |||
| if (selectedseasonTicketContract === null) { | |||
| return null; | |||
| } | |||
| if (done) { | |||
| return ( | |||
| <Box sx={{ mt: 1 }}> | |||
| <Stack spacing={2}> | |||
| <Box>依頼しました</Box> | |||
| <Box> | |||
| <Button | |||
| onClick={() => { | |||
| navigate(-1); | |||
| }} | |||
| > | |||
| 戻る | |||
| </Button> | |||
| </Box> | |||
| </Stack> | |||
| </Box> | |||
| ); | |||
| } | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Box sx={{ mt: 1 }}> | |||
| <Stack spacing={2}> | |||
| <Box> | |||
| <Button | |||
| onClick={() => { | |||
| navigate(-1); | |||
| }} | |||
| > | |||
| 戻る | |||
| </Button> | |||
| </Box> | |||
| <AreaBox label="解約希望日" require> | |||
| <RHFDatePicker name="date" size="small" /> | |||
| </AreaBox> | |||
| <AreaBox label="解約理由(複数選択可)" require> | |||
| <Typography variant="body2"> | |||
| 解約の理由についてお聞かせ下さい(複数選択可) | |||
| </Typography> | |||
| <RHFMultiCheckbox name="reason" options={reasons} /> | |||
| </AreaBox> | |||
| <AreaBox label="解約理由詳細"> | |||
| <Typography variant="body2"> | |||
| 前質問 7・8を選択された方 詳細を詳しくお聞かせください。 | |||
| </Typography> | |||
| <RHFTextField | |||
| name="reason_other" | |||
| size="small" | |||
| multiline | |||
| minRows={3} | |||
| maxRows={10} | |||
| /> | |||
| </AreaBox> | |||
| <AreaBox label="ご意見"> | |||
| <Typography variant="body2"> | |||
| 今後の管理運営に生かすため、ご意見がありましたらご記入ください。 | |||
| </Typography> | |||
| <RHFTextField | |||
| name="opinion" | |||
| size="small" | |||
| multiline | |||
| minRows={3} | |||
| maxRows={10} | |||
| /> | |||
| </AreaBox> | |||
| <AreaBox label="備考"> | |||
| <Typography variant="body2"> | |||
| ご不明点等がございましたらご入力ください。お電話またはメールにて回答させていただきます。 | |||
| </Typography> | |||
| <RHFTextField | |||
| name="memo" | |||
| size="small" | |||
| multiline | |||
| minRows={3} | |||
| maxRows={10} | |||
| /> | |||
| </AreaBox> | |||
| <Box> | |||
| <Button variant="contained" type="submit"> | |||
| 確定 | |||
| </Button> | |||
| </Box> | |||
| </Stack> | |||
| </Box> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| @@ -10,6 +10,10 @@ export const PageID = { | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_LIST: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_STICKER_RE_ORDER: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_PARKING_CERTIFICATE_ORDER: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_TERMINATE_ORDER: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_UPDATE_VEHICLE_INFO_ORDER: id++, | |||
| DASHBOARD_RECEIPT_DOWNLOAD: id++, | |||
| @@ -37,6 +37,16 @@ const PATHS_DASHBOARD = { | |||
| "/dashboard/contract/list/:page", | |||
| [makePathKey(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL)]: | |||
| "/dashboard/contract/detail/:id", | |||
| [makePathKey(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_STICKER_RE_ORDER)]: | |||
| "/dashboard/contract/sticker-re-order", | |||
| [makePathKey( | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_PARKING_CERTIFICATE_ORDER | |||
| )]: "/dashboard/contract/parking-certificate-order", | |||
| [makePathKey(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_TERMINATE_ORDER)]: | |||
| "/dashboard/contract/terminate-order", | |||
| [makePathKey( | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_UPDATE_VEHICLE_INFO_ORDER | |||
| )]: "/dashboard/contract/update-vehicle-info-order", | |||
| [makePathKey(PageID.DASHBOARD_RECEIPT_DOWNLOAD)]: | |||
| "/dashboard/receipt/download", | |||
| [makePathKey(PageID.DASHBOARD_USER_DETAIL)]: "/dashboard/user/detail", | |||
| @@ -62,6 +62,12 @@ export default function DashboardRoutes(): RouteObject[] { | |||
| const ContractDetail = Loadable( | |||
| lazy(() => import("pages/dashboard/contract/detail")) | |||
| ); | |||
| const StickerReOrder = Loadable( | |||
| lazy(() => import("pages/dashboard/contract/sticker-re-order")) | |||
| ); | |||
| const TerminateOrder = Loadable( | |||
| lazy(() => import("pages/dashboard/contract/terminate-order")) | |||
| ); | |||
| const allChildren = [ | |||
| { | |||
| @@ -72,6 +78,14 @@ export default function DashboardRoutes(): RouteObject[] { | |||
| pageId: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL, | |||
| element: <ContractDetail />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_STICKER_RE_ORDER, | |||
| element: <StickerReOrder />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_TERMINATE_ORDER, | |||
| element: <TerminateOrder />, | |||
| }, | |||
| ]; | |||
| return allChildren.map(({ pageId, ...others }) => ({ | |||
| ...others, | |||