fix: avatar URL breaking for team/orgs in BookerEmbed atom (#27424)

* fix: avatar URL breaking for team/orgs in booker embed

* fix: api v2 breaking because of type mismatch

* fix: atoms build

* chore: implement PR feedback
This commit is contained in:
Rajiv Sahal
2026-02-03 17:19:48 +00:00
committed by GitHub
parent 2ccff0aead
commit efcbc3a07b
3 changed files with 46 additions and 20 deletions
+8 -9
View File
@@ -1,21 +1,20 @@
import { z } from "zod";
import { AVATAR_FALLBACK, CAL_URL } from "@calcom/lib/constants";
import type { User } from "@calcom/prisma/client";
import { z } from "zod";
export const getAbsoluteAvatarUrl = (url: string): string => {
const isAbsolute = z.string().url().safeParse(url).success;
return isAbsolute ? url : CAL_URL + url;
};
/**
* Gives an organization aware avatar url for a user
* It ensures that the wrong avatar isn't fetched by ensuring that organizationId is always passed
* It should always return a fully formed url
*/
export const getUserAvatarUrl = (user: Pick<User, "avatarUrl"> | undefined) => {
export const getUserAvatarUrl = (user: Pick<User, "avatarUrl"> | undefined): string => {
if (user?.avatarUrl) {
const isAbsoluteUrl = z.string().url().safeParse(user.avatarUrl).success;
if (isAbsoluteUrl) {
return user.avatarUrl;
} else {
return CAL_URL + user.avatarUrl;
}
return getAbsoluteAvatarUrl(user.avatarUrl);
}
return CAL_URL + AVATAR_FALLBACK;
};
@@ -2,12 +2,14 @@ import { useQuery } from "@tanstack/react-query";
import { useMemo } from "react";
import { getUsernameList } from "@calcom/features/eventtypes/lib/defaultEvents";
import { getAbsoluteAvatarUrl } from "@calcom/lib/getAvatarUrl";
import { SUCCESS_STATUS, V2_ENDPOINTS } from "@calcom/platform-constants";
import type { PublicEventType } from "@calcom/features/eventtypes/lib/getPublicEvent";
import type { ApiResponse } from "@calcom/platform-types";
import http from "../../../lib/http";
import { useAtomsContext } from "../../../hooks/useAtomsContext";
import { useIsPlatformBookerEmbed } from "../../../hooks/useIsPlatformBookerEmbed";
export const QUERY_KEY = "get-public-event";
export type UsePublicEventReturnType = ReturnType<typeof useAtomGetPublicEvent>;
@@ -20,9 +22,15 @@ type Props = {
selectedDuration: number | null;
};
export const useAtomGetPublicEvent = ({ username, eventSlug, isTeamEvent, teamId, selectedDuration }: Props) => {
export const useAtomGetPublicEvent = ({
username,
eventSlug,
isTeamEvent,
teamId,
selectedDuration,
}: Props) => {
const { organizationId } = useAtomsContext();
const isPlatformBookerEmbed = useIsPlatformBookerEmbed();
const isDynamic = useMemo(() => {
return getUsernameList(username ?? "").length > 1;
@@ -31,22 +39,30 @@ export const useAtomGetPublicEvent = ({ username, eventSlug, isTeamEvent, teamId
const pathname = `/atoms/${V2_ENDPOINTS.eventTypes}/${eventSlug}/public`;
const event = useQuery({
queryKey: [QUERY_KEY, username, eventSlug, isTeamEvent, teamId, organizationId],
queryKey: [
QUERY_KEY,
username,
eventSlug,
isTeamEvent,
teamId,
organizationId,
],
queryFn: () => {
const params: Record<string, any> = {
isTeamEvent,
teamId,
username: getUsernameList(username ?? "").join("+")
username: getUsernameList(username ?? "").join("+"),
};
// Only include orgId if it's not 0
if (organizationId !== 0) {
params.orgId = organizationId;
}
return http?.get<ApiResponse<PublicEventType>>(pathname, {
params,
})
return http
?.get<ApiResponse<PublicEventType>>(pathname, {
params,
})
.then((res) => {
if (res.data.status === SUCCESS_STATUS) {
if (isDynamic && selectedDuration && res.data.data) {
@@ -54,6 +70,18 @@ export const useAtomGetPublicEvent = ({ username, eventSlug, isTeamEvent, teamId
// but we are re-using the dynamic event type as a team event, so we must set the event length to whatever the event length is.
res.data.data.length = selectedDuration;
}
if (isPlatformBookerEmbed && res.data.data) {
if (res.data.data.profile?.image) {
res.data.data.profile.image = getAbsoluteAvatarUrl(
res.data.data.profile.image
);
}
if (res.data.data.entity?.logoUrl) {
res.data.data.entity.logoUrl = getAbsoluteAvatarUrl(
res.data.data.entity.logoUrl
);
}
}
return res.data.data;
}
throw new Error(res.data.error.message);
+1 -2
View File
@@ -1,6 +1,5 @@
import path, { resolve } from "node:path";
import react from "@vitejs/plugin-react-swc";
import path from "node:path"
import { resolve } from "node:path";
import { defineConfig, loadEnv } from "vite";
import dts from "vite-plugin-dts";