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

31
    <div v-if="!loaded.shows">
32
33
34
      <b-row>
        <b-col align="center">
          <img src="../assets/radio.gif" alt="loading data" />
35
36
        </b-col>
      </b-row>
37
38
    </div>
    <div v-else>
39
40
      <app-modalNotes ref="appModalNotes" v-bind:note="current.note"></app-modalNotes>

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

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

85
86
      <hr />

87
      <div v-if="loaded.shows" class="showsettings">
88
        <h2>Allgemeine Einstellungen zur Sendereihe:</h2>
89
        <b-row>
90
91

          <b-col lg="6">
92
            <p>
93
              <b-badge variant="light">E-Mail:</b-badge>
94
95
96
97
98
              <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>
99
100

          <b-col lg="6">
101
            <p>
102
              <b-badge variant="light">Website:</b-badge>
103
104
105
106
107
              <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>
108
109

          <b-col lg="6">
110
            <p>
111
              <b-badge variant="light">Show type:</b-badge>
112
              <!-- TODO: discuss: should this be visible to show owners or only to administrators? -->
113
114
115
              <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>
116
                <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
117
118
              </span>
              <span v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /></span>
119
120
            </p>
          </b-col>
121
122

          <b-col lg="6">
123
            <p>
124
              <b-badge variant="light">Funding category (eg. for RTR):</b-badge>
125
              <!-- TODO: discuss: should this be visible to show owners or only to administrators? -->
126
127
128
              <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>
129
                <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
130
131
              </span>
              <span v-else><img src="../assets/radio.gif" height="24px" alt="loading data" /></span>
132
133
            </p>
          </b-col>
134
135

          <b-col lg="6">
136
137
138
            <p>
              <!-- TODO: discuss: should this be visible to show owners or only to administrators? -->
              <!-- TODO: fetch name for predecessor_id from steering api -->
139
              <b-badge variant="light">Predecessor:</b-badge>
140
141
142
143
144
              <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>
145
146

          <b-col lg="6">
147
            <p>
148
              <b-badge variant="light">CBA Series ID:</b-badge>
149
150
151
152
153
              <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>
154
155

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

        </b-row>

        <b-row>

          <b-col lg="2">
169
            <b-badge style="width:80%;">Categories:</b-badge> <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
170
          </b-col>
171
172
173
174
175
176
177
178
179
180
181
182
          <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>
183
          </b-col>
184
185

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

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

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

          <b-col lg="2">
237
            <b-badge style="width:80%;">Hosts:</b-badge> <img src="../assets/16x16/emblem-system.png" alt="edit" v-on:click="notYetImplemented" />
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
          </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>

254
        </b-row>
255
      </div>
256
    </div>
257
  </b-container>
258
259
260
</template>

<script>
261
import modalNotes from './ShowManagerModalNotes.vue'
262
import timeslotSort from '../mixins/timeslotSort'
263
import prettyDate from '../mixins/prettyDate'
264
import axios from 'axios'
265
266

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

526
<style scoped>
527
div.recenttimeslots {
528
529
  margin-top: 2em;
}
530
.showsettings, .recenttimeslots, .nexttimeslots {
531
532
  text-align: left;
}
533
</style>