Refactor app store & fix admin apps list bugs (#7812)
* If omni install do not show app successfully installed toast * Clean up. Up to hubspot * Fix double click to enable apps * Clean up rest of apps * Create cli app add dirName * Type fix * Only enable apps if keys are valid * Save dirName as slug * Remove dirname from metadata --------- Co-authored-by: zomars <zomars@me.com>
This commit is contained in:
parent
d61f50724b
commit
f6f257c705
|
@ -112,6 +112,7 @@ export const BaseAppFork = {
|
|||
isTemplate,
|
||||
// Store the template used to create an app
|
||||
__template: template,
|
||||
dirName: slug,
|
||||
};
|
||||
const currentConfig = JSON.parse(fs.readFileSync(`${appDirPath}/config.json`).toString());
|
||||
config = {
|
||||
|
|
|
@ -73,7 +73,7 @@ function useAddAppMutation(_type: App["type"] | null, allOptions?: UseAddAppMuta
|
|||
|
||||
if (!isOmniInstall) {
|
||||
gotoUrl(json.url, json.newTab);
|
||||
return;
|
||||
return { setupPending: externalUrl || json.url.endsWith("/setup") };
|
||||
}
|
||||
|
||||
// Skip redirection only if it is an OmniInstall and redirect URL isn't of some other origin
|
||||
|
@ -83,7 +83,7 @@ function useAddAppMutation(_type: App["type"] | null, allOptions?: UseAddAppMuta
|
|||
if (externalUrl) {
|
||||
// TODO: For Omni installation to authenticate and come back to the page where installation was initiated, some changes need to be done in all apps' add callbacks
|
||||
gotoUrl(json.url, json.newTab);
|
||||
return;
|
||||
return { setupPending: externalUrl };
|
||||
}
|
||||
|
||||
return { setupPending: externalUrl || json.url.endsWith("/setup") };
|
||||
|
|
|
@ -12,5 +12,6 @@
|
|||
"email": "support@cal.com",
|
||||
"description": "The joyful productivity app\r\r",
|
||||
"__createdUsingCli": true,
|
||||
"dependencies": ["google-calendar"]
|
||||
"dependencies": ["google-calendar"],
|
||||
"dirName": "amie"
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("apple_calendar");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -5,27 +5,10 @@
|
|||
import dynamic from "next/dynamic";
|
||||
|
||||
export const InstallAppButtonMap = {
|
||||
applecalendar: dynamic(() => import("./applecalendar/components/InstallAppButton")),
|
||||
around: dynamic(() => import("./around/components/InstallAppButton")),
|
||||
caldavcalendar: dynamic(() => import("./caldavcalendar/components/InstallAppButton")),
|
||||
closecom: dynamic(() => import("./closecom/components/InstallAppButton")),
|
||||
exchange2013calendar: dynamic(() => import("./exchange2013calendar/components/InstallAppButton")),
|
||||
exchange2016calendar: dynamic(() => import("./exchange2016calendar/components/InstallAppButton")),
|
||||
exchangecalendar: dynamic(() => import("./exchangecalendar/components/InstallAppButton")),
|
||||
googlecalendar: dynamic(() => import("./googlecalendar/components/InstallAppButton")),
|
||||
hubspot: dynamic(() => import("./hubspot/components/InstallAppButton")),
|
||||
huddle01video: dynamic(() => import("./huddle01video/components/InstallAppButton")),
|
||||
jitsivideo: dynamic(() => import("./jitsivideo/components/InstallAppButton")),
|
||||
larkcalendar: dynamic(() => import("./larkcalendar/components/InstallAppButton")),
|
||||
office365calendar: dynamic(() => import("./office365calendar/components/InstallAppButton")),
|
||||
office365video: dynamic(() => import("./office365video/components/InstallAppButton")),
|
||||
riverside: dynamic(() => import("./riverside/components/InstallAppButton")),
|
||||
tandemvideo: dynamic(() => import("./tandemvideo/components/InstallAppButton")),
|
||||
vital: dynamic(() => import("./vital/components/InstallAppButton")),
|
||||
whereby: dynamic(() => import("./whereby/components/InstallAppButton")),
|
||||
wipemycalother: dynamic(() => import("./wipemycalother/components/InstallAppButton")),
|
||||
zapier: dynamic(() => import("./zapier/components/InstallAppButton")),
|
||||
zoomvideo: dynamic(() => import("./zoomvideo/components/InstallAppButton")),
|
||||
};
|
||||
export const AppSettingsComponentsMap = {
|
||||
"general-app-settings": dynamic(() =>
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("around_video");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -21,7 +21,6 @@ export const metadata = {
|
|||
url: "https://cal.com/",
|
||||
verified: true,
|
||||
email: "ali@cal.com",
|
||||
dirName: "caldavcalendar",
|
||||
} as AppMeta;
|
||||
|
||||
export default metadata;
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("caldav_calendar");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -21,6 +21,7 @@ export const metadata = {
|
|||
url: "https://cal.com/",
|
||||
verified: true,
|
||||
email: "help@cal.com",
|
||||
dirName: "caldavcalendar",
|
||||
} as App;
|
||||
|
||||
export * as api from "./api";
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("closecom_other_calendar");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("exchange_calendar");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("google_calendar");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("hubspot_other_calendar");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("huddle01_video");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("jitsi_video");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("lark_calendar");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -20,7 +20,6 @@ export const metadata = {
|
|||
url: "https://cal.com/",
|
||||
verified: true,
|
||||
email: "help@cal.com",
|
||||
dirName: "office365calendar",
|
||||
} as AppMeta;
|
||||
|
||||
export default metadata;
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("office365_calendar");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -22,5 +22,6 @@
|
|||
"type": "integrations:office365_video",
|
||||
"label": "MS Teams"
|
||||
}
|
||||
}
|
||||
},
|
||||
"dirName": "office365video"
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("riverside_video");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("tandem_video");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("whereby_video");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("wipemycal_other");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("zapier_automation");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -1,18 +0,0 @@
|
|||
import type { InstallAppButtonProps } from "@calcom/app-store/types";
|
||||
|
||||
import useAddAppMutation from "../../_utils/useAddAppMutation";
|
||||
|
||||
export default function InstallAppButton(props: InstallAppButtonProps) {
|
||||
const mutation = useAddAppMutation("zoom_video");
|
||||
|
||||
return (
|
||||
<>
|
||||
{props.render({
|
||||
onClick() {
|
||||
mutation.mutate("");
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
})}
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { default as InstallAppButton } from "./InstallAppButton";
|
|
@ -49,7 +49,7 @@ const IntegrationContainer = ({
|
|||
const utils = trpc.useContext();
|
||||
const [disableDialog, setDisableDialog] = useState(false);
|
||||
|
||||
const showKeyModal = () => {
|
||||
const showKeyModal = (fromEnabled?: boolean) => {
|
||||
// FIXME: This is preventing the modal from opening for apps that has null keys
|
||||
if (app.keys) {
|
||||
handleModelOpen({
|
||||
|
@ -58,6 +58,8 @@ const IntegrationContainer = ({
|
|||
slug: app.slug,
|
||||
type: app.type,
|
||||
isOpen: "editKeys",
|
||||
fromEnabled,
|
||||
appName: app.name,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -99,6 +101,8 @@ const IntegrationContainer = ({
|
|||
onClick={() => {
|
||||
if (app.enabled) {
|
||||
setDisableDialog(true);
|
||||
} else if (app.keys) {
|
||||
showKeyModal(true);
|
||||
} else {
|
||||
enableAppMutation.mutate({ slug: app.slug, enabled: app.enabled });
|
||||
}
|
||||
|
@ -179,9 +183,12 @@ const EditKeysModal: FC<{
|
|||
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 } = props;
|
||||
const { dirName, slug, type, isOpen, keys, handleModelClose, fromEnabled, appName } = props;
|
||||
const appKeySchema = appKeysSchemas[dirName as keyof typeof appKeysSchemas];
|
||||
|
||||
const formMethods = useForm({
|
||||
|
@ -190,7 +197,8 @@ const EditKeysModal: FC<{
|
|||
|
||||
const saveKeysMutation = trpc.viewer.appsRouter.saveKeys.useMutation({
|
||||
onSuccess: () => {
|
||||
showToast(t("keys_have_been_saved"), "success");
|
||||
showToast(fromEnabled ? t("app_is_enabled", { appName }) : t("keys_have_been_saved"), "success");
|
||||
utils.viewer.appsRouter.listLocal.invalidate();
|
||||
handleModelClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
|
@ -211,6 +219,7 @@ const EditKeysModal: FC<{
|
|||
type,
|
||||
keys: values,
|
||||
dirName,
|
||||
fromEnabled,
|
||||
})
|
||||
}
|
||||
className="px-4 pb-4">
|
||||
|
@ -251,6 +260,8 @@ interface EditModalState extends Pick<App, "keys"> {
|
|||
dirName: string;
|
||||
type: string;
|
||||
slug: string;
|
||||
fromEnabled?: boolean;
|
||||
appName?: string;
|
||||
}
|
||||
|
||||
const AdminAppsListContainer = () => {
|
||||
|
@ -310,6 +321,8 @@ const AdminAppsListContainer = () => {
|
|||
isOpen={modalState.isOpen === "editKeys"}
|
||||
slug={modalState.slug}
|
||||
type={modalState.type}
|
||||
fromEnabled={modalState.fromEnabled}
|
||||
appName={modalState.appName}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -135,10 +135,11 @@ export const appsRouter = router({
|
|||
([appMetadata?.category] as AppCategories[]) ||
|
||||
undefined,
|
||||
keys: undefined,
|
||||
enabled: !input.enabled,
|
||||
},
|
||||
});
|
||||
|
||||
// If disabling an app then we need to alert users basesd on the app type
|
||||
// If disabling an app then we need to alert users based on the app type
|
||||
if (input.enabled) {
|
||||
if (app.categories.some((category) => ["calendar", "video"].includes(category))) {
|
||||
// Find all users with the app credentials
|
||||
|
@ -240,10 +241,12 @@ export const appsRouter = router({
|
|||
type: z.string(),
|
||||
// Validate w/ app specific schema
|
||||
keys: z.unknown(),
|
||||
fromEnabled: z.boolean().optional(),
|
||||
})
|
||||
)
|
||||
.mutation(async ({ ctx, input }) => {
|
||||
const appKey = deriveAppDictKeyFromType(input.type, appKeysSchemas);
|
||||
let appKey = deriveAppDictKeyFromType(input.type, appKeysSchemas);
|
||||
if (!appKey) appKey = deriveAppDictKeyFromType(input.slug, appKeysSchemas);
|
||||
const keysSchema = appKeysSchemas[appKey as keyof typeof appKeysSchemas];
|
||||
const keys = keysSchema.parse(input.keys);
|
||||
|
||||
|
@ -258,7 +261,7 @@ export const appsRouter = router({
|
|||
where: {
|
||||
slug: input.slug,
|
||||
},
|
||||
update: { keys },
|
||||
update: { keys, ...(input.fromEnabled && { enabled: true }) },
|
||||
create: {
|
||||
slug: input.slug,
|
||||
dirName: appMetadata?.dirName || appMetadata?.slug || "",
|
||||
|
@ -267,6 +270,7 @@ export const appsRouter = router({
|
|||
([appMetadata?.category] as AppCategories[]) ||
|
||||
undefined,
|
||||
keys: (input.keys as Prisma.InputJsonObject) || undefined,
|
||||
...(input.fromEnabled && { enabled: true }),
|
||||
},
|
||||
});
|
||||
}),
|
||||
|
|
|
@ -135,6 +135,10 @@ export interface App {
|
|||
licenseRequired?: boolean;
|
||||
isProOnly?: boolean;
|
||||
appData?: AppData;
|
||||
/**
|
||||
* @deprecated
|
||||
* Used only by legacy apps which had slug different from their directory name.
|
||||
*/
|
||||
dirName?: string;
|
||||
isTemplate?: boolean;
|
||||
__template?: string;
|
||||
|
|
Loading…
Reference in New Issue
Block a user