From 99caae8665c6a7532ae1e63b0ed2ab83bb09a4fd Mon Sep 17 00:00:00 2001 From: Somay Chauhan Date: Thu, 10 Oct 2024 19:39:03 +0530 Subject: [PATCH] feat: advanced tab platform wrapper (#16941) * fixed error in advanced tab atom build -- used `DOMPurify` instead of `sanitize-html` * hide BookerLayoutSelector for platform and removePlatformClientIdFromEmail * fixup! Merge branch 'main' into event-advanced-platform-wrapper * undo linting changes * undo linting changes * fix: active state of tabs items --------- Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com> Co-authored-by: Morgan Vernay --- apps/api/v2/package.json | 2 +- apps/web/package.json | 2 + .../playwright/tests/basic.e2e.ts | 32 ++-- .../booking-to-mutation-input-mapper.tsx | 1 - .../handleNewBooking/loadAndValidateUsers.ts | 8 +- .../tabs/advanced/EventAdvancedTab.tsx | 140 +++++++++++------- .../features/form-builder/FormBuilder.tsx | 6 +- .../settings/BookerLayoutSelector.tsx | 4 +- packages/lib/fromEntriesWithDuplicateKeys.ts | 2 +- packages/lib/markdownToSafeHTMLClient.ts | 30 ++++ .../hooks/usePlatformTabsNavigations.tsx | 10 +- .../wrappers/EventAdvancedPlatformWrapper.tsx | 17 +++ .../wrappers/EventAdvancedWebWrapper.tsx | 17 +-- .../wrappers/EventTypePlatformWrapper.tsx | 20 ++- .../wrappers/EventTypeWebWrapper.tsx | 5 +- .../navigation/tabs/HorizontalTabItem.tsx | 10 +- .../navigation/tabs/VerticalTabItem.tsx | 3 +- 17 files changed, 208 insertions(+), 101 deletions(-) create mode 100644 packages/lib/markdownToSafeHTMLClient.ts create mode 100644 packages/platform/atoms/event-types/wrappers/EventAdvancedPlatformWrapper.tsx diff --git a/apps/api/v2/package.json b/apps/api/v2/package.json index 45cc6fb1e5..fd614d0666 100644 --- a/apps/api/v2/package.json +++ b/apps/api/v2/package.json @@ -28,7 +28,7 @@ "dependencies": { "@calcom/platform-constants": "*", "@calcom/platform-enums": "*", - "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.44", + "@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.45", "@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2", "@calcom/platform-types": "*", "@calcom/platform-utils": "*", diff --git a/apps/web/package.json b/apps/web/package.json index 4438fd41ac..33e182ba72 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -79,6 +79,7 @@ "bcp-47-match": "^2.0.3", "bcryptjs": "^2.4.3", "classnames": "^2.3.1", + "dompurify": "^3.1.7", "dotenv-cli": "^6.0.0", "entities": "^4.4.0", "eslint-config-next": "^13.2.1", @@ -152,6 +153,7 @@ "@types/async": "^3.2.15", "@types/bcryptjs": "^2.4.2", "@types/detect-port": "^1.3.2", + "@types/dompurify": "^3.0.5", "@types/glidejs__glide": "^3.4.2", "@types/lodash": "^4.14.182", "@types/markdown-it": "^12.2.3", diff --git a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts index 82080314ea..f23e2c3461 100644 --- a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts +++ b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts @@ -34,14 +34,20 @@ async function enableContactOwnerOverride(page: Page) { async function selectFirstAttributeOption({ fromLocator }: { fromLocator: Locator }) { await selectOptionUsingLocator({ locator: fromLocator, - option: 1 + option: 1, }); } -async function selectFirstValueForAttributeValue({ fromLocator, option }: { fromLocator: Locator, option: number }) { +async function selectFirstValueForAttributeValue({ + fromLocator, + option, +}: { + fromLocator: Locator; + option: number; +}) { await selectOptionUsingLocator({ locator: fromLocator, - option + option, }); } @@ -59,7 +65,7 @@ async function addAttributeRoutingRule(page: Page) { await selectFirstValueForAttributeValue({ fromLocator: attributeValueSelector, // Select 'Value of Field Short Text' option - option: numOfOptionsInAttribute + 1 + option: numOfOptionsInAttribute + 1, }); } @@ -571,7 +577,7 @@ test.describe("Routing Forms", () => { await page.click('[data-testid="test-preview"]'); await page.fill('[data-testid="form-field-short-text"]', "large"); await page.click('[data-testid="test-routing"]'); - await page.waitForSelector('text=@example.com'); + await page.waitForSelector("text=@example.com"); await page.click('[data-testid="dialog-rejection"]'); })(); @@ -579,9 +585,8 @@ test.describe("Routing Forms", () => { await page.click('[data-testid="test-preview"]'); await page.fill('[data-testid="form-field-short-text"]', "medium"); await page.click('[data-testid="test-routing"]'); - await page.waitForSelector('text=No matching members.'); + await page.waitForSelector("text=No matching members."); await page.click('[data-testid="dialog-rejection"]'); - })(); }); }); @@ -751,15 +756,12 @@ async function selectOption({ .click(); } -export async function selectOptionUsingLocator({ - locator, - option, -}: { - locator: Locator; - option: number; -}) { +export async function selectOptionUsingLocator({ locator, option }: { locator: Locator; option: number }) { await locator.click(); - await locator.locator('[id*="react-select-"][aria-disabled]').nth(option - 1).click(); + await locator + .locator('[id*="react-select-"][aria-disabled]') + .nth(option - 1) + .click(); } async function verifyFieldOptionsInRule(options: string[], page: Page) { diff --git a/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx b/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx index 1a80c0570d..d2ed62b7d5 100644 --- a/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx +++ b/packages/features/bookings/lib/book-event-form/booking-to-mutation-input-mapper.tsx @@ -2,7 +2,6 @@ import { v4 as uuidv4 } from "uuid"; import dayjs from "@calcom/dayjs"; import { getRoutedTeamMemberIdsFromSearchParams } from "@calcom/lib/bookings/getRoutedTeamMemberIdsFromSearchParams"; -import { fromEntriesWithDuplicateKeys } from "@calcom/lib/fromEntriesWithDuplicateKeys"; import { parseRecurringDates } from "@calcom/lib/parse-dates"; import type { BookerEvent, BookingCreateBody, RecurringBookingCreateBody } from "../../types"; diff --git a/packages/features/bookings/lib/handleNewBooking/loadAndValidateUsers.ts b/packages/features/bookings/lib/handleNewBooking/loadAndValidateUsers.ts index 6380ec7314..e51c5e5b6e 100644 --- a/packages/features/bookings/lib/handleNewBooking/loadAndValidateUsers.ts +++ b/packages/features/bookings/lib/handleNewBooking/loadAndValidateUsers.ts @@ -39,7 +39,13 @@ export async function loadAndValidateUsers({ routedTeamMemberIds, contactOwnerEmail, }: InputProps): Promise { - let users: Users = await loadUsers({ eventType, dynamicUserList, req, routedTeamMemberIds, contactOwnerEmail }); + let users: Users = await loadUsers({ + eventType, + dynamicUserList, + req, + routedTeamMemberIds, + contactOwnerEmail, + }); const isDynamicAllowed = !users.some((user) => !user.allowDynamicBooking); if (!isDynamicAllowed && !eventTypeId) { logger.warn({ diff --git a/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx b/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx index 08c1f89e86..fbf65ee3a0 100644 --- a/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx +++ b/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx @@ -1,9 +1,8 @@ -import dynamic from "next/dynamic"; import { useState } from "react"; import { Controller, useFormContext } from "react-hook-form"; import type { z } from "zod"; -import type { EventAdvancedWebWrapperProps } from "@calcom/atoms/event-types/wrappers/EventAdvancedWebWrapper"; +import { useAtomsContext, useIsPlatform } from "@calcom/atoms/monorepo"; import type { EventNameObjectType } from "@calcom/core/event"; import { getEventName } from "@calcom/core/event"; import getLocationsOptionsForSelect from "@calcom/features/bookings/lib/getLocationOptionsForSelect"; @@ -14,7 +13,7 @@ import { allowDisablingHostConfirmationEmails, } from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails"; import { MultiplePrivateLinksController } from "@calcom/features/eventtypes/components"; -import type { FormValues } from "@calcom/features/eventtypes/lib/types"; +import type { FormValues, EventTypeSetupProps } from "@calcom/features/eventtypes/lib/types"; import { FormBuilder } from "@calcom/features/form-builder/FormBuilder"; import type { fieldSchema } from "@calcom/features/form-builder/schema"; import type { EditableSchema } from "@calcom/features/form-builder/schema"; @@ -39,21 +38,26 @@ import { SettingsToggle, Switch, TextField, - showToast, ColorPicker, } from "@calcom/ui"; +import CustomEventTypeModal from "./CustomEventTypeModal"; import { DisableAllEmailsSetting } from "./DisableAllEmailsSetting"; import RequiresConfirmationController from "./RequiresConfirmationController"; type BookingField = z.infer; -const CustomEventTypeModal = dynamic(() => import("./CustomEventTypeModal")); - -export type EventAdvancedTabProps = EventAdvancedWebWrapperProps & { - calendarsQueryData?: RouterOutputs["viewer"]["connectedCalendars"]; - user?: RouterOutputs["viewer"]["me"]; +export type EventAdvancedBaseProps = Pick & { + user?: Partial< + Pick + >; isUserLoading?: boolean; + showToast: (message: string, variant: "success" | "warning" | "error") => void; +}; + +export type EventAdvancedTabProps = EventAdvancedBaseProps & { + calendarsQueryData?: RouterOutputs["viewer"]["connectedCalendars"]; + showBookerLayoutSelector: boolean; }; export const EventAdvancedTab = ({ @@ -62,7 +66,11 @@ export const EventAdvancedTab = ({ calendarsQueryData, user, isUserLoading, + showToast, + showBookerLayoutSelector, }: EventAdvancedTabProps) => { + const isPlatform = useIsPlatform(); + const platformContext = useAtomsContext(); const formMethods = useFormContext(); const { t } = useLocale(); const [showEventNameTip, setShowEventNameTip] = useState(false); @@ -169,7 +177,7 @@ export const EventAdvancedTab = ({ const displayDestinationCalendarSelector = !!calendarsQueryData?.connectedCalendars?.length && (!team || isChildrenManagedEventType); - const verifiedSecondaryEmails = [ + let verifiedSecondaryEmails = [ { label: user?.email || "", value: -1, @@ -178,6 +186,20 @@ export const EventAdvancedTab = ({ .filter((secondaryEmail) => !!secondaryEmail.emailVerified) .map((secondaryEmail) => ({ label: secondaryEmail.email, value: secondaryEmail.id })), ]; + + const removePlatformClientIdFromEmail = (email: string, clientId: string) => + email.replace(`+${clientId}`, ""); + + let userEmail = user?.email || ""; + + if (isPlatform && platformContext.clientId) { + verifiedSecondaryEmails = verifiedSecondaryEmails.map((email) => ({ + ...email, + label: removePlatformClientIdFromEmail(email.label, platformContext.clientId), + })); + userEmail = removePlatformClientIdFromEmail(userEmail, platformContext.clientId); + } + const selectedSecondaryEmailId = formMethods.getValues("secondaryEmailId") || -1; return (
@@ -257,7 +279,7 @@ export const EventAdvancedTab = ({ placeholder={ selectedSecondaryEmailId === -1 && ( - {t("default")} {user?.email || ""} + {t("default")} {userEmail} ) } @@ -275,13 +297,15 @@ export const EventAdvancedTab = ({ )}
- + {showBookerLayoutSelector && ( + + )}
)} /> - { - return ( - { - if (!e) { - formMethods.setValue("multiplePrivateLinks", [], { shouldDirty: true }); - } else { - formMethods.setValue( - "multiplePrivateLinks", - [generateHashedLink(formMethods.getValues("users")[0]?.id ?? team?.id)], - { shouldDirty: true } - ); - } - setMultiplePrivateLinksVisible(e); - }}> - {!isManagedEventType && ( -
- -
- )} -
- ); - }} - /> + {!isPlatform && ( + { + return ( + { + if (!e) { + formMethods.setValue("multiplePrivateLinks", [], { shouldDirty: true }); + } else { + formMethods.setValue( + "multiplePrivateLinks", + [generateHashedLink(formMethods.getValues("users")[0]?.id ?? team?.id)], + { shouldDirty: true } + ); + } + setMultiplePrivateLinksVisible(e); + }}> + {!isManagedEventType && ( +
+ +
+ )} +
+ ); + }} + /> + )} ( diff --git a/packages/features/form-builder/FormBuilder.tsx b/packages/features/form-builder/FormBuilder.tsx index 0bc8f52b20..0abe51b487 100644 --- a/packages/features/form-builder/FormBuilder.tsx +++ b/packages/features/form-builder/FormBuilder.tsx @@ -7,7 +7,7 @@ import type { z } from "zod"; import { classNames } from "@calcom/lib"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { md } from "@calcom/lib/markdownIt"; -import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; +import { markdownToSafeHTMLClient } from "@calcom/lib/markdownToSafeHTMLClient"; import turndown from "@calcom/lib/turndownService"; import { Badge, @@ -153,7 +153,7 @@ export const FormBuilder = function FormBuilder({ } if (fieldsThatSupportLabelAsSafeHtml.includes(field.type)) { - field = { ...field, labelAsSafeHtml: markdownToSafeHTML(field.label ?? "") }; + field = { ...field, labelAsSafeHtml: markdownToSafeHTMLClient(field.label ?? "") }; } const numOptions = options?.length ?? 0; const firstOptionInput = @@ -692,7 +692,7 @@ function FieldLabel({ field }: { field: RhfFormField }) { ); diff --git a/packages/features/settings/BookerLayoutSelector.tsx b/packages/features/settings/BookerLayoutSelector.tsx index e1c3f0b02c..5686dd6e09 100644 --- a/packages/features/settings/BookerLayoutSelector.tsx +++ b/packages/features/settings/BookerLayoutSelector.tsx @@ -34,7 +34,7 @@ type BookerLayoutSelectorProps = { isLoading?: boolean; isDisabled?: boolean; isOuterBorder?: boolean; - user?: RouterOutputs["viewer"]["me"]; + user?: Partial>; isUserLoading?: boolean; }; @@ -104,7 +104,7 @@ type BookerLayoutFieldsProps = { showUserSettings: boolean; isDark?: boolean; isOuterBorder?: boolean; - user?: RouterOutputs["viewer"]["me"]; + user?: Partial>; isUserLoading?: boolean; }; diff --git a/packages/lib/fromEntriesWithDuplicateKeys.ts b/packages/lib/fromEntriesWithDuplicateKeys.ts index 6cc389215f..11bf425fd2 100644 --- a/packages/lib/fromEntriesWithDuplicateKeys.ts +++ b/packages/lib/fromEntriesWithDuplicateKeys.ts @@ -21,4 +21,4 @@ export function fromEntriesWithDuplicateKeys(entries: IterableIterator<[string, } } return result; -} \ No newline at end of file +} diff --git a/packages/lib/markdownToSafeHTMLClient.ts b/packages/lib/markdownToSafeHTMLClient.ts new file mode 100644 index 0000000000..a377608f60 --- /dev/null +++ b/packages/lib/markdownToSafeHTMLClient.ts @@ -0,0 +1,30 @@ +import DOMPurify from "dompurify"; + +import { md } from "@calcom/lib/markdownIt"; + +if (typeof window == "undefined") { + console.warn( + "`markdownToSafeHTMLClient` should not be used on the server side. use markdownToSafeHTML instead" + ); +} + +export function markdownToSafeHTMLClient(markdown: string | null) { + if (!markdown) return ""; + + const html = md.render(markdown); + + const safeHTML = DOMPurify.sanitize(html); + + const safeHTMLWithListFormatting = safeHTML + .replace( + /
    /g, + "
      " + ) + .replace( + /
        /g, + "
          " + ) + .replace(/ setCurrentTab("recurring"), + isActive: currentTab === "recurring", href: `${url}?tabName=recurring`, icon: "repeat", info: `recurring_event_tab_description`, @@ -75,6 +77,7 @@ export const usePlatformTabsNavigations = ({ formMethods, eventType, team, tabs navigation.splice(1, 0, { name: "availability", onClick: () => setCurrentTab("availability"), + isActive: currentTab === "availability", href: `${url}?tabName=availability`, icon: "calendar", info: @@ -96,6 +99,7 @@ export const usePlatformTabsNavigations = ({ formMethods, eventType, team, tabs navigation.splice(2, 0, { name: "assignment", onClick: () => setCurrentTab("team"), + isActive: currentTab === "team", href: `${url}?tabName=team`, icon: "users", info: `${t(watchSchedulingType?.toLowerCase() ?? "")}${ @@ -133,15 +137,17 @@ type getNavigationProps = { tabs: PlatformTabs[]; url: string; onClick: (tab: PlatformTabs) => void; + currentTab: PlatformTabs; }; -function getNavigation({ length, multipleDuration, t, tabs, url, onClick }: getNavigationProps) { +function getNavigation({ length, multipleDuration, t, tabs, url, onClick, currentTab }: getNavigationProps) { const duration = multipleDuration?.map((duration) => ` ${duration}`) || length; const tabsNavigation: VerticalTabItemProps[] = []; tabs.includes("setup") && tabsNavigation.push({ name: "event_setup_tab_title", onClick: () => onClick("setup"), + isActive: currentTab === "setup", href: `${url}?tabName=setup`, icon: "link", info: `${duration} ${t("minute_timeUnit")}`, // TODO: Get this from props @@ -150,6 +156,7 @@ function getNavigation({ length, multipleDuration, t, tabs, url, onClick }: getN tabsNavigation.push({ name: "event_limit_tab_title", onClick: () => onClick("limits"), + isActive: currentTab === "limits", href: `${url}?tabName=limits`, icon: "clock", info: `event_limit_tab_description`, @@ -159,6 +166,7 @@ function getNavigation({ length, multipleDuration, t, tabs, url, onClick }: getN tabsNavigation.push({ name: "event_advanced_tab_title", onClick: () => onClick("advanced"), + isActive: currentTab === "advanced", href: `${url}?tabName=advanced`, icon: "sliders-vertical", info: `event_advanced_tab_description`, diff --git a/packages/platform/atoms/event-types/wrappers/EventAdvancedPlatformWrapper.tsx b/packages/platform/atoms/event-types/wrappers/EventAdvancedPlatformWrapper.tsx new file mode 100644 index 0000000000..b669b030c4 --- /dev/null +++ b/packages/platform/atoms/event-types/wrappers/EventAdvancedPlatformWrapper.tsx @@ -0,0 +1,17 @@ +import type { EventAdvancedBaseProps } from "@calcom/features/eventtypes/components/tabs/advanced/EventAdvancedTab"; +import { EventAdvancedTab } from "@calcom/features/eventtypes/components/tabs/advanced/EventAdvancedTab"; + +import { useConnectedCalendars } from "../../hooks/useConnectedCalendars"; + +const EventAdvancedPlatformWrapper = (props: EventAdvancedBaseProps) => { + const { data: connectedCalendarsQuery } = useConnectedCalendars({}); + return ( + + ); +}; + +export default EventAdvancedPlatformWrapper; diff --git a/packages/platform/atoms/event-types/wrappers/EventAdvancedWebWrapper.tsx b/packages/platform/atoms/event-types/wrappers/EventAdvancedWebWrapper.tsx index 0fa9f2da68..8b38718c74 100644 --- a/packages/platform/atoms/event-types/wrappers/EventAdvancedWebWrapper.tsx +++ b/packages/platform/atoms/event-types/wrappers/EventAdvancedWebWrapper.tsx @@ -1,25 +1,14 @@ +import type { EventAdvancedBaseProps } from "@calcom/features/eventtypes/components/tabs/advanced/EventAdvancedTab"; import { EventAdvancedTab } from "@calcom/features/eventtypes/components/tabs/advanced/EventAdvancedTab"; -import type { EventTypeSetupProps } from "@calcom/features/eventtypes/lib/types"; -import type { RouterOutputs } from "@calcom/trpc/react"; import { trpc } from "@calcom/trpc/react"; -export type EventAdvancedWebWrapperProps = Pick & { - loggedInUser?: RouterOutputs["viewer"]["me"]; - isLoggedInUserPending?: boolean; -}; - -const EventAdvancedWebWrapper = ({ - loggedInUser, - isLoggedInUserPending, - ...props -}: EventAdvancedWebWrapperProps) => { +const EventAdvancedWebWrapper = ({ ...props }: EventAdvancedBaseProps) => { const connectedCalendarsQuery = trpc.viewer.connectedCalendars.useQuery(); return ( ); }; diff --git a/packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx b/packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx index 9dbd993b7b..9eaff311d4 100644 --- a/packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx +++ b/packages/platform/atoms/event-types/wrappers/EventTypePlatformWrapper.tsx @@ -10,6 +10,7 @@ import type { EventTypeSetupProps, FormValues, TabMap } from "@calcom/features/e import { useLocale } from "@calcom/lib/hooks/useLocale"; import { SchedulingType } from "@calcom/prisma/enums"; +import { useMe } from "../../hooks/useMe"; import { AtomsWrapper } from "../../src/components/atoms-wrapper"; import { useToast } from "../../src/components/ui/use-toast"; import { useAtomsEventTypeById, QUERY_KEY as ATOM_EVENT_TYPE_QUERY_KEY } from "../hooks/useAtomEventTypeById"; @@ -17,6 +18,7 @@ import { useAtomUpdateEventType } from "../hooks/useAtomUpdateEventType"; import { useEventTypeForm } from "../hooks/useEventTypeForm"; import { useHandleRouteChange } from "../hooks/useHandleRouteChange"; import { usePlatformTabsNavigations } from "../hooks/usePlatformTabsNavigations"; +import EventAdvancedPlatformWrapper from "./EventAdvancedPlatformWrapper"; import EventLimitsTabPlatformWrapper from "./EventLimitsTabPlatformWrapper"; import EventRecurringTabPlatformWrapper from "./EventRecurringTabPlatformWrapper"; import SetupTab from "./EventSetupTabPlatformWrapper"; @@ -42,6 +44,7 @@ const EventType = ({ const [pendingRoute, setPendingRoute] = useState(""); const { eventType, locationOptions, team, teamMembers, destinationCalendar } = props; const [slugExistsChildrenDialogOpen, setSlugExistsChildrenDialogOpen] = useState([]); + const { data: user, isLoading: isUserLoading } = useMe(); const updateMutation = useAtomUpdateEventType({ onSuccess: async () => { @@ -73,6 +76,10 @@ const EventType = ({ const { form, handleSubmit } = useEventTypeForm({ eventType, onSubmit: updateMutation.mutate }); const slug = form.watch("slug") ?? eventType.slug; + const showToast = (message: string, variant: "success" | "warning" | "error") => { + toast({ description: message }); + }; + const tabMap = { setup: tabs.includes("setup") ? ( , team: <>, + advanced: tabs.includes("advanced") ? ( + + ) : ( + <> + ), + limits: tabs.includes("limits") ? : <>, - advanced: <>, instant: <>, recurring: tabs.includes("recurring") ? ( diff --git a/packages/platform/atoms/event-types/wrappers/EventTypeWebWrapper.tsx b/packages/platform/atoms/event-types/wrappers/EventTypeWebWrapper.tsx index 1ddbfdbe2b..6ea31053a6 100644 --- a/packages/platform/atoms/event-types/wrappers/EventTypeWebWrapper.tsx +++ b/packages/platform/atoms/event-types/wrappers/EventTypeWebWrapper.tsx @@ -250,8 +250,9 @@ const EventTypeWeb = ({ ), instant: , diff --git a/packages/ui/components/navigation/tabs/HorizontalTabItem.tsx b/packages/ui/components/navigation/tabs/HorizontalTabItem.tsx index f00b33689a..7d2ba86a3d 100644 --- a/packages/ui/components/navigation/tabs/HorizontalTabItem.tsx +++ b/packages/ui/components/navigation/tabs/HorizontalTabItem.tsx @@ -18,6 +18,8 @@ export type HorizontalTabItemProps = { linkScroll?: boolean; icon?: IconName; avatar?: string; + onClick?: (name: string) => void; + isActive?: boolean; }; const HorizontalTabItem = function ({ @@ -30,10 +32,16 @@ const HorizontalTabItem = function ({ }: HorizontalTabItemProps) { const { t, isLocaleReady } = useLocale(); - const isCurrent = useUrlMatchesCurrentUrl(href); + const isCurrent = useUrlMatchesCurrentUrl(href) || props?.isActive; return ( { + if (props.onClick) { + e.preventDefault(); + props.onClick(name); + } + }} key={name} href={href} shallow={linkShallow} diff --git a/packages/ui/components/navigation/tabs/VerticalTabItem.tsx b/packages/ui/components/navigation/tabs/VerticalTabItem.tsx index 8754925813..4085511674 100644 --- a/packages/ui/components/navigation/tabs/VerticalTabItem.tsx +++ b/packages/ui/components/navigation/tabs/VerticalTabItem.tsx @@ -26,6 +26,7 @@ export type VerticalTabItemProps = { avatar?: string; iconClassName?: string; onClick?: (name: string) => void; + isActive?: boolean; }; const VerticalTabItem = ({ @@ -39,7 +40,7 @@ const VerticalTabItem = ({ ...props }: VerticalTabItemProps) => { const { t } = useLocale(); - const isCurrent = useUrlMatchesCurrentUrl(href); + const isCurrent = useUrlMatchesCurrentUrl(href) || props?.isActive; return (