refactoring and reworking how to edit rules

This commit is contained in:
Christian Seyfferth 2019-01-16 17:34:00 +01:00
parent b1b60ee95f
commit 0624a29b0b
3 changed files with 193 additions and 93 deletions

122
app.js
View File

@ -64,7 +64,14 @@ var shiftStorage = {
var ruleStorage = { var ruleStorage = {
fetch: function () { fetch: function () {
'use strict'; 'use strict';
var rules = JSON.parse(localStorage.getItem(RULE_STORAGE_KEY)) || Rule.defaults(); var parsed = JSON.parse(localStorage.getItem(RULE_STORAGE_KEY)) | [];
var rules = parsed.length > 0
? parsed.map((e,i) => {
var r = Rule.thaw(e);
r.id = i;
return r;
})
: Rule.defaults();
ruleStorage.uid = rules.length; ruleStorage.uid = rules.length;
return rules; return rules;
}, },
@ -75,6 +82,60 @@ var ruleStorage = {
} }
}; };
Vue.component('chip-input', {
template: `
<div class="chips no-autoinit" :id="name"></div>
`,
data() {
return {
instance: null,
chips: []
}
},
computed: {
chipsData() {
return this.instance.chipsData;
}
},
watch:{
initData:{
deep: true,
handler(n,o) {
if (n !== o) {
this.initialize();
this.$emit('init');
}
}
}
},
props: {
name: String,
initData: Array
},
methods: {
initialize() {
this.chips = this.initData.map(e => e);
var el = $('#'+this.name)[0];
this.instance = M.Chips.init(el, {
data: this.chips,
onChipAdd: () => {
this.$emit("change",this.chipsData);
},
onChipDelete: () => {
this.$emit("change",this.chipsData);
}
});
}
},
mounted() {
this.initialize();
}
});
var app = new Vue({ var app = new Vue({
el: '#app', el: '#app',
data: { data: {
@ -85,8 +146,10 @@ var app = new Vue({
dp_sheet: '', dp_sheet: '',
deletedShift: '', deletedShift: '',
remaining: shiftStorage.count(), remaining: shiftStorage.count(),
selectedShift: '', selectedShift: new Shift({}),
selectedRule: '', selectedShiftIndex: -1,
selectedRule: new Rule({}),
selectedRuleIndex: -1,
saveto: 'dienstplan.ics', saveto: 'dienstplan.ics',
uploadFileName: "", uploadFileName: "",
config: { config: {
@ -99,7 +162,7 @@ var app = new Vue({
display_language: 'de' display_language: 'de'
}, },
toast_length: 3000 toast_length: 3000
} },
}, },
watch: { watch: {
shifts: { shifts: {
@ -107,7 +170,7 @@ var app = new Vue({
'use strict'; 'use strict';
shiftStorage.save(shifts); shiftStorage.save(shifts);
this.remaining = shifts.length; this.remaining = shifts.length;
M.toast({html:"Änderungen gespeichert.", displayLength:this.config.toast_length}); this.makeToast("Änderungen gespeichert.");
}, },
deep: true deep: true
}, },
@ -115,12 +178,19 @@ var app = new Vue({
handler: function (rules) { handler: function (rules) {
'use strict'; 'use strict';
ruleStorage.save(rules); ruleStorage.save(rules);
M.toast({html:"Änderungen gespeichert.", displayLength:this.config.toast_length}); this.makeToast("Änderungen gespeichert.");
}, },
deep: true deep: true
} }
}, },
methods: { methods: {
updateArten(value){
this.selectedRule.Arten = value;
},
updateTitel(value){
this.selectedRule.Titel = value;
},
makeToast(message) { makeToast(message) {
M.toast({ M.toast({
html: message, html: message,
@ -134,6 +204,7 @@ var app = new Vue({
this.uploadFileName = files[0].name + " [" + Math.round(files[0].size / 1024) + " kB]"; this.uploadFileName = files[0].name + " [" + Math.round(files[0].size / 1024) + " kB]";
this.makeToast(this.uploadFileName + " ausgewählt"); this.makeToast(this.uploadFileName + " ausgewählt");
}, },
handleInputFile: function (file) { handleInputFile: function (file) {
var reader = new FileReader(); var reader = new FileReader();
var vm = this; var vm = this;
@ -154,6 +225,7 @@ var app = new Vue({
}; };
reader.readAsBinaryString(file); reader.readAsBinaryString(file);
}, },
parseForErfurt: function (dp) { parseForErfurt: function (dp) {
var arr = XLSX.utils.sheet_to_row_object_array(dp, { var arr = XLSX.utils.sheet_to_row_object_array(dp, {
range: 1 range: 1
@ -180,6 +252,7 @@ var app = new Vue({
} }
}); });
}, },
parseForStuttgart: function (dp) { parseForStuttgart: function (dp) {
var arr = XLSX.utils.sheet_to_json(dp, { var arr = XLSX.utils.sheet_to_json(dp, {
header: "A", header: "A",
@ -217,17 +290,21 @@ var app = new Vue({
} }
}); });
}, },
addShift: function (shift) { addShift: function (shift) {
this.shifts.push(shift); this.shifts.push(shift);
}, },
removeShift: function (shift) { removeShift: function (shift) {
this.shifts.splice(this.shifts.indexOf(shift), 1); this.shifts.splice(this.shifts.indexOf(shift), 1);
this.makeToast(shift.VEventTitle + " gelöscht"); this.makeToast(shift.VEventTitle + " gelöscht");
}, },
cleanStorage: function () { cleanStorage: function () {
this.shifts = []; this.shifts = [];
this.makeToast("Alle Einträge gelöscht"); this.makeToast("Alle Einträge gelöscht");
}, },
createDownloadFile: function () { createDownloadFile: function () {
var vCal = new VCalendar("Dienstplan Kalender"); var vCal = new VCalendar("Dienstplan Kalender");
this.shifts.forEach(function (shift) { this.shifts.forEach(function (shift) {
@ -245,58 +322,51 @@ var app = new Vue({
this.icsFile = window.URL.createObjectURL(this.blob); this.icsFile = window.URL.createObjectURL(this.blob);
this.makeToast(this.saveto + " erstellt."); this.makeToast(this.saveto + " erstellt.");
}, },
downloadFile: function () { downloadFile: function () {
if (window.navigator.msSaveOrOpenBlob) { if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(this.blob, this.saveto); window.navigator.msSaveOrOpenBlob(this.blob, this.saveto);
} }
}, },
selectShift: function (shift) { selectShift: function (shift) {
this.selectedShift = Shift.thaw(shift); this.selectedShift = Shift.thaw(shift);
this.keepShift = shift; this.keepShift = shift;
M.updateTextFields(); M.updateTextFields();
$('#shiftModal').modal('open'); $('#shiftModal').modal('open');
}, },
saveChanges: function (changedShift) { saveChanges: function (changedShift) {
this.shifts.splice(this.shifts.indexOf(this.keepShift), 1, changedShift); this.shifts.splice(this.shifts.indexOf(this.keepShift), 1, changedShift);
$('#shiftModal').modal('close'); $('#shiftModal').modal('close');
this.keepShift = ''; this.keepShift = '';
this.selectedShift = ''; this.selectedShift = '';
}, },
discardChanges: function (changedShift) { discardChanges: function (changedShift) {
$('#shiftModal').modal('close'); $('#shiftModal').modal('close');
this.keepShift = ''; this.keepShift = '';
this.selectedShift = ''; this.selectedShift = '';
}, },
selectRule: function (rule) {
this.selectedRule = new Rule(rule.duration, rule.art, rule.title);
this.keepRule = rule;
$('#rule_arten').chips({ editRule: function (rule) {
data: this.selectedRule.Arten this.selectedRule = Rule.thaw(rule);
}); this.selectedRuleIndex = this.rules.indexOf(rule);
$('#rule_titel').chips({
data: this.selectedRule.Titel
});
$('#ruleModal').modal('open'); $('#ruleModal').modal('open');
}, },
saveRule: function (changedRule) {
changedRule.Arten = $('#rule_arten').chips('data');
changedRule.Titel = $('#rule_titel').chips('data');
this.rules.splice(this.rules.indexOf(this.keepRule), 1, changedRule); saveRule: function () {
this.rules[this.selectedRuleIndex] = this.selectedRule;
$('#ruleModal').modal('close'); $('#ruleModal').modal('close');
this.keepRule = ''; this.selectedRule = new Rule();
this.selectedRule = '';
}, },
discardRule: function (changedRule) {
this.rules.splice(this.rules.indexOf(this.keepRule), 1, this.keepRule);
discardRule: function () {
$('#ruleModal').modal('close'); $('#ruleModal').modal('close');
this.keepRule = '';
this.selectedRule = ''; this.selectedRule = new Rule();
} }
}, },

View File

@ -38,6 +38,7 @@
<div id="body"> <div id="body">
<main class="container"> <main class="container">
<div id="termine"> <div id="termine">
<div class="card" v-if="shifts.length > 0"> <div class="card" v-if="shifts.length > 0">
<div class="card-content"> <div class="card-content">
<h3 class="card-title">Termine</h3> <h3 class="card-title">Termine</h3>
@ -140,28 +141,36 @@
</li> </li>
</ul> </ul>
</div> </div>
<div class="card" id="Einstellungen"> <div class="" id="Einstellungen">
<div class="card-content"> <div class="">
<h3 class="card-title">Regeln für die Dauer</h3> <h3 class="title">Regeln für die Dauer</h3>
<div> <div>
<span class="chip orange">Titel der Veranstaltung</span> <span class="chip orange">Titel der Veranstaltung</span>
<span class="chip indigo">Art des Dienstes</span> <span class="chip indigo">Art des Dienstes</span>
</div> </div>
<ul class="collection highlight"> <div class="collection">
<li class="collection-item" v-for="r in rules" :key="rules.indexOf(r)" @click="selectRule(r)"> <a class="collection-item avatar" v-for="(r,i) in rules" :key="'rule-'+i" @click="editRule(r)">
<span v-if="rules.indexOf(r) != 0" class="title">sonst </span> <i class="circle">#{{ i+1 }}</i>
<span class="title" v-if="rules.indexOf(r) < rules.length -1">wenn: </span> <span class="title">{{ r.Name}} </span>
<span class="secondary-content">{{ r.duration }} min</span> <div class="secondary-content">
<div> <span class="badge new" data-badge-caption="Minuten">{{ r.Dauer }}</span>
<span v-if="Array.isArray(r.art)">
<span class="chip indigo" :class="{'lighten-3' : r.art.length > 1, 'lighten-2' : r.art.length == 1}" v-for="a in r.art" :key="r.art.indexOf(a)">{{a}}</span>
</span>
<span v-if="Array.isArray(r.title)">
<span class="chip orange" :class="{'lighten-3' : r.title.length > 1, 'lighten-2' : r.title.length == 1}" v-for="t in r.title" :key="r.title.indexOf(t)">{{t}}</span>
</span>
</div> </div>
</li> <div>
</ul> <span class="chip indigo"
:class="'lighten-'+(r.Arten.length > 1 ? '3' : '1')"
v-for="a in r.Arten">
{{a.tag}}
</span>
<span class="chip orange"
:class="'lighten-'+(r.Titel.length > 1 ? '3' : '1')"
v-for="t in r.Titel">
{{t.tag}}
</span>
</div>
</a>
</div>
</div> </div>
</div> </div>
@ -170,17 +179,21 @@
<div class="modal-content"> <div class="modal-content">
<h4>Regel anpassen</h4> <h4>Regel anpassen</h4>
<div class="row"> <div class="row">
<div class="range-field col m6 s12"> <div class="range-field col s12 m6 l4">
<label class="active" for="rule_duration">Dauer in Minuten: {{selectedRule.Dauer}}</label> <label class="active" for="rule_duration">Dauer in Minuten: {{selectedRule.Dauer}}</label>
<input id="rule_duration" type="range" v-model="selectedRule.duration" min="30" max="300" step="10"> <input id="rule_duration" type="range" v-model="selectedRule.Dauer" min="30" max="300" step="10">
</div> </div>
<div class="input-field col s12 m8 l6"> <div class="range-field col s12 m6 l8">
<label class="active" for="rule_name">Name</label>
<input id="rule_name" type="text" v-model="selectedRule.Name">
</div>
<div class="input-field col s12">
<label class="active" for="shift_kind">Arten</label> <label class="active" for="shift_kind">Arten</label>
<div id="rule_arten" class="chips-initial"></div> <chip-input name="regel_arten" :init-data="selectedRule.Arten" @change="updateArten">
</div> </div>
<div class="input-field col m8 l6 s12"> <div class="input-field col s12">
<label class="active" for="rule_titel">Titel</label> <label class="active" for="rule_titel">Titel</label>
<div id="rule_titel" class="chips-inital"></div> <chip-input name="regel_titel" :init-data="selectedRule.Titel" @change="updateTitel">>
</div> </div>
</div> </div>
</div> </div>

View File

@ -7,91 +7,108 @@ var TIMEZONE_NAME = "Europe/Berlin";
//requires moment.js //requires moment.js
Array.prototype.asChipData = function () { Array.prototype.asChipData = function () {
var chips = []; return this.map((e,i) => {return {tag:e,id:i}; })
this.forEach(function (el, index, array) {
chips.push({
tag: el,
id: index
});
});
return chips;
} }
Array.prototype.fromChipData = function () { Array.prototype.fromChipData = function () {
var strings = []; return this.map(e => {return e.tag; })
this.forEach(function (el, id, array) {
strings.push(el.tag);
});
return strings;
} }
function Rule(duration, arten, titel) { function Rule() {
this.duration = duration; var options = {};
this.art = arten;
this.title = titel; if (arguments[0]) options = arguments[0];
var default_args = {
'arten' : [],
'dauer' : 60,
'titel': [],
'name' : "Standard"
}
for (var index in default_args) {
if (typeof options[index] == "undefined") options[index] = default_args[index];
}
this._duration = options.dauer;
this._arten = options.arten;
this._titel = options.titel;
this._name = options.name;
} }
Rule.prototype = { Rule.prototype = {
get Dauer() { get Dauer() {
return this.duration; return this._duration;
}, },
set Dauer(value) { set Dauer(value) {
this.duration = value; this._duration = value;
}, },
get Arten() { get Arten() {
return this.art.asChipData(); return this._arten.asChipData();
}, },
set Arten(value) { set Arten(value) {
this.art = value.fromChipData(); this._arten = value.fromChipData();
}, },
get Titel() { get Titel() {
return this.title.asChipData(); return this._titel.asChipData();
}, },
set Titel(value) { set Titel(value) {
this.title = value.fromChipData(); this._titel = value.fromChipData();
} },
get Name() {
return this._name;
},
set Name(value) {
this._name = value;
},
}; };
Rule.prototype.fits = function (art, title) { Rule.prototype.fits = function (art, title) {
var artMatch = false; var artMatch = false;
var nameMatch = false; var nameMatch = false;
if (this.art.length == 0) artMatch = true; if (this._arten.length == 0) artMatch = true;
else { else {
this.art.forEach(function (el, i) { this._arten.forEach(function (el, i) {
if (art.includes(el.toLowerCase())) artMatch = true; if (art.includes(el.toLowerCase())) artMatch = true;
}); });
} }
if (this.title.length == 0) nameMatch = true; if (this._titel.length == 0) nameMatch = true;
else { else {
this.title.forEach(function (el, i) { this._titel.forEach(function (el, i) {
if (name.includes(el.toLowerCase())) nameMatch = true; if (name.includes(el.toLowerCase())) nameMatch = true;
}); });
} }
return artMatch && nameMatch; return artMatch && nameMatch;
} }
Rule.thaw = function(json) {
return new Rule({
dauer: json._duration,
name: json._name,
arten: json._arten,
titel: json._titel
});
}
Rule.defaults = function () { Rule.defaults = function () {
var rules = []; var rules = [];
rules.push(new Rule(60, ['VS'], ['Kinderkonzert'])); var input = [
rules.push(new Rule(120, ['VS'], ["expeditionskonzert", "sinfoniekonzert"])); {name: "EF #01", dauer: 60, arten: ['VS'], titel: ['Kinderkonzert']},
rules.push(new Rule(150, ['Oa', 'GP'], ['Expeditionskonzert'])); {name: "EF #02", dauer: 120, arten: ['VS'], titel: ["Expeditionskonzert", "Sinfoniekonzert"]},
rules.push(new Rule(60, ['Oa', 'GP'], ['Expeditionskonzert'])); {name: "EF #03", dauer: 150, arten: ['Oa', 'GP'], titel: ['Expeditionskonzert']},
rules.push(new Rule(150, ['Oa', 'OSP'], [])); {name: "EF #04", dauer: 60, arten: ['Oa', 'GP'], titel: ['Expeditionskonzert']},
rules.push(new Rule(180, ["VS", "BO", "OHP", "HP", "GP", "Prem", "WA"], [])); {name: "EF #05", dauer: 150, arten: ['Oa', 'OSP']},
rules.push(new Rule(60, [], [])); {name: "EF #06", dauer: 180, arten: ["VS", "BO", "OHP", "HP", "GP", "Prem", "WA"]},
rules.push(new Rule(60, [], [])); {name: "Standard", dauer: 60}
rules.push(new Rule(60, [], [])); ];
rules.push(new Rule(60, [], [])); input.forEach(e => { rules.push(new Rule(e)); });
rules.push(new Rule(60, [], []));
return rules; return rules;
} }
var DURATION_RULES = Rule.defaults(); var DURATION_RULES = Rule.defaults();
function Shift() { function Shift() {
var evt = window.event || arguments[1] || arguments.callee.caller.arguments[0];
var target = evt.target || evt.srcElement;
var options = {}; var options = {};
if (arguments[0]) options = arguments[0]; if (arguments[0]) options = arguments[0];
@ -218,12 +235,12 @@ Shift.setDurationFromRules = function (shift) {
return; return;
} }
var art = shift.Art.toLowerCase(); var art = shift.Art.toLowerCase();
var name = shift.Name.toLowerCase(); var titel = shift.Name.toLowerCase();
var duration = 60; var duration = 60;
for (var rIndex in DURATION_RULES) { for (var rIndex in DURATION_RULES) {
var rule = DURATION_RULES[rIndex]; var rule = DURATION_RULES[rIndex];
if (rule.fits(art, name)) { if (rule.fits(art, titel)) {
duration = rule.Dauer; duration = rule.Dauer;
break; break;
} }