Загрузка...

Интерактивный календарь и дашборд встреч на Tailwind. Панель с таймлайном, заметками, навигацией по месяцам и дням. Создание событий, отображение часовых поясов. Идеально для SaaS.
<section class="relative ring-1 ring-white/10 supports-[backdrop-filter]:bg-white/5 overflow-hidden bg-white/5 rounded-2xl backdrop-blur text-white">
<div class="grid grid-cols-1 lg:grid-cols-2">
<!-- Left: Timeline -->
<div class="sm:p-8 lg:p-10 pt-5 pr-5 pb-5 pl-5">
<div class="space-y-6">
<div class="flex items-center justify-between">
<h2 class="text-xl sm:text-2xl tracking-tight font-semibold text-white">Tue, August 19th, 2025</h2>
<div class="flex items-center gap-2 text-slate-400">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="calendar-days" class="lucide lucide-calendar-days h-4 w-4"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path><path d="M8 14h.01"></path><path d="M12 14h.01"></path><path d="M16 14h.01"></path><path d="M8 18h.01"></path><path d="M12 18h.01"></path><path d="M16 18h.01"></path></svg>
<span class="text-xs sm:text-sm font-medium">Timeline</span>
</div>
</div>
<div class="space-y-8">
<div class="space-y-3">
<div class="flex items-start gap-3">
<div class="mt-1 h-1.5 w-1.5 rounded-full bg-violet-400"></div>
<p class="text-sm sm:text-base text-slate-300">
Kicked off using <a href="#" class="text-violet-400 hover:text-violet-300 underline underline-offset-4 decoration-violet-500/50">Nova Notes</a>!
</p>
</div>
<div class="ml-6 pl-4 border-l border-white/10 space-y-2">
<div class="flex items-center gap-2 text-slate-400">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="sparkles" class="lucide lucide-sparkles h-4 w-4"><path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"></path><path d="M20 2v4"></path><path d="M22 4h-4"></path><circle cx="4" cy="20" r="2"></circle></svg>
<p class="text-sm">What can I organize here?</p>
</div>
<div class="flex items-center gap-2 text-slate-400">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="list-checks" class="lucide lucide-list-checks h-4 w-4"><path d="m3 17 2 2 4-4"></path><path d="m3 7 2 2 4-4"></path><path d="M13 6h8"></path><path d="M13 12h8"></path><path d="M13 18h8"></path></svg>
<p class="text-sm">A few workflows to try this week</p>
</div>
<div class="flex items-center gap-2 text-slate-500">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="ellipsis" class="lucide lucide-ellipsis h-4 w-4"><circle cx="12" cy="12" r="1"></circle><circle cx="19" cy="12" r="1"></circle><circle cx="5" cy="12" r="1"></circle></svg>
<p class="text-sm">Next steps…</p>
</div>
</div>
</div>
<!-- Connected accounts -->
<div class="relative">
<div class="flex flex-col sm:flex-row gap-3">
<div class="flex gap-3 ring-1 ring-white/10 bg-white/5 rounded-xl pt-3 pr-4 pb-3 pl-4 shadow-sm backdrop-blur items-center">
<div class="h-8 w-8 rounded-lg bg-gradient-to-br from-cyan-400 to-blue-500 flex items-center justify-center ring-1 ring-white/20">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="calendar" class="lucide lucide-calendar h-4 w-4 text-white"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg>
</div>
<div class="min-w-0">
<p class="text-sm font-medium text-slate-200">Zenith Calendar</p>
<p class="text-xs text-slate-400 truncate">sara@nimbus.team</p>
</div>
</div>
<div class="flex gap-3 ring-1 ring-white/10 bg-white/5 rounded-xl pt-3 pr-4 pb-3 pl-4 shadow-sm backdrop-blur items-center">
<div class="h-8 w-8 rounded-lg bg-gradient-to-br from-fuchsia-500 to-violet-600 flex items-center justify-center ring-1 ring-white/20">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="mail" class="lucide lucide-mail h-4 w-4 text-white"><path d="m22 7-8.991 5.727a2 2 0 0 1-2.009 0L2 7"></path><rect x="2" y="4" width="20" height="16" rx="2"></rect></svg>
</div>
<div class="min-w-0">
<p class="text-sm font-medium text-slate-200">Orbit Mail</p>
<p class="text-xs text-slate-400 truncate">sara@orbitmail.io</p>
</div>
<div class="ml-auto">
<button class="text-xs text-slate-300 hover:text-white px-2 py-0.5 rounded bg-white/5 ring-1 ring-white/10">Manage</button>
</div>
</div>
</div>
</div>
<!-- Notes list -->
<div class="space-y-4">
<h3 class="text-lg tracking-tight font-semibold text-white">Notes</h3>
<div class="space-y-2">
<div class="flex items-center justify-between px-4 py-3 rounded-lg bg-white/5 ring-1 ring-white/10">
<div class="flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="file-text" class="lucide lucide-file-text h-4 w-4 text-slate-400"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"></path><path d="M14 2v4a2 2 0 0 0 2 2h4"></path><path d="M10 9H8"></path><path d="M16 13H8"></path><path d="M16 17H8"></path></svg>
<p class="text-sm text-slate-300 truncate">Launch checklist — beta cohort</p>
</div>
<span class="text-xs text-slate-400">Updated 2h ago</span>
</div>
<div class="flex items-center justify-between px-4 py-3 rounded-lg bg-white/5 ring-1 ring-white/10">
<div class="flex items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="lightbulb" class="lucide lucide-lightbulb h-4 w-4 text-slate-400"><path d="M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5"></path><path d="M9 18h6"></path><path d="M10 22h4"></path></svg>
<p class="text-sm text-slate-300 truncate">Onboarding ideas for v1.3</p>
</div>
<span class="text-xs text-slate-400">Today 9:12</span>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Right: Calendar + Meetings -->
<div class="lg:border-t-0 lg:border-l border-white/10 border-t">
<div class="sm:p-8 lg:p-10 pt-5 pr-5 pb-5 pl-5 space-y-6">
<!-- Calendar header -->
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<button class="p-2 rounded-lg bg-white/5 ring-1 ring-white/10 hover:bg-white/10" type="button" aria-label="Previous month" data-nav="prev"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-left" class="lucide lucide-chevron-left h-4 w-4 text-slate-200"><path d="m15 18-6-6 6-6"></path></svg></button>
<button class="p-2 rounded-lg bg-white/5 ring-1 ring-white/10 hover:bg-white/10" type="button" aria-label="Next month" data-nav="next"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="chevron-right" class="lucide lucide-chevron-right h-4 w-4 text-slate-200"><path d="m9 18 6-6-6-6"></path></svg></button>
</div>
<h3 class="text-xl tracking-tight font-semibold text-white" id="month-label">August 2025</h3>
<button class="p-2 rounded-lg bg-white/5 ring-1 ring-white/10 hover:bg-white/10" type="button" id="add-event-btn" aria-label="Add event">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="calendar-plus" class="lucide lucide-calendar-plus h-4 w-4 text-slate-200"><path d="M16 19h6"></path><path d="M16 2v4"></path><path d="M19 16v6"></path><path d="M21 12.598V6a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h8.5"></path><path d="M3 10h18"></path><path d="M8 2v4"></path></svg>
</button>
</div>
<!-- Calendar grid -->
<div class="rounded-xl bg-white/5 ring-1 ring-white/10 p-4">
<div class="grid grid-cols-7 text-center text-xs text-slate-400 mb-2">
<div>Su</div><div>Mo</div><div>Tu</div><div>We</div><div class="">Th</div><div>Fr</div><div class="">Sa</div>
</div>
<div class="grid grid-cols-7 gap-1 text-sm" id="calendar-grid" aria-live="polite"><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-07-27" role="button" aria-label="Sun Jul 27 2025">27</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-07-28" role="button" aria-label="Mon Jul 28 2025">28</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-07-29" role="button" aria-label="Tue Jul 29 2025">29</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-07-30" role="button" aria-label="Wed Jul 30 2025">30</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-07-31" role="button" aria-label="Thu Jul 31 2025">31</div><div class="h-9 flex items-center justify-center" data-date="2025-08-01" role="button" aria-label="Fri Aug 01 2025">1</div><div class="h-9 flex items-center justify-center" data-date="2025-08-02" role="button" aria-label="Sat Aug 02 2025">2</div><div class="h-9 flex items-center justify-center" data-date="2025-08-03" role="button" aria-label="Sun Aug 03 2025">3</div><div class="h-9 flex items-center justify-center" data-date="2025-08-04" role="button" aria-label="Mon Aug 04 2025">4</div><div class="h-9 flex items-center justify-center" data-date="2025-08-05" role="button" aria-label="Tue Aug 05 2025">5</div><div class="h-9 flex items-center justify-center" data-date="2025-08-06" role="button" aria-label="Wed Aug 06 2025">6</div><div class="h-9 flex items-center justify-center" data-date="2025-08-07" role="button" aria-label="Thu Aug 07 2025">7</div><div class="h-9 flex items-center justify-center" data-date="2025-08-08" role="button" aria-label="Fri Aug 08 2025">8</div><div class="h-9 flex items-center justify-center" data-date="2025-08-09" role="button" aria-label="Sat Aug 09 2025">9</div><div class="h-9 flex items-center justify-center" data-date="2025-08-10" role="button" aria-label="Sun Aug 10 2025">10</div><div class="h-9 flex items-center justify-center" data-date="2025-08-11" role="button" aria-label="Mon Aug 11 2025">11</div><div class="h-9 flex items-center justify-center" data-date="2025-08-12" role="button" aria-label="Tue Aug 12 2025">12</div><div class="h-9 flex items-center justify-center" data-date="2025-08-13" role="button" aria-label="Wed Aug 13 2025">13</div><div class="h-9 flex items-center justify-center" data-date="2025-08-14" role="button" aria-label="Thu Aug 14 2025">14</div><div class="h-9 flex items-center justify-center" data-date="2025-08-15" role="button" aria-label="Fri Aug 15 2025">15</div><div class="h-9 flex items-center justify-center" data-date="2025-08-16" role="button" aria-label="Sat Aug 16 2025">16</div><div class="h-9 flex items-center justify-center" data-date="2025-08-17" role="button" aria-label="Sun Aug 17 2025">17</div><div class="h-9 flex items-center justify-center" data-date="2025-08-18" role="button" aria-label="Mon Aug 18 2025">18</div><div class="h-9 flex items-center justify-center" data-date="2025-08-19" role="button" aria-label="Tue Aug 19 2025"><div class="h-7 w-7 rounded-lg bg-violet-500 text-white flex items-center justify-center shadow-inner shadow-violet-900/40">19</div></div><div class="h-9 flex items-center justify-center" data-date="2025-08-20" role="button" aria-label="Wed Aug 20 2025">20</div><div class="h-9 flex items-center justify-center" data-date="2025-08-21" role="button" aria-label="Thu Aug 21 2025">21</div><div class="h-9 flex items-center justify-center" data-date="2025-08-22" role="button" aria-label="Fri Aug 22 2025">22</div><div class="h-9 flex items-center justify-center" data-date="2025-08-23" role="button" aria-label="Sat Aug 23 2025">23</div><div class="h-9 flex items-center justify-center" data-date="2025-08-24" role="button" aria-label="Sun Aug 24 2025">24</div><div class="h-9 flex items-center justify-center" data-date="2025-08-25" role="button" aria-label="Mon Aug 25 2025">25</div><div class="h-9 flex items-center justify-center" data-date="2025-08-26" role="button" aria-label="Tue Aug 26 2025">26</div><div class="h-9 flex items-center justify-center" data-date="2025-08-27" role="button" aria-label="Wed Aug 27 2025">27</div><div class="h-9 flex items-center justify-center" data-date="2025-08-28" role="button" aria-label="Thu Aug 28 2025">28</div><div class="h-9 flex items-center justify-center" data-date="2025-08-29" role="button" aria-label="Fri Aug 29 2025">29</div><div class="h-9 flex items-center justify-center" data-date="2025-08-30" role="button" aria-label="Sat Aug 30 2025">30</div><div class="h-9 flex items-center justify-center" data-date="2025-08-31" role="button" aria-label="Sun Aug 31 2025">31</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-09-01" role="button" aria-label="Mon Sep 01 2025">1</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-09-02" role="button" aria-label="Tue Sep 02 2025">2</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-09-03" role="button" aria-label="Wed Sep 03 2025">3</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-09-04" role="button" aria-label="Thu Sep 04 2025">4</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-09-05" role="button" aria-label="Fri Sep 05 2025">5</div><div class="h-9 flex items-center justify-center text-slate-600" data-date="2025-09-06" role="button" aria-label="Sat Sep 06 2025">6</div></div>
</div>
<!-- Meetings -->
<div class="rounded-xl bg-white/5 ring-1 ring-white/10 p-4">
<div class="flex items-center justify-between mb-3">
<h4 class="text-lg tracking-tight font-semibold text-white">Meetings</h4>
<div class="flex items-center gap-2 text-xs text-slate-400">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="clock" class="lucide lucide-clock h-4 w-4"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg>
<span id="timezone-label" class="">Asia/Singapore</span>
</div>
</div>
<div class="space-y-3" id="meetings-list"><div class="flex items-center gap-3 px-3 py-3 rounded-lg bg-white/5 ring-1 ring-white/10">
<div class="h-8 w-8 rounded-md bg-gradient-to-br from-violet-500 to-fuchsia-600 flex items-center justify-center ring-1 ring-white/20">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="users" class="lucide lucide-users h-4 w-4 text-white"><path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"></path><path d="M16 3.128a4 4 0 0 1 0 7.744"></path><path d="M22 21v-2a4 4 0 0 0-3-3.87"></path><circle cx="9" cy="7" r="4"></circle></svg>
</div>
<div class="min-w-0">
<p class="text-sm font-medium text-slate-200 truncate">Sync with Lina about launch</p>
<p class="text-xs text-slate-400 truncate">Zoom · Hosted by Nova</p>
</div>
<div class="ml-auto flex items-center gap-2">
<span class="text-xs text-slate-300">9:30am</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="clock" class="lucide lucide-clock h-4 w-4 text-slate-400"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg>
</div>
</div><div class="flex items-center gap-3 px-3 py-3 rounded-lg bg-white/5 ring-1 ring-white/10">
<div class="h-8 w-8 rounded-md bg-gradient-to-br from-cyan-400 to-blue-600 flex items-center justify-center ring-1 ring-white/20">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="palette" class="lucide lucide-palette h-4 w-4 text-white"><path d="M12 22a1 1 0 0 1 0-20 10 9 0 0 1 10 9 5 5 0 0 1-5 5h-2.25a1.75 1.75 0 0 0-1.4 2.8l.3.4a1.75 1.75 0 0 1-1.4 2.8z"></path><circle cx="13.5" cy="6.5" r=".5" fill="currentColor"></circle><circle cx="17.5" cy="10.5" r=".5" fill="currentColor"></circle><circle cx="6.5" cy="12.5" r=".5" fill="currentColor"></circle><circle cx="8.5" cy="7.5" r=".5" fill="currentColor"></circle></svg>
</div>
<div class="min-w-0">
<p class="text-sm font-medium text-slate-200 truncate">Design jam with Core UI</p>
<p class="text-xs text-slate-400 truncate">Figma · Collaborative</p>
</div>
<div class="ml-auto flex items-center gap-2">
<span class="text-xs text-slate-300">11:00am</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="clock" class="lucide lucide-clock h-4 w-4 text-slate-400"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg>
</div>
</div><div class="flex items-center gap-3 px-3 py-3 rounded-lg bg-white/5 ring-1 ring-white/10 opacity-70">
<div class="h-8 w-8 rounded-md bg-gradient-to-br from-emerald-400 to-teal-600 flex items-center justify-center ring-1 ring-white/20">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="wrench" class="lucide lucide-wrench h-4 w-4 text-white"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z"></path></svg>
</div>
<div class="min-w-0">
<p class="text-sm font-medium text-slate-200 truncate">Weekly product triage</p>
<p class="text-xs text-slate-400 truncate">Zenith Calendar</p>
</div>
<div class="ml-auto flex items-center gap-2">
<span class="text-xs text-slate-300">2:00pm</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="clock" class="lucide lucide-clock h-4 w-4 text-slate-400"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg>
</div>
</div></div>
<div class="mt-4 flex items-center justify-between">
<button class="text-xs text-slate-300 hover:text-white px-3 py-2 rounded-lg bg-white/5 ring-1 ring-white/10 inline-flex items-center gap-2" type="button" id="share-day-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="link" class="lucide lucide-link h-4 w-4"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg>
Share day
</button>
<button class="text-xs text-violet-300 hover:text-violet-200 inline-flex items-center gap-2" type="button" id="preferences-btn">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="settings-2" class="lucide lucide-settings-2 h-4 w-4"><path d="M14 17H5"></path><path d="M19 7h-9"></path><circle cx="17" cy="17" r="3"></circle><circle cx="7" cy="7" r="3"></circle></svg>
Preferences
</button>
</div>
</div>
</div>
<script>
(() => {
const root = document.getElementById('aura-emei98atd');
const monthLabel = root.querySelector('#month-label');
const grid = root.querySelector('#calendar-grid');
const meetingsList = root.querySelector('#meetings-list');
const tzLabel = root.querySelector('#timezone-label');
const addBtn = root.querySelector('#add-event-btn');
const shareBtn = root.querySelector('#share-day-btn');
// Resolve and show local timezone
try {
const tz = Intl.DateTimeFormat().resolvedOptions().timeZone || 'Local';
tzLabel.textContent = tz.replace(/_/g, ' ');
} catch {
tzLabel.textContent = 'Local time';
}
// Sample events seeded to match initial design
const events = {
'2025-08-19': [
{ time: '09:30', title: 'Sync with Lina about launch', meta: 'Zoom · Hosted by Nova', icon: 'users', grad: 'from-violet-500 to-fuchsia-600' },
{ time: '11:00', title: 'Design jam with Core UI', meta: 'Figma · Collaborative', icon: 'palette', grad: 'from-cyan-400 to-blue-600' },
{ time: '14:00', title: 'Weekly product triage', meta: 'Zenith Calendar', icon: 'wrench', grad: 'from-emerald-400 to-teal-600', dim: true }
]
};
// State
let selected = new Date(2025, 7, 19); // August is 7 (0-indexed)
let viewYear = selected.getFullYear();
let viewMonth = selected.getMonth();
// Utilities
const pad = (n) => String(n).padStart(2, '0');
const keyFromDate = (d) => `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`;
const fmtMonthYear = (y, m) => new Intl.DateTimeFormat(undefined, { month: 'long', year: 'numeric' }).format(new Date(y, m, 1));
const to12h = (hhmm) => {
const [h, m] = hhmm.split(':').map(Number);
const am = h < 12 || h === 24;
const h12 = ((h % 12) || 12);
return `${h12}:${pad(m)}${am ? 'am' : 'pm'}`;
};
function renderCalendar() {
monthLabel.textContent = fmtMonthYear(viewYear, viewMonth);
grid.innerHTML = '';
const first = new Date(viewYear, viewMonth, 1);
const startDow = first.getDay(); // 0=Sun
const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate();
const prevMonthDays = new Date(viewYear, viewMonth, 0).getDate();
const totalCells = 42; // 6 rows
for (let i = 0; i < totalCells; i++) {
const cell = document.createElement('div');
cell.className = 'h-9 flex items-center justify-center';
let dayNum, cellDate, inCurrent = true;
if (i < startDow) {
dayNum = prevMonthDays - (startDow - 1 - i);
const d = new Date(viewYear, viewMonth - 1, dayNum);
cellDate = d;
inCurrent = false;
} else if (i >= startDow + daysInMonth) {
dayNum = i - (startDow + daysInMonth) + 1;
const d = new Date(viewYear, viewMonth + 1, dayNum);
cellDate = d;
inCurrent = false;
} else {
dayNum = i - startDow + 1;
const d = new Date(viewYear, viewMonth, dayNum);
cellDate = d;
inCurrent = true;
}
const iso = keyFromDate(cellDate);
cell.dataset.date = iso;
cell.setAttribute('role', 'button');
cell.setAttribute('aria-label', cellDate.toDateString());
// Styling for out-of-month days
if (!inCurrent) cell.className += ' text-slate-600';
// Highlight selected day
const isSelected = keyFromDate(selected) === iso;
if (isSelected) {
cell.innerHTML = `<div class="h-7 w-7 rounded-lg bg-violet-500 text-white flex items-center justify-center shadow-inner shadow-violet-900/40">${dayNum}</div>`;
} else {
cell.textContent = dayNum;
}
grid.appendChild(cell);
}
}
function renderMeetings() {
const k = keyFromDate(selected);
const list = (events[k] || []).slice().sort((a, b) => a.time.localeCompare(b.time));
meetingsList.innerHTML = '';
if (!list.length) {
const empty = document.createElement('div');
empty.className = 'px-3 py-3 rounded-lg bg-white/5 ring-1 ring-white/10 flex items-center gap-3';
empty.innerHTML = `
<div class="h-8 w-8 rounded-md bg-gradient-to-br from-violet-500 to-fuchsia-600 flex items-center justify-center ring-1 ring-white/20">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="calendar" class="lucide lucide-calendar h-4 w-4 text-white"><path d="M8 2v4"></path><path d="M16 2v4"></path><rect width="18" height="18" x="3" y="4" rx="2"></rect><path d="M3 10h18"></path></svg>
</div>
<div class="min-w-0">
<p class="text-sm font-medium text-slate-200 truncate">No meetings scheduled</p>
<p class="text-xs text-slate-400 truncate">Add one with the + button</p>
</div>
`;
meetingsList.appendChild(empty);
if (window.lucide && window.lucide.createIcons) window.lucide.createIcons({ attrs: { width: 24, height: 24 } });
return;
}
list.forEach(ev => {
const row = document.createElement('div');
row.className = `flex items-center gap-3 px-3 py-3 rounded-lg bg-white/5 ring-1 ring-white/10${ev.dim ? ' opacity-70' : ''}`;
row.innerHTML = `
<div class="h-8 w-8 rounded-md bg-gradient-to-br ${ev.grad} flex items-center justify-center ring-1 ring-white/20">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="${ev.icon}" class="lucide h-4 w-4 text-white"></svg>
</div>
<div class="min-w-0">
<p class="text-sm font-medium text-slate-200 truncate">${ev.title}</p>
<p class="text-xs text-slate-400 truncate">${ev.meta || ''}</p>
</div>
<div class="ml-auto flex items-center gap-2">
<span class="text-xs text-slate-300">${to12h(ev.time)}</span>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="clock" class="lucide h-4 w-4 text-slate-400"><path d="M12 6v6l4 2"></path><circle cx="12" cy="12" r="10"></circle></svg>
</div>
`;
meetingsList.appendChild(row);
});
if (window.lucide && window.lucide.createIcons) window.lucide.createIcons({ attrs: { width: 24, height: 24 } });
}
// Navigation
root.querySelector('[data-nav="prev"]').addEventListener('click', () => {
if (viewMonth === 0) { viewMonth = 11; viewYear -= 1; } else { viewMonth -= 1; }
// Keep selected day within new month when applicable
selected = new Date(viewYear, viewMonth, Math.min(selected.getDate(), new Date(viewYear, viewMonth + 1, 0).getDate()));
renderCalendar(); renderMeetings();
});
root.querySelector('[data-nav="next"]').addEventListener('click', () => {
if (viewMonth === 11) { viewMonth = 0; viewYear += 1; } else { viewMonth += 1; }
selected = new Date(viewYear, viewMonth, Math.min(selected.getDate(), new Date(viewYear, viewMonth + 1, 0).getDate()));
renderCalendar(); renderMeetings();
});
// Day selection (including out-of-month cells)
grid.addEventListener('click', (e) => {
const cell = e.target.closest('[data-date]');
if (!cell) return;
const [y, m, d] = cell.dataset.date.split('-').map(Number);
selected = new Date(y, m - 1, d);
viewYear = selected.getFullYear();
viewMonth = selected.getMonth();
renderCalendar(); renderMeetings();
});
// Add event flow
addBtn.addEventListener('click', async () => {
const title = prompt('Event title');
if (!title) return;
let time = prompt('Start time (24h, e.g., 14:30)');
if (!time) time = '09:00';
const meta = prompt('Location / notes (optional)') || '';
const k = keyFromDate(selected);
if (!events[k]) events[k] = [];
events[k].push({ time, title, meta, icon: 'calendar-plus', grad: 'from-violet-500 to-fuchsia-600' });
renderMeetings();
});
// Share selected day
shareBtn.addEventListener('click', async () => {
const k = keyFromDate(selected);
const text = `My schedule for ${selected.toDateString()}: ${location.href.split('#')[0]}#${k}`;
try {
await navigator.clipboard.writeText(text);
const original = shareBtn.innerHTML;
shareBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" data-lucide="check" class="lucide h-4 w-4"><path d="M20 6 9 17l-5-5"></path></svg> Copied`;
if (window.lucide && window.lucide.createIcons) window.lucide.createIcons();
setTimeout(() => { shareBtn.innerHTML = original; if (window.lucide && window.lucide.createIcons) window.lucide.createIcons(); }, 1200);
} catch {
alert('Copy failed. You can share this date: ' + text);
}
});
// Deep-link by hash (#YYYY-MM-DD)
const hash = (location.hash || '').slice(1);
if (/^\d{4}-\d{2}-\d{2}$/.test(hash)) {
const [hy, hm, hd] = hash.split('-').map(Number);
selected = new Date(hy, hm - 1, hd);
viewYear = selected.getFullYear();
viewMonth = selected.getMonth();
}
// Initial render
renderCalendar();
renderMeetings();
if (window.lucide && window.lucide.createIcons) window.lucide.createIcons({ attrs: { width: 24, height: 24 } });
})();
</script>
</div>
</div>
</section>