Skip to content

fix: Custom Event Redirect in Routing Form with Dropdown Based fields #17870

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from

Conversation

hariombalhara
Copy link
Member

@hariombalhara hariombalhara commented Nov 27, 2024

What does this PR do?

Note: This PR has #23032 merged as well to avoid doing unnecessary work of fixing the files that were going to be deleted.

Fixes routing forms variable substitution to use selected option labels instead of field labels when substituting variables in custom event redirect URLs.

Loom Demo(Before and After)

Fixes PRI-304

Bug-1

When using variables like {Country} in routing form custom URLs (e.g., /team/{Country}/meeting), the system was incorrectly substituting the selected option's UID instead of the selected option's label (e.g., "India"). This made the feature unusable for dropdown fields.

Root Cause

The substituteVariables function was using response[key].value which contains the selected option's UID, not the selected option's label. For dropdown fields, we need to look up the option label using the option ID stored in response[key].value.

Bug-2

Preview of the Routing Form wasn't substituting the variables

Tech Debt Payment

Service Organization

  • Moved getLabelsFromOptionIds and ensureStringOrStringArray as internal dependency of getHumanReadableFieldResponseValue packages/lib/server/service/routingForm/responseData/
  • These utilities are now reusable across the codebase

Type of change

  • Bug fix (non-breaking change which fixes an issue)

How should this be tested?

  1. Create a routing form with a dropdown field (e.g., Country with options like India, USA, etc.)
  2. Add a route with custom event redirect URL using the variable (e.g., /team/{Country}/30min)
  3. Test the form preview and select a country
  4. Verify the preview shows the substituted URL (e.g., /team/india/30min) instead of {Country}
  5. Verify the redirect uses the option label, not the field label

Testing Coverage

Added comprehensive test coverage:

  • ✅ Unit tests for getHumanReadableFieldResponseValue service (15 tests)
  • ✅ Unit tests for substituteVariables with select, multiselect, text, and number fields (15 tests)
  • ✅ E2E tests for variable substitution in TestFormDialog (2 tests)

Mandatory Tasks

  • Make sure you have self-reviewed the PR. A decent size PR without self-review might be rejected.

Copy link
Contributor

github-actions bot commented Nov 27, 2024

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "Fix custom event redirect". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@keithwillcode keithwillcode added core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO labels Nov 27, 2024
Copy link
Contributor

This PR is being marked as stale due to inactivity.

Copy link
Contributor

This PR is being marked as stale due to inactivity.

@github-actions github-actions bot added the Stale label Dec 28, 2024
@github-actions github-actions bot removed the Stale label Jan 12, 2025
Copy link
Contributor

This PR is being marked as stale due to inactivity.

@alishaz-polymath
Copy link
Member

@hariombalhara is this still relevant?

devin-ai-integration bot and others added 3 commits August 12, 2025 08:34
…lity

- Remove unused packages/app-store/routing-forms/trpc/report.handler.ts
- Remove packages/app-store/routing-forms/jsonLogicToPrisma.ts implementation
- Remove packages/app-store/routing-forms/__tests__/jsonLogicToPrisma.test.ts test file

These files were no longer being used in the routing-forms package.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
- Remove unused report.schema.ts file
- Remove buildResponsesForReporting test file
- Remove report endpoint from router
- Update comment referencing jsonLogicToPrisma

All references to the removed jsonLogicToPrisma and report handler functionality have been cleaned up.

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
@hariombalhara
Copy link
Member Author

Yeah It should be . Retesting it

…f field labels

- Fix custom URL redirects to substitute selected option labels (e.g., 'India') instead of field labels (e.g., 'Country')
- Fix preview to show substituted variables instead of placeholders
- Add getHumanReadableFieldResponseValue service to handle field value resolution
- Move getLabelsFromOptionIds and ensureStringOrStringArray to centralized service location
- Add comprehensive tests for variable substitution with select, text, and number fields
- Fix TestForm to not call team member matching for custom URLs (eventTypeId check)
Copy link

vercel bot commented Aug 12, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal ⬜️ Ignored Aug 13, 2025 0:38am
cal-eu ⬜️ Ignored Aug 13, 2025 0:38am

Copy link

linear bot commented Aug 12, 2025

PRI-304

@hariombalhara hariombalhara changed the title Fix custom event redirect fix: Custom Event Redirect in Routing Form with Dropdown Based fields Aug 12, 2025
Copy link
Contributor

coderabbitai bot commented Aug 12, 2025

Walkthrough

Centralizes response-to-label normalization by adding getHumanReadableFieldResponseValue and using it in substituteVariables and the form responses API. substituteVariables now uses human-readable option labels (slugified) for route placeholders. TestForm UI shows substituted internal event-type redirect URLs (UI-only) and guards team-members matching by eventTypeId. Adds unit tests for substituteVariables, getHumanReadableFieldResponseValue, and TestFormDialog URL-substitution cases. Removes routing-forms reporting utilities, jsonLogic-to-Prisma translator, TRPC report route/schema, report handler, and related reporting tests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Assessment against linked issues

Objective Addressed Explanation
Use option labels for variable substitution in routing form custom URLs instead of option IDs/field labels (PRI-304)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Remove TRPC report procedure (packages/app-store/routing-forms/trpc/_router.ts) Reporting route removal is unrelated to the variable-substitution objective.
Delete report handler (packages/app-store/routing-forms/trpc/report.handler.ts) Full server-side report handler deletion is not required to fix substitution behavior.
Remove reporting utilities (packages/app-store/routing-forms/lib/reportingUtils.ts) Deleting helpers used for reporting is outside PRI-304's stated scope.
Remove jsonLogic-to-Prisma translator (packages/app-store/routing-forms/jsonLogicToPrisma.ts) Removing query translation logic for reporting is unrelated to URL variable substitution.

Possibly related PRs


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54e1239 and 1b5c0de.

📒 Files selected for processing (5)
  • packages/app-store/routing-forms/__tests__/jsonLogicToPrisma.test.ts (0 hunks)
  • packages/app-store/routing-forms/jsonLogicToPrisma.ts (0 hunks)
  • packages/app-store/routing-forms/lib/getQueryBuilderConfig.ts (1 hunks)
  • packages/app-store/routing-forms/trpc/buildResponsesForReporting.test.ts (0 hunks)
  • packages/app-store/routing-forms/trpc/report.schema.ts (0 hunks)
💤 Files with no reviewable changes (4)
  • packages/app-store/routing-forms/trpc/buildResponsesForReporting.test.ts
  • packages/app-store/routing-forms/tests/jsonLogicToPrisma.test.ts
  • packages/app-store/routing-forms/trpc/report.schema.ts
  • packages/app-store/routing-forms/jsonLogicToPrisma.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/app-store/routing-forms/lib/getQueryBuilderConfig.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix-custom-event-redirect-routing-forms

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@hariombalhara hariombalhara force-pushed the fix-custom-event-redirect-routing-forms branch from 6a0834f to f7bbb69 Compare August 13, 2025 10:49
@@ -50,7 +35,10 @@ async function* getResponses(formId: string, fields: Fields) {
fields.forEach((field) => {
const fieldResponse = fieldResponses[field.id];
const value = fieldResponse?.value || "";
const readableValues = getHumanReadableFieldResponseValue({ field, value });
const humanReadableResponseValue = getHumanReadableFieldResponseValue({ field, value });
Copy link
Member Author

@hariombalhara hariombalhara Aug 13, 2025

Choose a reason for hiding this comment

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

It now returns array only if the input value was an array, which is a better way and keeps the type unchanged

@hariombalhara hariombalhara force-pushed the fix-custom-event-redirect-routing-forms branch 2 times, most recently from 9561f88 to 5ca07ce Compare August 13, 2025 12:09
Comment on lines -135 to -141
eventTypeRedirectUrl = getAbsoluteEventTypeRedirectUrl({
eventTypeRedirectUrl: route.action.value,
form,
allURLSearchParams: new URLSearchParams(),
});
setEventTypeUrlWithoutParams(eventTypeRedirectUrl);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

It was unused as eventTypeUrlWithoutParams is also unused

@hariombalhara hariombalhara force-pushed the fix-custom-event-redirect-routing-forms branch from 5ca07ce to d022fb1 Compare August 13, 2025 12:11
@hariombalhara hariombalhara marked this pull request as ready for review August 13, 2025 12:16
@hariombalhara
Copy link
Member Author

@alishaz-polymath Opened it for review

@graphite-app graphite-app bot requested a review from a team August 13, 2025 12:16
@dosubot dosubot bot added routing-forms area: routing forms, routing, forms 🐛 bug Something isn't working labels Aug 13, 2025
Copy link

graphite-app bot commented Aug 13, 2025

Graphite Automations

"Add consumer team as reviewer" took an action on this PR • (08/13/25)

1 reviewer was added to this PR based on Keith Williams's automation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (1)
packages/app-store/routing-forms/api/responses/[formId].ts (1)

21-29: Restrict Prisma selection to only required fields to reduce payload.

Per repo guidelines, select only what you need. For the CSV loop below, only response and createdAt are used from FormResponse rows.

Apply this diff:

   while (
     (responses = await prisma.app_RoutingForms_FormResponse.findMany({
       where: {
         formId,
       },
       take: take,
       skip: skip,
+      select: {
+        response: true,
+        createdAt: true,
+      },
     })) &&
🧹 Nitpick comments (7)
packages/app-store/routing-forms/lib/substituteVariables.ts (1)

26-41: Precompute maps to avoid quadratic scans and repeated work.

The nested loop (variables × response × fields.find) is O(V × R × F) and repeatedly computes identifiers/human-readable values. Pre-index fields by id and precompute identifier→value once from response, then substitute. This improves readability and performance.

Apply this refactor:

   variables.forEach((variable) => {
-    for (const key in response) {
-      const field = fields.find((field) => field.id === key);
-      if (!field) {
-        continue;
-      }
-      const identifier = getFieldIdentifier(field);
-      if (identifier.toLowerCase() === variable.toLowerCase()) {
-        const humanReadableValues = getHumanReadableFieldResponseValue({
-          field,
-          value: response[key].value,
-        });
-        // ['abc', 'def'] ----toString---> 'abc,def' ----slugify---> 'abc-def'
-        const valueToSubstitute = slugify(humanReadableValues.toString());
-        eventTypeUrl = eventTypeUrl.replace(`{${variable}}`, valueToSubstitute);
-      }
-    }
+    // Build maps once outside the variable loop for efficiency
+    // Note: hoist these outside variables.forEach if many variables are expected
+    const idToField = new Map(fields.map((f) => [f.id, f]));
+    const identifierToValue: Record<string, string> = {};
+    for (const key in response) {
+      const field = idToField.get(key);
+      if (!field) continue;
+      const identifier = getFieldIdentifier(field).toLowerCase();
+      const humanReadableValues = getHumanReadableFieldResponseValue({
+        field,
+        value: response[key].value,
+      });
+      identifierToValue[identifier] = slugify(humanReadableValues.toString());
+    }
+
+    const valueToSubstitute = identifierToValue[variable.toLowerCase()];
+    if (valueToSubstitute !== undefined) {
+      eventTypeUrl = eventTypeUrl.replaceAll(`{${variable}}`, valueToSubstitute);
+    }
   });
packages/app-store/routing-forms/components/_components/TestForm.tsx (1)

149-151: Be explicit about eventTypeId gate to avoid truthiness pitfalls.

The intent is to skip the mutation for custom event type (eventTypeId = 0) and only run when a valid, positive ID exists. Make the check explicit for readability.

-    if (supportsTeamMembersMatchingLogic && route.action.eventTypeId) {
+    if (
+      supportsTeamMembersMatchingLogic &&
+      typeof route.action.eventTypeId === "number" &&
+      route.action.eventTypeId > 0
+    ) {
packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts (4)

11-18: Prefer Array.isArray and clarify the scalar-number behavior

Use Array.isArray for array detection. Also, the JSDoc implies numbers become strings, but here numbers become a single-element array of strings. Clarify that to avoid confusion for downstream consumers.

Apply this diff:

 function ensureStringOrStringArray(value: string | number | (string | number)[]): string | string[] {
   if (typeof value === "string") {
     return value;
-  } else if (value instanceof Array) {
+  } else if (Array.isArray(value)) {
     return value.map((v) => v.toString());
   }
   return [value.toString()];
 }

And update the JSDoc above to reflect the array-wrapping for non-string scalars:

 /**
- * Ensures a value is either a string or string array.
- * Converts numbers to strings.
+ * Normalizes a response value to a string or string[].
+ * Numbers are converted to strings; non-string scalars are wrapped in a single-element array
+ * to preserve downstream join/CSV semantics.
  *
  * @param value - The value to normalize
- * @returns String or string array
+ * @returns string | string[]
  */

29-36: Annotate return type and use Array.isArray

Make the return type explicit (string[]) and use Array.isArray for consistency and cross-realm safety.

Apply this diff:

 function getLabelsFromOptionIds({
   options,
   optionIds,
 }: {
   options: NonNullable<Field["options"]>;
   optionIds: string | string[];
-}) {
-  if (optionIds instanceof Array) {
+}): string[] {
+  if (Array.isArray(optionIds)) {
     const labels = optionIds.map((optionId) => {
       const foundOption = options.find((option) => option.id === optionId);
       // It would mean that the optionId is actually a label which is why it isn't matching any option id.
       // Fallback to optionId(i.e. label) which was the case with legacy options
       if (!foundOption) {
         return optionId;

Optional note: If options can be large and this runs in hot paths, consider precomputing a Map from id to label to reduce repeated linear scans.


56-64: JSDoc return type mismatch

The comment says “Array of human-readable values” but the function can return string or string[]. Update the doc to avoid confusion.

Apply this diff:

 /**
  * Get the human-readable value for a field response.
  * For select/multiselect/radio fields, this returns the label of the selected option(s).
- * For other fields, it returns the value as-is.
+ * For other fields, it returns the normalized value (string or string[]).
  *
  * @param field - The field definition with options
  * @param value - The response value (typically an option ID for select fields)
- * @returns Array of human-readable values
+ * @returns string | string[]
  */

72-79: Consider normalizing to a single return shape (string[])

Returning a union (string | string[]) forces callers to normalize again. Given the downstream usage relies on array join semantics (toString), you could simplify by always returning string[] (e.g., wrap single strings into an array). This would reduce branching in consumers.

Happy to provide a small refactor if you want to adopt a consistent string[] return shape.

packages/app-store/routing-forms/lib/substituteVariables.test.ts (1)

219-229: Add tests for radio fields and legacy label fallbacks

Two high-signal additions:

  • Radio-type field behaves like select.
  • Legacy responses where value holds a label instead of an ID should still substitute correctly (you already support this in the utility).

Apply this diff to extend coverage:

   it("should handle textarea fields like text fields", () => {
     const fields = [
       { id: "field1", identifier: "description", label: "Description", type: "textarea" as const },
     ];
     const routeValue = "/ticket/{description}";
     const response = createFormResponse("field1", "Bug Report Summary", "Description");

     const result = substituteVariables(routeValue, response, fields);
     expect(result).toBe("/ticket/bug-report-summary");
   });
+
+  it("should substitute radio field option labels", () => {
+    const fields: Field[] = [
+      {
+        id: "fieldRadio",
+        identifier: "choice",
+        label: "Choice",
+        type: "radio" as const,
+        options: [
+          { id: "opt-1", label: "First Option" },
+          { id: "opt-2", label: "Second Option" },
+        ],
+      },
+    ];
+    const routeValue = "/pick/{choice}";
+    const response = createFormResponse("fieldRadio", "opt-1", "Choice");
+    const result = substituteVariables(routeValue, response, fields);
+    expect(result).toBe("/pick/first-option");
+  });
+
+  it("should fallback to labels when option IDs are not found (legacy)", () => {
+    const fields = [
+      createSelectField("field1", "country", "Country", [{ id: "ind-001", label: "India" }]),
+    ];
+    const routeValue = "/team/{country}/meeting";
+    // Simulate legacy value already being the label instead of the ID
+    const response = createFormResponse("field1", "India", "Country");
+    const result = substituteVariables(routeValue, response, fields);
+    expect(result).toBe("/team/india/meeting");
+  });
 });
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7fda31 and d022fb1.

📒 Files selected for processing (10)
  • packages/app-store/routing-forms/__tests__/TestFormDialog.test.tsx (2 hunks)
  • packages/app-store/routing-forms/api/responses/[formId].ts (2 hunks)
  • packages/app-store/routing-forms/components/_components/TestForm.tsx (2 hunks)
  • packages/app-store/routing-forms/lib/reportingUtils.ts (0 hunks)
  • packages/app-store/routing-forms/lib/substituteVariables.test.ts (1 hunks)
  • packages/app-store/routing-forms/lib/substituteVariables.ts (2 hunks)
  • packages/app-store/routing-forms/trpc/_router.ts (0 hunks)
  • packages/app-store/routing-forms/trpc/report.handler.ts (0 hunks)
  • packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.test.ts (1 hunks)
  • packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts (1 hunks)
💤 Files with no reviewable changes (3)
  • packages/app-store/routing-forms/trpc/_router.ts
  • packages/app-store/routing-forms/lib/reportingUtils.ts
  • packages/app-store/routing-forms/trpc/report.handler.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.test.ts
  • packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts
  • packages/app-store/routing-forms/lib/substituteVariables.test.ts
  • packages/app-store/routing-forms/lib/substituteVariables.ts
  • packages/app-store/routing-forms/api/responses/[formId].ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.test.ts
  • packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts
  • packages/app-store/routing-forms/lib/substituteVariables.test.ts
  • packages/app-store/routing-forms/lib/substituteVariables.ts
  • packages/app-store/routing-forms/__tests__/TestFormDialog.test.tsx
  • packages/app-store/routing-forms/api/responses/[formId].ts
  • packages/app-store/routing-forms/components/_components/TestForm.tsx
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • packages/app-store/routing-forms/__tests__/TestFormDialog.test.tsx
  • packages/app-store/routing-forms/components/_components/TestForm.tsx
🧠 Learnings (1)
📚 Learning: 2025-07-15T12:59:34.389Z
Learnt from: eunjae-lee
PR: calcom/cal.com#22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

Applied to files:

  • packages/app-store/routing-forms/components/_components/TestForm.tsx
🧬 Code Graph Analysis (7)
packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.test.ts (2)
packages/app-store/routing-forms/types/types.d.ts (2)
  • Field (30-30)
  • FormResponse (19-27)
packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts (1)
  • getHumanReadableFieldResponseValue (65-79)
packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts (1)
packages/app-store/routing-forms/types/types.d.ts (2)
  • Field (30-30)
  • FormResponse (19-27)
packages/app-store/routing-forms/lib/substituteVariables.test.ts (2)
packages/app-store/routing-forms/types/types.d.ts (2)
  • Field (30-30)
  • FormResponse (19-27)
packages/app-store/routing-forms/lib/substituteVariables.ts (1)
  • substituteVariables (16-46)
packages/app-store/routing-forms/lib/substituteVariables.ts (2)
packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts (1)
  • getHumanReadableFieldResponseValue (65-79)
packages/platform/libraries/index.ts (1)
  • slugify (25-25)
packages/app-store/routing-forms/__tests__/TestFormDialog.test.tsx (1)
packages/app-store/routing-forms/components/_components/TestForm.tsx (1)
  • TestFormRenderer (320-374)
packages/app-store/routing-forms/api/responses/[formId].ts (1)
packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts (1)
  • getHumanReadableFieldResponseValue (65-79)
packages/app-store/routing-forms/components/_components/TestForm.tsx (1)
packages/app-store/routing-forms/lib/substituteVariables.ts (1)
  • substituteVariables (16-46)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Tests / Unit
  • GitHub Check: Type check / check-types
🔇 Additional comments (11)
packages/app-store/routing-forms/lib/substituteVariables.ts (1)

34-41: Correct fix: substitute human-readable labels (not option UIDs).

This uses getHumanReadableFieldResponseValue to resolve option labels and slugifies them before substitution. This directly addresses the root cause (UIDs vs labels) and aligns with the PR objective. Good job.

packages/app-store/routing-forms/__tests__/TestFormDialog.test.tsx (3)

54-55: Good guard input for team-members matching logic.

Setting eventTypeId in the mock ensures the mutation path is exercised only when appropriate. This matches the new guard in TestForm.


426-454: Solid coverage: internal event redirect variables substituted in UI.

This validates the UI-only substitution path for eventTypeRedirectUrl, including slugification (“Sales Team” → “sales-team”). Looks correct.


455-481: Clear differentiation: external redirect URLs are left untouched.

This test ensures externalRedirectUrl values aren’t substituted in the UI, which aligns with the intended behavior.

packages/app-store/routing-forms/api/responses/[formId].ts (2)

7-7: Centralized human-readable conversion is correctly adopted.

Importing and using getHumanReadableFieldResponseValue here removes duplication and ensures consistent behavior across the app.


38-41: Correct normalization to array for CSV serialization.

Wrapping non-array results and passing arrays through preserves original shape while simplifying downstream joining. Good.

packages/app-store/routing-forms/components/_components/TestForm.tsx (2)

19-21: Right import: pulls in shared substitution logic.

Importing substituteVariables keeps UI behavior consistent with core logic and avoids reimplementations.


131-145: UI-only substitution is implemented cleanly.

Creating displayRoute with a substituted value for eventTypeRedirectUrl (and leaving the original route untouched for backend logic) avoids side effects and clarifies intent. Nice.

packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.test.ts (1)

1-205: Comprehensive and precise test coverage.

Great set of cases across select, multiselect, radio, text/number/textarea, and edge conditions (numeric values, empty options, undefined options). This gives high confidence in the normalization logic.

packages/lib/server/service/routingForm/responseData/getHumanReadableFieldResponseValue.ts (1)

65-79: Solid centralization; logic aligns with the bug fix

The approach to resolve option IDs to human-readable labels and reuse it across callers is correct and neatly scoped. Type-only imports avoid runtime coupling.

packages/app-store/routing-forms/lib/substituteVariables.test.ts (1)

1-229: Excellent, comprehensive coverage

The tests thoroughly validate substitution for select, multiselect, text, number, special characters, case-insensitivity, and querystring preservation. They capture the intended label-based behavior and the slugification nuances well.

@hariombalhara hariombalhara force-pushed the fix-custom-event-redirect-routing-forms branch from d022fb1 to 54e1239 Compare August 13, 2025 12:24
…er-1754987582' into fix-custom-event-redirect-routing-forms
Copy link
Contributor

E2E results are ready!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
🐛 bug Something isn't working core area: core, team members only enterprise area: enterprise, audit log, organisation, SAML, SSO ready-for-e2e routing-forms area: routing forms, routing, forms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants