Fix flow settings validation and save button (#2360)

This pull request fixes the validation and save button behavior in the
flow settings modal. Previously, the save button was not disabled when
it should have been, and the endpoint name validation was not working
correctly. This PR updates the validation logic and ensures that the
save button is disabled when necessary.
This commit is contained in:
anovazzi1 2024-06-26 18:45:03 +00:00 • committed by GitHub
commit a471c671d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 35 additions and 21 deletions

View file

@ -1,5 +1,5 @@
from datetime import datetime, timezone
import re import re
from datetime import datetime, timezone
from typing import List from typing import List
from uuid import UUID from uuid import UUID
@ -211,8 +211,7 @@ def update_flow(
if settings_service.settings.remove_api_keys: if settings_service.settings.remove_api_keys:
flow_data = remove_api_keys(flow_data) flow_data = remove_api_keys(flow_data)
for key, value in flow_data.items(): for key, value in flow_data.items():
if value is not None: setattr(db_flow, key, value)
setattr(db_flow, key, value)
webhook_component = get_webhook_component_in_flow(db_flow.data) webhook_component = get_webhook_component_in_flow(db_flow.data)
db_flow.webhook = webhook_component is not None db_flow.webhook = webhook_component is not None
db_flow.updated_at = datetime.now(timezone.utc) db_flow.updated_at = datetime.now(timezone.utc)

View file

@ -4,7 +4,7 @@ import { Label } from "../../components/ui/label";
import { Textarea } from "../../components/ui/textarea"; import { Textarea } from "../../components/ui/textarea";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { InputProps } from "../../types/components"; import { InputProps } from "../../types/components";
import { cn } from "../../utils/utils"; import { cn, isEndpointNameValid } from "../../utils/utils";
export const EditFlowSettings: React.FC<InputProps> = ({ export const EditFlowSettings: React.FC<InputProps> = ({
name, name,
@ -17,7 +17,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
setEndpointName, setEndpointName,
}: InputProps): JSX.Element => { }: InputProps): JSX.Element => {
const [isMaxLength, setIsMaxLength] = useState(false); const [isMaxLength, setIsMaxLength] = useState(false);
const [isEndpointNameValid, setIsEndpointNameValid] = useState(true); const [validEndpointName, setValidEndpointName] = useState(true);
const [isInvalidName, setIsInvalidName] = useState(false); const [isInvalidName, setIsInvalidName] = useState(false);
const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const currentFlow = useFlowsManagerStore((state) => state.currentFlow);
@ -34,10 +34,6 @@ export const EditFlowSettings: React.FC<InputProps> = ({
invalid = true; invalid = true;
break; break;
} }
if (value === currentFlow?.name) {
invalid = true;
break;
}
invalid = false; invalid = false;
} }
setIsInvalidName(invalid); setIsInvalidName(invalid);
@ -51,12 +47,8 @@ export const EditFlowSettings: React.FC<InputProps> = ({
const handleEndpointNameChange = (event: ChangeEvent<HTMLInputElement>) => { const handleEndpointNameChange = (event: ChangeEvent<HTMLInputElement>) => {
// 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 = const isValid = isEndpointNameValid(event.target.value, maxLength);
(/^[a-zA-Z0-9_-]+$/.test(event.target.value) && setValidEndpointName(isValid);
event.target.value.length <= maxLength) ||
// empty is also valid
event.target.value.length === 0;
setIsEndpointNameValid(isValid);
setEndpointName!(event.target.value); setEndpointName!(event.target.value);
}; };
@ -129,7 +121,7 @@ export const EditFlowSettings: React.FC<InputProps> = ({
<Label> <Label>
<div className="edit-flow-arrangement mt-3"> <div className="edit-flow-arrangement mt-3">
<span className="font-medium">Endpoint Name</span> <span className="font-medium">Endpoint Name</span>
{!isEndpointNameValid && ( {!validEndpointName && (
<span className="edit-flow-span"> <span className="edit-flow-span">
Invalid endpoint name. Use only letters, numbers, hyphens, and Invalid endpoint name. Use only letters, numbers, hyphens, and
underscores ({maxLength} characters max). underscores ({maxLength} characters max).

View file

@ -6,6 +6,7 @@ import useAlertStore from "../../stores/alertStore";
import useFlowsManagerStore from "../../stores/flowsManagerStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore";
import { FlowSettingsPropsType } from "../../types/components"; import { FlowSettingsPropsType } from "../../types/components";
import { FlowType } from "../../types/flow"; import { FlowType } from "../../types/flow";
import { isEndpointNameValid } from "../../utils/utils";
import BaseModal from "../baseModal"; import BaseModal from "../baseModal";
export default function FlowSettingsModal({ export default function FlowSettingsModal({
@ -22,13 +23,15 @@ export default function FlowSettingsModal({
const [name, setName] = useState(currentFlow!.name); const [name, setName] = useState(currentFlow!.name);
const [description, setDescription] = useState(currentFlow!.description); const [description, setDescription] = useState(currentFlow!.description);
const [endpoint_name, setEndpointName] = useState(currentFlow!.endpoint_name); const [endpoint_name, setEndpointName] = useState(currentFlow!.endpoint_name??"");
const [isSaving, setIsSaving] = useState(false); const [isSaving, setIsSaving] = useState(false);
const [disableSave, setDisableSave] = useState(true);
function handleClick(): void { function handleClick(): void {
setIsSaving(true); setIsSaving(true);
currentFlow!.name = name; currentFlow!.name = name;
currentFlow!.description = description; currentFlow!.description = description;
currentFlow!.endpoint_name = endpoint_name; currentFlow!.endpoint_name =
endpoint_name && endpoint_name.length > 0 ? endpoint_name : null;
saveFlow(currentFlow!) saveFlow(currentFlow!)
?.then(() => { ?.then(() => {
setOpen(false); setOpen(false);
@ -54,6 +57,18 @@ export default function FlowSettingsModal({
setNameList(tempNameList.filter((name) => name !== currentFlow!.name)); setNameList(tempNameList.filter((name) => name !== currentFlow!.name));
}, [flows]); }, [flows]);
useEffect(() => {
if (
(!nameLists.includes(name) && currentFlow?.name !== name) ||
currentFlow?.description !== description ||
((currentFlow?.endpoint_name??"") !== endpoint_name &&
isEndpointNameValid(endpoint_name ?? "", 50))
) {
setDisableSave(false);
} else {
setDisableSave(true);
}
}, [nameLists, currentFlow, description, endpoint_name, name]);
return ( return (
<BaseModal <BaseModal
open={open} open={open}
@ -80,8 +95,8 @@ export default function FlowSettingsModal({
<BaseModal.Footer <BaseModal.Footer
submit={{ submit={{
label: "Save", label: "Save",
disabled: nameLists.includes(name) || name === currentFlow!.name,
dataTestId: "save-flow-settings", dataTestId: "save-flow-settings",
disabled: disableSave,
loading: isSaving, loading: isSaving,
}} }}
/> />

View file

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

View file

@ -7,7 +7,7 @@ export type FlowType = {
id: string; id: string;
data: ReactFlowJsonObject | null; data: ReactFlowJsonObject | null;
description: string; description: string;
endpoint_name?: string; endpoint_name?: string|null;
style?: FlowStyleType; style?: FlowStyleType;
is_component?: boolean; is_component?: boolean;
last_tested_version?: string; last_tested_version?: string;

View file

@ -463,3 +463,11 @@ export const logTypeIsError = (
return isErrorLog(outputs); return isErrorLog(outputs);
} }
}; };
export function isEndpointNameValid(name: string, maxLength: number): boolean {
return (
(/^[a-zA-Z0-9_-]+$/.test(name) && name.length <= maxLength) ||
// empty is also valid
name.length === 0
);
}