Files
calendar/packages/embeds/embed-core/routing-playground.html
T
6e9ec113fe feat: Prerender booking link and reuse with headless router (#20720)
* mvp done

* wip

* fix ts errors and other code improvements

* fix ts errors

* ensure mobile layout support

* Make skeleton responsive on screen resize

* refactor

* Add test for EmbedElement

* make skeleton closer to pixel perfect

* Address PR feedback

* Router-preloading
## 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

* wip\

* wip

* fix mrge.io feedback

* wip

* Add README and lifecycle

* Add README and lifecycle

* Update routing form-seed and some other fixes

* remove linkFailed fix from the branch

* self-review

* self-review-2

* self-review-3

* Handle soft connect\

* Update README and fix a bug with query parmas

* Add one more case in routing-html playground

---------

Co-authored-by: Benny Joo <sldisek783@gmail.com>
Co-authored-by: amrit <iamamrit27@gmail.com>
2025-05-08 19:59:26 +01:00

176 lines
7.4 KiB
HTML

<html>
<head>
<title>Embed Playground - Routing Form</title>
<!-- <link rel="prerender" href="http://localhost:3000/free"> -->
<!-- <script src="./src/embed.ts" type="module"></script> -->
<script>
const url = new URL(document.URL);
window.only = url.searchParams.get("only")
window.calOrigin = url.searchParams.get("calOrigin") || "http://localhost:3000";
window.calLink = url.searchParams.get("calLink");
if (!location.search.includes("nonResponsive")) {
document.write('<meta name="viewport" content="width=device-width"/>');
}
(function addParamsAutomatically() {
const newSearchParams = new URLSearchParams(location.search);
let paramsChanged = false;
if (!newSearchParams.has("only")) {
newSearchParams.set("only", "ns:routingFormPrerender");
paramsChanged = true;
}
if (!newSearchParams.has("calOrigin")) {
newSearchParams.set("calOrigin", "http://acme.cal.local:3000");
paramsChanged = true;
}
if (!newSearchParams.has("cal.embed.logging")) {
newSearchParams.set("cal.embed.logging", "1");
paramsChanged = true;
}
if (paramsChanged) {
location.href = "?" + newSearchParams.toString();
}
})();
</script>
<script>
function embedSnippet() {
(function (C, A, L) {
let p = function (a, ar) {
a.q.push(ar);
};
let d = C.document;
C.Cal =
C.Cal ||
function () {
let cal = C.Cal;
let ar = arguments;
if (!cal.loaded) {
cal.ns = {};
cal.q = cal.q || [];
d.head.appendChild(d.createElement("script")).src = A;
cal.loaded = true;
}
if (ar[0] === L) {
const api = function () {
p(api, arguments);
};
const namespace = ar[1];
api.q = api.q || [];
if (typeof namespace === "string") {
// Make sure that even after re-execution of the snippet, the namespace is not overridden
cal.ns[namespace] = cal.ns[namespace] || api;
p(cal.ns[namespace], ar);
p(cal, ['initNamespace', namespace])
} else p(cal, ar);
return;
}
p(cal, ar);
};
})(window, window.calOrigin + "/embed/embed.js", "init");
}
embedSnippet();
</script>
<style>
body {
background: linear-gradient(90deg,
rgba(120, 116, 186, 1) 0%,
rgba(221, 221, 255, 1) 41%,
rgba(148, 232, 249, 1) 100%);
}
.inline-embed-container {
/* border: 1px solid black; */
margin-bottom: 5px;
border-bottom: 1px solid;
}
.loader {
color: green;
}
* {
--cal-brand-color: gray;
}
</style>
</head>
<body>
<div style="display: flex; flex-direction: column; gap: 10px;">
<div id="cal-booking-place-routingFormPrerender">
<a href="?only=ns:routingFormPrerender">Routing Form Prerender Demo</a>
<p>1. As soon as you start typing in the form, the form will prerender the booking page(where redirect is supposed to happen)</p>
<p>2. Clicking on submit will open the prerendered/being prerendered iframe in the modal</p>
<p>3. Changing the form response and clicking submit, will re-submit the response and reopen the modal with the updated data(update slots call, updated prefill fields)</p>
<p>4. Clicking submit again without changing the form response will just re-show the hidden modal(as long as threshold of 1 min since last time the modal was shown is met). But, in case of an error it iframe always tries to rerender. Trying adding an invalid email to see the error</p>
<form id="cal-booking-place-routingFormPrerender-form">
<input type="text" name="name" placeholder="John Doe" />
<input type="email" name="email" placeholder="john@example.com" />
<select name="skills" placeholder="JavaScript, Node.js">
<option value="JavaScript">JavaScript</option>
<option value="Sales">Sales</option>
</select>
</form>
<button id="cal-booking-place-routingFormPrerender-submit" data-cal-namespace="routingFormPrerender" data-cal-config='{"cal.embed.pageType":"team.event.booking.slots", "guests":["guest1@example.com", "guest2@example.com"]}'>Submit</button>
<script>
document.getElementById("cal-booking-place-routingFormPrerender-form").addEventListener("input", function prerender(e) {
if (prerender.initiated) return;
prerender.initiated = true;
console.log("Playground: prerender initiated");
Cal.ns.routingFormPrerender("prerender", {
calLink: "team/insights-team/team-javascript",
type: "modal",
pageType: "user.event.booking.slots",
});
});
requestAnimationFrame(function updateSubmitButtonLink() {
const seededFormAcmeId = "948ae412-d995-4865-885a-48302588de03";
const form = document.getElementById("cal-booking-place-routingFormPrerender-form");
const name = form.querySelector("input[name='name']").value;
const email = form.querySelector("input[name='email']").value;
const skills = form.querySelector("select[name='skills']").value;
if (name && email) {
document.getElementById("cal-booking-place-routingFormPrerender-submit").setAttribute("data-cal-link", `router?form=${seededFormAcmeId}&email=${email}&name=${name}&Location=London&Department=Engineering&Manager=John Doe&Rating=5&skills=${skills}&Email=${email}`);
}
requestAnimationFrame(updateSubmitButtonLink);
});
</script>
</div>
<div id="cal-booking-place-routingFormWithoutPrerender">
<a href="?only=ns:routingFormWithoutPrerender">Routing Form Without Prerender Demo</a>
<form id="cal-booking-place-routingFormWithoutPrerender-form">
<input type="text" name="name" placeholder="John Doe" />
<input type="email" name="email" placeholder="john@example.com" />
<select name="skills" placeholder="JavaScript, Node.js">
<option value="JavaScript">JavaScript</option>
<option value="Sales">Sales</option>
</select>
</form>
<button id="cal-booking-place-routingFormWithoutPrerender-submit" data-cal-namespace="routingFormWithoutPrerender" data-cal-config='{"cal.embed.pageType":"team.event.booking.slots", "guests":["guest1@example.com", "guest2@example.com"]}'>Submit</button>
<script>
requestAnimationFrame(function updateSubmitButtonLink() {
const seededFormAcmeId = "948ae412-d995-4865-885a-48302588de03";
const form = document.getElementById("cal-booking-place-routingFormWithoutPrerender-form");
const name = form.querySelector("input[name='name']").value;
const email = form.querySelector("input[name='email']").value;
const skills = form.querySelector("select[name='skills']").value;
if (name && email) {
document.getElementById("cal-booking-place-routingFormWithoutPrerender-submit").setAttribute("data-cal-link", `router?form=${seededFormAcmeId}&email=${email}&name=${name}&Location=London&Department=Engineering&Manager=John Doe&Rating=5&skills=${skills}&Email=${email}`);
}
requestAnimationFrame(updateSubmitButtonLink);
});
</script>
</div>
</div>
<script type="module" src="./playground.ts"></script>
</script>
</body>
</html>