SIGN IN SIGN UP
calcom / cal.com UNCLAIMED

Scheduling infrastructure for absolutely everyone.

40736 0 0 TypeScript
import { faker } from "@faker-js/faker";
2026-01-02 09:47:00 -03:00
import { randomUUID } from "node:crypto";
import { uuid } from "short-uuid";
import type z from "zod";
import dayjs from "@calcom/dayjs";
import { hashPassword } from "@calcom/lib/auth/hashPassword";
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
import prisma from "@calcom/prisma";
import type { Prisma, UserPermissionRole } from "@calcom/prisma/client";
import { MembershipRole } from "@calcom/prisma/enums";
import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
export async function createUserAndEventType({
user,
eventTypes = [],
credentials,
}: {
user: {
email: string;
password: string;
username: string;
name: string;
completedOnboarding?: boolean;
timeZone?: string;
role?: UserPermissionRole;
theme?: "dark" | "light";
avatarUrl?: string | null;
};
eventTypes?: Array<
Prisma.EventTypeUncheckedCreateInput & {
_bookings?: Prisma.BookingCreateInput[];
_numBookings?: number;
}
>;
credentials?: ({
type: string;
key: Prisma.JsonObject;
appId: string;
} | null)[];
}) {
const { password: _password, ...restOfUser } = user;
const userData = {
2024-02-26 20:01:25 +05:30
...restOfUser,
emailVerified: new Date(),
completedOnboarding: user.completedOnboarding ?? true,
locale: "en",
schedules:
user.completedOnboarding ?? true
? {
create: {
name: "Working Hours",
availability: {
createMany: {
data: getAvailabilityFromSchedule(DEFAULT_SCHEDULE),
},
},
},
}
: undefined,
};
const theUser = await prisma.user.upsert({
where: { email_username: { email: user.email, username: user.username } },
update: userData,
create: userData,
});
2024-02-26 20:01:25 +05:30
await prisma.userPassword.upsert({
where: { userId: theUser.id },
update: {
hash: await hashPassword(user.password),
},
create: {
hash: await hashPassword(user.password),
user: {
connect: {
id: theUser.id,
},
},
},
});
console.log(
`👤 Upserted '${user.username}' with email "${user.email}" & password "${user.password}". Booking page 👉 ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}`
);
for (const eventTypeInput of eventTypes) {
const { _bookings, _numBookings, ...eventTypeData } = eventTypeInput;
let bookingFields;
if (_bookings && _numBookings) {
throw new Error("You can't set both _bookings and _numBookings");
} else if (_numBookings) {
bookingFields = [...Array(_numBookings).keys()].map((i) => ({
startTime: dayjs()
.add(1, "day")
.add(i * 5 + 0, "minutes")
.toDate(),
endTime: dayjs()
.add(1, "day")
.add(i * 5 + 30, "minutes")
.toDate(),
title: `${eventTypeInput.title}:${i + 1}`,
uid: uuid(),
}));
} else {
bookingFields = _bookings || [];
}
eventTypeData.userId = theUser.id;
eventTypeData.users = { connect: { id: theUser.id } };
const eventType = await prisma.eventType.findFirst({
where: {
slug: eventTypeData.slug,
users: {
some: {
id: eventTypeData.userId,
},
},
},
select: {
id: true,
},
});
if (eventType) {
console.log(
`\t📆 Event type ${eventTypeData.slug} already seems seeded - ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}/${eventTypeData.slug}`
);
continue;
}
const { id } = await prisma.eventType.create({
data: eventTypeData,
});
console.log(
`\t📆 Event type ${eventTypeData.slug} with id ${id}, length ${eventTypeData.length}min - ${process.env.NEXT_PUBLIC_WEBAPP_URL}/${user.username}/${eventTypeData.slug}`
);
for (const bookingInput of bookingFields) {
await prisma.booking.create({
data: {
...bookingInput,
user: {
connect: {
email: user.email,
},
},
attendees: {
create: {
email: user.email,
name: user.name,
timeZone: "Europe/London",
},
},
eventType: {
connect: {
id,
},
},
status: bookingInput.status,
iCalUID: "",
},
});
console.log(
`\t\t☎ Created booking ${bookingInput.title} at ${new Date(
bookingInput.startTime
).toLocaleDateString()}`
);
}
}
console.log("👤 User with it's event-types and bookings created", theUser.email);
if (credentials) {
for (const credential of credentials) {
if (credential) {
await prisma.credential.create({
data: {
...credential,
userId: theUser.id,
},
});
console.log(`🔑 ${credential.type} credentials created for ${theUser.email}`);
}
}
}
return theUser;
}
feat: OAuth 2.0 support for atoms (#27158) * fix: useOAuthClient support OAuth 2.0 * fix: cannot read properties of undefined (reading NEXT_PUBLIC_IS_E2E) * fix: allow OAuth 2.0 token to connect gcal or ms calendar * fix: allow OAuth 2.0 token to save gcal or ms calendar credentials * refactor: dont set oauth id header for OAuth 2.0 * fix: calendar events not showing and emails not sent * feat: CalOAuth2Provider * chore: make OAuth 2.0 work in examples app * chore: refresh OAuth 2.0 tokens * docs: running examples app with oauth 2.0 * fix: remove sensitive console.log statements that leak secrets Remove logging of: - OAuth authorization codes (oauth2-user.ts) - Token-bearing exchange responses (oauth2-user.ts) - /me response data containing PII (oauth2-user.ts) - OAuth2 refresh response with tokens (refresh.ts) - Response payload with access tokens (_app.tsx) Addresses Cubic AI review feedback for issues with confidence >= 9/10 Co-Authored-By: unknown <> * docs: update readme * fix: implemente cubic feedback * fix: seed script import * fix: seed script pkce * fix: correct typos and SQLite capitalization in OAuth2 README (#27176) Co-authored-by: cubic-dev-ai[bot] <1082092+cubic-dev-ai[bot]@users.noreply.github.com> * refactor: dont return name in public oauth endpoint * docs: CalOAuthProvider * chore: add NEXT_PUBLIC_IS_E2E constant to test * docs: fix duplicated 'or' in Cal OAuth Provider documentation (#27177) Co-authored-by: cubic-dev-ai[bot] <1082092+cubic-dev-ai[bot]@users.noreply.github.com> * revert: is e2e constant * fix: typecheck * refactor: example app users select * update readme * chore: update oauth atoms readme * refactor: enable booking managed event types with user.username instead of profile.username * fix: EventTypeSettings when viewing round robin * test: add e2e tests for atoms-oauth2 controller Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com> * fix: correct error message path in atoms-oauth2 e2e test Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: cubic-dev-ai[bot] <1082092+cubic-dev-ai[bot]@users.noreply.github.com> Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
2026-02-04 12:54:15 +01:00
type OAuthClientInput = {
clientId: string;
clientSecret: string;
name: string;
purpose: string;
redirectUri: string;
websiteUrl: string;
enablePkce: boolean;
};
export async function createOAuthClientForUser(userId: number, oAuthClient: OAuthClientInput) {
const {enablePkce, ...restOfOAuthClient} = oAuthClient;
await prisma.oAuthClient.create({
data: {
userId,
...restOfOAuthClient,
clientType: enablePkce ? "PUBLIC" : "CONFIDENTIAL",
},
});
console.log(`\t👤 Created OAuth2 client '${oAuthClient.name}' for user with id '${userId}'`);
}
export async function createTeamAndAddUsers(
teamInput: Prisma.TeamCreateInput,
users: { id: number; username: string; role?: MembershipRole }[] = []
) {
const checkUnpublishedTeam = async (slug: string) => {
return await prisma.team.findFirst({
where: {
metadata: {
path: ["requestedSlug"],
equals: slug,
},
},
});
};
const createTeam = async (team: Prisma.TeamCreateInput) => {
try {
const requestedSlug = (team.metadata as z.infer<typeof teamMetadataSchema>)?.requestedSlug;
if (requestedSlug) {
const unpublishedTeam = await checkUnpublishedTeam(requestedSlug);
if (unpublishedTeam) {
throw Error("Unique constraint failed on the fields");
}
}
return await prisma.team.create({
data: {
...team,
},
include: {
eventTypes: true,
},
});
} catch (_err) {
if (_err instanceof Error && _err.message.indexOf("Unique constraint failed on the fields") !== -1) {
console.log(`Team '${team.name}' already exists, skipping.`);
return;
}
throw _err;
}
};
const team = await createTeam(teamInput);
if (!team) {
return;
}
console.log(
`🏢 Created team '${teamInput.name}' - ${process.env.NEXT_PUBLIC_WEBAPP_URL}/team/${team.slug}`
);
for (const eventType of team.eventTypes) {
await prisma.eventType.update({
where: {
id: eventType.id,
},
data: {
users: {
connect: users.map((user) => ({ id: user.id })),
},
},
});
}
for (const user of users) {
const { role = MembershipRole.OWNER, id, username } = user;
await prisma.membership.create({
data: {
createdAt: new Date(),
teamId: team.id,
userId: id,
role: role,
accepted: true,
},
});
console.log(`\t👤 Added '${teamInput.name}' membership for '${username}' with role '${role}'`);
}
return team;
}
export async function seedAttributes(teamId: number) {
const mockAttributes = [
{
name: "Department",
type: "SINGLE_SELECT",
options: ["Engineering", "Sales", "Marketing", "Product", "Design"],
},
{
name: "Location",
type: "SINGLE_SELECT",
options: ["New York", "London", "Tokyo", "Berlin", "Remote"],
},
{
name: "Skills",
type: "MULTI_SELECT",
options: ["JavaScript", "React", "Node.js", "Python", "Design", "Sales"],
},
{
name: "Years of Experience",
type: "NUMBER",
},
{
name: "Bio",
type: "TEXT",
},
];
// Check if attributes already exist
const existingAttributes = await prisma.attribute.findMany({
where: {
teamId: teamId,
name: {
in: mockAttributes.map((attr) => attr.name),
},
},
});
if (existingAttributes.length > 0) {
console.log(`Skipping attributes seed, attributes already exist`);
return;
}
// Get team members
const memberships = await prisma.membership.findMany({
where: {
teamId: teamId,
},
select: {
id: true,
userId: true,
},
});
console.log(`🎯 Creating attributes for team ${teamId}`);
const attributeRaw: { id: string; options: { id: string; value: string }[] }[] = [];
for (const attr of mockAttributes) {
const attribute = await prisma.attribute.create({
data: {
name: attr.name,
slug: attr.name.toLowerCase().replace(/ /g, "-"),
type: attr.type as "TEXT" | "NUMBER" | "SINGLE_SELECT" | "MULTI_SELECT",
teamId: teamId,
enabled: true,
options: attr.options
? {
create: attr.options.map((opt) => ({
value: opt,
slug: opt.toLowerCase().replace(/ /g, "-"),
})),
}
: undefined,
},
include: {
options: true,
},
});
attributeRaw.push({
id: attribute.id,
options: attribute.options.map((opt) => ({
id: opt.id,
value: opt.value,
})),
});
console.log(`\t📝 Created attribute: ${attr.name}`);
// Assign random values/options to members
for (const member of memberships) {
if (attr.type === "TEXT") {
const mockText = `Sample ${attr.name.toLowerCase()} text for user ${member.userId}`;
await prisma.attributeOption.create({
data: {
value: mockText,
slug: mockText.toLowerCase().replace(/ /g, "-"),
attribute: {
connect: {
id: attribute.id,
},
},
assignedUsers: {
create: {
memberId: member.id,
},
},
},
});
} else if (attr.type === "NUMBER") {
const mockNumber = Math.floor(Math.random() * 10 + 1).toString();
await prisma.attributeOption.create({
data: {
value: mockNumber,
slug: mockNumber,
attribute: {
connect: {
id: attribute.id,
},
},
assignedUsers: {
create: {
memberId: member.id,
},
},
},
});
} else if (attr.type === "SINGLE_SELECT" && attribute.options.length > 0) {
const randomOption = attribute.options[Math.floor(Math.random() * attribute.options.length)];
await prisma.attributeToUser.create({
data: {
memberId: member.id,
attributeOptionId: randomOption.id,
},
});
} else if (attr.type === "MULTI_SELECT" && attribute.options.length > 0) {
// Assign 1-3 random options
const numOptions = Math.floor(Math.random() * 3) + 1;
const shuffledOptions = [...attribute.options].sort(() => Math.random() - 0.5);
const selectedOptions = shuffledOptions.slice(0, numOptions);
for (const option of selectedOptions) {
await prisma.attributeToUser.create({
data: {
memberId: member.id,
attributeOptionId: option.id,
},
});
}
}
}
console.log(`\t✅ Assigned ${attr.name} values to ${memberships.length} members`);
}
return attributeRaw;
}
export async function seedRoutingForms(
teamId: number,
userId: number,
attributeRaw: { id: string; options: { id: string; value: string }[] }[],
javascriptEventId: number,
salesEventId: number
) {
const seededForm = {
id: "948ae412-d995-4865-885a-48302588de03",
name: `Seeded Form - Insights for ${teamId}`,
routes: [
{
id: "8a898988-89ab-4cde-b012-31823f708642",
value: "team/insights-team/team-javascript",
eventTypeId: javascriptEventId,
},
{
id: "8b2224b2-89ab-4cde-b012-31823f708642",
value: "team/insights-team/team-sales",
eventTypeId: salesEventId,
},
],
formFieldLocation: {
id: "674c169a-e40a-492c-b4bb-6f5213873bd6",
},
formFieldSkills: {
id: "83316968-45bf-4c9d-b5d4-5368a8d2d2a8",
},
formFieldEmail: {
id: "dd28ffcf-7029-401e-bddb-ce2e7496a1c1",
},
formFieldManager: {
id: "57734f65-8bbb-4065-9e71-fb7f0b7485f8",
},
formFieldRating: {
id: "f4e9fa6c-5c5d-4d8e-b15c-7f37e9d0c729",
},
};
const form = await prisma.app_RoutingForms_Form.findUnique({
where: {
id: seededForm.id,
},
});
if (form) {
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-09 00:29:26 +05:30
console.log(`Skipping Routing Form - Form Seed, ${seededForm.name} already exists`);
return;
}
if (attributeRaw.length === 0) {
throw new Error("No attributes found - Please call seedAttributes first");
}
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-09 00:29:26 +05:30
const formFieldSkillsOptions = attributeRaw[2].options.map((opt) => ({
id: opt.id,
label: opt.value,
}));
const formFieldLocationOptions = attributeRaw[1].options.map((opt) => ({
id: opt.id,
label: opt.value,
}));
await prisma.app_RoutingForms_Form.create({
data: {
id: seededForm.id,
routes: [
{
id: seededForm.routes[0].id,
action: {
type: "eventTypeRedirectUrl",
value: seededForm.routes[0].value,
eventTypeId: seededForm.routes[0].eventTypeId,
},
queryValue: {
id: "aaba9988-cdef-4012-b456-719300f53ef8",
type: "group",
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-09 00:29:26 +05:30
children1: {
"b98b98a8-0123-4456-b89a-b19300f55277": {
type: "rule",
properties: {
field: seededForm.formFieldSkills.id,
value: [
formFieldSkillsOptions.filter((opt) => opt.label === "JavaScript").map((opt) => opt.id),
],
operator: "multiselect_equals",
valueSrc: ["value"],
valueType: ["multiselect"],
valueError: [null],
},
},
},
},
attributesQueryValue: {
id: "ab99bbb9-89ab-4cde-b012-319300f53ef8",
type: "group",
children1: {
"b98b98a8-0123-4456-b89a-b19300f55277": {
type: "rule",
properties: {
field: attributeRaw[2].id,
value: [
attributeRaw[2].options.filter((opt) => opt.value === "JavaScript").map((opt) => opt.id),
],
operator: "multiselect_some_in",
valueSrc: ["value"],
valueType: ["multiselect"],
valueError: [null],
},
},
},
},
},
{
id: seededForm.routes[1].id,
action: {
type: "eventTypeRedirectUrl",
value: seededForm.routes[1].value,
eventTypeId: seededForm.routes[1].eventTypeId,
},
queryValue: {
id: "aaba9948-cdef-4012-b456-719300f53ef8",
type: "group",
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-09 00:29:26 +05:30
children1: {
"c98b98a8-1123-4456-e89a-a19300f55277": {
type: "rule",
properties: {
field: seededForm.formFieldSkills.id,
value: [formFieldSkillsOptions.filter((opt) => opt.label === "Sales").map((opt) => opt.id)],
operator: "multiselect_equals",
valueSrc: ["value"],
valueType: ["multiselect"],
valueError: [null],
},
},
},
},
attributesQueryValue: {
id: "ab988888-89ab-4cde-b012-319300f53ef8",
type: "group",
children1: {
"b98b98a12-0123-4456-b89a-b19300f55277": {
type: "rule",
properties: {
field: attributeRaw[2].id,
value: [
attributeRaw[2].options.filter((opt) => opt.value === "Sales").map((opt) => opt.id),
],
operator: "multiselect_some_in",
valueSrc: ["value"],
valueType: ["multiselect"],
valueError: [null],
},
},
},
},
fallbackAttributesQueryValue: {
id: "a9888488-4567-489a-bcde-f19300f53ef8",
type: "group",
},
},
{
id: "148899aa-4567-489a-bcde-f1823f708646",
action: { type: "customPageMessage", value: "Fallback Message" },
isFallback: true,
queryValue: { id: "814899aa-4567-489a-bcde-f1823f708646", type: "group" },
},
],
fields: [
{
id: seededForm.formFieldLocation.id,
type: "select",
label: "Location",
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-09 00:29:26 +05:30
options: formFieldLocationOptions,
required: true,
},
{
id: seededForm.formFieldSkills.id,
type: "multiselect",
label: "skills",
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-09 00:29:26 +05:30
options: formFieldSkillsOptions,
required: true,
},
{
id: seededForm.formFieldEmail.id,
type: "email",
label: "Email",
required: true,
},
{
id: seededForm.formFieldManager.id,
type: "text",
label: "Manager",
required: true,
},
{
id: seededForm.formFieldRating.id,
type: "number",
label: "Rating",
required: true,
},
],
team: {
connect: {
id: teamId,
},
},
user: {
connect: {
id: userId,
},
},
name: seededForm.name,
},
});
return seededForm;
}
type SeededForm = {
id: string;
name: string;
routes: {
id: string;
value: string;
}[];
formFieldLocation: {
id: string;
};
formFieldSkills: {
id: string;
};
formFieldEmail: {
id: string;
};
formFieldManager: {
id: string;
};
formFieldRating: {
id: string;
};
};
// id formFillerId formId response createdAt routedToBookingUid
// 1 cm35rrf1x0001rqjskhydev9i 948ae412-d995-4865-885a-48302588de03 "{""83316968-45bf-4c9d-b5d4-5368a8d2d2a8"": {""label"": ""skills"", ""value"": [""8d841a71-3b96-4930-9e2d-57a3584ec21b""]}}" 2024-11-06 11:01:56.853
export async function seedRoutingFormResponses(
seededForm: SeededForm,
attributeRaw: { id: string; options: { id: string; value: string }[] }[],
teamId: number
) {
// Get all bookings for this team
const bookings = await prisma.booking.findMany({
where: {
eventType: {
teamId: teamId,
},
},
take: 1000, // Limit to 1000 responses
select: {
id: true,
uid: true,
},
});
if (bookings.length === 0) {
console.log("No bookings found for team - skipping routing form responses");
return;
}
// Create routing form responses linked to random bookings
for (const booking of bookings) {
// Randomly select 1-3 skills from the form field options
const numSkills = Math.floor(Math.random() * 3) + 1;
const shuffledSkillOptions = [...attributeRaw[2].options].sort(() => Math.random() - 0.5);
const selectedSkills = shuffledSkillOptions.slice(0, numSkills);
const selectedLocation =
attributeRaw[1].options[Math.floor(Math.random() * attributeRaw[1].options.length)];
// Generate a random date within the last 30 days
const randomDate = dayjs()
.subtract(Math.floor(Math.random() * 30), "days")
.subtract(Math.floor(Math.random() * 24), "hours")
.subtract(Math.floor(Math.random() * 60), "minutes");
// Create the form response with the routedToBookingUid field set
const response = await prisma.app_RoutingForms_FormResponse.create({
data: {
formId: seededForm.id,
formFillerId: randomUUID(),
createdAt: randomDate.toDate(),
response: {
[seededForm.formFieldLocation.id]: {
label: "Location",
value: selectedLocation.id,
},
[seededForm.formFieldSkills.id]: {
label: "skills",
value: selectedSkills.map((opt) => opt.id),
},
[seededForm.formFieldEmail.id]: {
label: "Email",
value: faker.internet.email(),
},
[seededForm.formFieldManager.id]: {
label: "Manager",
value: faker.person.fullName(),
},
[seededForm.formFieldRating.id]: {
label: "Rating",
value: Math.floor(Math.random() * 5) + 1,
},
},
},
});
// Update the response with the booking UID
await prisma.booking.update({
where: {
id: booking.id,
},
data: {
routedFromRoutingFormReponse: { connect: { id: response.id } },
startTime: randomDate.toDate(),
endTime: randomDate.add(1, "hour").toDate(),
createdAt: randomDate.toDate(),
},
});
await prisma.app_RoutingForms_FormResponse.create({
data: {
formId: seededForm.id,
formFillerId: randomUUID(),
createdAt: randomDate.subtract(2, "hour").toDate(),
response: {
[seededForm.formFieldLocation.id]: {
label: "Location",
value: selectedLocation.id,
},
[seededForm.formFieldSkills.id]: {
label: "skills",
value: selectedSkills.map((opt) => opt.id),
},
[seededForm.formFieldEmail.id]: {
label: "Email",
value: faker.internet.email(),
},
[seededForm.formFieldManager.id]: {
label: "Manager",
value: faker.person.fullName(),
},
[seededForm.formFieldRating.id]: {
label: "Rating",
value: Math.floor(Math.random() * 5) + 1,
},
},
},
});
}
console.log(`Created ${bookings.length} routing form responses`);
}