Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Redesign add page] Add form status callout message #5634

Merged
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ import {
getOptionalParameterStepStatus,
showCommandsSections,
getPasswordStepStatus,
getIncompleteSteps,
getInvalidFields,
tFormFieldsLabel,
tFormStepsLabel,
} from '../../services/register-agent-steps-status-services';
import { webDocumentationLink } from '../../../../../common/services/web_documentation';

Expand Down Expand Up @@ -61,9 +65,17 @@ export const Steps = ({
protocol: connection.isUDP ? 'UDP' : '',
},
} as IParseRegisterFormValues;
const [missingStepsName, setMissingStepsName] = useState<tFormStepsLabel[]>(
[],
);
const [invalidFieldsName, setInvalidFieldsName] = useState<
tFormFieldsLabel[]
>([]);
const [registerAgentFormValues, setRegisterAgentFormValues] =
useState<IParseRegisterFormValues>(initialParsedFormValues);

const FORM_MESSAGE_CONJUNTION = ' and ';

useEffect(() => {
// get form values and parse them divided in OS and optional params
const registerAgentFormValuesParsed = parseRegisterAgentFormValues(
Expand All @@ -78,6 +90,8 @@ export const Steps = ({
setStartCommandStepStatus(
getAgentCommandsStepStatus(form.fields, startCommandWasCopied),
);
setMissingStepsName(getIncompleteSteps(form.fields) || []);
setInvalidFieldsName(getInvalidFields(form.fields) || []);
}, [form.fields]);

const { installCommand, startCommand, selectOS, setOptionalParams } =
Expand Down Expand Up @@ -142,7 +156,8 @@ export const Steps = ({
color='warning'
title={
<span>
The Wazuh password is required but wasn't defined. Please check our{' '}
The Wazuh password is required but wasn't defined. Please
check our{' '}
<EuiLink
target='_blank'
href={webDocumentationLink(
Expand Down Expand Up @@ -177,25 +192,67 @@ export const Steps = ({
</span>
),
children: (
<CommandOutput
commandText={installCommand}
showCommand={showCommandsSections(form.fields)}
os={registerAgentFormValues.operatingSystem.name}
onCopy={() => setInstallCommandWasCopied(true)}
password={registerAgentFormValues.optionalParams.wazuhPassword}
/>
<>
{missingStepsName?.length ? (
<EuiCallOut
color='warning'
title={`Please select the ${missingStepsName?.join(FORM_MESSAGE_CONJUNTION)}.`}
iconType='iInCircle'
/>
) : null}
{invalidFieldsName?.length ? (
<EuiCallOut
color='danger'
title={`There are fields with errors. Please verify them: ${invalidFieldsName?.join(
FORM_MESSAGE_CONJUNTION,
)}.`}
iconType='iInCircle'
style={{ marginTop: '1rem' }}
/>
) : null}
{!missingStepsName?.length && !invalidFieldsName?.length ? (
<CommandOutput
commandText={installCommand}
showCommand={showCommandsSections(form.fields)}
os={registerAgentFormValues.operatingSystem.name}
onCopy={() => setInstallCommandWasCopied(true)}
password={registerAgentFormValues.optionalParams.wazuhPassword}
/>
) : null}
</>
),
status: installCommandStepStatus,
},
{
title: <span className='stepTitle'>Start the Wazuh agent:</span>,
children: (
<CommandOutput
commandText={startCommand}
showCommand={showCommandsSections(form.fields)}
os={registerAgentFormValues.operatingSystem.name}
onCopy={() => setStartCommandWasCopied(true)}
/>
<>
{missingStepsName?.length ? (
<EuiCallOut
color='warning'
title={`Please select the ${missingStepsName?.join(FORM_MESSAGE_CONJUNTION)}.`}
iconType='iInCircle'
/>
) : null}
{invalidFieldsName?.length ? (
<EuiCallOut
color='danger'
title={`There are fields with errors. Please verify them: ${invalidFieldsName?.join(
FORM_MESSAGE_CONJUNTION,
)}.`}
iconType='iInCircle'
style={{ marginTop: '1rem' }}
/>
) : null}
{!missingStepsName?.length && !invalidFieldsName?.length ? (
<CommandOutput
commandText={startCommand}
showCommand={showCommandsSections(form.fields)}
os={registerAgentFormValues.operatingSystem.name}
onCopy={() => setStartCommandWasCopied(true)}
/>
) : null}
</>
),
status: startCommandStepStatus,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import {
EnhancedFieldConfiguration,
UseFormReturn,
} from '../../../components/common/form/types';
import {
FormStepsDependencies,
RegisterAgentFormStatusManager,
} from './form-status-manager';

const defaultFormFieldData: EnhancedFieldConfiguration = {
changed: true,
value: 'value1',
error: '',
currentValue: '',
initialValue: '',
type: 'text',
onChange: () => {
console.log('onChange');
},
setInputRef: () => {
console.log('setInputRef');
},
inputRef: null,
};

const formFieldsDefault: UseFormReturn['fields'] = {
field1: {
...defaultFormFieldData,
value: '',
error: null,
},
field2: {
...defaultFormFieldData,
value: '',
error: 'error message',
},
field3: {
...defaultFormFieldData,
value: 'value valid',
error: null,
},
};

describe('RegisterAgentFormStatusManager', () => {
it('should create a instance', () => {
const registerAgentFormStatusManager = new RegisterAgentFormStatusManager(
formFieldsDefault,
);
expect(registerAgentFormStatusManager).toBeDefined();
});

it('should return the form status', () => {
const registerAgentFormStatusManager = new RegisterAgentFormStatusManager(
formFieldsDefault,
);
const formStatus = registerAgentFormStatusManager.getFormStatus();
expect(formStatus).toEqual({
field1: 'empty',
field2: 'invalid',
field3: 'complete',
});
});

it('should return the field status', () => {
const registerAgentFormStatusManager = new RegisterAgentFormStatusManager(
formFieldsDefault,
);
const fieldStatus = registerAgentFormStatusManager.getFieldStatus('field1');
expect(fieldStatus).toEqual('empty');
});

it('should return error if fieldname not found', () => {
const registerAgentFormStatusManager = new RegisterAgentFormStatusManager(
formFieldsDefault,
);
expect(() =>
registerAgentFormStatusManager.getFieldStatus('field4'),
).toThrowError('Fieldname not found');
});

it('should return a INVALID when the step have an error', () => {
const formSteps: FormStepsDependencies = {
step1: ['field1', 'field2'],
step2: ['field3'],
};
const registerAgentFormStatusManager = new RegisterAgentFormStatusManager(
formFieldsDefault,
formSteps,
);
expect(registerAgentFormStatusManager).toBeDefined();
expect(registerAgentFormStatusManager.getStepStatus('step1')).toEqual(
'invalid',
);
});

it('should return COMPLETE when the step have no errors and is not empty', () => {
const formSteps: FormStepsDependencies = {
step1: ['field1', 'field2'],
step2: ['field3'],
};
const registerAgentFormStatusManager = new RegisterAgentFormStatusManager(
formFieldsDefault,
formSteps,
);
expect(registerAgentFormStatusManager).toBeDefined();
expect(registerAgentFormStatusManager.getStepStatus('step2')).toEqual(
'complete',
);
});

it('should return EMPTY when the step all fields empty', () => {
const formSteps: FormStepsDependencies = {
step1: ['field1'],
step2: [ 'field2',
'field3' ],
};
const registerAgentFormStatusManager = new RegisterAgentFormStatusManager(
formFieldsDefault,
formSteps,
);
expect(registerAgentFormStatusManager).toBeDefined();
expect(registerAgentFormStatusManager.getStepStatus('step1')).toEqual(
'empty',
);
});

it('should return all the steps status', () => {
const formSteps: FormStepsDependencies = {
step1: ['field1'],
step2: [ 'field2',
'field3' ],
step3: ['field3']
};
const registerAgentFormStatusManager = new RegisterAgentFormStatusManager(
formFieldsDefault,
formSteps,
);
expect(registerAgentFormStatusManager).toBeDefined();
expect(registerAgentFormStatusManager.getFormStepsStatus()).toEqual({
step1: 'empty',
step2: 'invalid',
step3: 'complete'
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import { UseFormReturn } from '../../../components/common/form/types';

type FieldStatus = 'invalid' | 'empty' | 'complete';
type FormStatus = {
[key: string]: FieldStatus;
};

type FormFields = UseFormReturn['fields'];
type FormFieldName = keyof FormFields;

export type FormStepsDependencies = {
[key: string]: FormFieldName[];
};

type FormStepsStatus = {
[key: string]: FieldStatus;
};

interface FormFieldsStatusManager {
getFieldStatus: (fieldname: FormFieldName) => FieldStatus;
getFormStatus: () => FormStatus;
getStepStatus: (stepName: string) => FieldStatus;
getFormStepsStatus: () => FormStepsStatus;
}

export class RegisterAgentFormStatusManager implements FormFieldsStatusManager {
constructor(
private formFields: FormFields,
private formSteps?: FormStepsDependencies,
) {}

getFieldStatus = (fieldname: FormFieldName): FieldStatus => {
const field = this.formFields[fieldname];
if (!field) {
throw Error('Fieldname not found');
}

if (field.error) {
return 'invalid';
}

if (field.value?.length === 0) {
return 'empty';
}

return 'complete';
};

getFormStatus = (): FormStatus => {
const fieldNames = Object.keys(this.formFields);
const formStatus: FormStatus | object = {};

fieldNames.forEach((fieldName: string) => {
formStatus[fieldName] = this.getFieldStatus(fieldName);
});

return formStatus as FormStatus;
};

getStepStatus = (stepName: string): FieldStatus => {
if (!this.formSteps) {
throw Error('Form steps not defined');
}
const stepFields = this.formSteps[stepName];
if (!stepFields) {
throw Error('Step name not found');
}

const formStepStatus: FormStepsStatus | object = {};
stepFields.forEach((fieldName: FormFieldName) => {
formStepStatus[fieldName] = this.getFieldStatus(fieldName);
});

const stepStatus = Object.values(formStepStatus);

// if any is invalid
if (stepStatus.includes('invalid')) {
return 'invalid';
} else if (stepStatus.includes('empty')) {
// if all are empty
return 'empty';
} else {
// if all are complete
return 'complete';
}
};

getFormStepsStatus = (): FormStepsStatus => {
if (!this.formSteps) {
throw Error('Form steps not defined');
}

const formStepsStatus: FormStepsStatus | object = {};
Object.keys(this.formSteps).forEach((stepName: string) => {
formStepsStatus[stepName] = this.getStepStatus(stepName);
});

return formStepsStatus as FormStepsStatus;
};

getIncompleteSteps = (): string[] => {
const formStepsStatus = this.getFormStepsStatus();
const notCompleteSteps = Object.entries(formStepsStatus).filter(
([ _, status ]) => status === 'empty',
);
return notCompleteSteps.map(( [ stepName, _]) => stepName);
};

getInvalidFields = (): string[] => {
const formStatus = this.getFormStatus();
const invalidFields = Object.entries(formStatus).filter(
([ _, status ]) => status === 'invalid',
);
return invalidFields.map(([ fieldName, _ ]) => fieldName);
}
}
Loading