Monitor Conditions (#5048)

This commit is contained in:
Shaun
2024-08-30 15:48:13 -04:00
committed by GitHub
parent 032ac161f7
commit 36f8be040d
21 changed files with 1526 additions and 35 deletions

View File

@@ -0,0 +1,152 @@
<template>
<div class="monitor-condition mb-3" data-testid="condition">
<button
v-if="!isInGroup || !isFirst || !isLast"
class="btn btn-outline-danger remove-button"
type="button"
:aria-label="$t('conditionDelete')"
data-testid="remove-condition"
@click="remove"
>
<font-awesome-icon icon="trash" />
</button>
<select v-if="!isFirst" v-model="model.andOr" class="form-select and-or-select" data-testid="condition-and-or">
<option value="and">{{ $t("and") }}</option>
<option value="or">{{ $t("or") }}</option>
</select>
<select v-model="model.variable" class="form-select" data-testid="condition-variable">
<option
v-for="variable in conditionVariables"
:key="variable.id"
:value="variable.id"
>
{{ $t(variable.id) }}
</option>
</select>
<select v-model="model.operator" class="form-select" data-testid="condition-operator">
<option
v-for="operator in getVariableOperators(model.variable)"
:key="operator.id"
:value="operator.id"
>
{{ $t(operator.caption) }}
</option>
</select>
<input
v-model="model.value"
type="text"
class="form-control"
:aria-label="$t('conditionValuePlaceholder')"
data-testid="condition-value"
required
/>
</div>
</template>
<script>
export default {
name: "EditMonitorCondition",
props: {
/**
* The monitor condition
*/
modelValue: {
type: Object,
required: true,
},
/**
* Whether this is the first condition
*/
isFirst: {
type: Boolean,
required: true,
},
/**
* Whether this is the last condition
*/
isLast: {
type: Boolean,
required: true,
},
/**
* Whether this condition is in a group
*/
isInGroup: {
type: Boolean,
required: false,
default: false,
},
/**
* Variable choices
*/
conditionVariables: {
type: Array,
required: true,
},
},
emits: [ "update:modelValue", "remove" ],
computed: {
model: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
}
}
},
methods: {
remove() {
this.$emit("remove", this.model);
},
getVariableOperators(variableId) {
return this.conditionVariables.find(v => v.id === variableId)?.operators ?? [];
},
},
};
</script>
<style lang="scss" scoped>
@import "../assets/vars.scss";
.monitor-condition {
display: flex;
flex-wrap: wrap;
}
.remove-button {
justify-self: flex-end;
margin-bottom: 12px;
margin-left: auto;
}
@container (min-width: 500px) {
.monitor-condition {
display: flex;
flex-wrap: nowrap;
}
.remove-button {
margin-bottom: 0;
margin-left: 10px;
order: 100;
}
.and-or-select {
width: auto;
}
}
</style>

View File

@@ -0,0 +1,189 @@
<template>
<div class="condition-group mb-3" data-testid="condition-group">
<div class="d-flex">
<select v-if="!isFirst" v-model="model.andOr" class="form-select" style="width: auto;" data-testid="condition-group-and-or">
<option value="and">{{ $t("and") }}</option>
<option value="or">{{ $t("or") }}</option>
</select>
</div>
<div class="condition-group-inner mt-2 pa-2">
<div class="condition-group-conditions">
<template v-for="(child, childIndex) in model.children" :key="childIndex">
<EditMonitorConditionGroup
v-if="child.type === 'group'"
v-model="model.children[childIndex]"
:is-first="childIndex === 0"
:get-new-group="getNewGroup"
:get-new-condition="getNewCondition"
:condition-variables="conditionVariables"
@remove="removeChild"
/>
<EditMonitorCondition
v-else
v-model="model.children[childIndex]"
:is-first="childIndex === 0"
:is-last="childIndex === model.children.length - 1"
:is-in-group="true"
:condition-variables="conditionVariables"
@remove="removeChild"
/>
</template>
</div>
<div class="condition-group-actions mt-3">
<button class="btn btn-outline-secondary me-2" type="button" data-testid="add-condition-button" @click="addCondition">
{{ $t("conditionAdd") }}
</button>
<button class="btn btn-outline-secondary me-2" type="button" data-testid="add-group-button" @click="addGroup">
{{ $t("conditionAddGroup") }}
</button>
<button
class="btn btn-outline-danger"
type="button"
:aria-label="$t('conditionDeleteGroup')"
data-testid="remove-condition-group"
@click="remove"
>
<font-awesome-icon icon="trash" />
</button>
</div>
</div>
</div>
</template>
<script>
import EditMonitorCondition from "./EditMonitorCondition.vue";
export default {
name: "EditMonitorConditionGroup",
components: {
EditMonitorCondition,
},
props: {
/**
* The condition group
*/
modelValue: {
type: Object,
required: true,
},
/**
* Whether this is the first condition
*/
isFirst: {
type: Boolean,
required: true,
},
/**
* Function to generate a new group model
*/
getNewGroup: {
type: Function,
required: true,
},
/**
* Function to generate a new condition model
*/
getNewCondition: {
type: Function,
required: true,
},
/**
* Variable choices
*/
conditionVariables: {
type: Array,
required: true,
},
},
emits: [ "update:modelValue", "remove" ],
computed: {
model: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
}
}
},
methods: {
addGroup() {
const conditions = [ ...this.model.children ];
conditions.push(this.getNewGroup());
this.model.children = conditions;
},
addCondition() {
const conditions = [ ...this.model.children ];
conditions.push(this.getNewCondition());
this.model.children = conditions;
},
remove() {
this.$emit("remove", this.model);
},
removeChild(child) {
const idx = this.model.children.indexOf(child);
if (idx !== -1) {
this.model.children.splice(idx, 1);
}
},
},
};
</script>
<style lang="scss" scoped>
@import "../assets/vars.scss";
.condition-group-inner {
background: rgba(0, 0, 0, 0.05);
padding: 20px;
}
.dark .condition-group-inner {
background: rgba(255, 255, 255, 0.05);
}
.condition-group-conditions {
container-type: inline-size;
}
.condition-group-actions {
display: grid;
gap: 10px;
}
// Delete button
.condition-group-actions > :last-child {
margin-left: auto;
margin-top: 14px;
}
@container (min-width: 400px) {
.condition-group-actions {
display: flex;
}
// Delete button
.condition-group-actions > :last-child {
margin-left: auto;
margin-top: 0;
}
.btn-delete-group {
margin-left: auto;
}
}
</style>

View File

@@ -0,0 +1,149 @@
<template>
<div class="monitor-conditions">
<label class="form-label">{{ $t("Conditions") }}</label>
<div class="monitor-conditions-conditions">
<template v-for="(condition, conditionIndex) in model" :key="conditionIndex">
<EditMonitorConditionGroup
v-if="condition.type === 'group'"
v-model="model[conditionIndex]"
:is-first="conditionIndex === 0"
:get-new-group="getNewGroup"
:get-new-condition="getNewCondition"
:condition-variables="conditionVariables"
@remove="removeCondition"
/>
<EditMonitorCondition
v-else
v-model="model[conditionIndex]"
:is-first="conditionIndex === 0"
:is-last="conditionIndex === model.length - 1"
:condition-variables="conditionVariables"
@remove="removeCondition"
/>
</template>
</div>
<div class="monitor-conditions-buttons">
<button class="btn btn-outline-secondary me-2" type="button" data-testid="add-condition-button" @click="addCondition">
{{ $t("conditionAdd") }}
</button>
<button class="btn btn-outline-secondary me-2" type="button" data-testid="add-group-button" @click="addGroup">
{{ $t("conditionAddGroup") }}
</button>
</div>
</div>
</template>
<script>
import EditMonitorConditionGroup from "./EditMonitorConditionGroup.vue";
import EditMonitorCondition from "./EditMonitorCondition.vue";
export default {
name: "EditMonitorConditions",
components: {
EditMonitorConditionGroup,
EditMonitorCondition,
},
props: {
/**
* The monitor conditions
*/
modelValue: {
type: Array,
required: true,
},
conditionVariables: {
type: Array,
required: true,
},
},
emits: [ "update:modelValue" ],
computed: {
model: {
get() {
return this.modelValue;
},
set(value) {
this.$emit("update:modelValue", value);
}
}
},
created() {
if (this.model.length === 0) {
this.addCondition();
}
},
methods: {
getNewGroup() {
return {
type: "group",
children: [ this.getNewCondition() ],
andOr: "and",
};
},
getNewCondition() {
const firstVariable = this.conditionVariables[0]?.id || null;
const firstOperator = this.getVariableOperators(firstVariable)[0] || null;
return {
type: "expression",
variable: firstVariable,
operator: firstOperator?.id || null,
value: "",
andOr: "and",
};
},
addGroup() {
const conditions = [ ...this.model ];
conditions.push(this.getNewGroup());
this.$emit("update:modelValue", conditions);
},
addCondition() {
const conditions = [ ...this.model ];
conditions.push(this.getNewCondition());
this.$emit("update:modelValue", conditions);
},
removeCondition(condition) {
const conditions = [ ...this.model ];
const idx = conditions.indexOf(condition);
if (idx !== -1) {
conditions.splice(idx, 1);
this.$emit("update:modelValue", conditions);
}
},
getVariableOperators(variableId) {
return this.conditionVariables.find(v => v.id === variableId)?.operators ?? [];
},
},
};
</script>
<style lang="scss" scoped>
@import "../assets/vars.scss";
.monitor-conditions,
.monitor-conditions-conditions {
container-type: inline-size;
}
.monitor-conditions-buttons {
display: grid;
gap: 10px;
}
@container (min-width: 400px) {
.monitor-conditions-buttons {
display: flex;
}
}
</style>