77 lines
2.7 KiB
Vue
77 lines
2.7 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* The read-only body of a reservation, shared by the admin card and the two
|
|
* dialogs so the three never drift apart.
|
|
*/
|
|
import { computed } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
import type { Bike, ReservationLike } from '@/utils/types'
|
|
|
|
type Field = 'period' | 'bikes' | 'telegram' | 'people' | 'linka' | 'reason'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
reservation: ReservationLike
|
|
bikes: Bike[]
|
|
/** Fields the caller renders itself — the edit dialog omits what it edits */
|
|
omit?: Field[]
|
|
}>(),
|
|
{ omit: () => [] },
|
|
)
|
|
|
|
function shows(field: Field) {
|
|
return !props.omit.includes(field)
|
|
}
|
|
|
|
const { locale } = useI18n()
|
|
|
|
const bikeNames = computed(() =>
|
|
props.reservation.bikes
|
|
.map((id) => props.bikes.find((bike) => bike.id === id)?.name ?? `#${id}`)
|
|
.join(', '),
|
|
)
|
|
|
|
const formatter = computed(
|
|
() =>
|
|
new Intl.DateTimeFormat(locale.value === 'fr' ? 'fr-CH' : 'en-GB', {
|
|
dateStyle: 'short',
|
|
timeStyle: 'short',
|
|
}),
|
|
)
|
|
|
|
function format(iso: string) {
|
|
return formatter.value.format(new Date(iso))
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<dl class="grid gap-1 text-sm">
|
|
<div v-if="shows('period')" class="flex gap-2">
|
|
<dt class="text-muted-foreground shrink-0">{{ $t('reservation.details.period') }}</dt>
|
|
<dd>{{ format(reservation.start_time) }} → {{ format(reservation.end_time) }}</dd>
|
|
</div>
|
|
<div v-if="shows('bikes')" class="flex gap-2">
|
|
<dt class="text-muted-foreground shrink-0">{{ $t('reservation.details.bikes') }}</dt>
|
|
<dd>{{ bikeNames }}</dd>
|
|
</div>
|
|
<div v-if="shows('telegram') && reservation.telegram" class="flex gap-2">
|
|
<dt class="text-muted-foreground shrink-0">{{ $t('reservation.details.telegram') }}</dt>
|
|
<dd class="truncate">{{ reservation.telegram }}</dd>
|
|
</div>
|
|
<div v-if="shows('people') && reservation.users?.length" class="flex gap-2">
|
|
<dt class="text-muted-foreground shrink-0">{{ $t('reservation.details.people') }}</dt>
|
|
<dd class="min-w-0 break-words">
|
|
{{ reservation.users.map((u) => `${u.firstname} ${u.name} <${u.email}>`).join(', ') }}
|
|
</dd>
|
|
</div>
|
|
<div v-if="shows('linka') && reservation.linka_emails?.length" class="flex gap-2">
|
|
<dt class="text-muted-foreground shrink-0">{{ $t('reservation.details.linka') }}</dt>
|
|
<dd class="min-w-0 break-words">{{ reservation.linka_emails.join(', ') }}</dd>
|
|
</div>
|
|
<div v-if="shows('reason') && reservation.description" class="flex gap-2">
|
|
<dt class="text-muted-foreground shrink-0">{{ $t('reservation.details.reason') }}</dt>
|
|
<dd class="min-w-0 break-words italic">{{ reservation.description }}</dd>
|
|
</div>
|
|
</dl>
|
|
</template>
|