Skip to content

Commit

Permalink
Merge branch 'main' into feat/skip-confirm-step
Browse files Browse the repository at this point in the history
  • Loading branch information
SomayChauhan committed Jan 21, 2025
2 parents 9290e43 + 30eb7ce commit 025c387
Show file tree
Hide file tree
Showing 115 changed files with 1,977 additions and 6,173 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ E2E_TEST_MAILHOG_ENABLED=

# Cloudflare Turnstile
NEXT_PUBLIC_CLOUDFLARE_SITEKEY=
NEXT_PUBLIC_CLOUDFLARE_USE_TURNSTILE_IN_BOOKER=
CLOUDFLARE_TURNSTILE_SECRET=

# Set the following value to true if you wish to enable Team Impersonation
Expand Down
8 changes: 8 additions & 0 deletions .yarn/versions/727b22e1.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
undecided:
- calcom-monorepo
- "@calcom/app-store-cli"
- "@calcom/platform-constants"
- "@calcom/platform-enums"
- "@calcom/platform-types"
- "@calcom/platform-utils"
- "@calcom/prisma"
6 changes: 5 additions & 1 deletion apps/web/components/booking/BookingListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type BookingItemProps = BookingItem & {
userTimeFormat: number | null | undefined;
userEmail: string | undefined;
};
isToday: boolean;
};

type ParsedBooking = ReturnType<typeof buildParsedBooking>;
Expand Down Expand Up @@ -511,7 +512,10 @@ function BookingListItem(booking: BookingItemProps) {
</DialogFooter>
</DialogContent>
</Dialog>
<div data-testid="booking-item" className="hover:bg-muted group w-full">
<div
data-testid="booking-item"
data-today={String(booking.isToday)}
className="hover:bg-muted group w-full">
<div className="flex flex-col sm:flex-row">
<div className="hidden align-top ltr:pl-3 rtl:pr-6 sm:table-cell sm:min-w-[12rem]">
<div className="flex h-full items-center">
Expand Down
215 changes: 114 additions & 101 deletions apps/web/modules/bookings/views/bookings-listing-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
createColumnHelper,
} from "@tanstack/react-table";
import type { ReactElement } from "react";
import { Fragment, useMemo, useState } from "react";
import { useMemo, useState } from "react";
import type { z } from "zod";

import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components";
Expand Down Expand Up @@ -85,10 +85,16 @@ export default function Bookings(props: BookingsProps) {
);
}

type RowData = {
booking: BookingOutput;
recurringInfo?: RecurringInfo;
};
type RowData =
| {
type: "data";
booking: BookingOutput;
isToday: boolean;
recurringInfo?: RecurringInfo;
}
| {
type: "today" | "next";
};

function BookingsContent({ status }: BookingsProps) {
const { data: filterQuery } = useFilterQuery();
Expand Down Expand Up @@ -118,88 +124,124 @@ function BookingsContent({ status }: BookingsProps) {
columnHelper.display({
id: "custom-view",
cell: (props) => {
const { booking, recurringInfo } = props.row.original;
return (
<BookingListItem
key={booking.id}
loggedInUser={{
userId: user?.id,
userTimeZone: user?.timeZone,
userTimeFormat: user?.timeFormat,
userEmail: user?.email,
}}
listingStatus={status}
recurringInfo={recurringInfo}
{...booking}
/>
);
if (props.row.original.type === "data") {
const { booking, recurringInfo, isToday } = props.row.original;
return (
<BookingListItem
key={booking.id}
isToday={isToday}
loggedInUser={{
userId: user?.id,
userTimeZone: user?.timeZone,
userTimeFormat: user?.timeFormat,
userEmail: user?.email,
}}
listingStatus={status}
recurringInfo={recurringInfo}
{...booking}
/>
);
} else if (props.row.original.type === "today") {
return (
<p className="text-subtle bg-subtle w-full py-4 pl-6 text-xs font-semibold uppercase leading-4">
{t("today")}
</p>
);
} else if (props.row.original.type === "next") {
return (
<p className="text-subtle bg-subtle w-full py-4 pl-6 text-xs font-semibold uppercase leading-4">
{t("next")}
</p>
);
}
},
}),
];
}, [user, status]);

const isEmpty = !query.data?.pages[0]?.bookings.length;
const isEmpty = useMemo(() => !query.data?.pages[0]?.bookings.length, [query.data]);

const shownBookings: Record<string, BookingOutput[]> = {};
const filterBookings = (booking: BookingOutput) => {
if (status === "recurring" || status == "unconfirmed" || status === "cancelled") {
if (!booking.recurringEventId) {
return true;
}
if (
shownBookings[booking.recurringEventId] !== undefined &&
shownBookings[booking.recurringEventId].length > 0
) {
shownBookings[booking.recurringEventId].push(booking);
return false;
const flatData = useMemo<RowData[]>(() => {
const shownBookings: Record<string, BookingOutput[]> = {};
const filterBookings = (booking: BookingOutput) => {
if (status === "recurring" || status == "unconfirmed" || status === "cancelled") {
if (!booking.recurringEventId) {
return true;
}
if (
shownBookings[booking.recurringEventId] !== undefined &&
shownBookings[booking.recurringEventId].length > 0
) {
shownBookings[booking.recurringEventId].push(booking);
return false;
}
shownBookings[booking.recurringEventId] = [booking];
} else if (status === "upcoming") {
return (
dayjs(booking.startTime).tz(user?.timeZone).format("YYYY-MM-DD") !==
dayjs().tz(user?.timeZone).format("YYYY-MM-DD")
);
}
shownBookings[booking.recurringEventId] = [booking];
} else if (status === "upcoming") {
return (
dayjs(booking.startTime).tz(user?.timeZone).format("YYYY-MM-DD") !==
dayjs().tz(user?.timeZone).format("YYYY-MM-DD")
);
}
return true;
};
return true;
};

const flatData = useMemo(() => {
return (
query.data?.pages.flatMap((page) =>
page.bookings.filter(filterBookings).map((booking) => ({
type: "data",
booking,
recurringInfo: page.recurringInfo.find(
(info) => info.recurringEventId === booking.recurringEventId
),
isToday: false,
}))
) || []
);
}, [query.data]);

const bookingsToday = useMemo<RowData[]>(() => {
return (
query.data?.pages.map((page) =>
page.bookings
.filter(
(booking: BookingOutput) =>
dayjs(booking.startTime).tz(user?.timeZone).format("YYYY-MM-DD") ===
dayjs().tz(user?.timeZone).format("YYYY-MM-DD")
)
.map((booking) => ({
type: "data" as const,
booking,
recurringInfo: page.recurringInfo.find(
(info) => info.recurringEventId === booking.recurringEventId
),
isToday: true,
}))
)[0] || []
);
}, [query.data]);

const finalData = useMemo<RowData[]>(() => {
if (bookingsToday.length > 0 && status === "upcoming") {
const merged: RowData[] = [
{ type: "today" as const },
...bookingsToday,
{ type: "next" as const },
...flatData,
];
return merged;
} else {
return flatData;
}
}, [bookingsToday, flatData, status]);

const table = useReactTable<RowData>({
data: flatData,
data: finalData,
columns,
getCoreRowModel: getCoreRowModel(),
getFilteredRowModel: getFilteredRowModel(),
getSortedRowModel: getSortedRowModel(),
});

let recurringInfoToday: RecurringInfo | undefined;

const bookingsToday =
query.data?.pages.map((page) =>
page.bookings.filter((booking: BookingOutput) => {
recurringInfoToday = page.recurringInfo.find(
(info) => info.recurringEventId === booking.recurringEventId
);

return (
dayjs(booking.startTime).tz(user?.timeZone).format("YYYY-MM-DD") ===
dayjs().tz(user?.timeZone).format("YYYY-MM-DD")
);
})
)[0] || [];

return (
<div className="flex flex-col">
<div className="flex flex-row flex-wrap justify-between">
Expand All @@ -216,48 +258,19 @@ function BookingsContent({ status }: BookingsProps) {
{query.status === "success" && !isEmpty && (
<>
{!!bookingsToday.length && status === "upcoming" && (
<div className="mb-6 pt-2 xl:pt-0">
<WipeMyCalActionButton bookingStatus={status} bookingsEmpty={isEmpty} />
<p className="text-subtle mb-2 text-xs font-medium uppercase leading-4">{t("today")}</p>
<div className="border-subtle overflow-hidden rounded-md border">
<div
className="bg-default divide-subtle w-full max-w-full divide-y"
data-testid="today-bookings">
<Fragment>
{bookingsToday.map((booking: BookingOutput) => (
<BookingListItem
key={booking.id}
loggedInUser={{
userId: user?.id,
userTimeZone: user?.timeZone,
userTimeFormat: user?.timeFormat,
userEmail: user?.email,
}}
listingStatus={status}
recurringInfo={recurringInfoToday}
{...booking}
/>
))}
</Fragment>
</div>
</div>
</div>
)}

{flatData.length > 0 && (
<DataTableWrapper
table={table}
testId={`${status}-bookings`}
bodyTestId="bookings"
hideHeader={true}
isPending={query.isFetching && !flatData}
hasNextPage={query.hasNextPage}
fetchNextPage={query.fetchNextPage}
isFetching={query.isFetching}
variant="compact"
containerClassName="!h-[inherit] max-h-[80dvh]"
/>
<WipeMyCalActionButton bookingStatus={status} bookingsEmpty={isEmpty} />
)}
<DataTableWrapper
table={table}
testId={`${status}-bookings`}
bodyTestId="bookings"
hideHeader={true}
isPending={query.isFetching && !flatData}
hasNextPage={query.hasNextPage}
fetchNextPage={query.fetchNextPage}
isFetching={query.isFetching}
variant="compact"
/>
</>
)}
{query.status === "success" && isEmpty && (
Expand Down
2 changes: 1 addition & 1 deletion apps/web/modules/signup-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const signupSchema = apiSignupSchema.extend({
cfToken: z.string().optional(),
});

const TurnstileCaptcha = dynamic(() => import("@components/auth/Turnstile"), { ssr: false });
const TurnstileCaptcha = dynamic(() => import("@calcom/features/auth/Turnstile"), { ssr: false });

type FormValues = z.infer<typeof signupSchema>;

Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "4.9.2",
"version": "4.9.3",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
Expand Down
8 changes: 8 additions & 0 deletions apps/web/pages/api/book/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,18 @@ import handleNewBooking from "@calcom/features/bookings/lib/handleNewBooking";
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
import getIP from "@calcom/lib/getIP";
import { defaultResponder } from "@calcom/lib/server";
import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken";

async function handler(req: NextApiRequest & { userId?: number }, res: NextApiResponse) {
const userIp = getIP(req);

if (process.env.NEXT_PUBLIC_CLOUDFLARE_USE_TURNSTILE_IN_BOOKER === "1") {
await checkCfTurnstileToken({
token: req.body["cfToken"] as string,
remoteIp: userIp,
});
}

await checkRateLimitAndThrowError({
rateLimitingType: "core",
identifier: userIp,
Expand Down
8 changes: 8 additions & 0 deletions apps/web/pages/api/book/recurring-event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,20 @@ import type { BookingResponse } from "@calcom/features/bookings/types";
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
import getIP from "@calcom/lib/getIP";
import { defaultResponder } from "@calcom/lib/server";
import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken";

// @TODO: Didn't look at the contents of this function in order to not break old booking page.

async function handler(req: NextApiRequest & { userId?: number }, res: NextApiResponse) {
const userIp = getIP(req);

if (process.env.NEXT_PUBLIC_CLOUDFLARE_USE_TURNSTILE_IN_BOOKER === "1") {
await checkCfTurnstileToken({
token: req.body[0]["cfToken"] as string,
remoteIp: userIp,
});
}

await checkRateLimitAndThrowError({
rateLimitingType: "core",
identifier: userIp,
Expand Down
6 changes: 3 additions & 3 deletions apps/web/playwright/wipe-my-cal.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ test.describe("Wipe my Cal App Test", () => {
await page.goto("/bookings/upcoming");
await expect(page.locator("data-testid=wipe-today-button")).toBeVisible();

const $openBookingCount = await page.locator('[data-testid="bookings"] > *').count();
const $todayBookingCount = await page.locator('[data-testid="today-bookings"] > *').count();
expect($openBookingCount + $todayBookingCount).toBe(3);
const $nonTodayBookingCount = await page.locator('[data-today="false"]').count();
const $todayBookingCount = await page.locator('[data-today="true"]').count();
expect($nonTodayBookingCount + $todayBookingCount).toBe(3);

await page.locator("data-testid=wipe-today-button").click();

Expand Down
1 change: 1 addition & 0 deletions apps/web/public/static/locales/ar/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2927,5 +2927,6 @@
"managed_users_description": "عرض جميع المستخدمين المُدارين الذين تم إنشاؤهم بواسطة عميل OAuth الخاص بك",
"select_oAuth_client": "اختر عميل OAuth",
"on_every_instance": "في كل مثيل",
"next": "التالي",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ أضف السلاسل الجديدة أعلاه هنا ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
1 change: 1 addition & 0 deletions apps/web/public/static/locales/az/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2927,5 +2927,6 @@
"managed_users_description": "OAuth müştəriniz tərəfindən yaradılmış bütün idarə edilən istifadəçiləri görün",
"select_oAuth_client": "OAuth müştərisini seçin",
"on_every_instance": "Hər nümunədə",
"next": "Növbəti",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni sətirləri bura əlavə edin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
1 change: 1 addition & 0 deletions apps/web/public/static/locales/bg/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -2927,5 +2927,6 @@
"managed_users_description": "Вижте всички управлявани потребители, създадени от вашия OAuth клиент",
"select_oAuth_client": "Изберете OAuth клиент",
"on_every_instance": "При всяко изпълнение",
"next": "Напред",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавете новите си низове над този ред ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
Loading

0 comments on commit 025c387

Please sign in to comment.