Display destination name

This commit is contained in:
Gabriel Augendre 2021-11-14 12:26:42 +01:00
parent 3d6037a56a
commit 97d2d4c460
6 changed files with 38 additions and 60 deletions

View file

@ -4,6 +4,7 @@
"private": true, "private": true,
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.36", "@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-regular-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4", "@fortawesome/free-solid-svg-icons": "^5.15.4",
"@fortawesome/react-fontawesome": "^0.1.16", "@fortawesome/react-fontawesome": "^0.1.16",
"@testing-library/jest-dom": "^5.11.4", "@testing-library/jest-dom": "^5.11.4",

View file

@ -7,16 +7,6 @@ body {
margin-bottom: 2rem; margin-bottom: 2rem;
} }
.placeholder:empty:before {
content: "\200b";
}
.placeholder {
display: inline-block;
width: 50px;
background-color: #c9c9c9;
}
footer { footer {
position: fixed; position: fixed;
bottom: 0; bottom: 0;

View file

@ -1,18 +1,19 @@
import React from 'react'; import React from 'react';
import './App.css'; import './App.css';
import Tcl, {ILigne} from "./Tcl"; import Tcl, {ILigne, IStop} from "./Tcl";
import {clearInterval, setInterval} from "timers"; import {clearInterval, setInterval} from "timers";
import {Col, Container, Dropdown, DropdownButton, Row} from "react-bootstrap"; import {Button, Col, Container, Row} from "react-bootstrap";
import {IStationInfo, IStationsInfoWrapper, IStationsStatusWrapper, IVelovStation} from "./IVelov"; import {IStationInfo, IStationsInfoWrapper, IStationsStatusWrapper, IVelovStation} from "./IVelov";
import Velov from "./Velov"; import Velov from "./Velov";
interface ITclFilteredApi { interface ITclFilteredApi {
passages: ILigne[]; passages: ILigne[];
stop: IStop;
} }
interface IAppState { interface IAppState {
passages: ILigne[]; tcl?: ITclFilteredApi;
refreshDate?: string; refreshDate?: string;
stations: IVelovStation[]; stations: IVelovStation[];
} }
@ -21,6 +22,7 @@ class App extends React.Component<{}, IAppState> {
timerId?: ReturnType<typeof setInterval>; timerId?: ReturnType<typeof setInterval>;
refreshSeconds: number; refreshSeconds: number;
monitoredVelovStationIds: string[] = []; monitoredVelovStationIds: string[] = [];
monitoredTclStopId: string;
constructor(props: {}) { constructor(props: {}) {
super(props); super(props);
@ -31,26 +33,23 @@ class App extends React.Component<{}, IAppState> {
if (velovStationIds) { if (velovStationIds) {
this.monitoredVelovStationIds = velovStationIds.split(";"); this.monitoredVelovStationIds = velovStationIds.split(";");
} }
this.monitoredTclStopId = urlParams.get("tclStopId") || "290";
if (this.refreshSeconds <= 5) { if (this.refreshSeconds <= 5) {
this.refreshSeconds = 60; this.refreshSeconds = 60;
} }
this.state = {passages: [{ligne: undefined, delais: [undefined]}], stations: []}; this.state = {stations: []};
} }
render() { render() {
return <div> return <div>
<Container className="main"> <Container className="main">
<Row sm={2} md={3} className="g-4"> <Row sm={2} md={3} className="g-4">
{this.state.passages.map((ligne) => <Tcl key={ligne.ligne} ligne={ligne.ligne} {this.state.tcl?.passages.map((ligne) => <Tcl key={ligne.ligne} {...ligne}/>)}
delais={ligne.delais}/>)} {this.state.stations.map((station) => <Velov key={station.info.station_id} {...station}/>)}
{this.state.stations.map((station) => <Velov key={station.info.station_id} info={station.info} status={station.status}/>)}
</Row> </Row>
<Row> <Row>
<Col> <Col>
<DropdownButton variant="secondary" size="lg" title="Refresh"> <Button variant="secondary" size="lg" onClick={this.refreshData}>Refresh</Button>
<Dropdown.Item onClick={this.refreshData}>Data</Dropdown.Item>
<Dropdown.Item onClick={this.reload}>Full page</Dropdown.Item>
</DropdownButton>
</Col> </Col>
</Row> </Row>
</Container> </Container>
@ -91,7 +90,7 @@ class App extends React.Component<{}, IAppState> {
private refresh() { private refresh() {
const headers = new Headers(); const headers = new Headers();
headers.set("Authorization", `Basic ${process.env.REACT_APP_TCL_AUTH}`); headers.set("Authorization", `Basic ${process.env.REACT_APP_TCL_AUTH}`);
const tclPromise = http<ITclFilteredApi>("https://tcl.augendre.info/stop/290", { const tclPromise = http<ITclFilteredApi>(`https://tcl.augendre.info/stop/${this.monitoredTclStopId}`, {
method: "GET", method: "GET",
headers: headers headers: headers
}); });
@ -126,7 +125,7 @@ class App extends React.Component<{}, IAppState> {
} }
this.setState({ this.setState({
refreshDate: new Date().toLocaleString("fr-fr"), refreshDate: new Date().toLocaleString("fr-fr"),
passages: tcl.passages, tcl: tcl,
stations: stations, stations: stations,
}); });
}) })

View file

@ -1,14 +1,20 @@
import React from "react"; import React from "react";
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-fontawesome'
import {faBus} from '@fortawesome/free-solid-svg-icons' import {faBus, faDirections} from '@fortawesome/free-solid-svg-icons'
import {faClock} from '@fortawesome/free-regular-svg-icons'
import {Card, Col, ListGroup} from "react-bootstrap"; import {Card, Col, ListGroup} from "react-bootstrap";
import {placeholder} from "./utils";
export type PassageType = string | undefined; export type PassageType = string | undefined;
export interface IStop {
id: number;
name: string;
}
export interface ILigne { export interface ILigne {
ligne?: string; ligne: string;
delais: PassageType[]; delais: PassageType[];
destination: IStop;
} }
export default class Tcl extends React.Component<ILigne> { export default class Tcl extends React.Component<ILigne> {
@ -16,33 +22,17 @@ export default class Tcl extends React.Component<ILigne> {
return <Col> return <Col>
<Card> <Card>
<Card.Header> <Card.Header>
<FontAwesomeIcon icon={faBus}/> {placeholder(this.props.ligne)} <FontAwesomeIcon icon={faBus}/> {this.props.ligne}
</Card.Header> </Card.Header>
<Passages passages={this.props.delais}/> <ListGroup variant="flush">
<ListGroup.Item>
<FontAwesomeIcon icon={faDirections}/> {this.props.destination.name}
</ListGroup.Item>
{this.props.delais.map((passage, index) => <ListGroup.Item key={index}>
<FontAwesomeIcon icon={faClock}/> {passage}
</ListGroup.Item>)}
</ListGroup>
</Card> </Card>
</Col> </Col>
} }
} }
interface IPassagesProps {
passages: PassageType[];
}
class Passages extends React.Component<IPassagesProps> {
render() {
return <ListGroup variant="flush">
{this.props.passages?.map((passage, index) => <Passage key={index} passage={passage}/>)}
</ListGroup>
}
}
interface IPassageProps {
passage: PassageType;
}
class Passage extends React.Component<IPassageProps> {
render() {
return <ListGroup.Item>{placeholder(this.props.passage)}</ListGroup.Item>
}
}

View file

@ -1,9 +0,0 @@
import React from "react";
export function placeholder(value?: string) {
if (value === undefined) {
return <span className="placeholder"/>
} else {
return value;
}
}

View file

@ -1254,6 +1254,13 @@
dependencies: dependencies:
"@fortawesome/fontawesome-common-types" "^0.2.36" "@fortawesome/fontawesome-common-types" "^0.2.36"
"@fortawesome/free-regular-svg-icons@^5.15.4":
version "5.15.4"
resolved "https://registry.yarnpkg.com/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.4.tgz#b97edab436954333bbeac09cfc40c6a951081a02"
integrity sha512-9VNNnU3CXHy9XednJ3wzQp6SwNwT3XaM26oS4Rp391GsxVYA+0oDR2J194YCIWf7jNRCYKjUCOduxdceLrx+xw==
dependencies:
"@fortawesome/fontawesome-common-types" "^0.2.36"
"@fortawesome/free-solid-svg-icons@^5.15.4": "@fortawesome/free-solid-svg-icons@^5.15.4":
version "5.15.4" version "5.15.4"
resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz#2a68f3fc3ddda12e52645654142b9e4e8fbb6cc5" resolved "https://registry.yarnpkg.com/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.4.tgz#2a68f3fc3ddda12e52645654142b9e4e8fbb6cc5"