Skip to content
Snippets Groups Projects
Commit f9dc6fef authored by Konrad Mohrfeldt's avatar Konrad Mohrfeldt :koala:
Browse files

feat: rework schedule overview

refs #121
parent 3b936ba0
No related branches found
No related tags found
No related merge requests found
Pipeline #3486 passed
<template>
<div
v-if="relevantSchedules.length > 0"
:class="{
'show-schedules': true,
expandable: isExpandable,
collapsed: isExpandable && isCollapsed,
}"
@click="isCollapsed = !isCollapsed"
>
<table class="table b-table table-striped border">
<thead>
<tr>
<th class="top-header">
{{ uppercaseFirst(t('showSchedules.schedule')) }}
</th>
<th class="top-header text-right font-weight-normal">
{{ relevantSchedules.length }}
{{
relevantSchedules.length === 1
? t('showSchedules.schedule')
: t('showSchedules.schedules')
}}
</th>
</tr>
</thead>
<tbody>
<tr v-for="schedule in relevantSchedules" :key="schedule.id">
<td>
{{ renderRruleForSchedule(schedule) }}
</td>
<td class="text-right">
<template v-if="schedule.firstDate === schedule.lastDate">
{{ prettyDate(parseISO(schedule.firstDate)) }} <br />
</template>
{{ prettyHours(schedule.startTime) }} - {{ prettyHours(schedule.endTime) }}
</td>
</tr>
</tbody>
</table>
<div v-if="isExpandable" class="collapser">
{{ t('showSchedules.collapse') }}
</div>
</div>
<div v-else class="border p-4 mb-4">
{{ t('showSchedules.noSchedulesAvailable') }}
</div>
<section>
<header class="tw-flex tw-items-center tw-gap-6 tw-mt-12 tw-mb-4">
<SectionTitle>{{ t('showSchedules.title') }}</SectionTitle>
<button
v-if="collapseSchedules"
type="button"
class="btn btn-default"
aria-controls="schedule-collapsable"
:aria-expanded="!isCollapsed"
@click="isCollapsed = !isCollapsed"
>
{{ t(isCollapsed ? 'showSchedules.showAll' : 'showSchedules.hide') }}
</button>
</header>
<Collapse
id="schedule-collapsable"
v-model:is-collapsed="isCollapsed"
class="tw-mb-12"
peek="10rem"
>
<div class="aura-table-wrapper tw-mb-0">
<table class="aura-table">
<thead>
<tr>
<th scope="col">
{{ t('showSchedules.rhythm') }}
</th>
<th scope="col">
{{ t('showSchedules.firstBroadcast') }}
</th>
<th scope="col">
{{ t('showSchedules.lastBroadcast') }}
</th>
<th scope="col" class="tw-text-right">
{{ t('showSchedules.times') }}
</th>
</tr>
</thead>
<tbody>
<tr
v-for="schedule in relevantSchedules"
:key="schedule.id"
:aria-label="
t(
schedule.lastDate
? 'showSchedules.scheduleDescriptionFinite'
: 'showSchedules.scheduleDescription',
{
rhythm: renderRruleForSchedule(schedule),
startDate: prettyDate(parseISO(schedule.firstDate)),
endDate: schedule.lastDate ? prettyDate(parseISO(schedule.lastDate)) : '',
startTime: prettyHours(schedule.startTime),
endTime: prettyHours(schedule.endTime),
},
)
"
>
<th scope="row">
{{ renderRruleForSchedule(schedule) }}
</th>
<td :colspan="schedule.firstDate === schedule.lastDate ? 2 : 1">
{{ prettyDate(parseISO(schedule.firstDate)) }}
</td>
<td v-if="schedule.firstDate !== schedule.lastDate">
<template v-if="schedule.lastDate">
{{ prettyDate(parseISO(schedule.lastDate)) }}
</template>
<template v-else> offen </template>
</td>
<td class="tw-text-right">
{{ prettyHours(schedule.startTime) }} - {{ prettyHours(schedule.endTime) }}
</td>
</tr>
<tr v-if="relevantSchedules.length === 0">
<td colspan="2">
<p>{{ t('showSchedules.noSchedulesAvailable') }}</p>
</td>
</tr>
</tbody>
</table>
</div>
</Collapse>
</section>
</template>
<script setup>
<script lang="ts" setup>
import { parseISO } from 'date-fns'
import { computed, ref, watch } from 'vue'
import { useStore } from 'vuex'
import { useRRule } from '@/mixins/rrules'
import { usePretty } from '@/mixins/prettyDate'
import { lowercaseFirst, uppercaseFirst, useSelectedShow } from '@/utilities'
import { computed, ref, watch } from 'vue'
import { useI18n } from '@/i18n'
import SectionTitle from '@/components/generic/SectionTitle.vue'
import Collapse from '@/components/generic/Collapse.vue'
import { Schedule } from '@/types'
defineOptions({
compatConfig: { MODE: 3 },
})
const { t } = useI18n()
const { rruleRender } = useRRule()
const { prettyWeekday, prettyDate, prettyHours } = usePretty()
const store = useStore()
const selectedShow = useSelectedShow()
const schedules = computed(() => store.state.shows.schedules)
const schedules = computed<Schedule[]>(() => store.state.shows.schedules)
const relevantSchedules = computed(() => schedules.value.filter((s) => !isPossiblyPastSchedule(s)))
const isExpandable = computed(() => relevantSchedules.value.length > 2)
const isCollapsed = ref(true)
const collapseSchedules = computed(() => relevantSchedules.value.length > 2)
const isCollapsed = ref(!collapseSchedules.value)
function isPossiblyPastSchedule(schedule) {
function isPossiblyPastSchedule(schedule: Schedule) {
// Recurrence rules get very complex very fast, so we only give
// a definitive answer if a lastDate is set for this schedule.
if (!schedule.lastDate) {
......@@ -89,7 +132,7 @@ function updateSchedules() {
}
}
function renderRruleForSchedule(schedule) {
function renderRruleForSchedule(schedule: Schedule) {
if (schedule.rruleId < 3) {
return uppercaseFirst(rruleRender(schedule.rruleId))
}
......@@ -103,57 +146,3 @@ function renderRruleForSchedule(schedule) {
// TODO[#127]: this belongs in the store
watch(selectedShow, updateSchedules, { immediate: true })
</script>
<script>
export default {
compatConfig: {
MODE: 3,
},
}
</script>
<style scoped>
.show-schedules {
box-sizing: border-box;
width: 100%;
margin-bottom: 0.5rem;
position: relative;
overflow-y: hidden;
}
.show-schedules:hover {
cursor: pointer;
}
.show-schedules.expandable {
margin-bottom: 1rem;
}
.show-schedules.collapsed {
height: 10rem;
}
.show-schedules.collapsed::after {
content: 'click to expand';
display: flex;
justify-content: center;
align-items: center;
background: linear-gradient(rgba(255, 255, 255, 0), rgba(255, 255, 255, 9) 66%);
height: 4rem;
width: 100%;
z-index: 1;
bottom: 0;
position: absolute;
}
.show-schedules.collapsed::after,
.collapser {
text-align: center;
color: var(--info);
}
.show-schedules:hover:after,
.show-schedules:hover .collapser {
text-decoration: underline;
}
</style>
......@@ -208,11 +208,18 @@ export default {
},
showSchedules: {
schedule: 'Programm',
schedules: 'Programme',
noSchedulesAvailable: 'Es gibt für diese Sendereihe noch kein Ausstrahlungs-Schema',
collapse: 'Zum Verkleinern klicken',
title: 'Ausstrahlungsschema',
times: 'Sendezeiten',
rhythm: 'Rhythmus',
firstBroadcast: 'Erste Ausstrahlung',
lastBroadcast: 'Letzte Ausstrahlung',
noSchedulesAvailable: 'Es gibt für diese Sendereihe noch kein Ausstrahlungsschema',
showAll: 'Alles anzeigen',
hide: 'Ausblenden',
scheduleDescription:
'%{rhythm} ab %{startDate} jeweils von %{startTime} Uhr bis %{endTime} Uhr.',
scheduleDescriptionFinite:
'%{rhythm} ab %{startDate} bis zum %{endDate} jeweils von %{startTime} Uhr bis %{endTime} Uhr.',
},
showTimeslots: {
......
......@@ -208,11 +208,18 @@ export default {
},
showSchedules: {
schedule: 'schedule',
schedules: 'schedules',
title: 'Schedules',
times: 'Airtime',
rhythm: 'Rhythm',
firstBroadcast: 'First airing',
lastBroadcast: 'Last airing',
noSchedulesAvailable: 'There are currently no schedules for this show',
collapse: 'click to collapse',
showAll: 'Show All',
hide: 'Hide',
scheduleDescription:
'%{rhythm} beginning %{startDate} from %{startTime} to %{endTime} respectively.',
scheduleDescriptionFinite:
'%{rhythm} beginning %{startDate} through %{endDate} from %{startTime} to %{endTime} respectively.',
},
showTimeslots: {
......
......@@ -19,3 +19,4 @@ export type PaginationData = {
export type TimeSlot = Required<steeringComponents['schemas']['TimeSlot']>
export type Show = Required<steeringComponents['schemas']['Show']>
export type Playlist = Required<tankComponents['schemas']['store.Playlist']>
export type Schedule = Required<steeringComponents['schemas']['Schedule']>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment