import type { MultiValue, SingleValue } from "react-select";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { Attribute } from "@calcom/app-store/routing-forms/types/types";
import { Button } from "@calcom/ui/components/button";
import { Input, Select } from "@calcom/ui/components/form";
import { FilterIcon } from "@coss/ui/icons";
import {
formatConditionValue,
generateConditionId,
getConditionTypeOptions,
getDefaultAttributeCondition,
getDefaultTeamCondition,
getOperatorOptionsForAttributeType,
getParentOperatorOptions,
getTeamOperatorOptions,
isArrayOperator,
isTeamCondition,
type TAttributeSyncRuleConditionWithId,
} from "@calcom/features/ee/integration-attribute-sync/lib/ruleHelpers";
import {
ConditionIdentifierEnum,
type ConditionOperatorEnum,
type IAttributeCondition,
type IAttributeSyncRule,
type ITeamCondition,
type RuleOperatorEnum,
type TAttributeSyncRuleCondition,
} from "@calcom/features/ee/integration-attribute-sync/repositories/IIntegrationAttributeSyncRepository";
const ensureConditionHasId = (condition: TAttributeSyncRuleCondition): TAttributeSyncRuleConditionWithId => {
if ("_id" in condition && condition._id) {
return condition as TAttributeSyncRuleConditionWithId;
}
return { ...condition, _id: generateConditionId() } as TAttributeSyncRuleConditionWithId;
};
interface RuleBuilderProps {
value: IAttributeSyncRule;
onChange: (value: IAttributeSyncRule) => void;
teamOptions: { value: string; label: string }[];
attributes: Attribute[];
isLoadingTeams?: boolean;
isLoadingAttributes?: boolean;
}
interface ConditionComponentProps {
condition: TAttributeSyncRuleCondition;
onChange: (condition: TAttributeSyncRuleCondition) => void;
onRemove: () => void;
teamOptions: { value: string; label: string }[];
attributes: Attribute[];
isLoading?: boolean;
}
const ConditionComponent = ({
condition,
onChange,
onRemove,
teamOptions,
attributes,
isLoading,
}: ConditionComponentProps) => {
const { t } = useLocale();
const conditionTypeOptions = getConditionTypeOptions(t);
const conditionTypeOption = conditionTypeOptions.find(
(opt: { value: ConditionIdentifierEnum; label: string }) => opt.value === condition.identifier
);
const handleTypeChange = (newType: { value: ConditionIdentifierEnum; label: string } | null) => {
if (!newType) return;
if (newType.value === ConditionIdentifierEnum.TEAM_ID) {
onChange(getDefaultTeamCondition());
} else {
onChange(getDefaultAttributeCondition());
}
};
return (
{/* Condition type selector */}
{/* Render team or attribute specific fields */}
{isTeamCondition(condition) ? (
) : (
)}
);
};
interface TeamConditionFieldsProps {
condition: ITeamCondition;
onChange: (condition: TAttributeSyncRuleCondition) => void;
teamOptions: { value: string; label: string }[];
isLoading?: boolean;
}
const TeamConditionFields = ({ condition, onChange, teamOptions, isLoading }: TeamConditionFieldsProps) => {
const { t } = useLocale();
const teamOperatorOptions = getTeamOperatorOptions(t);
const operatorOption = teamOperatorOptions.find(
(opt: { value: ConditionOperatorEnum; label: string }) => opt.value === condition.operator
);
const isMulti = isArrayOperator(condition.operator);
const selectedTeams = isMulti
? teamOptions.filter((opt) => condition.value.includes(Number(opt.value)))
: teamOptions.find((opt) => Number(opt.value) === condition.value[0]);
const handleOperatorChange = (newOperator: { value: ConditionOperatorEnum; label: string } | null) => {
if (!newOperator) return;
const formattedValue = formatConditionValue(newOperator.value, condition.value) as number[];
onChange({
...condition,
operator: newOperator.value,
value: formattedValue,
});
};
const handleTeamsChange = (
selected: MultiValue<{ value: string; label: string }> | SingleValue<{ value: string; label: string }>
) => {
if (!selected) {
onChange({ ...condition, value: [] });
return;
}
if (Array.isArray(selected)) {
onChange({
...condition,
value: selected.map((s) => Number(s.value)),
});
} else {
const singleValue = selected as { value: string; label: string };
onChange({
...condition,
value: [Number(singleValue.value)],
});
}
};
return (
<>
{t("team").toLowerCase()}
>
);
};
interface AttributeConditionFieldsProps {
condition: IAttributeCondition;
onChange: (condition: TAttributeSyncRuleCondition) => void;
attributes: Attribute[];
isLoading?: boolean;
}
const AttributeConditionFields = ({
condition,
onChange,
attributes,
isLoading,
}: AttributeConditionFieldsProps) => {
const { t } = useLocale();
const selectedAttribute = attributes.find((attr) => attr.id === condition.attributeId);
const attributeOptions = attributes.map((attr) => ({
value: attr.id,
label: attr.name,
}));
const operatorOptions = selectedAttribute
? getOperatorOptionsForAttributeType(selectedAttribute.type, t)
: [];
const operatorOption = operatorOptions.find(
(opt: { value: ConditionOperatorEnum; label: string }) => opt.value === condition.operator
);
const handleAttributeChange = (selected: { value: string; label: string } | null) => {
if (!selected) return;
const newAttribute = attributes.find((attr) => attr.id === selected.value);
if (!newAttribute) return;
// Reset operator and value when attribute changes
const defaultOperator = getOperatorOptionsForAttributeType(newAttribute.type, t)[0];
onChange({
...condition,
attributeId: selected.value,
operator: defaultOperator.value,
value: [],
});
};
const handleOperatorChange = (newOperator: { value: ConditionOperatorEnum; label: string } | null) => {
if (!newOperator) return;
const formattedValue = formatConditionValue(newOperator.value, condition.value) as string[];
onChange({
...condition,
operator: newOperator.value,
value: formattedValue,
});
};
const handleValueChange = (
selected: MultiValue<{ value: string; label: string }> | SingleValue<{ value: string; label: string }>
) => {
if (!selected) {
onChange({ ...condition, value: [] });
return;
}
if (Array.isArray(selected)) {
onChange({
...condition,
value: selected.map((s) => s.value),
});
} else {
const singleValue = selected as { value: string; label: string };
onChange({
...condition,
value: [singleValue.value],
});
}
};
const handleTextValueChange = (e: React.ChangeEvent) => {
onChange({
...condition,
value: [e.target.value],
});
};
const handleNumberValueChange = (e: React.ChangeEvent) => {
onChange({
...condition,
value: [e.target.value],
});
};
// Render value input based on attribute type
const renderValueInput = () => {
if (!selectedAttribute) {
return (
{t("attribute_sync_select_attribute_first")}
);
}
const isMulti = isArrayOperator(condition.operator);
switch (selectedAttribute.type) {
case "SINGLE_SELECT":
case "MULTI_SELECT": {
const valueOptions = selectedAttribute.options.map((opt: { id: string; value: string }) => ({
value: opt.id,
label: opt.value,
}));
const selectedValues = isMulti
? valueOptions.filter((opt: { value: string; label: string }) =>
condition.value.includes(opt.value)
)
: valueOptions.find((opt: { value: string; label: string }) => condition.value[0] === opt.value);
return (
);
}
case "TEXT":
return (
);
case "NUMBER":
return (
);
}
};
return (
<>
{t("attribute").toLowerCase()}