Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ffebdba44 | ||
|
|
cc1101ac43 | ||
|
|
7a80a5f929 | ||
|
|
a27e1498b8 | ||
|
|
a697857fa0 | ||
|
|
e2f45bdf5a | ||
|
|
d2930bc243 | ||
|
|
b0b93f612e | ||
|
|
558c8be740 | ||
|
|
1aee1742d5 | ||
|
|
ab942ed4cd | ||
|
|
b5bec79354 | ||
|
|
b48d0e1f48 | ||
|
|
4a38b4dc89 | ||
|
|
ff12e387b3 | ||
|
|
6b5e144042 | ||
|
|
cd7b93a51e | ||
|
|
a76366cbcb | ||
|
|
860c04bbda | ||
|
+6 |
225313e391 | ||
|
|
3994929a51 | ||
|
|
ae38027d76 | ||
|
|
9036080fcf | ||
|
|
e6ef4e1988 | ||
|
|
3c2619a7a3 | ||
|
|
cfb09a1b21 | ||
|
|
5fe4e09975 | ||
|
|
542799e2cf | ||
|
|
3eda7858c0 | ||
|
|
b59bf7d1ee | ||
|
|
0dadeac352 | ||
|
|
5eb7a4d56e | ||
|
|
43fa944e2f | ||
|
|
1866b2845c | ||
|
|
2cfc63e6e3 | ||
|
|
4f66f3d2c6 | ||
|
|
ca3e7bd97d | ||
|
|
cafa3f9cf3 | ||
|
|
e04faffcbb | ||
|
|
6f1556a2fd | ||
|
|
d72a954896 | ||
|
|
abeb20373c | ||
|
|
422ef2f5b8 | ||
|
|
ec920eb2a8 |
+3
-1
@@ -112,6 +112,9 @@ NEXT_PUBLIC_POSTHOG_HOST=
|
||||
PLAIN_API_KEY=
|
||||
PLAIN_API_URL=https://api.plain.com/v1
|
||||
PLAIN_HMAC_SECRET_KEY=
|
||||
PLAIN_CHAT_ID=
|
||||
PLAIN_CHAT_HMAC_SECRET_KEY=
|
||||
NEXT_PUBLIC_PLAIN_CHAT_EXCLUDED_PATHS=
|
||||
|
||||
# Zendesk Config
|
||||
NEXT_PUBLIC_ZENDESK_KEY=
|
||||
@@ -361,7 +364,6 @@ APP_ROUTER_APPS_SLUG_SETUP_ENABLED=0
|
||||
APP_ROUTER_APPS_CATEGORIES_ENABLED=0
|
||||
# whether we redirect to the future/apps/categories/[category] from /apps/categories/[category] or not
|
||||
APP_ROUTER_APPS_CATEGORIES_CATEGORY_ENABLED=0
|
||||
APP_ROUTER_BOOKINGS_STATUS_ENABLED=0
|
||||
APP_ROUTER_APPS_ENABLED=0
|
||||
APP_ROUTER_TEAM_ENABLED=0
|
||||
APP_ROUTER_AUTH_FORGOT_PASSWORD_ENABLED=0
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
undecided:
|
||||
- calcom-monorepo
|
||||
- "@calcom/app-store-cli"
|
||||
- "@calcom/platform-constants"
|
||||
- "@calcom/platform-enums"
|
||||
- "@calcom/platform-types"
|
||||
- "@calcom/platform-utils"
|
||||
- "@calcom/prisma"
|
||||
Vendored
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
undecided:
|
||||
- "@calcom/prisma"
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
undecided:
|
||||
- calcom-monorepo
|
||||
+18
-1
@@ -34,6 +34,7 @@ import {
|
||||
UpdateEventTypeInput_2024_06_14,
|
||||
GetEventTypesQuery_2024_06_14,
|
||||
CreateEventTypeInput_2024_06_14,
|
||||
EventTypeOutput_2024_06_14,
|
||||
} from "@calcom/platform-types";
|
||||
|
||||
@Controller({
|
||||
@@ -113,10 +114,26 @@ export class EventTypesController_2024_06_14 {
|
||||
@Query() queryParams: GetEventTypesQuery_2024_06_14
|
||||
): Promise<GetEventTypesOutput_2024_06_14> {
|
||||
const eventTypes = await this.eventTypesService.getEventTypes(queryParams);
|
||||
const eventTypesFormatted = this.eventTypeResponseTransformPipe.transform(eventTypes);
|
||||
const eventTypesWithoutHiddenFields = eventTypesFormatted.map((eventType) => {
|
||||
return {
|
||||
...eventType,
|
||||
bookingFields: Array.isArray(eventType?.bookingFields)
|
||||
? eventType?.bookingFields
|
||||
.map((field) => {
|
||||
if ("hidden" in field) {
|
||||
return field.hidden !== true ? field : null;
|
||||
}
|
||||
return field;
|
||||
})
|
||||
.filter((f) => f)
|
||||
: [],
|
||||
};
|
||||
}) as EventTypeOutput_2024_06_14[];
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: this.eventTypeResponseTransformPipe.transform(eventTypes),
|
||||
data: eventTypesWithoutHiddenFields,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,6 @@ const ROUTES: [URLPattern, boolean][] = [
|
||||
["/auth/error", process.env.APP_ROUTER_AUTH_ERROR_ENABLED === "1"] as const,
|
||||
["/auth/platform/:path*", process.env.APP_ROUTER_AUTH_PLATFORM_ENABLED === "1"] as const,
|
||||
["/auth/oauth2/:path*", process.env.APP_ROUTER_AUTH_OAUTH2_ENABLED === "1"] as const,
|
||||
["/bookings/:status", process.env.APP_ROUTER_BOOKINGS_STATUS_ENABLED === "1"] as const,
|
||||
["/team", process.env.APP_ROUTER_TEAM_ENABLED === "1"] as const,
|
||||
].map(([pathname, enabled]) => [
|
||||
new URLPattern({
|
||||
|
||||
@@ -1,24 +1,31 @@
|
||||
import type { GetStaticProps, GetStaticPropsContext } from "next";
|
||||
import { unstable_cache } from "next/cache";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
|
||||
export const withAppDirSsg =
|
||||
<T extends Record<string, any>>(getStaticProps: GetStaticProps<T>) =>
|
||||
<T extends Record<string, any>>(getStaticProps: GetStaticProps<T>, routePath: string) =>
|
||||
async (context: GetStaticPropsContext) => {
|
||||
const ssgResponse = await getStaticProps(context);
|
||||
const cacheKey = JSON.stringify({
|
||||
route: routePath,
|
||||
params: context.params || {},
|
||||
});
|
||||
|
||||
if ("redirect" in ssgResponse) {
|
||||
redirect(ssgResponse.redirect.destination);
|
||||
}
|
||||
const getCachedProps = unstable_cache(async () => {
|
||||
const ssgResponse = await getStaticProps(context);
|
||||
|
||||
if ("notFound" in ssgResponse) {
|
||||
notFound();
|
||||
}
|
||||
if ("redirect" in ssgResponse) {
|
||||
redirect(ssgResponse.redirect.destination);
|
||||
}
|
||||
|
||||
const props = await Promise.resolve(ssgResponse.props);
|
||||
if ("notFound" in ssgResponse) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return {
|
||||
...ssgResponse.props,
|
||||
// includes dehydratedState required for future page trpcPropvider
|
||||
...("trpcState" in props && { dehydratedState: props.trpcState }),
|
||||
};
|
||||
return {
|
||||
...ssgResponse.props,
|
||||
...("trpcState" in ssgResponse.props && { dehydratedState: ssgResponse.props.trpcState }),
|
||||
};
|
||||
}, [`ssg-${cacheKey}`]);
|
||||
|
||||
return getCachedProps();
|
||||
};
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
import type { Request, Response } from "express";
|
||||
import type { NextApiRequest, NextApiResponse, Redirect } from "next";
|
||||
import { redirect, notFound } from "next/navigation";
|
||||
import { createMocks } from "node-mocks-http";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import withEmbedSsrAppDir from "./WithEmbedSSR";
|
||||
|
||||
export type CustomNextApiRequest = NextApiRequest & Request;
|
||||
export type CustomNextApiResponse = NextApiResponse & Response;
|
||||
|
||||
export function createMockNextJsRequest(...args: Parameters<typeof createMocks>) {
|
||||
return createMocks<CustomNextApiRequest, CustomNextApiResponse>(...args);
|
||||
}
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
redirect: vi.fn(),
|
||||
notFound: vi.fn(),
|
||||
}));
|
||||
|
||||
function getServerSidePropsFnGenerator(
|
||||
config:
|
||||
| { redirectUrl: string }
|
||||
| { props: Record<string, unknown> }
|
||||
| {
|
||||
notFound: true;
|
||||
}
|
||||
) {
|
||||
if ("redirectUrl" in config)
|
||||
return async () => {
|
||||
return {
|
||||
redirect: {
|
||||
permanent: false,
|
||||
destination: config.redirectUrl,
|
||||
} satisfies Redirect,
|
||||
};
|
||||
};
|
||||
|
||||
if ("props" in config)
|
||||
return async () => {
|
||||
return {
|
||||
props: config.props,
|
||||
};
|
||||
};
|
||||
|
||||
if ("notFound" in config)
|
||||
return async () => {
|
||||
return {
|
||||
notFound: true as const,
|
||||
};
|
||||
};
|
||||
|
||||
throw new Error("Invalid config");
|
||||
}
|
||||
|
||||
interface ServerSidePropsContext {
|
||||
embedRelatedParams?: Record<string, string>;
|
||||
}
|
||||
|
||||
function getServerSidePropsContextArg({ embedRelatedParams = {} }: ServerSidePropsContext) {
|
||||
const { req, res } = createMockNextJsRequest();
|
||||
return {
|
||||
req,
|
||||
res,
|
||||
query: {
|
||||
...embedRelatedParams,
|
||||
},
|
||||
resolvedUrl: "/MOCKED_RESOLVED_URL",
|
||||
};
|
||||
}
|
||||
|
||||
describe("withEmbedSsrAppDir", () => {
|
||||
describe("when gSSP returns redirect", () => {
|
||||
describe("when redirect destination is relative", () => {
|
||||
it("should redirect with layout and embed params from the current query", async () => {
|
||||
const withEmbedGetSsr = withEmbedSsrAppDir(
|
||||
getServerSidePropsFnGenerator({
|
||||
redirectUrl: "/reschedule",
|
||||
})
|
||||
);
|
||||
|
||||
await withEmbedGetSsr(
|
||||
getServerSidePropsContextArg({
|
||||
embedRelatedParams: {
|
||||
layout: "week_view",
|
||||
embed: "namespace1",
|
||||
},
|
||||
})
|
||||
).catch(() => {});
|
||||
|
||||
expect(redirect).toHaveBeenCalledWith("/reschedule/embed?layout=week_view&embed=namespace1");
|
||||
});
|
||||
|
||||
it("should preserve existing query params in redirect URL", async () => {
|
||||
const withEmbedGetSsr = withEmbedSsrAppDir(
|
||||
getServerSidePropsFnGenerator({
|
||||
redirectUrl: "/reschedule?redirectParam=1",
|
||||
})
|
||||
);
|
||||
|
||||
await withEmbedGetSsr(
|
||||
getServerSidePropsContextArg({
|
||||
embedRelatedParams: {
|
||||
layout: "week_view",
|
||||
embed: "namespace1",
|
||||
},
|
||||
})
|
||||
).catch(() => {});
|
||||
|
||||
expect(redirect).toHaveBeenCalledWith(
|
||||
"/reschedule/embed?redirectParam=1&layout=week_view&embed=namespace1"
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle empty embed namespace", async () => {
|
||||
const withEmbedGetSsr = withEmbedSsrAppDir(
|
||||
getServerSidePropsFnGenerator({
|
||||
redirectUrl: "/reschedule?redirectParam=1",
|
||||
})
|
||||
);
|
||||
|
||||
await withEmbedGetSsr(
|
||||
getServerSidePropsContextArg({
|
||||
embedRelatedParams: {
|
||||
layout: "week_view",
|
||||
embed: "",
|
||||
},
|
||||
})
|
||||
).catch(() => {});
|
||||
|
||||
expect(redirect).toHaveBeenCalledWith("/reschedule/embed?redirectParam=1&layout=week_view&embed=");
|
||||
});
|
||||
});
|
||||
|
||||
describe("when redirect destination is absolute", () => {
|
||||
it("should handle HTTPS URLs", async () => {
|
||||
const withEmbedGetSsr = withEmbedSsrAppDir(
|
||||
getServerSidePropsFnGenerator({
|
||||
redirectUrl: "https://calcom.cal.local/owner",
|
||||
})
|
||||
);
|
||||
|
||||
await withEmbedGetSsr(
|
||||
getServerSidePropsContextArg({
|
||||
embedRelatedParams: {
|
||||
layout: "week_view",
|
||||
embed: "namespace1",
|
||||
},
|
||||
})
|
||||
).catch(() => {});
|
||||
|
||||
expect(redirect).toHaveBeenCalledWith(
|
||||
"https://calcom.cal.local/owner/embed?layout=week_view&embed=namespace1"
|
||||
);
|
||||
});
|
||||
|
||||
it("should handle HTTP URLs", async () => {
|
||||
const withEmbedGetSsr = withEmbedSsrAppDir(
|
||||
getServerSidePropsFnGenerator({
|
||||
redirectUrl: "http://calcom.cal.local/owner",
|
||||
})
|
||||
);
|
||||
|
||||
await withEmbedGetSsr(
|
||||
getServerSidePropsContextArg({
|
||||
embedRelatedParams: {
|
||||
layout: "week_view",
|
||||
embed: "namespace1",
|
||||
},
|
||||
})
|
||||
).catch(() => {});
|
||||
|
||||
expect(redirect).toHaveBeenCalledWith(
|
||||
"http://calcom.cal.local/owner/embed?layout=week_view&embed=namespace1"
|
||||
);
|
||||
});
|
||||
|
||||
it("should treat URLs without protocol as relative", async () => {
|
||||
const withEmbedGetSsr = withEmbedSsrAppDir(
|
||||
getServerSidePropsFnGenerator({
|
||||
redirectUrl: "calcom.cal.local/owner",
|
||||
})
|
||||
);
|
||||
|
||||
await withEmbedGetSsr(
|
||||
getServerSidePropsContextArg({
|
||||
embedRelatedParams: {
|
||||
layout: "week_view",
|
||||
embed: "namespace1",
|
||||
},
|
||||
})
|
||||
).catch(() => {});
|
||||
|
||||
expect(redirect).toHaveBeenCalledWith(
|
||||
"/calcom.cal.local/owner/embed?layout=week_view&embed=namespace1"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when gSSP returns props", () => {
|
||||
it("should add isEmbed=true prop", async () => {
|
||||
const withEmbedGetSsr = withEmbedSsrAppDir(
|
||||
getServerSidePropsFnGenerator({
|
||||
props: {
|
||||
prop1: "value1",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const ret = await withEmbedGetSsr(
|
||||
getServerSidePropsContextArg({
|
||||
embedRelatedParams: {
|
||||
layout: "week_view",
|
||||
embed: "",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
expect(ret).toEqual({
|
||||
prop1: "value1",
|
||||
isEmbed: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("when gSSP returns notFound", () => {
|
||||
it("should throw notFound", async () => {
|
||||
const withEmbedGetSsr = withEmbedSsrAppDir(
|
||||
getServerSidePropsFnGenerator({
|
||||
notFound: true,
|
||||
})
|
||||
);
|
||||
|
||||
await withEmbedGetSsr(
|
||||
getServerSidePropsContextArg({
|
||||
embedRelatedParams: {
|
||||
layout: "week_view",
|
||||
embed: "",
|
||||
},
|
||||
})
|
||||
).catch(() => {});
|
||||
|
||||
expect(notFound).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,4 @@
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import { isNotFoundError } from "next/dist/client/components/not-found";
|
||||
import { getURLFromRedirectError, isRedirectError } from "next/dist/client/components/redirect";
|
||||
import type { GetServerSideProps, GetServerSidePropsContext } from "next";
|
||||
import { notFound, redirect } from "next/navigation";
|
||||
|
||||
import { WebAppURL } from "@calcom/lib/WebAppURL";
|
||||
@@ -9,48 +7,46 @@ export type EmbedProps = {
|
||||
isEmbed?: boolean;
|
||||
};
|
||||
|
||||
export default function withEmbedSsrAppDir<T extends Record<string, any>>(
|
||||
getData: (context: GetServerSidePropsContext) => Promise<T>
|
||||
) {
|
||||
return async (context: GetServerSidePropsContext): Promise<T> => {
|
||||
const withEmbedSsrAppDir =
|
||||
<T extends Record<string, any>>(getServerSideProps: GetServerSideProps<T>) =>
|
||||
async (context: GetServerSidePropsContext): Promise<T> => {
|
||||
const { embed, layout } = context.query;
|
||||
try {
|
||||
const props = await getData(context);
|
||||
const ssrResponse = await getServerSideProps(context);
|
||||
|
||||
return {
|
||||
...props,
|
||||
isEmbed: true,
|
||||
};
|
||||
} catch (e) {
|
||||
if (isRedirectError(e)) {
|
||||
const destinationUrl = getURLFromRedirectError(e);
|
||||
let urlPrefix = "";
|
||||
if ("redirect" in ssrResponse) {
|
||||
const destinationUrl = ssrResponse.redirect.destination;
|
||||
let urlPrefix = "";
|
||||
|
||||
// Get the URL parsed from URL so that we can reliably read pathname and searchParams from it.
|
||||
const destinationUrlObj = new WebAppURL(destinationUrl);
|
||||
// Get the URL parsed from URL so that we can reliably read pathname and searchParams from it.
|
||||
const destinationUrlObj = new WebAppURL(ssrResponse.redirect.destination);
|
||||
|
||||
// If it's a complete URL, use the origin as the prefix to ensure we redirect to the same domain.
|
||||
if (destinationUrl.search(/^(http:|https:).*/) !== -1) {
|
||||
urlPrefix = destinationUrlObj.origin;
|
||||
} else {
|
||||
// Don't use any prefix for relative URLs to ensure we stay on the same domain
|
||||
urlPrefix = "";
|
||||
}
|
||||
|
||||
const destinationQueryStr = destinationUrlObj.searchParams.toString();
|
||||
// Make sure that redirect happens to /embed page and pass on embed query param as is for preserving Cal JS API namespace
|
||||
const newDestinationUrl = `${urlPrefix}${destinationUrlObj.pathname}/embed?${
|
||||
destinationQueryStr ? `${destinationQueryStr}&` : ""
|
||||
}layout=${layout}&embed=${embed}`;
|
||||
|
||||
redirect(newDestinationUrl);
|
||||
// If it's a complete URL, use the origin as the prefix to ensure we redirect to the same domain.
|
||||
if (destinationUrl.search(/^(http:|https:).*/) !== -1) {
|
||||
urlPrefix = destinationUrlObj.origin;
|
||||
} else {
|
||||
// Don't use any prefix for relative URLs to ensure we stay on the same domain
|
||||
urlPrefix = "";
|
||||
}
|
||||
|
||||
if (isNotFoundError(e)) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
throw e;
|
||||
const destinationQueryStr = destinationUrlObj.searchParams.toString();
|
||||
// Make sure that redirect happens to /embed page and pass on embed query param as is for preserving Cal JS API namespace
|
||||
const newDestinationUrl = `${urlPrefix}${destinationUrlObj.pathname}/embed?${
|
||||
destinationQueryStr ? `${destinationQueryStr}&` : ""
|
||||
}layout=${layout}&embed=${embed}`;
|
||||
redirect(newDestinationUrl);
|
||||
}
|
||||
|
||||
if ("notFound" in ssrResponse) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
return {
|
||||
...ssrResponse.props,
|
||||
...("trpcState" in ssrResponse.props && {
|
||||
dehydratedState: ssrResponse.props.trpcState,
|
||||
}),
|
||||
isEmbed: true,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default withEmbedSsrAppDir;
|
||||
|
||||
+53
-30
@@ -4,27 +4,39 @@ import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { headers } from "next/headers";
|
||||
|
||||
import { constructGenericImage } from "@calcom/lib/OgImages";
|
||||
import { IS_CALCOM, WEBAPP_URL, APP_NAME, SEO_IMG_OGIMG } from "@calcom/lib/constants";
|
||||
import { IS_CALCOM, WEBAPP_URL, APP_NAME, SEO_IMG_OGIMG, CAL_URL } from "@calcom/lib/constants";
|
||||
import { buildCanonical } from "@calcom/lib/next-seo.config";
|
||||
import { truncateOnWord } from "@calcom/lib/text";
|
||||
//@ts-expect-error no type definitions
|
||||
import config from "@calcom/web/next-i18next.config";
|
||||
|
||||
const create = async (locale: string, ns: string) => {
|
||||
const i18nInstanceCache: Record<string, any> = {};
|
||||
|
||||
const createI18nInstance = async (locale: string, ns: string) => {
|
||||
const cacheKey = `${locale}-${ns}`;
|
||||
// Check module-level cache first
|
||||
if (i18nInstanceCache[cacheKey]) {
|
||||
return i18nInstanceCache[cacheKey];
|
||||
}
|
||||
|
||||
const { _nextI18Next } = await serverSideTranslations(locale, [ns], config);
|
||||
|
||||
const _i18n = i18next.createInstance();
|
||||
_i18n.init({
|
||||
await _i18n.init({
|
||||
lng: locale,
|
||||
resources: _nextI18Next?.initialI18nStore,
|
||||
fallbackLng: _nextI18Next?.userConfig?.i18n.defaultLocale,
|
||||
});
|
||||
|
||||
// Cache the instance
|
||||
i18nInstanceCache[cacheKey] = _i18n;
|
||||
return _i18n;
|
||||
};
|
||||
|
||||
export const getFixedT = async (locale: string, ns = "common") => {
|
||||
const i18n = await create(locale, ns);
|
||||
|
||||
return i18n.getFixedT(locale, ns);
|
||||
const getTranslationWithCache = async (locale: string, ns: string = "common") => {
|
||||
const localeWithFallback = locale ?? "en";
|
||||
const i18n = await createI18nInstance(localeWithFallback, ns);
|
||||
return i18n.getFixedT(localeWithFallback, ns);
|
||||
};
|
||||
|
||||
export const getTranslate = async () => {
|
||||
@@ -32,51 +44,62 @@ export const getTranslate = async () => {
|
||||
// If "x-locale" does not exist in header,
|
||||
// ensure that config.matcher in middleware includes the page you are testing
|
||||
const locale = headersList.get("x-locale");
|
||||
const t = await getFixedT(locale ?? "en");
|
||||
const t = await getTranslationWithCache(locale ?? "en");
|
||||
return t;
|
||||
};
|
||||
|
||||
export const _generateMetadata = async (
|
||||
const _generateMetadataWithoutImage = async (
|
||||
getTitle: (t: TFunction<string, undefined>) => string,
|
||||
getDescription: (t: TFunction<string, undefined>) => string,
|
||||
excludeAppNameFromTitle?: boolean
|
||||
hideBranding?: boolean,
|
||||
origin?: string
|
||||
) => {
|
||||
const h = headers();
|
||||
const canonical = h.get("x-pathname") ?? "";
|
||||
const pathname = h.get("x-pathname") ?? "";
|
||||
const canonical = buildCanonical({ path: pathname, origin: origin ?? CAL_URL });
|
||||
const locale = h.get("x-locale") ?? "en";
|
||||
|
||||
const t = await getFixedT(locale, "common");
|
||||
const t = await getTranslationWithCache(locale);
|
||||
|
||||
const title = getTitle(t);
|
||||
const description = getDescription(t);
|
||||
|
||||
const metadataBase = new URL(IS_CALCOM ? "https://cal.com" : WEBAPP_URL);
|
||||
|
||||
const image =
|
||||
SEO_IMG_OGIMG +
|
||||
constructGenericImage({
|
||||
title,
|
||||
description,
|
||||
});
|
||||
|
||||
const titleSuffix = `| ${APP_NAME}`;
|
||||
const displayedTitle =
|
||||
title.includes(titleSuffix) || excludeAppNameFromTitle ? title : `${title} ${titleSuffix}`;
|
||||
const displayedTitle = title.includes(titleSuffix) || hideBranding ? title : `${title} ${titleSuffix}`;
|
||||
const metadataBase = new URL(IS_CALCOM ? "https://cal.com" : WEBAPP_URL);
|
||||
|
||||
return {
|
||||
title: title.length === 0 ? APP_NAME : displayedTitle,
|
||||
description,
|
||||
alternates: {
|
||||
canonical,
|
||||
},
|
||||
alternates: { canonical },
|
||||
openGraph: {
|
||||
description: truncateOnWord(description, 158),
|
||||
url: canonical,
|
||||
type: "website",
|
||||
siteName: APP_NAME,
|
||||
title,
|
||||
images: [image],
|
||||
title: displayedTitle,
|
||||
},
|
||||
metadataBase,
|
||||
};
|
||||
};
|
||||
|
||||
export const _generateMetadata = async (
|
||||
getTitle: (t: TFunction<string, undefined>) => string,
|
||||
getDescription: (t: TFunction<string, undefined>) => string,
|
||||
hideBranding?: boolean,
|
||||
origin?: string
|
||||
) => {
|
||||
const metadata = await _generateMetadataWithoutImage(getTitle, getDescription, hideBranding, origin);
|
||||
const image =
|
||||
SEO_IMG_OGIMG +
|
||||
constructGenericImage({
|
||||
title: metadata.title,
|
||||
description: metadata.description,
|
||||
});
|
||||
|
||||
return {
|
||||
...metadata,
|
||||
openGraph: {
|
||||
...metadata.openGraph,
|
||||
images: [image],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -26,6 +26,7 @@ export async function handler(request: Request) {
|
||||
|
||||
// HMAC verification
|
||||
const incomingSignature = headersList.get("plain-request-signature");
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const expectedSignature = createHmac("sha-256", process.env.PLAIN_HMAC_SECRET_KEY!)
|
||||
.update(JSON.stringify(requestBody))
|
||||
.digest("hex");
|
||||
@@ -88,18 +89,8 @@ export async function handler(request: Request) {
|
||||
rowAsideContent: [
|
||||
{
|
||||
componentBadge: {
|
||||
badgeLabel:
|
||||
customer.emailVerified === undefined
|
||||
? "Unknown"
|
||||
: customer.emailVerified
|
||||
? "Yes"
|
||||
: "No",
|
||||
badgeColor:
|
||||
customer.emailVerified === undefined
|
||||
? "YELLOW"
|
||||
: customer.emailVerified
|
||||
? "GREEN"
|
||||
: "RED",
|
||||
badgeLabel: customer.emailVerified ? "Yes" : "No",
|
||||
badgeColor: customer.emailVerified ? "GREEN" : "RED",
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -195,18 +186,8 @@ export async function handler(request: Request) {
|
||||
rowAsideContent: [
|
||||
{
|
||||
componentBadge: {
|
||||
badgeLabel:
|
||||
customer.twoFactorEnabled === undefined
|
||||
? "Unknown"
|
||||
: customer.twoFactorEnabled
|
||||
? "Yes"
|
||||
: "No",
|
||||
badgeColor:
|
||||
customer.twoFactorEnabled === undefined
|
||||
? "YELLOW"
|
||||
: customer.twoFactorEnabled
|
||||
? "GREEN"
|
||||
: "RED",
|
||||
badgeLabel: customer.twoFactorEnabled ? "Yes" : "No",
|
||||
badgeColor: customer.twoFactorEnabled ? "GREEN" : "RED",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
import { createHmac } from "crypto";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { apiRouteMiddleware } from "@calcom/lib/server/apiRouteMiddleware";
|
||||
|
||||
const responseSchema = z.object({
|
||||
hash: z.string(),
|
||||
email: z.string().email(),
|
||||
shortName: z.string(),
|
||||
appId: z.string(),
|
||||
fullName: z.string(),
|
||||
chatAvatarUrl: z.string(),
|
||||
});
|
||||
|
||||
async function handler(request: Request) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const session = await getServerSession({ req: request as any });
|
||||
if (!session?.user?.email) {
|
||||
return new Response("Unauthorized - No session email found", { status: 401 });
|
||||
}
|
||||
|
||||
const secret = process.env.PLAIN_CHAT_HMAC_SECRET_KEY;
|
||||
if (!secret) {
|
||||
return new Response("Missing Plain Chat secret", { status: 500 });
|
||||
}
|
||||
|
||||
const hmac = createHmac("sha256", secret);
|
||||
hmac.update(session.user.email.toLowerCase().trim());
|
||||
const hash = hmac.digest("hex");
|
||||
|
||||
const shortName =
|
||||
(session.user.name?.split(" ")[0] || session.user.email).charAt(0).toUpperCase() +
|
||||
(session.user.name?.split(" ")[0] || session.user.email).slice(1) || "User";
|
||||
|
||||
const response = responseSchema.parse({
|
||||
hash,
|
||||
email: session.user.email || "user@example.com",
|
||||
shortName,
|
||||
appId: process.env.PLAIN_CHAT_ID,
|
||||
fullName: session.user.name || "User",
|
||||
chatAvatarUrl: session.user.avatarUrl || "",
|
||||
});
|
||||
|
||||
return NextResponse.json(response);
|
||||
}
|
||||
|
||||
export const POST = apiRouteMiddleware(handler);
|
||||
@@ -1,12 +1,9 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import OldPage from "~/bookings/views/bookings-single-view";
|
||||
import { getServerSideProps, type PageProps } from "~/bookings/views/bookings-single-view.getServerSideProps";
|
||||
|
||||
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: OldPage });
|
||||
|
||||
@@ -4,6 +4,7 @@ import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { cookies, headers } from "next/headers";
|
||||
|
||||
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
@@ -12,7 +13,7 @@ import OldPage from "~/bookings/views/bookings-single-view";
|
||||
import { getServerSideProps, type PageProps } from "~/bookings/views/bookings-single-view.getServerSideProps";
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
||||
const { bookingInfo, eventType, recurringBookings } = await getData(
|
||||
const { bookingInfo, eventType, recurringBookings, orgSlug } = await getData(
|
||||
buildLegacyCtx(headers(), cookies(), params, searchParams)
|
||||
);
|
||||
const needsConfirmation = bookingInfo.status === BookingStatus.PENDING && eventType.requiresConfirmation;
|
||||
@@ -21,7 +22,9 @@ export const generateMetadata = async ({ params, searchParams }: _PageProps) =>
|
||||
(t) =>
|
||||
t(`booking_${needsConfirmation ? "submitted" : "confirmed"}${recurringBookings ? "_recurring" : ""}`),
|
||||
(t) =>
|
||||
t(`booking_${needsConfirmation ? "submitted" : "confirmed"}${recurringBookings ? "_recurring" : ""}`)
|
||||
t(`booking_${needsConfirmation ? "submitted" : "confirmed"}${recurringBookings ? "_recurring" : ""}`),
|
||||
false,
|
||||
getOrgFullOrigin(orgSlug)
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import BookingDryRunSuccessView from "~/bookings/views/booking-dry-run-success-view";
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
return await _generateMetadata(
|
||||
(t) => t("booking_dry_run_successful"),
|
||||
(t) => t("booking_dry_run_successful_description")
|
||||
);
|
||||
};
|
||||
|
||||
const Page = async () => {
|
||||
return <BookingDryRunSuccessView />;
|
||||
};
|
||||
|
||||
export default WithLayout({ ServerPage: Page });
|
||||
@@ -0,0 +1,33 @@
|
||||
import { PageProps } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { redirect } from "next/navigation";
|
||||
import { z } from "zod";
|
||||
|
||||
import { validStatuses } from "~/bookings/lib/validStatuses";
|
||||
import BookingsList from "~/bookings/views/bookings-listing-view";
|
||||
|
||||
const querySchema = z.object({
|
||||
status: z.enum(validStatuses),
|
||||
});
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("bookings"),
|
||||
(t) => t("bookings_description")
|
||||
);
|
||||
|
||||
export const generateStaticParams = async () => {
|
||||
return validStatuses.map((status) => ({ status }));
|
||||
};
|
||||
|
||||
const Page = async ({ params, searchParams }: PageProps) => {
|
||||
const parsed = querySchema.safeParse({ ...params, ...searchParams });
|
||||
if (!parsed.success) {
|
||||
redirect("/bookings/upcoming");
|
||||
}
|
||||
|
||||
return <BookingsList status={parsed.data.status} />;
|
||||
};
|
||||
|
||||
export default WithLayout({ ServerPage: Page })<"P">;
|
||||
@@ -1,4 +1,3 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
@@ -6,10 +5,6 @@ import { getServerSideProps } from "@server/lib/[user]/[type]/getServerSideProps
|
||||
|
||||
import LegacyPage, { type PageProps } from "~/users/views/users-type-public-view";
|
||||
|
||||
export { generateMetadata } from "../page";
|
||||
|
||||
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: LegacyPage })<"P">;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
@@ -7,10 +6,6 @@ import { getServerSideProps } from "@server/lib/[user]/getServerSideProps";
|
||||
import type { PageProps as UserPageProps } from "~/users/views/users-public-view";
|
||||
import LegacyPage from "~/users/views/users-public-view";
|
||||
|
||||
export { generateMetadata } from "../page";
|
||||
|
||||
const getData = withAppDirSsr<UserPageProps>(getServerSideProps);
|
||||
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir<UserPageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: LegacyPage })<"P">;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
import type { PageProps } from "~/apps/[slug]/slug-view";
|
||||
import Page from "~/apps/[slug]/slug-view";
|
||||
|
||||
const getData = withAppDirSsg<PageProps>(getStaticProps);
|
||||
const getData = withAppDirSsg<PageProps>(getStaticProps, "future/apps/[slug]");
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
||||
const legacyContext = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||
|
||||
@@ -28,7 +28,7 @@ export const generateStaticParams = async () => {
|
||||
return paths.map((category) => ({ category }));
|
||||
};
|
||||
|
||||
const getData = withAppDirSsg<PageProps>(getStaticProps);
|
||||
const getData = withAppDirSsg<PageProps>(getStaticProps, "future/apps/categories/[category]");
|
||||
|
||||
export default WithLayout({ getData, Page: CategoryPage, getLayout: null })<"P">;
|
||||
export const dynamic = "force-static";
|
||||
|
||||
@@ -13,7 +13,7 @@ export const generateMetadata = async () => {
|
||||
);
|
||||
};
|
||||
|
||||
const getData = withAppDirSsg(getStaticProps);
|
||||
const getData = withAppDirSsg(getStaticProps, "future/auth/error");
|
||||
|
||||
export default WithLayout({ getData, Page, getLayout: null })<"P">;
|
||||
export const dynamic = "force-static";
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import { withAppDirSsg } from "app/WithAppDirSsg";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import type { InferGetStaticPropsType } from "next";
|
||||
|
||||
import { validStatuses } from "~/bookings/lib/validStatuses";
|
||||
import Page from "~/bookings/views/bookings-listing-view";
|
||||
import { getStaticProps } from "~/bookings/views/bookings-listing-view.getStaticProps";
|
||||
|
||||
type Y = InferGetStaticPropsType<typeof getStaticProps>;
|
||||
const getData = withAppDirSsg<Y>(getStaticProps);
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("bookings"),
|
||||
(t) => t("bookings_description")
|
||||
);
|
||||
|
||||
export const generateStaticParams = async () => {
|
||||
return validStatuses.map((status) => ({ status }));
|
||||
};
|
||||
|
||||
export default WithLayout({ getLayout: null, getData, Page })<"P">;
|
||||
|
||||
export const dynamic = "force-static";
|
||||
@@ -1,12 +1,10 @@
|
||||
import { type PageProps } from "@pages/org/[orgSlug]/[user]/[type]";
|
||||
import Page from "@pages/org/[orgSlug]/[user]/[type]/embed";
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getServerSideProps } from "@lib/org/[orgSlug]/[user]/[type]/getServerSideProps";
|
||||
|
||||
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({ getLayout: null, getData: getEmbedData, isBookingPage: true, Page });
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
import { getServerSideProps, type PageProps } from "@pages/org/[orgSlug]/[user]";
|
||||
import Page from "@pages/org/[orgSlug]/[user]/embed";
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({ getLayout: null, getData: getEmbedData, isBookingPage: true, Page });
|
||||
|
||||
@@ -1,28 +1,14 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import type { PageProps as _PageProps } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { cookies, headers } from "next/headers";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
|
||||
|
||||
import type { PageProps } from "~/team/team-view";
|
||||
import TeamPage from "~/team/team-view";
|
||||
|
||||
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
||||
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
|
||||
|
||||
return await _generateMetadata(
|
||||
(t) => props.team.name || t("nameless_team"),
|
||||
(t) => props.team.name || t("nameless_team")
|
||||
);
|
||||
};
|
||||
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({
|
||||
Page: TeamPage,
|
||||
|
||||
@@ -1,43 +1,13 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import type { PageProps as _PageProps } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { cookies, headers } from "next/headers";
|
||||
|
||||
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { EventRepository } from "@calcom/lib/server/repository/event";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
|
||||
|
||||
import type { PageProps } from "~/team/type-view";
|
||||
import LegacyPage from "~/team/type-view";
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
||||
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||
const props = await getData(legacyCtx);
|
||||
const { user: username, slug: eventSlug, booking } = props;
|
||||
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(legacyCtx.req, legacyCtx.params?.orgSlug);
|
||||
|
||||
const event = await EventRepository.getPublicEvent({
|
||||
username,
|
||||
eventSlug,
|
||||
isTeamEvent: true,
|
||||
org: isValidOrgDomain ? currentOrgDomain : null,
|
||||
fromRedirectOfNonOrgLink: legacyCtx.query.orgRedirection === "true",
|
||||
});
|
||||
|
||||
const profileName = event?.profile?.name ?? "";
|
||||
const title = event?.title ?? "";
|
||||
|
||||
return await _generateMetadata(
|
||||
(t) => `${booking?.uid && !!booking ? t("reschedule") : ""} ${title} | ${profileName}`,
|
||||
(t) => `${booking?.uid ? t("reschedule") : ""} ${title}`
|
||||
);
|
||||
};
|
||||
|
||||
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: LegacyPage })<"P">;
|
||||
|
||||
@@ -3,24 +3,12 @@ import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import type { PageProps as _PageProps } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { cookies, headers } from "next/headers";
|
||||
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
|
||||
|
||||
import type { PageProps } from "~/team/team-view";
|
||||
import LegacyPage from "~/team/team-view";
|
||||
|
||||
export const generateMetadata = async ({ params, searchParams }: _PageProps) => {
|
||||
const props = await getData(buildLegacyCtx(headers(), cookies(), params, searchParams));
|
||||
|
||||
return await _generateMetadata(
|
||||
(t) => props.team.name || t("nameless_team"),
|
||||
(t) => props.team.name || t("nameless_team")
|
||||
);
|
||||
};
|
||||
|
||||
const getData = withAppDirSsr<PageProps>(getServerSideProps);
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir<PageProps>(getServerSideProps);
|
||||
|
||||
export default WithLayout({ getLayout: null, getData: getEmbedData, Page: LegacyPage })<"P">;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getServerSideProps } from "@lib/insights/getServerSideProps";
|
||||
|
||||
import InsightsVirtualQueuesPage from "~/insights/insights-virtual-queues-view";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("insights"),
|
||||
(t) => t("insights_subtitle")
|
||||
);
|
||||
|
||||
const getData = withAppDirSsr(getServerSideProps);
|
||||
|
||||
export default WithLayout({ getLayout: null, getData, Page: InsightsVirtualQueuesPage });
|
||||
@@ -21,15 +21,7 @@ const calFont = localFont({
|
||||
weight: "600",
|
||||
});
|
||||
|
||||
export const generateMetadata = () =>
|
||||
prepareRootMetadata({
|
||||
twitterCreator: "@calcom",
|
||||
twitterSite: "@calcom",
|
||||
robots: {
|
||||
index: false,
|
||||
follow: false,
|
||||
},
|
||||
});
|
||||
export const generateMetadata = () => prepareRootMetadata();
|
||||
|
||||
const getInitialProps = async (url: string) => {
|
||||
const { pathname, searchParams } = new URL(url);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { withAppDirSsr } from "app/WithAppDirSsr";
|
||||
import withEmbedSsrAppDir from "app/WithEmbedSSR";
|
||||
import type { PageProps } from "app/_types";
|
||||
import { cookies, headers } from "next/headers";
|
||||
@@ -6,8 +5,7 @@ import { cookies, headers } from "next/headers";
|
||||
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
import { getServerSideProps } from "@lib/reschedule/[uid]/getServerSideProps";
|
||||
|
||||
const getData = withAppDirSsr(getServerSideProps);
|
||||
const getEmbedData = withEmbedSsrAppDir(getData);
|
||||
const getEmbedData = withEmbedSsrAppDir(getServerSideProps);
|
||||
|
||||
const Page = async ({ params, searchParams }: PageProps) => {
|
||||
const legacyCtx = buildLegacyCtx(headers(), cookies(), params, searchParams);
|
||||
|
||||
@@ -17,12 +17,12 @@ import CacheProvider from "react-inlinesvg/provider";
|
||||
import DynamicPostHogProvider from "@calcom/features/ee/event-tracking/lib/posthog/providerDynamic";
|
||||
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
|
||||
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
|
||||
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
|
||||
import { FeatureProvider } from "@calcom/features/flags/context/provider";
|
||||
import { useFlags } from "@calcom/features/flags/hooks";
|
||||
import { MetaProvider } from "@calcom/ui";
|
||||
|
||||
import useIsBookingPage from "@lib/hooks/useIsBookingPage";
|
||||
import PlainChat from "@lib/plain/plainChat";
|
||||
import type { WithLocaleProps } from "@lib/withLocale";
|
||||
import type { WithNonceProps } from "@lib/withNonce";
|
||||
|
||||
@@ -265,9 +265,11 @@ function OrgBrandProvider({ children }: { children: React.ReactNode }) {
|
||||
const AppProviders = (props: PageWrapperProps) => {
|
||||
// No need to have intercom on public pages - Good for Page Performance
|
||||
const isBookingPage = useIsBookingPage();
|
||||
|
||||
const RemainingProviders = (
|
||||
<EventCollectionProvider options={{ apiPath: "/api/collect-events" }}>
|
||||
<SessionProvider>
|
||||
<PlainChat />
|
||||
<CustomI18nextProvider i18n={props.i18n}>
|
||||
<TooltipProvider>
|
||||
{/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */}
|
||||
@@ -301,11 +303,11 @@ const AppProviders = (props: PageWrapperProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DynamicHelpscoutProvider>
|
||||
<DynamicIntercomProvider>
|
||||
<>
|
||||
<DynamicHelpscoutProvider>
|
||||
<DynamicPostHogProvider>{Hydrated}</DynamicPostHogProvider>
|
||||
</DynamicIntercomProvider>
|
||||
</DynamicHelpscoutProvider>
|
||||
</DynamicHelpscoutProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@ import CacheProvider from "react-inlinesvg/provider";
|
||||
import DynamicPostHogProvider from "@calcom/features/ee/event-tracking/lib/posthog/providerDynamic";
|
||||
import { OrgBrandingProvider } from "@calcom/features/ee/organizations/context/provider";
|
||||
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
|
||||
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
|
||||
import { FeatureProvider } from "@calcom/features/flags/context/provider";
|
||||
import { useFlags } from "@calcom/features/flags/hooks";
|
||||
import { MetaProvider } from "@calcom/ui";
|
||||
|
||||
import useIsBookingPage from "@lib/hooks/useIsBookingPage";
|
||||
import PlainChat from "@lib/plain/plainChat";
|
||||
import type { WithLocaleProps } from "@lib/withLocale";
|
||||
import type { WithNonceProps } from "@lib/withNonce";
|
||||
|
||||
@@ -282,7 +282,6 @@ function OrgBrandProvider({ children }: { children: React.ReactNode }) {
|
||||
}
|
||||
|
||||
const AppProviders = (props: AppPropsWithChildren) => {
|
||||
// No need to have intercom on public pages - Good for Page Performance
|
||||
const isBookingPage = useIsBookingPage();
|
||||
const { pageProps, ...rest } = props;
|
||||
|
||||
@@ -298,9 +297,9 @@ const AppProviders = (props: AppPropsWithChildren) => {
|
||||
const RemainingProviders = (
|
||||
<EventCollectionProvider options={{ apiPath: "/api/collect-events" }}>
|
||||
<SessionProvider session={pageProps.session ?? undefined}>
|
||||
<PlainChat />
|
||||
<CustomI18nextProvider {...propsWithoutNonce}>
|
||||
<TooltipProvider>
|
||||
{/* color-scheme makes background:transparent not work which is required by embed. We need to ensure next-theme adds color-scheme to `body` instead of `html`(https://github.com/pacocoursey/next-themes/blob/main/src/index.tsx#L74). Once that's done we can enable color-scheme support */}
|
||||
<CalcomThemeProvider
|
||||
themeBasis={props.pageProps.themeBasis}
|
||||
nonce={props.pageProps.nonce}
|
||||
@@ -327,14 +326,14 @@ const AppProviders = (props: AppPropsWithChildren) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DynamicHelpscoutProvider>
|
||||
<DynamicIntercomProvider>
|
||||
<>
|
||||
<DynamicHelpscoutProvider>
|
||||
<DynamicPostHogProvider>
|
||||
<PostHogPageView />
|
||||
{RemainingProviders}
|
||||
</DynamicPostHogProvider>
|
||||
</DynamicIntercomProvider>
|
||||
</DynamicHelpscoutProvider>
|
||||
</DynamicHelpscoutProvider>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { RedirectType } from "@calcom/prisma/enums";
|
||||
|
||||
import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect";
|
||||
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import type { EmbedProps } from "@lib/withEmbedSsr";
|
||||
import type { EmbedProps } from "app/WithEmbedSSR";
|
||||
|
||||
export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
|
||||
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
import type { Metadata } from "next";
|
||||
|
||||
type RootMetadataRecipe = Readonly<{
|
||||
twitterCreator: string;
|
||||
twitterSite: string;
|
||||
robots: {
|
||||
index: boolean;
|
||||
follow: boolean;
|
||||
};
|
||||
}>;
|
||||
|
||||
export type PageMetadataRecipe = Readonly<{
|
||||
title: string;
|
||||
canonical: string;
|
||||
@@ -18,7 +9,7 @@ export type PageMetadataRecipe = Readonly<{
|
||||
metadataBase: URL;
|
||||
}>;
|
||||
|
||||
export const prepareRootMetadata = (recipe: RootMetadataRecipe): Metadata => ({
|
||||
export const prepareRootMetadata = (): Metadata => ({
|
||||
icons: {
|
||||
icon: "/favicon.ico",
|
||||
apple: "/api/logo?type=apple-touch-icon",
|
||||
@@ -48,7 +39,6 @@ export const prepareRootMetadata = (recipe: RootMetadataRecipe): Metadata => ({
|
||||
userScalable: false,
|
||||
viewportFit: "cover",
|
||||
},
|
||||
robots: recipe.robots,
|
||||
other: {
|
||||
"application-TileColor": "#ff0000",
|
||||
},
|
||||
@@ -63,8 +53,12 @@ export const prepareRootMetadata = (recipe: RootMetadataRecipe): Metadata => ({
|
||||
},
|
||||
],
|
||||
twitter: {
|
||||
site: recipe.twitterSite,
|
||||
creator: recipe.twitterCreator,
|
||||
site: "@calcom",
|
||||
creator: "@calcom",
|
||||
card: "summary_large_image",
|
||||
},
|
||||
robots: {
|
||||
index: true,
|
||||
follow: true,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,281 @@
|
||||
"use client";
|
||||
|
||||
import { useSession } from "next-auth/react";
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
import Script from "next/script";
|
||||
import { useEffect, useState, useCallback, useMemo } from "react";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
Plain?: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
init: (config: any) => void;
|
||||
open: () => void;
|
||||
};
|
||||
plainScriptLoaded?: () => void;
|
||||
__PLAIN_CONFIG__?: PlainChatConfig;
|
||||
}
|
||||
}
|
||||
|
||||
interface PlainChatConfig {
|
||||
appId: string;
|
||||
customerDetails: {
|
||||
email: string;
|
||||
emailHash: string;
|
||||
fullName: string;
|
||||
shortName: string;
|
||||
chatAvatarUrl: string;
|
||||
};
|
||||
links: Array<{
|
||||
icon: string;
|
||||
text: string;
|
||||
url: string;
|
||||
}>;
|
||||
chatButtons: Array<{
|
||||
icon: string;
|
||||
text: string;
|
||||
type: string;
|
||||
form?: {
|
||||
fields: Array<{
|
||||
type: string;
|
||||
placeholder: string;
|
||||
options: Array<{
|
||||
icon: string;
|
||||
text: string;
|
||||
threadDetails: {
|
||||
severity: string;
|
||||
labelTypeIds: Array<string>;
|
||||
issueType: string;
|
||||
priority: string;
|
||||
};
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
}>;
|
||||
entryPoint: {
|
||||
type: string;
|
||||
};
|
||||
hideBranding: boolean;
|
||||
theme: string;
|
||||
style: {
|
||||
brandColor: string;
|
||||
launcherBackgroundColor: string;
|
||||
launcherIconColor: string;
|
||||
};
|
||||
position: {
|
||||
bottom: string;
|
||||
right: string;
|
||||
};
|
||||
}
|
||||
|
||||
const PlainChat = () => {
|
||||
const [config, setConfig] = useState<PlainChatConfig | null>(null);
|
||||
const [isSmallScreen, setIsSmallScreen] = useState(false);
|
||||
const { data: session } = useSession();
|
||||
const pathname = usePathname();
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
const shouldOpenPlain = pathname === "/event-types" && searchParams?.has("openPlain");
|
||||
const userEmail = session?.user?.email;
|
||||
|
||||
const isAppDomain = useMemo(() => {
|
||||
const restrictedPaths = process.env.NEXT_PUBLIC_PLAIN_CHAT_EXCLUDED_PATHS?.split(",") || [];
|
||||
return (
|
||||
typeof window !== "undefined" &&
|
||||
window.location.origin === process.env.NEXT_PUBLIC_WEBAPP_URL &&
|
||||
!restrictedPaths.some((path) => pathname?.startsWith(path.trim()))
|
||||
);
|
||||
}, [pathname]);
|
||||
|
||||
const checkScreenSize = useCallback(() => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
const isSmall = window.innerWidth < 768;
|
||||
setIsSmallScreen(isSmall);
|
||||
|
||||
if (isSmall && window.Plain) {
|
||||
const plainElement = document.querySelector("#plain-container");
|
||||
plainElement?.remove();
|
||||
window.Plain = undefined;
|
||||
} else if (!isSmall && window.Plain === undefined) {
|
||||
window.plainScriptLoaded?.();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const initConfig = useCallback(async () => {
|
||||
if (!userEmail) return;
|
||||
|
||||
try {
|
||||
const response = await fetch("/api/plain-hash", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const errorText = await response.text();
|
||||
throw new Error(`Failed to generate hash: ${errorText}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!data.hash || !data.email || !data.appId) {
|
||||
throw new Error("Missing required fields in API response");
|
||||
}
|
||||
|
||||
const plainChatConfig: PlainChatConfig = {
|
||||
appId: data.appId,
|
||||
customerDetails: {
|
||||
email: data.email,
|
||||
shortName: data.shortName,
|
||||
fullName: data.fullName,
|
||||
emailHash: data.hash,
|
||||
chatAvatarUrl: data.chatAvatarUrl,
|
||||
},
|
||||
links: [
|
||||
{
|
||||
icon: "book",
|
||||
text: "Documentation",
|
||||
url: "https://cal.com/docs",
|
||||
},
|
||||
{
|
||||
icon: "chat",
|
||||
text: "Ask the community",
|
||||
url: "https://github.com/calcom/cal.com/discussions",
|
||||
},
|
||||
],
|
||||
chatButtons: [
|
||||
{
|
||||
icon: "chat",
|
||||
text: "Ask a question",
|
||||
type: "primary",
|
||||
},
|
||||
{
|
||||
icon: "bulb",
|
||||
text: "Send feedback",
|
||||
type: "default",
|
||||
},
|
||||
{
|
||||
icon: "error",
|
||||
text: "Report an issue",
|
||||
type: "default",
|
||||
form: {
|
||||
fields: [
|
||||
{
|
||||
type: "dropdown",
|
||||
placeholder: "Select severity...",
|
||||
options: [
|
||||
{
|
||||
icon: "support",
|
||||
text: "I'm unable to use the app",
|
||||
threadDetails: {
|
||||
severity: "critical",
|
||||
issueType: "critical",
|
||||
labelTypeIds: ["lt_01JFJWNWAC464N8DZ6YE71YJRF"],
|
||||
priority: "u",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "error",
|
||||
text: "Major functionality degraded",
|
||||
threadDetails: {
|
||||
severity: "major",
|
||||
issueType: "major",
|
||||
labelTypeIds: ["lt_01JFJWP3KECF1YQES6XF212RFW"],
|
||||
priority: "h",
|
||||
},
|
||||
},
|
||||
{
|
||||
icon: "bug",
|
||||
text: "Minor annoyance",
|
||||
threadDetails: {
|
||||
severity: "minor",
|
||||
issueType: "minor",
|
||||
labelTypeIds: ["lt_01JFJWPC8ADW0PK28JHMJR6NSS"],
|
||||
priority: "l",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
entryPoint: {
|
||||
type: "chat",
|
||||
},
|
||||
hideBranding: true,
|
||||
theme: "auto",
|
||||
style: {
|
||||
brandColor: "#FFFFFF",
|
||||
launcherBackgroundColor: "#262626",
|
||||
launcherIconColor: "#FFFFFF",
|
||||
},
|
||||
position: {
|
||||
bottom: "20px",
|
||||
right: "20px",
|
||||
},
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") {
|
||||
window.__PLAIN_CONFIG__ = plainChatConfig;
|
||||
}
|
||||
|
||||
setConfig(plainChatConfig);
|
||||
|
||||
if (shouldOpenPlain) {
|
||||
const timer = setTimeout(() => {
|
||||
if (window.Plain) {
|
||||
window.Plain.open();
|
||||
}
|
||||
}, 100);
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize Plain Chat:", error);
|
||||
}
|
||||
}, [userEmail, shouldOpenPlain]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAppDomain) return;
|
||||
|
||||
checkScreenSize();
|
||||
window.addEventListener("resize", checkScreenSize);
|
||||
initConfig();
|
||||
|
||||
return () => window.removeEventListener("resize", checkScreenSize);
|
||||
}, [isAppDomain, checkScreenSize, initConfig, userEmail]);
|
||||
|
||||
const plainChatScript = `
|
||||
window.plainScriptLoaded = function() {
|
||||
if (window.Plain && ${Boolean(config)}) {
|
||||
try {
|
||||
Plain.init(${config ? JSON.stringify(config) : null});
|
||||
} catch (error) {
|
||||
console.error("Failed to initialize Plain:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
if (!isAppDomain || isSmallScreen || !config || typeof window === "undefined") return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Script
|
||||
id="plain-chat"
|
||||
src="https://chat.cdn-plain.com/index.js"
|
||||
strategy="afterInteractive"
|
||||
onLoad={() => window.plainScriptLoaded?.()}
|
||||
/>
|
||||
<Script
|
||||
id="plain-chat-init"
|
||||
strategy="afterInteractive"
|
||||
dangerouslySetInnerHTML={{ __html: plainChatScript }}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default PlainChat;
|
||||
@@ -4,6 +4,7 @@ import { URLSearchParams } from "url";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { getSafe } from "@calcom/lib/bookingSuccessRedirect";
|
||||
import { buildEventUrlFromBooking } from "@calcom/lib/bookings/buildEventUrlFromBooking";
|
||||
import { getDefaultEvent } from "@calcom/lib/defaultEvents";
|
||||
import { maybeGetBookingUidFromSeat } from "@calcom/lib/server/maybeGetBookingUidFromSeat";
|
||||
@@ -35,10 +36,11 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
} = querySchema.parse(context.query);
|
||||
|
||||
const coepFlag = context.query["flag.coep"];
|
||||
const { uid, seatReferenceUid: maybeSeatReferenceUid } = await maybeGetBookingUidFromSeat(
|
||||
prisma,
|
||||
bookingUid
|
||||
);
|
||||
const {
|
||||
uid,
|
||||
seatReferenceUid: maybeSeatReferenceUid,
|
||||
bookingSeat,
|
||||
} = await maybeGetBookingUidFromSeat(prisma, bookingUid);
|
||||
|
||||
const booking = await prisma.booking.findUnique({
|
||||
where: {
|
||||
@@ -46,6 +48,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
},
|
||||
select: {
|
||||
...bookingMinimalSelect,
|
||||
responses: true,
|
||||
eventType: {
|
||||
select: {
|
||||
users: {
|
||||
@@ -129,9 +132,18 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
||||
|
||||
const isBookingInPast = booking.endTime && new Date(booking.endTime) < new Date();
|
||||
if (isBookingInPast) {
|
||||
const destinationUrlSearchParams = new URLSearchParams();
|
||||
const responses = bookingSeat ? getSafe<string>(bookingSeat.data, ["responses"]) : booking.responses;
|
||||
const name = getSafe<string>(responses, ["name"]);
|
||||
const email = getSafe<string>(responses, ["email"]);
|
||||
|
||||
if (name) destinationUrlSearchParams.set("name", name);
|
||||
if (email) destinationUrlSearchParams.set("email", email);
|
||||
|
||||
const searchParamsString = destinationUrlSearchParams.toString();
|
||||
return {
|
||||
redirect: {
|
||||
destination: eventUrl,
|
||||
destination: searchParamsString ? `${eventUrl}?${searchParamsString}` : eventUrl,
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -187,7 +187,6 @@ export const config = {
|
||||
"/getting-started/:path*",
|
||||
"/apps",
|
||||
"/bookings/:status/",
|
||||
"/future/bookings/:status/",
|
||||
"/video/:path*",
|
||||
"/teams",
|
||||
"/future/teams/",
|
||||
|
||||
@@ -18,6 +18,7 @@ import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { Team } from "@calcom/prisma/client";
|
||||
import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
||||
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils";
|
||||
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { AppMeta } from "@calcom/types/App";
|
||||
@@ -243,15 +244,20 @@ const OnboardingPage = ({
|
||||
const promises = group.eventTypes
|
||||
.filter((eventType) => eventType.selected)
|
||||
.map((value: TEventType) => {
|
||||
// Prevent two payment apps to be enabled
|
||||
// Ok to cast type here because this metadata will be updated as the event type metadata
|
||||
if (
|
||||
checkForMultiplePaymentApps(value.metadata as z.infer<typeof EventTypeMetaDataSchema>)
|
||||
)
|
||||
throw new Error(t("event_setup_multiple_payment_apps_error"));
|
||||
if (value.metadata?.apps?.stripe?.paymentOption === "HOLD" && value.seatsPerTimeSlot) {
|
||||
throw new Error(t("seats_and_no_show_fee_error"));
|
||||
if (value.metadata) {
|
||||
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(value.metadata);
|
||||
// Prevent two payment apps to be enabled
|
||||
// Ok to cast type here because this metadata will be updated as the event type metadata
|
||||
if (checkForMultiplePaymentApps(metadata))
|
||||
throw new Error(t("event_setup_multiple_payment_apps_error"));
|
||||
if (
|
||||
value.metadata?.apps?.stripe?.paymentOption === "HOLD" &&
|
||||
value.seatsPerTimeSlot
|
||||
) {
|
||||
throw new Error(t("seats_and_no_show_fee_error"));
|
||||
}
|
||||
}
|
||||
|
||||
let updateObject: TUpdateObject = { id: value.id };
|
||||
if (isConferencing) {
|
||||
updateObject = {
|
||||
|
||||
@@ -282,7 +282,7 @@ PageProps & WithNonceProps<{}>) {
|
||||
disabled={formState.isSubmitting}
|
||||
className="w-full justify-center">
|
||||
<span>{twoFactorRequired ? t("submit") : t("sign_in")}</span>
|
||||
{lastUsed === "credentials" && <LastUsed className="text-gray-600" />}
|
||||
{lastUsed === "credentials" && !twoFactorRequired && <LastUsed className="text-gray-600" />}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import { Suspense } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { HeadSeo, Loader } from "@calcom/ui";
|
||||
import { Loader } from "@calcom/ui";
|
||||
|
||||
const TroubleshooterClientOnly = dynamic(
|
||||
() => import("@calcom/features/troubleshooter/Troubleshooter").then((mod) => mod.Troubleshooter),
|
||||
@@ -14,10 +13,8 @@ const TroubleshooterClientOnly = dynamic(
|
||||
);
|
||||
|
||||
function TroubleshooterPage() {
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<>
|
||||
<HeadSeo title={t("troubleshoot")} description={t("troubleshoot_availability")} />
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex h-full w-full items-center justify-center">
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
"use client";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Icon } from "@calcom/ui";
|
||||
|
||||
export default function DryRunSuccessful() {
|
||||
const { t } = useLocale();
|
||||
|
||||
const sampleBookingInfo = {
|
||||
startTime: "2024-01-20T10:00:00Z",
|
||||
endTime: "2024-01-20T11:00:00Z",
|
||||
title: "Sample Meeting",
|
||||
description: "This is a sample dry run booking",
|
||||
attendees: [
|
||||
{
|
||||
email: "attendee@example.com",
|
||||
name: "Sample Attendee",
|
||||
timeZone: "America/New_York",
|
||||
},
|
||||
],
|
||||
user: {
|
||||
name: "Sample Host",
|
||||
email: "host@example.com",
|
||||
},
|
||||
location: "Zoom Meeting",
|
||||
status: "ACCEPTED",
|
||||
uid: "sample-uid",
|
||||
};
|
||||
|
||||
const sampleEventType = {
|
||||
title: "Sample Event Type",
|
||||
length: 60,
|
||||
eventName: "Sample Meeting",
|
||||
description: "This is a sample event type for dry run",
|
||||
requiresConfirmation: false,
|
||||
recurringEvent: null,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-screen">
|
||||
<div className="bg-default m-auto rounded-md p-10 text-center">
|
||||
<div className="bg-success mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-full">
|
||||
<Icon name="check" className="h-6 w-6 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
<h1 className="text-emphasis mb-4 text-2xl font-medium">{t("booking_dry_run_successful")}</h1>
|
||||
<p className="text-default mb-8 max-w-2xl text-sm">{t("booking_dry_run_successful_description")}</p>
|
||||
|
||||
{/* <div className="border-subtle text-default mt-8 grid grid-cols-3 border-t pt-8 text-left">
|
||||
<div className="font-medium">{t("what")}</div>
|
||||
<div className="col-span-2 mb-6">{sampleBookingInfo.title}</div>
|
||||
|
||||
<div className="font-medium">{t("when")}</div>
|
||||
<div className="col-span-2 mb-6">{new Date(sampleBookingInfo.startTime).toLocaleString()}</div>
|
||||
|
||||
<div className="font-medium">{t("who")}</div>
|
||||
<div className="col-span-2 mb-6">
|
||||
<div className="mb-3">
|
||||
<div>
|
||||
<span className="mr-2">{sampleBookingInfo.user.name}</span>
|
||||
<Badge variant="blue">{t("Host")}</Badge>
|
||||
</div>
|
||||
<p className="text-default">{sampleBookingInfo.user.email}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p>{sampleBookingInfo.attendees[0].name}</p>
|
||||
<p>{sampleBookingInfo.attendees[0].email}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="font-medium">{t("where")}</div>
|
||||
<div className="col-span-2 mb-6">{sampleBookingInfo.location}</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
import { type GetStaticProps } from "next";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getTranslations } from "@server/lib/getTranslations";
|
||||
|
||||
import { validStatuses } from "~/bookings/lib/validStatuses";
|
||||
|
||||
const querySchema = z.object({
|
||||
status: z.enum(validStatuses),
|
||||
});
|
||||
|
||||
export const getStaticProps: GetStaticProps = async (ctx) => {
|
||||
const params = querySchema.safeParse(ctx.params);
|
||||
const i18n = await getTranslations(ctx);
|
||||
|
||||
if (!params.success) return { notFound: true };
|
||||
|
||||
return {
|
||||
props: {
|
||||
status: params.data.status,
|
||||
i18n,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -13,7 +13,6 @@ import { useFilterQuery } from "@calcom/features/bookings/lib/useFilterQuery";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { useInViewObserver } from "@calcom/lib/hooks/useInViewObserver";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { HorizontalTabItemProps, VerticalTabItemProps } from "@calcom/ui";
|
||||
@@ -67,17 +66,13 @@ const descriptionByStatus: Record<NonNullable<BookingListingStatus>, string> = {
|
||||
unconfirmed: "unconfirmed_bookings",
|
||||
};
|
||||
|
||||
const querySchema = z.object({
|
||||
status: z.enum(validStatuses),
|
||||
});
|
||||
|
||||
export default function Bookings() {
|
||||
const params = useParamsWithFallback();
|
||||
export default function Bookings({ status }: { status: (typeof validStatuses)[number] }) {
|
||||
const { data: filterQuery } = useFilterQuery();
|
||||
const { status } = params ? querySchema.parse(params) : { status: "upcoming" as const };
|
||||
|
||||
const { t } = useLocale();
|
||||
const user = useMeQuery().data;
|
||||
const [isFiltersVisible, setIsFiltersVisible] = useState<boolean>(false);
|
||||
|
||||
const query = trpc.viewer.bookings.get.useInfiniteQuery(
|
||||
{
|
||||
limit: 10,
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
import { render } from "@testing-library/react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import React from "react";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import type { z } from "zod";
|
||||
|
||||
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
||||
import { BookingStatus } from "@calcom/prisma/enums";
|
||||
import { HeadSeo } from "@calcom/ui";
|
||||
|
||||
import Success from "./bookings-single-view";
|
||||
|
||||
function mockedSuccessComponentProps(props: Partial<React.ComponentProps<typeof Success>>) {
|
||||
return {
|
||||
eventType: {
|
||||
id: 1,
|
||||
title: "Event Title",
|
||||
description: "",
|
||||
locations: null,
|
||||
length: 15,
|
||||
userId: null,
|
||||
eventName: "d",
|
||||
timeZone: null,
|
||||
recurringEvent: null,
|
||||
requiresConfirmation: false,
|
||||
disableGuests: false,
|
||||
seatsPerTimeSlot: null,
|
||||
seatsShowAttendees: null,
|
||||
seatsShowAvailabilityCount: null,
|
||||
schedulingType: null,
|
||||
price: 0,
|
||||
currency: "usd",
|
||||
successRedirectUrl: null,
|
||||
customInputs: [],
|
||||
teamId: null,
|
||||
team: null,
|
||||
workflows: [],
|
||||
hosts: [],
|
||||
users: [],
|
||||
owner: null,
|
||||
isDynamic: false,
|
||||
periodStartDate: "1",
|
||||
periodEndDate: "1",
|
||||
metadata: null,
|
||||
bookingFields: [] as unknown as [] & z.BRAND<"HAS_SYSTEM_FIELDS">,
|
||||
},
|
||||
profile: {
|
||||
name: "John",
|
||||
email: null,
|
||||
theme: null,
|
||||
brandColor: null,
|
||||
darkBrandColor: null,
|
||||
slug: null,
|
||||
},
|
||||
bookingInfo: {
|
||||
uid: "uid",
|
||||
metadata: null,
|
||||
customInputs: [],
|
||||
startTime: new Date(),
|
||||
endTime: new Date(),
|
||||
id: 1,
|
||||
user: null,
|
||||
eventType: null,
|
||||
seatsReferences: [],
|
||||
userPrimaryEmail: null,
|
||||
eventTypeId: null,
|
||||
title: "Booking Title",
|
||||
description: null,
|
||||
location: null,
|
||||
recurringEventId: null,
|
||||
smsReminderNumber: "0",
|
||||
cancellationReason: null,
|
||||
rejectionReason: null,
|
||||
status: BookingStatus.ACCEPTED,
|
||||
attendees: [],
|
||||
responses: {
|
||||
name: "John",
|
||||
},
|
||||
rescheduled: false,
|
||||
fromReschedule: null,
|
||||
},
|
||||
orgSlug: null,
|
||||
userTimeFormat: 12,
|
||||
requiresLoginToUpdate: false,
|
||||
themeBasis: "dark",
|
||||
hideBranding: false,
|
||||
recurringBookings: null,
|
||||
trpcState: {
|
||||
queries: [],
|
||||
mutations: [],
|
||||
},
|
||||
dynamicEventName: "Event Title",
|
||||
paymentStatus: null,
|
||||
rescheduledToUid: null,
|
||||
...props,
|
||||
} satisfies React.ComponentProps<typeof Success>;
|
||||
}
|
||||
|
||||
describe("Success Component", () => {
|
||||
it("renders HeadSeo correctly", () => {
|
||||
vi.mocked(getOrgFullOrigin).mockImplementation((text: string | null) => `${text}.cal.local`);
|
||||
vi.mocked(useRouterQuery).mockReturnValue({
|
||||
uid: "uid",
|
||||
});
|
||||
vi.mock("@calcom/lib/constants", async (importOriginal) => {
|
||||
const actual = await importOriginal<any>();
|
||||
return {
|
||||
...actual,
|
||||
CURRENT_TIMEZONE: "Europe/London",
|
||||
};
|
||||
});
|
||||
vi.mocked(useSession).mockReturnValue({
|
||||
update: vi.fn(),
|
||||
status: "authenticated",
|
||||
data: {
|
||||
hasValidLicense: true,
|
||||
upId: "1",
|
||||
expires: "1",
|
||||
user: {
|
||||
name: "John",
|
||||
id: 1,
|
||||
profile: {
|
||||
id: null,
|
||||
upId: "1",
|
||||
username: null,
|
||||
organizationId: null,
|
||||
organization: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const mockObject = {
|
||||
props: mockedSuccessComponentProps({
|
||||
orgSlug: "org1",
|
||||
}),
|
||||
};
|
||||
|
||||
render(<Success {...mockObject.props} />);
|
||||
|
||||
const expectedTitle = `booking_confirmed`;
|
||||
const expectedDescription = expectedTitle;
|
||||
expect(HeadSeo).toHaveBeenCalledWith(
|
||||
{
|
||||
origin: `${mockObject.props.orgSlug}.cal.local`,
|
||||
title: expectedTitle,
|
||||
description: expectedDescription,
|
||||
},
|
||||
{}
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -48,7 +48,7 @@ import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
||||
import { getIs24hClockFromLocalStorage, isBrowserLocale24h } from "@calcom/lib/timeFormat";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
import { BookingStatus, SchedulingType } from "@calcom/prisma/enums";
|
||||
import { bookingMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { bookingMetadataSchema, eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import {
|
||||
Alert,
|
||||
@@ -56,12 +56,12 @@ import {
|
||||
Badge,
|
||||
Button,
|
||||
EmailInput,
|
||||
HeadSeo,
|
||||
useCalcomTheme,
|
||||
TextArea,
|
||||
showToast,
|
||||
EmptyScreen,
|
||||
Icon,
|
||||
HeadSeo,
|
||||
} from "@calcom/ui";
|
||||
import CancelBooking from "@calcom/web/components/booking/CancelBooking";
|
||||
import EventReservationSchema from "@calcom/web/components/schemas/EventReservationSchema";
|
||||
@@ -230,7 +230,13 @@ export default function Success(props: PageProps) {
|
||||
t,
|
||||
};
|
||||
|
||||
const giphyAppData = getEventTypeAppData(eventType, "giphy");
|
||||
const giphyAppData = getEventTypeAppData(
|
||||
{
|
||||
...eventType,
|
||||
metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata),
|
||||
},
|
||||
"giphy"
|
||||
);
|
||||
const giphyImage = giphyAppData?.thankYouPage;
|
||||
const isRoundRobin = eventType.schedulingType === SchedulingType.ROUND_ROBIN;
|
||||
|
||||
@@ -435,7 +441,9 @@ export default function Success(props: PageProps) {
|
||||
</div>
|
||||
)}
|
||||
<HeadSeo origin={getOrgFullOrigin(orgSlug)} title={title} description={title} />
|
||||
<BookingPageTagManager eventType={eventType} />
|
||||
<BookingPageTagManager
|
||||
eventType={{ ...eventType, metadata: eventTypeMetaDataSchemaWithTypedApps.parse(eventType.metadata) }}
|
||||
/>
|
||||
<main className={classNames(shouldAlignCentrally ? "mx-auto" : "", isEmbed ? "" : "max-w-3xl")}>
|
||||
<div className={classNames("overflow-y-auto", isEmbed ? "" : "z-50 ")}>
|
||||
<div
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
|
||||
import { TestForm } from "@calcom/app-store/routing-forms/components/SingleForm";
|
||||
import type { RoutingForm } from "@calcom/app-store/routing-forms/types/types";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc";
|
||||
import { Label, Select } from "@calcom/ui";
|
||||
|
||||
import InsightsLayout from "./layout";
|
||||
|
||||
export default function InsightsVirtualQueuesPage() {
|
||||
const { t } = useLocale();
|
||||
const { data: routingForms, isLoading: isRoutingFormsLoading } =
|
||||
trpc.viewer.insights.getUserRelevantTeamRoutingForms.useQuery();
|
||||
|
||||
const [selectedForm, setSelectedForm] = useState<RoutingForm | undefined>(
|
||||
routingForms && routingForms.length > 0 ? routingForms[0] : undefined
|
||||
);
|
||||
|
||||
if (routingForms && !selectedForm && routingForms.length > 0) {
|
||||
setSelectedForm(routingForms[0]);
|
||||
}
|
||||
|
||||
return (
|
||||
<InsightsLayout>
|
||||
<Label>{t("routing_form")}</Label>
|
||||
<Select
|
||||
placeholder="Select project"
|
||||
options={routingForms?.map((form) => ({ label: form.name, value: form.id })) ?? []}
|
||||
isLoading={isRoutingFormsLoading}
|
||||
className="w-60"
|
||||
onChange={(e) => {
|
||||
if (e && routingForms) {
|
||||
const form = routingForms.find((form) => form.id === e.value);
|
||||
setSelectedForm(form);
|
||||
}
|
||||
}}
|
||||
value={selectedForm ? { label: selectedForm.name, value: selectedForm.id } : undefined}
|
||||
/>
|
||||
<div className="mt-10">
|
||||
{selectedForm ? <TestForm form={selectedForm} showAllData={false} /> : <></>}
|
||||
</div>
|
||||
</InsightsLayout>
|
||||
);
|
||||
|
||||
return <></>;
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
import MembersView from "@calcom/features/ee/organizations/pages/members";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { HeadSeo } from "@calcom/ui";
|
||||
|
||||
const MembersPage: React.FC = () => {
|
||||
const { t } = useLocale();
|
||||
@@ -14,8 +13,6 @@ const MembersPage: React.FC = () => {
|
||||
description={t("organization_description")}
|
||||
withoutSeo
|
||||
subtitle={t("organization_description")}>
|
||||
<HeadSeo title={t("organization_members")} description={t("organization_description")} />
|
||||
|
||||
<MembersView />
|
||||
</Shell>
|
||||
);
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
|
||||
|
||||
import type { getServerSideProps } from "@lib/org/[orgSlug]/instant-meeting/team/[slug]/[type]/getServerSideProps";
|
||||
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import type { EmbedProps } from "@lib/withEmbedSsr";
|
||||
import type { EmbedProps } from "app/WithEmbedSSR";
|
||||
|
||||
export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
|
||||
|
||||
import type { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
|
||||
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import type { EmbedProps } from "@lib/withEmbedSsr";
|
||||
import type { EmbedProps } from "app/WithEmbedSSR";
|
||||
|
||||
export type PageProps = inferSSRProps<typeof getServerSideProps> & EmbedProps;
|
||||
|
||||
|
||||
@@ -128,6 +128,9 @@ vi.mock("@calcom/prisma/zod-utils", () => ({
|
||||
EventTypeMetaDataSchema: {
|
||||
parse: vi.fn(),
|
||||
},
|
||||
eventTypeMetaDataSchemaWithTypedApps: {
|
||||
parse: vi.fn(),
|
||||
},
|
||||
bookingMetadataSchema: {
|
||||
parse: vi.fn(),
|
||||
},
|
||||
|
||||
@@ -7,7 +7,7 @@ import { getBookerWrapperClasses } from "@calcom/features/bookings/Booker/utils/
|
||||
import { BookerSeo } from "@calcom/features/bookings/components/BookerSeo";
|
||||
|
||||
import type { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
import type { EmbedProps } from "@lib/withEmbedSsr";
|
||||
import type { EmbedProps } from "app/WithEmbedSSR";
|
||||
|
||||
import type { getServerSideProps } from "@server/lib/[user]/[type]/getServerSideProps";
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import dayjs from "@calcom/dayjs";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
||||
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||
import { Button, HeadSeo, Icon } from "@calcom/ui";
|
||||
import { Button, Icon } from "@calcom/ui";
|
||||
|
||||
import type { getServerSideProps } from "@lib/video/meeting-ended/[uid]/getServerSideProps";
|
||||
|
||||
@@ -14,7 +14,6 @@ export default function MeetingUnavailable(props: PageProps) {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<HeadSeo title={t("meeting_unavailable")} description={t("meeting_unavailable")} />
|
||||
<main className="mx-auto my-24 max-w-3xl">
|
||||
<div className="fixed inset-0 z-50 overflow-y-auto">
|
||||
<div className="flex min-h-screen items-end justify-center px-4 pb-20 pt-4 text-center sm:block sm:p-0">
|
||||
|
||||
@@ -4,7 +4,7 @@ import dayjs from "@calcom/dayjs";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
||||
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||
import { Button, HeadSeo, Icon, EmptyScreen } from "@calcom/ui";
|
||||
import { Button, Icon, EmptyScreen } from "@calcom/ui";
|
||||
|
||||
import type { getServerSideProps } from "@lib/video/meeting-not-started/[uid]/getServerSideProps";
|
||||
|
||||
@@ -14,7 +14,6 @@ export default function MeetingNotStarted(props: PageProps) {
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<>
|
||||
<HeadSeo title={t("this_meeting_has_not_started_yet")} description={props.booking.title} />
|
||||
<main className="mx-auto my-24 max-w-3xl">
|
||||
<EmptyScreen
|
||||
Icon="clock"
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
"use client";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button, EmptyScreen, HeadSeo } from "@calcom/ui";
|
||||
import { Button, EmptyScreen } from "@calcom/ui";
|
||||
|
||||
export default function NoMeetingFound() {
|
||||
const { t } = useLocale();
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeadSeo title={t("no_meeting_found")} description={t("no_meeting_found")} />
|
||||
<main className="mx-auto my-24 max-w-3xl">
|
||||
<EmptyScreen
|
||||
Icon="x"
|
||||
|
||||
@@ -292,10 +292,6 @@ const nextConfig = {
|
||||
source: "/forms/:formQuery*",
|
||||
destination: "/apps/routing-forms/routing-link/:formQuery*",
|
||||
},
|
||||
{
|
||||
source: "/router/:path*",
|
||||
destination: "/apps/routing-forms/router/:path*",
|
||||
},
|
||||
{
|
||||
source: "/success/:path*",
|
||||
has: [
|
||||
@@ -594,7 +590,7 @@ const nextConfig = {
|
||||
value: orgHostPath,
|
||||
},
|
||||
],
|
||||
destination: "/event-types?openIntercom=true",
|
||||
destination: "/event-types?openPlain=true",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@calcom/web",
|
||||
"version": "4.8.6",
|
||||
"version": "4.8.10",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE=true next build",
|
||||
|
||||
@@ -55,8 +55,8 @@ export default (
|
||||
rowAsideContent: [
|
||||
{
|
||||
componentBadge: {
|
||||
badgeLabel: emailVerified === null ? "Unknown" : emailVerified ? "Yes" : "No",
|
||||
badgeColor: emailVerified === null ? "YELLOW" : emailVerified ? "GREEN" : "RED",
|
||||
badgeLabel: emailVerified === null ? "No" : emailVerified ? "Yes" : "No",
|
||||
badgeColor: emailVerified === null ? "RED" : emailVerified ? "GREEN" : "RED",
|
||||
},
|
||||
},
|
||||
],
|
||||
@@ -152,8 +152,8 @@ export default (
|
||||
rowAsideContent: [
|
||||
{
|
||||
componentBadge: {
|
||||
badgeLabel: twoFactorEnabled === null ? "Unknown" : twoFactorEnabled ? "Yes" : "No",
|
||||
badgeColor: twoFactorEnabled === null ? "YELLOW" : twoFactorEnabled ? "GREEN" : "RED",
|
||||
badgeLabel: twoFactorEnabled === null ? "No" : twoFactorEnabled ? "Yes" : "No",
|
||||
badgeColor: twoFactorEnabled === null ? "RED" : twoFactorEnabled ? "GREEN" : "RED",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { GetStaticPaths } from "next";
|
||||
|
||||
import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
import { validStatuses } from "~/bookings/lib/validStatuses";
|
||||
import BookingsListingView from "~/bookings/views/bookings-listing-view";
|
||||
|
||||
export { getStaticProps } from "~/bookings/views/bookings-listing-view.getStaticProps";
|
||||
|
||||
const BookingsListingPage = new Proxy<{
|
||||
(): JSX.Element;
|
||||
PageWrapper?: typeof PageWrapper;
|
||||
}>(BookingsListingView, {});
|
||||
|
||||
BookingsListingPage.PageWrapper = PageWrapper;
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = () => {
|
||||
return {
|
||||
paths: validStatuses.map((status) => ({
|
||||
params: { status },
|
||||
locale: "en",
|
||||
})),
|
||||
fallback: "blocking",
|
||||
};
|
||||
};
|
||||
|
||||
export default BookingsListingPage;
|
||||
@@ -0,0 +1,6 @@
|
||||
import { getServerSideProps as _getServerSideProps } from "../../server/lib/router/getServerSideProps";
|
||||
|
||||
export { default } from "./index";
|
||||
// We are not using withEmbedSsr here because _getServerSideProps already has the logic to append /embed to the redirect URL and it ends up as /embed/embed
|
||||
// TODO: We should use withEmbedSsr here and remove the logic from _getServerSideProps
|
||||
export const getServerSideProps = _getServerSideProps;
|
||||
+1
-1
@@ -4,7 +4,7 @@ import Head from "next/head";
|
||||
|
||||
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||
|
||||
import { getServerSideProps } from "./getServerSideProps";
|
||||
import { getServerSideProps } from "../../server/lib/router/getServerSideProps";
|
||||
|
||||
export default function Router({ form, message }: inferSSRProps<typeof getServerSideProps>) {
|
||||
return (
|
||||
@@ -24,6 +24,7 @@ let pages = (exports.pages = glob
|
||||
"global-error",
|
||||
"WithAppDirSsr",
|
||||
"WithEmbedSSR",
|
||||
"WithEmbedSSR.test",
|
||||
].some((prefix) => v.startsWith(prefix))
|
||||
));
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import type Prisma from "@prisma/client";
|
||||
|
||||
import prisma from "@calcom/prisma";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { test, todo } from "./lib/fixtures";
|
||||
import type { Fixtures } from "./lib/fixtures";
|
||||
@@ -51,7 +51,7 @@ test.describe("Stripe integration skip true", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata);
|
||||
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata);
|
||||
|
||||
const stripeAppMetadata = metadata?.apps?.stripe;
|
||||
|
||||
@@ -93,7 +93,7 @@ test.describe("Stripe integration skip true", () => {
|
||||
},
|
||||
});
|
||||
|
||||
const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata);
|
||||
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata);
|
||||
|
||||
const stripeAppMetadata = metadata?.apps?.stripe;
|
||||
|
||||
@@ -291,7 +291,7 @@ test.describe("Stripe integration with the new app install flow skip false", ()
|
||||
});
|
||||
|
||||
for (const eventTypeMetadata of eventTypeMetadatas) {
|
||||
const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata);
|
||||
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata);
|
||||
const stripeAppMetadata = metadata?.apps?.stripe;
|
||||
expect(stripeAppMetadata).toHaveProperty("credentialId");
|
||||
expect(typeof stripeAppMetadata?.credentialId).toBe("number");
|
||||
@@ -328,7 +328,7 @@ test.describe("Stripe integration with the new app install flow skip false", ()
|
||||
},
|
||||
});
|
||||
|
||||
const metadata = EventTypeMetaDataSchema.parse(eventTypeMetadata?.metadata);
|
||||
const metadata = eventTypeMetaDataSchemaWithTypedApps.parse(eventTypeMetadata?.metadata);
|
||||
|
||||
const stripeAppMetadata = metadata?.apps?.stripe;
|
||||
|
||||
|
||||
@@ -542,16 +542,27 @@ export async function expectPageToBeNotFound({ page, url }: { page: Page; url: s
|
||||
export async function clickUntilDialogVisible(
|
||||
dialogOpenButton: Locator,
|
||||
visibleLocatorOnDialog: Locator,
|
||||
page: Page,
|
||||
matchUrl: string,
|
||||
retries = 3,
|
||||
delay = 500
|
||||
delay = 2000
|
||||
) {
|
||||
for (let i = 0; i < retries; i++) {
|
||||
await dialogOpenButton.click();
|
||||
try {
|
||||
const responsePromise = page.waitForResponse(
|
||||
(response) => response.url().includes(matchUrl) && response.status() === 200
|
||||
);
|
||||
await dialogOpenButton.click();
|
||||
await responsePromise;
|
||||
await visibleLocatorOnDialog.waitFor({ state: "visible", timeout: delay });
|
||||
return;
|
||||
} catch {
|
||||
if (i === retries - 1) throw new Error("Dialog did not appear after multiple attempts.");
|
||||
console.warn(`clickUntilDialogVisible: Attempt ${i + 1} failed to open dialog`);
|
||||
if (i === retries - 1) {
|
||||
console.log("clickUntilDialogVisible: Dialog did not appear after multiple attempts.");
|
||||
return;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,8 +21,12 @@ test.describe("Out of office", () => {
|
||||
await user.apiLogin();
|
||||
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
|
||||
const addOOOButton = page.getByTestId("add_entry_ooo");
|
||||
const dateButton = page.locator('[data-testid="date-range"]');
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
|
||||
await page.getByTestId("add_entry_ooo").click();
|
||||
await page.getByTestId("reason_select").click();
|
||||
|
||||
await page.getByTestId("select-option-4").click();
|
||||
@@ -68,8 +72,12 @@ test.describe("Out of office", () => {
|
||||
await user.apiLogin();
|
||||
|
||||
await page.goto(`/settings/my-account/out-of-office`);
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
|
||||
const addOOOButton = page.getByTestId("add_entry_ooo");
|
||||
const dateButton = page.locator('[data-testid="date-range"]');
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
|
||||
await page.getByTestId("add_entry_ooo").click();
|
||||
await page.getByTestId("reason_select").click();
|
||||
|
||||
await page.getByTestId("select-option-4").click();
|
||||
@@ -204,10 +212,13 @@ test.describe("Out of office", () => {
|
||||
await user.apiLogin();
|
||||
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
|
||||
await page.getByTestId("add_entry_ooo").click();
|
||||
const addOOOButton = page.getByTestId("add_entry_ooo");
|
||||
const dateButton = page.locator('[data-testid="date-range"]');
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
|
||||
await page.locator('[data-testid="date-range"]').click();
|
||||
await dateButton.click();
|
||||
|
||||
await selectToAndFromDates(page, "13", "22", true);
|
||||
|
||||
@@ -244,10 +255,13 @@ test.describe("Out of office", () => {
|
||||
await user.apiLogin();
|
||||
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
|
||||
await page.getByTestId("add_entry_ooo").click();
|
||||
const addOOOButton = page.getByTestId("add_entry_ooo");
|
||||
const dateButton = page.locator('[data-testid="date-range"]');
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
|
||||
await page.locator('[data-testid="date-range"]').click();
|
||||
await dateButton.click();
|
||||
|
||||
await selectToAndFromDates(page, "13", "22");
|
||||
|
||||
@@ -256,9 +270,8 @@ test.describe("Out of office", () => {
|
||||
await expect(page.locator(`data-testid=table-redirect-n-a`)).toBeVisible();
|
||||
|
||||
// add another entry
|
||||
await page.getByTestId("add_entry_ooo").click();
|
||||
|
||||
await page.locator('[data-testid="date-range"]').click();
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
await dateButton.click();
|
||||
|
||||
await selectToAndFromDates(page, "11", "24");
|
||||
|
||||
@@ -274,10 +287,13 @@ test.describe("Out of office", () => {
|
||||
await user.apiLogin();
|
||||
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState("domcontentloaded");
|
||||
|
||||
await page.getByTestId("add_entry_ooo").click();
|
||||
const addOOOButton = page.getByTestId("add_entry_ooo");
|
||||
const dateButton = page.locator('[data-testid="date-range"]');
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
|
||||
await page.locator('[data-testid="date-range"]').click();
|
||||
await dateButton.click();
|
||||
|
||||
await selectToAndFromDates(page, "13", "22");
|
||||
|
||||
@@ -286,9 +302,8 @@ test.describe("Out of office", () => {
|
||||
await expect(page.locator(`data-testid=table-redirect-n-a`)).toBeVisible();
|
||||
|
||||
// add another entry
|
||||
await page.getByTestId("add_entry_ooo").click();
|
||||
|
||||
await page.locator('[data-testid="date-range"]').click();
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
await dateButton.click();
|
||||
|
||||
await selectToAndFromDates(page, "13", "22");
|
||||
|
||||
@@ -308,13 +323,13 @@ test.describe("Out of office", () => {
|
||||
|
||||
//Creates 2 OOO entries:
|
||||
//First OOO is created on Next month 1st - 3rd
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "1", "3");
|
||||
await expect(page.locator(`data-testid=table-redirect-n-a`).nth(0)).toBeVisible();
|
||||
|
||||
//Second OOO is created on Next month 4th - 6th
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "4", "6");
|
||||
await expect(page.locator(`data-testid=table-redirect-n-a`).nth(1)).toBeVisible();
|
||||
@@ -341,7 +356,7 @@ test.describe("Out of office", () => {
|
||||
const dateButton = await page.locator('[data-testid="date-range"]');
|
||||
|
||||
//As owner,OOO is created on Next month 1st - 3rd, forwarding to 'member-1'
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "1", "3", "member-1");
|
||||
await expect(
|
||||
@@ -352,7 +367,7 @@ test.describe("Out of office", () => {
|
||||
await member1User?.apiLogin();
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState();
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "4", "5", "owner");
|
||||
await expect(page.locator(`data-testid=table-redirect-${owner.username ?? "n-a"}`).nth(0)).toBeVisible();
|
||||
@@ -380,7 +395,7 @@ test.describe("Out of office", () => {
|
||||
const dateButton = await page.locator('[data-testid="date-range"]');
|
||||
|
||||
//As owner,OOO is created on Next month 1st - 3rd, forwarding to 'member-1'
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "1", "3", "member-1");
|
||||
await expect(
|
||||
@@ -391,7 +406,7 @@ test.describe("Out of office", () => {
|
||||
await member1User?.apiLogin();
|
||||
await page.goto("/settings/my-account/out-of-office");
|
||||
await page.waitForLoadState();
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton);
|
||||
await clickUntilDialogVisible(addOOOButton, dateButton, page, "outOfOfficeReasonList?batch=1");
|
||||
await dateButton.click();
|
||||
await selectDateAndCreateOOO(page, "2", "5", "owner", 400);
|
||||
await expect(page.locator(`text=${t("booking_redirect_infinite_not_allowed")}`)).toBeTruthy();
|
||||
|
||||
@@ -2216,8 +2216,8 @@
|
||||
"org_no_teams_yet": "هذه المنظمة ليس لديها فرق بعد",
|
||||
"org_no_teams_yet_description": "إذا كنت مسؤول، تأكد من إنشاء فرق لعرضها هنا.",
|
||||
"set_up": "الإعداد",
|
||||
"my_profile": "الملف الشخصي",
|
||||
"my_settings": "الإعدادات",
|
||||
"my_profile": "ملفي الشخصي",
|
||||
"my_settings": "إعداداتي",
|
||||
"crm": "CRM",
|
||||
"messaging": "المراسلة",
|
||||
"sender_id_info": "الاسم أو الرقم الظاهر على أنه مرسل الرسالة النصية القصيرة (بعض البلدان لا تسمح بمعرفات المرسل الأبجدية الرقمية)",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "اسم المالك المراد تغييره",
|
||||
"no_filter_set": "لم يتم تعيين أي تصفية",
|
||||
"booking_dry_run_successful": "تم إجراء المحاكاة التجريبية للحجز بنجاح",
|
||||
"booking_dry_run_successful_description": "تم إكمال التشغيل التجريبي بنجاح.",
|
||||
"dont_have_access_this_page": "ليس لديك صلاحية الوصول إلى هذه الصفحة",
|
||||
"you_need_admin_or_owner_privileges_to_access": "لرؤية هذه الصفحة أو التفاعل معها، يجب أن تكون لديك صلاحيات المدير أو المالك",
|
||||
"field_type": "نوع الحقل",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "تمت إعادة التعيين",
|
||||
"rerouted": "تمت إعادة التوجيه",
|
||||
"salesforce_assigned": "تعيين Salesforce",
|
||||
"router_position": "موضع الموجه",
|
||||
"show_matching_hosts": "عرض المضيفين المتطابقين",
|
||||
"no_active_queues": "لا توجد قوائم انتظار نشطة لهذا المُدخل",
|
||||
"weight": "الوزن",
|
||||
"calibration": "المعايرة",
|
||||
"shortfall": "العجز",
|
||||
"routing_form": "نموذج التوجيه",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ أضف السلاسل الجديدة أعلاه هنا ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2216,8 +2216,8 @@
|
||||
"org_no_teams_yet": "Bu təşkilatda hələ komanda yoxdur",
|
||||
"org_no_teams_yet_description": "Əgər siz administratorsunuzsa, burada göstərmək üçün komandalar yaratdığınızdan əmin olun.",
|
||||
"set_up": "Quraşdır",
|
||||
"my_profile": "Profilim",
|
||||
"my_settings": "Ayarlarım",
|
||||
"my_profile": "Mənim profilim",
|
||||
"my_settings": "Mənim tənzimləmələrim",
|
||||
"crm": "CRM",
|
||||
"messaging": "Mesajlaşma",
|
||||
"sender_id_info": "SMS göndərən kimi göstərilən ad və ya nömrə (bəzi ölkələrdə alfasayısal göndərən ID-lərinə icazə verilmir)",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Dəyişdiriləcək sahibin adı",
|
||||
"no_filter_set": "Filtr təyin edilməyib",
|
||||
"booking_dry_run_successful": "Rezervasiya test yoxlaması uğurlu oldu",
|
||||
"booking_dry_run_successful_description": "Test yoxlaması uğurla başa çatdı.",
|
||||
"dont_have_access_this_page": "Bu səhifəyə girişiniz yoxdur",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Bu səhifəni görmək və ya onunla qarşılıqlı əlaqədə olmaq üçün admin və ya sahib imtiyazlarına malik olmalısınız",
|
||||
"field_type": "Sahə növü",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Yenidən təyin edildi",
|
||||
"rerouted": "Yenidən yönləndirildi",
|
||||
"salesforce_assigned": "Salesforce təyinatı",
|
||||
"router_position": "Router mövqeyi",
|
||||
"show_matching_hosts": "Uyğun hostları göstər",
|
||||
"no_active_queues": "Bu giriş üçün aktiv növbə yoxdur",
|
||||
"weight": "Çəki",
|
||||
"calibration": "Kalibrasiya",
|
||||
"shortfall": "Çatışmazlıq",
|
||||
"routing_form": "Marşrutlama forması",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni sətirləri bura əlavə edin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Име на собственик за промяна",
|
||||
"no_filter_set": "Няма зададен филтър",
|
||||
"booking_dry_run_successful": "Тестовото резервиране е успешно",
|
||||
"booking_dry_run_successful_description": "Тестовото изпълнение завърши успешно.",
|
||||
"dont_have_access_this_page": "Нямате достъп до тази страница",
|
||||
"you_need_admin_or_owner_privileges_to_access": "За да видите или взаимодействате с тази страница, трябва да имате администраторски права или да сте собственик",
|
||||
"field_type": "Тип на полето",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Преразпределен",
|
||||
"rerouted": "Пренасочен",
|
||||
"salesforce_assigned": "Възложен в Salesforce",
|
||||
"router_position": "Позиция на рутера",
|
||||
"show_matching_hosts": "Покажи съвпадащите хостове",
|
||||
"no_active_queues": "Няма активни опашки за този вход",
|
||||
"weight": "Тегло",
|
||||
"calibration": "Калибриране",
|
||||
"shortfall": "Недостиг",
|
||||
"routing_form": "Форма за маршрутизиране",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавете новите си низове над този ред ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Nom del propietari a canviar",
|
||||
"no_filter_set": "Cap filtre establert",
|
||||
"booking_dry_run_successful": "La prova de reserva s'ha realitzat correctament",
|
||||
"booking_dry_run_successful_description": "La prova de funcionament s'ha completat correctament.",
|
||||
"dont_have_access_this_page": "No tens accés a aquesta pàgina",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Per veure o interactuar amb aquesta pàgina, has de tenir privilegis d'administrador o propietari",
|
||||
"field_type": "Tipus de camp",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Reassignat",
|
||||
"rerouted": "Redirigit de nou",
|
||||
"salesforce_assigned": "Assignació de Salesforce",
|
||||
"router_position": "Posició del router",
|
||||
"show_matching_hosts": "Mostra els hosts coincidents",
|
||||
"no_active_queues": "No hi ha cues actives per a aquesta entrada",
|
||||
"weight": "Pes",
|
||||
"calibration": "Calibratge",
|
||||
"shortfall": "Dèficit",
|
||||
"routing_form": "Formulari d'encaminament",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Afegiu les vostres noves cadenes a dalt ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Jméno vlastníka ke změně",
|
||||
"no_filter_set": "Není nastaven žádný filtr",
|
||||
"booking_dry_run_successful": "Testovací rezervace proběhla úspěšně",
|
||||
"booking_dry_run_successful_description": "Testovací běh byl úspěšně dokončen.",
|
||||
"dont_have_access_this_page": "K této stránce nemáte přístup",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Pro zobrazení nebo interakci s touto stránkou musíte mít administrátorská nebo vlastnická práva",
|
||||
"field_type": "Typ pole",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Přeřazeno",
|
||||
"rerouted": "Přesměrováno jinam",
|
||||
"salesforce_assigned": "Přiřazení Salesforce",
|
||||
"router_position": "Pozice routeru",
|
||||
"show_matching_hosts": "Zobrazit odpovídající hosty",
|
||||
"no_active_queues": "Pro tento vstup nejsou žádné aktivní fronty",
|
||||
"weight": "Váha",
|
||||
"calibration": "Kalibrace",
|
||||
"shortfall": "Nedostatek",
|
||||
"routing_form": "Směrovací formulář",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Přidejte své nové řetězce nahoru ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Ejernavn der skal ændres",
|
||||
"no_filter_set": "Intet filter indstillet",
|
||||
"booking_dry_run_successful": "Booking-testkørsel gennemført",
|
||||
"booking_dry_run_successful_description": "Testkørslen blev gennemført med succes.",
|
||||
"dont_have_access_this_page": "Du har ikke adgang til denne side",
|
||||
"you_need_admin_or_owner_privileges_to_access": "For at kunne se eller interagere med denne side, skal du have administrator- eller ejerrettigheder",
|
||||
"field_type": "Felttype",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Omfordelt",
|
||||
"rerouted": "Omdirigeret",
|
||||
"salesforce_assigned": "Salesforce-tildeling",
|
||||
"router_position": "Router-position",
|
||||
"show_matching_hosts": "Vis matchende værter",
|
||||
"no_active_queues": "Ingen aktive køer for dette input",
|
||||
"weight": "Vægt",
|
||||
"calibration": "Kalibrering",
|
||||
"shortfall": "Underskud",
|
||||
"routing_form": "Routing-formular",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Tilføj dine nye strenge ovenfor her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Zu ändernder Besitzername",
|
||||
"no_filter_set": "Kein Filter gesetzt",
|
||||
"booking_dry_run_successful": "Buchungs-Testlauf erfolgreich",
|
||||
"booking_dry_run_successful_description": "Der Testlauf wurde erfolgreich abgeschlossen.",
|
||||
"dont_have_access_this_page": "Sie haben keinen Zugriff auf diese Seite",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Um diese Seite anzuzeigen oder mit ihr zu interagieren, benötigen Sie Administrator- oder Eigentümerrechte",
|
||||
"field_type": "Feldtyp",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Neu zugewiesen",
|
||||
"rerouted": "Umgeleitet",
|
||||
"salesforce_assigned": "Salesforce-Zuweisung",
|
||||
"router_position": "Router-Position",
|
||||
"show_matching_hosts": "Passende Hosts anzeigen",
|
||||
"no_active_queues": "Keine aktiven Warteschlangen für diese Eingabe",
|
||||
"weight": "Gewichtung",
|
||||
"calibration": "Kalibrierung",
|
||||
"shortfall": "Fehlmenge",
|
||||
"routing_form": "Routing-Formular",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Fügen Sie Ihre neuen Code-Zeilen über dieser hinzu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Όνομα κατόχου προς αλλαγή",
|
||||
"no_filter_set": "Δεν έχει οριστεί φίλτρο",
|
||||
"booking_dry_run_successful": "Η δοκιμαστική κράτηση ολοκληρώθηκε με επιτυχία",
|
||||
"booking_dry_run_successful_description": "Η δοκιμαστική εκτέλεση ολοκληρώθηκε με επιτυχία.",
|
||||
"dont_have_access_this_page": "Δεν έχετε πρόσβαση σε αυτή τη σελίδα",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Για να δείτε ή να αλληλεπιδράσετε με αυτή τη σελίδα, πρέπει να έχετε δικαιώματα διαχειριστή ή ιδιοκτήτη",
|
||||
"field_type": "Τύπος πεδίου",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Επανεκχωρήθηκε",
|
||||
"rerouted": "Ανακατευθύνθηκε",
|
||||
"salesforce_assigned": "Ανάθεση Salesforce",
|
||||
"router_position": "Θέση δρομολογητή",
|
||||
"show_matching_hosts": "Εμφάνιση αντίστοιχων hosts",
|
||||
"no_active_queues": "Δεν υπάρχουν ενεργές ουρές για αυτήν την είσοδο",
|
||||
"weight": "Βάρος",
|
||||
"calibration": "Βαθμονόμηση",
|
||||
"shortfall": "Έλλειμμα",
|
||||
"routing_form": "Φόρμα δρομολόγησης",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Προσθέστε τις νέες συμβολοσειρές σας πάνω από εδώ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2216,8 +2216,8 @@
|
||||
"org_no_teams_yet": "This organization has no teams yet",
|
||||
"org_no_teams_yet_description": "If you are an administrator, be sure to create teams to be shown here.",
|
||||
"set_up": "Set up",
|
||||
"my_profile": "My Profile",
|
||||
"my_settings": "My Settings",
|
||||
"my_profile": "My profile",
|
||||
"my_settings": "My settings",
|
||||
"crm": "CRM",
|
||||
"messaging": "Messaging",
|
||||
"sender_id_info": "Name or number shown as the sender of an SMS (some countries do not allow alphanumeric sender IDs)",
|
||||
@@ -2815,6 +2815,7 @@
|
||||
"salesforce_owner_name_to_change": "Owner name to change",
|
||||
"no_filter_set": "No filter set",
|
||||
"booking_dry_run_successful": "Booking dry run successful",
|
||||
"booking_dry_run_successful_description": "The dry run was completed successfully.",
|
||||
"dont_have_access_this_page": "You don't have access to this page",
|
||||
"you_need_admin_or_owner_privileges_to_access": "In order to view or interact with this page, you must have admin or owner privileges",
|
||||
"field_type": "Field type",
|
||||
@@ -2895,5 +2896,12 @@
|
||||
"reassigned": "Reassigned",
|
||||
"rerouted": "Rerouted",
|
||||
"salesforce_assigned": "Salesforce assignment",
|
||||
"router_position": "Router Position",
|
||||
"show_matching_hosts": "Show matching hosts",
|
||||
"no_active_queues": "No active queues for this input",
|
||||
"weight": "Weight",
|
||||
"calibration": "Calibration",
|
||||
"shortfall": "Shortfall",
|
||||
"routing_form": "Routing Form",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2216,8 +2216,8 @@
|
||||
"org_no_teams_yet": "Esta organización aún no tiene equipos",
|
||||
"org_no_teams_yet_description": "Si eres administrador, asegúrate de crear equipos para que se muestren aquí.",
|
||||
"set_up": "Configurar",
|
||||
"my_profile": "Mi Perfil",
|
||||
"my_settings": "Mis Configuraciones",
|
||||
"my_profile": "Mi perfil",
|
||||
"my_settings": "Mi configuración",
|
||||
"crm": "CRM",
|
||||
"messaging": "Mensajería",
|
||||
"sender_id_info": "Nombre o número mostrado como el remitente de un SMS (algunos países no permiten IDs de remitente alfanuméricos)",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Nombre del propietario a cambiar",
|
||||
"no_filter_set": "Sin filtros establecidos",
|
||||
"booking_dry_run_successful": "Prueba de reserva exitosa",
|
||||
"booking_dry_run_successful_description": "La prueba de simulación se completó exitosamente.",
|
||||
"dont_have_access_this_page": "No tienes acceso a esta página",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Para ver o interactuar con esta página, debes tener privilegios de administrador o propietario",
|
||||
"field_type": "Tipo de campo",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Reasignado",
|
||||
"rerouted": "Reenrutado",
|
||||
"salesforce_assigned": "Asignación de Salesforce",
|
||||
"router_position": "Posición del enrutador",
|
||||
"show_matching_hosts": "Mostrar hosts coincidentes",
|
||||
"no_active_queues": "No hay colas activas para esta entrada",
|
||||
"weight": "Peso",
|
||||
"calibration": "Calibración",
|
||||
"shortfall": "Déficit",
|
||||
"routing_form": "Formulario de enrutamiento",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agrega tus nuevas cadenas arriba de esta línea ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Nombre del propietario a cambiar",
|
||||
"no_filter_set": "Ningún filtro establecido",
|
||||
"booking_dry_run_successful": "Prueba de reserva exitosa",
|
||||
"booking_dry_run_successful_description": "La prueba de simulación se completó con éxito.",
|
||||
"dont_have_access_this_page": "No tienes acceso a esta página",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Para ver o interactuar con esta página, debes tener privilegios de administrador o propietario",
|
||||
"field_type": "Tipo de campo",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Reasignado",
|
||||
"rerouted": "Reenrutado",
|
||||
"salesforce_assigned": "Asignación de Salesforce",
|
||||
"router_position": "Posición del enrutador",
|
||||
"show_matching_hosts": "Mostrar hosts coincidentes",
|
||||
"no_active_queues": "No hay colas activas para esta entrada",
|
||||
"weight": "Peso",
|
||||
"calibration": "Calibración",
|
||||
"shortfall": "Déficit",
|
||||
"routing_form": "Formulario de enrutamiento",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agregue sus nuevas cadenas arriba ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Muudetava omaniku nimi",
|
||||
"no_filter_set": "Filter määramata",
|
||||
"booking_dry_run_successful": "Broneeringu proovikäivitus õnnestus",
|
||||
"booking_dry_run_successful_description": "Proovijooks lõppes edukalt.",
|
||||
"dont_have_access_this_page": "Teil ei ole sellele lehele juurdepääsu",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Selle lehe vaatamiseks või sellega suhtlemiseks peate omama administraatori või omaniku õigusi",
|
||||
"field_type": "Välja tüüp",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Ümber määratud",
|
||||
"rerouted": "Ümber suunatud",
|
||||
"salesforce_assigned": "Salesforce'i määramine",
|
||||
"router_position": "Ruuteri positsioon",
|
||||
"show_matching_hosts": "Näita sobivaid hoste",
|
||||
"no_active_queues": "Sellel sisendit jaoks pole aktiivseid järjekordi",
|
||||
"weight": "Kaal",
|
||||
"calibration": "Kalibreerimine",
|
||||
"shortfall": "Puudujääk",
|
||||
"routing_form": "Marsruutimise vorm",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Aldatu beharreko jabearen izena",
|
||||
"no_filter_set": "Ez dago iragazkirik ezarrita",
|
||||
"booking_dry_run_successful": "Erreserben proba arrakastatsua",
|
||||
"booking_dry_run_successful_description": "Proba exekuzioa arrakastaz burutu da.",
|
||||
"dont_have_access_this_page": "Ez duzu orri honetarako sarbiderik",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Orri hau ikusteko edo erabiltzeko, administratzaile edo jabe baimenak izan behar dituzu",
|
||||
"field_type": "Eremu mota",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Berresleituta",
|
||||
"rerouted": "Berbideratuta",
|
||||
"salesforce_assigned": "Salesforce esleipena",
|
||||
"router_position": "Bideratzailearen posizioa",
|
||||
"show_matching_hosts": "Erakutsi bat datozen ostalariak",
|
||||
"no_active_queues": "Sarrera honek ez du ilara aktiborik",
|
||||
"weight": "Pisua",
|
||||
"calibration": "Kalibrazioa",
|
||||
"shortfall": "Gabezia",
|
||||
"routing_form": "Bideratze-formularioa",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Gehitu zure kate berriak honen gainean ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2216,8 +2216,8 @@
|
||||
"org_no_teams_yet": "Tällä organisaatiolla ei ole vielä tiimejä",
|
||||
"org_no_teams_yet_description": "Jos olet järjestelmänvalvoja, muista luoda tiimejä, jotta ne näkyvät täällä.",
|
||||
"set_up": "Määritä",
|
||||
"my_profile": "Oma profiili",
|
||||
"my_settings": "Omat asetukset",
|
||||
"my_profile": "Profiilini",
|
||||
"my_settings": "Asetukseni",
|
||||
"crm": "CRM",
|
||||
"messaging": "Viestintä",
|
||||
"sender_id_info": "Nimi tai numero, joka näkyy tekstiviestin lähettäjänä (kaikki maat eivät salli aakkosnumeerisia lähettäjätunnisteita)",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Muutettavan omistajan nimi",
|
||||
"no_filter_set": "Ei suodatinta määritetty",
|
||||
"booking_dry_run_successful": "Varauksen testaus onnistui",
|
||||
"booking_dry_run_successful_description": "Testausajo suoritettiin onnistuneesti.",
|
||||
"dont_have_access_this_page": "Sinulla ei ole pääsyä tälle sivulle",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Nähdäksesi tai käyttääksesi tätä sivua sinulla täytyy olla ylläpitäjän tai omistajan oikeudet",
|
||||
"field_type": "Kenttätyyppi",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Uudelleenmääritetty",
|
||||
"rerouted": "Uudelleenreititetty",
|
||||
"salesforce_assigned": "Salesforce-määritys",
|
||||
"router_position": "Reitittimen sijainti",
|
||||
"show_matching_hosts": "Näytä täsmäävät isännät",
|
||||
"no_active_queues": "Ei aktiivisia jonoja tälle syötteelle",
|
||||
"weight": "Paino",
|
||||
"calibration": "Kalibrointi",
|
||||
"shortfall": "Vajaus",
|
||||
"routing_form": "Reitityskaavake",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Lisää uudet merkkijonot tämän yläpuolelle ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Nom du propriétaire à modifier",
|
||||
"no_filter_set": "Aucun filtre défini",
|
||||
"booking_dry_run_successful": "Test de réservation réussi",
|
||||
"booking_dry_run_successful_description": "Le test à blanc a été effectué avec succès.",
|
||||
"dont_have_access_this_page": "Vous n'avez pas accès à cette page",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Pour voir ou interagir avec cette page, vous devez avoir des privilèges d'administrateur ou de propriétaire",
|
||||
"field_type": "Type de champ",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Réaffecté",
|
||||
"rerouted": "Réacheminé",
|
||||
"salesforce_assigned": "Affectation Salesforce",
|
||||
"router_position": "Position du routeur",
|
||||
"show_matching_hosts": "Afficher les hôtes correspondants",
|
||||
"no_active_queues": "Aucune file d'attente active pour cette entrée",
|
||||
"weight": "Poids",
|
||||
"calibration": "Calibrage",
|
||||
"shortfall": "Déficit",
|
||||
"routing_form": "Formulaire de routage",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Ajoutez vos nouvelles chaînes ci-dessus ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "שם הבעלים לשינוי",
|
||||
"no_filter_set": "לא הוגדר סינון",
|
||||
"booking_dry_run_successful": "הרצת הניסיון של ההזמנה הצליחה",
|
||||
"booking_dry_run_successful_description": "הריצה הניסיונית הושלמה בהצלחה.",
|
||||
"dont_have_access_this_page": "אין לך גישה לדף זה",
|
||||
"you_need_admin_or_owner_privileges_to_access": "כדי לצפות או לפעול בדף זה, עליך להיות בעל הרשאות מנהל או בעלים",
|
||||
"field_type": "סוג שדה",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "הוקצה מחדש",
|
||||
"rerouted": "נותב מחדש",
|
||||
"salesforce_assigned": "הקצאת Salesforce",
|
||||
"router_position": "מיקום נתב",
|
||||
"show_matching_hosts": "הצג מארחים תואמים",
|
||||
"no_active_queues": "אין תורים פעילים עבור קלט זה",
|
||||
"weight": "משקל",
|
||||
"calibration": "כיול",
|
||||
"shortfall": "חוסר",
|
||||
"routing_form": "טופס ניתוב",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2216,7 +2216,7 @@
|
||||
"org_no_teams_yet": "Ennek a szervezetnek még nincsenek csapatai",
|
||||
"org_no_teams_yet_description": "Ha Ön rendszergazda, feltétlenül hozzon létre csapatokat, amelyek itt megjelennek.",
|
||||
"set_up": "Beállít",
|
||||
"my_profile": "A profilom",
|
||||
"my_profile": "Profilom",
|
||||
"my_settings": "Beállításaim",
|
||||
"crm": "CRM",
|
||||
"messaging": "Üzenetküldés",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Módosítandó tulajdonos neve",
|
||||
"no_filter_set": "Nincs beállított szűrő",
|
||||
"booking_dry_run_successful": "A foglalás tesztfuttatása sikeres",
|
||||
"booking_dry_run_successful_description": "A teszt futtatás sikeresen befejeződött.",
|
||||
"dont_have_access_this_page": "Nincs hozzáférése ehhez az oldalhoz",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Az oldal megtekintéséhez vagy használatához adminisztrátori vagy tulajdonosi jogosultság szükséges",
|
||||
"field_type": "Mező típusa",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Újra kiosztva",
|
||||
"rerouted": "Átirányítva",
|
||||
"salesforce_assigned": "Salesforce hozzárendelés",
|
||||
"router_position": "Router pozíció",
|
||||
"show_matching_hosts": "Egyező hosztok megjelenítése",
|
||||
"no_active_queues": "Nincsenek aktív sorok ehhez a bemenethez",
|
||||
"weight": "Súly",
|
||||
"calibration": "Kalibráció",
|
||||
"shortfall": "Hiány",
|
||||
"routing_form": "Útválasztási űrlap",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adja hozzá az új karakterláncokat fent ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Nome del proprietario da modificare",
|
||||
"no_filter_set": "Nessun filtro impostato",
|
||||
"booking_dry_run_successful": "Test di prenotazione completato con successo",
|
||||
"booking_dry_run_successful_description": "Il test di prova è stato completato con successo.",
|
||||
"dont_have_access_this_page": "Non hai accesso a questa pagina",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Per visualizzare o interagire con questa pagina, devi avere privilegi di amministratore o proprietario",
|
||||
"field_type": "Tipo di campo",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Riassegnato",
|
||||
"rerouted": "Reindirizzato",
|
||||
"salesforce_assigned": "Assegnazione Salesforce",
|
||||
"router_position": "Posizione del router",
|
||||
"show_matching_hosts": "Mostra host corrispondenti",
|
||||
"no_active_queues": "Nessuna coda attiva per questo input",
|
||||
"weight": "Peso",
|
||||
"calibration": "Calibrazione",
|
||||
"shortfall": "Deficit",
|
||||
"routing_form": "Modulo di instradamento",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Aggiungi le tue nuove stringhe qui sopra ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2216,7 +2216,7 @@
|
||||
"org_no_teams_yet": "この組織にはまだチームがありません",
|
||||
"org_no_teams_yet_description": "管理者である場合には、ここに表示されるチームを必ず作成してください。",
|
||||
"set_up": "設定",
|
||||
"my_profile": "プロフィール",
|
||||
"my_profile": "マイプロフィール",
|
||||
"my_settings": "設定",
|
||||
"crm": "CRM",
|
||||
"messaging": "メッセージング",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "変更する所有者名",
|
||||
"no_filter_set": "フィルター未設定",
|
||||
"booking_dry_run_successful": "予約のテストが正常に完了しました",
|
||||
"booking_dry_run_successful_description": "テスト実行は正常に完了しました。",
|
||||
"dont_have_access_this_page": "このページにアクセスする権限がありません",
|
||||
"you_need_admin_or_owner_privileges_to_access": "このページを表示または操作するには、管理者または所有者の権限が必要です",
|
||||
"field_type": "フィールドタイプ",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "再割り当て済み",
|
||||
"rerouted": "再ルーティング済み",
|
||||
"salesforce_assigned": "Salesforce割り当て",
|
||||
"router_position": "ルーターの位置",
|
||||
"show_matching_hosts": "一致するホストを表示",
|
||||
"no_active_queues": "この入力に対してアクティブなキューはありません",
|
||||
"weight": "重み",
|
||||
"calibration": "キャリブレーション",
|
||||
"shortfall": "不足",
|
||||
"routing_form": "ルーティングフォーム",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ この上に新しい文字列を追加してください ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "ឈ្មោះម្ចាស់ដែលត្រូវផ្លាស់ប្តូរ",
|
||||
"no_filter_set": "មិនមានការកំណត់តម្រង",
|
||||
"booking_dry_run_successful": "ការសាកល្បងការកក់បានជោគជ័យ",
|
||||
"booking_dry_run_successful_description": "ការសាកល្បងត្រូវបានបញ្ចប់ដោយជោគជ័យ។",
|
||||
"dont_have_access_this_page": "អ្នកមិនមានសិទ្ធិចូលប្រើទំព័រនេះទេ",
|
||||
"you_need_admin_or_owner_privileges_to_access": "ដើម្បីមើល ឬប្រើប្រាស់ទំព័រនេះ អ្នកត្រូវតែមានសិទ្ធិជាអ្នកគ្រប់គ្រង ឬម្ចាស់",
|
||||
"field_type": "ប្រភេទវាល",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "បានចាត់តាំងឡើងវិញ",
|
||||
"rerouted": "បញ្ជូនបន្តឡើងវិញ",
|
||||
"salesforce_assigned": "ការចាត់តាំង Salesforce",
|
||||
"router_position": "ទីតាំងរ៉ោតទ័រ",
|
||||
"show_matching_hosts": "បង្ហាញម៉ាស៊ីនដែលត្រូវគ្នា",
|
||||
"no_active_queues": "គ្មានជួររង់ចាំសកម្មសម្រាប់ការបញ្ចូលនេះទេ",
|
||||
"weight": "ទម្ងន់",
|
||||
"calibration": "ការកែតម្រូវ",
|
||||
"shortfall": "កង្វះខាត",
|
||||
"routing_form": "ទម្រង់ការបញ្ជូន",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ បន្ថែមខ្សែអក្សរថ្មីរបស់អ្នកនៅខាងលើនេះ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "변경할 소유자 이름",
|
||||
"no_filter_set": "설정된 필터 없음",
|
||||
"booking_dry_run_successful": "예약 테스트 실행 성공",
|
||||
"booking_dry_run_successful_description": "테스트 실행이 성공적으로 완료되었습니다.",
|
||||
"dont_have_access_this_page": "이 페이지에 접근할 수 없습니다",
|
||||
"you_need_admin_or_owner_privileges_to_access": "이 페이지를 보거나 상호작용하려면 관리자 또는 소유자 권한이 필요합니다",
|
||||
"field_type": "필드 유형",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "재할당됨",
|
||||
"rerouted": "재라우팅됨",
|
||||
"salesforce_assigned": "Salesforce 할당",
|
||||
"router_position": "라우터 위치",
|
||||
"show_matching_hosts": "일치하는 호스트 표시",
|
||||
"no_active_queues": "이 입력에 대한 활성 대기열 없음",
|
||||
"weight": "가중치",
|
||||
"calibration": "보정",
|
||||
"shortfall": "부족",
|
||||
"routing_form": "라우팅 양식",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 여기에 새 문자열을 추가하세요 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Eigenaarsnaam om te wijzigen",
|
||||
"no_filter_set": "Geen filter ingesteld",
|
||||
"booking_dry_run_successful": "Boeking test succesvol",
|
||||
"booking_dry_run_successful_description": "De testrun is succesvol afgerond.",
|
||||
"dont_have_access_this_page": "Je hebt geen toegang tot deze pagina",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Om deze pagina te bekijken of ermee te communiceren, moet je over admin- of eigenaarsrechten beschikken",
|
||||
"field_type": "Veldtype",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Opnieuw toegewezen",
|
||||
"rerouted": "Omgeleid",
|
||||
"salesforce_assigned": "Salesforce toewijzing",
|
||||
"router_position": "Router positie",
|
||||
"show_matching_hosts": "Toon overeenkomende hosts",
|
||||
"no_active_queues": "Geen actieve wachtrijen voor deze invoer",
|
||||
"weight": "Gewicht",
|
||||
"calibration": "Kalibratie",
|
||||
"shortfall": "Tekort",
|
||||
"routing_form": "Routeringsformulier",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Voeg uw nieuwe strings hierboven toe ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Eiernavn som skal endres",
|
||||
"no_filter_set": "Ingen filter er satt",
|
||||
"booking_dry_run_successful": "Test av booking var vellykket",
|
||||
"booking_dry_run_successful_description": "Testkjøringen ble fullført vellykket.",
|
||||
"dont_have_access_this_page": "Du har ikke tilgang til denne siden",
|
||||
"you_need_admin_or_owner_privileges_to_access": "For å se eller bruke denne siden, må du ha administrator- eller eierrettigheter",
|
||||
"field_type": "Felttype",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Omfordelt",
|
||||
"rerouted": "Omdirigert",
|
||||
"salesforce_assigned": "Salesforce-tildeling",
|
||||
"router_position": "Ruterposisjon",
|
||||
"show_matching_hosts": "Vis samsvarende verter",
|
||||
"no_active_queues": "Ingen aktive køer for dette inndata",
|
||||
"weight": "Vekt",
|
||||
"calibration": "Kalibrering",
|
||||
"shortfall": "Underskudd",
|
||||
"routing_form": "Rutingskjema",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Legg til dine nye strenger over her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Nazwa właściciela do zmiany",
|
||||
"no_filter_set": "Brak ustawionego filtru",
|
||||
"booking_dry_run_successful": "Test rezerwacji zakończony pomyślnie",
|
||||
"booking_dry_run_successful_description": "Test został zakończony pomyślnie.",
|
||||
"dont_have_access_this_page": "Nie masz dostępu do tej strony",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Aby wyświetlić lub korzystać z tej strony, musisz mieć uprawnienia administratora lub właściciela",
|
||||
"field_type": "Typ pola",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Przypisano ponownie",
|
||||
"rerouted": "Przekierowano ponownie",
|
||||
"salesforce_assigned": "Przypisanie Salesforce",
|
||||
"router_position": "Pozycja routera",
|
||||
"show_matching_hosts": "Pokaż pasujące hosty",
|
||||
"no_active_queues": "Brak aktywnych kolejek dla tego wejścia",
|
||||
"weight": "Waga",
|
||||
"calibration": "Kalibracja",
|
||||
"shortfall": "Niedobór",
|
||||
"routing_form": "Formularz routingu",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodaj nowe ciągi powyżej ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -577,7 +577,7 @@
|
||||
"forgotten_secret_description": "Caso tenha perdido ou esquecido o segredo, você poderá alterá-lo. Mas fique ciente de que todas as integrações com esse segredo precisarão ser atualizadas",
|
||||
"current_incorrect_password": "Senha atual está incorreta",
|
||||
"password_hint_caplow": "Mistura de letras maiúsculas e minúsculas",
|
||||
"password_hint_min": "Mínimo de oito caracteres",
|
||||
"password_hint_min": "Mínimo de sete caracteres",
|
||||
"password_hint_admin_min": "No mínimo 15 caracteres",
|
||||
"password_hint_num": "Contém pelo menos um número",
|
||||
"max_limit_allowed_hint": "O tamanho deve ser de {{limit}} ou menos caracteres",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Nome do proprietário a ser alterado",
|
||||
"no_filter_set": "Nenhum filtro definido",
|
||||
"booking_dry_run_successful": "Simulação de agendamento bem-sucedida",
|
||||
"booking_dry_run_successful_description": "O teste simulado foi concluído com sucesso.",
|
||||
"dont_have_access_this_page": "Você não tem acesso a esta página",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Para visualizar ou interagir com esta página, você deve ter privilégios de administrador ou proprietário",
|
||||
"field_type": "Tipo de campo",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Reatribuído",
|
||||
"rerouted": "Reencaminhado",
|
||||
"salesforce_assigned": "Atribuição do Salesforce",
|
||||
"router_position": "Posição do roteador",
|
||||
"show_matching_hosts": "Mostrar hosts correspondentes",
|
||||
"no_active_queues": "Não há filas ativas para esta entrada",
|
||||
"weight": "Peso",
|
||||
"calibration": "Calibração",
|
||||
"shortfall": "Déficit",
|
||||
"routing_form": "Formulário de roteamento",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adicione suas novas strings aqui em cima ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2216,8 +2216,8 @@
|
||||
"org_no_teams_yet": "Esta organização ainda não tem equipas",
|
||||
"org_no_teams_yet_description": "Se é um administrador, deve criar equipas para serem apresentadas aqui.",
|
||||
"set_up": "Configurar",
|
||||
"my_profile": "O meu perfil",
|
||||
"my_settings": "As minhas definições",
|
||||
"my_profile": "Meu perfil",
|
||||
"my_settings": "Minhas configurações",
|
||||
"crm": "CRM",
|
||||
"messaging": "Mensagens",
|
||||
"sender_id_info": "Nome ou número mostrado como remetente de um SMS (alguns países não permitem identificadores alfanuméricos de remetentes)",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Nome do proprietário a alterar",
|
||||
"no_filter_set": "Nenhum filtro definido",
|
||||
"booking_dry_run_successful": "Teste de agendamento realizado com sucesso",
|
||||
"booking_dry_run_successful_description": "O teste simulado foi concluído com sucesso.",
|
||||
"dont_have_access_this_page": "Você não tem acesso a esta página",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Para visualizar ou interagir com esta página, você deve ter privilégios de administrador ou proprietário",
|
||||
"field_type": "Tipo de campo",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Reatribuído",
|
||||
"rerouted": "Reencaminhado",
|
||||
"salesforce_assigned": "Atribuição do Salesforce",
|
||||
"router_position": "Posição do roteador",
|
||||
"show_matching_hosts": "Mostrar hosts correspondentes",
|
||||
"no_active_queues": "Não há filas ativas para esta entrada",
|
||||
"weight": "Peso",
|
||||
"calibration": "Calibração",
|
||||
"shortfall": "Déficit",
|
||||
"routing_form": "Formulário de roteamento",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Numele proprietarului de schimbat",
|
||||
"no_filter_set": "Niciun filtru setat",
|
||||
"booking_dry_run_successful": "Simulare programare reușită",
|
||||
"booking_dry_run_successful_description": "Testul de simulare a fost finalizat cu succes.",
|
||||
"dont_have_access_this_page": "Nu aveți acces la această pagină",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Pentru a vizualiza sau interacționa cu această pagină, trebuie să aveți privilegii de administrator sau proprietar",
|
||||
"field_type": "Tip câmp",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Realocat",
|
||||
"rerouted": "Redirecționat",
|
||||
"salesforce_assigned": "Atribuire Salesforce",
|
||||
"router_position": "Poziția routerului",
|
||||
"show_matching_hosts": "Afișează gazdele care se potrivesc",
|
||||
"no_active_queues": "Nu există cozi active pentru această intrare",
|
||||
"weight": "Greutate",
|
||||
"calibration": "Calibrare",
|
||||
"shortfall": "Deficit",
|
||||
"routing_form": "Formular de rutare",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adăugați stringurile noi deasupra acestui rând ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Имя владельца для изменения",
|
||||
"no_filter_set": "Фильтр не установлен",
|
||||
"booking_dry_run_successful": "Тестовое бронирование выполнено успешно",
|
||||
"booking_dry_run_successful_description": "Пробный запуск успешно завершен.",
|
||||
"dont_have_access_this_page": "У вас нет доступа к этой странице",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Для просмотра или взаимодействия с этой страницей необходимы права администратора или владельца",
|
||||
"field_type": "Тип поля",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Переназначено",
|
||||
"rerouted": "Перенаправлено",
|
||||
"salesforce_assigned": "Назначение в Salesforce",
|
||||
"router_position": "Позиция роутера",
|
||||
"show_matching_hosts": "Показать подходящие хосты",
|
||||
"no_active_queues": "Нет активных очередей для этого входа",
|
||||
"weight": "Вес",
|
||||
"calibration": "Калибровка",
|
||||
"shortfall": "Дефицит",
|
||||
"routing_form": "Форма маршрутизации",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавьте строки выше ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Meno vlastníka na zmenu",
|
||||
"no_filter_set": "Nie je nastavený žiadny filter",
|
||||
"booking_dry_run_successful": "Testovacia rezervácia prebehla úspešne",
|
||||
"booking_dry_run_successful_description": "Testovací beh bol úspešne dokončený.",
|
||||
"dont_have_access_this_page": "Nemáte prístup na túto stránku",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Na zobrazenie alebo interakciu s touto stránkou musíte mať administrátorské alebo vlastnícke práva",
|
||||
"field_type": "Typ poľa",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Preradené",
|
||||
"rerouted": "Presmerované inam",
|
||||
"salesforce_assigned": "Priradenie Salesforce",
|
||||
"router_position": "Pozícia routera",
|
||||
"show_matching_hosts": "Zobraziť zodpovedajúcich hostiteľov",
|
||||
"no_active_queues": "Pre tento vstup nie sú aktívne fronty",
|
||||
"weight": "Váha",
|
||||
"calibration": "Kalibrácia",
|
||||
"shortfall": "Deficit",
|
||||
"routing_form": "Smerovací formulár",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Pridajte svoje nové reťazce nad túto líniu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Ime vlasnika koje treba promeniti",
|
||||
"no_filter_set": "Nije postavljen filter",
|
||||
"booking_dry_run_successful": "Test rezervacije uspešno završen",
|
||||
"booking_dry_run_successful_description": "Test je uspešno završen.",
|
||||
"dont_have_access_this_page": "Nemate pristup ovoj stranici",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Da biste pregledali ili koristili ovu stranicu, morate imati administratorske ili vlasničke privilegije",
|
||||
"field_type": "Tip polja",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Preraspoređeno",
|
||||
"rerouted": "Preusmereno",
|
||||
"salesforce_assigned": "Salesforce dodeljivanje",
|
||||
"router_position": "Pozicija rutera",
|
||||
"show_matching_hosts": "Prikaži odgovarajuće hostove",
|
||||
"no_active_queues": "Nema aktivnih redova za ovaj unos",
|
||||
"weight": "Težina",
|
||||
"calibration": "Kalibracija",
|
||||
"shortfall": "Manjak",
|
||||
"routing_form": "Formular za rutiranje",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodajte svoje nove stringove iznad ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Ägarnamn att ändra",
|
||||
"no_filter_set": "Inget filter inställt",
|
||||
"booking_dry_run_successful": "Testbokning genomförd",
|
||||
"booking_dry_run_successful_description": "Testkörningen slutfördes framgångsrikt.",
|
||||
"dont_have_access_this_page": "Du har inte åtkomst till den här sidan",
|
||||
"you_need_admin_or_owner_privileges_to_access": "För att visa eller interagera med den här sidan måste du ha administratörs- eller ägarprivilegier",
|
||||
"field_type": "Fälttyp",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Omtilldelad",
|
||||
"rerouted": "Omdirigerad",
|
||||
"salesforce_assigned": "Salesforce-tilldelning",
|
||||
"router_position": "Routerposition",
|
||||
"show_matching_hosts": "Visa matchande värdar",
|
||||
"no_active_queues": "Inga aktiva köer för denna inmatning",
|
||||
"weight": "Vikt",
|
||||
"calibration": "Kalibrering",
|
||||
"shortfall": "Underskott",
|
||||
"routing_form": "Routingformulär",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Değiştirilecek sahip adı",
|
||||
"no_filter_set": "Filtre ayarlanmadı",
|
||||
"booking_dry_run_successful": "Rezervasyon test çalıştırması başarılı",
|
||||
"booking_dry_run_successful_description": "Test çalıştırması başarıyla tamamlandı.",
|
||||
"dont_have_access_this_page": "Bu sayfaya erişiminiz yok",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Bu sayfayı görüntülemek veya etkileşimde bulunmak için yönetici veya sahip ayrıcalıklarına sahip olmalısınız",
|
||||
"field_type": "Alan türü",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Yeniden atandı",
|
||||
"rerouted": "Yeniden yönlendirildi",
|
||||
"salesforce_assigned": "Salesforce ataması",
|
||||
"router_position": "Router konumu",
|
||||
"show_matching_hosts": "Eşleşen sunucuları göster",
|
||||
"no_active_queues": "Bu girdi için aktif kuyruk yok",
|
||||
"weight": "Ağırlık",
|
||||
"calibration": "Kalibrasyon",
|
||||
"shortfall": "Açık",
|
||||
"routing_form": "Yönlendirme formu",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni dizelerinizi yukarıya ekleyin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Ім'я власника для зміни",
|
||||
"no_filter_set": "Фільтр не встановлено",
|
||||
"booking_dry_run_successful": "Тестове бронювання успішне",
|
||||
"booking_dry_run_successful_description": "Тестовий запуск успішно завершено.",
|
||||
"dont_have_access_this_page": "Ви не маєте доступу до цієї сторінки",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Щоб переглядати або взаємодіяти з цією сторінкою, ви повинні мати права адміністратора або власника",
|
||||
"field_type": "Тип поля",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Перепризначено",
|
||||
"rerouted": "Перемаршрутизовано",
|
||||
"salesforce_assigned": "Призначення Salesforce",
|
||||
"router_position": "Позиція маршрутизатора",
|
||||
"show_matching_hosts": "Показати відповідні хости",
|
||||
"no_active_queues": "Немає активних черг для цього вводу",
|
||||
"weight": "Вага",
|
||||
"calibration": "Калібрування",
|
||||
"shortfall": "Дефіцит",
|
||||
"routing_form": "Форма маршрутизації",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "Tên chủ sở hữu cần thay đổi",
|
||||
"no_filter_set": "Chưa đặt bộ lọc",
|
||||
"booking_dry_run_successful": "Chạy thử đặt lịch thành công",
|
||||
"booking_dry_run_successful_description": "Quá trình chạy thử đã hoàn tất thành công.",
|
||||
"dont_have_access_this_page": "Bạn không có quyền truy cập trang này",
|
||||
"you_need_admin_or_owner_privileges_to_access": "Để xem hoặc tương tác với trang này, bạn cần có quyền quản trị viên hoặc chủ sở hữu",
|
||||
"field_type": "Loại trường",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "Đã phân công lại",
|
||||
"rerouted": "Đã chuyển hướng lại",
|
||||
"salesforce_assigned": "Phân công Salesforce",
|
||||
"router_position": "Vị trí bộ định tuyến",
|
||||
"show_matching_hosts": "Hiển thị các máy chủ phù hợp",
|
||||
"no_active_queues": "Không có hàng đợi đang hoạt động cho đầu vào này",
|
||||
"weight": "Trọng số",
|
||||
"calibration": "Hiệu chuẩn",
|
||||
"shortfall": "Thiếu hụt",
|
||||
"routing_form": "Biểu mẫu định tuyến",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -2216,7 +2216,7 @@
|
||||
"org_no_teams_yet": "此组织尚无团队",
|
||||
"org_no_teams_yet_description": "如果您是管理员,请务必创建要在此处显示的团队。",
|
||||
"set_up": "设置",
|
||||
"my_profile": "我的个人资料",
|
||||
"my_profile": "我的资料",
|
||||
"my_settings": "我的设置",
|
||||
"crm": "CRM",
|
||||
"messaging": "消息",
|
||||
@@ -2811,6 +2811,7 @@
|
||||
"salesforce_owner_name_to_change": "要更改的所有者名称",
|
||||
"no_filter_set": "未设置筛选条件",
|
||||
"booking_dry_run_successful": "预订测试成功",
|
||||
"booking_dry_run_successful_description": "测试运行已成功完成。",
|
||||
"dont_have_access_this_page": "您无权访问此页面",
|
||||
"you_need_admin_or_owner_privileges_to_access": "要查看或操作此页面,您必须拥有管理员或所有者权限",
|
||||
"field_type": "字段类型",
|
||||
@@ -2891,5 +2892,12 @@
|
||||
"reassigned": "已重新分配",
|
||||
"rerouted": "已重新路由",
|
||||
"salesforce_assigned": "Salesforce 分配",
|
||||
"router_position": "路由位置",
|
||||
"show_matching_hosts": "显示匹配的主机",
|
||||
"no_active_queues": "此输入没有活动队列",
|
||||
"weight": "权重",
|
||||
"calibration": "校准",
|
||||
"shortfall": "缺口",
|
||||
"routing_form": "路由表单",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 在此上方添加您的新字符串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user