Merge pull request #313 from SAP-samples/browser

Data Browser: UI improvements, cosmetics
This commit is contained in:
Christian Georgi
2022-01-31 20:24:38 +01:00
committed by GitHub
2 changed files with 149 additions and 122 deletions

View File

@@ -3,7 +3,7 @@ const GET = (url) => axios.get('/-data'+url)
const storageGet = (key, def) => localStorage.getItem('data-viewer:'+key) || def const storageGet = (key, def) => localStorage.getItem('data-viewer:'+key) || def
const storageSet = (key, val) => localStorage.setItem('data-viewer:'+key, val) const storageSet = (key, val) => localStorage.setItem('data-viewer:'+key, val)
const viewer = new Vue ({ const vue = new Vue ({
el:'#app', el:'#app',
@@ -15,58 +15,79 @@ const viewer = new Vue ({
entities: [], entities: [],
columns: [], columns: [],
data: [], data: [],
rowDetails: undefined, rowDetails: {},
rowKey: storageGet('rowKey')
}, },
watch: { watch: {
dataSource: (v) => { storageSet('data-source', v); viewer.fetchEntities() }, dataSource: (v) => { storageSet('data-source', v); vue.fetchEntities() },
skip: (v) => { storageSet('skip', v); if (viewer.entity) viewer.fetchData() }, skip: (v) => { storageSet('skip', v); if (vue.entity) vue.fetchData() },
top: (v) => { storageSet('top', v); if (viewer.entity) viewer.fetchData() }, top: (v) => { storageSet('top', v); if (vue.entity) vue.fetchData() },
}, },
methods: { methods: {
async fetchEntities () { async fetchEntities () {
let url = `/Entities` let url = `/Entities`
if (viewer.dataSource === 'db') url += `?dataSource=db` if (vue.dataSource === 'db') url += `?dataSource=db`
const {data} = await GET(url) const {data} = await GET(url)
viewer.entities = data.value vue.entities = data.value
const entity = viewer.entity && viewer.entities.find(e => e.name === viewer.entity.name) const entity = vue.entity && vue.entities.find(e => e.name === vue.entity.name)
if (entity) { // restore selection from previous fetch if (entity) { // restore selection from previous fetch
viewer.columns = entity.columns vue.columns = entity.columns
await viewer.fetchData(entity) await vue.fetchData(entity)
} else { } else {
viewer.entity = undefined vue.entity = undefined
viewer.columns = [] vue.columns = []
viewer.data = [] vue.data = []
viewer.rowDetails = {} vue.rowDetails = {}
} }
}, },
async inspectEntity (eve) { async inspectEntity (eve) {
const entity = viewer.entity = viewer.entities [eve.currentTarget.rowIndex-1] const entity = vue.entity = vue.entities [eve.currentTarget.rowIndex-1]
storageSet('entity', JSON.stringify(entity)) storageSet('entity', JSON.stringify(entity))
viewer.columns = viewer.entities.find(e => e.name === entity.name).columns vue.columns = vue.entities.find(e => e.name === entity.name).columns
return await this.fetchData() return await this.fetchData()
}, },
async fetchData () { async fetchData () {
let url = `/Data?entity=${viewer.entity.name}&$skip=${viewer.skip}&$top=${viewer.top}` let url = `/Data?entity=${vue.entity.name}&$skip=${vue.skip}&$top=${vue.top}`
if (viewer.dataSource === 'db') url += `&dataSource=db` if (vue.dataSource === 'db') url += `&dataSource=db`
const {data} = await GET(url) const {data} = await GET(url)
viewer.data = data.value.map(d => d.record.map(r => r.data)) vue.data = data.value.map(d => d.record.map(r => r.data))
viewer.rowDetails = undefined const row = vue.data.find(data => vue._makeRowKey(data) === vue.rowKey)
if (row) vue._setRowDetails(row)
else vue.rowDetails = {}
}, },
inspectRow (eve) { inspectRow (eve) {
viewer.rowDetails = {} vue.rowDetails = {}
const selectedRow = eve.currentTarget.rowIndex-1 const selectedRow = eve.currentTarget.rowIndex-1
viewer.data[selectedRow].forEach((line, colIndex) => { vue.rowKey = vue._makeRowKey(vue.data[selectedRow])
viewer.rowDetails[viewer.columns[colIndex].name] = line storageSet('rowKey', vue.rowKey)
vue._setRowDetails(vue.data[selectedRow])
},
_setRowDetails(row) {
vue.rowDetails = {}
row.forEach((line, colIndex) => {
vue.rowDetails[vue.columns[colIndex].name] = line
}) })
}, },
_makeRowKey(row) {
// to identify a row, build a key string out of all key columns' values
return row
.filter((_, colIndex) => vue.columns[colIndex] && vue.columns[colIndex].isKey)
.reduce(((prev, next) => prev += next), '')
},
isActiveRow(row) {
return vue._makeRowKey(row) === vue.rowKey
}
} }
}) })
viewer.fetchEntities() vue.fetchEntities()

View File

@@ -7,8 +7,9 @@
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="https://cdn.jsdelivr.net/npm/vue"></script>
<style> <style>
.noscroll { overflow: hidden; }
.hovering tr:hover td { background: #ebeefc; cursor: pointer; } .hovering tr:hover td { background: #ebeefc; cursor: pointer; }
.highlight { background: #ebeefc; } .highlight { background: #ebeefc !important; }
.rating-stars { color:teal } .rating-stars { color:teal }
.succeeded { color:teal } .succeeded { color:teal }
.failed { color:red } .failed { color:red }
@@ -17,14 +18,17 @@
.not-key { font-weight: lighter;} .not-key { font-weight: lighter;}
.with-sidebar { display: flex; flex-wrap: wrap; gap: 1rem; } .with-sidebar { display: flex; flex-wrap: wrap; gap: 1rem; }
.sidebar { flex-basis: 20rem; flex-grow: 1; } .sidebar { flex-basis: 20rem; flex-grow: 1; }
.not-sidebar { flex-basis: 0; flex-grow: 999; min-inline-size: 50%;} .sidebar-main { height: 100vh; overflow-y: scroll; }
.not-sidebar { flex-basis: 0; flex-grow: 999; min-inline-size: 50%; align-items: stretch;}
.not-sidebar-main { max-height: 40vh; overflow-y: scroll; }
.not-sidebar-sub { max-height: 40vh; overflow-y: scroll; }
.horizontal label { display: inline; } .horizontal label { display: inline; }
.horizontal input { width: initial; display: inline; } .horizontal input { width: initial; display: inline; }
</style> </style>
</head> </head>
<body> <body class="noscroll">
<div id='app' class="container"> <div id='app' class="full-container">
<h1> {{ document.title }}{{ entity ? ' &ndash; ' + entity.name : '' }}</h1> <h1> {{ document.title }}{{ entity ? ' &ndash; ' + entity.name : '' }}</h1>
@@ -37,15 +41,17 @@
<input type="radio" id="dataSource-srv" value="service" v-model="dataSource"> <input type="radio" id="dataSource-srv" value="service" v-model="dataSource">
<label for="dataSource-srv">Service</label> <label for="dataSource-srv">Service</label>
</div> </div>
<div class="sidebar-main">
<table id='entities' class="hovering"> <table id='entities' class="hovering">
<thead> <thead>
<th>Entity Name</th> <th>Entity Name</th>
</thead> </thead>
<tr v-for="e in entities" v-bind:id="e.name" v-on:click="inspectEntity" :class="{'highlight': (entity && e.name === entity.name)}"> <tr v-for="e in entities" :key="e.name" @click="inspectEntity" :class="{'highlight': (entity && e.name === entity.name)}">
<td>{{ e.name }}</td> <td>{{ e.name }}</td>
</tr> </tr>
</table> </table>
</div> </div>
</div>
<div class="not-sidebar"> <div class="not-sidebar">
<div class="horizontal"> <div class="horizontal">
@@ -54,18 +60,18 @@
<label for="top">Top:</label> <label for="top">Top:</label>
<input id="top" v-model.lazy="top" title="No. of entries to read" type="number" min="0"> <input id="top" v-model.lazy="top" title="No. of entries to read" type="number" min="0">
</div> </div>
<div v-if="entity"> <div v-if="entity" class="not-sidebar-main">
<table id='data' class="hovering striped-table condensed"> <table id='data' class="hovering striped-table condensed">
<thead> <thead>
<th v-for="col in columns" v-bind:title="col.type" :class="[col.isKey ? 'key' : 'not-key']">{{ col.name }} </th> <th v-for="col in columns" :title="col.type" :class="[col.isKey ? 'key' : 'not-key']">{{ col.name }} </th>
</thead> </thead>
<tr v-for="(row, index) in data" v-on:click="inspectRow"> <tr v-for="row in data" @click="inspectRow" :class="{'highlight': isActiveRow(row)}">
<td v-for="d in row" v-bind:title="d">{{ d }}</td> <td v-for="d in row" :title="d">{{ d }}</td>
</tr> </tr>
</table> </table>
</div> </div>
<p></p>
<div v-if="rowDetails"> <div v-if="rowDetails" class="not-sidebar-sub">
<table id='rowDetails'> <table id='rowDetails'>
<tr v-for="(key, value) in rowDetails" > <tr v-for="(key, value) in rowDetails" >
<td class="key">{{ value }}</td> <td class="key">{{ value }}</td>