diff --git a/src/app/scripts/common/filters/time-filter.js b/src/app/scripts/common/filters/time-filter.js new file mode 100644 index 0000000000000000000000000000000000000000..bc764007f9bc826107722e923c508836b3a6bdd5 --- /dev/null +++ b/src/app/scripts/common/filters/time-filter.js @@ -0,0 +1,58 @@ +/**---LICENSE-BEGIN - DO NOT CHANGE OR MOVE THIS HEADER + * This file is part of the Neurorobotics Platform software + * Copyright (C) 2014,2015,2016,2017 Human Brain Project + * https://www.humanbrainproject.eu + * + * The Human Brain Project is a European Commission funded project + * in the frame of the Horizon2020 FET Flagship plan. + * http://ec.europa.eu/programmes/horizon2020/en/h2020-section/fet-flagships + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * ---LICENSE-END**/ + + // This file contains filters to display times and dates + +export default function timeDDHHMMSS(input) { + + if (typeof input !== 'number') { + return '--\u00A0--:--:--'; + } + var timeValue = ''; + var timeSec = input; + var timeDay = Math.floor(timeSec / 86400); + timeSec -= timeDay * 86400; + var timeHour = Math.floor(timeSec / 3600); + timeSec -= timeHour * 3600; + var timeMin = Math.floor(timeSec / 60); + timeSec = Math.floor(timeSec) - timeMin * 60; + if (timeDay < 10) { + timeValue += '0'; + } + timeValue += timeDay.toFixed(0) + '\u00A0'; + if (timeHour < 10) { + timeValue += '0'; + } + timeValue += timeHour.toFixed(0) + ':'; + if (timeMin < 10) { + timeValue += '0'; + } + timeValue += timeMin.toFixed(0) + ':'; + if (timeSec < 10) { + timeValue += '0'; + } + timeValue += timeSec.toFixed(0); + + return timeValue; +} \ No newline at end of file diff --git a/src/components/experiment-list/experiment-list-element.css b/src/components/experiment-list/experiment-list-element.css new file mode 100644 index 0000000000000000000000000000000000000000..7d10ae35056947698550be1e42e51ed1439ccf01 --- /dev/null +++ b/src/components/experiment-list/experiment-list-element.css @@ -0,0 +1,99 @@ +.list-entry-container { + display: flex; +} + +.list-entry-container.hover { + background: linear-gradient(to right, #ffffff 0%, #eeeeee 50%, #ffffff 100%); +} + +.list-entry-container.selected { + background: linear-gradient( + to right, + rgba(255, 255, 255, 0.8) 0%, + rgba(245, 245, 245, 0.8) 100% + ); +} + +.list-entry-container.hover-wizard { + background: linear-gradient( + to right, + rgba(255, 255, 255, 0.8) 0%, + rgba(230, 239, 255, 0.8) 100% + ); +} + +.list-entry-container.selected-wizard { + background: linear-gradient( + to right, + rgba(255, 255, 255, 0.8) 0%, + rgba(230, 239, 255, 0.8) 100% + ); +} + +.list-entry-container.left-right { + flex-direction: row; +} + +.list-entry-container.up-down { + flex-direction: column; +} + +.list-entry-container.title-line { + align-items: baseline; +} + +.list-entry-container.center { + align-items: center; + justify-content: center; +} + +.list-entry-main { + flex: 1; +} + +.list-entry-left { + flex-shrink: 0; + flex-grow: 0; + width: 229px; + padding-right: 15px; + padding-left: 15px; + transition: width ease-in-out 0.5s; +} + +.list-entry-left.selected { + width: 359px; +} + +.list-entry-left > img { + width: 100%; + object-fit: contain; + object-position: top; + height: 100%; +} +.list-entry-middle { + flex: 1; +} + +.list-entry-running { + font-size: 12px; + font-style: italic; + color: #555555; + padding-left: 10px; +} + +.list-entry-buttons { + padding-top: 10px; + padding-bottom: 10px; +} + +.server-icon { + height: 20px; + width: 20px; + -moz-border-radius: 15px; + -webkit-border-radius: 15px; + border-radius: 15px; + padding: 2px; + margin-left: 10px; + margin-top: 5px; + margin-bottom: 5px; +} \ No newline at end of file diff --git a/src/components/experiment-list/experiment-list-element.js b/src/components/experiment-list/experiment-list-element.js index 23cf8dadc40aa26cc8238fd5db715cb74a8d148a..321ded77ab37fa15b1a97cd1ec71ad1393dba45b 100644 --- a/src/components/experiment-list/experiment-list-element.js +++ b/src/components/experiment-list/experiment-list-element.js @@ -1,10 +1,121 @@ import React from "react"; +import timeDDHHMMSS from "../../app/scripts/common/filters/time-filter.js"; + +import "./experiment-list-element.css"; export default class ExperimentListElement extends React.Component { render() { + const exp = this.props.experiment; + const config = this.props.experiment.configuration; + const pageState =this.props.pageState; return ( - <div className="experiment-box"> - {this.props.experiment && this.props.experiment.id} + <div className="list-entry-container left-right" style={{position:"relative"}}> + <div className="list-entry-left" style={{position:"relative"}}> + <img class="entity-thumbnail" src={exp.configuration.thumbnail} alt='' /> + </div> + <div className="list-entry-middle list-entry-container up-down"> + <div className="list-entry-container left-right title-line"> + <div className="h4"> + {exp.configuration.name} + </div> + <br /> + </div> + <div> + {exp.configuration.description} + <br/> + </div> + <div style={{position:"relative"}}> + <i> + Timeout: + {timeDDHHMMSS(exp.configuration.timeout)} + ({(exp.configuration.timeoutType==='simulation' ? 'simulation' : 'real')} time) + </i> + <br /> + <i> + Brain processes: {exp.configuration.brainProcesses} + </i> + <br /> + <div style={{display:"flex"}}> + <i style={{marginTop: "4px"}}>Server status: </i> + <i className={{serverIcon: 1}} title="Restricted."></i> + </div> + </div> + <div class="list-entry-buttons list-entry-container center" onClick={()=>{return exp.id === pageState.selected}}> + <div class="btn-group" role="group" > + {config.canLaunchExperiments && exp.availableServers.length > 0 && + exp.configuration.experimentFile && exp.configuration.bibiConfSr + + ? <button analytics-on analytics-event="Launch" analytics-category="Experiment" + onClick={()=>{return pageState.startingExperiment === exp.id }} + value = {exp.configuration.experimentFile && exp.configuration.bibiConfSrc} + disabled = {pageState.startingExperiment === exp.id || pageState.deletingExperiment} + class="btn btn-default" > + <i class="fa fa-plus"></i> Launch + </button> + + :<button canLaunchExp={config.canLaunchExperiments} serverLength={exp.joinableServers.length} class="btn btn-default disabled enable-tooltip" + title="Sorry, no available servers."> + <i class="fa fa-plus"></i> Launch + </button> + } + {/* Option to Launch in Single Process Mode */} + <button canLaunchExp={config.canLaunchExperiments} brainProcesse={exp.configuration.brainProcesses} serverLength={exp.joinableServers.length} + expFile = {exp.configuration.experimentFile} expBibi={exp.configuration.bibiConfSrc} expId={exp.id} + class="btn btn-default"> + <i class="fa fa-plus"></i> Launch in Single Process Mode + </button> + + <button analytics-on analytics-event="Launch Multiple Instances" analytics-category="Experiment" + canLaunchExp={config.canLaunchExperiments} serverLength={exp.joinableServers.length} + expFile = {exp.configuration.experimentFile} expBibi={exp.configuration.bibiConfSrc} expId={exp.id} + class="btn btn-default" > + <i class="fa fa-layer-group"></i> Launch Multiple + </button> + + <button analytics-on analytics-event="Delete" analytics-category="Experiment" + canLaunchExp={config.canLaunchExperiments} jServerLength={exp.joinableServers.length} + class="btn btn-default"> + <i class="fa fa-times"></i> Delete + </button> + + {/* Records button */} + <button analytics-on analytics-event="ShowRecords" analytics-category="Experiment" canLaunchExp={config.canLaunchExperiments} + class="btn btn-default"> + <i class="fa fa-sign-in"></i> Recordings » + </button> + + {/* Export button */} + <button analytics-on analytics-event="ExportZip" analytics-category="Experiment" canLaunchExp={config.canLaunchExperiments} + class="btn btn-default"> + <i class="fa fa-file-export"></i> Export + </button> + + {/* Join button */} + <button analytics-on analytics-event="Join" analytics-category="Experiment" canLaunchExp={config.canLaunchExperiments} jServerLength={exp.joinableServers.length} + class="btn btn-default" > + <i class="fa fa-sign-in"></i> Simulations » + </button> + + {/* Clone button */} + <button canCloneExp={config.canCloneExperiments} confStorage={exp.configuration.privateStorage} expFile={exp.configuration.experimentFile} expBibi={exp.configuration.bibiConfSrc} + analytics-on analytics-event="Clone" analytics-label="Collab" + analytics-value={exp.id} class="btn btn-default"> + <i class="fa fa-pencil-alt"></i> Clone + </button> + + {/* Files button */} + <button canLaunchExp={config.canLaunchExperiments} analytics-on analytics-event="Explorer" + analytics-label="Collab" expId={exp.id} class="btn btn-default" > + + <i class="fa fa-list-alt"></i> Files + </button> + {/* Shared button */} + <button canLaunchExp={config.canLaunchExperiments} expId={exp.id} analytics-on analytics-event="Explorer" class="btn btn-default" analytics-label="Collab"> + <i class="fas fa-share-alt"></i> Share + </button> + </div> + </div> + </div> </div> ); } diff --git a/src/components/experiment-list/experiment-list.css b/src/components/experiment-list/experiment-list.css new file mode 100644 index 0000000000000000000000000000000000000000..3596c6ab01d9d6789a47c5537bf63aa85932689c --- /dev/null +++ b/src/components/experiment-list/experiment-list.css @@ -0,0 +1,3 @@ +li.nostyle { + list-style-type: none; +} \ No newline at end of file diff --git a/src/components/experiment-list/experiment-list.js b/src/components/experiment-list/experiment-list.js index 91ada29f48cee9e62fa06afc33c591966f10e1f9..b70d92beebe4ec0ecdd57a79d8d990151a2a7d56 100644 --- a/src/components/experiment-list/experiment-list.js +++ b/src/components/experiment-list/experiment-list.js @@ -3,11 +3,14 @@ import experimentsService from "../../services/proxy/experiments.js"; import ExperimentListElement from "./experiment-list-element.js"; +import "./experiment-list.css"; + export default class ExperimentList extends React.Component { constructor(props) { super(props); this.state = { experiments: [], + pageState: {}, }; } @@ -23,30 +26,19 @@ export default class ExperimentList extends React.Component { } } - render() { - /*if (this.state.experiments) { - return this.state.experiments.then((file) => { - file.forEach((experiment) => { - this.renderExperiment(experiment); - }); - }); - } else { - return null; - }*/ - return <ExperimentListElement experiment={this.state.experiments[0]} />; - } -} - -/*export default class ExperimentContainer extends React.Component { render() { return ( - <div className="experiment-container"> - <div className="experiment-list"> - <ol> - <ExperimentList /> - </ol> - </div> + <div className="experiment-list"> + <ol> + {this.state.experiments.map(experiment => + {return ( + <li key={experiment.id} class="nostyle"> + <ExperimentListElement experiment={experiment} pageState={this.state.pageState} /> + </li> + );} + )} + </ol> </div> ); } -}*/ +}