feat: add slides / session materials links to session details modal (#4535)
* feat: add propose/upload slides button to session details modal * refactor: remove unneeded chaining operator * test: fix quotes around template string * feat: link to meeting materials page from AgendaDetailsModal * refactor: compute session details URL in JS instead of view * chore: restyle materials page link * test: fix test case to match changes to session modal
This commit is contained in:
parent
6f2114fb0c
commit
0ad293d6e9
|
@ -55,6 +55,17 @@ n-modal(v-model:show='modalShown')
|
|||
)
|
||||
i.bi.bi-journal-text.me-2
|
||||
span Notepad
|
||||
n-button.float-end(
|
||||
ghost
|
||||
color='gray'
|
||||
strong
|
||||
tag='a'
|
||||
:href='eventDetails.detailsUrl'
|
||||
target='_blank'
|
||||
aria-label='Materials page'
|
||||
)
|
||||
span.me-2 {{props.event.groupAcronym}} materials page
|
||||
i.bi.bi-box-arrow-up-right
|
||||
.detail-content
|
||||
.detail-title
|
||||
h6
|
||||
|
@ -95,19 +106,29 @@ n-modal(v-model:show='modalShown')
|
|||
:src='eventDetails.materialsUrl'
|
||||
)
|
||||
template(v-else-if='state.tab === `slides`')
|
||||
.text-center(v-if='state.isLoading')
|
||||
n-spin(description='Loading slides...')
|
||||
.text-center.p-3(v-else-if='!state.materials || !state.materials.slides || state.materials.slides.length < 1')
|
||||
span No slides submitted for this session.
|
||||
.list-group(v-else)
|
||||
a.list-group-item(
|
||||
v-for='slide of state.materials.slides'
|
||||
:key='slide.id'
|
||||
:href='slide.url'
|
||||
target='_blank'
|
||||
)
|
||||
i.bi.me-2(:class='`bi-filetype-` + slide.ext')
|
||||
span {{slide.title}}
|
||||
n-card(
|
||||
:bordered='false'
|
||||
size='small'
|
||||
)
|
||||
.text-center(v-if='state.isLoading')
|
||||
n-spin(description='Loading slides...')
|
||||
.text-center.p-3(v-else-if='!state.materials || !state.materials.slides || !state.materials.slides.decks || state.materials.slides.decks.length < 1')
|
||||
span No slides submitted for this session.
|
||||
.list-group(v-else)
|
||||
a.list-group-item(
|
||||
v-for='deck of state.materials.slides.decks'
|
||||
:key='deck.id'
|
||||
:href='deck.url'
|
||||
target='_blank'
|
||||
)
|
||||
i.bi.me-2(:class='`bi-filetype-` + deck.ext')
|
||||
span {{deck.title}}
|
||||
template(#action, v-if='state.materials.slides.actions')
|
||||
n-button(
|
||||
v-for='action of state.materials.slides.actions'
|
||||
tag='a'
|
||||
:href='action.url'
|
||||
) {{action.label}}
|
||||
template(v-else)
|
||||
.text-center(v-if='state.isLoading')
|
||||
n-spin(description='Loading minutes...')
|
||||
|
@ -184,9 +205,10 @@ const eventDetails = computed(() => {
|
|||
title: props.event.type === 'regular' ? `${props.event.groupName} (${props.event.acronym})` : props.event.name,
|
||||
showAgenda: props.event.flags.showAgenda,
|
||||
materialsUrl: materialsUrl,
|
||||
detailsUrl: `/meeting/${agendaStore.meeting.number}/session/${props.event.acronym}/`,
|
||||
tarUrl: `/meeting/${agendaStore.meeting.number}/agenda/${props.event.acronym}-drafts.tgz`,
|
||||
pdfUrl: `/meeting/${agendaStore.meeting.number}/agenda/${props.event.acronym}-drafts.pdf`,
|
||||
notepadUrl: `https://notes.ietf.org/notes-ietf-${agendaStore.meeting.number}-${props.event.type === 'plenary' ? 'plenary' : props.event.acronym}`
|
||||
notepadUrl: `https://notes.ietf.org/notes-ietf-${agendaStore.meeting.number}-${props.event.type === 'plenary' ? 'plenary' : props.event.acronym}`,
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -219,7 +241,10 @@ async function fetchSessionMaterials () {
|
|||
state.isLoading = true
|
||||
|
||||
try {
|
||||
const resp = await fetch(`/api/meeting/session/${props.event.sessionId}/materials`, { credentials: 'omit' })
|
||||
const resp = await fetch(
|
||||
`/api/meeting/session/${props.event.sessionId}/materials`,
|
||||
{ credentials: 'include' }
|
||||
)
|
||||
if (!resp.ok) {
|
||||
throw new Error(resp.statusText)
|
||||
}
|
||||
|
|
|
@ -1642,10 +1642,32 @@ def api_get_session_materials (request, session_id=None):
|
|||
session = get_object_or_404(Session,pk=session_id)
|
||||
|
||||
minutes = session.minutes()
|
||||
slides_actions = []
|
||||
if can_manage_session_materials(request.user, session.group, session):
|
||||
slides_actions.append({
|
||||
'label': 'Upload slides',
|
||||
'url': reverse(
|
||||
'ietf.meeting.views.upload_session_slides',
|
||||
kwargs={'num': session.meeting.number, 'session_id': session.pk},
|
||||
),
|
||||
})
|
||||
elif not session.is_material_submission_cutoff():
|
||||
slides_actions.append({
|
||||
'label': 'Propose slides',
|
||||
'url': reverse(
|
||||
'ietf.meeting.views.propose_session_slides',
|
||||
kwargs={'num': session.meeting.number, 'session_id': session.pk},
|
||||
),
|
||||
})
|
||||
else:
|
||||
pass # no action available if it's past cutoff
|
||||
|
||||
return JsonResponse({
|
||||
"url": session.agenda().get_href(),
|
||||
"slides": list(map(agenda_extract_slide, session.slides())),
|
||||
"slides": {
|
||||
"decks": list(map(agenda_extract_slide, session.slides())),
|
||||
"actions": slides_actions,
|
||||
},
|
||||
"minutes": {
|
||||
"id": minutes.id,
|
||||
"title": minutes.title,
|
||||
|
@ -1700,7 +1722,10 @@ def agenda_extract_schedule (item):
|
|||
"audioStream": item.timeslot.location.audio_stream_url() if item.timeslot.location else "",
|
||||
"webex": item.timeslot.location.webex_url() if item.timeslot.location else "",
|
||||
"onsiteTool": item.timeslot.location.onsite_tool_url() if item.timeslot.location else "",
|
||||
"calendar": reverse('ietf.meeting.views.agenda_ical', kwargs={'num': item.schedule.meeting.number, 'session_id': item.session.id, })
|
||||
"calendar": reverse(
|
||||
'ietf.meeting.views.agenda_ical',
|
||||
kwargs={'num': item.schedule.meeting.number, 'session_id': item.session.id},
|
||||
),
|
||||
}
|
||||
# "slotType": {
|
||||
# "slug": item.slot_type.slug
|
||||
|
|
|
@ -368,12 +368,18 @@ test.describe('past - desktop', () => {
|
|||
const materialsUrl = (new URL(event.agenda.url)).pathname
|
||||
const materialsInfo = {
|
||||
url: event.agenda.url,
|
||||
slides: _.times(5, idx => ({
|
||||
id: 100000 + idx,
|
||||
title: faker.commerce.productName(),
|
||||
url: `/meeting/${meetingData.meeting.number}/materials/slides-${meetingData.meeting.number}-${event.acronym}-${faker.internet.domainWord()}`,
|
||||
ext: ['pdf', 'html', 'md', 'txt', 'pptx'][idx]
|
||||
})),
|
||||
slides: {
|
||||
decks: _.times(5, idx => ({
|
||||
id: 100000 + idx,
|
||||
title: faker.commerce.productName(),
|
||||
url: `/meeting/${meetingData.meeting.number}/materials/slides-${meetingData.meeting.number}-${event.acronym}-${faker.internet.domainWord()}`,
|
||||
ext: ['pdf', 'html', 'md', 'txt', 'pptx'][idx]
|
||||
})),
|
||||
actions: [{
|
||||
label: 'Propose slides',
|
||||
url: `/meeting/${meetingData.meeting.number}/session/${event.sessionId}/propose_slides`
|
||||
}]
|
||||
},
|
||||
minutes: {
|
||||
ext: 'md',
|
||||
id: 123456,
|
||||
|
@ -427,13 +433,16 @@ test.describe('past - desktop', () => {
|
|||
await navLocator.nth(1).click()
|
||||
await expect(navLocator.nth(1)).toHaveClass(/active/)
|
||||
await expect(navLocator.first()).not.toHaveClass(/active/)
|
||||
const slidesLocator = page.locator('.agenda-eventdetails .detail-text > .list-group > .list-group-item')
|
||||
await expect(slidesLocator).toHaveCount(materialsInfo.slides.length)
|
||||
for (let idx = 0; idx < materialsInfo.slides.length; idx++) {
|
||||
await expect(slidesLocator.nth(idx)).toHaveAttribute('href', materialsInfo.slides[idx].url)
|
||||
await expect(slidesLocator.nth(idx).locator('.bi')).toHaveClass(new RegExp(`bi-filetype-${materialsInfo.slides[idx].ext}`))
|
||||
await expect(slidesLocator.nth(idx).locator('span')).toContainText(materialsInfo.slides[idx].title)
|
||||
const slideDecksLocator = page.locator('.agenda-eventdetails .detail-text .n-card__content > .list-group > .list-group-item')
|
||||
await expect(slideDecksLocator).toHaveCount(materialsInfo.slides.decks.length)
|
||||
for (let idx = 0; idx < materialsInfo.slides.decks.length; idx++) {
|
||||
await expect(slideDecksLocator.nth(idx)).toHaveAttribute('href', materialsInfo.slides.decks[idx].url)
|
||||
await expect(slideDecksLocator.nth(idx).locator('.bi')).toHaveClass(new RegExp(`bi-filetype-${materialsInfo.slides.decks[idx].ext}`))
|
||||
await expect(slideDecksLocator.nth(idx).locator('span')).toContainText(materialsInfo.slides.decks[idx].title)
|
||||
}
|
||||
const slideActionButtonLocator = page.locator('.agenda-eventdetails .detail-text .n-card__action > a')
|
||||
await expect(slideActionButtonLocator).toHaveCount(1)
|
||||
await expect(slideActionButtonLocator.first().locator('span')).toContainText('Propose slides')
|
||||
// Minutes Tab
|
||||
await navLocator.last().click()
|
||||
await expect(navLocator.last()).toHaveClass(/active/)
|
||||
|
@ -442,7 +451,7 @@ test.describe('past - desktop', () => {
|
|||
// Footer Buttons
|
||||
const hedgeDocLink = `https://notes.ietf.org/notes-ietf-${meetingData.meeting.number}-${event.type === 'plenary' ? 'plenary' : event.acronym}`
|
||||
const footerBtnsLocator = page.locator('.agenda-eventdetails .detail-action > a')
|
||||
await expect(footerBtnsLocator).toHaveCount(3)
|
||||
await expect(footerBtnsLocator).toHaveCount(4)
|
||||
await expect(footerBtnsLocator.first()).toContainText('Download as tarball')
|
||||
await expect(footerBtnsLocator.first()).toHaveAttribute('href', `/meeting/${meetingData.meeting.number}/agenda/${event.acronym}-drafts.tgz`)
|
||||
await expect(footerBtnsLocator.nth(1)).toContainText('Download as PDF')
|
||||
|
@ -483,7 +492,7 @@ test.describe('past - desktop', () => {
|
|||
await expect(page.locator('.agenda-eventdetails')).toBeVisible()
|
||||
// Slides Tab
|
||||
await page.locator('.agenda-eventdetails .detail-nav > a').nth(1).click()
|
||||
await expect(page.locator('.agenda-eventdetails .detail-text')).toContainText('No slides submitted for this session.')
|
||||
await expect(page.locator('.agenda-eventdetails .detail-text .n-card__content')).toContainText('No slides submitted for this session.')
|
||||
// Minutes Tab
|
||||
await page.locator('.agenda-eventdetails .detail-nav > a').nth(2).click()
|
||||
await expect(page.locator('.agenda-eventdetails .detail-text')).toContainText('No minutes submitted for this session.')
|
||||
|
|
Loading…
Reference in a new issue