Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ function Field({
"h-8 w-full justify-between text-left text-sm",
!!router && "bg-subtle cursor-not-allowed"
)}>
<span className="text-default">{defaultValue?.label || t("select_field_type")}</span>
<span className="text-default">
{defaultValue?.label || t("select_field_type")}
</span>
<ChevronDownIcon className="text-default h-4 w-4" />
</Button>
</Tooltip>
Expand Down Expand Up @@ -201,7 +203,7 @@ function Field({
}}
/>
</div>
{["select", "multiselect"].includes(fieldType) ? (
{["select", "multiselect", "checkbox", "radio"].includes(fieldType) ? (
<div className="bg-cal-muted w-full rounded-[10px] p-2">
<Label className="text-subtle">{t("options")}</Label>
<MultiOptionInput
Expand Down
32 changes: 19 additions & 13 deletions apps/web/modules/insights/hooks/useInsightsColumns.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
import { createColumnHelper } from "@tanstack/react-table";
import startCase from "lodash/startCase";
import { useMemo } from "react";
import { z } from "zod";

import dayjs from "@calcom/dayjs";
import { ColumnFilterType } from "@calcom/features/data-table";
import { WEBAPP_URL } from "@calcom/lib/constants";
Expand All @@ -11,18 +6,21 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
import { RoutingFormFieldType } from "@calcom/routing-forms/lib/FieldTypes";
import { Badge } from "@calcom/ui/components/badge";
import { Button } from "@calcom/ui/components/button";

import { BookedByCell } from "../components/BookedByCell";
import { BookingAtCell } from "../components/BookingAtCell";
import { BookingStatusBadge } from "../components/BookingStatusBadge";
import { ResponseValueCell } from "../components/ResponseValueCell";
import type { HeaderRow, RoutingFormTableRow } from "@calcom/web/modules/insights/lib/types";
import {
ZResponseMultipleValues,
ZResponseNumericValue,
ZResponseSingleValue,
ZResponseTextValue,
ZResponseNumericValue,
} from "@calcom/web/modules/insights/lib/types";
import { createColumnHelper } from "@tanstack/react-table";
import startCase from "lodash/startCase";
import { useMemo } from "react";
import { z } from "zod";
import { BookedByCell } from "../components/BookedByCell";
import { BookingAtCell } from "../components/BookingAtCell";
import { BookingStatusBadge } from "../components/BookingStatusBadge";
import { ResponseValueCell } from "../components/ResponseValueCell";

export const useInsightsColumns = ({
headers,
Expand Down Expand Up @@ -155,12 +153,20 @@ export const useInsightsColumns = ({
RoutingFormFieldType.EMAIL,
RoutingFormFieldType.PHONE,
RoutingFormFieldType.TEXTAREA,
RoutingFormFieldType.ADDRESS,
RoutingFormFieldType.MULTIEMAIL,
RoutingFormFieldType.URL,
RoutingFormFieldType.BOOLEAN,
].includes(fieldHeader.type as RoutingFormFieldType);

const isNumber = fieldHeader.type === RoutingFormFieldType.NUMBER;

const isSingleSelect = fieldHeader.type === RoutingFormFieldType.SINGLE_SELECT;
const isMultiSelect = fieldHeader.type === RoutingFormFieldType.MULTI_SELECT;
const isSingleSelect =
fieldHeader.type === RoutingFormFieldType.SINGLE_SELECT ||
fieldHeader.type === RoutingFormFieldType.RADIO;
const isMultiSelect =
fieldHeader.type === RoutingFormFieldType.MULTI_SELECT ||
fieldHeader.type === RoutingFormFieldType.CHECKBOX;

const filterType = isSingleSelect
? ColumnFilterType.SINGLE_SELECT
Expand Down
14 changes: 8 additions & 6 deletions packages/app-store/routing-forms/__tests__/config.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { describe, it, vi, expect } from "vitest";

import { describe, expect, it, vi } from "vitest";
import {
FormFieldsBaseConfig,
AttributesBaseConfig,
FormFieldsBaseConfig,
} from "../components/react-awesome-query-builder/config/config";

vi.mock("../components/react-awesome-query-builder/widgets", () => ({
Expand All @@ -25,6 +24,9 @@ const assertCommonWidgetTypes = (config: any) => {
expect(config.widgets).toHaveProperty("select");
expect(config.widgets).toHaveProperty("phone");
expect(config.widgets).toHaveProperty("email");
expect(config.widgets).toHaveProperty("address");
expect(config.widgets).toHaveProperty("url");
expect(config.widgets).toHaveProperty("multiemail");
};

const assertSelectOperators = (config: any) => {
Expand Down Expand Up @@ -95,7 +97,7 @@ describe("Query Builder Config", () => {
);

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// @ts-expect-error
const jsonLogic = AttributesBaseConfig.operators.multiselect_some_in.jsonLogic(
["A"],
"multiselect_some_in",
Expand All @@ -122,7 +124,7 @@ describe("Query Builder Config", () => {

it("should provide jsonlogic for between operator", () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// @ts-expect-error
const jsonLogic = AttributesBaseConfig.operators.between.jsonLogic(
{ var: "89ee81ae-953c-409b-aacc-700e1ce5ae20" },
"between",
Expand All @@ -140,7 +142,7 @@ describe("Query Builder Config", () => {

it("should provide jsonlogic for not_between operator", () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// @ts-expect-error
const jsonLogic = AttributesBaseConfig.operators.not_between.jsonLogic(
{ var: "89ee81ae-953c-409b-aacc-700e1ce5ae20" },
"not_between",
Expand Down
33 changes: 31 additions & 2 deletions packages/app-store/routing-forms/__tests__/uiConfig.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { Settings } from "react-awesome-query-builder";
import { describe, it, vi, expect } from "vitest";

import { describe, expect, it, vi } from "vitest";
import {
ConfigFor,
withRaqbSettingsAndWidgets,
Expand All @@ -14,6 +13,12 @@ vi.mock("../components/react-awesome-query-builder/widgets", () => ({
MultiSelectWidget: vi.fn(),
SelectWidget: vi.fn(),
NumberWidget: vi.fn(),
AddressWidget: vi.fn(),
URLWidget: vi.fn(),
MultiEmailWidget: vi.fn(),
CheckboxGroupWidget: vi.fn(),
RadioGroupWidget: vi.fn(),
BooleanWidget: vi.fn(),
FieldSelect: vi.fn(),
Conjs: vi.fn(),
Button: vi.fn(),
Expand Down Expand Up @@ -46,6 +51,24 @@ describe("uiConfig", () => {
email: {
type: "email",
},
address: {
type: "address",
},
url: {
type: "url",
},
multiemail: {
type: "multiemail",
},
checkbox: {
type: "checkbox",
},
radio: {
type: "radio",
},
boolean: {
type: "boolean",
},
},
settings: {} as Settings,
};
Expand All @@ -65,6 +88,12 @@ describe("uiConfig", () => {
expect(result.widgets.select).toHaveProperty("factory");
expect(result.widgets.phone).toHaveProperty("factory");
expect(result.widgets.email).toHaveProperty("factory");
expect(result.widgets.address).toHaveProperty("factory");
expect(result.widgets.url).toHaveProperty("factory");
expect(result.widgets.multiemail).toHaveProperty("factory");
expect(result.widgets.checkbox).toHaveProperty("factory");
expect(result.widgets.radio).toHaveProperty("factory");
expect(result.widgets.boolean).toHaveProperty("factory");
});

it("should add render functions to settings", () => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// This is taken from "react-awesome-query-builder/lib/config/basic";
import type {
Conjunction as RAQBConjunction,
Widget as RAQBWidget,
Type as RAQBType,
Settings as RAQBSettings,
Operator as RAQBOperator,
Settings as RAQBSettings,
Type as RAQBType,
Widget as RAQBWidget,
} from "react-awesome-query-builder";

export type Conjunction = RAQBConjunction;
Expand Down Expand Up @@ -284,6 +284,30 @@ const widgets: WidgetsWithoutFactory = {
valuePlaceholder: "Select values",
toJS: (val: any) => val,
},
checkbox: {
type: "multiselect",
jsType: "array",
valueSrc: "value" as const,
valueLabel: "Values",
valuePlaceholder: "Select values",
toJS: (val: any) => val,
},
radio: {
type: "select",
jsType: "string",
valueSrc: "value" as const,
valueLabel: "Value",
valuePlaceholder: "Select value",
toJS: (val: any) => val,
},
boolean: {
type: "boolean",
jsType: "boolean",
valueSrc: "value" as const,
valueLabel: "Value",
valuePlaceholder: "",
toJS: (val: any) => val,
},
};

const types: Types = {
Expand Down Expand Up @@ -388,6 +412,15 @@ const types: Types = {
},
},
},
boolean: {
defaultOperator: "equal",
mainWidget: "boolean",
widgets: {
boolean: {
operators: ["equal", "not_equal"],
},
},
},
// "!group": {
// defaultOperator: "some",
// mainWidget: "number",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// Figure out why routing-forms/env.d.ts doesn't work
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
//@ts-expect-error
import type { Operators, Types } from "./BasicConfig";
import BasicConfig from "./BasicConfig";
import { ConfigFor } from "./types";
import type { WidgetsWithoutFactory } from "./types";
import { ConfigFor } from "./types";

function getWidgetsWithoutFactory(_configFor: ConfigFor) {
const widgetsWithoutFactory: WidgetsWithoutFactory = {
Expand All @@ -15,6 +15,15 @@ function getWidgetsWithoutFactory(_configFor: ConfigFor) {
email: {
...BasicConfig.widgets.text,
},
address: {
...BasicConfig.widgets.text,
},
url: {
...BasicConfig.widgets.text,
},
multiemail: {
...BasicConfig.widgets.text,
},
};
return widgetsWithoutFactory;
}
Expand Down Expand Up @@ -42,6 +51,44 @@ function getTypes(configFor: ConfigFor) {
...BasicConfig.types.text.widgets,
},
},
address: {
...BasicConfig.types.text,
widgets: {
...BasicConfig.types.text.widgets,
},
},
url: {
...BasicConfig.types.text,
widgets: {
...BasicConfig.types.text.widgets,
},
},
multiemail: {
...BasicConfig.types.text,
widgets: {
...BasicConfig.types.text.widgets,
},
},
checkbox: {
...BasicConfig.types.multiselect,
widgets: {
...BasicConfig.types.multiselect.widgets,
// Checkbox uses the checkbox widget for rendering but multiselect type for query logic
checkbox: {
...BasicConfig.types.multiselect.widgets.multiselect,
operators: [...multiSelectOperators],
},
},
},
radio: {
...BasicConfig.types.select,
widgets: {
...BasicConfig.types.select.widgets,
radio: {
...BasicConfig.types.select.widgets.select,
},
},
},
Comment on lines +54 to +91
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Inspect query-builder field configuration for explicit widget/preferWidgets wiring.
rg -n -C5 --type=ts 'preferWidgets|widget:|widgets:|RoutingFormFieldType\.(ADDRESS|URL|MULTIEMAIL|CHECKBOX|RADIO|BOOLEAN)|type: field\.type|field\.type' packages/app-store/routing-forms

Repository: calcom/cal.diy

Length of output: 44136


🏁 Script executed:

# Find and examine getWidgetsWithoutFactory implementation
rg -n "getWidgetsWithoutFactory" packages/app-store/routing-forms/components/react-awesome-query-builder/config/ -A 20

Repository: calcom/cal.diy

Length of output: 3871


🏁 Script executed:

# Also check if there's a separate step that adds widgets for new types
rg -n "widgets\[" packages/app-store/routing-forms/lib/getQueryBuilderConfig.ts -B 5 -A 5

Repository: calcom/cal.diy

Length of output: 1286


🏁 Script executed:

# Check if widget being undefined is handled anywhere
sed -n '54,65p' packages/app-store/routing-forms/lib/getQueryBuilderConfig.ts

Repository: calcom/cal.diy

Length of output: 450


🏁 Script executed:

# Check if checkbox/radio should be in getWidgetsWithoutFactory but are missing
sed -n '9,35p' packages/app-store/routing-forms/components/react-awesome-query-builder/config/config.ts

Repository: calcom/cal.diy

Length of output: 853


Add checkbox and radio widgets to getWidgetsWithoutFactory.

The checkbox and radio type configurations are defined in getTypes() but their corresponding widget entries are missing from getWidgetsWithoutFactory. This causes getQueryBuilderConfig.ts:57-58 to access undefined when looking up widgets[fieldType] for these types, resulting in a runtime crash: Cannot read property 'type' of undefined.

Add checkbox and radio entries to getWidgetsWithoutFactory:

Widget entries to add
checkbox: {
  ...BasicConfig.widgets.multiselect,
},
radio: {
  ...BasicConfig.widgets.select,
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/app-store/routing-forms/components/react-awesome-query-builder/config/config.ts`
around lines 54 - 91, getWidgetsWithoutFactory is missing entries for the custom
field types defined in getTypes (checkbox and radio), causing widgets[fieldType]
to be undefined in getQueryBuilderConfig; add entries for "checkbox" and "radio"
to getWidgetsWithoutFactory so they point to the corresponding base widgets
(checkbox -> BasicConfig.widgets.multiselect, radio ->
BasicConfig.widgets.select) so lookup returns a valid widget config and avoids
the runtime crash in getQueryBuilderConfig when accessing
widgets[fieldType].type.

multiselect: {
...BasicConfig.types.multiselect,
widgets: {
Expand Down
Loading
Loading