diff --git a/apps/web/components/App.tsx b/apps/web/components/App.tsx index 5783747e9b..5e1b89537b 100644 --- a/apps/web/components/App.tsx +++ b/apps/web/components/App.tsx @@ -8,14 +8,13 @@ import { } from "@heroicons/react/outline"; import { ChevronLeftIcon } from "@heroicons/react/solid"; import Link from "next/link"; -import React from "react"; +import React, { useEffect, useState } from "react"; import { InstallAppButton } from "@calcom/app-store/components"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { App as AppType } from "@calcom/types/App"; import { Button } from "@calcom/ui"; -//import NavTabs from "@components/NavTabs"; import Shell from "@components/Shell"; import Badge from "@components/ui/Badge"; @@ -60,7 +59,29 @@ export default function App({ currency: "USD", useGrouping: false, }).format(price); - + const [installedApp, setInstalledApp] = useState(false); + useEffect(() => { + async function getInstalledApp(appCredentialType: string) { + const queryParam = new URLSearchParams(); + queryParam.set("app-credential-type", appCredentialType); + try { + const result = await fetch(`/api/app-store/installed?${queryParam.toString()}`, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + if (result.status === 200) { + setInstalledApp(true); + } + } catch (error) { + if (error instanceof Error) { + console.log(error.message); + } + } + } + getInstalledApp(type); + }, []); return ( <> @@ -83,7 +104,7 @@ export default function App({
- {isGlobal ? ( + {isGlobal || installedApp ? ( diff --git a/apps/web/pages/api/app-store/installed.ts b/apps/web/pages/api/app-store/installed.ts new file mode 100644 index 0000000000..77aa3b1a57 --- /dev/null +++ b/apps/web/pages/api/app-store/installed.ts @@ -0,0 +1,34 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +import { getSession } from "@lib/auth"; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + req.session = await getSession({ req }); + if (req.method === "GET" && req.session && req.session.user.id && req.query) { + const { "app-credential-type": appCredentialType } = req.query; + if (!appCredentialType && Array.isArray(appCredentialType)) { + return res.status(400); + } + + const userId = req.session.user.id; + try { + const installedApp = await prisma?.credential.findFirst({ + where: { + type: appCredentialType as string, + userId: userId, + }, + }); + + if (installedApp && !!installedApp.key) { + res.status(200); + } else { + res.status(404); + } + } catch (error) { + res.status(500); + } + } else { + res.status(400); + } + res.end(); +} diff --git a/apps/web/pages/bookings/[status].tsx b/apps/web/pages/bookings/[status].tsx index fe187fc582..f622f2b8c7 100644 --- a/apps/web/pages/bookings/[status].tsx +++ b/apps/web/pages/bookings/[status].tsx @@ -46,7 +46,7 @@ export default function Bookings() { return ( - +
diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index f41566de61..5bbb0f9fc5 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -69,7 +69,7 @@ "request_reschedule_title_attendee": "Request to reschedule your booking", "request_reschedule_subtitle": "{{organizer}} has cancelled the booking and requested you to pick another time.", "request_reschedule_title_organizer": "You have requested {{attendee}} to reschedule", - "request_reschedule_subtitle_organizer": "You have cancelled the booking and {{attendee}} should be pick a new booking time with you.", + "request_reschedule_subtitle_organizer": "You have cancelled the booking and {{attendee}} should pick a new booking time with you.", "reschedule_reason": "Reason for reschedule", "hi_user_name": "Hi {{name}}", "ics_event_title": "{{eventType}} with {{name}}", @@ -89,7 +89,7 @@ "meeting_url": "Meeting URL", "meeting_request_rejected": "Your meeting request has been rejected", "rescheduled_event_type_subject": "Rescheduled: {{eventType}} with {{name}} at {{date}}", - "requested_to_reschedule_subject_attendee": "Action Required Reschedule: Please book a new to time for {{eventType}} with {{name}}", + "requested_to_reschedule_subject_attendee": "Action Required Reschedule: Please book a new time for {{eventType}} with {{name}}", "rejected_event_type_with_organizer": "Rejected: {{eventType}} with {{organizer}} on {{date}}", "hi": "Hi", "join_team": "Join team", @@ -735,7 +735,7 @@ "personal_note": "Name this key", "personal_note_placeholder": "E.g. Development", "api_key_no_note": "Nameless API key", - "api_key_never_expires":"This API key has no expiration date", + "api_key_never_expires": "This API key has no expiration date", "edit_api_key": "Edit API key", "never_expire_key": "Never expires", "delete_api_key": "Revoke API key", diff --git a/packages/app-store/_utils/useAddAppMutation.ts b/packages/app-store/_utils/useAddAppMutation.ts index f815e96f3d..20b29a4c22 100644 --- a/packages/app-store/_utils/useAddAppMutation.ts +++ b/packages/app-store/_utils/useAddAppMutation.ts @@ -12,10 +12,13 @@ function useAddAppMutation(type: App["type"], options?: Parameters { - const { trpc } = props; - + const { trpc, bookingsEmpty, bookingStatus } = props; const [openDialog, setOpenDialog] = useState(false); const { isSuccess, isLoading, data } = trpc.useQuery(["viewer.integrations"]); + if (bookingStatus !== "upcoming" || bookingsEmpty) { + return <>; + } + const wipeMyCalCredentials: { credentialIds: number[] } = data?.other?.items.find( + (item: { type: string }) => item.type === "wipemycal_other" + ); + + const [credentialId] = wipeMyCalCredentials?.credentialIds || [false]; + return (
- {data && - isSuccess && - !isLoading && - data?.other?.items.find((item: { type: string }) => item.type === "wipemycal_other") && ( - <> - - - - )} + {data && isSuccess && !isLoading && credentialId && ( + <> + + + + )}
); };