import { usePathname } from "next/navigation"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation"; import { InstallAppButton } from "@calcom/app-store/components"; import { doesAppSupportTeamInstall } from "@calcom/app-store/utils"; import { Spinner } from "@calcom/features/calendars/weeklyview/components/spinner/Spinner"; import type { UserAdminTeams } from "@calcom/features/ee/teams/lib/getUserAdminTeams"; import classNames from "@calcom/lib/classNames"; import { CAL_URL } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { AppFrontendPayload as App } from "@calcom/types/App"; import type { CredentialFrontendPayload as Credential } from "@calcom/types/Credential"; import type { ButtonProps } from "@calcom/ui"; import { Dropdown, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuPortal, DropdownMenuLabel, DropdownItem, Avatar, Badge, } from "@calcom/ui"; import { Button } from "../button"; import { Plus } from "../icon"; import { showToast } from "../toast"; interface AppCardProps { app: App; credentials?: Credential[]; searchText?: string; userAdminTeams?: UserAdminTeams; } export function AppCard({ app, credentials, searchText, userAdminTeams }: AppCardProps) { const { t } = useLocale(); const allowedMultipleInstalls = app.categories && app.categories.indexOf("calendar") > -1; const appAdded = (credentials && credentials.length) || 0; const enabledOnTeams = doesAppSupportTeamInstall(app.categories, app.concurrentMeetings); const appInstalled = enabledOnTeams && userAdminTeams ? userAdminTeams.length < appAdded : appAdded > 0; const [searchTextIndex, setSearchTextIndex] = useState(undefined); useEffect(() => { setSearchTextIndex(searchText ? app.name.toLowerCase().indexOf(searchText.toLowerCase()) : undefined); }, [app.name, searchText]); return (
{`${app.name}

{searchTextIndex != undefined && searchText ? ( <> {app.name.substring(0, searchTextIndex)} {app.name.substring(searchTextIndex, searchTextIndex + searchText.length)} {app.name.substring(searchTextIndex + searchText.length)} ) : ( app.name )}

{/* TODO: add reviews
{props.rating} stars {props.reviews} reviews
*/}

{app.description}

{app.isGlobal || (credentials && credentials.length > 0 && allowedMultipleInstalls) ? !app.isGlobal && ( !data.installed)} wrapperClassName="[@media(max-width:260px)]:w-full" render={({ useDefaultComponent, ...props }) => { if (useDefaultComponent) { props = { ...props, }; } return ( ); }} /> ) : credentials && !appInstalled && ( !data.installed)} teamsPlanRequired={app.teamsPlanRequired} render={({ useDefaultComponent, ...props }) => { if (useDefaultComponent) { props = { ...props, disabled: !!props.disabled, }; } return ( ); }} /> )}
{appInstalled ? {t("installed", { count: appAdded })} : null} {app.isTemplate && ( Template )} {(app.isDefault || (!app.isDefault && app.isGlobal)) && ( {t("default")} )}
); } const InstallAppButtonChild = ({ userAdminTeams, addAppMutationInput, appCategories, credentials, concurrentMeetings, ...props }: { userAdminTeams?: UserAdminTeams; addAppMutationInput: { type: App["type"]; variant: string; slug: string }; appCategories: string[]; credentials?: Credential[]; concurrentMeetings?: boolean; } & ButtonProps) => { const { t } = useLocale(); const router = useRouter(); const pathname = usePathname(); const mutation = useAddAppMutation(null, { onSuccess: (data) => { // Refresh SSR page content without actual reload if (pathname !== null) { router.replace(pathname); } if (data?.setupPending) return; showToast(t("app_successfully_installed"), "success"); }, onError: (error) => { if (error instanceof Error) showToast(error.message || t("app_could_not_be_installed"), "error"); }, }); if (!userAdminTeams?.length || !doesAppSupportTeamInstall(appCategories, concurrentMeetings)) { return ( ); } return ( { if (mutation.isLoading) event.preventDefault(); }}> {mutation.isLoading && (
)} {t("install_app_on")} {userAdminTeams.map((team) => { const isInstalledTeamOrUser = credentials && credentials.some((credential) => credential?.teamId ? credential?.teamId === team.id : credential.userId === team.id ); return ( ( )} onClick={() => { mutation.mutate( team.isUser ? addAppMutationInput : { ...addAppMutationInput, teamId: team.id } ); }}>

{t(team.name)} {isInstalledTeamOrUser && `(${t("installed")})`}

); })}
); };