Adds upcoming public holidays widget

This commit is contained in:
Alicia Sykes 2021-12-17 16:52:12 +00:00
parent b7cd2d4c06
commit be5188ef7d
5 changed files with 184 additions and 19 deletions

View File

@ -12,12 +12,14 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
- [RSS Feed](#rss-feed) - [RSS Feed](#rss-feed)
- [XKCD Comics](#xkcd-comics) - [XKCD Comics](#xkcd-comics)
- [Code Stats](#code-stats) - [Code Stats](#code-stats)
- [Public Holidays](#public-holidays)
- [TFL Status](#tfl-status) - [TFL Status](#tfl-status)
- [Exchange Rates](#exchange-rates) - [Exchange Rates](#exchange-rates)
- [Stock Price History](#stock-price-history) - [Stock Price History](#stock-price-history)
- [Joke of the Day](#joke) - [Joke of the Day](#joke)
- [Flight Data](#flight-data) - [Flight Data](#flight-data)
- [Self-Hosted Services Widgets](#dynamic-widgets) - [Self-Hosted Services Widgets](#dynamic-widgets)
- [System Info](#system-info)
- [CPU History](#cpu-history-netdata) - [CPU History](#cpu-history-netdata)
- [Memory History](#memory-history-netdata) - [Memory History](#memory-history-netdata)
- [System Load History](#load-history-netdata) - [System Load History](#load-history-netdata)
@ -258,6 +260,32 @@ Display your coding summary. [Code::Stats](https://codestats.net/) is a free and
--- ---
### Public Holidays
Counting down to the next day off work? This widget displays upcoming public holidays for your country. Data is fetched from [Enrico](http://kayaposoft.com/enrico/)
<p align="center"><img width="400" src="https://i.ibb.co/VC6fZqn/public-holidays.png" /></p>
##### Options
**Field** | **Type** | **Required** | **Description**
--- | --- | --- | ---
**`country`** | `string` | Required | The region to fetch holiday data for, specified as a country code, e.g. `GB` or `US`
**`holidayType`** | `string` | __Optional__ | The type of holidays to fetch. Can be: `all`, `public_holiday`, `observance`, `school_holiday`, `other_day` or `extra_working_day`. Defaults to `public_holiday`
**`monthsToShow`** | `number` | __Optional__ | The number of months in advance to show. Min: `1`, max: `24`. Defaults to `12`
##### Example
```yaml
- type: public-holidays
options:
country: GB
holidayType: all
monthsToShow: 12
```
---
### TFL Status ### TFL Status
Shows real-time tube status of the London Underground. All options are optional. Shows real-time tube status of the London Underground. All options are optional.
@ -404,6 +432,25 @@ Displays airport departure and arrival flights, using data from [AeroDataBox](ht
## Self-Hosted Services Widgets ## Self-Hosted Services Widgets
### System Info
Displays info about the server which Dashy is hosted on. Includes user + host, operating system, uptime and basic memory & load data.
<p align="center"><img width="400" src="https://i.ibb.co/rvDPBDF/system-info.png" /></p>
##### Options
No config options.
##### Example
```yaml
- type: system-info
```
---
### CPU History (NetData) ### CPU History (NetData)
Pull recent CPU usage history from NetData. Pull recent CPU usage history from NetData.
@ -474,24 +521,6 @@ Pull recent load usage in 1, 5 and 15 minute intervals, from NetData.
--- ---
### System Info
Displays info about the server which Dashy is hosted on. Includes user + host, operating system, uptime and basic memory & load data.
<p align="center"><img width="400" src="https://i.ibb.co/rvDPBDF/system-info.png" /></p>
##### Options
No config options.
##### Example
```yaml
- type: system-info
```
---
## Dynamic Widgets ## Dynamic Widgets
### Iframe Widget ### Iframe Widget

View File

@ -0,0 +1,121 @@
<template>
<div class="public-holidays-wrapper">
<div
v-for="(holiday, indx) in holidays"
:key="indx"
v-tooltip="tooltip(holiday)"
class="holiday-row"
>
<p class="holiday-date">{{ holiday.date }}</p>
<p class="holiday-name">{{ holiday.name }}</p>
</div>
</div>
</template>
<script>
import axios from 'axios';
import WidgetMixin from '@/mixins/WidgetMixin';
import { widgetApiEndpoints } from '@/utils/defaults';
import { convertTimestampToDate, capitalize } from '@/utils/MiscHelpers';
export default {
mixins: [WidgetMixin],
components: {},
data() {
return {
holidays: [],
};
},
computed: {
country() {
if (this.options.country) return this.options.country;
return navigator.language.split('-')[1] || 'GB';
},
holidayType() {
const options = ['all', 'public_holiday', 'observance',
'school_holiday', 'other_day', 'extra_working_day'];
const usersChoice = this.options.holidayType;
if (usersChoice && options.includes(usersChoice)) return usersChoice;
return 'public_holiday';
},
monthsToShow() {
const usersChoice = this.options.monthsToShow;
if (usersChoice && !Number.isNaN(usersChoice) && usersChoice > 0 && usersChoice <= 24) {
return usersChoice;
}
return 12;
},
startDate() {
const now = new Date();
return `${now.getDate()}-${now.getMonth()}-${now.getFullYear()}`;
},
endDate() {
const now = new Date();
const then = new Date((now.setMonth(now.getMonth() + this.monthsToShow)));
return `${then.getDate()}-${then.getMonth()}-${then.getFullYear()}`;
},
endpoint() {
return `${widgetApiEndpoints.holidays}`
+ `&fromDate=${this.startDate}&toDate=${this.endDate}`
+ `&country=${this.country}&holidayType=${this.holidayType}`;
},
},
methods: {
/* Make GET request to CoinGecko API endpoint */
fetchData() {
axios.get(this.endpoint)
.then((response) => {
this.processData(response.data);
})
.catch((dataFetchError) => {
this.error('Unable to fetch holiday data', dataFetchError);
})
.finally(() => {
this.finishLoading();
});
},
/* Assign data variables to the returned data */
processData(holidays) {
const results = [];
const makeDate = (date) => convertTimestampToDate(
new Date(`${date.year}-${date.month}-${date.day}`).getTime(),
);
const formatType = (ht) => capitalize(ht.replaceAll('_', ' '));
holidays.forEach((holiday) => {
results.push({
name: holiday.name[0].text,
date: makeDate(holiday.date),
type: formatType(holiday.holidayType),
observed: holiday.observedOn ? makeDate(holiday.observedOn) : '',
});
});
this.holidays = results;
},
tooltip(holiday) {
const observed = holiday.observed ? `<br><b>Observed On</b>: ${holiday.observed}` : '';
const content = `<b>Type</b>: ${holiday.type}${observed}`;
return {
content, trigger: 'hover focus', html: true, delay: 250, classes: 'in-modal-tt',
};
},
},
};
</script>
<style scoped lang="scss">
.public-holidays-wrapper {
padding: 0.5rem 0;
.holiday-row {
display: flex;
justify-content: space-between;
p {
margin: 0.25rem 0;
color: var(--widget-text-color);
}
&:not(:last-child) {
border-bottom: 1px dashed var(--widget-text-color);
}
}
}
</style>

View File

@ -102,6 +102,13 @@
@error="handleError" @error="handleError"
:ref="widgetRef" :ref="widgetRef"
/> />
<PublicHolidays
v-else-if="widgetType === 'public-holidays'"
:options="widgetOptions"
@loading="setLoaderState"
@error="handleError"
:ref="widgetRef"
/>
<RssFeed <RssFeed
v-else-if="widgetType === 'rss-feed'" v-else-if="widgetType === 'rss-feed'"
:options="widgetOptions" :options="widgetOptions"
@ -178,6 +185,7 @@ import Jokes from '@/components/Widgets/Jokes.vue';
import NdCpuHistory from '@/components/Widgets/NdCpuHistory.vue'; import NdCpuHistory from '@/components/Widgets/NdCpuHistory.vue';
import NdLoadHistory from '@/components/Widgets/NdLoadHistory.vue'; import NdLoadHistory from '@/components/Widgets/NdLoadHistory.vue';
import NdRamHistory from '@/components/Widgets/NdRamHistory.vue'; import NdRamHistory from '@/components/Widgets/NdRamHistory.vue';
import PublicHolidays from '@/components/Widgets/PublicHolidays.vue';
import RssFeed from '@/components/Widgets/RssFeed.vue'; import RssFeed from '@/components/Widgets/RssFeed.vue';
import StockPriceChart from '@/components/Widgets/StockPriceChart.vue'; import StockPriceChart from '@/components/Widgets/StockPriceChart.vue';
import SystemInfo from '@/components/Widgets/SystemInfo.vue'; import SystemInfo from '@/components/Widgets/SystemInfo.vue';
@ -207,6 +215,7 @@ export default {
NdCpuHistory, NdCpuHistory,
NdLoadHistory, NdLoadHistory,
NdRamHistory, NdRamHistory,
PublicHolidays,
RssFeed, RssFeed,
StockPriceChart, StockPriceChart,
SystemInfo, SystemInfo,

View File

@ -1,3 +1,4 @@
/* eslint-disable arrow-body-style */
import { hideFurnitureOn } from '@/utils/defaults'; import { hideFurnitureOn } from '@/utils/defaults';
/* Returns false if page furniture should be hidden on said route */ /* Returns false if page furniture should be hidden on said route */
@ -50,7 +51,7 @@ export const applyItemId = (inputSections) => {
export const convertTimestampToDate = (timestamp) => { export const convertTimestampToDate = (timestamp) => {
const localFormat = navigator.language; const localFormat = navigator.language;
const dateFormat = { const dateFormat = {
weekday: 'short', day: 'numeric', month: 'short', year: 'numeric', weekday: 'short', day: 'numeric', month: 'short', year: '2-digit',
}; };
const date = new Date(timestamp).toLocaleDateString(localFormat, dateFormat); const date = new Date(timestamp).toLocaleDateString(localFormat, dateFormat);
return `${date}`; return `${date}`;
@ -91,3 +92,7 @@ export const showNumAsThousand = (bigNum) => {
if (bigNum < 1000) return bigNum; if (bigNum < 1000) return bigNum;
return `${Math.round(bigNum / 1000)}k`; return `${Math.round(bigNum / 1000)}k`;
}; };
export const capitalize = (str) => {
return str.replace(/\w\S*/g, (w) => (w.replace(/^\w/, (c) => c.toUpperCase())));
};

View File

@ -218,6 +218,7 @@ module.exports = {
flights: 'https://aerodatabox.p.rapidapi.com/flights/airports/icao/', flights: 'https://aerodatabox.p.rapidapi.com/flights/airports/icao/',
rssToJson: 'https://api.rss2json.com/v1/api.json', rssToJson: 'https://api.rss2json.com/v1/api.json',
codeStats: 'https://codestats.net/', codeStats: 'https://codestats.net/',
holidays: 'https://kayaposoft.com/enrico/json/v2.0/?action=getHolidaysForDateRange',
}, },
/* URLs for web search engines */ /* URLs for web search engines */
searchEngineUrls: { searchEngineUrls: {