Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements: Prefill Routing Forms and connect prefilling with Booking Form #8780

Merged
merged 7 commits into from
May 17, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,15 @@ const MultiSelectWidget = ({
};
});

const defaultValue = selectItems.filter((item) => value?.includes(item.value));
const validValue = selectItems.filter((item) => value?.includes(item.value));

return (
<Select
className="mb-2"
onChange={(items) => {
setValue(items?.map((item) => item.value));
}}
defaultValue={defaultValue}
value={validValue}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a controlled input so it should use value field.

isMulti={true}
isDisabled={remainingProps.readOnly}
options={selectItems}
Expand All @@ -184,7 +184,7 @@ function SelectWidget({ listValues, setValue, value, ...remainingProps }: Select
value: item.value,
};
});
const defaultValue = selectItems.find((item) => item.value === value);
const validValue = selectItems.find((item) => item.value === value);

return (
<Select
Expand All @@ -196,7 +196,7 @@ function SelectWidget({ listValues, setValue, value, ...remainingProps }: Select
setValue(item.value);
}}
isDisabled={remainingProps.readOnly}
defaultValue={defaultValue}
value={validValue}
options={selectItems}
{...remainingProps}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@ import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import { Button, showToast, useCalcomTheme } from "@calcom/ui";

import FormInputFields from "../../components/FormInputFields";
import getFieldIdentifier from "../../lib/getFieldIdentifier";
import { getSerializableForm } from "../../lib/getSerializableForm";
import { processRoute } from "../../lib/processRoute";
import type { Response, Route } from "../../types/types";

type Props = inferSSRProps<typeof getServerSideProps>;
const useBrandColors = ({
brandColor,
darkBrandColor,
Expand All @@ -34,7 +36,7 @@ const useBrandColors = ({
useCalcomTheme(brandTheme);
};

function RoutingForm({ form, profile, ...restProps }: inferSSRProps<typeof getServerSideProps>) {
function RoutingForm({ form, profile, ...restProps }: Props) {
const [customPageMessage, setCustomPageMessage] = useState<Route["action"]["value"]>("");
const formFillerIdRef = useRef(uuidv4());
const isEmbed = useIsEmbed(restProps.isEmbed);
Expand All @@ -48,9 +50,8 @@ function RoutingForm({ form, profile, ...restProps }: inferSSRProps<typeof getSe
// - like a network error
// - or he abandoned booking flow in between
const formFillerId = formFillerIdRef.current;
const decidedActionRef = useRef<Route["action"]>();
const decidedActionWithResponseRef = useRef<{ action: Route["action"]; response: Response }>();
const router = useRouter();

const onSubmit = (response: Response) => {
const decidedAction = processRoute({ form, response });

Expand All @@ -65,7 +66,10 @@ function RoutingForm({ form, profile, ...restProps }: inferSSRProps<typeof getSe
formFillerId,
response: response,
});
decidedActionRef.current = decidedAction;
decidedActionWithResponseRef.current = {
action: decidedAction,
response,
};
};

useEffect(() => {
Expand All @@ -75,18 +79,24 @@ function RoutingForm({ form, profile, ...restProps }: inferSSRProps<typeof getSe

const responseMutation = trpc.viewer.appRoutingForms.public.response.useMutation({
onSuccess: () => {
const decidedAction = decidedActionRef.current;
if (!decidedAction) {
const decidedActionWithResponse = decidedActionWithResponseRef.current;
if (!decidedActionWithResponse) {
return;
}
const fields = form.fields;
if (!fields) {
throw new Error("Routing Form fields must exist here");
}
const allURLSearchParams = getUrlSearchParamsToForward(decidedActionWithResponse.response, fields);
const decidedAction = decidedActionWithResponse.action;

//TODO: Maybe take action after successful mutation
if (decidedAction.type === "customPageMessage") {
setCustomPageMessage(decidedAction.value);
} else if (decidedAction.type === "eventTypeRedirectUrl") {
router.push(`/${decidedAction.value}`);
router.push(`/${decidedAction.value}?${allURLSearchParams}`);
} else if (decidedAction.type === "externalRedirectUrl") {
window.parent.location.href = decidedAction.value;
window.parent.location.href = `${decidedAction.value}?${allURLSearchParams}`;
}
// showToast("Form submitted successfully! Redirecting now ...", "success");
},
Expand All @@ -103,6 +113,13 @@ function RoutingForm({ form, profile, ...restProps }: inferSSRProps<typeof getSe

const [response, setResponse] = useState<Response>({});

form.fields?.forEach((field) => {
response[field.id] = response[field.id] || {
value: router.query[getFieldIdentifier(field)],
label: field.label,
};
}, {});

const handleOnSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
onSubmit(response);
Expand Down Expand Up @@ -161,6 +178,53 @@ function RoutingForm({ form, profile, ...restProps }: inferSSRProps<typeof getSe
);
}

function getUrlSearchParamsToForward(response: Response, fields: NonNullable<Props["form"]["fields"]>) {
const paramsFromResponse: Record<string, string | string[]> = {};
const paramsFromCurrentUrl: Record<string, string | string[]> = {};

// Build query params from response
Object.entries(response).forEach(([key, fieldResponse]) => {
const foundField = fields.find((f) => f.id === key);
if (!foundField) {
// If for some reason, the field isn't there, let's just
return;
}
paramsFromResponse[getFieldIdentifier(foundField) as keyof typeof paramsFromResponse] =
fieldResponse.value;
});

// Build query params from current URL. It excludes route params
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
for (const [name, value] of new URLSearchParams(window.location.search).entries()) {
const target = paramsFromCurrentUrl[name];
if (target instanceof Array) {
target.push(value);
} else {
paramsFromCurrentUrl[name] = [value];
}
}

const allQueryParams: Record<string, string | string[]> = {
...paramsFromResponse,
// Make sure that all params initially provided are passed as is.
// In case of conflict b/w paramsFromResponse and pageParams, pageParams should win.
...paramsFromCurrentUrl,
};

const allQueryURLSearchParams = new URLSearchParams();

// Make serializable URLSearchParams instance
Object.entries(allQueryParams).forEach(([param, value]) => {
const valueArray = value instanceof Array ? value : [value];
valueArray.forEach((v) => {
allQueryURLSearchParams.append(param, v);
});
});

return allQueryURLSearchParams;
}

export default function RoutingLink(props: inferSSRProps<typeof getServerSideProps>) {
return <RoutingForm {...props} />;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/features/form-builder/FormBuilder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ export const FormBuilder = function FormBuilder({
// It has the same drawback that if the label is changed, the value of the option will change. It is not a big deal for now.
value.splice(index, 1, {
label: e.target.value,
value: e.target.value.toLowerCase().trim(),
Copy link
Member Author

@hariombalhara hariombalhara May 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Routing Form uses the strategy of not lowercasing the option label and using it as is.
Changed this logic here to match that, otherwise an option value forwarded from Routing Form won't be prefillable in Booking Form.
It also fixes #8839

value: e.target.value.trim(),
});
onChange(value);
}}
Expand Down