Added a standard component for the validationErrorIcon.
Fixed a rampant re rendering issue.
This commit is contained in:
parent
849d0177ec
commit
f530fc7efa
@ -1,36 +1,39 @@
|
||||
@import './global.scss';
|
||||
@import "./global.scss";
|
||||
|
||||
@mixin table {
|
||||
background: $white;
|
||||
border-radius: $sm;
|
||||
background: $white;
|
||||
border-radius: $sm;
|
||||
}
|
||||
|
||||
.tableBackground {
|
||||
@include table;
|
||||
padding: $lge;
|
||||
@include table;
|
||||
padding: $lge;
|
||||
}
|
||||
|
||||
.tableInfo {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
table {
|
||||
@include table;
|
||||
display: table;
|
||||
width: $full-width;
|
||||
justify-content: left;
|
||||
border: $thin-border + $--es-mono-100;
|
||||
@include table;
|
||||
display: table;
|
||||
width: $full-width;
|
||||
justify-content: left;
|
||||
border: $thin-border + $--es-mono-100;
|
||||
}
|
||||
|
||||
thead {
|
||||
background-color: $background;
|
||||
height: $th-height;
|
||||
width: 100%;
|
||||
th {
|
||||
display: table-row;
|
||||
justify-content: start;
|
||||
}
|
||||
background-color: $background;
|
||||
height: $th-height;
|
||||
width: 100%;
|
||||
th {
|
||||
display: table-row;
|
||||
justify-content: start;
|
||||
}
|
||||
}
|
||||
|
||||
.align-top {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import React, { useCallback } from "react";
|
||||
import ValidationErrorIcon from "../validationErrorIcon";
|
||||
|
||||
interface TabHeaderProps {
|
||||
id: string;
|
||||
@ -26,11 +25,7 @@ export default function TabHeader({
|
||||
return (
|
||||
<li className={className} onClick={handleClick}>
|
||||
{label}
|
||||
{hasError && (
|
||||
<span className="error-icon">
|
||||
<FontAwesomeIcon icon={faExclamationCircle} />
|
||||
</span>
|
||||
)}
|
||||
<ValidationErrorIcon visible={hasError} />
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
21
src/components/validationErrorIcon.tsx
Normal file
21
src/components/validationErrorIcon.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import React from "react";
|
||||
|
||||
interface ValidationErrorIconProps {
|
||||
visible: boolean;
|
||||
}
|
||||
|
||||
const ValidationErrorIcon: React.FC<ValidationErrorIconProps> = ({
|
||||
visible,
|
||||
}) => {
|
||||
if (!visible) return null;
|
||||
|
||||
return (
|
||||
<span className="error-icon">
|
||||
<FontAwesomeIcon icon={faExclamationCircle} />
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default ValidationErrorIcon;
|
||||
@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Namespaces } from "../../../i18n/i18n";
|
||||
import HorizontalTabs from "../../../components/common/HorizionalTabs";
|
||||
@ -131,20 +131,12 @@ const WorkflowTemplateDetails: React.FC<{ editMode: boolean }> = ({
|
||||
|
||||
//const [tasksValid, setTasksValid] = useState(true);
|
||||
|
||||
const handleTaskValidate = (taskId: string, isValid: boolean) => {
|
||||
const handleTaskValidate = useCallback((taskId: string, isValid: boolean) => {
|
||||
setTaskValidation((prev) => {
|
||||
const updated = { ...prev, [taskId]: isValid };
|
||||
|
||||
// Compute overall validity
|
||||
//const allValid = Object.values(updated).every((v) => v === true);
|
||||
|
||||
// Bubble up to parent
|
||||
//setTasksValid(allValid);
|
||||
//setTaskValidation(updated);
|
||||
|
||||
return updated;
|
||||
});
|
||||
};
|
||||
}, []);
|
||||
|
||||
const { loaded, redirect, errors: formErrors, data } = form.state;
|
||||
if (redirect) return <Navigate to={redirect} />;
|
||||
|
||||
@ -8,33 +8,39 @@ import {
|
||||
defaultsContext,
|
||||
} from "../useCapabilityDefaults";
|
||||
import Button, { ButtonType } from "../../../../../components/common/Button";
|
||||
import ValidationErrorIcon from "../../../../../components/validationErrorIcon";
|
||||
import ErrorBlock from "../../../../../components/common/ErrorBlock";
|
||||
|
||||
// TODO: Replace with your real RolePicker and RaciPicker
|
||||
const RolePicker = (props: any) => (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<input
|
||||
type="text"
|
||||
name={props.name}
|
||||
value={props.value?.id ?? ""}
|
||||
onChange={(e) =>
|
||||
props.onChange(props.name, { id: BigInt(e.target.value) })
|
||||
}
|
||||
/>
|
||||
<div className="form-group">
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<input
|
||||
type="text"
|
||||
name={props.name}
|
||||
value={props.value?.id ?? ""}
|
||||
onChange={(e) =>
|
||||
props.onChange(props.name, { id: BigInt(e.target.value) })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const RaciPicker = (props: any) => (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<select
|
||||
name={props.name}
|
||||
value={props.value}
|
||||
onChange={(e) => props.onChange(props.name, e.target.value)}
|
||||
>
|
||||
<option value="Responsible">Responsible</option>
|
||||
<option value="Accountable">Accountable</option>
|
||||
<option value="Consulted">Consulted</option>
|
||||
<option value="Informed">Informed</option>
|
||||
</select>
|
||||
<div className="form-group">
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<select
|
||||
name={props.name}
|
||||
value={props.value}
|
||||
onChange={(e) => props.onChange(props.name, e.target.value)}
|
||||
>
|
||||
<option value="Responsible">Responsible</option>
|
||||
<option value="Accountable">Accountable</option>
|
||||
<option value="Consulted">Consulted</option>
|
||||
<option value="Informed">Informed</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -97,6 +103,7 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>Role</th>
|
||||
<th>Contact</th>
|
||||
<th>RACI</th>
|
||||
@ -109,7 +116,16 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
|
||||
</thead>
|
||||
<tbody>
|
||||
{assignees.map((assignee, index) => (
|
||||
<tr key={index}>
|
||||
<tr key={index} className="align-top">
|
||||
<td className="form-group">
|
||||
<ValidationErrorIcon
|
||||
visible={
|
||||
!!fieldErrors?.[`${guid}.assignees[${index}].role`] ||
|
||||
!!fieldErrors?.[`${guid}.assignees[${index}].contact`] ||
|
||||
!!fieldErrors?.[`${guid}.assignees[${index}].raci`]
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<RolePicker
|
||||
name="role"
|
||||
@ -144,7 +160,7 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<td className="form-group">
|
||||
<Button
|
||||
onClick={() => removeAssignee(index)}
|
||||
buttonType={ButtonType.secondary}
|
||||
@ -156,6 +172,15 @@ export const AssigneesOfITaskAssigneeEditor: React.FC<CapabilityEditorProps> = (
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
<ErrorBlock
|
||||
error={Object.values(fieldErrors ?? {})
|
||||
.filter((_, i) =>
|
||||
Object.keys(fieldErrors ?? {}).some(
|
||||
(key) => key === `${guid}.assignees`,
|
||||
),
|
||||
)
|
||||
.join("; ")}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -9,6 +9,7 @@ import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { sortTasksTopologically } from "./workflowGraphUtils";
|
||||
import { useCapabilityDefaults, validateTask } from "./useCapabilityDefaults";
|
||||
import ValidationErrorIcon from "../../../../components/validationErrorIcon";
|
||||
|
||||
interface TaskListProps {
|
||||
tasks: TaskDefinition[];
|
||||
@ -70,11 +71,11 @@ const TaskList: React.FC<TaskListProps> = ({
|
||||
<>
|
||||
{x.config.name as string}
|
||||
|
||||
{validTasksList[x.config.guid as string] === false && (
|
||||
<span className="error-icon">
|
||||
<FontAwesomeIcon icon={faExclamationCircle} />
|
||||
</span>
|
||||
)}
|
||||
{
|
||||
<ValidationErrorIcon
|
||||
visible={validTasksList[x.config.guid as string] === false}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
)}
|
||||
onSelect={(item) => onSelectTask(item)}
|
||||
|
||||
@ -27,26 +27,23 @@ export const TaskEditor: React.FC<TaskEditorProps> = ({
|
||||
|
||||
const runValidation = useCallback(
|
||||
(
|
||||
bubbleUp = true,
|
||||
taskToValidate: TaskDefinition,
|
||||
tasksList: TaskDefinition[],
|
||||
tasksMetadataList: TaskMetadata[],
|
||||
) => {
|
||||
const errors = validateTask(taskToValidate, tasksList, tasksMetadataList);
|
||||
setFieldErrors(errors);
|
||||
|
||||
if (bubbleUp) {
|
||||
onValidate(
|
||||
taskToValidate.config.guid as string,
|
||||
Object.keys(errors).length === 0,
|
||||
);
|
||||
}
|
||||
onValidate(
|
||||
taskToValidate.config.guid as string,
|
||||
Object.keys(errors).length === 0,
|
||||
);
|
||||
},
|
||||
[onValidate],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
runValidation(true, task, tasks, tasksMetadata);
|
||||
}, [runValidation, task, tasks, tasksMetadata]);
|
||||
runValidation(task, tasks, tasksMetadata);
|
||||
}, [task.config.guid, runValidation]);
|
||||
|
||||
const handleTaskChange = (updatedTask: TaskDefinition) => {
|
||||
// Update the task list
|
||||
@ -54,16 +51,7 @@ export const TaskEditor: React.FC<TaskEditorProps> = ({
|
||||
t.config.guid === updatedTask.config.guid ? updatedTask : t,
|
||||
);
|
||||
|
||||
runValidation(true, updatedTask, updatedTasks, tasksMetadata);
|
||||
// // Run validation
|
||||
// const errors = validateTask(updatedTask, updatedTasks, tasksMetadata);
|
||||
// setFieldErrors(errors);
|
||||
|
||||
// // Bubble validity up
|
||||
// onValidate(
|
||||
// updatedTask.config.guid as string,
|
||||
// Object.keys(errors).length === 0,
|
||||
// );
|
||||
runValidation(updatedTask, updatedTasks, tasksMetadata);
|
||||
|
||||
// Bubble updated task up
|
||||
onChange(updatedTask);
|
||||
|
||||
@ -66,7 +66,7 @@ const TasksTab: React.FC<TasksTabProps> = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="two-column-grid">
|
||||
<div className="two-column-grid no-scroll">
|
||||
<div className="fit-content-width`">
|
||||
<TaskList
|
||||
tasks={tasks}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user