CalDav Setup page (#3038)

This commit is contained in:
Leo Giovanetti
2022-06-11 19:33:04 +02:00
committed by GitHub
parent 567a7c8f12
commit 8fa0d0f8bb
9 changed files with 130 additions and 123 deletions
@@ -853,6 +853,11 @@
"copy_safe_api_key": "Copy this API key and save it somewhere safe. If you lose this key you have to generate a new one.",
"zapier_setup_instructions": "<0>Log into your Zapier account and create a new Zap.</0><1>Select Cal.com as your Trigger app. Also choose a Trigger event.</1><2>Choose your account and then enter your Unique API Key.</2><3>Test your Trigger.</3><4>You're set!</4>",
"install_zapier_app": "Please first install the Zapier App in the app store.",
"connect_apple_server": "Connect to Apple Server",
"connect_caldav_server": "Connect to CalDav Server",
"calendar_url": "Calendar URL",
"apple_server_generate_password": "Generate an app specific password to use with Cal.com at",
"credentials_stored_encrypted": "Your credentials will be stored and encrypted.",
"go_to_app_store": "Go to App Store",
"calendar_error": "Something went wrong, try reconnecting your calendar with all necessary permissions",
"set_your_phone_number": "Set a phone number for the meeting",
@@ -5,6 +5,9 @@ export const AppSetupPageMap = {
"apple-calendar": {
getStaticProps: null,
},
"caldav-calendar": {
getStaticProps: null,
},
};
export const getStaticProps = async (ctx: GetStaticPropsContext) => {
@@ -4,6 +4,7 @@ import { DynamicComponent } from "../../_components/DynamicComponent";
export const AppSetupMap = {
"apple-calendar": dynamic(() => import("../../applecalendar/pages/setup")),
"caldav-calendar": dynamic(() => import("../../caldavcalendar/pages/setup")),
zapier: dynamic(() => import("../../zapier/pages/setup")),
};
@@ -33,10 +33,10 @@ export default function AppleCalendarSetup() {
/>
</div>
<div>
<h1 className="text-gray-600">Connect to Apple Server</h1>
<h1 className="text-gray-600">{t("connect_apple_server")}</h1>
<div className="mt-1 text-sm">
Generate an app specific password to use with Cal.com at{" "}
{t("apple_server_generate_password")}{" "}
<a
className="text-indigo-400"
href="https://appleid.apple.com/account/manage"
@@ -44,7 +44,7 @@ export default function AppleCalendarSetup() {
rel="noopener noreferrer">
https://appleid.apple.com/account/manage
</a>
. Your credentials will be stored and encrypted.
. {t("credentials_stored_encrypted")}
</div>
<div className="my-2 mt-3">
<Form
@@ -60,7 +60,7 @@ export default function AppleCalendarSetup() {
});
const json = await res.json();
if (!res.ok) {
setErrorMessage(json?.message || "Something went wrong");
setErrorMessage(json?.message || t("something_went_wrong"));
} else {
router.push(json.url);
}
@@ -70,23 +70,26 @@ export default function AppleCalendarSetup() {
required
type="text"
{...form.register("username")}
label="Username"
label={t("username")}
placeholder="rickroll"
/>
<TextField
required
type="password"
{...form.register("password")}
label="Password"
label={t("password")}
placeholder="•••••••••••••"
autoComplete="password"
/>
</fieldset>
{errorMessage && <Alert severity="error" title={errorMessage} className="my-4" />}
<div className="mt-5 sm:mt-4 sm:flex sm:flex-row-reverse">
<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}>
Save
{t("save")}
</Button>
</div>
</Form>
+5 -1
View File
@@ -44,6 +44,10 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
return res.status(500).json({ message: "Could not add this caldav account" });
}
return res.status(200).json({});
return res.status(200).json({ url: "/apps/installed" });
}
if (req.method === "GET") {
return res.status(200).json({ url: "/apps/caldav-calendar/setup" });
}
}
@@ -1,106 +0,0 @@
import { useState } from "react";
import { useForm } from "react-hook-form";
import { Alert } from "@calcom/ui/Alert";
import Button from "@calcom/ui/Button";
import {
Dialog,
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogProps,
} from "@calcom/ui/Dialog";
import { Form, TextField } from "@calcom/ui/form/fields";
export const ADD_INTEGRATION_FORM_TITLE = "addCalDav";
export type AddIntegrationRequest = {
url: string;
username: string;
password: string;
};
export function AddIntegrationModal(props: DialogProps) {
const form = useForm({
defaultValues: {
url: "",
username: "",
password: "",
},
});
const [errorMessage, setErrorMessage] = useState("");
return (
<Dialog name={ADD_INTEGRATION_FORM_TITLE} {...props}>
<DialogContent>
<DialogHeader
title="Connect to CalDav Server"
subtitle="Your credentials will be stored and encrypted."
/>
<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 || "Something went wrong");
} else {
props.onOpenChange?.(false);
}
}}>
<fieldset className="space-y-2" disabled={form.formState.isSubmitting}>
<TextField
required
type="text"
{...form.register("url")}
label="Calendar URL"
placeholder="https://example.com/calendar"
/>
<TextField
required
type="text"
{...form.register("username")}
label="Username"
placeholder="rickroll"
/>
<TextField
required
type="password"
{...form.register("password")}
label="Password"
placeholder="•••••••••••••"
autoComplete="password"
/>
</fieldset>
{errorMessage && <Alert severity="error" title={errorMessage} className="my-4" />}
<DialogFooter>
<DialogClose
onClick={() => {
props.onOpenChange?.(false);
}}
asChild>
<Button type="button" color="secondary" tabIndex={-1}>
Cancel
</Button>
</DialogClose>
<Button type="submit" loading={form.formState.isSubmitting}>
Save
</Button>
</DialogFooter>
</Form>
</DialogContent>
</Dialog>
);
}
export default AddIntegrationModal;
@@ -1,20 +1,18 @@
import { useState } from "react";
import type { InstallAppButtonProps } from "@calcom/app-store/types";
import { InstallAppButtonProps } from "../../types";
import AddIntegration from "./AddIntegration";
import useAddAppMutation from "../../_utils/useAddAppMutation";
export default function InstallAppButton(props: InstallAppButtonProps) {
const [isModalOpen, setIsModalOpen] = useState(false);
const mutation = useAddAppMutation("caldav_calendar");
return (
<>
{props.render({
onClick() {
setIsModalOpen(true);
mutation.mutate("");
},
disabled: isModalOpen,
loading: mutation.isLoading,
})}
<AddIntegration open={isModalOpen} onOpenChange={setIsModalOpen} />
</>
);
}
@@ -1,2 +1 @@
export { default as AddIntegration } from "./AddIntegration";
export { default as InstallAppButton } from "./InstallAppButton";
@@ -0,0 +1,100 @@
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 } from "@calcom/ui/Alert";
import Button from "@calcom/ui/Button";
import { Form, TextField } from "@calcom/ui/form/fields";
export default function AppleCalendarSetup() {
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>
);
}