import { useEffect, useState } from "react"; import { useForm, Controller } from "react-hook-form"; import { Dialog } from "@calcom/features/components/controlled-dialog"; 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 { FormCard, FormCardBody } from "@calcom/ui/components/card"; import { ConfirmationDialogContent } from "@calcom/ui/components/dialog"; import { SelectField, Switch } from "@calcom/ui/components/form"; import { KeyIcon } from "@coss/ui/icons"; import { type IntegrationAttributeSync, type ISyncFormData, RuleOperatorEnum, } from "@calcom/features/ee/integration-attribute-sync/repositories/IIntegrationAttributeSyncRepository"; import { FieldMappingBuilder } from "./FieldMappingBuilder"; import { RuleBuilder } from "./RuleBuilder"; export interface IIntegrationAttributeSyncCardProps { sync?: IntegrationAttributeSync; // Optional for create mode credentialOptions: { value: string; label: string; }[]; teamOptions: { value: string; label: string; }[]; attributes: Attribute[]; attributeOptions: { value: string; label: string; }[]; organizationId: number; onSubmit: (data: ISyncFormData) => void; isSubmitting: boolean; onCancel?: () => void; onDelete?: () => void; } const IntegrationAttributeSyncCard = (props: IIntegrationAttributeSyncCardProps) => { const { sync, credentialOptions, teamOptions, attributes, attributeOptions, organizationId, onSubmit, onCancel, onDelete, isSubmitting, } = props; const { t } = useLocale(); const isCreateMode = !sync; const [showDeleteDialog, setShowDeleteDialog] = useState(false); // Initialize form with sync data or defaults const form = useForm({ defaultValues: sync ? { id: sync.id, name: sync.name, credentialId: sync.credentialId ?? undefined, enabled: sync.enabled, organizationId: sync.organizationId, ruleId: sync.attributeSyncRule?.id || "", rule: sync.attributeSyncRule?.rule || { operator: RuleOperatorEnum.AND, conditions: [], }, syncFieldMappings: Array.isArray(sync.syncFieldMappings) ? sync.syncFieldMappings : [], } : { id: "", name: t("attribute_sync_new_integration_sync"), credentialId: undefined, enabled: true, organizationId, ruleId: "", rule: { operator: RuleOperatorEnum.AND, conditions: [] }, syncFieldMappings: [], }, }); // Update form when sync prop changes (after refetch with new IDs) useEffect(() => { if (sync) { form.reset({ id: sync.id, name: sync.name, credentialId: sync.credentialId ?? undefined, enabled: sync.enabled, organizationId: sync.organizationId, ruleId: sync.attributeSyncRule?.id || "", rule: sync.attributeSyncRule?.rule || { operator: RuleOperatorEnum.AND, conditions: [], }, syncFieldMappings: Array.isArray(sync.syncFieldMappings) ? sync.syncFieldMappings : [], }); } // Only need to run if the sync changes }, [sync?.id]); const onFormSubmit = (data: ISyncFormData) => { onSubmit(data); }; const handleDelete = () => { if (!sync) return; setShowDeleteDialog(true); }; const confirmDelete = () => { if (!sync || !onDelete) return; onDelete(); setShowDeleteDialog(false); }; const formName = form.watch("name"); const isPending = isSubmitting; return ( <>
{ form.setValue("name", label, { shouldDirty: true }); }} customActions={ ( e.stopPropagation()} /> )} /> } deleteField={ !isCreateMode && onDelete ? { check: () => true, fn: handleDelete, } : undefined }>
{t("attribute_sync_credential")}
{ if (!value || value <= 0) { return t("attribute_sync_credential_validation"); } return true; }, }} render={({ field, fieldState }) => ( <> Number(opt.value) === field.value) || null} onChange={(option) => field.onChange(Number(option?.value))} /> {fieldState.error && (

{fieldState.error.message}

)} )} />

{t("attribute_sync_credential_description")}

( )} />

{t("attribute_sync_user_filter_rules_description")}

{ const seenAttributes = new Set(); for (const mapping of mappings || []) { if (mapping.attributeId && seenAttributes.has(mapping.attributeId)) { return t("attribute_sync_duplicate_attribute_mapping"); } if (mapping.attributeId) seenAttributes.add(mapping.attributeId); } return true; }, }} render={({ field }) => ( field.onChange(fieldMappings.mappings)} attributeOptions={attributeOptions} /> )} />

{t("attribute_sync_field_mappings_description")}

{isCreateMode && onCancel && ( )}
{t("attribute_sync_delete_confirmation")} ); }; export default IntegrationAttributeSyncCard;