From 38e9d231bcd939230f584ec49ac9df5b5acf9e2a Mon Sep 17 00:00:00 2001 From: Thomas Trompette Date: Tue, 28 Jul 2026 13:33:12 +0200 Subject: [PATCH] fix: hide add-select-option prompt for users without data model permission (#23410) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #23341 ## Problem A user whose role lacks the `DATA_MODEL` permission flag still saw the `Add "…" to options` prompt when typing a value that matched no option in a multiselect. Clicking it did nothing. `MultiSelectInput` renders `AddSelectOptionMenuItem` based purely on whether the callback exists: ```tsx {onAddSelectOption && searchFilter && filteredOptionsInDropDown.length === 0 && ( ``` `MultiSelectFieldInput` always passed a callback, and did the permission check *inside* it: ```tsx const handleAddSelectOption = (optionName: string) => { if (!canAddSelectOption) { return; } addSelectOption(optionName); }; ``` So the guard suppressed the click but not the render, which is exactly the reported symptom: the prompt is visible and inert. ## Fix Gate at the prop instead of inside the handler, so the menu item is never rendered when the action is unavailable: ```tsx onAddSelectOption={canAddSelectOption ? addSelectOption : undefined} ``` The wrapper is now redundant and removed; `addSelectOption` already has the matching `(optionName: string) => void` signature. `SelectFieldInput` had the byte-identical bug (`SelectInput` gates on `onAddSelectOption &&` the same way), so it gets the same change. ## Note on scope `useCanAddSelectOption` requires `objectNamePlural` from the route in addition to the permission flag: ```ts const canAddSelectOption = userHasPermissionToEditDataModel && isNonEmptyString(fieldName) && isNonEmptyString(objectNamePlural); ``` Record *detail* pages (`/object/:objectNameSingular/:recordId`) have no `objectNamePlural`, so the prompt was dead there for **every** user, admins included. This change hides it in that case too, which is the correct behavior since the click could never have worked. ## Testing Verified manually against a local instance, toggling the patch in and out on the same cell so before/after is directly comparable. Company `Work Policy` (multiselect) in the Companies table view, typing a string that matches no option: | user | route | before | after | |---|---|---|---| | Member (no `DATA_MODEL`) | `/objects/companies` | prompt shown, click does nothing | prompt hidden | | Admin | `/objects/companies` | prompt shown, click works | prompt shown, click works (navigates to `/settings/objects/companies/workPolicy?newOption=…`) | | Admin | `/object/company/:id` | prompt shown, click does nothing | prompt hidden | `nx lint:diff-with-main twenty-front` and `nx typecheck twenty-front` both pass. Review in cubic --- .../input/components/MultiSelectFieldInput.tsx | 9 +-------- .../ui/meta-types/input/components/SelectFieldInput.tsx | 9 +-------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiSelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiSelectFieldInput.tsx index c9aefa38fa..2ebd83940a 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiSelectFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/MultiSelectFieldInput.tsx @@ -41,13 +41,6 @@ export const MultiSelectFieldInput = () => { onSubmit?.({ newValue: draftValue }); }; - const handleAddSelectOption = (optionName: string) => { - if (!canAddSelectOption) { - return; - } - addSelectOption(optionName); - }; - return ( { onCancel={handleCancel} onOptionSelected={handleOptionSelected} values={draftValue} - onAddSelectOption={handleAddSelectOption} + onAddSelectOption={canAddSelectOption ? addSelectOption : undefined} /> ); }; diff --git a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/SelectFieldInput.tsx b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/SelectFieldInput.tsx index 8d512599d1..1571a8a104 100644 --- a/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/SelectFieldInput.tsx +++ b/packages/twenty-front/src/modules/object-record/record-field/ui/meta-types/input/components/SelectFieldInput.tsx @@ -55,13 +55,6 @@ export const SelectFieldInput = () => { onCancel?.(); }; - const handleAddSelectOption = (optionName: string) => { - if (!canAddSelectOption) { - return; - } - addSelectOption(optionName); - }; - const handleSubmit = (option: SelectOption) => { onSubmit?.({ newValue: option.value }); @@ -110,7 +103,7 @@ export const SelectFieldInput = () => { : undefined } clearLabel={fieldDefinition.label} - onAddSelectOption={handleAddSelectOption} + onAddSelectOption={canAddSelectOption ? addSelectOption : undefined} /> ); };