import { zodResolver } from "@hookform/resolvers/zod"; import { AppCategories } from "@prisma/client"; import { noop } from "lodash"; import { useRouter } from "next/router"; 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 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, TextField, Switch, } from "@calcom/ui"; import { FiAlertCircle, FiEdit } 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 = () => { // 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", }); } }; 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 { enableAppMutation.mutate({ slug: app.slug, enabled: app.enabled }); } }} /> } /> { enableAppMutation.mutate({ slug: app.slug, enabled: app.enabled }); }}> {t("disable_app_description")}
  • ); }; 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 (
    { e.preventDefault(); onSubmit(); }}>
    ); }; const EditKeysModal: FC<{ dirName: string; slug: string; type: string; isOpen: boolean; keys: App["keys"]; handleModelClose: () => void; }> = (props) => { const { t } = useLocale(); const { dirName, slug, type, isOpen, keys, handleModelClose } = props; const appKeySchema = appKeysSchemas[dirName as keyof typeof appKeysSchemas]; const formMethods = useForm({ resolver: zodResolver(appKeySchema), }); const saveKeysMutation = trpc.viewer.appsRouter.saveKeys.useMutation({ onSuccess: () => { showToast(t("keys_have_been_saved"), "success"); handleModelClose(); }, onError: (error) => { showToast(error.message, "error"); }, }); return ( {!!keys && typeof keys === "object" && (
    saveKeysMutation.mutate({ slug, type, keys: values, dirName, }) } className="px-4 pb-4"> {Object.keys(keys).map((key) => ( ( { formMethods.setValue(key, e?.target.value); }} /> )} /> ))} )}
    ); }; interface EditModalState extends Pick { isOpen: "none" | "editKeys" | "disableKeys"; dirName: string; type: string; slug: string; } const AdminAppsListContainer = () => { const { t } = useLocale(); const router = useRouter(); const { category } = querySchema.parse(router.query); const { data: apps, isLoading } = trpc.viewer.appsRouter.listLocal.useQuery( { category }, { enabled: router.isReady } ); 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) { return ( ); } return ( <> {apps.map((app) => ( ))} ); }; export default AdminAppsList; const SkeletonLoader = () => { return (
    ); };