Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Dockerfile 2

This file was deleted.

1 change: 0 additions & 1 deletion Dockerfile 3

This file was deleted.

23 changes: 0 additions & 23 deletions docker-compose 2.yml

This file was deleted.

23 changes: 0 additions & 23 deletions docker-compose 3.yml

This file was deleted.

2 changes: 1 addition & 1 deletion drizzle.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down
75 changes: 75 additions & 0 deletions src/server/actions/availability/free-times/action.ts
Original file line number Diff line number Diff line change
@@ -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<string> {
const slots = new Set<string>();
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) {
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
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<string>();
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<string>();
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." };
}
}
35 changes: 33 additions & 2 deletions src/server/data/availability/queries.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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 };
}
Loading