| @@ -13,6 +13,8 @@ export const ApiId = { | |||||
| LOGIN: id++, | LOGIN: id++, | ||||
| LOGOUT: id++, | LOGOUT: id++, | ||||
| SEASON_TICKET_CONTRACTS: id++, | |||||
| } as const; | } as const; | ||||
| export type ApiId = (typeof ApiId)[keyof typeof ApiId]; | 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.ME]: "me", | ||||
| [A.LOGIN]: "login", | [A.LOGIN]: "login", | ||||
| [A.LOGOUT]: "logout", | [A.LOGOUT]: "logout", | ||||
| [A.SEASON_TICKET_CONTRACTS]: "season-ticket-contracts", | |||||
| }; | }; | ||||
| const prefixs = { | 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: "契約", | label: "契約", | ||||
| icon: <ArticleIcon />, | icon: <ArticleIcon />, | ||||
| id: PageID.DASHBOARD_CONTRACT_LIST, | |||||
| id: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST, | |||||
| }, | }, | ||||
| { | { | ||||
| label: "新規利用申込", | label: "新規利用申込", | ||||
| icon: <ArticleIcon />, | icon: <ArticleIcon />, | ||||
| id: PageID.DASHBOARD_CONTRACT_ENTRY, | |||||
| id: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY, | |||||
| }, | }, | ||||
| { | { | ||||
| label: "領収証ダウンロード", | label: "領収証ダウンロード", | ||||
| @@ -9,33 +9,62 @@ import { | |||||
| TableRow, | TableRow, | ||||
| Typography, | Typography, | ||||
| } from "@mui/material"; | } from "@mui/material"; | ||||
| import { useSeasonTicketContractContext } from "contexts/dashboard/SeasonTicketContractContext"; | |||||
| import useDashboard from "hooks/useDashBoard"; | import useDashboard from "hooks/useDashBoard"; | ||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | import useNavigateCustom from "hooks/useNavigateCustom"; | ||||
| import { PageID, TabID } from "pages"; | import { PageID, TabID } from "pages"; | ||||
| import { useEffect } from "react"; | import { useEffect } from "react"; | ||||
| import { useParams } from "react-router-dom"; | |||||
| import { getPath } from "routes/path"; | |||||
| export default function ContractDetail() { | export default function ContractDetail() { | ||||
| const { setHeaderTitle, setTabs } = useDashboard( | const { setHeaderTitle, setTabs } = useDashboard( | ||||
| PageID.DASHBOARD_CONTRACT_DETAIL, | |||||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL, | |||||
| TabID.NONE | 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(() => { | useEffect(() => { | ||||
| setHeaderTitle("契約詳細"); | setHeaderTitle("契約詳細"); | ||||
| setTabs(null); | setTabs(null); | ||||
| }, [setHeaderTitle, setTabs]); | }, [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 ( | return ( | ||||
| <Box sx={{ p: 1, m: 1 }}> | |||||
| <Box> | |||||
| <Stack spacing={2}> | <Stack spacing={2}> | ||||
| <Box> | <Box> | ||||
| <Button | |||||
| onClick={() => { | |||||
| navigate(-1); | |||||
| }} | |||||
| > | |||||
| 戻る | |||||
| </Button> | |||||
| <Button onClick={moveToList}>戻る</Button> | |||||
| </Box> | </Box> | ||||
| <Paper sx={{ p: 2 }}> | <Paper sx={{ p: 2 }}> | ||||
| <Typography variant="h5">契約情報</Typography> | <Typography variant="h5">契約情報</Typography> | ||||
| @@ -43,15 +72,18 @@ export default function ContractDetail() { | |||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell>駐車場名</TableCell> | <TableCell>駐車場名</TableCell> | ||||
| <TableCell>A駐車場</TableCell> | |||||
| <TableCell>{seasonTicketContract.parking_name}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell>区画</TableCell> | <TableCell>区画</TableCell> | ||||
| <TableCell>1-1</TableCell> | |||||
| <TableCell>{seasonTicketContract.room_no}</TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| <TableRow> | <TableRow> | ||||
| <TableCell>契約期間</TableCell> | <TableCell>契約期間</TableCell> | ||||
| <TableCell>2023/8/1-2024/7/31</TableCell> | |||||
| <TableCell> | |||||
| {seasonTicketContract.contract_start_date}- | |||||
| {seasonTicketContract.contract_end_date} | |||||
| </TableCell> | |||||
| </TableRow> | </TableRow> | ||||
| </TableBody> | </TableBody> | ||||
| </Table> | </Table> | ||||
| @@ -5,7 +5,7 @@ import { useEffect } from "react"; | |||||
| export default function ContractEntry() { | export default function ContractEntry() { | ||||
| const { setHeaderTitle, setTabs } = useDashboard( | const { setHeaderTitle, setTabs } = useDashboard( | ||||
| PageID.DASHBOARD_CONTRACT_ENTRY, | |||||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY, | |||||
| TabID.NONE | TabID.NONE | ||||
| ); | ); | ||||
| @@ -17,11 +17,8 @@ export default function ContractEntry() { | |||||
| return ( | return ( | ||||
| <Stack> | <Stack> | ||||
| <Box>新規の申込は、こちらよりお願いします。</Box> | <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> | </Link> | ||||
| </Stack> | </Stack> | ||||
| ); | ); | ||||
| @@ -1,26 +1,44 @@ | |||||
| import { Box, Grid, Paper, Typography } from "@mui/material"; | 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 useDashboard from "hooks/useDashBoard"; | ||||
| import useNavigateCustom from "hooks/useNavigateCustom"; | import useNavigateCustom from "hooks/useNavigateCustom"; | ||||
| import { PageID, TabID } from "pages"; | import { PageID, TabID } from "pages"; | ||||
| import { useEffect } from "react"; | import { useEffect } from "react"; | ||||
| import { getPath } from "routes/path"; | 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() { | export default function ContractList() { | ||||
| const { setHeaderTitle, setTabs } = useDashboard( | const { setHeaderTitle, setTabs } = useDashboard( | ||||
| PageID.DASHBOARD_CONTRACT_LIST, | |||||
| PageID.DASHBOARD_SEASON_TICKET_CONTRACT_LIST, | |||||
| TabID.NONE | TabID.NONE | ||||
| ); | ); | ||||
| const { navigateWhenChanged } = useNavigateCustom(); | |||||
| const { seasonTicketContracts } = useSeasonTicketContractContext(); | |||||
| const items = [ | |||||
| { parkingName: "A駐車場", position: "1" }, | |||||
| { parkingName: "B駐車場", position: "1-1" }, | |||||
| ]; | |||||
| const { navigateWhenChanged } = useNavigateCustom(); | |||||
| const moveToDetail = () => { | const moveToDetail = () => { | ||||
| navigateWhenChanged( | navigateWhenChanged( | ||||
| getPath(PageID.DASHBOARD_CONTRACT_DETAIL, { | |||||
| getPath(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL, { | |||||
| query: { | query: { | ||||
| id: "test-test", | id: "test-test", | ||||
| }, | }, | ||||
| @@ -35,13 +53,10 @@ export default function ContractList() { | |||||
| return ( | return ( | ||||
| <Box sx={{ p: 1, m: 1 }}> | <Box sx={{ p: 1, m: 1 }}> | ||||
| <Grid container spacing={2}> | <Grid container spacing={2}> | ||||
| {items.map((item, index) => { | |||||
| {seasonTicketContracts.map((item, index) => { | |||||
| return ( | return ( | ||||
| <Grid item xs={12} md={6} key={index}> | <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> | </Grid> | ||||
| ); | ); | ||||
| })} | })} | ||||
| @@ -1,12 +1,11 @@ | |||||
| import { Button, Stack } from "@mui/material"; | import { Button, Stack } from "@mui/material"; | ||||
| import StackRow from "components/stack/StackRow"; | |||||
| import useDashboard from "hooks/useDashBoard"; | import useDashboard from "hooks/useDashBoard"; | ||||
| import { PageID, TabID } from "pages"; | import { PageID, TabID } from "pages"; | ||||
| import { useEffect } from "react"; | import { useEffect } from "react"; | ||||
| export default function UserDetail() { | export default function UserDetail() { | ||||
| const { setHeaderTitle, setTabs } = useDashboard( | const { setHeaderTitle, setTabs } = useDashboard( | ||||
| PageID.DASHBOARD_CONTRACT_DETAIL, | |||||
| PageID.DASHBOARD_USER_DETAIL, | |||||
| TabID.NONE | TabID.NONE | ||||
| ); | ); | ||||
| @@ -7,9 +7,9 @@ export const PageID = { | |||||
| DASHBOARD_OVERVIEW: id++, | 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++, | DASHBOARD_RECEIPT_DOWNLOAD: id++, | ||||
| @@ -41,7 +41,7 @@ export function Routes() { | |||||
| return useRoutes([ | return useRoutes([ | ||||
| CommonRoutes(), | CommonRoutes(), | ||||
| AuthRoutes(), | AuthRoutes(), | ||||
| DashboardRoutes(), | |||||
| ...DashboardRoutes(), | |||||
| { | { | ||||
| path: "403", | path: "403", | ||||
| element: <Page403 />, | element: <Page403 />, | ||||
| @@ -31,10 +31,11 @@ const getTabId = (key: PathKey): TabID => { | |||||
| const PATHS_DASHBOARD = { | const PATHS_DASHBOARD = { | ||||
| [makePathKey(PageID.DASHBOARD_OVERVIEW)]: "/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", | "/dashboard/contract/list/:page", | ||||
| [makePathKey(PageID.DASHBOARD_CONTRACT_DETAIL)]: | |||||
| [makePathKey(PageID.DASHBOARD_SEASON_TICKET_CONTRACT_DETAIL)]: | |||||
| "/dashboard/contract/detail/:id", | "/dashboard/contract/detail/:id", | ||||
| [makePathKey(PageID.DASHBOARD_RECEIPT_DOWNLOAD)]: | [makePathKey(PageID.DASHBOARD_RECEIPT_DOWNLOAD)]: | ||||
| "/dashboard/receipt/download", | "/dashboard/receipt/download", | ||||
| @@ -1,3 +1,4 @@ | |||||
| import { SeasonTicketContractContextProvider } from "contexts/dashboard/SeasonTicketContractContext"; | |||||
| import useAuth from "hooks/useAuth"; | import useAuth from "hooks/useAuth"; | ||||
| import DashboardLayout from "layouts/dashbord"; | import DashboardLayout from "layouts/dashbord"; | ||||
| import { PageID } from "pages"; | import { PageID } from "pages"; | ||||
| @@ -6,7 +7,7 @@ import { RouteObject } from "react-router-dom"; | |||||
| import { Loadable } from "routes"; | import { Loadable } from "routes"; | ||||
| import { getRoute } from "routes/path"; | import { getRoute } from "routes/path"; | ||||
| export default function DashboardRoutes(): RouteObject { | |||||
| export default function DashboardRoutes(): RouteObject[] { | |||||
| const { authenticated } = useAuth(); | const { authenticated } = useAuth(); | ||||
| const children: RouteObject[] = useMemo(() => { | const children: RouteObject[] = useMemo(() => { | ||||
| @@ -16,12 +17,6 @@ export default function DashboardRoutes(): RouteObject { | |||||
| const ContractEntry = Loadable( | const ContractEntry = Loadable( | ||||
| lazy(() => import("pages/dashboard/contract/entry")) | 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( | const ReceiptDownload = Loadable( | ||||
| lazy(() => import("pages/dashboard/receipt/download")) | lazy(() => import("pages/dashboard/receipt/download")) | ||||
| ); | ); | ||||
| @@ -36,17 +31,9 @@ export default function DashboardRoutes(): RouteObject { | |||||
| element: <Dashboard />, | element: <Dashboard />, | ||||
| }, | }, | ||||
| { | { | ||||
| pageId: PageID.DASHBOARD_CONTRACT_ENTRY, | |||||
| pageId: PageID.DASHBOARD_SEASON_TICKET_CONTRACT_ENTRY, | |||||
| element: <ContractEntry />, | element: <ContractEntry />, | ||||
| }, | }, | ||||
| { | |||||
| pageId: PageID.DASHBOARD_CONTRACT_LIST, | |||||
| element: <ContractList />, | |||||
| }, | |||||
| { | |||||
| pageId: PageID.DASHBOARD_CONTRACT_DETAIL, | |||||
| element: <ContractDetail />, | |||||
| }, | |||||
| { | { | ||||
| pageId: PageID.DASHBOARD_RECEIPT_DOWNLOAD, | pageId: PageID.DASHBOARD_RECEIPT_DOWNLOAD, | ||||
| element: <ReceiptDownload />, | element: <ReceiptDownload />, | ||||
| @@ -66,8 +53,44 @@ export default function DashboardRoutes(): RouteObject { | |||||
| })); | })); | ||||
| }, [authenticated]); | }, [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, | |||||
| }, | |||||
| ]; | |||||
| } | } | ||||