CalDav Setup page (#3038)
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user