Commit c75a659a authored by Andrea Ida Malkah Klaura's avatar Andrea Ida Malkah Klaura
Browse files

refactoring timeslot & note handling

parent 4d4d7f90
......@@ -43,8 +43,8 @@
</div>
<div v-else>
<!-- include the modals to edit show and timeslot entries from the modal compontents -->
<app-modalShow ref="appModalShow" v-bind:show="shows[currentShow]"></app-modalShow>
<app-modalNotes ref="appModalNotes" v-bind:show="shows[currentShow]"></app-modalNotes>
<app-modalShow ref="appModalShow" :show="shows[currentShow]"></app-modalShow>
<app-modalNotes ref="appModalNotes" :show="shows[currentShow]" :showAggregate="current"></app-modalNotes>
<!-- here we show our table of timeslots -->
<p>
......@@ -55,24 +55,6 @@
</div>
<div v-else style="text-align: center;"><img src="../assets/radio.gif" alt="loading data" /><br /></div>
<!-- This is the old old-school view for timeslots; remove when all functions are implmented in table above
<div v-if="loaded.timeslots">
<b-row>
<b-col>
<div v-for="timeslot in this.timeslotsFutureShow">
<img v-if="timeslot.playlist_id === null" src="../assets/16x16/go-top.png" alt="choose a playlist for this episode" v-on:click="notYetImplemented" />
<img v-else src="../assets/16x16/media-playback-start.png" alt="play" v-on:click="notYetImplemented" />
{{ prettyDateTime(timeslot.start) }} <small>(Duration: {{ prettyDuration(timeslot.start, timeslot.end) }})</small>
<span v-if="loaded.notes">{{ prettyTimeslotNote(timeslot.id) }}</span>
<span v-else style="background: ../assets/radio.gif"></span>
<img src="../assets/16x16/emblem-system.png" alt="edit note" v-on:click="editTimeslotNote(timeslot.id)" />
</div>
</b-col>
</b-row>
</div>
<div v-else style="text-align: center;"><img src="../assets/radio.gif" alt="loading data" /><br /></div>
-->
<hr />
<h2>Allgemeine Einstellungen zur Sendereihe:</h2>
......@@ -261,13 +243,6 @@ export default {
data () {
return {
shows: [], // an array of objects describing our shows (empty at load, will be populated on created())
timeslots: [], // same as with shows, only for the related timeslots
timeslotmeta: { // meta info when pagination is used
count: 0,
next: null,
previous: null
},
notes: [], // same as with shows, only for the related notes
currentShow: 0, // index of the currently selected show in our shows array
currentShowID: 0, // actual id of the currently selected show
numUpcoming: 8,
......@@ -295,7 +270,14 @@ export default {
musicfocus: [],
fundingcategory: [],
type: [],
note: {}
timeslots: [],
timeslotmeta: { // meta info when pagination is used
count: 0,
next: null,
previous: null
},
note: {},
notes: []
}
}
},
......@@ -303,30 +285,23 @@ export default {
computed: {
notesTableArray: function () {
var arr = []
for (var i in this.timeslots) {
for (var i in this.current.timeslots) {
var note = this.getNoteByTimeslotID(this.current.timeslots[i].id)
if (note !== null) note = note.title
arr.push({
title: this.getTimeslotNoteTitle(this.timeslots[i].id),
starts: this.prettyDateTime(this.timeslots[i].start),
duration: this.prettyDuration(this.timeslots[i].start, this.timeslots[i].end) + 'min',
title: note,
starts: this.prettyDateTime(this.current.timeslots[i].start),
duration: this.prettyDuration(this.current.timeslots[i].start, this.current.timeslots[i].end) + 'min',
// TODO: find out how to insert images or iconffont icons into b-table rows
// options: '<img src="../assets/16x16/emblem-system.png" alt="edit note" v-on:click="' + this.editTimeslotNote(this.timeslots[i].id) + '" />'
// options: '<img src="../assets/16x16/emblem-system.png" alt="edit note" v-on:click="' + this.editTimeslotNote(this.current.timeslots[i].id) + '" />'
options: '<span class="timeslotEditLink" onclick="' +
'document.getElementById(\'app\').children[1].__vue__.' +
'editTimeslotNote(' + this.timeslots[i].id + ', ' + this.timeslots[i].schedule + ')">edit</span> ' +
'editTimeslotNote(' + this.current.timeslots[i].id + ', ' + this.current.timeslots[i].schedule + ')">edit</span> ' +
'<span class="timeslotEditLink" onclick="alert(\'notYetImplemented\')">upload</span>' +
'<span class="timeslotEditLink" onclick="alert(\'notYetImplemented\')">...</span>'
})
}
return arr
},
timeslotsFutureShow: function () {
if (this.numUpcoming === 'all') return this.timeslotsFuture
else return this.timeslotsFuture.slice(0, this.numUpcoming)
},
timeslotsPastShow: function () {
// if (this.numRecent === 'all') return this.timeslotsPast
// else return this.timeslotsPast.slice(0, this.numRecent)
return this.timeslotsPast
}
},
methods: {
......@@ -370,33 +345,46 @@ export default {
// if we use the limit argument results are paginated and look different
// than without the limit argument
if (typeof limit === 'number') {
this.timeslots = response.data.results
this.timeslotmeta.count = response.data.count
this.timeslotmeta.next = response.data.next
this.timeslotmeta.previous = response.data.previous
this.current.timeslots = response.data.results
this.current.timeslotmeta.count = response.data.count
this.current.timeslotmeta.next = response.data.next
this.current.timeslotmeta.previous = response.data.previous
} else {
this.timeslots = response.data
this.timeslotmeta.count = response.data.length
this.timeslotmeta.next = null
this.timeslotmeta.previous = null
this.current.timeslots = response.data
this.current.timeslotmeta.count = response.data.length
this.current.timeslotmeta.next = null
this.current.timeslotmeta.previous = null
}
this.loaded.timeslots = true
// now that we have the timeslots we can fetch notes for all those timeslots
// TODO: curently we are fetching all notes for the show into a single array
// for bigger data sets it might be preferable to fetch only the notes for those
// timeslots that are also visible to the user
// TODO: discuss: when a timeslot can only have one not, why is the id
// of this note not stored in the timeslot? would be way more efficient
axios.get(process.env.API_STEERING_SHOWS + this.currentShowID + '/notes/', {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
}).then(response => {
this.notes = response.data
this.loaded.notes = true
}).catch(error => {
alert('There was an error fetching notes from the server' + error)
})
// done fetching notes
uri = process.env.API_STEERING_SHOWS + this.currentShowID + '/notes/?ids='
// add all note IDs from the timeslots that have existing notes
var thereIsANote = false
for (var i in this.current.timeslots) {
if (typeof this.current.timeslots[i].note_id === 'number') {
uri += this.current.timeslots[i].note_id + ','
thereIsANote = true
}
}
// now remove trailing ',' if at least one note already exists
// and make the api call to fetch them
if (thereIsANote) {
uri = uri.slice(0, -1)
axios.get(uri, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
}).then(response => {
this.current.notes = response.data
this.loaded.notes = true
}).catch(error => {
alert('There was an error fetching notes from the server' + error)
})
// done fetching notes
} else {
// if no notes exist that correspond to our selected timeslots, empty
// the corresponding array
this.current.notes = []
}
}).catch(error => {
alert('There was an error fetching timeslots from the server' + error)
})
......@@ -404,26 +392,18 @@ export default {
},
editTimeslotNote: function (timeslotID, scheduleID) {
this.current.note = null
for (var n in this.notes) {
if (this.notes[n].timeslot === timeslotID) {
this.current.note = this.notes[n]
for (var i in this.current.notes) {
if (this.current.notes[i].timeslot === timeslotID) {
this.current.note = this.current.notes[i]
break
}
}
this.$refs.appModalNotes.showModal(this.current.note, timeslotID, scheduleID)
},
getTimeslotNoteTitle: function (timeslotID) {
for (var n in this.notes) {
if (this.notes[n].timeslot === timeslotID && this.notes[n].title !== undefined) {
return this.notes[n].title
}
}
return null
},
getTimeslotNoteID: function (timeslotID) {
for (var n in this.notes) {
if (this.notes[n].timeslot === timeslotID) {
return this.notes[n].id
getNoteByTimeslotID: function (timeslotID) {
for (var i in this.current.notes) {
if (this.current.notes[i].timeslot === timeslotID && this.current.notes[i].title !== undefined) {
return this.current.notes[i]
}
}
return null
......@@ -589,7 +569,6 @@ export default {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
}).then(response => {
console.log(response.data[0])
this.shows = response.data
this.currentShowID = this.shows[0].id
this.currentShow = 0
......
......@@ -7,6 +7,8 @@
<b-row>
<b-col cols="2">Title:</b-col>
<b-col cols="10"><b-form-input v-model="title" type="text" placeholder="Enter a title"></b-form-input></b-col>
<b-col cols="2"></b-col>
<b-col cols="10"><small class="slug">Slug: {{ slug }}</small></b-col>
</b-row>
<br />
<b-row>
......@@ -18,6 +20,11 @@
<b-col cols="2">Content:</b-col>
<b-col cols="10"><b-form-textarea v-model="content" :rows="8" placeholder="Enter a text describing this timeslot"></b-form-textarea></b-col>
</b-row>
<br />
<b-row>
<b-col cols="2">Host:</b-col>
<b-col cols="10"><b-form-select v-model="host_selected" :options="hosts" class="mb-3" /></b-col>
</b-row>
</b-container>
</b-modal>
</div>
......@@ -27,16 +34,19 @@
import prettyDate from '../mixins/prettyDate'
import axios from 'axios'
/*
function debugErrorResponse (data) {
console.log('Response data provided to transformResponse:')
console.log(data)
// alert(data)
return data
}
*/
export default {
props: {
show: { type: Object, required: true }
show: { type: Object, required: true },
showAggregate: { type: Object, required: true }
},
data () {
return {
......@@ -48,41 +58,62 @@ export default {
content: '',
backuptitle: '',
backupsummary: '',
backupcontent: ''
backupcontent: '',
backuphost: null,
host_selected: null
}
},
computed: {
slug: function () {
return this.title.toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w-]+/g, '') // Remove all non-word chars
.replace(/--+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, '') // Trim - from end of text
},
hosts: function () {
// for the vue bootstrap select component we need an array of objects
// with a value, a text and optionally a disabled element
var hosts = []
for (var i in this.showAggregate.hosts) {
hosts.push({
value: this.showAggregate.hosts[i].id,
text: this.showAggregate.hosts[i].name,
disabled: !this.showAggregate.hosts[i].is_active
})
}
return hosts
}
},
mixins: [ prettyDate ],
methods: {
update (event) {
// only try to save if anything has changed
if (this.title !== this.note.title || this.summary !== this.note.summary || this.content !== this.note.content) {
if (this.title !== this.note.title || this.summary !== this.note.summary || this.content !== this.note.content || this.host_selected !== this.note.host) {
// prevent the modal from closing automatically on click
event.preventDefault()
// backup the note contents
this.backuptitle = this.note.title
this.backupsummary = this.note.summary
this.backupcontent = this.note.content
this.backuphost = this.note.host
// now set the new contents
this.note.title = this.title
this.note.summary = this.summary
this.note.content = this.content
this.note.host = this.host_selected
// generate the uri for the API call:
// /api/v1/shows/1/schedules/1/timeslots/1/note/1/
var uri = process.env.API_STEERING_SHOWS + this.show.id +
'/schedules/' + this.scheduleID +
'/timeslots/' + this.timeslotID +
'/note/' + this.note.id + '/'
console.log('trying to update a note')
console.log(uri)
console.log(this.note)
// now send the PUT request with our updated note
axios.put(uri, this.note, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$parent.$parent.user.access_token },
transformResponse: [debugErrorResponse]
headers: { 'Authorization': 'Bearer ' + this.$parent.$parent.user.access_token }
}).then(response => {
console.log('Response:')
console.log(response)
// everything was fine, we can close the modal now
this.$refs.modalNote.hide()
}).catch(error => {
......@@ -94,6 +125,7 @@ export default {
this.note.title = this.backuptitle
this.note.summary = this.backupsummary
this.note.content = this.backupcontent
this.note.host = this.backuphost
// and we leave the modal open, so no call to its .hide function here
})
// if nothing was changed, just close the modal
......@@ -101,18 +133,22 @@ export default {
this.$refs.modalNote.hide()
}
},
new () {
// only try to save if any data was filled in
if (this.title !== '' && this.summary !== '' && this.content !== '') {
new (event) {
// title and content are necessary
if (this.title.trim() === '' || this.content.trim() === '') {
event.preventDefault()
// TODO: make this nicer UI-wise (red text annotations next to input fields instead of simple alert)
alert('Please provide at least a title and some content.')
} else {
// prevent the modal from closing automatically on click
event.preventDefault()
// prepare the new note
this.note = {
show: this.show.id,
timeslot: this.timeslotID,
host: null, // TODO: implement
host: this.host_selected,
title: this.title,
slug: '', // TODO: implement
slug: this.slug,
summary: this.summary,
content: this.content,
ppoi: '(0.5,0.5)', // TODO: implement
......@@ -130,17 +166,19 @@ export default {
'/schedules/' + this.scheduleID +
'/timeslots/' + this.timeslotID +
'/note/'
console.log('trying to create a new note')
console.log(uri)
console.log(this.note)
// now send the POST request with our updated note
axios.post(uri, this.note, {
withCredentials: true,
headers: { 'Authorization': 'Bearer ' + this.$parent.$parent.user.access_token },
transformResponse: [debugErrorResponse]
responseType: 'json', // we need this explicitly here, as it does not seem to work automagically as in GET and PUT requests
headers: { 'Authorization': 'Bearer ' + this.$parent.$parent.user.access_token }
}).then(response => {
console.log('Response:')
console.log(response)
this.note = response.data
for (var i in this.showAggregate.timeslots) {
if (this.showAggregate.timeslots[i].id === this.timeslotID) {
this.showAggregate.timeslots[i].note_id = this.note.id
this.showAggregate.notes.push(this.note)
}
}
// everything was fine, we can close the modal now
this.$refs.modalNote.hide()
}).catch(error => {
......@@ -152,16 +190,16 @@ export default {
this.note.title = this.backuptitle
this.note.summary = this.backupsummary
this.note.content = this.backupcontent
// and we have to set this back to undefined so next time we edit it
// it will still be treated as a new note and not an existing one to update
this.note.start = undefined
// and we leave the modal open, so no call to its .hide function here
})
// if nothing was changed, just close the modal
} else {
this.$refs.modalNote.hide()
}
},
saveNote (event) {
if (typeof this.note.start === 'undefined') {
this.new()
this.new(event)
} else {
this.update(event)
}
......@@ -172,11 +210,15 @@ export default {
this.title = ''
this.summary = ''
this.content = ''
// TODO: integrate this into the user's app settings:
// should the field be empty by default or filled with the first host of the show?
this.host_selected = this.showAggregate.hosts[0].id
} else {
this.note = note
this.title = this.note.title
this.summary = this.note.summary
this.content = this.note.content
this.host_selected = this.note.host
}
// console.log(this.$refs.modalNote)
this.timeslotID = timeslotID
......@@ -188,5 +230,7 @@ export default {
</script>
<style scoped>
.slug {
color: gray;
}
</style>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment