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>
This commit is contained in:
@@ -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 (
|
||||
<Button color="secondary" StartIcon="plus" href="/apps/categories/conferencing">
|
||||
{t("add")}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsHeader
|
||||
<ConferencingAppsViewWebWrapper
|
||||
title={t("conferencing")}
|
||||
description={t("conferencing_description")}
|
||||
CTA={<AddConferencingButton />}
|
||||
borderInShellHeader={true}>
|
||||
<ConferencingView />
|
||||
</SettingsHeader>
|
||||
add={t("add")}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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<AppCategories, React.ComponentProps<typeof Icon>["name"]> = {
|
||||
calendar: "calendar",
|
||||
@@ -95,7 +141,15 @@ const IntegrationsContainer = ({
|
||||
}
|
||||
/>
|
||||
|
||||
<AppList handleDisconnect={handleDisconnect} data={data} variant={variant} />
|
||||
<AppList
|
||||
handleDisconnect={handleDisconnect}
|
||||
data={data}
|
||||
variant={variant}
|
||||
defaultConferencingApp={defaultConferencingApp}
|
||||
handleUpdateDefaultConferencingApp={handleUpdateDefaultConferencingApp}
|
||||
handleBulkUpdateDefaultLocation={handleBulkUpdateDefaultLocation}
|
||||
isBulkUpdateDefaultLocationPending={updateDefaultAppMutation.isPending}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
@@ -114,6 +168,7 @@ export type PageProps = inferSSRProps<typeof getServerSideProps>;
|
||||
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 (
|
||||
<>
|
||||
<InstalledAppsLayout heading={t("installed_apps")} subtitle={t("manage_your_connected_apps")}>
|
||||
@@ -156,6 +231,7 @@ export default function InstalledApps(props: PageProps) {
|
||||
isOpen={data.isOpen}
|
||||
credentialId={data.credentialId}
|
||||
teamId={data.teamId}
|
||||
handleRemoveApp={handleRemoveApp}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -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,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -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")}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -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 (
|
||||
<SkeletonContainer>
|
||||
<div className="divide-subtle border-subtle space-y-6 rounded-b-lg border border-t-0 px-6 py-4">
|
||||
<SkeletonText className="h-8 w-full" />
|
||||
<SkeletonText className="h-8 w-full" />
|
||||
</div>
|
||||
</SkeletonContainer>
|
||||
);
|
||||
};
|
||||
|
||||
type ModalState = {
|
||||
isOpen: boolean;
|
||||
credentialId: null | number;
|
||||
};
|
||||
|
||||
const ConferencingView = () => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const [modal, updateModal] = useReducer(
|
||||
(data: ModalState, partialData: Partial<ModalState>) => ({ ...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 (
|
||||
<>
|
||||
<div className="bg-default w-full sm:mx-0 xl:mt-0">
|
||||
<QueryCell
|
||||
query={query}
|
||||
customLoader={<SkeletonLoader />}
|
||||
success={({ data }) => {
|
||||
if (!data.items.length) {
|
||||
return (
|
||||
<EmptyScreen
|
||||
Icon="calendar"
|
||||
headline={t("no_category_apps", {
|
||||
category: t("conferencing").toLowerCase(),
|
||||
})}
|
||||
description={t("no_category_apps_description_conferencing")}
|
||||
buttonRaw={
|
||||
<Button
|
||||
color="secondary"
|
||||
data-testid="connect-conferencing-apps"
|
||||
href="/apps/categories/conferencing">
|
||||
{t("connect_conference_apps")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<AppList
|
||||
listClassName="rounded-lg rounded-t-none border-t-0 max-w-full"
|
||||
handleDisconnect={handleDisconnect}
|
||||
data={data}
|
||||
variant="conferencing"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<DisconnectIntegrationModal
|
||||
handleModelClose={handleModelClose}
|
||||
isOpen={modal.isOpen}
|
||||
credentialId={modal.credentialId}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConferencingView;
|
||||
@@ -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 = ({
|
||||
|
||||
+21
-24
@@ -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 (
|
||||
<>
|
||||
<List className={listClassName}>
|
||||
@@ -179,10 +176,10 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL
|
||||
|
||||
{bulkUpdateModal && (
|
||||
<BulkEditDefaultForEventsModal
|
||||
bulkUpdateFunction={updateLocationsMutation.mutate}
|
||||
bulkUpdateFunction={handleBulkUpdateDefaultLocation}
|
||||
open={bulkUpdateModal}
|
||||
setOpen={setBulkUpdateModal}
|
||||
isPending={updateLocationsMutation.isPending}
|
||||
isPending={isBulkUpdateDefaultLocationPending}
|
||||
description={t("default_conferencing_bulk_description")}
|
||||
/>
|
||||
)}
|
||||
+10
-1
@@ -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 <AppListCardComponent {...props} />;
|
||||
}
|
||||
function AppListCardWebWrapper(props: AppListCardProps) {
|
||||
const { slug, shouldHighlight } = props;
|
||||
const {
|
||||
data: { hl },
|
||||
@@ -70,3 +74,8 @@ export default function AppListCard(props: AppListCardProps) {
|
||||
|
||||
return <AppListCardComponent {...props} />;
|
||||
}
|
||||
|
||||
export default function AppListCard(props: AppListCardProps) {
|
||||
const isPlatform = useIsPlatform();
|
||||
return isPlatform ? <AppListCardPlatformWrapper {...props} /> : <AppListCardWebWrapper {...props} />;
|
||||
}
|
||||
@@ -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 (
|
||||
<Dialog open={isOpen} onOpenChange={handleModelClose}>
|
||||
<ConfirmationDialogContent
|
||||
@@ -39,7 +26,7 @@ export default function DisconnectIntegrationModal({
|
||||
confirmBtnText={t("yes_remove_app")}
|
||||
onConfirm={() => {
|
||||
if (credentialId) {
|
||||
mutation.mutate({ id: credentialId, teamId });
|
||||
handleRemoveApp({ credentialId, teamId, callback: () => handleModelClose() });
|
||||
}
|
||||
}}>
|
||||
<p className="mt-5">{t("are_you_sure_you_want_to_remove_this_app")}</p>
|
||||
|
||||
@@ -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: {
|
||||
<Form
|
||||
form={form}
|
||||
handleSubmit={(values) => {
|
||||
props.bulkUpdateFunction(values);
|
||||
props.bulkUpdateFunction({
|
||||
eventTypeIds: values.eventTypeIds,
|
||||
callback: () => props.setOpen(false),
|
||||
});
|
||||
}}>
|
||||
<div className="flex flex-col space-y-2">
|
||||
{data.eventTypes.length > 0 && (
|
||||
|
||||
@@ -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<SetStateAction<boolean>>;
|
||||
save: ({ eventTypeIds }: { eventTypeIds: number[] }) => void;
|
||||
save: (params: BulkUpdatParams) => void;
|
||||
isSaving: boolean;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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 (
|
||||
<SkeletonContainer>
|
||||
<div className="divide-subtle border-subtle space-y-6 rounded-b-lg border border-t-0 px-6 py-4">
|
||||
<SkeletonText className="h-8 w-full" />
|
||||
<SkeletonText className="h-8 w-full" />
|
||||
</div>
|
||||
</SkeletonContainer>
|
||||
);
|
||||
};
|
||||
|
||||
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<ModalState>) => ({ ...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 (
|
||||
<Button color="secondary" StartIcon="plus" href="/apps/categories/conferencing">
|
||||
{add}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<SettingsHeader
|
||||
title={title}
|
||||
description={description}
|
||||
CTA={<AddConferencingButton />}
|
||||
borderInShellHeader={true}>
|
||||
<>
|
||||
<div className="bg-default w-full sm:mx-0 xl:mt-0">
|
||||
<QueryCell
|
||||
query={installedIntegrationsQuery}
|
||||
customLoader={<SkeletonLoader />}
|
||||
success={({ data }) => {
|
||||
if (!data.items.length) {
|
||||
return (
|
||||
<EmptyScreen
|
||||
Icon="calendar"
|
||||
headline={t("no_category_apps", {
|
||||
category: t("conferencing").toLowerCase(),
|
||||
})}
|
||||
description={t("no_category_apps_description_conferencing")}
|
||||
buttonRaw={
|
||||
<Button
|
||||
color="secondary"
|
||||
data-testid="connect-conferencing-apps"
|
||||
href="/apps/categories/conferencing">
|
||||
{t("connect_conference_apps")}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<AppList
|
||||
listClassName="rounded-lg rounded-t-none border-t-0 max-w-full"
|
||||
handleDisconnect={handleDisconnect}
|
||||
data={data}
|
||||
variant="conferencing"
|
||||
defaultConferencingApp={defaultConferencingApp}
|
||||
handleUpdateDefaultConferencingApp={handleUpdateDefaultConferencingApp}
|
||||
handleBulkUpdateDefaultLocation={handleBulkUpdateDefaultLocation}
|
||||
isBulkUpdateDefaultLocationPending={updateDefaultAppMutation.isPending}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<DisconnectIntegrationModal
|
||||
handleModelClose={handleModelClose}
|
||||
isOpen={modal.isOpen}
|
||||
credentialId={modal.credentialId}
|
||||
handleRemoveApp={handleRemoveApp}
|
||||
/>
|
||||
</>
|
||||
</SettingsHeader>
|
||||
);
|
||||
};
|
||||
@@ -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";
|
||||
|
||||
+1
-1
@@ -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";
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user