From 5479b2f78402ca438e020f364cc86e8282709491 Mon Sep 17 00:00:00 2001 From: Andrii Mavdryk Date: Thu, 9 Nov 2023 18:36:25 +0200 Subject: [PATCH] Impl [Projects] Provide validation rules on labels --- src/actions/projects.js | 14 -- src/components/Details/details.util.js | 2 +- .../CreateProjectDialog.js | 162 +++++++++--------- .../createProjectDialog.scss | 26 +-- src/components/ProjectsPage/Projects.js | 37 ++-- src/components/ProjectsPage/ProjectsView.js | 14 -- src/constants.js | 4 - .../FormDataInputsRow/FormDataInputsRow.js | 2 +- .../FormEnvironmentVariablesRow.js | 2 +- .../FormParametersRow/FormParametersRow.js | 6 +- .../FormVolumesTable/formVolumesTable.util.js | 2 +- .../PanelResourcesUnits.js | 2 +- .../RegisterArtifactModalForm.js | 2 +- .../RegisterModelModal/RegisterModelModal.js | 2 +- src/reducers/projectReducer.js | 45 ----- src/scss/main.scss | 1 - 16 files changed, 107 insertions(+), 216 deletions(-) diff --git a/src/actions/projects.js b/src/actions/projects.js index 19f13c893..b8d5e1bb8 100644 --- a/src/actions/projects.js +++ b/src/actions/projects.js @@ -72,14 +72,10 @@ import { FETCH_PROJECTS_BEGIN, FETCH_PROJECTS_FAILURE, FETCH_PROJECTS_SUCCESS, - REMOVE_NEW_PROJECT, REMOVE_NEW_PROJECT_ERROR, REMOVE_PROJECT_SUMMARY, REMOVE_PROJECT_DATA, REMOVE_PROJECTS, - SET_NEW_PROJECT_DESCRIPTION, - SET_NEW_PROJECT_LABELS, - SET_NEW_PROJECT_NAME, SET_PROJECT_DATA, SET_PROJECT_LABELS, FETCH_PROJECTS_NAMES_BEGIN, @@ -587,20 +583,10 @@ const projectsAction = { type: FETCH_PROJECT_WORKFLOWS_SUCCESS, payload: workflows }), - removeNewProject: () => ({ type: REMOVE_NEW_PROJECT }), removeNewProjectError: () => ({ type: REMOVE_NEW_PROJECT_ERROR }), removeProjectData: () => ({ type: REMOVE_PROJECT_DATA }), removeProjectSummary: () => ({ type: REMOVE_PROJECT_SUMMARY }), removeProjects: () => ({ type: REMOVE_PROJECTS }), - setNewProjectDescription: description => ({ - type: SET_NEW_PROJECT_DESCRIPTION, - payload: description - }), - setNewProjectLabels: (label, labels) => ({ - type: SET_NEW_PROJECT_LABELS, - payload: { ...labels, ...label } - }), - setNewProjectName: name => ({ type: SET_NEW_PROJECT_NAME, payload: name }), setProjectData: data => ({ type: SET_PROJECT_DATA, payload: data diff --git a/src/components/Details/details.util.js b/src/components/Details/details.util.js index 0bda8d3b2..4eef8fbfb 100644 --- a/src/components/Details/details.util.js +++ b/src/components/Details/details.util.js @@ -97,7 +97,7 @@ export const generateArtifactsContent = (detailsType, selectedItem, projectName) name: 'common.tag', additionalRules: { name: 'tagUniqueness', - label: 'Artifact tag should be unique', + label: 'Artifact tag must be unique', pattern: isArtifactTagUnique(projectName, detailsType, selectedItem), async: true } diff --git a/src/components/ProjectsPage/CreateProjectDialog/CreateProjectDialog.js b/src/components/ProjectsPage/CreateProjectDialog/CreateProjectDialog.js index e6db5397e..1d3123741 100644 --- a/src/components/ProjectsPage/CreateProjectDialog/CreateProjectDialog.js +++ b/src/components/ProjectsPage/CreateProjectDialog/CreateProjectDialog.js @@ -18,31 +18,41 @@ under the Apache 2.0 license is conditioned upon your compliance with such restriction. */ import React from 'react' -import { useSelector } from 'react-redux' import PropTypes from 'prop-types' +import arrayMutators from 'final-form-arrays' +import { Form } from 'react-final-form' +import { useSelector } from 'react-redux' import ErrorMessage from '../../../common/ErrorMessage/ErrorMessage' -import Input from '../../../common/Input/Input' import Loader from '../../../common/Loader/Loader' -import ProjectLabels from '../../Project/ProjectLabels/ProjectLabels' -import { Button, PopUpDialog } from 'igz-controls/components' +import { Button, FormChipCell, FormInput, FormTextarea, PopUpDialog } from 'igz-controls/components' -import { getValidationRules } from 'igz-controls/utils/validation.util' import { SECONDARY_BUTTON, TERTIARY_BUTTON } from 'igz-controls/constants' +import { createForm } from 'final-form' +import { getChipOptions } from '../../../utils/getChipOptions' +import { getValidationRules } from 'igz-controls/utils/validation.util' +import { setFieldState } from 'igz-controls/utils/form.util' import './createProjectDialog.scss' const CreateProjectDialog = ({ closeNewProjectPopUp, handleCreateProject, - isNameValid, - removeNewProjectError, - setNameValid, - setNewProjectDescription, - setNewProjectLabels, - setNewProjectName + removeNewProjectError }) => { const projectStore = useSelector(store => store.projectStore) + const initialValues = { + name: '', + description: '', + labels: [] + } + const formRef = React.useRef( + createForm({ + initialValues, + mutators: { ...arrayMutators, setFieldState }, + onSubmit: () => {} + }) + ) return ( {projectStore.loading && } -
-
- setNameValid(value)} - type="text" - value={projectStore.newProject.name} - validationRules={getValidationRules('project.name')} - /> - -
- Labels: - -
-
- {projectStore.newProject.error && ( - { - if (projectStore.newProject.error) { - removeNewProjectError() - } - }} - message={projectStore.newProject.error} - /> - )} -
-
- +
{}}> + {formState => { + return ( + <> +
+ +
+
+ +
+
+ +
+ {projectStore.newProject.error && ( +
+ { + if (projectStore.newProject.error) { + removeNewProjectError() + } + }} + message={projectStore.newProject.error} + /> +
+ )} +
+
+ + ) + }} +
) } @@ -119,11 +131,7 @@ const CreateProjectDialog = ({ CreateProjectDialog.propTypes = { closeNewProjectPopUp: PropTypes.func.isRequired, handleCreateProject: PropTypes.func.isRequired, - isNameValid: PropTypes.bool.isRequired, - removeNewProjectError: PropTypes.func.isRequired, - setNameValid: PropTypes.func.isRequired, - setNewProjectDescription: PropTypes.func.isRequired, - setNewProjectName: PropTypes.func.isRequired + removeNewProjectError: PropTypes.func.isRequired } export default CreateProjectDialog diff --git a/src/components/ProjectsPage/CreateProjectDialog/createProjectDialog.scss b/src/components/ProjectsPage/CreateProjectDialog/createProjectDialog.scss index 34d959dff..c4830f62f 100644 --- a/src/components/ProjectsPage/CreateProjectDialog/createProjectDialog.scss +++ b/src/components/ProjectsPage/CreateProjectDialog/createProjectDialog.scss @@ -1,27 +1,5 @@ .create-project-dialog { - .pop-up-dialog { - &__footer-container { - align-items: center; - } - - &__form { - margin-bottom: 20px; - } - - .input-wrapper { - margin-bottom: 10px; - - &:last-child { - margin-bottom: 0; - } - } - - .chips-wrapper { - flex-wrap: wrap; - } - } - - .error-message { - max-width: 235px; + .error { + width: 100%; } } diff --git a/src/components/ProjectsPage/Projects.js b/src/components/ProjectsPage/Projects.js index 3e359aa7d..7fad69fdc 100644 --- a/src/components/ProjectsPage/Projects.js +++ b/src/components/ProjectsPage/Projects.js @@ -51,12 +51,8 @@ const Projects = ({ fetchProjectsNames, fetchProjectsSummary, projectStore, - removeNewProject, removeNewProjectError, - removeProjects, - setNewProjectDescription, - setNewProjectLabels, - setNewProjectName + removeProjects }) => { const [actionsMenu, setActionsMenu] = useState({}) const [confirmData, setConfirmData] = useState(null) @@ -66,7 +62,6 @@ const Projects = ({ const [filterByName, setFilterByName] = useState('') const [filterMatches, setFilterMatches] = useState([]) const [isDescendingOrder, setIsDescendingOrder] = useState(false) - const [isNameValid, setNameValid] = useState(true) const [selectedProjectsState, setSelectedProjectsState] = useState('active') const [sortProjectId, setSortProjectId] = useState('byName') const [source] = useState(axios.CancelToken.source()) @@ -356,34 +351,27 @@ const Projects = ({ removeNewProjectError() } - removeNewProject() - setNameValid(true) setCreateProject(false) - }, [projectStore.newProject.error, removeNewProject, removeNewProjectError]) + }, [projectStore.newProject.error, removeNewProjectError]) - const handleCreateProject = e => { + const handleCreateProject = (e, formState) => { e.preventDefault() - if (e.currentTarget.checkValidity()) { - if (projectStore.newProject.name.length === 0) { - setNameValid(false) - return false - } else if (isNameValid) { - setNameValid(true) - } - + if (e.currentTarget.checkValidity() && formState.valid) { createNewProject({ metadata: { - name: projectStore.newProject.name, - labels: projectStore.newProject.labels + name: formState.values.name, + labels: formState.values.labels?.reduce((acc, labelData) => { + acc[labelData.key] = labelData.value + return acc + }, {}) ?? {} }, spec: { - description: projectStore.newProject.description + description: formState.values.description } }).then(result => { if (result) { setCreateProject(false) - removeNewProject() refreshProjects() fetchProjectsNames() } @@ -406,7 +394,6 @@ const Projects = ({ handleSelectSortOption={handleSelectSortOption} handleSearchOnFocus={handleSearchOnFocus} isDescendingOrder={isDescendingOrder} - isNameValid={isNameValid} projectStore={projectStore} refreshProjects={refreshProjects} removeNewProjectError={removeNewProjectError} @@ -415,10 +402,6 @@ const Projects = ({ setFilterByName={setFilterByName} setFilterMatches={setFilterMatches} setIsDescendingOrder={setIsDescendingOrder} - setNameValid={setNameValid} - setNewProjectDescription={setNewProjectDescription} - setNewProjectName={setNewProjectName} - setNewProjectLabels={setNewProjectLabels} setSelectedProjectsState={setSelectedProjectsState} sortProjectId={sortProjectId} urlParams={urlParams} diff --git a/src/components/ProjectsPage/ProjectsView.js b/src/components/ProjectsPage/ProjectsView.js index 7ed66cdf9..9732e20ca 100644 --- a/src/components/ProjectsPage/ProjectsView.js +++ b/src/components/ProjectsPage/ProjectsView.js @@ -55,7 +55,6 @@ const ProjectsView = ({ handleSelectSortOption, handleSearchOnFocus, isDescendingOrder, - isNameValid, projectStore, refreshProjects, removeNewProjectError, @@ -64,10 +63,6 @@ const ProjectsView = ({ setFilterByName, setFilterMatches, setIsDescendingOrder, - setNameValid, - setNewProjectDescription, - setNewProjectLabels, - setNewProjectName, setSelectedProjectsState, sortProjectId }) => { @@ -83,12 +78,7 @@ const ProjectsView = ({ )} {confirmData && ( @@ -211,7 +201,6 @@ ProjectsView.propTypes = { handleCreateProject: PropTypes.func.isRequired, handleSearchOnFocus: PropTypes.func.isRequired, handleSelectSortOption: PropTypes.func.isRequired, - isNameValid: PropTypes.bool.isRequired, refreshProjects: PropTypes.func.isRequired, removeNewProjectError: PropTypes.func.isRequired, selectedProjectsState: PropTypes.string.isRequired, @@ -219,9 +208,6 @@ ProjectsView.propTypes = { setFilterByName: PropTypes.func.isRequired, setFilterMatches: PropTypes.func.isRequired, setIsDescendingOrder: PropTypes.func.isRequired, - setNewProjectDescription: PropTypes.func.isRequired, - setNameValid: PropTypes.func.isRequired, - setNewProjectName: PropTypes.func.isRequired, setSelectedProjectsState: PropTypes.func.isRequired, sortProjectId: PropTypes.string.isRequired } diff --git a/src/constants.js b/src/constants.js index deef4a8f7..387f949c4 100644 --- a/src/constants.js +++ b/src/constants.js @@ -423,14 +423,10 @@ export const FETCH_PROJECTS_SUMMARY_SUCCESS = 'FETCH_PROJECTS_SUMMARY_SUCCESS' export const FETCH_PROJECT_WORKFLOWS_BEGIN = 'FETCH_PROJECT_WORKFLOWS_BEGIN' export const FETCH_PROJECT_WORKFLOWS_FAILURE = 'FETCH_PROJECT_WORKFLOWS_FAILURE' export const FETCH_PROJECT_WORKFLOWS_SUCCESS = 'FETCH_PROJECT_WORKFLOWS_SUCCESS' -export const REMOVE_NEW_PROJECT = 'REMOVE_NEW_PROJECT' export const REMOVE_NEW_PROJECT_ERROR = 'REMOVE_NEW_PROJECT_ERROR' export const REMOVE_PROJECTS = 'REMOVE_PROJECTS' export const REMOVE_PROJECT_SUMMARY = 'REMOVE_PROJECT_SUMMARY' export const REMOVE_PROJECT_DATA = 'REMOVE_PROJECT_DATA' -export const SET_NEW_PROJECT_DESCRIPTION = 'SET_NEW_PROJECT_DESCRIPTION' -export const SET_NEW_PROJECT_LABELS = 'SET_NEW_PROJECT_LABELS' -export const SET_NEW_PROJECT_NAME = 'SET_NEW_PROJECT_NAME' /*=========== DETAILS =============*/ diff --git a/src/elements/FormDataInputsTable/FormDataInputsRow/FormDataInputsRow.js b/src/elements/FormDataInputsTable/FormDataInputsRow/FormDataInputsRow.js index b554bb929..2ae108c0d 100644 --- a/src/elements/FormDataInputsTable/FormDataInputsRow/FormDataInputsRow.js +++ b/src/elements/FormDataInputsTable/FormDataInputsRow/FormDataInputsRow.js @@ -93,7 +93,7 @@ const FormDataInputsRow = ({ validationRules={[ { name: 'uniqueness', - label: 'Name should be unique', + label: 'Name must be unique', pattern: newValue => uniquenessValidator(fields, newValue) } ]} diff --git a/src/elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesRow/FormEnvironmentVariablesRow.js b/src/elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesRow/FormEnvironmentVariablesRow.js index 8829aab88..22910a43f 100644 --- a/src/elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesRow/FormEnvironmentVariablesRow.js +++ b/src/elements/FormEnvironmentVariablesTable/FormEnvironmentVariablesRow/FormEnvironmentVariablesRow.js @@ -81,7 +81,7 @@ const FormEnvironmentVariablesRow = ({ validationRules={[ { name: 'uniqueness', - label: 'Name should be unique', + label: 'Name must be unique', pattern: newValue => uniquenessValidator(fields, newValue) } ]} diff --git a/src/elements/FormParametersTable/FormParametersRow/FormParametersRow.js b/src/elements/FormParametersTable/FormParametersRow/FormParametersRow.js index e815a5a62..f35f394d5 100644 --- a/src/elements/FormParametersTable/FormParametersRow/FormParametersRow.js +++ b/src/elements/FormParametersTable/FormParametersRow/FormParametersRow.js @@ -182,9 +182,9 @@ const FormParametersRow = ({ const getValueTip = parameterType => { switch (parameterType) { case parameterTypeMap: - return 'The valid `map` type should be in the JSON format\n e.g. {"hello": "world"}' + return 'The valid `map` type must be in the JSON format\n e.g. {"hello": "world"}' case parameterTypeList: - return 'The valid `list` type should be in the JSON format\n e.g. ["hello", "world"]' + return 'The valid `list` type must be in the JSON format\n e.g. ["hello", "world"]' default: return '' } @@ -281,7 +281,7 @@ const FormParametersRow = ({ validationRules={[ { name: 'uniqueness', - label: 'Name should be unique', + label: 'Name must be unique', pattern: newValue => uniquenessValidator(fields, fieldsPath, newValue) } ]} diff --git a/src/elements/FormVolumesTable/formVolumesTable.util.js b/src/elements/FormVolumesTable/formVolumesTable.util.js index 2a8066bda..5c997ac9e 100644 --- a/src/elements/FormVolumesTable/formVolumesTable.util.js +++ b/src/elements/FormVolumesTable/formVolumesTable.util.js @@ -88,7 +88,7 @@ export const generateVolumeInputsData = (selectedItem, fields, editingItem) => { validationRules: [ { name: 'uniqueness', - label: 'Name should be unique', + label: 'Name must be unique', pattern: newValue => { return !fields.value.some(({ data: { name } }, index) => { return newValue.trim() === name && index !== editingItemIndex diff --git a/src/elements/PanelResourcesUnits/PanelResourcesUnits.js b/src/elements/PanelResourcesUnits/PanelResourcesUnits.js index be55bbe1f..62f5d21b9 100644 --- a/src/elements/PanelResourcesUnits/PanelResourcesUnits.js +++ b/src/elements/PanelResourcesUnits/PanelResourcesUnits.js @@ -160,7 +160,7 @@ const PanelResourcesUnits = ({ density="dense" disabled={isPanelEditMode} invalid={!validation.isGpuLimitValid} - invalidText="The minimum value should be 1" + invalidText="The minimum value must be 1" label="Limit" labelType="labelAtTop" min={1} diff --git a/src/elements/RegisterArtifactModalForm/RegisterArtifactModalForm.js b/src/elements/RegisterArtifactModalForm/RegisterArtifactModalForm.js index 81b82fd4d..26eeb1179 100644 --- a/src/elements/RegisterArtifactModalForm/RegisterArtifactModalForm.js +++ b/src/elements/RegisterArtifactModalForm/RegisterArtifactModalForm.js @@ -88,7 +88,7 @@ const RegisterArtifactModalForm = ({ tip={messagesByKind?.nameTip} validationRules={getValidationRules('artifact.name', { name: 'ArtifactExists', - label: 'Artifact name should be unique', + label: 'Artifact name must be unique', pattern: isArtifactNameUnique(projectName), async: true })} diff --git a/src/elements/RegisterModelModal/RegisterModelModal.js b/src/elements/RegisterModelModal/RegisterModelModal.js index 0f592e529..6ed08738d 100644 --- a/src/elements/RegisterModelModal/RegisterModelModal.js +++ b/src/elements/RegisterModelModal/RegisterModelModal.js @@ -168,7 +168,7 @@ function RegisterModelModal({ actions, isOpen, onResolve, projectName, refresh } tip="Artifacts names in the same project must be unique." validationRules={getValidationRules('artifact.name', { name: 'ArtifactExists', - label: 'Artifact name should be unique', + label: 'Artifact name must be unique', pattern: isArtifactNameUnique(projectName), async: true })} diff --git a/src/reducers/projectReducer.js b/src/reducers/projectReducer.js index 62e73a127..da3a75baa 100644 --- a/src/reducers/projectReducer.js +++ b/src/reducers/projectReducer.js @@ -64,14 +64,10 @@ import { FETCH_PROJECTS_BEGIN, FETCH_PROJECTS_FAILURE, FETCH_PROJECTS_SUCCESS, - REMOVE_NEW_PROJECT, REMOVE_NEW_PROJECT_ERROR, REMOVE_PROJECT_SUMMARY, REMOVE_PROJECT_DATA, REMOVE_PROJECTS, - SET_NEW_PROJECT_DESCRIPTION, - SET_NEW_PROJECT_LABELS, - SET_NEW_PROJECT_NAME, SET_PROJECT_LABELS, FETCH_PROJECT_FEATURE_SETS_BEGIN, FETCH_PROJECT_FEATURE_SETS_SUCCESS, @@ -95,9 +91,6 @@ const initialState = { error: null, loading: false, newProject: { - name: '', - description: '', - labels: null, error: null }, project: { @@ -221,7 +214,6 @@ const projectReducer = (state = initialState, { type, payload }) => { ...state, loading: false, newProject: { - ...state.newProject, error: payload } } @@ -230,7 +222,6 @@ const projectReducer = (state = initialState, { type, payload }) => { ...state, loading: false, newProject: { - ...state.newProject, error: null } } @@ -771,15 +762,6 @@ const projectReducer = (state = initialState, { type, payload }) => { } } } - case REMOVE_NEW_PROJECT: - return { - ...state, - newProject: { - name: '', - description: '', - labels: null - } - } case REMOVE_PROJECT_SUMMARY: return { ...state, @@ -805,36 +787,9 @@ const projectReducer = (state = initialState, { type, payload }) => { return { ...state, newProject: { - ...state.newProject, error: null } } - case SET_NEW_PROJECT_DESCRIPTION: - return { - ...state, - newProject: { - ...state.newProject, - description: payload - } - } - case SET_NEW_PROJECT_LABELS: - return { - ...state, - newProject: { - ...state.newProject, - labels: { - ...payload - } - } - } - case SET_NEW_PROJECT_NAME: - return { - ...state, - newProject: { - ...state.newProject, - name: payload - } - } case SET_PROJECT_DATA: { return { ...state, diff --git a/src/scss/main.scss b/src/scss/main.scss index f7924ff46..cc138cc51 100644 --- a/src/scss/main.scss +++ b/src/scss/main.scss @@ -208,7 +208,6 @@ main { display: inline-flex; flex: 1; align-items: center; - overflow: hidden; @include tableDet(8px, 5px, 8px, 10px);