Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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>,
): PartialCustomOptionInputFields[] {
return options.map((option) => (
option.value === SINGLE_FEATURE_VALUE
? { ...option, ...overrides }
: option
));
}

interface Props {
projectId: string;
value: PartialLocateObjectSpecificFields | undefined | null;
error: LeafError | ObjectError<PartialLocateObjectSpecificFields>;
setFieldValue: (...entries: EntriesAsList<PartialLocateObjectSpecificFields>) => void;
defaultMultipleFeaturesOption: DefaultCustomOption | undefined;
defaultSingleFeatureOption: DefaultCustomOption | undefined;
disabled?: boolean;
}

Expand All @@ -64,6 +89,7 @@ function LocateObjectProjectSpecifics(props: Props) {
error: formError,
setFieldValue,
defaultMultipleFeaturesOption,
defaultSingleFeatureOption,
disabled,
} = props;

Expand All @@ -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(
Expand All @@ -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,
Expand All @@ -109,7 +150,7 @@ function LocateObjectProjectSpecifics(props: Props) {
},
'customOptions' as const,
);
}, [setFieldValue, defaultMultipleFeaturesOption]);
}, [setFieldValue, defaultMultipleFeaturesOption, defaultSingleFeatureOption]);

return (
<>
Expand Down Expand Up @@ -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)}
/>
<CustomOptionPreview
variant="tile"
Expand Down
4 changes: 4 additions & 0 deletions app/views/EditProject/UpdateProjectForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,10 @@ function UpdateProjectForm(props: Props) {
.defaultLocateObjectCustomOptions.find(
(option) => option.value === 2,
)}
defaultSingleFeatureOption={projectData
.defaultLocateObjectCustomOptions.find(
(option) => option.value === 1,
)}
disabled={projectTypeSpecificInputsDisabled || readOnly}
/>
)}
Expand Down
Loading