changeset 2874:b9a6abef9f1c

client: more unified table layout Expandable/collapsible rows are now also handled by the component
author Markus Kottlaender <markus@intevation.de>
date Mon, 01 Apr 2019 11:51:03 +0200
parents b1707f60f241
children 84effca50751
files client/src/components/Bottlenecks.vue client/src/components/ImportStretches.vue client/src/components/importoverview/ImportOverview.vue client/src/components/importoverview/LogEntry.vue client/src/components/importschedule/Importschedule.vue client/src/components/systemconfiguration/PDFTemplates.vue client/src/components/ui/UITableBody.vue client/src/components/ui/UITableHeader.vue client/src/components/usermanagement/Usermanagement.vue
diffstat 9 files changed, 267 insertions(+), 276 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/Bottlenecks.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/Bottlenecks.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -7,11 +7,11 @@
     />
     <UITableHeader
       :columns="[
-        { id: 'properties.name', title: `${nameLabel}`, width: '200px' },
+        { id: 'properties.name', title: `${nameLabel}`, width: '230px' },
         {
           id: 'properties.responsible_country',
           title: `${countryLabel}`,
-          width: '80px'
+          width: '100px'
         },
         {
           id: 'properties.current',
@@ -25,61 +25,56 @@
       :data="filteredBottlenecks() | sortTable(sortColumn, sortDirection)"
       :maxHeight="(showSplitscreen ? 18 : 35) + 'rem'"
       :active="openBottleneck"
-      v-slot="{ item: bottleneck }"
     >
-      <div class="table-cell truncate text-left" style="width: 200px">
-        <a href="#" @click="selectBottleneck(bottleneck)">{{
-          bottleneck.properties.name
-        }}</a>
-      </div>
-      <div class="table-cell text-center" style="width: 80px">
-        {{ bottleneck.properties.responsible_country }}
-      </div>
-      <div class="table-cell" style="width: 150px">
-        {{ bottleneck.properties.current | surveyDate }}
-      </div>
-      <div class="table-cell" style="width: 150px">
-        {{
-          displayCurrentChainage(
-            bottleneck.properties.from,
-            bottleneck.properties.to
-          )
-        }}
-      </div>
-      <div
-        class="table-cell pr-0 text-right d-flex flex-column"
-        style="width: 30px"
-      >
-        <a
-          class="text-info mt-auto mb-auto mr-2"
-          @click="loadSurveys(bottleneck)"
-          v-if="bottleneck.properties.current"
-        >
-          <font-awesome-icon
-            class="pointer"
-            icon="spinner"
-            fixed-width
-            spin
-            v-if="loading === bottleneck"
-          ></font-awesome-icon>
-          <font-awesome-icon
-            class="pointer"
-            icon="angle-down"
-            fixed-width
-            v-if="loading !== bottleneck && openBottleneck !== bottleneck"
-          ></font-awesome-icon>
-          <font-awesome-icon
-            class="pointer"
-            icon="angle-up"
-            fixed-width
-            v-if="loading !== bottleneck && openBottleneck === bottleneck"
-          ></font-awesome-icon>
-        </a>
-      </div>
-      <div
-        :class="['p-0', 'surveys', { open: openBottleneck === bottleneck }]"
-        style="width: 100%"
-      >
+      <template v-slot:row="{ item: bottleneck }">
+        <div class="table-cell truncate text-left" style="width: 230px">
+          <a href="#" @click="selectBottleneck(bottleneck)">{{
+            bottleneck.properties.name
+          }}</a>
+        </div>
+        <div class="table-cell text-center" style="width: 100px">
+          {{ bottleneck.properties.responsible_country }}
+        </div>
+        <div class="table-cell" style="width: 150px">
+          {{ bottleneck.properties.current | surveyDate }}
+        </div>
+        <div class="table-cell" style="width: 150px">
+          {{
+            displayCurrentChainage(
+              bottleneck.properties.from,
+              bottleneck.properties.to
+            )
+          }}
+        </div>
+        <div class="table-cell center" style="width: 30px">
+          <a
+            class="text-info"
+            @click="loadSurveys(bottleneck)"
+            v-if="bottleneck.properties.current"
+          >
+            <font-awesome-icon
+              class="pointer"
+              icon="spinner"
+              fixed-width
+              spin
+              v-if="loading === bottleneck"
+            ></font-awesome-icon>
+            <font-awesome-icon
+              class="pointer"
+              icon="angle-down"
+              fixed-width
+              v-if="loading !== bottleneck && openBottleneck !== bottleneck"
+            ></font-awesome-icon>
+            <font-awesome-icon
+              class="pointer"
+              icon="angle-up"
+              fixed-width
+              v-if="loading !== bottleneck && openBottleneck === bottleneck"
+            ></font-awesome-icon>
+          </a>
+        </div>
+      </template>
+      <template v-slot:expand="{ item: bottleneck }">
         <a
           href="#"
           class="d-inline-block px-3 py-2"
@@ -89,37 +84,11 @@
         >
           {{ survey.date_info | surveyDate }}
         </a>
-      </div>
+      </template>
     </UITableBody>
   </div>
 </template>
 
-<style lang="sass" scoped>
-.table-body
-  .row
-    > div:not(:last-child)
-      transition: background-color 0.3s, color 0.3s
-    &.active
-      > div:not(:last-child)
-        background-color: $color-info
-        color: #fff
-        a
-          color: #fff !important
-      .surveys
-        border-bottom: solid 1px $color-info
-    .table-cell
-      padding: 0 3px
-      border-right: solid 1px #dee2e6
-      &:last-child
-        border-right: none
-    .surveys
-      overflow: hidden
-      max-height: 0
-      &.open
-        overflow-y: auto
-        max-height: 5rem
-</style>
-
 <script>
 /* This is Free Software under GNU Affero General Public License v >= 3.0
  * without warranty, see README.md and license for details.
--- a/client/src/components/ImportStretches.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/ImportStretches.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -19,43 +19,44 @@
       />
       <UITableBody
         :data="filteredStretches() | sortTable(sortColumn, sortDirection)"
-        v-slot="{ item: stretch }"
       >
-        <div class="py-1 col-4 ">
-          <a
-            class="linkto text-info"
-            v-if="isInStaging(stretch.properties.name)"
-            @click="gotoStaging(getStagingLink(stretch.properties.name))"
-          >
-            {{ stretch.properties.name
-            }}<font-awesome-icon
-              class="ml-1 text-danger"
-              icon="exclamation-triangle"
-              fixed-width
-            ></font-awesome-icon
-            ><small class="ml-1">review</small>
-          </a>
-          <a v-else @click="moveMapToStretch(stretch)" href="#">{{
-            stretch.properties.name
-          }}</a>
-        </div>
-        <div class="py-1 col-2">
-          {{ stretch.properties.date_info | surveyDate }}
-        </div>
-        <div class="py-1 col-3">
-          {{ stretch.properties.source_organization }}
-        </div>
-        <div class="py-1 col text-right">
-          <button
-            class="btn btn-xs btn-dark mr-1"
-            @click="editStretch(stretch)"
-          >
-            <font-awesome-icon icon="pencil-alt" fixed-width />
-          </button>
-          <button class="btn btn-xs btn-dark" @click="deleteStretch(stretch)">
-            <font-awesome-icon icon="trash" fixed-width />
-          </button>
-        </div>
+        <template v-slot:row="{ item: stretch }">
+          <div class="py-1 col-4 ">
+            <a
+              class="linkto text-info"
+              v-if="isInStaging(stretch.properties.name)"
+              @click="gotoStaging(getStagingLink(stretch.properties.name))"
+            >
+              {{ stretch.properties.name
+              }}<font-awesome-icon
+                class="ml-1 text-danger"
+                icon="exclamation-triangle"
+                fixed-width
+              ></font-awesome-icon
+              ><small class="ml-1">review</small>
+            </a>
+            <a v-else @click="moveMapToStretch(stretch)" href="#">{{
+              stretch.properties.name
+            }}</a>
+          </div>
+          <div class="py-1 col-2">
+            {{ stretch.properties.date_info | surveyDate }}
+          </div>
+          <div class="py-1 col-3">
+            {{ stretch.properties.source_organization }}
+          </div>
+          <div class="py-1 col text-right">
+            <button
+              class="btn btn-xs btn-dark mr-1"
+              @click="editStretch(stretch)"
+            >
+              <font-awesome-icon icon="pencil-alt" fixed-width />
+            </button>
+            <button class="btn btn-xs btn-dark" @click="deleteStretch(stretch)">
+              <font-awesome-icon icon="trash" fixed-width />
+            </button>
+          </div>
+        </template>
       </UITableBody>
     </div>
     <div v-if="edit">
--- a/client/src/components/importoverview/ImportOverview.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/importoverview/ImportOverview.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -96,9 +96,10 @@
       <UITableBody
         :data="filteredImports() | sortTable(sortColumn, sortDirection)"
         maxHeight="73vh"
-        v-slot="{ item: entry }"
       >
-        <LogEntry :entry="entry"></LogEntry>
+        <template v-slot:row="{ item: entry }">
+          <LogEntry :entry="entry"></LogEntry>
+        </template>
       </UITableBody>
     </div>
   </div>
--- a/client/src/components/importoverview/LogEntry.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/importoverview/LogEntry.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -1,5 +1,5 @@
 <template>
-  <div class="logentry">
+  <div>
     <div class="row no-gutters text-left">
       <div
         style="width: 79px;"
@@ -71,34 +71,25 @@
 </template>
 
 <style lang="sass" scoped>
-.logentry
-  width: 100%
-  &:hover
-    background: #fafafa
-  .actions
-    height: 100%
-    width: 50%
-    border: 0
-    background: transparent
-    outline: none
-    &.approved
-      color: green
-      &.active,
-      &:hover
-        color: white
-        background: green
-    &.rejected
-      border-left: 1px solid #dee2e6
-      color: red
-      &.active,
-      &:hover
-        color: white
-        background: red
-.table-cell
-  padding: 0 3px
-  border-right: solid 1px #dee2e6
-  &:last-child
-    border-right: none
+.actions
+  height: 100%
+  width: 50%
+  border: 0
+  background: transparent
+  outline: none
+  &.approved
+    color: green
+    &.active,
+    &:hover
+      color: white
+      background: green
+  &.rejected
+    border-left: 1px solid #dee2e6
+    color: red
+    &.active,
+    &:hover
+      color: white
+      background: red
 </style>
 
 <script>
--- a/client/src/components/importschedule/Importschedule.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/importschedule/Importschedule.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -32,46 +32,47 @@
         />
         <UITableBody
           :data="filteredSchedules() | sortTable(sortColumn, sortDirection)"
-          v-slot="{ item: schedule }"
         >
-          <div class="py-1 col-1">{{ schedule.id }}</div>
-          <div class="py-1 col-2">{{ schedule.kind.toUpperCase() }}</div>
-          <div class="py-1 col-2">{{ schedule.user }}</div>
-          <div class="py-1 col-2">{{ schedule.config.cron }}</div>
-          <div class="py-1 col-2 text-center">
-            <font-awesome-icon
-              v-if="schedule.config['send-email']"
-              class="fa-fw mr-2"
-              fixed-width
-              icon="check"
-            ></font-awesome-icon>
-          </div>
-          <div class="py-1 col text-right">
-            <button
-              @click="editSchedule(schedule.id)"
-              class="btn btn-xs btn-dark mr-1"
-              :disabled="importScheduleDetailVisible"
-            >
+          <template v-slot:row="{ item: schedule }">
+            <div class="py-1 col-1">{{ schedule.id }}</div>
+            <div class="py-1 col-2">{{ schedule.kind.toUpperCase() }}</div>
+            <div class="py-1 col-2">{{ schedule.user }}</div>
+            <div class="py-1 col-2">{{ schedule.config.cron }}</div>
+            <div class="py-1 col-2 text-center">
               <font-awesome-icon
-                icon="pencil-alt"
+                v-if="schedule.config['send-email']"
+                class="fa-fw mr-2"
                 fixed-width
+                icon="check"
               ></font-awesome-icon>
-            </button>
-            <button
-              @click="deleteSchedule(schedule)"
-              class="btn btn-xs btn-dark mr-1"
-              :disabled="importScheduleDetailVisible"
-            >
-              <font-awesome-icon icon="trash" fixed-width></font-awesome-icon>
-            </button>
-            <button
-              @click="triggerManualImport(schedule.id)"
-              class="btn btn-xs btn-dark"
-              :disabled="importScheduleDetailVisible"
-            >
-              <font-awesome-icon icon="play" fixed-width></font-awesome-icon>
-            </button>
-          </div>
+            </div>
+            <div class="py-1 col text-right">
+              <button
+                @click="editSchedule(schedule.id)"
+                class="btn btn-xs btn-dark mr-1"
+                :disabled="importScheduleDetailVisible"
+              >
+                <font-awesome-icon
+                  icon="pencil-alt"
+                  fixed-width
+                ></font-awesome-icon>
+              </button>
+              <button
+                @click="deleteSchedule(schedule)"
+                class="btn btn-xs btn-dark mr-1"
+                :disabled="importScheduleDetailVisible"
+              >
+                <font-awesome-icon icon="trash" fixed-width></font-awesome-icon>
+              </button>
+              <button
+                @click="triggerManualImport(schedule.id)"
+                class="btn btn-xs btn-dark"
+                :disabled="importScheduleDetailVisible"
+              >
+                <font-awesome-icon icon="play" fixed-width></font-awesome-icon>
+              </button>
+            </div>
+          </template>
         </UITableBody>
         <div class="p-3 text-right">
           <button
--- a/client/src/components/systemconfiguration/PDFTemplates.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/systemconfiguration/PDFTemplates.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -18,28 +18,30 @@
           { id: 'country', title: `${countryLabel}`, class: 'col-2' }
         ]"
       />
-      <UITableBody
-        :data="templates | sortTable(sortColumn, sortDirection)"
-        v-slot="{ item: template }"
-      >
-        <div class="py-1 col-4">{{ template.name }}</div>
-        <div class="py-1 col-4">{{ template.time }}</div>
-        <div class="py-1 col-2" v-if="template.country">
-          {{ template.country }}
-        </div>
-        <div class="py-1 col-2" v-else><i>global</i></div>
-        <div class="col py-1 text-right">
-          <button
-            class="btn btn-xs btn-info mr-1"
-            ref="downloadTemplate"
-            @click="downloadTemplate(template)"
-          >
-            <font-awesome-icon icon="download" fixed-width />
-          </button>
-          <button class="btn btn-xs btn-dark" @click="deleteTemplate(template)">
-            <font-awesome-icon icon="trash" fixed-width />
-          </button>
-        </div>
+      <UITableBody :data="templates | sortTable(sortColumn, sortDirection)">
+        <template v-slot:row="{ item: template }">
+          <div class="py-1 col-4">{{ template.name }}</div>
+          <div class="py-1 col-4">{{ template.time }}</div>
+          <div class="py-1 col-2" v-if="template.country">
+            {{ template.country }}
+          </div>
+          <div class="py-1 col-2" v-else><i>global</i></div>
+          <div class="col py-1 text-right">
+            <button
+              class="btn btn-xs btn-info mr-1"
+              ref="downloadTemplate"
+              @click="downloadTemplate(template)"
+            >
+              <font-awesome-icon icon="download" fixed-width />
+            </button>
+            <button
+              class="btn btn-xs btn-dark"
+              @click="deleteTemplate(template)"
+            >
+              <font-awesome-icon icon="trash" fixed-width />
+            </button>
+          </div>
+        </template>
       </UITableBody>
       <button class="btn btn-info mt-2" @click="$refs.uploadTemplate.click()">
         <font-awesome-icon
--- a/client/src/components/ui/UITableBody.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/ui/UITableBody.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -7,12 +7,14 @@
     <div
       v-for="(item, index) in data"
       :key="key(index)"
-      :class="[
-        'border-top row mx-0 align-items-center',
-        { active: active === item }
-      ]"
+      :class="['row-container border-top', { active: active === item }]"
     >
-      <slot :item="item" :index="index"></slot>
+      <div class="row mx-0">
+        <slot :item="item" :index="index" name="row"></slot>
+      </div>
+      <div class="expand" v-if="active === item">
+        <slot :item="item" :index="index" name="expand"></slot>
+      </div>
     </div>
   </div>
   <div v-else class="small text-center py-3 border-top">
@@ -20,6 +22,37 @@
   </div>
 </template>
 
+<style lang="sass">
+.table-body
+  .row-container
+    > .row
+      &:hover
+        background-color: #fafafa
+      .table-cell
+        display: flex
+        align-items: center
+        padding: 1.5px 3px
+        border-right: solid 1px #dee2e6
+        &:hover
+          background-color: #f2f2f2
+        &:last-child
+          border-right: none
+        &.center
+          justify-content: center
+    .expand
+      border-bottom: solid 1px $color-info
+
+    &.active
+      > .row
+        color: #fff
+        .table-cell
+          border-right-color: rgba(255, 255, 255, 0.3)
+          background-color: $color-info
+          color: #fff
+          a
+            color: #fff !important
+</style>
+
 <script>
 /* This is Free Software under GNU Affero General Public License v >= 3.0
  * without warranty, see README.md and license for details.
--- a/client/src/components/ui/UITableHeader.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/ui/UITableHeader.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -5,7 +5,7 @@
       @click.prevent="!column.disableSorting && sortTable(column.id)"
       :key="column.id"
       :class="[
-        'd-flex py-1 align-items-center justify-content-center small ' +
+        'd-inline-block py-1 text-center truncate small ' +
           (column.class || '') +
           ' ' +
           (column.disableSorting ? ' sorting-disabled' : ''),
--- a/client/src/components/usermanagement/Usermanagement.vue	Mon Apr 01 10:46:18 2019 +0200
+++ b/client/src/components/usermanagement/Usermanagement.vue	Mon Apr 01 11:51:03 2019 +0200
@@ -8,7 +8,7 @@
           <UITableHeader
             :columns="[
               { id: 'role', title: `${roleForColumLabel}`, class: 'col-1' },
-              { id: 'user', title: `${usernameLabel}`, class: 'col-3' },
+              { id: 'user', title: `${usernameLabel}`, class: 'col-4' },
               { id: 'country', title: `${countryLabel}`, class: 'col-2' },
               { id: 'email', title: `${emailLabel}`, class: 'col-3' }
             ]"
@@ -17,60 +17,68 @@
             :data="users | sortTable(sortColumn, sortDirection, page, pageSize)"
             maxHeight="47rem"
             :active="currentUser"
-            v-slot="{ item: user }"
           >
-            <div class="py-1 col-1" @click="selectUser(user.user)">
-              <font-awesome-icon
-                v-tooltip="roleLabel(user.role)"
-                :icon="roleIcon(user.role)"
-                class="fa-lg"
-              ></font-awesome-icon>
-            </div>
-            <div class="py-1 col-3" @click="selectUser(user.user)">
-              {{ user.user }}
-            </div>
-            <div class="py-1 col-2" @click="selectUser(user.user)">
-              {{ user.country }}
-            </div>
-            <div class="py-1 col-3" @click="selectUser(user.user)">
-              {{ user.email }}
-            </div>
-            <div class="py-1 col text-right">
+            <template v-slot:row="{ item: user }">
+              <div
+                class="table-cell center col-1"
+                @click="selectUser(user.user)"
+              >
+                <font-awesome-icon
+                  v-tooltip="roleLabel(user.role)"
+                  :icon="roleIcon(user.role)"
+                  class="fa-lg"
+                ></font-awesome-icon>
+              </div>
+              <div class="table-cell col-4" @click="selectUser(user.user)">
+                {{ user.user }}
+              </div>
+              <div
+                class="table-cell center col-2"
+                @click="selectUser(user.user)"
+              >
+                {{ user.country }}
+              </div>
+              <div class="table-cell col-3" @click="selectUser(user.user)">
+                {{ user.email }}
+              </div>
+              <div class="table-cell col text-right justify-content-end">
+                <button
+                  @click="sendTestMail(user.user)"
+                  class="btn btn-xs btn-dark mr-1"
+                  v-tooltip="sendMailLabel"
+                  v-if="user.email"
+                >
+                  <font-awesome-icon icon="paper-plane" fixed-width />
+                </button>
+                <button
+                  @click="deleteUser(user.user)"
+                  class="btn btn-xs btn-dark"
+                  v-tooltip="deleteUserLabel"
+                >
+                  <font-awesome-icon icon="trash" fixed-width />
+                </button>
+              </div>
+            </template>
+          </UITableBody>
+          <div class="p-3 border-top d-flex justify-content-between">
+            <div></div>
+            <div>
               <button
-                @click="sendTestMail(user.user)"
-                class="btn btn-xs btn-dark mr-1"
-                v-tooltip="sendMailLabel"
-                v-if="user.email"
+                @click="prevPage"
+                v-if="this.page !== 1"
+                class="mr-2 btn btn-sm btn-light align-self-center"
               >
-                <font-awesome-icon icon="paper-plane" fixed-width />
+                <font-awesome-icon icon="angle-left"></font-awesome-icon>
               </button>
+              {{ this.page }} / {{ this.pages }}
               <button
-                @click="deleteUser(user.user)"
-                class="btn btn-xs btn-dark"
-                v-tooltip="deleteUserLabel"
+                @click="nextPage"
+                v-if="this.page !== this.pages"
+                class="ml-2 btn btn-sm btn-light align-self-center"
               >
-                <font-awesome-icon icon="trash" fixed-width />
+                <font-awesome-icon icon="angle-right"></font-awesome-icon>
               </button>
             </div>
-          </UITableBody>
-          <div class="d-flex mx-auto align-items-center">
-            <button
-              @click="prevPage"
-              v-if="this.page !== 1"
-              class="mr-2 btn btn-sm btn-light align-self-center"
-            >
-              <font-awesome-icon icon="angle-left"></font-awesome-icon>
-            </button>
-            {{ this.page }} / {{ this.pages }}
-            <button
-              @click="nextPage"
-              v-if="this.page !== this.pages"
-              class="ml-2 btn btn-sm btn-light align-self-center"
-            >
-              <font-awesome-icon icon="angle-right"></font-awesome-icon>
-            </button>
-          </div>
-          <div class="mr-3 py-3 text-right">
             <button @click="addUser" class="btn btn-info addbutton shadow-sm">
               <translate>Add User</translate>
             </button>
@@ -83,11 +91,6 @@
 </template>
 
 <style lang="sass" scoped>
-.addbutton
-  position: absolute
-  bottom: $offset
-  right: $offset
-
 .content
   width: 100%
 
@@ -109,16 +112,6 @@
 
 .userlistextended
   width: 100%
-
-.table-body
-  /deep/.row
-    > div
-      transition: background-color 0.3s, color 0.3s
-    &.active
-      background-color: $color-info
-      color: #fff
-      a
-        color: #fff !important
 </style>
 
 <script>