Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<template>
<tr :class="rowClass" v-bind="attrs">
<td>
<div class="tw-w-12 tw-aspect-square rounded-lg tw-bg-gray-400 tw-overflow-hidden">
<img
v-if="noteImage"
class="tw-object-cover tw-h-full tw-w-full tw-max-w-full tw-max-h-full"
:src="noteImage.image"
sizes="100px"
:srcset="
noteImage.thumbnails
.filter(({ width, height }) => width === height)
.map(({ width, url }) => `${url} ${width}w`)
.join(',')
"
alt=""
/>
</div>
</td>
<td>
<template v-if="!isLoadingNote">
<template v-if="note">
{{ note.title }}
</template>
<span v-else class="tw-text-sm tw-text-gray-500">
{{ t('noneSetMasculine') }}
</span>
</template>
</td>
<td>
{{ prettyDateTime(timeslot.start) }}
</td>
<td>
{{ time.duration }}
</td>
<td>
<template v-if="playlist">
<template v-if="playlist.description?.trim()">{{ playlist.description }}</template>
<template v-else>{{ playlist.id }}</template>
</template>
<span v-else class="tw-text-sm tw-text-gray-500">
{{ t('noneSetFeminine') }}
</span>
</td>
<td>
<div class="tw-flex tw-gap-2 tw-justify-end">
<button
type="button"
class="btn btn-sm btn-default"
:title="t('showTimeslots.editDescription')"
@click="editNote"
>
<icon-system-uicons-pen />
</button>
<button
type="button"
class="btn btn-sm btn-default"
:title="t('showTimeslots.editPlaylist')"
@click="editPlaylist"
>
<icon-ph-playlist-light />
</button>
</div>
</td>
</tr>
<Teleport to="body">
<PlaylistModal ref="playlistModal" :timeslot="timeslot" />
<NoteEditorModal
v-model="localNoteId"
:is-open="showNoteEditor"
@show="showNoteEditor = $event"
/>
</Teleport>
</template>
<script lang="ts" setup>
import { useObjectFromStore } from '@rokoli/bnb/drf'
import { parseISO } from 'date-fns'
import { computed, ref, useAttrs } from 'vue'
import { useStore } from 'vuex'
import { useI18n } from '@/i18n'
import { TimeSlot } from '@/types'
import { useNoteStore } from '@/stores/notes'
import { useImage } from '@/stores/images'
import { prettyDuration, usePretty } from '@/mixins/prettyDate'
import NoteEditorModal from './NoteEditorModal.vue'
import PlaylistModal from './PlaylistSelector.vue'
defineOptions({
compatConfig: { MODE: 3 },
inheritAttrs: false,
ATTR_FALSE_VALUE: false,
})
const props = defineProps<{
timeslot: TimeSlot
}>()
const attrs = useAttrs()
const { t } = useI18n()
const { prettyDateTime } = usePretty()
const store = useStore()
const noteStore = useNoteStore()
// TODO: once the timeslot store is migrated to pinia we actually want to trigger
// an API update for this timeslot.
const localNoteId = ref(props.timeslot.noteId)
const { obj: note, isLoading: isLoadingNote } = useObjectFromStore(localNoteId, noteStore)
const noteImage = useImage(computed(() => note.value?.imageId ?? null))
const time = computed(() => prettyDuration(props.timeslot.start, props.timeslot.end))
const playlist = computed<{ id: number; description: string } | null>(() =>
props.timeslot.playlistId
? store.getters['playlists/getPlaylistById'](props.timeslot.playlistId) ?? null
: null,
)
const rowClass = computed(() => {
const minutesInMs = time.value.minutes * 60 * 1000
const now = new Date()
const startDate = parseISO(props.timeslot.start)
const endDate = new Date(startDate.getTime() + minutesInMs)
return {
'tw-opacity-50': now > endDate,
}
})
const playlistModal = ref()
const showNoteEditor = ref(false)
function editNote() {
showNoteEditor.value = true
}
function editPlaylist() {
playlistModal.value.open(props.timeslot.scheduleId)