diff --git a/packages/app-store/ee/routing_forms/playwright/tests/basic.e2e.ts b/packages/app-store/ee/routing-forms/playwright/tests/basic.e2e.ts
similarity index 92%
rename from packages/app-store/ee/routing_forms/playwright/tests/basic.e2e.ts
rename to packages/app-store/ee/routing-forms/playwright/tests/basic.e2e.ts
index 19feb7c9e2..8190cbbcd5 100644
--- a/packages/app-store/ee/routing_forms/playwright/tests/basic.e2e.ts
+++ b/packages/app-store/ee/routing-forms/playwright/tests/basic.e2e.ts
@@ -74,13 +74,13 @@ test.describe("Routing Forms", () => {
});
test.describe("Zero State Routing Forms", () => {
test.beforeEach(async ({ page, users }) => {
- const user = await users.create({ username: "routing_forms" });
+ const user = await users.create({ username: "routing-forms" });
await user.login();
// Install app
- await page.goto(`/apps/routing_forms`);
+ await page.goto(`/apps/routing-forms`);
await page.click('[data-testid="install-app-button"]');
await page.waitForNavigation({
- url: (url) => url.pathname === `/apps/routing_forms/forms`,
+ url: (url) => url.pathname === `/apps/routing-forms/forms`,
});
});
@@ -89,9 +89,9 @@ test.describe("Routing Forms", () => {
const formId = await addForm(page);
- await page.click('[href="/apps/routing_forms/forms"]');
+ await page.click('[href="/apps/routing-forms/forms"]');
// TODO: Workaround for bug in https://github.com/calcom/cal.com/issues/3410
- await page.click('[href="/apps/routing_forms/forms"]');
+ await page.click('[href="/apps/routing-forms/forms"]');
await page.waitForSelector('[data-testid="routing-forms-list"]');
// Ensure that it's visible in forms list
@@ -100,7 +100,7 @@ test.describe("Routing Forms", () => {
await gotoRoutingLink(page, formId);
await page.isVisible("text=Test Form Name");
- await page.goto(`apps/routing_forms/route-builder/${formId}`);
+ await page.goto(`apps/routing-forms/route-builder/${formId}`);
await page.click('[data-testid="toggle-form"] [value="on"]');
await gotoRoutingLink(page, formId);
await page.isVisible("text=ERROR 404");
@@ -129,7 +129,7 @@ test.describe("Routing Forms", () => {
types[field.typeIndex]
);
- await page.click('[href*="/apps/routing_forms/route-builder/"]');
+ await page.click('[href*="/apps/routing-forms/route-builder/"]');
await page.click('[data-testid="add-route"]');
await page.click('[data-testid="add-rule"]');
await verifySelectOptions(
@@ -151,13 +151,13 @@ test.describe("Routing Forms", () => {
users: Fixtures["users"];
page: Page;
}) {
- const user = await users.create({ username: "routing_forms" }, { seedRoutingForms: true });
+ const user = await users.create({ username: "routing-forms" }, { seedRoutingForms: true });
await user.login();
// Install app
- await page.goto(`/apps/routing_forms`);
+ await page.goto(`/apps/routing-forms`);
await page.click('[data-testid="install-app-button"]');
await page.waitForNavigation({
- url: (url) => url.pathname === `/apps/routing_forms/forms`,
+ url: (url) => url.pathname === `/apps/routing-forms/forms`,
});
return user;
};
@@ -197,7 +197,7 @@ test.describe("Routing Forms", () => {
// Log back in to view form responses.
await user.login();
- await page.goto(`/apps/routing_forms/route-builder/${routingForm.id}`);
+ await page.goto(`/apps/routing-forms/route-builder/${routingForm.id}`);
const [download] = await Promise.all([
// Start waiting for the download
page.waitForEvent("download"),
diff --git a/packages/app-store/ee/routing_forms/static/icon.svg b/packages/app-store/ee/routing-forms/static/icon.svg
similarity index 100%
rename from packages/app-store/ee/routing_forms/static/icon.svg
rename to packages/app-store/ee/routing-forms/static/icon.svg
diff --git a/packages/app-store/ee/routing_forms/trpc-router.ts b/packages/app-store/ee/routing-forms/trpc-router.ts
similarity index 100%
rename from packages/app-store/ee/routing_forms/trpc-router.ts
rename to packages/app-store/ee/routing-forms/trpc-router.ts
diff --git a/packages/app-store/ee/routing_forms/types/types.d.ts b/packages/app-store/ee/routing-forms/types/types.d.ts
similarity index 100%
rename from packages/app-store/ee/routing_forms/types/types.d.ts
rename to packages/app-store/ee/routing-forms/types/types.d.ts
diff --git a/packages/app-store/ee/routing_forms/zod.ts b/packages/app-store/ee/routing-forms/zod.ts
similarity index 100%
rename from packages/app-store/ee/routing_forms/zod.ts
rename to packages/app-store/ee/routing-forms/zod.ts
diff --git a/packages/app-store/typeform/pages/how-to-use/[...appPages].tsx b/packages/app-store/typeform/pages/how-to-use/[...appPages].tsx
index 59a02e89f5..b38706ebd0 100644
--- a/packages/app-store/typeform/pages/how-to-use/[...appPages].tsx
+++ b/packages/app-store/typeform/pages/how-to-use/[...appPages].tsx
@@ -17,7 +17,7 @@ export default function HowToUse() {
-
Make sure that you have{" "}
-
+
Routing Forms
{" "}
app installed
diff --git a/packages/features/webhooks/components/WebhookForm.tsx b/packages/features/webhooks/components/WebhookForm.tsx
index df07ac159b..c9ab8476d5 100644
--- a/packages/features/webhooks/components/WebhookForm.tsx
+++ b/packages/features/webhooks/components/WebhookForm.tsx
@@ -1,5 +1,5 @@
import { WebhookTriggerEvents } from "@prisma/client";
-import { useEffect, useMemo, useState } from "react";
+import { useEffect, useState } from "react";
import { Controller, useForm } from "react-hook-form";
import { classNames } from "@calcom/lib";
@@ -30,7 +30,7 @@ export type WebhookFormSubmitData = WebhookFormData & {
newSecret: string;
};
-type WebhookTriggerEventOptions = { value: WebhookTriggerEvents; label: string }[];
+type WebhookTriggerEventOptions = readonly { value: WebhookTriggerEvents; label: string }[];
const WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2: Record = {
core: [
@@ -39,8 +39,8 @@ const WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2: Record {
const { apps = [] } = props;
const { t } = useLocale();
- const triggerOptions = useMemo(
- () =>
- apps.reduce((acc, app) => {
- if (!WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2[app]) return acc;
- acc.push(...WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2[app]);
- return acc;
- }, WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2["core"]),
- [apps]
- );
+ const triggerOptions = [...WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP_V2["core"]];
+ if (apps) {
+ for (const app of 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 || [],
+ subscriberUrl: props.webhook?.subscriberUrl || "",
+ active: props.webhook ? props.webhook.active : true,
+ eventTriggers: !props.webhook
+ ? translatedTriggerOptions.map((option) => option.value)
+ : props.webhook.eventTriggers,
secret: props?.webhook?.secret || "",
payloadTemplate: props?.webhook?.payloadTemplate || undefined,
},
@@ -128,20 +127,24 @@ const WebhookForm = (props: {
(
-
-
-
- )}
+ render={({ field: { onChange, value } }) => {
+ const selectValue = translatedTriggerOptions.filter((option) => value.includes(option.value));
+ return (
+
+
+
+ );
+ }}
/>
[number];
@@ -21,8 +21,7 @@ export default function WebhookListItem(props: {
const deleteWebhook = trpc.useMutation("viewer.webhook.delete", {
async onSuccess() {
await utils.invalidateQueries(["viewer.webhook.list"]);
- // TODO: Better success message
- showToast(t("deleted"), "success");
+ showToast(t("webhook_removed_successfully"), "success");
},
});
const toggleWebhook = trpc.useMutation("viewer.webhook.edit", {
@@ -38,14 +37,13 @@ export default function WebhookListItem(props: {
{webhook.subscriberUrl}
-
(
- {t(`${trigger.toLowerCase()}`)}
- ))}>
-
-
- {t("triggers_when")}
-
+
+
+ {webhook.eventTriggers.map((trigger) => (
+
+ {t(`${trigger.toLowerCase()}`)}
+
+ ))}
diff --git a/packages/features/webhooks/pages/webhook-edit-view.tsx b/packages/features/webhooks/pages/webhook-edit-view.tsx
index 75857c8bf2..31ba73d75a 100644
--- a/packages/features/webhooks/pages/webhook-edit-view.tsx
+++ b/packages/features/webhooks/pages/webhook-edit-view.tsx
@@ -16,76 +16,90 @@ const EditWebhook = () => {
const { t } = useLocale();
const utils = trpc.useContext();
const router = useRouter();
- const { id: webhookId } = querySchema.parse(router.query);
- const { data: installedApps, isLoading } = trpc.useQuery(
- ["viewer.integrations", { variant: "other", onlyInstalled: true }],
- {
+ function Component({ webhookId }: { webhookId: string }) {
+ const { data: installedApps, isLoading } = trpc.useQuery(
+ ["viewer.integrations", { variant: "other", onlyInstalled: true }],
+ {
+ suspense: true,
+ enabled: router.isReady,
+ }
+ );
+ const { data: webhook } = trpc.useQuery(["viewer.webhook.get", { webhookId }], {
suspense: true,
enabled: router.isReady,
- }
- );
- const { data: webhook } = trpc.useQuery(["viewer.webhook.get", { webhookId }], {
- suspense: true,
- enabled: router.isReady,
- });
- const { data: webhooks } = trpc.useQuery(["viewer.webhook.list"], {
- suspense: true,
- enabled: router.isReady,
- });
- const editWebhookMutation = trpc.useMutation("viewer.webhook.edit", {
- async onSuccess() {
- await utils.invalidateQueries(["viewer.webhook.list"]);
- },
- onError(error) {
- showToast(`${error.message}`, "error");
- },
- });
+ });
+ const { data: webhooks } = trpc.useQuery(["viewer.webhook.list"], {
+ suspense: true,
+ enabled: router.isReady,
+ });
+ const editWebhookMutation = trpc.useMutation("viewer.webhook.edit", {
+ async onSuccess() {
+ await utils.invalidateQueries(["viewer.webhook.list"]);
+ showToast(t("webhook_updated_successfully"), "success");
+ router.back();
+ },
+ onError(error) {
+ showToast(`${error.message}`, "error");
+ },
+ });
- const subscriberUrlReserved = (subscriberUrl: string, id: string): boolean => {
- return !!webhooks?.find((webhook) => webhook.subscriberUrl === subscriberUrl && webhook.id !== id);
- };
+ const subscriberUrlReserved = (subscriberUrl: string, id: string): boolean => {
+ return !!webhooks?.find((webhook) => webhook.subscriberUrl === subscriberUrl && webhook.id !== id);
+ };
- if (isLoading || !webhook) return ;
+ if (isLoading || !webhook) return ;
- return (
- <>
-
- {
- if (subscriberUrlReserved(values.subscriberUrl, webhook.id)) {
- showToast(t("webhook_subscriber_url_reserved"), "error");
- return;
- }
+ return (
+ <>
+
+ {
+ if (subscriberUrlReserved(values.subscriberUrl, webhook.id)) {
+ showToast(t("webhook_subscriber_url_reserved"), "error");
+ return;
+ }
- if (values.changeSecret) {
- values.secret = values.newSecret.length ? values.newSecret : null;
- }
+ if (values.changeSecret) {
+ values.secret = values.newSecret.length ? values.newSecret : null;
+ }
- if (!values.payloadTemplate) {
- values.payloadTemplate = null;
- }
+ if (!values.payloadTemplate) {
+ values.payloadTemplate = null;
+ }
- editWebhookMutation.mutate({
- id: webhook.id,
- subscriberUrl: values.subscriberUrl,
- eventTriggers: values.eventTriggers,
- active: values.active,
- payloadTemplate: values.payloadTemplate,
- secret: values.secret,
- });
- showToast(t("webhook_updated_successfully"), "success");
- router.back();
- }}
- apps={installedApps?.items.map((app) => app.slug)}
- />
- >
- );
+ editWebhookMutation.mutate({
+ id: webhook.id,
+ subscriberUrl: values.subscriberUrl,
+ eventTriggers: values.eventTriggers,
+ active: values.active,
+ payloadTemplate: values.payloadTemplate,
+ secret: values.secret,
+ });
+ }}
+ apps={installedApps?.items.map((app) => app.slug)}
+ />
+ >
+ );
+ }
+
+ if (!router.isReady) return null;
+
+ const parsed = querySchema.safeParse(router.query);
+
+ if (!parsed.success) {
+ throw new Error("Invalid query");
+ }
+
+ // tRPC useQuery needs webhookId to be available and it becomes available only after router.isReady is true
+ // It causes this requirement of a new component.
+ // I think we should do SSR for this page
+ return ;
};
EditWebhook.getLayout = getLayout;
diff --git a/packages/features/webhooks/pages/webhook-new-view.tsx b/packages/features/webhooks/pages/webhook-new-view.tsx
index c18e0567d4..41f035897d 100644
--- a/packages/features/webhooks/pages/webhook-new-view.tsx
+++ b/packages/features/webhooks/pages/webhook-new-view.tsx
@@ -27,7 +27,9 @@ const NewWebhookView = () => {
const createWebhookMutation = trpc.useMutation("viewer.webhook.create", {
async onSuccess() {
+ showToast(t("webhook_created_successfully"), "success");
await utils.invalidateQueries(["viewer.webhook.list"]);
+ router.back();
},
onError(error) {
showToast(`${error.message}`, "error");
@@ -55,8 +57,6 @@ const NewWebhookView = () => {
payloadTemplate: values.payloadTemplate,
secret: values.secret,
});
- showToast(t("webhook_created_successfully"), "success");
- router.back();
};
if (isLoading) return ;
diff --git a/packages/features/webhooks/pages/webhooks-view.tsx b/packages/features/webhooks/pages/webhooks-view.tsx
index af51aef371..fdde2f3edb 100644
--- a/packages/features/webhooks/pages/webhooks-view.tsx
+++ b/packages/features/webhooks/pages/webhooks-view.tsx
@@ -5,7 +5,7 @@ import { WEBAPP_URL } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { Icon } from "@calcom/ui";
-import { ButtonGroup, EmptyScreen, SkeletonText } from "@calcom/ui/v2";
+import { EmptyScreen, SkeletonText } from "@calcom/ui/v2";
import { Button } from "@calcom/ui/v2/core/Button";
import Meta from "@calcom/ui/v2/core/Meta";
import { getLayout } from "@calcom/ui/v2/core/layouts/SettingsLayout";
@@ -31,7 +31,11 @@ const WebhooksView = () => {
const NewWebhookButton = () => {
const { t, isLocaleReady } = useLocale();
return (
-