From 868cf91c35a3ac99d03d7fed58a3ed87f4f42334 Mon Sep 17 00:00:00 2001 From: Somay Chauhan Date: Mon, 25 Nov 2024 13:23:38 +0530 Subject: [PATCH] refactor: conferencing apps atoms web wrapper (#17732) * conferencing apps atoms web wrapper * Update DisconnectIntegrationModal.tsx * formating changes * handleBulkUpdateDefaultLocation -> bulkUpdateFunction * fix: type error for handleRemoveApp * fix: type error for handleRemoveApp * refactor: removed ConferencingView component and migrated its functionality to ConferencingAppsViewWebWrapper * refactor: moved AppList, AppListCard to packages/features * fix: type changes --------- Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com> --- .../my-account/conferencing/page.tsx | 21 +- .../components/apps/CalendarListContainer.tsx | 2 +- .../[category]/installed-category-view.tsx | 82 +++++++- .../availability/[schedule]/schedule-view.tsx | 24 ++- .../availability/availability-view.tsx | 24 ++- .../settings/my-account/conferencing-view.tsx | 100 ---------- packages/features/apps/AdminAppsList.tsx | 3 +- .../features/apps/components}/AppList.tsx | 45 ++--- .../features/apps}/components/AppListCard.tsx | 11 +- .../components/DisconnectIntegrationModal.tsx | 23 +-- .../BulkEditDefaultForEventsModal.tsx | 9 +- .../availability/AvailabilitySettings.tsx | 3 +- .../ConferencingAppsViewWebWrapper.tsx | 187 ++++++++++++++++++ packages/platform/atoms/monorepo.ts | 1 + .../SelectedCalendarsSettingsWebWrapper.tsx | 2 +- .../components/app-list-card/AppListCard.tsx | 3 +- 16 files changed, 354 insertions(+), 186 deletions(-) delete mode 100644 apps/web/modules/settings/my-account/conferencing-view.tsx rename {apps/web/components/apps => packages/features/apps/components}/AppList.tsx (86%) rename {apps/web => packages/features/apps}/components/AppListCard.tsx (85%) create mode 100644 packages/platform/atoms/connect/conferencing-apps/ConferencingAppsViewWebWrapper.tsx diff --git a/apps/web/app/settings/(settings-layout)/my-account/conferencing/page.tsx b/apps/web/app/settings/(settings-layout)/my-account/conferencing/page.tsx index dbfe964287..7d65fdf452 100644 --- a/apps/web/app/settings/(settings-layout)/my-account/conferencing/page.tsx +++ b/apps/web/app/settings/(settings-layout)/my-account/conferencing/page.tsx @@ -1,11 +1,8 @@ import { _generateMetadata } from "app/_utils"; import { getFixedT } from "app/_utils"; +import { ConferencingAppsViewWebWrapper } from "@calcom/atoms/monorepo"; import { getServerSessionForAppDir } from "@calcom/feature-auth/lib/get-server-session-for-app-dir"; -import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; -import { Button } from "@calcom/ui"; - -import ConferencingView from "~/settings/my-account/conferencing-view"; export const generateMetadata = async () => await _generateMetadata( @@ -17,22 +14,12 @@ const Page = async () => { const session = await getServerSessionForAppDir(); const t = await getFixedT(session?.user.locale || "en"); - const AddConferencingButton = () => { - return ( - - ); - }; - return ( - } - borderInShellHeader={true}> - - + add={t("add")} + /> ); }; diff --git a/apps/web/components/apps/CalendarListContainer.tsx b/apps/web/components/apps/CalendarListContainer.tsx index 2e956fcc16..194a7f586a 100644 --- a/apps/web/components/apps/CalendarListContainer.tsx +++ b/apps/web/components/apps/CalendarListContainer.tsx @@ -7,6 +7,7 @@ import { SelectedCalendarsSettingsWebWrapper, DestinationCalendarSettingsWebWrapper, } from "@calcom/atoms/monorepo"; +import AppListCard from "@calcom/features/apps/components/AppListCard"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { trpc } from "@calcom/trpc/react"; import { @@ -21,7 +22,6 @@ import { import { QueryCell } from "@lib/QueryCell"; import useRouterQuery from "@lib/hooks/useRouterQuery"; -import AppListCard from "@components/AppListCard"; import SubHeadingTitleWithConnections from "@components/integrations/SubHeadingTitleWithConnections"; type Props = { diff --git a/apps/web/modules/apps/installed/[category]/installed-category-view.tsx b/apps/web/modules/apps/installed/[category]/installed-category-view.tsx index 3e9a15293b..87fa5ef212 100644 --- a/apps/web/modules/apps/installed/[category]/installed-category-view.tsx +++ b/apps/web/modules/apps/installed/[category]/installed-category-view.tsx @@ -3,19 +3,28 @@ import { useReducer } from "react"; import getAppCategoryTitle from "@calcom/app-store/_utils/getAppCategoryTitle"; +import type { UpdateDefaultConferencingAppParams } from "@calcom/features/apps/components/AppList"; +import { AppList } from "@calcom/features/apps/components/AppList"; import DisconnectIntegrationModal from "@calcom/features/apps/components/DisconnectIntegrationModal"; +import type { RemoveAppParams } from "@calcom/features/apps/components/DisconnectIntegrationModal"; +import type { BulkUpdatParams } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { AppCategories } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; import type { inferSSRProps } from "@calcom/types/inferSSRProps"; import type { Icon } from "@calcom/ui"; -import { AppSkeletonLoader as SkeletonLoader, Button, EmptyScreen, ShellSubHeading } from "@calcom/ui"; +import { + AppSkeletonLoader as SkeletonLoader, + Button, + EmptyScreen, + ShellSubHeading, + showToast, +} from "@calcom/ui"; import { QueryCell } from "@lib/QueryCell"; import type { querySchemaType, getServerSideProps } from "@lib/apps/installed/[category]/getServerSideProps"; -import { AppList } from "@components/apps/AppList"; import { CalendarListContainer } from "@components/apps/CalendarListContainer"; import InstalledAppsLayout from "@components/apps/layouts/InstalledAppsLayout"; @@ -31,6 +40,7 @@ const IntegrationsContainer = ({ handleDisconnect, }: IntegrationsContainerProps): JSX.Element => { const { t } = useLocale(); + const utils = trpc.useUtils(); const query = trpc.viewer.integrations.useQuery({ variant, exclude, @@ -38,6 +48,42 @@ const IntegrationsContainer = ({ includeTeamInstalledApps: true, }); + const { data: defaultConferencingApp } = trpc.viewer.getUsersDefaultConferencingApp.useQuery(); + + const updateDefaultAppMutation = trpc.viewer.updateUserDefaultConferencingApp.useMutation(); + + const updateLocationsMutation = trpc.viewer.eventTypes.bulkUpdateToDefaultLocation.useMutation(); + + const handleUpdateDefaultConferencingApp = ({ appSlug, callback }: UpdateDefaultConferencingAppParams) => { + updateDefaultAppMutation.mutate( + { appSlug }, + { + onSuccess: () => { + showToast("Default app updated successfully", "success"); + utils.viewer.getUsersDefaultConferencingApp.invalidate(); + callback(); + }, + onError: (error) => { + showToast(`Error: ${error.message}`, "error"); + }, + } + ); + }; + + const handleBulkUpdateDefaultLocation = ({ eventTypeIds, callback }: BulkUpdatParams) => { + updateLocationsMutation.mutate( + { + eventTypeIds, + }, + { + onSuccess: () => { + utils.viewer.getUsersDefaultConferencingApp.invalidate(); + callback(); + }, + } + ); + }; + // TODO: Refactor and reuse getAppCategories? const emptyIcon: Record["name"]> = { calendar: "calendar", @@ -95,7 +141,15 @@ const IntegrationsContainer = ({ } /> - + ); }} @@ -114,6 +168,7 @@ export type PageProps = inferSSRProps; export default function InstalledApps(props: PageProps) { const searchParams = useCompatSearchParams(); const { t } = useLocale(); + const utils = trpc.useUtils(); const category = searchParams?.get("category") as querySchemaType["category"]; const categoryList: AppCategories[] = Object.values(AppCategories).filter((category) => { // Exclude calendar and other from categoryList, we handle those slightly differently below @@ -136,6 +191,26 @@ export default function InstalledApps(props: PageProps) { updateData({ isOpen: true, credentialId, teamId }); }; + const deleteCredentialMutation = trpc.viewer.deleteCredential.useMutation(); + + const handleRemoveApp = ({ credentialId, teamId, callback }: RemoveAppParams) => { + deleteCredentialMutation.mutate( + { id: credentialId, teamId }, + { + onSuccess: () => { + showToast(t("app_removed_successfully"), "success"); + callback(); + utils.viewer.integrations.invalidate(); + utils.viewer.connectedCalendars.invalidate(); + }, + onError: () => { + showToast(t("error_removing_app"), "error"); + callback(); + }, + } + ); + }; + return ( <> @@ -156,6 +231,7 @@ export default function InstalledApps(props: PageProps) { isOpen={data.isOpen} credentialId={data.credentialId} teamId={data.teamId} + handleRemoveApp={handleRemoveApp} /> ); diff --git a/apps/web/modules/availability/[schedule]/schedule-view.tsx b/apps/web/modules/availability/[schedule]/schedule-view.tsx index 52b7b8ea2a..4de9f78185 100644 --- a/apps/web/modules/availability/[schedule]/schedule-view.tsx +++ b/apps/web/modules/availability/[schedule]/schedule-view.tsx @@ -4,6 +4,7 @@ import { useRouter } from "next/navigation"; import { useState } from "react"; import { AvailabilitySettings } from "@calcom/atoms/monorepo"; +import type { BulkUpdatParams } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import { withErrorFromUnknown } from "@calcom/lib/getClientErrorFromUnknown"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -47,13 +48,22 @@ export const AvailabilitySettingsWebWrapper = ({ const [isBulkUpdateModalOpen, setIsBulkUpdateModalOpen] = useState(false); const bulkUpdateDefaultAvailabilityMutation = - trpc.viewer.availability.schedule.bulkUpdateToDefaultAvailability.useMutation({ - onSuccess: () => { - utils.viewer.availability.list.invalidate(); - setIsBulkUpdateModalOpen(false); - showToast(t("success"), "success"); + trpc.viewer.availability.schedule.bulkUpdateToDefaultAvailability.useMutation(); + + const bulkUpdateFunction = ({ eventTypeIds, callback }: BulkUpdatParams) => { + bulkUpdateDefaultAvailabilityMutation.mutate( + { + eventTypeIds, }, - }); + { + onSuccess: () => { + utils.viewer.availability.list.invalidate(); + callback(); + showToast(t("success"), "success"); + }, + } + ); + }; const isDefaultSchedule = me.data?.defaultScheduleId === scheduleId; @@ -130,7 +140,7 @@ export const AvailabilitySettingsWebWrapper = ({ bulkUpdateModalProps={{ isOpen: isBulkUpdateModalOpen, setIsOpen: setIsBulkUpdateModalOpen, - save: bulkUpdateDefaultAvailabilityMutation.mutate, + save: bulkUpdateFunction, isSaving: bulkUpdateDefaultAvailabilityMutation.isPending, }} /> diff --git a/apps/web/modules/availability/availability-view.tsx b/apps/web/modules/availability/availability-view.tsx index 361c062b83..a884ab62a8 100644 --- a/apps/web/modules/availability/availability-view.tsx +++ b/apps/web/modules/availability/availability-view.tsx @@ -7,6 +7,7 @@ import { useCallback, useState } from "react"; import SkeletonLoader from "@calcom/features/availability/components/SkeletonLoader"; import { BulkEditDefaultForEventsModal } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; +import type { BulkUpdatParams } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import { NewScheduleButton, ScheduleListItem } from "@calcom/features/schedules"; import Shell from "@calcom/features/shell/Shell"; import { AvailabilitySliderTable } from "@calcom/features/timezone-buddy/components/AvailabilitySliderTable"; @@ -80,13 +81,22 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab }); const bulkUpdateDefaultAvailabilityMutation = - trpc.viewer.availability.schedule.bulkUpdateToDefaultAvailability.useMutation({ - onSuccess: () => { - utils.viewer.availability.list.invalidate(); - setBulkUpdateModal(false); - showToast(t("success"), "success"); + trpc.viewer.availability.schedule.bulkUpdateToDefaultAvailability.useMutation(); + + const bulkUpdateFunction = ({ eventTypeIds, callback }: BulkUpdatParams) => { + bulkUpdateDefaultAvailabilityMutation.mutate( + { + eventTypeIds, }, - }); + { + onSuccess: () => { + utils.viewer.availability.list.invalidate(); + showToast(t("success"), "success"); + callback(); + }, + } + ); + }; const duplicateMutation = trpc.viewer.availability.schedule.duplicate.useMutation({ onSuccess: async ({ schedule }) => { @@ -149,7 +159,7 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab isPending={bulkUpdateDefaultAvailabilityMutation.isPending} open={bulkUpdateModal} setOpen={setBulkUpdateModal} - bulkUpdateFunction={bulkUpdateDefaultAvailabilityMutation.mutate} + bulkUpdateFunction={bulkUpdateFunction} description={t("default_schedules_bulk_description")} /> )} diff --git a/apps/web/modules/settings/my-account/conferencing-view.tsx b/apps/web/modules/settings/my-account/conferencing-view.tsx deleted file mode 100644 index e0fd77fd9a..0000000000 --- a/apps/web/modules/settings/my-account/conferencing-view.tsx +++ /dev/null @@ -1,100 +0,0 @@ -"use client"; - -import { useReducer } from "react"; - -import DisconnectIntegrationModal from "@calcom/features/apps/components/DisconnectIntegrationModal"; -import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { trpc } from "@calcom/trpc/react"; -import { Button, EmptyScreen, SkeletonContainer, SkeletonText } from "@calcom/ui"; - -import { QueryCell } from "@lib/QueryCell"; - -import { AppList } from "@components/apps/AppList"; - -const SkeletonLoader = () => { - return ( - -
- - -
-
- ); -}; - -type ModalState = { - isOpen: boolean; - credentialId: null | number; -}; - -const ConferencingView = () => { - const { t } = useLocale(); - - const [modal, updateModal] = useReducer( - (data: ModalState, partialData: Partial) => ({ ...data, ...partialData }), - { - isOpen: false, - credentialId: null, - } - ); - - const query = trpc.viewer.integrations.useQuery({ - variant: "conferencing", - onlyInstalled: true, - }); - - const handleModelClose = () => { - updateModal({ isOpen: false, credentialId: null }); - }; - - const handleDisconnect = (credentialId: number) => { - updateModal({ isOpen: true, credentialId }); - }; - - return ( - <> -
- } - success={({ data }) => { - if (!data.items.length) { - return ( - - {t("connect_conference_apps")} - - } - /> - ); - } - return ( - - ); - }} - /> -
- - - ); -}; - -export default ConferencingView; diff --git a/packages/features/apps/AdminAppsList.tsx b/packages/features/apps/AdminAppsList.tsx index 34749b1cd6..211d15e985 100644 --- a/packages/features/apps/AdminAppsList.tsx +++ b/packages/features/apps/AdminAppsList.tsx @@ -10,6 +10,7 @@ import { z } from "zod"; import AppCategoryNavigation from "@calcom/app-store/_components/AppCategoryNavigation"; import { appKeysSchemas } from "@calcom/app-store/apps.keys-schemas.generated"; +import AppListCard from "@calcom/features/apps/components/AppListCard"; import { classNames as cs } from "@calcom/lib"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -35,8 +36,6 @@ import { TextField, } from "@calcom/ui"; -import AppListCard from "../../../apps/web/components/AppListCard"; - type App = RouterOutputs["viewer"]["appsRouter"]["listLocal"][number]; const IntegrationContainer = ({ diff --git a/apps/web/components/apps/AppList.tsx b/packages/features/apps/components/AppList.tsx similarity index 86% rename from apps/web/components/apps/AppList.tsx rename to packages/features/apps/components/AppList.tsx index ee065026e4..9533029fc6 100644 --- a/apps/web/components/apps/AppList.tsx +++ b/packages/features/apps/components/AppList.tsx @@ -4,7 +4,9 @@ import { AppSettings } from "@calcom/app-store/_components/AppSettings"; import { InstallAppButton } from "@calcom/app-store/components"; import { getLocationFromApp, type EventLocationType } from "@calcom/app-store/locations"; import type { CredentialOwner } from "@calcom/app-store/types"; +import AppListCard from "@calcom/features/apps/components/AppListCard"; import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog"; +import type { BulkUpdatParams } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import { BulkEditDefaultForEventsModal } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import type { AppCategories } from "@calcom/prisma/enums"; @@ -22,18 +24,29 @@ import { showToast, } from "@calcom/ui"; -import AppListCard from "@components/AppListCard"; +export type UpdateDefaultConferencingAppParams = { appSlug: string; callback: () => void }; interface AppListProps { variant?: AppCategories; data: RouterOutputs["viewer"]["integrations"]; handleDisconnect: (credentialId: number) => void; listClassName?: string; + defaultConferencingApp: RouterOutputs["viewer"]["getUsersDefaultConferencingApp"]; + handleUpdateDefaultConferencingApp: (params: UpdateDefaultConferencingAppParams) => void; + handleBulkUpdateDefaultLocation: (params: BulkUpdatParams) => void; + isBulkUpdateDefaultLocationPending: boolean; } -export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppListProps) => { - const { data: defaultConferencingApp } = trpc.viewer.getUsersDefaultConferencingApp.useQuery(); - const utils = trpc.useUtils(); +export const AppList = ({ + data, + handleDisconnect, + variant, + listClassName, + defaultConferencingApp, + handleUpdateDefaultConferencingApp, + handleBulkUpdateDefaultLocation, + isBulkUpdateDefaultLocationPending, +}: AppListProps) => { const [bulkUpdateModal, setBulkUpdateModal] = useState(false); const [locationType, setLocationType] = useState<(EventLocationType & { slug: string }) | undefined>( undefined @@ -44,17 +57,6 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL showToast("Default app updated successfully", "success"); }, []); - const updateDefaultAppMutation = trpc.viewer.updateUserDefaultConferencingApp.useMutation({ - onSuccess: () => { - showToast("Default app updated successfully", "success"); - utils.viewer.getUsersDefaultConferencingApp.invalidate(); - setBulkUpdateModal(true); - }, - onError: (error) => { - showToast(`Error: ${error.message}`, "error"); - }, - }); - const ChildAppCard = ({ item, }: { @@ -96,8 +98,9 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL if (locationType?.linkType === "static") { setLocationType({ ...locationType, slug: appSlug }); } else { - updateDefaultAppMutation.mutate({ + handleUpdateDefaultConferencingApp({ appSlug, + callback: () => setBulkUpdateModal(true), }); } }}> @@ -153,12 +156,6 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL }); const { t } = useLocale(); - const updateLocationsMutation = trpc.viewer.eventTypes.bulkUpdateToDefaultLocation.useMutation({ - onSuccess: () => { - utils.viewer.getUsersDefaultConferencingApp.invalidate(); - setBulkUpdateModal(false); - }, - }); return ( <> @@ -179,10 +176,10 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL {bulkUpdateModal && ( )} diff --git a/apps/web/components/AppListCard.tsx b/packages/features/apps/components/AppListCard.tsx similarity index 85% rename from apps/web/components/AppListCard.tsx rename to packages/features/apps/components/AppListCard.tsx index 7a7eccfa66..200b56dde7 100644 --- a/apps/web/components/AppListCard.tsx +++ b/packages/features/apps/components/AppListCard.tsx @@ -6,6 +6,7 @@ import { useEffect, useRef, useState } from "react"; import { z } from "zod"; import type { CredentialOwner } from "@calcom/app-store/types"; +import { useIsPlatform } from "@calcom/atoms/monorepo"; import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams"; import { useTypedQuery } from "@calcom/lib/hooks/useTypedQuery"; import { AppListCard as AppListCardComponent } from "@calcom/ui"; @@ -35,7 +36,10 @@ export type AppListCardProps = { const schema = z.object({ hl: z.string().optional() }); -export default function AppListCard(props: AppListCardProps) { +function AppListCardPlatformWrapper(props: AppListCardProps) { + return ; +} +function AppListCardWebWrapper(props: AppListCardProps) { const { slug, shouldHighlight } = props; const { data: { hl }, @@ -70,3 +74,8 @@ export default function AppListCard(props: AppListCardProps) { return ; } + +export default function AppListCard(props: AppListCardProps) { + const isPlatform = useIsPlatform(); + return isPlatform ? : ; +} diff --git a/packages/features/apps/components/DisconnectIntegrationModal.tsx b/packages/features/apps/components/DisconnectIntegrationModal.tsx index 0899e07d98..b49c4aa49c 100644 --- a/packages/features/apps/components/DisconnectIntegrationModal.tsx +++ b/packages/features/apps/components/DisconnectIntegrationModal.tsx @@ -1,12 +1,13 @@ import { useLocale } from "@calcom/lib/hooks/useLocale"; -import { trpc } from "@calcom/trpc/react"; -import { Dialog, showToast, ConfirmationDialogContent } from "@calcom/ui"; +import { Dialog, ConfirmationDialogContent } from "@calcom/ui"; +export type RemoveAppParams = { credentialId: number; teamId?: number; callback: () => void }; interface DisconnectIntegrationModalProps { credentialId: number | null; isOpen: boolean; handleModelClose: () => void; teamId?: number; + handleRemoveApp: (params: RemoveAppParams) => void; } export default function DisconnectIntegrationModal({ @@ -14,23 +15,9 @@ export default function DisconnectIntegrationModal({ isOpen, handleModelClose, teamId, + handleRemoveApp, }: DisconnectIntegrationModalProps) { const { t } = useLocale(); - const utils = trpc.useUtils(); - - const mutation = trpc.viewer.deleteCredential.useMutation({ - onSuccess: () => { - showToast(t("app_removed_successfully"), "success"); - handleModelClose(); - utils.viewer.integrations.invalidate(); - utils.viewer.connectedCalendars.invalidate(); - }, - onError: () => { - showToast(t("error_removing_app"), "error"); - handleModelClose(); - }, - }); - return ( { if (credentialId) { - mutation.mutate({ id: credentialId, teamId }); + handleRemoveApp({ credentialId, teamId, callback: () => handleModelClose() }); } }}>

{t("are_you_sure_you_want_to_remove_this_app")}

diff --git a/packages/features/eventtypes/components/BulkEditDefaultForEventsModal.tsx b/packages/features/eventtypes/components/BulkEditDefaultForEventsModal.tsx index 6786fe0808..32544b43a3 100644 --- a/packages/features/eventtypes/components/BulkEditDefaultForEventsModal.tsx +++ b/packages/features/eventtypes/components/BulkEditDefaultForEventsModal.tsx @@ -10,10 +10,12 @@ export const BulkUpdateEventSchema = z.object({ eventTypeIds: z.array(z.number()), }); +export type BulkUpdatParams = { eventTypeIds: number[]; callback: () => void }; + export function BulkEditDefaultForEventsModal(props: { open: boolean; setOpen: (open: boolean) => void; - bulkUpdateFunction: ({ eventTypeIds }: { eventTypeIds: number[] }) => void; + bulkUpdateFunction: (params: BulkUpdatParams) => void; isPending: boolean; description: string; }) { @@ -42,7 +44,10 @@ export function BulkEditDefaultForEventsModal(props: {
{ - props.bulkUpdateFunction(values); + props.bulkUpdateFunction({ + eventTypeIds: values.eventTypeIds, + callback: () => props.setOpen(false), + }); }}>
{data.eventTypes.length > 0 && ( diff --git a/packages/platform/atoms/availability/AvailabilitySettings.tsx b/packages/platform/atoms/availability/AvailabilitySettings.tsx index 7bb6be7b7d..9af4e8d763 100644 --- a/packages/platform/atoms/availability/AvailabilitySettings.tsx +++ b/packages/platform/atoms/availability/AvailabilitySettings.tsx @@ -5,6 +5,7 @@ import { useMemo, useState } from "react"; import { Controller, useFieldArray, useForm, useWatch } from "react-hook-form"; import dayjs from "@calcom/dayjs"; +import type { BulkUpdatParams } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import { BulkEditDefaultForEventsModal } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal"; import { DateOverrideInputDialog, DateOverrideList } from "@calcom/features/schedules"; import WebSchedule, { @@ -106,7 +107,7 @@ type AvailabilitySettingsProps = { bulkUpdateModalProps?: { isOpen: boolean; setIsOpen: Dispatch>; - save: ({ eventTypeIds }: { eventTypeIds: number[] }) => void; + save: (params: BulkUpdatParams) => void; isSaving: boolean; }; }; diff --git a/packages/platform/atoms/connect/conferencing-apps/ConferencingAppsViewWebWrapper.tsx b/packages/platform/atoms/connect/conferencing-apps/ConferencingAppsViewWebWrapper.tsx new file mode 100644 index 0000000000..0571f4b03d --- /dev/null +++ b/packages/platform/atoms/connect/conferencing-apps/ConferencingAppsViewWebWrapper.tsx @@ -0,0 +1,187 @@ +"use client"; + +import { useReducer } from "react"; + +import { AppList } from "@calcom/features/apps/components/AppList"; +import DisconnectIntegrationModal from "@calcom/features/apps/components/DisconnectIntegrationModal"; +import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { QueryCell } from "@calcom/trpc/components/QueryCell"; +import { trpc } from "@calcom/trpc/react"; +import { Button, EmptyScreen, showToast, SkeletonContainer, SkeletonText } from "@calcom/ui"; + +type ConferencingAppsViewWebWrapperProps = { + title: string; + description: string; + add: string; +}; + +type UpdateDefaultConferencingAppParams = { appSlug: string; callback: () => void }; +type BulkUpdatParams = { eventTypeIds: number[]; callback: () => void }; +type RemoveAppParams = { credentialId: number; teamId?: number; callback: () => void }; + +const SkeletonLoader = () => { + return ( + +
+ + +
+
+ ); +}; + +type ModalState = { + isOpen: boolean; + credentialId: null | number; +}; + +export const ConferencingAppsViewWebWrapper = ({ + title, + description, + add, +}: ConferencingAppsViewWebWrapperProps) => { + const { t } = useLocale(); + const utils = trpc.useUtils(); + + const [modal, updateModal] = useReducer( + (data: ModalState, partialData: Partial) => ({ ...data, ...partialData }), + { + isOpen: false, + credentialId: null, + } + ); + + const handleModelClose = () => { + updateModal({ isOpen: false, credentialId: null }); + }; + + const handleDisconnect = (credentialId: number) => { + updateModal({ isOpen: true, credentialId }); + }; + + const installedIntegrationsQuery = trpc.viewer.integrations.useQuery({ + variant: "conferencing", + onlyInstalled: true, + }); + + const { data: defaultConferencingApp } = trpc.viewer.getUsersDefaultConferencingApp.useQuery(); + + const deleteCredentialMutation = trpc.viewer.deleteCredential.useMutation(); + + const updateDefaultAppMutation = trpc.viewer.updateUserDefaultConferencingApp.useMutation(); + + const updateLocationsMutation = trpc.viewer.eventTypes.bulkUpdateToDefaultLocation.useMutation(); + + const handleRemoveApp = ({ credentialId, teamId, callback }: RemoveAppParams) => { + deleteCredentialMutation.mutate( + { id: credentialId, teamId }, + { + onSuccess: () => { + showToast(t("app_removed_successfully"), "success"); + callback(); + utils.viewer.integrations.invalidate(); + utils.viewer.connectedCalendars.invalidate(); + }, + onError: () => { + showToast(t("error_removing_app"), "error"); + callback(); + }, + } + ); + }; + + const handleUpdateDefaultConferencingApp = ({ appSlug, callback }: UpdateDefaultConferencingAppParams) => { + updateDefaultAppMutation.mutate( + { appSlug }, + { + onSuccess: () => { + showToast("Default app updated successfully", "success"); + utils.viewer.getUsersDefaultConferencingApp.invalidate(); + callback(); + }, + onError: (error) => { + showToast(`Error: ${error.message}`, "error"); + }, + } + ); + }; + + const handleBulkUpdateDefaultLocation = ({ eventTypeIds, callback }: BulkUpdatParams) => { + updateLocationsMutation.mutate( + { + eventTypeIds, + }, + { + onSuccess: () => { + utils.viewer.getUsersDefaultConferencingApp.invalidate(); + callback(); + }, + } + ); + }; + + const AddConferencingButton = () => { + return ( + + ); + }; + + return ( + } + borderInShellHeader={true}> + <> +
+ } + success={({ data }) => { + if (!data.items.length) { + return ( + + {t("connect_conference_apps")} + + } + /> + ); + } + return ( + + ); + }} + /> +
+ + +
+ ); +}; diff --git a/packages/platform/atoms/monorepo.ts b/packages/platform/atoms/monorepo.ts index fae9470223..879e4e1bc9 100644 --- a/packages/platform/atoms/monorepo.ts +++ b/packages/platform/atoms/monorepo.ts @@ -10,6 +10,7 @@ export { SelectedCalendarsSettingsWebWrapper } from "./selected-calendars/wrappe export { DestinationCalendarSettingsWebWrapper } from "./destination-calendar/wrappers/DestinationCalendarSettingsWebWrapper"; export * from "./availability"; export { EventTypeWebWrapper as EventType } from "./event-types/wrappers/EventTypeWebWrapper"; +export { ConferencingAppsViewWebWrapper } from "./connect/conferencing-apps/ConferencingAppsViewWebWrapper"; export type { UpdateScheduleInput_2024_06_11 as UpdateScheduleBody } from "@calcom/platform-types"; export { Shell } from "./src/components/ui/shell"; export { AddMembersWithSwitchWebWrapper } from "./add-members-switch/AddMembersWithSwitchWebWrapper"; diff --git a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx index ffe36056df..3adb3b0746 100644 --- a/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx +++ b/packages/platform/atoms/selected-calendars/wrappers/SelectedCalendarsSettingsWebWrapper.tsx @@ -1,6 +1,7 @@ import Link from "next/link"; import React from "react"; +import AppListCard from "@calcom/features/apps/components/AppListCard"; import DisconnectIntegration from "@calcom/features/apps/components/DisconnectIntegration"; import { CalendarSwitch } from "@calcom/features/calendars/CalendarSwitch"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -8,7 +9,6 @@ import { QueryCell } from "@calcom/trpc/components/QueryCell"; import { trpc } from "@calcom/trpc/react"; import { Alert } from "@calcom/ui"; import { List } from "@calcom/ui"; -import AppListCard from "@calcom/web/components/AppListCard"; import AdditionalCalendarSelector from "@calcom/web/components/apps/AdditionalCalendarSelector"; import { SelectedCalendarsSettings } from "../SelectedCalendarsSettings"; diff --git a/packages/ui/components/app-list-card/AppListCard.tsx b/packages/ui/components/app-list-card/AppListCard.tsx index 81d6a144d9..cf1988d141 100644 --- a/packages/ui/components/app-list-card/AppListCard.tsx +++ b/packages/ui/components/app-list-card/AppListCard.tsx @@ -1,12 +1,11 @@ "use client"; +import type { AppListCardProps } from "@calcom/features/apps/components/AppListCard"; import classNames from "@calcom/lib/classNames"; import { getPlaceholderAvatar } from "@calcom/lib/defaultAvatarImage"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { Avatar, Badge, Icon, ListItemText } from "@calcom/ui"; -import type { AppListCardProps } from "../../../../apps/web/components/AppListCard"; - export const AppListCard = (props: AppListCardProps & { highlight?: boolean }) => { const { t } = useLocale(); const {