From b86fbc5331456fb66d52b99f456ba1670a32be51 Mon Sep 17 00:00:00 2001 From: frozenhelium Date: Thu, 25 Jun 2026 11:26:53 +0545 Subject: [PATCH] feat(locate): rename option label when Multiple Features is disabled Rename "Single Feature" to "Feature exists" when multiple features is disabled --- .../LocateObjectProjectSpecifics/index.tsx | 58 ++++++++++++++++--- .../EditProject/UpdateProjectForm/index.tsx | 4 ++ 2 files changed, 55 insertions(+), 7 deletions(-) diff --git a/app/views/EditProject/UpdateProjectForm/LocateObjectProjectSpecifics/index.tsx b/app/views/EditProject/UpdateProjectForm/LocateObjectProjectSpecifics/index.tsx index b2644d1..20f70f8 100644 --- a/app/views/EditProject/UpdateProjectForm/LocateObjectProjectSpecifics/index.tsx +++ b/app/views/EditProject/UpdateProjectForm/LocateObjectProjectSpecifics/index.tsx @@ -43,17 +43,42 @@ import { type PartialLocateObjectSpecificFields } from './schema'; type DefaultCustomOption = ProjectDetailsQuery['defaultLocateObjectCustomOptions'][number]; +// FIXME(frozenhelium): The custom options for both cases (with and without multiple features) +// should come from the server + // NOTE: In the LOCATE custom options, value 2 corresponds to the "Multiple // Features" option, which managers can optionally exclude to keep only the // "Single Feature" and "No" options. const MULTIPLE_FEATURES_VALUE = 2; +// Value 1 is the "Single Feature" option. Drop "Multiple Features" and the +// single-vs-multiple split stops making sense. The option then just means a +// feature is present, so we show it as "Feature Exists". +// Only these two strings live here. The original label is restored from the +// backend default (see the toggle handler), not duplicated, so they can't +// drift apart. +const SINGLE_FEATURE_VALUE = 1; +const FEATURE_EXISTS_TITLE = 'Feature Exists'; +const FEATURE_EXISTS_DESCRIPTION = 'the shape outlines one or more features in the image'; + +function relabelSingleFeatureOption( + options: PartialCustomOptionInputFields[], + overrides: Partial, +): PartialCustomOptionInputFields[] { + return options.map((option) => ( + option.value === SINGLE_FEATURE_VALUE + ? { ...option, ...overrides } + : option + )); +} + interface Props { projectId: string; value: PartialLocateObjectSpecificFields | undefined | null; error: LeafError | ObjectError; setFieldValue: (...entries: EntriesAsList) => void; defaultMultipleFeaturesOption: DefaultCustomOption | undefined; + defaultSingleFeatureOption: DefaultCustomOption | undefined; disabled?: boolean; } @@ -64,6 +89,7 @@ function LocateObjectProjectSpecifics(props: Props) { error: formError, setFieldValue, defaultMultipleFeaturesOption, + defaultSingleFeatureOption, disabled, } = props; @@ -87,9 +113,13 @@ function LocateObjectProjectSpecifics(props: Props) { const baseOptions = oldOptions ?? []; if (!include) { - return baseOptions.filter( + const withoutMultiple = baseOptions.filter( (option) => option.value !== MULTIPLE_FEATURES_VALUE, ); + return relabelSingleFeatureOption(withoutMultiple, { + title: FEATURE_EXISTS_TITLE, + description: FEATURE_EXISTS_DESCRIPTION, + }); } const alreadyPresent = baseOptions.some( @@ -99,8 +129,19 @@ function LocateObjectProjectSpecifics(props: Props) { return baseOptions; } + // The two options are handled differently here. "Single + // Feature" was never removed, only relabeled, so just put its + // original label back. "Multiple Features" was removed + // entirely, so re-add it below. + const restoredOptions = isDefined(defaultSingleFeatureOption) + ? relabelSingleFeatureOption(baseOptions, { + title: defaultSingleFeatureOption.title, + description: defaultSingleFeatureOption.description, + }) + : baseOptions; + return [ - ...baseOptions, + ...restoredOptions, { clientId: ulid(), ...defaultMultipleFeaturesOption, @@ -109,7 +150,7 @@ function LocateObjectProjectSpecifics(props: Props) { }, 'customOptions' as const, ); - }, [setFieldValue, defaultMultipleFeaturesOption]); + }, [setFieldValue, defaultMultipleFeaturesOption, defaultSingleFeatureOption]); return ( <> @@ -166,10 +207,13 @@ function LocateObjectProjectSpecifics(props: Props) { label="Include 'Multiple Features' option" value={includeMultipleFeatures} onChange={handleMultipleFeaturesToggle} - // NOTE: re-adding the option needs the canonical default from - // the backend; without it the toggle could not function, so - // we disable it rather than let a click silently no-op. - disabled={disabled || isNotDefined(defaultMultipleFeaturesOption)} + // Re-enabling needs both backend defaults: one to re-add + // "Multiple Features", one to restore the "Single Feature" + // label. Missing either, a click would do nothing, so + // disable the checkbox instead. + disabled={disabled + || isNotDefined(defaultMultipleFeaturesOption) + || isNotDefined(defaultSingleFeatureOption)} /> option.value === 2, )} + defaultSingleFeatureOption={projectData + .defaultLocateObjectCustomOptions.find( + (option) => option.value === 1, + )} disabled={projectTypeSpecificInputsDisabled || readOnly} /> )}