diff --git a/gradle/settings.gradle b/gradle/settings.gradle
index 94053b08c09bf8d3f178fed9c7981fcc80f24364..66310dbb5c5af7153a686e4c6335ba304c6d2099 100644
--- a/gradle/settings.gradle
+++ b/gradle/settings.gradle
@@ -1,4 +1,4 @@
 includeFlat 'commonbase', 'common', 'openbis_api', 'openbis-common', 'authentication', 'dbmigration', 'openbis', 
     'datastore_server', 'screening', 'rtd_phosphonetx', 'deep_sequencing_unit', 'rtd_yeastx', 
     'openbis_standard_technologies', 'installation', 'image_readers', 'ui-test', 'js-test', 'datamover', 
-    'plasmid', 'rtd_cina', 'openbis_oai_pmh', 'big_data_link_server'
+    'plasmid', 'rtd_cina', 'openbis_oai_pmh', 'big_data_link_server', 'openbis_ng_ui'
diff --git a/installation/settings.gradle b/installation/settings.gradle
index f4f7784b74b2f9f07a0cfe2bdcb319264b4e13e9..3f3662d1ec0119b599baabe637f170dfdfa0bab0 100644
--- a/installation/settings.gradle
+++ b/installation/settings.gradle
@@ -1,4 +1,4 @@
 includeFlat 'commonbase', 'common', 
     'openbis_api', 'openbis-common', 'authentication', 'dbmigration', 'openbis', 
     'datastore_server', 'screening', 'rtd_yeastx', 'rtd_phosphonetx', 'deep_sequencing_unit', 'plasmid',
-    'openbis_standard_technologies', 'big_data_link_server'
+    'openbis_standard_technologies', 'big_data_link_server', 'openbis_ng_ui'
diff --git a/microscopy-migration-tool/src/ethz/ch/Migration.java b/microscopy-migration-tool/src/ethz/ch/Migration.java
index ce6107ab308a5ce4c784234dc56dc4a41aa938b0..9004db568d266f19e0b6cbbc0883ef22435ac8d3 100644
--- a/microscopy-migration-tool/src/ethz/ch/Migration.java
+++ b/microscopy-migration-tool/src/ethz/ch/Migration.java
@@ -63,10 +63,9 @@ public class Migration
     private static final String OPENBIS_LOCAL_DEV = "http://localhost:8888";
     private static final String OPENBIS_LOCAL_PROD = "https://localhost:8443";
     private static final String OPENBIS_SCU = "https://openbis-scu.ethz.ch";
-        
-    private static final String OPENBIS_URL = OPENBIS_LOCAL_DEV + "/openbis/openbis" + IApplicationServerApi.SERVICE_URL;
-
-    private static final int TIMEOUT = 300000;
+    private static final String OPENBIS_SCU_TEST = "https://bs-lamp09.ethz.ch:8443/";
+    
+    private static final int TIMEOUT = Integer.MAX_VALUE;
     
     private static final List<String> EXCLUDE_SPACES = Collections.EMPTY_LIST;
     
@@ -80,7 +79,7 @@ public class Migration
             doTheWork(COMMIT_CHANGES_TO_OPENBIS, URL, user, pass, true, true, true);
         } else {
             System.out.println("Example: java -jar microscopy_migration_tool.jar https://openbis-domain.ethz.ch user password");
-            //doTheWork(true, OPENBIS_URL, "pontia", "a", true, true, true);
+            doTheWork(false, OPENBIS_SCU_TEST + "/openbis/openbis" + IApplicationServerApi.SERVICE_URL, "migration", "migrationtool", true, true, true);
         }
     }
     
@@ -89,6 +88,15 @@ public class Migration
         SslCertificateHelper.trustAnyCertificate(URL);
         IApplicationServerApi v3 = HttpInvokerUtils.createServiceStub(IApplicationServerApi.class, URL, TIMEOUT);
         String sessionToken = v3.login(userId, pass);
+        Map<String, String> serverInfo = v3.getServerInformation(sessionToken);
+        
+        if(serverInfo.containsKey("project-samples-enabled") && serverInfo.get("project-samples-enabled").equals("true")) {
+            System.out.println("Project samples enabled.");
+        } else {
+            System.out.println("Enable project samples before running the migration.");
+            return;
+        }
+        
         if(installELNTypes) {
             installELNTypes(sessionToken, v3, COMMIT_CHANGES_TO_OPENBIS);
         }
diff --git a/openbis_ng_ui/.gitignore b/openbis_ng_ui/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..9abf471530fedae378d9938b8c80d01dd02f4cca
--- /dev/null
+++ b/openbis_ng_ui/.gitignore
@@ -0,0 +1,10 @@
+.vagrant
+.project
+.settings
+ubuntu-bionic-18.04-cloudimg-console.log
+nodejs
+node_modules
+.gradle
+.DS_Store
+build
+package-lock.json
diff --git a/openbis_ng_ui/README.md b/openbis_ng_ui/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7b29edbf02850f3f2d24e98847fad13bd306860f
--- /dev/null
+++ b/openbis_ng_ui/README.md
@@ -0,0 +1,29 @@
+# openBIS - prototypes for new UI
+
+## Setting up the environment
+
+1. Install latest version of VirtualBox (https://www.virtualbox.org)
+
+2. Install latest version of Vagrant (https://www.vagrantup.com/downloads.html)
+
+3. vagrant plugin install vagrant-vbguest vagrant-notify-forwarder vagrant-disksize
+ 
+4. cd env/dev
+
+5. vagrant up
+
+6. Browse to https://localhost:8122/openbis and accept the self-signed certificate
+
+* openBIS is now running at https://localhost:8122/openbis (login: admin/password)
+* React proto is now running at http://localhost:8124
+
+## Additional info for Linux users
+
+On Ubuntu (and maybe on other Linux distributions, too), you might get errors like this when running "vagrant up":
+
+  terminate called after throwing an instance of 'std::runtime_error'
+    what():  Could not add watch
+
+This situation will prevent the development environment from working properly. The reason for the error is https://github.com/mhallin/vagrant-notify-forwarder/issues/5. It can be fixed by increasing the maximum number of watches on the host system. On Ubuntu, this is done like this:
+
+  echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p
diff --git a/openbis_ng_ui/build.gradle b/openbis_ng_ui/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..6d7cee42e49c71ed9b3973522ba824f56bb16a14
--- /dev/null
+++ b/openbis_ng_ui/build.gradle
@@ -0,0 +1,21 @@
+buildscript {
+    repositories {
+        mavenCentral()
+        maven {
+          url "https://plugins.gradle.org/m2/"
+        }
+    }
+}
+
+configure(allprojects) {
+  apply plugin:'base'
+
+  task wrapper(type: Wrapper) {
+    gradleVersion = '4.10'
+  }
+  
+  repositories {
+    mavenCentral()
+  }
+}
+
diff --git a/openbis_ng_ui/env/dev/Vagrantfile b/openbis_ng_ui/env/dev/Vagrantfile
new file mode 100644
index 0000000000000000000000000000000000000000..ade770771848aba272c7361a2801fa00c687d3ba
--- /dev/null
+++ b/openbis_ng_ui/env/dev/Vagrantfile
@@ -0,0 +1,27 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+Vagrant.configure("2") do |config|
+  config.vm.box = "ubuntu/bionic64"
+  config.vm.box_version = "20180531.0.0"
+  
+  config.vm.provider "virtualbox" do |v|
+    v.memory = 4096
+    v.cpus = 1
+    v.name = "openbis-ng-ui"
+  end
+
+  # Needs to be unique among VMs
+  config.notify_forwarder.port = 22021
+
+  config.vm.synced_folder "../..", "/home/vagrant/openbis-ui-proto"
+  config.vm.synced_folder '.', '/vagrant', disabled: true
+  config.vm.synced_folder "files", "/files", :mount_options => ["ro"]
+
+  config.vm.network "forwarded_port", guest: 8122, host: 8122, host_ip: "0.0.0.0"
+  config.vm.network "forwarded_port", guest: 8123, host: 8123, host_ip: "0.0.0.0"
+  config.vm.network "forwarded_port", guest: 8124, host: 8124, host_ip: "0.0.0.0"
+
+  config.vm.provision :shell, path: "bootstrap.sh"
+  config.vm.provision :shell, path: "files/start-services.sh", run: "always", privileged: false
+end
diff --git a/openbis_ng_ui/env/dev/bootstrap.sh b/openbis_ng_ui/env/dev/bootstrap.sh
new file mode 100644
index 0000000000000000000000000000000000000000..50efc646eaddff48373bc80fcfdf987ba41364fa
--- /dev/null
+++ b/openbis_ng_ui/env/dev/bootstrap.sh
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+sudo locale-gen en_US.UTF-8
+
+echo "
+LC_ALL=en_US.UTF-8
+LANG=en_US.UTF-8
+" >> /etc/environment
+
+apt update
+apt install -y unzip elinks openjdk-8-jdk inotify-tools git postgresql
+
+cp /files/pg_hba.conf /etc/postgresql/10/main/pg_hba.conf 
+service postgresql restart
+sleep 10 # let the db engine start
+
+mkdir -p /node/node_modules
+chown -R vagrant:vagrant /node
+
+sudo -E -u postgres -H -i /files/setup-postgres.sh
+sudo -E -u vagrant -H -i /files/setup-vagrant.sh
diff --git a/openbis_ng_ui/env/dev/files/console.properties b/openbis_ng_ui/env/dev/files/console.properties
new file mode 100644
index 0000000000000000000000000000000000000000..faa327b56b38bcb8352771ef160acba48fddf193
--- /dev/null
+++ b/openbis_ng_ui/env/dev/files/console.properties
@@ -0,0 +1,62 @@
+#
+# The path where openBIS will be installed.
+# 
+# Example : 
+#     INSTALL_PATH=/home/openbis/
+#
+#   will result in the following directory structure
+#
+#   + /home/openbis
+#     +  bin/
+#     +  servers/
+#        + core-plugins/
+#        + openBIS-server/
+#        + datastore_server/
+# 
+INSTALL_PATH=/home/vagrant/openbis
+
+#
+# The path where openBIS will keep the imported data (e.g. images, analysis files)
+# and its incoming folders. 
+#
+DSS_ROOT_DIR=/home/vagrant/dss_root
+
+# Possible configuration options
+#   'local' - if the openBIS servers will only be accessed from this machine
+#   'server' - if the installation is meant to be accessible for remote users
+INSTALLATION_TYPE=local
+
+# Path to the file which should replace the current Java key store file
+#KEY_STORE_FILE = <path to key store>
+
+# Password of the key store
+KEY_STORE_PASSWORD = changeit
+
+# Password of the key
+KEY_PASSWORD = changeit
+
+# Standard technology PROTEOMICS is disabled by default
+#PROTEOMICS = true
+
+# Standard technology SCREENING is disabled by default
+#SCREENING = true
+
+# Standard technology ILLUMINA-NGS (ETH BSSE Setup) is disabled by default
+#ILLUMINA-NGS = true
+
+# Standard technology ELN-LIMS is disabled by default
+#ELN-LIMS = true
+
+# Standard technology MICROSCOPY is disabled by default
+#MICROSCOPY = true
+
+# Standard technology FLOW CYTOMETRY is disabled by default
+#FLOW = true
+
+# Full ELN/LIMS master data is enabled by default. This setting is meaningful only if ELN-LIMS is enabled
+ELN-LIMS-MASTER-DATA = false
+
+#
+# Comma-separated list of databases to backup. If the list is empty or undefined all databases
+# will be backauped.
+#DATABASES_TO_BACKUP =
\ No newline at end of file
diff --git a/openbis_ng_ui/env/dev/files/pg_hba.conf b/openbis_ng_ui/env/dev/files/pg_hba.conf
new file mode 100644
index 0000000000000000000000000000000000000000..5383ee2765aad917cb2c909e743f2a9cf8b335e3
--- /dev/null
+++ b/openbis_ng_ui/env/dev/files/pg_hba.conf
@@ -0,0 +1,99 @@
+# PostgreSQL Client Authentication Configuration File
+# ===================================================
+#
+# Refer to the "Client Authentication" section in the PostgreSQL
+# documentation for a complete description of this file.  A short
+# synopsis follows.
+#
+# This file controls: which hosts are allowed to connect, how clients
+# are authenticated, which PostgreSQL user names they can use, which
+# databases they can access.  Records take one of these forms:
+#
+# local      DATABASE  USER  METHOD  [OPTIONS]
+# host       DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
+# hostssl    DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
+# hostnossl  DATABASE  USER  ADDRESS  METHOD  [OPTIONS]
+#
+# (The uppercase items must be replaced by actual values.)
+#
+# The first field is the connection type: "local" is a Unix-domain
+# socket, "host" is either a plain or SSL-encrypted TCP/IP socket,
+# "hostssl" is an SSL-encrypted TCP/IP socket, and "hostnossl" is a
+# plain TCP/IP socket.
+#
+# DATABASE can be "all", "sameuser", "samerole", "replication", a
+# database name, or a comma-separated list thereof. The "all"
+# keyword does not match "replication". Access to replication
+# must be enabled in a separate record (see example below).
+#
+# USER can be "all", a user name, a group name prefixed with "+", or a
+# comma-separated list thereof.  In both the DATABASE and USER fields
+# you can also write a file name prefixed with "@" to include names
+# from a separate file.
+#
+# ADDRESS specifies the set of hosts the record matches.  It can be a
+# host name, or it is made up of an IP address and a CIDR mask that is
+# an integer (between 0 and 32 (IPv4) or 128 (IPv6) inclusive) that
+# specifies the number of significant bits in the mask.  A host name
+# that starts with a dot (.) matches a suffix of the actual host name.
+# Alternatively, you can write an IP address and netmask in separate
+# columns to specify the set of hosts.  Instead of a CIDR-address, you
+# can write "samehost" to match any of the server's own IP addresses,
+# or "samenet" to match any address in any subnet that the server is
+# directly connected to.
+#
+# METHOD can be "trust", "reject", "md5", "password", "scram-sha-256",
+# "gss", "sspi", "ident", "peer", "pam", "ldap", "radius" or "cert".
+# Note that "password" sends passwords in clear text; "md5" or
+# "scram-sha-256" are preferred since they send encrypted passwords.
+#
+# OPTIONS are a set of options for the authentication in the format
+# NAME=VALUE.  The available options depend on the different
+# authentication methods -- refer to the "Client Authentication"
+# section in the documentation for a list of which options are
+# available for which authentication methods.
+#
+# Database and user names containing spaces, commas, quotes and other
+# special characters must be quoted.  Quoting one of the keywords
+# "all", "sameuser", "samerole" or "replication" makes the name lose
+# its special character, and just match a database or username with
+# that name.
+#
+# This file is read on server startup and when the server receives a
+# SIGHUP signal.  If you edit the file on a running system, you have to
+# SIGHUP the server for the changes to take effect, run "pg_ctl reload",
+# or execute "SELECT pg_reload_conf()".
+#
+# Put your actual configuration here
+# ----------------------------------
+#
+# If you want to allow non-local connections, you need to add more
+# "host" records.  In that case you will also need to make PostgreSQL
+# listen on a non-local interface via the listen_addresses
+# configuration parameter, or via the -i or -h command line switches.
+
+
+
+
+# DO NOT DISABLE!
+# If you change this first entry you will need to make sure that the
+# database superuser can access the database using some other method.
+# Noninteractive access to all databases is required during automatic
+# maintenance (custom daily cronjobs, replication, and similar tasks).
+#
+# Database administrative login by Unix domain socket
+#local   all             postgres                                peer
+
+# TYPE  DATABASE        USER            ADDRESS                 METHOD
+
+# "local" is for Unix domain socket connections only
+local   all             all                                     trust
+# IPv4 local connections:
+host    all             all             127.0.0.1/32            trust
+# IPv6 local connections:
+host    all             all             ::1/128                 md5
+# Allow replication connections from localhost, by a user with the
+# replication privilege.
+#local   replication     all                                     peer
+#host    replication     all             127.0.0.1/32            md5
+#host    replication     all             ::1/128                 md5
\ No newline at end of file
diff --git a/openbis_ng_ui/env/dev/files/restart-services.sh b/openbis_ng_ui/env/dev/files/restart-services.sh
new file mode 100755
index 0000000000000000000000000000000000000000..dec60f6b7fc5589c76db08fdb5ded1deef5be1fc
--- /dev/null
+++ b/openbis_ng_ui/env/dev/files/restart-services.sh
@@ -0,0 +1,2 @@
+screen -ls | grep Detached | cut -d. -f1 | awk '{print $1}' | xargs kill
+/files/start-services.sh
diff --git a/openbis_ng_ui/env/dev/files/setup-postgres.sh b/openbis_ng_ui/env/dev/files/setup-postgres.sh
new file mode 100755
index 0000000000000000000000000000000000000000..4cbd481a4eb5f70eda6808e53c2fc080c4c6c052
--- /dev/null
+++ b/openbis_ng_ui/env/dev/files/setup-postgres.sh
@@ -0,0 +1 @@
+createuser vagrant
diff --git a/openbis_ng_ui/env/dev/files/setup-vagrant.sh b/openbis_ng_ui/env/dev/files/setup-vagrant.sh
new file mode 100755
index 0000000000000000000000000000000000000000..cf30c8e5513a2ef14fc62f9bdbef1b60b23f235c
--- /dev/null
+++ b/openbis_ng_ui/env/dev/files/setup-vagrant.sh
@@ -0,0 +1,17 @@
+build="http://stage-jenkins.ethz.ch:8090/job/installation-18.06/lastSuccessfulBuild"
+path=$(curl -s "$build/api/xml?xpath=//relativePath"|sed -e "s/<relativePath>//"|sed -e "s/<\/relativePath>//")
+wget -q $build/artifact/$path
+archive=$(basename $path)
+tar xvfz $archive
+directory=$(echo "$archive" | cut -f 1 -d '.')
+cp /files/console.properties $directory
+export ADMIN_PASSWORD='password'
+$directory/run-console.sh
+
+sed -i "/jetty.ssl.port=/ s/=.*/=8122/" /home/vagrant/openbis/servers/openBIS-server/jetty/start.d/ssl.ini
+sed -i "/host-address =/ s/=.*/= https:\/\/localhost/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties
+sed -i "/port =/ s/=.*/= 8123/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties
+sed -i "/server-url =/ s/=.*/= \${host-address}:8122/" /home/vagrant/openbis/servers/datastore_server/etc/service.properties
+
+cd  /home/vagrant/openbis-ui-proto/react
+ln -s /node/node_modules
diff --git a/openbis_ng_ui/env/dev/files/start-services.sh b/openbis_ng_ui/env/dev/files/start-services.sh
new file mode 100755
index 0000000000000000000000000000000000000000..a57f5b7841bf3a3f433523adec4d179f713e9726
--- /dev/null
+++ b/openbis_ng_ui/env/dev/files/start-services.sh
@@ -0,0 +1,17 @@
+sudo /usr/sbin/VBoxService --timesync-set-start
+
+/home/vagrant/openbis/bin/allup.sh
+
+#cd openbis-ui-proto/react
+#../gradlew npmInstall
+#cd 
+
+#export PATH=$PATH:/home/vagrant/openbis-ui-proto/react/nodejs/node-v10.1.0-linux-x64/bin
+
+#screen -S dev -t webpack -Adm bash -c "cd openbis-ui-proto/react; npm run dev; bash"
+
+#echo "Waiting Webpack to launch on 8124..."
+
+#while ! nc -z localhost 8124; do
+#  sleep 3
+#done
diff --git a/openbis_ng_ui/gradle/wrapper/gradle-wrapper.jar b/openbis_ng_ui/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000000000000000000000000000000000000..91ca28c8b802289c3a438766657a5e98f20eff03
Binary files /dev/null and b/openbis_ng_ui/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/openbis_ng_ui/gradle/wrapper/gradle-wrapper.properties b/openbis_ng_ui/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000000000000000000000000000000000..115e6ac0aaba10c8e73340fdd7744897234b59ac
--- /dev/null
+++ b/openbis_ng_ui/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/openbis_ng_ui/gradlew b/openbis_ng_ui/gradlew
new file mode 100755
index 0000000000000000000000000000000000000000..cccdd3d517fc5249beaefa600691cf150f2fa3e6
--- /dev/null
+++ b/openbis_ng_ui/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/openbis_ng_ui/gradlew.bat b/openbis_ng_ui/gradlew.bat
new file mode 100644
index 0000000000000000000000000000000000000000..f9553162f122c71b34635112e717c3e733b5b212
--- /dev/null
+++ b/openbis_ng_ui/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/openbis_ng_ui/react/.babelrc b/openbis_ng_ui/react/.babelrc
new file mode 100644
index 0000000000000000000000000000000000000000..99c679001f8169cc931d5c5d7c0d57717d3f0d19
--- /dev/null
+++ b/openbis_ng_ui/react/.babelrc
@@ -0,0 +1,4 @@
+{
+    "presets":["env", "react"],
+    "plugins": ["transform-class-properties"]
+}
diff --git a/openbis_ng_ui/react/.eslintrc.js b/openbis_ng_ui/react/.eslintrc.js
new file mode 100644
index 0000000000000000000000000000000000000000..c7098787dadc161c6a5d87172499eba27b514eb9
--- /dev/null
+++ b/openbis_ng_ui/react/.eslintrc.js
@@ -0,0 +1,44 @@
+module.exports = {
+  root: true,
+  parser: "babel-eslint",
+  parserOptions: {
+    ecmaVersion: 2017,
+    sourceType: 'module',
+    ecmaFeatures: {
+      jsx: true
+    }
+  },
+  env: {
+    browser: true,
+  },
+  extends: [
+    'eslint:recommended',
+    'plugin:react/recommended'
+  ],
+  plugins: [
+    'react'
+  ],
+  settings: {
+    react: {
+      createClass: "createReactClass",
+      pragma: "React",
+      version: "16.4.2"
+    },
+    propWrapperFunctions: [ "forbidExtraProps" ]
+  },
+  rules: {
+    "react/jsx-uses-react": "error",
+    "react/jsx-uses-vars": "error",
+
+    "indent": ["error", 2],
+    "linebreak-style": ["error", "unix"],
+    "quotes": ["error", "single"],
+    "semi": ["error", "never"],
+    "eqeqeq": ["error", "always"],
+
+    "react/prop-types": "off",
+
+    // override default options for rules from base configurations
+    "no-cond-assign": ["error", "always"],
+  }
+}
diff --git a/openbis_ng_ui/react/build.gradle b/openbis_ng_ui/react/build.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..fc7e9c044f67c1e20332096e5bf858b17653dbc3
--- /dev/null
+++ b/openbis_ng_ui/react/build.gradle
@@ -0,0 +1,14 @@
+plugins {
+  id "com.moowork.node" version "1.2.0"
+}
+
+node {
+  download = true
+  // npmVersion not defined => use the one bundled with node
+  version = '10.1.0'
+  workDir = file("${projectDir}/nodejs")
+  nodeModulesDir = file("${projectDir}")
+}
+
+npm_run_build.dependsOn npm_run_test
+build.dependsOn npm_run_build
diff --git a/openbis_ng_ui/react/index.html b/openbis_ng_ui/react/index.html
new file mode 100644
index 0000000000000000000000000000000000000000..093b537e039a0dc2c9bd426df85ec1cc8c0789e1
--- /dev/null
+++ b/openbis_ng_ui/react/index.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html lang="en" style="height: 100%;">
+  <head>
+    <title>openBIS / React</title>
+    <meta charset="utf-8">
+    <script src="/openbis/resources/api/v3/config.js"></script>
+    <script src="/openbis/resources/api/v3/require.js"></script>
+  </head>
+  <body style="min-height:100%; margin: 0px 0px 0px 0px;">
+    <div id="app"></div>
+  </body>
+</html>
diff --git a/openbis_ng_ui/react/package.json b/openbis_ng_ui/react/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..2be6afa14663370ce00e15b968c7870b9cf81966
--- /dev/null
+++ b/openbis_ng_ui/react/package.json
@@ -0,0 +1,58 @@
+{
+  "name": "openbis-ng-ui",
+  "version": "1.0.0",
+  "description": "React-based UI proto for openBIS",
+  "author": "Antti Luomi",
+  "license": "Apache-2.0",
+  "dependencies": {
+    "@material-ui/core": "3.0.2",
+    "@material-ui/icons": "3.0.1",
+    "install": "^0.12.2",
+    "npm": "^6.4.1",
+    "prop-types": "^15.6.2",
+    "react": "16.4.2",
+    "react-dnd": "5.0.0",
+    "react-dnd-html5-backend": "5.0.1",
+    "react-dom": "16.4.2",
+    "react-redux": "5.0.7",
+    "redux": "4.0.0",
+    "redux-saga": "0.16.0",
+    "typeface-roboto": "0.0.54"
+  },
+  "devDependencies": {
+    "babel-core": "6.26.0",
+    "babel-loader": "7.1.4",
+    "babel-preset-env": "1.7.0",
+    "babel-preset-react": "6.24.1",
+    "html-webpack-plugin": "3.2.0",
+    "source-map-loader": "0.2.3",
+    "webpack": "4.6.0",
+    "webpack-dev-server": "3.1.3",
+    "webpack-cli": "2.0.14",
+    "style-loader": "0.21.0",
+    "css-loader": "1.0.0",
+    "url-loader": "1.1.1",
+    "raw-loader": "0.5.1",
+    "file-loader": "1.1.11",
+    "jest": "23.1.0",
+    "eslint": "4.19.1",
+    "react-loader": "2.4.5",
+    "react-hot-loader": "4.3.6",
+    "eslint-config-standard": "11.0.0",
+    "eslint-plugin-standard": "3.1.0",
+    "eslint-plugin-react": "7.11.1",
+    "eslint-plugin-import": "2.13.0",
+    "eslint-plugin-node": "6.0.1",
+    "eslint-plugin-promise": "3.8.0",
+    "eslint-plugin-jest": "21.17.0",
+    "babel-eslint": "8.2.5"
+  },
+  "scripts": {
+    "dev": "webpack-dev-server --hot --inline --watch --host 0.0.0.0 --port 9124 --config webpack.config.dev.js",
+    "build": "webpack-cli --config webpack.config.js",
+    "unit": "jest",
+    "lint": "eslint --ext .js,.jsx src test",
+    "lint:fix": "eslint --ext .js,.jsx src test --fix",
+    "test": "npm run lint && npm run unit"
+  }
+}
diff --git a/openbis_ng_ui/react/src/components/App.jsx b/openbis_ng_ui/react/src/components/App.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..db8d156935d58fdfbc8648a6aabb739efeef86b6
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/App.jsx
@@ -0,0 +1,116 @@
+import React from 'react'
+import Hidden from '@material-ui/core/Hidden'
+import { withStyles } from '@material-ui/core/styles'
+import { connect } from 'react-redux'
+import CircularProgress from '@material-ui/core/CircularProgress'
+import HTML5Backend from 'react-dnd-html5-backend'
+import { DragDropContext } from 'react-dnd'
+import flow from 'lodash/flow'
+
+import Browser from './Browser.jsx'
+import BrowserFilter from './BrowserFilter.jsx'
+import BrowserButtons from './BrowserButtons.jsx'
+import ModeBar from './ModeBar.jsx'
+import TabPanel from './TabPanel.jsx'
+import TopBar from './TopBar.jsx'
+
+const drawerWidth = 400
+
+/* eslint-disable-next-line no-unused-vars */
+const styles = theme => ({
+  right: {
+    width: `calc(100% - ${drawerWidth + 4 + 4 + 1}px)`,
+    paddingLeft: 4,
+    marginLeft: drawerWidth + 5,
+  },
+  
+  left: {
+    float: 'left',
+    width: drawerWidth,
+    paddingRight: 4,
+    borderRight: '1px dotted',
+    borderColor: '#e3e5ea',
+    height: '100%',
+    position: 'absolute',
+  },
+
+  browser: {
+    height: 'calc(100% - 160px)',
+    overflow: 'auto'
+  },
+
+  topMargin: {
+    marginTop: 8
+  },
+
+  loader: { 
+    position: 'absolute',
+    paddingTop: '15%',      
+    width: '100%',
+    height: '100%',
+    zIndex: 1000,
+    backgroundColor: '#000000',
+    opacity: 0.5,
+    textAlign: 'center',
+  },
+
+})
+
+function mapStateToProps(state) {
+  return {
+    loading: state.loading,
+  }
+}
+
+class App extends React.Component {
+  
+  render() {
+    const classes = this.props.classes
+    
+    return (
+      <div>
+        {
+          this.props.loading &&
+          <div className={classes.loader}>
+            <CircularProgress className={classes.progress} />
+          </div>
+        }
+        <Hidden mdUp>
+          <TopBar/>
+          <div className={classes.topMargin}>
+            <ModeBar/>
+          </div>
+          <BrowserFilter/>
+          <Browser/>
+          <BrowserButtons />
+          <div className={classes.topMargin}>
+            <TabPanel />
+          </div>
+        </Hidden>
+
+        <Hidden smDown>
+          <div className={classes.left}>
+            <ModeBar/>
+            <BrowserFilter/>
+            <div className={classes.browser}>
+              <Browser />
+            </div>
+            <BrowserButtons />
+          </div>
+          <div className={classes.right}>
+            <TopBar />
+            <div className={classes.topMargin}>
+              <TabPanel />
+            </div>
+          </div>
+        </Hidden>
+      </div>
+    )
+  }
+}
+
+export default flow(
+  connect(mapStateToProps),
+  withStyles(styles),
+  DragDropContext(HTML5Backend)
+)(App)
diff --git a/openbis_ng_ui/react/src/components/Browser.jsx b/openbis_ng_ui/react/src/components/Browser.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..86f0406b1396a689f03a8cfa0d7a1ef4fe1521b1
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/Browser.jsx
@@ -0,0 +1,40 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import ListItemText from '@material-ui/core/ListItemText'
+
+import BrowserList from './BrowserList.jsx'
+import actions from '../reducer/actions.js'
+
+
+function mapDispatchToProps(dispatch) {
+  return {
+    selectEntity: permId => dispatch(actions.selectEntity(permId)),
+  }
+}
+
+
+function mapStateToProps(state) {
+  // TODO stack tree nodes here when the final tree model is done
+  return {
+    databaseTreeNodes: state.databaseTreeNodes,
+    selectedEntity: state.openEntities.selectedEntity,
+  }
+}
+
+
+class Browser extends React.Component {
+
+  render() {
+    return (
+      <BrowserList 
+        nodes={ this.props.databaseTreeNodes } 
+        level={ 0 } 
+        selectedNodeId={ this.props.selectedEntity } 
+        onSelect={ node => { if (node.type === 'as.dto.space.Space') this.props.selectEntity(node.id) } }
+        renderNode={ node => { return (<ListItemText secondary={node.id} />)} }
+      />
+    )
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(Browser)
diff --git a/openbis_ng_ui/react/src/components/BrowserButtons.jsx b/openbis_ng_ui/react/src/components/BrowserButtons.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..1996ecab692d050f39dbaec2eb19d4007ed61ebc
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/BrowserButtons.jsx
@@ -0,0 +1,38 @@
+import React from 'react'
+import AppBar from '@material-ui/core/AppBar'
+import Toolbar from '@material-ui/core/Toolbar'
+import Grid from '@material-ui/core/Grid'
+import Button from '@material-ui/core/Button'
+import RemoveIcon from '@material-ui/icons/Remove'
+import AddIcon from '@material-ui/icons/Add'
+
+class BrowserButtons extends React.Component {
+
+  render() {
+    return (
+      <AppBar position='static'>
+        <Toolbar>
+          <Grid container alignItems='center'>
+            <Grid item xs={2}>
+              <Button 
+                variant="contained" 
+                color="primary">
+                <RemoveIcon />
+              </Button>
+            </Grid> 
+            <Grid item xs={8} />
+            <Grid item xs={2}>
+              <Button 
+                variant="contained"
+                color="primary">
+                <AddIcon />
+              </Button>
+            </Grid> 
+          </Grid>
+        </Toolbar>
+      </AppBar>
+    )
+  }
+}
+
+export default BrowserButtons
diff --git a/openbis_ng_ui/react/src/components/BrowserFilter.jsx b/openbis_ng_ui/react/src/components/BrowserFilter.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..9ca605b39d7af2357c02a96a2a1de4b0ed24eb2e
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/BrowserFilter.jsx
@@ -0,0 +1,34 @@
+import React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import InputAdornment from '@material-ui/core/InputAdornment'
+import TextField from '@material-ui/core/TextField'
+import FilterIcon from '@material-ui/icons/FilterList'
+
+/*eslint-disable-next-line no-unused-vars*/
+const styles = theme => ({
+  browserFilter: {
+    width: '100%'
+  }
+})
+
+class BrowserFilter extends React.Component {
+
+  render() {
+    const classes = this.props.classes
+
+    return (
+      <TextField
+        className = { classes.browserFilter }
+        placeholder = "Filter"
+        InputProps={{
+          startAdornment: (
+            <InputAdornment position="start">
+              <FilterIcon />
+            </InputAdornment>
+          ),
+        }}/>
+    )
+  }
+}
+
+export default withStyles(styles)(BrowserFilter)
diff --git a/openbis_ng_ui/react/src/components/BrowserList.jsx b/openbis_ng_ui/react/src/components/BrowserList.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..8b17fa4a4383bba35b16f9349cd23d1ea5eec18c
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/BrowserList.jsx
@@ -0,0 +1,88 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import { withStyles } from '@material-ui/core/styles'
+import List from '@material-ui/core/List'
+import ListItem from '@material-ui/core/ListItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import ChevronRightIcon from '@material-ui/icons/ChevronRight'
+import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
+import HourglassEmptyIcon from '@material-ui/icons/HourglassEmpty'
+import PropTypes from 'prop-types'
+
+
+import actions from '../reducer/actions.js'
+
+
+/*eslint-disable-next-line no-unused-vars*/
+const styles = theme => ({
+  noPadding: {
+    paddingTop: '0',
+    paddingBottom: '0',
+  },
+})
+
+
+function mapDispatchToProps(dispatch) {
+  return {
+    expandNode: (e, node) => {
+      e.stopPropagation()
+      dispatch(actions.expandNode(node))
+    },
+    collapseNode: (e, node) => {
+      e.stopPropagation()
+      dispatch(actions.collapseNode(node))
+    },
+  }
+}
+
+class BrowserList extends React.Component {
+
+  icon(node) {
+    if (node.expanded) {
+      return (<ExpandMoreIcon  onClick={ (e) => this.props.collapseNode(e, node) }/>)
+    } else {
+      return (<ChevronRightIcon  onClick={ (e) => this.props.expandNode(e, node) }/>)
+    }
+  }
+
+  render() {
+    const classes = this.props.classes
+
+    return (
+      <List className={classes.noPadding}>
+        {
+          this.props.nodes.map(node => 
+            <div key={node.id}>
+              <ListItem 
+                button
+                selected={this.props.selectedNodeId ===  node.id}
+                key={node.id}
+                onClick = {() => this.props.onSelect(node)}
+                style={{ paddingLeft: '' + this.props.level * 20 + 'px' }}>
+                <ListItemIcon>
+                  {this.icon(node)}
+                </ListItemIcon>
+                { node.loading &&
+                <ListItemIcon>
+                  <HourglassEmptyIcon/>
+                </ListItemIcon>
+                }
+                { this.props.renderNode(node) }
+              </ListItem>
+              {
+                node.expanded &&
+                <BrowserList {...this.props} nodes={node.children} level={this.props.level+1}/>
+              }
+            </div>
+          )
+        }
+      </List>
+    )
+  }
+}
+
+BrowserList.propTypes = {
+  renderNode: PropTypes.func.isRequired,
+}
+
+export default connect(null, mapDispatchToProps)(withStyles(styles)(BrowserList))
diff --git a/openbis_ng_ui/react/src/components/ModeBar.jsx b/openbis_ng_ui/react/src/components/ModeBar.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..1a198dd056585a64dbf56208047c8d302ab24a6c
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/ModeBar.jsx
@@ -0,0 +1,39 @@
+import React from 'react'
+import AppBar from '@material-ui/core/AppBar'
+import Toolbar from '@material-ui/core/Toolbar'
+import Tabs from '@material-ui/core/Tabs'
+import Tab from '@material-ui/core/Tab'
+import { withStyles } from '@material-ui/core/styles'
+
+/*eslint-disable-next-line no-unused-vars*/
+const styles = theme => ({
+  browserTabs: {
+    width: '100%'
+  },
+  browserTab: {
+    minWidth: '50px'
+  }
+})
+
+class ModeBar extends React.Component {
+
+  render() {
+    const classes = this.props.classes
+
+    return (
+      <AppBar position="static">
+        <Toolbar>
+          <Tabs value={0} fullWidth className = { classes.browserTabs }>
+            <Tab className = { classes.browserTab } label="Database" />
+            <Tab className = { classes.browserTab } label="Types" />
+            <Tab className = { classes.browserTab } label="Users" />
+            <Tab className = { classes.browserTab } label="Favourites" />
+            <Tab className = { classes.browserTab } label="Tools" />
+          </Tabs>
+        </Toolbar>
+      </AppBar>
+    )
+  }
+}
+
+export default withStyles(styles)(ModeBar)
diff --git a/openbis_ng_ui/react/src/components/TabContainer.jsx b/openbis_ng_ui/react/src/components/TabContainer.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..30a983e16b4352788f3fc18f9dd85ec044647cad
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/TabContainer.jsx
@@ -0,0 +1,96 @@
+import React from 'react'
+import { withStyles } from '@material-ui/core/styles'
+import AppBar from '@material-ui/core/AppBar'
+import CloseIcon from '@material-ui/icons/Close'
+import IconButton from '@material-ui/core/IconButton'
+import Toolbar from '@material-ui/core/Toolbar'
+import Tabs from '@material-ui/core/Tabs'
+import Tab from '@material-ui/core/Tab'
+import PropTypes from 'prop-types'
+
+import TabContent from './TabContent.jsx'
+
+
+/* eslint-disable-next-line no-unused-vars */
+const styles = theme => ({
+  entityTabs: {
+    width: '100%'
+  },
+  inlineElement: {
+    display: 'inline-block'
+  },
+  hidden: {
+    display: 'none',
+  }
+})
+
+class TabContainer extends React.Component {
+
+  render() {
+    const classes = this.props.classes
+    const selectedKey = this.props.selectedKey
+    const selectedTabIndex = this.props.children.findIndex(child => child.key === selectedKey)
+    return (
+      <div>
+        <div>
+          <AppBar position="static">
+            <Toolbar>
+              <Tabs 
+                value={selectedTabIndex}
+                scrollable={ React.Children.count(this.props.children) > 0 }
+                scrollButtons="auto"
+                className = { classes.entityTabs }>
+                {
+                  React.Children.map(this.props.children, child =>
+                    <Tab 
+                      component="div" 
+                      label={
+                        <div>
+                          <div className = { classes.inlineElement }>{child.props.name}</div>
+                          { child.props.dirty &&
+                            <div className = { classes.inlineElement }>*</div>
+                          }
+                          <div className = { classes.inlineElement }>
+                            <IconButton onClick={ (e) => child.props.onClose(e) } >
+                              <CloseIcon/>
+                            </IconButton>
+                          </div>
+                        </div>}
+                      onClick = {child.props.onSelect}/>
+                  )
+                }
+              </Tabs>
+            </Toolbar>
+          </AppBar>
+        </div>
+        <div>
+          {
+            React.Children.map(this.props.children, child => {
+              return (
+                <div className={ selectedKey === child.key ? {} : classes.hidden }>
+                  {child}
+                </div>
+              )
+            })
+          }
+        </div>
+      </div>
+    )
+  }
+}
+
+TabContainer.propTypes = {
+  selectedKey: PropTypes.string.isRequired,
+  children: function (props, propName, componentName) {
+    const prop = props[propName]
+    let error = null
+    React.Children.forEach(prop, function (child) {
+      if (child.type !== TabContent) {
+        error = new Error('`' + componentName + '` children should be of type `TabContent`.')
+      }
+    })
+    return error
+  }
+}
+
+export default withStyles(styles)(TabContainer)
diff --git a/openbis_ng_ui/react/src/components/TabContent.jsx b/openbis_ng_ui/react/src/components/TabContent.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..d1a925010af5b7ee525fe74ddaa5e52a59bc2b64
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/TabContent.jsx
@@ -0,0 +1,18 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+
+class TabContent extends React.Component {
+  render() {
+    return <div>{this.props.children}</div>
+  }
+}
+
+TabContent.propTypes ={
+  name: PropTypes.string.isRequired,
+  dirty: PropTypes.bool.isRequired,
+  onSelect: PropTypes.func.isRequired,
+  onClose: PropTypes.func.isRequired,
+}
+
+export default TabContent
diff --git a/openbis_ng_ui/react/src/components/TabPanel.jsx b/openbis_ng_ui/react/src/components/TabPanel.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..970e381d19fe46871f6005ce9610670bc04c4ade
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/TabPanel.jsx
@@ -0,0 +1,69 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import EntityDetails from './database/EntityDetails.jsx'
+import TabContainer from './TabContainer.jsx'
+import TabContent from './TabContent.jsx'
+import actions from '../reducer/actions.js'
+
+
+/**
+ * This component at the moment only makes tabs for entities.
+ * In the future, it should be extended for other kinds of tabs 
+ * (settings forms etc.).
+ */
+
+function mapDispatchToProps(dispatch) {
+  return {
+    selectEntity: (entityPermId) => dispatch(actions.selectEntity(entityPermId)),
+    closeEntity: (e, entityPermId) => {
+      e.stopPropagation()
+      dispatch(actions.closeEntity(entityPermId))
+    }
+  }
+}
+
+
+function mapStateToProps(state) {
+  const selectedEntity = state.openEntities.selectedEntity
+  const spaces = state.database.spaces
+  return {
+    openEntities: state.openEntities.entities
+      .filter(permId => permId in spaces)
+      .map(permId => spaces[permId]),
+    selectedEntity: selectedEntity,
+    dirtyEntities: state.dirtyEntities,
+  }
+}
+
+
+class TabPanel extends React.Component {
+
+  render() {
+    if (this.props.openEntities.length === 0) {
+      return null
+    }
+    return (
+      <TabContainer selectedKey={this.props.selectedEntity}>
+        {
+          this.props.openEntities.map(entity => {
+            return(
+              <TabContent
+                key={entity.permId.permId}
+                name={entity.code}
+                dirty={this.props.dirtyEntities.indexOf(entity.permId.permId) > -1}
+                onSelect={() => {this.props.selectEntity(entity.permId.permId)}}
+                onClose={(e) => {this.props.closeEntity(e, entity.permId.permId)}}
+              >
+                <EntityDetails 
+                  entity={entity} 
+                />
+              </TabContent>
+            )
+          })
+        }
+      </TabContainer>
+    )
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(TabPanel)
diff --git a/openbis_ng_ui/react/src/components/TopBar.jsx b/openbis_ng_ui/react/src/components/TopBar.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..bae126a64c5994f36cc35cefd1424fd4758b75fe
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/TopBar.jsx
@@ -0,0 +1,78 @@
+import React from 'react'
+import AppBar from '@material-ui/core/AppBar'
+import Toolbar from '@material-ui/core/Toolbar'
+import InputAdornment from '@material-ui/core/InputAdornment'
+import TextField from '@material-ui/core/TextField'
+import SearchIcon from '@material-ui/icons/Search'
+import { withStyles } from '@material-ui/core/styles'
+import Grid from '@material-ui/core/Grid'
+import Button from '@material-ui/core/Button'
+import Hidden from '@material-ui/core/Hidden'
+import LogoutIcon from '@material-ui/icons/PowerSettingsNew'
+
+
+const styles = theme => ({
+  searchField: {
+    backgroundColor: 'white',
+    width: '100%'
+  },
+  leftIcon: {
+    marginRight: theme.spacing.unit,
+  },
+  grid: {
+    minWidth: 0
+  }
+})
+
+class TopBar extends React.Component {
+
+  render() {
+    const classes = this.props.classes
+
+    return (
+      <AppBar position='static'>
+        <Toolbar>
+          <Grid container alignItems='center' spacing={8}>
+
+            <Grid item xs className={ classes.grid }>
+              <TextField
+                className = { classes.searchField }
+                InputProps={{
+                  startAdornment: (
+                    <InputAdornment position="start">
+                      <SearchIcon />
+                    </InputAdornment>
+                  ),
+                }}/>
+            </Grid> 
+
+            <Grid item>
+              <Button
+                variant="contained" 
+                color="primary">
+                <SearchIcon className={ classes.leftIcon } />
+                <Hidden mdUp>
+                  Adv.
+                </Hidden>
+                <Hidden smDown>
+                  Adv. search
+                </Hidden>
+              </Button>
+            </Grid>
+
+            <Grid item>
+              <Button
+                variant="contained" 
+                color="primary">
+                <LogoutIcon />
+              </Button>
+            </Grid>
+  
+          </Grid>
+        </Toolbar>
+      </AppBar>
+    )
+  }
+}
+
+export default withStyles(styles)(TopBar)
diff --git a/openbis_ng_ui/react/src/components/database/EntityDetails.jsx b/openbis_ng_ui/react/src/components/database/EntityDetails.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..0a7e97e641911f552642b3ae05d281c829fe6320
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/database/EntityDetails.jsx
@@ -0,0 +1,148 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import { withStyles } from '@material-ui/core/styles'
+import Button from '@material-ui/core/Button'
+import Card from '@material-ui/core/Card'
+import CardHeader from '@material-ui/core/CardHeader'
+import CardContent from '@material-ui/core/CardContent'
+import CardActions from '@material-ui/core/CardActions'
+import Avatar from '@material-ui/core/Avatar'
+import IconButton from '@material-ui/core/IconButton'
+import FavoriteIcon from '@material-ui/icons/Favorite'
+import ShareIcon from '@material-ui/icons/Share'
+import ExpandMoreIcon from '@material-ui/icons/ExpandMore'
+import TextField from '@material-ui/core/TextField'
+import PropTypes from 'prop-types'
+import _ from 'lodash'
+
+import OpenBISTable from './OpenBISTable.jsx'
+import actions from '../../reducer/actions.js'
+
+
+/* eslint-disable-next-line no-unused-vars */
+const styles = theme => ({
+  textField: {
+    width: '100%'
+  }
+})
+
+function mapStateToProps(state) {
+  return {
+    dirtyEntities: state.dirtyEntities,    
+  }
+}
+
+function mapDispatchToProps(dispatch) {
+  return {
+    setDirty: (entity, dirty) => dispatch(actions.setDirty(entity.permId.permId, dirty)),
+    saveEntity: (entity) => dispatch(actions.saveEntity(entity)),
+  }
+}
+
+class EntityDetails extends React.Component {
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      description: this.props.entity ? this.props.entity.description : '',
+      dirty: false
+    }
+  }
+
+  matches(value1, value2) {
+    if (value1 === null || value1.length === 0) {
+      return value2 === null || value2.length === 0
+    }
+    return value1 === value2
+  }
+
+  handleChange(name, e) {
+    const currentlyDirty = !this.matches(e.target.value, this.props.entity.description)
+    const stateDirty = this.props.dirtyEntities.indexOf(this.props.entity.permId.permId) > -1
+    if (currentlyDirty !== stateDirty) {
+      this.props.setDirty(this.props.entity, currentlyDirty)
+    }
+    this.setState({
+      description: e.target.value,
+      dirty: currentlyDirty
+    })
+  }
+
+  handleSave(entity) {
+    const entityCopy = _.cloneDeep(entity)
+    entityCopy.description = this.state.description
+    this.props.saveEntity(entityCopy)
+  }
+
+  render() {
+    const { classes } = this.props
+    if (this.props.entity === null) {
+      return (<div />)
+    }
+    const entity = this.props.entity
+
+    const options = { 
+      weekday: 'long', 
+      year: 'numeric', 
+      month: 'long', 
+      day: 'numeric', 
+      hour: '2-digit',
+      minute: '2-digit',
+      second: '2-digit'
+    }
+    const created = new Date(entity.registrationDate).toLocaleDateString('en-US', options)
+
+    return(
+      <Card className={classes.card}>
+        <CardHeader
+          avatar={
+            <Avatar aria-label="Recipe" className={classes.avatar}>
+              S
+            </Avatar>
+          }
+          action={
+            <Button 
+              color="primary" 
+              disabled={!this.state.dirty} 
+              variant="contained"
+              onClick={ () => this.handleSave(this.props.entity) }>
+              Save
+            </Button>
+          }
+          title={ 'Space '+entity.code }
+          subheader={ 'Created on ' + created }
+        />
+        <CardContent>
+          <TextField
+            label='Description'
+            className={classes.textField}
+            value={this.state.description ? this.state.description : ''}
+            onChange={ (e) => {
+              this.handleChange('description', e)
+            }}
+            margin='normal'
+          />
+          <OpenBISTable />
+
+        </CardContent>
+        <CardActions disableActionSpacing>
+          <IconButton aria-label="Add to favorites">
+            <FavoriteIcon />
+          </IconButton>
+          <IconButton aria-label="Share">
+            <ShareIcon />
+          </IconButton>
+          <IconButton>
+            <ExpandMoreIcon />
+          </IconButton>
+        </CardActions>
+      </Card>
+    )
+  }
+}
+
+EntityDetails.propTypes = {
+  entity: PropTypes.any.isRequired,
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(EntityDetails))
diff --git a/openbis_ng_ui/react/src/components/database/OpenBISTable.jsx b/openbis_ng_ui/react/src/components/database/OpenBISTable.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..7dd715bfc412dac940643756706a41c3952feee5
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/database/OpenBISTable.jsx
@@ -0,0 +1,119 @@
+import React from 'react'
+import { connect } from 'react-redux'
+import { withStyles } from '@material-ui/core/styles'
+import Table from '@material-ui/core/Table'
+import TableBody from '@material-ui/core/TableBody'
+import TableCell from '@material-ui/core/TableCell'
+import TableHead from '@material-ui/core/TableHead'
+import TableRow from '@material-ui/core/TableRow'
+import Paper from '@material-ui/core/Paper'
+import TableFooter from '@material-ui/core/TableFooter'
+import TablePagination from '@material-ui/core/TablePagination'
+import TableSortLabel from '@material-ui/core/TableSortLabel'
+import InputAdornment from '@material-ui/core/InputAdornment'
+import TextField from '@material-ui/core/TextField'
+import FilterIcon from '@material-ui/icons/FilterList'
+import SettingsIcon from '@material-ui/icons/Settings'
+
+import OpenBISTableRow from './OpenBISTableRow.jsx'
+import actions from '../../reducer/actions.js'
+
+/* eslint-disable-next-line no-unused-vars */
+const styles = theme => ({
+  table: {
+    marginTop: 30,
+    overflowX: 'auto'
+  },
+  headerCell: {
+    backgroundColor: theme.palette.grey[300],
+  }
+})
+
+function mapStateToProps(state) {
+  const table = state.database.table
+  return {
+    spaces: state.database.spaces,
+    columns: table.columns,
+    data: table.data,
+    page: table.page,
+    sortColumn: table.sortColumn,
+    sortDirection: table.sortDirection,
+    filter: table.filter
+  }
+}
+
+function mapDispatchToProps(dispatch) {
+  return {
+    selectEntity: e => dispatch(actions.selectEntity(e)),
+    changePage: page => dispatch(actions.changePage(page)),
+    sortBy: column => dispatch(actions.sortBy(column)),
+    setFilter: filter => dispatch(actions.setFilter(filter))
+  }
+}
+
+class OpenBISTable extends React.Component {
+
+  render() {
+    const { data, columns, classes, page } = this.props
+    return (
+      <Paper className={classes.table}>
+        <Table padding='checkbox'>
+          <TableHead>
+            <TableRow>
+              <TableCell className={classes.headerCell} variant='head'/>
+              { Object.entries(columns).map(([column, type]) => 
+                <TableCell key={column} className={classes.headerCell} numeric={type === 'int'} variant='head'>
+                  <TableSortLabel
+                    className={classes.headerCell}
+                    active={ this.props.sortColumn === column }
+                    direction={ this.props.sortDirection }
+                    onClick={() => this.props.sortBy(column)} >
+                    { column }
+                  </TableSortLabel>
+                </TableCell>
+              )}
+            </TableRow>
+          </TableHead>
+          <TableBody>
+            {
+              data.slice(page * 10, page * 10 + 10).map(row => 
+                <OpenBISTableRow key = { row.Id } row = { row } columns = { columns } />
+              )
+            }
+          </TableBody>
+          <TableFooter className={classes.headerCell}>
+            <TableRow>
+              <TableCell>
+                <div 
+                  style = {{ cursor: 'pointer' }}>
+                  <SettingsIcon/>
+                </div>
+              </TableCell>
+              <TableCell colSpan={2}>
+                <TextField
+                  onChange = { e => this.props.setFilter(e.target.value) }
+                  placeholder = "filter rows by value"
+                  InputProps={{
+                    startAdornment: (
+                      <InputAdornment position="start" variant="outlined">
+                        <FilterIcon />
+                      </InputAdornment>
+                    ),
+                  }}/>
+              </TableCell>
+              <TablePagination
+                count={data.length}
+                rowsPerPage={10}
+                rowsPerPageOptions={[10]}
+                page={page}
+                onChangePage={(_, page) => this.props.changePage(page)}
+              />
+            </TableRow>
+          </TableFooter>
+        </Table>
+      </Paper>
+    )
+  }  
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(withStyles(styles)(OpenBISTable))
diff --git a/openbis_ng_ui/react/src/components/database/OpenBISTableRow.jsx b/openbis_ng_ui/react/src/components/database/OpenBISTableRow.jsx
new file mode 100644
index 0000000000000000000000000000000000000000..57cc32ac4ec7d7f3535907704cd6a7e763d997b2
--- /dev/null
+++ b/openbis_ng_ui/react/src/components/database/OpenBISTableRow.jsx
@@ -0,0 +1,93 @@
+import React from 'react'
+import ReactDOM from 'react-dom'
+import { connect } from 'react-redux'
+import TableCell from '@material-ui/core/TableCell'
+import TableRow from '@material-ui/core/TableRow'
+import { DragSource } from 'react-dnd'
+import { DropTarget } from 'react-dnd'
+import DragHandle from '@material-ui/icons/DragHandle'
+
+import actions from '../../reducer/actions.js'
+
+
+function targetCollect(connect, monitor) {
+  return {
+    connectDropTarget: connect.dropTarget()
+  }
+}
+
+function sourceCollect(connect, monitor) {
+  return {
+    connectDragSource: connect.dragSource(),
+    isDragging: monitor.isDragging(),
+    connectDragPreview: connect.dragPreview()
+  }
+}
+
+const source = {
+  beginDrag(props, monitor, component) {
+    return {
+      id: props.row.Id
+    }
+  },
+  endDrag(props, monitor, component) {
+    monitor.getDropResult().fire()
+  }
+}
+
+const target = {
+  drop(props, monitor, component) {
+    return {
+      source: monitor.getItem(),
+      target: {
+        id: props.row.Id
+      },
+      fire: () => props.moveEntity(monitor.getItem().id, props.row.Id)
+    }
+  }
+}
+
+function mapStateToProps(state) {
+  return {
+    spaces: state.database.spaces,
+  }
+}
+
+function mapDispatchToProps(dispatch) {
+  return {
+    selectEntity: e => dispatch(actions.selectEntity(e)),
+    moveEntity: (source, target) => dispatch(actions.moveEntity(source, target))
+  }
+}
+
+class OpenBISTableRow extends React.Component {
+
+  constructor(props) {
+    super(props)
+  }
+
+  render() {
+    const row = this.props.row
+    return (
+      <TableRow 
+        key ={ row.Id }
+        ref = { instance => {
+          this.props.connectDragPreview(ReactDOM.findDOMNode(instance)) 
+          this.props.connectDropTarget(ReactDOM.findDOMNode(instance)) 
+        }}>
+        <TableCell>
+          <div 
+            style = {{ cursor: 'pointer' }}
+            ref = { instance => this.props.connectDragSource(ReactDOM.findDOMNode(instance)) }>
+            <DragHandle/>
+          </div>
+        </TableCell>
+        { Object.entries(this.props.columns).map(([col, type]) =>
+          <TableCell key={col} numeric={type === 'int'}> { row[col] }</TableCell>
+        )}
+      </TableRow>
+    )
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(DropTarget('row', target, targetCollect)(DragSource('row', source, sourceCollect)(OpenBISTableRow)))
diff --git a/openbis_ng_ui/react/src/index.js b/openbis_ng_ui/react/src/index.js
new file mode 100644
index 0000000000000000000000000000000000000000..0c7c99656e61395792cb7afe66225cce6b9bf4d1
--- /dev/null
+++ b/openbis_ng_ui/react/src/index.js
@@ -0,0 +1,37 @@
+/* eslint-disable */
+import "regenerator-runtime/runtime"
+import React from 'react'
+import ReactDOM from 'react-dom'
+import { createStore, applyMiddleware, compose } from 'redux'
+import { Provider } from 'react-redux'
+import createSagaMiddleware from 'redux-saga'
+import reducer from './reducer/reducer.js'
+import { watchInit, watchSetSpaces, watchSaveEntity, watchExpandNode } from './reducer/sagas'
+
+const sagaMiddleware = createSagaMiddleware()
+const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
+export const store = createStore(reducer, composeEnhancers(applyMiddleware(sagaMiddleware)))
+
+sagaMiddleware.run(watchInit)
+sagaMiddleware.run(watchSetSpaces)
+sagaMiddleware.run(watchSaveEntity)
+sagaMiddleware.run(watchExpandNode)
+
+const render = () => {
+  const App = require('./components/App.jsx').default
+  ReactDOM.render(
+    <Provider store = { store }>
+      <App />
+    </Provider>,
+    document.getElementById("app")
+  )
+}
+
+if (module.hot) {
+  module.hot.accept('./components/App.jsx', () => setTimeout(render))  
+  module.hot.accept('./reducer/reducer.js', () => {
+    const nextRootReducer = require('./reducer/reducer.js').default
+    store.replaceReducer(nextRootReducer)
+  });
+}
+render()
\ No newline at end of file
diff --git a/openbis_ng_ui/react/src/reducer/actions.js b/openbis_ng_ui/react/src/reducer/actions.js
new file mode 100644
index 0000000000000000000000000000000000000000..213bcd005f8b4f9b6482d00e9cdcc59a23d3fa95
--- /dev/null
+++ b/openbis_ng_ui/react/src/reducer/actions.js
@@ -0,0 +1,50 @@
+export default {
+  // TODO setDirty for generic tabs
+  setDirty: (entityPermId, dirty) => ({
+    type: 'SET-DIRTY',
+    entityPermId: entityPermId,
+    dirty: dirty
+  }),
+  expandNode: (node) => ({
+    type: 'EXPAND-NODE',
+    node: node
+  }),
+  collapseNode: (node) => ({
+    type: 'COLLAPSE-NODE',
+    node: node
+  }),
+  // database stuff
+  setSpaces: spaces => ({
+    type: 'SET-SPACES',
+    spaces: spaces,
+  }),
+  selectEntity: entityPermId => ({
+    type: 'SELECT-ENTITY',
+    entityPermId: entityPermId
+  }),
+  closeEntity: entityPermId => ({
+    type: 'CLOSE-ENTITY',
+    entityPermId: entityPermId
+  }),
+  changePage: page => ({
+    type: 'CHANGE-PAGE',
+    page: page
+  }),
+  sortBy: column => ({
+    type: 'SORT-BY',
+    column: column
+  }),
+  setFilter: filter => ({
+    type: 'SET-FILTER',
+    value: filter
+  }),
+  moveEntity: (source, target) => ({
+    type: 'MOVE-ENTITY',
+    source: source,
+    target: target
+  }),
+  saveEntity: (entity) => ({
+    type: 'SAVE-ENTITY',
+    entity: entity
+  }),
+}
diff --git a/openbis_ng_ui/react/src/reducer/database/reducer.js b/openbis_ng_ui/react/src/reducer/database/reducer.js
new file mode 100644
index 0000000000000000000000000000000000000000..345a5a270a5423e927820fbb72919a93ac159d41
--- /dev/null
+++ b/openbis_ng_ui/react/src/reducer/database/reducer.js
@@ -0,0 +1,145 @@
+import initialState from '../initialstate.js'
+
+
+function filterOf(filter, columns) {
+  if (filter == null || filter.length === 0) {
+    return _ => true
+  } else { 
+    return entity => 
+      Object.keys(columns)
+        .map(col => entity[col])
+        .map(value => value != null && value.toString().includes(filter))
+        .includes(true)
+  }
+}
+
+function sortOf(orderColumn, direction, columns) {
+  return (a, b) => {
+    let valA = a[orderColumn]
+    let valB = b[orderColumn]
+    if (valA == null && valB == null) {
+      return 0
+    }
+    if (valA == null) {
+      return 1
+    }
+    if (valB == null) {
+      return -1
+    }
+    const type = columns[orderColumn]
+    if (type === 'int') {
+      return direction === 'asc' ? valA - valB : valB - valA
+    } else {
+      if (valA < valB){
+        return direction === 'asc' ? -1 : 1
+      } else if (valA > valB) {
+        return direction === 'asc' ? 1 : -1
+      } else {
+        return 0
+      }    
+    }
+  }
+}
+
+function transformData(data, columns, filter, orderColumn, direction) {
+  const filtered = data.filter(filterOf(filter, columns))
+  if (orderColumn in columns) {
+    return filtered.sort(sortOf(orderColumn, direction, columns))
+  } else {
+    return filtered
+  }
+}
+
+const concat = (...arrays) => [].concat(...arrays)
+const take = (array, n) => array.slice(0, n)
+const drop = (array, n) => array.slice(n)
+const remove = (array, index) => [...take(array, index), ...drop(array, index + 1)]
+
+function move (array, oldIndex, newIndex) {
+  const value = array[oldIndex]
+  const removed = remove(array, oldIndex)
+  return concat(take(removed, newIndex), [value], drop(removed, newIndex))
+}
+
+
+function entitiesByPermId(entities) {
+  const byPermId = {}
+  for (let entity of entities) {
+    byPermId[entity.permId.permId] = entity
+  }
+  return byPermId
+}
+
+
+function spaces(spaces = initialState.spaces, action) {
+  switch (action.type) {
+  case 'SET-SPACES': {
+    return entitiesByPermId(action.spaces)
+  }
+  case 'SAVED-ENTITY': {
+    const newSpaces = Object.assign({}, spaces)
+    newSpaces[action.entity.permId.permId] = action.entity
+    return newSpaces
+  }
+  default: {
+    return spaces
+  }}
+}
+
+
+function projects(projects = initialState.projects, action) {
+  switch (action.type) {
+  case 'SET-PROJECTS': {
+    return  entitiesByPermId(action.projects)
+  }
+  default: {
+    return projects
+  }}
+}
+
+
+function table(table = initialState.table, action) {
+  switch (action.type) {
+  case 'CHANGE-PAGE': {
+    return Object.assign({}, table, { page: action.page })
+  }  
+  case 'SORT-BY': {
+    const column = action.column
+    const direction = 
+      column === table.sortColumn 
+        ? table.sortDirection === 'asc' ? 'desc' : 'asc' 
+        : 'asc'
+    return Object.assign({}, table, {
+      sortColumn: column,
+      sortDirection: direction,
+      page: 0,
+      data: transformData(table.initialData, table.columns, table.filter, column, direction)
+    })
+  }
+  case 'SET-FILTER': {
+    return Object.assign({}, table, {
+      page: 0,
+      filter: action.value,
+      data: transformData(table.initialData, table.columns, action.value, table.sortColumn, table.sortDirection)
+    })
+  }
+  case 'MOVE-ENTITY': {
+    const sourceIndex = table.data.findIndex(e => e.Id === action.source)
+    const targetIndex = table.data.findIndex(e => e.Id === action.target)
+    return Object.assign({}, table, {
+      data: move(table.data, sourceIndex, targetIndex)
+    })
+  }
+  default: {
+    return table
+  }}
+}
+
+
+export default function database(database = initialState.database, action) {
+  return {
+    spaces: spaces(database.spaces, action),
+    projects: projects(database.projects, action),
+    table: table(database.table, action),
+  }
+}
diff --git a/openbis_ng_ui/react/src/reducer/initialstate.js b/openbis_ng_ui/react/src/reducer/initialstate.js
new file mode 100644
index 0000000000000000000000000000000000000000..14fc0455a5083b048ccc5a4b5b0b53feb56f8908
--- /dev/null
+++ b/openbis_ng_ui/react/src/reducer/initialstate.js
@@ -0,0 +1,73 @@
+
+function foldLeft(array, acc, reducer) {
+  if (array.length === 0) {
+    return acc
+  } else {
+    const [head, ...tail] = array
+    return foldLeft(tail, reducer(acc, head), reducer)
+  }
+}
+
+const take = (array, n) => array.slice(0, n)
+
+//const stringColumns = ['Description', 'Color', 'Address', 'Details', 'Info', 'Additonals', 'Other', 'Note', 'Optional', 'Something']
+//const intColumns = ['Weight', 'Length', 'Width', 'Number', 'Amount', 'Depth', 'Temperature', 'Volume', 'Height', 'Opacity']
+
+const stringColumns = ['Description', 'Color', 'Address']
+const intColumns = ['Weight', 'Length', 'Width']
+
+
+function shuffle(arr) {
+  return arr.slice(0).sort((a, b) => Math.random() * 3 - 1.5)
+}
+
+function randomString() {
+  return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15)
+}
+
+function createEntity(id) {
+  const numStrings = Math.random()  * 3
+  const numInts = Math.random()  * 3
+  let entity = { Id: id }
+  entity = foldLeft(take(shuffle(stringColumns), numStrings), entity, (acc, a) => Object.assign({}, acc, { [a] : randomString() }))
+  entity = foldLeft(take(shuffle(intColumns), numInts), entity, (acc, a) => Object.assign({}, acc, { [a] : Math.floor(Math.random() * 100) }))
+  return entity
+}
+
+const data = [...Array(10000).keys()].map(i => createEntity(i + 1))
+
+let tableColumns = foldLeft(stringColumns, {}, (acc, a) => Object.assign({}, acc, { [a]: 'string'}))
+tableColumns = foldLeft(intColumns, tableColumns, (acc, a) => Object.assign({}, acc, { [a]: 'int'}))
+tableColumns = foldLeft(shuffle(Object.entries(tableColumns)), { Id: 'int' }, (acc, a) => Object.assign({}, acc, { [a[0]] : a[1] }))
+
+// TODO split initialstate
+export default {
+  database: {
+    spaces: {},
+    projects: {},
+    table: {
+      initialData: data,
+      data: data,
+      columns: tableColumns,
+      page: 0,
+      sortColumn: '',
+      sortDirection: 'asc'
+    },
+  },
+
+  types: {},
+  users: {},
+  favourites: {},
+  tools: {},
+
+  // TODO replace with generic tab description
+  openEntities: {
+    entities: [],
+    selectedEntity: null,
+  },
+  dirtyEntities: [],
+
+  loading: true,
+  // TODO generic tree
+  databaseTreeNodes: [],
+}
\ No newline at end of file
diff --git a/openbis_ng_ui/react/src/reducer/reducer.js b/openbis_ng_ui/react/src/reducer/reducer.js
new file mode 100644
index 0000000000000000000000000000000000000000..a88665d184d613c261793832a48a9bbb684fac40
--- /dev/null
+++ b/openbis_ng_ui/react/src/reducer/reducer.js
@@ -0,0 +1,131 @@
+import merge from 'lodash/merge'
+import initialState from './initialstate.js'
+import database from './database/reducer.js'
+
+
+function replaceNode(nodes, newNode) {
+  return nodes.map( node => {
+    if (node.id === newNode.id) {
+      return newNode
+    }
+    return node
+  })
+}
+
+function asTreeNode(entity) {
+  return {
+    id: entity.permId.permId,
+    type: entity['@type'],
+    expanded: false,
+    loading: false,
+    loaded: false,
+    children: [],
+  }
+}
+
+// reducers
+
+function loading(loading = initialState.loading, action) {
+  switch (action.type) {
+  case 'SET-SPACES': {
+    return false
+  }
+  case 'SAVE-ENTITY': {
+    return true
+  }
+  case 'SAVED-ENTITY': {
+    return false
+  }
+  default: {
+    return loading
+  }}
+}
+
+
+function databaseTreeNodes(databaseTreeNodes = initialState.databaseTreeNodes, action) {
+  switch (action.type) {
+  case 'SET-SPACES': {
+    return action.spaces.map(asTreeNode)
+  }
+  case 'SET-PROJECTS': {
+    const oldNode = databaseTreeNodes.filter( node => node.id === action.spacePermId )[0]
+    const projectNodes = action.projects.map(asTreeNode)
+    const node = merge({}, oldNode, { loading: false, loaded: true, children: projectNodes })
+    return replaceNode(databaseTreeNodes, node)
+  }
+  case 'EXPAND-NODE': {
+    const loading = action.node.loaded === false
+    const node = merge({}, action.node, { expanded: true, loading: loading })
+    return replaceNode(databaseTreeNodes, node)
+  }
+  case 'COLLAPSE-NODE': {
+    const node = merge({}, action.node, { expanded: false })
+    return replaceNode(databaseTreeNodes, node)
+  }
+  default: {
+    return databaseTreeNodes
+  }}
+}
+
+function openEntities(openEntities = initialState.openEntities, action) {
+  switch (action.type) {
+  case 'SELECT-ENTITY': {
+    const entities = openEntities.entities
+    return {
+      entities: entities.indexOf(action.entityPermId) > -1 ? entities : [].concat(entities, [action.entityPermId]),
+      selectedEntity: action.entityPermId,
+    }
+  }
+  case 'CLOSE-ENTITY': {
+    const newOpenEntities = openEntities.entities.filter(e => e !== action.entityPermId)
+    if (openEntities.selectedEntity === action.entityPermId) {
+      const oldIndex = openEntities.entities.indexOf(action.entityPermId)
+      const newIndex = oldIndex === newOpenEntities.length ? oldIndex - 1 : oldIndex
+      const selectedEntity = newIndex > -1 ? newOpenEntities[newIndex] : null  
+      return {
+        entities: newOpenEntities,
+        selectedEntity: selectedEntity,
+      }
+    } else {
+      return {
+        entities: newOpenEntities,
+        selectedEntity: openEntities.selectedEntity,
+      }
+    }  
+  }
+  default: {
+    return openEntities
+  }}
+}
+
+function dirtyEntities(dirtyEntities = initialState.dirtyEntities, action) {
+  switch (action.type) {
+  case 'SET-DIRTY': {
+    if (action.dirty) {
+      return [].concat(dirtyEntities, [action.entityPermId])
+    } else {
+      return dirtyEntities.filter(e => e !== action.entityPermId)
+    }
+  }
+  case 'SAVED-ENTITY': {
+    return dirtyEntities.filter(permId => permId !== action.entity.permId.permId)
+  }
+  case 'CLOSE-ENTITY': {
+    return dirtyEntities.filter(permId => permId !== action.entityPermId)
+  }
+  default: {
+    return dirtyEntities
+  }}
+}
+
+function reducer(state = initialState, action) {
+  return {
+    database: database(state.database, action),
+    loading: loading(state.loading, action),
+    databaseTreeNodes: databaseTreeNodes(state.databaseTreeNodes, action),
+    openEntities: openEntities(state.openEntities, action),
+    dirtyEntities: dirtyEntities(state.dirtyEntities, action),
+  }
+}
+
+export default reducer
diff --git a/openbis_ng_ui/react/src/reducer/sagas.js b/openbis_ng_ui/react/src/reducer/sagas.js
new file mode 100644
index 0000000000000000000000000000000000000000..45c47e4897a3ed9e1a234e71393a35115fd7686c
--- /dev/null
+++ b/openbis_ng_ui/react/src/reducer/sagas.js
@@ -0,0 +1,46 @@
+import { put, takeEvery, call } from 'redux-saga/effects'
+import openbis from '../services/openbis'
+
+// TODO split sagas
+
+function* init() {
+  const spaces = yield call(openbis.getSpaces)
+  yield put({ type: 'SET-SPACES', spaces: spaces })
+}
+
+export function* watchInit() {
+  yield takeEvery('INIT', init)
+}
+
+function* selectSpace(action) {
+  yield put({ type: 'SELECT-ENTITY', entityPermId: action.spaces[0].permId.permId })
+}
+
+export function* watchSetSpaces() {
+  yield takeEvery('SET-SPACES', selectSpace)
+}
+
+function* saveEntity(action) {
+  yield openbis.updateSpace(action.entity.permId, action.entity.description)
+  const spaces = yield call(openbis.getSpaces)
+  const space = spaces.filter(space => space.permId.permId === action.entity.permId.permId)[0]
+  yield put({ type: 'SAVED-ENTITY', entity: space })
+}
+
+export function* watchSaveEntity() {
+  yield takeEvery('SAVE-ENTITY', saveEntity)
+}
+
+function* expandNode(action) {
+  const node = action.node
+  if (node.loaded === false) {
+    if (node.type === 'as.dto.space.Space') {
+      const projects = yield openbis.searchProjects(node.id)
+      yield put({ type: 'SET-PROJECTS', projects: projects, spacePermId: node.id })
+    }
+  }
+}
+
+export function* watchExpandNode() {
+  yield takeEvery('EXPAND-NODE', expandNode)
+}
diff --git a/openbis_ng_ui/react/src/services/openbis.js b/openbis_ng_ui/react/src/services/openbis.js
new file mode 100644
index 0000000000000000000000000000000000000000..0fed4df3db922212e5bcf12e6c76ac488a458cbe
--- /dev/null
+++ b/openbis_ng_ui/react/src/services/openbis.js
@@ -0,0 +1,55 @@
+import { store } from '../index.js'
+
+let v3 = null
+/* eslint-disable-next-line no-undef */
+requirejs([ 'openbis' ], openbis => {
+  v3 = new openbis()
+  v3.login('admin', 'password').done(() => store.dispatch({type: 'INIT'}))
+})
+
+function getSpaces() {
+  return new Promise( (resolve, reject) => {
+    /* eslint-disable-next-line no-undef */
+    requirejs(
+      ['as/dto/space/search/SpaceSearchCriteria', 'as/dto/space/fetchoptions/SpaceFetchOptions' ], 
+      (SpaceSearchCriteria, SpaceFetchOptions) => 
+        v3.searchSpaces(new SpaceSearchCriteria(), new SpaceFetchOptions()).done(res => resolve(res.getObjects()))
+    )
+  })
+}
+
+function updateSpace(permId, description) {
+  return new Promise( (resolve, reject) => {
+    /* eslint-disable-next-line no-undef */
+    requirejs(
+      ['as/dto/space/update/SpaceUpdate'], 
+      (SpaceUpdate) => {
+        let spaceUpdate = new SpaceUpdate()
+        spaceUpdate.setSpaceId(permId)
+        spaceUpdate.setDescription(description)
+        v3.updateSpaces([spaceUpdate]).done(res => resolve(res))
+      }
+    )
+  })
+}
+
+function searchProjects(spacePermId) {
+  return new Promise( (resolve, reject) => {
+    /* eslint-disable-next-line no-undef */
+    requirejs(
+      ['as/dto/project/search/ProjectSearchCriteria', 'as/dto/project/fetchoptions/ProjectFetchOptions'], 
+      (ProjectSearchCriteria, ProjectFetchOptions) => {
+        let searchCriteria = new ProjectSearchCriteria()
+        searchCriteria.withSpace().withPermId().thatEquals(spacePermId)
+        let fetchOptions = new ProjectFetchOptions()
+        v3.searchProjects(searchCriteria, fetchOptions).done(res => resolve(res.getObjects()))
+      }
+    )
+  })
+}
+
+export default {
+  getSpaces: getSpaces,
+  updateSpace: updateSpace,
+  searchProjects: searchProjects
+}
diff --git a/openbis_ng_ui/react/webpack.config.dev.js b/openbis_ng_ui/react/webpack.config.dev.js
new file mode 100644
index 0000000000000000000000000000000000000000..f91123fea8c2efed5eada224b76df33ee291c554
--- /dev/null
+++ b/openbis_ng_ui/react/webpack.config.dev.js
@@ -0,0 +1,61 @@
+/* eslint-disable */
+const HtmlWebpackPlugin = require('html-webpack-plugin')
+
+module.exports = {
+  entry: './src/index.js',
+  output: {
+    path: __dirname + '/build/npm-build/',
+    filename: 'bundle.js'
+  },
+  
+  devServer: {
+    contentBase: "./src",
+    hot: true, 
+    https: false,
+    proxy: {
+      "/openbis": {
+        "target": 'https://localhost:8122',
+        "changeOrigin": true,
+        "secure": false
+      }
+    }    
+  },
+
+  devtool: "source-map",
+
+  mode: 'development',
+
+  module: {
+    rules: [
+      {
+        test: /\.(js|jsx)$/,
+        exclude: /node_modules/,
+        use: {
+          loader: "babel-loader"
+        }
+      },
+      {
+        test: /\.(css)$/,
+        use: [
+          'style-loader',
+          'css-loader'
+        ]
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000
+        }
+      }
+    ]
+  },
+
+  plugins: [
+    new HtmlWebpackPlugin({
+      inject: 'body',
+      filename: './index.html',
+      template: './index.html'
+    })
+  ]
+};
diff --git a/openbis_ng_ui/react/webpack.config.js b/openbis_ng_ui/react/webpack.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..26566999acd67f245e879931d3daa9edf27d2843
--- /dev/null
+++ b/openbis_ng_ui/react/webpack.config.js
@@ -0,0 +1,46 @@
+/* eslint-disable */
+const HtmlWebpackPlugin = require('html-webpack-plugin');
+
+module.exports = {
+  entry: './src/index.js',
+  output: {
+    path: __dirname + '/build/',
+    filename: 'bundle.[hash].js'
+  },
+  
+  mode: 'production',
+
+  module: {
+    rules: [
+      {
+        test: /\.(js|jsx)$/,
+        exclude: /node_modules/,
+        use: {
+          loader: "babel-loader"
+        }
+      },
+      {
+        test: /\.(css)$/,
+        use: [
+          'style-loader',
+          'css-loader'
+        ]
+      },
+      {
+        test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
+        loader: 'url-loader',
+        options: {
+          limit: 10000
+        }
+      }
+    ]
+  },
+
+  plugins: [
+    new HtmlWebpackPlugin({
+      inject: 'body',
+      filename: './index.html',
+      template: './index.html'
+    })
+  ]
+};
diff --git a/openbis_ng_ui/settings.gradle b/openbis_ng_ui/settings.gradle
new file mode 100644
index 0000000000000000000000000000000000000000..94b3605cf52df33a56e9df92d4c4c5c7b75fe774
--- /dev/null
+++ b/openbis_ng_ui/settings.gradle
@@ -0,0 +1,2 @@
+include 'react'
+
diff --git a/openbis_standard_technologies/build.gradle b/openbis_standard_technologies/build.gradle
index b031c41ff62b8d1055fc204a8c8bba385b94e501..10296d8abf578d74c193d852b44572651703076b 100644
--- a/openbis_standard_technologies/build.gradle
+++ b/openbis_standard_technologies/build.gradle
@@ -39,6 +39,7 @@ evaluationDependsOn(':rtd_yeastx')
 evaluationDependsOn(':deep_sequencing_unit')
 evaluationDependsOn(':plasmid')
 evaluationDependsOn(':big_data_link_server')
+evaluationDependsOn(':openbis_ng_ui')
 
 
 apply from: '../gradle/javaproject.gradle'
@@ -452,6 +453,17 @@ task clientsAndApis(type: Zip, dependsOn: [dssClientZip, queryApiZip, apiV3Zip,
   }
 }
 
+task deleteOpenbisNgUi(type: Delete) {
+  delete 'dist/core-plugins/openbis-ng-ui/1/as/webapps/openbis-ng-ui/html'
+}
+
+task copyOpenbisNgUiToCorePlugins(type: Copy, dependsOn: [deleteOpenbisNgUi, project(':openbis_ng_ui').tasks.build]) {
+    from project(':openbis_ng_ui').file('react/build')
+    into file('dist/core-plugins/openbis-ng-ui/1/as/webapps/openbis-ng-ui/html')
+}
+
+zipCorePlugins.dependsOn copyOpenbisNgUiToCorePlugins
+
 task generateJavadoc(type: Javadoc) {
 	source = configurations.javadoc_sources.collect { zipTree(it).matching {
 		include "**/ch/ethz/sis/openbis/generic/asapi/**/*.java"
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/StandardProfile.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/StandardProfile.js
index 52cd3406d34145576a820e5304f301d0601419f7..c04e7b62a87bc3d55ec8a4e88d208cd63d3829dd 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/StandardProfile.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/config/StandardProfile.js
@@ -329,18 +329,33 @@ $.extend(StandardProfile.prototype, DefaultProfile.prototype, {
 				var samplesToDelete = null;
 				var changesToDo = null;
 				if((orderStatus === "ORDERED" || orderStatus === "DELIVERED" || orderStatus === "PAID") && !sample.properties["ORDER_STATE"]) {
-					//Set property
-					sample.properties["ORDER_STATE"] = window.btoa(unescape(encodeURIComponent(JSON.stringify(JSON.decycle(sample)))));
-					//Update order state on the requests
-					changesToDo = [];
-					var requests = sample.parents;
-					if(requests) {
-						for(var rIdx = 0; rIdx < requests.length; rIdx++) {
-							changesToDo.push({ "permId" : requests[rIdx].permId, "identifier" : requests[rIdx].identifier, "properties" : {"ORDER_STATUS" : orderStatus } });
-						}
+					//Update parents to hold all info
+    	    				var searchSamples = { entityKind : "SAMPLE", logicalOperator : "OR", rules : {} };
+					for(var pIdx = 0; pIdx < sample.parents.length; pIdx++) {
+						searchSamples.rules["UUIDv4_" + pIdx] = { type : "Attribute", name : "PERM_ID", value : sample.parents[pIdx].permId };
 					}
+					
+					mainController.serverFacade.searchForSamplesAdvanced(searchSamples, { only : true, withProperties : true, withAncestors : true, withAncestorsProperties : true }, function(result) {
+						sample.parents = mainController.serverFacade.getV3SamplesAsV1(result.objects);
+						
+						//Set property
+						sample.properties["ORDER_STATE"] = window.btoa(unescape(encodeURIComponent(JSON.stringify(sample))));
+						
+						//Update order state on the requests
+						changesToDo = [];
+						var requests = sample.parents;
+						if(requests) {
+							for(var rIdx = 0; rIdx < requests.length; rIdx++) {
+								changesToDo.push({ "permId" : requests[rIdx].permId, "identifier" : requests[rIdx].identifier, "properties" : {"ORDER_STATUS" : orderStatus } });
+							}
+						}
+						
+						action(sample, null, samplesToDelete, changesToDo);
+					});
+				} else {
+					action(sample, null, samplesToDelete, changesToDo);
 				}
-				action(sample, null, samplesToDelete, changesToDo);
+				
 			} else if(sample.sampleTypeCode === "REQUEST") {
 				mainController.currentView._newProductsController.createAndAddToForm(sample, action);
 			} else if(action) {
@@ -357,173 +372,278 @@ $.extend(StandardProfile.prototype, DefaultProfile.prototype, {
 			} else if(sampleTypeCode === "ORDER") {
 				var isExisting = mainController.currentView._sampleFormModel.mode === FormMode.VIEW;
 				var isFromState = false;
-				if(isExisting) {
-					//
-					// Data Structures to Help the reports functionality
-					//
-					var order = mainController.currentView._sampleFormModel.sample;
-					if(order.properties["ORDER_STATE"]) {
-						isFromState = true;
-						order = JSON.parse(decodeURIComponent(escape(window.atob(order.properties["ORDER_STATE"]))));
-					}
-					
-					var requests = order.parents;
-					var providerByPermId = {};
-					var productsByProviderPermId = {};
-					var quantityByProductPermId = {};
-					var absoluteTotalByCurrency = {};
-					
-					//
-					// Fills data structures
-					//
-					for(var rIdx = 0; rIdx < requests.length; rIdx++) {
-						var request = requests[rIdx];
-						var requestProductsAnnotations = FormUtil.getAnnotationsFromSample(request);
-						var requestProducts = request.parents;
-						for(var pIdx = 0; pIdx < requestProducts.length; pIdx++) {
-							var requestProduct = requestProducts[pIdx];
-							var requestProductAnnotations = requestProductsAnnotations[requestProduct.permId];
-							
-							if(requestProduct.parents.length === 0) {
-								Util.showUserError("Product " + requestProduct.code + " does not have a provider, FIX IT!.");
-								return;
-							}
-							var provider = requestProduct.parents[0];
-							var providerProducts = productsByProviderPermId[provider.permId];
-							if(!providerProducts) {
-								providerProducts = [];
-								productsByProviderPermId[provider.permId] = providerProducts;
-								providerByPermId[provider.permId] = provider;
-							}
-							var quantity = quantityByProductPermId[requestProduct.permId];
-							if(!quantity) {
-								quantity = 0;
-							}
-							quantity += parseInt(requestProductAnnotations["QUANTITY_OF_ITEMS"]);
-							if(!quantity) {
-								Util.showUserError("Product " + requestProduct.code + " from request " +  request.code + " does not have a quantity, FIX IT!.");
-								return;
-							}
-							var currencyCode = requestProduct.properties["CURRENCY"];
-							if(!currencyCode) {
-								currencyCode = "N/A";
-							}
-							
-							var absoluteTotalForCurrency = absoluteTotalByCurrency[currencyCode];
-							if(!absoluteTotalForCurrency) {
-								absoluteTotalForCurrency = 0;
-							}
-							
-							if(requestProduct.properties["PRICE_PER_UNIT"]) {
-								absoluteTotalForCurrency += parseFloat(requestProduct.properties["PRICE_PER_UNIT"]) * quantity;
+				if(isExisting) {					
+					var showOrderSummary = function(order) {			
+						var requests = order.parents;
+						var providerByPermId = {};
+						var productsByProviderPermId = {};
+						var quantityByProductPermId = {};
+						var absoluteTotalByCurrency = {};
+						
+						//
+						// Fills data structures
+						//
+						for(var rIdx = 0; rIdx < requests.length; rIdx++) {
+							var request = requests[rIdx];
+							var requestProductsAnnotations = FormUtil.getAnnotationsFromSample(request);
+							var requestProducts = request.parents;
+							for(var pIdx = 0; pIdx < requestProducts.length; pIdx++) {
+								var requestProduct = requestProducts[pIdx];
+								var requestProductAnnotations = requestProductsAnnotations[requestProduct.permId];
+								
+								if(requestProduct.parents.length === 0) {
+									Util.showUserError("Product " + requestProduct.code + " does not have a provider, FIX IT!.");
+									return;
+								}
+								var provider = requestProduct.parents[0];
+								var providerProducts = productsByProviderPermId[provider.permId];
+								if(!providerProducts) {
+									providerProducts = [];
+									productsByProviderPermId[provider.permId] = providerProducts;
+									providerByPermId[provider.permId] = provider;
+								}
+								var quantity = quantityByProductPermId[requestProduct.permId];
+								if(!quantity) {
+									quantity = 0;
+								}
+								quantity += parseInt(requestProductAnnotations["QUANTITY_OF_ITEMS"]);
+								if(!quantity) {
+									Util.showUserError("Product " + requestProduct.code + " from request " +  request.code + " does not have a quantity, FIX IT!.");
+									return;
+								}
+								var currencyCode = requestProduct.properties["CURRENCY"];
+								if(!currencyCode) {
+									currencyCode = "N/A";
+								}
+								
+								var absoluteTotalForCurrency = absoluteTotalByCurrency[currencyCode];
+								if(!absoluteTotalForCurrency) {
+									absoluteTotalForCurrency = 0;
+								}
+								
+								if(requestProduct.properties["PRICE_PER_UNIT"]) {
+									absoluteTotalForCurrency += parseFloat(requestProduct.properties["PRICE_PER_UNIT"]) * quantity;
+								}
+								
+								absoluteTotalByCurrency[currencyCode] = absoluteTotalForCurrency;
+								
+								quantityByProductPermId[requestProduct.permId] = quantity;
+								providerProducts.push(requestProduct);
 							}
-							
-							absoluteTotalByCurrency[currencyCode] = absoluteTotalForCurrency;
-							
-							quantityByProductPermId[requestProduct.permId] = quantity;
-							providerProducts.push(requestProduct);
 						}
-					}
-					
-					//
-					// Button that prints an order report
-					//
-					var printOrder = FormUtil.getButtonWithIcon("glyphicon-print",function() {
-						//Create an order page for each provider
-						var orderPages = [];
-						for(var providerPermId in productsByProviderPermId) {
-							var provider = providerByPermId[providerPermId];
-							var preferredSupplierLanguage = provider.properties["COMPANY_LANGUAGE"];
-							
-							var languageLabels = profile.orderLanguage[preferredSupplierLanguage];
-							if(!languageLabels) {
-								languageLabels = profile.orderLanguage["ENGLISH"];
-							}
-							
-							var providerProducts = productsByProviderPermId[providerPermId];
-							
-							var registrationDate = null;
-							if(order.registrationDetails && order.registrationDetails.modificationDate) {
-								registrationDate = order.registrationDetails.modificationDate;
-							} else if(mainController.currentView._sampleFormModel.sample.registrationDetails && 
-									mainController.currentView._sampleFormModel.sample.registrationDetails.modificationDate) {
-								registrationDate = mainController.currentView._sampleFormModel.sample.registrationDetails.modificationDate;
-							}
-							
-							var page = languageLabels["ORDER_FORM"];
-								page += "\n";
-								page += "\n";
-								page += languageLabels["ORDER_INFORMATION"];
-								page += "\n";
-								page += "- " + languageLabels["ORDER_DATE"] + ": " + Util.getFormatedDate(new Date(registrationDate));
-								page += "\n";
-								page += "- " + languageLabels["ORDER_STATUS"] + ": " + order.properties["ORDER_STATUS"];
-								page += "\n";
-								page += "- " + languageLabels["ORDER_CODE"] + ": " + order.code;
-								page += "\n";
-								page += "\n";
-								page += "\n";
-								page += languageLabels["COSTUMER_INFORMATION"];
-								page += "\n";
-								if(order.properties["SHIP_TO"]) {
-									page += "- " + languageLabels["SHIP_TO"] + ": " + order.properties["SHIP_TO"];
-									page += "\n";
+						
+						//
+						// Button that prints an order report
+						//
+						var printOrder = FormUtil.getButtonWithIcon("glyphicon-print",function() {
+							//Create an order page for each provider
+							var orderPages = [];
+							for(var providerPermId in productsByProviderPermId) {
+								var provider = providerByPermId[providerPermId];
+								var preferredSupplierLanguage = provider.properties["COMPANY_LANGUAGE"];
+								
+								var languageLabels = profile.orderLanguage[preferredSupplierLanguage];
+								if(!languageLabels) {
+									languageLabels = profile.orderLanguage["ENGLISH"];
 								}
-								if(order.properties["BILL_TO"]) {
-									page += "- " + languageLabels["BILL_TO"] + ": " + order.properties["BILL_TO"];
-									page += "\n";
+								
+								var providerProducts = productsByProviderPermId[providerPermId];
+								
+								var registrationDate = null;
+								if(order.registrationDetails && order.registrationDetails.modificationDate) {
+									registrationDate = order.registrationDetails.modificationDate;
+								} else if(mainController.currentView._sampleFormModel.sample.registrationDetails && 
+										mainController.currentView._sampleFormModel.sample.registrationDetails.modificationDate) {
+									registrationDate = mainController.currentView._sampleFormModel.sample.registrationDetails.modificationDate;
 								}
-								if(order.properties["SHIP_ADDRESS"]) {
-									page += "- " + languageLabels["SHIP_ADDRESS"] + ": " + order.properties["SHIP_ADDRESS"];
+								
+								var page = languageLabels["ORDER_FORM"];
 									page += "\n";
-								}
-								if(order.properties["CONTACT_PHONE"]) {
-									page += "- " + languageLabels["PHONE"] + ": " + order.properties["CONTACT_PHONE"];
 									page += "\n";
-								}
-								if(order.properties["CONTACT_FAX"]) {
-									page += "- " + languageLabels["FAX"] + ": " + order.properties["CONTACT_FAX"];
+									page += languageLabels["ORDER_INFORMATION"];
 									page += "\n";
-								}
-								page += "\n";
-								page += "\n";
-								page += languageLabels["SUPPLIER_INFORMATION"];
-								page += "\n";
-								if(provider.properties["NAME"]) {
-									page += "- " + languageLabels["SUPPLIER"] + ": " + provider.properties["NAME"];
+									page += "- " + languageLabels["ORDER_DATE"] + ": " + Util.getFormatedDate(new Date(registrationDate));
 									page += "\n";
-								}
-								if(provider.properties["COMPANY_ADDRESS_LINE_1"]) {
-									page += "- " + languageLabels["SUPPLIER_ADDRESS_LINE_1"] + ": " + provider.properties["COMPANY_ADDRESS_LINE_1"]
+									page += "- " + languageLabels["ORDER_STATUS"] + ": " + order.properties["ORDER_STATUS"];
 									page += "\n";
-								}
-								if(provider.properties["COMPANY_ADDRESS_LINE_2"]) {
-									page += "  " + languageLabels["SUPPLIER_ADDRESS_LINE_2"] + "  " + provider.properties["COMPANY_ADDRESS_LINE_2"]
+									page += "- " + languageLabels["ORDER_CODE"] + ": " + order.code;
 									page += "\n";
-								}
-								if(provider.properties["COMPANY_PHONE"]) {
-									page += "- " + languageLabels["SUPPLIER_PHONE"] + ": " + provider.properties["COMPANY_PHONE"];
 									page += "\n";
-								}
-								if(provider.properties["COMPANY_FAX"]) {
-									page += "- " + languageLabels["SUPPLIER_FAX"] + ": " + provider.properties["COMPANY_FAX"];
 									page += "\n";
-								}
-								if(provider.properties["COMPANY_EMAIL"]) {
-									page += "- " + languageLabels["SUPPLIER_EMAIL"] + ": " + provider.properties["COMPANY_EMAIL"];
+									page += languageLabels["COSTUMER_INFORMATION"];
 									page += "\n";
-								}
-								if(provider.properties["CUSTOMER_NUMBER"]) {
-									page += "- " + languageLabels["CUSTOMER_NUMBER"] + ": " + provider.properties["CUSTOMER_NUMBER"];
+									if(order.properties["SHIP_TO"]) {
+										page += "- " + languageLabels["SHIP_TO"] + ": " + order.properties["SHIP_TO"];
+										page += "\n";
+									}
+									if(order.properties["BILL_TO"]) {
+										page += "- " + languageLabels["BILL_TO"] + ": " + order.properties["BILL_TO"];
+										page += "\n";
+									}
+									if(order.properties["SHIP_ADDRESS"]) {
+										page += "- " + languageLabels["SHIP_ADDRESS"] + ": " + order.properties["SHIP_ADDRESS"];
+										page += "\n";
+									}
+									if(order.properties["CONTACT_PHONE"]) {
+										page += "- " + languageLabels["PHONE"] + ": " + order.properties["CONTACT_PHONE"];
+										page += "\n";
+									}
+									if(order.properties["CONTACT_FAX"]) {
+										page += "- " + languageLabels["FAX"] + ": " + order.properties["CONTACT_FAX"];
+										page += "\n";
+									}
 									page += "\n";
-								}
-								page += "\n";
-								page += "\n";
-								page += languageLabels["REQUESTED_PRODUCTS_LABEL"];
-								page += "\n";
-								page += languageLabels["PRODUCTS_COLUMN_NAMES_LABEL"];
-								page += "\n";
-								var providerTotalByCurrency = {};
+									page += "\n";
+									page += languageLabels["SUPPLIER_INFORMATION"];
+									page += "\n";
+									if(provider.properties["NAME"]) {
+										page += "- " + languageLabels["SUPPLIER"] + ": " + provider.properties["NAME"];
+										page += "\n";
+									}
+									if(provider.properties["COMPANY_ADDRESS_LINE_1"]) {
+										page += "- " + languageLabels["SUPPLIER_ADDRESS_LINE_1"] + ": " + provider.properties["COMPANY_ADDRESS_LINE_1"]
+										page += "\n";
+									}
+									if(provider.properties["COMPANY_ADDRESS_LINE_2"]) {
+										page += "  " + languageLabels["SUPPLIER_ADDRESS_LINE_2"] + "  " + provider.properties["COMPANY_ADDRESS_LINE_2"]
+										page += "\n";
+									}
+									if(provider.properties["COMPANY_PHONE"]) {
+										page += "- " + languageLabels["SUPPLIER_PHONE"] + ": " + provider.properties["COMPANY_PHONE"];
+										page += "\n";
+									}
+									if(provider.properties["COMPANY_FAX"]) {
+										page += "- " + languageLabels["SUPPLIER_FAX"] + ": " + provider.properties["COMPANY_FAX"];
+										page += "\n";
+									}
+									if(provider.properties["COMPANY_EMAIL"]) {
+										page += "- " + languageLabels["SUPPLIER_EMAIL"] + ": " + provider.properties["COMPANY_EMAIL"];
+										page += "\n";
+									}
+									if(provider.properties["CUSTOMER_NUMBER"]) {
+										page += "- " + languageLabels["CUSTOMER_NUMBER"] + ": " + provider.properties["CUSTOMER_NUMBER"];
+										page += "\n";
+									}
+									page += "\n";
+									page += "\n";
+									page += languageLabels["REQUESTED_PRODUCTS_LABEL"];
+									page += "\n";
+									page += languageLabels["PRODUCTS_COLUMN_NAMES_LABEL"];
+									page += "\n";
+									var providerTotalByCurrency = {};
+									for(var pIdx = 0; pIdx < providerProducts.length; pIdx++) {
+										var product = providerProducts[pIdx];
+										var quantity = quantityByProductPermId[product.permId];
+										var unitPriceAsString = product.properties["PRICE_PER_UNIT"];
+										var unitPrice = "N/A";
+										if(unitPriceAsString) {
+											unitPrice = parseFloat(unitPriceAsString);
+										}
+										var currencyCode = product.properties["CURRENCY"];
+										if(!currencyCode) {
+											currencyCode = "N/A";
+										}
+										page += quantity + "\t\t" + product.properties["NAME"] + "\t\t" + product.properties["CATALOG_NUM"] + "\t\t" + unitPrice + " " + currencyCode;
+										page += "\n";
+										
+										if(unitPriceAsString) {
+											var totalForCurrency = providerTotalByCurrency[product.properties["CURRENCY"]];
+											if(!totalForCurrency) {
+												totalForCurrency = 0;
+											}
+											totalForCurrency += unitPrice * quantity;
+											
+											providerTotalByCurrency[product.properties["CURRENCY"]] = totalForCurrency;
+										} else {
+											providerTotalByCurrency[product.properties["CURRENCY"]] = "N/A";
+										}
+									}
+									page += "\n";
+									page += "\n";
+									page += "\n";
+									
+									var showTotals = false;
+									for(var currency in providerTotalByCurrency) {
+										if(providerTotalByCurrency[currency] > 0) {
+											showTotals = true;
+										}
+									}
+									if(showTotals) {
+										page += languageLabels["PRICE_TOTALS_LABEL"] + ":";
+										page += "\n";
+										for(var currency in providerTotalByCurrency) {
+											page += providerTotalByCurrency[currency] + " " + currency;
+											page += "\n";
+										}
+										page += "\n";
+										page += "\n";
+										page += "\n";
+									}
+									page += languageLabels["ADDITIONAL_INFO_LABEL"];
+									page += "\n";
+									var additionalInfo = order.properties["ADDITIONAL_INFORMATION"];
+									if(!additionalInfo) {
+										additionalInfo = "N/A";
+									}
+									page += additionalInfo;
+								orderPages.push(page);
+							}
+							
+							//Print Pages
+							for(var pageIdx = 0; pageIdx < orderPages.length; pageIdx++) {
+								Util.downloadTextFile(orderPages[pageIdx], "order_" + sample.code + "_p" + pageIdx + ".txt");
+							}
+							
+						}, "Print Order");
+						
+						//
+						// Order Summary Grid
+						//
+						var columns = [ {
+							label : 'Catalog Num',
+							property : 'catalogNum',
+							isExportable: true,
+							sortable : true,
+							render : function(data) {
+								return FormUtil.getFormLink(data.catalogNum, "Sample", data.permId);
+							}
+						},{
+							label : 'Supplier',
+							property : 'supplier',
+							isExportable: true,
+							sortable : true
+						}, {
+							label : 'Name',
+							property : 'name',
+							isExportable: true,
+							sortable : true
+						}, {
+							label : 'Quantity',
+							property : 'quantity',
+							isExportable: true,
+							sortable : true,
+						}, {
+							label : 'Unit Price',
+							property : 'unitPrice',
+							isExportable: true,
+							sortable : true
+						}, {
+							label : 'Total Product Cost',
+							property : 'totalProductCost',
+							isExportable: true,
+							sortable : true
+						}, {
+							label : 'Currency',
+							property : 'currency',
+							isExportable: true,
+							sortable : true
+						}];
+						
+						var getDataRows = function(callback) {
+							var rows = [];
+							for(var providerPermId in productsByProviderPermId) {
+								var provider = providerByPermId[providerPermId];
+								var providerProducts = productsByProviderPermId[providerPermId];
 								for(var pIdx = 0; pIdx < providerProducts.length; pIdx++) {
 									var product = providerProducts[pIdx];
 									var quantity = quantityByProductPermId[product.permId];
@@ -532,157 +652,61 @@ $.extend(StandardProfile.prototype, DefaultProfile.prototype, {
 									if(unitPriceAsString) {
 										unitPrice = parseFloat(unitPriceAsString);
 									}
+									var rowData = {};
+									rowData.permId = product.permId;
+									rowData.supplier = provider.properties["NAME"];
+									rowData.name = product.properties["NAME"];
+									rowData.catalogNum =  product.properties["CATALOG_NUM"];
+									rowData.quantity = quantity;
+									rowData.unitPrice = unitPrice;
+									if(unitPrice !== "N/A") {
+										rowData.totalProductCost = rowData.quantity * rowData.unitPrice;
+									} else {
+										rowData.totalProductCost = "N/A";
+									}
 									var currencyCode = product.properties["CURRENCY"];
 									if(!currencyCode) {
 										currencyCode = "N/A";
 									}
-									page += quantity + "\t\t" + product.properties["NAME"] + "\t\t" + product.properties["CATALOG_NUM"] + "\t\t" + unitPrice + " " + currencyCode;
-									page += "\n";
-									
-									if(unitPriceAsString) {
-										var totalForCurrency = providerTotalByCurrency[product.properties["CURRENCY"]];
-										if(!totalForCurrency) {
-											totalForCurrency = 0;
-										}
-										totalForCurrency += unitPrice * quantity;
-										
-										providerTotalByCurrency[product.properties["CURRENCY"]] = totalForCurrency;
-									} else {
-										providerTotalByCurrency[product.properties["CURRENCY"]] = "N/A";
-									}
+									rowData.currency = currencyCode;
+									rows.push(rowData);
 								}
-								page += "\n";
-								page += "\n";
-								page += "\n";
 								
-								var showTotals = false;
-								for(var currency in providerTotalByCurrency) {
-									if(providerTotalByCurrency[currency] > 0) {
-										showTotals = true;
-									}
-								}
-								if(showTotals) {
-									page += languageLabels["PRICE_TOTALS_LABEL"] + ":";
-									page += "\n";
-									for(var currency in providerTotalByCurrency) {
-										page += providerTotalByCurrency[currency] + " " + currency;
-										page += "\n";
-									}
-									page += "\n";
-									page += "\n";
-									page += "\n";
-								}
-								page += languageLabels["ADDITIONAL_INFO_LABEL"];
-								page += "\n";
-								var additionalInfo = order.properties["ADDITIONAL_INFORMATION"];
-								if(!additionalInfo) {
-									additionalInfo = "N/A";
-								}
-								page += additionalInfo;
-							orderPages.push(page);
-						}
+							}
+							callback(rows);
+						};
 						
-						//Print Pages
-						for(var pageIdx = 0; pageIdx < orderPages.length; pageIdx++) {
-							Util.downloadTextFile(orderPages[pageIdx], "order_" + sample.code + "_p" + pageIdx + ".txt");
+						var orderSummaryContainer = $("<div>");
+						var repTitle = "Order Summary";
+						if(isFromState) {
+							repTitle += " (as saved when ordered)"
 						}
 						
-					}, "Print Order");
-					
-					//
-					// Order Summary Grid
-					//
-					var columns = [ {
-						label : 'Catalog Num',
-						property : 'catalogNum',
-						isExportable: true,
-						sortable : true,
-						render : function(data) {
-							return FormUtil.getFormLink(data.catalogNum, "Sample", data.permId);
-						}
-					},{
-						label : 'Supplier',
-						property : 'supplier',
-						isExportable: true,
-						sortable : true
-					}, {
-						label : 'Name',
-						property : 'name',
-						isExportable: true,
-						sortable : true
-					}, {
-						label : 'Quantity',
-						property : 'quantity',
-						isExportable: true,
-						sortable : true,
-					}, {
-						label : 'Unit Price',
-						property : 'unitPrice',
-						isExportable: true,
-						sortable : true
-					}, {
-						label : 'Total Product Cost',
-						property : 'totalProductCost',
-						isExportable: true,
-						sortable : true
-					}, {
-						label : 'Currency',
-						property : 'currency',
-						isExportable: true,
-						sortable : true
-					}];
-					
-					var getDataRows = function(callback) {
-						var rows = [];
-						for(var providerPermId in productsByProviderPermId) {
-							var provider = providerByPermId[providerPermId];
-							var providerProducts = productsByProviderPermId[providerPermId];
-							for(var pIdx = 0; pIdx < providerProducts.length; pIdx++) {
-								var product = providerProducts[pIdx];
-								var quantity = quantityByProductPermId[product.permId];
-								var unitPriceAsString = product.properties["PRICE_PER_UNIT"];
-								var unitPrice = "N/A";
-								if(unitPriceAsString) {
-									unitPrice = parseFloat(unitPriceAsString);
-								}
-								var rowData = {};
-								rowData.permId = product.permId;
-								rowData.supplier = provider.properties["NAME"];
-								rowData.name = product.properties["NAME"];
-								rowData.catalogNum =  product.properties["CATALOG_NUM"];
-								rowData.quantity = quantity;
-								rowData.unitPrice = unitPrice;
-								if(unitPrice !== "N/A") {
-									rowData.totalProductCost = rowData.quantity * rowData.unitPrice;
-								} else {
-									rowData.totalProductCost = "N/A";
-								}
-								var currencyCode = product.properties["CURRENCY"];
-								if(!currencyCode) {
-									currencyCode = "N/A";
-								}
-								rowData.currency = currencyCode;
-								rows.push(rowData);
-							}
-							
+						var orderSummary = new DataGridController(repTitle, columns, [], null, getDataRows, null, false, "ORDER_SUMMARY");
+						orderSummary.init(orderSummaryContainer);
+						
+						var totalsByCurrencyContainer = $("<div>").append($("<br>")).append($("<legend>").append("Total:"));
+						for(var currency in absoluteTotalByCurrency) {
+							totalsByCurrencyContainer.append(absoluteTotalByCurrency[currency] + " " + currency).append($("<br>"));
 						}
-						callback(rows);
-					};
-					
-					var orderSummaryContainer = $("<div>");
-					var repTitle = "Order Summary";
-					if(isFromState) {
-						repTitle += " (as saved when ordered)"
+						$("#" + containerId).append(orderSummaryContainer).append(totalsByCurrencyContainer).append($("<br>")).append(printOrder);
 					}
 					
-					var orderSummary = new DataGridController(repTitle, columns, [], null, getDataRows, null, false, "ORDER_SUMMARY");
-					orderSummary.init(orderSummaryContainer);
-					
-					var totalsByCurrencyContainer = $("<div>").append($("<br>")).append($("<legend>").append("Total:"));
-					for(var currency in absoluteTotalByCurrency) {
-						totalsByCurrencyContainer.append(absoluteTotalByCurrency[currency] + " " + currency).append($("<br>"));
+					//
+					// Data Structures to Help the reports functionality
+					//
+					var orderSample = mainController.currentView._sampleFormModel.sample;
+					if(orderSample.properties["ORDER_STATE"]) {
+						isFromState = true;
+						var order = JSON.parse(decodeURIComponent(escape(window.atob(orderSample.properties["ORDER_STATE"]))));
+						showOrderSummary(order);
+					} else {
+						var searchSamples = { entityKind : "SAMPLE", logicalOperator : "OR", rules : { "UUIDv4" : { type : "Attribute", name : "PERM_ID", value : orderSample.permId } } };
+						mainController.serverFacade.searchForSamplesAdvanced(searchSamples, { only : true, withProperties : true, withAncestors : true, withAncestorsProperties : true }, function(result) {
+							var order = mainController.serverFacade.getV3SamplesAsV1(result.objects)[0];
+							showOrderSummary(order);
+						});
 					}
-					$("#" + containerId).append(orderSummaryContainer).append(totalsByCurrencyContainer).append($("<br>")).append(printOrder);
 				}
 			} else if(sampleTypeCode === "REQUEST") {
 				var isEnabled = mainController.currentView._sampleFormModel.mode !== FormMode.VIEW;
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/LayoutManager.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/LayoutManager.js
index 874b7d90daa06ff84d7a8fff1df6524e5ece792b..c3922e869f3269ca830beab2838aa5ce7e239777 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/LayoutManager.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/controllers/LayoutManager.js
@@ -428,8 +428,6 @@ var LayoutManager = {
 			}
 		}
 		
-		console.log("reloadView called with isFirstTime:" + isFirstTime);
-		
 		if (this.FOUND_SIZE === this.DESKTOP_SIZE) {
 			this._setDesktopLayout(view, isFirstTime);
 		} else if (this.FOUND_SIZE === this.TABLET_SIZE) {
diff --git a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
index f1d812e7343b4ffa89221e1e553b7846253cb467..cd600c62a48fb79c32b37f4bc6802390ab8e55ce 100644
--- a/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
+++ b/openbis_standard_technologies/dist/core-plugins/eln-lims/1/as/webapps/eln-lims/html/js/server/ServerFacade.js
@@ -1024,6 +1024,13 @@ function ServerFacade(openbisServer) {
 							childrenFetchOptions.withType();
 						}
 					}
+					if(advancedFetchOptions.withAncestors) {
+						var ancestorsFetchOptions = fetchOptions.withParents();
+						if(advancedFetchOptions.withAncestorsProperties) {
+							ancestorsFetchOptions.withProperties();
+						}
+						ancestorsFetchOptions.withParentsUsing(ancestorsFetchOptions);
+					}
 				}
 				
 				if(advancedFetchOptions && advancedFetchOptions.cache) {
diff --git a/openbis_standard_technologies/settings.gradle b/openbis_standard_technologies/settings.gradle
index f996c84ccde18315345bdd3df01884e9b585ec83..c5d11ff9434eedc79a0ebd73a05575c943eaef2f 100644
--- a/openbis_standard_technologies/settings.gradle
+++ b/openbis_standard_technologies/settings.gradle
@@ -1,2 +1,3 @@
 includeFlat 'commonbase', 'common', 'openbis_api', 'openbis-common', 'authentication', 'dbmigration', 'openbis', 
-    'datastore_server', 'screening', 'rtd_yeastx', 'rtd_phosphonetx', 'deep_sequencing_unit', 'plasmid', 'big_data_link_server'
+    'datastore_server', 'screening', 'rtd_yeastx', 'rtd_phosphonetx', 'deep_sequencing_unit', 'plasmid', 'big_data_link_server',
+    'openbis_ng_ui'