diff --git a/src/tapis-api/systems/createChildSystem.ts b/src/tapis-api/systems/createChildSystem.ts new file mode 100644 index 000000000..0d3c2d253 --- /dev/null +++ b/src/tapis-api/systems/createChildSystem.ts @@ -0,0 +1,21 @@ +import { Systems } from '@tapis/tapis-typescript'; +import { apiGenerator, errorDecoder } from 'tapis-api/utils'; + +const createChildSystem = ( + reqPostChildSystem: Systems.ReqPostChildSystem, + parentId: string, + basePath: string, + jwt: string +) => { + const api: Systems.ChildSystemsApi = apiGenerator( + Systems, + Systems.ChildSystemsApi, + basePath, + jwt + ); + return errorDecoder(() => + api.createChildSystem({ reqPostChildSystem, parentId }) + ); +}; + +export default createChildSystem; diff --git a/src/tapis-api/systems/index.ts b/src/tapis-api/systems/index.ts index c5214e454..8cb2e63c0 100644 --- a/src/tapis-api/systems/index.ts +++ b/src/tapis-api/systems/index.ts @@ -6,3 +6,4 @@ export { default as deleteSystem } from './deleteSystem'; export { default as undeleteSystem } from './undeleteSystem'; export { default as shareSystemPublic } from './shareSystemPublic'; export { default as unShareSystemPublic } from './unShareSystemPublic'; +export { default as createChildSystem } from './createChildSystem'; diff --git a/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/CreateChildSystemModal.module.scss b/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/CreateChildSystemModal.module.scss new file mode 100644 index 000000000..04ba38e91 --- /dev/null +++ b/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/CreateChildSystemModal.module.scss @@ -0,0 +1,16 @@ +.modal-settings { + max-height: 38rem; + overflow-y: scroll; +} + +.item { + border: 1px dotted lightgray; + padding: 0.5em 0.5em 0.5em 0.5em; + margin-bottom: 1em; +} + +.array { + border: 1px solid gray; + padding: 0.5em 0.5em 0.5em 0.5em; + margin-bottom: 0.5em; +} diff --git a/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/CreateChildSystemModal.tsx b/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/CreateChildSystemModal.tsx new file mode 100644 index 000000000..3e25b2db3 --- /dev/null +++ b/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/CreateChildSystemModal.tsx @@ -0,0 +1,130 @@ +import { Button } from 'reactstrap'; +import { GenericModal } from 'tapis-ui/_common'; +import { SubmitWrapper } from 'tapis-ui/_wrappers'; +import { ToolbarModalProps } from '../SystemLayoutToolbar'; +import { Form, Formik } from 'formik'; +import { FormikInput } from 'tapis-ui/_common'; +import { useTapisConfig } from 'tapis-hooks'; +import { useCreateChildSystem } from 'tapis-hooks/systems'; +import { useEffect } from 'react'; +import { useLocation } from 'react-router-dom'; +import styles from './CreateChildSystemModal.module.scss'; +import * as Yup from 'yup'; + +const CreateChildSystemModal: React.FC = ({ toggle }) => { + const { isLoading, isSuccess, error, reset, createChildSystem } = + useCreateChildSystem(); + + useEffect(() => { + reset(); + }, [reset]); + + const validationSchema = Yup.object({ + sysname: Yup.string() + .min(1) + .max(80, 'System name should not be longer than 80 characters') + .matches( + /^[a-zA-Z0-9_.-]+$/, + "Must contain only alphanumeric characters and the following: '.', '_', '-'" + ) + .required('System name is a required field'), + rootDir: Yup.string() + .min(1) + .max(4096, 'Root Directory should not be longer than 4096 characters'), + effectiveUserId: Yup.string() + .max(60, 'Effective User ID should not be longer than 60 characters') + .required('Effective User ID is a required field'), + parentId: Yup.string().required('Parent ID is a required field'), + }); + + const initialValues = { + sysname: '', + rootDir: '/', + effectiveUserId: useTapisConfig().claims['tapis/username'], + parentId: String(useLocation().pathname.split('/').pop()), + }; + + const onSubmit = ({ + sysname, + rootDir, + effectiveUserId, + parentId, + }: { + sysname: string; + rootDir: string; + effectiveUserId: string; + parentId: string; + }) => { + createChildSystem( + { + id: sysname, + effectiveUserId, + rootDir, + }, + parentId + ); + }; + + return ( + + + {() => ( +
+ + + + + )} +
+ + } + footer={ + + + + } + /> + ); +}; + +export default CreateChildSystemModal; diff --git a/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/index.ts b/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/index.ts new file mode 100644 index 000000000..ae8170912 --- /dev/null +++ b/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/CreateChildSystemModal/index.ts @@ -0,0 +1,3 @@ +import CreateChildSystemModal from './CreateChildSystemModal'; + +export default CreateChildSystemModal; diff --git a/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/SystemLayoutToolbar.tsx b/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/SystemLayoutToolbar.tsx index 9acece787..d5fed5bae 100644 --- a/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/SystemLayoutToolbar.tsx +++ b/src/tapis-app/Systems/SystemDetail/_Layout/SystemLayoutToolbar/SystemLayoutToolbar.tsx @@ -5,6 +5,7 @@ import styles from './SystemLayoutToolbar.module.scss'; import { useLocation } from 'react-router-dom'; import ShareSystemPublicModal from './ShareSystemPublicModal'; import UnShareSystemPublicModal from './UnShareSystemPublicModal'; +import CreateChildSystemModal from './CreateChildSystemModal'; type ToolbarButtonProps = { text: string; @@ -64,13 +65,22 @@ const SystemLayoutToolbar: React.FC = () => { onClick={() => setModal('unsharesystempublic')} aria-label="unShareSystemPublic" /> - + setModal('ConfirmModal-CreateChildSystem')} + aria-label="unShareSystemPublic" + /> {modal === 'sharesystempublic' && ( )} {modal === 'unsharesystempublic' && ( )} + {modal === 'ConfirmModal-CreateChildSystem' && ( + + )} )} diff --git a/src/tapis-hooks/systems/index.ts b/src/tapis-hooks/systems/index.ts index fd711ab0c..a223a77de 100644 --- a/src/tapis-hooks/systems/index.ts +++ b/src/tapis-hooks/systems/index.ts @@ -7,3 +7,4 @@ export { default as useDeleteSystem } from './useDeleteSystem'; export { default as useUndeleteSystem } from './useUndeleteSystem'; export { default as useShareSystemPublic } from './useShareSystemPublic'; export { default as useUnShareSystemPublic } from './useUnShareSystemPublic'; +export { default as useCreateChildSystem } from './useCreateChildSystem'; diff --git a/src/tapis-hooks/systems/queryKeys.ts b/src/tapis-hooks/systems/queryKeys.ts index 9d1e15507..6fcbf40e5 100644 --- a/src/tapis-hooks/systems/queryKeys.ts +++ b/src/tapis-hooks/systems/queryKeys.ts @@ -6,6 +6,7 @@ const QueryKeys = { deleteSystem: 'systems/deleteSystem', shareSystemPublic: 'systems/shareSystemPublic', unShareSystemPublic: 'systems/unShareSystemPublic', + createChildSystem: 'systems/createChildSystem', }; export default QueryKeys; diff --git a/src/tapis-hooks/systems/useCreateChildSystem.ts b/src/tapis-hooks/systems/useCreateChildSystem.ts new file mode 100644 index 000000000..5a1f92721 --- /dev/null +++ b/src/tapis-hooks/systems/useCreateChildSystem.ts @@ -0,0 +1,40 @@ +import { useMutation, MutateOptions } from 'react-query'; +import { Systems } from '@tapis/tapis-typescript'; +import { createChildSystem } from 'tapis-api/systems'; +import { useTapisConfig } from '../context'; +import QueryKeys from './queryKeys'; + +type createChildSystemParams = { + reqPostChildSystem: Systems.ReqPostChildSystem; + parentId: string; +}; + +const useCreateChildSystem = () => { + const { basePath, accessToken } = useTapisConfig(); + const jwt = accessToken?.access_token || ''; + + const { mutate, isLoading, isError, isSuccess, data, error, reset } = + useMutation( + [QueryKeys.createChildSystem, basePath, jwt], + ({ reqPostChildSystem, parentId }) => + createChildSystem(reqPostChildSystem, parentId, basePath, jwt) + ); + + return { + isLoading, + isError, + isSuccess, + data, + error, + reset, + createChildSystem: ( + reqPostChildSystem: Systems.ReqPostChildSystem, + parentId: string, + options?: MutateOptions + ) => { + return mutate({ reqPostChildSystem, parentId }, options); + }, + }; +}; + +export default useCreateChildSystem; diff --git a/src/tapis-ui/_common/ConfirmModal/ConfirmModal.tsx b/src/tapis-ui/_common/ConfirmModal/ConfirmModal.tsx index a8920c644..3b5d0fc9d 100644 --- a/src/tapis-ui/_common/ConfirmModal/ConfirmModal.tsx +++ b/src/tapis-ui/_common/ConfirmModal/ConfirmModal.tsx @@ -5,6 +5,7 @@ import { SubmitWrapper } from 'tapis-ui/_wrappers'; type ConfirmModalProps = { toggle: () => void; + title?: string; message?: string; onConfirm: () => void; isLoading: boolean; @@ -15,6 +16,7 @@ type ConfirmModalProps = { const ConfirmModal: React.FC = ({ toggle, + title, message, onConfirm, isLoading, @@ -25,7 +27,7 @@ const ConfirmModal: React.FC = ({ return (