ShowManager.vue 31.1 KB
Newer Older
1
<template>
2
  <b-container>
3
4
    <!-- 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) -->
5
    <b-row>
6
7
8
      <b-col>
        <h3>Sendungen verwalten</h3>
      </b-col>
9
      <b-col align="right">
10
11
12
        <b-button
          v-if="$parent.user.steeringUser.is_superuser"
          v-b-popover.hover.top="'Add a new show'"
13
          variant="info"
14
          @click="$refs.addShowModal.openModal()"
15
16
17
18
        >
          +
        </b-button>
        &nbsp;
19
20
21
        <b-dropdown
          id="ddshows"
          text="Sendereihe auswählen"
22
          variant="outline-info"
23
24
        >
          <b-dropdown-item
25
            v-for="(show, index) in shows"
26
27
28
29
30
            :key="show.id"
            @click="switchShow(index)"
          >
            {{ show.name }}
          </b-dropdown-item>
31
32
33
        </b-dropdown>
      </b-col>
    </b-row>
34
    <hr>
35

36
37
    <!-- The jumbotron is used to display the name and description of the
    currently selected show -->
38
    <show-jumbotron />
39

40
    <!-- If the shows are not fully loaded yet, we just put the loading sign -->
41
    <div v-if="!loaded.shows">
42
43
      <b-row>
        <b-col align="center">
44
45
46
47
          <img
            src="../assets/radio.gif"
            alt="loading data"
          >
48
49
        </b-col>
      </b-row>
50
    </div>
51
52

    <!-- When all show data is loaded, here we display all the rest -->
53
    <div v-else>
54
      <!-- include the modals to edit show and timeslot entries from the modal compontents -->
55
56
57
58
59
      <app-modalNotes
        ref="appModalNotes"
        :show="shows[currentShow]"
        :show-aggregate="current"
      />
60
61
      <addShowModal
        ref="addShowModal"
62
      />
63
64
65
      <app-modalPlaylist
        ref="appModalPlaylist"
      />
66

67
      <!-- here are the filter settings for our timeslots table -->
68
      <b-card>
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
        <b-row>
          <b-col>
            <b-btn v-b-toggle.timeslotFilterCollapse>
              Toggle timeslot filters
            </b-btn>
          </b-col>
          <b-col align="right">
            <b-button
              v-if="$parent.user.steeringUser.is_superuser"
              variant="info"
              @click="$router.push({path: 'emissions', query: { show: currentShow }})"
            >
              Switch to Emission Manager
            </b-button>
          </b-col>
        </b-row>

86
        <b-collapse id="timeslotFilterCollapse">
87
          <br>
88
          <!-- How many slots to show per table page -->
89
          <b-row>
90
91
92
93
94
95
96
97
98
99
            <b-col sm="3">
              <label for="inputNumSlots">Number of slots to show:</label>
            </b-col>
            <b-col sm="9">
              <b-form-input
                id="inputNumSlots"
                v-model="numSlots"
                type="number"
              />
            </b-col>
100
          </b-row>
101
          <!-- The start date to display timeslots from (defaults to today) -->
102
          <b-row>
103
104
105
106
107
108
109
110
111
112
            <b-col sm="3">
              <label for="inputDateStart">From:</label>
            </b-col>
            <b-col sm="9">
              <b-form-input
                id="inputDateStart"
                v-model="dateStart"
                type="date"
              />
            </b-col>
113
          </b-row>
114
          <!-- The end date until to wich to display timeslots -->
115
          <b-row>
116
117
118
119
120
121
122
123
124
125
            <b-col sm="3">
              <label for="inputNumSlots">Until (exclusive):</label>
            </b-col>
            <b-col sm="9">
              <b-form-input
                id="inputDateEnd"
                v-model="dateEnd"
                type="date"
              />
            </b-col>
126
          </b-row>
127
          <br>
128
          <!-- And finally two buttons, one to reset and one to apply the filter -->
129
130
131
132
133
134
135
136
137
138
139
          <b-container
            fluid
            class="text-right"
          >
            <b-btn
              variant="outline-danger"
              @click="resetFilter()"
            >
              Reset filter
            </b-btn> &nbsp;
            <b-btn
140
              variant="outline-success"
141
142
143
144
              @click="applyFilter()"
            >
              Apply filter
            </b-btn>
145
146
147
148
          </b-container>
        </b-collapse>
      </b-card>

149
      <br>
150

151
152
      <!-- here we show our table of timeslots, if the timeslots are already
      loaded (otherwise we just show the loading symbol) -->
153
      <div v-if="loaded.timeslots">
154
155
156
157
158
159
160
        <b-table
          striped
          hover
          outlined
          :fields="notesTableArrayFields"
          :items="notesTableArray"
        >
161
          <!-- Title of the timeslot (if already set) -->
162
          <template v-slot:cell(title)="data">
163
164
165
            <span v-if="data.value">{{ data.value }}</span>
            <span v-else><small><i>(none set)</i></small></span>
          </template>
166
          <!-- Date and time when this timeslot starts -->
167
          <template v-slot:cell(starts)="data">
168
169
            {{ data.value }}
          </template>
170
          <!-- The duration of this timeslot -->
171
          <template v-slot:cell(duration)="data">
172
173
            {{ data.value }}
          </template>
174
175
          <!-- And here all the buttons for editing and doing other things
          with the displayed timeslot -->
176
          <template v-slot:cell(options)="data">
177
178
179
180
181
182
183
184
185
186
            <span
              class="timeslotEditLink"
              @click="editTimeslotNote(data.item.options.id, data.item.options.schedule)"
            ><img
              src="../assets/16x16/emblem-system.png"
              alt="Edit description"
              title="Edit description"
            ></span>
            <span
              class="timeslotEditLink"
187
              @click="editTimeslotPlaylist(shows[currentShow], data.item.options.schedule, data.item.options.id)"
188
189
            ><img
              src="../assets/16x16/media-eject.png"
190
191
              alt="Edit playlist"
              title="Edit playlist"
192
193
            ></span>
            <span
194
              v-if="data.item.options.play"
195
196
197
198
              class="timeslotEditLink"
              @click="notYetImplemented()"
            ><img
              src="../assets/16x16/media-playback-start.png"
199
200
              alt="Open player"
              title="Open player"
201
            ></span>
202
203
204
205
206
          </template>

          <template v-slot:cell(playlist)="data">
            <span v-if="data.value">{{ data.value }}</span>
            <span v-else><small><i>(none set)</i></small></span>
207
208
          </template>
        </b-table>
209
210
211
212
213
214
215
216
        <b-pagination
          v-model="current.timeslotmeta.page"
          align="center"
          :total-rows="current.timeslotmeta.count"
          :per-page="current.timeslotmeta.perpage"
          @change="timeslotsPage"
        />
      </div>
217
218
219
220
221
222
223
224
225
      <!-- If the timeslot data is not loaded, we just show the spinner instead
      of the table itself -->
      <div v-else>
        <div style="text-align: center;">
          <img
            src="../assets/radio.gif"
            alt="loading data"
          ><br>
        </div>
226
227
      </div>

228
      <hr>
229

230
      <h2>Allgemeine Einstellungen zur Sendereihe:</h2>
231
      <show-metaSimpleTypes />
232
      <show-metaArrays />
233
      <show-metaOwners />
234
      <show-metaImages />
235

236
      <hr>
237
    </div>
238
  </b-container>
239
240
241
</template>

<script>
242
import showJumbotron from './ShowJumbotron.vue'
243
244
245
import showMetaSimpleTypes from './ShowMetaSimpleTypes.vue'
import showMetaArrays from './ShowMetaArrays.vue'
import showMetaOwners from './ShowMetaOwners.vue'
246
import showMetaImages from './ShowMetaImages.vue'
247
import modalNotes from './ShowManagerModalNotes.vue'
248
import modalAddShow from './AddShowModal.vue'
249
import modalPlaylist from './ShowManagerModalPlaylist.vue'
250
import timeslotSort from '../mixins/timeslotSort'
251
import prettyDate from '../mixins/prettyDate'
252
import axios from 'axios'
253
import DOMPurify from 'dompurify'
254
255

export default {
256
257
  // 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
258
  components: {
259
    'app-modalNotes': modalNotes,
260
    'app-modalPlaylist': modalPlaylist,
261
    'addShowModal': modalAddShow,
262
    'show-jumbotron': showJumbotron,
263
    'show-metaArrays': showMetaArrays,
264
    'show-metaSimpleTypes': showMetaSimpleTypes,
265
    'show-metaOwners': showMetaOwners,
266
    'show-metaImages': showMetaImages,
267
  },
268
269

  // generic functions that we want to use from our mixins folder
270
  mixins: [ timeslotSort, prettyDate ],
271
272
273

  // this component will be handling a lot of data - probably the component can
  // be refactored to get rid of some redundancy here
274
275
  data () {
    return {
276
      shows: [],      // an array of objects describing our shows (empty at load, will be populated on created())
277
      numSlots: process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS, // all form input values are provided as strings
278
      dateStart: this.apiDate(new Date()),
279
      dateEnd: this.apiDate(new Date(new Date().getTime() + process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_DAYS * 86400000)),
280
281
282

      // the loaded object holds flags for the different things we will fetch
      // from the AuRa steering module
283
284
285
286
287
288
289
290
291
      loaded: {
        shows: false,
        timeslots: false,
        notes: false,
        categories: false,
        hosts: false,
        languages: false,
        topics: false,
        musicfocus: false,
292
        fundingcategory: false,
293
294
        type: false,
        owners: false,
295
        playlists: false,
296
      },
297
298
299

      // 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
300
301
302
303
304
305
      current: {
        categories: [],
        hosts: [],
        languages: [],
        topics: [],
        musicfocus: [],
306
        fundingcategory: [],
307
        type: [],
308
        playlists: [],
309
310
311
312
        timeslots: [],
        timeslotmeta: {    // meta info when pagination is used
          count: 0,
          next: null,
313
314
315
          previous: null,
          page: 1,   // page indexes start at 1 for <b-pagination> components
          perpage: 10
316
317
        },
        note: {},
318
319
        notes: [],
        image: '',
320
321
        logo: '',
        owners: [],
322
      },
323
324

      // this is used to configure the table with all the filtered timeslots
325
326
327
328
      notesTableArrayFields: [
        { key: 'title', label: 'Title of emission' },
        { key: 'starts', label: 'Emission start' },
        { key: 'duration', label: 'Duration' },
329
330
        { key: 'options', label: 'Edit' },
        { key: 'playlist', label: 'Playlist' },
331
      ]
332
333
    }
  },
334

jackie / Andrea Ida Malkah Klaura's avatar
jackie / Andrea Ida Malkah Klaura committed
335
  // Some of the info we need in the template are not easily and directly
336
  // retrievable, so we are computing them on the fly, when they are needed
337
  computed: {
338

339
340
    currentShow () { return this.$store.state.shows.selected.index },
    currentShowID () { return this.$store.state.shows.selected.id },
341
    isSuperuser () { return this.$store.state.auth.user.steeringUser.is_superuser },
342

343
344
345
346
    // 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.
347
348
349
    sanitizedShowDescription: function () {
      return DOMPurify.sanitize(this.shows[this.currentShow].description)
    },
350
351
352

    // 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
353
    notesTableArray: function () {
354
      let rows = []
355
      for (var i in this.current.timeslots) {
356
        let note = this.getNoteByTimeslotID(this.current.timeslots[i].id)
357
        if (note !== null) { note = note.title }
358
359
360
361

        let playlistTitle = ''
        if (this.current.timeslots[i].playlist_id !== null) {
          let playlist = this.current.playlists.find(list => list.id === this.current.timeslots[i].playlist_id)
362
363
364
365
366
367
          if (playlist) {
            if (playlist.description.length > 0) {
              playlistTitle = playlist.description
            } else {
              playlistTitle = playlist.id
            }
368
369
370
371
          }
        }

        rows.push({
372
373
          title: note,
          starts: this.prettyDateTime(this.current.timeslots[i].start),
374
          duration: this.prettyDuration(this.current.timeslots[i].start, this.current.timeslots[i].end),
375
376
          options: {
            id: this.current.timeslots[i].id,
377
378
379
380
            schedule: this.current.timeslots[i].schedule,
            play: this.current.timeslots[i].playlist_id !== null
          },
          playlist: playlistTitle
381
382
        })
      }
383
      return rows
384
385
    }
  },
386
387
388

  // Right after this component is set up, we want to fetch all available shows
  // from the AuRa steering module.
389
  created () {
390
391
392
    // As we don't know any shows yet, we use null as id parameter, so the
    // first show in the returned show array will be displayed
    this.loadAndSwitch(null)
393
  },
394
395
396

  // 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)
397
  methods: {
398
399

    // Apply the newly set filter parameters for our timeslot table
400
401
402
403
    applyFilter: function () {
      this.current.timeslotmeta.page = 1
      this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots)
    },
404
405

    // Reset the filter parameters for our timeslot table to config defaults
406
    resetFilter: function () {
407
      this.numSlots = process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS
408
      this.dateStart = this.apiDate(new Date())
409
      this.dateEnd = this.apiDate(new Date(new Date().getTime() + process.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_DAYS * 86400000))
410
411
412
      this.current.timeslotmeta.page = 1
      this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots)
    },
413
414

    // Load a different page of timeslots for the timeslots table
415
416
417
418
419
420
    timeslotsPage: function (page) {
      if (this.current.timeslotmeta.page !== page) {
        this.current.timeslotmeta.page = page
        this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots, (page - 1) * this.numSlots)
      }
    },
421
422
423
424
425
426
427
428

    // 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.
429
    switchShow: function (index) {
430
431
432
      // 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
433
434
435
436
437
438
439
440
      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
441
      this.loaded.fundingcategory = false
442
      this.loaded.owners = false
443
      // set the current show and its ID to whatever we want to switch to now
444
      this.$store.commit('shows/switchShow', index)
445
446
      // and check if images are available and set image strings, because we
      // cannot use them directly inside the b-img if they are null
447
448
449
450
      if (this.shows[this.currentShow].logo === null) { this.current.logo = '' }
      else { this.current.logo = this.shows[this.currentShow].logo }
      if (this.shows[this.currentShow].image === null) { this.current.image = '' }
      else { this.current.image = this.shows[this.currentShow].image }
451
452
453
454
455
456
      // before we load timeslots and notes, we want to fetch the general settings first
      this.getCategories()
      this.getHosts()
      this.getLanguages()
      this.getTopics()
      this.getMusicfocus()
457
      this.getFundingCategory()
458
      this.getType()
459
460
461
      if (this.$parent.user.steeringUser.is_superuser) {
        this.getOwners()
      }
462
      this.getTimeslots(this.dateStart, this.dateEnd, this.numSlots)
463
      this.loadPlaylists()
464
    },
465

466
467
468
    // (Re)Load all shows from server and switch to a show with a specific ID.
    // If the id argument is null, the first show in the show array will be used
    loadAndSwitch: function (id) {
469
      this.$store.dispatch('shows/fetchShows')
470
471
472
473
474
475
476
      this.$store.dispatch('shows/fetchMetaArray', {property: 'types', onlyActive: true})
      this.$store.dispatch('shows/fetchMetaArray', {property: 'fundingcategories', onlyActive: true})
      this.$store.dispatch('shows/fetchMetaArray', {property: 'categories'})
      this.$store.dispatch('shows/fetchMetaArray', {property: 'topics'})
      this.$store.dispatch('shows/fetchMetaArray', {property: 'musicfocus'})
      this.$store.dispatch('shows/fetchMetaArray', {property: 'languages'})
      this.$store.dispatch('shows/fetchMetaArray', {property: 'hosts'})
477
      if (this.isSuperuser) { this.$store.dispatch('auth/fetchUsers') }
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
      this.loaded.shows = false
      var uri = process.env.VUE_APP_API_STEERING_SHOWS
      // normal users should only see their own shows, only superusers see all shows
      if (!this.$parent.user.steeringUser.is_superuser) {
        uri += '?owner=' + this.$parent.user.steeringUser.id
      }
      axios.get(uri, {
        withCredentials: true,
        headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
      }).then(response => {
        // if now shows are found, we'll just print a short info message and leave
        if (response.data.length === 0) {
          this.$log.info('The returned show set has 0 length:')
          this.$log.info(response)
          alert('Info: There are no shows connected to your account. See console for details.')
          this.loaded.shows = true
          return
        }
        // now set the new show array and find the index of the show ID
        this.shows = response.data
        let index = 0
        if (id !== null) {
          index = this.shows.findIndex(show => show.id === id)
          // if no show with the given ID was found, we use the first item in the show array
          if (index === -1) { index = 0 }
        }
504
        this.$store.commit('shows/switchShow', index)
505
506
507
508
509
510
511
512
513
        this.loaded.shows = true
        this.switchShow(this.currentShow)
      }).catch(error => {
        this.$log.error(error.response.status + ' ' + error.response.statusText)
        this.$log.error(error.response)
        alert('Error: could not fetch show data. See console for details.')
      })
    },

514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
    loadPlaylists () {
      this.loaded.playlists = false
      let uri = process.env.VUE_APP_API_TANK + 'shows/' + this.shows[this.currentShow].slug + '/playlists'
      axios.get(uri, {
        withCredentials: true,
        headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
      }).then(response => {
        // we don't have to check separately, if there are playlists, because tank
        // always provides an empty array if there are no playlists (or even if there is no corresponding show)
        this.current.playlists = response.data.results
        this.loaded.playlists = true
      }).catch(error => {
        //this.$log.error(error.response.status + ' ' + error.response.statusText)
        //this.$log.error(error.response)
        this.$log.error(error)
        alert('Error: could not fetch playlists from tank. See console for details.')
      })
    },

533
    // Fetch timeslots for the current show and use filter variables if provided
534
    getTimeslots: function (start, end, limit, offset) {
535
      var dateRegex = new RegExp('^\\d{4}-\\d{2}-\\d{2}$')
536
      var uri = process.env.VUE_APP_API_STEERING_SHOWS + this.currentShowID + '/timeslots/?'
537
538
539
540
      if (dateRegex.test(start)) { uri += 'start=' + start + '&' }
      if (dateRegex.test(end)) { uri += 'end=' + end + '&' }
      if (!isNaN(parseInt(limit))) { uri += 'limit=' + parseInt(limit) + '&' }
      if (!isNaN(parseInt(offset))) { uri += 'offset=' + parseInt(offset) }
541
542
      this.loaded.timeslots = false
      this.loaded.notes = false
543
544
545
546
      axios.get(uri, {
        withCredentials: true,
        headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
      }).then(response => {
547
548
        // if we use the limit argument results are paginated and look different
        // than without the limit argument
549
        if (!isNaN(parseInt(limit))) {
550
551
552
553
          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
554
          this.current.timeslotmeta.perpage = parseInt(limit)
555
        } else {
556
557
558
559
          this.current.timeslots = response.data
          this.current.timeslotmeta.count = response.data.length
          this.current.timeslotmeta.next = null
          this.current.timeslotmeta.previous = null
560
          this.current.timeslotmeta.perpage = response.data.length
561
        }
562
        this.loaded.timeslots = true
563
        // now that we have the timeslots we can fetch notes for all those timeslots
564
        uri = process.env.VUE_APP_API_STEERING_SHOWS + this.currentShowID + '/notes/?ids='
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
        // 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 = []
        }
592
      }).catch(error => {
593
594
595
        this.$log.error(error.response.status + ' ' + error.response.statusText)
        this.$log.error(error.response)
        alert('Error: could not fetch timeslots. See console for details.')
596
      })
597
598
      // done fetching timeslots
    },
599
600

    // Open the modal to edit a timeslot's note, given its ID and schedule ID
601
602
    editTimeslotNote: function (timeslotID, scheduleID) {
      this.current.note = null
603
604
605
      for (var i in this.current.notes) {
        if (this.current.notes[i].timeslot === timeslotID) {
          this.current.note = this.current.notes[i]
606
607
608
          break
        }
      }
609
      this.$refs.appModalNotes.showModal(this.current.note, timeslotID, scheduleID)
610
    },
611

612
    editTimeslotPlaylist: function (show, schedule, timeslot) {
613
      this.$refs.appModalPlaylist.open(show, schedule, timeslot, this.current.playlists)
614
615
    },

616
    // For a given timeslot ID return the corresponding note, if there is one
617
618
619
620
    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]
621
622
623
624
        }
      }
      return null
    },
625
626

    // For a given timeslot ID, check if there is a note and return it
627
    prettyTimeslotNote: function (timeslotID) {
628
      var note = this.getTimeslotNoteTitle(timeslotID)
629
630
631
632
633
634
      if (note !== null) {
        return this.prettyTitle(note)
      } else {
        return ''
      }
    },
635
636

    // Limiting display of strings up to 25 characters plus "..."
637
    prettyTitle: function (title) {
638
639
640
      if (title === '') { return '...' }
      else if (title.length > 25) { return title.slice(0, 25) + '...' }
      else { return title }
641
    },
642
643
644
645

    // TODO: all those getSomething functions could be probably merged into one
    // generic getItem function. Maybe this.current should be implemented
    // in a dictionary kind of way then?
646
647
648
649
650
651
652
    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) {
653
          axios.get(process.env.VUE_APP_API_STEERING + 'categories/' + this.shows[this.currentShow].category[i] + '/', {
654
655
656
            withCredentials: true,
            headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
          }).then(response => {
657
658
659
660
661
662
            this.current.categories.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching categories from the server: ' + error)
          })
        }
663
        if (!loadingError) { this.loaded.categories = true }
664
665
      }
    },
666

667
668
669
670
671
672
673
    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) {
674
          axios.get(process.env.VUE_APP_API_STEERING + 'hosts/' + this.shows[this.currentShow].hosts[i] + '/', {
675
676
677
            withCredentials: true,
            headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
          }).then(response => {
678
679
680
681
682
683
            this.current.hosts.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching hosts from the server: ' + error)
          })
        }
684
        if (!loadingError) { this.loaded.hosts = true }
685
686
      }
    },
687

688
689
690
691
    getLanguages: function () {
      this.current.languages = []
      var loadingError = false
      if (this.shows[this.currentShow].language.length === 0) {
692
        this.loaded.languages = true
693
694
      } else {
        for (var i in this.shows[this.currentShow].language) {
695
          axios.get(process.env.VUE_APP_API_STEERING + 'languages/' + this.shows[this.currentShow].language[i] + '/', {
696
697
698
            withCredentials: true,
            headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
          }).then(response => {
699
700
701
702
703
704
            this.current.languages.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching languages from the server: ' + error)
          })
        }
705
        if (!loadingError) { this.loaded.languages = true }
706
707
      }
    },
708

709
710
711
712
713
714
715
    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) {
716
          axios.get(process.env.VUE_APP_API_STEERING + 'topics/' + this.shows[this.currentShow].topic[i] + '/', {
717
718
719
            withCredentials: true,
            headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
          }).then(response => {
720
721
722
723
724
725
            this.current.topics.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching topics from the server: ' + error)
          })
        }
726
        if (!loadingError) { this.loaded.topics = true }
727
728
      }
    },
729

730
731
732
733
734
735
736
    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) {
737
          axios.get(process.env.VUE_APP_API_STEERING + 'musicfocus/' + this.shows[this.currentShow].musicfocus[i] + '/', {
738
739
740
            withCredentials: true,
            headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
          }).then(response => {
741
742
743
744
745
746
747
            this.current.musicfocus.push(response.data)
          }).catch(error => {
            loadingError = true
            alert('There was an error fetching musicfocus from the server: ' + error)
          })
        }
      }
748
      if (!loadingError) { this.loaded.musicfocus = true }
749
    },
750

751
    getFundingCategory: function () {
752
      this.current.fundingcategory = []
753
      var loadingError = false
754
755
      if (typeof this.shows[this.currentShow].fundingcategory !== 'number') {
        this.loaded.fundingcategory = true
756
      } else {
757
        axios.get(process.env.VUE_APP_API_STEERING + 'fundingcategories/' + this.shows[this.currentShow].fundingcategory + '/', {
758
759
760
          withCredentials: true,
          headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
        }).then(response => {
761
          this.current.fundingcategory.push(response.data)
762
763
        }).catch(error => {
          loadingError = true
764
          alert('There was an error fetching funding category from the server: ' + error)
765
766
        })
      }
767
      if (!loadingError) { this.loaded.fundingcategory = true }
768
    },
769

770
771
772
773
774
775
    getType: function () {
      this.current.type = []
      var loadingError = false
      if (typeof this.shows[this.currentShow].type !== 'number') {
        this.loaded.type = true
      } else {
776
        axios.get(process.env.VUE_APP_API_STEERING + 'types/' + this.shows[this.currentShow].type + '/', {
777
778
779
          withCredentials: true,
          headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
        }).then(response => {
780
781
782
783
784
          this.current.type.push(response.data)
        }).catch(error => {
          loadingError = true
          alert('There was an error fetching show type from the server: ' + error)
        })
785
      }
786
      if (!loadingError) { this.loaded.type = true }
787
    },
788

789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
    getOwners: function () {
      this.current.owners = []
      let loadingError = false
      if (this.shows[this.currentShow].owners.length === 0) {
        this.loaded.owners = true
      } else {
        for (let i in this.shows[this.currentShow].owners) {
          axios.get(process.env.VUE_APP_API_STEERING + 'users/' + this.shows[this.currentShow].owners[i] + '/', {
            withCredentials: true,
            headers: { 'Authorization': 'Bearer ' + this.$parent.user.access_token }
          }).then(response => {
            this.current.owners.push(response.data)
          }).catch(error => {
            loadingError = true
            this.$log.error(error.response.status + ' ' + error.response.statusText)
            this.$log.error(error.response)
            alert('Error: could not load owner data. See console for details.')
          })
        }
        if (!loadingError) { this.loaded.owners = true }
      }
    },

812
813
    // Just a placeholder function we can use in the UI, to signal if something
    // is not yet implemented
814
    notYetImplemented: function () {
815
      alert('By the mighty witchcraftry of the mother of time!\n\nThis feature is not implemented yet.')
816
817
818
819
820
    }
  }
}
</script>

821
<style>
822
823
824
825
826
827
828
829
830
span.timeslotEditLink {
  color: #8d5f82 !important;
  font-weight: bold;
  font-size: 0.8em;
  padding: 0.25em;
}
span.timeslotEditLink:hover {
  color: #ad7fa8 !important;
}
831
</style>