Upgraded dependencies, bumped to Node 24.
This commit is contained in:
@@ -1,3 +1,121 @@
|
||||
<script setup>
|
||||
import { format } from 'date-fns';
|
||||
import { computed, inject, ref } from 'vue';
|
||||
|
||||
import AlertDialog from '#/components/alerts/create.vue';
|
||||
|
||||
import { del, get } from '#/src/api.js';
|
||||
|
||||
const pageContext = inject('pageContext');
|
||||
|
||||
const alerts = ref(pageContext.pageProps.alerts);
|
||||
const showAlertDialog = ref(false);
|
||||
const done = ref(true);
|
||||
|
||||
const query = ref('');
|
||||
const filterActors = ref(false);
|
||||
const filterEntities = ref(false);
|
||||
const filterTags = ref(false);
|
||||
const filterExpressions = ref(false);
|
||||
const filterCombined = ref(null);
|
||||
|
||||
const filteredAlerts = computed(() => {
|
||||
const queryRegex = new RegExp(query.value, 'i');
|
||||
|
||||
return alerts.value.filter((alert) => {
|
||||
if (filterActors.value && alert.actors.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterEntities.value && alert.entities.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterTags.value && alert.tags.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterExpressions.value && alert.matches.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterCombined.value === false && [...alert.actors, ...alert.entities, ...alert.tags, ...alert.matches].length > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterCombined.value === true && [...alert.actors, ...alert.entities, ...alert.tags, ...alert.matches].length === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queryRegex.test(alert.id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alert.actors.some((actor) => queryRegex.test(actor.name))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alert.tags.some((tag) => queryRegex.test(tag.name))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alert.entities.some((entity) => queryRegex.test(entity.name))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alert.matches.some((match) => queryRegex.test(match.expression))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
async function reloadAlerts() {
|
||||
alerts.value = await get('/alerts');
|
||||
}
|
||||
|
||||
async function removeAlert(alert) {
|
||||
if (done.value === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`Are you sure you want to remove alert #${alert.id}?`)) { // eslint-disable-line no-alert
|
||||
return;
|
||||
}
|
||||
|
||||
done.value = false;
|
||||
|
||||
const alertLabel = [
|
||||
...alert.actors.map((actor) => actor.name),
|
||||
...alert.tags.map((tag) => tag.name),
|
||||
...alert.entities.map((entity) => entity.name),
|
||||
...alert.matches.map((match) => match.expression),
|
||||
].filter(Boolean).join(', ');
|
||||
|
||||
try {
|
||||
await del(`/alerts/${alert.id}`, {
|
||||
undoFeedback: `Removed alert for '${alertLabel}'`,
|
||||
errorFeedback: `Failed to remove alert for '${alertLabel}'`,
|
||||
appendErrorMessage: true,
|
||||
});
|
||||
|
||||
await reloadAlerts();
|
||||
}
|
||||
finally {
|
||||
done.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
const filterCombinedStates = [null, true, false];
|
||||
|
||||
function toggleFilterCombined() {
|
||||
const index = filterCombinedStates.indexOf(filterCombined.value);
|
||||
|
||||
filterCombined.value = filterCombinedStates[(index + 1) % filterCombinedStates.length];
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="profile-section">
|
||||
<div class="section-header">
|
||||
@@ -169,28 +287,28 @@
|
||||
<div class="alert-triggers">
|
||||
<Icon
|
||||
v-if="alert.notify && alert.isFromPreset"
|
||||
v-tooltip="'Notify in traxxx, added as quick alert'"
|
||||
v-tooltip="{ content: 'Notify in traxxx, added as quick alert', ariaId: `alert-notify-preset-${alert.id}` }"
|
||||
icon="bell-plus"
|
||||
class="trigger"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
v-else-if="alert.notify"
|
||||
v-tooltip="'Notify in traxxx'"
|
||||
v-tooltip="{ content: 'Notify in traxxx', ariaId: `alert-notify-${alert.id}` }"
|
||||
icon="bell2"
|
||||
class="trigger"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
v-if="alert.stashes.some((stash) => !stash.isPrimary)"
|
||||
v-tooltip="`Add to ${alert.stashes.map((stash) => stash.name).join(', ')}`"
|
||||
v-tooltip="{ content: `Add to ${alert.stashes.map((stash) => stash.name).join(', ')}`, ariaId: `alert-stashes-${alert.id}` }"
|
||||
icon="folder-heart"
|
||||
class="trigger"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
v-else-if="alert.stashes.length > 0"
|
||||
v-tooltip="alert.stashes.length > 0 ? 'Add to Favorites' : undefined"
|
||||
v-tooltip="alert.stashes.length > 0 ? { content: 'Add to Favorites', ariaId: `alert-favorites-${alert.id}` } : undefined"
|
||||
icon="heart7"
|
||||
class="trigger"
|
||||
/>
|
||||
@@ -206,7 +324,7 @@
|
||||
|
||||
<div class="alert-actions">
|
||||
<span
|
||||
v-tooltip="format(alert.createdAt, 'yyyy-MM-dd hh:mm')"
|
||||
v-tooltip="{ content: format(alert.createdAt, 'yyyy-MM-dd hh:mm'), ariaId: `alert-created-${alert.id}` }"
|
||||
class="alert-id"
|
||||
title="Alert ID"
|
||||
>#{{ alert.id }}</span>
|
||||
@@ -227,123 +345,6 @@
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, inject } from 'vue';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
import AlertDialog from '#/components/alerts/create.vue';
|
||||
|
||||
import { get, del } from '#/src/api.js';
|
||||
|
||||
const pageContext = inject('pageContext');
|
||||
|
||||
const alerts = ref(pageContext.pageProps.alerts);
|
||||
const showAlertDialog = ref(false);
|
||||
const done = ref(true);
|
||||
|
||||
const query = ref('');
|
||||
const filterActors = ref(false);
|
||||
const filterEntities = ref(false);
|
||||
const filterTags = ref(false);
|
||||
const filterExpressions = ref(false);
|
||||
const filterCombined = ref(null);
|
||||
|
||||
const filteredAlerts = computed(() => {
|
||||
const queryRegex = new RegExp(query.value, 'i');
|
||||
|
||||
return alerts.value.filter((alert) => {
|
||||
if (filterActors.value && alert.actors.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterEntities.value && alert.entities.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterTags.value && alert.tags.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterExpressions.value && alert.matches.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterCombined.value === false && [...alert.actors, ...alert.entities, ...alert.tags, ...alert.matches].length > 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (filterCombined.value === true && [...alert.actors, ...alert.entities, ...alert.tags, ...alert.matches].length === 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (queryRegex.test(alert.id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alert.actors.some((actor) => queryRegex.test(actor.name))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alert.tags.some((tag) => queryRegex.test(tag.name))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alert.entities.some((entity) => queryRegex.test(entity.name))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (alert.matches.some((match) => queryRegex.test(match.expression))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
async function reloadAlerts() {
|
||||
alerts.value = await get('/alerts');
|
||||
}
|
||||
|
||||
async function removeAlert(alert) {
|
||||
if (done.value === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirm(`Are you sure you want to remove alert #${alert.id}?`)) { // eslint-disable-line no-restricted-globals, no-alert
|
||||
return;
|
||||
}
|
||||
|
||||
done.value = false;
|
||||
|
||||
const alertLabel = [
|
||||
...alert.actors.map((actor) => actor.name),
|
||||
...alert.tags.map((tag) => tag.name),
|
||||
...alert.entities.map((entity) => entity.name),
|
||||
...alert.matches.map((match) => match.expression),
|
||||
].filter(Boolean).join(', ');
|
||||
|
||||
try {
|
||||
await del(`/alerts/${alert.id}`, {
|
||||
undoFeedback: `Removed alert for '${alertLabel}'`,
|
||||
errorFeedback: `Failed to remove alert for '${alertLabel}'`,
|
||||
appendErrorMessage: true,
|
||||
});
|
||||
|
||||
await reloadAlerts();
|
||||
} finally {
|
||||
done.value = true;
|
||||
}
|
||||
}
|
||||
|
||||
const filterCombinedStates = [null, true, false];
|
||||
|
||||
function toggleFilterCombined() {
|
||||
const index = filterCombinedStates.indexOf(filterCombined.value);
|
||||
|
||||
filterCombined.value = filterCombinedStates[(index + 1) % filterCombinedStates.length];
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.alerts {
|
||||
width: 100%;
|
||||
|
||||
@@ -1,390 +1,11 @@
|
||||
<template>
|
||||
<Dialog
|
||||
title="New alert"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<form
|
||||
class="dialog-body"
|
||||
@submit.prevent="createAlert"
|
||||
>
|
||||
<div class="dialog-section">
|
||||
<div class="section-header">
|
||||
<h3 class="heading">IF</h3>
|
||||
</div>
|
||||
|
||||
<div class="field actors">
|
||||
<span
|
||||
v-tooltip="fieldsAnd ? 'The alert is triggered if all fields are matched.' : 'The alert is triggered if any of the fields are matched.'"
|
||||
class="field-logic fields-logic noselect"
|
||||
@click="fieldsAnd = !fieldsAnd"
|
||||
>
|
||||
<Icon
|
||||
v-show="fieldsAnd"
|
||||
icon="link3"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
v-show="!fieldsAnd"
|
||||
icon="unlink3"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<ul
|
||||
class="field-items nolist noselect"
|
||||
:class="{ and: actorAnd, or: !actorAnd }"
|
||||
>
|
||||
<li
|
||||
v-for="(actor, index) in actors"
|
||||
:key="`actor-${actor.id}`"
|
||||
class="field-item actor"
|
||||
>
|
||||
<div
|
||||
v-if="index > 0"
|
||||
class="field-logic item-logic"
|
||||
@click="actorAnd = !actorAnd"
|
||||
>{{ actorAnd ? 'AND' : 'OR' }}</div>
|
||||
|
||||
<div class="field-tile">
|
||||
<div class="field-label">{{ actor.name }}</div>
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="actors = actors.filter((selectedActor) => selectedActor.id !== actor.id)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown @show="focusActorInput">
|
||||
<button
|
||||
class="button"
|
||||
type="button"
|
||||
><Icon icon="plus3" />Add actor</button>
|
||||
|
||||
<template #popper>
|
||||
<input
|
||||
ref="actorInput"
|
||||
v-model="actorQuery"
|
||||
class="input"
|
||||
@input="searchActors"
|
||||
>
|
||||
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="actor in actorResults"
|
||||
:key="`actor-result-${actor.id}`"
|
||||
v-close-popper
|
||||
class="result-item"
|
||||
@click="selectActor(actor)"
|
||||
>
|
||||
<img
|
||||
v-if="actor.avatar"
|
||||
class="field-avatar"
|
||||
:src="getPath(actor.avatar, 'lazy')"
|
||||
>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="field-avatar"
|
||||
/>
|
||||
|
||||
<div class="result-label">
|
||||
{{ actor.name }}
|
||||
<template v-if="actor.ageFromBirth || actor.origin?.country">({{ [actor.ageFromBirth, actor.origin?.country?.alpha2].filter(Boolean).join(', ') }})</template>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="field tags">
|
||||
<span
|
||||
class="field-logic noselect"
|
||||
>{{ fieldsAnd ? 'AND' : 'OR' }}</span>
|
||||
|
||||
<ul
|
||||
class="field-items nolist noselect"
|
||||
:class="{ and: actorAnd, or: !actorAnd }"
|
||||
>
|
||||
<li
|
||||
v-for="(tag, index) in tags"
|
||||
:key="`tag-${tag.id}`"
|
||||
class="field-item tag"
|
||||
>
|
||||
<div
|
||||
v-if="index > 0"
|
||||
class="field-logic item-logic"
|
||||
@click="tagAnd = !tagAnd"
|
||||
>{{ tagAnd ? 'AND' : 'OR' }}</div>
|
||||
|
||||
<div class="field-tile field-label">{{ tag.name }}</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="tags = tags.filter((selectedTag) => selectedTag.id !== tag.id)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown>
|
||||
<button
|
||||
type="button"
|
||||
class="button"
|
||||
><Icon icon="plus3" />Add tag</button>
|
||||
|
||||
<template #popper>
|
||||
<input
|
||||
ref="tagInput"
|
||||
v-model="tagQuery"
|
||||
class="input"
|
||||
@input="searchTags"
|
||||
>
|
||||
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="tag in tagResults"
|
||||
:key="`tag-result-${tag.id}`"
|
||||
v-close-popper
|
||||
class="result-item result-label"
|
||||
@click="selectTag(tag)"
|
||||
>{{ tag.name }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="field entities">
|
||||
<span
|
||||
class="field-logic noselect"
|
||||
>{{ fieldsAnd ? 'AND' : 'OR' }}</span>
|
||||
|
||||
<ul class="field-items nolist noselect">
|
||||
<li
|
||||
v-for="(entity, index) in entities"
|
||||
:key="`entity-${entity.id}`"
|
||||
class="field-item entity"
|
||||
>
|
||||
<div
|
||||
v-if="index > 0"
|
||||
v-tooltip.click="{
|
||||
content: 'Scenes are only associated to one channel, \'AND\' would never match.',
|
||||
triggers: ['click'],
|
||||
autoHide: true,
|
||||
}"
|
||||
class="field-logic"
|
||||
>OR</div>
|
||||
|
||||
<div class="field-tile field-label">
|
||||
<Icon :icon="entity.type === 'network' ? 'device_hub' : 'tv'" />
|
||||
{{ entity.name }}
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="entities = entities.filter((selectedEntity) => selectedEntity.id !== entity.id)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown>
|
||||
<button
|
||||
type="button"
|
||||
class="button"
|
||||
><Icon icon="plus3" />Add channel</button>
|
||||
|
||||
<template #popper>
|
||||
<input
|
||||
ref="entityInput"
|
||||
v-model="entityQuery"
|
||||
class="input"
|
||||
@input="searchEntities"
|
||||
>
|
||||
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="entity in entityResults"
|
||||
:key="`entity-result-${entity.id}`"
|
||||
v-close-popper
|
||||
class="result-item result-label"
|
||||
@click="selectEntity(entity)"
|
||||
>
|
||||
<Icon :icon="entity.type === 'network' ? 'device_hub' : 'tv'" />
|
||||
|
||||
{{ entity.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="field matches">
|
||||
<span
|
||||
class="field-logic noselect"
|
||||
>{{ fieldsAnd ? 'AND' : 'OR' }}</span>
|
||||
|
||||
<ul class="field-items nolist noselect">
|
||||
<li
|
||||
v-for="(match, index) in matches"
|
||||
:key="`match-${match.property}-${match.expression}`"
|
||||
class="field-item match"
|
||||
>
|
||||
<div
|
||||
v-if="index > 0"
|
||||
class="field-logic item-logic"
|
||||
@click="matchAnd = !matchAnd"
|
||||
>{{ matchAnd ? 'AND' : 'OR' }}</div>
|
||||
|
||||
<div class="field-tile field-label">
|
||||
<strong>{{ match.property }}:</strong> {{ match.expression }}
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="matches = matches.filter((selectedEntity, selectedIndex) => selectedIndex !== index)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown>
|
||||
<button
|
||||
type="button"
|
||||
class="button"
|
||||
><Icon icon="plus3" />Add expression</button>
|
||||
|
||||
<template #popper>
|
||||
<form @submit.prevent="addMatch">
|
||||
<select
|
||||
v-model="matchProperty"
|
||||
class="input"
|
||||
>
|
||||
<option value="title">Title</option>
|
||||
<option value="description">Description</option>
|
||||
</select>
|
||||
|
||||
<input
|
||||
v-model="matchExpression"
|
||||
placeholder="Expression, // for regex"
|
||||
class="input"
|
||||
>
|
||||
</form>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-section then">
|
||||
<h3 class="heading">THEN</h3>
|
||||
|
||||
<label class="field notify">
|
||||
<span>Notify me in traxxx</span>
|
||||
<Checkbox
|
||||
:checked="notify"
|
||||
@change="(checked) => notify = checked"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<!--
|
||||
<label class="field email">
|
||||
<span>E-mail me</span>
|
||||
|
||||
<Checkbox
|
||||
:checked="email"
|
||||
@change="(checked) => email = checked"
|
||||
/>
|
||||
</label>
|
||||
-->
|
||||
|
||||
<div class="stash">
|
||||
<ul class="field-items nolist noselect">
|
||||
<li
|
||||
v-for="stash in stashes"
|
||||
:key="`stash-${stash.id}`"
|
||||
class="field-item tag"
|
||||
>
|
||||
<div class="field-tile field-label stash">
|
||||
<Icon
|
||||
v-if="stash.isPrimary"
|
||||
class="favorites"
|
||||
icon="heart7"
|
||||
/>{{ stash.name }}
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="stashes = stashes.filter((selectedStash) => selectedStash.id !== stash.id)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<template v-if="stashes.length < assets.stashes.length">
|
||||
<li class="field-add">
|
||||
<button
|
||||
v-if="stashes.length === 0"
|
||||
type="button"
|
||||
class="button favorites"
|
||||
@click="selectStash(assets.primaryStash)"
|
||||
><Icon icon="heart7" />Add to favorites</button>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown>
|
||||
<button
|
||||
type="button"
|
||||
class="button field-add"
|
||||
><Icon icon="folder-heart" />Add to stash</button>
|
||||
|
||||
<template #popper>
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="stash in assets.stashes.filter((stash) => !stashes.some((selectedStash) => selectedStash.id === stash.id))"
|
||||
:key="`stash-result-${stash.id}`"
|
||||
v-close-popper
|
||||
class="result-item result-stash result-label"
|
||||
@click="selectStash(stash)"
|
||||
>
|
||||
{{ stash.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-section dialog-actions">
|
||||
<button
|
||||
class="button button-submit"
|
||||
>Set alert</button>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, inject } from 'vue';
|
||||
|
||||
import { get, post } from '#/src/api.js';
|
||||
import getPath from '#/src/get-path.js';
|
||||
import { inject, ref, useId } from 'vue';
|
||||
|
||||
import Dialog from '#/components/dialog/dialog.vue';
|
||||
import Checkbox from '#/components/form/checkbox.vue';
|
||||
|
||||
const { assets } = inject('pageContext');
|
||||
import { get, post } from '#/src/api.js';
|
||||
import getPath from '#/src/get-path.js';
|
||||
|
||||
const props = defineProps({
|
||||
presetActors: {
|
||||
@@ -403,6 +24,8 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['alert', 'close']);
|
||||
|
||||
const { assets } = inject('pageContext');
|
||||
|
||||
const actors = ref(props.presetActors);
|
||||
const actorResults = ref([]);
|
||||
const actorInput = ref(null);
|
||||
@@ -427,6 +50,13 @@ const actorAnd = ref(true);
|
||||
const tagAnd = ref(true);
|
||||
const matchAnd = ref(true);
|
||||
|
||||
const fieldsLogicTooltipId = useId();
|
||||
const addActorDropdownId = useId();
|
||||
const addTagDropdownId = useId();
|
||||
const addEntityDropdownId = useId();
|
||||
const addMatchDropdownId = useId();
|
||||
const addStashDropdownId = useId();
|
||||
|
||||
const notify = ref(true);
|
||||
const email = ref(false);
|
||||
|
||||
@@ -530,6 +160,387 @@ function selectStash(selectedStash) {
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog
|
||||
title="New alert"
|
||||
@close="emit('close')"
|
||||
>
|
||||
<form
|
||||
class="dialog-body"
|
||||
@submit.prevent="createAlert"
|
||||
>
|
||||
<div class="dialog-section">
|
||||
<div class="section-header">
|
||||
<h3 class="heading">IF</h3>
|
||||
</div>
|
||||
|
||||
<div class="field actors">
|
||||
<span
|
||||
v-tooltip="{ content: fieldsAnd ? 'The alert is triggered if all fields are matched.' : 'The alert is triggered if any of the fields are matched.', ariaId: fieldsLogicTooltipId }"
|
||||
class="field-logic fields-logic noselect"
|
||||
@click="fieldsAnd = !fieldsAnd"
|
||||
>
|
||||
<Icon
|
||||
v-show="fieldsAnd"
|
||||
icon="link3"
|
||||
/>
|
||||
|
||||
<Icon
|
||||
v-show="!fieldsAnd"
|
||||
icon="unlink3"
|
||||
/>
|
||||
</span>
|
||||
|
||||
<ul
|
||||
class="field-items nolist noselect"
|
||||
:class="{ and: actorAnd, or: !actorAnd }"
|
||||
>
|
||||
<li
|
||||
v-for="(actor, index) in actors"
|
||||
:key="`actor-${actor.id}`"
|
||||
class="field-item actor"
|
||||
>
|
||||
<div
|
||||
v-if="index > 0"
|
||||
class="field-logic item-logic"
|
||||
@click="actorAnd = !actorAnd"
|
||||
>{{ actorAnd ? 'AND' : 'OR' }}</div>
|
||||
|
||||
<div class="field-tile">
|
||||
<div class="field-label">{{ actor.name }}</div>
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="actors = actors.filter((selectedActor) => selectedActor.id !== actor.id)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown
|
||||
:aria-id="addActorDropdownId"
|
||||
@show="focusActorInput"
|
||||
>
|
||||
<button
|
||||
class="button"
|
||||
type="button"
|
||||
><Icon icon="plus3" />Add actor</button>
|
||||
|
||||
<template #popper>
|
||||
<input
|
||||
ref="actorInput"
|
||||
v-model="actorQuery"
|
||||
class="input"
|
||||
@input="searchActors"
|
||||
>
|
||||
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="actor in actorResults"
|
||||
:key="`actor-result-${actor.id}`"
|
||||
v-close-popper
|
||||
class="result-item"
|
||||
@click="selectActor(actor)"
|
||||
>
|
||||
<img
|
||||
v-if="actor.avatar"
|
||||
class="field-avatar"
|
||||
:src="getPath(actor.avatar, 'lazy')"
|
||||
>
|
||||
|
||||
<span
|
||||
v-else
|
||||
class="field-avatar"
|
||||
/>
|
||||
|
||||
<div class="result-label">
|
||||
{{ actor.name }}
|
||||
<template v-if="actor.ageFromBirth || actor.origin?.country">({{ [actor.ageFromBirth, actor.origin?.country?.alpha2].filter(Boolean).join(', ') }})</template>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="field tags">
|
||||
<span
|
||||
class="field-logic noselect"
|
||||
>{{ fieldsAnd ? 'AND' : 'OR' }}</span>
|
||||
|
||||
<ul
|
||||
class="field-items nolist noselect"
|
||||
:class="{ and: actorAnd, or: !actorAnd }"
|
||||
>
|
||||
<li
|
||||
v-for="(tag, index) in tags"
|
||||
:key="`tag-${tag.id}`"
|
||||
class="field-item tag"
|
||||
>
|
||||
<div
|
||||
v-if="index > 0"
|
||||
class="field-logic item-logic"
|
||||
@click="tagAnd = !tagAnd"
|
||||
>{{ tagAnd ? 'AND' : 'OR' }}</div>
|
||||
|
||||
<div class="field-tile field-label">{{ tag.name }}</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="tags = tags.filter((selectedTag) => selectedTag.id !== tag.id)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown :aria-id="addTagDropdownId">
|
||||
<button
|
||||
type="button"
|
||||
class="button"
|
||||
><Icon icon="plus3" />Add tag</button>
|
||||
|
||||
<template #popper>
|
||||
<input
|
||||
ref="tagInput"
|
||||
v-model="tagQuery"
|
||||
class="input"
|
||||
@input="searchTags"
|
||||
>
|
||||
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="tag in tagResults"
|
||||
:key="`tag-result-${tag.id}`"
|
||||
v-close-popper
|
||||
class="result-item result-label"
|
||||
@click="selectTag(tag)"
|
||||
>{{ tag.name }}</li>
|
||||
</ul>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="field entities">
|
||||
<span
|
||||
class="field-logic noselect"
|
||||
>{{ fieldsAnd ? 'AND' : 'OR' }}</span>
|
||||
|
||||
<ul class="field-items nolist noselect">
|
||||
<li
|
||||
v-for="(entity, index) in entities"
|
||||
:key="`entity-${entity.id}`"
|
||||
class="field-item entity"
|
||||
>
|
||||
<div
|
||||
v-if="index > 0"
|
||||
v-tooltip.click="{
|
||||
content: 'Scenes are only associated to one channel, \'AND\' would never match.',
|
||||
triggers: ['click'],
|
||||
autoHide: true,
|
||||
ariaId: `entity-and-tooltip-${entity.id}`,
|
||||
}"
|
||||
class="field-logic"
|
||||
>OR</div>
|
||||
|
||||
<div class="field-tile field-label">
|
||||
<Icon :icon="entity.type === 'network' ? 'device_hub' : 'tv'" />
|
||||
{{ entity.name }}
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="entities = entities.filter((selectedEntity) => selectedEntity.id !== entity.id)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown :aria-id="addEntityDropdownId">
|
||||
<button
|
||||
type="button"
|
||||
class="button"
|
||||
><Icon icon="plus3" />Add channel</button>
|
||||
|
||||
<template #popper>
|
||||
<input
|
||||
ref="entityInput"
|
||||
v-model="entityQuery"
|
||||
class="input"
|
||||
@input="searchEntities"
|
||||
>
|
||||
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="entity in entityResults"
|
||||
:key="`entity-result-${entity.id}`"
|
||||
v-close-popper
|
||||
class="result-item result-label"
|
||||
@click="selectEntity(entity)"
|
||||
>
|
||||
<Icon :icon="entity.type === 'network' ? 'device_hub' : 'tv'" />
|
||||
|
||||
{{ entity.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="field matches">
|
||||
<span
|
||||
class="field-logic noselect"
|
||||
>{{ fieldsAnd ? 'AND' : 'OR' }}</span>
|
||||
|
||||
<ul class="field-items nolist noselect">
|
||||
<li
|
||||
v-for="(match, index) in matches"
|
||||
:key="`match-${match.property}-${match.expression}`"
|
||||
class="field-item match"
|
||||
>
|
||||
<div
|
||||
v-if="index > 0"
|
||||
class="field-logic item-logic"
|
||||
@click="matchAnd = !matchAnd"
|
||||
>{{ matchAnd ? 'AND' : 'OR' }}</div>
|
||||
|
||||
<div class="field-tile field-label">
|
||||
<strong>{{ match.property }}:</strong> {{ match.expression }}
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="matches = matches.filter((selectedEntity, selectedIndex) => selectedIndex !== index)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown :aria-id="addMatchDropdownId">
|
||||
<button
|
||||
type="button"
|
||||
class="button"
|
||||
><Icon icon="plus3" />Add expression</button>
|
||||
|
||||
<template #popper>
|
||||
<form @submit.prevent="addMatch">
|
||||
<select
|
||||
v-model="matchProperty"
|
||||
class="input"
|
||||
>
|
||||
<option value="title">Title</option>
|
||||
<option value="description">Description</option>
|
||||
</select>
|
||||
|
||||
<input
|
||||
v-model="matchExpression"
|
||||
placeholder="Expression, // for regex"
|
||||
class="input"
|
||||
>
|
||||
</form>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-section then">
|
||||
<h3 class="heading">THEN</h3>
|
||||
|
||||
<label class="field notify">
|
||||
<span>Notify me in traxxx</span>
|
||||
<Checkbox
|
||||
:checked="notify"
|
||||
@change="(checked) => notify = checked"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<!--
|
||||
<label class="field email">
|
||||
<span>E-mail me</span>
|
||||
|
||||
<Checkbox
|
||||
:checked="email"
|
||||
@change="(checked) => email = checked"
|
||||
/>
|
||||
</label>
|
||||
-->
|
||||
|
||||
<div class="stash">
|
||||
<ul class="field-items nolist noselect">
|
||||
<li
|
||||
v-for="stash in stashes"
|
||||
:key="`stash-${stash.id}`"
|
||||
class="field-item tag"
|
||||
>
|
||||
<div class="field-tile field-label stash">
|
||||
<Icon
|
||||
v-if="stash.isPrimary"
|
||||
class="favorites"
|
||||
icon="heart7"
|
||||
/>{{ stash.name }}
|
||||
</div>
|
||||
|
||||
<Icon
|
||||
icon="cross2"
|
||||
class="field-remove"
|
||||
@click="stashes = stashes.filter((selectedStash) => selectedStash.id !== stash.id)"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<template v-if="stashes.length < assets.stashes.length">
|
||||
<li class="field-add">
|
||||
<button
|
||||
v-if="stashes.length === 0"
|
||||
type="button"
|
||||
class="button favorites"
|
||||
@click="selectStash(assets.primaryStash)"
|
||||
><Icon icon="heart7" />Add to favorites</button>
|
||||
</li>
|
||||
|
||||
<li class="field-add">
|
||||
<VDropdown :aria-id="addStashDropdownId">
|
||||
<button
|
||||
type="button"
|
||||
class="button field-add"
|
||||
><Icon icon="folder-heart" />Add to stash</button>
|
||||
|
||||
<template #popper>
|
||||
<ul class="nolist">
|
||||
<li
|
||||
v-for="stash in assets.stashes.filter((stash) => !stashes.some((selectedStash) => selectedStash.id === stash.id))"
|
||||
:key="`stash-result-${stash.id}`"
|
||||
v-close-popper
|
||||
class="result-item result-stash result-label"
|
||||
@click="selectStash(stash)"
|
||||
>
|
||||
{{ stash.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</VDropdown>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="dialog-section dialog-actions">
|
||||
<button
|
||||
class="button button-submit"
|
||||
>Set alert</button>
|
||||
</div>
|
||||
</form>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.dialog-body {
|
||||
width: 30rem;
|
||||
|
||||
Reference in New Issue
Block a user