Skip to content

Commit

Permalink
feat: create research route (hasadna#307)
Browse files Browse the repository at this point in the history
  • Loading branch information
NoamGaash authored Dec 16, 2023
1 parent 264653b commit 244b222
Show file tree
Hide file tree
Showing 6 changed files with 246 additions and 4 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ We've hidden a couple of fun surprises in our web app, just for you. Discovering
Watch the magic unfold as you type "storybook" on your keyboard. You might just stumble upon our Storybook, a treasure trove of UI components showcasing the beauty and functionality of our app.
- **Type "english":**
Feel like switching up the language? Type "english" and see the language toggle in action. Our app is multilingual, and you can experience it by triggering this secret command.
- **Type "geek":**
To get some experimental charts with some additional data and aggregation


## deployments
Expand Down
7 changes: 6 additions & 1 deletion src/layout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import MainHeader from './header/Header'
import SideBar from './sidebar/SideBar'
import styled from 'styled-components'
import LayoutContext from './LayoutContext'
import { Outlet } from 'react-router-dom'
import { Link, Outlet } from 'react-router-dom'
import { Suspense } from 'react'
import Preloader from 'src/shared/Preloader'
import { EasterEgg } from 'src/pages/EasterEgg/EasterEgg'
Expand Down Expand Up @@ -39,6 +39,11 @@ export function MainLayout() {
<Envelope />
</a>
</EasterEgg>
<EasterEgg code="geek">
<Link to="/data-research">
<Envelope />
</Link>
</EasterEgg>
</Suspense>
</StyledBody>
</StyledContent>
Expand Down
3 changes: 3 additions & 0 deletions src/pages/DataResearch/DataResearch.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.recharts-tooltip-wrapper {
z-index: 1;
}
229 changes: 229 additions & 0 deletions src/pages/DataResearch/DataResearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
import React, { useMemo } from 'react'
import { useGroupBy } from 'src/api/groupByService'
import Widget from 'src/shared/Widget'
import { useDate } from '../components/DateTimePicker'
import moment from 'moment'
import { Grid } from '@mui/material'
import { DateSelector } from '../components/DateSelector'
import { useTranslation } from 'react-i18next'
import { Skeleton } from 'antd'
import {
Area,
Tooltip,
AreaChart,
CartesianGrid,
ResponsiveContainer,
XAxis,
YAxis,
} from 'recharts'
import { PageContainer } from '../components/PageContainer'
import { getColorName } from '../dashboard/AllLineschart/OperatorHbarChart/OperatorHbarChart'

import './DataResearch.scss'

const now = moment()
const unique: (value: string, index: number, self: string[]) => boolean = (value, index, self) =>
self.indexOf(value) === index

export const DataResearch = () => {
return (
<PageContainer>
<Widget>
<h1>מחקרים</h1>
<p>אם יש לכם רעיון מעניין למה קורים פה דברים, דברו איתנו בסלאק!</p>
</Widget>
<StackedResearchSection />
</PageContainer>
)
}

function StackedResearchSection() {
const [startDate, setStartDate] = useDate(now.clone().subtract(7, 'days'))
const [endDate, setEndDate] = useDate(now.clone().subtract(1, 'day'))
const [groupByHour, setGroupByHour] = React.useState<boolean>(false)
const [graphData, loadingGraph] = useGroupBy({
dateTo: endDate,
dateFrom: startDate,
groupBy: groupByHour ? 'operator_ref,gtfs_route_hour' : 'operator_ref,gtfs_route_date',
})

return (
<Widget>
<h1>בעיות etl/gps/משהו גלובאלי אחר</h1>
<StackedResearchInputs
startDate={startDate}
setStartDate={setStartDate}
endDate={endDate}
setEndDate={setEndDate}
groupByHour={groupByHour}
setGroupByHour={setGroupByHour}
/>
<StackedResearchChart
graphData={graphData}
isLoading={loadingGraph}
field="total_actual_rides"
title="מספר נסיעות בפועל"
description="כמה נסיעות נרשמו כהתבצעו בכל יום/שעה בטווח הזמן שבחרתם. (נסיעות = siri rides)"
/>
<StackedResearchChart
graphData={graphData}
isLoading={loadingGraph}
field="total_planned_rides"
title="מספר נסיעות מתוכננות"
description="כמה נסיעות היו אמורות להיות בכל יום/שעה בטווח הזמן שבחרתם. (נסיעות = נסיעות מתוכננות בgtfs)"
/>
<StackedResearchChart
graphData={graphData}
isLoading={loadingGraph}
field="total_missed_rides"
title="מספר נסיעות שלא התבצעו"
description="כמה נסיעות היו אמורות להיות בכל יום/שעה בטווח הזמן שבחרתם אבל לא התבצעו. (הפרש בין שני הגרפים הקודמים)"
/>
</Widget>
)
}

function StackedResearchInputs({
startDate,
setStartDate,
endDate,
setEndDate,
groupByHour,
setGroupByHour,
}: {
startDate: moment.Moment
setStartDate: (date: moment.Moment) => void
endDate: moment.Moment
setEndDate: (date: moment.Moment) => void
groupByHour: boolean
setGroupByHour: (value: boolean) => void
}) {
const { t } = useTranslation()
return (
<>
<Grid container>
<Grid xs={6} item>
<DateSelector
time={startDate}
onChange={(data) => setStartDate(data)}
customLabel={t('start')}
/>
</Grid>
<Grid xs={6} item>
<DateSelector
time={endDate}
onChange={(data) => setEndDate(data)}
customLabel={t('end')}
/>
</Grid>
</Grid>
<label>
<input
type="checkbox"
checked={groupByHour}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => setGroupByHour(e.target.checked)}
/>
{t('group_by_hour_tooltip_content')}
</label>
</>
)
}

const StackedResearchChart = ({
graphData,
isLoading,
title,
description,
field = 'total_actual_rides',
}: {
graphData: {
gtfs_route_date: string
gtfs_route_hour: string
operator_ref?: {
agency_name?: string
}
total_actual_rides: number
total_planned_rides: number
}[]
isLoading?: boolean
title?: string
description?: string
field?: 'total_actual_rides' | 'total_planned_rides' | 'total_missed_rides'
}) => {
const data = useMemo(
() =>
graphData
.reduce((acc, curr) => {
const val =
field === 'total_missed_rides'
? curr.total_planned_rides - curr.total_actual_rides
: curr[field]
const date = curr.gtfs_route_date ?? curr.gtfs_route_hour
const entry = acc.find((item) => item.date === date)
if (entry) {
if (val) entry[curr.operator_ref?.agency_name || 'Unknown'] = val
} else {
const newEntry = {
date: date,
[curr.operator_ref?.agency_name || 'Unknown']: val,
}
acc.push(newEntry)
}
return acc
}, [] as Record<string, string | number>[])
.sort((a, b) => {
if (a.date > b.date) return 1
if (a.date < b.date) return -1
return 0
}),
[graphData],
)

const operators = graphData
.map((operator) => operator.operator_ref?.agency_name || 'Unknown')
.filter(unique)

return (
<>
{title && <h2>{title}</h2>}
{description && (
<p>
<strong>מה רואים בגרף?</strong>
<br />
{description}
</p>
)}
{isLoading ? (
<Skeleton active />
) : (
<ResponsiveContainer width="100%" height="100%" minHeight="500px">
<AreaChart
width={500}
height={400}
data={data}
margin={{
top: 10,
right: 30,
left: 0,
bottom: 0,
}}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
{operators.map((operator) => (
<Area
type="monotone"
dataKey={operator}
key={operator}
stackId="1"
stroke={getColorName(operator)}
fill={getColorName(operator)}
/>
))}
<Tooltip />
</AreaChart>
</ResponsiveContainer>
)}
</>
)
}
7 changes: 5 additions & 2 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ const SingleLineMapPage = lazy(() => import('../pages/singleLineMap'))
const About = lazy(() => import('../pages/about'))
const Profile = lazy(() => import('../pages/Profile'))
const BugReportForm = lazy(() => import('../pages/BugReportForm '))
const DataResearch = lazy(() =>
import('../pages/DataResearch/DataResearch').then((m) => ({ default: m.DataResearch })),
)

import {
RadarChartOutlined,
Expand Down Expand Up @@ -104,8 +107,8 @@ const getRoutesList = () => {
return gtfs_route
}}
/>
,
<Route path="*" element={<RedirectToDashboard />} key="back" />,
<Route path="data-research" element={<DataResearch />} />
<Route path="*" element={<RedirectToDashboard />} key="back" />
</Route>
// </Suspense>
)
Expand Down
2 changes: 1 addition & 1 deletion src/shared/Widget.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const Widget = (props: { children: React.ReactNode }) => {
return <div className="widget">{props.children}</div>
return <section className="widget">{props.children}</section>
}

export default Widget

0 comments on commit 244b222

Please sign in to comment.