Skip to content

Commit

Permalink
new: Entities Table (#716)
Browse files Browse the repository at this point in the history
* Putting basic entity table components in place

* Entity odata resource and test data

* Refresh button and loading all entities at once

* Tests for entity metadata-row

* entity odata apiPath tests

* Tests for entity table

* Tests for entity list and dataset entities

* Addressing code review comments

* Moving translation strings aroung

* Simplifying test setup a bit

* Load dataset extended metadata and show entity count

* Fixed up a lot of tests

* Entity table odata modal tests
  • Loading branch information
ktuite authored Feb 24, 2023
1 parent 07dff80 commit 5f01f84
Show file tree
Hide file tree
Showing 23 changed files with 961 additions and 11 deletions.
75 changes: 75 additions & 0 deletions src/components/dataset/entities.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<!--
Copyright 2023 ODK Central Developers
See the NOTICE file at the top-level directory of this distribution and at
https://github.com/getodk/central-frontend/blob/master/NOTICE.
This file is part of ODK Central. It is subject to the license terms in
the LICENSE file found in the top-level directory of this distribution and at
https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central,
including this file, may be copied, modified, propagated, or distributed
except according to the terms contained in the LICENSE file.
-->

<template>
<div>
<page-section>
<template #heading>
<span>{{ $t('resource.entities') }}</span>
<odata-data-access @analyze="showModal('analyze')"/>
</template>
<template #body>
<entity-list :project-id="projectId" :dataset-name="datasetName"/>
</template>
</page-section>
<odata-analyze v-bind="analyze" :odata-url="odataUrl" @hide="hideModal('analyze')"/>
</div>
</template>

<script>
import OdataAnalyze from '../submission/analyze.vue';
import OdataDataAccess from '../odata/data-access.vue';
import EntityList from '../entity/list.vue';
import PageSection from '../page/section.vue';
import modal from '../../mixins/modal';
import { apiPaths } from '../../util/request';
export default {
name: 'DatasetEntities',
components: {
OdataAnalyze,
OdataDataAccess,
EntityList,
PageSection
},
mixins: [modal()],
props: {
projectId: {
type: String,
required: true
},
datasetName: {
type: String,
required: true
}
},
data() {
return {
analyze: {
state: false
}
};
},
computed: {
odataUrl() {
const path = apiPaths.odataEntitiesSvc(this.projectId, this.datasetName);
return `${window.location.origin}${path}`;
}
}
};
</script>

<style lang="scss">
#odata-data-access { float: right; }
</style>
7 changes: 7 additions & 0 deletions src/components/dataset/show.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ except according to the terms contained in the LICENSE file.
{{ $t('common.tab.overview') }}
</router-link>
</li>
<li v-if="canRoute(tabPath('entities'))" :class="tabClass('entities')"
role="presentation">
<router-link :to="tabPath('entities')">
{{ $t('common.data') }}
</router-link>
</li>
</template>
</page-head>
<page-body>
Expand Down Expand Up @@ -95,6 +101,7 @@ export default {
fetchDataset(resend) {
this.dataset.request({
url: apiPaths.dataset(this.projectId, this.datasetName),
extended: true,
resend
}).catch(noop);
},
Expand Down
36 changes: 36 additions & 0 deletions src/components/entity/data-row.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<!--
Copyright 2023 ODK Central Developers
See the NOTICE file at the top-level directory of this distribution and at
https://github.com/getodk/central-frontend/blob/master/NOTICE.
This file is part of ODK Central. It is subject to the license terms in
the LICENSE file found in the top-level directory of this distribution and at
https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central,
including this file, may be copied, modified, propagated, or distributed
except according to the terms contained in the LICENSE file.
-->
<template>
<tr>
<td v-for="property of properties" :key="property.id">
<span v-tooltip.text>{{ entity[property.name] }}</span>
</td>
<td><span v-tooltip.text>{{ entity.label }}</span></td>
<td>{{ entity.name }}</td>
</tr>
</template>

<script>
export default {
name: 'EntityDataRow',
props: {
entity: {
type: Object,
required: true
},
properties: {
type: Array,
required: true
}
}
};
</script>
144 changes: 144 additions & 0 deletions src/components/entity/list.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
<!--
Copyright 2023 ODK Central Developers
See the NOTICE file at the top-level directory of this distribution and at
https://github.com/getodk/central-frontend/blob/master/NOTICE.
This file is part of ODK Central. It is subject to the license terms in
the LICENSE file found in the top-level directory of this distribution and at
https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central,
including this file, may be copied, modified, propagated, or distributed
except according to the terms contained in the LICENSE file.
-->
<template>
<div id="entity-list">
<div id="entity-list-actions">
<button id="entity-list-refresh-button" type="button"
class="btn btn-default" :aria-disabled="refreshing"
@click="fetchData(false)">
<span class="icon-refresh"></span>{{ $t('action.refresh') }}
<spinner :state="refreshing"/>
</button>
<a id="entity-download-button" type="button" class="btn btn-primary" :href="href">
<span class="icon-arrow-circle-down"></span>{{ downloadText }}
</a>
</div>
<entity-table v-show="odataEntities.dataExists && odataEntities.value.length !== 0"
:properties="dataset.properties"/>
<p v-show="odataEntities.dataExists && odataEntities.value.length === 0"
class="empty-table-message">
{{ $t('noEntities') }}
</p>
<loading :state="odataEntities.initiallyLoading"/>
</div>
</template>

<script>
import Loading from '../loading.vue';
import Spinner from '../spinner.vue';
import EntityTable from './table.vue';
import useEntities from '../../request-data/entities';
import { useRequestData } from '../../request-data';
import { apiPaths } from '../../util/request';
import { noop } from '../../util/util';
export default {
name: 'EntityList',
components: {
Loading,
Spinner,
EntityTable
},
props: {
projectId: {
type: String,
required: true
},
datasetName: {
type: String,
required: true
}
},
setup() {
// The dataset request object is how we get access to the
// dataset properties for the columns.
const { dataset } = useRequestData();
const odataEntities = useEntities();
return { dataset, odataEntities };
},
data() {
return {
refreshing: false
};
},
computed: {
href() {
return apiPaths.entities(this.projectId, this.datasetName);
},
downloadText() {
return !this.odataEntities.dataExists
? this.$t('action.download')
: this.$tcn('action.download.unfiltered', this.odataEntities.count);
}
},
created() {
this.fetchData(true);
},
methods: {
fetchData(clear) {
this.refreshing = !clear;
this.odataEntities.request({
url: apiPaths.odataEntities(
this.projectId,
this.datasetName,
{ $count: true }
),
clear
})
.finally(() => { this.refreshing = false; })
.catch(noop);
}
}
};
</script>

<style lang="scss">
@import '../../assets/scss/variables';
#entity-list {
// Make sure that there is enough space for the DateRangePicker when it is
// open.
min-height: 375px;
}
#entity-list-actions {
align-items: baseline;
display: flex;
flex-wrap: wrap-reverse;
}
#entity-list-refresh-button {
margin-right: 5px;
}
#entity-download-button {
// The bottom margin is for if the download button wraps above the other
// actions.
margin-bottom: 10px;
margin-left: auto;
}
</style>

<i18n lang="json5">
{
"en": {
"action": {
"download": {
// This is the text of a button shown when the count of Entities is known.
"unfiltered": "Download {count} Entity | Download {count} Entities",
}
},
// This text is shown when there are no Entities to show in a table.
"noEntities": "There are no Entities to show."
}
}
</i18n>
59 changes: 59 additions & 0 deletions src/components/entity/metadata-row.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!--
Copyright 2023 ODK Central Developers
See the NOTICE file at the top-level directory of this distribution and at
https://github.com/getodk/central-frontend/blob/master/NOTICE.
This file is part of ODK Central. It is subject to the license terms in
the LICENSE file found in the top-level directory of this distribution and at
https://www.apache.org/licenses/LICENSE-2.0. No part of ODK Central,
including this file, may be copied, modified, propagated, or distributed
except according to the terms contained in the LICENSE file.
-->
<template>
<tr class="entity-metadata-row">
<td class="row-number">{{ $n(rowNumber, 'noGrouping') }}</td>
<td class="creator-name">
<span v-tooltip.text>{{ entity.__system.creatorName }}</span>
</td>
<td><date-time :iso="entity.__system.createdAt"/></td>
</tr>
</template>

<script>
import DateTime from '../date-time.vue';
export default {
name: 'EntityMetadataRow',
components: { DateTime },
props: {
entity: {
type: Object,
required: true
},
rowNumber: {
type: Number,
required: true
}
}
};
</script>

<style lang="scss">
@import '../../assets/scss/mixins';
.entity-metadata-row {
// TODO: move .row-number to app.css
.row-number {
color: #999;
font-size: 11px;
padding-top: 11px;
text-align: right;
vertical-align: middle;
}
.creator-name {
@include text-overflow-ellipsis;
max-width: 250px;
}
}
</style>
Loading

0 comments on commit 5f01f84

Please sign in to comment.