mirror of https://github.com/Lissy93/dashy.git
✨ Adds a London Underground status widget
This commit is contained in:
parent
c38a094a63
commit
cf7e021a82
|
@ -86,6 +86,33 @@ Displays the weather (temperature and conditions) for the next few days for a gi
|
||||||
units: imperial
|
units: imperial
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### TFL Status
|
||||||
|
|
||||||
|
Shows real-time tube status of the London Underground. All options are optional.
|
||||||
|
|
||||||
|
##### Options
|
||||||
|
|
||||||
|
**Field** | **Type** | **Required** | **Description**
|
||||||
|
--- | --- | --- | ---
|
||||||
|
**`showAll`** | `boolean` | _Optional_ | By default, details for lines with a Good Service are not visible, but you can click More Details to see all. Setting this option to `true` will show all lines on initial page load
|
||||||
|
**`sortAlphabetically`** | `boolean` | _Optional_ | By default lines are sorted by current status, set this option to `true` to instead sort them alphabetically
|
||||||
|
**`linesToShow`** | `array` | _Optional_ | By default all lines are shown. If you're only interested in the status of a few lines, then pass in an array of lines to show, specified by name
|
||||||
|
|
||||||
|
##### Example
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- name: TFL Status
|
||||||
|
icon: '🚋'
|
||||||
|
type: tfl-status
|
||||||
|
options:
|
||||||
|
showAll: true
|
||||||
|
sortAlphabetically: true
|
||||||
|
linesToShow:
|
||||||
|
- District
|
||||||
|
- Jubilee
|
||||||
|
- Central
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Dynamic Widgets
|
## Dynamic Widgets
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{
|
{
|
||||||
"home": {
|
"home": {
|
||||||
"no-results": "No Search Results",
|
"no-results": "No Search Results",
|
||||||
"no-data": "No Data Configured"
|
"no-data": "No Data Configured",
|
||||||
|
"no-items-section": "No Items to Show Yet"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"search-label": "Search",
|
"search-label": "Search",
|
||||||
|
@ -243,5 +244,19 @@
|
||||||
"download-file-tooltip": "Download all app config to your device, in a YAML file",
|
"download-file-tooltip": "Download all app config to your device, in a YAML file",
|
||||||
"view-title": "View Config"
|
"view-title": "View Config"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"widgets": {
|
||||||
|
"general": {
|
||||||
|
"loading": "Loading...",
|
||||||
|
"show-more": "Expand Details",
|
||||||
|
"show-less": "Show Less"
|
||||||
|
},
|
||||||
|
"clock": {},
|
||||||
|
"weather": {},
|
||||||
|
"weather-forecast": {},
|
||||||
|
"tfl-status": {
|
||||||
|
"good-service-all": "Good Service on all Lines",
|
||||||
|
"good-service-rest": "Good Service on all other Lines"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,170 @@
|
||||||
|
<template>
|
||||||
|
<div class="tfl-status">
|
||||||
|
<template v-if="lineStatuses">
|
||||||
|
<div v-for="line in filterLines" :key="line.index" class="line-row">
|
||||||
|
<p class="row name">{{ line.line }}</p>
|
||||||
|
<p :class="`row status ${getStatusColor(line.statusCode)}`">{{ line.status }}</p>
|
||||||
|
<p class="row disruption" v-if="line.disruption">{{ line.disruption | format }}</p>
|
||||||
|
</div>
|
||||||
|
<div v-if="!showAll" class="line-row">
|
||||||
|
<p class="row all-other">
|
||||||
|
{{
|
||||||
|
filterLines.length > 0 ?
|
||||||
|
$t('widgets.tfl-status.good-service-rest') :
|
||||||
|
$t('widgets.tfl-status.good-service-all')
|
||||||
|
}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<p class="more-details-btn" @click="toggleAllLines">
|
||||||
|
{{ showAll ? $t('widgets.general.show-less') : $t('widgets.general.show-more') }}
|
||||||
|
</p>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import axios from 'axios';
|
||||||
|
import WidgetMixin from '@/mixins/WidgetMixin';
|
||||||
|
import ErrorHandler from '@/utils/ErrorHandler';
|
||||||
|
import { widgetApiEndpoints } from '@/utils/defaults';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
mixins: [WidgetMixin],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
lineStatuses: null,
|
||||||
|
showAll: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/* Return only the lines without a good service, unless showing all */
|
||||||
|
filterLines() {
|
||||||
|
if (this.showAll) { return this.lineStatuses; }
|
||||||
|
return this.lineStatuses.filter((line) => line.statusCode !== 10);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
filters: {
|
||||||
|
format(description) {
|
||||||
|
const parts = description.split(':');
|
||||||
|
return parts.length > 1 ? parts[1] : parts[0];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/* Makes GET request to the TFL API */
|
||||||
|
fetchData() {
|
||||||
|
axios.get(widgetApiEndpoints.tflStatus)
|
||||||
|
.then((response) => {
|
||||||
|
this.lineStatuses = this.processData(response.data);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
ErrorHandler('Unable to fetch data from TFL API');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/* Processes the results to be rendered by the UI */
|
||||||
|
processData(data) {
|
||||||
|
let results = [];
|
||||||
|
data.forEach((line, index) => {
|
||||||
|
results.push({
|
||||||
|
index,
|
||||||
|
line: line.name,
|
||||||
|
statusCode: line.lineStatuses[0].statusSeverity,
|
||||||
|
status: line.lineStatuses[0].statusSeverityDescription,
|
||||||
|
disruption: line.lineStatuses[0].reason,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (!this.options.sortAlphabetically) {
|
||||||
|
results = this.sortByStatusCode(results);
|
||||||
|
}
|
||||||
|
if (this.options.linesToShow && Array.isArray(this.options.linesToShow)) {
|
||||||
|
results = this.filterByLineName(results, this.options.linesToShow);
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
},
|
||||||
|
/* Get color, depending on the status code */
|
||||||
|
getStatusColor(code) {
|
||||||
|
if (code <= 6) return 'red';
|
||||||
|
if (code <= 9) return 'orange';
|
||||||
|
return 'green';
|
||||||
|
},
|
||||||
|
/* If user only wants to see results from certain lines, filter the rest out */
|
||||||
|
filterByLineName(allLines, usersLines) {
|
||||||
|
const chosenLines = usersLines.map(name => name.toLowerCase());
|
||||||
|
const filtered = allLines.filter((line) => chosenLines.includes(line.line.toLowerCase()));
|
||||||
|
if (filtered.length < 1) {
|
||||||
|
ErrorHandler('No TFL lines match your filter');
|
||||||
|
return allLines;
|
||||||
|
}
|
||||||
|
return filtered;
|
||||||
|
},
|
||||||
|
/* Sort results in order of most-delayed first */
|
||||||
|
sortByStatusCode(lines) {
|
||||||
|
return lines.reverse().sort((a, b) => (a.statusCode > b.statusCode ? 1 : -1));
|
||||||
|
},
|
||||||
|
/* Toggle show/ hide all lines */
|
||||||
|
toggleAllLines() {
|
||||||
|
this.showAll = !this.showAll;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
if (this.options.showAll) this.showAll = true;
|
||||||
|
this.fetchData();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
|
||||||
|
.tfl-status {
|
||||||
|
.line-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
padding: 0.5rem 0.25rem;
|
||||||
|
.row {
|
||||||
|
margin: 0.2rem 0;
|
||||||
|
}
|
||||||
|
.status {
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: right;
|
||||||
|
&.green { color: var(--success); }
|
||||||
|
&.orange { color: var(--warning); }
|
||||||
|
&.red { color: var(--danger); }
|
||||||
|
}
|
||||||
|
.disruption {
|
||||||
|
opacity: var(--dimming-factor);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
grid-column-start: span 2;
|
||||||
|
}
|
||||||
|
.all-other {
|
||||||
|
grid-column-start: span 2;
|
||||||
|
font-weight: bold;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--success)
|
||||||
|
}
|
||||||
|
&:not(:last-child) {
|
||||||
|
border-bottom: 1px dashed var(--widget-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
color: var(--widget-text-color);
|
||||||
|
cursor: default;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
// Show more details button
|
||||||
|
.more-details-btn {
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.5rem 0.25rem 0.25rem;
|
||||||
|
padding: 0.1rem 0.25rem;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: var(--curve-factor);
|
||||||
|
&:hover {
|
||||||
|
border: 1px solid var(--widget-text-color);
|
||||||
|
}
|
||||||
|
&:focus, &:active {
|
||||||
|
background: var(--widget-text-color);
|
||||||
|
color: var(--widget-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
|
@ -13,6 +13,7 @@
|
||||||
<Clock v-if="widgetType === 'clock'" :options="widgetOptions" />
|
<Clock v-if="widgetType === 'clock'" :options="widgetOptions" />
|
||||||
<Weather v-else-if="widgetType === 'weather'" :options="widgetOptions" />
|
<Weather v-else-if="widgetType === 'weather'" :options="widgetOptions" />
|
||||||
<WeatherForecast v-else-if="widgetType === 'weather-forecast'" :options="widgetOptions" />
|
<WeatherForecast v-else-if="widgetType === 'weather-forecast'" :options="widgetOptions" />
|
||||||
|
<TflStatus v-else-if="widgetType === 'tfl-status'" :options="widgetOptions" />
|
||||||
</Collapsable>
|
</Collapsable>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
import Clock from '@/components/Widgets/Clock.vue';
|
import Clock from '@/components/Widgets/Clock.vue';
|
||||||
import Weather from '@/components/Widgets/Weather.vue';
|
import Weather from '@/components/Widgets/Weather.vue';
|
||||||
import WeatherForecast from '@/components/Widgets/WeatherForecast.vue';
|
import WeatherForecast from '@/components/Widgets/WeatherForecast.vue';
|
||||||
|
import TflStatus from '@/components/Widgets/TflStatus.vue';
|
||||||
import Collapsable from '@/components/LinkItems/Collapsable.vue';
|
import Collapsable from '@/components/LinkItems/Collapsable.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -30,6 +32,7 @@ export default {
|
||||||
Clock,
|
Clock,
|
||||||
Weather,
|
Weather,
|
||||||
WeatherForecast,
|
WeatherForecast,
|
||||||
|
TflStatus,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
widget: Object,
|
widget: Object,
|
||||||
|
|
|
@ -207,6 +207,7 @@ module.exports = {
|
||||||
widgetApiEndpoints: {
|
widgetApiEndpoints: {
|
||||||
weather: 'https://api.openweathermap.org/data/2.5/weather',
|
weather: 'https://api.openweathermap.org/data/2.5/weather',
|
||||||
weatherForecast: 'https://api.openweathermap.org/data/2.5/forecast/daily',
|
weatherForecast: 'https://api.openweathermap.org/data/2.5/forecast/daily',
|
||||||
|
tflStatus: 'https://api.tfl.gov.uk/line/mode/tube/status',
|
||||||
},
|
},
|
||||||
/* URLs for web search engines */
|
/* URLs for web search engines */
|
||||||
searchEngineUrls: {
|
searchEngineUrls: {
|
||||||
|
|
Loading…
Reference in New Issue