vue-hotel-booking-calendar
Version:
A Vue 3 calendar component suite featuring hotel booking calendars, dashboard management, and resource scheduler with yearly/monthly/weekly/daily/hourly views. Includes price calculation, recurring events, event categories, working hours, and full interna
2,188 lines • 91.9 kB
JavaScript
import { defineComponent as he, ref as ue, computed as b, onMounted as pe, watch as ke, createElementBlock as d, openBlock as c, normalizeClass as V, createElementVNode as t, createCommentVNode as N, toDisplayString as m, Fragment as B, renderList as F, withKeys as be, withModifiers as me, createTextVNode as De, renderSlot as $e, nextTick as fe, normalizeStyle as Q, createBlock as ye } from "vue";
const _e = { class: "calendar-header" }, Se = ["disabled", "aria-label"], Ce = { class: "month-year" }, Te = ["disabled", "aria-label"], Me = {
key: 0,
class: "selection-error"
}, Ie = { class: "error-content" }, He = { class: "error-message" }, Ee = {
key: 0,
class: "blocked-dates"
}, Oe = ["aria-label"], Be = { class: "calendar-body" }, Fe = { class: "calendar-grid-container" }, We = ["onClick", "onMouseenter", "tabindex", "aria-label", "onKeydown"], Ve = { class: "day-content" }, Ne = { class: "day-number" }, Pe = {
key: 0,
class: "day-price"
}, Le = {
key: 1,
class: "price-summary"
}, Ye = { class: "price-summary-header" }, xe = ["aria-label"], ze = { class: "price-content" }, Ae = { class: "stay-info" }, Re = { class: "nights" }, Ue = { class: "dates" }, Ge = {
key: 0,
class: "breakdown"
}, je = { class: "breakdown-toggle" }, qe = { class: "daily-list" }, Ke = { class: "totals" }, Je = { class: "subtotal" }, Qe = { class: "total" }, Xe = ["disabled"], Ze = {
key: 2,
class: "calendar-legend"
}, et = { class: "legend-item" }, tt = { class: "legend-item" }, nt = { class: "legend-item" }, at = /* @__PURE__ */ he({
__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(J, { emit: oe }) {
const e = J, p = oe, w = ue(/* @__PURE__ */ new Date()), _ = ue(null), U = ue(!1), P = ue(null), x = b(() => {
if (!e.minDate) {
if (e.disablePastDates) {
const r = /* @__PURE__ */ new Date();
return r.setHours(0, 0, 0, 0), r;
}
return null;
}
return typeof e.minDate == "string" ? new Date(e.minDate) : e.minDate;
}), ne = b(() => e.maxDate ? typeof e.maxDate == "string" ? new Date(e.maxDate) : e.maxDate : null), Y = b(() => {
const r = /* @__PURE__ */ new Map();
return e.availabilityData.forEach((k) => {
r.set(k.date, k);
}), r;
}), X = b(() => {
const r = new Intl.DateTimeFormat(e.locale, { weekday: "short" }), k = [];
for (let T = 0; T < 7; T++) {
const H = new Date(2023, 0, T + 1);
k.push(r.format(H));
}
return k;
}), se = b(() => {
const r = w.value.getFullYear(), k = w.value.getMonth(), T = new Date(r, k, 1), H = new Date(r, k + 1, 0), z = new Date(T), $ = new Date(H), q = z.getDay();
z.setDate(z.getDate() - q);
const ae = 6 - $.getDay();
$.setDate($.getDate() + ae);
const re = [], K = new Date(z);
for (; K <= $; ) {
const ie = S(K), ve = {
date: new Date(K),
dateString: ie,
isToday: ie === S(/* @__PURE__ */ new Date()),
isCurrentMonth: K.getMonth() === k,
isPreviousMonth: K.getMonth() < k,
isNextMonth: K.getMonth() > k,
availability: Y.value.get(ie)
};
re.push(ve), K.setDate(K.getDate() + 1);
}
return re;
}), j = b(() => {
if (e.allowPreviousMonthNavigation || !x.value) return !0;
const r = new Date(w.value);
r.setMonth(r.getMonth() - 1), r.setDate(1);
const k = new Date(x.value);
return k.setDate(1), r >= k;
}), ee = b(() => {
if (!ne.value) return !0;
const r = new Date(w.value);
r.setMonth(r.getMonth() + 1), r.setDate(1);
const k = new Date(ne.value);
return k.setDate(1), r <= k;
}), W = b(() => {
var r, k, T, H, z, $, q, ae, re, K, ie, ve, ce;
return {
previousMonth: ((r = e.textLabels) == null ? void 0 : r.previousMonth) || "Previous month",
nextMonth: ((k = e.textLabels) == null ? void 0 : k.nextMonth) || "Next month",
bookingSummary: ((T = e.textLabels) == null ? void 0 : T.bookingSummary) || "Booking Summary",
nights: ((H = e.textLabels) == null ? void 0 : H.nights) || "nights",
night: ((z = e.textLabels) == null ? void 0 : z.night) || "night",
priceBreakdown: (($ = e.textLabels) == null ? void 0 : $.priceBreakdown) || "Price breakdown",
total: ((q = e.textLabels) == null ? void 0 : q.total) || "Total",
bookNow: ((ae = e.textLabels) == null ? void 0 : ae.bookNow) || "Book Now",
available: ((re = e.textLabels) == null ? void 0 : re.available) || "Available",
checkoutOnly: ((K = e.textLabels) == null ? void 0 : K.checkoutOnly) || "Checkout Only",
blocked: ((ie = e.textLabels) == null ? void 0 : ie.blocked) || "Blocked",
clearSelection: ((ve = e.textLabels) == null ? void 0 : ve.clearSelection) || "Clear selection",
dismissError: ((ce = e.textLabels) == null ? void 0 : ce.dismissError) || "Dismiss error"
};
}), L = b(() => {
const { checkIn: r, checkOut: k } = e.modelValue;
if (!r || !k) return null;
const T = new Date(r), H = new Date(k), z = Math.ceil((H.getTime() - T.getTime()) / (1e3 * 60 * 60 * 24));
if (z <= 0) return null;
const $ = [];
let q = 0;
const ae = new Date(T);
for (; ae < H; ) {
const re = S(ae), K = Y.value.get(re), ie = (K == null ? void 0 : K.price) || e.basePrice || 85;
$.push({ date: re, price: ie }), q += ie, ae.setDate(ae.getDate() + 1);
}
return {
basePrice: e.basePrice || 85,
nights: z,
dailyPrices: $,
totalPrice: q,
currency: e.currency || "GBP",
averagePerNight: q / z
};
}), S = (r) => {
const k = r.getFullYear(), T = String(r.getMonth() + 1).padStart(2, "0"), H = String(r.getDate()).padStart(2, "0");
return `${k}-${T}-${H}`;
}, v = (r) => new Intl.DateTimeFormat(e.locale, {
month: "long",
year: "numeric"
}).format(r), l = (r) => new Intl.NumberFormat(e.locale, {
style: "currency",
currency: e.currency || "GBP"
}).format(r), a = (r, k) => {
const T = new Date(r), H = new Date(k), z = new Intl.DateTimeFormat(e.locale, {
month: "short",
day: "numeric"
});
return `${z.format(T)} - ${z.format(H)}`;
}, o = (r) => new Intl.DateTimeFormat(e.locale, {
month: "short",
day: "numeric"
}).format(new Date(r)), u = (r) => r.map((k) => o(k)).join(", "), y = (r) => {
var k;
return ((k = r.availability) == null ? void 0 : k.price) || e.basePrice || 0;
}, O = (r) => {
if (!r.isCurrentMonth) return !0;
if (e.disablePastDates) {
const k = /* @__PURE__ */ new Date();
k.setHours(0, 0, 0, 0);
const T = new Date(r.date);
if (T.setHours(0, 0, 0, 0), T < k) return !0;
}
return !!(x.value && r.date < x.value || ne.value && r.date > ne.value);
}, D = (r) => !!(O(r) || f(r) === "blocked"), f = (r) => {
var k;
return ((k = r.availability) == null ? void 0 : k.status) || "available";
}, C = (r) => {
var k, T;
return ((T = (k = P.value) == null ? void 0 : k.blockedDates) == null ? void 0 : T.includes(r)) || !1;
}, M = (r, k, T = !1) => {
const H = new Date(r), z = new Date(k), $ = new Date(H), q = [], ae = [];
for ($.setDate($.getDate() + 1); $ < z || T && $.getTime() === z.getTime(); ) {
const re = S($), K = Y.value.get(re);
(K == null ? void 0 : K.status) === "checkout-only" && (!T || $ < z) ? ae.push(re) : (K == null ? void 0 : K.status) === "blocked" && q.push(re), $.setDate($.getDate() + 1);
}
return { blockedDates: q, checkoutOnlyDates: ae };
}, R = (r) => r === e.modelValue.checkIn || r === e.modelValue.checkOut, Z = (r) => {
const { checkIn: k, checkOut: T } = e.modelValue;
return !k || !T ? !1 : r > k && r < T;
}, le = (r) => r === e.modelValue.checkIn, n = (r) => r === e.modelValue.checkOut, E = (r) => {
const k = r.date.toLocaleDateString(e.locale, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
}), T = f(r), H = y(r);
return `${k}, ${T}${H ? `, ${l(H)}` : ""}`;
}, s = () => {
P.value = null;
}, g = (r, k) => {
const T = {
"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": (k == null ? void 0 : k[0]) || "Invalid date range selected.",
"checkout-only-in-range": "Cannot include checkout-only dates within your stay. These dates can only be used as the final checkout date."
};
return {
type: r,
message: T[r],
blockedDates: r === "blocked-dates-in-range" || r === "checkout-only-in-range" ? k : void 0
};
}, i = () => {
const r = { checkIn: null, checkOut: null };
p("update:modelValue", r), p("selection-change", r), s(), U.value = !1;
}, h = () => {
if (L.value && e.modelValue.checkIn && e.modelValue.checkOut) {
const r = {
selection: {
checkIn: e.modelValue.checkIn,
checkOut: e.modelValue.checkOut
},
calculation: L.value
};
p("book-now", r);
}
}, I = (r) => {
var $;
if (D(r)) return;
s();
const { checkIn: k, checkOut: T } = e.modelValue, H = r.dateString, z = f(r);
if (p("date-click", H, z), !k || k && T) {
if (z === "checkout-only") {
const ae = g("invalid-range", ["Checkout-only dates cannot be used as check-in dates"]);
P.value = ae, p("selection-error", ae);
return;
}
const q = { checkIn: H, checkOut: null };
p("update:modelValue", q), p("selection-change", q), U.value = !0;
} else if (k && !T) {
if (H === k && !e.allowSingleDay)
return;
const q = H < k ? H : k, ae = H < k ? k : H;
if (H < k && z === "checkout-only") {
const ce = g("invalid-range", ["Checkout-only dates cannot be used as check-in dates"]);
P.value = ce, p("selection-error", ce);
return;
}
if (H > k && (($ = Y.value.get(q)) == null ? void 0 : $.status) === "checkout-only") {
const de = g("invalid-range", ["Checkout-only dates cannot be used as check-in dates"]);
P.value = de, p("selection-error", de);
return;
}
const { blockedDates: re, checkoutOnlyDates: K } = M(q, ae, !0), ie = f({});
if (re.length > 0 || ie === "blocked") {
const ce = [...re];
ie === "blocked" && ce.push(ae);
const de = g("blocked-dates-in-range", ce);
P.value = de, p("selection-error", de);
const we = { checkIn: H, checkOut: null };
p("update:modelValue", we), p("selection-change", we), U.value = !0;
return;
}
if (K.length > 0) {
const ce = g("checkout-only-in-range", K);
P.value = ce, p("selection-error", ce);
const de = { checkIn: H, checkOut: null };
p("update:modelValue", de), p("selection-change", de), U.value = !0;
return;
}
const ve = { checkIn: q, checkOut: ae };
p("update:modelValue", ve), p("selection-change", ve), U.value = !1;
}
}, A = (r) => {
O(r) || (_.value = r.dateString);
}, G = () => {
if (!j.value) return;
const r = new Date(w.value);
r.setMonth(r.getMonth() - 1), w.value = r;
}, te = () => {
if (!ee.value) return;
const r = new Date(w.value);
r.setMonth(r.getMonth() + 1), w.value = r;
};
return pe(() => {
if (e.modelValue.checkIn) {
const r = new Date(e.modelValue.checkIn);
w.value = new Date(r.getFullYear(), r.getMonth(), 1);
}
}), ke(() => e.modelValue, (r, k) => {
if (r.checkIn && !r.checkOut ? U.value = !0 : U.value = !1, r.checkIn && r.checkIn !== (k == null ? void 0 : k.checkIn)) {
const T = new Date(r.checkIn);
w.value = new Date(T.getFullYear(), T.getMonth(), 1);
}
}, { deep: !0 }), ke(L, (r) => {
p("price-calculation", r);
}, { immediate: !0 }), (r, k) => {
var T, H, z;
return c(), d("div", {
class: V(["hotel-booking-calendar", [
`theme-${r.theme}`,
{ "show-prices": r.showPrices },
{ "has-selection-error": P.value }
]])
}, [
t("div", _e, [
t("button", {
type: "button",
class: "nav-button prev",
onClick: G,
disabled: !j.value,
"aria-label": W.value.previousMonth
}, " ‹ ", 8, Se),
t("div", Ce, [
t("h2", null, m(v(w.value)), 1)
]),
t("button", {
type: "button",
class: "nav-button next",
onClick: te,
disabled: !ee.value,
"aria-label": W.value.nextMonth
}, " › ", 8, Te)
]),
P.value && r.showSelectionErrors ? (c(), d("div", Me, [
k[0] || (k[0] = t("div", { class: "error-icon" }, "⚠️", -1)),
t("div", Ie, [
t("div", He, m(P.value.message), 1),
(T = P.value.blockedDates) != null && T.length ? (c(), d("div", Ee, " Blocked dates: " + m(u(P.value.blockedDates)), 1)) : N("", !0)
]),
t("button", {
onClick: s,
class: "error-dismiss",
"aria-label": W.value.dismissError
}, "✕", 8, Oe)
])) : N("", !0),
t("div", Be, [
t("div", Fe, [
(c(!0), d(B, null, F(X.value, ($) => (c(), d("div", {
key: $,
class: "weekday"
}, m($), 1))), 128)),
(c(!0), d(B, null, F(se.value, ($) => (c(), d("div", {
key: $.dateString,
class: V(["day-cell", {
"other-month": !$.isCurrentMonth,
today: $.isToday,
selected: R($.dateString),
"in-range": Z($.dateString),
"range-start": le($.dateString),
"range-end": n($.dateString),
available: f($) === "available",
blocked: f($) === "blocked",
"checkout-only": f($) === "checkout-only",
disabled: O($),
"click-disabled": D($),
"error-blocked": C($.dateString)
}]),
onClick: (q) => I($),
onMouseenter: (q) => A($),
role: "button",
tabindex: D($) ? -1 : 0,
"aria-label": E($),
onKeydown: [
be((q) => I($), ["enter"]),
be(me((q) => I($), ["prevent"]), ["space"])
]
}, [
t("div", Ve, [
t("span", Ne, m($.date.getDate()), 1),
r.showPrices && y($) ? (c(), d("div", Pe, m(l(y($))), 1)) : N("", !0)
])
], 42, We))), 128))
])
]),
r.showPriceCalculation && L.value ? (c(), d("div", Le, [
t("div", Ye, [
t("h4", null, m(W.value.bookingSummary), 1),
t("button", {
onClick: i,
class: "clear-btn",
"aria-label": W.value.clearSelection
}, "✕", 8, xe)
]),
t("div", ze, [
t("div", Ae, [
t("span", Re, m(L.value.nights) + " " + m(L.value.nights === 1 ? W.value.night : W.value.nights), 1),
t("span", Ue, m((H = r.modelValue) != null && H.checkIn && ((z = r.modelValue) != null && z.checkOut) ? a(
r.modelValue.checkIn,
r.modelValue.checkOut
) : ""), 1)
]),
L.value.dailyPrices.length > 1 ? (c(), d("div", Ge, [
t("details", je, [
t("summary", null, [
De(m(W.value.priceBreakdown) + " ", 1),
k[1] || (k[1] = t("span", { class: "arrow" }, "▼", -1))
]),
t("div", qe, [
(c(!0), d(B, null, F(L.value.dailyPrices, ($) => (c(), d("div", {
key: $.date,
class: "daily-row"
}, [
t("span", null, m(o($.date)), 1),
t("span", null, m(l($.price)), 1)
]))), 128))
])
])
])) : N("", !0),
t("div", Ke, [
t("div", Je, [
t("span", null, m(L.value.nights) + " × " + m(l(L.value.averagePerNight)), 1),
t("span", null, m(l(L.value.totalPrice)), 1)
]),
t("div", Qe, [
t("span", null, m(W.value.total), 1),
t("strong", null, m(l(L.value.totalPrice)), 1)
])
]),
t("button", {
onClick: h,
class: "book-btn",
disabled: !L.value
}, m(W.value.bookNow), 9, Xe)
])
])) : N("", !0),
(r.$slots.legend, c(), d("div", Ze, [
$e(r.$slots, "legend", {}, () => [
t("div", et, [
k[2] || (k[2] = t("div", { class: "legend-indicator available" }, null, -1)),
t("span", null, m(W.value.available), 1)
]),
t("div", tt, [
k[3] || (k[3] = t("div", { class: "legend-indicator checkout-only" }, null, -1)),
t("span", null, m(W.value.checkoutOnly), 1)
]),
t("div", nt, [
k[4] || (k[4] = t("div", { class: "legend-indicator blocked" }, null, -1)),
t("span", null, m(W.value.blocked), 1)
])
], !0)
]))
], 2);
};
}
}), ge = (J, oe) => {
const e = J.__vccOpts || J;
for (const [p, w] of oe)
e[p] = w;
return e;
}, ot = /* @__PURE__ */ ge(at, [["__scopeId", "data-v-405ca090"]]), st = { class: "dashboard-header" }, lt = ["aria-label"], rt = { class: "nav-text" }, it = { class: "current-month" }, ct = ["aria-label"], ut = { class: "nav-text" }, dt = { class: "calendar-container desktop-view" }, ht = { class: "calendar-grid" }, gt = { class: "grid-header" }, vt = { class: "room-header" }, kt = { class: "date-number" }, mt = { class: "date-weekday" }, yt = { class: "room-name" }, Dt = ["onClick", "title"], ft = { class: "span-content" }, wt = { class: "span-text" }, bt = ["onClick", "title"], pt = {
key: 0,
class: "booking-indicator"
}, $t = { class: "guest-initials" }, _t = { class: "calendar-container mobile-view" }, St = { class: "mobile-view-toggle" }, Ct = {
key: 0,
class: "vertical-mobile-view"
}, Tt = { class: "mobile-room-header" }, Mt = { class: "mobile-calendar-grid" }, It = { class: "mobile-weekdays" }, Ht = { class: "mobile-days" }, Et = ["onClick"], Ot = { class: "mobile-date-number" }, Bt = {
key: 0,
class: "mobile-booking-indicator"
}, Ft = { class: "mobile-guest-initials" }, Wt = {
key: 1,
class: "horizontal-mobile-view"
}, Vt = { class: "horizontal-calendar-container" }, Nt = { class: "horizontal-calendar-grid" }, Pt = { class: "horizontal-grid-header" }, Lt = { class: "horizontal-room-header" }, Yt = { class: "horizontal-date-number" }, xt = { class: "horizontal-date-weekday" }, zt = { class: "horizontal-room-name" }, At = ["onClick", "title"], Rt = { class: "horizontal-span-content" }, Ut = { class: "horizontal-span-text" }, Gt = ["onClick", "title"], jt = {
key: 0,
class: "horizontal-booking-indicator"
}, qt = { class: "horizontal-guest-initials" }, Kt = { class: "legend" }, Jt = /* @__PURE__ */ he({
__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(J, { emit: oe }) {
const e = J, p = oe, w = ue(new Date(e.selectedMonth)), _ = ue(!1), U = [
{
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"
}
], P = b(
() => e.statusConfig || U
), x = b(() => {
var s, g, i, h, I, A;
return {
previousMonth: ((s = e.textLabels) == null ? void 0 : s.previousMonth) || "← Previous",
nextMonth: ((g = e.textLabels) == null ? void 0 : g.nextMonth) || "Next →",
room: ((i = e.textLabels) == null ? void 0 : i.room) || "Room",
available: ((h = e.textLabels) == null ? void 0 : h.available) || "Available",
createBooking: ((I = e.textLabels) == null ? void 0 : I.createBooking) || "Click to create booking",
clickForDetails: ((A = e.textLabels) == null ? void 0 : A.clickForDetails) || "Click for details"
};
}), ne = b(() => {
const s = new Intl.DateTimeFormat("en-US", { weekday: "short" }), g = [];
for (let i = 0; i < 7; i++) {
const h = new Date(2023, 0, i + 1);
g.push(s.format(h));
}
return g;
}), Y = b(() => {
const s = w.value.getFullYear(), g = w.value.getMonth(), i = new Date(s, g + 1, 0).getDate(), h = /* @__PURE__ */ new Date();
h.setHours(0, 0, 0, 0);
const I = [];
for (let A = 1; A <= i; A++) {
const G = new Date(s, g, A), te = `${s}-${String(g + 1).padStart(2, "0")}-${String(A).padStart(2, "0")}`;
I.push({
day: A,
dateString: te,
date: G,
isToday: G.getTime() === h.getTime(),
isWeekend: G.getDay() === 0 || G.getDay() === 6,
weekday: new Intl.DateTimeFormat("en-US", { weekday: "short" }).format(G)
});
}
return I;
}), X = b(() => {
const s = {};
return e.rooms.forEach((g) => {
s[g.id] = [], e.bookings.filter(
(h) => h.roomNumber === g.number
).forEach((h) => {
const I = new Date(h.checkIn), A = new Date(h.checkOut), G = w.value.getFullYear(), te = w.value.getMonth(), r = new Date(G, te, 1), k = new Date(G, te + 1, 0);
if (I <= k && A > r) {
let T, H;
if (I >= r ? T = I.getDate() : T = 1, A <= k ? H = A.getDate() - 1 : H = k.getDate(), H = Math.max(T, H), T >= 1 && H <= k.getDate() && T <= H) {
const z = v(h.status);
s[g.id].push({
booking: h,
startDay: T,
endDay: H,
length: H - T + 1,
statusConfig: z
});
}
}
}), s[g.id].sort((h, I) => h.startDay - I.startDay);
}), s;
}), se = (s) => new Intl.DateTimeFormat("en-US", {
year: "numeric",
month: "long"
}).format(s), j = (s) => {
if (s < 0 && !e.allowPreviousMonthNavigation) {
const i = /* @__PURE__ */ new Date(), h = new Date(w.value.getFullYear(), w.value.getMonth(), 1), I = new Date(i.getFullYear(), i.getMonth(), 1);
if (h <= I) return;
}
const g = new Date(w.value);
g.setMonth(g.getMonth() + s), w.value = g, p("update:selectedMonth", g);
}, ee = (s, g) => {
try {
return !s || !g ? (console.warn("HotelDashboardCalendar: Invalid room or dateString in hasBooking", { room: s, dateString: g }), !1) : Array.isArray(e.bookings) ? !!e.bookings.find((h) => {
try {
if (!h || typeof h != "object")
return console.warn("HotelDashboardCalendar: Invalid booking object", h), !1;
if (!h.roomNumber || !h.checkIn || !h.checkOut)
return console.warn("HotelDashboardCalendar: Missing required booking properties", h), !1;
const I = h.roomNumber === s.number && W(g, h);
return h.roomNumber === s.number && !I && console.debug(
"HotelDashboardCalendar: Date range mismatch",
{
room: s.number,
date: g,
booking: {
checkIn: h.checkIn,
checkOut: h.checkOut,
normalized: {
checkIn: new Date(h.checkIn).toISOString(),
checkOut: new Date(h.checkOut).toISOString(),
date: new Date(g).toISOString()
}
}
}
), I;
} catch (I) {
return console.error("HotelDashboardCalendar: Error processing booking", I, h), !1;
}
}) : (console.warn("HotelDashboardCalendar: bookings prop is not an array", e.bookings), !1);
} catch (i) {
return console.error("HotelDashboardCalendar: Error in hasBooking", i, { room: s, dateString: g }), !1;
}
}, W = (s, g) => {
try {
if (!s || !(g != null && g.checkIn) || !(g != null && g.checkOut))
return console.warn("HotelDashboardCalendar: Invalid date or booking in isDateInBookingRange", { dateString: s, booking: g }), !1;
let i, h, I;
try {
if (i = new Date(s), h = new Date(g.checkIn), I = new Date(g.checkOut), isNaN(i.getTime()) || isNaN(h.getTime()) || isNaN(I.getTime()))
return console.warn("HotelDashboardCalendar: Invalid date format", {
date: s,
checkIn: g.checkIn,
checkOut: g.checkOut
}), !1;
} catch (G) {
return console.error("HotelDashboardCalendar: Date parsing error", G, { dateString: s, booking: g }), !1;
}
i.setHours(0, 0, 0, 0), h.setHours(0, 0, 0, 0), I.setHours(0, 0, 0, 0);
const A = h.getTime() === I.getTime();
return !A && I <= h ? (console.warn("HotelDashboardCalendar: Invalid booking dates (checkOut <= checkIn)", {
checkIn: g.checkIn,
checkOut: g.checkOut
}), !1) : A ? i.getTime() === h.getTime() : i >= h && i < I;
} catch (i) {
return console.error("HotelDashboardCalendar: Error in isDateInBookingRange", i, { dateString: s, booking: g }), !1;
}
}, L = (s, g) => e.bookings.find(
(i) => i.roomNumber === s.number && W(g, i)
) || null, S = (s, g) => {
const i = L(s, g);
return i ? i.status : "available";
}, v = (s) => P.value.find((g) => g.key === s) || P.value[0], l = (s, g) => {
const i = [], h = Y.value.find((I) => I.dateString === g);
return h != null && h.isToday && i.push("is-today"), h != null && h.isWeekend && i.push("is-weekend"), ee(s, g) && i.push("has-booking"), i;
}, a = (s, g) => {
const i = S(s, g), h = v(i);
return {
backgroundColor: e.theme === "dark" && h.darkBackgroundColor ? h.darkBackgroundColor : h.backgroundColor,
color: h.color
};
}, o = (s, g) => {
const i = L(s, g);
return i ? i.guestName.split(" ").map((h) => h.charAt(0).toUpperCase()).join("").substring(0, 2) : "";
}, u = (s, g) => {
const i = new Date(s), I = new Date(g).getTime() - i.getTime();
return Math.ceil(I / (1e3 * 60 * 60 * 24));
}, y = (s, g) => {
const i = L(s, g);
if (!i) return `${x.value.room} ${s.number} - ${x.value.available} (${x.value.createBooking})`;
const h = v(i.status), I = u(i.checkIn, i.checkOut);
return `${i.guestName} - ${x.value.room} ${s.number} - ${h.label} - ${I} nights (${x.value.clickForDetails})`;
}, O = (s, g) => {
const i = L(s, g);
i ? p("booking-click", i) : p("booking-create", { roomId: s.id, date: g });
}, D = (s) => {
var A;
const g = [], i = X.value[s.id] || [], h = /* @__PURE__ */ new Set(), I = [...i].sort((G, te) => G.startDay - te.startDay);
for (let G = 1; G <= Y.value.length; G++) {
if (h.has(G)) continue;
const te = I.find((r) => r.startDay === G);
if (te) {
const r = ((A = Y.value[G - 1]) == null ? void 0 : A.dateString) || "";
g.push({
type: "booking-span",
booking: te.booking,
startDay: te.startDay,
endDay: te.endDay,
length: te.length,
statusConfig: te.statusConfig,
startDate: r
});
for (let k = te.startDay; k <= te.endDay; k++)
h.add(k);
} else {
const r = Y.value[G - 1];
r && g.push({
type: "date-cell",
dateString: r.dateString,
day: G
});
}
}
return g;
}, f = (s) => ({
backgroundColor: e.theme === "dark" && s.statusConfig.darkBackgroundColor ? s.statusConfig.darkBackgroundColor : s.statusConfig.backgroundColor,
color: s.statusConfig.color,
border: `1px solid ${s.statusConfig.color}`,
borderRadius: "4px",
gridColumnStart: (s.startDay + 1).toString(),
// +1 for room column
gridColumnEnd: (s.endDay + 2).toString()
// +2 for room column and exclusive end
}), C = (s) => {
const g = u(s.booking.checkIn, s.booking.checkOut), i = s.booking.guestName;
return s.length === 1 ? i.split(" ").map((h) => h.charAt(0)).join("").substring(0, 2) : s.length <= 3 ? i.split(" ")[0] : s.length <= 6 ? `${i.split(" ")[0]} (${g}n)` : `${i} - ${g} nights`;
}, M = (s) => {
const g = u(s.booking.checkIn, s.booking.checkOut), i = e.rooms.find((h) => h.number === s.booking.roomNumber);
return `${s.booking.guestName} - Room ${i == null ? void 0 : i.number} - ${s.statusConfig.label} - ${g} nights`;
}, R = (s) => {
p("booking-click", s);
}, Z = () => {
_.value = !1;
}, le = () => {
_.value = !0, fe(() => {
n();
});
}, n = () => {
const s = /* @__PURE__ */ new Date(), g = `${s.getFullYear()}-${String(s.getMonth() + 1).padStart(2, "0")}-${String(s.getDate()).padStart(2, "0")}`, i = Y.value.findIndex((h) => h.dateString === g);
if (i !== -1) {
const h = document.querySelector(".horizontal-calendar-container");
if (h) {
const G = i * 80 + 60 - h.clientWidth / 2 + 40;
h.scrollTo({
left: Math.max(0, G),
behavior: "smooth"
});
}
}
}, E = b(() => Y.value.length === 0 ? 0 : new Date(Y.value[0].dateString).getDay());
return ke(() => e.selectedMonth, (s) => {
s && (w.value = new Date(s), _.value && fe(() => {
n();
}));
}), ke(_, (s) => {
s && fe(() => {
n();
});
}), (s, g) => (c(), d("div", {
class: V(["hotel-dashboard-calendar", `theme-${s.theme}`]),
style: Q({ "--days-in-month": Y.value.length })
}, [
t("div", st, [
t("button", {
onClick: g[0] || (g[0] = (i) => j(-1)),
class: "nav-btn",
"aria-label": x.value.previousMonth
}, [
t("span", rt, m(x.value.previousMonth), 1),
g[2] || (g[2] = t("span", { class: "nav-arrow" }, "←", -1))
], 8, lt),
t("h2", it, m(se(w.value)), 1),
t("button", {
onClick: g[1] || (g[1] = (i) => j(1)),
class: "nav-btn",
"aria-label": x.value.nextMonth
}, [
t("span", ut, m(x.value.nextMonth), 1),
g[3] || (g[3] = t("span", { class: "nav-arrow" }, "→", -1))
], 8, ct)
]),
t("div", dt, [
t("div", ht, [
t("div", gt, [
t("div", vt, m(x.value.room), 1),
(c(!0), d(B, null, F(Y.value, (i) => (c(), d("div", {
key: i.dateString,
class: V(["date-header", {
"is-today": i.isToday,
"is-weekend": i.isWeekend
}])
}, [
t("div", kt, m(i.day), 1),
t("div", mt, m(i.weekday), 1)
], 2))), 128))
]),
(c(!0), d(B, null, F(e.rooms, (i) => (c(), d("div", {
key: i.id,
class: "room-row"
}, [
t("div", yt, m(i.number), 1),
(c(!0), d(B, null, F(D(i), (h, I) => (c(), d(B, {
key: `${i.id}-${I}`
}, [
h.type === "booking-span" ? (c(), d("div", {
key: 0,
class: V(["booking-span-cell", l(i, h.startDate)]),
style: Q(f(h)),
onClick: (A) => R(h.booking),
title: M(h)
}, [
t("div", ft, [
t("span", wt, m(C(h)), 1)
])
], 14, Dt)) : (c(), d("div", {
key: 1,
class: V(["date-cell", l(i, h.dateString)]),
style: Q(a(i, h.dateString)),
onClick: (A) => O(i, h.dateString),
title: y(i, h.dateString)
}, [
ee(i, h.dateString) ? (c(), d("div", pt, [
t("span", $t, m(o(i, h.dateString)), 1)
])) : N("", !0)
], 14, bt))
], 64))), 128))
]))), 128))
])
]),
t("div", _t, [
t("div", St, [
t("button", {
onClick: Z,
class: V([{ active: !_.value }, "toggle-btn"])
}, g[4] || (g[4] = [
t("span", { class: "toggle-icon" }, "⊞", -1),
t("span", { class: "toggle-text" }, "Vertical", -1)
]), 2),
t("button", {
onClick: le,
class: V([{ active: _.value }, "toggle-btn"])
}, g[5] || (g[5] = [
t("span", { class: "toggle-icon" }, "⊟", -1),
t("span", { class: "toggle-text" }, "Horizontal", -1)
]), 2)
]),
_.value ? N("", !0) : (c(), d("div", Ct, [
(c(!0), d(B, null, F(e.rooms, (i) => (c(), d("div", {
key: i.id,
class: "mobile-room-calendar"
}, [
t("div", Tt, [
t("h3", null, m(x.value.room) + " " + m(i.number), 1)
]),
t("div", Mt, [
t("div", It, [
(c(!0), d(B, null, F(ne.value, (h) => (c(), d("div", {
key: h,
class: "mobile-weekday"
}, m(h), 1))), 128))
]),
t("div", Ht, [
(c(!0), d(B, null, F(E.value, (h) => (c(), d("div", {
key: `empty-${h}`,
class: "mobile-date-cell empty"
}))), 128)),
(c(!0), d(B, null, F(Y.value, (h) => (c(), d("div", {
key: h.dateString,
class: V(["mobile-date-cell", [
l(i, h.dateString),
{ "other-month": h.date.getMonth() !== w.value.getMonth() }
]]),
style: Q(ee(i, h.dateString) ? a(i, h.dateString) : {}),
onClick: (I) => O(i, h.dateString)
}, [
t("div", Ot, m(h.day), 1),
ee(i, h.dateString) ? (c(), d("div", Bt, [
t("span", Ft, m(o(i, h.dateString)), 1)
])) : N("", !0)
], 14, Et))), 128))
])
])
]))), 128))
])),
_.value ? (c(), d("div", Wt, [
t("div", Vt, [
t("div", Nt, [
t("div", Pt, [
t("div", Lt, m(x.value.room), 1),
(c(!0), d(B, null, F(Y.value, (i) => (c(), d("div", {
key: i.dateString,
class: V(["horizontal-date-header", {
"is-today": i.isToday,
"is-weekend": i.isWeekend
}])
}, [
t("div", Yt, m(i.day), 1),
t("div", xt, m(i.weekday), 1)
], 2))), 128))
]),
(c(!0), d(B, null, F(e.rooms, (i) => (c(), d("div", {
key: i.id,
class: "horizontal-room-row"
}, [
t("div", zt, m(i.number), 1),
(c(!0), d(B, null, F(D(i), (h, I) => (c(), d(B, {
key: `${i.id}-${I}`
}, [
h.type === "booking-span" ? (c(), d("div", {
key: 0,
class: V(["horizontal-booking-span-cell", l(i, h.startDate)]),
style: Q(f(h)),
onClick: (A) => R(h.booking),
title: M(h)
}, [
t("div", Rt, [
t("span", Ut, m(C(h)), 1)
])
], 14, At)) : (c(), d("div", {
key: 1,
class: V(["horizontal-date-cell", l(i, h.dateString)]),
style: Q(a(i, h.dateString)),
onClick: (A) => O(i, h.dateString),
title: y(i, h.dateString)
}, [
ee(i, h.dateString) ? (c(), d("div", jt, [
t("span", qt, m(o(i, h.dateString)), 1)
])) : N("", !0)
], 14, Gt))
], 64))), 128))
]))), 128))
])
])
])) : N("", !0)
]),
t("div", Kt, [
(c(!0), d(B, null, F(P.value, (i) => (c(), d("div", {
key: i.key,
class: "legend-item"
}, [
t("div", {
class: "legend-color",
style: Q({
backgroundColor: i.backgroundColor,
borderColor: i.color
})
}, null, 4),
t("span", null, m(i.label), 1)
]))), 128))
])
], 6));
}
}), Qt = /* @__PURE__ */ ge(Jt, [["__scopeId", "data-v-e4727a14"]]), Xt = { class: "months-grid" }, Zt = ["onClick"], en = { class: "month-header" }, tn = { class: "month-name" }, nn = {
key: 0,
class: "event-count"
}, an = { class: "mini-calendar" }, on = {
key: 0,
class: "mini-day empty"
}, sn = ["onClick"], ln = {
key: 0,
class: "category-indicators"
}, rn = ["title"], cn = { class: "year-summary" }, un = { class: "summary-item" }, dn = { class: "summary-value" }, hn = { class: "summary-item" }, gn = { class: "summary-value" }, vn = { class: "summary-item" }, kn = { class: "summary-value" }, mn = { class: "summary-item" }, yn = { class: "summary-value" }, Dn = /* @__PURE__ */ he({
__name: "YearlyView",
props: {
year: {},
events: {},
theme: {},
locale: {},
categories: {},
highlightToday: { type: Boolean, default: !0 },
firstDayOfWeek: { default: 0 }
},
emits: ["month-click", "date-click"],
setup(J, { emit: oe }) {
const e = J, p = oe, w = b(() => {
const S = new Intl.DateTimeFormat(e.locale, { weekday: "narrow" }), v = [];
for (let l = 0; l < 7; l++) {
const a = (e.firstDayOfWeek + l) % 7, o = new Date(2023, 0, 1 + a);
v.push(S.format(o));
}
return v;
}), _ = b(() => {
const S = new Intl.DateTimeFormat(e.locale, { month: "long" }), v = new Intl.DateTimeFormat(e.locale, { month: "short" }), l = /* @__PURE__ */ new Date();
return Array.from({ length: 12 }, (a, o) => {
const u = new Date(e.year, o, 1), y = Y(o);
return {
date: u,
month: o,
year: e.year,
name: S.format(u),
shortName: v.format(u),
eventCount: y,
hasEvents: y > 0,
isCurrentMonth: l.getFullYear() === e.year && l.getMonth() === o
};
});
}), U = b(() => _.value.reduce((S, v) => S + v.eventCount, 0)), P = b(() => _.value.reduce((S, v) => !S || v.eventCount > S.eventCount ? v : S, null)), x = b(() => _.value.filter((S) => S.hasEvents).length), ne = b(() => U.value === 0 ? 0 : Math.round(U.value / 12)), Y = (S) => e.events.filter((v) => {
const l = new Date(v.start), a = new Date(v.end), o = new Date(e.year, S, 1), u = new Date(e.year, S + 1, 0, 23, 59, 59);
return l <= u && a >= o;
}).length, X = (S) => {
const v = new Date(e.year, S.month, 1), a = new Date(e.year, S.month + 1, 0).getDate(), o = v.getDay(), u = [];
for (let y = 0; y < o; y++)
u.push(null);
for (let y = 1; y <= a; y++)
u.push(y);
return u;
}, se = (S) => {
const v = /* @__PURE__ */ new Set();
return e.events.forEach((l) => {
const a = new Date(l.start), o = new Date(l.end), u = new Date(e.year, S.month, 1), y = new Date(e.year, S.month + 1, 0);
if (a <= y && o >= u) {
const O = a.getMonth() === S.month && a.getFullYear() === e.year ? a.getDate() : 1, D = o.getMonth() === S.month && o.getFullYear() === e.year ? o.getDate() : y.getDate();
for (let f = O; f <= D; f++)
v.add(f);
}
}), Array.from(v);
}, j = (S) => {
const v = /* @__PURE__ */ new Set();
return e.events.forEach((l) => {
const a = new Date(l.start), o = new Date(l.end), u = new Date(e.year, S.month, 1), y = new Date(e.year, S.month + 1, 0);
a <= y && o >= u && l.categoryId && v.add(l.categoryId);
}), e.categories.filter((l) => v.has(l.id)).slice(0, 5);
}, ee = (S, v) => {
const l = /* @__PURE__ */ new Date(), a = e.highlightToday && l.getFullYear() === e.year && l.getMonth() === S.month && l.getDate() === v, o = se(S).includes(v);
return {
"is-today": a,
"has-event": o
};
}, W = (S) => {
p("month-click", S);
}, L = (S, v) => {
const l = `${e.year}-${String(S.month + 1).padStart(2, "0")}-${String(v).padStart(2, "0")}`;
p("date-click", l);
};
return (S, v) => {
var l;
return c(), d("div", {
class: V(["yearly-view", [`theme-${S.theme}`]])
}, [
t("div", Xt, [
(c(!0), d(B, null, F(_.value, (a) => (c(), d("div", {
key: a.month,
class: V(["month-card", { "current-month": a.isCurrentMonth, "has-events": a.hasEvents }]),
onClick: (o) => W(a.month)
}, [
t("div", en, [
t("span", tn, m(a.name), 1),
a.eventCount > 0 ? (c(), d("span", nn, m(a.eventCount), 1)) : N("", !0)
]),
t("div", an, [
(c(!0), d(B, null, F(w.value, (o) => (c(), d("div", {
key: o,
class: "mini-weekday"
}, m(o), 1))), 128)),
(c(!0), d(B, null, F(X(a), (o, u) => (c(), d(B, { key: u }, [
o === null ? (c(), d("div", on)) : (c(), d("div", {
key: 1,
class: V(["mini-day", ee(a, o)]),
onClick: me((y) => L(a, o), ["stop"])
}, m(o), 11, sn))
], 64))), 128))
]),
j(a).length > 0 ? (c(), d("div", ln, [
(c(!0), d(B, null, F(j(a), (o, u) => (c(), d("div", {
key: u,
class: "category-dot",
style: Q({ backgroundColor: o.color }),
title: o.name
}, null, 12, rn))), 128))
])) : N("", !0)
], 10, Zt))), 128))
]),
t("div", cn, [
t("div", un, [
t("div", dn, m(U.value), 1),
v[0] || (v[0] = t("div", { class: "summary-label" }, "Total Events", -1))
]),
t("div", hn, [
t("div", gn, m(((l = P.value) == null ? void 0 : l.shortName) || "-"), 1),
v[1] || (v[1] = t("div", { class: "summary-label" }, "Busiest Month", -1))
]),
t("div", vn, [
t("div", kn, m(x.value), 1),
v[2] || (v[2] = t("div", { class: "summary-label" }, "Active Months", -1))
]),
t("div", mn, [
t("div", yn, m(ne.value), 1),
v[3] || (v[3] = t("div", { class: "summary-label" }, "Avg per Month", -1))
])
])
], 2);
};
}
}), fn = /* @__PURE__ */ ge(Dn, [["__scopeId", "data-v-02063305"]]), wn = { class: "weekday-headers" }, bn = {
key: 0,
class: "weekday-header week-number-header"
}, pn = {
key: 0,
class: "week-number-cell"
}, $n = ["onClick"], _n = { class: "day-header" }, Sn = {
key: 0,
class: "more-events"
}, Cn = { class: "events-list" }, Tn = ["title", "onClick"], Mn = {
key: 0,
class: "event-time"
}, In = /* @__PURE__ */ he({
__name: "MonthlyView",
props: {
currentDate: {},
events: {},
theme: {},
locale: {},
categories: {},
firstDayOfWeek: {},
highlightToday: { type: Boolean, default: !0 },
highlightWeekends: { type: Boolean, default: !1 },
maxEventsPerSlot: { default: 3 },
showWeekNumbers: { type: Boolean, default: !1 }
},
emits: ["date-click", "event-click", "slot-click"],
setup(J, { emit: oe }) {
const e = J, p = oe, w = b(() => {
const v = new Intl.DateTimeFormat(e.locale, { weekday: "short" }), l = [];
for (let a = 0; a < 7; a++) {
const o = (e.firstDayOfWeek + a) % 7, u = new Date(2023, 0, 1 + o);
l.push(v.format(u));
}
return l;
}), _ = b(() => {
const v = e.currentDate.getFullYear(), l = e.currentDate.getMonth(), a = new Date(v, l, 1), o = new Date(a);
let y = (o.getDay() - e.firstDayOfWeek + 7) % 7;
o.setDate(o.getDate() - y);
const O = new Date(o);
O.setDate(O.getDate() + 41);
const D = [], f = new Date(o), C = /* @__PURE__ */ new Date();
for (C.setHours(0, 0, 0, 0); f <= O; ) {
const M = P(f), R = Y(M);
D.push({
date: new Date(f),
dateString: M,
isToday: f.getTime() === C.getTime(),
isCurrentMonth: f.getMonth() === l,
isCurrentWeek: x(f, C),
isWeekend: f.getDay() === 0 || f.getDay() === 6,
isWorkingDay: ne(f),
events: R
}), f.setDate(f.getDate() + 1);
}
return D;
}), U = b(() => P(e.currentDate)), P = (v) => {
const l = v.getFullYear(), a = String(v.getMonth() + 1).padStart(2, "0"), o = String(v.getDate()).padStart(2, "0");
return `${l}-${a}-${o}`;
}, x = (v, l) => {
const a = new Date(v), o = new Date(l);
a.setHours(0, 0, 0, 0), o.setHours(0, 0, 0, 0);
const u = new Date(a);
u.setDate(a.getDate() - (a.getDay() - e.firstDayOfWeek + 7) % 7);
const y = new Date(o);
return y.setDate(o.getDate() - (o.getDay() - e.firstDayOfWeek + 7) % 7), u.getTime() === y.getTime();
}, ne = (v) => {
const l = v.getDay();
return l !== 0 && l !== 6;
}, Y = (v) => e.events.filter((l) => {
const a = new Date(l.start), o = new Date(l.end), u = new Date(v);
return a.setHours(0, 0, 0, 0), o.setHours(23, 59, 59, 999), u.setHours(12, 0, 0, 0), u >= a && u <= o;
}).sort((l, a) => l.allDay && !a.allDay ? -1 : !l.allDay && a.allDay ? 1 : new Date(l.start).getTime() - new Date(a.start).getTime()), X = (v) => Y(v), se = (v) => v === U.value, j = (v) => {
let l = v.backgroundColor || "#3b82f6", a = v.color || "#ffffff";
if (v.categoryId) {
const o = e.categories.find((u) => u.id === v.categoryId);
o && (l = e.theme === "dark" && o.darkBackgroundColor ? o.darkBackgroundColor : o.backgroundColor, a = o.color);
}
return {
backgroundColor: l,
color: a,
borderLeft: `3px solid ${a}`
};
}, ee = (v) => {
const l = new Date(v);
return new Intl.DateTimeFormat(e.locale, {
hour: "numeric",
minute: "2-digit"
}).format(l);
}, W = (v) => {
p("date-click", v.dateString), p("slot-click", {
start: `${v.dateString}T09:00:00`,
end: `${v.dateString}T10:00:00`,
date: v.dateString
});
}, L = (v) => {
p("event-click", v);
}, S = (v) => {
const l = new Date(Date.UTC(v.getFullYear(), v.getMonth(), v.getDate())), a = l.getUTCDay() || 7;
l.setUTCDate(l.getUTCDate() + 4 - a);
const o = new Date(Date.UTC(l.getUTCFullYear(), 0, 1));
return Math.ceil(((l.getTime() - o.getTime()) / 864e5 + 1) / 7);
};
return (v, l) => (c(), d("div", {
class: V(["monthly-view", [`theme-${v.theme}`, { "show-week-numbers": v.showWeekNumbers }]])
}, [
t("div", wn, [
v.showWeekNumbers ? (c(), d("div", bn, "Wk")) : N("", !0),
(c(!0), d(B, null, F(w.value, (a) => (c(), d("div", {
key: a,
class: "weekday-header"
}, m(a), 1))), 128))
]),
t("div", {
class: V(["calendar-grid", { "with-week-numbers": v.showWeekNumbers }])
}, [
(c(!0), d(B, null, F(_.value, (a, o) => (c(), d(B, {
key: a.dateString
}, [
v.showWeekNumbers && o % 7 === 0 ? (c(), d("div", pn, m(S(a.date)), 1)) : N("", !0),
t("div", {
class: V(["day-cell", {
"other-month": !a.isCurrentMonth,
today: v.highlightToday && a.isToday,
weekend: v.highlightWeekends && a.isWeekend,
selected: se(a.dateString)
}]),
onClick: (u) => W(a)
}, [
t("div", _n, [
t("span", {
class: V(["day-number", { "today-number": v.highlightToday && a.isToday }])
}, m(a.date.getDate()), 3),
X(a.dateString).length > v.maxEventsPerSlot ? (c(), d("span", Sn, " +" + m(X(a.dateString).length - v.maxEventsPerSlot), 1)) : N("", !0)
]),
t("div", Cn, [
(c(!0), d(B, null, F(X(a.dateString).slice(0, v.maxEventsPerSlot), (u) => (c(), d("div", {
key: u.id,
class: "event-item",
style: Q(j(u)),
title: u.title,
onClick: me((y) => L(u), ["stop"])
}, [
u.allDay ? N("", !0) : (c(), d("span", Mn, m(ee(u.start)), 1)),
De(" " + m(u.title), 1)
], 12, Tn))), 128))
])
], 10, $n)
], 64))), 128))
], 2)
], 2));
}
}), Hn = /* @__PURE__ */ ge(In, [["__scopeId", "data-v-3ffb4cee"]]), En = { class: "week-header" }, On = { class: "day-name" }, Bn = {
key: 0,
class: "all-day-row"
}, Fn = ["onClick"], Wn = ["onClick"], Vn = { class: "time-grid-container" }, Nn = { class: "time-grid" }, Pn = ["onClick"], Ln = ["onClick"], Yn = { class: "event-title" }, xn = { class: "event-time" }, zn = /* @__PURE__ */ he({
__name: "WeeklyView",
props: {
currentDate: {},
events: {},
theme: {},
locale: {},
categories: {},
workingHours: {},
timeInterval: {},
firstDayOfWeek: {},
showAllDaySlot: { type: Boolean },
highlightToday: { type: Boolean, default: !0 },
highlightWeekends: { type: Boolean, default: !1 },
showCurrentTimeIndicator: { type: Boolean, default: !0 },
slotHeight: { default: 48 },
eventMinHeight: { default: 20 }
},
emits: ["date-click", "event-click", "slot-click"],
setup(J, { emit: oe }) {
const e = J, p = oe, w = b(() => {
if (e.slotHeight) return e.slotHeight;
switch (e.timeInterval) {
case 15:
return 20;
case 30:
return 30;
default:
return 48;
}
}), _ = b(() => {
const l = [], a = P(e.currentDate), o = /* @__PURE__ */ new Date();
o.setHours(0, 0, 0, 0);
const u = new Intl.DateTimeFormat(e.locale, { weekday: "short" });
for (let y = 0; y < 7; y++) {
const O = new Date(a);
O.setDate(O.getDate() + y), l.push({
date: new Date(O),
dateString: x(O),
weekdayShort: u.format(O),
isToday: O.getTime() === o.getTime(),
isWeekend: O.getDay() === 0 || O.getDay() === 6
});
}
return l;
}), U = b(() => {
const l = [];
for (let a = 0; a < 24; a++)
l.push(a);
return l;
}), P = (l) => {
const a = new Date(l), o = a.getDay(), u = (o < e.firstDayOfWeek ? 7 : 0) + o - e.firstDayOfWeek;
return a.setDate(a.getDate() - u), a.setHours(0, 0, 0, 0), a;
}, x = (l) => {
const a = l.getFullYear(), o = String(l.getMonth() + 1).padStart(2, "0"), u = String(l.getDate()).padStart(2, "0");
return `${a}-${o}-${u}`;
}, ne = (l) => l >= e.workingHours.start && l < e.workingHours.end, Y = (l) => {
const a = /* @__PURE__ */ new Date();
return a.setHours(l, 0, 0, 0), new Intl.DateTimeFormat(e.locale, {
hour: "numeric",
hour12: !0
}).format(a);
}, X = (l) => e.events.filter((a) => {
if (!a.allDay) return !1;
const o = new Date(a.start), u = new Date(a.end), y = new Date(l);
return o.setHours(0, 0, 0, 0), u.setHours(23, 59, 59, 999), y.setHours(12, 0, 0, 0), y >= o && y <= u;
}), se = (l, a) => e.events.filter((o) => {
if (o.allDay) return !1;
const u = new Date(o.start), y = /* @__PURE__ */ new Date(`${l}T${String(a).padStart(2, "0")}:00:00`), O = new Date(y);
return O.setHours(O.getHours() + 1), u >= y && u < O;
}), j = (l) => {
let a = l.backgroundColor || "#3b82f6", o = l.color || "#ffffff";
if (l.categoryId) {
const u = e.categories.find((y) => y.id === l.categoryId);
u && (a = e.theme === "dark" && u.darkBackgroundColor ? u.darkBackgroundColor : u.backgroundColor, o = u.color);
}
return {
backgroundColor: a,
color: o,
borderLeft: `3px solid ${o}`
};
}, ee = (l) => {
const a = j(l), o = new Date(l.start), u = new Date(l.end), O = o.getMinutes() / 60 * w.value, f = (u.getTime() - o.getTime()) / (1e3 * 60 * 60), C = Math.max(f * w.value, e.eventMinHeight);
return {
...a,
top: `${O}px`,
height: `${C}px`,
minHeight: "20px"
};
}, W = (l) => {
const a = new Date(l.start), o = new Date(l.end), u = new Intl.DateTimeFormat(e.locale, {
hour: "numeric",
minute: "2-digit"
});
return `${u.format(a)} - ${u.format(o)}`;
}, L = (l, a) => {
const o = `${l.dateString}T${String(a).padStart(2, "0")}:00:00`, u = a + 1, y = `${l.dateString}T${String(u).padStart(2, "0")}:00:00`;
p("slot-click", { start: o, end: y, date: l.dateString, hour: a });
}, S = (l) => {
p("slot-click", {
start: `${l.dateString}T00:00:00`,
end: `${l.dateString}T23:59:59`,
date: l.dateString,
hour: 0
});
}, v = (l) => {
p("event-click", l);
};
return (l, a) => (c(), d("div", {
class: V(["weekly-view", [`theme-${l.theme}`]]),
style: Q({ "--slot-height": `${w.value}px`, "--event-min-height": `${l.eventMinHeight}px` })
}, [
t("div", En, [
a[0] || (a[0] = t("div", { class: "time-column-header" }, null, -1)),
(c(!0), d(B, null, F(_.value, (o) => (c(), d("div", {
key: o.dateString,
class: V(["day-column-header", { "is-today": l.highlightToday && o.isToday, "is-weekend": l.highlightWeekends && o.isWeekend }])
}, [
t("div", On, m(o.weekdayShort), 1),
t("div", {
class: V(["day-number", { "today-number": l.highlightToday && o.isToday }])
}, m(o.date.getDate()), 3)
], 2))), 128))
]),
l.showAllDaySlot ? (c(), d("div", Bn, [
a[1] || (a[1] = t("div", { class: "time-label all-day-label" }, "All Day", -1)),
(c(!0), d(B, null, F(_.value, (o) => (c(), d("div", {
key: `allday-${o.dateString}`,
class: V(["all-day-cell", { "is-today": l.highlightToday && o.isToday }]),
onClick: (u) => S(o)
}, [
(c(!0), d(B, null, F(X(o.dateString), (u) => (c(), d("div", {
key: u.id,
class: "all-day-event",
style: Q(j(u)),
onClick: me((y) => v(u), ["stop"])
}, m(u.title), 13, Wn))), 128))
], 10, Fn))), 128))
])) : N("", !0),
t("div", Vn, [
t("div", Nn, [
(c(!0), d(B, null, F(U.value, (o) => (c(), d("div", {
key: o,
class: "time-row"
}, [
t("div", {
class: V(["time-label", { "working-hour": ne(o) }])
}, m(Y(o)), 3),
(c(!0), d(B, null, F(_.value, (u) => (c(), d("div", {
key: `${u.dateString}-${o}`,
class: V(["time-slot", {
"working-hour": ne(o),
"is-today": l.highlightToday && u.isToday,
"is-weekend": l.highlightWeekends && u.isWeekend
}]),
onClick: (y) => L(u, o)
}, [
(c(!0), d(B, null, F(se(u.dateString, o), (y) => (c(), d("div", {
key: y.id,
class: "slot-event",
style: Q(ee(y)),
onClick: me((O) => v(y), ["stop"])
}, [
t("div", Yn, m(y.title), 1),
t("div", xn, m(W(y)), 1)
], 12, Ln))), 128))
], 10, Pn))), 128))
]))), 128))
])
])
], 6));
}
}), An = /* @__PURE__ */ ge(zn, [["__scopeId", "data-v-cb3c7540"]]), Rn = { class: "day-header-section" }, Un = { class: "weekday-label" }, Gn = {
key: 0,
class: "all-day-section"
}, jn = {
key: 0,
class: "no-events"
}, qn = { class: "all-day-events" }, Kn = ["onClick"], Jn = { class: "event-title" }, Qn = {
key: 0,
class: "event-description"
}, Xn = { class: "time-grid-wrapper" }, Zn = { class: "time-grid" }, ea = ["onClick"], ta = { class: "hour-events" }, na = ["onClick"], aa = { class: "event-header" }, oa = { class: "event-title" }, sa = { class: "event-time-range" }, la = {
key: 0,
class: "event-location"
}, ra = { class: "summary-section" }, ia = { class: "summary-count" }, ca = /* @__PURE__ */ he({
__name: "DailyView",
props: {
currentDate: {},
events: {},
theme: {},
locale: {},
categories: {},
workingHours: {},
timeInterval: {},
showAllDaySlot: { type: Boolean },
highlightToday: { type: Boolean, default: !0 },
showCurrentTimeIndicator: { type: Boolean, default: !0 },
slotHeight: { default: 48 },
eventMinHeight: { default: 20 }
},
emits: ["event-click", "slot-click"],
setup(J, { emit: oe }) {
const e = J, p = oe, w = b(() => Y(e.currentDate)), _ = b(() => {
const a = /* @__PURE__ */ new Date();
a.setHours(0, 0, 0, 0);
const o = new Date(e.currentDate);
return o.setHours(0, 0, 0, 0), {
weekday: new Intl.DateTimeFormat(e.locale, { weekday: "long" }).format(e.currentDate),
dayNumber: e.currentDate.getDate(),
isToday: o.getTime() === a.getTime()
};
}), U = b(() => {
const a = [];
for (let o = 0; o < 24; o++)
a.push(o);
return a;
}), P = b(() => e.events.filter((a) => {
if (!a.allDay) return !1;
const o = new Date(a.start), u = new Date(a.end), y = new Date(w.value);
return o.setHours(0, 0, 0, 0), u.setHours(23, 59, 59, 999), y.setHours(12, 0, 0, 0), y >= o && y <= u;
})), x = b(() => ee().length), ne = b(() => (/* @__PURE__ */ new Date()).getMinutes() / 60 * 100), Y = (a) => {
const o = a.getFullYear(), u = String(a.getMonth() + 1).padStart(2, "0"), y = String(a.getDate()).padStart(2, "0");
return `${o}-${u}-${y}`;
}, X = (a) => a >= e.workingHours.start && a < e.workingHours.end, se = (a) => _.value.isToday ? (/* @__PURE__ */ new Date()).getHours() === a : !1, j = (a) => {
const o = /* @__PURE__ */ new Date();
return o.setHours(a, 0, 0, 0), new Intl.DateTimeFormat(e.locale, {
hour: "numeric",
minute: "2-digit",
hour12: !0
}).format(o);
}, ee = () => e.events.filter((a) => {
const o = new Date(a.start), u = new Date(a.end), y = new Date(w.value), O = new Date(y);
O.setHours(0, 0, 0, 0);
const D = new Date(y);
return D.setHours(23, 59, 59, 999), o <= D && u >= O;
}), W = (a) => e.events.filter((o) => {
if (o.allDay) return !1;
const u = new Date(o.start), y = new Date(o.end);
if (Y(u) !== w.value) return !1;
const D = u.getHours(), f = y.getHours() + (y.getMinutes() > 0 ? 1 : 0);
return a >= D && a < f;
}).sort((o, u) => new Date(o.start).getTime() - new Date(u.start).getTime()), L = (a) => {
let o = a.backgroundColor || "#3b82f6", u = a.color || "#ffffff";
if (a.categoryId) {
const y = e.categories.find((O) => O.id === a.categoryId);
y && (o = e.theme === "dark" && y.darkBackgroundColor ? y.darkBackgroundColor : y.backgroundColor, u = y.color);
}
return {
backgroundColor: o,
color: u,
borderLeft: `3px solid ${u}`
};
}, S = (a) => {
const o = new Date(a.start), u = new Date(a.end), y = new Intl.DateTimeFormat(e.locale, {
hour: "numeric",
minute: "2-digit"
});
return `${y.format(o)} - ${y.format(u)}`;
}, v = (a) => {
const o = `${w.value}T${String(a).padStart(2, "0")}:00:00`, u = a + 1, y = `${w.value}T${String(u).padStart(2, "0")}:00:00`;
p("slot-click", { start: o, end: y, date: w.value, hour: a });
}, l = (a) => {
p("event-click", a);
};
return (a, o) => (c(), d("div", {
class: V(["daily-view", [`theme-${a.theme}`]]),
style: Q({ "--slot-height": `${a.slotHeight}px`, "--event-min-height": `${a.eventMinHeight}px` })
}, [
t("div", Rn, [
t("div", Un, m(_.value.weekday), 1),
t("div", {
class: V(["date-display", { "is-today": a.highlightToday && _.value.isToday }])
}, m(_.value.dayNumber), 3)
]),
a.showAllDaySlot ? (c(), d("div", Gn, [
o[0] || (o[0] = t("div", { class: "section-label" }, "All Day Events", -1)),
P.value.length === 0 ? (c(), d("div", jn, "No all-day events")) : N("", !0),
t("div", qn, [
(c(!0), d(B, null, F(P.value, (u) => (c(), d("div", {
key: u.id,
class: "all-day-event",
style: Q(L(u)),
onClick: (y) => l(u)
}, [
t("div", Jn, m(u.title), 1),
u.description ? (c(), d("div", Qn, m(u.description), 1)) : N("", !0)
], 12, Kn))), 128))
])
])) : N("", !0),
t("div", Xn, [
t("div", Zn, [
(c(!0), d(B, null, F(U.value, (u) => (c(), d("div", {
key: u,
class: "hour-row"
}, [
t("div", {
class: V(["hour-label", { "working-hour": X(u) }])
}, m(j(u)), 3),
t("div", {
class: V(["hour-slot", {
"working-hour": X(u),
"current-hour": se(u)
}]),
onClick: (y) => v(u)
}, [
a.showCurrentTimeIndicator && se(u) ? (c(), d("div", {
key: 0,
class: "current-time-indicator",
style: Q({ top: `${ne.value}%` })
}, o[1] || (o[1] = [
t("div", { class: "time-dot" }, null, -1)
]), 4)) : N("", !0),
t("div", ta, [
(c(!0), d(B, null, F(W(u), (y) => (c(), d("div", {
key: y.id,
class: "hour-event",
style: Q(L(y)),
onClick: me((O) => l(y), ["stop"])
}, [
t("div", aa, [
t("span", oa, m(y.title), 1),
t("span", sa, m(S(y)), 1)
]),
y.location ? (c(), d("div", la, [
o[2] || (o[2] = t("span", { class: "location-icon" }, "📍", -1)),
De(" " + m(y.location), 1)
])) : N("", !0)
], 12, na))), 128))
])
], 10, ea)
]))), 128))
])
]),
t("div", ra, [
o[3] || (o[3] = t("span", { class: "summary-label" }, "Total events today:", -1)),
t("span", ia, m(x.value), 1)
])
], 6));
}
}), ua = /* @__PURE__ */ ge(ca, [["__scopeId", "data-v-406daf97"]]), da = { class: "day-info-header" }, ha = { class: "date-info" }, ga = { class: "weekday-name" }, va = { class: "full-date" }, ka = { class: "day-status" }, ma = {
key: 0,
class: "today-badge"
}, ya = { class: "event-count-label" }, Da = { class: "interval-selector" }, fa = { class: "interval-buttons" }, wa = ["onClick"], ba = { class: "time-slots-container" }, pa = { class: "time-slots" }, $a = ["onClick"], _a = { class: "slot-events" }, Sa = ["onClick"], Ca = { class: "event-main" }, Ta = { class: "event-title" }, Ma = { class: "event-duration" }, Ia = {
key: 0,
class: "event-desc"
}, Ha = {
key: 1,
class: "event-location"
}, Ea = { class: "stats-section" }, Oa = { class: "stat-item" }, Ba = { class: "stat-value" }, Fa = { class: "stat-item" }, Wa = { class: "stat-value" }, Va = { class: "stat-item" }, Na = { class: "stat-value highlight" }, Pa = /* @__PURE__ */ he({
__name: "HourlyView",
props: {
currentDate: {},
events: {},
theme: {},
locale: {},
categories: {},
workingHours: {},
timeInterval: {},
showCurrentTimeIndicator: { type: Boolean, default: !0 },
slotHeight: { default: 60 },
eventMinHeight: { default: 20 }
},
emits: ["event-click", "slot-click"],
setup(J, { emit: oe }) {
const e = J, p = oe, w = [15, 30, 60], _ = ue(e.timeInterval), U = b(() => L(e.currentDate)), P = b(() => {
const D = /* @__PURE__ */ new Date();
D.setHours(0, 0, 0, 0);
const f = new Date(e.currentDate);
return f.setHours(0, 0, 0, 0), {
weekday: new Intl.DateTimeFormat(e.locale, { weekday: "long" }).format(e.currentDate),
formattedDate: new Intl.DateTimeFormat(e.locale, {
month: "long",
day: "numeric",
year: "numeric"
}).format(e.currentDate),
isToday: f.getTime() === D.getTime()
};
}), x = b(() => l().length), ne = b(() => {
if (e.slotHeight) return e.slotHeight;
switch (_.value) {
case 15:
return 40;
case 30:
return 50;
default:
return 70;
}
}), Y = b(() => {
const D = [], f = _.value;
for (let C = 0; C < 24; C++)
for (let M = 0; M < 60; M += f) {
const R = `${String(C).padStart(2, "0")}:${String(M).padStart(2, "0")}`, Z = /* @__PURE__ */ new Date();
Z.setHours(C, M, 0, 0);
const le = new Intl.DateTimeFormat(e.locale, {
hour: "numeric",
minute: "2-digit",
hour12: !0
}).format(Z);
D.push({
time: R,
hour: C,
minute: M,
formattedTime: le,
isWorkingHour: S(C)
});
}
return D;
}), X = b(() => Y.value.filter((D) => D.isWorkingHour)), se = b(() => X.value.filter((D) => a(D).length === 0).length), j = b(() => X.value.filter((D) => a(D).length > 0).length), ee = b(() => {
const D = X.value.length;
return D === 0 ? 0 : Math.round(j.value / D * 100);
}), W = b(() => (/* @__PURE__ */ new Date()).getMinutes() % _.value / _.value * 100), L = (D) => {
const f = D.getFullYear(), C = String(D.getMonth() + 1).padStart(2, "0"), M = String(D.getDate()).padStart(2, "0");
return `${f}-${C}-${M}`;
}, S = (D) => D >= e.workingHours.start && D < e.workingHours.end, v = (D) => {
if (!P.value.isToday) return !1;
const f = /* @__PURE__ */ new Date(), C = f.getHours(), M = f.getMinutes(), R = D.minute + _.value;
return D.hour === C && M >= D.minute && M < R;
}, l = () => e.events.filter((D) => {
const f = new Date(D.start), C = new Date(D.end), M = new Date(U.value), R = new Date(M);
R.setHours(0, 0, 0, 0);
const Z = new Date(M);
return Z.setHours(23, 59, 59, 999), f <= Z && C >= R;
}), a = (D) => e.events.filter((f) => {
if (f.allDay) return !1;
const C = new Date(f.start), M = new Date(f.end);
if (L(C) !== U.value) return !1;
const Z = new Date(e.currentDate);
Z.setHours(D.hour, D.minute, 0, 0);
const le = new Date(Z);
return le.setMinutes(le.getMinutes() + _.value), C < le && M > Z;
}), o = (D) => {
let f = D.backgroundColor || "#3b82f6", C = D.color || "#ffffff";
if (D.categoryId) {
const M = e.categories.find((R) => R.id === D.categoryId);
M && (f = e.theme === "dark" && M.darkBackgroundColor ? M.darkBackgroundColor : M.backgroundColor, C = M.color);
}
return {
backgroundColor: f,
color: C,
borderLeft: `3px solid ${C}`
};
}, u = (D) => {
const f = new Date(D.start), M = new Date(D.end).getTime() - f.getTime(), R = Math.round(M / (1e3 * 60));
if (R < 60)
return `${R}m`;
const Z = Math.floor(R / 60), le = R % 60;
return le > 0 ? `${Z}h ${le}m` : `${Z}h`;
}, y = (D) => {
const f = `${U.value}T${D.time}:00`, C = new Date(e.currentDate);
C.setHours(D.hour, D.minute + _.value, 0, 0);
const M = `${String(C.getHours()).padStart(2, "0")}:${String(C.getMinutes()).padStart(2, "0")}`, R = `${U.value}T${M}:00`;
p("slot-click", {
start: f,
end: R,
date: U.value,
hour: D.hour,
minute: D.minute
});
}, O = (D) => {
p("event-click", D);
};
return (D, f) => (c(), d("div", {
class: V(["hourly-view", [`theme-${D.theme}`]]),
style: Q({ "--slot-height": `${ne.value}px`, "--event-min-height": `${D.eventMinHeight}px` })
}, [
t("div", da, [
t("div", ha, [
t("div", ga, m(P.value.weekday), 1),
t("div", va, m(P.value.formattedDate), 1)
]),
t("div", ka, [
P.value.isToday ? (c(), d("span", ma, "Today")) : N("", !0),
t("span", ya, m(x.value) + " event" + m(x.value !== 1 ? "s" : ""), 1)
])
]),
t("div", Da, [
f[0] || (f[0] = t("span", { class: "interval-label" }, "Interval:", -1)),
t("div", fa, [
(c(), d(B, null, F(w, (C) => t("button", {
key: C,
type: "button",
class: V(["interval-btn", { active: _.value === C }]),
onClick: (M) => _.value = C
}, m(C) + "min ", 11, wa)), 64))
])
]),
t("div", ba, [
t("div", pa, [
(c(!0), d(B, null, F(Y.value, (C) => (c(), d("div", {
key: C.time,
class: "time-slot-row"
}, [
t("div", {
class: V(["slot-time-label", {
"working-hour": C.isWorkingHour,
"hour-start": C.minute === 0
}])
}, m(C.formattedTime), 3),
t("div", {
class: V(["slot-content", {
"working-hour": C.isWorkingHour,
"current-slot": v(C)
}]),
onClick: (M) => y(C)
}, [
D.showCurrentTimeIndicator && v(C) ? (c(), d("div", {
key: 0,
class: "current-time-line",
style: Q({ top: `${W.value}%` })
}, f[1] || (f[1] = [
t("div", { class: "time-marker" }, null, -1)
]), 4)) : N("", !0),
t("div", _a, [
(c(!0), d(B, null, F(a(C), (M) => (c(), d("div", {
key: M.id,
class: "slot-event",
style: Q(o(M)),
onClick: me((R) => O(M), ["stop"])
}, [
t("div", Ca, [
t("span", Ta, m(M.title), 1),
t("span", Ma, m(u(M)), 1)
]),
M.description ? (c(), d("div", Ia, m(M.description), 1)) : N("", !0),
M.location ? (c(), d("div", Ha, [
f[2] || (f[2] = t("span", { class: "location-icon" }, "📍", -1)),
De(" " + m(M.location), 1)
])) : N("", !0)
], 12, Sa))), 128))
])
], 10, $a)
]))), 128))
])
]),
t("div", Ea, [
t("div", Oa, [
t("div", Ba, m(se.value), 1),
f[3] || (f[3] = t("div", { class: "stat-label" }, "Free Slots", -1))
]),
t("div", Fa, [
t("div", Wa, m(j.value), 1),
f[4] || (f[4] = t("div", { class: "stat-label" }, "Busy Slots", -1))
]),
t("div", Va, [
t("div", Na, m(ee.value) + "%", 1),
f[5] || (f[5] = t("div", { class: "stat-label" }, "Utilization", -1))
])
])
], 6));
}
}), La = /* @__PURE__ */ ge(Pa, [["__scopeId", "data-v-79e73dec"]]), Ya = {
key: 0,
class: "scheduler-legend legend-top"
}, xa = {
key: 1,
class: "scheduler-header"
}, za = {
key: 0,
class: "nav-controls"
}, Aa = ["aria-label"], Ra = ["aria-label"], Ua = {
key: 1,
class: "period-title"
}, Ga = {
key: 2,
class: "view-switcher"
}, ja = ["onClick"], qa = { class: "scheduler-content" }, Ka = {
key: 2,
class: "scheduler-legend legend-bottom"
}, Ja = /* @__PURE__ */ he({
__name: "ResourceSchedulerCalendar",
props: {
events: { default: () => [] },
categories: { default: () => [] },
selectedDate: { default: () => /* @__PURE__ */ new Date() },
view: { default: "monthly" },
theme: { default: "light" },
locale: { default: "en-US" },
timeInterval: { default: 60 },
workingHours: { default: () => ({ start: 9, end: 17, daysOfWeek: [1, 2, 3, 4, 5] }) },
showWeekNumbers: { type: Boolean, default: !1 },
showAllDaySlot: { type: Boolean, default: !0 },
allowEventCreation: { type: Boolean, default: !0 },
allowEventEditing: { type: Boolean, default: !0 },
allowEventDeletion: { type: Boolean, default: !0 },
minDate: {},
maxDate: {},
textLabels: { default: () => ({}) },
firstDayOfWeek: { default: 0 },
enabledViews: { default: () => ["yearly", "monthly", "weekly", "daily", "hourly"] },
defaultView: {},
slotHeight: { default: () => ({ hourly: 60, daily: 48, weekly: 48 }) },
responsiveBreakpoints: { default: () => ({ mobile: 480, tablet: 768, desktop: 1024 }) },
headerOptions: { default: () => ({ showNavigation: !0, showToday: !0, showViewSwitcher: !0, showTitle: !0 }) },
legendOptions: { default: () => ({ show: !0, position: "bottom" }) },
showCurrentTimeIndicator: { type: Boolean, default: !0 },
highlightToday: { type: Boolean, default: !0 },
highlightWeekends: { type: Boolean, default: !1 },
maxEventsPerSlot: { default: 3 },
eventMinHeight: { default: 20 },
compactMode: { type: Boolean, default: !1 }
},
emits: ["update:selectedDate", "update:view", "event-click", "event-create", "event-update", "event-delete", "slot-click", "date-click", "view-change", "date-range-change", "conflict-detected"],
setup(J, { emit: oe }) {
const e = J, p = oe, w = ue(new Date(e.selectedDate)), _ = ue(e.view), U = b(() => {
const n = e.headerOptions;
return (n == null ? void 0 : n.showNavigation) || (n == null ? void 0 : n.showToday) || (n == null ? void 0 : n.showViewSwitcher) || (n == null ? void 0 : n.showTitle);
}), P = b(() => {
var n;
return ((n = e.headerOptions) == null ? void 0 : n.showNavigation) !== !1;
}), x = b(() => {
var n;
return ((n = e.headerOptions) == null ? void 0 : n.showToday) !== !1;
}), ne = b(() => {
var n;
return ((n = e.headerOptions) == null ? void 0 : n.showViewSwitcher) !== !1;
}), Y = b(() => {
var n;
return ((n = e.headerOptions) == null ? void 0 : n.showTitle) !== !1;
}), X = b(() => {
var n;
return ((n = e.legendOptions) == null ? void 0 : n.show) === !1 ? !1 : e.categories && e.categories.length > 0;
}), se = b(() => {
var n;
return ((n = e.legendOptions) == null ? void 0 : n.position) || "bottom";
}), j = b(() => {
var n, E, s;
return {
hourly: ((n = e.slotHeight) == null ? void 0 : n.hourly) ?? 60,
daily: ((E = e.slotHeight) == null ? void 0 : E.daily) ?? 48,
weekly: ((s = e.slotHeight) == null ? void 0 : s.weekly) ?? 48
};
}), ee = b(() => {
var n, E, s;
return {
"--breakpoint-mobile": `${((n = e.responsiveBreakpoints) == null ? void 0 : n.mobile) ?? 480}px`,
"--breakpoint-tablet": `${((E = e.responsiveBreakpoints) == null ? void 0 : E.tablet) ?? 768}px`,
"--breakpoint-desktop": `${((s = e.responsiveBreakpoints) == null ? void 0 : s.desktop) ?? 1024}px`,
"--slot-height-hourly": `${j.value.hourly}px`,
"--slot-height-daily": `${j.value.daily}px`,
"--slot-height-weekly": `${j.value.weekly}px`,
"--event-min-height": `${e.eventMinHeight ?? 20}px`
};
}), W = b(() => {
var n, E, s, g, i, h, I, A, G, te, r, k, T, H, z, $, q;
return {
today: ((n = e.textLabels) == null ? void 0 : n.today) || "Today",
previousPeriod: ((E = e.textLabels) == null ? void 0 : E.previousPeriod) || "Previous",
nextPeriod: ((s = e.textLabels) == null ? void 0 : s.nextPeriod) || "Next",
yearView: ((g = e.textLabels) == null ? void 0 : g.yearView) || "Year",
monthView: ((i = e.textLabels) == null ? void 0 : i.monthView) || "Month",
weekView: ((h = e.textLabels) == null ? void 0 : h.weekView) || "Week",
dayView: ((I = e.textLabels) == null ? void 0 : I.dayView) || "Day",
hourView: ((A = e.textLabels) == null ? void 0 : A.hourView) || "Hour",
allDay: ((G = e.textLabels) == null ? void 0 : G.allDay) || "All Day",
noEvents: ((te = e.textLabels) == null ? void 0 : te.noEvents) || "No events",
createEvent: ((r = e.textLabels) == null ? void 0 : r.createEvent) || "Create Event",
editEvent: ((k = e.textLabels) == null ? void 0 : k.editEvent) || "Edit Event",
deleteEvent: ((T = e.textLabels) == null ? void 0 : T.deleteEvent) || "Delete Event",
eventDetails: ((H = e.textLabels) == null ? void 0 : H.eventDetails) || "Event Details",
conflictWarning: ((z = e.textLabels) == null ? void 0 : z.conflictWarning) || "Time conflict detected",
cancel: (($ = e.textLabels) == null ? void 0 : $.cancel) || "Cancel",
save: ((q = e.textLabels) == null ? void 0 : q.save) || "Save"
};
}), L = b(() => [
{ value: "yearly", label: W.value.yearView },
{ value: "monthly", label: W.value.monthView },
{ value: "weekly", label: W.value.weekView },
{ value: "daily", label: W.value.dayView },
{ value: "hourly", label: W.value.hourView }
]), S = b(() => {
const n = e.enabledViews || ["yearly", "monthly", "weekly", "daily", "hourly"];
return L.value.filter((E) => n.includes(E.value));
}), v = b(() => w.value.getFullYear()), l = b(() => {
const n = w.value;
switch (_.value) {
case "yearly":
return n.getFullYear().toString();
case "monthly":
return new Intl.DateTimeFormat(e.locale, {
year: "numeric",
month: "long"
}).format(n);
case "weekly": {
const E = a(n), s = new Date(E);
s.setDate(s.getDate() + 6);
const g = new Intl.DateTimeFormat(e.locale, {
month: "short",
day: "numeric"
}).format(E), i = new Intl.DateTimeFormat(e.locale, {
month: "short",
day: "numeric",
year: "numeric"
}).format(s);
return `${g} - ${i}`;
}
case "daily":
case "hourly":
return new Intl.DateTimeFormat(e.locale, {
weekday: "long",
year: "numeric",
month: "long",
day: "numeric"
}).format(n);
default:
return new Intl.DateTimeFormat(e.locale, {
year: "numeric",
month: "long",
day: "numeric"
}).format(n);
}
}), a = (n) => {
const E = new Date(n), s = E.getDay(), g = (s < e.firstDayOfWeek ? 7 : 0) + s - e.firstDayOfWeek;
return E.setDate(E.getDate() - g), E.setHours(0, 0, 0, 0), E;
}, o = (n) => {
const E = n.getFullYear(), s = String(n.getMonth() + 1).padStart(2, "0"), g = String(n.getDate()).padStart(2, "0");
return `${E}-${s}-${g}`;
}, u = () => {
const n = new Date(w.value);
switch (_.value) {
case "yearly":
n.setFullYear(n.getFullYear() - 1);
break;
case "monthly":
n.setMonth(n.getMonth() - 1);
break;
case "weekly":
n.setDate(n.getDate() - 7);
break;
case "daily":
case "hourly":
n.setDate(n.getDate() - 1);
break;
}
w.value = n, p("update:selectedDate", n), f();
}, y = () => {
const n = new Date(w.value);
switch (_.value) {
case "yearly":
n.setFullYear(n.getFullYear() + 1);
break;
case "monthly":
n.setMonth(n.getMonth() + 1);
break;
case "weekly":
n.setDate(n.getDate() + 7);
break;
case "daily":
case "hourly":
n.setDate(n.getDate() + 1);
break;
}
w.value = n, p("update:selectedDate", n), f();
}, O = () => {
w.value = /* @__PURE__ */ new Date(), p("update:selectedDate", w.value), f();
}, D = (n) => {
(e.enabledViews || ["yearly", "monthly", "weekly", "daily", "hourly"]).includes(n) && (_.value = n, p("update:view", n), p("view-change", n), f());
}, f = () => {
const n = C();
p("date-range-change", n);
}, C = () => {
const n = w.value;
let E, s;
switch (_.value) {
case "yearly":
E = new Date(n.getFullYear(), 0, 1), s = new Date(n.getFullYear(), 11, 31);
break;
case "monthly":
E = new Date(n.getFullYear(), n.getMonth(), 1), s = new Date(n.getFullYear(), n.getMonth() + 1, 0);
break;
case "weekly":
E = a(n), s = new Date(E), s.setDate(s.getDate() + 6);
break;
case "daily":
case "hourly":
default:
E = new Date(n), E.setHours(0, 0, 0, 0), s = new Date(n), s.setHours(23, 59, 59, 999);
break;
}
return {
start: o(E),
end: o(s)
};
}, M = (n) => {
const E = new Date(w.value);
E.setMonth(n), w.value = E, (e.enabledViews || ["yearly", "monthly", "weekly", "daily", "hourly"]).includes("monthly") && D("monthly");
}, R = (n) => {
w.value = new Date(n), p("date-click", n), p("update:selectedDate", w.value);
}, Z = (n) => {
p("event-click", n);
}, le = (n) => {
p("slot-click", n), e.allowEventCreation && p("event-create", {
start: n.start,
end: n.end,
allDay: !1
});
};
return ke(() => e.selectedDate, (n) => {
n && (w.value = new Date(n));
}), ke(() => e.view, (n) => {
n && (_.value = n);
}), ke(() => e.enabledViews, (n) => {
n && n.length > 0 && !n.includes(_.value) && (_.value = n[0], p("update:view", _.value), p("view-change", _.value));
}, { immediate: !0 }), (n, E) => (c(), d("div", {
class: V(["resource-scheduler-calendar", [
`theme-${n.theme}`,
{ "compact-mode": n.compactMode },
{ "highlight-weekends": n.highlightWeekends }
]]),
style: Q(ee.value)
}, [
X.value && se.value === "top" ? (c(), d("div", Ya, [
(c(!0), d(B, null, F(n.categories, (s) => (c(), d("div", {
key: s.id,
class: "legend-item"
}, [
t("div", {
class: "legend-color",
style: Q({
backgroundColor: n.theme === "dark" && s.darkBackgroundColor ? s.darkBackgroundColor : s.backgroundColor,
borderColor: s.color
})
}, null, 4),
t("span", null, m(s.name), 1)
]))), 128))
])) : N("", !0),
U.value ? (c(), d("div", xa, [
P.value ? (c(), d("div", za, [
t("button", {
type: "button",
onClick: u,
class: "nav-btn",
"aria-label": W.value.previousPeriod
}, E[0] || (E[0] = [
t("span", { class: "nav-arrow" }, "‹", -1)
]), 8, Aa),
x.value ? (c(), d("button", {
key: 0,
type: "button",
onClick: O,
class: "today-btn"
}, m(W.value.today), 1)) : N("", !0),
t("button", {
type: "button",
onClick: y,
class: "nav-btn",
"aria-label": W.value.nextPeriod
}, E[1] || (E[1] = [
t("span", { class: "nav-arrow" }, "›", -1)
]), 8, Ra)
])) : N("", !0),
Y.value ? (c(), d("h2", Ua, m(l.value), 1)) : N("", !0),
ne.value && S.value.length > 1 ? (c(), d("div", Ga, [
(c(!0), d(B, null, F(S.value, (s) => (c(), d("button", {
key: s.value,
type: "button",
onClick: (g) => D(s.value),
class: V(["view-btn", { active: _.value === s.value }])
}, m(s.label), 11, ja))), 128))
])) : N("", !0)
])) : N("", !0),
t("div", qa, [
_.value === "yearly" ? (c(), ye(fn, {
key: 0,
year: v.value,
events: n.events,
theme: n.theme,
locale: n.locale,
categories: n.categories,
"highlight-today": n.highlightToday,
"first-day-of-week": n.firstDayOfWeek,
onMonthClick: M,
onDateClick: R
}, null, 8, ["year", "events", "theme", "locale", "categories", "highlight-today", "first-day-of-week"])) : _.value === "monthly" ? (c(), ye(Hn, {
key: 1,
"current-date": w.value,
events: n.events,
theme: n.theme,
locale: n.locale,
categories: n.categories,
"first-day-of-week": n.firstDayOfWeek,
"highlight-today": n.highlightToday,
"highlight-weekends": n.highlightWeekends,
"max-events-per-slot": n.maxEventsPerSlot,
"show-week-numbers": n.showWeekNumbers,
onDateClick: R,
onEventClick: Z,
onSlotClick: le
}, null, 8, ["current-date", "events", "theme", "locale", "categories", "first-day-of-week", "highlight-today", "highlight-weekends", "max-events-per-slot", "show-week-numbers"])) : _.value === "weekly" ? (c(), ye(An, {
key: 2,
"current-date": w.value,
events: n.events,
theme: n.theme,
locale: n.locale,
categories: n.categories,
"working-hours": n.workingHours,
"time-interval": n.timeInterval,
"first-day-of-week": n.firstDayOfWeek,
"show-all-day-slot": n.showAllDaySlot,
"highlight-today": n.highlightToday,
"highlight-weekends": n.highlightWeekends,
"show-current-time-indicator": n.showCurrentTimeIndicator,
"slot-height": j.value.weekly,
"event-min-height": n.eventMinHeight,
onDateClick: R,
onEventClick: Z,
onSlotClick: le
}, null, 8, ["current-date", "events", "theme", "locale", "categories", "working-hours", "time-interval", "first-day-of-week", "show-all-day-slot", "highlight-today", "highlight-weekends", "show-current-time-indicator", "slot-height", "event-min-height"])) : _.value === "daily" ? (c(), ye(ua, {
key: 3,
"current-date": w.value,
events: n.events,
theme: n.theme,
locale: n.locale,
categories: n.categories,
"working-hours": n.workingHours,
"time-interval": n.timeInterval,
"show-all-day-slot": n.showAllDaySlot,
"highlight-today": n.highlightToday,
"show-current-time-indicator": n.showCurrentTimeIndicator,
"slot-height": j.value.daily,
"event-min-height": n.eventMinHeight,
onEventClick: Z,
onSlotClick: le
}, null, 8, ["current-date", "events", "theme", "locale", "categories", "working-hours", "time-interval", "show-all-day-slot", "highlight-today", "show-current-time-indicator", "slot-height", "event-min-height"])) : _.value === "hourly" ? (c(), ye(La, {
key: 4,
"current-date": w.value,
events: n.events,
theme: n.theme,
locale: n.locale,
categories: n.categories,
"working-hours": n.workingHours,
"time-interval": n.timeInterval,
"show-current-time-indicator": n.showCurrentTimeIndicator,
"slot-height": j.value.hourly,
"event-min-height": n.eventMinHeight,
onEventClick: Z,
onSlotClick: le
}, null, 8, ["current-date", "events", "theme", "locale", "categories", "working-hours", "time-interval", "show-current-time-indicator", "slot-height", "event-min-height"])) : N("", !0)
]),
X.value && se.value === "bottom" ? (c(), d("div", Ka, [
(c(!0), d(B, null, F(n.categories, (s) => (c(), d("div", {
key: s.id,
class: "legend-item"
}, [
t("div", {
class: "legend-color",
style: Q({
backgroundColor: n.theme === "dark" && s.darkBackgroundColor ? s.darkBackgroundColor : s.backgroundColor,
borderColor: s.color
})
}, null, 4),
t("span", null, m(s.name), 1)
]))), 128))
])) : N("", !0)
], 6));
}
}), Qa = /* @__PURE__ */ ge(Ja, [["__scopeId", "data-v-6cd84d42"]]), Za = {
install(J) {
J.component("HotelBookingCalendar", ot), J.component("HotelDashboardCalendar", Qt), J.component("ResourceSchedulerCalendar", Qa);
}
};
export {
ot as HotelBookingCalendar,
Qt as HotelDashboardCalendar,
Qa as ResourceSchedulerCalendar,
Za as default
};