diff --git a/src/api/app/index.ts b/src/api/app/index.ts index e6c1459..fd02cfe 100644 --- a/src/api/app/index.ts +++ b/src/api/app/index.ts @@ -1,6 +1,6 @@ import { APICommonResponse, ApiId, HttpMethod, request } from ".."; -import { ReceiptIssuingOrder } from "../receipt-issuing-order"; import { getUrl } from "../url"; +import { AppReceiptIssuingOrder } from "./receipt-issuing-order"; export type CheckTokenRequest = { access_token: string; @@ -8,7 +8,7 @@ export type CheckTokenRequest = { export type CheckTokenResponse = { data: { - receipt_issuing_order: ReceiptIssuingOrder; + receipt_issuing_order: AppReceiptIssuingOrder; }; } & APICommonResponse; export const checkToken = async (data: CheckTokenRequest) => { diff --git a/src/api/app/receipt-issuing-order.ts b/src/api/app/receipt-issuing-order.ts index ee55a34..2533efe 100644 --- a/src/api/app/receipt-issuing-order.ts +++ b/src/api/app/receipt-issuing-order.ts @@ -1,7 +1,25 @@ -import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from ".."; -import { ReceiptIssuingOrder } from "../receipt-issuing-order"; +import { ApiId, HttpMethod, makeParam, request } from ".."; import { getUrl } from "../url"; +export type AppReceiptIssuingOrder = { + id: string; + + access_token_expires_at: string; + + receipt_use_date?: string; + receipt_shop_name?: string; + receipt_issuer?: string; + receipt_purpose?: string; + receipt_name?: string; + receipt_amount?: string; + confirmed: boolean; + + status_order_mail_datetime?: string; + status_mail_post_date?: string; + + updated_at: string; +}; + // 領収証確定 export type ConfirmRequest = { id: string; diff --git a/src/api/custom/hello-techno/receipt-issuing-order.ts b/src/api/custom/hello-techno/receipt-issuing-order.ts index fb52a3b..4ba6f47 100644 --- a/src/api/custom/hello-techno/receipt-issuing-order.ts +++ b/src/api/custom/hello-techno/receipt-issuing-order.ts @@ -5,6 +5,7 @@ import { makeFormData, request, } from "api"; +import { ReceiptIssuingOrder } from "api/receipt-issuing-order"; import { getUrl } from "api/url"; export type HTCustomer = { @@ -50,19 +51,16 @@ export const createReceiptIssuingOrder = async ( return res; }; -export type ReceiptIssuingOrder = { - id: string; - order_datetime: string; +export type ReceiptIssuingOrderHTCustom = { customer_code: string; parking_management_code: string; customer_name: string; parking_name: string; - status_name: string; - handler_id: string; - handler_name: string; -}; + adjust_seq_no?: number; +} & ReceiptIssuingOrder; export type ReceiptIssuingOrdersRequest = { + id?: string; customer_code?: string; customer_name?: string; parking_management_code?: string; @@ -71,7 +69,7 @@ export type ReceiptIssuingOrdersRequest = { export type ReceiptIssuingOrdersResponse = { data: { - records: ReceiptIssuingOrder[]; + records: ReceiptIssuingOrderHTCustom[]; }; } & APICommonResponse; diff --git a/src/api/index.ts b/src/api/index.ts index 0c820a1..3886998 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -24,6 +24,8 @@ export const ApiId = { RECEIPT_ISSUING_ORDERS: id++, RECEIPT_ISSUING_ORDER: id++, RECEIPT_ISSUING_ORDER_CREATE: id++, + RECEIPT_ISSUING_ORDER_MAIL_POST_COMPLETE: id++, + DOWNLOAD_RECEIPT_LETTER: id++, // FOR CUSTOM HT_CUSTOM_CUSTOMERS: id++, diff --git a/src/api/receipt-issuing-order.ts b/src/api/receipt-issuing-order.ts index 5707e09..47e0003 100644 --- a/src/api/receipt-issuing-order.ts +++ b/src/api/receipt-issuing-order.ts @@ -3,19 +3,42 @@ import { getUrl } from "./url"; export type ReceiptIssuingOrder = { id: string; + order_datetime: string; + status_name: string; + handler_id: string; + handler_name: string; + summary_key1?: string; + summary_key2?: string; + status_done: boolean; + contract_id: string; - access_token_expires_at: string; - + status_sms_send_datetime?: string; + status_first_access_datetime?: string; + status_receipt_confirm_datetime?: string; + status_order_mail_datetime?: string; + status_mail_download_datetime?: string; + status_mail_post_date?: string; + status_receipt_download_datetime?: string; + status_receipt_email_send_order_datetime?: string; + status_receipt_email_send_datetime?: string; + sms_phone_number?: string; + sms_send_success?: boolean; + receipt_no?: string; receipt_use_date?: string; receipt_shop_name?: string; receipt_issuer?: string; receipt_purpose?: string; receipt_name?: string; - receipt_amount?: string; - confirmed: boolean; - - status_order_mail_datetime?: string; - status_mail_post_date?: string; + receipt_invoice_no?: string; + receipt_amount?: number; + email?: string; + mail_pref_code?: string; + mail_zip_code?: string; + mail_address1?: string; + mail_address2?: string; + mail_address3?: string; + mail_name?: string; + memo?: string; updated_at: string; }; @@ -41,3 +64,19 @@ export const getReceiptIssuingOrders = async ( }); return res; }; + +// 郵送投函完了登録 ----------------------- +export type MailPostCompleteRequest = { + id: string; + status_mail_post_date: Date | null; + timestamp: string; +}; + +export const completeMailPost = async (data: MailPostCompleteRequest) => { + const res = await request({ + url: getUrl(ApiId.RECEIPT_ISSUING_ORDER_MAIL_POST_COMPLETE), + method: HttpMethod.POST, + data: makeParam(data), + }); + return res; +}; diff --git a/src/api/url.ts b/src/api/url.ts index ab19838..2d5273c 100644 --- a/src/api/url.ts +++ b/src/api/url.ts @@ -12,6 +12,9 @@ const urls = { [A.RECEIPT_ISSUING_ORDER_CONFIRM]: "receipt-issuing-order/confirm", [A.DOWNLOAD_RECEIPT]: "receipt/download", [A.RECEIPT_ISSUING_ORDER_MAIL_ORDER]: "receipt-issuing-order/mail-order", + [A.RECEIPT_ISSUING_ORDER_MAIL_POST_COMPLETE]: + "receipt-issuing-order/mail-complete", + [A.DOWNLOAD_RECEIPT_LETTER]: "receipt-letter/download", [A.RECEIPT_ISSUING_ORDERS]: "receipt-issuing-orders", @@ -28,6 +31,7 @@ const urls = { const prefixs = { [A.CSRF_TOKEN]: "", [A.DOWNLOAD_RECEIPT]: "", + [A.DOWNLOAD_RECEIPT_LETTER]: "", }; const DEFAULT_API_URL_PREFIX = "api"; diff --git a/src/components/table/index.ts b/src/components/table/index.ts deleted file mode 100644 index 8ef8635..0000000 --- a/src/components/table/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as TableHeadCustom } from "./TableHeadCustom"; diff --git a/src/components/table/index.tsx b/src/components/table/index.tsx new file mode 100644 index 0000000..e2ca244 --- /dev/null +++ b/src/components/table/index.tsx @@ -0,0 +1,61 @@ +import { + Button, + Stack, + SxProps, + Table, + TableBody, + TableCell, + TableRow, + Typography, + useTheme, +} from "@mui/material"; +import TextFieldEx from "components/form/TextFieldEx"; +import { ReactNode, useEffect, useMemo } from "react"; + +export { default as TableHeadCustom } from "./TableHeadCustom"; + +type SimpleDataListProps = { + data: { + title: string; + value?: string; + end?: ReactNode; + }[]; + tableSx?: SxProps; +}; +export const SimpleDataList = ({ data, tableSx }: SimpleDataListProps) => { + const { typography } = useTheme(); + + const fontSize = useMemo(() => { + return typography.body2.fontSize; + }, [typography]); + + return ( + + + {data.map(({ title, value, end }, index) => { + return ( + + + {title} + + + + + {end} + + + + ); + })} + +
+ ); +}; diff --git a/src/contexts/AppContext.tsx b/src/contexts/AppContext.tsx index 8801921..412e9cb 100644 --- a/src/contexts/AppContext.tsx +++ b/src/contexts/AppContext.tsx @@ -1,9 +1,6 @@ import { HasChildren } from "@types"; import { checkToken } from "api/app"; -import { - ReceiptIssuingOrder, - getReceiptIssuingOrders, -} from "api/receipt-issuing-order"; +import { AppReceiptIssuingOrder } from "api/app/receipt-issuing-order"; import { PageID } from "codes/page"; import useAPICall from "hooks/useAPICall"; import useNavigateCustom from "hooks/useNavigateCustom"; @@ -14,7 +11,7 @@ import { getPath } from "routes/path"; type App = { token: string; tokenResult: "cheking" | "ok" | "ng"; - receiptIssuingOrder: ReceiptIssuingOrder | null; + receiptIssuingOrder: AppReceiptIssuingOrder | null; setToken: (token: string) => void; fetch: VoidFunction; navigateToHome: VoidFunction; @@ -38,7 +35,7 @@ export function AppContextProvider({ children }: Props) { const [token, _setToken] = useState(""); const [receiptIssuingOrder, setReceiptIssuingOrder] = - useState(null); + useState(null); const [tokenResult, setTokenResult] = useState<"cheking" | "ok" | "ng">( "cheking" diff --git a/src/pages/dashboard/receipt-issuing-order/detail.tsx b/src/pages/dashboard/receipt-issuing-order/detail.tsx new file mode 100644 index 0000000..30628ab --- /dev/null +++ b/src/pages/dashboard/receipt-issuing-order/detail.tsx @@ -0,0 +1,271 @@ +import { + Box, + Button, + Divider, + Grid, + Paper, + Stack, + Typography, +} from "@mui/material"; +import { + ReceiptIssuingOrderHTCustom, + getReceiptIssuingOrders, +} from "api/custom/hello-techno/receipt-issuing-order"; +import { PageID } from "codes/page"; +import { getPrefName } from "codes/prefcode"; +import { SimpleDataList } from "components/table"; +import useAPICall from "hooks/useAPICall"; +import useBackDrop from "hooks/useBackDrop"; +import useDashboard from "hooks/useDashBoard"; +import useNavigateCustom from "hooks/useNavigateCustom"; +import useSnackbarCustom from "hooks/useSnackbarCustom"; +import { isNumber } from "lodash"; +import { useEffect, useMemo, useState } from "react"; +import { useParams } from "react-router-dom"; +import { sprintf } from "sprintf-js"; +import { formatDateStr, formatDateTimeStr } from "utils/datetime"; +import useMailPostCompleteDialog from "./hooks/useMailPostCompleteDialog"; +import { getFullUrl } from "api/url"; +import { ApiId } from "api"; + +export default function ReceiptIssuingOrderDetail() { + const { setHeaderTitle, setTabs } = useDashboard( + PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL + ); + + const { navigate } = useNavigateCustom(); + + const { error } = useSnackbarCustom(); + + const { id: receiptIssuingOrderId } = useParams(); + + const { setShowBackDrop } = useBackDrop(); + + const [order, setOrder] = useState(null); + + const postCompleteDialog = useMailPostCompleteDialog(order, () => { + fetch(); + }); + + const { callAPI, sending } = useAPICall({ + apiMethod: getReceiptIssuingOrders, + onSuccess: (res) => { + const target = res.data.records[0]; + if (target) { + setOrder(target); + } + }, + onFailed: () => { + error("情報取得失敗"); + navigate(-1); + }, + }); + + const fetch = () => { + if (receiptIssuingOrderId) { + callAPI({ + id: receiptIssuingOrderId, + }); + } + }; + + const hasMailOrder = useMemo(() => { + return !!order?.status_order_mail_datetime; + }, [order]); + const orderInfo = useMemo(() => { + if (!order) return []; + return [ + { title: "ステータス", value: order.status_name }, + { + title: "担当者", + value: order.handler_name, + end: , + }, + { title: "SMS送信先", value: order.sms_phone_number }, + { title: "依頼日", value: formatDateStr(order.order_datetime) }, + { title: "運営会社", value: order.customer_name }, + { title: "駐車場", value: order.parking_name }, + { title: "利用日", value: formatDateStr(order.receipt_use_date) }, + { + title: "精算連番", + value: isNumber(order.adjust_seq_no) + ? sprintf("%06d", order.adjust_seq_no) + : "-", + }, + { + title: "金額", + value: isNumber(order.receipt_amount) + ? order.receipt_amount.toLocaleString() + "円" + : "-", + }, + { title: "備考", value: order.memo }, + ]; + }, [order]); + + const mailStatus1 = useMemo(() => { + if (!order) return []; + return [{ title: "郵送依頼", value: hasMailOrder ? "あり" : "なし" }]; + }, [order]); + const mailStatus2 = useMemo(() => { + if (!order || !hasMailOrder) return []; + return [ + { title: "郵便番号", value: "〒" + order.mail_zip_code }, + { title: "都道府県", value: getPrefName(order.mail_pref_code ?? "") }, + { title: "市区町村", value: order.mail_address1 }, + { title: "番地等", value: order.mail_address2 }, + { title: "建物名/部屋番号等", value: order.mail_address3 }, + { title: "宛名", value: order.mail_name }, + ]; + }, [order]); + + const receiptInfo = useMemo(() => { + if (!order) return []; + return [ + { title: "領収証番号", value: order.receipt_no }, + { + title: "宛名", + value: order.receipt_name, + }, + ]; + }, [order]); + + const actionDatetimes = useMemo(() => { + if (!order) return []; + return [ + { + title: "SMS送信", + value: formatDateTimeStr(order.status_sms_send_datetime), + }, + { + title: "初回アクセス", + value: formatDateTimeStr(order.status_first_access_datetime), + }, + { + title: "領収証確定", + value: formatDateTimeStr(order.status_receipt_confirm_datetime), + }, + { + title: "郵送依頼", + value: formatDateTimeStr(order.status_order_mail_datetime), + }, + { title: "郵送投函", value: formatDateStr(order.status_mail_post_date) }, + { + title: "領収証ダウンロード", + value: formatDateTimeStr(order.status_receipt_download_datetime), + }, + { + title: "メール配信依頼", + value: formatDateTimeStr( + order.status_receipt_email_send_order_datetime + ), + }, + { + title: "メール配信完了", + value: formatDateTimeStr(order.status_receipt_email_send_datetime), + }, + ]; + }, [order]); + + const downloadUrl = useMemo(() => { + return getFullUrl(ApiId.DOWNLOAD_RECEIPT_LETTER) + "?id=" + order?.id; + }, [order]); + + useEffect(() => { + setHeaderTitle("領収証発行依頼"); + setTabs(null); + + fetch(); + }, []); + + useEffect(() => { + setShowBackDrop(sending); + }, [sending]); + + return ( + + + + + + + + + + + + 依頼内容 + + + + + 領収証情報 + + + + + + + + + + 郵送管理 + + + {hasMailOrder && ( + <> + + + 郵送先 + + + + + + + + + {postCompleteDialog.element} + + )} + + + + + + + + 各種アクション日時 + + + + + + + + ); +} diff --git a/src/pages/dashboard/receipt-issuing-order/hooks/useMailPostCompleteDialog.tsx b/src/pages/dashboard/receipt-issuing-order/hooks/useMailPostCompleteDialog.tsx new file mode 100644 index 0000000..fa980bb --- /dev/null +++ b/src/pages/dashboard/receipt-issuing-order/hooks/useMailPostCompleteDialog.tsx @@ -0,0 +1,118 @@ +import { yupResolver } from "@hookform/resolvers/yup"; +import { + Button, + Dialog, + DialogActions, + DialogContent, + DialogTitle, +} from "@mui/material"; +import { + ReceiptIssuingOrder, + completeMailPost, +} from "api/receipt-issuing-order"; +import { FormProvider } from "components/hook-form"; +import RHFDatePicker from "components/hook-form/RHFDatePicker"; +import useAPICall from "hooks/useAPICall"; +import useBackDrop from "hooks/useBackDrop"; +import useSnackbarCustom from "hooks/useSnackbarCustom"; +import { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { now } from "utils/datetime"; +import * as Yup from "yup"; + +type FormProps = { + date: Date | null; +}; + +export default function useMailPostCompleteDialog( + order: ReceiptIssuingOrder | null, + onComplete?: VoidFunction +) { + const [show, setShow] = useState(false); + const { setShowBackDrop } = useBackDrop(); + const { success, error } = useSnackbarCustom(); + + const form = useForm({ + defaultValues: { + date: now(), + }, + resolver: yupResolver( + Yup.object().shape({ + date: Yup.date() + .required("必須項目です") + .typeError("正しく入力してください"), + }) + ), + }); + + const { callAPI, sending } = useAPICall({ + apiMethod: completeMailPost, + onSuccess: () => { + success("登録しました"); + setShow(false); + if (onComplete) { + onComplete(); + } + }, + onFailed: () => { + error("失敗しました"); + }, + }); + + const open = () => { + setShow(true); + }; + + const close = () => { + setShow(false); + }; + + const handleSubmit = (data: FormProps) => { + if (!order) return; + callAPI({ + id: order.id, + status_mail_post_date: data.date, + timestamp: order.updated_at, + }); + }; + + const element = ( + + 投稿完了登録 + + + + + + + + + + + + + ); + + useEffect(() => { + setShowBackDrop(sending); + }, [sending]); + + useEffect(() => { + console.log(now()); + // console.log(formatDateStr(now())); + // form.setValue('date', formatDateStr(now()) + }, []); + + return { + // param + show, + + // Element + element, + // function + + open, + close, + setShow, + }; +} diff --git a/src/pages/dashboard/receipt-issuing-order/list.tsx b/src/pages/dashboard/receipt-issuing-order/list.tsx index 6d18530..94bef6a 100644 --- a/src/pages/dashboard/receipt-issuing-order/list.tsx +++ b/src/pages/dashboard/receipt-issuing-order/list.tsx @@ -10,7 +10,7 @@ import { } from "@mui/material"; import { Dictionary } from "@types"; import { - ReceiptIssuingOrder, + ReceiptIssuingOrderHTCustom, getReceiptIssuingOrders, } from "api/custom/hello-techno/receipt-issuing-order"; import { PageID, TabID } from "codes/page"; @@ -20,10 +20,12 @@ import { SearchConditionContextProvider } from "contexts/SearchConditionContext" import useAPICall from "hooks/useAPICall"; import useBackDrop from "hooks/useBackDrop"; import useDashboard from "hooks/useDashBoard"; +import useNavigateCustom from "hooks/useNavigateCustom"; import useSearchConditionContext from "hooks/useSearchConditionContext"; import useTable, { UseTableReturn } from "hooks/useTable"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; +import { getPath } from "routes/path"; export default function ReceiptIssuingOrderList() { const { setHeaderTitle, setTabs } = useDashboard( @@ -44,7 +46,7 @@ export default function ReceiptIssuingOrderList() { } function Page() { - const table = useTable(); + const table = useTable(); return ( @@ -59,7 +61,7 @@ type FormProps = { parking_name: string; }; type CommonProps = { - table: UseTableReturn; + table: UseTableReturn; }; function SearchBox({ table }: CommonProps) { const { @@ -226,11 +228,23 @@ function TableBox({ table }: CommonProps) { } type RowProps = { - data: ReceiptIssuingOrder; + data: ReceiptIssuingOrderHTCustom; }; function Row({ data }: RowProps) { + const { navigateWhenChanged } = useNavigateCustom(); + + const handleClick = () => { + navigateWhenChanged( + getPath(PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL, { + query: { + id: data.id, + }, + }) + ); + }; + return ( - + {data.order_datetime} {data.customer_name} {data.parking_name} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 8e61d00..88d2283 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -78,6 +78,11 @@ const DashboardRoutes = (): RouteObject => { element: , target: UserRole.NORMAL_ADMIN, }, + { + pageId: PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL, + element: , + target: UserRole.NORMAL_ADMIN, + }, ]; const children: RouteObject[] = useMemo(() => { @@ -138,6 +143,9 @@ const ReceiptIssuingOrderCreate = Loadable( const ReceiptIssuingOrderList = Loadable( lazy(() => import("pages/dashboard/receipt-issuing-order/list")) ); +const ReceiptIssuingOrderDetail = Loadable( + lazy(() => import("pages/dashboard/receipt-issuing-order/detail")) +); // その他 --------------------------------- const Page403 = Loadable(lazy(() => import("pages/common/Page403"))); diff --git a/src/routes/path.ts b/src/routes/path.ts index baf9a24..0dc12fe 100644 --- a/src/routes/path.ts +++ b/src/routes/path.ts @@ -54,7 +54,7 @@ const PATHS = { [makePathKey(PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_LIST)]: "/dashboard/receipt-issusing-order/list/:page", [makePathKey(PageID.DASHBOARD_RECEIPT_ISSUING_ORDER_DETAIL)]: - "/dashboard/receipt-issusing-order/detail", + "/dashboard/receipt-issusing-order/detail/:id", // その他 [makePathKey(PageID.PAGE_403)]: "403",