| @@ -7,8 +7,10 @@ export type HTCustomer = { | |||
| }; | |||
| export type HTParking = { | |||
| customer_code: string; | |||
| customer_name: string; | |||
| parking_management_code: string; | |||
| name: string; | |||
| parking_name: string; | |||
| name: string; // 旧API仕様 | |||
| }; | |||
| export type HTAdjustData = { | |||
| customer_code: string; | |||
| @@ -39,7 +41,9 @@ export const getHTCustomers = async (data = {}) => { | |||
| // 駐車場一覧取得 --------------------- | |||
| export type HTParkingsRequest = { | |||
| customer_code: string; | |||
| customer_code?: string; | |||
| parking_management_code?: string; | |||
| parking_name?: string; | |||
| }; | |||
| export type HTParkingsResponse = { | |||
| @@ -0,0 +1,41 @@ | |||
| import { HasChildren } from "@types"; | |||
| import { HTAdjustData, HTParking } from "api/custom/hello-techno"; | |||
| import { createContext, useState } from "react"; | |||
| type ContextProps = { | |||
| parking: HTParking | null; | |||
| setParking: (parking: HTParking | null) => void; | |||
| adjustData: HTAdjustData | null; | |||
| setAdjustData: (adjustData: HTAdjustData | null) => void; | |||
| }; | |||
| export const CreateContext = createContext<ContextProps>({ | |||
| parking: null, | |||
| setParking: (parking: HTParking | null) => { | |||
| console.error("setParking未定義"); | |||
| }, | |||
| adjustData: null, | |||
| setAdjustData: (adjustData: HTAdjustData | null) => { | |||
| console.error("setAdjustData未定義"); | |||
| }, | |||
| }); | |||
| type ProviderProps = {} & HasChildren; | |||
| export function CreateContextProvider({ children }: ProviderProps) { | |||
| const [parking, setParking] = useState<HTParking | null>(null); | |||
| const [adjustData, setAdjustData] = useState<HTAdjustData | null>(null); | |||
| return ( | |||
| <CreateContext.Provider | |||
| value={{ | |||
| parking, | |||
| setParking, | |||
| adjustData, | |||
| setAdjustData, | |||
| }} | |||
| > | |||
| {children} | |||
| </CreateContext.Provider> | |||
| ); | |||
| } | |||
| @@ -1,16 +1,17 @@ | |||
| import { Box, Step, StepLabel, Stepper } from "@mui/material"; | |||
| import { getHTAdjustData } from "api/custom/hello-techno"; | |||
| import { HTParking } from "api/custom/hello-techno"; | |||
| import { createReceiptIssuingOrder } from "api/custom/hello-techno/receipt-issuing-order"; | |||
| import { PageID, TabID } from "codes/page"; | |||
| import { getValue } from "components/hook-form/RHFAutoComplete"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { useEffect, useMemo, useState } from "react"; | |||
| import { CreateContextProvider } from "./contexts/CreateContext"; | |||
| import useConfirm, { ConfirmDataProps } from "./hooks/useConfirm"; | |||
| import useCreate from "./hooks/useCreate"; | |||
| import useInputReceiptStep from "./hooks/useInputReceiptStep"; | |||
| import useInputSMSSendAddress from "./hooks/useInputSMSSendAddress"; | |||
| import useSelectParkingStep from "./hooks/useSelectParkingStep"; | |||
| import useParkingList from "./hooks/useParkingList"; | |||
| export default function ReceiptIssuingOrderCreate() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| @@ -18,6 +19,19 @@ export default function ReceiptIssuingOrderCreate() { | |||
| TabID.NONE | |||
| ); | |||
| useEffect(() => { | |||
| setHeaderTitle("領収証発行依頼作成"); | |||
| setTabs(null); | |||
| }, []); | |||
| return ( | |||
| <CreateContextProvider> | |||
| <Create /> | |||
| </CreateContextProvider> | |||
| ); | |||
| } | |||
| function Create() { | |||
| const { parking, adjustData } = useCreate(); | |||
| const { success, error } = useSnackbarCustom(); | |||
| const [mode, setMode] = useState< | |||
| @@ -41,9 +55,9 @@ export default function ReceiptIssuingOrderCreate() { | |||
| const getConfimData = (): ConfirmDataProps => { | |||
| return { | |||
| customerName: selectParkingStep.values("customerCode.label"), | |||
| parkingName: selectParkingStep.values("parkingManagementCode.label"), | |||
| adjustSeqNo: selectParkingStep.values("adjustSeqNo"), | |||
| customerName: parking?.customer_name ?? "XXXXXXXXXXXX", | |||
| parkingName: parking?.parking_name ?? "XXXXXXXXXXXXXXX", | |||
| adjustSeqNo: adjustData ? String(adjustData.adjust_seq_no) : "", | |||
| amount: inputReceiptStep.values("amount"), | |||
| date: inputReceiptStep.values("date"), | |||
| address: inputSMSSendAddress.values("address"), | |||
| @@ -52,39 +66,17 @@ export default function ReceiptIssuingOrderCreate() { | |||
| }; | |||
| const getFormData = () => { | |||
| return { | |||
| ...selectParkingStep.values(), | |||
| ...parking, | |||
| ...inputReceiptStep.values(), | |||
| ...inputSMSSendAddress.values(), | |||
| }; | |||
| }; | |||
| const adjustDataAPI = useAPICall({ | |||
| apiMethod: getHTAdjustData, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| inputReceiptStep.setAdjustData(data); | |||
| const selectParkingStep = useParkingList({ | |||
| onNext: (parking: HTParking) => { | |||
| inputReceiptStep.init(); | |||
| setMode("input_receipt"); | |||
| }, | |||
| onFailed: () => { | |||
| error("精算履歴が存在しません"); | |||
| }, | |||
| }); | |||
| const selectParkingStep = useSelectParkingStep({ | |||
| onNext: () => { | |||
| const { customerCode, parkingManagementCode, adjustSeqNo } = | |||
| getFormData(); | |||
| if (adjustSeqNo) { | |||
| adjustDataAPI.callAPI({ | |||
| customer_code: getValue(customerCode), | |||
| parking_management_code: getValue(parkingManagementCode), | |||
| adjust_seq_no: adjustSeqNo, | |||
| }); | |||
| } else { | |||
| inputReceiptStep.setAdjustData(null); | |||
| setMode("input_receipt"); | |||
| } | |||
| }, | |||
| }); | |||
| const inputReceiptStep = useInputReceiptStep({ | |||
| @@ -131,9 +123,9 @@ export default function ReceiptIssuingOrderCreate() { | |||
| const formData = getFormData(); | |||
| const sendData = makeSendData({ | |||
| customer_code: getValue(formData.customerCode), | |||
| parking_management_code: getValue(formData.parkingManagementCode), | |||
| adjust_seq_no: formData.adjustSeqNo, | |||
| customer_code: formData.customer_code ?? "", | |||
| parking_management_code: formData.parking_management_code ?? "", | |||
| adjust_seq_no: adjustData?.adjust_seq_no, | |||
| receipt_use_date: formData.date, | |||
| receipt_amount: formData.amount, | |||
| tax_amount: formData.tax_amount, | |||
| @@ -144,10 +136,6 @@ export default function ReceiptIssuingOrderCreate() { | |||
| callAPI(sendData); | |||
| }; | |||
| useEffect(() => { | |||
| setHeaderTitle("領収証発行依頼作成"); | |||
| setTabs(null); | |||
| }, []); | |||
| return ( | |||
| <Box sx={{ p: 1, m: 1 }}> | |||
| <Stepper activeStep={step}> | |||
| @@ -0,0 +1,8 @@ | |||
| import { useContext } from "react"; | |||
| import { CreateContext } from "../contexts/CreateContext"; | |||
| export default function useCreate() { | |||
| const context = useContext(CreateContext); | |||
| return context; | |||
| } | |||
| @@ -1,16 +1,19 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { Box, Button, Stack, Typography } from "@mui/material"; | |||
| import { Box, Button, Divider, Stack, Typography } from "@mui/material"; | |||
| import { HasChildren } from "@types"; | |||
| import { HTAdjustData } from "api/custom/hello-techno"; | |||
| import { getHTAdjustData } from "api/custom/hello-techno"; | |||
| import RequireChip from "components/chip/RequireChip"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import RHFDatePicker from "components/hook-form/RHFDatePicker"; | |||
| import StackRow from "components/stack/StackRow"; | |||
| import { useEffect, useMemo, useState } from "react"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||
| import { useEffect, useMemo } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { dateTimeParse } from "utils/datetime"; | |||
| import { calcInnerTax } from "utils/tax"; | |||
| import * as Yup from "yup"; | |||
| import useCreate from "./useCreate"; | |||
| type AreaBoxProps = { | |||
| title: string; | |||
| @@ -18,17 +21,18 @@ type AreaBoxProps = { | |||
| } & HasChildren; | |||
| function AreaBox({ title, children, require }: AreaBoxProps) { | |||
| return ( | |||
| <Box sx={{ maxWidth: 500 }}> | |||
| <Stack sx={{ maxWidth: 500 }}> | |||
| <StackRow> | |||
| <Typography variant="subtitle1">{title} </Typography> | |||
| <RequireChip show={require ?? false} /> | |||
| <RequireChip require={require ?? false} /> | |||
| </StackRow> | |||
| {children} | |||
| </Box> | |||
| </Stack> | |||
| ); | |||
| } | |||
| type FormProps = { | |||
| adjust_seq_no: string; | |||
| amount: string; | |||
| tax_amount: string; | |||
| date: Date | null; | |||
| @@ -40,10 +44,13 @@ type Props = { | |||
| onPrev?: VoidFunction; | |||
| }; | |||
| export default function useInputReceiptStep({ onNext, onPrev }: Props) { | |||
| const [adjustData, _setAdjustData] = useState<HTAdjustData | null>(null); | |||
| const { parking, adjustData, setAdjustData } = useCreate(); | |||
| const { error } = useSnackbarCustom(); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| adjust_seq_no: "", | |||
| amount: "", | |||
| tax_amount: "", | |||
| date: null, | |||
| @@ -82,10 +89,6 @@ export default function useInputReceiptStep({ onNext, onPrev }: Props) { | |||
| } | |||
| }; | |||
| const setAdjustData = (data: HTAdjustData | null) => { | |||
| _setAdjustData(data); | |||
| }; | |||
| const canCalcTax = useMemo(() => { | |||
| return !adjustData; | |||
| }, [adjustData]); | |||
| @@ -97,29 +100,108 @@ export default function useInputReceiptStep({ onNext, onPrev }: Props) { | |||
| form.setValue("tax_amount", String(calcInnerTax(amount))); | |||
| }; | |||
| const adjustDataAPI = useAPICall({ | |||
| apiMethod: getHTAdjustData, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| setAdjustData(data); | |||
| }, | |||
| onFailed: () => { | |||
| error("精算履歴が存在しません"); | |||
| }, | |||
| }); | |||
| const handleSearchAdjsutData = () => { | |||
| const adjustSeqNo = form.getValues("adjust_seq_no"); | |||
| if (!adjustSeqNo) { | |||
| error("精算連番を入力してください"); | |||
| return; | |||
| } | |||
| const adjustSeqNoNumber = Number(adjustSeqNo); | |||
| if (!(1 <= adjustSeqNoNumber && adjustSeqNoNumber <= 99999)) { | |||
| error("1-999999の範囲で入力してください"); | |||
| return; | |||
| } | |||
| if (!parking) { | |||
| error("駐車場未選択"); | |||
| return; | |||
| } | |||
| adjustDataAPI.callAPI({ | |||
| adjust_seq_no: adjustSeqNo, | |||
| customer_code: parking.customer_code, | |||
| parking_management_code: parking.parking_management_code, | |||
| }); | |||
| }; | |||
| const handleClearAdjustData = () => { | |||
| init(); | |||
| }; | |||
| const readOnly = useMemo(() => { | |||
| return adjustData !== null; | |||
| }, [adjustData]); | |||
| const init = () => { | |||
| form.clearErrors(); | |||
| setAdjustData(null); | |||
| form.setValue("adjust_seq_no", ""); | |||
| form.setValue("amount", ""); | |||
| form.setValue("tax_amount", ""); | |||
| form.setValue("date", null); | |||
| form.setValue("memo", ""); | |||
| }; | |||
| const element = ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Stack spacing={2} sx={{ p: 1, m: 1 }}> | |||
| <AreaBox title="利用日" require={true}> | |||
| <Box mt={3}>〇精算連番が特定できる場合は入力してください</Box> | |||
| <StackRow> | |||
| <AreaBox title="精算連番"> | |||
| <RHFTextField | |||
| name="adjust_seq_no" | |||
| size="small" | |||
| type="number" | |||
| readOnly={readOnly} | |||
| /> | |||
| </AreaBox> | |||
| <Box display="flex" justifyContent="flex-end" flexDirection="column"> | |||
| <Button | |||
| variant="contained" | |||
| color="info" | |||
| onClick={handleSearchAdjsutData} | |||
| > | |||
| 検索 | |||
| </Button> | |||
| </Box> | |||
| <Box display="flex" justifyContent="flex-end" flexDirection="column"> | |||
| <Button variant="outlined" onClick={handleClearAdjustData}> | |||
| クリアー | |||
| </Button> | |||
| </Box> | |||
| </StackRow> | |||
| <Divider /> | |||
| <AreaBox title="利用日" require> | |||
| <RHFDatePicker name="date" size="small" readOnly={readOnly} /> | |||
| </AreaBox> | |||
| <AreaBox title="金額" require={true}> | |||
| <RHFTextField | |||
| type="number" | |||
| name="amount" | |||
| size="small" | |||
| InputProps={{ | |||
| endAdornment: <div style={{ color: "black !important" }}>円</div>, | |||
| }} | |||
| sx={{ maxWidth: 150 }} | |||
| readOnly={readOnly} | |||
| /> | |||
| <AreaBox title="金額" require> | |||
| <Box> | |||
| <RHFTextField | |||
| type="number" | |||
| name="amount" | |||
| size="small" | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| <div style={{ color: "black !important" }}>円</div> | |||
| ), | |||
| }} | |||
| sx={{ maxWidth: 150 }} | |||
| readOnly={readOnly} | |||
| /> | |||
| </Box> | |||
| </AreaBox> | |||
| <AreaBox title="消費税(内税)" require={true}> | |||
| <AreaBox title="消費税(内税)" require> | |||
| <Stack direction="row" spacing={2}> | |||
| <RHFTextField | |||
| type="number" | |||
| @@ -171,5 +253,6 @@ export default function useInputReceiptStep({ onNext, onPrev }: Props) { | |||
| values: form.getValues, | |||
| setValue: form.setValue, | |||
| setAdjustData, | |||
| init, | |||
| }; | |||
| } | |||
| @@ -1,29 +1,25 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { Box, Button, Stack, Typography } from "@mui/material"; | |||
| import { Button, Stack, Typography } from "@mui/material"; | |||
| import { HasChildren } from "@types"; | |||
| import { | |||
| FormProvider, | |||
| RHFAutoComplete, | |||
| RHFTextField, | |||
| } from "components/hook-form"; | |||
| import { | |||
| AutoCompleteOption, | |||
| AutoCompleteOptionType, | |||
| getValue, | |||
| } from "components/hook-form/RHFAutoComplete"; | |||
| import { useState } from "react"; | |||
| import RequireChip from "components/chip/RequireChip"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import StackRow from "components/stack/StackRow"; | |||
| import { useForm } from "react-hook-form"; | |||
| import * as Yup from "yup"; | |||
| type AreaBoxProps = { | |||
| title: string; | |||
| require?: boolean; | |||
| } & HasChildren; | |||
| function AreaBox({ title, children }: AreaBoxProps) { | |||
| function AreaBox({ title, children, require }: AreaBoxProps) { | |||
| return ( | |||
| <Box sx={{ maxWidth: 500 }}> | |||
| <Typography variant="subtitle1">{title}</Typography> | |||
| <Stack sx={{ maxWidth: 500 }}> | |||
| <StackRow> | |||
| <Typography variant="subtitle1">{title} </Typography> | |||
| <RequireChip require={require ?? false} /> | |||
| </StackRow> | |||
| {children} | |||
| </Box> | |||
| </Stack> | |||
| ); | |||
| } | |||
| @@ -64,7 +60,7 @@ export default function useInputSMSSendAddress({ onNext, onPrev }: Props) { | |||
| const element = ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Stack spacing={2} sx={{ p: 1, m: 1 }}> | |||
| <AreaBox title="SMS送信先"> | |||
| <AreaBox title="SMS送信先" require> | |||
| <RHFTextField name="address" size="small" /> | |||
| </AreaBox> | |||
| <Stack direction="row" spacing={2}> | |||
| @@ -0,0 +1,227 @@ | |||
| import { | |||
| Box, | |||
| Grid, | |||
| Stack, | |||
| Table, | |||
| TableBody, | |||
| TableCell, | |||
| TableContainer, | |||
| TablePagination, | |||
| TableRow, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { | |||
| HTParking, | |||
| HTParkingsRequest, | |||
| getHTParkings, | |||
| } from "api/custom/hello-techno"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import { TableHeadCustom } from "components/table"; | |||
| import { HeadLabelProps } from "components/table/TableHeadCustom"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useTable, { UseTableReturn } from "hooks/useTable"; | |||
| import { isEqual } from "lodash"; | |||
| import { useState } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import useCreate from "./useCreate"; | |||
| export const SearchParam = { | |||
| PARKING_MANAGEMENT_CODE: "parking_management_code", | |||
| PARKING_NAME: "parking_name", | |||
| } as const; | |||
| export type SearchParam = (typeof SearchParam)[keyof typeof SearchParam]; | |||
| type Props = { onNext: (parking: HTParking) => void }; | |||
| export default function useParkingList({ onNext }: Props) { | |||
| const table = useTable<HTParking>(); | |||
| const { setParking } = useCreate(); | |||
| const handleNext = (parking: HTParking) => { | |||
| setParking(parking); | |||
| onNext(parking); | |||
| }; | |||
| const element = ( | |||
| <Box> | |||
| <SearchBox table={table} /> | |||
| <TableBox table={table} onNext={handleNext} /> | |||
| </Box> | |||
| ); | |||
| return { element }; | |||
| } | |||
| type FormProps = { | |||
| [SearchParam.PARKING_MANAGEMENT_CODE]: string; | |||
| [SearchParam.PARKING_NAME]: string; | |||
| }; | |||
| type CommonProps = { | |||
| table: UseTableReturn<HTParking>; | |||
| }; | |||
| function SearchBox({ table }: CommonProps) { | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| [SearchParam.PARKING_MANAGEMENT_CODE]: "", | |||
| [SearchParam.PARKING_NAME]: "", | |||
| }, | |||
| }); | |||
| const { callAPI: callGetHTParkings, makeSendData } = useAPICall({ | |||
| apiMethod: getHTParkings, | |||
| backDrop: true, | |||
| form, | |||
| onSuccess: (res) => { | |||
| table.setRowData(res.data.records); | |||
| }, | |||
| }); | |||
| const [currentSendData, setCurrentSendData] = | |||
| useState<HTParkingsRequest | null>(null); | |||
| const handleSubmit = () => { | |||
| fetch(); | |||
| }; | |||
| const handleBlur = () => { | |||
| fetch(); | |||
| }; | |||
| const fetch = async () => { | |||
| const data = form.getValues(); | |||
| if (!data.parking_name && !data.parking_management_code) return; | |||
| const sendData = makeSendData({ | |||
| ...data, | |||
| }); | |||
| if (!isEqual(sendData, currentSendData)) { | |||
| callGetHTParkings(sendData); | |||
| setCurrentSendData(sendData); | |||
| } | |||
| }; | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Box sx={{ p: 1, m: 1 }}> | |||
| <Grid container spacing={2}> | |||
| <Grid item xs={6} lg={4}> | |||
| <Stack> | |||
| <Typography>駐車場管理コード5桁</Typography> | |||
| <RHFTextField | |||
| name={SearchParam.PARKING_MANAGEMENT_CODE} | |||
| onBlur={handleBlur} | |||
| /> | |||
| </Stack> | |||
| </Grid> | |||
| <Grid item xs={6} lg={8}> | |||
| <Stack> | |||
| <Typography>駐車場名</Typography> | |||
| <RHFTextField | |||
| name={SearchParam.PARKING_NAME} | |||
| onBlur={handleBlur} | |||
| /> | |||
| </Stack> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </FormProvider> | |||
| ); | |||
| } | |||
| type TableProps = { | |||
| onNext: (parking: HTParking) => void; | |||
| } & CommonProps; | |||
| function TableBox({ table, onNext }: TableProps) { | |||
| const TABLE_HEAD: HeadLabelProps[] = [ | |||
| { | |||
| id: "customer_name", | |||
| label: "運営会社名", | |||
| align: "left", | |||
| needSort: false, | |||
| }, | |||
| { id: "parking_name", label: "駐車場名", align: "left", needSort: false }, | |||
| { | |||
| id: "parking_management_code", | |||
| label: "駐車場管理コード", | |||
| align: "left", | |||
| needSort: false, | |||
| }, | |||
| ]; | |||
| const { | |||
| order, | |||
| page, | |||
| sort, | |||
| rowsPerPage, | |||
| fetched, | |||
| fillteredRow, | |||
| isNotFound, | |||
| dataLength, | |||
| // | |||
| onSort, | |||
| onChangePage, | |||
| onChangeRowsPerPage, | |||
| // | |||
| setRowData, | |||
| // | |||
| ROWS_PER_PAGES, | |||
| } = table; | |||
| return ( | |||
| <> | |||
| <TableContainer | |||
| sx={{ | |||
| // minWidth: 800, | |||
| position: "relative", | |||
| }} | |||
| > | |||
| <Table size="small"> | |||
| <TableHeadCustom | |||
| order={order} | |||
| orderBy={sort} | |||
| headLabel={TABLE_HEAD} | |||
| rowCount={1} | |||
| numSelected={0} | |||
| onSort={onSort} | |||
| /> | |||
| <TableBody> | |||
| {fillteredRow.map((row, index) => ( | |||
| <Row data={row} key={index} onNext={onNext} /> | |||
| ))} | |||
| </TableBody> | |||
| </Table> | |||
| </TableContainer> | |||
| <Box sx={{ position: "relative" }}> | |||
| <TablePagination | |||
| rowsPerPageOptions={ROWS_PER_PAGES} | |||
| component="div" | |||
| count={dataLength} | |||
| rowsPerPage={rowsPerPage} | |||
| page={page} | |||
| onPageChange={onChangePage} | |||
| onRowsPerPageChange={onChangeRowsPerPage} | |||
| /> | |||
| </Box> | |||
| </> | |||
| ); | |||
| } | |||
| type RowProps = { | |||
| data: HTParking; | |||
| onNext: (parking: HTParking) => void; | |||
| }; | |||
| function Row({ data, onNext }: RowProps) { | |||
| const handleClick = () => { | |||
| onNext(data); | |||
| }; | |||
| return ( | |||
| <TableRow hover sx={{ cursor: "pointer" }} onClick={handleClick}> | |||
| <TableCell>{data.customer_name}</TableCell> | |||
| <TableCell>{data.parking_name ?? data.name}</TableCell> | |||
| <TableCell>{data.parking_management_code}</TableCell> | |||
| </TableRow> | |||
| ); | |||
| } | |||
| @@ -1,221 +0,0 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { Box, Button, Grid, Stack, Typography } from "@mui/material"; | |||
| import { HasChildren } from "@types"; | |||
| import { | |||
| getHTCustomers, | |||
| getHTParking, | |||
| getHTParkings, | |||
| } from "api/custom/hello-techno"; | |||
| import { | |||
| FormProvider, | |||
| RHFAutoComplete, | |||
| RHFTextField, | |||
| } from "components/hook-form"; | |||
| import { | |||
| AutoCompleteOption, | |||
| AutoCompleteOptionType, | |||
| } 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 * as Yup from "yup"; | |||
| type AreaBoxProps = { | |||
| title: string; | |||
| } & HasChildren; | |||
| function AreaBox({ title, children }: AreaBoxProps) { | |||
| return ( | |||
| <Box sx={{ maxWidth: 500 }}> | |||
| <Typography variant="subtitle1">{title}</Typography> | |||
| {children} | |||
| </Box> | |||
| ); | |||
| } | |||
| type FormProps = { | |||
| customerCode: AutoCompleteOptionType; | |||
| parkingManagementCode: AutoCompleteOptionType; | |||
| adjustSeqNo: string; | |||
| serachParkingManagementCode: string; | |||
| }; | |||
| type Props = { | |||
| onNext?: VoidFunction; | |||
| }; | |||
| export default function useSelectParkingStep({ onNext }: Props) { | |||
| const [customers, setCustomers] = useState<AutoCompleteOption[]>([]); | |||
| const [parkings, setParkings] = useState<AutoCompleteOption[]>([]); | |||
| const { error } = useSnackbarCustom(); | |||
| const customerAPI = useAPICall({ | |||
| apiMethod: getHTCustomers, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| const options: AutoCompleteOption[] = data.records.map( | |||
| ({ customer_code, name }) => { | |||
| return { | |||
| label: name, | |||
| value: customer_code, | |||
| }; | |||
| } | |||
| ); | |||
| setCustomers(options); | |||
| }, | |||
| }); | |||
| const parkingAPI = useAPICall({ | |||
| apiMethod: getHTParkings, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| const options: AutoCompleteOption[] = data.records.map( | |||
| ({ parking_management_code, name }) => { | |||
| return { | |||
| label: name, | |||
| value: parking_management_code, | |||
| }; | |||
| } | |||
| ); | |||
| setParkings(options); | |||
| }, | |||
| }); | |||
| const parkingByCodeAPI = useAPICall({ | |||
| apiMethod: getHTParking, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| const options: AutoCompleteOption[] = []; | |||
| options.push({ | |||
| label: data.name, | |||
| value: data.parking_management_code, | |||
| }); | |||
| setParkings(options); | |||
| form.setValue("customerCode", data.customer_code); | |||
| }, | |||
| onFailed: (res) => { | |||
| error("駐車場の特定に失敗しました"); | |||
| console.log(res); | |||
| }, | |||
| }); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| customerCode: null, | |||
| parkingManagementCode: null, | |||
| adjustSeqNo: "", | |||
| serachParkingManagementCode: "", | |||
| }, | |||
| resolver: yupResolver( | |||
| Yup.object().shape({ | |||
| customerCode: Yup.object().required("必須項目です"), | |||
| parkingManagementCode: Yup.object().required("必須項目です"), | |||
| adjustSeqNo: Yup.number() | |||
| .nullable() | |||
| .transform((value, originalValue) => | |||
| String(originalValue).trim() === "" ? null : value | |||
| ) | |||
| .typeError("数値を入力してください"), | |||
| }) | |||
| ), | |||
| }); | |||
| const customerCode = form.watch("customerCode.value"); | |||
| const searchParkingManagementCode = form.watch("serachParkingManagementCode"); | |||
| const handleSubmit = () => { | |||
| if (onNext) { | |||
| onNext(); | |||
| } | |||
| }; | |||
| const handleClickSearchParking = () => { | |||
| parkingByCodeAPI.callAPI({ | |||
| parking_management_code: searchParkingManagementCode, | |||
| }); | |||
| }; | |||
| const canSearchParking = useMemo(() => { | |||
| const reg = /^[0-9]{5}$/; | |||
| return reg.test(searchParkingManagementCode); | |||
| }, [searchParkingManagementCode]); | |||
| // 顧客一覧取得 | |||
| useEffect(() => { | |||
| customerAPI.callAPI({}); | |||
| }, []); | |||
| // 駐車場一覧取得 | |||
| useEffect(() => { | |||
| if (!canSearchParking) { | |||
| setParkings([]); | |||
| form.setValue("parkingManagementCode", null); | |||
| if (customerCode) { | |||
| parkingAPI.callAPI({ customer_code: customerCode }); | |||
| } | |||
| } | |||
| }, [customerCode]); | |||
| useEffect(() => { | |||
| if (parkings.length === 1) { | |||
| const parking = parkings[0]; | |||
| form.setValue("parkingManagementCode", parking); | |||
| } | |||
| }, [parkings]); | |||
| const element = ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Stack spacing={2} sx={{ p: 1, m: 1 }}> | |||
| <Grid container spacing={2}> | |||
| <Grid item xs={12} md={4}> | |||
| <Stack> | |||
| <AreaBox title="運営会社"> | |||
| <RHFAutoComplete | |||
| name="customerCode" | |||
| options={customers} | |||
| size="small" | |||
| /> | |||
| </AreaBox> | |||
| <AreaBox title="駐車場"> | |||
| <RHFAutoComplete | |||
| name="parkingManagementCode" | |||
| options={parkings} | |||
| size="small" | |||
| /> | |||
| </AreaBox> | |||
| </Stack> | |||
| </Grid> | |||
| <Grid item xs={12} md={4}> | |||
| <Stack spacing={3}> | |||
| <AreaBox title="駐車場管理コード"> | |||
| <RHFTextField name="serachParkingManagementCode" /> | |||
| </AreaBox> | |||
| <Stack direction="row"> | |||
| <Button | |||
| variant="outlined" | |||
| color="info" | |||
| onClick={handleClickSearchParking} | |||
| disabled={!canSearchParking} | |||
| > | |||
| 検索 | |||
| </Button> | |||
| </Stack> | |||
| </Stack> | |||
| </Grid> | |||
| </Grid> | |||
| <AreaBox title="精算連番"> | |||
| <RHFTextField name="adjustSeqNo" size="small" /> | |||
| </AreaBox> | |||
| <Stack direction="row" spacing={2}> | |||
| <Button variant="contained" type="submit"> | |||
| 次へ | |||
| </Button> | |||
| </Stack> | |||
| </Stack> | |||
| </FormProvider> | |||
| ); | |||
| return { element, values: form.getValues, setValue: form.setValue }; | |||
| } | |||