| @@ -13,6 +13,8 @@ export const ApiId = { | |||
| LOGIN: id++, | |||
| LOGOUT: id++, | |||
| SEASON_TICKET_CONTRACTS: id++, | |||
| } as const; | |||
| export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | |||
| @@ -0,0 +1,26 @@ | |||
| import { APICommonResponse, ApiId, HttpMethod, request } from "api"; | |||
| import { getUrl } from "./url"; | |||
| export type SeasonTicketContract = { | |||
| season_ticekt_contract_record_no: string | null; | |||
| parking_name: string | null; | |||
| room_no: string | null; | |||
| season_ticket_seq_no: string | null; | |||
| vehicle_no: string | null; | |||
| vehicle_type: string | null; | |||
| contract_start_date: string | null; | |||
| contract_end_date: string | null; | |||
| revision: number; | |||
| }; | |||
| type SeasonTicketContractsResponse = { | |||
| data: SeasonTicketContract[]; | |||
| } & APICommonResponse; | |||
| export const getSeasonTicketContracts = async () => { | |||
| const res = await request<SeasonTicketContractsResponse>({ | |||
| url: getUrl(ApiId.SEASON_TICKET_CONTRACTS), | |||
| method: HttpMethod.GET, | |||
| }); | |||
| return res; | |||
| }; | |||
| @@ -6,6 +6,7 @@ const urls = { | |||
| [A.ME]: "me", | |||
| [A.LOGIN]: "login", | |||
| [A.LOGOUT]: "logout", | |||
| [A.SEASON_TICKET_CONTRACTS]: "season-ticket-contracts", | |||
| }; | |||
| const prefixs = { | |||
| @@ -0,0 +1,84 @@ | |||
| import { HasChildren } from "@types"; | |||
| import { | |||
| SeasonTicketContract, | |||
| getSeasonTicketContracts, | |||
| } from "api/season-ticket-contract"; | |||
| import useAPICall from "hooks/useAPICall"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import { | |||
| createContext, | |||
| useContext, | |||
| useEffect, | |||
| useLayoutEffect, | |||
| useState, | |||
| } from "react"; | |||
| type ContextProps = { | |||
| initialized: boolean; | |||
| seasonTicketContracts: SeasonTicketContract[]; | |||
| selectedseasonTicketContract: SeasonTicketContract | null; | |||
| fetch: VoidFunction; | |||
| select: (target: SeasonTicketContract | null) => void; | |||
| }; | |||
| export const SeasonTicketContractContext = createContext<ContextProps>({ | |||
| initialized: false, | |||
| seasonTicketContracts: [], | |||
| selectedseasonTicketContract: null, | |||
| fetch: () => {}, | |||
| select: (target: SeasonTicketContract | null) => {}, | |||
| }); | |||
| type Props = HasChildren; | |||
| export function SeasonTicketContractContextProvider({ children }: Props) { | |||
| const { initialized, authenticated } = useAuth(); | |||
| const [fetchInitialized, setFetchInitialized] = useState(false); | |||
| const [seasonTicketContracts, setSeasonTicketContracts] = useState< | |||
| SeasonTicketContract[] | |||
| >([]); | |||
| const [selectedseasonTicketContract, setSelectedseasonTicketContract] = | |||
| useState<SeasonTicketContract | null>(null); | |||
| const { callAPI: callGetSeasonTicketContracts } = useAPICall({ | |||
| apiMethod: getSeasonTicketContracts, | |||
| backDrop: true, | |||
| onSuccess: ({ data }) => { | |||
| setSeasonTicketContracts(data); | |||
| setFetchInitialized(true); | |||
| }, | |||
| onFailed: () => { | |||
| setSeasonTicketContracts([]); | |||
| setFetchInitialized(true); | |||
| }, | |||
| }); | |||
| const fetch = () => { | |||
| callGetSeasonTicketContracts({}); | |||
| }; | |||
| useEffect(() => { | |||
| if (authenticated) { | |||
| fetch(); | |||
| } | |||
| }, [authenticated]); | |||
| return ( | |||
| <SeasonTicketContractContext.Provider | |||
| value={{ | |||
| initialized: fetchInitialized, | |||
| seasonTicketContracts, | |||
| selectedseasonTicketContract, | |||
| fetch, | |||
| select: setSelectedseasonTicketContract, | |||
| }} | |||
| > | |||
| {children} | |||
| </SeasonTicketContractContext.Provider> | |||
| ); | |||
| } | |||
| export function useSeasonTicketContractContext() { | |||
| return useContext(SeasonTicketContractContext); | |||
| } | |||
| @@ -79,12 +79,12 @@ export default function Navigator(props: DrawerProps) { | |||
| { | |||
| label: "契約", | |||
| icon: <ArticleIcon />, | |||
| id: PageID.DASHBOARD_CONTRACT_LIST, | |||
| id: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST, | |||
| }, | |||
| { | |||
| label: "新規利用申込", | |||
| icon: <ArticleIcon />, | |||
| id: PageID.DASHBOARD_CONTRACT_ENTRY, | |||
| id: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY, | |||
| }, | |||
| { | |||
| label: "領収証ダウンロード", | |||
| @@ -9,33 +9,62 @@ import { | |||
| TableRow, | |||
| Typography, | |||
| } from "@mui/material"; | |||
| import { useSeasonTicketContractContext } from "contexts/dashboard/SeasonTicketContractContext"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { useEffect } from "react"; | |||
| import { useParams } from "react-router-dom"; | |||
| import { getPath } from "routes/path"; | |||
| export default function ContractDetail() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| PageID.DASHBOARD_CONTRACT_DETAIL, | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL, | |||
| TabID.NONE | |||
| ); | |||
| const { navigate } = useNavigateCustom(); | |||
| const { id: paramId } = useParams(); | |||
| const { | |||
| initialized, | |||
| selectedseasonTicketContract: seasonTicketContract, | |||
| seasonTicketContracts, | |||
| select, | |||
| } = useSeasonTicketContractContext(); | |||
| const { navigate, navigateWhenChanged } = useNavigateCustom(); | |||
| const moveToList = () => { | |||
| navigateWhenChanged(getPath(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST)); | |||
| }; | |||
| useEffect(() => { | |||
| setHeaderTitle("契約詳細"); | |||
| setTabs(null); | |||
| }, [setHeaderTitle, setTabs]); | |||
| useEffect(() => { | |||
| if (initialized && !!paramId) { | |||
| const target = seasonTicketContracts.find((ele) => { | |||
| return ele.season_ticekt_contract_record_no === paramId; | |||
| }); | |||
| if (target) { | |||
| select(target); | |||
| } else { | |||
| select(null); | |||
| moveToList(); | |||
| } | |||
| } | |||
| }, [initialized]); | |||
| if (!initialized || seasonTicketContract === null) { | |||
| return null; | |||
| } | |||
| return ( | |||
| <Box sx={{ p: 1, m: 1 }}> | |||
| <Box> | |||
| <Stack spacing={2}> | |||
| <Box> | |||
| <Button | |||
| onClick={() => { | |||
| navigate(-1); | |||
| }} | |||
| > | |||
| 戻る | |||
| </Button> | |||
| <Button onClick={moveToList}>戻る</Button> | |||
| </Box> | |||
| <Paper sx={{ p: 2 }}> | |||
| <Typography variant="h5">契約情報</Typography> | |||
| @@ -43,15 +72,18 @@ export default function ContractDetail() { | |||
| <TableBody> | |||
| <TableRow> | |||
| <TableCell>駐車場名</TableCell> | |||
| <TableCell>A駐車場</TableCell> | |||
| <TableCell>{seasonTicketContract.parking_name}</TableCell> | |||
| </TableRow> | |||
| <TableRow> | |||
| <TableCell>区画</TableCell> | |||
| <TableCell>1-1</TableCell> | |||
| <TableCell>{seasonTicketContract.room_no}</TableCell> | |||
| </TableRow> | |||
| <TableRow> | |||
| <TableCell>契約期間</TableCell> | |||
| <TableCell>2023/8/1-2024/7/31</TableCell> | |||
| <TableCell> | |||
| {seasonTicketContract.contract_start_date}- | |||
| {seasonTicketContract.contract_end_date} | |||
| </TableCell> | |||
| </TableRow> | |||
| </TableBody> | |||
| </Table> | |||
| @@ -5,7 +5,7 @@ import { useEffect } from "react"; | |||
| export default function ContractEntry() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| PageID.DASHBOARD_CONTRACT_ENTRY, | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY, | |||
| TabID.NONE | |||
| ); | |||
| @@ -17,11 +17,8 @@ export default function ContractEntry() { | |||
| return ( | |||
| <Stack> | |||
| <Box>新規の申込は、こちらよりお願いします。</Box> | |||
| <Link href="https://www.kyotopublic.or.jp/" target="_blank"> | |||
| 京都・滋賀駐車場なび | |||
| </Link> | |||
| <Link href="https://osaka-parking.jp/" target="_blank"> | |||
| 大阪・神戸・奈良駐車場なび | |||
| <Link href="https://www.kyotopublic.or.jp/teiki-parking/" target="_blank"> | |||
| 月極定期駐車場ナビ | |||
| </Link> | |||
| </Stack> | |||
| ); | |||
| @@ -1,26 +1,44 @@ | |||
| import { Box, Grid, Paper, Typography } from "@mui/material"; | |||
| import { SeasonTicketContract } from "api/season-ticket-contract"; | |||
| import { useSeasonTicketContractContext } from "contexts/dashboard/SeasonTicketContractContext"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import useNavigateCustom from "hooks/useNavigateCustom"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { useEffect } from "react"; | |||
| import { getPath } from "routes/path"; | |||
| function SeasonTicketContractCard({ data }: { data: SeasonTicketContract }) { | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const handleClick = () => { | |||
| navigateWhenChanged( | |||
| getPath(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL, { | |||
| query: { | |||
| id: data.season_ticekt_contract_record_no ?? "", | |||
| }, | |||
| }) | |||
| ); | |||
| }; | |||
| return ( | |||
| <Paper sx={{ p: 2, cursor: "pointer" }} onClick={handleClick}> | |||
| <Typography variant="h5">{data.parking_name}</Typography> | |||
| <Typography variant="h6">区画:{data.room_no}</Typography> | |||
| </Paper> | |||
| ); | |||
| } | |||
| export default function ContractList() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| PageID.DASHBOARD_CONTRACT_LIST, | |||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST, | |||
| TabID.NONE | |||
| ); | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const { seasonTicketContracts } = useSeasonTicketContractContext(); | |||
| const items = [ | |||
| { parkingName: "A駐車場", position: "1" }, | |||
| { parkingName: "B駐車場", position: "1-1" }, | |||
| ]; | |||
| const { navigateWhenChanged } = useNavigateCustom(); | |||
| const moveToDetail = () => { | |||
| navigateWhenChanged( | |||
| getPath(PageID.DASHBOARD_CONTRACT_DETAIL, { | |||
| getPath(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL, { | |||
| query: { | |||
| id: "test-test", | |||
| }, | |||
| @@ -35,13 +53,10 @@ export default function ContractList() { | |||
| return ( | |||
| <Box sx={{ p: 1, m: 1 }}> | |||
| <Grid container spacing={2}> | |||
| {items.map((item, index) => { | |||
| {seasonTicketContracts.map((item, index) => { | |||
| return ( | |||
| <Grid item xs={12} md={6} key={index}> | |||
| <Paper sx={{ p: 2, cursor: "pointer" }} onClick={moveToDetail}> | |||
| <Typography variant="h5">{item.parkingName}</Typography> | |||
| <Typography variant="h6">区画:{item.position}</Typography> | |||
| </Paper> | |||
| <SeasonTicketContractCard data={item} /> | |||
| </Grid> | |||
| ); | |||
| })} | |||
| @@ -1,12 +1,11 @@ | |||
| import { Button, Stack } from "@mui/material"; | |||
| import StackRow from "components/stack/StackRow"; | |||
| import useDashboard from "hooks/useDashBoard"; | |||
| import { PageID, TabID } from "pages"; | |||
| import { useEffect } from "react"; | |||
| export default function UserDetail() { | |||
| const { setHeaderTitle, setTabs } = useDashboard( | |||
| PageID.DASHBOARD_CONTRACT_DETAIL, | |||
| PageID.DASHBOARD_USER_DETAIL, | |||
| TabID.NONE | |||
| ); | |||
| @@ -7,9 +7,9 @@ export const PageID = { | |||
| DASHBOARD_OVERVIEW: id++, | |||
| DASHBOARD_CONTRACT_ENTRY: id++, | |||
| DASHBOARD_CONTRACT_LIST: id++, | |||
| DASHBOARD_CONTRACT_DETAIL: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_LIST: id++, | |||
| DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL: id++, | |||
| DASHBOARD_RECEIPT_DOWNLOAD: id++, | |||
| @@ -41,7 +41,7 @@ export function Routes() { | |||
| return useRoutes([ | |||
| CommonRoutes(), | |||
| AuthRoutes(), | |||
| DashboardRoutes(), | |||
| ...DashboardRoutes(), | |||
| { | |||
| path: "403", | |||
| element: <Page403 />, | |||
| @@ -31,10 +31,11 @@ const getTabId = (key: PathKey): TabID => { | |||
| const PATHS_DASHBOARD = { | |||
| [makePathKey(PageID.DASHBOARD_OVERVIEW)]: "/dashboard", | |||
| // --契約----------------------- | |||
| [makePathKey(PageID.DASHBOARD_CONTRACT_ENTRY)]: "/dashboard/contract/entry", | |||
| [makePathKey(PageID.DASHBOARD_CONTRACT_LIST)]: | |||
| [makePathKey(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY)]: | |||
| "/dashboard/contract/entry", | |||
| [makePathKey(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST)]: | |||
| "/dashboard/contract/list/:page", | |||
| [makePathKey(PageID.DASHBOARD_CONTRACT_DETAIL)]: | |||
| [makePathKey(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL)]: | |||
| "/dashboard/contract/detail/:id", | |||
| [makePathKey(PageID.DASHBOARD_RECEIPT_DOWNLOAD)]: | |||
| "/dashboard/receipt/download", | |||
| @@ -1,3 +1,4 @@ | |||
| import { SeasonTicketContractContextProvider } from "contexts/dashboard/SeasonTicketContractContext"; | |||
| import useAuth from "hooks/useAuth"; | |||
| import DashboardLayout from "layouts/dashbord"; | |||
| import { PageID } from "pages"; | |||
| @@ -6,7 +7,7 @@ import { RouteObject } from "react-router-dom"; | |||
| import { Loadable } from "routes"; | |||
| import { getRoute } from "routes/path"; | |||
| export default function DashboardRoutes(): RouteObject { | |||
| export default function DashboardRoutes(): RouteObject[] { | |||
| const { authenticated } = useAuth(); | |||
| const children: RouteObject[] = useMemo(() => { | |||
| @@ -16,12 +17,6 @@ export default function DashboardRoutes(): RouteObject { | |||
| const ContractEntry = Loadable( | |||
| lazy(() => import("pages/dashboard/contract/entry")) | |||
| ); | |||
| const ContractList = Loadable( | |||
| lazy(() => import("pages/dashboard/contract/list")) | |||
| ); | |||
| const ContractDetail = Loadable( | |||
| lazy(() => import("pages/dashboard/contract/detail")) | |||
| ); | |||
| const ReceiptDownload = Loadable( | |||
| lazy(() => import("pages/dashboard/receipt/download")) | |||
| ); | |||
| @@ -36,17 +31,9 @@ export default function DashboardRoutes(): RouteObject { | |||
| element: <Dashboard />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_CONTRACT_ENTRY, | |||
| pageId: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY, | |||
| element: <ContractEntry />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_CONTRACT_LIST, | |||
| element: <ContractList />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_CONTRACT_DETAIL, | |||
| element: <ContractDetail />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_RECEIPT_DOWNLOAD, | |||
| element: <ReceiptDownload />, | |||
| @@ -66,8 +53,44 @@ export default function DashboardRoutes(): RouteObject { | |||
| })); | |||
| }, [authenticated]); | |||
| return { | |||
| element: <DashboardLayout />, | |||
| children: children, | |||
| }; | |||
| const seasonTicketContractChildren: RouteObject[] = useMemo(() => { | |||
| if (!authenticated) return []; | |||
| const ContractList = Loadable( | |||
| lazy(() => import("pages/dashboard/contract/list")) | |||
| ); | |||
| const ContractDetail = Loadable( | |||
| lazy(() => import("pages/dashboard/contract/detail")) | |||
| ); | |||
| const allChildren = [ | |||
| { | |||
| pageId: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST, | |||
| element: <ContractList />, | |||
| }, | |||
| { | |||
| pageId: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL, | |||
| element: <ContractDetail />, | |||
| }, | |||
| ]; | |||
| return allChildren.map(({ pageId, ...others }) => ({ | |||
| ...others, | |||
| path: getRoute(pageId), | |||
| })); | |||
| }, [authenticated]); | |||
| return [ | |||
| { | |||
| element: <DashboardLayout />, | |||
| children: children, | |||
| }, | |||
| { | |||
| element: ( | |||
| <SeasonTicketContractContextProvider> | |||
| <DashboardLayout /> | |||
| </SeasonTicketContractContextProvider> | |||
| ), | |||
| children: seasonTicketContractChildren, | |||
| }, | |||
| ]; | |||
| } | |||