Cleaned up v1 for app store pages. (#5204)
Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Peer Richelsen
kodiakhq[bot]
parent
63ce4a68ac
commit
d6c4970bf4
@@ -2,8 +2,8 @@ import { GetStaticPaths, InferGetStaticPropsType } from "next";
|
||||
import { useSession } from "next-auth/react";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import { AppSetupPage } from "@calcom/app-store/_pages/setup";
|
||||
import { getStaticProps } from "@calcom/app-store/_pages/setup/_getStaticProps";
|
||||
import { AppSetupPage } from "@calcom/app-store/_pages/v2/setup";
|
||||
|
||||
export default function SetupInformation(props: InferGetStaticPropsType<typeof getStaticProps>) {
|
||||
const router = useRouter();
|
||||
@@ -23,7 +23,7 @@ export default function SetupInformation(props: InferGetStaticPropsType<typeof g
|
||||
});
|
||||
}
|
||||
|
||||
return <AppSetupPage slug={`${slug}-V2`} {...props} />;
|
||||
return <AppSetupPage slug={slug} {...props} />;
|
||||
}
|
||||
|
||||
export const getStaticPaths: GetStaticPaths = async () => {
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { GetStaticPropsContext } from "next";
|
||||
|
||||
export const AppSetupPageMap = {
|
||||
zapier: import("../../../zapier/pages/setup/_getStaticProps"),
|
||||
};
|
||||
|
||||
export const getStaticProps = async (ctx: GetStaticPropsContext) => {
|
||||
const { slug } = ctx.params || {};
|
||||
if (typeof slug !== "string") return { notFound: true } as const;
|
||||
|
||||
if (!(slug in AppSetupPageMap)) return { props: {} };
|
||||
|
||||
const page = await AppSetupPageMap[slug as keyof typeof AppSetupPageMap];
|
||||
|
||||
if (!page.getStaticProps) return { props: {} };
|
||||
|
||||
const props = await page.getStaticProps(ctx);
|
||||
|
||||
return props;
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
import { DynamicComponent } from "../../../_components/DynamicComponent";
|
||||
|
||||
export const AppSetupMap = {
|
||||
"apple-calendar-V2": dynamic(() => import("../../../applecalendar/pages/v2/setup")),
|
||||
"exchange-V2": dynamic(() => import("../../../exchangecalendar/pages/v2/setup")),
|
||||
"exchange2013-calendar-V2": dynamic(() => import("../../../exchange2013calendar/pages/v2/setup")),
|
||||
"exchange2016-calendar-V2": dynamic(() => import("../../../exchange2016calendar/pages/v2/setup")),
|
||||
"caldav-calendar-V2": dynamic(() => import("../../../caldavcalendar/pages/v2/setup")),
|
||||
"zapier-V2": dynamic(() => import("../../../zapier/pages/v2/setup")),
|
||||
"closecom-V2": dynamic(() => import("../../../closecomothercalendar/pages/v2/setup")),
|
||||
};
|
||||
|
||||
export const AppSetupPage = (props: { slug: string }) => {
|
||||
return <DynamicComponent<typeof AppSetupMap> componentMap={AppSetupMap} {...props} />;
|
||||
};
|
||||
|
||||
export default AppSetupPage;
|
||||
@@ -4,9 +4,7 @@ import { useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Form, TextField } from "@calcom/ui/form/fields";
|
||||
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default function AppleCalendarSetup() {
|
||||
const { t } = useLocale();
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default function AppleCalendarSetup() {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
});
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-200">
|
||||
<div className="m-auto rounded bg-white p-5 md:w-[560px] md:p-10">
|
||||
<div className="flex flex-col space-y-5 md:flex-row md:space-y-0 md:space-x-5">
|
||||
<div>
|
||||
{/* eslint-disable @next/next/no-img-element */}
|
||||
<img
|
||||
src="/api/app-store/applecalendar/icon.svg"
|
||||
alt="Apple Calendar"
|
||||
className="h-12 w-12 max-w-2xl"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-gray-600">{t("connect_apple_server")}</h1>
|
||||
|
||||
<div className="mt-1 text-sm">
|
||||
{t("apple_server_generate_password")}{" "}
|
||||
<a
|
||||
className="text-indigo-400"
|
||||
href="https://appleid.apple.com/account/manage"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
https://appleid.apple.com/account/manage
|
||||
</a>
|
||||
. {t("credentials_stored_encrypted")}
|
||||
</div>
|
||||
<div className="my-2 mt-3">
|
||||
<Form
|
||||
form={form}
|
||||
handleSubmit={async (values) => {
|
||||
setErrorMessage("");
|
||||
const res = await fetch("/api/integrations/applecalendar/add", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(values),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!res.ok) {
|
||||
setErrorMessage(json?.message || t("something_went_wrong"));
|
||||
} else {
|
||||
router.push(json.url);
|
||||
}
|
||||
}}>
|
||||
<fieldset className="space-y-2" disabled={form.formState.isSubmitting}>
|
||||
<TextField
|
||||
required
|
||||
type="text"
|
||||
{...form.register("username")}
|
||||
label="Apple ID"
|
||||
placeholder="appleid@domain.com"
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="password"
|
||||
{...form.register("password")}
|
||||
label={t("password")}
|
||||
placeholder="•••••••••••••"
|
||||
autoComplete="password"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
{errorMessage && <Alert severity="error" title={errorMessage} className="my-4" />}
|
||||
<div className="mt-5 justify-end space-x-2 sm:mt-4 sm:flex">
|
||||
<Button type="button" color="secondary" onClick={() => router.back()}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Toaster position="bottom-right" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,9 +4,7 @@ import { useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Form, TextField } from "@calcom/ui/form/fields";
|
||||
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default function CalDavCalendarSetup() {
|
||||
const { t } = useLocale();
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default function CalDavCalendarSetup() {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
url: "",
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
});
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-200">
|
||||
<div className="m-auto rounded bg-white p-5 md:w-[560px] md:p-10">
|
||||
<div className="flex flex-col space-y-5 md:flex-row md:space-y-0 md:space-x-5">
|
||||
<div>
|
||||
{/* eslint-disable @next/next/no-img-element */}
|
||||
<img
|
||||
src="/api/app-store/caldavcalendar/icon.svg"
|
||||
alt="CalDav Calendar"
|
||||
className="h-12 w-12 max-w-2xl"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-10/12 flex-col">
|
||||
<h1 className="text-gray-600">{t("connect_caldav_server")}</h1>
|
||||
<div className="mt-1 text-sm">{t("credentials_stored_encrypted")}</div>
|
||||
<div className="my-2 mt-3">
|
||||
<Form
|
||||
form={form}
|
||||
handleSubmit={async (values) => {
|
||||
setErrorMessage("");
|
||||
const res = await fetch("/api/integrations/caldavcalendar/add", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(values),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!res.ok) {
|
||||
setErrorMessage(json?.message || t("something_went_wrong"));
|
||||
} else {
|
||||
router.push(json.url);
|
||||
}
|
||||
}}>
|
||||
<fieldset className="space-y-2" disabled={form.formState.isSubmitting}>
|
||||
<TextField
|
||||
required
|
||||
type="text"
|
||||
{...form.register("url")}
|
||||
label={t("calendar_url")}
|
||||
placeholder="https://example.com/calendar"
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="text"
|
||||
{...form.register("username")}
|
||||
label={t("username")}
|
||||
placeholder="rickroll"
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="password"
|
||||
{...form.register("password")}
|
||||
label={t("password")}
|
||||
placeholder="•••••••••••••"
|
||||
autoComplete="password"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
{errorMessage && <Alert severity="error" title={errorMessage} className="my-4" />}
|
||||
<div className="mt-5 justify-end space-x-2 sm:mt-4 sm:flex">
|
||||
<Button type="button" color="secondary" onClick={() => router.back()}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Toaster position="bottom-right" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -7,9 +7,7 @@ import { Toaster } from "react-hot-toast";
|
||||
import z from "zod";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import { Form, TextField } from "@calcom/ui/v2/core/form/fields";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
import { Button, Form, showToast, TextField } from "@calcom/ui/v2";
|
||||
|
||||
const formSchema = z.object({
|
||||
api_key: z.string(),
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useRouter } from "next/router";
|
||||
import { useState, useEffect } from "react";
|
||||
import { Check, X } from "react-feather";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import z from "zod";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button, Form, showToast, TextField } from "@calcom/ui/v2";
|
||||
|
||||
const formSchema = z.object({
|
||||
api_key: z.string(),
|
||||
});
|
||||
|
||||
export default function CloseComSetup() {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const [testPassed, setTestPassed] = useState<boolean | undefined>(undefined);
|
||||
const [testLoading, setTestLoading] = useState<boolean>(false);
|
||||
|
||||
const form = useForm<{
|
||||
api_key: string;
|
||||
}>({
|
||||
resolver: zodResolver(formSchema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
if (testPassed === false) {
|
||||
setTestPassed(undefined);
|
||||
}
|
||||
}, 3000);
|
||||
return () => clearTimeout(timer);
|
||||
}, [testPassed]);
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-200">
|
||||
<div className="m-auto rounded bg-white p-5 md:w-[520px] md:p-10">
|
||||
<div className="flex flex-col space-y-5 md:flex-row md:space-y-0 md:space-x-5">
|
||||
<div>
|
||||
{/* eslint-disable @next/next/no-img-element */}
|
||||
<img
|
||||
src="/api/app-store/closecomothercalendar/icon.svg"
|
||||
alt="Apple Calendar"
|
||||
className="h-12 w-12 max-w-2xl"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-gray-600">{t("provide_api_key")}</h1>
|
||||
|
||||
<div className="mt-1 text-sm">
|
||||
{t("generate_api_key_description")}{" "}
|
||||
<a
|
||||
className="text-indigo-400"
|
||||
href="https://app.close.com/settings/api/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer">
|
||||
Close.com
|
||||
</a>
|
||||
. {t("it_stored_encrypted")}
|
||||
</div>
|
||||
<div className="my-2 mt-3">
|
||||
<Form
|
||||
form={form}
|
||||
handleSubmit={async (values) => {
|
||||
const res = await fetch("/api/integrations/closecomothercalendar/add", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(values),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
if (res.ok) {
|
||||
router.push(json.url);
|
||||
} else {
|
||||
showToast(json.message, "error");
|
||||
}
|
||||
}}>
|
||||
<fieldset className="space-y-2" disabled={form.formState.isSubmitting}>
|
||||
<Controller
|
||||
name="api_key"
|
||||
control={form.control}
|
||||
render={({ field: { onBlur, onChange } }) => (
|
||||
<TextField
|
||||
className="my-0"
|
||||
onBlur={onBlur}
|
||||
disabled={testPassed === true}
|
||||
name="api_key"
|
||||
placeholder="api_xyz..."
|
||||
onChange={async (e) => {
|
||||
onChange(e.target.value);
|
||||
form.setValue("api_key", e.target.value);
|
||||
await form.trigger("api_key");
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</fieldset>
|
||||
<div className="mt-5 justify-end space-x-2 sm:mt-4 sm:flex">
|
||||
<Button type="button" color="secondary" onClick={() => router.back()}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
loading={testLoading}
|
||||
disabled={testPassed === true}
|
||||
StartIcon={testPassed !== undefined ? (testPassed ? Check : X) : undefined}
|
||||
className={
|
||||
testPassed !== undefined
|
||||
? testPassed
|
||||
? " !bg-green-100 !text-green-700 hover:bg-green-100"
|
||||
: "!border-red-700 bg-red-100 !text-red-700 hover:bg-red-100"
|
||||
: "secondary"
|
||||
}
|
||||
color={testPassed === true ? "minimal" : "secondary"}
|
||||
onClick={async () => {
|
||||
const check = await form.trigger("api_key");
|
||||
if (!check) return;
|
||||
const api_key = form.getValues("api_key");
|
||||
setTestLoading(true);
|
||||
const res = await fetch("/api/integrations/closecomothercalendar/check", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({ api_key }),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
setTestPassed(true);
|
||||
} else {
|
||||
setTestPassed(false);
|
||||
}
|
||||
setTestLoading(false);
|
||||
}}>
|
||||
{t(
|
||||
testPassed !== undefined ? (testPassed ? "test_passed" : "test_failed") : "test_api_key"
|
||||
)}
|
||||
</Button>
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Toaster position="bottom-right" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,9 +4,7 @@ import { useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Form, TextField } from "@calcom/ui/form/fields";
|
||||
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default function Exchange2013CalendarSetup() {
|
||||
const { t } = useLocale();
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default function Exchange2013CalendarSetup() {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
username: "",
|
||||
password: "",
|
||||
url: process.env.EXCHANGE_DEFAULT_EWS_URL || "",
|
||||
},
|
||||
});
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-200">
|
||||
<div className="m-auto rounded bg-white p-5 md:w-[560px] md:p-10">
|
||||
<div>
|
||||
<img
|
||||
src="/api/app-store/exchange2013calendar/icon.svg"
|
||||
alt="Exchange 2013 Calendar"
|
||||
className="h-12 w-12 max-w-2xl"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-10/12 flex-col">
|
||||
<h1 className="text-gray-600">{t("add_exchange2013")}</h1>
|
||||
<div className="mt-1 text-sm">{t("credentials_stored_encrypted")}</div>
|
||||
<div className="my-2 mt-3">
|
||||
<Form
|
||||
form={form}
|
||||
handleSubmit={async (values) => {
|
||||
setErrorMessage("");
|
||||
const res = await fetch("/api/integrations/exchange2013calendar/add", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(values),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!res.ok) {
|
||||
setErrorMessage(json?.message || t("something_went_wrong"));
|
||||
} else {
|
||||
router.push(json.url);
|
||||
}
|
||||
}}>
|
||||
<fieldset className="space-y-2" disabled={form.formState.isSubmitting}>
|
||||
<TextField
|
||||
required
|
||||
type="text"
|
||||
{...form.register("url")}
|
||||
label={t("calendar_url")}
|
||||
placeholder="https://example.com/Ews/Exchange.asmx"
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="text"
|
||||
{...form.register("username")}
|
||||
label="E-Mail"
|
||||
placeholder="rickroll@example.com"
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="password"
|
||||
{...form.register("password")}
|
||||
label="Password"
|
||||
placeholder="•••••••••••••"
|
||||
autoComplete="password"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
{errorMessage && <Alert severity="error" title={errorMessage} className="my-4" />}
|
||||
<div className="mt-5 justify-end space-x-2 sm:mt-4 sm:flex">
|
||||
<Button type="button" color="secondary" onClick={() => router.back()}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Toaster position="bottom-right" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,9 +4,7 @@ import { useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Form, TextField } from "@calcom/ui/form/fields";
|
||||
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default function Exchange2016CalendarSetup() {
|
||||
const { t } = useLocale();
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
|
||||
|
||||
export default function Exchange2016CalendarSetup() {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const form = useForm({
|
||||
defaultValues: {
|
||||
username: "",
|
||||
password: "",
|
||||
url: process.env.EXCHANGE_DEFAULT_EWS_URL || "",
|
||||
},
|
||||
});
|
||||
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-200">
|
||||
<div className="m-auto rounded bg-white p-5 md:w-[560px] md:p-10">
|
||||
<div>
|
||||
<img
|
||||
src="/api/app-store/exchange2016calendar/icon.svg"
|
||||
alt="Exchange 2016 Calendar"
|
||||
className="h-12 w-12 max-w-2xl"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex w-10/12 flex-col">
|
||||
<h1 className="text-gray-600">{t("add_exchange2016")}</h1>
|
||||
<div className="mt-1 text-sm">{t("credentials_stored_encrypted")}</div>
|
||||
<div className="my-2 mt-3">
|
||||
<Form
|
||||
form={form}
|
||||
handleSubmit={async (values) => {
|
||||
setErrorMessage("");
|
||||
const res = await fetch("/api/integrations/exchange2016calendar/add", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(values),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!res.ok) {
|
||||
setErrorMessage(json?.message || t("something_went_wrong"));
|
||||
} else {
|
||||
router.push(json.url);
|
||||
}
|
||||
}}>
|
||||
<fieldset className="space-y-2" disabled={form.formState.isSubmitting}>
|
||||
<TextField
|
||||
required
|
||||
type="text"
|
||||
{...form.register("url")}
|
||||
label={t("calendar_url")}
|
||||
placeholder="https://example.com/Ews/Exchange.asmx"
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="text"
|
||||
{...form.register("username")}
|
||||
label="E-Mail"
|
||||
placeholder="rickroll@example.com"
|
||||
/>
|
||||
<TextField
|
||||
required
|
||||
type="password"
|
||||
{...form.register("password")}
|
||||
label="Password"
|
||||
placeholder="•••••••••••••"
|
||||
autoComplete="password"
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
{errorMessage && <Alert severity="error" title={errorMessage} className="my-4" />}
|
||||
<div className="mt-5 justify-end space-x-2 sm:mt-4 sm:flex">
|
||||
<Button type="button" color="secondary" onClick={() => router.back()}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Toaster position="bottom-right" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -6,9 +6,16 @@ import { Toaster } from "react-hot-toast";
|
||||
import z from "zod";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button, Select, Switch } from "@calcom/ui";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import { EmailField, Form, Label, PasswordField, TextField } from "@calcom/ui/form/fields";
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
EmailField,
|
||||
Form,
|
||||
PasswordField,
|
||||
TextField,
|
||||
SelectField,
|
||||
Switch,
|
||||
} from "@calcom/ui/v2";
|
||||
|
||||
import { ExchangeAuthentication } from "../../enums";
|
||||
|
||||
@@ -42,16 +49,16 @@ export default function ExchangeSetup() {
|
||||
{ value: ExchangeAuthentication.STANDARD, label: t("exchange_authentication_standard") },
|
||||
{ value: ExchangeAuthentication.NTLM, label: t("exchange_authentication_ntlm") },
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-screen bg-gray-200">
|
||||
<div className="m-auto rounded bg-white p-5 md:w-[560px] md:p-10">
|
||||
<div className="flex flex-col space-y-5 md:flex-row md:space-y-0 md:space-x-5">
|
||||
<div className="flex">
|
||||
<div>
|
||||
{/* eslint-disable @next/next/no-img-element */}
|
||||
<img
|
||||
src="/api/app-store/exchangecalendar/icon.svg"
|
||||
alt="Microsoft Exchange Calendar"
|
||||
alt="Microsoft Exchange"
|
||||
className="h-12 w-12 max-w-2xl"
|
||||
/>
|
||||
</div>
|
||||
@@ -98,24 +105,21 @@ export default function ExchangeSetup() {
|
||||
label={t("password")}
|
||||
autoComplete="password"
|
||||
/>
|
||||
<div>
|
||||
<Label>{t("exchange_authentication")}</Label>
|
||||
<Controller
|
||||
name="authenticationMethod"
|
||||
control={form.control}
|
||||
render={({ field: { onChange } }) => (
|
||||
<Select
|
||||
options={authenticationMethods}
|
||||
defaultValue={authenticationMethods[0]}
|
||||
className="mt-1"
|
||||
onChange={async (authentication) => {
|
||||
onChange(authentication?.value);
|
||||
form.setValue("authenticationMethod", authentication!.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Controller
|
||||
name="authenticationMethod"
|
||||
control={form.control}
|
||||
render={({ field: { onChange } }) => (
|
||||
<SelectField
|
||||
label={t("exchange_authentication")}
|
||||
options={authenticationMethods}
|
||||
defaultValue={authenticationMethods[0]}
|
||||
onChange={async (authentication) => {
|
||||
onChange(authentication?.value);
|
||||
form.setValue("authenticationMethod", authentication!.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Switch
|
||||
label={t("exchange_compression")}
|
||||
name="useCompression"
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
import z from "zod";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import {
|
||||
Alert,
|
||||
Button,
|
||||
EmailField,
|
||||
Form,
|
||||
PasswordField,
|
||||
TextField,
|
||||
SelectField,
|
||||
Switch,
|
||||
} from "@calcom/ui/v2";
|
||||
|
||||
import { ExchangeAuthentication } from "../../../enums";
|
||||
|
||||
interface IFormData {
|
||||
url: string;
|
||||
username: string;
|
||||
password: string;
|
||||
authenticationMethod: ExchangeAuthentication;
|
||||
useCompression: boolean;
|
||||
}
|
||||
|
||||
const schema = z
|
||||
.object({
|
||||
url: z.string().url(),
|
||||
username: z.string().email(),
|
||||
password: z.string(),
|
||||
authenticationMethod: z.number().default(ExchangeAuthentication.STANDARD),
|
||||
useCompression: z.boolean().default(false),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export default function ExchangeSetup() {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
const form = useForm<IFormData>({
|
||||
defaultValues: { authenticationMethod: ExchangeAuthentication.STANDARD },
|
||||
resolver: zodResolver(schema),
|
||||
});
|
||||
const authenticationMethods = [
|
||||
{ value: ExchangeAuthentication.STANDARD, label: t("exchange_authentication_standard") },
|
||||
{ value: ExchangeAuthentication.NTLM, label: t("exchange_authentication_ntlm") },
|
||||
];
|
||||
return (
|
||||
<>
|
||||
<div className="flex h-screen bg-gray-200">
|
||||
<div className="m-auto rounded bg-white p-5 md:w-[560px] md:p-10">
|
||||
<div className="flex flex-col space-y-5 md:flex-row md:space-y-0 md:space-x-5">
|
||||
<div>
|
||||
{/* eslint-disable @next/next/no-img-element */}
|
||||
<img
|
||||
src="/api/app-store/exchangecalendar/icon.svg"
|
||||
alt="Microsoft Exchange"
|
||||
className="h-12 w-12 max-w-2xl"
|
||||
/>
|
||||
</div>
|
||||
<div className="grow">
|
||||
<h1 className="text-gray-600">{t("exchange_add")}</h1>
|
||||
<div className="text-sm">{t("credentials_stored_encrypted")}</div>
|
||||
<div className="my-2 mt-5">
|
||||
<Form
|
||||
form={form}
|
||||
handleSubmit={async (values) => {
|
||||
setErrorMessage("");
|
||||
const res = await fetch("/api/integrations/exchangecalendar/add", {
|
||||
method: "POST",
|
||||
body: JSON.stringify(values),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
const json = await res.json();
|
||||
if (!res.ok) {
|
||||
setErrorMessage(json?.message || t("something_went_wrong"));
|
||||
} else {
|
||||
router.push(json.url);
|
||||
}
|
||||
}}>
|
||||
<fieldset className="space-y-4" disabled={form.formState.isSubmitting}>
|
||||
<TextField
|
||||
required
|
||||
type="url"
|
||||
{...form.register("url")}
|
||||
label={t("url")}
|
||||
placeholder="https://example.com/Ews/Exchange.asmx"
|
||||
inputMode="url"
|
||||
/>
|
||||
<EmailField
|
||||
required
|
||||
{...form.register("username")}
|
||||
label={t("email_address")}
|
||||
placeholder="john.doe@example.com"
|
||||
/>
|
||||
<PasswordField
|
||||
required
|
||||
{...form.register("password")}
|
||||
label={t("password")}
|
||||
autoComplete="password"
|
||||
/>
|
||||
<Controller
|
||||
name="authenticationMethod"
|
||||
control={form.control}
|
||||
render={({ field: { onChange } }) => (
|
||||
<SelectField
|
||||
label={t("exchange_authentication")}
|
||||
options={authenticationMethods}
|
||||
defaultValue={authenticationMethods[0]}
|
||||
onChange={async (authentication) => {
|
||||
onChange(authentication?.value);
|
||||
form.setValue("authenticationMethod", authentication!.value);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Switch
|
||||
label={t("exchange_compression")}
|
||||
name="useCompression"
|
||||
onCheckedChange={async (alt) => {
|
||||
form.setValue("useCompression", alt);
|
||||
}}
|
||||
/>
|
||||
</fieldset>
|
||||
{errorMessage && <Alert severity="error" title={errorMessage} className="my-4" />}
|
||||
<div className="mt-4 flex justify-end space-x-2">
|
||||
<Button type="button" color="secondary" onClick={() => router.back()}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button type="submit" loading={form.formState.isSubmitting}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Toaster position="bottom-right" />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { ClipboardCopyIcon } from "@heroicons/react/solid";
|
||||
import { Trans } from "next-i18next";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
@@ -6,8 +5,8 @@ import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button, Loader, Tooltip } from "@calcom/ui";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
import { ClipboardCopyIcon } from "@calcom/ui/Icon";
|
||||
import { Button, Loader, showToast, Tooltip } from "@calcom/ui/v2";
|
||||
|
||||
export interface IZapierSetupProps {
|
||||
inviteLink: string;
|
||||
@@ -19,7 +18,7 @@ export default function ZapierSetup(props: IZapierSetupProps) {
|
||||
const [newApiKey, setNewApiKey] = useState("");
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const integrations = trpc.useQuery(["viewer.integrations", { variant: "other" }]);
|
||||
const integrations = trpc.useQuery(["viewer.integrations", { variant: "automation" }]);
|
||||
const oldApiKey = trpc.useQuery(["viewer.apiKeys.findKeyOfType", { appId: ZAPIER }]);
|
||||
|
||||
const deleteApiKey = trpc.useMutation("viewer.apiKeys.delete");
|
||||
@@ -42,11 +41,7 @@ export default function ZapierSetup(props: IZapierSetupProps) {
|
||||
}
|
||||
|
||||
if (integrations.isLoading) {
|
||||
return (
|
||||
<div className="absolute z-50 flex h-screen w-full items-center bg-gray-200">
|
||||
<Loader />
|
||||
</div>
|
||||
);
|
||||
return <div className="absolute z-50 flex h-screen w-full items-center bg-gray-200" />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -69,16 +64,18 @@ export default function ZapierSetup(props: IZapierSetupProps) {
|
||||
) : (
|
||||
<>
|
||||
<div className="mt-1 text-xl">{t("your_unique_api_key")}</div>
|
||||
<div className="my-2 mt-3 flex">
|
||||
<div className="w-full rounded bg-gray-100 py-2 pl-2 pr-5">{newApiKey}</div>
|
||||
<Tooltip side="top" content="copy">
|
||||
<div className="my-2 mt-3 flex-wrap sm:flex sm:flex-nowrap">
|
||||
<code className="h-full w-full whitespace-pre-wrap rounded-md bg-gray-100 py-2 pl-2 pr-2 sm:rounded-r-none sm:pr-5">
|
||||
{newApiKey}
|
||||
</code>
|
||||
<Tooltip side="top" content={t("copy_to_clipboard")}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(newApiKey);
|
||||
showToast(t("api_key_copied"), "success");
|
||||
}}
|
||||
type="button"
|
||||
className="rounded-l-none px-4 text-base">
|
||||
className="mt-4 text-base sm:mt-0 sm:rounded-l-none">
|
||||
<ClipboardCopyIcon className="mr-2 h-5 w-5 text-neutral-100" />
|
||||
{t("copy")}
|
||||
</Button>
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
import { GetStaticPropsContext } from "next";
|
||||
|
||||
import getAppKeysFromSlug from "../../../../_utils/getAppKeysFromSlug";
|
||||
|
||||
export interface IZapierSetupProps {
|
||||
inviteLink: string;
|
||||
}
|
||||
|
||||
export const getStaticProps = async (ctx: GetStaticPropsContext) => {
|
||||
if (typeof ctx.params?.slug !== "string") return { notFound: true } as const;
|
||||
let inviteLink = "";
|
||||
const appKeys = await getAppKeysFromSlug("zapier");
|
||||
if (typeof appKeys.invite_link === "string") inviteLink = appKeys.invite_link;
|
||||
|
||||
return {
|
||||
props: {
|
||||
inviteLink,
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -1,126 +0,0 @@
|
||||
import { Trans } from "next-i18next";
|
||||
import Link from "next/link";
|
||||
import { useState } from "react";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { ClipboardCopyIcon } from "@calcom/ui/Icon";
|
||||
import { Button, Loader, showToast, Tooltip } from "@calcom/ui/v2";
|
||||
|
||||
export interface IZapierSetupProps {
|
||||
inviteLink: string;
|
||||
}
|
||||
|
||||
const ZAPIER = "zapier";
|
||||
|
||||
export default function ZapierSetup(props: IZapierSetupProps) {
|
||||
const [newApiKey, setNewApiKey] = useState("");
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const integrations = trpc.useQuery(["viewer.integrations", { variant: "automation" }]);
|
||||
const oldApiKey = trpc.useQuery(["viewer.apiKeys.findKeyOfType", { appId: ZAPIER }]);
|
||||
|
||||
const deleteApiKey = trpc.useMutation("viewer.apiKeys.delete");
|
||||
const zapierCredentials: { credentialIds: number[] } | undefined = integrations.data?.items.find(
|
||||
(item: { type: string }) => item.type === "zapier_automation"
|
||||
);
|
||||
const [credentialId] = zapierCredentials?.credentialIds || [false];
|
||||
const showContent = integrations.data && integrations.isSuccess && credentialId;
|
||||
const isCalDev = process.env.NEXT_PUBLIC_WEBAPP_URL === "https://app.cal.dev";
|
||||
|
||||
async function createApiKey() {
|
||||
const event = { note: "Zapier", expiresAt: null, appId: ZAPIER };
|
||||
const apiKey = await utils.client.mutation("viewer.apiKeys.create", event);
|
||||
if (oldApiKey.data) {
|
||||
deleteApiKey.mutate({
|
||||
id: oldApiKey.data.id,
|
||||
});
|
||||
}
|
||||
setNewApiKey(apiKey);
|
||||
}
|
||||
|
||||
if (integrations.isLoading) {
|
||||
return <div className="absolute z-50 flex h-screen w-full items-center bg-gray-200" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-screen bg-gray-200">
|
||||
{showContent ? (
|
||||
<div className="m-auto max-w-[43em] overflow-auto rounded bg-white pb-10 md:p-10">
|
||||
<div className="md:flex md:flex-row">
|
||||
<div className="invisible md:visible">
|
||||
<img className="h-11" src="/api/app-store/zapier/icon.svg" alt="Zapier Logo" />
|
||||
</div>
|
||||
<div className="ml-2 mr-2 md:ml-5">
|
||||
<div className="text-gray-600">{t("setting_up_zapier")}</div>
|
||||
{!newApiKey ? (
|
||||
<>
|
||||
<div className="mt-1 text-xl">{t("generate_api_key")}:</div>
|
||||
<Button onClick={() => createApiKey()} className="mt-4 mb-4">
|
||||
{t("generate_api_key")}
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="mt-1 text-xl">{t("your_unique_api_key")}</div>
|
||||
<div className="my-2 mt-3 flex-wrap sm:flex sm:flex-nowrap">
|
||||
<code className="h-full w-full whitespace-pre-wrap rounded-md bg-gray-100 py-2 pl-2 pr-2 sm:rounded-r-none sm:pr-5">
|
||||
{newApiKey}
|
||||
</code>
|
||||
<Tooltip side="top" content={t("copy_to_clipboard")}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(newApiKey);
|
||||
showToast(t("api_key_copied"), "success");
|
||||
}}
|
||||
type="button"
|
||||
className="mt-4 text-base sm:mt-0 sm:rounded-l-none">
|
||||
<ClipboardCopyIcon className="mr-2 h-5 w-5 text-neutral-100" />
|
||||
{t("copy")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
<div className="mt-2 mb-5 text-sm font-semibold text-gray-600">
|
||||
{t("copy_safe_api_key")}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
<ol className="mt-5 mb-5 ml-5 mr-5 list-decimal">
|
||||
{isCalDev && (
|
||||
<li>
|
||||
{t("go_to")}
|
||||
<a href={props.inviteLink} className="text-orange-600 underline">
|
||||
{t("zapier_invite_link")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
<Trans i18nKey="zapier_setup_instructions">
|
||||
<li>Log into your Zapier account and create a new Zap.</li>
|
||||
<li>Select Cal.com as your Trigger app. Also choose a Trigger event.</li>
|
||||
<li>Choose your account and then enter your Unique API Key.</li>
|
||||
<li>Test your Trigger.</li>
|
||||
<li>You're set!</li>
|
||||
</Trans>
|
||||
</ol>
|
||||
<Link href="/apps/installed" passHref={true}>
|
||||
<Button color="secondary">{t("done")}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-5 ml-5">
|
||||
<div>{t("install_zapier_app")}</div>
|
||||
<div className="mt-3">
|
||||
<Link href="/apps/zapier" passHref={true}>
|
||||
<Button>{t("go_to_app_store")}</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<Toaster position="bottom-right" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user