Newer
Older
<ASection :title="t('showTimeslots.title')">
<template #header>
<TimeSlotFilter
v-model:start-date="formattedLocalStartTime"
class="tw-flex tw-gap-3 tw-items-center tw-ml-auto"
/>
<div class="aura-table-wrapper">
<table ref="tableEl" class="aura-table">
<thead class="tw-sticky tw-top-0 tw-z-10 tw-bg-white">
<tr>
<th>{{ t('emissionTable.title') }}</th>
<th>{{ t('emissionTable.start') }}</th>
<th>{{ t('emissionTable.duration') }}</th>
<th>{{ t('emissionTable.playlist') }}</th>
<th class="tw-p-0" />

Konrad Mohrfeldt
committed
<template v-for="timeslot in result.items" :key="timeslot.id">
<TimeSlotRow
class="tw-transition hover:tw-bg-gray-50"
:timeslot="timeslot"
:is-next-up="nextBroadcastedTimeslot?.id === timeslot.id"
:is-on-air="onAirTimeslot?.id === timeslot.id"
/>
</template>
<template v-if="!hasTimeslots">
<tr>
<p class="tw-text-center tw-p-3 tw-m-0">
{{ t('showTimeslots.noTimeslotsScheduled') }}
</p>
</td>
</tr>
</template>
</tbody>
</table>
<div
class="tw-flex tw-flex-wrap tw-gap-3 tw-items-center tw-px-6 tw-mt-3 tw-py-3 tw-border-0 tw-border-t tw-border-solid tw-border-gray-200 empty:tw-hidden"

Konrad Mohrfeldt
committed
<PaginationRange v-if="result.count > 0" :pagination-data="result" />
<FormGroup v-slot="attrs" class="tw-ml-auto tw-m-0 last:tw-mr-0">
<label class="tw-flex tw-items-center tw-gap-3 tw-m-0">
<span>{{ t('showTimeslots.numberOfSlots') }}</span>
<input
v-model.lazy="limit"
v-bind="attrs"
type="number"
min="1"
step="1"
class="tw-w-[80px] tw-self-center"
/>
</label>
</FormGroup>
<Pagination v-model="page" :items-per-page="result.itemsPerPage" :count="result.count" />
</ASection>
</template>
<script lang="ts" setup>
import { usePaginatedList } from '@rokoli/bnb/drf'
import { useNow, useStorage } from '@vueuse/core'
import { formatISO, roundToNearestMinutes } from 'date-fns'
import { computed, ref, watch, watchEffect } from 'vue'
import { useI18n } from '@/i18n'
import { Show } from '@/types'
import { useFormattedISODate, useQuery } from '@/util'
import { useTimeSlotStore } from '@/stores/timeslots'
import TimeSlotFilter from './TimeSlotFilter.vue'
import TimeSlotRow from '@/components/shows/TimeSlotRow.vue'
import Pagination from '@/components/generic/Pagination.vue'
import PaginationRange from '@/components/generic/PaginationRange.vue'
import ASection from '@/components/generic/ASection.vue'
import FormGroup from '@/components/generic/FormGroup.vue'

Konrad Mohrfeldt
committed
const DEFAULT_TIMESLOT_LIMIT = parseInt(
import.meta.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS ?? 5,
defineOptions({
compatConfig: { MODE: 3 },
ATTR_FALSE_VALUE: false,
})

Konrad Mohrfeldt
committed
const props = defineProps<{
show: Show
}>()

Konrad Mohrfeldt
committed
const timeslotStore = useTimeSlotStore()
const now = useNow({ interval: 60_000 })
const direction = ref<'future' | 'past'>('future')
const limit = useStorage('aura:timeslotList:timeslotsPerPage', DEFAULT_TIMESLOT_LIMIT)
const startTime = ref(roundToNearestMinutes(new Date()))
const formattedStartTime = useFormattedISODate(startTime)
const formattedLocalStartTime = useFormattedISODate(startTime, now, { stripOffset: true })
const tableEl = ref<HTMLTableElement>()
const hasPageBeenModified = ref(false)

Konrad Mohrfeldt
committed
const { result } = usePaginatedList(timeslotStore.listIsolated, page, limit, {
events: timeslotStore.events,
query: useQuery(() => ({
showIds: props.show.id,
endsAfter: direction.value === 'future' ? formattedStartTime.value : undefined,
endsBefore: direction.value === 'past' ? formattedStartTime.value : undefined,
order: direction.value === 'future' ? 'start' : '-start',

Konrad Mohrfeldt
committed
})
const hasTimeslots = computed(() => result.value.count > 0)
const onAirTimeslot = computed(() => {
const _now = formatISO(now.value)
return result.value.items.find((t) => t.start <= _now && _now < t.end) ?? null
})
const currentResultTime = ref(new Date())
const { result: currentResult } = usePaginatedList(timeslotStore.listIsolated, 1, 1, {
events: timeslotStore.events,
query: useQuery(() => ({
showIds: props.show.id,
startsAfter: formatISO(currentResultTime.value),
order: 'start',
})),
})
const nextBroadcastedTimeslot = computed(() => currentResult.value.items[0] ?? null)
watch([onAirTimeslot, useNow({ interval: 15 * 60_000 })], () => {
// reload from usePaginatedList doesn’t cut it, because we actually need to change the query
currentResultTime.value = new Date()
})
watchEffect(() => {
if (page.value !== 1) {
hasPageBeenModified.value = true
}
if (hasPageBeenModified.value && tableEl.value) {
tableEl.value.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' })
}
})
</script>