Skip to content

feat: Prerender booking link and reuse with headless router #20720

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 36 commits into
base: main
Choose a base branch
from

Conversation

hariombalhara
Copy link
Member

@hariombalhara hariombalhara commented Apr 16, 2025

What does this PR do?

Fixes #20807

Video Demo (if applicable):

https://www.loom.com/share/448e7ee236684cf1842c0b9a914e1e99

PR Changes Summary

📝 Documentation:
+338 -1 (339 total)
🧪 Tests:
+1305 -14 (1319 total)
💻 Code:
+1278 -195 (1473 total)

Added router preloading to improve embed performance and reduce loading times. This feature preloads booking pages while routing form responses are being processed, creating a much faster user experience.

image
^Screenshot is from README

How to use

Just fire prerender instruction when you think is a good time. e.g. when user starts typing in the form(example in routing-playground.html)

Technical Details

  • Created a new API endpoint at /api/router for headless router.
    • Not using API v2 one because that requires API Key and this one is public. This is similar to /router except that fact that it accepts POST data and returns JSON vs an HTML page which is /router
    • This new endpoint is only used when prerender instruction is used. This will change soon and will always be used later.
  • Heavily improved "connect" flow to pass on existing data to the pre-rendered iframe and decide when to do a full page reload and when to do "connect" and when to just re-open the modal.
  • Enhanced booker state tracking with more granular states: initializing, slotsPending, slotsLoading, slotsLoaded.
  • Replaced direct Next.js router push with declarative URL parameter management that ensures better and consistent results - Fixes scenario where react what silently ignoring the router.push because a render was already in progress.
  • Updated embed lifecycle to better handle prerendering and loading states.
  • Updated/Fixed insights seed to be able to use the Routing Form in the playground
  • Update Playground to have dedicated page to test router prerendering at /routing-playground.html

Changes in App

  • getSchedule or getTeamSchedule requests would have an additional parameter embedConnectVersion(mapped from 'cal.embedConnectVersion' query param of the page)
    • The param is set only in embed mode and only when the iframe is reused. This is to ensure that we can force refresh of availablity(e.g. after a stale time configured in embed)
  • getSchedule or getTeamSchedule requests will not be sent if cal.skipSlotsFetch=true param is set on the page.

Documentation Improvements

  • Heavily updated README.md and created LIFECYCLE.md to have a superficial diagram of the entire flow. It was a pain to revisit various lifecycle events without proper documentation and took the time to create it.
    image
    See LIFECYCLE.md for full view

Tests

  • Added Modal transition Unit tests as most of the changes are through that flow.

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  1. First run from packages/prisma, yarn seed-insights. So that appropriate state of the DB is there to be able to test the scenario that is configured in routing-playground
  2. Open the routing-playground.html page for testing as follows
    • Go to packages/embed-core and run yarn dev. It will open up embed playground which you could use to test other cases e.g. for regression testing
    • Open the page http://localhost:3100/embed/routing-playground.html after that to be able to test this newly added feature
  3. The playground describes the usecase that is being tested there. You could tweak the code in playground.js and routing-playground.html to test other scenarios like what happens when you don't preload.
    image
  • You could test with different network speed(throttled), like what happens if user opens the modal before pre-rendering is complete
  • What happens if the user closes the modal and re-open

Followup

  • A 404 page like http://acme.cal.remote:3000/team1/team1-dvent-1 doesn't correctly set CalcomPageStatus to 404. Infact it serems that notFoundClient.tsx is not being rendered. Thus, iframe isn't able to correctly fire linkFailed event and thus, the modal box(on clicked again) doesn't retry on 404 error.

hariombalhara and others added 12 commits April 10, 2025 09:16
## What does this PR do?

<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->

- Fixes #XXXX (GitHub issue number)
- Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)

## Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change **(video / image - any one)**.

#### Video Demo (if applicable):

- Show screen recordings of the issue or feature.
- Demonstrate how to reproduce the issue, the behavior before and after the change.

#### Image Demo (if applicable):

- Add side-by-side screenshots of the original and updated change.
- Highlight any significant change(s).

## Mandatory Tasks (DO NOT REMOVE)

- [ ] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [ ] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). If N/A, write N/A here and check the checkbox.
- [ ] I confirm automated tests are in place that prove my fix is effective or that my feature works.

## How should this be tested?

<!-- Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration. Write details that help to start the tests -->

- Are there environment variables that should be set?
- What are the minimal test data to have?
- What is expected (happy path) to have (input and output)?
- Any other important info that could help to test that PR

## Checklist

<!-- Remove bullet points below that don't apply to you -->

- I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- My code doesn't follow the style guidelines of this project
- I haven't commented my code, particularly in hard-to-understand areas
- I haven't checked if my changes generate no new warnings
Copy link

vercel bot commented Apr 16, 2025

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

2 Skipped Deployments
Name Status Preview Comments Updated (UTC)
cal ⬜️ Ignored (Inspect) Visit Preview May 1, 2025 9:36am
calcom-web-canary ⬜️ Ignored (Inspect) Visit Preview May 1, 2025 9:36am

Copy link
Contributor

github-actions bot commented Apr 16, 2025

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 "Router-preloading". 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 Apr 16, 2025
Base automatically changed from embed-loader to main April 16, 2025 13:40
@github-actions github-actions bot added the embed area: embed, widget, react embed label Apr 22, 2025
@@ -340,16 +416,37 @@ export const useEmbedType = () => {
return state;
};

function unhideBody() {
function makeBodyVisible() {
Copy link
Member Author

Choose a reason for hiding this comment

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

Better named

@@ -36,13 +42,13 @@ customElements.define("cal-inline", Inline);

declare module "*.css";
type Namespace = string;
type InitConfig = {
Copy link
Member Author

Choose a reason for hiding this comment

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

Renamed InitConfig -> CalConfig as "init" is just how it is set. This was ambiguous with initialConfig

@@ -280,7 +290,23 @@ export class Cal {
iframe.className = "cal-embed";
iframe.name = `cal-embed=${this.namespace}`;
iframe.title = `Book a call`;
const embedConfig = this.getInitConfig();

this.loadInIframe({ calLink, config, calOrigin, iframe });
Copy link
Member Author

Choose a reason for hiding this comment

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

Abstracted it out from createIframe as it now used separately as well.

@hariombalhara hariombalhara changed the title feat: Prerender booking link and reuse with headlesss router feat: Prerender booking link and reuse with headless router May 1, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
consumer core area: core, team members only embed area: embed, widget, react embed enterprise area: enterprise, audit log, organisation, SAML, SSO ✨ feature New feature or request ready-for-e2e
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Embed: Headless Router -> Pre-render iframe with the expected event booking page redirect
4 participants