refactor: remove circular dependency between appstore and trpc (#23768)

* mv PaymentPage to web

* mv 2 more components to web

* refactor

* update imports

* mv

* revert

* fix

* fix

* update

* migrate relevant test too

* move setup components to web from appstore

* move wipemycalother

* mv

* fix typechecks

* mv more routing forms components

* fix

* fix

* fix

* fix: resolve type errors in SingleForm.tsx after migration

- Updated SingleFormComponentProps type to include enriched form properties
- Added proper typing for user, team, nonOrgUsername, nonOrgTeamslug, userOrigin, teamOrigin
- Fixes type errors that occurred after migrating SingleForm.tsx from packages/app-store/routing-forms/components/
- Resolves both SingleForm.tsx and TestForm.tsx type errors

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* delete

* refactors

* fix

* fix

* fix

* remove usage of trpc client from OmniInstallAppButton.tsx

* fix test

* import only type

* fix test

* remove trpc package from appstore package.json

* remove remaining trpc usage

* update test

* fix type checks

* fix errors

* fix

* make it error

* nit

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Benny Joo
2025-09-14 12:51:05 +00:00
committed by GitHub
co-authored by Devin AI
parent 06fcea4116
commit 76bba1ca05
99 changed files with 728 additions and 634 deletions
+8 -3
View File
@@ -23,10 +23,15 @@ module.exports = {
files: ["packages/app-store/**/*.{ts,tsx,js,jsx}"],
rules: {
"no-restricted-imports": [
"warn",
"error",
{
paths: ["@calcom/trpc"],
patterns: ["@calcom/trpc/*"],
allowTypeImports: false,
patterns: [
{
group: ["@calcom/trpc/*", "@trpc/*"],
message: "tRPC imports are blocked in packages/app-store. Move UI to apps/web/components/apps or introduce an API boundary.",
},
],
},
],
},
@@ -6,6 +6,8 @@ import { Controller, useFieldArray, useWatch } from "react-hook-form";
import { Toaster } from "sonner";
import { v4 as uuidv4 } from "uuid";
import { FieldTypes } from "@calcom/app-store/routing-forms/lib/FieldTypes";
import type { RoutingFormWithResponseCount } from "@calcom/app-store/routing-forms/types/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import classNames from "@calcom/ui/classNames";
import { Button } from "@calcom/ui/components/button";
@@ -19,13 +21,11 @@ import {
} from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
import { Tooltip } from "@calcom/ui/components/tooltip";
import type { getServerSidePropsForSingleFormView as getServerSideProps } from "@calcom/web/lib/apps/routing-forms/[...pages]/getServerSidePropsSingleForm";
import type { inferSSRProps } from "@lib/types/inferSSRProps";
import SingleForm from "../../components/SingleForm";
import type { getServerSidePropsForSingleFormView as getServerSideProps } from "../../components/getServerSidePropsSingleForm";
import { FieldTypes } from "../../lib/FieldTypes";
import type { RoutingFormWithResponseCount } from "../../types/types";
import SingleForm from "@components/apps/routing-forms/SingleForm";
type HookForm = UseFormReturn<RoutingFormWithResponseCount>;
@@ -3,6 +3,7 @@
import { useState, useEffect } from "react";
import type z from "zod";
import type { RoutingFormWithResponseCount } from "@calcom/app-store/routing-forms/types/types";
import { WhenToWriteToRecord, SalesforceFieldType } from "@calcom/app-store/salesforce/lib/enums";
import type { writeToRecordDataSchema as salesforceWriteToRecordDataSchema } from "@calcom/app-store/salesforce/zod";
import { routingFormIncompleteBookingDataSchema as salesforceRoutingFormIncompleteBookingDataSchema } from "@calcom/app-store/salesforce/zod";
@@ -17,10 +18,9 @@ import { Select } from "@calcom/ui/components/form";
import { Label } from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
import type { getServerSidePropsForSingleFormView as getServerSideProps } from "@calcom/web/lib/apps/routing-forms/[...pages]/getServerSidePropsSingleForm";
import SingleForm from "../../components/SingleForm";
import type { getServerSidePropsForSingleFormView as getServerSideProps } from "../../components/getServerSidePropsSingleForm";
import type { RoutingFormWithResponseCount } from "../../types/types";
import SingleForm from "@components/apps/routing-forms/SingleForm";
function Page({ form }: { form: RoutingFormWithResponseCount }) {
const { t } = useLocale();
@@ -10,6 +10,36 @@ import type { UseFormReturn } from "react-hook-form";
import { Toaster } from "sonner";
import type { z } from "zod";
import { routingFormAppComponents } from "@calcom/app-store/routing-forms/appComponents";
import DynamicAppComponent from "@calcom/app-store/routing-forms/components/DynamicAppComponent";
import { EmptyState } from "@calcom/app-store/routing-forms/components/_components/EmptyState";
import { RoutingSkeleton } from "@calcom/app-store/routing-forms/components/_components/RoutingSkeleton";
import {
withRaqbSettingsAndWidgets,
ConfigFor,
} from "@calcom/app-store/routing-forms/components/react-awesome-query-builder/config/uiConfig";
import { RoutingPages } from "@calcom/app-store/routing-forms/lib/RoutingPages";
import { createFallbackRoute } from "@calcom/app-store/routing-forms/lib/createFallbackRoute";
import getEventTypeAppMetadata from "@calcom/app-store/routing-forms/lib/getEventTypeAppMetadata";
import {
getQueryBuilderConfigForFormFields,
getQueryBuilderConfigForAttributes,
type FormFieldsQueryBuilderConfigWithRaqbFields,
type AttributesQueryBuilderConfigWithRaqbFields,
isDynamicOperandField,
} from "@calcom/app-store/routing-forms/lib/getQueryBuilderConfig";
import isRouter from "@calcom/app-store/routing-forms/lib/isRouter";
import type { RoutingFormWithResponseCount } from "@calcom/app-store/routing-forms/types/types";
import type {
GlobalRoute,
LocalRoute,
SerializableRoute,
Attribute,
EditFormRoute,
AttributeRoutingConfig,
} from "@calcom/app-store/routing-forms/types/types";
import type { zodRoutes } from "@calcom/app-store/routing-forms/zod";
import { RouteActionType } from "@calcom/app-store/routing-forms/zod";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import { areTheySiblingEntities } from "@calcom/lib/entityPermissionUtils.shared";
import type { EventTypesByViewer } from "@calcom/lib/event-types/getEventTypesByViewer";
@@ -29,39 +59,9 @@ import { SelectField } from "@calcom/ui/components/form";
import { Switch } from "@calcom/ui/components/form";
import type { IconName } from "@calcom/ui/components/icon";
import { Icon } from "@calcom/ui/components/icon";
import type { getServerSidePropsForSingleFormView as getServerSideProps } from "@calcom/web/lib/apps/routing-forms/[...pages]/getServerSidePropsSingleForm";
import { routingFormAppComponents } from "../../appComponents";
import DynamicAppComponent from "../../components/DynamicAppComponent";
import SingleForm from "../../components/SingleForm";
import { EmptyState } from "../../components/_components/EmptyState";
import { RoutingSkeleton } from "../../components/_components/RoutingSkeleton";
import type { getServerSidePropsForSingleFormView as getServerSideProps } from "../../components/getServerSidePropsSingleForm";
import {
withRaqbSettingsAndWidgets,
ConfigFor,
} from "../../components/react-awesome-query-builder/config/uiConfig";
import { RoutingPages } from "../../lib/RoutingPages";
import { createFallbackRoute } from "../../lib/createFallbackRoute";
import getEventTypeAppMetadata from "../../lib/getEventTypeAppMetadata";
import {
getQueryBuilderConfigForFormFields,
getQueryBuilderConfigForAttributes,
type FormFieldsQueryBuilderConfigWithRaqbFields,
type AttributesQueryBuilderConfigWithRaqbFields,
isDynamicOperandField,
} from "../../lib/getQueryBuilderConfig";
import isRouter from "../../lib/isRouter";
import type { RoutingFormWithResponseCount } from "../../types/types";
import type {
GlobalRoute,
LocalRoute,
SerializableRoute,
Attribute,
EditFormRoute,
AttributeRoutingConfig,
} from "../../types/types";
import type { zodRoutes } from "../../zod";
import { RouteActionType } from "../../zod";
import SingleForm from "@components/apps/routing-forms/SingleForm";
type Form = inferSSRProps<typeof getServerSideProps>["form"];
@@ -6,7 +6,15 @@ import { useEffect, useRef, useState } from "react";
import { Toaster } from "sonner";
import { v4 as uuidv4 } from "uuid";
import FormInputFields from "@calcom/app-store/routing-forms/components/FormInputFields";
import { getAbsoluteEventTypeRedirectUrlWithEmbedSupport } from "@calcom/app-store/routing-forms/getEventTypeRedirectUrl";
import getFieldIdentifier from "@calcom/app-store/routing-forms/lib/getFieldIdentifier";
import { findMatchingRoute } from "@calcom/app-store/routing-forms/lib/processRoute";
import { substituteVariables } from "@calcom/app-store/routing-forms/lib/substituteVariables";
import { getFieldResponseForJsonLogic } from "@calcom/app-store/routing-forms/lib/transformResponse";
import type { NonRouterRoute, FormResponse } from "@calcom/app-store/routing-forms/types/types";
import { sdkActionManager, useIsEmbed } from "@calcom/embed-core/embed-iframe";
import { getUrlSearchParamsToForward } from "@calcom/features/routing-forms/lib/getUrlSearchParamsToForward";
import useGetBrandingColours from "@calcom/lib/getBrandColours";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -18,16 +26,7 @@ import classNames from "@calcom/ui/classNames";
import { Button } from "@calcom/ui/components/button";
import { showToast } from "@calcom/ui/components/toast";
import { useCalcomTheme } from "@calcom/ui/styles";
import FormInputFields from "../../components/FormInputFields";
import { getAbsoluteEventTypeRedirectUrlWithEmbedSupport } from "../../getEventTypeRedirectUrl";
import getFieldIdentifier from "../../lib/getFieldIdentifier";
import { findMatchingRoute } from "../../lib/processRoute";
import { substituteVariables } from "../../lib/substituteVariables";
import { getFieldResponseForJsonLogic } from "../../lib/transformResponse";
import type { NonRouterRoute, FormResponse } from "../../types/types";
import type { getServerSideProps } from "./getServerSideProps";
import { getUrlSearchParamsToForward } from "./getUrlSearchParamsToForward";
import type { getServerSideProps } from "@calcom/web/lib/apps/routing-forms/[...pages]/getServerSidePropsRoutingLink";
type Props = inferSSRProps<typeof getServerSideProps>;
const useBrandColors = ({
@@ -0,0 +1,6 @@
export const routingFormsComponents = {
"form-edit": () => import("./FormEdit").then((mod) => mod.default),
"route-builder": () => import("./RouteBuilder").then((mod) => mod.default),
"routing-link": () => import("./RoutingLink").then((mod) => mod.default),
"incomplete-booking": () => import("./IncompleteBooking").then((mod) => mod.default),
};
@@ -3,12 +3,12 @@ import type { PageProps as ServerPageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { cookies, headers } from "next/headers";
import { routingFormsComponents } from "@calcom/app-store/routing-forms/pages/app-routing.client-config";
import type { routingServerSidePropsConfig } from "@calcom/app-store/routing-forms/pages/app-routing.server-config";
import type { routingServerSidePropsConfig } from "@lib/apps/routing-forms/[...pages]/app-routing.server-config";
import { getServerSideProps } from "@lib/apps/routing-forms/[...pages]/getServerSideProps";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { routingFormsComponents } from "./app-routing.client-config";
const normalizePages = (pages: string[] | string | undefined) => {
const normalizedPages = Array.isArray(pages) ? pages : pages?.split("/") ?? [];
return normalizedPages[0];
@@ -4,6 +4,8 @@ import { useAutoAnimate } from "@formkit/auto-animate/react";
import { useEffect, useState } from "react";
import { useFormContext } from "react-hook-form";
import { isFallbackRoute } from "@calcom/app-store/routing-forms/lib/isFallbackRoute";
import type { RoutingFormWithResponseCount } from "@calcom/app-store/routing-forms/types/types";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import SkeletonLoaderTeamList from "@calcom/features/ee/teams/components/SkeletonloaderTeamList";
import { CreateButtonWithTeamsList } from "@calcom/features/ee/teams/components/createButton/CreateButtonWithTeamsList";
@@ -25,11 +27,15 @@ import { EmptyScreen } from "@calcom/ui/components/empty-screen";
import { Icon } from "@calcom/ui/components/icon";
import { List, ListLinkItem } from "@calcom/ui/components/list";
import { Tooltip } from "@calcom/ui/components/tooltip";
import type { SetNewFormDialogState, NewFormDialogState } from "../../components/FormActions";
import { FormAction, FormActionsDropdown, FormActionsProvider } from "../../components/FormActions";
import { isFallbackRoute } from "../../lib/isFallbackRoute";
import type { RoutingFormWithResponseCount } from "../../types/types";
import type {
SetNewFormDialogState,
NewFormDialogState,
} from "@calcom/web/components/apps/routing-forms/FormActions";
import {
FormAction,
FormActionsDropdown,
FormActionsProvider,
} from "@calcom/web/components/apps/routing-forms/FormActions";
function NewFormButton({ setNewFormDialogState }: { setNewFormDialogState: SetNewFormDialogState }) {
const { t } = useLocale();
@@ -1,6 +1,6 @@
import { _generateMetadata } from "app/_utils";
import Forms from "@calcom/app-store/routing-forms/pages/forms/[...appPages]";
import Forms from "./Forms";
export const generateMetadata = async ({ params }: { params: Promise<{ pages: string[] }> }) => {
const pages = (await params).pages;
@@ -10,6 +10,7 @@ import dayjs from "@calcom/dayjs";
import { sdkActionManager, useIsEmbed } from "@calcom/embed-core/embed-iframe";
import { PayIcon } from "@calcom/features/bookings/components/event-meta/PayIcon";
import { Price } from "@calcom/features/bookings/components/event-meta/Price";
import type { PaymentPageProps } from "@calcom/features/ee/payments/pages/payment";
import { APP_NAME, WEBSITE_URL } from "@calcom/lib/constants";
import { getPaymentAppData } from "@calcom/lib/getPaymentAppData";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -18,24 +19,20 @@ import { getIs24hClockFromLocalStorage, isBrowserLocale24h } from "@calcom/lib/t
import { CURRENT_TIMEZONE } from "@calcom/lib/timezoneConstants";
import { localStorage } from "@calcom/lib/webstorage";
import type { PaymentPageProps } from "../pages/payment";
const StripePaymentComponent = dynamic(() => import("./Payment"), {
const StripePaymentComponent = dynamic(() => import("@calcom/features/ee/payments/components/Payment"), {
ssr: false,
});
const PaypalPaymentComponent = dynamic(
() =>
import("@calcom/app-store/paypal/components/PaypalPaymentComponent").then(
(m) => m.PaypalPaymentComponent
),
import("@calcom/web/components/apps/paypal/PaypalPaymentComponent").then((m) => m.PaypalPaymentComponent),
{
ssr: false,
}
);
const AlbyPaymentComponent = dynamic(
() => import("@calcom/app-store/alby/components/AlbyPaymentComponent").then((m) => m.AlbyPaymentComponent),
() => import("@calcom/web/components/apps/alby/AlbyPaymentComponent").then((m) => m.AlbyPaymentComponent),
{
ssr: false,
}
@@ -43,9 +40,7 @@ const AlbyPaymentComponent = dynamic(
const HitpayPaymentComponent = dynamic(
() =>
import("@calcom/app-store/hitpay/components/HitpayPaymentComponent").then(
(m) => m.HitpayPaymentComponent
),
import("@calcom/web/components/apps/hitpay/HitpayPaymentComponent").then((m) => m.HitpayPaymentComponent),
{
ssr: false,
}
@@ -53,7 +48,7 @@ const HitpayPaymentComponent = dynamic(
const BtcpayPaymentComponent = dynamic(
() =>
import("@calcom/app-store/btcpayserver/components/BtcpayPaymentComponent").then(
import("@calcom/web/components/apps/btcpayserver/BtcpayPaymentComponent").then(
(m) => m.BtcpayPaymentComponent
),
{
@@ -3,12 +3,13 @@ import type { PageProps } from "app/_types";
import { _generateMetadata } from "app/_utils";
import { cookies, headers } from "next/headers";
import PaymentPage from "@calcom/features/ee/payments/components/PaymentPage";
import { getServerSideProps, type PaymentPageProps } from "@calcom/features/ee/payments/pages/payment";
import { APP_NAME } from "@calcom/lib/constants";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import PaymentPage from "./PaymentPage";
export const generateMetadata = async ({ params, searchParams }: PageProps) => {
const props = await getData(
buildLegacyCtx(await headers(), await cookies(), await params, await searchParams)
+26
View File
@@ -0,0 +1,26 @@
import dynamic from "next/dynamic";
import { DynamicComponent } from "@calcom/app-store/_components/DynamicComponent";
export const AppSetupMap = {
alby: dynamic(() => import("@calcom/web/components/apps/alby/Setup")),
"apple-calendar": dynamic(() => import("@calcom/web/components/apps/applecalendar/Setup")),
exchange: dynamic(() => import("@calcom/web/components/apps/exchangecalendar/Setup")),
"exchange2013-calendar": dynamic(() => import("@calcom/web/components/apps/exchange2013calendar/Setup")),
"exchange2016-calendar": dynamic(() => import("@calcom/web/components/apps/exchange2016calendar/Setup")),
"caldav-calendar": dynamic(() => import("@calcom/web/components/apps/caldavcalendar/Setup")),
"ics-feed": dynamic(() => import("@calcom/web/components/apps/ics-feedcalendar/Setup")),
zapier: dynamic(() => import("@calcom/web/components/apps/zapier/Setup")),
make: dynamic(() => import("@calcom/web/components/apps/make/Setup")),
sendgrid: dynamic(() => import("@calcom/web/components/apps/sendgrid/Setup")),
stripe: dynamic(() => import("@calcom/web/components/apps/stripepayment/Setup")),
paypal: dynamic(() => import("@calcom/web/components/apps/paypal/Setup")),
hitpay: dynamic(() => import("@calcom/web/components/apps/hitpay/Setup")),
btcpayserver: dynamic(() => import("@calcom/web/components/apps/btcpayserver/Setup")),
};
export const AppSetupPage = (props: { slug: string }) => {
return <DynamicComponent<typeof AppSetupMap> componentMap={AppSetupMap} {...props} />;
};
export default AppSetupPage;
@@ -5,22 +5,15 @@ import { useState, useCallback, useEffect } from "react";
import { Toaster } from "sonner";
import AppNotInstalledMessage from "@calcom/app-store/_components/AppNotInstalledMessage";
import { albyCredentialKeysSchema } from "@calcom/app-store/alby/lib/albyCredentialKeysSchema";
import type { IAlbySetupProps } from "@calcom/app-store/alby/pages/setup/_getServerSideProps";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { showToast } from "@calcom/ui/components/toast";
import { Badge } from "@calcom/ui/components/badge";
import { Button } from "@calcom/ui/components/button";
import { Icon } from "@calcom/ui/components/icon";
import { albyCredentialKeysSchema } from "../../lib/albyCredentialKeysSchema";
export interface IAlbySetupProps {
email: string | null;
lightningAddress: string | null;
clientId: string;
clientSecret: string;
}
import { showToast } from "@calcom/ui/components/toast";
export default function AlbySetup(props: IAlbySetupProps) {
const params = useCompatSearchParams();
@@ -5,10 +5,10 @@ import { Toaster } from "sonner";
import { APP_NAME } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { PasswordField } from "@calcom/ui/components/form";
import { TextField } from "@calcom/ui/components/form";
import { Alert } from "@calcom/ui/components/alert";
import { Button } from "@calcom/ui/components/button";
import { PasswordField } from "@calcom/ui/components/form";
import { TextField } from "@calcom/ui/components/form";
import { Form } from "@calcom/ui/components/form";
export default function AppleCalendarSetup() {
@@ -1,354 +1,352 @@
import { zodResolver } from "@hookform/resolvers/zod";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import { Toaster } from "sonner";
import { z } from "zod";
import AppNotInstalledMessage from "@calcom/app-store/_components/AppNotInstalledMessage";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { Button } from "@calcom/ui/components/button";
import { Icon } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
import KeyField from "../../components/KeyInput";
import { btcpayCredentialKeysSchema } from "../../lib/btcpayCredentialKeysSchema";
export type IBTCPaySetupProps = z.infer<typeof btcpayCredentialKeysSchema>;
export default function BTCPaySetup(props: IBTCPaySetupProps) {
const params = useCompatSearchParams();
if (params?.get("callback") === "true") {
return <BTCPaySetupCallback />;
}
return <BTCPaySetupPage {...props} />;
}
enum BTCPayOAuthError {
Declined = "declined",
Unknown = "unknown",
}
function BTCPaySetupCallback() {
const [error, setError] = useState<string | null>(null);
const searchParams = useCompatSearchParams();
useEffect(() => {
if (!searchParams) {
return;
}
if (!window.opener) {
setError("Something went wrong. Opener not available. Please contact support");
return;
}
const code = searchParams?.get("code");
const error = searchParams?.get("error");
if (!code) {
setError(BTCPayOAuthError.Declined);
}
if (error) {
setError(error);
return;
}
window.opener.postMessage({
type: "btcpayserver:oauth:success",
payload: { code },
});
window.close();
}, [searchParams]);
return (
<div>
{error && <p>Authorization failed: {error}</p>}
{!error && <p>Connecting...</p>}
</div>
);
}
function BTCPaySetupPage(props: IBTCPaySetupProps) {
const router = useRouter();
const { t } = useLocale();
const session = useSession();
const [loading, setLoading] = useState<boolean>(false);
const [validating, setValidating] = useState<boolean>(false);
const [updatable, setUpdatable] = useState<boolean>(false);
const [keyData, setKeyData] = useState<
| {
storeId: string;
serverUrl: string;
apiKey: string;
webhookSecret: string;
}
| undefined
>();
const settingsSchema = z.object({
storeId: z.string().trim(),
serverUrl: z.string().trim(),
apiKey: z.string().trim(),
webhookSecret: z.string().optional(),
});
const integrations = trpc.viewer.apps.integrations.useQuery({ variant: "payment", appId: "btcpayserver" });
const [btcPayPaymentAppCredentials] = integrations.data?.items || [];
const [credentialId] = btcPayPaymentAppCredentials?.userCredentialIds || [-1];
const showContent = !!integrations.data && integrations.isSuccess && !!credentialId;
const saveKeysMutation = trpc.viewer.apps.updateAppCredentials.useMutation({
onSuccess: () => {
showToast(t("keys_have_been_saved"), "success");
router.push("/event-types");
},
onError: (error) => {
showToast(error.message, "error");
},
});
const deleteMutation = trpc.viewer.credentials.delete.useMutation({
onSuccess: () => {
router.push("/apps/btcpayserver");
},
onError: () => {
showToast(t("error_removing_app"), "error");
},
});
const {
register,
handleSubmit,
formState: { errors },
watch,
reset,
} = useForm<z.infer<typeof settingsSchema>>({
reValidateMode: "onChange",
resolver: zodResolver(settingsSchema),
});
useEffect(() => {
const _keyData = {
storeId: props?.storeId || "",
serverUrl: props?.serverUrl || "",
apiKey: props?.apiKey || "",
webhookSecret: props?.webhookSecret || "",
};
setKeyData(_keyData);
}, [props]);
useEffect(() => {
const subscription = watch((value) => {
const { serverUrl, storeId, apiKey, webhookSecret } = value;
if (
serverUrl &&
storeId &&
apiKey &&
(keyData?.serverUrl !== serverUrl || keyData?.storeId !== storeId || keyData?.apiKey !== apiKey)
) {
setUpdatable(true);
} else {
setUpdatable(false);
}
});
return () => subscription.unsubscribe();
}, [watch, keyData]);
const configureBTCPayWebhook = async (data: z.infer<typeof settingsSchema>) => {
setValidating(true);
const specificEvents = ["InvoiceSettled", "InvoiceProcessing"];
const serverUrl = data.serverUrl.endsWith("/") ? data.serverUrl.slice(0, -1) : data.serverUrl;
const endpoint = `${serverUrl}/api/v1/stores/${data.storeId}/webhooks`;
const webhookUrl = `${WEBAPP_URL}/api/integrations/btcpayserver/webhook`;
const requestBody = {
enabled: true,
automaticRedelivery: false,
url: webhookUrl,
authorizedEvents: {
everything: false,
specificEvents: specificEvents,
},
secret: null,
};
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `token ${data.apiKey}`,
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
const errorBody = await response.text();
showToast(`Failed to configure webhook: ${errorBody}`, "error");
return false;
}
const webhookResponse = await response.json();
saveKeysMutation.mutate({
credentialId,
key: btcpayCredentialKeysSchema.parse({
...data,
webhookSecret: webhookResponse.secret,
}),
});
return true;
} catch (error) {
if (error instanceof Error) {
showToast(error.message || "Failed to configure BTCPay webhook", "error");
} else {
showToast("An unknown error occurred while configuring BTCPay webhook", "error");
}
return false;
} finally {
setValidating(false);
}
};
const onSubmit = handleSubmit(async (data) => {
if (loading) return;
setLoading(true);
try {
const isValid = await configureBTCPayWebhook(data);
if (!isValid) {
setLoading(false);
return;
}
} catch (error: unknown) {
let message = "";
if (error instanceof Error) {
message = error.message;
}
showToast(message, "error");
} finally {
setLoading(false);
}
});
const onCancel = () => {
deleteMutation.mutate({ id: credentialId });
};
const btcpayIcon = (
<>
<img className="h-5 w-5" src="/app-store/btcpayserver/btcpay.svg" alt="BTCPay Server Icon" />
</>
);
if (session.status === "loading") return <></>;
if (integrations.isPending) {
return <div className="absolute z-50 flex h-screen w-full items-center bg-gray-200" />;
}
const isNewCredential = !props.serverUrl && !props.storeId && !props.webhookSecret && !props.apiKey;
const webhookUri = `${WEBAPP_URL}/api/integrations/btcpayserver/webhook`;
return (
<>
<div className="bg-default flex h-screen items-center justify-center">
{showContent ? (
<div className="flex w-full w-full max-w-[43em] flex-col items-center justify-center space-y-4 p-4 lg:space-y-5">
<form className="w-full space-y-4" onSubmit={onSubmit}>
<div className="bg-default border-subtle overflow-auto rounded border">
<div className="border-subtle flex items-center justify-between border-b-[1px] p-4 md:p-5">
<h2 className="text-2xl font-semibold">BTCPay Server Information</h2>
</div>
<div className="w-full space-y-4 p-4 md:p-5">
<div className="w-full">
<KeyField
{...register("serverUrl", { required: true })}
id="serverUrl"
name="serverUrl"
containerClassName="w-full"
label="BTCPay Server Url"
autoComplete="off"
autoCorrect="off"
defaultValue={keyData?.serverUrl || ""}
/>
{errors.serverUrl && (
<p data-testid="required" className="py-2 text-xs text-red-500">
{errors.serverUrl?.message}
</p>
)}
</div>
<div className="w-full">
<KeyField
{...register("storeId", { required: true })}
id="storeId"
name="storeId"
containerClassName="w-full"
label="BTCPay Store Id"
autoComplete="off"
autoCorrect="off"
defaultValue={keyData?.storeId || ""}
/>
{errors.storeId && (
<p data-testid="required" className="py-2 text-xs text-red-500">
{errors.storeId?.message}
</p>
)}
</div>
<div className="w-full">
<KeyField
{...register("apiKey", { required: true })}
id="apiKey"
name="apiKey"
containerClassName="w-full"
label="API Key"
autoComplete="off"
autoCorrect="off"
defaultValue={keyData?.apiKey || ""}
/>
{errors.apiKey && (
<p data-testid="required" className="py-2 text-xs text-red-500">
{errors.apiKey?.message}
</p>
)}
</div>
</div>
</div>
<div className="flex justify-end gap-4">
{isNewCredential ? (
<>
<Button color="secondary" className="h-10" onClick={onCancel}>
Cancel
</Button>
<Button
className="h-10"
color="primary"
type="submit"
disabled={loading || validating || !updatable}>
{loading ? (
<>
<Icon name="loader" className="mr-2 animate-spin" />
{t("connecting")}
</>
) : (
t("connect")
)}
</Button>
</>
) : (
<>
<Link href="/apps/btcpayserver" className="inline-block">
<Button color="secondary">Go to App Store</Button>
</Link>
</>
)}
</div>
</form>
</div>
) : (
<AppNotInstalledMessage appName="btcpayserver" />
)}
<Toaster position="bottom-right" />
</div>
</>
);
}
import { zodResolver } from "@hookform/resolvers/zod";
import { useSession } from "next-auth/react";
import Link from "next/link";
import { useRouter } from "next/navigation";
import { useState, useEffect } from "react";
import { useForm } from "react-hook-form";
import { Toaster } from "sonner";
import { z } from "zod";
import AppNotInstalledMessage from "@calcom/app-store/_components/AppNotInstalledMessage";
import KeyField from "@calcom/app-store/btcpayserver/components/KeyInput";
import { btcpayCredentialKeysSchema } from "@calcom/app-store/btcpayserver/lib/btcpayCredentialKeysSchema";
import type { IBTCPaySetupProps } from "@calcom/app-store/btcpayserver/pages/setup/_getServerSideProps";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { Button } from "@calcom/ui/components/button";
import { Icon } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
export default function BTCPaySetup(props: IBTCPaySetupProps) {
const params = useCompatSearchParams();
if (params?.get("callback") === "true") {
return <BTCPaySetupCallback />;
}
return <BTCPaySetupPage {...props} />;
}
enum BTCPayOAuthError {
Declined = "declined",
Unknown = "unknown",
}
function BTCPaySetupCallback() {
const [error, setError] = useState<string | null>(null);
const searchParams = useCompatSearchParams();
useEffect(() => {
if (!searchParams) {
return;
}
if (!window.opener) {
setError("Something went wrong. Opener not available. Please contact support");
return;
}
const code = searchParams?.get("code");
const error = searchParams?.get("error");
if (!code) {
setError(BTCPayOAuthError.Declined);
}
if (error) {
setError(error);
return;
}
window.opener.postMessage({
type: "btcpayserver:oauth:success",
payload: { code },
});
window.close();
}, [searchParams]);
return (
<div>
{error && <p>Authorization failed: {error}</p>}
{!error && <p>Connecting...</p>}
</div>
);
}
function BTCPaySetupPage(props: IBTCPaySetupProps) {
const router = useRouter();
const { t } = useLocale();
const session = useSession();
const [loading, setLoading] = useState<boolean>(false);
const [validating, setValidating] = useState<boolean>(false);
const [updatable, setUpdatable] = useState<boolean>(false);
const [keyData, setKeyData] = useState<
| {
storeId: string;
serverUrl: string;
apiKey: string;
webhookSecret: string;
}
| undefined
>();
const settingsSchema = z.object({
storeId: z.string().trim(),
serverUrl: z.string().trim(),
apiKey: z.string().trim(),
webhookSecret: z.string().optional(),
});
const integrations = trpc.viewer.apps.integrations.useQuery({ variant: "payment", appId: "btcpayserver" });
const [btcPayPaymentAppCredentials] = integrations.data?.items || [];
const [credentialId] = btcPayPaymentAppCredentials?.userCredentialIds || [-1];
const showContent = !!integrations.data && integrations.isSuccess && !!credentialId;
const saveKeysMutation = trpc.viewer.apps.updateAppCredentials.useMutation({
onSuccess: () => {
showToast(t("keys_have_been_saved"), "success");
router.push("/event-types");
},
onError: (error) => {
showToast(error.message, "error");
},
});
const deleteMutation = trpc.viewer.credentials.delete.useMutation({
onSuccess: () => {
router.push("/apps/btcpayserver");
},
onError: () => {
showToast(t("error_removing_app"), "error");
},
});
const {
register,
handleSubmit,
formState: { errors },
watch,
reset,
} = useForm<z.infer<typeof settingsSchema>>({
reValidateMode: "onChange",
resolver: zodResolver(settingsSchema),
});
useEffect(() => {
const _keyData = {
storeId: props?.storeId || "",
serverUrl: props?.serverUrl || "",
apiKey: props?.apiKey || "",
webhookSecret: props?.webhookSecret || "",
};
setKeyData(_keyData);
}, [props]);
useEffect(() => {
const subscription = watch((value) => {
const { serverUrl, storeId, apiKey, webhookSecret } = value;
if (
serverUrl &&
storeId &&
apiKey &&
(keyData?.serverUrl !== serverUrl || keyData?.storeId !== storeId || keyData?.apiKey !== apiKey)
) {
setUpdatable(true);
} else {
setUpdatable(false);
}
});
return () => subscription.unsubscribe();
}, [watch, keyData]);
const configureBTCPayWebhook = async (data: z.infer<typeof settingsSchema>) => {
setValidating(true);
const specificEvents = ["InvoiceSettled", "InvoiceProcessing"];
const serverUrl = data.serverUrl.endsWith("/") ? data.serverUrl.slice(0, -1) : data.serverUrl;
const endpoint = `${serverUrl}/api/v1/stores/${data.storeId}/webhooks`;
const webhookUrl = `${WEBAPP_URL}/api/integrations/btcpayserver/webhook`;
const requestBody = {
enabled: true,
automaticRedelivery: false,
url: webhookUrl,
authorizedEvents: {
everything: false,
specificEvents: specificEvents,
},
secret: null,
};
try {
const response = await fetch(endpoint, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `token ${data.apiKey}`,
},
body: JSON.stringify(requestBody),
});
if (!response.ok) {
const errorBody = await response.text();
showToast(`Failed to configure webhook: ${errorBody}`, "error");
return false;
}
const webhookResponse = await response.json();
saveKeysMutation.mutate({
credentialId,
key: btcpayCredentialKeysSchema.parse({
...data,
webhookSecret: webhookResponse.secret,
}),
});
return true;
} catch (error) {
if (error instanceof Error) {
showToast(error.message || "Failed to configure BTCPay webhook", "error");
} else {
showToast("An unknown error occurred while configuring BTCPay webhook", "error");
}
return false;
} finally {
setValidating(false);
}
};
const onSubmit = handleSubmit(async (data) => {
if (loading) return;
setLoading(true);
try {
const isValid = await configureBTCPayWebhook(data);
if (!isValid) {
setLoading(false);
return;
}
} catch (error: unknown) {
let message = "";
if (error instanceof Error) {
message = error.message;
}
showToast(message, "error");
} finally {
setLoading(false);
}
});
const onCancel = () => {
deleteMutation.mutate({ id: credentialId });
};
const btcpayIcon = (
<>
<img className="h-5 w-5" src="/app-store/btcpayserver/btcpay.svg" alt="BTCPay Server Icon" />
</>
);
if (session.status === "loading") return <></>;
if (integrations.isPending) {
return <div className="absolute z-50 flex h-screen w-full items-center bg-gray-200" />;
}
const isNewCredential = !props.serverUrl && !props.storeId && !props.webhookSecret && !props.apiKey;
const webhookUri = `${WEBAPP_URL}/api/integrations/btcpayserver/webhook`;
return (
<>
<div className="bg-default flex h-screen items-center justify-center">
{showContent ? (
<div className="flex w-full w-full max-w-[43em] flex-col items-center justify-center space-y-4 p-4 lg:space-y-5">
<form className="w-full space-y-4" onSubmit={onSubmit}>
<div className="bg-default border-subtle overflow-auto rounded border">
<div className="border-subtle flex items-center justify-between border-b-[1px] p-4 md:p-5">
<h2 className="text-2xl font-semibold">BTCPay Server Information</h2>
</div>
<div className="w-full space-y-4 p-4 md:p-5">
<div className="w-full">
<KeyField
{...register("serverUrl", { required: true })}
id="serverUrl"
name="serverUrl"
containerClassName="w-full"
label="BTCPay Server Url"
autoComplete="off"
autoCorrect="off"
defaultValue={keyData?.serverUrl || ""}
/>
{errors.serverUrl && (
<p data-testid="required" className="py-2 text-xs text-red-500">
{errors.serverUrl?.message}
</p>
)}
</div>
<div className="w-full">
<KeyField
{...register("storeId", { required: true })}
id="storeId"
name="storeId"
containerClassName="w-full"
label="BTCPay Store Id"
autoComplete="off"
autoCorrect="off"
defaultValue={keyData?.storeId || ""}
/>
{errors.storeId && (
<p data-testid="required" className="py-2 text-xs text-red-500">
{errors.storeId?.message}
</p>
)}
</div>
<div className="w-full">
<KeyField
{...register("apiKey", { required: true })}
id="apiKey"
name="apiKey"
containerClassName="w-full"
label="API Key"
autoComplete="off"
autoCorrect="off"
defaultValue={keyData?.apiKey || ""}
/>
{errors.apiKey && (
<p data-testid="required" className="py-2 text-xs text-red-500">
{errors.apiKey?.message}
</p>
)}
</div>
</div>
</div>
<div className="flex justify-end gap-4">
{isNewCredential ? (
<>
<Button color="secondary" className="h-10" onClick={onCancel}>
Cancel
</Button>
<Button
className="h-10"
color="primary"
type="submit"
disabled={loading || validating || !updatable}>
{loading ? (
<>
<Icon name="loader" className="mr-2 animate-spin" />
{t("connecting")}
</>
) : (
t("connect")
)}
</Button>
</>
) : (
<>
<Link href="/apps/btcpayserver" className="inline-block">
<Button color="secondary">Go to App Store</Button>
</Link>
</>
)}
</div>
</form>
</div>
) : (
<AppNotInstalledMessage appName="btcpayserver" />
)}
<Toaster position="bottom-right" />
</div>
</>
);
}
@@ -5,18 +5,17 @@ import { Controller, useForm } from "react-hook-form";
import { Toaster } from "sonner";
import z from "zod";
import { ExchangeAuthentication, ExchangeVersion } from "@calcom/app-store/exchangecalendar/enums";
import { emailSchema } from "@calcom/lib/emailSchema";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { EmailField } from "@calcom/ui/components/form";
import { Alert } from "@calcom/ui/components/alert";
import { Button } from "@calcom/ui/components/button";
import { EmailField } from "@calcom/ui/components/form";
import { Form } from "@calcom/ui/components/form";
import { PasswordField } from "@calcom/ui/components/form";
import { TextField } from "@calcom/ui/components/form";
import { SelectField } from "@calcom/ui/components/form";
import { ExchangeAuthentication, ExchangeVersion } from "../../enums";
interface IFormData {
url: string;
username: string;
@@ -1,9 +1,11 @@
"use client";
import { useRouter } from "next/navigation";
import qs from "qs";
import { useEffect, useRef } from "react";
import { z } from "zod";
import { useHitPayDropIn } from "./HitPayDropIn";
import { useHitPayDropIn } from "@calcom/app-store/hitpay/components/HitPayDropIn";
const PaymentHitpayDataSchema = z.object({
id: z.string(),
@@ -8,6 +8,9 @@ import { Toaster } from "sonner";
import { z } from "zod";
import AppNotInstalledMessage from "@calcom/app-store/_components/AppNotInstalledMessage";
import KeyField from "@calcom/app-store/hitpay/components/KeyInput";
import { hitpayCredentialKeysSchema } from "@calcom/app-store/hitpay/lib/hitpayCredentialKeysSchema";
import type { IHitPaySetupProps } from "@calcom/app-store/hitpay/pages/setup/_getServerSideProps";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
@@ -16,11 +19,6 @@ import { Switch } from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
import KeyField from "../../components/KeyInput";
import { hitpayCredentialKeysSchema } from "../../lib/hitpayCredentialKeysSchema";
export type IHitPaySetupProps = z.infer<typeof hitpayCredentialKeysSchema>;
export default function HitPaySetup(props: IHitPaySetupProps) {
const params = useCompatSearchParams();
if (params?.get("callback") === "true") {
@@ -4,6 +4,7 @@ import { useState } from "react";
import { Toaster } from "sonner";
import AppNotInstalledMessage from "@calcom/app-store/_components/AppNotInstalledMessage";
import type { getServerSideProps } from "@calcom/app-store/make/pages/setup/_getServerSideProps";
import ServerTrans from "@calcom/lib/components/ServerTrans";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
@@ -12,8 +13,6 @@ import { Icon } from "@calcom/ui/components/icon";
import { showToast } from "@calcom/ui/components/toast";
import { Tooltip } from "@calcom/ui/components/tooltip";
import type { getServerSideProps } from "./_getServerSideProps";
const MAKE = "make";
export default function MakeSetup({ inviteLink }: InferGetServerSidePropsType<typeof getServerSideProps>) {
@@ -1,3 +1,5 @@
"use client";
import Link from "next/link";
import z from "zod";
@@ -3,6 +3,7 @@ import { createContext, forwardRef, useContext, useState } from "react";
import { useForm } from "react-hook-form";
import { v4 as uuidv4 } from "uuid";
import getFieldIdentifier from "@calcom/app-store/routing-forms/lib/getFieldIdentifier";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import { dataTableQueryParamsSerializer } from "@calcom/features/data-table/lib/serializers";
import { ColumnFilterType } from "@calcom/features/data-table/lib/types";
@@ -34,8 +35,6 @@ import { Form } from "@calcom/ui/components/form";
import { Switch } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";
import getFieldIdentifier from "../lib/getFieldIdentifier";
type FormField = {
identifier?: string;
id: string;
@@ -5,6 +5,8 @@ import { useRef } from "react";
import { Controller } from "react-hook-form";
import type { UseFormReturn } from "react-hook-form";
import { TeamMemberSelect } from "@calcom/app-store/routing-forms/components/_components/TeamMemberSelect";
import type { RoutingFormWithResponseCount } from "@calcom/app-store/routing-forms/types/types";
import { IS_CALCOM } from "@calcom/lib/constants";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
@@ -13,9 +15,6 @@ import { Button } from "@calcom/ui/components/button";
import { Switch, TextAreaField, TextField } from "@calcom/ui/components/form";
import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetFooter } from "@calcom/ui/components/sheet";
import type { RoutingFormWithResponseCount } from "../../types/types";
import { TeamMemberSelect } from "./TeamMemberSelect";
type FormSettingsSlideoverProps = {
form: RoutingFormWithResponseCount;
hookForm: UseFormReturn<RoutingFormWithResponseCount>;
@@ -12,7 +12,7 @@ import { ToggleGroup } from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
import { Tooltip } from "@calcom/ui/components/tooltip";
import { FormAction, FormActionsDropdown } from "../FormActions";
import { FormAction, FormActionsDropdown } from "./FormActions";
import { FormSettingsSlideover } from "./FormSettingsSlideover";
// Toggle group doesnt support HREF navigation, so we need to use this hook to handle navigation
@@ -3,21 +3,39 @@
import { AnimatePresence, motion } from "framer-motion";
import { useEffect, useState } from "react";
import { useFormContext } from "react-hook-form";
import type { UseFormReturn } from "react-hook-form";
import { InfoLostWarningDialog } from "@calcom/app-store/routing-forms/components/InfoLostWarningDialog";
import type { RoutingFormWithResponseCount } from "@calcom/app-store/routing-forms/types/types";
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import classNames from "@calcom/ui/classNames";
import { Form } from "@calcom/ui/components/form";
import { showToast } from "@calcom/ui/components/toast";
import type { SingleFormComponentProps } from "../types/shared";
import type { RoutingFormWithResponseCount } from "../types/types";
import type { getServerSidePropsForSingleFormView } from "@lib/apps/routing-forms/[...pages]/getServerSidePropsSingleForm";
import type { NewFormDialogState } from "./FormActions";
import { FormActionsProvider } from "./FormActions";
import { InfoLostWarningDialog } from "./InfoLostWarningDialog";
import { Header } from "./_components/Header";
import { TestFormRenderer, type UptoDateForm } from "./_components/TestForm";
import { Header } from "./Header";
import type { UptoDateForm } from "./TestForm";
import { TestFormRenderer } from "./TestForm";
export type SingleFormComponentProps = {
form: RoutingFormWithResponseCount;
appUrl: string;
Page: React.FC<{
form: RoutingFormWithResponseCount;
appUrl: string;
hookForm: UseFormReturn<RoutingFormWithResponseCount>;
}>;
enrichedWithUserProfileForm: inferSSRProps<
typeof getServerSidePropsForSingleFormView
>["enrichedWithUserProfileForm"];
permissions: inferSSRProps<typeof getServerSidePropsForSingleFormView>["permissions"];
};
const BREAKPOINTS = {
sm: 640,
@@ -4,29 +4,30 @@ import { AnimatePresence, motion } from "framer-motion";
import type { Dispatch, SetStateAction } from "react";
import { useState, useMemo } from "react";
import FormInputFields from "@calcom/app-store/routing-forms/components/FormInputFields";
import { ResultsView as Results } from "@calcom/app-store/routing-forms/components/_components/ResultSection";
import { TeamMembersMatchResult } from "@calcom/app-store/routing-forms/components/_components/TeamMembersMatchResult";
import type { MembersMatchResultType } from "@calcom/app-store/routing-forms/components/_components/TeamMembersMatchResult";
import { findMatchingRoute } from "@calcom/app-store/routing-forms/lib/processRoute";
import { substituteVariables } from "@calcom/app-store/routing-forms/lib/substituteVariables";
import type { RoutingForm, FormResponse, NonRouterRoute } from "@calcom/app-store/routing-forms/types/types";
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
import { WEBSITE_URL } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import type { Brand } from "@calcom/types/utils";
import { Button } from "@calcom/ui/components/button";
import { Dialog, DialogContent, DialogHeader, DialogFooter } from "@calcom/ui/components/dialog";
import { showToast } from "@calcom/ui/components/toast";
import type { getServerSidePropsForSingleFormView } from "@lib/apps/routing-forms/[...pages]/getServerSidePropsSingleForm";
import { TRPCClientError } from "@trpc/react-query";
import { findMatchingRoute } from "../../lib/processRoute";
import { substituteVariables } from "../../lib/substituteVariables";
import type { SingleFormComponentProps } from "../../types/shared";
import type { RoutingForm, FormResponse, NonRouterRoute } from "../../types/types";
import FormInputFields from "../FormInputFields";
import { ResultsView as Results } from "./ResultSection";
import type { MembersMatchResultType } from "./TeamMembersMatchResult";
import { TeamMembersMatchResult } from "./TeamMembersMatchResult";
export type UptoDateForm = Brand<
NonNullable<SingleFormComponentProps["enrichedWithUserProfileForm"]>,
NonNullable<inferSSRProps<typeof getServerSidePropsForSingleFormView>["enrichedWithUserProfileForm"]>,
"UptoDateForm"
>;
@@ -2,8 +2,9 @@ import { render, screen, fireEvent } from "@testing-library/react";
import type { Mock } from "vitest";
import { vi } from "vitest";
import { TestFormRenderer } from "../components/_components/TestForm";
import { findMatchingRoute } from "../lib/processRoute";
import { findMatchingRoute } from "@calcom/app-store/routing-forms/lib/processRoute";
import { TestFormRenderer } from "./TestForm";
vi.mock("framer-motion", async () => {
return {
@@ -14,7 +15,7 @@ vi.mock("framer-motion", async () => {
};
});
vi.mock("../lib/processRoute", () => ({
vi.mock("@calcom/app-store/routing-forms/lib/processRoute", () => ({
findMatchingRoute: vi.fn(),
}));
@@ -70,15 +71,18 @@ vi.mock("@calcom/lib/hooks/useApp", () => ({
* Avoids the error due to Formbricks
*/
vi.mock("../components/FormActions", () => ({
vi.mock("./FormActions", () => ({
FormAction: vi.fn(),
FormActionsDropdown: vi.fn(),
FormActionsProvider: vi.fn(),
}));
vi.mock("../../components/react-awesome-query-builder/widgets", () => ({
default: {},
}));
vi.mock(
"@calcom/app-store/routing-forms/components/react-awesome-query-builder/widgets",
async (importOriginal) => {
return await importOriginal();
}
);
// Mock the necessary dependencies
vi.mock("@calcom/lib/hooks/useLocale", () => ({
+1 -1
View File
@@ -12,12 +12,12 @@ import FormInputFields, {
import { getAbsoluteEventTypeRedirectUrl } from "@calcom/app-store/routing-forms/getEventTypeRedirectUrl";
import { findMatchingRoute } from "@calcom/app-store/routing-forms/lib/processRoute";
import { substituteVariables } from "@calcom/app-store/routing-forms/lib/substituteVariables";
import { getUrlSearchParamsToForwardForReroute } from "@calcom/app-store/routing-forms/pages/routing-link/getUrlSearchParamsToForward";
import type { FormResponse, LocalRoute } from "@calcom/app-store/routing-forms/types/types";
import { RouteActionType } from "@calcom/app-store/routing-forms/zod";
import dayjs from "@calcom/dayjs";
import { createBooking } from "@calcom/features/bookings/lib/create-booking";
import { Dialog } from "@calcom/features/components/controlled-dialog";
import { getUrlSearchParamsToForwardForReroute } from "@calcom/features/routing-forms/lib/getUrlSearchParamsToForward";
import { useBookerUrl } from "@calcom/lib/hooks/useBookerUrl";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { EventType, User, Team, Attendee, Booking as PrismaBooking } from "@calcom/prisma/client";
@@ -2,8 +2,8 @@ import "server-only";
import type { AppGetServerSideProps } from "@calcom/types/AppGetServerSideProps";
import { getServerSidePropsForSingleFormView as getServerSidePropsSingleForm } from "../components/getServerSidePropsSingleForm";
import { getServerSideProps as getServerSidePropsRoutingLink } from "./routing-link/getServerSideProps";
import { getServerSideProps as getServerSidePropsRoutingLink } from "./getServerSidePropsRoutingLink";
import { getServerSidePropsForSingleFormView as getServerSidePropsSingleForm } from "./getServerSidePropsSingleForm";
export const routingServerSidePropsConfig: Record<string, AppGetServerSideProps> = {
"form-edit": getServerSidePropsSingleForm,
@@ -1,11 +1,12 @@
import type { GetServerSidePropsResult, GetServerSidePropsContext } from "next";
import { z } from "zod";
import { routingServerSidePropsConfig } from "@calcom/app-store/routing-forms/pages/app-routing.server-config";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import prisma from "@calcom/prisma";
import type { AppGetServerSidePropsContext } from "@calcom/types/AppGetServerSideProps";
import { routingServerSidePropsConfig } from "./app-routing.server-config";
const paramsSchema = z.object({
pages: z.array(z.string()),
});
@@ -1,10 +1,9 @@
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { isAuthorizedToViewFormOnOrgDomain } from "@calcom/features/routing-forms/lib/isAuthorizedToViewForm";
import type { AppGetServerSidePropsContext, AppPrisma } from "@calcom/types/AppGetServerSideProps";
import { enrichFormWithMigrationData } from "../../enrichFormWithMigrationData";
import { getSerializableForm } from "../../lib/getSerializableForm";
export const getServerSideProps = async function getServerSideProps(
context: AppGetServerSidePropsContext,
prisma: AppPrisma
@@ -1,12 +1,11 @@
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSerializableForm";
import { Resource } from "@calcom/features/pbac/domain/types/permission-registry";
import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permissions";
import { MembershipRepository } from "@calcom/lib/server/repository/membership";
import { MembershipRole } from "@calcom/prisma/enums";
import type { AppGetServerSidePropsContext, AppPrisma, AppUser } from "@calcom/types/AppGetServerSideProps";
import { enrichFormWithMigrationData } from "../enrichFormWithMigrationData";
import { getSerializableForm } from "../lib/getSerializableForm";
export const getServerSidePropsForSingleFormView = async function getServerSidePropsForSingleFormView(
context: AppGetServerSidePropsContext,
prisma: AppPrisma,
@@ -3,10 +3,10 @@
import { useSession } from "next-auth/react";
import { useRouter } from "next/navigation";
import { AppSetupPage } from "@calcom/app-store/_pages/setup";
import type { getServerSideProps } from "@calcom/app-store/_pages/setup/_getServerSideProps";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import { AppSetupPage } from "@calcom/web/components/apps/AppSetupPage";
export type PageProps = inferSSRProps<typeof getServerSideProps>;
@@ -4,7 +4,6 @@ import { useReactTable, getCoreRowModel, getSortedRowModel, createColumnHelper }
import { useSearchParams, usePathname } from "next/navigation";
import { useMemo, useRef } from "react";
import { WipeMyCalActionButton } from "@calcom/app-store/wipemycalother/components";
import dayjs from "@calcom/dayjs";
import {
useDataTable,
@@ -29,6 +28,7 @@ import { EmptyScreen } from "@calcom/ui/components/empty-screen";
import type { HorizontalTabItemProps } from "@calcom/ui/components/navigation";
import { HorizontalTabs } from "@calcom/ui/components/navigation";
import type { VerticalTabItemProps } from "@calcom/ui/components/navigation";
import { WipeMyCalActionButton } from "@calcom/web/components/apps/wipemycalother/wipeMyCalActionButton";
import BookingListItem from "@components/booking/BookingListItem";
import SkeletonLoader from "@components/booking/SkeletonLoader";
@@ -2,13 +2,14 @@
import { useState } from "react";
import { TestForm } from "@calcom/app-store/routing-forms/components/_components/TestForm";
import type { RoutingForm } from "@calcom/app-store/routing-forms/types/types";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc";
import { Label } from "@calcom/ui/components/form";
import { Select } from "@calcom/ui/components/form";
import { TestForm } from "../../components/apps/routing-forms/TestForm";
export default function InsightsVirtualQueuesPage() {
const { t } = useLocale();
const { data: routingForms, isLoading: isRoutingFormsLoading } =
@@ -25,8 +25,10 @@ export default function AppCard({
switchTooltip,
hideSettingsIcon = false,
hideAppCardOptions = false,
onAppInstallSuccess,
}: {
app: AppCardApp;
onAppInstallSuccess: () => void;
description?: React.ReactNode;
switchChecked?: boolean;
switchOnClick?: (e: boolean) => void;
@@ -112,6 +114,7 @@ export default function AppCard({
appId={app.slug}
returnTo={returnTo}
teamId={teamId}
onAppInstallSuccess={onAppInstallSuccess}
/>
)}
</div>
@@ -2,6 +2,7 @@
export function DynamicComponent<T extends Record<string, React.ComponentType<any>>>(props: {
componentMap: T;
slug: string;
onAppInstallSuccess?: () => void;
wrapperClassName?: string;
}) {
const { componentMap, slug, wrapperClassName, ...rest } = props;
@@ -16,12 +16,13 @@ export const EventTypeAppCard = (props: {
eventType: EventTypeForAppCard;
getAppData: GetAppData;
setAppData: SetAppData;
onAppInstallSuccess?: () => void;
// For event type apps, get these props from shouldLockDisableProps
LockedIcon?: JSX.Element | false;
eventTypeFormMetadata: z.infer<typeof EventTypeMetaDataSchema>;
disabled?: boolean;
}) => {
const { app, getAppData, setAppData, LockedIcon, disabled } = props;
const { app, getAppData, setAppData, LockedIcon, disabled, onAppInstallSuccess } = props;
return (
<ErrorBoundary message={`There is some problem with ${app.name} App`}>
<EventTypeAppContext.Provider value={{ getAppData, setAppData, LockedIcon, disabled }}>
@@ -29,6 +30,7 @@ export const EventTypeAppCard = (props: {
slug={app.slug === "stripe" ? "stripepayment" : app.slug}
componentMap={EventTypeAddonMap}
{...props}
onAppInstallSuccess={onAppInstallSuccess}
/>
</EventTypeAppContext.Provider>
</ErrorBoundary>
@@ -1,6 +1,5 @@
import useApp from "@calcom/lib/hooks/useApp";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import classNames from "@calcom/ui/classNames";
import { Button } from "@calcom/ui/components/button";
import { showToast } from "@calcom/ui/components/toast";
@@ -17,24 +16,21 @@ export default function OmniInstallAppButton({
className,
returnTo,
teamId,
onAppInstallSuccess,
}: {
appId: string;
className: string;
onAppInstallSuccess: () => void;
returnTo?: string;
teamId?: number;
}) {
const { t } = useLocale();
const { data: app } = useApp(appId);
const utils = trpc.useUtils();
const mutation = useAddAppMutation(null, {
returnTo,
onSuccess: (data) => {
utils.viewer.apps.appById.invalidate({ appId });
utils.viewer.apps.integrations.invalidate({
extendsFeature: "EventType",
...(teamId && { teamId }),
});
onAppInstallSuccess();
if (data?.setupPending) return;
showToast(t("app_successfully_installed"), "success");
},
@@ -30,6 +30,7 @@ const mockProps = {
setAppData: setAppDataMock,
LockedIcon: <div>MockedIcon</div>,
disabled: false,
onAppInstallSuccess: vi.fn(),
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any;
-26
View File
@@ -1,26 +0,0 @@
import dynamic from "next/dynamic";
import { DynamicComponent } from "../../_components/DynamicComponent";
export const AppSetupMap = {
alby: dynamic(() => import("../../alby/pages/setup")),
"apple-calendar": dynamic(() => import("../../applecalendar/pages/setup")),
exchange: dynamic(() => import("../../exchangecalendar/pages/setup")),
"exchange2013-calendar": dynamic(() => import("../../exchange2013calendar/pages/setup")),
"exchange2016-calendar": dynamic(() => import("../../exchange2016calendar/pages/setup")),
"caldav-calendar": dynamic(() => import("../../caldavcalendar/pages/setup")),
"ics-feed": dynamic(() => import("../../ics-feedcalendar/pages/setup")),
zapier: dynamic(() => import("../../zapier/pages/setup")),
make: dynamic(() => import("../../make/pages/setup")),
sendgrid: dynamic(() => import("../../sendgrid/pages/setup")),
stripe: dynamic(() => import("../../stripepayment/pages/setup")),
paypal: dynamic(() => import("../../paypal/pages/setup")),
hitpay: dynamic(() => import("../../hitpay/pages/setup")),
btcpayserver: dynamic(() => import("../../btcpayserver/pages/setup")),
};
export const AppSetupPage = (props: { slug: string }) => {
return <DynamicComponent<typeof AppSetupMap> componentMap={AppSetupMap} {...props} />;
};
export default AppSetupPage;
@@ -15,6 +15,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
eventTypeFormMetadata,
onAppInstallSuccess,
}) {
const searchParams = useSearchParams();
const { t } = useLocale();
@@ -32,6 +33,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={WEBAPP_URL + asPath}
app={app}
switchChecked={requirePayment}
@@ -4,7 +4,13 @@ import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import prisma from "@calcom/prisma";
import { getAlbyKeys } from "../../lib/getAlbyKeys";
import type { IAlbySetupProps } from "./index";
export interface IAlbySetupProps {
email: string | null;
lightningAddress: string | null;
clientId: string;
clientSecret: string;
}
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const notFound = { notFound: true } as const;
@@ -6,12 +6,17 @@ import useIsAppEnabled from "../../_utils/useIsAppEnabled";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
onAppInstallSuccess,
}) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
app={app}
switchOnClick={(e) => {
updateEnabled(e);
@@ -17,6 +17,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
eventType,
app,
eventTypeFormMetadata,
onAppInstallSuccess,
}) {
const { t } = useLocale();
const pathname = usePathname();
@@ -27,6 +28,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
switchChecked={requirePayment}
@@ -1,10 +1,12 @@
import type { GetServerSidePropsContext } from "next";
import { z } from "zod";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { CredentialRepository } from "@calcom/lib/server/repository/credential";
import { btcpayCredentialKeysSchema } from "../../lib/btcpayCredentialKeysSchema";
import type { IBTCPaySetupProps } from "./index";
export type IBTCPaySetupProps = z.infer<typeof btcpayCredentialKeysSchema>;
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
try {
@@ -5,13 +5,18 @@ import useIsAppEnabled from "@calcom/app-store/_utils/useIsAppEnabled";
import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import { WEBAPP_URL } from "@calcom/lib/constants";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
onAppInstallSuccess,
}) {
const pathname = usePathname();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
teamId={eventType.team?.id || undefined}
@@ -6,12 +6,13 @@ import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
hideSettingsIcon
app={app}
switchOnClick={(e) => {
@@ -6,12 +6,17 @@ import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
onAppInstallSuccess,
}) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
hideSettingsIcon
app={app}
switchOnClick={(e) => {
@@ -7,7 +7,11 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
onAppInstallSuccess,
}) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const { enabled: showGifSelection, updateEnabled: setShowGifSelection } = useIsAppEnabled(app);
@@ -15,6 +19,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
app={app}
description={t("confirmation_page_gif")}
switchOnClick={(e) => {
@@ -6,12 +6,13 @@ import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
hideSettingsIcon
app={app}
switchOnClick={(e) => {
@@ -16,6 +16,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
eventTypeFormMetadata,
onAppInstallSuccess,
}) {
const { t } = useLocale();
const pathname = usePathname();
@@ -27,6 +28,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
switchChecked={enabled}
@@ -1,10 +1,12 @@
import type { GetServerSidePropsContext } from "next";
import { z } from "zod";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import prisma from "@calcom/prisma";
import { hitpayCredentialKeysSchema } from "../../lib/hitpayCredentialKeysSchema";
import type { IHitPaySetupProps } from "./index";
export type IHitPaySetupProps = z.infer<typeof hitpayCredentialKeysSchema>;
export const getServerSideProps = async (ctx: GetServerSidePropsContext) => {
const notFound = { notFound: true } as const;
@@ -5,13 +5,14 @@ import useIsAppEnabled from "@calcom/app-store/_utils/useIsAppEnabled";
import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import { WEBAPP_URL } from "@calcom/lib/constants";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const pathname = usePathname();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
teamId={eventType.team?.id || undefined}
@@ -6,13 +6,14 @@ import { TextField } from "@calcom/ui/components/form";
import type { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const siteId = getAppData("SITE_ID");
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
app={app}
switchOnClick={(e) => {
updateEnabled(e);
@@ -6,7 +6,7 @@ import { TextField } from "@calcom/ui/components/form";
import type { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const matomoUrl = getAppData("MATOMO_URL");
const siteId = getAppData("SITE_ID");
@@ -14,6 +14,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
hideSettingsIcon
app={app}
switchOnClick={(e) => {
@@ -6,12 +6,13 @@ import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
hideSettingsIcon
app={app}
switchOnClick={updateEnabled}
@@ -20,7 +20,7 @@ import { paymentOptions } from "../zod";
type Option = { value: string; label: string };
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const searchParams = useSearchParams();
/** TODO "pathname" no longer contains square-bracket expressions. Rewrite the code relying on them if required. **/
const pathname = usePathname();
@@ -51,6 +51,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={WEBAPP_URL + asPath}
app={app}
switchChecked={requirePayment}
+1 -2
View File
@@ -20,7 +20,6 @@
"@calcom/features": "*",
"@calcom/lib": "*",
"@calcom/office365video": "*",
"@calcom/trpc": "*",
"@calcom/ui": "*",
"@calcom/zoomvideo": "*",
"lodash": "^4.17.21",
@@ -31,4 +30,4 @@
"devDependencies": {
"@calcom/types": "*"
}
}
}
@@ -15,6 +15,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
eventTypeFormMetadata,
onAppInstallSuccess,
}) {
const searchParams = useSearchParams();
/** TODO "pathname" no longer contains square-bracket expressions. Rewrite the code relying on them if required. **/
@@ -32,6 +33,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={WEBAPP_URL + asPath}
app={app}
switchChecked={requirePayment}
@@ -1,7 +1,5 @@
import { z } from "zod";
import type { UpdateAppCredentialsOptions } from "@calcom/trpc/server/routers/viewer/apps/updateAppCredentials.handler";
import Paypal from "./Paypal";
const schema = z.object({
@@ -12,7 +10,9 @@ const schema = z.object({
}),
});
const handlePaypalValidations = async ({ input }: UpdateAppCredentialsOptions) => {
type TUpdateAppCredentialsInputSchema = { credentialId: number; key: { [k: string]: unknown } };
const handlePaypalValidations = async ({ input }: { input: TUpdateAppCredentialsInputSchema }) => {
const validated = schema.safeParse(input);
if (!validated.success) throw new Error("Invalid input");
const { key } = validated.data;
@@ -5,13 +5,14 @@ import useIsAppEnabled from "@calcom/app-store/_utils/useIsAppEnabled";
import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import { WEBAPP_URL } from "@calcom/lib/constants";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const pathname = usePathname();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
teamId={eventType.team?.id || undefined}
@@ -6,13 +6,14 @@ import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
hideSettingsIcon
app={app}
switchOnClick={(e) => {
@@ -6,7 +6,11 @@ import { TextField } from "@calcom/ui/components/form";
import type { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
onAppInstallSuccess,
}) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const trackingId = getAppData("TRACKING_ID");
const apiHost = getAppData("API_HOST");
@@ -14,6 +18,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
hideSettingsIcon
app={app}
switchOnClick={(e) => {
@@ -6,12 +6,13 @@ import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import type { appDataSchema } from "../zod";
import EventTypeAppSettingsInterface from "./EventTypeAppSettingsInterface";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ eventType, app }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ eventType, app, onAppInstallSuccess }) {
const { enabled, updateEnabled } = useIsAppEnabled(app);
const { disabled, getAppData, setAppData } = useAppContextWithSchema<typeof appDataSchema>();
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
app={app}
switchOnClick={(e) => {
updateEnabled(e);
@@ -1,20 +1,18 @@
import { prisma } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import type { App_RoutingForms_Form, User } from "@calcom/prisma/client";
import dayjs from "@calcom/dayjs";
import type { Tasker } from "@calcom/features/tasker/tasker";
import getWebhooks from "@calcom/features/webhooks/lib/getWebhooks";
import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload";
import getOrgIdFromMemberOrTeamId from "@calcom/lib/getOrgIdFromMemberOrTeamId";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { withReporting } from "@calcom/lib/sentryWrapper";
import { prisma } from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
import type { App_RoutingForms_Form, User } from "@calcom/prisma/client";
import { WebhookTriggerEvents } from "@calcom/prisma/enums";
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
import type { Ensure } from "@calcom/types/utils";
import { TRPCError } from "@trpc/server";
import type { FormResponse, SerializableForm, SerializableField, OrderedResponses } from "../types/types";
const moduleLogger = logger.getSubLogger({ prefix: ["routing-forms/lib/formSubmissionUtils"] });
@@ -269,9 +267,7 @@ export const onSubmissionOfFormResponse = async ({
}) => {
if (!form.fields) {
// There is no point in submitting a form that doesn't have fields defined
throw new TRPCError({
code: "BAD_REQUEST",
});
throw new HttpError({ statusCode: 400 });
}
const settings = RoutingFormSettings.parse(form.settings);
let userWithEmails: string[] = [];
@@ -2,11 +2,9 @@ import "@calcom/lib/__mocks__/logger";
import { prisma } from "@calcom/prisma/__mocks__/prisma";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { z } from "zod";
import { findTeamMembersMatchingAttributeLogic } from "@calcom/lib/raqb/findTeamMembersMatchingAttributeLogic";
import { RoutingFormResponseRepository } from "@calcom/lib/server/repository/formResponse";
import type { ZResponseInputSchema } from "@calcom/trpc/server/routers/viewer/routing-forms/response.schema";
import isRouter from "../lib/isRouter";
import routerGetCrmContactOwnerEmail from "./crmRouting/routerGetCrmContactOwnerEmail";
@@ -104,7 +102,14 @@ const mockForm: TargetRoutingFormForResponse = {
updatedById: null,
};
const mockResponse: z.infer<typeof ZResponseInputSchema>["response"] = {
const mockResponse: Record<
string,
{
value: (string | number | string[]) & (string | number | string[] | undefined);
label: string;
identifier?: string | undefined;
}
> = {
name: { value: "John Doe", label: "Name" },
email: { value: "john.doe@example.com", label: "Email" },
};
@@ -118,7 +123,7 @@ describe("handleResponse", () => {
);
});
it("should throw a TRPCError for missing required fields", async () => {
it("should throw an Error for missing required fields", async () => {
await expect(
handleResponse({
response: { email: { value: "test@test.com", label: "Email" } }, // Name is missing
@@ -131,7 +136,7 @@ describe("handleResponse", () => {
).rejects.toThrow(/Missing required fields Name/);
});
it("should throw a TRPCError for invalid email", async () => {
it("should throw an Error for invalid email", async () => {
await expect(
handleResponse({
response: {
@@ -1,6 +1,7 @@
import { z } from "zod";
import { emailSchema } from "@calcom/lib/emailSchema";
import { HttpError } from "@calcom/lib/http-error";
import logger from "@calcom/lib/logger";
import { findTeamMembersMatchingAttributeLogic } from "@calcom/lib/raqb/findTeamMembersMatchingAttributeLogic";
import { safeStringify } from "@calcom/lib/safeStringify";
@@ -8,9 +9,6 @@ import { withReporting } from "@calcom/lib/sentryWrapper";
import { RoutingFormResponseRepository } from "@calcom/lib/server/repository/formResponse";
import { prisma } from "@calcom/prisma";
import { Prisma } from "@calcom/prisma/client";
import type { ZResponseInputSchema } from "@calcom/trpc/server/routers/viewer/routing-forms/response.schema";
import { TRPCError } from "@trpc/server";
import isRouter from "../lib/isRouter";
import routerGetCrmContactOwnerEmail from "./crmRouting/routerGetCrmContactOwnerEmail";
@@ -29,7 +27,14 @@ const _handleResponse = async ({
queueFormResponse,
fetchCrm,
}: {
response: z.infer<typeof ZResponseInputSchema>["response"];
response: Record<
string,
{
value: string | number | string[];
label: string;
identifier?: string;
}
>;
identifierKeyedResponse: Record<string, string | string[]> | null;
form: TargetRoutingFormForResponse;
formFillerId: string;
@@ -41,9 +46,7 @@ const _handleResponse = async ({
try {
if (!form.fields) {
// There is no point in submitting a form that doesn't have fields defined
throw new TRPCError({
code: "BAD_REQUEST",
});
throw new HttpError({ statusCode: 400 });
}
const formTeamId = form.teamId;
@@ -58,8 +61,8 @@ const _handleResponse = async ({
.map((f) => f.label);
if (missingFields.length) {
throw new TRPCError({
code: "BAD_REQUEST",
throw new HttpError({
statusCode: 400,
message: `Missing required fields ${missingFields.join(", ")}`,
});
}
@@ -83,8 +86,8 @@ const _handleResponse = async ({
.map((f) => ({ label: f.label, type: f.type, value: response[f.id]?.value }));
if (invalidFields.length) {
throw new TRPCError({
code: "BAD_REQUEST",
throw new HttpError({
statusCode: 400,
message: `Invalid value for fields ${invalidFields
.map((f) => `'${f.label}' with value '${f.value}' should be valid ${f.type}`)
.join(", ")}`,
@@ -100,8 +103,8 @@ const _handleResponse = async ({
let timeTaken: Record<string, number | null> = {};
if (chosenRoute) {
if (isRouter(chosenRoute)) {
throw new TRPCError({
code: "BAD_REQUEST",
throw new HttpError({
statusCode: 400,
message: "Chosen route is a router",
});
}
@@ -225,8 +228,9 @@ const _handleResponse = async ({
} catch (e) {
if (e instanceof Prisma.PrismaClientKnownRequestError) {
if (e.code === "P2002") {
throw new TRPCError({
code: "CONFLICT",
throw new HttpError({
statusCode: 409,
message: "Form response already exists",
});
}
}
@@ -1,6 +0,0 @@
export const routingFormsComponents = {
"form-edit": () => import("./form-edit/[...appPages]").then((mod) => mod.default),
"route-builder": () => import("./route-builder/[...appPages]").then((mod) => mod.default),
"routing-link": () => import("./routing-link/[...appPages]").then((mod) => mod.default),
"incomplete-booking": () => import("./incomplete-booking/[...appPages]").then((mod) => mod.default),
};
@@ -1,20 +0,0 @@
import type { UseFormReturn } from "react-hook-form";
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import type { getServerSidePropsForSingleFormView } from "../components/getServerSidePropsSingleForm";
import type { RoutingFormWithResponseCount } from "../types/types";
export type SingleFormComponentProps = {
form: RoutingFormWithResponseCount;
appUrl: string;
Page: React.FC<{
form: RoutingFormWithResponseCount;
appUrl: string;
hookForm: UseFormReturn<RoutingFormWithResponseCount>;
}>;
enrichedWithUserProfileForm: inferSSRProps<
typeof getServerSidePropsForSingleFormView
>["enrichedWithUserProfileForm"];
permissions: inferSSRProps<typeof getServerSidePropsForSingleFormView>["permissions"];
};
@@ -20,7 +20,7 @@ import { SalesforceRecordEnum } from "../lib/enums";
import type { appDataSchema } from "../zod";
import WriteToObjectSettings, { BookingActionEnum } from "./components/WriteToObjectSettings";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const pathname = usePathname();
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
@@ -95,6 +95,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
teamId={eventType.team?.id || undefined}
@@ -16,6 +16,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
eventTypeFormMetadata,
onAppInstallSuccess,
}) {
const { t } = useLocale();
const pathname = usePathname();
@@ -27,6 +28,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
switchChecked={enabled}
@@ -7,13 +7,14 @@ import { TextField } from "@calcom/ui/components/form";
import type { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const { getAppData, setAppData } = useAppContextWithSchema<typeof appDataSchema>();
const trackingId = getAppData("trackingId");
const [enabled, setEnabled] = useState(getAppData("enabled"));
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
app={app}
switchOnClick={(e) => {
if (!e) {
@@ -6,13 +6,18 @@ import { Icon } from "@calcom/ui/components/icon";
import type { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ eventType, app }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
eventType,
app,
onAppInstallSuccess,
}) {
const { getAppData, setAppData } = useAppContextWithSchema<typeof appDataSchema>();
const isSunrise = getAppData("isSunrise");
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
app={app}
switchOnClick={(e) => {
if (!e) {
-6
View File
@@ -1,6 +0,0 @@
// TODO: Generate this file automatically. This supposed to contain trpc-routes from all the apps automatically imported here and then exported.
// Can't use this file right now as I am not able to figure out how to keep getting tRPC typesafety with merge calls done on already created router
// Till that time import routers from each app directly to core.
// import { Router } from "@trpc/server/dist/declarations/src/router";
export {};
@@ -6,13 +6,14 @@ import { TextField } from "@calcom/ui/components/form";
import type { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType, onAppInstallSuccess }) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const siteId = getAppData("SITE_ID");
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
app={app}
switchOnClick={(e) => {
updateEnabled(e);
+1
View File
@@ -65,6 +65,7 @@ export type EventTypeAppCardComponentProps = {
URL: string;
};
app: EventTypeAppCardApp;
onAppInstallSuccess: () => void;
disabled?: boolean;
LockedIcon?: JSX.Element | false;
eventTypeFormMetadata?: z.infer<typeof EventTypeFormMetadataSchema>;
@@ -6,7 +6,11 @@ import { TextField } from "@calcom/ui/components/form";
import type { appDataSchema } from "../zod";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
onAppInstallSuccess,
}) {
const { getAppData, setAppData, disabled } = useAppContextWithSchema<typeof appDataSchema>();
const siteId = getAppData("SITE_ID");
const scriptURL = getAppData("SCRIPT_URL");
@@ -14,6 +18,7 @@ const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
app={app}
switchOnClick={(e) => {
updateEnabled(e);
@@ -1 +0,0 @@
export { WipeMyCalActionButton } from "./wipeMyCalActionButton";
@@ -5,13 +5,18 @@ import useIsAppEnabled from "@calcom/app-store/_utils/useIsAppEnabled";
import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import { WEBAPP_URL } from "@calcom/lib/constants";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
onAppInstallSuccess,
}) {
const pathname = usePathname();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
teamId={eventType.team?.id || undefined}
@@ -5,13 +5,18 @@ import useIsAppEnabled from "@calcom/app-store/_utils/useIsAppEnabled";
import type { EventTypeAppCardComponent } from "@calcom/app-store/types";
import { WEBAPP_URL } from "@calcom/lib/constants";
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({ app, eventType }) {
const EventTypeAppCard: EventTypeAppCardComponent = function EventTypeAppCard({
app,
eventType,
onAppInstallSuccess,
}) {
const pathname = usePathname();
const { enabled, updateEnabled } = useIsAppEnabled(app);
return (
<AppCard
onAppInstallSuccess={onAppInstallSuccess}
returnTo={`${WEBAPP_URL}${pathname}?tabName=apps`}
app={app}
teamId={eventType.team?.id || undefined}
@@ -25,6 +25,8 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
teamId: eventType.team?.id || eventType.parent?.teamId,
});
const utils = trpc.useUtils();
const formMethods = useFormContext<FormValues>();
const installedApps =
eventTypeApps?.items.filter((app) => app.userCredentialIds.length || app.teams.length) || [];
@@ -41,6 +43,14 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
const appsDisableProps = shouldLockDisableProps("apps", { simple: true });
const lockedText = appsDisableProps.isLocked ? "locked" : "unlocked";
const handleAppInstallSuccess = (appId: string) => () => {
utils.viewer.apps.appById.invalidate({ appId });
utils.viewer.apps.integrations.invalidate({
extendsFeature: "EventType",
...(eventType.team?.id && { teamId: eventType.team.id }),
});
};
const appsWithTeamCredentials = eventTypeApps?.items.filter((app) => app.teams.length) || [];
const cardsForAppsWithTeams = appsWithTeamCredentials.map((app) => {
const appCards = [];
@@ -58,6 +68,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
app={app}
eventType={eventType}
eventTypeFormMetadata={eventTypeFormMetadata}
onAppInstallSuccess={handleAppInstallSuccess(app.slug)}
/>
);
}
@@ -82,6 +93,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
eventType={eventType}
eventTypeFormMetadata={eventTypeFormMetadata}
disabled={shouldLockDisableProps("apps").disabled}
onAppInstallSuccess={handleAppInstallSuccess(app.slug)}
/>
);
}
@@ -147,6 +159,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
app={app}
eventType={eventType}
eventTypeFormMetadata={eventTypeFormMetadata}
onAppInstallSuccess={handleAppInstallSuccess(app.slug)}
/>
);
})}
@@ -180,6 +193,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
app={app}
eventType={eventType}
eventTypeFormMetadata={eventTypeFormMetadata}
onAppInstallSuccess={handleAppInstallSuccess(app.slug)}
/>
))}
</div>
@@ -10,7 +10,6 @@ import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSeri
import { handleResponse } from "@calcom/app-store/routing-forms/lib/handleResponse";
import { findMatchingRoute } from "@calcom/app-store/routing-forms/lib/processRoute";
import { substituteVariables } from "@calcom/app-store/routing-forms/lib/substituteVariables";
import { getUrlSearchParamsToForward } from "@calcom/app-store/routing-forms/pages/routing-link/getUrlSearchParamsToForward";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { isAuthorizedToViewFormOnOrgDomain } from "@calcom/features/routing-forms/lib/isAuthorizedToViewForm";
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
@@ -18,8 +17,10 @@ import { PrismaRoutingFormRepository } from "@calcom/lib/server/repository/Prism
import { UserRepository } from "@calcom/lib/server/repository/user";
import { getRoutedUrl } from "./getRoutedUrl";
import { getUrlSearchParamsToForward } from "./getUrlSearchParamsToForward";
// Mock dependencies
vi.mock("./getUrlSearchParamsToForward");
vi.mock("@calcom/lib/checkRateLimitAndThrowError");
vi.mock("@calcom/app-store/routing-forms/lib/handleResponse");
vi.mock("@calcom/lib/server/repository/PrismaRoutingFormRepository");
@@ -36,7 +37,6 @@ vi.mock("@calcom/app-store/routing-forms/lib/getSerializableForm");
vi.mock("@calcom/app-store/routing-forms/lib/getResponseToStore");
vi.mock("@calcom/app-store/routing-forms/lib/processRoute");
vi.mock("@calcom/app-store/routing-forms/lib/substituteVariables");
vi.mock("@calcom/app-store/routing-forms/pages/routing-link/getUrlSearchParamsToForward");
vi.mock("@calcom/app-store/routing-forms/getEventTypeRedirectUrl");
vi.mock("@calcom/app-store/routing-forms/enrichFormWithMigrationData", () => ({
enrichFormWithMigrationData: vi.fn((form) => form),
@@ -13,7 +13,6 @@ import { getServerTimingHeader } from "@calcom/app-store/routing-forms/lib/getSe
import { handleResponse } from "@calcom/app-store/routing-forms/lib/handleResponse";
import { findMatchingRoute } from "@calcom/app-store/routing-forms/lib/processRoute";
import { substituteVariables } from "@calcom/app-store/routing-forms/lib/substituteVariables";
import { getUrlSearchParamsToForward } from "@calcom/app-store/routing-forms/pages/routing-link/getUrlSearchParamsToForward";
import type { FormResponse } from "@calcom/app-store/routing-forms/types/types";
import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains";
import { isAuthorizedToViewFormOnOrgDomain } from "@calcom/features/routing-forms/lib/isAuthorizedToViewForm";
@@ -27,6 +26,8 @@ import prisma from "@calcom/prisma";
import { TRPCError } from "@trpc/server";
import { getUrlSearchParamsToForward } from "./getUrlSearchParamsToForward";
const log = logger.getSubLogger({ prefix: ["[routing-forms]", "[router]"] });
const querySchema = z
.object({
@@ -1,12 +1,9 @@
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import { ROUTING_FORM_RESPONSE_ID_QUERY_STRING } from "../../lib/constants";
import getFieldIdentifier from "../../lib/getFieldIdentifier";
import type { FormResponse, LocalRoute } from "../../types/types";
import type { getServerSideProps } from "./getServerSideProps";
import { ROUTING_FORM_RESPONSE_ID_QUERY_STRING } from "@calcom/app-store/routing-forms/lib/constants";
import getFieldIdentifier from "@calcom/app-store/routing-forms/lib/getFieldIdentifier";
import type { FormResponse, LocalRoute } from "@calcom/app-store/routing-forms/types/types";
type FormResponseValueOnly = { [key: string]: { value: FormResponse[keyof FormResponse]["value"] } };
type Props = inferSSRProps<typeof getServerSideProps>;
type AttributeRoutingConfig = NonNullable<LocalRoute["attributeRoutingConfig"]>;
type GetUrlSearchParamsToForwardOptions = {
formResponse: Record<
@@ -15,10 +12,16 @@ type GetUrlSearchParamsToForwardOptions = {
value: number | string | string[];
}
>;
fields: Pick<
NonNullable<Props["form"]["fields"]>[number],
"id" | "type" | "options" | "identifier" | "label"
>[];
fields: {
id: string;
type: string;
label: string;
options?: {
id: string | null;
label: string;
}[];
identifier?: string;
}[];
searchParams: URLSearchParams;
formResponseId: number | null;
queuedFormResponseId: string | null;
@@ -18,7 +18,6 @@ const validators = {
};
export const handleCustomValidations = async ({
ctx,
input,
appId,
}: UpdateAppCredentialsOptions & { appId: string }) => {
@@ -28,7 +27,7 @@ export const handleCustomValidations = async ({
if (!validatorGetter) return key;
try {
const validator = (await validatorGetter()).default;
return await validator({ input, ctx });
return await validator({ input });
} catch (error) {
throw new TRPCError({
code: "BAD_REQUEST",
@@ -7,7 +7,7 @@ import type { ServerResponse } from "http";
import type { NextApiResponse } from "next";
import { enrichFormWithMigrationData } from "@calcom/app-store/routing-forms/enrichFormWithMigrationData";
import { getUrlSearchParamsToForwardForTestPreview } from "@calcom/app-store/routing-forms/pages/routing-link/getUrlSearchParamsToForward";
import { getUrlSearchParamsToForwardForTestPreview } from "@calcom/features/routing-forms/lib/getUrlSearchParamsToForward";
import { enrichHostsWithDelegationCredentials } from "@calcom/lib/delegationCredential/server";
import { getLuckyUserService } from "@calcom/lib/di/containers/LuckyUser";
import { entityPrismaWhereClause } from "@calcom/lib/entityPermissionUtils.server";