Skip to content

Commit

Permalink
DBZ-8474: Add basic validation in UI forms (#33)
Browse files Browse the repository at this point in the history
* DBZ-8474: Add basic validation in UI forms

* DBZ-8474: Add basic validation to source, destination and pipeline.
  • Loading branch information
indraraj authored Dec 2, 2024
1 parent 3404a0c commit a49656e
Show file tree
Hide file tree
Showing 9 changed files with 168 additions and 58 deletions.
Binary file modified .DS_Store
Binary file not shown.
14 changes: 6 additions & 8 deletions src/appLayout/AppLayout.css
Original file line number Diff line number Diff line change
@@ -1,26 +1,24 @@
.custom-switch{
/* .custom-switch{
margin-left: auto;
}
} */

@media (min-width: 75rem){
.custom-app-page {
grid-template-columns: 100px 1fr !important;
}
.pf-v6-c-page__main-container {
margin-left: 0 !important;
/* overflow-y: clip !important; */
}
}
.pf-v6-c-page__main-container {
/* overflow-y: clip !important; */
/* .pf-v6-c-page__main-container {
width: 100%;
}
} */
.pf-v6-c-drawer__body{
display: flex;
max-height: 100%;
}

.custom-app-page__sidebar-body {
/* .custom-app-page__sidebar-body {
padding-inline-start: 1rem;
padding-inline-end: 1rem;
}
} */
4 changes: 4 additions & 0 deletions src/components/SourceSinkForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import ConnectorImage from "./ComponentImage";
interface SourceSinkFormProps {
ConnectorId: string;
dataType?: string;
errorWarning: string[];
connectorType: "source" | "destination";
properties: Map<string, { key: string; value: string }>;
setValue: (key: string, value: string) => void;
Expand All @@ -44,6 +45,7 @@ const SourceSinkForm = ({
setValue,
getValue,
setError,
errorWarning,
errors,
handleAddProperty,
handleDeleteProperty,
Expand Down Expand Up @@ -137,6 +139,7 @@ const SourceSinkForm = ({
isRequired
type="text"
placeholder="Key"
validated={errorWarning.includes(key) ? "error" : "default"}
id={`${connectorType}-config-props-key-${key}`}
name={`${connectorType}-config-props-key-${key}`}
value={properties.get(key)?.key || ""}
Expand All @@ -155,6 +158,7 @@ const SourceSinkForm = ({
type="text"
id={`${connectorType}-config-props-value-${key}`}
placeholder="Value"
validated={errorWarning.includes(key) ? "error" : "default"}
name={`${connectorType}-config-props-value-${key}`}
value={properties.get(key)?.value || ""}
onChange={(_e, value) =>
Expand Down
40 changes: 33 additions & 7 deletions src/pages/Destination/CreateDestination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ import destinationCatalog from "../../__mocks__/data/DestinationCatalog.json";
import { useNavigate, useParams } from "react-router-dom";
import "./CreateDestination.css";
import { CodeEditor, Language } from "@patternfly/react-code-editor";
import _ from "lodash";
import { find } from "lodash";
import { createPost, Destination } from "../../apis/apis";
import { API_URL } from "../../utils/constants";
import { convertMapToObject } from "../../utils/helpers";
import { useNotification } from "../../appLayout/AppNotificationContext";
import PageHeader from "@components/PageHeader";
import SourceSinkForm from "@components/SourceSinkForm";
import { useState } from "react";

interface CreateDestinationProps {
modelLoaded?: boolean;
Expand All @@ -32,6 +33,8 @@ interface CreateDestinationProps {
onSelection?: (selection: Destination) => void;
}

type Properties = { key: string; value: string };

const CreateDestination: React.FunctionComponent<CreateDestinationProps> = ({
modelLoaded,
selectedId,
Expand All @@ -51,13 +54,15 @@ const CreateDestination: React.FunctionComponent<CreateDestinationProps> = ({

const { addNotification } = useNotification();

const [errorWarning, setErrorWarning] = useState<string[]>([]);

const [editorSelected, setEditorSelected] = React.useState("form-editor");

const [isLoading, setIsLoading] = React.useState(false);

const [properties, setProperties] = React.useState<
Map<string, { key: string; value: string }>
>(new Map([["key0", { key: "", value: "" }]]));
const [properties, setProperties] = useState<Map<string, Properties>>(
new Map([["key0", { key: "", value: "" }]])
);
const [keyCount, setKeyCount] = React.useState<number>(1);

const handleAddProperty = () => {
Expand Down Expand Up @@ -97,7 +102,7 @@ const CreateDestination: React.FunctionComponent<CreateDestinationProps> = ({
const createNewDestination = async (values: Record<string, string>) => {
const payload = {
description: values["details"],
type: _.find(destinationCatalog, { id: destinationId })?.type || "",
type: find(destinationCatalog, { id: destinationId })?.type || "",
schema: "schema321",
vaults: [],
config: convertMapToObject(properties),
Expand All @@ -110,20 +115,40 @@ const CreateDestination: React.FunctionComponent<CreateDestinationProps> = ({
addNotification(
"danger",
`Destination creation failed`,
`Failed to create ${(response.data as Destination).name}: ${response.error}`
`Failed to create ${(response.data as Destination).name}: ${
response.error
}`
);
} else {
modelLoaded && onSelection && onSelection(response.data as Destination);
addNotification(
"success",
`Create successful`,
`Destination "${(response.data as Destination).name}" created successfully.`
`Destination "${
(response.data as Destination).name
}" created successfully.`
);
}
};

const handleCreateDestination = async (values: Record<string, string>) => {
setIsLoading(true);
const errorWarning = [] as string[];
properties.forEach((value: Properties, key: string) => {
if (value.key === "" || value.value === "") {
errorWarning.push(key);
}
});
setErrorWarning(errorWarning);
if (errorWarning.length > 0) {
addNotification(
"danger",
`Destination creation failed`,
`Please fill both Key and Value fields for all the properties.`
);
setIsLoading(false);
return;
}
await createNewDestination(values);
setIsLoading(false);
!modelLoaded && navigateTo("/destination");
Expand Down Expand Up @@ -203,6 +228,7 @@ const CreateDestination: React.FunctionComponent<CreateDestinationProps> = ({
getValue={getValue}
setError={setError}
errors={errors}
errorWarning={errorWarning}
handleAddProperty={handleAddProperty}
handleDeleteProperty={handleDeleteProperty}
handlePropertyChange={handlePropertyChange}
Expand Down
23 changes: 22 additions & 1 deletion src/pages/Destination/EditDestination.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import { useNotification } from "../../appLayout/AppNotificationContext";
import SourceSinkForm from "@components/SourceSinkForm";
import PageHeader from "@components/PageHeader";

type Properties = { key: string; value: string };

const EditDestination: React.FunctionComponent = () => {
const navigate = useNavigate();
const { destinationId } = useParams<{ destinationId: string }>();
Expand All @@ -44,14 +46,16 @@ const EditDestination: React.FunctionComponent = () => {

const [editorSelected, setEditorSelected] = React.useState("form-editor");

const [errorWarning, setErrorWarning] = useState<string[]>([]);

const [destination, setDestination] = useState<Destination>();
const [isFetchLoading, setIsFetchLoading] = useState<boolean>(true);
const [error, setError] = useState<string | null>(null);

const [isLoading, setIsLoading] = useState(false);

const [properties, setProperties] = useState<
Map<string, { key: string; value: string }>
Map<string, Properties>
>(new Map([["key0", { key: "", value: "" }]]));
const [keyCount, setKeyCount] = useState<number>(1);

Expand Down Expand Up @@ -148,6 +152,22 @@ const EditDestination: React.FunctionComponent = () => {

const handleEditDestination = async (values: Record<string, string>) => {
setIsLoading(true);
const errorWarning = [] as string[];
properties.forEach((value: Properties, key: string) => {
if (value.key === "" || value.value === "") {
errorWarning.push(key);
}
});
setErrorWarning(errorWarning);
if (errorWarning.length > 0) {
addNotification(
"danger",
`Destination edit failed`,
`Please fill both Key and Value fields for all the properties.`
);
setIsLoading(false);
return;
}
await editDestination(values);
setIsLoading(false);
navigateTo("/destination");
Expand Down Expand Up @@ -237,6 +257,7 @@ const EditDestination: React.FunctionComponent = () => {
getValue={getValue}
setError={setError}
errors={errors}
errorWarning={errorWarning}
handleAddProperty={handleAddProperty}
handleDeleteProperty={handleDeleteProperty}
handlePropertyChange={handlePropertyChange}
Expand Down
85 changes: 52 additions & 33 deletions src/pages/Pipeline/ConfigurePipeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { API_URL } from "../../utils/constants";
import PageHeader from "@components/PageHeader";
import { useAtom } from "jotai";
import { selectedTransformAtom } from "./PipelineDesigner";
import { useNotification } from "@appContext/AppNotificationContext";

const ConfigurePipeline: React.FunctionComponent = () => {
const navigate = useNavigate();
Expand All @@ -57,7 +58,7 @@ const ConfigurePipeline: React.FunctionComponent = () => {

const [selectedTransform] = useAtom(selectedTransformAtom);

console.log("selectedTransform", selectedTransform);
const { addNotification } = useNotification();

const navigateTo = (url: string) => {
navigate(url);
Expand Down Expand Up @@ -117,10 +118,10 @@ const ConfigurePipeline: React.FunctionComponent = () => {
fetchDestination();
}, [destinationId]);

const createNewPipline = async (values: Record<string, string>) => {
const createNewPipeline = async (values: Record<string, string>) => {
const payload = {
description: values["description"],
logLevel: logLevel,
logLevel: values["log-level"],
source: {
name: source?.name,
id: source?.id,
Expand All @@ -135,31 +136,41 @@ const ConfigurePipeline: React.FunctionComponent = () => {

const response = await createPost(`${API_URL}/api/pipelines`, payload);

if (response.error) {
console.error("Failed to create source:", response.error);
} else {
console.log("Source created successfully:", response.data);
}
return response;
};

const handleCreatePipline = async (values: Record<string, string>) => {
const handleCreatePipeline = async (values: Record<string, string>) => {
console.log("values", values);
setIsLoading(true);
await createNewPipline(values);
if(!values["log-level"]) {
setLogLevelError(true);
setIsLoading(false);
return;
}
const response = await createNewPipeline(values);
if (response.error) {
addNotification(
"danger",
`Pipeline creation failed`,
`${response.error}`
);
setIsLoading(false);
return;
}
addNotification(
"success",
`Pipeline creation successful.`,
`Pipeline "${values["pipeline-name"]}" created successfully.`
);
setIsLoading(false);

navigateTo("/pipeline");
};

const [logLevel, setLogLevel] = React.useState("");

const onChange = (
_event: React.FormEvent<HTMLSelectElement>,
value: string
) => {
setLogLevel(value);
};
const [logLevelError, setLogLevelError] = React.useState<boolean>(false);

const options = [
{ value: "", label: "Select log level", disabled: false },
{ value: "", label: "Select log level", disabled: true },
{ value: "OFF", label: "OFF", disabled: false },
{ value: "FATAL", label: "FATAL", disabled: false },
{ value: "ERROR", label: "ERROR", disabled: false },
Expand Down Expand Up @@ -342,22 +353,30 @@ const ConfigurePipeline: React.FunctionComponent = () => {
<FormGroup
label="Log level"
isRequired
fieldId="logLevel-field"
fieldId="log-level-field"
>
<FormSelect
value={logLevel}
onChange={onChange}
aria-label="FormSelect Input"
ouiaId="BasicFormSelect"
value={getValue("log-level")}
isRequired
id={'log-level'}
onChange={(_event, value) => {
setValue("log-level", value);
setLogLevelError(false);
}}
aria-label="FormSelect Input"
ouiaId="BasicFormSelect"
validated={
logLevelError ? "error" : "default"
}
>
{options.map((option, index) => (
<FormSelectOption
isDisabled={option.disabled}
key={index}
value={option.value}
label={option.label}
/>
))}
{options.map((option, index) => (
<FormSelectOption
isDisabled={option.disabled}
key={index}
value={option.value}
label={option.label}
/>
))}
</FormSelect>
</FormGroup>
</FormSection>
Expand Down Expand Up @@ -389,7 +408,7 @@ const ConfigurePipeline: React.FunctionComponent = () => {
if (!values["pipeline-name"]) {
setError("pipeline-name", "Pipeline name is required.");
} else {
handleCreatePipline(values);
handleCreatePipeline(values);
}
}}
>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/Pipeline/EditPipeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ const EditPipeline: React.FunctionComponent = () => {
};

const options = [
{ value: "", label: "Select log level", disabled: false },
{ value: "", label: "Select log level", disabled: true },
{ value: "OFF", label: "OFF", disabled: false },
{ value: "FATAL", label: "FATAL", disabled: false },
{ value: "ERROR", label: "ERROR", disabled: false },
Expand Down
Loading

0 comments on commit a49656e

Please sign in to comment.