gh-105: Improve child entity visualization
diff --git a/src/lib/components/block/entity-view/entity-table-body.vue b/src/lib/components/block/entity-view/entity-table-body.vue
deleted file mode 100644
index 674c404..0000000
--- a/src/lib/components/block/entity-view/entity-table-body.vue
+++ /dev/null
@@ -1,152 +0,0 @@
-<template>
-<b-tbody>
-  <b-tr>
-    <!-- column One -->
-    <b-td>
-      <template>
-        <b-button variant="link" size="sm" v-on:click="onClickExpand()" v-b-tooltip.hover :title="expandChild?'Colapse':'Expand'">
-          <b-icon :icon="expandChild?'chevron-up':'chevron-down'"></b-icon>
-        </b-button>
-      </template>
-      <router-link :to="`/tenants/${clientId}/entities/${entity.entityId}`" v-slot="{href, navigate}">
-        <b-link :href="href" v-on:click="navigate">{{ entity.entityId }}</b-link>
-      </router-link>
-      <button-copy :value="entity.entityId"/>
-    </b-td>
-    <!-- Column Two -->
-    <b-td>{{ entity.name }}</b-td>
-    <!-- Column Three -->
-    <b-td>{{ entity.type }}</b-td>
-    <!-- Column Four -->
-    <b-td>{{ entity.description }}</b-td>
-    <!-- Column Five -->
-    <b-td>{{ entity.createdAt }}</b-td>
-    <!-- Column Six -->
-    <b-td>{{ entity.updatedAt }}</b-td>
-    <!-- Column Seven -->
-    <b-td>
-      <!-- Create Child Entity Button -->
-      <router-link :to="{name:'create_entity', query:{ entityId:`${entity.entityId}`}}" v-slot="{href, navigate}" tag="">
-        <b-button variant="link" size="sm" v-on:click="navigate" v-b-tooltip.hover title="Create Child Entity">
-          <b-icon icon="folder-plus"></b-icon>
-          <!-- <v-icon>mdi-pl?us</v-icon> -->
-        </b-button>
-      </router-link>
-      <!-- Share Entity Button -->
-      <b-button variant="link" size="sm" v-b-modal="`modal-select-users-or-groups-${entityIndex}`" v-b-tooltip.hover title="Share">
-        <span>{{ entity.sharedCount }}</span>&nbsp;
-        <b-icon icon="share"></b-icon>
-      </b-button>
-      <!-- Modal Share Entity -->
-      <modal-share-entity 
-        :client-id="clientId" :entity-id="entity.entityId"
-        :modal-id="`modal-select-users-or-groups-${entityIndex}`"
-        :title="`Share Entity '${entity.name}'`"
-        v-on:close="refreshData"
-      />
-      <!-- Delete Entity Button -->
-      <button-overlay :show="processingDelete[entity.entityId]">
-        <b-button variant="link" size="sm" v-on:click="onClickDelete(entity)" v-b-tooltip.hover title="Delete">
-          <b-icon icon="trash"></b-icon>
-        </b-button>
-      </button-overlay>
-    </b-td>
-  </b-tr>
-  <!-- Child Entities -->
-  <template v-if="expandChild">
-    <b-tr><b-td colspan="100%">
-      <template v-if="!childEntities || !childEntities.length" >
-        <span style="display: block; text-align: center;">No child entities to show.</span>
-      </template>
-      <template v-else>
-        <b-table-simple>      
-          <b-thead style="display:none">
-            <b-tr>
-              <b-th>Entity ID</b-th>
-              <b-th>Name</b-th>
-              <b-th>Type</b-th>
-              <b-th>Description</b-th>
-              <b-th>Created</b-th>
-              <b-th>Last Updated</b-th>
-            </b-tr>
-          </b-thead>
-          <template  v-for="(childEntity, childEntityIndex) in childEntities">
-              <entity-table-body v-on:refresh-data="refreshData" :key="childEntity.entityId" :entity="childEntity" :entityIndex="entityIndex.toString()+'-'+childEntityIndex.toString()" :allEntities="allEntities"></entity-table-body>
-          </template>
-        </b-table-simple>
-      </template>
-    </b-td></b-tr>
-  </template>
-</b-tbody>
-</template>
-
-<script>
-import store from "../../../store"
-import ModalShareEntity from "../../modals/modal-share-entity";
-import ButtonOverlay from "../../overlay/button-overlay";
-import ButtonCopy from "../../button/button-copy";
-
-
-export default {
-  name: 'entity-table-body',
-  store: store,
-  components: {ButtonCopy, ButtonOverlay, ModalShareEntity},
-  props: {
-    entity: Object,
-    entityIndex: [Number, String],
-    allEntities: Array
-  },
-  data() {
-    return {
-      processingDelete: {},
-      expandChild: false,
-      errors: []
-    }
-  },
-  computed: {
-    clientId() {
-      return this.$route.params.clientId;
-    },
-    childEntities() {
-      return this.allEntities==undefined?this.allEntities:
-        this.allEntities.filter((entity)=>{return entity.parentId==this.entity.entityId;});
-    }
-  },
-  methods: {
-    onClickExpand() {
-      this.expandChild = !this.expandChild
-    },
-    refreshData() {
-      this.$emit('refresh-data');
-    },
-    async onClickDelete({entityId, name, description, type, ownerId}) {
-      this.processingDelete = {...this.processingDelete, [entityId]: true};
-
-      try {
-        await this.$store.dispatch("entity/deleteEntity", {
-          clientId: this.clientId,
-          entityId,
-          name,
-          description,
-          type,
-          ownerId
-        });
-        this.refreshData();
-      } catch (error) {
-        this.errors.push({
-          title: `Unknown error when deleting the entity '${entityId}'.`,
-          source: error, variant: "danger"
-        });
-      }
-
-      this.processingDelete = {...this.processingDelete, [entityId]: false};
-    }
-  }
-}
-</script>
-
-<style scoped>
-.hidden_header{
-  display: none;
-}
-</style>
\ No newline at end of file
diff --git a/src/lib/components/pages/TenantEntities.vue b/src/lib/components/pages/TenantEntities.vue
index 87df364..069b969 100644
--- a/src/lib/components/pages/TenantEntities.vue
+++ b/src/lib/components/pages/TenantEntities.vue
@@ -5,7 +5,7 @@
         <b-button variant="primary" @click="navigate">Create New Entity</b-button>
       </router-link>
     </template>
-    <table-overlay-info :rows="5" :columns="1" :data="rootEntities">
+    <table-overlay-info :rows="5" :columns="1" :data="entityIds">
       <b-table-simple>
         <b-thead>
           <b-tr>
@@ -14,13 +14,99 @@
             <b-th>Type</b-th>
             <b-th>Description</b-th>
             <b-th>Created</b-th>
-            <b-th>Owner</b-th>
             <b-th>Last Updated</b-th>
+            <b-th>Owner</b-th>
           </b-tr>
         </b-thead>
-        <template v-for="(entity, entityIndex) in rootEntities">
-          <entity-table-body v-on:refresh-data="refreshData" :key="entity.entityId" :entity="entity" :entityIndex="entityIndex" :allEntities="allEntities"></entity-table-body>
-        </template>
+        <b-tbody>
+          <template v-for="(entityId, entityIndex) in entityIds">
+            <b-tr :key="entityIndex" v-if="entityChildrenExpanded[entityMap[entityId].parentId]">
+              <!-- column One -->
+              <b-td>
+                <div style="display: flex; flex-direction: row;">
+                  <div :style="`padding-right: ${entityIndent[entityId] * 20}px;`"></div>
+                  <div style="width: 36px;">
+                    <div v-if="hasChildren(entityMap[entityId])">
+                      <b-button v-if="entityChildrenExpanded[entityId]" variant="link" size="sm"
+                                v-on:click="onClickCollapse(entityMap[entityId])" v-b-tooltip.hover title="Collapse">
+                        <b-icon icon="chevron-down"></b-icon>
+                      </b-button>
+                      <b-button v-else variant="link" size="sm" v-on:click="onClickExpand(entityMap[entityId])"
+                                v-b-tooltip.hover title="Collapse">
+                        <b-icon icon="chevron-right"></b-icon>
+                      </b-button>
+                    </div>
+                  </div>
+                  <div style="flex: 1;">
+                    <router-link :to="`/tenants/${clientId}/entities/${entityId}`" v-slot="{href, navigate}">
+                      <b-link :href="href" v-on:click="navigate">{{ entityId }}</b-link>
+                    </router-link>
+                  </div>
+                  <button-copy :value="entityId"/>
+                </div>
+              </b-td>
+              <!-- Column Two -->
+              <b-td>{{ entityMap[entityId].name }}</b-td>
+              <!-- Column Three -->
+              <b-td>{{ entityMap[entityId].type }}</b-td>
+              <!-- Column Four -->
+              <b-td>{{ entityMap[entityId].description }}</b-td>
+              <!-- Column Five -->
+              <b-td>{{ entityMap[entityId].createdAt }}</b-td>
+              <!-- Column Six -->
+              <b-td>{{ entityMap[entityId].updatedAt }}</b-td>
+              <!-- Column Seven -->
+              <b-td>{{ entityMap[entityId].ownerId }}</b-td>
+              <!-- Column Eight -->
+              <b-td>
+                <div style="display: flex; flex-direction: row;">
+                  <div>
+                    <!-- Create Child Entity Button -->
+                    <router-link :to="{name:'create_entity', query:{ entityId:`${entityId}`}}"
+                                 v-slot="{href, navigate}"
+                                 tag="">
+                      <b-button variant="link" size="sm" v-on:click="navigate" v-b-tooltip.hover
+                                title="Create Child Entity">
+                        <b-icon icon="folder-plus"></b-icon>
+                        <!-- <v-icon>mdi-pl?us</v-icon> -->
+                      </b-button>
+                    </router-link>
+                  </div>
+                  <div>
+                    <!-- Share Entity Button -->
+                    <b-button variant="link" size="sm" v-b-modal="`modal-select-users-or-groups-${entityIndex}`"
+                              v-b-tooltip.hover
+                              title="Share">
+                      <span style="display: flex; flex-direction: row;">
+                        <span style="padding-right: 4px;">{{ entityMap[entityId].sharedCount }}</span>
+                      <b-icon icon="share"></b-icon>
+                      </span>
+                    </b-button>
+                    <!-- Modal Share Entity -->
+                    <modal-share-entity
+                        :client-id="clientId" :entity-id="entityId"
+                        :modal-id="`modal-select-users-or-groups-${entityIndex}`"
+                        :title="`Share Entity '${entityMap[entityId].name}'`"
+                        v-on:close="refreshData"
+                    />
+                  </div>
+                  <div>
+                    <!-- Delete Entity Button -->
+                    <button-overlay :show="processingDelete[entityId]">
+                      <b-button variant="link" size="sm" v-on:click="onClickDelete(entityMap[entityId])"
+                                v-b-tooltip.hover
+                                title="Delete">
+                        <b-icon icon="trash"></b-icon>
+                      </b-button>
+                    </button-overlay>
+                  </div>
+                </div>
+
+
+              </b-td>
+            </b-tr>
+          </template>
+        </b-tbody>
       </b-table-simple>
 
       <!--    <b-pagination-->
@@ -40,17 +126,27 @@
 import store from "../../store"
 import TenantHome from "./TenantHome";
 import TableOverlayInfo from "../overlay/table-overlay-info";
-import EntityTableBody from "../block/entity-view/entity-table-body";
+import ModalShareEntity from "@/lib/components/modals/modal-share-entity";
+import ButtonOverlay from "@/lib/components/overlay/button-overlay";
+import ButtonCopy from "@/lib/components/button/button-copy";
 
 export default {
   name: "TenantEntities",
   store: store,
-  components: {TableOverlayInfo, TenantHome, EntityTableBody},
+  components: {ButtonCopy, ButtonOverlay, ModalShareEntity, TableOverlayInfo, TenantHome},
   data() {
     return {
       processingDelete: {},
-      expandEntities: {},
-      errors: []
+      errors: [],
+
+      entityIds: null,
+      entityMap: {},
+      entityIndent: {},
+      entityChildrenIds: {},
+      entityChildrenMapped: {},
+      entityChildrenExpanded: {
+        [""]: true
+      }
     }
   },
   computed: {
@@ -61,9 +157,6 @@
     allEntities() {
       return this.$store.getters["entity/getEntities"]({clientId: this.clientId, ownerId: this.currentUsername});
     },
-    rootEntities() {
-      return this.allEntities==undefined?this.allEntities:this.allEntities.filter((entity)=>{return !entity.parentId;});
-    },
     breadcrumbLinks() {
       return [{to: `/tenants/${this.clientId}/entities`, name: "Entities"}];
     },
@@ -71,7 +164,98 @@
       return this.$store.getters["auth/currentUsername"];
     }
   },
+  watch: {
+    allEntities() {
+      const entityIds = [];
+      const entityMap = {};
+      const entityIndent = {};
+      const entityChildrenIds = {};
+      const entityChildrenMapped = {};
+      if (this.allEntities) {
+        for (let i = 0; i < this.allEntities.length; i++) {
+          const entity = this.allEntities[i];
+          entityMap[entity.entityId] = entity;
+          entityChildrenMapped[entity.entityId] = false;
+          if (!entityChildrenIds[entity.parentId]) {
+            entityChildrenIds[entity.parentId] = [entity.entityId];
+          } else {
+            entityChildrenIds[entity.parentId].push(entity.entityId);
+          }
+
+          if (!entity.parentId) {
+            entityIds.push(entity.entityId);
+            entityIndent[entity.entityId] = 0;
+          }
+        }
+      }
+
+      console.log("this.entityIds : ", entityIds);
+
+      this.entityIds = entityIds;
+      this.entityMap = entityMap;
+      this.entityIndent = entityIndent;
+      this.entityChildrenIds = entityChildrenIds;
+      this.entityChildrenMapped = entityChildrenMapped;
+
+    }
+  },
   methods: {
+    rootEntities() {
+      return this.childEntities({entityId: null});
+    },
+    hasChildren(parentEntity) {
+      return this.entityChildrenIds[parentEntity.entityId] && this.entityChildrenIds[parentEntity.entityId].length > 0;
+    },
+    childEntities(parentEntity) {
+      return this.entities ? [] :
+          this.entities.filter((entity) => {
+            return entity.parentId === parentEntity.entityId;
+          });
+    },
+    onClickExpand({entityId}) {
+      if (!this.entityChildrenMapped[entityId]) {
+        let entityIds = [];
+        let entityIndent = {...this.entityIndent};
+        for (let i = 0; i < this.entityIds.length; i++) {
+          entityIds.push(this.entityIds[i]);
+          if (this.entityIds[i] === entityId && this.hasChildren(this.entityMap[this.entityIds[i]])) {
+            for (let j = 0; j < this.entityChildrenIds[entityId].length; j++) {
+              entityIds.push(this.entityChildrenIds[entityId][j]);
+              entityIndent[this.entityChildrenIds[entityId][j]] = entityIndent[entityId] + 1;
+            }
+          }
+        }
+        this.entityIds = entityIds;
+        this.entityIndent = entityIndent;
+        this.entityChildrenMapped = {...this.entityChildrenMapped, [entityId]: true};
+      }
+      this.entityChildrenExpanded = {...this.entityChildrenExpanded, [entityId]: true};
+    },
+    onClickCollapse({entityId}) {
+      this.entityChildrenExpanded = {...this.entityChildrenExpanded, [entityId]: false};
+    },
+    async onClickDelete({entityId, name, description, type, ownerId}) {
+      this.processingDelete = {...this.processingDelete, [entityId]: true};
+
+      try {
+        await this.$store.dispatch("entity/deleteEntity", {
+          clientId: this.clientId,
+          entityId,
+          name,
+          description,
+          type,
+          ownerId
+        });
+        this.refreshData();
+      } catch (error) {
+        this.errors.push({
+          title: `Unknown error when deleting the entity '${entityId}'.`,
+          source: error, variant: "danger"
+        });
+      }
+
+      this.processingDelete = {...this.processingDelete, [entityId]: false};
+    },
     refreshData() {
       this.$store.dispatch("entity/fetchEntities", {clientId: this.clientId, ownerId: this.currentUsername});
     }