vue-hotel-booking-calendar
Version:
A Vue 3 calendar component suite for hotel bookings with price calculation, hotel dashboard, custom text labels, and flexible navigation. Features guest booking calendar and owner dashboard with elegant white design and full internationalization support.
694 lines (693 loc) • 28.7 kB
JavaScript
import { defineComponent as ge, ref as Z, computed as M, onMounted as be, watch as ie, createElementBlock as k, openBlock as v, normalizeClass as j, createElementVNode as s, createCommentVNode as A, toDisplayString as u, Fragment as E, renderList as G, withKeys as he, withModifiers as me, createTextVNode as De, renderSlot as fe, normalizeStyle as ae } from "vue";
const ye = { class: "calendar-header" }, pe = ["disabled", "aria-label"], we = { class: "month-year" }, _e = ["disabled", "aria-label"], Se = {
key: 0,
class: "selection-error"
}, Ce = { class: "error-content" }, Me = { class: "error-message" }, $e = {
key: 0,
class: "blocked-dates"
}, xe = ["aria-label"], Be = { class: "calendar-body" }, Ie = { class: "weekdays" }, Pe = { class: "days-grid" }, Oe = ["onClick", "onMouseenter", "tabindex", "aria-label", "onKeydown"], Ve = { class: "day-content" }, Ne = { class: "day-number" }, Le = {
key: 0,
class: "day-price"
}, Te = {
key: 1,
class: "price-summary"
}, Fe = { class: "price-summary-header" }, Ee = ["aria-label"], Re = { class: "price-content" }, He = { class: "stay-info" }, Ge = { class: "nights" }, Ye = { class: "dates" }, Ae = {
key: 0,
class: "breakdown"
}, je = { class: "breakdown-toggle" }, Ke = { class: "daily-list" }, Ue = { class: "totals" }, We = { class: "subtotal" }, ze = { class: "total" }, qe = ["disabled"], Je = {
key: 2,
class: "calendar-legend"
}, Qe = { class: "legend-item" }, Xe = { class: "legend-item" }, Ze = { class: "legend-item" }, et = /* @__PURE__ */ ge({
__name: "HotelBookingCalendar",
props: {
modelValue: { default: () => ({ checkIn: null, checkOut: null }) },
availabilityData: { default: () => [] },
minDate: {},
maxDate: {},
locale: { default: "en-GB" },
disablePastDates: { type: Boolean, default: !0 },
allowPreviousMonthNavigation: { type: Boolean, default: !0 },
showPrices: { type: Boolean, default: !1 },
allowSingleDay: { type: Boolean, default: !1 },
theme: { default: "light" },
basePrice: { default: 85 },
currency: { default: "GBP" },
showPriceCalculation: { type: Boolean, default: !0 },
showSelectionErrors: { type: Boolean, default: !0 },
textLabels: { default: () => ({}) }
},
emits: ["update:modelValue", "date-click", "date-select", "selection-change", "selection-error", "price-calculation", "book-now"],
setup(L, { emit: K }) {
const n = L, y = K, b = Z(/* @__PURE__ */ new Date()), oe = Z(null), O = Z(!1), p = Z(null), x = M(() => {
if (!n.minDate) {
if (n.disablePastDates) {
const e = /* @__PURE__ */ new Date();
return e.setHours(0, 0, 0, 0), e;
}
return null;
}
return typeof n.minDate == "string" ? new Date(n.minDate) : n.minDate;
}), Y = M(() => n.maxDate ? typeof n.maxDate == "string" ? new Date(n.maxDate) : n.maxDate : null), U = M(() => {
const e = /* @__PURE__ */ new Map();
return n.availabilityData.forEach((o) => {
e.set(o.date, o);
}), e;
}), ee = M(() => {
const e = new Intl.DateTimeFormat(n.locale, { weekday: "short" }), o = [];
for (let i = 0; i < 7; i++) {
const d = new Date(2023, 0, i + 1);
o.push(e.format(d));
}
return o;
}), te = M(() => {
const e = b.value.getFullYear(), o = b.value.getMonth(), i = new Date(e, o, 1), d = new Date(e, o + 1, 0), h = new Date(i), c = new Date(d);
h.setDate(h.getDate() - h.getDay()), c.setDate(c.getDate() + (6 - c.getDay()));
const w = [], m = new Date(h);
for (; m <= c; ) {
const $ = H(m), P = {
date: new Date(m),
dateString: $,
isToday: $ === H(/* @__PURE__ */ new Date()),
isCurrentMonth: m.getMonth() === o,
isPreviousMonth: m.getMonth() < o,
isNextMonth: m.getMonth() > o,
availability: U.value.get($)
};
w.push(P), m.setDate(m.getDate() + 1);
}
return w;
}), W = M(() => {
if (n.allowPreviousMonthNavigation || !x.value) return !0;
const e = new Date(b.value);
e.setMonth(e.getMonth() - 1), e.setDate(1);
const o = new Date(x.value);
return o.setDate(1), e >= o;
}), R = M(() => {
if (!Y.value) return !0;
const e = new Date(b.value);
e.setMonth(e.getMonth() + 1), e.setDate(1);
const o = new Date(Y.value);
return o.setDate(1), e <= o;
}), C = M(() => {
var e, o, i, d, h, c, w, m, $, P, X, ue, de;
return {
previousMonth: ((e = n.textLabels) == null ? void 0 : e.previousMonth) || "Previous month",
nextMonth: ((o = n.textLabels) == null ? void 0 : o.nextMonth) || "Next month",
bookingSummary: ((i = n.textLabels) == null ? void 0 : i.bookingSummary) || "Booking Summary",
nights: ((d = n.textLabels) == null ? void 0 : d.nights) || "nights",
night: ((h = n.textLabels) == null ? void 0 : h.night) || "night",
priceBreakdown: ((c = n.textLabels) == null ? void 0 : c.priceBreakdown) || "Price breakdown",
total: ((w = n.textLabels) == null ? void 0 : w.total) || "Total",
bookNow: ((m = n.textLabels) == null ? void 0 : m.bookNow) || "Book Now",
available: (($ = n.textLabels) == null ? void 0 : $.available) || "Available",
checkoutOnly: ((P = n.textLabels) == null ? void 0 : P.checkoutOnly) || "Checkout Only",
blocked: ((X = n.textLabels) == null ? void 0 : X.blocked) || "Blocked",
clearSelection: ((ue = n.textLabels) == null ? void 0 : ue.clearSelection) || "Clear selection",
dismissError: ((de = n.textLabels) == null ? void 0 : de.dismissError) || "Dismiss error"
};
}), _ = M(() => {
const { checkIn: e, checkOut: o } = n.modelValue;
if (!e || !o) return null;
const i = new Date(e), d = new Date(o), h = Math.ceil((d.getTime() - i.getTime()) / (1e3 * 60 * 60 * 24));
if (h <= 0) return null;
const c = [];
let w = 0;
const m = new Date(i);
for (; m < d; ) {
const $ = H(m), P = U.value.get($), X = (P == null ? void 0 : P.price) || n.basePrice || 85;
c.push({ date: $, price: X }), w += X, m.setDate(m.getDate() + 1);
}
return {
basePrice: n.basePrice || 85,
nights: h,
dailyPrices: c,
totalPrice: w,
currency: n.currency || "GBP",
averagePerNight: w / h
};
}), H = (e) => {
const o = e.getFullYear(), i = String(e.getMonth() + 1).padStart(2, "0"), d = String(e.getDate()).padStart(2, "0");
return `${o}-${i}-${d}`;
}, se = (e) => new Intl.DateTimeFormat(n.locale, {
month: "long",
year: "numeric"
}).format(e), T = (e) => new Intl.NumberFormat(n.locale, {
style: "currency",
currency: n.currency || "GBP"
}).format(e), z = (e, o) => {
const i = new Date(e), d = new Date(o), h = new Intl.DateTimeFormat(n.locale, {
month: "short",
day: "numeric"
});
return `${h.format(i)} - ${h.format(d)}`;
}, ne = (e) => new Intl.DateTimeFormat(n.locale, {
month: "short",
day: "numeric"
}).format(new Date(e)), le = (e) => e.map((o) => ne(o)).join(", "), q = (e) => {
var o;
return ((o = e.availability) == null ? void 0 : o.price) || n.basePrice || 0;
}, J = (e) => {
if (!e.isCurrentMonth) return !0;
if (n.disablePastDates) {
const o = /* @__PURE__ */ new Date();
o.setHours(0, 0, 0, 0);
const i = new Date(e.date);
if (i.setHours(0, 0, 0, 0), i < o) return !0;
}
return !!(x.value && e.date < x.value || Y.value && e.date > Y.value);
}, Q = (e) => !!(J(e) || F(e) === "blocked"), F = (e) => {
var o;
return ((o = e.availability) == null ? void 0 : o.status) || "available";
}, re = (e) => {
var o, i;
return ((i = (o = p.value) == null ? void 0 : o.blockedDates) == null ? void 0 : i.includes(e)) || !1;
}, a = (e, o) => {
const i = new Date(e), d = new Date(o), h = new Date(i), c = [];
for (h.setDate(h.getDate() + 1); h < d; ) {
const w = H(h), m = U.value.get(w);
(m == null ? void 0 : m.status) === "blocked" && c.push(w), h.setDate(h.getDate() + 1);
}
return c;
}, l = (e) => e === n.modelValue.checkIn || e === n.modelValue.checkOut, t = (e) => {
const { checkIn: o, checkOut: i } = n.modelValue;
return !o || !i ? !1 : e > o && e < i;
}, r = (e) => e === n.modelValue.checkIn, g = (e) => e === n.modelValue.checkOut, f = (e) => {
const o = e.date.toLocaleDateString(n.locale, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
}), i = F(e), d = q(e);
return `${o}, ${i}${d ? `, ${T(d)}` : ""}`;
}, D = () => {
p.value = null;
}, S = (e, o) => ({
type: e,
message: {
"blocked-dates-in-range": "Selected dates include unavailable periods. Please choose different dates.",
"min-stay-not-met": "Minimum stay requirement not met for selected dates.",
"max-stay-exceeded": "Selected stay exceeds maximum allowed duration.",
"invalid-range": "Invalid date range selected."
}[e],
blockedDates: o
}), B = () => {
const e = { checkIn: null, checkOut: null };
y("update:modelValue", e), y("selection-change", e), D(), O.value = !1;
}, V = () => {
if (_.value && n.modelValue.checkIn && n.modelValue.checkOut) {
const e = {
selection: {
checkIn: n.modelValue.checkIn,
checkOut: n.modelValue.checkOut
},
calculation: _.value
};
y("book-now", e);
}
}, I = (e) => {
if (Q(e)) return;
D();
const { checkIn: o, checkOut: i } = n.modelValue, d = e.dateString;
if (y("date-click", d, F(e)), !o || o && i) {
const h = { checkIn: d, checkOut: null };
y("update:modelValue", h), y("selection-change", h), O.value = !0;
} else if (o && !i) {
if (d === o && !n.allowSingleDay)
return;
const h = d < o ? d : o, c = d < o ? o : d, w = a(h, c);
if (w.length > 0) {
const $ = S("blocked-dates-in-range", w);
p.value = $, y("selection-error", $);
const P = { checkIn: d, checkOut: null };
y("update:modelValue", P), y("selection-change", P), O.value = !0;
return;
}
const m = { checkIn: h, checkOut: c };
y("update:modelValue", m), y("selection-change", m), O.value = !1;
}
}, N = (e) => {
J(e) || (oe.value = e.dateString);
}, ce = () => {
if (!W.value) return;
const e = new Date(b.value);
e.setMonth(e.getMonth() - 1), b.value = e;
}, ve = () => {
if (!R.value) return;
const e = new Date(b.value);
e.setMonth(e.getMonth() + 1), b.value = e;
};
return be(() => {
if (n.modelValue.checkIn) {
const e = new Date(n.modelValue.checkIn);
b.value = new Date(e.getFullYear(), e.getMonth(), 1);
}
}), ie(() => n.modelValue, (e) => {
e.checkIn && !e.checkOut ? O.value = !0 : O.value = !1;
}, { deep: !0 }), ie(_, (e) => {
y("price-calculation", e);
}, { immediate: !0 }), (e, o) => {
var i, d, h;
return v(), k("div", {
class: j(["hotel-booking-calendar", [
`theme-${e.theme}`,
{ "show-prices": e.showPrices },
{ "has-selection-error": p.value }
]])
}, [
s("div", ye, [
s("button", {
type: "button",
class: "nav-button prev",
onClick: ce,
disabled: !W.value,
"aria-label": C.value.previousMonth
}, " ‹ ", 8, pe),
s("div", we, [
s("h2", null, u(se(b.value)), 1)
]),
s("button", {
type: "button",
class: "nav-button next",
onClick: ve,
disabled: !R.value,
"aria-label": C.value.nextMonth
}, " › ", 8, _e)
]),
p.value && e.showSelectionErrors ? (v(), k("div", Se, [
o[0] || (o[0] = s("div", { class: "error-icon" }, "⚠️", -1)),
s("div", Ce, [
s("div", Me, u(p.value.message), 1),
(i = p.value.blockedDates) != null && i.length ? (v(), k("div", $e, " Blocked dates: " + u(le(p.value.blockedDates)), 1)) : A("", !0)
]),
s("button", {
onClick: D,
class: "error-dismiss",
"aria-label": C.value.dismissError
}, "✕", 8, xe)
])) : A("", !0),
s("div", Be, [
s("div", Ie, [
(v(!0), k(E, null, G(ee.value, (c) => (v(), k("div", {
key: c,
class: "weekday"
}, u(c), 1))), 128))
]),
s("div", Pe, [
(v(!0), k(E, null, G(te.value, (c) => (v(), k("div", {
key: c.dateString,
class: j(["day-cell", {
"other-month": !c.isCurrentMonth,
today: c.isToday,
selected: l(c.dateString),
"in-range": t(c.dateString),
"range-start": r(c.dateString),
"range-end": g(c.dateString),
available: F(c) === "available",
blocked: F(c) === "blocked",
"checkout-only": F(c) === "checkout-only",
disabled: J(c),
"click-disabled": Q(c),
"error-blocked": re(c.dateString)
}]),
onClick: (w) => I(c),
onMouseenter: (w) => N(c),
role: "button",
tabindex: Q(c) ? -1 : 0,
"aria-label": f(c),
onKeydown: [
he((w) => I(c), ["enter"]),
he(me((w) => I(c), ["prevent"]), ["space"])
]
}, [
s("div", Ve, [
s("span", Ne, u(c.date.getDate()), 1),
e.showPrices && q(c) ? (v(), k("div", Le, u(T(q(c))), 1)) : A("", !0)
])
], 42, Oe))), 128))
])
]),
e.showPriceCalculation && _.value ? (v(), k("div", Te, [
s("div", Fe, [
s("h4", null, u(C.value.bookingSummary), 1),
s("button", {
onClick: B,
class: "clear-btn",
"aria-label": C.value.clearSelection
}, "✕", 8, Ee)
]),
s("div", Re, [
s("div", He, [
s("span", Ge, u(_.value.nights) + " " + u(_.value.nights === 1 ? C.value.night : C.value.nights), 1),
s("span", Ye, u((d = e.modelValue) != null && d.checkIn && ((h = e.modelValue) != null && h.checkOut) ? z(e.modelValue.checkIn, e.modelValue.checkOut) : ""), 1)
]),
_.value.dailyPrices.length > 1 ? (v(), k("div", Ae, [
s("details", je, [
s("summary", null, [
De(u(C.value.priceBreakdown) + " ", 1),
o[1] || (o[1] = s("span", { class: "arrow" }, "▼", -1))
]),
s("div", Ke, [
(v(!0), k(E, null, G(_.value.dailyPrices, (c) => (v(), k("div", {
key: c.date,
class: "daily-row"
}, [
s("span", null, u(ne(c.date)), 1),
s("span", null, u(T(c.price)), 1)
]))), 128))
])
])
])) : A("", !0),
s("div", Ue, [
s("div", We, [
s("span", null, u(_.value.nights) + " × " + u(T(_.value.averagePerNight)), 1),
s("span", null, u(T(_.value.totalPrice)), 1)
]),
s("div", ze, [
s("span", null, u(C.value.total), 1),
s("strong", null, u(T(_.value.totalPrice)), 1)
])
]),
s("button", {
onClick: V,
class: "book-btn",
disabled: !_.value
}, u(C.value.bookNow), 9, qe)
])
])) : A("", !0),
(e.$slots.legend, v(), k("div", Je, [
fe(e.$slots, "legend", {}, () => [
s("div", Qe, [
o[2] || (o[2] = s("div", { class: "legend-indicator available" }, null, -1)),
s("span", null, u(C.value.available), 1)
]),
s("div", Xe, [
o[3] || (o[3] = s("div", { class: "legend-indicator checkout-only" }, null, -1)),
s("span", null, u(C.value.checkoutOnly), 1)
]),
s("div", Ze, [
o[4] || (o[4] = s("div", { class: "legend-indicator blocked" }, null, -1)),
s("span", null, u(C.value.blocked), 1)
])
], !0)
]))
], 2);
};
}
}), ke = (L, K) => {
const n = L.__vccOpts || L;
for (const [y, b] of K)
n[y] = b;
return n;
}, tt = /* @__PURE__ */ ke(et, [["__scopeId", "data-v-fd5ea05e"]]), nt = { class: "dashboard-header" }, at = ["aria-label"], ot = { class: "current-month" }, st = ["aria-label"], lt = { class: "calendar-container" }, rt = { class: "calendar-grid" }, ct = { class: "grid-header" }, it = { class: "room-header" }, ut = { class: "date-number" }, dt = { class: "date-weekday" }, ht = { class: "room-name" }, gt = ["onClick", "title"], kt = { class: "span-content" }, vt = { class: "span-text" }, bt = ["onClick", "title"], mt = {
key: 0,
class: "booking-indicator"
}, Dt = { class: "guest-initials" }, ft = { class: "legend" }, yt = /* @__PURE__ */ ge({
__name: "HotelDashboardCalendar",
props: {
rooms: {},
bookings: {},
selectedMonth: { default: () => /* @__PURE__ */ new Date() },
theme: { default: "light" },
statusConfig: {},
allowPreviousMonthNavigation: { type: Boolean, default: !1 },
textLabels: { default: () => ({}) }
},
emits: ["update:selectedMonth", "booking-click", "booking-create"],
setup(L, { emit: K }) {
const n = L, y = K, b = Z(new Date(n.selectedMonth)), oe = [
{
key: "available",
label: "Available",
color: "#666",
backgroundColor: "#f9f9f9",
darkBackgroundColor: "#2a2a2a"
},
{
key: "confirmed",
label: "Confirmed",
color: "#166534",
backgroundColor: "#dcf8c6",
darkBackgroundColor: "#1a4d14"
},
{
key: "pending",
label: "Pending",
color: "#92400e",
backgroundColor: "#fef3c7",
darkBackgroundColor: "#4a3c00"
},
{
key: "checked-in",
label: "Checked In",
color: "#1e40af",
backgroundColor: "#dbeafe",
darkBackgroundColor: "#1e3a8a"
},
{
key: "checked-out",
label: "Checked Out",
color: "#7c3aed",
backgroundColor: "#f3e8ff",
darkBackgroundColor: "#5b21b6"
},
{
key: "cancelled",
label: "Cancelled",
color: "#dc2626",
backgroundColor: "#fee2e2",
darkBackgroundColor: "#7f1d1d"
}
], O = M(
() => n.statusConfig || oe
), p = M(() => {
var a, l, t, r, g, f;
return {
previousMonth: ((a = n.textLabels) == null ? void 0 : a.previousMonth) || "← Previous",
nextMonth: ((l = n.textLabels) == null ? void 0 : l.nextMonth) || "Next →",
room: ((t = n.textLabels) == null ? void 0 : t.room) || "Room",
available: ((r = n.textLabels) == null ? void 0 : r.available) || "Available",
createBooking: ((g = n.textLabels) == null ? void 0 : g.createBooking) || "Click to create booking",
clickForDetails: ((f = n.textLabels) == null ? void 0 : f.clickForDetails) || "Click for details"
};
}), x = M(() => {
const a = b.value.getFullYear(), l = b.value.getMonth(), t = new Date(a, l + 1, 0).getDate(), r = /* @__PURE__ */ new Date();
r.setHours(0, 0, 0, 0);
const g = [];
for (let f = 1; f <= t; f++) {
const D = new Date(a, l, f), S = `${a}-${String(l + 1).padStart(2, "0")}-${String(f).padStart(2, "0")}`;
g.push({
day: f,
dateString: S,
date: D,
isToday: D.getTime() === r.getTime(),
isWeekend: D.getDay() === 0 || D.getDay() === 6,
weekday: new Intl.DateTimeFormat("en-US", { weekday: "short" }).format(D)
});
}
return g;
}), Y = M(() => {
const a = {};
return n.rooms.forEach((l) => {
a[l.id] = [], n.bookings.filter(
(r) => r.roomNumber === l.number
).forEach((r) => {
const g = new Date(r.checkIn), f = new Date(r.checkOut), D = b.value.getFullYear(), S = b.value.getMonth(), B = new Date(D, S, 1), V = new Date(D, S + 1, 0);
if (g <= V && f > B) {
let I, N;
if (g >= B ? I = g.getDate() : I = 1, f <= V ? N = f.getDate() - 1 : N = V.getDate(), N = Math.max(I, N), I >= 1 && N <= V.getDate() && I <= N) {
const ce = _(r.status);
a[l.id].push({
booking: r,
startDay: I,
endDay: N,
length: N - I + 1,
statusConfig: ce
});
}
}
}), a[l.id].sort((r, g) => r.startDay - g.startDay);
}), a;
}), U = (a) => new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long"
}).format(a), ee = (a) => {
if (a < 0 && !n.allowPreviousMonthNavigation) {
const t = /* @__PURE__ */ new Date(), r = new Date(b.value.getFullYear(), b.value.getMonth(), 1), g = new Date(t.getFullYear(), t.getMonth(), 1);
if (r <= g) return;
}
const l = new Date(b.value);
l.setMonth(l.getMonth() + a), b.value = l, y("update:selectedMonth", l);
}, te = (a, l) => n.bookings.some(
(t) => t.roomNumber === a.number && W(l, t)
), W = (a, l) => {
const t = new Date(a), r = new Date(l.checkIn), g = new Date(l.checkOut);
return t >= r && t < g;
}, R = (a, l) => n.bookings.find(
(t) => t.roomNumber === a.number && W(l, t)
) || null, C = (a, l) => {
const t = R(a, l);
return t ? t.status : "available";
}, _ = (a) => O.value.find((l) => l.key === a) || O.value[0], H = (a, l) => {
const t = [], r = x.value.find((g) => g.dateString === l);
return r != null && r.isToday && t.push("is-today"), r != null && r.isWeekend && t.push("is-weekend"), te(a, l) && t.push("has-booking"), t;
}, se = (a, l) => {
const t = C(a, l), r = _(t);
return {
backgroundColor: n.theme === "dark" && r.darkBackgroundColor ? r.darkBackgroundColor : r.backgroundColor,
color: r.color
};
}, T = (a, l) => {
const t = R(a, l);
return t ? t.guestName.split(" ").map((r) => r.charAt(0).toUpperCase()).join("").substring(0, 2) : "";
}, z = (a, l) => {
const t = new Date(a), g = new Date(l).getTime() - t.getTime();
return Math.ceil(g / (1e3 * 60 * 60 * 24));
}, ne = (a, l) => {
const t = R(a, l);
if (!t) return `${p.value.room} ${a.number} - ${p.value.available} (${p.value.createBooking})`;
const r = _(t.status), g = z(t.checkIn, t.checkOut);
return `${t.guestName} - ${p.value.room} ${a.number} - ${r.label} - ${g} nights (${p.value.clickForDetails})`;
}, le = (a, l) => {
const t = R(a, l);
t ? y("booking-click", t) : y("booking-create", { roomId: a.id, date: l });
}, q = (a) => {
var f;
const l = [], t = Y.value[a.id] || [], r = /* @__PURE__ */ new Set(), g = [...t].sort((D, S) => D.startDay - S.startDay);
for (let D = 1; D <= x.value.length; D++) {
if (r.has(D)) continue;
const S = g.find((B) => B.startDay === D);
if (S) {
const B = ((f = x.value[D - 1]) == null ? void 0 : f.dateString) || "";
l.push({
type: "booking-span",
booking: S.booking,
startDay: S.startDay,
endDay: S.endDay,
length: S.length,
statusConfig: S.statusConfig,
startDate: B
});
for (let V = S.startDay; V <= S.endDay; V++)
r.add(V);
} else {
const B = x.value[D - 1];
B && l.push({
type: "date-cell",
dateString: B.dateString,
day: D
});
}
}
return l;
}, J = (a) => ({
backgroundColor: n.theme === "dark" && a.statusConfig.darkBackgroundColor ? a.statusConfig.darkBackgroundColor : a.statusConfig.backgroundColor,
color: a.statusConfig.color,
border: `1px solid ${a.statusConfig.color}`,
borderRadius: "4px",
gridColumnStart: (a.startDay + 1).toString(),
// +1 because room column is first
gridColumnEnd: (a.endDay + 2).toString()
// +2 because grid-end is exclusive and room column is first
}), Q = (a) => {
const l = z(a.booking.checkIn, a.booking.checkOut), t = a.booking.guestName;
return a.length === 1 ? t.split(" ").map((r) => r.charAt(0)).join("").substring(0, 2) : a.length <= 3 ? t.split(" ")[0] : a.length <= 6 ? `${t.split(" ")[0]} (${l}n)` : `${t} - ${l} nights`;
}, F = (a) => {
const l = z(a.booking.checkIn, a.booking.checkOut), t = n.rooms.find((r) => r.number === a.booking.roomNumber);
return `${a.booking.guestName} - Room ${t == null ? void 0 : t.number} - ${a.statusConfig.label} - ${l} nights`;
}, re = (a) => {
y("booking-click", a);
};
return ie(() => n.selectedMonth, (a) => {
a && (b.value = new Date(a));
}), (a, l) => (v(), k("div", {
class: j(["hotel-dashboard-calendar", `theme-${a.theme}`]),
style: ae({ "--days-in-month": x.value.length })
}, [
s("div", nt, [
s("button", {
onClick: l[0] || (l[0] = (t) => ee(-1)),
class: "nav-btn",
"aria-label": p.value.previousMonth
}, u(p.value.previousMonth), 9, at),
s("h2", ot, u(U(b.value)), 1),
s("button", {
onClick: l[1] || (l[1] = (t) => ee(1)),
class: "nav-btn",
"aria-label": p.value.nextMonth
}, u(p.value.nextMonth), 9, st)
]),
s("div", lt, [
s("div", rt, [
s("div", ct, [
s("div", it, u(p.value.room), 1),
(v(!0), k(E, null, G(x.value, (t) => (v(), k("div", {
key: t.dateString,
class: j(["date-header", {
"is-today": t.isToday,
"is-weekend": t.isWeekend
}])
}, [
s("div", ut, u(t.day), 1),
s("div", dt, u(t.weekday), 1)
], 2))), 128))
]),
(v(!0), k(E, null, G(n.rooms, (t) => (v(), k("div", {
key: t.id,
class: "room-row"
}, [
s("div", ht, u(t.number), 1),
(v(!0), k(E, null, G(q(t), (r, g) => (v(), k(E, {
key: `${t.id}-${g}`
}, [
r.type === "booking-span" ? (v(), k("div", {
key: 0,
class: j(["booking-span-cell", H(t, r.startDate)]),
style: ae(J(r)),
onClick: (f) => re(r.booking),
title: F(r)
}, [
s("div", kt, [
s("span", vt, u(Q(r)), 1)
])
], 14, gt)) : (v(), k("div", {
key: 1,
class: j(["date-cell", H(t, r.dateString)]),
style: ae(se(t, r.dateString)),
onClick: (f) => le(t, r.dateString),
title: ne(t, r.dateString)
}, [
te(t, r.dateString) ? (v(), k("div", mt, [
s("span", Dt, u(T(t, r.dateString)), 1)
])) : A("", !0)
], 14, bt))
], 64))), 128))
]))), 128))
])
]),
s("div", ft, [
(v(!0), k(E, null, G(O.value, (t) => (v(), k("div", {
key: t.key,
class: "legend-item"
}, [
s("div", {
class: "legend-color",
style: ae({
backgroundColor: t.backgroundColor,
borderColor: t.color
})
}, null, 4),
s("span", null, u(t.label), 1)
]))), 128))
])
], 6));
}
}), pt = /* @__PURE__ */ ke(yt, [["__scopeId", "data-v-f67bbf1d"]]), _t = {
install(L) {
L.component("HotelBookingCalendar", tt), L.component("HotelDashboardCalendar", pt);
}
};
export {
tt as HotelBookingCalendar,
pt as HotelDashboardCalendar,
_t as default
};