Feature/ Manage Booking Questions (#6560)

* WIP

* Create Booking Questions builder

* Renaming things

* wip

* wip

* Implement Add Guests and other fixes

* Fixes after testing

* Fix wrong status code 404

* Fixes

* Lint fixes

* Self review comments addressed

* More self review comments addressed

* Feedback from zomars

* BugFixes after testing

* More fixes discovered during review

* Update packages/lib/hooks/useHasPaidPlan.ts

Co-authored-by: Omar López <zomars@me.com>

* More fixes discovered during review

* Update packages/ui/components/form/inputs/Input.tsx

Co-authored-by: Omar López <zomars@me.com>

* More fixes discovered during review

* Update packages/features/bookings/lib/getBookingFields.ts

Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>

* More PR review fixes

* Hide label using labelSrOnly

* Fix Carinas feedback and implement 2 workflows thingy

* Misc fixes

* Fixes from Loom comments and PR

* Fix a lint errr

* Fix cancellation reason

* Fix regression in edit due to name conflict check

* Update packages/features/form-builder/FormBuilder.tsx

Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>

* Fix options not set when default value is used

* Restoring reqBody to avoid uneeded conflicts with main

* Type fix

* Update apps/web/components/booking/pages/BookingPage.tsx

Co-authored-by: Omar López <zomars@me.com>

* Update packages/features/form-builder/FormBuilder.tsx

Co-authored-by: Omar López <zomars@me.com>

* Update apps/web/components/booking/pages/BookingPage.tsx

Co-authored-by: Omar López <zomars@me.com>

* Apply suggestions from code review

Co-authored-by: Omar López <zomars@me.com>

* Show fields but mark them disabled

* Apply suggestions from code review

Co-authored-by: Omar López <zomars@me.com>

* More comments

* Fix booking success page crash when a booking doesnt have newly added required fields response

* Dark theme asterisk not visible

* Make location required in zodSchema as was there in production

* Linting

* Remove _metadata.ts files for apps that have config.json

* Revert "Remove _metadata.ts files for apps that have config.json"

This reverts commit d79bdd336cf312a30a8943af94c059947bd91ccd.

* Fix lint error

* Fix missing condition for samlSPConfig

* Delete unexpectedly added file

* yarn.lock change not required

* fix types

* Make checkboxes rounded

* Fix defaultLabel being stored as label due to SSR rendering

* Shaved 16kb from booking page

* Explicit types for profile

* Show payment value only if price is greater than 0

* Fix type error

* Add back inferred types as they are failing

* Fix duplicate label on number

---------

Co-authored-by: zomars <zomars@me.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Carina Wollendorfer <30310907+CarinaWolli@users.noreply.github.com>
Co-authored-by: Efraín Rochín <roae.85@gmail.com>
This commit is contained in:
Hariom Balhara
2023-03-02 11:15:28 -07:00
committed by GitHub
co-authored by Omar López sean-brydon Carina Wollendorfer Efraín Rochín
parent 51bf613621
commit 517cfde5b8
58 changed files with 2921 additions and 1463 deletions
@@ -1,42 +0,0 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Badge, Button, ButtonGroup } from "@calcom/ui";
import { FiTrash } from "@calcom/ui/components/icon";
type Props = {
required?: boolean;
question?: string;
type?: string;
editOnClick: () => void;
deleteOnClick: () => void;
};
function CustomInputItem({ required, deleteOnClick, editOnClick, type, question }: Props) {
const { t } = useLocale();
return (
<li className="flex rounded-b-md border-t border-gray-200 bg-white px-6 py-4 first:rounded-t-md first:border-0">
<div className="flex flex-col">
<div className="flex items-center">
<span className="pr-2 text-sm font-semibold leading-none text-black">{question}</span>
<Badge variant="default" color="gray" withDot={false}>
{required ? t("required") : t("optional")}
</Badge>
</div>
<p className="text-sm leading-normal text-gray-600">{type}</p>
</div>
<ButtonGroup containerProps={{ className: "ml-auto" }}>
<Button color="secondary" onClick={editOnClick}>
{t("edit")}
</Button>
<Button
StartIcon={FiTrash}
variant="icon"
color="destructive"
onClick={deleteOnClick}
className="h-[36px] border border-gray-200"
/>
</ButtonGroup>
</li>
);
}
export default CustomInputItem;
@@ -1,7 +1,6 @@
import dynamic from "next/dynamic";
export { default as CheckedTeamSelect } from "./CheckedTeamSelect";
export { default as CustomInputItem } from "./CustomInputItem";
export { default as CreateEventTypeDialog } from "./CreateEventTypeDialog";
export { default as EventTypeDescription } from "./EventTypeDescription";
export const EventTypeDescriptionLazy = dynamic(() => import("./EventTypeDescription"));
@@ -0,0 +1,141 @@
import { z } from "zod";
import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/getBookingFields";
import { prisma } from "@calcom/prisma";
import { EventType } from "@calcom/prisma/client";
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
type Field = z.infer<typeof eventTypeBookingFields>[number];
async function getEventType(eventTypeId: EventType["id"]) {
const rawEventType = await prisma.eventType.findUnique({
where: {
id: eventTypeId,
},
include: {
customInputs: true,
workflows: {
select: {
workflow: {
select: {
id: true,
steps: true,
},
},
},
},
},
});
if (!rawEventType) {
throw new Error(`EventType:${eventTypeId} not found`);
}
const eventType = {
...rawEventType,
bookingFields: getBookingFieldsWithSystemFields(rawEventType),
};
return eventType;
}
/**
*
* @param fieldToAdd Field to add
* @param source Source of the field to be shown in UI
* @param eventTypeId
*/
export async function upsertBookingField(
fieldToAdd: Omit<Field, "required">,
source: NonNullable<Field["sources"]>[number],
eventTypeId: EventType["id"]
) {
const eventType = await getEventType(eventTypeId);
let fieldFound = false;
const newFields = eventType.bookingFields.map((f) => {
if (f.name === fieldToAdd.name) {
fieldFound = true;
const currentSources = f.sources ? f.sources : ([] as NonNullable<typeof f.sources>[]);
let sourceFound = false;
let newSources = currentSources.map((s) => {
if (s.id !== source.id) {
// If the source is not found, nothing to update
return s;
}
sourceFound = true;
return {
...s,
...source,
};
});
if (!sourceFound) {
newSources = [...newSources, source];
}
const newField = {
...f,
// If any source requires the field, mark the field required
required: newSources.some((s) => s.fieldRequired),
sources: newSources,
};
return newField;
}
return f;
});
if (!fieldFound) {
newFields.push({
...fieldToAdd,
required: source.fieldRequired,
sources: [source],
});
}
await prisma.eventType.update({
where: {
id: eventTypeId,
},
data: {
bookingFields: newFields,
},
});
}
export async function removeBookingField(
fieldToRemove: Pick<Field, "name">,
source: Pick<NonNullable<Field["sources"]>[number], "id" | "type">,
eventTypeId: EventType["id"]
) {
const eventType = await getEventType(eventTypeId);
const newFields = eventType.bookingFields
.map((f) => {
if (f.name === fieldToRemove.name) {
const currentSources = f.sources ? f.sources : ([] as NonNullable<typeof f.sources>[]);
if (!currentSources.find((s) => s.id === source.id)) {
// No need to remove the source - It doesn't exist already
return f;
}
const newSources = currentSources.filter((s) => s.id !== source.id);
const newField = {
...f,
required: newSources.some((s) => s.fieldRequired),
sources: newSources,
};
if (newField.sources.length === 0) {
return null;
}
return newField;
}
return f;
})
.filter((f): f is Field => !!f);
await prisma.eventType.update({
where: {
id: eventTypeId,
},
data: {
bookingFields: newFields,
},
});
}