ShowManager.vue 21.9 KB
Newer Older
1
<template>
2
  <b-container>
3
4
5
6
7
8
9
10
11
12
13
    <b-row>
      <b-col align="right">
        <b-dropdown id="ddshows" text="Sendereihe auswählen" variant="info">
          <b-dropdown-item v-for="(show, index) in this.shows" :key="show.id" v-on:click="switchShow(index)">{{ show.name }}</b-dropdown-item>
        </b-dropdown>
      </b-col>
    </b-row>
    <hr />

    <b-jumbotron>
      <template slot="header">
14
15
16
17
        <span v-if="loaded.shows">
          {{ shows[currentShow].name }}
          <img src="../assets/16x16/emblem-system.png" alt="edit name of show" v-on:click="$refs.appModalShow.$refs.modalShowName.show()" />
        </span>
18
19
20
        <span v-else>Shows are being loaded</span>
      </template>
      <template slot="lead">
21
        <span v-if="loaded.shows">{{ shows[currentShow].short_description }}</span>
22
        <img src="../assets/16x16/emblem-system.png" alt="edit short description" v-on:click="$refs.appModalShow.$refs.modalShowShortDescription.show()" />
23
      </template>
24
      <p v-if="loaded.shows">
25
        <b>Description:</b> <img src="../assets/16x16/emblem-system.png" alt="edit description" v-on:click="$refs.appModalShow.$refs.modalShowDescription.show()" />
26
        <div v-if="loaded.shows">
27
          <!-- TODO: see if we can make a nice but secure html rendering of the description -->
28
29
          <!--{{ shows[currentShow].description.replace(/<[^>]*>/g, '') }}-->
          {{ shows[currentShow].description }}
30
31
32
33
34
          <!-- TODO: add image and logo here? -->
        </div>
      </p>
    </b-jumbotron>

35
    <div v-if="!loaded.shows">
36
37
38
      <b-row>
        <b-col align="center">
          <img src="../assets/radio.gif" alt="loading data" />
39
40
        </b-col>
      </b-row>
41
42
    </div>
    <div v-else>
43
      <app-modalShow ref="appModalShow" v-bind:show="shows[currentShow]"></app-modalShow>
44
45
      <app-modalNotes ref="appModalNotes" v-bind:note="current.note"></app-modalNotes>

46
      <p align="left">Die nächsten <select v-model="numUpcoming">
47
48
49
50
        <option>8</option>
        <option>16</option>
        <option value="all">alle</option>
      </select> Sendungen:</p>
51
      <div v-if="loaded.timeslots">
52
53
        <b-row>
          <b-col>
54
            <div v-for="timeslot in this.timeslotsFutureShow">
55
56
              <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" />
57
              {{ prettyDate(timeslot.start) }} ({{ prettyDuration(timeslot.start, timeslot.end) }})
58
              <span v-if="loaded.notes">{{ prettyTimeslotNote(timeslot.id) }}</span>
59
              <span v-else style="background: ../assets/radio.gif"></span>
60
              <img src="../assets/16x16/emblem-system.png" alt="edit note" v-on:click="editNote(timeslot.id)" />
61
            </div>
62
63
          </b-col>
        </b-row>
64
      </div>
65
      <div v-else style="text-align: center;"><img src="../assets/radio.gif" alt="loading data" /><br /></div>
66

67
      <div class="recenttimeslots">
68
69
70
71
72
        <p>Die letzten <select v-model="numRecent">
          <option>8</option>
          <option>16</option>
          <option value="all">alle</option>
        </select> Sendungen:</p>
73
        <div v-if="loaded.timeslots">
74
75
          <b-row>
            <b-col>
76
              <div v-for="timeslot in this.timeslotsPastShow">
77
78
                <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" />
79
                {{ prettyDate(timeslot.start) }} ({{ prettyDuration(timeslot.start, timeslot.end) }})
80
                <span v-if="loaded.notes">{{ prettyTimeslotNote(timeslot.id) }}</span>
81
                <span v-else style="background: ../assets/radio.gif"></span>
82
                <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
83
              </div>
84
85
            </b-col>
          </b-row>
86
87
88
        </div>
        <div v-else style="text-align: center;"><img src="../assets/radio.gif" alt="loading data" /><br /></div>
      </div>
89

90
91
      <hr />

92
      <div v-if="loaded.shows" class="showsettings">
93
        <h2>Allgemeine Einstellungen zur Sendereihe:</h2>
94
        <b-row>
95
96

          <b-col lg="6">
97
            <p>
98
              <b-badge variant="light">E-Mail:</b-badge>
99
100
101
102
103
              <span v-if="shows[currentShow].email === null"><small><i>(none set)</i></small></span>
              <span v-else>{{ shows[currentShow].email }}</span>
              <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
            </p>
          </b-col>
104
105

          <b-col lg="6">
106
            <p>
107
              <b-badge variant="light">Website:</b-badge>
108
109
110
111
112
              <span v-if="shows[currentShow].website === null"><small><i>(none set)</i></small></span>
              <span v-else><a :href="shows[currentShow].website">{{ shows[currentShow].website }}</a></span>
              <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
            </p>
          </b-col>
113
114

          <b-col lg="6">
115
            <p>
116
              <b-badge variant="light">Show type:</b-badge>
117
              <!-- TODO: discuss: should this be visible to show owners or only to administrators? -->
118
119
120
              <span v-if="loaded.type">
                <span v-if="current.type.length === 0"><small><i>(none set)</i></small></span>
                <span v-else>{{ current.type[0].type }}</span>
121
                <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
122
123
              </span>
              <span v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /></span>
124
125
            </p>
          </b-col>
126
127

          <b-col lg="6">
128
            <p>
129
              <b-badge variant="light">Funding category (eg. for RTR):</b-badge>
130
              <!-- TODO: discuss: should this be visible to show owners or only to administrators? -->
131
132
133
              <span v-if="loaded.rtrcategory">
                <span v-if="current.rtrcategory.length === 0"><small><i>(none set)</i></small></span>
                <span v-else>{{ current.rtrcategory[0].rtrcategory }}</span>
134
                <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
135
136
              </span>
              <span v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /></span>
137
138
            </p>
          </b-col>
139
140

          <b-col lg="6">
141
142
143
            <p>
              <!-- TODO: discuss: should this be visible to show owners or only to administrators? -->
              <!-- TODO: fetch name for predecessor_id from steering api -->
144
              <b-badge variant="light">Predecessor:</b-badge>
145
146
147
148
149
              <span v-if="shows[currentShow].predecessor_id === null"><small><i>This show has no predecessor show.</i></small></span>
              <span v-else>{{ shows[currentShow].predecessor_id }}</span>
              <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
            </p>
          </b-col>
150
151

          <b-col lg="6">
152
            <p>
153
              <b-badge variant="light">CBA Series ID:</b-badge>
154
155
156
157
158
              <span v-if="shows[currentShow].cba_series_id === null"><small><i>(none set)</i></small></span>
              <span v-else>{{ shows[currentShow].cba_series_id }}</span>
              <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
            </p>
          </b-col>
159
160

          <b-col lg="6">
161
            <p>
162
              <b-badge variant="light">Fallback List/Pool:</b-badge>
163
164
              <span v-if="shows[currentShow].fallback_id === ''"><small><i>(none set)</i></small></span>
              <span v-else>ID: {{ shows[currentShow].fallback_id }}</span>
165
166
167
              <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
            </p>
          </b-col>
168
169
170
171
172
173

        </b-row>

        <b-row>

          <b-col lg="2">
174
            <b-badge style="width:80%;">Categories:</b-badge> <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
175
          </b-col>
176
177
178
179
180
181
182
183
184
185
186
187
          <b-col lg="4">
            <div v-if="loaded.categories">
              <p v-if="shows[currentShow].category.length === 0">
                <small><i>(none set)</i></small>
              </p>
              <p v-else>
                <ul>
                  <li v-for="cat in current.categories">{{ cat.category }}</li>
                </ul>
              </p>
            </div>
            <div v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /><br /></div>
188
          </b-col>
189
190

          <b-col lg="2">
191
            <b-badge style="width:80%;">Topics:</b-badge> <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
192
          </b-col>
193
          <b-col lg="4">
194
195
            <div v-if="loaded.topics">
              <p v-if="shows[currentShow].topic.length === 0">
196
197
198
199
                <small><i>(none set)</i></small>
              </p>
              <p v-else>
                <ul>
200
                  <li v-for="topic in current.topics">{{ topic.topic }}</li>
201
202
203
204
                </ul>
              </p>
            </div>
            <div v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /><br /></div>
205
          </b-col>
206
207

          <b-col lg="2">
208
            <b-badge style="width:80%;">Musicfocus:</b-badge> <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
209
210
          </b-col>
          <b-col lg="4">
211
212
            <div v-if="loaded.musicfocus">
              <p v-if="shows[currentShow].musicfocus.length === 0">
213
214
215
216
                <small><i>(none set)</i></small>
              </p>
              <p v-else>
                <ul>
217
                  <li v-for="focus in current.musicfocus">{{ focus.focus }}</li>
218
219
220
221
                </ul>
              </p>
            </div>
            <div v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /><br /></div>
222
          </b-col>
223
224

          <b-col lg="2">
225
            <b-badge style="width:80%;">Languages:</b-badge> <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
226
227
          </b-col>
          <b-col lg="4">
228
229
            <div v-if="loaded.languages">
              <p v-if="shows[currentShow].language.length === 0">
230
231
232
233
                <small><i>(none set)</i></small>
              </p>
              <p v-else>
                <ul>
234
                  <li v-for="lang in current.languages">{{ lang.name }}</li>
235
236
237
238
239
240
241
                </ul>
              </p>
            </div>
            <div v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /><br /></div>
          </b-col>

          <b-col lg="2">
242
            <b-badge style="width:80%;">Hosts:</b-badge> <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
          </b-col>
          <b-col lg="4">
            <div v-if="loaded.hosts">
              <p v-if="shows[currentShow].hosts.length === 0">
                <small><i>(none set)</i></small>
              </p>
              <p v-else>
                <!-- TODO: make link on name; when user clicks, open modal to edit host -->
                <ul>
                  <li v-for="host in current.hosts">{{ host.name }}</li>
                </ul>
              </p>
            </div>
            <div v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /><br /></div>
          </b-col>

259
        </b-row>
260
      </div>
261
    </div>
262
  </b-container>
263
264
265
</template>

<script>
266
import modalNotes from './ShowManagerModalNotes.vue'
267
import modalShow from './ShowManagerModalShow.vue'
268
import timeslotSort from '../mixins/timeslotSort'
269
import prettyDate from '../mixins/prettyDate'
270
import axios from 'axios'
271
272

export default {
273
  components: {
274
275
    'app-modalNotes': modalNotes,
    'app-modalShow': modalShow
276
  },
277
278
  data () {
    return {
279
280
281
282
283
      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
      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
284
      numUpcoming: 8,
285
286
287
288
289
290
291
292
293
294
      numRecent: 8,
      loaded: {
        shows: false,
        timeslots: false,
        notes: false,
        categories: false,
        hosts: false,
        languages: false,
        topics: false,
        musicfocus: false,
295
296
        rtrcategory: false,
        type: false
297
298
299
300
301
302
303
      },
      current: {
        categories: [],
        hosts: [],
        languages: [],
        topics: [],
        musicfocus: [],
304
        rtrcategory: [],
305
306
        type: [],
        note: {}
307
      }
308
309
    }
  },
310
  mixins: [ timeslotSort, prettyDate ],
311
  computed: {
312
313
314
    timeslotsFutureShow: function () {
      if (this.numUpcoming === 'all') return this.timeslotsFuture
      else return this.timeslotsFuture.slice(0, this.numUpcoming)
315
    },
316
317
318
319
    timeslotsPastShow: function () {
      // if (this.numRecent === 'all') return this.timeslotsPast
      // else return this.timeslotsPast.slice(0, this.numRecent)
      return this.timeslotsPast
320
321
322
    }
  },
  methods: {
323
    switchShow: function (index) {
324
325
326
      // 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
      // the new show is displayed and new timeslots and notes are still loading
327
328
329
330
331
332
333
334
335
      this.loaded.timeslots = false
      this.loaded.notes = false
      // also for those settings of the show which are only ids or arrays of ids
      // we have to fetch the corresponding names first
      this.loaded.categories = false
      this.loaded.hosts = false
      this.loaded.languages = false
      this.loaded.musicfocus = false
      this.loaded.rtrcategory = false
336
      // set the current show and its ID to whatever we want to switch to now
337
      this.currentShow = index
338
      this.currentShowID = this.shows[this.currentShow].id
339
340
341
342
343
344
345
      // before we load timeslots and notes, we want to fetch the general settings first
      this.getCategories()
      this.getHosts()
      this.getLanguages()
      this.getTopics()
      this.getMusicfocus()
      this.getRTRCategory()
346
      this.getType()
347
      // now fetch the single timeslots for a given show from PV backend
348
      axios.get(process.env.API_STEERING_SHOWS + this.currentShowID + '/timeslots/', {withCredentials: true}).then(response => {
349
        this.timeslots = response.data
350
        this.loaded.timeslots = true
351
352
353
354
        // 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
355
        axios.get(process.env.API_STEERING_SHOWS + this.currentShowID + '/notes/', {withCredentials: true}).then(response => {
356
357
          this.notes = response.data
          this.loaded.notes = true
358
359
360
361
362
363
        }).catch(error => {
          alert('There was an error fetching notes from the server' + error)
        })
        // done fetching notes
      }).catch(error => {
        alert('There was an error fetching timeslots from the server' + error)
364
      })
365
366
      // done fetching timeslots
    },
367
368
369
370
371
372
373
374
375
    editNote: function (timeslotID) {
      for (var n in this.notes) {
        if (this.notes[n].timeslot === timeslotID) {
          this.current.note = this.notes[n]
          break
        }
      }
      this.$refs.appModalNotes.$refs.modalNote.show()
    },
376
377
378
379
380
381
382
    getTimeslotNote: 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
383
    },
384
385
386
387
388
389
390
391
    prettyTimeslotNote: function (timeslotID) {
      var note = this.getTimeslotNote(timeslotID)
      if (note !== null) {
        return this.prettyTitle(note)
      } else {
        return ''
      }
    },
392
393
394
395
396
    prettyTitle: function (title) {
      if (title === '') return '...'
      else if (title.length > 25) return title.slice(0, 25) + '...'
      else return title
    },
397
398
399
400
401
402
403
404
405
406
    // TODO: all thos getSomething functions could be probably merged into one
    // generic getItem function. Maybe this.current should be an associative array
    // instead of an object then?
    getCategories: function () {
      this.current.categories = []
      var loadingError = false
      if (this.shows[this.currentShow].category.length === 0) {
        this.loaded.categories = true
      } else {
        for (var i in this.shows[this.currentShow].category) {
407
          axios.get(process.env.API_STEERING + 'categories/' + this.shows[this.currentShow].category[i], {withCredentials: true}).then(response => {
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
            this.current.categories.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching categories from the server: ' + error)
          })
        }
        if (!loadingError) this.loaded.categories = true
      }
    },
    getHosts: function () {
      this.current.hosts = []
      var loadingError = false
      if (this.shows[this.currentShow].hosts.length === 0) {
        this.loaded.hosts = true
      } else {
        for (var i in this.shows[this.currentShow].hosts) {
424
          axios.get(process.env.API_STEERING + 'hosts/' + this.shows[this.currentShow].hosts[i] + '/', {withCredentials: true}).then(response => {
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
            this.current.hosts.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching hosts from the server: ' + error)
          })
        }
        if (!loadingError) this.loaded.hosts = true
      }
    },
    getLanguages: function () {
      this.current.languages = []
      var loadingError = false
      if (this.shows[this.currentShow].language.length === 0) {
        this.loaded.categories = true
      } else {
        for (var i in this.shows[this.currentShow].language) {
441
          axios.get(process.env.API_STEERING + 'languages/' + this.shows[this.currentShow].language[i], {withCredentials: true}).then(response => {
442
443
444
445
446
447
            this.current.languages.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching languages from the server: ' + error)
          })
        }
448
        if (!loadingError) this.loaded.languages = true
449
450
451
452
453
454
455
456
457
      }
    },
    getTopics: function () {
      this.current.topics = []
      var loadingError = false
      if (this.shows[this.currentShow].topic.length === 0) {
        this.loaded.topics = true
      } else {
        for (var i in this.shows[this.currentShow].topic) {
458
          axios.get(process.env.API_STEERING + 'topics/' + this.shows[this.currentShow].topic[i], {withCredentials: true}).then(response => {
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
            this.current.topics.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching topics from the server: ' + error)
          })
        }
        if (!loadingError) this.loaded.topics = true
      }
    },
    getMusicfocus: function () {
      this.current.musicfocus = []
      var loadingError = false
      if (this.shows[this.currentShow].musicfocus.length === 0) {
        this.loaded.musicfocus = true
      } else {
        for (var i in this.shows[this.currentShow].musicfocus) {
475
          axios.get(process.env.API_STEERING + 'musicfocus/' + this.shows[this.currentShow].musicfocus[i], {withCredentials: true}).then(response => {
476
477
478
479
480
481
482
483
484
485
486
487
            this.current.musicfocus.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching musicfocus from the server: ' + error)
          })
        }
      }
      if (!loadingError) this.loaded.musicfocus = true
    },
    getRTRCategory: function () {
      this.current.rtrcategory = []
      var loadingError = false
488
      if (typeof this.shows[this.currentShow].rtrcategory !== 'number') {
489
490
        this.loaded.rtrcategory = true
      } else {
491
        axios.get(process.env.API_STEERING + 'rtrcategories/' + this.shows[this.currentShow].rtrcategory, {withCredentials: true}).then(response => {
492
493
494
495
496
497
498
499
500
501
502
503
504
505
          this.current.rtrcategory.push(response.data)
        }).catch(error => {
          loadingError = true
          alert('There was an error fetching RTR category from the server: ' + error)
        })
      }
      if (!loadingError) this.loaded.rtrcategory = true
    },
    getType: function () {
      this.current.type = []
      var loadingError = false
      if (typeof this.shows[this.currentShow].type !== 'number') {
        this.loaded.type = true
      } else {
506
        axios.get(process.env.API_STEERING + 'types/' + this.shows[this.currentShow].type, {withCredentials: true}).then(response => {
507
508
509
510
511
          this.current.type.push(response.data)
        }).catch(error => {
          loadingError = true
          alert('There was an error fetching show type from the server: ' + error)
        })
512
      }
513
      if (!loadingError) this.loaded.type = true
514
    },
515
    notYetImplemented: function () {
516
      alert('By the mighty witchcraftry of the mother of time!\n\nThis feature is not implemented yet.')
517
518
519
    }
  },
  created () {
520
    axios.get(process.env.API_STEERING_SHOWS, {withCredentials: true}).then(response => {
521
522
523
      this.shows = response.data
      this.currentShowID = this.shows[0].id
      this.currentShow = 0
524
      this.loaded.shows = true
525
526
527
      this.switchShow(this.currentShow)
    }).catch(error => {
      alert('There was an error fetching shows from the server: ' + error)
528
529
530
531
532
    })
  }
}
</script>

533
<style scoped>
534
div.recenttimeslots {
535
536
  margin-top: 2em;
}
537
.showsettings, .recenttimeslots, .nexttimeslots {
538
539
  text-align: left;
}
540
</style>