Merge branch 'main' into issue-5-1
This commit is contained in:
commit
0b55470ce6
|
@ -19,12 +19,12 @@ Fixes # (issue)
|
|||
|
||||
<!-- Please delete bullets that are not relevant. -->
|
||||
|
||||
- [ ] Bug fix (non-breaking change which fixes an issue)
|
||||
- [ ] Chore (refactoring code, technical debt, workflow improvements)
|
||||
- [ ] New feature (non-breaking change which adds functionality)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- [ ] Tests (Unit/Integration/E2E or any other test)
|
||||
- [ ] This change requires a documentation update
|
||||
- Bug fix (non-breaking change which fixes an issue)
|
||||
- Chore (refactoring code, technical debt, workflow improvements)
|
||||
- New feature (non-breaking change which adds functionality)
|
||||
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- Tests (Unit/Integration/E2E or any other test)
|
||||
- This change requires a documentation update
|
||||
|
||||
## How should this be tested?
|
||||
|
||||
|
|
|
@ -17,10 +17,11 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/stale@v7
|
||||
with:
|
||||
days-before-close: -1
|
||||
days-before-issue-stale: 60
|
||||
days-before-issue-close: -1
|
||||
days-before-pr-stale: 14
|
||||
days-before-pr-close: 7
|
||||
days-before-pr-close: -1
|
||||
stale-pr-message: "This PR is being marked as stale due to inactivity."
|
||||
close-pr-message: "This PR is being closed due to inactivity. Please reopen if work is intended to be continued."
|
||||
operations-per-run: 100
|
||||
|
|
|
@ -2,6 +2,7 @@ name: "Next.js Bundle Analysis"
|
|||
|
||||
on:
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
@ -34,7 +35,7 @@ jobs:
|
|||
|
||||
- name: Download base branch bundle stats
|
||||
uses: dawidd6/action-download-artifact@v2
|
||||
if: success() && github.event.number
|
||||
if: success()
|
||||
with:
|
||||
workflow: nextjs-bundle-analysis.yml
|
||||
branch: ${{ github.event.pull_request.base.ref }}
|
||||
|
@ -54,7 +55,7 @@ jobs:
|
|||
# Either of these arguments can be changed or removed by editing the `nextBundleAnalysis`
|
||||
# entry in your package.json file.
|
||||
- name: Compare with base branch bundle
|
||||
if: success() && github.event.number
|
||||
if: success()
|
||||
run: |
|
||||
cd apps/web
|
||||
ls -laR .next/analyze/base && npx -p nextjs-bundle-analysis compare
|
||||
|
@ -68,10 +69,10 @@ jobs:
|
|||
body="${body//'%'/'%25'}"
|
||||
body="${body//$'\n'/'%0A'}"
|
||||
body="${body//$'\r'/'%0D'}"
|
||||
echo ::set-output name=body::$body
|
||||
echo "{body}={$body}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Find Comment
|
||||
uses: peter-evans/find-comment@v1
|
||||
uses: peter-evans/find-comment@v2
|
||||
if: success() && github.event.number
|
||||
id: fc
|
||||
with:
|
||||
|
@ -79,14 +80,14 @@ jobs:
|
|||
body-includes: "<!-- __NEXTJS_BUNDLE_@calcom/web -->"
|
||||
|
||||
- name: Create Comment
|
||||
uses: peter-evans/create-or-update-comment@v1.4.4
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
if: success() && github.event.number && steps.fc.outputs.comment-id == 0
|
||||
with:
|
||||
issue-number: ${{ github.event.number }}
|
||||
body: ${{ steps.get-comment-body.outputs.body }}
|
||||
|
||||
- name: Update Comment
|
||||
uses: peter-evans/create-or-update-comment@v1.4.4
|
||||
uses: peter-evans/create-or-update-comment@v3
|
||||
if: success() && github.event.number && steps.fc.outputs.comment-id != 0
|
||||
with:
|
||||
issue-number: ${{ github.event.number }}
|
||||
|
|
|
@ -12,6 +12,14 @@ concurrency:
|
|||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
login:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
changes:
|
||||
name: Detect changes
|
||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||
|
@ -56,6 +64,41 @@ jobs:
|
|||
uses: ./.github/workflows/production-build.yml
|
||||
secrets: inherit
|
||||
|
||||
build-without-database:
|
||||
name: Production build (without database)
|
||||
needs: [changes]
|
||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
||||
uses: ./.github/workflows/production-build-without-database.yml
|
||||
secrets: inherit
|
||||
|
||||
e2e:
|
||||
name: E2E tests
|
||||
needs: [changes, lint, build]
|
||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
||||
uses: ./.github/workflows/e2e.yml
|
||||
secrets: inherit
|
||||
|
||||
e2e-app-store:
|
||||
name: E2E App Store tests
|
||||
needs: [changes, lint, build]
|
||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
||||
uses: ./.github/workflows/e2e-app-store.yml
|
||||
secrets: inherit
|
||||
|
||||
e2e-embed:
|
||||
name: E2E embeds tests
|
||||
needs: [changes, lint, build]
|
||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
||||
uses: ./.github/workflows/e2e-embed.yml
|
||||
secrets: inherit
|
||||
|
||||
e2e-embed-react:
|
||||
name: E2E React embeds tests
|
||||
needs: [changes, lint, build]
|
||||
if: ${{ needs.changes.outputs.has-files-requiring-all-checks == 'true' }}
|
||||
uses: ./.github/workflows/e2e-embed-react.yml
|
||||
secrets: inherit
|
||||
|
||||
analyze:
|
||||
name: Analyze Build
|
||||
needs: [changes, build]
|
||||
|
@ -64,7 +107,7 @@ jobs:
|
|||
secrets: inherit
|
||||
|
||||
required:
|
||||
needs: [changes, lint, type-check, test, build]
|
||||
needs: [changes, lint, type-check, test, build, e2e, e2e-embed, e2e-embed-react, e2e-app-store]
|
||||
if: always()
|
||||
runs-on: buildjet-4vcpu-ubuntu-2204
|
||||
steps:
|
||||
|
|
|
@ -2,9 +2,6 @@ name: Pre-release checks
|
|||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
changes:
|
||||
|
|
|
@ -64,6 +64,25 @@ export async function patchHandler(req: NextApiRequest) {
|
|||
where: { id: teamId, members: { some: { userId, role: { in: ["OWNER", "ADMIN"] } } } },
|
||||
});
|
||||
if (!_team) throw new HttpError({ statusCode: 401, message: "Unauthorized: OWNER or ADMIN required" });
|
||||
|
||||
// Check if parentId is related to this user
|
||||
if (data.parentId && data.parentId === teamId) {
|
||||
throw new HttpError({
|
||||
statusCode: 400,
|
||||
message: "Bad request: Parent id cannot be the same as the team id.",
|
||||
});
|
||||
}
|
||||
if (data.parentId) {
|
||||
const parentTeam = await prisma.team.findFirst({
|
||||
where: { id: data.parentId, members: { some: { userId, role: { in: ["OWNER", "ADMIN"] } } } },
|
||||
});
|
||||
if (!parentTeam)
|
||||
throw new HttpError({
|
||||
statusCode: 401,
|
||||
message: "Unauthorized: Invalid parent id. You can only use parent id of your own teams.",
|
||||
});
|
||||
}
|
||||
|
||||
let paymentUrl;
|
||||
if (_team.slug === null && data.slug) {
|
||||
data.metadata = {
|
||||
|
|
|
@ -71,6 +71,18 @@ async function postHandler(req: NextApiRequest) {
|
|||
}
|
||||
}
|
||||
|
||||
// Check if parentId is related to this user
|
||||
if (data.parentId) {
|
||||
const parentTeam = await prisma.team.findFirst({
|
||||
where: { id: data.parentId, members: { some: { userId, role: { in: ["OWNER", "ADMIN"] } } } },
|
||||
});
|
||||
if (!parentTeam)
|
||||
throw new HttpError({
|
||||
statusCode: 401,
|
||||
message: "Unauthorized: Invalid parent id. You can only use parent id of your own teams.",
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Perhaps there is a better fix for this?
|
||||
const cloneData: typeof data & {
|
||||
metadata: NonNullable<typeof data.metadata> | undefined;
|
||||
|
|
|
@ -1,212 +0,0 @@
|
|||
// originally from in the "experimental playground for tRPC + next.js 13" repo owned by trpc team
|
||||
// file link: https://github.com/trpc/next-13/blob/main/%40trpc/next-layout/createTRPCNextLayout.ts
|
||||
// repo link: https://github.com/trpc/next-13
|
||||
// code is / will continue to be adapted for our usage
|
||||
import { dehydrate, QueryClient } from "@tanstack/query-core";
|
||||
import type { DehydratedState, QueryKey } from "@tanstack/react-query";
|
||||
|
||||
import type { Maybe, TRPCClientError, TRPCClientErrorLike } from "@calcom/trpc";
|
||||
import {
|
||||
callProcedure,
|
||||
type AnyProcedure,
|
||||
type AnyQueryProcedure,
|
||||
type AnyRouter,
|
||||
type DataTransformer,
|
||||
type inferProcedureInput,
|
||||
type inferProcedureOutput,
|
||||
type inferRouterContext,
|
||||
type MaybePromise,
|
||||
type ProcedureRouterRecord,
|
||||
} from "@calcom/trpc/server";
|
||||
|
||||
import { createRecursiveProxy, createFlatProxy } from "@trpc/server/shared";
|
||||
|
||||
export function getArrayQueryKey(
|
||||
queryKey: string | [string] | [string, ...unknown[]] | unknown[],
|
||||
type: string
|
||||
): QueryKey {
|
||||
const queryKeyArrayed = Array.isArray(queryKey) ? queryKey : [queryKey];
|
||||
const [arrayPath, input] = queryKeyArrayed;
|
||||
|
||||
if (!input && (!type || type === "any")) {
|
||||
return Array.isArray(arrayPath) && arrayPath.length !== 0 ? [arrayPath] : ([] as unknown as QueryKey);
|
||||
}
|
||||
|
||||
return [
|
||||
arrayPath,
|
||||
{
|
||||
...(typeof input !== "undefined" && { input: input }),
|
||||
...(type && type !== "any" && { type: type }),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// copy starts
|
||||
// copied from trpc/trpc repo
|
||||
// ref: https://github.com/trpc/trpc/blob/main/packages/next/src/withTRPC.tsx#L37-#L58
|
||||
function transformQueryOrMutationCacheErrors<
|
||||
TState extends DehydratedState["queries"][0] | DehydratedState["mutations"][0]
|
||||
>(result: TState): TState {
|
||||
const error = result.state.error as Maybe<TRPCClientError<any>>;
|
||||
if (error instanceof Error && error.name === "TRPCClientError") {
|
||||
const newError: TRPCClientErrorLike<any> = {
|
||||
message: error.message,
|
||||
data: error.data,
|
||||
shape: error.shape,
|
||||
};
|
||||
return {
|
||||
...result,
|
||||
state: {
|
||||
...result.state,
|
||||
error: newError,
|
||||
},
|
||||
};
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// copy ends
|
||||
|
||||
interface CreateTRPCNextLayoutOptions<TRouter extends AnyRouter> {
|
||||
router: TRouter;
|
||||
createContext: () => MaybePromise<inferRouterContext<TRouter>>;
|
||||
transformer?: DataTransformer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type DecorateProcedure<TProcedure extends AnyProcedure> = TProcedure extends AnyQueryProcedure
|
||||
? {
|
||||
fetch(input: inferProcedureInput<TProcedure>): Promise<inferProcedureOutput<TProcedure>>;
|
||||
fetchInfinite(input: inferProcedureInput<TProcedure>): Promise<inferProcedureOutput<TProcedure>>;
|
||||
prefetch(input: inferProcedureInput<TProcedure>): Promise<inferProcedureOutput<TProcedure>>;
|
||||
prefetchInfinite(input: inferProcedureInput<TProcedure>): Promise<inferProcedureOutput<TProcedure>>;
|
||||
}
|
||||
: never;
|
||||
|
||||
type OmitNever<TType> = Pick<
|
||||
TType,
|
||||
{
|
||||
[K in keyof TType]: TType[K] extends never ? never : K;
|
||||
}[keyof TType]
|
||||
>;
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export type DecoratedProcedureRecord<
|
||||
TProcedures extends ProcedureRouterRecord,
|
||||
TPath extends string = ""
|
||||
> = OmitNever<{
|
||||
[TKey in keyof TProcedures]: TProcedures[TKey] extends AnyRouter
|
||||
? DecoratedProcedureRecord<TProcedures[TKey]["_def"]["record"], `${TPath}${TKey & string}.`>
|
||||
: TProcedures[TKey] extends AnyQueryProcedure
|
||||
? DecorateProcedure<TProcedures[TKey]>
|
||||
: never;
|
||||
}>;
|
||||
|
||||
type CreateTRPCNextLayout<TRouter extends AnyRouter> = DecoratedProcedureRecord<TRouter["_def"]["record"]> & {
|
||||
dehydrate(): Promise<DehydratedState>;
|
||||
queryClient: QueryClient;
|
||||
};
|
||||
|
||||
const getStateContainer = <TRouter extends AnyRouter>(opts: CreateTRPCNextLayoutOptions<TRouter>) => {
|
||||
let _trpc: {
|
||||
queryClient: QueryClient;
|
||||
context: inferRouterContext<TRouter>;
|
||||
} | null = null;
|
||||
|
||||
return () => {
|
||||
if (_trpc === null) {
|
||||
_trpc = {
|
||||
context: opts.createContext(),
|
||||
queryClient: new QueryClient(),
|
||||
};
|
||||
}
|
||||
|
||||
return _trpc;
|
||||
};
|
||||
};
|
||||
|
||||
export function createTRPCNextLayout<TRouter extends AnyRouter>(
|
||||
opts: CreateTRPCNextLayoutOptions<TRouter>
|
||||
): CreateTRPCNextLayout<TRouter> {
|
||||
const getState = getStateContainer(opts);
|
||||
|
||||
const transformer = opts.transformer ?? {
|
||||
serialize: (v) => v,
|
||||
deserialize: (v) => v,
|
||||
};
|
||||
|
||||
return createFlatProxy((key) => {
|
||||
const state = getState();
|
||||
const { queryClient } = state;
|
||||
if (key === "queryClient") {
|
||||
return queryClient;
|
||||
}
|
||||
|
||||
if (key === "dehydrate") {
|
||||
// copy starts
|
||||
// copied from trpc/trpc repo
|
||||
// ref: https://github.com/trpc/trpc/blob/main/packages/next/src/withTRPC.tsx#L214-#L229
|
||||
const dehydratedCache = dehydrate(queryClient, {
|
||||
shouldDehydrateQuery() {
|
||||
// makes sure errors are also dehydrated
|
||||
return true;
|
||||
},
|
||||
});
|
||||
|
||||
// since error instances can't be serialized, let's make them into `TRPCClientErrorLike`-objects
|
||||
const dehydratedCacheWithErrors = {
|
||||
...dehydratedCache,
|
||||
queries: dehydratedCache.queries.map(transformQueryOrMutationCacheErrors),
|
||||
mutations: dehydratedCache.mutations.map(transformQueryOrMutationCacheErrors),
|
||||
};
|
||||
|
||||
return () => transformer.serialize(dehydratedCacheWithErrors);
|
||||
}
|
||||
// copy ends
|
||||
|
||||
return createRecursiveProxy(async (callOpts) => {
|
||||
const path = [key, ...callOpts.path];
|
||||
const utilName = path.pop();
|
||||
const ctx = await state.context;
|
||||
|
||||
const caller = opts.router.createCaller(ctx);
|
||||
|
||||
const pathStr = path.join(".");
|
||||
const input = callOpts.args[0];
|
||||
|
||||
if (utilName === "fetchInfinite") {
|
||||
return queryClient.fetchInfiniteQuery(getArrayQueryKey([path, input], "infinite"), () =>
|
||||
caller.query(pathStr, input)
|
||||
);
|
||||
}
|
||||
|
||||
if (utilName === "prefetch") {
|
||||
return queryClient.prefetchQuery({
|
||||
queryKey: getArrayQueryKey([path, input], "query"),
|
||||
queryFn: async () => {
|
||||
const res = await callProcedure({
|
||||
procedures: opts.router._def.procedures,
|
||||
path: pathStr,
|
||||
rawInput: input,
|
||||
ctx,
|
||||
type: "query",
|
||||
});
|
||||
return res;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (utilName === "prefetchInfinite") {
|
||||
return queryClient.prefetchInfiniteQuery(getArrayQueryKey([path, input], "infinite"), () =>
|
||||
caller.query(pathStr, input)
|
||||
);
|
||||
}
|
||||
|
||||
return queryClient.fetchQuery(getArrayQueryKey([path, input], "query"), () =>
|
||||
caller.query(pathStr, input)
|
||||
);
|
||||
}) as CreateTRPCNextLayout<TRouter>;
|
||||
});
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { headers } from "next/headers";
|
||||
import superjson from "superjson";
|
||||
|
||||
import { CALCOM_VERSION } from "@calcom/lib/constants";
|
||||
import prisma, { readonlyPrisma } from "@calcom/prisma";
|
||||
import { appRouter } from "@calcom/trpc/server/routers/_app";
|
||||
|
||||
import { createTRPCNextLayout } from "./createTRPCNextLayout";
|
||||
|
||||
export async function ssgInit() {
|
||||
const locale = headers().get("x-locale") ?? "en";
|
||||
|
||||
const i18n = (await serverSideTranslations(locale, ["common"])) || "en";
|
||||
|
||||
const ssg = createTRPCNextLayout({
|
||||
router: appRouter,
|
||||
transformer: superjson,
|
||||
createContext() {
|
||||
return { prisma, insightsDb: readonlyPrisma, session: null, locale, i18n };
|
||||
},
|
||||
});
|
||||
|
||||
// i18n translations are already retrieved from serverSideTranslations call, there is no need to run a i18n.fetch
|
||||
// we can set query data directly to the queryClient
|
||||
const queryKey = [
|
||||
["viewer", "public", "i18n"],
|
||||
{ input: { locale, CalComVersion: CALCOM_VERSION }, type: "query" },
|
||||
];
|
||||
|
||||
ssg.queryClient.setQueryData(queryKey, { i18n });
|
||||
|
||||
return ssg;
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
import { type GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import { headers, cookies } from "next/headers";
|
||||
import superjson from "superjson";
|
||||
|
||||
import { getLocale } from "@calcom/features/auth/lib/getLocale";
|
||||
import { CALCOM_VERSION } from "@calcom/lib/constants";
|
||||
import prisma, { readonlyPrisma } from "@calcom/prisma";
|
||||
import { appRouter } from "@calcom/trpc/server/routers/_app";
|
||||
|
||||
import { createTRPCNextLayout } from "./createTRPCNextLayout";
|
||||
|
||||
export async function ssrInit(options?: { noI18nPreload: boolean }) {
|
||||
const req = {
|
||||
headers: headers(),
|
||||
cookies: cookies(),
|
||||
};
|
||||
|
||||
const locale = await getLocale(req);
|
||||
|
||||
const i18n = (await serverSideTranslations(locale, ["common", "vital"])) || "en";
|
||||
|
||||
const ssr = createTRPCNextLayout({
|
||||
router: appRouter,
|
||||
transformer: superjson,
|
||||
createContext() {
|
||||
return {
|
||||
prisma,
|
||||
insightsDb: readonlyPrisma,
|
||||
session: null,
|
||||
locale,
|
||||
i18n,
|
||||
req: req as unknown as GetServerSidePropsContext["req"],
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
// i18n translations are already retrieved from serverSideTranslations call, there is no need to run a i18n.fetch
|
||||
// we can set query data directly to the queryClient
|
||||
const queryKey = [
|
||||
["viewer", "public", "i18n"],
|
||||
{ input: { locale, CalComVersion: CALCOM_VERSION }, type: "query" },
|
||||
];
|
||||
if (!options?.noI18nPreload) {
|
||||
ssr.queryClient.setQueryData(queryKey, { i18n });
|
||||
}
|
||||
|
||||
await Promise.allSettled([
|
||||
// So feature flags are available on first render
|
||||
ssr.viewer.features.map.prefetch(),
|
||||
// Provides a better UX to the users who have already upgraded.
|
||||
ssr.viewer.teams.hasTeamPlan.prefetch(),
|
||||
ssr.viewer.public.session.prefetch(),
|
||||
]);
|
||||
|
||||
return ssr;
|
||||
}
|
|
@ -67,5 +67,5 @@ const getPageProps = async ({ params }: { params: Record<string, string | string
|
|||
};
|
||||
|
||||
// @ts-expect-error getData arg
|
||||
export default WithLayout({ getData: getPageProps, Page: CategoryPage })<P>;
|
||||
export default WithLayout({ getData: getPageProps, Page: CategoryPage })<"P">;
|
||||
export const dynamic = "force-static";
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import LegacyPage from "@pages/apps/categories/index";
|
||||
import { ssrInit } from "app/_trpc/ssrInit";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { cookies, headers } from "next/headers";
|
||||
|
||||
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
return await _generateMetadata(
|
||||
() => `Categories | ${APP_NAME}`,
|
||||
|
@ -15,12 +17,12 @@ export const generateMetadata = async () => {
|
|||
);
|
||||
};
|
||||
|
||||
async function getPageProps() {
|
||||
const ssr = await ssrInit();
|
||||
const req = { headers: headers(), cookies: cookies() };
|
||||
const getData = async (ctx: ReturnType<typeof buildLegacyCtx>) => {
|
||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
||||
const ssr = await ssrInit(ctx);
|
||||
|
||||
// @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest | IncomingMessage
|
||||
const session = await getServerSession({ req });
|
||||
const session = await getServerSession({ req: ctx.req });
|
||||
|
||||
let appStore;
|
||||
if (session?.user?.id) {
|
||||
|
@ -38,8 +40,8 @@ async function getPageProps() {
|
|||
|
||||
return {
|
||||
categories: Object.entries(categories).map(([name, count]) => ({ name, count })),
|
||||
dehydratedState: await ssr.dehydrate(),
|
||||
dehydratedState: ssr.dehydrate(),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
export default WithLayout({ getData: getPageProps, Page: LegacyPage, getLayout: null })<"P">;
|
||||
export default WithLayout({ getData, Page: LegacyPage, getLayout: null })<"P">;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import AppsPage from "@pages/apps";
|
||||
import { ssrInit } from "app/_trpc/ssrInit";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
|
||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||
|
@ -11,7 +10,9 @@ import getUserAdminTeams from "@calcom/features/ee/teams/lib/getUserAdminTeams";
|
|||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import type { AppCategories } from "@calcom/prisma/enums";
|
||||
|
||||
import PageWrapper from "@components/PageWrapperAppDir";
|
||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
export const generateMetadata = async () => {
|
||||
return await _generateMetadata(
|
||||
|
@ -20,12 +21,12 @@ export const generateMetadata = async () => {
|
|||
);
|
||||
};
|
||||
|
||||
const getPageProps = async () => {
|
||||
const ssr = await ssrInit();
|
||||
const req = { headers: headers(), cookies: cookies() };
|
||||
const getData = async (ctx: ReturnType<typeof buildLegacyCtx>) => {
|
||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
||||
const ssr = await ssrInit(ctx);
|
||||
|
||||
// @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest
|
||||
const session = await getServerSession({ req });
|
||||
const session = await getServerSession({ req: ctx.req });
|
||||
|
||||
let appStore, userAdminTeams: UserAdminTeams;
|
||||
if (session?.user?.id) {
|
||||
|
@ -58,24 +59,8 @@ const getPageProps = async () => {
|
|||
}),
|
||||
appStore,
|
||||
userAdminTeams,
|
||||
dehydratedState: await ssr.dehydrate(),
|
||||
dehydratedState: ssr.dehydrate(),
|
||||
};
|
||||
};
|
||||
|
||||
export default async function AppPageAppDir() {
|
||||
const { categories, appStore, userAdminTeams, dehydratedState } = await getPageProps();
|
||||
|
||||
const h = headers();
|
||||
const nonce = h.get("x-nonce") ?? undefined;
|
||||
|
||||
return (
|
||||
<PageWrapper
|
||||
getLayout={getLayout}
|
||||
requiresLicense={false}
|
||||
nonce={nonce}
|
||||
themeBasis={null}
|
||||
dehydratedState={dehydratedState}>
|
||||
<AppsPage categories={categories} appStore={appStore} userAdminTeams={userAdminTeams} />
|
||||
</PageWrapper>
|
||||
);
|
||||
}
|
||||
export default WithLayout({ getLayout, getData, Page: AppsPage });
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
import { ssgInit } from "app/_trpc/ssgInit";
|
||||
import type { Params } from "app/_types";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { notFound } from "next/navigation";
|
||||
import type { ReactElement } from "react";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
|
||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { ssgInit } from "@server/lib/ssg";
|
||||
|
||||
const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const;
|
||||
|
||||
const querySchema = z.object({
|
||||
status: z.enum(validStatuses),
|
||||
});
|
||||
|
||||
type Props = { params: Params; children: ReactElement };
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => `${APP_NAME} | ${t("bookings")}`,
|
||||
|
@ -27,18 +26,18 @@ export const generateStaticParams = async () => {
|
|||
return validStatuses.map((status) => ({ status }));
|
||||
};
|
||||
|
||||
const getData = async ({ params }: { params: Params }) => {
|
||||
const parsedParams = querySchema.safeParse(params);
|
||||
const getData = async (ctx: ReturnType<typeof buildLegacyCtx>) => {
|
||||
const parsedParams = querySchema.safeParse(ctx.params);
|
||||
|
||||
if (!parsedParams.success) {
|
||||
notFound();
|
||||
}
|
||||
|
||||
const ssg = await ssgInit();
|
||||
const ssg = await ssgInit(ctx);
|
||||
|
||||
return {
|
||||
status: parsedParams.data.status,
|
||||
dehydratedState: await ssg.dehydrate(),
|
||||
dehydratedState: ssg.dehydrate(),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import LegacyPage from "@pages/getting-started/[[...step]]";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { cookies, headers } from "next/headers";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
const getData = async (ctx: ReturnType<typeof buildLegacyCtx>) => {
|
||||
const req = { headers: headers(), cookies: cookies() };
|
||||
|
||||
//@ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest
|
||||
const session = await getServerSession({ req });
|
||||
|
||||
if (!session?.user?.id) {
|
||||
return redirect("/auth/login");
|
||||
}
|
||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
||||
const ssr = await ssrInit(ctx);
|
||||
await ssr.viewer.me.prefetch();
|
||||
|
||||
const user = await prisma.user.findUnique({
|
||||
where: {
|
||||
id: session.user.id,
|
||||
},
|
||||
select: {
|
||||
completedOnboarding: true,
|
||||
teams: {
|
||||
select: {
|
||||
accepted: true,
|
||||
team: {
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
logo: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new Error("User from session not found");
|
||||
}
|
||||
|
||||
if (user.completedOnboarding) {
|
||||
redirect("/event-types");
|
||||
}
|
||||
|
||||
return {
|
||||
dehydratedState: ssr.dehydrate(),
|
||||
hasPendingInvites: user.teams.find((team: any) => team.accepted === false) ?? false,
|
||||
requiresLicense: false,
|
||||
themeBasis: null,
|
||||
};
|
||||
};
|
||||
|
||||
export default WithLayout({ getLayout: null, getData, Page: LegacyPage });
|
|
@ -0,0 +1,5 @@
|
|||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||
|
||||
export default WithLayout({ getLayout })<"L">;
|
|
@ -0,0 +1,11 @@
|
|||
import { _generateMetadata } from "app/_utils";
|
||||
|
||||
import Page from "@calcom/features/ee/teams/pages/team-appearance-view";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("booking_appearance"),
|
||||
(t) => t("appearance_team_description")
|
||||
);
|
||||
|
||||
export default Page;
|
|
@ -0,0 +1,5 @@
|
|||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||
|
||||
export default WithLayout({ getLayout })<"L">;
|
|
@ -0,0 +1,10 @@
|
|||
import Page from "@pages/settings/billing/index";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("billing"),
|
||||
(t) => t("team_billing_description")
|
||||
);
|
||||
|
||||
export default Page;
|
|
@ -0,0 +1,5 @@
|
|||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||
|
||||
export default WithLayout({ getLayout })<"L">;
|
|
@ -0,0 +1,11 @@
|
|||
import { _generateMetadata } from "app/_utils";
|
||||
|
||||
import Page from "@calcom/features/ee/teams/pages/team-members-view";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("team_members"),
|
||||
(t) => t("members_team_description")
|
||||
);
|
||||
|
||||
export default Page;
|
|
@ -0,0 +1,11 @@
|
|||
import LegacyPage, { GetLayout } from "@pages/settings/teams/[id]/onboard-members";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("add_team_members"),
|
||||
(t) => t("add_team_members_description")
|
||||
);
|
||||
|
||||
export default WithLayout({ Page: LegacyPage, getLayout: GetLayout })<"P">;
|
|
@ -0,0 +1,5 @@
|
|||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||
|
||||
export default WithLayout({ getLayout })<"L">;
|
|
@ -0,0 +1,11 @@
|
|||
import { _generateMetadata } from "app/_utils";
|
||||
|
||||
import Page from "@calcom/features/ee/teams/pages/team-profile-view";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("profile"),
|
||||
(t) => t("profile_team_description")
|
||||
);
|
||||
|
||||
export default Page;
|
|
@ -0,0 +1,5 @@
|
|||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||
|
||||
export default WithLayout({ getLayout })<"L">;
|
|
@ -0,0 +1,11 @@
|
|||
import { _generateMetadata } from "app/_utils";
|
||||
|
||||
import Page from "@calcom/features/ee/sso/page/teams-sso-view";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("sso_configuration"),
|
||||
(t) => t("sso_configuration_description")
|
||||
);
|
||||
|
||||
export default Page;
|
|
@ -0,0 +1,5 @@
|
|||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import { getLayout } from "@calcom/features/settings/layouts/SettingsLayoutAppDir";
|
||||
|
||||
export default WithLayout({ getLayout })<"L">;
|
|
@ -0,0 +1,11 @@
|
|||
import LegacyPage, { LayoutWrapper } from "@pages/settings/teams/new/index";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("create_new_team"),
|
||||
(t) => t("create_new_team_description")
|
||||
);
|
||||
|
||||
export default WithLayout({ Page: LegacyPage, getLayout: LayoutWrapper })<"P">;
|
|
@ -0,0 +1,11 @@
|
|||
import { _generateMetadata } from "app/_utils";
|
||||
|
||||
import Page from "@calcom/features/ee/teams/pages/team-listing-view";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("teams"),
|
||||
(t) => t("create_manage_teams_collaborative")
|
||||
);
|
||||
|
||||
export default Page;
|
|
@ -1,24 +1,29 @@
|
|||
import OldPage from "@pages/teams/index";
|
||||
import { ssrInit } from "app/_trpc/ssrInit";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import { type GetServerSidePropsContext } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getLayout } from "@calcom/features/MainLayoutAppDir";
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
|
||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("teams"),
|
||||
(t) => t("create_manage_teams_collaborative")
|
||||
);
|
||||
|
||||
async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolvedUrl">) {
|
||||
const ssr = await ssrInit();
|
||||
async function getData(context: ReturnType<typeof buildLegacyCtx>) {
|
||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
||||
const ssr = await ssrInit(context);
|
||||
|
||||
await ssr.viewer.me.prefetch();
|
||||
|
||||
const session = await getServerSession({
|
||||
// @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest | (IncomingMessage & { cookies: Partial<{ [key: string]: string; }>; })'.
|
||||
req: context.req,
|
||||
});
|
||||
|
||||
|
@ -29,8 +34,7 @@ async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolve
|
|||
return redirect(callbackUrl ? `/auth/login?callbackUrl=${callbackUrl}` : "/auth/login");
|
||||
}
|
||||
|
||||
return { dehydratedState: await ssr.dehydrate() };
|
||||
return { dehydratedState: ssr.dehydrate() };
|
||||
}
|
||||
|
||||
// @ts-expect-error getData arg
|
||||
export default WithLayout({ getData, getLayout, Page: OldPage })<"P">;
|
||||
|
|
|
@ -1,15 +1,17 @@
|
|||
import OldPage from "@pages/video/[uid]";
|
||||
import { ssrInit } from "app/_trpc/ssrInit";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
import MarkdownIt from "markdown-it";
|
||||
import { type GetServerSidePropsContext } from "next";
|
||||
import { redirect } from "next/navigation";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
|
||||
|
||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
() => `${APP_NAME} Video`,
|
||||
|
@ -18,8 +20,9 @@ export const generateMetadata = async () =>
|
|||
|
||||
const md = new MarkdownIt("default", { html: true, breaks: true, linkify: true });
|
||||
|
||||
async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolvedUrl">) {
|
||||
const ssr = await ssrInit();
|
||||
async function getData(context: ReturnType<typeof buildLegacyCtx>) {
|
||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
||||
const ssr = await ssrInit(context);
|
||||
|
||||
const booking = await prisma.booking.findUnique({
|
||||
where: {
|
||||
|
@ -76,6 +79,7 @@ async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolve
|
|||
endTime: booking.endTime.toString(),
|
||||
});
|
||||
|
||||
// @ts-expect-error Type '{ headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }' is not assignable to type 'NextApiRequest | (IncomingMessage & { cookies: Partial<{ [key: string]: string; }>; })'.
|
||||
const session = await getServerSession({ req: context.req });
|
||||
|
||||
// set meetingPassword to null for guests
|
||||
|
@ -94,9 +98,8 @@ async function getData(context: Omit<GetServerSidePropsContext, "res" | "resolve
|
|||
...bookingObj,
|
||||
...(bookingObj.description && { description: md.render(bookingObj.description) }),
|
||||
},
|
||||
dehydratedState: await ssr.dehydrate(),
|
||||
dehydratedState: ssr.dehydrate(),
|
||||
};
|
||||
}
|
||||
|
||||
// @ts-expect-error getData arg
|
||||
export default WithLayout({ getData, Page: OldPage, getLayout: null })<"P">;
|
||||
|
|
|
@ -1,19 +1,23 @@
|
|||
import LegacyPage from "@pages/video/no-meeting-found";
|
||||
import { ssrInit } from "app/_trpc/ssrInit";
|
||||
import { _generateMetadata } from "app/_utils";
|
||||
import { WithLayout } from "app/layoutHOC";
|
||||
|
||||
import type { buildLegacyCtx } from "@lib/buildLegacyCtx";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
export const generateMetadata = async () =>
|
||||
await _generateMetadata(
|
||||
(t) => t("no_meeting_found"),
|
||||
(t) => t("no_meeting_found")
|
||||
);
|
||||
|
||||
const getData = async () => {
|
||||
const ssr = await ssrInit();
|
||||
const getData = async (context: ReturnType<typeof buildLegacyCtx>) => {
|
||||
// @ts-expect-error Argument of type '{ query: Params; params: Params; req: { headers: ReadonlyHeaders; cookies: ReadonlyRequestCookies; }; }' is not assignable to parameter of type 'GetServerSidePropsContext'.
|
||||
const ssr = await ssrInit(context);
|
||||
|
||||
return {
|
||||
dehydratedState: await ssr.dehydrate(),
|
||||
dehydratedState: ssr.dehydrate(),
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
|
|||
|
||||
const ActionButtons = () => {
|
||||
return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
|
||||
<div className="me-2 ms-2 flex flex-row space-x-2">
|
||||
<div className="relative bottom-[6px] me-2 ms-2 flex flex-row space-x-2">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => setOpenDialogSaveUsername(true)}
|
||||
|
@ -137,7 +137,7 @@ const UsernameTextfield = (props: ICustomUsernameProps & Partial<React.Component
|
|||
{currentUsername !== inputUsernameValue && (
|
||||
<div className="absolute right-[2px] top-6 flex flex-row">
|
||||
<span className={classNames("mx-2 py-3.5")}>
|
||||
{usernameIsAvailable ? <Check className="h-4 w-4" /> : <></>}
|
||||
{usernameIsAvailable ? <Check className="relative bottom-[6px] h-4 w-4" /> : <></>}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -3,12 +3,14 @@ import prismock from "../../../tests/libs/__mocks__/prisma";
|
|||
import { describe, expect, it } from "vitest";
|
||||
import type { z } from "zod";
|
||||
|
||||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import type { MembershipRole, Prisma } from "@calcom/prisma/client";
|
||||
import { RedirectType } from "@calcom/prisma/enums";
|
||||
import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { moveTeamToOrg, moveUserToOrg, removeTeamFromOrg, removeUserFromOrg } from "./orgMigration";
|
||||
|
||||
const WEBSITE_PROTOCOL = new URL(WEBSITE_URL).protocol;
|
||||
describe("orgMigration", () => {
|
||||
describe("moveUserToOrg", () => {
|
||||
describe("when user email does not match orgAutoAcceptEmail", () => {
|
||||
|
@ -317,11 +319,13 @@ describe("orgMigration", () => {
|
|||
await expectTeamToBeAPartOfOrg({
|
||||
teamId: team1.id,
|
||||
orgId: dbOrg.id,
|
||||
teamSlugInOrg: team1.slug,
|
||||
});
|
||||
|
||||
await expectTeamToBeAPartOfOrg({
|
||||
teamId: team2.id,
|
||||
orgId: dbOrg.id,
|
||||
teamSlugInOrg: team2.slug,
|
||||
});
|
||||
|
||||
await expectUserToBeNotAPartOfTheOrg({
|
||||
|
@ -873,6 +877,7 @@ describe("orgMigration", () => {
|
|||
id: 1,
|
||||
name: "Team 1",
|
||||
slug: "team1",
|
||||
newSlug: "team1-new-slug",
|
||||
},
|
||||
targetOrg: {
|
||||
id: 2,
|
||||
|
@ -902,19 +907,23 @@ describe("orgMigration", () => {
|
|||
|
||||
await moveTeamToOrg({
|
||||
teamId: data.teamToMigrate.id,
|
||||
targetOrgId: data.targetOrg.id,
|
||||
targetOrg: {
|
||||
id: data.targetOrg.id,
|
||||
teamSlug: data.teamToMigrate.newSlug,
|
||||
},
|
||||
});
|
||||
|
||||
await expectTeamToBeAPartOfOrg({
|
||||
teamId: data.teamToMigrate.id,
|
||||
orgId: data.targetOrg.id,
|
||||
teamSlugInOrg: data.teamToMigrate.newSlug,
|
||||
});
|
||||
|
||||
expectTeamRedirectToBeEnabled({
|
||||
from: {
|
||||
teamSlug: data.teamToMigrate.slug,
|
||||
},
|
||||
to: data.teamToMigrate.slug,
|
||||
to: data.teamToMigrate.newSlug,
|
||||
orgSlug: data.targetOrg.slug,
|
||||
});
|
||||
});
|
||||
|
@ -1198,7 +1207,15 @@ async function expectUserToBeNotAPartOfTheOrg({
|
|||
expect(membership).toBeUndefined();
|
||||
}
|
||||
|
||||
async function expectTeamToBeAPartOfOrg({ teamId, orgId }: { teamId: number; orgId: number }) {
|
||||
async function expectTeamToBeAPartOfOrg({
|
||||
teamId,
|
||||
orgId,
|
||||
teamSlugInOrg,
|
||||
}: {
|
||||
teamId: number;
|
||||
orgId: number;
|
||||
teamSlugInOrg: string | null;
|
||||
}) {
|
||||
const migratedTeam = await prismock.team.findUnique({
|
||||
where: {
|
||||
id: teamId,
|
||||
|
@ -1208,7 +1225,11 @@ async function expectTeamToBeAPartOfOrg({ teamId, orgId }: { teamId: number; org
|
|||
throw new Error(`Team with id ${teamId} does not exist`);
|
||||
}
|
||||
|
||||
if (!teamSlugInOrg) {
|
||||
throw new Error(`teamSlugInOrg should be defined`);
|
||||
}
|
||||
expect(migratedTeam.parentId).toBe(orgId);
|
||||
expect(migratedTeam.slug).toBe(teamSlugInOrg);
|
||||
}
|
||||
|
||||
async function expectTeamToBeNotPartOfAnyOrganization({ teamId }: { teamId: number }) {
|
||||
|
@ -1347,7 +1368,7 @@ async function expectRedirectToBeEnabled({
|
|||
}
|
||||
|
||||
expect(redirect).not.toBeNull();
|
||||
expect(redirect?.toUrl).toBe(`http://${orgSlug}.cal.local:3000/${to}`);
|
||||
expect(redirect?.toUrl).toBe(`${WEBSITE_PROTOCOL}//${orgSlug}.cal.local:3000/${to}`);
|
||||
if (!redirect) {
|
||||
throw new Error(`Redirect doesn't exist for ${JSON.stringify(tempOrgRedirectWhere)}`);
|
||||
}
|
||||
|
|
|
@ -182,35 +182,39 @@ export async function removeUserFromOrg({ targetOrgId, userId }: { targetOrgId:
|
|||
* Make sure that the migration is idempotent
|
||||
*/
|
||||
export async function moveTeamToOrg({
|
||||
targetOrgId,
|
||||
targetOrg,
|
||||
teamId,
|
||||
moveMembers,
|
||||
}: {
|
||||
targetOrgId: number;
|
||||
targetOrg: { id: number; teamSlug: string };
|
||||
teamId: number;
|
||||
moveMembers?: boolean;
|
||||
}) {
|
||||
const possibleOrg = await getTeamOrThrowError(targetOrgId);
|
||||
const movedTeam = await dbMoveTeamToOrg({ teamId, targetOrgId });
|
||||
const possibleOrg = await getTeamOrThrowError(targetOrg.id);
|
||||
const { oldTeamSlug, updatedTeam } = await dbMoveTeamToOrg({ teamId, targetOrg });
|
||||
|
||||
const teamMetadata = teamMetadataSchema.parse(possibleOrg?.metadata);
|
||||
|
||||
if (!teamMetadata?.isOrganization) {
|
||||
throw new Error(`${targetOrgId} is not an Org`);
|
||||
throw new Error(`${targetOrg.id} is not an Org`);
|
||||
}
|
||||
|
||||
const targetOrganization = possibleOrg;
|
||||
const orgMetadata = teamMetadata;
|
||||
await addTeamRedirect(movedTeam.slug, targetOrganization.slug || orgMetadata.requestedSlug || null);
|
||||
await setOrgSlugIfNotSet({ slug: targetOrganization.slug }, orgMetadata, targetOrgId);
|
||||
await addTeamRedirect({
|
||||
oldTeamSlug,
|
||||
teamSlug: updatedTeam.slug,
|
||||
orgSlug: targetOrganization.slug || orgMetadata.requestedSlug || null,
|
||||
});
|
||||
await setOrgSlugIfNotSet({ slug: targetOrganization.slug }, orgMetadata, targetOrg.id);
|
||||
if (moveMembers) {
|
||||
for (const membership of movedTeam.members) {
|
||||
for (const membership of updatedTeam.members) {
|
||||
await moveUserToOrg({
|
||||
user: {
|
||||
id: membership.userId,
|
||||
},
|
||||
targetOrg: {
|
||||
id: targetOrgId,
|
||||
id: targetOrg.id,
|
||||
membership: {
|
||||
role: membership.role,
|
||||
accepted: membership.accepted,
|
||||
|
@ -220,21 +224,30 @@ export async function moveTeamToOrg({
|
|||
});
|
||||
}
|
||||
}
|
||||
log.debug(`Successfully moved team ${teamId} to org ${targetOrgId}`);
|
||||
log.debug(`Successfully moved team ${teamId} to org ${targetOrg.id}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make sure that the migration is idempotent
|
||||
*/
|
||||
export async function removeTeamFromOrg({ targetOrgId, teamId }: { targetOrgId: number; teamId: number }) {
|
||||
const removedTeam = await dbRemoveTeamFromOrg({ teamId, targetOrgId });
|
||||
const removedTeam = await dbRemoveTeamFromOrg({ teamId });
|
||||
|
||||
await removeTeamRedirect(removedTeam.slug);
|
||||
|
||||
log.debug(`Successfully removed team ${teamId} from org ${targetOrgId}`);
|
||||
}
|
||||
|
||||
async function dbMoveTeamToOrg({ teamId, targetOrgId }: { teamId: number; targetOrgId: number }) {
|
||||
async function dbMoveTeamToOrg({
|
||||
teamId,
|
||||
targetOrg,
|
||||
}: {
|
||||
teamId: number;
|
||||
targetOrg: {
|
||||
id: number;
|
||||
teamSlug: string;
|
||||
};
|
||||
}) {
|
||||
const team = await prisma.team.findUnique({
|
||||
where: {
|
||||
id: teamId,
|
||||
|
@ -251,21 +264,30 @@ async function dbMoveTeamToOrg({ teamId, targetOrgId }: { teamId: number; target
|
|||
});
|
||||
}
|
||||
|
||||
if (team.parentId === targetOrgId) {
|
||||
log.warn(`Team ${teamId} is already in org ${targetOrgId}`);
|
||||
return team;
|
||||
}
|
||||
const teamMetadata = teamMetadataSchema.parse(team?.metadata);
|
||||
const oldTeamSlug = teamMetadata?.migratedToOrgFrom?.teamSlug || team.slug;
|
||||
|
||||
await prisma.team.update({
|
||||
const updatedTeam = await prisma.team.update({
|
||||
where: {
|
||||
id: teamId,
|
||||
},
|
||||
data: {
|
||||
parentId: targetOrgId,
|
||||
slug: targetOrg.teamSlug,
|
||||
parentId: targetOrg.id,
|
||||
metadata: {
|
||||
...teamMetadata,
|
||||
migratedToOrgFrom: {
|
||||
teamSlug: team.slug,
|
||||
lastMigrationTime: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
include: {
|
||||
members: true,
|
||||
},
|
||||
});
|
||||
|
||||
return team;
|
||||
return { oldTeamSlug, updatedTeam };
|
||||
}
|
||||
|
||||
async function getUniqueUserThatDoesntBelongToOrg(
|
||||
|
@ -460,11 +482,25 @@ async function addRedirect({
|
|||
}
|
||||
}
|
||||
|
||||
async function addTeamRedirect(teamSlug: string | null, orgSlug: string | null) {
|
||||
async function addTeamRedirect({
|
||||
oldTeamSlug,
|
||||
teamSlug,
|
||||
orgSlug,
|
||||
}: {
|
||||
oldTeamSlug: string | null;
|
||||
teamSlug: string | null;
|
||||
orgSlug: string | null;
|
||||
}) {
|
||||
if (!oldTeamSlug) {
|
||||
throw new HttpError({
|
||||
statusCode: 400,
|
||||
message: "No oldSlug for team. Not adding the redirect",
|
||||
});
|
||||
}
|
||||
if (!teamSlug) {
|
||||
throw new HttpError({
|
||||
statusCode: 400,
|
||||
message: "No slug for team. Not removing the redirect",
|
||||
message: "No slug for team. Not adding the redirect",
|
||||
});
|
||||
}
|
||||
if (!orgSlug) {
|
||||
|
@ -477,13 +513,13 @@ async function addTeamRedirect(teamSlug: string | null, orgSlug: string | null)
|
|||
where: {
|
||||
from_type_fromOrgId: {
|
||||
type: RedirectType.Team,
|
||||
from: teamSlug,
|
||||
from: oldTeamSlug,
|
||||
fromOrgId: 0,
|
||||
},
|
||||
},
|
||||
create: {
|
||||
type: RedirectType.Team,
|
||||
from: teamSlug,
|
||||
from: oldTeamSlug,
|
||||
fromOrgId: 0,
|
||||
toUrl: `${orgUrlPrefix}/${teamSlug}`,
|
||||
},
|
||||
|
@ -678,7 +714,7 @@ async function removeUserAlongWithItsTeamsRedirects({
|
|||
}
|
||||
}
|
||||
|
||||
async function dbRemoveTeamFromOrg({ teamId, targetOrgId }: { teamId: number; targetOrgId: number }) {
|
||||
async function dbRemoveTeamFromOrg({ teamId }: { teamId: number }) {
|
||||
const team = await prisma.team.findUnique({
|
||||
where: {
|
||||
id: teamId,
|
||||
|
@ -692,13 +728,7 @@ async function dbRemoveTeamFromOrg({ teamId, targetOrgId }: { teamId: number; ta
|
|||
});
|
||||
}
|
||||
|
||||
if (team.parentId !== targetOrgId) {
|
||||
log.warn(`Team ${teamId} is not part of org ${targetOrgId}. Not updating`);
|
||||
return {
|
||||
slug: team.slug,
|
||||
};
|
||||
}
|
||||
|
||||
const teamMetadata = teamMetadataSchema.parse(team?.metadata);
|
||||
try {
|
||||
return await prisma.team.update({
|
||||
where: {
|
||||
|
@ -706,6 +736,14 @@ async function dbRemoveTeamFromOrg({ teamId, targetOrgId }: { teamId: number; ta
|
|||
},
|
||||
data: {
|
||||
parentId: null,
|
||||
slug: teamMetadata?.migratedToOrgFrom?.teamSlug || team.slug,
|
||||
metadata: {
|
||||
...teamMetadata,
|
||||
migratedToOrgFrom: {
|
||||
reverted: true,
|
||||
lastRevertTime: new Date().toISOString(),
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
slug: true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "@calcom/web",
|
||||
"version": "3.6.1",
|
||||
"version": "3.6.3",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE=true next build",
|
||||
|
|
|
@ -3,9 +3,9 @@ import type { NextApiRequest, NextApiResponse } from "next";
|
|||
import isAuthorized from "@calcom/features/auth/lib/oAuthAuthorization";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const requriedScopes = ["READ_PROFILE"];
|
||||
const requiredScopes = ["READ_PROFILE"];
|
||||
|
||||
const account = await isAuthorized(req, requriedScopes);
|
||||
const account = await isAuthorized(req, requiredScopes);
|
||||
|
||||
if (!account) {
|
||||
return res.status(401).json({ message: "Unauthorized" });
|
||||
|
|
|
@ -40,7 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
return res.status(400).json({ message: JSON.stringify(parsedBody.error) });
|
||||
}
|
||||
|
||||
const { teamId, targetOrgId, moveMembers } = parsedBody.data;
|
||||
const { teamId, targetOrgId, moveMembers, teamSlugInOrganization } = parsedBody.data;
|
||||
const isAllowed = isAdmin;
|
||||
if (!isAllowed) {
|
||||
return res.status(403).json({ message: "Not Authorized" });
|
||||
|
@ -48,7 +48,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
|
||||
try {
|
||||
await moveTeamToOrg({
|
||||
targetOrgId,
|
||||
targetOrg: {
|
||||
id: targetOrgId,
|
||||
teamSlug: teamSlugInOrganization,
|
||||
},
|
||||
teamId,
|
||||
moveMembers,
|
||||
});
|
||||
|
|
|
@ -486,48 +486,24 @@ export default function Success(props: SuccessProps) {
|
|||
<div className="mt-3 font-medium">{t("where")}</div>
|
||||
<div className="col-span-2 mt-3" data-testid="where">
|
||||
{!rescheduleLocation || locationToDisplay === rescheduleLocationToDisplay ? (
|
||||
locationToDisplay.startsWith("http") ? (
|
||||
<a
|
||||
href={locationToDisplay}
|
||||
target="_blank"
|
||||
title={locationToDisplay}
|
||||
className="text-default flex items-center gap-2"
|
||||
rel="noreferrer">
|
||||
{providerName || "Link"}
|
||||
<ExternalLink className="text-default inline h-4 w-4" />
|
||||
</a>
|
||||
) : (
|
||||
locationToDisplay
|
||||
)
|
||||
<DisplayLocation
|
||||
locationToDisplay={locationToDisplay}
|
||||
providerName={providerName}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{!!formerTime &&
|
||||
(locationToDisplay.startsWith("http") ? (
|
||||
<a
|
||||
href={locationToDisplay}
|
||||
target="_blank"
|
||||
title={locationToDisplay}
|
||||
className="text-default flex items-center gap-2 line-through"
|
||||
rel="noreferrer">
|
||||
{providerName || "Link"}
|
||||
<ExternalLink className="text-default inline h-4 w-4" />
|
||||
</a>
|
||||
) : (
|
||||
<p className="line-through">{locationToDisplay}</p>
|
||||
))}
|
||||
{rescheduleLocationToDisplay.startsWith("http") ? (
|
||||
<a
|
||||
href={rescheduleLocationToDisplay}
|
||||
target="_blank"
|
||||
title={rescheduleLocationToDisplay}
|
||||
className="text-default flex items-center gap-2"
|
||||
rel="noreferrer">
|
||||
{rescheduleProviderName || "Link"}
|
||||
<ExternalLink className="text-default inline h-4 w-4" />
|
||||
</a>
|
||||
) : (
|
||||
rescheduleLocationToDisplay
|
||||
{!!formerTime && (
|
||||
<DisplayLocation
|
||||
locationToDisplay={locationToDisplay}
|
||||
providerName={providerName}
|
||||
className="line-through"
|
||||
/>
|
||||
)}
|
||||
|
||||
<DisplayLocation
|
||||
locationToDisplay={rescheduleLocationToDisplay}
|
||||
providerName={rescheduleProviderName}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
@ -830,6 +806,29 @@ export default function Success(props: SuccessProps) {
|
|||
);
|
||||
}
|
||||
|
||||
const DisplayLocation = ({
|
||||
locationToDisplay,
|
||||
providerName,
|
||||
className,
|
||||
}: {
|
||||
locationToDisplay: string;
|
||||
providerName?: string;
|
||||
className?: string;
|
||||
}) =>
|
||||
locationToDisplay.startsWith("http") ? (
|
||||
<a
|
||||
href={locationToDisplay}
|
||||
target="_blank"
|
||||
title={locationToDisplay}
|
||||
className={classNames("text-default flex items-center gap-2", className)}
|
||||
rel="noreferrer">
|
||||
{providerName || "Link"}
|
||||
<ExternalLink className="text-default inline h-4 w-4" />
|
||||
</a>
|
||||
) : (
|
||||
<p className={className}>{locationToDisplay}</p>
|
||||
);
|
||||
|
||||
Success.isBookingPage = true;
|
||||
Success.PageWrapper = PageWrapper;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import { serverSideTranslations } from "next-i18next/serverSideTranslations";
|
||||
import Head from "next/head";
|
||||
|
@ -51,13 +53,18 @@ const stepRouteSchema = z.object({
|
|||
const OnboardingPage = () => {
|
||||
const pathname = usePathname();
|
||||
const params = useParamsWithFallback();
|
||||
|
||||
const router = useRouter();
|
||||
const [user] = trpc.viewer.me.useSuspenseQuery();
|
||||
const { t } = useLocale();
|
||||
const result = stepRouteSchema.safeParse(params);
|
||||
|
||||
const result = stepRouteSchema.safeParse({
|
||||
...params,
|
||||
step: Array.isArray(params.step) ? params.step : [params.step],
|
||||
});
|
||||
|
||||
const currentStep = result.success ? result.data.step[0] : INITIAL_STEP;
|
||||
const from = result.success ? result.data.from : "";
|
||||
|
||||
const headers = [
|
||||
{
|
||||
title: `${t("welcome_to_cal_header", { appName: APP_NAME })}`,
|
||||
|
@ -218,7 +225,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
|||
return { redirect: { permanent: false, destination: "/event-types" } };
|
||||
}
|
||||
const locale = await getLocale(context.req);
|
||||
|
||||
return {
|
||||
props: {
|
||||
...(await serverSideTranslations(locale, ["common"])),
|
||||
|
|
|
@ -21,6 +21,7 @@ export const getFormSchema = (t: TFunction) => {
|
|||
teamId: z.number().or(getStringAsNumberRequiredSchema(t)),
|
||||
targetOrgId: z.number().or(getStringAsNumberRequiredSchema(t)),
|
||||
moveMembers: z.boolean(),
|
||||
teamSlugInOrganization: z.string(),
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -103,6 +104,12 @@ export default function MoveTeamToOrg() {
|
|||
required
|
||||
placeholder="Enter teamId to move to org"
|
||||
/>
|
||||
<TextField
|
||||
{...register("teamSlugInOrganization")}
|
||||
label="New Slug"
|
||||
required
|
||||
placeholder="Team slug in the Organization"
|
||||
/>
|
||||
<TextField
|
||||
{...register("targetOrgId")}
|
||||
label="Target Organization ID"
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
import { useIntercom } from "@calcom/features/ee/support/lib/intercom/useIntercom";
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import Head from "next/head";
|
||||
|
||||
import AddNewTeamMembers from "@calcom/features/ee/teams/components/AddNewTeamMembers";
|
||||
|
@ -19,12 +21,13 @@ const OnboardTeamMembersPage = () => {
|
|||
);
|
||||
};
|
||||
|
||||
OnboardTeamMembersPage.getLayout = (page: React.ReactElement) => (
|
||||
export const GetLayout = (page: React.ReactElement) => (
|
||||
<WizardLayout currentStep={2} maxSteps={2}>
|
||||
{page}
|
||||
</WizardLayout>
|
||||
);
|
||||
|
||||
OnboardTeamMembersPage.getLayout = GetLayout;
|
||||
OnboardTeamMembersPage.PageWrapper = PageWrapper;
|
||||
|
||||
export default OnboardTeamMembersPage;
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import Head from "next/head";
|
||||
|
||||
import { CreateANewTeamForm } from "@calcom/features/ee/teams/components";
|
||||
|
@ -18,7 +20,7 @@ const CreateNewTeamPage = () => {
|
|||
</>
|
||||
);
|
||||
};
|
||||
const LayoutWrapper = (page: React.ReactElement) => {
|
||||
export const LayoutWrapper = (page: React.ReactElement) => {
|
||||
return (
|
||||
<WizardLayout currentStep={1} maxSteps={2}>
|
||||
{page}
|
||||
|
|
|
@ -333,11 +333,14 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
|
|||
});
|
||||
}
|
||||
|
||||
const videoReferences = bookingObj.references.filter((reference) => reference.type.includes("_video"));
|
||||
const latestVideoReference = videoReferences[videoReferences.length - 1];
|
||||
|
||||
return {
|
||||
props: {
|
||||
meetingUrl: bookingObj.references[0].meetingUrl ?? "",
|
||||
...(typeof bookingObj.references[0].meetingPassword === "string" && {
|
||||
meetingPassword: bookingObj.references[0].meetingPassword,
|
||||
meetingUrl: latestVideoReference.meetingUrl ?? "",
|
||||
...(typeof latestVideoReference.meetingPassword === "string" && {
|
||||
meetingPassword: latestVideoReference.meetingPassword,
|
||||
}),
|
||||
booking: {
|
||||
...bookingObj,
|
||||
|
|
|
@ -166,4 +166,31 @@ test.describe("apps/ A/B tests", () => {
|
|||
|
||||
await expect(locator).toHaveClass(/bg-emphasis/);
|
||||
});
|
||||
|
||||
test("should point to the /future/getting-started", async ({ page, users, context }) => {
|
||||
await context.addCookies([
|
||||
{
|
||||
name: "x-calcom-future-routes-override",
|
||||
value: "1",
|
||||
url: "http://localhost:3000",
|
||||
},
|
||||
]);
|
||||
const user = await users.create({ completedOnboarding: false, name: null });
|
||||
|
||||
await user.apiLogin();
|
||||
|
||||
await page.goto("/getting-started/connected-calendar");
|
||||
|
||||
await page.waitForLoadState();
|
||||
|
||||
const dataNextJsRouter = await page.evaluate(() =>
|
||||
window.document.documentElement.getAttribute("data-nextjs-router")
|
||||
);
|
||||
|
||||
expect(dataNextJsRouter).toEqual("app");
|
||||
|
||||
const locator = page.getByText("Apple Calendar");
|
||||
|
||||
await expect(locator).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -214,7 +214,7 @@ test.describe("Booking limits", () => {
|
|||
await page.goto(slotUrl);
|
||||
await bookTimeSlot(page);
|
||||
|
||||
await expect(page.getByTestId("booking-fail")).toBeVisible({ timeout: 1000 });
|
||||
await expect(page.getByTestId("booking-fail")).toBeVisible({ timeout: 5000 });
|
||||
});
|
||||
|
||||
await test.step(`month after booking`, async () => {
|
||||
|
@ -224,7 +224,9 @@ test.describe("Booking limits", () => {
|
|||
await expect(page.getByTestId("day").nth(0)).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
// the month after we made bookings should have availability unless we hit a yearly limit
|
||||
await expect((await availableDays.count()) === 0).toBe(limitUnit === "year");
|
||||
// TODO: Temporary fix for failing test. It passes locally but fails on CI.
|
||||
// See #13097
|
||||
// await expect((await availableDays.count()) === 0).toBe(limitUnit === "year");
|
||||
});
|
||||
|
||||
// increment date by unit after hitting each limit
|
||||
|
|
|
@ -751,7 +751,7 @@ export async function makePaymentUsingStripe(page: Page) {
|
|||
const stripeFrame = stripeElement.frameLocator("iframe").first();
|
||||
await stripeFrame.locator('[name="number"]').fill("4242 4242 4242 4242");
|
||||
const now = new Date();
|
||||
await stripeFrame.locator('[name="expiry"]').fill(`${now.getMonth()} / ${now.getFullYear() + 1}`);
|
||||
await stripeFrame.locator('[name="expiry"]').fill(`${now.getMonth() + 1} / ${now.getFullYear() + 1}`);
|
||||
await stripeFrame.locator('[name="cvc"]').fill("111");
|
||||
const postcalCodeIsVisible = await stripeFrame.locator('[name="postalCode"]').isVisible();
|
||||
if (postcalCodeIsVisible) {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import { expect } from "@playwright/test";
|
||||
|
||||
import { test } from "./lib/fixtures";
|
||||
|
||||
test.describe.configure({ mode: "parallel" });
|
||||
|
@ -19,6 +17,6 @@ test.describe("Teams", () => {
|
|||
await page.goto("/settings/my-account/profile");
|
||||
|
||||
// check if user avatar is loaded
|
||||
await expect(page.locator('[data-testid="organization-avatar"]')).toBeVisible();
|
||||
await page.getByTestId("profile-upload-avatar").isVisible();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -56,6 +56,16 @@
|
|||
"a_refund_failed": "ההחזר הכספי נכשל",
|
||||
"awaiting_payment_subject": "ממתין לתשלום: {{title}} ב- {{date}}",
|
||||
"meeting_awaiting_payment": "התשלום על הפגישה שלך טרם בוצע",
|
||||
"dark_theme_contrast_error": "צבע ערכת עיצוב כהה ללא עובר בדיקת ניגודיות. אנו ממליצים לשנות את הצבע הזה כדי שהכפתורים שלך יהיו יותר בולטים.",
|
||||
"light_theme_contrast_error": "צבע ערכת עיצוב בהירה ללא עובר בדיקת ניגודיות. אנו ממליצים לשנות את הצבע הזה כדי שהכפתורים שלך יהיו יותר בולטים.",
|
||||
"payment_not_created_error": "אי אפשר ליצור תשלום",
|
||||
"couldnt_charge_card_error": "לא ניתן לחייב את התשלום בכרטיס",
|
||||
"no_available_users_found_error": "לא נמצאו משתמשים זמינים. אפשר לנסות ליצור חלון זמן נוסף?",
|
||||
"request_body_end_time_internal_error": "שגיאה פנימית. גוף הבקשה לא מכיל זמן סיום",
|
||||
"create_calendar_event_error": "לא ניתן ליצור אירוע לוח שנה בלוח השנה של הגוף המארגן",
|
||||
"update_calendar_event_error": "לא ניתן לעדכן אירוע לוח שנה.",
|
||||
"delete_calendar_event_error": "לא ניתן למחוק אירוע לוח שנה.",
|
||||
"already_signed_up_for_this_booking_error": "כבר נרשמת להזמנה הזאת.",
|
||||
"help": "עזרה",
|
||||
"price": "מחיר",
|
||||
"paid": "שולם",
|
||||
|
@ -67,6 +77,7 @@
|
|||
"cannot_repackage_codebase": "לא ניתן לארוז מחדש או למכור את בסיס הקוד",
|
||||
"acquire_license": "כדי להסיר את התנאים האלה, ניתן לקנות רישיון מסחרי על ידי שליחת דוא\"ל",
|
||||
"terms_summary": "סיכום התנאים",
|
||||
"signing_up_terms": "הרשמה מהווה את הסכמתך ל<2>תנאים</2> ול<3>מדיניות הפרטיות</3> שלנו.",
|
||||
"open_env": "יש לפתוח את .env ולאשר את הרישיון שלנו",
|
||||
"env_changed": "שיניתי את ה-.env שלי",
|
||||
"accept_license": "אישור הרישיון",
|
||||
|
@ -101,6 +112,7 @@
|
|||
"requested_to_reschedule_subject_attendee": "הפעולה חייבה קביעת מועד חדש: יש לקבוע מועד חדש עבור {{eventType}} עם {{name}}",
|
||||
"hi_user_name": "שלום {{name}}",
|
||||
"ics_event_title": "{{eventType}} עם {{name}}",
|
||||
"please_book_a_time_sometime_later": "אף אחד לא זמין כרגע. נא לקבוע זימון למועד אחר",
|
||||
"new_event_subject": "אירוע חדש: {{attendeeName}} - {{date}} - {{eventType}}",
|
||||
"join_by_entrypoint": "ניתן להצטרף עד {{entryPoint}}",
|
||||
"notes": "הערות",
|
||||
|
@ -117,15 +129,20 @@
|
|||
"meeting_id": "מזהה הפגישה",
|
||||
"meeting_password": "סיסמת הפגישה",
|
||||
"meeting_url": "כתובת ה-URL של הפגישה",
|
||||
"meeting_url_not_found": "כתובת הפגישה לא נמצאה",
|
||||
"token_not_found": "האסימון לא נמצא",
|
||||
"some_other_host_already_accepted_the_meeting": "מארח אחר כבר קיבל את הפגישה. בכל זאת מעניין אותך להצטרף? <1>להמשיך לפגישה</1>",
|
||||
"meeting_request_rejected": "בקשת הפגישה שלך נדחתה",
|
||||
"rejected_event_type_with_organizer": "נדחה: {{eventType}} עם {{organizer}} בתאריך {{date}}",
|
||||
"hi": "שלום",
|
||||
"join_team": "להצטרף לצוות",
|
||||
"manage_this_team": "לנהל את הצוות הנוכחי",
|
||||
"team_info": "מידע על הצוות",
|
||||
"join_meeting": "הצטרפות לפגישה",
|
||||
"request_another_invitation_email": "אם אתה מעדיף לא להשתמש בכתובת {{toEmail}} ככתובת הדוא\"ל שלך עבור {{appName}} או שכבר יש לך חשבון {{appName}}, יש לבקש לקבל הזמנה נוספת לכתובת הדוא\"ל הרצויה.",
|
||||
"you_have_been_invited": "הוזמנת להצטרף לצוות {{teamName}}",
|
||||
"user_invited_you": "{{user}} הזמין/ה אותך להצטרף ל{{entity}} {{team}} ב-{{appName}}",
|
||||
"user_invited_you_to_subteam": "הוזמנת על ידי {{user}} להצטרף לצוות {{team}} של הארגון {{parentTeamName}} אצל {{appName}}",
|
||||
"hidden_team_member_title": "אתה מוסתר בצוות זה",
|
||||
"hidden_team_member_message": "לא בוצע תשלום עבור המקום שלך. ניתן לשדרג ל-PRO או ליידע את הבעלים של הצוות שהוא או היא יכולים לשלם עבור המקום שלך.",
|
||||
"hidden_team_owner_message": "נדרש חשבון Pro כדי להשתמש בתכונות הצוותים. תהיה מוסתר עד שתבצע שידרוג.",
|
||||
|
@ -229,6 +246,7 @@
|
|||
"reset_your_password": "הגדר/י את הסיסמה החדשה שלך לפי ההוראות שנשלחו אל כתובת הדוא\"ל שלך.",
|
||||
"email_change": "התחבר/י שוב עם כתובת הדוא\"ל החדשה והסיסמה.",
|
||||
"create_your_account": "צור את החשבון שלך",
|
||||
"create_your_calcom_account": "יצירת החשבון שלך ב־Cal.com",
|
||||
"sign_up": "הרשמה",
|
||||
"youve_been_logged_out": "יצאת מהמערכת",
|
||||
"hope_to_see_you_soon": "מקווים לראותך שוב בקרוב!",
|
||||
|
@ -266,6 +284,9 @@
|
|||
"nearly_there_instructions": "דבר אחרון: תיאור קצר אודותיך/ייך בתוספת תמונה עוזרים מאוד להשיג הזמנות ומאפשרים לאנשים לדעת עם מי הם עומדים להיפגש.",
|
||||
"set_availability_instructions": "הגדר טווחי זמן שבהם אתה זמין באופן קבוע. ניתן יהיה ליצור טווחי זמן נוספים מאוחר יותר ולהקצות אותם ללוחות שנה אחרים.",
|
||||
"set_availability": "ציין את הזמינות שלך",
|
||||
"set_availbility_description": "הגדרת תזמונים למועדים שמתאים לך לקבוע בהם זימונים.",
|
||||
"share_a_link_or_embed": "שיתוף קישור או הטמעה",
|
||||
"share_a_link_or_embed_description": "שיתוף הקישור שלך אל {{appName}} באתר שלך.",
|
||||
"availability_settings": "הגדרות זמינוּת",
|
||||
"continue_without_calendar": "להמשיך בלי לוח שנה",
|
||||
"continue_with": "להמשיך עם {{appName}}",
|
||||
|
@ -419,6 +440,7 @@
|
|||
"browse_api_documentation": "עיון במסמכי ממשק תכנות היישומים (API) שלנו",
|
||||
"leverage_our_api": "מומלץ להיעזר בממשק תכנות היישומים (API) שלנו לקבלת שליטה מלאה ויכולת התאמה אישית.",
|
||||
"create_webhook": "יצירת Webhook",
|
||||
"instant_meeting_created": "נוצרה פגישה מיידית",
|
||||
"booking_cancelled": "ההזמנה בוטלה",
|
||||
"booking_rescheduled": "מועד ההזמנה השתנה",
|
||||
"recording_ready": "הקישור להורדת ההקלטה מוכן",
|
||||
|
@ -606,6 +628,7 @@
|
|||
"hide_book_a_team_member_description": "הסתר/י את הלחצן לשריון זמן של חבר/ת צוות מהדפים הציבוריים שלך.",
|
||||
"danger_zone": "אזור מסוכן",
|
||||
"account_deletion_cannot_be_undone": "יש לנקוט זהירות. מחיקת חשבון היא פעולה בלתי הפיכה.",
|
||||
"team_deletion_cannot_be_undone": "יש לנקוט במשנה זהירות. מחיקת צוות היא פעולה בלתי הפיכה",
|
||||
"back": "הקודם",
|
||||
"cancel": "ביטול",
|
||||
"cancel_all_remaining": "לבטל את כל הנותרים",
|
||||
|
@ -656,6 +679,7 @@
|
|||
"default_duration": "משך הזמן המוגדר כברירת מחדל",
|
||||
"default_duration_no_options": "ראשית, אנא בחר משך זמינות",
|
||||
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
|
||||
"multiple_duration_timeUnit": "{{count}} $t({{unit}}_timeUnit)",
|
||||
"minutes": "דקות",
|
||||
"round_robin": "לפי תורות",
|
||||
"round_robin_description": "פגישות מחזוריות בין חברי צוות מרובים.",
|
||||
|
@ -668,6 +692,7 @@
|
|||
"add_members": "הוספת חברים...",
|
||||
"no_assigned_members": "לא הוקצה אף חבר",
|
||||
"assigned_to": "הוקצה ל",
|
||||
"you_must_be_logged_in_to": "חובה להיכנס אל {{url}}",
|
||||
"start_assigning_members_above": "התחל/י להקצות חברים למעלה",
|
||||
"locked_fields_admin_description": "חברים לא יוכלו לערוך את זה",
|
||||
"locked_fields_member_description": "מנהל הצוות נעל את האפשרות הזו",
|
||||
|
@ -780,6 +805,9 @@
|
|||
"requires_confirmation_description": "יש לאשר את ההזמנה באופן ידני כדי שניתן יהיה להעביר אותה אל השילובים ולשלוח הודעת אישור בדוא״ל.",
|
||||
"recurring_event": "אירוע חוזר",
|
||||
"recurring_event_description": "אנשים יכולים להירשם לאירועים חוזרים",
|
||||
"cannot_be_used_with_paid_event_types": "אי אפשר להשתמש בזה עם סוגי פגישות בתשלום",
|
||||
"warning_payment_instant_meeting_event": "אין תמיכה בפגישות מיידיות עם אירועים מחזוריים ויישומוני תשלום",
|
||||
"warning_instant_meeting_experimental": "ניסיוני: פגישות מיידיות הן ניסיוניות כרגע.",
|
||||
"starting": "מועד התחלה",
|
||||
"disable_guests": "השבתת אורחים",
|
||||
"disable_guests_description": "השבת את האפשרות להוסיף אורחים נוספים בעת ביצוע הזמנה.",
|
||||
|
@ -847,6 +875,7 @@
|
|||
"next_step": "לדלג על שלב זה",
|
||||
"prev_step": "לשלב הקודם",
|
||||
"install": "התקנה",
|
||||
"start_paid_trial": "התחלת ניסיון בחינם",
|
||||
"installed": "מותקן",
|
||||
"active_install_one": "התקנה פעילה {{count}}",
|
||||
"active_install_other": "{{count}} התקנות פעילות",
|
||||
|
@ -1032,6 +1061,7 @@
|
|||
"user_impersonation_heading": "התחזות למשתמשים",
|
||||
"user_impersonation_description": "מצב זה מאפשר לצוות התמיכה שלנו להתחבר באופן זמני לחשבונך כדי שנוכל לפתור במהירות את הבעיות שתדווח/י לנו עליהם.",
|
||||
"team_impersonation_description": "מאפשר לבעלים של הצוות/מנהלים להיכנס זמנית בשמך.",
|
||||
"cal_signup_description": "בחינם למשתמשים פרטיים. התוכנית לצוותים מאפשרת יכולות לשיתופי פעולה.",
|
||||
"make_team_private": "להגדיר את הצוות כפרטי",
|
||||
"make_team_private_description": "כשההגדרה הזו מופעלת, חברי הצוות שלך לא יוכלו לראות חברי צוות אחרים.",
|
||||
"you_cannot_see_team_members": "אין לך אפשרות לראות את כל חברי הצוות של צוות פרטי.",
|
||||
|
@ -1091,6 +1121,7 @@
|
|||
"developer_documentation": "מסמכי מפתחים",
|
||||
"get_in_touch": "יצירת קשר",
|
||||
"contact_support": "פנייה לתמיכה",
|
||||
"premium_support": "תמיכת פרימיום",
|
||||
"community_support": "תמיכת קהילה",
|
||||
"feedback": "משוב",
|
||||
"submitted_feedback": "תודה על המשוב!",
|
||||
|
@ -1297,6 +1328,7 @@
|
|||
"customize_your_brand_colors": "בצע התאמה אישית של דף ההזמנות שלך עם צבעי מותג משלך.",
|
||||
"pro": "Pro",
|
||||
"removes_cal_branding": "הסרת מיתוגים הקשורים ל-{{appName}}, כגון 'מופעל על ידי {{appName}}'",
|
||||
"instant_meeting_with_title": "פגישה מיידית עם {{name}}",
|
||||
"profile_picture": "תמונת פרופיל",
|
||||
"upload": "העלאה",
|
||||
"add_profile_photo": "הוספת תמונת פרופיל",
|
||||
|
@ -1352,6 +1384,7 @@
|
|||
"event_name_info": "שם סוג האירוע",
|
||||
"event_date_info": "תאריך האירוע",
|
||||
"event_time_info": "שעת ההתחלה של האירוע",
|
||||
"event_type_not_found": "EventType לא נמצא",
|
||||
"location_info": "מיקום האירוע",
|
||||
"additional_notes_info": "הערות נוספות להזמנה",
|
||||
"attendee_name_info": "שם האדם שביצע את ההזמנה",
|
||||
|
@ -1392,6 +1425,7 @@
|
|||
"slot_length": "אורך חלון הזמן",
|
||||
"booking_appearance": "מראה ההזמנה",
|
||||
"appearance_team_description": "ניהול ההגדרות של מראה הזמנות הצוות שלך",
|
||||
"appearance_org_description": "ניהול ההגדרות למראה ההזמנות של הארגון שלך",
|
||||
"only_owner_change": "רק הבעלים של הצוות יכולים לבצע שינויים בהזמנת הצוות ",
|
||||
"team_disable_cal_branding_description": "הסרת מיתוגים הקשורים ל-{{appName}}, כגון 'מופעל על ידי {{appName}}'",
|
||||
"invited_by_team": "הוזמנת על ידי {{teamName}} להצטרף לצוות בתפקיד {{role}}",
|
||||
|
@ -1456,6 +1490,8 @@
|
|||
"report_app": "דיווח על האפליקציה",
|
||||
"limit_booking_frequency": "הגבלת תדירות ההזמנות",
|
||||
"limit_booking_frequency_description": "הגבלת מספר הפעמים שבהן ניתן להזמין את האירוע הזה",
|
||||
"limit_booking_only_first_slot": "להגביל את ההזמנה לחלון הפנוי הראשון בלבד",
|
||||
"limit_booking_only_first_slot_description": "לאפשר להזמין רק את החלון הפנוי הראשון בכל יום",
|
||||
"limit_total_booking_duration": "הגבל משך תזמון כולל",
|
||||
"limit_total_booking_duration_description": "הגבלת משך הזמן הכולל שבו ניתן להזמין את האירוע הזה",
|
||||
"add_limit": "הוספת הגבלה",
|
||||
|
@ -1523,10 +1559,14 @@
|
|||
"your_org_disbanded_successfully": "פירוק הארגון שלך בוצע בהצלחה",
|
||||
"error_creating_team": "אירעה שגיאה במהלך יצירת הצוות",
|
||||
"you": "את/ה",
|
||||
"or_continue_with": "או להמשיך עם",
|
||||
"resend_email": "לשלוח שוב את הדוא״ל",
|
||||
"member_already_invited": "החבר כבר הוזמן",
|
||||
"already_in_use_error": "שם המשתמש כבר קיים",
|
||||
"enter_email_or_username": "יש להזין כתובת דוא\"ל או שם משתמש",
|
||||
"enter_email": "נא למלא כתובת דוא״ל",
|
||||
"enter_emails": "נא למלא כתובות דוא״ל",
|
||||
"too_many_invites": "חלה מגבלה על הזמנת עד {{nbUsers}} משתמשים בבת אחת.",
|
||||
"team_name_taken": "השם הזה כבר תפוס",
|
||||
"must_enter_team_name": "יש להזין שם צוות",
|
||||
"team_url_required": "יש להזין כתובת URL של הצוות",
|
||||
|
@ -1606,6 +1646,7 @@
|
|||
"individual": "משתמש בודד",
|
||||
"all_bookings_filter_label": "כל ההזמנות",
|
||||
"all_users_filter_label": "כל המשתמשים",
|
||||
"all_event_types_filter_label": "כל סוגי האירועים",
|
||||
"your_bookings_filter_label": "התזמונים שלך",
|
||||
"meeting_url_variable": "כתובת ה-URL של הפגישה",
|
||||
"meeting_url_info": "כתובת ה-URL של שיחת הוועידה באירוע",
|
||||
|
@ -1702,6 +1743,7 @@
|
|||
"organizer_timezone": "מארגן אזורי זמן",
|
||||
"email_user_cta": "צפה בהזמנה",
|
||||
"email_no_user_invite_heading_team": "הוזמנת להצטרף לצוות ב-{{appName}}",
|
||||
"email_no_user_invite_heading_subteam": "הוזמנת להצטרף לצוות בארגון {{parentTeamName}}",
|
||||
"email_no_user_invite_heading_org": "הוזמנת להצטרף לארגון ב-{{appName}}",
|
||||
"email_no_user_invite_subheading": "{{invitedBy}} הזמין אותך להצטרף לצוות שלו ב- {{appName}}. {{appName}} הינה מתזמן זימונים שמאפשר לך ולצוות שלך לזמן פגישות בלי כל הפינג פונג במיילים.",
|
||||
"email_user_invite_subheading_team": "{{invitedBy}} הזמין/ה אותך להצטרף לצוות שלו/ה בשם '{{teamName}}' באפליקציה {{appName}}. אפליקציית {{appName}} היא כלי לקביעת מועדים לאירועים שמאפשר לך ולצוות שלך לתזמן פגישות בלי כל הפינג פונג במיילים.",
|
||||
|
@ -1836,7 +1878,9 @@
|
|||
"review_event_type": "בדיקת סוג האירוע",
|
||||
"looking_for_more_analytics": "מחפש עוד מידע אנליטי?",
|
||||
"looking_for_more_insights": "רוצה עוד Insights?",
|
||||
"filters": "מסננים",
|
||||
"add_filter": "הוסף סנן",
|
||||
"remove_filters": "ניקוי כל המסננים",
|
||||
"select_user": "בחר משתמש",
|
||||
"select_event_type": "בחר סוג ארוע",
|
||||
"select_date_range": "בחר טווח תאריכים",
|
||||
|
@ -1991,6 +2035,8 @@
|
|||
"add_times_to_your_email": "בחר/י כמה מועדים פנויים והטבע/י אותם בדוא\"ל",
|
||||
"select_time": "בחירת שעה",
|
||||
"select_date": "בחירת תאריך",
|
||||
"connecting_you_to_someone": "אנו מחברים אותך למישהו או מישהי.",
|
||||
"please_do_not_close_this_tab": "נא לא לסגור את הלשונית הזאת",
|
||||
"see_all_available_times": "לצפייה בכל המועדים הפנויים",
|
||||
"org_team_names_example_1": "לדוגמה, מחלקת שיווק",
|
||||
"org_team_names_example_2": "לדוגמה, מחלקת מכירות",
|
||||
|
@ -2008,8 +2054,12 @@
|
|||
"description_requires_booker_email_verification": "כדי להבטיח אימות של כתובת הדוא\"ל של המזמין לפני תזמון אירועים",
|
||||
"requires_confirmation_mandatory": "ניתן לשלוח הודעות טקסט למשתתפים רק כאשר סוג האירוע מחייב אישור.",
|
||||
"organizations": "ארגונים",
|
||||
"upload_cal_video_logo": "העלאת סרטון לוגו ל־Cal",
|
||||
"update_cal_video_logo": "עדכון סרטון לוגו ל־Cal",
|
||||
"cal_video_logo_upload_instruction": "כדי לוודא שהלוגו שלך גלוי כנגד הרקע הכהה של הסרטון של Cal, נא להעלות תמונה בצבעים בהירים מהסוגים PNG או SVG כדי שהחלקים המתאימים יישארו שקופים.",
|
||||
"org_admin_other_teams": "צוותים אחרים",
|
||||
"org_admin_other_teams_description": "כאן תוכל/י לראות צוותים בארגון שאינך שייך/ת אליהם. יש לך אפשרות להוסיף את עצמך, במקרה הצורך.",
|
||||
"not_part_of_org": "אינך חלק משום ארגון",
|
||||
"no_other_teams_found": "לא נמצא אף צוות אחר",
|
||||
"no_other_teams_found_description": "אין צוותים אחרים בארגון הזה.",
|
||||
"attendee_first_name_variable": "השם הפרטי של המשתתף",
|
||||
|
@ -2045,7 +2095,9 @@
|
|||
"org_error_processing": "היתה שגיאה בעיבוד של ארגון זה",
|
||||
"orgs_page_description": "רשימה של כל הארגונים. קבלת ארגון תאפשר לכל המשתמשים מאותו דומיין דוא\"ל להירשם בלי להצטרך לבצע אימות של כתובת הדוא\"ל.",
|
||||
"unverified": "לא אומת",
|
||||
"verified": "מאומת",
|
||||
"dns_missing": "DNS חסר",
|
||||
"dns_configured": "DNS מוגדר",
|
||||
"mark_dns_configured": "סימון כי DNS הוגדר",
|
||||
"value": "ערך",
|
||||
"your_organization_updated_sucessfully": "עדכון הארגון שלך בוצע בהצלחה",
|
||||
|
@ -2055,7 +2107,9 @@
|
|||
"oAuth": "OAuth",
|
||||
"recently_added": "נוספו לאחרונה",
|
||||
"connect_all_calendars": "חבר את כל לוחות השנה שלך",
|
||||
"connect_all_calendars_description": "{{appName}} קורא את הזמינות מכל לוחות השנה הקיימים שלך.",
|
||||
"workflow_automation": "אוטומצית תהליך עבודה",
|
||||
"workflow_automation_description": "אפשר לכוון את חוויית התזמון שלך עם תהליכי עבודה",
|
||||
"scheduling_for_your_team": "אוטומצית תהליך עבודה",
|
||||
"no_members_found": "לא נמצא אף חבר",
|
||||
"event_setup_length_error": "הגדרת אירוע: משך הזמן חייב להיות לפחות דקה אחת.",
|
||||
|
@ -2089,9 +2143,39 @@
|
|||
"overlay_my_calendar": "הצג את לוח השנה שלי בשכבת-על",
|
||||
"overlay_my_calendar_toc": "על ידי חיבור אל לוח השנה שלך, את/ה מקבל/ת את מדיניות הפרטיות ואת תנאי השימוש שלנו. אפשר לשלול את הגישה בכל שלב.",
|
||||
"view_overlay_calendar_events": "ראה/י את האירועים שלך בלוח השנה כדי למנוע התנגשות בהזמנות.",
|
||||
"join_event_location": "הצטרפות אל {{eventLocationType}}",
|
||||
"troubleshooting": "פתרון בעיות",
|
||||
"calendars_were_checking_for_conflicts": "לוחות השנה לא בודקים סתירות",
|
||||
"manage_calendars": "ניהול לוחות שנה",
|
||||
"lock_timezone_toggle_on_booking_page": "נעילת אזור הזמן בדף ההזמנות",
|
||||
"description_lock_timezone_toggle_on_booking_page": "כדי לנעול את אזור הזמן בדף ההזמנות – שימושי לאירועים אישיים.",
|
||||
"install_calendar": "התקנת לוח שנה",
|
||||
"branded_subdomain": "תת־תחום ממותג",
|
||||
"branded_subdomain_description": "קבלת תת־תחום ממותג משלך, כגון acme.cal.com",
|
||||
"org_insights": "תובנות כלל־ארגוניות",
|
||||
"extensive_whitelabeling": "תהליך הטמעה והנדסת תמיכה אישי",
|
||||
"unlimited_teams": "כמות בלתי מוגבלת של צוותים",
|
||||
"unlimited_teams_description": "אפשר להוסיף כמה תת־צוותים שדרושים לארגון שלך",
|
||||
"unified_billing": "חיוב מאוחד",
|
||||
"unified_billing_description": "ניתן להוסיף כרטיס אשראי אחד כדי לשלם על כל המינויים של הצוות שלך",
|
||||
"advanced_managed_events": "סוגי אירועים מנוהלים מתקדמים",
|
||||
"advanced_managed_events_description": "אפשר להוסיף כרטיס אשראי יחיד כדי לשלם עבור כל המינויים של הצוות שלך",
|
||||
"enterprise_description": "יש לשדרג לרישיון תאגידי כדי ליצור את הארגון שלך",
|
||||
"create_your_org": "יצירת הארגון שלך",
|
||||
"create_your_org_description": "אפשר לשדרג לרישיון תאגידי ולקבל תת־תחום, חיוב מאוחד, תובנות, שינוי מיתוג נרחב ועוד",
|
||||
"other_payment_app_enabled": "אפשר להפעיל רק יישומון תשלום אחד לכל סוג אירוע",
|
||||
"admin_delete_organization_title": "למחוק את הארגון?",
|
||||
"published": "מפורסם",
|
||||
"unpublished": "לא מפורסם",
|
||||
"publish": "פרסום",
|
||||
"org_publish_error": "אי אפשר לפרסם את הארגון",
|
||||
"need_help": "צריך עזרה?",
|
||||
"troubleshooter": "פותר בעיות",
|
||||
"please_install_a_calendar": "נא להתקין לוח שנה",
|
||||
"instant_tab_title": "הזמנה מיידית",
|
||||
"instant_event_tab_description": "לאפשר לאנשים ליצור הזמנות מיידית",
|
||||
"uprade_to_create_instant_bookings": "ניתן לשדג לרישיון התאגידי ולאפשר למשתמשים להצטרף לשיחה מיידית שמשתתפים יכולים לקפוץ ישירות אליה. זה מיועד רק לסוגי אירועים של צוותים",
|
||||
"dont_want_to_wait": "לא רוצה להמתין?",
|
||||
"meeting_started": "הפגישה החלה",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
|
|
@ -143,7 +143,8 @@ html.todesktop header {
|
|||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
html.todesktop header button {
|
||||
html.todesktop header button,
|
||||
html.todesktop header a {
|
||||
-webkit-app-region: no-drag;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import type { TestFunction } from "vitest";
|
||||
|
||||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { test } from "@calcom/web/test/fixtures/fixtures";
|
||||
import type { Fixtures } from "@calcom/web/test/fixtures/fixtures";
|
||||
import { createOrganization } from "@calcom/web/test/utils/bookingScenario/bookingScenario";
|
||||
|
||||
const WEBSITE_PROTOCOL = new URL(WEBSITE_URL).protocol;
|
||||
const _testWithAndWithoutOrg = (
|
||||
description: Parameters<typeof testWithAndWithoutOrg>[0],
|
||||
fn: Parameters<typeof testWithAndWithoutOrg>[1],
|
||||
|
@ -28,7 +30,7 @@ const _testWithAndWithoutOrg = (
|
|||
skip,
|
||||
org: {
|
||||
organization: org,
|
||||
urlOrigin: `http://${org.slug}.cal.local:3000`,
|
||||
urlOrigin: `${WEBSITE_PROTOCOL}//${org.slug}.cal.local:3000`,
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
|
@ -94,7 +94,6 @@ export default function AppCard({
|
|||
{app?.isInstalled || app.credentialOwner ? (
|
||||
<div className="ml-auto flex items-center">
|
||||
<Switch
|
||||
data-testid="app-switch"
|
||||
disabled={!app.enabled || managedDisabled || disableSwitch}
|
||||
onCheckedChange={(enabled) => {
|
||||
if (switchOnClick) {
|
||||
|
@ -104,7 +103,7 @@ export default function AppCard({
|
|||
}}
|
||||
checked={switchChecked}
|
||||
LockedIcon={LockedIcon}
|
||||
data-testId={`${app.slug}-app-switch`}
|
||||
data-testid={`${app.slug}-app-switch`}
|
||||
tooltip={switchTooltip}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -553,11 +553,18 @@ export default function RouteBuilder({
|
|||
<SingleForm
|
||||
form={form}
|
||||
appUrl={appUrl}
|
||||
Page={({ hookForm, form }) => (
|
||||
<div className="route-config">
|
||||
<Routes hookForm={hookForm} appUrl={appUrl} form={form} />
|
||||
</div>
|
||||
)}
|
||||
Page={({ hookForm, form }) => {
|
||||
// If hookForm hasn't been initialized, don't render anything
|
||||
// This is important here because some states get initialized which aren't reset when the hookForm is reset with the form values and they don't get the updated values
|
||||
if (!hookForm.getValues().id) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<div className="route-config">
|
||||
<Routes hookForm={hookForm} appUrl={appUrl} form={form} />
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -439,6 +439,10 @@ class CalApi {
|
|||
elementOrSelector: string | HTMLElement;
|
||||
config?: PrefillAndIframeAttrsConfig;
|
||||
}) {
|
||||
if (this.cal.inlineEl) {
|
||||
console.warn("Inline embed already exists. Ignoring this call");
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
validate(arguments[0], {
|
||||
required: true,
|
||||
|
|
|
@ -33,7 +33,9 @@ import { useBrandColors } from "./utils/use-brand-colors";
|
|||
|
||||
const loadFramerFeatures = () => import("./framer-features").then((res) => res.default);
|
||||
const PoweredBy = dynamic(() => import("@calcom/ee/components/PoweredBy"));
|
||||
const UnpublishedEntity = dynamic(() => import("@calcom/ui").then((mod) => mod.UnpublishedEntity));
|
||||
const UnpublishedEntity = dynamic(() =>
|
||||
import("@calcom/ui/components/unpublished-entity/UnpublishedEntity").then((mod) => mod.UnpublishedEntity)
|
||||
);
|
||||
const DatePicker = dynamic(() => import("./components/DatePicker").then((mod) => mod.DatePicker), {
|
||||
ssr: false,
|
||||
});
|
||||
|
@ -186,12 +188,6 @@ const BookerComponent = ({
|
|||
return setBookerState("booking");
|
||||
}, [event, selectedDate, selectedTimeslot, setBookerState]);
|
||||
|
||||
useEffect(() => {
|
||||
if (layout === "mobile") {
|
||||
timeslotsRef.current?.scrollIntoView({ behavior: "smooth" });
|
||||
}
|
||||
}, [layout]);
|
||||
|
||||
const hideEventTypeDetails = isEmbed ? embedUiConfig.hideEventTypeDetails : false;
|
||||
|
||||
if (entity.isUnpublished) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useRef, useEffect } from "react";
|
||||
import { useRef } from "react";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
|
||||
|
@ -93,13 +93,6 @@ export const AvailableTimeSlots = ({
|
|||
|
||||
const slotsPerDay = useSlotsForAvailableDates(dates, schedule?.data?.slots);
|
||||
|
||||
useEffect(() => {
|
||||
if (isEmbed) return;
|
||||
if (containerRef.current && !schedule.isLoading && isMobile) {
|
||||
containerRef.current.scrollIntoView({ behavior: "smooth", block: "center" });
|
||||
}
|
||||
}, [containerRef, schedule.isLoading, isEmbed, isMobile]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex">
|
||||
|
|
|
@ -200,7 +200,7 @@ export const BookEventFormChild = ({
|
|||
const { uid, paymentUid } = responseData;
|
||||
const fullName = getFullName(bookingForm.getValues("responses.name"));
|
||||
if (paymentUid) {
|
||||
return router.push(
|
||||
router.push(
|
||||
createPaymentLink({
|
||||
paymentUid,
|
||||
date: timeslot,
|
||||
|
@ -209,6 +209,7 @@ export const BookEventFormChild = ({
|
|||
absolute: false,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!uid) {
|
||||
|
@ -225,7 +226,7 @@ export const BookEventFormChild = ({
|
|||
isRescheduling && bookingData?.startTime ? dayjs(bookingData.startTime).toString() : undefined,
|
||||
};
|
||||
|
||||
return bookingSuccessRedirect({
|
||||
bookingSuccessRedirect({
|
||||
successRedirectUrl: eventType?.successRedirectUrl || "",
|
||||
query,
|
||||
booking: responseData,
|
||||
|
@ -272,7 +273,7 @@ export const BookEventFormChild = ({
|
|||
isRescheduling && bookingData?.startTime ? dayjs(bookingData.startTime).toString() : undefined,
|
||||
};
|
||||
|
||||
return bookingSuccessRedirect({
|
||||
bookingSuccessRedirect({
|
||||
successRedirectUrl: eventType?.successRedirectUrl || "",
|
||||
query,
|
||||
booking,
|
||||
|
@ -325,10 +326,6 @@ export const BookEventFormChild = ({
|
|||
);
|
||||
|
||||
const bookEvent = (values: BookingFormValues) => {
|
||||
// Clears form values stored in store, so old values won't stick around.
|
||||
setFormValues({});
|
||||
bookingForm.clearErrors();
|
||||
|
||||
// It shouldn't be possible that this method is fired without having eventQuery data,
|
||||
// but since in theory (looking at the types) it is possible, we still handle that case.
|
||||
if (!eventQuery?.data) {
|
||||
|
@ -375,6 +372,9 @@ export const BookEventFormChild = ({
|
|||
} else {
|
||||
createBookingMutation.mutate(mapBookingToMutationInput(bookingInput));
|
||||
}
|
||||
// Clears form values stored in store, so old values won't stick around.
|
||||
setFormValues({});
|
||||
bookingForm.clearErrors();
|
||||
};
|
||||
|
||||
if (!eventType) {
|
||||
|
@ -442,7 +442,14 @@ export const BookEventFormChild = ({
|
|||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
loading={createBookingMutation.isLoading || createRecurringBookingMutation.isLoading}
|
||||
loading={
|
||||
bookingForm.formState.isSubmitting ||
|
||||
createBookingMutation.isLoading ||
|
||||
createRecurringBookingMutation.isLoading ||
|
||||
// A redirect is triggered on mutation success, so keep the button disabled as this is happening.
|
||||
createBookingMutation.isSuccess ||
|
||||
createRecurringBookingMutation.isSuccess
|
||||
}
|
||||
data-testid={
|
||||
rescheduleUid && bookingData ? "confirm-reschedule-button" : "confirm-book-button"
|
||||
}>
|
||||
|
|
|
@ -15,9 +15,12 @@ import { useBookerStore } from "../store";
|
|||
import { FromToTime } from "../utils/dates";
|
||||
import { useEvent } from "../utils/event";
|
||||
|
||||
const TimezoneSelect = dynamic(() => import("@calcom/ui").then((mod) => mod.TimezoneSelect), {
|
||||
ssr: false,
|
||||
});
|
||||
const TimezoneSelect = dynamic(
|
||||
() => import("@calcom/ui/components/form/timezone-select/TimezoneSelect").then((mod) => mod.TimezoneSelect),
|
||||
{
|
||||
ssr: false,
|
||||
}
|
||||
);
|
||||
|
||||
export const EventMeta = () => {
|
||||
const { setTimezone, timeFormat, timezone } = useTimePreferences();
|
||||
|
|
|
@ -4,7 +4,16 @@ import useDigitInput from "react-digit-input";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader, Label, Input } from "@calcom/ui";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
Label,
|
||||
Input,
|
||||
} from "@calcom/ui";
|
||||
import { Info } from "@calcom/ui/components/icon";
|
||||
|
||||
export const VerifyCodeDialog = ({
|
||||
|
@ -45,6 +54,7 @@ export const VerifyCodeDialog = ({
|
|||
},
|
||||
onError: (err) => {
|
||||
setIsLoading(false);
|
||||
setHasVerified(false);
|
||||
if (err.message === "invalid_code") {
|
||||
setError(t("code_provided_invalid"));
|
||||
}
|
||||
|
@ -58,6 +68,7 @@ export const VerifyCodeDialog = ({
|
|||
},
|
||||
onError: (err) => {
|
||||
setIsLoading(false);
|
||||
setHasVerified(false);
|
||||
if (err.message === "invalid_code") {
|
||||
setError(t("code_provided_invalid"));
|
||||
}
|
||||
|
@ -138,6 +149,9 @@ export const VerifyCodeDialog = ({
|
|||
)}
|
||||
<DialogFooter>
|
||||
<DialogClose />
|
||||
<Button type="submit" onClick={verifyCode} loading={isLoading}>
|
||||
{t("submit")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import type { Prisma } from "@prisma/client";
|
||||
import type { IncomingMessage } from "http";
|
||||
|
||||
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
||||
import { IS_PRODUCTION, WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { ALLOWED_HOSTNAMES, RESERVED_SUBDOMAINS, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
|
@ -100,9 +100,10 @@ export function subdomainSuffix() {
|
|||
}
|
||||
|
||||
export function getOrgFullOrigin(slug: string, options: { protocol: boolean } = { protocol: true }) {
|
||||
if (!slug) return options.protocol ? WEBAPP_URL : WEBAPP_URL.replace("https://", "").replace("http://", "");
|
||||
if (!slug)
|
||||
return options.protocol ? WEBSITE_URL : WEBSITE_URL.replace("https://", "").replace("http://", "");
|
||||
const orgFullOrigin = `${
|
||||
options.protocol ? `${new URL(WEBAPP_URL).protocol}//` : ""
|
||||
options.protocol ? `${new URL(WEBSITE_URL).protocol}//` : ""
|
||||
}${slug}.${subdomainSuffix()}`;
|
||||
return orgFullOrigin;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useEffect } from "react";
|
||||
|
||||
|
|
|
@ -271,7 +271,7 @@ export default function MemberInvitationModal(props: MemberInvitationModalProps)
|
|||
required
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
const targetValues = e.target.value.split(",");
|
||||
const targetValues = e.target.value.split(/[\n,]/);
|
||||
const emails =
|
||||
targetValues.length === 1
|
||||
? targetValues[0].trim().toLocaleLowerCase()
|
||||
|
|
|
@ -107,9 +107,7 @@ export default function TeamListItem(props: Props) {
|
|||
<span className="text-default text-sm font-bold">{team.name}</span>
|
||||
<span className="text-muted block text-xs">
|
||||
{team.slug ? (
|
||||
`${getTeamUrlSync({ orgSlug: team.parent ? team.parent.slug : null, teamSlug: team.slug })}/${
|
||||
team.slug
|
||||
}`
|
||||
`${getTeamUrlSync({ orgSlug: team.parent ? team.parent.slug : null, teamSlug: team.slug })}`
|
||||
) : (
|
||||
<Badge>{t("upgrade")}</Badge>
|
||||
)}
|
||||
|
@ -245,11 +243,10 @@ export default function TeamListItem(props: Props) {
|
|||
color="secondary"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
`${
|
||||
orgBranding
|
||||
? `${orgBranding.fullDomain}`
|
||||
: `${process.env.NEXT_PUBLIC_WEBSITE_URL}/team`
|
||||
}/${team.slug}`
|
||||
`${getTeamUrlSync({
|
||||
orgSlug: team.parent ? team.parent.slug : null,
|
||||
teamSlug: team.slug,
|
||||
})}`
|
||||
);
|
||||
showToast(t("link_copied"), "success");
|
||||
}}
|
||||
|
@ -285,11 +282,10 @@ export default function TeamListItem(props: Props) {
|
|||
<DropdownItem
|
||||
type="button"
|
||||
target="_blank"
|
||||
href={`${
|
||||
orgBranding
|
||||
? `${orgBranding.fullDomain}`
|
||||
: `${process.env.NEXT_PUBLIC_WEBSITE_URL}/team`
|
||||
}/${team.slug}`}
|
||||
href={`${getTeamUrlSync({
|
||||
orgSlug: team.parent ? team.parent.slug : null,
|
||||
teamSlug: team.slug,
|
||||
})}`}
|
||||
StartIcon={ExternalLink}>
|
||||
{t("preview_team") as string}
|
||||
</DropdownItem>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Meta } from "@calcom/ui";
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import { useSession } from "next-auth/react";
|
||||
|
|
|
@ -893,7 +893,7 @@ function SideBar({ bannersHeight, user }: SideBarProps) {
|
|||
<div className="flex h-full flex-col justify-between py-3 lg:pt-4">
|
||||
<header className="todesktop:-mt-3 todesktop:flex-col-reverse todesktop:[-webkit-app-region:drag] items-center justify-between md:hidden lg:flex">
|
||||
{orgBranding ? (
|
||||
<Link href="/settings/organizations/profile" className="px-1.5">
|
||||
<Link href="/settings/organizations/profile" className="mt-3 w-full px-1.5">
|
||||
<div className="flex items-center gap-2 font-medium">
|
||||
<Avatar
|
||||
alt={`${orgBranding.name} logo`}
|
||||
|
|
|
@ -106,9 +106,9 @@ const commons = {
|
|||
const dynamicEvent = {
|
||||
length: 30,
|
||||
slug: "dynamic",
|
||||
title: "Dynamic",
|
||||
eventName: "Dynamic Event",
|
||||
description: "",
|
||||
title: "Group Meeting",
|
||||
eventName: "Group Meeting",
|
||||
description: "Join us for a meeting with multiple people",
|
||||
descriptionAsSafeHTML: "",
|
||||
position: 0,
|
||||
...commons,
|
||||
|
|
|
@ -334,6 +334,14 @@ export const teamMetadataSchema = z
|
|||
isOrganizationVerified: z.boolean().nullable(),
|
||||
isOrganizationConfigured: z.boolean().nullable(),
|
||||
orgAutoAcceptEmail: z.string().nullable(),
|
||||
migratedToOrgFrom: z
|
||||
.object({
|
||||
teamSlug: z.string().or(z.null()).optional(),
|
||||
lastMigrationTime: z.string().optional(),
|
||||
reverted: z.boolean().optional(),
|
||||
lastRevertTime: z.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
})
|
||||
.partial()
|
||||
.nullable();
|
||||
|
|
|
@ -43,17 +43,22 @@ export const createHandler = async ({ input, ctx }: CreateOptions) => {
|
|||
},
|
||||
});
|
||||
|
||||
// An org doesn't have a parentId. A team that isn't part of an org also doesn't have a parentId.
|
||||
// So, an org can't have the same slug as a non-org team.
|
||||
// There is a unique index on [slug, parentId] in Team because we don't add the slug to the team always. We only add metadata.requestedSlug in some cases. So, DB won't prevent creation of such an organization.
|
||||
const hasANonOrgTeamOrOrgWithSameSlug = await prisma.team.findFirst({
|
||||
const hasAnOrgWithSameSlug = await prisma.team.findFirst({
|
||||
where: {
|
||||
slug: slug,
|
||||
parentId: null,
|
||||
metadata: {
|
||||
path: ["isOrganization"],
|
||||
equals: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (hasANonOrgTeamOrOrgWithSameSlug || RESERVED_SUBDOMAINS.includes(slug))
|
||||
// Allow creating an organization with same requestedSlug as a non-org Team's slug
|
||||
// It is needed so that later we can migrate the non-org Team(with the conflicting slug) to the newly created org
|
||||
// Publishing the organization would fail if the team with the same slug is not migrated first
|
||||
|
||||
if (hasAnOrgWithSameSlug || RESERVED_SUBDOMAINS.includes(slug))
|
||||
throw new TRPCError({ code: "BAD_REQUEST", message: "organization_url_taken" });
|
||||
if (userCollisions) throw new TRPCError({ code: "BAD_REQUEST", message: "admin_email_taken" });
|
||||
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
"./components/icon": "./components/icon/index.ts",
|
||||
"./components/icon/Discord": "./components/icon/Discord.tsx",
|
||||
"./components/icon/SatSymbol": "./components/icon/SatSymbol.tsx",
|
||||
"./components/icon/Spinner": "./components/icon/Spinner.tsx"
|
||||
"./components/icon/Spinner": "./components/icon/Spinner.tsx",
|
||||
"./components/unpublished-entity/UnpublishedEntity": "./components/unpublished-entity/index.ts",
|
||||
"./components/form/timezone-select/TimezoneSelect": "./components/form/timezone-select/index.ts"
|
||||
},
|
||||
"types": "./index.tsx",
|
||||
"license": "MIT",
|
||||
|
|
|
@ -112,6 +112,7 @@
|
|||
"cache": false
|
||||
},
|
||||
"dx": {
|
||||
"dependsOn": ["//#env-check:common", "//#env-check:app-store"],
|
||||
"cache": false
|
||||
},
|
||||
"lint": {
|
||||
|
|
30
yarn.lock
30
yarn.lock
|
@ -3745,6 +3745,7 @@ __metadata:
|
|||
"@tailwindcss/forms": ^0.5.2
|
||||
"@tailwindcss/line-clamp": ^0.4.0
|
||||
"@tailwindcss/typography": ^0.5.4
|
||||
"@todesktop/tailwind-variants": ^1.0.0
|
||||
"@trivago/prettier-plugin-sort-imports": 4.1.1
|
||||
"@typescript-eslint/eslint-plugin": ^5.52.0
|
||||
"@typescript-eslint/parser": ^5.52.0
|
||||
|
@ -4175,6 +4176,7 @@ __metadata:
|
|||
"@calcom/tsconfig": "*"
|
||||
"@calcom/types": "*"
|
||||
"@faker-js/faker": ^7.3.0
|
||||
"@formbricks/api": ^1.1.0
|
||||
"@sendgrid/client": ^7.7.0
|
||||
"@vercel/og": ^0.5.0
|
||||
bcryptjs: ^2.4.3
|
||||
|
@ -4590,7 +4592,7 @@ __metadata:
|
|||
"@calcom/lib": "*"
|
||||
"@calcom/trpc": "*"
|
||||
"@calcom/tsconfig": "*"
|
||||
"@formkit/auto-animate": ^1.0.0-beta.5
|
||||
"@formkit/auto-animate": ^0.8.1
|
||||
"@radix-ui/react-checkbox": ^1.0.4
|
||||
"@radix-ui/react-dialog": ^1.0.4
|
||||
"@radix-ui/react-popover": ^1.0.2
|
||||
|
@ -4673,7 +4675,7 @@ __metadata:
|
|||
"@calcom/types": "*"
|
||||
"@calcom/ui": "*"
|
||||
"@daily-co/daily-js": ^0.37.0
|
||||
"@formkit/auto-animate": ^1.0.0-beta.5
|
||||
"@formkit/auto-animate": ^0.8.1
|
||||
"@glidejs/glide": ^3.5.2
|
||||
"@hookform/error-message": ^2.0.0
|
||||
"@hookform/resolvers": ^2.9.7
|
||||
|
@ -6228,10 +6230,17 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@formkit/auto-animate@npm:^1.0.0-beta.5":
|
||||
version: 1.0.0-beta.5
|
||||
resolution: "@formkit/auto-animate@npm:1.0.0-beta.5"
|
||||
checksum: 479a4f694d91e7272b2d8bdf2051fd1ae2dd5fbb522612406eaef55d88b7131402a7d5f683df8eb4e7c00810ebec8d3048483a774b0864c299d2d7af81262eb2
|
||||
"@formbricks/api@npm:^1.1.0":
|
||||
version: 1.4.0
|
||||
resolution: "@formbricks/api@npm:1.4.0"
|
||||
checksum: 2fabf27f7a1013d7e327e1c1d3d99d4860451a51ce7775968d62e9cd6149b62fce4e736b4a5c21800040b257bff82733a28c3409d8d0ae48cb59d61ca1738c6a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@formkit/auto-animate@npm:^0.8.1":
|
||||
version: 0.8.1
|
||||
resolution: "@formkit/auto-animate@npm:0.8.1"
|
||||
checksum: ffc1a3432ce81bcf7abd4360fabf05b7aee01005e64ce519f57d134e68a5e15a81f295a7b2979678896bfd7d2048202a9fa2dbd09781267899e9056e918120c1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
@ -12255,6 +12264,15 @@ __metadata:
|
|||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@todesktop/tailwind-variants@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "@todesktop/tailwind-variants@npm:1.0.1"
|
||||
peerDependencies:
|
||||
tailwindcss: ^3.4.0
|
||||
checksum: 53dffba36455b555553cf4d1d74554b54f4a586ef6dc8f7eb66d586b935765f56a0b806c2bbba0fddb435340744fe68bb9e1bbf66a4646fdc3b18af13521e4b6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@tootallnate/once@npm:1":
|
||||
version: 1.1.2
|
||||
resolution: "@tootallnate/once@npm:1.1.2"
|
||||
|
|
Loading…
Reference in New Issue
Block a user