diff --git a/.idea/webServers.xml b/.idea/webServers.xml index f312376..5dd1bf4 100644 --- a/.idea/webServers.xml +++ b/.idea/webServers.xml @@ -1,18 +1,18 @@ \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml index a458a33..8f590ad 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -1,604 +1,646 @@ - - + - - - - - - + + - + - + - + - - + + - - + + - + - - + + - + - + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + - - + + tip tooltip url icon props.data. data ButtonInput TxtInput numeric here if UserCost > upd rawexport randomint console Url Icon Data UserCostSelect &gt; &lt true true true true DEFINITION_ORDER - + Amount : {value} {this.props.unit} ); } } class SelectorInput extends React.Component { constructor(props) { super(props); // this.state={listoptions:this.makelist(props.options)}; this.handleChange = this.handleChange.bind(this); } rate(i){ return i; } makelist(data){ var listoptions=[]; for (var i = 0; i < data.length; i++) { listoptions.push(); } return listoptions; } handleChange(select) { this.props.onChange(select.target.value); } makerate(){ if (this.props.rate!=null){ return( Rate : {this.props.rate} {this.props.unit} );} } maketitle(title){ const maxstr=20 if (title.length>maxstr){ title=title.substr(0,maxstr)+"..."; } return title; } render() { return (
{this.makerate()}
) } } class MakeknowmoreInput extends React.Component { constructor(props) { super(props); this.state={btnsize:20} } render() { const data = this.props.data; if (((data.Url !== '') )) { // if (data.Url.length==1){ // return( // } url={data.Url[0].Url} // id="btn-plugin-knowmore" // class="btn-primary btn-sm" tips={"Know more about " + data.Name}/> // ); // }else { return (} options={data.Url} id="btn-plugin-knowmore" class="btn-primary btn-sm" tips={"Know more about " + data.Name}/>); //} } else{ return null } } } class CheckboxInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state={checked:this.props.defaults}; } handleChange() { this.setState({checked: !this.state.checked}); this.props.onChange(!this.state.checked); } render() { return (
); } } class ButtonHrefInput extends React.Component { constructor(props) { super(props); } render() { return ( {this.props.name} ); } } class ButtonInputWpop extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.state={target:"Modal"+this.props.idp}; } handleChange() { const out={n:this.props.n,target:this.state.target}; this.props.onClick(out); } render() { return ( ); } } class ButtonInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange() { this.props.onClick(this.props.n); } render() { return ( ); } } class MenuInput extends React.Component { constructor(props) { super(props); this.state={listoptions:this.makelist(props.options)}; } makelist(data){ var listoptions=[]; for (var i = 0; i < data.length; i++) { listoptions.push({data[i].Name}); } return listoptions; } render() { return (
{this.state.listoptions}
); } } class TxtInput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange(e) { this.props.onChange(e.target.value); } render() { return (
{this.props.Prepend}
{this.props.InvalidMessage}
); } } // Outputs definition // --------------------- // --------------------- class CostOutput extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); } handleChange() { this.props.onCostChange(this.props.display); } render() { return (
); } } function Textoutput(props){ return(
{props.text}
); } // Plugins definition // --------------------- // --------------------- class AmountRatesCost extends React.Component { constructor(props) { super(props); this.handleAmountChange = this.handleAmountChange.bind(this); this.handleRateChange = this.handleRateChange.bind(this); this.state={ Amount : 1, SelectRate : 0 , Rate : this.props.data.Rates[Object.keys(this.props.data.Rates)[0]] }; this.make_export(); } handleAmountChange(amount) { this.setState({Amount: amount}); } handleRateChange(select) { this.setState({SelectRate: select}); this.setState({Rate: this.props.data.Rates[Object.keys(this.props.data.Rates)[select]]}); } make_export(){ this.export=[ {Name:"Amount",Value:this.state.Amount+" "+this.props.data.AmountUnit}, {Name:this.props.data.RateName,Value:Object.keys(this.props.data.Rates)[this.state.SelectRate]} ]; this.props.export(this.export); } componentDidUpdate(){ this.makecost(this.state.Amount,this.state.Rate); this.make_export(); } render() { return (
); } makecost(amount,rate) { if (amount<=this.props.data.AmountFree){ amount=0; } var total=amount*rate; total=tomoney(total); this.props.onCostChange(this.props.n,total); return total; } } class CategoryAmountRatesCost extends React.Component { constructor(props) { super(props); this.handleCatChange = this.handleCatChange.bind(this); this.handleAmountChange = this.handleAmountChange.bind(this); this.handleRateChange = this.handleRateChange.bind(this); this.state={ SelectCat : 0, Cat : this.props.data.Cat[Object.keys(this.props.data.Cat)[0]], Amount : 1, SelectRate : 0 , Rate : this.props.data.Rates[Object.keys(this.props.data.Rates)[0]] }; this.make_export(); } handleAmountChange(amount) { this.setState({Amount: amount}); } handleRateChange(select) { this.setState({SelectRate: select}); this.setState({Rate: this.props.data.Rates[Object.keys(this.props.data.Rates)[select]]}); } handleCatChange(select) { this.setState({SelectCat: select}); this.setState({Cat: this.props.data.Cat[Object.keys(this.props.data.Cat)[select]]}); } make_export(){ this.export=[ {Name:this.props.data.CatName,Value:Object.keys(this.props.data.Cat)[this.state.SelectCat]}, {Name:"Amount",Value:this.state.Amount+" "+this.props.data.AmountUnit}, {Name:this.props.data.RateName,Value:Object.keys(this.props.data.Rates)[this.state.SelectRate]} ]; this.props.export(this.export); } componentDidUpdate(){ this.makecost(this.state.Cat,this.state.Amount,this.state.Rate); this.make_export(); } render() { return (
); } makecost(cat,amount,rate) { if (amount<=this.props.data.AmountFree){ amount=0; } var total=cat+amount*rate; total=tomoney(total); this.props.onCostChange(this.props.n,total); return total; } } class CategoryCost extends React.Component { constructor(props) { super(props); this.handleCatChange = this.handleCatChange.bind(this); this.state={SelectCat : 0, Cat : this.props.data.Cat[Object.keys(this.props.data.Cat)[0]] }; this.make_export(); } handleCatChange(select) { this.setState({SelectCat: select}); this.setState({Cat: this.props.data.Cat[Object.keys(this.props.data.Cat)[select]]}); } make_export(){ this.export=[ {Name:this.props.data.CatName,Value:Object.keys(this.props.data.Cat)[this.state.SelectCat]}, ]; this.props.export(this.export); } componentDidUpdate(){ this.makecost(this.state.Cat); this.make_export(); } render() { return (
); } makecost(cat) { var total=cat; total=tomoney(total); this.props.onCostChange(this.props.n,total); return total; } } class NoneSelect extends React.Component { constructor(props) { super(props); this.export=[] } render() { const Cost=tomoney(0); this.props.onCostChange(this.props.n,Cost); this.props.export(this.export); return (
Please select a provider in the list.
); } } class UserCost extends React.Component { constructor(props) { super(props); this.handleChange = this.handleChange.bind(this); this.handleProviderChange = this.handleProviderChange.bind(this); this.handleServiceChange = this.handleServiceChange.bind(this); this.state={total:0, CostError:false, ProviderError:true, ServiceError:true, }; this.export=[] } handleChange(value){ if (isNaN(value)||value===''){ this.setState({CostError: true}); value=0; }else{ this.setState({CostError: false}); } this.setState({total:value}); this.props.onCostChange(this.props.n,tomoney(value)); } handleProviderChange(txt){ this.props.handleProviderChange(txt); if(txt ===''){ this.setState({ProviderError: true}); } else { this.setState({ProviderError: false}); } } handleServiceChange(txt){ this.props.handleServiceChange(txt); if(txt ===''){ this.setState({ServiceError: true}); } else { this.setState({ServiceError: false}); } } classtxt(error){ if(error){ return "is-invalid"; } else { return "is-valid"; } } render() { this.props.export(this.export); return (
); } } // Combine plugins // --------------------- // --------------------- class ProviderPluginsSelector extends React.Component { constructor(props) { super(props); this.handleCostChange = this.handleCostChange.bind(this); this.handleProviderChange = this.handleProviderChange.bind(this); this.handleCommentChange = this.handleCommentChange.bind(this); this.handleAddPlugin = this.handleAddPlugin.bind(this); this.handleRmvPlugin = this.handleRmvPlugin.bind(this); this.handleProviderChangetxt = this.handleProviderChangetxt.bind(this); this.handleServiceChangetxt = this.handleServiceChangetxt.bind(this); this.make_exportcmp = this.make_exportcmp.bind(this); this.make_export = this.make_export.bind(this); this.state={ selected:0, keys:this.ProvidersName(props.data), n:1, cost:0, comments:"", Provider:"", Name:"", manualname:false, show_plus:false, exportcmp:"", }; } handleCostChange(n,e) { if (this.state.cost !== e ) { this.setState({cost: e}); this.props.handleCostChange(n,e); } } handleProviderChange(select){ this.setState({selected:select}); if (select>0){ this.setState({show_plus:true}); }else { this.setState({show_plus:false}); } this.state.Provider=this.props.data.Data[select].Provider; this.state.Name=this.props.data.Data[select].Name; this.props.handleCostChange(this.props.n,this.state.cost); } componentDidUpdate(){ this.props.handleCostChange(this.props.n,this.state.cost); this.make_export(); } make_exportcmp(data){ this.state.exportcmp=data } make_export(){ const out={ Category:this.props.data.Name, Provider:this.state.Provider, Name:this.state.Name, Comments:this.state.comments, ExportCmp:this.state.exportcmp, Cost:this.state.cost, }; this.props.export(out,this.props.n) } handleCommentChange(com){ this.setState({comments:com}); } handleAddPlugin(n){ this.props.handleAddPlugin(n); } handleRmvPlugin(n){ this.props.handleRmvPlugin(n); } handleProviderChangetxt(txt){ this.setState({Provider:txt}); // this.props.handleCostChange(this.props.n,this.state.cost);//provoke export update on the parent } handleServiceChangetxt(txt){ this.setState({Name:txt}); // this.props.handleCostChange(this.props.n,this.state.cost);//provoke export update on the parent } render() { const selected=this.state.selected; this.state.manualname=false; this.state.keys=this.ProvidersName(this.props.data); const Cmp=this.cmp2string(this.cmpdata(selected).Style); const Cdata=this.cmpdata(selected); const id=this.props.data.Name.replace(/\s/g,'')+this.props.n; return(
); } cmpdata(select){ let out=this.props.data.Data[select]; if (this.state.manualname){ out.Name=this.state.Name; if ( this.state.Provider ==='') { this.state.keys[select] = 'Please provide a Provider'; }else { this.state.keys[select]=this.state.Provider; } } return out; } cmp2string(str){ switch (str) { case "AmountRatesCost" : return AmountRatesCost; case "CategoryCost" : return CategoryCost; case "CategoryAmountRatesCost" : return CategoryAmountRatesCost; case "NoneSelect":return NoneSelect; case "UserCost":{this.state.manualname=true; return UserCost;} } } ProvidersName(main){ const data = main.Data; var providers=[]; for (var i = 0; i < data.length; i++) { providers.push(data[i].Provider); } return providers; } } class ModuleHeader extends React.Component{ constructor(props) { super(props); this.handleAddPlugin = this.handleAddPlugin.bind(this); this.handleRmvPlugin = this.handleRmvPlugin.bind(this); } handleAddPlugin(n){ this.props.handleAddPlugin(n); } handleRmvPlugin(n){ this.props.handleRmvPlugin(n); } render() { let minus=''; let plus=''; if (this.props.show_minus){ minus=} onClick={this.handleRmvPlugin} n={this.props.n} tips={"Remove this line"} idp={this.props.id} info={this.props.data.Name}/>; } if (this.props.show_plus){ plus= } onClick={this.handleAddPlugin} n={this.props.n} tips={"Add a new "+this.props.data.Name}/> } return(
{plus}
{minus}
{this.makeinfo(this.props.keys,this.props.selected,this.props.Cdata)}
{this.props.comments}
); } makeinfo(keys,selected,Cdata){ let name=Cdata.Name; if ( name ===''&&keys[selected]===''){ name='Please provide a Provider'; return ({name}); }else if(keys[selected]==='None'){ return ({name}); }else{ return ({keys[selected]} : {name}); } } } class ManagePlugins extends React.Component{ constructor(props) { super(props); this.handleCostChange = this.handleCostChange.bind(this); this.handleAddPlugin = this.handleAddPlugin.bind(this); this.handleRmvPlugin = this.handleRmvPlugin.bind(this); this.make_exportplug=this.make_exportplug.bind(this); this.make_export = this.make_export.bind(this); this.state={ displayed:[], varsum:{}, plugins:[], export:[], }; this.state.displayed.push(randomint(this.state.displayed)); } handleRmvPlugin(n){ $('#'+n.target).modal('hide'); var tmp=this.state.displayed; tmp.splice(n.n,1); this.setState({displayed:tmp}); this.handleCostChange(n.n,0); } handleAddPlugin(n){ var tmp=this.state.displayed; tmp.splice(n+1,0,randomint(this.state.displayed)); this.setState({displayed:tmp}); } handleCostChange(n,cost) { this.state.varsum[n]=cost; this.props.handleCostChange(this.props.n,sum(this.state.varsum)); } make_exportplug(data,n) { this.state.export[n] = data; this.make_export() } make_export(){ // var out=[]; // for (var i = 0; i < this.state.export.length; i++) { // if((this.state.export[i]!==null)&& // (typeof this.state.export[i].data !== 'undefined')&&(typeof this.state.export[i].state !== 'undefined')){ // out.push({data:this.state.export[i].data,state:this.state.export[i].state}); // }} // // this.props.export(out,this.props.n) if (this.state.export.length === this.give_n()) { this.props.export(this.state.export, this.props.n) } } give_id(index){ return this.state.displayed[index] } give_n(){ const disp=this.state.displayed; return disp.length } componentDidUpdate(){ this.make_export(); } render() { let show_minus = false; if (this.give_n()>1) { show_minus=true; } this.make_export(); return(
{(index) => }
);} } class PluginsMain extends React.Component { constructor(props) { super(props); this.handleCostChange = this.handleCostChange.bind(this); this.make_exportplug=this.make_exportplug.bind(this); this.make_export = this.make_export.bind(this); this.state={ varsum:{}, export:[] }; } handleCostChange(name,e) { this.state.varsum[name]=e; this.props.TotalCost(sum(this.state.varsum)); } make_exportplug(data,n) { this.state.export[n] = data; this.make_export() } make_export(){ if (this.state.export.length === this.props.data.length) { this.props.export(this.state.export); } } render() { return(
Line controls
Category
Provider information
Cost
{(index) => }
); } } // MAIN // --------------------- // --------------------- class Main extends React.Component { constructor(props) { super(props); this.handleCostChange = this.handleCostChange.bind(this); this.make_exportmain = this.make_exportmain.bind(this); this.state= { total: 0, export: [], exportmain:[], }; this.init=true; } handleCostChange(total) { if (this.state.total !== total){ this.setState({total:total}); } } make_exportmain(idata) { const tmp=JSON.parse(JSON.stringify(idata)); let disp=false; if(!this.init){ if(!Object.compare(tmp,this.state.exportmain.data)){ disp=true; } } if((this.init)||(disp)){ this.setState({exportmain: {data: tmp, total: tomoney(this.state.total)}}); this.init=false; } } render() { return(
{this.page_head()}
{this.final_cost()} {this.howto()}
{this.page_foot()}
); } howto(){ return(

HOWTO

Categories
This tool is divided by categories (for example Activate storage). Click on the category name, and it will expand.
Providers

Providers can be chosen from the Select a provider box. You can then tune your setting for this provider to fit your needs.

If the provider you want is not registered, you can add it manually with Provide your own provider and then enter your provider/service and cost.

Add or Remove Line

If you want to add a new provider use the } tips={"Add a new category"} onClick={this.fctnull}/> button.

You can also remove a provider with } tips={"Remove this line"} onClick={this.fctnull}/> button.

To know more about
Some extra information about the category or the provider can be obtained with the } tips={"Know more"} onClick={this.fctnull}/> button.
Comments your input
Comments are for your own usage, you can use for remembering what each section is and for a nice export.
Export
You can export your work into different format :
HTML : This format can be used in any wordprocessing software (such as Microsoft Word or Libreoffice).
HTML Source code and Markdown formats are also possible.
Click on the in order to copy your work into your clipboard. A simple Paste will transfer your work into any software.
); } final_cost(){ return(
{/**/}

Total Cost

); } page_head(){ return( ); } move2howto(){ $('html,body').animate({ scrollTop: $("#howto").offset().top}, 'slow'); } page_foot(){ return( ); } fctnull(){} } //Main Declaration // --------------------- // --------------------- ReactDOM.render(
,document.getElementById('root')); $(function () { $('[data-toggle="tooltip"]').tooltip() }); diff --git a/js/data.js b/js/data.js index 9668513..6759c98 100644 --- a/js/data.js +++ b/js/data.js @@ -1,347 +1,348 @@ // Providers // ---------------------------------------------------- // ---------------------------------------------------- // Storage const NasEpfl = { Style: "AmountRatesCost", Provider : "EPFL-VPSI", Name:'NAS', Url : [ {Name:'VPSI-Website',Url:'https://support.epfl.ch/help/epfl?id=epfl_service_status&service=49a363acdb34c700ef64731b8c96191f'}, {Name:'SV-IT Storage Website',Url:'https://sv-it.epfl.ch/stockage'} ], AmountName: "Amount", AmountUnit: "TB", AmountMin : 1, AmountMax : 100, AmountStep : 1, AmountFree:1, AmountFreeCumulative:false, RateVar : true, RateName : 'Performance', Rates : { 'Collaborative': 165, 'On-line archive': 110, 'Raw': 55 }, RateUnit : "CHF / TB" }; const SwitchEpfl = { Style : 'CategoryCost', Provider : "Switch-EPFL", Name:'Online Storage', Url : [ {Name:'Switch Website',Url:'https://drive.switch.ch/'} ], CatName:'Options', Cat:{ 'Cloud Based max 50GB':0, }, CatUnit:'CHF', }; const GoogleDriveEdu = { Style : 'CategoryCost', Provider : "Google Drive Educ", Name:'Online Storage', Url : [ {Name :'Google Education Page',Url:'https://edu.google.com/?modal_active=none'} ], CatName:'Options', Cat:{ 'Cloud Based illimited':0, }, CatUnit:'CHF', }; // ELN const SLIMSEpfl = { Style:'CategoryAmountRatesCost', Provider : "EPFL-SV-IT", Name:'SLIMS', Url : [ {Name:'SLIMS on SV-IT Website',Url:'https://sv-it.epfl.ch/lims'}, {Name: 'Genohm (SLIMS Company)',Url:'https://www.genohm.com/'} ], CatName:'PI Status', Cat:{ 'Full Professor':3000, 'Associate Professor':2000, 'Tenure Track Assistant Professor or Core Facility':1000 }, CatUnit:'CHF', AmountName: "Storage", AmountUnit: "TB", AmountMin : 1, AmountMax : 100, AmountStep : 1, AmountFree:0, AmountFreeCumulative:false, RateVar : true, RateName: 'ELN Storage', Rates : { 'Stored on EPFL Server': 165, }, RateUnit : "CHF / TB" }; // Database const MysqlEpfl = { Style : 'CategoryCost', Provider : "EPFL-VPSI", Name:'MySql', Url : [ {Name:'EPFL VPSI ',Url:'https://support.epfl.ch/epfl?id=epfl_service_status&service=eb026fa0db34c700ef64731b8c96198e'} ], CatName:'Options', Cat:{ 'MySQL max 2GB':0, }, CatUnit:'CHF', }; // Repository const Zenodo = { Style : 'CategoryCost', Provider : "Zenodo-CERN", Name:'Zenodo', Url : [ {Name:'Zenodo Website',Url:'https://www.zenodo.org/'}, {Name:'About Zenodo',Url:'http://about.zenodo.org/'}, ], CatName:'Options', Cat:{ 'Max 50GB per Dataset':0, }, CatUnit:'CHF', }; const C4science = { Style : 'CategoryCost', Provider : "EPFL-SCITAS", Name:'C4Science', Url : [ {Name:'C4Science Website',Url:'https://www.c4science.ch/'} ], CatName:'Options', Cat:{ 'Free for text file':0, }, CatUnit:'CHF', }; const Github = { Style: "AmountRatesCost", Provider : "GitHub", Name:'GitHub', Url : [ {Name:'Github Website Pricing',Url:'https://github.com/pricing'} ], AmountName: "Number of user", AmountUnit: "User(s)", AmountMin : 1, AmountMax : 100, AmountStep : 1, AmountFree:0, AmountFreeCumulative:false, RateVar : true, RateName:'Plan', Rates:{ 'OpenSource project':0, 'Developer (for one user)':81.6, 'Team (min 5 users)':104.9, 'Business Cloud':244.7 }, RateUnit:'CHF / Users', }; const Bitbucket= { Style: "AmountRatesCost", Provider : "Bitbucket", Name:'BitBucket', Url : [ {Name:'Bitbucket Website Pricing',Url:'https://bitbucket.org/product/pricing'} ], AmountName: "Number of user", AmountUnit: "User(s)", AmountMin : 1, AmountMax : 100, AmountStep : 1, AmountFree:0, AmountFreeCumulative:false, RateVar : true, RateName:'Plan', Rates:{ 'Free (up to 5 users)':0, 'Standard for growing teams (min 5 users)':24, 'Premium for large teams (min 5 users)':60, }, RateUnit:'CHF / Users', }; const Gitlab= { Style: "AmountRatesCost", Provider : "Gitlab", Name:'Gitlab', Url : [ {Name:'Gitlab Website Pricing',Url:'https://about.gitlab.com/pricing/'} ], AmountName: "Number of user", AmountUnit: "User(s)", AmountMin : 1, AmountMax : 100, AmountStep : 1, AmountFree:0, AmountFreeCumulative:false, RateVar : true, RateName:'Plan', Rates:{ 'Core Self Hosted':0, 'Free Cloud Based' : 0, 'Starter Self Hosted':48, 'Bronze Cloud based':48, 'Premium Self Hosted':228, 'Silver Cloud Based':228 }, RateUnit:'CHF / Users', }; const Figshare = { Style : 'CategoryCost', Provider : "FigShare", Name:'Figshare', Url : [ {Name:'Figshare website',Url:'https://figshare.com/'}, {Name:'Figshare Pricing',Url:'https://www.g2crowd.com/products/figshare/pricing'} ], CatName:'Options', Cat:{ 'Free 1GB':0, '10GB':96, '15GB':132, '20GB':180 }, CatUnit:'CHF', }; const Dryad = { Style : 'CategoryAmountRatesCost', Provider : "Dryad", Name:'Dyrad', Url : [ {Name:'Dryad Website Pricing',Url:'https://Datadryad.org/pages/payment'} ], CatName:'Options', Cat:{ 'up to 20GB if DPC covered':0, 'up to 20GB if no DPC covered':120 }, CatUnit:'CHF', AmountName: "Extra - Storage", AmountUnit: "GB", AmountMin : 0, AmountMax : 100, AmountStep : 10, AmountFree:0, AmountFreeCumulative:false, RateVar : true, RateName: 'ELN Storage', Rates : { 'Extra Storage': 50, }, RateUnit : "CHF / GB" }; // System variable definition // ---------------------------------------------------- // ---------------------------------------------------- const NoneSelected={ Style: 'NoneSelect', Provider:'None', Name:'Select a Provider', Url:'', }; const UserCostSelect={ Style : 'UserCost', Provider:'Provide your own provider', Name:'', Url:'', }; // Categories definition // ---------------------------------------------------- // ---------------------------------------------------- const storage={ Name : 'Active Storage', Icon : 'storage.png', Url : [ {Name:'EPFL RDM',Url:'https://researchData.epfl.ch/work-with-Data/storage/'} ], Intro :'', Data :[NoneSelected, NasEpfl, SwitchEpfl, GoogleDriveEdu, UserCostSelect, ] }; const ELN={ Name : 'Electronic LabBook', Icon : 'eln.png', Url : [ {Name: 'EPFL RDM',Url:'https://researchData.epfl.ch/work-with-Data/active-Data-management/'} ], Intro :'', Data :[NoneSelected, SLIMSEpfl, UserCostSelect, ] }; const Database={ Name : 'Database', Icon : 'database.png', Url : '', Intro :'', Data :[NoneSelected, MysqlEpfl, UserCostSelect, ] }; const repository={ Name : 'Repository', Icon : 'repos.png', Url : [ {Name:'EPFL RDM WebPage',Url:'https://researchData.epfl.ch/publish-preserve/'} ], Intro :'', Data :[ NoneSelected, Zenodo, C4science, Github, Bitbucket, Gitlab, Figshare, Dryad, UserCostSelect, ] }; // Combine Categories // ---------------------------------------------------- // ---------------------------------------------------- const MainData={ Currency:'CHF', + Updated:'29/11/2018', Data:[storage,ELN,Database,repository] };