| @@ -27,6 +27,8 @@ export const ApiId = { | |||
| RECEIPT_ISSUING_ORDER: id++, | |||
| RECEIPT_ISSUING_ORDER_CREATE: id++, | |||
| RECEIPT_ISSUING_ORDER_MAIL_POST_COMPLETE: id++, | |||
| RECEIPT_ISSUING_ORDER_HANDELRS: id++, | |||
| RECEIPT_ISSUING_ORDER_CHANGE_HANDELR: id++, | |||
| DOWNLOAD_RECEIPT_LETTER: id++, | |||
| CONTRACTS: id++, | |||
| @@ -1,5 +1,14 @@ | |||
| import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "."; | |||
| import { Dictionary } from "@types"; | |||
| import { | |||
| APICommonResponse, | |||
| ApiId, | |||
| HttpMethod, | |||
| TimestampRequest, | |||
| makeParam, | |||
| request, | |||
| } from "."; | |||
| import { getUrl } from "./url"; | |||
| import { string } from "yup"; | |||
| export type ReceiptIssuingOrder = { | |||
| id: string; | |||
| @@ -43,6 +52,11 @@ export type ReceiptIssuingOrder = { | |||
| updated_at: string; | |||
| }; | |||
| export type Handler = { | |||
| id: string; | |||
| name: string; | |||
| }; | |||
| // 領収証発行一覧取得 ----------------------- | |||
| export type ReceiptIssuingOrdersRequest = { | |||
| address?: string; | |||
| @@ -80,3 +94,35 @@ export const completeMailPost = async (data: MailPostCompleteRequest) => { | |||
| }); | |||
| return res; | |||
| }; | |||
| // 担当者一覧取得 ----------------------- | |||
| export type HandlersRequest = {}; | |||
| export type HandlersResponse = { | |||
| data: { | |||
| records: Handler[]; | |||
| }; | |||
| } & APICommonResponse; | |||
| export const getHandlers = async (data: HandlersRequest) => { | |||
| const res = await request<HandlersResponse>({ | |||
| url: getUrl(ApiId.RECEIPT_ISSUING_ORDER_HANDELRS), | |||
| method: HttpMethod.GET, | |||
| data: makeParam(data), | |||
| }); | |||
| return res; | |||
| }; | |||
| // 担当者変更 ----------------------- | |||
| export type ChangeHandlerRequest = { | |||
| id: string; | |||
| handler_id: string; | |||
| } & TimestampRequest; | |||
| export const changeHandler = async (data: ChangeHandlerRequest) => { | |||
| const res = await request({ | |||
| url: getUrl(ApiId.RECEIPT_ISSUING_ORDER_CHANGE_HANDELR), | |||
| method: HttpMethod.POST, | |||
| data: makeParam(data), | |||
| }); | |||
| return res; | |||
| }; | |||
| @@ -15,6 +15,9 @@ const urls = { | |||
| [A.RECEIPT_ISSUING_ORDER_MAIL_ORDER]: "receipt-issuing-order/mail-order", | |||
| [A.RECEIPT_ISSUING_ORDER_MAIL_POST_COMPLETE]: | |||
| "receipt-issuing-order/mail-complete", | |||
| [A.RECEIPT_ISSUING_ORDER_HANDELRS]: "receipt-issuing-order/handlers", | |||
| [A.RECEIPT_ISSUING_ORDER_CHANGE_HANDELR]: | |||
| "receipt-issuing-order/change-handler", | |||
| [A.DOWNLOAD_RECEIPT_LETTER]: "receipt-letter/download", | |||
| [A.RECEIPT_ISSUING_ORDERS]: "receipt-issuing-orders", | |||
| @@ -27,6 +27,7 @@ import { formatDateStr, formatDateTimeStr } from "utils/datetime"; | |||
| import useMailPostCompleteDialog from "./hooks/useMailPostCompleteDialog"; | |||
| import { getFullUrl } from "api/url"; | |||
| import { ApiId } from "api"; | |||
| import useChangeHandlerDialog from "../../hooks/useChangeHandlerDialog"; | |||
| export default function ReceiptIssuingOrderDetail() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| @@ -47,6 +48,10 @@ export default function ReceiptIssuingOrderDetail() { | |||
| fetch(); | |||
| }); | |||
| const changeHandlerDialog = useChangeHandlerDialog(order, () => { | |||
| fetch(); | |||
| }); | |||
| const { callAPI, sending } = useAPICall({ | |||
| apiMethod: getReceiptIssuingOrders, | |||
| onSuccess: (res) => { | |||
| @@ -79,7 +84,16 @@ export default function ReceiptIssuingOrderDetail() { | |||
| { | |||
| title: "担当者", | |||
| value: order.handler_name, | |||
| // end: <Button sx={{ minWidth: 80 }}>担当変更</Button>, | |||
| end: ( | |||
| <Button | |||
| sx={{ minWidth: 80 }} | |||
| onClick={() => { | |||
| changeHandlerDialog.open(); | |||
| }} | |||
| > | |||
| 担当変更 | |||
| </Button> | |||
| ), | |||
| }, | |||
| { title: "SMS送信先", value: order.sms_phone_number }, | |||
| { title: "依頼日", value: formatDateStr(order.order_datetime) }, | |||
| @@ -182,94 +196,97 @@ export default function ReceiptIssuingOrderDetail() { | |||
| }, [sending]); | |||
| return ( | |||
| <Box> | |||
| <Grid container spacing={2}> | |||
| <Grid item xs={12}> | |||
| <Stack direction="row" spacing={2}> | |||
| <Button onClick={() => navigate(-1)}>戻る</Button> | |||
| </Stack> | |||
| </Grid> | |||
| <Grid item xs={4}> | |||
| <Paper elevation={0} sx={{ border: "1px solid" }}> | |||
| <Stack spacing={2}> | |||
| <Typography | |||
| variant="h6" | |||
| fontWeight="bold" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 依頼内容 | |||
| </Typography> | |||
| <SimpleDataList data={orderInfo} /> | |||
| <Divider sx={{ my: 2 }} /> | |||
| <Typography | |||
| variant="subtitle1" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 領収証情報 | |||
| </Typography> | |||
| <SimpleDataList data={receiptInfo} /> | |||
| <> | |||
| <Box> | |||
| <Grid container spacing={2}> | |||
| <Grid item xs={12}> | |||
| <Stack direction="row" spacing={2}> | |||
| <Button onClick={() => navigate(-1)}>戻る</Button> | |||
| </Stack> | |||
| </Paper> | |||
| </Grid> | |||
| <Grid item xs={4}> | |||
| <Paper elevation={0} sx={{ pb: 2, border: "1px solid" }}> | |||
| <Stack spacing={2}> | |||
| <Typography | |||
| variant="h6" | |||
| fontWeight="bold" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 郵送管理 | |||
| </Typography> | |||
| <SimpleDataList data={mailStatus1} /> | |||
| {hasMailOrder && ( | |||
| <> | |||
| <Divider sx={{ my: 2 }} /> | |||
| <Typography | |||
| variant="subtitle1" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 郵送先 | |||
| </Typography> | |||
| <SimpleDataList data={mailStatus2} /> | |||
| <Divider /> | |||
| <Stack direction="row" spacing={2} sx={{ px: 2 }}> | |||
| <Button | |||
| variant="contained" | |||
| href={downloadUrl} | |||
| target="_blank" | |||
| </Grid> | |||
| <Grid item xs={4}> | |||
| <Paper elevation={0} sx={{ border: "1px solid" }}> | |||
| <Stack spacing={2}> | |||
| <Typography | |||
| variant="h6" | |||
| fontWeight="bold" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 依頼内容 | |||
| </Typography> | |||
| <SimpleDataList data={orderInfo} /> | |||
| <Divider sx={{ my: 2 }} /> | |||
| <Typography | |||
| variant="subtitle1" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 領収証情報 | |||
| </Typography> | |||
| <SimpleDataList data={receiptInfo} /> | |||
| </Stack> | |||
| </Paper> | |||
| </Grid> | |||
| <Grid item xs={4}> | |||
| <Paper elevation={0} sx={{ pb: 2, border: "1px solid" }}> | |||
| <Stack spacing={2}> | |||
| <Typography | |||
| variant="h6" | |||
| fontWeight="bold" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 郵送管理 | |||
| </Typography> | |||
| <SimpleDataList data={mailStatus1} /> | |||
| {hasMailOrder && ( | |||
| <> | |||
| <Divider sx={{ my: 2 }} /> | |||
| <Typography | |||
| variant="subtitle1" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 印字 | |||
| </Button> | |||
| 郵送先 | |||
| </Typography> | |||
| <SimpleDataList data={mailStatus2} /> | |||
| <Divider /> | |||
| <Stack direction="row" spacing={2} sx={{ px: 2 }}> | |||
| <Button | |||
| variant="contained" | |||
| href={downloadUrl} | |||
| target="_blank" | |||
| > | |||
| 印字 | |||
| </Button> | |||
| <Button | |||
| variant="contained" | |||
| onClick={postCompleteDialog.open} | |||
| > | |||
| 投函完了 | |||
| </Button> | |||
| </Stack> | |||
| {postCompleteDialog.element} | |||
| </> | |||
| )} | |||
| </Stack> | |||
| </Paper> | |||
| </Grid> | |||
| <Grid item xs={4}> | |||
| <Paper elevation={0} sx={{ border: "1px solid" }}> | |||
| <Stack spacing={2}> | |||
| <Typography | |||
| variant="h6" | |||
| fontWeight="bold" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 各種アクション日時 | |||
| </Typography> | |||
| <SimpleDataList data={actionDatetimes} /> | |||
| </Stack> | |||
| </Paper> | |||
| <Button | |||
| variant="contained" | |||
| onClick={postCompleteDialog.open} | |||
| > | |||
| 投函完了 | |||
| </Button> | |||
| </Stack> | |||
| {postCompleteDialog.element} | |||
| </> | |||
| )} | |||
| </Stack> | |||
| </Paper> | |||
| </Grid> | |||
| <Grid item xs={4}> | |||
| <Paper elevation={0} sx={{ border: "1px solid" }}> | |||
| <Stack spacing={2}> | |||
| <Typography | |||
| variant="h6" | |||
| fontWeight="bold" | |||
| sx={{ my: 2, textAlign: "center" }} | |||
| > | |||
| 各種アクション日時 | |||
| </Typography> | |||
| <SimpleDataList data={actionDatetimes} /> | |||
| </Stack> | |||
| </Paper> | |||
| </Grid> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| </Box> | |||
| {changeHandlerDialog.element} | |||
| </> | |||
| ); | |||
| } | |||
| @@ -0,0 +1,144 @@ | |||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||
| import { | |||
| Button, | |||
| Dialog, | |||
| DialogActions, | |||
| DialogContent, | |||
| DialogTitle, | |||
| } from "@mui/material"; | |||
| import { Dictionary } from "@types"; | |||
| import { | |||
| Handler, | |||
| ReceiptIssuingOrder, | |||
| changeHandler, | |||
| getHandlers, | |||
| } from "api/receipt-issuing-order"; | |||
| import { FormProvider } from "components/hook-form"; | |||
| import RHFSelect, { | |||
| SelectOptionProps, | |||
| makeOptions, | |||
| } from "components/hook-form/RHFSelect"; | |||
| 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 FormProps = { | |||
| handler_id: string; | |||
| }; | |||
| export default function useChangeHandlerDialog( | |||
| order: ReceiptIssuingOrder | null, | |||
| onComplete?: VoidFunction | |||
| ) { | |||
| const [show, setShow] = useState(false); | |||
| const { success, error } = useSnackbarCustom(); | |||
| const [handlers, setHandlers] = useState<Handler[] | null>(null); | |||
| const options: SelectOptionProps[] = useMemo(() => { | |||
| if (handlers === null) return []; | |||
| return handlers.map((ele) => ({ | |||
| value: ele.id, | |||
| label: ele.name, | |||
| })); | |||
| }, [handlers]); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| handler_id: "", | |||
| }, | |||
| resolver: yupResolver( | |||
| Yup.object().shape({ | |||
| handler_id: Yup.string().required("必須項目です"), | |||
| }) | |||
| ), | |||
| }); | |||
| const { callAPI: callGetHandlers } = useAPICall({ | |||
| apiMethod: getHandlers, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| setHandlers(data.records); | |||
| }, | |||
| }); | |||
| const { callAPI: callChangeHandler } = useAPICall({ | |||
| apiMethod: changeHandler, | |||
| backDrop: true, | |||
| onSuccess: () => { | |||
| success("変更しました"); | |||
| setShow(false); | |||
| if (onComplete) { | |||
| onComplete(); | |||
| } | |||
| }, | |||
| onFailed: () => { | |||
| error("失敗しました"); | |||
| }, | |||
| }); | |||
| const open = () => { | |||
| setShow(true); | |||
| }; | |||
| const close = () => { | |||
| setShow(false); | |||
| }; | |||
| const handleSubmit = (data: FormProps) => { | |||
| if (!order) return; | |||
| callChangeHandler({ | |||
| id: order.id, | |||
| handler_id: data.handler_id, | |||
| timestamp: order.updated_at, | |||
| }); | |||
| }; | |||
| const element = ( | |||
| <Dialog | |||
| open={show} | |||
| onClose={close} | |||
| PaperProps={{ | |||
| sx: { | |||
| width: "50%", | |||
| }, | |||
| }} | |||
| > | |||
| <DialogTitle>担当者変更</DialogTitle> | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <DialogContent> | |||
| <RHFSelect name="handler_id" label="担当者" options={options} /> | |||
| </DialogContent> | |||
| <DialogActions> | |||
| <Button onClick={close}>CANCEL</Button> | |||
| <Button type="submit">OK</Button> | |||
| </DialogActions> | |||
| </FormProvider> | |||
| </Dialog> | |||
| ); | |||
| useEffect(() => { | |||
| setHandlers(null); | |||
| form.setValue("handler_id", ""); | |||
| if (show) { | |||
| callGetHandlers({}); | |||
| } | |||
| }, [show]); | |||
| return { | |||
| // param | |||
| show, | |||
| // Element | |||
| element, | |||
| // function | |||
| open, | |||
| close, | |||
| setShow, | |||
| }; | |||
| } | |||