From 23b7c23a80550a26a9f30027b7b2fc282e0e3681 Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Tue, 16 Apr 2024 08:39:20 +0200 Subject: [PATCH] Hotfix UI bugs --- .../at/tuwien/endpoints/TableEndpoint.java | 28 ++- .../endpoints/TableEndpointUnitTest.java | 84 ++++++- .../tuwien/mvc/PrometheusEndpointMvcTest.java | 2 +- .../main/java/at/tuwien/test/BaseTest.java | 2 +- dbrepo-ui/components/identifier/Persist.vue | 31 +-- dbrepo-ui/components/subset/Builder.vue | 2 - dbrepo-ui/components/subset/Results.vue | 16 +- dbrepo-ui/components/table/TableImport.vue | 20 +- dbrepo-ui/components/table/TableSchema.vue | 28 +-- dbrepo-ui/composables/query-service.ts | 2 +- dbrepo-ui/composables/table-service.ts | 26 +- dbrepo-ui/composables/view-service.ts | 2 +- dbrepo-ui/locales/de-AT.json | 193 ++++++++------- dbrepo-ui/locales/en-US.json | 225 +++++++++--------- .../[database_id]/table/[table_id]/data.vue | 135 ++++++----- .../database/[database_id]/table/import.vue | 55 +++-- .../[database_id]/view/[view_id]/data.vue | 19 +- 17 files changed, 485 insertions(+), 385 deletions(-) diff --git a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java index ec1f5f655c..d69c6e7319 100644 --- a/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java +++ b/dbrepo-metadata-service/rest-service/src/main/java/at/tuwien/endpoints/TableEndpoint.java @@ -34,6 +34,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; import java.security.Principal; +import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -83,15 +84,26 @@ public class TableEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<List<TableBriefDto>> list(@NotNull @PathVariable("databaseId") Long databaseId, - Principal principal) + Principal principal, + @RequestParam(required = false) String internalName) throws DatabaseNotFoundException, NotAllowedException, AccessDeniedException { - log.debug("endpoint list tables, databaseId={}, {}", databaseId, PrincipalUtil.formatForDebug(principal)); + log.debug("endpoint list tables, databaseId={}, internalName={} {}", databaseId, internalName, + PrincipalUtil.formatForDebug(principal)); endpointValidator.validateOnlyPrivateAccess(databaseId, principal); endpointValidator.validateOnlyPrivateHasRole(databaseId, principal, "list-tables"); - final List<TableBriefDto> dto = tableService.findAll(databaseId) - .stream() - .map(tableMapper::tableToTableBriefDto) - .collect(Collectors.toList()); + List<TableBriefDto> dto = new LinkedList<>(); + if (internalName != null) { + try { + dto = List.of(tableMapper.tableToTableBriefDto(tableService.find(databaseId, internalName))); + } catch (TableNotFoundException e) { + /* ignore */ + } + } else { + dto = tableService.findAll(databaseId) + .stream() + .map(tableMapper::tableToTableBriefDto) + .collect(Collectors.toList()); + } log.trace("list tables resulted in tables {}", dto); return ResponseEntity.ok(dto); } @@ -129,8 +141,8 @@ public class TableEndpoint { schema = @Schema(implementation = ApiErrorDto.class))}), }) public ResponseEntity<TableDto> create(@NotNull @PathVariable("databaseId") Long databaseId, - @NotNull @Valid @RequestBody TableCreateDto createDto, - @NotNull Principal principal) + @NotNull @Valid @RequestBody TableCreateDto createDto, + @NotNull Principal principal) throws ImageNotSupportedException, DatabaseNotFoundException, TableMalformedException, TableNameExistsException, QueryMalformedException, NotAllowedException, AccessDeniedException, TableNotFoundException, UserNotFoundException { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java index a7ac48a931..acc968b8e1 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/endpoints/TableEndpointUnitTest.java @@ -66,7 +66,66 @@ public class TableEndpointUnitTest extends BaseUnitTest { at.tuwien.exception.AccessDeniedException { /* test */ - generic_list(DATABASE_3_ID, DATABASE_3, null, null, null); + final ResponseEntity<List<TableBriefDto>> response = generic_list(DATABASE_3_ID, DATABASE_3, null, null, null, + true, null); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + final List<TableBriefDto> body = response.getBody(); + assertEquals(1, body.size()); + } + + @Test + @WithAnonymousUser + public void list_publicAnonymousFilter_succeeds() throws NotAllowedException, DatabaseNotFoundException, + at.tuwien.exception.AccessDeniedException, TableNotFoundException { + + /* mock */ + when(tableService.find(eq(DATABASE_3_ID), anyString())) + .thenReturn(TABLE_8); + + /* test */ + final ResponseEntity<List<TableBriefDto>> response = generic_list(DATABASE_3_ID, DATABASE_3, null, null, null, + true, TABLE_8_INTERNAL_NAME); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + final List<TableBriefDto> body = response.getBody(); + assertEquals(1, body.size()); + } + + @Test + @WithAnonymousUser + public void list_publicAnonymousFilter_fails() throws NotAllowedException, DatabaseNotFoundException, + at.tuwien.exception.AccessDeniedException, TableNotFoundException { + + /* mock */ + doThrow(TableNotFoundException.class) + .when(tableService) + .find(eq(DATABASE_3_ID), anyString()); + + /* test */ + final ResponseEntity<List<TableBriefDto>> response = generic_list(DATABASE_3_ID, DATABASE_3, null, null, null, + true, "i_do_not_exist"); + assertEquals(HttpStatus.OK, response.getStatusCode()); + assertNotNull(response.getBody()); + final List<TableBriefDto> body = response.getBody(); + assertEquals(0, body.size()); + } + + @Test + @WithAnonymousUser + public void list_publicAnonymousFilterHead_fails() throws NotAllowedException, DatabaseNotFoundException, + at.tuwien.exception.AccessDeniedException, TableNotFoundException { + + /* mock */ + doThrow(TableNotFoundException.class) + .when(tableService) + .find(eq(DATABASE_3_ID), anyString()); + + /* test */ + final ResponseEntity<List<TableBriefDto>> response = generic_list(DATABASE_3_ID, DATABASE_3, null, null, null, + false, "i_do_not_exist"); + assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode()); + assertNull(response.getBody()); } @Test @@ -75,7 +134,7 @@ public class TableEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(DatabaseNotFoundException.class, () -> { - generic_list(DATABASE_3_ID, null, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_READ_ACCESS); + generic_list(DATABASE_3_ID, null, USER_1_ID, USER_1_PRINCIPAL, DATABASE_3_USER_1_READ_ACCESS, true, null); }); } @@ -85,7 +144,8 @@ public class TableEndpointUnitTest extends BaseUnitTest { at.tuwien.exception.AccessDeniedException { /* test */ - final ResponseEntity<List<TableBriefDto>> response = generic_list(DATABASE_3_ID, DATABASE_3, USER_1_ID, USER_1_PRINCIPAL, DATABASE_1_USER_1_READ_ACCESS); + final ResponseEntity<List<TableBriefDto>> response = generic_list(DATABASE_3_ID, DATABASE_3, USER_1_ID, + USER_1_PRINCIPAL, DATABASE_1_USER_1_READ_ACCESS, true, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final List<TableBriefDto> body = response.getBody(); assertNotNull(body); @@ -97,7 +157,7 @@ public class TableEndpointUnitTest extends BaseUnitTest { public void list_publicNoRole_succeeds() throws NotAllowedException, DatabaseNotFoundException, at.tuwien.exception.AccessDeniedException { /* test */ - generic_list(DATABASE_3_ID, DATABASE_3, USER_4_ID, USER_4_PRINCIPAL, null); + generic_list(DATABASE_3_ID, DATABASE_3, USER_4_ID, USER_4_PRINCIPAL, null, true, null); } @Test @@ -312,7 +372,7 @@ public class TableEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(NotAllowedException.class, () -> { - generic_list(DATABASE_1_ID, DATABASE_1, null, null, null); + generic_list(DATABASE_1_ID, DATABASE_1, null, null, null, true, null); }); } @@ -322,7 +382,7 @@ public class TableEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(DatabaseNotFoundException.class, () -> { - generic_list(DATABASE_1_ID, null, USER_1_ID, USER_1_PRINCIPAL, DATABASE_1_USER_1_READ_ACCESS); + generic_list(DATABASE_1_ID, null, USER_1_ID, USER_1_PRINCIPAL, DATABASE_1_USER_1_READ_ACCESS, true, null); }); } @@ -332,7 +392,8 @@ public class TableEndpointUnitTest extends BaseUnitTest { at.tuwien.exception.AccessDeniedException { /* test */ - final ResponseEntity<List<TableBriefDto>> response = generic_list(DATABASE_1_ID, DATABASE_1, USER_1_ID, USER_1_PRINCIPAL, DATABASE_1_USER_1_READ_ACCESS); + final ResponseEntity<List<TableBriefDto>> response = generic_list(DATABASE_1_ID, DATABASE_1, USER_1_ID, + USER_1_PRINCIPAL, DATABASE_1_USER_1_READ_ACCESS, true, null); assertEquals(HttpStatus.OK, response.getStatusCode()); final List<TableBriefDto> body = response.getBody(); assertNotNull(body); @@ -345,7 +406,7 @@ public class TableEndpointUnitTest extends BaseUnitTest { /* test */ assertThrows(AccessDeniedException.class, () -> { - generic_list(DATABASE_1_ID, DATABASE_1, USER_4_ID, USER_4_PRINCIPAL, null); + generic_list(DATABASE_1_ID, DATABASE_1, USER_4_ID, USER_4_PRINCIPAL, null, true, null); }); } @@ -505,7 +566,8 @@ public class TableEndpointUnitTest extends BaseUnitTest { /* ################################################################################################### */ protected ResponseEntity<List<TableBriefDto>> generic_list(Long databaseId, Database database, UUID userId, - Principal principal, DatabaseAccess access) + Principal principal, DatabaseAccess access, + boolean isGet, String internalName) throws DatabaseNotFoundException, NotAllowedException, at.tuwien.exception.AccessDeniedException { /* mock */ @@ -533,11 +595,11 @@ public class TableEndpointUnitTest extends BaseUnitTest { } /* test */ - return tableEndpoint.list(databaseId, principal); + return tableEndpoint.list(databaseId, principal, internalName); } protected ResponseEntity<TableDto> generic_create(Long databaseId, Database database, TableCreateDto data, - UUID userId, Principal principal, DatabaseAccess access) + UUID userId, Principal principal, DatabaseAccess access) throws DatabaseNotFoundException, NotAllowedException, TableMalformedException, QueryMalformedException, ImageNotSupportedException, TableNameExistsException, AccessDeniedException, TableNotFoundException, UserNotFoundException { diff --git a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java index d45d641f7c..af811b9aa2 100644 --- a/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java +++ b/dbrepo-metadata-service/rest-service/src/test/java/at/tuwien/mvc/PrometheusEndpointMvcTest.java @@ -625,7 +625,7 @@ public class PrometheusEndpointMvcTest extends BaseUnitTest { /* mock */ try { - tableEndpoint.list(DATABASE_1_ID, USER_1_PRINCIPAL); + tableEndpoint.list(DATABASE_1_ID, USER_1_PRINCIPAL, null); } catch (Exception e) { /* ignore */ } diff --git a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java index b8e43cdd23..ed6c52af46 100644 --- a/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java +++ b/dbrepo-metadata-service/test/src/main/java/at/tuwien/test/BaseTest.java @@ -1061,7 +1061,7 @@ public abstract class BaseTest { .name(DATABASE_3_NAME) .internalName(DATABASE_3_INTERNALNAME) .exchangeName(DATABASE_3_EXCHANGE) - .tables(List.of()) /* TABLE_3, TABLE_3, TABLE_3 */ + .tables(List.of()) /* TABLE_8 */ .views(List.of()) .build(); diff --git a/dbrepo-ui/components/identifier/Persist.vue b/dbrepo-ui/components/identifier/Persist.vue index 16649685e4..34ec6cf33a 100644 --- a/dbrepo-ui/components/identifier/Persist.vue +++ b/dbrepo-ui/components/identifier/Persist.vue @@ -24,7 +24,7 @@ color="primary" variant="flat" :loading="loading" - :disabled="!formValid || loading" + :disabled="!formValid || !validPublicationMonth || !validPublicationDay || loading" :text="($vuetify.display.xl ? $t('toolbars.identifier.update.xl') + ' ' : '') + $t('toolbars.identifier.update.permanent')" @click="save" /> </v-toolbar> @@ -57,11 +57,9 @@ :label="$t('pages.identifier.subpages.create.creators.identifier.label')" clearable :variant="inputVariant" - name="name-identifier" :hint="$t('pages.identifier.subpages.create.creators.identifier.hint')" :loading="creator.name_loading" persistent-hint - required @focusout="retrieveCreator(creator)" /> </v-col> <v-col cols="4"> @@ -100,7 +98,9 @@ </v-row> <v-row dense> <v-col cols="8"> - <v-radio-group v-model="creator.name_type" row> + <v-radio-group + v-model="creator.name_type" + row> <v-radio :label="$t('pages.identifier.subpages.create.creators.person.label')" value="Personal" /> @@ -121,7 +121,6 @@ :variant="inputVariant" :hint="$t('pages.identifier.subpages.create.creators.given-name.hint')" persistent-hint - required @focusout="suggestName(creator)" /> </v-col> </v-row> @@ -136,7 +135,6 @@ :variant="inputVariant" :hint="$t('pages.identifier.subpages.create.creators.family-name.hint')" persistent-hint - required @focusout="suggestName(creator)" /> </v-col> </v-row> @@ -158,7 +156,6 @@ <v-text-field v-model="creator.affiliation_identifier" :label="$t('pages.identifier.subpages.create.creators.affiliation-identifier.label')" - name="affiliation-identifier" :variant="inputVariant" :loading="creator.affiliation_loading" :hint="$t('pages.identifier.subpages.create.creators.affiliation-identifier.hint')" @@ -172,7 +169,6 @@ <v-text-field v-model="creator.affiliation" :label="$t('pages.identifier.subpages.create.creators.affiliation.label')" - name="affiliation" :variant="inputVariant" clearable :hint="$t('pages.identifier.subpages.create.creators.affiliation.hint')" @@ -248,8 +244,7 @@ variant="underlined" :items="titleType" item-title="value" - item-value="value" - required /> + item-value="value" /> </v-col> </v-row> <v-row dense> @@ -263,8 +258,7 @@ variant="underlined" :items="languages" item-title="name" - item-value="code" - required /> + item-value="code" /> </v-col> </v-row> </v-container> @@ -337,8 +331,7 @@ variant="underlined" :items="descriptionType" item-title="value" - item-value="value" - required /> + item-value="value" /> </v-col> </v-row> <v-row dense> @@ -352,8 +345,7 @@ variant="underlined" :items="languages" item-title="name" - item-value="code" - required /> + item-value="code" /> </v-col> </v-row> </v-container> @@ -384,7 +376,6 @@ <v-col cols="8"> <v-text-field v-model="identifier.publisher" - name="publisher" :variant="inputVariant" :label="$t('pages.identifier.subpages.create.publisher.label')" :hint="$t('pages.identifier.subpages.create.publisher.hint')" @@ -454,7 +445,6 @@ <v-col cols="4"> <v-text-field v-model="related.value" - name="related" :variant="inputVariant" :label="$t('pages.identifier.subpages.create.related-identifiers.identifier.label')" :hint="$t('pages.identifier.subpages.create.related-identifiers.identifier.hint')" @@ -578,8 +568,7 @@ persistent-hint :items="languages" item-title="name" - item-value="code" - required /> + item-value="code" /> </v-col> </v-row> </v-container> @@ -609,12 +598,10 @@ <v-text-field v-model="funder.funder_identifier" :label="$t('pages.identifier.subpages.create.funders.identifier.label')" - name="funder-identifier" :hint="$t('pages.identifier.subpages.create.funders.identifier.hint')" :loading="funder.loading" persistent-hint :variant="inputVariant" - required clearable @focusout="retrieveFunder(funder)" /> </v-col> diff --git a/dbrepo-ui/components/subset/Builder.vue b/dbrepo-ui/components/subset/Builder.vue index 0c64055308..3557dbbd9f 100644 --- a/dbrepo-ui/components/subset/Builder.vue +++ b/dbrepo-ui/components/subset/Builder.vue @@ -37,7 +37,6 @@ <v-card-text> <v-form ref="formView" - v-model="valid" @submit.prevent="prevent"> <v-row v-if="isView" @@ -347,7 +346,6 @@ export default { ], tableDetails: null, resultId: null, - valid: false, errorKeyword: null, query: { raw: null, diff --git a/dbrepo-ui/components/subset/Results.vue b/dbrepo-ui/components/subset/Results.vue index 8881c334c9..4ef5728245 100644 --- a/dbrepo-ui/components/subset/Results.vue +++ b/dbrepo-ui/components/subset/Results.vue @@ -1,13 +1,14 @@ <template> <div> - <v-data-table + <v-data-table-server flat :headers="headers" - :items="result.rows" :loading="loading > 0" - :options.sync="options" + :options="options" + :items="result.rows" + :items-length="total" :footer-props="footerProps" - :server-items-length="total" /> + @update:options="updateOptions" /> </div> </template> @@ -42,7 +43,7 @@ export default { showFirstLastPage: true, itemsPerPageOptions: [10, 25, 50, 100] }, - total: null + total: null, } }, computed: { @@ -150,6 +151,11 @@ export default { }) console.debug('query result', data) this.result.rows = data.result + }, + updateOptions ({ page, itemsPerPage, sortBy }) { + this.options.page = page + this.options.itemsPerPage = itemsPerPage + this.reExecute(this.id) } } } diff --git a/dbrepo-ui/components/table/TableImport.vue b/dbrepo-ui/components/table/TableImport.vue index 8923a45e81..72f6d922be 100644 --- a/dbrepo-ui/components/table/TableImport.vue +++ b/dbrepo-ui/components/table/TableImport.vue @@ -23,10 +23,16 @@ required clearable persistent-hint - :base-color="suggestedAnalyseSeparator && providedSeparator !== analysedSeparator ? 'warning' : ''" :variant="inputVariant" :hint="$t('pages.table.subpages.import.separator.hint')" - :label="$t('pages.table.subpages.import.separator.label')"/> + :label="$t('pages.table.subpages.import.separator.label')"> + <template + v-if="suggestedAnalyseSeparator && providedSeparator !== analysedSeparator" + v-slot:prepend> + <v-icon + color="warning">mdi-alert-outline</v-icon> + </template> + </v-select> </v-col> </v-row> <v-row dense> @@ -64,14 +70,20 @@ <v-select v-model="tableImport.line_termination" :items="lineTerminationItems" - :base-color="suggestedAnalyseLineTerminator && providedTerminator !== analysedTerminator ? 'warning' : ''" item-title="name" item-value="value" clearable persistent-hint :variant="inputVariant" :hint="$t('pages.table.subpages.import.terminator.hint')" - :label="$t('pages.table.subpages.import.terminator.label')"/> + :label="$t('pages.table.subpages.import.terminator.label')"> + <template + v-if="suggestedAnalyseLineTerminator && providedTerminator !== analysedTerminator" + v-slot:prepend> + <v-icon + color="warning">mdi-alert-outline</v-icon> + </template> + </v-select> </v-col> </v-row> <v-row dense> diff --git a/dbrepo-ui/components/table/TableSchema.vue b/dbrepo-ui/components/table/TableSchema.vue index db40298d40..f5bdc54a10 100644 --- a/dbrepo-ui/components/table/TableSchema.vue +++ b/dbrepo-ui/components/table/TableSchema.vue @@ -8,8 +8,7 @@ color="info" /> <v-form ref="form" - v-model="valid" - :disabled="disabled"> + v-model="valid"> <v-row v-for="(c, idx) in columns" :key="`r-${idx}`" @@ -27,7 +26,7 @@ :hint="$t('pages.table.subpages.schema.name.hint')" /> </v-col> <v-col cols="2"> - <v-select + <v-autocomplete v-model="c.type" :items="columnTypes" item-title="text" @@ -156,19 +155,12 @@ </v-row> <v-row> <v-col> - <v-btn - :color="disabled ? '' : 'tertiary'" - :variant="buttonVariant" - size="small" - class="mr-2" - :disabled="disabled" - :text="$t('navigation.back')" - @click="back" /> <v-btn color="secondary" variant="flat" size="small" - :disabled="disabled" + :loading="loading" + :disabled="submitDisabled" :text="submitText" @click="submit" /> </v-col> @@ -188,13 +180,19 @@ export default { return [] } }, - back: { + disabled: { type: Boolean, default () { return false } }, - disabled: { + submitDisabled: { + type: Boolean, + default () { + return false + } + }, + loading: { type: Boolean, default () { return false @@ -380,5 +378,3 @@ export default { } } </script> -<style scoped> -</style> diff --git a/dbrepo-ui/composables/query-service.ts b/dbrepo-ui/composables/query-service.ts index 28e2be085b..40c722ed12 100644 --- a/dbrepo-ui/composables/query-service.ts +++ b/dbrepo-ui/composables/query-service.ts @@ -111,7 +111,7 @@ export const useQueryService = (): any => { axios.head<void>(`/api/database/${databaseId}/query/${queryId}/data`) .then((response) => { const count: number = Number(response.headers['x-count']) - console.info('Re-executed query in database with id', databaseId) + console.info('Found', count, 'tuples for query', queryId, 'in database with id', databaseId) resolve(count) }) .catch((error) => { diff --git a/dbrepo-ui/composables/table-service.ts b/dbrepo-ui/composables/table-service.ts index 58e4a9ed2d..a71f6e5f86 100644 --- a/dbrepo-ui/composables/table-service.ts +++ b/dbrepo-ui/composables/table-service.ts @@ -2,11 +2,11 @@ import type {AxiosRequestConfig, AxiosResponse} from 'axios' export const useTableService = (): any => { - function findAll(databaseId: number): Promise<TableBriefDto> { + function findAll(databaseId: number, internalName: string): Promise<TableBriefDto[]> { const axios = useAxiosInstance() console.debug('find tables') - return new Promise<TableBriefDto>((resolve, reject) => { - axios.get<TableBriefDto>(`/api/database/${databaseId}/table`) + return new Promise<TableBriefDto[]>((resolve, reject) => { + axios.get<TableBriefDto[]>(`/api/database/${databaseId}/table`, {params: (internalName && {internal_name: internalName})}) .then((response) => { console.info('Found tables(s)') resolve(response.data) @@ -68,7 +68,7 @@ export const useTableService = (): any => { async function getData(databaseId: number, tableId: number, page: number, size: number, timestamp: Date): Promise<QueryResultDto> { const axios = useAxiosInstance() - console.debug('get data for table with id', tableId, 'in database with id', databaseId); + console.debug('get data for table with id', tableId, 'in database with id', databaseId, 'page', page, 'size', size); return new Promise<QueryResultDto>((resolve, reject) => { axios.get<QueryResultDto>(`/api/database/${databaseId}/table/${tableId}/data`, {params: mapFilter(timestamp, page, size)}) .then((response) => { @@ -89,7 +89,7 @@ export const useTableService = (): any => { axios.head<number>(`/api/database/${databaseId}/table/${tableId}/data`, {params: mapFilter(timestamp, null, null)}) .then((response: AxiosResponse<void>) => { const count: number = Number(response.headers['x-count']) - console.info('Found' + count + 'in table with id', tableId, 'in database with id', databaseId) + console.info('Found' + count + 'in table with id', tableId, 'in database with id', databaseId) resolve(count) }) .catch((error) => { @@ -102,7 +102,7 @@ export const useTableService = (): any => { async function exportData(databaseId: number, tableId: number, timestamp: Date): Promise<QueryResultDto> { const axios = useAxiosInstance() const config: AxiosRequestConfig = { - params: (timestamp && { timestamp }), + params: (timestamp && {timestamp}), responseType: 'blob', headers: { Accept: 'text/csv' @@ -143,9 +143,9 @@ export const useTableService = (): any => { console.debug('delete table with id', tableId, 'in database with id', databaseId) return new Promise<void>((resolve, reject) => { axios.delete<void>(`/api/database/${databaseId}/table/${tableId}`) - .then((response) => { + .then(() => { console.info('Deleted table with id', tableId, 'in database with id', databaseId) - resolve(response.data) + resolve() }) .catch((error) => { console.error('Failed to delete table', error) @@ -190,7 +190,7 @@ export const useTableService = (): any => { const axios = useAxiosInstance() console.debug('suggest semantic entities for table column with id', columnId, 'of table with id', tableId, 'of database with id', databaseId) return new Promise<TableColumnEntityDto[]>((resolve, reject) => { - axios.get<TableColumnEntityDto[]>(`/api/semantic/database/${databaseId}/table/${tableId}/column/${columnId}`, { timeout: 10000 }) + axios.get<TableColumnEntityDto[]>(`/api/semantic/database/${databaseId}/table/${tableId}/column/${columnId}`, {timeout: 10000}) .then((response) => { console.info('Suggested semantic entities for table column with id', columnId, 'of table with id', tableId, 'of database with id', databaseId) resolve(response.data) @@ -219,11 +219,11 @@ export const useTableService = (): any => { } function mapFilter(timestamp: Date | null, page: number | null, size: number | null) { - if (!timestamp) { - if (!page || !size) { - return null + if (page !== null && size !== null) { + if (!timestamp) { + return {page, size} } - return {page, size} + return {page, size, timestamp} } if (!page || !size) { return {timestamp} diff --git a/dbrepo-ui/composables/view-service.ts b/dbrepo-ui/composables/view-service.ts index cd23a9db6c..23b8123842 100644 --- a/dbrepo-ui/composables/view-service.ts +++ b/dbrepo-ui/composables/view-service.ts @@ -54,7 +54,7 @@ export const useViewService = (): any => { axios.head<number>(`/api/database/${databaseId}/view/${viewId}/data`) .then((response) => { const count: number = Number(response.headers['x-count']) - console.info('Re-executed view with id', viewId, 'in database with id', databaseId) + console.info('Found', count, 'tuples for view with id', viewId, 'in database with id', databaseId) resolve(count) }) .catch((error) => { diff --git a/dbrepo-ui/locales/de-AT.json b/dbrepo-ui/locales/de-AT.json index 6d84176806..749698e44d 100644 --- a/dbrepo-ui/locales/de-AT.json +++ b/dbrepo-ui/locales/de-AT.json @@ -79,12 +79,12 @@ }, "publication-year": { "label": "Erscheinungsjahr", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "titles": { "title": { "label": "Titel", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "type": { "label": "Typ", @@ -105,7 +105,7 @@ "descriptions": { "description": { "label": "Beschreibung", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "type": { "label": "Typ", @@ -127,14 +127,14 @@ "title": "Veröffentlichungsinformationen", "subtitle": "Der Name der Entität, die die Ressource hält, archiviert, veröffentlicht, druckt, verteilt, freigibt, herausgibt oder produziert. ", "label": "Herausgeber", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "related-identifiers": { "title": "Zugehöriger Bezeichner", "subtitle": "Bezeichner verwandter Ressourcen. ", "identifier": { "label": "Kennung", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "type": { "label": "Typ", @@ -160,7 +160,7 @@ }, "language": { "title": "Sprache", - "subtitle": "Die primäre Sprache des Datensatzes.", + "subtitle": "Die primäre Sprache des Datensatzes", "language": { "label": "Sprache", "hint": "" @@ -168,14 +168,14 @@ }, "funders": { "title": "Finanzierungsreferenz", - "subtitle": "Informationen zur finanziellen Unterstützung (Finanzierung) für den zu registrierenden Datensatz.", + "subtitle": "Informationen zur finanziellen Unterstützung (Finanzierung) für den zu registrierenden Datensatz", "identifier": { "label": "Kennung des Geldgebers", - "hint": "Verwenden Sie eine Namenskennung, ausgedrückt als URL von ORCID*, ROR*, DOI*, ISNI, GND (Schemata mit * unterstützen den automatischen Metadatenabruf)." + "hint": "Verwenden Sie eine Namenskennung, ausgedrückt als URL von ORCID*, ROR*, DOI*, ISNI, GND (Schemata mit * unterstützen den automatischen Metadatenabruf)" }, "name": { "label": "Name des Geldgebers", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "award-number": { "label": "Auszeichnungsnummer", @@ -193,10 +193,10 @@ } }, "creators": { - "subtitle": "Die wichtigsten Forscher, die an der Erstellung der Daten beteiligt waren, in der Reihenfolge ihrer Priorität.", + "subtitle": "Die wichtigsten Forscher, die an der Erstellung der Daten beteiligt waren, in der Reihenfolge ihrer Priorität", "identifier": { "label": "Namensbezeichner", - "hint": "Verwenden Sie eine Namenskennung, ausgedrückt als URL von ORCID*, ROR*, DOI*, ISNI, GND (Schemata mit * unterstützen den automatischen Metadatenabruf)." + "hint": "Verwenden Sie eine Namenskennung, ausgedrückt als URL von ORCID*, ROR*, DOI*, ISNI, GND (Schemata mit * unterstützen den automatischen Metadatenabruf)" }, "insert": { "text": "Füge mich ein" @@ -220,11 +220,11 @@ }, "name": { "label": "Name", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "affiliation-identifier": { "label": "Zugehörigkeitskennung", - "hint": "Verwenden Sie eine Zugehörigkeitskennung, ausgedrückt als URL von ORCID*, ROR*, DOI*, ISNI, GND (Schemata mit * unterstützen den automatischen Metadatenabruf)." + "hint": "Verwenden Sie eine Zugehörigkeitskennung, ausgedrückt als URL von ORCID*, ROR*, DOI*, ISNI, GND (Schemata mit * unterstützen den automatischen Metadatenabruf)" }, "affiliation": { "label": "Zugehörigkeitsname", @@ -234,7 +234,7 @@ }, "summary": { "title": "Zusammenfassung", - "subtitle": "Details zur Kennung, die zur Identifizierung dieses Datensatzes erstellt wird.", + "subtitle": "Details zur Kennung, die zur Identifizierung dieses Datensatzes erstellt wird", "record": "Der Bezeichner beschreibt", "publisher": "Herausgeber", "license": "Lizenz", @@ -268,8 +268,8 @@ "secure": "sicher", "insecure": "unsicher", "permissions": { - "write": "Sie können in diese Tabelle schreiben.", - "read": "Sie können den gesamten Inhalt dieser Tabelle lesen." + "write": "Sie können in diese Tabelle schreiben", + "read": "Sie können den gesamten Inhalt dieser Tabelle lesen" } }, "protocol": { @@ -310,8 +310,7 @@ "hint": "Erforderlich. " }, "generated": { - "label": "Generierter Name", - "hint": "" + "label": "Generierter Tabellenname:" }, "description": { "label": "Beschreibung", @@ -366,7 +365,7 @@ "summary": { "title": "Zusammenfassung", "prefix": "Importiert", - "suffix": "Zeilen aus dem Datensatz." + "suffix": "Zeilen aus dem Datensatz" }, "analyse": { "text": "Hochladen" @@ -379,7 +378,7 @@ }, "name": { "label": "Tabellenname", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "description": { "label": "Tabellenbeschreibung", @@ -388,18 +387,18 @@ "summary": { "prefix": "Tabelle mit Namen erstellt", "middle": "und importiert", - "suffix": "Zeilen aus dem Datensatz." + "suffix": "Zeilen aus dem Datensatz" } }, "drop": { "title": "Drop-Tisch", "warning": { "prefix": "Diese Aktion kann nicht rückgängig gemacht werden! ", - "suffix": "unten, wenn Sie es wirklich mit allen gespeicherten Daten löschen möchten." + "suffix": "unten, wenn Sie es wirklich mit allen gespeicherten Daten löschen möchten" }, "name": { "label": "Tabellenname", - "hint": "Erforderlich." + "hint": "Erforderlich" } }, "schema": { @@ -432,14 +431,14 @@ }, "name": { "label": "Name", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "add": { "text": "Spalte hinzufügen" }, "type": { "label": "Typ", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "size": { "label": "Größe" @@ -480,18 +479,18 @@ }, "semantics": { "title": "Semantische Instanz für Tabellenspalte zuweisen:", - "subtitle": "Semantische Instanzen helfen Maschinen dabei, den richtigen Kontext Ihres Datensatzes zu ermitteln.", + "subtitle": "Semantische Instanzen helfen Maschinen dabei, den richtigen Kontext Ihres Datensatzes zu ermitteln", "recommended": "Empfohlene semantische Instanzen", "bullet": "●", "info": "Die folgenden Ontologien fragen automatisch die Felder rdfs:label ab und speichern sie für diese Spalte. ", "uri": { "label": "Semantischer Instanz-URI", - "hint": "Dieser URI kann automatisch aufgelöst werden." + "hint": "Dieser URI kann automatisch aufgelöst werden" } }, "versioning": { "title": "Verlauf", - "subtitle": "Wählen Sie einen Zeitstempel aus, um die Daten für diese bestimmte Tageszeit anzuzeigen.", + "subtitle": "Wählen Sie einen Zeitstempel aus, um die Daten für diese bestimmte Tageszeit anzuzeigen", "chart": { "title": "Datenereignisse" }, @@ -504,10 +503,10 @@ }, "data": { "auto": { - "hint": "Der Wert wird automatisch durch eine Sequenz generiert." + "hint": "Der Wert wird automatisch durch eine Sequenz generiert" }, "primary-key": { - "hint": "Der Wert ist ein Primärschlüssel." + "hint": "Der Wert ist ein Primärschlüssel" }, "format": { "hint": "Der Wert muss folgendes Format haben:" @@ -516,10 +515,10 @@ "hint": "Erforderlich. " }, "float": { - "max": "max.", - "min": "Mindest.", - "before": "Ziffer(n) vor dem Punkt.", - "after": "Ziffer(n) nach dem Punkt." + "max": "max", + "min": "Mindest", + "before": "Ziffer(n) vor dem Punkt", + "after": "Ziffer(n) nach dem Punkt" } } } @@ -559,7 +558,7 @@ "subpages": { "access": { "title": "Datenbankzugriff", - "subtitle": "Übersicht über Benutzer mit ihrem Zugriff auf die Datenbank.", + "subtitle": "Übersicht über Benutzer mit ihrem Zugriff auf die Datenbank", "read": "Sie können alle Inhalte lesen", "write-own": "Sie können eigene Tabellen schreiben und alle Inhalte lesen", "write-all": "Sie können eigene Tabellen schreiben und alle Inhalte lesen", @@ -579,7 +578,7 @@ }, "create": { "title": "Datenbank erstellen", - "subtitle": "Wählen Sie einen aussagekräftigen Datenbanknamen und eine Datenbank-Engine.", + "subtitle": "Wählen Sie einen aussagekräftigen Datenbanknamen und eine Datenbank-Engine", "name": { "label": "Name", "hint": "Erforderlich. ", @@ -587,7 +586,7 @@ }, "engine": { "label": "Motor", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "submit": { "text": "Erstellen" @@ -613,7 +612,7 @@ }, "settings": { "title": "Einstellungen", - "subtitle": "Das Bild wird in einem Feld mit den maximalen Abmessungen 200x200 Pixel angezeigt.", + "subtitle": "Das Bild wird in einem Feld mit den maximalen Abmessungen 200x200 Pixel angezeigt", "image": { "label": "Teaser-Bild", "hint": "max. " @@ -626,9 +625,9 @@ }, "ownership": { "title": "Eigentum", - "subtitle": "Benutzer, der Eigentümer dieser Datenbank ist.", + "subtitle": "Benutzer, der Eigentümer dieser Datenbank ist", "label": "Datenbankbesitzer", - "hint": "Erforderlich.", + "hint": "Erforderlich", "submit": { "text": "Überweisen" } @@ -638,7 +637,7 @@ "subtitle": "Private Datenbanken verbergen die Daten, während Metadaten weiterhin sichtbar sind. ", "visibility": { "label": "Datenbanksichtbarkeit", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "submit": { "text": "Ändern" @@ -651,19 +650,19 @@ "name": "Melden Sie sich an", "email": { "label": "E-Mail-Adresse", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "username": { "label": "Nutzername", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "password": { "label": "Passwort", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "confirm": { "label": "Bestätige das Passwort", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "submit": { "label": "Absenden" @@ -673,11 +672,11 @@ "name": "Anmeldung", "username": { "label": "Nutzername", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "password": { "label": "Passwort", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "submit": { "label": "Absenden" @@ -690,7 +689,7 @@ "subpages": { "info": { "title": "Information", - "subtitle": "Allgemeine Benutzermetadaten.", + "subtitle": "Allgemeine Benutzermetadaten", "id": { "label": "ID" }, @@ -719,7 +718,7 @@ }, "theme": { "title": "Thema", - "subtitle": "Aktualisieren Sie das Benutzerdesign, wenn Sie angemeldet sind.", + "subtitle": "Aktualisieren Sie das Benutzerdesign, wenn Sie angemeldet sind", "label": "Thema", "dark": "Dunkel", "dark-contrast": "Dunkel – hoher Kontrast", @@ -735,14 +734,14 @@ "subpages": { "authentication": { "title": "Benutzer-Passwort", - "subtitle": "Aktualisieren Sie das Benutzerkennwort, das für die Basisauthentifizierung bei allen Schnittstellen verwendet wird.", + "subtitle": "Aktualisieren Sie das Benutzerkennwort, das für die Basisauthentifizierung bei allen Schnittstellen verwendet wird", "password": { "label": "Passwort", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "confirm": { "label": "Bestätige das Passwort", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "submit": { "text": "Aktualisieren" @@ -751,7 +750,7 @@ "developer": { "token": { "title": "Token-Informationen", - "subtitle": "Sehen Sie sich Ihre Token-Geheimnisse zu Debugging-Zwecken an.", + "subtitle": "Sehen Sie sich Ihre Token-Geheimnisse zu Debugging-Zwecken an", "expiry": "Läuft ab", "access": { "label": "Zugangstoken" @@ -770,11 +769,11 @@ "title": "Wartungsmeldung", "type": { "label": "Typ", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "message": { "label": "Nachricht", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "start": { "label": "Zeitstempel starten" @@ -822,15 +821,15 @@ "title": "Ansicht erstellen", "name": { "label": "Name", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "table": { "label": "Datentabelle", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "columns": { "label": "Datenspalten", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "visibility": { "warn": "Die Ansichtsmetadaten, d. h. Ansichtsname, Abfrage, bleiben weiterhin öffentlich. " @@ -885,15 +884,15 @@ "text": "Filter hinzufügen", "column": { "label": "Spalte", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "operator": { "label": "Operator", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "value": { "label": "Wert", - "hint": "Erforderlich." + "hint": "Erforderlich" }, "remove": { "text": "Entfernen" @@ -936,7 +935,7 @@ "hint": "" }, "publication-range": { - "hint": "Geben Sie Ihren benutzerdefinierten Veröffentlichungsjahresbereich an." + "hint": "Geben Sie Ihren benutzerdefinierten Veröffentlichungsjahresbereich an" }, "start-year": { "label": "Startjahr", @@ -947,7 +946,7 @@ "hint": "" }, "concept-unit": { - "hint": "Wenn Sie ein KONZEPT und eine EINHEIT auswählen, können Sie unabhängig von der Maßeinheit spaltenübergreifend suchen." + "hint": "Wenn Sie ein KONZEPT und eine EINHEIT auswählen, können Sie unabhängig von der Maßeinheit spaltenübergreifend suchen" }, "concept": { "label": "Konzept", @@ -990,20 +989,20 @@ }, "error": { "import": { - "dataset": "Der Datensatz konnte nicht importiert werden." + "dataset": "Der Datensatz konnte nicht importiert werden" }, "upload": { - "dataset": "Der Datensatz konnte nicht hochgeladen werden." + "dataset": "Der Datensatz konnte nicht hochgeladen werden" }, "schema": { - "id": "Die Spalte „id“ muss ein Primärschlüssel sein." + "id": "Die Spalte „id“ muss ein Primärschlüssel sein" }, "identifier": { "requestinvalid": "Bezeichner konnte nicht erstellt werden:" }, "user": { - "credentials": "Ungültige Benutzername und Passwort Kombination.", - "email-exists": "Das Konto mit dieser E-Mail-Adresse existiert bereits." + "credentials": "Ungültige Benutzername und Passwort Kombination", + "email-exists": "Das Konto mit dieser E-Mail-Adresse existiert bereits" }, "query": { "viewmalformed": "Die Ansicht ist fehlerhaft:", @@ -1024,7 +1023,7 @@ "table": { "tablemalformed": "Eintrag konnte nicht eingefügt werden:", "create": "Tabelle konnte nicht erstellt werden:", - "connection": "Das Laden der Tabellendaten ist fehlgeschlagen, da die Datenbank nicht erreichbar ist." + "connection": "Das Laden der Tabellendaten ist fehlgeschlagen, da die Datenbank nicht erreichbar ist" }, "view": { "create": "Ansicht konnte nicht erstellt werden:" @@ -1033,59 +1032,59 @@ "value": "Spaltenwert konnte nicht festgelegt werden:", "drift": "Ihre Browser-Uhrzeit ist nicht synchron mit UTC und scheint falschzugehen um:" }, - "transfer": "Der Datenbankeigentümer konnte nicht übertragen werden." + "transfer": "Der Datenbankeigentümer konnte nicht übertragen werden" }, "success": { - "signup": "Konto erfolgreich erstellt.", + "signup": "Konto erfolgreich erstellt", "query": { "build": "Abfrage konnte nicht erstellt werden: Spalte nicht gefunden", "fatal": "Abfragen mit diesem Schema können derzeit nicht über die Benutzeroberfläche erstellt werden" }, "import": { - "dataset": "Datensatz erfolgreich importiert." + "dataset": "Datensatz erfolgreich importiert" }, "upload": { - "dataset": "Datensatz erfolgreich hochgeladen.", - "blob": "Datei erfolgreich hochgeladen." + "dataset": "Datensatz erfolgreich hochgeladen", + "blob": "Datei erfolgreich hochgeladen" }, "analyse": { - "dataset": "Datensatz erfolgreich analysiert." + "dataset": "Datensatz erfolgreich analysiert" }, "access": { - "created": "Zugriff erfolgreich bereitgestellt.", - "modified": "Zugriff erfolgreich geändert.", - "revoked": "Zugriff erfolgreich widerrufen." + "created": "Zugriff erfolgreich bereitgestellt", + "modified": "Zugriff erfolgreich geändert", + "revoked": "Zugriff erfolgreich widerrufen" }, "data": { - "add": "Dateneingabe erfolgreich hinzugefügt.", - "update": "Dateneingabe erfolgreich aktualisiert." + "add": "Dateneingabe erfolgreich hinzugefügt", + "update": "Dateneingabe erfolgreich aktualisiert" }, "table": { - "created": "Tabelle erfolgreich erstellt.", - "semantics": "Semantische Instanz erfolgreich zugewiesen." + "created": "Tabelle erfolgreich erstellt", + "semantics": "Semantische Instanz erfolgreich zugewiesen" }, "database": { - "upload": "Datenbankbild erfolgreich hochgeladen.", - "transfer": "Der Datenbankeigentümer wurde erfolgreich übertragen.", + "upload": "Datenbankbild erfolgreich hochgeladen", + "transfer": "Der Datenbankeigentümer wurde erfolgreich übertragen", "image": { - "update": "Datenbankbild erfolgreich aktualisiert.", - "remove": "Datenbankbild erfolgreich entfernt." + "update": "Datenbankbild erfolgreich aktualisiert", + "remove": "Datenbankbild erfolgreich entfernt" } }, "pid": { - "created": "Erfolgreich persistierter Bezeichner.", - "updated": "Kennung erfolgreich aktualisiert." + "created": "Erfolgreich persistierter Bezeichner", + "updated": "Kennung erfolgreich aktualisiert" }, "user": { - "info": "Benutzerinformationen erfolgreich aktualisiert.", - "theme": "Benutzerthema erfolgreich aktualisiert." + "info": "Benutzerinformationen erfolgreich aktualisiert", + "theme": "Benutzerthema erfolgreich aktualisiert" }, "view": { - "create": "Ansicht erfolgreich erstellt.", - "delete": "Ansicht erfolgreich gelöscht." + "create": "Ansicht erfolgreich erstellt", + "delete": "Ansicht erfolgreich gelöscht" }, "subset": { - "create": "Teilmenge erfolgreich erstellt." + "create": "Teilmenge erfolgreich erstellt" } }, "toolbars": { @@ -1097,7 +1096,7 @@ "semantic": { "register": { "title": "Registrieren Sie die Ontologie", - "subtitle": "Registrieren Sie einen neuen Ontologie-Endpunkt." + "subtitle": "Registrieren Sie einen neuen Ontologie-Endpunkt" }, "ontologies": { "title": "Ontologien", @@ -1167,7 +1166,7 @@ }, "search": { "fuzzy": { - "placeholder": "Suchen ..." + "placeholder": "Suchen .." }, "result": "Ergebnis", "results": "Ergebnisse" @@ -1209,7 +1208,7 @@ "tuple": "Eintrag", "download": "Herunterladen", "version": "Verlauf", - "subtitle": "Stellen Sie Daten bereit, die direkt in den Datensatz eingefügt werden sollen." + "subtitle": "Stellen Sie Daten bereit, die direkt in den Datensatz eingefügt werden sollen" } } }, @@ -1221,7 +1220,7 @@ "month": "Ungültiger Monat", "schema": { "id": "Die Spalte muss als Primärschlüssel deklariert werden", - "primary-key": "Wir erstellen eine Spalte mit dem Namen „id“ mit einer automatisch ansteigenden Sequenz, die bei 1 beginnt. Bitte geben Sie eine Spalte mit Primärschlüssel an, wenn Sie dieses Verhalten nicht wünschen." + "primary-key": "Wir erstellen eine Spalte mit dem Namen „id“ mit einer automatisch ansteigenden Sequenz, die bei 1 beginnt. Bitte geben Sie eine Spalte mit Primärschlüssel an, wenn Sie dieses Verhalten nicht wünschen" }, "uri": { "pattern": "Ungültiger URI", diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index 30e15e9962..c3315a0a70 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -79,12 +79,12 @@ }, "publication-year": { "label": "Publication Year", - "hint": "Required." + "hint": "Required" }, "titles": { "title": { "label": "Title", - "hint": "Required." + "hint": "Required" }, "type": { "label": "Type", @@ -94,7 +94,7 @@ "label": "Language", "hint": "" }, - "subtitle": "A name or title by which a resource is known. May be the title of a dataset.", + "subtitle": "A name or title by which a resource is known. May be the title of a dataset", "remove": { "text": "Remove" }, @@ -105,7 +105,7 @@ "descriptions": { "description": { "label": "Description", - "hint": "Required." + "hint": "Required" }, "type": { "label": "Type", @@ -115,7 +115,7 @@ "label": "Language", "hint": "" }, - "subtitle": "All additional information. May be used for technical information or detailed information associated with a dataset.", + "subtitle": "All additional information. May be used for technical information or detailed information associated with a dataset", "remove": { "text": "Remove" }, @@ -125,16 +125,16 @@ }, "publisher": { "title": "Publication Information", - "subtitle": "The name of the entity that holds, archives, publishes, prints, distributes, releases, issues, or produces the resource. This property will be used to formulate the citation, so consider the prominence of the role.", + "subtitle": "The name of the entity that holds, archives, publishes, prints, distributes, releases, issues, or produces the resource. This property will be used to formulate the citation, so consider the prominence of the role", "label": "Publisher", - "hint": "Required." + "hint": "Required" }, "related-identifiers": { "title": "Related Identifier", - "subtitle": "Identifiers of related resources. These must be globally unique identifiers.", + "subtitle": "Identifiers of related resources. These must be globally unique identifiers", "identifier": { "label": "Identifier", - "hint": "Required." + "hint": "Required" }, "type": { "label": "Type", @@ -153,14 +153,14 @@ }, "licenses": { "title": "License", - "subtitle": "Identifiers of related resources. These must be globally unique identifiers.", + "subtitle": "Identifiers of related resources. These must be globally unique identifiers", "license": { "label": "License" } }, "language": { "title": "Language", - "subtitle": "The primary language of the dataset.", + "subtitle": "The primary language of the dataset", "language": { "label": "Language", "hint": "" @@ -168,14 +168,14 @@ }, "funders": { "title": "Funding Reference", - "subtitle": "Information about financial support (funding) for the dataset being registered.", + "subtitle": "Information about financial support (funding) for the dataset being registered", "identifier": { "label": "Funder Identifier", "hint": "Use a name identifier expressed as URL from ORCID*, ROR*, DOI*, ISNI, GND (schemes with * support automatic metadata retrieval)" }, "name": { "label": "Funder Name", - "hint": "Required." + "hint": "Required" }, "award-number": { "label": "Award Number", @@ -193,7 +193,7 @@ } }, "creators": { - "subtitle": "The main researchers involved in producing the data, in priority order.", + "subtitle": "The main researchers involved in producing the data, in priority order", "identifier": { "label": "Name Identifier", "hint": "Use a name identifier expressed as URL from ORCID*, ROR*, DOI*, ISNI, GND (schemes with * support automatic metadata retrieval)" @@ -220,7 +220,7 @@ }, "name": { "label": "Name", - "hint": "Required." + "hint": "Required" }, "affiliation-identifier": { "label": "Affiliation Identifier", @@ -234,7 +234,7 @@ }, "summary": { "title": "Summary", - "subtitle": "Details of the identifier that will be created to identify this record.", + "subtitle": "Details of the identifier that will be created to identify this record", "record": "The identifier describes", "publisher": "Publisher", "license": "License", @@ -268,8 +268,8 @@ "secure": "secure", "insecure": "insecure", "permissions": { - "write": "You can write to this table.", - "read": "You can read all contents of this table." + "write": "You can write to this table", + "read": "You can read all contents of this table" } }, "protocol": { @@ -303,32 +303,31 @@ }, "dataset": { "title": "Dataset Structure", - "warn": "The dataset schema does not match the target table schema. You can still force the import but it is not recommended." + "warn": "The dataset schema does not match the target table schema. You can still force the import but it is not recommended" }, "name": { "label": "Name", - "hint": "Required. Maximum length is 64 characters." + "hint": "Required. Maximum length is 64 characters" }, "generated": { - "label": "Generated Name", - "hint": "" + "label": "Generated table name:" }, "description": { "label": "Description", - "hint": "Optional. Short and concise description of the data." + "hint": "Optional. Short and concise description of the data" }, "separator": { "label": "Column Separator", - "hint": "Optional. Character that separates the columns.", + "hint": "Optional. Character that separates the columns", "warn": { "prefix": "We analysed your .csv/.tsv dataset and found that the separator you provided", "middle": "is not correct, the separator", - "suffix": "is more likely to be correct. It is advised to change the separator above." + "suffix": "is more likely to be correct. It is advised to change the separator above" } }, "skip": { "label": "Skip Rows", - "hint": "Optional. Number of rows to skip, e.g. when the first one contains header and no data." + "hint": "Optional. Number of rows to skip, e.g. when the first one contains header and no data" }, "quote": { "label": "Quote Encoding", @@ -336,11 +335,11 @@ }, "terminator": { "label": "Line Termination Encoding", - "hint": "Optional. Character that terminates the newlines.", + "hint": "Optional. Character that terminates the newlines", "warn": { "prefix": "We analysed your .csv/.tsv dataset and found that the line termination encoding you provided", "middle": "is not correct, the line termination encoding", - "suffix": "is more likely to be correct. It is advised to change the line termination encoding above." + "suffix": "is more likely to be correct. It is advised to change the line termination encoding above" } }, "null": { @@ -349,16 +348,16 @@ }, "true": { "label": "True Encoding", - "hint": "Optional. Character sequence that represents boolean true, e.g. 1, true, yes." + "hint": "Optional. Character sequence that represents boolean true, e.g. 1, true, yes" }, "false": { "label": "False Encoding", - "hint": "Optional. Character sequence that represents boolean false, e.g. 0, false, no." + "hint": "Optional. Character sequence that represents boolean false, e.g. 0, false, no" }, "file": { "title": "Dataset Upload", "label": "Dataset File", - "hint": "Required. Needs to be in .csv/.tsv file format." + "hint": "Required. Needs to be in .csv/.tsv file format" }, "preview": { "title": "Preview" @@ -366,7 +365,7 @@ "summary": { "title": "Summary", "prefix": "Imported", - "suffix": "rows from dataset." + "suffix": "rows from dataset" }, "analyse": { "text": "Upload & Analyse" @@ -379,7 +378,7 @@ }, "name": { "label": "Table Name", - "hint": "Required." + "hint": "Required" }, "description": { "label": "Table Description", @@ -388,18 +387,18 @@ "summary": { "prefix": "Created table with name", "middle": "and imported", - "suffix": "rows from dataset." + "suffix": "rows from dataset" } }, "drop": { "title": "Drop table", "warning": { "prefix": "This action cannot be undone! Type the table name", - "suffix": "below if you really want to drop it with all stored data." + "suffix": "below if you really want to drop it with all stored data" }, "name": { "label": "Table Name", - "hint": "Required." + "hint": "Required" } }, "schema": { @@ -432,14 +431,14 @@ }, "name": { "label": "Name", - "hint": "Required." + "hint": "Required" }, "add": { "text": "Add Column" }, "type": { "label": "Type", - "hint": "Required." + "hint": "Required" }, "size": { "label": "Size" @@ -480,18 +479,18 @@ }, "semantics": { "title": "Assign semantic instance for table column:", - "subtitle": "Semantic instances help machines to get the proper context of your dataset.", + "subtitle": "Semantic instances help machines to get the proper context of your dataset", "recommended": "Recommended semantic instances", "bullet": "●", - "info": "The following ontologies automatically will query the fields rdfs:label and store it for this column. You can still use other URIs that are not matching these ontologies, the URI will be displayed instead.", + "info": "The following ontologies automatically will query the fields rdfs:label and store it for this column. You can still use other URIs that are not matching these ontologies, the URI will be displayed instead", "uri": { "label": "Semantic Instance URI", - "hint": "This URI can be automatically resolved." + "hint": "This URI can be automatically resolved" } }, "versioning": { "title": "History", - "subtitle": "Select a timestamp to view the data for this specific time of day.", + "subtitle": "Select a timestamp to view the data for this specific time of day", "chart": { "title": "Data Events" }, @@ -504,10 +503,10 @@ }, "data": { "auto": { - "hint": "Value is automatically generated by a sequence." + "hint": "Value is automatically generated by a sequence" }, "primary-key": { - "hint": "Value is a primary key." + "hint": "Value is a primary key" }, "format": { "hint": "Value must be in format:" @@ -516,10 +515,10 @@ "hint": "Required. " }, "float": { - "max": "max.", - "min": "min.", - "before": "digit(s) before the dot.", - "after": "digit(s) after the dot." + "max": "max", + "min": "min", + "before": "digit(s) before the dot", + "after": "digit(s) after the dot" } } } @@ -559,7 +558,7 @@ "subpages": { "access": { "title": "Database Access", - "subtitle": "Overview on users with their access to the database.", + "subtitle": "Overview on users with their access to the database", "read": "You can read all contents", "write-own": "You can write own tables and read all contents", "write-all": "You can write own tables and read all contents", @@ -579,7 +578,7 @@ }, "create": { "title": "Create Database", - "subtitle": "Choose an expressive database name and select a database engine.", + "subtitle": "Choose an expressive database name and select a database engine", "name": { "label": "Name", "hint": "Required. The internal database name will be lowercase alphanumeric, others will be replaced with _", @@ -587,7 +586,7 @@ }, "engine": { "label": "Engine", - "hint": "Required." + "hint": "Required" }, "submit": { "text": "Create" @@ -613,7 +612,7 @@ }, "settings": { "title": "Settings", - "subtitle": "The image will be displayed in a box with maximum dimensions 200x200 pixels.", + "subtitle": "The image will be displayed in a box with maximum dimensions 200x200 pixels", "image": { "label": "Teaser Image", "hint": "max. 1MB file size" @@ -626,19 +625,19 @@ }, "ownership": { "title": "Ownership", - "subtitle": "User who has ownership over this database.", + "subtitle": "User who has ownership over this database", "label": "Database Owner", - "hint": "Required.", + "hint": "Required", "submit": { "text": "Transfer" } }, "visibility": { "title": "Visibility", - "subtitle": "Private databases hide the data while metadata is still visible. Public databases are fully transparent.", + "subtitle": "Private databases hide the data while metadata is still visible. Public databases are fully transparent", "visibility": { "label": "Database Visibility", - "hint": "Required." + "hint": "Required" }, "submit": { "text": "Modify" @@ -651,19 +650,19 @@ "name": "Signup", "email": { "label": "E-Mail Address", - "hint": "Required." + "hint": "Required" }, "username": { "label": "Username", - "hint": "Required." + "hint": "Required" }, "password": { "label": "Password", - "hint": "Required." + "hint": "Required" }, "confirm": { "label": "Confirm Password", - "hint": "Required." + "hint": "Required" }, "submit": { "label": "Submit" @@ -673,11 +672,11 @@ "name": "Login", "username": { "label": "Username", - "hint": "Required." + "hint": "Required" }, "password": { "label": "Password", - "hint": "Required." + "hint": "Required" }, "submit": { "label": "Submit" @@ -690,7 +689,7 @@ "subpages": { "info": { "title": "Information", - "subtitle": "General user metadata.", + "subtitle": "General user metadata", "id": { "label": "ID" }, @@ -719,7 +718,7 @@ }, "theme": { "title": "Theme", - "subtitle": "Update the user theme when logged in.", + "subtitle": "Update the user theme when logged in", "label": "Theme", "dark": "Dark", "dark-contrast": "Dark - High Contrast", @@ -735,14 +734,14 @@ "subpages": { "authentication": { "title": "User Password", - "subtitle": "Update the user password used for basic authentication with all interfaces.", + "subtitle": "Update the user password used for basic authentication with all interfaces", "password": { "label": "Password", - "hint": "Required." + "hint": "Required" }, "confirm": { "label": "Confirm Password", - "hint": "Required." + "hint": "Required" }, "submit": { "text": "Update" @@ -751,7 +750,7 @@ "developer": { "token": { "title": "Token Information", - "subtitle": "View your token secrets for debugging purposes.", + "subtitle": "View your token secrets for debugging purposes", "expiry": "Expires", "access": { "label": "Access Token" @@ -770,11 +769,11 @@ "title": "Maintenance Message", "type": { "label": "Type", - "hint": "Required." + "hint": "Required" }, "message": { "label": "Message", - "hint": "Required." + "hint": "Required" }, "start": { "label": "Start Timestamp" @@ -822,19 +821,19 @@ "title": "Create View", "name": { "label": "Name", - "hint": "Required." + "hint": "Required" }, "table": { "label": "Data Table", - "hint": "Required." + "hint": "Required" }, "columns": { "label": "Data Columns", - "hint": "Required." + "hint": "Required" }, "visibility": { "label": "Data Visibility", - "hint": "Required. When private, the view metadata will still be public but the data will only be visible to people with at least read access to this database." + "hint": "Required. When private, the view metadata will still be public but the data will only be visible to people with at least read access to this database" } } } @@ -886,15 +885,15 @@ "text": "Add Filter", "column": { "label": "Column", - "hint": "Required." + "hint": "Required" }, "operator": { "label": "Operator", - "hint": "Required." + "hint": "Required" }, "value": { "label": "Value", - "hint": "Required." + "hint": "Required" }, "remove": { "text": "Remove" @@ -937,7 +936,7 @@ "hint": "" }, "publication-range": { - "hint": "Specify your custom publication year range." + "hint": "Specify your custom publication year range" }, "start-year": { "label": "Start Year", @@ -948,7 +947,7 @@ "hint": "" }, "concept-unit": { - "hint": "If you select a CONCEPT and UNIT, you can search across columns regardless of their unit of measurement." + "hint": "If you select a CONCEPT and UNIT, you can search across columns regardless of their unit of measurement" }, "concept": { "label": "Concept", @@ -991,20 +990,20 @@ }, "error": { "import": { - "dataset": "Failed to import dataset." + "dataset": "Failed to import dataset" }, "upload": { - "dataset": "Failed to upload dataset." + "dataset": "Failed to upload dataset" }, "schema": { - "id": "Column \"id\" must be a primary key." + "id": "Column \"id\" must be a primary key" }, "identifier": { "requestinvalid": "Failed to create identifier:" }, "user": { - "credentials": "Invalid username/password combination.", - "email-exists": "Account with this e-mail exists already." + "credentials": "Invalid username/password combination", + "email-exists": "Account with this e-mail exists already" }, "query": { "viewmalformed": "View is malformed:", @@ -1025,7 +1024,7 @@ "table": { "tablemalformed": "Failed to insert entry:", "create": "Failed to create table:", - "connection": "Failed to load table data because database is not reachable." + "connection": "Failed to load table data because database is not reachable" }, "view": { "create": "Failed to create view:" @@ -1034,59 +1033,59 @@ "value": "Failed to set column value:", "drift": "Your browser clock is not synchronized with UTC and seems to be off by:" }, - "transfer": "Failed to transfer the database owner." + "transfer": "Failed to transfer the database owner" }, "success": { - "signup": "Successfully created account.", + "signup": "Successfully created account", "query": { "build": "Failed to build query: column not found", "fatal": "Query with this schema is not buildable through the UI at the moment" }, "import": { - "dataset": "Successfully imported dataset." + "dataset": "Successfully imported dataset" }, "upload": { - "dataset": "Successfully uploaded dataset.", - "blob": "Successfully uploaded file." + "dataset": "Successfully uploaded dataset", + "blob": "Successfully uploaded file" }, "analyse": { - "dataset": "Successfully analysed dataset." + "dataset": "Successfully analysed dataset" }, "access": { - "created": "Successfully provisioned access.", - "modified": "Successfully modified access.", - "revoked": "Successfully revoked access." + "created": "Successfully provisioned access", + "modified": "Successfully modified access", + "revoked": "Successfully revoked access" }, "data": { - "add": "Successfully added data entry.", - "update": "Successfully updated data entry." + "add": "Successfully added data entry", + "update": "Successfully updated data entry" }, "table": { - "created": "Successfully created table.", - "semantics": "Successfully assigned semantic instance." + "created": "Successfully created table", + "semantics": "Successfully assigned semantic instance" }, "database": { - "upload": "Successfully uploaded database image.", - "transfer": "Successfully transferred the database owner.", + "upload": "Successfully uploaded database image", + "transfer": "Successfully transferred the database owner", "image": { - "update": "Successfully updated database image.", - "remove": "Successfully removed database image." + "update": "Successfully updated database image", + "remove": "Successfully removed database image" } }, "pid": { - "created": "Successfully persisted identifier.", - "updated": "Successfully updated identifier." + "created": "Successfully persisted identifier", + "updated": "Successfully updated identifier" }, "user": { - "info": "Successfully updated user information.", - "theme": "Successfully updated user theme." + "info": "Successfully updated user information", + "theme": "Successfully updated user theme" }, "view": { - "create": "Successfully created view.", - "delete": "Successfully deleted view." + "create": "Successfully created view", + "delete": "Successfully deleted view" }, "subset": { - "create": "Successfully created subset." + "create": "Successfully created subset" } }, "toolbars": { @@ -1098,7 +1097,7 @@ "semantic": { "register": { "title": "Register Ontology", - "subtitle": "Register a new ontology endpoint." + "subtitle": "Register a new ontology endpoint" }, "ontologies": { "title": "Ontologies", @@ -1168,7 +1167,7 @@ }, "search": { "fuzzy": { - "placeholder": "Search ..." + "placeholder": "Search .." }, "result": "Result", "results": "Results" @@ -1210,7 +1209,7 @@ "tuple": "Entry", "download": "Download", "version": "History", - "subtitle": "Provide data to be directly inserted into the dataset." + "subtitle": "Provide data to be directly inserted into the dataset" } } }, @@ -1222,7 +1221,7 @@ "month": "Invalid month", "schema": { "id": "Column needs to be declared as primary key", - "primary-key": "We create a column named id with a auto-increasing sequence starting at 1. Please specify a column with primary key if you don't want this behavior." + "primary-key": "We create a column named id with a auto-increasing sequence starting at 1. Please specify a column with primary key if you don't want this behavior" }, "uri": { "pattern": "Invalid URI", diff --git a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue index d0bd1a7a4a..73bb8603bb 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/[table_id]/data.vue @@ -1,19 +1,19 @@ <template> <div> - <TableToolbar /> + <TableToolbar/> <v-toolbar v-if="canViewTableData" :color="versionColor" :title="title" flat> - <v-spacer /> + <v-spacer/> <v-btn v-if="canAddTuple" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-plus' : null" variant="flat" :text="$t('toolbars.table.data.add')" class="mb-1 ml-2" - @click="addTuple" /> + @click="addTuple"/> <v-btn v-if="canEditTuple" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-pencil' : null" @@ -21,7 +21,7 @@ variant="flat" :text="$t('toolbars.table.data.edit')" class="mb-1 ml-2" - @click="editTuple" /> + @click="editTuple"/> <v-btn v-if="canDeleteTuple" :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-delete' : null" @@ -30,14 +30,14 @@ :text="$t('toolbars.table.data.delete')" class="mb-1 ml-2" :loading="loadingDelete" - @click="deleteItems" /> + @click="deleteItems"/> <v-btn :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-download' : null" variant="flat" :loading="downloadLoading" :text="$t('toolbars.table.data.download')" class="mb-1 ml-2" - @click.stop="download" /> + @click.stop="download"/> <v-btn :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-refresh' : null" variant="flat" @@ -45,39 +45,41 @@ class="mb-1 ml-2" :disabled="loadingData !== 0" :loading="loadingData > 0" - @click="reload" /> + @click="reload"/> <v-btn :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-update' : null" variant="flat" :text="$t('toolbars.table.data.version')" class="mb-1 ml-2" - @click.stop="pick" /> + @click.stop="pick"/> </v-toolbar> - <TimeDrift /> + <TimeDrift/> <v-card tile> - <v-progress-linear v-if="loadingData > 0 || error" :indeterminate="!error" :color="loadingColor" /> + <v-progress-linear v-if="loadingData > 0 || error" :indeterminate="!error" :color="loadingColor"/> <v-card v-if="error" variant="flat"> <v-card-text - v-text="$t('error.table.connection')" /> + v-text="$t('error.table.connection')"/> </v-card> - <v-data-table + <v-data-table-server v-if="!error" flat :headers="headers" + :loading="loadingData > 0" + :options="options" :items="rows" - :options.sync="options" - :server-items-length="total" - :footer-props="footerProps"> + :items-length="total" + :footer-props="footerProps" + @update:options="updateOptions"> <template v-if="canModify" v-slot:item.selection="{ item }"> <input v-model="selection" type="checkbox" :value="item" @click="edit = true"> </template> <template v-for="(blobColumn, idx) in blobColumns" v-slot:[blobColumn]="{ item }"> <BlobDownload - :blob="item[blobColumn.substring(5)]" /> + :blob="item[blobColumn.substring(5)]"/> </template> - </v-data-table> + </v-data-table-server> </v-card> <v-dialog v-model="pickVersionDialog" @@ -85,7 +87,7 @@ @close="closeVersion"> <TimeTravel ref="timeTravel" - @close="pickVersion" /> + @close="pickVersion"/> </v-dialog> <v-dialog v-model="editTupleDialog" @@ -95,9 +97,9 @@ :table="table" :tuple="tuple" :edit="edit" - @close="close" /> + @close="close"/> </v-dialog> - <v-breadcrumbs :items="items" class="pa-0 mt-2" /> + <v-breadcrumbs :items="items" class="pa-0 mt-2"/> </div> </template> @@ -106,8 +108,8 @@ import TimeTravel from '@/components/dialogs/TimeTravel.vue' import TimeDrift from '@/components/TimeDrift.vue' import TableToolbar from '@/components/table/TableToolbar.vue' import {formatTimestampUTC, formatDateUTC, formatTimestamp, localizedMessage} from '@/utils' -import { useUserStore } from '@/stores/user' -import { useCacheStore } from '@/stores/cache' +import {useUserStore} from '@/stores/user' +import {useCacheStore} from '@/stores/cache' import EditTuple from '@/components/dialogs/EditTuple.vue' import BlobDownload from '@/components/table/BlobDownload.vue' @@ -119,13 +121,13 @@ export default { TableToolbar, TimeDrift }, - data () { + data() { return { loading: true, loadingData: 0, loadingDelete: false, editTupleDialog: false, - total: -1, + total: null, footerProps: { showFirstLastPage: true, itemsPerPageOptions: [10, 25, 50, 100] @@ -175,52 +177,52 @@ export default { } }, computed: { - loadingColor () { + loadingColor() { return this.error ? 'error' : 'primary' }, - roles () { + roles() { return this.userStore.getRoles }, - database () { + database() { return this.cacheStore.getDatabase }, - table () { + table() { return this.cacheStore.getTable }, - user () { + user() { return this.userStore.getUser }, - tables () { + tables() { return this.cacheStore.getTable }, - access () { + access() { return this.userStore.getAccess }, - title () { + title() { return (this.version ? this.$t('toolbars.database.history') : this.$t('toolbars.database.current')) + ' ' + this.versionFormatted }, - blobColumns () { + blobColumns() { if (!this.table || !this.table.columns) { return [] } return this.table.columns.filter(c => this.isFileField(c)).map(c => 'item.' + c.internal_name) }, - versionColor () { + versionColor() { return this.version ? 'primary' : 'secondary' }, - versionFormatted () { + versionFormatted() { if (this.version === null) { return '' } return this.version + ' (UTC)' }, - versionISO () { + versionISO() { if (this.version === null) { return null } return this.version.substring(0, 10) + 'T' + this.version.substring(11, 19) + 'Z' }, - canModify () { + canModify() { if (!this.user || !this.access || !this.table) { return false } @@ -229,7 +231,7 @@ export default { } return this.access.type === 'write_all' }, - canViewTableData () { + canViewTableData() { /* view when database is public or when private: 1) view-table-data role present 2) access is at least read */ if (!this.database) { return false @@ -242,50 +244,50 @@ export default { } return this.access.type === 'read' || this.access.type === 'write_own' || this.access.type === 'write_all' }, - canAddTuple () { + canAddTuple() { if (!this.roles) { return false } const userService = useUserService() return userService.hasWriteAccess(this.table, this.access, this.user) && this.roles.includes('insert-table-data') }, - canEditTuple () { + canEditTuple() { if (!this.roles || this.selection === null || this.selection.length !== 1) { return false } const userService = useUserService() return userService.hasWriteAccess(this.table, this.access, this.user) && this.roles.includes('insert-table-data') }, - canDeleteTuple () { + canDeleteTuple() { if (!this.roles || this.selection === null || this.selection.length < 1) { return false } const userService = useUserService() return userService.hasWriteAccess(this.table, this.access, this.user) && this.roles.includes('delete-table-data') }, - tuple () { + tuple() { return this.edit ? this.selection[0] : {} }, }, watch: { - version () { + version() { this.reload() }, - options () { + options() { this.loadData() }, - table (newTable, oldTable) { + table(newTable, oldTable) { if (newTable !== oldTable && oldTable === null) { this.loadProperties() } } }, - mounted () { + mounted() { this.reload() this.loadProperties() }, methods: { - addTuple () { + addTuple() { const data = {} this.edit = false this.table.columns.forEach((c) => { @@ -294,11 +296,11 @@ export default { this.selection = [] this.editTupleDialog = true }, - editTuple () { + editTuple() { this.edit = true this.editTupleDialog = true }, - deleteItems () { + deleteItems() { this.loadingDelete = true const wait = [] for (const select of this.selection) { @@ -317,17 +319,17 @@ export default { }) } const tupleService = useTupleService() - wait.push(tupleService.remove(this.$route.params.database_id, this.$route.params.table_id, { keys: constraints })) + wait.push(tupleService.remove(this.$route.params.database_id, this.$route.params.table_id, {keys: constraints})) } Promise.all(wait) .then(() => { this.$toast.success(`Deleted ${this.selection.length} row(s)`) - this.$emit('modified', { success: true, action: 'delete' }) + this.$emit('modified', {success: true, action: 'delete'}) this.reload() }) this.loadingDelete = false }, - download () { + download() { this.downloadLoading = true if (!this.version) { const tableService = useTableService() @@ -365,14 +367,14 @@ export default { }) } }, - pick () { + pick() { this.pickVersionDialog = true }, - closeVersion () { + closeVersion() { this.pickVersionDialog = false }, - pickVersion (event) { - const { success, timestamp } = event + pickVersion(event) { + const {success, timestamp} = event if (success) { if (timestamp === null) { this.version = null @@ -382,12 +384,12 @@ export default { } this.pickVersionDialog = false }, - loadProperties () { + loadProperties() { if (!this.table || this.headers.length > 0) { return } try { - this.headers = [{ value: 'selection', title: '', sortable: false }] + this.headers = [{value: 'selection', title: '', sortable: false}] this.table.columns.map((c) => { return { value: c.internal_name, @@ -402,15 +404,15 @@ export default { } this.loading = false }, - reload () { + reload() { this.lastReload = new Date() this.loadData() this.loadCount() }, - loadData () { + loadData() { this.loadingData++ const tableService = useTableService() - tableService.getData(this.$route.params.database_id, this.$route.params.table_id, (this.options.page - 1), this.options.itemsPerPage, (this.versionISO || this.lastReload.toISOString())) + tableService.getData(this.$route.params.database_id, this.$route.params.table_id, this.options.page - 1, this.options.itemsPerPage, (this.versionISO || this.lastReload.toISOString())) .then((data) => { this.rows = data.result.map((row) => { for (const col in row) { @@ -435,7 +437,7 @@ export default { this.loadingData-- }) }, - loadCount () { + loadCount() { this.loadingData++ const tableService = useTableService() tableService.getCount(this.$route.params.database_id, this.$route.params.table_id, (this.versionISO || this.lastReload.toISOString())) @@ -449,13 +451,18 @@ export default { this.loadingData-- }) }, - isFileField (column) { + isFileField(column) { return ['blob', 'longblob', 'mediumblob', 'tinyblob'].includes(column.column_type) }, - close (event) { + close(event) { console.debug('closed edit/create tuple dialog', event) this.editTupleDialog = false this.reload() + }, + updateOptions({page, itemsPerPage, sortBy}) { + this.options.page = page + this.options.itemsPerPage = itemsPerPage + this.loadData() } } } diff --git a/dbrepo-ui/pages/database/[database_id]/table/import.vue b/dbrepo-ui/pages/database/[database_id]/table/import.vue index 62a8a56675..75ce7cfeb6 100644 --- a/dbrepo-ui/pages/database/[database_id]/table/import.vue +++ b/dbrepo-ui/pages/database/[database_id]/table/import.vue @@ -49,20 +49,13 @@ </v-row> <v-row dense> <v-col md="8"> - <v-text-field - v-model="generatedTableName" - :rules="[ - v => notEmpty(v) || $t('validation.required'), - v => generatedTableName.length <= 64 || ($t('validation.max-length') + 64), - ]" - disabled - clearable - counter="64" - persistent-counter - persistent-hint - :variant="inputVariant" - :hint="$t('pages.table.subpages.import.generated.hint')" - :label="$t('pages.table.subpages.import.generated.label')"/> + <v-alert + v-if="generatedTableName" + class="mt-1" + border="start" + color="info"> + {{ $t('pages.table.subpages.import.generated.label') + ' ' + generatedTableName }} + </v-alert> </v-col> </v-row> <v-row dense> @@ -102,11 +95,11 @@ v-if="step >= 4"> <TableSchema ref="schema" - :back="true" :submit-text="$t('navigation.continue')" + :submit-disabled="!validStep1" :columns="tableCreate.columns" + :loading="loadingCreateAndImport" @schema-valid="schemaValidity" - @back="onBack" @close="createEmptyTableAndImport"/> </v-container> </v-stepper-window> @@ -173,6 +166,7 @@ export default { error: false, fileModel: null, rowCount: null, + loadingCreateAndImport: false, file: { filename: null, path: null @@ -332,7 +326,24 @@ export default { } delete c.unique }) - this.createTableAndImport(this.tableCreate) + const tableService = useTableService() + this.loadingCreateAndImport = true + tableService.findAll(this.$route.params.database_id, this.generatedTableName) + .then((response) => { + if (response.length !== 0) { + /* table does exist */ + tableService.remove(this.$route.params.database_id, response[0].id) + .then(() => { + this.createTableAndImport(this.tableCreate) + }) + .catch((error) => { + this.$toast.error(this.$t('error.import.dataset') + ': ' + error.response.data.message) + this.loadingCreateAndImport = false + }) + } else { + this.createTableAndImport(this.tableCreate) + } + }) }, createTableAndImport(table) { const tableService = useTableService() @@ -343,27 +354,27 @@ export default { .then(() => { this.$toast.success(this.$t('success.import.dataset')) this.cacheStore.reloadDatabase() - tableService.getCount(this.$route.params.database_id, table.id, null) - .then((rowCount) => { - this.rowCount = rowCount - this.step = 5 - }) + this.loadingCreateAndImport = true }) .catch((error) => { console.error('Failed to import csv', error) this.$toast.error(this.$t('error.import.dataset') + ': ' + error.response.data.message) this.loading = false this.$refs.schema.loading = false + this.loadingCreateAndImport = false }) .finally(() => { this.loading = false + this.loadingCreateAndImport = false }) }) .catch(() => { this.$refs.schema.loading = false + this.loadingCreateAndImport = false }) .finally(() => { this.loading = false + this.loadingCreateAndImport = false }) }, schemaValidity(event) { diff --git a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue index aed29aed24..783ad56637 100644 --- a/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue +++ b/dbrepo-ui/pages/database/[database_id]/view/[view_id]/data.vue @@ -5,6 +5,13 @@ color="secondary" :title="$t('toolbars.database.current')" flat> + <v-btn + :prepend-icon="$vuetify.display.lgAndUp ? 'mdi-refresh' : null" + variant="flat" + :text="$t('toolbars.table.data.refresh')" + class="mb-1 ml-2" + :loading="loadingData" + @click="reload" /> </v-toolbar> <TimeDrift /> <v-card tile> @@ -31,6 +38,7 @@ export default { }, data () { return { + loadingData: false, items: [ { title: this.$t('navigation.databases'), @@ -71,10 +79,13 @@ export default { if (!this.view) { return } - this.$refs.queryResults.reExecute(this.view.id) - this.$refs.queryResults.reExecuteCount(this.view.id) + this.reload() + }, + methods: { + reload () { + this.$refs.queryResults.reExecute(this.view.id) + this.$refs.queryResults.reExecuteCount(this.view.id) + } } } </script> -<style> -</style> -- GitLab