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]",
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: {},
attendees: [
{

View File

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

View File

@ -363,11 +363,23 @@ function getBookingData({
const reqBody = bookingDataSchema.parse(req.body);
if ("responses" in reqBody) {
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) => {
if (field.editable === "user" || field.editable === "user-readonly") {
userFieldsResponses[field.name] = responses[field.name];
const label = field.label || field.defaultLabel;
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 {
...reqBody,
@ -377,8 +389,9 @@ function getBookingData({
location: responses.location?.optionValue || responses.location?.value || "",
smsReminderNumber: responses.smsReminderNumber,
notes: responses.notes || "",
userFieldsResponses,
calEventUserFieldsResponses,
rescheduleReason: responses.rescheduleReason,
calEventResponses,
};
} else {
// Check if required custom inputs exist
@ -721,7 +734,8 @@ async function handler(
}
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 = {
type: eventType.title,
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,
language: { translate: tOrganizer, locale: organizerUser.locale ?? "en" },
},
responses,
userFieldsResponses,
responses: "calEventResponses" in reqBody ? reqBody.calEventResponses : null,
userFieldsResponses: calEventUserFieldsResponses,
attendees: attendeesList,
location: bookingLocation, // Will be processed by the EventManager later.
/** 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 { WEBAPP_URL } from "./constants";
import getLabelValueMapFromResponses from "./getLabelValueMapFromResponses";
const translator = short();
@ -72,17 +73,18 @@ ${calEvent.additionalNotes}
};
export const getUserFieldsResponses = (calEvent: CalendarEvent) => {
const responses = calEvent.userFieldsResponses || calEvent.customInputs;
if (!responses) {
const labelValueMap = getLabelValueMapFromResponses(calEvent);
if (!labelValueMap) {
return "";
}
const responsesString = Object.keys(responses)
const responsesString = Object.keys(labelValueMap)
.map((key) => {
if (!responses) return "";
if (responses[key] !== "") {
if (!labelValueMap) return "";
if (labelValueMap[key] !== "") {
return `
${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;
// 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
userFieldsResponses?: Prisma.JsonObject | null;
userFieldsResponses?: Record<
string,
{
value: string | string[];
label: string;
}
> | null;
}
export interface EntryPoint {