* feat: agenda page in vue (wip) * feat: scroll to agenda day * fix: vue 3 composition api + eslint settings * fix: agenda day scroll match indicator * fix: convert vite deps to yarn * fix: missing lodash + legacy build step * fix: agenda - move calendar into drawer * fix: improve agenda filter UI * fix: download ics + move agenda into own component * feat: use fullcalendar for agenda calendar view (wip) * feat: add events to agenda calendar * feat: agenda filter UI improvements * feat: agenda add to calendar dropdown * feat: agenda calendar filter + timezone + event coloring * feat: agenda calendar color improvements * chore: exclude dist-neue from git * feat: agenda calendar event modal * fix: rebuild yarn deps * chore: add run migration task to vscode * fix: agenda buttons display flag * feat: agenda event modal component * feat: show calendar event quick info on hover * fix: clear calendar quick info on timezone change * feat: agenda list view improvements * feat: agenda list row coloring * feat: agenda list note * feat: agenda list icons for office hours + hackathon * fix: agenda top links * refactor: use pinia as store for agenda components * feat: agenda jump to now * fix: agenda mobile improvements * feat: agenda search * feat: agenda search improvements * feat: agenda event recordings buttons for post-meeting * fix: agenda switch to meeting timezone on load * feat: agenda pre & live session buttons * fix: remove agenda utc + personalize links in top menu * feat: add pre-vue loading state on page load * feat: filter from agenda picker mode * fix: agenda UI improvements * fix: django-vite non-dev mode * chore: update yarn dependencies for vue + vite * feat: agenda settings panel + UI improvements * feat: agenda settings colors + import/export feature * feat: agenda color assignments + responsive UI improvements * feat: agenda realtime red line + debug datetime offset * feat: agenda add aria labels for settings * feat: add new agenda path + pages/menu * fix: bring base/menu.html up to main * fix: agenda various fixes * test: add new agenda item to meetings menu for item count * chore: restore devcontainer extensions list * fix: agenda UI improvements + montserrat default font * feat: agenda bolder text + hide event icons options * feat: agenda warning badge * fix: agenda various UI improvements + intersectionObserver fix * feat: agenda floorplan page + various UI improvements * feat: agenda floor plan pin * feat: view floor plan room from agenda * feat: agenda floor plan mobile optimization * feat: adjust calendar options + default calendar view in settings * feat: agenda persist picked events + change base font only on new agenda page * feat: agenda mobile view optimizations * fix: add .vite to cached volumes * fix: mobile view for filters, calendar, settings panels * test: upgrade cypress existing tests to work on bs5 + update dependencies * fix: use named url patterns to avoid hardcoded URLs. Add rudimentary test coverage for the neue views. Co-authored-by: Robert Sparks <rjsparks@nostrum.com>
266 lines
6.5 KiB
Vue
266 lines
6.5 KiB
Vue
<template lang="pug">
|
|
.agenda-quickaccess
|
|
n-affix(:trigger-top='82')
|
|
.card.shadow-sm
|
|
.card-body
|
|
n-button(
|
|
block
|
|
type='success'
|
|
size='large'
|
|
strong
|
|
@click='agendaStore.$patch({ filterShown: true })'
|
|
)
|
|
i.bi.bi-funnel.me-2
|
|
span {{ shortMode ? 'Filter...' : 'Filter Areas + Groups...' }}
|
|
n-badge.ms-2(:value='agendaStore.selectedCatSubs.length', processing)
|
|
n-button.mt-2(
|
|
v-if='!agendaStore.pickerMode'
|
|
block
|
|
secondary
|
|
type='success'
|
|
size='large'
|
|
strong
|
|
@click='pickerStart'
|
|
)
|
|
i.bi.bi-ui-checks.me-2
|
|
span {{ shortMode ? 'Pick...' : 'Pick Sessions...' }}
|
|
.agenda-quickaccess-btnrow(v-else)
|
|
.agenda-quickaccess-btnrow-title {{ shortMode ? 'Sess. Pick' : 'Session Selection' }}
|
|
n-button.me-1(
|
|
v-if='!agendaStore.pickerModeView'
|
|
type='success'
|
|
size='large'
|
|
strong
|
|
@click='pickerApply'
|
|
)
|
|
i.bi.bi-check2-square.me-2
|
|
span Apply
|
|
n-button.me-1(
|
|
v-else
|
|
color='#6f42c1'
|
|
size='large'
|
|
strong
|
|
@click='pickerModify'
|
|
)
|
|
i.bi.bi-pencil-square.me-2
|
|
span Modify
|
|
n-button.ms-1(
|
|
secondary
|
|
color='#666'
|
|
size='large'
|
|
strong
|
|
@click='pickerDiscard'
|
|
)
|
|
i.bi.bi-x-square.me-2
|
|
span Discard
|
|
n-divider: small.text-muted Calendar
|
|
n-button.mt-2(
|
|
block
|
|
color='#6c757d'
|
|
size='large'
|
|
strong
|
|
@click='agendaStore.$patch({ calendarShown: true })'
|
|
)
|
|
i.bi.bi-calendar3.me-2
|
|
span {{ shortMode ? 'Cal View' : 'Calendar View' }}
|
|
n-dropdown(
|
|
:options='downloadIcsOptions'
|
|
size='large'
|
|
:show-arrow='true'
|
|
trigger='click'
|
|
@select='downloadIcs'
|
|
)
|
|
n-button.mt-2(
|
|
block
|
|
secondary
|
|
color='#6c757d'
|
|
size='large'
|
|
strong
|
|
)
|
|
i.bi.bi-calendar-check.me-2
|
|
span {{ shortMode ? '.ics' : 'Add to your calendar...' }}
|
|
template(v-if='agendaStore.meetingDays.length > 0')
|
|
n-divider: small.text-muted Jump to...
|
|
ul.nav.nav-pills.flex-column.small
|
|
li.nav-item(v-if='agendaStore.isMeetingLive')
|
|
a.nav-link(
|
|
href='#now'
|
|
@click='scrollToNow'
|
|
)
|
|
i.bi.bi-arrow-right-short.d-none.d-xxl-inline.me-2
|
|
span Now
|
|
li.nav-item(v-for='day of agendaStore.meetingDays')
|
|
a.nav-link(
|
|
:class='agendaStore.dayIntersectId === day.slug ? `active` : ``'
|
|
:href='`#slot-` + day.slug'
|
|
@click='scrollToDay(day.slug, $event)'
|
|
)
|
|
i.bi.bi-arrow-right-short.d-none.d-xxl-inline.me-2
|
|
span {{day.label}}
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, h } from 'vue'
|
|
import { DateTime } from 'luxon'
|
|
import {
|
|
NAffix,
|
|
NBadge,
|
|
NButton,
|
|
NDivider,
|
|
NDropdown,
|
|
useMessage
|
|
} from 'naive-ui'
|
|
|
|
import { useAgendaStore } from './store'
|
|
|
|
// MESSAGE PROVIDER
|
|
|
|
const message = useMessage()
|
|
|
|
// STORES
|
|
|
|
const agendaStore = useAgendaStore()
|
|
|
|
// Download Ics Options
|
|
|
|
const downloadIcsOptions = [
|
|
{
|
|
label: 'Subscribe... (webcal)',
|
|
key: 'subscribe',
|
|
icon: () => h('i', { class: 'bi bi-calendar-week text-blue' })
|
|
},
|
|
{
|
|
label: 'Download... (.ics)',
|
|
key: 'download',
|
|
icon: () => h('i', { class: 'bi bi-arrow-down-square' })
|
|
}
|
|
]
|
|
|
|
// COMPUTED
|
|
|
|
const shortMode = computed(() => {
|
|
return agendaStore.viewport < 1350
|
|
})
|
|
|
|
// METHODS
|
|
|
|
function pickerStart () {
|
|
agendaStore.$patch({ pickerMode: true })
|
|
}
|
|
function pickerApply () {
|
|
agendaStore.$patch({ pickerModeView: true })
|
|
agendaStore.persistMeetingPreferences()
|
|
}
|
|
function pickerModify () {
|
|
agendaStore.$patch({ pickerModeView: false })
|
|
}
|
|
function pickerDiscard () {
|
|
agendaStore.$patch({ pickerMode: false })
|
|
}
|
|
|
|
function downloadIcs (key) {
|
|
message.loading('Generating calendar file... Download will begin shortly.')
|
|
let icsUrl = ''
|
|
if (agendaStore.pickerMode) {
|
|
const sessionKeywords = agendaStore.scheduleAdjusted.map(s => s.sessionKeyword)
|
|
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics?show=${sessionKeywords.join(',')}`
|
|
} else if (agendaStore.selectedCatSubs.length > 0) {
|
|
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics?show=${agendaStore.selectedCatSubs.join(',')}`
|
|
} else {
|
|
icsUrl = `/meeting/${agendaStore.meeting.number}/agenda.ics`
|
|
}
|
|
if (key === 'subscribe') {
|
|
window.location.assign(`webcal://${window.location.host}${icsUrl}`)
|
|
} else {
|
|
window.location.assign(icsUrl)
|
|
}
|
|
}
|
|
|
|
function scrollToDay (dayId, ev) {
|
|
ev.preventDefault()
|
|
document.getElementById(`agenda-day-${dayId}`)?.scrollIntoView(true)
|
|
}
|
|
|
|
function scrollToNow (ev) {
|
|
ev.preventDefault()
|
|
|
|
const lastEventId = agendaStore.findCurrentEventId()
|
|
|
|
if (lastEventId) {
|
|
document.getElementById(`agenda-rowid-${lastEventId}`)?.scrollIntoView(true)
|
|
} else {
|
|
message.warning('There is no event happening right now.')
|
|
}
|
|
}
|
|
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.agenda-quickaccess {
|
|
width: 300px;
|
|
|
|
@media screen and (max-width: 1350px) {
|
|
width: 150px !important;
|
|
}
|
|
|
|
.card {
|
|
width: 300px;
|
|
|
|
@media screen and (max-width: 1350px) {
|
|
width: 150px;
|
|
|
|
.card-body {
|
|
padding: .5rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
&-btnrow {
|
|
border: 1px solid #CCC;
|
|
padding: 8px 6px 6px 6px;
|
|
border-radius: 5px;
|
|
display: flex;
|
|
justify-content: stretch;
|
|
position: relative;
|
|
text-align: center;
|
|
margin-top: 12px;
|
|
|
|
@media screen and (max-width: 1350px) {
|
|
flex-direction: column;
|
|
}
|
|
|
|
&-title {
|
|
position: absolute;
|
|
top: -8px;
|
|
font-size: 9px;
|
|
font-weight: 600;
|
|
color: #999;
|
|
left: 50%;
|
|
padding: 0 5px;
|
|
background-color: #FFF;
|
|
transform: translate(-50%, 0);
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
button {
|
|
flex: 1;
|
|
|
|
@media screen and (max-width: 1350px) {
|
|
padding: 12px 0;
|
|
margin-left: 0 !important;
|
|
margin-right: 0 !important;
|
|
|
|
& + button {
|
|
margin-top: 6px;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
.n-divider {
|
|
margin-top: 15px;
|
|
margin-bottom: 15px;
|
|
}
|
|
}
|
|
</style>
|