Fix infinite renders on event-type edit page (#2820)
This commit is contained in:
parent
67770bf878
commit
f0a36f8194
|
@ -8,6 +8,8 @@ Fixes # (issue)
|
||||||
Loom Video: https://www.loom.com/
|
Loom Video: https://www.loom.com/
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
**Environment**: Staging(main branch) / Production
|
||||||
|
|
||||||
## Type of change
|
## Type of change
|
||||||
|
|
||||||
<!-- Please delete options that are not relevant. -->
|
<!-- Please delete options that are not relevant. -->
|
||||||
|
|
|
@ -1,57 +1,31 @@
|
||||||
import { CheckIcon, XIcon } from "@heroicons/react/outline";
|
import { CheckIcon, XIcon } from "@heroicons/react/outline";
|
||||||
import React, { useEffect, useState } from "react";
|
import React from "react";
|
||||||
import { MultiValue } from "react-select";
|
import { Props } from "react-select";
|
||||||
|
|
||||||
import { useLocale } from "@lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
|
|
||||||
import Avatar from "@components/ui/Avatar";
|
import Avatar from "@components/ui/Avatar";
|
||||||
import Select from "@components/ui/form/Select";
|
import Select from "@components/ui/form/Select";
|
||||||
|
|
||||||
type CheckedSelectValue = {
|
type CheckedSelectOption = {
|
||||||
avatar: string;
|
avatar: string;
|
||||||
label: string;
|
label: string;
|
||||||
value: string;
|
value: string;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}[];
|
|
||||||
|
|
||||||
export type CheckedSelectProps = {
|
|
||||||
defaultValue?: CheckedSelectValue;
|
|
||||||
placeholder?: string;
|
|
||||||
name?: string;
|
|
||||||
options: CheckedSelectValue;
|
|
||||||
onChange: (options: CheckedSelectValue) => void;
|
|
||||||
disabled: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CheckedSelect = (props: CheckedSelectProps) => {
|
export const CheckedSelect = ({
|
||||||
const { onChange } = props;
|
options = [],
|
||||||
const [selectedOptions, setSelectedOptions] = useState<CheckedSelectValue>(props.defaultValue || []);
|
value = [],
|
||||||
|
...props
|
||||||
|
}: Omit<Props<CheckedSelectOption, true>, "value" | "onChange"> & {
|
||||||
|
value?: readonly CheckedSelectOption[];
|
||||||
|
onChange: (value: readonly CheckedSelectOption[]) => void;
|
||||||
|
}) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onChange(selectedOptions);
|
|
||||||
}, [onChange, selectedOptions]);
|
|
||||||
|
|
||||||
const options = props.options.map((option) => ({
|
|
||||||
...option,
|
|
||||||
disabled: !!selectedOptions.find((selectedOption) => selectedOption.value === option.value),
|
|
||||||
}));
|
|
||||||
|
|
||||||
const removeOption = (value: string) =>
|
|
||||||
setSelectedOptions(selectedOptions.filter((option) => option.value !== value));
|
|
||||||
|
|
||||||
const changeHandler = (selections: MultiValue<CheckedSelectValue[number]>) =>
|
|
||||||
selections.forEach((selected) => {
|
|
||||||
if (selectedOptions.find((option) => option.value === selected.value)) {
|
|
||||||
removeOption(selected.value);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setSelectedOptions(selectedOptions.concat(selected));
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Select<CheckedSelectValue[number], true>
|
<Select
|
||||||
styles={{
|
styles={{
|
||||||
option: (styles, { isDisabled }) => ({
|
option: (styles, { isDisabled }) => ({
|
||||||
...styles,
|
...styles,
|
||||||
|
@ -73,11 +47,11 @@ export const CheckedSelect = (props: CheckedSelectProps) => {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
options={options}
|
options={options}
|
||||||
|
value={value}
|
||||||
isMulti
|
isMulti
|
||||||
// value={props.placeholder || t("select")}
|
{...props}
|
||||||
onChange={changeHandler}
|
|
||||||
/>
|
/>
|
||||||
{selectedOptions.map((option) => (
|
{value.map((option) => (
|
||||||
<div key={option.value} className="border-1 border p-2 font-medium">
|
<div key={option.value} className="border-1 border p-2 font-medium">
|
||||||
<Avatar
|
<Avatar
|
||||||
className="inline h-6 w-6 rounded-full ltr:mr-2 rtl:ml-2"
|
className="inline h-6 w-6 rounded-full ltr:mr-2 rtl:ml-2"
|
||||||
|
@ -86,7 +60,7 @@ export const CheckedSelect = (props: CheckedSelectProps) => {
|
||||||
/>
|
/>
|
||||||
{option.label}
|
{option.label}
|
||||||
<XIcon
|
<XIcon
|
||||||
onClick={() => changeHandler([option])}
|
onClick={() => props.onChange(value.filter((item) => item.value !== option.value))}
|
||||||
className="float-right mt-0.5 h-5 w-5 cursor-pointer text-neutral-500"
|
className="float-right mt-0.5 h-5 w-5 cursor-pointer text-neutral-500"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -95,6 +69,4 @@ export const CheckedSelect = (props: CheckedSelectProps) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
CheckedSelect.displayName = "CheckedSelect";
|
|
||||||
|
|
||||||
export default CheckedSelect;
|
export default CheckedSelect;
|
||||||
|
|
|
@ -148,6 +148,7 @@ const SuccessRedirectEdit = <T extends UseFormReturn<FormValues>>({
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const proUpgradeRequired = !isSuccessRedirectAvailable(eventType);
|
const proUpgradeRequired = !isSuccessRedirectAvailable(eventType);
|
||||||
const [modalOpen, setModalOpen] = useState(false);
|
const [modalOpen, setModalOpen] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<hr className="border-neutral-200" />
|
<hr className="border-neutral-200" />
|
||||||
|
@ -233,6 +234,7 @@ const AvailabilitySelect = ({
|
||||||
|
|
||||||
const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
|
||||||
const PERIOD_TYPES = [
|
const PERIOD_TYPES = [
|
||||||
{
|
{
|
||||||
type: "ROLLING" as const,
|
type: "ROLLING" as const,
|
||||||
|
@ -1189,16 +1191,19 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||||
name="users"
|
name="users"
|
||||||
control={formMethods.control}
|
control={formMethods.control}
|
||||||
defaultValue={eventType.users.map((user) => user.id.toString())}
|
defaultValue={eventType.users.map((user) => user.id.toString())}
|
||||||
render={() => (
|
render={({ field: { onChange, value } }) => (
|
||||||
<CheckedSelect
|
<CheckedSelect
|
||||||
disabled={false}
|
isDisabled={false}
|
||||||
onChange={(options) => {
|
onChange={(options) => onChange(options.map((user) => user.value))}
|
||||||
formMethods.setValue(
|
value={value
|
||||||
"users",
|
.map(
|
||||||
options.map((user) => user.value)
|
(userId) =>
|
||||||
);
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
}}
|
teamMembers
|
||||||
defaultValue={eventType.users.map(mapUserToValue)}
|
.map(mapUserToValue)
|
||||||
|
.find((member) => member.value === userId)!
|
||||||
|
)
|
||||||
|
.filter(Boolean)}
|
||||||
options={teamMembers.map(mapUserToValue)}
|
options={teamMembers.map(mapUserToValue)}
|
||||||
placeholder={t("add_attendees")}
|
placeholder={t("add_attendees")}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue
Block a user