Skip to content

Commit

Permalink
feat(ui): preview values when importing data source #3837
Browse files Browse the repository at this point in the history
  • Loading branch information
ymarcon committed May 12, 2024
1 parent cf1e7a3 commit 31dac8a
Show file tree
Hide file tree
Showing 7 changed files with 236 additions and 20 deletions.
2 changes: 1 addition & 1 deletion opal-ui/src/components/datasource/DatasourceTables.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ export default defineComponent({
import { TableDto, TimestampsDto } from 'src/models/Magma';
import AddTableDialog from 'src/components/datasource/AddTableDialog.vue';
import AddTablesDialog from 'src/components/datasource/AddTablesDialog.vue';
import ImportDataDialog from 'src/components/datasource/ImportDataDialog.vue';
import ImportDataDialog from 'src/components/datasource/import/ImportDataDialog.vue';
import CopyTablesDialog from 'src/components/datasource/CopyTablesDialog.vue';
import ConfirmDialog from 'src/components/ConfirmDialog.vue';
import { tableStatusColor } from 'src/utils/colors';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,19 +96,28 @@
:title="$t('preview_import_source')"
icon="table_chart"
>
<q-select
v-show="transientDatasourceStore.datasource?.table?.length > 1"
v-model="selectedTable"
:options="transientDatasourceStore.datasource.table"
:label="$t('tables')"
dense
@update:model-value="onTableSelection"
class="q-mb-md"/>
<table-preview
v-if="transientDatasourceStore.datasource"
:table="transientDatasourceStore.table"
:variables="transientDatasourceStore.variables"
:loading="variablesLoading" />
<div v-if="transientDatasourceStore.datasource.table">
<q-select
v-show="transientDatasourceStore.datasource?.table?.length > 1"
v-model="selectedTable"
:options="transientDatasourceStore.datasource.table"
:label="$t('tables')"
dense
@update:model-value="onTableSelection"
class="q-mb-md"/>
<table-preview
v-if="transientDatasourceStore.datasource"
:table="transientDatasourceStore.table"
:variables="transientDatasourceStore.variables"
:loading="variablesLoading" />
</div>
<div v-else>
<q-spinner
color="grey-6"
size="3em"
:thickness="5"
/>
</div>
</q-step>
</q-stepper>
</q-card-section>
Expand Down Expand Up @@ -167,7 +176,7 @@ import FileSelect from 'src/components/files/FileSelect.vue';
import ImportCsvForm from 'src/components/datasource/import/ImportCsvForm.vue';
import ImportFsForm from 'src/components/datasource/import/ImportFsForm.vue';
import ImportHavenForm from 'src/components/datasource/import/ImportHavenForm.vue';
import TablePreview from 'src/components/datasource/TablePreview.vue';
import TablePreview from 'src/components/datasource/preview/TablePreview.vue';
import { notifyError, notifySuccess } from 'src/utils/notify';
interface DialogProps {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@
:variables="props.variables" :loading="props.loading" />
</q-tab-panel>
<q-tab-panel name="values">
<pre>{{ props.table }}</pre>
<values-list
:table="props.table"
:variables="props.variables" />
</q-tab-panel>
</q-tab-panels>
</div>
Expand All @@ -32,7 +34,8 @@ export default defineComponent({
});
</script>
<script setup lang="ts">
import VariablesList from 'src/components/datasource/VariablesList.vue';
import VariablesList from 'src/components/datasource/preview/VariablesList.vue';
import ValuesList from 'src/components/datasource/preview/ValuesList.vue';
import { TableDto, VariableDto } from 'src/models/Magma';
interface TablePreviewProps {
Expand Down
190 changes: 190 additions & 0 deletions opal-ui/src/components/datasource/preview/ValuesList.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<template>
<div>
<q-table
ref="tableRef"
flat
:rows="rows"
:columns="columns"
row-key="name"
v-model:pagination="pagination"
:rows-per-page-options="[5, 10, 20, 50, 100]"
:loading="loading"
:visible-columns="visibleColumns"
@request="onRequest"
class="table-values"
>
<template v-slot:top>
<div class="row q-gutter-sm">
<q-select
v-model="visibleColumns"
multiple
flat
dense
options-dense
size="sm"
display-value=""
:label="$t('select_columns')"
emit-value
map-options
:options="selectableColumns"
option-value="name"
options-cover
use-input
@filter="onFilter"
style="min-width: 150px"
@popup-hide="onVariableSelection"
>
<template v-slot:option="{ itemProps, opt, selected, toggleOption }">
<q-item v-bind="itemProps" v-if="!opt.required">
<q-item-section>
<q-item-label>{{ opt.label }}</q-item-label>
</q-item-section>
<q-item-section side>
<q-toggle :model-value="selected" @update:model-value="toggleOption(opt)" />
</q-item-section>
</q-item>
</template>
</q-select>
</div>
</template>
<template v-slot:header="props">
<q-tr :props="props">
<q-th
v-for="col in props.cols"
:key="col.name"
:props="props"
>
<span>{{ col.label }}</span>
<div v-if="col.variable" class="text-grey-5 text-caption">
<span>{{ col.variable.valueType }}</span>
<q-badge
v-if="col.variable.isRepeatable && col.variable.occurrenceGroup"
color="grey-6 on-right">
{{ col.variable.occurrenceGroup }}
</q-badge>
</div>
</q-th>
</q-tr>
</template>
<template v-slot:body-cell-_id="props">
<q-td :props="props" auto-width>
<span class="text-bold">{{ props.value }}</span>
</q-td>
</template>
<template v-for="col in visibleColumns" v-slot:[`body-cell-${col}`]="props" :key="col">
<q-td :props="props">
<value-cell :value="props.value" :variable="getVariable(col)" />
</q-td>
</template>
</q-table>
</div>
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { QTableColumn } from 'quasar';
export default defineComponent({
name: 'ValuesList',
});
</script>
<script setup lang="ts">
import { TableDto, ValueSetsDto, VariableDto } from 'src/models/Magma';
import ValueCell from 'src/components/datasource/ValueCell.vue';
import { t } from 'src/boot/i18n';
interface ValuesListProps {
table: TableDto;
variables: VariableDto[];
}
const props = defineProps<ValuesListProps>();
const transientDatasourceStore = useTransientDatasourceStore();
const tableRef = ref();
const loading = ref(false);
const pagination = ref({
sortBy: 'desc',
descending: false,
page: 1,
rowsPerPage: 5,
rowsNumber: 0,
});
const COLUMNS_COUNT = 20;
const rows = ref([]);
const columns = ref<QTableColumn[]>([]);
const visibleColumns = ref<string[]>([]);
const varFilter = ref<string>('');
const selectableColumns = computed(() => columns.value.filter((c) => c.name !== 'ID' && (varFilter.value === '' || c.name.toLowerCase().indexOf(varFilter.value) > -1)));
onMounted(() => {
init();
});
function init() {
pagination.value.rowsNumber = props.table.valueSetCount ? props.table.valueSetCount : 0;
columns.value = props.variables.map((v) => ({
name: v.name,
label: v.name,
align: 'left',
field: v.name,
required: false,
variable: v,
}));
visibleColumns.value = columns.value.map((c) => c.name).slice(0, COLUMNS_COUNT);
columns.value.unshift({
name: '_id',
required: true,
label: t('id'),
align: 'left',
field: '_id',
format: (val: string) => val,
sortable: false,
});
tableRef.value.requestServerInteraction();
}
function onFilter(val: string, update, abort) {
update(() => {
varFilter.value = val.toLowerCase();
})
}
function onRequest(props) {
const { page, rowsPerPage, sortBy, descending } = props.pagination;
const offset = (page - 1) * rowsPerPage;
const limit = rowsPerPage;
const select = visibleColumns.value;
loading.value = true;
transientDatasourceStore.loadValueSets(offset, limit, select).then((res: ValueSetsDto) => {
if (res.valueSets) {
rows.value = res.valueSets.map((vs) => {
const row = { _id: vs.identifier };
vs.values.forEach((val, idx: number) => {
row[res.variables[idx]] = val;
});
return row;
});
} else {
rows.value = [];
}
// don't forget to update local pagination object
pagination.value.page = page
pagination.value.rowsPerPage = rowsPerPage
pagination.value.sortBy = sortBy
pagination.value.descending = descending
loading.value = false;
});
}
function onVariableSelection() {
tableRef.value.requestServerInteraction();
}
function getVariable(name: string) {
return props.variables.find((v) => v.name === name);
}
</script>
6 changes: 3 additions & 3 deletions opal-ui/src/pages/ProjectTasksPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</q-toolbar>
<q-page class="q-pa-md">
<command-states
:commands="commandStates"
:commands="commands"
:project="projectsStore.project.name"
@refresh="onRefresh"
@clear="onClear"
Expand Down Expand Up @@ -48,14 +48,14 @@ onMounted(() => {
})
});
const commandStates = computed(() => projectsStore.commandStates ? projectsStore.commandStates : []);
const commands = computed(() => projectsStore.commandStates ? projectsStore.commandStates : []);
function onRefresh() {
projectsStore.loadCommandStates();
};
function onClear(command: CommandStateDto) {
selectedToClear.value = command ? [command] : commandStates.value;
selectedToClear.value = command ? [command] : commands.value;
showConfirmClear.value = true;
};
Expand Down
14 changes: 14 additions & 0 deletions opal-ui/src/stores/transient-datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ export const useTransientDatasourceStore = defineStore('transientDatasource', ()
});
}

function loadValueSets(offset: number, limit: number, select: string[] | undefined) {
const params = { offset, limit };
if (select && select.length > 0) {
params.select = `name().matches(/^${select.join('$|^')}$/)`
}
return api
.get(`/datasource/${datasource.value.name}/table/${table.value.name}/valueSets`, { params })
.then((response) => {
return response.data;
});
}


return {
project,
datasource,
Expand All @@ -58,5 +71,6 @@ export const useTransientDatasourceStore = defineStore('transientDatasource', ()
deleteDatasource,
loadTable,
loadVariables,
loadValueSets,
};
});

0 comments on commit 31dac8a

Please sign in to comment.