From 446f38440508cd32b01ab6fcfc7d00f6622c4ba8 Mon Sep 17 00:00:00 2001 From: Alex van Andel Date: Fri, 5 May 2023 17:36:33 +0200 Subject: [PATCH 01/34] Add publishHandler user.id (#8703) --- packages/trpc/server/routers/viewer/teams/publish.handler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/trpc/server/routers/viewer/teams/publish.handler.ts b/packages/trpc/server/routers/viewer/teams/publish.handler.ts index f2d5d95769..d492d4caff 100644 --- a/packages/trpc/server/routers/viewer/teams/publish.handler.ts +++ b/packages/trpc/server/routers/viewer/teams/publish.handler.ts @@ -5,7 +5,6 @@ import { isTeamAdmin } from "@calcom/lib/server/queries/teams"; import { closeComUpdateTeam } from "@calcom/lib/sync/SyncServiceManager"; import { prisma } from "@calcom/prisma"; import { teamMetadataSchema } from "@calcom/prisma/zod-utils"; -import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { TRPCError } from "@trpc/server"; @@ -13,7 +12,7 @@ import type { TPublishInputSchema } from "./publish.schema"; type PublishOptions = { ctx: { - user: NonNullable; + user: NonNullable<{ id: number }>; }; input: TPublishInputSchema; }; From 98a3508b8f53674cda9aab2b2842a86f472efc59 Mon Sep 17 00:00:00 2001 From: Jeroen Reumkens Date: Fri, 5 May 2023 16:55:54 +0100 Subject: [PATCH 02/34] CAL-1552: Add newline in front of time for selected timeslot in booker meta. (#8669) --- .../bookings/Booker/components/EventMeta.tsx | 30 +++++++-------- .../features/bookings/Booker/utils/dates.ts | 18 --------- .../features/bookings/Booker/utils/dates.tsx | 38 +++++++++++++++++++ 3 files changed, 53 insertions(+), 33 deletions(-) delete mode 100644 packages/features/bookings/Booker/utils/dates.ts create mode 100644 packages/features/bookings/Booker/utils/dates.tsx diff --git a/packages/features/bookings/Booker/components/EventMeta.tsx b/packages/features/bookings/Booker/components/EventMeta.tsx index 59b9dd35e3..4b896df51e 100644 --- a/packages/features/bookings/Booker/components/EventMeta.tsx +++ b/packages/features/bookings/Booker/components/EventMeta.tsx @@ -9,7 +9,7 @@ import { Calendar, Globe } from "@calcom/ui/components/icon"; import { fadeInUp } from "../config"; import { useBookerStore } from "../store"; -import { formatEventFromToTime } from "../utils/dates"; +import { FromToTime } from "../utils/dates"; import { useEvent } from "../utils/event"; const TimezoneSelect = dynamic(() => import("@calcom/ui").then((mod) => mod.TimezoneSelect), { @@ -42,25 +42,25 @@ export const EventMeta = () => { {t("former_time")}
- {formatEventFromToTime( - rescheduleBooking.startTime.toString(), - null, - timeFormat, - timezone, - i18n.language - )} + )} {selectedTimeslot && ( - {formatEventFromToTime( - selectedTimeslot, - selectedDuration, - timeFormat, - timezone, - i18n.language - )} + )} diff --git a/packages/features/bookings/Booker/utils/dates.ts b/packages/features/bookings/Booker/utils/dates.ts deleted file mode 100644 index 7306119b08..0000000000 --- a/packages/features/bookings/Booker/utils/dates.ts +++ /dev/null @@ -1,18 +0,0 @@ -import dayjs from "@calcom/dayjs"; -import type { TimeFormat } from "@calcom/lib/timeFormat"; - -export const formatEventFromToTime = ( - date: string, - duration: number | null, - timeFormat: TimeFormat, - timeZone: string, - language: string -) => { - const start = dayjs(date).tz(timeZone); - const end = duration ? start.add(duration, "minute") : null; - return `${start.format("dddd")}, ${start - .toDate() - .toLocaleDateString(language, { dateStyle: "long" })} ${start.format(timeFormat)} ${ - end ? `– ${end.format(timeFormat)}` : `` - }`; -}; diff --git a/packages/features/bookings/Booker/utils/dates.tsx b/packages/features/bookings/Booker/utils/dates.tsx new file mode 100644 index 0000000000..7584c1add8 --- /dev/null +++ b/packages/features/bookings/Booker/utils/dates.tsx @@ -0,0 +1,38 @@ +import dayjs from "@calcom/dayjs"; +import type { TimeFormat } from "@calcom/lib/timeFormat"; + +interface EventFromToTime { + date: string; + duration: number | null; + timeFormat: TimeFormat; + timeZone: string; + language: string; +} + +export const formatEventFromToTime = ({ + date, + duration, + timeFormat, + timeZone, + language, +}: EventFromToTime) => { + const start = dayjs(date).tz(timeZone); + const end = duration ? start.add(duration, "minute") : null; + const formattedDate = `${start.format("dddd")}, ${start + .toDate() + .toLocaleDateString(language, { dateStyle: "long" })}`; + const formattedTime = `${start.format(timeFormat)} ${end ? `– ${end.format(timeFormat)}` : ``}`; + + return { date: formattedDate, time: formattedTime }; +}; + +export const FromToTime = (props: EventFromToTime) => { + const formatted = formatEventFromToTime(props); + return ( + <> + {formatted.date} +
+ {formatted.time} + + ); +}; From cdba1920fce52deefeb314696f34049b7614cf7f Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 5 May 2023 18:19:10 +0200 Subject: [PATCH 03/34] refactor: split up routers to separate lambdas (#8041) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * split up routers to separate lambdas * fix responsemeta * move * add typeguards to make sure all endpoints are covered in the approuter * prettier * move slotsrouter * split ssg/ssr * make sure correct headers are sent on viewer.public * make sure correct headers are sent + use ctx.prisma * Fixed new prefetch broken by merge * Fixes after merge * Created separate API route for all tRPC routers * More fixes from refactor * Fixed tRPC query for slots * Put back extra line * Fixed type checks * Removed Endpoint type check since it loads from client * Reverted change in getSchedule test * Fix trpc routes in expectations * Fix one more route test --------- Co-authored-by: Efraín Rochín Co-authored-by: Alex van Andel Co-authored-by: Peer Richelsen Co-authored-by: Keith Williams Co-authored-by: zomars Co-authored-by: Hariom Balhara --- apps/web/pages/api/trpc/[trpc].ts | 83 ------------------- apps/web/pages/api/trpc/apiKeys/[trpc].ts | 4 + .../pages/api/trpc/appRoutingForms/[trpc].ts | 4 + apps/web/pages/api/trpc/apps/[trpc].ts | 4 + apps/web/pages/api/trpc/auth/[trpc].ts | 4 + .../web/pages/api/trpc/availability/[trpc].ts | 4 + apps/web/pages/api/trpc/bookings/[trpc].ts | 4 + .../pages/api/trpc/deploymentSetup/[trpc].ts | 4 + apps/web/pages/api/trpc/eth/[trpc].ts | 4 + apps/web/pages/api/trpc/eventTypes/[trpc].ts | 4 + apps/web/pages/api/trpc/features/[trpc].ts | 4 + apps/web/pages/api/trpc/insights/[trpc].ts | 4 + apps/web/pages/api/trpc/payments/[trpc].ts | 4 + apps/web/pages/api/trpc/public/[trpc].ts | 4 + apps/web/pages/api/trpc/saml/[trpc].ts | 4 + apps/web/pages/api/trpc/slots/[trpc].ts | 4 + apps/web/pages/api/trpc/teams/[trpc].ts | 4 + apps/web/pages/api/trpc/users/[trpc].ts | 4 + apps/web/pages/api/trpc/viewer/[trpc].ts | 4 + apps/web/pages/api/trpc/webhook/[trpc].ts | 4 + apps/web/pages/api/trpc/workflows/[trpc].ts | 4 + apps/web/playwright/availability.e2e.ts | 2 +- apps/web/playwright/booking-pages.e2e.ts | 2 +- .../playwright/tests/basic.e2e.ts | 2 +- packages/trpc/react/trpc.ts | 76 ++++++++++++++--- packages/trpc/server/createNextApiHandler.ts | 74 +++++++++++++++++ playwright.config.ts | 12 +++ 27 files changed, 235 insertions(+), 96 deletions(-) delete mode 100644 apps/web/pages/api/trpc/[trpc].ts create mode 100644 apps/web/pages/api/trpc/apiKeys/[trpc].ts create mode 100644 apps/web/pages/api/trpc/appRoutingForms/[trpc].ts create mode 100644 apps/web/pages/api/trpc/apps/[trpc].ts create mode 100644 apps/web/pages/api/trpc/auth/[trpc].ts create mode 100644 apps/web/pages/api/trpc/availability/[trpc].ts create mode 100644 apps/web/pages/api/trpc/bookings/[trpc].ts create mode 100644 apps/web/pages/api/trpc/deploymentSetup/[trpc].ts create mode 100644 apps/web/pages/api/trpc/eth/[trpc].ts create mode 100644 apps/web/pages/api/trpc/eventTypes/[trpc].ts create mode 100644 apps/web/pages/api/trpc/features/[trpc].ts create mode 100644 apps/web/pages/api/trpc/insights/[trpc].ts create mode 100644 apps/web/pages/api/trpc/payments/[trpc].ts create mode 100644 apps/web/pages/api/trpc/public/[trpc].ts create mode 100644 apps/web/pages/api/trpc/saml/[trpc].ts create mode 100644 apps/web/pages/api/trpc/slots/[trpc].ts create mode 100644 apps/web/pages/api/trpc/teams/[trpc].ts create mode 100644 apps/web/pages/api/trpc/users/[trpc].ts create mode 100644 apps/web/pages/api/trpc/viewer/[trpc].ts create mode 100644 apps/web/pages/api/trpc/webhook/[trpc].ts create mode 100644 apps/web/pages/api/trpc/workflows/[trpc].ts create mode 100644 packages/trpc/server/createNextApiHandler.ts diff --git a/apps/web/pages/api/trpc/[trpc].ts b/apps/web/pages/api/trpc/[trpc].ts deleted file mode 100644 index 4dd7642397..0000000000 --- a/apps/web/pages/api/trpc/[trpc].ts +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file contains tRPC's HTTP response handler - */ -import { z } from "zod"; - -import { getServerSession } from "@calcom/features/auth/lib/getServerSession"; -import * as trpcNext from "@calcom/trpc/server/adapters/next"; -import { createContext as createTrpcContext } from "@calcom/trpc/server/createContext"; -import { appRouter } from "@calcom/trpc/server/routers/_app"; - -export default trpcNext.createNextApiHandler({ - router: appRouter, - /** - * @link https://trpc.io/docs/context - */ - createContext: ({ req, res }) => { - const sessionGetter = () => getServerSession({ req, res }); - - return createTrpcContext({ req, res }, sessionGetter); - }, - /** - * @link https://trpc.io/docs/error-handling - */ - onError({ error }) { - if (error.code === "INTERNAL_SERVER_ERROR") { - // send to bug reporting - console.error("Something went wrong", error); - } - }, - /** - * Enable query batching - */ - batching: { - enabled: true, - }, - /** - * @link https://trpc.io/docs/caching#api-response-caching - */ - responseMeta({ ctx, paths, type, errors }) { - // Some helpers relevant to this function only - const ONE_DAY_IN_SECONDS = 60 * 60 * 24; - // assuming you have all your public routes with the keyword `public` in them - const allPublic = paths && paths.every((path) => path.startsWith("viewer.public.")); - // checking that no procedures errored - const allOk = errors.length === 0; - // checking we're doing a query request - const isQuery = type === "query"; - const noHeaders = {}; - - // We cannot set headers on SSG queries - if (!ctx?.res) return noHeaders; - - const defaultHeaders: Record<"headers", Record> = { - headers: {}, - }; - - const timezone = z.string().safeParse(ctx.req?.headers["x-vercel-ip-timezone"]); - if (timezone.success) defaultHeaders.headers["x-cal-timezone"] = timezone.data; - - // We need all these conditions to be true to set cache headers - if (!(allPublic && allOk && isQuery)) return defaultHeaders; - - // No cache by default - defaultHeaders.headers["cache-control"] = `no-cache`; - - // Our cache can change depending on our current paths value. Since paths is an array, - // we want to create a map that can match potential paths with their desired cache value - const cacheRules = { - "viewer.public.session": `no-cache`, - "viewer.public.i18n": `no-cache`, - // Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321 - "viewer.public.slots.getSchedule": `no-cache`, // FIXME - "viewer.public.cityTimezones": `max-age=${ONE_DAY_IN_SECONDS}, stale-while-revalidate`, - } as const; - - // Find which element above is an exact match for this group of paths - const matchedPath = paths.find((v) => v in cacheRules) as keyof typeof cacheRules; - - if (matchedPath) defaultHeaders.headers["cache-control"] = cacheRules[matchedPath]; - - return defaultHeaders; - }, -}); diff --git a/apps/web/pages/api/trpc/apiKeys/[trpc].ts b/apps/web/pages/api/trpc/apiKeys/[trpc].ts new file mode 100644 index 0000000000..60b65ea0a1 --- /dev/null +++ b/apps/web/pages/api/trpc/apiKeys/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { apiKeysRouter } from "@calcom/trpc/server/routers/viewer/apiKeys/_router"; + +export default createNextApiHandler(apiKeysRouter); diff --git a/apps/web/pages/api/trpc/appRoutingForms/[trpc].ts b/apps/web/pages/api/trpc/appRoutingForms/[trpc].ts new file mode 100644 index 0000000000..63037301d8 --- /dev/null +++ b/apps/web/pages/api/trpc/appRoutingForms/[trpc].ts @@ -0,0 +1,4 @@ +import appRoutingForms from "@calcom/app-store/routing-forms/trpc-router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(appRoutingForms); diff --git a/apps/web/pages/api/trpc/apps/[trpc].ts b/apps/web/pages/api/trpc/apps/[trpc].ts new file mode 100644 index 0000000000..e7e03ae67f --- /dev/null +++ b/apps/web/pages/api/trpc/apps/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router"; + +export default createNextApiHandler(appsRouter); diff --git a/apps/web/pages/api/trpc/auth/[trpc].ts b/apps/web/pages/api/trpc/auth/[trpc].ts new file mode 100644 index 0000000000..28363de1d2 --- /dev/null +++ b/apps/web/pages/api/trpc/auth/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { authRouter } from "@calcom/trpc/server/routers/viewer/auth/_router"; + +export default createNextApiHandler(authRouter); diff --git a/apps/web/pages/api/trpc/availability/[trpc].ts b/apps/web/pages/api/trpc/availability/[trpc].ts new file mode 100644 index 0000000000..b8ba4c7cb8 --- /dev/null +++ b/apps/web/pages/api/trpc/availability/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { availabilityRouter } from "@calcom/trpc/server/routers/viewer/availability/_router"; + +export default createNextApiHandler(availabilityRouter); diff --git a/apps/web/pages/api/trpc/bookings/[trpc].ts b/apps/web/pages/api/trpc/bookings/[trpc].ts new file mode 100644 index 0000000000..908a82a82c --- /dev/null +++ b/apps/web/pages/api/trpc/bookings/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { bookingsRouter } from "@calcom/trpc/server/routers/viewer/bookings/_router"; + +export default createNextApiHandler(bookingsRouter); diff --git a/apps/web/pages/api/trpc/deploymentSetup/[trpc].ts b/apps/web/pages/api/trpc/deploymentSetup/[trpc].ts new file mode 100644 index 0000000000..c7dc57b734 --- /dev/null +++ b/apps/web/pages/api/trpc/deploymentSetup/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { deploymentSetupRouter } from "@calcom/trpc/server/routers/viewer/deploymentSetup/_router"; + +export default createNextApiHandler(deploymentSetupRouter); diff --git a/apps/web/pages/api/trpc/eth/[trpc].ts b/apps/web/pages/api/trpc/eth/[trpc].ts new file mode 100644 index 0000000000..9015a59cdc --- /dev/null +++ b/apps/web/pages/api/trpc/eth/[trpc].ts @@ -0,0 +1,4 @@ +import ethRouter from "@calcom/app-store/rainbow/trpc/router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(ethRouter); diff --git a/apps/web/pages/api/trpc/eventTypes/[trpc].ts b/apps/web/pages/api/trpc/eventTypes/[trpc].ts new file mode 100644 index 0000000000..90affa56f6 --- /dev/null +++ b/apps/web/pages/api/trpc/eventTypes/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { eventTypesRouter } from "@calcom/trpc/server/routers/viewer/eventTypes/_router"; + +export default createNextApiHandler(eventTypesRouter); diff --git a/apps/web/pages/api/trpc/features/[trpc].ts b/apps/web/pages/api/trpc/features/[trpc].ts new file mode 100644 index 0000000000..314293a648 --- /dev/null +++ b/apps/web/pages/api/trpc/features/[trpc].ts @@ -0,0 +1,4 @@ +import { featureFlagRouter } from "@calcom/features/flags/server/router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(featureFlagRouter); diff --git a/apps/web/pages/api/trpc/insights/[trpc].ts b/apps/web/pages/api/trpc/insights/[trpc].ts new file mode 100644 index 0000000000..f0eb3490ac --- /dev/null +++ b/apps/web/pages/api/trpc/insights/[trpc].ts @@ -0,0 +1,4 @@ +import { insightsRouter } from "@calcom/features/insights/server/trpc-router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(insightsRouter); diff --git a/apps/web/pages/api/trpc/payments/[trpc].ts b/apps/web/pages/api/trpc/payments/[trpc].ts new file mode 100644 index 0000000000..673a2b1e57 --- /dev/null +++ b/apps/web/pages/api/trpc/payments/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { paymentsRouter } from "@calcom/trpc/server/routers/viewer/payments/_router"; + +export default createNextApiHandler(paymentsRouter); diff --git a/apps/web/pages/api/trpc/public/[trpc].ts b/apps/web/pages/api/trpc/public/[trpc].ts new file mode 100644 index 0000000000..da8470cdfc --- /dev/null +++ b/apps/web/pages/api/trpc/public/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { publicViewerRouter } from "@calcom/trpc/server/routers/publicViewer/_router"; + +export default createNextApiHandler(publicViewerRouter, true); diff --git a/apps/web/pages/api/trpc/saml/[trpc].ts b/apps/web/pages/api/trpc/saml/[trpc].ts new file mode 100644 index 0000000000..d149507757 --- /dev/null +++ b/apps/web/pages/api/trpc/saml/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { ssoRouter } from "@calcom/trpc/server/routers/viewer/sso/_router"; + +export default createNextApiHandler(ssoRouter); diff --git a/apps/web/pages/api/trpc/slots/[trpc].ts b/apps/web/pages/api/trpc/slots/[trpc].ts new file mode 100644 index 0000000000..c220bf20f7 --- /dev/null +++ b/apps/web/pages/api/trpc/slots/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { slotsRouter } from "@calcom/trpc/server/routers/viewer/slots/_router"; + +export default createNextApiHandler(slotsRouter); diff --git a/apps/web/pages/api/trpc/teams/[trpc].ts b/apps/web/pages/api/trpc/teams/[trpc].ts new file mode 100644 index 0000000000..504b2692ea --- /dev/null +++ b/apps/web/pages/api/trpc/teams/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { viewerTeamsRouter } from "@calcom/trpc/server/routers/viewer/teams/_router"; + +export default createNextApiHandler(viewerTeamsRouter); diff --git a/apps/web/pages/api/trpc/users/[trpc].ts b/apps/web/pages/api/trpc/users/[trpc].ts new file mode 100644 index 0000000000..9829c4a3bb --- /dev/null +++ b/apps/web/pages/api/trpc/users/[trpc].ts @@ -0,0 +1,4 @@ +import { userAdminRouter } from "@calcom/features/ee/users/server/trpc-router"; +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; + +export default createNextApiHandler(userAdminRouter); diff --git a/apps/web/pages/api/trpc/viewer/[trpc].ts b/apps/web/pages/api/trpc/viewer/[trpc].ts new file mode 100644 index 0000000000..2e44c1f966 --- /dev/null +++ b/apps/web/pages/api/trpc/viewer/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { loggedInViewerRouter } from "@calcom/trpc/server/routers/loggedInViewer/_router"; + +export default createNextApiHandler(loggedInViewerRouter); diff --git a/apps/web/pages/api/trpc/webhook/[trpc].ts b/apps/web/pages/api/trpc/webhook/[trpc].ts new file mode 100644 index 0000000000..c19093f285 --- /dev/null +++ b/apps/web/pages/api/trpc/webhook/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { webhookRouter } from "@calcom/trpc/server/routers/viewer/webhook/_router"; + +export default createNextApiHandler(webhookRouter); diff --git a/apps/web/pages/api/trpc/workflows/[trpc].ts b/apps/web/pages/api/trpc/workflows/[trpc].ts new file mode 100644 index 0000000000..b086ebab6f --- /dev/null +++ b/apps/web/pages/api/trpc/workflows/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { workflowsRouter } from "@calcom/trpc/server/routers/viewer/workflows/_router"; + +export default createNextApiHandler(workflowsRouter); diff --git a/apps/web/playwright/availability.e2e.ts b/apps/web/playwright/availability.e2e.ts index abee0f3a49..723da4c766 100644 --- a/apps/web/playwright/availability.e2e.ts +++ b/apps/web/playwright/availability.e2e.ts @@ -33,7 +33,7 @@ test.describe("Availablity tests", () => { }); await test.step("Date override is displayed in troubleshooter", async () => { - const response = await page.waitForResponse("**/api/trpc/viewer.availability.schedule.update?batch=1"); + const response = await page.waitForResponse("**/api/trpc/availability/schedule.update?batch=1"); const json = await response.json(); // @ts-expect-error trust me bro const date = json[0].result.data.json.schedule.availability.find((a) => !!a.date); diff --git a/apps/web/playwright/booking-pages.e2e.ts b/apps/web/playwright/booking-pages.e2e.ts index 59a811e7a6..d5ef9c0ef3 100644 --- a/apps/web/playwright/booking-pages.e2e.ts +++ b/apps/web/playwright/booking-pages.e2e.ts @@ -131,7 +131,7 @@ testBothBookers.describe("pro user", () => { await page.goto("/bookings/unconfirmed"); await Promise.all([ page.click('[data-testid="confirm"]'), - page.waitForResponse((response) => response.url().includes("/api/trpc/viewer.bookings.confirm")), + page.waitForResponse((response) => response.url().includes("/api/trpc/bookings/confirm")), ]); // This is the only booking in there that needed confirmation and now it should be empty screen await expect(page.locator('[data-testid="empty-screen"]')).toBeVisible(); diff --git a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts index 3e47c36c98..6d2d4924b1 100644 --- a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts +++ b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts @@ -179,7 +179,7 @@ test.describe("Routing Forms", () => { await page.goto(`/apps/routing-forms/reporting/${routingForm.id}`); // Can't keep waiting forever. So, added a timeout of 5000ms - await page.waitForResponse((response) => response.url().includes("viewer.appRoutingForms.report"), { + await page.waitForResponse((response) => response.url().includes("appRoutingForms/report"), { timeout: 5000, }); const headerEls = page.locator("[data-testid='reporting-header'] th"); diff --git a/packages/trpc/react/trpc.ts b/packages/trpc/react/trpc.ts index ea3b579f09..aa1d08b191 100644 --- a/packages/trpc/react/trpc.ts +++ b/packages/trpc/react/trpc.ts @@ -12,6 +12,58 @@ import type { TRPCClientErrorLike } from "../react"; import type { inferRouterInputs, inferRouterOutputs, Maybe } from "../server"; import type { AppRouter } from "../server/routers/_app"; +/** + * We deploy our tRPC router on multiple lambdas to keep number of imports as small as possible + * TODO: Make this dynamic based on folders in trpc server? + */ +const ENDPOINTS = [ + "apiKeys", + "appRoutingForms", + "apps", + "auth", + "availability", + "bookings", + "deploymentSetup", + "eth", + "eventTypes", + "features", + "insights", + "payments", + "public", + "saml", + "slots", + "teams", + "users", + "viewer", + "webhook", + "workflows", +] as const; +export type Endpoint = (typeof ENDPOINTS)[number]; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const resolveEndpoint = (links: any) => { + // TODO: Update our trpc routes so they are more clear. + // This function parses paths like the following and maps them + // to the correct API endpoints. + // - viewer.me - 2 segment paths like this are for logged in requests + // - viewer.public.i18n - 3 segments paths can be public or authed + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return (ctx: any) => { + const parts = ctx.op.path.split("."); + let endpoint; + let path = ''; + if (parts.length == 2) { + endpoint = parts[0] as keyof typeof links; + path = parts[1]; + } else { + endpoint = parts[1] as keyof typeof links; + path = parts.splice(2, parts.length - 2).join('.'); + } + + return links[endpoint]({ ...ctx, op: { ...ctx.op, path } }); + }; +}; + /** * A set of strongly-typed React hooks from your `AppRouter` type signature with `createTRPCReact`. * @link https://trpc.io/docs/v10/react#2-create-trpc-hooks @@ -41,17 +93,21 @@ export const trpc = createTRPCNext { - return op.context.skipBatch === true; - }, + condition: (op) => !!op.context.skipBatch, // when condition is true, use normal request - true: httpLink({ url }), - // when condition is false, use batching - false: httpBatchLink({ - url, - /** @link https://github.com/trpc/trpc/issues/2008 */ - // maxBatchSize: 7 - }), + true: (runtime) => { + const links = Object.fromEntries( + ENDPOINTS.map((endpoint) => [endpoint, httpLink({ url: url + "/" + endpoint })(runtime)]) + ); + return resolveEndpoint(links); + }, + // when condition is false, use batch request + false: (runtime) => { + const links = Object.fromEntries( + ENDPOINTS.map((endpoint) => [endpoint, httpBatchLink({ url: url + "/" + endpoint })(runtime)]) + ); + return resolveEndpoint(links); + }, }), ], /** diff --git a/packages/trpc/server/createNextApiHandler.ts b/packages/trpc/server/createNextApiHandler.ts new file mode 100644 index 0000000000..05ac119017 --- /dev/null +++ b/packages/trpc/server/createNextApiHandler.ts @@ -0,0 +1,74 @@ +import { z } from "zod"; +import type { AnyRouter } from "@trpc/server"; +import * as trpcNext from "@calcom/trpc/server/adapters/next"; +import { createContext as createTrpcContext } from "@calcom/trpc/server/createContext"; + +/** + * Creates an API handler executed by Next.js. + */ +export function createNextApiHandler(router: AnyRouter, isPublic: boolean = false) { + return trpcNext.createNextApiHandler({ + router, + /** + * @link https://trpc.io/docs/context + */ + createContext: ({ req, res }) => { + return createTrpcContext({ req, res }); + }, + /** + * @link https://trpc.io/docs/error-handling + */ + onError({ error }) { + if (error.code === "INTERNAL_SERVER_ERROR") { + // send to bug reporting + console.error("Something went wrong", error); + } + }, + /** + * Enable query batching + */ + batching: { + enabled: true, + }, + /** + * @link https://trpc.io/docs/caching#api-response-caching + */ + responseMeta({ ctx, paths, type, errors }) { + const allOk = errors.length === 0; + const isQuery = type === "query"; + const noHeaders = {}; + + // We cannot set headers on SSG queries + if (!ctx?.res) return noHeaders; + + const defaultHeaders: Record<"headers", Record> = { + headers: {}, + }; + + const timezone = z.string().safeParse(ctx.req?.headers["x-vercel-ip-timezone"]); + if (timezone.success) defaultHeaders.headers["x-cal-timezone"] = timezone.data; + + // We need all these conditions to be true to set cache headers + if (!(isPublic && allOk && isQuery)) return defaultHeaders; + + // No cache by default + defaultHeaders.headers["cache-control"] = `no-cache`; + + if (isPublic && paths) { + const ONE_DAY_IN_SECONDS = 60 * 60 * 24; + const cacheRules = { + "session": `no-cache`, + "i18n": `no-cache`, + // Revalidation time here should be 1 second, per https://github.com/calcom/cal.com/pull/6823#issuecomment-1423215321 + "slots.getSchedule": `no-cache`, // FIXME + "cityTimezones": `max-age=${ONE_DAY_IN_SECONDS}, stale-while-revalidate`, + } as const; + + const matchedPath = paths.find((v) => v in cacheRules) as keyof typeof cacheRules; + if (matchedPath) defaultHeaders.headers["cache-control"] = cacheRules[matchedPath]; + } + + return defaultHeaders; + }, + }); +}; diff --git a/playwright.config.ts b/playwright.config.ts index 77f818ad84..3332efae4b 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -102,11 +102,17 @@ const config: PlaywrightTestConfig = { name: "@calcom/embed-core", testDir: "./packages/embeds/embed-core/", testMatch: /.*\.(e2e|test)\.tsx?/, + expect: { + timeout: DEFAULT_EXPECT_TIMEOUT, + }, use: { ...devices["Desktop Chrome"], baseURL: "http://localhost:3100/" }, }, { name: "@calcom/embed-react", testDir: "./packages/embeds/embed-react/", + expect: { + timeout: DEFAULT_EXPECT_TIMEOUT, + }, testMatch: /.*\.(e2e|test)\.tsx?/, use: { ...devices["Desktop Chrome"], baseURL: "http://localhost:3101/" }, }, @@ -114,12 +120,18 @@ const config: PlaywrightTestConfig = { name: "@calcom/embed-core--firefox", testDir: "./packages/embeds/", testMatch: /.*\.e2e\.tsx?/, + expect: { + timeout: DEFAULT_EXPECT_TIMEOUT, + }, use: { ...devices["Desktop Firefox"] }, }, { name: "@calcom/embed-core--webkit", testDir: "./packages/embeds/", testMatch: /.*\.e2e\.tsx?/, + expect: { + timeout: DEFAULT_EXPECT_TIMEOUT, + }, use: { ...devices["Desktop Safari"] }, }, ], From ab50067194bc7c41215b5fe90fb5e1c8d166236f Mon Sep 17 00:00:00 2001 From: Jeroen Reumkens Date: Fri, 5 May 2023 17:42:47 +0100 Subject: [PATCH 04/34] =?UTF-8?q?Set=20selecteddate=20to=20today=20when=20?= =?UTF-8?q?switching=20layout.=20Before=20we=20were=20getting=20the=20curr?= =?UTF-8?q?ent=20layout=20from=20the=20store,=20which=20would=20actually?= =?UTF-8?q?=20be=20the=20OLD=20layout=20you=20were=20switching=20from=20?= =?UTF-8?q?=F0=9F=98=85=20(#8711)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/features/bookings/Booker/store.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/features/bookings/Booker/store.ts b/packages/features/bookings/Booker/store.ts index 359d84d426..ba84ced3ca 100644 --- a/packages/features/bookings/Booker/store.ts +++ b/packages/features/bookings/Booker/store.ts @@ -101,7 +101,7 @@ export const useBookerStore = create((set, get) => ({ setLayout: (layout: BookerLayout) => { // If we switch to a large layout and don't have a date selected yet, // we selected it here, so week title is rendered properly. - if (["large_calendar", "large_timeslots"].includes(get().layout) && !get().selectedDate) { + if (["large_calendar", "large_timeslots"].includes(layout) && !get().selectedDate) { set({ selectedDate: dayjs().format("YYYY-MM-DD") }); } return set({ layout }); From d7051c4a3ff6870cd15ddfe8e5a3ae7730a5aded Mon Sep 17 00:00:00 2001 From: Jeroen Reumkens Date: Fri, 5 May 2023 21:39:00 +0100 Subject: [PATCH 05/34] Centered small_calendar view vertically (#8712) --- apps/web/pages/new-booker/[user]/[type].tsx | 2 +- apps/web/pages/new-booker/team/[slug]/[type].tsx | 2 +- packages/features/bookings/Booker/Booker.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/pages/new-booker/[user]/[type].tsx b/apps/web/pages/new-booker/[user]/[type].tsx index 6a6db3e0e8..5151f13f50 100644 --- a/apps/web/pages/new-booker/[user]/[type].tsx +++ b/apps/web/pages/new-booker/[user]/[type].tsx @@ -15,7 +15,7 @@ type PageProps = inferSSRProps; export default function Type({ slug, user, booking, away }: PageProps) { return ( -
+
); diff --git a/apps/web/pages/new-booker/team/[slug]/[type].tsx b/apps/web/pages/new-booker/team/[slug]/[type].tsx index 46a91e6890..6267178e9c 100644 --- a/apps/web/pages/new-booker/team/[slug]/[type].tsx +++ b/apps/web/pages/new-booker/team/[slug]/[type].tsx @@ -14,7 +14,7 @@ type PageProps = inferSSRProps; export default function Type({ slug, user, booking, away }: PageProps) { return ( -
+
); diff --git a/packages/features/bookings/Booker/Booker.tsx b/packages/features/bookings/Booker/Booker.tsx index aec279eaf4..684b6f447e 100644 --- a/packages/features/bookings/Booker/Booker.tsx +++ b/packages/features/bookings/Booker/Booker.tsx @@ -135,7 +135,7 @@ const BookerComponent = ({ username, eventSlug, month, rescheduleBooking }: Book // Sizes for fullscreen layouts: layout !== "small_calendar" && "[--booker-meta-width:340px] lg:[--booker-meta-width:424px]", // Other styles - layout === "small_calendar" && "border-subtle mt-20 min-h-[450px] rounded-md border", + layout === "small_calendar" && "border-subtle min-h-[450px] rounded-md border", layout !== "small_calendar" && "h-auto min-h-screen w-screen", "transition-[width,max-width] duration-300" )}> From b9ade55ac136db3307008039cd18029f72960a36 Mon Sep 17 00:00:00 2001 From: Satyam Singh <97077594+Satyxm@users.noreply.github.com> Date: Sat, 6 May 2023 02:11:47 +0530 Subject: [PATCH 06/34] Update README.md (#8679) Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Hariom Balhara Co-authored-by: Peer Richelsen --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3ef368a738..7564e700e7 100644 --- a/README.md +++ b/README.md @@ -500,7 +500,7 @@ following ## License -Distributed under the AGPLv3 License. See `LICENSE` for more information. +Distributed under the [AGPLv3 License](https://github.com/calcom/cal.com/blob/main/LICENSE). See `LICENSE` for more information. From 5d2fd7aafb27739a3fa3683d3c067f05a9a32de8 Mon Sep 17 00:00:00 2001 From: Soham Datta <78294692+sohamdata@users.noreply.github.com> Date: Sat, 6 May 2023 02:13:40 +0530 Subject: [PATCH 07/34] [Symbolic Link Issues] Added instructions for setup on windows (#8574) Co-authored-by: Hariom Balhara Co-authored-by: Keith Williams Co-authored-by: Peer Richelsen Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 7564e700e7..6ded34330f 100644 --- a/README.md +++ b/README.md @@ -121,6 +121,9 @@ Here is what you need to be able to run Cal.com. ```sh git clone https://github.com/calcom/cal.com.git ``` + > If you are on windows, run the following command on `gitbash` with admin privileges:
+ ```git clone -c core.symlinks=true https://github.com/calcom/cal.com.git```
+ See [docs](https://cal.com/docs/how-to-guides/how-to-troubleshoot-symbolic-link-issues-on-windows#enable-symbolic-links) for more details. 1. Go to the project folder From 7611d5c24e77579dab1515d7744721e9d7a3d5d2 Mon Sep 17 00:00:00 2001 From: Jeroen Reumkens Date: Fri, 5 May 2023 21:44:21 +0100 Subject: [PATCH 08/34] CAL-1555: Show correct team avatars (#8665) --- .../components/event-meta/Members.tsx | 56 ++++++++++++++----- .../features/eventtypes/lib/getPublicEvent.ts | 3 +- 2 files changed, 43 insertions(+), 16 deletions(-) diff --git a/packages/features/bookings/components/event-meta/Members.tsx b/packages/features/bookings/components/event-meta/Members.tsx index df4773e927..3076270f6d 100644 --- a/packages/features/bookings/components/event-meta/Members.tsx +++ b/packages/features/bookings/components/event-meta/Members.tsx @@ -1,4 +1,4 @@ -import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants"; +import { CAL_URL } from "@calcom/lib/constants"; import { SchedulingType } from "@calcom/prisma/enums"; import { AvatarGroup } from "@calcom/ui"; @@ -14,28 +14,54 @@ export interface EventMembersProps { profile: PublicEvent["profile"]; } +type Avatar = { + title: string; + image: string | undefined; + alt: string | undefined; + href: string | undefined; +}; + +type AvatarWithRequiredImage = Avatar & { image: string }; + export const EventMembers = ({ schedulingType, users, profile }: EventMembersProps) => { const showMembers = schedulingType !== SchedulingType.ROUND_ROBIN; - const shownUsers = showMembers ? [...users, profile] : [profile]; + const shownUsers = showMembers ? users : []; - const avatars = shownUsers - .map((user) => ({ - title: `${user.name}`, - image: "image" in user ? `${user.image}` : `${WEBAPP_URL}/${user.username}/avatar.png`, - alt: user.name || undefined, - href: user.username ? `${CAL_URL}/${user.username}` : undefined, - })) - .filter((item) => !!item.image) + // In some cases we don't show the user's names, but only show the profile name. + const showOnlyProfileName = + (profile.name && schedulingType === SchedulingType.ROUND_ROBIN) || + !users.length || + (profile.name !== users[0].name && schedulingType === SchedulingType.COLLECTIVE); + + const avatars: Avatar[] = shownUsers.map((user) => ({ + title: `${user.name}`, + image: "image" in user ? `${user.image}` : `${CAL_URL}/${user.username}/avatar.png`, + alt: user.name || undefined, + href: user.username ? `${CAL_URL}/${user.username}` : undefined, + })); + + // Add profile later since we don't want to force creating an avatar for this if it doesn't exist. + avatars.unshift({ + title: `${profile.name}`, + image: "logo" in profile && profile.logo ? `${profile.logo}` : undefined, + alt: profile.name || undefined, + href: profile.username ? `${CAL_URL}/${profile.username}` : undefined, + }); + + const uniqueAvatars = avatars + .filter((item): item is AvatarWithRequiredImage => !!item.image) .filter((item, index, self) => self.findIndex((t) => t.image === item.image) === index); return ( <> - +

- {users - .map((user) => user.name) - .filter((name) => name) - .join(", ")} + {showOnlyProfileName + ? profile.name + : shownUsers + .map((user) => user.name) + .filter((name) => name) + .join(", ")}

); diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index a29b4e41b1..e232aa8c0a 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -174,7 +174,8 @@ function getProfileFromEvent(event: Event) { username, name: profile.name, weekStart, - image: `${WEBAPP_URL}${basePath}/avatar.png`, + image: team ? undefined : `${WEBAPP_URL}${basePath}/avatar.png`, + logo: !team ? undefined : team.logo, brandColor: profile.brandColor, darkBrandColor: profile.darkBrandColor, }; From bc38b3bc551bd961bb8a99267046ebc3624ea197 Mon Sep 17 00:00:00 2001 From: "sumit shinde ( Roni )" <110285294+sumitshinde-84@users.noreply.github.com> Date: Sat, 6 May 2023 03:08:19 +0530 Subject: [PATCH 09/34] enhance Timezone Component with Box Shadow (#8582) Co-authored-by: Udit Takkar Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Co-authored-by: Peer Richelsen --- packages/ui/components/form/timezone-select/TimezoneSelect.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/ui/components/form/timezone-select/TimezoneSelect.tsx b/packages/ui/components/form/timezone-select/TimezoneSelect.tsx index 5977a7c6bc..fc29857166 100644 --- a/packages/ui/components/form/timezone-select/TimezoneSelect.tsx +++ b/packages/ui/components/form/timezone-select/TimezoneSelect.tsx @@ -87,6 +87,7 @@ export function TimezoneSelect({ menu: (state) => classNames( "rounded-md bg-default text-sm leading-4 text-default mt-1 border border-subtle", + state.selectProps.menuIsOpen && "shadow-dropdown", // Add box-shadow when menu is open timezoneClassNames?.menu && timezoneClassNames.menu(state) ), groupHeading: () => "leading-none text-xs uppercase text-default pl-2.5 pt-4 pb-2", From a2e2b0ed34ebaa42dc738bf72b18c08e85017955 Mon Sep 17 00:00:00 2001 From: Hariom Balhara Date: Sat, 6 May 2023 13:20:52 +0530 Subject: [PATCH 10/34] Fix broken appsRouter (#8719) --- apps/web/pages/api/trpc/appsRouter/[trpc].ts | 4 ++++ packages/trpc/react/trpc.ts | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 apps/web/pages/api/trpc/appsRouter/[trpc].ts diff --git a/apps/web/pages/api/trpc/appsRouter/[trpc].ts b/apps/web/pages/api/trpc/appsRouter/[trpc].ts new file mode 100644 index 0000000000..e7e03ae67f --- /dev/null +++ b/apps/web/pages/api/trpc/appsRouter/[trpc].ts @@ -0,0 +1,4 @@ +import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler"; +import { appsRouter } from "@calcom/trpc/server/routers/viewer/apps/_router"; + +export default createNextApiHandler(appsRouter); diff --git a/packages/trpc/react/trpc.ts b/packages/trpc/react/trpc.ts index aa1d08b191..01339a319d 100644 --- a/packages/trpc/react/trpc.ts +++ b/packages/trpc/react/trpc.ts @@ -37,6 +37,7 @@ const ENDPOINTS = [ "viewer", "webhook", "workflows", + "appsRouter", ] as const; export type Endpoint = (typeof ENDPOINTS)[number]; @@ -51,15 +52,14 @@ const resolveEndpoint = (links: any) => { return (ctx: any) => { const parts = ctx.op.path.split("."); let endpoint; - let path = ''; + let path = ""; if (parts.length == 2) { endpoint = parts[0] as keyof typeof links; path = parts[1]; } else { endpoint = parts[1] as keyof typeof links; - path = parts.splice(2, parts.length - 2).join('.'); + path = parts.splice(2, parts.length - 2).join("."); } - return links[endpoint]({ ...ctx, op: { ...ctx.op, path } }); }; }; From c6ab889b982b1d6bb37ad1b2c3d8ab9c2d1fe51f Mon Sep 17 00:00:00 2001 From: Manish Roy <34767051+ManVTish@users.noreply.github.com> Date: Sat, 6 May 2023 13:22:54 +0530 Subject: [PATCH 11/34] handles scenario where defaultschedule is nil (#8714) --- packages/trpc/server/routers/viewer/availability/util.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/trpc/server/routers/viewer/availability/util.ts b/packages/trpc/server/routers/viewer/availability/util.ts index 94b3621274..74ce058b82 100644 --- a/packages/trpc/server/routers/viewer/availability/util.ts +++ b/packages/trpc/server/routers/viewer/availability/util.ts @@ -27,7 +27,12 @@ export const getDefaultScheduleId = async (userId: number, prisma: PrismaClient) }, }); - return defaultSchedule?.id; // TODO: Handle no schedules AT ALL + if (!defaultSchedule) { + // Handle case where defaultSchedule is null by throwing an error + throw new Error("No schedules found for user"); + } + + return defaultSchedule.id; }; export const hasDefaultSchedule = async (user: Partial, prisma: PrismaClient) => { From 3c46c391143a52e5d97e0574f4f42ee190cf22b0 Mon Sep 17 00:00:00 2001 From: "Jon@1599" <55296387+Jonathan1599@users.noreply.github.com> Date: Sun, 7 May 2023 03:38:04 +0530 Subject: [PATCH 12/34] feat: catch emails sent locally using Mailhog (#8470) * feat: added mailhog to catch emails sent locally * fix: added new line at the end of packages/emails/docker-compose.yml * chore: removed EMAIL_SERVER_USER and EMAIL_SERVER_PASSWORD for local usage with Mailhog --------- Co-authored-by: Keith Williams Co-authored-by: Peer Richelsen --- .env.example | 12 ++++++++---- README.md | 6 ++++++ packages/emails/docker-compose.yml | 8 ++++++++ packages/emails/package.json | 3 +++ 4 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 packages/emails/docker-compose.yml diff --git a/.env.example b/.env.example index 0a75c7390a..d1023ca7c0 100644 --- a/.env.example +++ b/.env.example @@ -138,12 +138,16 @@ API_KEY_PREFIX=cal_ EMAIL_FROM='notifications@yourselfhostedcal.com' # Configure SMTP settings (@see https://nodemailer.com/smtp/). +# Configuration to receive emails locally (mailhog) +EMAIL_SERVER_HOST='localhost' +EMAIL_SERVER_PORT=1025 + # Note: The below configuration for Office 365 has been verified to work. -EMAIL_SERVER_HOST='smtp.office365.com' -EMAIL_SERVER_PORT=587 -EMAIL_SERVER_USER='' +# EMAIL_SERVER_HOST='smtp.office365.com' +# EMAIL_SERVER_PORT=587 +# EMAIL_SERVER_USER='' # Keep in mind that if you have 2FA enabled, you will need to provision an App Password. -EMAIL_SERVER_PASSWORD='' +# EMAIL_SERVER_PASSWORD='' # The following configuration for Gmail has been verified to work. # EMAIL_SERVER_HOST='smtp.gmail.com' diff --git a/README.md b/README.md index 6ded34330f..dd0fd9a1b5 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,12 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env ```sh yarn workspace @calcom/prisma db-deploy ``` +1. Run [mailhog](https://github.com/mailhog/MailHog) to view emails sent during development + + ```sh + docker pull mailhog/mailhog + docker run -d -p 8025:8025 -p 1025:1025 mailhog/mailhog + ``` 1. Run (in development mode) diff --git a/packages/emails/docker-compose.yml b/packages/emails/docker-compose.yml new file mode 100644 index 0000000000..4eaf50158d --- /dev/null +++ b/packages/emails/docker-compose.yml @@ -0,0 +1,8 @@ +# Starts mailhog SMTP server on port 1025, web interface on port 8025 +version: "3.6" +services: + mailhog: + image: 'mailhog/mailhog:latest' + ports: + - "1025:1025" + - "8025:8025" diff --git a/packages/emails/package.json b/packages/emails/package.json index 9454e7db25..786d6ab25e 100644 --- a/packages/emails/package.json +++ b/packages/emails/package.json @@ -3,6 +3,9 @@ "sideEffects": false, "version": "0.0.0", "private": true, + "scripts": { + "dx": "docker compose up -d" + }, "dependencies": { "@calcom/dayjs": "*", "@calcom/lib": "*", From 2907405d4e3f15feffebe1c1358b23819a4553ed Mon Sep 17 00:00:00 2001 From: Zoheb Ahmed <75200954+oneknucklehead@users.noreply.github.com> Date: Mon, 8 May 2023 17:41:25 +0530 Subject: [PATCH 13/34] Fixes Bold/Italics Issue (#8613) Co-authored-by: Alex van Andel --- packages/ui/components/editor/plugins/ToolbarPlugin.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/ui/components/editor/plugins/ToolbarPlugin.tsx b/packages/ui/components/editor/plugins/ToolbarPlugin.tsx index adb30ae029..5e254d32a1 100644 --- a/packages/ui/components/editor/plugins/ToolbarPlugin.tsx +++ b/packages/ui/components/editor/plugins/ToolbarPlugin.tsx @@ -459,9 +459,6 @@ export default function ToolbarPlugin(props: TextEditorProps) { StartIcon={Bold} onClick={() => { editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold"); - if (isItalic) { - editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic"); - } }} className={isBold ? "bg-subtle" : ""} /> @@ -474,9 +471,6 @@ export default function ToolbarPlugin(props: TextEditorProps) { StartIcon={Italic} onClick={() => { editor.dispatchCommand(FORMAT_TEXT_COMMAND, "italic"); - if (isItalic) { - editor.dispatchCommand(FORMAT_TEXT_COMMAND, "bold"); - } }} className={isItalic ? "bg-subtle" : ""} /> From 5362cd66b4ab23392f1ad5ce5d5eca84aad0ab2a Mon Sep 17 00:00:00 2001 From: Peer Richelsen Date: Mon, 8 May 2023 20:03:24 +0100 Subject: [PATCH 14/34] added gradients to dark dashboard and dark booking page (#8459) * added gradients to dark dashboard and dark booking page * removed bg of timezone dropdown for dak * added gradient to confirmation screen * only gradient in dashboard * fix: prettier Signed-off-by: Udit Takkar --------- Signed-off-by: Udit Takkar Co-authored-by: Udit Takkar --- apps/web/components/booking/TimezoneDropdown.tsx | 2 +- apps/web/components/booking/pages/BookingPage.tsx | 2 +- packages/features/shell/Shell.tsx | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/web/components/booking/TimezoneDropdown.tsx b/apps/web/components/booking/TimezoneDropdown.tsx index 8766c3530d..239ca6c673 100644 --- a/apps/web/components/booking/TimezoneDropdown.tsx +++ b/apps/web/components/booking/TimezoneDropdown.tsx @@ -17,7 +17,7 @@ export function TimezoneDropdown({ return ( <> -
+
diff --git a/apps/web/components/booking/pages/BookingPage.tsx b/apps/web/components/booking/pages/BookingPage.tsx index da214f7b85..98a390f8a4 100644 --- a/apps/web/components/booking/pages/BookingPage.tsx +++ b/apps/web/components/booking/pages/BookingPage.tsx @@ -550,7 +550,7 @@ const BookingPage = ({ )}>
{showEventTypeDetails && ( -
+
{!rescheduleUid && eventType.recurringEvent?.freq && recurringEventCount && ( diff --git a/packages/features/shell/Shell.tsx b/packages/features/shell/Shell.tsx index 7fc489a248..c9e1080bb5 100644 --- a/packages/features/shell/Shell.tsx +++ b/packages/features/shell/Shell.tsx @@ -299,19 +299,19 @@ function UserDropdown({ small }: { small?: boolean }) { /> } {!user.away && ( -
+
)} {user.away && ( -
+
)} {!small && ( - + {user.name || "Nameless User"} - + {user.username ? process.env.NEXT_PUBLIC_WEBSITE_URL === "https://cal.com" ? `cal.com/${user.username}` @@ -746,7 +746,7 @@ function SideBarContainer() { function SideBar() { return (
-