| @@ -21,6 +21,7 @@ | |||||
| "@types/react": "^18.0.0", | "@types/react": "^18.0.0", | ||||
| "@types/react-dom": "^18.0.0", | "@types/react-dom": "^18.0.0", | ||||
| "@types/react-router-dom": "^5.3.3", | "@types/react-router-dom": "^5.3.3", | ||||
| "@types/sprintf-js": "^1.1.2", | |||||
| "axios": "^1.4.0", | "axios": "^1.4.0", | ||||
| "date-fns": "^2.30.0", | "date-fns": "^2.30.0", | ||||
| "lodash": "^4.17.21", | "lodash": "^4.17.21", | ||||
| @@ -30,6 +31,7 @@ | |||||
| "react-hook-form": "^7.43.9", | "react-hook-form": "^7.43.9", | ||||
| "react-router-dom": "^6.11.0", | "react-router-dom": "^6.11.0", | ||||
| "react-scripts": "5.0.1", | "react-scripts": "5.0.1", | ||||
| "sprintf-js": "^1.1.2", | |||||
| "typescript": "^4.4.2", | "typescript": "^4.4.2", | ||||
| "web-vitals": "^2.1.0", | "web-vitals": "^2.1.0", | ||||
| "yup": "^1.1.1" | "yup": "^1.1.1" | ||||
| @@ -1,15 +0,0 @@ | |||||
| import { ApiId, HttpMethod, request } from "."; | |||||
| import { getUrl } from "./url"; | |||||
| export type CheckTokenRequest = { | |||||
| access_token: string; | |||||
| }; | |||||
| export const checkToken = async (data: CheckTokenRequest) => { | |||||
| const res = await request({ | |||||
| url: getUrl(ApiId.APP_TOKEN_CHECK), | |||||
| method: HttpMethod.GET, | |||||
| data: new URLSearchParams(data), | |||||
| }); | |||||
| return res; | |||||
| }; | |||||
| @@ -0,0 +1,21 @@ | |||||
| import { APICommonResponse, ApiId, HttpMethod, request } from ".."; | |||||
| import { ReceiptIssuingOrder } from "../receipt-issuing-order"; | |||||
| import { getUrl } from "../url"; | |||||
| export type CheckTokenRequest = { | |||||
| access_token: string; | |||||
| }; | |||||
| export type CheckTokenResponse = { | |||||
| data: { | |||||
| receipt_issuing_order: ReceiptIssuingOrder; | |||||
| }; | |||||
| } & APICommonResponse; | |||||
| export const checkToken = async (data: CheckTokenRequest) => { | |||||
| const res = await request<CheckTokenResponse>({ | |||||
| url: getUrl(ApiId.APP_TOKEN_CHECK), | |||||
| method: HttpMethod.GET, | |||||
| data: new URLSearchParams(data), | |||||
| }); | |||||
| return res; | |||||
| }; | |||||
| @@ -0,0 +1,40 @@ | |||||
| import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from ".."; | |||||
| import { ReceiptIssuingOrder } from "../receipt-issuing-order"; | |||||
| import { getUrl } from "../url"; | |||||
| // 領収証確定 | |||||
| export type ConfirmRequest = { | |||||
| id: string; | |||||
| receipt_name: string; | |||||
| timestamp: string; | |||||
| }; | |||||
| export const confirm = async (data: ConfirmRequest) => { | |||||
| const res = await request({ | |||||
| url: getUrl(ApiId.RECEIPT_ISSUING_ORDER_CONFIRM), | |||||
| method: HttpMethod.POST, | |||||
| data: makeParam(data), | |||||
| }); | |||||
| return res; | |||||
| }; | |||||
| // 領収証郵送依頼 ------------------------------- | |||||
| export type MailOrderRequest = { | |||||
| id: string; | |||||
| mail_pref_code: string; | |||||
| mail_zip_code: string; | |||||
| mail_address1: string; | |||||
| mail_address2: string; | |||||
| mail_address3?: string; | |||||
| mail_name: string; | |||||
| timestamp: string; | |||||
| }; | |||||
| export const mailRequest = async (data: MailOrderRequest) => { | |||||
| const res = await request({ | |||||
| url: getUrl(ApiId.RECEIPT_ISSUING_ORDER_MAIL_ORDER), | |||||
| method: HttpMethod.POST, | |||||
| data: makeParam(data), | |||||
| }); | |||||
| return res; | |||||
| }; | |||||
| @@ -6,7 +6,6 @@ import { | |||||
| request, | request, | ||||
| } from "api"; | } from "api"; | ||||
| import { getUrl } from "api/url"; | import { getUrl } from "api/url"; | ||||
| import { ReceiptIssuingOrderStatus } from "codes/receipt-issuing-order"; | |||||
| export type HTCustomer = { | export type HTCustomer = { | ||||
| customer_code: string; | customer_code: string; | ||||
| @@ -28,8 +27,7 @@ export type CreateReceiptIssuingOrderRequest = { | |||||
| customer_code: string; | customer_code: string; | ||||
| parking_management_code: string; | parking_management_code: string; | ||||
| adjust_seq_no?: string | number; | adjust_seq_no?: string | number; | ||||
| receipt_name: string; | |||||
| receipt_use_datetime: Date | null; | |||||
| receipt_use_date: Date | null; | |||||
| receipt_amount: string | number; | receipt_amount: string | number; | ||||
| memo?: string; | memo?: string; | ||||
| sms_phone_number: string; | sms_phone_number: string; | ||||
| @@ -59,7 +57,7 @@ export type ReceiptIssuingOrder = { | |||||
| parking_management_code: string; | parking_management_code: string; | ||||
| customer_name: string; | customer_name: string; | ||||
| parking_name: string; | parking_name: string; | ||||
| status: ReceiptIssuingOrderStatus; | |||||
| status_name: string; | |||||
| handler_id: string; | handler_id: string; | ||||
| handler_name: string; | handler_name: string; | ||||
| }; | }; | ||||
| @@ -7,15 +7,20 @@ import axios from "utils/axios"; | |||||
| let id = 0; | let id = 0; | ||||
| export const ApiId = { | export const ApiId = { | ||||
| // 共通--------------------------------------- | |||||
| CSRF_TOKEN: id++, | CSRF_TOKEN: id++, | ||||
| ME: id++, | ME: id++, | ||||
| LOGIN: id++, | LOGIN: id++, | ||||
| LOGOUT: id++, | LOGOUT: id++, | ||||
| // APP向け------------------------------------ | |||||
| APP_TOKEN_CHECK: id++, | APP_TOKEN_CHECK: id++, | ||||
| RECEIPT_ISSUING_ORDER_CONFIRM: id++, | |||||
| DOWNLOAD_RECEIPT: id++, | DOWNLOAD_RECEIPT: id++, | ||||
| RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, | |||||
| // DASHBOARD向け------------------------------- | |||||
| RECEIPT_ISSUING_ORDERS: id++, | RECEIPT_ISSUING_ORDERS: id++, | ||||
| RECEIPT_ISSUING_ORDER: id++, | RECEIPT_ISSUING_ORDER: id++, | ||||
| RECEIPT_ISSUING_ORDER_CREATE: id++, | RECEIPT_ISSUING_ORDER_CREATE: id++, | ||||
| @@ -245,6 +250,9 @@ export async function apiRequest< | |||||
| } | } | ||||
| return res; | return res; | ||||
| } catch (e) { | } catch (e) { | ||||
| if (setSending) { | |||||
| setSending(false); | |||||
| } | |||||
| console.error(e); | console.error(e); | ||||
| if (onFailed) { | if (onFailed) { | ||||
| onFailed(null); | onFailed(null); | ||||
| @@ -1,15 +1,28 @@ | |||||
| import { ReceiptIssuingOrderStatus } from "codes/receipt-issuing-order"; | |||||
| import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "."; | import { APICommonResponse, ApiId, HttpMethod, makeParam, request } from "."; | ||||
| import { getUrl } from "./url"; | import { getUrl } from "./url"; | ||||
| export type ReceiptIssuingOrder = { | export type ReceiptIssuingOrder = { | ||||
| id: string; | id: string; | ||||
| status: ReceiptIssuingOrderStatus; | |||||
| 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 ReceiptIssuingOrdersRequest = { | export type ReceiptIssuingOrdersRequest = { | ||||
| address?: string; | address?: string; | ||||
| status?: ReceiptIssuingOrderStatus; | |||||
| }; | }; | ||||
| export type ReceiptIssuingOrdersResponse = { | export type ReceiptIssuingOrdersResponse = { | ||||
| @@ -9,7 +9,9 @@ const urls = { | |||||
| [A.LOGOUT]: "logout", | [A.LOGOUT]: "logout", | ||||
| [A.APP_TOKEN_CHECK]: "app-token-check", | [A.APP_TOKEN_CHECK]: "app-token-check", | ||||
| [A.RECEIPT_ISSUING_ORDER_CONFIRM]: "receipt-issuing-order/confirm", | |||||
| [A.DOWNLOAD_RECEIPT]: "receipt/download", | [A.DOWNLOAD_RECEIPT]: "receipt/download", | ||||
| [A.RECEIPT_ISSUING_ORDER_MAIL_ORDER]: "receipt-issuing-order/mail-order", | |||||
| [A.RECEIPT_ISSUING_ORDERS]: "receipt-issuing-orders", | [A.RECEIPT_ISSUING_ORDERS]: "receipt-issuing-orders", | ||||
| @@ -6,6 +6,7 @@ export const PageID = { | |||||
| LOGOUT: id++, | LOGOUT: id++, | ||||
| APP_RECEIPT_ISSUING_ORDER_INDEX: id++, | APP_RECEIPT_ISSUING_ORDER_INDEX: id++, | ||||
| APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER: id++, | |||||
| DASHBOARD_OVERVIEW: id++, | DASHBOARD_OVERVIEW: id++, | ||||
| @@ -0,0 +1,59 @@ | |||||
| import { SelectOptionProps } from "components/hook-form/RHFSelect"; | |||||
| export const prefCodeOptions: SelectOptionProps[] = [ | |||||
| { value: "01", label: "北海道" }, | |||||
| { value: "02", label: "青森県" }, | |||||
| { value: "03", label: "岩手県" }, | |||||
| { value: "04", label: "宮城県" }, | |||||
| { value: "05", label: "秋田県" }, | |||||
| { value: "06", label: "山形県" }, | |||||
| { value: "07", label: "福島県" }, | |||||
| { value: "08", label: "茨城県" }, | |||||
| { value: "09", label: "栃木県" }, | |||||
| { value: "10", label: "群馬県" }, | |||||
| { value: "11", label: "埼玉県" }, | |||||
| { value: "12", label: "千葉県" }, | |||||
| { value: "13", label: "東京都" }, | |||||
| { value: "14", label: "神奈川県" }, | |||||
| { value: "15", label: "新潟県" }, | |||||
| { value: "16", label: "富山県" }, | |||||
| { value: "17", label: "石川県" }, | |||||
| { value: "18", label: "福井県" }, | |||||
| { value: "19", label: "山梨県" }, | |||||
| { value: "20", label: "長野県" }, | |||||
| { value: "21", label: "岐阜県" }, | |||||
| { value: "22", label: "静岡県" }, | |||||
| { value: "23", label: "愛知県" }, | |||||
| { value: "24", label: "三重県" }, | |||||
| { value: "25", label: "滋賀県" }, | |||||
| { value: "26", label: "京都府" }, | |||||
| { value: "27", label: "大阪府" }, | |||||
| { value: "28", label: "兵庫県" }, | |||||
| { value: "29", label: "奈良県" }, | |||||
| { value: "30", label: "和歌山県" }, | |||||
| { value: "31", label: "鳥取県" }, | |||||
| { value: "32", label: "島根県" }, | |||||
| { value: "33", label: "岡山県" }, | |||||
| { value: "34", label: "広島県" }, | |||||
| { value: "35", label: "山口県" }, | |||||
| { value: "36", label: "徳島県" }, | |||||
| { value: "37", label: "香川県" }, | |||||
| { value: "38", label: "愛媛県" }, | |||||
| { value: "39", label: "高知県" }, | |||||
| { value: "40", label: "福岡県" }, | |||||
| { value: "41", label: "佐賀県" }, | |||||
| { value: "42", label: "長崎県" }, | |||||
| { value: "43", label: "熊本県" }, | |||||
| { value: "44", label: "大分県" }, | |||||
| { value: "45", label: "宮崎県" }, | |||||
| { value: "46", label: "鹿児島県" }, | |||||
| { value: "47", label: "沖縄県" }, | |||||
| ]; | |||||
| export function getPrefName(code: string): string { | |||||
| const pref = prefCodeOptions.find((ele) => { | |||||
| return ele.value === code; | |||||
| }); | |||||
| return pref?.label ?? "-"; | |||||
| } | |||||
| @@ -1,40 +0,0 @@ | |||||
| export const ReceiptIssuingOrderStatus = { | |||||
| NONE: 0, | |||||
| CREATED: 100, // 新規作成 | |||||
| SMS_SENDING: 200, // SMS送信中 | |||||
| SMS_RECEIVED: 300, // SMS送信完了 | |||||
| SMS_OPENED: 400, // SMS開封 | |||||
| // 郵送関連 | |||||
| MAIL_REQUEST: 500, // 郵送依頼中 | |||||
| PREPARING_FOR_MAIL: 510, // 郵送準備中 | |||||
| MAIL_DONE: 520, // 郵送完了 | |||||
| // Email送信関連 | |||||
| EMAIL_SENDING: 600, // Email送信中 | |||||
| EMAIL_DONE: 610, // Email送信完了 | |||||
| // PDFダウンロード | |||||
| DOWNLOAD_DONE: 700, // ダウンロード完了 | |||||
| } as const; | |||||
| export type ReceiptIssuingOrderStatus = | |||||
| (typeof ReceiptIssuingOrderStatus)[keyof typeof ReceiptIssuingOrderStatus]; | |||||
| const ReceiptIssuingOrderStatusName = { | |||||
| [ReceiptIssuingOrderStatus.NONE]: "", | |||||
| [ReceiptIssuingOrderStatus.CREATED]: "受付済み", | |||||
| [ReceiptIssuingOrderStatus.SMS_SENDING]: "SMS送信中", | |||||
| [ReceiptIssuingOrderStatus.SMS_RECEIVED]: "SMS送信完了", | |||||
| [ReceiptIssuingOrderStatus.SMS_OPENED]: "SMS開封済み", | |||||
| [ReceiptIssuingOrderStatus.MAIL_REQUEST]: "郵送依頼中", | |||||
| [ReceiptIssuingOrderStatus.PREPARING_FOR_MAIL]: "郵送準備中", | |||||
| [ReceiptIssuingOrderStatus.MAIL_DONE]: "郵送投函済み", | |||||
| [ReceiptIssuingOrderStatus.EMAIL_SENDING]: "メール送信中", | |||||
| [ReceiptIssuingOrderStatus.EMAIL_DONE]: "メール送信済み", | |||||
| [ReceiptIssuingOrderStatus.DOWNLOAD_DONE]: "ダウンロード済み", | |||||
| } as const; | |||||
| export function getStatusName(status: ReceiptIssuingOrderStatus): string { | |||||
| return ReceiptIssuingOrderStatusName[status]; | |||||
| } | |||||
| @@ -0,0 +1,6 @@ | |||||
| import { prefCodeOptions } from "codes/prefcode"; | |||||
| import RHFSelect, { RHFSelectProps, SelectOptionProps } from "../RHFSelect"; | |||||
| export default function RHFPrefCodeSelect({ ...other }: RHFSelectProps) { | |||||
| return <RHFSelect {...other} options={prefCodeOptions} />; | |||||
| } | |||||
| @@ -1,24 +1,45 @@ | |||||
| import { HasChildren } from "@types"; | import { HasChildren } from "@types"; | ||||
| import { checkToken } from "api/app"; | import { checkToken } from "api/app"; | ||||
| import { | |||||
| ReceiptIssuingOrder, | |||||
| getReceiptIssuingOrders, | |||||
| } from "api/receipt-issuing-order"; | |||||
| import { PageID } from "codes/page"; | |||||
| import useAPICall from "hooks/useAPICall"; | import useAPICall from "hooks/useAPICall"; | ||||
| import { createContext, useState } from "react"; | |||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||||
| import { createContext, useEffect, useState } from "react"; | |||||
| import { useParams } from "react-router-dom"; | |||||
| import { getPath } from "routes/path"; | |||||
| type App = { | type App = { | ||||
| token: string; | token: string; | ||||
| tokenResult: "cheking" | "ok" | "ng"; | tokenResult: "cheking" | "ok" | "ng"; | ||||
| receiptIssuingOrder: ReceiptIssuingOrder | null; | |||||
| setToken: (token: string) => void; | setToken: (token: string) => void; | ||||
| fetch: VoidFunction; | |||||
| navigateToHome: VoidFunction; | |||||
| }; | }; | ||||
| export const AppContext = createContext<App>({ | export const AppContext = createContext<App>({ | ||||
| token: "", | token: "", | ||||
| tokenResult: "cheking", | tokenResult: "cheking", | ||||
| receiptIssuingOrder: null, | |||||
| setToken: (token: string) => {}, | setToken: (token: string) => {}, | ||||
| fetch: () => {}, | |||||
| navigateToHome: () => {}, | |||||
| }); | }); | ||||
| type Props = HasChildren; | type Props = HasChildren; | ||||
| export function AppContextProvider({ children }: Props) { | export function AppContextProvider({ children }: Props) { | ||||
| const { navigateWhenChanged } = useNavigateCustom(); | |||||
| const { token: paramToken } = useParams(); | |||||
| const [token, _setToken] = useState(""); | const [token, _setToken] = useState(""); | ||||
| const [receiptIssuingOrder, setReceiptIssuingOrder] = | |||||
| useState<ReceiptIssuingOrder | null>(null); | |||||
| const [tokenResult, setTokenResult] = useState<"cheking" | "ok" | "ng">( | const [tokenResult, setTokenResult] = useState<"cheking" | "ok" | "ng">( | ||||
| "cheking" | "cheking" | ||||
| ); | ); | ||||
| @@ -28,9 +49,11 @@ export function AppContextProvider({ children }: Props) { | |||||
| onSuccess: (res, sendData) => { | onSuccess: (res, sendData) => { | ||||
| setTokenResult("ok"); | setTokenResult("ok"); | ||||
| _setToken(sendData.access_token); | _setToken(sendData.access_token); | ||||
| setReceiptIssuingOrder(res.data.receipt_issuing_order); | |||||
| }, | }, | ||||
| onFailed: () => { | onFailed: () => { | ||||
| setTokenResult("ng"); | setTokenResult("ng"); | ||||
| navigateWhenChanged(getPath(PageID.PAGE_404)); | |||||
| }, | }, | ||||
| }); | }); | ||||
| @@ -40,8 +63,39 @@ export function AppContextProvider({ children }: Props) { | |||||
| }); | }); | ||||
| }; | }; | ||||
| const fetch = () => { | |||||
| if (token) { | |||||
| setToken(token); | |||||
| } | |||||
| }; | |||||
| const navigateToHome = () => { | |||||
| const path = getPath(PageID.APP_RECEIPT_ISSUING_ORDER_INDEX, { | |||||
| query: { token: token }, | |||||
| }); | |||||
| navigateWhenChanged(path); | |||||
| }; | |||||
| useEffect(() => { | |||||
| if (paramToken && !token) { | |||||
| setToken(paramToken); | |||||
| } else if (!token && !paramToken) { | |||||
| setTokenResult("ng"); | |||||
| navigateWhenChanged(getPath(PageID.PAGE_404)); | |||||
| } | |||||
| }, [paramToken]); | |||||
| return ( | return ( | ||||
| <AppContext.Provider value={{ token, tokenResult, setToken }}> | |||||
| <AppContext.Provider | |||||
| value={{ | |||||
| token, | |||||
| tokenResult, | |||||
| receiptIssuingOrder, | |||||
| setToken, | |||||
| fetch, | |||||
| navigateToHome, | |||||
| }} | |||||
| > | |||||
| {children} | {children} | ||||
| </AppContext.Provider> | </AppContext.Provider> | ||||
| ); | ); | ||||
| @@ -0,0 +1,86 @@ | |||||
| import { | |||||
| Button, | |||||
| Dialog, | |||||
| DialogActions, | |||||
| DialogContent, | |||||
| DialogContentText, | |||||
| DialogTitle, | |||||
| } from "@mui/material"; | |||||
| import { ReactNode, useState } from "react"; | |||||
| type Props = { | |||||
| message?: string; | |||||
| onClose?: VoidFunction; | |||||
| onAgree?: VoidFunction; | |||||
| onDisagree?: VoidFunction; | |||||
| }; | |||||
| export type UseDialogReturn = { | |||||
| show: boolean; | |||||
| open: VoidFunction; | |||||
| close: VoidFunction; | |||||
| setShow: (show: boolean) => void; | |||||
| element: ReactNode; | |||||
| }; | |||||
| export function useDialog({ | |||||
| message, | |||||
| onClose, | |||||
| onAgree, | |||||
| onDisagree, | |||||
| }: Props = {}): UseDialogReturn { | |||||
| const [show, setShow] = useState(false); | |||||
| const open = () => { | |||||
| setShow(true); | |||||
| }; | |||||
| const close = () => { | |||||
| if (onClose) { | |||||
| onClose(); | |||||
| } | |||||
| setShow(false); | |||||
| }; | |||||
| const agree = () => { | |||||
| if (onAgree) { | |||||
| onAgree(); | |||||
| } | |||||
| setShow(false); | |||||
| }; | |||||
| const disagree = () => { | |||||
| if (onDisagree) { | |||||
| onDisagree(); | |||||
| } | |||||
| setShow(false); | |||||
| }; | |||||
| const element = ( | |||||
| <Dialog open={show} onClose={close}> | |||||
| <DialogTitle>確認</DialogTitle> | |||||
| {message && ( | |||||
| <DialogContent> | |||||
| <DialogContentText>{message}</DialogContentText> | |||||
| </DialogContent> | |||||
| )} | |||||
| <DialogActions> | |||||
| <Button onClick={disagree}>CANCEL</Button> | |||||
| <Button onClick={agree}>OK</Button> | |||||
| </DialogActions> | |||||
| </Dialog> | |||||
| ); | |||||
| return { | |||||
| // param | |||||
| show, | |||||
| // Element | |||||
| element, | |||||
| // function | |||||
| open, | |||||
| close, | |||||
| setShow, | |||||
| }; | |||||
| } | |||||
| @@ -0,0 +1,198 @@ | |||||
| import { | |||||
| Box, | |||||
| Button, | |||||
| Divider, | |||||
| Paper, | |||||
| Stack, | |||||
| Step, | |||||
| StepLabel, | |||||
| Stepper, | |||||
| Table, | |||||
| TableBody, | |||||
| TableCell, | |||||
| TableRow, | |||||
| Typography, | |||||
| } from "@mui/material"; | |||||
| import { HasChildren } from "@types"; | |||||
| import { getPrefName } from "codes/prefcode"; | |||||
| import useApp from "hooks/useApp"; | |||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||||
| import { useMemo, useState } from "react"; | |||||
| import useInputMailStep from "./hooks/useInputMailStep"; | |||||
| import useAPICall from "hooks/useAPICall"; | |||||
| import { mailRequest } from "api/app/receipt-issuing-order"; | |||||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||||
| type TableRowCustomProps = { | |||||
| title: string; | |||||
| value: string; | |||||
| }; | |||||
| const TableRowCustom = ({ title, value }: TableRowCustomProps) => { | |||||
| return ( | |||||
| <TableRow> | |||||
| <TableCell sx={{ borderRight: "1px solid rgba(224, 224, 224, 1)" }}> | |||||
| {title} | |||||
| </TableCell> | |||||
| <TableCell>{value}</TableCell> | |||||
| </TableRow> | |||||
| ); | |||||
| }; | |||||
| type SectionProps = { | |||||
| title: string; | |||||
| subtitle?: string; | |||||
| } & HasChildren; | |||||
| const Section = ({ title, subtitle, children }: SectionProps) => { | |||||
| return ( | |||||
| <Paper | |||||
| sx={{ py: 2, border: "1px solid rgba(224, 224, 224, 1)" }} | |||||
| elevation={0} | |||||
| > | |||||
| <Box> | |||||
| <Typography variant="subtitle1">{title}</Typography> | |||||
| {subtitle && <Typography variant="body2">{subtitle}</Typography>} | |||||
| </Box> | |||||
| <Box sx={{ mt: 2 }}>{children}</Box> | |||||
| </Paper> | |||||
| ); | |||||
| }; | |||||
| export default function MailOrder() { | |||||
| const { | |||||
| tokenResult, | |||||
| navigateToHome, | |||||
| fetch, | |||||
| receiptIssuingOrder: order, | |||||
| } = useApp(); | |||||
| const { success, error } = useSnackbarCustom(); | |||||
| const { navigate } = useNavigateCustom(); | |||||
| const [mode, setMode] = useState<"input" | "confirm" | "done">("input"); | |||||
| const input = useInputMailStep({ | |||||
| onNext: () => { | |||||
| setMode("confirm"); | |||||
| }, | |||||
| onPrev: () => { | |||||
| navigate(-1); | |||||
| }, | |||||
| }); | |||||
| const requestMailAPI = useAPICall({ | |||||
| apiMethod: mailRequest, | |||||
| onSuccess: () => { | |||||
| fetch(); | |||||
| setMode("done"); | |||||
| }, | |||||
| onFailed: () => { | |||||
| error("登録失敗"); | |||||
| }, | |||||
| }); | |||||
| const currentStep = useMemo(() => { | |||||
| if (mode === "input") return 0; | |||||
| if (mode === "confirm") return 1; | |||||
| if (mode === "done") return 2; | |||||
| }, [mode]); | |||||
| const handleConfirm = () => { | |||||
| if (!order) return; | |||||
| requestMailAPI.callAPI({ | |||||
| id: order.id, | |||||
| timestamp: order.updated_at, | |||||
| ...input.values(), | |||||
| }); | |||||
| }; | |||||
| if (tokenResult !== "ok") { | |||||
| return null; | |||||
| } | |||||
| return ( | |||||
| <> | |||||
| <Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center"> | |||||
| <Stack spacing={3}> | |||||
| <Box> | |||||
| <Typography variant="h5">領収証郵送依頼</Typography> | |||||
| </Box> | |||||
| <Section title=""> | |||||
| <Stepper activeStep={currentStep}> | |||||
| <Step> | |||||
| <StepLabel>郵送先情報入力</StepLabel> | |||||
| </Step> | |||||
| <Step> | |||||
| <StepLabel>確認</StepLabel> | |||||
| </Step> | |||||
| <Step> | |||||
| <StepLabel>完了</StepLabel> | |||||
| </Step> | |||||
| </Stepper> | |||||
| {mode === "input" && input.element} | |||||
| {mode === "confirm" && ( | |||||
| <Stack spacing={2} sx={{ p: 1, py: 3, m: 1 }}> | |||||
| <Typography variant="h5">郵送先情報確認</Typography> | |||||
| <Table> | |||||
| <TableBody> | |||||
| <TableRowCustom | |||||
| title="都道府県" | |||||
| value={getPrefName(input.values("mail_pref_code"))} | |||||
| /> | |||||
| <TableRowCustom | |||||
| title="郵便番号" | |||||
| value={"〒" + input.values("mail_zip_code")} | |||||
| /> | |||||
| <TableRowCustom | |||||
| title="市区町村" | |||||
| value={input.values("mail_address1")} | |||||
| /> | |||||
| <TableRowCustom | |||||
| title="番地等" | |||||
| value={input.values("mail_address2")} | |||||
| /> | |||||
| <TableRowCustom | |||||
| title="建物名・部屋番号等" | |||||
| value={input.values("mail_address3")} | |||||
| /> | |||||
| <TableRowCustom | |||||
| title="宛名" | |||||
| value={input.values("mail_name")} | |||||
| /> | |||||
| </TableBody> | |||||
| </Table> | |||||
| <Box> | |||||
| <Stack> | |||||
| <Box>郵送先に間違いがないか確認してください。</Box> | |||||
| <Box>投函まで数営業日を要しますのでご了承ください。</Box> | |||||
| </Stack> | |||||
| </Box> | |||||
| <Stack direction="row" spacing={2}> | |||||
| <Button | |||||
| variant="text" | |||||
| onClick={() => { | |||||
| setMode("input"); | |||||
| }} | |||||
| > | |||||
| 戻る | |||||
| </Button> | |||||
| <Button variant="contained" onClick={handleConfirm}> | |||||
| 確定 | |||||
| </Button> | |||||
| </Stack> | |||||
| </Stack> | |||||
| )} | |||||
| {mode === "done" && ( | |||||
| <Stack spacing={2} sx={{ p: 1, py: 3, m: 1 }}> | |||||
| <Box>郵送依頼を受付いたしました。</Box> | |||||
| <Box>到着までしばらくお待ちください。</Box> | |||||
| <Button onClick={navigateToHome}>戻る</Button> | |||||
| </Stack> | |||||
| )} | |||||
| </Section> | |||||
| </Stack> | |||||
| </Box> | |||||
| </> | |||||
| ); | |||||
| } | |||||
| @@ -1,25 +1,85 @@ | |||||
| import { Box, Button, Paper, Stack, Typography } from "@mui/material"; | |||||
| import { | |||||
| Box, | |||||
| Button, | |||||
| Paper, | |||||
| Stack, | |||||
| Table, | |||||
| TableBody, | |||||
| TableCell, | |||||
| TableHead, | |||||
| TableRow, | |||||
| TextField, | |||||
| Typography, | |||||
| styled, | |||||
| } from "@mui/material"; | |||||
| import { HasChildren } from "@types"; | |||||
| import { ApiId } from "api"; | import { ApiId } from "api"; | ||||
| import { confirm } from "api/app/receipt-issuing-order"; | |||||
| import { getFullUrl } from "api/url"; | import { getFullUrl } from "api/url"; | ||||
| import useAPICall from "hooks/useAPICall"; | |||||
| import useApp from "hooks/useApp"; | import useApp from "hooks/useApp"; | ||||
| import { useEffect, useMemo } from "react"; | |||||
| import { useDialog } from "hooks/useDialog"; | |||||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||||
| import React, { ReactNode, useEffect, useMemo, useRef, useState } from "react"; | |||||
| import { useParams } from "react-router-dom"; | import { useParams } from "react-router-dom"; | ||||
| import { sprintf } from "sprintf-js"; | |||||
| import { formatDateStr, formatDateTimeStr } from "utils/datetime"; | |||||
| import useReceiptIssuingOrderConfirm from "./hooks/useReceiptIssuingOrderConfirm"; | |||||
| import useReceiptIssuingOrderSelectHowToGet from "./hooks/useReceiptIssuingOrderSelectHowToGet"; | |||||
| export default function ReceiptIssuingOrder() { | |||||
| const { token: paramToken } = useParams(); | |||||
| type TableRowCustomProps = { | |||||
| title: string; | |||||
| value: string; | |||||
| }; | |||||
| const TableRowCustom = ({ title, value }: TableRowCustomProps) => { | |||||
| return ( | |||||
| <TableRow> | |||||
| <TableCell sx={{ borderRight: "1px solid rgba(224, 224, 224, 1)" }}> | |||||
| {title} | |||||
| </TableCell> | |||||
| <TableCell>{value}</TableCell> | |||||
| </TableRow> | |||||
| ); | |||||
| }; | |||||
| type SectionProps = { | |||||
| title: string; | |||||
| subtitle?: string; | |||||
| } & HasChildren; | |||||
| const Section = ({ title, subtitle, children }: SectionProps) => { | |||||
| return ( | |||||
| <Paper | |||||
| sx={{ py: 2, border: "1px solid rgba(224, 224, 224, 1)" }} | |||||
| elevation={0} | |||||
| > | |||||
| <Box> | |||||
| <Typography variant="subtitle1">{title}</Typography> | |||||
| {subtitle && <Typography variant="body2">{subtitle}</Typography>} | |||||
| </Box> | |||||
| <Box sx={{ mt: 2 }}>{children}</Box> | |||||
| </Paper> | |||||
| ); | |||||
| }; | |||||
| const { token, setToken, tokenResult } = useApp(); | |||||
| export default function ReceiptIssuingOrder() { | |||||
| const { tokenResult, receiptIssuingOrder: order } = useApp(); | |||||
| const downloadUrl = useMemo(() => { | |||||
| return getFullUrl(ApiId.DOWNLOAD_RECEIPT) + "?access_token=" + token; | |||||
| }, [token]); | |||||
| const confirm = useReceiptIssuingOrderConfirm(); | |||||
| const selectHowToGet = useReceiptIssuingOrderSelectHowToGet(); | |||||
| useEffect(() => { | |||||
| if (paramToken) { | |||||
| setToken(paramToken); | |||||
| console.log("render"); | |||||
| const tokenExpiresAtStr = useMemo(() => { | |||||
| if (order) { | |||||
| return formatDateTimeStr(order.access_token_expires_at); | |||||
| } | } | ||||
| }, []); | |||||
| return "-"; | |||||
| }, [order]); | |||||
| const mailOrderStatusStr = useMemo(() => { | |||||
| if (!order) return "-"; | |||||
| if (order.status_mail_post_date) return "投函済み"; | |||||
| if (order.status_order_mail_datetime) return "郵送依頼確認中"; | |||||
| return "-"; | |||||
| }, [order]); | |||||
| if (tokenResult === "cheking") { | if (tokenResult === "cheking") { | ||||
| return null; | return null; | ||||
| @@ -30,19 +90,69 @@ export default function ReceiptIssuingOrder() { | |||||
| } | } | ||||
| return ( | return ( | ||||
| <Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center"> | |||||
| <Stack spacing={3}> | |||||
| <Typography variant="h5">領収証発行依頼</Typography> | |||||
| <Paper> | |||||
| <Typography variant="h5">状況</Typography> | |||||
| </Paper> | |||||
| <Paper> | |||||
| <Button href={downloadUrl} variant="contained"> | |||||
| PDFダウンロード | |||||
| </Button> | |||||
| </Paper> | |||||
| </Stack> | |||||
| </Box> | |||||
| <> | |||||
| <Box sx={{ p: 3, pt: 5, mx: "auto", maxWidth: 500 }} textAlign="center"> | |||||
| <Stack spacing={3}> | |||||
| <Box> | |||||
| <Typography variant="h5">領収証発行依頼</Typography> | |||||
| <Typography variant="body2"> | |||||
| ページ有効期限:{tokenExpiresAtStr}まで | |||||
| </Typography> | |||||
| </Box> | |||||
| <Section title="申込内容"> | |||||
| <Table> | |||||
| <TableBody> | |||||
| <TableRowCustom | |||||
| title="店舗名" | |||||
| value={order?.receipt_shop_name ?? "-"} | |||||
| /> | |||||
| <TableRowCustom | |||||
| title="金額" | |||||
| value={ | |||||
| order?.receipt_amount | |||||
| ? sprintf("%d円", order.receipt_amount) | |||||
| : "-" | |||||
| } | |||||
| /> | |||||
| <TableRowCustom | |||||
| title="利用日" | |||||
| value={ | |||||
| order?.receipt_use_date | |||||
| ? formatDateStr(order.receipt_use_date) | |||||
| : "-" | |||||
| } | |||||
| /> | |||||
| <TableRowCustom | |||||
| title="宛名" | |||||
| value={order?.receipt_name ?? "-"} | |||||
| /> | |||||
| </TableBody> | |||||
| </Table> | |||||
| </Section> | |||||
| {order?.status_order_mail_datetime && ( | |||||
| <Section title="郵送依頼状況"> | |||||
| <Table sx={{ borderBottom: "none" }}> | |||||
| <TableBody> | |||||
| <TableRow /> | |||||
| <TableRowCustom title="状況" value={mailOrderStatusStr} /> | |||||
| </TableBody> | |||||
| </Table> | |||||
| </Section> | |||||
| )} | |||||
| {confirm.shouldShow && ( | |||||
| <Section title="宛名入力">{confirm.element}</Section> | |||||
| )} | |||||
| {selectHowToGet.shouldShow && ( | |||||
| <Section title="領収証取得方法選択"> | |||||
| {selectHowToGet.element} | |||||
| </Section> | |||||
| )} | |||||
| </Stack> | |||||
| </Box> | |||||
| </> | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -0,0 +1,119 @@ | |||||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||||
| import { Box, Button, Divider, Stack, Typography } from "@mui/material"; | |||||
| import { HasChildren } from "@types"; | |||||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||||
| import RHFDatePicker from "components/hook-form/RHFDatePicker"; | |||||
| import RHFPrefCodeSelect from "components/hook-form/ex/RHFPrefCodeSelect"; | |||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||||
| 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 = { | |||||
| mail_pref_code: string; | |||||
| mail_zip_code: string; | |||||
| mail_address1: string; | |||||
| mail_address2: string; | |||||
| mail_address3: string; | |||||
| mail_name: string; | |||||
| }; | |||||
| type Props = { | |||||
| onNext?: VoidFunction; | |||||
| onPrev?: VoidFunction; | |||||
| }; | |||||
| export default function useInputMailStep({ onNext, onPrev }: Props) { | |||||
| const form = useForm<FormProps>({ | |||||
| defaultValues: { | |||||
| mail_pref_code: "", | |||||
| mail_zip_code: "", | |||||
| mail_address1: "", | |||||
| mail_address2: "", | |||||
| mail_address3: "", | |||||
| mail_name: "", | |||||
| }, | |||||
| resolver: yupResolver( | |||||
| Yup.object().shape({ | |||||
| mail_pref_code: Yup.string() | |||||
| .required("必須項目です") | |||||
| .typeError("必須項目です"), | |||||
| mail_zip_code: Yup.string() | |||||
| .required("必須項目です") | |||||
| .matches(/^[0-9]{7}$/, "半角数値7桁を入力してください"), | |||||
| mail_address1: Yup.string() | |||||
| .required("必須項目です") | |||||
| .max(100, "100文字以内で入力してください"), | |||||
| mail_address2: Yup.string() | |||||
| .required("必須項目です") | |||||
| .max(100, "100文字以内で入力してください"), | |||||
| mail_address3: Yup.string().max(100, "100文字以内で入力してください"), | |||||
| mail_name: Yup.string() | |||||
| .required("必須項目です") | |||||
| .max(100, "100文字以内で入力してください"), | |||||
| }) | |||||
| ), | |||||
| }); | |||||
| const handleSubmit = () => { | |||||
| if (onNext) { | |||||
| onNext(); | |||||
| } | |||||
| }; | |||||
| const handlePrev = () => { | |||||
| if (onPrev) { | |||||
| onPrev(); | |||||
| } | |||||
| }; | |||||
| const element = ( | |||||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||||
| <Stack spacing={2} sx={{ p: 1, m: 1 }} textAlign="left"> | |||||
| <AreaBox title="都道府県"> | |||||
| <RHFPrefCodeSelect name="mail_pref_code" size="small" /> | |||||
| </AreaBox> | |||||
| <AreaBox title="郵便番号"> | |||||
| <Typography variant="body2">ハイフン無の7桁</Typography> | |||||
| <RHFTextField | |||||
| name="mail_zip_code" | |||||
| InputProps={{ startAdornment: <div>〒</div> }} | |||||
| /> | |||||
| </AreaBox> | |||||
| <AreaBox title="市区町村"> | |||||
| <RHFTextField name="mail_address1" /> | |||||
| </AreaBox> | |||||
| <AreaBox title="番地等"> | |||||
| <RHFTextField name="mail_address2" /> | |||||
| </AreaBox> | |||||
| <AreaBox title="建物名・部屋番号等"> | |||||
| <RHFTextField name="mail_address3" /> | |||||
| </AreaBox> | |||||
| <Divider /> | |||||
| <AreaBox title="宛先名"> | |||||
| <RHFTextField name="mail_name" /> | |||||
| </AreaBox> | |||||
| <Stack direction="row" spacing={2}> | |||||
| <Button variant="text" onClick={handlePrev}> | |||||
| 戻る | |||||
| </Button> | |||||
| <Button variant="contained" type="submit"> | |||||
| 次へ | |||||
| </Button> | |||||
| </Stack> | |||||
| </Stack> | |||||
| </FormProvider> | |||||
| ); | |||||
| return { element, values: form.getValues, setValue: form.setValue }; | |||||
| } | |||||
| @@ -0,0 +1,103 @@ | |||||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||||
| import { Box, Button, Stack, Typography } from "@mui/material"; | |||||
| import { confirm } from "api/app/receipt-issuing-order"; | |||||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||||
| import useAPICall from "hooks/useAPICall"; | |||||
| import useApp from "hooks/useApp"; | |||||
| import useBackDrop from "hooks/useBackDrop"; | |||||
| import { useDialog } from "hooks/useDialog"; | |||||
| import useSnackbarCustom from "hooks/useSnackbarCustom"; | |||||
| import { useEffect, useMemo } from "react"; | |||||
| import { useForm } from "react-hook-form"; | |||||
| import { sprintf } from "sprintf-js"; | |||||
| import * as Yup from "yup"; | |||||
| type FormProps = { | |||||
| receipt_name: string; | |||||
| }; | |||||
| const schema = Yup.object().shape({ | |||||
| receipt_name: Yup.string().required("必須項目です"), | |||||
| }); | |||||
| export default function useReceiptIssuingOrderConfirm() { | |||||
| const { receiptIssuingOrder: order, fetch } = useApp(); | |||||
| const { success, error } = useSnackbarCustom(); | |||||
| const { setShowBackDrop } = useBackDrop(); | |||||
| const shouldShow = useMemo(() => { | |||||
| return !order?.confirmed; | |||||
| }, [order]); | |||||
| const form = useForm<FormProps>({ | |||||
| defaultValues: { | |||||
| receipt_name: "", | |||||
| }, | |||||
| resolver: yupResolver(schema), | |||||
| }); | |||||
| const receiptName = form.watch("receipt_name"); | |||||
| const confirmMessage = useMemo(() => { | |||||
| return sprintf("宛名[%s]でよろしいでしょうか。", receiptName); | |||||
| }, [receiptName]); | |||||
| const confirmDialog = useDialog({ | |||||
| message: confirmMessage, | |||||
| onAgree: () => { | |||||
| if (!order) return; | |||||
| api.callAPI({ | |||||
| id: order.id, | |||||
| receipt_name: receiptName, | |||||
| timestamp: order.updated_at, | |||||
| }); | |||||
| }, | |||||
| }); | |||||
| const api = useAPICall({ | |||||
| apiMethod: confirm, | |||||
| onSuccess: () => { | |||||
| success("登録しました"); | |||||
| fetch(); | |||||
| }, | |||||
| onFailed: () => { | |||||
| error("失敗しました"); | |||||
| }, | |||||
| }); | |||||
| const handleSubmit = () => { | |||||
| confirmDialog.setShow(true); | |||||
| }; | |||||
| useEffect(() => { | |||||
| setShowBackDrop(api.sending); | |||||
| }, [api.sending]); | |||||
| const element = ( | |||||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||||
| <Stack spacing={1}> | |||||
| <Box> | |||||
| <Typography variant="body2"> | |||||
| 領収証の宛名を入力してください | |||||
| </Typography> | |||||
| <Typography variant="body2"> | |||||
| 一度登録すると変更できないので、ご注意ください | |||||
| </Typography> | |||||
| </Box> | |||||
| <Box sx={{ px: 2 }}> | |||||
| <RHFTextField label="宛名" name="receipt_name" /> | |||||
| </Box> | |||||
| <Box> | |||||
| <Button type="submit" variant="contained"> | |||||
| 登録 | |||||
| </Button> | |||||
| </Box> | |||||
| </Stack> | |||||
| {confirmDialog.element} | |||||
| </FormProvider> | |||||
| ); | |||||
| return { | |||||
| element, | |||||
| shouldShow, | |||||
| }; | |||||
| } | |||||
| @@ -0,0 +1,47 @@ | |||||
| import { Box, Button, Stack } from "@mui/material"; | |||||
| import { ApiId } from "api"; | |||||
| import { getFullUrl } from "api/url"; | |||||
| import { PageID } from "codes/page"; | |||||
| import useApp from "hooks/useApp"; | |||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||||
| import { useMemo } from "react"; | |||||
| import { getPath } from "routes/path"; | |||||
| export default function useReceiptIssuingOrderSelectHowToGet() { | |||||
| const { token, receiptIssuingOrder: order } = useApp(); | |||||
| const { navigateWhenChanged } = useNavigateCustom(); | |||||
| const shouldShow = useMemo(() => { | |||||
| return !!order?.confirmed; | |||||
| }, [order]); | |||||
| const downloadUrl = useMemo(() => { | |||||
| return getFullUrl(ApiId.DOWNLOAD_RECEIPT) + "?access_token=" + token; | |||||
| }, [token]); | |||||
| const handleClickMail = () => { | |||||
| navigateWhenChanged(getPath(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER)); | |||||
| }; | |||||
| const element = ( | |||||
| <Stack spacing={1}> | |||||
| <Box> | |||||
| <Button href={downloadUrl} variant="contained"> | |||||
| PDFダウンロード | |||||
| </Button> | |||||
| </Box> | |||||
| {!order?.status_order_mail_datetime && ( | |||||
| <Box> | |||||
| <Button variant="text" onClick={handleClickMail}> | |||||
| 郵送希望 | |||||
| </Button> | |||||
| </Box> | |||||
| )} | |||||
| </Stack> | |||||
| ); | |||||
| return { | |||||
| element, | |||||
| shouldShow, | |||||
| }; | |||||
| } | |||||
| @@ -47,7 +47,6 @@ export default function ReceiptIssuingOrderCreate() { | |||||
| adjustSeqNo: selectParkingStep.values("adjustSeqNo"), | adjustSeqNo: selectParkingStep.values("adjustSeqNo"), | ||||
| amount: inputReceiptStep.values("amount"), | amount: inputReceiptStep.values("amount"), | ||||
| date: inputReceiptStep.values("date"), | date: inputReceiptStep.values("date"), | ||||
| name: inputReceiptStep.values("name"), | |||||
| address: inputSMSSendAddress.values("address"), | address: inputSMSSendAddress.values("address"), | ||||
| memo: inputReceiptStep.values("memo"), | memo: inputReceiptStep.values("memo"), | ||||
| }; | }; | ||||
| @@ -112,8 +111,7 @@ export default function ReceiptIssuingOrderCreate() { | |||||
| customer_code: getValue(formData.customerCode), | customer_code: getValue(formData.customerCode), | ||||
| parking_management_code: getValue(formData.parkingManagementCode), | parking_management_code: getValue(formData.parkingManagementCode), | ||||
| adjust_seq_no: formData.adjustSeqNo, | adjust_seq_no: formData.adjustSeqNo, | ||||
| receipt_name: formData.name, | |||||
| receipt_use_datetime: formData.date, | |||||
| receipt_use_date: formData.date, | |||||
| receipt_amount: formData.amount, | receipt_amount: formData.amount, | ||||
| sms_phone_number: formData.address, | sms_phone_number: formData.address, | ||||
| }); | }); | ||||
| @@ -1,4 +1,3 @@ | |||||
| import { yupResolver } from "@hookform/resolvers/yup"; | |||||
| import { | import { | ||||
| Box, | Box, | ||||
| Button, | Button, | ||||
| @@ -7,26 +6,12 @@ import { | |||||
| TableBody, | TableBody, | ||||
| TableCell, | TableCell, | ||||
| TableRow, | TableRow, | ||||
| TextField, | |||||
| Typography, | Typography, | ||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import { HasChildren } from "@types"; | import { HasChildren } from "@types"; | ||||
| import TextFieldEx from "components/form/TextFieldEx"; | import TextFieldEx from "components/form/TextFieldEx"; | ||||
| import { | |||||
| FormProvider, | |||||
| RHFAutoComplete, | |||||
| RHFTextField, | |||||
| } from "components/hook-form"; | |||||
| import { | |||||
| AutoCompleteOption, | |||||
| AutoCompleteOptionType, | |||||
| getValue, | |||||
| } from "components/hook-form/RHFAutoComplete"; | |||||
| import RHFDatePicker from "components/hook-form/RHFDatePicker"; | |||||
| import { useState } from "react"; | import { useState } from "react"; | ||||
| import { useForm } from "react-hook-form"; | |||||
| import { formatDateStr, formatDateTimeStr } from "utils/datetime"; | |||||
| import * as Yup from "yup"; | |||||
| import { formatDateStr } from "utils/datetime"; | |||||
| type AreaBoxProps = { | type AreaBoxProps = { | ||||
| title: string; | title: string; | ||||
| @@ -51,7 +36,6 @@ export type ConfirmDataProps = { | |||||
| adjustSeqNo: string; | adjustSeqNo: string; | ||||
| amount: string; | amount: string; | ||||
| date: Date | null; | date: Date | null; | ||||
| name: string; | |||||
| address: string; | address: string; | ||||
| memo: string; | memo: string; | ||||
| }; | }; | ||||
| @@ -64,7 +48,6 @@ export default function useConfirm({ onNext, onPrev }: Props) { | |||||
| adjustSeqNo: "", | adjustSeqNo: "", | ||||
| amount: "", | amount: "", | ||||
| date: null, | date: null, | ||||
| name: "", | |||||
| address: "", | address: "", | ||||
| memo: "", | memo: "", | ||||
| }); | }); | ||||
| @@ -102,10 +85,6 @@ export default function useConfirm({ onNext, onPrev }: Props) { | |||||
| <TableCell>利用日</TableCell> | <TableCell>利用日</TableCell> | ||||
| <TableCell>{formatDateStr(data.date)}</TableCell> | <TableCell>{formatDateStr(data.date)}</TableCell> | ||||
| </TableRow> | </TableRow> | ||||
| <TableRow> | |||||
| <TableCell>宛名</TableCell> | |||||
| <TableCell>{data.name}</TableCell> | |||||
| </TableRow> | |||||
| <TableRow> | <TableRow> | ||||
| <TableCell>金額</TableCell> | <TableCell>金額</TableCell> | ||||
| <TableCell>{data.amount}円</TableCell> | <TableCell>{data.amount}円</TableCell> | ||||
| @@ -31,7 +31,6 @@ function AreaBox({ title, children }: AreaBoxProps) { | |||||
| type FormProps = { | type FormProps = { | ||||
| amount: string; | amount: string; | ||||
| date: Date | null; | date: Date | null; | ||||
| name: string; | |||||
| memo: string; | memo: string; | ||||
| }; | }; | ||||
| @@ -46,7 +45,6 @@ export default function useInputReceiptStep({ onNext, onPrev }: Props) { | |||||
| defaultValues: { | defaultValues: { | ||||
| amount: "", | amount: "", | ||||
| date: null, | date: null, | ||||
| name: "", | |||||
| memo: "", | memo: "", | ||||
| }, | }, | ||||
| resolver: yupResolver( | resolver: yupResolver( | ||||
| @@ -55,7 +53,6 @@ export default function useInputReceiptStep({ onNext, onPrev }: Props) { | |||||
| .required("必須項目です") | .required("必須項目です") | ||||
| .typeError("正しく入力してください"), | .typeError("正しく入力してください"), | ||||
| amount: Yup.number().required("必須項目です"), | amount: Yup.number().required("必須項目です"), | ||||
| name: Yup.string().required("必須項目です"), | |||||
| memo: Yup.string().nullable(), | memo: Yup.string().nullable(), | ||||
| }) | }) | ||||
| ), | ), | ||||
| @@ -89,9 +86,6 @@ export default function useInputReceiptStep({ onNext, onPrev }: Props) { | |||||
| sx={{ maxWidth: 150 }} | sx={{ maxWidth: 150 }} | ||||
| /> | /> | ||||
| </AreaBox> | </AreaBox> | ||||
| <AreaBox title="宛名"> | |||||
| <RHFTextField name="name" size="small" /> | |||||
| </AreaBox> | |||||
| <AreaBox title="メモ"> | <AreaBox title="メモ"> | ||||
| <RHFTextField name="memo" size="small" multiline rows={3} /> | <RHFTextField name="memo" size="small" multiline rows={3} /> | ||||
| </AreaBox> | </AreaBox> | ||||
| @@ -14,7 +14,6 @@ import { | |||||
| getReceiptIssuingOrders, | getReceiptIssuingOrders, | ||||
| } from "api/custom/hello-techno/receipt-issuing-order"; | } from "api/custom/hello-techno/receipt-issuing-order"; | ||||
| import { PageID, TabID } from "codes/page"; | import { PageID, TabID } from "codes/page"; | ||||
| import { getStatusName } from "codes/receipt-issuing-order"; | |||||
| import { FormProvider, RHFTextField } from "components/hook-form"; | import { FormProvider, RHFTextField } from "components/hook-form"; | ||||
| import { TableHeadCustom } from "components/table"; | import { TableHeadCustom } from "components/table"; | ||||
| import { SearchConditionContextProvider } from "contexts/SearchConditionContext"; | import { SearchConditionContextProvider } from "contexts/SearchConditionContext"; | ||||
| @@ -235,7 +234,7 @@ function Row({ data }: RowProps) { | |||||
| <TableCell>{data.order_datetime}</TableCell> | <TableCell>{data.order_datetime}</TableCell> | ||||
| <TableCell>{data.customer_name}</TableCell> | <TableCell>{data.customer_name}</TableCell> | ||||
| <TableCell>{data.parking_name}</TableCell> | <TableCell>{data.parking_name}</TableCell> | ||||
| <TableCell>{getStatusName(data.status)}</TableCell> | |||||
| <TableCell>{data.status_name}</TableCell> | |||||
| <TableCell>{data.handler_name}</TableCell> | <TableCell>{data.handler_name}</TableCell> | ||||
| </TableRow> | </TableRow> | ||||
| ); | ); | ||||
| @@ -42,6 +42,10 @@ const AppRoutes = (): RouteObject => ({ | |||||
| path: getRoute(PageID.APP_RECEIPT_ISSUING_ORDER_INDEX), | path: getRoute(PageID.APP_RECEIPT_ISSUING_ORDER_INDEX), | ||||
| element: <ReceiptIssuingOrder />, | element: <ReceiptIssuingOrder />, | ||||
| }, | }, | ||||
| { | |||||
| path: getRoute(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER), | |||||
| element: <MailOrder />, | |||||
| }, | |||||
| ], | ], | ||||
| }); | }); | ||||
| @@ -116,6 +120,7 @@ const Logout = Loadable(lazy(() => import("pages/auth/logout"))); | |||||
| const ReceiptIssuingOrder = Loadable( | const ReceiptIssuingOrder = Loadable( | ||||
| lazy(() => import("pages/app/ReceiptIssuingOrder")) | lazy(() => import("pages/app/ReceiptIssuingOrder")) | ||||
| ); | ); | ||||
| const MailOrder = Loadable(lazy(() => import("pages/app/MailOrder"))); | |||||
| //ダッシュボード ---------------------------- | //ダッシュボード ---------------------------- | ||||
| const Dashboard = Loadable(lazy(() => import("pages/dashboard"))); | const Dashboard = Loadable(lazy(() => import("pages/dashboard"))); | ||||
| @@ -40,6 +40,8 @@ const PATHS = { | |||||
| // APP | // APP | ||||
| [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_INDEX)]: | [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_INDEX)]: | ||||
| "/app/receipt-issuing-oreder/:token", | "/app/receipt-issuing-oreder/:token", | ||||
| [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_MAIL_ORDER)]: | |||||
| "/app/receipt-issuing-oreder/mail", | |||||
| // 契約関連 | // 契約関連 | ||||
| [makePathKey(PageID.DASHBOARD_CONTRACT_LIST)]: | [makePathKey(PageID.DASHBOARD_CONTRACT_LIST)]: | ||||
| @@ -158,6 +158,15 @@ theme = { | |||||
| }, | }, | ||||
| }, | }, | ||||
| }, | }, | ||||
| MuiTableRow: { | |||||
| styleOverrides: { | |||||
| root: { | |||||
| "&:last-child td, &:last-child th ": { | |||||
| borderBottomStyle: "none", | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, | |||||
| }, | }, | ||||
| }; | }; | ||||
| @@ -2484,6 +2484,11 @@ | |||||
| dependencies: | dependencies: | ||||
| "@types/node" "*" | "@types/node" "*" | ||||
| "@types/sprintf-js@^1.1.2": | |||||
| version "1.1.2" | |||||
| resolved "https://registry.yarnpkg.com/@types/sprintf-js/-/sprintf-js-1.1.2.tgz#a4fcb84c7344f39f70dc4eec0e1e7f10a48597a3" | |||||
| integrity sha512-hkgzYF+qnIl8uTO8rmUSVSfQ8BIfMXC4yJAF4n8BE758YsKBZvFC4NumnAegj7KmylP0liEZNpb9RRGFMbFejA== | |||||
| "@types/stack-utils@^2.0.0": | "@types/stack-utils@^2.0.0": | ||||
| version "2.0.1" | version "2.0.1" | ||||
| resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" | resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" | ||||
| @@ -8634,6 +8639,11 @@ spdy@^4.0.2: | |||||
| select-hose "^2.0.0" | select-hose "^2.0.0" | ||||
| spdy-transport "^3.0.0" | spdy-transport "^3.0.0" | ||||
| sprintf-js@^1.1.2: | |||||
| version "1.1.2" | |||||
| resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" | |||||
| integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== | |||||
| sprintf-js@~1.0.2: | sprintf-js@~1.0.2: | ||||
| version "1.0.3" | version "1.0.3" | ||||
| resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" | resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" | ||||