Merge branch 'main' into integromat-app
This commit is contained in:
commit
f5911d8995
|
@ -2,7 +2,7 @@ import { useRouter } from "next/router";
|
|||
import { useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { getOrgFullDomain } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
|
||||
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
|
@ -24,9 +24,9 @@ interface UsernameAvailabilityFieldProps {
|
|||
function useUserNamePrefix(organization: RouterOutputs["viewer"]["me"]["organization"]): string {
|
||||
return organization
|
||||
? organization.slug
|
||||
? `${organization.slug}.${subdomainSuffix()}/`
|
||||
? getOrgFullDomain(organization.slug, { protocol: false })
|
||||
: organization.metadata && organization.metadata.requestedSlug
|
||||
? `${organization.metadata.requestedSlug}.${subdomainSuffix()}/`
|
||||
? getOrgFullDomain(organization.metadata.requestedSlug, { protocol: false })
|
||||
: process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")
|
||||
: process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "");
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ import { useEffect, useState, memo } from "react";
|
|||
import { z } from "zod";
|
||||
|
||||
import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks";
|
||||
import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import useIntercom from "@calcom/features/ee/support/lib/intercom/useIntercom";
|
||||
import { EventTypeDescriptionLazy as EventTypeDescription } from "@calcom/features/eventtypes/components";
|
||||
import CreateEventTypeDialog from "@calcom/features/eventtypes/components/CreateEventTypeDialog";
|
||||
|
@ -371,9 +370,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
|||
<ul ref={parent} className="divide-subtle !static w-full divide-y" data-testid="event-types">
|
||||
{types.map((type, index) => {
|
||||
const embedLink = `${group.profile.slug}/${type.slug}`;
|
||||
const calLink = `${
|
||||
orgBranding ? `${new URL(CAL_URL).protocol}//${orgBranding.slug}.${subdomainSuffix()}` : CAL_URL
|
||||
}/${embedLink}`;
|
||||
const calLink = `${orgBranding?.fullDomain ?? CAL_URL}/${embedLink}`;
|
||||
const isManagedEventType = type.schedulingType === SchedulingType.MANAGED;
|
||||
const isChildrenManagedEventType =
|
||||
type.metadata?.managedEventConfig !== undefined && type.schedulingType !== SchedulingType.MANAGED;
|
||||
|
@ -698,10 +695,10 @@ const EventTypeListHeading = ({
|
|||
profile,
|
||||
membershipCount,
|
||||
teamId,
|
||||
orgSlug,
|
||||
}: EventTypeListHeadingProps): JSX.Element => {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const orgBranding = useOrgBrandingValues();
|
||||
|
||||
const publishTeamMutation = trpc.viewer.teams.publish.useMutation({
|
||||
onSuccess(data) {
|
||||
|
@ -739,9 +736,9 @@ const EventTypeListHeading = ({
|
|||
)}
|
||||
{profile?.slug && (
|
||||
<Link href={`${CAL_URL}/${profile.slug}`} className="text-subtle block text-xs">
|
||||
{orgSlug
|
||||
? `${orgSlug}.${subdomainSuffix()}/${profile.slug}`
|
||||
: `${CAL_URL?.replace("https://", "")}/${profile.slug}`}
|
||||
{orgBranding
|
||||
? `${orgBranding.fullDomain.replace("https://", "").replace("http://", "")}${profile.slug}`
|
||||
: `${CAL_URL?.replace("https://", "").replace("http://", "")}/${profile.slug}`}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -93,16 +93,18 @@ async function getDynamicGroupPageProps(context: GetServerSidePropsContext) {
|
|||
}
|
||||
|
||||
async function getUserPageProps(context: GetServerSidePropsContext) {
|
||||
const { user: username, type: slug } = paramsSchema.parse(context.params);
|
||||
const { user: uname, type: slug } = paramsSchema.parse(context.params);
|
||||
const { rescheduleUid } = context.query;
|
||||
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(context.req.headers.host ?? "");
|
||||
|
||||
/** TODO: We should standarize this */
|
||||
const username = uname.toLowerCase().replace(/( |%20)/g, "+");
|
||||
|
||||
const { ssrInit } = await import("@server/lib/ssr");
|
||||
const ssr = await ssrInit(context);
|
||||
const user = await prisma.user.findFirst({
|
||||
where: {
|
||||
/** TODO: We should standarize this */
|
||||
username: username.toLowerCase().replace(/( |%20)/g, "+"),
|
||||
username,
|
||||
organization: isValidOrgDomain
|
||||
? {
|
||||
slug: currentOrgDomain,
|
||||
|
|
|
@ -8,12 +8,14 @@ import { z } from "zod";
|
|||
|
||||
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
|
||||
import { checkPremiumUsername } from "@calcom/features/ee/common/lib/checkPremiumUsername";
|
||||
import { getOrgFullDomain } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { isSAMLLoginEnabled } from "@calcom/features/ee/sso/lib/saml";
|
||||
import { useFlagMap } from "@calcom/features/flags/context/provider";
|
||||
import { getFeatureFlagMap } from "@calcom/features/flags/server/utils";
|
||||
import { IS_SELF_HOSTED, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||
import { Alert, Button, EmailField, HeadSeo, PasswordField, TextField } from "@calcom/ui";
|
||||
|
||||
|
@ -30,7 +32,9 @@ type FormValues = {
|
|||
token?: string;
|
||||
};
|
||||
|
||||
export default function Signup({ prepopulateFormValues, token }: inferSSRProps<typeof getServerSideProps>) {
|
||||
type SignupProps = inferSSRProps<typeof getServerSideProps>;
|
||||
|
||||
export default function Signup({ prepopulateFormValues, token, orgSlug }: SignupProps) {
|
||||
const { t, i18n } = useLocale();
|
||||
const router = useRouter();
|
||||
const flags = useFlagMap();
|
||||
|
@ -116,7 +120,11 @@ export default function Signup({ prepopulateFormValues, token }: inferSSRProps<t
|
|||
{errors.apiError && <Alert severity="error" message={errors.apiError?.message} />}
|
||||
<div className="space-y-4">
|
||||
<TextField
|
||||
addOnLeading={`${process.env.NEXT_PUBLIC_WEBSITE_URL}/`}
|
||||
addOnLeading={
|
||||
orgSlug
|
||||
? getOrgFullDomain(orgSlug, { protocol: false })
|
||||
: `${process.env.NEXT_PUBLIC_WEBSITE_URL}/`
|
||||
}
|
||||
{...register("username")}
|
||||
required
|
||||
/>
|
||||
|
@ -233,6 +241,22 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
|||
|
||||
let username = guessUsernameFromEmail(verificationToken.identifier);
|
||||
|
||||
const orgInfo = await prisma.user.findFirst({
|
||||
where: {
|
||||
email: verificationToken?.identifier,
|
||||
},
|
||||
select: {
|
||||
organization: {
|
||||
select: {
|
||||
slug: true,
|
||||
metadata: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const userOrgMetadata = teamMetadataSchema.parse(orgInfo?.organization?.metadata ?? {});
|
||||
|
||||
if (!IS_SELF_HOSTED) {
|
||||
// Im not sure we actually hit this because of next redirects signup to website repo - but just in case this is pretty cool :)
|
||||
const { available, suggestion } = await checkPremiumUsername(username);
|
||||
|
@ -248,6 +272,7 @@ export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
|
|||
email: verificationToken.identifier,
|
||||
username,
|
||||
},
|
||||
orgSlug: (orgInfo?.organization?.slug || userOrgMetadata?.requestedSlug) ?? null,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
|
|
@ -65,6 +65,7 @@ export const AboutOrganizationForm = () => {
|
|||
fallback={<Plus className="text-subtle h-6 w-6" />}
|
||||
asChild
|
||||
className="items-center"
|
||||
imageSrc={image}
|
||||
size="lg"
|
||||
/>
|
||||
<div className="ms-4">
|
||||
|
|
|
@ -12,7 +12,9 @@ export type OrganizationBranding =
|
|||
| ({
|
||||
logo?: string | null | undefined;
|
||||
name?: string;
|
||||
slug?: string;
|
||||
slug: string;
|
||||
fullDomain: string;
|
||||
domainSuffix: string;
|
||||
} & z.infer<typeof teamMetadataSchema>)
|
||||
| null
|
||||
| undefined;
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ALLOWED_HOSTNAMES, RESERVED_SUBDOMAINS, WEBAPP_URL } from "@calcom/lib/
|
|||
* return the org slug
|
||||
* @param hostname
|
||||
*/
|
||||
export function getOrgDomain(hostname: string) {
|
||||
export function getOrgSlug(hostname: string) {
|
||||
// Find which hostname is being currently used
|
||||
const currentHostname = ALLOWED_HOSTNAMES.find((ahn) => {
|
||||
const url = new URL(WEBAPP_URL);
|
||||
|
@ -20,7 +20,7 @@ export function getOrgDomain(hostname: string) {
|
|||
}
|
||||
|
||||
export function orgDomainConfig(hostname: string) {
|
||||
const currentOrgDomain = getOrgDomain(hostname);
|
||||
const currentOrgDomain = getOrgSlug(hostname);
|
||||
return {
|
||||
currentOrgDomain,
|
||||
isValidOrgDomain: currentOrgDomain !== null && !RESERVED_SUBDOMAINS.includes(currentOrgDomain),
|
||||
|
@ -31,3 +31,7 @@ export function subdomainSuffix() {
|
|||
const urlSplit = WEBAPP_URL.replace("https://", "")?.replace("http://", "").split(".");
|
||||
return urlSplit.length === 3 ? urlSplit.slice(1).join(".") : urlSplit.join(".");
|
||||
}
|
||||
|
||||
export function getOrgFullDomain(slug: string, options: { protocol: boolean } = { protocol: true }) {
|
||||
return `${options.protocol ? `${new URL(WEBAPP_URL).protocol}//` : ""}${slug}.${subdomainSuffix()}/`;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
@ -14,7 +13,6 @@ import {
|
|||
SkeletonButton,
|
||||
SkeletonContainer,
|
||||
SkeletonText,
|
||||
Switch,
|
||||
} from "@calcom/ui";
|
||||
|
||||
import ThemeLabel from "../../../../settings/ThemeLabel";
|
||||
|
@ -170,59 +168,6 @@ const OrgAppearanceView = () => {
|
|||
)}
|
||||
/>
|
||||
</div>
|
||||
<hr className="border-subtle my-8" />
|
||||
|
||||
<div className="flex flex-col gap-8">
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex-grow text-sm">
|
||||
<label htmlFor="hide-branding" className="text-default font-medium">
|
||||
{t("disable_cal_branding", { appName: APP_NAME })}
|
||||
</label>
|
||||
<p className="text-subtle">
|
||||
{t("team_disable_cal_branding_description", { appName: APP_NAME })}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex-none">
|
||||
<Controller
|
||||
control={form.control}
|
||||
defaultValue={currentOrg?.hideBranding ?? false}
|
||||
name="hideBranding"
|
||||
render={({ field }) => (
|
||||
<Switch
|
||||
defaultChecked={field.value}
|
||||
onCheckedChange={(isChecked) => {
|
||||
form.setValue("hideBranding", isChecked);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative flex items-start">
|
||||
<div className="flex-grow text-sm">
|
||||
<label htmlFor="hide-branding" className="text-default font-medium">
|
||||
{t("hide_book_a_team_member")}
|
||||
</label>
|
||||
<p className="text-subtle">{t("hide_book_a_team_member_description")}</p>
|
||||
</div>
|
||||
<div className="flex-none">
|
||||
<Controller
|
||||
control={form.control}
|
||||
defaultValue={currentOrg?.hideBookATeamMember ?? false}
|
||||
name="hideBookATeamMember"
|
||||
render={({ field }) => (
|
||||
<Switch
|
||||
defaultChecked={field.value}
|
||||
onCheckedChange={(isChecked) => {
|
||||
form.setValue("hideBookATeamMember", isChecked);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Button color="primary" className="mt-8" type="submit" loading={mutation.isLoading}>
|
||||
{t("update")}
|
||||
</Button>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import { LinkIcon, Trash2 } from "lucide-react";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useState, useLayoutEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
@ -34,8 +33,6 @@ import {
|
|||
|
||||
import { getLayout } from "../../../../settings/layouts/SettingsLayout";
|
||||
|
||||
const regex = new RegExp("^[a-zA-Z0-9-]*$");
|
||||
|
||||
const orgProfileFormSchema = z.object({
|
||||
name: z.string(),
|
||||
logo: z.string(),
|
||||
|
@ -46,7 +43,6 @@ const OrgProfileView = () => {
|
|||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const utils = trpc.useContext();
|
||||
const session = useSession();
|
||||
const [firstRender, setFirstRender] = useState(true);
|
||||
const orgBranding = useOrgBrandingValues();
|
||||
|
||||
|
@ -54,6 +50,10 @@ const OrgProfileView = () => {
|
|||
document.body.focus();
|
||||
}, []);
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(orgProfileFormSchema),
|
||||
});
|
||||
|
||||
const mutation = trpc.viewer.organizations.update.useMutation({
|
||||
onError: (err) => {
|
||||
showToast(err.message, "error");
|
||||
|
@ -64,10 +64,6 @@ const OrgProfileView = () => {
|
|||
},
|
||||
});
|
||||
|
||||
const form = useForm({
|
||||
resolver: zodResolver(orgProfileFormSchema),
|
||||
});
|
||||
|
||||
const { data: currentOrganisation, isLoading } = trpc.viewer.organizations.listCurrent.useQuery(undefined, {
|
||||
onError: () => {
|
||||
router.push("/settings");
|
||||
|
@ -90,10 +86,6 @@ const OrgProfileView = () => {
|
|||
(currentOrganisation.user.role === MembershipRole.OWNER ||
|
||||
currentOrganisation.user.role === MembershipRole.ADMIN);
|
||||
|
||||
const permalink = `${new URL(process.env.NEXT_PUBLIC_WEBSITE_URL || "").protocol}//${
|
||||
orgBranding?.slug
|
||||
}.${subdomainSuffix()}`;
|
||||
|
||||
const isBioEmpty =
|
||||
!currentOrganisation ||
|
||||
!currentOrganisation.bio ||
|
||||
|
@ -107,6 +99,8 @@ const OrgProfileView = () => {
|
|||
},
|
||||
});
|
||||
|
||||
if (!orgBranding) return null;
|
||||
|
||||
function deleteTeam() {
|
||||
if (currentOrganisation?.id) deleteTeamMutation.mutate({ teamId: currentOrganisation.id });
|
||||
}
|
||||
|
@ -222,7 +216,7 @@ const OrgProfileView = () => {
|
|||
<LinkIconButton
|
||||
Icon={LinkIcon}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(permalink);
|
||||
navigator.clipboard.writeText(orgBranding.fullDomain);
|
||||
showToast("Copied to clipboard", "success");
|
||||
}}>
|
||||
{t("copy_link_org")}
|
||||
|
|
|
@ -4,13 +4,14 @@ import { Controller, useForm } from "react-hook-form";
|
|||
import { z } from "zod";
|
||||
|
||||
import { extractDomainFromWebsiteUrl } from "@calcom/ee/organizations/lib/utils";
|
||||
import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks";
|
||||
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Avatar, Button, Form, ImageUploader, TextField, Alert } from "@calcom/ui";
|
||||
import { ArrowRight } from "@calcom/ui/components/icon";
|
||||
import { Avatar, Button, Form, ImageUploader, TextField, Alert, Label } from "@calcom/ui";
|
||||
import { ArrowRight, Plus } from "@calcom/ui/components/icon";
|
||||
|
||||
import type { NewTeamFormValues } from "../lib/types";
|
||||
|
||||
|
@ -26,6 +27,7 @@ export const CreateANewTeamForm = () => {
|
|||
const parsedQuery = querySchema.safeParse(router.query);
|
||||
console.log({ parsedQuery });
|
||||
const [serverErrorMessage, setServerErrorMessage] = useState<string | null>(null);
|
||||
const orgBranding = useOrgBrandingValues();
|
||||
|
||||
const returnToParam =
|
||||
(parsedQuery.success ? getSafeRedirectUrl(parsedQuery.data.returnTo) : "/settings/teams") ||
|
||||
|
@ -107,7 +109,10 @@ export const CreateANewTeamForm = () => {
|
|||
name="slug"
|
||||
placeholder="acme"
|
||||
label={t("team_url")}
|
||||
addOnLeading={`${extractDomainFromWebsiteUrl}/team/`}
|
||||
addOnLeading={`${
|
||||
orgBranding?.fullDomain.replace("https://", "").replace("http://", "") ??
|
||||
`${extractDomainFromWebsiteUrl}/team/`
|
||||
}`}
|
||||
defaultValue={value}
|
||||
onChange={(e) => {
|
||||
newTeamFormMethods.setValue("slug", slugify(e?.target.value), {
|
||||
|
@ -125,21 +130,29 @@ export const CreateANewTeamForm = () => {
|
|||
control={newTeamFormMethods.control}
|
||||
name="logo"
|
||||
render={({ field: { value } }) => (
|
||||
<div className="flex items-center">
|
||||
<Avatar alt="" imageSrc={value || null} gravatarFallbackMd5="newTeam" size="lg" />
|
||||
<div className="ms-4">
|
||||
<ImageUploader
|
||||
target="avatar"
|
||||
id="avatar-upload"
|
||||
buttonMsg={t("update")}
|
||||
handleAvatarChange={(newAvatar: string) => {
|
||||
newTeamFormMethods.setValue("logo", newAvatar);
|
||||
createTeamMutation.reset();
|
||||
}}
|
||||
<>
|
||||
<Label>{t("team_logo")}</Label>
|
||||
<div className="flex items-center">
|
||||
<Avatar
|
||||
alt=""
|
||||
imageSrc={value}
|
||||
fallback={<Plus className="text-subtle h-6 w-6" />}
|
||||
size="lg"
|
||||
/>
|
||||
<div className="ms-4">
|
||||
<ImageUploader
|
||||
target="avatar"
|
||||
id="avatar-upload"
|
||||
buttonMsg={t("update")}
|
||||
handleAvatarChange={(newAvatar: string) => {
|
||||
newTeamFormMethods.setValue("logo", newAvatar);
|
||||
createTeamMutation.reset();
|
||||
}}
|
||||
imageSrc={value}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,6 @@ import { useState } from "react";
|
|||
import InviteLinkSettingsModal from "@calcom/ee/teams/components/InviteLinkSettingsModal";
|
||||
import MemberInvitationModal from "@calcom/ee/teams/components/MemberInvitationModal";
|
||||
import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks";
|
||||
import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
@ -104,7 +103,7 @@ export default function TeamListItem(props: Props) {
|
|||
<span className="text-muted block text-xs">
|
||||
{team.slug
|
||||
? orgBranding
|
||||
? `${orgBranding.slug}.${subdomainSuffix()}/${team.slug}`
|
||||
? `${orgBranding.fullDomain}${team.slug}`
|
||||
: `${process.env.NEXT_PUBLIC_WEBSITE_URL}/team/${team.slug}`
|
||||
: "Unpublished team"}
|
||||
</span>
|
||||
|
@ -235,7 +234,11 @@ export default function TeamListItem(props: Props) {
|
|||
color="secondary"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
process.env.NEXT_PUBLIC_WEBSITE_URL + "/team/" + team.slug
|
||||
`${
|
||||
orgBranding
|
||||
? `${orgBranding.fullDomain}`
|
||||
: process.env.NEXT_PUBLIC_WEBSITE_URL + "/team/"
|
||||
}${team.slug}`
|
||||
);
|
||||
showToast(t("link_copied"), "success");
|
||||
}}
|
||||
|
@ -271,7 +274,11 @@ export default function TeamListItem(props: Props) {
|
|||
<DropdownItem
|
||||
type="button"
|
||||
target="_blank"
|
||||
href={`${process.env.NEXT_PUBLIC_WEBSITE_URL}/team/${team.slug}`}
|
||||
href={`${
|
||||
orgBranding
|
||||
? `${orgBranding.fullDomain}`
|
||||
: `${process.env.NEXT_PUBLIC_WEBSITE_URL}/team/`
|
||||
}${team.slug}`}
|
||||
StartIcon={ExternalLink}>
|
||||
{t("preview_team") as string}
|
||||
</DropdownItem>
|
||||
|
|
|
@ -7,7 +7,6 @@ import { useForm } from "react-hook-form";
|
|||
import { z } from "zod";
|
||||
|
||||
import { useOrgBrandingValues } from "@calcom/features/ee/organizations/hooks";
|
||||
import { subdomainSuffix } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { useFlagMap } from "@calcom/features/flags/context/provider";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
|
@ -139,9 +138,7 @@ export default function CreateEventTypeDialog({
|
|||
});
|
||||
|
||||
const flags = useFlagMap();
|
||||
const urlPrefix = orgBranding
|
||||
? `${orgBranding.slug}.${subdomainSuffix()}`
|
||||
: process.env.NEXT_PUBLIC_WEBSITE_URL;
|
||||
const urlPrefix = orgBranding?.fullDomain ?? process.env.NEXT_PUBLIC_WEBSITE_URL;
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
|
|
@ -362,7 +362,7 @@ function UserDropdown({ small }: UserDropdownProps) {
|
|||
/>
|
||||
</span>
|
||||
{!small && (
|
||||
<span className="flex flex-grow items-center">
|
||||
<span className="flex flex-grow items-center gap-2">
|
||||
<span className="line-clamp-1 flex-grow text-sm leading-none">
|
||||
<span className="text-emphasis block font-medium">{user.name || "Nameless User"}</span>
|
||||
</span>
|
||||
|
@ -778,13 +778,11 @@ type SideBarProps = {
|
|||
|
||||
function SideBarContainer({ bannersHeight }: SideBarContainerProps) {
|
||||
const { status, data } = useSession();
|
||||
const router = useRouter();
|
||||
|
||||
// Make sure that Sidebar is rendered optimistically so that a refresh of pages when logged in have SideBar from the beginning.
|
||||
// This improves the experience of refresh on app store pages(when logged in) which are SSG.
|
||||
// Though when logged out, app store pages would temporarily show SideBar until session status is confirmed.
|
||||
if (status !== "loading" && status !== "authenticated") return null;
|
||||
if (router.route.startsWith("/v2/settings/")) return null;
|
||||
return <SideBar bannersHeight={bannersHeight} user={data?.user} />;
|
||||
}
|
||||
|
||||
|
@ -819,9 +817,7 @@ function SideBar({ bannersHeight, user }: SideBarProps) {
|
|||
},
|
||||
{
|
||||
name: "settings",
|
||||
href: user?.organizationId
|
||||
? `/settings/teams/${user.organizationId}/profile`
|
||||
: "/settings/my-account/profile",
|
||||
href: user?.organizationId ? `/settings/organizations/profile` : "/settings/my-account/profile",
|
||||
icon: Settings,
|
||||
},
|
||||
];
|
||||
|
@ -834,20 +830,16 @@ function SideBar({ bannersHeight, user }: SideBarProps) {
|
|||
<header className="items-center justify-between md:hidden lg:flex">
|
||||
{orgBranding ? (
|
||||
<Link href="/event-types" className="px-1.5">
|
||||
{orgBranding ? (
|
||||
<div className="flex items-center gap-2 font-medium">
|
||||
<Avatar
|
||||
alt={`${orgBranding.name} logo`}
|
||||
imageSrc={getPlaceholderAvatar(orgBranding.logo, orgBranding.name)}
|
||||
size="xsm"
|
||||
/>
|
||||
<p className="text line-clamp-1 text-sm">
|
||||
<span>{orgBranding.name}</span>
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<Logo small />
|
||||
)}
|
||||
<div className="flex items-center gap-2 font-medium">
|
||||
<Avatar
|
||||
alt={`${orgBranding.name} logo`}
|
||||
imageSrc={getPlaceholderAvatar(orgBranding.logo, orgBranding.name)}
|
||||
size="xsm"
|
||||
/>
|
||||
<p className="text line-clamp-1 text-sm">
|
||||
<span>{orgBranding.name}</span>
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
) : (
|
||||
<div data-testid="user-dropdown-trigger">
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { orgDomainConfig, getOrgDomain } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { orgDomainConfig, getOrgSlug } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import * as constants from "@calcom/lib/constants";
|
||||
|
||||
function setupEnvs({ WEBAPP_URL = "https://app.cal.com" } = {}) {
|
||||
|
@ -32,25 +32,25 @@ describe("Org Domains Utils", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("getOrgDomain", () => {
|
||||
describe("getOrgSlug", () => {
|
||||
it("should handle a prod web app url with a prod subdomain hostname", () => {
|
||||
setupEnvs();
|
||||
expect(getOrgDomain("acme.cal.com")).toEqual("acme");
|
||||
expect(getOrgSlug("acme.cal.com")).toEqual("acme");
|
||||
});
|
||||
|
||||
it("should handle a prod web app url with a staging subdomain hostname", () => {
|
||||
setupEnvs();
|
||||
expect(getOrgDomain("acme.cal.dev")).toEqual(null);
|
||||
expect(getOrgSlug("acme.cal.dev")).toEqual(null);
|
||||
});
|
||||
|
||||
it("should handle a local web app with port url with a local subdomain hostname", () => {
|
||||
setupEnvs({ WEBAPP_URL: "http://app.cal.local:3000" });
|
||||
expect(getOrgDomain("acme.cal.local:3000")).toEqual("acme");
|
||||
expect(getOrgSlug("acme.cal.local:3000")).toEqual("acme");
|
||||
});
|
||||
|
||||
it("should handle a local web app with port url with a non-local subdomain hostname", () => {
|
||||
setupEnvs({ WEBAPP_URL: "http://app.cal.local:3000" });
|
||||
expect(getOrgDomain("acme.cal.com:3000")).toEqual(null);
|
||||
expect(getOrgSlug("acme.cal.com:3000")).toEqual(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -25,7 +25,7 @@ const vercelCreateDomain = async (domain: string) => {
|
|||
const response = await fetch(
|
||||
`https://api.vercel.com/v8/projects/${process.env.PROJECT_ID_VERCEL}/domains?teamId=${process.env.TEAM_ID_VERCEL}`,
|
||||
{
|
||||
body: `{\n "name": "${domain}.${subdomainSuffix()}"\n}`,
|
||||
body: JSON.stringify({ name: `${domain}.${subdomainSuffix()}` }),
|
||||
headers: {
|
||||
Authorization: `Bearer ${process.env.AUTH_BEARER_TOKEN_VERCEL}`,
|
||||
"Content-Type": "application/json",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { subdomainSuffix, getOrgFullDomain } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { teamMetadataSchema } from "@calcom/prisma/zod-utils";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
|
||||
|
@ -26,11 +27,15 @@ export const getBrandHandler = async ({ ctx }: VerifyCodeOptions) => {
|
|||
});
|
||||
|
||||
const metadata = teamMetadataSchema.parse(team?.metadata);
|
||||
const slug = team?.slug || metadata?.requestedSlug;
|
||||
const slug = (team?.slug || metadata?.requestedSlug) as string;
|
||||
const fullDomain = getOrgFullDomain(slug);
|
||||
const domainSuffix = subdomainSuffix();
|
||||
|
||||
return {
|
||||
...team,
|
||||
metadata,
|
||||
slug,
|
||||
fullDomain,
|
||||
domainSuffix,
|
||||
};
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue
Block a user