import axios from 'axios' import { APIError, callOrReturn, handleApiError } from '../api-helper' const cloneMinimalShowObject = function (show) { /* returns a new minimal object from the current show object with all properties needed for a PUT request to the /show/ endpoint */ let s = {} s.name = show.name s.slug = show.slug s.short_description = show.short_description s.fundingcategory = show.fundingcategory s.type = show.type // we do not want the arrays do be passed as references, because the // current show object should not get modified when the update object // gets modified, therefore we use slice to clone the arrays s.category = show.category.slice() s.hosts = show.hosts.slice() s.owners = show.owners.slice() s.language = show.language.slice() s.topic = show.topic.slice() s.musicfocus = show.musicfocus.slice() return s } const state = { shows: [], schedule: null, schedules: [], scheduleTimeslots: [], timeslots: [], notes: [], types: [], fundingcategories: [], categories: [], topics: [], musicfocus: [], languages: [], hosts: [], loaded: { shows: false, timeslots: false, notes: false, schedule: false, scheduleTimeslots: false, schedules: false, types: false, fundingcategories: false, categories: false, topics: false, musicfocus: false, languages: false, hosts: false, }, selected: { index: 0, // index of the currently selected show in our shows array id: 0, // actual id of the currently selected show } } const getters = { shows: state => state.shows, selectedShow: state => state.shows[state.selected.index], schedule: state => state.schedule, schedules: state => state.schedules, scheduleTimeslots: state => state.scheduleTimeslots, timeslots: state => state.timeslots, notes: state => state.notes, types: state => state.types, fundingcategories: state => state.fundingcategories, categories: state => state.categories, topics: state => state.topics, musicfocus: state => state.musicfocus, languages: state => state.languages, hosts: state => state.hosts, getShowByDataParam: state => data => { let show if (data.id !== undefined) { show = state.shows.find(s => s.id === data.id) if (show === undefined) { console.log('[ERROR] getShowByDataParam: ID not found in store!') } } else if (data.index !== undefined) { show = state.shows[data.index] } else { console.log('[ERROR] getShowByDataParam: no ID or index was provided') } return show }, getTimeslotById: state => id => { return state.timeslots.find(s => s.id === id) } } const mutations = { loading(state, item) { state.loaded[item] = false }, finishLoading(state, item) { state.loaded[item] = true }, setShows(state, shows) { state.shows = shows }, addShow(state, show) { state.shows.push(show) state.shows.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase()) }, setSchedule(state, schedule) { state.schedule = schedule }, setSchedules(state, schedules) { state.schedules = schedules }, setScheduleTimeslots(state, slots) { state.scheduleTimeslots = slots }, setTimeslots(state, slots) { state.timeslots = slots }, setTimeslot(state, slot) { let index = state.timeslots.findIndex(s => s.id === slot.id) state.timeslots.splice(index, 1, slot) }, setNotes(state, notes) { state.notes = notes }, addNote(state, note) { state.notes.push(note) }, setNote(state, note) { let index = state.notes.findIndex(n => n.id === note.id) state.notes.splice(index, 1, note) }, setName(state, data) { let index = state.shows.findIndex(s => s.id === data.id) state.shows[index].name = data.text }, setShortDescription(state, data) { let index = state.shows.findIndex(s => s.id === data.id) state.shows[index].short_description = data.text }, setDescription(state, data) { let index = state.shows.findIndex(s => s.id === data.id) state.shows[index].description = data.text }, setActive(state, data) { let index = state.shows.findIndex(s => s.id === data.id) state.shows[index].is_active = data.active }, setProperty(state, data) { let index = state.shows.findIndex(s => s.id === data.id) state.shows[index][data.property] = data.value }, setMetaArray(state, data) { state[data.property] = data.value }, switchShow(state, index) { if (state.loaded.shows) { state.selected.index = index state.selected.id = state.shows[index].id } }, switchShowById(state, id) { if (state.loaded.shows) { state.selected.index = state.shows.findIndex(s => s.id === id) state.selected.id = id } }, } const actions = { fetchShows(ctx, data) { const user = ctx.rootState.auth.user.steeringUser ctx.commit('loading', 'shows') let uri = process.env.VUE_APP_API_STEERING + 'shows' // normal users should only see their own shows, only superusers see all shows if (user && !user.is_superuser) { uri += '?owner=' + ctx.rootState.auth.user.steeringUser.id } axios.get(uri, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { ctx.commit('setShows', response.data) ctx.commit('finishLoading', 'shows') if (data && typeof (data.callback) === 'function') { data.callback() } }).catch(error => { handleApiError(this, error, 'could not load shows') console.log(error) if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, fetchSchedule(ctx, data) { ctx.commit('loading', 'schedule') let uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.show + '/schedules/' + data.schedule + '/' axios.get(uri, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { ctx.commit('setSchedule', response.data) ctx.commit('finishLoading', 'schedule') if (data && typeof (data.callback) === 'function') { data.callback(response) } }).catch(error => { handleApiError(this, error, 'could not load schedule') if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, fetchSchedules(ctx, data) { ctx.commit('loading', 'schedule') let uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.show + '/schedules/' axios.get(uri, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { ctx.commit('setSchedules', response.data) ctx.commit('finishLoading', 'schedule') if (data && typeof (data.callback) === 'function') { data.callback(response) } }).catch(error => { handleApiError(this, error, 'could not load schedule') if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, fetchTimeslots(ctx, data) { if (data.schedule !== undefined) { ctx.commit('loading', 'scheduleTimeslots') } else { ctx.commit('loading', 'timeslots') } let uri if (data.id !== undefined) { uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.id if (data.schedule !== undefined) { uri += '/schedules/' + data.schedule + '/timeslots/?' } else { uri += '/timeslots/?' } if (data.surrounding) { uri += 'surrounding' } } else { uri = process.env.VUE_APP_API_STEERING + 'timeslots?' } if (!data.surrounding) { uri += 'start=' + data.start uri += '&end=' + data.end if (data.limit) { uri += '&limit=' + data.limit } if (data.offset) { uri += '&offset=' + data.offset } } axios.get(uri, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { if (data.schedule !== undefined) { ctx.commit('setScheduleTimeslots', response.data) ctx.commit('finishLoading', 'scheduleTimeslots') } else { if (data.limit) { ctx.commit('setTimeslots', response.data.results) } else { ctx.commit('setTimeslots', response.data) } ctx.commit('finishLoading', 'timeslots') } if (data && typeof (data.callback) === 'function') { data.callback(response) } }).catch(error => { handleApiError(this, error, 'could not load timeslots') if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, fetchNotes(ctx, data) { ctx.commit('loading', 'notes') // we only have to make an API call if there are actually any notes // otherwise the notes are just and empty array if (data.notes.length === 0) { ctx.commit('setNotes', []) ctx.commit('finishLoading', 'notes') return } let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.id + '/notes/?ids=' for (let id of data.notes) { uri += id + ',' } uri = uri.slice(0, -1) // now remove trailing ',' this.$log.debug('fetchNotes: uri:', uri) axios.get(uri, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { ctx.commit('setNotes', response.data) ctx.commit('finishLoading', 'notes') if (data && typeof (data.callback) === 'function') { data.callback(response) } }).catch(error => { handleApiError(this, error, 'could not load notes') if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, fetchMetaArray(ctx, data) { ctx.commit('loading', data.property) let uri = process.env.VUE_APP_API_STEERING + data.property + '/' if (data.onlyActive === true) { uri += '?active=true' } axios.get(uri, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { ctx.commit('setMetaArray', {property: data.property, value: response.data}) ctx.commit('finishLoading', data.property) if (data && typeof (data.callback) === 'function') { data.callback() } }).catch(error => { handleApiError(this, error, 'could not load ' + data.property) if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, submitSchedule(ctx, data) { ctx.commit('loading', 'schedules') let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.showId + '/schedules/' return axios.post(uri, data.schedule, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { ctx.commit('finishLoading', 'schedules') return callOrReturn(response, data?.callback) }).catch(error => { APIError.handle(error, 'Unable to submit schedule', data, this) }) }, submitShow(ctx, data) { let uri = process.env.VUE_APP_API_STEERING_SHOWS axios.post(uri, data.show, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { ctx.commit('addShow', response.data) if (data && typeof (data.callback) === 'function') { data.callback(response) } }).catch(error => { handleApiError(this, error, 'could not add new show') if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, submitNote(ctx, data) { const NON_UPDATEABLE_PROPERTIES = ['thumbnails', 'width', 'height'] let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.id + '/schedules/' + data.scheduleID + '/timeslots/' + data.timeslotID + '/note/' if (data.update) { uri += data.note.id + '/' } let method = data.update ? 'put' : 'post' let formData = new FormData() // We serialize the data to a FormData object, so we can send the image // as binary blob to the Steering API. for (const [key, value] of Object.entries(data.note)) { // When we're updating we don't want to include some properties that cause errors. if (data.update && NON_UPDATEABLE_PROPERTIES.includes(key)) { continue } formData.append(key, value) } axios.request({ url: uri, method: method, data: formData, responseType: 'json', // we need this explicitly here, as it does not seem to work automagically as in GET and PUT requests headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { if (data.update) { ctx.commit('setNote', response.data) } else { ctx.commit('addNote', response.data) } if (data && typeof (data.callback) === 'function') { data.callback(response) } }).catch(error => { let msg = data.update ? 'could not update note' : 'could not add new note' handleApiError(this, error, msg) if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, updateShow(ctx, data) { let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.id + '/' axios.put(uri, data.show, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { if (data && typeof (data.callback) === 'function') { data.callback(response) } }).catch(error => { if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel(error.response) } }) }, updateProperty(ctx, data) { let show = cloneMinimalShowObject(ctx.getters.getShowByDataParam(data)) show[data.property] = data.value ctx.dispatch('updateShow', { id: data.id, show: show, callback: () => { ctx.commit('setProperty', { id: data.id, property: data.property, value: data.value }) if (typeof (data.callback) === 'function') { data.callback() } }, callbackCancel: error => { if (typeof (data.callbackCancel) === 'function') { data.callbackCancel(error) } } }) }, updateImage(ctx, data) { let show = ctx.getters.getShowByDataParam(data) let uri = process.env.VUE_APP_API_STEERING_SHOWS + data.id + '/' let formData = new FormData() // these propoerties have to be sent always (and they must not be null) formData.append('name', show.name) formData.append('slug', show.slug) formData.append('short_description', show.short_description) formData.append('type', show.type) formData.append('fundingcategory', show.fundingcategory) formData.append('is_active', show.is_active) // now we append the new logo/image file (signified by data.type) formData.append(data.type, data.file, data.file.name) axios.put(uri, formData, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(response => { ctx.commit('setProperty', { id: data.id, property: data.type, // when updating images of a show the steering API does not return // the full URI but only the path on the server // TODO: create an issue in steering, to create consistency value: process.env.VUE_APP_BASEURI_STEERING + response.data[data.type] }) if (data && typeof (data.callback) === 'function') { data.callback(response) } }).catch(error => { handleApiError(this, error, 'could not update show ' + data.type) if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, updateTimeslot(ctx, data) { let uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.show + '/schedules/' + data.schedule + '/timeslots/' + data.timeslot.id + '/' axios.put(uri, data.timeslot, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(() => { ctx.commit('setTimeslot', data.timeslot) if (data && typeof (data.callback) === 'function') { data.callback() } }).catch(error => { handleApiError(this, error, 'could not update timeslot') if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, deleteSchedule(ctx, data) { let uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.show + '/schedules/' + data.schedule + '/' axios.delete(uri, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(() => { if (data && typeof (data.callback) === 'function') { data.callback() } }).catch(error => { handleApiError(this, error, 'could not delete full schedule') if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, deleteTimeslot(ctx, data) { let uri = process.env.VUE_APP_API_STEERING + 'shows/' + data.show + '/schedules/' + data.schedule + '/timeslots/' + data.timeslot + '/' axios.delete(uri, { headers: {'Authorization': 'Bearer ' + ctx.rootState.auth.user.access_token} }).then(() => { if (data && typeof (data.callback) === 'function') { data.callback() } }).catch(error => { handleApiError(this, error, 'could not delete single timeslot') if (data && typeof (data.callbackCancel) === 'function') { data.callbackCancel() } }) }, } export default { namespaced: true, state, getters, actions, mutations, }