diff --git a/src/app/api/version.jsx b/src/app/api/version.jsx new file mode 100755 index 0000000000..8c76188ba2 --- /dev/null +++ b/src/app/api/version.jsx @@ -0,0 +1,19 @@ +import { useMutation, useQuery, useQueryClient } from 'react-query'; + +import { useAxios } from './axios'; + +const usePutVersion = () => { + const axios = useAxios(); + const queryClient = useQueryClient(); + return useMutation(async (data) => axios.put('versions', data) + .then((response) => response), { + onSuccess: () => { + queryClient.invalidateQueries('developers'); + queryClient.invalidateQueries('developers/hierarchy'); + }, + }); +}; + +export { + usePutVersion, +}; diff --git a/src/app/components/products/product-view.jsx b/src/app/components/products/product-view.jsx index 97e1e6902b..9839ca8d51 100755 --- a/src/app/components/products/product-view.jsx +++ b/src/app/components/products/product-view.jsx @@ -226,7 +226,10 @@ function ChplProductView({ product, productCount, dispatch }) { Edit Product v.id === selectedVersion), + productId: product.id, + })} disabled={selectedVersion === 'all'} > Edit Version diff --git a/src/app/components/version/version-edit.jsx b/src/app/components/version/version-edit.jsx new file mode 100755 index 0000000000..5cef60c62d --- /dev/null +++ b/src/app/components/version/version-edit.jsx @@ -0,0 +1,166 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { + Card, + CardHeader, + CardContent, + Container, + makeStyles, +} from '@material-ui/core'; +import { + arrayOf, + bool, + func, + object, + string, +} from 'prop-types'; +import { useFormik } from 'formik'; +import * as yup from 'yup'; + +import { ChplActionBar } from 'components/action-bar'; +import { ChplTextField } from 'components/util'; +import { eventTrack } from 'services/analytics.service'; +import { DeveloperContext, UserContext, useAnalyticsContext } from 'shared/contexts'; +import { utilStyles } from 'themes'; + +const useStyles = makeStyles({ + ...utilStyles, + content: { + display: 'grid', + gridTemplateColumns: '1fr 1fr', + gap: '16px', + alignItems: 'start', + }, + developerHeader: { + margin: '0', + fontSize: '1.25em', + }, +}); + +const validationSchema = yup.object({ + version: yup.string() + .required('Version is required'), +}); + +function ChplVersionEdit(props) { + const { + dispatch, + errorMessages: initialErrorMessages, + isInvalid: initialIsInvalid, + isProcessing, + isSplitting, + version, + } = props; + const { analytics } = useAnalyticsContext(); + const { developer } = useContext(DeveloperContext); + const [errorMessages, setErrorMessages] = useState([]); + const [warnings, setWarnings] = useState([]); + const [isInvalid, setIsInvalid] = useState(false); + const classes = useStyles(); + let formik; + + useEffect(() => { + setIsInvalid(initialIsInvalid); + }, [initialIsInvalid]); + + useEffect(() => { + setErrorMessages(initialErrorMessages); + }, [initialErrorMessages]); + + const cancel = () => { + eventTrack({ + ...analytics, + event: 'Cancel Version Edit', + }); + dispatch('cancel'); + }; + + const save = () => { + const updatedVersion = { + ...version, + version: formik.values.version, + }; + dispatch('save', updatedVersion); + }; + + const handleDispatch = (action) => { + switch (action) { + case 'cancel': + cancel(); + break; + case 'save': + formik.submitForm(); + break; + // no default + } + }; + + const isActionDisabled = () => isInvalid || !formik.isValid; + + formik = useFormik({ + initialValues: { + version: version.version || '', + }, + onSubmit: () => { + save(); + }, + validationSchema, + }); + + return ( + + + { isSplitting + && ( + + )} + { !isSplitting + && ( + + )} + + + + + + + ); +} + +export default ChplVersionEdit; + +ChplVersionEdit.propTypes = { + dispatch: func.isRequired, + errorMessages: arrayOf(string).isRequired, + isInvalid: bool.isRequired, + isProcessing: bool, + isSplitting: bool.isRequired, + version: object.isRequired, +}; + +ChplVersionEdit.defaultProps = { + isProcessing: false, +}; diff --git a/src/app/components/version/version.jsx b/src/app/components/version/version.jsx new file mode 100755 index 0000000000..e0743d890a --- /dev/null +++ b/src/app/components/version/version.jsx @@ -0,0 +1,55 @@ +import React, { useEffect, useState } from 'react'; +import { + arrayOf, + bool, + func, + object, + string, +} from 'prop-types'; + +import ChplVersionEdit from './version-edit'; + +function ChplVersion({ + dispatch, + errorMessages, + isInvalid: initialIsInvalid, + isProcessing, + isSplitting, + version, +}) { + const [isInvalid, setIsInvalid] = useState(false); + + useEffect(() => { + setIsInvalid(initialIsInvalid); + }, [initialIsInvalid]); + + return ( + + ); +} + +export default ChplVersion; + +ChplVersion.propTypes = { + dispatch: func, + errorMessages: arrayOf(string), + isInvalid: bool, + isProcessing: bool, + isSplitting: bool, + version: object.isRequired, +}; + +ChplVersion.defaultProps = { + dispatch: () => {}, + errorMessages: [], + isInvalid: false, + isProcessing: false, + isSplitting: false, +}; diff --git a/src/app/pages/organizations/developers/developer/developer-view.jsx b/src/app/pages/organizations/developers/developer/developer-view.jsx index df4f64431d..d4e8b50780 100755 --- a/src/app/pages/organizations/developers/developer/developer-view.jsx +++ b/src/app/pages/organizations/developers/developer/developer-view.jsx @@ -18,7 +18,7 @@ import { theme, utilStyles } from 'themes'; const useStyles = makeStyles({ ...utilStyles, - editUser:{ + focus: { display: 'flex', flexDirection: 'column-reverse', paddingTop: '16px', @@ -121,7 +121,7 @@ function ChplDeveloperView({ dispatch }) { }; return ( - + { state === 'view' && ( @@ -154,9 +154,11 @@ function ChplDeveloperView({ dispatch }) { /> )} - )} + {state === 'view' && ( + + )} {(state === 'view' || state === 'editUser') && ( )} + { state === 'editVersion' + && ( + + )} { state === 'join' && ( { + switch (action) { + case 'cancel': + dispatch('cancel'); + break; + case 'save': + setIsProcessing(true); + eventTrack({ + ...analytics, + event: 'Save Version', + }); + setErrorMessages([]); + mutate({ + version: payload, + versionIds: [payload.id], + }, { + onSuccess: () => { + setIsProcessing(false); + dispatch('cancel'); + }, + onError: (error) => { + setIsProcessing(false); + const body = error?.response?.data?.error; + if (body) { + enqueueSnackbar(body, { + variant: 'error', + }); + } + }, + }); + break; + // no default + } + }; + + if (!version) { return ; } + + return ( + + + + + + ); +} + +export default ChplEditVersion; + +ChplEditVersion.propTypes = { + dispatch: func.isRequired, + version: object.isRequired, +}; diff --git a/src/app/pages/organizations/organizations.state.js b/src/app/pages/organizations/organizations.state.js index a9ddcadfec..246825f3a5 100644 --- a/src/app/pages/organizations/organizations.state.js +++ b/src/app/pages/organizations/organizations.state.js @@ -92,16 +92,6 @@ const states = [ name: 'organizations.developers.developer.product.version', url: '/versions/{versionId}', abstract: true, - }, { - name: 'organizations.developers.developer.product.version.edit', - url: '/edit', - views: { - 'view@^.^.^': 'chplVersionsEdit', - }, - data: { - title: 'CHPL Developers - Edit Version', - roles: ['chpl-admin', 'chpl-onc', 'chpl-onc-acb'], - }, }, { name: 'organizations.developers.developer.product.version.merge', url: '/merge', diff --git a/src/app/pages/reports/activity/activity-view.jsx b/src/app/pages/reports/activity/activity-view.jsx index 63a40433a4..e17d77919d 100755 --- a/src/app/pages/reports/activity/activity-view.jsx +++ b/src/app/pages/reports/activity/activity-view.jsx @@ -148,6 +148,7 @@ function ChplActivityView() { if (![ 'CERTIFIED_PRODUCT', 'DEVELOPER', + 'VERSION', ].includes(activity.concept)) { return null; } @@ -183,6 +184,22 @@ function ChplActivityView() { router={{ sref: 'organizations.developers.developer', options: { id: activity.objectId } }} /> ); + case 'VERSION': + if (before && after && before.id !== after.id) { + return null; + } + return ( + + ); default: return null; }