* Various fixes * Remove tooltip * another string change * Fix width of custom event name modal to prevent overflow * Booking questions fixes * minor string fix * checkbox alignment * padding in reassign dialog * fix: update FormBuilder tests for checkbox UI changes - Updated test utilities to work with CheckboxField instead of BooleanToggleGroupField - Fixed badge expectations to check for 'optional' instead of 'required' - Updated dialog interaction methods to use checkbox.checked instead of button text - All 13 FormBuilder tests now pass successfully Fixes failing tests in PR #22727 caused by UI changes from toggle buttons to checkboxes for required field selection. Co-Authored-By: amit@cal.com <samit91848@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: amit@cal.com <samit91848@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
115 lines
4.1 KiB
TypeScript
115 lines
4.1 KiB
TypeScript
import { type TFunction } from "i18next";
|
|
import { useEffect, useState } from "react";
|
|
import type { Dispatch, SetStateAction } from "react";
|
|
|
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|
import { userMetadata } from "@calcom/prisma/zod-utils";
|
|
import { trpc } from "@calcom/trpc/react";
|
|
import type { RouterOutputs } from "@calcom/trpc/react";
|
|
import classNames from "@calcom/ui/classNames";
|
|
import { Button } from "@calcom/ui/components/button";
|
|
import { List } from "@calcom/ui/components/list";
|
|
|
|
import { AppConnectionItem } from "../components/AppConnectionItem";
|
|
import { StepConnectionLoader } from "../components/StepConnectionLoader";
|
|
|
|
interface ConnectedAppStepProps {
|
|
nextStep: () => void;
|
|
isPageLoading: boolean;
|
|
user: RouterOutputs["viewer"]["me"]["get"];
|
|
}
|
|
|
|
const ConnectedVideoStepInner = ({
|
|
t,
|
|
setAnyInstalledVideoApps,
|
|
user,
|
|
}: {
|
|
t: TFunction;
|
|
setAnyInstalledVideoApps: Dispatch<SetStateAction<boolean>>;
|
|
user: RouterOutputs["viewer"]["me"]["get"];
|
|
}) => {
|
|
const { data: queryConnectedVideoApps, isPending } = trpc.viewer.apps.integrations.useQuery({
|
|
variant: "conferencing",
|
|
onlyInstalled: false,
|
|
|
|
/**
|
|
* Both props together sort by most popular first, then by installed first.
|
|
* So, installed apps are always shown at the top, followed by remaining apps sorted by descending popularity.
|
|
*
|
|
* This is done because there could be not so popular app already installed by the admin(e.g. through Delegation Credential)
|
|
* and we want to show it at the top so that user can set it as default if he wants to.
|
|
*/
|
|
sortByMostPopular: true,
|
|
sortByInstalledFirst: true,
|
|
});
|
|
|
|
const hasAnyInstalledVideoApps = queryConnectedVideoApps?.items.some(
|
|
(item) => item.userCredentialIds.length > 0
|
|
);
|
|
|
|
useEffect(() => {
|
|
setAnyInstalledVideoApps(Boolean(hasAnyInstalledVideoApps));
|
|
}, [hasAnyInstalledVideoApps, setAnyInstalledVideoApps]);
|
|
|
|
if (isPending) {
|
|
return <StepConnectionLoader />;
|
|
}
|
|
|
|
const result = userMetadata.safeParse(user.metadata);
|
|
if (!result.success) {
|
|
return <StepConnectionLoader />;
|
|
}
|
|
const { data: metadata } = result;
|
|
const defaultConferencingApp = metadata?.defaultConferencingApp?.appSlug;
|
|
return (
|
|
<List className="bg-default border-subtle divide-subtle scroll-bar mx-1 max-h-[45vh] divide-y !overflow-y-scroll rounded-md border p-0 sm:mx-0">
|
|
{queryConnectedVideoApps?.items &&
|
|
queryConnectedVideoApps?.items.map((item) => {
|
|
if (item.slug === "daily-video") return null; // we dont want to show daily here as it is installed by default
|
|
return (
|
|
<li key={item.name}>
|
|
{item.name && item.logo && (
|
|
<AppConnectionItem
|
|
type={item.type}
|
|
title={item.name}
|
|
isDefault={item.slug === defaultConferencingApp}
|
|
description={item.description}
|
|
dependencyData={item.dependencyData}
|
|
logo={item.logo}
|
|
slug={item.slug}
|
|
installed={item.userCredentialIds.length > 0}
|
|
defaultInstall={!defaultConferencingApp && item.appData?.location?.linkType === "dynamic"}
|
|
/>
|
|
)}
|
|
</li>
|
|
);
|
|
})}
|
|
</List>
|
|
);
|
|
};
|
|
|
|
const ConnectedVideoStep = (props: ConnectedAppStepProps) => {
|
|
const { nextStep, isPageLoading, user } = props;
|
|
const { t } = useLocale();
|
|
const [hasAnyInstalledVideoApps, setAnyInstalledVideoApps] = useState(false);
|
|
return (
|
|
<>
|
|
<ConnectedVideoStepInner setAnyInstalledVideoApps={setAnyInstalledVideoApps} t={t} user={user} />
|
|
<Button
|
|
EndIcon="arrow-right"
|
|
data-testid="save-video-button"
|
|
className={classNames(
|
|
"text-inverted border-inverted bg-inverted mt-8 flex w-full flex-row justify-center rounded-md border p-2 text-center text-sm",
|
|
!hasAnyInstalledVideoApps ? "cursor-not-allowed opacity-20" : ""
|
|
)}
|
|
disabled={!hasAnyInstalledVideoApps}
|
|
loading={isPageLoading}
|
|
onClick={() => nextStep()}>
|
|
{t("set_availability")}
|
|
</Button>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export { ConnectedVideoStep };
|