// utils.jsx — shared icons + helpers for Sao Mai Hotel UI
// ─── Icons (Material-style line icons) ──────────────────────
const Icon = ({ name, size = 20, stroke = 1.8, ...rest }) => {
const s = { width: size, height: size, display: 'inline-block', flexShrink: 0, ...rest.style };
const common = {
width: size,
height: size,
viewBox: '0 0 24 24',
fill: 'none',
stroke: 'currentColor',
strokeWidth: stroke,
strokeLinecap: 'round',
strokeLinejoin: 'round',
...rest,
style: s,
};
const paths = {
menu: <>>,
calendar: <>>,
timeline: <>>,
list: <>>,
invoice: <>>,
bed: <>>,
chart: <>>,
chevron_left: ,
chevron_right: ,
chevron_down: ,
plus: <>>,
minus: ,
x: <>>,
search: <>>,
filter: ,
settings: <>>,
user: <>>,
users: <>>,
phone: ,
mail: <>>,
location: <>>,
clock: <>>,
moon: ,
sun: <>>,
print: <>>,
download: <>>,
edit: <>>,
trash: <>>,
check: ,
check_circle: <>>,
arrow_right: <>>,
sparkle: <>>,
bell: <>>,
qr: <>>,
refresh: <>>,
sidebar: <>>,
cash: <>>,
card: <>>,
bank: <>>,
later: <>>,
info: <>>,
star: ,
};
return ;
};
// ─── Date helpers ────────────────────────────────────────────
const DOW_VN = ['CN', 'T2', 'T3', 'T4', 'T5', 'T6', 'T7'];
const DOW_VN_LONG = ['Chủ nhật', 'Thứ Hai', 'Thứ Ba', 'Thứ Tư', 'Thứ Năm', 'Thứ Sáu', 'Thứ Bảy'];
const MONTH_VN = ['Tháng 1', 'Tháng 2', 'Tháng 3', 'Tháng 4', 'Tháng 5', 'Tháng 6', 'Tháng 7', 'Tháng 8', 'Tháng 9', 'Tháng 10', 'Tháng 11', 'Tháng 12'];
const sameDay = (a, b) => a && b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
const startOfDay = (d) => { const x = new Date(d); x.setHours(0,0,0,0); return x; };
const daysBetween = (a, b) => Math.round((startOfDay(b) - startOfDay(a)) / 86400000);
const formatDateVN = (d) => `${DOW_VN_LONG[d.getDay()]}, ${d.getDate()} ${MONTH_VN[d.getMonth()]} ${d.getFullYear()}`;
const formatDateShort = (d) => `${String(d.getDate()).padStart(2,'0')}/${String(d.getMonth()+1).padStart(2,'0')}/${d.getFullYear()}`;
const formatRange = (a, b) => {
if (a.getMonth() === b.getMonth() && a.getFullYear() === b.getFullYear()) {
return `${MONTH_VN[a.getMonth()]} ${a.getFullYear()}`;
}
return `${MONTH_VN[a.getMonth()]} – ${MONTH_VN[b.getMonth()]} ${b.getFullYear()}`;
};
// ─── Currency ────────────────────────────────────────────────
const VND = (n) => {
if (n == null) return '—';
return new Intl.NumberFormat('de-DE').format(Math.round(n)) + 'đ';
};
const VNDshort = (n) => {
if (n == null) return '—';
if (n >= 1_000_000) return (n / 1_000_000).toFixed(n >= 10_000_000 ? 0 : 1).replace(/\.0$/, '') + 'tr';
if (n >= 1_000) return Math.round(n / 1_000) + 'k';
return String(n);
};
// ─── Misc ────────────────────────────────────────────────────
const initials = (name) => {
if (!name) return '?';
const parts = name.trim().split(/\s+/);
if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();
return (parts[parts.length - 2][0] + parts[parts.length - 1][0]).toUpperCase();
};
const findRoom = (rooms, id) => rooms.find(r => r.id === id);
const findGuest = (guests, id) => guests.find(g => g.id === id);
const findService = (catalog, id) => catalog.find(s => s.id === id);
const findMenuItem = (menu, id) => menu.find(m => m.id === id);
const computeBookingTotal = (booking, rooms, catalog, menu) => {
const room = findRoom(rooms, booking.roomId);
const nights = Math.max(1, daysBetween(booking.checkIn, booking.checkOut));
const roomTotal = room ? room.price * nights : 0;
const servicesTotal = (booking.services || []).reduce((sum, s) => {
const sv = findService(catalog, s.id);
return sum + (sv ? sv.price * s.qty : 0);
}, 0);
const ordersTotal = menu ? (booking.orders || [])
.filter(o => o.status !== 'cancelled')
.reduce((sum, o) => {
const item = findMenuItem(menu, o.itemId);
return sum + (item ? item.price * o.qty : 0);
}, 0) : 0;
const subtotal = roomTotal + servicesTotal + ordersTotal;
const tax = Math.round(subtotal * 0.1);
const total = subtotal + tax;
return { nights, roomTotal, servicesTotal, ordersTotal, subtotal, tax, total };
};
// Export everything to window
Object.assign(window, {
Icon,
DOW_VN, DOW_VN_LONG, MONTH_VN,
sameDay, startOfDay, daysBetween,
formatDateVN, formatDateShort, formatRange,
VND, VNDshort,
initials,
findRoom, findGuest, findService, findMenuItem,
computeBookingTotal,
});