From 2d749620f70e6522a69ba102fb040f69692fc8ad Mon Sep 17 00:00:00 2001 From: Arya Palanivel Date: Mon, 23 Feb 2026 23:44:25 -0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E2=9C=A8=20edited=20drizzle=20conf?= =?UTF-8?q?ig=20so=20drizzle=20studio=20can=20be=20run=20with=20new=20vers?= =?UTF-8?q?ion=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile 2 | 1 - Dockerfile 3 | 1 - docker-compose 2.yml | 23 ----------------------- docker-compose 3.yml | 23 ----------------------- drizzle.config.ts | 2 +- 5 files changed, 1 insertion(+), 49 deletions(-) delete mode 100644 Dockerfile 2 delete mode 100644 Dockerfile 3 delete mode 100644 docker-compose 2.yml delete mode 100644 docker-compose 3.yml diff --git a/Dockerfile 2 b/Dockerfile 2 deleted file mode 100644 index 5e60bfdc7..000000000 --- a/Dockerfile 2 +++ /dev/null @@ -1 +0,0 @@ -FROM postgres:16.8-alpine3.21 diff --git a/Dockerfile 3 b/Dockerfile 3 deleted file mode 100644 index 5e60bfdc7..000000000 --- a/Dockerfile 3 +++ /dev/null @@ -1 +0,0 @@ -FROM postgres:16.8-alpine3.21 diff --git a/docker-compose 2.yml b/docker-compose 2.yml deleted file mode 100644 index 7adeff53d..000000000 --- a/docker-compose 2.yml +++ /dev/null @@ -1,23 +0,0 @@ -services: - db: - build: - context: . - dockerfile: Dockerfile - container_name: zotmeet-db - restart: unless-stopped - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: zotmeet - ports: - - "5432:5432" - volumes: - - zotmeet-db-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d zotmeet"] - interval: 5s - timeout: 5s - retries: 5 - -volumes: - zotmeet-db-data: diff --git a/docker-compose 3.yml b/docker-compose 3.yml deleted file mode 100644 index 7adeff53d..000000000 --- a/docker-compose 3.yml +++ /dev/null @@ -1,23 +0,0 @@ -services: - db: - build: - context: . - dockerfile: Dockerfile - container_name: zotmeet-db - restart: unless-stopped - environment: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: zotmeet - ports: - - "5432:5432" - volumes: - - zotmeet-db-data:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U postgres -d zotmeet"] - interval: 5s - timeout: 5s - retries: 5 - -volumes: - zotmeet-db-data: diff --git a/drizzle.config.ts b/drizzle.config.ts index 2d8599d05..dce3de099 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -3,7 +3,7 @@ import "dotenv/config"; import { defineConfig } from "drizzle-kit"; export default defineConfig({ - schema: "./src/db/schema.ts", + schema: ["./src/db/schema.ts", "./src/db/relations.ts"], out: "./src/db/migrations", dialect: "postgresql", dbCredentials: { From 6f29855d40d35babe24899552b12d31e78d81a48 Mon Sep 17 00:00:00 2001 From: Arya Palanivel Date: Mon, 23 Feb 2026 23:55:13 -0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=E2=9C=A8=20added=20action=20to=20g?= =?UTF-8?q?et=20free=20times?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../actions/availability/free-times/action.ts | 75 +++++++++++++++++++ src/server/data/availability/queries.ts | 35 ++++++++- 2 files changed, 108 insertions(+), 2 deletions(-) create mode 100644 src/server/actions/availability/free-times/action.ts diff --git a/src/server/actions/availability/free-times/action.ts b/src/server/actions/availability/free-times/action.ts new file mode 100644 index 000000000..3035c8e09 --- /dev/null +++ b/src/server/actions/availability/free-times/action.ts @@ -0,0 +1,75 @@ +"use server"; + +import { getUserAvailabilitiesAndScheduled } from "@data/availability/queries"; +import { fromZonedTime } from "date-fns-tz"; +import { getCurrentSession } from "@/lib/auth"; +import { BLOCK_LENGTH } from "@/lib/availability/utils"; + +function expandScheduledBlockToISOSlots( + scheduledDate: Date, + fromTime: string, + toTime: string, + timezone: string, +): Set { + const slots = new Set(); + const datePart = scheduledDate.toISOString().substring(0, 10); + + const [fh, fm] = fromTime.split(":").map(Number); + const [th, tm] = toTime.split(":").map(Number); + + let currentMinutes = fh * 60 + fm; + const endMinutes = th * 60 + tm; + while (currentMinutes < endMinutes) { + const h = Math.floor(currentMinutes / 60) + .toString() + .padStart(2, "0"); + const m = (currentMinutes % 60).toString().padStart(2, "0"); + const utcDate = fromZonedTime(`${datePart}T${h}:${m}:00`, timezone); + slots.add(utcDate.toISOString()); + currentMinutes += BLOCK_LENGTH; + } + + return slots; +} + +export async function getUserFreeTimes(): Promise< + { freeTimes: string[] } | { error: string } +> { + const { user } = await getCurrentSession(); + + if (!user) { + return { error: "You must be logged in to view free times." }; + } + + try { + const { userAvailabilities, scheduledBlocks } = + await getUserAvailabilitiesAndScheduled(user.memberId); + + const scheduledSlots = new Set(); + for (const block of scheduledBlocks) { + const blockSlots = expandScheduledBlockToISOSlots( + block.scheduledDate, + block.scheduledFromTime, + block.scheduledToTime, + block.timezone, + ); + for (const slot of blockSlots) { + scheduledSlots.add(slot); + } + } + + const freeTimes = new Set(); + for (const avail of userAvailabilities) { + for (const isoString of avail.meetingAvailabilities) { + if (!scheduledSlots.has(isoString)) { + freeTimes.add(isoString); + } + } + } + + return { freeTimes: [...freeTimes].sort() }; + } catch (error) { + console.error("Failed to fetch free times:", error); + return { error: "Failed to fetch free times." }; + } +} diff --git a/src/server/data/availability/queries.ts b/src/server/data/availability/queries.ts index 1e48feae1..3191d989b 100644 --- a/src/server/data/availability/queries.ts +++ b/src/server/data/availability/queries.ts @@ -1,8 +1,8 @@ import "server-only"; -import { and, eq } from "drizzle-orm"; +import { and, eq, inArray } from "drizzle-orm"; import { db } from "@/db"; -import { availabilities } from "@/db/schema"; +import { availabilities, meetings, scheduledMeetings } from "@/db/schema"; export async function getMemberMeetingAvailability({ memberId, @@ -24,3 +24,34 @@ export async function getMemberMeetingAvailability({ return availabilityData; } + +export async function getUserAvailabilitiesAndScheduled(memberId: string) { + const userAvailabilities = await db + .select({ + meetingId: availabilities.meetingId, + meetingAvailabilities: availabilities.meetingAvailabilities, + }) + .from(availabilities) + .innerJoin(meetings, eq(availabilities.meetingId, meetings.id)) + .where( + and(eq(availabilities.memberId, memberId), eq(meetings.archived, false)), + ); + + if (userAvailabilities.length === 0) { + return { userAvailabilities: [], scheduledBlocks: [] }; + } + const meetingIds = userAvailabilities.map((a) => a.meetingId); + + const scheduledBlocks = await db + .select({ + scheduledDate: scheduledMeetings.scheduledDate, + scheduledFromTime: scheduledMeetings.scheduledFromTime, + scheduledToTime: scheduledMeetings.scheduledToTime, + timezone: meetings.timezone, + }) + .from(scheduledMeetings) + .innerJoin(meetings, eq(scheduledMeetings.meetingId, meetings.id)) + .where(inArray(scheduledMeetings.meetingId, meetingIds)); + + return { userAvailabilities, scheduledBlocks }; +} From 8151eb7ebdc8411905ef57f540b731142a31a265 Mon Sep 17 00:00:00 2001 From: Arya Palanivel Date: Mon, 2 Mar 2026 14:51:06 -0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E2=9C=A8=20added=20overnight=20fun?= =?UTF-8?q?ctionality?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../actions/availability/free-times/action.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/server/actions/availability/free-times/action.ts b/src/server/actions/availability/free-times/action.ts index 3035c8e09..b472deda7 100644 --- a/src/server/actions/availability/free-times/action.ts +++ b/src/server/actions/availability/free-times/action.ts @@ -19,12 +19,21 @@ function expandScheduledBlockToISOSlots( let currentMinutes = fh * 60 + fm; const endMinutes = th * 60 + tm; - while (currentMinutes < endMinutes) { - const h = Math.floor(currentMinutes / 60) + const effectiveEndMinutes = + endMinutes <= currentMinutes ? endMinutes + 1440 : endMinutes; + while (currentMinutes < effectiveEndMinutes) { + const wrappedMinutes = currentMinutes % 1440; + const h = Math.floor(wrappedMinutes / 60) .toString() .padStart(2, "0"); - const m = (currentMinutes % 60).toString().padStart(2, "0"); - const utcDate = fromZonedTime(`${datePart}T${h}:${m}:00`, timezone); + const m = (wrappedMinutes % 60).toString().padStart(2, "0"); + let slotDatePart = datePart; + if (currentMinutes >= 1440) { + const nextDay = new Date(scheduledDate); + nextDay.setUTCDate(nextDay.getUTCDate() + 1); + slotDatePart = nextDay.toISOString().substring(0, 10); + } + const utcDate = fromZonedTime(`${slotDatePart}T${h}:${m}:00`, timezone); slots.add(utcDate.toISOString()); currentMinutes += BLOCK_LENGTH; } From a0f6fda197687b278d629750ab6bf678442ce803 Mon Sep 17 00:00:00 2001 From: Arya Palanivel Date: Mon, 2 Mar 2026 15:29:49 -0800 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E2=9C=A8changed=20overnight=20calc?= =?UTF-8?q?ulation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/server/actions/availability/free-times/action.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/actions/availability/free-times/action.ts b/src/server/actions/availability/free-times/action.ts index b472deda7..0311496da 100644 --- a/src/server/actions/availability/free-times/action.ts +++ b/src/server/actions/availability/free-times/action.ts @@ -20,7 +20,7 @@ function expandScheduledBlockToISOSlots( let currentMinutes = fh * 60 + fm; const endMinutes = th * 60 + tm; const effectiveEndMinutes = - endMinutes <= currentMinutes ? endMinutes + 1440 : endMinutes; + endMinutes < currentMinutes ? endMinutes + 1440 : endMinutes; while (currentMinutes < effectiveEndMinutes) { const wrappedMinutes = currentMinutes % 1440; const h = Math.floor(wrappedMinutes / 60)