From ded1fce6fae99668d9653bc4291f8200f3b9806f Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com> Date: Mon, 12 Sep 2022 15:07:52 -0400 Subject: [PATCH] V2 settings - developer view (#4085) * Fix breadcrumb colors * HorizontalTabs * Team List Item WIP * Horizontal Tabs * Cards * Remove team list item WIP * Login Page * Add welcome back i118n * EventType page work * Update EventType Icons * WIP Availability * Horizontal Tab Work * Add build command for in root * Update build DIr/command * Add Edit Button + change buttons to v2 * Availablitiy page * Fix IPAD * Make mobile look a little nicer * WIP bookingshell * Remove list items from breaking build * Mian bulk of Booking Page. * Few updates to components * Fix chormatic feedback * Fix banner * Fix Empty Screen * Text area + embded window fixes * Semi fix avatar * Troubleshoot container + Active on count * Improve mobile * NITS * Fix padding on input * Fix icons * Starting to move event types settings to tabs * Begin migration to single page form * Single page tabs * Limits Page * Advanced tab * Add RHF to dependancies * Most of advanced tab * Solved RHF mismtach * Build fixes * RHF conditionals fixes * Improved legibility * Major refactor/organisation into optional V2 UI * Portal EditLocationModal * Fix dialoug form * Update imports * Auto Animate + custom inputs WIP * Custom Inputs * WIP Apps * Fixing stories imports * Stripe app * Remove duplicate dialog * Remove duplicate dialog * Fix embed URL * Fix app toggles + number of active apps * Fix container padding on disabledBorder prop * Removes strict * EventType Team page WIP * Fix embed * NIT * Add Darkmode gray color * V2 Shell WIP * Create my account folder * Add profile section * Fix headings on shell V2 * Fix mobile layout with V2 shell * V2 create event type button * Checked Team Select * Hidden to happen on save - not on toggle * Team Attendee Select animation * WIP * Fix scheduling type and remove multi select label * Fix overflow on teams url * Finish profile fields * Show toast on success * General tab WIP * Even Type move order handles * Add switching of destination calendar * List calendar and delete * Render empty screenwhen no calendars * Fix Embed TS errors * Fix TS errors * Fix Eslint errors * Fix TS errors for UI * Fix ESLINT error * added SidebarCard for promo to v2 and storybook (#3906) Co-authored-by: Julian Benegas Co-authored-by: Alan Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * Tooltip Provider - Wrapper due to dep upgrade * public event type list darkmode * V2 Color changes to public booking * Remove unused component * Fix typecheck * Transfer to SSR * Appearance screen made * V2 image uploader * WIP appearance page * Remove unnecessary data from viewer.me * Add profile translations * Add translations to general page * Add calendar switch * Add calendar switch * Add translations to appearance page * Clean up conferencing page * Clean up appearance page * Draft shell on md screen * fixed slots availability by fixing buffertime (#3756) Co-authored-by: Peer Richelsen * Hidding import features from other services (#3970) * Update PULL_REQUEST_TEMPLATE.md * Fixes CLS when loading Avatars (#3973) * Fixes CLS when loading Avatars * Update packages/ui/v2/core/Avatar.tsx Co-authored-by: Leo Giovanetti Co-authored-by: Leo Giovanetti * New Crowdin translations by Github Action (#3954) Co-authored-by: Crowdin Bot * Adding continue button to connectCalendar on getting-started view (#3971) * Adding continue button to connectCalendar view getting-started * Fixing extra space Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Leo Giovanetti * 2.0 Settings / My Account {View} (#3874) * Fix breadcrumb colors * HorizontalTabs * Team List Item WIP * Horizontal Tabs * Cards * Remove team list item WIP * Login Page * Add welcome back i118n * EventType page work * Update EventType Icons * WIP Availability * Horizontal Tab Work * Add build command for in root * Update build DIr/command * Add Edit Button + change buttons to v2 * Availablitiy page * Fix IPAD * Make mobile look a little nicer * WIP bookingshell * Remove list items from breaking build * Mian bulk of Booking Page. * Few updates to components * Fix chormatic feedback * Fix banner * Fix Empty Screen * Text area + embded window fixes * Semi fix avatar * Troubleshoot container + Active on count * Improve mobile * NITS * Fix padding on input * Fix icons * Starting to move event types settings to tabs * Begin migration to single page form * Single page tabs * Limits Page * Advanced tab * Add RHF to dependancies * Most of advanced tab * Solved RHF mismtach * Build fixes * RHF conditionals fixes * Improved legibility * Major refactor/organisation into optional V2 UI * Portal EditLocationModal * Fix dialoug form * Update imports * Auto Animate + custom inputs WIP * Custom Inputs * WIP Apps * Fixing stories imports * Stripe app * Remove duplicate dialog * Remove duplicate dialog * Fix embed URL * Fix app toggles + number of active apps * Fix container padding on disabledBorder prop * Removes strict * EventType Team page WIP * Fix embed * NIT * Add Darkmode gray color * V2 Shell WIP * Create my account folder * Add profile section * Fix headings on shell V2 * Fix mobile layout with V2 shell * V2 create event type button * Checked Team Select * Hidden to happen on save - not on toggle * Team Attendee Select animation * WIP * Fix scheduling type and remove multi select label * Fix overflow on teams url * Finish profile fields * Show toast on success * General tab WIP * Even Type move order handles * Add switching of destination calendar * List calendar and delete * Render empty screenwhen no calendars * Fix Embed TS errors * Fix TS errors * Fix Eslint errors * Fix TS errors for UI * Fix ESLINT error * added SidebarCard for promo to v2 and storybook (#3906) Co-authored-by: Julian Benegas Co-authored-by: Alan Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> * Tooltip Provider - Wrapper due to dep upgrade * public event type list darkmode * V2 Color changes to public booking * Remove unused component * Fix typecheck * Transfer to SSR * Appearance screen made * V2 image uploader * WIP appearance page * Remove unnecessary data from viewer.me * Add profile translations * Add translations to general page * Add calendar switch * Add calendar switch * Add translations to appearance page * Clean up conferencing page * Settings sidebar fixes * Updates middleware * Update SettingsLayout.tsx * Settings layout improvements * Type fix Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Peer Richelsen Co-authored-by: zomars Co-authored-by: Hariom Balhara Co-authored-by: Julian Benegas Co-authored-by: Alan Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * AppStore CLI: Making video app creation a breeze with major cleanup of locations code throughout (#3825) * Fix breadcrumb colors * HorizontalTabs * Team List Item WIP * Horizontal Tabs * Cards * Remove team list item WIP * Login Page * Add welcome back i118n * EventType page work * Update EventType Icons * WIP Availability * Horizontal Tab Work * Add build command for in root * Update build DIr/command * Add Edit Button + change buttons to v2 * Availablitiy page * Fix IPAD * Make mobile look a little nicer * WIP bookingshell * Remove list items from breaking build * Add Embed ModalBox for routing forms * Mian bulk of Booking Page. * Few updates to components * Fix chormatic feedback * Add duplicate form support * Fix duplication logic * Change to feathericons everywhere and other fixes * Dont allow routes for fallback route * Fix banner * Fix Empty Screen * Text area + embded window fixes * Semi fix avatar * Fix all TS issues * Fix tests * Troubleshoot container + Active on count * Support routing using query params * Improve mobile * NITS * Fix padding on input * Support multiselect in router endpoint * Fix the issue where app goes in embed mode after viewing embed once * Fix icons * Add router url tests * Add Responses download and form toggling tests * Add required validation test * Change Icons everywhere * App typeform app * Improvements in cli * Starting to move event types settings to tabs * Begin migration to single page form * Single page tabs * Limits Page * Advanced tab * Add RHF to dependancies * Add typeform how-to-use page * Add typeform how-to-use page and screenshots * Most of advanced tab * Solved RHF mismtach * Build fixes * RHF conditionals fixes * Improved legibility * Fix TS error * Add missing image * Update CliApp.tsx * Major refactor/organisation into optional V2 UI * Portal EditLocationModal * Fix dialoug form * Update imports * Auto Animate + custom inputs WIP * Custom Inputs * WIP Apps * Fixing stories imports * Stripe app * Remove duplicate dialog * Remove duplicate dialog * Major locations cleanup, 10s of bug fixes and app-store improvements * Fix missing pieces * More fixes * Fix embed URL * Fix app toggles + number of active apps * Fix container padding on disabledBorder prop * Removes strict * more fixes * EventType Team page WIP * Fix embed * NIT * Add Darkmode gray color * V2 Shell WIP * Fix headings on shell V2 * Fix mobile layout with V2 shell * V2 create event type button * Checked Team Select * Hidden to happen on save - not on toggle * Team Attendee Select animation * Fix scheduling type and remove multi select label * Fix overflow on teams url * Revert console * Revert api * Fix Embed TS errors * Fix TS errors * Fix Eslint errors * Fix TS errors for UI * Fix ESLINT error * Fix TS errors * Add missing import * Fix CLI * Add a default placeholder * Remove hardcoded daily:integrations * Fix message for payment page * Revert api and console to main * Update README * Fix TS errors * Fix Lint warnings * Fix Tests * Fix conflict issues * Fix conflict issues Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Peer Richelsen Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars * Button to test a workflow action (#3873) * add Test action button + UI improvements * add test action functionality * add confirmation dialog before sending SMS * code clean up * show error message if test action fails * disable test action button in edit mode * fixes SMS testing * use updated values * fix wrongly updated data in useEffect * fix typo * code clean up * fix UI issue in mobile view * small design fix Co-authored-by: CarinaWolli Co-authored-by: Peer Richelsen Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * Improve CLI App Help Text (#3982) * Adds deprecation notice to QueryCell (#3977) Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * added campfire.to app (#3984) * fixed file size campfire (#3985) * added campfire.to app * fixed file size * Draft shell on md screen * Draft small settings bar * Add padding to dropdown menu * WIP * Create mobile settings nav * Fix conferencing render * Only display mobile settings nav * Clean up my account section * Clean up * Remove old draft shell * Remove old draft shell * Implement changes from #3817 * WIP * Create webhooks page * WIP Webhook form * WIP * WIP Webhook * WIP * Create edit webhook page * Edit webhooks * Edit API keys * Update API key name * Create embeds page * Fix type errors * Remove embeds page * Improves Zapier response and payload (#3892) * improve listBookings API call * improve payload for zapier call * use more readable location for integrations * remove cancellation reason * convert location to readbale string * code clean up * code clean up * updated expected webhook response for e2e tests * fix failing e2e * revert webhook response Co-authored-by: CarinaWolli Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> * fixed dark mode team page (#4315) * Checkly formatting * Move hidden label to toggle on events page (#4320) * Add events for routing forms * Bring shell changes from main * Clean up * WIP * V2 Multi-select (Team Select) (#4324) * --init * design improved * further fine tuning * more fixes * removed extra JSX tag * added story * NIT * revert to use of CheckedTeamSelect * Removes comments Co-authored-by: Peer Richelsen * fix: toggle alligment (#4361) * fix: add checked tranform for switch (#4357) * fixed input size on mobile, fixed settings (#4360) * fixed input size on mobile, fixed settings (#4360) * Fix merge errors * Fix API key page * Fix type error Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com> Co-authored-by: Peer Richelsen Co-authored-by: zomars Co-authored-by: Hariom Balhara Co-authored-by: Julian Benegas Co-authored-by: Alan Co-authored-by: Kszemi Co-authored-by: Leo Giovanetti Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Crowdin Bot Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com> Co-authored-by: CarinaWolli Co-authored-by: Noah Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> --- .../components/webhook/WebhookDialogForm.tsx | 3 +- .../webhook/WebhookListContainer.tsx | 1 + .../components/webhook/WebhookListItem.tsx | 1 + .../webhook/WebhookTestDisclosure.tsx | 1 + apps/web/lib/webhooks/sendPayload.tsx | 2 - apps/web/middleware.ts | 2 + apps/web/pages/api/book/event.ts | 6 +- .../pages/v2/settings/developer/api-keys.tsx | 88 ++++++ .../developer/webhooks/[id]/index.tsx | 1 + .../v2/settings/developer/webhooks/index.tsx | 1 + .../v2/settings/developer/webhooks/new.tsx | 1 + apps/web/public/static/locales/en/common.json | 23 +- .../app-store/ee/routing_forms/trpc-router.ts | 2 +- .../components/v2/ApiKeyDialogForm.tsx | 182 ++++++++++++ .../api-keys/components/v2/ApiKeyListItem.tsx | 92 ++++++ .../webhooks/components/WebhookForm.tsx | 266 ++++++++++++++++++ .../webhooks/components/WebhookListItem.tsx | 78 +++++ .../components/WebhookListItemSkeleton.tsx | 19 ++ .../components/WebhookListSkeleton.tsx | 15 + .../components/WebhookTestDisclosure.tsx | 48 ++++ .../features/webhooks/components/index.ts | 5 + .../webhooks/pages/webhook-edit-view.tsx | 89 ++++++ .../webhooks/pages/webhook-new-view.tsx | 76 +++++ .../features/webhooks/pages/webhooks-view.tsx | 81 ++++++ .../webhooks/utils/getWebhooks.tsx} | 0 .../webhooks/utils}/integrationTemplate.tsx | 0 packages/lib/webhooks/integrationTemplate.tsx | 28 -- .../trpc/server/routers/viewer/bookings.tsx | 4 +- .../trpc/server/routers/viewer/webhook.tsx | 20 ++ packages/ui/v2/core/Meta.tsx | 6 +- packages/ui/v2/core/form/fields.tsx | 4 +- packages/ui/v2/core/layouts/AdminLayout.tsx | 5 +- .../ui/v2/core/layouts/SettingsLayout.tsx | 39 ++- packages/ui/v2/core/skeleton/index.tsx | 2 +- packages/ui/v2/modules/List.tsx | 2 +- .../_IMPORTANT_README/_IMPORTANT_README.md | 7 + 36 files changed, 1140 insertions(+), 60 deletions(-) create mode 100644 apps/web/pages/v2/settings/developer/api-keys.tsx create mode 100644 apps/web/pages/v2/settings/developer/webhooks/[id]/index.tsx create mode 100644 apps/web/pages/v2/settings/developer/webhooks/index.tsx create mode 100644 apps/web/pages/v2/settings/developer/webhooks/new.tsx create mode 100644 packages/features/ee/api-keys/components/v2/ApiKeyDialogForm.tsx create mode 100644 packages/features/ee/api-keys/components/v2/ApiKeyListItem.tsx create mode 100644 packages/features/webhooks/components/WebhookForm.tsx create mode 100644 packages/features/webhooks/components/WebhookListItem.tsx create mode 100644 packages/features/webhooks/components/WebhookListItemSkeleton.tsx create mode 100644 packages/features/webhooks/components/WebhookListSkeleton.tsx create mode 100644 packages/features/webhooks/components/WebhookTestDisclosure.tsx create mode 100644 packages/features/webhooks/components/index.ts create mode 100644 packages/features/webhooks/pages/webhook-edit-view.tsx create mode 100644 packages/features/webhooks/pages/webhook-new-view.tsx create mode 100644 packages/features/webhooks/pages/webhooks-view.tsx rename packages/{lib/webhooks/subscriptions.tsx => features/webhooks/utils/getWebhooks.tsx} (100%) rename {apps/web/lib/webhooks => packages/features/webhooks/utils}/integrationTemplate.tsx (100%) delete mode 100644 packages/lib/webhooks/integrationTemplate.tsx create mode 100644 packages/ui/v2/modules/_IMPORTANT_README/_IMPORTANT_README.md diff --git a/apps/web/components/webhook/WebhookDialogForm.tsx b/apps/web/components/webhook/WebhookDialogForm.tsx index e1988a5c0a..6a803f94c4 100644 --- a/apps/web/components/webhook/WebhookDialogForm.tsx +++ b/apps/web/components/webhook/WebhookDialogForm.tsx @@ -2,6 +2,7 @@ import { Webhook } from "@prisma/client"; import { useEffect, useState } from "react"; import { Controller, useForm } from "react-hook-form"; +import customTemplate, { hasTemplateIntegration } from "@calcom/features/webhooks/utils/integrationTemplate"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import showToast from "@calcom/lib/notification"; import { trpc } from "@calcom/trpc/react"; @@ -11,11 +12,11 @@ import Switch from "@calcom/ui/Switch"; import { FieldsetLegend, Form, InputGroupBox, TextArea, TextField } from "@calcom/ui/form/fields"; import { WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP } from "@lib/webhooks/constants"; -import customTemplate, { hasTemplateIntegration } from "@lib/webhooks/integrationTemplate"; import { TWebhook } from "@components/webhook/WebhookListItem"; import WebhookTestDisclosure from "@components/webhook/WebhookTestDisclosure"; +/** @deprecated Moved to `packages/features/webhooks` */ export default function WebhookDialogForm(props: { eventTypeId?: number; defaultValues?: TWebhook; diff --git a/apps/web/components/webhook/WebhookListContainer.tsx b/apps/web/components/webhook/WebhookListContainer.tsx index 74f99e8b35..7d0f8a3f20 100644 --- a/apps/web/components/webhook/WebhookListContainer.tsx +++ b/apps/web/components/webhook/WebhookListContainer.tsx @@ -21,6 +21,7 @@ export type WebhookListContainerType = { appId?: string; }; +/** @deprecated Moved to `packages/features/webhooks` */ export default function WebhookListContainer(props: WebhookListContainerType) { const router = useRouter(); const query = trpc.useQuery( diff --git a/apps/web/components/webhook/WebhookListItem.tsx b/apps/web/components/webhook/WebhookListItem.tsx index 65118b7159..e23a90344b 100644 --- a/apps/web/components/webhook/WebhookListItem.tsx +++ b/apps/web/components/webhook/WebhookListItem.tsx @@ -11,6 +11,7 @@ import { useLocale } from "@lib/hooks/useLocale"; export type TWebhook = inferQueryOutput<"viewer.webhook.list">[number]; +/** @deprecated Moved to `packages/features/webhooks` */ export default function WebhookListItem(props: { webhook: TWebhook; onEditWebhook: () => void }) { const { t } = useLocale(); const utils = trpc.useContext(); diff --git a/apps/web/components/webhook/WebhookTestDisclosure.tsx b/apps/web/components/webhook/WebhookTestDisclosure.tsx index 34d2ae03d7..21a4d20a9e 100644 --- a/apps/web/components/webhook/WebhookTestDisclosure.tsx +++ b/apps/web/components/webhook/WebhookTestDisclosure.tsx @@ -11,6 +11,7 @@ import { InputGroupBox } from "@calcom/ui/form/fields"; import { useLocale } from "@lib/hooks/useLocale"; +/** @deprecated Moved to `packages/features/webhooks` */ export default function WebhookTestDisclosure() { const subscriberUrl: string = useWatch({ name: "subscriberUrl" }); const payloadTemplate = useWatch({ name: "payloadTemplate" }) || null; diff --git a/apps/web/lib/webhooks/sendPayload.tsx b/apps/web/lib/webhooks/sendPayload.tsx index be765d53ed..e396f36258 100644 --- a/apps/web/lib/webhooks/sendPayload.tsx +++ b/apps/web/lib/webhooks/sendPayload.tsx @@ -5,8 +5,6 @@ import { compile } from "handlebars"; import { getHumanReadableLocationValue } from "@calcom/app-store/locations"; import type { CalendarEvent } from "@calcom/types/Calendar"; -import { getTranslation } from "@server/lib/i18n"; - type ContentType = "application/json" | "application/x-www-form-urlencoded"; export type EventTypeInfo = { diff --git a/apps/web/middleware.ts b/apps/web/middleware.ts index 8d53b468d4..b00d073d89 100644 --- a/apps/web/middleware.ts +++ b/apps/web/middleware.ts @@ -7,6 +7,8 @@ import { extendEventData, nextCollectBasicSettings } from "@calcom/lib/telemetry const V2_WHITELIST = [ "/settings/admin", + "/settings/developer/webhooks", + "/settings/developer/api-keys", "/settings/my-account", "/settings/security", "/availability", diff --git a/apps/web/pages/api/book/event.ts b/apps/web/pages/api/book/event.ts index d9a5fe0007..aa3eee55bd 100644 --- a/apps/web/pages/api/book/event.ts +++ b/apps/web/pages/api/book/event.ts @@ -21,6 +21,7 @@ import { sendScheduledSeatsEmails, } from "@calcom/emails"; import { scheduleWorkflowReminders } from "@calcom/features/ee/workflows/lib/reminders/reminderScheduler"; +import getWebhooks from "@calcom/features/webhooks/utils/getWebhooks"; import { isPrismaObjOrUndefined, parseRecurringEvent } from "@calcom/lib"; import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defaultEvents"; import { getErrorFromUnknown } from "@calcom/lib/errors"; @@ -28,7 +29,6 @@ import isOutOfBounds, { BookingDateInPastError } from "@calcom/lib/isOutOfBounds import logger from "@calcom/lib/logger"; import { defaultResponder, getLuckyUser } from "@calcom/lib/server"; import { updateWebUser as syncServicesUpdateWebUser } from "@calcom/lib/sync/SyncServiceManager"; -import getSubscribers from "@calcom/lib/webhooks/subscriptions"; import prisma, { userSelect } from "@calcom/prisma"; import { extendedBookingCreateBody, requiredCustomInputSchema } from "@calcom/prisma/zod-utils"; import type { BufferedBusyTime } from "@calcom/types/BufferedBusyTime"; @@ -827,7 +827,7 @@ async function handler(req: NextApiRequest) { triggerEvent: WebhookTriggerEvents.MEETING_ENDED, }; - const subscribersMeetingEnded = await getSubscribers(subscriberOptionsMeetingEnded); + const subscribersMeetingEnded = await getWebhooks(subscriberOptionsMeetingEnded); subscribersMeetingEnded.forEach((subscriber) => { if (rescheduleUid && originalRescheduledBooking) { @@ -839,7 +839,7 @@ async function handler(req: NextApiRequest) { }); // Send Webhook call if hooked to BOOKING_CREATED & BOOKING_RESCHEDULED - const subscribers = await getSubscribers(subscriberOptions); + const subscribers = await getWebhooks(subscriberOptions); console.log("evt:", { ...evt, metadata: reqBody.metadata, diff --git a/apps/web/pages/v2/settings/developer/api-keys.tsx b/apps/web/pages/v2/settings/developer/api-keys.tsx new file mode 100644 index 0000000000..a128acb0dc --- /dev/null +++ b/apps/web/pages/v2/settings/developer/api-keys.tsx @@ -0,0 +1,88 @@ +import { useState } from "react"; + +import { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem"; +import LicenseRequired from "@calcom/ee/common/components/v2/LicenseRequired"; +import ApiKeyDialogForm from "@calcom/features/ee/api-keys/components/v2/ApiKeyDialogForm"; +import ApiKeyListItem from "@calcom/features/ee/api-keys/components/v2/ApiKeyListItem"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { trpc } from "@calcom/trpc/react"; +import { Icon } from "@calcom/ui"; +import SkeletonLoader from "@calcom/ui/apps/SkeletonLoader"; +import { EmptyScreen, Button } from "@calcom/ui/v2"; +import { Dialog, DialogContent } from "@calcom/ui/v2/core/Dialog"; +import Meta from "@calcom/ui/v2/core/Meta"; +import { getLayout } from "@calcom/ui/v2/core/layouts/SettingsLayout"; + +const ApiKeysView = () => { + const { t } = useLocale(); + + const { data, isLoading } = trpc.useQuery(["viewer.apiKeys.list"]); + + const [apiKeyModal, setApiKeyModal] = useState(false); + const [apiKeyToEdit, setApiKeyToEdit] = useState<(TApiKeys & { neverExpires?: boolean }) | undefined>( + undefined + ); + + const NewApiKeyButton = () => { + return ( + + ); + }; + + return ( + <> + + + + <> + {isLoading && } +
+ {data?.length ? ( + <> +
+ {data?.map((apiKey, index) => ( + { + setApiKeyToEdit(apiKey); + setApiKeyModal(true); + }} + /> + ))} +
+ + + ) : ( + } + /> + )} +
+ +
+ + + + setApiKeyModal(false)} defaultValues={apiKeyToEdit} /> + + + + ); +}; + +ApiKeysView.getLayout = getLayout; + +export default ApiKeysView; diff --git a/apps/web/pages/v2/settings/developer/webhooks/[id]/index.tsx b/apps/web/pages/v2/settings/developer/webhooks/[id]/index.tsx new file mode 100644 index 0000000000..b8da528284 --- /dev/null +++ b/apps/web/pages/v2/settings/developer/webhooks/[id]/index.tsx @@ -0,0 +1 @@ +export { default } from "@calcom/features/webhooks/pages/webhook-edit-view"; diff --git a/apps/web/pages/v2/settings/developer/webhooks/index.tsx b/apps/web/pages/v2/settings/developer/webhooks/index.tsx new file mode 100644 index 0000000000..d06442e3d2 --- /dev/null +++ b/apps/web/pages/v2/settings/developer/webhooks/index.tsx @@ -0,0 +1 @@ +export { default } from "@calcom/features/webhooks/pages/webhooks-view"; diff --git a/apps/web/pages/v2/settings/developer/webhooks/new.tsx b/apps/web/pages/v2/settings/developer/webhooks/new.tsx new file mode 100644 index 0000000000..05e067b199 --- /dev/null +++ b/apps/web/pages/v2/settings/developer/webhooks/new.tsx @@ -0,0 +1 @@ +export { default } from "@calcom/features/webhooks/pages/webhook-new-view"; diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index d0cd0a5148..1d2caa4f9a 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -352,10 +352,10 @@ "booking_cancelled": "Booking Cancelled", "booking_rescheduled": "Booking Rescheduled", "booking_created": "Booking Created", - "meeting_ended": "Meeting ended", + "meeting_ended": "Meeting Ended", "form_submitted": "Form Submitted", "event_triggers": "Event Triggers", - "subscriber_url": "Subscriber Url", + "subscriber_url": "Subscriber URL", "create_new_webhook": "Create a new webhook", "webhooks": "Webhooks", "team_webhooks": "Team Webhooks", @@ -1166,6 +1166,8 @@ "download_responses": "Download Responses", "create_your_first_form": "Create your first form", "create_your_first_form_description": "With Routing Forms you can ask qualifying questions and route to the correct person or event type.", + "create_your_first_webhook": "Create your first Webhook", + "create_your_first_webhook_description": "With Webhooks you can receive meeting data in real-time when something happens in Cal.com.", "for_a_maximum_of": "For a maximum of", "event_one": "event", "event_other": "events", @@ -1187,6 +1189,23 @@ "add_new_form": "Add new form", "form_description": "Create your form to route a booker", "copy_link_to_form": "Copy link to form", + "add_webhook_description": "Receive meeting data in real-time when something happens in Cal.com", + "triggers_when": "Triggers when", + "test_webhook": "Please ping test before creating.", + "enable_webhook": "Enable Webhook", + "add_webhook": "Add Webhook", + "webhook_edited_successfully": "Webhook saved", + "webhooks_description": "Receive meeting data in real-time when something happens in Cal.com", + "api_keys_description": "Generate API keys for accessing your own account", + "new_api_key": "New API key", + "active": "Active", + "api_key_updated": "API key name updated", + "api_key_update_failed": "Error updating API key name", + "embeds_title": "HTML iframe embed", + "embeds_description": "Embed all your event types on your website", + "pending_payment": "Pending payment", + "create_first_api_key": "Create your first API key", + "create_first_api_key_description": "API keys allow other apps to communicate with Cal.com", "back_to_signin": "Back to sign in", "reset_link_sent": "Reset link sent", "password_reset_email":"An email is on itโ€™s way to {{email}} with instructions to reset your password.", diff --git a/packages/app-store/ee/routing_forms/trpc-router.ts b/packages/app-store/ee/routing_forms/trpc-router.ts index e3fdc9f816..614c3dd67a 100644 --- a/packages/app-store/ee/routing_forms/trpc-router.ts +++ b/packages/app-store/ee/routing_forms/trpc-router.ts @@ -2,8 +2,8 @@ import { Prisma, WebhookTriggerEvents } from "@prisma/client"; import { v4 as uuidv4 } from "uuid"; import { z } from "zod"; +import getWebhooks from "@calcom/features/webhooks/utils/getWebhooks"; import { sendGenericWebhookPayload } from "@calcom/lib/webhooks/sendPayload"; -import getWebhooks from "@calcom/lib/webhooks/subscriptions"; import { TRPCError } from "@calcom/trpc/server"; import { createProtectedRouter, createRouter } from "@calcom/trpc/server/createRouter"; diff --git a/packages/features/ee/api-keys/components/v2/ApiKeyDialogForm.tsx b/packages/features/ee/api-keys/components/v2/ApiKeyDialogForm.tsx new file mode 100644 index 0000000000..0217d72cd7 --- /dev/null +++ b/packages/features/ee/api-keys/components/v2/ApiKeyDialogForm.tsx @@ -0,0 +1,182 @@ +import { useState } from "react"; +import { Controller, useForm } from "react-hook-form"; + +import dayjs from "@calcom/dayjs"; +import { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem"; +import LicenseRequired from "@calcom/ee/common/components/v2/LicenseRequired"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import showToast from "@calcom/lib/notification"; +import { trpc } from "@calcom/trpc/react"; +import { DialogFooter } from "@calcom/ui/Dialog"; +import { ClipboardCopyIcon } from "@calcom/ui/Icon"; +import { Tooltip } from "@calcom/ui/Tooltip"; +import { DatePicker } from "@calcom/ui/v2"; +import Button from "@calcom/ui/v2/core/Button"; +import Switch from "@calcom/ui/v2/core/Switch"; +import { Form, TextField } from "@calcom/ui/v2/core/form/fields"; + +export default function ApiKeyDialogForm({ + defaultValues, + handleClose, +}: { + defaultValues?: Omit & { neverExpires?: boolean }; + handleClose: () => void; +}) { + const { t } = useLocale(); + const utils = trpc.useContext(); + + const updateApiKeyMutation = trpc.useMutation("viewer.apiKeys.edit", { + onSuccess() { + utils.invalidateQueries("viewer.apiKeys.list"); + showToast(t("api_key_updated"), "success"); + handleClose(); + }, + onError() { + showToast(t("api_key_update_failed"), "error"); + }, + }); + + const [apiKey, setApiKey] = useState(""); + const [successfulNewApiKeyModal, setSuccessfulNewApiKeyModal] = useState(false); + const [apiKeyDetails, setApiKeyDetails] = useState({ + expiresAt: null as Date | null, + note: "" as string | null, + neverExpires: false, + }); + + const form = useForm({ + defaultValues: { + note: defaultValues?.note || "", + neverExpires: defaultValues?.neverExpires || false, + expiresAt: defaultValues?.expiresAt || dayjs().add(1, "month").toDate(), + }, + }); + const watchNeverExpires = form.watch("neverExpires"); + + return ( + + {successfulNewApiKeyModal ? ( + <> +
+

+ {t("success_api_key_created")} +

+
+ {t("success_api_key_created_bold_tagline")}{" "} + {t("you_will_only_view_it_once")} +
+
+
+
+ + {apiKey} + + + + +
+ + {apiKeyDetails.neverExpires + ? t("never_expire_key") + : `${t("expires")} ${apiKeyDetails?.expiresAt?.toLocaleDateString()}`} + +
+ + + + + ) : ( +
{ + if (defaultValues) { + console.log("Name changed"); + await updateApiKeyMutation.mutate({ id: defaultValues.id, note: event.note }); + } else { + const apiKey = await utils.client.mutation("viewer.apiKeys.create", event); + setApiKey(apiKey); + setApiKeyDetails({ ...event }); + await utils.invalidateQueries(["viewer.apiKeys.list"]); + setSuccessfulNewApiKeyModal(true); + } + }} + className="space-y-4"> +
+

+ {defaultValues ? t("edit_api_key") : t("create_api_key")} +

+

{t("api_key_modal_subtitle")}

+
+
+ ( + { + form.setValue("note", e?.target.value); + }} + type="text" + /> + )} + /> +
+ {!defaultValues && ( +
+
+ {t("expire_date")} + ( + + )} + /> +
+ ( + + )} + /> +
+ )} + + + + + +
+ )} +
+ ); +} diff --git a/packages/features/ee/api-keys/components/v2/ApiKeyListItem.tsx b/packages/features/ee/api-keys/components/v2/ApiKeyListItem.tsx new file mode 100644 index 0000000000..3157c90310 --- /dev/null +++ b/packages/features/ee/api-keys/components/v2/ApiKeyListItem.tsx @@ -0,0 +1,92 @@ +import dayjs from "@calcom/dayjs"; +import { TApiKeys } from "@calcom/ee/api-keys/components/ApiKeyListItem"; +import { classNames } from "@calcom/lib"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { trpc } from "@calcom/trpc/react"; +import { Icon } from "@calcom/ui"; +import Badge from "@calcom/ui/v2/core/Badge"; +import Button from "@calcom/ui/v2/core/Button"; +import Dropdown, { + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@calcom/ui/v2/core/Dropdown"; + +const ApiKeyListItem = ({ + apiKey, + lastItem, + onEditClick, +}: { + apiKey: TApiKeys; + lastItem: boolean; + onEditClick: () => void; +}) => { + const { t } = useLocale(); + const utils = trpc.useContext(); + + const isExpired = apiKey?.expiresAt ? apiKey.expiresAt < new Date() : null; + const neverExpires = apiKey?.expiresAt === null; + + const deleteApiKey = trpc.useMutation("viewer.apiKeys.delete", { + async onSuccess() { + await utils.invalidateQueries(["viewer.apiKeys.list"]); + }, + }); + + return ( +
+
+

{apiKey?.note ? apiKey.note : t("api_key_no_note")}

+
+ {!neverExpires && isExpired && ( + + {t("expired")} + + )} + {!isExpired && ( + + {t("active")} + + )} +

+ {" "} + {neverExpires ? ( +

{t("api_key_never_expires")}
+ ) : ( + `${isExpired ? t("expired") : t("expires")} ${dayjs(apiKey?.expiresAt?.toString()).fromNow()}` + )} +

+
+
+
+ + +
+
+ ); +}; + +export default ApiKeyListItem; diff --git a/packages/features/webhooks/components/WebhookForm.tsx b/packages/features/webhooks/components/WebhookForm.tsx new file mode 100644 index 0000000000..d0d101029e --- /dev/null +++ b/packages/features/webhooks/components/WebhookForm.tsx @@ -0,0 +1,266 @@ +import { WebhookTriggerEvents } from "@prisma/client"; +import { useEffect, useState } from "react"; +import { Controller, useForm } from "react-hook-form"; + +import { classNames } from "@calcom/lib"; +import { WEBAPP_URL } from "@calcom/lib/constants"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { inferQueryOutput } from "@calcom/trpc/react"; +import Button from "@calcom/ui/v2/core/Button"; +import Switch from "@calcom/ui/v2/core/Switch"; +import Select from "@calcom/ui/v2/core/form/Select"; +import { Form, Label, TextArea, TextField } from "@calcom/ui/v2/core/form/fields"; + +import customTemplate, { hasTemplateIntegration } from "../utils/integrationTemplate"; +import WebhookTestDisclosure from "./WebhookTestDisclosure"; + +export type TWebhook = inferQueryOutput<"viewer.webhook.list">[number]; + +export type WebhookFormData = { + id?: string; + subscriberUrl: string; + active: boolean; + eventTriggers: WebhookTriggerEvents[]; + secret: string | null; + payloadTemplate: string | undefined | null; +}; + +export type WebhookFormSubmitData = WebhookFormData & { + changeSecret: boolean; + newSecret: string; +}; + +type WebhookTriggerEventOptions = { value: WebhookTriggerEvents; label: string }[]; + +const WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2: Record = { + core: [ + { value: WebhookTriggerEvents.BOOKING_CANCELLED, label: "booking_cancelled" }, + { value: WebhookTriggerEvents.BOOKING_CREATED, label: "booking_created" }, + { value: WebhookTriggerEvents.BOOKING_RESCHEDULED, label: "booking_rescheduled" }, + { value: WebhookTriggerEvents.MEETING_ENDED, label: "meeting_ended" }, + ], + routing_forms: [{ value: WebhookTriggerEvents.FORM_SUBMITTED, label: "form_submitted" }], +}; + +const WebhookForm = (props: { + webhook?: WebhookFormData; + apps?: (keyof typeof WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2)[]; + onSubmit: (event: WebhookFormSubmitData) => void; +}) => { + const { t } = useLocale(); + + const triggerOptions: WebhookTriggerEventOptions = WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2["core"]; + if (props.apps) { + for (const app of props.apps) { + triggerOptions.push(...WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2[app]); + } + } + const translatedTriggerOptions = triggerOptions.map((option) => ({ ...option, label: t(option.label) })); + + const formMethods = useForm({ + defaultValues: { + subscriberUrl: props?.webhook?.subscriberUrl || "", + active: props?.webhook?.active || false, + eventTriggers: props?.webhook?.eventTriggers || [], + secret: props?.webhook?.secret || "", + payloadTemplate: props?.webhook?.payloadTemplate || undefined, + }, + }); + + const [useCustomTemplate, setUseCustomTemplate] = useState(false); + const [newSecret, setNewSecret] = useState(""); + const [changeSecret, setChangeSecret] = useState(false); + const hasSecretKey = !!props?.webhook?.secret; + // const currentSecret = props?.webhook?.secret; + + useEffect(() => { + if (changeSecret) { + formMethods.unregister("secret", { keepDefaultValue: false }); + } + }, [changeSecret]); + + return ( + <> +
props.onSubmit({ ...values, changeSecret, newSecret })}> + ( + <> + { + formMethods.setValue("subscriberUrl", e?.target.value); + if (hasTemplateIntegration({ url: e.target.value })) { + setUseCustomTemplate(true); + formMethods.setValue("payloadTemplate", customTemplate({ url: e.target.value })); + } + }} + /> + + )} + /> + ( +
+ { + formMethods.setValue("active", value); + }} + /> +
+ )} + /> + ( +
+ +