forked from platypush/platypush
Added AlarmEditor
component.
This commit is contained in:
parent
430a111303
commit
b7423e1c34
1 changed files with 419 additions and 0 deletions
|
@ -0,0 +1,419 @@
|
|||
<template>
|
||||
<div class="alarm-editor-container" :class="{'with-changes': hasChanges}">
|
||||
<Loading v-if="loading" />
|
||||
|
||||
<form class="alarm-editor" @submit.prevent="save">
|
||||
<div class="head">
|
||||
<div class="row item">
|
||||
<div class="col-8">
|
||||
<input type="text" ref="nameInput" placeholder="Alarm name" v-model="editForm.name" />
|
||||
</div>
|
||||
|
||||
<div class="col-4 buttons" v-if="hasChanges">
|
||||
<button type="button" class="reset-btn" title="Reset" @click="editForm = {...value}">
|
||||
<i class="fas fa-undo" />
|
||||
</button>
|
||||
|
||||
<button type="submit" class="save-btn" title="Save">
|
||||
<i class="fas fa-save" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
<div class="row item">
|
||||
<div class="name">
|
||||
<label>
|
||||
<i class="icon fas fa-question" />
|
||||
Condition
|
||||
</label>
|
||||
<br />
|
||||
|
||||
<span class="subtext">
|
||||
<span class="text">
|
||||
The condition that must be met for the alarm to trigger.
|
||||
<a href="https://crontab.guru" target="_blank">Cron syntax</a> is supported.
|
||||
</span>
|
||||
</span>
|
||||
|
||||
<div class="condition-type radio">
|
||||
<label :class="{selected: editForm.condition_type === 'cron'}">
|
||||
<input type="radio" value="cron" v-model="editForm.condition_type" />
|
||||
Periodic
|
||||
</label>
|
||||
|
||||
<label :class="{selected: editForm.condition_type === 'timestamp'}">
|
||||
<input type="radio" value="timestamp" v-model="editForm.condition_type" />
|
||||
Date/Time
|
||||
</label>
|
||||
|
||||
<label :class="{selected: editForm.condition_type === 'interval'}">
|
||||
<input type="radio" value="interval" v-model="editForm.condition_type" />
|
||||
Timer
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="value">
|
||||
<CronEditor :value="value.condition_type === 'cron' ? editForm.when : null"
|
||||
@input="onWhenInput($event, 'cron')"
|
||||
v-if="editForm.condition_type === 'cron'" />
|
||||
|
||||
<input type="datetime-local"
|
||||
:value="value.condition_type === 'timestamp' ? editForm.when : null"
|
||||
@input="onWhenInput($event.target.value, 'timestamp')"
|
||||
v-else-if="editForm.condition_type === 'timestamp'">
|
||||
|
||||
<TimeInterval :value="value.condition_type === 'interval' ? editForm.when : null"
|
||||
@input="onWhenInput($event, 'interval')"
|
||||
v-else-if="editForm.condition_type === 'interval'" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row item">
|
||||
<div class="name">
|
||||
<label>
|
||||
<i class="icon fas fa-music" />
|
||||
Media
|
||||
</label>
|
||||
<br />
|
||||
<span class="subtext">
|
||||
<span class="text">
|
||||
Path or URL of the media resource to play when the alarm triggers.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="value">
|
||||
<FileSelector :value="editForm.media" @input="editForm.media = $event" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row item">
|
||||
<div class="name">
|
||||
<label>
|
||||
<i class="icon fas fa-puzzle-piece" />
|
||||
Media Plugin
|
||||
</label>
|
||||
<br />
|
||||
<span class="subtext">
|
||||
<span class="text">
|
||||
The plugin to use to play the media resource.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="value">
|
||||
<input type="text" v-model="editForm.media_plugin" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row item">
|
||||
<div class="name">
|
||||
<label>
|
||||
<i class="icon fas fa-volume-high" />
|
||||
Volume
|
||||
</label>
|
||||
<br />
|
||||
<span class="subtext">
|
||||
<span class="text">
|
||||
The volume to play the media resource at.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="value">
|
||||
<Slider :value="audioVolume" :range="[0, 100]"
|
||||
@input="onVolumeChange" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row item">
|
||||
<div class="name">
|
||||
<label>
|
||||
<i class="icon fas fa-bell" />
|
||||
Snooze interval
|
||||
</label>
|
||||
<br />
|
||||
<span class="subtext">
|
||||
<span class="text">
|
||||
How long the interval should be paused after being triggered and
|
||||
snoozed.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="value">
|
||||
<TimeInterval :value="editForm.snooze_interval"
|
||||
@input="editForm.snooze_interval = $event" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row item">
|
||||
<div class="name">
|
||||
<label>
|
||||
<i class="icon fas fa-play" />
|
||||
Actions
|
||||
</label>
|
||||
<br />
|
||||
<span class="subtext">
|
||||
<span class="text">
|
||||
Actions to perform when the alarm triggers.
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="value">
|
||||
<ProcedureEditor :value="procedure"
|
||||
:with-name="false"
|
||||
@input="onActionsInput($event)" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Loading from "@/components/Loading";
|
||||
import ProcedureEditor from "@/components/Procedure/ProcedureEditor"
|
||||
import Slider from "@/components/elements/Slider"
|
||||
import CronEditor from "@/components/elements/CronEditor"
|
||||
import FileSelector from "@/components/elements/FileSelector"
|
||||
import TimeInterval from "@/components/elements/TimeInterval"
|
||||
import Utils from "@/Utils"
|
||||
|
||||
export default {
|
||||
emits: ['input'],
|
||||
mixins: [Utils],
|
||||
components: {
|
||||
CronEditor,
|
||||
FileSelector,
|
||||
Loading,
|
||||
ProcedureEditor,
|
||||
Slider,
|
||||
TimeInterval,
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
editForm: {...this.value},
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
procedure() {
|
||||
return {
|
||||
actions: [...(this.editForm.actions || [])],
|
||||
}
|
||||
},
|
||||
|
||||
audioVolume() {
|
||||
return this.editForm.audio_volume ?? this.defaultVolume
|
||||
},
|
||||
|
||||
defaultVolume() {
|
||||
return this.$root.config?.alarm?.audio_volume ?? 100
|
||||
},
|
||||
|
||||
hasChanges() {
|
||||
return Object.keys(this.changes).length > 0
|
||||
},
|
||||
|
||||
changes() {
|
||||
const changes = {}
|
||||
|
||||
if ((this.value.audio_volume ?? this.defaultVolume) !== this.audioVolume)
|
||||
changes.audio_volume = this.audioVolume
|
||||
if (JSON.stringify(this.editForm.actions) !== JSON.stringify(this.value.actions))
|
||||
changes.actions = this.editForm.actions;
|
||||
|
||||
[
|
||||
'media',
|
||||
'media_plugin',
|
||||
'name',
|
||||
'snooze_interval',
|
||||
'when',
|
||||
].forEach(key => {
|
||||
if (this.editForm[key] !== this.value[key])
|
||||
changes[key] = this.editForm[key]
|
||||
})
|
||||
|
||||
return changes
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
onWhenInput(value, type) {
|
||||
if (value == null)
|
||||
return
|
||||
|
||||
switch (type) {
|
||||
case 'timestamp':
|
||||
value = new Date(value).toISOString()
|
||||
break
|
||||
|
||||
case 'cron':
|
||||
case 'interval':
|
||||
break
|
||||
|
||||
default:
|
||||
console.error('Unknown cron type', type)
|
||||
return
|
||||
}
|
||||
|
||||
this.editForm.when = value
|
||||
this.editForm.condition_type = type
|
||||
},
|
||||
|
||||
onActionsInput(procedure) {
|
||||
this.editForm.actions = procedure.actions
|
||||
},
|
||||
|
||||
onVolumeChange(event) {
|
||||
this.editForm.audio_volume = parseFloat(event.target.value)
|
||||
},
|
||||
|
||||
async save() {
|
||||
this.loading = true
|
||||
|
||||
const request = {
|
||||
name: this.value.name,
|
||||
...this.changes,
|
||||
}
|
||||
|
||||
if (this.changes.name != null) {
|
||||
request.name = this.value.name
|
||||
request.new_name = this.changes.name
|
||||
}
|
||||
|
||||
try {
|
||||
const newAlarm = await this.request('alarm.edit', request)
|
||||
this.$emit('input', newAlarm)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.nameInput.focus()
|
||||
})
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$header-height: 3.5em;
|
||||
|
||||
.alarm-editor-container {
|
||||
width: 100%;
|
||||
height: 50em;
|
||||
max-height: 70vh;
|
||||
background: $default-bg-2;
|
||||
cursor: default;
|
||||
|
||||
.head {
|
||||
width: 100%;
|
||||
height: $header-height;
|
||||
|
||||
.row {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: $border-shadow-bottom;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
width: 100%;
|
||||
max-width: 30em;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
padding: 0;
|
||||
|
||||
button {
|
||||
margin: 0.25em 0 0.25em 0.5em;
|
||||
|
||||
&.save-btn:hover {
|
||||
background: $background-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.body {
|
||||
height: calc(100% - #{$header-height});
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.alarm-editor {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
|
||||
.item {
|
||||
padding: 0.5em 1em;
|
||||
border-bottom: $default-border-2;
|
||||
|
||||
.name {
|
||||
label {
|
||||
margin: 0;
|
||||
font-weight: bold;
|
||||
|
||||
.icon {
|
||||
opacity: 0.75;
|
||||
margin-right: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.condition-type {
|
||||
margin: 0.25em 0;
|
||||
|
||||
label {
|
||||
&.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
&:not(.selected) {
|
||||
font-weight: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.value {
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.subtext {
|
||||
display: block;
|
||||
font-size: 0.9em;
|
||||
opacity: 0.8;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
input[type=text] {
|
||||
min-height: 2em;
|
||||
}
|
||||
}
|
||||
</style>
|
Loading…
Reference in a new issue