chore: lint

This commit is contained in:
dancingCycle 2024-02-15 22:12:28 +01:00
parent f3be1cd91d
commit 787f525e0f
105 changed files with 10445 additions and 4557 deletions

View File

@ -2,5 +2,13 @@
"presets": [ "presets": [
"@babel/preset-env", "@babel/preset-env",
"@babel/preset-react" "@babel/preset-react"
],
"plugins": [
[
"@babel/plugin-transform-runtime",
{
"regenerator": true
}
]
] ]
} }

9
.eslintrc Normal file
View File

@ -0,0 +1,9 @@
{
"parser": "@babel/eslint-parser",
"extends": ["airbnb"],
"rules": {
"no-tabs": 0,
"no-mixed-spaces-and-tabs": 0
}
}

View File

@ -1,61 +1,50 @@
//generate a HTML5 file including all webpack bundles in the body using script tags // generate a HTML5 file including all webpack bundles in the body using script tags
const HtmlWebpackPlugin = require('html-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin');
//path is used to resolve properly across the OS // path is used to resolve properly across the OS
const path = require('path'); const path = require('path');
module.exports = { module.exports = {
//bundle *.js from this entry point // bundle *.js from this entry point
entry: path.resolve(__dirname, '../src/index.jsx'), entry: path.resolve(__dirname, '../src/index.jsx'),
//create output file to be linked to index.html // create output file to be linked to index.html
output: { output: {
filename: '[name].bundle.js', filename: '[name].bundle.js',
path: path.resolve(__dirname, '../dist'), path: path.resolve(__dirname, '../dist'),
clean: true, clean: true,
}, },
module: { module: {
rules: [ rules: [
{ {
//test all *.js using babel-loader // test all *.js using babel-loader
//test all *.jsx (e.g. React.js) using babel-loader // test all *.jsx (e.g. React.js) using babel-loader
test: /\.(js|jsx)$/, test: /\.(js|jsx)$/,
exclude: /node_modules/, exclude: /node_modules/,
include: path.resolve(__dirname, '../src'), include: path.resolve(__dirname, '../src'),
use: ['babel-loader'], use: {
}, loader: 'babel-loader',
{ options: {
//test all *.css using style-loader and css-loader presets: [
test: /\.css$/i, '@babel/preset-env',
use: ["style-loader", "css-loader"], '@babel/preset-react',
}, ],
{
//test all *.svg using svg-loader
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
limit: 10000,
},
}, },
], },
}, },
{ {
test: /\.(png|jpe?g|gif)$/i, // test all *.css using style-loader and css-loader
use: [ test: /\.css$/i,
{ use: ['style-loader', 'css-loader'],
loader: 'file-loader',
},
],
}, },
]
},
resolve: {
extensions: ['*', '.js', '.jsx'],
},
plugins: [
// create a plugin instance so that you can use it several times anywhere
new HtmlWebpackPlugin({
title: 'Production',
template: path.resolve(__dirname, "../public/index.html")
}),
], ],
},
resolve: {
extensions: ['*', '.js', '.jsx'],
},
plugins: [
// create a plugin instance so that you can use it several times anywhere
new HtmlWebpackPlugin({
title: 'Production',
template: path.resolve(__dirname, '../public/index.html'),
}),
],
}; };

View File

@ -1,16 +1,20 @@
//path is used to resolve properly across the OS // path is used to resolve properly across the OS
const path = require('path'); const path = require('path');
const { merge } = require('webpack-merge'); const { merge } = require('webpack-merge');
const common = require('./webpack.common.js'); const common = require('./webpack.common');
//merge() calls in the environment-specific configuration to include commons // merge() calls in the environment-specific configuration to include commons
module.exports = merge(common, { module.exports = merge(common, {
//set development mode // set development mode
mode: 'development', mode: 'development',
//enable strong source mapping // enable strong source mapping
devtool: 'inline-source-map', devtool: 'inline-source-map',
devServer: { devServer: {
static: path.resolve(__dirname, '../dist'), static: path.resolve(__dirname, '../dist'),
//When using the HTML5 History API, the index.html page will likely have to be served in place of any 404 responses. Enable devServer.historyApiFallback by setting it to true. /*
historyApiFallback: true, When using the HTML5 History API,
}, the index.html page will likely have to be served in place of any 404 responses.
Enable devServer.historyApiFallback by setting it to true.
*/
historyApiFallback: true,
},
}); });

View File

@ -1,8 +1,9 @@
const { merge } = require('webpack-merge'); const { merge } = require('webpack-merge');
const common = require('./webpack.common.js'); const common = require('./webpack.common');
module.exports = merge(common, { module.exports = merge(common, {
mode: 'production', mode: 'production',
//source maps encouraged in production // source maps encouraged in production
//choose mapping with fairly quick build speed like source-map // choose mapping with fairly quick build speed like source-map
devtool: 'source-map', devtool: 'source-map',
}); });

8018
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -14,29 +14,35 @@
"author": "Software Ingenieur Begerad <dialog@SwIngBe.de>", "author": "Software Ingenieur Begerad <dialog@SwIngBe.de>",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"engines": { "engines": {
"node": ">=18.19.0" "node": ">=18.17.0"
}, },
"scripts": { "scripts": {
"start": "webpack serve --config config/webpack.dev.js", "start": "webpack serve --config config/webpack.dev.js",
"build": "webpack --config config/webpack.prod.js", "build": "webpack --config config/webpack.prod.js",
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1",
"lint": "eslint .",
"lint:fix": "eslint --fix ."
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "7.22.10", "@babel/core": "7.23.9",
"@babel/preset-env": "7.22.10", "@babel/eslint-parser": "7.23.10",
"@babel/preset-react": "7.22.5", "@babel/plugin-transform-runtime": "7.23.9",
"@babel/preset-env": "7.23.9",
"@babel/preset-react": "7.23.3",
"@babel/runtime": "7.23.9",
"babel-loader": "9.1.3", "babel-loader": "9.1.3",
"css-loader": "6.8.1", "css-loader": "6.10.0",
"file-loader": "6.2.0", "eslint": "8.56.0",
"html-webpack-plugin": "5.5.3", "eslint-config-airbnb": "19.0.4",
"style-loader": "3.3.2", "eslint-webpack-plugin": "4.0.1",
"svg-url-loader": "8.0.0", "style-loader": "3.3.4",
"webpack": "5.88.2", "webpack": "5.90.2",
"webpack-cli": "5.1.4", "webpack-cli": "5.1.4",
"webpack-dev-server": "4.15.1", "webpack-dev-server": "5.0.1"
"webpack-merge": "5.9.0"
}, },
"dependencies": { "dependencies": {
"webpack-merge": "5.10.0",
"html-webpack-plugin": "5.6.0",
"axios": "1.3.6", "axios": "1.3.6",
"prop-types": "15.8.1", "prop-types": "15.8.1",
"react": "18.2.0", "react": "18.2.0",

View File

@ -1,37 +1,35 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const AgencyTableEntry = ({
agencyId, function AgencyTableEntry({
agencyName, agencyId,
tripCalendar agencyName,
}) => { tripCalendar,
if(tripCalendar!==undefined && Object.keys(tripCalendar).length>0){ }) {
return ( if (tripCalendar !== undefined && Object.keys(tripCalendar).length > 0) {
<tr> return (
<td>{agencyId}</td> <tr>
<td>{agencyName}</td> <td>{agencyId}</td>
{ <td>{agencyName}</td>
Object.values(tripCalendar).map((value,index)=>{ {
return ( Object.values(tripCalendar).map((value, index) => (
<th key={index}>{value}</th> <th key={index}>{value}</th>
); ))
})
} }
</tr> </tr>
); );
}else{ }
//console.log('AgencyTableEntry waiting for prop'); // console.log('AgencyTableEntry waiting for prop');
return ( return (
<tr> <tr>
<th>Table Body loading...</th> <th>Table Body loading...</th>
</tr> </tr>
); );
} }
};
AgencyTableEntry.propTypes = { AgencyTableEntry.propTypes = {
agencyId: PropTypes.string, agencyId: PropTypes.string,
agencyName: PropTypes.string, agencyName: PropTypes.string,
tripCalendar: PropTypes.object tripCalendar: PropTypes.object,
}; };
export default AgencyTableEntry; export default AgencyTableEntry;

View File

@ -1,40 +1,37 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default function AgencyTableHead({tripCalendar}) { export default function AgencyTableHead({ tripCalendar }) {
const handleTs=(ts)=>{ const handleTs = (ts) => {
//console.log(`AgencyTableHead ts: ${ts}`); // console.log(`AgencyTableHead ts: ${ts}`);
const date=new Date(parseInt(ts,10)); const date = new Date(parseInt(ts, 10));
//console.log(`AgencyTableHead date: ${date}`); // console.log(`AgencyTableHead date: ${date}`);
//return date.toString(); // return date.toString();
//return date.toISOString().split('T')[0] // return date.toISOString().split('T')[0]
return date.toLocaleDateString(); return date.toLocaleDateString();
}; };
if(tripCalendar!==undefined && Object.keys(tripCalendar).length>0){ if (tripCalendar !== undefined && Object.keys(tripCalendar).length > 0) {
//console.log('AgencyTableHead tripCalendar.length: '+Object.keys(tripCalendar).length); // console.log('AgencyTableHead tripCalendar.length: '+Object.keys(tripCalendar).length);
return ( return (
<tr> <tr>
<th>agency_id</th> <th>agency_id</th>
<th>agency_name</th> <th>agency_name</th>
{ {
Object.keys(tripCalendar).map((key,index)=>{ Object.keys(tripCalendar).map((key, index) => (
return ( <th key={index}>{handleTs(key)}</th>
<th key={index}>{handleTs(key)}</th> ))
);
})
} }
</tr> </tr>
); );
}else{ }
//console.log('AgencyTableHead waiting for prop'); // console.log('AgencyTableHead waiting for prop');
return ( return (
<tr> <tr>
<th>Table Head loading...</th> <th>Table Head loading...</th>
</tr> </tr>
); );
} }
};
AgencyTableHead.propTypes = { AgencyTableHead.propTypes = {
tripCalendar: PropTypes.object tripCalendar: PropTypes.object,
}; };

View File

@ -1,32 +1,53 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default function AgencyPerDayTableEntries ({array}) { export default function AgencyPerDayTableEntries({ array }) {
if ( array !== undefined && array !== null && array.length > 0 ) { if (array !== undefined && array !== null && array.length > 0) {
//TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql? // TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
//iterate over array // iterate over array
return array.map((item, index) => { return array.map((item, index) => (
return ( <tr
<tr key={index}
key={index} >
> <td>
<td>{item.agency_name}&nbsp;|</td> {item.agency_name}
<td>{item.agency_id}&nbsp;|</td> &nbsp;|
<td>{item.route_id}&nbsp;|</td> </td>
<td>{item.route_short_name}&nbsp;|</td> <td>
<td>{item.trip_id}&nbsp;|</td> {item.agency_id}
<td>{item.rt_part}&nbsp;|</td> &nbsp;|
<td>{item.trip_id === 0 ? 0 : ((item.rt_part / item.trip_id) * 100).toFixed(2)}&nbsp;|</td> </td>
<td>{item.timestamp_pgsql}&nbsp;|</td> <td>
</tr> {item.route_id}
); &nbsp;|
}); </td>
}else{ <td>
//data is empty {item.route_short_name}
return null; &nbsp;|
} </td>
}; <td>
{item.trip_id}
&nbsp;|
</td>
<td>
{item.rt_part}
&nbsp;|
</td>
<td>
{item.trip_id === 0 ? 0 : ((item.rt_part / item.trip_id) * 100).toFixed(2)}
&nbsp;|
</td>
<td>
{item.timestamp_pgsql}
&nbsp;|
</td>
</tr>
));
}
// data is empty
return null;
}
AgencyPerDayTableEntries.propTypes = { AgencyPerDayTableEntries.propTypes = {
array: PropTypes.array array: PropTypes.array,
}; };

View File

@ -3,43 +3,47 @@ import PropTypes from 'prop-types';
import AgencyPerDayTableEntries from './agency-per-day-table-entries'; import AgencyPerDayTableEntries from './agency-per-day-table-entries';
/*destructure props object*/ /* destructure props object */
export default function AgencyPerDayTable ({array, title, date}){ export default function AgencyPerDayTable({ array, title, date }) {
if (array !== undefined && array !== null) {
if ( array !== undefined && array !== null) { /* return a React element */
/*return a React element*/ return (
return ( <>
<> <p>
<p>Table of {array.length}&nbsp;{title} for {date}:</p> Table of
<table> {array.length}
<thead> {title}
<tr> {' '}
<th>agency_name&nbsp;|</th> for
<th>agency_id&nbsp;|</th> {date}
<th>route_id&nbsp;|</th> :
<th>route_short_name&nbsp;|</th> </p>
<th>abs. trip count&nbsp;|</th> <table>
<th>RT TU count&nbsp;|</th> <thead>
<th>RT TU %&nbsp;|</th> <tr>
<th>latest RT timestamp&nbsp;|</th> <th>agency_name&nbsp;|</th>
</tr> <th>agency_id&nbsp;|</th>
</thead> <th>route_id&nbsp;|</th>
<tbody> <th>route_short_name&nbsp;|</th>
<AgencyPerDayTableEntries array={array} /> <th>abs. trip count&nbsp;|</th>
</tbody> <th>RT TU count&nbsp;|</th>
</table> <th>RT TU %&nbsp;|</th>
</> <th>latest RT timestamp&nbsp;|</th>
); </tr>
}else{ </thead>
return ( <tbody>
<> <AgencyPerDayTableEntries array={array} />
<p>loading...</p> </tbody>
</> </table>
); </>
} );
}; }
return (
<p>loading...</p>
);
}
AgencyPerDayTable.propTypes = { AgencyPerDayTable.propTypes = {
array: PropTypes.array, array: PropTypes.array,
title: PropTypes.string, title: PropTypes.string,
date: PropTypes.string date: PropTypes.string,
}; };

View File

@ -2,33 +2,35 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Dropdown from 'react-bootstrap/Dropdown'; import Dropdown from 'react-bootstrap/Dropdown';
export default function AgencySelect({ name, onChange, placeholder, rry, title }) { export default function AgencySelect({
name, onChange, placeholder, rry, title,
if (rry.length > 0) { }) {
return (<> if (rry.length > 0) {
<Dropdown> return (
<Dropdown.Toggle id="dropdown-basic">{placeholder} <Dropdown>
</Dropdown.Toggle> <Dropdown.Toggle id="dropdown-basic">
<Dropdown.Menu> {placeholder}
{rry.map((item) => ( </Dropdown.Toggle>
<Dropdown.Item <Dropdown.Menu>
eventKey={item.agency_id} {rry.map((item) => (
key={item.agency_id}> <Dropdown.Item
{item.agency_name} eventKey={item.agency_id}
</Dropdown.Item> key={item.agency_id}
>
{item.agency_name}
</Dropdown.Item>
))} ))}
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
</>); );
} else { }
return <p>Loading...</p>; return <p>Loading...</p>;
} }
};
AgencySelect.propTypes = { AgencySelect.propTypes = {
name: PropTypes.string, name: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
title: PropTypes.string, title: PropTypes.string,
placeholder: PropTypes.string, placeholder: PropTypes.string,
rry: PropTypes.array rry: PropTypes.array,
}; };

View File

@ -2,38 +2,38 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Form from 'react-bootstrap/Form'; import Form from 'react-bootstrap/Form';
export default function AgencySelect({ name, onChange, placeholder, rry, title }) { export default function AgencySelect({
name, onChange, placeholder, rry, title,
if (rry.length > 0) { }) {
return (<> if (rry.length > 0) {
<Form.Select return (
aria-label="select table entries per page" <Form.Select
name={name} aria-label="select table entries per page"
id={name} name={name}
className={name} id={name}
onChange={onChange} className={name}
placeholder={placeholder} onChange={onChange}
defaultValue={placeholder} placeholder={placeholder}
title={title} defaultValue={placeholder}
type="text" title={title}
required type="text"
required
> >
{rry.map((item) => ( {rry.map((item) => (
<option key={item.agency_id} value={item.agency_id}> <option key={item.agency_id} value={item.agency_id}>
{item.agency_name} {item.agency_name}
</option> </option>
))} ))}
</Form.Select> </Form.Select>
</>); );
} else { }
return <p>Loading...</p>; return <p>Loading...</p>;
} }
};
AgencySelect.propTypes = { AgencySelect.propTypes = {
name: PropTypes.string, name: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
title: PropTypes.string, title: PropTypes.string,
placeholder: PropTypes.string, placeholder: PropTypes.string,
rry: PropTypes.array rry: PropTypes.array,
}; };

View File

@ -2,33 +2,35 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Dropdown from 'react-bootstrap/Dropdown'; import Dropdown from 'react-bootstrap/Dropdown';
export default function AgencySelect({ name, onChange, placeholder, rry, title }) { export default function AgencySelect({
name, onChange, placeholder, rry, title,
if (rry.length > 0) { }) {
return (<> if (rry.length > 0) {
<Dropdown> return (
<Dropdown.Toggle id="dropdown-basic">{placeholder} <Dropdown>
</Dropdown.Toggle> <Dropdown.Toggle id="dropdown-basic">
<Dropdown.Menu> {placeholder}
{rry.map((item) => ( </Dropdown.Toggle>
<Dropdown.Item <Dropdown.Menu>
eventKey={item.agency_id} {rry.map((item) => (
key={item.agency_id}> <Dropdown.Item
{item.agency_name} eventKey={item.agency_id}
</Dropdown.Item> key={item.agency_id}
>
{item.agency_name}
</Dropdown.Item>
))} ))}
</Dropdown.Menu> </Dropdown.Menu>
</Dropdown> </Dropdown>
</>); );
} else { }
return <p>Loading...</p>; return <p>Loading...</p>;
} }
};
AgencySelect.propTypes = { AgencySelect.propTypes = {
name: PropTypes.string, name: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
title: PropTypes.string, title: PropTypes.string,
placeholder: PropTypes.string, placeholder: PropTypes.string,
rry: PropTypes.array rry: PropTypes.array,
}; };

View File

@ -5,35 +5,39 @@ import PropTypes from 'prop-types';
* @param rry Array of agency objects containing id and name * @param rry Array of agency objects containing id and name
*/ */
export default function AgencySelect({ name, onChange, rry }) { export default function AgencySelect({ name, onChange, rry }) {
if (rry !== undefined && rry !== null && rry.length > 0) {
if (rry !== undefined && rry !== null && rry.length > 0) { return (
return <form > <form>
<label htmlFor="input-agency">{name}: </label> <label htmlFor="input-agency">
<select {name}
name={name} :
id={name} {' '}
className={name} </label>
onChange={onChange} <select
placeholder={name} name={name}
defaultValue={name} id={name}
title={name} className={name}
type="text" onChange={onChange}
required placeholder={name}
> defaultValue={name}
{rry.map((item) => ( title={name}
<option key={item.agency_id} value={item.agency_id}> type="text"
{item.agency_name} required
</option> >
{rry.map((item) => (
<option key={item.agency_id} value={item.agency_id}>
{item.agency_name}
</option>
))} ))}
</select> </select>
</form>; </form>
} else { );
return <p>Loading...</p>; }
} return <p>Loading...</p>;
}; }
AgencySelect.propTypes = { AgencySelect.propTypes = {
name: PropTypes.string, name: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
rry: PropTypes.array rry: PropTypes.array,
}; };

View File

@ -1,30 +1,31 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const AgencyTableEntry = ({
agencyId, function AgencyTableEntry({
agencyName, agencyId,
agencyUrl, agencyName,
agencyTimezone, agencyUrl,
agencyLang, agencyTimezone,
agencyPhone agencyLang,
}) => { agencyPhone,
return ( }) {
<tr> return (
<td>{agencyId}</td> <tr>
<td>{agencyName}</td> <td>{agencyId}</td>
<td>{agencyUrl}</td> <td>{agencyName}</td>
<td>{agencyTimezone}</td> <td>{agencyUrl}</td>
<td>{agencyLang}</td> <td>{agencyTimezone}</td>
<td>{agencyPhone}</td> <td>{agencyLang}</td>
</tr> <td>{agencyPhone}</td>
); </tr>
}; );
}
AgencyTableEntry.propTypes = { AgencyTableEntry.propTypes = {
agencyId: PropTypes.string, agencyId: PropTypes.string,
agencyName: PropTypes.string, agencyName: PropTypes.string,
agencyUrl: PropTypes.string, agencyUrl: PropTypes.string,
agencyTimezone: PropTypes.string, agencyTimezone: PropTypes.string,
agencyLang: PropTypes.string, agencyLang: PropTypes.string,
agencyPhone: PropTypes.string agencyPhone: PropTypes.string,
}; };
export default AgencyTableEntry; export default AgencyTableEntry;

View File

@ -1,14 +1,15 @@
import React from 'react'; import React from 'react';
const AgencyTableHead = () => {
return ( function AgencyTableHead() {
<tr> return (
<th>agency_id</th> <tr>
<th>agency_name</th> <th>agency_id</th>
<th>agency_url</th> <th>agency_name</th>
<th>agency_timezone</th> <th>agency_url</th>
<th>agency_lang</th> <th>agency_timezone</th>
<th>agency_phone</th> <th>agency_lang</th>
</tr> <th>agency_phone</th>
); </tr>
}; );
}
export default AgencyTableHead; export default AgencyTableHead;

View File

@ -1,21 +1,21 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const CalendarDatesTableEntry = ({ serviceId, date, exceptionType }) => { function CalendarDatesTableEntry({ serviceId, date, exceptionType }) {
return ( return (
<tr> <tr>
<td>{serviceId}</td> <td>{serviceId}</td>
<td>{date}</td> <td>{date}</td>
<td>{exceptionType}</td> <td>{exceptionType}</td>
</tr> </tr>
); );
}; }
CalendarDatesTableEntry.propTypes = { CalendarDatesTableEntry.propTypes = {
serviceId: PropTypes.string, serviceId: PropTypes.string,
date: PropTypes.string, date: PropTypes.string,
exceptionType: PropTypes.number exceptionType: PropTypes.number,
}; };
export default CalendarDatesTableEntry; export default CalendarDatesTableEntry;

View File

@ -1,13 +1,13 @@
import React from 'react'; import React from 'react';
const CalendarDatesTableHead = () => { function CalendarDatesTableHead() {
return ( return (
<tr> <tr>
<th>service_id</th> <th>service_id</th>
<th>date</th> <th>date</th>
<th>exception_type</th> <th>exception_type</th>
</tr> </tr>
); );
}; }
export default CalendarDatesTableHead; export default CalendarDatesTableHead;

View File

@ -1,46 +1,46 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const CalendarTableEntry = ({ function CalendarTableEntry({
serviceId, serviceId,
monday, monday,
tuesday, tuesday,
wednesday, wednesday,
thursday, thursday,
friday, friday,
saturday, saturday,
sunday, sunday,
startDate, startDate,
endDate endDate,
}) => { }) {
return ( return (
<tr> <tr>
<td>{serviceId}</td> <td>{serviceId}</td>
<td>{monday ? 'true' : 'false'}</td> <td>{monday ? 'true' : 'false'}</td>
<td>{tuesday ? 'true' : 'false'}</td> <td>{tuesday ? 'true' : 'false'}</td>
<td>{wednesday ? 'true' : 'false'}</td> <td>{wednesday ? 'true' : 'false'}</td>
<td>{thursday ? 'true' : 'false'}</td> <td>{thursday ? 'true' : 'false'}</td>
<td>{friday ? 'true' : 'false'}</td> <td>{friday ? 'true' : 'false'}</td>
<td>{saturday ? 'true' : 'false'}</td> <td>{saturday ? 'true' : 'false'}</td>
<td>{sunday ? 'true' : 'false'}</td> <td>{sunday ? 'true' : 'false'}</td>
<td>{startDate}</td> <td>{startDate}</td>
<td>{endDate}</td> <td>{endDate}</td>
</tr> </tr>
); );
}; }
CalendarTableEntry.propTypes = { CalendarTableEntry.propTypes = {
serviceId: PropTypes.string, serviceId: PropTypes.string,
monday: PropTypes.number, monday: PropTypes.number,
tuesday: PropTypes.number, tuesday: PropTypes.number,
wednesday: PropTypes.number, wednesday: PropTypes.number,
thursday: PropTypes.number, thursday: PropTypes.number,
friday: PropTypes.number, friday: PropTypes.number,
saturday: PropTypes.number, saturday: PropTypes.number,
sunday: PropTypes.number, sunday: PropTypes.number,
startDate: PropTypes.string, startDate: PropTypes.string,
endDate: PropTypes.string endDate: PropTypes.string,
}; };
export default CalendarTableEntry; export default CalendarTableEntry;

View File

@ -1,20 +1,20 @@
import React from 'react'; import React from 'react';
const CalendarTableHead = () => { function CalendarTableHead() {
return ( return (
<tr> <tr>
<th>service_id</th> <th>service_id</th>
<th>monday</th> <th>monday</th>
<th>tuesday</th> <th>tuesday</th>
<th>wednesday</th> <th>wednesday</th>
<th>thursday</th> <th>thursday</th>
<th>friday</th> <th>friday</th>
<th>saturday</th> <th>saturday</th>
<th>sunday</th> <th>sunday</th>
<th>start_date</th> <th>start_date</th>
<th>end_date</th> <th>end_date</th>
</tr> </tr>
); );
}; }
export default CalendarTableHead; export default CalendarTableHead;

View File

@ -1,79 +1,78 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Chart as ChartJS, Chart as ChartJS,
CategoryScale, CategoryScale,
LinearScale, LinearScale,
BarElement, BarElement,
Title, Title,
Tooltip, Tooltip,
Legend Legend,
} from 'chart.js'; } from 'chart.js';
import { Bar } from 'react-chartjs-2'; import { Bar } from 'react-chartjs-2';
ChartJS.register( ChartJS.register(
CategoryScale, CategoryScale,
LinearScale, LinearScale,
BarElement, BarElement,
Title, Title,
Tooltip, Tooltip,
Legend Legend,
); );
/* destructure props*/ /* destructure props */
const ChartBarVertical = ({ route, time, trip }) => { function ChartBarVertical({ route, time, trip }) {
const data = { const data = {
labels: time, labels: time,
datasets: [ datasets: [
{ {
label: `trip count of route ${route}`, label: `trip count of route ${route}`,
data: trip, data: trip,
backgroundColor: 'rgba(255, 99, 132, 0.5)', backgroundColor: 'rgba(255, 99, 132, 0.5)',
borderColor: 'rgba(255, 99, 132, 0.2)' borderColor: 'rgba(255, 99, 132, 0.2)',
} },
] ],
}; };
const options = { const options = {
responsive: true, responsive: true,
plugins: { plugins: {
legend: { legend: {
display: true, display: true,
position: 'top' position: 'top',
}, },
title: { title: {
display: true, display: true,
text: `trip count of route ${route}` text: `trip count of route ${route}`,
} },
},
scales: {
y: {
ticks: {
max: 300,
min: 0,
stepSize: 10,
}, },
scales: { scaleLabel: {
y: { display: false,
ticks: { },
max: 300, },
min: 0, },
stepSize: 10 };
}, return (
scaleLabel: { <div className="ChartBar">
display: false <div
} style={{
} height: '300px',
} width: '900px',
}; }}
return ( >
<> <Bar data={data} options={options} />
<div className="ChartBar"> </div>
<div </div>
style={{ );
height: '300px', }
width: '900px'
}}
>
<Bar data={data} options={options} />
</div>
</div>
</>
);
};
ChartBarVertical.propTypes = { ChartBarVertical.propTypes = {
time: PropTypes.array, time: PropTypes.array,
trip: PropTypes.array, trip: PropTypes.array,
route: PropTypes.string route: PropTypes.string,
}; };
export default ChartBarVertical; export default ChartBarVertical;

View File

@ -1,82 +1,81 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { import {
Chart as ChartJS, Chart as ChartJS,
CategoryScale, CategoryScale,
LinearScale, LinearScale,
LineElement, LineElement,
Title, Title,
Tooltip, Tooltip,
Legend, Legend,
PointElement PointElement,
} from 'chart.js'; } from 'chart.js';
import { Line } from 'react-chartjs-2'; import { Line } from 'react-chartjs-2';
ChartJS.register( ChartJS.register(
CategoryScale, CategoryScale,
LinearScale, LinearScale,
LineElement, LineElement,
Title, Title,
Tooltip, Tooltip,
Legend, Legend,
PointElement PointElement,
); );
/* destructure props*/ /* destructure props */
const ChartLine = ({ route, time, trip }) => { function ChartLine({ route, time, trip }) {
const data = { const data = {
labels: time, labels: time,
datasets: [ datasets: [
{ {
label: `trip count of route ${route}`, label: `trip count of route ${route}`,
data: trip, data: trip,
fill: false, fill: false,
backgroundColor: 'rgb(255, 99, 132)', backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgba(255, 99, 132, 0.2)' borderColor: 'rgba(255, 99, 132, 0.2)',
} },
] ],
}; };
const options = { const options = {
responsive: true, responsive: true,
plugins: { plugins: {
legend: { legend: {
display: true, display: true,
position: 'top' position: 'top',
}, },
title: { title: {
display: true, display: true,
text: `trip count of route ${route}` text: `trip count of route ${route}`,
} },
},
scales: {
y: {
ticks: {
max: 300,
min: 0,
stepSize: 10,
}, },
scales: { scaleLabel: {
y: { display: false,
ticks: { },
max: 300, },
min: 0, },
stepSize: 10 };
}, return (
scaleLabel: { <div className="ChartLine">
display: false <div
} style={{
} height: '300px',
} width: '900px',
}; }}
return ( >
<> <Line data={data} options={options} />
<div className="ChartLine"> </div>
<div </div>
style={{ );
height: '300px', }
width: '900px'
}}
>
<Line data={data} options={options} />
</div>
</div>
</>
);
};
ChartLine.propTypes = { ChartLine.propTypes = {
time: PropTypes.array, time: PropTypes.array,
trip: PropTypes.array, trip: PropTypes.array,
route: PropTypes.string route: PropTypes.string,
}; };
export default ChartLine; export default ChartLine;

View File

@ -1,36 +1,43 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default function FileSelect({ options, name, onChange, title }) { export default function FileSelect({
if (options.length > 0) { options, name, onChange, title,
return <form > }) {
<label htmlFor="input-agency">{name}: </label> if (options.length > 0) {
<select return (
name={name} <form>
id={name} <label htmlFor="input-agency">
className={name} {name}
onChange={onChange} :
placeholder={name} {' '}
defaultValue={name} </label>
title={name} <select
type="text" name={name}
required id={name}
> className={name}
{options.map((item, index) => ( onChange={onChange}
<option key={index} value={item}> placeholder={name}
{item} defaultValue={name}
</option> title={name}
type="text"
required
>
{options.map((item, index) => (
<option key={index} value={item}>
{item}
</option>
))} ))}
</select> </select>
</form>; </form>
} else { );
return <p>Loading...</p>; }
} return <p>Loading...</p>;
}; }
FileSelect.propTypes = { FileSelect.propTypes = {
name: PropTypes.string, name: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
title: PropTypes.string, title: PropTypes.string,
options: PropTypes.array options: PropTypes.array,
}; };

View File

@ -1,32 +1,32 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import FileSelect from '../components/file-select'; import FileSelect from './file-select';
import TablePage from '../components/table-page'; import TablePage from './table-page';
import gtfs from '../utils/gtfs'; import gtfs from '../utils/gtfs';
export default function FileSelection({ options }) { export default function FileSelection({ options }) {
/* store and initialize data in function component state */
/*store and initialize data in function component state*/ const [fileName, setFileName] = useState(gtfs.datasetFiles[0]);
const [fileName, setFileName] = useState(gtfs.datasetFiles[0]); const handleChangeFile = (event) => {
const handleChangeFile = (event) => { setFileName((fileName) => event.target.value);
setFileName((fileName) => event.target.value); };
}; if (options.length > 0) {
if (options.length > 0) { return (
return <> <>
<FileSelect <FileSelect
name="file" name="file"
onChange={handleChangeFile} onChange={handleChangeFile}
options={options} options={options}
title='Select GTFS dataset file' title="Select GTFS dataset file"
/> />
<TablePage name={fileName} /> <TablePage name={fileName} />
</>; </>
} else { );
return <p>Loading...</p>; }
} return <p>Loading...</p>;
}; }
FileSelection.propTypes = { FileSelection.propTypes = {
options: PropTypes.array options: PropTypes.array,
}; };

View File

@ -1,30 +1,34 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*controlled component: input form value controlled by React*/ /* controlled component: input form value controlled by React */
const FormValue = (props) => { function FormValue(props) {
/*destructuring*/ /* destructuring */
const { value, valueName, onChange, onSubmit } = props; const {
value, valueName, onChange, onSubmit,
} = props;
return ( return (
/*one onChange handler for each value*/ /* one onChange handler for each value */
/*input names should match state names*/ /* input names should match state names */
<> <form onSubmit={onSubmit}>
<form onSubmit={onSubmit}> <label>
<label> <p>
<p>Please enter {valueName}:</p> Please enter
<input type="text" name="value" value={value} onChange={onChange} /> {valueName}
</label> :
<input type="submit" value="Submit" /> </p>
</form> <input type="text" name="value" value={value} onChange={onChange} />
</> </label>
); <input type="submit" value="Submit" />
}; </form>
);
}
export default FormValue; export default FormValue;
FormValue.propTypes = { FormValue.propTypes = {
value: PropTypes.string, value: PropTypes.string,
valueName: PropTypes.string, valueName: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
onSubmit: PropTypes.func onSubmit: PropTypes.func,
}; };

View File

@ -1,31 +1,31 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const FrequenciesTableEntry = ({ function FrequenciesTableEntry({
tripId, tripId,
startTime, startTime,
endTime, endTime,
headwaySecs, headwaySecs,
exactTimes exactTimes,
}) => { }) {
return ( return (
<tr> <tr>
<td>{tripId}</td> <td>{tripId}</td>
<td>{startTime}</td> <td>{startTime}</td>
<td>{endTime}</td> <td>{endTime}</td>
<td>{headwaySecs}</td> <td>{headwaySecs}</td>
<td>{exactTimes}</td> <td>{exactTimes}</td>
</tr> </tr>
); );
}; }
FrequenciesTableEntry.propTypes = { FrequenciesTableEntry.propTypes = {
tripId: PropTypes.string, tripId: PropTypes.string,
startTime: PropTypes.string, startTime: PropTypes.string,
endTime: PropTypes.string, endTime: PropTypes.string,
headwaySecs: PropTypes.number, headwaySecs: PropTypes.number,
exactTimes: PropTypes.number exactTimes: PropTypes.number,
}; };
export default FrequenciesTableEntry; export default FrequenciesTableEntry;

View File

@ -1,15 +1,15 @@
import React from 'react'; import React from 'react';
const FrequenciesTableHead = () => { function FrequenciesTableHead() {
return ( return (
<tr> <tr>
<th>trip_id</th> <th>trip_id</th>
<th>start_time</th> <th>start_time</th>
<th>end_time</th> <th>end_time</th>
<th>headway_secs</th> <th>headway_secs</th>
<th>exact_times</th> <th>exact_times</th>
</tr> </tr>
); );
}; }
export default FrequenciesTableHead; export default FrequenciesTableHead;

View File

@ -1,29 +1,41 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default function GroupAgencyPerDayTableEntries ({array}) { export default function GroupAgencyPerDayTableEntries({ array }) {
if ( array !== undefined && array !== null && array.length > 0 ) { if (array !== undefined && array !== null && array.length > 0) {
//TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql? // TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
//iterate over array // iterate over array
return array.map((item, index) => { return array.map((item, index) => (
return ( <tr
<tr key={index}
key={index} >
> <td>
<td>{item.agency_name}&nbsp;|</td> {item.agency_name}
<td>{item.agency_id}&nbsp;|</td> &nbsp;|
<td>{item.rt_part}&nbsp;|</td> </td>
<td>{item.agency_id === 0 ? 0 : ((item.rt_part / item.agency_id) * 100).toFixed(2)}&nbsp;|</td> <td>
<td>{item.timestamp_pgsql}&nbsp;|</td> {item.agency_id}
</tr> &nbsp;|
); </td>
}); <td>
}else{ {item.rt_part}
//data is empty &nbsp;|
return null; </td>
} <td>
}; {item.agency_id === 0 ? 0 : ((item.rt_part / item.agency_id) * 100).toFixed(2)}
&nbsp;|
</td>
<td>
{item.timestamp_pgsql}
&nbsp;|
</td>
</tr>
));
}
// data is empty
return null;
}
GroupAgencyPerDayTableEntries.propTypes = { GroupAgencyPerDayTableEntries.propTypes = {
array: PropTypes.array array: PropTypes.array,
}; };

View File

@ -3,42 +3,46 @@ import PropTypes from 'prop-types';
import GroupAgencyPerDayTableEntries from './group-agency-per-day-table-entries'; import GroupAgencyPerDayTableEntries from './group-agency-per-day-table-entries';
//TODO For the cnnct feed, why do we get a 93 length agency array instead of 123? // TODO For the cnnct feed, why do we get a 93 length agency array instead of 123?
/*destructure props object*/ /* destructure props object */
export default function GroupAgencyPerDayTable ({array, title, date}){ export default function GroupAgencyPerDayTable({ array, title, date }) {
if (array !== undefined && array !== null) {
if ( array !== undefined && array !== null) { /* return a React element */
/*return a React element*/ return (
return ( <>
<> <p>
<p>Table of {array.length}&nbsp;{title} for {date}:</p> Table of
<table> {array.length}
<thead> {title}
<tr> {' '}
<th>agency_name&nbsp;|</th> for
<th>abs. trip count&nbsp;|</th> {date}
<th>RT TU count&nbsp;|</th> :
<th>RT TU %&nbsp;|</th> </p>
<th>latest RT timestamp&nbsp;|</th> <table>
</tr> <thead>
</thead> <tr>
<tbody> <th>agency_name&nbsp;|</th>
<GroupAgencyPerDayTableEntries array={array} /> <th>abs. trip count&nbsp;|</th>
</tbody> <th>RT TU count&nbsp;|</th>
</table> <th>RT TU %&nbsp;|</th>
</> <th>latest RT timestamp&nbsp;|</th>
); </tr>
}else{ </thead>
return ( <tbody>
<> <GroupAgencyPerDayTableEntries array={array} />
<p>loading...</p> </tbody>
</> </table>
); </>
} );
}; }
return (
<p>loading...</p>
);
}
GroupAgencyPerDayTable.propTypes = { GroupAgencyPerDayTable.propTypes = {
array: PropTypes.array, array: PropTypes.array,
title: PropTypes.string, title: PropTypes.string,
date: PropTypes.string, date: PropTypes.string,
}; };

View File

@ -4,42 +4,46 @@ import PropTypes from 'prop-types';
import config from '../config'; import config from '../config';
//destructure props // destructure props
export default function GtfsFile({ name }) { export default function GtfsFile({ name }) {
/*store count as array in function component state*/ /* store count as array in function component state */
/*initialise as empty array*/ /* initialise as empty array */
const [count, setCount] = useState(null); const [count, setCount] = useState(null);
/*fetch count in a JavaScript function*/ /* fetch count in a JavaScript function */
const getCount = async () => { const getCount = async () => {
try { try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/ /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const address = `${config.API}table-${name}-count`; const address = `${config.API}table-${name}-count`;
const count = await axios.get(address); const count = await axios.get(address);
/*set state*/ /* set state */
setCount(count.data[0]['count']); setCount(count.data[0].count);
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
}; };
/*this hook is run after a DOM update. Changing state migh result in an infinite loop*/ /* this hook is run after a DOM update. Changing state migh result in an infinite loop */
useEffect(() => { useEffect(() => {
/*effect goes here*/ /* effect goes here */
/*hook need to be placed in body of the function component in which it is used*/ /* hook need to be placed in body of the function component in which it is used */
getCount(); getCount();
/*use an empty dependency array to ensure the hook is running only once*/ /* use an empty dependency array to ensure the hook is running only once */
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []); }, []);
return <li key={name}> return (
{name}:&nbsp;{count ? count : 'loading...'} <li key={name}>
</li>; {name}
}; :&nbsp;
{count || 'loading...'}
</li>
);
}
GtfsFile.propTypes = { GtfsFile.propTypes = {
name: PropTypes.string name: PropTypes.string,
}; };

View File

@ -6,17 +6,21 @@ import GtfsFile from './gtfs-file';
import gtfs from '../utils/gtfs'; import gtfs from '../utils/gtfs';
export default function GtfsFiles() { export default function GtfsFiles() {
//TODO Do we have to call an API for each and every dataset file? // TODO Do we have to call an API for each and every dataset file?
return <fieldset> return (
<legend><b>GTFS Schedule</b> feed overview (file: item count)</legend> <fieldset>
<ul> <legend>
{gtfs.datasetFiles.map((item, index) => { <b>GTFS Schedule</b>
return <GtfsFile key={index} name={item} />; {' '}
})} feed overview (file: item count)
</ul> </legend>
</fieldset>; <ul>
}; {gtfs.datasetFiles.map((item, index) => <GtfsFile key={index} name={item} />)}
</ul>
</fieldset>
);
}
GtfsFiles.propTypes = { GtfsFiles.propTypes = {
data: PropTypes.array data: PropTypes.array,
}; };

View File

@ -1,31 +1,36 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import Alert from 'react-bootstrap/Alert'; import Alert from 'react-bootstrap/Alert';
import config from '../config'; import config from '../config';
const GtfsValidatorReport = () => {
const [show, setShow] = useState(true); function GtfsValidatorReport() {
if (show) { const [show, setShow] = useState(true);
return ( if (show) {
<> return (
<Alert variant={'secondary'} onClose={() => setShow(false)} dismissible> <Alert variant="secondary" onClose={() => setShow(false)} dismissible>
A daily{' '} A daily
<Alert.Link href={config.GTFS_VALIDATOR_REPORT}> {' '}
report <Alert.Link href={config.GTFS_VALIDATOR_REPORT}>
</Alert.Link>{' '} report
about compliance of this GTFS feed with these{' '} </Alert.Link>
<Alert.Link href="https://github.com/MobilityData/GTFS_Schedule_Best-Practices"> {' '}
Best Practices{' '} about compliance of this GTFS feed with these
</Alert.Link>{' '} {' '}
is generated using the{' '} <Alert.Link href="https://github.com/MobilityData/GTFS_Schedule_Best-Practices">
<Alert.Link href="https://github.com/MobilityData/gtfs-validator"> Best Practices
gtfs-validator{' '} {' '}
</Alert.Link> </Alert.Link>
. {' '}
</Alert> is generated using the
</> {' '}
); <Alert.Link href="https://github.com/MobilityData/gtfs-validator">
} else { gtfs-validator
return null; {' '}
} </Alert.Link>
}; .
</Alert>
);
}
return null;
}
export default GtfsValidatorReport; export default GtfsValidatorReport;

View File

@ -1,49 +1,60 @@
import React from 'react' import React from 'react';
export default function Header(){ export default function Header() {
return <> return (
<a href='/' rel="noopener noreferrer"> <>
<button> <a href="/" rel="noopener noreferrer">
Home <button>
</button> Home
</a> </button>
<a href='/agency' rel="noopener noreferrer"> </a>
<button> <a href="/agency" rel="noopener noreferrer">
Agency <button>
</button> Agency
</a> </button>
<a href='/files' rel="noopener noreferrer"> </a>
<button> <a href="/files" rel="noopener noreferrer">
Files <button>
</button> Files
</a> </button>
<a href='/realtime' rel="noopener noreferrer"> </a>
<button> <a href="/realtime" rel="noopener noreferrer">
Realtime <button>
</button> Realtime
</a> </button>
<a href='/contact' rel="noopener noreferrer"> </a>
<button> <a href="/contact" rel="noopener noreferrer">
Contact <button>
</button> Contact
</a> </button>
<a href='https://www.swingbe.de/imprint/' </a>
target="_blank" rel="noopener noreferrer"> <a
<button> href="https://www.swingbe.de/imprint/"
Imprint target="_blank"
</button> rel="noopener noreferrer"
</a> >
<a href='https://www.swingbe.de/privacy-policy/' <button>
target="_blank" rel="noopener noreferrer"> Imprint
<button> </button>
Privacy Policy </a>
</button> <a
</a> href="https://www.swingbe.de/privacy-policy/"
<a href='https://git.wtf-eg.de/dancingCycle/gtfs-display' target="_blank"
target="_blank" rel="noopener noreferrer"> rel="noopener noreferrer"
<button> >
Source <button>
</button> Privacy Policy
</a> </button>
</>; </a>
}; <a
href="https://git.wtf-eg.de/dancingCycle/gtfs-display"
target="_blank"
rel="noopener noreferrer"
>
<button>
Source
</button>
</a>
</>
);
}

View File

@ -1,30 +1,38 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*controlled component: input form value controlled by React*/ /* controlled component: input form value controlled by React */
export default function Input({id, name, onChange, placeholder, title, type, value}) { export default function Input({
return <form > id, name, onChange, placeholder, title, type, value,
<label htmlFor="input-agency">{name}: </label> }) {
<input return (
name={name} <form>
id={id} <label htmlFor="input-agency">
className={name} {name}
onChange={onChange} :
placeholder={placeholder} {' '}
title={title} </label>
type={type} <input
value={value} name={name}
required id={id}
/> className={name}
</form>; onChange={onChange}
}; placeholder={placeholder}
title={title}
type={type}
value={value}
required
/>
</form>
);
}
Input.propTypes = { Input.propTypes = {
id: PropTypes.string, id: PropTypes.string,
value: PropTypes.string, value: PropTypes.string,
name: PropTypes.string, name: PropTypes.string,
placeholder: PropTypes.string, placeholder: PropTypes.string,
title: PropTypes.string, title: PropTypes.string,
type: PropTypes.string, type: PropTypes.string,
onChange: PropTypes.func onChange: PropTypes.func,
}; };

View File

@ -1,15 +1,16 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
const LevelsTableEntry = ({ levelId, levelIndex }) => {
return ( function LevelsTableEntry({ levelId, levelIndex }) {
<tr> return (
<td>{levelId}</td> <tr>
<td>{levelIndex}</td> <td>{levelId}</td>
</tr> <td>{levelIndex}</td>
); </tr>
}; );
}
LevelsTableEntry.propTypes = { LevelsTableEntry.propTypes = {
levelId: PropTypes.string, levelId: PropTypes.string,
levelIndex: PropTypes.number levelIndex: PropTypes.number,
}; };
export default LevelsTableEntry; export default LevelsTableEntry;

View File

@ -1,12 +1,12 @@
import React from 'react'; import React from 'react';
const LevelsTableHead = () => { function LevelsTableHead() {
return ( return (
<tr> <tr>
<th>level_id</th> <th>level_id</th>
<th>level_index</th> <th>level_index</th>
</tr> </tr>
); );
}; }
export default LevelsTableHead; export default LevelsTableHead;

View File

@ -1,19 +1,18 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*the simplest way to define a component is to write a JavaScript function*/ /* the simplest way to define a component is to write a JavaScript function */
/*Accept a single property object argument*/ /* Accept a single property object argument */
function Loading (props) { function Loading(props) {
if (props.loading) { if (props.loading) {
/*return a React element*/ /* return a React element */
return <p>Loading...</p>; return <p>Loading...</p>;
} else { }
return null; return null;
}
} }
Loading.propTypes = { Loading.propTypes = {
loading: PropTypes.bool loading: PropTypes.bool,
}; };
export default Loading; export default Loading;

View File

@ -2,36 +2,36 @@ import React from 'react';
import { Navbar, Nav } from 'react-bootstrap'; import { Navbar, Nav } from 'react-bootstrap';
import { LinkContainer } from 'react-router-bootstrap'; import { LinkContainer } from 'react-router-bootstrap';
function NavigationBar () { function NavigationBar() {
return ( return (
<Navbar collapseOnSelect fixed="top" bg="dark" expand="xxl" variant="dark"> <Navbar collapseOnSelect fixed="top" bg="dark" expand="xxl" variant="dark">
//TODO make brand available through configuration //TODO make brand available through configuration
<Navbar.Brand href="/">GTFS Display</Navbar.Brand> <Navbar.Brand href="/">GTFS Display</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" /> <Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav"> <Navbar.Collapse id="basic-navbar-nav">
<Nav className="mr-auto"> <Nav className="mr-auto">
<LinkContainer to="/agency"> <LinkContainer to="/agency">
<Nav.Link>Agency</Nav.Link> <Nav.Link>Agency</Nav.Link>
</LinkContainer> </LinkContainer>
</Nav> </Nav>
<Nav className="mr-auto"> <Nav className="mr-auto">
<LinkContainer to="/files"> <LinkContainer to="/files">
<Nav.Link>Files</Nav.Link> <Nav.Link>Files</Nav.Link>
</LinkContainer> </LinkContainer>
</Nav> </Nav>
<Nav className="mr-auto"> <Nav className="mr-auto">
<LinkContainer to="/realtime"> <LinkContainer to="/realtime">
<Nav.Link>Realtime</Nav.Link> <Nav.Link>Realtime</Nav.Link>
</LinkContainer> </LinkContainer>
</Nav> </Nav>
<Nav className="mr-auto"> <Nav className="mr-auto">
<LinkContainer to="/contact"> <LinkContainer to="/contact">
<Nav.Link>Contact</Nav.Link> <Nav.Link>Contact</Nav.Link>
</LinkContainer> </LinkContainer>
</Nav> </Nav>
</Navbar.Collapse> </Navbar.Collapse>
</Navbar> </Navbar>
); );
} }
export default NavigationBar; export default NavigationBar;

View File

@ -2,49 +2,48 @@ import React, { useState, useEffect } from 'react';
import axios from 'axios'; import axios from 'axios';
import config from '../config'; import config from '../config';
import Input from '../components/input'; import Input from './input';
export default function OddTrips() { export default function OddTrips() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const [data, setData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10; // Number of items to display per page
const dateDefault = 'Select date'; // TODO How do we handle invalid date input?
const [date, setDate] = useState(dateDefault); const handleDate = (e) => {
const [data, setData] = useState([]); if (e.target.value.indexOf('2023') !== -1
const [currentPage, setCurrentPage] = useState(1); || e.target.value.indexOf('2024') !== -1) {
const itemsPerPage = 10; // Number of items to display per page setDate((date) => e.target.value);
}
};
//TODO How do we handle invalid date input? /* this hook is run after a DOM update. Changing state might result in an infinite loop */
const handleDate = (e) => { /* hook need to be placed in body of the function component in which it is used */
if (e.target.value.indexOf('2023') !== -1 || useEffect(() => {
e.target.value.indexOf('2024') !== -1) { fetchData();
setDate((date) => e.target.value); /* use an empty dependency array to ensure the hook is running only once */
} /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}; }, [date]);
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ const fetchData = async () => {
/*hook need to be placed in body of the function component in which it is used*/ if (date !== dateDefault) {
useEffect(() => { try {
fetchData();
/*use an empty dependency array to ensure the hook is running only once*/
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
}, [date]);
const fetchData = async () => {
if ( date !== dateDefault) {
try {
const address = `${config.API}trip-updates-odd-routes?day=${date}`; const address = `${config.API}trip-updates-odd-routes?day=${date}`;
//console.log('address: ' + address); // console.log('address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) { if (res.data !== undefined && res.data !== null) {
//console.log('res.data.length: ' + res.data.length); // console.log('res.data.length: ' + res.data.length);
setData((data) => res.data); setData((data) => res.data);
} else { } else {
console.error('ERROR: trip-updates with odd routes request FAILED'); console.error('ERROR: trip-updates with odd routes request FAILED');
} }
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
} }
}; };
const startIndex = (currentPage - 1) * itemsPerPage; const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage; const endIndex = startIndex + itemsPerPage;
@ -64,28 +63,34 @@ export default function OddTrips() {
}; };
return ( return (
<> <>
<label> <label>
<Input <Input
id="inputDate" id="inputDate"
name={dateDefault} name={dateDefault}
onChange={handleDate} onChange={handleDate}
placeholder="Enter date ${dateDefault}" placeholder="Enter date ${dateDefault}"
type="date" type="date"
title="Enter date ${dateDefault}" title="Enter date ${dateDefault}"
value={date} value={date}
/> />
</label> </label>
<p> <p>
abs odd route count: {data.length}, total pages: {Math.ceil(data.length / itemsPerPage)}, current page: {currentPage} abs odd route count:
</p> {' '}
<dl> {data.length}
{currentItems.map((item, index) => ( , total pages:
<dt key={index}>{item.trip_route_id}</dt> {Math.ceil(data.length / itemsPerPage)}
, current page:
{currentPage}
</p>
<dl>
{currentItems.map((item, index) => (
<dt key={index}>{item.trip_route_id}</dt>
))} ))}
</dl> </dl>
<button onClick={handlePreviousPage}>Previous</button> <button onClick={handlePreviousPage}>Previous</button>
<button onClick={handleNextPage}>Next</button> <button onClick={handleNextPage}>Next</button>
</> </>
); );
}; }

View File

@ -2,49 +2,48 @@ import React, { useState, useEffect } from 'react';
import axios from 'axios'; import axios from 'axios';
import config from '../config'; import config from '../config';
import Input from '../components/input'; import Input from './input';
export default function OddTrips() { export default function OddTrips() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const [data, setData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10; // Number of items to display per page
const dateDefault = 'Select date'; // TODO How do we handle invalid date input?
const [date, setDate] = useState(dateDefault); const handleDate = (e) => {
const [data, setData] = useState([]); if (e.target.value.indexOf('2023') !== -1
const [currentPage, setCurrentPage] = useState(1); || e.target.value.indexOf('2024') !== -1) {
const itemsPerPage = 10; // Number of items to display per page setDate((date) => e.target.value);
//TODO How do we handle invalid date input?
const handleDate = (e) => {
if (e.target.value.indexOf('2023') !== -1 ||
e.target.value.indexOf('2024') !== -1) {
setDate((date) => e.target.value);
}
};
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/
/*hook need to be placed in body of the function component in which it is used*/
useEffect(() => {
fetchData();
/*use an empty dependency array to ensure the hook is running only once*/
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
}, [date]);
const fetchData = async () => {
if ( date !== dateDefault) {
try {
const address = `${config.API}trip-updates-odd-trips?day=${date}`;
//console.log('address: ' + address);
const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) {
//console.log('res.data.length: ' + res.data.length);
setData((data) => res.data);
} else {
console.error('ERROR: trip-updates with odd trips request FAILED');
}
} catch (error) {
console.log(error);
} }
} };
};
/* this hook is run after a DOM update. Changing state might result in an infinite loop */
/* hook need to be placed in body of the function component in which it is used */
useEffect(() => {
fetchData();
/* use an empty dependency array to ensure the hook is running only once */
/* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [date]);
const fetchData = async () => {
if (date !== dateDefault) {
try {
const address = `${config.API}trip-updates-odd-trips?day=${date}`;
// console.log('address: ' + address);
const res = await axios.get(address);
if (res.data !== undefined && res.data !== null) {
// console.log('res.data.length: ' + res.data.length);
setData((data) => res.data);
} else {
console.error('ERROR: trip-updates with odd trips request FAILED');
}
} catch (error) {
console.log(error);
}
}
};
const startIndex = (currentPage - 1) * itemsPerPage; const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage; const endIndex = startIndex + itemsPerPage;
@ -64,23 +63,29 @@ export default function OddTrips() {
}; };
return ( return (
<> <>
<label> <label>
<Input <Input
id="inputDate" id="inputDate"
name={dateDefault} name={dateDefault}
onChange={handleDate} onChange={handleDate}
placeholder="Enter date ${dateDefault}" placeholder="Enter date ${dateDefault}"
type="date" type="date"
title="Enter date ${dateDefault}" title="Enter date ${dateDefault}"
value={date} value={date}
/> />
</label> </label>
<p> <p>
abs odd trip count: {data.length}, total pages: {Math.ceil(data.length / itemsPerPage)}, current page: {currentPage} abs odd trip count:
</p> {' '}
{data.length}
, total pages:
{Math.ceil(data.length / itemsPerPage)}
, current page:
{currentPage}
</p>
<dl> <dl>
{currentItems.map((item, index) => ( {currentItems.map((item, index) => (
<dt key={index}>{item.trip_trip_id}</dt> <dt key={index}>{item.trip_trip_id}</dt>
))} ))}
</dl> </dl>
@ -88,4 +93,4 @@ export default function OddTrips() {
<button onClick={handleNextPage}>Next</button> <button onClick={handleNextPage}>Next</button>
</> </>
); );
}; }

View File

@ -2,15 +2,14 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default function CountNext({ count }) { export default function CountNext({ count }) {
if (count === '0' if (count === '0'
|| count === null || count === null
|| count === undefined) { || count === undefined) {
return '0'; return '0';
} else { }
return count; return count;
} }
};
CountNext.propTypes = { CountNext.propTypes = {
count: PropTypes.string count: PropTypes.string,
}; };

View File

@ -3,24 +3,26 @@ import PropTypes from 'prop-types';
import Count from './overview-next-table-count'; import Count from './overview-next-table-count';
/*destructure props object*/ /* destructure props object */
export default function OverviewNextTableEntry ({ agencyName, routeCount, stopCount, tripCount }) { export default function OverviewNextTableEntry({
const routeCountBadge = <Count count={routeCount} />; agencyName, routeCount, stopCount, tripCount,
const stopCountBadge = <Count count={stopCount} />; }) {
const tripCountBadge = <Count count={tripCount} />; const routeCountBadge = <Count count={routeCount} />;
return ( const stopCountBadge = <Count count={stopCount} />;
<tr> const tripCountBadge = <Count count={tripCount} />;
<td>{agencyName}</td> return (
<td>{routeCountBadge}</td> <tr>
<td>{stopCountBadge}</td> <td>{agencyName}</td>
<td>{tripCountBadge}</td> <td>{routeCountBadge}</td>
</tr> <td>{stopCountBadge}</td>
); <td>{tripCountBadge}</td>
}; </tr>
);
}
OverviewNextTableEntry.propTypes = { OverviewNextTableEntry.propTypes = {
agencyName: PropTypes.string, agencyName: PropTypes.string,
routeCount: PropTypes.string, routeCount: PropTypes.string,
stopCount: PropTypes.string, stopCount: PropTypes.string,
tripCount: PropTypes.string tripCount: PropTypes.string,
}; };

View File

@ -1,12 +1,12 @@
import React from 'react'; import React from 'react';
export default function OverviewNextTableHead () { export default function OverviewNextTableHead() {
return ( return (
<tr> <tr>
<th>Agency&nbsp;|</th> <th>Agency&nbsp;|</th>
<th>Route Count&nbsp;|</th> <th>Route Count&nbsp;|</th>
<th>Stop Count&nbsp;|</th> <th>Stop Count&nbsp;|</th>
<th>Trip Count&nbsp;|</th> <th>Trip Count&nbsp;|</th>
</tr> </tr>
); );
}; }

View File

@ -4,44 +4,46 @@ import PropTypes from 'prop-types';
import Entry from './overview-next-table-entry'; import Entry from './overview-next-table-entry';
import Head from './overview-next-table-head'; import Head from './overview-next-table-head';
/*the simplest way to define a component is to write a JavaScript function*/ /* the simplest way to define a component is to write a JavaScript function */
/*destructure props object*/ /* destructure props object */
export default function OverviewNextTable ({ overview }) { export default function OverviewNextTable({ overview }) {
const handleOverview = () => { const handleOverview = () => {
if (overview !== null && overview !== undefined && overview.length !== 0) {
// iterate over array
return overview.map((item, index) => (
<Entry
agencyName={item.agency_name}
routeCount={item.rts_cnt}
stopCount={item.stps_cnt}
tripCount={item.trps_cnt}
key={item.agency_id}
/>
));
}
console.error('overview NOT available');
return null;
};
if ( overview !== null && overview !== undefined && overview.length !== 0 ) { /* return a React element */
//iterate over array return (
return overview.map((item, index) => { <fieldset>
return ( <legend>
<Entry <b>GTFS Schedule</b>
agencyName={item.agency_name} {' '}
routeCount={item.rts_cnt} agency overview
stopCount={item.stps_cnt} </legend>
tripCount={item.trps_cnt} {/* size="sm" cuts cell padding in half */}
key={item.agency_id} {/* variant="dark" inverts colors */}
/> <table>
); <thead>
}); <Head />
} else { </thead>
console.error('overview NOT available'); <tbody>{handleOverview()}</tbody>
return null; </table>
} </fieldset>
}; );
}
/*return a React element*/
return <fieldset>
<legend><b>GTFS Schedule</b> agency overview</legend>
{/*size="sm" cuts cell padding in half*/}
{/*variant="dark" inverts colors*/}
<table>
<thead>
<Head />
</thead>
<tbody>{handleOverview()}</tbody>
</table>
</fieldset>;
};
OverviewNextTable.propTypes = { OverviewNextTable.propTypes = {
overview: PropTypes.array overview: PropTypes.array,
}; };

View File

@ -2,16 +2,15 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Badge from '../utils/badge'; import Badge from '../utils/badge';
const count = ({ count }) => { function count({ count }) {
if (count === 0) { if (count === 0) {
return <Badge msg={count} modifier={'danger'} />; return <Badge msg={count} modifier="danger" />;
} else { }
return count; return count;
} }
};
count.propTypes = { count.propTypes = {
count: PropTypes.number count: PropTypes.number,
}; };
export default count; export default count;

View File

@ -2,25 +2,27 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Count from './overview-table-count.js'; import Count from './overview-table-count.js';
/*destructure props object*/ /* destructure props object */
const OverviewTableEntry = ({ agencyName, routeCount, tripCount, day }) => { function OverviewTableEntry({
const routeCountBadge = <Count count={routeCount} />; agencyName, routeCount, tripCount, day,
const tripCountBadge = <Count count={tripCount} />; }) {
return ( const routeCountBadge = <Count count={routeCount} />;
<tr> const tripCountBadge = <Count count={tripCount} />;
<td>{agencyName}</td> return (
<td>{routeCount === null ? 'loading...' : routeCountBadge}</td> <tr>
<td>{tripCount === null ? 'loading...' : tripCountBadge}</td> <td>{agencyName}</td>
<td>{day === null ? 'loading...' : day}</td> <td>{routeCount === null ? 'loading...' : routeCountBadge}</td>
</tr> <td>{tripCount === null ? 'loading...' : tripCountBadge}</td>
); <td>{day === null ? 'loading...' : day}</td>
}; </tr>
);
}
OverviewTableEntry.propTypes = { OverviewTableEntry.propTypes = {
agencyName: PropTypes.string, agencyName: PropTypes.string,
routeCount: PropTypes.number, routeCount: PropTypes.number,
tripCount: PropTypes.number, tripCount: PropTypes.number,
day: PropTypes.object day: PropTypes.object,
}; };
export default OverviewTableEntry; export default OverviewTableEntry;

View File

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
const OverviewTableHead = () => { function OverviewTableHead() {
return ( return (
<tr> <tr>
<th>Agency</th> <th>Agency</th>
<th>Route Count</th> <th>Route Count</th>
<th>Trip Count</th> <th>Trip Count</th>
<th>{new Date().toDateString()}</th> <th>{new Date().toDateString()}</th>
</tr> </tr>
); );
}; }
export default OverviewTableHead; export default OverviewTableHead;

View File

@ -4,46 +4,43 @@ import Table from 'react-bootstrap/Table';
import Entry from './overview-table-entry'; import Entry from './overview-table-entry';
import Head from './overview-table-head'; import Head from './overview-table-head';
/*the simplest way to define a component is to write a JavaScript function*/ /* the simplest way to define a component is to write a JavaScript function */
/*destructure props object*/ /* destructure props object */
function OverviewTable ({ overview }) { function OverviewTable({ overview }) {
const handleOverview = () => { const handleOverview = () => {
if (overview) { if (overview) {
//iterate over array // iterate over array
return overview.map((item, index) => { return overview.map((item, index) => (
return ( <Entry
<Entry agencyName={item.agency_name}
agencyName={item.agency_name} routeCount={item.route_count}
routeCount={item.route_count} tripCount={item.trip_count}
tripCount={item.trip_count} day={item.day}
day={item.day} key={index}
key={index} />
/> ));
); }
}); console.error('overview NOT available');
} else { return null;
console.error('overview NOT available'); };
return null;
}
};
/*return a React element*/ /* return a React element */
return ( return (
<> <>
{/*size="sm" cuts cell padding in half*/} {/* size="sm" cuts cell padding in half */}
{/*variant="dark" inverts colors*/} {/* variant="dark" inverts colors */}
<Table striped bordered hover size="sm" variant="dark" responsive> <Table striped bordered hover size="sm" variant="dark" responsive>
<thead> <thead>
<Head /> <Head />
</thead> </thead>
<tbody>{handleOverview()}</tbody> <tbody>{handleOverview()}</tbody>
</Table> </Table>
</> </>
); );
} }
OverviewTable.propTypes = { OverviewTable.propTypes = {
overview: PropTypes.array overview: PropTypes.array,
}; };
export default OverviewTable; export default OverviewTable;

View File

@ -1,52 +1,52 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const PathwaysTableEntry = ({ function PathwaysTableEntry({
pathwayId, pathwayId,
fromStopId, fromStopId,
toStopId, toStopId,
pathwayMode, pathwayMode,
isBidirectional, isBidirectional,
length, length,
traversalTime, traversalTime,
stairCount, stairCount,
maxSlope, maxSlope,
minWidth, minWidth,
signpostedAs, signpostedAs,
reversedSignpostedAs reversedSignpostedAs,
}) => { }) {
return ( return (
<tr> <tr>
<td>{pathwayId}</td> <td>{pathwayId}</td>
<td>{fromStopId}</td> <td>{fromStopId}</td>
<td>{toStopId}</td> <td>{toStopId}</td>
<td>{pathwayMode}</td> <td>{pathwayMode}</td>
<td>{isBidirectional}</td> <td>{isBidirectional}</td>
<td>{length}</td> <td>{length}</td>
<td>{traversalTime}</td> <td>{traversalTime}</td>
<td>{stairCount}</td> <td>{stairCount}</td>
<td>{maxSlope}</td> <td>{maxSlope}</td>
<td>{minWidth}</td> <td>{minWidth}</td>
<td>{signpostedAs}</td> <td>{signpostedAs}</td>
<td>{reversedSignpostedAs}</td> <td>{reversedSignpostedAs}</td>
</tr> </tr>
); );
}; }
PathwaysTableEntry.propTypes = { PathwaysTableEntry.propTypes = {
pathwayId: PropTypes.string, pathwayId: PropTypes.string,
fromStopId: PropTypes.string, fromStopId: PropTypes.string,
toStopId: PropTypes.string, toStopId: PropTypes.string,
pathwayMode: PropTypes.number, pathwayMode: PropTypes.number,
isBidirectional: PropTypes.number, isBidirectional: PropTypes.number,
length: PropTypes.number, length: PropTypes.number,
traversalTime: PropTypes.number, traversalTime: PropTypes.number,
stairCount: PropTypes.number, stairCount: PropTypes.number,
maxSlope: PropTypes.number, maxSlope: PropTypes.number,
minWidth: PropTypes.number, minWidth: PropTypes.number,
signpostedAs: PropTypes.string, signpostedAs: PropTypes.string,
reversedSignpostedAs: PropTypes.string reversedSignpostedAs: PropTypes.string,
}; };
export default PathwaysTableEntry; export default PathwaysTableEntry;

View File

@ -1,22 +1,22 @@
import React from 'react'; import React from 'react';
const PathwaysTableHead = () => { function PathwaysTableHead() {
return ( return (
<tr> <tr>
<th>pathway_id</th> <th>pathway_id</th>
<th>from_stop_id</th> <th>from_stop_id</th>
<th>to_stop_id</th> <th>to_stop_id</th>
<th>pathway_mode</th> <th>pathway_mode</th>
<th>is_bidirectional</th> <th>is_bidirectional</th>
<th>length</th> <th>length</th>
<th>traversal_time</th> <th>traversal_time</th>
<th>stair_count</th> <th>stair_count</th>
<th>max_slope</th> <th>max_slope</th>
<th>min_width</th> <th>min_width</th>
<th>signposted_as</th> <th>signposted_as</th>
<th>reversed_signposted_as</th> <th>reversed_signposted_as</th>
</tr> </tr>
); );
}; }
export default PathwaysTableHead; export default PathwaysTableHead;

View File

@ -1,28 +1,37 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default function PerDayTableEntries ({array}) { export default function PerDayTableEntries({ array }) {
if ( array !== undefined && array !== null && array.length > 0 ) { if (array !== undefined && array !== null && array.length > 0) {
//TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql? // TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
//iterate over array // iterate over array
return array.map((item, index) => { return array.map((item, index) => (
return ( <tr
<tr key={index}
key={index} >
> <td>
<td>{item.count}&nbsp;|</td> {item.count}
<td>{item.rt_part}&nbsp;|</td> &nbsp;|
<td>{item.count === 0 ? 0 : ((item.rt_part / item.count) * 100).toFixed(2)}&nbsp;|</td> </td>
<td>{item.timestamp_pgsql}&nbsp;|</td> <td>
</tr> {item.rt_part}
); &nbsp;|
}); </td>
}else{ <td>
//data is empty {item.count === 0 ? 0 : ((item.rt_part / item.count) * 100).toFixed(2)}
return null; &nbsp;|
} </td>
}; <td>
{item.timestamp_pgsql}
&nbsp;|
</td>
</tr>
));
}
// data is empty
return null;
}
PerDayTableEntries.propTypes = { PerDayTableEntries.propTypes = {
array: PropTypes.array array: PropTypes.array,
}; };

View File

@ -3,39 +3,43 @@ import PropTypes from 'prop-types';
import PerDayTableEntries from './per-day-table-entries'; import PerDayTableEntries from './per-day-table-entries';
/*destructure props object*/ /* destructure props object */
export default function PerDayTable ({array, title, date}){ export default function PerDayTable({ array, title, date }) {
if (array !== undefined && array !== null) {
if ( array !== undefined && array !== null) { /* return a React element */
/*return a React element*/ return (
return ( <>
<> <p>
<p>Table of {array.length}&nbsp;{title} for {date}:</p> Table of
<table> {array.length}
<thead> {title}
<tr> {' '}
<th>abs. trip count&nbsp;|</th> for
<th>RT TU count&nbsp;|</th> {date}
<th>RT TU %&nbsp;|</th> :
<th>latest RT timestamp&nbsp;|</th> </p>
</tr> <table>
</thead> <thead>
<tbody> <tr>
<PerDayTableEntries array={array} /> <th>abs. trip count&nbsp;|</th>
</tbody> <th>RT TU count&nbsp;|</th>
</table> <th>RT TU %&nbsp;|</th>
</> <th>latest RT timestamp&nbsp;|</th>
); </tr>
}else{ </thead>
return ( <tbody>
<> <PerDayTableEntries array={array} />
<p>loading...</p> </tbody>
</> </table>
); </>
} );
}; }
return (
<p>loading...</p>
);
}
PerDayTable.propTypes = { PerDayTable.propTypes = {
array: PropTypes.array, array: PropTypes.array,
title: PropTypes.string, title: PropTypes.string,
date: PropTypes.string date: PropTypes.string,
}; };

View File

@ -2,83 +2,168 @@ import PropTypes from 'prop-types';
import React, { useState } from 'react'; import React, { useState } from 'react';
export default function RadioButton({ state, onChange }) { export default function RadioButton({ state, onChange }) {
return ( return (
<div > <div>
<form> <form>
<fieldset> <fieldset>
<legend>Select <b>GTFS Realtime</b> analysis rule</legend> <legend>
<input Select
<b>GTFS Realtime</b>
{' '}
analysis rule
</legend>
<input
type="radio" type="radio"
name="state" name="state"
id='state-odd-routes' id="state-odd-routes"
value="odd-routes" value="odd-routes"
onChange={onChange} onChange={onChange}
checked={state === 'odd-routes'} /> checked={state === 'odd-routes'}
<label htmlFor="state-odd-routes"> />
&nbsp;Show <b>GTFS Realtime</b> entities <b>TripUpdate</b> with <b>odd routes</b> <label htmlFor="state-odd-routes">
</label><br /> &nbsp;Show
{' '}
<b>GTFS Realtime</b>
{' '}
entities
{' '}
<b>TripUpdate</b>
{' '}
with
{' '}
<b>odd routes</b>
</label>
<br />
<input <input
type="radio" type="radio"
name="state" name="state"
id='state-odd-trips' id="state-odd-trips"
value="odd-trips" value="odd-trips"
onChange={onChange} onChange={onChange}
checked={state === 'odd-trips'} /> checked={state === 'odd-trips'}
<label htmlFor="state-odd-trips"> />
&nbsp;Show <b>GTFS Realtime</b> entities <b>TripUpdate</b> with <b>odd trips</b> <label htmlFor="state-odd-trips">
</label><br /> &nbsp;Show
{' '}
<b>GTFS Realtime</b>
{' '}
entities
{' '}
<b>TripUpdate</b>
{' '}
with
{' '}
<b>odd trips</b>
</label>
<br />
<input <input
type="radio" type="radio"
name="state" name="state"
id='state-feed' id="state-feed"
value="feed" value="feed"
onChange={onChange} onChange={onChange}
checked={state === 'feed'} /> checked={state === 'feed'}
<label htmlFor="state-feed"> />
&nbsp;Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>feed</b> level <label htmlFor="state-feed">
</label><br /> &nbsp;Analyse
{' '}
<b>GTFS Realtime</b>
{' '}
entities
{' '}
<b>TripUpdate</b>
{' '}
on
{' '}
<b>feed</b>
{' '}
level
</label>
<br />
<input <input
type="radio" type="radio"
name="state" name="state"
id='state-agencies' id="state-agencies"
value="agencies" value="agencies"
onChange={onChange} onChange={onChange}
checked={state === 'agencies'} /> checked={state === 'agencies'}
<label htmlFor="state-agencies"> />
&nbsp;Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>agencies</b> level <label htmlFor="state-agencies">
</label><br /> &nbsp;Analyse
{' '}
<b>GTFS Realtime</b>
{' '}
entities
{' '}
<b>TripUpdate</b>
{' '}
on
{' '}
<b>agencies</b>
{' '}
level
</label>
<br />
<input <input
type="radio" type="radio"
name="state" name="state"
id='state-routes' id="state-routes"
value="routes" value="routes"
onChange={onChange} onChange={onChange}
checked={state === 'routes'} /> checked={state === 'routes'}
<label htmlFor="state-routes"> />
&nbsp;Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>routes</b> level <label htmlFor="state-routes">
</label><br /> &nbsp;Analyse
{' '}
<b>GTFS Realtime</b>
{' '}
entities
{' '}
<b>TripUpdate</b>
{' '}
on
{' '}
<b>routes</b>
{' '}
level
</label>
<br />
<input <input
type="radio" type="radio"
name="state" name="state"
id='state-trips' id="state-trips"
value="trips" value="trips"
onChange={onChange} onChange={onChange}
checked={state === 'trips'} /> checked={state === 'trips'}
<label htmlFor="state-trips"> />
&nbsp;Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>trips</b> level <label htmlFor="state-trips">
</label><br /> &nbsp;Analyse
{' '}
<b>GTFS Realtime</b>
{' '}
entities
{' '}
<b>TripUpdate</b>
{' '}
on
{' '}
<b>trips</b>
{' '}
level
</label>
<br />
</fieldset> </fieldset>
</form> </form>
</div> </div>
); );
}; }
RadioButton.propTypes = { RadioButton.propTypes = {
state: PropTypes.string, state: PropTypes.string,
onChange: PropTypes.func onChange: PropTypes.func,
}; };

View File

@ -9,67 +9,117 @@ import PerDay from '../pages/per-day';
import TripUpdates from '../pages/trip-updates-route-day'; import TripUpdates from '../pages/trip-updates-route-day';
export default function Realtime({ state }) { export default function Realtime({ state }) {
if ( state === 'odd-routes' ) { if (state === 'odd-routes') {
return (
<>
<p>
Show <b>GTFS Realtime TripUpdate</b> entities that do <b>not</b> match any <b>GTFS Schedule routes</b>:
</p>
<OddRoutes />
</>
);
} else if ( state === 'odd-trips' ) {
return (
<>
<p>
Show <b>GTFS Realtime TripUpdate</b> entities that do <b>not</b> match any <b>GTFS Schedule trips</b>:
</p>
<OddTrips />
</>
);
} else if ( state === 'feed' ) {
return (
<>
<p>
Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>feed</b> level:
</p>
<PerDay />
</>
);
}
else if ( state === 'agencies' ) {
return ( return (
<> <>
<p> <p>
Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>agencies</b> level: Show <b>GTFS Realtime TripUpdate</b>
</p> {' '}
<GroupAgencyPerDay /> entities that do<b>not</b>
</> {' '}
match any<b>GTFS Schedule routes</b>
:
</p>
<OddRoutes />
</>
); );
} else if ( state === 'routes' ) { } if (state === 'odd-trips') {
return ( return (
<> <>
<p> <p>
Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>routes</b> level: Show <b>GTFS Realtime TripUpdate</b>
</p> {' '}
<AgencyPerDay /> entities that do<b>not</b>
</> {' '}
match any<b>GTFS Schedule trips</b>
:
</p>
<OddTrips />
</>
); );
} else if ( state === 'trips' ) { } if (state === 'feed') {
return ( return (
<> <>
<p> <p>
Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>trips</b> level: Analyse <b>GTFS Realtime</b>
</p> {' '}
<TripUpdates /> entities<b>TripUpdate</b>
</> {' '}
on<b>feed</b>
{' '}
level:
</p>
<PerDay />
</>
); );
} else { }
return <p>Select a <b>GTFS Realtime</b> analysis rule to contine!</p>; if (state === 'agencies') {
} return (
}; <>
<p>
Analyse
{' '}
<b>GTFS Realtime</b>
{' '}
entities
<b>TripUpdate</b>
{' '}
on
<b>agencies</b>
{' '}
level:
</p>
<GroupAgencyPerDay />
</>
);
} if (state === 'routes') {
return (
<>
<p>
Analyse
{' '}
<b>GTFS Realtime</b>
{' '}
entities
<b>TripUpdate</b>
{' '}
on
<b>routes</b>
{' '}
level:
</p>
<AgencyPerDay />
</>
);
} if (state === 'trips') {
return (
<>
<p>
Analyse
{' '}
<b>GTFS Realtime</b>
{' '}
entities
<b>TripUpdate</b>
{' '}
on
<b>trips</b>
{' '}
level:
</p>
<TripUpdates />
</>
);
}
return (
<p>
Select a<b>GTFS Realtime</b>
{' '}
analysis rule to contine!
</p>
);
}
Realtime.propTypes = { Realtime.propTypes = {
state: PropTypes.string state: PropTypes.string,
}; };

View File

@ -5,37 +5,39 @@ import PropTypes from 'prop-types';
* @param rry Array of route objects containing id and name * @param rry Array of route objects containing id and name
*/ */
export default function RouteSelect({ name, onChange, rry }) { export default function RouteSelect({ name, onChange, rry }) {
if (rry !== undefined && rry !== null) {
if (rry !== undefined && rry !== null) { return (
return (<> <form>
<form > <label htmlFor="input-route">
<label htmlFor="input-route">{name}: </label> {name}
<select :
name={name} {' '}
id={name} </label>
className={name} <select
onChange={onChange} name={name}
placeholder={name} id={name}
defaultValue={name} className={name}
title={name} onChange={onChange}
type="text" placeholder={name}
required defaultValue={name}
> title={name}
{rry.map((item) => ( type="text"
<option key={item.route_id} value={item.route_id}> required
{item.route_short_name} >
</option> {rry.map((item) => (
<option key={item.route_id} value={item.route_id}>
{item.route_short_name}
</option>
))} ))}
</select> </select>
</form> </form>
</>); );
} else { }
return <p>Loading...</p>; return <p>Loading...</p>;
} }
};
RouteSelect.propTypes = { RouteSelect.propTypes = {
name: PropTypes.string, name: PropTypes.string,
onChange: PropTypes.func, onChange: PropTypes.func,
rry: PropTypes.array rry: PropTypes.array,
}; };

View File

@ -1,40 +1,40 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const RoutesTableEntry = ({ function RoutesTableEntry({
routeId, routeId,
agencyId, agencyId,
routeShortName, routeShortName,
routeLongName, routeLongName,
routeType, routeType,
routeColor, routeColor,
routeTextColor, routeTextColor,
routeDesc routeDesc,
}) => { }) {
return ( return (
<tr> <tr>
<td>{routeId}</td> <td>{routeId}</td>
<td>{agencyId}</td> <td>{agencyId}</td>
<td>{routeShortName}</td> <td>{routeShortName}</td>
<td>{routeLongName}</td> <td>{routeLongName}</td>
<td>{routeType}</td> <td>{routeType}</td>
<td>{routeColor}</td> <td>{routeColor}</td>
<td>{routeTextColor}</td> <td>{routeTextColor}</td>
<td>{routeDesc}</td> <td>{routeDesc}</td>
</tr> </tr>
); );
}; }
RoutesTableEntry.propTypes = { RoutesTableEntry.propTypes = {
routeId: PropTypes.string, routeId: PropTypes.string,
agencyId: PropTypes.string, agencyId: PropTypes.string,
routeShortName: PropTypes.string, routeShortName: PropTypes.string,
routeLongName: PropTypes.string, routeLongName: PropTypes.string,
routeType: PropTypes.number, routeType: PropTypes.number,
routeColor: PropTypes.string, routeColor: PropTypes.string,
routeTextColor: PropTypes.string, routeTextColor: PropTypes.string,
routeDesc: PropTypes.string routeDesc: PropTypes.string,
}; };
export default RoutesTableEntry; export default RoutesTableEntry;

View File

@ -1,18 +1,18 @@
import React from 'react'; import React from 'react';
const RoutesTableHead = () => { function RoutesTableHead() {
return ( return (
<tr> <tr>
<th>route_id</th> <th>route_id</th>
<th>agency_id</th> <th>agency_id</th>
<th>route_short_name</th> <th>route_short_name</th>
<th>route_long_name</th> <th>route_long_name</th>
<th>route_type</th> <th>route_type</th>
<th>route_color</th> <th>route_color</th>
<th>route_text_color</th> <th>route_text_color</th>
<th>route_desc</th> <th>route_desc</th>
</tr> </tr>
); );
}; }
export default RoutesTableHead; export default RoutesTableHead;

View File

@ -1,39 +1,46 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*controlled component: select controlled by React*/ /* controlled component: select controlled by React */
export default function Select({defaultValue, id, name, onChange, options, title}) { export default function Select({
if (options) { defaultValue, id, name, onChange, options, title,
return <form > }) {
<label htmlFor="input-agency">{name}: </label> if (options) {
<select return (
name={name} <form>
id={id} <label htmlFor="input-agency">
className={name} {name}
onChange={onChange} :
placeholder={name} {' '}
defaultValue={defaultValue} </label>
title={title} <select
type="text" name={name}
required id={id}
> className={name}
{options.map((item, index) => ( onChange={onChange}
<option key={index} value={item}> placeholder={name}
{item} defaultValue={defaultValue}
</option> title={title}
type="text"
required
>
{options.map((item, index) => (
<option key={index} value={item}>
{item}
</option>
))} ))}
</select> </select>
</form>; </form>
} else { );
return <p>Select options unavailable.</p>; }
} return <p>Select options unavailable.</p>;
}; }
Select.propTypes = { Select.propTypes = {
id: PropTypes.string, id: PropTypes.string,
name: PropTypes.string, name: PropTypes.string,
defaultValue: PropTypes.number, defaultValue: PropTypes.number,
onChange: PropTypes.func, onChange: PropTypes.func,
options: PropTypes.array, options: PropTypes.array,
title: PropTypes.string title: PropTypes.string,
}; };

View File

@ -2,19 +2,19 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
class ServiceTableEntry extends Component { class ServiceTableEntry extends Component {
render () { render() {
return ( return (
<tr> <tr>
<td>{this.props.date}</td> <td>{this.props.date}</td>
<td>{this.props.count}</td> <td>{this.props.count}</td>
</tr> </tr>
); );
} }
} }
ServiceTableEntry.propTypes = { ServiceTableEntry.propTypes = {
date: PropTypes.string, date: PropTypes.string,
count: PropTypes.number count: PropTypes.number,
}; };
export default ServiceTableEntry; export default ServiceTableEntry;

View File

@ -1,14 +1,14 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
class MsgsTableHead extends Component { class MsgsTableHead extends Component {
render () { render() {
return ( return (
<tr> <tr>
<th>Time</th> <th>Time</th>
<th>Trip Count</th> <th>Trip Count</th>
</tr> </tr>
); );
} }
} }
export default MsgsTableHead; export default MsgsTableHead;

View File

@ -4,52 +4,50 @@ import Table from 'react-bootstrap/Table';
import Entry from './service-table-entry'; import Entry from './service-table-entry';
import Head from './service-table-head'; import Head from './service-table-head';
/*the simplest way to define a component is to write a JavaScript function*/ /* the simplest way to define a component is to write a JavaScript function */
function ServiceTable ({service,render}) { function ServiceTable({ service, render }) {
/*map over msgs array and return Standortmeldungen*/ /* map over msgs array and return Standortmeldungen */
const getService = () => { const getService = () => {
//iterate over object // iterate over object
if (service) { if (service) {
return Object.entries(service).map((trips, key) => { return Object.entries(service).map((trips, key) => {
/*the strict equals operator does not converts operants of differnet type*/ /* the strict equals operator does not converts operants of differnet type */
//console.log('key: ' + key); // console.log('key: ' + key);
let objTrips = trips[1]; const objTrips = trips[1];
let count = Object.keys(objTrips).length; const count = Object.keys(objTrips).length;
//console.log('count: ' + count); // console.log('count: ' + count);
let time = parseInt(trips[0], 10); const time = parseInt(trips[0], 10);
//console.log('time: ' + parseInt(time, 10)); // console.log('time: ' + parseInt(time, 10));
let date = new Date(time); const date = new Date(time);
//console.log('date: ' + date); // console.log('date: ' + date);
return <Entry date={date.toDateString()} count={count} key={key} />; return <Entry date={date.toDateString()} count={count} key={key} />;
}); });
} else {
console.error('service NOT available');
return null;
}
};
if (render) {
/*return a React element*/
return (
<>
{/*size="sm" cuts cell padding in half*/}
{/*variant="dark" inverts colors*/}
<Table striped bordered hover size="sm" variant="dark" responsive>
<thead className="thead-dark">
<Head />
</thead>
<tbody>{getService()}</tbody>
</Table>
</>
);
} else {
return null;
} }
console.error('service NOT available');
return null;
};
if (render) {
/* return a React element */
return (
<>
{/* size="sm" cuts cell padding in half */}
{/* variant="dark" inverts colors */}
<Table striped bordered hover size="sm" variant="dark" responsive>
<thead className="thead-dark">
<Head />
</thead>
<tbody>{getService()}</tbody>
</Table>
</>
);
}
return null;
} }
ServiceTable.propTypes = { ServiceTable.propTypes = {
service: PropTypes.object, service: PropTypes.object,
render: PropTypes.bool render: PropTypes.bool,
}; };
export default ServiceTable; export default ServiceTable;

View File

@ -1,28 +1,28 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const ShapesTableEntry = ({ function ShapesTableEntry({
shapeId, shapeId,
shapePtLat, shapePtLat,
shapePtLon, shapePtLon,
shapePtSequence shapePtSequence,
}) => { }) {
return ( return (
<tr> <tr>
<td>{shapeId}</td> <td>{shapeId}</td>
<td>{shapePtLat}</td> <td>{shapePtLat}</td>
<td>{shapePtLon}</td> <td>{shapePtLon}</td>
<td>{shapePtSequence}</td> <td>{shapePtSequence}</td>
</tr> </tr>
); );
}; }
ShapesTableEntry.propTypes = { ShapesTableEntry.propTypes = {
shapeId: PropTypes.string, shapeId: PropTypes.string,
shapePtLat: PropTypes.number, shapePtLat: PropTypes.number,
shapePtLon: PropTypes.number, shapePtLon: PropTypes.number,
shapePtSequence: PropTypes.number shapePtSequence: PropTypes.number,
}; };
export default ShapesTableEntry; export default ShapesTableEntry;

View File

@ -1,14 +1,14 @@
import React from 'react'; import React from 'react';
const ShapesTableHead = () => { function ShapesTableHead() {
return ( return (
<tr> <tr>
<th>shape_id</th> <th>shape_id</th>
<th>shape_pt_lat</th> <th>shape_pt_lat</th>
<th>shape_pt_lon</th> <th>shape_pt_lon</th>
<th>shape_pt_sequence</th> <th>shape_pt_sequence</th>
</tr> </tr>
); );
}; }
export default ShapesTableHead; export default ShapesTableHead;

View File

@ -1,46 +1,46 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const StopTimesTableEntry = ({ function StopTimesTableEntry({
tripId, tripId,
arrivalTimeHours, arrivalTimeHours,
arrivalTimeMinutes, arrivalTimeMinutes,
departureTimeHours, departureTimeHours,
departureTimeMinutes, departureTimeMinutes,
stopId, stopId,
stopSequence, stopSequence,
pickupType, pickupType,
dropOffType, dropOffType,
stopHeadsign stopHeadsign,
}) => { }) {
return ( return (
<tr> <tr>
<td>{tripId}</td> <td>{tripId}</td>
<td>{arrivalTimeHours}</td> <td>{arrivalTimeHours}</td>
<td>{arrivalTimeMinutes}</td> <td>{arrivalTimeMinutes}</td>
<td>{departureTimeHours}</td> <td>{departureTimeHours}</td>
<td>{departureTimeMinutes}</td> <td>{departureTimeMinutes}</td>
<td>{stopId}</td> <td>{stopId}</td>
<td>{stopSequence}</td> <td>{stopSequence}</td>
<td>{pickupType}</td> <td>{pickupType}</td>
<td>{dropOffType}</td> <td>{dropOffType}</td>
<td>{stopHeadsign}</td> <td>{stopHeadsign}</td>
</tr> </tr>
); );
}; }
StopTimesTableEntry.propTypes = { StopTimesTableEntry.propTypes = {
tripId: PropTypes.string, tripId: PropTypes.string,
arrivalTimeHours: PropTypes.number, arrivalTimeHours: PropTypes.number,
arrivalTimeMinutes: PropTypes.number, arrivalTimeMinutes: PropTypes.number,
departureTimeHours: PropTypes.number, departureTimeHours: PropTypes.number,
departureTimeMinutes: PropTypes.number, departureTimeMinutes: PropTypes.number,
stopId: PropTypes.string, stopId: PropTypes.string,
stopSequence: PropTypes.number, stopSequence: PropTypes.number,
pickupType: PropTypes.number, pickupType: PropTypes.number,
dropOffType: PropTypes.number, dropOffType: PropTypes.number,
stopHeadsign: PropTypes.string stopHeadsign: PropTypes.string,
}; };
export default StopTimesTableEntry; export default StopTimesTableEntry;

View File

@ -1,20 +1,20 @@
import React from 'react'; import React from 'react';
const StopTimesTableHead = () => { function StopTimesTableHead() {
return ( return (
<tr> <tr>
<th>trip_id</th> <th>trip_id</th>
<th>arrival_time_hours</th> <th>arrival_time_hours</th>
<th>arrival_time_minutes</th> <th>arrival_time_minutes</th>
<th>departure_time_hours</th> <th>departure_time_hours</th>
<th>departure_time_minutes</th> <th>departure_time_minutes</th>
<th>stop_id</th> <th>stop_id</th>
<th>stop_sequence</th> <th>stop_sequence</th>
<th>pickup_type</th> <th>pickup_type</th>
<th>drop_off_type</th> <th>drop_off_type</th>
<th>stop_headsign</th> <th>stop_headsign</th>
</tr> </tr>
); );
}; }
export default StopTimesTableHead; export default StopTimesTableHead;

View File

@ -1,49 +1,49 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const StopsTableEntry = ({ function StopsTableEntry({
stopId, stopId,
stopCode, stopCode,
stopName, stopName,
stopDesc, stopDesc,
stopLat, stopLat,
stopLon, stopLon,
locationType, locationType,
parentStation, parentStation,
wheelchairBoarding, wheelchairBoarding,
platformCode, platformCode,
zoneId zoneId,
}) => { }) {
return ( return (
<tr> <tr>
<td>{stopId}</td> <td>{stopId}</td>
<td>{stopCode}</td> <td>{stopCode}</td>
<td>{stopName}</td> <td>{stopName}</td>
<td>{stopDesc}</td> <td>{stopDesc}</td>
<td>{stopLat}</td> <td>{stopLat}</td>
<td>{stopLon}</td> <td>{stopLon}</td>
<td>{locationType}</td> <td>{locationType}</td>
<td>{parentStation}</td> <td>{parentStation}</td>
<td>{wheelchairBoarding}</td> <td>{wheelchairBoarding}</td>
<td>{platformCode}</td> <td>{platformCode}</td>
<td>{zoneId}</td> <td>{zoneId}</td>
</tr> </tr>
); );
}; }
StopsTableEntry.propTypes = { StopsTableEntry.propTypes = {
stopId: PropTypes.string, stopId: PropTypes.string,
stopCode: PropTypes.string, stopCode: PropTypes.string,
stopName: PropTypes.string, stopName: PropTypes.string,
stopDesc: PropTypes.string, stopDesc: PropTypes.string,
stopLat: PropTypes.number, stopLat: PropTypes.number,
stopLon: PropTypes.number, stopLon: PropTypes.number,
locationType: PropTypes.number, locationType: PropTypes.number,
parentStation: PropTypes.string, parentStation: PropTypes.string,
wheelchairBoarding: PropTypes.number, wheelchairBoarding: PropTypes.number,
platformCode: PropTypes.string, platformCode: PropTypes.string,
zoneId: PropTypes.string zoneId: PropTypes.string,
}; };
export default StopsTableEntry; export default StopsTableEntry;

View File

@ -1,21 +1,21 @@
import React from 'react'; import React from 'react';
const StopsTableHead = () => { function StopsTableHead() {
return ( return (
<tr> <tr>
<th>stop_id</th> <th>stop_id</th>
<th>stop_code</th> <th>stop_code</th>
<th>stop_name</th> <th>stop_name</th>
<th>stop_desc</th> <th>stop_desc</th>
<th>stop_lat</th> <th>stop_lat</th>
<th>stop_lon</th> <th>stop_lon</th>
<th>location_type</th> <th>location_type</th>
<th>parent_station</th> <th>parent_station</th>
<th>wheelchair_boarding</th> <th>wheelchair_boarding</th>
<th>platform_code</th> <th>platform_code</th>
<th>zone_id</th> <th>zone_id</th>
</tr> </tr>
); );
}; }
export default StopsTableHead; export default StopsTableHead;

View File

@ -13,223 +13,222 @@ import StopsEntry from './stops-table-entry';
import StopTimesEntry from './stop-times-table-entry'; import StopTimesEntry from './stop-times-table-entry';
import TransfersEntry from './transfers-table-entry'; import TransfersEntry from './transfers-table-entry';
import TripsEntry from './trips-table-entry'; import TripsEntry from './trips-table-entry';
/*the simplest way to define a component is to write a JavaScript function*/ /* the simplest way to define a component is to write a JavaScript function */
/*destructure props object*/ /* destructure props object */
function TableEntrySwitch ({ aryData, name }) { function TableEntrySwitch({ aryData, name }) {
/*TODO add table entry for pathways*/ /* TODO add table entry for pathways */
if (aryData.length > 0) { if (aryData.length > 0) {
//iterate over array // iterate over array
return aryData.map((item, index) => { return aryData.map((item, index) => {
switch (name) { switch (name) {
case 'agency': case 'agency':
return ( return (
<AgencyEntry <AgencyEntry
agencyId={item.agency_id} agencyId={item.agency_id}
agencyName={item.agency_name} agencyName={item.agency_name}
agencyUrl={item.agency_url} agencyUrl={item.agency_url}
agencyTimezone={item.agency_timezone} agencyTimezone={item.agency_timezone}
agencyLanguage={item.agency_language} agencyLanguage={item.agency_language}
agencyPhone={item.agency_phone} agencyPhone={item.agency_phone}
key={index} key={index}
/> />
); );
break; break;
case 'agency-id-name': case 'agency-id-name':
return ( return (
<AgencyIdNameEntry <AgencyIdNameEntry
agencyId={item.agency_id} agencyId={item.agency_id}
agencyName={item.agency_name} agencyName={item.agency_name}
key={index} key={index}
/> />
); );
break; break;
case 'calendar': case 'calendar':
return ( return (
<CalendarEntry <CalendarEntry
serviceId={item.service_id} serviceId={item.service_id}
monday={item.monday} monday={item.monday}
tuesday={item.tuesday} tuesday={item.tuesday}
wednesday={item.wednesday} wednesday={item.wednesday}
thursday={item.thursday} thursday={item.thursday}
friday={item.friday} friday={item.friday}
saturday={item.saturday} saturday={item.saturday}
sunday={item.sunday} sunday={item.sunday}
startDate={item.start_date} startDate={item.start_date}
endDate={item.end_date} endDate={item.end_date}
key={index} key={index}
/> />
); );
break; break;
case 'calendar_dates': case 'calendar_dates':
return ( return (
<CalendarDatesEntry <CalendarDatesEntry
serviceId={item.service_id} serviceId={item.service_id}
date={item.date} date={item.date}
exceptionType={item.exception_type} exceptionType={item.exception_type}
key={index} key={index}
/> />
); );
break; break;
case 'frequencies': case 'frequencies':
return ( return (
<FrequenciesEntry <FrequenciesEntry
tripId={item.trip_id} tripId={item.trip_id}
startTime={item.start_time} startTime={item.start_time}
endTime={item.end_time} endTime={item.end_time}
headwaySecs={item.headway_secs} headwaySecs={item.headway_secs}
exactTimes={item.exact_times} exactTimes={item.exact_times}
key={index} key={index}
/> />
); );
break; break;
case 'levels': case 'levels':
return ( return (
<LevelsEntry <LevelsEntry
levelId={item.level_id} levelId={item.level_id}
levelIndex={item.level_index} levelIndex={item.level_index}
key={index} key={index}
/> />
); );
break; break;
case 'pathways': case 'pathways':
return ( return (
<PathwaysEntry <PathwaysEntry
pathwayId={item.pathway_id} pathwayId={item.pathway_id}
fromStopId={item.from_stop_id} fromStopId={item.from_stop_id}
toStopId={item.to_stop_id} toStopId={item.to_stop_id}
pathwayMode={item.pathway_mode} pathwayMode={item.pathway_mode}
isBidirectional={item.is_bidirectional} isBidirectional={item.is_bidirectional}
length={item.length} length={item.length}
traversalTime={item.traversal_time} traversalTime={item.traversal_time}
stairCount={item.stair_count} stairCount={item.stair_count}
maxSlope={item.max_slope} maxSlope={item.max_slope}
minWidth={item.min_width} minWidth={item.min_width}
signpostedAs={item.signposted_as} signpostedAs={item.signposted_as}
reversedSignpostedAs={item.reversed_signposted_as} reversedSignpostedAs={item.reversed_signposted_as}
key={index} key={index}
/> />
); );
break; break;
case 'routes': case 'routes':
return ( return (
<RoutesEntry <RoutesEntry
routeId={item.route_id} routeId={item.route_id}
agencyId={item.agency_id} agencyId={item.agency_id}
routeShortName={item.route_short_name} routeShortName={item.route_short_name}
routeLongName={item.route_long_name} routeLongName={item.route_long_name}
routeType={item.route_type} routeType={item.route_type}
routeColor={item.route_color} routeColor={item.route_color}
routeTextColor={item.route_text_color} routeTextColor={item.route_text_color}
routeDesc={item.route_desc} routeDesc={item.route_desc}
key={index} key={index}
/> />
); );
break; break;
case 'shapes': case 'shapes':
return ( return (
<ShapesEntry <ShapesEntry
shapeId={item.shape_id} shapeId={item.shape_id}
shapePtLat={item.shape_pt_lat} shapePtLat={item.shape_pt_lat}
shapePtLon={item.shape_pt_lon} shapePtLon={item.shape_pt_lon}
shapePtSequence={item.shape_pt_sequence} shapePtSequence={item.shape_pt_sequence}
key={index} key={index}
/> />
); );
break; break;
case 'stops': case 'stops':
return ( return (
<StopsEntry <StopsEntry
stopId={item.stop_id} stopId={item.stop_id}
stopCode={item.stop_code} stopCode={item.stop_code}
stopName={item.stop_name} stopName={item.stop_name}
stopDesc={item.stop_desc} stopDesc={item.stop_desc}
stopLat={item.stop_lat} stopLat={item.stop_lat}
stopLon={item.stop_lon} stopLon={item.stop_lon}
locationType={item.location_type} locationType={item.location_type}
parentStation={item.parent_station} parentStation={item.parent_station}
wheelchairBoarding={item.wheelchair_boarding} wheelchairBoarding={item.wheelchair_boarding}
platformCode={item.platform_code} platformCode={item.platform_code}
zoneId={item.zone_id} zoneId={item.zone_id}
key={index} key={index}
/> />
); );
break; break;
case 'stop_times': case 'stop_times':
let arrivalTime = item.arrival_time; const arrivalTime = item.arrival_time;
/*TODO Why is this condition neccessary?*/ /* TODO Why is this condition neccessary? */
if (arrivalTime) { if (arrivalTime) {
return ( return (
<StopTimesEntry <StopTimesEntry
tripId={item.trip_id} tripId={item.trip_id}
arrivalTimeHours={item.arrival_time['hours']} arrivalTimeHours={item.arrival_time.hours}
arrivalTimeMinutes={item.arrival_time['minutes']} arrivalTimeMinutes={item.arrival_time.minutes}
departureTimeHours={item.departure_time['hours']} departureTimeHours={item.departure_time.hours}
departureTimeMinutes={item.departure_time['minutes']} departureTimeMinutes={item.departure_time.minutes}
stopId={item.stop_id} stopId={item.stop_id}
stopSequence={item.stop_sequence} stopSequence={item.stop_sequence}
pickupType={item.pickup_type} pickupType={item.pickup_type}
dropOffType={item.drop_off_type} dropOffType={item.drop_off_type}
stopHeadsign={item.stop_headsign} stopHeadsign={item.stop_headsign}
key={index} key={index}
/> />
); );
} else { }
return ( return (
<StopTimesEntry <StopTimesEntry
tripId={item.trip_id} tripId={item.trip_id}
stopId={item.stop_id} stopId={item.stop_id}
stopSequence={item.stop_sequence} stopSequence={item.stop_sequence}
pickupType={item.pickup_type} pickupType={item.pickup_type}
dropOffType={item.drop_off_type} dropOffType={item.drop_off_type}
stopHeadsign={item.stop_headsign} stopHeadsign={item.stop_headsign}
key={index} key={index}
/> />
); );
}
break; break;
case 'transfers': case 'transfers':
return ( return (
<TransfersEntry <TransfersEntry
fromStopId={item.from_stop_id} fromStopId={item.from_stop_id}
toStopId={item.to_stop_id} toStopId={item.to_stop_id}
fromRouteId={item.from_route_id} fromRouteId={item.from_route_id}
toRouteId={item.to_route_id} toRouteId={item.to_route_id}
fromTripId={item.from_trip_id} fromTripId={item.from_trip_id}
toTripId={item.to_trip_id} toTripId={item.to_trip_id}
transferType={item.transfer_type} transferType={item.transfer_type}
minTransferTime={item.min_transfer_time} minTransferTime={item.min_transfer_time}
key={index} key={index}
/> />
); );
break; break;
case 'trips': case 'trips':
return ( return (
<TripsEntry <TripsEntry
routeId={item.route_id} routeId={item.route_id}
serviceId={item.service_id} serviceId={item.service_id}
tripId={item.trip_id} tripId={item.trip_id}
tripHeadsign={item.trip_headsign} tripHeadsign={item.trip_headsign}
tripShortName={item.trip_short_name} tripShortName={item.trip_short_name}
directionId={item.direction_id} directionId={item.direction_id}
blockId={item.block_id} blockId={item.block_id}
shapeId={item.shape_id} shapeId={item.shape_id}
wheelchairAccessible={item.wheelchair_accessible} wheelchairAccessible={item.wheelchair_accessible}
bikesAllowed={item.bikes_allowed} bikesAllowed={item.bikes_allowed}
key={index} key={index}
/> />
); );
break; break;
default: default:
console.error(`${name} unknown`); console.error(`${name} unknown`);
return null; return null;
} }
}); });
}else{ }
return null; return null;
}
} }
TableEntrySwitch.propTypes = { TableEntrySwitch.propTypes = {
aryData: PropTypes.array, aryData: PropTypes.array,
name: PropTypes.string name: PropTypes.string,
}; };
export default TableEntrySwitch; export default TableEntrySwitch;

View File

@ -13,55 +13,55 @@ import StopsHead from './stops-table-head';
import StopTimesHead from './stop-times-table-head'; import StopTimesHead from './stop-times-table-head';
import TransfersHead from './transfers-table-head'; import TransfersHead from './transfers-table-head';
import TripsHead from './trips-table-head'; import TripsHead from './trips-table-head';
/*the simplest way to define a component is to write a JavaScript function*/ /* the simplest way to define a component is to write a JavaScript function */
/*destructure props object*/ /* destructure props object */
function TableHeadSwitch ({ name }) { function TableHeadSwitch({ name }) {
switch (name) { switch (name) {
case 'agency': case 'agency':
return <AgencyHead />; return <AgencyHead />;
break; break;
case 'agency-id-name': case 'agency-id-name':
return <AgencyIdNameHead />; return <AgencyIdNameHead />;
break; break;
case 'calendar': case 'calendar':
return <CalendarHead />; return <CalendarHead />;
break; break;
case 'calendar_dates': case 'calendar_dates':
return <CalendarDatesHead />; return <CalendarDatesHead />;
break; break;
case 'frequencies': case 'frequencies':
return <FrequenciesHead />; return <FrequenciesHead />;
break; break;
case 'levels': case 'levels':
return <LevelsHead />; return <LevelsHead />;
break; break;
case 'pathways': case 'pathways':
return <PathwaysHead />; return <PathwaysHead />;
break; break;
case 'routes': case 'routes':
return <RoutesHead />; return <RoutesHead />;
break; break;
case 'shapes': case 'shapes':
return <ShapesHead />; return <ShapesHead />;
break; break;
case 'stops': case 'stops':
return <StopsHead />; return <StopsHead />;
break; break;
case 'stop_times': case 'stop_times':
return <StopTimesHead />; return <StopTimesHead />;
break; break;
case 'transfers': case 'transfers':
return <TransfersHead />; return <TransfersHead />;
break; break;
case 'trips': case 'trips':
return <TripsHead />; return <TripsHead />;
break; break;
default: default:
console.error(`${name} unknown`); console.error(`${name} unknown`);
return null; return null;
} }
} }
TableHeadSwitch.propTypes = { TableHeadSwitch.propTypes = {
name: PropTypes.string name: PropTypes.string,
}; };
export default TableHeadSwitch; export default TableHeadSwitch;

View File

@ -6,54 +6,50 @@ import TableSwitchBody from './table-switch-trip-calendar-body';
import config from '../config'; import config from '../config';
export default function TablePageBody({ agencyIdName }) { export default function TablePageBody({ agencyIdName }) {
if (agencyIdName !== undefined) {
if(agencyIdName !== undefined){ // console.log('TablePageBody agencyIdName:'+JSON.stringify(agencyIdName));
//console.log('TablePageBody agencyIdName:'+JSON.stringify(agencyIdName)); // console.log('TablePageBody agencyIdName.length:'+agencyIdName.length);
//console.log('TablePageBody agencyIdName.length:'+agencyIdName.length); const agencyId = agencyIdName.agency_id;
const agencyId=agencyIdName.agency_id; // console.log('TablePageBody agencyId:'+agencyId);
//console.log('TablePageBody agencyId:'+agencyId); const agencyName = agencyIdName.agency_name;
const agencyName=agencyIdName.agency_name; // console.log('TablePageBody agencyName:'+agencyName);
//console.log('TablePageBody agencyName:'+agencyName); /* store and initialise data in function component state */
/*store and initialise data in function component state*/
const [tripCalendar, setTripCalendar] = useState({}); const [tripCalendar, setTripCalendar] = useState({});
const getTripCalendar = async () => { const getTripCalendar = async () => {
try { try {
/*get trip calendar*/ /* get trip calendar */
const address=`${config.API}trip-calendar-by-agency-id?agencyid=${agencyId}`; const address = `${config.API}trip-calendar-by-agency-id?agencyid=${agencyId}`;
//console.log('address:'+address); // console.log('address:'+address);
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/ /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const res = await axios.get(address); const res = await axios.get(address);
let aryTripCalendar = res.data; const aryTripCalendar = res.data;
//console.log('aryTripCalendar.length:'+aryTripCalendar.length); // console.log('aryTripCalendar.length:'+aryTripCalendar.length);
setTripCalendar(aryTripCalendar); setTripCalendar(aryTripCalendar);
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
}; };
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ /* this hook is run after a DOM update. Changing state might result in an infinite loop */
/*hook need to be placed in body of the function component in which it is used*/ /* hook need to be placed in body of the function component in which it is used */
useEffect(() => { useEffect(() => {
getTripCalendar(); getTripCalendar();
/*use an empty dependency array to ensure the hook is running only once*/ /* use an empty dependency array to ensure the hook is running only once */
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []); }, []);
return ( return (
<> <TableSwitchBody
<TableSwitchBody tripCalendar={tripCalendar}
tripCalendar={tripCalendar} agencyId={agencyId}
agencyId={agencyId} agencyName={agencyName}
agencyName={agencyName} />
/> );
</> }
); return <p>Table Page Body loading...</p>;
}else{ }
return <p>Table Page Body loading...</p>
}
};
TablePageBody.propTypes = { TablePageBody.propTypes = {
agencyIdName: PropTypes.object agencyIdName: PropTypes.object,
}; };

View File

@ -6,66 +6,57 @@ import TableSwitchHead from './table-switch-trip-calendar-head';
import config from '../config'; import config from '../config';
export default function TablePageHead({ agencyIdName }) { export default function TablePageHead({ agencyIdName }) {
if (agencyIdName !== undefined) {
// console.log('TablePageHead agencyIdName:'+JSON.stringify(agencyIdName));
// console.log('TablePageHead agencyIdName.length:'+agencyIdName.length);
const agencyId = agencyIdName.agency_id;
// console.log('TablePageHead agencyId:'+agencyId);
const agencyName = agencyIdName.agency_name;
// console.log('TablePageHead agencyName:'+agencyName);
if(agencyIdName !== undefined){ /* store and initialise data in function component state */
//console.log('TablePageHead agencyIdName:'+JSON.stringify(agencyIdName)); const [tripCalendar, setTripCalendar] = useState({});
//console.log('TablePageHead agencyIdName.length:'+agencyIdName.length);
const agencyId=agencyIdName.agency_id;
//console.log('TablePageHead agencyId:'+agencyId);
const agencyName=agencyIdName.agency_name;
//console.log('TablePageHead agencyName:'+agencyName);
/*store and initialise data in function component state*/ const getTripCalendar = async () => {
const [tripCalendar, setTripCalendar] = useState({}); try {
/* get trip calendar */
const address = `${config.API}trip-calendar-by-agency-id?agencyid=${agencyId}`;
// console.log('TablePageHead address:'+address);
const getTripCalendar = async () => { /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const res = await axios.get(address);
try { const aryTripCalendar = res.data;
/*get trip calendar*/ // console.log('TablePageHead aryTripCalendar.length:'+aryTripCalendar.length);
const address=`${config.API}trip-calendar-by-agency-id?agencyid=${agencyId}`;
//console.log('TablePageHead address:'+address);
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const res = await axios.get(address);
let aryTripCalendar = res.data;
//console.log('TablePageHead aryTripCalendar.length:'+aryTripCalendar.length);
setTripCalendar(aryTripCalendar); setTripCalendar(aryTripCalendar);
} catch (err) {
console.error(`err.message: ${err.message}`);
}
};
} catch (err) { /* this hook is run after a DOM update. Changing state might result in an infinite loop */
console.error('err.message: ' + err.message);
}
};
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ /* hook need to be placed in body of the function component in which it is used */
/*hook need to be placed in body of the function component in which it is used*/ useEffect(() => {
getTripCalendar();
useEffect(() => { /* use an empty dependency array to ensure the hook is running only once */
getTripCalendar(); /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []);
/*use an empty dependency array to ensure the hook is running only once*/ return (
<TableSwitchHead
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ tripCalendar={tripCalendar}
agencyId={agencyId}
}, []); agencyName={agencyName}
/>
return ( );
<> }
<TableSwitchHead return <p>loading...</p>;
tripCalendar={tripCalendar} }
agencyId={agencyId}
agencyName={agencyName}
/>
</>
);
}else{
return <p>loading...</p>
}
};
TablePageHead.propTypes = { TablePageHead.propTypes = {
agencyIdName: PropTypes.object agencyIdName: PropTypes.object,
}; };

View File

@ -5,58 +5,60 @@ import PropTypes from 'prop-types';
import Input from './input'; import Input from './input';
import TableSwitch from './table-switch'; import TableSwitch from './table-switch';
import Select from './select'; import Select from './select';
import {selectOptions} from '../utils/select-options'; import { selectOptions } from '../utils/select-options';
const TablePage = ({ name }) => { function TablePage({ name }) {
/*store and initialise data in function component state*/ /* store and initialise data in function component state */
const [oset, setOset] = useState(1); const [oset, setOset] = useState(1);
const [limit, setLimit] = useState(parseInt(selectOptions[0],10)); const [limit, setLimit] = useState(parseInt(selectOptions[0], 10));
const [searchField, setSearchField] = useState(''); const [searchField, setSearchField] = useState('');
const handleClickPrev = () => { const handleClickPrev = () => {
setOset((oset) => (oset > 1 ? --oset : oset)); setOset((oset) => (oset > 1 ? --oset : oset));
}; };
const handleClickNext = () => { const handleClickNext = () => {
setOset((oset) => ++oset); setOset((oset) => ++oset);
}; };
const handleChangeLimit = (event) => { const handleChangeLimit = (event) => {
setLimit((limit) => parseInt(event.target.value,10)); setLimit((limit) => parseInt(event.target.value, 10));
}; };
const handleSearch = (e) => { const handleSearch = (e) => {
setSearchField((searchField)=>e.target.value); setSearchField((searchField) => e.target.value);
}; };
if (name && name.indexOf(' ') === -1) { if (name && name.indexOf(' ') === -1) {
return <> return (
<button onClick={handleClickPrev}>prev</button>&nbsp; <>
<button onClick={handleClickNext}>next</button> <button onClick={handleClickPrev}>prev</button>
<Select &nbsp;
defaultValue={selectOptions[0]} <button onClick={handleClickNext}>next</button>
id="tablePageLimit" <Select
name="tablePageLimit" defaultValue={selectOptions[0]}
onChange={handleChangeLimit} id="tablePageLimit"
options={selectOptions} name="tablePageLimit"
/> onChange={handleChangeLimit}
<Input options={selectOptions}
id="tablePageSearch" />
name="tablePageSearch" <Input
onChange={handleSearch} id="tablePageSearch"
placeholder="Search table globally" name="tablePageSearch"
type="text" onChange={handleSearch}
title="Enter search value" placeholder="Search table globally"
value={searchField} type="text"
/> title="Enter search value"
<TableSwitch value={searchField}
name={name} />
isFetched={false} <TableSwitch
oset={oset} name={name}
limit={limit} isFetched={false}
filter={searchField} oset={oset}
/> limit={limit}
</>; filter={searchField}
} else { />
return <p>Loading...</p>; </>
} );
}; }
return <p>Loading...</p>;
}
TablePage.propTypes = { TablePage.propTypes = {
name: PropTypes.string name: PropTypes.string,
}; };
export default TablePage; export default TablePage;

View File

@ -2,39 +2,36 @@ import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import Table from 'react-bootstrap/Table'; import Table from 'react-bootstrap/Table';
import TableEntry from './agency-id-name-table-entry'; import TableEntry from './agency-id-name-table-entry';
/*the simplest way to define a component is to write a JavaScript function*/ /* the simplest way to define a component is to write a JavaScript function */
/*destructure props object*/ /* destructure props object */
function TableSwitchBody ({ function TableSwitchBody({
tripCalendar, tripCalendar,
agencyId, agencyId,
agencyName agencyName,
}) { }) {
if(tripCalendar!==undefined && if (tripCalendar !== undefined
agencyId!==undefined && && agencyId !== undefined
agencyName!==undefined){ && agencyName !== undefined) {
//console.log('TableSwitchBody agencyId: '+agencyId); // console.log('TableSwitchBody agencyId: '+agencyId);
//console.log('TableSwitchBody agencyName: '+agencyName); // console.log('TableSwitchBody agencyName: '+agencyName);
//console.log('TableSwitchBody tripCalendar.length: '+Object.keys(tripCalendar).length); // console.log('TableSwitchBody tripCalendar.length: '+Object.keys(tripCalendar).length);
/*return a React element*/ /* return a React element */
return ( return (
<> <tbody>
<tbody> <TableEntry
<TableEntry agencyId={agencyId}
agencyId={agencyId} agencyName={agencyName}
agencyName={agencyName} tripCalendar={tripCalendar}
tripCalendar={tripCalendar} />
/> </tbody>
</tbody> );
</> }
); // console.log('TableSwitchBody waiting for prop');
}else{ return <p>Table Switch Body loading...</p>;
//console.log('TableSwitchBody waiting for prop');
return <p>Table Switch Body loading...</p>
}
} }
TableSwitchBody.propTypes = { TableSwitchBody.propTypes = {
tripCalendar: PropTypes.object, tripCalendar: PropTypes.object,
agencyId: PropTypes.string, agencyId: PropTypes.string,
agencyName: PropTypes.string agencyName: PropTypes.string,
}; };
export default TableSwitchBody; export default TableSwitchBody;

View File

@ -3,25 +3,22 @@ import PropTypes from 'prop-types';
import TableHead from './agency-id-name-table-head'; import TableHead from './agency-id-name-table-head';
/*the simplest way to define a component is to write a JavaScript function*/ /* the simplest way to define a component is to write a JavaScript function */
/*destructure props object*/ /* destructure props object */
export default function TableSwitchHead({tripCalendar}) { export default function TableSwitchHead({ tripCalendar }) {
if(tripCalendar!==undefined){ if (tripCalendar !== undefined) {
/*return a React element*/ /* return a React element */
return ( return (
<> <thead>
<thead> <TableHead
<TableHead tripCalendar={tripCalendar}
tripCalendar={tripCalendar} />
/> </thead>
</thead> );
</> }
); return <p>loading...</p>;
}else{
return <p>loading...</p>
}
} }
TableSwitchHead.propTypes = { TableSwitchHead.propTypes = {
tripCalendar: PropTypes.object tripCalendar: PropTypes.object,
}; };

View File

@ -5,103 +5,79 @@ import PropTypes from 'prop-types';
import TableHeadSwitch from './table-head-switch'; import TableHeadSwitch from './table-head-switch';
import TableEntrySwitch from './table-entry-switch'; import TableEntrySwitch from './table-entry-switch';
import config from '../config'; import config from '../config';
import {filterData} from '../utils/filter-data'; import { filterData } from '../utils/filter-data';
/* the simplest way to define a component is to write a JavaScript function */
/* destructure props object */
function TableSwitch({
name, isFetched, oset, limit, filter,
}) {
// console.log('TableSwitch name: '+name);
// console.log('TableSwitch isFetched: '+isFetched);
// console.log('TableSwitch filter: '+filter);
const [ary, setAry] = useState([]);
const [aryFiltered, setAryFiltered] = useState([]);
const [fetchCompleted, setFetchCompleted] = useState(isFetched);
/* fetch ary in a JavaScript function */
const fetch = async () => {
try {
/* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
// fetch data only if user selection is unequal default value
if (name.length > 0 && name.indexOf(' ') === -1) {
const address = `${config.API}${name}-oset-limit?oset=${oset}&limit=${limit}`;
const res = await axios.get(address);
if (res.data) {
/*the simplest way to define a component is to write a JavaScript function*/
/*destructure props object*/
function TableSwitch ({name, isFetched, oset, limit, filter}) {
//console.log('TableSwitch name: '+name);
//console.log('TableSwitch isFetched: '+isFetched);
//console.log('TableSwitch filter: '+filter);
const [ary, setAry] = useState([]);
const [aryFiltered, setAryFiltered] = useState([]);
const [fetchCompleted, setFetchCompleted] = useState(isFetched);
/*fetch ary in a JavaScript function*/
const fetch = async () => {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
//fetch data only if user selection is unequal default value
if (name.length>0 && name.indexOf(' ') === -1) {
const address = `${config.API}${name}-oset-limit?oset=${oset}&limit=${limit}`;
const res = await axios.get(address);
if(res.data){
setAry((ary) => res.data); setAry((ary) => res.data);
let data=filterData(res.data,name,filter); const data = filterData(res.data, name, filter);
setAryFiltered((aryFiltered) => data); setAryFiltered((aryFiltered) => data);
}else{ } else {
console.error('fetch() res NOT available'); console.error('fetch() res NOT available');
} }
} else { } else {
console.error(`name ${name} not valid`); console.error(`name ${name} not valid`);
setAry((ary) => []); setAry((ary) => []);
} }
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
setAry((ary) => []); setAry((ary) => []);
setAryFiltered((aryFiltered) => []); setAryFiltered((aryFiltered) => []);
}
};
useEffect(()=>{
setAryFiltered((aryFiltered)=>{
let filtered=filterData(ary,name,filter);
return filtered;
});
},[filter]);
useEffect(() => {
/*effect goes here*/
fetch();
setFetchCompleted((fetchCompleted)=>true);
/*use an empty dependency array to ensure the hook is running only once*/
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
}, [name,oset,limit]);
if(fetchCompleted && aryFiltered.length > -1){
/*return a React element*/
return (
<>
<table>
<thead>
<TableHeadSwitch name={name}/>
</thead>
<tbody>
<TableEntrySwitch aryData={aryFiltered} name={name}/>
</tbody>
</table>
</>
);
}else{
return null;
} }
};
useEffect(() => {
setAryFiltered((aryFiltered) => {
const filtered = filterData(ary, name, filter);
return filtered;
});
}, [filter]);
useEffect(() => {
/* effect goes here */
fetch();
setFetchCompleted((fetchCompleted) => true);
/* use an empty dependency array to ensure the hook is running only once */
/* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [name, oset, limit]);
if (fetchCompleted && aryFiltered.length > -1) {
/* return a React element */
return (
<table>
<thead>
<TableHeadSwitch name={name} />
</thead>
<tbody>
<TableEntrySwitch aryData={aryFiltered} name={name} />
</tbody>
</table>
);
}
return null;
} }
TableSwitch.propTypes = { TableSwitch.propTypes = {
name: PropTypes.string, name: PropTypes.string,
isFetched: PropTypes.bool, isFetched: PropTypes.bool,
offset: PropTypes.number, offset: PropTypes.number,
limit: PropTypes.number, limit: PropTypes.number,
filter: PropTypes.string filter: PropTypes.string,
}; };
export default TableSwitch; export default TableSwitch;

View File

@ -4,13 +4,12 @@ import PropTypes from 'prop-types';
import FileSelection from './file-selection'; import FileSelection from './file-selection';
export default function Tables({ data }) { export default function Tables({ data }) {
if (data.length > 0) { if (data.length > 0) {
return <FileSelection options={data} />; return <FileSelection options={data} />;
} else { }
return <p>Selection loading...</p>; return <p>Selection loading...</p>;
} }
};
Tables.propTypes = { Tables.propTypes = {
data: PropTypes.array data: PropTypes.array,
}; };

View File

@ -1,40 +1,40 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const TransfersTableEntry = ({ function TransfersTableEntry({
fromStopId, fromStopId,
toStopId, toStopId,
fromRouteId, fromRouteId,
toRouteId, toRouteId,
fromTripId, fromTripId,
toTripId, toTripId,
transferType, transferType,
minTransferTime minTransferTime,
}) => { }) {
return ( return (
<tr> <tr>
<td>{fromStopId}</td> <td>{fromStopId}</td>
<td>{toStopId}</td> <td>{toStopId}</td>
<td>{fromRouteId}</td> <td>{fromRouteId}</td>
<td>{toRouteId}</td> <td>{toRouteId}</td>
<td>{fromTripId}</td> <td>{fromTripId}</td>
<td>{toTripId}</td> <td>{toTripId}</td>
<td>{transferType}</td> <td>{transferType}</td>
<td>{minTransferTime}</td> <td>{minTransferTime}</td>
</tr> </tr>
); );
}; }
TransfersTableEntry.propTypes = { TransfersTableEntry.propTypes = {
fromStopId: PropTypes.string, fromStopId: PropTypes.string,
toStopId: PropTypes.string, toStopId: PropTypes.string,
fromRouteId: PropTypes.string, fromRouteId: PropTypes.string,
toRouteId: PropTypes.string, toRouteId: PropTypes.string,
fromTripId: PropTypes.string, fromTripId: PropTypes.string,
toTripId: PropTypes.string, toTripId: PropTypes.string,
transferType: PropTypes.number, transferType: PropTypes.number,
minTransferTime: PropTypes.number minTransferTime: PropTypes.number,
}; };
export default TransfersTableEntry; export default TransfersTableEntry;

View File

@ -1,18 +1,18 @@
import React from 'react'; import React from 'react';
const TransfersTableHead = () => { function TransfersTableHead() {
return ( return (
<tr> <tr>
<th>from_stop_id</th> <th>from_stop_id</th>
<th>to_stop_id</th> <th>to_stop_id</th>
<th>from_route_id</th> <th>from_route_id</th>
<th>to_route_id</th> <th>to_route_id</th>
<th>from_trip_id</th> <th>from_trip_id</th>
<th>to_trip_id</th> <th>to_trip_id</th>
<th>transfer_type</th> <th>transfer_type</th>
<th>min_transfer_time</th> <th>min_transfer_time</th>
</tr> </tr>
); );
}; }
export default TransfersTableHead; export default TransfersTableHead;

View File

@ -1,33 +1,53 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default function TripUpdatesRouteDayTableEntries ({array}) { export default function TripUpdatesRouteDayTableEntries({ array }) {
if ( array !== undefined && array !== null && array.length > 0 ) { if (array !== undefined && array !== null && array.length > 0) {
// TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
//TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql? // iterate over array
//iterate over array return array.map((item, index) => (
return array.map((item, index) => { <tr
return ( key={index}
<tr >
key={index} <td>
> {item.agency_name}
<td>{item.agency_name}&nbsp;|</td> &nbsp;|
<td>{item.agency_id}&nbsp;|</td> </td>
<td>{item.route_id}&nbsp;|</td> <td>
<td>{item.route_short_name}&nbsp;|</td> {item.agency_id}
<td>{item.trip_id}&nbsp;|</td> &nbsp;|
<td>{item.trip_short_name}&nbsp;|</td> </td>
<td>{item.trip_headsign}&nbsp;|</td> <td>
<td>{item.timestamp_pgsql}&nbsp;|</td> {item.route_id}
</tr> &nbsp;|
); </td>
}); <td>
}else{ {item.route_short_name}
//data is empty &nbsp;|
return null; </td>
} <td>
}; {item.trip_id}
&nbsp;|
</td>
<td>
{item.trip_short_name}
&nbsp;|
</td>
<td>
{item.trip_headsign}
&nbsp;|
</td>
<td>
{item.timestamp_pgsql}
&nbsp;|
</td>
</tr>
));
}
// data is empty
return null;
}
TripUpdatesRouteDayTableEntries.propTypes = { TripUpdatesRouteDayTableEntries.propTypes = {
array: PropTypes.array array: PropTypes.array,
}; };

View File

@ -3,43 +3,47 @@ import PropTypes from 'prop-types';
import TripUpdatesRouteDayTableEntries from './trip-updates-route-day-table-entries'; import TripUpdatesRouteDayTableEntries from './trip-updates-route-day-table-entries';
/*destructure props object*/ /* destructure props object */
export default function TripUpdatesRouteDayTable ({array, title, date}){ export default function TripUpdatesRouteDayTable({ array, title, date }) {
if (array !== undefined && array !== null) {
if ( array !== undefined && array !== null) { /* return a React element */
/*return a React element*/ return (
return ( <>
<> <p>
<p>Table of {array.length}&nbsp;{title} for {date}:</p> Table of
<table> {array.length}
<thead> {title}
<tr> {' '}
<th>agency_name&nbsp;|</th> for
<th>agency_id&nbsp;|</th> {date}
<th>route_id&nbsp;|</th> :
<th>route_short_name&nbsp;|</th> </p>
<th>trip_id&nbsp;|</th> <table>
<th>trip_short_name&nbsp;|</th> <thead>
<th>trip_headsign&nbsp;|</th> <tr>
<th>timestamp_pgsql&nbsp;|</th> <th>agency_name&nbsp;|</th>
</tr> <th>agency_id&nbsp;|</th>
</thead> <th>route_id&nbsp;|</th>
<tbody> <th>route_short_name&nbsp;|</th>
<TripUpdatesRouteDayTableEntries array={array} /> <th>trip_id&nbsp;|</th>
</tbody> <th>trip_short_name&nbsp;|</th>
</table> <th>trip_headsign&nbsp;|</th>
</> <th>timestamp_pgsql&nbsp;|</th>
); </tr>
}else{ </thead>
return ( <tbody>
<> <TripUpdatesRouteDayTableEntries array={array} />
<p>loading...</p> </tbody>
</> </table>
); </>
} );
}; }
return (
<p>loading...</p>
);
}
TripUpdatesRouteDayTable.propTypes = { TripUpdatesRouteDayTable.propTypes = {
array: PropTypes.array, array: PropTypes.array,
title: PropTypes.string, title: PropTypes.string,
date: PropTypes.string date: PropTypes.string,
}; };

View File

@ -1,31 +1,28 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
export default function TableEntries ({array}) { export default function TableEntries({ array }) {
if ( array !== undefined && array !== null && array.length > 0 ) { if (array !== undefined && array !== null && array.length > 0) {
//iterate over array // iterate over array
return array.map((item, index) => { return array.map((item, index) => (
return ( <tr
<tr key={item.trip_id}
key={item.trip_id} >
> <td>{item.agency_name}</td>
<td>{item.agency_name}</td> <td>{item.agency_id}</td>
<td>{item.agency_id}</td> <td>{item.route_id}</td>
<td>{item.route_id}</td> <td>{item.route_short_name}</td>
<td>{item.route_short_name}</td> <td>{item.service_id}</td>
<td>{item.service_id}</td> <td>{item.trip_id}</td>
<td>{item.trip_id}</td> <td>{item.trip_short_name}</td>
<td>{item.trip_short_name}</td> <td>{item.trip_headsign}</td>
<td>{item.trip_headsign}</td> </tr>
</tr> ));
); }
}); // data is empty
}else{ return null;
//data is empty }
return null;
}
};
TableEntries.propTypes = { TableEntries.propTypes = {
array: PropTypes.array array: PropTypes.array,
}; };

View File

@ -3,42 +3,44 @@ import PropTypes from 'prop-types';
import TableEntries from './trips-route-day-table-entries'; import TableEntries from './trips-route-day-table-entries';
/*destructure props object*/ /* destructure props object */
export default function TripsRouteDayTable ({array, title}){ export default function TripsRouteDayTable({ array, title }) {
if (array !== undefined && array !== null) {
if ( array !== undefined && array !== null) { /* return a React element */
/*return a React element*/ return (
return ( <>
<> <p>
<p>Table of {array.length}&nbsp;{title} for today:</p> Table of
<table> {array.length}
<thead> {title}
<tr> {' '}
<th>agency_name</th> for today:
<th>agency_id</th> </p>
<th>route_id</th> <table>
<th>route_short_name</th> <thead>
<th>service_id</th> <tr>
<th>trip_id</th> <th>agency_name</th>
<th>trip_short_name</th> <th>agency_id</th>
<th>trip_headsign</th> <th>route_id</th>
</tr> <th>route_short_name</th>
</thead> <th>service_id</th>
<tbody> <th>trip_id</th>
<TableEntries array={array} /> <th>trip_short_name</th>
</tbody> <th>trip_headsign</th>
</table> </tr>
</> </thead>
); <tbody>
}else{ <TableEntries array={array} />
return ( </tbody>
<> </table>
<p>loading...</p> </>
</> );
); }
} return (
}; <p>loading...</p>
);
}
TripsRouteDayTable.propTypes = { TripsRouteDayTable.propTypes = {
array: PropTypes.array, array: PropTypes.array,
title: PropTypes.string title: PropTypes.string,
}; };

View File

@ -1,46 +1,46 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/*destructure props object*/ /* destructure props object */
const TripsTableEntry = ({ function TripsTableEntry({
routeId, routeId,
serviceId, serviceId,
tripId, tripId,
tripHeadsign, tripHeadsign,
tripShortName, tripShortName,
directionId, directionId,
blockId, blockId,
shapeId, shapeId,
wheelchairAccessible, wheelchairAccessible,
bikesAllowed bikesAllowed,
}) => { }) {
return ( return (
<tr> <tr>
<td>{routeId}</td> <td>{routeId}</td>
<td>{serviceId}</td> <td>{serviceId}</td>
<td>{tripId}</td> <td>{tripId}</td>
<td>{tripHeadsign}</td> <td>{tripHeadsign}</td>
<td>{tripShortName}</td> <td>{tripShortName}</td>
<td>{directionId ? 'true' : 'false'}</td> <td>{directionId ? 'true' : 'false'}</td>
<td>{blockId}</td> <td>{blockId}</td>
<td>{shapeId}</td> <td>{shapeId}</td>
<td>{wheelchairAccessible}</td> <td>{wheelchairAccessible}</td>
<td>{bikesAllowed}</td> <td>{bikesAllowed}</td>
</tr> </tr>
); );
}; }
TripsTableEntry.propTypes = { TripsTableEntry.propTypes = {
routeId: PropTypes.string, routeId: PropTypes.string,
serviceId: PropTypes.string, serviceId: PropTypes.string,
tripId: PropTypes.string, tripId: PropTypes.string,
tripHeadsign: PropTypes.string, tripHeadsign: PropTypes.string,
tripShortName: PropTypes.string, tripShortName: PropTypes.string,
directionId: PropTypes.number, directionId: PropTypes.number,
blockId: PropTypes.string, blockId: PropTypes.string,
shapeId: PropTypes.string, shapeId: PropTypes.string,
wheelchairAccessible: PropTypes.number, wheelchairAccessible: PropTypes.number,
bikesAllowed: PropTypes.number bikesAllowed: PropTypes.number,
}; };
export default TripsTableEntry; export default TripsTableEntry;

View File

@ -1,20 +1,20 @@
import React from 'react'; import React from 'react';
const TripsTableHead = () => { function TripsTableHead() {
return ( return (
<tr> <tr>
<th>route_id</th> <th>route_id</th>
<th>service_id</th> <th>service_id</th>
<th>trip_id</th> <th>trip_id</th>
<th>trip_headsign</th> <th>trip_headsign</th>
<th>trip_short_name</th> <th>trip_short_name</th>
<th>direction_id</th> <th>direction_id</th>
<th>block_id</th> <th>block_id</th>
<th>shape_id</th> <th>shape_id</th>
<th>wheelchair_accessible</th> <th>wheelchair_accessible</th>
<th>bikes_allowed</th> <th>bikes_allowed</th>
</tr> </tr>
); );
}; }
export default TripsTableHead; export default TripsTableHead;

View File

@ -1,4 +1,4 @@
export default { export default {
API: 'http://localhost:65529/', API: 'http://localhost:65529/',
GTFS_VALIDATOR_REPORT: 'https://www.v1gtfs.vbn.api.swingbe.de/gtfs-validator/report.html', GTFS_VALIDATOR_REPORT: 'https://www.v1gtfs.vbn.api.swingbe.de/gtfs-validator/report.html',
}; };

View File

@ -1,54 +1,57 @@
import React from 'react'; import React from 'react';
import { BrowserRouter as Router, Route, Routes, Navigate } from 'react-router-dom'; import {
BrowserRouter as Router, Route, Routes, Navigate,
} from 'react-router-dom';
import Header from './components/header'; import Header from './components/header';
import Home from './pages/homepage'; import Home from './pages/homepage';
import Files from './pages/files'; import Files from './pages/files';
//TODO Date [today] is not implemented! import Overview from './pages/overview'; // TODO Date [today] is not implemented! import Overview from './pages/overview';
import OverviewNext from './pages/overview-next'; import OverviewNext from './pages/overview-next';
//TODO Disable this route as it is not working! import Service from './pages/service'; // TODO Disable this route as it is not working! import Service from './pages/service';
//TODO Disable this route as it is way too much overhead for API and database! import TripCalendar from './pages/trip-calendar'; // TODO Disable this route as it is way too much overhead for API and database! import TripCalendar from './pages/trip-calendar';
import Realtime from './pages/realtime'; import Realtime from './pages/realtime';
//TODO import Trips from './pages/trips-route-day'; // TODO import Trips from './pages/trips-route-day';
import Contact from './pages/contact'; import Contact from './pages/contact';
import packageInfo from '../package.json' import packageInfo from '../package.json';
const VERSION = packageInfo.version; const VERSION = packageInfo.version;
export default function Main() { export default function Main() {
return ( return (
//Router is the router implementation for HTML5 browsers // Router is the router implementation for HTML5 browsers
//Link enables Routes on an anchor tag // Link enables Routes on an anchor tag
//Switch returns only the first matching route rather than all // Switch returns only the first matching route rather than all
//Route is the conditionally shown component //based on matching a path to a URL // Route is the conditionally shown component //based on matching a path to a URL
<Router> <Router>
<Header /> <Header />
<h1>GTFS-Display</h1> <h1>GTFS-Display</h1>
<p> <p>
This website processes GTFS Realtime and Schedule data. This website processes GTFS Realtime and Schedule data.
</p> </p>
<Routes> <Routes>
<Route exact path="/" element={<Home />} /> <Route exact path="/" element={<Home />} />
<Route exact path="/agency" element={<OverviewNext />} /> <Route exact path="/agency" element={<OverviewNext />} />
<Route exact path="/files" element={<Files />} /> <Route exact path="/files" element={<Files />} />
<Route exact path="/realtime" element={<Realtime />} /> <Route exact path="/realtime" element={<Realtime />} />
<Route exact path="/contact" element={<Contact />} /> <Route exact path="/contact" element={<Contact />} />
<Route path="*" element={<Navigate to="/" />} /> <Route path="*" element={<Navigate to="/" />} />
</Routes> </Routes>
</Router> </Router>
); );
/** TODO Is this route of any value? /** TODO Is this route of any value?
<Route exact path="/trips" element={<Trips />} /> <Route exact path="/trips" element={<Trips />} />
*/ */
/** TODO Date [today] is not implemented! /** TODO Date [today] is not implemented!
<Route exact path="/overview-today" element={<Overview />} /> <Route exact path="/overview-today" element={<Overview />} />
*/ */
/** TODO Disable this route as it is not working! /** TODO Disable this route as it is not working!
<Route exact path="/service" element={<Service />} /> <Route exact path="/service" element={<Service />} />
*/ */
/** TODO Disable this route as it is way too much overhead for API and database! /** TODO Disable this route as it is way too much overhead for API and database!
<Route exact path="/trip-calendar" element={<TripCalendar />} /> <Route exact path="/trip-calendar" element={<TripCalendar />} />
*/ */
}; }

View File

@ -7,92 +7,92 @@ import AgencySelect from '../components/agency-select';
import Input from '../components/input'; import Input from '../components/input';
export default function AgencyPerDay() { export default function AgencyPerDay() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const dateDefault = 'Select date'; const agencyNameDefault = 'Select GTFS agency_name';
const [date, setDate] = useState(dateDefault);
const agencyNameDefault = 'Select GTFS agency_name'; /* store and initialize data in function component state */
const [strngAgencyId, setStrngAgencyId] = useState(agencyNameDefault);
/*store and initialize data in function component state*/ const [rryAgencyPerDay, setRryAgencyPerDay] = useState([]);
const [strngAgencyId, setStrngAgencyId] = useState(agencyNameDefault); const [rryAgencies, setRryAgencies] = useState([]);
const [rryAgencyPerDay, setRryAgencyPerDay] = useState([]); // TODO How do we handle invalid date input?
const [rryAgencies, setRryAgencies] = useState([]); const handleDate = (e) => {
if (e.target.value.indexOf('2023') !== -1
|| e.target.value.indexOf('2024') !== -1) {
setDate((date) => e.target.value);
}
};
//TODO How do we handle invalid date input? const getRryAgencies = async () => {
const handleDate = (e) => { try {
if (e.target.value.indexOf('2023') !== -1 || /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
e.target.value.indexOf('2024') !== -1) {
setDate((date)=>e.target.value);
}
};
const getRryAgencies = async () => {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const address = `${config.API}agencyids`; const address = `${config.API}agencyids`;
//console.log('trip-updates-route-day res.data.length: address: ' + address); // console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
//console.log('trip-updates-route-day res.data.length: Agencies: ' + res.data.length); // console.log('trip-updates-route-day res.data.length: Agencies: ' + res.data.length);
setRryAgencies((rryAgencies) => res.data); setRryAgencies((rryAgencies) => res.data);
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
}; };
const getRryAgencyPerDay = async () => { const getRryAgencyPerDay = async () => {
if ( strngAgencyId !== agencyNameDefault && if (strngAgencyId !== agencyNameDefault
date !== dateDefault) { && date !== dateDefault) {
try { try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/ /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const address = `${config.API}trip-updates-by-agency-day?agencyid=${strngAgencyId}&day=${date}`; const address = `${config.API}trip-updates-by-agency-day?agencyid=${strngAgencyId}&day=${date}`;
//console.log('trip-updates-route-day res.data.length: address: ' + address); // console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) { if (res.data !== undefined && res.data !== null) {
//console.log('trip-updates-route-day res.data.length: AgencyPerDay: ' + res.data.length); // console.log('trip-updates-route-day res.data.length: AgencyPerDay: ' + res.data.length);
setRryAgencyPerDay((rryAgencyPerDay) => res.data); setRryAgencyPerDay((rryAgencyPerDay) => res.data);
} else { } else {
console.error('ERROR: trip-updates by routes and day request FAILED'); console.error('ERROR: trip-updates by routes and day request FAILED');
} }
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
} }
}; };
const handleChangeAgencyId = (event) => { const handleChangeAgencyId = (event) => {
//console.log('trip-updates-route-day: handleChangeAgencyId() value: ' + event.target.value); // console.log('trip-updates-route-day: handleChangeAgencyId() value: ' + event.target.value);
setStrngAgencyId((strngAgencyId) => event.target.value); setStrngAgencyId((strngAgencyId) => event.target.value);
}; };
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ /* this hook is run after a DOM update. Changing state might result in an infinite loop */
/*hook need to be placed in body of the function component in which it is used*/ /* hook need to be placed in body of the function component in which it is used */
useEffect(() => { useEffect(() => {
getRryAgencies(); getRryAgencies();
/*use an empty dependency array to ensure the hook is running only once*/ /* use an empty dependency array to ensure the hook is running only once */
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []); }, []);
useEffect(() => { useEffect(() => {
//console.log('trip-updates-route-day: useEffect() strngAgencyId: ' + strngAgencyId); // console.log('trip-updates-route-day: useEffect() strngAgencyId: ' + strngAgencyId);
getRryAgencyPerDay(); getRryAgencyPerDay();
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [strngAgencyId, date]); }, [strngAgencyId, date]);
return (
return <> <>
<label> <label>
<Input <Input
id="inputDate" id="inputDate"
name={dateDefault} name={dateDefault}
onChange={handleDate} onChange={handleDate}
placeholder="Enter date ${dateDefault}" placeholder="Enter date ${dateDefault}"
type="date" type="date"
title="Enter date ${dateDefault}" title="Enter date ${dateDefault}"
value={date} value={date}
/> />
</label> </label>
<AgencySelect rry={rryAgencies} name={agencyNameDefault} onChange={handleChangeAgencyId} /> <AgencySelect rry={rryAgencies} name={agencyNameDefault} onChange={handleChangeAgencyId} />
<AgencyPerDayTable array={rryAgencyPerDay} title={'routes'} date={date}/> <AgencyPerDayTable array={rryAgencyPerDay} title="routes" date={date} />
</>; </>
}; );
}

View File

@ -1,27 +1,46 @@
import React from 'react'; import React from 'react';
import packageInfo from '../../package.json' import packageInfo from '../../package.json';
const VERSION = packageInfo.version; const VERSION = packageInfo.version;
export default function Contact() { export default function Contact() {
return ( return (
<> <>
<p> <p>
For questions about this website please do not hesitate to reach out to dialog (at) swingbe (dot) de. For questions about this website please do not hesitate to reach out to dialog (at) swingbe (dot) de.
</p> </p>
<p> <p>
Please feel <b>free</b> to <b>use</b>, <b>study</b>, <b>share</b> or <b>improve</b> the{' '} Please feel
<a {' '}
href="https://git.wtf-eg.de/dancingCycle/gtfs-display" <b>free</b>
target="_blank" {' '}
> to
source <b>use</b>
</a> ,
. <b>study</b>
</p> ,
<p> <b>share</b>
Version: {VERSION} {' '}
</p> or
</> <b>improve</b>
); {' '}
}; the
{' '}
<a
href="https://git.wtf-eg.de/dancingCycle/gtfs-display"
target="_blank"
rel="noreferrer"
>
source
</a>
.
</p>
<p>
Version:
{' '}
{VERSION}
</p>
</>
);
}

View File

@ -4,10 +4,16 @@ import axios from 'axios';
import Tables from '../components/tables'; import Tables from '../components/tables';
import gtfs from '../utils/gtfs'; import gtfs from '../utils/gtfs';
const Files = () => { function Files() {
return <fieldset> return (
<legend><b>GTFS Schedule</b> file overview</legend> <fieldset>
<Tables data={gtfs.datasetFiles} /> <legend>
</fieldset>; <b>GTFS Schedule</b>
}; {' '}
file overview
</legend>
<Tables data={gtfs.datasetFiles} />
</fieldset>
);
}
export default Files; export default Files;

View File

@ -6,60 +6,61 @@ import GroupAgencyPerDayTable from '../components/group-agency-per-day-table';
import Input from '../components/input'; import Input from '../components/input';
export default function GroupAgencyPerDay() { export default function GroupAgencyPerDay() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const dateDefault = 'Select date'; const [rryGroupAgencyPerDay, setRryGroupAgencyPerDay] = useState([]);
const [date, setDate] = useState(dateDefault); const [rryAgencies, setRryAgencies] = useState([]);
const [rryGroupAgencyPerDay, setRryGroupAgencyPerDay] = useState([]); // TODO How do we handle invalid date input?
const [rryAgencies, setRryAgencies] = useState([]); const handleDate = (e) => {
if (e.target.value.indexOf('2023') !== -1
|| e.target.value.indexOf('2024') !== -1) {
setDate((date) => e.target.value);
}
};
//TODO How do we handle invalid date input? const getRryGroupAgencyPerDay = async () => {
const handleDate = (e) => { if (date !== dateDefault) {
if (e.target.value.indexOf('2023') !== -1 || try {
e.target.value.indexOf('2024') !== -1) { /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
setDate((date)=>e.target.value);
}
};
const getRryGroupAgencyPerDay = async () => {
if ( date !== dateDefault) {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const address = `${config.API}trip-updates-by-group-agency-day?day=${date}`; const address = `${config.API}trip-updates-by-group-agency-day?day=${date}`;
//console.log('address: ' + address); // console.log('address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) { if (res.data !== undefined && res.data !== null) {
//console.log('res.data.length: ' + res.data.length); // console.log('res.data.length: ' + res.data.length);
setRryGroupAgencyPerDay((rryGroupAgencyPerDay) => res.data); setRryGroupAgencyPerDay((rryGroupAgencyPerDay) => res.data);
} else { } else {
console.error('ERROR: trip-updates by routes and day request FAILED'); console.error('ERROR: trip-updates by routes and day request FAILED');
} }
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
} }
}; };
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ /* this hook is run after a DOM update. Changing state might result in an infinite loop */
/*hook need to be placed in body of the function component in which it is used*/ /* hook need to be placed in body of the function component in which it is used */
useEffect(() => { useEffect(() => {
getRryGroupAgencyPerDay(); getRryGroupAgencyPerDay();
/*use an empty dependency array to ensure the hook is running only once*/ /* use an empty dependency array to ensure the hook is running only once */
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [date]); }, [date]);
return <> return (
<label> <>
<Input <label>
id="inputDate" <Input
name={dateDefault} id="inputDate"
onChange={handleDate} name={dateDefault}
placeholder="Enter date ${dateDefault}" onChange={handleDate}
type="date" placeholder="Enter date ${dateDefault}"
title="Enter date ${dateDefault}" type="date"
value={date} title="Enter date ${dateDefault}"
/> value={date}
</label> />
<GroupAgencyPerDayTable array={rryGroupAgencyPerDay} title={'agencies'} date={date}/> </label>
</>; <GroupAgencyPerDayTable array={rryGroupAgencyPerDay} title="agencies" date={date} />
}; </>
);
}

View File

@ -1,8 +1,8 @@
import React from 'react'; import React from 'react';
//TODO enable import GtfsValidatorReport from '../components/gtfs-validator-report.js'; // TODO enable import GtfsValidatorReport from '../components/gtfs-validator-report.js';
import GtfsFiles from '../components/gtfs-files'; import GtfsFiles from '../components/gtfs-files';
export default function Homepage() { export default function Homepage() {
return <GtfsFiles />; return <GtfsFiles />;
}; }

View File

@ -5,32 +5,28 @@ import OverviewTable from '../components/overview-next-table';
import config from '../config'; import config from '../config';
export default function OverviewNext() { export default function OverviewNext() {
const [cnts, setCnts] = useState([]);
const [cnts, setCnts] = useState([]); const getCnts = async () => {
try {
/* get cnts */
/* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const res = await axios.get(`${config.API}rts-stps-trps-cnt`);
const getCnts = async () => { if (res !== null && res !== undefined) {
try { setCnts(res.data);
/*get cnts*/
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const res = await axios.get(`${config.API}rts-stps-trps-cnt`);
if ( res !== null && res !== undefined ) {
setCnts(res.data);
} }
} catch (err) {
} catch (err) { console.error(`err.message: ${err.message}`);
console.error('err.message: ' + err.message);
}
};
useEffect(() => {
getCnts();
}, []);
if ( cnts === null || cnts === undefined || cnts.length === 0 ) {
return <p>loading...</p>;
} else {
return <OverviewTable overview={cnts} />;
} }
}; };
useEffect(() => {
getCnts();
}, []);
if (cnts === null || cnts === undefined || cnts.length === 0) {
return <p>loading...</p>;
}
return <OverviewTable overview={cnts} />;
}

View File

@ -5,132 +5,131 @@ import OverviewTable from '../components/overview-table';
import config from '../config'; import config from '../config';
export default function Overview() { export default function Overview() {
/* store data in function component state */
/* initialise as empty array */
const [overview, setOverview] = useState([]);
const [agencies, setAgencies] = useState(false);
const [wait, setWait] = useState(false);
/*store data in function component state*/ const handleAsyncOps = async (isMounted) => {
/*initialise as empty array*/ /* get data for each agency */
const [overview, setOverview] = useState([]); for (let j = 0; j < overview.length; j++) {
const [agencies, setAgencies] = useState(false); // console.log('j: ' + j);
const [wait, setWait] = useState(false); let isBreaking = false;
// 1. Make a shallow copy of the objects
const aryObjs = [...overview];
/// ///console.log('aryObjs len: ' + aryObjs.length);
// 2. Make a shallow copy of the object you want to mutate
const obj = { ...aryObjs[j] };
// 3. Replace the property you're intested in
const agencyId = obj.agency_id;
/// /console.log('agencyId: ' + agencyId);
let routeCount = obj.route_count;
// console.log('routeCount: ' + routeCount);
if (routeCount === null) {
const resRouteCount = await axios.get(
`${config.API}route-count?agencyid=${agencyId}`,
);
obj.route_count = resRouteCount.data;
routeCount = obj.route_count;
// console.log('routeCount: ' + routeCount);
isBreaking = true;
}
let tripCount = obj.trip_count;
// console.log('tripCount: ' + tripCount);
if (tripCount === null) {
const resTripCount = await axios.get(
`${config.API}trip-count?agencyid=${agencyId}`,
);
obj.trip_count = resTripCount.data;
tripCount = obj.trip_count;
// console.log('tripCount: ' + tripCount);
isBreaking = true;
}
// 4. Put it back into the array. N.B. we *are* mutating the array here, but that's why we made a copy first
aryObjs[j] = obj;
if (isBreaking && isMounted) {
// 5. Set the state to the new copy
setOverview((overview) => aryObjs);
setWait((wait) => !wait);
break;
}
}
};
const handleAsyncOps = async (isMounted) => { const getAgencies = async (isMounted) => {
/*get data for each agency*/ try {
for (var j = 0; j < overview.length; j++) { /* get agencies */
//console.log('j: ' + j); /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
let isBreaking = false; const res = await axios.get(`${config.API}agency-all`);
// 1. Make a shallow copy of the objects
const aryObjs = [...overview]; const aryOv = res.data;
//////console.log('aryObjs len: ' + aryObjs.length); for (let i = 0; i < aryOv.length; i++) {
// 2. Make a shallow copy of the object you want to mutate const agencyId = aryOv[i].agency_id;
const obj = { ...aryObjs[j] }; const agencyName = aryOv[i].agency_name;
// 3. Replace the property you're intested in const objOvItem = {};
const agencyId = obj.agency_id; objOvItem.agency_id = agencyId;
////console.log('agencyId: ' + agencyId); objOvItem.agency_name = agencyName;
let routeCount = obj.route_count; objOvItem.route_count = null;
//console.log('routeCount: ' + routeCount); objOvItem.trip_count = null;
if (routeCount === null) { objOvItem.day = null;
const resRouteCount = await axios.get(
`${config.API}route-count?agencyid=${agencyId}` /* set state */
); if (isMounted) {
obj.route_count = resRouteCount.data; // TODO Study! Is objOvitem added to array overview?
routeCount = obj.route_count; setOverview((overview) => [...overview, objOvItem]);
//console.log('routeCount: ' + routeCount);
isBreaking = true;
}
let tripCount = obj.trip_count;
//console.log('tripCount: ' + tripCount);
if (tripCount === null) {
const resTripCount = await axios.get(
`${config.API}trip-count?agencyid=${agencyId}`
);
obj.trip_count = resTripCount.data;
tripCount = obj.trip_count;
//console.log('tripCount: ' + tripCount);
isBreaking = true;
}
// 4. Put it back into the array. N.B. we *are* mutating the array here, but that's why we made a copy first
aryObjs[j] = obj;
if (isBreaking && isMounted) {
// 5. Set the state to the new copy
setOverview((overview) => aryObjs);
setWait((wait) => !wait);
break;
}
} }
}
/* set... is an async function and you cannot get the state value immediately after update. Use useEffect hook instead */
setAgencies((agencies) => !agencies);
} catch (err) {
console.error(`err.message: ${err.message}`);
}
};
useEffect(() => {
/* declare let isMounted = true inside useEffect, which will be changed in the cleanup callback, as soon as the component is unmounted. Before state updates, check this variable conditionally. */
let isMounted = true;
if (wait) {
setWait((wait) => !wait);
handleAsyncOps(isMounted);
}
return () => {
isMounted = false;
}; };
}, [wait]);
const getAgencies = async (isMounted) => { /* If you want to get an updated state value then use useEffect hook with dependency array. React will execute this hook after each state update. */
try { useEffect(() => {
/*get agencies*/ /* declare let isMounted = true inside useEffect, which will be changed in the cleanup callback, as soon as the component is unmounted. Before state updates, check this variable conditionally. */
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/ let isMounted = true;
const res = await axios.get(`${config.API}agency-all`); /// ///console.log('useEffect() agencies: ' + agencies);
if (agencies) {
let aryOv = res.data; /// /console.log('agencies available');
for (var i = 0; i < aryOv.length; i++) { handleAsyncOps(isMounted);
let agencyId = aryOv[i].agency_id; } else {
let agencyName = aryOv[i].agency_name; /// /console.log('agencies not available');
let objOvItem = {}; }
objOvItem['agency_id'] = agencyId; return () => {
objOvItem['agency_name'] = agencyName; isMounted = false;
objOvItem['route_count'] = null;
objOvItem['trip_count'] = null;
objOvItem['day'] = null;
/*set state*/
if (isMounted) {
//TODO Study! Is objOvitem added to array overview?
setOverview((overview) => [...overview, objOvItem]);
}
}
/*set... is an async function and you cannot get the state value immediately after update. Use useEffect hook instead*/
setAgencies((agencies) => !agencies);
} catch (err) {
console.error('err.message: ' + err.message);
}
}; };
}, [agencies]);
useEffect(() => { /* this hook is run after a DOM update. Changing state might result in an infinite loop */
/*declare let isMounted = true inside useEffect, which will be changed in the cleanup callback, as soon as the component is unmounted. Before state updates, check this variable conditionally.*/ /* hook need to be placed in body of the function component in which it is used */
let isMounted = true; useEffect(() => {
if (wait) { /* declare let isMounted = true inside useEffect, which will be changed in the cleanup callback, as soon as the component is unmounted. Before state updates, check this variable conditionally. */
setWait((wait) => !wait); let isMounted = true;
handleAsyncOps(isMounted);
}
return () => {
isMounted = false;
};
}, [wait]);
/*If you want to get an updated state value then use useEffect hook with dependency array. React will execute this hook after each state update.*/ getAgencies(isMounted);
useEffect(() => {
/*declare let isMounted = true inside useEffect, which will be changed in the cleanup callback, as soon as the component is unmounted. Before state updates, check this variable conditionally.*/
let isMounted = true;
//////console.log('useEffect() agencies: ' + agencies);
if (agencies) {
////console.log('agencies available');
handleAsyncOps(isMounted);
} else {
////console.log('agencies not available');
}
return () => {
isMounted = false;
};
}, [agencies]);
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ return () => {
/*hook need to be placed in body of the function component in which it is used*/ isMounted = false;
useEffect(() => { };
/*declare let isMounted = true inside useEffect, which will be changed in the cleanup callback, as soon as the component is unmounted. Before state updates, check this variable conditionally.*/ /* use an empty dependency array to ensure the hook is running only once */
let isMounted = true; /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []);
getAgencies(isMounted); return <OverviewTable overview={overview} />;
}
return () => {
isMounted = false;
};
/*use an empty dependency array to ensure the hook is running only once*/
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
}, []);
return <OverviewTable overview={overview} />;
};

View File

@ -6,60 +6,61 @@ import PerDayTable from '../components/per-day-table';
import Input from '../components/input'; import Input from '../components/input';
export default function PerDay() { export default function PerDay() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const dateDefault = 'Select date'; const [rryPerDay, setRryPerDay] = useState([]);
const [date, setDate] = useState(dateDefault); const [rryAgencies, setRryAgencies] = useState([]);
const [rryPerDay, setRryPerDay] = useState([]); // TODO How do we handle invalid date input?
const [rryAgencies, setRryAgencies] = useState([]); const handleDate = (e) => {
if (e.target.value.indexOf('2023') !== -1
|| e.target.value.indexOf('2024') !== -1) {
setDate((date) => e.target.value);
}
};
//TODO How do we handle invalid date input? const fetchData = async () => {
const handleDate = (e) => { if (date !== dateDefault) {
if (e.target.value.indexOf('2023') !== -1 || try {
e.target.value.indexOf('2024') !== -1) { /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
setDate((date)=>e.target.value);
}
};
const fetchData = async () => {
if ( date !== dateDefault) {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const address = `${config.API}trip-updates-by-day?day=${date}`; const address = `${config.API}trip-updates-by-day?day=${date}`;
//console.log('address: ' + address); // console.log('address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) { if (res.data !== undefined && res.data !== null) {
//console.log('res.data.length: ' + res.data.length); // console.log('res.data.length: ' + res.data.length);
setRryPerDay((rryPerDay) => res.data); setRryPerDay((rryPerDay) => res.data);
} else { } else {
console.error('ERROR: trip-updates by routes and day request FAILED'); console.error('ERROR: trip-updates by routes and day request FAILED');
} }
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
} }
}; };
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ /* this hook is run after a DOM update. Changing state might result in an infinite loop */
/*hook need to be placed in body of the function component in which it is used*/ /* hook need to be placed in body of the function component in which it is used */
useEffect(() => { useEffect(() => {
fetchData(); fetchData();
/*use an empty dependency array to ensure the hook is running only once*/ /* use an empty dependency array to ensure the hook is running only once */
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [date]); }, [date]);
return <> return (
<label> <>
<Input <label>
id="inputDate" <Input
name={dateDefault} id="inputDate"
onChange={handleDate} name={dateDefault}
placeholder="Enter date ${dateDefault}" onChange={handleDate}
type="date" placeholder="Enter date ${dateDefault}"
title="Enter date ${dateDefault}" type="date"
value={date} title="Enter date ${dateDefault}"
/> value={date}
</label> />
<PerDayTable array={rryPerDay} title={'feeds'} date={date}/> </label>
</>; <PerDayTable array={rryPerDay} title="feeds" date={date} />
}; </>
);
}

View File

@ -4,17 +4,16 @@ import RadioButton from '../components/radio-button';
import Rltm from '../components/realtime'; import Rltm from '../components/realtime';
export default function Realtime() { export default function Realtime() {
const [state, setState] = useState(''); const [state, setState] = useState('');
const handleChange = (e) => { const handleChange = (e) => {
setState(e.target.value); setState(e.target.value);
} };
return ( return (
<> <>
<RadioButton state={state} onChange={handleChange}/> <RadioButton state={state} onChange={handleChange} />
<br /> <br />
<Rltm state={state} /> <Rltm state={state} />
</> </>
); );
}
};

View File

@ -7,78 +7,79 @@ import ChartBar from '../components/chart-bar';
import ChartLine from '../components/chart-line'; import ChartLine from '../components/chart-line';
import GtfsService from '../utils/gtfs-service'; import GtfsService from '../utils/gtfs-service';
import config from '../config'; import config from '../config';
const Service = () => {
/*store route as string*/
const [route, setRoute] = useState('');
const [render, setRender] = useState(false);
const [loading, setLoading] = useState(false);
const [objService, setObjService] = useState({});
const [time, setTime] = useState([]);
const [trip, setTrip] = useState([]);
/*fetch objService in a JavaScript function*/ function Service() {
const getObjService = async () => { /* store route as string */
try { const [route, setRoute] = useState('');
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/ const [render, setRender] = useState(false);
let url = `${config.API}servicedays?routeshortname=${route}`; const [loading, setLoading] = useState(false);
setLoading(true); const [objService, setObjService] = useState({});
const objService = await axios.get(url); const [time, setTime] = useState([]);
setLoading(false); const [trip, setTrip] = useState([]);
setRender(true);
/*set state*/ /* fetch objService in a JavaScript function */
setObjService(objService.data); const getObjService = async () => {
const aryTripCount = GtfsService.getAryTripCount(objService); try {
setTrip(aryTripCount); /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const aryTime = GtfsService.getAryTime(objService); const url = `${config.API}servicedays?routeshortname=${route}`;
const aryDate = aryTime.map((time) => new Date(time).toDateString()); setLoading(true);
setTime(aryDate); const objService = await axios.get(url);
} catch (err) { setLoading(false);
console.error('err.message: ' + err.message); setRender(true);
}
};
const handleSubmit = () => { /* set state */
event.preventDefault(); setObjService(objService.data);
getObjService(); const aryTripCount = GtfsService.getAryTripCount(objService);
}; setTrip(aryTripCount);
const aryTime = GtfsService.getAryTime(objService);
const aryDate = aryTime.map((time) => new Date(time).toDateString());
setTime(aryDate);
} catch (err) {
console.error(`err.message: ${err.message}`);
}
};
const handleChange = (e) => { const handleSubmit = () => {
setRoute(e.target.value); event.preventDefault();
}; getObjService();
};
/*element representing user-defined React component*/ const handleChange = (e) => {
const msgTable = <ServiceTable render={render} service={objService} />; setRoute(e.target.value);
const bar = <ChartBar route={route} time={time} trip={trip} />; };
const line = <ChartLine route={route} time={time} trip={trip} />;
return ( /* element representing user-defined React component */
<> const msgTable = <ServiceTable render={render} service={objService} />;
<FormValue const bar = <ChartBar route={route} time={time} trip={trip} />;
value={route} const line = <ChartLine route={route} time={time} trip={trip} />;
valueName={'route'}
onSubmit={handleSubmit} return (
onChange={handleChange} <>
/> <FormValue
<Loading loading={loading} /> value={route}
<div valueName="route"
style={{ onSubmit={handleSubmit}
height: '500px', onChange={handleChange}
width: '900px' />
}} <Loading loading={loading} />
> <div
{bar} style={{
</div> height: '500px',
<div width: '900px',
style={{ }}
height: '500px', >
width: '900px' {bar}
}} </div>
> <div
{line} style={{
</div> height: '500px',
{msgTable} width: '900px',
</> }}
); >
}; {line}
</div>
{msgTable}
</>
);
}
export default Service; export default Service;

View File

@ -7,59 +7,59 @@ import TablePageHead from '../components/table-page-trip-calendar-head';
import TablePageBody from '../components/table-page-trip-calendar-body'; import TablePageBody from '../components/table-page-trip-calendar-body';
export default function TripCalendar() { export default function TripCalendar() {
/* store and initialize data in function component state */
const [agencyIds, setAgencyIds] = useState([]);
/*store and initialize data in function component state*/ const getAgencyIds = async () => {
const [agencyIds, setAgencyIds] = useState([]); try {
/* get agencyIds */
/* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const res = await axios.get(`${config.API}agencyids`);
const getAgencyIds = async () => { const aryAgencyIds = res.data;
try { // console.log('TripCalendar aryAgencyIds.length:'+aryAgencyIds.length);
/*get agencyIds*/
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const res = await axios.get(`${config.API}agencyids`);
let aryAgencyIds = res.data;
//console.log('TripCalendar aryAgencyIds.length:'+aryAgencyIds.length);
setAgencyIds(aryAgencyIds); setAgencyIds(aryAgencyIds);
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
}
};
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/
/*hook need to be placed in body of the function component in which it is used*/
useEffect(() => {
getAgencyIds();
/*use an empty dependency array to ensure the hook is running only once*/
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
}, []);
const agencysTableBody = agencyIds.map(value =>
<TablePageBody
className={value.agency_name}
agencyIdName={value}
key={value.agency_id}
/>
);
const agencysTableHead = <TablePageHead
className='tablePageHead'
agencyIdName={agencyIds[0]}
/>
if(agencyIds.length > 0){
/*TODO Introduce thead and tbody in Table?*/
return <>
<Table
striped
bordered
hover
size="sm"
variant="dark"
responsive
>
{agencysTableHead}
{agencysTableBody}
</Table>
</>
}else{
return <p>loading...</p>
} }
}; };
/* this hook is run after a DOM update. Changing state might result in an infinite loop */
/* hook need to be placed in body of the function component in which it is used */
useEffect(() => {
getAgencyIds();
/* use an empty dependency array to ensure the hook is running only once */
/* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []);
const agencysTableBody = agencyIds.map((value) => (
<TablePageBody
className={value.agency_name}
agencyIdName={value}
key={value.agency_id}
/>
));
const agencysTableHead = (
<TablePageHead
className="tablePageHead"
agencyIdName={agencyIds[0]}
/>
);
if (agencyIds.length > 0) {
/* TODO Introduce thead and tbody in Table? */
return (
<Table
striped
bordered
hover
size="sm"
variant="dark"
responsive
>
{agencysTableHead}
{agencysTableBody}
</Table>
);
}
return <p>loading...</p>;
}

View File

@ -8,134 +8,133 @@ import RouteSelect from '../components/route-select';
import Input from '../components/input'; import Input from '../components/input';
export default function TripUpdates() { export default function TripUpdates() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const dateDefault = 'Select date'; const agencyNameDefault = 'Select GTFS agency_name';
const [date, setDate] = useState(dateDefault); const routeNameDefault = 'Select GTFS route_short_name';
const agencyNameDefault = 'Select GTFS agency_name'; /* store and initialize data in function component state */
const routeNameDefault = 'Select GTFS route_short_name'; const [strngAgencyId, setStrngAgencyId] = useState(agencyNameDefault);
const [strngRouteId, setStrngRouteId] = useState(routeNameDefault);
/*store and initialize data in function component state*/ const [rryTripUpdates, setRryTripUpdates] = useState([]);
const [strngAgencyId, setStrngAgencyId] = useState(agencyNameDefault); const [rryAgencies, setRryAgencies] = useState([]);
const [strngRouteId, setStrngRouteId] = useState(routeNameDefault); const [rryRoutes, setRryRoutes] = useState([]);
const [rryTripUpdates, setRryTripUpdates] = useState([]); // TODO How do we handle invalid date input?
const [rryAgencies, setRryAgencies] = useState([]); const handleDate = (e) => {
const [rryRoutes, setRryRoutes] = useState([]); if (e.target.value.indexOf('2023') !== -1
|| e.target.value.indexOf('2024') !== -1) {
setDate((date) => e.target.value);
}
};
//TODO How do we handle invalid date input? const getRryAgencies = async () => {
const handleDate = (e) => { try {
if (e.target.value.indexOf('2023') !== -1 || /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
e.target.value.indexOf('2024') !== -1) {
setDate((date)=>e.target.value);
}
};
const getRryAgencies = async () => {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const address = `${config.API}agencyids`; const address = `${config.API}agencyids`;
//console.log('trip-updates-route-day res.data.length: address: ' + address); // console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
//console.log('trip-updates-route-day res.data.length: Agencies: ' + res.data.length); // console.log('trip-updates-route-day res.data.length: Agencies: ' + res.data.length);
setRryAgencies((rryAgency) => res.data); setRryAgencies((rryAgency) => res.data);
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
}; };
const getRryRoutes = async () => { const getRryRoutes = async () => {
if ( strngAgencyId !== agencyNameDefault ) { if (strngAgencyId !== agencyNameDefault) {
try { try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/ /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const address = `${config.API}routes?agencyid=${strngAgencyId}`; const address = `${config.API}routes?agencyid=${strngAgencyId}`;
//console.log('trip-updates-route-day res.data.length: address: ' + address); // console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) { if (res.data !== undefined && res.data !== null) {
//console.log('trip-updates-route-day res.data.length: Routes: ' + res.data.length); // console.log('trip-updates-route-day res.data.length: Routes: ' + res.data.length);
setRryRoutes((rryRoutes) => res.data); setRryRoutes((rryRoutes) => res.data);
if ( res.data.length > 0 ) { if (res.data.length > 0) {
setStrngRouteId((strngRouteId) => res.data[0].route_id); setStrngRouteId((strngRouteId) => res.data[0].route_id);
} else { } else {
console.error('ERROR: agency has NO routes'); console.error('ERROR: agency has NO routes');
} }
} else { } else {
console.error('ERROR: routes by agency request FAILED'); console.error('ERROR: routes by agency request FAILED');
} }
} catch (err) {
console.error(`err.message: ${err.message}`);
}
}
};
} catch (err) { const getRryTripUpdates = async () => {
console.error('err.message: ' + err.message); if (strngRouteId !== routeNameDefault
} && date !== dateDefault) {
} try {
}; /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
// console.log('route: ' + strngRouteId);
const getRryTripUpdates = async () => {
if ( strngRouteId !== routeNameDefault &&
date !== dateDefault) {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
//console.log('route: ' + strngRouteId);
const address = `${config.API}trip-updates-by-route-day?routeid=${strngRouteId}&day=${date}`; const address = `${config.API}trip-updates-by-route-day?routeid=${strngRouteId}&day=${date}`;
//console.log('trip-updates-route-day res.data.length: address: ' + address); // console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) { if (res.data !== undefined && res.data !== null) {
//console.log('trip-updates-route-day res.data.length: TripUpdates: ' + res.data.length); // console.log('trip-updates-route-day res.data.length: TripUpdates: ' + res.data.length);
setRryTripUpdates((rryTripUpdates) => res.data); setRryTripUpdates((rryTripUpdates) => res.data);
} else { } else {
console.error('ERROR: trip-updates by routes and day request FAILED'); console.error('ERROR: trip-updates by routes and day request FAILED');
} }
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
} }
}; };
const handleChangeAgencyId = (event) => { const handleChangeAgencyId = (event) => {
//console.log('trip-updates-route-day: handleChangeAgencyId() value: ' + event.target.value); // console.log('trip-updates-route-day: handleChangeAgencyId() value: ' + event.target.value);
setStrngAgencyId((strngAgencyId) => event.target.value); setStrngAgencyId((strngAgencyId) => event.target.value);
}; };
const handleChangeRouteId = (event) => { const handleChangeRouteId = (event) => {
//console.log('trip-updates-route-day: handleChangeRouteId() value: ' + event.target.value); // console.log('trip-updates-route-day: handleChangeRouteId() value: ' + event.target.value);
setStrngRouteId((strngRouteId) => event.target.value); setStrngRouteId((strngRouteId) => event.target.value);
}; };
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ /* this hook is run after a DOM update. Changing state might result in an infinite loop */
/*hook need to be placed in body of the function component in which it is used*/ /* hook need to be placed in body of the function component in which it is used */
useEffect(() => { useEffect(() => {
getRryAgencies(); getRryAgencies();
/*use an empty dependency array to ensure the hook is running only once*/ /* use an empty dependency array to ensure the hook is running only once */
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []); }, []);
useEffect(() => { useEffect(() => {
//console.log('trip-updates-route-day: useEffect() strngAgencyId: ' + strngAgencyId); // console.log('trip-updates-route-day: useEffect() strngAgencyId: ' + strngAgencyId);
getRryRoutes(); getRryRoutes();
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [strngAgencyId]); }, [strngAgencyId]);
useEffect(() => { useEffect(() => {
//console.log('trip-updates-route-day: useEffect() strngRouteId: ' + strngRouteId); // console.log('trip-updates-route-day: useEffect() strngRouteId: ' + strngRouteId);
getRryTripUpdates(); getRryTripUpdates();
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [strngRouteId, date]); }, [strngRouteId, date]);
// TODO get rry based on route_id!
//TODO get rry based on route_id! return (
return <> <>
<label> <label>
<Input <Input
id="inputDate" id="inputDate"
name={dateDefault} name={dateDefault}
onChange={handleDate} onChange={handleDate}
placeholder="Enter date ${dateDefault}" placeholder="Enter date ${dateDefault}"
type="date" type="date"
title="Enter date ${dateDefault}" title="Enter date ${dateDefault}"
value={date} value={date}
/> />
</label> </label>
<AgencySelect rry={rryAgencies} name={agencyNameDefault} onChange={handleChangeAgencyId} /> <AgencySelect rry={rryAgencies} name={agencyNameDefault} onChange={handleChangeAgencyId} />
<RouteSelect rry={rryRoutes} name={routeNameDefault} onChange={handleChangeRouteId} /> <RouteSelect rry={rryRoutes} name={routeNameDefault} onChange={handleChangeRouteId} />
<TripUpdatesRouteDayTable array={rryTripUpdates} title={'trips'} date={date}/> <TripUpdatesRouteDayTable array={rryTripUpdates} title="trips" date={date} />
</>; </>
}; );
}

View File

@ -7,112 +7,111 @@ import AgencySelect from '../components/agency-select';
import RouteSelect from '../components/route-select'; import RouteSelect from '../components/route-select';
export default function Trips() { export default function Trips() {
const agencyNameDefault = 'Select GTFS agency_name';
const routeNameDefault = 'Select GTFS route_short_name';
const agencyNameDefault = 'Select GTFS agency_name'; /* store and initialize data in function component state */
const routeNameDefault = 'Select GTFS route_short_name'; const [strngAgencyId, setStrngAgencyId] = useState(agencyNameDefault);
const [strngRouteId, setStrngRouteId] = useState(routeNameDefault);
const [rryTrips, setRryTrips] = useState([]);
const [rryAgencies, setRryAgencies] = useState([]);
const [rryRoutes, setRryRoutes] = useState([]);
/*store and initialize data in function component state*/ const getRryAgencies = async () => {
const [strngAgencyId, setStrngAgencyId] = useState(agencyNameDefault); try {
const [strngRouteId, setStrngRouteId] = useState(routeNameDefault); /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const [rryTrips, setRryTrips] = useState([]);
const [rryAgencies, setRryAgencies] = useState([]);
const [rryRoutes, setRryRoutes] = useState([]);
const getRryAgencies = async () => {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const address = `${config.API}agencyids`; const address = `${config.API}agencyids`;
//console.log('trips-route-day res.data.length: address: ' + address); // console.log('trips-route-day res.data.length: address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
//console.log('trips-route-day res.data.length: Agencies: ' + res.data.length); // console.log('trips-route-day res.data.length: Agencies: ' + res.data.length);
setRryAgencies((rryAgency) => res.data); setRryAgencies((rryAgency) => res.data);
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
}; };
const getRryRoutes = async () => { const getRryRoutes = async () => {
if ( strngAgencyId !== agencyNameDefault ) { if (strngAgencyId !== agencyNameDefault) {
try { try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/ /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const address = `${config.API}routes?agencyid=${strngAgencyId}`; const address = `${config.API}routes?agencyid=${strngAgencyId}`;
//console.log('trips-route-day res.data.length: address: ' + address); // console.log('trips-route-day res.data.length: address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) { if (res.data !== undefined && res.data !== null) {
//console.log('trips-route-day res.data.length: Routes: ' + res.data.length); // console.log('trips-route-day res.data.length: Routes: ' + res.data.length);
setRryRoutes((rryRoutes) => res.data); setRryRoutes((rryRoutes) => res.data);
if ( res.data.length > 0 ) { if (res.data.length > 0) {
setStrngRouteId((strngRouteId) => res.data[0].route_id); setStrngRouteId((strngRouteId) => res.data[0].route_id);
} else { } else {
console.error('ERROR: agency has NO routes'); console.error('ERROR: agency has NO routes');
} }
} else { } else {
console.error('ERROR: routes by agency request FAILED'); console.error('ERROR: routes by agency request FAILED');
} }
} catch (err) {
console.error(`err.message: ${err.message}`);
}
}
};
} catch (err) { const getRryTrips = async () => {
console.error('err.message: ' + err.message); if (strngRouteId !== routeNameDefault) {
} try {
} /* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
};
const getRryTrips = async () => {
if ( strngRouteId !== routeNameDefault ) {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const date = new Date(); const date = new Date();
const dateShort = date.getFullYear() + '-' + (date.getMonth()+1) + '-' + date.getDate(); const dateShort = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
//console.log('trips-route-day dateShort: ' + dateShort); // console.log('trips-route-day dateShort: ' + dateShort);
const address = `${config.API}trips-by-route-day?routeid=${strngRouteId}&day=${dateShort}`; const address = `${config.API}trips-by-route-day?routeid=${strngRouteId}&day=${dateShort}`;
//console.log('trips-route-day res.data.length: address: ' + address); // console.log('trips-route-day res.data.length: address: ' + address);
const res = await axios.get(address); const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) { if (res.data !== undefined && res.data !== null) {
//console.log('trips-route-day res.data.length: Trips: ' + res.data.length); // console.log('trips-route-day res.data.length: Trips: ' + res.data.length);
setRryTrips((rryTrips) => res.data); setRryTrips((rryTrips) => res.data);
} else { } else {
console.error('ERROR: trips by routes and day request FAILED'); console.error('ERROR: trips by routes and day request FAILED');
} }
} catch (err) { } catch (err) {
console.error('err.message: ' + err.message); console.error(`err.message: ${err.message}`);
} }
} }
}; };
const handleChangeAgencyId = (event) => { const handleChangeAgencyId = (event) => {
//console.log('trips-route-day: handleChangeAgencyId() value: ' + event.target.value); // console.log('trips-route-day: handleChangeAgencyId() value: ' + event.target.value);
setStrngAgencyId((strngAgencyId) => event.target.value); setStrngAgencyId((strngAgencyId) => event.target.value);
}; };
const handleChangeRouteId = (event) => { const handleChangeRouteId = (event) => {
//console.log('trips-route-day: handleChangeRouteId() value: ' + event.target.value); // console.log('trips-route-day: handleChangeRouteId() value: ' + event.target.value);
setStrngRouteId((strngRouteId) => event.target.value); setStrngRouteId((strngRouteId) => event.target.value);
}; };
/*this hook is run after a DOM update. Changing state might result in an infinite loop*/ /* this hook is run after a DOM update. Changing state might result in an infinite loop */
/*hook need to be placed in body of the function component in which it is used*/ /* hook need to be placed in body of the function component in which it is used */
useEffect(() => { useEffect(() => {
getRryAgencies(); getRryAgencies();
/*use an empty dependency array to ensure the hook is running only once*/ /* use an empty dependency array to ensure the hook is running only once */
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []); }, []);
useEffect(() => { useEffect(() => {
//console.log('trips-route-day: useEffect() strngAgencyId: ' + strngAgencyId); // console.log('trips-route-day: useEffect() strngAgencyId: ' + strngAgencyId);
getRryRoutes(); getRryRoutes();
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [strngAgencyId]); }, [strngAgencyId]);
useEffect(() => { useEffect(() => {
//console.log('trips-route-day: useEffect() strngRouteId: ' + strngRouteId); // console.log('trips-route-day: useEffect() strngRouteId: ' + strngRouteId);
getRryTrips(); getRryTrips();
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/ /* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [strngRouteId]); }, [strngRouteId]);
// TODO get rry based on route_id!
//TODO get rry based on route_id! return (
return <> <>
<AgencySelect rry={rryAgencies} name={agencyNameDefault} onChange={handleChangeAgencyId} /> <AgencySelect rry={rryAgencies} name={agencyNameDefault} onChange={handleChangeAgencyId} />
<RouteSelect rry={rryRoutes} name={routeNameDefault} onChange={handleChangeRouteId} /> <RouteSelect rry={rryRoutes} name={routeNameDefault} onChange={handleChangeRouteId} />
<TripsRouteDayTable array={rryTrips} title={'Trips'} /> <TripsRouteDayTable array={rryTrips} title="Trips" />
</>; </>
}; );
}

Some files were not shown because too many files have changed in this diff Show More