Commit d2fbd7bc authored by jackie / Andrea Ida Malkah Klaura's avatar jackie / Andrea Ida Malkah Klaura
Browse files

META: improve ShowManager structure & comments

parent 09efcd58
<template> <template>
<b-container> <b-container>
<!-- This first row is so far only used to provide a dropdown for
choosing one of the loaded shows (which the user has access to) -->
<b-row> <b-row>
<b-col>
<h3>Sendungen verwalten</h3>
</b-col>
<b-col align="right"> <b-col align="right">
<b-dropdown <b-dropdown
id="ddshows" id="ddshows"
...@@ -19,7 +24,10 @@ ...@@ -19,7 +24,10 @@
</b-row> </b-row>
<hr> <hr>
<!-- The jumbotron is used to display the name and description of the
currently selected show -->
<b-jumbotron> <b-jumbotron>
<!-- The show title goes into the jumbotron header -->
<template slot="header"> <template slot="header">
<span v-if="loaded.shows"> <span v-if="loaded.shows">
{{ shows[currentShow].name }} {{ shows[currentShow].name }}
...@@ -31,6 +39,7 @@ ...@@ -31,6 +39,7 @@
</span> </span>
<span v-else>Shows are being loaded</span> <span v-else>Shows are being loaded</span>
</template> </template>
<!-- The short description of the show goes into the jumbotron lead -->
<template slot="lead"> <template slot="lead">
<span v-if="loaded.shows">{{ shows[currentShow].short_description }}</span> <span v-if="loaded.shows">{{ shows[currentShow].short_description }}</span>
<img <img
...@@ -39,6 +48,7 @@ ...@@ -39,6 +48,7 @@
@click="$refs.appModalShow.showShortDescription()" @click="$refs.appModalShow.showShortDescription()"
> >
</template> </template>
<!-- The rest of the jumbotron is filled with the show description -->
<div v-if="loaded.shows"> <div v-if="loaded.shows">
<b>Description:</b> <img <b>Description:</b> <img
src="../assets/16x16/emblem-system.png" src="../assets/16x16/emblem-system.png"
...@@ -58,6 +68,7 @@ ...@@ -58,6 +68,7 @@
</div> </div>
</b-jumbotron> </b-jumbotron>
<!-- If the shows are not fully loaded yet, we just put the loading sign -->
<div v-if="!loaded.shows"> <div v-if="!loaded.shows">
<b-row> <b-row>
<b-col align="center"> <b-col align="center">
...@@ -68,6 +79,8 @@ ...@@ -68,6 +79,8 @@
</b-col> </b-col>
</b-row> </b-row>
</div> </div>
<!-- When all show data is loaded, here we display all the rest -->
<div v-else> <div v-else>
<!-- include the modals to edit show and timeslot entries from the modal compontents --> <!-- include the modals to edit show and timeslot entries from the modal compontents -->
<app-modalShow <app-modalShow
...@@ -80,13 +93,14 @@ ...@@ -80,13 +93,14 @@
:show-aggregate="current" :show-aggregate="current"
/> />
<!-- here we show our table of timeslots --> <!-- here are the filter settings for our timeslots table -->
<b-card> <b-card>
<b-btn v-b-toggle.timeslotFilterCollapse> <b-btn v-b-toggle.timeslotFilterCollapse>
Toggle timeslot filters Toggle timeslot filters
</b-btn> </b-btn>
<b-collapse id="timeslotFilterCollapse"> <b-collapse id="timeslotFilterCollapse">
<br> <br>
<!-- How many slots to show per table page -->
<b-row> <b-row>
<b-col sm="3"> <b-col sm="3">
<label for="inputNumSlots">Number of slots to show:</label> <label for="inputNumSlots">Number of slots to show:</label>
...@@ -99,6 +113,7 @@ ...@@ -99,6 +113,7 @@
/> />
</b-col> </b-col>
</b-row> </b-row>
<!-- The start date to display timeslots from (defaults to today) -->
<b-row> <b-row>
<b-col sm="3"> <b-col sm="3">
<label for="inputDateStart">From:</label> <label for="inputDateStart">From:</label>
...@@ -111,6 +126,7 @@ ...@@ -111,6 +126,7 @@
/> />
</b-col> </b-col>
</b-row> </b-row>
<!-- The end date until to wich to display timeslots -->
<b-row> <b-row>
<b-col sm="3"> <b-col sm="3">
<label for="inputNumSlots">Until (exclusive):</label> <label for="inputNumSlots">Until (exclusive):</label>
...@@ -124,6 +140,7 @@ ...@@ -124,6 +140,7 @@
</b-col> </b-col>
</b-row> </b-row>
<br> <br>
<!-- And finally two buttons, one to reset and one to apply the filter -->
<b-container <b-container
fluid fluid
class="text-right" class="text-right"
...@@ -146,6 +163,8 @@ ...@@ -146,6 +163,8 @@
<br> <br>
<!-- here we show our table of timeslots, if the timeslots are already
loaded (otherwise we just show the loading symbol) -->
<div v-if="loaded.timeslots"> <div v-if="loaded.timeslots">
<b-table <b-table
striped striped
...@@ -154,6 +173,7 @@ ...@@ -154,6 +173,7 @@
:fields="notesTableArrayFields" :fields="notesTableArrayFields"
:items="notesTableArray" :items="notesTableArray"
> >
<!-- Title of the timeslot (if already set) -->
<template <template
slot="title" slot="title"
slot-scope="data" slot-scope="data"
...@@ -161,18 +181,22 @@ ...@@ -161,18 +181,22 @@
<span v-if="data.value">{{ data.value }}</span> <span v-if="data.value">{{ data.value }}</span>
<span v-else><small><i>(none set)</i></small></span> <span v-else><small><i>(none set)</i></small></span>
</template> </template>
<!-- Date and time when this timeslot starts -->
<template <template
slot="starts" slot="starts"
slot-scope="data" slot-scope="data"
> >
{{ data.value }} {{ data.value }}
</template> </template>
<!-- The duration of this timeslot -->
<template <template
slot="duration" slot="duration"
slot-scope="data" slot-scope="data"
> >
{{ data.value }} {{ data.value }}
</template> </template>
<!-- And here all the buttons for editing and doing other things
with the displayed timeslot -->
<template <template
slot="options" slot="options"
slot-scope="data" slot-scope="data"
...@@ -215,14 +239,15 @@ ...@@ -215,14 +239,15 @@
@change="timeslotsPage" @change="timeslotsPage"
/> />
</div> </div>
<div <!-- If the timeslot data is not loaded, we just show the spinner instead
v-else of the table itself -->
style="text-align: center;" <div v-else>
> <div style="text-align: center;">
<img <img
src="../assets/radio.gif" src="../assets/radio.gif"
alt="loading data" alt="loading data"
><br> ><br>
</div>
</div> </div>
<hr> <hr>
...@@ -590,11 +615,18 @@ import axios from 'axios' ...@@ -590,11 +615,18 @@ import axios from 'axios'
import DOMPurify from 'dompurify' import DOMPurify from 'dompurify'
export default { export default {
// all modals to edit a show and its timeslots/notes, are importet as separate
// components, to make it a tiny lickle bit less messy here
components: { components: {
'app-modalNotes': modalNotes, 'app-modalNotes': modalNotes,
'app-modalShow': modalShow 'app-modalShow': modalShow
}, },
// generic functions that we want to use from our mixins folder
mixins: [ timeslotSort, prettyDate ], mixins: [ timeslotSort, prettyDate ],
// this component will be handling a lot of data - probably the component can
// be refactored to get rid of some redundancy here
data () { data () {
return { return {
shows: [], // an array of objects describing our shows (empty at load, will be populated on created()) shows: [], // an array of objects describing our shows (empty at load, will be populated on created())
...@@ -603,6 +635,9 @@ export default { ...@@ -603,6 +635,9 @@ export default {
numSlots: process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS, // all form input values are provided as strings numSlots: process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS, // all form input values are provided as strings
dateStart: this.apiDate(new Date()), dateStart: this.apiDate(new Date()),
dateEnd: this.apiDate(new Date(new Date().getTime() + process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_DAYS * 86400000)), dateEnd: this.apiDate(new Date(new Date().getTime() + process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_DAYS * 86400000)),
// the loaded object holds flags for the different things we will fetch
// from the AuRa steering module
loaded: { loaded: {
shows: false, shows: false,
timeslots: false, timeslots: false,
...@@ -615,6 +650,9 @@ export default { ...@@ -615,6 +650,9 @@ export default {
fundingcategory: false, fundingcategory: false,
type: false type: false
}, },
// the current object is used to hold all the necessary data to describe
// the show which is currently selected by the user in the frontend
current: { current: {
categories: [], categories: [],
hosts: [], hosts: [],
...@@ -636,6 +674,8 @@ export default { ...@@ -636,6 +674,8 @@ export default {
image: '', image: '',
logo: '' logo: ''
}, },
// this is used to configure the table with all the filtered timeslots
notesTableArrayFields: [ notesTableArrayFields: [
{ key: 'title', label: 'Title of emission' }, { key: 'title', label: 'Title of emission' },
{ key: 'starts', label: 'Emission start' }, { key: 'starts', label: 'Emission start' },
...@@ -644,11 +684,26 @@ export default { ...@@ -644,11 +684,26 @@ export default {
] ]
} }
}, },
// Some of the info we need in the tempalte are not easily and directly
// retrievable, so we are computing them on the fly, when they are needed
computed: { computed: {
// As the show description should allow to be html-formatted, we have to
// make sure no malicous code can be inserted into the DOM. For that the
// DOMPurify library (https://github.com/cure53/DOMPurify) does us a much
// better service than trying to sanitize it with some RegExp.
sanitizedShowDescription: function () { sanitizedShowDescription: function () {
//return this.shows[this.currentShow].description.replace(/<[^>]*>/g, '')
return DOMPurify.sanitize(this.shows[this.currentShow].description) return DOMPurify.sanitize(this.shows[this.currentShow].description)
}, },
// In order to not only just show the predecessor of a show as an ID. we
// have to find it in our shows array to then output the predecessors name.
// This currently assumes that a user has access to all the predecessors
// of the shows as well.
// TODO/discuss: if all predecessor names should be accessible, independent
// of access rights, then we would need to load all predecessors show after
// loading our initial shows as well.
predecessorName: function () { predecessorName: function () {
for (var i in this.shows) { for (var i in this.shows) {
if (this.shows[i].id === this.shows[this.currentShow].predecessor) { if (this.shows[i].id === this.shows[this.currentShow].predecessor) {
...@@ -657,6 +712,9 @@ export default { ...@@ -657,6 +712,9 @@ export default {
} }
return 'Name of predecessor show not available' return 'Name of predecessor show not available'
}, },
// As we do not have a single object which holds all info we need to display
// in the table with our timeslots, we use this computed array to do that
notesTableArray: function () { notesTableArray: function () {
var arr = [] var arr = []
for (var i in this.current.timeslots) { for (var i in this.current.timeslots) {
...@@ -675,6 +733,9 @@ export default { ...@@ -675,6 +733,9 @@ export default {
return arr return arr
} }
}, },
// Right after this component is set up, we want to fetch all available shows
// from the AuRa steering module.
created () { created () {
var uri = process.env.VUE_APP_API_STEERING_SHOWS var uri = process.env.VUE_APP_API_STEERING_SHOWS
if (!this.$parent.user.steeringUser.is_superuser) { if (!this.$parent.user.steeringUser.is_superuser) {
...@@ -697,11 +758,18 @@ export default { ...@@ -697,11 +758,18 @@ export default {
alert('There was an error fetching shows from the server: ' + error) alert('There was an error fetching shows from the server: ' + error)
}) })
}, },
// Now for our hotchpotch of methods, mostly used for fetching data from the
// AuRa steering API (updateing will be done in the imported modal components)
methods: { methods: {
// Apply the newly set filter parameters for our timeslot table
applyFilter: function () { applyFilter: function () {
this.current.timeslotmeta.page = 1 this.current.timeslotmeta.page = 1
this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots) this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots)
}, },
// Reset the filter parameters for our timeslot table to config defaults
resetFilter: function () { resetFilter: function () {
this.numSlots = process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS this.numSlots = process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS
this.dateStart = this.apiDate(new Date()) this.dateStart = this.apiDate(new Date())
...@@ -709,12 +777,22 @@ export default { ...@@ -709,12 +777,22 @@ export default {
this.current.timeslotmeta.page = 1 this.current.timeslotmeta.page = 1
this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots) this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots)
}, },
// Load a different page of timeslots for the timeslots table
timeslotsPage: function (page) { timeslotsPage: function (page) {
if (this.current.timeslotmeta.page !== page) { if (this.current.timeslotmeta.page !== page) {
this.current.timeslotmeta.page = page this.current.timeslotmeta.page = page
this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots, (page - 1) * this.numSlots) this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots, (page - 1) * this.numSlots)
} }
}, },
// Every time the user switches to another show, we will load all related
// data (as e.g. categories, hosts, etc.) from the AuRa steering API, in case
// something changed.
// TODO/discuss: maybe this is too inefficient and we could only load this
// data just in the beginning after shows are loaded and assume they do not
// change throughout a session or put it into the user's responsibility to
// reload the show manager page after relevant changes.
switchShow: function (index) { switchShow: function (index) {
// if we already had some show loaded with timeslots and notes, set these to // if we already had some show loaded with timeslots and notes, set these to
// not loaded, so we don't display old timeslots and notes while already // not loaded, so we don't display old timeslots and notes while already
...@@ -748,6 +826,8 @@ export default { ...@@ -748,6 +826,8 @@ export default {
// now fetch the timeslots (including notes) for a given show from PV backend // now fetch the timeslots (including notes) for a given show from PV backend
this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots) this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots)
}, },
// Fetch timeslots for the current show and use filter variables if provided
getTimeslots: function (start, end, limit, offset) { getTimeslots: function (start, end, limit, offset) {
var dateRegex = new RegExp('^\\d{4}-\\d{2}-\\d{2}$') var dateRegex = new RegExp('^\\d{4}-\\d{2}-\\d{2}$')
var uri = process.env.VUE_APP_API_STEERING_SHOWS + this.currentShowID + '/timeslots/?' var uri = process.env.VUE_APP_API_STEERING_SHOWS + this.currentShowID + '/timeslots/?'
...@@ -812,6 +892,8 @@ export default { ...@@ -812,6 +892,8 @@ export default {
}) })
// done fetching timeslots // done fetching timeslots
}, },
// Open the modal to edit a timeslot's note, given its ID and schedule ID
editTimeslotNote: function (timeslotID, scheduleID) { editTimeslotNote: function (timeslotID, scheduleID) {
this.current.note = null this.current.note = null
for (var i in this.current.notes) { for (var i in this.current.notes) {
...@@ -822,6 +904,8 @@ export default { ...@@ -822,6 +904,8 @@ export default {
} }
this.$refs.appModalNotes.showModal(this.current.note, timeslotID, scheduleID) this.$refs.appModalNotes.showModal(this.current.note, timeslotID, scheduleID)
}, },
// For a given timeslot ID return the corresponding note, if there is one
getNoteByTimeslotID: function (timeslotID) { getNoteByTimeslotID: function (timeslotID) {
for (var i in this.current.notes) { for (var i in this.current.notes) {
if (this.current.notes[i].timeslot === timeslotID && this.current.notes[i].title !== undefined) { if (this.current.notes[i].timeslot === timeslotID && this.current.notes[i].title !== undefined) {
...@@ -830,6 +914,8 @@ export default { ...@@ -830,6 +914,8 @@ export default {
} }
return null return null
}, },
// For a given timeslot ID, check if there is a note and return it
prettyTimeslotNote: function (timeslotID) { prettyTimeslotNote: function (timeslotID) {
var note = this.getTimeslotNoteTitle(timeslotID) var note = this.getTimeslotNoteTitle(timeslotID)
if (note !== null) { if (note !== null) {
...@@ -838,14 +924,17 @@ export default { ...@@ -838,14 +924,17 @@ export default {
return '' return ''
} }
}, },
// Limiting display of strings up to 25 characters plus "..."
prettyTitle: function (title) { prettyTitle: function (title) {
if (title === '') { return '...' } if (title === '') { return '...' }
else if (title.length > 25) { return title.slice(0, 25) + '...' } else if (title.length > 25) { return title.slice(0, 25) + '...' }
else { return title } else { return title }
}, },
// TODO: all thos getSomething functions could be probably merged into one
// generic getItem function. Maybe this.current should be an associative array // TODO: all those getSomething functions could be probably merged into one
// instead of an object then? // generic getItem function. Maybe this.current should be implemented
// in a dictionary kind of way then?
getCategories: function () { getCategories: function () {
this.current.categories = [] this.current.categories = []
var loadingError = false var loadingError = false
...@@ -866,6 +955,7 @@ export default { ...@@ -866,6 +955,7 @@ export default {
if (!loadingError) { this.loaded.categories = true } if (!loadingError) { this.loaded.categories = true }
} }
}, },
getHosts: function () { getHosts: function () {
this.current.hosts = [] this.current.hosts = []
var loadingError = false var loadingError = false
...@@ -886,6 +976,7 @@ export default { ...@@ -886,6 +976,7 @@ export default {
if (!loadingError) { this.loaded.hosts = true } if (!loadingError) { this.loaded.hosts = true }
} }
}, },
getLanguages: function () { getLanguages: function () {
this.current.languages = [] this.current.languages = []
var loadingError = false var loadingError = false
...@@ -906,6 +997,7 @@ export default { ...@@ -906,6 +997,7 @@ export default {
if (!loadingError) { this.loaded.languages = true } if (!loadingError) { this.loaded.languages = true }
} }
}, },
getTopics: function () { getTopics: function () {
this.current.topics = [] this.current.topics = []
var loadingError = false var loadingError = false
...@@ -926,6 +1018,7 @@ export default { ...@@ -926,6 +1018,7 @@ export default {
if (!loadingError) { this.loaded.topics = true } if (!loadingError) { this.loaded.topics = true }
} }
}, },
getMusicfocus: function () { getMusicfocus: function () {
this.current.musicfocus = [] this.current.musicfocus = []
var loadingError = false var loadingError = false
...@@ -946,6 +1039,7 @@ export default { ...@@ -946,6 +1039,7 @@ export default {
} }
if (!loadingError) { this.loaded.musicfocus = true } if (!loadingError) { this.loaded.musicfocus = true }
}, },
getFundingCategory: function () { getFundingCategory: function () {
this.current.fundingcategory = [] this.current.fundingcategory = []
var loadingError = false var loadingError = false
...@@ -964,6 +1058,7 @@ export default { ...@@ -964,6 +1058,7 @@ export default {
} }
if (!loadingError) { this.loaded.fundingcategory = true } if (!loadingError) { this.loaded.fundingcategory = true }
}, },
getType: function () { getType: function () {
this.current.type = [] this.current.type = []
var loadingError = false var loadingError = false
...@@ -982,6 +1077,9 @@ export default { ...@@ -982,6 +1077,9 @@ export default {
} }
if (!loadingError) { this.loaded.type = true } if (!loadingError) { this.loaded.type = true }
}, },
// Just a placeholder function we can use in the UI, to signal if something
// is not yet implemented
notYetImplemented: function () { notYetImplemented: function () {
alert('By the mighty witchcraftry of the mother of time!\n\nThis feature is not implemented yet.') alert('By the mighty witchcraftry of the mother of time!\n\nThis feature is not implemented yet.')
} }
......
Markdown is supported
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