diff --git a/apiserver/plane/ee/views/app/issue_property/base.py b/apiserver/plane/ee/views/app/issue_property/base.py index e02962e650..31ee646c9c 100644 --- a/apiserver/plane/ee/views/app/issue_property/base.py +++ b/apiserver/plane/ee/views/app/issue_property/base.py @@ -198,6 +198,14 @@ class IssuePropertyEndpoint(BaseAPIView): # Reset the default options if the property is required if issue_property.is_required: self.reset_options_default(issue_property) + # Reset the default options if property is not multi and more than one default value + if not issue_property.is_multi and IssuePropertyOption.objects.filter( + property_id=issue_property.id, + workspace_id=issue_property.workspace_id, + project_id=issue_property.project_id, + is_default=True, + ).count() > 1: + self.reset_options_default(issue_property) self.update_property_default_options(issue_property) except IntegrityError: @@ -297,6 +305,14 @@ class IssuePropertyEndpoint(BaseAPIView): # Reset the default options if the property is required if issue_property.is_required: self.reset_options_default(issue_property) + # Reset the default options if property is not multi and more than one default value + if not issue_property.is_multi and IssuePropertyOption.objects.filter( + property_id=issue_property.id, + workspace_id=issue_property.workspace_id, + project_id=issue_property.project_id, + is_default=True, + ).count() > 1: + self.reset_options_default(issue_property) self.update_property_default_options(issue_property) except IntegrityError: diff --git a/web/ee/components/issue-types/properties/attributes/dropdown.tsx b/web/ee/components/issue-types/properties/attributes/dropdown.tsx index 7971f8baa7..7150888af2 100644 --- a/web/ee/components/issue-types/properties/attributes/dropdown.tsx +++ b/web/ee/components/issue-types/properties/attributes/dropdown.tsx @@ -30,23 +30,26 @@ export const DropdownAttributes = observer((props: TDropdownAttributesProps) => const { issueTypeId, dropdownPropertyDetail, currentOperationMode, onDropdownDetailChange, error } = props; // store hooks const issueType = useIssueType(issueTypeId); - const { setPropertyOptions } = usePropertyOptions(); + const { propertyOptions, setPropertyOptions } = usePropertyOptions(); // derived values const isAnyIssueAttached = issueType?.issue_exists; const isOptionDefaultDisabled = dropdownPropertyDetail.is_multi === undefined || !!dropdownPropertyDetail.is_required; // helpers const resetToSingleSelectDefault = () => { - const firstDefaultOption = dropdownPropertyDetail.default_value?.[0]; + const firstDefaultOption = propertyOptions?.find((option) => option.is_default); + const firstDefaultOptionIdentifier = firstDefaultOption?.id ?? firstDefaultOption?.key; // Update property options setPropertyOptions((prevOptions) => { if (!prevOptions) return []; return prevOptions.map((option) => ({ ...option, - is_default: option.id === firstDefaultOption, + is_default: + !!firstDefaultOptionIdentifier && + (option.id === firstDefaultOptionIdentifier || option.key === firstDefaultOptionIdentifier), })); }); // Update default value - const newDefaultValue = firstDefaultOption ? [firstDefaultOption] : []; + const newDefaultValue = firstDefaultOptionIdentifier ? [firstDefaultOptionIdentifier] : []; onDropdownDetailChange("default_value", newDefaultValue); }; diff --git a/web/ee/components/issue-types/properties/attributes/options/create-option-item.tsx b/web/ee/components/issue-types/properties/attributes/options/create-option-item.tsx index 7f63f6d7cf..42cdf70a62 100644 --- a/web/ee/components/issue-types/properties/attributes/options/create-option-item.tsx +++ b/web/ee/components/issue-types/properties/attributes/options/create-option-item.tsx @@ -8,6 +8,7 @@ import { TIssuePropertyOptionCreateUpdateData } from "@/plane-web/types"; type TIssuePropertyCreateOptionItem = { propertyOptionCreateListData: TIssuePropertyOptionCreateUpdateData; updateCreateListData: (value: TIssuePropertyOptionCreateUpdateData) => void; + scrollIntoNewOptionView: () => void; error?: string; }; @@ -16,13 +17,14 @@ export const IssuePropertyCreateOptionItem = observer( props: TIssuePropertyCreateOptionItem, ref: React.Ref ) { - const { propertyOptionCreateListData, updateCreateListData, error } = props; + const { propertyOptionCreateListData, updateCreateListData, scrollIntoNewOptionView, error } = props; return (
diff --git a/web/ee/components/issue-types/properties/attributes/options/option.tsx b/web/ee/components/issue-types/properties/attributes/options/option.tsx index 52f7473ef6..b1f999b036 100644 --- a/web/ee/components/issue-types/properties/attributes/options/option.tsx +++ b/web/ee/components/issue-types/properties/attributes/options/option.tsx @@ -15,11 +15,12 @@ type TIssuePropertyOptionItem = { optionId?: string; propertyOptionData: TIssuePropertyOptionCreateUpdateData; updateOptionData: (value: TIssuePropertyOptionCreateUpdateData) => void; + scrollIntoNewOptionView: () => void; error?: string; }; export const IssuePropertyOptionItem: FC = observer((props) => { - const { optionId, propertyOptionData, updateOptionData, error: optionsError } = props; + const { optionId, propertyOptionData, updateOptionData, scrollIntoNewOptionView, error: optionsError } = props; // store hooks const { propertyOptions } = usePropertyOptions(); // derived values @@ -71,7 +72,12 @@ export const IssuePropertyOptionItem: FC = observer((p id={`option-${optionId}-${key}`} value={optionData.name} onChange={(e) => handleOptionDataChange("name", e.target.value)} - onKeyDown={(e) => e.key === "Enter" && !!optionData.name && e.currentTarget.blur()} + onKeyDown={(e) => { + if (["Enter", "Tab"].includes(e.key) && !!optionData.name) { + e.currentTarget.blur(); + scrollIntoNewOptionView(); + } + }} onBlur={() => handleCreateUpdate()} placeholder={"Add option"} className={cn("w-full text-sm bg-custom-background-100 border-[0.5px] rounded", { diff --git a/web/ee/components/issue-types/properties/attributes/options/root.tsx b/web/ee/components/issue-types/properties/attributes/options/root.tsx index f8f927cff7..e99937faac 100644 --- a/web/ee/components/issue-types/properties/attributes/options/root.tsx +++ b/web/ee/components/issue-types/properties/attributes/options/root.tsx @@ -92,6 +92,11 @@ export const IssuePropertyOptionsRoot: FC = observer( updateOptionData={(value) => { handlePropertyOptionsList("update", value); }} + scrollIntoNewOptionView={() => { + setTimeout(() => { + scrollIntoElementView(); + }, 0); + }} /> )} @@ -117,6 +122,8 @@ export const IssuePropertyOptionsRoot: FC = observer( propertyOptionCreateListData={issuePropertyOption} updateCreateListData={(value) => { handlePropertyOptionsList("update", value); + }} + scrollIntoNewOptionView={() => { setTimeout(() => { scrollIntoElementView(); }, 0); diff --git a/web/ee/components/issue-types/properties/attributes/text.tsx b/web/ee/components/issue-types/properties/attributes/text.tsx index fce7eb1f4e..cbd0a17bc1 100644 --- a/web/ee/components/issue-types/properties/attributes/text.tsx +++ b/web/ee/components/issue-types/properties/attributes/text.tsx @@ -1,3 +1,4 @@ +import { useEffect, useState } from "react"; import { observer } from "mobx-react"; // ui import { TextArea } from "@plane/ui"; @@ -23,11 +24,23 @@ type TTextAttributesProps = { export const TextAttributes = observer((props: TTextAttributesProps) => { const { issueTypeId, textPropertyDetail, currentOperationMode, onTextDetailChange } = props; + const [data, setData] = useState([]); // store hooks const issueType = useIssueType(issueTypeId); // derived values const isAnyIssueAttached = issueType?.issue_exists; + useEffect(() => { + setData(textPropertyDetail.default_value ?? []); + }, [textPropertyDetail.default_value]); + + const handleReadOnlyFieldChange = () => { + // trim and filter empty values + const trimmedValue = data.map((val) => val.trim()).filter((val) => val); + // update readonly data + onTextDetailChange("default_value", trimmedValue); + }; + return ( <>
@@ -52,8 +65,10 @@ export const TextAttributes = observer((props: TTextAttributesProps) => {
Read only data