From d8dac426eb0c504183a95c1aaa38cd99f6b9bdd4 Mon Sep 17 00:00:00 2001 From: Alex Johansson Date: Mon, 18 Oct 2021 09:02:25 +0200 Subject: [PATCH] refactor webhooks UI (#982) --- components/Dialog.tsx | 10 +- components/form/fields.tsx | 21 +- components/ui/Switch.tsx | 11 +- components/webhook/EditWebhook.tsx | 179 ------------- components/webhook/WebhookList.tsx | 23 -- components/webhook/WebhookListItem.tsx | 106 -------- package.json | 2 +- pages/integrations/embed.tsx | 285 --------------------- pages/integrations/index.tsx | 332 ++++++++++++++++++++++++- public/static/locales/en/common.json | 1 + server/createContext.ts | 1 + server/routers/viewer.tsx | 11 +- yarn.lock | 8 +- 13 files changed, 374 insertions(+), 616 deletions(-) delete mode 100644 components/webhook/EditWebhook.tsx delete mode 100644 components/webhook/WebhookList.tsx delete mode 100644 components/webhook/WebhookListItem.tsx delete mode 100644 pages/integrations/embed.tsx diff --git a/components/Dialog.tsx b/components/Dialog.tsx index 9465662c9f..86e4de64a0 100644 --- a/components/Dialog.tsx +++ b/components/Dialog.tsx @@ -25,17 +25,17 @@ export const DialogContent = React.forwardRef -
{subtitle}
+ {props.subtitle &&
{props.subtitle}
} ); } diff --git a/components/form/fields.tsx b/components/form/fields.tsx index 88b0c7155b..f4b27cc8c7 100644 --- a/components/form/fields.tsx +++ b/components/form/fields.tsx @@ -4,7 +4,8 @@ import { FormProvider, UseFormReturn } from "react-hook-form"; import classNames from "@lib/classNames"; -export const Input = forwardRef(function Input(props, ref) { +type InputProps = Omit & { name: string }; +export const Input = forwardRef(function Input(props, ref) { return ( } & J ); } ); + +export function FieldsetLegend(props: JSX.IntrinsicElements["legend"]) { + return ( + + {props.children} + + ); +} + +export function InputGroupBox(props: JSX.IntrinsicElements["div"]) { + return ( +
+ {props.children} +
+ ); +} diff --git a/components/ui/Switch.tsx b/components/ui/Switch.tsx index 20bbb386eb..d645661ff9 100644 --- a/components/ui/Switch.tsx +++ b/components/ui/Switch.tsx @@ -1,13 +1,14 @@ import { useId } from "@radix-ui/react-id"; import * as Label from "@radix-ui/react-label"; import * as PrimitiveSwitch from "@radix-ui/react-switch"; -import { useState } from "react"; +import React, { useState } from "react"; -function classNames(...classes) { - return classes.filter(Boolean).join(" "); -} +import classNames from "@lib/classNames"; -export default function Switch(props) { +type SwitchProps = React.ComponentProps & { + label: string; +}; +export default function Switch(props: SwitchProps) { const { label, onCheckedChange, ...primitiveProps } = props; const [checked, setChecked] = useState(props.defaultChecked || false); diff --git a/components/webhook/EditWebhook.tsx b/components/webhook/EditWebhook.tsx deleted file mode 100644 index 5487a5c634..0000000000 --- a/components/webhook/EditWebhook.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import { ArrowLeftIcon } from "@heroicons/react/solid"; -import { useEffect, useRef, useState } from "react"; - -import { useLocale } from "@lib/hooks/useLocale"; -import showToast from "@lib/notification"; -import { Webhook } from "@lib/webhook"; - -import Button from "@components/ui/Button"; -import Switch from "@components/ui/Switch"; - -export default function EditTeam(props: { webhook: Webhook; onCloseEdit: () => void }) { - const { t } = useLocale(); - const [bookingCreated, setBookingCreated] = useState( - props.webhook.eventTriggers.includes("booking_created") - ); - const [bookingRescheduled, setBookingRescheduled] = useState( - props.webhook.eventTriggers.includes("booking_rescheduled") - ); - const [bookingCancelled, setBookingCancelled] = useState( - props.webhook.eventTriggers.includes("booking_cancelled") - ); - const [webhookEnabled, setWebhookEnabled] = useState(props.webhook.active); - const [webhookEventTrigger, setWebhookEventTriggers] = useState([ - "BOOKING_CREATED", - "BOOKING_RESCHEDULED", - "BOOKING_CANCELLED", - ]); - const [btnLoading, setBtnLoading] = useState(false); - const subUrlRef = useRef() as React.MutableRefObject; - - useEffect(() => { - const arr = []; - bookingCreated && arr.push("BOOKING_CREATED"); - bookingRescheduled && arr.push("BOOKING_RESCHEDULED"); - bookingCancelled && arr.push("BOOKING_CANCELLED"); - setWebhookEventTriggers(arr); - }, [bookingCreated, bookingRescheduled, bookingCancelled, webhookEnabled]); - - const handleErrors = async (resp: Response) => { - if (!resp.ok) { - const err = await resp.json(); - throw new Error(err.message); - } - return resp.json(); - }; - - const updateWebhookHandler = (event) => { - event.preventDefault(); - setBtnLoading(true); - return fetch("/api/webhooks/" + props.webhook.id, { - method: "PATCH", - body: JSON.stringify({ - subscriberUrl: subUrlRef.current.value, - eventTriggers: webhookEventTrigger, - enabled: webhookEnabled, - }), - headers: { - "Content-Type": "application/json", - }, - }) - .then(handleErrors) - .then(() => { - showToast(t("webhook_updated_successfully"), "success"); - setBtnLoading(false); - }); - }; - - return ( -
-
-
- -
-
-
-

{t("manage_your_webhook")}

-
-
-
-
-
-
- - - - {" "} - {t("event_triggers")}{" "} - -
-
-
-

{t("booking_created")}

-
-
- { - setBookingCreated(!bookingCreated); - }} - /> -
-
-
-
-

{t("booking_rescheduled")}

-
-
- { - setBookingRescheduled(!bookingRescheduled); - }} - /> -
-
-
-
-

{t("booking_cancelled")}

-
-
- { - setBookingCancelled(!bookingCancelled); - }} - /> -
-
-
- - {" "} - {t("webhook_status")}{" "} - -
-
-
-

{t("webhook_enabled")}

-
-
- { - setWebhookEnabled(!webhookEnabled); - }} - /> -
-
-
-
-
- -
-
-
-
-
- ); -} diff --git a/components/webhook/WebhookList.tsx b/components/webhook/WebhookList.tsx deleted file mode 100644 index a046a039b4..0000000000 --- a/components/webhook/WebhookList.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { Webhook } from "@lib/webhook"; - -import WebhookListItem from "./WebhookListItem"; - -export default function WebhookList(props: { - webhooks: Webhook[]; - onChange: () => void; - onEditWebhook: (webhook: Webhook) => void; -}) { - return ( -
-
    - {props.webhooks.map((webhook: Webhook) => ( - props.onEditWebhook(webhook)}> - ))} -
-
- ); -} diff --git a/components/webhook/WebhookListItem.tsx b/components/webhook/WebhookListItem.tsx deleted file mode 100644 index 9f64837a5c..0000000000 --- a/components/webhook/WebhookListItem.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { TrashIcon, PencilAltIcon } from "@heroicons/react/outline"; - -import { useLocale } from "@lib/hooks/useLocale"; -import showToast from "@lib/notification"; -import { Webhook } from "@lib/webhook"; - -import { Dialog, DialogTrigger } from "@components/Dialog"; -import { Tooltip } from "@components/Tooltip"; -import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent"; -import Button from "@components/ui/Button"; - -export default function WebhookListItem(props: { - onChange: () => void; - key: string; - webhook: Webhook; - onEditWebhook: () => void; -}) { - const { t } = useLocale(); - const handleErrors = async (resp: Response) => { - if (!resp.ok) { - const err = await resp.json(); - throw new Error(err.message); - } - return resp.json(); - }; - - const deleteWebhook = (webhookId: string) => { - fetch("/api/webhooks/" + webhookId, { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - }) - .then(handleErrors) - .then(() => { - showToast(t("webhook_removed_successfully"), "success"); - props.onChange(); - }); - }; - - return ( -
  • -
    -
    - - {props.webhook.eventTriggers.map((eventTrigger, ind) => ( - - {t(`${eventTrigger}`)} - - ))} - -
    -
    -
    - {props.webhook.subscriberUrl} -
    -
    -
    - {!props.webhook.active && ( - - {t("disabled")} - - )} - {!!props.webhook.active && ( - - {t("enabled")} - - )} - - - - - - - - - - - { - deleteWebhook(props.webhook.id); - }}> - {t("delete_webhook_confirmation_message")} - - -
    -
    -
  • - ); -} diff --git a/package.json b/package.json index 4c965bab58..19c189ebf5 100644 --- a/package.json +++ b/package.json @@ -77,7 +77,7 @@ "react": "17.0.2", "react-dom": "17.0.2", "react-easy-crop": "^3.5.2", - "react-hook-form": "^7.16.1", + "react-hook-form": "^7.17.4", "react-hot-toast": "^2.1.0", "react-intl": "^5.20.7", "react-multi-email": "^0.5.3", diff --git a/pages/integrations/embed.tsx b/pages/integrations/embed.tsx deleted file mode 100644 index ca1504a255..0000000000 --- a/pages/integrations/embed.tsx +++ /dev/null @@ -1,285 +0,0 @@ -import { useSession } from "next-auth/client"; -import Image from "next/image"; -import { useEffect, useRef, useState } from "react"; - -import classNames from "@lib/classNames"; -import { useLocale } from "@lib/hooks/useLocale"; -import { trpc } from "@lib/trpc"; -import { Webhook } from "@lib/webhook"; - -import { Dialog, DialogClose, DialogContent, DialogHeader, DialogTrigger } from "@components/Dialog"; -import { List, ListItem, ListItemText, ListItemTitle } from "@components/List"; -import Loader from "@components/Loader"; -import { ShellSubHeading } from "@components/Shell"; -import Button from "@components/ui/Button"; -import Switch from "@components/ui/Switch"; -import EditWebhook from "@components/webhook/EditWebhook"; -import WebhookList from "@components/webhook/WebhookList"; - -export default function Embed() { - const user = trpc.useQuery(["viewer.me"]).data; - const [, loading] = useSession(); - const { t } = useLocale(); - - const [isLoading, setLoading] = useState(false); - const [bookingCreated, setBookingCreated] = useState(true); - const [bookingRescheduled, setBookingRescheduled] = useState(true); - const [bookingCancelled, setBookingCancelled] = useState(true); - const [editWebhookEnabled, setEditWebhookEnabled] = useState(false); - const [webhooks, setWebhooks] = useState([]); - const [webhookToEdit, setWebhookToEdit] = useState(); - const [webhookEventTrigger, setWebhookEventTriggers] = useState([ - "BOOKING_CREATED", - "BOOKING_RESCHEDULED", - "BOOKING_CANCELLED", - ]); - - const subUrlRef = useRef() as React.MutableRefObject; - - useEffect(() => { - const arr = []; - bookingCreated && arr.push("BOOKING_CREATED"); - bookingRescheduled && arr.push("BOOKING_RESCHEDULED"); - bookingCancelled && arr.push("BOOKING_CANCELLED"); - setWebhookEventTriggers(arr); - }, [bookingCreated, bookingRescheduled, bookingCancelled]); - - useEffect(() => { - getWebhooks(); - }, []); - - const iframeTemplate = ``; - const htmlTemplate = `${t( - "schedule_a_meeting" - )}${iframeTemplate}`; - const handleErrors = async (resp: Response) => { - if (!resp.ok) { - const err = await resp.json(); - throw new Error(err.message); - } - return resp.json(); - }; - - const getWebhooks = () => { - fetch("/api/webhook") - .then(handleErrors) - .then((data) => { - setWebhooks( - data.webhooks.map((webhook: Webhook) => { - return { - ...webhook, - eventTriggers: webhook.eventTriggers.map((eventTrigger: string) => eventTrigger.toLowerCase()), - }; - }) - ); - console.log(data.webhooks); - }) - .catch(console.log); - setLoading(false); - }; - - const createWebhook = () => { - setLoading(true); - fetch("/api/webhook", { - method: "POST", - body: JSON.stringify({ - subscriberUrl: subUrlRef.current.value, - eventTriggers: webhookEventTrigger, - }), - headers: { - "Content-Type": "application/json", - }, - }) - .then(getWebhooks) - .catch(console.log); - }; - - const editWebhook = (webhook: Webhook) => { - setEditWebhookEnabled(true); - setWebhookToEdit(webhook); - }; - - const onCloseEdit = () => { - getWebhooks(); - setEditWebhookEnabled(false); - }; - - if (loading) { - return ; - } - - return ( - <> - {!editWebhookEnabled && ( - <> - - - -
    - Webhooks -
    - Webhooks - Automation -
    -
    - - - - - - -
    -
    - - - - {" "} - {t("event_triggers")}{" "} - -
    -
    -
    -

    {t("booking_created")}

    -
    -
    - { - setBookingCreated(!bookingCreated); - }} - /> -
    -
    -
    -
    -

    {t("booking_rescheduled")}

    -
    -
    - { - setBookingRescheduled(!bookingRescheduled); - }} - /> -
    -
    -
    -
    -

    {t("booking_cancelled")}

    -
    -
    - { - setBookingCancelled(!bookingCancelled); - }} - /> -
    -
    -
    -
    -
    - - - - - - -
    -
    -
    -
    -
    -
    -
    -
    - -
    -
    -
    - {!!webhooks.length && ( - - )} -
    -
    -
    - - -
    -
    -

    -

    -
    -
    -
    - -
    -