Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create research route #307

Merged
merged 9 commits into from
Dec 16, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
227 changes: 227 additions & 0 deletions src/pages/DataResearch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
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'

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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should use here useTranslation and not Hebrew

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's a temporary internal thing, I don't think it should ever be translated

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I understand

<p>אם יש לכם רעיון מעניין למה קורים פה דברים, דברו איתנו בסלאק!</p>
</Widget>
<StackedResearchSection />
</PageContainer>
)
}

function StackedResearchSection() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should move it above the DataResearch component

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I wrote in different comment you don't have to

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>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here and in some more places in this file , pls use useTranslation

<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({
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should move it above the StackedResearchSection component

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you don't have to, but when I read the file from above to down you use functions and then declare them. However, you use the component in different file so it is not give an errors, up to you.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually, I prefer putting helper function and small utilities at the bottom, so when you read it you'll see the important things first.
But it's just an opinion
https://softwareengineering.stackexchange.com/questions/196997/helper-methods-placement

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok 👍

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)}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: It is better not to allow to choose an end date earlier than the start date

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you're right, but since it's an internal endpoint, it's less important here.
However, it is a great idea to open an issue to fix that on the front page

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>
)}
</>
)
}
5 changes: 3 additions & 2 deletions src/routes/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
LineChartOutlined,
} from '@ant-design/icons'
import { MainRoute } from './MainRoute'
import { DataResearch } from 'src/pages/DataResearch'
NoamGaash marked this conversation as resolved.
Show resolved Hide resolved

export const PAGES = [
{
Expand Down Expand Up @@ -104,8 +105,8 @@ const getRoutesList = () => {
return gtfs_route
}}
/>
,
<Route path="*" element={<RedirectToDashboard />} key="back" />,
<Route path="data-research" element={<DataResearch />} />
ArkadiK94 marked this conversation as resolved.
Show resolved Hide resolved
<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
Loading