cartodb-4.42/lib/assets/javascripts/new-dashboard/pages/Data/CatalogDatasetData.vue
2024-04-06 05:25:13 +00:00

410 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="!isFetching" class="grid grid-cell u-flex__justify--center">
<div class="grid-cell grid-cell--col12 u-mt--28">
<div class="u-flex u-flex__justify--between title-container">
<h2 class="title is-caption is-txtMainTextColor">
Data sample
<transition name="fade">
<a
v-if="variables && variables.length > 0"
@click="scrollToVariables()"
class="is-small"
>View {{ numberColumns }} variables list</a
>
</transition>
</h2>
<div class="is-small text txtMainTextColor">
<span class="source u-flex u-flex__align--center" style="white-space:pre-wrap;" v-if="source"
>(*) Sample not available: this preview is for&nbsp;
<i class="is-semibold is-italic">{{ source }}</i></span
>
<span class="grey" v-else-if="numberRows > 0"
>First {{ numberRows }} rows</span
>
<span class="grey" v-else>Not available</span>
</div>
</div>
<div class="table-wrapper" ref="tableWrapper" v-if="columns.length">
<div
class="tooltip is-small"
:class="{ first: tooltip.isFirst, last: tooltip.isLast }"
v-if="tooltip.visible"
:style="{ left: tooltip.left + 'px' }"
>
<p class="text is-small">
<span class="is-semibold">Description:</span>
{{ tooltip.description }}
</p>
<p class="text is-small">
<span class="is-semibold">Type:</span> {{ tooltip.type }}
</p>
</div>
<div v-if="numberRows > 0" class="scrollable-table u-mt--24">
<table class="text is-small u-width--100">
<tr>
<th></th>
<th
@mousemove="showTooltip(value, $event)"
@mouseleave="hideTooltip"
v-for="value in columns"
:key="value"
>
{{ value }}
</th>
</tr>
<tr v-for="n in numberRows" :key="n">
<td class="is-semibold">{{ n - 1 }}</td>
<td v-for="sample of tableSample" :key="sample.column_name">
<template v-if="sample.column_name !== 'geom'">
<span
v-if="
sample.values[n - 1] !== null &&
sample.values[n - 1] !== undefined
"
>{{ sample.values[n - 1] }}</span
>
<span v-else class="is-txtLightGrey is-italic">null</span>
</template>
<template v-else>
<span>GeoJSON</span>
</template>
</td>
</tr>
</table>
</div>
</div>
<NotAvailable
v-else
:title="'Sample is not available'"
:description="
'This data sample cant be shown because the real dataset only contains a few rows.'
"
:contactUrl="getFormURL()"
:mode="'contact'"
></NotAvailable>
</div>
<transition name="fade">
<div
v-if="variables && variables.length > 0"
class="grid-cell--col12 u-mt--60"
ref="variablesSection"
>
<div class="u-flex u-flex__align--center u-flex__justify--between">
<h2 class="grid-cell title is-caption is-txtMainTextColor">
Variables
</h2>
<span class="is-txtMidGrey text is-small u-pr--10">{{numberColumns}} variables</span>
</div>
<ul class="u-mt--24 text f12 is-small is-txtMainTextColor">
<li class="grid title is-txtMidGrey header-row">
<div class="grid-cell grid-cell--col4">Column Name</div>
<div
class="grid-cell grid-cell--col7 grid-cell--col6--tablet grid-cell--col5--mobile"
>
Description
</div>
<div
class="grid-cell grid-cell--col1 grid-cell--col2--tablet grid-cell--col3--mobile"
>
Type
</div>
</li>
<li
class="grid info-row"
v-for="variable in variables"
:key="variable.slug"
>
<div class="grid-cell grid-cell--col4 is-semibold name-cell">
{{ variable.column_name }}
</div>
<div
class="grid-cell grid-cell--col7 grid-cell--col6--tablet grid-cell--col5--mobile"
>
{{ variable.description }}
</div>
<div
class="grid-cell grid-cell--col1 grid-cell--col2--tablet grid-cell--col3--mobile"
>
{{ variable.db_type }}
</div>
</li>
</ul>
</div>
</transition>
</div>
</template>
<script>
import { mapState } from 'vuex';
import NotAvailable from 'new-dashboard/components/Catalog/NotAvailable.vue';
import { formURL } from 'new-dashboard/utils/catalog/form-url';
import { sendCustomDimensions } from 'new-dashboard/utils/catalog/custom-dimensions-ga';
export default {
name: 'CatalogDatasetData',
components: {
NotAvailable
},
data () {
return {
tooltip: {
visible: false,
isFirst: false,
isLast: false,
left: 0,
description: null,
type: null
}
};
},
watch: {
dataset: {
handler (value) {
if (value && value.category_name) {
sendCustomDimensions(
value.category_name,
value.country_name,
value.is_public_data,
value.provider_name
);
}
},
immediate: true
}
},
computed: {
...mapState({
dataset: state => state.catalog.dataset,
variables: state => state.catalog.variables,
isFetching: state => state.catalog.isFetching
}),
isPublicWebsite () {
return !(this.$store.state.user && this.$store.state.user.id);
},
tableKey () {
if (this.dataset && this.dataset.summary_json) {
if (this.dataset.summary_json.ordered_glimpses) {
return 'ordered_glimpses';
} else if (this.dataset.summary_json.default_ordered_glimpses) {
return 'default_ordered_glimpses';
}
}
return null;
},
source () {
if (this.tableKey === 'default_ordered_glimpses') {
return this.dataset.summary_json[this.tableKey].source;
}
return null;
},
tableSample () {
if (this.tableKey && this.dataset.summary_json) {
const geom_column = { column_name: 'geom', values: Array(10) };
return [...this.dataset.summary_json[this.tableKey].tail, geom_column];
}
return [];
},
columns () {
return this.tableSample ? this.tableSample.map(t => t.column_name) : [];
},
numberRows () {
// return this.columns.length ? this.tableSample[this.columns[0]].length : 0;
return this.tableSample && this.tableSample.length > 0 ? this.tableSample[0].values.length : 0;
},
numberColumns () {
return this.variables ? this.variables.length : this.columns.length;
},
isGeography () {
return this.$route.params.type === 'geography';
}
},
methods: {
fetchVariables () {
this.$store.dispatch('catalog/fetchVariables', {
id: this.$route.params.datasetId,
type: this.$route.params.type
});
},
findVariableInfo (variableName) {
return this.variables.find(e => e.column_name === variableName);
},
showTooltip (variableName, event) {
let tooltipInfo = this.findVariableInfo(variableName);
if (tooltipInfo) {
let tableBoundingSize = this.$refs.tableWrapper.getBoundingClientRect();
this.tooltip.left =
event.target.getBoundingClientRect().left -
this.$refs.tableWrapper.getBoundingClientRect().left +
event.offsetX;
if (this.tooltip.left < 140) {
this.tooltip.isFirst = true;
this.tooltip.left -= 26;
} else if (tableBoundingSize.width - this.tooltip.left < 120) {
this.tooltip.isLast = true;
this.tooltip.left += 26;
} else {
this.tooltip.isFirst = false;
this.tooltip.isLast = false;
}
this.tooltip.description = tooltipInfo.description;
this.tooltip.type = tooltipInfo.db_type;
this.tooltip.visible = true;
} else {
this.hideTooltip();
}
},
hideTooltip () {
this.tooltip.visible = false;
this.tooltip.isFirst = false;
this.tooltip.isLast = false;
},
getFormURL () {
return formURL(this.dataset);
},
scrollToVariables () {
window.scrollTo({
top: this.$refs.variablesSection.offsetTop,
left: 0,
behavior: 'smooth'
});
}
},
mounted () {
this.fetchVariables();
}
};
</script>
<style scoped lang="scss">
@import 'new-dashboard/styles/variables';
.title a {
margin-left: 26px;
}
a {
cursor: pointer;
}
.scrollable-table {
width: 100%;
overflow: auto;
td,
th {
padding: 12px 24px 12px 8px;
white-space: nowrap;
}
tr:nth-child(even) {
background-color: $color-primary--soft;
}
th {
position: relative;
color: $link__color;
text-align: left;
}
}
.info-row {
padding: 14px 0;
border-bottom: 1px solid $blue--100;
}
.name-cell {
overflow: hidden;
text-overflow: ellipsis;
}
.header-row {
padding-bottom: 12px;
border-bottom: 2px solid $blue--100;
}
.tooltip-container {
position: absolute;
z-index: 2;
bottom: 100%;
margin-left: 32px;
padding-bottom: 8px;
}
.table-wrapper {
position: relative;
}
.tooltip {
position: absolute;
bottom: calc(100% + 8px);
width: 300px;
padding: 12px 16px 8px;
transform: translateX(-50%);
border: 1px solid $border-color;
border-radius: 4px;
background-color: #fff;
word-break: break-word;
&::before {
content: '';
position: absolute;
bottom: -8px;
left: calc(50% - 12px); // To compensate right extra padding
width: 14px;
height: 14px;
transform: rotate(45deg);
border: 1px solid $neutral--200;
border-top: none;
border-left: none;
border-radius: 2px;
background-color: #fff;
}
&.first {
transform: translateX(0);
&::before {
left: 12px;
}
}
&.last {
transform: translateX(-100%);
&::before {
right: 24px;
left: auto;
}
}
}
.grey {
opacity: 0.48;
}
.source {
&:after {
content: url('../../assets/icons/catalog/interface-alert-triangle.svg');
margin-left: 12px;
}
}
@media (max-width: $layout-mobile) {
.title-container {
flex-wrap: wrap;
>h2 {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
>div {
width: 100%;
text-align: right;
}
}
}
</style>