Mega push vol 4

This commit is contained in:
AlacrisDevs
2026-02-06 16:08:40 +02:00
parent b517bb975c
commit d8bbfd9dc3
95 changed files with 8019 additions and 3946 deletions

View File

@@ -22,31 +22,20 @@
let currentView = $state<ViewType>(initialView);
const today = new Date();
const weekDays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
const weekDayHeaders = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
const days = $derived(
getMonthDays(currentDate.getFullYear(), currentDate.getMonth()),
);
function prevMonth() {
currentDate = new Date(
currentDate.getFullYear(),
currentDate.getMonth() - 1,
1,
);
}
function nextMonth() {
currentDate = new Date(
currentDate.getFullYear(),
currentDate.getMonth() + 1,
1,
);
}
function goToToday() {
currentDate = new Date();
}
// Group days into weeks (rows of 7)
const weeks = $derived.by(() => {
const result: Date[][] = [];
for (let i = 0; i < days.length; i += 7) {
result.push(days.slice(i, i + 7));
}
return result;
});
function getEventsForDay(date: Date): CalendarEvent[] {
return events.filter((event) => {
@@ -66,10 +55,12 @@
}),
);
// Get week days for week view
// Get week days for week view (Mon-Sun)
function getWeekDays(date: Date): Date[] {
const startOfWeek = new Date(date);
startOfWeek.setDate(date.getDate() - date.getDay());
const dayOfWeek = startOfWeek.getDay();
const mondayOffset = dayOfWeek === 0 ? -6 : 1 - dayOfWeek;
startOfWeek.setDate(date.getDate() + mondayOffset);
return Array.from({ length: 7 }, (_, i) => {
const d = new Date(startOfWeek);
d.setDate(startOfWeek.getDate() + i);
@@ -79,7 +70,6 @@
const weekDates = $derived(getWeekDays(currentDate));
// Navigation functions for different views
function prev() {
if (currentView === "month") {
currentDate = new Date(
@@ -112,7 +102,11 @@
}
}
const headerTitle = $derived(() => {
function goToToday() {
currentDate = new Date();
}
const headerTitle = $derived.by(() => {
if (currentView === "day") {
return currentDate.toLocaleDateString("en-US", {
weekday: "long",
@@ -129,207 +123,200 @@
});
</script>
<div class="bg-surface rounded-xl p-4">
<div class="flex items-center justify-between mb-4">
<h2 class="text-xl font-semibold text-light">{headerTitle()}</h2>
<div class="flex flex-col h-full gap-2">
<!-- Navigation bar -->
<div class="flex items-center justify-between px-2">
<div class="flex items-center gap-2">
<!-- View Switcher -->
<div class="flex bg-dark rounded-lg p-0.5">
<button
class="px-3 py-1 text-sm rounded-md transition-colors {currentView ===
'day'
? 'bg-primary text-white'
: 'text-light/60 hover:text-light'}"
onclick={() => (currentView = "day")}
>
Day
</button>
<button
class="px-3 py-1 text-sm rounded-md transition-colors {currentView ===
'week'
? 'bg-primary text-white'
: 'text-light/60 hover:text-light'}"
onclick={() => (currentView = "week")}
>
Week
</button>
<button
class="px-3 py-1 text-sm rounded-md transition-colors {currentView ===
'month'
? 'bg-primary text-white'
: 'text-light/60 hover:text-light'}"
onclick={() => (currentView = "month")}
>
Month
</button>
</div>
<button
class="px-3 py-1.5 text-sm text-light/60 hover:text-light hover:bg-light/10 rounded-lg transition-colors"
class="p-1 text-light/60 hover:text-light hover:bg-dark rounded-lg transition-colors"
onclick={prev}
aria-label="Previous"
>
<span
class="material-symbols-rounded"
style="font-size: 24px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;"
>chevron_left</span
>
</button>
<span
class="font-heading text-h4 text-white min-w-[200px] text-center"
>{headerTitle}</span
>
<button
class="p-1 text-light/60 hover:text-light hover:bg-dark rounded-lg transition-colors"
onclick={next}
aria-label="Next"
>
<span
class="material-symbols-rounded"
style="font-size: 24px; font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;"
>chevron_right</span
>
</button>
<button
class="px-3 py-1 text-body-md font-body text-light/60 hover:text-white hover:bg-dark rounded-[32px] transition-colors ml-2"
onclick={goToToday}
>
Today
</button>
</div>
<div class="flex bg-dark rounded-[32px] p-0.5">
<button
class="p-2 text-light/60 hover:text-light hover:bg-light/10 rounded-lg transition-colors"
onclick={prev}
aria-label="Previous"
class="px-3 py-1 text-body-md font-body rounded-[32px] transition-colors {currentView ===
'day'
? 'bg-primary text-night'
: 'text-light/60 hover:text-light'}"
onclick={() => (currentView = "day")}>Day</button
>
<svg
class="w-5 h-5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="m15 18-6-6 6-6" />
</svg>
</button>
<button
class="p-2 text-light/60 hover:text-light hover:bg-light/10 rounded-lg transition-colors"
onclick={next}
aria-label="Next"
class="px-3 py-1 text-body-md font-body rounded-[32px] transition-colors {currentView ===
'week'
? 'bg-primary text-night'
: 'text-light/60 hover:text-light'}"
onclick={() => (currentView = "week")}>Week</button
>
<button
class="px-3 py-1 text-body-md font-body rounded-[32px] transition-colors {currentView ===
'month'
? 'bg-primary text-night'
: 'text-light/60 hover:text-light'}"
onclick={() => (currentView = "month")}>Month</button
>
<svg
class="w-5 h-5"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
>
<path d="m9 18 6-6-6-6" />
</svg>
</button>
</div>
</div>
<!-- Month View -->
{#if currentView === "month"}
<div
class="grid grid-cols-7 gap-px bg-light/10 rounded-lg overflow-hidden"
class="flex flex-col flex-1 gap-2 min-h-0 bg-background rounded-xl p-2"
>
{#each weekDays as day}
<div
class="bg-dark px-2 py-2 text-center text-sm font-medium text-light/50"
>
{day}
</div>
{/each}
{#each days as day}
{@const dayEvents = getEventsForDay(day)}
{@const isToday = isSameDay(day, today)}
{@const inMonth = isCurrentMonth(day)}
<button
class="bg-dark min-h-[80px] p-1 text-left transition-colors hover:bg-light/5"
class:opacity-40={!inMonth}
onclick={() => onDateClick?.(day)}
>
<div class="flex items-center justify-center w-7 h-7 mb-1">
<!-- Day Headers -->
<div class="grid grid-cols-7 gap-2">
{#each weekDayHeaders as day}
<div class="flex items-center justify-center py-2 px-2">
<span
class="text-sm {isToday
? 'bg-primary text-white rounded-full w-7 h-7 flex items-center justify-center'
: 'text-light/80'}"
class="font-heading text-h4 text-white text-center"
>{day}</span
>
{day.getDate()}
</span>
</div>
<div class="space-y-0.5">
{#each dayEvents.slice(0, 3) as event}
<button
class="w-full text-xs px-1 py-0.5 rounded truncate text-left"
style="background-color: {event.color ??
'#6366f1'}20; color: {event.color ??
'#6366f1'}"
onclick={(e) => {
e.stopPropagation();
onEventClick?.(event);
}}
{/each}
</div>
<!-- Calendar Grid -->
<div class="flex-1 flex flex-col gap-2 min-h-0">
{#each weeks as week}
<div class="grid grid-cols-7 gap-2 flex-1">
{#each week as day}
{@const dayEvents = getEventsForDay(day)}
{@const isToday = isSameDay(day, today)}
{@const inMonth = isCurrentMonth(day)}
<div
class="bg-night rounded-none flex flex-col items-start px-4 py-5 overflow-hidden transition-colors hover:bg-dark/50 min-h-0 cursor-pointer
{!inMonth ? 'opacity-50' : ''}"
onclick={() => onDateClick?.(day)}
>
{event.title}
</button>
<span
class="font-body text-body text-white {isToday
? 'text-primary font-bold'
: ''}"
>
{day.getDate()}
</span>
{#each dayEvents.slice(0, 2) as event}
<button
class="w-full mt-1 px-2 py-0.5 rounded-[4px] text-body-sm font-bold font-body text-night truncate text-left"
style="background-color: {event.color ??
'#00A3E0'}"
onclick={(e) => {
e.stopPropagation();
onEventClick?.(event);
}}
>
{event.title}
</button>
{/each}
{#if dayEvents.length > 2}
<span
class="text-body-sm text-light/40 mt-0.5"
>+{dayEvents.length - 2} more</span
>
{/if}
</div>
{/each}
{#if dayEvents.length > 3}
<p class="text-xs text-light/40 px-1">
+{dayEvents.length - 3} more
</p>
{/if}
</div>
</button>
{/each}
{/each}
</div>
</div>
{/if}
<!-- Week View -->
{#if currentView === "week"}
<div
class="grid grid-cols-7 gap-px bg-light/10 rounded-lg overflow-hidden"
class="flex flex-col flex-1 gap-2 min-h-0 bg-background rounded-xl p-2"
>
{#each weekDates as day}
{@const dayEvents = getEventsForDay(day)}
{@const isToday = isSameDay(day, today)}
<div class="bg-dark">
<div class="px-2 py-2 text-center border-b border-light/10">
<div class="text-xs text-light/50">
{weekDays[day.getDay()]}
</div>
<div
class="text-lg font-medium {isToday
? 'text-primary'
: 'text-light'}"
>
{day.getDate()}
</div>
</div>
<div class="min-h-[300px] p-1 space-y-1">
{#each dayEvents as event}
<button
class="w-full text-xs px-2 py-1.5 rounded text-left"
style="background-color: {event.color ??
'#6366f1'}20; color: {event.color ??
'#6366f1'}"
onclick={() => onEventClick?.(event)}
<div class="grid grid-cols-7 gap-2 flex-1">
{#each weekDates as day}
{@const dayEvents = getEventsForDay(day)}
{@const isToday = isSameDay(day, today)}
<div class="flex flex-col overflow-hidden">
<div class="px-4 py-3 text-center">
<div
class="font-heading text-h4 {isToday
? 'text-primary'
: 'text-white'}"
>
<div class="font-medium truncate">
{weekDayHeaders[(day.getDay() + 6) % 7]}
</div>
<div
class="font-body text-body-md {isToday
? 'text-primary'
: 'text-light/60'}"
>
{day.getDate()}
</div>
</div>
<div class="flex-1 px-2 pb-2 space-y-1 overflow-y-auto">
{#each dayEvents as event}
<button
class="w-full px-2 py-1.5 rounded-[4px] text-body-sm font-bold font-body text-night truncate text-left"
style="background-color: {event.color ??
'#00A3E0'}"
onclick={() => onEventClick?.(event)}
>
{event.title}
</div>
<div class="text-[10px] opacity-70">
{new Date(
event.start_time,
).toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
})}
</div>
</button>
{/each}
</button>
{/each}
</div>
</div>
</div>
{/each}
{/each}
</div>
</div>
{/if}
<!-- Day View -->
{#if currentView === "day"}
{@const dayEvents = getEventsForDay(currentDate)}
<div class="bg-dark rounded-lg p-4 min-h-[400px]">
<div class="flex-1 bg-night px-4 py-5 min-h-0 overflow-auto">
{#if dayEvents.length === 0}
<div class="text-center text-light/40 py-12">
<p>No events for this day</p>
<p class="font-body text-body">No events for this day</p>
</div>
{:else}
<div class="space-y-2">
{#each dayEvents as event}
<button
class="w-full text-left p-3 rounded-lg transition-colors hover:opacity-80"
class="w-full text-left p-3 rounded-[8px] transition-colors hover:opacity-80"
style="background-color: {event.color ??
'#6366f1'}20; border-left: 3px solid {event.color ??
'#6366f1'}"
'#00A3E0'}20; border-left: 3px solid {event.color ??
'#00A3E0'}"
onclick={() => onEventClick?.(event)}
>
<div class="font-medium text-light">
<div class="font-heading text-h5 text-white">
{event.title}
</div>
<div class="text-sm text-light/60 mt-1">
<div
class="font-body text-body-md text-light/60 mt-1"
>
{new Date(event.start_time).toLocaleTimeString(
"en-US",
{ hour: "numeric", minute: "2-digit" },
@@ -340,7 +327,9 @@
)}
</div>
{#if event.description}
<div class="text-sm text-light/50 mt-2">
<div
class="font-body text-body-md text-light/50 mt-2"
>
{event.description}
</div>
{/if}