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": [
"@babel/preset-env",
"@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');
//path is used to resolve properly across the OS
// path is used to resolve properly across the OS
const path = require('path');
module.exports = {
//bundle *.js from this entry point
entry: path.resolve(__dirname, '../src/index.jsx'),
//create output file to be linked to index.html
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../dist'),
clean: true,
},
module: {
rules: [
// bundle *.js from this entry point
entry: path.resolve(__dirname, '../src/index.jsx'),
// create output file to be linked to index.html
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, '../dist'),
clean: true,
},
module: {
rules: [
{
//test all *.js using babel-loader
//test all *.jsx (e.g. React.js) using babel-loader
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.resolve(__dirname, '../src'),
use: ['babel-loader'],
},
{
//test all *.css using style-loader and css-loader
test: /\.css$/i,
use: ["style-loader", "css-loader"],
},
{
//test all *.svg using svg-loader
test: /\.svg$/,
use: [
{
loader: 'svg-url-loader',
options: {
limit: 10000,
},
// test all *.js using babel-loader
// test all *.jsx (e.g. React.js) using babel-loader
test: /\.(js|jsx)$/,
exclude: /node_modules/,
include: path.resolve(__dirname, '../src'),
use: {
loader: 'babel-loader',
options: {
presets: [
'@babel/preset-env',
'@babel/preset-react',
],
},
],
},
},
{
test: /\.(png|jpe?g|gif)$/i,
use: [
{
loader: 'file-loader',
},
],
// test all *.css using style-loader and css-loader
test: /\.css$/i,
use: ['style-loader', 'css-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 { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
//merge() calls in the environment-specific configuration to include commons
const common = require('./webpack.common');
// merge() calls in the environment-specific configuration to include commons
module.exports = merge(common, {
//set development mode
mode: 'development',
//enable strong source mapping
devtool: 'inline-source-map',
devServer: {
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,
},
// set development mode
mode: 'development',
// enable strong source mapping
devtool: 'inline-source-map',
devServer: {
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,
},
});

View File

@ -1,8 +1,9 @@
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const common = require('./webpack.common');
module.exports = merge(common, {
mode: 'production',
//source maps encouraged in production
//choose mapping with fairly quick build speed like source-map
devtool: 'source-map',
mode: 'production',
// source maps encouraged in production
// choose mapping with fairly quick build speed like 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>",
"license": "GPL-3.0-or-later",
"engines": {
"node": ">=18.19.0"
"node": ">=18.17.0"
},
"scripts": {
"start": "webpack serve --config config/webpack.dev.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": {
"@babel/core": "7.22.10",
"@babel/preset-env": "7.22.10",
"@babel/preset-react": "7.22.5",
"@babel/core": "7.23.9",
"@babel/eslint-parser": "7.23.10",
"@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",
"css-loader": "6.8.1",
"file-loader": "6.2.0",
"html-webpack-plugin": "5.5.3",
"style-loader": "3.3.2",
"svg-url-loader": "8.0.0",
"webpack": "5.88.2",
"css-loader": "6.10.0",
"eslint": "8.56.0",
"eslint-config-airbnb": "19.0.4",
"eslint-webpack-plugin": "4.0.1",
"style-loader": "3.3.4",
"webpack": "5.90.2",
"webpack-cli": "5.1.4",
"webpack-dev-server": "4.15.1",
"webpack-merge": "5.9.0"
"webpack-dev-server": "5.0.1"
},
"dependencies": {
"webpack-merge": "5.10.0",
"html-webpack-plugin": "5.6.0",
"axios": "1.3.6",
"prop-types": "15.8.1",
"react": "18.2.0",

View File

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

View File

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

View File

@ -1,32 +1,53 @@
import React from 'react';
import PropTypes from 'prop-types';
export default function AgencyPerDayTableEntries ({array}) {
if ( array !== undefined && array !== null && array.length > 0 ) {
//TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
//iterate over array
return array.map((item, index) => {
return (
<tr
key={index}
>
<td>{item.agency_name}&nbsp;|</td>
<td>{item.agency_id}&nbsp;|</td>
<td>{item.route_id}&nbsp;|</td>
<td>{item.route_short_name}&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>
);
});
}else{
//data is empty
return null;
}
};
export default function AgencyPerDayTableEntries({ array }) {
if (array !== undefined && array !== null && array.length > 0) {
// TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
// iterate over array
return array.map((item, index) => (
<tr
key={index}
>
<td>
{item.agency_name}
&nbsp;|
</td>
<td>
{item.agency_id}
&nbsp;|
</td>
<td>
{item.route_id}
&nbsp;|
</td>
<td>
{item.route_short_name}
&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 = {
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';
/*destructure props object*/
export default function AgencyPerDayTable ({array, title, date}){
if ( array !== undefined && array !== null) {
/*return a React element*/
return (
<>
<p>Table of {array.length}&nbsp;{title} for {date}:</p>
<table>
<thead>
<tr>
<th>agency_name&nbsp;|</th>
<th>agency_id&nbsp;|</th>
<th>route_id&nbsp;|</th>
<th>route_short_name&nbsp;|</th>
<th>abs. trip count&nbsp;|</th>
<th>RT TU count&nbsp;|</th>
<th>RT TU %&nbsp;|</th>
<th>latest RT timestamp&nbsp;|</th>
</tr>
</thead>
<tbody>
<AgencyPerDayTableEntries array={array} />
</tbody>
</table>
</>
);
}else{
return (
<>
<p>loading...</p>
</>
);
}
};
/* destructure props object */
export default function AgencyPerDayTable({ array, title, date }) {
if (array !== undefined && array !== null) {
/* return a React element */
return (
<>
<p>
Table of
{array.length}
{title}
{' '}
for
{date}
:
</p>
<table>
<thead>
<tr>
<th>agency_name&nbsp;|</th>
<th>agency_id&nbsp;|</th>
<th>route_id&nbsp;|</th>
<th>route_short_name&nbsp;|</th>
<th>abs. trip count&nbsp;|</th>
<th>RT TU count&nbsp;|</th>
<th>RT TU %&nbsp;|</th>
<th>latest RT timestamp&nbsp;|</th>
</tr>
</thead>
<tbody>
<AgencyPerDayTableEntries array={array} />
</tbody>
</table>
</>
);
}
return (
<p>loading...</p>
);
}
AgencyPerDayTable.propTypes = {
array: PropTypes.array,
title: PropTypes.string,
date: PropTypes.string
array: PropTypes.array,
title: PropTypes.string,
date: PropTypes.string,
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,29 +1,41 @@
import React from 'react';
import PropTypes from 'prop-types';
export default function GroupAgencyPerDayTableEntries ({array}) {
if ( array !== undefined && array !== null && array.length > 0 ) {
//TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
//iterate over array
return array.map((item, index) => {
return (
<tr
key={index}
>
<td>{item.agency_name}&nbsp;|</td>
<td>{item.agency_id}&nbsp;|</td>
<td>{item.rt_part}&nbsp;|</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>
);
});
}else{
//data is empty
return null;
}
};
export default function GroupAgencyPerDayTableEntries({ array }) {
if (array !== undefined && array !== null && array.length > 0) {
// TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
// iterate over array
return array.map((item, index) => (
<tr
key={index}
>
<td>
{item.agency_name}
&nbsp;|
</td>
<td>
{item.agency_id}
&nbsp;|
</td>
<td>
{item.rt_part}
&nbsp;|
</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 = {
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';
//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*/
export default function GroupAgencyPerDayTable ({array, title, date}){
if ( array !== undefined && array !== null) {
/*return a React element*/
return (
<>
<p>Table of {array.length}&nbsp;{title} for {date}:</p>
<table>
<thead>
<tr>
<th>agency_name&nbsp;|</th>
<th>abs. trip count&nbsp;|</th>
<th>RT TU count&nbsp;|</th>
<th>RT TU %&nbsp;|</th>
<th>latest RT timestamp&nbsp;|</th>
</tr>
</thead>
<tbody>
<GroupAgencyPerDayTableEntries array={array} />
</tbody>
</table>
</>
);
}else{
return (
<>
<p>loading...</p>
</>
);
}
};
/* destructure props object */
export default function GroupAgencyPerDayTable({ array, title, date }) {
if (array !== undefined && array !== null) {
/* return a React element */
return (
<>
<p>
Table of
{array.length}
{title}
{' '}
for
{date}
:
</p>
<table>
<thead>
<tr>
<th>agency_name&nbsp;|</th>
<th>abs. trip count&nbsp;|</th>
<th>RT TU count&nbsp;|</th>
<th>RT TU %&nbsp;|</th>
<th>latest RT timestamp&nbsp;|</th>
</tr>
</thead>
<tbody>
<GroupAgencyPerDayTableEntries array={array} />
</tbody>
</table>
</>
);
}
return (
<p>loading...</p>
);
}
GroupAgencyPerDayTable.propTypes = {
array: PropTypes.array,
title: PropTypes.string,
date: PropTypes.string,
array: PropTypes.array,
title: PropTypes.string,
date: PropTypes.string,
};

View File

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

View File

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

View File

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

View File

@ -1,49 +1,60 @@
import React from 'react'
import React from 'react';
export default function Header(){
return <>
<a href='/' rel="noopener noreferrer">
<button>
Home
</button>
</a>
<a href='/agency' rel="noopener noreferrer">
<button>
Agency
</button>
</a>
<a href='/files' rel="noopener noreferrer">
<button>
Files
</button>
</a>
<a href='/realtime' rel="noopener noreferrer">
<button>
Realtime
</button>
</a>
<a href='/contact' rel="noopener noreferrer">
<button>
Contact
</button>
</a>
<a href='https://www.swingbe.de/imprint/'
target="_blank" rel="noopener noreferrer">
<button>
Imprint
</button>
</a>
<a href='https://www.swingbe.de/privacy-policy/'
target="_blank" rel="noopener noreferrer">
<button>
Privacy Policy
</button>
</a>
<a href='https://git.wtf-eg.de/dancingCycle/gtfs-display'
target="_blank" rel="noopener noreferrer">
<button>
Source
</button>
</a>
</>;
};
export default function Header() {
return (
<>
<a href="/" rel="noopener noreferrer">
<button>
Home
</button>
</a>
<a href="/agency" rel="noopener noreferrer">
<button>
Agency
</button>
</a>
<a href="/files" rel="noopener noreferrer">
<button>
Files
</button>
</a>
<a href="/realtime" rel="noopener noreferrer">
<button>
Realtime
</button>
</a>
<a href="/contact" rel="noopener noreferrer">
<button>
Contact
</button>
</a>
<a
href="https://www.swingbe.de/imprint/"
target="_blank"
rel="noopener noreferrer"
>
<button>
Imprint
</button>
</a>
<a
href="https://www.swingbe.de/privacy-policy/"
target="_blank"
rel="noopener noreferrer"
>
<button>
Privacy Policy
</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 PropTypes from 'prop-types';
/*controlled component: input form value controlled by React*/
export default function Input({id, name, onChange, placeholder, title, type, value}) {
return <form >
<label htmlFor="input-agency">{name}: </label>
<input
name={name}
id={id}
className={name}
onChange={onChange}
placeholder={placeholder}
title={title}
type={type}
value={value}
required
/>
</form>;
};
/* controlled component: input form value controlled by React */
export default function Input({
id, name, onChange, placeholder, title, type, value,
}) {
return (
<form>
<label htmlFor="input-agency">
{name}
:
{' '}
</label>
<input
name={name}
id={id}
className={name}
onChange={onChange}
placeholder={placeholder}
title={title}
type={type}
value={value}
required
/>
</form>
);
}
Input.propTypes = {
id: PropTypes.string,
value: PropTypes.string,
name: PropTypes.string,
placeholder: PropTypes.string,
title: PropTypes.string,
type: PropTypes.string,
onChange: PropTypes.func
id: PropTypes.string,
value: PropTypes.string,
name: PropTypes.string,
placeholder: PropTypes.string,
title: PropTypes.string,
type: PropTypes.string,
onChange: PropTypes.func,
};

View File

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

View File

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

View File

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

View File

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

View File

@ -2,49 +2,48 @@ import React, { useState, useEffect } from 'react';
import axios from 'axios';
import config from '../config';
import Input from '../components/input';
import Input from './input';
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';
const [date, setDate] = useState(dateDefault);
const [data, setData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10; // Number of items to display per page
// 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);
}
};
//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]);
/*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 fetchData = async () => {
if (date !== dateDefault) {
try {
const address = `${config.API}trip-updates-odd-routes?day=${date}`;
//console.log('address: ' + address);
// 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);
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 routes request FAILED');
console.error('ERROR: trip-updates with odd routes request FAILED');
}
} catch (err) {
} catch (err) {
console.log(err);
}
}
};
}
}
};
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
@ -64,28 +63,34 @@ export default function OddTrips() {
};
return (
<>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<p>
abs odd route count: {data.length}, total pages: {Math.ceil(data.length / itemsPerPage)}, current page: {currentPage}
</p>
<dl>
{currentItems.map((item, index) => (
<dt key={index}>{item.trip_route_id}</dt>
<>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<p>
abs odd route count:
{' '}
{data.length}
, total pages:
{Math.ceil(data.length / itemsPerPage)}
, current page:
{currentPage}
</p>
<dl>
{currentItems.map((item, index) => (
<dt key={index}>{item.trip_route_id}</dt>
))}
</dl>
<button onClick={handlePreviousPage}>Previous</button>
<button onClick={handleNextPage}>Next</button>
</dl>
<button onClick={handlePreviousPage}>Previous</button>
<button onClick={handleNextPage}>Next</button>
</>
);
};
}

View File

@ -2,49 +2,48 @@ import React, { useState, useEffect } from 'react';
import axios from 'axios';
import config from '../config';
import Input from '../components/input';
import Input from './input';
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';
const [date, setDate] = useState(dateDefault);
const [data, setData] = useState([]);
const [currentPage, setCurrentPage] = useState(1);
const itemsPerPage = 10; // Number of items to display per page
//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);
// 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);
}
}
};
const startIndex = (currentPage - 1) * itemsPerPage;
const endIndex = startIndex + itemsPerPage;
@ -64,23 +63,29 @@ export default function OddTrips() {
};
return (
<>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<p>
abs odd trip count: {data.length}, total pages: {Math.ceil(data.length / itemsPerPage)}, current page: {currentPage}
</p>
<>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<p>
abs odd trip count:
{' '}
{data.length}
, total pages:
{Math.ceil(data.length / itemsPerPage)}
, current page:
{currentPage}
</p>
<dl>
{currentItems.map((item, index) => (
{currentItems.map((item, index) => (
<dt key={index}>{item.trip_trip_id}</dt>
))}
</dl>
@ -88,4 +93,4 @@ export default function OddTrips() {
<button onClick={handleNextPage}>Next</button>
</>
);
};
}

View File

@ -2,15 +2,14 @@ import React from 'react';
import PropTypes from 'prop-types';
export default function CountNext({ count }) {
if (count === '0'
if (count === '0'
|| count === null
|| count === undefined) {
return '0';
} else {
return count;
}
};
return '0';
}
return count;
}
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';
/*destructure props object*/
export default function OverviewNextTableEntry ({ agencyName, routeCount, stopCount, tripCount }) {
const routeCountBadge = <Count count={routeCount} />;
const stopCountBadge = <Count count={stopCount} />;
const tripCountBadge = <Count count={tripCount} />;
return (
<tr>
<td>{agencyName}</td>
<td>{routeCountBadge}</td>
<td>{stopCountBadge}</td>
<td>{tripCountBadge}</td>
</tr>
);
};
/* destructure props object */
export default function OverviewNextTableEntry({
agencyName, routeCount, stopCount, tripCount,
}) {
const routeCountBadge = <Count count={routeCount} />;
const stopCountBadge = <Count count={stopCount} />;
const tripCountBadge = <Count count={tripCount} />;
return (
<tr>
<td>{agencyName}</td>
<td>{routeCountBadge}</td>
<td>{stopCountBadge}</td>
<td>{tripCountBadge}</td>
</tr>
);
}
OverviewNextTableEntry.propTypes = {
agencyName: PropTypes.string,
routeCount: PropTypes.string,
stopCount: PropTypes.string,
tripCount: PropTypes.string
agencyName: PropTypes.string,
routeCount: PropTypes.string,
stopCount: PropTypes.string,
tripCount: PropTypes.string,
};

View File

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

View File

@ -4,44 +4,46 @@ import PropTypes from 'prop-types';
import Entry from './overview-next-table-entry';
import Head from './overview-next-table-head';
/*the simplest way to define a component is to write a JavaScript function*/
/*destructure props object*/
export default function OverviewNextTable ({ overview }) {
const handleOverview = () => {
/* the simplest way to define a component is to write a JavaScript function */
/* destructure props object */
export default function OverviewNextTable({ overview }) {
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 ) {
//iterate over array
return overview.map((item, index) => {
return (
<Entry
agencyName={item.agency_name}
routeCount={item.rts_cnt}
stopCount={item.stps_cnt}
tripCount={item.trps_cnt}
key={item.agency_id}
/>
);
});
} else {
console.error('overview NOT available');
return null;
}
};
/*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>;
};
/* 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 = {
overview: PropTypes.array
overview: PropTypes.array,
};

View File

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

View File

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

View File

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

View File

@ -4,46 +4,43 @@ import Table from 'react-bootstrap/Table';
import Entry from './overview-table-entry';
import Head from './overview-table-head';
/*the simplest way to define a component is to write a JavaScript function*/
/*destructure props object*/
function OverviewTable ({ overview }) {
const handleOverview = () => {
if (overview) {
//iterate over array
return overview.map((item, index) => {
return (
<Entry
agencyName={item.agency_name}
routeCount={item.route_count}
tripCount={item.trip_count}
day={item.day}
key={index}
/>
);
});
} else {
console.error('overview NOT available');
return null;
}
};
/* the simplest way to define a component is to write a JavaScript function */
/* destructure props object */
function OverviewTable({ overview }) {
const handleOverview = () => {
if (overview) {
// iterate over array
return overview.map((item, index) => (
<Entry
agencyName={item.agency_name}
routeCount={item.route_count}
tripCount={item.trip_count}
day={item.day}
key={index}
/>
));
}
console.error('overview NOT available');
return null;
};
/*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>
<Head />
</thead>
<tbody>{handleOverview()}</tbody>
</Table>
</>
);
/* 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>
<Head />
</thead>
<tbody>{handleOverview()}</tbody>
</Table>
</>
);
}
OverviewTable.propTypes = {
overview: PropTypes.array
overview: PropTypes.array,
};
export default OverviewTable;

View File

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

View File

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

View File

@ -1,28 +1,37 @@
import React from 'react';
import PropTypes from 'prop-types';
export default function PerDayTableEntries ({array}) {
if ( array !== undefined && array !== null && array.length > 0 ) {
//TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
//iterate over array
return array.map((item, index) => {
return (
<tr
key={index}
>
<td>{item.count}&nbsp;|</td>
<td>{item.rt_part}&nbsp;|</td>
<td>{item.count === 0 ? 0 : ((item.rt_part / item.count) * 100).toFixed(2)}&nbsp;|</td>
<td>{item.timestamp_pgsql}&nbsp;|</td>
</tr>
);
});
}else{
//data is empty
return null;
}
};
export default function PerDayTableEntries({ array }) {
if (array !== undefined && array !== null && array.length > 0) {
// TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
// iterate over array
return array.map((item, index) => (
<tr
key={index}
>
<td>
{item.count}
&nbsp;|
</td>
<td>
{item.rt_part}
&nbsp;|
</td>
<td>
{item.count === 0 ? 0 : ((item.rt_part / item.count) * 100).toFixed(2)}
&nbsp;|
</td>
<td>
{item.timestamp_pgsql}
&nbsp;|
</td>
</tr>
));
}
// data is empty
return null;
}
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';
/*destructure props object*/
export default function PerDayTable ({array, title, date}){
if ( array !== undefined && array !== null) {
/*return a React element*/
return (
<>
<p>Table of {array.length}&nbsp;{title} for {date}:</p>
<table>
<thead>
<tr>
<th>abs. trip count&nbsp;|</th>
<th>RT TU count&nbsp;|</th>
<th>RT TU %&nbsp;|</th>
<th>latest RT timestamp&nbsp;|</th>
</tr>
</thead>
<tbody>
<PerDayTableEntries array={array} />
</tbody>
</table>
</>
);
}else{
return (
<>
<p>loading...</p>
</>
);
}
};
/* destructure props object */
export default function PerDayTable({ array, title, date }) {
if (array !== undefined && array !== null) {
/* return a React element */
return (
<>
<p>
Table of
{array.length}
{title}
{' '}
for
{date}
:
</p>
<table>
<thead>
<tr>
<th>abs. trip count&nbsp;|</th>
<th>RT TU count&nbsp;|</th>
<th>RT TU %&nbsp;|</th>
<th>latest RT timestamp&nbsp;|</th>
</tr>
</thead>
<tbody>
<PerDayTableEntries array={array} />
</tbody>
</table>
</>
);
}
return (
<p>loading...</p>
);
}
PerDayTable.propTypes = {
array: PropTypes.array,
title: PropTypes.string,
date: PropTypes.string
array: PropTypes.array,
title: PropTypes.string,
date: PropTypes.string,
};

View File

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

View File

@ -9,67 +9,117 @@ import PerDay from '../pages/per-day';
import TripUpdates from '../pages/trip-updates-route-day';
export default function Realtime({ state }) {
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' ) {
if (state === 'odd-routes') {
return (
<>
<p>
Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>agencies</b> level:
</p>
<GroupAgencyPerDay />
</>
<>
<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 === 'routes' ) {
} if (state === 'odd-trips') {
return (
<>
<p>
Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>routes</b> level:
</p>
<AgencyPerDay />
</>
<>
<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 === 'trips' ) {
} if (state === 'feed') {
return (
<>
<p>
Analyse <b>GTFS Realtime</b> entities <b>TripUpdate</b> on <b>trips</b> level:
</p>
<TripUpdates />
</>
<>
<p>
Analyse <b>GTFS Realtime</b>
{' '}
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 = {
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
*/
export default function RouteSelect({ name, onChange, rry }) {
if (rry !== undefined && rry !== null) {
return (<>
<form >
<label htmlFor="input-route">{name}: </label>
<select
name={name}
id={name}
className={name}
onChange={onChange}
placeholder={name}
defaultValue={name}
title={name}
type="text"
required
>
{rry.map((item) => (
<option key={item.route_id} value={item.route_id}>
{item.route_short_name}
</option>
if (rry !== undefined && rry !== null) {
return (
<form>
<label htmlFor="input-route">
{name}
:
{' '}
</label>
<select
name={name}
id={name}
className={name}
onChange={onChange}
placeholder={name}
defaultValue={name}
title={name}
type="text"
required
>
{rry.map((item) => (
<option key={item.route_id} value={item.route_id}>
{item.route_short_name}
</option>
))}
</select>
</form>
</>);
} else {
return <p>Loading...</p>;
}
};
</select>
</form>
);
}
return <p>Loading...</p>;
}
RouteSelect.propTypes = {
name: PropTypes.string,
onChange: PropTypes.func,
rry: PropTypes.array
name: PropTypes.string,
onChange: PropTypes.func,
rry: PropTypes.array,
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,52 +4,50 @@ import Table from 'react-bootstrap/Table';
import Entry from './service-table-entry';
import Head from './service-table-head';
/*the simplest way to define a component is to write a JavaScript function*/
function ServiceTable ({service,render}) {
/*map over msgs array and return Standortmeldungen*/
const getService = () => {
//iterate over object
if (service) {
return Object.entries(service).map((trips, key) => {
/*the strict equals operator does not converts operants of differnet type*/
//console.log('key: ' + key);
let objTrips = trips[1];
let count = Object.keys(objTrips).length;
//console.log('count: ' + count);
let time = parseInt(trips[0], 10);
//console.log('time: ' + parseInt(time, 10));
let date = new Date(time);
//console.log('date: ' + date);
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;
/* the simplest way to define a component is to write a JavaScript function */
function ServiceTable({ service, render }) {
/* map over msgs array and return Standortmeldungen */
const getService = () => {
// iterate over object
if (service) {
return Object.entries(service).map((trips, key) => {
/* the strict equals operator does not converts operants of differnet type */
// console.log('key: ' + key);
const objTrips = trips[1];
const count = Object.keys(objTrips).length;
// console.log('count: ' + count);
const time = parseInt(trips[0], 10);
// console.log('time: ' + parseInt(time, 10));
const date = new Date(time);
// console.log('date: ' + date);
return <Entry date={date.toDateString()} count={count} key={key} />;
});
}
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 = {
service: PropTypes.object,
render: PropTypes.bool
service: PropTypes.object,
render: PropTypes.bool,
};
export default ServiceTable;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,25 +3,22 @@ import PropTypes from 'prop-types';
import TableHead from './agency-id-name-table-head';
/*the simplest way to define a component is to write a JavaScript function*/
/*destructure props object*/
export default function TableSwitchHead({tripCalendar}) {
if(tripCalendar!==undefined){
/*return a React element*/
return (
<>
<thead>
<TableHead
tripCalendar={tripCalendar}
/>
</thead>
</>
);
}else{
return <p>loading...</p>
}
/* the simplest way to define a component is to write a JavaScript function */
/* destructure props object */
export default function TableSwitchHead({ tripCalendar }) {
if (tripCalendar !== undefined) {
/* return a React element */
return (
<thead>
<TableHead
tripCalendar={tripCalendar}
/>
</thead>
);
}
return <p>loading...</p>;
}
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 TableEntrySwitch from './table-entry-switch';
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);
/*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){
if (res.data) {
setAry((ary) => res.data);
let data=filterData(res.data,name,filter);
const data = filterData(res.data, name, filter);
setAryFiltered((aryFiltered) => data);
}else{
console.error('fetch() res NOT available');
}
} else {
console.error(`name ${name} not valid`);
setAry((ary) => []);
} else {
console.error('fetch() res NOT available');
}
} else {
console.error(`name ${name} not valid`);
setAry((ary) => []);
}
} catch (err) {
console.error('err.message: ' + err.message);
} catch (err) {
console.error(`err.message: ${err.message}`);
setAry((ary) => []);
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 = {
name: PropTypes.string,
isFetched: PropTypes.bool,
offset: PropTypes.number,
limit: PropTypes.number,
filter: PropTypes.string
name: PropTypes.string,
isFetched: PropTypes.bool,
offset: PropTypes.number,
limit: PropTypes.number,
filter: PropTypes.string,
};
export default TableSwitch;

View File

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

View File

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

View File

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

View File

@ -1,33 +1,53 @@
import React from 'react';
import PropTypes from 'prop-types';
export default function TripUpdatesRouteDayTableEntries ({array}) {
if ( array !== undefined && array !== null && array.length > 0 ) {
//TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
//iterate over array
return array.map((item, index) => {
return (
<tr
key={index}
>
<td>{item.agency_name}&nbsp;|</td>
<td>{item.agency_id}&nbsp;|</td>
<td>{item.route_id}&nbsp;|</td>
<td>{item.route_short_name}&nbsp;|</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>
);
});
}else{
//data is empty
return null;
}
};
export default function TripUpdatesRouteDayTableEntries({ array }) {
if (array !== undefined && array !== null && array.length > 0) {
// TODO Shall we switch from UTC to local time zone for item.timestamp_pgsql?
// iterate over array
return array.map((item, index) => (
<tr
key={index}
>
<td>
{item.agency_name}
&nbsp;|
</td>
<td>
{item.agency_id}
&nbsp;|
</td>
<td>
{item.route_id}
&nbsp;|
</td>
<td>
{item.route_short_name}
&nbsp;|
</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 = {
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';
/*destructure props object*/
export default function TripUpdatesRouteDayTable ({array, title, date}){
if ( array !== undefined && array !== null) {
/*return a React element*/
return (
<>
<p>Table of {array.length}&nbsp;{title} for {date}:</p>
<table>
<thead>
<tr>
<th>agency_name&nbsp;|</th>
<th>agency_id&nbsp;|</th>
<th>route_id&nbsp;|</th>
<th>route_short_name&nbsp;|</th>
<th>trip_id&nbsp;|</th>
<th>trip_short_name&nbsp;|</th>
<th>trip_headsign&nbsp;|</th>
<th>timestamp_pgsql&nbsp;|</th>
</tr>
</thead>
<tbody>
<TripUpdatesRouteDayTableEntries array={array} />
</tbody>
</table>
</>
);
}else{
return (
<>
<p>loading...</p>
</>
);
}
};
/* destructure props object */
export default function TripUpdatesRouteDayTable({ array, title, date }) {
if (array !== undefined && array !== null) {
/* return a React element */
return (
<>
<p>
Table of
{array.length}
{title}
{' '}
for
{date}
:
</p>
<table>
<thead>
<tr>
<th>agency_name&nbsp;|</th>
<th>agency_id&nbsp;|</th>
<th>route_id&nbsp;|</th>
<th>route_short_name&nbsp;|</th>
<th>trip_id&nbsp;|</th>
<th>trip_short_name&nbsp;|</th>
<th>trip_headsign&nbsp;|</th>
<th>timestamp_pgsql&nbsp;|</th>
</tr>
</thead>
<tbody>
<TripUpdatesRouteDayTableEntries array={array} />
</tbody>
</table>
</>
);
}
return (
<p>loading...</p>
);
}
TripUpdatesRouteDayTable.propTypes = {
array: PropTypes.array,
title: PropTypes.string,
date: PropTypes.string
array: PropTypes.array,
title: PropTypes.string,
date: PropTypes.string,
};

View File

@ -1,31 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
export default function TableEntries ({array}) {
if ( array !== undefined && array !== null && array.length > 0 ) {
//iterate over array
return array.map((item, index) => {
return (
<tr
key={item.trip_id}
>
<td>{item.agency_name}</td>
<td>{item.agency_id}</td>
<td>{item.route_id}</td>
<td>{item.route_short_name}</td>
<td>{item.service_id}</td>
<td>{item.trip_id}</td>
<td>{item.trip_short_name}</td>
<td>{item.trip_headsign}</td>
</tr>
);
});
}else{
//data is empty
return null;
}
};
export default function TableEntries({ array }) {
if (array !== undefined && array !== null && array.length > 0) {
// iterate over array
return array.map((item, index) => (
<tr
key={item.trip_id}
>
<td>{item.agency_name}</td>
<td>{item.agency_id}</td>
<td>{item.route_id}</td>
<td>{item.route_short_name}</td>
<td>{item.service_id}</td>
<td>{item.trip_id}</td>
<td>{item.trip_short_name}</td>
<td>{item.trip_headsign}</td>
</tr>
));
}
// data is empty
return null;
}
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';
/*destructure props object*/
export default function TripsRouteDayTable ({array, title}){
if ( array !== undefined && array !== null) {
/*return a React element*/
return (
<>
<p>Table of {array.length}&nbsp;{title} for today:</p>
<table>
<thead>
<tr>
<th>agency_name</th>
<th>agency_id</th>
<th>route_id</th>
<th>route_short_name</th>
<th>service_id</th>
<th>trip_id</th>
<th>trip_short_name</th>
<th>trip_headsign</th>
</tr>
</thead>
<tbody>
<TableEntries array={array} />
</tbody>
</table>
</>
);
}else{
return (
<>
<p>loading...</p>
</>
);
}
};
/* destructure props object */
export default function TripsRouteDayTable({ array, title }) {
if (array !== undefined && array !== null) {
/* return a React element */
return (
<>
<p>
Table of
{array.length}
{title}
{' '}
for today:
</p>
<table>
<thead>
<tr>
<th>agency_name</th>
<th>agency_id</th>
<th>route_id</th>
<th>route_short_name</th>
<th>service_id</th>
<th>trip_id</th>
<th>trip_short_name</th>
<th>trip_headsign</th>
</tr>
</thead>
<tbody>
<TableEntries array={array} />
</tbody>
</table>
</>
);
}
return (
<p>loading...</p>
);
}
TripsRouteDayTable.propTypes = {
array: PropTypes.array,
title: PropTypes.string
array: PropTypes.array,
title: PropTypes.string,
};

View File

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

View File

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

View File

@ -1,4 +1,4 @@
export default {
API: 'http://localhost:65529/',
GTFS_VALIDATOR_REPORT: 'https://www.v1gtfs.vbn.api.swingbe.de/gtfs-validator/report.html',
API: 'http://localhost:65529/',
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 { 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 Home from './pages/homepage';
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';
//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 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';
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 packageInfo from '../package.json'
import packageInfo from '../package.json';
const VERSION = packageInfo.version;
export default function Main() {
return (
//Router is the router implementation for HTML5 browsers
//Link enables Routes on an anchor tag
//Switch returns only the first matching route rather than all
//Route is the conditionally shown component //based on matching a path to a URL
<Router>
<Header />
<h1>GTFS-Display</h1>
<p>
This website processes GTFS Realtime and Schedule data.
</p>
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/agency" element={<OverviewNext />} />
<Route exact path="/files" element={<Files />} />
<Route exact path="/realtime" element={<Realtime />} />
<Route exact path="/contact" element={<Contact />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</Router>
);
/** TODO Is this route of any value?
return (
// Router is the router implementation for HTML5 browsers
// Link enables Routes on an anchor tag
// Switch returns only the first matching route rather than all
// Route is the conditionally shown component //based on matching a path to a URL
<Router>
<Header />
<h1>GTFS-Display</h1>
<p>
This website processes GTFS Realtime and Schedule data.
</p>
<Routes>
<Route exact path="/" element={<Home />} />
<Route exact path="/agency" element={<OverviewNext />} />
<Route exact path="/files" element={<Files />} />
<Route exact path="/realtime" element={<Realtime />} />
<Route exact path="/contact" element={<Contact />} />
<Route path="*" element={<Navigate to="/" />} />
</Routes>
</Router>
);
/** TODO Is this route of any value?
<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 />} />
*/
/** TODO Disable this route as it is not working!
/** TODO Disable this route as it is not working!
<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 />} />
*/
};
}

View File

@ -7,92 +7,92 @@ import AgencySelect from '../components/agency-select';
import Input from '../components/input';
export default function AgencyPerDay() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const agencyNameDefault = 'Select GTFS agency_name';
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 [strngAgencyId, setStrngAgencyId] = useState(agencyNameDefault);
const [rryAgencyPerDay, setRryAgencyPerDay] = useState([]);
const [rryAgencies, setRryAgencies] = useState([]);
const [rryAgencyPerDay, setRryAgencyPerDay] = useState([]);
const [rryAgencies, setRryAgencies] = useState([]);
// 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);
}
};
//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);
}
};
const getRryAgencies = async () => {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const getRryAgencies = async () => {
try {
/* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const address = `${config.API}agencyids`;
//console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address);
//console.log('trip-updates-route-day res.data.length: Agencies: ' + res.data.length);
setRryAgencies((rryAgencies) => res.data);
} catch (err) {
console.error('err.message: ' + err.message);
}
};
// console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address);
// console.log('trip-updates-route-day res.data.length: Agencies: ' + res.data.length);
setRryAgencies((rryAgencies) => res.data);
} catch (err) {
console.error(`err.message: ${err.message}`);
}
};
const getRryAgencyPerDay = async () => {
if ( strngAgencyId !== agencyNameDefault &&
date !== dateDefault) {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const getRryAgencyPerDay = async () => {
if (strngAgencyId !== agencyNameDefault
&& date !== dateDefault) {
try {
/* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const address = `${config.API}trip-updates-by-agency-day?agencyid=${strngAgencyId}&day=${date}`;
//console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address);
if ( res.data !== undefined && res.data !== null ) {
//console.log('trip-updates-route-day res.data.length: AgencyPerDay: ' + res.data.length);
setRryAgencyPerDay((rryAgencyPerDay) => res.data);
// console.log('trip-updates-route-day res.data.length: address: ' + address);
const res = await axios.get(address);
if (res.data !== undefined && res.data !== null) {
// console.log('trip-updates-route-day res.data.length: AgencyPerDay: ' + res.data.length);
setRryAgencyPerDay((rryAgencyPerDay) => res.data);
} 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) {
console.error('err.message: ' + err.message);
}
}
};
} catch (err) {
console.error(`err.message: ${err.message}`);
}
}
};
const handleChangeAgencyId = (event) => {
//console.log('trip-updates-route-day: handleChangeAgencyId() value: ' + event.target.value);
setStrngAgencyId((strngAgencyId) => event.target.value);
};
const handleChangeAgencyId = (event) => {
// console.log('trip-updates-route-day: handleChangeAgencyId() value: ' + event.target.value);
setStrngAgencyId((strngAgencyId) => event.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(() => {
getRryAgencies();
/*use an empty dependency array to ensure the hook is running only once*/
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
}, []);
/* 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(() => {
getRryAgencies();
/* use an empty dependency array to ensure the hook is running only once */
/* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, []);
useEffect(() => {
//console.log('trip-updates-route-day: useEffect() strngAgencyId: ' + strngAgencyId);
getRryAgencyPerDay();
/*TODO study dependency array: https://reactjs.org/docs/hooks-effect.html*/
}, [strngAgencyId, date]);
useEffect(() => {
// console.log('trip-updates-route-day: useEffect() strngAgencyId: ' + strngAgencyId);
getRryAgencyPerDay();
/* TODO study dependency array: https://reactjs.org/docs/hooks-effect.html */
}, [strngAgencyId, date]);
return <>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<AgencySelect rry={rryAgencies} name={agencyNameDefault} onChange={handleChangeAgencyId} />
<AgencyPerDayTable array={rryAgencyPerDay} title={'routes'} date={date}/>
</>;
};
return (
<>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<AgencySelect rry={rryAgencies} name={agencyNameDefault} onChange={handleChangeAgencyId} />
<AgencyPerDayTable array={rryAgencyPerDay} title="routes" date={date} />
</>
);
}

View File

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

View File

@ -6,60 +6,61 @@ import GroupAgencyPerDayTable from '../components/group-agency-per-day-table';
import Input from '../components/input';
export default function GroupAgencyPerDay() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const [rryGroupAgencyPerDay, setRryGroupAgencyPerDay] = useState([]);
const [rryAgencies, setRryAgencies] = useState([]);
const [rryGroupAgencyPerDay, setRryGroupAgencyPerDay] = useState([]);
const [rryAgencies, setRryAgencies] = useState([]);
// 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);
}
};
//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);
}
};
const getRryGroupAgencyPerDay = async () => {
if ( date !== dateDefault) {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
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}`;
//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);
setRryGroupAgencyPerDay((rryGroupAgencyPerDay) => res.data);
// 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);
setRryGroupAgencyPerDay((rryGroupAgencyPerDay) => res.data);
} 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) {
console.error('err.message: ' + err.message);
}
}
};
} catch (err) {
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(() => {
getRryGroupAgencyPerDay();
/*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 */
/* hook need to be placed in body of the function component in which it is used */
useEffect(() => {
getRryGroupAgencyPerDay();
/* 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]);
return <>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<GroupAgencyPerDayTable array={rryGroupAgencyPerDay} title={'agencies'} date={date}/>
</>;
};
return (
<>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<GroupAgencyPerDayTable array={rryGroupAgencyPerDay} title="agencies" date={date} />
</>
);
}

View File

@ -1,8 +1,8 @@
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';
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';
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 () => {
try {
/*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);
if (res !== null && res !== undefined) {
setCnts(res.data);
}
} catch (err) {
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} />;
} catch (err) {
console.error(`err.message: ${err.message}`);
}
};
};
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';
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*/
/*initialise as empty array*/
const [overview, setOverview] = useState([]);
const [agencies, setAgencies] = useState(false);
const [wait, setWait] = useState(false);
const handleAsyncOps = async (isMounted) => {
/* get data for each agency */
for (let j = 0; j < overview.length; j++) {
// console.log('j: ' + j);
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) => {
/*get data for each agency*/
for (var j = 0; j < overview.length; j++) {
//console.log('j: ' + j);
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 getAgencies = async (isMounted) => {
try {
/* get agencies */
/* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const res = await axios.get(`${config.API}agency-all`);
const aryOv = res.data;
for (let i = 0; i < aryOv.length; i++) {
const agencyId = aryOv[i].agency_id;
const agencyName = aryOv[i].agency_name;
const objOvItem = {};
objOvItem.agency_id = agencyId;
objOvItem.agency_name = agencyName;
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}`);
}
};
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) => {
try {
/*get agencies*/
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
const res = await axios.get(`${config.API}agency-all`);
let aryOv = res.data;
for (var i = 0; i < aryOv.length; i++) {
let agencyId = aryOv[i].agency_id;
let agencyName = aryOv[i].agency_name;
let objOvItem = {};
objOvItem['agency_id'] = agencyId;
objOvItem['agency_name'] = agencyName;
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);
}
/* 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. */
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]);
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]);
/* 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(() => {
/* 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 you want to get an updated state value then use useEffect hook with dependency array. React will execute this hook after each state update.*/
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]);
getAgencies(isMounted);
/*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(() => {
/*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;
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 */
}, []);
getAgencies(isMounted);
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} />;
};
return <OverviewTable overview={overview} />;
}

View File

@ -6,60 +6,61 @@ import PerDayTable from '../components/per-day-table';
import Input from '../components/input';
export default function PerDay() {
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const dateDefault = 'Select date';
const [date, setDate] = useState(dateDefault);
const [rryPerDay, setRryPerDay] = useState([]);
const [rryAgencies, setRryAgencies] = useState([]);
const [rryPerDay, setRryPerDay] = useState([]);
const [rryAgencies, setRryAgencies] = useState([]);
// 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);
}
};
//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);
}
};
const fetchData = async () => {
if ( date !== dateDefault) {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
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}`;
//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);
setRryPerDay((rryPerDay) => res.data);
// 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);
setRryPerDay((rryPerDay) => res.data);
} 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) {
console.error('err.message: ' + err.message);
}
}
};
} catch (err) {
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(() => {
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]);
/* 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]);
return <>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={date}
/>
</label>
<PerDayTable array={rryPerDay} title={'feeds'} date={date}/>
</>;
};
return (
<>
<label>
<Input
id="inputDate"
name={dateDefault}
onChange={handleDate}
placeholder="Enter date ${dateDefault}"
type="date"
title="Enter date ${dateDefault}"
value={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';
export default function Realtime() {
const [state, setState] = useState('');
const [state, setState] = useState('');
const handleChange = (e) => {
setState(e.target.value);
}
return (
<>
<RadioButton state={state} onChange={handleChange}/>
<br />
<Rltm state={state} />
</>
);
};
const handleChange = (e) => {
setState(e.target.value);
};
return (
<>
<RadioButton state={state} onChange={handleChange} />
<br />
<Rltm state={state} />
</>
);
}

View File

@ -7,78 +7,79 @@ import ChartBar from '../components/chart-bar';
import ChartLine from '../components/chart-line';
import GtfsService from '../utils/gtfs-service';
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*/
const getObjService = async () => {
try {
/*TODO handle errors: https://www.valentinog.com/blog/await-react/*/
let url = `${config.API}servicedays?routeshortname=${route}`;
setLoading(true);
const objService = await axios.get(url);
setLoading(false);
setRender(true);
function 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([]);
/*set state*/
setObjService(objService.data);
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);
}
};
/* fetch objService in a JavaScript function */
const getObjService = async () => {
try {
/* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const url = `${config.API}servicedays?routeshortname=${route}`;
setLoading(true);
const objService = await axios.get(url);
setLoading(false);
setRender(true);
const handleSubmit = () => {
event.preventDefault();
getObjService();
};
/* set state */
setObjService(objService.data);
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) => {
setRoute(e.target.value);
};
const handleSubmit = () => {
event.preventDefault();
getObjService();
};
/*element representing user-defined React component*/
const msgTable = <ServiceTable render={render} service={objService} />;
const bar = <ChartBar route={route} time={time} trip={trip} />;
const line = <ChartLine route={route} time={time} trip={trip} />;
const handleChange = (e) => {
setRoute(e.target.value);
};
return (
<>
<FormValue
value={route}
valueName={'route'}
onSubmit={handleSubmit}
onChange={handleChange}
/>
<Loading loading={loading} />
<div
style={{
height: '500px',
width: '900px'
}}
>
{bar}
</div>
<div
style={{
height: '500px',
width: '900px'
}}
>
{line}
</div>
{msgTable}
</>
);
};
/* element representing user-defined React component */
const msgTable = <ServiceTable render={render} service={objService} />;
const bar = <ChartBar route={route} time={time} trip={trip} />;
const line = <ChartLine route={route} time={time} trip={trip} />;
return (
<>
<FormValue
value={route}
valueName="route"
onSubmit={handleSubmit}
onChange={handleChange}
/>
<Loading loading={loading} />
<div
style={{
height: '500px',
width: '900px',
}}
>
{bar}
</div>
<div
style={{
height: '500px',
width: '900px',
}}
>
{line}
</div>
{msgTable}
</>
);
}
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';
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 [agencyIds, setAgencyIds] = useState([]);
const getAgencyIds = async () => {
try {
/* get agencyIds */
/* TODO handle errors: https://www.valentinog.com/blog/await-react/ */
const res = await axios.get(`${config.API}agencyids`);
const getAgencyIds = async () => {
try {
/*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);
const aryAgencyIds = res.data;
// console.log('TripCalendar aryAgencyIds.length:'+aryAgencyIds.length);
setAgencyIds(aryAgencyIds);
} catch (err) {
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>
} catch (err) {
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>
);
}
return <p>loading...</p>;
}

View File

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

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