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 116bee825c.

* 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 <cristhian.lousa@gmail.com>
This commit is contained in:
Deon Sanchez 2025-05-13 11:38:40 -07:00 • committed by GitHub
commit 63e70a54e9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 52 additions and 13 deletions

View file

@ -322,6 +322,10 @@ async def update_flow(
update_data = flow.model_dump(exclude_unset=True, exclude_none=True) 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: if settings_service.settings.remove_api_keys:
update_data = remove_api_keys(update_data) update_data = remove_api_keys(update_data)

View file

@ -216,8 +216,8 @@ export const MenuBar = memo((): JSX.Element => {
]); ]);
useEffect(() => { useEffect(() => {
if (currentFlowName && !editingName) { if (!editingName) {
setFlowName(currentFlowName); setFlowName(currentFlowName ?? "Untitled Flow");
} }
}, [currentFlowName, editingName]); }, [currentFlowName, editingName]);
@ -233,7 +233,7 @@ export const MenuBar = memo((): JSX.Element => {
: getNumberFromString(currentFlowGradient ?? currentFlowId ?? "")) % : getNumberFromString(currentFlowGradient ?? currentFlowId ?? "")) %
swatchColors.length; swatchColors.length;
return currentFlowName && onFlowPage ? ( return onFlowPage ? (
<div <div
className="flex w-full items-center justify-center gap-2" className="flex w-full items-center justify-center gap-2"
data-testid="menu_bar_wrapper" data-testid="menu_bar_wrapper"
@ -303,7 +303,7 @@ export const MenuBar = memo((): JSX.Element => {
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
onFocus={() => { onFocus={() => {
setEditingName(true); setEditingName(true);
setFlowName(currentFlowName); setFlowName(currentFlowName ?? "Untitled Flow");
const flows = useFlowsManagerStore.getState().flows; const flows = useFlowsManagerStore.getState().flows;
setFlowNames( setFlowNames(
flows flows
@ -315,6 +315,7 @@ export const MenuBar = memo((): JSX.Element => {
value={flowName} value={flowName}
id="input-flow-name" id="input-flow-name"
data-testid="input-flow-name" data-testid="input-flow-name"
placeholder="Untitled Flow"
/> />
<span <span
ref={measureRef} ref={measureRef}
@ -322,7 +323,7 @@ export const MenuBar = memo((): JSX.Element => {
aria-hidden="true" aria-hidden="true"
data-testid="flow_name" data-testid="flow_name"
> >
{flowName} {flowName || "Untitled Flow"}
</span> </span>
</div> </div>
</div> </div>

View file

@ -11,11 +11,13 @@ export const EditFlowSettings: React.FC<InputProps> = ({
description, description,
endpointName, endpointName,
maxLength = 50, maxLength = 50,
minLength = 1,
setName, setName,
setDescription, setDescription,
setEndpointName, setEndpointName,
}: InputProps): JSX.Element => { }: InputProps): JSX.Element => {
const [isMaxLength, setIsMaxLength] = useState(false); const [isMaxLength, setIsMaxLength] = useState(false);
const [isMinLength, setIsMinLength] = useState(false);
const [validEndpointName, setValidEndpointName] = useState(true); const [validEndpointName, setValidEndpointName] = useState(true);
const [isInvalidName, setIsInvalidName] = useState(false); const [isInvalidName, setIsInvalidName] = useState(false);
@ -26,6 +28,11 @@ export const EditFlowSettings: React.FC<InputProps> = ({
} else { } else {
setIsMaxLength(false); setIsMaxLength(false);
} }
if (value.length < minLength) {
setIsMinLength(true);
} else {
setIsMinLength(false);
}
let invalid = false; let invalid = false;
for (let i = 0; i < invalidNameList!.length; i++) { for (let i = 0; i < invalidNameList!.length; i++) {
if (value === invalidNameList![i]) { if (value === invalidNameList![i]) {
@ -35,7 +42,15 @@ export const EditFlowSettings: React.FC<InputProps> = ({
invalid = false; invalid = false;
} }
setIsInvalidName(invalid); 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<HTMLTextAreaElement>) => { const handleDescriptionChange = (event: ChangeEvent<HTMLTextAreaElement>) => {
@ -43,11 +58,19 @@ export const EditFlowSettings: React.FC<InputProps> = ({
}; };
const handleEndpointNameChange = (event: ChangeEvent<HTMLInputElement>) => { const handleEndpointNameChange = (event: ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
// Validate the endpoint name // Validate the endpoint name
// use this regex r'^[a-zA-Z0-9_-]+$' // use this regex r'^[a-zA-Z0-9_-]+$'
const isValid = isEndpointNameValid(event.target.value, maxLength); const isValid = isEndpointNameValid(event.target.value, maxLength);
setValidEndpointName(isValid); 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 //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<InputProps> = ({
{isMaxLength && ( {isMaxLength && (
<span className="edit-flow-span">Character limit reached</span> <span className="edit-flow-span">Character limit reached</span>
)} )}
{isMinLength && (
<span className="edit-flow-span">
Minimum {minLength} character(s) required
</span>
)}
{isInvalidName && ( {isInvalidName && (
<span className="edit-flow-span"> <span className="edit-flow-span">
Name invalid or already exists Name invalid or already exists
@ -77,6 +105,8 @@ export const EditFlowSettings: React.FC<InputProps> = ({
placeholder="Flow name" placeholder="Flow name"
id="name" id="name"
maxLength={maxLength} maxLength={maxLength}
minLength={minLength}
required={true}
onDoubleClickCapture={(event) => { onDoubleClickCapture={(event) => {
handleFocus(event); handleFocus(event);
}} }}
@ -137,6 +167,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
value={endpointName ?? ""} value={endpointName ?? ""}
placeholder="An alternative name to run the endpoint" placeholder="An alternative name to run the endpoint"
maxLength={maxLength} maxLength={maxLength}
minLength={minLength}
id="endpoint_name" id="endpoint_name"
onDoubleClickCapture={(event) => { onDoubleClickCapture={(event) => {
handleFocus(event); handleFocus(event);

View file

@ -28,17 +28,19 @@ export default function FlowSettingsModal({
const setSuccessData = useAlertStore((state) => state.setSuccessData); const setSuccessData = useAlertStore((state) => state.setSuccessData);
const flows = useFlowsManagerStore((state) => state.flows); const flows = useFlowsManagerStore((state) => state.flows);
const flow = flowData ?? currentFlow; const flow = flowData ?? currentFlow;
useEffect(() => {
setName(flow?.name ?? "");
setDescription(flow?.description ?? "");
}, [flow?.name, flow?.description, open]);
const [name, setName] = useState(flow?.name ?? ""); const [name, setName] = useState(flow?.name ?? "");
const [description, setDescription] = useState(flow?.description ?? ""); const [description, setDescription] = useState(flow?.description ?? "");
const [endpoint_name, setEndpointName] = useState(flow?.endpoint_name ?? ""); const [endpoint_name, setEndpointName] = useState(flow?.endpoint_name ?? "");
const [isSaving, setIsSaving] = useState(false); const [isSaving, setIsSaving] = useState(false);
const [disableSave, setDisableSave] = useState(true); const [disableSave, setDisableSave] = useState(true);
const autoSaving = useFlowsManagerStore((state) => state.autoSaving); 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 { function handleClick(): void {
setIsSaving(true); setIsSaving(true);
if (!flow) return; if (!flow) return;

View file

@ -149,7 +149,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element {
const getFlowToAddToCanvas = async (id: string) => { const getFlowToAddToCanvas = async (id: string) => {
const flow = await getFlow({ id: id }); const flow = await getFlow({ id: id });
console.log(flow);
setCurrentFlow(flow); setCurrentFlow(flow);
}; };

View file

@ -303,6 +303,7 @@ export type InputProps = {
description: string | null; description: string | null;
endpointName?: string | null; endpointName?: string | null;
maxLength?: number; maxLength?: number;
minLength?: number;
setName?: (name: string) => void; setName?: (name: string) => void;
setDescription?: (description: string) => void; setDescription?: (description: string) => void;
setEndpointName?: (endpointName: string) => void; setEndpointName?: (endpointName: string) => void;

View file

@ -14,6 +14,7 @@ test(
await page.getByRole("heading", { name: "Basic Prompting" }).click(); await page.getByRole("heading", { name: "Basic Prompting" }).click();
await page.getByTestId("input-flow-name").click(); await page.getByTestId("input-flow-name").click();
await page.waitForTimeout(1000);
await page.getByTestId("input-flow-name").fill(randomName); await page.getByTestId("input-flow-name").fill(randomName);