diff --git a/src/modules/integration-picker/components/IntegrationFields.tsx b/src/modules/integration-picker/components/IntegrationFields.tsx index 892b727..f06ef2b 100644 --- a/src/modules/integration-picker/components/IntegrationFields.tsx +++ b/src/modules/integration-picker/components/IntegrationFields.tsx @@ -10,6 +10,8 @@ import { FlexJustify, Form, Input, + MultiSelect, + type MultiSelectOption, Padded, Spacer, TextArea, @@ -198,9 +200,70 @@ const FieldRenderer: React.FC = ({ ); } + if (field.type === 'multi-select') { + return ( + + ); + } + return null; }; +/** Multi-select field using Malachite MultiSelect with defaultValue (uncontrolled mode) */ +const MultiSelectField: React.FC<{ + field: ConnectorConfigField; + fieldKey: string; + errors: FieldErrors; + setValue: UseFormSetValue>; +}> = ({ field, fieldKey, errors, setValue }) => { + const delimiter = field.delimiter?.trim() || ' '; + const initialValue = field.value?.toString() || ''; + const defaultValues = initialValue + ? initialValue + .split(delimiter) + .map((v) => v.trim()) + .filter(Boolean) + : []; + + const multiSelectOptions: MultiSelectOption[] = (field.options ?? []).map((opt) => ({ + value: opt.value, + label: opt.label, + })); + + const errorMessage = errors[fieldKey] && ( + + {errors[fieldKey]?.message as string} + + ); + + return ( + <> + { + setValue(fieldKey, selected.join(delimiter), { + shouldValidate: true, + }); + }} + placeholder={field.placeholder || 'Select...'} + size="medium" + error={!!errors[fieldKey]} + name={fieldKey} + label={field.label} + description={field.guide?.description ?? field.description} + tooltip={field.guide?.tooltip ?? field.tooltip} + required={field.required} + disabled={field.readOnly} + chips + chipsMax={4} + inlineSearch + /> + {errorMessage} + + ); +}; + const ErrorBlock = ({ error }: { error?: { message: string; provider_response: string } }) => { if (!error) { return null; diff --git a/src/modules/integration-picker/hooks/useIntegrationPicker.ts b/src/modules/integration-picker/hooks/useIntegrationPicker.ts index ba4411d..e34635e 100644 --- a/src/modules/integration-picker/hooks/useIntegrationPicker.ts +++ b/src/modules/integration-picker/hooks/useIntegrationPicker.ts @@ -547,8 +547,11 @@ export const useIntegrationPicker = ({ }); } + const multiSelectKeys = new Set( + fields.filter((f) => f.type === 'multi-select').map((f) => f.key), + ); Object.keys(cleanedFormData).forEach((key) => { - if (cleanedFormData[key] === '') { + if (cleanedFormData[key] === '' && !multiSelectKeys.has(key)) { delete cleanedFormData[key]; } }); diff --git a/src/modules/integration-picker/types.ts b/src/modules/integration-picker/types.ts index bf7543c..bc0bbd5 100644 --- a/src/modules/integration-picker/types.ts +++ b/src/modules/integration-picker/types.ts @@ -18,7 +18,7 @@ export interface HubData { } export interface ConnectorConfigField { - type?: 'text' | 'password' | 'number' | 'select' | 'text_area'; + type?: 'text' | 'password' | 'number' | 'select' | 'multi-select' | 'text_area'; label: string; key: string; required: boolean; @@ -43,6 +43,8 @@ export interface ConnectorConfigField { error?: string; }; display?: boolean; + /** Delimiter for joining multi-select values. Defaults to space. */ + delimiter?: string; } export interface LegacyConnectorConfig {