diff --git a/.docs/api/ui.md b/.docs/api/ui.md index d14303aa2cd7b33d48a743353bc6a82c53b0f55c..1f957bb9c76ea6ec2d4759f5a90e45603dea6308 100644 --- a/.docs/api/ui.md +++ b/.docs/api/ui.md @@ -27,26 +27,15 @@ image as well, in this example we want to mount a custom logo `my_logo.png` into ```yaml title=".env" NUXT_PUBLIC_TITLE="My overriden title" - NUXT_PUBLIC_LOGO="/my_logo.png" - NUXT_PUBLIC_ICON="/favicon.ico" + NUXT_PUBLIC_LOGO="https://mydomain/my_logo.png" + NUXT_PUBLIC_ICON="https://mydomain/my_favicon.ico" ... ``` - To work, you need to mount the `my_logo.png` file into the `dbrepo-ui` container via the `docker-compose.yml` file. - - ```yaml title="docker-compose.yml" - services: - dbrepo-ui: - image: registry.datalab.tuwien.ac.at/dbrepo/ui:1.4.7 - volumes: - - ./my_logo.png:/app/.output/public/my_logo.png - - ./favicon.ico:/app/.output/public/favicon.ico - environment: - ... - ... - ``` - - If you want to override more environment variables, extend the dictionary in `environment:` + To work, you need to serve the `my_logo.png` and `my_favicon.ico` from a separate webserver. Note that simply + copying the files into the Nuxt [`public/`](https://nuxt.com/docs/guide/directory-structure/public) directory will + not work as the content length is calculated only during build time. The development + team [#19263](https://github.com/nuxt/nuxt/issues/19263) does not plan to fix this. === "Kubernetes" diff --git a/dbrepo-ui/components/subset/Builder.vue b/dbrepo-ui/components/subset/Builder.vue index a6f53c6e4f0dba0ef1f8935c9112dc1e2d1d0df2..3a3cd3a142dce81172d23d1ee979515514c0326a 100644 --- a/dbrepo-ui/components/subset/Builder.vue +++ b/dbrepo-ui/components/subset/Builder.vue @@ -44,7 +44,7 @@ v-if="isView" class="mt-1" dense> - <v-col md="8"> + <v-col lg="8"> <v-text-field v-model="view.name" :disabled="isExecuted" @@ -74,7 +74,7 @@ <v-row v-if="isView" dense> - <v-col md="8"> + <v-col lg="8"> <v-select v-model="view.is_public" :items="visibilities" @@ -95,7 +95,7 @@ <v-window-item value="0"> <v-row dense> - <v-col md="4"> + <v-col lg="4"> <v-select v-model="table" :disabled="isExecuted" @@ -109,7 +109,7 @@ :hint="$t('pages.view.subpages.create.table.hint')" :rules="[v => !!v || $t('validation.required')]" /> </v-col> - <v-col md="4"> + <v-col lg="4"> <v-select v-model="select" item-title="internal_name" @@ -143,7 +143,7 @@ </v-col> </v-row> <v-row v-if="select.length > 0"> - <v-col md="8"> + <v-col lg="8"> <v-btn v-if="clauses.length === 0" size="small" @@ -157,15 +157,15 @@ <div class="mb-5"> <v-row v-if="clauses.length > 0"> <v-col - md="8" + lg="8" class="text-center"> - <pre>WHERE</pre> + <pre>FILTER</pre> </v-col> </v-row> <div v-for="(clause, idx) in clauses" :key="idx"> <v-row v-if="clause.type === 'where'"> - <v-col md="3"> + <v-col lg="3"> <v-select v-model="clause.params[0]" :disabled="clausesDisabled" @@ -177,16 +177,36 @@ :hint="$t('pages.subset.subpages.create.filter.column.hint')" :items="select" /> </v-col> - <v-col md="1"> + <v-col lg="2"> <v-select v-model="clause.params[1]" :disabled="clausesDisabled" + item-title="value" + item-value="value" persistent-hint - :label="$t('pages.subset.subpages.create.filter.operator.label')" - :hint="$t('pages.subset.subpages.create.filter.operator.hint')" - :items="operators" /> + :label="operatorHint(clause.params[1])" + :hint="$t('pages.subset.subpages.create.filter.operator.label')" + :items="operators"> + <template + v-slot:append> + <NuxtLink + target="_blank" + :href="documentationLink(clause.params[1])"> + <v-tooltip + location="bottom"> + <template + v-slot:activator="{ props }"> + <v-icon + v-bind="props" + icon="mdi-help-circle-outline" /> + </template> + {{ $t('navigation.help') }} + </v-tooltip> + </NuxtLink> + </template> + </v-select> </v-col> - <v-col md="3"> + <v-col lg="3"> <v-text-field v-model="clause.params[2]" :disabled="clausesDisabled" @@ -194,7 +214,7 @@ :label="$t('pages.subset.subpages.create.filter.value.label')" :hint="$t('pages.subset.subpages.create.filter.value.hint')" /> </v-col> - <v-col md="1"> + <v-col lg="1"> <v-btn :disabled="clausesDisabled" class="mt-4" @@ -208,7 +228,7 @@ <v-row v-else> <v-col - md="8" + lg="8" class="text-center"> <pre>{{ clause.type.toUpperCase() }}</pre> </v-col> @@ -308,48 +328,6 @@ export default { { title: this.$t('toolbars.database.public'), value: true }, { title: this.$t('toolbars.database.private'), value: false }, ], - operators: [ - '=', - '<', - '>', - '<=', - '>=', - '<>', - '!=', - 'like', - 'not like', - 'between', - 'not between', - 'ilike', - 'not ilike', - 'exists', - 'not exist', - 'rlike', - 'not rlike', - 'regexp', - 'not regexp', - 'match', - '&', - '|', - '^', - '<<', - '>>', - '~', - '~=', - '~*', - '!~', - '!~*', - '#', - '&&', - '@>', - '<@', - '||', - '&<', - '&>', - '-|-', - '@@', - '!!' - ], tableDetails: null, resultId: null, valid: false, @@ -375,6 +353,12 @@ export default { columnNames () { return this.columns && this.columns.map(s => s.internal_name) }, + operators () { + if (!this.database) { + return [] + } + return this.database.container.image.operators + }, columns () { if (!this.table) { return [] @@ -451,13 +435,13 @@ export default { return true } } - return false + return this.sql.includes(';') }, canExecute () { if (this.isView) { return this.view.name !== null && this.view.is_public !== null && this.view.query !== null } - return this.query.raw !== null + return this.sql !== null && !this.sql.includes(';') }, inputVariant () { const runtimeConfig = useRuntimeConfig() @@ -523,13 +507,13 @@ export default { await this.$router.push(`/database/${this.$route.params.database_id}/subset/${subset.id}/data`) this.loadingQuery = false }) - .catch(({code}) => { + .catch(({code, message}) => { this.loadingQuery = false const toast = useToastInstance() if (typeof code !== 'string') { return } - toast.error(this.$t(code)) + toast.error(`${this.$t(code)}: ${message}`) }) }, createView () { @@ -618,6 +602,20 @@ export default { } else { this.select = [] } + }, + documentationLink (value) { + const filter = this.operators.filter(o => o.value === value) + if (filter.length !== 1) { + return null + } + return filter[0].documentation + }, + operatorHint (value) { + const filter = this.operators.filter(o => o.value === value) + if (filter.length !== 1) { + return null + } + return filter[0].display_name } } } diff --git a/dbrepo-ui/dto/index.ts b/dbrepo-ui/dto/index.ts index 74b3911764fbd3da5d41d5d72cd269d79ee3963d..7658b4d1715af0b26d0b43bfd16493c84a02d391 100644 --- a/dbrepo-ui/dto/index.ts +++ b/dbrepo-ui/dto/index.ts @@ -75,18 +75,18 @@ interface ImageDto { version: string; dialect: string; driver_class: string; - date_formats: ImageDateDto[]; + data_types: DataTypeDto[]; + operators: OperatorDto[]; jdbc_method: string; default_port: number; } -interface ImageDateDto { +interface OperatorDto { id: number; - example: string; - database_format: string; - unix_format: string; - has_time: boolean; - created_at: Date; + image_id: number; + display_name: string; + documentation: string; + value: string; } interface TableBriefDto { diff --git a/dbrepo-ui/locales/en-US.json b/dbrepo-ui/locales/en-US.json index 3a3846e2efdb70cb02793834c3e018ea9c189f08..cf2d07b8a8486cef2896d8d9bdc06a3504aae0fb 100644 --- a/dbrepo-ui/locales/en-US.json +++ b/dbrepo-ui/locales/en-US.json @@ -1,5 +1,6 @@ { "navigation": { + "logo": "Logo", "information": "Information", "search": "Search", "container": "Engines", @@ -920,17 +921,17 @@ "query": { "title": "Query" }, - "query-hash": { - "prefix": "sha256", - "title": "Query Hash" + "hash": { + "title": "Hash", + "prefix": "sha256" }, "executed": { "title": "Created" }, - "result-hash": { - "title": "Result Hash" + "result": { + "title": "Result" }, - "result-rows": { + "rows": { "title": "Result Rows" }, "tabs": { @@ -946,7 +947,7 @@ }, "expert": { "text": "Expert", - "warn": "It is not recommended to use comments, aggregation functions and the following operations" + "warn": "It is not recommended to use comments, aggregation functions, the semicolon and the following operations" }, "name": { "label": "" diff --git a/dbrepo-ui/nuxt.config.ts b/dbrepo-ui/nuxt.config.ts index 8def4012d992f9f998969ec2b247398706ec5db4..be6fb2940f66b5ae026427b7a4eae964d05fa714 100644 --- a/dbrepo-ui/nuxt.config.ts +++ b/dbrepo-ui/nuxt.config.ts @@ -27,6 +27,7 @@ export default defineNuxtConfig({ charset: 'utf-8', viewport: 'width=device-width, initial-scale=1', meta: [ + {'ref': 'icon', type: 'image/x-icon', href: '/favicon.ico'}, {'http-equiv': 'Content-Security-Policy', content: 'upgrade-insecure-requests'} ], htmlAttrs: { @@ -86,7 +87,7 @@ export default defineNuxtConfig({ prefix: '/' }, database: { - unsupported: '*,AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--', + unsupported: 'AVG,BIT_AND,BIT_OR,BIT_XOR,COUNT,COUNTDISTINCT,GROUP_CONCAT,JSON_ARRAYAGG,JSON_OBJECTAGG,MAX,MIN,STD,STDDEV,STDDEV_POP,STDDEV_SAMP,SUM,VARIANCE,VAR_POP,VAR_SAMP,--', image: { width: 200, height: 200 diff --git a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue index 01620ea35eebf78969c6353b9ccaa0e7e9fdfb05..0d59b1ed25e405db59c50e3f492d9122f9fbf5ae 100644 --- a/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue +++ b/dbrepo-ui/pages/database/[database_id]/subset/[subset_id]/info.vue @@ -50,9 +50,9 @@ <pre>{{ subset.query }}</pre> </v-list-item> <v-list-item - :title="$t('pages.subset.query-hash.title')" + :title="`${$t('pages.subset.query.title')} ${$t('pages.subset.hash.title')}`" density="compact"> - <pre>{{ $t('pages.subset.query-hash.prefix') }}{{ subset.query_hash }}</pre> + <pre>{{ $t('pages.subset.hash.prefix') }}:{{ subset.query_hash }}</pre> </v-list-item> <v-list-item v-if="executionUTC" @@ -61,9 +61,9 @@ {{ executionUTC }} </v-list-item> <v-list-item - :title="$t('pages.subset.result-hash.title')" + :title="`${$t('pages.subset.result.title')} ${$t('pages.subset.hash.title')}`" density="compact"> - <pre>{{ result_hash }}</pre> + <pre>{{ $t('pages.subset.hash.prefix') }}:{{ result_hash }}</pre> </v-list-item> <v-list-item :title="$t('pages.subset.result-rows.title')" @@ -215,7 +215,7 @@ export default { if (!this.subset.result_hash) { return '(none)' } - return `sha256:${this.subset.result_hash}` + return this.subset.result_hash }, publisher () { if (this.database.publisher === null) {