- }
+ const { className, quickOptions = [] } = props;
+ const [field, meta, helpers] = useField(props);
+ const { value } = field;
+ const { setValue, setTouched } = helpers;
+
+ return (
+
+ );
+};
-export default CronField;
\ No newline at end of file
+export default CronField;
diff --git a/ui/src/components/Form/form-fields/DateField/date-field.scss b/ui/src/components/Form/form-fields/DateField/date-field.scss
index e7a2dd0b4d..0db29ef270 100644
--- a/ui/src/components/Form/form-fields/DateField/date-field.scss
+++ b/ui/src/components/Form/form-fields/DateField/date-field.scss
@@ -1,96 +1,96 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$calendar-font-size: 11px;
$calendar-backgroud-color: white;
.form-field-wrapper.date-field-wrapper {
- .react-datetime-picker.date-field-select {
- height: 36px;
- background-color: white;
-
- .react-datetime-picker__wrapper {
- border: 1px solid $color-grey-light;
- border-radius: 2px;
+ .react-datetime-picker.date-field-select {
+ height: 36px;
+ background-color: white;
- .react-datetime-picker__inputGroup {
- padding: 0 8px;
-
- .react-datetime-picker__inputGroup__input,
- .react-datetime-picker__inputGroup__leadingZero {
- color: $color-grey-black;
- font-size: 14px;
-
- &:invalid {
- background-color: white;
- }
- &:focus-visible {
- outline: none;
- }
- }
- }
+ .react-datetime-picker__wrapper {
+ border: 1px solid $color-grey-light;
+ border-radius: 2px;
+
+ .react-datetime-picker__inputGroup {
+ padding: 0 8px;
+
+ .react-datetime-picker__inputGroup__input,
+ .react-datetime-picker__inputGroup__leadingZero {
+ color: $color-grey-black;
+ font-size: 14px;
+
+ &:invalid {
+ background-color: white;
+ }
+ &:focus-visible {
+ outline: none;
+ }
}
+ }
}
- .react-datetime-picker__calendar {
- width: 230px;
-
- .react-calendar.date-field-select-calendar {
- border: 1px solid $color-grey-light;
- border-radius: 2px;
+ }
+ .react-datetime-picker__calendar {
+ width: 230px;
+
+ .react-calendar.date-field-select-calendar {
+ border: 1px solid $color-grey-light;
+ border-radius: 2px;
- .react-calendar__navigation {
- margin-bottom: 0;
-
- .react-calendar__navigation__label {
- background-color: $calendar-backgroud-color;
- color: $color-main;
- font-size: $calendar-font-size;
- font-weight: bold;
- text-transform: uppercase;
- }
- .react-calendar__navigation__arrow {
- color: $color-grey-dark;
-
- &:disabled {
- background-color: $calendar-backgroud-color;
- color: $color-grey-light;
- }
- &:hover,
- &:focus {
- background-color: $calendar-backgroud-color;
- }
- }
- }
- .react-calendar__month-view__weekdays__weekday {
- color: $color-grey-dark;
- font-size: $calendar-font-size;
- font-weight: normal;
-
- abbr {
- text-decoration: none;
- text-transform: none;
- }
- }
- .react-calendar__tile {
- padding-top: 7px;
- padding-bottom: 7px;
- color: $color-grey-dark;
- font-size: $calendar-font-size;
+ .react-calendar__navigation {
+ margin-bottom: 0;
- &:disabled {
- background-color: $calendar-backgroud-color;
- color: $color-grey-light;
- }
- &.react-calendar__tile--now {
- background-color: $calendar-backgroud-color;
- }
- &.react-calendar__tile--active {
- background-color: $color-main;
- color: white;
- }
- &:hover {
- background-color: $color-grey-light;
- }
- }
+ .react-calendar__navigation__label {
+ background-color: $calendar-backgroud-color;
+ color: $color-main;
+ font-size: $calendar-font-size;
+ font-weight: bold;
+ text-transform: uppercase;
+ }
+ .react-calendar__navigation__arrow {
+ color: $color-grey-dark;
+
+ &:disabled {
+ background-color: $calendar-backgroud-color;
+ color: $color-grey-light;
+ }
+ &:hover,
+ &:focus {
+ background-color: $calendar-backgroud-color;
+ }
+ }
+ }
+ .react-calendar__month-view__weekdays__weekday {
+ color: $color-grey-dark;
+ font-size: $calendar-font-size;
+ font-weight: normal;
+
+ abbr {
+ text-decoration: none;
+ text-transform: none;
+ }
+ }
+ .react-calendar__tile {
+ padding-top: 7px;
+ padding-bottom: 7px;
+ color: $color-grey-dark;
+ font-size: $calendar-font-size;
+
+ &:disabled {
+ background-color: $calendar-backgroud-color;
+ color: $color-grey-light;
+ }
+ &.react-calendar__tile--now {
+ background-color: $calendar-backgroud-color;
+ }
+ &.react-calendar__tile--active {
+ background-color: $color-main;
+ color: white;
+ }
+ &:hover {
+ background-color: $color-grey-light;
}
+ }
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Form/form-fields/DateField/index.jsx b/ui/src/components/Form/form-fields/DateField/index.jsx
index e8904b29fa..375542df0d 100644
--- a/ui/src/components/Form/form-fields/DateField/index.jsx
+++ b/ui/src/components/Form/form-fields/DateField/index.jsx
@@ -1,55 +1,74 @@
-import React from 'react';
-import classnames from 'classnames';
-import { isNull, isEmpty } from 'lodash';
-import DateTimePicker from 'react-datetime-picker';
-import { useField } from 'formik';
-import Arrow from 'components/Arrow';
-import { formatDateBy } from 'utils/utils';
-import FieldError from 'components/Form/FieldError';
-import FieldLabel from 'components/Form/FieldLabel';
+import React from "react";
+import classnames from "classnames";
+import { isNull, isEmpty } from "lodash";
+import DateTimePicker from "react-datetime-picker";
+import { useField } from "formik";
+import Arrow from "components/Arrow";
+import { formatDateBy } from "utils/utils";
+import FieldError from "components/Form/FieldError";
+import FieldLabel from "components/Form/FieldLabel";
-import './date-field.scss';
+import "./date-field.scss";
const DateField = (props) => {
- const {label, className, tooltipText, displayFormat="MMM dd", valueFormat="YYYY-MM-DD", disabled, minDate} = props;
- const [field, meta, helpers] = useField(props);
- const {name, value} = field;
- const {setValue, setTouched} = helpers;
-
- const formattedValue = !!value ? new Date(value) : null;
+ const {
+ label,
+ className,
+ tooltipText,
+ displayFormat = "MMM dd",
+ valueFormat = "YYYY-MM-DD",
+ disabled,
+ minDate,
+ } = props;
+ const [field, meta, helpers] = useField(props);
+ const { name, value } = field;
+ const { setValue, setTouched } = helpers;
- return (
-
- )
-}
+ const formattedValue = !!value ? new Date(value) : null;
-export default DateField;
\ No newline at end of file
+ return (
+
+ );
+};
+
+export default DateField;
diff --git a/ui/src/components/Form/form-fields/FieldsPair/fields-pair.scss b/ui/src/components/Form/form-fields/FieldsPair/fields-pair.scss
index 0bfe57e16c..58f54e76ea 100644
--- a/ui/src/components/Form/form-fields/FieldsPair/fields-pair.scss
+++ b/ui/src/components/Form/form-fields/FieldsPair/fields-pair.scss
@@ -1,47 +1,47 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$action-width: 40px;
$actions-padding: 10px;
.form-field-wrapper.fields-pair-field-wrapper {
- .fields-wrapper {
- background: rgba(34, 37, 41, 0.05);
- padding: 20px;
- padding-bottom: 10px;
- margin-bottom: 10px;
+ .fields-wrapper {
+ background: rgba(34, 37, 41, 0.05);
+ padding: 20px;
+ padding-bottom: 10px;
+ margin-bottom: 10px;
- .form-field-wrapper {
- margin-bottom: 17px;
- }
- .field-with-actions-container {
- display: flex;
- justify-content: space-between;
-
- .form-field-wrapper {
- width: calc(100% - 2 * #{$action-width} - 2 * #{$actions-padding});
- }
- .actions-wrapper {
- display: flex;
+ .form-field-wrapper {
+ margin-bottom: 17px;
+ }
+ .field-with-actions-container {
+ display: flex;
+ justify-content: space-between;
+
+ .form-field-wrapper {
+ width: calc(100% - 2 * #{$action-width} - 2 * #{$actions-padding});
+ }
+ .actions-wrapper {
+ display: flex;
+
+ .field-action {
+ height: 36px;
+ width: $action-width;
+ border: 1px solid transparent;
+ border-radius: 5px;
+ background-color: $color-blue;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-left: $actions-padding;
+ cursor: pointer;
- .field-action {
- height: 36px;
- width: $action-width;
- border: 1px solid transparent;
- border-radius: 5px;
- background-color: $color-blue;
- box-sizing: border-box;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-left: $actions-padding;
- cursor: pointer;
-
- &.disabled {
- background-color: $color-grey-light;
- cursor: not-allowed;
- }
- }
- }
+ &.disabled {
+ background-color: $color-grey-light;
+ cursor: not-allowed;
+ }
}
+ }
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Form/form-fields/FieldsPair/index.jsx b/ui/src/components/Form/form-fields/FieldsPair/index.jsx
index 2215a35042..fda57661e1 100644
--- a/ui/src/components/Form/form-fields/FieldsPair/index.jsx
+++ b/ui/src/components/Form/form-fields/FieldsPair/index.jsx
@@ -1,111 +1,166 @@
-import React, { useEffect } from 'react';
-import classnames from 'classnames';
-import { isEmpty } from 'lodash';
-import { FieldArray, useField, useFormikContext } from 'formik';
-import { usePrevious } from 'hooks';
-import FieldLabel from 'components/Form/FieldLabel';
-import Icon, { ICON_NAMES } from 'components/Icon';
-
-import './fields-pair.scss';
-
-const FieldAction = ({iconName, onClick, disabled, count}) => (
-
-)
-
-const FieldItemWrapper = ({name, value, index, push, remove, replace, firstFieldProps, secondFieldProps, disabled, count}) => {
- const {component: FirstFieldComponent, emptyValue: firstEmptyValue="", ...firstProps} = firstFieldProps;
- const {component: SecondFieldComponent, getDependentFieldProps, emptyValue: secondEmptyValue="", ...secondProps} = secondFieldProps;
-
- const firstKey = firstProps.key;
- const secondKey = secondProps.key;
-
- const firstValue = value[index][firstKey];
- const prevFirstValue = usePrevious(firstValue);
-
- const prevCount = usePrevious(count);
-
- const allowRemove = value.length > 1;
-
- const formattedFirstProps = {
- ...firstProps,
- disabled
- };
- const formattedSecondProps = {
- ...secondProps,
- ...(!getDependentFieldProps ? {} : {...getDependentFieldProps(value[index]), index})
- };
- formattedSecondProps.disabled = disabled || formattedSecondProps.disabled || isEmpty(firstValue);
-
- useEffect(() => {
- if (count === prevCount && !!prevFirstValue && prevFirstValue !== firstValue) {
- replace(index, {...value[index], [secondKey]: secondEmptyValue});
- }
- }, [prevFirstValue, firstValue, index, value, secondKey, secondEmptyValue, replace, count, prevCount]);
-
- return (
-
-
-
-
- remove(index)}
- disabled={disabled || !allowRemove}
- />
- push({[firstKey]: firstEmptyValue, [secondKey]: secondEmptyValue})}
- disabled={disabled}
- />
-
-
-
+import React, { useEffect } from "react";
+import classnames from "classnames";
+import { isEmpty } from "lodash";
+import { FieldArray, useField, useFormikContext } from "formik";
+import { usePrevious } from "hooks";
+import FieldLabel from "components/Form/FieldLabel";
+import Icon, { ICON_NAMES } from "components/Icon";
+
+import "./fields-pair.scss";
+
+const FieldAction = ({ iconName, onClick, disabled, count }) => (
+
+
+
+);
+
+const FieldItemWrapper = ({
+ name,
+ value,
+ index,
+ push,
+ remove,
+ replace,
+ firstFieldProps,
+ secondFieldProps,
+ disabled,
+ count,
+}) => {
+ const {
+ component: FirstFieldComponent,
+ emptyValue: firstEmptyValue = "",
+ ...firstProps
+ } = firstFieldProps;
+ const {
+ component: SecondFieldComponent,
+ getDependentFieldProps,
+ emptyValue: secondEmptyValue = "",
+ ...secondProps
+ } = secondFieldProps;
+
+ const firstKey = firstProps.key;
+ const secondKey = secondProps.key;
+
+ const firstValue = value[index][firstKey];
+ const prevFirstValue = usePrevious(firstValue);
+
+ const prevCount = usePrevious(count);
+
+ const allowRemove = value.length > 1;
+
+ const formattedFirstProps = {
+ ...firstProps,
+ disabled,
+ };
+ const formattedSecondProps = {
+ ...secondProps,
+ ...(!getDependentFieldProps
+ ? {}
+ : { ...getDependentFieldProps(value[index]), index }),
+ };
+ formattedSecondProps.disabled =
+ disabled || formattedSecondProps.disabled || isEmpty(firstValue);
+
+ useEffect(() => {
+ if (
+ count === prevCount &&
+ !!prevFirstValue &&
+ prevFirstValue !== firstValue
+ ) {
+ replace(index, { ...value[index], [secondKey]: secondEmptyValue });
+ }
+ }, [
+ prevFirstValue,
+ firstValue,
+ index,
+ value,
+ secondKey,
+ secondEmptyValue,
+ replace,
+ count,
+ prevCount,
+ ]);
+
+ return (
+
+
+
+
+ remove(index)}
+ disabled={disabled || !allowRemove}
+ />
+
+ push({
+ [firstKey]: firstEmptyValue,
+ [secondKey]: secondEmptyValue,
+ })
+ }
+ disabled={disabled}
+ />
- );
-}
+
+
+
+ );
+};
const FieldsPair = (props) => {
- const {label, className, tooltipText} = props;
- const [field] = useField(props);
-
- const {name, value} = field;
-
- const {validateForm} = useFormikContext();
-
- useEffect(() => {
- validateForm();
- }, [value, validateForm])
-
- return (
-
- {!isEmpty(label) && {label}}
-
- {({remove, push, replace}) => value.map((item, index) => (
-
- ))}
-
-
- )
-}
+ const { label, className, tooltipText } = props;
+ const [field] = useField(props);
+
+ const { name, value } = field;
+
+ const { validateForm } = useFormikContext();
+
+ useEffect(() => {
+ validateForm();
+ }, [value, validateForm]);
+
+ return (
+
+ {!isEmpty(label) && (
+
+ {label}
+
+ )}
+
+ {({ remove, push, replace }) =>
+ value.map((item, index) => (
+
+ ))
+ }
+
+
+ );
+};
-export default FieldsPair;
\ No newline at end of file
+export default FieldsPair;
diff --git a/ui/src/components/Form/form-fields/MultiselectField/index.jsx b/ui/src/components/Form/form-fields/MultiselectField/index.jsx
index d731627b37..490992f35c 100644
--- a/ui/src/components/Form/form-fields/MultiselectField/index.jsx
+++ b/ui/src/components/Form/form-fields/MultiselectField/index.jsx
@@ -1,100 +1,145 @@
-import React, { useState, useEffect, useMemo } from 'react';
-import { cloneDeep, isEqual, isEmpty } from 'lodash';
-import classnames from 'classnames';
-import { useField } from 'formik';
-import { components } from 'react-select';
-import DropdownSelect from 'components/DropdownSelect';
-import Loader from 'components/Loader';
-import FieldError from 'components/Form/FieldError';
-import FieldLabel from 'components/Form/FieldLabel';
-import { usePrevious } from 'hooks';
-
-import './multiselect-field.scss';
-
-const ConnectorMultiValueContainer = ({connector, ...props}) => (
-
-)
-
-const PrevixLabelControl = ({children, label, ...props}) => {
- const hasValue = !isEmpty(props.getValue());
-
- return (
-
- {hasValue && {label}}
- {children}
-
- )
-}
+import React, { useState, useEffect, useMemo } from "react";
+import { cloneDeep, isEqual, isEmpty } from "lodash";
+import classnames from "classnames";
+import { useField } from "formik";
+import { components } from "react-select";
+import DropdownSelect from "components/DropdownSelect";
+import Loader from "components/Loader";
+import FieldError from "components/Form/FieldError";
+import FieldLabel from "components/Form/FieldLabel";
+import { usePrevious } from "hooks";
+
+import "./multiselect-field.scss";
+
+const ConnectorMultiValueContainer = ({ connector, ...props }) => (
+
+);
+
+const PrevixLabelControl = ({ children, label, ...props }) => {
+ const hasValue = !isEmpty(props.getValue());
+
+ return (
+
+ {hasValue && {label}}
+ {children}
+
+ );
+};
const getMissingValueItemKeys = (valueKeys, items) => {
- const missingItems = valueKeys.filter(key => !items.find(item => item.value === key));
+ const missingItems = valueKeys.filter(
+ (key) => !items.find((item) => item.value === key),
+ );
- if(missingItems.length > 0) {
- items = cloneDeep(items);
- missingItems.forEach(item => {
- items.push({value: item, label: item});
- });
- }
+ if (missingItems.length > 0) {
+ items = cloneDeep(items);
+ missingItems.forEach((item) => {
+ items.push({ value: item, label: item });
+ });
+ }
- return items;
-}
+ return items;
+};
const MultiselectField = (props) => {
- const {items: fieldItems=[], placeholder, creatable=false, disabled, className, label, tooltipText, connector,
- prefixLabel, loading} = props;
- const [field, meta, helpers] = useField(props);
- const {value} = meta;
- const {name} = field;
- const {setValue} = helpers;
-
- const formattedItems = useMemo(() => (
- creatable ? getMissingValueItemKeys(value || [], fieldItems) : fieldItems
- ), [creatable, fieldItems, value]);
- const prevFormattedItems = usePrevious(formattedItems);
-
- const [items, setItems] = useState(formattedItems);
-
- useEffect(() => {
- if (!isEqual(formattedItems, prevFormattedItems)) {
- setItems(formattedItems);
- }
- }, [prevFormattedItems, formattedItems]);
-
- const selectedItems = items.filter(item => value?.includes(item.value));
-
- return (
-
- {!!label &&
{label}}
-
{
- const formattedSelectedItems = selectedItems || [];
- const valueKeys = formattedSelectedItems.map(item => item.value);
-
- if (creatable) {
- setItems(getMissingValueItemKeys(valueKeys, items));
- }
-
- setValue(valueKeys);
- }}
- creatable={creatable}
- disabled={disabled || loading}
- placeholder={placeholder}
- isMulti={true}
- components={{
- ...(connector ? {MultiValueContainer: props => } : {}),
- ...(prefixLabel ? {Control: props => } : {}),
- }}
- />
- {loading && }
- {meta.error && {meta.error}}
-
- )
-}
-
-export default MultiselectField;
\ No newline at end of file
+ const {
+ items: fieldItems = [],
+ placeholder,
+ creatable = false,
+ disabled,
+ className,
+ label,
+ tooltipText,
+ connector,
+ prefixLabel,
+ loading,
+ } = props;
+ const [field, meta, helpers] = useField(props);
+ const { value } = meta;
+ const { name } = field;
+ const { setValue } = helpers;
+
+ const formattedItems = useMemo(
+ () =>
+ creatable ? getMissingValueItemKeys(value || [], fieldItems) : fieldItems,
+ [creatable, fieldItems, value],
+ );
+ const prevFormattedItems = usePrevious(formattedItems);
+
+ const [items, setItems] = useState(formattedItems);
+
+ useEffect(() => {
+ if (!isEqual(formattedItems, prevFormattedItems)) {
+ setItems(formattedItems);
+ }
+ }, [prevFormattedItems, formattedItems]);
+
+ const selectedItems = items.filter((item) => value?.includes(item.value));
+
+ return (
+
+ {!!label && (
+
+ {label}
+
+ )}
+
{
+ const formattedSelectedItems = selectedItems || [];
+ const valueKeys = formattedSelectedItems.map((item) => item.value);
+
+ if (creatable) {
+ setItems(getMissingValueItemKeys(valueKeys, items));
+ }
+
+ setValue(valueKeys);
+ }}
+ creatable={creatable}
+ disabled={disabled || loading}
+ placeholder={placeholder}
+ isMulti={true}
+ components={{
+ ...(connector
+ ? {
+ MultiValueContainer: (props) => (
+
+ ),
+ }
+ : {}),
+ ...(prefixLabel
+ ? {
+ Control: (props) => (
+
+ ),
+ }
+ : {}),
+ }}
+ />
+ {loading && }
+ {meta.error && {meta.error}}
+
+ );
+};
+
+export default MultiselectField;
diff --git a/ui/src/components/Form/form-fields/MultiselectField/multiselect-field.scss b/ui/src/components/Form/form-fields/MultiselectField/multiselect-field.scss
index 1bbfbddafc..fdcb5e8962 100644
--- a/ui/src/components/Form/form-fields/MultiselectField/multiselect-field.scss
+++ b/ui/src/components/Form/form-fields/MultiselectField/multiselect-field.scss
@@ -1,22 +1,22 @@
.form-field-wrapper.multiselect-field-wrapper {
- position: relative;
-
- .dropdown-select__value-container {
- .multi-select-custom-item-with-connector {
- display: flex;
- align-items: center;
-
- .multi-select-connector {
- margin: 0 10px;
- }
- &:first-child .multi-select-connector {
- display: none;
- }
- }
+ position: relative;
+
+ .dropdown-select__value-container {
+ .multi-select-custom-item-with-connector {
+ display: flex;
+ align-items: center;
+
+ .multi-select-connector {
+ margin: 0 10px;
+ }
+ &:first-child .multi-select-connector {
+ display: none;
+ }
}
- .dropdown-select__control.multi-select-custom-control-with-label {
- .multi-select-control-label {
- margin-left: 10px;
- }
+ }
+ .dropdown-select__control.multi-select-custom-control-with-label {
+ .multi-select-control-label {
+ margin-left: 10px;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Form/form-fields/RadioField/index.jsx b/ui/src/components/Form/form-fields/RadioField/index.jsx
index 6d4e529869..c6f6a7c53a 100644
--- a/ui/src/components/Form/form-fields/RadioField/index.jsx
+++ b/ui/src/components/Form/form-fields/RadioField/index.jsx
@@ -1,37 +1,55 @@
-import React from 'react';
-import classnames from 'classnames';
-import { useField } from 'formik';
-import FieldError from 'components/Form/FieldError';
-import FieldLabel from 'components/Form/FieldLabel';
+import React from "react";
+import classnames from "classnames";
+import { useField } from "formik";
+import FieldError from "components/Form/FieldError";
+import FieldLabel from "components/Form/FieldLabel";
-import './radio-field.scss';
+import "./radio-field.scss";
-const RadioField = ({items, className, label, disabled, tooltipText, ...props}) => {
- const [field, meta, helpers] = useField(props);
- const {name} = field;
- const {setValue} = helpers;
-
- return (
-
- {!!label && {label}}
- {
- items.map(({value, label}) => (
-
- ))
- }
- {meta.touched && meta.error && {meta.error}}
-
- )
-}
+const RadioField = ({
+ items,
+ className,
+ label,
+ disabled,
+ tooltipText,
+ ...props
+}) => {
+ const [field, meta, helpers] = useField(props);
+ const { name } = field;
+ const { setValue } = helpers;
-export default RadioField;
\ No newline at end of file
+ return (
+
+ {!!label && (
+
+ {label}
+
+ )}
+ {items.map(({ value, label }) => (
+
+ ))}
+ {meta.touched && meta.error && {meta.error}}
+
+ );
+};
+
+export default RadioField;
diff --git a/ui/src/components/Form/form-fields/RadioField/radio-field.scss b/ui/src/components/Form/form-fields/RadioField/radio-field.scss
index c2cab9356a..8ea8a5ee5d 100644
--- a/ui/src/components/Form/form-fields/RadioField/radio-field.scss
+++ b/ui/src/components/Form/form-fields/RadioField/radio-field.scss
@@ -1,62 +1,62 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$selector-size: 20px;
$selector-inner-size: 14px;
$selector-inner-padding: 3px;
.form-field-wrapper.radio-field-wrapper {
- .radio-field-item {
- display: flex;
- align-items: center;
- position: relative;
- margin-bottom: 12px;
- padding-left: 22px;
- cursor: pointer;
+ .radio-field-item {
+ display: flex;
+ align-items: center;
+ position: relative;
+ margin-bottom: 12px;
+ padding-left: 22px;
+ cursor: pointer;
- &.disabled {
- cursor: not-allowed;
+ &.disabled {
+ cursor: not-allowed;
- .radio-text {
- color: $color-grey;
- }
- .checkmark {
- border-color: $color-grey;
+ .radio-text {
+ color: $color-grey;
+ }
+ .checkmark {
+ border-color: $color-grey;
- &:after {
- background-color: $color-grey !important;
- }
- }
+ &:after {
+ background-color: $color-grey !important;
}
- .radio-text {
- padding-left: 10px;
- -webkit-box-decoration-break: clone;
- box-decoration-break: clone;
- }
- input {
- position: absolute;
- opacity: 0;
- cursor: pointer;
+ }
+ }
+ .radio-text {
+ padding-left: 10px;
+ -webkit-box-decoration-break: clone;
+ box-decoration-break: clone;
+ }
+ input {
+ position: absolute;
+ opacity: 0;
+ cursor: pointer;
- &:checked ~ .checkmark:after {
- content: "";
- position: absolute;
- display: block;
- top: $selector-inner-padding;
- left: $selector-inner-padding;
- width: $selector-inner-size;
- height: $selector-inner-size;
- border-radius: 50px;
- background: $color-main-light;
- }
- }
- .checkmark {
- position: absolute;
- left: 0;
- height: $selector-size;
- width: $selector-size;
- background-color: white;
- border-radius: 50px;
- border: 1px solid $color-grey-light;
- }
+ &:checked ~ .checkmark:after {
+ content: "";
+ position: absolute;
+ display: block;
+ top: $selector-inner-padding;
+ left: $selector-inner-padding;
+ width: $selector-inner-size;
+ height: $selector-inner-size;
+ border-radius: 50px;
+ background: $color-main-light;
+ }
+ }
+ .checkmark {
+ position: absolute;
+ left: 0;
+ height: $selector-size;
+ width: $selector-size;
+ background-color: white;
+ border-radius: 50px;
+ border: 1px solid $color-grey-light;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Form/form-fields/SelectField/index.jsx b/ui/src/components/Form/form-fields/SelectField/index.jsx
index 541388ed04..45c969d546 100644
--- a/ui/src/components/Form/form-fields/SelectField/index.jsx
+++ b/ui/src/components/Form/form-fields/SelectField/index.jsx
@@ -1,81 +1,108 @@
-import React, { useEffect, useState, useMemo } from 'react';
-import { cloneDeep, isNull, isEqual } from 'lodash';
-import classnames from 'classnames';
-import { useField } from 'formik';
-import { usePrevious } from 'hooks';
-import DropdownSelect from 'components/DropdownSelect';
-import Loader from 'components/Loader';
-import FieldError from 'components/Form/FieldError';
-import FieldLabel from 'components/Form/FieldLabel';
+import React, { useEffect, useState, useMemo } from "react";
+import { cloneDeep, isNull, isEqual } from "lodash";
+import classnames from "classnames";
+import { useField } from "formik";
+import { usePrevious } from "hooks";
+import DropdownSelect from "components/DropdownSelect";
+import Loader from "components/Loader";
+import FieldError from "components/Form/FieldError";
+import FieldLabel from "components/Form/FieldLabel";
-import './select-field.scss';
+import "./select-field.scss";
const getMissingValueItemKeys = (valueKey, items) => {
- if (isNull(valueKey)) {
- return items;
- }
+ if (isNull(valueKey)) {
+ return items;
+ }
- const valueInItems = items.find(item => item.value === valueKey);
+ const valueInItems = items.find((item) => item.value === valueKey);
- if (!valueInItems) {
- items = cloneDeep(items);
- items.push({value: valueKey, label: valueKey});
- }
+ if (!valueInItems) {
+ items = cloneDeep(items);
+ items.push({ value: valueKey, label: valueKey });
+ }
- return items;
-}
+ return items;
+};
const SelectField = (props) => {
- const {items: fieldItems=[], placeholder, creatable=false, clearable=false, disabled, className, label, tooltipText,
- components={}, loading} = props;
- const [field, meta, helpers] = useField(props);
- const {value} = meta;
- const {name} = field;
- const {setValue, setTouched} = helpers;
-
- const formattedItems = useMemo(() => (
- creatable && value !== "" ? getMissingValueItemKeys(value, fieldItems) : fieldItems
- ), [creatable, fieldItems, value]);
- const prevFormattedItems = usePrevious(formattedItems);
-
- const [items, setItems] = useState(formattedItems);
+ const {
+ items: fieldItems = [],
+ placeholder,
+ creatable = false,
+ clearable = false,
+ disabled,
+ className,
+ label,
+ tooltipText,
+ components = {},
+ loading,
+ } = props;
+ const [field, meta, helpers] = useField(props);
+ const { value } = meta;
+ const { name } = field;
+ const { setValue, setTouched } = helpers;
+
+ const formattedItems = useMemo(
+ () =>
+ creatable && value !== ""
+ ? getMissingValueItemKeys(value, fieldItems)
+ : fieldItems,
+ [creatable, fieldItems, value],
+ );
+ const prevFormattedItems = usePrevious(formattedItems);
+
+ const [items, setItems] = useState(formattedItems);
+
+ useEffect(() => {
+ if (!isEqual(formattedItems, prevFormattedItems)) {
+ setItems(formattedItems);
+ }
+ }, [prevFormattedItems, formattedItems]);
+
+ const selectedValue = items.find((item) => item.value === value) || null;
+
+ return (
+
+ {!!label && (
+
+ {label}
+
+ )}
+
{
+ const { value } = selectedItem || {};
- useEffect(() => {
- if (!isEqual(formattedItems, prevFormattedItems)) {
- setItems(formattedItems);
- }
- }, [prevFormattedItems, formattedItems]);
+ if (creatable) {
+ setItems(getMissingValueItemKeys(value, items));
+ }
- const selectedValue = items.find(item => item.value === value) || null;
-
- return (
-
- {!!label && {label}}
- {
- const {value} = selectedItem || {};
-
- if (creatable) {
- setItems(getMissingValueItemKeys(value, items));
- }
-
- setTouched(true, true);
- setValue(value);
- }}
- onBlur={() => setTouched(true, true)}
- creatable={creatable}
- clearable={clearable}
- disabled={disabled || loading}
- placeholder={placeholder}
- components={components}
- />
- {loading && }
- {meta.touched && meta.error && {meta.error}}
-
- )
-}
+ setTouched(true, true);
+ setValue(value);
+ }}
+ onBlur={() => setTouched(true, true)}
+ creatable={creatable}
+ clearable={clearable}
+ disabled={disabled || loading}
+ placeholder={placeholder}
+ components={components}
+ />
+ {loading && }
+ {meta.touched && meta.error && {meta.error}}
+
+ );
+};
-export default SelectField;
\ No newline at end of file
+export default SelectField;
diff --git a/ui/src/components/Form/form-fields/SelectField/select-field.scss b/ui/src/components/Form/form-fields/SelectField/select-field.scss
index 21218c5a78..2e40cb989c 100644
--- a/ui/src/components/Form/form-fields/SelectField/select-field.scss
+++ b/ui/src/components/Form/form-fields/SelectField/select-field.scss
@@ -1,3 +1,3 @@
.form-field-wrapper.select-field-wrapper {
- position: relative;
-}
\ No newline at end of file
+ position: relative;
+}
diff --git a/ui/src/components/Form/form-fields/TextField/index.jsx b/ui/src/components/Form/form-fields/TextField/index.jsx
index bd4c410573..973b01bea8 100644
--- a/ui/src/components/Form/form-fields/TextField/index.jsx
+++ b/ui/src/components/Form/form-fields/TextField/index.jsx
@@ -1,21 +1,42 @@
-import React from 'react';
-import classnames from 'classnames';
-import { useField } from 'formik';
-import FieldError from 'components/Form/FieldError';
-import FieldLabel from 'components/Form/FieldLabel';
+import React from "react";
+import classnames from "classnames";
+import { useField } from "formik";
+import FieldError from "components/Form/FieldError";
+import FieldLabel from "components/Form/FieldLabel";
-import './text-field.scss';
+import "./text-field.scss";
-const TextField = ({className, label, disabled, tooltipText, type="text", placeholder, ...props}) => {
- const [field, meta] = useField(props);
-
- return (
-
- {!!label && {label}}
-
- {meta.touched && meta.error && {meta.error}}
-
- )
-}
+const TextField = ({
+ className,
+ label,
+ disabled,
+ tooltipText,
+ type = "text",
+ placeholder,
+ ...props
+}) => {
+ const [field, meta] = useField(props);
-export default TextField;
\ No newline at end of file
+ return (
+
+ {!!label && (
+
+ {label}
+
+ )}
+
+ {meta.touched && meta.error && {meta.error}}
+
+ );
+};
+
+export default TextField;
diff --git a/ui/src/components/Form/form-fields/TextField/text-field.scss b/ui/src/components/Form/form-fields/TextField/text-field.scss
index c0baa5eb91..40f543a21f 100644
--- a/ui/src/components/Form/form-fields/TextField/text-field.scss
+++ b/ui/src/components/Form/form-fields/TextField/text-field.scss
@@ -1,31 +1,31 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.form-field-wrapper.text-field {
- .form-field {
- color: $color-grey-black;
- border: 1px solid $color-grey-light;
- border-radius: 4px;
- padding: 2px 8px;
- box-sizing: border-box;
- outline: none;
- font-size: 14px;
- line-height: 18px;
- font-family: CiscoSansTT;
- background-color: white;
- height: $field-height;
- width: 100%;
+ .form-field {
+ color: $color-grey-black;
+ border: 1px solid $color-grey-light;
+ border-radius: 4px;
+ padding: 2px 8px;
+ box-sizing: border-box;
+ outline: none;
+ font-size: 14px;
+ line-height: 18px;
+ font-family: CiscoSansTT;
+ background-color: white;
+ height: $field-height;
+ width: 100%;
- &::placeholder {
- color: $color-grey-dark;
- }
- &:disabled {
- background-color: white;
- color: $color-grey;
- cursor: not-allowed;
+ &::placeholder {
+ color: $color-grey-dark;
+ }
+ &:disabled {
+ background-color: white;
+ color: $color-grey;
+ cursor: not-allowed;
- &::placeholder {
- color: $color-grey;
- }
- }
+ &::placeholder {
+ color: $color-grey;
+ }
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Form/form-fields/TimeField/index.jsx b/ui/src/components/Form/form-fields/TimeField/index.jsx
index eeee6ec8a4..f39673b5a8 100644
--- a/ui/src/components/Form/form-fields/TimeField/index.jsx
+++ b/ui/src/components/Form/form-fields/TimeField/index.jsx
@@ -1,26 +1,42 @@
-import React from 'react';
-import { isEmpty } from 'lodash';
-import classnames from 'classnames';
-import TimePicker from 'react-time-picker';
-import { useField } from 'formik';
-import FieldError from 'components/Form/FieldError';
-import FieldLabel from 'components/Form/FieldLabel';
+import React from "react";
+import { isEmpty } from "lodash";
+import classnames from "classnames";
+import TimePicker from "react-time-picker";
+import { useField } from "formik";
+import FieldError from "components/Form/FieldError";
+import FieldLabel from "components/Form/FieldLabel";
-import './time-field.scss';
+import "./time-field.scss";
const TimeField = (props) => {
- const {label, className, tooltipText} = props;
- const [field, meta, helpers] = useField(props);
- const {name, value} = field;
- const {setValue, setTouched} = helpers;
+ const { label, className, tooltipText } = props;
+ const [field, meta, helpers] = useField(props);
+ const { name, value } = field;
+ const { setValue, setTouched } = helpers;
- return (
-
setTouched(true, true)}>
- {!isEmpty(label) && {label}}
-
- {meta.touched && meta.error && {meta.error}}
-
- )
-}
+ return (
+
setTouched(true, true)}
+ >
+ {!isEmpty(label) && (
+
+ {label}
+
+ )}
+
+ {meta.touched && meta.error && {meta.error}}
+
+ );
+};
-export default TimeField;
\ No newline at end of file
+export default TimeField;
diff --git a/ui/src/components/Form/form-fields/TimeField/time-field.scss b/ui/src/components/Form/form-fields/TimeField/time-field.scss
index 10921c3b9b..5f28e96fe2 100644
--- a/ui/src/components/Form/form-fields/TimeField/time-field.scss
+++ b/ui/src/components/Form/form-fields/TimeField/time-field.scss
@@ -1,27 +1,27 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.form-field-wrapper.time-field-wrapper {
- .react-time-picker {
- height: 34px;
- border: 1px solid $color-grey-light;
- color: $color-grey-black;
- font-size: 14px;
+ .react-time-picker {
+ height: 34px;
+ border: 1px solid $color-grey-light;
+ color: $color-grey-black;
+ font-size: 14px;
- .react-time-picker__wrapper {
- border: none;
+ .react-time-picker__wrapper {
+ border: none;
- .react-time-picker__inputGroup {
- padding: 0 7px;
- min-width: 50px;
+ .react-time-picker__inputGroup {
+ padding: 0 7px;
+ min-width: 50px;
- input {
- outline: none;
-
- &::placeholder {
- color: $color-grey;
- }
- }
- }
+ input {
+ outline: none;
+
+ &::placeholder {
+ color: $color-grey;
+ }
}
+ }
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Form/form.scss b/ui/src/components/Form/form.scss
index 93ebe38c2c..0958b46af0 100644
--- a/ui/src/components/Form/form.scss
+++ b/ui/src/components/Form/form.scss
@@ -1,25 +1,25 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.form-wrapper {
- .main-error-message {
- font-size: 10px;
- line-height: 14px;
- color: $color-grey-black;
- background-color: $color-error-light;
- border: 1px solid $color-error;
- padding: 10px;
- margin-bottom: 30px;
- display: flex;
- align-items: center;
+ .main-error-message {
+ font-size: 10px;
+ line-height: 14px;
+ color: $color-grey-black;
+ background-color: $color-error-light;
+ border: 1px solid $color-error;
+ padding: 10px;
+ margin-bottom: 30px;
+ display: flex;
+ align-items: center;
- .icon {
- color: $color-error;
- margin-right: 10px;
- min-width: 22px;
- }
- }
- .form-field-wrapper {
- min-width: 200px;
- margin-bottom: 30px;
+ .icon {
+ color: $color-error;
+ margin-right: 10px;
+ min-width: 22px;
}
+ }
+ .form-field-wrapper {
+ min-width: 200px;
+ margin-bottom: 30px;
+ }
}
diff --git a/ui/src/components/Form/index.jsx b/ui/src/components/Form/index.jsx
index 186838cdf9..116c554e70 100644
--- a/ui/src/components/Form/index.jsx
+++ b/ui/src/components/Form/index.jsx
@@ -1,112 +1,155 @@
-import React, { useEffect } from 'react';
-import { Formik, Form, useFormikContext } from 'formik';
-import { isNull, isEmpty, cloneDeep } from 'lodash';
-import classnames from 'classnames';
-import Loader from 'components/Loader';
-import Button from 'components/Button';
-import Icon, { ICON_NAMES } from 'components/Icon';
-import { useFetch, FETCH_METHODS, usePrevious } from 'hooks';
-import * as validators from './validators';
-import SelectField from './form-fields/SelectField';
-import MultiselectField from './form-fields/MultiselectField';
-import TextField from './form-fields/TextField';
-import RadioField from './form-fields/RadioField';
-import FieldsPair from './form-fields/FieldsPair';
-import CheckboxField from './form-fields/CheckboxField';
-import DateField from './form-fields/DateField';
-import TimeField from './form-fields/TimeField';
-import CronField from './form-fields/CronField';
-import FieldLabel from './FieldLabel';
-
-import './form.scss';
+import React, { useEffect } from "react";
+import { Formik, Form, useFormikContext } from "formik";
+import { isNull, isEmpty, cloneDeep } from "lodash";
+import classnames from "classnames";
+import Loader from "components/Loader";
+import Button from "components/Button";
+import Icon, { ICON_NAMES } from "components/Icon";
+import { useFetch, FETCH_METHODS, usePrevious } from "hooks";
+import * as validators from "./validators";
+import SelectField from "./form-fields/SelectField";
+import MultiselectField from "./form-fields/MultiselectField";
+import TextField from "./form-fields/TextField";
+import RadioField from "./form-fields/RadioField";
+import FieldsPair from "./form-fields/FieldsPair";
+import CheckboxField from "./form-fields/CheckboxField";
+import DateField from "./form-fields/DateField";
+import TimeField from "./form-fields/TimeField";
+import CronField from "./form-fields/CronField";
+import FieldLabel from "./FieldLabel";
+
+import "./form.scss";
export {
- validators,
- useFormikContext,
- SelectField,
- MultiselectField,
- TextField,
- RadioField,
- CheckboxField,
- FieldsPair,
- DateField,
- TimeField,
- CronField,
- FieldLabel
-}
-
-const FormComponent = ({children, className, submitUrl, getSubmitParams, onSubmitSuccess, onSubmitError, saveButtonTitle="Finish", hideSaveButton=false}) => {
- const {values, isSubmitting, isValidating, setSubmitting, status, setStatus, isValid, setErrors} = useFormikContext();
-
- const [{loading, data, error}, submitFormData] = useFetch(submitUrl, {loadOnMount: false});
- const prevLoading = usePrevious(loading);
-
- const handleSubmit = () => {
- setSubmitting(true);
-
- const submitQueryParams = !!getSubmitParams ? getSubmitParams(cloneDeep(values)) : {};
- submitFormData({method: FETCH_METHODS.POST, submitData: values, ...submitQueryParams});
+ validators,
+ useFormikContext,
+ SelectField,
+ MultiselectField,
+ TextField,
+ RadioField,
+ CheckboxField,
+ FieldsPair,
+ DateField,
+ TimeField,
+ CronField,
+ FieldLabel,
+};
+
+const FormComponent = ({
+ children,
+ className,
+ submitUrl,
+ getSubmitParams,
+ onSubmitSuccess,
+ onSubmitError,
+ saveButtonTitle = "Finish",
+ hideSaveButton = false,
+}) => {
+ const {
+ values,
+ isSubmitting,
+ isValidating,
+ setSubmitting,
+ status,
+ setStatus,
+ isValid,
+ setErrors,
+ } = useFormikContext();
+
+ const [{ loading, data, error }, submitFormData] = useFetch(submitUrl, {
+ loadOnMount: false,
+ });
+ const prevLoading = usePrevious(loading);
+
+ const handleSubmit = () => {
+ setSubmitting(true);
+
+ const submitQueryParams = !!getSubmitParams
+ ? getSubmitParams(cloneDeep(values))
+ : {};
+ submitFormData({
+ method: FETCH_METHODS.POST,
+ submitData: values,
+ ...submitQueryParams,
+ });
+ };
+
+ useEffect(() => {
+ if (prevLoading && !loading) {
+ setSubmitting(false);
+ setStatus(null);
+
+ if (isNull(error)) {
+ if (!!onSubmitSuccess) {
+ onSubmitSuccess(data);
+ }
+ } else {
+ const { message, errors } = error;
+
+ if (!!message) {
+ setStatus(message);
+ }
+
+ if (!isEmpty(errors)) {
+ setErrors(errors);
+ }
+
+ if (!!onSubmitError) {
+ onSubmitError();
+ }
+ }
}
-
- useEffect(() => {
- if (prevLoading && !loading) {
- setSubmitting(false);
- setStatus(null);
-
- if (isNull(error)) {
- if (!!onSubmitSuccess) {
- onSubmitSuccess(data);
- }
- } else {
- const {message, errors} = error;
-
- if (!!message) {
- setStatus(message);
- }
-
- if (!isEmpty(errors)) {
- setErrors(errors);
- }
-
- if (!!onSubmitError) {
- onSubmitError();
- }
- }
- }
- }, [prevLoading, loading, error, data, setSubmitting, setStatus, onSubmitSuccess, setErrors, onSubmitError]);
-
- if (isSubmitting || loading) {
- return
;
- }
-
- const disableSubmitClick = isSubmitting || isValidating || !isValid;
-
- return (
-
- )
-}
-
-const FormWrapper = ({children, initialValues, validate, ...props}) => {
- return (
-
-
- {children}
-
-
- )
-}
-
-export default FormWrapper;
\ No newline at end of file
+ }, [
+ prevLoading,
+ loading,
+ error,
+ data,
+ setSubmitting,
+ setStatus,
+ onSubmitSuccess,
+ setErrors,
+ onSubmitError,
+ ]);
+
+ if (isSubmitting || loading) {
+ return
;
+ }
+
+ const disableSubmitClick = isSubmitting || isValidating || !isValid;
+
+ return (
+
+ );
+};
+
+const FormWrapper = ({ children, initialValues, validate, ...props }) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default FormWrapper;
diff --git a/ui/src/components/Form/validators.js b/ui/src/components/Form/validators.js
index fe3cc685df..c145d53dc6 100644
--- a/ui/src/components/Form/validators.js
+++ b/ui/src/components/Form/validators.js
@@ -1,15 +1,16 @@
-import { isEmpty, isNumber } from 'lodash';
+import { isEmpty, isNumber } from "lodash";
-export const validateRequired = value => (
- isEmpty(value) && !isNumber(value) && value !== 0 ? "This field is required" : undefined
-);
+export const validateRequired = (value) =>
+ isEmpty(value) && !isNumber(value) && value !== 0
+ ? "This field is required"
+ : undefined;
export const keyValueListValidator = (valuesList) => {
- const errorItem = valuesList.find(item => {
- const valueList = item.split("=");
+ const errorItem = valuesList.find((item) => {
+ const valueList = item.split("=");
- return valueList.length !== 2 || valueList.find(item => item === "");
- })
+ return valueList.length !== 2 || valueList.find((item) => item === "");
+ });
- return !!errorItem ? "Values must be in the key=value format" : null;
-}
\ No newline at end of file
+ return !!errorItem ? "Values must be in the key=value format" : null;
+};
diff --git a/ui/src/components/Icon/IconTemplates.jsx b/ui/src/components/Icon/IconTemplates.jsx
index a860dacb37..316d1cc192 100644
--- a/ui/src/components/Icon/IconTemplates.jsx
+++ b/ui/src/components/Icon/IconTemplates.jsx
@@ -1,11 +1,19 @@
-import React from 'react';
-import { ICON_NAMES } from './utils';
+import React from "react";
+import { ICON_NAMES } from "./utils";
const IconTemplates = () => (
-
+ 13.2073 10.3619 12.4546C10.3619 11.7019 9.75095 11.091 8.99822 11.091Z"
+ />
+
+
+
);
-export default IconTemplates;
\ No newline at end of file
+export default IconTemplates;
diff --git a/ui/src/components/Icon/icon.scss b/ui/src/components/Icon/icon.scss
index 4b5c9017b1..e9e0356cde 100644
--- a/ui/src/components/Icon/icon.scss
+++ b/ui/src/components/Icon/icon.scss
@@ -1,16 +1,16 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.icon {
- display: inline-block;
- stroke-width: 0;
- stroke: currentColor;
- fill: currentColor;
+ display: inline-block;
+ stroke-width: 0;
+ stroke: currentColor;
+ fill: currentColor;
- &.clickable:not(.disabled) {
- cursor: pointer;
- }
- &.disabled {
- cursor: not-allowed;
- color: $color-grey;
- }
-}
\ No newline at end of file
+ &.clickable:not(.disabled) {
+ cursor: pointer;
+ }
+ &.disabled {
+ cursor: not-allowed;
+ color: $color-grey;
+ }
+}
diff --git a/ui/src/components/Icon/index.jsx b/ui/src/components/Icon/index.jsx
index 9da9b90810..a8cb83627d 100644
--- a/ui/src/components/Icon/index.jsx
+++ b/ui/src/components/Icon/index.jsx
@@ -1,38 +1,42 @@
-import React from 'react';
-import classnames from 'classnames';
-import { isEmpty } from 'lodash';
-import { ICON_NAMES } from './utils';
-import IconTemplates from './IconTemplates';
+import React from "react";
+import classnames from "classnames";
+import { isEmpty } from "lodash";
+import { ICON_NAMES } from "./utils";
+import IconTemplates from "./IconTemplates";
-import './icon.scss';
+import "./icon.scss";
-export {
- ICON_NAMES,
- IconTemplates
-}
+export { ICON_NAMES, IconTemplates };
-const Icon = ({name, className, onClick, disabled, size=22, style={}}) => {
- if (!Object.values(ICON_NAMES).includes(name)) {
- console.error(`Icon name '${name}' does not exist`);
- }
-
- return (
-
- )
-}
+const Icon = ({
+ name,
+ className,
+ onClick,
+ disabled,
+ size = 22,
+ style = {},
+}) => {
+ if (!Object.values(ICON_NAMES).includes(name)) {
+ console.error(`Icon name '${name}' does not exist`);
+ }
-export default Icon;
\ No newline at end of file
+ return (
+
+ );
+};
+
+export default Icon;
diff --git a/ui/src/components/Icon/utils.js b/ui/src/components/Icon/utils.js
index e7e3d8c5f1..dd406fa4b0 100644
--- a/ui/src/components/Icon/utils.js
+++ b/ui/src/components/Icon/utils.js
@@ -1,34 +1,34 @@
export const ICON_NAMES = {
- DASHBOARD: "dashboard",
- SCANS: "scans",
- ASSETS: "assets",
- ASSET_SCANS: "asset-scans",
- FINDINGS: "findings",
- DUPLICATE: "duplicate",
- X_MARK: "x-mark",
- CHECK_MARK: "check-mark",
- BLOCK: "block",
- SORT: "sort",
- CHEVRON_RIGHT: "chevron-right",
- CHEVRON_RIGHT_DOUBLE: "chevron-right-double",
- ARROW_HEAD_LEFT: "arrow-head-left",
- ARROW_UP: "ARROW_UP",
- PLUS: "plus",
- MINUS: "minus",
- PLAY: "play",
- STOP: "stop",
- DELETE: "delete",
- EDIT: "edit",
- INFO: "info",
- SHIELD: "shield",
- WARNING: "warning",
- BOMB: "bomb",
- COG: "cog",
- KEY: "key",
- BUG: "bug",
- GHOST: "ghost",
- PACKAGE: "package",
- REFRESH: "refresh",
- STROKE: "stroke",
- FILTER: "filter"
-}
\ No newline at end of file
+ DASHBOARD: "dashboard",
+ SCANS: "scans",
+ ASSETS: "assets",
+ ASSET_SCANS: "asset-scans",
+ FINDINGS: "findings",
+ DUPLICATE: "duplicate",
+ X_MARK: "x-mark",
+ CHECK_MARK: "check-mark",
+ BLOCK: "block",
+ SORT: "sort",
+ CHEVRON_RIGHT: "chevron-right",
+ CHEVRON_RIGHT_DOUBLE: "chevron-right-double",
+ ARROW_HEAD_LEFT: "arrow-head-left",
+ ARROW_UP: "ARROW_UP",
+ PLUS: "plus",
+ MINUS: "minus",
+ PLAY: "play",
+ STOP: "stop",
+ DELETE: "delete",
+ EDIT: "edit",
+ INFO: "info",
+ SHIELD: "shield",
+ WARNING: "warning",
+ BOMB: "bomb",
+ COG: "cog",
+ KEY: "key",
+ BUG: "bug",
+ GHOST: "ghost",
+ PACKAGE: "package",
+ REFRESH: "refresh",
+ STROKE: "stroke",
+ FILTER: "filter",
+};
diff --git a/ui/src/components/IconWithTooltip/index.jsx b/ui/src/components/IconWithTooltip/index.jsx
index fa4d631a41..013c9141c2 100644
--- a/ui/src/components/IconWithTooltip/index.jsx
+++ b/ui/src/components/IconWithTooltip/index.jsx
@@ -1,13 +1,16 @@
-import React from 'react';
-import Icon from 'components/Icon';
-import { TooltipWrapper } from 'components/Tooltip';
+import React from "react";
+import Icon from "components/Icon";
+import { TooltipWrapper } from "components/Tooltip";
-const IconWithTooltip = ({tooltipId, tooltipText, size=22, ...props}) => (
-
-
-
-
-
-)
+const IconWithTooltip = ({ tooltipId, tooltipText, size = 22, ...props }) => (
+
+
+
+
+
+);
-export default IconWithTooltip;
\ No newline at end of file
+export default IconWithTooltip;
diff --git a/ui/src/components/InfoIcon/index.jsx b/ui/src/components/InfoIcon/index.jsx
index b4fe9ba442..8cf837d820 100644
--- a/ui/src/components/InfoIcon/index.jsx
+++ b/ui/src/components/InfoIcon/index.jsx
@@ -1,14 +1,18 @@
-import React from 'react';
-import classnames from 'classnames';
-import Icon, { ICON_NAMES } from 'components/Icon';
-import { TooltipWrapper } from 'components/Tooltip';
+import React from "react";
+import classnames from "classnames";
+import Icon, { ICON_NAMES } from "components/Icon";
+import { TooltipWrapper } from "components/Tooltip";
-import './info-icon.scss';
+import "./info-icon.scss";
-const InfoIcon = ({tooltipId, tooltipText, large=false}) => (
-
-
-
-)
+const InfoIcon = ({ tooltipId, tooltipText, large = false }) => (
+
+
+
+);
-export default InfoIcon;
\ No newline at end of file
+export default InfoIcon;
diff --git a/ui/src/components/InfoIcon/info-icon.scss b/ui/src/components/InfoIcon/info-icon.scss
index 2d2fa2a57a..24dcf37af9 100644
--- a/ui/src/components/InfoIcon/info-icon.scss
+++ b/ui/src/components/InfoIcon/info-icon.scss
@@ -1,27 +1,27 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$size: 12px;
$size-large: 14px;
.info-icon-wrapper {
- background-color: $color-grey-light;
- width: $size;
- height: $size;
- border-radius: 50%;
- display: inline-flex !important;
- align-items: center;
- justify-content: space-around;
-
- &.large {
- width: $size-large;
- height: $size-large;
- background-color: $color-main;
+ background-color: $color-grey-light;
+ width: $size;
+ height: $size;
+ border-radius: 50%;
+ display: inline-flex !important;
+ align-items: center;
+ justify-content: space-around;
+
+ &.large {
+ width: $size-large;
+ height: $size-large;
+ background-color: $color-main;
- .icon {
- color: white;
- }
- }
.icon {
- color: $color-grey-black;
+ color: white;
}
-}
\ No newline at end of file
+ }
+ .icon {
+ color: $color-grey-black;
+ }
+}
diff --git a/ui/src/components/LinksList/index.jsx b/ui/src/components/LinksList/index.jsx
index 18d6ea0104..36775df279 100644
--- a/ui/src/components/LinksList/index.jsx
+++ b/ui/src/components/LinksList/index.jsx
@@ -1,32 +1,36 @@
-import React from 'react';
-import { useNavigate } from 'react-router-dom';
-import Arrow, { ARROW_NAMES } from 'components/Arrow';
+import React from "react";
+import { useNavigate } from "react-router-dom";
+import Arrow, { ARROW_NAMES } from "components/Arrow";
-import './links-list.scss';
+import "./links-list.scss";
-const LinksList = ({items}) => {
- const navigate = useNavigate();
+const LinksList = ({ items }) => {
+ const navigate = useNavigate();
- const onItemClick = (path, callback) => {
- if (!!callback) {
- callback(path);
- }
-
- navigate(path);
+ const onItemClick = (path, callback) => {
+ if (!!callback) {
+ callback(path);
}
- return (
-
- {
- items.map(({path, component: Component, callback}, index) => (
-
onItemClick(path, callback)}>
-
-
-
- ))
- }
+ navigate(path);
+ };
+
+ return (
+
+ {items.map(({ path, component: Component, callback }, index) => (
+
onItemClick(path, callback)}
+ >
+
+
+
+
- )
-}
+ ))}
+
+ );
+};
-export default LinksList;
\ No newline at end of file
+export default LinksList;
diff --git a/ui/src/components/LinksList/links-list.scss b/ui/src/components/LinksList/links-list.scss
index b0c3a5d764..0842d48599 100644
--- a/ui/src/components/LinksList/links-list.scss
+++ b/ui/src/components/LinksList/links-list.scss
@@ -1,23 +1,23 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.links-list-wrapper {
- .links-list-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 10px 20px;
- min-height: 50px;
- box-sizing: border-box;
- box-shadow: 0px 0px 10px rgba(177, 171, 189, 0.2);
- border: 1px solid $color-grey-light;
- overflow: hidden;
- cursor: pointer;
+ .links-list-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 10px 20px;
+ min-height: 50px;
+ box-sizing: border-box;
+ box-shadow: 0px 0px 10px rgba(177, 171, 189, 0.2);
+ border: 1px solid $color-grey-light;
+ overflow: hidden;
+ cursor: pointer;
- &:not(:last-child) {
- margin-bottom: 10px;
- }
- &:hover {
- background-color: $color-blue-light;
- }
+ &:not(:last-child) {
+ margin-bottom: 10px;
}
-}
\ No newline at end of file
+ &:hover {
+ background-color: $color-blue-light;
+ }
+ }
+}
diff --git a/ui/src/components/ListAndDetailsRouter/index.jsx b/ui/src/components/ListAndDetailsRouter/index.jsx
index b6f61be128..17827dd76d 100644
--- a/ui/src/components/ListAndDetailsRouter/index.jsx
+++ b/ui/src/components/ListAndDetailsRouter/index.jsx
@@ -1,13 +1,17 @@
-import React from 'react';
-import { Route, Routes, Outlet } from 'react-router-dom';
+import React from "react";
+import { Route, Routes, Outlet } from "react-router-dom";
-const ListAndDetailsRouter = ({listComponent: ListComponent, detailsComponent: DetailsComponent, detailsPath=":id"}) => (
-
- }>
- } />
- } />
-
-
-)
+const ListAndDetailsRouter = ({
+ listComponent: ListComponent,
+ detailsComponent: DetailsComponent,
+ detailsPath = ":id",
+}) => (
+
+ }>
+ } />
+ } />
+
+
+);
-export default ListAndDetailsRouter;
\ No newline at end of file
+export default ListAndDetailsRouter;
diff --git a/ui/src/components/Loader/index.jsx b/ui/src/components/Loader/index.jsx
index f7cc9a2667..f3ae21d6e2 100644
--- a/ui/src/components/Loader/index.jsx
+++ b/ui/src/components/Loader/index.jsx
@@ -1,20 +1,20 @@
-import React from 'react';
-import classnames from 'classnames';
-import { SpinnerCircularFixed } from 'spinners-react';
+import React from "react";
+import classnames from "classnames";
+import { SpinnerCircularFixed } from "spinners-react";
-import COLORS from 'utils/scss_variables.module.scss';
+import COLORS from "utils/scss_variables.module.scss";
-import './loader.scss';
+import "./loader.scss";
-const Loader = ({large=false, small=false, absolute=true}) => (
-
+const Loader = ({ large = false, small = false, absolute = true }) => (
+
);
-export default Loader;
\ No newline at end of file
+export default Loader;
diff --git a/ui/src/components/Loader/loader.scss b/ui/src/components/Loader/loader.scss
index 9f2e59a59b..f0d11d46e4 100644
--- a/ui/src/components/Loader/loader.scss
+++ b/ui/src/components/Loader/loader.scss
@@ -1,8 +1,8 @@
.clarity-loader {
- &.absolute {
- position: absolute;
- top: 50%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
-}
\ No newline at end of file
+ &.absolute {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+}
diff --git a/ui/src/components/Modal/index.jsx b/ui/src/components/Modal/index.jsx
index 83fc60368c..2ee0c0fef5 100644
--- a/ui/src/components/Modal/index.jsx
+++ b/ui/src/components/Modal/index.jsx
@@ -1,55 +1,93 @@
-import React, { useEffect, useState } from 'react';
-import ReactDOM from 'react-dom';
-import classnames from 'classnames';
-import CloseButton from 'components/CloseButton';
-import Button from 'components/Button';
-import Title from 'components/Title';
+import React, { useEffect, useState } from "react";
+import ReactDOM from "react-dom";
+import classnames from "classnames";
+import CloseButton from "components/CloseButton";
+import Button from "components/Button";
+import Title from "components/Title";
-import './modal.scss';
+import "./modal.scss";
const Modal = (props) => {
- const {title, isMediumTitle=false, children, onClose, className, height=380, width=720, stickLeft=false, onDone, doneTitle="Done", disableDone=false, hideCancel=false,
- hideSubmit=false} = props;
+ const {
+ title,
+ isMediumTitle = false,
+ children,
+ onClose,
+ className,
+ height = 380,
+ width = 720,
+ stickLeft = false,
+ onDone,
+ doneTitle = "Done",
+ disableDone = false,
+ hideCancel = false,
+ hideSubmit = false,
+ } = props;
- const [portalContainer, setPortalContainer] = useState(null);
+ const [portalContainer, setPortalContainer] = useState(null);
- useEffect(() => {
- const container = document.querySelector("#main-wrapper");
+ useEffect(() => {
+ const container = document.querySelector("#main-wrapper");
- if (!container) {
- return;
- }
-
- setPortalContainer(container);
- }, []);
-
- if (!portalContainer) {
- return null;
+ if (!container) {
+ return;
}
- return ReactDOM.createPortal(
-
{
- event.stopPropagation();
- event.preventDefault();
-
- onClose();
- }}>
-
event.stopPropagation()}
+ setPortalContainer(container);
+ }, []);
+
+ if (!portalContainer) {
+ return null;
+ }
+
+ return ReactDOM.createPortal(
+
{
+ event.stopPropagation();
+ event.preventDefault();
+
+ onClose();
+ }}
+ >
+
event.stopPropagation()}
+ >
+
+ {title}
+
+
{children}
+
+
+ {!hideCancel && (
+
+ )}
+ {!hideSubmit && (
+
-
,
- portalContainer
- );
-}
-
-export default Modal;
\ No newline at end of file
+ {doneTitle}
+
+ )}
+
+
+
,
+ portalContainer,
+ );
+};
+
+export default Modal;
diff --git a/ui/src/components/Modal/modal.scss b/ui/src/components/Modal/modal.scss
index 8ec5c5c26d..6c3e676864 100644
--- a/ui/src/components/Modal/modal.scss
+++ b/ui/src/components/Modal/modal.scss
@@ -1,53 +1,53 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$padding: 30px;
.modal-outer-wrapper {
- position: absolute;
- top: 0;
- left: $side-bar-width;
- width: calc(100% - #{$side-bar-width});
- height: 100%;
- background-color: rgba(#E4E6E8, 0.8);
- z-index: 1;
+ position: absolute;
+ top: 0;
+ left: $side-bar-width;
+ width: calc(100% - #{$side-bar-width});
+ height: 100%;
+ background-color: rgba(#e4e6e8, 0.8);
+ z-index: 1;
- .modal-inner-wrapper {
- position: absolute;
- background: white;
- box-sizing: border-box;
- box-shadow: -8px 12px 19px rgba(55, 72, 95, 0.13);
+ .modal-inner-wrapper {
+ position: absolute;
+ background: white;
+ box-sizing: border-box;
+ box-shadow: -8px 12px 19px rgba(55, 72, 95, 0.13);
- &:not(.stick-left) {
- top: 40%;
- left: 50%;
- transform: translate(-50%, -50%);
- }
- &.stick-left {
- top: 0;
- bottom: 0;
- left: 0;
- }
- .close-button {
- position: absolute;
- top: $padding;
- right: $padding;
- }
- .modal-content {
- font-size: 14px;
- line-height: 18px;
- color: $color-grey-black;
- }
- .modal-actions {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- position: absolute;
- right: $padding;
- bottom: $padding;
+ &:not(.stick-left) {
+ top: 40%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ }
+ &.stick-left {
+ top: 0;
+ bottom: 0;
+ left: 0;
+ }
+ .close-button {
+ position: absolute;
+ top: $padding;
+ right: $padding;
+ }
+ .modal-content {
+ font-size: 14px;
+ line-height: 18px;
+ color: $color-grey-black;
+ }
+ .modal-actions {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ position: absolute;
+ right: $padding;
+ bottom: $padding;
- button:not(:last-child) {
- margin-right: 10px;
- }
- }
+ button:not(:last-child) {
+ margin-right: 10px;
+ }
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Notification/index.jsx b/ui/src/components/Notification/index.jsx
index 3254314902..b7ea8b6d5c 100644
--- a/ui/src/components/Notification/index.jsx
+++ b/ui/src/components/Notification/index.jsx
@@ -1,41 +1,46 @@
-import React, { useEffect, useState } from 'react';
-import ReactDOM from 'react-dom';
-import classnames from 'classnames';
-import CloseButton from 'components/CloseButton';
+import React, { useEffect, useState } from "react";
+import ReactDOM from "react-dom";
+import classnames from "classnames";
+import CloseButton from "components/CloseButton";
-import './notification.scss';
+import "./notification.scss";
export const NOTIFICATION_TYPES = {
- INFO: "INFO",
- ERROR: "ERROR"
-}
+ INFO: "INFO",
+ ERROR: "ERROR",
+};
-const Notification = ({message, type, onClose}) => {
- const [portalContainer, setPortalContainer] = useState(null);
+const Notification = ({ message, type, onClose }) => {
+ const [portalContainer, setPortalContainer] = useState(null);
- useEffect(() => {
- const container = document.querySelector("main");
+ useEffect(() => {
+ const container = document.querySelector("main");
- if (!container) {
- return;
- }
-
- setPortalContainer(container);
- }, []);
-
- if (!portalContainer) {
- return null;
+ if (!container) {
+ return;
}
- const notificationType = type || NOTIFICATION_TYPES.INFO;
-
- return ReactDOM.createPortal(
-
,
- portalContainer
- );
-}
-
-export default Notification;
\ No newline at end of file
+ setPortalContainer(container);
+ }, []);
+
+ if (!portalContainer) {
+ return null;
+ }
+
+ const notificationType = type || NOTIFICATION_TYPES.INFO;
+
+ return ReactDOM.createPortal(
+
,
+ portalContainer,
+ );
+};
+
+export default Notification;
diff --git a/ui/src/components/Notification/notification.scss b/ui/src/components/Notification/notification.scss
index edb32b981b..b816671662 100644
--- a/ui/src/components/Notification/notification.scss
+++ b/ui/src/components/Notification/notification.scss
@@ -1,25 +1,25 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.notification-wrapper {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- background-color: $color-blue;
- color: $color-grey-black;
- height: 50px;
- font-size: 12px;
- line-height: 18px;
- display: flex;
- align-items: center;
- padding: 0 30px;
- z-index: 3;
- filter: drop-shadow(0px 5px 10px rgba(34, 43, 54, 0.11));
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ background-color: $color-blue;
+ color: $color-grey-black;
+ height: 50px;
+ font-size: 12px;
+ line-height: 18px;
+ display: flex;
+ align-items: center;
+ padding: 0 30px;
+ z-index: 3;
+ filter: drop-shadow(0px 5px 10px rgba(34, 43, 54, 0.11));
- &.error {
- background-color: $color-error-light;
- }
- .notification-content {
- width: 100%;
- }
-}
\ No newline at end of file
+ &.error {
+ background-color: $color-error-light;
+ }
+ .notification-content {
+ width: 100%;
+ }
+}
diff --git a/ui/src/components/ProgressBar/index.jsx b/ui/src/components/ProgressBar/index.jsx
index 64deb42377..97e88b8fb1 100644
--- a/ui/src/components/ProgressBar/index.jsx
+++ b/ui/src/components/ProgressBar/index.jsx
@@ -1,43 +1,82 @@
-import React from 'react';
-import classnames from 'classnames';
-import Icon, { ICON_NAMES } from 'components/Icon';
-import IconWithTooltip from 'components/IconWithTooltip';
+import React from "react";
+import classnames from "classnames";
+import Icon, { ICON_NAMES } from "components/Icon";
+import IconWithTooltip from "components/IconWithTooltip";
-import COLORS from 'utils/scss_variables.module.scss';
+import COLORS from "utils/scss_variables.module.scss";
-import './progress-bar.scss';
+import "./progress-bar.scss";
export const STATUS_MAPPPING = {
- IN_PROGRESS: {value: "IN_PROGRESS", color: COLORS["color-main"]},
- SUCCESS: {value: "SUCCESS", icon: ICON_NAMES.CHECK_MARK, color: COLORS["color-success"]},
- ERROR: {value: "ERROR", icon: ICON_NAMES.X_MARK, color: COLORS["color-error"]},
- STOPPED: {value: "STOPPED", icon: ICON_NAMES.BLOCK, color: COLORS["color-grey"]},
- WARNING: {value: "WARNING", icon: ICON_NAMES.WARNING, color: COLORS["color-success"], iconColor: COLORS["color-warning"]}
-}
+ IN_PROGRESS: { value: "IN_PROGRESS", color: COLORS["color-main"] },
+ SUCCESS: {
+ value: "SUCCESS",
+ icon: ICON_NAMES.CHECK_MARK,
+ color: COLORS["color-success"],
+ },
+ ERROR: {
+ value: "ERROR",
+ icon: ICON_NAMES.X_MARK,
+ color: COLORS["color-error"],
+ },
+ STOPPED: {
+ value: "STOPPED",
+ icon: ICON_NAMES.BLOCK,
+ color: COLORS["color-grey"],
+ },
+ WARNING: {
+ value: "WARNING",
+ icon: ICON_NAMES.WARNING,
+ color: COLORS["color-success"],
+ iconColor: COLORS["color-warning"],
+ },
+};
-const ProgressBar = ({status=STATUS_MAPPPING.IN_PROGRESS.value, itemsCompleted=0, itemsLeft=0, width="100%", message=null, messageTooltipId=null, customeTitle}) => {
- const totalItems = itemsCompleted + itemsLeft;
- const percent = status === STATUS_MAPPPING.IN_PROGRESS.value ? (!!totalItems ? Math.round((itemsCompleted / totalItems) * 100) : 0) : 100;
+const ProgressBar = ({
+ status = STATUS_MAPPPING.IN_PROGRESS.value,
+ itemsCompleted = 0,
+ itemsLeft = 0,
+ width = "100%",
+ message = null,
+ messageTooltipId = null,
+ customeTitle,
+}) => {
+ const totalItems = itemsCompleted + itemsLeft;
+ const percent =
+ status === STATUS_MAPPPING.IN_PROGRESS.value
+ ? !!totalItems
+ ? Math.round((itemsCompleted / totalItems) * 100)
+ : 0
+ : 100;
- const {icon, color, iconColor} = STATUS_MAPPPING[status];
- const progressIconColor = iconColor || color;
- const IconComponent = !!message ? IconWithTooltip : Icon;
+ const { icon, color, iconColor } = STATUS_MAPPPING[status];
+ const progressIconColor = iconColor || color;
+ const IconComponent = !!message ? IconWithTooltip : Icon;
- return (
-
-
- {!icon ?
{!!customeTitle ? customeTitle : `${percent}%`}
:
-
- }
+ return (
+
+
+ {!icon ? (
+
+ {!!customeTitle ? customeTitle : `${percent}%`}
- )
-}
+ ) : (
+
+ )}
+
+ );
+};
-export default ProgressBar;
\ No newline at end of file
+export default ProgressBar;
diff --git a/ui/src/components/ProgressBar/progress-bar.scss b/ui/src/components/ProgressBar/progress-bar.scss
index adaf93cc34..9f00fdd857 100644
--- a/ui/src/components/ProgressBar/progress-bar.scss
+++ b/ui/src/components/ProgressBar/progress-bar.scss
@@ -1,29 +1,29 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$loader-border-radius: 3px;
.progress-bar-wrapper {
- display: flex;
- align-items: center;
- width: 100%;
+ display: flex;
+ align-items: center;
+ width: 100%;
- .progress-bar-container {
- position: relative;
- height: 6px;
- background-color: $color-grey-light;
- border-radius: $loader-border-radius;
+ .progress-bar-container {
+ position: relative;
+ height: 6px;
+ background-color: $color-grey-light;
+ border-radius: $loader-border-radius;
- .progress-bar-filler {
- border-radius: $loader-border-radius;
- height: 100%;
- }
+ .progress-bar-filler {
+ border-radius: $loader-border-radius;
+ height: 100%;
}
- .progress-bar-title,
- .icon {
- margin-left: 10px;
- }
- .progress-bar-title {
- font-size: 14px;
- line-height: 16px;
- }
-}
\ No newline at end of file
+ }
+ .progress-bar-title,
+ .icon {
+ margin-left: 10px;
+ }
+ .progress-bar-title {
+ font-size: 14px;
+ line-height: 16px;
+ }
+}
diff --git a/ui/src/components/ScanProgressBar/index.jsx b/ui/src/components/ScanProgressBar/index.jsx
index d3f30816cc..f9c3e77258 100644
--- a/ui/src/components/ScanProgressBar/index.jsx
+++ b/ui/src/components/ScanProgressBar/index.jsx
@@ -1,51 +1,103 @@
-import React from 'react';
-import ProgressBar, { STATUS_MAPPPING } from 'components/ProgressBar';
-import ErrorMessageDisplay from 'components/ErrorMessageDisplay';
+import React from "react";
+import ProgressBar, { STATUS_MAPPPING } from "components/ProgressBar";
+import ErrorMessageDisplay from "components/ErrorMessageDisplay";
-import './scan-progress-bar.scss';
+import "./scan-progress-bar.scss";
export const SCAN_STATES = {
- Pending: {state: "Pending", title: "Pending"},
- Discovered: {state: "Discovered", title: "Discovered"},
- InProgress: {state: "InProgress", title: "In progress"},
- Failed: {state: "Failed", title: "Failed"},
- Done: {state: "Done", title: "Done"},
- Aborted: {state: "Aborted", title: "Aborted"},
-}
+ Pending: { state: "Pending", title: "Pending" },
+ Discovered: { state: "Discovered", title: "Discovered" },
+ InProgress: { state: "InProgress", title: "In progress" },
+ Failed: { state: "Failed", title: "Failed" },
+ Done: { state: "Done", title: "Done" },
+ Aborted: { state: "Aborted", title: "Aborted" },
+};
const SCAN_STATES_AND_REASONS_MAPPINGS = [
- {...SCAN_STATES.Pending, reason: "Created", status: STATUS_MAPPPING.IN_PROGRESS.value},
- {...SCAN_STATES.Discovered, reason: "AssetsDiscovered", status: STATUS_MAPPPING.IN_PROGRESS.value},
- {...SCAN_STATES.InProgress, reason: "AssetScansRunning", status: STATUS_MAPPPING.IN_PROGRESS.value},
- {...SCAN_STATES.Failed, reason: "Cancellation", status: STATUS_MAPPPING.STOPPED.value},
- {...SCAN_STATES.Failed, reason: "Timeout", status: STATUS_MAPPPING.ERROR.value, errorTitle: "Scan has been timed out"},
- {...SCAN_STATES.Failed, reason: "AssetScanFailed", status: STATUS_MAPPPING.ERROR.value, errorTitle: "Some of the elements were failed to be scanned"},
- {...SCAN_STATES.Done, reason: "NothingToScan", status: STATUS_MAPPPING.SUCCESS.value},
- {...SCAN_STATES.Done, reason: "Success", status: STATUS_MAPPPING.SUCCESS.value},
- {...SCAN_STATES.Aborted, reason: "Cancellation", status: STATUS_MAPPPING.STOPPED.value}
+ {
+ ...SCAN_STATES.Pending,
+ reason: "Created",
+ status: STATUS_MAPPPING.IN_PROGRESS.value,
+ },
+ {
+ ...SCAN_STATES.Discovered,
+ reason: "AssetsDiscovered",
+ status: STATUS_MAPPPING.IN_PROGRESS.value,
+ },
+ {
+ ...SCAN_STATES.InProgress,
+ reason: "AssetScansRunning",
+ status: STATUS_MAPPPING.IN_PROGRESS.value,
+ },
+ {
+ ...SCAN_STATES.Failed,
+ reason: "Cancellation",
+ status: STATUS_MAPPPING.STOPPED.value,
+ },
+ {
+ ...SCAN_STATES.Failed,
+ reason: "Timeout",
+ status: STATUS_MAPPPING.ERROR.value,
+ errorTitle: "Scan has been timed out",
+ },
+ {
+ ...SCAN_STATES.Failed,
+ reason: "AssetScanFailed",
+ status: STATUS_MAPPPING.ERROR.value,
+ errorTitle: "Some of the elements were failed to be scanned",
+ },
+ {
+ ...SCAN_STATES.Done,
+ reason: "NothingToScan",
+ status: STATUS_MAPPPING.SUCCESS.value,
+ },
+ {
+ ...SCAN_STATES.Done,
+ reason: "Success",
+ status: STATUS_MAPPPING.SUCCESS.value,
+ },
+ {
+ ...SCAN_STATES.Aborted,
+ reason: "Cancellation",
+ status: STATUS_MAPPPING.STOPPED.value,
+ },
];
-const ScanProgressBar = ({itemsCompleted, itemsLeft, state, stateReason, stateMessage, barWidth, isMinimized=false, minimizedTooltipId=null}) => {
- const {status, errorTitle} = SCAN_STATES_AND_REASONS_MAPPINGS.find(item => item.state === state && item.reason === stateReason) || {};
+const ScanProgressBar = ({
+ itemsCompleted,
+ itemsLeft,
+ state,
+ stateReason,
+ stateMessage,
+ barWidth,
+ isMinimized = false,
+ minimizedTooltipId = null,
+}) => {
+ const { status, errorTitle } =
+ SCAN_STATES_AND_REASONS_MAPPINGS.find(
+ (item) => item.state === state && item.reason === stateReason,
+ ) || {};
- return (
-
-
- {!isMinimized && errorTitle &&
-
-
{errorTitle}
-
{stateMessage || errorTitle}
-
- }
+ return (
+
+
+ {!isMinimized && errorTitle && (
+
+
{errorTitle}
+
+ {stateMessage || errorTitle}
+
- )
-}
+ )}
+
+ );
+};
-export default ScanProgressBar;
\ No newline at end of file
+export default ScanProgressBar;
diff --git a/ui/src/components/ScanProgressBar/scan-progress-bar.scss b/ui/src/components/ScanProgressBar/scan-progress-bar.scss
index 6996f35979..5b470c2d65 100644
--- a/ui/src/components/ScanProgressBar/scan-progress-bar.scss
+++ b/ui/src/components/ScanProgressBar/scan-progress-bar.scss
@@ -1,13 +1,13 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.scan-progres-bar-wrapper {
- .error-display-wrapper {
- margin-top: 25px;
+ .error-display-wrapper {
+ margin-top: 25px;
- .error-display-title {
- font-weight: 400;
- font-size: 14px;
- line-height: 18px;
- }
+ .error-display-title {
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 18px;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/SeverityDisplay/index.jsx b/ui/src/components/SeverityDisplay/index.jsx
index eb153985ea..22d3303474 100644
--- a/ui/src/components/SeverityDisplay/index.jsx
+++ b/ui/src/components/SeverityDisplay/index.jsx
@@ -1,30 +1,40 @@
-import React from 'react';
-import Icon from 'components/Icon';
-import { toCapitalized } from 'utils/utils';
-import { VULNERABIITY_FINDINGS_ITEM } from 'utils/systemConsts';
+import React from "react";
+import Icon from "components/Icon";
+import { toCapitalized } from "utils/utils";
+import { VULNERABIITY_FINDINGS_ITEM } from "utils/systemConsts";
-import './severity-display.scss';
+import "./severity-display.scss";
-import COLORS from 'utils/scss_variables.module.scss';
+import COLORS from "utils/scss_variables.module.scss";
export const SEVERITY_ITEMS = {
- CRITICAL: {valueKey: "CRITICAL", color: COLORS["color-error-dark"]},
- HIGH: {valueKey: "HIGH", color: COLORS["color-error"]},
- MEDIUM: {valueKey: "MEDIUM", color: COLORS["color-warning"]},
- LOW: {valueKey: "LOW", color: COLORS["color-warning-low"]},
- NEGLIGIBLE: {valueKey: "NEGLIGIBLE", color: COLORS["color-status-blue"]},
- NONE: {valueKey: "NONE", color: COLORS["color-status-blue"]}
-}
+ CRITICAL: { valueKey: "CRITICAL", color: COLORS["color-error-dark"] },
+ HIGH: { valueKey: "HIGH", color: COLORS["color-error"] },
+ MEDIUM: { valueKey: "MEDIUM", color: COLORS["color-warning"] },
+ LOW: { valueKey: "LOW", color: COLORS["color-warning-low"] },
+ NEGLIGIBLE: { valueKey: "NEGLIGIBLE", color: COLORS["color-status-blue"] },
+ NONE: { valueKey: "NONE", color: COLORS["color-status-blue"] },
+};
-const SeverityDisplay = ({severity, score}) => {
- const {color} = SEVERITY_ITEMS[severity];
+const SeverityDisplay = ({ severity, score }) => {
+ const { color } = SEVERITY_ITEMS[severity];
- return (
-
- {!!score ?
{score}
:
}
-
{toCapitalized(severity)}
-
- )
-}
+ return (
+
+ {!!score ? (
+
{score}
+ ) : (
+
+ )}
+
+ {toCapitalized(severity)}
+
+
+ );
+};
-export default SeverityDisplay;
\ No newline at end of file
+export default SeverityDisplay;
diff --git a/ui/src/components/SeverityDisplay/severity-display.scss b/ui/src/components/SeverityDisplay/severity-display.scss
index 2f024a7c97..683c16dea4 100644
--- a/ui/src/components/SeverityDisplay/severity-display.scss
+++ b/ui/src/components/SeverityDisplay/severity-display.scss
@@ -1,19 +1,19 @@
.severity-display {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
- .severity-title {
- margin-left: 5px;
- }
+ .severity-title {
+ margin-left: 5px;
+ }
}
.severity-with-cvss-display {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
- .cvss-score-display {
- margin-left: 3px;
- font-size: 10px;
- line-height: 18px;
- }
-}
\ No newline at end of file
+ .cvss-score-display {
+ margin-left: 3px;
+ font-size: 10px;
+ line-height: 18px;
+ }
+}
diff --git a/ui/src/components/SeverityWithCvssDisplay/index.jsx b/ui/src/components/SeverityWithCvssDisplay/index.jsx
index ac73213e1c..8b4c4288f1 100644
--- a/ui/src/components/SeverityWithCvssDisplay/index.jsx
+++ b/ui/src/components/SeverityWithCvssDisplay/index.jsx
@@ -1,35 +1,52 @@
-import React from 'react';
-import SeverityDisplay, { SEVERITY_ITEMS } from 'components/SeverityDisplay';
-import { TooltipWrapper } from 'components/Tooltip';
-import { toCapitalized } from 'utils/utils';
+import React from "react";
+import SeverityDisplay, { SEVERITY_ITEMS } from "components/SeverityDisplay";
+import { TooltipWrapper } from "components/Tooltip";
+import { toCapitalized } from "utils/utils";
-import './severity-with-cvss-display.scss';
+import "./severity-with-cvss-display.scss";
-export {
- SEVERITY_ITEMS
-}
+export { SEVERITY_ITEMS };
-const SeverityWithCvssDisplay = ({severity, cvssScore, cvssSeverity, compareTooltipId}) => {
- if (!severity) {
- return null;
- }
-
- const showSeverityTooltip = !!cvssSeverity && cvssSeverity !== severity &&
- !(cvssSeverity === SEVERITY_ITEMS.NONE.value && severity === SEVERITY_ITEMS.NEGLIGIBLE.value);
+const SeverityWithCvssDisplay = ({
+ severity,
+ cvssScore,
+ cvssSeverity,
+ compareTooltipId,
+}) => {
+ if (!severity) {
+ return null;
+ }
- const severityNotAlignedMessage = (
-
- {`Although the CVSS base impact score is ${cvssScore} (${toCapitalized(cvssSeverity || "")}), the linux distribution severity reflects the risk more accurately.`}
-
+ const showSeverityTooltip =
+ !!cvssSeverity &&
+ cvssSeverity !== severity &&
+ !(
+ cvssSeverity === SEVERITY_ITEMS.NONE.value &&
+ severity === SEVERITY_ITEMS.NEGLIGIBLE.value
);
-
- return (
-
-
- {!!cvssScore &&
{`(cvss ${cvssScore})`}
}
- {!!showSeverityTooltip &&
*}
-
- );
-}
-export default SeverityWithCvssDisplay;
\ No newline at end of file
+ const severityNotAlignedMessage = (
+
+ {`Although the CVSS base impact score is ${cvssScore} (${toCapitalized(cvssSeverity || "")}), the linux distribution severity reflects the risk more accurately.`}
+
+ );
+
+ return (
+
+
+ {!!cvssScore && (
+
{`(cvss ${cvssScore})`}
+ )}
+ {!!showSeverityTooltip && (
+
+ *
+
+ )}
+
+ );
+};
+
+export default SeverityWithCvssDisplay;
diff --git a/ui/src/components/SeverityWithCvssDisplay/severity-with-cvss-display.scss b/ui/src/components/SeverityWithCvssDisplay/severity-with-cvss-display.scss
index b1a4f85e71..3a9e67c1c6 100644
--- a/ui/src/components/SeverityWithCvssDisplay/severity-with-cvss-display.scss
+++ b/ui/src/components/SeverityWithCvssDisplay/severity-with-cvss-display.scss
@@ -1,10 +1,10 @@
.severity-with-cvss-display {
- display: flex;
- align-items: center;
+ display: flex;
+ align-items: center;
- .cvss-score-display {
- margin-left: 3px;
- font-size: 10px;
- line-height: 18px;
- }
-}
\ No newline at end of file
+ .cvss-score-display {
+ margin-left: 3px;
+ font-size: 10px;
+ line-height: 18px;
+ }
+}
diff --git a/ui/src/components/SystemFiltersBanner/index.jsx b/ui/src/components/SystemFiltersBanner/index.jsx
index 37507d5252..df668d8c72 100644
--- a/ui/src/components/SystemFiltersBanner/index.jsx
+++ b/ui/src/components/SystemFiltersBanner/index.jsx
@@ -1,25 +1,34 @@
-import React from 'react';
-import { useNavigate } from 'react-router-dom';
-import CloseButton from 'components/CloseButton';
-import Arrow, { ARROW_NAMES } from 'components/Arrow';
+import React from "react";
+import { useNavigate } from "react-router-dom";
+import CloseButton from "components/CloseButton";
+import Arrow, { ARROW_NAMES } from "components/Arrow";
-import './system-filter-banner.scss';
+import "./system-filter-banner.scss";
-const SystemFilterBanner = ({onClose, displayText, backPath, customDisplay: CustomDisplay}) => {
- const navigate = useNavigate();
+const SystemFilterBanner = ({
+ onClose,
+ displayText,
+ backPath,
+ customDisplay: CustomDisplay,
+}) => {
+ const navigate = useNavigate();
- return (
-
-
-
navigate(backPath)} />
- {displayText}
-
-
- {!!CustomDisplay && }
-
-
-
- )
-}
+ return (
+
+
+
navigate(backPath)}
+ />
+ {displayText}
+
+
+ {!!CustomDisplay && }
+
+
+
+ );
+};
-export default SystemFilterBanner;
\ No newline at end of file
+export default SystemFilterBanner;
diff --git a/ui/src/components/SystemFiltersBanner/system-filter-banner.scss b/ui/src/components/SystemFiltersBanner/system-filter-banner.scss
index c5f6c41ef7..1779ed4709 100644
--- a/ui/src/components/SystemFiltersBanner/system-filter-banner.scss
+++ b/ui/src/components/SystemFiltersBanner/system-filter-banner.scss
@@ -1,31 +1,31 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.system-filter-banner {
- background-color: $color-blue;
- box-shadow: 0px 5px 10px rgba(34, 43, 54, 0.11);
- height: 50px;
- width: 100%;
- padding: 0 10px;
- box-sizing: border-box;
+ background-color: $color-blue;
+ box-shadow: 0px 5px 10px rgba(34, 43, 54, 0.11);
+ height: 50px;
+ width: 100%;
+ padding: 0 10px;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ font-weight: 400;
+ font-size: 12px;
+ position: fixed;
+ top: $top-bar-height;
+ left: $side-bar-width;
+ width: calc(100% - #{$side-bar-width});
+ z-index: 1;
+
+ .system-filter-content {
display: flex;
align-items: center;
- justify-content: space-between;
- font-weight: 400;
- font-size: 12px;
- position: fixed;
- top: $top-bar-height;
- left: $side-bar-width;
- width: calc(100% - #{$side-bar-width});
- z-index: 1;
-
- .system-filter-content {
- display: flex;
- align-items: center;
- white-space: nowrap;
- overflow: hidden;
+ white-space: nowrap;
+ overflow: hidden;
- .icon {
- margin-right: 10px;
- }
+ .icon {
+ margin-right: 10px;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/TabbedPage/index.jsx b/ui/src/components/TabbedPage/index.jsx
index aec4a050dc..062a7e0b71 100644
--- a/ui/src/components/TabbedPage/index.jsx
+++ b/ui/src/components/TabbedPage/index.jsx
@@ -1,46 +1,85 @@
-import React from 'react';
-import { Routes, Route, useNavigate, Outlet, useLocation, useParams, Navigate } from 'react-router-dom';
-import classnames from 'classnames';
-import Tabs from 'components/Tabs';
-
-import './tabbed-page.scss';
-
-const TabbedPage= ({items, redirectTo, basePath, headerCustomDisplay, withInnerPadding=true, withStickyTabs=false}) => {
- const navigate = useNavigate();
-
- const {pathname} = useLocation();
- const params = useParams();
-
- const tabInnerPath = params["*"];
- const cleanPath = !!basePath ? basePath : (!!tabInnerPath ? pathname.replace(`/${tabInnerPath}`, "") : pathname);
-
- const checkIsActive = ({isIndex, path}) => (isIndex && pathname === cleanPath) || path === pathname.replace(`${cleanPath}/`, "");
-
- const isInTab = items.find(({path, isIndex}) => checkIsActive({isIndex, path}));
-
- return (
-
- {isInTab &&
- navigate(isIndex ? cleanPath : path)}
- headerCustomDisplay={headerCustomDisplay}
- withStickyTabs={withStickyTabs}
- />
- }
-
-
}>
- {
- items.map(({id, path, isIndex, component: Component}) => (
-
} />
- ))
- }
- {!!redirectTo &&
} />}
-
-
-
- );
-}
+import React from "react";
+import {
+ Routes,
+ Route,
+ useNavigate,
+ Outlet,
+ useLocation,
+ useParams,
+ Navigate,
+} from "react-router-dom";
+import classnames from "classnames";
+import Tabs from "components/Tabs";
+
+import "./tabbed-page.scss";
+
+const TabbedPage = ({
+ items,
+ redirectTo,
+ basePath,
+ headerCustomDisplay,
+ withInnerPadding = true,
+ withStickyTabs = false,
+}) => {
+ const navigate = useNavigate();
+
+ const { pathname } = useLocation();
+ const params = useParams();
+
+ const tabInnerPath = params["*"];
+ const cleanPath = !!basePath
+ ? basePath
+ : !!tabInnerPath
+ ? pathname.replace(`/${tabInnerPath}`, "")
+ : pathname;
+
+ const checkIsActive = ({ isIndex, path }) =>
+ (isIndex && pathname === cleanPath) ||
+ path === pathname.replace(`${cleanPath}/`, "");
+
+ const isInTab = items.find(({ path, isIndex }) =>
+ checkIsActive({ isIndex, path }),
+ );
+
+ return (
+
+ {isInTab && (
+ navigate(isIndex ? cleanPath : path)}
+ headerCustomDisplay={headerCustomDisplay}
+ withStickyTabs={withStickyTabs}
+ />
+ )}
+
+
+
+
+ }
+ >
+ {items.map(({ id, path, isIndex, component: Component }) => (
+
}
+ />
+ ))}
+ {!!redirectTo && (
+
} />
+ )}
+
+
+
+ );
+};
export default TabbedPage;
diff --git a/ui/src/components/TabbedPage/tabbed-page.scss b/ui/src/components/TabbedPage/tabbed-page.scss
index d0aabc9d6b..773f070c17 100644
--- a/ui/src/components/TabbedPage/tabbed-page.scss
+++ b/ui/src/components/TabbedPage/tabbed-page.scss
@@ -1,20 +1,20 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
-.tabbed-page-container{
+.tabbed-page-container {
+ display: flex;
+ flex-direction: column;
+ flex-grow: 1;
+
+ .tab-content {
display: flex;
flex-direction: column;
flex-grow: 1;
- .tab-content {
- display: flex;
- flex-direction: column;
- flex-grow: 1;
-
- > * {
- flex-grow: 1;
- }
- &.with-padding {
- padding: $main-content-padding;
- }
+ > * {
+ flex-grow: 1;
+ }
+ &.with-padding {
+ padding: $main-content-padding;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Table/Pagination/index.jsx b/ui/src/components/Table/Pagination/index.jsx
index 034b75441c..8de248c6c2 100644
--- a/ui/src/components/Table/Pagination/index.jsx
+++ b/ui/src/components/Table/Pagination/index.jsx
@@ -1,76 +1,136 @@
-import React from 'react';
-import classnames from 'classnames';
-import Icon, { ICON_NAMES } from 'components/Icon';
+import React from "react";
+import classnames from "classnames";
+import Icon, { ICON_NAMES } from "components/Icon";
-import './pagination.scss';
+import "./pagination.scss";
const FIRST_PAGE_NUMBER = 1;
-const PaginationItem = ({children, className, isActive=false, onClick}) => (
-
{children}
+const PaginationItem = ({ children, className, isActive = false, onClick }) => (
+
+ {children}
+
);
-const PaginationNumber = ({page, isActive, onClick}) => (
-
{String(page)}
+const PaginationNumber = ({ page, isActive, onClick }) => (
+
+ {String(page)}
+
);
-const PaginationArrow = ({isLeft=false, isDouble=false, onClick, disabled}) => (
-
-
-
+const PaginationArrow = ({
+ isLeft = false,
+ isDouble = false,
+ onClick,
+ disabled,
+}) => (
+
+
+
);
const PaginationDots = () => (
-
...
+
...
);
-const Pagination = ({canPreviousPage, previousPage, nextPage, pageIndex, pageSize, gotoPage, page, total, loading, displayName="items"}) => {
- if (total === 0 || loading) {
- return
;
- }
-
- const startPageItem = (pageIndex * pageSize) + 1;
- const endPageItem = page.length + (pageIndex * pageSize);
- const lastPageNumber = Math.ceil(total / pageSize) || 1;
- const activePageNumber = pageIndex + 1;
- const canNextPage = lastPageNumber > activePageNumber;
-
- const prevPageNumber = activePageNumber - 1;
- const nextPageNumber = activePageNumber + 1;
- const displayPageNumbers = [...new Set([
- prevPageNumber < FIRST_PAGE_NUMBER ? FIRST_PAGE_NUMBER : prevPageNumber,
- activePageNumber,
- nextPageNumber > lastPageNumber ? lastPageNumber : nextPageNumber
- ])].filter(page => page !== FIRST_PAGE_NUMBER && page !== lastPageNumber);
-
- const goToPageNumber = page => gotoPage(page - 1);
- const goToFirstPage = () => goToPageNumber(FIRST_PAGE_NUMBER);
- const goToLastPage = () => goToPageNumber(lastPageNumber);
+const Pagination = ({
+ canPreviousPage,
+ previousPage,
+ nextPage,
+ pageIndex,
+ pageSize,
+ gotoPage,
+ page,
+ total,
+ loading,
+ displayName = "items",
+}) => {
+ if (total === 0 || loading) {
+ return
;
+ }
- return (
-
-
- {`Showing ${startPageItem}-${endPageItem} of ${total} ${displayName}`}
-
-
-
-
-
- {displayPageNumbers[0] > (FIRST_PAGE_NUMBER + 1) &&
}
- {
- displayPageNumbers.map(page => (
-
goToPageNumber(page)} isActive={page === activePageNumber} />
- ))
- }
- {displayPageNumbers[displayPageNumbers.length - 1] < (lastPageNumber - 1) && }
- {FIRST_PAGE_NUMBER !== lastPageNumber &&
-
- }
-
-
-
-
- );
-}
+ const startPageItem = pageIndex * pageSize + 1;
+ const endPageItem = page.length + pageIndex * pageSize;
+ const lastPageNumber = Math.ceil(total / pageSize) || 1;
+ const activePageNumber = pageIndex + 1;
+ const canNextPage = lastPageNumber > activePageNumber;
-export default Pagination;
\ No newline at end of file
+ const prevPageNumber = activePageNumber - 1;
+ const nextPageNumber = activePageNumber + 1;
+ const displayPageNumbers = [
+ ...new Set([
+ prevPageNumber < FIRST_PAGE_NUMBER ? FIRST_PAGE_NUMBER : prevPageNumber,
+ activePageNumber,
+ nextPageNumber > lastPageNumber ? lastPageNumber : nextPageNumber,
+ ]),
+ ].filter((page) => page !== FIRST_PAGE_NUMBER && page !== lastPageNumber);
+
+ const goToPageNumber = (page) => gotoPage(page - 1);
+ const goToFirstPage = () => goToPageNumber(FIRST_PAGE_NUMBER);
+ const goToLastPage = () => goToPageNumber(lastPageNumber);
+
+ return (
+
+
+ {`Showing ${startPageItem}-${endPageItem} of ${total} ${displayName}`}
+
+
+
+
+
+ {displayPageNumbers[0] > FIRST_PAGE_NUMBER + 1 &&
}
+ {displayPageNumbers.map((page) => (
+
goToPageNumber(page)}
+ isActive={page === activePageNumber}
+ />
+ ))}
+ {displayPageNumbers[displayPageNumbers.length - 1] <
+ lastPageNumber - 1 && }
+ {FIRST_PAGE_NUMBER !== lastPageNumber && (
+
+ )}
+
+
+
+
+ );
+};
+
+export default Pagination;
diff --git a/ui/src/components/Table/Pagination/pagination.scss b/ui/src/components/Table/Pagination/pagination.scss
index 6044ee2e0f..3132447f09 100644
--- a/ui/src/components/Table/Pagination/pagination.scss
+++ b/ui/src/components/Table/Pagination/pagination.scss
@@ -1,58 +1,57 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$box-size: 30px;
.pagination-container {
- height: $box-size;
- padding: 8px 20px;
- font-size: 14px;
+ height: $box-size;
+ padding: 8px 20px;
+ font-size: 14px;
+ color: $color-grey-black;
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ border-bottom: 3px solid $color-grey-lighter;
+
+ .pagination-results {
+ font-weight: 400;
color: $color-grey-black;
+ }
+ .pagination-navigation {
+ margin-left: 15px;
display: flex;
- align-items: center;
- justify-content: flex-end;
- border-bottom: 3px solid $color-grey-lighter;
- .pagination-results {
- font-weight: 400;
- color: $color-grey-black;
- }
- .pagination-navigation {
- margin-left: 15px;
- display: flex;
+ .pagination-item {
+ min-width: $box-size;
+ height: $box-size;
+ box-sizing: border-box;
+ padding: 0 5px;
+ background-color: $color-grey-lighter;
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ cursor: pointer;
- .pagination-item {
- min-width: $box-size;
- height: $box-size;
- box-sizing: border-box;
- padding: 0 5px;
- background-color: $color-grey-lighter;
- display: flex;
- align-items: center;
- justify-content: space-around;
- cursor: pointer;
-
- &:not(:last-child) {
- margin-right: 4px;
- }
- &.pagination-arrow {
- color: $color-grey;
+ &:not(:last-child) {
+ margin-right: 4px;
+ }
+ &.pagination-arrow {
+ color: $color-grey;
- &.left .icon {
- transform: rotate(180deg);
- }
- &.disabled {
- cursor: not-allowed;
- color: $color-grey;
- }
- }
- &.pagination-dots {
- background-color: transparent;
- cursor: auto;
- }
- &.is-active {
- background-color: $color-main-light;
- }
+ &.left .icon {
+ transform: rotate(180deg);
+ }
+ &.disabled {
+ cursor: not-allowed;
+ color: $color-grey;
}
+ }
+ &.pagination-dots {
+ background-color: transparent;
+ cursor: auto;
+ }
+ &.is-active {
+ background-color: $color-main-light;
+ }
}
-
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Table/index.jsx b/ui/src/components/Table/index.jsx
index a7e5fe1843..a8c1651350 100644
--- a/ui/src/components/Table/index.jsx
+++ b/ui/src/components/Table/index.jsx
@@ -1,325 +1,398 @@
-import React, { useMemo, useCallback, useEffect, useState } from 'react';
-import classnames from 'classnames';
-import { isEmpty, isEqual, pickBy, isNull, isUndefined } from 'lodash';
-import { useTable, usePagination, useResizeColumns, useFlexLayout, useRowSelect } from 'react-table';
-import Icon, { ICON_NAMES } from 'components/Icon';
-import Loader from 'components/Loader';
-import { useFetch, usePrevious } from 'hooks';
-import Pagination from './Pagination';
-import * as utils from './utils';
-
-import './table.scss';
-import { ValueWithFallback } from 'components/ValueWithFallback';
-
-export {
- utils
-}
+import React, { useMemo, useCallback, useEffect, useState } from "react";
+import classnames from "classnames";
+import { isEmpty, isEqual, pickBy, isNull, isUndefined } from "lodash";
+import {
+ useTable,
+ usePagination,
+ useResizeColumns,
+ useFlexLayout,
+ useRowSelect,
+} from "react-table";
+import Icon, { ICON_NAMES } from "components/Icon";
+import Loader from "components/Loader";
+import { useFetch, usePrevious } from "hooks";
+import Pagination from "./Pagination";
+import * as utils from "./utils";
+
+import "./table.scss";
+import { ValueWithFallback } from "components/ValueWithFallback";
+
+export { utils };
const ACTIONS_COLUMN_ID = "ACTIONS";
const STATIC_COLUMN_IDS = [ACTIONS_COLUMN_ID];
-const Table = props => {
- const {columns, defaultSortBy, onSortChnage, onLineClick, paginationItemsName, url, formatFetchedData, filters, defaultPageIndex=0, defaultPageSize=50,
- onPageChange, noResultsTitle="items", refreshTimestamp, withPagination=true, data: externalData, onRowSelect,
- actionsComponent: ActionsComponent, customEmptyResultsDisplay: CustomEmptyResultsDisplay, showCustomEmptyDisplay=true,
- actionsColumnWidth=80} = props;
-
- const [sortBy, setSortBy] = useState(defaultSortBy || {});
- const prevSortBy = usePrevious(sortBy);
-
- useEffect(() => {
- if (!!onSortChnage && !isEqual(prevSortBy, sortBy)) {
- onSortChnage(sortBy);
- }
- }, [prevSortBy, sortBy, onSortChnage]);
-
-
- const defaultColumn = React.useMemo(() => ({
- minWidth: 30,
- width: 100
- }), []);
-
- const [{loading, data, error}, fetchData] = useFetch(url, {loadOnMount: false, formatFetchedData});
- const prevLoading = usePrevious(loading);
- const tableData = !!url ? data : externalData;
- const {items, count} = tableData || {};
- const tableItems = useMemo(() => items || [], [items]);
-
- const withRowActions = !!ActionsComponent;
-
- const [inititalLoaded, setInititalLoaded] = useState(!!externalData);
-
- const {
- getTableProps,
- getTableBodyProps,
- headerGroups,
- prepareRow,
- page,
- canPreviousPage,
- nextPage,
- previousPage,
- gotoPage,
- state: {
- pageIndex,
- pageSize,
- selectedRowIds
- }
- } = useTable({
- columns,
- getRowId: (rowData, rowIndex) => !!rowData.id ? rowData.id : rowIndex,
- data: tableItems,
- defaultColumn,
- initialState: {
- pageIndex: defaultPageIndex,
- pageSize: defaultPageSize,
- selectedRowIds: {}
- },
- manualPagination: true,
- pageCount: -1,
- disableMultiSort: true
- },
- useResizeColumns,
- useFlexLayout,
- usePagination,
- useRowSelect,
- hooks => {
- hooks.useInstanceBeforeDimensions.push(({headerGroups}) => {
- // fix the parent group of the framework columns to not be resizable
- headerGroups[0].headers.forEach(header => {
- if (STATIC_COLUMN_IDS.includes(header.originalId)) {
- header.canResize = false;
- }
- });
- });
-
- hooks.visibleColumns.push(columns => {
- const updatedColumns = columns;
-
- if (withRowActions) {
- updatedColumns.push({
- Header: () => null, // No header
- id: ACTIONS_COLUMN_ID,
- accessor: original => (
-
- {!!ActionsComponent &&
}
-
- ),
- disableResizing: true,
- minWidth: actionsColumnWidth,
- width: actionsColumnWidth,
- maxWidth: actionsColumnWidth
- });
- }
-
- return updatedColumns;
- })
- }
- );
-
- const updatePage = useCallback(pageIndex => {
- if (!!onPageChange) {
- onPageChange(pageIndex);
- }
-
- gotoPage(pageIndex);
- }, [gotoPage, onPageChange]);
-
- const cleanFilters = pickBy(filters, value => !isNull(value) && value !== "");
- const prevCleanFilters = usePrevious(cleanFilters);
- const filtersChanged = !isEqual(cleanFilters, prevCleanFilters) && !isUndefined(prevCleanFilters);
-
- const prevPageIndex = usePrevious(pageIndex);
-
- const {sortIds: sortKeys, desc: sortDesc} = sortBy || {};
- const prevSortKeys = usePrevious(sortKeys);
- const prevSortDesc = usePrevious(sortDesc);
- const sortingChanged = !isEqual(sortKeys, prevSortKeys) || sortDesc !== prevSortDesc;
-
- const prevRefreshTimestamp = usePrevious(refreshTimestamp);
-
- const getQueryParams = useCallback(() => {
- const queryParams = {
- ...cleanFilters,
- "$count": true
- }
-
- if (withPagination) {
- queryParams["$skip"] = pageIndex * pageSize;
- queryParams["$top"] = pageSize;
- }
-
- if (!isEmpty(sortKeys)) {
- queryParams["$orderby"] = sortKeys.map(sortKey => `${sortKey} ${sortDesc ? "desc" : "asc"}`);
- }
-
- return queryParams;
- }, [pageIndex, pageSize, sortKeys, sortDesc, cleanFilters, withPagination]);
-
- const doFetchWithQueryParams = useCallback(() => {
- if (loading) {
- return;
- }
-
- fetchData({queryParams: {...getQueryParams()}});
- }, [fetchData, getQueryParams, loading])
-
- useEffect(() => {
- if (!filtersChanged && pageIndex === prevPageIndex && !sortingChanged && prevRefreshTimestamp === refreshTimestamp) {
- return;
+const Table = (props) => {
+ const {
+ columns,
+ defaultSortBy,
+ onSortChnage,
+ onLineClick,
+ paginationItemsName,
+ url,
+ formatFetchedData,
+ filters,
+ defaultPageIndex = 0,
+ defaultPageSize = 50,
+ onPageChange,
+ noResultsTitle = "items",
+ refreshTimestamp,
+ withPagination = true,
+ data: externalData,
+ onRowSelect,
+ actionsComponent: ActionsComponent,
+ customEmptyResultsDisplay: CustomEmptyResultsDisplay,
+ showCustomEmptyDisplay = true,
+ actionsColumnWidth = 80,
+ } = props;
+
+ const [sortBy, setSortBy] = useState(defaultSortBy || {});
+ const prevSortBy = usePrevious(sortBy);
+
+ useEffect(() => {
+ if (!!onSortChnage && !isEqual(prevSortBy, sortBy)) {
+ onSortChnage(sortBy);
+ }
+ }, [prevSortBy, sortBy, onSortChnage]);
+
+ const defaultColumn = React.useMemo(
+ () => ({
+ minWidth: 30,
+ width: 100,
+ }),
+ [],
+ );
+
+ const [{ loading, data, error }, fetchData] = useFetch(url, {
+ loadOnMount: false,
+ formatFetchedData,
+ });
+ const prevLoading = usePrevious(loading);
+ const tableData = !!url ? data : externalData;
+ const { items, count } = tableData || {};
+ const tableItems = useMemo(() => items || [], [items]);
+
+ const withRowActions = !!ActionsComponent;
+
+ const [inititalLoaded, setInititalLoaded] = useState(!!externalData);
+
+ const {
+ getTableProps,
+ getTableBodyProps,
+ headerGroups,
+ prepareRow,
+ page,
+ canPreviousPage,
+ nextPage,
+ previousPage,
+ gotoPage,
+ state: { pageIndex, pageSize, selectedRowIds },
+ } = useTable(
+ {
+ columns,
+ getRowId: (rowData, rowIndex) => (!!rowData.id ? rowData.id : rowIndex),
+ data: tableItems,
+ defaultColumn,
+ initialState: {
+ pageIndex: defaultPageIndex,
+ pageSize: defaultPageSize,
+ selectedRowIds: {},
+ },
+ manualPagination: true,
+ pageCount: -1,
+ disableMultiSort: true,
+ },
+ useResizeColumns,
+ useFlexLayout,
+ usePagination,
+ useRowSelect,
+ (hooks) => {
+ hooks.useInstanceBeforeDimensions.push(({ headerGroups }) => {
+ // fix the parent group of the framework columns to not be resizable
+ headerGroups[0].headers.forEach((header) => {
+ if (STATIC_COLUMN_IDS.includes(header.originalId)) {
+ header.canResize = false;
+ }
+ });
+ });
+
+ hooks.visibleColumns.push((columns) => {
+ const updatedColumns = columns;
+
+ if (withRowActions) {
+ updatedColumns.push({
+ Header: () => null, // No header
+ id: ACTIONS_COLUMN_ID,
+ accessor: (original) => (
+
+ {!!ActionsComponent &&
}
+
+ ),
+ disableResizing: true,
+ minWidth: actionsColumnWidth,
+ width: actionsColumnWidth,
+ maxWidth: actionsColumnWidth,
+ });
}
- if (filtersChanged && pageIndex !== 0) {
- updatePage(0);
+ return updatedColumns;
+ });
+ },
+ );
+
+ const updatePage = useCallback(
+ (pageIndex) => {
+ if (!!onPageChange) {
+ onPageChange(pageIndex);
+ }
+
+ gotoPage(pageIndex);
+ },
+ [gotoPage, onPageChange],
+ );
+
+ const cleanFilters = pickBy(
+ filters,
+ (value) => !isNull(value) && value !== "",
+ );
+ const prevCleanFilters = usePrevious(cleanFilters);
+ const filtersChanged =
+ !isEqual(cleanFilters, prevCleanFilters) && !isUndefined(prevCleanFilters);
+
+ const prevPageIndex = usePrevious(pageIndex);
+
+ const { sortIds: sortKeys, desc: sortDesc } = sortBy || {};
+ const prevSortKeys = usePrevious(sortKeys);
+ const prevSortDesc = usePrevious(sortDesc);
+ const sortingChanged =
+ !isEqual(sortKeys, prevSortKeys) || sortDesc !== prevSortDesc;
+
+ const prevRefreshTimestamp = usePrevious(refreshTimestamp);
+
+ const getQueryParams = useCallback(() => {
+ const queryParams = {
+ ...cleanFilters,
+ $count: true,
+ };
+
+ if (withPagination) {
+ queryParams["$skip"] = pageIndex * pageSize;
+ queryParams["$top"] = pageSize;
+ }
- return;
- }
+ if (!isEmpty(sortKeys)) {
+ queryParams["$orderby"] = sortKeys.map(
+ (sortKey) => `${sortKey} ${sortDesc ? "desc" : "asc"}`,
+ );
+ }
- if (!!url) {
- doFetchWithQueryParams();
- }
- }, [filtersChanged, pageIndex, prevPageIndex, doFetchWithQueryParams, updatePage, sortingChanged, refreshTimestamp, prevRefreshTimestamp, url]);
+ return queryParams;
+ }, [pageIndex, pageSize, sortKeys, sortDesc, cleanFilters, withPagination]);
- const selectedRows = Object.keys(selectedRowIds);
- const prevSelectedRows = usePrevious(selectedRows);
+ const doFetchWithQueryParams = useCallback(() => {
+ if (loading) {
+ return;
+ }
- useEffect(() => {
- if (!!onRowSelect && !isEqual(selectedRows, prevSelectedRows)) {
- onRowSelect(selectedRows);
- }
- }, [prevSelectedRows, selectedRows, onRowSelect]);
+ fetchData({ queryParams: { ...getQueryParams() } });
+ }, [fetchData, getQueryParams, loading]);
+
+ useEffect(() => {
+ if (
+ !filtersChanged &&
+ pageIndex === prevPageIndex &&
+ !sortingChanged &&
+ prevRefreshTimestamp === refreshTimestamp
+ ) {
+ return;
+ }
- useEffect(() => {
- if (prevLoading && !loading && !inititalLoaded) {
- setInititalLoaded(true);
- }
- }, [prevLoading, loading, inititalLoaded]);
+ if (filtersChanged && pageIndex !== 0) {
+ updatePage(0);
- if (!!error) {
- return null;
+ return;
}
- if (!inititalLoaded) {
- return (
-
- )
+ if (!!url) {
+ doFetchWithQueryParams();
}
-
- if (isEmpty(page) && !loading && showCustomEmptyDisplay && !!CustomEmptyResultsDisplay) {
- return (
-
- )
+ }, [
+ filtersChanged,
+ pageIndex,
+ prevPageIndex,
+ doFetchWithQueryParams,
+ updatePage,
+ sortingChanged,
+ refreshTimestamp,
+ prevRefreshTimestamp,
+ url,
+ ]);
+
+ const selectedRows = Object.keys(selectedRowIds);
+ const prevSelectedRows = usePrevious(selectedRows);
+
+ useEffect(() => {
+ if (!!onRowSelect && !isEqual(selectedRows, prevSelectedRows)) {
+ onRowSelect(selectedRows);
}
+ }, [prevSelectedRows, selectedRows, onRowSelect]);
- return (
-
- {!withPagination ?
{`Showing ${count} entries`}
:
-
- }
-
-
- {
- headerGroups.map(headerGroup => {
- return (
-
- {
- headerGroup.headers.map(column => {
- const {sortIds} = column;
- const isSorted = isEqual(sortIds, sortKeys);
-
- return (
-
-
{column.render('Header')}
- {!isEmpty(sortIds) &&
-
setSortBy(({sortIds, desc}) =>
- ({sortIds: column.sortIds, desc: isEqual(column.sortIds, sortIds) ? !desc : false})
- )}
- />
- }
- {column.canResize &&
-
- }
-
- )
- })
- }
-
- )
- })
- }
-
-
- {isEmpty(page) && !loading &&
{`No results available for ${noResultsTitle}`}
}
- {loading &&
}
- {
- page.map((row) => {
- prepareRow(row);
-
- return (
-
- {
- if (!!onLineClick) {
- onLineClick(row.original);
- }
- }}
- >
- {
- row.cells.map(cell => {
- const {className, alignToTop} = cell.column;
- const cellClassName = classnames(
- "table-td",
- {"align-to-top": alignToTop},
- {[className]: className}
- );
-
- const isTextValue = !!cell.column.accessor;
-
- return (
-
-
- {isTextValue ? cell.value : cell.render('Cell')}
-
-
- )
- })
- }
-
-
- )
- })
+ useEffect(() => {
+ if (prevLoading && !loading && !inititalLoaded) {
+ setInititalLoaded(true);
+ }
+ }, [prevLoading, loading, inititalLoaded]);
+
+ if (!!error) {
+ return null;
+ }
+
+ if (!inititalLoaded) {
+ return
;
+ }
+
+ if (
+ isEmpty(page) &&
+ !loading &&
+ showCustomEmptyDisplay &&
+ !!CustomEmptyResultsDisplay
+ ) {
+ return
;
+ }
+
+ return (
+
+ {!withPagination ? (
+
{`Showing ${count} entries`}
+ ) : (
+
+ )}
+
+
+ {headerGroups.map((headerGroup) => {
+ return (
+
+ {headerGroup.headers.map((column) => {
+ const { sortIds } = column;
+ const isSorted = isEqual(sortIds, sortKeys);
+
+ return (
+
+
+ {column.render("Header")}
+
+ {!isEmpty(sortIds) && (
+
+ setSortBy(({ sortIds, desc }) => ({
+ sortIds: column.sortIds,
+ desc: isEqual(column.sortIds, sortIds)
+ ? !desc
+ : false,
+ }))
+ }
+ />
+ )}
+ {column.canResize && (
+
+ )}
+
+ );
+ })}
+
+ );
+ })}
+
+
+ {isEmpty(page) && !loading && (
+
{`No results available for ${noResultsTitle}`}
+ )}
+ {loading && (
+
+
+
+ )}
+ {page.map((row) => {
+ prepareRow(row);
+
+ return (
+
+ {
+ if (!!onLineClick) {
+ onLineClick(row.original);
}
+ }}
+ >
+ {row.cells.map((cell) => {
+ const { className, alignToTop } = cell.column;
+ const cellClassName = classnames(
+ "table-td",
+ { "align-to-top": alignToTop },
+ { [className]: className },
+ );
+
+ const isTextValue = !!cell.column.accessor;
+
+ return (
+
+
+ {isTextValue ? cell.value : cell.render("Cell")}
+
+
+ );
+ })}
-
-
+
+ );
+ })}
- )
-}
+
+
+ );
+};
export default React.memo(Table, (prevProps, nextProps) => {
- const {filters: prevFilters, refreshTimestamp: prevRefreshTimestamp, data: prevData} = prevProps;
- const {filters, refreshTimestamp, data} = nextProps;
-
- const shouldRefresh = !isEqual(prevFilters, filters) || prevRefreshTimestamp !== refreshTimestamp || !isEqual(prevData, data);
-
- return !shouldRefresh;
+ const {
+ filters: prevFilters,
+ refreshTimestamp: prevRefreshTimestamp,
+ data: prevData,
+ } = prevProps;
+ const { filters, refreshTimestamp, data } = nextProps;
+
+ const shouldRefresh =
+ !isEqual(prevFilters, filters) ||
+ prevRefreshTimestamp !== refreshTimestamp ||
+ !isEqual(prevData, data);
+
+ return !shouldRefresh;
});
diff --git a/ui/src/components/Table/table.scss b/ui/src/components/Table/table.scss
index 470a1563ec..86dab6f31f 100644
--- a/ui/src/components/Table/table.scss
+++ b/ui/src/components/Table/table.scss
@@ -1,127 +1,127 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$cell-padding: 10px;
.table-wrapper {
- .no-pagination-results-total {
- font-size: 14px;
- line-height: 19px;
- color: $color-grey-black;
- display: flex;
- justify-content: flex-end;
- }
- .table {
- color: $color-grey-black;
- overflow-x: hidden;
- padding: 0 18px;
+ .no-pagination-results-total {
+ font-size: 14px;
+ line-height: 19px;
+ color: $color-grey-black;
+ display: flex;
+ justify-content: flex-end;
+ }
+ .table {
+ color: $color-grey-black;
+ overflow-x: hidden;
+ padding: 0 18px;
- .table-head {
- font-weight: 700;
- font-size: 14px;
- line-height: 18px;
- letter-spacing: 0.27px;
- overflow-y: hidden;
+ .table-head {
+ font-weight: 700;
+ font-size: 14px;
+ line-height: 18px;
+ letter-spacing: 0.27px;
+ overflow-y: hidden;
- .table-tr {
- border-bottom: 2px solid $color-grey-lighter;
-
- .table-th {
- padding: $cell-padding;
- margin: auto 0;
- display: flex;
- align-items: center;
+ .table-tr {
+ border-bottom: 2px solid $color-grey-lighter;
- .resizer {
- right: 0;
- width: 10px;
- height: 100%;
- position: absolute;
- top: 0;
- touch-action: none;
- }
- .table-sort-icon {
- margin-left: 5px;
+ .table-th {
+ padding: $cell-padding;
+ margin: auto 0;
+ display: flex;
+ align-items: center;
- &:not(.sorted) {
- display: none;
- }
- &.rotate {
- transform: rotate(180deg);
- }
- }
- &:hover {
- .table-sort-icon {
- display: inline-block;
- }
- }
- }
- }
- }
- .table-body {
- font-size: 14px;
- line-height: 18px;
- overflow-y: hidden;
+ .resizer {
+ right: 0;
+ width: 10px;
+ height: 100%;
+ position: absolute;
+ top: 0;
+ touch-action: none;
+ }
+ .table-sort-icon {
+ margin-left: 5px;
- .empty-results-display-wrapper {
- margin: 30px 10px;
+ &:not(.sorted) {
+ display: none;
}
- .table-loading {
- position: relative;
- height: 200px;
+ &.rotate {
+ transform: rotate(180deg);
}
- &:not(:hover) {
- .table-tr.with-row-actions:first-child {
- background-color: $color-blue-light;
-
- .table-td .actions-column-container {
- visibility: visible;
- }
- }
+ }
+ &:hover {
+ .table-sort-icon {
+ display: inline-block;
}
- .table-tr {
- border-bottom: 1px solid $color-grey-lighter;
+ }
+ }
+ }
+ }
+ .table-body {
+ font-size: 14px;
+ line-height: 18px;
+ overflow-y: hidden;
- &:hover,
- &.marked {
- background-color: $color-blue-light;
+ .empty-results-display-wrapper {
+ margin: 30px 10px;
+ }
+ .table-loading {
+ position: relative;
+ height: 200px;
+ }
+ &:not(:hover) {
+ .table-tr.with-row-actions:first-child {
+ background-color: $color-blue-light;
- &.clickable {
- cursor: pointer;
- }
- }
- &:hover {
- .actions-column-container {
- visibility: visible !important;
- }
- }
- .table-td {
- padding: $cell-padding;
- display: flex;
- flex-direction: column;
- justify-content: center;
- overflow-x: hidden;
+ .table-td .actions-column-container {
+ visibility: visible;
+ }
+ }
+ }
+ .table-tr {
+ border-bottom: 1px solid $color-grey-lighter;
+
+ &:hover,
+ &.marked {
+ background-color: $color-blue-light;
- &.align-to-top {
- align-self: flex-start;
- }
- .actions-column-container {
- visibility: hidden;
+ &.clickable {
+ cursor: pointer;
+ }
+ }
+ &:hover {
+ .actions-column-container {
+ visibility: visible !important;
+ }
+ }
+ .table-td {
+ padding: $cell-padding;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ overflow-x: hidden;
- .icon {
- color: $color-main;
+ &.align-to-top {
+ align-self: flex-start;
+ }
+ .actions-column-container {
+ visibility: hidden;
- &.disabled {
- color: $color-grey;
- }
- }
- }
- }
- }
+ .icon {
+ color: $color-main;
- //----- table utils -----
- .table-empty-value {
+ &.disabled {
color: $color-grey;
+ }
}
+ }
}
+ }
+
+ //----- table utils -----
+ .table-empty-value {
+ color: $color-grey;
+ }
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Table/utils.jsx b/ui/src/components/Table/utils.jsx
index 48a7a9646c..3d66a7ab66 100644
--- a/ui/src/components/Table/utils.jsx
+++ b/ui/src/components/Table/utils.jsx
@@ -1,6 +1,6 @@
-import React from 'react';
-import Icon, { ICON_NAMES } from 'components/Icon';
+import React from "react";
+import Icon, { ICON_NAMES } from "components/Icon";
export const EmptyValue = () => (
-
-);
\ No newline at end of file
+
+);
diff --git a/ui/src/components/TablePage/index.jsx b/ui/src/components/TablePage/index.jsx
index c74fa7d472..81cdb072b9 100644
--- a/ui/src/components/TablePage/index.jsx
+++ b/ui/src/components/TablePage/index.jsx
@@ -1,130 +1,221 @@
-import React, { useEffect } from 'react';
-import { useNavigate, useLocation, useSearchParams } from 'react-router-dom';
-import { isEmpty } from 'lodash';
-import { ErrorBoundary } from 'react-error-boundary';
-import ContentContainer from 'components/ContentContainer';
-import Table from 'components/Table';
-import SystemFilterBanner from 'components/SystemFiltersBanner';
-import Filter, { formatFiltersToOdataItems } from 'components/Filter';
-import Loader from 'components/Loader';
-import { toCapitalized, BoldText } from 'utils/utils';
-import { useFilterState, useFilterDispatch, resetSystemFilters, setPage, setSort, setFilters, initializeFilters } from 'context/FiltersProvider';
+import React, { useEffect } from "react";
+import { useNavigate, useLocation, useSearchParams } from "react-router-dom";
+import { isEmpty } from "lodash";
+import { ErrorBoundary } from "react-error-boundary";
+import ContentContainer from "components/ContentContainer";
+import Table from "components/Table";
+import SystemFilterBanner from "components/SystemFiltersBanner";
+import Filter, { formatFiltersToOdataItems } from "components/Filter";
+import Loader from "components/Loader";
+import { toCapitalized, BoldText } from "utils/utils";
+import {
+ useFilterState,
+ useFilterDispatch,
+ resetSystemFilters,
+ setPage,
+ setSort,
+ setFilters,
+ initializeFilters,
+} from "context/FiltersProvider";
const TablePage = (props) => {
- const {tableTitle, filterType, systemFilterType, filters, expand, select, withMargin, defaultSortBy: initialSortBy,
- filtersConfig, customHeaderDisplay: CustomHeaderDisplay, ...tableProps} = props;
-
- const [searchParams, setSearchParams] = useSearchParams();
-
- const navigate = useNavigate();
- const {pathname} = useLocation();
-
- const filtersDispatch = useFilterDispatch();
- const filtersState = useFilterState();
-
- const {initialized} = filtersState;
-
- const {systemFilters} = filtersState[systemFilterType || filterType];
- const {name: systemFilterName, suffix: systemSuffix, backPath: systemFilterBackPath, filter: systemFilter, customDisplay} = systemFilters;
-
- const {tableFilters, customFilters, selectedPageIndex, tableSort} = filtersState[filterType];
-
- const setTableFilters = (filters) => setFilters(filtersDispatch, {type: filterType, filters, isSystem: false});
-
- const resetSystemFilter = () => resetSystemFilters(filtersDispatch, systemFilterType || filterType);
-
- const fitlersList = [
- ...(!!filters ? [filters] : []),
- ...(!!tableFilters ? formatFiltersToOdataItems(tableFilters) : []),
- ...(!!systemFilter ? [systemFilter] : [])
- ];
-
- useEffect(() => {
- if (!initialized) {
- try {
- const {filterType, systemFilterType, tableFilters, systemFilters, customFilters} = JSON.parse(searchParams.get("filters") || {});
-
- initializeFilters(filtersDispatch, {filterType, systemFilterType, tableFilters, systemFilters, customFilters});
- } catch(error) {
- console.log("invalid filters");
-
- setSearchParams({}, { replace: true });
- }
- }
- }, [initialized, searchParams, filtersDispatch, setSearchParams]);
+ const {
+ tableTitle,
+ filterType,
+ systemFilterType,
+ filters,
+ expand,
+ select,
+ withMargin,
+ defaultSortBy: initialSortBy,
+ filtersConfig,
+ customHeaderDisplay: CustomHeaderDisplay,
+ ...tableProps
+ } = props;
+
+ const [searchParams, setSearchParams] = useSearchParams();
+
+ const navigate = useNavigate();
+ const { pathname } = useLocation();
+
+ const filtersDispatch = useFilterDispatch();
+ const filtersState = useFilterState();
+
+ const { initialized } = filtersState;
+
+ const { systemFilters } = filtersState[systemFilterType || filterType];
+ const {
+ name: systemFilterName,
+ suffix: systemSuffix,
+ backPath: systemFilterBackPath,
+ filter: systemFilter,
+ customDisplay,
+ } = systemFilters;
+
+ const { tableFilters, customFilters, selectedPageIndex, tableSort } =
+ filtersState[filterType];
+
+ const setTableFilters = (filters) =>
+ setFilters(filtersDispatch, { type: filterType, filters, isSystem: false });
+
+ const resetSystemFilter = () =>
+ resetSystemFilters(filtersDispatch, systemFilterType || filterType);
- useEffect(() => {
- const {customDisplay, ...cleanSystemFilters} = systemFilters;
- setSearchParams({filters: JSON.stringify({filterType, systemFilterType, tableFilters, systemFilters: cleanSystemFilters, customFilters})}, { replace: true });
- }, [filterType, systemFilterType, tableFilters, systemFilters, customFilters, setSearchParams]);
+ const fitlersList = [
+ ...(!!filters ? [filters] : []),
+ ...(!!tableFilters ? formatFiltersToOdataItems(tableFilters) : []),
+ ...(!!systemFilter ? [systemFilter] : []),
+ ];
+ useEffect(() => {
if (!initialized) {
- return
;
+ try {
+ const {
+ filterType,
+ systemFilterType,
+ tableFilters,
+ systemFilters,
+ customFilters,
+ } = JSON.parse(searchParams.get("filters") || {});
+
+ initializeFilters(filtersDispatch, {
+ filterType,
+ systemFilterType,
+ tableFilters,
+ systemFilters,
+ customFilters,
+ });
+ } catch (error) {
+ console.log("invalid filters");
+
+ setSearchParams({}, { replace: true });
+ }
}
-
- return (
-
- {!!systemFilterName &&
-
{
- if (isEmpty(systemFilters)) {
- resetErrorBoundary();
- }
-
- return null;
- }}
- onError={resetSystemFilter}
- >
- {`${toCapitalized(tableTitle)} for `}{systemFilterName}{` ${systemSuffix}`}}
- onClose={resetSystemFilter}
- backPath={systemFilterBackPath}
- customDisplay={customDisplay}
- />
-
+ }, [initialized, searchParams, filtersDispatch, setSearchParams]);
+
+ useEffect(() => {
+ const { customDisplay, ...cleanSystemFilters } = systemFilters;
+ setSearchParams(
+ {
+ filters: JSON.stringify({
+ filterType,
+ systemFilterType,
+ tableFilters,
+ systemFilters: cleanSystemFilters,
+ customFilters,
+ }),
+ },
+ { replace: true },
+ );
+ }, [
+ filterType,
+ systemFilterType,
+ tableFilters,
+ systemFilters,
+ customFilters,
+ setSearchParams,
+ ]);
+
+ if (!initialized) {
+ return
;
+ }
+
+ return (
+
+ {!!systemFilterName && (
+
{
+ if (isEmpty(systemFilters)) {
+ resetErrorBoundary();
+ }
+
+ return null;
+ }}
+ onError={resetSystemFilter}
+ >
+
+ {`${toCapitalized(tableTitle)} for `}
+ {systemFilterName}
+ {` ${systemSuffix}`}
+
+ }
+ onClose={resetSystemFilter}
+ backPath={systemFilterBackPath}
+ customDisplay={customDisplay}
+ />
+
+ )}
+
+ {!!filtersConfig && (
+
{
+ if (isEmpty(tableFilters)) {
+ resetErrorBoundary();
+ }
+
+ return null;
+ }}
+ onError={() =>
+ setFilters(filtersDispatch, {
+ type: filterType,
+ filters: [],
+ isSystem: false,
+ })
}
-
- {!!filtersConfig &&
-
{
- if (isEmpty(tableFilters)) {
- resetErrorBoundary();
- }
-
- return null;
- }}
- onError={() => setFilters(filtersDispatch, {type: filterType, filters: [], isSystem: false})}
- >
- setTableFilters(filters)}
- filtersConfig={filtersConfig}
- filtersOnCopyText={window.location.href}
- />
-
- }
- {!!CustomHeaderDisplay &&
}
-
-
- 0 ? {"$filter": fitlersList.join(" and ")} : {})
- }}
- noResultsTitle={tableTitle}
- onLineClick={({id}) => navigate(`${pathname}/${id}`)}
- defaultPageIndex={selectedPageIndex}
- onPageChange={pageIndex => setPage(filtersDispatch, {type: filterType, pageIndex})}
- defaultSortBy={isEmpty(tableSort) ? initialSortBy : tableSort}
- onSortChnage={tableSort => setSort(filtersDispatch, {type: filterType, tableSort})}
- showCustomEmptyDisplay={isEmpty(tableFilters)}
- {...tableProps}
- />
-
-
- )
-}
+ >
+ setTableFilters(filters)}
+ filtersConfig={filtersConfig}
+ filtersOnCopyText={window.location.href}
+ />
+
+ )}
+ {!!CustomHeaderDisplay && (
+
+
+
+ )}
+
+
+ 0
+ ? { $filter: fitlersList.join(" and ") }
+ : {}),
+ }}
+ noResultsTitle={tableTitle}
+ onLineClick={({ id }) => navigate(`${pathname}/${id}`)}
+ defaultPageIndex={selectedPageIndex}
+ onPageChange={(pageIndex) =>
+ setPage(filtersDispatch, { type: filterType, pageIndex })
+ }
+ defaultSortBy={isEmpty(tableSort) ? initialSortBy : tableSort}
+ onSortChnage={(tableSort) =>
+ setSort(filtersDispatch, { type: filterType, tableSort })
+ }
+ showCustomEmptyDisplay={isEmpty(tableFilters)}
+ {...tableProps}
+ />
+
+
+ );
+};
export default TablePage;
diff --git a/ui/src/components/Tabs/index.jsx b/ui/src/components/Tabs/index.jsx
index 80802dc92c..85cfcda74c 100644
--- a/ui/src/components/Tabs/index.jsx
+++ b/ui/src/components/Tabs/index.jsx
@@ -1,36 +1,63 @@
-import React from 'react';
-import classnames from 'classnames';
-import { TooltipWrapper } from 'components/Tooltip';
+import React from "react";
+import classnames from "classnames";
+import { TooltipWrapper } from "components/Tooltip";
-import './tabs.scss';
+import "./tabs.scss";
-const Tabs = ({items, checkIsActive, onClick, headerCustomDisplay: HeaderCustomDisplay, withStickyTabs=false, tabItemPadding=20}) => (
-
- {
- items.map((item) => {
- const {id, title, disabled, tabTooltip, customTitle: CustomTitle} = item;
- const isActive = checkIsActive(item);
+const Tabs = ({
+ items,
+ checkIsActive,
+ onClick,
+ headerCustomDisplay: HeaderCustomDisplay,
+ withStickyTabs = false,
+ tabItemPadding = 20,
+}) => (
+
+ {items.map((item) => {
+ const {
+ id,
+ title,
+ disabled,
+ tabTooltip,
+ customTitle: CustomTitle,
+ } = item;
+ const isActive = checkIsActive(item);
- const onTabClick = () => {
- if (disabled) {
- return;
- }
-
- onClick(item);
- }
-
- const WrapperElement = !!tabTooltip ? TooltipWrapper : "div";
- const wrapperProps = !!tabTooltip ? {tooltipId: `tab-disabled-tooltip-${id}`, tooltipText: tabTooltip} : {};
-
- return (
-
- {!!CustomTitle ? : title}
-
- )
- })
+ const onTabClick = () => {
+ if (disabled) {
+ return;
}
- {!!HeaderCustomDisplay &&
}
-
+
+ onClick(item);
+ };
+
+ const WrapperElement = !!tabTooltip ? TooltipWrapper : "div";
+ const wrapperProps = !!tabTooltip
+ ? { tooltipId: `tab-disabled-tooltip-${id}`, tooltipText: tabTooltip }
+ : {};
+
+ return (
+
+ {!!CustomTitle ? (
+
+ ) : (
+ title
+ )}
+
+ );
+ })}
+ {!!HeaderCustomDisplay && (
+
+
+
+ )}
+
);
-export default Tabs;
\ No newline at end of file
+export default Tabs;
diff --git a/ui/src/components/Tabs/tabs.scss b/ui/src/components/Tabs/tabs.scss
index 18f2b31bff..2e296a8810 100644
--- a/ui/src/components/Tabs/tabs.scss
+++ b/ui/src/components/Tabs/tabs.scss
@@ -1,38 +1,38 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.tabs-container {
- display: flex;
- height: 44px;
- border-bottom: 2px solid $color-grey-lighter;
- background-color: white;
- position: relative;
+ display: flex;
+ height: 44px;
+ border-bottom: 2px solid $color-grey-lighter;
+ background-color: white;
+ position: relative;
- &.sticky {
- position: sticky;
- top: 0;
- z-index: 1;
- }
- .tab-item {
- display: inline-flex;
- align-items: center;
- font-size: 14px;
- line-height: 16px;
- text-align: center;
- cursor: pointer;
+ &.sticky {
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+ .tab-item {
+ display: inline-flex;
+ align-items: center;
+ font-size: 14px;
+ line-height: 16px;
+ text-align: center;
+ cursor: pointer;
- &.active {
- font-weight: bold;
- border-bottom: 2px solid $color-main-light;
- }
- &.disabled {
- color: $color-grey-light;
- cursor: not-allowed;
- }
+ &.active {
+ font-weight: bold;
+ border-bottom: 2px solid $color-main-light;
}
- .tabs-header-custom-content-wrapper {
- position: absolute;
- top: 50%;
- right: 10px;
- transform: translateY(-50%);
+ &.disabled {
+ color: $color-grey-light;
+ cursor: not-allowed;
}
-}
\ No newline at end of file
+ }
+ .tabs-header-custom-content-wrapper {
+ position: absolute;
+ top: 50%;
+ right: 10px;
+ transform: translateY(-50%);
+ }
+}
diff --git a/ui/src/components/Tag/index.jsx b/ui/src/components/Tag/index.jsx
index 962d96cd1f..dc4d25a2ef 100644
--- a/ui/src/components/Tag/index.jsx
+++ b/ui/src/components/Tag/index.jsx
@@ -1,19 +1,26 @@
-import React from 'react';
-import classnames from 'classnames';
+import React from "react";
+import classnames from "classnames";
-import './tag.scss';
-import { ValueWithFallback } from 'components/ValueWithFallback';
+import "./tag.scss";
+import { ValueWithFallback } from "components/ValueWithFallback";
-const Tag = ({children, onClick}) => (
- {children}
-)
+const Tag = ({ children, onClick }) => (
+
+ {children}
+
+);
-export const TagsList = ({items}) => (
-
-
- {items?.map((tag, index) => {tag})}
-
-
-)
+export const TagsList = ({ items }) => (
+
+
+ {items?.map((tag, index) => (
+ {tag}
+ ))}
+
+
+);
export default Tag;
diff --git a/ui/src/components/Tag/tag.scss b/ui/src/components/Tag/tag.scss
index 527ca86ea0..d428b778a1 100644
--- a/ui/src/components/Tag/tag.scss
+++ b/ui/src/components/Tag/tag.scss
@@ -1,25 +1,25 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.clarity-tag {
- display: inline-block;
- padding: 5px 10px;
- background-color: $color-blue-light;
- border: 1px solid $color-blue;
- font-weight: 400;
- font-size: 10px;
- line-height: 14px;
+ display: inline-block;
+ padding: 5px 10px;
+ background-color: $color-blue-light;
+ border: 1px solid $color-blue;
+ font-weight: 400;
+ font-size: 10px;
+ line-height: 14px;
- &.clickable {
- cursor: pointer;
- }
+ &.clickable {
+ cursor: pointer;
+ }
}
.clarity-tags-list {
- .clarity-tag {
- margin-bottom: 5px;
-
- &:not(:last-child) {
- margin-right: 10px;
- }
+ .clarity-tag {
+ margin-bottom: 5px;
+
+ &:not(:last-child) {
+ margin-right: 10px;
}
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/Title/index.jsx b/ui/src/components/Title/index.jsx
index 7dfeec5b65..d1d86195c8 100644
--- a/ui/src/components/Title/index.jsx
+++ b/ui/src/components/Title/index.jsx
@@ -1,12 +1,27 @@
-import React from 'react';
-import classnames from 'classnames';
+import React from "react";
+import classnames from "classnames";
-import './title.scss';
+import "./title.scss";
-const Title = ({children, className, medium, onClick, removeMargin=false}) => (
-
- {children}
-
-)
+const Title = ({
+ children,
+ className,
+ medium,
+ onClick,
+ removeMargin = false,
+}) => (
+
+ {children}
+
+);
-export default Title;
\ No newline at end of file
+export default Title;
diff --git a/ui/src/components/Title/title.scss b/ui/src/components/Title/title.scss
index 4114e1a8c5..74e3d78225 100644
--- a/ui/src/components/Title/title.scss
+++ b/ui/src/components/Title/title.scss
@@ -1,22 +1,22 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.clarity-title {
- color: $color-grey-black;
- font-weight: 300;
- font-size: 35px;
- line-height: 48px;
+ color: $color-grey-black;
+ font-weight: 300;
+ font-size: 35px;
+ line-height: 48px;
- &:not(.no-margin) {
- margin-bottom: 30px;
- }
- &.clickable {
- color: $color-main;
- text-decoration: underline;
- cursor: pointer;
- }
- &.medium {
- font-weight: 350;
- font-size: 26px;
- line-height: 30px;
- }
-}
\ No newline at end of file
+ &:not(.no-margin) {
+ margin-bottom: 30px;
+ }
+ &.clickable {
+ color: $color-main;
+ text-decoration: underline;
+ cursor: pointer;
+ }
+ &.medium {
+ font-weight: 350;
+ font-size: 26px;
+ line-height: 30px;
+ }
+}
diff --git a/ui/src/components/TitleValueDisplay/index.jsx b/ui/src/components/TitleValueDisplay/index.jsx
index b2f6843d88..6df725375d 100644
--- a/ui/src/components/TitleValueDisplay/index.jsx
+++ b/ui/src/components/TitleValueDisplay/index.jsx
@@ -1,39 +1,70 @@
-import React, { useState } from 'react';
-import classnames from 'classnames';
-import Arrow, { ARROW_NAMES } from 'components/Arrow';
-import { isEmpty } from 'lodash';
+import React, { useState } from "react";
+import classnames from "classnames";
+import Arrow, { ARROW_NAMES } from "components/Arrow";
+import { isEmpty } from "lodash";
-import './title-value-display.scss';
-import { ValueWithFallback } from 'components/ValueWithFallback';
+import "./title-value-display.scss";
+import { ValueWithFallback } from "components/ValueWithFallback";
-export const TitleValueDisplayRow = ({children}) => (
- {children}
+export const TitleValueDisplayRow = ({ children }) => (
+ {children}
);
-export const TitleValueDisplayColumn = ({children}) => (
- {children}
+export const TitleValueDisplayColumn = ({ children }) => (
+ {children}
);
-export const ValuesListDisplay = ({values}) => (
-
- {values?.map((value, index) =>
{value}
)}
-
-)
+export const ValuesListDisplay = ({ values }) => (
+
+ {values?.map((value, index) => (
+
+ {value}
+
+ ))}
+
+);
-const TitleValueDisplay = ({title, children, className, withOpen=false, defaultOpen=false, isSubItem=false, isLargeTitle=false}) => {
- const [isOpen, setIsOpen] = useState(defaultOpen);
+const TitleValueDisplay = ({
+ title,
+ children,
+ className,
+ withOpen = false,
+ defaultOpen = false,
+ isSubItem = false,
+ isLargeTitle = false,
+}) => {
+ const [isOpen, setIsOpen] = useState(defaultOpen);
- return (
-
-
setIsOpen(!isOpen)}>
-
{title}
- {withOpen &&
}
-
- {(!withOpen || isOpen) &&
- {children}
-
}
+ return (
+
+
setIsOpen(!isOpen)}
+ >
+
+ {title}
- );
-}
+ {withOpen && (
+
+ )}
+
+ {(!withOpen || isOpen) && (
+
+ {children}
+
+ )}
+
+ );
+};
-export default TitleValueDisplay
+export default TitleValueDisplay;
diff --git a/ui/src/components/TitleValueDisplay/title-value-display.scss b/ui/src/components/TitleValueDisplay/title-value-display.scss
index 99f6de9b8e..eaa1a2556f 100644
--- a/ui/src/components/TitleValueDisplay/title-value-display.scss
+++ b/ui/src/components/TitleValueDisplay/title-value-display.scss
@@ -1,91 +1,91 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$open-icon-size: 14px;
.title-value-display-wrapper {
- min-width: 220px;
+ min-width: 220px;
- &.sub-item {
- .title-value-display-title-wrapper .title-value-display-title {
- color: $color-grey-dark;
- margin-bottom: 5px;
- }
+ &.sub-item {
+ .title-value-display-title-wrapper .title-value-display-title {
+ color: $color-grey-dark;
+ margin-bottom: 5px;
}
- .title-value-display-title-wrapper {
- display: flex;
- align-items: center;
- justify-content: space-between;
-
- &.with-open {
- border-bottom: 1px solid $color-grey-lighter;
- margin-bottom: 10px;
- cursor: pointer;
- }
- .title-value-display-title {
- font-weight: bold;
- font-size: 10px;
- line-height: 14px;
- letter-spacing: 0.27px;
- color: $color-grey-black;
- text-transform: uppercase;
- margin-bottom: 10px;
+ }
+ .title-value-display-title-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
- &.large {
- font-size: 12px;
- }
- }
+ &.with-open {
+ border-bottom: 1px solid $color-grey-lighter;
+ margin-bottom: 10px;
+ cursor: pointer;
}
- .title-value-display-content {
+ .title-value-display-title {
+ font-weight: bold;
+ font-size: 10px;
+ line-height: 14px;
+ letter-spacing: 0.27px;
+ color: $color-grey-black;
+ text-transform: uppercase;
+ margin-bottom: 10px;
+
+ &.large {
font-size: 12px;
- line-height: 16px;
- color: $color-grey-dark;
- word-wrap: break-word;
+ }
+ }
+ }
+ .title-value-display-content {
+ font-size: 12px;
+ line-height: 16px;
+ color: $color-grey-dark;
+ word-wrap: break-word;
- a {
- color: $color-main;
- font-weight: bold;
- font-size: 12px;
- line-height: 16px;
- text-decoration: none;
+ a {
+ color: $color-main;
+ font-weight: bold;
+ font-size: 12px;
+ line-height: 16px;
+ text-decoration: none;
- &:hover {
- text-decoration: underline;
- }
- }
+ &:hover {
+ text-decoration: underline;
+ }
+ }
- .empty {
- color: $color-grey;
- }
+ .empty {
+ color: $color-grey;
}
+ }
}
.title-value-display-row {
- display: flex;
- margin-bottom: 30px;
+ display: flex;
+ margin-bottom: 30px;
- > * {
- width: 100%;
- }
- .title-value-display-wrapper:not(:last-child) {
- margin-right: 10px;
- }
+ > * {
+ width: 100%;
+ }
+ .title-value-display-wrapper:not(:last-child) {
+ margin-right: 10px;
+ }
}
.title-value-display-column {
- padding-right: 30px;
- flex-grow: 1;
+ padding-right: 30px;
+ flex-grow: 1;
- .title-value-display-wrapper {
- margin-bottom: 30px;
+ .title-value-display-wrapper {
+ margin-bottom: 30px;
- &.sub-item {
- margin-bottom: 20px;
- }
+ &.sub-item {
+ margin-bottom: 20px;
}
+ }
}
.title-value-values-list {
- .title-value-values-list-item:not(:last-child) {
- margin-bottom: 10px;
- }
+ .title-value-values-list-item:not(:last-child) {
+ margin-bottom: 10px;
+ }
}
diff --git a/ui/src/components/ToggleButton/index.jsx b/ui/src/components/ToggleButton/index.jsx
index 7df1e7012e..d97d5b4873 100644
--- a/ui/src/components/ToggleButton/index.jsx
+++ b/ui/src/components/ToggleButton/index.jsx
@@ -1,18 +1,18 @@
-import React from 'react';
-import Toggle from 'react-toggle';
+import React from "react";
+import Toggle from "react-toggle";
-import 'react-toggle/style.css';
-import './toggle-button.scss';
+import "react-toggle/style.css";
+import "./toggle-button.scss";
-const ToggleButton = ({title, checked, onChange}) => (
-
-
onChange(target.checked)}
- />
- {title}
-
-)
+const ToggleButton = ({ title, checked, onChange }) => (
+
+
onChange(target.checked)}
+ />
+ {title}
+
+);
-export default ToggleButton;
\ No newline at end of file
+export default ToggleButton;
diff --git a/ui/src/components/ToggleButton/toggle-button.scss b/ui/src/components/ToggleButton/toggle-button.scss
index 0c83c484e6..f8d415c7f2 100644
--- a/ui/src/components/ToggleButton/toggle-button.scss
+++ b/ui/src/components/ToggleButton/toggle-button.scss
@@ -1,45 +1,45 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.toggle-button {
- display: flex;
- align-items: center;
- font-weight: 400;
- font-size: 14px;
- color: $color-grey-black;
+ display: flex;
+ align-items: center;
+ font-weight: 400;
+ font-size: 14px;
+ color: $color-grey-black;
- .react-toggle {
- margin-right: 13px;
+ .react-toggle {
+ margin-right: 13px;
- &.react-toggle--focus,
- &:active {
- .react-toggle-thumb {
- box-shadow: none;
- outline: none;
- }
- }
- &:hover {
- .react-toggle-track {
- background-color: $color-grey;
- }
- }
- &.react-toggle--checked {
- .react-toggle-track {
- background-color: $color-main-light;
- }
- .react-toggle-thumb {
- left: 18px;
- border-color: $color-main-light;
- }
- }
- .react-toggle-track {
- height: 12px;
- width: 40px;
- background-color: $color-grey;
- }
- .react-toggle-thumb {
- top: -5px;
- left: 0;
- border-color: $color-grey;
- }
+ &.react-toggle--focus,
+ &:active {
+ .react-toggle-thumb {
+ box-shadow: none;
+ outline: none;
+ }
}
-}
\ No newline at end of file
+ &:hover {
+ .react-toggle-track {
+ background-color: $color-grey;
+ }
+ }
+ &.react-toggle--checked {
+ .react-toggle-track {
+ background-color: $color-main-light;
+ }
+ .react-toggle-thumb {
+ left: 18px;
+ border-color: $color-main-light;
+ }
+ }
+ .react-toggle-track {
+ height: 12px;
+ width: 40px;
+ background-color: $color-grey;
+ }
+ .react-toggle-thumb {
+ top: -5px;
+ left: 0;
+ border-color: $color-grey;
+ }
+ }
+}
diff --git a/ui/src/components/Tooltip/index.jsx b/ui/src/components/Tooltip/index.jsx
index 70d4e432de..8ddd4c6ab8 100644
--- a/ui/src/components/Tooltip/index.jsx
+++ b/ui/src/components/Tooltip/index.jsx
@@ -1,27 +1,39 @@
-import React from 'react';
-import classnames from 'classnames';
-import ReactTooltip from 'react-tooltip';
+import React from "react";
+import classnames from "classnames";
+import ReactTooltip from "react-tooltip";
-import './tooltip.scss';
+import "./tooltip.scss";
-const Tooltip = ({id, text, placement="top"}) => (
-
- {text}
-
-)
+const Tooltip = ({ id, text, placement = "top" }) => (
+
+ {text}
+
+);
export default Tooltip;
-export const TooltipWrapper = ({children, className, tooltipId, tooltipText}) => (
-
- {children}
-
-
-)
\ No newline at end of file
+export const TooltipWrapper = ({
+ children,
+ className,
+ tooltipId,
+ tooltipText,
+}) => (
+
+
+ {children}
+
+
+
+);
diff --git a/ui/src/components/Tooltip/tooltip.scss b/ui/src/components/Tooltip/tooltip.scss
index 352897358c..5e3ead57e3 100644
--- a/ui/src/components/Tooltip/tooltip.scss
+++ b/ui/src/components/Tooltip/tooltip.scss
@@ -1,12 +1,12 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.__react_component_tooltip.clarity-tooltip {
- font-weight: 400;
- font-size: 14px;
- line-height: 21px;
- padding: 10px 15px;
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 21px;
+ padding: 10px 15px;
- &.place-top::before {
- height: 0;
- }
-}
\ No newline at end of file
+ &.place-top::before {
+ height: 0;
+ }
+}
diff --git a/ui/src/components/ValueWithFallback/index.jsx b/ui/src/components/ValueWithFallback/index.jsx
index 631d9fc6d3..eba8ac9aa4 100644
--- a/ui/src/components/ValueWithFallback/index.jsx
+++ b/ui/src/components/ValueWithFallback/index.jsx
@@ -1,12 +1,11 @@
+import React from "react";
+import { isEmpty } from "lodash";
-import React from 'react';
-import { isEmpty } from 'lodash';
-
-import './value-with-fallback.scss';
+import "./value-with-fallback.scss";
export const ValueWithFallback = ({
- children,
- fallback =
-
+ children,
+ fallback =
-,
}) => {
- return isEmpty(children) ? fallback : <>{children}>;
-}
+ return isEmpty(children) ? fallback : <>{children}>;
+};
diff --git a/ui/src/components/ValueWithFallback/value-with-fallback.scss b/ui/src/components/ValueWithFallback/value-with-fallback.scss
index 3e869ec911..8c030819bf 100644
--- a/ui/src/components/ValueWithFallback/value-with-fallback.scss
+++ b/ui/src/components/ValueWithFallback/value-with-fallback.scss
@@ -1,5 +1,5 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
.value-with-fallback-empty {
- color: $color-grey;
+ color: $color-grey;
}
diff --git a/ui/src/components/VulnerabilitiesDisplay/index.jsx b/ui/src/components/VulnerabilitiesDisplay/index.jsx
index 6b7410079c..eea323f0d1 100644
--- a/ui/src/components/VulnerabilitiesDisplay/index.jsx
+++ b/ui/src/components/VulnerabilitiesDisplay/index.jsx
@@ -1,128 +1,177 @@
-import React from 'react';
-import { TooltipWrapper } from 'components/Tooltip';
-import Icon from 'components/Icon';
-import { SEVERITY_ITEMS } from 'components/SeverityDisplay';
-import { VULNERABIITY_FINDINGS_ITEM } from 'utils/systemConsts';
-import { formatNumber } from 'utils/utils';
+import React from "react";
+import { TooltipWrapper } from "components/Tooltip";
+import Icon from "components/Icon";
+import { SEVERITY_ITEMS } from "components/SeverityDisplay";
+import { VULNERABIITY_FINDINGS_ITEM } from "utils/systemConsts";
+import { formatNumber } from "utils/utils";
-import COLORS from 'utils/scss_variables.module.scss';
+import COLORS from "utils/scss_variables.module.scss";
-import './vulnerabilities-display.scss';
+import "./vulnerabilities-display.scss";
-export const getTotlalVulnerabilitiesFromCounters = (counters={}) => (
- Object.values(VULNERABILITY_SEVERITY_ITEMS).reduce((acc, curr) => {
- return acc + (counters[curr.totalKey] || 0);
- }, 0)
-)
+export const getTotlalVulnerabilitiesFromCounters = (counters = {}) =>
+ Object.values(VULNERABILITY_SEVERITY_ITEMS).reduce((acc, curr) => {
+ return acc + (counters[curr.totalKey] || 0);
+ }, 0);
export const VULNERABILITY_SEVERITY_ITEMS = {
- totalCriticalVulnerabilities: {
- totalKey: "totalCriticalVulnerabilities",
- countKey: "criticalVulnerabilitiesCount",
- title: "Critical",
- color: SEVERITY_ITEMS.CRITICAL.color,
- innerTextColor: "white"
- },
- totalHighVulnerabilities: {
- totalKey: "totalHighVulnerabilities",
- countKey: "highVulnerabilitiesCount",
- title: "High",
- color: SEVERITY_ITEMS.HIGH.color,
- innerTextColor: "white"
- },
- totalMediumVulnerabilities: {
- totalKey: "totalMediumVulnerabilities",
- countKey: "mediumVulnerabilitiesCount",
- title: "Medium",
- color: SEVERITY_ITEMS.MEDIUM.color,
- innerTextColor: COLORS["color-grey-black"]
- },
- totalLowVulnerabilities: {
- totalKey: "totalLowVulnerabilities",
- countKey: "lowVulnerabilitiesCount",
- title: "Low",
- color: SEVERITY_ITEMS.LOW.color,
- innerTextColor: COLORS["color-grey-black"]
- },
- totalNegligibleVulnerabilities: {
- totalKey: "totalNegligibleVulnerabilities",
- countKey: "negligibleVulnerabilitiesCount",
- title: "Negligible",
- color: SEVERITY_ITEMS.NEGLIGIBLE.color,
- innerTextColor: COLORS["color-grey-black"],
- backgroundColor: "transparent"
- }
-}
+ totalCriticalVulnerabilities: {
+ totalKey: "totalCriticalVulnerabilities",
+ countKey: "criticalVulnerabilitiesCount",
+ title: "Critical",
+ color: SEVERITY_ITEMS.CRITICAL.color,
+ innerTextColor: "white",
+ },
+ totalHighVulnerabilities: {
+ totalKey: "totalHighVulnerabilities",
+ countKey: "highVulnerabilitiesCount",
+ title: "High",
+ color: SEVERITY_ITEMS.HIGH.color,
+ innerTextColor: "white",
+ },
+ totalMediumVulnerabilities: {
+ totalKey: "totalMediumVulnerabilities",
+ countKey: "mediumVulnerabilitiesCount",
+ title: "Medium",
+ color: SEVERITY_ITEMS.MEDIUM.color,
+ innerTextColor: COLORS["color-grey-black"],
+ },
+ totalLowVulnerabilities: {
+ totalKey: "totalLowVulnerabilities",
+ countKey: "lowVulnerabilitiesCount",
+ title: "Low",
+ color: SEVERITY_ITEMS.LOW.color,
+ innerTextColor: COLORS["color-grey-black"],
+ },
+ totalNegligibleVulnerabilities: {
+ totalKey: "totalNegligibleVulnerabilities",
+ countKey: "negligibleVulnerabilitiesCount",
+ title: "Negligible",
+ color: SEVERITY_ITEMS.NEGLIGIBLE.color,
+ innerTextColor: COLORS["color-grey-black"],
+ backgroundColor: "transparent",
+ },
+};
-const TooltipContentDisplay = ({total, counters}) => (
-
-
{`Vulnerabilities: ${formatNumber(total || 0)}`}
-
- {
- Object.values(VULNERABILITY_SEVERITY_ITEMS).map(({totalKey, color}) => (
-
- {formatNumber(counters[totalKey] || 0)}
-
- ))
- }
-
+const TooltipContentDisplay = ({ total, counters }) => (
+
+
{`Vulnerabilities: ${formatNumber(total || 0)}`}
+
+ {Object.values(VULNERABILITY_SEVERITY_ITEMS).map(
+ ({ totalKey, color }) => (
+
+
+ {formatNumber(counters[totalKey] || 0)}
+
+ ),
+ )}
-)
+
+);
-const MinimizedVulnerabilitiesDisplay = ({id, highestSeverity, total, counters}) => {
- const {color, innerTextColor, backgroundColor} = VULNERABILITY_SEVERITY_ITEMS[highestSeverity];
+const MinimizedVulnerabilitiesDisplay = ({
+ id,
+ highestSeverity,
+ total,
+ counters,
+}) => {
+ const { color, innerTextColor, backgroundColor } =
+ VULNERABILITY_SEVERITY_ITEMS[highestSeverity];
- return (
-
-
}>
-
- {formatNumber(counters[highestSeverity] || 0)}
-
-
+ return (
+
+
+ }
+ >
+
+ {formatNumber(counters[highestSeverity] || 0)}
- )
-}
+
+
+ );
+};
-const CounterItemDisplay = ({count, title, color}) => (
-
-
{count || 0}
-
{title}
+const CounterItemDisplay = ({ count, title, color }) => (
+
+
+ {count || 0}
-)
+
{title}
+
+);
-const VulnerabilitiesDisplay = ({highestSeverity, total, counters}) => {
- const {color} = VULNERABILITY_SEVERITY_ITEMS[highestSeverity];
- const {color: vulnerabilitiesColor, title: vulnerabilitiesTitle, icon: vulnerabilitiesIcon} = VULNERABIITY_FINDINGS_ITEM;
+const VulnerabilitiesDisplay = ({ highestSeverity, total, counters }) => {
+ const { color } = VULNERABILITY_SEVERITY_ITEMS[highestSeverity];
+ const {
+ color: vulnerabilitiesColor,
+ title: vulnerabilitiesTitle,
+ icon: vulnerabilitiesIcon,
+ } = VULNERABIITY_FINDINGS_ITEM;
- return (
-
-
-
-
-
-
- {
- Object.values(VULNERABILITY_SEVERITY_ITEMS).map(({totalKey, title, color}) => (
-
- ))
- }
-
-
- )
-}
+ return (
+
+
+
+
+
+
+ {Object.values(VULNERABILITY_SEVERITY_ITEMS).map(
+ ({ totalKey, title, color }) => (
+
+ ),
+ )}
+
+
+ );
+};
+
+const VulnerabilitiesDisplayWrapper = ({
+ counters = {},
+ isMinimized = false,
+ minimizedTooltipId = null,
+}) => {
+ const total = getTotlalVulnerabilitiesFromCounters(counters);
+ const highestSeverity = (
+ Object.values(VULNERABILITY_SEVERITY_ITEMS).find(
+ ({ totalKey }) => !!counters[totalKey] && counters[totalKey] > 0,
+ ) || VULNERABILITY_SEVERITY_ITEMS.totalNegligibleVulnerabilities
+ ).totalKey;
-const VulnerabilitiesDisplayWrapper = ({counters={}, isMinimized=false, minimizedTooltipId=null}) => {
- const total = getTotlalVulnerabilitiesFromCounters(counters);
-
- const highestSeverity = (Object.values(VULNERABILITY_SEVERITY_ITEMS).find(({totalKey}) =>
- !!counters[totalKey] && counters[totalKey] > 0) || VULNERABILITY_SEVERITY_ITEMS.totalNegligibleVulnerabilities).totalKey;
+ const DisplayComponent = isMinimized
+ ? MinimizedVulnerabilitiesDisplay
+ : VulnerabilitiesDisplay;
- const DisplayComponent = isMinimized ? MinimizedVulnerabilitiesDisplay : VulnerabilitiesDisplay;
-
- return (
-
- )
-}
+ return (
+
+ );
+};
export default VulnerabilitiesDisplayWrapper;
diff --git a/ui/src/components/VulnerabilitiesDisplay/vulnerabilities-display.scss b/ui/src/components/VulnerabilitiesDisplay/vulnerabilities-display.scss
index 1aad79213e..8d5e49b165 100644
--- a/ui/src/components/VulnerabilitiesDisplay/vulnerabilities-display.scss
+++ b/ui/src/components/VulnerabilitiesDisplay/vulnerabilities-display.scss
@@ -1,69 +1,69 @@
-@import 'utils/scss_variables.module.scss';
+@import "utils/scss_variables.module.scss";
$inner-padding: 21px;
.vulnerabilities-minimized-display-wrapper {
- display: inline-block;
- text-align: left;
+ display: inline-block;
+ text-align: left;
- .vulnerabilities-minimized-display-summary-item {
- padding: 2px 5px;
- font-weight: 400;
- font-size: 14px;
- line-height: 18px;
- }
+ .vulnerabilities-minimized-display-summary-item {
+ padding: 2px 5px;
+ font-weight: 400;
+ font-size: 14px;
+ line-height: 18px;
+ }
}
.vulnerabilities-minimized-tooltip-content {
- .vulnerabilities-tooltip-counters {
- display: flex;
- border-top: 1px solid $color-grey-dark;
- padding-top: 5px;
- margin-top: 5px;
+ .vulnerabilities-tooltip-counters {
+ display: flex;
+ border-top: 1px solid $color-grey-dark;
+ padding-top: 5px;
+ margin-top: 5px;
- .vulnerabilities-tooltip-counters-item {
- display: flex;
- align-items: center;
+ .vulnerabilities-tooltip-counters-item {
+ display: flex;
+ align-items: center;
- &:not(:last-child) {
- margin-right: 10px;
- }
- .icon {
- margin-right: 2px;
- }
- }
+ &:not(:last-child) {
+ margin-right: 10px;
+ }
+ .icon {
+ margin-right: 2px;
+ }
}
+ }
}
.vulnerabilities-display-wrapper {
+ display: flex;
+ align-items: center;
+
+ .icon {
+ margin-right: 5px;
+ }
+ .vulnerabilities-display-summary {
+ display: flex;
+ align-items: center;
+ border-right: 1px solid $color-grey-light;
+ padding-right: $inner-padding;
+ }
+ .vulnerabilities-display-counters {
display: flex;
align-items: center;
- .icon {
- margin-right: 5px;
+ .vulnerabilities-display-counter-item {
+ padding-left: $inner-padding;
}
- .vulnerabilities-display-summary {
- display: flex;
- align-items: center;
- border-right: 1px solid $color-grey-light;
- padding-right: $inner-padding;
+ }
+ .vulnerabilities-display-counter-item {
+ min-width: 42px;
+
+ .vulnerabilities-counter-item-count {
+ font-size: 22px;
}
- .vulnerabilities-display-counters {
- display: flex;
- align-items: center;
-
- .vulnerabilities-display-counter-item {
- padding-left: $inner-padding;
- }
+ .vulnerabilities-counter-item-title {
+ font-size: 11px;
}
- .vulnerabilities-display-counter-item {
- min-width: 42px;
-
- .vulnerabilities-counter-item-count {
- font-size: 22px;
- }
- .vulnerabilities-counter-item-title {
- font-size: 11px;
- }
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/ui/src/components/WizardModal/index.jsx b/ui/src/components/WizardModal/index.jsx
index f98d617a33..be6afb6aa9 100644
--- a/ui/src/components/WizardModal/index.jsx
+++ b/ui/src/components/WizardModal/index.jsx
@@ -1,122 +1,172 @@
-import React, { useState, useEffect } from 'react';
-import classnames from 'classnames';
-import { Formik, Form, useFormikContext } from 'formik';
-import { cloneDeep, isNull, isEmpty } from 'lodash';
-import { useFetch, FETCH_METHODS, usePrevious } from 'hooks';
-import Button from 'components/Button';
-import Modal from 'components/Modal';
-import Loader from 'components/Loader';
-import Title from 'components/Title';
-import Arrow, { ARROW_NAMES } from 'components/Arrow';
-
-import './wizard-modal.scss';
-
-const Wizard = ({steps, onClose, submitUrl, onSubmitSuccess, getSubmitParams}) => {
- const {values, isSubmitting, isValidating, setSubmitting, status, setStatus, isValid, setErrors, validateForm} = useFormikContext();
-
- const [activeStepId, setActiveStepId] = useState(steps[0].id);
-
- const activeStepIndex = steps.findIndex(({id}) => id === activeStepId);
- const {component: ActiveStepComponent, title: activeTitle} = steps[activeStepIndex];
- const {title: nextStepTitle, id: nextStepId} = steps[activeStepIndex + 1] || {};
-
- const disableStepDone = isSubmitting || isValidating || !isValid;
-
- const [{loading, data, error}, submitFormData] = useFetch(submitUrl, {loadOnMount: false});
- const prevLoading = usePrevious(loading);
-
- const onStepClick = (stepId) => {
- if (disableStepDone || stepId === activeStepId) {
- return;
- }
+import React, { useState, useEffect } from "react";
+import classnames from "classnames";
+import { Formik, Form, useFormikContext } from "formik";
+import { cloneDeep, isNull, isEmpty } from "lodash";
+import { useFetch, FETCH_METHODS, usePrevious } from "hooks";
+import Button from "components/Button";
+import Modal from "components/Modal";
+import Loader from "components/Loader";
+import Title from "components/Title";
+import Arrow, { ARROW_NAMES } from "components/Arrow";
+
+import "./wizard-modal.scss";
+
+const Wizard = ({
+ steps,
+ onClose,
+ submitUrl,
+ onSubmitSuccess,
+ getSubmitParams,
+}) => {
+ const {
+ values,
+ isSubmitting,
+ isValidating,
+ setSubmitting,
+ status,
+ setStatus,
+ isValid,
+ setErrors,
+ validateForm,
+ } = useFormikContext();
+
+ const [activeStepId, setActiveStepId] = useState(steps[0].id);
+
+ const activeStepIndex = steps.findIndex(({ id }) => id === activeStepId);
+ const { component: ActiveStepComponent, title: activeTitle } =
+ steps[activeStepIndex];
+ const { title: nextStepTitle, id: nextStepId } =
+ steps[activeStepIndex + 1] || {};
- setActiveStepId(stepId);
+ const disableStepDone = isSubmitting || isValidating || !isValid;
+
+ const [{ loading, data, error }, submitFormData] = useFetch(submitUrl, {
+ loadOnMount: false,
+ });
+ const prevLoading = usePrevious(loading);
+
+ const onStepClick = (stepId) => {
+ if (disableStepDone || stepId === activeStepId) {
+ return;
}
- const handleSubmit = () => {
- const submitQueryParams = !!getSubmitParams ? getSubmitParams(cloneDeep(values)) : {};
- submitFormData({method: FETCH_METHODS.POST, submitData: values, ...submitQueryParams});
+ setActiveStepId(stepId);
+ };
+
+ const handleSubmit = () => {
+ const submitQueryParams = !!getSubmitParams
+ ? getSubmitParams(cloneDeep(values))
+ : {};
+ submitFormData({
+ method: FETCH_METHODS.POST,
+ submitData: values,
+ ...submitQueryParams,
+ });
+ };
+
+ useEffect(() => {
+ validateForm();
+ }, [activeStepId, validateForm]);
+
+ useEffect(() => {
+ if (prevLoading && !loading) {
+ setSubmitting(false);
+ setStatus(null);
+
+ if (isNull(error)) {
+ if (!!onSubmitSuccess) {
+ onSubmitSuccess(data);
+ }
+ } else {
+ const { message, errors } = error;
+
+ if (!!message) {
+ setStatus(message);
+ }
+
+ if (!isEmpty(errors)) {
+ setErrors(errors);
+ }
+ }
}
+ }, [
+ prevLoading,
+ loading,
+ error,
+ data,
+ setSubmitting,
+ setStatus,
+ onSubmitSuccess,
+ setErrors,
+ ]);
- useEffect(() => {
- validateForm();
- }, [activeStepId, validateForm]);
-
- useEffect(() => {
- if (prevLoading && !loading) {
- setSubmitting(false);
- setStatus(null);
-
- if (isNull(error)) {
- if (!!onSubmitSuccess) {
- onSubmitSuccess(data);
- }
- } else {
- const {message, errors} = error;
-
- if (!!message) {
- setStatus(message);
- }
-
- if (!isEmpty(errors)) {
- setErrors(errors);
- }
- }
- }
- }, [prevLoading, loading, error, data, setSubmitting, setStatus, onSubmitSuccess, setErrors]);
-
- if (isSubmitting) {
- return
;
- }
-
- return (
-