| @@ -0,0 +1,15 @@ | |||
| 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; | |||
| }; | |||
| @@ -6,6 +6,7 @@ import { | |||
| request, | |||
| } from "api"; | |||
| import { getUrl } from "api/url"; | |||
| import { ReceiptIssuingOrderStatus } from "codes/receipt-issuing-order"; | |||
| export type HTCustomer = { | |||
| customer_code: string; | |||
| @@ -50,3 +51,39 @@ export const createReceiptIssuingOrder = async ( | |||
| }); | |||
| return res; | |||
| }; | |||
| export type ReceiptIssuingOrder = { | |||
| id: string; | |||
| order_datetime: string; | |||
| customer_code: string; | |||
| parking_management_code: string; | |||
| customer_name: string; | |||
| parking_name: string; | |||
| status: ReceiptIssuingOrderStatus; | |||
| handler_id: string; | |||
| handler_name: string; | |||
| }; | |||
| export type ReceiptIssuingOrdersRequest = { | |||
| customer_code?: string; | |||
| customer_name?: string; | |||
| parking_management_code?: string; | |||
| parking_name?: string; | |||
| }; | |||
| export type ReceiptIssuingOrdersResponse = { | |||
| data: { | |||
| records: ReceiptIssuingOrder[]; | |||
| }; | |||
| } & APICommonResponse; | |||
| export const getReceiptIssuingOrders = async ( | |||
| data: ReceiptIssuingOrdersRequest | |||
| ) => { | |||
| const res = await request<ReceiptIssuingOrdersResponse>({ | |||
| url: getUrl(ApiId.HT_CUSTOM_RECEIPT_ISSUING_ORDERS), | |||
| method: HttpMethod.GET, | |||
| data: new URLSearchParams(data), | |||
| }); | |||
| return res; | |||
| }; | |||
| @@ -13,6 +13,9 @@ export const ApiId = { | |||
| LOGIN: id++, | |||
| LOGOUT: id++, | |||
| APP_TOKEN_CHECK: id++, | |||
| DOWNLOAD_RECEIPT: id++, | |||
| RECEIPT_ISSUING_ORDERS: id++, | |||
| RECEIPT_ISSUING_ORDER: id++, | |||
| RECEIPT_ISSUING_ORDER_CREATE: id++, | |||
| @@ -21,6 +24,7 @@ export const ApiId = { | |||
| HT_CUSTOM_CUSTOMERS: id++, | |||
| HT_CUSTOM_PARKINGS: id++, | |||
| HT_CUSTOM_ADJUST_DATA: id++, | |||
| HT_CUSTOM_RECEIPT_ISSUING_ORDERS: id++, | |||
| HT_CUSTOM_RECEIPT_ISSUING_ORDER_CREATE: id++, | |||
| } as const; | |||
| export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | |||
| @@ -1,4 +1,6 @@ | |||
| import { env } from "process"; | |||
| import { ApiId as A } from "."; | |||
| import { HOST_API } from "config"; | |||
| const urls = { | |||
| [A.CSRF_TOKEN]: "sanctum/csrf-cookie", | |||
| @@ -6,18 +8,24 @@ const urls = { | |||
| [A.LOGIN]: "login", | |||
| [A.LOGOUT]: "logout", | |||
| [A.APP_TOKEN_CHECK]: "app-token-check", | |||
| [A.DOWNLOAD_RECEIPT]: "receipt/download", | |||
| [A.RECEIPT_ISSUING_ORDERS]: "receipt-issuing-orders", | |||
| // FOR CUSTOM | |||
| [A.HT_CUSTOM_CUSTOMERS]: "custom/hello-techno/customers", | |||
| [A.HT_CUSTOM_PARKINGS]: "custom/hello-techno/parkings", | |||
| [A.HT_CUSTOM_ADJUST_DATA]: "custom/hello-techno/adjust-data", | |||
| [A.HT_CUSTOM_RECEIPT_ISSUING_ORDERS]: | |||
| "custom/hello-techno/receipt-issuing-orders", | |||
| [A.HT_CUSTOM_RECEIPT_ISSUING_ORDER_CREATE]: | |||
| "custom/hello-techno/receipt-issuing-order/create", | |||
| }; | |||
| const prefixs = { | |||
| [A.CSRF_TOKEN]: "", | |||
| [A.DOWNLOAD_RECEIPT]: "", | |||
| }; | |||
| const DEFAULT_API_URL_PREFIX = "api"; | |||
| @@ -32,3 +40,7 @@ export const getUrl = (apiId: A) => { | |||
| } | |||
| return url + (urls[apiId] ?? ""); | |||
| }; | |||
| export const getFullUrl = (apiId: A) => { | |||
| return HOST_API + "/" + getUrl(apiId); | |||
| }; | |||
| @@ -5,6 +5,8 @@ export const PageID = { | |||
| LOGIN: id++, | |||
| LOGOUT: id++, | |||
| APP_RECEIPT_ISSUING_ORDER_INDEX: id++, | |||
| DASHBOARD_OVERVIEW: id++, | |||
| DASHBOARD_CONTRACT_LIST: id++, | |||
| @@ -20,3 +20,21 @@ export const ReceiptIssuingOrderStatus = { | |||
| 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,48 @@ | |||
| import { HasChildren } from "@types"; | |||
| import { checkToken } from "api/app"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import { createContext, useState } from "react"; | |||
| type App = { | |||
| token: string; | |||
| tokenResult: "cheking" | "ok" | "ng"; | |||
| setToken: (token: string) => void; | |||
| }; | |||
| export const AppContext = createContext<App>({ | |||
| token: "", | |||
| tokenResult: "cheking", | |||
| setToken: (token: string) => {}, | |||
| }); | |||
| type Props = HasChildren; | |||
| export function AppContextProvider({ children }: Props) { | |||
| const [token, _setToken] = useState(""); | |||
| const [tokenResult, setTokenResult] = useState<"cheking" | "ok" | "ng">( | |||
| "cheking" | |||
| ); | |||
| const checkTokenAPI = useAPICall({ | |||
| apiMethod: checkToken, | |||
| onSuccess: (res, sendData) => { | |||
| setTokenResult("ok"); | |||
| _setToken(sendData.access_token); | |||
| }, | |||
| onFailed: () => { | |||
| setTokenResult("ng"); | |||
| }, | |||
| }); | |||
| const setToken = (token: string) => { | |||
| checkTokenAPI.callAPI({ | |||
| access_token: token, | |||
| }); | |||
| }; | |||
| return ( | |||
| <AppContext.Provider value={{ token, tokenResult, setToken }}> | |||
| {children} | |||
| </AppContext.Provider> | |||
| ); | |||
| } | |||
| @@ -14,7 +14,9 @@ const initialState = { | |||
| condition: _condition, | |||
| get: (key: string): string => "", | |||
| initializeCondition: () => {}, | |||
| addCondition: (condition: Dictionary) => {}, | |||
| addCondition: (condition: Dictionary) => { | |||
| console.log("not init SearchConditionContext"); | |||
| }, | |||
| clearCondition: () => {}, | |||
| }; | |||
| @@ -45,7 +47,6 @@ export function SearchConditionContextProvider({ children }: Props) { | |||
| } | |||
| if (!isEqual(after, condition)) { | |||
| console.log("initialCondition", { before: condition, after }); | |||
| setCondition(after, "initializeCondition"); | |||
| } | |||
| setInitialized(true); | |||
| @@ -72,7 +73,6 @@ export function SearchConditionContextProvider({ children }: Props) { | |||
| }); | |||
| if (!isEqual(before, after)) { | |||
| console.log("addCondition", { additional, before, after }); | |||
| setCondition(after, "addCondition"); | |||
| } | |||
| }; | |||
| @@ -0,0 +1,8 @@ | |||
| import { AppContext } from "contexts/AppContext"; | |||
| import { useContext } from "react"; | |||
| export default function useApp() { | |||
| const context = useContext(AppContext); | |||
| return context; | |||
| } | |||
| @@ -0,0 +1,48 @@ | |||
| import { Box, Button, Paper, Stack, Typography } from "@mui/material"; | |||
| import { ApiId } from "api"; | |||
| import { getFullUrl } from "api/url"; | |||
| import useApp from "hooks/useApp"; | |||
| import { useEffect, useMemo } from "react"; | |||
| import { useParams } from "react-router-dom"; | |||
| export default function ReceiptIssuingOrder() { | |||
| const { token: paramToken } = useParams(); | |||
| const { token, setToken, tokenResult } = useApp(); | |||
| const downloadUrl = useMemo(() => { | |||
| return getFullUrl(ApiId.DOWNLOAD_RECEIPT) + "?access_token=" + token; | |||
| }, [token]); | |||
| useEffect(() => { | |||
| if (paramToken) { | |||
| setToken(paramToken); | |||
| console.log("render"); | |||
| } | |||
| }, []); | |||
| if (tokenResult === "cheking") { | |||
| return null; | |||
| } | |||
| if (tokenResult === "ng") { | |||
| return <Box>不正なアクセス</Box>; | |||
| } | |||
| 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> | |||
| ); | |||
| } | |||
| @@ -7,26 +7,24 @@ import { | |||
| TableContainer, | |||
| TablePagination, | |||
| TableRow, | |||
| TextField, | |||
| } from "@mui/material"; | |||
| import { Dictionary } from "@types"; | |||
| import { | |||
| ReceiptIssuingOrder, | |||
| getReceiptIssuingOrders, | |||
| } from "api/receipt-issuing-order"; | |||
| } from "api/custom/hello-techno/receipt-issuing-order"; | |||
| import { PageID, TabID } from "codes/page"; | |||
| import { ReceiptIssuingOrderStatus } from "codes/receipt-issuing-order"; | |||
| import { getStatusName } from "codes/receipt-issuing-order"; | |||
| import { FormProvider, RHFTextField } from "components/hook-form"; | |||
| import { TableHeadCustom } from "components/table"; | |||
| import { SearchConditionContextProvider } from "contexts/SearchConditionContext"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useBackDrop from "hooks/useBackDrop"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import useSearchConditionContext from "hooks/useSearchConditionContext"; | |||
| import useTable, { UseTableReturn } from "hooks/useTable"; | |||
| import ContractTabs from "layouts/dashbord/tab/ContractTabs"; | |||
| import { useEffect } from "react"; | |||
| import { useForm } from "react-hook-form"; | |||
| import { Contract } from "types/contract"; | |||
| export default function ReceiptIssuingOrderList() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| @@ -34,8 +32,6 @@ export default function ReceiptIssuingOrderList() { | |||
| TabID.NONE | |||
| ); | |||
| const table = useTable<ReceiptIssuingOrder>(); | |||
| useEffect(() => { | |||
| setHeaderTitle("領収証発行依頼一覧"); | |||
| setTabs(null); | |||
| @@ -43,33 +39,14 @@ export default function ReceiptIssuingOrderList() { | |||
| return ( | |||
| <SearchConditionContextProvider> | |||
| <Page table={table} /> | |||
| <Page /> | |||
| </SearchConditionContextProvider> | |||
| ); | |||
| } | |||
| type CommonProps = { | |||
| table: UseTableReturn<ReceiptIssuingOrder>; | |||
| }; | |||
| function Page({ table }: CommonProps) { | |||
| const { | |||
| order, | |||
| page, | |||
| sort, | |||
| rowsPerPage, | |||
| fetched, | |||
| fillteredRow, | |||
| isNotFound, | |||
| dataLength, | |||
| // | |||
| onSort, | |||
| onChangePage, | |||
| onChangeRowsPerPage, | |||
| // | |||
| setRowData, | |||
| // | |||
| ROWS_PER_PAGES, | |||
| } = table; | |||
| function Page() { | |||
| const table = useTable<ReceiptIssuingOrder>(); | |||
| return ( | |||
| <Box> | |||
| <SearchBox table={table} /> | |||
| @@ -79,7 +56,11 @@ function Page({ table }: CommonProps) { | |||
| } | |||
| type FormProps = { | |||
| address: string; | |||
| customer_name: string; | |||
| parking_name: string; | |||
| }; | |||
| type CommonProps = { | |||
| table: UseTableReturn<ReceiptIssuingOrder>; | |||
| }; | |||
| function SearchBox({ table }: CommonProps) { | |||
| const { | |||
| @@ -89,13 +70,20 @@ function SearchBox({ table }: CommonProps) { | |||
| addCondition: add, | |||
| } = useSearchConditionContext(); | |||
| const { setShowBackDrop } = useBackDrop(); | |||
| const form = useForm<FormProps>({ | |||
| defaultValues: { | |||
| address: "", | |||
| customer_name: "", | |||
| parking_name: "", | |||
| }, | |||
| }); | |||
| const { callAPI: calGetReceiptIssuingOrders, makeSendData } = useAPICall({ | |||
| const { | |||
| callAPI: calGetReceiptIssuingOrders, | |||
| makeSendData, | |||
| sending, | |||
| } = useAPICall({ | |||
| apiMethod: getReceiptIssuingOrders, | |||
| form, | |||
| onSuccess: (res) => { | |||
| @@ -111,6 +99,10 @@ function SearchBox({ table }: CommonProps) { | |||
| add({ ...data }); | |||
| }; | |||
| const handleBlur = () => { | |||
| addCondition(form.getValues()); | |||
| }; | |||
| const fetch = async (data: Dictionary) => { | |||
| const sendData = makeSendData({ | |||
| ...data, | |||
| @@ -121,7 +113,8 @@ function SearchBox({ table }: CommonProps) { | |||
| // 初期値設定 | |||
| useEffect(() => { | |||
| if (initialized) { | |||
| form.setValue("address", get("address")); | |||
| form.setValue("customer_name", get("customer_name")); | |||
| form.setValue("parking_name", get("parking_name")); | |||
| } | |||
| }, [initialized, condition]); | |||
| @@ -132,38 +125,32 @@ function SearchBox({ table }: CommonProps) { | |||
| } | |||
| }, [condition, initialized]); | |||
| // バックドロップ | |||
| useEffect(() => { | |||
| setShowBackDrop(sending); | |||
| }, [sending]); | |||
| return ( | |||
| <FormProvider methods={form} onSubmit={form.handleSubmit(handleSubmit)}> | |||
| <Box sx={{ p: 1, m: 1 }}> | |||
| <Grid container spacing={1}> | |||
| <Grid item xs={3}> | |||
| <RHFTextField | |||
| label="ステータス" | |||
| name="status" | |||
| label="運営会社名" | |||
| name="customer_name" | |||
| fullWidth | |||
| size="small" | |||
| onBlur={handleBlur} | |||
| /> | |||
| </Grid> | |||
| <Grid item xs={3}> | |||
| <TextField label="a" fullWidth size="small" /> | |||
| </Grid> | |||
| <Grid item xs={3}> | |||
| <TextField label="a" fullWidth size="small" /> | |||
| </Grid> | |||
| <Grid item xs={3}> | |||
| <TextField label="a" fullWidth size="small" /> | |||
| </Grid> | |||
| <Grid item xs={3}> | |||
| <TextField label="a" fullWidth size="small" /> | |||
| </Grid> | |||
| <Grid item xs={3}> | |||
| <TextField label="a" fullWidth size="small" /> | |||
| </Grid> | |||
| <Grid item xs={3}> | |||
| <TextField label="a" fullWidth size="small" /> | |||
| </Grid> | |||
| <Grid item xs={3}> | |||
| <TextField label="a" fullWidth size="small" /> | |||
| <RHFTextField | |||
| label="駐車場名" | |||
| name="parking_name" | |||
| fullWidth | |||
| size="small" | |||
| onBlur={handleBlur} | |||
| /> | |||
| </Grid> | |||
| </Grid> | |||
| </Box> | |||
| @@ -173,9 +160,11 @@ function SearchBox({ table }: CommonProps) { | |||
| function TableBox({ table }: CommonProps) { | |||
| const TABLE_HEAD = [ | |||
| { id: "id", label: "ID", align: "left" }, | |||
| { id: "name", label: "名前", align: "left" }, | |||
| { id: "emply", label: "---", align: "left" }, | |||
| { id: "order_datetime", label: "受付時刻", align: "left" }, | |||
| { id: "customer_name", label: "運営会社名", align: "left" }, | |||
| { id: "parking_name", label: "駐車場名", align: "left" }, | |||
| { id: "status", label: "ステータス", align: "left" }, | |||
| { id: "handler_name", label: "担当者", align: "left" }, | |||
| ]; | |||
| const { | |||
| order, | |||
| @@ -196,13 +185,6 @@ function TableBox({ table }: CommonProps) { | |||
| ROWS_PER_PAGES, | |||
| } = table; | |||
| useEffect(() => { | |||
| setRowData([ | |||
| { id: "iwabuchi", status: ReceiptIssuingOrderStatus.NONE }, | |||
| { id: "iwabuchi", status: ReceiptIssuingOrderStatus.MAIL_DONE }, | |||
| ]); | |||
| }, []); | |||
| return ( | |||
| <> | |||
| <TableContainer | |||
| @@ -250,8 +232,11 @@ type RowProps = { | |||
| function Row({ data }: RowProps) { | |||
| return ( | |||
| <TableRow hover sx={{ cursor: "pointer" }}> | |||
| <TableCell>{data.id}</TableCell> | |||
| <TableCell>{data.status}</TableCell> | |||
| <TableCell>{data.order_datetime}</TableCell> | |||
| <TableCell>{data.customer_name}</TableCell> | |||
| <TableCell>{data.parking_name}</TableCell> | |||
| <TableCell>{getStatusName(data.status)}</TableCell> | |||
| <TableCell>{data.handler_name}</TableCell> | |||
| </TableRow> | |||
| ); | |||
| } | |||
| @@ -1,12 +1,13 @@ | |||
| import { PageID } from "codes/page"; | |||
| import { UserRole } from "codes/user"; | |||
| import LoadingScreen from "components/LoadingScreen"; | |||
| import { AppContextProvider } from "contexts/AppContext"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import DashboardLayout from "layouts/dashbord"; | |||
| import SimpleLayout from "layouts/simple"; | |||
| import { ElementType, Suspense, lazy, useMemo } from "react"; | |||
| import { RouteObject, useRoutes } from "react-router-dom"; | |||
| import { getRoute } from "./path"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import { UserRole } from "codes/user"; | |||
| const Loadable = (Component: ElementType) => (props: any) => { | |||
| return ( | |||
| @@ -30,6 +31,20 @@ const AuthRoutes = (): RouteObject => ({ | |||
| ], | |||
| }); | |||
| const AppRoutes = (): RouteObject => ({ | |||
| element: ( | |||
| <AppContextProvider> | |||
| <SimpleLayout /> | |||
| </AppContextProvider> | |||
| ), | |||
| children: [ | |||
| { | |||
| path: getRoute(PageID.APP_RECEIPT_ISSUING_ORDER_INDEX), | |||
| element: <ReceiptIssuingOrder />, | |||
| }, | |||
| ], | |||
| }); | |||
| const DashboardRoutes = (): RouteObject => { | |||
| const { canAccess } = useAuth(); | |||
| @@ -80,6 +95,7 @@ export function Routes() { | |||
| const { initialized } = useAuth(); | |||
| return useRoutes([ | |||
| AuthRoutes(), | |||
| AppRoutes(), | |||
| DashboardRoutes(), | |||
| { | |||
| path: "403", | |||
| @@ -96,6 +112,11 @@ export function Routes() { | |||
| const Login = Loadable(lazy(() => import("pages/auth/login"))); | |||
| const Logout = Loadable(lazy(() => import("pages/auth/logout"))); | |||
| // App --------------------------- | |||
| const ReceiptIssuingOrder = Loadable( | |||
| lazy(() => import("pages/app/ReceiptIssuingOrder")) | |||
| ); | |||
| //ダッシュボード ---------------------------- | |||
| const Dashboard = Loadable(lazy(() => import("pages/dashboard"))); | |||
| // 契約関連 | |||
| @@ -37,6 +37,10 @@ const PATHS = { | |||
| [makePathKey(PageID.DASHBOARD_OVERVIEW)]: "/dashboard", | |||
| // APP | |||
| [makePathKey(PageID.APP_RECEIPT_ISSUING_ORDER_INDEX)]: | |||
| "/app/receipt-issuing-oreder/:token", | |||
| // 契約関連 | |||
| [makePathKey(PageID.DASHBOARD_CONTRACT_LIST)]: | |||
| "/dashboard/contract/list/:page", | |||
| @@ -153,7 +153,7 @@ theme = { | |||
| styleOverrides: { | |||
| root: { | |||
| "&.Mui-disabled:before": { | |||
| "border-bottom-style": "none", | |||
| borderBottomStyle: "none", | |||
| }, | |||
| }, | |||
| }, | |||