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, { useState } from "react"; import { Controller, useForm, useWatch } from "react-hook-form"; import { QueryCell } from "@lib/QueryCell"; import classNames from "@lib/classNames"; 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 } 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()}`)} ))} props.onEditWebhook()} color="minimal" size="icon" StartIcon={PencilAltIcon} className="self-center w-full p-2 ml-4"> { e.stopPropagation(); }} color="minimal" size="icon" StartIcon={TrashIcon} className="self-center w-full p-2 ml-2"> 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.mutate({ url: subscriberUrl, type: "PING", payloadTemplate })}> {t("ping_test")} {!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 { 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")} setUseCustomPayloadTemplate(!value.target.checked)} defaultChecked={!useCustomPayloadTemplate} />{" "} Default setUseCustomPayloadTemplate(value.target.checked)} name="useCustomPayloadTemplate" type="radio" defaultChecked={useCustomPayloadTemplate} />{" "} Custom {useCustomPayloadTemplate && ( )} {t("cancel")} {t("save")} ); } function WebhookListContainer() { const { t } = useLocale(); const query = trpc.useQuery(["viewer.webhook.list"], { suspense: true }); const [newWebhookModal, setNewWebhookModal] = useState(false); const [editModalOpen, setEditModalOpen] = useState(false); const [editing, setEditing] = useState(null); return ( ( <> Webhooks {t("automation")} setNewWebhookModal(true)} data-testid="new_webhook"> {t("new_webhook")} {data.length ? ( {data.map((item) => ( { setEditing(item); setEditModalOpen(true); }} /> ))} ) : null} {/* New webhook dialog */} !isOpen && setNewWebhookModal(false)}> setNewWebhookModal(false)} /> {/* Edit webhook dialog */} !isOpen && setEditModalOpen(false)}> {editing && ( setEditModalOpen(false)} defaultValues={editing} /> )} > )} /> ); } function IframeEmbedContainer() { const { t } = useLocale(); // doesn't need suspense as it should already be loaded const user = trpc.useQuery(["viewer.me"]).data; const iframeTemplate = ``; const htmlTemplate = `${t( "schedule_a_meeting" )}${iframeTemplate}`; return ( <> {t("standard_iframe")} {t("embed_your_calendar")} { navigator.clipboard.writeText(iframeTemplate); showToast("Copied to clipboard", "success"); }}> {t("responsive_fullscreen_iframe")} A fullscreen scheduling experience on your website { navigator.clipboard.writeText(htmlTemplate); showToast("Copied to clipboard", "success"); }}> > ); } function ConnectOrDisconnectIntegrationButton(props: { // credentialIds: number[]; type: string; installed: boolean; }) { const { t } = useLocale(); const [credentialId] = props.credentialIds; const utils = trpc.useContext(); const handleOpenChange = () => { utils.invalidateQueries(["viewer.integrations"]); }; if (credentialId) { return ( ( {t("disconnect")} )} onOpenChange={handleOpenChange} /> ); } if (!props.installed) { return ( ); } /** We don't need to "Connect", just show that it's installed */ if (props.type === "daily_video") { return ( {t("installed")} ); } return ( ( {t("connect")} )} onOpenChange={handleOpenChange} /> ); } function IntegrationsContainer() { const { t } = useLocale(); const query = trpc.useQuery(["viewer.integrations"], { suspense: true }); return ( ( <> } /> {data.conferencing.items.map((item) => ( } /> ))} } /> {data.payment.items.map((item) => ( } /> ))} > )}> ); } export default function IntegrationsPage() { const { t } = useLocale(); return ( }> ); }
{JSON.stringify(mutation.data, null, 4)}