import { zodResolver } from "@hookform/resolvers/zod";
// eslint-disable-next-line no-restricted-imports
import { noop } from "lodash";
import { useSearchParams } from "next/navigation";
import type { FC } from "react";
import { useReducer, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { z } from "zod";
import AppCategoryNavigation from "@calcom/app-store/_components/AppCategoryNavigation";
import { appKeysSchemas } from "@calcom/app-store/apps.keys-schemas.generated";
import { classNames as cs } from "@calcom/lib";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { AppCategories } from "@calcom/prisma/enums";
import type { RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import {
Button,
ConfirmationDialogContent,
Dialog,
DialogClose,
DialogContent,
DialogFooter,
EmptyScreen,
Form,
List,
showToast,
SkeletonButton,
SkeletonContainer,
SkeletonText,
Switch,
TextField,
} from "@calcom/ui";
import { AlertCircle, Edit } from "@calcom/ui/components/icon";
import AppListCard from "../../../apps/web/components/AppListCard";
type App = RouterOutputs["viewer"]["appsRouter"]["listLocal"][number];
const IntegrationContainer = ({
app,
category,
handleModelOpen,
}: {
app: App;
category: string;
handleModelOpen: (data: EditModalState) => void;
}) => {
const { t } = useLocale();
const utils = trpc.useContext();
const [disableDialog, setDisableDialog] = useState(false);
const showKeyModal = (fromEnabled?: boolean) => {
// FIXME: This is preventing the modal from opening for apps that has null keys
if (app.keys) {
handleModelOpen({
dirName: app.dirName,
keys: app.keys,
slug: app.slug,
type: app.type,
isOpen: "editKeys",
fromEnabled,
appName: app.name,
});
}
};
const enableAppMutation = trpc.viewer.appsRouter.toggle.useMutation({
onSuccess: (enabled) => {
utils.viewer.appsRouter.listLocal.invalidate({ category });
setDisableDialog(false);
showToast(
enabled ? t("app_is_enabled", { appName: app.name }) : t("app_is_disabled", { appName: app.name }),
"success"
);
if (enabled) {
showKeyModal();
}
},
onError: (error) => {
showToast(error.message, "error");
},
});
return (
{app.keys && (
)}
{
if (app.enabled) {
setDisableDialog(true);
} else if (app.keys) {
showKeyModal(true);
} else {
enableAppMutation.mutate({ slug: app.slug, enabled: !app.enabled });
}
}}
/>
}
/>
);
};
const querySchema = z.object({
category: z
.nativeEnum({ ...AppCategories, conferencing: "conferencing" })
.optional()
.default(AppCategories.calendar),
});
const AdminAppsList = ({
baseURL,
className,
useQueryParam = false,
classNames,
onSubmit = noop,
...rest
}: {
baseURL: string;
classNames?: {
form?: string;
appCategoryNavigationRoot?: string;
appCategoryNavigationContainer?: string;
verticalTabsItem?: string;
};
className?: string;
useQueryParam?: boolean;
onSubmit?: () => void;
} & Omit) => {
return (
);
};
const EditKeysModal: FC<{
dirName: string;
slug: string;
type: string;
isOpen: boolean;
keys: App["keys"];
handleModelClose: () => void;
fromEnabled?: boolean;
appName?: string;
}> = (props) => {
const utils = trpc.useContext();
const { t } = useLocale();
const { dirName, slug, type, isOpen, keys, handleModelClose, fromEnabled, appName } = props;
const appKeySchema = appKeysSchemas[dirName as keyof typeof appKeysSchemas];
const formMethods = useForm({
resolver: zodResolver(appKeySchema),
});
const saveKeysMutation = trpc.viewer.appsRouter.saveKeys.useMutation({
onSuccess: () => {
showToast(fromEnabled ? t("app_is_enabled", { appName }) : t("keys_have_been_saved"), "success");
utils.viewer.appsRouter.listLocal.invalidate();
handleModelClose();
},
onError: (error) => {
showToast(error.message, "error");
},
});
return (
);
};
interface EditModalState extends Pick {
isOpen: "none" | "editKeys" | "disableKeys";
dirName: string;
type: string;
slug: string;
fromEnabled?: boolean;
appName?: string;
}
const AdminAppsListContainer = () => {
const searchParams = useSearchParams();
const { t } = useLocale();
const category = searchParams.get("category") || AppCategories.calendar;
const { data: apps, isLoading } = trpc.viewer.appsRouter.listLocal.useQuery(
{ category },
{ enabled: searchParams !== null }
);
const [modalState, setModalState] = useReducer(
(data: EditModalState, partialData: Partial) => ({ ...data, ...partialData }),
{
keys: null,
isOpen: "none",
dirName: "",
type: "",
slug: "",
}
);
const handleModelClose = () =>
setModalState({ keys: null, isOpen: "none", dirName: "", slug: "", type: "" });
const handleModelOpen = (data: EditModalState) => setModalState({ ...data });
if (isLoading) return ;
if (!apps || apps.length === 0) {
return (
);
}
return (
<>
{apps.map((app) => (
))}
>
);
};
export default AdminAppsList;
const SkeletonLoader = () => {
return (
);
};