diff --git a/apps/web/src/app/[locale]/(space)/events/page.tsx b/apps/web/src/app/[locale]/(space)/events/page.tsx index 3fcd60571..994d0780f 100644 --- a/apps/web/src/app/[locale]/(space)/events/page.tsx +++ b/apps/web/src/app/[locale]/(space)/events/page.tsx @@ -159,7 +159,6 @@ export default async function Page(props: { title={event.title} start={event.start} end={event.end} - status={event.status} allDay={event.allDay} invites={event.invites} /> diff --git a/apps/web/src/app/[locale]/(space)/page.tsx b/apps/web/src/app/[locale]/(space)/page.tsx index 2a4a0a054..a0f816415 100644 --- a/apps/web/src/app/[locale]/(space)/page.tsx +++ b/apps/web/src/app/[locale]/(space)/page.tsx @@ -22,6 +22,7 @@ import { Trans } from "@/components/trans"; import { IfCloudHosted } from "@/contexts/environment"; import { getTranslation } from "@/i18n/server"; import { prisma } from "@rallly/database"; +import dayjs from "dayjs"; import { FeedbackAlert } from "./feedback-alert"; async function loadData() { @@ -34,7 +35,10 @@ async function loadData() { }; } + const todayStart = dayjs().startOf("day").toDate(); + const todayEnd = dayjs().endOf("day").toDate(); const now = new Date(); + const [livePollCount, upcomingEventCount] = await Promise.all([ prisma.poll.count({ where: { @@ -43,12 +47,16 @@ async function loadData() { deleted: false, }, }), - prisma.event.count({ + prisma.scheduledEvent.count({ where: { userId: user.id, - start: { - gte: now, - }, + OR: [ + { allDay: false, start: { gte: now } }, + { + allDay: true, + start: { gte: todayStart, lte: todayEnd }, + }, + ], }, }), ]); diff --git a/apps/web/src/components/poll/scheduled-event.tsx b/apps/web/src/components/poll/scheduled-event.tsx index 568c0e847..b04f5f84c 100644 --- a/apps/web/src/components/poll/scheduled-event.tsx +++ b/apps/web/src/components/poll/scheduled-event.tsx @@ -4,7 +4,6 @@ import { CalendarIcon } from "lucide-react"; import { AddToCalendarButton } from "@/components/add-to-calendar-button"; import { ParticipantAvatarBar } from "@/components/participant-avatar-bar"; -import { useVisibleParticipants } from "@/components/participants-provider"; import { Trans } from "@/components/trans"; import { IfParticipantsVisible } from "@/components/visibility"; import { usePoll } from "@/contexts/poll"; @@ -49,15 +48,8 @@ function FinalTime({ start, duration }: { start: Date; duration: number }) { } function useAttendees() { - const participants = useVisibleParticipants(); const poll = usePoll(); - return participants.filter((participant) => - participant.votes.some( - (vote) => - vote.optionId === poll?.event?.optionId && - (vote.type === "yes" || vote.type === "ifNeedBe"), - ), - ); + return poll.event?.attendees ?? []; } function Attendees() { @@ -117,8 +109,8 @@ export function ScheduledEvent() { : undefined } guests={attendees - .filter((participant) => !!participant.email) - .map((participant) => participant.email as string)} + .filter((attendee) => !!attendee.email) + .map((attendee) => attendee.email as string)} /> diff --git a/apps/web/src/features/poll/api/get-polls.ts b/apps/web/src/features/poll/api/get-polls.ts index f80d1e369..a032dbcda 100644 --- a/apps/web/src/features/poll/api/get-polls.ts +++ b/apps/web/src/features/poll/api/get-polls.ts @@ -48,12 +48,6 @@ export async function getPolls({ image: true, }, }, - event: { - select: { - start: true, - duration: true, - }, - }, participants: { where: { deleted: false, @@ -95,7 +89,6 @@ export async function getPolls({ email: participant.email ?? undefined, image: participant.user?.image ?? undefined, })), - event: poll.event ?? undefined, }; }), hasNextPage, diff --git a/apps/web/src/features/poll/api/get-recently-updated-polls.ts b/apps/web/src/features/poll/api/get-recently-updated-polls.ts deleted file mode 100644 index 50aa2c227..000000000 --- a/apps/web/src/features/poll/api/get-recently-updated-polls.ts +++ /dev/null @@ -1,99 +0,0 @@ -import type { Prisma } from "@rallly/database"; -import { prisma } from "@rallly/database"; -import { unstable_cache } from "next/cache"; - -type PollFilters = { - userId: string; - limit?: number; -}; - -export const getRecentlyUpdatedPolls = async ({ - userId, - limit = 3, -}: PollFilters) => { - // Build the where clause based on filters - const where: Prisma.PollWhereInput = { - userId, - deleted: false, - }; - - const data = await prisma.poll.findMany({ - where, - select: { - id: true, - title: true, - status: true, - createdAt: true, - updatedAt: true, - event: { - select: { - start: true, - duration: true, - }, - }, - participants: { - where: { - deleted: false, - }, - select: { - id: true, - name: true, - user: { - select: { - image: true, - }, - }, - }, - orderBy: { - createdAt: "desc", - }, - }, - options: { - select: { - id: true, - startTime: true, - duration: true, - }, - }, - }, - orderBy: { - updatedAt: "desc", - }, - take: limit, - }); - - return data.map((poll) => { - const { options, ...rest } = poll; - const durations = new Set(); - for (const option of options) { - durations.add(option.duration); - } - return { - ...rest, - participants: poll.participants.map((participant) => ({ - id: participant.id, - name: participant.name, - image: participant.user?.image ?? undefined, - })), - dateOptions: { - first: options[0]?.startTime, - last: options[options.length - 1]?.startTime, - count: options.length, - duration: - durations.size === 1 - ? (durations.values().next().value as number) - : Array.from(durations), - }, - event: poll.event ?? undefined, - }; - }); -}; - -export const getCachedRecentlyUpdatedPolls = unstable_cache( - getRecentlyUpdatedPolls, - undefined, - { - revalidate: 60 * 5, - tags: ["polls"], - }, -); diff --git a/apps/web/src/features/scheduled-event/api/get-scheduled-events.ts b/apps/web/src/features/scheduled-event/api/get-scheduled-events.ts index c21c458ca..5bb5ff7b5 100644 --- a/apps/web/src/features/scheduled-event/api/get-scheduled-events.ts +++ b/apps/web/src/features/scheduled-event/api/get-scheduled-events.ts @@ -31,11 +31,24 @@ export async function getScheduledEvents({ }) { const now = new Date(); + const todayStart = dayjs().startOf("day").toDate(); + const todayEnd = dayjs().endOf("day").toDate(); + const where: Prisma.ScheduledEventWhereInput = { userId, deletedAt: null, - ...(status !== "past" && { start: { gte: now } }), - ...(status === "past" && { start: { lt: now } }), + ...(status === "upcoming" && { + OR: [ + { allDay: false, start: { gte: now } }, + { allDay: true, start: { gte: todayStart, lte: todayEnd } }, + ], + }), + ...(status === "past" && { + OR: [ + { allDay: false, start: { lt: now } }, + { allDay: true, start: { lt: todayStart } }, + ], + }), ...(search && { title: { contains: search, mode: "insensitive" } }), status: mapStatus[status], }; @@ -76,10 +89,6 @@ export async function getScheduledEvents({ const events = rawEvents.map((event) => ({ ...event, - status: - event.status === "confirmed" - ? ((event.start < now ? "past" : "upcoming") as Status) - : event.status, invites: event.invites.map((invite) => ({ id: invite.id, inviteeName: invite.inviteeName, diff --git a/apps/web/src/features/scheduled-event/components/scheduled-event-list.tsx b/apps/web/src/features/scheduled-event/components/scheduled-event-list.tsx index 5cac5efa3..96a868cc6 100644 --- a/apps/web/src/features/scheduled-event/components/scheduled-event-list.tsx +++ b/apps/web/src/features/scheduled-event/components/scheduled-event-list.tsx @@ -1,8 +1,6 @@ import { ParticipantAvatarBar } from "@/components/participant-avatar-bar"; import { StackedList } from "@/components/stacked-list"; import { Trans } from "@/components/trans"; -import { ScheduledEventStatusBadge } from "@/features/scheduled-event/components/scheduled-event-status-badge"; -import type { Status } from "@/features/scheduled-event/schema"; import { FormattedDateTime } from "@/features/timezone/client/formatted-date-time"; export const ScheduledEventList = StackedList; @@ -11,7 +9,6 @@ export function ScheduledEventListItem({ title, start, end, - status, allDay, invites, floating: isFloating, @@ -20,7 +17,6 @@ export function ScheduledEventListItem({ title: string; start: Date; end: Date; - status: Status; allDay: boolean; invites: { id: string; inviteeName: string; inviteeImage?: string }[]; floating: boolean; @@ -30,9 +26,6 @@ export function ScheduledEventListItem({
{title}
-
- -
diff --git a/apps/web/src/trpc/routers/index.ts b/apps/web/src/trpc/routers/index.ts index 91f552a57..34e2b98d3 100644 --- a/apps/web/src/trpc/routers/index.ts +++ b/apps/web/src/trpc/routers/index.ts @@ -7,7 +7,6 @@ import { mergeRouters, router } from "../trpc"; import { auth } from "./auth"; import { dashboard } from "./dashboard"; import { polls } from "./polls"; -import { scheduledEvents } from "./scheduled-events"; import { user } from "./user"; dayjs.extend(toArray); // used for creating ics @@ -16,7 +15,6 @@ dayjs.extend(utc); export const appRouter = mergeRouters( router({ - scheduledEvents, auth, polls, user, diff --git a/apps/web/src/trpc/routers/polls.ts b/apps/web/src/trpc/routers/polls.ts index 1105a8042..00e16fa7c 100644 --- a/apps/web/src/trpc/routers/polls.ts +++ b/apps/web/src/trpc/routers/polls.ts @@ -461,18 +461,27 @@ export const polls = router({ userId: true, guestId: true, deleted: true, - event: { - select: { - start: true, - duration: true, - optionId: true, - }, - }, watchers: { select: { userId: true, }, }, + scheduledEvent: { + select: { + start: true, + end: true, + allDay: true, + invites: { + select: { + id: true, + inviteeName: true, + inviteeEmail: true, + inviteeTimeZone: true, + status: true, + }, + }, + }, + }, }, where: { id: input.urlId, @@ -490,10 +499,33 @@ export const polls = router({ ? userId === res.guestId : userId === res.userId; + const event = res.scheduledEvent + ? { + start: res.scheduledEvent.start, + duration: res.scheduledEvent.allDay + ? 0 + : dayjs(res.scheduledEvent.end).diff( + dayjs(res.scheduledEvent.start), + "minute", + ), + attendees: res.scheduledEvent.invites + .map((invite) => ({ + name: invite.inviteeName, + email: invite.inviteeEmail, + status: invite.status, + })) + .filter((invite) => invite.status === "accepted"), + } + : null; + if (isOwner || res.adminUrlId === input.adminToken) { - return { ...res, inviteLink }; + return { + ...res, + inviteLink, + event, + }; } else { - return { ...res, adminUrlId: "", inviteLink }; + return { ...res, adminUrlId: "", inviteLink, event }; } }), book: proProcedure @@ -626,15 +658,6 @@ export const polls = router({ }, }, }, - event: { - create: { - optionId: input.optionId, - start: eventStart.toDate(), - duration: option.duration, - title: poll.title, - userId: ctx.user.id, - }, - }, }, }); @@ -818,14 +841,6 @@ export const polls = router({ }, }); - if (poll.eventId) { - await prisma.event.delete({ - where: { - id: poll.eventId, - }, - }); - } - if (poll.scheduledEventId) { await prisma.scheduledEvent.delete({ where: { diff --git a/apps/web/src/trpc/routers/scheduled-events.ts b/apps/web/src/trpc/routers/scheduled-events.ts deleted file mode 100644 index 7d77dbb05..000000000 --- a/apps/web/src/trpc/routers/scheduled-events.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { prisma } from "@rallly/database"; -import dayjs from "dayjs"; -import timezone from "dayjs/plugin/timezone"; -import toArray from "dayjs/plugin/toArray"; -import utc from "dayjs/plugin/utc"; -import { z } from "zod"; - -import { privateProcedure, router } from "../trpc"; - -dayjs.extend(toArray); -dayjs.extend(timezone); -dayjs.extend(utc); - -export const scheduledEvents = router({ - list: privateProcedure - .input( - z.object({ - period: z.enum(["upcoming", "past"]).default("upcoming"), - }), - ) - .query(async ({ input, ctx }) => { - const events = await prisma.event.findMany({ - select: { - id: true, - title: true, - start: true, - duration: true, - poll: { - select: { - timeZone: true, - participants: { - select: { - id: true, - name: true, - }, - }, - }, - }, - }, - where: { - userId: ctx.user.id, - start: - input.period === "upcoming" - ? { gte: new Date() } - : { lt: new Date() }, - }, - orderBy: [ - { - start: "desc", - }, - { - title: "asc", - }, - ], - }); - - return events.map(({ poll, ...event }) => ({ - ...event, - timeZone: poll?.timeZone || null, - participants: poll?.participants ?? [], - })); - }), -}); diff --git a/packages/database/prisma/migrations/20250614062854_remove_deprecated_event_model/migration.sql b/packages/database/prisma/migrations/20250614062854_remove_deprecated_event_model/migration.sql new file mode 100644 index 000000000..3be795570 --- /dev/null +++ b/packages/database/prisma/migrations/20250614062854_remove_deprecated_event_model/migration.sql @@ -0,0 +1,14 @@ +/* + Warnings: + + - You are about to drop the `events` table. If the table is not empty, all the data it contains will be lost. + +*/ +-- DropForeignKey +ALTER TABLE "events" DROP CONSTRAINT "events_user_id_fkey"; + +-- DropForeignKey +ALTER TABLE "polls" DROP CONSTRAINT "polls_event_id_fkey"; + +-- DropTable +DROP TABLE "events"; diff --git a/packages/database/prisma/models/event.prisma b/packages/database/prisma/models/event.prisma index 9e93f418a..1b3fd1901 100644 --- a/packages/database/prisma/models/event.prisma +++ b/packages/database/prisma/models/event.prisma @@ -1,23 +1,3 @@ -/** - * Events are created when a user creates a new event - * @deprecated - */ -model Event { - id String @id @default(cuid()) - userId String @map("user_id") - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - optionId String @map("option_id") - title String - start DateTime @db.Timestamp(0) - duration Int @default(0) @map("duration_minutes") - createdAt DateTime @default(now()) @map("created_at") - - poll Poll? - - @@index([userId], type: Hash) - @@map("events") -} - enum ScheduledEventStatus { confirmed canceled diff --git a/packages/database/prisma/models/poll.prisma b/packages/database/prisma/models/poll.prisma index 77d7ac93e..05c2f55ba 100644 --- a/packages/database/prisma/models/poll.prisma +++ b/packages/database/prisma/models/poll.prisma @@ -39,7 +39,6 @@ model Poll { requireParticipantEmail Boolean @default(false) @map("require_participant_email") user User? @relation(fields: [userId], references: [id], onDelete: Cascade) - event Event? @relation(fields: [eventId], references: [id], onDelete: SetNull) scheduledEvent ScheduledEvent? @relation(fields: [scheduledEventId], references: [id], onDelete: SetNull) options Option[] participants Participant[] diff --git a/packages/database/prisma/models/user.prisma b/packages/database/prisma/models/user.prisma index 7859c65b8..cf9b026e7 100644 --- a/packages/database/prisma/models/user.prisma +++ b/packages/database/prisma/models/user.prisma @@ -53,7 +53,6 @@ model User { comments Comment[] polls Poll[] watcher Watcher[] - events Event[] accounts Account[] participants Participant[] paymentMethods PaymentMethod[]