mirror of https://github.com/lissy93/dashy
245 lines
7.2 KiB
Vue
245 lines
7.2 KiB
Vue
<template>
|
|
<div class="code-stats-wrapper">
|
|
<!-- User Info -->
|
|
<div class="user-meta" v-if="basicInfo && !hideMeta">
|
|
<div class="user-info-wrap">
|
|
<p class="username">{{ basicInfo.username }}</p>
|
|
<p class="user-level">{{ basicInfo.level }}</p>
|
|
</div>
|
|
<div class="total-xp-wrap">
|
|
<p class="total-xp">{{ basicInfo.totalXp | formatTotalXp }}</p>
|
|
<p class="new-xp">{{ basicInfo.newXp | formatNewXp }}</p>
|
|
</div>
|
|
</div>
|
|
<!-- XP History Heatmap -->
|
|
<div :id="`xp-history-${chartId}`" class="xp-heat-chart"></div>
|
|
<!-- Language Breakdown -->
|
|
<div :id="`languages-${chartId}`" class="language-pie-chart"></div>
|
|
<!-- Machines Percentage -->
|
|
<div :id="`machines-${chartId}`" class="machine-percentage-chart"></div>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import axios from 'axios';
|
|
import WidgetMixin from '@/mixins/WidgetMixin';
|
|
import ChartingMixin from '@/mixins/ChartingMixin';
|
|
import { widgetApiEndpoints } from '@/utils/defaults';
|
|
import { putCommasInBigNum, showNumAsThousand } from '@/utils/MiscHelpers';
|
|
|
|
export default {
|
|
mixins: [WidgetMixin, ChartingMixin],
|
|
data() {
|
|
return {
|
|
basicInfo: null,
|
|
};
|
|
},
|
|
computed: {
|
|
/* The username to fetch data from - REQUIRED */
|
|
username() {
|
|
if (!this.options.username) this.error('You must specify a username');
|
|
return this.parseAsEnvVar(this.options.username);
|
|
},
|
|
/* Optionally override hostname, if using a self-hosted instance */
|
|
hostname() {
|
|
if (this.options.hostname) return this.options.hostname;
|
|
return this.parseAsEnvVar(widgetApiEndpoints.codeStats);
|
|
},
|
|
hideMeta() {
|
|
return this.options.hideMeta || false;
|
|
},
|
|
hideHistory() {
|
|
return this.options.hideHistory || false;
|
|
},
|
|
hideLanguages() {
|
|
return this.options.hideLanguages || false;
|
|
},
|
|
hideMachines() {
|
|
return this.options.hideMachines || false;
|
|
},
|
|
monthsToShow() {
|
|
return this.options.monthsToShow || 5;
|
|
},
|
|
endpoint() {
|
|
return `${this.hostname}/api/users/${this.username}`;
|
|
},
|
|
chartStartDate() {
|
|
const now = new Date();
|
|
return new Date((now.setMonth(now.getMonth() - this.monthsToShow)));
|
|
},
|
|
},
|
|
filters: {
|
|
formatTotalXp(bigNum) {
|
|
return showNumAsThousand(bigNum);
|
|
},
|
|
formatNewXp(newXp) {
|
|
return `+${putCommasInBigNum(newXp)} XP`;
|
|
},
|
|
},
|
|
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 data from CodeStats.net', dataFetchError);
|
|
})
|
|
.finally(() => {
|
|
this.finishLoading();
|
|
});
|
|
},
|
|
/* Assign data variables to the returned data */
|
|
processData(data) {
|
|
// Make basic info data
|
|
if (!this.hideMeta) {
|
|
this.basicInfo = {
|
|
username: data.user,
|
|
level: this.makeLevel(data.total_xp),
|
|
totalXp: data.total_xp,
|
|
newXp: data.new_xp,
|
|
};
|
|
}
|
|
// Make language breakdown pie chart data
|
|
if (!this.hideLanguages) {
|
|
const langLabels = [];
|
|
const langXpValues = [];
|
|
Object.keys(data.languages).forEach((lang) => {
|
|
langLabels.push(lang);
|
|
langXpValues.push(data.languages[lang].xps);
|
|
});
|
|
const languagesPieData = {
|
|
labels: langLabels,
|
|
datasets: [{ values: langXpValues }],
|
|
};
|
|
this.drawLanguagePieChart(languagesPieData);
|
|
}
|
|
// Make day-by-day historical XP heat chart data
|
|
if (!this.hideHistory) {
|
|
const xpHistoryChartData = {};
|
|
Object.keys(data.dates).forEach((date) => {
|
|
const timestamp = Math.round(new Date(date).getTime() / 1000);
|
|
xpHistoryChartData[timestamp] = data.dates[date];
|
|
});
|
|
this.drawXpHistoryChart(xpHistoryChartData);
|
|
}
|
|
// Make machine proportion percentage chart data
|
|
if (!this.hideMachines) {
|
|
const machinesLabels = [];
|
|
const machinesXpValues = [];
|
|
Object.keys(data.machines).forEach((machine) => {
|
|
machinesLabels.push(machine);
|
|
machinesXpValues.push(data.machines[machine].xps);
|
|
});
|
|
const machinesPercentageData = {
|
|
labels: machinesLabels,
|
|
datasets: [{ values: machinesXpValues }],
|
|
};
|
|
this.drawMachinesPercentageChart(machinesPercentageData);
|
|
}
|
|
},
|
|
drawLanguagePieChart(languagesPieData) {
|
|
return new this.Chart(`#languages-${this.chartId}`, {
|
|
title: 'Languages',
|
|
type: 'donut',
|
|
data: languagesPieData,
|
|
height: 250,
|
|
strokeWidth: 15,
|
|
tooltipOptions: {
|
|
formatTooltipY: d => showNumAsThousand(d),
|
|
},
|
|
});
|
|
},
|
|
drawXpHistoryChart(xpHistoryData) {
|
|
return new this.Chart(`#xp-history-${this.chartId}`, {
|
|
title: 'Historical XP',
|
|
type: 'heatmap',
|
|
data: {
|
|
dataPoints: xpHistoryData,
|
|
start: this.chartStartDate,
|
|
end: new Date(),
|
|
},
|
|
discreteDomains: 0,
|
|
radius: 2,
|
|
colors: ['#caf0f8', '#48cae4', '#0077b6', '#023e8a', '#090a79'],
|
|
});
|
|
},
|
|
drawMachinesPercentageChart(machineChartData) {
|
|
return new this.Chart(`#machines-${this.chartId}`, {
|
|
title: 'Machines',
|
|
type: 'percentage',
|
|
data: machineChartData,
|
|
height: 180,
|
|
strokeWidth: 15,
|
|
tooltipOptions: {
|
|
formatTooltipY: d => showNumAsThousand(d),
|
|
},
|
|
colors: ['#f9c80e', '#43bccd', '#ea3546', '#662e9b', '#f86624'],
|
|
});
|
|
},
|
|
/* Given a users XP score, return text level */
|
|
makeLevel(xp) {
|
|
if (xp < 100) return 'New Joiner';
|
|
if (xp < 1000) return 'Noob';
|
|
if (xp < 10000) return 'Intermediate';
|
|
if (xp < 50000) return 'Code ninja in the making';
|
|
if (xp < 100000) return 'Expert Developer';
|
|
if (xp < 500000) return 'Ultra Expert Developer';
|
|
if (xp < 1000000) return 'Code Super Hero';
|
|
if (xp < 1500000) return 'Super Epic Code Hero';
|
|
if (xp >= 15000000) return 'God Level';
|
|
return xp;
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.code-stats-wrapper {
|
|
p {
|
|
margin: 0;
|
|
font-size: 1rem;
|
|
color: var(--widget-text-color);
|
|
}
|
|
.user-meta {
|
|
display: flex;
|
|
margin: 0.5rem;
|
|
padding: 0.5rem 0;
|
|
border-bottom: 1px dashed var(--widget-text-color);
|
|
justify-content: space-between;
|
|
.user-info-wrap {
|
|
.username {
|
|
font-size: 1.4rem;
|
|
text-transform: capitalize;
|
|
}
|
|
.user-level {
|
|
font-size: 0.8rem;
|
|
text-transform: capitalize;
|
|
opacity: var(--dimming-factor);
|
|
color: var(--widget-text-color);
|
|
}
|
|
}
|
|
.total-xp-wrap {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
.total-xp {
|
|
font-size: 1.4rem;
|
|
font-family: var(--font-monospace);
|
|
}
|
|
.new-xp {
|
|
font-size: 0.8rem;
|
|
margin: 0 0 0 0.5rem;
|
|
color: var(--success);
|
|
font-family: var(--font-monospace);
|
|
}
|
|
}
|
|
}
|
|
.xp-heat-chart,
|
|
.language-pie-chart,
|
|
.machine-percentage-chart {
|
|
&:not(:last-child) { border-bottom: 1px dashed var(--widget-text-color); }
|
|
}
|
|
}
|
|
</style>
|