refactor: Top Banner and add google calendar credential banner (#12532)
Fixes: https://github.com/calcom/cal.com/issues/12473 TODO: - [x] Fix Type error <img width="1512" alt="Screenshot 2023-12-02 at 12 47 19 AM" src="https://github.com/calcom/cal.com/assets/53316345/8a5c6dd0-6095-482b-b4d0-81653607a270"> <img width="1512" alt="Screenshot 2023-12-02 at 12 47 39 AM" src="https://github.com/calcom/cal.com/assets/53316345/fc64edb9-27b3-438f-b42d-75b200ac96e9">
This commit is contained in:
parent
969411041b
commit
90a6fc3f26
|
@ -880,6 +880,7 @@
|
|||
"toggle_calendars_conflict": "Toggle the calendars you want to check for conflicts to prevent double bookings.",
|
||||
"connect_additional_calendar": "Connect additional calendar",
|
||||
"calendar_updated_successfully": "Calendar updated successfully",
|
||||
"check_here":"Check here",
|
||||
"conferencing": "Conferencing",
|
||||
"calendar": "Calendar",
|
||||
"payments": "Payments",
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import { useSession } from "next-auth/react";
|
||||
import type { SessionContextValue } from "next-auth/react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { TopBanner } from "@calcom/ui";
|
||||
|
||||
function ImpersonatingBanner() {
|
||||
export type ImpersonatingBannerProps = { data: SessionContextValue["data"] };
|
||||
|
||||
function ImpersonatingBanner({ data }: ImpersonatingBannerProps) {
|
||||
const { t } = useLocale();
|
||||
const { data } = useSession();
|
||||
|
||||
if (!data?.user.impersonatedByUID) return null;
|
||||
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import { useRouter } from "next/navigation";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { showToast, TopBanner } from "@calcom/ui";
|
||||
|
||||
export function OrgUpgradeBanner() {
|
||||
export type OrgUpgradeBannerProps = {
|
||||
data: RouterOutputs["viewer"]["getUserTopBanners"]["orgUpgradeBanner"];
|
||||
};
|
||||
|
||||
export function OrgUpgradeBanner({ data }: OrgUpgradeBannerProps) {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const { data } = trpc.viewer.organizations.checkIfOrgNeedsUpgrade.useQuery();
|
||||
const publishOrgMutation = trpc.viewer.organizations.publish.useMutation({
|
||||
onSuccess(data) {
|
||||
router.push(data.url);
|
||||
|
|
|
@ -2,12 +2,17 @@ import { useRouter } from "next/navigation";
|
|||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { showToast, TopBanner } from "@calcom/ui";
|
||||
|
||||
export function TeamsUpgradeBanner() {
|
||||
export type TeamsUpgradeBannerProps = {
|
||||
data: RouterOutputs["viewer"]["getUserTopBanners"]["teamUpgradeBanner"];
|
||||
};
|
||||
|
||||
export function TeamsUpgradeBanner({ data }: TeamsUpgradeBannerProps) {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const { data } = trpc.viewer.teams.getUpgradeable.useQuery();
|
||||
|
||||
const publishTeamMutation = trpc.viewer.teams.publish.useMutation({
|
||||
onSuccess(data) {
|
||||
router.push(data.url);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
export { CreateANewTeamForm } from "./CreateANewTeamForm";
|
||||
export { TeamsListing } from "./TeamsListing";
|
||||
export { TeamsUpgradeBanner } from "./TeamsUpgradeBanner";
|
||||
export { TeamsUpgradeBanner, type TeamsUpgradeBannerProps } from "./TeamsUpgradeBanner";
|
||||
|
|
|
@ -213,7 +213,9 @@ const SettingsSidebarContainer = ({
|
|||
enabled: !!session.data?.user?.org,
|
||||
});
|
||||
|
||||
const { data: otherTeams } = trpc.viewer.organizations.listOtherTeams.useQuery();
|
||||
const { data: otherTeams } = trpc.viewer.organizations.listOtherTeams.useQuery(undefined, {
|
||||
enabled: !!session.data?.user?.org,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (teams) {
|
||||
|
|
|
@ -4,27 +4,39 @@ import dynamic from "next/dynamic";
|
|||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import type { Dispatch, ReactElement, ReactNode, SetStateAction } from "react";
|
||||
import React, { cloneElement, Fragment, useEffect, useMemo, useRef, useState } from "react";
|
||||
import React, { cloneElement, Fragment, useEffect, useMemo, useState } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { useIsEmbed } from "@calcom/embed-core/embed-iframe";
|
||||
import UnconfirmedBookingBadge from "@calcom/features/bookings/UnconfirmedBookingBadge";
|
||||
import ImpersonatingBanner from "@calcom/features/ee/impersonation/components/ImpersonatingBanner";
|
||||
import { OrgUpgradeBanner } from "@calcom/features/ee/organizations/components/OrgUpgradeBanner";
|
||||
import ImpersonatingBanner, {
|
||||
type ImpersonatingBannerProps,
|
||||
} from "@calcom/features/ee/impersonation/components/ImpersonatingBanner";
|
||||
import {
|
||||
OrgUpgradeBanner,
|
||||
type OrgUpgradeBannerProps,
|
||||
} from "@calcom/features/ee/organizations/components/OrgUpgradeBanner";
|
||||
import { getOrgFullOrigin } from "@calcom/features/ee/organizations/lib/orgDomains";
|
||||
import HelpMenuItem from "@calcom/features/ee/support/components/HelpMenuItem";
|
||||
import { TeamsUpgradeBanner } from "@calcom/features/ee/teams/components";
|
||||
import { TeamsUpgradeBanner, type TeamsUpgradeBannerProps } from "@calcom/features/ee/teams/components";
|
||||
import { useFlagMap } from "@calcom/features/flags/context/provider";
|
||||
import { KBarContent, KBarRoot, KBarTrigger } from "@calcom/features/kbar/Kbar";
|
||||
import TimezoneChangeDialog from "@calcom/features/settings/TimezoneChangeDialog";
|
||||
import AdminPasswordBanner from "@calcom/features/users/components/AdminPasswordBanner";
|
||||
import VerifyEmailBanner from "@calcom/features/users/components/VerifyEmailBanner";
|
||||
import AdminPasswordBanner, {
|
||||
type AdminPasswordBannerProps,
|
||||
} from "@calcom/features/users/components/AdminPasswordBanner";
|
||||
import CalendarCredentialBanner, {
|
||||
type CalendarCredentialBannerProps,
|
||||
} from "@calcom/features/users/components/CalendarCredentialBanner";
|
||||
import VerifyEmailBanner, {
|
||||
type VerifyEmailBannerProps,
|
||||
} from "@calcom/features/users/components/VerifyEmailBanner";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { TOP_BANNER_HEIGHT } from "@calcom/lib/constants";
|
||||
import { APP_NAME, DESKTOP_APP_LINK, JOIN_DISCORD, ROADMAP, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import getBrandColours from "@calcom/lib/getBrandColours";
|
||||
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
|
||||
import { useIsomorphicLayoutEffect } from "@calcom/lib/hooks/useIsomorphicLayoutEffect";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { isKeyInObject } from "@calcom/lib/isKeyInObject";
|
||||
import type { User } from "@calcom/prisma/client";
|
||||
|
@ -131,38 +143,29 @@ function useRedirectToLoginIfUnauthenticated(isPublic = false) {
|
|||
};
|
||||
}
|
||||
|
||||
function AppTop({ setBannersHeight }: { setBannersHeight: Dispatch<SetStateAction<number>> }) {
|
||||
const bannerRef = useRef<HTMLDivElement | null>(null);
|
||||
type BannerTypeProps = {
|
||||
teamUpgradeBanner: TeamsUpgradeBannerProps;
|
||||
orgUpgradeBanner: OrgUpgradeBannerProps;
|
||||
verifyEmailBanner: VerifyEmailBannerProps;
|
||||
adminPasswordBanner: AdminPasswordBannerProps;
|
||||
impersonationBanner: ImpersonatingBannerProps;
|
||||
calendarCredentialBanner: CalendarCredentialBannerProps;
|
||||
};
|
||||
|
||||
useIsomorphicLayoutEffect(() => {
|
||||
const resizeObserver = new ResizeObserver((entries) => {
|
||||
const { offsetHeight } = entries[0].target as HTMLElement;
|
||||
setBannersHeight(offsetHeight);
|
||||
});
|
||||
type BannerType = keyof BannerTypeProps;
|
||||
|
||||
const currentBannerRef = bannerRef.current;
|
||||
type BannerComponent = {
|
||||
[Key in BannerType]: (props: BannerTypeProps[Key]) => JSX.Element;
|
||||
};
|
||||
|
||||
if (currentBannerRef) {
|
||||
resizeObserver.observe(currentBannerRef);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (currentBannerRef) {
|
||||
resizeObserver.unobserve(currentBannerRef);
|
||||
}
|
||||
};
|
||||
}, [bannerRef]);
|
||||
|
||||
return (
|
||||
<div ref={bannerRef} className="sticky top-0 z-10 w-full divide-y divide-black">
|
||||
<TeamsUpgradeBanner />
|
||||
<OrgUpgradeBanner />
|
||||
<ImpersonatingBanner />
|
||||
<AdminPasswordBanner />
|
||||
<VerifyEmailBanner />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const BannerComponent: BannerComponent = {
|
||||
teamUpgradeBanner: (props: TeamsUpgradeBannerProps) => <TeamsUpgradeBanner {...props} />,
|
||||
orgUpgradeBanner: (props: OrgUpgradeBannerProps) => <OrgUpgradeBanner {...props} />,
|
||||
verifyEmailBanner: (props: VerifyEmailBannerProps) => <VerifyEmailBanner {...props} />,
|
||||
adminPasswordBanner: (props: AdminPasswordBannerProps) => <AdminPasswordBanner {...props} />,
|
||||
impersonationBanner: (props: ImpersonatingBannerProps) => <ImpersonatingBanner {...props} />,
|
||||
calendarCredentialBanner: (props: CalendarCredentialBannerProps) => <CalendarCredentialBanner {...props} />,
|
||||
};
|
||||
|
||||
function useRedirectToOnboardingIfNeeded() {
|
||||
const router = useRouter();
|
||||
|
@ -188,10 +191,41 @@ function useRedirectToOnboardingIfNeeded() {
|
|||
};
|
||||
}
|
||||
|
||||
type allBannerProps = { [Key in BannerType]: BannerTypeProps[Key]["data"] };
|
||||
|
||||
const useBanners = () => {
|
||||
const { data: getUserTopBanners, isLoading } = trpc.viewer.getUserTopBanners.useQuery();
|
||||
const { data: userSession } = useSession();
|
||||
|
||||
if (isLoading || !userSession) return null;
|
||||
|
||||
const isUserInactiveAdmin = userSession?.user.role === "INACTIVE_ADMIN";
|
||||
const userImpersonatedByUID = userSession?.user.impersonatedByUID;
|
||||
|
||||
const userSessionBanners = {
|
||||
adminPasswordBanner: isUserInactiveAdmin ? userSession : null,
|
||||
impersonationBanner: userImpersonatedByUID ? userSession : null,
|
||||
};
|
||||
|
||||
const allBanners: allBannerProps = Object.assign({}, getUserTopBanners, userSessionBanners);
|
||||
|
||||
return allBanners;
|
||||
};
|
||||
|
||||
const Layout = (props: LayoutProps) => {
|
||||
const [bannersHeight, setBannersHeight] = useState<number>(0);
|
||||
const banners = useBanners();
|
||||
|
||||
const pageTitle = typeof props.heading === "string" && !props.title ? props.heading : props.title;
|
||||
|
||||
const bannersHeight = useMemo(() => {
|
||||
const activeBanners =
|
||||
banners &&
|
||||
Object.entries(banners).filter(([_, value]) => {
|
||||
return value && (!Array.isArray(value) || value.length > 0);
|
||||
});
|
||||
return (activeBanners?.length ?? 0) * TOP_BANNER_HEIGHT;
|
||||
}, [banners]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{!props.withoutSeo && (
|
||||
|
@ -207,7 +241,32 @@ const Layout = (props: LayoutProps) => {
|
|||
{/* todo: only run this if timezone is different */}
|
||||
<TimezoneChangeDialog />
|
||||
<div className="flex min-h-screen flex-col">
|
||||
<AppTop setBannersHeight={setBannersHeight} />
|
||||
{banners && (
|
||||
<div className="sticky top-0 z-10 w-full divide-y divide-black">
|
||||
{Object.keys(banners).map((key) => {
|
||||
if (key === "teamUpgradeBanner") {
|
||||
const Banner = BannerComponent[key];
|
||||
return <Banner data={banners[key]} key={key} />;
|
||||
} else if (key === "orgUpgradeBanner") {
|
||||
const Banner = BannerComponent[key];
|
||||
return <Banner data={banners[key]} key={key} />;
|
||||
} else if (key === "verifyEmailBanner") {
|
||||
const Banner = BannerComponent[key];
|
||||
return <Banner data={banners[key]} key={key} />;
|
||||
} else if (key === "adminPasswordBanner") {
|
||||
const Banner = BannerComponent[key];
|
||||
return <Banner data={banners[key]} key={key} />;
|
||||
} else if (key === "impersonationBanner") {
|
||||
const Banner = BannerComponent[key];
|
||||
return <Banner data={banners[key]} key={key} />;
|
||||
} else if (key === "calendarCredentialBanner") {
|
||||
const Banner = BannerComponent[key];
|
||||
return <Banner data={banners[key]} key={key} />;
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex flex-1" data-testid="dashboard-shell">
|
||||
{props.SidebarContainer ? (
|
||||
cloneElement(props.SidebarContainer, { bannersHeight })
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { useSession } from "next-auth/react";
|
||||
import type { SessionContextValue } from "next-auth/react";
|
||||
import Link from "next/link";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { TopBanner } from "@calcom/ui";
|
||||
|
||||
function AdminPasswordBanner() {
|
||||
export type AdminPasswordBannerProps = { data: SessionContextValue["data"] };
|
||||
|
||||
function AdminPasswordBanner({ data }: AdminPasswordBannerProps) {
|
||||
const { t } = useLocale();
|
||||
const { data } = useSession();
|
||||
|
||||
if (data?.user.role !== "INACTIVE_ADMIN") return null;
|
||||
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
import Link from "next/link";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { type RouterOutputs } from "@calcom/trpc";
|
||||
import { TopBanner } from "@calcom/ui";
|
||||
|
||||
export type CalendarCredentialBannerProps = {
|
||||
data: RouterOutputs["viewer"]["getUserTopBanners"]["calendarCredentialBanner"];
|
||||
};
|
||||
|
||||
function CalendarCredentialBanner({ data }: CalendarCredentialBannerProps) {
|
||||
const { t } = useLocale();
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopBanner
|
||||
text={`${t("something_went_wrong")} ${t("calendar_error")}`}
|
||||
variant="error"
|
||||
actions={
|
||||
<Link href="/apps/installed/calendar" className="border-b border-b-black">
|
||||
{t("check_here")}
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default CalendarCredentialBanner;
|
|
@ -1,23 +1,21 @@
|
|||
import { useSession } from "next-auth/react";
|
||||
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc";
|
||||
import useEmailVerifyCheck from "@calcom/trpc/react/hooks/useEmailVerifyCheck";
|
||||
import { TopBanner, showToast } from "@calcom/ui";
|
||||
import { Mail } from "@calcom/ui/components/icon";
|
||||
|
||||
import { useFlagMap } from "../../flags/context/provider";
|
||||
|
||||
function VerifyEmailBanner() {
|
||||
export type VerifyEmailBannerProps = {
|
||||
data: boolean;
|
||||
};
|
||||
|
||||
function VerifyEmailBanner({ data }: VerifyEmailBannerProps) {
|
||||
const flags = useFlagMap();
|
||||
const { t } = useLocale();
|
||||
const { data, isLoading } = useEmailVerifyCheck();
|
||||
const mutation = trpc.viewer.auth.resendVerifyEmail.useMutation();
|
||||
const session = useSession();
|
||||
const isLoggedIn = session?.data?.user;
|
||||
|
||||
if (!isLoggedIn || isLoading || data?.isVerified || !flags["email-verification"]) return null;
|
||||
if (!data || !flags["email-verification"]) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -108,6 +108,8 @@ export const APP_CREDENTIAL_SHARING_ENABLED =
|
|||
export const DEFAULT_LIGHT_BRAND_COLOR = "#292929";
|
||||
export const DEFAULT_DARK_BRAND_COLOR = "#fafafa";
|
||||
|
||||
export const TOP_BANNER_HEIGHT = 40;
|
||||
|
||||
const defaultOnNaN = (testedValue: number, defaultValue: number) =>
|
||||
!Number.isNaN(testedValue) ? testedValue : defaultValue;
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ type AppsRouterHandlerCache = {
|
|||
getUsersDefaultConferencingApp?: typeof import("./getUsersDefaultConferencingApp.handler").getUsersDefaultConferencingAppHandler;
|
||||
updateUserDefaultConferencingApp?: typeof import("./updateUserDefaultConferencingApp.handler").updateUserDefaultConferencingAppHandler;
|
||||
teamsAndUserProfilesQuery?: typeof import("./teamsAndUserProfilesQuery.handler").teamsAndUserProfilesQuery;
|
||||
getUserTopBanners?: typeof import("./getUserTopBanners.handler").getUserTopBannersHandler;
|
||||
};
|
||||
|
||||
const UNSTABLE_HANDLER_CACHE: AppsRouterHandlerCache = {};
|
||||
|
@ -340,6 +341,21 @@ export const loggedInViewerRouter = router({
|
|||
return UNSTABLE_HANDLER_CACHE.getCalVideoRecordings({ ctx, input });
|
||||
}),
|
||||
|
||||
getUserTopBanners: authedProcedure.query(async ({ ctx }) => {
|
||||
if (!UNSTABLE_HANDLER_CACHE.getUserTopBanners) {
|
||||
UNSTABLE_HANDLER_CACHE.getUserTopBanners = (
|
||||
await import("./getUserTopBanners.handler")
|
||||
).getUserTopBannersHandler;
|
||||
}
|
||||
|
||||
// Unreachable code but required for type safety
|
||||
if (!UNSTABLE_HANDLER_CACHE.getUserTopBanners) {
|
||||
throw new Error("Failed to load handler");
|
||||
}
|
||||
|
||||
return UNSTABLE_HANDLER_CACHE.getUserTopBanners({ ctx });
|
||||
}),
|
||||
|
||||
getDownloadLinkOfCalVideoRecordings: authedProcedure
|
||||
.input(ZGetDownloadLinkOfCalVideoRecordingsInputSchema)
|
||||
.query(async ({ ctx, input }) => {
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
import { getCalendarCredentials, getConnectedCalendars } from "@calcom/core/CalendarManager";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import { credentialForCalendarServiceSelect } from "@calcom/prisma/selects/credential";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
|
||||
|
||||
import { checkIfOrgNeedsUpgradeHandler } from "../viewer/organizations/checkIfOrgNeedsUpgrade.handler";
|
||||
import { getUpgradeableHandler } from "../viewer/teams/getUpgradeable.handler";
|
||||
import { shouldVerifyEmailHandler } from "./shouldVerifyEmail.handler";
|
||||
|
||||
type Props = {
|
||||
ctx: {
|
||||
user: NonNullable<TrpcSessionUser>;
|
||||
};
|
||||
};
|
||||
|
||||
const checkInvalidGoogleCalendarCredentials = async ({ ctx }: Props) => {
|
||||
const userCredentials = await prisma.credential.findMany({
|
||||
where: {
|
||||
userId: ctx.user.id,
|
||||
type: "google_calendar",
|
||||
},
|
||||
select: credentialForCalendarServiceSelect,
|
||||
});
|
||||
|
||||
const calendarCredentials = getCalendarCredentials(userCredentials);
|
||||
|
||||
const { connectedCalendars } = await getConnectedCalendars(
|
||||
calendarCredentials,
|
||||
ctx.user.selectedCalendars,
|
||||
ctx.user.destinationCalendar?.externalId
|
||||
);
|
||||
|
||||
return connectedCalendars.some((calendar) => !!calendar.error);
|
||||
};
|
||||
|
||||
export const getUserTopBannersHandler = async ({ ctx }: Props) => {
|
||||
const upgradeableTeamMememberships = getUpgradeableHandler({ ctx });
|
||||
const upgradeableOrgMememberships = checkIfOrgNeedsUpgradeHandler({ ctx });
|
||||
const shouldEmailVerify = shouldVerifyEmailHandler({ ctx });
|
||||
const isInvalidCalendarCredential = checkInvalidGoogleCalendarCredentials({ ctx });
|
||||
|
||||
const [teamUpgradeBanner, orgUpgradeBanner, verifyEmailBanner, calendarCredentialBanner] =
|
||||
await Promise.allSettled([
|
||||
upgradeableTeamMememberships,
|
||||
upgradeableOrgMememberships,
|
||||
shouldEmailVerify,
|
||||
isInvalidCalendarCredential,
|
||||
]);
|
||||
|
||||
return {
|
||||
teamUpgradeBanner: teamUpgradeBanner.status === "fulfilled" ? teamUpgradeBanner.value : [],
|
||||
orgUpgradeBanner: orgUpgradeBanner.status === "fulfilled" ? orgUpgradeBanner.value : [],
|
||||
verifyEmailBanner: verifyEmailBanner.status === "fulfilled" ? !verifyEmailBanner.value.isVerified : false,
|
||||
calendarCredentialBanner:
|
||||
calendarCredentialBanner.status === "fulfilled" ? calendarCredentialBanner.value : false,
|
||||
};
|
||||
};
|
|
@ -1,6 +1,7 @@
|
|||
import classNames from "classnames";
|
||||
import type { ComponentType, ReactNode } from "react";
|
||||
|
||||
import { TOP_BANNER_HEIGHT } from "@calcom/lib/constants";
|
||||
import type { LucideIcon, LucideProps } from "@calcom/ui/components/icon";
|
||||
import { AlertTriangle, Info } from "@calcom/ui/components/icon";
|
||||
|
||||
|
@ -40,8 +41,9 @@ export function TopBanner(props: TopBannerProps) {
|
|||
return (
|
||||
<div
|
||||
data-testid="banner"
|
||||
style={{ minHeight: TOP_BANNER_HEIGHT }}
|
||||
className={classNames(
|
||||
"flex min-h-[40px] w-full items-start justify-between gap-8 px-4 py-2 text-center lg:items-center",
|
||||
"flex w-full items-start justify-between gap-8 px-4 py-2 text-center lg:items-center",
|
||||
variantClassName[variant]
|
||||
)}>
|
||||
<div className="flex flex-1 flex-col items-start justify-center gap-2 p-1 lg:flex-row lg:items-center">
|
||||
|
|
Loading…
Reference in New Issue
Block a user