* test: agenda-neue - separate timezone controls assertions to allow retries * test: agenda-neue - use dom query selectors instead of first() / eq() * test: agenda-neue - playwright * test: fix playwright setup for ci * test: playwright - remove safari + fix timezone local * test: upload playwright report * test: playwright - fix trace upload * test * test: playwright - agenda search * test: fix startdate timezone * test: playwright - agenda table events * test: playwright - remove only filter * test: remove exit early flag * test: allow longer tests * test: agenda materials dialog * test: agenda filter by area/group * test: agenda calendar view * test: agenda settings * test: jump to day * test: fix agenda jump to day timezone parse * test: increase test timeout * test: remove fail fast * test: test sharding + increase delay * test: fixes * test: use macos image * test: fixes * test: agenda color assign + future + live meeting tests * test: agenda mobile tests * test: remainder of tests for playwright + optimizations * test: red line intersection accept close value * test: add delay for agenda search tests * chore: cleanup old tests + adapt build workflow * ci: fix build workflow * ci: fix build workflow order * fix: point to playwright floor plan images + readme
486 lines
13 KiB
Vue
486 lines
13 KiB
Vue
<template lang="pug">
|
|
n-drawer(v-model:show='isShown', placement='right', :width='panelWidth')
|
|
n-drawer-content.agenda-settings
|
|
template(#header)
|
|
span Agenda Settings
|
|
.agenda-settings-actions.d-flex.justify-content-end
|
|
n-dropdown(
|
|
:options='actionOptions'
|
|
size='large'
|
|
:show-arrow='true'
|
|
trigger='click'
|
|
@select='actionClick'
|
|
)
|
|
n-button.me-2(
|
|
ghost
|
|
color='#6c757d'
|
|
strong
|
|
)
|
|
i.bi.bi-three-dots-vertical
|
|
n-button(
|
|
ghost
|
|
color='gray'
|
|
strong
|
|
@click='close'
|
|
)
|
|
i.bi.bi-x-lg.me-2
|
|
span Close
|
|
|
|
.agenda-settings-content
|
|
|
|
n-divider(title-placement='left')
|
|
i.bi.bi-globe.me-2
|
|
small Timezone
|
|
n-button-group.mt-2#agenda-settings-tz-btn(style='justify-content: stretch; width: 100%;')
|
|
n-button(
|
|
style='flex-grow: 1;'
|
|
:type='agendaStore.isTimezoneMeeting ? `primary` : `default`'
|
|
@click='setTimezone(`meeting`)'
|
|
) Meeting
|
|
n-button(
|
|
style='flex-grow: 1;'
|
|
:type='agendaStore.isTimezoneLocal ? `primary` : `default`'
|
|
@click='setTimezone(`local`)'
|
|
) Local
|
|
n-button(
|
|
style='flex-grow: 1;'
|
|
:type='agendaStore.timezone === `UTC` ? `primary` : `default`'
|
|
@click='setTimezone(`UTC`)'
|
|
) UTC
|
|
n-select.mt-2#agenda-settings-tz-ddn(
|
|
v-model:value='agendaStore.timezone'
|
|
:options='timezones'
|
|
placeholder='Select Time Zone'
|
|
filterable
|
|
@update:value='() => { agendaStore.persistMeetingPreferences() }'
|
|
)
|
|
|
|
n-divider(title-placement='left')
|
|
i.bi.bi-sliders.me-2
|
|
small Display
|
|
//- .d-flex.align-items-center.mt-3
|
|
//- n-switch.me-3(v-model:value='agendaStore.listDayCollapse', disabled)
|
|
//- span.small Collapse Days by Default
|
|
#agenda-settings-tgl-colorlgd.d-flex.align-items-center.mt-3
|
|
n-switch.me-3(
|
|
v-model:value='agendaStore.colorLegendShown'
|
|
aria-label='Display Color Legend'
|
|
)
|
|
span.small.me-2 Display Color Legend
|
|
n-popover
|
|
template(#trigger)
|
|
i.bi.bi-info-circle
|
|
span Only displayed when a color is assigned to at least 1 event.
|
|
#agenda-settings-tgl-infonote.d-flex.align-items-center.mt-3
|
|
n-switch.me-3(
|
|
v-model:value='agendaStore.infoNoteShown'
|
|
aria-label='Display Current Meeting Info Note'
|
|
)
|
|
span.small.me-2 Display Current Meeting Info Note
|
|
n-popover
|
|
template(#trigger)
|
|
i.bi.bi-info-circle
|
|
span Any update to the note will result in this setting being turned back on.
|
|
#agenda-settings-tgl-eventicons.d-flex.align-items-center.mt-3
|
|
n-switch.me-3(
|
|
v-model:value='agendaStore.eventIconsShown'
|
|
aria-label='Display Event Icons'
|
|
)
|
|
span.small Display Event Icons
|
|
#agenda-settings-tgl-floorind.d-flex.align-items-center.mt-3
|
|
n-switch.me-3(
|
|
v-model:value='agendaStore.floorIndicatorsShown'
|
|
aria-label='Display Floor Indicators'
|
|
)
|
|
span.small Display Floor Indicators
|
|
#agenda-settings-tgl-groupind.d-flex.align-items-center.mt-3
|
|
n-switch.me-3(
|
|
v-model:value='agendaStore.areaIndicatorsShown'
|
|
aria-label='Display Group Area Indicators'
|
|
)
|
|
span.small.me-2 Display Group Area Indicators
|
|
n-popover
|
|
template(#trigger)
|
|
i.bi.bi-info-circle
|
|
span Will not be shown on smaller screens, regardless of this setting.
|
|
#agenda-settings-tgl-redline.d-flex.align-items-center.mt-3
|
|
n-switch.me-3(
|
|
v-model:value='agendaStore.redhandShown'
|
|
aria-label='Display Realtime Red Line'
|
|
)
|
|
span.small.me-2 Display Realtime Red Line
|
|
n-popover
|
|
template(#trigger)
|
|
i.bi.bi-info-circle
|
|
span Only shown during live events. Updated every 5 seconds.
|
|
#agenda-settings-tgl-boldertxt.d-flex.align-items-center.mt-3
|
|
n-switch.me-3(
|
|
v-model:value='agendaStore.bolderText'
|
|
aria-label='Use Bolder Text'
|
|
)
|
|
span.small.me-2 Use Bolder Text
|
|
|
|
n-divider(title-placement='left')
|
|
i.bi.bi-calendar3.me-2
|
|
small Calendar View
|
|
|
|
span.small.me-2 Default View
|
|
n-button-group.mt-2(style='justify-content: stretch; width: 100%;')
|
|
n-button(
|
|
style='flex-grow: 1;'
|
|
:type='agendaStore.defaultCalendarView === `week` ? `primary` : `default`'
|
|
@click='agendaStore.defaultCalendarView = `week`'
|
|
) Week
|
|
n-button(
|
|
style='flex-grow: 1;'
|
|
:type='agendaStore.defaultCalendarView === `day` ? `primary` : `default`'
|
|
@click='agendaStore.defaultCalendarView = `day`'
|
|
) Day
|
|
|
|
n-divider#agenda-settings-colors-header(title-placement='left')
|
|
i.bi.bi-palette.me-2
|
|
small Custom Colors / Tags
|
|
.agenda-settings-colors-row.d-flex.align-items-center.mt-3(v-for='(cl, idx) of state.colors')
|
|
n-color-picker.me-3(
|
|
:modes='[`hex`]'
|
|
:render-label='() => {}'
|
|
:show-alpha='false'
|
|
size='small'
|
|
:swatches='swatches'
|
|
v-model:value='cl.hex'
|
|
:aria-label='"Color for " + cl.tag'
|
|
)
|
|
n-input(
|
|
type='text'
|
|
v-model:value='cl.tag'
|
|
:placeholder='"Color " + (idx + 1)'
|
|
:aria-label='"Color Name " + (idx + 1)'
|
|
)
|
|
.agenda-settings-note.mt-3 #[strong Note:] You can hide a color by entering an empty name.
|
|
|
|
.agenda-settings-debug(v-if='agendaStore.debugTools')
|
|
n-divider(title-placement='left')
|
|
i.bi.bi-clock-history.me-2
|
|
small Override Local DateTime
|
|
n-date-picker(
|
|
v-model:value='state.currentDateTime'
|
|
type='datetime'
|
|
style='width: 100%;'
|
|
aria-label='Override Local DateTime'
|
|
)
|
|
template(#date-icon)
|
|
i.bi.bi-calendar-check
|
|
.agenda-settings-calcoffset
|
|
span Calculated Offset: {{ calcOffset }}
|
|
</template>
|
|
|
|
<script setup>
|
|
import { computed, h, onMounted, ref, reactive, watch } from 'vue'
|
|
import { DateTime } from 'luxon'
|
|
import cloneDeep from 'lodash/cloneDeep'
|
|
import debounce from 'lodash/debounce'
|
|
import { fileOpen } from 'browser-fs-access'
|
|
import FileSaver from 'file-saver'
|
|
import {
|
|
NButton,
|
|
NButtonGroup,
|
|
NColorPicker,
|
|
NDatePicker,
|
|
NDivider,
|
|
NDrawer,
|
|
NDrawerContent,
|
|
NDropdown,
|
|
NInput,
|
|
NPopover,
|
|
NSelect,
|
|
NSwitch,
|
|
useMessage
|
|
} from 'naive-ui'
|
|
|
|
import { useAgendaStore } from './store'
|
|
import timezones from '../shared/timezones'
|
|
|
|
// MESSAGE PROVIDER
|
|
|
|
const message = useMessage()
|
|
|
|
// STORES
|
|
|
|
const agendaStore = useAgendaStore()
|
|
|
|
// STATE
|
|
|
|
const isShown = ref(false)
|
|
const state = reactive({
|
|
currentDateTime: null,
|
|
colors: []
|
|
})
|
|
const swatches = [
|
|
'#0d6efd',
|
|
'#6610f2',
|
|
'#6f42c1',
|
|
'#d63384',
|
|
'#dc3545',
|
|
'#fd7e14',
|
|
'#ffc107',
|
|
'#198754',
|
|
'#20c997',
|
|
'#0dcaf0',
|
|
'#adb5bd',
|
|
'#000000'
|
|
]
|
|
const actionOptions = [
|
|
{
|
|
label: 'Export Configuration...',
|
|
key: 'export',
|
|
icon: () => h('i', { class: 'bi bi-box-arrow-down' })
|
|
},
|
|
{
|
|
label: 'Import Configuration...',
|
|
key: 'import',
|
|
icon: () => h('i', { class: 'bi bi-box-arrow-in-down' })
|
|
},
|
|
{
|
|
type: 'divider',
|
|
key: 'divider1'
|
|
},
|
|
{
|
|
label: 'Clear Color Assignments',
|
|
key: 'clearColors',
|
|
icon: () => h('i', { class: 'bi bi-palette' })
|
|
},
|
|
{
|
|
type: 'divider',
|
|
key: 'divider1'
|
|
},
|
|
{
|
|
label: 'Toggle Debugging Controls',
|
|
key: 'toggleDebug',
|
|
icon: () => h('i', { class: 'bi bi-tools' })
|
|
}
|
|
]
|
|
|
|
// COMPUTED
|
|
|
|
const calcOffset = computed(() => {
|
|
return agendaStore.nowDebugDiff ? JSON.stringify(agendaStore.nowDebugDiff.toObject()) : 'None'
|
|
})
|
|
const panelWidth = computed(() => {
|
|
return agendaStore.viewport > 500 ? 500 : agendaStore.viewport
|
|
})
|
|
|
|
// WATCHERS
|
|
|
|
watch(() => agendaStore.settingsShown, (newValue) => {
|
|
isShown.value = newValue
|
|
})
|
|
watch(isShown, (newValue) => {
|
|
agendaStore.$patch({ settingsShown: newValue })
|
|
})
|
|
watch(() => agendaStore.infoNoteShown, () => {
|
|
agendaStore.persistMeetingPreferences()
|
|
})
|
|
watch(() => state.colors, debounce(() => {
|
|
agendaStore.$patch({
|
|
colors: cloneDeep(state.colors)
|
|
})
|
|
}, 1000), { deep: true })
|
|
watch(() => state.currentDateTime, (newValue) => {
|
|
if (!newValue) {
|
|
agendaStore.$patch({ nowDebugDiff: null })
|
|
} else {
|
|
const newDiff = DateTime.now().diff(DateTime.fromMillis(newValue))
|
|
if (newDiff.as('minutes') <= 2 && newDiff.as('minutes') >= -2) { // Set to 0 if almost current time
|
|
agendaStore.$patch({ nowDebugDiff: null })
|
|
} else {
|
|
agendaStore.$patch({ nowDebugDiff: newDiff })
|
|
}
|
|
}
|
|
})
|
|
|
|
// METHODS
|
|
|
|
function close () {
|
|
isShown.value = false
|
|
}
|
|
|
|
async function actionClick (key) {
|
|
switch (key) {
|
|
/**
|
|
* EXPORT CONFIGURATION
|
|
*/
|
|
case 'export': {
|
|
try {
|
|
const configBlob = new Blob([
|
|
JSON.stringify({
|
|
areaIndicatorsShown: agendaStore.areaIndicatorsShown,
|
|
bolderText: agendaStore.bolderText,
|
|
colorLegendShown: agendaStore.colorLegendShown,
|
|
colors: agendaStore.colors,
|
|
defaultCalendarView: agendaStore.defaultCalendarView,
|
|
eventIconsShown: agendaStore.eventIconsShown,
|
|
floorIndicatorsShown: agendaStore.floorIndicatorsShown,
|
|
listDayCollapse: agendaStore.listDayCollapse,
|
|
redhandShown: agendaStore.redhandShown
|
|
}, null, 2)
|
|
], {
|
|
type: 'application/json;charset=utf-8'
|
|
})
|
|
FileSaver.saveAs(configBlob, 'agenda-settings.json')
|
|
} catch (err) {
|
|
console.warn(err)
|
|
message.error('Failed to generate JSON config for download.')
|
|
}
|
|
break
|
|
}
|
|
/**
|
|
* IMPORT CONFIGURATION
|
|
*/
|
|
case 'import': {
|
|
try {
|
|
const blob = await fileOpen({
|
|
mimeTypes: ['application/json'],
|
|
extensions: ['.json'],
|
|
startIn: 'downloads',
|
|
excludeAcceptAllOption: true
|
|
})
|
|
const configRaw = await blob.text()
|
|
const configJson = JSON.parse(configRaw)
|
|
if (!Array.isArray(configJson.colors) || configJson.colors.length !== agendaStore.colors.length) {
|
|
throw new Error('Config contains invalid colors array.')
|
|
}
|
|
agendaStore.$patch({
|
|
areaIndicatorsShown: configJson.areaIndicatorsShown === true,
|
|
bolderText: configJson.bolderText === true,
|
|
colorLegendShown: configJson.colorLegendShown === true,
|
|
colors: configJson.colors.map(c => ({
|
|
hex: c.hex || '#FF0000',
|
|
tag: c.tag || 'Unknown Color'
|
|
})),
|
|
defaultCalendarView: configJson.defaultCalendarView === 'day' ? 'day' : 'week',
|
|
eventIconsShown: configJson.eventIconsShown === true,
|
|
floorIndicatorsShown: configJson.floorIndicatorsShown === true,
|
|
listDayCollapse: configJson.listDayCollapse === true,
|
|
redhandShown: configJson.redhandShown === true
|
|
})
|
|
state.colors = cloneDeep(agendaStore.colors)
|
|
message.success('Config imported successfully.')
|
|
} catch (err) {
|
|
console.warn(err)
|
|
message.error('Failed to import JSON config.')
|
|
}
|
|
break
|
|
}
|
|
/**
|
|
* CLEAR COLOR ASSIGNMENTS
|
|
*/
|
|
case 'clearColors': {
|
|
agendaStore.colorAssignments = {}
|
|
agendaStore.persistMeetingPreferences()
|
|
message.info('All color assignments cleared.')
|
|
close()
|
|
break
|
|
}
|
|
/**
|
|
* TOGGLE DEBUG TOOLS
|
|
*/
|
|
case 'toggleDebug': {
|
|
agendaStore.$patch({ debugTools: !agendaStore.debugTools })
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
function setTimezone (tz) {
|
|
switch (tz) {
|
|
case 'meeting':
|
|
agendaStore.$patch({ timezone: agendaStore.meeting.timezone })
|
|
break
|
|
case 'local':
|
|
agendaStore.$patch({ timezone: DateTime.local().zoneName })
|
|
break
|
|
default:
|
|
agendaStore.$patch({ timezone: tz })
|
|
break
|
|
}
|
|
agendaStore.persistMeetingPreferences()
|
|
}
|
|
|
|
// MOUNTED
|
|
|
|
onMounted(() => {
|
|
state.currentDateTime = (agendaStore.nowDebugDiff ? DateTime.now().minus(agendaStore.nowDebugDiff) : DateTime.now()).toMillis()
|
|
state.colors = cloneDeep(agendaStore.colors)
|
|
})
|
|
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import "bootstrap/scss/functions";
|
|
@import "bootstrap/scss/variables";
|
|
|
|
.agenda-settings {
|
|
.n-drawer-header {
|
|
padding-top: 10px !important;
|
|
padding-bottom: 10px !important;
|
|
|
|
&__main {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
width: 100%;
|
|
}
|
|
}
|
|
|
|
.n-divider {
|
|
margin-top: 24px;
|
|
margin-bottom: 12px;
|
|
|
|
&:first-child {
|
|
margin-top: 0;
|
|
}
|
|
}
|
|
|
|
.n-color-picker {
|
|
width: 40px;
|
|
}
|
|
|
|
&-note {
|
|
background-color: $gray-100;
|
|
border-bottom: 1px solid $gray-200;
|
|
border-right: 1px solid $gray-200;
|
|
border-radius: 5px;;
|
|
padding: 6px 12px;
|
|
font-size: .8rem;
|
|
color: $gray-700;
|
|
text-shadow: 1px 1px 0 #FFF;
|
|
}
|
|
|
|
&-calcoffset {
|
|
padding: 5px 0 0 12px;
|
|
font-size: 11px;
|
|
}
|
|
|
|
&-debug {
|
|
margin-top: 25px;
|
|
border: 2px dotted $pink-500;
|
|
border-radius: 12px;
|
|
padding: 15px;
|
|
position: relative;
|
|
|
|
&::before {
|
|
content: 'DEBUG';
|
|
position: absolute;
|
|
background-color: $pink-500;
|
|
color: #FFF;
|
|
top: -13px;
|
|
right: 25px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
padding: 3px 8px;
|
|
border-radius: 12px;
|
|
}
|
|
}
|
|
}
|
|
</style>
|