diff --git a/apps/web/components/getting-started/components/AppConnectionItem.tsx b/apps/web/components/getting-started/components/AppConnectionItem.tsx
index cdaf308ee6..579a52a2a2 100644
--- a/apps/web/components/getting-started/components/AppConnectionItem.tsx
+++ b/apps/web/components/getting-started/components/AppConnectionItem.tsx
@@ -1,6 +1,11 @@
+import Link from "next/link";
+
+import type { TDependencyData } from "@calcom/app-store/_appRegistry";
import { InstallAppButtonWithoutPlanCheck } from "@calcom/app-store/components";
+import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { App } from "@calcom/types/App";
+import { Icon } from "@calcom/ui";
import { Button } from "@calcom/ui";
interface IAppConnectionItem {
@@ -9,11 +14,13 @@ interface IAppConnectionItem {
logo: string;
type: App["type"];
installed?: boolean;
+ dependencyData?: TDependencyData;
}
const AppConnectionItem = (props: IAppConnectionItem) => {
const { title, logo, type, installed } = props;
const { t } = useLocale();
+ const dependency = props.dependencyData?.find((data) => !data.installed);
return (
@@ -26,9 +33,43 @@ const AppConnectionItem = (props: IAppConnectionItem) => {
+ ) : undefined
+ }
onClick={(event) => {
// Save cookie key to return url step
document.cookie = `return-to=${window.location.href};path=/;max-age=3600;SameSite=Lax`;
diff --git a/apps/web/components/getting-started/steps-views/ConnectedVideoStep.tsx b/apps/web/components/getting-started/steps-views/ConnectedVideoStep.tsx
index 5be304eb28..0789555d7b 100644
--- a/apps/web/components/getting-started/steps-views/ConnectedVideoStep.tsx
+++ b/apps/web/components/getting-started/steps-views/ConnectedVideoStep.tsx
@@ -37,6 +37,7 @@ const ConnectedVideoStep = (props: ConnectedAppStepProps) => {
type={item.type}
title={item.name}
description={item.description}
+ dependencyData={item.dependencyData}
logo={item.logo}
installed={item.userCredentialIds.length > 0}
/>
diff --git a/packages/app-store/_appRegistry.ts b/packages/app-store/_appRegistry.ts
index a6b3d9d73a..0999a76e53 100644
--- a/packages/app-store/_appRegistry.ts
+++ b/packages/app-store/_appRegistry.ts
@@ -7,6 +7,11 @@ import { userMetadata } from "@calcom/prisma/zod-utils";
import type { AppFrontendPayload as App } from "@calcom/types/App";
import type { CredentialFrontendPayload as Credential } from "@calcom/types/Credential";
+export type TDependencyData = {
+ name?: string;
+ installed?: boolean;
+}[];
+
/**
* Get App metdata either using dirName or slug
*/
@@ -96,10 +101,7 @@ export async function getAppRegistryWithCredentials(userId: number, userAdminTea
/* This is now handled from the DB */
// if (!app.installed) return apps;
app.createdAt = dbapp.createdAt.toISOString();
- let dependencyData: {
- name?: string;
- installed?: boolean;
- }[] = [];
+ let dependencyData: TDependencyData = [];
if (app.dependencies) {
dependencyData = app.dependencies.map((dependency) => {
const dependencyInstalled = dbApps.some(
diff --git a/packages/trpc/server/routers/loggedInViewer/integrations.handler.ts b/packages/trpc/server/routers/loggedInViewer/integrations.handler.ts
index 7f2bd190bb..ac9b06944c 100644
--- a/packages/trpc/server/routers/loggedInViewer/integrations.handler.ts
+++ b/packages/trpc/server/routers/loggedInViewer/integrations.handler.ts
@@ -1,7 +1,9 @@
import type { Prisma } from "@prisma/client";
import appStore from "@calcom/app-store";
+import type { TDependencyData } from "@calcom/app-store/_appRegistry";
import type { CredentialOwner } from "@calcom/app-store/types";
+import { getAppFromSlug } from "@calcom/app-store/utils";
import getEnabledAppsFromCredentials from "@calcom/lib/apps/getEnabledAppsFromCredentials";
import getInstallCountPerApp from "@calcom/lib/apps/getInstallCountPerApp";
import { getUsersCredentials } from "@calcom/lib/server/getUsersCredentials";
@@ -179,6 +181,18 @@ export const integrationsHandler = async ({ ctx, input }: IntegrationsOptions) =
}
}
+ let dependencyData: TDependencyData = [];
+ if (app.dependencies?.length) {
+ dependencyData = app.dependencies.map((dependency) => {
+ const dependencyInstalled = enabledApps.some(
+ (dbAppIterator) => dbAppIterator.credentials.length && dbAppIterator.slug === dependency
+ );
+ // If the app marked as dependency is simply deleted from the codebase, we can have the situation where App is marked installed in DB but we couldn't get the app.
+ const dependencyName = getAppFromSlug(dependency)?.name;
+ return { name: dependencyName, installed: dependencyInstalled };
+ });
+ }
+
return {
...app,
...(teams.length && {
@@ -189,6 +203,7 @@ export const integrationsHandler = async ({ ctx, input }: IntegrationsOptions) =
teams,
isInstalled: !!userCredentialIds.length || !!teams.length || app.isGlobal,
isSetupAlready,
+ ...(app.dependencies && { dependencyData }),
};
})
);
diff --git a/packages/ui/components/button/Button.tsx b/packages/ui/components/button/Button.tsx
index 1a0495438b..4faa22569f 100644
--- a/packages/ui/components/button/Button.tsx
+++ b/packages/ui/components/button/Button.tsx
@@ -22,7 +22,7 @@ export type ButtonBaseProps = {
EndIcon?: IconName;
shallow?: boolean;
/**Tool tip used when icon size is set to small */
- tooltip?: string;
+ tooltip?: string | React.ReactNode;
tooltipSide?: "top" | "right" | "bottom" | "left";
tooltipOffset?: number;
disabled?: boolean;
@@ -240,7 +240,7 @@ const Wrapper = ({
tooltipSide,
tooltipOffset,
}: {
- tooltip?: string;
+ tooltip?: string | React.ReactNode;
children: React.ReactNode;
tooltipSide?: "top" | "right" | "bottom" | "left";
tooltipOffset?: number;