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
@@ -180,6 +180,7 @@ export const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function
placeholder={placeholder}
className={className}
{...passThrough}
readOnly={readOnly}
ref={ref}
isFullWidth={inputIsFullWidth}
/>
@@ -340,6 +341,7 @@ const PlainForm = <T extends FieldValues>(props: FormProps<T>, ref: Ref<HTMLForm
form
.handleSubmit(handleSubmit)(event)
.catch((err) => {
// FIXME: Booking Pages don't have toast, so this error is never shown
showToast(`${getErrorFromUnknown(err).message}`, "error");
});
}}
+4 -1
View File
@@ -4,7 +4,10 @@ export function Label(props: JSX.IntrinsicElements["label"]) {
return (
<label
{...props}
className={classNames("mb-2 block text-sm font-medium leading-none text-gray-700", props.className)}>
className={classNames(
"mb-2 block text-sm font-medium leading-none text-gray-700 dark:text-white",
props.className
)}>
{props.children}
</label>
);
@@ -120,6 +120,7 @@ export const SelectField = function SelectField<
Group extends GroupBase<Option> = GroupBase<Option>
>(
props: {
required?: boolean;
name?: string;
containerClassName?: string;
label?: string;
+40 -29
View File
@@ -5,6 +5,14 @@ import React from "react";
import classNames from "@calcom/lib/classNames";
import { Tooltip } from "../../tooltip";
const Wrapper = ({ children, tooltip }: { tooltip?: string; children: React.ReactNode }) => {
if (!tooltip) {
return <>{children}</>;
}
return <Tooltip content={tooltip}>{children}</Tooltip>;
};
const Switch = (
props: React.ComponentProps<typeof PrimitiveSwitch.Root> & {
label?: string;
@@ -12,43 +20,46 @@ const Switch = (
className?: string;
};
fitToHeight?: boolean;
tooltip?: string;
}
) => {
const { label, fitToHeight, ...primitiveProps } = props;
const id = useId();
return (
<div className={classNames("flex h-auto w-auto flex-row items-center", fitToHeight && "h-fit")}>
<PrimitiveSwitch.Root
className={classNames(
props.checked ? "bg-gray-900" : "bg-gray-200",
primitiveProps.disabled ? "cursor-not-allowed" : "hover:bg-gray-300",
"focus:ring-brand-800 h-5 w-[34px] rounded-full shadow-none",
props.className
)}
{...primitiveProps}>
<PrimitiveSwitch.Thumb
id={id}
// Since we dont support global dark mode - we have to style dark mode components specifically on the instance for now
// TODO: Remove once we support global dark mode
<Wrapper tooltip={props.tooltip}>
<div className={classNames("flex h-auto w-auto flex-row items-center", fitToHeight && "h-fit")}>
<PrimitiveSwitch.Root
className={classNames(
"block h-[14px] w-[14px] rounded-full bg-white transition will-change-transform ltr:translate-x-[4px] rtl:-translate-x-[4px] ltr:[&[data-state='checked']]:translate-x-[17px] rtl:[&[data-state='checked']]:-translate-x-[17px]",
props.checked && "shadow-inner",
props.thumbProps?.className
props.checked ? "bg-gray-900" : "bg-gray-200",
primitiveProps.disabled ? "cursor-not-allowed" : "hover:bg-gray-300",
"focus:ring-brand-800 h-5 w-[34px] rounded-full shadow-none",
props.className
)}
/>
</PrimitiveSwitch.Root>
{label && (
<Label.Root
htmlFor={id}
className={classNames(
"align-text-top text-sm font-medium text-gray-900 ltr:ml-3 rtl:mr-3 dark:text-white",
primitiveProps.disabled ? "cursor-not-allowed opacity-25" : "cursor-pointer "
)}>
{label}
</Label.Root>
)}
</div>
{...primitiveProps}>
<PrimitiveSwitch.Thumb
id={id}
// Since we dont support global dark mode - we have to style dark mode components specifically on the instance for now
// TODO: Remove once we support global dark mode
className={classNames(
"block h-[14px] w-[14px] rounded-full bg-white transition will-change-transform ltr:translate-x-[4px] rtl:-translate-x-[4px] ltr:[&[data-state='checked']]:translate-x-[17px] rtl:[&[data-state='checked']]:-translate-x-[17px]",
props.checked && "shadow-inner",
props.thumbProps?.className
)}
/>
</PrimitiveSwitch.Root>
{label && (
<Label.Root
htmlFor={id}
className={classNames(
"align-text-top text-sm font-medium text-gray-900 ltr:ml-3 rtl:mr-3 dark:text-white",
primitiveProps.disabled ? "cursor-not-allowed opacity-25" : "cursor-pointer "
)}>
{label}
</Label.Root>
)}
</div>
</Wrapper>
);
};
@@ -32,7 +32,7 @@ export const BooleanToggleGroup = function BooleanToggleGroup({
return null;
}
const commonClass =
"mb-2 inline-flex items-center justify-center rounded-md py-[10px] px-4 text-sm font-medium leading-4 md:mb-0";
"w-full inline-flex items-center justify-center rounded py-[10px] px-4 text-sm font-medium leading-4";
const selectedClass = classNames(commonClass, "bg-gray-200 text-gray-900");
const unselectedClass = classNames(commonClass, "text-gray-600 hover:bg-gray-100 hover:text-gray-900");
return (
@@ -40,7 +40,7 @@ export const BooleanToggleGroup = function BooleanToggleGroup({
value={yesNoValue}
type="single"
disabled={disabled}
className="space-x-2 rounded-sm rtl:space-x-reverse"
className="flex space-x-2 rounded-md border border-gray-200 p-1 rtl:space-x-reverse"
onValueChange={(yesNoValue: "yes" | "no") => {
setYesNoValue(yesNoValue);
onValueChange(boolean(yesNoValue));
+12 -34
View File
@@ -1,46 +1,24 @@
import type { UseFormReturn } from "react-hook-form";
import type { Props } from "react-phone-number-input/react-hook-form";
import type { EventLocationType } from "@calcom/app-store/locations";
import { FiMapPin } from "../components/icon";
type BookingFormValues = {
name: string;
email: string;
notes?: string;
locationType?: EventLocationType["type"];
guests?: { email: string }[];
address?: string;
attendeeAddress?: string;
phone?: string;
hostPhoneNumber?: string; // Maybe come up with a better way to name this to distingish between two types of phone numbers
customInputs?: {
[key: string]: string | boolean;
};
rescheduleReason?: string;
smsReminderNumber?: string;
export type AddressInputProps = {
value: string;
id?: string;
placeholder?: string;
required?: boolean;
onChange: (val: string) => void;
className?: string;
};
export type AddressInputProps<FormValues> = Props<
{
value: string;
id: string;
placeholder: string;
required: boolean;
bookingForm: UseFormReturn<BookingFormValues>;
},
FormValues
>;
function AddressInput<FormValues>({ bookingForm, name, className, ...rest }: AddressInputProps<FormValues>) {
function AddressInput({ className = "", value, onChange, ...rest }: AddressInputProps) {
return (
<div className="relative ">
<FiMapPin color="#D2D2D2" className="absolute top-1/2 left-0.5 ml-3 h-6 -translate-y-1/2" />
<input
{...rest}
{...bookingForm.register("attendeeAddress")}
name={name}
value={value}
onChange={(e) => {
onChange(e.target.value);
}}
color="#D2D2D2"
className={`${className} focus-within:border-brand dark:bg-darkgray-100 dark:border-darkgray-300 block h-10 w-full rounded-md border border border-gray-300 py-px pl-10 text-sm outline-none ring-black focus-within:ring-1 disabled:text-gray-500 disabled:opacity-50 dark:text-white dark:placeholder-gray-500 dark:selection:bg-green-500 disabled:dark:text-gray-500`}
/>
+13 -20
View File
@@ -1,34 +1,27 @@
import { isSupportedCountry } from "libphonenumber-js";
import { useEffect, useState } from "react";
import type { Props } from "react-phone-number-input/react-hook-form";
import BasePhoneInput from "react-phone-number-input/react-hook-form";
import BasePhoneInput from "react-phone-number-input";
import type { Props, Country } from "react-phone-number-input";
import "react-phone-number-input/style.css";
export type PhoneInputProps<FormValues> = Props<
{
value: string;
id: string;
placeholder: string;
required: boolean;
},
FormValues
> & { onChange?: (e: any) => void };
export type PhoneInputProps = Props<{
value: string;
id?: string;
placeholder?: string;
required?: boolean;
className?: string;
name?: string;
}>;
function PhoneInput<FormValues>({
control,
name,
className,
onChange,
...rest
}: PhoneInputProps<FormValues>) {
function PhoneInput({ name, className = "", onChange, ...rest }: PhoneInputProps) {
const defaultCountry = useDefaultCountry();
return (
<BasePhoneInput
{...rest}
international
defaultCountry={defaultCountry}
name={name}
control={control}
onChange={onChange}
countrySelectProps={{ className: "text-black" }}
numberInputProps={{
@@ -40,7 +33,7 @@ function PhoneInput<FormValues>({
}
const useDefaultCountry = () => {
const [defaultCountry, setDefaultCountry] = useState("US");
const [defaultCountry, setDefaultCountry] = useState<Country>("US");
useEffect(() => {
fetch("/api/countrycode")
.then((res) => res.json())