-
{t("setting_up_zapier")}
+
How to integrate a Typeform with Routing Form
{!newApiKey ? (
<>
{t("generate_api_key")}:
diff --git a/packages/app-store/zoomvideo/_metadata.ts b/packages/app-store/zoomvideo/_metadata.ts
index 21fd806153..2bf28fa617 100644
--- a/packages/app-store/zoomvideo/_metadata.ts
+++ b/packages/app-store/zoomvideo/_metadata.ts
@@ -1,9 +1,9 @@
-import type { App } from "@calcom/types/App";
+import type { AppMeta } from "@calcom/types/App";
-import { LocationType } from "../locations";
import _package from "./package.json";
export const metadata = {
+ linkType: "dynamic",
name: "Zoom Video",
description: _package.description,
type: "zoom_video",
@@ -20,8 +20,14 @@ export const metadata = {
title: "Zoom Video",
trending: true,
email: "help@cal.com",
- locationType: LocationType.Zoom,
- locationLabel: "Zoom Video",
-} as App;
+ appData: {
+ location: {
+ default: false,
+ linkType: "dynamic",
+ type: "integrations:zoom",
+ label: "Zoom Video",
+ },
+ },
+} as AppMeta;
export default metadata;
diff --git a/packages/core/EventManager.ts b/packages/core/EventManager.ts
index f2173eb4e0..b1f4a2dc18 100644
--- a/packages/core/EventManager.ts
+++ b/packages/core/EventManager.ts
@@ -4,6 +4,7 @@ import merge from "lodash/merge";
import { v5 as uuidv5 } from "uuid";
import { FAKE_DAILY_CREDENTIAL } from "@calcom/app-store/dailyvideo/lib/VideoApiAdapter";
+import { getEventLocationTypeFromApp } from "@calcom/app-store/locations";
import getApps from "@calcom/app-store/utils";
import prisma from "@calcom/prisma";
import type { AdditionalInformation, CalendarEvent, NewCalendarEventType } from "@calcom/types/Calendar";
@@ -16,55 +17,15 @@ import type {
} from "@calcom/types/EventManager";
import { createEvent, updateEvent } from "./CalendarManager";
-import { LocationType } from "./location";
import { createMeeting, updateMeeting } from "./videoClient";
-export const isZoom = (location: string): boolean => {
- return location === "integrations:zoom";
-};
-
-export const isDaily = (location: string): boolean => {
- return location === "integrations:daily";
-};
-
-export const isHuddle01 = (location: string): boolean => {
- return location === "integrations:huddle01";
-};
-
-export const isTandem = (location: string): boolean => {
- return location === "integrations:tandem";
-};
-
-export const isTeams = (location: string): boolean => {
- return location === "integrations:office365_video";
-};
-
-export const isJitsi = (location: string): boolean => {
- return location === "integrations:jitsi";
-};
-
export const isDedicatedIntegration = (location: string): boolean => {
- return (
- isZoom(location) ||
- isDaily(location) ||
- isHuddle01(location) ||
- isTandem(location) ||
- isJitsi(location) ||
- isTeams(location)
- );
+ return location !== "integrations:google:meet" && location.includes("integrations:");
};
export const getLocationRequestFromIntegration = (location: string) => {
- if (
- /** TODO: Handle this dynamically */
- location === LocationType.GoogleMeet.valueOf() ||
- location === LocationType.Zoom.valueOf() ||
- location === LocationType.Daily.valueOf() ||
- location === LocationType.Jitsi.valueOf() ||
- location === LocationType.Huddle01.valueOf() ||
- location === LocationType.Tandem.valueOf() ||
- location === LocationType.Teams.valueOf()
- ) {
+ const eventLocationType = getEventLocationTypeFromApp(location);
+ if (eventLocationType) {
const requestId = uuidv5(location, uuidv5.URL);
return {
@@ -84,6 +45,8 @@ export const processLocation = (event: CalendarEvent): CalendarEvent => {
// If location is set to an integration location
// Build proper transforms for evt object
// Extend evt object with those transformations
+
+ // TODO: Rely on linkType:"dynamic" here. static links don't send their type. They send their URL directly.
if (event.location?.includes("integration")) {
const maybeLocationRequestObject = getLocationRequestFromIntegration(event.location);
diff --git a/apps/web/lib/event.ts b/packages/core/event.ts
similarity index 57%
rename from apps/web/lib/event.ts
rename to packages/core/event.ts
index 5d7865fe94..355ecf500d 100644
--- a/apps/web/lib/event.ts
+++ b/packages/core/event.ts
@@ -1,5 +1,7 @@
import { TFunction } from "next-i18next";
+import { guessEventLocationType } from "@calcom/app-store/locations";
+
type EventNameObjectType = {
attendeeName: string;
eventType: string;
@@ -18,35 +20,12 @@ export function getEventName(eventNameObj: EventNameObjectType, forAttendeeView
});
let eventName = eventNameObj.eventName;
- let locationString = "";
+ let locationString = eventNameObj.location || "";
if (eventNameObj.eventName.includes("{LOCATION}")) {
- switch (eventNameObj.location) {
- case "inPerson":
- locationString = "In Person";
- break;
- case "userPhone":
- case "phone":
- locationString = "Phone";
- break;
- case "integrations:daily":
- locationString = "Cal Video";
- break;
- case "integrations:zoom":
- locationString = "Zoom";
- break;
- case "integrations:huddle01":
- locationString = "Huddle01";
- break;
- case "integrations:tandem":
- locationString = "Tandem";
- break;
- case "integrations:office365_video":
- locationString = "MS Teams";
- break;
- case "integrations:jitsi":
- locationString = "Jitsi";
- break;
+ const eventLocationType = guessEventLocationType(eventNameObj.location);
+ if (eventLocationType) {
+ locationString = eventLocationType.label;
}
eventName = eventName.replace("{LOCATION}", locationString);
}
diff --git a/packages/emails/src/components/LocationInfo.tsx b/packages/emails/src/components/LocationInfo.tsx
index 2c38e10751..4a85f127c0 100644
--- a/packages/emails/src/components/LocationInfo.tsx
+++ b/packages/emails/src/components/LocationInfo.tsx
@@ -1,7 +1,8 @@
import type { TFunction } from "next-i18next";
-import { getAppName } from "@calcom/app-store/utils";
-import { getVideoCallPassword, getVideoCallUrl, getProviderName } from "@calcom/lib/CalEventParser";
+import { guessEventLocationType } from "@calcom/app-store/locations";
+import { getVideoCallPassword, getVideoCallUrl } from "@calcom/lib/CalEventParser";
+import logger from "@calcom/lib/logger";
import type { CalendarEvent } from "@calcom/types/Calendar";
import { Info } from "./Info";
@@ -9,89 +10,74 @@ import { LinkIcon } from "./LinkIcon";
export function LocationInfo(props: { calEvent: CalendarEvent; t: TFunction }) {
const { t } = props;
- const providerName =
- (props.calEvent.location && getAppName(props.calEvent.location)) || getProviderName(props.calEvent);
- if (props.calEvent.videoCallData) {
- const meetingId = props.calEvent.videoCallData.id;
- const meetingPassword = getVideoCallPassword(props.calEvent);
- const meetingUrl = getVideoCallUrl(props.calEvent);
+ // We would not be able to determine provider name for DefaultEventLocationTypes
+ const providerName = guessEventLocationType(props.calEvent.location)?.label;
+ logger.debug(`LocationInfo: ${JSON.stringify(props.calEvent)} ${providerName}`);
- return (
-
- {providerName}
-
- ) : (
- <>{t("something_went_wrong")}>
- )
- }
- extraInfo={
- <>
- {meetingId && (
-
- <>
- {t("meeting_id")}: {meetingId}
- >
-
- )}
- {meetingPassword && (
-
- <>
- {t("meeting_password")}: {meetingPassword}
- >
-
- )}
- {meetingUrl && (
-
- )}
- >
- }
- />
- );
+ const location = props.calEvent.location;
+ let meetingUrl = location?.search(/^https?:/) !== -1 ? location : undefined;
+
+ if (props.calEvent) {
+ meetingUrl = getVideoCallUrl(props.calEvent) || meetingUrl;
}
- if (props.calEvent.additionalInformation?.hangoutLink) {
- const hangoutLink: string = props.calEvent.additionalInformation.hangoutLink;
+ const isPhone = location?.startsWith("+");
+ // Because of location being a value here, we can determine the app that generated the location only for Dynamic Link based apps where the value is integrations:*
+ // For static link based location apps, the value is that URL itself. So, it is not straightforward to determine the app that generated the location.
+ // If we know the App we can always provide the name of the app like we do it for Google Hangout/Google Meet
+
+ if (meetingUrl) {
return (
- Google
+ {providerName || "Link"}
+
+ }
+ extraInfo={
+ meetingUrl && (
+
+ )
+ }
+ />
+ );
+ }
+
+ if (isPhone) {
+ return (
+
+ {location}
}
/>
);
}
+
return (
diff --git a/packages/features/ee/payments/components/PaymentPage.tsx b/packages/features/ee/payments/components/PaymentPage.tsx
index 0f179fc557..6c997063ea 100644
--- a/packages/features/ee/payments/components/PaymentPage.tsx
+++ b/packages/features/ee/payments/components/PaymentPage.tsx
@@ -4,7 +4,7 @@ import Head from "next/head";
import { FC, useEffect, useState } from "react";
import { FormattedNumber, IntlProvider } from "react-intl";
-import { LocationOptionsToString } from "@calcom/app-store/locations";
+import { getSuccessPageLocationMessage } from "@calcom/app-store/locations";
import getStripe from "@calcom/app-store/stripepayment/lib/client";
import dayjs from "@calcom/dayjs";
import { sdkActionManager, useIsEmbed } from "@calcom/embed-core/embed-iframe";
@@ -103,7 +103,7 @@ const PaymentPage: FC = (props) => {
<>
{t("where")}
- {LocationOptionsToString(props.booking.location, t)}
+ {getSuccessPageLocationMessage(props.booking.location, t)}
>
)}
diff --git a/packages/features/ee/workflows/lib/reminders/templates/customTemplate.ts b/packages/features/ee/workflows/lib/reminders/templates/customTemplate.ts
index 8a73466d74..e450c8b8f8 100644
--- a/packages/features/ee/workflows/lib/reminders/templates/customTemplate.ts
+++ b/packages/features/ee/workflows/lib/reminders/templates/customTemplate.ts
@@ -1,3 +1,4 @@
+import { guessEventLocationType } from "@calcom/app-store/locations";
import { Dayjs } from "@calcom/dayjs";
import { Prisma } from "@calcom/prisma/client";
@@ -18,38 +19,7 @@ const customTemplate = async (text: string, variables: VariablesType, locale: st
let locationString = variables.location || "";
if (text.includes("{LOCATION}")) {
- switch (variables.location) {
- case "integrations:google:meet":
- locationString = "Google Meet";
- break;
- case "integrations:daily":
- locationString = "Cal Video";
- break;
- case "integrations:zoom":
- locationString = "Zoom";
- break;
- case "integrations:huddle01":
- locationString = "Huddle01";
- break;
- case "integrations:tandem":
- locationString = "Tandem";
- break;
- case "integrations:office365_video":
- locationString = "MS Teams";
- break;
- case "integrations:jitsi":
- locationString = "Jitsi";
- break;
- case "integrations:whereby_video":
- locationString = "Whereby";
- break;
- case "integrations:around_video":
- locationString = "Around";
- break;
- case "integrations:riverside_video":
- locationString = "Riverside";
- break;
- }
+ locationString = guessEventLocationType(locationString)?.label || "";
}
let dynamicText = text
diff --git a/packages/lib/defaultEvents.ts b/packages/lib/defaultEvents.ts
index 5a4779d10b..66741cd0e2 100644
--- a/packages/lib/defaultEvents.ts
+++ b/packages/lib/defaultEvents.ts
@@ -1,6 +1,7 @@
import type { EventTypeCustomInput } from "@prisma/client";
import { PeriodType, Prisma, SchedulingType, UserPlan } from "@prisma/client";
+import { DailyLocationType } from "@calcom/app-store/locations";
import { userSelect } from "@calcom/prisma/selects";
type User = Prisma.UserGetPayload;
@@ -59,7 +60,7 @@ const commons = {
periodType: PeriodType.UNLIMITED,
periodDays: null,
slotInterval: null,
- locations: [{ type: "integrations:daily" }],
+ locations: [{ type: DailyLocationType }],
customInputs,
disableGuests: true,
minimumBookingNotice: 120,
diff --git a/packages/lib/server/defaultResponder.ts b/packages/lib/server/defaultResponder.ts
index 8e5584edda..4a24bc350b 100644
--- a/packages/lib/server/defaultResponder.ts
+++ b/packages/lib/server/defaultResponder.ts
@@ -15,6 +15,7 @@ function defaultResponder(f: Handle) {
ok = true;
if (result) res.json(result);
} catch (err) {
+ console.error(err);
const error = getServerErrorFromUnknown(err);
res.statusCode = error.statusCode;
res.json({ message: error.message });
diff --git a/packages/prisma/seed-app-store.config.json b/packages/prisma/seed-app-store.config.json
index c939802bbc..3f4bd7c013 100644
--- a/packages/prisma/seed-app-store.config.json
+++ b/packages/prisma/seed-app-store.config.json
@@ -2,38 +2,50 @@
{
"/*": "This file is auto-generated and updated by `yarn app-store create/edit`. Don't edit it manually",
"dirName": "routing_forms",
- "categories": ["other"],
+ "categories": [
+ "other"
+ ],
"slug": "routing_forms",
"type": "routing_forms_other"
},
{
"dirName": "whereby",
- "categories": ["video"],
+ "categories": [
+ "video"
+ ],
"slug": "whereby",
"type": "whereby_video"
},
{
"dirName": "around",
- "categories": ["video"],
+ "categories": [
+ "video"
+ ],
"slug": "around",
"type": "around_video"
},
{
"dirName": "riverside",
- "categories": ["video"],
+ "categories": [
+ "video"
+ ],
"slug": "riverside",
"type": "riverside_video"
},
{
"dirName": "typeform",
- "categories": ["other"],
+ "categories": [
+ "other"
+ ],
"slug": "typeform",
"type": "typeform_other"
},
{
"dirName": "ping",
- "categories": ["video"],
+ "categories": [
+ "video"
+ ],
"slug": "ping",
"type": "ping_video"
}
-]
+]
\ No newline at end of file
diff --git a/packages/prisma/seed.ts b/packages/prisma/seed.ts
index 098b7a5a5b..4808d08a36 100644
--- a/packages/prisma/seed.ts
+++ b/packages/prisma/seed.ts
@@ -1,6 +1,9 @@
import { BookingStatus, MembershipRole, Prisma, UserPermissionRole, UserPlan } from "@prisma/client";
import { uuid } from "short-uuid";
+import dailyMeta from "@calcom/app-store/dailyvideo/_metadata";
+import googleMeetMeta from "@calcom/app-store/googlevideo/_metadata";
+import zoomMeta from "@calcom/app-store/zoomvideo/_metadata";
import dayjs from "@calcom/dayjs";
import { hashPassword } from "@calcom/lib/auth";
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
@@ -263,19 +266,19 @@ async function main() {
title: "Zoom Event",
slug: "zoom",
length: 60,
- locations: [{ type: "integrations:zoom" }],
+ locations: [{ type: zoomMeta.appData?.location.type }],
},
{
title: "Daily Event",
slug: "daily",
length: 60,
- locations: [{ type: "integrations:daily" }],
+ locations: [{ type: dailyMeta.appData?.location.type }],
},
{
title: "Google Meet",
slug: "google-meet",
length: 60,
- locations: [{ type: "integrations:google:meet" }],
+ locations: [{ type: googleMeetMeta.appData?.location.type }],
},
{
title: "Yoga class",
diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts
index bb84f58211..57a30083fa 100644
--- a/packages/prisma/zod-utils.ts
+++ b/packages/prisma/zod-utils.ts
@@ -17,7 +17,9 @@ export enum Frequency {
export const eventTypeLocations = z.array(
z.object({
- type: z.nativeEnum(LocationType),
+ // TODO: Couldn't find a way to make it a union of types from App Store locations
+ // Creating a dynamic union by iterating over the object doesn't seem to make TS happy
+ type: z.string(),
address: z.string().optional(),
link: z.string().url().optional(),
displayLocationPublicly: z.boolean().optional(),
diff --git a/packages/trpc/server/routers/viewer.tsx b/packages/trpc/server/routers/viewer.tsx
index 27b6b15fc4..b4c644e791 100644
--- a/packages/trpc/server/routers/viewer.tsx
+++ b/packages/trpc/server/routers/viewer.tsx
@@ -8,6 +8,7 @@ import stripe, { closePayments } from "@calcom/app-store/stripepayment/lib/serve
import getApps, { getLocationOptions } from "@calcom/app-store/utils";
import { cancelScheduledJobs } from "@calcom/app-store/zapier/lib/nodeScheduler";
import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/CalendarManager";
+import { DailyLocationType } from "@calcom/core/location";
import dayjs from "@calcom/dayjs";
import { sendCancelledEmails, sendFeedbackEmail } from "@calcom/emails";
import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib";
@@ -1031,7 +1032,7 @@ const loggedInViewerRouter = createProtectedRouter()
const updatedLocations = locations.map((location: { type: string }) => {
if (location.type.includes(integrationQuery)) {
- return { type: "integrations:daily" };
+ return { type: DailyLocationType };
}
return location;
});
diff --git a/packages/trpc/server/routers/viewer/bookings.tsx b/packages/trpc/server/routers/viewer/bookings.tsx
index c47d976afa..9ebdcf7c3d 100644
--- a/packages/trpc/server/routers/viewer/bookings.tsx
+++ b/packages/trpc/server/routers/viewer/bookings.tsx
@@ -1,7 +1,7 @@
import { SchedulingType } from "@prisma/client";
import { z } from "zod";
-import { LocationType } from "@calcom/app-store/locations";
+import { DailyLocationType } from "@calcom/app-store/locations";
import EventManager from "@calcom/core/EventManager";
import dayjs from "@calcom/dayjs";
import { sendLocationChangeEmails } from "@calcom/emails";
@@ -74,7 +74,7 @@ export const bookingsRouter = createProtectedRouter()
})
.mutation("editLocation", {
input: commonBookingSchema.extend({
- newLocation: z.string().transform((val) => val || LocationType.Daily),
+ newLocation: z.string().transform((val) => val || DailyLocationType),
}),
async resolve({ ctx, input }) {
const { bookingId, newLocation: location } = input;
diff --git a/packages/trpc/server/routers/viewer/eventTypes.tsx b/packages/trpc/server/routers/viewer/eventTypes.tsx
index bca1ae3dba..0eaaa69c83 100644
--- a/packages/trpc/server/routers/viewer/eventTypes.tsx
+++ b/packages/trpc/server/routers/viewer/eventTypes.tsx
@@ -3,6 +3,7 @@ import { PrismaClientKnownRequestError } from "@prisma/client/runtime";
import { z } from "zod";
import getAppKeysFromSlug from "@calcom/app-store/_utils/getAppKeysFromSlug";
+import { DailyLocationType } from "@calcom/app-store/locations";
import { stripeDataSchema } from "@calcom/app-store/stripepayment/lib/server";
import { _DestinationCalendarModel, _EventTypeCustomInputModel, _EventTypeModel } from "@calcom/prisma/zod";
import { stringOrNumber } from "@calcom/prisma/zod-utils";
@@ -122,7 +123,7 @@ export const eventTypesRouter = createProtectedRouter()
const appKeys = await getAppKeysFromSlug("daily-video");
if (typeof appKeys.api_key === "string") {
- data.locations = [{ type: "integrations:daily" }];
+ data.locations = [{ type: DailyLocationType }];
}
if (teamId && schedulingType) {
diff --git a/packages/types/App.d.ts b/packages/types/App.d.ts
index 59b814d7ef..9daa079c3f 100644
--- a/packages/types/App.d.ts
+++ b/packages/types/App.d.ts
@@ -1,6 +1,38 @@
import type { Prisma } from "@prisma/client";
-import type { LocationType } from "@calcom/app-store/locations";
+import { Optional } from "./utils";
+
+type CommonProperties = {
+ default?: false;
+ type: string;
+ label: string;
+ messageForOrganizer?: string;
+ iconUrl?: string;
+ variable?: "locationLink";
+ defaultValueVariable?: "link";
+ attendeeInputType?: null;
+ attendeeInputPlaceholder?: null;
+};
+
+type StaticLinkBasedEventLocation = {
+ linkType: "static";
+ urlRegExp: string;
+ organizerInputPlaceholder?: string;
+ organizerInputType?: "text" | "phone";
+} & CommonProperties;
+
+type DynamicLinkBasedEventLocation = {
+ linkType: "dynamic";
+ urlRegExp?: null;
+ organizerInputType?: null;
+ organizerInputPlaceholder?: null;
+} & CommonProperties;
+
+export type EventLocationTypeFromAppMeta = StaticLinkBasedEventLocation | DynamicLinkBasedEventLocation;
+
+type EventLocationAppData = {
+ location: EventLocationTypeFromAppMeta;
+};
/**
* This is the definition for an app store's app metadata.
@@ -37,8 +69,16 @@ export interface App {
variant: "calendar" | "payment" | "conferencing" | "video" | "other" | "other_calendar";
/** The slug for the app store public page inside `/apps/[slug] */
slug: string;
+
/** The category to which this app belongs, currently we have `calendar`, `payment` or `video` */
+ /*
+ * @deprecated Use categories
+ */
category: string;
+
+ /** The category to which this app belongs, currently we have `calendar`, `payment` or `video` */
+ categories?: string[];
+
/** An absolute url to the app logo */
logo: string;
/** Company or individual publishing this app */
@@ -62,10 +102,7 @@ export interface App {
isGlobal?: boolean;
/** A contact email, mainly to ask for support */
email: string;
- /** Add this value as a posible location option in event types */
- locationType?: LocationType;
- /** If the app adds a location, how should it be displayed? */
- locationLabel?: string;
+
/** Needed API Keys (usually for global apps) */
key?: Prisma.JsonValue;
/** Needed API Keys (usually for global apps) */
@@ -78,4 +115,7 @@ export interface App {
commission?: number;
licenseRequired?: boolean;
isProOnly?: boolean;
+ appData?: EventLocationAppData;
}
+
+export type AppMeta = Optional;
diff --git a/packages/ui/v2/core/Card.tsx b/packages/ui/v2/core/Card.tsx
index 0c1fabb30a..bb9a38931a 100644
--- a/packages/ui/v2/core/Card.tsx
+++ b/packages/ui/v2/core/Card.tsx
@@ -4,7 +4,6 @@ import React from "react";
import classNames from "@calcom/lib/classNames";
-import { Badge } from "./Badge";
import Button from "./Button";
export type BaseCardProps = {
diff --git a/yarn.lock b/yarn.lock
index 1cd590045a..c3db8d9e8a 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7116,12 +7116,12 @@ acorn@^8.0.0, acorn@^8.0.4, acorn@^8.4.1:
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
-acorn@^8.5.0, acorn@^8.7.1:
+acorn@^8.5.0:
version "8.7.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.1.tgz#0197122c843d1bf6d0a5e83220a788f278f63c30"
integrity sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==
-acorn@^8.6.0, acorn@^8.8.0:
+acorn@^8.6.0, acorn@^8.7.1, acorn@^8.8.0:
version "8.8.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8"
integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==