Fix/Next.js-13/Circular Dependency in Routing Forms (#6321)
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
co-authored by
Peer Richelsen
parent
b1193eef1e
commit
364e187080
@@ -1,8 +1,8 @@
|
||||
import { App_RoutingForms_Form } from "@prisma/client";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
|
||||
import { getQueryBuilderConfig } from "../lib/getQueryBuilderConfig";
|
||||
import isRouterLinkedField from "../lib/isRouterLinkedField";
|
||||
import { getQueryBuilderConfig } from "../pages/route-builder/[...appPages]";
|
||||
import { SerializableForm, Response } from "../types/types";
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -35,9 +35,9 @@ import {
|
||||
VerticalDivider,
|
||||
} from "@calcom/ui";
|
||||
|
||||
import { RoutingPages } from "../lib/RoutingPages";
|
||||
import { getSerializableForm } from "../lib/getSerializableForm";
|
||||
import { processRoute } from "../lib/processRoute";
|
||||
import { RoutingPages } from "../pages/route-builder/[...appPages]";
|
||||
import { Response, Route, SerializableForm } from "../types/types";
|
||||
import { FormAction, FormActionsDropdown, FormActionsProvider } from "./FormActions";
|
||||
import FormInputFields from "./FormInputFields";
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import QueryBuilderInitialConfig from "../components/react-awesome-query-builder/config/config";
|
||||
|
||||
export const InitialConfig = QueryBuilderInitialConfig;
|
||||
@@ -0,0 +1,16 @@
|
||||
import { LocalRoute } from "../types/types";
|
||||
|
||||
export const RoutingPages: { label: string; value: NonNullable<LocalRoute["action"]>["type"] }[] = [
|
||||
{
|
||||
label: "Custom Page",
|
||||
value: "customPageMessage",
|
||||
},
|
||||
{
|
||||
label: "External Redirect",
|
||||
value: "externalRedirectUrl",
|
||||
},
|
||||
{
|
||||
label: "Event Redirect",
|
||||
value: "eventTypeRedirectUrl",
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,63 @@
|
||||
import { FieldTypes } from "../pages/form-edit/[...appPages]";
|
||||
import { QueryBuilderUpdatedConfig, RoutingForm } from "../types/types";
|
||||
import { InitialConfig } from "./InitialConfig";
|
||||
|
||||
export function getQueryBuilderConfig(form: RoutingForm, forReporting = false) {
|
||||
const fields: Record<
|
||||
string,
|
||||
{
|
||||
label: string;
|
||||
type: string;
|
||||
valueSources: ["value"];
|
||||
fieldSettings: {
|
||||
listValues?: {
|
||||
value: string;
|
||||
title: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
> = {};
|
||||
form.fields?.forEach((field) => {
|
||||
if ("routerField" in field) {
|
||||
field = field.routerField;
|
||||
}
|
||||
if (FieldTypes.map((f) => f.value).includes(field.type)) {
|
||||
const optionValues = field.selectText?.trim().split("\n");
|
||||
const options = optionValues?.map((value) => {
|
||||
const title = value;
|
||||
return {
|
||||
value,
|
||||
title,
|
||||
};
|
||||
});
|
||||
|
||||
const widget = InitialConfig.widgets[field.type];
|
||||
const widgetType = widget.type;
|
||||
|
||||
fields[field.id] = {
|
||||
label: field.label,
|
||||
type: widgetType,
|
||||
valueSources: ["value"],
|
||||
fieldSettings: {
|
||||
listValues: options,
|
||||
},
|
||||
// preferWidgets: field.type === "textarea" ? ["textarea"] : [],
|
||||
};
|
||||
} else {
|
||||
throw new Error("Unsupported field type:" + field.type);
|
||||
}
|
||||
});
|
||||
|
||||
const initialConfigCopy = { ...InitialConfig, operators: { ...InitialConfig.operators } };
|
||||
if (forReporting) {
|
||||
delete initialConfigCopy.operators.is_empty;
|
||||
delete initialConfigCopy.operators.is_not_empty;
|
||||
initialConfigCopy.operators.__calReporting = true;
|
||||
}
|
||||
// You need to provide your own config. See below 'Config format'
|
||||
const config: QueryBuilderUpdatedConfig = {
|
||||
...initialConfigCopy,
|
||||
fields: fields,
|
||||
};
|
||||
return config;
|
||||
}
|
||||
@@ -2,9 +2,9 @@ import jsonLogic from "json-logic-js";
|
||||
import { Utils as QbUtils } from "react-awesome-query-builder";
|
||||
import { z } from "zod";
|
||||
|
||||
import { getQueryBuilderConfig } from "../pages/route-builder/[...appPages]";
|
||||
import { Response, Route, SerializableForm } from "../types/types";
|
||||
import { zodNonRouterRoute } from "../zod";
|
||||
import { getQueryBuilderConfig } from "./getQueryBuilderConfig";
|
||||
import { isFallbackRoute } from "./isFallbackRoute";
|
||||
import isRouter from "./isRouter";
|
||||
import { App_RoutingForms_Form } from ".prisma/client";
|
||||
|
||||
@@ -24,7 +24,7 @@ import SingleForm, {
|
||||
import QueryBuilderInitialConfig from "../../components/react-awesome-query-builder/config/config";
|
||||
import "../../components/react-awesome-query-builder/styles.css";
|
||||
import { JsonLogicQuery } from "../../jsonLogicToPrisma";
|
||||
import { getQueryBuilderConfig } from "../route-builder/[...appPages]";
|
||||
import { getQueryBuilderConfig } from "../../lib/getQueryBuilderConfig";
|
||||
|
||||
export { getServerSideProps };
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { App_RoutingForms_Form } from "@prisma/client";
|
||||
import Link from "next/link";
|
||||
import React, { useCallback, useState } from "react";
|
||||
import { Query, Config, Builder, Utils as QbUtils } from "react-awesome-query-builder";
|
||||
import { Query, Builder, Utils as QbUtils } from "react-awesome-query-builder";
|
||||
// types
|
||||
import { JsonTree, ImmutableTree, BuilderProps } from "react-awesome-query-builder";
|
||||
|
||||
@@ -21,83 +20,19 @@ import {
|
||||
import SingleForm, {
|
||||
getServerSidePropsForSingleFormView as getServerSideProps,
|
||||
} from "../../components/SingleForm";
|
||||
import QueryBuilderInitialConfig from "../../components/react-awesome-query-builder/config/config";
|
||||
import "../../components/react-awesome-query-builder/styles.css";
|
||||
import { RoutingPages } from "../../lib/RoutingPages";
|
||||
import { createFallbackRoute } from "../../lib/createFallbackRoute";
|
||||
import { getQueryBuilderConfig } from "../../lib/getQueryBuilderConfig";
|
||||
import isRouter from "../../lib/isRouter";
|
||||
import { GlobalRoute, LocalRoute, SerializableForm, SerializableRoute } from "../../types/types";
|
||||
import { FieldTypes } from "../form-edit/[...appPages]";
|
||||
import { GlobalRoute, LocalRoute, QueryBuilderUpdatedConfig, SerializableRoute } from "../../types/types";
|
||||
|
||||
export { getServerSideProps };
|
||||
|
||||
type RoutingForm = SerializableForm<App_RoutingForms_Form>;
|
||||
|
||||
const InitialConfig = QueryBuilderInitialConfig;
|
||||
const hasRules = (route: Route) => {
|
||||
if (isRouter(route)) return false;
|
||||
route.queryValue.children1 && Object.keys(route.queryValue.children1).length;
|
||||
};
|
||||
type QueryBuilderUpdatedConfig = typeof QueryBuilderInitialConfig & { fields: Config["fields"] };
|
||||
export function getQueryBuilderConfig(form: RoutingForm, forReporting = false) {
|
||||
const fields: Record<
|
||||
string,
|
||||
{
|
||||
label: string;
|
||||
type: string;
|
||||
valueSources: ["value"];
|
||||
fieldSettings: {
|
||||
listValues?: {
|
||||
value: string;
|
||||
title: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
> = {};
|
||||
form.fields?.forEach((field) => {
|
||||
if ("routerField" in field) {
|
||||
field = field.routerField;
|
||||
}
|
||||
if (FieldTypes.map((f) => f.value).includes(field.type)) {
|
||||
const optionValues = field.selectText?.trim().split("\n");
|
||||
const options = optionValues?.map((value) => {
|
||||
const title = value;
|
||||
return {
|
||||
value,
|
||||
title,
|
||||
};
|
||||
});
|
||||
|
||||
const widget = InitialConfig.widgets[field.type];
|
||||
const widgetType = widget.type;
|
||||
|
||||
fields[field.id] = {
|
||||
label: field.label,
|
||||
type: widgetType,
|
||||
valueSources: ["value"],
|
||||
fieldSettings: {
|
||||
listValues: options,
|
||||
},
|
||||
// preferWidgets: field.type === "textarea" ? ["textarea"] : [],
|
||||
};
|
||||
} else {
|
||||
throw new Error("Unsupported field type:" + field.type);
|
||||
}
|
||||
});
|
||||
|
||||
const initialConfigCopy = { ...InitialConfig, operators: { ...InitialConfig.operators } };
|
||||
if (forReporting) {
|
||||
delete initialConfigCopy.operators.is_empty;
|
||||
delete initialConfigCopy.operators.is_not_empty;
|
||||
initialConfigCopy.operators.__calReporting = true;
|
||||
}
|
||||
// You need to provide your own config. See below 'Config format'
|
||||
const config: QueryBuilderUpdatedConfig = {
|
||||
...initialConfigCopy,
|
||||
fields: fields,
|
||||
};
|
||||
return config;
|
||||
}
|
||||
|
||||
const getEmptyRoute = (): Exclude<SerializableRoute, GlobalRoute> => {
|
||||
const uuid = QbUtils.uuid();
|
||||
return {
|
||||
@@ -122,21 +57,6 @@ type Route =
|
||||
})
|
||||
| GlobalRoute;
|
||||
|
||||
export const RoutingPages: { label: string; value: NonNullable<LocalRoute["action"]>["type"] }[] = [
|
||||
{
|
||||
label: "Custom Page",
|
||||
value: "customPageMessage",
|
||||
},
|
||||
{
|
||||
label: "External Redirect",
|
||||
value: "externalRedirectUrl",
|
||||
},
|
||||
{
|
||||
label: "Event Redirect",
|
||||
value: "eventTypeRedirectUrl",
|
||||
},
|
||||
];
|
||||
|
||||
const Route = ({
|
||||
route,
|
||||
routes,
|
||||
|
||||
@@ -92,6 +92,9 @@ test.describe("Routing Forms", () => {
|
||||
|
||||
// Expect F1 fields to be available in F2
|
||||
await page.goto(`/apps/routing-forms/form-edit/${form2Id}`);
|
||||
//FIXME: Figure out why this delay is required. Without it field count comes out to be 1 only
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
|
||||
expect(await page.locator('[data-testid="field"]').count()).toBe(2);
|
||||
await expectCurrentFormToHaveFields(page, { 1: { label: "F1 Field1", typeIndex: 1 } }, types);
|
||||
// Add 1 more field in F1
|
||||
@@ -103,6 +106,8 @@ test.describe("Routing Forms", () => {
|
||||
});
|
||||
|
||||
await page.goto(`/apps/routing-forms/form-edit/${form2Id}`);
|
||||
//FIXME: Figure out why this delay is required. Without it field count comes out to be 1 only
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
expect(await page.locator('[data-testid="field"]').count()).toBe(3);
|
||||
await expectCurrentFormToHaveFields(page, { 2: { label: "F1 Field2", typeIndex: 1 } }, types);
|
||||
});
|
||||
|
||||
@@ -3,8 +3,13 @@ import z from "zod";
|
||||
|
||||
import { RoutingFormSettings } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import QueryBuilderInitialConfig from "../components/react-awesome-query-builder/config/config";
|
||||
import { zodRouterRouteView, zodNonRouterRoute, zodFieldsView, zodRoutesView } from "../zod";
|
||||
|
||||
export type RoutingForm = SerializableForm<App_RoutingForms_Form>;
|
||||
|
||||
export type QueryBuilderUpdatedConfig = typeof QueryBuilderInitialConfig & { fields: Config["fields"] };
|
||||
|
||||
export type Response = Record<
|
||||
// Field ID
|
||||
string,
|
||||
|
||||
Reference in New Issue
Block a user