Commit 6b9ea06f authored by Richard Blechinger's avatar Richard Blechinger
Browse files

Merge branch 'feature/meta-validation' into develop

parents 71a795e8 f1757e6a
...@@ -30,6 +30,9 @@ ...@@ -30,6 +30,9 @@
"copy-webpack-plugin": "^6.0.3", "copy-webpack-plugin": "^6.0.3",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-plugin-vue": "^5.2.3", "eslint-plugin-vue": "^5.2.3",
"minimist": "^1.2.5",
"tailwindcss": "^1.7.6",
"vue-cli-plugin-tailwind": "~1.4.2",
"vue-template-compiler": "^2.6.12" "vue-template-compiler": "^2.6.12"
}, },
"eslintConfig": { "eslintConfig": {
...@@ -53,6 +56,8 @@ ...@@ -53,6 +56,8 @@
}, },
"postcss": { "postcss": {
"plugins": { "plugins": {
"tailwindcss": {},
"vue-cli-plugin-tailwind/purgecss": {},
"autoprefixer": {} "autoprefixer": {}
} }
}, },
......
...@@ -10,8 +10,10 @@ ...@@ -10,8 +10,10 @@
</template> </template>
<script> <script>
import 'bootstrap/dist/css/bootstrap.css' import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css' import 'bootstrap-vue/dist/bootstrap-vue.css'
import header from './components/Header.vue' import header from './components/Header.vue'
import footer from './components/Footer.vue' import footer from './components/Footer.vue'
......
@tailwind components;
@tailwind utilities;
...@@ -465,6 +465,8 @@ export default { ...@@ -465,6 +465,8 @@ export default {
// submit a conflict-resolved schedule to steering // submit a conflict-resolved schedule to steering
resolveSubmit () { resolveSubmit () {
console.log("before", this.resolveData);
// TODO: check why steering retourns undefined and null values here // TODO: check why steering retourns undefined and null values here
if (this.resolveData.schedule.add_business_days_only === undefined) { this.resolveData.schedule.add_business_days_only = false } if (this.resolveData.schedule.add_business_days_only === undefined) { this.resolveData.schedule.add_business_days_only = false }
if (this.resolveData.schedule.add_days_no === null) { this.resolveData.schedule.add_days_no = 0 } if (this.resolveData.schedule.add_days_no === null) { this.resolveData.schedule.add_days_no = 0 }
...@@ -472,6 +474,9 @@ export default { ...@@ -472,6 +474,9 @@ export default {
if (this.resolveData.schedule.fallback_id === null) { this.resolveData.schedule.fallback_id = 0 } if (this.resolveData.schedule.fallback_id === null) { this.resolveData.schedule.fallback_id = 0 }
if (this.resolveData.schedule.automation_id === null) { this.resolveData.schedule.automation_id = 0 } if (this.resolveData.schedule.automation_id === null) { this.resolveData.schedule.automation_id = 0 }
if (this.resolveData.schedule.byweekday === undefined) { this.resolveData.schedule.byweekday = 0 } if (this.resolveData.schedule.byweekday === undefined) { this.resolveData.schedule.byweekday = 0 }
console.log("after", this.resolveData);
// create the resolved schedule object including solutions // create the resolved schedule object including solutions
let resolvedSchedule = { let resolvedSchedule = {
schedule: this.resolveData.schedule, schedule: this.resolveData.schedule,
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
size="lg" size="lg"
> >
<p v-if="loaded.shows"> <p v-if="loaded.shows">
Editing a timeslot/schedule for show: <b>{{ selectedShow.name }}</b> Schedule for: <b>{{ selectedShow.name }}</b>
</p> </p>
<div v-if="timeslot && loaded.schedule"> <div v-if="timeslot && loaded.schedule">
...@@ -50,62 +50,34 @@ ...@@ -50,62 +50,34 @@
<div v-else> <div v-else>
<p>This is a recurring event: <b>{{ rruleRender(schedule.rrule) }}</b>, until: {{ prettyDate(schedule.until) }}</p> <p>This is a recurring event: <b>{{ rruleRender(schedule.rrule) }}</b>, until: {{ prettyDate(schedule.until) }}</p>
<p>All timeslots of this schedule:</p> <p>All timeslots of this schedule:</p>
<ul v-if="loaded.scheduleTimeslots">
<li
v-for="slot in scheduleTimeslots"
:key="slot.id"
>
from
<b-badge :variant="timeslot.id === slot.id ? 'info' : 'light'">
{{ prettyDateTime(slot.start) }}
</b-badge>
to
<b-badge :variant="timeslot.id === slot.id ? 'info' : 'light'">
{{ prettyDateTime(slot.end) }}
</b-badge>
</li>
</ul>
</div>
<div v-if="loaded.scheduleTimeslots"> <b-table
<p>What do you want to do?</p> id="emission-table"
<div align="center"> striped
<b-button-group> :per-page="perPage"
<b-button :current-page="currentPage"
variant="danger" :fields="['start', 'end']"
size="sm" :items="scheduleTimeslots"
@click="deleteFullSchedule(schedule.id)" :busy="!loaded.scheduleTimeslots"
> :tbody-tr-class="trClass"
<span v-if="scheduleTimeslots.length === 1">Delete</span>
<span v-else-if="schedule.rrule === 1">Delete both</span>
<span v-else>Delete schedule + all timeslots</span>
</b-button>
<b-button
v-if="schedule.rrule > 1 && scheduleTimeslots.length > 1"
variant="danger"
size="sm"
@click="deleteSingleTimeslot(schedule.id, timeslot.id)"
>
Delete only this timeslot
</b-button>
<b-button
v-if="schedule.rrule > 1 && scheduleTimeslots.length > 1"
variant="danger"
size="sm"
@click="deleteAllFutureTimeslots(schedule.id, timeslot.id)"
>
Delete this + all future timeslots
</b-button>
</b-button-group>
</div>
</div>
<div v-else>
<img
src="../../assets/radio.gif"
alt="loading timeslot data"
> >
<template v-slot:cell(start)="data">
{{ prettyDateTime(data.item.start) }}
</template>
<template v-slot:cell(end)="data">
{{ prettyDateTime(data.item.end) }}
</template>
</b-table>
<div class="tw-w-full tw-flex tw-justify-end">
<b-pagination
v-model="currentPage"
:total-rows="scheduleTimeslots.length"
:per-page="perPage"
aria-controls="emission-table"
/>
</div>
</div> </div>
</div> </div>
<div v-else> <div v-else>
...@@ -114,6 +86,109 @@ ...@@ -114,6 +86,109 @@
alt="loading schedule data" alt="loading schedule data"
> >
</div> </div>
<hr>
<h4>Add repetition of schedule</h4>
<b-row>
<b-col cols="6">
<label class="tw-leading-loose">
When to repeat?
<b-form-select
v-model="repetitionRule"
:options="repetitionOptions"
>
</b-form-select>
</label>
<b-checkbox v-model="useSameTime">
Use same start and end time
</b-checkbox>
</b-col>
<b-col cols="6" v-if="!useSameTime">
<label class="tw-leading-loose">
Repeat at
<b-form-input type="time" />
</label>
</b-col>
</b-row>
<b-row
v-if="repetitionRule === 3"
class="tw-mt-4"
>
<b-col cols="6">
<label class="tw-leading-loose">
How many days later?
<b-form-input
v-model="addNoOfDays"
type="number"
/>
</label>
<b-checkbox v-model="onlyBusinessDays">
Only count business days
</b-checkbox>
</b-col>
</b-row>
<b-row class="mt-4">
<b-col>
<b-button
variant="primary"
size="sm"
@click="createRepetitionSchedule"
>
Create repetition schedule
</b-button>
</b-col>
</b-row>
<template v-slot:modal-footer>
<div
v-if="loaded.scheduleTimeslots"
class="tw-w-full tw-flex tw-justify-between tw-items-center"
>
<div class="tw-space-x-2">
<b-button
variant="danger"
size="sm"
@click="deleteFullSchedule(schedule.id)"
>
<span v-if="scheduleTimeslots.length === 1">Delete</span>
<span v-else-if="schedule.rrule === 1">Delete both</span>
<span v-else>Delete schedule and all timeslots</span>
</b-button>
<b-button
v-if="schedule.rrule > 1 && scheduleTimeslots.length > 1"
variant="danger"
size="sm"
@click="deleteSingleTimeslot(schedule.id, timeslot.id)"
>
Delete this timeslot
</b-button>
<b-button
v-if="schedule.rrule > 1 && scheduleTimeslots.length > 1"
variant="danger"
size="sm"
@click="deleteAllFutureTimeslots(schedule.id, timeslot.id)"
>
Delete all timeslots
</b-button>
</div>
</div>
<div v-else>
<img
src="../../assets/radio.gif"
alt="loading timeslot data"
>
</div>
</template>
</b-modal> </b-modal>
<b-modal <b-modal
...@@ -125,7 +200,7 @@ ...@@ -125,7 +200,7 @@
no-close-on-esc no-close-on-esc
no-close-on-backdrop no-close-on-backdrop
> >
<div align="center"> <div>
<img <img
src="../../assets/radio.gif" src="../../assets/radio.gif"
alt="loading schedule data" alt="loading schedule data"
...@@ -151,11 +226,23 @@ export default { ...@@ -151,11 +226,23 @@ export default {
data () { data () {
return { return {
currentPage: 1,
perPage: 5,
timeslot: null, timeslot: null,
deletion: { deletion: {
amount: 0, amount: 0,
count: 0, count: 0,
} },
useSameTime: true,
onlyBusinessDays: false,
addNoOfDays: 1,
repetitionRule: 1,
repetitionOptions: [
{ value: 1, text: "On the following day" },
{ value: 2, text: "On the following business day" },
{ value: 3, text: "Number of days later" },
{ value: 4, text: "Custom (WIP)" },
]
} }
}, },
...@@ -176,12 +263,59 @@ export default { ...@@ -176,12 +263,59 @@ export default {
}, },
methods: { methods: {
trClass(item, type) {
if (!item || type !== 'row') {
return '';
}
return item.id === this.timeslot.id
? 'table-info'
: ''
},
createRepetitionSchedule() {
const { onlyBusinessDays, addNoOfDays } = this.getRepetitionParameters();
const { dstart, tstart, tend, rrule, until, fallback_id, automation_id, byweekday } = this.schedule;
const newSchedule = {
schedule: {
dstart,
tstart,
tend,
rrule,
until,
fallback_id,
automation_id,
byweekday,
is_repetition: true,
add_business_days_only: onlyBusinessDays,
add_days_no: addNoOfDays,
}
};
console.log(newSchedule);
this.$store.dispatch('shows/submitSchedule', {
showId: this.selectedShow.id,
schedule: newSchedule,
callback: (response) => {
if (response.data.projected === undefined) {
this.$parent.renderView(null)
} else {
this.$log.debug('Timeslot conflict. Switching to resolve mode.');
this.$parent.resolve(response.data)
}
this.$refs.modalEmissionManagerEdit.hide()
},
});
},
deleteFullSchedule (id) { deleteFullSchedule (id) {
this.$store.dispatch('shows/deleteSchedule', { this.$store.dispatch('shows/deleteSchedule', {
show: this.selectedShow.id, show: this.selectedShow.id,
schedule: id, schedule: id,
callback: () => { callback: () => {
this.$refs.modalEmissionManagerEdit.hide() this.$refs.modalEmissionManagerEdit.hide();
this.$parent.renderView(null) this.$parent.renderView(null)
} }
}) })
...@@ -257,6 +391,29 @@ export default { ...@@ -257,6 +391,29 @@ export default {
}) })
}, },
getRepetitionParameters() {
console.log(typeof this.repetitionRule);
if (this.repetitionRule == 1) {
return {
onlyBusinessDays: false,
addNoOfDays: 1,
}
}
if (this.repetitionRule == 2) {
return {
onlyBusinessDays: true,
addNoOfDays: 1,
}
}
return {
onlyBusinessDays: this.onlyBusinessDays,
addNoOfDays: this.addNoOfDays,
}
},
// initialise a new schedule and open the modal // initialise a new schedule and open the modal
open (timeslot) { open (timeslot) {
this.timeslot = timeslot this.timeslot = timeslot
......
...@@ -125,34 +125,58 @@ ...@@ -125,34 +125,58 @@
</b-col> </b-col>
</b-row> </b-row>
<!-- TODO: use b-form outside the b-form-input, so that
simple input validation works automagically -->
<b-modal <b-modal
ref="modalEmail" ref="modalEmail"
title="E-Mail" title="E-Mail"
size="lg" size="lg"
@ok="saveEmail" @ok="(modalEvt) => { modalEvt.preventDefault(); saveEmail() }"
> >
<b-form-input <form
v-model="string" ref="emailForm"
type="email" @submit.stop.prevent="saveEmail"
placeholder="Put a contact address of your show here" >
/> <b-form-group
:state="emailState"
label="Name"
label-for="show-email"
invalid-feedback="Please enter a valid email address"
>
<b-form-input
id="show-email"
v-model="email"
type="email"
:state="emailState"
placeholder="Put a contact address of your show here"
/>
</b-form-group>
</form>
</b-modal> </b-modal>
<!-- TODO: use b-form outside the b-form-input, so that
simple input validation works automagically -->
<b-modal <b-modal
ref="modalWebsite" ref="modalWebsite"
title="Website" title="Website"
size="lg" size="lg"
@ok="saveWebsite" @ok="(modalEvt) => { modalEvt.preventDefault(); saveWebsite() }"
> >
<b-form-input <form
v-model="string" ref="urlForm"
type="url" @submit.stop.prevent="saveWebsite"
placeholder="Put the website of your show here" >
/> <b-form-group
:state="urlState"
label="Name"
label-for="show-url"
invalid-feedback="Please enter a valid URL"
>
<b-form-input
id="show-url"
v-model="url"
type="url"
:state="urlState"
placeholder="Put the website of your show here"
/>
</b-form-group>
</form>
</b-modal> </b-modal>
<b-modal <b-modal
...@@ -243,7 +267,10 @@ export default { ...@@ -243,7 +267,10 @@ export default {
data () { data () {
return { return {
string: '', email: '',
url: '',
emailState: null,
urlState: null,
id: 0, id: 0,
} }
}, },
...@@ -332,14 +359,18 @@ export default { ...@@ -332,14 +359,18 @@ export default {
}, },
methods: { methods: {
openModalEmail () { openModalEmail () {
if (this.selectedShow.email !== null) { this.string = this.selectedShow.email } this.emailState = null;
else { this.string = '' }
if (this.selectedShow.email !== null) { this.email = this.selectedShow.email }
else { this.email = '' }
this.$refs.modalEmail.show() this.$refs.modalEmail.show()
}, },
openModalWebsite() { openModalWebsite() {
if (this.selectedShow.website !== null) { this.string = this.selectedShow.website } this.urlState = null;
else { this.string = '' }
if (this.selectedShow.website !== null) { this.url = this.selectedShow.website }
else { this.url = '' }
this.$refs.modalWebsite.show() this.$refs.modalWebsite.show()
}, },
...@@ -367,26 +398,48 @@ export default { ...@@ -367,26 +398,48 @@ export default {
this.$refs.modalType.show() this.$refs.modalType.show()
}, },
saveProperty (property, value, modal, event) { saveProperty (property, value, modal) {
if (value !== this.selectedShow[property]) { if (value !== this.selectedShow[property]) {
if (event) { event.preventDefault() }
this.$store.dispatch('shows/updateProperty', { this.$store.dispatch('shows/updateProperty', {
id: this.selectedShow.id, id: this.selectedShow.id,
property: property, property: property,
value: value, value: value,
callback: () => { callback: () => {
modal.hide() modal.hide()
},
callbackCancel: (error) => {
if (error.data.email) {
this.emailState = false;
}
if (error.data.website) {