From d9f44ec390796b30edd72a450c6caebd9563328d Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Fri, 5 Nov 2021 11:46:24 +0100 Subject: [PATCH 01/24] mise en page tableau repository --- .../components/common/AuthorizationTable.vue | 105 +++-- ui2/src/components/common/DropDownMenu.vue | 28 +- .../DataTypeAuthorizationInfoView.vue | 419 +++++++----------- .../datatype/DataTypesRepositoryView.vue | 46 +- 4 files changed, 255 insertions(+), 343 deletions(-) diff --git a/ui2/src/components/common/AuthorizationTable.vue b/ui2/src/components/common/AuthorizationTable.vue index c796ade12..91dca9a23 100644 --- a/ui2/src/components/common/AuthorizationTable.vue +++ b/ui2/src/components/common/AuthorizationTable.vue @@ -5,25 +5,40 @@ <div v-for="(scope, index) of authReference" :key="index"> <div class="columns"> <div v-for="(column, indexColumn) of columnsVisible" :key="indexColumn" class="column"> - <b-button v-if="column.display && indexColumn=='label' && (!scope.isLeaf || remainingOption.length)" :class="(!scope.isLeaf || remainingOption.length)?'leaf':'folder'" - :field="indexColumn" :label="localName(scope)" - @click="indexColumn=='label' && toggle(index)"/> - <b-field v-else-if="column.display && indexColumn=='label' && !(!scope.isLeaf || remainingOption.length)" :class="(!scope.isLeaf || remainingOption.length)?'leaf':'folder'" - :field="indexColumn" :label="localName(scope)" /> - <b-field v-else-if="column.display && indexColumn!='date'" :field="indexColumn"> - <b-checkbox @input="selectCheckbox($event, indexColumn, scope)"/> + <a + v-if=" + column.display && + indexColumn == 'label' && + (!scope.isLeaf || remainingOption.length) + " + :class="!scope.isLeaf || remainingOption.length ? 'leaf' : 'folder'" + :field="indexColumn" + @click="indexColumn == 'label' && toggle(index)" + >{{ localName(scope) }}</a> + <p + v-else-if=" + column.display && + indexColumn == 'label' && + !(!scope.isLeaf || remainingOption.length) + " + :class="!scope.isLeaf || remainingOption.length ? 'leaf' : 'folder'" + :field="indexColumn" + > {{ localName(scope) }}</p> + <b-field v-else-if="column.display && indexColumn != 'date'" :field="indexColumn"> + <b-checkbox @input="selectCheckbox($event, indexColumn, scope)" /> </b-field> - <b-field v-else-if="column.display && indexColumn=='date'" :field="indexColumn"> - <b-radio/> + <b-field v-else-if="column.display && indexColumn == 'date'" :field="indexColumn"> + <b-radio /> </b-field> </div> </div> <ul v-show="(!scope.isLeaf || remainingOption.length) && open[index]" class="rows"> <AuthorizationTable - :authReference="getNextAuthreference(scope)" - :columnsVisible="columnsVisible" - :remaining-option="getRemainingOption(scope)" - v-on:selected-checkbox="emitSelectedCheckbox($event, scope)"/> + :authReference="getNextAuthreference(scope)" + :columnsVisible="columnsVisible" + :remaining-option="getRemainingOption(scope)" + v-on:selected-checkbox="emitSelectedCheckbox($event, scope)" + /> </ul> </div> </li> @@ -31,22 +46,21 @@ </template> <script> -import {Component, Prop, Vue} from "vue-property-decorator"; -import {FontAwesomeIcon} from "@fortawesome/vue-fontawesome"; +import { Component, Prop, Vue } from "vue-property-decorator"; +import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; @Component({ - components: {FontAwesomeIcon}, + components: { FontAwesomeIcon }, }) export default class AuthorizationTable extends Vue { @Prop() authReference; @Prop() remainingOption; @Prop() columnsVisible; initialized = false; - open = {} + open = {}; emits = ["selected-checkbox"]; - mounted() { - } + mounted() {} init() { if (this.initialized) { @@ -55,20 +69,24 @@ export default class AuthorizationTable extends Vue { if (this?.authReference && !this?.authReference?.hierarchicalKey) { for (const index in this.authReference) { if (!this.authReference[index].isLeaf || this.remainingOption.length) - this.open[index] = false; + this.open[index] = false; } } this.initialized = !this.initialized; } - localName(scope){ - return scope.localName || (this.authReference.authorizationScope && this.authReference.authorizationScope.localName) || 'pas trouve' + localName(scope) { + return ( + scope.localName || + (this.authReference.authorizationScope && this.authReference.authorizationScope.localName) || + "pas trouve" + ); } toggle(index) { this.init(); - var open = {} + var open = {}; open[index] = !this.open[index]; - this.open = {...this.open, ...open} + this.open = { ...this.open, ...open }; } select(option) { @@ -77,43 +95,42 @@ export default class AuthorizationTable extends Vue { getNextAuthreference(scope) { if (!scope.isLeaf) { - return scope.referenceValues + return scope.referenceValues; } else { - return this.remainingOption.length?this.remainingOption[0] :scope.referenceValues ; + return this.remainingOption.length ? this.remainingOption[0] : scope.referenceValues; } } getRemainingOption(scope) { if (scope.isLeaf) { - return this.remainingOption.slice(1, this.remainingOption.length) + return this.remainingOption.slice(1, this.remainingOption.length); } else { return this.remainingOption; } } selectCheckbox(event, indexColumn, scope) { - var authorizationScope = {} + var authorizationScope = {}; + console.log(scope); let id = scope.authorizationScope; authorizationScope[id] = scope.key; { - this.$emit('selected-checkbox', - { - checked: event, - type: indexColumn, - authorizationScope: authorizationScope - } - ) + this.$emit("selected-checkbox", { + checked: event, + type: indexColumn, + authorizationScope: authorizationScope, + }); } } - emitSelectedCheckbox(event,scope) { + emitSelectedCheckbox(event, scope) { let id = scope.authorizationScope; - if (event.authorizationScope[id]==null){ - event.authorizationScope[id]=scope.key - }else{ - event.authorizationScope[id] =scope.key+'.'+event.authorizationScope[id] + if (event.authorizationScope[id] == null) { + event.authorizationScope[id] = scope.key; + } else { + event.authorizationScope[id] = scope.key + "." + event.authorizationScope[id]; } - this.$emit('selected-checkbox', event); + this.$emit("selected-checkbox", event); } } </script> @@ -121,8 +138,7 @@ export default class AuthorizationTable extends Vue { <style lang="scss" scoped> .authorizationTable { margin-left: 10px; - margin-right: 10px; - padding: 5px; + padding: 0 0 0 5px; button { opacity: 0.75; @@ -132,4 +148,7 @@ export default class AuthorizationTable extends Vue { opacity: 0.5; } } +::marker{ + color: transparent; +} </style> \ No newline at end of file diff --git a/ui2/src/components/common/DropDownMenu.vue b/ui2/src/components/common/DropDownMenu.vue index 63eb47cfe..ce05f443e 100644 --- a/ui2/src/components/common/DropDownMenu.vue +++ b/ui2/src/components/common/DropDownMenu.vue @@ -4,7 +4,7 @@ <b-dropdown-item v-if="option.isLeaf" :value="option.referenceValues" @click="select()"> {{ option.localName }} </b-dropdown-item> - <b-dropdown v-else v-on:select-menu-item="select" :ref="option.key" expanded > + <b-dropdown v-else v-on:select-menu-item="select" :ref="option.key" expanded> <template #trigger="{ active }"> <b-button expanded @@ -47,21 +47,21 @@ export default class DropDownMenu extends Vue { margin-right: 10px; padding: 5px; button { - background-color: rgba(0,100,100, 0.85); + background-color: rgba(0, 100, 100, 0.85); } - .dropdown-menu .dropdown-content .dropDownMenu { - button{ - background-color: rgba(0,100,100, 0.70); + .dropdown-menu .dropdown-content .dropDownMenu { + button { + background-color: rgba(0, 100, 100, 0.7); } - .dropdown-menu .dropdown-content .dropDownMenu { - button{ - background-color: rgba(0,100,100, 0.55); - .dropdown-menu .dropdown-content .dropDownMenu { - button{ - background-color: rgba(0,100,100, 0.40); - .dropdown-menu .dropdown-content .dropDownMenu { - button{ - background-color: rgba(0,100,100, 0.25); + .dropdown-menu .dropdown-content .dropDownMenu { + button { + background-color: rgba(0, 100, 100, 0.55); + .dropdown-menu .dropdown-content .dropDownMenu { + button { + background-color: rgba(0, 100, 100, 0.4); + .dropdown-menu .dropdown-content .dropDownMenu { + button { + background-color: rgba(0, 100, 100, 0.25); color: black; } } diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue index 31bdf9c83..495c0c383 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue @@ -1,30 +1,30 @@ <template> <PageView class="with-submenu"> - <SubMenu :paths="subMenuPaths" :root="application.localName || application.title"/> + <SubMenu :paths="subMenuPaths" :root="application.localName || application.title" /> <h1 class="title main-title"> <span v-if="authorizationId === 'new'">{{ - $t("titles.data-type-new-authorization", { - dataType: application.localDatatypeName || dataTypeId, - }) - }}</span> + $t("titles.data-type-new-authorization", { + dataType: application.localDatatypeName || dataTypeId, + }) + }}</span> </h1> <ValidationObserver ref="observer" v-slot="{ handleSubmit }"> <ValidationProvider v-slot="{ errors, valid }" name="users" rules="required" vid="users"> <b-field - :label="$t('dataTypeAuthorizations.users')" - :message="errors[0]" - :type="{ + :label="$t('dataTypeAuthorizations.users')" + :message="errors[0]" + :type="{ 'is-danger': errors && errors.length > 0, 'is-success': valid, }" - class="mb-4" + class="mb-4" > <b-select - v-model="userToAuthorize" - :placeholder="$t('dataTypeAuthorizations.users-placeholder')" - expanded + v-model="userToAuthorize" + :placeholder="$t('dataTypeAuthorizations.users-placeholder')" + expanded > <option v-for="user in users" :key="user.id" :value="user.id"> {{ user.label }} @@ -34,188 +34,51 @@ </ValidationProvider> <ValidationProvider - v-slot="{ errors, valid }" - name="dataGroups" - rules="required" - vid="dataGroups" + v-slot="{ errors, valid }" + name="dataGroups" + rules="required" + vid="dataGroups" > <b-field - :label="$t('dataTypeAuthorizations.data-groups')" - :message="errors[0]" - :type="{ + :label="$t('dataTypeAuthorizations.data-groups')" + :message="errors[0]" + :type="{ 'is-danger': errors && errors.length > 0, 'is-success': valid, }" > <b-taginput - v-model="dataGroupToAuthorize" - :data="dataGroups" - :open-on-focus="true" - :placeholder="$t('dataTypeAuthorizations.data-groups-placeholder')" - :value="dataGroups.id" - autocomplete - field="label" - type="is-primary" + v-model="dataGroupToAuthorize" + :data="dataGroups" + :open-on-focus="true" + :placeholder="$t('dataTypeAuthorizations.data-groups-placeholder')" + :value="dataGroups.id" + autocomplete + field="label" + type="is-primary" > </b-taginput> </b-field> </ValidationProvider> - <AuthorizationTable :authReference="authReferences[0]" - :columnsVisible="columnsVisible" - :remaining-option="authReferences.slice && authReferences.slice(1,authReferences.length)" - @selected-checkbox="emitSelectedCheckbox($event)" - class="rows"> + <AuthorizationTable + :authReference="authReferences[0]" + :columnsVisible="columnsVisible" + :remaining-option="authReferences.slice && authReferences.slice(1, authReferences.length)" + @selected-checkbox="emitSelectedCheckbox($event)" + class="rows" + > <div class="row"> <div class="columns"> - <b-field v-for="(column, indexColumn) of columnsVisible" :key="indexColumn" :field="indexColumn" - :label="column.title" class="column"></b-field> + <b-field + v-for="(column, indexColumn) of columnsVisible" + :key="indexColumn" + :field="indexColumn" + :label="column.title" + class="column" + ></b-field> </div> </div> </AuthorizationTable> - <!--ValidationProvider-- rules="required" name="scopes" v-slot="{ errors, valid }" vid="scopes"> - <b-field - :label="$t('dataTypeAuthorizations.authorization-scopes')" - class="mb-4" - :type="{ - 'is-danger': errors && errors.length > 0, - 'is-success': valid, - }" - :message="errors[0]" - > - <b-collapse - class="card" - animation="slide" - v-for="(scope, index) of authorizationScopes" - :key="scope.id" - :open="openCollapse == index" - @open="openCollapse = index" - > - <template #trigger="props"> - <div class="card-header" role="button"> - <p class="card-header-title"> - {{ scope.label }} - </p> - <a class="card-header-icon"> - <b-icon :icon="props.open ? 'chevron-down' : 'chevron-up'"></b-icon> - </a> - </div> - </template> - - <div class="card-content"> - <div class="content"> - <b-table - :data="scope.options" - class="table is-striped" - ref="table" - detailed - hoverable - custom-detail-row - detail-key="id" - :show-detail-icon="false" - > - <b-table-column - field="label" - :visible="columnsVisible['label'].display" - :label="columnsVisible['label'].title" - v-slot="props" - > - <template v-if="props.row.children.length === 0"> - {{ props.row.label }} - </template> - <template v-else> - <a @click="props.toggleDetails(props.row)"> - {{ props.row.label }} - </a> - </template> - </b-table-column> - <b-table-column - field="admin" - :visible="columnsVisible['admin'].display" - :label="columnsVisible['admin'].title" - centered - v-slot="props" - > - <b-checkbox size="is-medium" v-model="props.row.admin"> </b-checkbox> - </b-table-column> - <b-table-column - field="depot" - :visible="columnsVisible['depot'].display" - :label="columnsVisible['depot'].title" - centered - v-slot="props" - > - <b-checkbox size="is-medium" v-model="props.row.depot"> </b-checkbox> - </b-table-column> - <b-table-column - field="publication" - :visible="columnsVisible['publication'].display" - :label="columnsVisible['publication'].title" - centered - v-slot="props" - > - <b-checkbox size="is-medium" v-model="props.row.publication"></b-checkbox> - </b-table-column> - <b-table-column - field="extraction" - :visible="columnsVisible['extraction'].display" - :label="columnsVisible['extraction'].title" - centered - v-slot="props" - > - <b-checkbox size="is-medium" v-model="props.row.extraction"> </b-checkbox> - </b-table-column> - <b-table-column - field="date" - :visible="columnsVisible['date'].display" - :label="columnsVisible['date'].title" - centered - > - <b-radio - class="DataTypeAuthorizationInfoView-radio-field" - name="dataTypeAuthorization-period" - v-model="period" - :native-value="periods.ALWAYS" - > - <span class="DataTypeAuthorizationInfoView-radio-label"> - {{ periods.ALWAYS }}</span - > - </b-radio> - </b-table-column> - <template slot="detail" slot-scope="props" v-if="props.row.children.length > 0"> - <tr v-for="item in props.row.children" :key="item.id"> - <td v-show="columnsVisible['label'].display"> - <template v-if="item.children.length === 0"> - {{ item.label }} - </template> - <template v-else> - <a @click="item.toggleDetails(item)"> - {{ item.label }} - </a> - </template> - </td> - <td v-show="columnsVisible['admin'].display" class="has-text-centered"> - <b-checkbox v-model="item.admin"> </b-checkbox> - </td> - <td v-show="columnsVisible['depot'].display" class="has-text-centered"> - <b-checkbox v-model="item.depot"> </b-checkbox> - </td> - <td v-show="columnsVisible['publication'].display" class="has-text-centered"> - <b-checkbox v-model="item.publication"> </b-checkbox> - </td> - <td v-show="columnsVisible['extraction'].display" class="has-text-centered"> - <b-checkbox v-model="item.extraction"> </b-checkbox> - </td> - <td v-show="columnsVisible['date'].display" class="has-text-centered"> - {{ item.date }} - </td> - </tr> - </template> - </b-table> - </div> - </div> - </b-collapse> - </b-field> - </ValidationProvider--> <div class="buttons"> <b-button icon-left="plus" type="is-primary" @click="handleSubmit(createAuthorization)"> @@ -228,23 +91,30 @@ <script> import CollapsibleTree from "@/components/common/CollapsibleTree.vue"; -import SubMenu, {SubMenuPath} from "@/components/common/SubMenu.vue"; -import {DataTypeAuthorization} from "@/model/DataTypeAuthorization"; -import {AlertService} from "@/services/AlertService"; -import {ApplicationService} from "@/services/rest/ApplicationService"; -import {AuthorizationService} from "@/services/rest/AuthorizationService"; -import {UserPreferencesService} from "@/services/UserPreferencesService"; -import {ValidationObserver, ValidationProvider} from "vee-validate"; -import {Component, Prop, Vue, Watch} from "vue-property-decorator"; +import SubMenu, { SubMenuPath } from "@/components/common/SubMenu.vue"; +import { DataTypeAuthorization } from "@/model/DataTypeAuthorization"; +import { AlertService } from "@/services/AlertService"; +import { ApplicationService } from "@/services/rest/ApplicationService"; +import { AuthorizationService } from "@/services/rest/AuthorizationService"; +import { UserPreferencesService } from "@/services/UserPreferencesService"; +import { ValidationObserver, ValidationProvider } from "vee-validate"; +import { Component, Prop, Vue, Watch } from "vue-property-decorator"; import PageView from "../common/PageView.vue"; -import {InternationalisationService} from "@/services/InternationalisationService"; -import {ApplicationResult} from "@/model/ApplicationResult"; -import {LOCAL_STORAGE_LANG} from "@/services/Fetcher"; -import {ReferenceService} from "@/services/rest/ReferenceService"; +import { InternationalisationService } from "@/services/InternationalisationService"; +import { ApplicationResult } from "@/model/ApplicationResult"; +import { LOCAL_STORAGE_LANG } from "@/services/Fetcher"; +import { ReferenceService } from "@/services/rest/ReferenceService"; import AuthorizationTable from "@/components/common/AuthorizationTable"; @Component({ - components: {AuthorizationTable, PageView, SubMenu, CollapsibleTree, ValidationObserver, ValidationProvider}, + components: { + AuthorizationTable, + PageView, + SubMenu, + CollapsibleTree, + ValidationObserver, + ValidationProvider, + }, }) export default class DataTypeAuthorizationInfoView extends Vue { @Prop() dataTypeId; @@ -267,12 +137,12 @@ export default class DataTypeAuthorizationInfoView extends Vue { }; columnsVisible = { - label: {title: "Label", display: true}, - admin: {title: "Admin", display: true}, - depot: {title: "Dépôt", display: true}, - publication: {title: "Publication", display: true}, - extraction: {title: "Extraction", display: true}, - date: {title: "Périodes", display: true}, + label: { title: "Label", display: true }, + admin: { title: "Admin", display: true }, + depot: { title: "Dépôt", display: true }, + publication: { title: "Publication", display: true }, + extraction: { title: "Extraction", display: true }, + date: { title: "Périodes", display: true }, }; checkbox = false; authorizations = []; @@ -298,30 +168,29 @@ export default class DataTypeAuthorizationInfoView extends Vue { this.chosenLocale = this.userPreferencesService.getUserPrefLocale(); this.subMenuPaths = [ new SubMenuPath( - this.$t("dataTypesManagement.data-types").toLowerCase(), - () => this.$router.push(`/applications/${this.applicationName}/dataTypes`), - () => this.$router.push("/applications") + this.$t("dataTypesManagement.data-types").toLowerCase(), + () => this.$router.push(`/applications/${this.applicationName}/dataTypes`), + () => this.$router.push("/applications") ), new SubMenuPath( - this.$t(`dataTypeAuthorizations.sub-menu-data-type-authorizations`, { - dataType: this.dataTypeId, - }), - () => { - this.$router.push( - `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations` - ); - }, - () => this.$router.push(`/applications/${this.applicationName}/dataTypes`) + this.$t(`dataTypeAuthorizations.sub-menu-data-type-authorizations`, { + dataType: this.dataTypeId, + }), + () => { + this.$router.push( + `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations` + ); + }, + () => this.$router.push(`/applications/${this.applicationName}/dataTypes`) ), new SubMenuPath( - this.$t(`dataTypeAuthorizations.sub-menu-new-authorization`), - () => { - }, - () => { - this.$router.push( - `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations` - ); - } + this.$t(`dataTypeAuthorizations.sub-menu-new-authorization`), + () => {}, + () => { + this.$router.push( + `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations` + ); + } ), ]; } @@ -329,10 +198,10 @@ export default class DataTypeAuthorizationInfoView extends Vue { showDetail(parent) { for (const child in parent) { if (parent[child].children.length !== 0) { - parent[child] = {...parent[child], showDetailIcon: true}; + parent[child] = { ...parent[child], showDetailIcon: true }; console.log(parent[child]); } - parent[child] = {...parent[child], showDetailIcon: false}; + parent[child] = { ...parent[child], showDetailIcon: false }; console.log(parent[child]); } } @@ -342,21 +211,21 @@ export default class DataTypeAuthorizationInfoView extends Vue { this.applications = await this.applicationService.getApplications(); this.application = await this.applicationService.getApplication(this.applicationName); this.configuration = this.applications - .filter((a) => a.name === this.applicationName) - .map((a) => a.configuration.dataTypes[this.dataTypeId])[0]; + .filter((a) => a.name === this.applicationName) + .map((a) => a.configuration.dataTypes[this.dataTypeId])[0]; this.application = { ...this.application, localName: this.internationalisationService.mergeInternationalization(this.application) - .localName, + .localName, localDatatypeName: this.internationalisationService.localeDataTypeIdName( - this.application, - this.application.dataTypes[this.dataTypeId] + this.application, + this.application.dataTypes[this.dataTypeId] ), }; this.authorizations = this.configuration.authorization.authorizationScopes; const grantableInfos = await this.authorizationService.getAuthorizationGrantableInfos( - this.applicationName, - this.dataTypeId + this.applicationName, + this.dataTypeId ); ({ authorizationScopes: this.authorizationScopes, @@ -373,33 +242,36 @@ export default class DataTypeAuthorizationInfoView extends Vue { let authorizationScope = grantableInfos.authorizationScopes[auth]; let vc = this.authorizations[authorizationScope?.label]; var reference = - this.configuration.data[vc.variable].components[vc.component].checker.params.refType; + this.configuration.data[vc.variable].components[vc.component].checker.params.refType; let ref = await this.getOrLoadReferences(reference); - ret[auth] = {references: ref, authorizationScope:authorizationScope.label}; + ret[auth] = { references: ref, authorizationScope: authorizationScope.label }; } let refs = Object.values(ret) - .reduce( - (acc, k) => [ - ...acc, - ...k.references.referenceValues.reduce( - (a, b) => [...a, ...b.hierarchicalReference.split(".")], - acc - ), - ], - [] - ) - .reduce((a, b) => { - if (a.indexOf(b) < 0) { - a.push(b); - } - return a; - }, []); + .reduce( + (acc, k) => [ + ...acc, + ...k.references.referenceValues.reduce( + (a, b) => [...a, ...b.hierarchicalReference.split(".")], + acc + ), + ], + [] + ) + .reduce((a, b) => { + if (a.indexOf(b) < 0) { + a.push(b); + } + return a; + }, []); for (const refsKey in refs) { await this.getOrLoadReferences(refs[refsKey]); } - var remainingAuthorizations = [] + var remainingAuthorizations = []; for (const key in ret) { - let partition = await this.partitionReferencesValues(ret[key]?.references?.referenceValues,ret[key]?.authorizationScope); + let partition = await this.partitionReferencesValues( + ret[key]?.references?.referenceValues, + ret[key]?.authorizationScope + ); remainingAuthorizations[key] = partition; } this.authReferences = remainingAuthorizations.reverse(); @@ -408,7 +280,12 @@ export default class DataTypeAuthorizationInfoView extends Vue { } } - async partitionReferencesValues(referencesValues, authorizationScope, currentPath, currentCompleteLocalName) { + async partitionReferencesValues( + referencesValues, + authorizationScope, + currentPath, + currentCompleteLocalName + ) { let returnValues = {}; for (const referenceValue of referencesValues) { var previousKeySplit = currentPath ? currentPath.split(".") : []; @@ -435,7 +312,7 @@ export default class DataTypeAuthorizationInfoView extends Vue { localName = key; } var completeLocalName = - typeof currentCompleteLocalName === "undefined" ? "" : currentCompleteLocalName; + typeof currentCompleteLocalName === "undefined" ? "" : currentCompleteLocalName; completeLocalName = completeLocalName + (completeLocalName == "" ? "" : ",") + localName; let authPartition = returnValues[key] || { key, @@ -454,21 +331,21 @@ export default class DataTypeAuthorizationInfoView extends Vue { var auth = returnValues[returnValuesKey]; let referenceValueLeaf = auth.referenceValues?.[0]; if ( - auth.referenceValues.length <= 1 && - referenceValueLeaf.hierarchicalKey == auth.currentPath + auth.referenceValues.length <= 1 && + referenceValueLeaf.hierarchicalKey == auth.currentPath ) { returnValues[returnValuesKey] = { ...auth, authorizationScope, isLeaf: true, - referenceValues: {...referenceValueLeaf, authorizationScope}, + referenceValues: { ...referenceValueLeaf, authorizationScope }, }; } else { var r = await this.partitionReferencesValues( - auth.referenceValues, - authorizationScope, - auth.currentPath, - auth.completeLocalName + auth.referenceValues, + authorizationScope, + auth.currentPath, + auth.completeLocalName ); returnValues[returnValuesKey] = { ...auth, @@ -521,20 +398,20 @@ export default class DataTypeAuthorizationInfoView extends Vue { try { await this.authorizationService.createAuthorization( - this.applicationName, - this.dataTypeId, - dataTypeAuthorization + this.applicationName, + this.dataTypeId, + dataTypeAuthorization ); this.alertService.toastSuccess(this.$t("alert.create-authorization")); this.$router.push( - `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations` + `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations` ); } catch (error) { this.alertService.toastServerError(error); } } - emitSelectedCheckbox(event){ - console.log(event) + emitSelectedCheckbox(event) { + console.log(event); } } </script> @@ -567,13 +444,25 @@ export default class DataTypeAuthorizationInfoView extends Vue { visibility: hidden; display: none; } -.leaf label{ +.leaf label { font-weight: lighter; font-style: italic; color: #2c3e50; } -.folder label{ +.folder label { font-weight: bolder; - color: #007F7F; + color: #007f7f; +} +.rows .row .columns .column { + padding: 0 0 0 10px; + border-bottom: 2px solid; + border-color: $dark; + margin-bottom: 12px; +} +ul li.card-content { + background-color: rgba(0, 0, 0, 0.05); +} +a { + color: $dark; } -</style> \ No newline at end of file +</style> diff --git a/ui2/src/views/datatype/DataTypesRepositoryView.vue b/ui2/src/views/datatype/DataTypesRepositoryView.vue index ac6772a51..3b240a87c 100644 --- a/ui2/src/views/datatype/DataTypesRepositoryView.vue +++ b/ui2/src/views/datatype/DataTypesRepositoryView.vue @@ -114,7 +114,9 @@ <b-upload v-model="file" class="file-label" style="margin-top: 30px"> <span class="file-cta"> <b-icon class="file-icon" icon="upload"></b-icon> - <span class="file-label">{{ $t("dataTypesRepository.choose-file") }}</span> + <span class="file-label">{{ + $t("dataTypesRepository.choose-file") + }}</span> </span> <span v-if="file" class="file-name"> {{ file.name }} @@ -129,9 +131,9 @@ </div> <footer class="card-footer"> <div class="column is-10"></div> - <div class="column is-2" style="float: right;"> - <b-button type="is-dark" @click="upload" style="float: right;" - expanded>{{ $t("dataTypesRepository.submit") }} + <div class="column is-2" style="float: right"> + <b-button type="is-dark" @click="upload" style="float: right" expanded + >{{ $t("dataTypesRepository.submit") }} </b-button> </div> </footer> @@ -139,12 +141,13 @@ </form> </div> </div> - <div v-if="isAuthorisationsSelected()" class="columns"> + <div v-if="isAuthorisationsSelected()" class="columns"> <div class="card column"> <div class="card-content"> <table - v-if="datasets && Object.keys(datasets).length" - class="table is-striped is-fullwidth" style="text-align: center; vertical-align: center" + v-if="datasets && Object.keys(datasets).length" + class="table is-striped is-fullwidth" + style="text-align: center; vertical-align: center" > <caption> {{ @@ -157,10 +160,10 @@ <th align>{{ $t("dataTypesRepository.table-file-data-publication") }}</th> </tr> <tr - v-for="(dataset, periode) in datasets" - :key="dataset.id" - @click="showDatasets(dataset)" - style="cursor: pointer" + v-for="(dataset, periode) in datasets" + :key="dataset.id" + @click="showDatasets(dataset)" + style="cursor: pointer" > <td align>{{ periode }}</td> <td align>{{ Object.keys(dataset.datasets).length }}</td> @@ -168,8 +171,9 @@ </tr> </table> <table - v-if="currentDataset && currentDataset.length" - class="table is-striped is-fullwidth" style="text-align: center; vertical-align: center" + v-if="currentDataset && currentDataset.length" + class="table is-striped is-fullwidth" + style="text-align: center; vertical-align: center" > <caption> {{ @@ -199,20 +203,20 @@ <td align> <b-field> <b-button - :icon-right="dataset.params.published ? 'check-circle' : 'circle'" - size="is-medium" - type="is-primary is-light" - @click="publish(dataset, !dataset.params.published)" + :icon-right="dataset.params.published ? 'check-circle' : 'circle'" + size="is-medium" + type="is-primary is-light" + @click="publish(dataset, !dataset.params.published)" /> </b-field> </td> <td> <b-field> <b-button - icon-right="trash-alt" - size="is-medium" - type="is-danger is-light" - @click="remove(dataset, dataset.params.published)" + icon-right="trash-alt" + size="is-medium" + type="is-danger is-light" + @click="remove(dataset, dataset.params.published)" /> </b-field> </td> -- GitLab From db4daccfc4651751e582ea1b0478cb65473db514 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Mon, 8 Nov 2021 16:50:58 +0100 Subject: [PATCH 02/24] =?UTF-8?q?essai=20du=20check=20des=20box=20avec=20l?= =?UTF-8?q?a=20r=C3=A9cursivit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/AuthorizationTable.vue | 56 +++++++++++++++++-- .../DataTypeAuthorizationInfoView.vue | 1 + 2 files changed, 52 insertions(+), 5 deletions(-) diff --git a/ui2/src/components/common/AuthorizationTable.vue b/ui2/src/components/common/AuthorizationTable.vue index 91dca9a23..e9763084a 100644 --- a/ui2/src/components/common/AuthorizationTable.vue +++ b/ui2/src/components/common/AuthorizationTable.vue @@ -14,7 +14,8 @@ :class="!scope.isLeaf || remainingOption.length ? 'leaf' : 'folder'" :field="indexColumn" @click="indexColumn == 'label' && toggle(index)" - >{{ localName(scope) }}</a> + >{{ localName(scope) }}</a + > <p v-else-if=" column.display && @@ -23,7 +24,9 @@ " :class="!scope.isLeaf || remainingOption.length ? 'leaf' : 'folder'" :field="indexColumn" - > {{ localName(scope) }}</p> + > + {{ localName(scope) }} + </p> <b-field v-else-if="column.display && indexColumn != 'date'" :field="indexColumn"> <b-checkbox @input="selectCheckbox($event, indexColumn, scope)" /> </b-field> @@ -59,6 +62,10 @@ export default class AuthorizationTable extends Vue { initialized = false; open = {}; emits = ["selected-checkbox"]; + admin = {}; + depot = {}; + publication = {}; + extraction = {}; mounted() {} @@ -111,9 +118,45 @@ export default class AuthorizationTable extends Vue { selectCheckbox(event, indexColumn, scope) { var authorizationScope = {}; - console.log(scope); let id = scope.authorizationScope; authorizationScope[id] = scope.key; + if (indexColumn === "admin") { + this.admin= { + admin: { + checked: event, + type: indexColumn, + authorizationScope: authorizationScope, + } + }; + } + if (indexColumn === "depot") { + this.depot= { + depot: { + checked: event, + type: indexColumn, + authorizationScope: authorizationScope, + } + }; + } + if (indexColumn === "publication") { + this.publication= { + publication: { + checked: event, + type: indexColumn, + authorizationScope: authorizationScope, + } + }; + } + if (indexColumn === "extraction") { + this.extraction= { + extraction: { + checked: event, + type: indexColumn, + authorizationScope: authorizationScope, + } + }; + } + scope = {...scope, ...this.depot, ...this.admin, ...this.publication, ...this.extraction,} { this.$emit("selected-checkbox", { checked: event, @@ -121,6 +164,8 @@ export default class AuthorizationTable extends Vue { authorizationScope: authorizationScope, }); } + console.log(scope); + console.log(scope.admin); } emitSelectedCheckbox(event, scope) { @@ -131,6 +176,7 @@ export default class AuthorizationTable extends Vue { event.authorizationScope[id] = scope.key + "." + event.authorizationScope[id]; } this.$emit("selected-checkbox", event); + console.log(this.$emit("selected-checkbox", event)); } } </script> @@ -148,7 +194,7 @@ export default class AuthorizationTable extends Vue { opacity: 0.5; } } -::marker{ +::marker { color: transparent; } -</style> \ No newline at end of file +</style> diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue index 495c0c383..4d0c7cd85 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue @@ -411,6 +411,7 @@ export default class DataTypeAuthorizationInfoView extends Vue { } } emitSelectedCheckbox(event) { + console.log(event); } } -- GitLab From bebed0db486e4952470b13125207b76e2388acce Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Tue, 16 Nov 2021 10:08:46 +0100 Subject: [PATCH 03/24] =?UTF-8?q?r=C3=A9glage=20bug=20responssive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui2/src/style/_common.scss | 4 ++++ ui2/src/views/application/ApplicationsView.vue | 10 +++++----- ui2/src/views/common/MenuView.vue | 2 +- ui2/src/views/datatype/DataTypeTableView.vue | 12 ++++++------ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/ui2/src/style/_common.scss b/ui2/src/style/_common.scss index 9f495de85..a7fe321e8 100644 --- a/ui2/src/style/_common.scss +++ b/ui2/src/style/_common.scss @@ -4,6 +4,10 @@ body { height: 100%; } +.navbar-menu.is-active{ + background-color: $primary; +} + .title { color: $primary; margin-top: $title-margin-top; diff --git a/ui2/src/views/application/ApplicationsView.vue b/ui2/src/views/application/ApplicationsView.vue index 734ec7ba6..f22fc5193 100644 --- a/ui2/src/views/application/ApplicationsView.vue +++ b/ui2/src/views/application/ApplicationsView.vue @@ -3,7 +3,7 @@ <h1 class="title main-title">{{ $t("titles.applications-page") }}</h1> <div class="columns columnPrincipale"> - <div class="column is-3-desktop is-12-tablet"> + <div class="column is-3-widescreen is-12-desktop"> <section> <div v-if="canCreateApplication" class="card is-clickable"> <div @@ -89,10 +89,10 @@ </div> </section> </div> - <div class="column is-9-desktop is-12-tablet"> + <div class="column is-9-widescreen is-12-desktop"> <div class="columns"> - <div v-for="(application, index) in selectedApplications" v-bind:key="application.name"> - <div class="column is-3-desktop is-6-tablet is-12-mobile"> + <div v-for="(application, index) in selectedApplications" v-bind:key="application.name" style="margin-left: 30px"> + <div class="column is-3-widescreen is-6-desktop is-12-tablet"> <div v-if="index >= (current - 1) * perPage && index < current * perPage" class="applicationCard card" @@ -294,7 +294,7 @@ export default class ApplicationsView extends Vue { margin: 0px; &.columnPrincipale { - margin-left: 100px; + margin-left: 50px; margin-top: 50px; } } diff --git a/ui2/src/views/common/MenuView.vue b/ui2/src/views/common/MenuView.vue index b5d862810..352344ff1 100644 --- a/ui2/src/views/common/MenuView.vue +++ b/ui2/src/views/common/MenuView.vue @@ -6,13 +6,13 @@ <img class="logo_blanc" src="@/assets/logo-inrae_blanc.svg" /> <img class="logo_vert" src="@/assets/Logo-INRAE.svg" /> </b-navbar-item> - <img class="logo_rep" src="@/assets/Rep-FR-logo.svg" /> <b-navbar-item tag="router-link" :to="{ path: '/applications' }"> {{ $t("menu.applications") }} </b-navbar-item> </template> <template #end> + <img class="logo_rep" src="@/assets/Rep-FR-logo.svg" /> <b-navbar-item tag="div"> <b-field> <b-select diff --git a/ui2/src/views/datatype/DataTypeTableView.vue b/ui2/src/views/datatype/DataTypeTableView.vue index 9531c6bd8..a0b5c4dcf 100644 --- a/ui2/src/views/datatype/DataTypeTableView.vue +++ b/ui2/src/views/datatype/DataTypeTableView.vue @@ -111,7 +111,7 @@ <h2>{{ $t("applications.trier") }}</h2> <div class="content"> <div class="columns is-multiline"> - <div class="column is-9-desktop is-12-tablet"> + <div class="column is-9-widescreen is-12-desktop"> <b-tabs v-model="activeTab" :multiline="true" @@ -166,7 +166,7 @@ </template> </b-tabs> </div> - <div class="column is-3-desktop is-12-tablet"> + <div class="column is-3-widescreen is-12-desktop"> <draggable class="rows"> <div v-for="(variableComponent, index) in this.params.variableComponentOrderBy" @@ -231,7 +231,7 @@ <h2>{{ $t("applications.filter") }}</h2> <div class="columns is-multiline"> <div - class="column is-2-desktop is-6-tablet is-12-mobile" + class="column is-2-widescreen is-6-desktop is-12-tablet" v-for="(variable, index) in variables" :key="variable.id" :variable="variable.id" @@ -288,7 +288,7 @@ </div> </div> <div class="columns"> - <div class="column is-8"> + <div class="column is-8-widescreen is-6-desktop"> {{ $t("dataTypesManagement.filtered") }} {{ $t("ponctuation.colon") }} <b-field grouped group-multiline> <b-taglist> @@ -311,13 +311,13 @@ </b-taglist> </b-field> </div> - <div class="column is-2"> + <div class="column is-2-widescreen is-3-desktop"> <b-button icon-left="redo" expanded type="is-danger" outlined @click="clearSearch" >{{ $t("dataTypesManagement.réinitialiser") }} {{ $t("dataTypesManagement.filtre") }}</b-button > </div> - <div class="column is-2"> + <div class="column is-2-widescreen is-3-desktop"> <p class="control"> <b-button icon-left="check" type="is-dark" expanded outlined @click="addSearch" >{{ $t("dataTypesManagement.validate") }} -- GitLab From 5ffe850fe15ff38138909245665a30ff7997a593 Mon Sep 17 00:00:00 2001 From: TCHERNIATINSKY <philippe.tcherniatinsky@inrae.fr> Date: Tue, 16 Nov 2021 11:58:48 +0100 Subject: [PATCH 04/24] =?UTF-8?q?pr=C3=A9sentation=20des=20authorizations?= =?UTF-8?q?=20par=20utilisateurs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../DataTypeAuthorizationsView.vue | 237 ++++++++++-------- 1 file changed, 138 insertions(+), 99 deletions(-) diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue index a2ca446ae..dcc05aa8a 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue @@ -1,6 +1,6 @@ <template> <PageView class="with-submenu"> - <SubMenu :root="application.localName || application.title" :paths="subMenuPaths" /> + <SubMenu :paths="subMenuPaths" :root="application.localName || application.title"/> <h1 class="title main-title"> {{ $t("titles.data-type-authorizations", { @@ -8,86 +8,107 @@ }) }} </h1> - <div class="buttons"> - <b-button type="is-primary" @click="addAuthorization" icon-left="plus"> - {{ $t("dataTypeAuthorizations.add-auhtorization") }} - </b-button> - </div> + <div class="rows"> + <div class="row"> + <div class="columns"> + <div class="card column is-10"> + <p class="card-header-title"> + Utilisateur + </p> + <b-select v-model="selectedUser" placeholder="Select a name"> + <option + v-for="(option, key) in authorizationByUser" + :key="key" + :value="option"> + {{ key }} + </option> + </b-select> + </div> + <div class="card column is-2"> + <b-button icon-left="plus" type="is-primary is-right" @click="addAuthorization"> + {{ $t("dataTypeAuthorizations.add-auhtorization") }} + </b-button> + </div> + </div> + </div> - <b-table - :data="authorizations" - :striped="true" - :isFocusable="true" - :isHoverable="true" - :sticky-header="true" - :paginated="true" - :per-page="15" - height="100%" - > - <b-table-column - b-table-column - field="user" - :label="$t('dataTypeAuthorizations.user')" - sortable - v-slot="props" + <b-table + v-if="selectedUser" + :data="selectedUser" + :isFocusable="true" + :isHoverable="true" + :paginated="true" + :per-page="15" + :sticky-header="true" + :striped="true" + class="row" + height="100%" > - {{ props.row.user }} - </b-table-column> + <!--b-table-column + v-slot="props" + :label="$t('dataTypeAuthorizations.user')" + b-table-column + field="user" + sortable + > + {{ props.row.user }} + </b-table-column--> - <b-table-column - b-table-column - field="dataGroup" - :label="$t('dataTypeAuthorizations.data-group')" - sortable - v-slot="props" - > - {{ props.row.dataGroup }} - </b-table-column> - <b-table-column - b-table-column - field="dataGroup" - :label="$t('dataTypeAuthorizations.period')" - sortable - v-slot="props" - > - {{ getPeriod(props.row) }} - </b-table-column> - <b-table-column - v-for="scope in scopes" - :key="scope" - b-table-column - :label="scope" - sortable - v-slot="props" - > - {{ props.row.authorizedScopes[scope] }} - </b-table-column> - <b-table-column b-table-column :label="$t('dataTypeAuthorizations.actions')" v-slot="props"> - <b-button - type="is-danger" - size="is-small" - @click="revoke(props.row.id)" - icon-left="trash-alt" + <b-table-column + v-slot="props" + :label="$t('dataTypeAuthorizations.data-group')" + b-table-column + field="dataGroup" + sortable > - {{ $t("dataTypeAuthorizations.revoke") }} - </b-button> - </b-table-column> - </b-table> + {{ props.row.dataGroup }} + </b-table-column> + <b-table-column + v-slot="props" + :label="$t('dataTypeAuthorizations.period')" + b-table-column + field="dataGroup" + sortable + > + {{ getPeriod(props.row) }} + </b-table-column> + <b-table-column + v-for="scope in scopes" + :key="scope" + v-slot="props" + :label="scope" + b-table-column + sortable + > + {{ props.row.authorizedScopes[scope] }} + </b-table-column> + <b-table-column v-slot="props" :label="$t('dataTypeAuthorizations.actions')" b-table-column> + <b-button + icon-left="trash-alt" + size="is-small" + type="is-danger" + @click="revoke(props.row.id)" + > + {{ $t("dataTypeAuthorizations.revoke") }} + </b-button> + </b-table-column> + </b-table> + </div> </PageView> </template> <script> -import SubMenu, { SubMenuPath } from "@/components/common/SubMenu.vue"; -import { AlertService } from "@/services/AlertService"; -import { ApplicationService } from "@/services/rest/ApplicationService"; -import { AuthorizationService } from "@/services/rest/AuthorizationService"; -import { InternationalisationService } from "@/services/InternationalisationService"; -import { Component, Prop, Vue } from "vue-property-decorator"; +import SubMenu, {SubMenuPath} from "@/components/common/SubMenu.vue"; +import {AlertService} from "@/services/AlertService"; +import {ApplicationService} from "@/services/rest/ApplicationService"; +import {AuthorizationService} from "@/services/rest/AuthorizationService"; +import {InternationalisationService} from "@/services/InternationalisationService"; +import {Component, Prop, Vue} from "vue-property-decorator"; import PageView from "../common/PageView.vue"; -import { ApplicationResult } from "@/model/ApplicationResult"; +import {ApplicationResult} from "@/model/ApplicationResult"; @Component({ - components: { PageView, SubMenu }, + components: {PageView, SubMenu}, }) export default class DataTypeAuthorizationsView extends Vue { @Prop() dataTypeId; @@ -97,8 +118,9 @@ export default class DataTypeAuthorizationsView extends Vue { internationalisationService = InternationalisationService.INSTANCE; alertService = AlertService.INSTANCE; applicationService = ApplicationService.INSTANCE; - + selectedUser = null; authorizations = []; + authorizationByUser = {}; application = new ApplicationResult(); scopes = []; periods = { @@ -112,23 +134,33 @@ export default class DataTypeAuthorizationsView extends Vue { this.init(); this.subMenuPaths = [ new SubMenuPath( - this.$t("dataTypesManagement.data-types").toLowerCase(), - () => this.$router.push(`/applications/${this.applicationName}/dataTypes`), - () => this.$router.push("/applications") + this.$t("dataTypesManagement.data-types").toLowerCase(), + () => this.$router.push(`/applications/${this.applicationName}/dataTypes`), + () => this.$router.push("/applications") ), new SubMenuPath( - this.$t(`dataTypeAuthorizations.sub-menu-data-type-authorizations`, { - dataType: this.dataTypeId, - }), - () => { - this.$router.push( - `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations` - ); - }, - () => this.$router.push(`/applications/${this.applicationName}/dataTypes`) + this.$t(`dataTypeAuthorizations.sub-menu-data-type-authorizations`, { + dataType: this.dataTypeId, + }), + () => { + this.$router.push( + `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations` + ); + }, + () => this.$router.push(`/applications/${this.applicationName}/dataTypes`) ), ]; } + // fillAuthorizationtTree(tree, auth){ + // tree = tree ||{}; + // for (const scope in auth.authorizedScopes) { + // var nodes = auth.authorizedScopes[scope].split('.') + // while(node.length){ + // var node = nodes.shift(); + // var nodeScope = tree[node]; + // } + // } + // } async init() { try { @@ -136,16 +168,23 @@ export default class DataTypeAuthorizationsView extends Vue { this.application = { ...this.application, localName: this.internationalisationService.mergeInternationalization(this.application) - .localName, + .localName, localDatatypeName: this.internationalisationService.localeDataTypeIdName( - this.application, - this.application.dataTypes[this.dataTypeId] + this.application, + this.application.dataTypes[this.dataTypeId] ), }; this.authorizations = await this.authorizationService.getDataAuthorizations( - this.applicationName, - this.dataTypeId + this.applicationName, + this.dataTypeId ); + this.authorizationByUser = this.authorizations.reduce((acc, auth) => { + var user = auth.user; + var userAuth = acc[user] || []; + userAuth.push(auth); + acc[user] = userAuth; + return acc; + }, {}) if (this.authorizations && this.authorizations.length !== 0) { this.scopes = Object.keys(this.authorizations[0].authorizedScopes); } @@ -156,21 +195,21 @@ export default class DataTypeAuthorizationsView extends Vue { addAuthorization() { this.$router.push( - `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations/new` + `/applications/${this.applicationName}/dataTypes/${this.dataTypeId}/authorizations/new` ); } async revoke(id) { try { await this.authorizationService.revokeAuthorization( - this.applicationName, - this.dataTypeId, - id + this.applicationName, + this.dataTypeId, + id ); this.alertService.toastSuccess(this.$t("alert.revoke-authorization")); this.authorizations.splice( - this.authorizations.findIndex((a) => a.id === id), - 1 + this.authorizations.findIndex((a) => a.id === id), + 1 ); } catch (error) { this.alertService.toastServerError(error); @@ -182,17 +221,17 @@ export default class DataTypeAuthorizationsView extends Vue { return this.periods.ALWAYS; } else if (authorization.fromDay && !authorization.toDay) { return ( - this.periods.FROM_DATE + - ` ${authorization.fromDay[2]}/${authorization.fromDay[1]}/${authorization.fromDay[0]}` + this.periods.FROM_DATE + + ` ${authorization.fromDay[2]}/${authorization.fromDay[1]}/${authorization.fromDay[0]}` ); } else if (!authorization.fromDay && authorization.toDay) { return ( - this.periods.TO_DATE + - ` ${authorization.toDay[2]}/${authorization.toDay[1]}/${authorization.toDay[0]}` + this.periods.TO_DATE + + ` ${authorization.toDay[2]}/${authorization.toDay[1]}/${authorization.toDay[0]}` ); } else { return `${authorization.fromDay[2]}/${authorization.fromDay[1]}/${authorization.fromDay[0]} - ${authorization.toDay[2]}/${authorization.toDay[1]}/${authorization.toDay[0]}`; } } } -</script> +</script> \ No newline at end of file -- GitLab From afef71f535a835a1fe6066ddf66de92904ddeddd Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Wed, 17 Nov 2021 10:42:21 +0100 Subject: [PATCH 05/24] uniformisation de la pagination des pages --- .../views/application/ApplicationsView.vue | 5 ++++ .../DataTypeAuthorizationInfoView.vue | 7 ++++- .../DataTypeAuthorizationsView.vue | 27 +++++++++++++++---- .../views/references/ReferenceTableView.vue | 21 +++++++++++++-- 4 files changed, 52 insertions(+), 8 deletions(-) diff --git a/ui2/src/views/application/ApplicationsView.vue b/ui2/src/views/application/ApplicationsView.vue index 76c6bb31c..d23730ed2 100644 --- a/ui2/src/views/application/ApplicationsView.vue +++ b/ui2/src/views/application/ApplicationsView.vue @@ -182,6 +182,11 @@ :range-after="2" :range-before="2" :rounded="true" + aria-current-label="Current page" + aria-next-label="Next page" + aria-page-label="Page" + aria-previous-label="Previous page" + order="is-centered" :total="applications.length" > </b-pagination> diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue index 522771d86..3b6855165 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue @@ -57,7 +57,12 @@ </AuthorizationTable> <div class="buttons"> - <b-button icon-left="plus" type="is-primary" @click="handleSubmit(createAuthorization)" style="margin-bottom: 10px"> + <b-button + icon-left="plus" + type="is-primary" + @click="handleSubmit(createAuthorization)" + style="margin-bottom: 10px" + > {{ $t("dataTypeAuthorizations.create") }} </b-button> </div> diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue index d8f4f7862..a0777334f 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue @@ -11,15 +11,15 @@ <div class="rows"> <div class="row"> <div class="columns"> - <div class="card column is-10"> - <p class="card-header-title">{{ $t("dataTypeAuthorizations.users") }}</p> + <div class="column is-10"> + <p>{{ $t("dataTypeAuthorizations.users") }}</p> <b-select v-model="selectedUser" placeholder="Select a name"> <option v-for="(option, key) in authorizationByUser" :key="key" :value="option"> {{ key }} </option> </b-select> </div> - <div class="card column is-2"> + <div class="column is-2"> <b-button icon-left="plus" type="is-primary is-right" @click="addAuthorization"> {{ $t("dataTypeAuthorizations.add-auhtorization") }} </b-button> @@ -32,12 +32,11 @@ :data="selectedUser" :isFocusable="true" :isHoverable="true" - :paginated="true" - :per-page="15" :sticky-header="true" :striped="true" class="row" height="100%" + style="padding-bottom: 20px" > <!--b-table-column v-slot="props" @@ -88,6 +87,22 @@ </b-button> </b-table-column> </b-table> + <b-pagination + v-if="selectedUser && perPage <= selectedUser.length" + v-model="currentPage" + :per-page="perPage" + :total="selectedUser.length" + aria-current-label="Current page" + aria-next-label="Next page" + aria-page-label="Page" + aria-previous-label="Previous page" + order="is-centered" + range-after="3" + range-before="3" + :rounded="true" + style="padding-bottom: 20px" + > + </b-pagination> </div> </PageView> </template> @@ -118,6 +133,8 @@ export default class DataTypeAuthorizationsView extends Vue { authorizationByUser = {}; application = new ApplicationResult(); scopes = []; + currentPage = 1; + perPage = 15; periods = { FROM_DATE: this.$t("dataTypeAuthorizations.from-date"), TO_DATE: this.$t("dataTypeAuthorizations.to-date"), diff --git a/ui2/src/views/references/ReferenceTableView.vue b/ui2/src/views/references/ReferenceTableView.vue index 12ab25380..81d387685 100644 --- a/ui2/src/views/references/ReferenceTableView.vue +++ b/ui2/src/views/references/ReferenceTableView.vue @@ -12,9 +12,8 @@ :isFocusable="true" :isHoverable="true" :sticky-header="true" - :paginated="true" - :per-page="15" height="100%" + style="padding-bottom: 20px" > <b-table-column v-for="column in columns" @@ -40,6 +39,22 @@ </b-collapse> </b-table-column> </b-table> + <b-pagination + v-if="perPage <= tableValues.length" + v-model="currentPage" + :per-page="perPage" + :total="tableValues.length" + aria-current-label="Current page" + aria-next-label="Next page" + aria-page-label="Page" + aria-previous-label="Previous page" + order="is-centered" + range-after="3" + range-before="3" + :rounded="true" + style="padding-bottom: 20px" + > + </b-pagination> </div> </PageView> </template> @@ -72,6 +87,8 @@ export default class ReferenceTableView extends Vue { columns = []; referenceValues = []; tableValues = []; + currentPage = 1; + perPage = 15; async created() { await this.init(); -- GitLab From 2f90ed87d87f2f6a939648b9d20e3418075eae90 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Wed, 17 Nov 2021 10:48:49 +0100 Subject: [PATCH 06/24] =?UTF-8?q?voir=20pour=20mettre=20une=20pagination?= =?UTF-8?q?=20sur=20la=20liste=20des=20r=C3=A9f=C3=A9rences?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../references/ReferencesManagementView.vue | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ui2/src/views/references/ReferencesManagementView.vue b/ui2/src/views/references/ReferencesManagementView.vue index 290e99884..13927bb25 100644 --- a/ui2/src/views/references/ReferencesManagementView.vue +++ b/ui2/src/views/references/ReferencesManagementView.vue @@ -21,6 +21,21 @@ :reference="chosenRef" :closeCb="(newVal) => (openPanel = newVal)" /> +<!-- <b-pagination + v-model="currentPage" + :per-page="params.limit" + :total="references.length" + aria-current-label="Current page" + aria-next-label="Next page" + aria-page-label="Page" + aria-previous-label="Previous page" + order="is-centered" + range-after="3" + range-before="3" + :rounded="true" + style="padding-bottom: 20px" + > + </b-pagination>--> </div> </PageView> </template> @@ -52,6 +67,7 @@ export default class ReferencesManagementView extends Vue { alertService = AlertService.INSTANCE; references = []; + currentPage = 1; openPanel = false; chosenRef = null; application = new ApplicationResult(); -- GitLab From 9a0bece0ef7df30e80ff0509b2a36080302c1ff7 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Thu, 18 Nov 2021 11:35:10 +0100 Subject: [PATCH 07/24] =?UTF-8?q?respect=20des=20normes=20d'accessibilit?= =?UTF-8?q?=C3=A9s=20(contrast=20et=20navigation=20via=20la=20touche=20Tab?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui2/src/components/common/CollapsibleTree.vue | 10 ++++-- ui2/src/components/common/SidePanel.vue | 4 +++ .../datatype/DataTypeDetailsPanel.vue | 2 +- ui2/src/locales/en.json | 6 ++-- ui2/src/locales/fr.json | 7 +++-- ui2/src/main.js | 4 ++- ui2/src/style/_common.scss | 11 +++++-- ui2/src/style/_variables.scss | 5 ++- .../views/application/ApplicationsView.vue | 1 + .../DataTypeAuthorizationInfoView.vue | 11 +++++-- .../DataTypeAuthorizationsView.vue | 9 ++++-- ui2/src/views/common/MenuView.vue | 29 +++++++++++++---- ui2/src/views/common/PageView.vue | 2 +- ui2/src/views/datatype/DataTypeTableView.vue | 31 +++++++++++++------ .../datatype/DataTypesManagementView.vue | 9 ++++-- .../datatype/DataTypesRepositoryView.vue | 7 ++++- .../views/references/ReferenceTableView.vue | 7 ++++- .../references/ReferencesManagementView.vue | 11 +++++-- 18 files changed, 122 insertions(+), 44 deletions(-) diff --git a/ui2/src/components/common/CollapsibleTree.vue b/ui2/src/components/common/CollapsibleTree.vue index 365e3c566..d407be1bc 100644 --- a/ui2/src/components/common/CollapsibleTree.vue +++ b/ui2/src/components/common/CollapsibleTree.vue @@ -6,6 +6,7 @@ } ${option.children && option.children.length !== 0 && displayChildren ? '' : 'mb-1'}`" :style="`background-color:rgba(240, 245, 245, ${1 - level / 2})`" @click="displayChildren = !displayChildren" + @keypress.enter="displayChildren = !displayChildren" > <div class="CollapsibleTree-header-infos"> <div class="CollapsibleTree-header-infos" :style="`transform:translate(${level * 50}px);`"> @@ -13,6 +14,7 @@ v-if="option.children && option.children.length !== 0" :icon="displayChildren ? 'caret-down' : 'caret-right'" class="clickable mr-3" + tabindex="0" /> <b-checkbox @@ -28,6 +30,8 @@ v-else :class="onClickLabelCb ? 'link' : ''" @click="(event) => onClickLabelCb && onClickLabelCb(event, option.label)" + @keypress.enter="(event) => onClickLabelCb && onClickLabelCb(event, option.label)" + tabindex="0" > {{ option.localName || option.label }} </div> @@ -51,12 +55,12 @@ </div> <div v-else> <b-button + icon-left="archive" size="is-small" class="ml-1" - label="Gérer les jeux de données" + :label="$t('dataTypesManagement.manage-datasets')" @click="repositoryRedirect(option.label)" - type="is-dark" - outlined + type="is-info" > </b-button> </div> diff --git a/ui2/src/components/common/SidePanel.vue b/ui2/src/components/common/SidePanel.vue index ff6565ff9..84929bbd2 100644 --- a/ui2/src/components/common/SidePanel.vue +++ b/ui2/src/components/common/SidePanel.vue @@ -47,6 +47,10 @@ export default class SidePanel extends Vue { padding: $container-padding-vert 2.5rem; transition: transform 250ms; + .title { + color: $dark; + } + &.right-align { right: 0; transform: translateX(100%); diff --git a/ui2/src/components/datatype/DataTypeDetailsPanel.vue b/ui2/src/components/datatype/DataTypeDetailsPanel.vue index 691cb272d..9689a0631 100644 --- a/ui2/src/components/datatype/DataTypeDetailsPanel.vue +++ b/ui2/src/components/datatype/DataTypeDetailsPanel.vue @@ -6,7 +6,7 @@ :closeCb="closeCb" > <div class="Panel-buttons"> - <b-button type="is-primary" icon-left="key" @click="consultAuthorization">{{ + <b-button type="is-dark" icon-left="key" @click="consultAuthorization">{{ $t("dataTypesManagement.consult-authorization") }}</b-button> </div> diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json index d7f99ba48..0568a65fa 100644 --- a/ui2/src/locales/en.json +++ b/ui2/src/locales/en.json @@ -44,7 +44,8 @@ "data-updated":"Data type updated", "registered-user":"User registered", "revoke-authorization":"Authorization revoked", - "create-authorization":"Authorization created" + "create-authorization":"Authorization created", + "dataTypeFiltreEmpty" : "No data matching your criteria" }, "message":{ "app-config-error":"Error in yaml file", @@ -166,7 +167,8 @@ "filtered": "Filters used", "sorted": "The sorts used", "title-modal-numeric": "Choice of value range", - "title-modal-date": "Choice of date range" + "title-modal-date": "Choice of date range", + "manage-datasets": "Manage datasets" }, "dataTypesRepository": { "card-title-upload-file": "Drop a version on this dataset", diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index b99b4e6d0..958a076e2 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -58,7 +58,9 @@ "references": "Référentiels", "french": "Français", "english": "English", - "language": "Langue" + "language": "Langue", + "sub-menu": "chemin d'acces", + "nav-bar": "Menu principal" }, "applications": { "chose-config": "Choisir une configuration", @@ -167,7 +169,8 @@ "filtered": "Les filtres utilisés", "sorted": "Les tris utilisés", "title-modal-numeric": "Choix de l'interval de valeur", - "title-modal-date": "Choix de l'interval de date" + "title-modal-date": "Choix de l'interval de date", + "manage-datasets": "Gérer les jeux de données" }, "dataTypesRepository": { "card-title-upload-file": "Déposer une version sur ce jeux de données", diff --git a/ui2/src/main.js b/ui2/src/main.js index d23c46226..f13477fe3 100644 --- a/ui2/src/main.js +++ b/ui2/src/main.js @@ -51,6 +51,7 @@ import { faSortAmountDown, faSortUp, faSortDown, + faArchive, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; library.add( @@ -100,7 +101,8 @@ library.add( faStream, faSortAmountDown, faSortDown, - faSortUp + faSortUp, + faArchive ); Vue.component("vue-fontawesome", FontAwesomeIcon); diff --git a/ui2/src/style/_common.scss b/ui2/src/style/_common.scss index a7fe321e8..794b7e21b 100644 --- a/ui2/src/style/_common.scss +++ b/ui2/src/style/_common.scss @@ -30,8 +30,8 @@ a { .link { cursor: pointer; &:hover { - color: $primary; - text-decoration: underline; + color: $dark; + font-weight: bold; } } @@ -72,7 +72,12 @@ a { // Buefy/Bulma UI overrides .tabs.is-boxed li.is-active a { - color: $primary; + color: $dark; + text-decoration: none; + font-weight: bold; +} +.notification a:not(.button):not(.dropdown-item){ + text-decoration: none; } .tabs.is-boxed.is-right { diff --git a/ui2/src/style/_variables.scss b/ui2/src/style/_variables.scss index 6583e1ac1..6bd32aaa1 100644 --- a/ui2/src/style/_variables.scss +++ b/ui2/src/style/_variables.scss @@ -23,12 +23,11 @@ $menu-height: 80px; ***************************************************************************************************/ // General variables -$primary: rgb(0,166,163); -$info: rgb(78, 198, 194); +$primary: rgb(0,166,166); +$info: rgb(20, 164, 180); $dark: rgb(0, 100, 100); $success: rgb(186, 222, 129); $warning: rgb(255, 170, 0); $danger: rgb(166, 0, 0); $light: rgb(202, 216, 216); $family-primary: $font-family; -$primary-dark: #007F7F; diff --git a/ui2/src/views/application/ApplicationsView.vue b/ui2/src/views/application/ApplicationsView.vue index d23730ed2..935847cac 100644 --- a/ui2/src/views/application/ApplicationsView.vue +++ b/ui2/src/views/application/ApplicationsView.vue @@ -11,6 +11,7 @@ role="button" style="margin-bottom: 50px" @click="createApplication" + tabindex="0" > <a class="card-header-icon createApplication"> <b-icon icon="plus"></b-icon> diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue index 3b6855165..7688ddea8 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue @@ -1,6 +1,11 @@ <template> <PageView class="with-submenu"> - <SubMenu :paths="subMenuPaths" :root="application.localName || application.title" /> + <SubMenu + :paths="subMenuPaths" + :root="application.localName || application.title" + role="navigation" + :aria-label="$t('menu.sub-menu')" + /> <h1 class="title main-title"> <span v-if="authorizationId === 'new'">{{ @@ -59,7 +64,7 @@ <div class="buttons"> <b-button icon-left="plus" - type="is-primary" + type="is-dark" @click="handleSubmit(createAuthorization)" style="margin-bottom: 10px" > @@ -448,7 +453,7 @@ export default class DataTypeAuthorizationInfoView extends Vue { .folder label { font-weight: bolder; - color: #007f7f; + color: $dark; } .rows .card-content .row.label .columns .column { padding: 0 0 0 10px; diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue index a0777334f..17a2fe543 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue @@ -1,6 +1,11 @@ <template> <PageView class="with-submenu"> - <SubMenu :paths="subMenuPaths" :root="application.localName || application.title" /> + <SubMenu + :paths="subMenuPaths" + :root="application.localName || application.title" + role="navigation" + :aria-label="$t('menu.sub-menu')" + /> <h1 class="title main-title"> {{ $t("titles.data-type-authorizations", { @@ -20,7 +25,7 @@ </b-select> </div> <div class="column is-2"> - <b-button icon-left="plus" type="is-primary is-right" @click="addAuthorization"> + <b-button icon-left="plus" type="is-dark is-right" @click="addAuthorization"> {{ $t("dataTypeAuthorizations.add-auhtorization") }} </b-button> </div> diff --git a/ui2/src/views/common/MenuView.vue b/ui2/src/views/common/MenuView.vue index 352344ff1..2f959178f 100644 --- a/ui2/src/views/common/MenuView.vue +++ b/ui2/src/views/common/MenuView.vue @@ -1,10 +1,18 @@ <template> - <div class="menu-view-container"> - <b-navbar class="menu-view" v-if="open"> + <div class="menu-view-container" role="navigation"> + <b-navbar class="menu-view" v-if="open" role="menubar" :aria-label="$t('menu.nav-bar')"> <template #start> <b-navbar-item href="https://www.inrae.fr/"> - <img class="logo_blanc" src="@/assets/logo-inrae_blanc.svg" /> - <img class="logo_vert" src="@/assets/Logo-INRAE.svg" /> + <img + class="logo_blanc" + src="@/assets/logo-inrae_blanc.svg" + alt="Accès page de l’institut national de recherche pour l’agriculture, l’alimentation et l’environnement" + /> + <img + class="logo_vert" + src="@/assets/Logo-INRAE.svg" + alt="Accès page de l’institut national de recherche pour l’agriculture, l’alimentation et l’environnement" + /> </b-navbar-item> <b-navbar-item tag="router-link" :to="{ path: '/applications' }"> {{ $t("menu.applications") }} @@ -12,7 +20,11 @@ </template> <template #end> - <img class="logo_rep" src="@/assets/Rep-FR-logo.svg" /> + <img + class="logo_rep" + src="@/assets/Rep-FR-logo.svg" + alt="Logo de la République Francçaise" + /> <b-navbar-item tag="div"> <b-field> <b-select @@ -37,7 +49,12 @@ </a> </template> - <b-dropdown-item @click="logout()" aria-role="menuitem"> + <b-dropdown-item + @click="logout()" + @keypress.enter="logout()" + tabindex="0" + aria-role="menuitem" + > <b-icon icon="sign-out-alt" /> {{ $t("menu.logout") }} </b-dropdown-item> diff --git a/ui2/src/views/common/PageView.vue b/ui2/src/views/common/PageView.vue index cb1a54ee6..2948a766f 100644 --- a/ui2/src/views/common/PageView.vue +++ b/ui2/src/views/common/PageView.vue @@ -1,5 +1,5 @@ <template> - <div class="PageView"> + <div class="PageView" role="main"> <MenuView v-if="hasMenu" /> <div :class="`PageView-container ${hasMenu ? '' : 'noMenu'}`"> <slot></slot> diff --git a/ui2/src/views/datatype/DataTypeTableView.vue b/ui2/src/views/datatype/DataTypeTableView.vue index a0b5c4dcf..c5a685f5f 100644 --- a/ui2/src/views/datatype/DataTypeTableView.vue +++ b/ui2/src/views/datatype/DataTypeTableView.vue @@ -1,7 +1,12 @@ /* eslint-disable @intlify/vue-i18n/no-raw-text */ <template> <PageView class="with-submenu"> - <SubMenu :paths="subMenuPaths" :root="application.localName || application.title" /> + <SubMenu + :paths="subMenuPaths" + :root="application.localName || application.title" + role="navigation" + :aria-label="$t('menu.sub-menu')" + /> <h1 class="title main-title">{{ application.localDatatypeName || dataTypeId }}</h1> <div class="columns" v-if="!showSort && !showFilter"> @@ -20,7 +25,7 @@ :key="index" > <b-tag - type="is-primary" + type="is-dark" size="is-medium" rounded style="margin-left: 10px; margin-right: 10px; margin-bottom: 10px" @@ -70,7 +75,7 @@ <b-button icon-left="sort-amount-down" :label="$t('applications.trier')" - type="is-primary" + type="is-dark" @click="showSort = !showSort" outlined ></b-button> @@ -107,7 +112,12 @@ </div> </div> </b-modal> - <div v-if="showSort" class="notification" style="background-color: rgba(0, 163, 166, 0.1)"> + <div + v-if="showSort" + class="notification" + role="search" + style="background-color: rgba(0, 163, 166, 0.1)" + > <h2>{{ $t("applications.trier") }}</h2> <div class="content"> <div class="columns is-multiline"> @@ -183,10 +193,10 @@ " > <div class="tags has-addons"> - <span class="tag is-primary grape" style="font-size: 1rem"> + <span class="tag is-dark grape" style="font-size: 1rem"> <b-icon icon="stream" style="transform: rotate(180deg)"></b-icon> </span> - <span class="tag is-primary orderLabel" style="font-size: 1rem"> + <span class="tag is-dark orderLabel" style="font-size: 1rem"> {{ variableComponent.variableComponentKey.variable }} {{ $t("ponctuation.colon") }} {{ variableComponent.variableComponentKey.component }} @@ -194,7 +204,7 @@ {{ variableComponent.order }} </span> <a - class="tag is-delete is-primary" + class="tag is-delete is-dark" style="font-size: 1rem; color: white" @click=" deleteTag( @@ -227,7 +237,7 @@ </div> </div> </div> - <div v-if="showFilter" class="notification"> + <div v-if="showFilter" class="notification" role="search"> <h2>{{ $t("applications.filter") }}</h2> <div class="columns is-multiline"> <div @@ -400,8 +410,10 @@ </div> <b-pagination v-model="currentPage" + role="navigation" :per-page="params.limit" :total="totalRows" + aria-label="pagination" aria-current-label="Current page" aria-next-label="Next page" aria-page-label="Page" @@ -863,7 +875,7 @@ $row-variable-height: 60px; .ASC .asc, .DESC .desc { - background-color: $primary; + background-color: $dark; color: white; } @@ -882,7 +894,6 @@ $row-variable-height: 60px; border: transparent; text-decoration: underline; } - .columns { margin: 0; } diff --git a/ui2/src/views/datatype/DataTypesManagementView.vue b/ui2/src/views/datatype/DataTypesManagementView.vue index f4e94593e..6b9de9683 100644 --- a/ui2/src/views/datatype/DataTypesManagementView.vue +++ b/ui2/src/views/datatype/DataTypesManagementView.vue @@ -1,6 +1,11 @@ <template> <PageView class="with-submenu"> - <SubMenu :root="application.localName || application.title" :paths="subMenuPaths" /> + <SubMenu + :root="application.localName || application.title" + :paths="subMenuPaths" + role="navigation" + :aria-label="$t('menu.sub-menu')" + /> <h1 class="title main-title"> {{ $t("titles.data-types-page", { @@ -78,7 +83,7 @@ export default class DataTypesManagementView extends Vue { this.$t("referencesManagement.consult"), "eye", (label) => this.consultDataType(label), - "is-primary" + "is-dark" ), new Button(this.$t("referencesManagement.download"), "download", (label) => this.downloadDataType(label) diff --git a/ui2/src/views/datatype/DataTypesRepositoryView.vue b/ui2/src/views/datatype/DataTypesRepositoryView.vue index 3b240a87c..86f0fcc10 100644 --- a/ui2/src/views/datatype/DataTypesRepositoryView.vue +++ b/ui2/src/views/datatype/DataTypesRepositoryView.vue @@ -1,7 +1,12 @@ <template> <div> <PageView class="with-submenu"> - <SubMenu :paths="subMenuPaths" :root="application.localName || application.title" /> + <SubMenu + :paths="subMenuPaths" + :root="application.localName || application.title" + role="navigation" + :aria-label="$t('menu.sub-menu')" + /> <h1 class="title main-title"> {{ $t("titles.data-types-repository", { diff --git a/ui2/src/views/references/ReferenceTableView.vue b/ui2/src/views/references/ReferenceTableView.vue index 81d387685..1056573a2 100644 --- a/ui2/src/views/references/ReferenceTableView.vue +++ b/ui2/src/views/references/ReferenceTableView.vue @@ -1,6 +1,11 @@ <template> <PageView class="with-submenu"> - <SubMenu :root="application.localName" :paths="subMenuPaths" /> + <SubMenu + :root="application.localName" + :paths="subMenuPaths" + role="navigation" + :aria-label="$t('menu.sub-menu')" + /> <h1 class="title main-title"> {{ $t("titles.references-data", { refName: application.localRefName }) }} </h1> diff --git a/ui2/src/views/references/ReferencesManagementView.vue b/ui2/src/views/references/ReferencesManagementView.vue index 13927bb25..554903b45 100644 --- a/ui2/src/views/references/ReferencesManagementView.vue +++ b/ui2/src/views/references/ReferencesManagementView.vue @@ -1,6 +1,11 @@ <template> <PageView class="with-submenu"> - <SubMenu :root="application.localName" :paths="subMenuPaths" /> + <SubMenu + :root="application.localName" + :paths="subMenuPaths" + role="navigation" + :aria-label="$t('menu.sub-menu')" + /> <h1 class="title main-title"> {{ $t("titles.references-page", { applicationName: application.localName }) }} </h1> @@ -21,7 +26,7 @@ :reference="chosenRef" :closeCb="(newVal) => (openPanel = newVal)" /> -<!-- <b-pagination + <!-- <b-pagination v-model="currentPage" :per-page="params.limit" :total="references.length" @@ -77,7 +82,7 @@ export default class ReferencesManagementView extends Vue { this.$t("referencesManagement.consult"), "eye", (label) => this.consultReference(label), - "is-primary" + "is-dark" ), new Button(this.$t("referencesManagement.download"), "download", (label) => this.downloadReference(label) -- GitLab From 855f0f81b63c7f1432897279ae805969b7afb8ed Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Mon, 22 Nov 2021 17:12:33 +0100 Subject: [PATCH 08/24] ajout aria sur balise html, ajout champs commentaire --- .../inra/oresing/model/BinaryFileDataset.java | 1 + .../oresing/persistence/BinaryFileInfos.java | 1 + ui2/src/components/common/SubMenu.vue | 4 ++ ui2/src/components/login/Register.vue | 7 +++- ui2/src/components/login/Signin.vue | 14 ++++++- ui2/src/locales/en.json | 12 +++++- ui2/src/locales/fr.json | 12 ++++-- ui2/src/model/file/BinaryFileDataset.js | 4 +- ui2/src/model/file/BinaryFileInfos.js | 1 + ui2/src/style/_common.scss | 37 +++++++++++++---- ui2/src/style/_variables.scss | 4 +- ui2/src/views/LoginView.vue | 9 +++- .../views/application/ApplicationsView.vue | 9 ++-- .../DataTypeAuthorizationInfoView.vue | 2 +- .../DataTypeAuthorizationsView.vue | 12 +++--- ui2/src/views/common/MenuView.vue | 2 +- ui2/src/views/datatype/DataTypeTableView.vue | 14 +++---- .../datatype/DataTypesManagementView.vue | 2 +- .../datatype/DataTypesRepositoryView.vue | 41 ++++++++++++++++--- .../views/references/ReferenceTableView.vue | 12 +++--- .../references/ReferencesManagementView.vue | 11 ++--- 21 files changed, 153 insertions(+), 58 deletions(-) diff --git a/src/main/java/fr/inra/oresing/model/BinaryFileDataset.java b/src/main/java/fr/inra/oresing/model/BinaryFileDataset.java index 2d6d65d91..d4fa4e8e0 100644 --- a/src/main/java/fr/inra/oresing/model/BinaryFileDataset.java +++ b/src/main/java/fr/inra/oresing/model/BinaryFileDataset.java @@ -20,6 +20,7 @@ public class BinaryFileDataset { private Map<String, String> requiredauthorizations = new HashMap<>(); private String from; private String to; + private String comment; @Override public String toString() { diff --git a/src/main/java/fr/inra/oresing/persistence/BinaryFileInfos.java b/src/main/java/fr/inra/oresing/persistence/BinaryFileInfos.java index 7050b0a22..fa67d5df8 100644 --- a/src/main/java/fr/inra/oresing/persistence/BinaryFileInfos.java +++ b/src/main/java/fr/inra/oresing/persistence/BinaryFileInfos.java @@ -15,4 +15,5 @@ public class BinaryFileInfos { public String publisheddate; public UUID createuser; public String createdate; + public String comment; } \ No newline at end of file diff --git a/ui2/src/components/common/SubMenu.vue b/ui2/src/components/common/SubMenu.vue index 1f2311a70..f4a7bcc38 100644 --- a/ui2/src/components/common/SubMenu.vue +++ b/ui2/src/components/common/SubMenu.vue @@ -3,6 +3,8 @@ <FontAwesomeIcon icon="arrow-left" @click="goBack()" + @keypress.enter="goBack()" + tabindex="0" class="clickable mr-4 SubMenu-back-button" /> <span class="SubMenu-root">{{ root }}</span> @@ -11,6 +13,8 @@ <span @click="index !== paths.length - 1 ? path.clickCb() : ''" :class="index !== paths.length - 1 ? 'link' : ''" + @keypress.enter="index !== paths.length - 1 ? path.clickCb() : ''" + tabindex="0" >{{ path.label }}</span > </div> diff --git a/ui2/src/components/login/Register.vue b/ui2/src/components/login/Register.vue index a5cbb0495..d2f0a944a 100644 --- a/ui2/src/components/login/Register.vue +++ b/ui2/src/components/login/Register.vue @@ -83,7 +83,12 @@ </section> <div class="buttons"> - <b-button type="is-primary" @click="handleSubmit(register)" icon-left="user-plus"> + <b-button + type="is-primary" + @click="handleSubmit(register)" + icon-left="user-plus" + :aria-label="$t('login.aria-btn-signup')" + > {{ $t("login.register") }} </b-button> </div> diff --git a/ui2/src/components/login/Signin.vue b/ui2/src/components/login/Signin.vue index 93f284d20..1541b355c 100644 --- a/ui2/src/components/login/Signin.vue +++ b/ui2/src/components/login/Signin.vue @@ -16,7 +16,12 @@ {{ $t("validation.obligatoire") }} </span> </template> - <b-input v-model="login" :placeholder="$t('login.login-placeholder')"> </b-input> + <b-input + v-model="login" + title="Champs nom d'utilisateur" + :placeholder="$t('login.login-placeholder')" + > + </b-input> </b-field> </ValidationProvider> @@ -53,7 +58,12 @@ </section> <div class="buttons"> - <b-button type="is-primary" @click="handleSubmit(signIn)" icon-left="sign-in-alt"> + <b-button + type="is-primary" + @click="handleSubmit(signIn)" + icon-left="sign-in-alt" + :aria-label="$t('login.aria-btn-login')" + > {{ $t("login.signin") }} </b-button> <router-link :to="{ path: '/' }"> diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json index 0568a65fa..d88144c1f 100644 --- a/ui2/src/locales/en.json +++ b/ui2/src/locales/en.json @@ -19,7 +19,9 @@ "pwd-placeholder":"Ex: xxxx", "pwd-forgotten":"Forgotten password ? ", "register":"Register", - "confirm-pwd":"Confirmer le mot de passe" + "confirm-pwd":"Confirmer le mot de passe", + "aria-btn-login": "Validate login form button", + "aria-btn-signup": "Create an account button" }, "validation":{ "obligatoire":"Mandatory", @@ -58,7 +60,13 @@ "references":"References", "french":"Français", "english":"English", - "language":"Language" + "language":"Language", + "aria-sub-menu": "Access path", + "aria-nav-bar": "Main menu", + "aria-pagination": "Pagination", + "aria-curent-page": "Curent page", + "aria-next-page": "Next page", + "aria-previous-page": "Previous page" }, "applications":{ "chose-config":"Chose a configuration", diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index 958a076e2..360305566 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -19,7 +19,9 @@ "pwd-placeholder": "Ex: xxxx", "pwd-forgotten": "Mot de passe oublié ?", "register": "Créer un compte", - "confirm-pwd": "Confirmer le mot de passe" + "confirm-pwd": "Confirmer le mot de passe", + "aria-btn-login": "Bouton valider le formulaire de connexion", + "aria-btn-signup": "Bouton création d'un compte" }, "validation": { "obligatoire": "Obligatoire", @@ -59,8 +61,12 @@ "french": "Français", "english": "English", "language": "Langue", - "sub-menu": "chemin d'acces", - "nav-bar": "Menu principal" + "aria-sub-menu": "Chemin d'acces", + "aria-nav-bar": "Menu principal", + "aria-pagination": "Pagination", + "aria-curent-page": "Page actuelle", + "aria-next-page": "Page suivante", + "aria-previous-page": "Page précédente" }, "applications": { "chose-config": "Choisir une configuration", diff --git a/ui2/src/model/file/BinaryFileDataset.js b/ui2/src/model/file/BinaryFileDataset.js index 1a305b6ae..aaa9deec3 100644 --- a/ui2/src/model/file/BinaryFileDataset.js +++ b/ui2/src/model/file/BinaryFileDataset.js @@ -3,7 +3,8 @@ export class BinaryFileDataset { requiredauthorizations = {}; from; to; - constructor(datatypeOrBinaryDataset, requiredauthorizations, from, to) { + comment; + constructor(datatypeOrBinaryDataset, requiredauthorizations, from, to, comment) { if (typeof datatypeOrBinaryDataset == "object") { Object.keys(this).forEach( (key) => (this[key] = datatypeOrBinaryDataset[key] ? datatypeOrBinaryDataset[key] : null) @@ -13,6 +14,7 @@ export class BinaryFileDataset { this.requiredauthorizations = requiredauthorizations == null ? {} : requiredauthorizations; this.from = from; this.to = to; + this.comment = comment; } } } diff --git a/ui2/src/model/file/BinaryFileInfos.js b/ui2/src/model/file/BinaryFileInfos.js index 9080dc461..247cf0670 100644 --- a/ui2/src/model/file/BinaryFileInfos.js +++ b/ui2/src/model/file/BinaryFileInfos.js @@ -6,6 +6,7 @@ export class BinaryFileInfos { publisheddate; createuser; createdate; + comment; constructor(binaryFileInfos) { if (typeof binaryFileInfos == "object") { Object.keys(this).forEach( diff --git a/ui2/src/style/_common.scss b/ui2/src/style/_common.scss index 794b7e21b..19de09fbc 100644 --- a/ui2/src/style/_common.scss +++ b/ui2/src/style/_common.scss @@ -70,15 +70,14 @@ a { } // Buefy/Bulma UI overrides - -.tabs.is-boxed li.is-active a { - color: $dark; - text-decoration: none; - font-weight: bold; -} .notification a:not(.button):not(.dropdown-item){ text-decoration: none; } +.b-tabs .tabs.is-boxed li:not(.is-active) a:focus { + background-color: $primary; + color: white; + font-weight: bold; +} .tabs.is-boxed.is-right { ul { @@ -91,7 +90,28 @@ a { .b-tabs .tab-content { padding: 0.5rem; } - +.tabs.is-boxed{ + li.is-active { + a { + color: $dark; + text-decoration: none; + font-weight: bold; + } + a:focus { + background-color: $primary; + color: white; + font-weight: bold; + } + } + a:hover{ + background-color: $primary; + color: white; + font-weight: bold; + } +} +.dropdown-content{ + box-shadow: 0 0.5em 1em -0.125em rgba(10, 10, 10, 0.5), 0 0px 0 1px rgba(10, 10, 10, 0.1); +} .pagination-link.is-current { background-color: $dark; border-color: $dark; @@ -105,6 +125,9 @@ a { background-color: $dark; border-color: $dark; } +.pagination { + padding-bottom: 20px; +} a.dropdown-item { display: flex; diff --git a/ui2/src/style/_variables.scss b/ui2/src/style/_variables.scss index 6bd32aaa1..abcdfddc3 100644 --- a/ui2/src/style/_variables.scss +++ b/ui2/src/style/_variables.scss @@ -23,8 +23,8 @@ $menu-height: 80px; ***************************************************************************************************/ // General variables -$primary: rgb(0,166,166); -$info: rgb(20, 164, 180); +$primary: rgb(0,157,157); +$info: rgb(20, 155, 170); $dark: rgb(0, 100, 100); $success: rgb(186, 222, 129); $warning: rgb(255, 170, 0); diff --git a/ui2/src/views/LoginView.vue b/ui2/src/views/LoginView.vue index d1cadcf51..1c062b027 100644 --- a/ui2/src/views/LoginView.vue +++ b/ui2/src/views/LoginView.vue @@ -3,10 +3,15 @@ <h1 class="title main-title">{{ $t("titles.login-page") }}</h1> <div class="card LoginView-card"> <b-tabs v-model="selectedTab" type="is-boxed" :animated="false"> - <b-tab-item :label="$t('login.signin')" icon="sign-in-alt"> + <b-tab-item :label="$t('login.signin')" icon="sign-in-alt" tabindex="0"> <SignIn /> </b-tab-item> - <b-tab-item :label="$t('login.register')" icon="user-plus"> + <b-tab-item + :label="$t('login.register')" + icon="user-plus" + @keypress.enter="changeTabToSignIn" + tabindex="0" + > <Register @userRegistered="changeTabToSignIn" /> </b-tab-item> </b-tabs> diff --git a/ui2/src/views/application/ApplicationsView.vue b/ui2/src/views/application/ApplicationsView.vue index 935847cac..07cc2353d 100644 --- a/ui2/src/views/application/ApplicationsView.vue +++ b/ui2/src/views/application/ApplicationsView.vue @@ -183,10 +183,11 @@ :range-after="2" :range-before="2" :rounded="true" - aria-current-label="Current page" - aria-next-label="Next page" - aria-page-label="Page" - aria-previous-label="Previous page" + role="navigation" + :aria-label="$t('menu.aria-pagination')" + :aria-current-label="$t('menu.aria-curent-page')" + :aria-next-label="$t('menu.aria-next-page')" + :aria-previous-label="$t('menu.aria-previous-page')" order="is-centered" :total="applications.length" > diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue index 7688ddea8..3058cd4dd 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue @@ -4,7 +4,7 @@ :paths="subMenuPaths" :root="application.localName || application.title" role="navigation" - :aria-label="$t('menu.sub-menu')" + :aria-label="$t('menu.aria-sub-menu')" /> <h1 class="title main-title"> diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue index 17a2fe543..707d644c7 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue @@ -4,7 +4,7 @@ :paths="subMenuPaths" :root="application.localName || application.title" role="navigation" - :aria-label="$t('menu.sub-menu')" + :aria-label="$t('menu.aria-sub-menu')" /> <h1 class="title main-title"> {{ @@ -97,15 +97,15 @@ v-model="currentPage" :per-page="perPage" :total="selectedUser.length" - aria-current-label="Current page" - aria-next-label="Next page" - aria-page-label="Page" - aria-previous-label="Previous page" + role="navigation" + :aria-label="$t('menu.aria-pagination')" + :aria-current-label="$t('menu.aria-curent-page')" + :aria-next-label="$t('menu.aria-next-page')" + :aria-previous-label="$t('menu.aria-previous-page')" order="is-centered" range-after="3" range-before="3" :rounded="true" - style="padding-bottom: 20px" > </b-pagination> </div> diff --git a/ui2/src/views/common/MenuView.vue b/ui2/src/views/common/MenuView.vue index 2f959178f..a80d1a3d2 100644 --- a/ui2/src/views/common/MenuView.vue +++ b/ui2/src/views/common/MenuView.vue @@ -1,6 +1,6 @@ <template> <div class="menu-view-container" role="navigation"> - <b-navbar class="menu-view" v-if="open" role="menubar" :aria-label="$t('menu.nav-bar')"> + <b-navbar class="menu-view" v-if="open" role="menubar" :aria-label="$t('menu.aria-nav-bar')"> <template #start> <b-navbar-item href="https://www.inrae.fr/"> <img diff --git a/ui2/src/views/datatype/DataTypeTableView.vue b/ui2/src/views/datatype/DataTypeTableView.vue index c5a685f5f..ec37b2e7d 100644 --- a/ui2/src/views/datatype/DataTypeTableView.vue +++ b/ui2/src/views/datatype/DataTypeTableView.vue @@ -5,7 +5,7 @@ :paths="subMenuPaths" :root="application.localName || application.title" role="navigation" - :aria-label="$t('menu.sub-menu')" + :aria-label="$t('menu.aria-sub-menu')" /> <h1 class="title main-title">{{ application.localDatatypeName || dataTypeId }}</h1> @@ -410,20 +410,18 @@ </div> <b-pagination v-model="currentPage" - role="navigation" :per-page="params.limit" :total="totalRows" - aria-label="pagination" - aria-current-label="Current page" - aria-next-label="Next page" - aria-page-label="Page" - aria-previous-label="Previous page" + role="navigation" + :aria-label="$t('menu.aria-pagination')" + :aria-current-label="$t('menu.aria-curent-page')" + :aria-next-label="$t('menu.aria-next-page')" + :aria-previous-label="$t('menu.aria-previous-page')" order="is-centered" range-after="3" range-before="3" :rounded="true" @change="changePage" - style="padding-bottom: 20px" > </b-pagination> </div> diff --git a/ui2/src/views/datatype/DataTypesManagementView.vue b/ui2/src/views/datatype/DataTypesManagementView.vue index 6b9de9683..e20638b75 100644 --- a/ui2/src/views/datatype/DataTypesManagementView.vue +++ b/ui2/src/views/datatype/DataTypesManagementView.vue @@ -4,7 +4,7 @@ :root="application.localName || application.title" :paths="subMenuPaths" role="navigation" - :aria-label="$t('menu.sub-menu')" + :aria-label="$t('menu.aria-sub-menu')" /> <h1 class="title main-title"> {{ diff --git a/ui2/src/views/datatype/DataTypesRepositoryView.vue b/ui2/src/views/datatype/DataTypesRepositoryView.vue index 86f0fcc10..1a05eea79 100644 --- a/ui2/src/views/datatype/DataTypesRepositoryView.vue +++ b/ui2/src/views/datatype/DataTypesRepositoryView.vue @@ -5,7 +5,7 @@ :paths="subMenuPaths" :root="application.localName || application.title" role="navigation" - :aria-label="$t('menu.sub-menu')" + :aria-label="$t('menu.aria-sub-menu')" /> <h1 class="title main-title"> {{ @@ -130,7 +130,9 @@ </div> </div> <div class="columns"> - <!-- TO DO ajouter un champs commentaire --> + <b-field class="column" label="Commentaire" expanded> + <b-input v-model="comment" maxlength="200" type="textarea"></b-input> + </b-field> </div> </div> </div> @@ -151,7 +153,7 @@ <div class="card-content"> <table v-if="datasets && Object.keys(datasets).length" - class="table is-striped is-fullwidth" + class="table is-striped is-fullwidth numberData" style="text-align: center; vertical-align: center" > <caption> @@ -168,6 +170,8 @@ v-for="(dataset, periode) in datasets" :key="dataset.id" @click="showDatasets(dataset)" + @keypress.enter="showDatasets(dataset)" + tabindex="0" style="cursor: pointer" > <td align>{{ periode }}</td> @@ -199,7 +203,14 @@ <th align>{{ $t("dataTypesRepository.table-file-data-delete") }}</th> </tr> <tr v-for="dataset in currentDataset" :key="dataset.id"> - <td align>{{ dataset.id.slice(0, 8) }}</td> + <td align> + <b-tooltip type="is-dark" multilined> + <a>{{ dataset.id.slice(0, 8) }}</a> + <template v-slot:content> + <p>{{ UTCToString(dataset.params.binaryFiledataset.comment) }}</p> + </template> + </b-tooltip> + </td> <td align>{{ dataset.size }}</td> <td align>{{ UTCToString(dataset.params.createdate) }}</td> <td align>{{ dataset.createuser }}</td> @@ -284,6 +295,7 @@ export default class DataTypesRepositoryView extends Vue { file = null; startDate = null; endDate = null; + comment = ""; currentDataset = null; mounted() { @@ -333,6 +345,7 @@ export default class DataTypesRepositoryView extends Vue { }, {}), from: "", to: "", + comment: "", }); this.requiredauthorizationsObject = Object.keys(this.authorizations).reduce((acc, auth) => { acc[auth] = null; @@ -433,7 +446,8 @@ export default class DataTypesRepositoryView extends Vue { /(.{10})T(.{8}).*/ .exec(new Date(this.endDate).toISOString()) .filter((a, i) => i != 0) - .join(" ") + .join(" "), + this.comment ), false ); @@ -443,6 +457,7 @@ export default class DataTypesRepositoryView extends Vue { this.file, fileOrId ); + console.log(fileOrId); this.$emit("uploaded", uuid); } } @@ -621,7 +636,10 @@ export default class DataTypesRepositoryView extends Vue { overflow-wrap: break-word; } } - +.dropdown-content{ + margin-left: 20px; + margin-right: -20px; +} table.datasetsPanel { width: 50%; } @@ -632,4 +650,15 @@ table.datasetsPanel td { border-collapse: collapse; text-align: center; } +.numberData tr:hover td { + background-color: $primary; + color: white; +} + +caption { + color: $dark; + font-weight: bold; + font-size: 20px; + margin-bottom: 15px; +} </style> diff --git a/ui2/src/views/references/ReferenceTableView.vue b/ui2/src/views/references/ReferenceTableView.vue index 1056573a2..673c46dc7 100644 --- a/ui2/src/views/references/ReferenceTableView.vue +++ b/ui2/src/views/references/ReferenceTableView.vue @@ -4,7 +4,7 @@ :root="application.localName" :paths="subMenuPaths" role="navigation" - :aria-label="$t('menu.sub-menu')" + :aria-label="$t('menu.aria-sub-menu')" /> <h1 class="title main-title"> {{ $t("titles.references-data", { refName: application.localRefName }) }} @@ -49,15 +49,15 @@ v-model="currentPage" :per-page="perPage" :total="tableValues.length" - aria-current-label="Current page" - aria-next-label="Next page" - aria-page-label="Page" - aria-previous-label="Previous page" + role="navigation" + :aria-label="$t('menu.aria-pagination')" + :aria-current-label="$t('menu.aria-curent-page')" + :aria-next-label="$t('menu.aria-next-page')" + :aria-previous-label="$t('menu.aria-previous-page')" order="is-centered" range-after="3" range-before="3" :rounded="true" - style="padding-bottom: 20px" > </b-pagination> </div> diff --git a/ui2/src/views/references/ReferencesManagementView.vue b/ui2/src/views/references/ReferencesManagementView.vue index 554903b45..356bf3a6d 100644 --- a/ui2/src/views/references/ReferencesManagementView.vue +++ b/ui2/src/views/references/ReferencesManagementView.vue @@ -4,7 +4,7 @@ :root="application.localName" :paths="subMenuPaths" role="navigation" - :aria-label="$t('menu.sub-menu')" + :aria-label="$t('menu.aria-sub-menu')" /> <h1 class="title main-title"> {{ $t("titles.references-page", { applicationName: application.localName }) }} @@ -30,10 +30,11 @@ v-model="currentPage" :per-page="params.limit" :total="references.length" - aria-current-label="Current page" - aria-next-label="Next page" - aria-page-label="Page" - aria-previous-label="Previous page" + role="navigation" + :aria-label="$t('menu.aria-pagination')" + :aria-current-label="$t('menu.aria-curent-page')" + :aria-next-label="$t('menu.aria-next-page')" + :aria-previous-label="$t('menu.aria-previous-page')" order="is-centered" range-after="3" range-before="3" -- GitLab From bec4d83c2620eb21d0a68afa63c38709d602392f Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Tue, 23 Nov 2021 15:45:45 +0100 Subject: [PATCH 09/24] =?UTF-8?q?affichage=20comment=20repository=20avec?= =?UTF-8?q?=20accessibilit=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/common/AuthorizationTable.vue | 19 ++++++------ ui2/src/locales/en.json | 1 + ui2/src/locales/fr.json | 1 + ui2/src/style/_common.scss | 4 +++ .../datatype/DataTypesRepositoryView.vue | 31 +++++++++++++++---- 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/ui2/src/components/common/AuthorizationTable.vue b/ui2/src/components/common/AuthorizationTable.vue index 4405c1aba..e738a8a1e 100644 --- a/ui2/src/components/common/AuthorizationTable.vue +++ b/ui2/src/components/common/AuthorizationTable.vue @@ -460,7 +460,6 @@ export default class AuthorizationTable extends Vue { authorizationScope[id] = scope.key; state = 1; } - } else if (event instanceof Array) { state = event.length ? 1 : 0; eventType = event.length ? "add-authorization" : "delete-authorization"; @@ -473,16 +472,18 @@ export default class AuthorizationTable extends Vue { localAuthorizationsTree[indexColumn][index][fromOrTo] = event; } if (this.EXTRACTION == indexColumn) { - if (event instanceof Array) { //c'est un datagroup - state = event.length ? 1 : 0 - eventType = event.length ? 'add-authorization' : 'delete-authorization' - localAuthorizationsTree[indexColumn][index].dataGroups = event + if (event instanceof Array) { + //c'est un datagroup + state = event.length ? 1 : 0; + eventType = event.length ? "add-authorization" : "delete-authorization"; + localAuthorizationsTree[indexColumn][index].dataGroups = event; // si indeterminate alors je ne supprime les enfants que - } else if (event instanceof Date) {//c'est une date - state = event ? 1 : 0 - eventType = event ? 'add-authorization' : 'delete-authorization' - localAuthorizationsTree[indexColumn][index][fromOrTo] = event + } else if (event instanceof Date) { + //c'est une date + state = event ? 1 : 0; + eventType = event ? "add-authorization" : "delete-authorization"; + localAuthorizationsTree[indexColumn][index][fromOrTo] = event; } //si je veux restreindre les enfants je dois le faire après avoir défini le parent this.changeChildrenAuthorization(localAuthorizationsTree?.[indexColumn]?.[index]); //si je selectionne alors c'est cette authorization qui s'applique aux enfants (ils n'ont plus leur propre authorization diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json index d88144c1f..264faf529 100644 --- a/ui2/src/locales/en.json +++ b/ui2/src/locales/en.json @@ -182,6 +182,7 @@ "card-title-upload-file": "Drop a version on this dataset", "start-date": "Start date", "end-date": "End date", + "comment": "Comment", "choose-file": "Choose a file", "placeholder-datepicker": "Type or select a date...", "placeholder-select": "Select...", diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index 360305566..6a7f9e444 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -182,6 +182,7 @@ "card-title-upload-file": "Déposer une version sur ce jeux de données", "start-date": "Date de début", "end-date": "Date de fin", + "comment": "Commentaire", "choose-file": "Choisir un fichier", "placeholder-datepicker": "Taper ou sélectionner une date...", "placeholder-select": "Sélectionner...", diff --git a/ui2/src/style/_common.scss b/ui2/src/style/_common.scss index 19de09fbc..1b05d0ae7 100644 --- a/ui2/src/style/_common.scss +++ b/ui2/src/style/_common.scss @@ -157,4 +157,8 @@ a.dropdown-item.is-active, .dropdown .dropdown-menu .has-link a.is-active, butto } .button.is-danger.is-light { color: $danger; +} + +.textarea:not([rows]) { + min-height: 4em; } \ No newline at end of file diff --git a/ui2/src/views/datatype/DataTypesRepositoryView.vue b/ui2/src/views/datatype/DataTypesRepositoryView.vue index 1a05eea79..5b1c98b3c 100644 --- a/ui2/src/views/datatype/DataTypesRepositoryView.vue +++ b/ui2/src/views/datatype/DataTypesRepositoryView.vue @@ -74,9 +74,9 @@ <b-collapse animation="slide" aria-id="fileDeposit" class="card"> <template #trigger="props"> <div aria-controls="fileDeposit" class="card-header" role="button"> - <p class="card-header-title"> + <h2 class="card-header-title"> {{ $t("dataTypesRepository.card-title-upload-file") }} - </p> + </h2> <a class="card-header-icon"> <b-icon :icon="props.open ? 'chevron-down' : 'chevron-up'"></b-icon> </a> @@ -130,7 +130,7 @@ </div> </div> <div class="columns"> - <b-field class="column" label="Commentaire" expanded> + <b-field class="column" :label="$t('dataTypesRepository.comment')" expanded> <b-input v-model="comment" maxlength="200" type="textarea"></b-input> </b-field> </div> @@ -204,11 +204,17 @@ </tr> <tr v-for="dataset in currentDataset" :key="dataset.id"> <td align> - <b-tooltip type="is-dark" multilined> - <a>{{ dataset.id.slice(0, 8) }}</a> + <b-tooltip type="is-dark" :id="dataset.id" multilined role="tooltip"> <template v-slot:content> + <h3>{{ $t("dataTypesRepository.comment") }} {{ $t("ponctuation.colon") }}</h3> <p>{{ UTCToString(dataset.params.binaryFiledataset.comment) }}</p> </template> + <a + :aria-describedby="dataset.id" + tabindex="0" + @keypress.enter="changeCss(dataset.id)" + >{{ dataset.id.slice(0, 8) }}</a + > </b-tooltip> </td> <td align>{{ dataset.size }}</td> @@ -307,6 +313,12 @@ export default class DataTypesRepositoryView extends Vue { this.$on("parseAuth", this.parseAuth); } + changeCss(id) { + if (document.getElementById(id).querySelector(".tooltip-content").style.display === "block") + document.getElementById(id).querySelector(".tooltip-content").style.display = "none"; + else document.getElementById(id).querySelector(".tooltip-content").style.display = "block"; + } + created() { const prevPath = `/applications/${this.applicationName}/dataTypes`; this.subMenuPaths = [ @@ -636,7 +648,7 @@ export default class DataTypesRepositoryView extends Vue { overflow-wrap: break-word; } } -.dropdown-content{ +.dropdown-content { margin-left: 20px; margin-right: -20px; } @@ -661,4 +673,11 @@ caption { font-size: 20px; margin-bottom: 15px; } + +.b-tooltip { + .tooltip-trigger a { + } + .tooltip-content { + } +} </style> -- GitLab From 4f21ea91844be4ea764c6fda6b89c7fe3725e127 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Wed, 24 Nov 2021 16:00:33 +0100 Subject: [PATCH 10/24] =?UTF-8?q?essai=20ajout=20champs=20commentaire=20?= =?UTF-8?q?=C3=A0=20la=20cr=C3=A9ation=20de=20l'application=20--=20non=20c?= =?UTF-8?q?oncluant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/fr/inra/oresing/model/Application.java | 1 + .../java/fr/inra/oresing/rest/ApplicationResult.java | 1 + src/main/java/fr/inra/oresing/rest/OreSiResources.java | 6 +++--- src/main/java/fr/inra/oresing/rest/OreSiService.java | 3 ++- ui2/src/model/Application.js | 1 + ui2/src/model/ApplicationConfig.js | 2 ++ ui2/src/model/ApplicationResult.js | 1 + ui2/src/services/rest/ApplicationService.js | 3 ++- ui2/src/views/application/ApplicationCreationView.vue | 10 ++++++++-- ui2/src/views/application/ApplicationsView.vue | 6 ++++-- 10 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/main/java/fr/inra/oresing/model/Application.java b/src/main/java/fr/inra/oresing/model/Application.java index 657deb37b..a758476fe 100644 --- a/src/main/java/fr/inra/oresing/model/Application.java +++ b/src/main/java/fr/inra/oresing/model/Application.java @@ -13,6 +13,7 @@ import java.util.UUID; @ToString(callSuper = true) public class Application extends OreSiEntity { private String name; + private String comment; private List<String> referenceType; private List<String> dataType; private Configuration configuration; diff --git a/src/main/java/fr/inra/oresing/rest/ApplicationResult.java b/src/main/java/fr/inra/oresing/rest/ApplicationResult.java index cee9cf203..c792940ab 100644 --- a/src/main/java/fr/inra/oresing/rest/ApplicationResult.java +++ b/src/main/java/fr/inra/oresing/rest/ApplicationResult.java @@ -12,6 +12,7 @@ public class ApplicationResult { String id; String name; String title; + String comment; InternationalizationMap internationalization; Map<String, Reference> references; Map<String, DataType> dataTypes; diff --git a/src/main/java/fr/inra/oresing/rest/OreSiResources.java b/src/main/java/fr/inra/oresing/rest/OreSiResources.java index d59af7505..e324ca783 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiResources.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiResources.java @@ -87,12 +87,12 @@ public class OreSiResources { } @PostMapping(value = "/applications/{name}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<?> createApplication(@PathVariable("name") String name, + public ResponseEntity<?> createApplication(@PathVariable("name") String name, @RequestParam("comment") String comment, @RequestParam("file") MultipartFile file) throws IOException, BadApplicationConfigurationException { if (INVALID_APPLICATION_NAME_PREDICATE.test(name)) { return ResponseEntity.badRequest().body("'" + name + "' n’est pas un nom d'application valide, seules les lettres minuscules sont acceptées"); } - UUID result = service.createApplication(name, file); + UUID result = service.createApplication(name, file, comment); String uri = UriUtils.encodePath("/applications/" + result, Charset.defaultCharset()); return ResponseEntity.created(URI.create(uri)).body(Map.of("id", result.toString())); } @@ -130,7 +130,7 @@ public class OreSiResources { Map<String, String> repository = application.getConfiguration().getDataTypes().get(dataType).getRepository(); return new ApplicationResult.DataType(dataType, dataType, variables, Optional.ofNullable(repository).filter(m -> !m.isEmpty()).orElse(null)); }); - ApplicationResult applicationResult = new ApplicationResult(application.getId().toString(), application.getName(), application.getConfiguration().getApplication().getName(), application.getConfiguration().getInternationalization(), references, dataTypes); + ApplicationResult applicationResult = new ApplicationResult(application.getId().toString(), application.getName(), application.getConfiguration().getApplication().getName(), application.getComment(), application.getConfiguration().getInternationalization(), references, dataTypes); return ResponseEntity.ok(applicationResult); } diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java index 06b734db4..3e59e14e9 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiService.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java @@ -134,10 +134,11 @@ public class OreSiService { return result; } - public UUID createApplication(String name, MultipartFile configurationFile) throws IOException, BadApplicationConfigurationException { + public UUID createApplication(String name, MultipartFile configurationFile, String comment) throws IOException, BadApplicationConfigurationException { Application app = new Application(); app.setName(name); + app.setComment(comment); authenticationService.resetRole(); diff --git a/ui2/src/model/Application.js b/ui2/src/model/Application.js index 118b1ddb7..4633eb6af 100644 --- a/ui2/src/model/Application.js +++ b/ui2/src/model/Application.js @@ -5,6 +5,7 @@ export class Application { dataType; id; name; + comment; referenceType; updateDate; } diff --git a/ui2/src/model/ApplicationConfig.js b/ui2/src/model/ApplicationConfig.js index 8dcea9e05..e79d0d47d 100644 --- a/ui2/src/model/ApplicationConfig.js +++ b/ui2/src/model/ApplicationConfig.js @@ -1,9 +1,11 @@ export class ApplicationConfig { file; name; + comment; constructor() { this.file = null; this.name = ""; + this.comment=""; } } diff --git a/ui2/src/model/ApplicationResult.js b/ui2/src/model/ApplicationResult.js index 835c10792..71a65b9e9 100644 --- a/ui2/src/model/ApplicationResult.js +++ b/ui2/src/model/ApplicationResult.js @@ -2,6 +2,7 @@ export class ApplicationResult { id; name; title; + comment; references = { idRef: { id: "", diff --git a/ui2/src/services/rest/ApplicationService.js b/ui2/src/services/rest/ApplicationService.js index 4a2039044..14b33922e 100644 --- a/ui2/src/services/rest/ApplicationService.js +++ b/ui2/src/services/rest/ApplicationService.js @@ -8,9 +8,10 @@ export class ApplicationService extends Fetcher { super(); } - async createApplication(applicationConfig) { + async createApplication(applicationConfig, comment) { return this.post("applications/" + applicationConfig.name, { file: applicationConfig.file, + comment: comment, }); } diff --git a/ui2/src/views/application/ApplicationCreationView.vue b/ui2/src/views/application/ApplicationCreationView.vue index de85979b8..1e9e1b23d 100644 --- a/ui2/src/views/application/ApplicationCreationView.vue +++ b/ui2/src/views/application/ApplicationCreationView.vue @@ -55,6 +55,11 @@ </b-upload> </b-field> </ValidationProvider> + <div class="columns"> + <b-field class="column" :label="$t('dataTypesRepository.comment')" expanded> + <b-input v-model="comment" maxlength="200" type="textarea"></b-input> + </b-field> + </div> <div class="buttons"> <b-button type="is-light" @click="handleSubmit(testApplication)" icon-left="vial"> {{ $t("applications.test") }} @@ -102,11 +107,12 @@ export default class ApplicationCreationView extends Vue { applicationConfig = new ApplicationConfig(); errorsMessages = []; + comment = ""; async createApplication() { this.errorsMessages = []; try { - await this.applicationService.createApplication(this.applicationConfig); + await this.applicationService.createApplication(this.applicationConfig, this.comment); this.alertService.toastSuccess(this.$t("alert.application-creation-success")); this.$router.push("/applications"); } catch (error) { @@ -118,7 +124,7 @@ export default class ApplicationCreationView extends Vue { this.errorsMessages = []; try { let response = await this.applicationService.validateConfiguration(this.applicationConfig); - if (response.valid == true) { + if (response.valid === true) { this.alertService.toastSuccess(this.$t("alert.application-validate-success")); } else { this.errorsMessages = this.errorsService.getErrorsMessages(response.validationCheckResults); diff --git a/ui2/src/views/application/ApplicationsView.vue b/ui2/src/views/application/ApplicationsView.vue index 07cc2353d..2e62f328f 100644 --- a/ui2/src/views/application/ApplicationsView.vue +++ b/ui2/src/views/application/ApplicationsView.vue @@ -114,7 +114,7 @@ @click="showModal(application.name)" /> <b-modal - v-show="isSelectedName == application.name" + v-show="isSelectedName === application.name" :id="application.name" v-model="isCardModalActive" > @@ -127,9 +127,11 @@ <div class="card-content"> <div class="content"> <p> - {{ application.referenceType }} {{ $t("ponctuation.comma") }} {{ application.dataType }} </p> + <p> + {{ application.comment }} + </p> </div> </div> <div class="card-footer"> -- GitLab From bc0997b650da5290baa50421bdef423f6b1473ad Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Mon, 3 Jan 2022 16:03:22 +0100 Subject: [PATCH 11/24] step by step for comment --- .../fr/inra/oresing/model/Configuration.java | 1 + .../oresing/persistence/BinaryFileInfos.java | 8 ++++++++ .../fr/inra/oresing/rest/OreSiResources.java | 4 ++-- .../fr/inra/oresing/rest/OreSiService.java | 20 ++++++++++--------- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/fr/inra/oresing/model/Configuration.java b/src/main/java/fr/inra/oresing/model/Configuration.java index 75002f39c..079445aef 100644 --- a/src/main/java/fr/inra/oresing/model/Configuration.java +++ b/src/main/java/fr/inra/oresing/model/Configuration.java @@ -19,6 +19,7 @@ public class Configuration { private String defaultLanguage; private InternationalizationMap internationalization; private int version; + private String comment; private ApplicationDescription application; private LinkedHashMap<String, ReferenceDescription> references = new LinkedHashMap<>(); private LinkedHashMap<String, CompositeReferenceDescription> compositeReferences = new LinkedHashMap<>(); diff --git a/src/main/java/fr/inra/oresing/persistence/BinaryFileInfos.java b/src/main/java/fr/inra/oresing/persistence/BinaryFileInfos.java index fa67d5df8..3495d1823 100644 --- a/src/main/java/fr/inra/oresing/persistence/BinaryFileInfos.java +++ b/src/main/java/fr/inra/oresing/persistence/BinaryFileInfos.java @@ -16,4 +16,12 @@ public class BinaryFileInfos { public UUID createuser; public String createdate; public String comment; + + public void setComment(String comment) { + this.comment = comment; + } + + public String getComment() { + return comment; + } } \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/rest/OreSiResources.java b/src/main/java/fr/inra/oresing/rest/OreSiResources.java index e324ca783..a9fc0bfc9 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiResources.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiResources.java @@ -142,11 +142,11 @@ public class OreSiResources { } @PostMapping(value = "/applications/{nameOrId}/configuration", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<Map<String, Object>> changeConfiguration(@PathVariable("nameOrId") String nameOrId, @RequestParam("file") MultipartFile file) throws IOException, BadApplicationConfigurationException { + public ResponseEntity<Map<String, Object>> changeConfiguration(@PathVariable("nameOrId") String nameOrId, @RequestParam("file") MultipartFile file, @RequestParam("comment") String comment) throws IOException, BadApplicationConfigurationException { if (file.isEmpty()) { return ResponseEntity.badRequest().build(); } - UUID result = service.changeApplicationConfiguration(nameOrId, file); + UUID result = service.changeApplicationConfiguration(nameOrId, file, comment); String uri = UriUtils.encodePath(String.format("/applications/%s/configuration/%s", nameOrId, result), Charset.defaultCharset()); return ResponseEntity.created(URI.create(uri)).body(Map.of("id", result.toString())); } diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java index 3e59e14e9..b13cb65cc 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiService.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java @@ -118,7 +118,7 @@ public class OreSiService { Splitter.on(LTREE_SEPARATOR).split(compositeKey).forEach(OreSiService::checkNaturalKeySyntax); } - protected UUID storeFile(Application app, MultipartFile file) throws IOException { + protected UUID storeFile(Application app, MultipartFile file, String commentApplication) throws IOException { authenticationService.setRoleForClient(); // creation du fichier BinaryFile binaryFile = new BinaryFile(); @@ -128,6 +128,7 @@ public class OreSiService { binaryFile.setData(file.getBytes()); BinaryFileInfos binaryFileInfos = new BinaryFileInfos(); binaryFile.setParams(binaryFileInfos); + binaryFile.getParams().setComment(commentApplication); binaryFile.getParams().createuser = request.getRequestClient().getId(); binaryFile.getParams().createdate = LocalDateTime.now().toString(); UUID result = repo.getRepository(app).binaryFile().store(binaryFile); @@ -138,7 +139,7 @@ public class OreSiService { Application app = new Application(); app.setName(name); - app.setComment(comment); + //app.setComment(comment); authenticationService.resetRole(); @@ -187,21 +188,22 @@ public class OreSiService { authenticationService.setRoleForClient(); UUID result = repo.application().store(app); - changeApplicationConfiguration(app, configurationFile); + changeApplicationConfiguration(app, configurationFile, comment); relationalService.createViews(app.getName()); return result; } - public UUID changeApplicationConfiguration(String nameOrId, MultipartFile configurationFile) throws IOException, BadApplicationConfigurationException { + public UUID changeApplicationConfiguration(String nameOrId, MultipartFile configurationFile, String comment) throws IOException, BadApplicationConfigurationException { relationalService.dropViews(nameOrId); authenticationService.setRoleForClient(); Application app = getApplication(nameOrId); Configuration oldConfiguration = app.getConfiguration(); UUID oldConfigFileId = app.getConfigFile(); - UUID uuid = changeApplicationConfiguration(app, configurationFile); + UUID uuid = changeApplicationConfiguration(app, configurationFile, comment); Configuration newConfiguration = app.getConfiguration(); + newConfiguration.setComment(comment); int oldVersion = oldConfiguration.getApplication().getVersion(); int newVersion = newConfiguration.getApplication().getVersion(); Preconditions.checkArgument(newVersion > oldVersion, "l'application " + app.getName() + " est déjà dans la version " + oldVersion); @@ -298,14 +300,14 @@ public class OreSiService { .forEach(validateRow); } - private UUID changeApplicationConfiguration(Application app, MultipartFile configurationFile) throws IOException, BadApplicationConfigurationException { + private UUID changeApplicationConfiguration(Application app, MultipartFile configurationFile, String commentApplication) throws IOException, BadApplicationConfigurationException { ConfigurationParsingResult configurationParsingResult = applicationConfigurationService.parseConfigurationBytes(configurationFile.getBytes()); BadApplicationConfigurationException.check(configurationParsingResult); Configuration configuration = configurationParsingResult.getResult(); app.setReferenceType(new ArrayList<>(configuration.getReferences().keySet())); app.setDataType(new ArrayList<>(configuration.getDataTypes().keySet())); app.setConfiguration(configuration); - UUID confId = storeFile(app, configurationFile); + UUID confId = storeFile(app, configurationFile, commentApplication); app.setConfigFile(confId); repo.application().store(app); return confId; @@ -313,7 +315,7 @@ public class OreSiService { public UUID addReference(Application app, String refType, MultipartFile file) throws IOException { authenticationService.setRoleForClient(); - UUID fileId = storeFile(app, file); + UUID fileId = storeFile(app, file, app.getComment()); Configuration conf = app.getConfiguration(); Configuration.ReferenceDescription ref = conf.getReferences().get(refType); @@ -689,7 +691,7 @@ public class OreSiService { .orElseGet(() -> { UUID fileId = null; try { - fileId = storeFile(app, file); + fileId = storeFile(app, file, app.getComment()); } catch (IOException e) { return null; } -- GitLab From 6d0163c1c8feaac5e88815418c1918aac9353b44 Mon Sep 17 00:00:00 2001 From: TCHERNIATINSKY <philippe.tcherniatinsky@inrae.fr> Date: Thu, 6 Jan 2022 15:23:03 +0100 Subject: [PATCH 12/24] modification de binaryfile --- src/main/java/fr/inra/oresing/model/BinaryFile.java | 7 ++----- .../oresing/persistence/BinaryFileRepository.java | 11 ++++++----- .../migration/application/V1__init_schema.sql | 1 + 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/main/java/fr/inra/oresing/model/BinaryFile.java b/src/main/java/fr/inra/oresing/model/BinaryFile.java index c74a16e2d..fcdb89e98 100644 --- a/src/main/java/fr/inra/oresing/model/BinaryFile.java +++ b/src/main/java/fr/inra/oresing/model/BinaryFile.java @@ -1,14 +1,10 @@ package fr.inra.oresing.model; import fr.inra.oresing.persistence.BinaryFileInfos; -import fr.inra.oresing.persistence.UserRepository; import lombok.Getter; import lombok.Setter; import lombok.ToString; -import org.postgresql.util.Base64; -import javax.xml.bind.DatatypeConverter; -import java.util.Date; import java.util.UUID; @Getter @@ -20,7 +16,8 @@ public class BinaryFile extends OreSiEntity { } private UUID application; private String name; + private String comment; private long size; private byte[] data; private BinaryFileInfos params; -} +} \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/persistence/BinaryFileRepository.java b/src/main/java/fr/inra/oresing/persistence/BinaryFileRepository.java index d9aa25c07..dc9938b95 100644 --- a/src/main/java/fr/inra/oresing/persistence/BinaryFileRepository.java +++ b/src/main/java/fr/inra/oresing/persistence/BinaryFileRepository.java @@ -34,13 +34,13 @@ public class BinaryFileRepository extends JsonTableInApplicationSchemaRepository public Optional<BinaryFile> tryFindByIdWithData(UUID id) { Preconditions.checkArgument(id != null); - String query = String.format("SELECT '%s' as \"@class\", to_jsonb(t) as json FROM (select id, application, name, size, convert_from(data, 'UTF8') as \"data\", params from %s WHERE id = :id) t", getEntityClass().getName(), getTable().getSqlIdentifier()); + String query = String.format("SELECT '%s' as \"@class\", to_jsonb(t) as json FROM (select id, application, name, comment, size, convert_from(data, 'UTF8') as \"data\", params from %s WHERE id = :id) t", getEntityClass().getName(), getTable().getSqlIdentifier()); Optional<BinaryFile> result = getNamedParameterJdbcTemplate().query(query, new MapSqlParameterSource("id", id), getJsonRowMapper()).stream().findFirst(); return result; } protected List<BinaryFile> find(String whereClause, SqlParameterSource sqlParameterSource) { - String sql = "SELECT '%s' as \"@class\", to_jsonb(t) as json FROM (select id, application, name, size, null as \"data\", params from %s "; + String sql = "SELECT '%s' as \"@class\", to_jsonb(t) as json FROM (select id, application, name, comment, size, null as \"data\", params from %s "; if (whereClause != null) { sql += " WHERE " + whereClause; } @@ -57,9 +57,9 @@ public class BinaryFileRepository extends JsonTableInApplicationSchemaRepository @Override protected String getUpsertQuery() { - return "INSERT INTO " + getTable().getSqlIdentifier() + "(id, application, name, size, data, params) " + + return "INSERT INTO " + getTable().getSqlIdentifier() + "(id, application, name, comment, size, data, params) " + "SELECT " + - " id, application, name, size, data, " + + " id, application, name, comment, size, data, " + "jsonb_set(jsonb_set((case when params is null then '{}' else params end ),\n" + "\t'{createdate}',('\"' ||CURRENT_TIMESTAMP::text ||'\"')::jsonb),\n" + "\t'{create_user}' , ('\"' ||current_role::text ||'\"')::jsonb)" + @@ -69,6 +69,7 @@ public class BinaryFileRepository extends JsonTableInApplicationSchemaRepository "SET " + " updateDate=current_timestamp, " + " application=EXCLUDED.application, " + + " comment=EXCLUDED.comment, " + " name=EXCLUDED.name, " + " size=EXCLUDED.size, " + " data=CASE WHEN EXCLUDED.data IS NULL THEN " + getTable().getSqlIdentifier() + ".data ELSE EXCLUDED.data END, " + @@ -133,4 +134,4 @@ public class BinaryFileRepository extends JsonTableInApplicationSchemaRepository protected Class<BinaryFile> getEntityClass() { return BinaryFile.class; } -} +} \ No newline at end of file diff --git a/src/main/resources/migration/application/V1__init_schema.sql b/src/main/resources/migration/application/V1__init_schema.sql index 498522b7f..b47b42b65 100644 --- a/src/main/resources/migration/application/V1__init_schema.sql +++ b/src/main/resources/migration/application/V1__init_schema.sql @@ -4,6 +4,7 @@ create table BinaryFile ( updateDate DateOrNow, application EntityRef REFERENCES Application(id), name Text, + comment TEXT, size INT, data bytea, params jsonb -- GitLab From 872c53c874a6b4073b3d62203505754e5a8cdf77 Mon Sep 17 00:00:00 2001 From: TCHERNIATINSKY <philippe.tcherniatinsky@inrae.fr> Date: Thu, 6 Jan 2022 15:23:03 +0100 Subject: [PATCH 13/24] Ajout d'un champs commentaire dans application et binary file --- .../persistence/ApplicationRepository.java | 6 +++--- .../fr/inra/oresing/rest/OreSiResources.java | 4 ++-- .../fr/inra/oresing/rest/OreSiService.java | 20 +++++++++---------- .../migration/main/V1__init_schema.sql | 3 ++- .../inra/oresing/rest/OreSiResourcesTest.java | 2 ++ 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/main/java/fr/inra/oresing/persistence/ApplicationRepository.java b/src/main/java/fr/inra/oresing/persistence/ApplicationRepository.java index 4119703d9..e8a6747f8 100644 --- a/src/main/java/fr/inra/oresing/persistence/ApplicationRepository.java +++ b/src/main/java/fr/inra/oresing/persistence/ApplicationRepository.java @@ -17,8 +17,8 @@ public class ApplicationRepository extends JsonTableRepositoryTemplate<Applicati @Override protected String getUpsertQuery() { - return "INSERT INTO " + getTable().getSqlIdentifier() + " (id, name, referenceType, dataType, configuration, configFile) SELECT id, name, referenceType, dataType, configuration, configFile FROM json_populate_recordset(NULL::" + getTable().getSqlIdentifier() + ", :json::json)" - + " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, name=EXCLUDED.name, referenceType=EXCLUDED.referenceType, dataType=EXCLUDED.dataType, configuration=EXCLUDED.configuration, configFile=EXCLUDED.configFile" + return "INSERT INTO " + getTable().getSqlIdentifier() + " (id, name, comment, referenceType, dataType, configuration, configFile) SELECT id, name, comment, referenceType, dataType, configuration, configFile FROM json_populate_recordset(NULL::" + getTable().getSqlIdentifier() + ", :json::json)" + + " ON CONFLICT (id) DO UPDATE SET updateDate=current_timestamp, comment=EXCLUDED.comment, name=EXCLUDED.name, referenceType=EXCLUDED.referenceType, dataType=EXCLUDED.dataType, configuration=EXCLUDED.configuration, configFile=EXCLUDED.configFile" + " RETURNING id"; } @@ -48,4 +48,4 @@ public class ApplicationRepository extends JsonTableRepositoryTemplate<Applicati public Application findApplication(UUID id) { return findApplication(id.toString()); } -} +} \ No newline at end of file diff --git a/src/main/java/fr/inra/oresing/rest/OreSiResources.java b/src/main/java/fr/inra/oresing/rest/OreSiResources.java index a9fc0bfc9..ad705f850 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiResources.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiResources.java @@ -87,7 +87,7 @@ public class OreSiResources { } @PostMapping(value = "/applications/{name}", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<?> createApplication(@PathVariable("name") String name, @RequestParam("comment") String comment, + public ResponseEntity<?> createApplication(@PathVariable("name") String name, @RequestParam(name = "comment",defaultValue = "") String comment, @RequestParam("file") MultipartFile file) throws IOException, BadApplicationConfigurationException { if (INVALID_APPLICATION_NAME_PREDICATE.test(name)) { return ResponseEntity.badRequest().body("'" + name + "' n’est pas un nom d'application valide, seules les lettres minuscules sont acceptées"); @@ -142,7 +142,7 @@ public class OreSiResources { } @PostMapping(value = "/applications/{nameOrId}/configuration", produces = MediaType.APPLICATION_JSON_VALUE) - public ResponseEntity<Map<String, Object>> changeConfiguration(@PathVariable("nameOrId") String nameOrId, @RequestParam("file") MultipartFile file, @RequestParam("comment") String comment) throws IOException, BadApplicationConfigurationException { + public ResponseEntity<Map<String, Object>> changeConfiguration(@PathVariable("nameOrId") String nameOrId, @RequestParam("file") MultipartFile file, @RequestParam(name = "comment", defaultValue = "") String comment) throws IOException, BadApplicationConfigurationException { if (file.isEmpty()) { return ResponseEntity.badRequest().build(); } diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java index b13cb65cc..3d56c8a96 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiService.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java @@ -118,17 +118,17 @@ public class OreSiService { Splitter.on(LTREE_SEPARATOR).split(compositeKey).forEach(OreSiService::checkNaturalKeySyntax); } - protected UUID storeFile(Application app, MultipartFile file, String commentApplication) throws IOException { + protected UUID storeFile(Application app, MultipartFile file, String comment) throws IOException { authenticationService.setRoleForClient(); // creation du fichier BinaryFile binaryFile = new BinaryFile(); + binaryFile.setComment(comment); binaryFile.setApplication(app.getId()); binaryFile.setName(file.getOriginalFilename()); binaryFile.setSize(file.getSize()); binaryFile.setData(file.getBytes()); BinaryFileInfos binaryFileInfos = new BinaryFileInfos(); binaryFile.setParams(binaryFileInfos); - binaryFile.getParams().setComment(commentApplication); binaryFile.getParams().createuser = request.getRequestClient().getId(); binaryFile.getParams().createdate = LocalDateTime.now().toString(); UUID result = repo.getRepository(app).binaryFile().store(binaryFile); @@ -139,7 +139,7 @@ public class OreSiService { Application app = new Application(); app.setName(name); - //app.setComment(comment); + app.setComment(comment); authenticationService.resetRole(); @@ -188,7 +188,7 @@ public class OreSiService { authenticationService.setRoleForClient(); UUID result = repo.application().store(app); - changeApplicationConfiguration(app, configurationFile, comment); + changeApplicationConfiguration(app, configurationFile); relationalService.createViews(app.getName()); @@ -199,11 +199,11 @@ public class OreSiService { relationalService.dropViews(nameOrId); authenticationService.setRoleForClient(); Application app = getApplication(nameOrId); + app.setComment(comment); Configuration oldConfiguration = app.getConfiguration(); UUID oldConfigFileId = app.getConfigFile(); - UUID uuid = changeApplicationConfiguration(app, configurationFile, comment); + UUID uuid = changeApplicationConfiguration(app, configurationFile); Configuration newConfiguration = app.getConfiguration(); - newConfiguration.setComment(comment); int oldVersion = oldConfiguration.getApplication().getVersion(); int newVersion = newConfiguration.getApplication().getVersion(); Preconditions.checkArgument(newVersion > oldVersion, "l'application " + app.getName() + " est déjà dans la version " + oldVersion); @@ -300,14 +300,14 @@ public class OreSiService { .forEach(validateRow); } - private UUID changeApplicationConfiguration(Application app, MultipartFile configurationFile, String commentApplication) throws IOException, BadApplicationConfigurationException { + private UUID changeApplicationConfiguration(Application app, MultipartFile configurationFile) throws IOException, BadApplicationConfigurationException { ConfigurationParsingResult configurationParsingResult = applicationConfigurationService.parseConfigurationBytes(configurationFile.getBytes()); BadApplicationConfigurationException.check(configurationParsingResult); Configuration configuration = configurationParsingResult.getResult(); app.setReferenceType(new ArrayList<>(configuration.getReferences().keySet())); app.setDataType(new ArrayList<>(configuration.getDataTypes().keySet())); app.setConfiguration(configuration); - UUID confId = storeFile(app, configurationFile, commentApplication); + UUID confId = storeFile(app, configurationFile, app.getComment()); app.setConfigFile(confId); repo.application().store(app); return confId; @@ -315,7 +315,7 @@ public class OreSiService { public UUID addReference(Application app, String refType, MultipartFile file) throws IOException { authenticationService.setRoleForClient(); - UUID fileId = storeFile(app, file, app.getComment()); + UUID fileId = storeFile(app, file,""); Configuration conf = app.getConfiguration(); Configuration.ReferenceDescription ref = conf.getReferences().get(refType); @@ -691,7 +691,7 @@ public class OreSiService { .orElseGet(() -> { UUID fileId = null; try { - fileId = storeFile(app, file, app.getComment()); + fileId = storeFile(app, file,""); } catch (IOException e) { return null; } diff --git a/src/main/resources/migration/main/V1__init_schema.sql b/src/main/resources/migration/main/V1__init_schema.sql index 6775366c9..60fa6d9a0 100644 --- a/src/main/resources/migration/main/V1__init_schema.sql +++ b/src/main/resources/migration/main/V1__init_schema.sql @@ -153,6 +153,7 @@ create table Application ( creationDate DateOrNow, updateDate DateOrNow, name Text, + comment TEXT, referenceType TEXT[], -- liste des types de references existantes dataType TEXT[], -- liste des types de data existants configuration jsonb, -- le fichier de configuration sous forme json @@ -187,4 +188,4 @@ CREATE POLICY "applicationCreator_Application_select" ON Application AS PERMISSI USING ( true ); CREATE AGGREGATE jsonb_object_agg(jsonb) (SFUNC = 'jsonb_concat', STYPE = jsonb, INITCOND = '{}'); -CREATE AGGREGATE aggregate_by_array_concatenation(anyarray) (SFUNC = 'array_cat', STYPE = anyarray, INITCOND = '{}'); +CREATE AGGREGATE aggregate_by_array_concatenation(anyarray) (SFUNC = 'array_cat', STYPE = anyarray, INITCOND = '{}'); \ No newline at end of file diff --git a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java index bcb048adf..8aa7296ac 100644 --- a/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java +++ b/src/test/java/fr/inra/oresing/rest/OreSiResourcesTest.java @@ -105,6 +105,7 @@ public class OreSiResourcesTest { String response = mockMvc.perform(MockMvcRequestBuilders.multipart("/api/v1/applications/monsore") .file(configuration) + .param("comment", "commentaire") .cookie(authCookie)) .andExpect(status().isCreated()) .andExpect(jsonPath("$.id", IsNull.notNullValue())) @@ -124,6 +125,7 @@ public class OreSiResourcesTest { ApplicationResult applicationResult = objectMapper.readValue(response, ApplicationResult.class); + Assert.assertEquals("commentaire", applicationResult.getComment()); Assert.assertEquals("monsore", applicationResult.getName()); Assert.assertEquals(Set.of("especes", "projet", "sites", "themes", "type de fichiers", "type_de_sites", "types_de_donnees_par_themes_de_sites_et_projet", "unites", "valeurs_qualitatives", "variables", "variables_et_unites_par_types_de_donnees"), applicationResult.getReferences().keySet()); // Assert.assertEquals(List.of("pem"), applicationResult.getDataType()); -- GitLab From bd2d8c4d8e55d22410658942c1943aab3f06f27e Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Fri, 4 Feb 2022 20:11:54 +0100 Subject: [PATCH 14/24] =?UTF-8?q?gestion=20des=20confli=20apr=C3=A8s=20le?= =?UTF-8?q?=20merge=20(j'en=20avais=20loup=C3=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fr/inra/oresing/rest/OreSiService.java | 2 +- .../components/common/AuthorizationTable.vue | 397 ++++++++++-------- ui2/src/model/ApplicationConfig.js | 2 +- ui2/src/model/DataTypeAuthorization.js | 2 +- ui2/src/model/authorization/Authorization.js | 72 ++-- .../services/InternationalisationService.js | 6 +- .../application/ApplicationCreationView.vue | 2 +- .../DataTypeAuthorizationInfoView.vue | 74 ++-- .../DataTypeAuthorizationsView.vue | 59 +-- 9 files changed, 334 insertions(+), 282 deletions(-) diff --git a/src/main/java/fr/inra/oresing/rest/OreSiService.java b/src/main/java/fr/inra/oresing/rest/OreSiService.java index 4dc2ce73b..ac6c5208e 100644 --- a/src/main/java/fr/inra/oresing/rest/OreSiService.java +++ b/src/main/java/fr/inra/oresing/rest/OreSiService.java @@ -137,7 +137,7 @@ public class OreSiService { return result; } - public UUID createApplication(String name, MultipartFile configurationFile) throws IOException, BadApplicationConfigurationException { + public UUID createApplication(String name, MultipartFile configurationFile, String comment) throws IOException, BadApplicationConfigurationException { Application app = new Application(); app.setName(name); app.setComment(comment); diff --git a/ui2/src/components/common/AuthorizationTable.vue b/ui2/src/components/common/AuthorizationTable.vue index 511e9772c..362f4f31a 100644 --- a/ui2/src/components/common/AuthorizationTable.vue +++ b/ui2/src/components/common/AuthorizationTable.vue @@ -85,20 +85,28 @@ @input.capture="selectCheckbox($event, index, indexColumn, scope)" > </b-taginput> - <div v-if="states && states[indexColumn] && states[indexColumn][index]==1 && - localAuthorizationsTree && localAuthorizationsTree[indexColumn] && localAuthorizationsTree[indexColumn][index]" - class="column"> + <div + v-if=" + states && + states[indexColumn] && + states[indexColumn][index] == 1 && + localAuthorizationsTree && + localAuthorizationsTree[indexColumn] && + localAuthorizationsTree[indexColumn][index] + " + class="column" + > <b-datepicker - v-model="localAuthorizationsTree[indexColumn][index].from" - :date-parser="parseDate" - :placeholder=" - $t('dataTypesRepository.placeholder-datepicker') + - ' dd-MM-YYYY, dd-MM-YYYY hh, dd-MM-YYYY hh:mm, dd-MM-YYYY hh:mm:ss' - " - editable - icon="calendar" - @remove.capture="()=>selectCheckbox($event,index, indexColumn, scope)" - @input.capture="selectCheckbox($event,index, indexColumn, scope, 'from')" + v-model="localAuthorizationsTree[indexColumn][index].from" + :date-parser="parseDate" + :placeholder=" + $t('dataTypesRepository.placeholder-datepicker') + + ' dd-MM-YYYY, dd-MM-YYYY hh, dd-MM-YYYY hh:mm, dd-MM-YYYY hh:mm:ss' + " + editable + icon="calendar" + @remove.capture="() => selectCheckbox($event, index, indexColumn, scope)" + @input.capture="selectCheckbox($event, index, indexColumn, scope, 'from')" > </b-datepicker> </div> @@ -114,15 +122,15 @@ class="column" > <b-datepicker - v-model="localAuthorizationsTree[indexColumn][index].to" - :date-parser="parseDate" - :placeholder=" - $t('dataTypesRepository.placeholder-datepicker') + - ' dd-MM-YYYY, dd-MM-YYYY hh, dd-MM-YYYY hh:mm, dd-MM-YYYY hh:mm:ss' - " - editable - icon="calendar" - @input="selectCheckbox($event,index, indexColumn, scope, 'to')" + v-model="localAuthorizationsTree[indexColumn][index].to" + :date-parser="parseDate" + :placeholder=" + $t('dataTypesRepository.placeholder-datepicker') + + ' dd-MM-YYYY, dd-MM-YYYY hh, dd-MM-YYYY hh:mm, dd-MM-YYYY hh:mm:ss' + " + editable + icon="calendar" + @input="selectCheckbox($event, index, indexColumn, scope, 'to')" > </b-datepicker> </div> @@ -135,17 +143,18 @@ class="rows" > <AuthorizationTable - v-if="authorizationByScope && authReference" - :authReference="getNextAuthreference(scope)" - :authorization-scopes="remainingScopes[index]" - :authorizations-tree="authorizationByScope && authorizationByScope[index]" - :columnsVisible="columnsVisible" - :dataGroups="dataGroups" - :path="getPath(index)" - :remaining-option="getRemainingOption(scope)" - :required-authorizations="requiredAuthorizationByindex[index]" - @add-authorization="emitAddAuthorization($event, index)" - @delete-authorization="emitDeleteAuthorization($event, index)"/> + v-if="authorizationByScope && authReference" + :authReference="getNextAuthreference(scope)" + :authorization-scopes="remainingScopes[index]" + :authorizations-tree="authorizationByScope && authorizationByScope[index]" + :columnsVisible="columnsVisible" + :dataGroups="dataGroups" + :path="getPath(index)" + :remaining-option="getRemainingOption(scope)" + :required-authorizations="requiredAuthorizationByindex[index]" + @add-authorization="emitAddAuthorization($event, index)" + @delete-authorization="emitDeleteAuthorization($event, index)" + /> </ul> </div> </li> @@ -171,11 +180,11 @@ export default class AuthorizationTable extends Vue { @Prop() dataGroups; @Prop() requiredAuthorizations; @Prop() authorizationScopes; - name = 'AuthorizationTable' - authorizationByScope = {} - states = {} - statesIcons = {} - open = {} + name = "AuthorizationTable"; + authorizationByScope = {}; + states = {}; + statesIcons = {}; + open = {}; emits = ["add-authorization", "delete-authorization"]; upHere = false; requiredAuthorizationByindex = {}; @@ -187,20 +196,21 @@ export default class AuthorizationTable extends Vue { getRequiredAuthorization() { var requiredAuthorizationByIndex = {}; - var remainingScopes = this.remainingScopes || {} + var remainingScopes = this.remainingScopes || {}; for (const index in this.authReference) { - remainingScopes[index] = this.getNextScope(this.authReference?.[index]) - var requiredAuthorization = {...(this.requiredAuthorizations || {})} + remainingScopes[index] = this.getNextScope(this.authReference?.[index]); + var requiredAuthorization = { ...(this.requiredAuthorizations || {}) }; let scope = this.getScope(); if (scope) { - var requiredAuthorizationForIndex = (requiredAuthorization)[scope] || ""; - requiredAuthorizationForIndex = requiredAuthorizationForIndex + (requiredAuthorizationForIndex == "" ? "" : ".") + index - requiredAuthorization[this.authorizationScopes[0].id] = requiredAuthorizationForIndex + var requiredAuthorizationForIndex = requiredAuthorization[scope] || ""; + requiredAuthorizationForIndex = + requiredAuthorizationForIndex + (requiredAuthorizationForIndex == "" ? "" : ".") + index; + requiredAuthorization[this.authorizationScopes[0].id] = requiredAuthorizationForIndex; } - requiredAuthorizationByIndex[index] = requiredAuthorization + requiredAuthorizationByIndex[index] = requiredAuthorization; } this.remainingScopes = remainingScopes; - this.requiredAuthorizationByindex = requiredAuthorizationByIndex + this.requiredAuthorizationByindex = requiredAuthorizationByIndex; } getScope() { @@ -227,20 +237,31 @@ export default class AuthorizationTable extends Vue { let nextReference = this.getNextAuthreference(this.authReference[reference]); var auth = {}; for (const ref in nextReference) { - auth[ref] = new Authorization(this.authorizationsTree[type][reference], this.requiredAuthorizationByindex[reference]) + auth[ref] = new Authorization( + this.authorizationsTree[type][reference], + this.requiredAuthorizationByindex[reference] + ); } - authorizationByScope[reference][type] = auth + authorizationByScope[reference][type] = auth; } else { - authorizationByScope[reference][type] = new Authorization(this.authorizationsTree?.[type]?.[reference], this.requiredAuthorizationByindex[reference]); + authorizationByScope[reference][type] = new Authorization( + this.authorizationsTree?.[type]?.[reference], + this.requiredAuthorizationByindex[reference] + ); } } } - authorizationByScope[reference] = authorizationByScope[reference] || {} - authorizationByScope[reference][this.EXTRACTION] = authorizationByScope[reference][this.EXTRACTION] || (this.authorizationsTree?.[this.EXTRACTION]?.[reference] && new Authorization(this.authorizationsTree[this.EXTRACTION][reference], this.requiredAuthorizationByindex[reference])) - + authorizationByScope[reference] = authorizationByScope[reference] || {}; + authorizationByScope[reference][this.EXTRACTION] = + authorizationByScope[reference][this.EXTRACTION] || + (this.authorizationsTree?.[this.EXTRACTION]?.[reference] && + new Authorization( + this.authorizationsTree[this.EXTRACTION][reference], + this.requiredAuthorizationByindex[reference] + )); } - this.authorizationByScope = {...authorizationByScope}; - this.getRequiredAuthorization() + this.authorizationByScope = { ...authorizationByScope }; + this.getRequiredAuthorization(); } initStates() { @@ -287,9 +308,9 @@ export default class AuthorizationTable extends Vue { } updateState(type, reference, value, updateChildren) { - this.states[type] || this.initStates() - var states = this.states - var statesIcons = this.statesIcons + this.states[type] || this.initStates(); + var states = this.states; + var statesIcons = this.statesIcons; states[type][reference] = value; statesIcons[type][reference] = this.STATES[value]; this.states = states; @@ -299,15 +320,14 @@ export default class AuthorizationTable extends Vue { return; } if (updateChildren) { - this.getChildAuthorizationTable() - .forEach(child => { - child.states[type] || child.initStates() - if (child.states[type]) { - for (const childType in child.states[type]) { - child.updateState(type, childType, value, updateChildren) - } - } - }) + this.getChildAuthorizationTable().forEach((child) => { + child.states[type] || child.initStates(); + if (child.states[type]) { + for (const childType in child.states[type]) { + child.updateState(type, childType, value, updateChildren); + } + } + }); } } @@ -366,7 +386,7 @@ export default class AuthorizationTable extends Vue { getNextScope(scope) { if (!scope.isLeaf) { - return this.authorizationScopes + return this.authorizationScopes; } else { return (this.authorizationScopes || []).slice(1, (this.authorizationScopes || []).length); } @@ -382,100 +402,126 @@ export default class AuthorizationTable extends Vue { emitDeleteAuthorization(event, index) { let localAuthorizationsTree = this.localAuthorizationsTree || {}; - localAuthorizationsTree[event.indexColumn] = localAuthorizationsTree?.[event.indexColumn] || {} - localAuthorizationsTree[event.indexColumn][index] = localAuthorizationsTree?.[event.indexColumn][index] || {} - delete localAuthorizationsTree[event.indexColumn][index][event.child] - this.localAuthorizationsTree = {...localAuthorizationsTree}; - this.updateState(event.indexColumn, index, event.state, false) - this.$emit('delete-authorization', - { - indexColumn: event.indexColumn, - child: index, - state: this.buildState(event.indexColumn, index).state, - authorizationsTree: this.localAuthorizationsTree, - authorizationScope: localAuthorizationsTree?.[event.type]?.[index] - } - ) + localAuthorizationsTree[event.indexColumn] = localAuthorizationsTree?.[event.indexColumn] || {}; + localAuthorizationsTree[event.indexColumn][index] = + localAuthorizationsTree?.[event.indexColumn][index] || {}; + delete localAuthorizationsTree[event.indexColumn][index][event.child]; + this.localAuthorizationsTree = { ...localAuthorizationsTree }; + this.updateState(event.indexColumn, index, event.state, false); + this.$emit("delete-authorization", { + indexColumn: event.indexColumn, + child: index, + state: this.buildState(event.indexColumn, index).state, + authorizationsTree: this.localAuthorizationsTree, + authorizationScope: localAuthorizationsTree?.[event.type]?.[index], + }); } emitAddAuthorization(event, index) { let localAuthorizationsTree = this.localAuthorizationsTree; - var isEqual = event.isEqual + var isEqual = event.isEqual; if (isEqual.state == 1) { - localAuthorizationsTree = localAuthorizationsTree || {} - localAuthorizationsTree[event.indexColumn] = localAuthorizationsTree[event.indexColumn] || {} - localAuthorizationsTree[event.indexColumn][index] = localAuthorizationsTree[event.indexColumn][index] || {} - isEqual.auth.requiredAuthorization = this.requiredAuthorizationByindex[index] - localAuthorizationsTree[event.indexColumn][index] = isEqual.auth - isEqual = this.buildState(event.indexColumn, index) + localAuthorizationsTree = localAuthorizationsTree || {}; + localAuthorizationsTree[event.indexColumn] = localAuthorizationsTree[event.indexColumn] || {}; + localAuthorizationsTree[event.indexColumn][index] = + localAuthorizationsTree[event.indexColumn][index] || {}; + isEqual.auth.requiredAuthorization = this.requiredAuthorizationByindex[index]; + localAuthorizationsTree[event.indexColumn][index] = isEqual.auth; + isEqual = this.buildState(event.indexColumn, index); } else if (isEqual.state == -1) { - localAuthorizationsTree = localAuthorizationsTree || {} - localAuthorizationsTree[event.indexColumn] = localAuthorizationsTree[event.indexColumn] || {} - localAuthorizationsTree[event.indexColumn][index] = localAuthorizationsTree[event.indexColumn][index] || {} - localAuthorizationsTree[event.indexColumn][index] = event.authorizationsTree[event.indexColumn] + localAuthorizationsTree = localAuthorizationsTree || {}; + localAuthorizationsTree[event.indexColumn] = localAuthorizationsTree[event.indexColumn] || {}; + localAuthorizationsTree[event.indexColumn][index] = + localAuthorizationsTree[event.indexColumn][index] || {}; + localAuthorizationsTree[event.indexColumn][index] = + event.authorizationsTree[event.indexColumn]; } else { - delete localAuthorizationsTree?.[event.indexColumn]?.[index] - if (localAuthorizationsTree?.[event.indexColumn] && Object.keys(localAuthorizationsTree[event.indexColumn]).length) { - delete localAuthorizationsTree[event.indexColumn] + delete localAuthorizationsTree?.[event.indexColumn]?.[index]; + if ( + localAuthorizationsTree?.[event.indexColumn] && + Object.keys(localAuthorizationsTree[event.indexColumn]).length + ) { + delete localAuthorizationsTree[event.indexColumn]; } } - this.localAuthorizationsTree = localAuthorizationsTree ? {...(localAuthorizationsTree || {})} : localAuthorizationsTree; - this.updateState(event.indexColumn, index, event.state, false, this.localAuthorizationsTree) - this.authorizationByScope = this.localAuthorizationsTree[event.indexColumn] - this.$emit('add-authorization', - { - isEqual, - state: this.buildState(event.indexColumn, index).state, - child: index, - indexColumn: event.indexColumn, - authorizationsTree: this.localAuthorizationsTree, - authorizationScope: {...(localAuthorizationsTree?.[event.indexColumn]?.[index])} - } - ) + this.localAuthorizationsTree = localAuthorizationsTree + ? { ...(localAuthorizationsTree || {}) } + : localAuthorizationsTree; + this.updateState(event.indexColumn, index, event.state, false, this.localAuthorizationsTree); + this.authorizationByScope = this.localAuthorizationsTree[event.indexColumn]; + this.$emit("add-authorization", { + isEqual, + state: this.buildState(event.indexColumn, index).state, + child: index, + indexColumn: event.indexColumn, + authorizationsTree: this.localAuthorizationsTree, + authorizationScope: { ...localAuthorizationsTree?.[event.indexColumn]?.[index] }, + }); } - changeChildrenAuthorization(authorization ,onlyIndex) { + changeChildrenAuthorization(authorization, onlyIndex) { var returnAuthorizationTree = {}; this.getChildAuthorizationTable() - .filter(child => { - return (child.path.endsWith(onlyIndex)); - }) - .forEach(child => { - var authorizationTree = child.localAuthorizationsTree ? {...(child.localAuthorizationsTree || {})} : child.localAuthorizationsTree; - for (const index in authorizationTree[child.EXTRACTION] || {}) { - if (!authorization && authorizationTree?.[child.EXTRACTION]?.[index]) { - delete authorizationTree?.[child.EXTRACTION]?.[index] - } else { - authorizationTree[child.EXTRACTION] = authorizationTree[child.EXTRACTION] || {} - authorizationTree[child.EXTRACTION][index] = new Authorization(authorization, this.requiredAuthorizationByindex[index]) - } - returnAuthorizationTree[index] = authorizationTree[child.EXTRACTION][index] + .filter((child) => { + return child.path.endsWith(onlyIndex); + }) + .forEach((child) => { + var authorizationTree = child.localAuthorizationsTree + ? { ...(child.localAuthorizationsTree || {}) } + : child.localAuthorizationsTree; + for (const index in authorizationTree[child.EXTRACTION] || {}) { + if (!authorization && authorizationTree?.[child.EXTRACTION]?.[index]) { + delete authorizationTree?.[child.EXTRACTION]?.[index]; + } else { + authorizationTree[child.EXTRACTION] = authorizationTree[child.EXTRACTION] || {}; + authorizationTree[child.EXTRACTION][index] = new Authorization( + authorization, + this.requiredAuthorizationByindex[index] + ); } - child.localAuthorizationsTree = authorizationTree; - child.changeChildrenAuthorization(authorization, onlyIndex); - }) - return returnAuthorizationTree ? {...(returnAuthorizationTree || {})} : returnAuthorizationTree; + returnAuthorizationTree[index] = authorizationTree[child.EXTRACTION][index]; + } + child.localAuthorizationsTree = authorizationTree; + child.changeChildrenAuthorization(authorization, onlyIndex); + }); + return returnAuthorizationTree + ? { ...(returnAuthorizationTree || {}) } + : returnAuthorizationTree; } selectCheckbox(event, index, indexColumn, scope, fromOrTo) { - var eventType = 'add-authorization' - let localAuthorizationsTree = this.localAuthorizationsTree ? {...(this.localAuthorizationsTree || {})} : this.localAuthorizationsTree; - var actualState = this.states && this.states[indexColumn] && this.states[indexColumn][index] || 0 - if (event instanceof PointerEvent) { //cliock sur checkbox - this.states[indexColumn] || this.initStates() - var states, state - if (actualState == 1) { //je supprime l'authorization et eventuellement son contenant - delete localAuthorizationsTree?.[indexColumn]?.[index] - if (localAuthorizationsTree?.[indexColumn] && !Object.keys(localAuthorizationsTree[indexColumn]).length) { - delete localAuthorizationsTree?.[indexColumn] - delete this.authorizationByScope?.[index]?.[indexColumn] + var eventType = "add-authorization"; + let localAuthorizationsTree = this.localAuthorizationsTree + ? { ...(this.localAuthorizationsTree || {}) } + : this.localAuthorizationsTree; + var actualState = + (this.states && this.states[indexColumn] && this.states[indexColumn][index]) || 0; + if (event instanceof PointerEvent) { + //cliock sur checkbox + this.states[indexColumn] || this.initStates(); + var states, state; + if (actualState == 1) { + //je supprime l'authorization et eventuellement son contenant + delete localAuthorizationsTree?.[indexColumn]?.[index]; + if ( + localAuthorizationsTree?.[indexColumn] && + !Object.keys(localAuthorizationsTree[indexColumn]).length + ) { + delete localAuthorizationsTree?.[indexColumn]; + delete this.authorizationByScope?.[index]?.[indexColumn]; } - eventType = 'delete-authorization' + eventType = "delete-authorization"; state = 0; - } else { //création ou modification - localAuthorizationsTree[indexColumn] = localAuthorizationsTree?.[indexColumn] || {} - localAuthorizationsTree[indexColumn][index] = new Authorization([], this.requiredAuthorizationByindex[index], null, null); - var authorizationScope = {} + } else { + //création ou modification + localAuthorizationsTree[indexColumn] = localAuthorizationsTree?.[indexColumn] || {}; + localAuthorizationsTree[indexColumn][index] = new Authorization( + [], + this.requiredAuthorizationByindex[index], + null, + null + ); + var authorizationScope = {}; let id = scope.authorizationScope; authorizationScope[id] = scope.key; state = 1; @@ -506,12 +552,13 @@ export default class AuthorizationTable extends Vue { localAuthorizationsTree[indexColumn][index][fromOrTo] = event; } //si je veux restreindre les enfants je dois le faire après avoir défini le parent - this.changeChildrenAuthorization(localAuthorizationsTree?.[indexColumn]?.[index], index);//si je selectionne alors c'est cette authorization qui s'applique aux enfants (ils n'ont plus leur propre authorization + this.changeChildrenAuthorization(localAuthorizationsTree?.[indexColumn]?.[index], index); //si je selectionne alors c'est cette authorization qui s'applique aux enfants (ils n'ont plus leur propre authorization } - this.localAuthorizationsTree = localAuthorizationsTree ? {...(localAuthorizationsTree || {})} : localAuthorizationsTree; - this.updateState(indexColumn, index, state, false) - this.authorizationByScope = this.localAuthorizationsTree[indexColumn] - + this.localAuthorizationsTree = localAuthorizationsTree + ? { ...(localAuthorizationsTree || {}) } + : localAuthorizationsTree; + this.updateState(indexColumn, index, state, false); + this.authorizationByScope = this.localAuthorizationsTree[indexColumn]; states = this.states[indexColumn]; if (states) { @@ -527,26 +574,29 @@ export default class AuthorizationTable extends Vue { } else { state = 0; } - var isEqual = this.buildState(indexColumn, index) - this.$emit(eventType, - { - isEqual, - state, - child: index, - indexColumn, - authorizationsTree: localAuthorizationsTree ? {...(localAuthorizationsTree || {})} : localAuthorizationsTree, - authorizationScope: localAuthorizationsTree?.[indexColumn]?.[index] - } - ) + var isEqual = this.buildState(indexColumn, index); + this.$emit(eventType, { + isEqual, + state, + child: index, + indexColumn, + authorizationsTree: localAuthorizationsTree + ? { ...(localAuthorizationsTree || {}) } + : localAuthorizationsTree, + authorizationScope: localAuthorizationsTree?.[indexColumn]?.[index], + }); } buildState(indexColumn, index) { var isEqual = { equal: true, - state: 0 - } - var localAuthorizationsTree = {...this.localAuthorizationsTree} - if (!localAuthorizationsTree[indexColumn] || Object.keys(localAuthorizationsTree[indexColumn]).length == 0) { + state: 0, + }; + var localAuthorizationsTree = { ...this.localAuthorizationsTree }; + if ( + !localAuthorizationsTree[indexColumn] || + Object.keys(localAuthorizationsTree[indexColumn]).length == 0 + ) { isEqual.equal = true; isEqual.state = 0; isEqual.auth == null; @@ -557,21 +607,24 @@ export default class AuthorizationTable extends Vue { var auth = localAuthorizationsTree[indexColumn][reference]; if (isEqual.equal) { if (isEqual.auth) { - isEqual.equal = auth && - JSON.stringify(isEqual.auth.dataGroups) == JSON.stringify(auth.dataGroups) && - isEqual.auth.from?.toString() == auth.from?.toString() && - isEqual.auth.to?.toString() == auth.to?.toString() - + isEqual.equal = + auth && + JSON.stringify(isEqual.auth.dataGroups) == JSON.stringify(auth.dataGroups) && + isEqual.auth.from?.toString() == auth.from?.toString() && + isEqual.auth.to?.toString() == auth.to?.toString(); } } } - if (isEqual.equal && isEqual.auth) { //tous les noeuds sont semblables - isEqual.state = 1 + if (isEqual.equal && isEqual.auth) { + //tous les noeuds sont semblables + isEqual.state = 1; } else if (isEqual.auth) { - isEqual.state = -1 + isEqual.state = -1; } - this.localAuthorizationsTree = localAuthorizationsTree ? {...(localAuthorizationsTree || {})} : localAuthorizationsTree - return isEqual + this.localAuthorizationsTree = localAuthorizationsTree + ? { ...(localAuthorizationsTree || {}) } + : localAuthorizationsTree; + return isEqual; } } </script> diff --git a/ui2/src/model/ApplicationConfig.js b/ui2/src/model/ApplicationConfig.js index e79d0d47d..5b1f849a2 100644 --- a/ui2/src/model/ApplicationConfig.js +++ b/ui2/src/model/ApplicationConfig.js @@ -6,6 +6,6 @@ export class ApplicationConfig { constructor() { this.file = null; this.name = ""; - this.comment=""; + this.comment = ""; } } diff --git a/ui2/src/model/DataTypeAuthorization.js b/ui2/src/model/DataTypeAuthorization.js index 3596a9426..376c1e8aa 100644 --- a/ui2/src/model/DataTypeAuthorization.js +++ b/ui2/src/model/DataTypeAuthorization.js @@ -3,4 +3,4 @@ export class DataTypeAuthorization { applicationNameOrId; dataType; authorizations; -} \ No newline at end of file +} diff --git a/ui2/src/model/authorization/Authorization.js b/ui2/src/model/authorization/Authorization.js index 097afbf6f..ea4bb6070 100644 --- a/ui2/src/model/authorization/Authorization.js +++ b/ui2/src/model/authorization/Authorization.js @@ -1,46 +1,40 @@ export class Authorization { - requiredAuthorization = {} - dataGroups = []; - from = null; - to = null; + requiredAuthorization = {}; + dataGroups = []; + from = null; + to = null; - constructor(datagroupsOrAuthorization, requiredAuthorization, from, to) { - if (typeof datagroupsOrAuthorization == "object") { - Object.keys(this).forEach( - (key) => - (this[key] = datagroupsOrAuthorization[key] - ? datagroupsOrAuthorization[key] - : null) - ); - this.requiredAuthorization = requiredAuthorization - } else { - this.dataGroups = [...(datagroupsOrAuthorization || [])]; - this.from = from; - this.to = to; - this.requiredAuthorization = requiredAuthorization - } + constructor(datagroupsOrAuthorization, requiredAuthorization, from, to) { + if (typeof datagroupsOrAuthorization == "object") { + Object.keys(this).forEach( + (key) => + (this[key] = datagroupsOrAuthorization[key] ? datagroupsOrAuthorization[key] : null) + ); + this.requiredAuthorization = requiredAuthorization; + } else { + this.dataGroups = [...(datagroupsOrAuthorization || [])]; + this.from = from; + this.to = to; + this.requiredAuthorization = requiredAuthorization; } + } - parse() { - return { - requiredauthorizations: this.requiredAuthorization, - dataGroup:(this.dataGroups || []).map(dataGroups => dataGroups.id), - intervalDates: { - fromDay: this.parseDate(this.from), - toDay: this.parseDate((this.to)), - } - } - } + parse() { + return { + requiredauthorizations: this.requiredAuthorization, + dataGroup: (this.dataGroups || []).map((dataGroups) => dataGroups.id), + intervalDates: { + fromDay: this.parseDate(this.from), + toDay: this.parseDate(this.to), + }, + }; + } - parseDate(date) { - let parsedDate = null; - if (date) { - parsedDate = [ - date.getFullYear(), - date.getMonth() + 1, - date.getDate(), - ]; - } - return parsedDate + parseDate(date) { + let parsedDate = null; + if (date) { + parsedDate = [date.getFullYear(), date.getMonth() + 1, date.getDate()]; } + return parsedDate; + } } diff --git a/ui2/src/services/InternationalisationService.js b/ui2/src/services/InternationalisationService.js index b6459c659..39754faf9 100644 --- a/ui2/src/services/InternationalisationService.js +++ b/ui2/src/services/InternationalisationService.js @@ -81,7 +81,9 @@ export class InternationalisationService extends Fetcher { } localeDataTypeIdName(application, datatype) { - if (application?.internationalization?.dataTypes?.[datatype.id]?.internationalizationName != null) { + if ( + application?.internationalization?.dataTypes?.[datatype.id]?.internationalizationName != null + ) { return application.internationalization.dataTypes[datatype.id].internationalizationName[ localStorage.getItem(LOCAL_STORAGE_LANG) ]; @@ -156,4 +158,4 @@ export class InternationalisationService extends Fetcher { } return refs.references; } -} \ No newline at end of file +} diff --git a/ui2/src/views/application/ApplicationCreationView.vue b/ui2/src/views/application/ApplicationCreationView.vue index 6aa338e18..8eb798d96 100644 --- a/ui2/src/views/application/ApplicationCreationView.vue +++ b/ui2/src/views/application/ApplicationCreationView.vue @@ -144,4 +144,4 @@ export default class ApplicationCreationView extends Vue { } } } -</script> \ No newline at end of file +</script> diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue index bf7085c2d..211a72fd8 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationInfoView.vue @@ -27,10 +27,10 @@ class="mb-4" > <b-select - v-model="usersToAuthorize" - :placeholder="$t('dataTypeAuthorizations.users-placeholder')" - expanded - multiple + v-model="usersToAuthorize" + :placeholder="$t('dataTypeAuthorizations.users-placeholder')" + expanded + multiple > <option v-for="user in users" :key="user.id" :value="user.id"> {{ user.label }} @@ -39,31 +39,30 @@ </b-field> <b-field - :label="$t('dataTypeAuthorizations.name')" - :message="errors[0]" - :type="{ + :label="$t('dataTypeAuthorizations.name')" + :message="errors[0]" + :type="{ 'is-danger': errors && errors.length > 0, 'is-success': valid, }" - class="mb-4" + class="mb-4" > - <b-input - v-model="name" - /> + <b-input v-model="name" /> </b-field> </ValidationProvider> <AuthorizationTable - v-if="dataGroups && authReferences && columnsVisible && authReferences[0]" - :authReference="authReferences[0]" - :authorization-scopes="authorizationScopes" - :authorizations-tree="authorizationsTree" - :columnsVisible="columnsVisible" - :dataGroups="dataGroups" - :remaining-option="authReferences.slice && authReferences.slice(1,authReferences.length)" - :required-authorizations="{}" - class="rows" - @add-authorization="emitUpdateAuthorization($event)" - @delete-authorization="emitUpdateAuthorization($event)"> + v-if="dataGroups && authReferences && columnsVisible && authReferences[0]" + :authReference="authReferences[0]" + :authorization-scopes="authorizationScopes" + :authorizations-tree="authorizationsTree" + :columnsVisible="columnsVisible" + :dataGroups="dataGroups" + :remaining-option="authReferences.slice && authReferences.slice(1, authReferences.length)" + :required-authorizations="{}" + class="rows" + @add-authorization="emitUpdateAuthorization($event)" + @delete-authorization="emitUpdateAuthorization($event)" + > <div class="row"> <div class="columns"> <b-field @@ -131,7 +130,7 @@ export default class DataTypeAuthorizationInfoView extends Vue { alertService = AlertService.INSTANCE; applicationService = ApplicationService.INSTANCE; userPreferencesService = UserPreferencesService.INSTANCE; - authorizationsTree = {} + authorizationsTree = {}; checkbox = false; authorizations = []; users = []; @@ -164,7 +163,7 @@ export default class DataTypeAuthorizationInfoView extends Vue { endDate = null; applications = []; configuration = {}; - ToAuthorize + ToAuthorize; authReferences = {}; authorizationsToSave = {}; @@ -200,8 +199,7 @@ export default class DataTypeAuthorizationInfoView extends Vue { ]; } - mounted() { - } + mounted() {} showDetail(parent) { for (const child in parent) { @@ -229,8 +227,8 @@ export default class DataTypeAuthorizationInfoView extends Vue { ), }; this.authorizations = await this.authorizationService.getDataAuthorizations( - this.applicationName, - this.dataTypeId + this.applicationName, + this.dataTypeId ); this.authorizations = this.configuration.authorization.authorizationScopes; const grantableInfos = await this.authorizationService.getAuthorizationGrantableInfos( @@ -242,7 +240,7 @@ export default class DataTypeAuthorizationInfoView extends Vue { dataGroups: this.dataGroups, users: this.users, } = grantableInfos); - grantableInfos.authorizationScopes.reverse() + grantableInfos.authorizationScopes.reverse(); // this.authorizationScopes[0].options[0].children[0].children.push({ // children: [], // id: "toto", @@ -431,22 +429,26 @@ export default class DataTypeAuthorizationInfoView extends Vue { for (const type in event.authorizationsTree) { authorizationsToSave[type] = this.extractAuthorizations(event.authorizationsTree[type]); } - this.authorizationsToSave = {...authorizationsToSave} - console.log(JSON.stringify(this.authorizationsToSave)) + this.authorizationsToSave = { ...authorizationsToSave }; + console.log(JSON.stringify(this.authorizationsToSave)); } extractAuthorizations(authorizationTree) { - var authorizationArray = [] + var authorizationArray = []; if (!authorizationTree || Object.keys(authorizationTree).length == 0) { return authorizationArray; } for (const key in authorizationTree) { - var treeOrAuthorization = authorizationTree[key] - authorizationArray = [...authorizationArray, ...((treeOrAuthorization instanceof Authorization) ? [treeOrAuthorization.parse()] : this.extractAuthorizations(treeOrAuthorization))] + var treeOrAuthorization = authorizationTree[key]; + authorizationArray = [ + ...authorizationArray, + ...(treeOrAuthorization instanceof Authorization + ? [treeOrAuthorization.parse()] + : this.extractAuthorizations(treeOrAuthorization)), + ]; } - return authorizationArray + return authorizationArray; } - } </script> diff --git a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue index 0c1691e40..9001497cd 100644 --- a/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue +++ b/ui2/src/views/authorizations/DataTypeAuthorizationsView.vue @@ -25,16 +25,16 @@ </div> <b-table - v-if="authorizations" - :data="authorizations" - :isFocusable="true" - :isHoverable="true" - :paginated="true" - :per-page="15" - :sticky-header="true" - :striped="true" - class="row" - height="100%" + v-if="authorizations" + :data="authorizations" + :isFocusable="true" + :isHoverable="true" + :paginated="true" + :per-page="15" + :sticky-header="true" + :striped="true" + class="row" + height="100%" > <!--b-table-column v-slot="props" @@ -47,31 +47,31 @@ </b-table-column--> <b-table-column - v-slot="props" - :label="$t('dataTypeAuthorizations.name')" - b-table-column - field="name" - sortable + v-slot="props" + :label="$t('dataTypeAuthorizations.name')" + b-table-column + field="name" + sortable > {{ props.row.name }} </b-table-column> <b-table-column - v-slot="props" - :label="$t('dataTypeAuthorizations.roles')" - b-table-column - field="authorizations" - sortable + v-slot="props" + :label="$t('dataTypeAuthorizations.roles')" + b-table-column + field="authorizations" + sortable > - {{Object.keys( props.row.authorizations || {} ) }} + {{ Object.keys(props.row.authorizations || {}) }} </b-table-column> <b-table-column - v-slot="props" - :label="$t('dataTypeAuthorizations.users')" - b-table-column - field="users" - sortable + v-slot="props" + :label="$t('dataTypeAuthorizations.users')" + b-table-column + field="users" + sortable > - {{ props.row.users.map(use=>use.login) }} + {{ props.row.users.map((use) => use.login) }} </b-table-column> <b-table-column v-slot="props" :label="$t('dataTypeAuthorizations.actions')" b-table-column> <b-button @@ -119,7 +119,8 @@ import { ApplicationResult } from "@/model/ApplicationResult"; }) export default class DataTypeAuthorizationsView extends Vue { @Prop() dataTypeId; - @Prop() applicationName;toList + @Prop() applicationName; + toList; authorizationService = AuthorizationService.INSTANCE; internationalisationService = InternationalisationService.INSTANCE; @@ -191,7 +192,7 @@ export default class DataTypeAuthorizationsView extends Vue { this.scopes = Object.keys(this.authorizations[0].authorizations); } } catch (error) { - this.alertService.toastServerError + this.alertService.toastServerError; this.authorizationByUser = this.authorizations.reduce((acc, auth) => { var user = auth.user; var userAuth = acc[user] || []; -- GitLab From 73d38242217e7106ad7173f03610136d8bca43f1 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Mon, 7 Feb 2022 17:40:52 +0100 Subject: [PATCH 15/24] optimisation de l'affichage de la page repository --- ui2/src/main.js | 4 +++- ui2/src/style/_common.scss | 3 +++ ui2/src/views/datatype/DataTypesRepositoryView.vue | 8 +++++--- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/ui2/src/main.js b/ui2/src/main.js index f13477fe3..6df3a5912 100644 --- a/ui2/src/main.js +++ b/ui2/src/main.js @@ -52,6 +52,7 @@ import { faSortUp, faSortDown, faArchive, + faTimesCircle, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; library.add( @@ -102,7 +103,8 @@ library.add( faSortAmountDown, faSortDown, faSortUp, - faArchive + faArchive, + faTimesCircle ); Vue.component("vue-fontawesome", FontAwesomeIcon); diff --git a/ui2/src/style/_common.scss b/ui2/src/style/_common.scss index 1b05d0ae7..10b3ead84 100644 --- a/ui2/src/style/_common.scss +++ b/ui2/src/style/_common.scss @@ -158,6 +158,9 @@ a.dropdown-item.is-active, .dropdown .dropdown-menu .has-link a.is-active, butto .button.is-danger.is-light { color: $danger; } +.button.is-primary.is-light { + color: $dark; +} .textarea:not([rows]) { min-height: 4em; diff --git a/ui2/src/views/datatype/DataTypesRepositoryView.vue b/ui2/src/views/datatype/DataTypesRepositoryView.vue index 5b1c98b3c..d0aa9e2d1 100644 --- a/ui2/src/views/datatype/DataTypesRepositoryView.vue +++ b/ui2/src/views/datatype/DataTypesRepositoryView.vue @@ -229,16 +229,18 @@ size="is-medium" type="is-primary is-light" @click="publish(dataset, !dataset.params.published)" + style="height:1.5em; background-color: transparent; font-size: 1.45rem;" /> </b-field> </td> <td> <b-field> <b-button - icon-right="trash-alt" + icon-right="times-circle" size="is-medium" type="is-danger is-light" @click="remove(dataset, dataset.params.published)" + style="height:1.5em; background-color: transparent; font-size: 1.45rem;" /> </b-field> </td> @@ -649,8 +651,8 @@ export default class DataTypesRepositoryView extends Vue { } } .dropdown-content { - margin-left: 20px; - margin-right: -20px; + margin-left: 10px; + margin-right: -30px; } table.datasetsPanel { width: 50%; -- GitLab From 04552a9003489ea4de05e4625c066fbbfb611e2f Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Mon, 7 Feb 2022 18:01:37 +0100 Subject: [PATCH 16/24] ajout bouton modifier application avec traduction --- ui2/src/locales/en.json | 3 ++- ui2/src/locales/fr.json | 3 ++- ui2/src/main.js | 4 +++- ui2/src/services/rest/ApplicationService.js | 6 ++++++ .../views/application/ApplicationCreationView.vue | 14 ++++++++++++++ ui2/src/views/datatype/DataTypesRepositoryView.vue | 4 ++-- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json index 17b6232cb..dc3cf3867 100644 --- a/ui2/src/locales/en.json +++ b/ui2/src/locales/en.json @@ -82,7 +82,8 @@ "trierA_z":"Name A - z", "trierZ_a":"Name Z - a", "trierRecent":"Recent date", - "filter":"Filter by" + "filter":"Filter by", + "change": "Edit app" }, "errors":{ "emptyFile":"File is empty", diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index 4a58ea8b0..39b291698 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -82,7 +82,8 @@ "trierA_z": "Nom A - z", "trierZ_a": "Nom Z - a", "trierRecent": "Date récente", - "filter": "Filtrer" + "filter": "Filtrer", + "change": "Modifier l'application" }, "errors": { "emptyFile": "Le fichier est vide", diff --git a/ui2/src/main.js b/ui2/src/main.js index 6df3a5912..02a15e11e 100644 --- a/ui2/src/main.js +++ b/ui2/src/main.js @@ -53,6 +53,7 @@ import { faSortDown, faArchive, faTimesCircle, + faEdit, } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/vue-fontawesome"; library.add( @@ -104,7 +105,8 @@ library.add( faSortDown, faSortUp, faArchive, - faTimesCircle + faTimesCircle, + faEdit ); Vue.component("vue-fontawesome", FontAwesomeIcon); diff --git a/ui2/src/services/rest/ApplicationService.js b/ui2/src/services/rest/ApplicationService.js index 14b33922e..75038da28 100644 --- a/ui2/src/services/rest/ApplicationService.js +++ b/ui2/src/services/rest/ApplicationService.js @@ -32,6 +32,12 @@ export class ApplicationService extends Fetcher { file: applicationConfig.file, }); } + async changeApplication(applicationConfig, comment) { + return this.post("applications/" + applicationConfig.name, { + file: applicationConfig.file, + comment: comment, + }); + } async getValidateConfiguration() { return this.post("validate-configuration"); diff --git a/ui2/src/views/application/ApplicationCreationView.vue b/ui2/src/views/application/ApplicationCreationView.vue index 8eb798d96..6cffe120c 100644 --- a/ui2/src/views/application/ApplicationCreationView.vue +++ b/ui2/src/views/application/ApplicationCreationView.vue @@ -64,6 +64,9 @@ <b-button type="is-light" @click="handleSubmit(testApplication)" icon-left="vial"> {{ $t("applications.test") }} </b-button> + <b-button type="is-warning" @click="handleSubmit(changeApplication)" icon-left="edit"> + {{ $t("applications.change") }} + </b-button> <b-button type="is-primary" @click="handleSubmit(createApplication)" icon-left="plus"> {{ $t("applications.create") }} </b-button> @@ -120,6 +123,17 @@ export default class ApplicationCreationView extends Vue { } } + async changeApplication() { + this.errorsMessages = []; + try { + await this.applicationService.changeApplication(this.applicationConfig); + this.alertService.toastSuccess(this.$t("alert.application-validate-success")); + this.$router.push("/applications"); + } catch (error) { + this.checkMessageErrors(error); + } + } + async testApplication() { this.errorsMessages = []; try { diff --git a/ui2/src/views/datatype/DataTypesRepositoryView.vue b/ui2/src/views/datatype/DataTypesRepositoryView.vue index d0aa9e2d1..cdab0a130 100644 --- a/ui2/src/views/datatype/DataTypesRepositoryView.vue +++ b/ui2/src/views/datatype/DataTypesRepositoryView.vue @@ -229,7 +229,7 @@ size="is-medium" type="is-primary is-light" @click="publish(dataset, !dataset.params.published)" - style="height:1.5em; background-color: transparent; font-size: 1.45rem;" + style="height: 1.5em; background-color: transparent; font-size: 1.45rem" /> </b-field> </td> @@ -240,7 +240,7 @@ size="is-medium" type="is-danger is-light" @click="remove(dataset, dataset.params.published)" - style="height:1.5em; background-color: transparent; font-size: 1.45rem;" + style="height: 1.5em; background-color: transparent; font-size: 1.45rem" /> </b-field> </td> -- GitLab From bf4ce07d246578a6b7fb4044f9a60ef34e4a7341 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Mon, 7 Feb 2022 18:47:28 +0100 Subject: [PATCH 17/24] ajout favicon Inrae --- ui/public/favicon.ico | Bin 1150 -> 7140 bytes ui2/public/INRAE_LOGO_AE.svg | 64 +++++++++++++++++++++++++++++++++++ ui2/public/index.html | 1 + 3 files changed, 65 insertions(+) create mode 100644 ui2/public/INRAE_LOGO_AE.svg diff --git a/ui/public/favicon.ico b/ui/public/favicon.ico index c7b9a43c8cd16d0b434adaf513fcacb340809a11..b425000c935b18eb122c24e8765fa60d87a9a375 100644 GIT binary patch literal 7140 zcmV<A8yn<_P)<h;3K|Lk000e1NJLTq007Sb004yu1^@s6{6ef%00009a7bBm000id z000id0mpBsWB>pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H18*xcQ zK~#90?VWpgTy>epKku0&)26k>mbyr_7bry$Sfo%83ksA)EsE$8NHR%gS}?n)^@@+| z0*>4iLB+c65^Xz|%%o+w3Op=wv4{l<1!V<PE>$X*Vujw5Hj~VG_m4?0X)?)N&hMPb zG@s|;0g`jx@0&L7{LcCP-iuHS@%RU^eYF5&>>Y^JLsdrc`L+O1UA>FCx}8PY9>p8l z+N$xtYc^yKN0^Dq41}pDQ-Fhk$-tPtf5z@}=XRhM*bHm|c?Z~lupZ@ggqM-b3p6)x zbonPTjud>ys#R62f8#aakUa0$24p~cLALjOtrJN1{rBd+e|MvjAk?_BV4p@Qj3rC_ zOg#8_R8IzbF32&!Y@php>N~)*z#4=#ka>cZmUj&58K>~ziMB5Qxz~U%0DmGFILeUi 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 a8~i`=*_|Xs)Fb%-0000<MNUMnLSTZ6F4YnM literal 1150 zcmchVOGsN$5QZm2NTI$erQpKHrdQX(jn+pVxKN`Ng)RzW5+8_2Xb@Y)Dkd6tq9V8u z3WAh^C@KZ1kA;tohzs}b3NC_*QmUXr$oP*rH(2mdT{z*(KX=aj=bX$9kqMvFRKj;Q zwI&d~A);J>5-PDega~WT5us%#Dc(Y}C4WpP?+fS;FaZ*z_CFzgiW=w{I02=q_TUz( z?=^H2uwoIK1n%|Ay21~QgjV1emYtWttJdz^L#=DjJ@Ex*9UPc*7<=rZo*_NAh4PxA zqkso~Ioa1y$e+3kIkXi29YNLi&lW}vY6C}ut4{8ou(7w=$_=$v{yJ$h?y!&bJfq*( zL_NQRF37$6e>%9erGV?p^lRFD?|5J_eupXaS;QluyrOmBT>PJhirMYb*<GPW1y7aE zons}&9sZ*4{SBXZ)4`5-`(_;6T$j)8a`;{PI@prD#aqC^`S>i?(4Tf=j~?VvnUlY_ zDCVuuk3E&T9aP~Cr-0i-MaKUjf_|U!=R&t}_CfD=d${p~HH`BPaqb9aXT}UI$iGRg z>0^GlZ`vM4?;$*LhfI(RG|XK4GF+@-W*W}YJT5&2N_ZyZuaM_Ry=%PWx>r0P(Rc?> jRc4}SfGA>*agjwN{7E7DEm(*)%rSx{B0<6wBoglxJAy|R diff --git a/ui2/public/INRAE_LOGO_AE.svg b/ui2/public/INRAE_LOGO_AE.svg new file mode 100644 index 000000000..00036e118 --- /dev/null +++ b/ui2/public/INRAE_LOGO_AE.svg @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + version="1.1" + id="svg2" + width="225" + height="225" + viewBox="0 0 225 225" + sodipodi:docname="INRAE_LOGO_AE.svg" + inkscape:version="1.0.2 (e86c870879, 2021-01-15)" + inkscape:export-filename="/home/lvarloteaux/Documents/INRAE_LOGO_AE.png" + inkscape:export-xdpi="96" + inkscape:export-ydpi="96"> + <metadata + id="metadata8"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs6" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="2507" + inkscape:window-height="1376" + id="namedview4" + showgrid="false" + inkscape:zoom="3.8044444" + inkscape:cx="98.432185" + inkscape:cy="112.5" + inkscape:window-x="1973" + inkscape:window-y="27" + inkscape:window-maximized="1" + inkscape:current-layer="g10" + inkscape:document-rotation="0" /> + <g + inkscape:groupmode="layer" + inkscape:label="Image" + id="g10"> + <path + style="fill:#00a0a4" + d="m 126.104,205.45352 c -11.39016,-2.47085 -22.53544,-8.80851 -31.904123,-18.14198 l -8.300123,-8.26893 -4.949877,1.06156 c -5.927274,1.27117 -5.839077,-0.0419 -0.878796,13.08392 2.239094,5.92508 3.787536,11.23166 3.440983,11.79239 -0.924614,1.49606 -24.19269,1.34529 -25.124738,-0.16279 -0.40189,-0.65027 -2.403858,-5.93777 -4.448818,-11.75 L 50.220399,182.5 25.1102,182.23283 0,181.96566 -0.01945396,146.23283 -0.03890793,110.5 6.6559798,92.71889 13.350867,74.937779 24.38776,75.21889 35.424652,75.5 l 4.237547,11 c 2.330652,6.05 9.595996,25.175 16.145211,42.5 11.285899,29.85522 12.032585,31.48533 14.300126,31.21899 5.69501,-0.66892 7.078683,-1.29133 6.459258,-2.90552 C 70.994026,142.79107 75.579576,118.09902 86.906706,101.6356 107.35813,71.910468 155.25944,64.869388 181.75889,87.693143 L 185.01778,90.5 181.58651,98.311179 C 168.80187,127.415 145.81821,151.98407 116.94554,167.41107 l -8.13225,4.34516 5.72415,4.09897 c 28.44161,20.36659 64.73456,4.68445 70.81123,-30.59745 1.45171,-8.42882 14.39207,-32.15405 16.74463,-30.70009 2.68252,1.65789 5.40506,15.17516 5.32582,26.44234 -0.30019,42.68245 -39.27286,73.5737 -81.31512,64.45352 z M 42.541185,160.25 c -0.370036,-0.9625 -4.565169,-12.88325 -9.322517,-26.49056 -4.757349,-13.60731 -8.959735,-24.18231 -9.338637,-23.5 C 23.081573,111.69727 6,160.58632 6,161.43375 6,161.74519 14.373145,162 24.606989,162 c 17.310577,0 18.560113,-0.12193 17.934196,-1.75 z M 108,147.88926 c 19.29833,-10.505 37.72215,-27.93879 46.42667,-43.93186 l 2.69908,-4.959093 -2.81288,-1.192044 C 152.76579,97.150638 146.775,96.588519 141,96.557109 116.12763,96.421829 96.970159,115.25354 97.013655,139.79549 97.038201,153.64524 97.20295,153.76661 108,147.88926 Z" + id="path14" /> + </g> +</svg> diff --git a/ui2/public/index.html b/ui2/public/index.html index 82a14a087..32142545a 100644 --- a/ui2/public/index.html +++ b/ui2/public/index.html @@ -3,6 +3,7 @@ <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <link rel="icon" type="image/svg+xml" href="INRAE_LOGO_AE.svg"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <title>si-ore</title> </head> -- GitLab From ea105501638f1e658deb436a8ca8fbba5d8a1d89 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Tue, 8 Feb 2022 10:19:02 +0100 Subject: [PATCH 18/24] bouton modifier yaml fonctionne --- ui2/src/locales/en.json | 1 + ui2/src/locales/fr.json | 1 + ui2/src/services/rest/ApplicationService.js | 4 ++-- ui2/src/views/application/ApplicationCreationView.vue | 8 ++++---- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json index dc3cf3867..ac042aabe 100644 --- a/ui2/src/locales/en.json +++ b/ui2/src/locales/en.json @@ -38,6 +38,7 @@ "user-unknown":"Unknown user", "application-creation-success":"The app has been created!", "application-validate-success":"The YAML file is valid!", + "application-edit-success": "The YAML file is update !", "warning":"Warning !", "reference-deletion-msg":"You're about to delete the reference : {label}. Are you sure ?", "delete":"Delete", diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index 39b291698..5b898b466 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -38,6 +38,7 @@ "user-unknown": "Identifiants inconnus", "application-creation-success": "L'application a été créée !", "application-validate-success": "Le fichier YAML est valide !", + "application-edit-success": "Le fichier YAML est mis à jours !", "warning": "Attention !", "reference-deletion-msg": "Vous allez supprimer le référentiel : {label}. Êtes-vous sûr ?", "delete": "Supprimer", diff --git a/ui2/src/services/rest/ApplicationService.js b/ui2/src/services/rest/ApplicationService.js index 75038da28..c199a377e 100644 --- a/ui2/src/services/rest/ApplicationService.js +++ b/ui2/src/services/rest/ApplicationService.js @@ -32,8 +32,8 @@ export class ApplicationService extends Fetcher { file: applicationConfig.file, }); } - async changeApplication(applicationConfig, comment) { - return this.post("applications/" + applicationConfig.name, { + async changeConfiguration(applicationConfig, comment) { + return this.post("/applications/" + applicationConfig.name + "/configuration", { file: applicationConfig.file, comment: comment, }); diff --git a/ui2/src/views/application/ApplicationCreationView.vue b/ui2/src/views/application/ApplicationCreationView.vue index 6cffe120c..00581b88d 100644 --- a/ui2/src/views/application/ApplicationCreationView.vue +++ b/ui2/src/views/application/ApplicationCreationView.vue @@ -64,7 +64,7 @@ <b-button type="is-light" @click="handleSubmit(testApplication)" icon-left="vial"> {{ $t("applications.test") }} </b-button> - <b-button type="is-warning" @click="handleSubmit(changeApplication)" icon-left="edit"> + <b-button type="is-warning" @click="handleSubmit(changeConfiguration)" icon-left="edit"> {{ $t("applications.change") }} </b-button> <b-button type="is-primary" @click="handleSubmit(createApplication)" icon-left="plus"> @@ -123,11 +123,11 @@ export default class ApplicationCreationView extends Vue { } } - async changeApplication() { + async changeConfiguration() { this.errorsMessages = []; try { - await this.applicationService.changeApplication(this.applicationConfig); - this.alertService.toastSuccess(this.$t("alert.application-validate-success")); + await this.applicationService.changeConfiguration(this.applicationConfig, this.comment); + this.alertService.toastSuccess(this.$t("alert.application-edit-success")); this.$router.push("/applications"); } catch (error) { this.checkMessageErrors(error); -- GitLab From 86e2ac4c334aa5625c2cdf6b0b22d56190b4d8e6 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Tue, 8 Feb 2022 11:56:41 +0100 Subject: [PATCH 19/24] ajout logo anaEE --- ui2/src/assets/logo-AnaEE-france.png | Bin 0 -> 17769 bytes ui2/src/views/common/MenuView.vue | 9 +++++++++ 2 files changed, 9 insertions(+) create mode 100644 ui2/src/assets/logo-AnaEE-france.png diff --git a/ui2/src/assets/logo-AnaEE-france.png b/ui2/src/assets/logo-AnaEE-france.png new file mode 100644 index 0000000000000000000000000000000000000000..8be986f0cf6f76245a18b0a102a19b81287742e2 GIT binary patch literal 17769 zcmXUsbyQT}^MIsuh=g>kARx`sA)&+~T>{b_OE*Y^q#y_^Ah}9+cjppIcQ0K_=g-gg z{N5k)=FEK)_no<O=ggfO^%<mq|B~t@3JMDTr;oDgC@3!=C@82Y*f0K3n5_LuP*Bhw zKPzj>J^$~yySqaoFAfh65eG-xh`pty<+-`}@$repm5r0L^Qo!nwJpTM!^73p^~uTE z*!VbXY~uRn7B)8a^!z*u8+&?setdemd;ABUZXO;-M`0tQu<QGWtNVw`yZhmhQRLnI z#qIss?cM3k?a=VZ@zw4A<;}q0(C)=G;^KM-dDTBKxP5-Ld49QZcDZ$Sxp98Ies-~a zez|^nxpsQFes<a0*Wb`Nkd#~I_$}Ty>SuOwZQI~vbxYso@nYM2Xh>pK(Xig{A<c=I zrSt3kj>Yo)LA|08wdk~bXmxAlnE4E1?9Zs_=vLXy!)4}x+T|^>r?;<e(qrZH@>eft z@#JFh<aF-nXyxRheQl(4wlROAB(*!*sm`KcD8;tc%D76;pyHEise)Xwly9S1cXv<n z*dLP$@uXh+se_I0tpP4|UxS;?yc@NgDn+a+#WYGKT<SirpIt|{nMJniMmG!iHwf9( zn6IAvqbP(o2_<zacT8#bZErMB=y=yja6_d#ySgi2x&bX(ZZ#snKQh+UA1fAyJQ^M2 zIus*XWNm7`D3$3*7HgsxsG#J^7*-ie7m1)`$)G@_QGS15EszoXtE^L@_pM0=B~K}< zPerX%puN454hot-zCg)UoH#g7?J;2gqXFAHn?1gsIYJhX`6YDePah)lM?6~F+Vh9a z1L`F!CVoTu^dZw#BfDp&)s{iS1%2D6otq~ut!)i!J59}P+LcBo%?T(OGAQZ)RDVSI z{fV#mGjEXwN}3ePFKLutGO;7|DCu%2sZ!c?zH*gLDA}J-eo30wgIhOF%9jqI3+pH; z(h_CnjZMuptH&8rEAeAPkt4l!<pK%giy82KscK)8A~S&scaFcGQ3}*iaz0y>@}i_j z)i*So6m!?rHyQomJV5U17QCyiYpAKMud1%iDr@ph%+<>l3$F)<{`%9<J>pwwVNsw2 zF40jpvWZLkQ(0Y8QCVG9QCV7ESzK0LR8nf`8C_IT<{XrmwtaT<Z#|&7se?40QMT*3 z6;V)RC_l+cYkHv`&RPU70m)EIO3isF`V#Jq-ChKCJuGor>^FZ9|D1sK08m7w2L%D6 zNa;Z+Jvc-FlnwMP;{RYIDGC_v{C}_qCkTiN{vX^xmx36g*6@S_eNia*^boCtXG$yg z=7xglz;6+}^DK0MB|U_}<uL0F^y8Tr6^17XW55SUhssVtxWE&}i3<}*DkV!j3T<ZX zKlG!ANo;U%W)#v1)#1|_W`X5D<-=tAt_(0qv@J3x?ImSMJI*gVJ-Qs_SYRMcMd+nT zooysC6{RWVvT|rL5^05^F$;JLdVZ_AfgWR=UC=f#`w6l(om`<)+V&0jt-yY)_GEP_ zt6U7!adWhH^|-j01e8t}ztCG$aW6VUdGCJEPnCJMP#i<*3|_9!O7>I8d`Y3qWf0hs zGNyt0VZvs$X{+ZzvqLJ5?YNa)z5#?0V|L{`W=mSCf!ghCg)mQrqG9HA&r1$&|Lt>s zGwbUl;oj=`CgKj!(dUY8LSF3J!@8<-lhS%W4$_M10=DVM(!ZhL=nuwFv&HlA34Fe8 z64pwRmJg4`^E_I`&3~E|`e3?&^Qe3oNXll?cVMG%QMrGpnSu*WmW%a}RNGj9A9GWH zGMD!zi}M!4emvD^92+K!Nc&ee*7vM=FLNYwn}uz!aJw7&o|QlAmR~EvY2dm!(!vWK z^vcR3887xIX1_30;QNoiK%IfRAp^oXWPl67T@Bpe@gR@kuMK5`f_LJ7)*OFl9QbJ4 zA<H~6GvAVC*9SS$Kc<`LFVGA)-}gBi_K%kuKyxj)9t+*`R832_RYu!z-7g^exJA<? z)!i117s99o#Fii(yHDYLzuTGvVkItp+=ETYv)UVTw%=&VJJjj2%4EMQ*3SG*W|DT} zC-_2u*s~$U^qO+bB{SzsCT6i3;E!1y-om=C6xmi>9f`I!Vfybh^^;h`JW!Zpy0ovL z+r4J|y^ld=DW7$;U8%(ilWG&l0#-fOc>8oNyt-;1Jp5L*7tdT~{I{BNxx|j$?U5$a z=+_7Uc9#1f3l(~^=@TAN!VD~N2ZgHtV&B*Cx``V4I@B8NnO#~p@?7(~T`YUPx1v<- zrQgxe^n5^iGtnSOx3EDq5KWdrx!wRco?8Nt$s8}c<?3SKN_s7ek&+D6$L!O#{?o0( zV`mUMYQAqTG)mt`jG1A~j-c#=SmVgDnOXfzcA;2Wm{Vp?EFxXtJ2N9wqj+HaiC}&E zars3qS>gPk+z!P8cY{+4$a<8-LEw4*<9sh<`Aww0kG8tCNKgtlbWbFQ#V9gu2S+>0 z?NSq_`+ko7yR`2Y(Z3!0A0bI72eS9(^|-d*P2;d+1=4R#1%1c4S|>!B{c|620Asn; zN9d8ZU+mH!%(&Oyie(wlz}P4eV#+){YF<^FA7iZU&|;y{zZ`KLGxk@+YHi)?crt(v z;bTYp*Mse5{8@@r6q0lmG1B-UHZG-qi`^jA+;YTW_>meglE~n`xM~!wn4^OOb7%QV zU@BX5_E<1TMvM*c3zeaO9OS}D4f{)m;E=LKhW(*{c}<QcVcQ(A($06`dxOIncjQvF zO8GW#q&^B??~4eqY+k_eK2YI0H_nvtW&IQIEAxxhQ5U7$1|HuVu*HOG?Y^=&eLfjq zen7lx7|@DMJA;?~*h>4+S}F3U81rle*?0JGLO$fBzY*tVnAFF=VNcF9_^=vUXnTKR zL71uAeAb88s*JhfUo1q!`8fM2kHOBvdL=m`aN)1?n$S{ZNHs<IC|;>*ln5UoH&GG% z8y{z%@Gvd-u-X*=XZouaEb0!aKcLsJ8ghi#F!<C#5Q30E<V=0~12PB$w))30Zwf3~ z8PTK5DB4(KEfy~Njhsbj-~p!i-xIxHD7a9>G5|yN>u)u*QRTBJr`f(yU{%@?)Q#t_ zMOqqjktT2mNRmtaN&1|LWvFYVurQL1ecv<2C$3$dln~`lv{*&HiMR55@Y!387{NxM z#q6uWj3F~rF(Exdq+$hFco2p(_!rI&70-Se=vk)iF{*uqIOk0FT*9fJdkWUiZ1Xh; z-Dlp$#NwIY%jsJXYm;*o>x5}6SzV><Gm}6<e$6OChJ~CaS^`~@2S^<x;xz$@CK7LC z<x6T{g$oZsh*2fF?5)@0S--`&h9t<o2TjQmhCvqlg~ue<Z+*}FjyD^J^P1j+9kOnr zrVK>txt>IdiqPsbK}n{&)2(4uC)I$uyB%e4)>3{nDd)wWYb6TyU)Q4tA7@7zt1HS0 zs45-T_TVKOOrvP*!L_npsR3izXEIwFL3B^g&-GWMR!}%Q;O;aDIL7zKJD^b$(K7K? zVYMyr8N73U?ZiaW6cUpZ5($O_pUqaeQmG@pp~N{gx;yoWk0qzExh3GgeFIJ4<!gHu zeS?%sX_+Sf@_bio{9HYwm?mHSgVvjs7BFU!9O*ngpEVfW=W;7U*uv<+qr{88plI>K z=(C*)T;boYW)?Z{3u@{Y@G#(Vqnz7=#=Xm?9SR*f*sD$;<(HT&(KdWJKJke*6Aw(l znjn2HTDY9hG=@Di5^Mv~wjMAzeL2M*{B{*rUwcR^wQ#BU*+l70W=H#<ZMy7-2-Re& z7fC*u(c{VZepyF)(w5)CbAD>2PVJ;0l%+~3`RdEHRro(x5Y5gyK#&K5;k;4O-2Fwk zbX7-0=G4JlxFSPZp35u?Q!8_YuXfUJH#OgfG_*TXmTRYcW=AWWqwa#{;&-y3BiBL& zQXO&mYsVD;zxZR8{&@G%`rIi0o)Y_IvK{L5>Rn8d7_hTok(LTgMO)wGf@kAR<R(+X z)}UW4N$1Ip(Mt>TPpmP>KyqJ*=<iZ*uF`SVze&#>gMl}G$zsHM@tGb*L=JN%kRP3c zuMt;VyLL+N+|K%iW8(q0yMz%N1Kx)<2f-z@<0A7|fWwIfUptFaGyc-?+{mQs@FYpq zW2-ab3vRo%(F)L2CyuZA{hETOzVtZ}YK)$Y;6t;)rHeIAD;~8IUrCY#aDI~`MAC_X zEZut(BG)5@NRuKwLxnqpyH8>Pd?75SP=i-izTFdpf!|4zmmScF^^P@skQ<%*CXjaB zLoh8G1#PC2-e%12FWep<hIegZ5CjEniG#W+TEph&uhvb>-o_xCUsm)t3ZNG%SmByM zst2ODB`EJ?Rl)RCp~_{tA(Hd8v5*9C7qH!RZgYtiZ8HyU%P*3BTX?lg>A%cML@8KT zS5hAhUtDX?j4s*A`rV&4@#5|(?)hnPLIVw>Yxm36cUK%r2qeqj)#v!VYMJrRUZPPa z`v^hiQsIEW^<VftkgrRU4#%iWy}QcwF1^CvcKqKXH_rFJ4}ag{^8%7dJ1JpG+6vh} z$45boP607Ux{g08JR;PF?h<gJTEyy5OWW=Cm5iinp>ZikUKjt34iu8{GB%<Ve@vSK zpzPC6X%X$3QZV}cpgH5@ShCuaF2G^YAFY2{uKc(I!C&kfAJdE=FQuW!hzl|O;;Hst z3S0sC^ObrLqlzjLO|St@+0aZDN3y$-zIZ*B89-umt|KEESfCmEn3(N8=BiiTcK^5s z(4+e9yri4#;z!jkG=&-Ay^{Jub4GpjwJs#a-EPBPxL|p?a81lQdK7M-Xc?Nc(Z53Y z`$Spl2Hhj{^tEX7Ux%UEn&&}zD^f(JLM<5WK>yt_CO!t*CTWQ-2W>2XHHj@QS@Fzf z3`?=EAY6GHXB}N6k{VK>q6;lhup$MFw=Mv`xtG48t4E|d$U}Pyt$JY0ESX~2wC}4g zZXdtxKRkUM9~ouvY9_A+Y!%&$EE^5D3z;pa15ZQ>mLFNy9t=pyYxAsdR3>AVD0#(i z$>{v$?u-YxIv?78>xnTyBJV@j&O9H8dzd_YD2Hcy27xu;1r*54Ejglbw#AgOg}BZd z`7i={?PcS34v<azwtSAI;=RP_#ffn?3R8%C(XD#zZT8N&ZQpStf3N$xh!B%FKxr34 z{x4iHRjuW9K}&U&%A~TeQBT^2YB*Zc%XH^+9A(hbHd&C)`~A?`%zbq$R~L3S-W?AF z+tF1Au+*7yJKMt^cB!0f4;o8uPS{Xq<Km?nPg}iF%64eJu5ZvP#dXNY-PzLY`L*<7 z0ce2N=iSkeVcY+HXtcGrw=fy_DHR<QgMa(i`$vq|l0!yi9z3dLxF0pUQooiJ=F4Tb z;fK;;lhgSr-FR(M4uY^6${aLjfiR&y-l}=BPK6<zczkP*@Kk&kpEXwx>^EAH&P}5Q zFadu<T@#h2Krq}i<?z=I@^eurD$-2t7xR{fyRw1rNardXEK0OU0liIET<D;lwftWY z*Dw1Inw6731epZc*3J9>y4~UX(d%a9<iW!wEDpnHUwoZU2huevz<0LE|DUDYb<hR_ zhD#}&wF#=Se@ib!`;s$u06#)zADS!O?fBh^&&|yv7h!ZznJz+EJk#@OrdAV>IYR*n zjcX5ebt#&v@vGD8qhtbFrOL~1-{0#B?-{%10m3_RIVJMiv}P=KcilF|D6nijLn#)H z>*pcrt%ANG>kqr#`yo87_x#|tBH&)l_^5Oi4dm~fW@EgSC2ucGIXVQHqzZ6BXVTPt z=<nHM`B}i3W%b`gFj_T#l*KLB@$gHAF0X5!(mIP0$%q<8i$FjT4rAGvh=9IB+Ln^E zjeNLByw@{Udx`5)(ypf5Yu*AbW~OZatTAH~f$zO@6YF{ysxR8K3h59GFCh)ovpn6i zE1$li;RBEQB)|n3k@d_VxtP7>0viqQ0ywYg5|i}<ZTT$eQ13f1*BAB$<(J+hL8L?? zB41ja_|1D>+%J%=A6E#h>0!n-8P_`={PyN~|HKpgBC}KlVp?vy-!jV$QAIC^>84DM zld5rX>3h8qg}1j~d*F=%^=Ty4+T@G$qNm`5rSA89Qt8qLfHK5t&&1X{aS(MvVBWxj zD5ONUGwn-l0e*9$$tdfeDreORAWq{kTntJ;aq|RGNs`A&%1glB?*j0GyLfL?cN`4_ zWh}gZ-bGOZpg|CZ{7U4A{r4h-wDCM$5M(dUbh~XaQ5b{7)ec-&%-TiR?5{ritaJgd zMS^g!rdOgUAkIfH`TlvuYNpVJIFis0e4N>CiW+<pDaNdV7!2<(S(LOeRJV$U-a0>R z$9K=$t~DQt>Jx{MI(Do`0rylz4Dh;c6%%q9)H6IjlzX=t8XCTS&vl+UX<R_csH}zn zV8~R{lVKrdIV{#mH&KNp&%M(Fp-F`|2Ggzr7{PLBG&ell?^R?z+fIwd`<8|vTNro9 zFM?QMSh1bAnnY@)n(aE8jc_3`RmYk8EB1DZ&1n9xu37I%C`T=9$ypDu^X+Tk{HS@w zRAQ4e)!7i*9ae~qx~()J=&Nc|B3R-kbA^Th9mXW#CoO#T(n^nYk7Ock>vl>@uaJIB zW%AlyJQkKB7)B)aEd4#dyeTB5=aapu+Xfb3&x>iZ#&~Am;)65o1*LX}C{Rd9Qs;Q% zKy2*0ptng{a;kj*zOh1-T;AD@W}}xFRajXu$Djz$i9Xxr8}&$ZpNVMTkt~rSAi((t z1V07|8@{WzJ6Iv$joyj&bPfmzaBM5OR%eTAZu-Jud?QA9geixk^v6rn=9hSm?p(B| zn3vd{pW?IhgJdV|`T|EV>GO{j4^d0!E@7(I0Av*_sF@h}Y?`1l)-0Fq`YQXDu+jc$ zq1JAAt6KR<<q7MINEO&^HHek+_sG?>&W*HX)j*{C=-dZwLzcry7hg+TLV@+x+Go-S zXm~Sgxrqh+;`CuXe~!3m5d{)Y^})5piMGs&A<%Gt!Cg2JQ0Xnm5h$D1OgAz440$D* zfm<3^j_LO<tL)ar6^RTnq}}z?CFX>EAn(ADIEa@J>gA&(`o4|XX#?-E;X^?vy_1*i zV_jO%HM#Y1AAa^>d1G17Nf~><$*!t@n?!9yXG}<(KGY;8S@zM!!SGhw`dkkms)GA{ zcsefK6~tX%OwAKF+JAd?yKna16}QP?b-5NBg3vy7@WPz7Z+k_Io|BHK?Mgi-W9Daz zj(V0X7&|?T9nJFo(?-Hxiw#Q8swIbMzXB}x`+646(PqHiM|Q7M=V@qc4eH1{{zf*w zns0~iONhY~bnph0eg$0;WrSJ<=o!rnz1D0Kd)L=zV>Xu3M6wDo%E0pV{K9i<tDkd_ zQ0q*KHQi`H7ApELevW*2JaqgFEtLlIrUSF(*Z3W^?(?qkwKB|P{uax~8UJ|2=|7P> z`W}NpymelZP;ggIEwp>DnhYcgZ2cXzYahYFP}Tf9arqBW)n>nE6OW2pXmwZ3PVKGE zTkKc<B<1hxtdn-X9DJj`c=$8u8dhah8VbkXwVrQ&ozEZSH7C&P=x7zP@nB~NjDks8 zYBjIpUj7m6!*QM;RAvMjczYQQDlWj`;<h6FI-0q4pG52#ARg(QX$nj7@u1TJ>!Z@x zC?u*1#M1=a*6?kV?~jC#h74-VaAfZR$50mMQ^j5_?a!6vKV?G;m<|%S>uzxIu^#+v zcPcSa(Pw9fu@>D3XH!aU7(A|5IJCC$uB>R3zyH;5bW_qCK3cZJWR;6Q)Ac#nA+DP^ z!)x{q(3a$N-Yc8wQ^NV>-@tZ0Qp7iWd;jMpIGiw`P?*agXoJsX&&!{M_sb`Qe*1!| zWLqt&tb@j}oW3sCzS~BLp=KR-`urkzObD%NqU<L)P$VX<P5pvYYv+rSH&GN)k9qXx z_<=aP*vh6RrWLzJ2@DQXE-c~8wM^o$U*GS%eaQ)8LL0lWEfWy=tCK?$bCw5p*vKXn z2Q7?r+I-j$3+|z$IMjJ3<J=_(@QhOyqk=v&^ZpbeeY73Ds~5yO2$FN=J-YCGRYToY z<o3DDF5bi?Ee`2#w{hX{dtqOrpz`ek8GS~QMMhGTkF(K_r|ib_F71Lc{1?76j6erB zCS2M~LCpCqGq>*tX20w$Hy`Hg`V6rp&A_xvT>?+v(_?HvB?&sTxuZk8YCahgpBhUZ zm|iB<->nDC^HzKy@0|Y>CIcfV59Mjxy|PoGgS>@gur4!Rpb#LC1`V262z|wxw9?)| z746i*Y{=fH#(et61Prd_a&>E5YjH9vwst-nB`0aSHNPIiF*~Vde91kx$xOF&2_O$J z@G?CV{;k^^-OBY5|5A=KQsb9qa=!6wq0)2vIbr&?;Qe}l!F-XehnG43h;}M9jh_DM z<OHgnGDt`K#2nn+jhz_l*pVgyOfx<>dC2hPjDlNDu0eJ_hPC9%R28}<*A1FiYGyGz zn>{)U)v@b~h<8?*6sX)j^YZXE*BXrJp#V-Dl&lP0<2q*)uUVboSO83`>@%U+4e*Al zu+xQ8w?Zh>wjBs)2j-HuQA$a=c@5-3eP$m5WahwyQ^bdDlSNy<kENFI2p`<p&4%~j zOo7}xyDvJUI&a^{)7O=bcHb_s7naUDyj7D+U(}n{?q~Uyo6Pd#6%OOASU-5txmf++ zSup1$@m#5xD$`CAa=p0qd%N})=sVhXJB=S+$qZvE{&QESwP6m49@JZ7fY{)HB;KcN zH`LsCqW|VRdwqNVkwAAeSfrb@yLexK7Sz1ywXN~0NmRtZQ2^#BZxz`Y)pc7f4}>6` zfll__1=acKou9~SZ`>U?^e7daEvuJy1e0Y+KYt|n4EPsni!MW1zzdW#wNu~V0e&c5 zlk+dddI!WH1l3@pyQt2K3UXM48c+x9&|M0q2QlD8{DPeClJ#WU5%)DFzj)O(Xs=+U zv!3-Y(Mh7+KcwefUm{TjPytMx(F}{~X9;vk5v0HjhRaTz^{u2+;_fx(ylN?V1l5IS zlv;|cg@(QPtHY1oYfA^aGXWO!oZWleGzcoQiNV`9PsyaT5SQDcsusl>K&GEN29A|r zt07!QHkmaz&(YL_W`Wq248r+B#3<Ta>|f?8yLO@f3c>IOw)zzk#SxjL^ZhEHZ&0T< z>bSST!Q%Towt>#Nb6EpVjnRZ{$eP=}Px~Ba=YQ(RLJlzC&X|e!(so~oiJ)I8U~+9D zlvaXj_ro{I_;5FE5yurVUp_zN>T_P5g3Z&0tGX3Fq?F6w5%V)J^Uid?e;)dtFyz3h z^vcB9an1P&eE|Xs25Pj}sx?jexkF-HOYU@}XJ!DN({Dw{IAv5Gtngt7j#FmzYH*#~ zCPM5<M};zo4>`c(v=5{;Nq9`vehVpXnCT!lfwY(a%~}{KtvvK?_^2)I1iB~~GM`I( zZm;V(K|Q`0rIATJyMtZ8Y`uPr&5;y+SSlewtcJf~#!rJU?^v?00;pJ*llz$5I}XJ> zeK%$2YEWP?jF@!fi07s69k*g62v$u-gZ+r@^Tdtm?{a%_syF-UdTfra&^=k{e|dwk zyO))o;E?>bZc5nM)2h7ytaOnC&}B(#RlKuR!{AKI`vURnvu)!Sh61%1K?$u_**~*% z4L@@9kWR>QcvR810}WIPv8x5ib&qz6tPm&lETA4y$qe98;rc@d9t84vKY|xlk^_|d zbT%oS;KCNSd9I$_C!3Z#+VVyW*sV4_w+s_?a)r?Jrvo`d;1Sig_d9M!mGsH%Tf;yN zY={(boc&G0w@*(|O54ciu@!${ee5CDoBoE~hcDsLB!=Ft*Fr4I;@xi0>lL@ut4ZIJ zR*K(bL4sI(>uceA;*)IrjkrHLsK%vLN!)%EgC;jG@84J95}}iSK|@bHhpRnsEU8Q$ z9nhuc=i;GEQS0Y?+F_m0*VR3o4`AN@0%v?g+9_#5PG}B*<>oLj2}08p0swA2%m%I@ z_l!?2Q-x$qAjk)>(R1TL`*x{Z3%T<TR1&EDuTTOv8v3_Mg18N(bNUaZ*-G^vC3gkg zIV~WY5LnhGGYlRD2D$bO&hJ!9jzl6UwIPEM5LE!ieE%0+M66^RYQkQnb0$$>R5a4o z;af14$uG-XI=y__Yn?=(qzDXb)G(0p9FBxWr6FIlg8MQR#SDgD0ibk`m;WkoH+N>1 zkibL=fFpO4pSc7p$C-SpDN6T+6pe130<m2!hArpQz!?61eJ<e_y#U9wrg7myaDYHF za6cqq$FhuO1sIrU)WvjGX7DKvpALcLwfT@Ed2og+^b7n1lyHDMtpi^)G>tFG<T_?{ z1XYj%COP4(Bl4*Iw|))NlYOk){fvZPbaT1f;v68}+D@s^uwU@oZb}4cb_P%~g~BR- zbOikL9!j?xi+sNa+zvnVzXR8`--x<MKxIWn+I~8E{r<KYT1ZH5ZYHnxj@&lK-hrex zvYoLSZZH<-1mP=bUQDCY*xp}<Ks3|5#2Lh1Mme1Ooi4?n_T6H&g4H7+LR3|U2@VqJ zOyPv2Op2}o<@F`T$&Nn+b#$1(G8rhn-e0T=$eW&A^=4&C5BL-qV7M`YQ1tNm`81_} z{z|c~Ho($%%M}Ndylrohc*G4PHxSA@;oX&2V<l_+cuf2|O!6e&&Q+l0XZX#R7KZXW za9O$pgf;oQ1REq{w)*?jt;R6TGj|;r1gIZuOSC3IxS2wnMCX9d3=KlzM-c%G$MJ~f zXk@zmZFD9<TDmLC55(u}@8rBX4B^52-@f4eG36HW9Wl&C)`yDNp@J)^riAOjMuQi1 z$xr~;XC;7(2L^uwezKME>T$K3zKktuFS0AizJAeu`mX2lz$=4EQBGe76kNmyfv^xz z1-MwR1M#UpaB;|M$$~Y=>|853l=vTpk2Rpz&Xue)DSZ+k7jnUF-yf2aQE*pw9+;~W zodnD(m2GUIh#EXqYCoJcmW-yFik(OUrDOVOuFIy7kt??=GR|qPM_v_cn%t!K&Ov?= zlGm+_TNB>ARI)Dfy4Z{su=!Yz2xXpS1(Td8{X4L*`6dLaD|JY@-2Rn)PU4+z&r&7u zonJ}?RLH6T!T8?cuupEX0c7J8yJ?zF<W9QS5mt}~%=dhCYcs>!G&kN7d(SK)9+lMa z^Q|X6xXb}vc%{+M_-y*Xv%X%aE`?1?%}<|9$&{WJYARjzV}mS0)@ZttTS}Lr%CPw( zTVzH-W1RXODXf<>vZt`2vElH3+^cVJPGuBlu#Bee0MhOi|Ig{2#e_lML}8(o-t|v@ z#kGy|jHjvUb{2a9_FhjFZ+}QA1>_XC@L>A5lYd7(YU^!%RRgCpgBmGvqD=MElS!7X zwoC~7ME})d;($@_A{<;+z7x}a``aV9`r5|T99-r^dYwvJ7paI5$a<9Uz93VvxJ^1< zcj7wc`S0iCJq#j-YlrH$F4lUqUok@9wPx?%r|kAs?KDd5u&U9W0Lv``>OJ8}KsXF5 zb2|zdcwgfkc>nZxRPg#8us?H#)zMX9AyNMZ_l-?~)?b}erj&duW;GHyHO69&eumuV ztt~1@vo@IvIFnbU$AlF+IKsE2cq7E(p!h@gUxcgn$MaSVAc3C#?E%tlM!5=Ke8oX} zE*D)_iW0t)E#CHzlWVVRB%{onPM76pQI)R5TMWVbGw>^nyK%0+h1ungE`#+vR~n>- zgw;|jnuI0Z>)yJTEx(C73OQFQ3l{^Ixof7%4pw{ldOO6{G}dTzdbE^I@B5iK8P9^P zQ`(I6{&JOLq~w<szAzLW`fQ#+VWmeRJi}G<RS0C>{7X6QdVi>CSA@mMMbZ7l(@%4w zh5udtZTKe${GVU+(IL+JqNgr&G>UZU!5No|Sd^42KlCwZ-1Qb<#uyG<@39D1-RZeP z!|_mvWfT)tel{d`3xXl$&V3sXX62=avOaD;GjZyW*P)sS060t09tpsB0v4O{ZFn#3 z48;kt8L0m$cx8d%j_Q5VssEe3E?xHpW2ACDR+m&_DGS&}9upe&MMfLt0#z>S8CQ)8 zbm0@b<@;~$&i%J~KH||IU_C%dCv<RGs6|~mXKO%UfjXN`7k||9?q405#OrPmy0wVo zee8alUtA$ko5^~`9N?<2bA?By4nkcih={W6FLaE!N%`b@N(#31+8>cu#St7Z-HiCJ zF0{T3#O@N-4M4gY52h=($932Wt?*=WS1AV;$JUEz##KYyr4>%yaI8i=2Xu=R8I2$M zUoy-v62gqJ@-{-HW`?@OT{ZA(#ok4Tzy@)0#`>_aj?G;l@CWdWClmX9Q(L<Zo2I_n zcJ7h}H1}<y_~I*EX{X9fd!{sLJMg<uu#e3oxU7j+o1-RtBDv|cvq4*F9vPx0fi-Yz zQ!K+pB2p5jEvz`3*!J4awf?O_(DAOOM^UF5qA*0{i$~_mywPQHA)IlCN%w`1S7gJb zCJDF=UZe$<z5lv(+O87wTu8fgkgK8GSKudK(~NAAhK&cTrD-2wTh)3Zc1;hqB4t!o z6SkMm`<-h&T1@eKytRh%84t{#LTKx0ob$*UKP=P8z$LXFm5C6B58=Xe;_US&hOgAW z2%m##0~sdRo`7^@Ly5JjR7e=s&u*4?7s1$kL1-OMGgOuN<S#7%!m#PNq(9BCR^&G9 z_kp9%_LJ`E>%#&=A6ih#lB`#z_*JytU0xnts*cbmQm-OaUk;utNWe4(!CCnWBLVlc zY{f*C_3`&rL;NaHNOt}Nn{?54O7%j5>=>qWi6`LuX`EziJx(NYgN^!e!M=Iw9r@6_ zK*ngs7N_bfjJHy&dJ8MG@IhfwSDs^jL`F<(ASCcyJ=csWvbqXN-Zb1bFKm%s(u4XV zMCq5&WUTnfM0(suh{KF+vquS8K(mSr?jm!Sj{S=;-VwVM$n`)_!nMaf9nC+9{ydvM zVRrv)-5hiXXzZXy%q(ad;vcOGe$y@_^I5D3A)fXScmqJ`P!lJ}Rmx$d(nQnuW=_xI zwXJ55TQ8}mf1L+r-(OjoYs>iIS2y9ky^-@BxlOSil&KhpB(s8yIj)PG{K>(L(_kB) z@=BL)c(z^~ax6gQmeb%-fiC86@W0B=BbU!<@l7hJvZy$4^W8|soljV8G`5<EcU9Hu z7lf@CygV1qDgXOD38-;ptLoBsD{hw$Ya;*r%DhYdTdu8CPKpxEQs+4r4!P?oX-DXw zO>>Y~7L&?CnW_TCI~DX-L<S%R+!G<L2(bRkCz?1rNOjB-7w+TMc@j{yEjh-K&ll3( zD!2b-bqXydYAd*iJhvD%_v@igZcY(Ox3_+k<wVZ7J)GXAC~Ts{#k)R6cwn<{l3(Qw zU^mCY3qu9<m-=Ck4XP5|A`nO3Y4y}2XSIJWv&UPoaWrkBN6#=&&F*GcriltsvEUX0 zOk|`+4<(;6%N$?QD#(dbo`>cVRMS)uhelEYK4U>q;=eGX)X<MpFAUxE4T?lRghM*G z-H$eyVP=C4ZM?nI2!(nP#}j^R1<#s%V7Fj1m<okr{B+)t0*afsCtp|+^|ey>vqHgV zF0WY&X@Ck`HZw5_4_C(eDoR5eizOtE5fx|LItC1BREk+DQ^9F{m6Sa|gTNpN>Akgk z6RHU{R!rH9MSd{&w*R<zd3Pj|l_gF^6!eu(v@{GXgAyCG7^e(+aX0Y!67>yKkOT-a zei7DUOnN9~1R=VxU-;cbsMZADkK^kDzeK<#(C8C029W{SQUPf2VxIS|9LI3!i?C#q zjfVm4sfC$tdeBSsit0{}(1Xe#;wU)&rffxoSb1C_Y`{XVOIDV;n#q9?Tl%EXIz+8x zjd;bNSsD!pQ;oHQOzq}xmj~3+?OyC5Q*R}`xZ3>`ArArDI1IfPN`oNRem47wkC0aC zAk;}3{QgE;^cK_Z!TIWzVa_dKVWIsY!dK9FZx&gKgaV$q;tsA4ePEw4+gMM?7FUjD z1OqHBBo4X#Ofu;w-PfxB1T?m<7^vvBzLp}#+pKx?-q*3?PAK`9iJzsS(d<SVwxH`7 zPKsSM@S-*H#fPn9?+}zYWQZ9x0KCpgY-I%81qR+NhLJU<PF~qIZbJOa0HD$dTn|qJ zUn%UP=`z9fSsO-7h0BAjoWBfYas<MOV{SO`ua#6GDDv0?Rl(dJW;30r4uGzyD=)#Y zH(n6QbSl{Vh;QvY0P##f1~t7|5LXrfEq*8U&aMPzR^7lafM-Cpptz(R^H|aiAb#jx z98yj-&K1(NS1$$eUhmlSQ~3x!eC9oww#<e+&yfOX2n0k%Js`L|u0M-NT^9vSU8ssw zHyxA4?|b)r5<2twF{LDBFbCo_i2J-;NOTASA$&4)<?SNrm-~dcHnhGh^AZ2H$*>im zfVNPa^xOnTH(yn;T3xI?oR`=@H?=Q=71a#f<OV70%(sfyz}Z+zqd@}0oPdYmov>u! z?W|6I`}@6Q;MiF|WqV4Mk>{IOE$HU-Uf+fr@x`24G3K~?<S&j0u|Rs?3~=NO4WuxG zU!-&djE2EDGI{yRWNFjyUg*s^(CV&Vn6bT29Oy)oxA=w&M(9|opyR)4Q&9}w1unx% z56c`Fdu#Frwxwuz_JA;J(>E4X8*Xa$F6o$XA~2&Q$ZBu!EH3FL`~wZ5VE#osG9zn? z24a;>AqP>Fg@0LX+Kd;B=#_CuUnI_k5bYlVRU&@#c25(^nt?w{mJwD8zu^8Hy59fy z;O;qQc56%IxaCSX3fba34u0BRY{UeB<=5hpbdBN{c6&A}-g1cxiQ0!s%6WnHBT+?6 zGAYmV3oT_Jg^ucA)9!wo9Bk}hAnB8aN<b0;)vBxctyFprUV9V#R(fH?;+`hn+IfdS z3|9V1eQOdE;W_R=K!bQz2Rk0QEtse#!3K4|{HCr_siv7~RNUal%M=%Jk0|47Rc#tc z26EE$Zu09T{5$l@Bjm=Ll$dgV{1EcInN3at7Q^y?zOnfhi%@GU{`=GK1J~xsO$Qbt za^t~<VRX^x6WCD}{In@zG3W&CEFq<$MF~<0MQvCXX$9uM-eiW8eqH9C-TgErIjKAc znOmFUB=tT#l_VhVr3|8+jxt_-eYg3MZ_`y))sR(Aewggya4KvIRgf>^($jA(#Py%( z*~Kt(tCf%7gE{LB$m4*BMTYFUtB<a92BrXzhgZeFOh}l=ypKNfIF616Dz|abXvq4V z58FIE;nQx@)92lIQI$JkE>-Ar$wLfxLrL}3g%ESh=)bOHQHj$|VbLFg#-LW$6Eil4 zHt%TLBr_L@T97Zrr~W9^22pEi5m0-09d$gM*Q{hta|@dHmgd75aAF+1Uu>CbXKEg} z2DOAgpBai|lm0lxn(}90_-=1Q(wH_}B6W@sWl^^&6p?%>%+MJaE3;3Gn0n`wKM>0f zo3C{rGK2<ZMx*+>K_({l?wV`S6_s;4W0mU@?82UzWLHF?qF*2vyRs|Q34;$WWCAJS zTiDhOuZOCr-*h=KdvH?iY`@cx2~tE7dEH}U+r&Nk>t~#h+-&5JVXGPC!*hJ9e)oT= z{oAUaOO0TUN>YO!_ng-6HN4FyCEuT#A|U7Wm9nC5qC6#05V^_;Z5LbNkLt8sbe2n~ z8A#mVE(H0)u*uj*KWzMq3Wu@$fL%dvR)kYIypD88?RS4|H*l0dQYEl_W>TDddB^&| z4f`%KX}4z{M3tGsM=^ZCaL&AiQ9{Fj>Xa_)liuSjx5ghfkqZ~2g5}rU@_c&-8uESO z*(ra@ZfzRyV2tbn24n-40q@KBEO*~{vJMdZ0^AQs%%MDeWodl@*>6f@A^LLQ=g)2! z=HKqMupMZ-adq+d!dnuq>I#x^wkJK+eT3pK(MB%*C08UBb!R8W8`(lpP>FAK7<k^n zrUil_Pfg3mcLz7ywoR+-o<gfmr|^G;32ZvzN6#Pi12hU5?_^|iruagpC;$^q+=2t* zCJ@=PBg*G*Nnnz&Br0o;n54H&qrR969`%F3C>de%-=tl@s9Ytyy3zk$_}cFO(b48d z@RFw~0cvg<FSdP;1^;36S|Q7e<`CJ$*ehk!=6^hl#c6Luzb*VDSuDbEy^O5>jTPt_ zb*y05?omRge`>xg`fIB~Bu2WStD(|vfq@pmXlFZXE5N<o@*IFx?KE0o{@Q8TXH+m7 zfuStd)iXbHt#8=3*G#~@?G)=Oks(f2rzab|coAKlvFj@o3cmXa=@NWL@)p$i`-F2S z7Ovk+$^lDHHtW}?W`Nnva5d*_^xSg8#vMRv8Ba_Inai4@<D32{xhOt~H&(hae4q>| zDh?QpmWt-3*rqH6w7t5W_X)6|?3~dCZZkU(bEDQN;}!#_)JN_J&|8}J@9uerG%L%n zhY=Bdd)u;$iJnui&v_+&{!)hUO5h5AGFe71KR=@up2vqgaOPJ^r#0iW3wcah65WEh zTFRcn(^vt83=Tc;T>3Z-;+P>h;RS(xoti9a7HJi%v?FVf`<dNiu#5Bd!5?tROI{ke zQvr;Fs@({Cu6qo<yN|m8B%DJ%Cqbm8Ne>QepC41jqsbz23Bn{gltZ-|DgH8=qBGJY zj?VOJ&`DT(US%tYh#5}i2T>K4C9-6p*fuEbwggbLO(arOUh!v_l$FoIb3RkSEYm0) z)#hWcGM&2wNXMO<+S-g%b}TP1U49)rKL@{CS|%T4qIMeR5@_OlU;oNHVeF8=|A$Jw zXWt?)WUtAQ=;*zP-T_kH|0=L!2f8t_#>}OVyU_NLq1<Tv)u*xK$zD*DxyWRC;tw?J z_rYrON$zd9ICgQp!2)dOCM)09PH@!Ar&4WRL(IEft=f+z89UM6O=8pkDV2FunMwxO zB`~<&YIALJy;A%Y?uOIb<p1ksb@=Phuipq3EAjAWXZ7K4DDA6yGK+_s*Ue`u>9<AO zYdyC1N6C?+Df(UxC+0HBUJh(fd4>hebpo&=%>i|>UnG0%W=wQbE-7eIi7if(ZYgPW zHPfvUN8Fi-MeDs{Jn5b(z<5pI`^zv;{NAZI{o#bc`>r3QLlxoGzYlVC*M1b_?%iYb zm3<`@dT{CY!GgR__WmO!UNrKw*>(svSbFv{=^m88`#irhQdk-L^%0$FU6cHtkaxpU zu2i{oX;^>(ll)IfO{1tQ!CLgousNT`+(3f;0qWK|B|o-MSbc#&hkn<5sB7EKAJXOz z20Cl)`0n0X5fq*sIFBlCplB7df2}so)|3T81{}OX*o{MRPN|9gUaO;vd<Z7M3nlJI z^4Bf+_}M(aQ;SLNX!tyVqYInTU?L%WDsn&WV~rINr^p(axOr{pZ>eWe;~S<6S)$e~ zP5($P4?j8nDRZFKK{JQ&f{4P$%%S<$wbhK=M7ia2gdssMrHO8Gr~c~qO=Y)Ib?j|^ z!1As1QSW6h_&U+RP09!;tfDdLe(;N<_Uvdh*9n&E{Kz!M;Gthwq;Donqyvc(Vh2p# zcoJBNe5+UxvC=CXY5d@t_aRNjO=K3w=g$iV%#U;4uOYvY%5QQ&#;Y21?$x(nffl)> z%HPD8Ou5-UplF!_-@Utx`$o{DKZUwA)1L6r9ETT5K;BhP#<A3oON{~|`~%0=V>Fuk zDO{jwsK5>#KNVvtOSngSB3DB_jj{!rFp_3A2&XGkL)z)oi=(c7HdADolBm)P+WqS? zF@56FCQQ@l&86bIM{V?LEqK9Uj`--;?E##Bl|m)oJD_Y<tK=`NwU|m-$)50pNzuA* zOSWSj_$g&7Cn}g$fKPCoX0T~3w9VJgEz1wtW*@*#OAInC{~<zpBOt`Qq2ijPIK>UY zWR~5;pzIIkpS0(OU_gb`-gWxZKJmV1Q0P~#vJK*6_c%wlXW>LZ)ZQl|Z<tu3)qY@d zeZJ6l?qaq;{IKUlv}w||;zsRS#&q~Og5eCI*m+oft2wI`%;!0jRdF!g^e`>_P47j9 z&=I%gx4Keve;SEmM&vQl8M!Bbk^ta5kZ<h`V^LGJ7&OqtD-(Qd$hH_mjZz>8S+$!a zZ1-Y|Q?z=2JiJ2(YMS>0LTGtk1)lM#lAxoaQiEFBepLsXms~BRk-kgf(0e6k(M?G$ z#M~j{DhR0!rZJFS4y0!|IP>haiX`1x39jaK;x>8h93CrpD%`Xe`~v^TIsDS<4cpd% zY99;hZzCurH#3B7alM2miz5|+i#nG4Sc&5I>FmtC!ldeg_joDDHTVxVzt68df~q4F z<su@LG^BfV@TUIDyY7Sgq1W+P073Pe#idYEmK3_9Zs9JLbJBJnRyQ_z$I|GGb#zlj z8s`mMOeFAJ7F9ZS+;%sX|DVA^T{glQn1T>XEb4W0w6i_^^`);o7!3^_e~Cg-u9uht zx`*pLqTd`8Pj@UJ%#A_&f)L-j*<VvK%g!>!BE$-L@kXl#-(F-KJI|Pv62El_=gT*g zA_d{oewAX>ll^Jc6-BwpxECdlQDZ!Z4RBGShCF_S5(#IK&9zzrzw5G9-Ry_+)nqoW zUEEJ>tUE8D?`R$dxk8ST7caj?eb<dQ#7CelaI5Q3Rd6nKx1hpCm2KuRp!&^*1PGAx z52H83J;*KieYN*~SD`3GBL3{|<6A+<L*KmfU5pybfq2VqqZ8wFcbzN=wvvs^T3tsJ zGn7n#1N+mx#&0KlXP@u-an1`iO}L=)F4z4a(&}|bpd0{l<&FhO))&aW6~-)45~mo& z@K7ep7I5|mI1b`NV9H<tKLuj1WHNAGsGLPQGd{BLM6K-$b+T0!WHVzzezR(KuhZ%c zuB>DXet;1l$H}NEZ4k*!HNOC%DmTTSWUAHVQ^M9hJ8!hekqM0nThP%U=1(;`*et0J z70;VEVgq=fBmEt&0$%hG#%zWj%QZB>UIsulTTs<_)%ZOKGUpaSx+}#rk78AzP?!^= z0#qyiky62nb>hk%#(tuI57{4(Sxwz2?V#N3ktKzsw2DINT&W@Z7t*gj>bY5f4u{1s zAi!J&st$&{%3WEh52FZHEN6VR#r3JCNro9DCeZ14a@cCa=JIfR^zqWF;?~QVggKmw z7g`=aH$8{BBNFq`q>S0(_Z2dY$6w<H=Zc5Hzv7!|^s%tjJdTy_{?rP#RU{Hf7z}TI z51|MNb$0*dN=ptRYM9UL$@tMdw3H-!Oik6{09o782qwh>=D?ExDj7mt_IF(NP2EvH zd!=x3p@I-Gs_Hd#!JnYy93u#-=Sm!pyHq&J(d-3khOA=pl8#-7kPf7>yZn7jNR*{@ zyw4^Yz(Z)h@fC>UPuRIbM`NFF?6f`rxE|d~vho{U70EHoA5>%i{c_2IpbJw^a4ipS z7+E;IAe>gy9p&?;&#<Wjr}bfN&obk^)0c~pSQSqoOSUJX`mZ2_<ky_DX(B%tS@hL+ z%zO$wHl^f-zX1pdE9^xs;D>0H=a1t`UZ!K{oFP=6Km&*xubJD4>y_b^Fj%u61Jr<) z;@Y92uZ9mD`<}y<IW~x<SL2bT{@3E2#XJFEcJnZSn^|-{v<=BWTxYe~2v(=3eGP)V zspUEx(d$mrbuMDyJWuXdutU-CN0FLw;xNyQwHbn-_A805m%O$58*mqtG@CsvZmXyR z{1*;eUPIKFAV9@1T}|7N>VWcoO_v~2h3L<GEdz~4sr$-`b9^-haHXS`4ccGM!8V^4 zQ4#4pM#7e$-7z}x&d94gNzp3ry}o~o>0`>coQyD(7-USDDhSEpnTBI@kWn3oB=Yo_ zgPZfnff!VIL^RHK3pz9yn2^LZjW(ctPudhQtYLayqE6?RWN_3dmDm6-WCUT{D9{z< zC%lwdnkmvL9QrQqn8=5^LS{Ct2s411>|@B?17~D!+6r6nG!0c<_us`a3NyqAPV37a zX?Mz&7BDO$9uNBSr*p+rcy)wXc|gsdKxJa*IH{ND77HAveRs|Tq%B*YL$CQe2?3ly zHF)aQ9Euf5wO=AV`B+*dZ_?KfQwZaZ-_Kmm>98>C5WX}2_O86$pEo04%wSmZH!1JB zwSkwyS*RpdGZ+Y$zVvrK0|Hn-(TC@Lp{ZZK`5a66-Xi+1C4pk+n#jF*#cIy2_d5vC zJUMTDy*DEbd9`D7cbITcHzv^x)^z^gucNn>#9-0h=?Qiub5hY_sC_MXgUvA#DVQY^ zK7{ZlVbGHK@n+cEx0z#);y%M!F<VTj_R6li5sDI(qz>S1RaZm9V0gEz8x<hhfrI$d znWjW5=sDf4P?JWc*5BF`PPZ3ns+C8QG-e4*6q((`32>iBh5Daoe(AUS%Bd|oY#*(! z<u9ID8k0$|2~8btT7HE&$h=R~_&0ifxww|}B~Rub%=6a6tpwq3h{{}V-~BA1<@K#f zrhN5bYUisU=`GH~*bhyl)84O(m?^r$S*>y8O1Zn1H7D%5ZTgC%o7ZJQJ5!WNgg$T1 z_fTqBA#1X#8JUe_sGTo8J~b&_g<)7NCn^qk0NGBYikFB%C}C4jjkm@b(+3PP%8fAY z6}%wYsAY;x4<M37Xxs~X{Y2>4ixxk9{I3`a<R(v)V}8*nBk&ZnAP9wh^Yc}7?#G|0 z#_&;`2_>>fCK|96**>y5F>dfqzkoLb(3N;6ps-9DcN|+|98K*GRheKhjaW2Q?ew02 z9%_Z5DKbs-#lKi<yA=v85ou$&$A`VmnKvz_)sIqV+YQ&5Jq_84n~J^F#u+MnnV43| zIeB9cKcBTTq)RL~>NV?<SN3fPAad>__L_)1sv@#^@@PX|(8y7%xwNd|!-uEE588AQ zO0P_FKUB1Jr6D6vAJ$N<S1`MNs(gH-&0rbhx6ncEDEXxfrfZl|x7yi+a*#K@&GMQ$ zMQdzKbcwbs`z`Pp?0S%EjFlQ+FGJnZ<R7H(a}kyoLi(;+fw{x`ed}xOY*wsW_0Mi1 z)u;P;3iH>=R?t9i?DS=Cr0yEtnQyh{YuI(yNh2Y>9757*`7*OU(Dv;OrGn>JMn54` z`JF%p-ije`)vjm;oEky(`MFQnRVGmQa&<=;Is7sG`0+GMG@~R$MoMw385iMuR<&=! zoE`WSzlgOzZLon1(HP+;gze<4bC|KMH~?3>8z1OIufd47c)j2UaXs^!wNc>+YR^@V z<{YBy^_PBG<9#07-r-2}{!w9|;*8?yvwDZ<BTCe1%1G20=7I0)+k9DbLam)^jK94$ zd_nn=pVcvON)^JR^Kg3w(@HSuhrZ<&;T8PW+6vGl(3IoHon;0EMZCbj`aZ5|&65^p zA@Etp;#zCb9IayMv4qOH%2)#L1)J-g2BQT9c|n^r&S+O;wLJYiowd%-)eEe<SHVO8 z{*bqrTsSs@WqJ^l&JN1^D4&c-(l<XYA{^g_{)a*sbHBZ?(Vq9e6eI>T^p%%5Rgc&Z zl-WU0aw{IHD96RVYywdq{H@++^ianm(K@hawX?K5j7wE6fdCZwalFC0pAHfv{I97a zk@!Kx{2I=}RikyxM-IHGKm}Qk0`H#_XfePXw(~M^8>4WUz<(N-SR4kCdZ6#z8=;no z5svwv{*BulTSXB34~u#@4pc#=JpHrg>(Srcb3M@T*B+eFpOSppJ0|SbZ2vx<kp1d$ zHXr**T%`vGf8A60r}wMvdTnb`Fk0l-S*dh6WhJv7oKqE1kvC3NBg<0b3l5ARsch<Z ze1M<)!~iML<;?w=C|Abqe=33^jk9fWU4_o0Y6_x&s0*D38Z;&^8JGWY1f|ej&K#ZB zBq{#um@*VRWkN@*C?2n=USO}e_6bOJtrVob2_r(CgR+Fwz(r}ssuz7}`c|v>Gg&v# zt(EqmHaGVS1@?&|mOVIMS^m58ELr7=Xe#@^0Nw;4`@32k$5%x`1C2{w!~!h|$rbPa z9&YHC(2Y%!9bz~c{?py^?^B%t7H9hIOioURmy1>Z3H=8E5NDPPO#py60T3tR1VEg> z4glf=K%9sZ0C6gww{6i)bY~Wx^FA!*oYp^2v@Yc+A~`2#r|~&!+DH1Fc`-C(9Oqm_ zO3qy80h}jJEf<+KOOz|7tI0)qQi2|wGr25}qU#5oyfo%^;PC9Y8Ui?BoMskzj&t$S zlWp%DeOw&XbVzgV4Lq(R*E>6%FHb<6CdwhsBm+eq&gm~Mj#{hqF||yVuCfrox#Dcv z6mHt|GxMVL!W^<x#XO%AWS0{=t(or&+uOt-S3dBn^~!URD$pb4MeYL^?KpV3S_$Ax zan@=|aM~KJoxcB!6t?uhIN!8%J16a9mg7R-X|tKB!?8|VWS5<x8=H#km;g=`r{0q> z#{tpM6H{X{%`)DWYHFBSHg2Mu)PLZpeY@~>Gwzq}$#o&XN#gV((bA(y?R^+JFus|* zpHsRS$M&XEj~+GFg~c=xcLq+bPOt><>*EYOrf<b`Q>cY3)Uh^qE^}vpQ=^)w^6hsI zcVh!pN;a7~oYaK?zdOzs_%Y66dix9Y=w6(bI-*>x&CT9QJ?rDIVeZ~$SKJ&1aDq6G zkK=~7USv9Prg2J%Dqkn|`JZ2em5u(>!bAXPiPLxXUpVTRR3}dR>hhzyYuH@+xx2d7 zO#_Qu6aqL)oGX369pjAjD$aT<>S@5}3pL#bE;Qns2d=*rAkI~f*@rk4y=`*k7-xDN zXRf<mlMM|eT#0Zdcqv8bElMvOyvQ+5ejTSD|9XaddKNridp5Z6ePGg#L(W;A@c5km zaM_qQ<BU9%>dbQk#A(+o9pA@Eud%$r)0qL{v_U#b(BH(V|JFE#P7DyIE7B-AGiysk zh!g%ph;wqyo3@%k|NPzBpkJWV0>r6gmlvx3tvKtRnE(b5XYRj)<oh_w-|<ok0OGW= z>9=XeaejXhg5JEki3LEMlY4Pd-sf?0GqJnU(<Y9;EC3)*;u>IA`YcZW_#jqI)D_;n z@ZF3qW&l8(rhFEs^xrbpnW^dZTgDjA7r_YPO!8fvPBzPUrhl9mUsQZzfH?bsOisRx zv)*(THM*ExT(c`s&y44C&kk{F8Ekj*b)3ublWZ$h+FwK%)*0{tUx3#+efLwl%)Hl8 zw|p8Wg|_RPRi2VFUN%v6PZvjL28gp3<C_7N%O2N0T1_9vNm+J&wRGpA?3VVXbQd1; zRH@*z_W*JJgcR30!)VU33&gU?Ij0ZfG(oOnVy(5~xk%;c!K8{*KJcQoUU~MiLz{p& zO_VO)_f+|VI4hQ}qN%0gU?D#49S|o`lB2Ba?6=~a)FDo?S&o9C11`OVmIwfGk`06Z zH`k=SoRVIh)5ck;P}-@3;>_x$;nQzT6A<U$&nzkLiNqXOH#dq})7R7*Zew|&F?(C* zb66Ff-v8RCV*&sG7z9B5Td%{^4?2LHdmtwuC&&rN33AE_asqOKoPeB=lM|2=<OJjd zIpqX70XacVK+YV<3CIa@0&;?!a)O+IoFFG4C*<S=<ODeZIYCZ2K~6wUkQ0y-=HvwA i1UUgYAs{CpC&+mtyaa;{+%qKr0000<MNUMnLSTZ`{QQ6b literal 0 HcmV?d00001 diff --git a/ui2/src/views/common/MenuView.vue b/ui2/src/views/common/MenuView.vue index a80d1a3d2..a341304d4 100644 --- a/ui2/src/views/common/MenuView.vue +++ b/ui2/src/views/common/MenuView.vue @@ -20,6 +20,11 @@ </template> <template #end> + <img + class="logo_anaee" + src="@/assets/logo-AnaEE-france.png" + alt="Logo de l'Infrastructure de recherche nationale AnaEE France (Analyses et Expérimentations pour les Ecosystèmes)" + /> <img class="logo_rep" src="@/assets/Rep-FR-logo.svg" @@ -113,6 +118,10 @@ export default class MenuView extends Vue { height: $menu-height; width: 100%; + .logo_anaee { + margin: 0.7rem; + max-height: 4.5rem; + } .logo_rep { margin: 0.7rem; max-height: 4.5rem; -- GitLab From 1596256aab6d419ccb4ee808bc145455dac7fa24 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Thu, 10 Feb 2022 11:46:52 +0100 Subject: [PATCH 20/24] affichage version dans modal --- ui2/src/locales/en.json | 3 ++- ui2/src/locales/fr.json | 3 ++- ui2/src/views/application/ApplicationsView.vue | 4 ++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json index ac042aabe..1267ee029 100644 --- a/ui2/src/locales/en.json +++ b/ui2/src/locales/en.json @@ -84,7 +84,8 @@ "trierZ_a":"Name Z - a", "trierRecent":"Recent date", "filter":"Filter by", - "change": "Edit app" + "change": "Edit app", + "version" : "The current version of the application is " }, "errors":{ "emptyFile":"File is empty", diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index 5b898b466..4a7333b22 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -84,7 +84,8 @@ "trierZ_a": "Nom Z - a", "trierRecent": "Date récente", "filter": "Filtrer", - "change": "Modifier l'application" + "change": "Modifier l'application", + "version" : "La version actuelle de l'application est la " }, "errors": { "emptyFile": "Le fichier est vide", diff --git a/ui2/src/views/application/ApplicationsView.vue b/ui2/src/views/application/ApplicationsView.vue index 2e62f328f..70ff172bd 100644 --- a/ui2/src/views/application/ApplicationsView.vue +++ b/ui2/src/views/application/ApplicationsView.vue @@ -121,13 +121,13 @@ <div class="card"> <div class="card-header"> <div class="title card-header-title"> - <p field="name">{{ application.name }}</p> + <p field="name">{{ application.localName }}</p> </div> </div> <div class="card-content"> <div class="content"> <p> - {{ application.dataType }} + {{ $t("applications.version") }}<strong>{{ application.configuration.application.version }}</strong>. </p> <p> {{ application.comment }} -- GitLab From 9fa3629191529ccaa23d1bf878de874ad2e8f0cc Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Tue, 15 Feb 2022 15:12:34 +0100 Subject: [PATCH 21/24] =?UTF-8?q?d=C3=A9bug=20sidePanel=20ref=20avec=20tra?= =?UTF-8?q?duction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui2/src/components/common/SidePanel.vue | 4 ++-- ui2/src/components/references/ReferencesDetailsPanel.vue | 2 +- ui2/src/style/_variables.scss | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/ui2/src/components/common/SidePanel.vue b/ui2/src/components/common/SidePanel.vue index 84929bbd2..ebd4b2845 100644 --- a/ui2/src/components/common/SidePanel.vue +++ b/ui2/src/components/common/SidePanel.vue @@ -38,9 +38,9 @@ export default class SidePanel extends Vue { <style lang="scss" scoped> .SidePanel { - background-color: $light; + background-color: $white; z-index: 1; - position: absolute; + position: fixed; height: 100%; top: 0; width: 33%; diff --git a/ui2/src/components/references/ReferencesDetailsPanel.vue b/ui2/src/components/references/ReferencesDetailsPanel.vue index 33771735e..905e59813 100644 --- a/ui2/src/components/references/ReferencesDetailsPanel.vue +++ b/ui2/src/components/references/ReferencesDetailsPanel.vue @@ -2,7 +2,7 @@ <SidePanel :open="open" :leftAlign="leftAlign" - :title="reference && (reference.localName || reference.label)" + :title="reference && (reference.refNameLocal || reference.label)" :closeCb="closeCb" > <div class="Panel-buttons"> diff --git a/ui2/src/style/_variables.scss b/ui2/src/style/_variables.scss index abcdfddc3..e4d99d484 100644 --- a/ui2/src/style/_variables.scss +++ b/ui2/src/style/_variables.scss @@ -23,7 +23,7 @@ $menu-height: 80px; ***************************************************************************************************/ // General variables -$primary: rgb(0,157,157); +$primary: rgb(0, 157, 157); $info: rgb(20, 155, 170); $dark: rgb(0, 100, 100); $success: rgb(186, 222, 129); @@ -31,3 +31,4 @@ $warning: rgb(255, 170, 0); $danger: rgb(166, 0, 0); $light: rgb(202, 216, 216); $family-primary: $font-family; +$white: rgb(255, 255, 255); -- GitLab From 6a9e54c5d20103f1887715700dec98f923995e86 Mon Sep 17 00:00:00 2001 From: lucile varloteaux <lucile.varloteaux@inrae.fr> Date: Wed, 16 Feb 2022 14:14:27 +0100 Subject: [PATCH 22/24] =?UTF-8?q?uniformisation=20des=20icons=20cass=C3=A9?= =?UTF-8?q?=20apr=C3=A8s=20merge=20avec=20to=5Fmaster?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ui2/src/style/_common.scss | 16 ++++++++++++++++ ui2/src/views/application/ApplicationsView.vue | 5 +++++ 2 files changed, 21 insertions(+) diff --git a/ui2/src/style/_common.scss b/ui2/src/style/_common.scss index 10b3ead84..2829156e0 100644 --- a/ui2/src/style/_common.scss +++ b/ui2/src/style/_common.scss @@ -23,6 +23,22 @@ a { color: $info; } +// affichage icon uniformisé +.button .icon, .button .icon.is-small, .icon{ + font-size: 0.75rem; + align-items: center; + display: inline-flex; +} + +.control.has-icons-left .icon, .control.has-icons-left .icon.is-left{ + top: 5px; + left: 5px; +} +.control.has-icons-right .icon.is-right{ + top: 5px; + right: 15px; +} + .clickable { cursor: pointer; } diff --git a/ui2/src/views/application/ApplicationsView.vue b/ui2/src/views/application/ApplicationsView.vue index 70ff172bd..46eca3e83 100644 --- a/ui2/src/views/application/ApplicationsView.vue +++ b/ui2/src/views/application/ApplicationsView.vue @@ -349,4 +349,9 @@ export default class ApplicationsView extends Vue { margin-bottom: 0px; } } + +.control.has-icons-left .icon, .control.has-icons-right .icon { + top: 5px; + left: 5px; +} </style> -- GitLab From 7a7c51a0d42cf3b6982a92c4908f08565e3f5676 Mon Sep 17 00:00:00 2001 From: TCHERNIATINSKY <philippe.tcherniatinsky@inrae.fr> Date: Mon, 21 Feb 2022 15:26:49 +0100 Subject: [PATCH 23/24] Mise en forme des messages de commentaire Mise en forme du message de version modification du nom d'application : limite 3 au lieu de 4 --- ui2/src/locales/en.json | 4 +++- ui2/src/locales/fr.json | 4 +++- ui2/src/main.js | 4 ++-- ui2/src/views/application/ApplicationsView.vue | 18 ++++++++++++------ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/ui2/src/locales/en.json b/ui2/src/locales/en.json index 1267ee029..a6fc3af7f 100644 --- a/ui2/src/locales/en.json +++ b/ui2/src/locales/en.json @@ -72,6 +72,8 @@ "applications":{ "chose-config":"Chose a configuration", "create":"Create application", + "comment": "Comment : ", + "no-comment": "No comment", "test":"Test", "name":"Application name", "name-placeholder":"Ex : olac", @@ -85,7 +87,7 @@ "trierRecent":"Recent date", "filter":"Filter by", "change": "Edit app", - "version" : "The current version of the application is " + "version" : "The current version of the application <b class=\"has-text-primary\">{applicationName}</b> is <b class=\"has-text-primary\">{version}.</b>" }, "errors":{ "emptyFile":"File is empty", diff --git a/ui2/src/locales/fr.json b/ui2/src/locales/fr.json index 4a7333b22..c15157363 100644 --- a/ui2/src/locales/fr.json +++ b/ui2/src/locales/fr.json @@ -72,6 +72,8 @@ "applications": { "chose-config": "Choisir une configuration", "create": "Créer l'application", + "comment": "Commentaire : ", + "no-comment": "Pas de commentaire", "test": "Tester", "name": "Nom de l'application", "name-placeholder": "Ex : olac", @@ -85,7 +87,7 @@ "trierRecent": "Date récente", "filter": "Filtrer", "change": "Modifier l'application", - "version" : "La version actuelle de l'application est la " + "version" : "La version actuelle de l'application <b class=\"has-text-primary\">{applicationName}</b> est la version <b class=\"has-text-primary\">{version}.</b>" }, "errors": { "emptyFile": "Le fichier est vide", diff --git a/ui2/src/main.js b/ui2/src/main.js index 02a15e11e..323d1de40 100644 --- a/ui2/src/main.js +++ b/ui2/src/main.js @@ -155,7 +155,7 @@ extend("validApplicationName", { extend("validApplicationNameLength", { message: i18n.t("validation.invalid-application-name-length"), validate: (value) => { - return value && value.length >= 4 && value.length <= 20; + return value && value.length >= 3 && value.length <= 20; }, }); @@ -180,4 +180,4 @@ const app = new Vue({ i18n, render: (h) => h(App), }).$mount("#app"); -export default app; +export default app; \ No newline at end of file diff --git a/ui2/src/views/application/ApplicationsView.vue b/ui2/src/views/application/ApplicationsView.vue index 46eca3e83..947032e6f 100644 --- a/ui2/src/views/application/ApplicationsView.vue +++ b/ui2/src/views/application/ApplicationsView.vue @@ -126,11 +126,12 @@ </div> <div class="card-content"> <div class="content"> - <p> - {{ $t("applications.version") }}<strong>{{ application.configuration.application.version }}</strong>. - </p> - <p> - {{ application.comment }} + <p v-html="$t('applications.version', {'applicationName': application.localName,'version':application.configuration.application.version})" /> + <p class="comment"> + <span :class="application.comment?'has-text-primary':'has-text-warning'"> + {{ application.comment?$t("applications.comment"):$t("applications.no-comment") }} + </span> + <span>{{ application.comment }}</span> </p> </div> </div> @@ -315,6 +316,11 @@ export default class ApplicationsView extends Vue { .column { display: grid; + .comment{ + display: flex; + align-items: center; + align-content: start; + } .card { &.applicationCard { width: 300px; @@ -354,4 +360,4 @@ export default class ApplicationsView extends Vue { top: 5px; left: 5px; } -</style> +</style> \ No newline at end of file -- GitLab From d3cddb5787762f31bba8a1d4305b626afe34df73 Mon Sep 17 00:00:00 2001 From: Brendan Le Ny <bleny@codelutin.com> Date: Tue, 22 Feb 2022 14:50:59 +0100 Subject: [PATCH 24/24] =?UTF-8?q?D=C3=A9clare=20NOT=20NULl=20les=20colonne?= =?UTF-8?q?s=20'comment'=20en=20base?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/migration/application/V1__init_schema.sql | 2 +- src/main/resources/migration/main/V1__init_schema.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/migration/application/V1__init_schema.sql b/src/main/resources/migration/application/V1__init_schema.sql index fa7c19c5d..47f954f3f 100644 --- a/src/main/resources/migration/application/V1__init_schema.sql +++ b/src/main/resources/migration/application/V1__init_schema.sql @@ -5,7 +5,7 @@ create table BinaryFile updateDate DateOrNow, application EntityRef REFERENCES Application (id), name Text, - comment TEXT, + comment TEXT NOT NULL, size INT, data bytea, params jsonb diff --git a/src/main/resources/migration/main/V1__init_schema.sql b/src/main/resources/migration/main/V1__init_schema.sql index 3bdd94c02..9222f240c 100644 --- a/src/main/resources/migration/main/V1__init_schema.sql +++ b/src/main/resources/migration/main/V1__init_schema.sql @@ -164,7 +164,7 @@ create table Application ( creationDate DateOrNow, updateDate DateOrNow, name Text, - comment TEXT, + comment TEXT NOT NULL, referenceType TEXT[], -- liste des types de references existantes dataType TEXT[], -- liste des types de data existants configuration jsonb, -- le fichier de configuration sous forme json -- GitLab