2022-04-10 13:07:36 +02:00
|
|
|
<template>
|
|
|
|
<div class="row plugin entities-container">
|
|
|
|
<Loading v-if="loading" />
|
|
|
|
|
2022-04-12 00:43:22 +02:00
|
|
|
<header>
|
|
|
|
<div class="col-11">
|
|
|
|
<Selector :entity-groups="entityGroups" :value="selector" @input="selector = $event" />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="col-1 pull-right">
|
|
|
|
<button title="Refresh" @click="refresh">
|
|
|
|
<i class="fa fa-sync-alt" />
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</header>
|
2022-04-10 13:07:36 +02:00
|
|
|
|
|
|
|
<div class="groups-canvas">
|
2022-04-10 14:27:32 +02:00
|
|
|
<NoItems v-if="!Object.keys(displayGroups || {})?.length">No entities found</NoItems>
|
|
|
|
<div class="groups-container" v-else>
|
2022-04-10 13:07:36 +02:00
|
|
|
<div class="group fade-in" v-for="group in displayGroups" :key="group.name">
|
|
|
|
<div class="frame">
|
|
|
|
<div class="header">
|
|
|
|
<span class="section left">
|
|
|
|
<Icon v-bind="entitiesMeta[group.name].icon || {}"
|
|
|
|
v-if="selector.grouping === 'type' && entitiesMeta[group.name]" />
|
|
|
|
<Icon :class="pluginIcons[group.name]?.class" :url="pluginIcons[group.name]?.imgUrl"
|
|
|
|
v-else-if="selector.grouping === 'plugin' && pluginIcons[group.name]" />
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="section center">
|
|
|
|
<div class="title" v-text="entitiesMeta[group.name].name_plural"
|
|
|
|
v-if="selector.grouping === 'type' && entitiesMeta[group.name]"/>
|
|
|
|
<div class="title" v-text="group.name" v-else-if="selector.grouping === 'plugin'"/>
|
|
|
|
</span>
|
|
|
|
|
|
|
|
<span class="section right" />
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="body">
|
|
|
|
<div class="entity-frame" v-for="entity in group.entities" :key="entity.id">
|
2022-04-12 00:43:22 +02:00
|
|
|
<Entity
|
|
|
|
:value="entity"
|
|
|
|
@input="onEntityInput"
|
|
|
|
:loading="!!loadingEntities[entity.id]"
|
2022-04-12 01:10:09 +02:00
|
|
|
@loading="loadingEntities[entity.id] = $event"
|
2022-04-12 00:43:22 +02:00
|
|
|
/>
|
2022-04-10 13:07:36 +02:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
|
|
import Utils from "@/Utils"
|
|
|
|
import Loading from "@/components/Loading";
|
|
|
|
import Icon from "@/components/elements/Icon";
|
2022-04-10 14:27:32 +02:00
|
|
|
import NoItems from "@/components/elements/NoItems";
|
2022-04-10 13:07:36 +02:00
|
|
|
import Entity from "./Entity.vue";
|
|
|
|
import Selector from "./Selector.vue";
|
|
|
|
import icons from '@/assets/icons.json'
|
|
|
|
import meta from './meta.json'
|
|
|
|
|
|
|
|
export default {
|
|
|
|
name: "Entities",
|
2022-04-10 14:27:32 +02:00
|
|
|
components: {Loading, Icon, Entity, Selector, NoItems},
|
2022-04-10 13:07:36 +02:00
|
|
|
mixins: [Utils],
|
|
|
|
|
|
|
|
data() {
|
|
|
|
return {
|
|
|
|
loading: false,
|
2022-04-12 00:43:22 +02:00
|
|
|
loadingEntities: {},
|
2022-04-10 13:07:36 +02:00
|
|
|
entities: {},
|
|
|
|
selector: {
|
|
|
|
grouping: 'type',
|
|
|
|
selectedEntities: {},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
computed: {
|
|
|
|
entitiesMeta() {
|
|
|
|
return meta
|
|
|
|
},
|
|
|
|
|
|
|
|
pluginIcons() {
|
|
|
|
return icons
|
|
|
|
},
|
|
|
|
|
|
|
|
entityGroups() {
|
|
|
|
return {
|
|
|
|
'id': Object.entries(this.groupEntities('id')).reduce((obj, [id, entities]) => {
|
|
|
|
obj[id] = entities[0]
|
|
|
|
return obj
|
|
|
|
}, {}),
|
|
|
|
'type': this.groupEntities('type'),
|
|
|
|
'plugin': this.groupEntities('plugin'),
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
displayGroups() {
|
|
|
|
return Object.entries(this.entityGroups[this.selector.grouping]).filter(
|
|
|
|
(entry) => entry[1].filter(
|
|
|
|
(e) => !!this.selector.selectedEntities[e.id]
|
|
|
|
).length > 0
|
|
|
|
).sort((a, b) => a[0].localeCompare(b[0])).map(
|
|
|
|
([grouping, entities]) => {
|
|
|
|
return {
|
|
|
|
name: grouping,
|
2022-04-10 17:57:51 +02:00
|
|
|
entities: entities.filter(
|
|
|
|
(e) => e.id in this.selector.selectedEntities
|
|
|
|
),
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
)
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
methods: {
|
|
|
|
groupEntities(attr) {
|
|
|
|
return Object.values(this.entities).reduce((obj, entity) => {
|
|
|
|
const entities = obj[entity[attr]] || {}
|
|
|
|
entities[entity.id] = entity
|
|
|
|
obj[entity[attr]] = Object.values(entities).sort((a, b) => {
|
|
|
|
return a.name.localeCompare(b.name)
|
|
|
|
})
|
|
|
|
|
|
|
|
return obj
|
|
|
|
}, {})
|
|
|
|
},
|
|
|
|
|
|
|
|
async refresh() {
|
2022-04-12 00:43:22 +02:00
|
|
|
const actions = Object.keys(
|
|
|
|
Object.values(this.selector.selectedEntities).reduce((obj, entity) => {
|
|
|
|
if (entity.plugin)
|
|
|
|
obj[entity.plugin] = true
|
|
|
|
return obj
|
|
|
|
}, {})
|
|
|
|
).map((plugin) => `${plugin}.status`)
|
|
|
|
|
|
|
|
this.loadingEntities = {
|
|
|
|
...this.loadingEntities,
|
|
|
|
...Object.keys(this.selector.selectedEntities).reduce((obj, id) => {
|
|
|
|
obj[id] = true
|
|
|
|
return obj
|
|
|
|
}, {}),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Force refresh by calling `.status` on all the selected plugins
|
|
|
|
await Promise.all(actions.map((act) => this.request(act)))
|
|
|
|
},
|
|
|
|
|
|
|
|
async sync() {
|
2022-04-10 13:07:36 +02:00
|
|
|
this.loading = true
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.entities = (await this.request('entities.get')).reduce((obj, entity) => {
|
|
|
|
entity.meta = {
|
|
|
|
...(meta[entity.type] || {}),
|
|
|
|
...(entity.meta || {}),
|
|
|
|
}
|
|
|
|
|
|
|
|
obj[entity.id] = entity
|
|
|
|
return obj
|
|
|
|
}, {})
|
|
|
|
|
|
|
|
this.selector.selectedEntities = this.entityGroups.id
|
|
|
|
} finally {
|
|
|
|
this.loading = false
|
|
|
|
}
|
|
|
|
},
|
2022-04-11 23:16:29 +02:00
|
|
|
|
2022-04-12 00:43:22 +02:00
|
|
|
onEntityInput(entity) {
|
|
|
|
const entityId = entity.id
|
|
|
|
this.entities[entityId] = entity
|
|
|
|
if (this.loadingEntities[entity.id])
|
|
|
|
delete this.loadingEntities[entity.id]
|
|
|
|
},
|
|
|
|
|
2022-04-11 23:16:29 +02:00
|
|
|
onEntityUpdate(event) {
|
|
|
|
const entityId = event.entity.id
|
|
|
|
if (entityId == null)
|
|
|
|
return
|
2022-04-12 00:43:22 +02:00
|
|
|
if (this.loadingEntities[entityId])
|
|
|
|
delete this.loadingEntities[entityId]
|
2022-04-11 23:16:29 +02:00
|
|
|
|
|
|
|
this.entities[entityId] = {
|
|
|
|
...event.entity,
|
|
|
|
meta: {
|
|
|
|
...(this.entities[entityId]?.meta || {}),
|
|
|
|
...(event.entity?.meta || {}),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
},
|
2022-04-10 13:07:36 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
mounted() {
|
2022-04-11 23:16:29 +02:00
|
|
|
this.subscribe(
|
|
|
|
this.onEntityUpdate,
|
|
|
|
'on-entity-update',
|
|
|
|
'platypush.message.event.entities.EntityUpdateEvent'
|
|
|
|
)
|
|
|
|
|
2022-04-12 00:43:22 +02:00
|
|
|
this.sync()
|
2022-04-10 13:07:36 +02:00
|
|
|
},
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
|
|
|
|
<style lang="scss">
|
|
|
|
@import "vars";
|
|
|
|
@import "~@/style/items";
|
|
|
|
|
|
|
|
.entities-container {
|
|
|
|
--groups-per-row: 1;
|
|
|
|
|
|
|
|
@include from($desktop) {
|
|
|
|
--groups-per-row: 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
@include from($fullhd) {
|
|
|
|
--groups-per-row: 3;
|
|
|
|
}
|
|
|
|
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
overflow: auto;
|
|
|
|
color: $default-fg-2;
|
|
|
|
font-weight: 400;
|
|
|
|
|
2022-04-12 00:43:22 +02:00
|
|
|
button {
|
|
|
|
background: #ffffff00;
|
|
|
|
border: 0;
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
color: $default-hover-fg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
header {
|
|
|
|
width: 100%;
|
2022-04-10 13:07:36 +02:00
|
|
|
height: $selector-height;
|
2022-04-12 00:43:22 +02:00
|
|
|
display: flex;
|
|
|
|
background: $default-bg-2;
|
|
|
|
box-shadow: $border-shadow-bottom;
|
2022-04-10 13:07:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
.groups-canvas {
|
|
|
|
width: 100%;
|
|
|
|
height: calc(100% - #{$selector-height});
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
}
|
|
|
|
|
|
|
|
.groups-container {
|
|
|
|
overflow: auto;
|
|
|
|
@include from($desktop) {
|
|
|
|
column-count: var(--groups-per-row);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.group {
|
|
|
|
width: 100%;
|
|
|
|
max-height: 100%;
|
|
|
|
position: relative;
|
|
|
|
padding: $main-margin 0;
|
|
|
|
display: flex;
|
|
|
|
break-inside: avoid;
|
2022-04-10 17:57:51 +02:00
|
|
|
padding: $main-margin;
|
2022-04-10 13:07:36 +02:00
|
|
|
|
|
|
|
.frame {
|
|
|
|
max-height: calc(100% - #{2 * $main-margin});
|
|
|
|
display: flex;
|
|
|
|
flex-direction: column;
|
|
|
|
flex-grow: 1;
|
|
|
|
position: relative;
|
|
|
|
box-shadow: $group-shadow;
|
|
|
|
border-radius: 1em;
|
|
|
|
}
|
|
|
|
|
|
|
|
.header {
|
|
|
|
width: 100%;
|
|
|
|
height: $header-height;
|
|
|
|
display: table;
|
|
|
|
background: $header-bg;
|
|
|
|
box-shadow: $header-shadow;
|
|
|
|
border-radius: 1em 1em 0 0;
|
|
|
|
|
|
|
|
.section {
|
|
|
|
height: 100%;
|
|
|
|
display: table-cell;
|
|
|
|
vertical-align: middle;
|
|
|
|
|
|
|
|
&.left, &.right {
|
|
|
|
width: 10%;
|
|
|
|
}
|
|
|
|
|
|
|
|
&.center {
|
|
|
|
width: 80%;
|
|
|
|
text-align: center;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
.body {
|
|
|
|
background: $default-bg-2;
|
|
|
|
max-height: calc(100% - #{$header-height});
|
|
|
|
overflow: auto;
|
|
|
|
flex-grow: 1;
|
|
|
|
|
|
|
|
.entity-frame:last-child {
|
|
|
|
border-radius: 0 0 1em 1em;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
</style>
|