Skip to content

Commit

Permalink
system/timers: Add system customization
Browse files Browse the repository at this point in the history
  • Loading branch information
sogehige committed Aug 2, 2018
1 parent 556712e commit 9154b3f
Show file tree
Hide file tree
Showing 13 changed files with 506 additions and 556 deletions.
385 changes: 0 additions & 385 deletions public/pages/timers.html

This file was deleted.

192 changes: 192 additions & 0 deletions public/pages/timers/edit.html
@@ -0,0 +1,192 @@
<span id="timers-edit">
<span class="title text-default" style="padding: 0 !important;">
<a class="btn btn-outline-info" style="border: 0 !important;" href="/#timers/list"><i class="fas fa-chevron-circle-left"></i></a>
<span style="position: relative; top: 2px;">{{ title }} </span>
<span style="position: relative; top: 2px;" v-if="isEditation">{{ timer.name }}</span>
</span>

<span class="float-right">
<span v-if="pending" style="cursor: auto;" class="alert-danger btn m-0">{{commons.translate('dialog.changesPending')}}</span>
<button v-if="states.save === 0" type="button" class="btn btn-primary" v-on:click="saveChanges()">{{ commons.translate('dialog.buttons.saveChanges.idle') }}</button>
<button v-if="states.save === 1" disabled="disabled" type="button" class="btn btn-primary"><i class="fas fa-circle-notch fa-spin"></i> {{ commons.translate('dialog.buttons.saveChanges.progress') }}</button>
<button v-if="states.save === 2" disabled="disabled" type="button" class="btn btn-success"><i class="fas fa-check"></i> {{ commons.translate('dialog.buttons.saveChanges.done') }}</span></button>
<button v-if="states.save === 3" disabled="disabled" type="button" class="btn btn-danger"><i class="fas fa-exclamation"></i> {{ commons.translate('dialog.buttons.something-went-wrong') }}</span></button>
</span>

<div class="widget pt-3">
<!-- Editation stuff here -->
<form>
<div class="form-group col-md-12">
<label style="margin: 0px 0px 3px; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 1px;">{{ commons.translate('timers.dialog.name') }}</label>
<input v-bind:class="{ 'is-invalid': hasError.name }" v-model="timer.name" type="text" class="form-control" @keydown="pending=true" @change="pending=true">
<small class="form-text text-muted">{{ commons.translate('timers.dialog.placeholders.name') }}</small>
<div class="invalid-feedback">
{{ commons.translate('timers.errors.timer_name_must_be_compliant') }}
</div>
</div>

<div class="form-group col-md-12">
<label style="margin: 0px 0px 3px; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 1px;">{{ commons.translate('timers.dialog.messages') }}</label>
<input v-bind:class="{ 'is-invalid': hasError.messages }" v-model="timer.messages" type="number" class="form-control" @keydown="pending=true" @change="pending=true">
<div class="invalid-feedback">{{ commons.translate('timers.errors.this_value_must_be_a_positive_number_or_0') }}</div>
</div>

<div class="form-group col-md-12">
<label style="margin: 0px 0px 3px; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 1px;">{{ commons.translate('timers.dialog.seconds') }}</label>
<input v-bind:class="{ 'is-invalid': hasError.seconds }" v-model="timer.seconds" type="number" class="form-control" @keydown="pending=true" @change="pending=true">
<div class="invalid-feedback">{{ commons.translate('timers.errors.this_value_must_be_a_positive_number_or_0') }}</div>
</div>

<div class="form-group row pl-3 pr-3">
<div class="col-md-2">
<label style="margin: 0px 0px 3px; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 1px;">{{ commons.translate('status') }}</label>
<button type="button" class="btn btn-block" v-on:click="timer.enabled = !timer.enabled;pending=true" v-bind:class="[ timer.enabled ? 'btn-success' : 'btn-danger' ]" aria-hidden="true">{{ (timer.enabled ? commons.translate('enabled') : commons.translate('disabled')) | capitalize }}</button>
</div>
</div>

<div class="form-group col-md-12">
<label style="margin: 0px 0px 3px; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 1px;">{{ commons.translate('timers.dialog.responses') }}</label>
<div class="input-group" v-for="response of responses" :key="response._id">
<div class="input-group-prepend">
<button class="btn" type="button" @click="response.enabled=!response.enabled;pending=true" :class="[response.enabled ? 'btn-success' : 'btn-danger']">
{{commons.translate(response.enabled ? 'enabled' : 'disabled')}}
</button>
</div>
<textarea-with-tags
v-bind:value="response.response"
v-bind:placeholder="''"
v-on:update="response.response=$event;pending=true" />
<div class="input-group-append">
<button class="btn btn-danger" type="button" @click="removeResponse(response._id);pending=true">
<i class="far fa-trash-alt"></i>
</button>
</div>
</div>
<button type="button" class="btn btn-success btn-block" @click="addResponse();pending=true">Add response</button>
</div>
</form>

<div class="form-group col-md-12" v-if="isEditation">
<button type="button" class="btn btn-danger" key="deleting" data-lang="dialog.buttons.delete" v-if="states.delete === 0" v-on:click="states.delete=1">{{ commons.translate('dialog.buttons.delete') }}</button>
<div class="btn-group" role="group" v-if="states.delete === 1">
<button type="button" class="btn btn-danger" key="deleted" data-lang="dialog.buttons.yes" v-on:click="deleteItem">{{ commons.translate('dialog.buttons.yes') }}</button>
<button type="button" class="btn btn-success" key="waiting" data-lang="dialog.buttons.no" v-on:click="states.delete=0">{{ commons.translate('dialog.buttons.no') }}</button>
</div>
<small class="form-text text-danger" v-html="commons.translate('systems.timers.warning')"></small>
</div>
</div>
</span>

<script>
Vue.prototype.commons = commons

function timersEditInit () {
if (_.size(translations) === 0) return setTimeout(() => timersEditInit(), 1)

var timersEdit = new Vue({
el: '#timers-edit',
components: {
'textarea-with-tags': textAreaWithTags
},
data: {
id: null,
timer: { name: '', seconds: 0, messages: 0, enabled: true },
responses: [],

pending: false,

hasError: {
name: false,
seconds: false,
messages: false
},

socket: io('/system/timers', { query: "token=" + token }),

states: {
save: 0,
delete: 0
}
},
methods: {
addResponse: function () {
this.responses.push({
_id: 'new_' + _.now(),
response: '',
enabled: true
})
},
removeResponse: function (id) {
this.responses = this.responses.filter(o => o._id !== id)
},
validateForm: function () {
// reset errors
for (let [key, value] of Object.entries(this.hasError)) {
this.hasError[key] = false
}
if (String(this.timer.seconds).trim().length === 0 || _.isNaN(Number(this.timer.seconds)) || Number(this.timer.seconds) < 0) this.hasError.seconds = true
if (String(this.timer.messages).trim().length === 0 || _.isNaN(Number(this.timer.messages)) || Number(this.timer.messages) < 0) this.hasError.messages = true
if (this.timer.name.length > 0 && !this.timer.name.match(/^[a-zA-Z0-9_]+$/)) this.hasError.name = true
return _.filter(this.hasError, (o) => o === true).length === 0
},
deleteItem: function () {
this.socket.emit('delete.timer', this.timer._id, () => {
page('/#timers/list')
})
},
saveChanges: function () {
if (this.validateForm()) {
this.states.save = 1
const data = {
timer: {
_id: commons.urlParam('id') ? commons.urlParam('id') : null,
seconds: parseInt(this.timer.seconds),
messages: parseInt(this.timer.messages),
name: this.timer.name,
enabled: this.timer.enabled
},
responses: this.responses
}
this.socket.emit('update.timer', data, (err, data) => {
if (err) {
console.error(err)
return this.states.save = 3
}
this.states.save = 2
this.pending = false
this.timer = data.timer
this.responses = data.responses
setTimeout(() => this.states.save = 0, 1000)
})
}
},
},
computed: {
isEditation: function () {
return !_.isNil(this.timer._id)
},
title: function () {
return commons.translate(this.isEditation ? 'dialog.title.edit' : 'dialog.title.add')
}
},
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
}
})

// load up from db
if (commons.urlParam('id')) {
timersEdit.id = commons.urlParam('id')
timersEdit.socket.emit('findOne.timer', { _id: commons.urlParam('id') }, (err, data) => {
timersEdit.timer = data.timer
timersEdit.responses = data.responses
timersEdit.$nextTick(() => { timersEdit.pending = false })
})
}
}
timersEditInit()
</script>
151 changes: 151 additions & 0 deletions public/pages/timers/list.html
@@ -0,0 +1,151 @@
<div id="systemTimersApp">
<div class="container-fluid">
<div class="row">
<div class="col-12">
<span data-lang="menu.timers" class="title text-default"></span>

<div class="pt-3 pb-3 mt-3 mb-3 m-0 border-top border-bottom row">
<div class="col-sm-6">
<a class="btn btn-primary" href="#timers/edit"><i class="fas fa-plus"></i> New Timers</a>
</div>
<div class="col-sm-6 text-right form-inline d-block">
<button class="btn border-0" v-on:click="showAs='cards'" v-bind:class="[ showAs === 'cards' ? 'btn-dark' : 'btn-outline-dark' ]"><i class="fas fa-th-large"></i></button>
<button class="btn border-0" v-on:click="showAs='table'" v-bind:class="[ showAs === 'table' ? 'btn-dark' : 'btn-outline-dark' ]"><i class="fas fa-th-list"></i></button>
<i class="fas fa-search text-muted" style="position: relative; left: 2.2rem;"></i>
<input type="search" class="form-control w-auto pl-5" v-model="search" placeholder="Search...">
</div>
</div>

<div v-if="showAs === 'cards' && filtered.length > 0" class="card" v-for="(item, index) of filtered" v-bind:class="{ 'mt-3': index !== 0 }">
<div class="card-body row">
<div class="col-sm-5">
<div style="margin: 0; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 3px;">{{commons.translate('name')}}</div>
<div class="font-weight-bold text-primary" style="font-size: 1.5rem">{{ item.name }}</div>
</div>

<div class="text-muted col-sm-1 text-center" style="margin-top: auto; margin-bottom: auto;font-size: 1.5rem">
<span class="fa-stack">
<i class="fas fa-stopwatch fa-stack-1x"></i>
<i v-if="!item.enabled" class="fas fa-times fa-stack-2x" style="color:Tomato; opacity: 0.7"></i>
</span>
</div>

<div style="word-break: break-all; " class="col-sm-3">
<div style="margin: 0; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 3px;">{{commons.translate('settings')}}</div>
<div><span class="font-weight-bold text-primary" style="font-size: 1.5rem">{{ item.messages }}</span> {{commons.translate('messages').toLowerCase()}}</div>
<div><span class="font-weight-bold text-primary" style="font-size: 1.5rem">{{ item.seconds }}</span> {{commons.translate('seconds').toLowerCase()}}</div>
</div>

<div style="word-break: break-all; " class="col-sm-3">
<div style="margin: 0; font-size: 11px; font-weight: 400; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 3px;">{{commons.translate('timers.dialog.responses')}}</div>
<div><span class="font-weight-bold text-primary" style="font-size: 1.5rem">{{ items.responses.filter(o => o.timerId === item._id && o.enabled).length }}</span> {{commons.translate('enabled').toLowerCase()}}</div>
<div><span class="font-weight-bold text-primary" style="font-size: 1.5rem">{{ items.responses.filter(o => o.timerId === item._id && !o.enabled).length }}</span> {{commons.translate('disabled').toLowerCase()}}</div>
</div>
</div>

<div class="card-body border-top p-0 text-right">
<div class="btn-group" role="group">
<a v-bind:href="'?id='+ item._id + '#timers/edit'" class="btn btn-outline-dark p-3 border-0"><i class="fas fa-pencil-alt mr-1" aria-hidden="true"></i> {{ commons.translate('dialog.buttons.edit') }}</a>
<button v-on:click="item.enabled = !item.enabled; updateItem(item._id)" class="btn btn-outline-dark p-3 border-0"><i class="fas mr-1" v-bind:class="[ item.enabled ? 'fa-toggle-on' : 'fa-toggle-off' ]" aria-hidden="true"></i> {{ item.enabled ? commons.translate('enabled') : commons.translate('disabled') }}</button>
<button type="button" type="button" v-on:click="deleteItem(item._id)" class="btn btn-outline-dark p-3 border-0"><i class="far fa-trash-alt mr-1" aria-hidden="true"></i> {{ commons.translate('delete') }}</button>
</div>
</div>
</div>

<div class="widget h-auto" v-if="showAs === 'table' && filtered.length > 0">
<div class="row p-2 m-0 border-bottom">
<div class="col-md-2"><strong>{{ commons.translate('name') }}</strong></div>
<div class="col-md-4"><strong>{{ commons.translate('settings') }}</strong></div>
<div class="col-md-4"><strong>{{ commons.translate('timers.dialog.responses') }}</strong></div>
</div>
<div class="row p-2 m-0 border-bottom" v-for="(item, index) of filtered">
<div class="col-md-2">{{ item.name }}</div>
<div class="col-md-4">
<span class="font-weight-bold text-primary">{{ item.messages }}</span> {{commons.translate('messages').toLowerCase()}},
<span class="font-weight-bold text-primary">{{ item.seconds }}</span> {{commons.translate('seconds').toLowerCase()}}
</div>
<div class="col-md-4">
<span class="font-weight-bold text-primary">{{ items.responses.filter(o => o.timerId === item._id && o.enabled).length }}</span> {{commons.translate('enabled').toLowerCase()}},
<span class="font-weight-bold text-primary">{{ items.responses.filter(o => o.timerId === item._id && !o.enabled).length }}</span> {{commons.translate('disabled').toLowerCase()}}
</div>
<div class="col-md-2">
<div class="p-0 text-right">
<a v-bind:href="'?id='+ item._id + '#timers/edit'" v-bind:title="commons.translate('dialog.buttons.edit')" class="btn btn-outline-dark p-1 border-0"><i class="fas fa-pencil-alt m-1" aria-hidden="true"></i></a>
<button v-bind:title="item.enabled ? commons.translate('enabled') : commons.translate('disabled')" v-on:click="item.enabled = !item.enabled; updateItem(item._id)" class="btn btn-outline-dark p-1 border-0"><i class="fas m-1" v-bind:class="[ item.enabled ? 'fa-toggle-on' : 'fa-toggle-off' ]" aria-hidden="true"></i></button>
<button type="button" v-on:click="deleteItem(item._id)" v-bind:title="commons.translate('delete')" class="btn btn-outline-dark p-1 border-0"><i class="far fa-trash-alt m-1" aria-hidden="true"></i></button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

<script>
Vue.prototype.commons = commons

function timersInit () {
if (_.size(translations) === 0) return setTimeout(() => timersInit(), 1)
var systemTimersApp = new Vue({
el: '#systemTimersApp',
components: {
'toggle': toggleEnable,
'command-input': commandInput,
'text-with-tags': textWithTags
},
data: {
showAs: 'cards',
search: '',

items: { timers: [], responses: [] },

isDataChanged: false,

state: {
settings: 0
},
socket: io('/system/timers', { query: "token=" + token }),
},
computed: {
filtered: function () {
if (this.search.length === 0) return this.items.timers
return this.items.timers.filter((o) => {
const isSearchInName = !_.isNil(o.name.match(new RegExp(this.search, 'ig')))
return isSearchInName
})
}
},
created: function () {
this.socket.emit('find.timers', (err, items) => {
this.items.timers = items.timers
this.items.responses = items.responses
})
if (localStorage.getItem('/system/Timers/showAs')) this.showAs = JSON.parse(localStorage.getItem('/system/Timers/showAs'));
},
watch: {
showAs: function(val) {
localStorage.setItem('/system/Timers/showAs', JSON.stringify(this.showAs))
}
},
filters: {
capitalize: function (value) {
if (!value) return ''
value = value.toString()
return value.charAt(0).toUpperCase() + value.slice(1)
}
},
methods: {
updateItem: function (_id) {
this.socket.emit('update', { items: this.items.timers.filter((o) => o._id === _id) })
},
deleteItem: function (id) {
this.socket.emit('delete.timer', id, () => {
this.items.timers = this.items.timers.filter((o) => o._id !== id)
})
}
}
})
}
timersInit()
</script>
1 change: 1 addition & 0 deletions src/bot/databases/mongodb.js
Expand Up @@ -94,6 +94,7 @@ class IMongoDB extends Interface {
}

async insert (table, object) {
object = _.clone(object)
delete object._id
if (_.isEmpty(object)) throw Error('Object cannot be empty')

Expand Down
1 change: 1 addition & 0 deletions src/bot/databases/nedb.js
Expand Up @@ -119,6 +119,7 @@ class INeDB extends Interface {
}

async insert (table, object) {
object = _.clone(object)
this.on(table) // init table

delete object._id
Expand Down

0 comments on commit 9154b3f

Please sign in to comment.