Tidy up of compiler warnings, also make sure that the unique naming works better.

This commit is contained in:
Colin Dawson 2026-02-14 14:59:34 +00:00
parent 6f49add5f7
commit 291a391faf
3 changed files with 60 additions and 31 deletions

View File

@ -28,46 +28,76 @@ export const TaskCoreEditor: React.FC<TaskCoreEditorProps> = ({
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
const prevErrorsRef = useRef<Record<string, string>>({});
const formatNewTaskName = (
tasks: TaskDefinition<Record<string, unknown>>[],
) => {
const displayName = allowedTasks.find(
(t) => t.taskType === task.type,
)?.displayName;
// Generate a unique default name
const nameExists = (tasks: TaskDefinition[], candidate: string): boolean => {
const target = candidate.trim().toLowerCase();
return `${tTaskType(displayName!)} ${tasks.length + 1}`;
return tasks.some(
(t) => (t.config.name as string)?.trim().toLowerCase() === target,
);
};
const formatNewTaskName = useCallback(
(tasks: TaskDefinition[]) => {
const displayName = allowedTasks.find(
(t) => t.taskType === task.type,
)?.displayName;
if (!displayName) return "New Task";
const base = `${tTaskType(displayName)} `;
let index = 1;
while (nameExists(tasks, `${base}${index}`)) {
index++;
}
return `${base}${index}`;
},
[allowedTasks, task.type, tTaskType],
);
const runValidation = useCallback(() => {
const errors: Record<string, string> = {};
//If the task doesn't have a name (can happen when adding a new task), generate a default one.
if (task.config.name === undefined) {
task.config.name = formatNewTaskName(allTasks);
// If the task doesn't have a name, generate a default one via onChange
if (!task.config.name) {
const newName = formatNewTaskName(allTasks);
onChange({
...task,
config: {
...task.config,
name: newName,
},
});
// Stop here — next render will validate again
return { errors: {}, isValid: true };
}
if (!task.config.name || (task.config.name as string).trim() === "") {
// Name required
if (!(task.config.name as string).trim()) {
errors["name"] = "Name cannot be empty";
}
if (task.config.name) {
// Name must be unique across all tasks
const duplicate = allTasks.find(
(t) =>
(t.config.guid as string) !== (task.config.guid as string) && // exclude self
(t.config.name as string).trim().toLowerCase() ===
(task.config.name as string).trim().toLowerCase(),
);
// Name must be unique
const duplicate = allTasks.find(
(t) =>
t.config.guid !== task.config.guid &&
(t.config.name as string).trim().toLowerCase() ===
(task.config.name as string).trim().toLowerCase(),
);
if (duplicate) {
errors["name"] = "Name must be unique.";
}
if (duplicate) {
errors["name"] = "Name must be unique.";
}
// Description max length
const descriptionMaxLength = 5000;
if (
task.config.description &&
(task.config.description as string).length >= descriptionMaxLength
(task.config.description && (task.config.description as string).length) ??
0 > descriptionMaxLength
) {
errors["description"] =
`Description can be up to ${descriptionMaxLength} characters long.`;
@ -75,9 +105,9 @@ export const TaskCoreEditor: React.FC<TaskCoreEditorProps> = ({
const isValid = Object.keys(errors).length === 0;
return { errors, isValid };
}, [allTasks, task.config.description, task.config.guid, task.config.name]);
}, [task, allTasks, formatNewTaskName, onChange]);
//Validate when task changes.
// Validate when task changes (new task selected / created)
useEffect(() => {
const { errors, isValid } = runValidation();
@ -85,9 +115,9 @@ export const TaskCoreEditor: React.FC<TaskCoreEditorProps> = ({
prevErrorsRef.current = errors;
onValidate({ isValid, errors });
}, [onValidate, runValidation, task.config.guid]);
}, [task.config.guid, runValidation, onValidate]);
//Validate when fields change
// Validate when fields change
useEffect(() => {
const { errors, isValid } = runValidation();
@ -101,7 +131,7 @@ export const TaskCoreEditor: React.FC<TaskCoreEditorProps> = ({
onValidate({ isValid, errors });
prevErrorsRef.current = errors;
}
}, [task.config.description, task.config.name, onValidate, runValidation]);
}, [task.config.name, task.config.description, runValidation, onValidate]);
return (
<div>

View File

@ -4,8 +4,6 @@ import {
TaskMetadata,
} from "../services/WorkflowTemplateService";
import AddTaskButton from "./AddTaskButton";
import { Namespaces } from "../../../../i18n/i18n";
import { useTranslation } from "react-i18next";
import { SelectableList } from "../../../../components/common/SelectableList";
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

View File

@ -22,6 +22,7 @@ export const TaskEditor: React.FC<TaskEditorProps> = ({
onChange,
onValidate,
}) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [validationMap, setValidationMap] = useState<
Record<string, TaskValidationResult>
>({});