Merge commit 'fcc50c1d0f90d77253455e7caadab383a35ebefb' into testE2E-timezone
This commit is contained in:
commit
612ef2678c
|
@ -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
|
||||
|
|
|
@ -56,6 +56,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 +99,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:
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue
Block a user