From c6fca1a38a6308e2db1c14d75cb7b3e77858de51 Mon Sep 17 00:00:00 2001 From: erdar2 Date: Mon, 27 Dec 2021 11:03:01 +0100 Subject: [PATCH] feat: implement multiple charts on multiple sites --- package.json | 4 + resources/js/components/App.tsx | 116 ++++---------------- resources/js/components/AreaChart.tsx | 45 ++++++++ resources/js/components/DatePicker.tsx | 31 ++++++ resources/js/components/Home.tsx | 131 +++++++++++++++++++++++ resources/js/components/LineChart.tsx | 45 ++++++++ resources/js/components/PieChart.tsx | 48 +++++++++ resources/js/components/RecoveryRate.tsx | 114 ++++++++++++++++++++ resources/js/services/covidDataApi.ts | 10 +- resources/js/types.ts | 31 ++++++ 10 files changed, 471 insertions(+), 104 deletions(-) create mode 100644 resources/js/components/AreaChart.tsx create mode 100644 resources/js/components/DatePicker.tsx create mode 100644 resources/js/components/Home.tsx create mode 100644 resources/js/components/LineChart.tsx create mode 100644 resources/js/components/PieChart.tsx create mode 100644 resources/js/components/RecoveryRate.tsx create mode 100644 resources/js/types.ts diff --git a/package.json b/package.json index 9ca785f..a7692db 100644 --- a/package.json +++ b/package.json @@ -27,15 +27,19 @@ "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@mui/icons-material": "^5.2.5", + "@mui/lab": "^5.0.0-alpha.61", "@mui/material": "^5.2.5", "@reduxjs/toolkit": "^1.7.1", "@rtk-query/graphql-request-base-query": "^1.0.3", "@types/react": "^17.0.37", "@types/react-dom": "^17.0.11", "@types/react-redux": "^7.1.20", + "@types/recharts": "^1.8.23", + "date-fns": "^2.27.0", "graphql-request": "^3.7.0", "react-redux": "^7.2.6", "react-router-dom": "^6.2.1", + "recharts": "^2.1.8", "ts-loader": "^9.2.6", "typescript": "^4.5.4" } diff --git a/resources/js/components/App.tsx b/resources/js/components/App.tsx index 26e2689..aa70619 100644 --- a/resources/js/components/App.tsx +++ b/resources/js/components/App.tsx @@ -15,10 +15,12 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import ListItem from "@mui/material/ListItem"; import ListItemIcon from "@mui/material/ListItemIcon"; import ListItemText from "@mui/material/ListItemText"; -import InboxIcon from "@mui/icons-material/MoveToInbox"; -import MailIcon from "@mui/icons-material/Mail"; +import HomeIcon from "@mui/icons-material/Home"; +import TrendingUpIcon from '@mui/icons-material/TrendingUp'; import { Routes, Route, Link } from "react-router-dom"; import Paper from "@mui/material/Paper"; +import Home from "./Home"; +import RecoveryRate from "./RecoveryRate"; const drawerWidth = 240; @@ -93,8 +95,8 @@ const Drawer = styled(MuiDrawer, { const Container = styled(Paper)(({ theme }) => ({ ...theme.typography.body2, - padding: 16 - })); + padding: 16, +})); export default function App() { const theme = useTheme(); @@ -142,20 +144,18 @@ export default function App() { - {["Inbox", "Starred", "Send email", "Drafts"].map( - (text, index) => ( - - - {index % 2 === 0 ? ( - - ) : ( - - )} - - - - ) - )} + + + + + + + + + + + + @@ -164,85 +164,11 @@ export default function App() { - - Lorem ipsum dolor sit amet, consectetur - adipiscing elit, sed do eiusmod tempor - incididunt ut labore et dolore magna - aliqua. Rhoncus dolor purus non enim - praesent elementum facilisis leo vel. - Risus at ultrices mi tempus imperdiet. - Semper risus in hendrerit gravida rutrum - quisque non tellus. Convallis convallis - tellus id interdum velit laoreet id - donec ultrices. Odio morbi quis commodo - odio aenean sed adipiscing. Amet nisl - suscipit adipiscing bibendum est - ultricies integer quis. Cursus euismod - quis viverra nibh cras. Metus vulputate - eu scelerisque felis imperdiet proin - fermentum leo. Mauris commodo quis - imperdiet massa tincidunt. Cras - tincidunt lobortis feugiat vivamus at - augue. At augue eget arcu dictum varius - duis at consectetur lorem. Velit sed - ullamcorper morbi tincidunt. Lorem donec - massa sapien faucibus et molestie ac. - - - Consequat mauris nunc congue nisi vitae - suscipit. Fringilla est ullamcorper eget - nulla facilisi etiam dignissim diam. - Pulvinar elementum integer enim neque - volutpat ac tincidunt. Ornare - suspendisse sed nisi lacus sed viverra - tellus. Purus sit amet volutpat - consequat mauris. Elementum eu facilisis - sed odio morbi. Euismod lacinia at quis - risus sed vulputate odio. Morbi - tincidunt ornare massa eget egestas - purus viverra accumsan in. In hendrerit - gravida rutrum quisque non tellus orci - ac. Pellentesque nec nam aliquam sem et - tortor. Habitant morbi tristique - senectus et. Adipiscing elit duis - tristique sollicitudin nibh sit. Ornare - aenean euismod elementum nisi quis - eleifend. Commodo viverra maecenas - accumsan lacus vel facilisis. Nulla - posuere sollicitudin aliquam ultrices - sagittis orci a. - - - } + element={} /> - Consequat mauris nunc congue nisi vitae - suscipit. Fringilla est ullamcorper eget - nulla facilisi etiam dignissim diam. - Pulvinar elementum integer enim neque - volutpat ac tincidunt. Ornare suspendisse - sed nisi lacus sed viverra tellus. Purus sit - amet volutpat consequat mauris. Elementum eu - facilisis sed odio morbi. Euismod lacinia at - quis risus sed vulputate odio. Morbi - tincidunt ornare massa eget egestas purus - viverra accumsan in. In hendrerit gravida - rutrum quisque non tellus orci ac. - Pellentesque nec nam aliquam sem et tortor. - Habitant morbi tristique senectus et. - Adipiscing elit duis tristique sollicitudin - nibh sit. Ornare aenean euismod elementum - nisi quis eleifend. Commodo viverra maecenas - accumsan lacus vel facilisis. Nulla posuere - sollicitudin aliquam ultrices sagittis orci - a. - - } + path="/gyogyulas-halalozas" + element={} /> diff --git a/resources/js/components/AreaChart.tsx b/resources/js/components/AreaChart.tsx new file mode 100644 index 0000000..d56c9c1 --- /dev/null +++ b/resources/js/components/AreaChart.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { + ResponsiveContainer, + AreaChart as AC, + CartesianGrid, + XAxis, + YAxis, + Tooltip, + Legend, + Area, +} from "recharts"; +import { ChartProps } from "../types"; + +export default function AreaChart(props: ChartProps) { + return ( + + { + return { + name: d.name, + ...d.values, + }; + })} + > + {Object.keys(props.data[0].values).map((k, i) => ( + + ))} + + + + + + + + ); +} diff --git a/resources/js/components/DatePicker.tsx b/resources/js/components/DatePicker.tsx new file mode 100644 index 0000000..9d5d123 --- /dev/null +++ b/resources/js/components/DatePicker.tsx @@ -0,0 +1,31 @@ +import React from "react"; +import TextField from "@mui/material/TextField"; +import AdapterDateFns from "@mui/lab/AdapterDateFns"; +import LocalizationProvider from "@mui/lab/LocalizationProvider"; +import DateRangePicker, { DateRange } from "@mui/lab/DateRangePicker"; +import Box from "@mui/material/Box"; +import { DatePickerProps } from "../types"; + +export default function DatePicker(props: DatePickerProps) { + return ( + + ) => { + props.setValue(newValue); + }} + startText="Kezdeti dátum" + endText="Végső dátum" + renderInput={(startProps, endProps) => ( + <> + + - + + + )} + /> + + ); +} diff --git a/resources/js/components/Home.tsx b/resources/js/components/Home.tsx new file mode 100644 index 0000000..262680c --- /dev/null +++ b/resources/js/components/Home.tsx @@ -0,0 +1,131 @@ +import React from "react"; +import AreaChart from "./AreaChart"; +import Typography from "@mui/material/Typography"; +import Grid from "@mui/material/Grid"; +import { useGetAllDataQuery } from "../services/covidDataApi"; +import { ChartData } from "../types"; +import { DateRange } from "@mui/lab/DateRangePicker"; +import DatePicker from "./DatePicker"; + +export default function Home() { + const { data, error, isLoading } = useGetAllDataQuery({}); + const [dateRange, setDateRange] = React.useState>([ + null, + null, + ]); + + let allData: ChartData[] = []; + let newInfected: ChartData[] = []; + + if (data) { + if (dateRange[0] == null && dateRange[1] == null) { + setDateRange([ + new Date(data[0].created_at), + new Date(data[data.length - 1].created_at), + ]); + } + + allData = data + .map((d) => { + const date = new Date(d.created_at); + const currentInfected = d.infected - d.recovered - d.deceased; + return { + name: + date.getFullYear() + + "." + + ("0" + (date.getMonth() + 1)).slice(-2) + + "." + + ("0" + date.getDate()).slice(-2), + values: { + "Aktív fertőzöttek napi alakulása": currentInfected > 0 ? currentInfected : 0, + }, + }; + }) + .filter((d) => { + const ddate = new Date(d.name); + if (dateRange[0] == null || dateRange[1] == null) return true; + let endDate = new Date(dateRange[1].getTime()); + endDate.setDate(endDate.getDate() + 1); + return ddate >= dateRange[0] && ddate < endDate; + }); + + let newData = 0; + newInfected = data + .map((d) => { + const date = new Date(d.created_at); + const newActual = d.infected - newData; + newData = d.infected; + return { + name: + date.getFullYear() + + "." + + ("0" + (date.getMonth() + 1)).slice(-2) + + "." + + ("0" + date.getDate()).slice(-2), + values: { + "Új fertőzöttek napi alakulása": newActual > 0 ? newActual : 0, + }, + }; + }) + .filter((d) => { + const ddate = new Date(d.name); + if (dateRange[0] == null || dateRange[1] == null) return true; + let endDate = new Date(dateRange[1].getTime()); + endDate.setDate(endDate.getDate() + 1); + return ddate >= dateRange[0] && ddate < endDate; + }); + } + + return ( + <> + + Ez az oldal elsősorban a hivatalos, kormány által közölt + adatokat dolgozza fel, de nem tekinthető hivatalos + tájékoztatásnak, továbbá nem vállalunk felelősséget a kormány + által közölt adatok és információk valóságtartalmáért. Kérjük, a + hivatalos információkért látogasson el a kormány által + működtetett{" "} + + koronavirus.gov.hu + {" "} + oldalra, illetve olvassa a nagyobb hírportálok híreit. + + {typeof data != "undefined" ? ( + + + + ) => + setDateRange(n) + } + /> + + + + + + + + + + ) : null} + + ); +} diff --git a/resources/js/components/LineChart.tsx b/resources/js/components/LineChart.tsx new file mode 100644 index 0000000..5a28020 --- /dev/null +++ b/resources/js/components/LineChart.tsx @@ -0,0 +1,45 @@ +import React from "react"; +import { + ResponsiveContainer, + LineChart as LC, + CartesianGrid, + XAxis, + YAxis, + Tooltip, + Legend, + Line, +} from "recharts"; +import { ChartProps } from "../types"; + +export default function LineChart(props: ChartProps) { + return ( + + { + return { + name: d.name, + ...d.values, + }; + })} + > + {Object.keys(props.data[0].values).map((k, i) => ( + + ))} + + + + + + + + ); +} diff --git a/resources/js/components/PieChart.tsx b/resources/js/components/PieChart.tsx new file mode 100644 index 0000000..59e4722 --- /dev/null +++ b/resources/js/components/PieChart.tsx @@ -0,0 +1,48 @@ +import React from "react"; +import { + PieChart as PC, + Pie, + Legend, + Tooltip, + ResponsiveContainer, + Cell, +} from "recharts"; +import { ChartProps } from "../types"; + +const colors = ["#82ca9d", "#8884d8"]; + +export default function PieChart(props: ChartProps) { + return ( + + + ({ + name: k, + value: props.data[props.data.length - 1].values[k], + }))} + innerRadius={40} + outerRadius={80} + fill="#82ca9d" + label + > + {Object.keys(props.data[props.data.length - 1].values) + .map((k) => ({ + name: k, + value: props.data[props.data.length - 1].values[k], + })) + .map((entry, index) => ( + + ))} + + + + + + ); +} diff --git a/resources/js/components/RecoveryRate.tsx b/resources/js/components/RecoveryRate.tsx new file mode 100644 index 0000000..77c2035 --- /dev/null +++ b/resources/js/components/RecoveryRate.tsx @@ -0,0 +1,114 @@ +import React from "react"; +import Typography from "@mui/material/Typography"; +import Grid from "@mui/material/Grid"; +import { useGetAllDataQuery } from "../services/covidDataApi"; +import { ChartData } from "../types"; +import { DateRange } from "@mui/lab/DateRangePicker"; +import DatePicker from "./DatePicker"; +import LineChart from "./LineChart"; +import PieChart from "./PieChart"; + +export default function RecoveryRate() { + const { data, error, isLoading } = useGetAllDataQuery({}); + const [dateRange, setDateRange] = React.useState>([ + null, + null, + ]); + + let lineData: ChartData[] = []; + let pieData: ChartData[] = []; + + if (data) { + if (dateRange[0] == null && dateRange[1] == null) { + setDateRange([ + new Date(data[0].created_at), + new Date(data[data.length - 1].created_at), + ]); + } + + lineData = data + .map((d) => { + const date = new Date(d.created_at); + return { + name: + date.getFullYear() + + "." + + ("0" + (date.getMonth() + 1)).slice(-2) + + "." + + ("0" + date.getDate()).slice(-2), + values: { + "Gyógyultak száma": d.recovered ?? 0, + "Elhalálozottak száma": d.deceased ?? 0, + }, + }; + }) + .filter((d) => { + const ddate = new Date(d.name); + if (dateRange[0] == null || dateRange[1] == null) return true; + let endDate = new Date(dateRange[1].getTime()); + endDate.setDate(endDate.getDate() + 1); + return ddate >= dateRange[0] && ddate < endDate; + }); + + pieData = data + .filter((n) => { + const ddate = new Date(n.created_at); + if (dateRange[0] == null || dateRange[1] == null) return true; + let endDate = new Date(dateRange[1].getTime()); + endDate.setDate(endDate.getDate() + 1); + return ddate >= dateRange[0] && ddate < endDate; + }) + .map((d) => { + const date = new Date(d.created_at); + return { + name: + date.getFullYear() + + "." + + ("0" + (date.getMonth() + 1)).slice(-2) + + "." + + ("0" + date.getDate()).slice(-2), + values: { + Gyógyultak: d.recovered ?? 0, + Elhalálozottak: d.deceased ?? 0, + }, + }; + }); + } + return ( + <> + + A gyógyultak és az elhalálozottak számának alakulása + + {typeof data != "undefined" ? ( + + + + ) => + setDateRange(n) + } + /> + + + + + + + + + + ) : null} + + ); +} diff --git a/resources/js/services/covidDataApi.ts b/resources/js/services/covidDataApi.ts index 5e329a8..a489c45 100644 --- a/resources/js/services/covidDataApi.ts +++ b/resources/js/services/covidDataApi.ts @@ -1,15 +1,7 @@ import { createApi } from "@reduxjs/toolkit/query/react"; import { gql } from "graphql-request"; import { graphqlRequestBaseQuery } from "@rtk-query/graphql-request-base-query"; - -export interface CovidData { - infected: number; - deceased: number; - recovered: number; - quarantined: number; - tested: number; - created_at: Date; -} +import { CovidData } from "../types"; export interface GetAllDataResponse { allData: CovidData[]; diff --git a/resources/js/types.ts b/resources/js/types.ts new file mode 100644 index 0000000..ae26bea --- /dev/null +++ b/resources/js/types.ts @@ -0,0 +1,31 @@ +import { DateRange } from "@mui/lab/DateRangePicker"; +import { ScaleType } from "recharts/types/util/types"; + + +export interface CovidData { + infected: number; + deceased: number; + recovered: number; + quarantined: number; + tested: number; + created_at: Date; +} + +export interface ChartData { + name: string; + values: { + [key: string]: number + }; +} + +export interface ChartProps { + data: ChartData[]; + scale?: ScaleType; +} + +export interface DatePickerProps { + minDate: Date; + maxDate: Date; + value: DateRange; + setValue: Function; +}