diff --git a/src/assets/Interactive.css b/src/assets/Interactive.css new file mode 100644 index 0000000000000000000000000000000000000000..7875dc5ef9ec355a9ec0311eb1c592742e7d7632 --- /dev/null +++ b/src/assets/Interactive.css @@ -0,0 +1,162 @@ +.ida h2 { + color:gray; +} +.ida span { + overflow-wrap: break-word; +} + +.ida ul { + counter-reset: li; + list-style: none; + padding: 0; + text-shadow: 0 1px 0 rgba(255,255,255,.5); +} + +.workflow-li, .facility-li { + margin-bottom:20px; + width:40%; + border: 1px solid #ccc!important; + border-radius: 4px; + padding:15px; +} + +.ida { + margin:50px; +} + +.ida .input-contain{ + position: relative; +} + +.ida input[type=text] { + height: 5rem; + width: 40rem; + border: 2px solid black; + border-radius: 1rem; + margin-bottom:30px; +} + +.ida input[type=text]:focus{ + outline: none; + border-color: blue; +} +.ida input[type=text]:focus + .placeholder-text .text, :not(input[value=""]) + .placeholder-text .text{ + background-color: white; + font-size: 1.1rem; + color: black; + transform: translate(0, -170%); +} +.ida input[type=text]:focus + .placeholder-text .text{ + border-color: blueviolet; + color: blue; +} +.ida .placeholder-text{ + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + border: 3px solid transparent; + background-color: transparent; + pointer-events: none; + display: flex; + align-items: center; +} +.ida .text{ + font-size: 1.4rem; + padding: 0 0.5rem; + background-color: transparent; + transform: translate(0); + color: black; + transition: transform 0.15s ease-out, font-size 0.15s ease-out, background-color 0.2s ease-out, color 0.15s ease-out; +} + +input[type=text], .placeholder-text{ + font-size: 1.4rem; + padding: 0 1.2rem; + +} + +.ida @media (max-width: 40rem) { + input{ + width: 70vw; + } +} + +.workflow-checkbox { + margin-right:10px; +} + + +/* Customize the label (the container) */ +.ida .container { + display: block; + position: relative; + padding-left: 35px; + margin-bottom: 12px; + cursor: pointer; + font-size: 22px; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Hide the browser's default checkbox */ +.container input { + position: absolute; + opacity: 0; + cursor: pointer; + height: 0; + width: 0; +} + +/* Create a custom checkbox */ +.checkmark { + float:right; + height: 25px; + width: 25px; + background-color: #eee; +} + +/* On mouse-over, add a grey background color */ +.container:hover input ~ .checkmark { + background-color: #ccc; +} + +/* When the checkbox is checked, add a blue background */ +.container input:checked ~ .checkmark { + background-color: #2196F3; +} + +/* Create the checkmark/indicator (hidden when not checked) */ +.checkmark:after { + content: ""; + position: absolute; + display: none; +} + +/* Show the checkmark when checked */ +.container input:checked ~ .checkmark:after { + display: block; +} + +/* Style the checkmark/indicator */ +.container .checkmark:after { + margin-left:8px; + margin-top:2px; + width: 8px; + height: 15px; + border: solid white; + border-width: 0 3px 3px 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); +} + +.next-button { + margin-left:350px; + width:180px; + height:40px; +} + diff --git a/src/components/services/Interactive.js b/src/components/services/Interactive.js index 8f4517485c48f8e81b140743fcb24506bb57672f..37cdf09bf26a2ba919a189b1ab52b63c0e6aa02a 100644 --- a/src/components/services/Interactive.js +++ b/src/components/services/Interactive.js @@ -2,49 +2,153 @@ import React, { useContext } from "react"; import { Button, Form, Container, Alert } from "react-bootstrap"; import { IDAContext } from "../../contexts/IDAContext"; import { GlobalContext } from "../../contexts/GlobalContext"; +import "../../assets/Interactive.css"; export default function Interactive() { - const { idaSystemURL, setIdaSystemURL, jnotebookURL, setJnotebookURL, batchsystemsURL, setBatchsystemsURL, list_of_jnotebooks, setList_of_jnotebooks, list_of_idaSystems, setList_of_idaSystems} = useContext(IDAContext); + const { idaSystemURL, setIdaSystemURL, workflowURL, setWorkflowURL, batchsystemsURL, setBatchsystemsURL, list_of_workflows, setList_of_workflows, list_of_idaSystems, setList_of_idaSystems} = useContext(IDAContext); const { api_host } = useContext(GlobalContext); + const [searchTerm, setSearchTerm] = React.useState(""); + const [showFacilities, setShowFacilities] = React.useState(false); + const [showNext, setShowNext] = React.useState(false); + const [showDeploy, setShowDeploy] = React.useState(false); let list_of_batchsystems = [ {"name" : "DIRAC EGI (LOFAR, KM3Net)", "url" : "https://dirac.egi.eu"}, {"name" : "CTA DIRAC", "url" : "https://ccdcta-web.in2p3.fr/DIRAC/"}, ] - if ((!list_of_jnotebooks) || (!list_of_idaSystems) || (!list_of_batchsystems)) { + if ((!list_of_workflows) || (!list_of_idaSystems) || (!list_of_batchsystems)) { return null } + const handleChange = event => { + setSearchTerm(event.target.value); + }; + + const workflow_results = !searchTerm + ? list_of_workflows + : list_of_workflows.filter(workflow => + (typeof workflow.name === 'string') && workflow.name.toLowerCase().includes(searchTerm.toLocaleLowerCase()) || + (typeof workflow.keywords === 'string') && workflow.keywords.toLowerCase().includes(searchTerm.toLocaleLowerCase()) || + (typeof workflow.author === 'string') && workflow.author.toLowerCase().includes(searchTerm.toLocaleLowerCase()) || + (typeof workflow.runtimePlatform === 'string') && workflow.runtimePlatform.toLowerCase().includes(searchTerm.toLocaleLowerCase()) || + (typeof workflow.description === 'string') && workflow.description.toLowerCase().includes(searchTerm.toLocaleLowerCase()) + ); + + const facility_results = !searchTerm + ? list_of_idaSystems + : list_of_idaSystems.filter(facility => + facility.name.toLowerCase().includes(searchTerm.toLocaleLowerCase()) + ); + + + const setWorkflow = event => { + setWorkflowURL(event.target.value); + setShowNext(true) + }; + + const setFacility = event => { + setIdaSystemURL(event.target.value); + setShowDeploy(true) + }; + + const onClickNext = e => { + e.preventDefault(); + setSearchTerm("") + setShowFacilities(true); + }; + + + + return ( - <Container fluid> + <Container className="ida" fluid> + + <h1>Interactive Analysis</h1> + + { !showFacilities ? + + <div class="workflow-div"> + <h2>Workflows</h2> + <Form className="mt-5"> - <Form.Group controlId="jnotebook" onChange={ - (event) => setJnotebookURL(list_of_jnotebooks.find((item) => item.name === event.target.value).url) - }> - <Form.Label> - <h3>Select an analysis workflow (Jupyter Notebook)</h3> - </Form.Label> - <Form.Control className="mt-1" as="select"> - {list_of_jnotebooks.map((option) => <option>{option.name}</option>)} - </Form.Control> - </Form.Group> + + <input + type="text" + placeholder="Search for Workflows" + value={searchTerm} + onChange={handleChange} + /> + + + { showNext ? + <Button className="next-button" onClick={onClickNext}>Next</Button> + : null } + + <ul class="workflow-ul"> + {workflow_results.map(item => ( + <li class="workflow-li"> + <label class="container workflow-checkbox"><input type="radio" name="workflow" onChange={setWorkflow} value={item.url} /> <span class="checkmark"></span></label><h5>{item.name}</h5><br/> + <span><b>Description: </b> <span dangerouslySetInnerHTML={{ __html: item.description }}></span></span><br/> + <span><b>Link: </b> <a href="{item.url}">{item.url}</a></span><br/> + <span><b>Author: </b>{item.author}</span><br/> + <span><b>Runtime Platform: </b>{item.runtimePlatform}</span><br/> + <span><b>Keywords: </b>{item.keywords}</span> + + </li> + ))} + </ul> + + </Form> + + </div> + + : null } + + + + + { showFacilities ? + + <div class="facility-div"> + <h2>Compute Facilities</h2> + <Form className="mt-5"> - <Form.Group controlId="jhub" onChange={ - (event) => setIdaSystemURL(list_of_idaSystems.find((item) => item.name === event.target.value).url) - }> - <Form.Label> - <h3>Select an analysis service</h3> - </Form.Label> - <Form.Control className="mt-1" as="select"> - {list_of_idaSystems.map((option) => <option>{option.name}</option>)} - </Form.Control> - </Form.Group> + + <input + type="text" + placeholder="Search for Facilities" + value={searchTerm} + onChange={handleChange} + /> + + { showDeploy ? + <Button className="next-button" href={api_host + "ida/deploy?facility=" + idaSystemURL + "&workflow=" + workflowURL} target="_blank">Deploy</Button> + : null } + + + <ul class="facility-ul"> + {facility_results.map(item => ( + <li class="facility-li"> + <label class="container facility-checkbox"><input class="radio" onChange={setFacility} name="facility" type="radio" value={item.url} /> <span class="checkmark"></span></label><h5>{item.name}</h5><br/> + <span><b>Description:</b><br/> {item.description}</span> <br/> + <span><b>Link:</b> <a href="{item.url}">{item.url}</a></span> + + </li> + ))} + </ul> + + + </Form> - <Button href={api_host + "ida/deploy?facility=" + idaSystemURL + "&workflow=" + jnotebookURL} target="_blank">Deploy</Button> + </div> + + : null } + + </Container> ); diff --git a/src/contexts/GlobalContext.js b/src/contexts/GlobalContext.js index 174f751c0da15c61cdf1d9d76c15f767e4500ed1..fd6feb7529a0ee42b36fc464ba82512ffbb2e6b6 100644 --- a/src/contexts/GlobalContext.js +++ b/src/contexts/GlobalContext.js @@ -41,13 +41,12 @@ function setProfileState(api_host, } export function GlobalContextProvider({ children }) { - + const api_host = process.env.NODE_ENV === "development" ? "http://localhost:5555/esap-api/" : "https://sdc-dev.astron.nl/esap-api/"; - const [archives, setArchives] = useState(); const [navbar, setNavbar] = useState(); const [loggedInUserName, setLoggedInUserName] = useState(); diff --git a/src/contexts/IDAContext.js b/src/contexts/IDAContext.js index 2e5174ce2d8645666037441be752be7b8042b320..95033904712008e6bd7ac51ce7557ed212cfe392 100644 --- a/src/contexts/IDAContext.js +++ b/src/contexts/IDAContext.js @@ -8,9 +8,9 @@ export function IDAContextProvider({ children }) { const { api_host } = useContext(GlobalContext); const [idaSystemURL, setIdaSystemURL] = useState(); - const [jnotebookURL, setJnotebookURL] = useState(); + const [workflowURL, setWorkflowURL] = useState(); const [batchsystemsURL, setBatchsystemsURL] = useState(); - const [list_of_jnotebooks, setList_of_jnotebooks] = useState(); + const [list_of_workflows, setList_of_workflows] = useState(); const [list_of_idaSystems, setList_of_idaSystems] = useState(); // Fetch Notebooks @@ -18,8 +18,8 @@ export function IDAContextProvider({ children }) { axios .get(api_host + "ida/workflows/search") .then((response) => { - setList_of_jnotebooks(response.data.results); - setJnotebookURL(response.data.results[0].url); + setList_of_workflows(response.data.results); + setWorkflowURL(response.data.results[0].url); }); }, [api_host]); @@ -40,12 +40,12 @@ export function IDAContextProvider({ children }) { value={{ idaSystemURL, setIdaSystemURL, - jnotebookURL, - setJnotebookURL, + workflowURL, + setWorkflowURL, batchsystemsURL, setBatchsystemsURL, - list_of_jnotebooks, - setList_of_jnotebooks, + list_of_workflows, + setList_of_workflows, list_of_idaSystems, setList_of_idaSystems }} diff --git a/src/routes/Routes.js b/src/routes/Routes.js index b04a44e50a1ac80e9b4e68ccc6061b2692c5881f..1c2a8643610b80cf7057213cc30a330729d1035a 100644 --- a/src/routes/Routes.js +++ b/src/routes/Routes.js @@ -87,7 +87,7 @@ export default function Routes() { </Switch> - <footer><small>esap-gui version 31 aug 2021</small></footer> + <footer><small>esap-gui version 5 oct 2021</small></footer> </Router> ); }