Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
AURA
dashboard
Commits
4a424a9c
Commit
4a424a9c
authored
May 14, 2020
by
jackie / Andrea Ida Malkah Klaura
Browse files
create separate timeslot component
parent
351fa3d0
Changes
5
Expand all
Hide whitespace changes
Inline
Side-by-side
src/components/ShowManager.vue
View file @
4a424a9c
This diff is collapsed.
Click to expand it.
src/components/
S
how
ManagerModalNotes
.vue
→
src/components/
s
how
s/NotesModal
.vue
View file @
4a424a9c
...
@@ -74,16 +74,14 @@
...
@@ -74,16 +74,14 @@
</
template
>
</
template
>
<
script
>
<
script
>
import
prettyDate
from
'
../mixins/prettyDate
'
import
{
mapGetters
}
from
'
vuex
'
import
slugify
from
'
../mixins/slugify
'
import
prettyDate
from
'
../../mixins/prettyDate
'
import
slugify
from
'
../../mixins/slugify
'
import
axios
from
'
axios
'
import
axios
from
'
axios
'
export
default
{
export
default
{
mixins
:
[
prettyDate
,
slugify
],
mixins
:
[
prettyDate
,
slugify
],
props
:
{
show
:
{
type
:
Object
,
required
:
true
},
showAggregate
:
{
type
:
Object
,
required
:
true
}
},
data
()
{
data
()
{
return
{
return
{
note
:
{},
note
:
{},
...
@@ -99,24 +97,30 @@ export default {
...
@@ -99,24 +97,30 @@ export default {
host_selected
:
null
host_selected
:
null
}
}
},
},
computed
:
{
computed
:
{
slug
:
function
()
{
slug
()
{
return
this
.
slugify
(
this
.
title
)
},
return
this
.
slugify
(
this
.
title
)
hosts
()
{
},
hosts
:
function
()
{
// for the vue bootstrap select component we need an array of objects
// for the vue bootstrap select component we need an array of objects
// with a value, a text and optionally a disabled element
// with a value, a text and optionally a disabled element
var
hosts
=
[]
let
hosts
=
[]
for
(
var
i
in
this
.
showAggregate
.
hosts
)
{
for
(
let
id
of
this
.
selectedShow
.
hosts
)
{
let
host
=
this
.
allHosts
.
find
(
h
=>
h
.
id
===
id
)
hosts
.
push
({
hosts
.
push
({
value
:
this
.
showAggregate
.
hosts
[
i
]
.
id
,
value
:
host
.
id
,
text
:
this
.
showAggregate
.
hosts
[
i
]
.
name
,
text
:
host
.
name
,
disabled
:
!
this
.
showAggregate
.
hosts
[
i
]
.
is_active
disabled
:
!
host
.
is_active
})
})
}
}
return
hosts
return
hosts
}
},
...
mapGetters
({
selectedShow
:
'
shows/selectedShow
'
,
allHosts
:
'
shows/hosts
'
,
})
},
},
methods
:
{
methods
:
{
update
(
event
)
{
update
(
event
)
{
// only try to save if anything has changed
// only try to save if anything has changed
...
@@ -135,7 +139,7 @@ export default {
...
@@ -135,7 +139,7 @@ export default {
this
.
note
.
host
=
this
.
host_selected
this
.
note
.
host
=
this
.
host_selected
// generate the uri for the API call:
// generate the uri for the API call:
// /api/v1/shows/1/schedules/1/timeslots/1/note/1/
// /api/v1/shows/1/schedules/1/timeslots/1/note/1/
var
uri
=
process
.
env
.
VUE_APP_API_STEERING_SHOWS
+
this
.
show
.
id
+
var
uri
=
process
.
env
.
VUE_APP_API_STEERING_SHOWS
+
this
.
s
electedS
how
.
id
+
'
/schedules/
'
+
this
.
scheduleID
+
'
/schedules/
'
+
this
.
scheduleID
+
'
/timeslots/
'
+
this
.
timeslotID
+
'
/timeslots/
'
+
this
.
timeslotID
+
'
/note/
'
+
this
.
note
.
id
+
'
/
'
'
/note/
'
+
this
.
note
.
id
+
'
/
'
...
@@ -163,6 +167,7 @@ export default {
...
@@ -163,6 +167,7 @@ export default {
this
.
$refs
.
modalNote
.
hide
()
this
.
$refs
.
modalNote
.
hide
()
}
}
},
},
new
(
event
)
{
new
(
event
)
{
// title and content are necessary
// title and content are necessary
if
(
this
.
title
.
trim
()
===
''
||
this
.
content
.
trim
()
===
''
)
{
if
(
this
.
title
.
trim
()
===
''
||
this
.
content
.
trim
()
===
''
)
{
...
@@ -174,7 +179,7 @@ export default {
...
@@ -174,7 +179,7 @@ export default {
event
.
preventDefault
()
event
.
preventDefault
()
// prepare the new note
// prepare the new note
this
.
note
=
{
this
.
note
=
{
show
:
this
.
show
.
id
,
show
:
this
.
s
electedS
how
.
id
,
timeslot
:
this
.
timeslotID
,
timeslot
:
this
.
timeslotID
,
host
:
this
.
host_selected
,
host
:
this
.
host_selected
,
title
:
this
.
title
,
title
:
this
.
title
,
...
@@ -190,43 +195,28 @@ export default {
...
@@ -190,43 +195,28 @@ export default {
cba_id
:
null
,
// TODO: implement
cba_id
:
null
,
// TODO: implement
audio_url
:
''
// TODO: implement
audio_url
:
''
// TODO: implement
}
}
// generate the uri for the API call:
let
modal
=
this
.
$refs
.
modalNote
// /api/v1/shows/1/schedules/1/timeslots/1/note
this
.
$store
.
dispatch
(
'
shows/submitNote
'
,
{
var
uri
=
process
.
env
.
VUE_APP_API_STEERING_SHOWS
+
this
.
show
.
id
+
id
:
this
.
selectedShow
.
id
,
'
/schedules/
'
+
this
.
scheduleID
+
scheduleID
:
this
.
scheduleID
,
'
/timeslots/
'
+
this
.
timeslotID
+
timeslotID
:
this
.
timeslotID
,
'
/note/
'
note
:
this
.
note
,
// now send the POST request with our updated note
callback
:
()
=>
{
modal
.
hide
()
},
axios
.
post
(
uri
,
this
.
note
,
{
callbackCancel
:
()
=>
{
withCredentials
:
true
,
// as there was an error saving the show, we have to make sure
responseType
:
'
json
'
,
// we need this explicitly here, as it does not seem to work automagically as in GET and PUT requests
// to restore the initial values of the note object
headers
:
{
'
Authorization
'
:
'
Bearer
'
+
this
.
$parent
.
$parent
.
user
.
access_token
}
this
.
note
.
title
=
this
.
backuptitle
}).
then
(
response
=>
{
this
.
note
.
summary
=
this
.
backupsummary
this
.
note
=
response
.
data
this
.
note
.
content
=
this
.
backupcontent
for
(
var
i
in
this
.
showAggregate
.
timeslots
)
{
// and we have to set this back to undefined so next time we edit it
if
(
this
.
showAggregate
.
timeslots
[
i
].
id
===
this
.
timeslotID
)
{
// it will still be treated as a new note and not an existing one to update
this
.
showAggregate
.
timeslots
[
i
].
note_id
=
this
.
note
.
id
this
.
note
.
start
=
undefined
this
.
showAggregate
.
notes
.
push
(
this
.
note
)
// and we leave the modal open, so no call to its .hide function here
}
}
}
// everything was fine, we can close the modal now
this
.
$refs
.
modalNote
.
hide
()
}).
catch
(
error
=>
{
this
.
$log
.
error
(
error
.
response
.
status
+
'
'
+
error
.
response
.
statusText
)
this
.
$log
.
error
(
error
.
response
)
alert
(
'
Error: could not post the new note. See console for details.
'
)
// as there was an error saving the show, we have to make sure
// to restore the initial values of the note object
this
.
note
.
title
=
this
.
backuptitle
this
.
note
.
summary
=
this
.
backupsummary
this
.
note
.
content
=
this
.
backupcontent
// and we have to set this back to undefined so next time we edit it
// it will still be treated as a new note and not an existing one to update
this
.
note
.
start
=
undefined
// and we leave the modal open, so no call to its .hide function here
})
})
}
}
},
},
saveNote
(
event
)
{
saveNote
(
event
)
{
if
(
typeof
this
.
note
.
start
===
'
undefined
'
)
{
if
(
typeof
this
.
note
.
start
===
'
undefined
'
)
{
this
.
new
(
event
)
this
.
new
(
event
)
...
@@ -234,7 +224,8 @@ export default {
...
@@ -234,7 +224,8 @@ export default {
this
.
update
(
event
)
this
.
update
(
event
)
}
}
},
},
showModal
(
note
,
timeslotID
,
scheduleID
)
{
openModal
(
note
,
timeslotID
,
scheduleID
)
{
if
(
note
===
null
)
{
if
(
note
===
null
)
{
this
.
note
=
{}
this
.
note
=
{}
this
.
title
=
''
this
.
title
=
''
...
@@ -242,7 +233,7 @@ export default {
...
@@ -242,7 +233,7 @@ export default {
this
.
content
=
''
this
.
content
=
''
// TODO: integrate this into the user's app settings:
// TODO: integrate this into the user's app settings:
// should the field be empty by default or filled with the first host of the show?
// should the field be empty by default or filled with the first host of the show?
this
.
host_selected
=
this
.
showAggregate
.
hosts
[
0
].
id
this
.
host_selected
=
null
}
else
{
}
else
{
this
.
note
=
note
this
.
note
=
note
this
.
title
=
this
.
note
.
title
this
.
title
=
this
.
note
.
title
...
...
src/components/ShowManagerModalPlaylist.vue
→
src/components/
shows/
ShowManagerModalPlaylist.vue
View file @
4a424a9c
...
@@ -66,7 +66,7 @@
...
@@ -66,7 +66,7 @@
</div>
</div>
<div
v-else
>
<div
v-else
>
<img
<img
src=
"../assets/radio.gif"
src=
"../
../
assets/radio.gif"
alt=
"loading playlists"
alt=
"loading playlists"
>
>
</div>
</div>
...
@@ -82,7 +82,7 @@
...
@@ -82,7 +82,7 @@
<
script
>
<
script
>
import
axios
from
'
axios
'
import
axios
from
'
axios
'
import
prettyDate
from
'
../mixins/prettyDate
'
import
prettyDate
from
'
../
../
mixins/prettyDate
'
export
default
{
export
default
{
mixins
:
[
prettyDate
],
mixins
:
[
prettyDate
],
...
...
src/components/shows/Timeslots.vue
0 → 100644
View file @
4a424a9c
<
template
>
<div>
<app-modalNotes
ref=
"appModalNotes"
/>
<app-modalPlaylist
ref=
"appModalPlaylist"
/>
<!-- here are the filter settings for our timeslots table -->
<b-card>
<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=
"user.steeringUser.is_superuser"
variant=
"info"
@
click=
"$router.push(
{path: 'emissions', query: { show: selectedShow.slug }})"
>
Switch to Emission Manager
</b-button>
</b-col>
</b-row>
<b-collapse
id=
"timeslotFilterCollapse"
>
<br>
<!-- How many slots to show per table page -->
<b-row>
<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>
</b-row>
<!-- The start date to display timeslots from (defaults to today) -->
<b-row>
<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>
</b-row>
<!-- The end date until to wich to display timeslots -->
<b-row>
<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>
</b-row>
<br>
<!-- And finally two buttons, one to reset and one to apply the filter -->
<b-container
fluid
class=
"text-right"
>
<b-btn
variant=
"outline-danger"
@
click=
"resetFilter()"
>
Reset filter
</b-btn>
<b-btn
variant=
"outline-success"
@
click=
"applyFilter()"
>
Apply filter
</b-btn>
</b-container>
</b-collapse>
</b-card>
<br>
<!-- here we show our table of timeslots, if the timeslots are already
loaded (otherwise we just show the loading symbol) -->
<div
v-if=
"loaded.timeslots"
>
<b-table
striped
hover
outlined
:fields=
"notesTableArrayFields"
:items=
"notesTableArray"
>
<!-- Title of the timeslot (if already set) -->
<template
v-slot:cell(title)=
"data"
>
<span
v-if=
"data.value"
>
{{
data
.
value
}}
</span>
<span
v-else
><small><i>
(none set)
</i></small></span>
</
template
>
<!-- Date and time when this timeslot starts -->
<
template
v-slot:cell(starts)=
"data"
>
{{
data
.
value
}}
</
template
>
<!-- The duration of this timeslot -->
<
template
v-slot:cell(duration)=
"data"
>
{{
data
.
value
}}
</
template
>
<!-- And here all the buttons for editing and doing other things
with the displayed timeslot -->
<
template
v-slot:cell(options)=
"data"
>
<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"
@
click=
"editTimeslotPlaylist(selectedShow, data.item.options.schedule, data.item.options.id)"
><img
src=
"../../assets/16x16/media-eject.png"
alt=
"Edit playlist"
title=
"Edit playlist"
></span>
<span
v-if=
"data.item.options.play"
class=
"timeslotEditLink"
@
click=
"notYetImplemented()"
><img
src=
"../../assets/16x16/media-playback-start.png"
alt=
"Open player"
title=
"Open player"
></span>
</
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>
</
template
>
</b-table>
<b-pagination
v-model=
"timeslotmeta.page"
align=
"center"
:total-rows=
"timeslotmeta.count"
:per-page=
"timeslotmeta.perpage"
@
change=
"timeslotsPage"
/>
</div>
<!-- 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>
</div>
</div>
</template>
<
script
>
import
{
mapGetters
}
from
'
vuex
'
import
modalNotes
from
'
./NotesModal.vue
'
import
modalPlaylist
from
'
./ShowManagerModalPlaylist.vue
'
import
timeslotSort
from
'
../../mixins/timeslotSort
'
import
prettyDate
from
'
../../mixins/prettyDate
'
import
axios
from
'
axios
'
export
default
{
components
:
{
'
app-modalNotes
'
:
modalNotes
,
'
app-modalPlaylist
'
:
modalPlaylist
,
},
mixins
:
[
timeslotSort
,
prettyDate
],
data
()
{
return
{
numSlots
:
process
.
env
.
VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS
,
// all form input values are provided as strings
dateStart
:
this
.
apiDate
(
new
Date
()),
dateEnd
:
this
.
apiDate
(
new
Date
(
new
Date
().
getTime
()
+
process
.
env
.
VUE_APP_TIMESLOT_FILTER_DEFAULT_DAYS
*
86400000
)),
loadedLocal
:
{
playlists
:
false
,
},
note
:
{},
playlists
:
[],
timeslotmeta
:
{
// meta info when pagination is used
count
:
0
,
next
:
null
,
previous
:
null
,
page
:
1
,
// page indexes start at 1 for
<
b
-
pagination
>
components
perpage
:
10
},
// this is used to configure the table with all the filtered timeslots
notesTableArrayFields
:
[
{
key
:
'
title
'
,
label
:
'
Title of emission
'
},
{
key
:
'
starts
'
,
label
:
'
Emission start
'
},
{
key
:
'
duration
'
,
label
:
'
Duration
'
},
{
key
:
'
options
'
,
label
:
'
Edit
'
},
{
key
:
'
playlist
'
,
label
:
'
Playlist
'
},
],
}
},
computed
:
{
shows
()
{
return
this
.
$store
.
state
.
shows
.
shows
},
user
()
{
return
this
.
$store
.
state
.
auth
.
user
},
isSuperuser
()
{
return
this
.
$store
.
state
.
auth
.
user
.
steeringUser
.
is_superuser
},
loaded
()
{
return
{
shows
:
this
.
$store
.
state
.
shows
.
loaded
.
shows
,
timeslots
:
this
.
$store
.
state
.
shows
.
loaded
.
timeslots
,
notes
:
this
.
$store
.
state
.
shows
.
loaded
.
notes
,
}
},
// As we do not have a single object which holds all info we need to display
// in the table with our timeslots, we use this computed array to do that
notesTableArray
:
function
()
{
let
rows
=
[]
for
(
let
i
in
this
.
timeslots
)
{
let
note
=
this
.
getNoteByTimeslotID
(
this
.
timeslots
[
i
].
id
)
if
(
note
!==
null
)
{
note
=
note
.
title
}
let
playlistTitle
=
''
if
(
this
.
timeslots
[
i
].
playlist_id
!==
null
)
{
let
playlist
=
this
.
playlists
.
find
(
list
=>
list
.
id
===
this
.
timeslots
[
i
].
playlist_id
)
if
(
playlist
)
{
if
(
playlist
.
description
.
length
>
0
)
{
playlistTitle
=
playlist
.
description
}
else
{
playlistTitle
=
playlist
.
id
}
}
}
rows
.
push
({
title
:
note
,
starts
:
this
.
prettyDateTime
(
this
.
timeslots
[
i
].
start
),
duration
:
this
.
prettyDuration
(
this
.
timeslots
[
i
].
start
,
this
.
timeslots
[
i
].
end
),
options
:
{
id
:
this
.
timeslots
[
i
].
id
,
schedule
:
this
.
timeslots
[
i
].
schedule
,
play
:
this
.
timeslots
[
i
].
playlist_id
!==
null
},
playlist
:
playlistTitle
})
}
return
rows
},
...
mapGetters
({
selectedShow
:
'
shows/selectedShow
'
,
timeslots
:
'
shows/timeslots
'
,
notes
:
'
shows/notes
'
,
})
},
created
()
{
this
.
resetFilter
()
},
methods
:
{
// Apply the newly set filter parameters for our timeslot table
applyFilter
()
{
this
.
timeslotmeta
.
page
=
1
this
.
getTimeslots
(
this
.
dateStart
,
this
.
dateEnd
,
this
.
numSlots
)
},
// Reset the filter parameters for our timeslot table to config defaults
resetFilter
()
{
this
.
numSlots
=
process
.
env
.
VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS
this
.
dateStart
=
this
.
apiDate
(
new
Date
())
this
.
dateEnd
=
this
.
apiDate
(
new
Date
(
new
Date
().
getTime
()
+
process
.
env
.
VUE_APP_TIMESLOT_FILTER_DEFAULT_DAYS
*
86400000
))
this
.
timeslotmeta
.
page
=
1
this
.
getTimeslots
(
this
.
dateStart
,
this
.
dateEnd
,
this
.
numSlots
)
},
showHasSwitched
()
{
this
.
resetFilter
()
},
// Load a different page of timeslots for the timeslots table
timeslotsPage
(
page
)
{
if
(
this
.
timeslotmeta
.
page
!==
page
)
{
this
.
timeslotmeta
.
page
=
page
this
.
getTimeslots
(
this
.
dateStart
,
this
.
dateEnd
,
this
.
numSlots
,
(
page
-
1
)
*
this
.
numSlots
)
}
},
loadPlaylists
()
{
this
.
loadedLocal
.
playlists
=
false
let
uri
=
process
.
env
.
VUE_APP_API_TANK
+
'
shows/
'
+
this
.
selectedShow
.
slug
+
'
/playlists
'
axios
.
get
(
uri
,
{
withCredentials
:
true
,
headers
:
{
'
Authorization
'
:
'
Bearer
'
+
this
.
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
.
playlists
=
response
.
data
.
results
this
.
loadedLocal
.
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.
'
)
})
},
// Fetch timeslots for the current show and use filter variables if provided
getTimeslots
(
start
,
end
,
limit
,
offset
)
{
let
options
=
{}
let
dateRegex
=
new
RegExp
(
'
^
\\
d{4}-
\\
d{2}-
\\
d{2}$
'
)
if
(
dateRegex
.
test
(
start
))
{
options
.
start
=
start
}
if
(
dateRegex
.
test
(
end
))
{
options
.
end
=
end
}
if
(
!
isNaN
(
parseInt
(
limit
)))
{
options
.
limit
=
limit
}
if
(
!
isNaN
(
parseInt
(
offset
)))
{
options
.
offset
=
offset
}
this
.
$store
.
dispatch
(
'
shows/fetchTimeslots
'
,
{
id
:
this
.
selectedShow
.
id
,
callback
:
(
response
)
=>
{
if
(
!
isNaN
(
parseInt
(
limit
)))
{
this
.
timeslotmeta
.
count
=
response
.
data
.
count
this
.
timeslotmeta
.
next
=
response
.
data
.
next
this
.
timeslotmeta
.
previous
=
response
.
data
.
previous
this
.
timeslotmeta
.
perpage
=
parseInt
(
limit
)
}
else
{
this
.
timeslotmeta
.
count
=
response
.
data
.
length
this
.
timeslotmeta
.
next
=
null
this
.
timeslotmeta
.
previous
=
null
this
.
timeslotmeta
.
perpage
=
response
.
data
.
length
}
this
.
getNotes
()
},
...
options
})
},
getNotes
()
{
let
notes
=
[]
for
(
let
ts
of
this
.
timeslots
)
{
if
(
typeof
ts
.
note_id
===
'
number
'
)
{
notes
.
push
(
ts
.
note_id
)
}
}
this
.
$store
.
dispatch
(
'
shows/fetchNotes
'
,
{
id
:
this
.
selectedShow
.
id
,
notes
:
notes
})
},
// Open the modal to edit a timeslot's note, given its ID and schedule ID
editTimeslotNote
(
timeslotID
,
scheduleID
)
{
if
(
this
.
selectedShow
.
hosts
.
length
===
0
)
{
alert
(
'
There are no hosts set for this show!
\n\n
Notes can only be edited with active show hosts.
'
)
return
}