From 92abb5e9e11328c813be0245f5b80f4cd70e227f Mon Sep 17 00:00:00 2001 From: Imre Toth <imre.toth@ess.eu> Date: Thu, 11 Nov 2021 09:13:23 +0000 Subject: [PATCH] Icshwi 8184 ioc admin --- src/api/SwaggerApi.js | 21 ++++ src/components/IOC/IOCAdmin.js | 13 ++ src/components/IOC/IOCDelete.js | 70 +++++++++++ src/components/IOC/IOCDetailAdmin.js | 115 ++++++++++++++++++ src/components/IOC/IOCService.js | 4 +- .../ConfirmationDialog.js} | 2 +- src/views/IOC/IOCDetailsView.js | 46 +++++-- 7 files changed, 258 insertions(+), 13 deletions(-) create mode 100644 src/components/IOC/IOCAdmin.js create mode 100644 src/components/IOC/IOCDelete.js create mode 100644 src/components/IOC/IOCDetailAdmin.js rename src/components/{IOC/ConfirmAdHocDialog.js => dialog/ConfirmationDialog.js} (95%) diff --git a/src/api/SwaggerApi.js b/src/api/SwaggerApi.js index f8498bca..56ee34bb 100644 --- a/src/api/SwaggerApi.js +++ b/src/api/SwaggerApi.js @@ -558,4 +558,25 @@ export function useAllowedGitProjects() { const api = useContext(apiContext); const method = callAndUnpack((repoUrl) => api.apis.Git.listProjects(), unpackAllowedGitProjects) return useAsync({ fcn: method, call: false, init: [] }); +} + +export function useDeleteIOC(id) { + const api = useContext(apiContext); + const method = callAndUnpack(api.apis.IOCs.deleteIoc); + return useAsync({ fcn: method.bind(null, {iocId: id}), call: false }); +} + +export function useUpdateIoc(id) { + const api = useContext(apiContext); + const method = callAndUnpack( + body => api.apis.IOCs.updateIoc({ iocId: id }, { requestBody: body }), + unpackUpdateIoc + ); + return useAsync({ fcn: method, call: false }); +} + +export function unpackUpdateIoc(updateIoc) { + const d = { ...updateIoc }; + console.log(d); + return d; } \ No newline at end of file diff --git a/src/components/IOC/IOCAdmin.js b/src/components/IOC/IOCAdmin.js new file mode 100644 index 00000000..0b33b719 --- /dev/null +++ b/src/components/IOC/IOCAdmin.js @@ -0,0 +1,13 @@ +import React from 'react'; +import IOCDelete from './IOCDelete'; +import IOCDetailAdmin from './IOCDetailAdmin'; + +export default function IOCAdmin({ ioc, getIOC, resetTab }) { + + return ( + <> + <IOCDetailAdmin ioc={ioc} getIOC={getIOC} resetTab={resetTab}/> + <IOCDelete ioc={ioc} /> + </> + ); +} \ No newline at end of file diff --git a/src/components/IOC/IOCDelete.js b/src/components/IOC/IOCDelete.js new file mode 100644 index 00000000..614319ed --- /dev/null +++ b/src/components/IOC/IOCDelete.js @@ -0,0 +1,70 @@ +import React, { useState, useRef } from 'react'; +import { useHistory } from "react-router-dom"; +import { makeStyles } from '@material-ui/core/styles'; +import { Button, Typography } from "@material-ui/core"; +import { theme } from "../../Theme"; +import { useDeleteIOC } from '../../api/SwaggerApi'; +import { SimpleAccordion } from "../common/Accordion/SimpleAccordion"; +import { ConfirmationDialog } from "../dialog/ConfirmationDialog"; +import { SimpleModal } from "../../components/common/SimpleModal/SimpleModal"; + +const useStyles = makeStyles({ + deleteButton: { + backgroundColor: '#aa2e25', + color: '#FFFFFF', + "&:disabled": { + backgroundColor: '#8d8d8d', + color: '#FFFFFF', + }, + "&:hover": { + backgroundColor: '#6c0000', + color: '#FFFFFF', + } + }, +}); + +export default function IOCDelete({ ioc }) { + const classes = useStyles(theme); + + const history = useHistory(); + + //for the dialog + const [adHocDialogOpen, setAdHocDialogOpen] = useState(false); + const [adHocDialogTitle, setAdHocDiatlogTitle] = useState(); + const [adHocDialogDescription, setAdHocDialogDescription] = useState(); + const callbackRef = useRef(); + + const [, deleteIOC] = useDeleteIOC(ioc.id); + + //Setting up dialog properties + const openDeleteModal = () => { + setAdHocDiatlogTitle("Deleting IOC"); + setAdHocDialogDescription( + <> + <Typography style={{ display: 'inline-block' }}>Are you sure want to delete </Typography> + <Typography style={{ fontFamily: "monospace", display: 'inline-block' }}> {ioc.namingName}? </Typography> + </>); + callbackRef.current = deleteIoc; + setAdHocDialogOpen(true); + } + + //what to do when deleting IOC + const deleteIoc = async () => { + + await deleteIOC(); + history.goBack(); + } + + + return ( + <> + <SimpleModal open={adHocDialogOpen} setOpen={setAdHocDialogOpen}> + <ConfirmationDialog open={adHocDialogOpen} setOpen={setAdHocDialogOpen} title={adHocDialogTitle} description={adHocDialogDescription} callback={callbackRef.current} /> + </SimpleModal> + + <SimpleAccordion summary="Delete IOC" defaultExpanded> + <Button className={classes.deleteButton} onClick={openDeleteModal}>Delete IOC</Button> + </SimpleAccordion> + </> + ); +} \ No newline at end of file diff --git a/src/components/IOC/IOCDetailAdmin.js b/src/components/IOC/IOCDetailAdmin.js new file mode 100644 index 00000000..34ed4801 --- /dev/null +++ b/src/components/IOC/IOCDetailAdmin.js @@ -0,0 +1,115 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { SimpleAccordion } from "../common/Accordion/SimpleAccordion"; +import { Button, makeStyles, TextField, Typography } from "@material-ui/core"; +import { Autocomplete } from "@material-ui/lab"; +import { useAllowedGitProjects, useNamingNames, useUpdateIoc } from "../../api/SwaggerApi"; +import { useTypingTimer } from "../common/SearchBoxFilter/TypingTimer"; +import { ConfirmationDialog } from "../dialog/ConfirmationDialog"; +import { SimpleModal } from "../../components/common/SimpleModal/SimpleModal"; + +const useStyles = makeStyles((theme) => ({ + textField: { + marginBottom: theme.spacing(1), + }, +})); + +export default function IOCDetailAdmin({ ioc, getIOC, resetTab }) { + const classes = useStyles(); + + const [gitUrl, setGitUrl] = useState(ioc.git); + const [allowedGitProjects, getAllowedGitProjects] = useAllowedGitProjects(); + const [nameQuery, onNameKeyUp] = useTypingTimer({ interval: 500 }); + const [names, getNames] = useNamingNames(); + const [name, setName] = useState({ id: ioc.latestVersion.externalNameId, description: ioc.description, name: ioc.latestVersion.namingName }); + const [owner, setOwner] = useState(ioc.owner); + + //for the dialog + const [adHocDialogOpen, setAdHocDialogOpen] = useState(false); + const [adHocDialogTitle, setAdHocDiatlogTitle] = useState(); + const [adHocDialogDescription, setAdHocDialogDescription] = useState(); + const callbackRef = useRef(); + + const [uioc, actionUpdateIoc] = useUpdateIoc(ioc.id); + + useEffect(() => { + getAllowedGitProjects(); + }, [getAllowedGitProjects]); + + useEffect(() => getNames(nameQuery), [nameQuery, getNames]); + + //when user clicks Submit button a dialog should open + const onSubmit = (event) => { + event.preventDefault(); + + openModifyModal(); + } + + //Setting up dialog properties + const openModifyModal = () => { + setAdHocDiatlogTitle("Modifying IOC"); + setAdHocDialogDescription( + <> + <Typography style={{ display: 'inline-block' }}>Are you sure want to modify </Typography> + <Typography style={{ fontFamily: "monospace", display: 'inline-block' }}> {ioc.namingName}? </Typography> + </>); + callbackRef.current = modifyIoc; + setAdHocDialogOpen(true); + } + + //what to do when modifying IOC + const modifyIoc = () => { + + actionUpdateIoc({ + owner: owner, + sourceUrl: gitUrl, + externalNameId: name ? parseInt(name.id) : null + }); + } + + useEffect(() => + { + if(uioc) { + getIOC(); + resetTab(); + } + }, [uioc, getIOC, resetTab]); + + return ( + <> + <SimpleModal open={adHocDialogOpen} setOpen={setAdHocDialogOpen}> + <ConfirmationDialog open={adHocDialogOpen} setOpen={setAdHocDialogOpen} title={adHocDialogTitle} description={adHocDialogDescription} callback={callbackRef.current} /> + </SimpleModal> + + <SimpleAccordion summary="Modify IOC" defaultExpanded> + <form onSubmit={onSubmit} style={{ width: "100%" }}> + <Autocomplete + className={classes.textField} + autoHighlight + id="namingName" + options={names} + defaultValue={name} + getOptionLabel={(option) => { console.log(option?.name); return option?.name }} + renderInput={(params) => <TextField {...params} label="IOC name" variant="outlined" required />} + onChange={(event, value, reason) => setName(value)} + onInputChange={(event, value, reason) => { event && onNameKeyUp(event.nativeEvent) }} + /> + + <Autocomplete + className={classes.textField} + autoHighlight + id="gitUrl" + options={allowedGitProjects} + defaultValue={gitUrl} + getOptionLabel={(option) => { return option }} + onChange={(event, value, reason) => setGitUrl(value)} + renderInput={(params) => <TextField {...params} label="Git repository" variant="outlined" fullWidth required />} /> + + <TextField id="owner" label="Owner" variant="outlined" fullWidth required value={owner} onChange={(event) => setOwner(event.target.value)} /> + + <br /><br /> + <Button color="primary" variant="contained" type="submit">Modify IOC</Button> + </form> + </SimpleAccordion> + </> + ) +} \ No newline at end of file diff --git a/src/components/IOC/IOCService.js b/src/components/IOC/IOCService.js index 2e14c67f..f523e4dc 100644 --- a/src/components/IOC/IOCService.js +++ b/src/components/IOC/IOCService.js @@ -4,7 +4,7 @@ import React, { useState, useEffect, useRef, useContext } from "react"; import { useStartIOC, useStopIOC } from "../../api/SwaggerApi"; import { CommandJobStatus } from "./CommandJobStatus"; import { SimpleModal } from "../../components/common/SimpleModal/SimpleModal"; -import { ConfirmAdHocDialog } from "./ConfirmAdHocDialog"; +import { ConfirmationDialog } from "../dialog/ConfirmationDialog"; import { notificationContext } from "../../components/common/notification/Notifications"; import { theme } from "../../Theme"; @@ -148,7 +148,7 @@ export function IOCService({ ioc, getIOC, ongoingCommand, getCommands }) { return ( <> <SimpleModal open={adHocDialogOpen} setOpen={setAdHocDialogOpen}> - <ConfirmAdHocDialog open={adHocDialogOpen} setOpen={setAdHocDialogOpen} title={adHocDialogTitle} description={adHocDialogDescription} callback={callbackRef.current} /> + <ConfirmationDialog open={adHocDialogOpen} setOpen={setAdHocDialogOpen} title={adHocDialogTitle} description={adHocDialogDescription} callback={callbackRef.current} /> </SimpleModal> <Grid container spacing={1}> diff --git a/src/components/IOC/ConfirmAdHocDialog.js b/src/components/dialog/ConfirmationDialog.js similarity index 95% rename from src/components/IOC/ConfirmAdHocDialog.js rename to src/components/dialog/ConfirmationDialog.js index 4994b55b..86432129 100644 --- a/src/components/IOC/ConfirmAdHocDialog.js +++ b/src/components/dialog/ConfirmationDialog.js @@ -8,7 +8,7 @@ const useStyles = makeStyles((theme) => ({ } })); -export function ConfirmAdHocDialog({ open, setOpen, title, description, callback }) { +export function ConfirmationDialog({ open, setOpen, title, description, callback }) { const classes = useStyles(); const handleClose = () => { diff --git a/src/views/IOC/IOCDetailsView.js b/src/views/IOC/IOCDetailsView.js index 219221fb..e7b0c743 100644 --- a/src/views/IOC/IOCDetailsView.js +++ b/src/views/IOC/IOCDetailsView.js @@ -5,6 +5,8 @@ import { useIOC } from "../../api/SwaggerApi"; import { IOCLiveStatus } from "../../components/IOC/IOCLiveStatus"; import { IOCManage } from "../../components/IOC/IOCManage"; import { useHistory } from "react-router-dom"; +import IOCAdmin from "../../components/IOC/IOCAdmin"; +import AccessControl from "../../components/auth/AccessControl"; export function IOCDetailsView({ id }) { const [selectedTab, setSelectedTab] = useState("Manage deployment"); @@ -28,6 +30,14 @@ export function IOCDetailsView({ id }) { history.goBack(); }; + function resetTab() { + if (ioc && ioc.activeDeployment) { + setSelectedTab("Live Status"); + } else { + setSelectedTab("Manage deployment"); + } + } + return ( <Paper> <Grid container spacing={1}> @@ -38,20 +48,36 @@ export function IOCDetailsView({ id }) { </Grid> <Grid item xs={11}> <Grid container justify="center"> - <Tabs - value={selectedTab} - onChange={handleTabChange} - indicatorColor="primary" - textColor="primary" - > - {ioc && ioc.activeDeployment && <Tab label={<Typography variant="h5">Live Status</Typography>} value="Live Status" />} - {ioc && <Tab label={<Typography variant="h5">Manage deployment</Typography>} value="Manage deployment" />} - </Tabs> + <AccessControl allowedRoles={["DeploymentToolAdmin"]} + renderNoAccess={() => + <Tabs + value={selectedTab} + onChange={handleTabChange} + indicatorColor="primary" + textColor="primary" + > + {ioc && ioc.activeDeployment && <Tab label={<Typography variant="h5">Live Status</Typography>} value="Live Status" />} + {ioc && <Tab label={<Typography variant="h5">Manage deployment</Typography>} value="Manage deployment" />} + </Tabs> + }> + + <Tabs + value={selectedTab} + onChange={handleTabChange} + indicatorColor="primary" + textColor="primary" + > + {ioc && ioc.activeDeployment && <Tab label={<Typography variant="h5">Live Status</Typography>} value="Live Status" />} + {ioc && <Tab label={<Typography variant="h5">Manage deployment</Typography>} value="Manage deployment" />} + {ioc && <Tab label={<Typography variant="h5">IOC Admin</Typography>} value="IOC Admin" />} + </Tabs> + </AccessControl> </Grid> </Grid> - <Grid item xs={12} style={{paddingBottom: 0}}> + <Grid item xs={12} style={{ paddingBottom: 0 }}> {selectedTab === "Live Status" && <IOCLiveStatus ioc={ioc} getIOC={getIOC} />} {selectedTab === "Manage deployment" && <IOCManage ioc={ioc} getIOC={getIOC} />} + {selectedTab === "IOC Admin" && <IOCAdmin ioc={ioc} getIOC={getIOC} resetTab={resetTab}/>} </Grid> </Grid> </Paper> -- GitLab