Skip to content

Commit

Permalink
Merge pull request #10 from mjaakko/search_trains_by_route
Browse files Browse the repository at this point in the history
Search trains by route
  • Loading branch information
mjaakko authored Jun 2, 2020
2 parents 6a3ce16 + 0cf59ea commit e783960
Show file tree
Hide file tree
Showing 18 changed files with 475 additions and 54 deletions.
6 changes: 4 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next';
import FrontPage, { MapContextProvider } from './components/pages/FrontPage';
import Station from './components/pages/Station';
import Train from './components/pages/Train';
import SearchTrain from './components/pages/SearchTrain';
import SearchTrain from './components/SearchTrain';

import DocumentTitle from './components/DocumentTitle';
import Header from './components/Header';
Expand Down Expand Up @@ -84,7 +84,9 @@ export default () => {
<Route path="/train/:trainNumber/:departureDate">
<Train />
</Route>
<Route path="/searchtrain">
<Route
path={['/searchtrainbynumber', '/searchtrainbyroute']}
>
<SearchTrain />
</Route>
<Route path="/">
Expand Down
55 changes: 55 additions & 0 deletions src/components/SearchTrain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import { useHistory, useRouteMatch } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Tab, Container, Header } from 'semantic-ui-react';

import SearchTrainByRoute from './pages/SearchTrainByRoute';
import SearchTrainByNumber from './pages/SearchTrainByNumber';

import DocumentTitle from './DocumentTitle';

const tabs = t => [
{
menuItem: t('searchTrains.byNumber'),
render: () => <SearchTrainByNumber />,
},
{
menuItem: t('searchTrains.byRoute'),
render: () => <SearchTrainByRoute />,
},
];

const SearchTrain = () => {
const { t } = useTranslation();

const history = useHistory();

const byNumber = useRouteMatch('/searchtrainbynumber');
const byRoute = useRouteMatch('/searchtrainbyroute');

const onTabChange = (_, { activeIndex: index }) => {
if (index === 0) {
history.push('/searchtrainbynumber');
} else if (index === 1) {
history.push('/searchtrainbyroute');
}
};

const activeIndex = byNumber ? 0 : byRoute ? 1 : -1;

return (
<>
<DocumentTitle title={t('searchTrains.search')} />
<Container as="main">
<Header as="h1">{t('searchTrains.search')}</Header>
<Tab
panes={tabs(t)}
onTabChange={onTabChange}
activeIndex={activeIndex}
/>
</Container>
</>
);
};

export default SearchTrain;
2 changes: 1 addition & 1 deletion src/components/SidebarMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const SidebarMenu = ({ hideSidebar }) => {
<Menu.Item
key="searchtrains"
as={Link}
to="/searchtrain"
to="/searchtrainbynumber"
onClick={hideSidebar}
>
<Icon name="search" />
Expand Down
11 changes: 2 additions & 9 deletions src/components/TrainTime.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import { useTranslation } from 'react-i18next';
import { MetadataContext } from '../App';

import { formatTime } from '../utils/format';
import timezones from '../utils/timezones';

import './TrainTime.css';

const MINOR_DELAY = 2;
const MAJOR_DELAY = 15;

const TIMEZONE_FINLAND = 'Europe/Helsinki';
const TIMEZONE_RUSSIA = 'Europe/Moscow'; //In Russia trains run on Moscow time

const generateCausesString = (
detailedCauseCodes,
detailedCauses,
Expand Down Expand Up @@ -106,12 +104,7 @@ export default ({ timetableRow }) => {
}`}
dateTime={time}
>
{formatTime(
time,
timetableRow.countryCode === 'RU'
? TIMEZONE_RUSSIA
: TIMEZONE_FINLAND
)}
{formatTime(time, timezones[timetableRow.countryCode])}
{timetableRow.unknownDelay && '\u00a0?'}
</time>
}
Expand Down
3 changes: 0 additions & 3 deletions src/components/pages/SearchTrain/index.js

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import React, { useState } from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Container, Header, Form, Divider } from 'semantic-ui-react';
import { Container, Form, Divider } from 'semantic-ui-react';
import SemanticDatepicker from 'react-semantic-ui-datepickers';
import 'react-semantic-ui-datepickers/dist/react-semantic-ui-datepickers.css';
import moment from 'moment';

import DocumentTitle from '../../DocumentTitle';
import TrainResults from './TrainResults';

const validateDate = date => {
Expand Down Expand Up @@ -50,42 +49,38 @@ const SearchTrain = () => {
);

return (
<>
<DocumentTitle title={t('searchTrains.search')} />
<Container as="main">
<Header as="h1">{t('searchTrains.search')}</Header>
<Form onSubmit={onSubmit}>
<Form.Input
label={t('searchTrains.trainNumber')}
value={trainNumber}
required
onChange={onChangeTrainNumber}
style={{ width: '15rem' }}
<Container style={{ marginTop: '1rem' }}>
<Form onSubmit={onSubmit}>
<Form.Input
label={t('searchTrainsByNumber.trainNumber')}
value={trainNumber}
required
onChange={onChangeTrainNumber}
style={{ width: '15rem' }}
/>
<SemanticDatepicker
size="small"
locale={i18n.language}
label={t('searchTrainsByNumber.departureDate')}
value={new Date(departureDate)}
date={new Date(departureDate)}
required
firstDayOfWeek={moment.localeData().firstDayOfWeek()}
onChange={onChangeDepartureDate}
style={{ width: '15rem' }}
/>
<Form.Button>{t('searchTrains.search')}</Form.Button>
</Form>
{queryParams.has('trainNumber') && queryParams.has('departureDate') && (
<>
<Divider section />
<TrainResults
trainNumber={queryParams.get('trainNumber')}
departureDate={queryParams.get('departureDate')}
/>
<SemanticDatepicker
size="small"
locale={i18n.language}
label={t('searchTrains.departureDate')}
value={new Date(departureDate)}
date={new Date(departureDate)}
required
firstDayOfWeek={moment.localeData().firstDayOfWeek()}
onChange={onChangeDepartureDate}
style={{ width: '15rem' }}
/>
<Form.Button>{t('searchTrains.search')}</Form.Button>
</Form>
{queryParams.has('trainNumber') && queryParams.has('departureDate') && (
<>
<Divider section />
<TrainResults
trainNumber={queryParams.get('trainNumber')}
departureDate={queryParams.get('departureDate')}
/>
</>
)}
</Container>
</>
</>
)}
</Container>
);
};

Expand Down
3 changes: 3 additions & 0 deletions src/components/pages/SearchTrainByNumber/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import SearchTrainByNumber from './SearchTrainByNumber';

export default SearchTrainByNumber;
128 changes: 128 additions & 0 deletions src/components/pages/SearchTrainByRoute/SearchTrainByRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import React, { useState, useContext } from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import { useTranslation } from 'react-i18next';
import { Container, Form, Divider } from 'semantic-ui-react';
import SemanticDatepicker from 'react-semantic-ui-datepickers';
import 'react-semantic-ui-datepickers/dist/react-semantic-ui-datepickers.css';
import moment from 'moment';

import { MetadataContext } from '../../../App';

import TrainResults from './TrainResults';
import { formatStationName } from '../../../utils/format';

const validateDate = date => {
return !isNaN(new Date(date).getTime());
};

const toISODate = date =>
`${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
2,
'0'
)}-${String(date.getDate()).padStart(2, '0')}`;

const SearchTrainByRoute = () => {
const { stations } = useContext(MetadataContext);

const { t, i18n } = useTranslation();

const history = useHistory();

const { search } = useLocation();
const queryParams = new URLSearchParams(search);

const [fromStation, setFromStation] = useState(
queryParams.has('fromStation') ? queryParams.get('fromStation') : ''
);
const [toStation, setToStation] = useState(
queryParams.has('toStation') ? queryParams.get('toStation') : ''
);
const [date, setDate] = useState(
queryParams.has('departureDate') &&
validateDate(queryParams.get('departureDate'))
? queryParams.get('departureDate')
: toISODate(new Date())
);

const onChangeFromStation = (_, { value }) => setFromStation(value);
const onChangeToStation = (_, { value }) => setToStation(value);
const onChangeDate = (_, { value }) => {
if (value) {
setDate(toISODate(value));
}
};

if (!stations) {
return null;
}

const onSubmit = () => {
history.replace(
`?fromStation=${fromStation}&toStation=${toStation}&date=${date}`
);
};

const options = Array.from(stations.values())
.filter(station => station.passengerTraffic)
.map(station => ({
text: formatStationName(station.stationName),
value: station.stationShortCode,
}));

return (
<Container style={{ marginTop: '1rem' }}>
<Form onSubmit={onSubmit}>
<Form.Group widths={4}>
<Form.Select
className="stationdropdown"
options={options}
placeholder={t('searchTrainsByRoute.from')}
label={t('searchTrainsByRoute.from')}
noResultsMessage={t('common.noResults')}
value={fromStation}
search
required
onChange={onChangeFromStation}
/>
<Form.Select
className="stationdropdown"
options={options}
placeholder={t('searchTrainsByRoute.to')}
label={t('searchTrainsByRoute.to')}
noResultsMessage={t('common.noResults')}
value={toStation}
search
required
onChange={onChangeToStation}
/>
</Form.Group>
<SemanticDatepicker
size="small"
locale={i18n.language}
label={t('searchTrains.departureDate')}
value={new Date(date)}
date={new Date(date)}
required
firstDayOfWeek={moment.localeData().firstDayOfWeek()}
onChange={onChangeDate}
style={{ width: '15rem' }}
/>
<Form.Button>{t('searchTrains.search')}</Form.Button>
</Form>
{queryParams.has('fromStation') &&
queryParams.has('toStation') &&
queryParams.has('date') && (
<>
<Divider section />
<TrainResults
fromStation={queryParams.get('fromStation')}
toStation={queryParams.get('toStation')}
date={queryParams.get('date')}
/>
</>
)}
</Container>
);
};

export default SearchTrainByRoute;
Loading

0 comments on commit e783960

Please sign in to comment.