From 63e70a54e91a683bc56dea87cec35758851182e4 Mon Sep 17 00:00:00 2001 From: Deon Sanchez <69873175+deon-sanchez@users.noreply.github.com> Date: Tue, 13 May 2025 11:38:40 -0700 Subject: [PATCH] fix: edit flow name settings not to be empty (#8020) * Revert "feat: enhance flow settings with minimum length validation and default naming (#7970)" This reverts commit 116bee825c043a8c11bcc86eb283950544c90537. * feat: enhance flow settings with minimum length validation and default naming (#7970) * feat: enhance flow settings with minimum length validation and default naming * fix: remove console log from getFlowToAddToCanvas function * test: add delay before filling flow name input to ensure stability --------- Co-authored-by: cristhianzl --- src/backend/base/langflow/api/v1/flows.py | 4 +++ .../components/FlowMenu/index.tsx | 11 +++--- .../core/editFlowSettingsComponent/index.tsx | 35 +++++++++++++++++-- .../src/modals/flowSettingsModal/index.tsx | 12 ++++--- src/frontend/src/pages/FlowPage/index.tsx | 1 - src/frontend/src/types/components/index.ts | 1 + .../extended/features/edit-flow-name.spec.ts | 1 + 7 files changed, 52 insertions(+), 13 deletions(-) diff --git a/src/backend/base/langflow/api/v1/flows.py b/src/backend/base/langflow/api/v1/flows.py index 7797ad65b..531ae888d 100644 --- a/src/backend/base/langflow/api/v1/flows.py +++ b/src/backend/base/langflow/api/v1/flows.py @@ -322,6 +322,10 @@ async def update_flow( update_data = flow.model_dump(exclude_unset=True, exclude_none=True) + # Specifically handle endpoint_name when it's explicitly set to null or empty string + if flow.endpoint_name is None or flow.endpoint_name == "": + update_data["endpoint_name"] = None + if settings_service.settings.remove_api_keys: update_data = remove_api_keys(update_data) diff --git a/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx b/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx index 4701e5be3..4a915a025 100644 --- a/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx +++ b/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx @@ -216,8 +216,8 @@ export const MenuBar = memo((): JSX.Element => { ]); useEffect(() => { - if (currentFlowName && !editingName) { - setFlowName(currentFlowName); + if (!editingName) { + setFlowName(currentFlowName ?? "Untitled Flow"); } }, [currentFlowName, editingName]); @@ -233,7 +233,7 @@ export const MenuBar = memo((): JSX.Element => { : getNumberFromString(currentFlowGradient ?? currentFlowId ?? "")) % swatchColors.length; - return currentFlowName && onFlowPage ? ( + return onFlowPage ? (
{ onKeyDown={handleKeyDown} onFocus={() => { setEditingName(true); - setFlowName(currentFlowName); + setFlowName(currentFlowName ?? "Untitled Flow"); const flows = useFlowsManagerStore.getState().flows; setFlowNames( flows @@ -315,6 +315,7 @@ export const MenuBar = memo((): JSX.Element => { value={flowName} id="input-flow-name" data-testid="input-flow-name" + placeholder="Untitled Flow" /> { aria-hidden="true" data-testid="flow_name" > - {flowName} + {flowName || "Untitled Flow"}
diff --git a/src/frontend/src/components/core/editFlowSettingsComponent/index.tsx b/src/frontend/src/components/core/editFlowSettingsComponent/index.tsx index 2a89d28ec..b2009d739 100644 --- a/src/frontend/src/components/core/editFlowSettingsComponent/index.tsx +++ b/src/frontend/src/components/core/editFlowSettingsComponent/index.tsx @@ -11,11 +11,13 @@ export const EditFlowSettings: React.FC = ({ description, endpointName, maxLength = 50, + minLength = 1, setName, setDescription, setEndpointName, }: InputProps): JSX.Element => { const [isMaxLength, setIsMaxLength] = useState(false); + const [isMinLength, setIsMinLength] = useState(false); const [validEndpointName, setValidEndpointName] = useState(true); const [isInvalidName, setIsInvalidName] = useState(false); @@ -26,6 +28,11 @@ export const EditFlowSettings: React.FC = ({ } else { setIsMaxLength(false); } + if (value.length < minLength) { + setIsMinLength(true); + } else { + setIsMinLength(false); + } let invalid = false; for (let i = 0; i < invalidNameList!.length; i++) { if (value === invalidNameList![i]) { @@ -35,7 +42,15 @@ export const EditFlowSettings: React.FC = ({ invalid = false; } setIsInvalidName(invalid); - setName!(value); + + // Only update the name if it's valid (not empty and not invalid) + if (value.length >= minLength && !invalid) { + setName!(value); + } else if (value.length === 0) { + // For empty string, update state but keep isMinLength true + setName!(""); + setIsMinLength(true); + } }; const handleDescriptionChange = (event: ChangeEvent) => { @@ -43,11 +58,19 @@ export const EditFlowSettings: React.FC = ({ }; const handleEndpointNameChange = (event: ChangeEvent) => { + const { value } = event.target; // Validate the endpoint name // use this regex r'^[a-zA-Z0-9_-]+$' const isValid = isEndpointNameValid(event.target.value, maxLength); setValidEndpointName(isValid); - setEndpointName!(event.target.value); + + // Only update if valid and meets minimum length (if set) + if (isValid && value.length >= minLength) { + setEndpointName!(value); + } else if (value.length === 0) { + // Always allow empty endpoint name (it's optional) + setEndpointName!(""); + } }; //this function is necessary to select the text when double clicking, this was not working with the onFocus event @@ -61,6 +84,11 @@ export const EditFlowSettings: React.FC = ({ {isMaxLength && ( Character limit reached )} + {isMinLength && ( + + Minimum {minLength} character(s) required + + )} {isInvalidName && ( Name invalid or already exists @@ -77,6 +105,8 @@ export const EditFlowSettings: React.FC = ({ placeholder="Flow name" id="name" maxLength={maxLength} + minLength={minLength} + required={true} onDoubleClickCapture={(event) => { handleFocus(event); }} @@ -137,6 +167,7 @@ export const EditFlowSettings: React.FC = ({ value={endpointName ?? ""} placeholder="An alternative name to run the endpoint" maxLength={maxLength} + minLength={minLength} id="endpoint_name" onDoubleClickCapture={(event) => { handleFocus(event); diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx index d88096f6d..44f4fb242 100644 --- a/src/frontend/src/modals/flowSettingsModal/index.tsx +++ b/src/frontend/src/modals/flowSettingsModal/index.tsx @@ -28,17 +28,19 @@ export default function FlowSettingsModal({ const setSuccessData = useAlertStore((state) => state.setSuccessData); const flows = useFlowsManagerStore((state) => state.flows); const flow = flowData ?? currentFlow; - useEffect(() => { - setName(flow?.name ?? ""); - setDescription(flow?.description ?? ""); - }, [flow?.name, flow?.description, open]); - const [name, setName] = useState(flow?.name ?? ""); const [description, setDescription] = useState(flow?.description ?? ""); const [endpoint_name, setEndpointName] = useState(flow?.endpoint_name ?? ""); const [isSaving, setIsSaving] = useState(false); const [disableSave, setDisableSave] = useState(true); const autoSaving = useFlowsManagerStore((state) => state.autoSaving); + + useEffect(() => { + setName(flow?.name ?? ""); + setDescription(flow?.description ?? ""); + setEndpointName(flow?.endpoint_name ?? ""); + }, [flow?.name, flow?.description, flow?.endpoint_name, open]); + function handleClick(): void { setIsSaving(true); if (!flow) return; diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx index 4062f4c25..87de565e7 100644 --- a/src/frontend/src/pages/FlowPage/index.tsx +++ b/src/frontend/src/pages/FlowPage/index.tsx @@ -149,7 +149,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element { const getFlowToAddToCanvas = async (id: string) => { const flow = await getFlow({ id: id }); - console.log(flow); setCurrentFlow(flow); }; diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index d765b8c13..9d999a66f 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -303,6 +303,7 @@ export type InputProps = { description: string | null; endpointName?: string | null; maxLength?: number; + minLength?: number; setName?: (name: string) => void; setDescription?: (description: string) => void; setEndpointName?: (endpointName: string) => void; diff --git a/src/frontend/tests/extended/features/edit-flow-name.spec.ts b/src/frontend/tests/extended/features/edit-flow-name.spec.ts index 1324c7778..1c3ec59d9 100644 --- a/src/frontend/tests/extended/features/edit-flow-name.spec.ts +++ b/src/frontend/tests/extended/features/edit-flow-name.spec.ts @@ -14,6 +14,7 @@ test( await page.getByRole("heading", { name: "Basic Prompting" }).click(); await page.getByTestId("input-flow-name").click(); + await page.waitForTimeout(1000); await page.getByTestId("input-flow-name").fill(randomName);