<template> <ASection :title="t('showTimeslots.title')"> <template #header> <Popover class="tw-relative"> <!-- TODO: this span is on purpose because headless ui seems to interfere with vue compat --> <PopoverButton as="span"> <button type="button" class="btn btn-default"> <icon-system-uicons-filter /> {{ t('showTimeslots.filter') }} </button> </PopoverButton> <PopoverPanel :unmount="false" class="tw-absolute tw-mt-3 tw-right-0 tw-w-max tw-max-w-sm tw-top-full tw-z-20 tw-bg-white tw-p-6 tw-shadow-lg tw-rounded-lg" > <TimeSlotFilter v-model:number-of-timeslots="limit" v-model:start-date="formattedStartDate" v-model:end-date="formattedEndDate" /> </PopoverPanel> </Popover> </template> <div class="aura-table-wrapper"> <table ref="tableEl" class="aura-table"> <thead class="tw-sticky tw-top-0 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-text-right">{{ t('emissionTable.actions') }}</th> </tr> </thead> <tbody> <template v-for="timeslot in result.items" :key="timeslot.id"> <TimeSlotRow class="tw-transition hover:tw-bg-gray-50" :timeslot="timeslot" /> </template> <template v-if="!hasTimeslots"> <tr> <td colspan="6"> <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-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" > <PaginationRange v-if="result.count > 0" :pagination-data="result" /> <Pagination v-model="page" class="tw-ml-auto" :items-per-page="result.itemsPerPage" :count="result.count" /> </div> </div> </ASection> </template> <script lang="ts" setup> import { computed, ref, watchEffect } from 'vue' import { Popover, PopoverPanel, PopoverButton } from '@headlessui/vue' import { useI18n } from '@/i18n' 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 { Show } from '@/types' import { useTimeSlotStore } from '@/stores/timeslots' import { usePaginatedList } from '@rokoli/bnb/drf' import { useFormattedISODate } from '@/util' import { addDays } from 'date-fns' const DEFAULT_TIMESLOT_LIMIT = parseInt(import.meta.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_NUMSLOTS) const DEFAULT_DATE_OFFSET = parseInt(import.meta.env.VUE_APP_TIMESLOT_FILTER_DEFAULT_DAYS) defineOptions({ compatConfig: { MODE: 3 }, ATTR_FALSE_VALUE: false, }) const props = defineProps<{ show: Show }>() const { t } = useI18n() const timeslotStore = useTimeSlotStore() const page = ref(1) const limit = ref(DEFAULT_TIMESLOT_LIMIT) const startDate = ref(new Date()) const formattedStartDate = useFormattedISODate(startDate) const endDate = ref(addDays(startDate.value, DEFAULT_DATE_OFFSET)) const formattedEndDate = useFormattedISODate(endDate) const tableEl = ref<HTMLTableElement>() const hasPageBeenModified = ref(false) const query = computed( () => new URLSearchParams({ showIds: props.show.id.toString(), start: formattedStartDate.value, end: formattedEndDate.value, order: 'start', }), ) const { result } = usePaginatedList(timeslotStore.listIsolated, page, limit, { query, events: timeslotStore.events, }) const hasTimeslots = computed(() => result.value.count > 0) watchEffect(() => { if (page.value !== 1) { hasPageBeenModified.value = true } if (hasPageBeenModified.value && tableEl.value) { tableEl.value.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' }) } }) </script>