event-calendar
Event calendar with resource view for room bookings
Event calendar with resource view for room bookings
Загрузка превью…
src/event-calendar/c-event-calendar-2.vue
<!--
ПОРТ c-event-calendar-2 ("Event calendar with resource view for room
bookings"). `EventCalendarNav` — настоящий порт, уже без view switcher
(см. EventCalendarNav.vue) — совпадает с `showViewSwitcher={false}`
оригинала буквально. `EventCalendarToolbar` инлайнена как `flex items-center
gap-2` (см. комментарий в c-event-calendar-1.vue). IconPlaceholder ("plus")
заменён инлайновым `<svg>` (путь lucide "plus" v0.545.0). `timeZone="UTC"` и
фиксированный якорь `Date.UTC(2026, 5, 15)` — те же причины, что и в
`ui-event-calendar`/`c-event-calendar-1`.
-->
<script setup lang="ts">
import { ref } from "vue"
import { addMinutes, setHours, startOfDay } from "date-fns"
import { EventCalendar, EventCalendarContent, EventCalendarNav, type CalendarEvent, type EventCalendarApi, type EventCalendarResource } from "@/components/reui/event-calendar"
import { Button } from "@/components/ui/button"
import { Card, CardContent } from "@/components/ui/card"
const ANCHOR = new Date(Date.UTC(2026, 5, 15))
const ROOMS: EventCalendarResource[] = [
{ id: "r101", title: "101 · King", color: "var(--color-blue-500)" },
{ id: "r102", title: "102 · Queen", color: "var(--color-emerald-500)" },
{ id: "r204", title: "204 · Twin", color: "var(--color-violet-500)" },
{ id: "r301", title: "Suite 301", color: "var(--color-amber-500)" },
]
type BookingStatus = "occupied" | "checkout" | "housekeeping" | "checkin"
const STATUS: Record<BookingStatus, { label: string; color: string }> = {
occupied: { label: "Occupied", color: "var(--color-emerald-500)" },
checkout: { label: "Check-out", color: "var(--color-rose-500)" },
housekeeping: { label: "Housekeeping", color: "var(--color-cyan-500)" },
checkin: { label: "Check-in", color: "var(--color-blue-500)" },
}
interface BookingData {
status: BookingStatus
}
function buildBookings(anchor: Date): CalendarEvent<BookingData>[] {
const base = startOfDay(anchor)
const at = (hour: number) => addMinutes(base, Math.round(hour * 60))
const booking = (
id: string,
title: string,
startHour: number,
endHour: number,
resourceId: string,
status: BookingStatus
): CalendarEvent<BookingData> => ({
id,
title,
start: at(startHour),
end: at(endHour),
resourceId,
color: STATUS[status].color,
data: { status },
})
return [
booking("stay-reed", "Occupied · Reed", 8, 10, "r101", "occupied"),
booking("checkout-reed", "Check-out · Reed", 10, 10.5, "r101", "checkout"),
booking("clean-101", "Housekeeping", 10.5, 12, "r101", "housekeeping"),
booking("checkin-alvarez", "Check-in · Alvarez", 14, 15, "r101", "checkin"),
booking("stay-chen", "Occupied · Chen", 8, 16, "r102", "occupied"),
booking("checkout-chen", "Check-out · Chen", 16, 16.5, "r102", "checkout"),
booking("clean-102", "Housekeeping", 16.5, 18, "r102", "housekeeping"),
booking("checkout-novak", "Check-out · Novak", 11, 11.5, "r204", "checkout"),
booking("clean-204", "Deep clean", 11.5, 13, "r204", "housekeeping"),
booking("checkin-ford", "Check-in · Ford", 15, 16, "r204", "checkin"),
booking("stay-ford", "Occupied · Ford", 16, 20, "r204", "occupied"),
booking("clean-301", "Housekeeping", 8, 9.5, "r301", "housekeeping"),
booking("checkin-osei", "Check-in · Osei", 10, 11, "r301", "checkin"),
booking("stay-osei", "Occupied · Osei", 11, 20, "r301", "occupied"),
]
}
const events = buildBookings(ANCHOR)
const calRef = ref<{ api: EventCalendarApi<BookingData> } | null>(null)
let bookingCount = 0
function addBooking() {
const start = setHours(startOfDay(ANCHOR), 12)
const end = addMinutes(start, 60)
calRef.value?.api.addEvent({
id: `booking-${bookingCount++}`,
title: "New booking",
start,
end,
resourceId: "r101",
color: STATUS.checkin.color,
data: { status: "checkin" },
})
calRef.value?.api.goTo(start)
}
</script>
<template>
<div class="w-full p-4">
<Card class="w-full py-0">
<CardContent class="p-0">
<EventCalendar
ref="calRef"
:events="events"
view="resource"
:resources="ROOMS"
:day-start-hour="8"
:day-end-hour="20"
:interval="60"
:interactions="{ drag: true, resize: true, selectSlot: false }"
time-zone="UTC"
class="h-[600px] w-full"
>
<div class="flex flex-wrap items-center gap-2 pe-2">
<EventCalendarNav class="min-w-0 flex-1" />
<div data-slot="event-calendar-toolbar" class="flex items-center gap-2">
<Button size="sm" @click="addBooking">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4" aria-hidden="true"><path d="M5 12h14" /><path d="M12 5v14" /></svg>
New booking
</Button>
</div>
</div>
<EventCalendarContent />
</EventCalendar>
<div class="text-muted-foreground flex flex-wrap items-center gap-x-4 gap-y-2 border-t px-4 py-3 text-xs">
<span v-for="status in Object.values(STATUS)" :key="status.label" class="flex items-center gap-1.5"><span aria-hidden="true" class="size-2 rounded-full" :style="{ backgroundColor: status.color }" />{{ status.label }}</span>
</div>
</CardContent>
</Card>
</div>
</template>
Установка
npx shadcn-vue@latest add https://revueui.rootapi.dev/r/c-event-calendar-2.jsonЗависимости реестра
npm-зависимости
- date-fns
Источник: порт из ReUI (Keenthemes, MIT)