Fix "User Added Question" Label in Email and Calendar Invite (#7559)

* Show label in email and calendar invite and send label as well as name in webhook

* Make common code reusable

---------

Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
Hariom Balhara 2023-03-07 23:20:54 +05:30 committed by GitHub
parent 6f8ea490d0
commit ce8e1d52da
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 75 additions and 22 deletions

View File

@ -90,7 +90,16 @@ test("add webhook & test that creating an event triggers a webhook call", async
timeZone: "[redacted/dynamic]", timeZone: "[redacted/dynamic]",
language: "[redacted/dynamic]", language: "[redacted/dynamic]",
}, },
responses: { email: "test@example.com", name: "Test Testson" }, responses: {
email: {
value: "test@example.com",
label: "email_address",
},
name: {
value: "Test Testson",
label: "your_name",
},
},
userFieldsResponses: {}, userFieldsResponses: {},
attendees: [ attendees: [
{ {

View File

@ -1,16 +1,17 @@
import getLabelValueMapFromResponses from "@calcom/lib/getLabelValueMapFromResponses";
import type { CalendarEvent } from "@calcom/types/Calendar"; import type { CalendarEvent } from "@calcom/types/Calendar";
import { Info } from "./Info"; import { Info } from "./Info";
export function UserFieldsResponses(props: { calEvent: CalendarEvent }) { export function UserFieldsResponses(props: { calEvent: CalendarEvent }) {
const { customInputs, userFieldsResponses } = props.calEvent; const labelValueMap = getLabelValueMapFromResponses(props.calEvent);
const responses = userFieldsResponses || customInputs;
if (!responses) return null; if (!labelValueMap) return null;
return ( return (
<> <>
{Object.keys(responses).map((key) => {Object.keys(labelValueMap).map((key) =>
responses[key] !== "" ? ( labelValueMap[key] !== "" ? (
<Info key={key} label={key} description={`${responses[key]}`} withSpacer /> <Info key={key} label={key} description={`${labelValueMap[key]}`} withSpacer />
) : null ) : null
)} )}
</> </>

View File

@ -363,11 +363,23 @@ function getBookingData({
const reqBody = bookingDataSchema.parse(req.body); const reqBody = bookingDataSchema.parse(req.body);
if ("responses" in reqBody) { if ("responses" in reqBody) {
const responses = reqBody.responses; const responses = reqBody.responses;
const userFieldsResponses = {} as typeof responses; const calEventResponses = {} as NonNullable<CalendarEvent["responses"]>;
const calEventUserFieldsResponses = {} as NonNullable<CalendarEvent["userFieldsResponses"]>;
eventType.bookingFields.forEach((field) => { eventType.bookingFields.forEach((field) => {
if (field.editable === "user" || field.editable === "user-readonly") { const label = field.label || field.defaultLabel;
userFieldsResponses[field.name] = responses[field.name]; if (!label) {
throw new Error('Missing label for booking field "' + field.name + '"');
} }
if (field.editable === "user" || field.editable === "user-readonly") {
calEventUserFieldsResponses[field.name] = {
label,
value: responses[field.name],
};
}
calEventResponses[field.name] = {
label,
value: responses[field.name],
};
}); });
return { return {
...reqBody, ...reqBody,
@ -377,8 +389,9 @@ function getBookingData({
location: responses.location?.optionValue || responses.location?.value || "", location: responses.location?.optionValue || responses.location?.value || "",
smsReminderNumber: responses.smsReminderNumber, smsReminderNumber: responses.smsReminderNumber,
notes: responses.notes || "", notes: responses.notes || "",
userFieldsResponses, calEventUserFieldsResponses,
rescheduleReason: responses.rescheduleReason, rescheduleReason: responses.rescheduleReason,
calEventResponses,
}; };
} else { } else {
// Check if required custom inputs exist // Check if required custom inputs exist
@ -721,7 +734,8 @@ async function handler(
} }
const responses = "responses" in reqBody ? reqBody.responses : null; const responses = "responses" in reqBody ? reqBody.responses : null;
const userFieldsResponses = "userFieldsResponses" in reqBody ? reqBody.userFieldsResponses : null; const calEventUserFieldsResponses =
"calEventUserFieldsResponses" in reqBody ? reqBody.calEventUserFieldsResponses : null;
let evt: CalendarEvent = { let evt: CalendarEvent = {
type: eventType.title, type: eventType.title,
title: getEventName(eventNameObject), //this needs to be either forced in english, or fetched for each attendee and organizer separately title: getEventName(eventNameObject), //this needs to be either forced in english, or fetched for each attendee and organizer separately
@ -737,8 +751,8 @@ async function handler(
timeZone: organizerUser.timeZone, timeZone: organizerUser.timeZone,
language: { translate: tOrganizer, locale: organizerUser.locale ?? "en" }, language: { translate: tOrganizer, locale: organizerUser.locale ?? "en" },
}, },
responses, responses: "calEventResponses" in reqBody ? reqBody.calEventResponses : null,
userFieldsResponses, userFieldsResponses: calEventUserFieldsResponses,
attendees: attendeesList, attendees: attendeesList,
location: bookingLocation, // Will be processed by the EventManager later. location: bookingLocation, // Will be processed by the EventManager later.
/** For team events & dynamic collective events, we will need to handle each member destinationCalendar eventually */ /** For team events & dynamic collective events, we will need to handle each member destinationCalendar eventually */

View File

@ -4,6 +4,7 @@ import { v5 as uuidv5 } from "uuid";
import type { CalendarEvent } from "@calcom/types/Calendar"; import type { CalendarEvent } from "@calcom/types/Calendar";
import { WEBAPP_URL } from "./constants"; import { WEBAPP_URL } from "./constants";
import getLabelValueMapFromResponses from "./getLabelValueMapFromResponses";
const translator = short(); const translator = short();
@ -72,17 +73,18 @@ ${calEvent.additionalNotes}
}; };
export const getUserFieldsResponses = (calEvent: CalendarEvent) => { export const getUserFieldsResponses = (calEvent: CalendarEvent) => {
const responses = calEvent.userFieldsResponses || calEvent.customInputs; const labelValueMap = getLabelValueMapFromResponses(calEvent);
if (!responses) {
if (!labelValueMap) {
return ""; return "";
} }
const responsesString = Object.keys(responses) const responsesString = Object.keys(labelValueMap)
.map((key) => { .map((key) => {
if (!responses) return ""; if (!labelValueMap) return "";
if (responses[key] !== "") { if (labelValueMap[key] !== "") {
return ` return `
${key}: ${key}:
${responses[key]} ${labelValueMap[key]}
`; `;
} }
}) })

View File

@ -0,0 +1,15 @@
import type { CalendarEvent } from "@calcom/types/Calendar";
export default function getLabelValueMapFromResponses(calEvent: CalendarEvent) {
const { customInputs, userFieldsResponses } = calEvent;
let labelValueMap: Record<string, string | string[]> = {};
if (userFieldsResponses) {
for (const [, value] of Object.entries(userFieldsResponses)) {
labelValueMap[value.label] = value.value;
}
} else {
labelValueMap = customInputs as Record<string, string | string[]>;
}
return labelValueMap;
}

View File

@ -165,10 +165,22 @@ export interface CalendarEvent {
seatsPerTimeSlot?: number | null; seatsPerTimeSlot?: number | null;
// It has responses to all the fields(system + user) // It has responses to all the fields(system + user)
responses?: Prisma.JsonObject | null; responses?: Record<
string,
{
value: string | string[];
label: string;
}
> | null;
// It just has responses to only the user fields. It allows to easily iterate over to show only user fields // It just has responses to only the user fields. It allows to easily iterate over to show only user fields
userFieldsResponses?: Prisma.JsonObject | null; userFieldsResponses?: Record<
string,
{
value: string | string[];
label: string;
}
> | null;
} }
export interface EntryPoint { export interface EntryPoint {