refactor: Use template literal instead of '+' operator (#11444)

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
Aldrin 2023-10-04 00:22:19 +05:30 committed by GitHub
parent a186b130cb
commit 3e08c66888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
190 changed files with 478 additions and 483 deletions

View File

@ -94,11 +94,13 @@ To develop locally:
6. Setup Node
If your Node version does not meet the project's requirements as instructed by the docs, "nvm" (Node Version Manager) allows using Node at the version required by the project:
```sh
nvm use
```
You first might need to install the specific version and then use it:
```sh
nvm install && nvm use
```
@ -134,6 +136,7 @@ yarn test-e2e
```
#### Resolving issues
##### E2E test browsers not installed
Run `npx playwright install` to download test browsers and resolve the error below when running `yarn test-e2e`:
@ -157,4 +160,4 @@ If you get errors, be sure to fix them before committing.
- Be sure to [check the "Allow edits from maintainers" option](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/working-with-forks/allowing-changes-to-a-pull-request-branch-created-from-a-fork) while creating your PR.
- If your PR refers to or fixes an issue, be sure to add `refs #XXX` or `fixes #XXX` to the PR description. Replacing `XXX` with the respective issue number. See more about [Linking a pull request to an issue](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue).
- Be sure to fill the PR Template accordingly.
- Review [App Contribution Guidelines](./packages/app-store/CONTRIBUTING.md) when building integrations
- Review [App Contribution Guidelines](./packages/app-store/CONTRIBUTING.md) when building integrations

View File

@ -144,17 +144,20 @@ Here is what you need to be able to run Cal.com.
```
4. Set up your `.env` file
- Duplicate `.env.example` to `.env`
- Use `openssl rand -base64 32` to generate a key and add it under `NEXTAUTH_SECRET` in the `.env` file.
- Use `openssl rand -base64 24` to generate a key and add it under `CALENDSO_ENCRYPTION_KEY` in the `.env` file.
5. Setup Node
If your Node version does not meet the project's requirements as instructed by the docs, "nvm" (Node Version Manager) allows using Node at the version required by the project:
```sh
nvm use
```
You first might need to install the specific version and then use it:
```sh
nvm install && nvm use
```
@ -234,6 +237,7 @@ echo 'NEXT_PUBLIC_DEBUG=1' >> .env
```
1. Run [mailhog](https://github.com/mailhog/MailHog) to view emails sent during development
> **_NOTE:_** Required when `E2E_TEST_MAILHOG_ENABLED` is "1"
```sh
@ -273,6 +277,7 @@ yarn playwright show-report test-results/reports/playwright-html-report
```
#### Resolving issues
##### E2E test browsers not installed
Run `npx playwright install` to download test browsers and resolve the error below when running `yarn test-e2e`:
@ -492,9 +497,8 @@ following
4. Select Basecamp 4 as the product to integrate with.
5. Set the Redirect URL for OAuth `<Cal.com URL>/api/integrations/basecamp3/callback` replacing Cal.com URL with the URI at which your application runs.
6. Click on done and copy the Client ID and secret into the `BASECAMP3_CLIENT_ID` and `BASECAMP3_CLIENT_SECRET` fields.
7. Set the `BASECAMP3_CLIENT_SECRET` env variable to `{your_domain} ({support_email})`.
For example, `Cal.com (support@cal.com)`.
7. Set the `BASECAMP3_CLIENT_SECRET` env variable to `{your_domain} ({support_email})`.
For example, `Cal.com (support@cal.com)`.
### Obtaining HubSpot Client ID and Secret
@ -529,6 +533,7 @@ For example, `Cal.com (support@cal.com)`.
### Obtaining Zoho Calendar Client ID and Secret
[Follow these steps](./packages/app-store/zohocalendar/)
### Obtaining Zoho Bigin Client ID and Secret
[Follow these steps](./packages/app-store/zoho-bigin/)

View File

@ -4,9 +4,9 @@ Welcome to the first stage of Cal.ai!
This app lets you chat with your calendar via email:
- Turn informal emails into bookings eg. forward "wanna meet tmrw at 2pm?"
- List and rearrange your bookings eg. "Cancel my next meeting"
- Answer basic questions about your busiest times eg. "How does my Tuesday look?"
- Turn informal emails into bookings eg. forward "wanna meet tmrw at 2pm?"
- List and rearrange your bookings eg. "Cancel my next meeting"
- Answer basic questions about your busiest times eg. "How does my Tuesday look?"
The core logic is contained in [agent/route.ts](/apps/ai/src/app/api/agent/route.ts). Here, a [LangChain Agent Executor](https://docs.langchain.com/docs/components/agents/agent-executor) is tasked with following your instructions. Given your last-known timezone, working hours, and busy times, it attempts to CRUD your bookings.
@ -24,10 +24,10 @@ If you haven't yet, please run the [root setup](/README.md) steps.
Before running the app, please see [env.mjs](./src/env.mjs) for all required environment variables. You'll need:
- An [OpenAI API key](https://platform.openai.com/account/api-keys) with access to GPT-4
- A [SendGrid API key](https://app.sendgrid.com/settings/api_keys)
- A default sender email (for example, `ai@cal.dev`)
- The Cal.ai's app ID and URL (see [add.ts](/packages/app-store/cal-ai/api/index.ts))
- An [OpenAI API key](https://platform.openai.com/account/api-keys) with access to GPT-4
- A [SendGrid API key](https://app.sendgrid.com/settings/api_keys)
- A default sender email (for example, `ai@cal.dev`)
- The Cal.ai's app ID and URL (see [add.ts](/packages/app-store/cal-ai/api/index.ts))
To stand up the API and AI apps simultaneously, simply run `yarn dev:ai`.

View File

@ -74,18 +74,19 @@ ${
? `The email references the following @usernames and emails: ${users
.map(
(u) =>
(u.id ? `, id: ${u.id}` : "id: (non user)") +
(u.username
? u.type === "fromUsername"
? `, username: @${u.username}`
: ", username: REDACTED"
: ", (no username)") +
(u.email
? u.type === "fromEmail"
? `, email: ${u.email}`
: ", email: REDACTED"
: ", (no email)") +
";"
`${
(u.id ? `, id: ${u.id}` : "id: (non user)") +
(u.username
? u.type === "fromUsername"
? `, username: @${u.username}`
: ", username: REDACTED"
: ", (no username)") +
(u.email
? u.type === "fromEmail"
? `, email: ${u.email}`
: ", email: REDACTED"
: ", (no email)")
};`
)
.join("\n")}`
: ""

View File

@ -1,3 +1,5 @@
import prismaMock from "../../../../../tests/libs/__mocks__/prisma";
import type { Request, Response } from "express";
import type { NextApiRequest, NextApiResponse } from "next";
import { createMocks } from "node-mocks-http";
@ -8,7 +10,6 @@ import sendPayload from "@calcom/features/webhooks/lib/sendPayload";
import { buildBooking, buildEventType, buildWebhook } from "@calcom/lib/test/builder";
import prisma from "@calcom/prisma";
import prismaMock from "../../../../../tests/libs/__mocks__/prisma";
import handler from "../../../pages/api/bookings/_post";
type CustomNextApiRequest = NextApiRequest & Request;

View File

@ -265,8 +265,8 @@ export const AppPage = ({
{price !== 0 && (
<span className="block text-right">
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
{feeType === "monthly" && "/" + t("month")}
{feeType === "usage-based" ? `${commission}% + ${priceInDollar}/booking` : priceInDollar}
{feeType === "monthly" && `/${t("month")}`}
</span>
)}
@ -286,7 +286,7 @@ export const AppPage = ({
currency: "USD",
useGrouping: false,
}).format(price)}
{feeType === "monthly" && "/" + t("month")}
{feeType === "monthly" && `/${t("month")}`}
</>
)}
</span>
@ -323,7 +323,7 @@ export const AppPage = ({
target="_blank"
rel="noreferrer"
className="text-emphasis font-normal no-underline hover:underline"
href={"mailto:" + email}>
href={`mailto:${email}`}>
<Mail className="text-subtle -mt-px mr-1 inline h-4 w-4" />
{email}

View File

@ -130,7 +130,7 @@ function ConnectedCalendarsList(props: Props) {
title={t("something_went_wrong")}
message={
<span>
<Link href={"/apps/" + item.integration.slug}>{item.integration.name}</Link>:{" "}
<Link href={`/apps/${item.integration.slug}`}>{item.integration.name}</Link>:{" "}
{t("calendar_error")}
</span>
}

View File

@ -380,7 +380,7 @@ function BookingListItem(booking: BookingItemProps) {
</div>
</Link>
</td>
<td className={"w-full px-4" + (isRejected ? " line-through" : "")}>
<td className={`w-full px-4${isRejected ? " line-through" : ""}`}>
<Link href={bookingLink}>
{/* Time and Badges for mobile */}
<div className="w-full pb-2 pt-4 sm:hidden">
@ -576,7 +576,7 @@ const FirstAttendee = ({
<a
key={user.email}
className=" hover:text-blue-500"
href={"mailto:" + user.email}
href={`mailto:${user.email}`}
onClick={(e) => e.stopPropagation()}>
{user.name}
</a>
@ -590,7 +590,7 @@ type AttendeeProps = {
const Attendee = ({ email, name }: AttendeeProps) => {
return (
<a className="hover:text-blue-500" href={"mailto:" + email} onClick={(e) => e.stopPropagation()}>
<a className="hover:text-blue-500" href={`mailto:${email}`} onClick={(e) => e.stopPropagation()}>
{name || email}
</a>
);

View File

@ -121,7 +121,7 @@ export const EditLocationDialog = (props: ISetLocationDialog) => {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: `Invalid URL for ${eventLocationType.label}. ${
sampleUrl ? "Sample URL: " + sampleUrl : ""
sampleUrl ? `Sample URL: ${sampleUrl}` : ""
}`,
});
}

View File

@ -67,7 +67,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
(!user?.theme && typeof document !== "undefined" && document.documentElement.classList.contains("dark"));
eventType.bookingFields.forEach(({ name }) => {
bookingFields[name] = name + " input";
bookingFields[name] = `${name} input`;
});
const eventNameObject: EventNameObjectType = {

View File

@ -189,7 +189,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
value: 0,
},
...[5, 10, 15, 20, 30, 45, 60, 90, 120].map((minutes) => ({
label: minutes + " " + t("minutes"),
label: `minutes ${t("minutes")}`,
value: minutes,
})),
];
@ -225,7 +225,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
value: 0,
},
...[5, 10, 15, 20, 30, 45, 60, 90, 120].map((minutes) => ({
label: minutes + " " + t("minutes"),
label: `minutes ${t("minutes")}`,
value: minutes,
})),
];
@ -272,7 +272,7 @@ export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventTy
value: -1,
},
...[5, 10, 15, 20, 30, 45, 60, 75, 90, 105, 120].map((minutes) => ({
label: minutes + " " + t("minutes"),
label: `minutes ${t("minutes")}`,
value: minutes,
})),
];

View File

@ -247,7 +247,7 @@ function EventTypeSingleLayout({
return (
<Shell
backPath="/event-types"
title={eventType.title + " | " + t("event_type")}
title={`${eventType.title} | ${t("event_type")}`}
heading={eventType.title}
CTA={
<div className="flex items-center justify-end">

View File

@ -22,7 +22,7 @@ const Member = ({ member, teamName }: { member: MemberType; teamName: string | n
return (
<Link key={member.id} href={{ pathname: `/${member.username}`, query: queryParamsToForward }}>
<div className="sm:min-w-80 sm:max-w-80 bg-default hover:bg-muted border-subtle group flex min-h-full flex-col space-y-2 rounded-md border p-4 hover:cursor-pointer">
<Avatar size="md" alt={member.name || ""} imageSrc={"/" + member.username + "/avatar.png"} />
<Avatar size="md" alt={member.name || ""} imageSrc={`/${member.username}/avatar.png`} />
<section className="mt-2 line-clamp-4 w-full space-y-1">
<p className="text-default font-medium">{member.name}</p>
<div className="text-subtle line-clamp-3 overflow-ellipsis text-sm font-normal">

View File

@ -179,14 +179,14 @@ function getThemeProviderProps({
);
}
const appearanceIdSuffix = themeBasis ? ":" + themeBasis : "";
const appearanceIdSuffix = themeBasis ? `:${themeBasis}` : "";
const forcedTheme = themeSupport === ThemeSupport.None ? "light" : undefined;
let embedExplicitlySetThemeSuffix = "";
if (typeof window !== "undefined") {
const embedTheme = window.getEmbedTheme();
if (embedTheme) {
embedExplicitlySetThemeSuffix = ":" + embedTheme;
embedExplicitlySetThemeSuffix = `:${embedTheme}`;
}
}

View File

@ -21,7 +21,7 @@ function getCspPolicy(nonce: string) {
script-src ${
IS_PRODUCTION
? // 'self' 'unsafe-inline' https: added for Browsers not supporting strict-dynamic not supporting strict-dynamic
"'nonce-" + nonce + "' 'strict-dynamic' 'self' 'unsafe-inline' https:"
`'nonce-${nonce}' 'strict-dynamic' 'self' 'unsafe-inline' https:`
: // Note: We could use 'strict-dynamic' with 'nonce-..' instead of unsafe-inline but there are some streaming related scripts that get blocked(because they don't have nonce on them). It causes a really frustrating full page error model by Next.js to show up sometimes
"'unsafe-inline' 'unsafe-eval' https: http:"
};

View File

@ -15,14 +15,9 @@ export default function withEmbedSsr(getServerSideProps: GetServerSideProps) {
const destinationUrlObj = new URL(ssrResponse.redirect.destination, "https://base");
// Make sure that redirect happens to /embed page and pass on embed query param as is for preserving Cal JS API namespace
const newDestinationUrl =
destinationUrlObj.pathname +
"/embed?" +
destinationUrlObj.searchParams.toString() +
"&layout=" +
layout +
"&embed=" +
embed;
const newDestinationUrl = `${
destinationUrlObj.pathname
}/embed?${destinationUrlObj.searchParams.toString()}&layout=${layout}&embed=${embed}`;
return {
...ssrResponse,

View File

@ -21,11 +21,11 @@ process.env.NEXT_PUBLIC_CALCOM_VERSION = version;
// So we can test deploy previews preview
if (process.env.VERCEL_URL && !process.env.NEXT_PUBLIC_WEBAPP_URL) {
process.env.NEXT_PUBLIC_WEBAPP_URL = "https://" + process.env.VERCEL_URL;
process.env.NEXT_PUBLIC_WEBAPP_URL = `https://${process.env.VERCEL_URL}`;
}
// Check for configuration of NEXTAUTH_URL before overriding
if (!process.env.NEXTAUTH_URL && process.env.NEXT_PUBLIC_WEBAPP_URL) {
process.env.NEXTAUTH_URL = process.env.NEXT_PUBLIC_WEBAPP_URL + "/api/auth";
process.env.NEXTAUTH_URL = `${process.env.NEXT_PUBLIC_WEBAPP_URL}/api/auth`;
}
if (!process.env.NEXT_PUBLIC_WEBSITE_URL) {
process.env.NEXT_PUBLIC_WEBSITE_URL = process.env.NEXT_PUBLIC_WEBAPP_URL;

View File

@ -125,7 +125,7 @@ export function UserPage(props: InferGetServerSidePropsType<typeof getServerSide
{user.away ? (
<div className="overflow-hidden rounded-sm border ">
<div className="text-muted p-8 text-center">
<h2 className="font-cal text-default mb-2 text-3xl">😴{" " + t("user_away")}</h2>
<h2 className="font-cal text-default mb-2 text-3xl">😴{` ${t("user_away")}`}</h2>
<p className="mx-auto max-w-md">{t("user_away_description") as string}</p>
</div>
</div>

View File

@ -19,7 +19,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(400).json({ message: "Google client_secret missing." });
// use differnt callback to normal calendar connection
const redirect_uri = WEBAPP_URL + "/api/teams/googleworkspace/callback";
const redirect_uri = `${WEBAPP_URL}/api/teams/googleworkspace/callback`;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
const authUrl = oAuth2Client.generateAuthUrl({

View File

@ -36,7 +36,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (!client_secret || typeof client_secret !== "string")
return res.status(400).json({ message: "Google client_secret missing." });
const redirect_uri = WEBAPP_URL + "/api/teams/googleworkspace/callback";
const redirect_uri = `${WEBAPP_URL}/api/teams/googleworkspace/callback`;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
if (!code) {
@ -54,11 +54,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
if (!teamId) {
res.redirect(getSafeRedirectUrl(WEBAPP_URL + "/settings") ?? `${WEBAPP_URL}/teams`);
res.redirect(getSafeRedirectUrl(`${WEBAPP_URL}/settings`) ?? `${WEBAPP_URL}/teams`);
}
res.redirect(
getSafeRedirectUrl(WEBAPP_URL + `/settings/teams/${teamId}/members?inviteModal=true&bulk=true`) ??
getSafeRedirectUrl(`${WEBAPP_URL}/settings/teams/${teamId}/members?inviteModal=true&bulk=true`) ??
`${WEBAPP_URL}/teams`
);
}

View File

@ -31,7 +31,7 @@ export default function Apps({ categories }: inferSSRProps<typeof getServerSideP
{categories.map((category) => (
<Link
key={category.name}
href={"/apps/categories/" + category.name}
href={`/apps/categories/${category.name}`}
data-testid={`app-store-category-${category.name}`}
className="bg-subtle relative flex rounded-sm px-6 py-4 sm:block">
<div className="self-center">

View File

@ -30,12 +30,12 @@ export default function Provider(props: SSOProviderPageProps) {
const email = searchParams?.get("email");
if (!email) {
router.push("/auth/error?error=" + "Email not provided");
router.push(`/auth/error?error=Email not provided`);
return;
}
if (!props.isSAMLLoginEnabled) {
router.push("/auth/error?error=" + "SAML login not enabled");
router.push(`/auth/error?error=SAML login not enabled`);
return;
}
@ -56,7 +56,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const providerParam = asStringOrNull(context.query.provider);
const emailParam = asStringOrNull(context.query.email);
const usernameParam = asStringOrNull(context.query.username);
const successDestination = "/getting-started" + (usernameParam ? `?username=${usernameParam}` : "");
const successDestination = `/getting-started${usernameParam ? `?username=${usernameParam}` : ""}`;
if (!providerParam) {
throw new Error(`File is not named sso/[provider]`);
}
@ -120,7 +120,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
if (error) {
return {
redirect: {
destination: "/auth/error?error=" + error,
destination: `/auth/error?error=${error}`,
permanent: false,
},
};

View File

@ -120,7 +120,7 @@ export default function Verify() {
? "Your payment failed"
: sessionId
? "Payment successful!"
: "Verify your email" + " | " + APP_NAME}
: `Verify your email | ${APP_NAME}`}
</title>
</Head>
<div className="flex min-h-screen flex-col items-center justify-center px-6">

View File

@ -151,7 +151,7 @@ export default function Availability() {
return (
<Shell
backPath={fromEventType ? true : "/availability"}
title={schedule?.name ? schedule.name + " | " + t("availability") : t("availability")}
title={schedule?.name ? `${schedule.name} | ${t("availability")}` : t("availability")}
heading={
<Controller
control={form.control}

View File

@ -133,7 +133,7 @@ Troubleshoot.PageWrapper = PageWrapper;
function convertMinsToHrsMins(mins: number) {
const h = Math.floor(mins / 60);
const m = mins % 60;
const hs = h < 10 ? "0" + h : h;
const ms = m < 10 ? "0" + m : m;
const hs = h < 10 ? `0${h}` : h;
const ms = m < 10 ? `0${m}` : m;
return `${hs}:${ms}`;
}

View File

@ -269,13 +269,13 @@ export default function Success(props: SuccessProps) {
}
if (needsConfirmation) {
if (props.profile.name !== null) {
return t("user_needs_to_confirm_or_reject_booking" + titleSuffix, {
return t(`user_needs_to_confirm_or_reject_booking${titleSuffix}`, {
user: props.profile.name,
});
}
return t("needs_to_be_confirmed_or_rejected" + titleSuffix);
return t(`needs_to_be_confirmed_or_rejected${titleSuffix}`);
}
return t("emailed_you_and_attendees" + titleSuffix);
return t(`emailed_you_and_attendees${titleSuffix}`);
}
// This is a weird case where the same route can be opened in booking flow as a success page or as a booking detail page from the app
@ -592,23 +592,24 @@ export default function Success(props: SuccessProps) {
</span>
<div className="justify-left mt-1 flex text-left sm:mt-0">
<Link
href={
`https://calendar.google.com/calendar/r/eventedit?dates=${date
.utc()
.format("YYYYMMDDTHHmmss[Z]")}/${date
.add(calculatedDuration, "minute")
.utc()
.format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${
props.eventType.description
}` +
(typeof locationVideoCallUrl === "string"
? "&location=" + encodeURIComponent(locationVideoCallUrl)
: "") +
(props.eventType.recurringEvent
? "&recur=" +
encodeURIComponent(new RRule(props.eventType.recurringEvent).toString())
: "")
}
href={`https://calendar.google.com/calendar/r/eventedit?dates=${date
.utc()
.format("YYYYMMDDTHHmmss[Z]")}/${date
.add(calculatedDuration, "minute")
.utc()
.format("YYYYMMDDTHHmmss[Z]")}&text=${eventName}&details=${
props.eventType.description
}${
typeof locationVideoCallUrl === "string"
? `&location=${encodeURIComponent(locationVideoCallUrl)}`
: ""
}${
props.eventType.recurringEvent
? `&recur=${encodeURIComponent(
new RRule(props.eventType.recurringEvent).toString()
)}`
: ""
}`}
className="text-default border-subtle h-10 w-10 rounded-sm border px-3 py-2 ltr:mr-2 rtl:ml-2">
<svg
className="-mt-1.5 inline-block h-4 w-4"
@ -622,17 +623,17 @@ export default function Success(props: SuccessProps) {
<Link
href={
encodeURI(
"https://outlook.live.com/calendar/0/deeplink/compose?body=" +
props.eventType.description +
"&enddt=" +
date.add(calculatedDuration, "minute").utc().format() +
"&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" +
date.utc().format() +
"&subject=" +
eventName
`https://outlook.live.com/calendar/0/deeplink/compose?body=${
props.eventType.description
}&enddt=${date
.add(calculatedDuration, "minute")
.utc()
.format()}&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=${date
.utc()
.format()}&subject=${eventName}`
) +
(locationVideoCallUrl
? "&location=" + encodeURIComponent(locationVideoCallUrl)
? `&location=${encodeURIComponent(locationVideoCallUrl)}`
: "")
}
className="border-subtle text-default mx-2 h-10 w-10 rounded-sm border px-3 py-2"
@ -649,17 +650,17 @@ export default function Success(props: SuccessProps) {
<Link
href={
encodeURI(
"https://outlook.office.com/calendar/0/deeplink/compose?body=" +
props.eventType.description +
"&enddt=" +
date.add(calculatedDuration, "minute").utc().format() +
"&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=" +
date.utc().format() +
"&subject=" +
eventName
`https://outlook.office.com/calendar/0/deeplink/compose?body=${
props.eventType.description
}&enddt=${date
.add(calculatedDuration, "minute")
.utc()
.format()}&path=%2Fcalendar%2Faction%2Fcompose&rru=addevent&startdt=${date
.utc()
.format()}&subject=${eventName}`
) +
(locationVideoCallUrl
? "&location=" + encodeURIComponent(locationVideoCallUrl)
? `&location=${encodeURIComponent(locationVideoCallUrl)}`
: "")
}
className="text-default border-subtle mx-2 h-10 w-10 rounded-sm border px-3 py-2"
@ -674,9 +675,9 @@ export default function Success(props: SuccessProps) {
</svg>
</Link>
<Link
href={"data:text/calendar," + eventLink()}
href={`data:text/calendar,${eventLink()}`}
className="border-subtle text-default mx-2 h-10 w-10 rounded-sm border px-3 py-2"
download={props.eventType.title + ".ics"}>
download={`${props.eventType.title}.ics`}>
<svg
version="1.1"
fill="currentColor"

View File

@ -140,13 +140,13 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
<div>
<span
className="text-default font-semibold ltr:mr-1 rtl:ml-1"
data-testid={"event-type-title-" + type.id}>
data-testid={`event-type-title-${type.id}`}>
{type.title}
</span>
{group.profile.slug ? (
<small
className="text-subtle hidden font-normal leading-4 sm:inline"
data-testid={"event-type-slug-" + type.id}>
data-testid={`event-type-slug-${type.id}`}>
{`/${
type.schedulingType !== SchedulingType.MANAGED ? group.profile.slug : t("username_placeholder")
}/${type.slug}`}
@ -177,13 +177,13 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
<div>
<span
className="text-default font-semibold ltr:mr-1 rtl:ml-1"
data-testid={"event-type-title-" + type.id}>
data-testid={`event-type-title-${type.id}`}>
{type.title}
</span>
{group.profile.slug ? (
<small
className="text-subtle hidden font-normal leading-4 sm:inline"
data-testid={"event-type-slug-" + type.id}>
data-testid={`event-type-slug-${type.id}`}>
{`/${group.profile.slug}/${type.slug}`}
</small>
) : null}
@ -479,7 +479,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
</>
)}
<Dropdown modal={false}>
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
<DropdownMenuTrigger asChild data-testid={`event-type-options-${type.id}`}>
<Button
type="button"
variant="icon"
@ -493,9 +493,9 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
<DropdownMenuItem>
<DropdownItem
type="button"
data-testid={"event-type-edit-" + type.id}
data-testid={`event-type-edit-${type.id}`}
StartIcon={Edit2}
onClick={() => router.push("/event-types/" + type.id)}>
onClick={() => router.push(`/event-types/${type.id}`)}>
{t("edit")}
</DropdownItem>
</DropdownMenuItem>
@ -505,7 +505,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
<DropdownMenuItem className="outline-none">
<DropdownItem
type="button"
data-testid={"event-type-duplicate-" + type.id}
data-testid={`event-type-duplicate-${type.id}`}
StartIcon={Copy}
onClick={() => openDuplicateModal(type, group)}>
{t("duplicate")}
@ -555,7 +555,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
</div>
<div className="min-w-9 mx-5 flex sm:hidden">
<Dropdown>
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
<DropdownMenuTrigger asChild data-testid={`event-type-options-${type.id}`}>
<Button type="button" variant="icon" color="secondary" StartIcon={MoreHorizontal} />
</DropdownMenuTrigger>
<DropdownMenuPortal>
@ -573,7 +573,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
</DropdownMenuItem>
<DropdownMenuItem className="outline-none">
<DropdownItem
data-testid={"event-type-duplicate-" + type.id}
data-testid={`event-type-duplicate-${type.id}`}
onClick={() => {
navigator.clipboard.writeText(calLink);
showToast(t("link_copied"), "success");
@ -588,7 +588,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
{isNativeShare ? (
<DropdownMenuItem className="outline-none">
<DropdownItem
data-testid={"event-type-duplicate-" + type.id}
data-testid={`event-type-duplicate-${type.id}`}
onClick={() => {
navigator
.share({
@ -608,7 +608,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
{!readOnly && (
<DropdownMenuItem className="outline-none">
<DropdownItem
onClick={() => router.push("/event-types/" + type.id)}
onClick={() => router.push(`/event-types/${type.id}`)}
StartIcon={Edit}
className="w-full rounded-none">
{t("edit")}
@ -620,7 +620,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
<DropdownItem
onClick={() => openDuplicateModal(type, group)}
StartIcon={Copy}
data-testid={"event-type-duplicate-" + type.id}>
data-testid={`event-type-duplicate-${type.id}`}>
{t("duplicate")}
</DropdownItem>
</DropdownMenuItem>

View File

@ -63,14 +63,13 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
const eventType = booking.eventType ? booking.eventType : getDefaultEvent(dynamicEventSlugRef);
const eventPage =
(eventType.team
? "team/" + eventType.team.slug
const eventPage = `${
eventType.team
? `team/${eventType.team.slug}`
: dynamicEventSlugRef
? booking.dynamicGroupSlugRef
: booking.user?.username || "rick") /* This shouldn't happen */ +
"/" +
eventType?.slug;
: booking.user?.username || "rick" /* This shouldn't happen */
}/${eventType?.slug}`;
const destinationUrl = new URLSearchParams();
destinationUrl.set("rescheduleUid", seatReferenceUid || bookingId);

View File

@ -190,7 +190,7 @@ const CalendarsView = () => {
}
<div className="flex-grow truncate pl-2">
<ListItemTitle component="h3" className="mb-1 space-x-2 rtl:space-x-reverse">
<Link href={"/apps/" + item.integration.slug}>
<Link href={`/apps/${item.integration.slug}`}>
{item.integration.name || item.integration.title}
</Link>
{data?.destinationCalendar?.credentialId === item.credentialId && (

View File

@ -281,7 +281,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
return {
redirect: {
permanent: false,
destination: "/auth/login?callbackUrl=" + `${WEBAPP_URL}/${ctx.query.callbackUrl}`,
destination: `/auth/login?callbackUrl=${WEBAPP_URL}/${ctx.query.callbackUrl}`,
},
};
}

View File

@ -102,7 +102,7 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
items={type.users.map((user) => ({
alt: user.name || "",
title: user.name || "",
image: "/" + user.username + "/avatar.png" || "",
image: `/${user.username}/avatar.png` || "",
}))}
/>
</div>
@ -160,7 +160,7 @@ function TeamPage({ team, isUnpublished, markdownStrippedBio, isValidOrgDomain }
<div className="space-y-6" data-testid="event-types">
<div className="overflow-hidden rounded-sm border dark:border-gray-900">
<div className="text-muted p-8 text-center">
<h2 className="font-cal text-emphasis mb-2 text-3xl">{" " + t("org_no_teams_yet")}</h2>
<h2 className="font-cal text-emphasis mb-2 text-3xl">{` ${t("org_no_teams_yet")}`}</h2>
<p className="text-emphasis mx-auto max-w-md">{t("org_no_teams_yet_description")}</p>
</div>
</div>
@ -324,7 +324,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
...type,
users: type.users.map((user) => ({
...user,
avatar: "/" + user.username + "/avatar.png",
avatar: `/${user.username}/avatar.png`,
})),
descriptionAsSafeHTML: markdownToSafeHTML(type.description),
})) ?? null;

View File

@ -95,12 +95,12 @@ export default function JoinCall(props: JoinCallPageProps) {
<meta property="og:image" content={SEO_IMG_OGIMG_VIDEO} />
<meta property="og:type" content="website" />
<meta property="og:url" content={`${WEBSITE_URL}/video`} />
<meta property="og:title" content={APP_NAME + " Video"} />
<meta property="og:title" content={`${APP_NAME} Video`} />
<meta property="og:description" content={t("quick_video_meeting")} />
<meta property="twitter:image" content={SEO_IMG_OGIMG_VIDEO} />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:url" content={`${WEBSITE_URL}/video`} />
<meta property="twitter:title" content={APP_NAME + " Video"} />
<meta property="twitter:title" content={`${APP_NAME} Video`} />
<meta property="twitter:description" content={t("quick_video_meeting")} />
</Head>
<div style={{ zIndex: 2, position: "relative" }}>

View File

@ -44,7 +44,7 @@ export default function MeetingUnavailable(props: inferSSRProps<typeof getServer
</h2>
<p className="text-subtle text-center">
<Calendar className="-mt-1 mr-1 inline-block h-4 w-4" />
{dayjs(props.booking.startTime).format(detectBrowserTimeFormat + ", dddd DD MMMM YYYY")}
{dayjs(props.booking.startTime).format(`${detectBrowserTimeFormat}, dddd DD MMMM YYYY`)}
</p>
</div>
</div>

View File

@ -24,7 +24,7 @@ export default function MeetingNotStarted(props: inferSSRProps<typeof getServerS
<h2 className="mb-2 text-center font-medium">{props.booking.title}</h2>
<p className="text-subtle text-center">
<Calendar className="-mt-1 mr-1 inline-block h-4 w-4" />
{dayjs(props.booking.startTime).format(detectBrowserTimeFormat + ", dddd DD MMMM YYYY")}
{dayjs(props.booking.startTime).format(`${detectBrowserTimeFormat}, dddd DD MMMM YYYY`)}
</p>
</>
}

View File

@ -23,7 +23,7 @@ const otherNonExistingRoutePrefixes = ["forms", "router", "success", "cancel"];
// book$ ensures that only /book is excluded from rewrite(which is at the end always) and not /booked
let subdomainRegExp = (exports.subdomainRegExp = getSubdomainRegExp(
process.env.NEXT_PUBLIC_WEBAPP_URL || "https://" + process.env.VERCEL_URL
process.env.NEXT_PUBLIC_WEBAPP_URL || `https://${process.env.VERCEL_URL}`
));
exports.orgHostPath = `^(?<orgSlug>${subdomainRegExp})\\.(?!vercel\.app).*`;

View File

@ -28,7 +28,7 @@ export const createPaymentsFixture = (page: Page) => {
},
},
data: {},
externalId: "DEMO_PAYMENT_FROM_DB_" + Date.now(),
externalId: `DEMO_PAYMENT_FROM_DB_${Date.now()}`,
booking: {
connect: {
id: bookingId,

View File

@ -23,7 +23,7 @@ export const nextServer = async ({ port = 3000 } = { port: 3000 }) => {
process.env.PLAYWRIGHT_TEST_BASE_URL =
process.env.NEXT_PUBLIC_WEBAPP_URL =
process.env.NEXT_PUBLIC_WEBSITE_URL =
"http://localhost:" + port;
`http://localhost:${port}`;
const app = next({
dev: dev,
port,
@ -46,7 +46,7 @@ export const nextServer = async ({ port = 3000 } = { port: 3000 }) => {
resolve(server);
});
server.on("error", (error) => {
if (error) throw new Error("Could not start Next.js server -" + error.message);
if (error) throw new Error(`Could not start Next.js server - ${error.message}`);
});
});
return server;

View File

@ -65,7 +65,7 @@ test.describe("OAuth Provider", () => {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + tokenData.access_token,
Authorization: `Bearer ${tokenData.access_token}`,
},
});
@ -96,7 +96,7 @@ test.describe("OAuth Provider", () => {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + tokenData.access_token,
Authorization: `Bearer ${tokenData.access_token}`,
},
});
@ -150,7 +150,7 @@ test.describe("OAuth Provider", () => {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + tokenData.access_token,
Authorization: `Bearer ${tokenData.access_token}`,
},
});
@ -181,7 +181,7 @@ test.describe("OAuth Provider", () => {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + tokenData.access_token,
Authorization: `Bearer ${tokenData.access_token}`,
},
});

View File

@ -404,8 +404,8 @@ export const getDate = (
year = year + 1;
}
const date = _date < 10 ? "0" + _date : _date;
const month = _month < 10 ? "0" + _month : _month;
const date = _date < 10 ? `0${_date}` : _date;
const month = _month < 10 ? `0${_month}` : _month;
return {
date,

View File

@ -208,7 +208,7 @@ export const AppForm = ({
</Text>
<Text>
Tip : Go and change the logo of your {isTemplate ? "template" : "app"} by replacing{" "}
{getAppDirPath(slug, isTemplate) + "/static/icon.svg"}
{`${getAppDirPath(slug, isTemplate)}/static/icon.svg`}
</Text>
<Newline />
<Text bold underline color="blue">

View File

@ -12,7 +12,7 @@ export function Message({
if (message.showInProgressIndicator) {
const interval = setInterval(() => {
setProgressText((progressText) => {
return progressText.length > 3 ? "" : progressText + ".";
return progressText.length > 3 ? "" : `${progressText}.`;
});
}, 1000);
return () => {

View File

@ -1,7 +1,7 @@
import fs from "fs";
import path from "path";
import { APP_STORE_PATH, TEMPLATES_PATH, IS_WINDOWS_PLATFORM} from "./constants";
import { APP_STORE_PATH, TEMPLATES_PATH, IS_WINDOWS_PLATFORM } from "./constants";
import execSync from "./utils/execSync";
const slugify = (str: string) => {
@ -70,7 +70,11 @@ export const BaseAppFork = {
const appDirPath = getAppDirPath(slug, isTemplate);
if (!editMode) {
await execSync(IS_WINDOWS_PLATFORM ? `mkdir ${appDirPath}` : `mkdir -p ${appDirPath}`);
await execSync(IS_WINDOWS_PLATFORM ? `xcopy "${TEMPLATES_PATH}\\${template}\\*" "${appDirPath}" /e /i` : `cp -r ${TEMPLATES_PATH}/${template}/* ${appDirPath}`);
await execSync(
IS_WINDOWS_PLATFORM
? `xcopy "${TEMPLATES_PATH}\\${template}\\*" "${appDirPath}" /e /i`
: `cp -r ${TEMPLATES_PATH}/${template}/* ${appDirPath}`
);
} else {
if (!oldSlug) {
throw new Error("oldSlug is required when editMode is true");
@ -79,7 +83,9 @@ export const BaseAppFork = {
// We need to rename only if they are different
const oldAppDirPath = getAppDirPath(oldSlug, isTemplate);
await execSync(IS_WINDOWS_PLATFORM ? `move ${oldAppDirPath} ${appDirPath}` : `mv ${oldAppDirPath} ${appDirPath}`);
await execSync(
IS_WINDOWS_PLATFORM ? `move ${oldAppDirPath} ${appDirPath}` : `mv ${oldAppDirPath} ${appDirPath}`
);
}
}
updatePackageJson({ slug, appDirPath, appDescription: description });

View File

@ -1,11 +1,13 @@
## App Contribution Guidelines
#### `DESCRIPTION.md`
1. images - include atleast 4 images (do we have a recommended size here?). Can show app in use and/or installation steps
2. add only file name for images, path not required. i.e. `1.jpeg`, not `/app-store/zohocalendar/1.jpeg`
3. description should include what the integration with Cal allows the user to do e.g. `Allows you to sync Cal bookings with your Zoho Calendar`
#### `README.md`
1. Include installation instructions and links to the app's website.
2. For url use `<baseUrl>/api/integrations`, rather than `<Cal.com>/api/integrations`
@ -16,7 +18,8 @@
2. description here should not exceed 10 words (this is arbitrary, but should not be long otherwise it's truncated in the app store)
#### Others
1. Add API documentation links in comments for files `api`, `lib` and `types`
1. Add API documentation links in comments for files `api`, `lib` and `types`
2. Use [`AppDeclarativeHandler`](../types/AppHandler.d.ts) across all apps. Whatever isn't supported in it, support that.
3. README should be added in the respective app and can be linked in main README [like this](https://github.com/calcom/cal.com/pull/10429/files/155ac84537d12026f595551fe3542e810b029714#diff-b335630551682c19a781afebcf4d07bf978fb1f8ac04c6bf87428ed5106870f5R509)
4. Also, no env variables should be added by an app. They should be [added in `zod.ts`](https://github.com/calcom/cal.com/blob/main/packages/app-store/jitsivideo/zod.ts) and then they would be automatically available to be modified by the cal.com app admin. In local development you can open /settings/admin with the admin credentials (see [seed.ts](packages/prisma/seed.ts))
4. Also, no env variables should be added by an app. They should be [added in `zod.ts`](https://github.com/calcom/cal.com/blob/main/packages/app-store/jitsivideo/zod.ts) and then they would be automatically available to be modified by the cal.com app admin. In local development you can open /settings/admin with the admin credentials (see [seed.ts](packages/prisma/seed.ts))

View File

@ -44,7 +44,7 @@ export default function AppCard({
<div className="flex w-full flex-col gap-2 sm:flex-row sm:gap-0">
{/* Don't know why but w-[42px] isn't working, started happening when I started using next/dynamic */}
<Link
href={"/apps/" + app.slug}
href={`/apps/${app.slug}`}
className={classNames(app?.isInstalled ? "mr-[11px]" : "mr-3", "h-auto w-10 rounded-sm")}>
<img
className={classNames(

View File

@ -64,7 +64,7 @@ function useAddAppMutation(_type: App["type"] | null, allOptions?: UseAddAppMuta
const stateStr = encodeURIComponent(JSON.stringify(state));
const searchParams = `?state=${stateStr}${teamId ? `&teamId=${teamId}` : ""}`;
const res = await fetch(`/api/integrations/${type}/add` + searchParams);
const res = await fetch(`/api/integrations/${type}/add${searchParams}`);
if (!res.ok) {
const errorBody = await res.json();

View File

@ -24,9 +24,7 @@ async function handler(req: NextApiRequest) {
client_id,
};
const query = stringify(params);
const url = `https://launchpad.37signals.com/authorization/new?${query}&redirect_uri=${
WEBAPP_URL + "/api/integrations/basecamp3/callback"
}`;
const url = `https://launchpad.37signals.com/authorization/new?${query}&redirect_uri=${WEBAPP_URL}/api/integrations/basecamp3/callback`;
return { url };
}

View File

@ -11,7 +11,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const { code } = req.query;
const { client_id, client_secret, user_agent } = await getAppKeysFromSlug("basecamp3");
const redirectUri = WEBAPP_URL + "/api/integrations/basecamp3/callback";
const redirectUri = `${WEBAPP_URL}/api/integrations/basecamp3/callback`;
const params = new URLSearchParams({
type: "web_server",

View File

@ -105,14 +105,9 @@ export default class BasecampCalendarService implements Calendar {
minute: "numeric",
});
const baseString = `<div>Event title: ${event.title}<br/>Date and time: ${date}, ${startTime} - ${endTime} ${timeZone}<br/>View on Cal.com: <a target="_blank" rel="noreferrer" class="autolinked" data-behavior="truncate" href="https://app.cal.com/booking/${event.uid}">https://app.cal.com/booking/${event.uid}</a> `;
const guestString =
"<br/>Guests: " +
event.attendees.reduce((acc, attendee) => {
return (
acc +
`<br/><a target=\"_blank\" rel=\"noreferrer\" class=\"autolinked\" data-behavior=\"truncate\" href=\"mailto:${attendee.email}\">${attendee.email}</a>`
);
}, "");
const guestString = `<br/>Guests: ${event.attendees.reduce((acc, attendee) => {
return `${acc}<br/><a target=\"_blank\" rel=\"noreferrer\" class=\"autolinked\" data-behavior=\"truncate\" href=\"mailto:${attendee.email}\">${attendee.email}</a>`;
}, "")}`;
const videoString = event.videoCallData
? `<br/>Join on video: ${event.videoCallData.url}</div>`

View File

@ -9,4 +9,4 @@ Example questions:
- Send an email with the title: What's my schedule?
- Forward an email from someone looking to meet you
- Ask to schedule a call with someone
- Ask to schedule a call with someone

View File

@ -19,4 +19,3 @@ Every CalDav Provider is different and needs minor adjustments.
Follow our CalDav Roadmap here: <a class="text-blue-500" target="_blank" href="https://github.com/calcom/cal.com/issues/3457">https://github.com/calcom/cal.com/issues/3457</a>.
If your CalDav connection is not working, please try another Calendar provider (Google Mail or another CalDav).

View File

@ -49,12 +49,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
let message = e.message;
if (e.message.indexOf("Invalid credentials") > -1 && url.indexOf("dav.php") > -1) {
const parsedUrl = new URL(url);
const adminUrl =
parsedUrl.protocol +
"//" +
parsedUrl.hostname +
(parsedUrl.port ? ":" + parsedUrl.port : "") +
"/admin/?/settings/standard/";
const adminUrl = `${parsedUrl.protocol}//${parsedUrl.hostname}${
parsedUrl.port ? `:${parsedUrl.port}` : ""
}/admin/?/settings/standard/`;
message = `Couldn\'t connect to caldav account, please verify WebDAV authentication type is set to "Basic"`;
return res.status(500).json({ message, actionUrl: adminUrl });
}

View File

@ -71,7 +71,7 @@ export const fetcher = async (endpoint: string, init?: RequestInit | undefined)
return fetch(`https://api.daily.co/v1${endpoint}`, {
method: "GET",
headers: {
Authorization: "Bearer " + api_key,
Authorization: `Bearer ${api_key}`,
"Content-Type": "application/json",
...init?.headers,
},

View File

@ -22,7 +22,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (typeof appKeys.client_secret === "string") client_secret = appKeys.client_secret;
if (!client_id) return res.status(400).json({ message: "Google client_id missing." });
if (!client_secret) return res.status(400).json({ message: "Google client_secret missing." });
const redirect_uri = WEBAPP_URL_FOR_OAUTH + "/api/integrations/googlecalendar/callback";
const redirect_uri = `${WEBAPP_URL_FOR_OAUTH}/api/integrations/googlecalendar/callback`;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
const authUrl = oAuth2Client.generateAuthUrl({

View File

@ -30,7 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (!client_id) return res.status(400).json({ message: "Google client_id missing." });
if (!client_secret) return res.status(400).json({ message: "Google client_secret missing." });
const redirect_uri = WEBAPP_URL_FOR_OAUTH + "/api/integrations/googlecalendar/callback";
const redirect_uri = `${WEBAPP_URL_FOR_OAUTH}/api/integrations/googlecalendar/callback`;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
@ -69,7 +69,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
res.redirect(
getSafeRedirectUrl(CAL_URL + "/apps/installed/conferencing?hl=google-meet") ??
getSafeRedirectUrl(`${CAL_URL}/apps/installed/conferencing?hl=google-meet`) ??
getInstalledAppPath({ variant: "conferencing", slug: "google-meet" })
);
}

View File

@ -18,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (typeof appKeys.client_id === "string") client_id = appKeys.client_id;
if (!client_id) return res.status(400).json({ message: "HubSpot client id missing." });
const redirectUri = WEBAPP_URL + "/api/integrations/hubspot/callback";
const redirectUri = `${WEBAPP_URL}/api/integrations/hubspot/callback`;
const url = hubspotClient.oauth.getAuthorizationUrl(
client_id,
redirectUri,

View File

@ -39,7 +39,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const hubspotToken: HubspotToken = await hubspotClient.oauth.tokensApi.createToken(
"authorization_code",
code,
WEBAPP_URL + "/api/integrations/hubspot/callback",
`${WEBAPP_URL}/api/integrations/hubspot/callback`,
client_id,
client_secret
);

View File

@ -179,7 +179,7 @@ export default class HubspotCalendarService implements Calendar {
await hubspotClient.oauth.tokensApi.createToken(
"refresh_token",
undefined,
WEBAPP_URL + "/api/integrations/hubspot/callback",
`${WEBAPP_URL}/api/integrations/hubspot/callback`,
this.client_id,
this.client_secret,
refreshToken

View File

@ -18,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const params = {
client_id,
redirect_uri: WEBAPP_URL_FOR_OAUTH + "/api/integrations/intercom/callback",
redirect_uri: `${WEBAPP_URL_FOR_OAUTH}/api/integrations/intercom/callback`,
state,
response_type: "code",
};

View File

@ -48,7 +48,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (response.status !== 200) {
log.error("get user_access_token failed", responseBody);
return res.redirect("/apps/installed?error=" + JSON.stringify(responseBody));
return res.redirect(`/apps/installed?error=${JSON.stringify(responseBody)}`);
}
// Find the admin id from the accompte thanks to access_token and store it
@ -64,7 +64,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (admin.status !== 200) {
log.error("get admin_id failed", adminBody);
return res.redirect("/apps/installed?error=" + JSON.stringify(adminBody));
return res.redirect(`/apps/installed?error=${JSON.stringify(adminBody)}`);
}
const adminId = adminBody.id;
@ -86,7 +86,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
);
res.redirect(
getSafeRedirectUrl(CAL_URL + "/apps/installed/automation?hl=intercom") ??
getSafeRedirectUrl(`${CAL_URL}/apps/installed/automation?hl=intercom`) ??
getInstalledAppPath({ variant: "automation", slug: "intercom" })
);
}

View File

@ -6,9 +6,7 @@
"logo": "icon.svg",
"url": "https://github.com/vachmara",
"variant": "automation",
"categories": [
"automation"
],
"categories": ["automation"],
"publisher": "Valentin Chmara",
"email": "valentinchmara@gmail.com",
"description": "Enhance your scheduling and appointment management experience with the Intercom Integration for Cal.com.",

View File

@ -33,7 +33,7 @@ const JitsiVideoApiAdapter = (): VideoApiAdapter => {
type: metadata.type,
id: meetingID,
password: "",
url: hostUrl + "/" + encodeURIComponent(meetingID),
url: `${hostUrl}/${encodeURIComponent(meetingID)}`,
});
},
deleteMeeting: async (): Promise<void> => {

View File

@ -22,7 +22,7 @@ async function getHandler(req: NextApiRequest) {
const params = {
app_id,
redirect_uri: WEBAPP_URL + "/api/integrations/larkcalendar/callback",
redirect_uri: `${WEBAPP_URL}/api/integrations/larkcalendar/callback`,
state,
};

View File

@ -28,7 +28,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
const response = await fetch(`https://${LARK_HOST}/open-apis/authen/v1/access_token`, {
method: "POST",
headers: {
Authorization: "Bearer " + appAccessToken,
Authorization: `Bearer ${appAccessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
@ -41,7 +41,7 @@ async function getHandler(req: NextApiRequest, res: NextApiResponse) {
if (!response.ok || responseBody.code !== 0) {
log.error("get user_access_token failed with none 0 code", responseBody);
return res.redirect("/apps/installed?error=" + JSON.stringify(responseBody));
return res.redirect(`/apps/installed?error=${JSON.stringify(responseBody)}`);
}
const key: LarkAuthCredentials = {

View File

@ -114,7 +114,7 @@ export async function sendPostMsg(
const response = await fetch(`https://${LARK_HOST}/open-apis/im/v1/messages?receive_id_type=open_id`, {
method: "POST",
headers: {
Authorization: "Bearer " + tenantAccessToken,
Authorization: `Bearer ${tenantAccessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({

View File

@ -122,7 +122,7 @@ export default class LarkCalendarService implements Calendar {
return fetch(`${this.url}${endpoint}`, {
method: "GET",
headers: {
Authorization: "Bearer " + accessToken,
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
...init?.headers,
},

View File

@ -402,8 +402,9 @@ export function getSuccessPageLocationMessage(
if (bookingStatus === BookingStatus.CANCELLED || bookingStatus === BookingStatus.REJECTED) {
locationToDisplay == t("web_conference");
} else if (isConfirmed) {
locationToDisplay =
getHumanReadableLocationValue(location, t) + ": " + t("meeting_url_in_confirmation_email");
locationToDisplay = `${getHumanReadableLocationValue(location, t)}: ${t(
"meeting_url_in_confirmation_email"
)}`;
} else {
locationToDisplay = t("web_conferencing_details_to_follow");
}

View File

@ -7,4 +7,4 @@ items:
- 5.jpeg
---
Workflow automation for everyone. Use the Cal.com app in Make to automate your workflows when a booking is created, rescheduled, cancelled or when a meeting has ended. You can also get all your booking with the 'List Bookings' module.<br /><br />**After Installation:** Have you lost your API key? You can always generate a new key on the <a href="/apps/make/setup">**<ins>Make Setup Page</ins>**</a>
Workflow automation for everyone. Use the Cal.com app in Make to automate your workflows when a booking is created, rescheduled, cancelled or when a meeting has ended. You can also get all your booking with the 'List Bookings' module.<br /><br />**After Installation:** Have you lost your API key? You can always generate a new key on the <a href="/apps/make/setup">**<ins>Make Setup Page</ins>**</a>

View File

@ -1,6 +1,5 @@
# Setting up Make Integration
1. Install the app from the Cal app store and generate an API key. Copy the API key.
2. Go to `/admin/apps/automation` in Cal and set the `invite_link` for Make to `https://www.make.com/en/hq/app-invitation/6cb2772b61966508dd8f414ba3b44510` to use the app.
3. Create a [Make account](https://www.make.com/en/login), if you don't have one.
@ -19,4 +18,4 @@ Possible solution: using [https://ngrok.com/](https://ngrok.com/)
1. Create Account
2. [Download](https://ngrok.com/download) ngrok and start a tunnel to your running localhost
- Use forwarding url as your baseUrl for the URL endpoints
3. Use the ngrok url as your Cal deployment url when creating the **Connection** in Make.
3. Use the ngrok url as your Cal deployment url when creating the **Connection** in Make.

View File

@ -22,7 +22,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
return res.status(500).json({ message: "Unable to get bookings." });
}
if (bookings.length === 0) {
const requested = validKey.teamId ? "teamId: " + validKey.teamId : "userId: " + validKey.userId;
const requested = validKey.teamId ? `teamId: ${validKey.teamId}` : `userId: ${validKey.userId}`;
return res.status(404).json({
message: `There are no bookings to retrieve, please create a booking first. Requested: \`${requested}\``,
});

View File

@ -20,7 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
response_type: "code",
scope: scopes.join(" "),
client_id,
redirect_uri: WEBAPP_URL + "/api/integrations/office365calendar/callback",
redirect_uri: `${WEBAPP_URL}/api/integrations/office365calendar/callback`,
state,
};
const query = stringify(params);

View File

@ -29,7 +29,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const toUrlEncoded = (payload: Record<string, string>) =>
Object.keys(payload)
.map((key) => key + "=" + encodeURIComponent(payload[key]))
.map((key) => `${key}=${encodeURIComponent(payload[key])}`)
.join("&");
const body = toUrlEncoded({
@ -37,7 +37,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
grant_type: "authorization_code",
code,
scope: scopes.join(" "),
redirect_uri: WEBAPP_URL + "/api/integrations/office365calendar/callback",
redirect_uri: `${WEBAPP_URL}/api/integrations/office365calendar/callback`,
client_secret,
});
@ -52,11 +52,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const responseBody = await response.json();
if (!response.ok) {
return res.redirect("/apps/installed?error=" + JSON.stringify(responseBody));
return res.redirect(`/apps/installed?error=${JSON.stringify(responseBody)}`);
}
const whoami = await fetch("https://graph.microsoft.com/v1.0/me", {
headers: { Authorization: "Bearer " + responseBody.access_token },
headers: { Authorization: `Bearer ${responseBody.access_token}` },
});
const graphUser = await whoami.json();

View File

@ -343,7 +343,7 @@ export default class Office365CalendarService implements Calendar {
return fetch(`${this.apiGraphUrl}${endpoint}`, {
method: "get",
headers: {
Authorization: "Bearer " + this.accessToken,
Authorization: `Bearer ${this.accessToken}`,
"Content-Type": "application/json",
},
...init,
@ -479,8 +479,8 @@ export default class Office365CalendarService implements Calendar {
subResponse.body.value.reduce((acc: BufferedBusyTime[], evt: BodyValue) => {
if (evt.showAs === "free" || evt.showAs === "workingElsewhere") return acc;
return acc.concat({
start: evt.start.dateTime + "Z",
end: evt.end.dateTime + "Z",
start: `${evt.start.dateTime}Z`,
end: `${evt.end.dateTime}Z`,
});
}, [])
);

View File

@ -20,7 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
response_type: "code",
scope: scopes.join(" "),
client_id,
redirect_uri: WEBAPP_URL + "/api/integrations/office365video/callback",
redirect_uri: `${WEBAPP_URL}/api/integrations/office365video/callback`,
state,
};
const query = stringify(params);

View File

@ -30,7 +30,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const toUrlEncoded = (payload: Record<string, string>) =>
Object.keys(payload)
.map((key) => key + "=" + encodeURIComponent(payload[key]))
.map((key) => `${key}=${encodeURIComponent(payload[key])}`)
.join("&");
const body = toUrlEncoded({
@ -38,7 +38,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
grant_type: "authorization_code",
code,
scope: scopes.join(" "),
redirect_uri: WEBAPP_URL + "/api/integrations/office365video/callback",
redirect_uri: `${WEBAPP_URL}/api/integrations/office365video/callback`,
client_secret,
});
@ -53,11 +53,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const responseBody = await response.json();
if (!response.ok) {
return res.redirect("/apps/installed?error=" + JSON.stringify(responseBody));
return res.redirect(`/apps/installed?error=${JSON.stringify(responseBody)}`);
}
const whoami = await fetch("https://graph.microsoft.com/v1.0/me", {
headers: { Authorization: "Bearer " + responseBody.access_token },
headers: { Authorization: `Bearer ${responseBody.access_token}` },
});
const graphUser = await whoami.json();

View File

@ -128,7 +128,7 @@ const TeamsVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
const resultString = await fetch("https://graph.microsoft.com/v1.0/me/onlineMeetings", {
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(translateEvent(event)),
@ -152,7 +152,7 @@ const TeamsVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
const resultString = await fetch("https://graph.microsoft.com/v1.0/me/onlineMeetings", {
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify(translateEvent(event)),

View File

@ -272,7 +272,7 @@ class Paypal {
webhook_id: options.body.webhook_id,
});
const bodyToString = stringy.slice(0, -1) + `,"webhook_event":${options.body.webhook_event}` + "}";
const bodyToString = `${stringy.slice(0, -1)},"webhook_event":${options.body.webhook_event}}`;
try {
const response = await this.fetcher(`/v1/notifications/verify-webhook-signature`, {

View File

@ -19,7 +19,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
const eventTypeURL = eventType.URL + query;
function QRCode({ size, data }: { size: number; data: string }) {
const QR_URL = "https://api.qrserver.com/v1/create-qr-code/?size=" + size + "&data=" + data;
const QR_URL = `https://api.qrserver.com/v1/create-qr-code/?size=${size}&data=${data}`;
return (
<Tooltip content={eventTypeURL}>
<a download href={QR_URL} target="_blank" rel="noreferrer">

View File

@ -108,10 +108,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
// Make Header
res.write(
headerFields
`${headerFields
.map((field) => `${field.label}${field.deleted ? "(Deleted)" : ""}`)
.concat(["Submission Time"])
.join(",") + "\n"
.join(",")}\n`
);
for await (const partialCsv of csvIterator) {

View File

@ -44,7 +44,7 @@ export function getQueryBuilderConfig(form: RoutingForm, forReporting = false) {
// preferWidgets: field.type === "textarea" ? ["textarea"] : [],
};
} else {
throw new Error("Unsupported field type:" + field.type);
throw new Error(`Unsupported field type:${field.type}`);
}
});

View File

@ -30,7 +30,7 @@ export async function getSerializableForm<TForm extends App_RoutingForms_Form>({
const fieldsParsed = zodFields.safeParse(form.fields);
if (!fieldsParsed.success) {
throw new Error("Error parsing fields" + fieldsParsed.error);
throw new Error(`Error parsing fields: ${fieldsParsed.error}`);
}
const settings = RoutingFormSettings.parse(
@ -91,7 +91,7 @@ export async function getSerializableForm<TForm extends App_RoutingForms_Form>({
},
});
if (!router) {
throw new Error("Form -" + route.id + ", being used as router, not found");
throw new Error(`Form - ${route.id}, being used as router, not found`);
}
const parsedRouter = await getSerializableForm({ form: router });

View File

@ -241,7 +241,7 @@ export default function RoutingForms({
<ArrowButton onClick={() => moveRoutingForm(index, 1)} arrowDirection="down" />
)}
<ListLinkItem
href={appUrl + "/form-edit/" + form.id}
href={`${appUrl}/form-edit/${form.id}`}
heading={form.name}
disabled={readOnly}
subHeading={description}

View File

@ -33,7 +33,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const conn = new jsforce.Connection({
clientId: consumer_key,
clientSecret: consumer_secret,
redirectUri: WEBAPP_URL + "/api/integrations/salesforce/callback",
redirectUri: `${WEBAPP_URL}/api/integrations/salesforce/callback`,
});
const salesforceTokenInfo = await conn.oauth2.requestToken(code as string);

View File

@ -110,7 +110,7 @@ export default class SalesforceCalendarService implements Calendar {
return new jsforce.Connection({
clientId: consumer_key,
clientSecret: consumer_secret,
redirectUri: WEBAPP_URL + "/api/integrations/salesforce/callback",
redirectUri: `${WEBAPP_URL}/api/integrations/salesforce/callback`,
instanceUrl: credentialKey.instance_url,
accessToken: credentialKey.access_token,
refreshToken: credentialKey.refresh_token,

View File

@ -23,7 +23,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
},
});
const redirect_uri = encodeURI(WEBAPP_URL + "/api/integrations/stripepayment/callback");
const redirect_uri = encodeURI(`${WEBAPP_URL}/api/integrations/stripepayment/callback`);
const stripeConnectParams: Stripe.OAuthAuthorizeUrlParams = {
client_id,
scope: "read_write",

View File

@ -22,7 +22,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (error) {
const query = stringify({ error, error_description });
res.redirect("/apps/installed?" + query);
res.redirect(`/apps/installed?${query}`);
return;
}

View File

@ -60,7 +60,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
returnTo={WEBAPP_URL + pathname + "?tabName=apps"}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
switchChecked={requirePayment}
switchOnClick={(enabled) => {

View File

@ -15,5 +15,5 @@ export function createPaymentLink(opts: {
let link = "";
if (absolute) link = WEBSITE_URL;
const query = stringify({ date, name, email });
return link + `/payment/${paymentUid}?${query}`;
return `${link}/payment/${paymentUid}?${query}`;
}

View File

@ -73,7 +73,7 @@ export async function deleteStripeCustomer(user: UserType): Promise<string | nul
const customerId = await getStripeCustomerId(user);
if (!customerId) {
console.warn("No stripe customer found for user:" + user.email);
console.warn(`No stripe customer found for user:${user.email}`);
return null;
}

View File

@ -14,7 +14,7 @@ const SylapsApiAdapter = (): VideoApiAdapter => {
type: "sylaps_video",
id: meetingID,
password: "",
url: "https://sylaps.com/r/" + meetingID,
url: `https://sylaps.com/r/${meetingID}`,
});
},
deleteMeeting: async (): Promise<void> => {

View File

@ -27,7 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (!client_id) return res.status(400).json({ message: "Tandem client_id missing." });
if (!base_url) return res.status(400).json({ message: "Tandem base_url missing." });
const redirect_uri = encodeURI(WEBAPP_URL + "/api/integrations/tandemvideo/callback");
const redirect_uri = encodeURI(`${WEBAPP_URL}/api/integrations/tandemvideo/callback`);
const params = {
client_id,

View File

@ -125,7 +125,7 @@ const TandemVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =
const result = await fetch(`${base_url}/api/v1/meetings`, {
method: "POST",
headers: {
Authorization: "Bearer " + accessToken,
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: _translateEvent(event, "meeting"),
@ -141,7 +141,7 @@ const TandemVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =
await fetch(`${base_url}/api/v1/meetings/${uid}`, {
method: "DELETE",
headers: {
Authorization: "Bearer " + accessToken,
Authorization: `Bearer ${accessToken}`,
},
}).then(handleErrorsRaw);
@ -154,7 +154,7 @@ const TandemVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =
const result = await fetch(`${base_url}/api/v1/meetings/${bookingRef.meetingId}`, {
method: "PUT",
headers: {
Authorization: "Bearer " + accessToken,
Authorization: `Bearer ${accessToken}`,
"Content-Type": "application/json",
},
body: _translateEvent(event, "updates"),

View File

@ -2,6 +2,7 @@
items:
- 1.jpg
---
Vital App is an app that can can help you combine your health peripherals with your calendar.
#### Supported Actions:

View File

@ -45,7 +45,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const token = await vitalClient.Link.create(
userVital?.user_id,
undefined,
WEBAPP_URL + "/api/integrations/vital/callback"
`${WEBAPP_URL}/api/integrations/vital/callback`
);
return res.status(200).json({
token: token.link_token,

View File

@ -15,7 +15,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
/** @link https://developer.webex.com/docs/integrations#getting-an-access-token **/
const redirectUri = encodeURI(`${WEBAPP_URL}/api/integrations/${config.slug}/callback`);
const authHeader = "Basic " + Buffer.from(client_id + ":" + client_secret).toString("base64");
const authHeader = `Basic ${Buffer.from(`${client_id}:${client_secret}`).toString("base64")}`;
const result = await fetch(
`https://webexapis.com/v1/access_token?grant_type=authorization_code&client_id=${client_id}&client_secret=${client_secret}&code=${code}&redirect_uri=${redirectUri}`,
{

View File

@ -154,7 +154,7 @@ const WebexVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
method: "GET",
...options,
headers: {
Authorization: "Bearer " + accessToken,
Authorization: `Bearer ${accessToken}`,
...options?.headers,
},
});
@ -208,7 +208,7 @@ const WebexVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
url: result.webLink,
};
}
throw new Error("Failed to create meeting. Response is " + JSON.stringify(result));
throw new Error(`Failed to create meeting. Response is ${JSON.stringify(result)}`);
} catch (err) {
console.error(err);
throw new Error("Unexpected error");
@ -258,7 +258,7 @@ const WebexVideoApiAdapter = (credential: CredentialPayload): VideoApiAdapter =>
url: result.webLink,
};
}
throw new Error("Failed to create meeting. Response is " + JSON.stringify(result));
throw new Error(`Failed to create meeting. Response is ${JSON.stringify(result)}`);
} catch (err) {
console.error(err);
throw new Error("Unexpected error");

View File

@ -27,7 +27,7 @@ const wipeMyCalAction = async (props: IWipeMyCalAction) => {
};
try {
const endpoint = "/api/integrations/wipemycalother/wipe";
return fetch(`${process.env.NEXT_PUBLIC_WEBAPP_URL}` + endpoint, {
return fetch(`${process.env.NEXT_PUBLIC_WEBAPP_URL}${endpoint}`, {
method: "POST",
headers: {
"Content-Type": "application/json",

View File

@ -26,7 +26,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
? authorizedAccount.name
: null;
const requested = teamInfo ? "team: " + teamInfo : "user: " + userInfo;
const requested = teamInfo ? `team: ${teamInfo}` : `user: ${userInfo}`;
return res.status(404).json({
message: `There are no bookings to retrieve, please create a booking first. Requested: \`${requested}\``,
});

View File

@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const clientId = typeof appKeys.client_id === "string" ? appKeys.client_id : "";
if (!clientId) return res.status(400).json({ message: "Zoho Bigin client_id missing." });
const redirectUri = WEBAPP_URL + `/api/integrations/zoho-bigin/callback`;
const redirectUri = `${WEBAPP_URL}/api/integrations/zoho-bigin/callback`;
const authUrl = axios.getUri({
url: "https://accounts.zoho.com/oauth/v2/auth",

View File

@ -33,7 +33,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
if (!clientSecret) return res.status(400).json({ message: "Zoho Bigin client_secret missing." });
const accountsUrl = `${accountsServer}/oauth/v2/token`;
const redirectUri = WEBAPP_URL + `/api/integrations/${appConfig.slug}/callback`;
const redirectUri = `${WEBAPP_URL}/api/integrations/${appConfig.slug}/callback`;
const formData = {
client_id: clientId,

View File

@ -148,8 +148,9 @@ export default class BiginCalendarService implements Calendar {
*/
private async contactSearch(event: CalendarEvent) {
const token = await this.auth.getToken();
const searchCriteria =
"(" + event.attendees.map((attendee) => `(Email:equals:${encodeURI(attendee.email)})`).join("or") + ")";
const searchCriteria = `(${event.attendees
.map((attendee) => `(Email:equals:${encodeURI(attendee.email)})`)
.join("or")})`;
return await axios({
method: "get",
@ -298,21 +299,9 @@ const toISO8601String = (date: Date) => {
return (num < 10 ? "0" : "") + num;
};
return (
date.getFullYear() +
"-" +
pad(date.getMonth() + 1) +
"-" +
pad(date.getDate()) +
"T" +
pad(date.getHours()) +
":" +
pad(date.getMinutes()) +
":" +
pad(date.getSeconds()) +
dif +
pad(Math.floor(Math.abs(tzo) / 60)) +
":" +
pad(Math.abs(tzo) % 60)
);
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(
date.getHours()
)}:${pad(date.getMinutes())}:${pad(date.getSeconds())}${dif}${pad(Math.floor(Math.abs(tzo) / 60))}:${pad(
Math.abs(tzo) % 60
)}`;
};

Some files were not shown because too many files have changed in this diff Show More