Statistics styling
This commit is contained in:
parent
6c47c5b178
commit
3ce2ba5013
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {RouterLink, RouterView} from "vue-router";
|
import {RouterLink, RouterView} from "vue-router";
|
||||||
import PageHeader from "@/components/PageHeader.vue";
|
import PageHeader from "@/components/page/PageHeader.vue";
|
||||||
import PageFooter from "@/components/PageFooter.vue";
|
import PageFooter from "@/components/page/PageFooter.vue";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
113
frontend/src/components/admin/StatisticsCard.vue
Normal file
113
frontend/src/components/admin/StatisticsCard.vue
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import {computed, defineProps} from "vue";
|
||||||
|
|
||||||
|
const {
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
variant,
|
||||||
|
value,
|
||||||
|
link,
|
||||||
|
filter,
|
||||||
|
} = defineProps({
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
icon: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
variant: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
type: Object,
|
||||||
|
required: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const linkTarget = computed(() => {
|
||||||
|
if (filter) {
|
||||||
|
const json = JSON.stringify(filter);
|
||||||
|
return `${link}?search=${encodeURIComponent(json)}`;
|
||||||
|
} else {
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<RouterLink :to="linkTarget" :class="['statistics-card', 'statistics-card-' + variant]">
|
||||||
|
<i :class="['fa', 'fa-' + icon]" aria-hidden="true" />
|
||||||
|
<dl>
|
||||||
|
<dt>{{ title }}</dt>
|
||||||
|
<dd>{{ value }}</dd>
|
||||||
|
</dl>
|
||||||
|
</RouterLink>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "../../scss/variables";
|
||||||
|
|
||||||
|
.statistics-card {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
min-height: $statistics-card-height;
|
||||||
|
|
||||||
|
margin: $statistics-card-margin;
|
||||||
|
padding: $statistics-card-padding;
|
||||||
|
|
||||||
|
border-radius: $statistics-card-border-radius;
|
||||||
|
|
||||||
|
@each $variant, $color in $variant-colors {
|
||||||
|
&.statistics-card-#{$variant} {
|
||||||
|
background-color: $color;
|
||||||
|
color: map-get($variant-text-colors, $variant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
display: inline-block;
|
||||||
|
margin-right: $statistics-card-icon-gap;
|
||||||
|
font-size: $statistics-card-icon-size;
|
||||||
|
width: 1em;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
dl {
|
||||||
|
position: relative;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
text-align: right;
|
||||||
|
|
||||||
|
dt {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
dd {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: $statistics-card-value-font-size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -35,7 +35,7 @@ refresh();
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@use "sass:math";
|
@use "sass:math";
|
||||||
@import "../scss/variables";
|
@import "../../scss/variables";
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
position: absolute;
|
position: absolute;
|
|
@ -36,7 +36,7 @@ refresh();
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import "../scss/variables";
|
@import "../../scss/variables";
|
||||||
|
|
||||||
header {
|
header {
|
||||||
background-color: $nav-bar-background-color;
|
background-color: $nav-bar-background-color;
|
|
@ -1,17 +1,34 @@
|
||||||
// Grays
|
// Grays
|
||||||
|
$black: #000000;
|
||||||
$gray-darkest: #222222;
|
$gray-darkest: #222222;
|
||||||
$gray-darker: #333333;
|
$gray-darker: #333333;
|
||||||
$gray-dark: #444444;
|
$gray-dark: #444444;
|
||||||
$gray: #666666;
|
$gray: #666666;
|
||||||
$gray-light: #d6d6d6;
|
$gray-light: #d6d6d6;
|
||||||
$gray-lighter: #ededed;
|
$gray-lighter: #ededed;
|
||||||
|
$white: #ffffff;
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
$color-primary: #e5287a;
|
$variant-colors: (
|
||||||
$color-success: #449d44;
|
primary: #e5287a,
|
||||||
$color-warning: #fdbc41;
|
success: #4ba74b,
|
||||||
$color-danger: #c9302c;
|
warning: #fdbc41,
|
||||||
$color-info: #009ee0;
|
danger: #ef5652,
|
||||||
|
info: #009ee0,
|
||||||
|
);
|
||||||
|
$variant-text-colors: (
|
||||||
|
// primary: do not use, contrast too low
|
||||||
|
success: $gray-darkest,
|
||||||
|
warning: $gray-darkest,
|
||||||
|
danger: $gray-darkest,
|
||||||
|
info: $gray-darkest,
|
||||||
|
);
|
||||||
|
|
||||||
|
$variant-color-primary: map-get($variant-colors, primary);
|
||||||
|
$variant-color-success: map-get($variant-colors, success);
|
||||||
|
$variant-color-warning: map-get($variant-colors, warning);
|
||||||
|
$variant-color-danger: map-get($variant-colors, danger);
|
||||||
|
$variant-color-info: map-get($variant-colors, info);
|
||||||
|
|
||||||
// Page
|
// Page
|
||||||
$page-background-color: $gray-darkest;
|
$page-background-color: $gray-darkest;
|
||||||
|
@ -19,7 +36,7 @@ $page-text-color: $gray-lighter;
|
||||||
$page-padding: 0.5em;
|
$page-padding: 0.5em;
|
||||||
|
|
||||||
// Links
|
// Links
|
||||||
$link-color: $color-warning;
|
$link-color: $variant-color-warning;
|
||||||
|
|
||||||
// Navigation
|
// Navigation
|
||||||
$nav-bar-background-color: $gray-darker;
|
$nav-bar-background-color: $gray-darker;
|
||||||
|
@ -31,3 +48,12 @@ $nav-header-logo-size: 2em;
|
||||||
$nav-header-logo-margin: 0 0.5em 0 0;
|
$nav-header-logo-margin: 0 0.5em 0 0;
|
||||||
|
|
||||||
$nav-footer-padding: 0.75em;
|
$nav-footer-padding: 0.75em;
|
||||||
|
|
||||||
|
// Statistics
|
||||||
|
$statistics-card-height: 4.5em;
|
||||||
|
$statistics-card-margin: 0.5em;
|
||||||
|
$statistics-card-padding: 0.3em 0.5em;
|
||||||
|
$statistics-card-border-radius: 0.5em;
|
||||||
|
$statistics-card-icon-size: 4em;
|
||||||
|
$statistics-card-icon-gap: 0.15em;
|
||||||
|
$statistics-card-value-font-size: 2.5em;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import StatisticsCard from "@/components/admin/StatisticsCard.vue";
|
||||||
import { useStatisticsStore } from "@/stores/statistics";
|
import { useStatisticsStore } from "@/stores/statistics";
|
||||||
|
|
||||||
const statistics = useStatisticsStore();
|
const statistics = useStatisticsStore();
|
||||||
|
@ -13,18 +14,57 @@ refresh();
|
||||||
<template>
|
<template>
|
||||||
<main>
|
<main>
|
||||||
<div v-if="statistics.getStatistics">
|
<div v-if="statistics.getStatistics">
|
||||||
<h1>Nodes</h1>
|
<h2>Knotenstatistik</h2>
|
||||||
|
|
||||||
<div>
|
<div class="statistics">
|
||||||
Registered: {{ statistics.getStatistics.nodes.registered }}<br />
|
<StatisticsCard
|
||||||
With VPN-key: {{ statistics.getStatistics.nodes.withVPN }}<br />
|
title="Registrierte Knoten"
|
||||||
With coordinates: {{ statistics.getStatistics.nodes.withCoords }}<br />
|
icon="circle-o"
|
||||||
Monitoring active: {{ statistics.getStatistics.nodes.monitoring.active }}<br />
|
variant="info"
|
||||||
Monitoring pending: {{ statistics.getStatistics.nodes.monitoring.pending }}
|
:value="statistics.getStatistics.nodes.registered"
|
||||||
|
link="/admin/nodes"
|
||||||
|
/>
|
||||||
|
<StatisticsCard
|
||||||
|
title="Mit hinterlegtem fastd-Key"
|
||||||
|
icon="lock"
|
||||||
|
variant="warning"
|
||||||
|
:value="statistics.getStatistics.nodes.withVPN"
|
||||||
|
link="/admin/nodes"
|
||||||
|
:filter="{hasKey: true}"
|
||||||
|
/>
|
||||||
|
<StatisticsCard
|
||||||
|
title="Mit Koordinaten"
|
||||||
|
icon="map-marker"
|
||||||
|
variant="success"
|
||||||
|
:value="statistics.getStatistics.nodes.withCoords"
|
||||||
|
link="/admin/nodes"
|
||||||
|
:filter="{hasCoords: true}"
|
||||||
|
/>
|
||||||
|
<StatisticsCard
|
||||||
|
title="Monitoring aktiv"
|
||||||
|
icon="heartbeat"
|
||||||
|
variant="success"
|
||||||
|
:value="statistics.getStatistics.nodes.monitoring.active"
|
||||||
|
link="/admin/nodes"
|
||||||
|
:filter="{monitoringState: 'active'}"
|
||||||
|
/>
|
||||||
|
<StatisticsCard
|
||||||
|
title="Monitoring noch nicht bestätigt"
|
||||||
|
icon="envelope"
|
||||||
|
variant="danger"
|
||||||
|
:value="statistics.getStatistics.nodes.monitoring.pending"
|
||||||
|
link="/admin/nodes"
|
||||||
|
:filter="{monitoringState: 'pending'}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<button @click="refresh()">Refresh</button>
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
.statistics {
|
||||||
|
display: grid;
|
||||||
|
// TODO: Responsive sizes
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(25%, 100%));
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue