+
+
+ {m["calendar.shownAppointments.title"]()}
+
- {/each}
-
+
+ {#each appointmentStates as state (state.value)}
+
+
+
+
+ {/each}
+
+
+ {#if channels.length > 1}
+
+
{m["channels.title"]()}
+ {#each channels as channel (channel.id)}
+ {@const locale = getLocale()}
+ {@const name = channel.names[locale] || Object.values(channel.names)[0]}
+
+ {
+ if (v) {
+ shownChannels = [...shownChannels, channel.id];
+ } else {
+ shownChannels = shownChannels.filter((id) => id !== channel.id);
+ }
+ }}
+ class="mt-2 mb-1"
+ />
+
+ {/each}
+
+ {/if}
+ {#if agents.length > 1}
+
+
{m["agents.title"]()}
+ {#each agents as agent (agent.id)}
+
+ {
+ if (v) {
+ shownAgents = [...shownAgents, agent.id];
+ } else {
+ shownAgents = shownAgents.filter((id) => id !== agent.id);
+ }
+ }}
+ class="mt-2 mb-1"
+ />
+
+ {/each}
+
+ {/if}
+
- {#if channels.length > 1}
-
-
{m["channels.title"]()}
- {#each channels as channel (channel.id)}
- {@const locale = getLocale()}
- {@const name = channel.names[locale] || Object.values(channel.names)[0]}
-
- {
- if (v) {
- shownChannels = [...shownChannels, channel.id];
- } else {
- shownChannels = shownChannels.filter((id) => id !== channel.id);
- }
- }}
- class="mt-2 mb-1"
- />
-
- {/each}
-
- {/if}
- {#if agents.length > 1}
-
-
{m["agents.title"]()}
- {#each agents as agent (agent.id)}
-
- {
- if (v) {
- shownAgents = [...shownAgents, agent.id];
- } else {
- shownAgents = shownAgents.filter((id) => id !== agent.id);
- }
- }}
- class="mt-2 mb-1"
- />
-
- {/each}
-
- {/if}
+
+
+ sidebarStore.setCalendarExpanded(!sidebar.isCalendarExpanded)}
+ />
diff --git a/src/routes/(pages)/dashboard/calendar/(components)/CalendarHeader.svelte b/src/routes/(pages)/dashboard/calendar/(components)/CalendarHeader.svelte
index 356f336..eb717d1 100644
--- a/src/routes/(pages)/dashboard/calendar/(components)/CalendarHeader.svelte
+++ b/src/routes/(pages)/dashboard/calendar/(components)/CalendarHeader.svelte
@@ -1,32 +1,53 @@
@@ -37,15 +58,28 @@
-
- {Intl.DateTimeFormat(getLocale(), {
- year: "numeric",
- month: "long",
- day: "numeric",
- weekday: "short",
- timeZone: getLocalTimeZone().toString(),
- }).format(startDate.toDate(getLocalTimeZone()))}
-
+
+
+ {Intl.DateTimeFormat(getLocale(), {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ weekday: "short",
+ timeZone: getLocalTimeZone().toString(),
+ }).format(selectedDate.toDate(getLocalTimeZone()))}
+
+
+
+
+
@@ -54,7 +88,7 @@
size="sm"
variant="outline"
onclick={setToToday}
- disabled={startDate.toString() === today(getLocalTimeZone()).toString()}
+ disabled={selectedDate.toString() === today(getLocalTimeZone()).toString()}
class="-order-1 ml-auto min-[500px]:order-0"
>
{m["calendar.today"]()}
diff --git a/src/routes/(pages)/dashboard/calendar/(components)/CalendarMonth.svelte b/src/routes/(pages)/dashboard/calendar/(components)/CalendarMonth.svelte
new file mode 100644
index 0000000..32b40bf
--- /dev/null
+++ b/src/routes/(pages)/dashboard/calendar/(components)/CalendarMonth.svelte
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {#snippet day({ day, outsideMonth })}
+
+ {/snippet}
+
+
diff --git a/src/routes/(pages)/dashboard/calendar/(components)/queries.ts b/src/routes/(pages)/dashboard/calendar/(components)/queries.ts
new file mode 100644
index 0000000..73710da
--- /dev/null
+++ b/src/routes/(pages)/dashboard/calendar/(components)/queries.ts
@@ -0,0 +1,9 @@
+import type { CalendarDate } from "@internationalized/date";
+import { fetchCalendar } from "./utils";
+
+export const calendarMonthQuery = (tenant: string | undefined | null, date: CalendarDate) => ({
+ queryKey: ["calendar", `${date.year}-${date.month}`],
+ queryFn: () => fetchCalendar({ tenant: tenant as string, selectedDate: date }),
+ enabled: Boolean(tenant),
+ staleTime: 5000,
+});
diff --git a/src/routes/(pages)/dashboard/calendar/(components)/utils.ts b/src/routes/(pages)/dashboard/calendar/(components)/utils.ts
index 0e7c44d..00b24c6 100644
--- a/src/routes/(pages)/dashboard/calendar/(components)/utils.ts
+++ b/src/routes/(pages)/dashboard/calendar/(components)/utils.ts
@@ -10,14 +10,26 @@ import { localToUTCWithoutDST } from "$lib/utils/datetime";
import { getLocalTimeZone, type CalendarDate } from "@internationalized/date";
import { get } from "svelte/store";
-export const fetchCalendar = async (opts: { tenant: string; startDate: CalendarDate }) => {
+export const fetchCalendar = async (opts: { tenant: string; selectedDate: CalendarDate }) => {
if (!browser) return;
const localStartDate = new Date(
- Date.UTC(opts.startDate.year, opts.startDate.month - 1, opts.startDate.day, 0, 0, 0, 0),
+ opts.selectedDate.year,
+ opts.selectedDate.month - 1,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
);
const localEndDate = new Date(
- Date.UTC(opts.startDate.year, opts.startDate.month - 1, opts.startDate.day, 23, 59, 59, 999),
+ opts.selectedDate.year,
+ opts.selectedDate.month,
+ 0,
+ 23,
+ 59,
+ 59,
+ 999,
);
const params = new URLSearchParams({
diff --git a/src/routes/(pages)/dashboard/calendar/+page.svelte b/src/routes/(pages)/dashboard/calendar/+page.svelte
index 1e540ee..82c00cc 100644
--- a/src/routes/(pages)/dashboard/calendar/+page.svelte
+++ b/src/routes/(pages)/dashboard/calendar/+page.svelte
@@ -12,7 +12,7 @@
import { calendarStore } from "$lib/stores/calendar";
import { channels as channelsStore } from "$lib/stores/channels";
import { sidebar } from "$lib/stores/sidebar";
- import type { TAppointmentFilter, TCalendar, TCalendarItem } from "$lib/types/calendar";
+ import type { TAppointmentFilter, TCalendarItem } from "$lib/types/calendar";
import { timeUTCToLocalWithoutOffset, utcToLocalWithoutDST } from "$lib/utils/datetime";
import { getCurrentTranlslation } from "$lib/utils/localizations";
import {
@@ -22,31 +22,35 @@
today,
type CalendarDate,
} from "@internationalized/date";
- import { Funnel } from "@lucide/svelte";
+ import { SlidersHorizontal } from "@lucide/svelte";
import { onMount } from "svelte";
import AddAppointment from "./(components)/add-appointment/AddAppointment.svelte";
import Appointment from "./(components)/Appointment.svelte";
import CalendarDay from "./(components)/CalendarDay.svelte";
import CalendarFilters from "./(components)/CalendarFilters.svelte";
import CalendarHeader from "./(components)/CalendarHeader.svelte";
- import { fetchCalendar, openAppointmentById } from "./(components)/utils";
+ import { openAppointmentById } from "./(components)/utils";
+ import { createQuery, useQueryClient } from "@tanstack/svelte-query";
+ import { calendarMonthQuery } from "./(components)/queries";
const convertDate = (dateStr: string) => {
const zonedDateTime = parseAbsoluteToLocal(dateStr);
return toCalendarDate(zonedDateTime);
};
+ const queryClient = useQueryClient();
const tenantId = $derived($auth.user?.tenantId);
const curEmptySlot = $derived($calendarStore.curEmptySlot);
const curItem = $derived($calendarStore.curItem);
const channels = $derived($channelsStore.channels);
const agents = $derived($agentsStore.agents);
- let startDate: CalendarDate = $state(
+ let selectedDate: CalendarDate = $state(
"date" in page.state
? convertDate(history?.state["sveltekit:states"].date)
: today(getLocalTimeZone()),
);
- let calender: TCalendar | undefined = $state();
+ const query = createQuery(() => calendarMonthQuery(tenantId as string, selectedDate));
+ const calendar = $derived(query.data);
let shownAppointments: TAppointmentFilter = $state("all");
let shownChannels: string[] = $state([]);
let shownAgents: string[] = $state([]);
@@ -70,7 +74,10 @@
let scale = $state(1);
$effect(() => {
- updateCalendar();
+ const getMonth = (x: string | undefined) => (x ? new Date(x).getMonth() + 1 : undefined);
+ if (!calendar || getMonth(calendar?.period.startDate) !== getMonth(selectedDate.toString())) {
+ updateCalendar();
+ }
});
$effect(() => {
@@ -100,8 +107,8 @@
"appointmentId" in page.state
) {
const date = convertDate(page.state.date as string);
- if (date.toString() !== startDate.toString()) {
- startDate = date;
+ if (date.toString() !== selectedDate.toString()) {
+ selectedDate = date;
replaceState("", { appointmentId: page.state.appointmentId });
} else {
if (items) {
@@ -115,8 +122,9 @@
const updateCalendar = async () => {
if (tenantId) {
- calender = undefined;
- calender = await fetchCalendar({ startDate, tenant: tenantId });
+ queryClient.invalidateQueries({
+ queryKey: ["calendar"],
+ });
}
};
@@ -125,8 +133,8 @@
};
let items: TCalendarItem[] | undefined = $derived.by(() => {
- if (!calender) return undefined;
- const dayEntry = calender.calendar.find((d) => d.date === startDate.toString());
+ if (!calendar) return undefined;
+ const dayEntry = calendar.calendar.find((d) => d.date === selectedDate.toString());
if (!dayEntry) return [];
return Object.keys(dayEntry.channels).reduce((allItems, channelId) => {
const channelData = dayEntry.channels[channelId];
@@ -203,10 +211,10 @@
-
+
sidebar.setCalendarExpanded(!$sidebar.isCalendarExpanded)}
class="lg:hidden"
>
-
+
{/snippet}
{#snippet sidebarRight()}
-
+
{/snippet}