import { ChevronRightIcon, PencilAltIcon, SwitchHorizontalIcon, TrashIcon } from "@heroicons/react/outline"; import { ClipboardIcon } from "@heroicons/react/solid"; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible"; import Image from "next/image"; import React, { useEffect, useState } from "react"; import { Controller, useForm, useWatch } from "react-hook-form"; import { JSONObject } from "superjson/dist/types"; import { QueryCell } from "@lib/QueryCell"; import classNames from "@lib/classNames"; import { HttpError } from "@lib/core/http/error"; import { useLocale } from "@lib/hooks/useLocale"; import showToast from "@lib/notification"; import { inferQueryOutput, trpc } from "@lib/trpc"; import { WEBHOOK_TRIGGER_EVENTS } from "@lib/webhooks/constants"; import { ClientSuspense } from "@components/ClientSuspense"; import { Dialog, DialogContent, DialogFooter, DialogTrigger } from "@components/Dialog"; import { List, ListItem, ListItemText, ListItemTitle } from "@components/List"; import Loader from "@components/Loader"; import Shell, { ShellSubHeading } from "@components/Shell"; import { Tooltip } from "@components/Tooltip"; import ConfirmationDialogContent from "@components/dialog/ConfirmationDialogContent"; import { FieldsetLegend, Form, InputGroupBox, TextField, TextArea } from "@components/form/fields"; import { CalendarListContainer } from "@components/integrations/CalendarListContainer"; import ConnectIntegration from "@components/integrations/ConnectIntegrations"; import DisconnectIntegration from "@components/integrations/DisconnectIntegration"; import IntegrationListItem from "@components/integrations/IntegrationListItem"; import SubHeadingTitleWithConnections from "@components/integrations/SubHeadingTitleWithConnections"; import { Alert } from "@components/ui/Alert"; import Button from "@components/ui/Button"; import Switch from "@components/ui/Switch"; type TWebhook = inferQueryOutput<"viewer.webhook.list">[number]; function WebhookListItem(props: { webhook: TWebhook; onEditWebhook: () => void }) { const { t } = useLocale(); const utils = trpc.useContext(); const deleteWebhook = trpc.useMutation("viewer.webhook.delete", { async onSuccess() { await utils.invalidateQueries(["viewer.webhook.list"]); }, }); return (
{props.webhook.subscriberUrl}
{props.webhook.eventTriggers.map((eventTrigger, ind) => ( {t(`${eventTrigger.toLowerCase()}`)} ))}
deleteWebhook.mutate({ id: props.webhook.id })}> {t("delete_webhook_confirmation_message")}
); } function WebhookTestDisclosure() { const subscriberUrl: string = useWatch({ name: "subscriberUrl" }); const payloadTemplate = useWatch({ name: "payloadTemplate" }) || null; const { t } = useLocale(); const [open, setOpen] = useState(false); const mutation = trpc.useMutation("viewer.webhook.testTrigger", { onError(err) { showToast(err.message, "error"); }, }); return ( setOpen(!open)}> {t("webhook_test")}

{t("webhook_response")}

{!mutation.data && {t("no_data_yet")}} {mutation.status === "success" && ( <>
{mutation.data.ok ? t("success") : t("failed")}
{JSON.stringify(mutation.data, null, 4)}
)}
); } function WebhookDialogForm(props: { // defaultValues?: TWebhook; handleClose: () => void; }) { const { t } = useLocale(); const utils = trpc.useContext(); const supportedWebhookIntegrationList = ["https://discord.com/api/webhooks/"]; const handleSubscriberUrlChange = (e) => { form.setValue("subscriberUrl", e.target.value); const ind = supportedWebhookIntegrationList.findIndex((integration) => { return e.target.value.includes(integration); }); if (ind > -1) updateCustomTemplate(supportedWebhookIntegrationList[ind]); }; const updateCustomTemplate = (webhookIntegration) => { setUseCustomPayloadTemplate(true); switch (webhookIntegration) { case "https://discord.com/api/webhooks/": form.setValue( "payloadTemplate", '{"content": "A new event has been scheduled","embeds": [{"color": 2697513,"fields": [{"name": "What","value": "{{title}} ({{type}})"},{"name": "When","value": "Start: {{startTime}} \\n End: {{endTime}} \\n Timezone: ({{organizer.timeZone}})"},{"name": "Who","value": "Organizer: {{organizer.name}} ({{organizer.email}}) \\n Booker: {{attendees.0.name}} ({{attendees.0.email}})" },{"name":"Description", "value":": {{description}}"},{"name":"Where","value":": {{location}} "}]}]}' ); } }; const { defaultValues = { id: "", eventTriggers: WEBHOOK_TRIGGER_EVENTS, subscriberUrl: "", active: true, payloadTemplate: null, } as Omit, } = props; const [useCustomPayloadTemplate, setUseCustomPayloadTemplate] = useState(!!defaultValues.payloadTemplate); const form = useForm({ defaultValues, }); return (
{ if (!useCustomPayloadTemplate && event.payloadTemplate) { event.payloadTemplate = null; } if (event.id) { await utils.client.mutation("viewer.webhook.edit", event); await utils.invalidateQueries(["viewer.webhook.list"]); showToast(t("webhook_updated_successfully"), "success"); } else { await utils.client.mutation("viewer.webhook.create", event); await utils.invalidateQueries(["viewer.webhook.list"]); showToast(t("webhook_created_successfully"), "success"); } props.handleClose(); }} className="space-y-4">
( { form.setValue("active", isChecked); }} /> )} />
{t("event_triggers")} {WEBHOOK_TRIGGER_EVENTS.map((key) => ( ( { const value = field.value; const newValue = isChecked ? [...value, key] : value.filter((v) => v !== key); form.setValue("eventTriggers", newValue, { shouldDirty: true, }); }} /> )} /> ))}
{t("payload_template")}
{useCustomPayloadTemplate && (