Add checkbox for monitoring when creating node.

This commit is contained in:
baldo 2022-08-26 19:08:31 +02:00
parent f80657e4e0
commit ac6fb7b57a
7 changed files with 338 additions and 58 deletions

View file

@ -0,0 +1,95 @@
<script setup lang="ts">
import { onMounted, ref } from "vue";
import type { ComponentVariant } from "@/types";
interface Props {
variant: ComponentVariant;
icon: string;
scrollIntoView: boolean;
}
const props = defineProps<Props>();
const element = ref<HTMLElement>();
function scrollIntoView() {
element.value?.scrollIntoView({
behavior: "smooth",
block: "nearest",
});
}
if (props.scrollIntoView) {
onMounted(scrollIntoView);
}
</script>
<template>
<div ref="element" :class="['card', props.variant]">
<i :class="['icon', 'fa', `fa-${props.icon}`]" aria-hidden="true" />
<slot />
</div>
</template>
<style lang="scss">
@import "../scss/variables";
.card {
position: relative;
clear: both;
min-height: $base-card-min-height;
margin: $base-card-margin;
padding: $base-card-padding;
border: {
width: $base-card-border-width;
style: $base-card-border-style;
radius: $base-card-border-radius;
}
font-weight: $base-card-font-weight;
*:nth-child(2) {
margin-top: 0;
}
*:last-child {
margin-bottom: 0;
}
.icon {
float: left;
margin: $base-card-icon-margin;
font-size: $base-card-icon-size;
}
a:focus {
outline: {
width: $base-card-link-focus-outline-width;
style: $base-card-link-focus-outline-style;
}
}
}
@each $variant, $color in $variant-colors {
.card.#{$variant} {
border-color: $color;
background-color: darken($color, 30%);
color: lighten($color, 30%);
a {
color: darken($color, 30%);
&:hover {
color: darken($color, 30%);
}
&:focus {
outline-color: darken($color, 30%);
}
}
}
}
</style>

View file

@ -1,49 +1,16 @@
<script setup lang="ts">
import { onMounted, ref } from "vue";
const element = ref<HTMLElement>();
function scrollIntoView() {
element.value?.scrollIntoView({
behavior: "smooth",
block: "nearest",
});
}
onMounted(scrollIntoView);
import BaseCard from "@/components/BaseCard.vue";
import { ComponentVariant } from "@/types";
</script>
<template>
<div ref="element" class="error-card">
<slot></slot>
</div>
<BaseCard
:variant="ComponentVariant.DANGER"
icon="warning"
:scrollIntoView="true"
>
<slot />
</BaseCard>
</template>
<style lang="scss">
@import "../scss/variables";
.error-card {
margin: $error-card-margin;
padding: $error-card-padding;
border: $error-card-border;
border-radius: $error-card-border-radius;
font-weight: $error-card-font-weight;
background-color: $error-card-background-color;
color: $error-card-color;
a {
color: $error-card-link-color;
&:hover {
color: $error-card-link-hover-color;
}
&:focus {
outline: $error-card-link-focus-outline;
}
}
}
</style>
<style lang="scss"></style>

View file

@ -0,0 +1,16 @@
<script setup lang="ts">
import BaseCard from "@/components/BaseCard.vue";
import { ComponentVariant } from "@/types";
</script>
<template>
<BaseCard
:variant="ComponentVariant.INFO"
icon="info-circle"
:scrollIntoView="false"
>
<slot />
</BaseCard>
</template>
<style lang="scss"></style>

View file

@ -0,0 +1,135 @@
<script setup lang="ts">
import { ref } from "vue";
import ExpandableHelpBox from "@/components/ExpandableHelpBox.vue";
interface Props {
name: string;
modelValue?: boolean;
label: string;
help?: string;
}
const props = defineProps<Props>();
const emit = defineEmits<{
(e: "update:modelValue", value: boolean): void;
}>();
const input = ref<HTMLInputElement>();
const focussed = ref(false);
function onChange() {
const element = input.value;
if (!element) {
console.warn("Could not get referenced input element.");
return;
}
emit("update:modelValue", element.checked);
}
function onFocus() {
focussed.value = true;
}
function onBlur() {
focussed.value = false;
}
</script>
<template>
<div
:class="{
'checkbox-input': true,
checked: modelValue,
focussed,
}"
>
<label>
<input
ref="input"
type="checkbox"
:name="props.name"
:checked="props.modelValue"
@change="onChange"
@focus="onFocus"
@blur="onBlur"
/>
{{ props.label }}
<ExpandableHelpBox v-if="props.help" :text="props.help" />
</label>
</div>
</template>
<style scoped lang="scss">
@import "../../scss/variables";
.checkbox-input {
margin: $validation-form-input-margin;
}
input {
// hide the default checkbox and then rebuild a new one via label::before
z-index: -1;
position: absolute;
margin: 0;
padding: 0;
width: 0;
height: 0;
opacity: 0;
border: none;
box-shadow: none;
}
label {
position: relative;
clear: both;
display: block;
width: 100%;
font-weight: $label-font-weight;
cursor: pointer;
&::before {
float: left;
display: block;
vertical-align: top;
content: "";
margin: $checkbox-margin;
width: $checkbox-size;
height: $checkbox-size;
background-color: $input-background-color;
border-radius: $checkbox-border-radius;
cursor: pointer;
}
.checked &::before {
// TODO: Fallback if font does not load?
content: "\f00c";
//noinspection CssNoGenericFontName
font-family: ForkAwesome;
text-align: center;
line-height: $checkbox-size;
font-weight: bold;
color: $input-text-color;
}
.focussed &::before {
outline: $input-outline;
outline-offset: $input-outline-offset;
}
}
</style>

View file

@ -3,11 +3,13 @@ import { computed, getCurrentInstance, onMounted, ref } from "vue";
import { type Constraint, forConstraint } from "@/shared/validation/validator";
import ExpandableHelpBox from "@/components/ExpandableHelpBox.vue";
type InputType = "text" | "number" | "password" | "email" | "tel" | "url";
interface Props {
name: string;
modelValue?: string;
label?: string;
type?: string;
type?: InputType;
placeholder: string;
constraint: Constraint;
validationError: string;

View file

@ -27,6 +27,8 @@ import { route, RouteName } from "@/router";
import RouteButton from "@/components/form/RouteButton.vue";
import { ApiError } from "@/utils/Api";
import NodeCoordinatesInput from "@/components/nodes/NodeCoordinatesInput.vue";
import CheckboxInput from "@/components/form/CheckboxInput.vue";
import InfoCard from "@/components/InfoCard.vue";
const configStore = useConfigStore();
const nodeStore = useNodeStore();
@ -211,8 +213,57 @@ async function onSubmit() {
/>
</fieldset>
<fieldset>
<h3>TODO: Monitoring</h3>
<fieldset v-if="configStore.getConfig.monitoring.enabled">
<h3>Möchtest Du automatisiert Status-E-Mails bekommen?</h3>
<i class="monitoring-icon fa fa-heartbeat" aria-hidden="true" />
<p class="help-block">
Du kannst Dich automatisiert benachrichtigen lassen, sobald
Dein Knoten längere Zeit offline ist. Die erste E-Mail
bekommst Du nach 3 Stunden, nach einem Tag gibt es dann
nochmal eine Erinnerung. Sollte Dein Knoten nach einer Woche
immernoch offline sein, bekommst Du eine letzte
Status-E-Mail.
</p>
<p class="help-block">
Du kannst den automatisierten Versand von Status-E-Mails
hier selbstverständlich jederzeit wieder deaktivieren.
</p>
<CheckboxInput
v-model="monitoringModel"
name="monitoring"
label="Informiert mich, wenn mein Knoten offline ist"
/>
<InfoCard v-if="monitoringModel">
<p>
Zur Bestätigung Deiner E-Mail-Adresse schicken wir Dir
nach dem Speichern Deiner Knotendaten eine E-Mail mit
einem Bestätigungs-Link. Erst nach der Bestätigung
deiner E-Mail-Adresse wirst Du informiert, falls Dein
Knoten längere Zeit offline ist.
</p>
<p>
Die Inbetriebnahme Deines Knotens kannst Du
selbstverständlich unabhängig von der Bestätigung immer
sofort duchführen.
</p>
<p v-if="emailModel">
<strong>
<i
class="fa fa-envelope-o"
aria-hidden="true"
title="E-Mail-Adresse"
/>
{{ emailModel }}
</strong>
</p>
</InfoCard>
</fieldset>
<h1>TODO: Check community bounds</h1>
@ -243,4 +294,13 @@ async function onSubmit() {
</ValidationForm>
</template>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
@import "../../scss/variables";
.monitoring-icon {
float: left;
margin: 0 0.25em 0.125em 0;
font-size: 3em;
color: $variant-color-primary;
}
</style>

View file

@ -99,6 +99,10 @@ $input-border-radius: 0.5em;
$input-outline: 0.1em solid $variant-color-info;
$input-outline-offset: 0.125em;
$checkbox-size: 1.25em;
$checkbox-margin: 0 0.5em 0 0;
$checkbox-border-radius: 0.25em;
$button-margin: 0.3em;
$button-padding: 0.25em 0.5em;
$button-border-radius: $input-border-radius;
@ -115,17 +119,18 @@ $button-sizes: (
$help-icon-color: $variant-color-info;
$help-text-margin: 0.5em 0 0.75em 0;
// Error cards
$error-card-margin: 1em 0;
$error-card-padding: 0.5em;
$error-card-border: 0.1em solid $variant-color-danger;
$error-card-border-radius: 0.5em;
$error-card-font-weight: 600;
$error-card-background-color: lighten($variant-color-danger, 10%);
$error-card-color: $page-background-color;
$error-card-link-color: darken($variant-color-danger, 30%);
$error-card-link-hover-color: $error-card-link-color;
$error-card-link-focus-outline: 0.1em solid $error-card-link-hover-color;
// Cards
$base-card-margin: 1em 0;
$base-card-padding: 1em 0.5em;
$base-card-border-width: 0.1em;
$base-card-border-style: solid;
$base-card-border-radius: 0.5em;
$base-card-font-weight: normal;
$base-card-link-focus-outline-width: 0.1em;
$base-card-link-focus-outline-style: solid;
$base-card-icon-margin: 0 0.25em 0.125em 0;
$base-card-icon-size: 2em;
$base-card-min-height: $base-card-icon-size;
// Node map
$node-map-aspect-ratio: math.div(16, 10);