From 0300cdf8a5a229d2529f86982368bde069094580 Mon Sep 17 00:00:00 2001 From: florent <flavelle@neogeo.fr> Date: Mon, 4 Apr 2022 18:04:11 +0200 Subject: [PATCH] fix css affichage --- .../Feature/Detail/FeatureTable.vue | 66 ++- .../Project/Detail/ProjectHeader.vue | 8 +- .../FeaturesListAndMap/FeatureListTable.vue | 1 + .../FeaturesListAndMapFilters.vue | 388 +++++++++++++++++ .../FeaturesListAndMap/FeaturesMap.vue | 11 - src/views/Project/FeaturesListAndMap.vue | 399 ++++-------------- src/views/Project/ProjectDetail.vue | 2 +- src/views/Projects/ProjectsList.vue | 19 +- 8 files changed, 508 insertions(+), 386 deletions(-) create mode 100644 src/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters.vue delete mode 100644 src/components/Project/FeaturesListAndMap/FeaturesMap.vue diff --git a/src/components/Feature/Detail/FeatureTable.vue b/src/components/Feature/Detail/FeatureTable.vue index 5cd73e03..ce7bfb01 100644 --- a/src/components/Feature/Detail/FeatureTable.vue +++ b/src/components/Feature/Detail/FeatureTable.vue @@ -5,45 +5,39 @@ aria-describedby="Table des données du signalement" > <tbody> - <div + <tr v-for="(field, index) in currentFeature.feature_data" :key="'field' + index" > - <tr v-if="field"> - <th scope="row"> - <strong>{{ field.label }}</strong> - </th> - <td> - <strong> - <i - v-if="field.field_type === 'boolean'" - :class="[ - 'icon', - field.value ? 'olive check' : 'grey times', - ]" - aria-hidden="true" - /> - <span v-else> - {{ field.value }} - </span> - </strong> - </td> - </tr> - </div> + <td> + <strong>{{ field.label }}</strong> + </td> + <td> + <strong> + <i + v-if="field.field_type === 'boolean'" + :class="[ + 'icon', + field.value ? 'olive check' : 'grey times', + ]" + aria-hidden="true" + /> + <span v-else> + {{ field.value }} + </span> + </strong> + </td> + </tr> <tr> - <th - scope="row" - > + <td> Auteur - </th> + </td> <td>{{ currentFeature.display_creator }}</td> </tr> <tr> - <th - scope="row" - > + <td> Statut - </th> + </td> <td> <i v-if="currentFeature.status" @@ -54,21 +48,17 @@ </td> </tr> <tr> - <th - scope="row" - > + <td> Date de création - </th> + </td> <td v-if="currentFeature.created_on"> {{ currentFeature.created_on | formatDate }} </td> </tr> <tr> - <th - scope="row" - > + <td> Date de dernière modification - </th> + </td> <td v-if="currentFeature.updated_on"> {{ currentFeature.updated_on | formatDate }} </td> diff --git a/src/components/Project/Detail/ProjectHeader.vue b/src/components/Project/Detail/ProjectHeader.vue index 41f9a283..211f4667 100644 --- a/src/components/Project/Detail/ProjectHeader.vue +++ b/src/components/Project/Detail/ProjectHeader.vue @@ -1,5 +1,5 @@ <template> - <div class="project-header ui grid"> + <div class="project-header ui grid stackable"> <div class="row"> <div class="three wide middle aligned column"> <img @@ -290,4 +290,10 @@ export default { } } +@media screen and (max-width: 767px) { + .middle.aligned.column { + text-align: center; + } +} + </style> diff --git a/src/components/Project/FeaturesListAndMap/FeatureListTable.vue b/src/components/Project/FeaturesListAndMap/FeatureListTable.vue index 6308c014..90a1fa99 100644 --- a/src/components/Project/FeaturesListAndMap/FeatureListTable.vue +++ b/src/components/Project/FeaturesListAndMap/FeatureListTable.vue @@ -512,6 +512,7 @@ export default { .dataTables_wrapper { position: relative; clear: both; + margin: 0 1em; } table.dataTable th.dt-center, table.dataTable td.dt-center, table.dataTable td.dataTables_empty { text-align: center; diff --git a/src/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters.vue b/src/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters.vue new file mode 100644 index 00000000..31586731 --- /dev/null +++ b/src/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters.vue @@ -0,0 +1,388 @@ +<template> + <div> + <div + id="feature-list-container" + class="ui mobile-column" + > + <div class="mobile-fullwidth"> + <h1>Signalements</h1> + </div> + <div class="no-padding-mobile mobile-fullwidth"> + <div class="ui large text loader"> + Chargement + </div> + <div class="ui secondary menu no-margin"> + <a + :class="['item no-margin', { active: showMap }]" + data-tab="map" + data-tooltip="Carte" + data-position="bottom left" + @click="$emit('show-map', true)" + > + <i + class="map fitted icon" + aria-hidden="true" + /> + </a> + <a + :class="['item no-margin', { active: !showMap }]" + data-tab="list" + data-tooltip="Liste" + data-position="bottom left" + @click="$emit('show-map', false)" + > + <i + class="list fitted icon" + aria-hidden="true" + /> + </a> + <div class="item"> + <h4> + {{ featuresCount }} signalement{{ featuresCount > 1 ? "s" : "" }} + </h4> + </div> + <div + v-if=" + project && + feature_types.length > 0 && + permissions.can_create_feature + " + id="button-dropdown" + class="item right" + > + <div + class="ui dropdown button compact button-hover-green" + data-tooltip="Ajouter un signalement" + data-position="bottom right" + @click="toggleAddFeature" + > + <i + class="plus fitted icon" + aria-hidden="true" + /> + <div + v-if="showAddFeature" + class="menu left transition visible" + style="z-index: 9999" + > + <div class="header"> + Ajouter un signalement du type + </div> + <div class="scrolling menu text-wrap"> + <router-link + v-for="(type, index) in feature_types" + :key="type.slug + index" + :to="{ + name: 'ajouter-signalement', + params: { slug_type_signal: type.slug }, + }" + class="item" + > + {{ type.title }} + </router-link> + </div> + </div> + </div> + <div + v-if="checkedFeatures.length > 0 && massMode === 'modify'" + class="ui dropdown button compact button-hover-green margin-left-25" + data-tooltip="Modifier le statut des Signalements" + data-position="bottom right" + @click="toggleModifyStatus" + > + <i + class="pencil fitted icon" + aria-hidden="true" + /> + <div + v-if="showModifyStatus" + class="menu left transition visible" + style="z-index: 9999" + > + <div class="header"> + Modifier le statut des Signalements + </div> + <div class="scrolling menu text-wrap"> + <span + v-for="status in availableStatus" + :key="status.value" + class="item" + @click="modifyStatus(status.value)" + > + {{ status.name }} + </span> + </div> + </div> + </div> + <div + v-if="checkedFeatures.length > 0 && massMode === 'delete'" + class="ui button compact button-hover-red margin-left-25" + data-tooltip="Supprimer tous les signalements sélectionnés" + data-position="bottom right" + @click="toggleDeleteModal" + > + <i + class="grey trash fitted icon" + aria-hidden="true" + /> + </div> + </div> + </div> + </div> + </div> + <section + id="form-filters" + class="ui form grid equal width" + > + <div class="field column no-margin-mobile"> + <label>Type</label> + <Dropdown + :options="featureTypeChoices" + :selected="form.type.selected" + :selection.sync="form.type.selected" + :search="true" + :clearable="true" + /> + </div> + <div class="field column no-padding-mobile no-margin-mobile"> + <label>Statut</label> + <!-- //* giving an object mapped on key name --> + <Dropdown + :options="filteredStatusChoices" + :selected="form.status.selected.name" + :selection.sync="form.status.selected" + :search="true" + :clearable="true" + /> + </div> + <div class="field column"> + <label>Nom</label> + <div class="ui icon input"> + <i + class="search icon" + aria-hidden="true" + /> + <div class="ui action input"> + <input + v-model="form.title" + type="text" + name="title" + @keyup.enter="resetPaginationNfetchFeatures" + > + <button + id="submit-search" + class="ui teal icon button" + @click="resetPaginationNfetchFeatures" + > + <i + class="search icon" + aria-hidden="true" + /> + </button> + </div> + </div> + </div> + <!-- map params, updated on map move --> + <input + v-model="zoom" + type="hidden" + name="zoom" + > + <input + v-model="lat" + type="hidden" + name="lat" + > + <input + v-model="lng" + type="hidden" + name="lng" + > + </section> + </div> +</template> + +<script> +import { mapState, mapGetters } from 'vuex'; + +import { allowedStatus2change } from '@/utils'; + +import Dropdown from '@/components/Dropdown.vue'; + +const initialPagination = { + currentPage: 1, + pagesize: 15, + start: 0, + end: 15, +}; + +export default { + + name: 'FeaturesListAndMapFilters', + + components: { + Dropdown + }, + + props: { + showMap: { + type: Boolean, + default: true + }, + pagination: { + type: Object, + default: () => { + return { + ...initialPagination + }; + } + } + }, + + data() { + return { + featuresCount: 0, + form: { + type: { + selected: '', + }, + status: { + selected: '', + }, + title: null, + }, + lat: null, + lng: null, + showAddFeature: false, + showModifyStatus: false, + zoom: null, + }; + }, + + computed: { + ...mapState([ + 'user', + 'USER_LEVEL_PROJECTS' + ]), + ...mapState('feature', [ + 'checkedFeatures', + 'statusChoices', + 'massMode', + ]), + ...mapState('feature-type', [ + 'feature_types', + ]), + ...mapState('projects', [ + 'project', + ]), + ...mapGetters([ + 'permissions', + ]), + + availableStatus() { + if (this.project && this.user) { + const isModerate = this.project.moderation; + const userStatus = this.USER_LEVEL_PROJECTS[this.project.slug]; + const isOwnFeature = true; //* dans ce cas le contributeur est toujours l'auteur des signalements qu'il peut modifier + return allowedStatus2change(this.statusChoices, isModerate, userStatus, isOwnFeature); + } + return []; + }, + featureTypeChoices() { + return this.feature_types.map((el) => el.title); + }, + filteredStatusChoices() { + //* if project is not moderate, remove pending status + return this.statusChoices.filter((el) => + this.project && this.project.moderation ? true : el.value !== 'pending' + ); + }, + }, + + watch: { + 'form.type.selected'(newValue) { + this.$emit('set-filter', { type: newValue }); + this.resetPaginationNfetchFeatures(); + }, + 'form.status.selected': { + deep: true, + handler(newValue) { + this.$emit('set-filter', { status: newValue }); + this.resetPaginationNfetchFeatures(); + } + }, + 'form.title'(newValue) { + this.$emit('set-filter', { title: newValue }); + this.resetPaginationNfetchFeatures(); + }, + }, + + methods: { + resetPaginationNfetchFeatures() { + this.$emit('reset-pagination'); + this.$emit('fetch-features'); + }, + + toggleAddFeature() { + this.showAddFeature = !this.showAddFeature; + this.showModifyStatus = false; + }, + } + +}; +</script> + +<style lang="less" scoped> + +#feature-list-container { + display: flex; + justify-content: space-between; + margin: 0 1em; + + .no-padding-mobile { + width: 100%; + margin-left: 25%; + + .secondary.menu { + + #button-dropdown { + z-index: 1; + margin-right: 0; + padding-right: 0; + } + } + } +} + +#form-filters { + margin: 0; +} + +.ui.dropdown .menu .left.menu, .ui.dropdown > .left.menu .menu { + margin-right: 0 !important; +} + +@media screen and (max-width: 767px) { + #feature-list-container > .mobile-fullwidth { + width: 100% !important; + } + .no-margin-mobile { + margin: 0 !important; + } + .no-padding-mobile { + padding-top: 0 !important; + padding-bottom: 0 !important; + margin-left: 0 !important; + } + .mobile-column { + flex-direction: column !important; + } + #form-filters > .field.column { + width: 100% !important; + } + .map-container { + width: 100%; + } +} + +</style> diff --git a/src/components/Project/FeaturesListAndMap/FeaturesMap.vue b/src/components/Project/FeaturesListAndMap/FeaturesMap.vue deleted file mode 100644 index 75b80c9a..00000000 --- a/src/components/Project/FeaturesListAndMap/FeaturesMap.vue +++ /dev/null @@ -1,11 +0,0 @@ -<template> - -</template> - -<script> -export default { - - name: 'FeaturesMap', - -} -</script> diff --git a/src/views/Project/FeaturesListAndMap.vue b/src/views/Project/FeaturesListAndMap.vue index 2ca7ddb1..82b1ac22 100644 --- a/src/views/Project/FeaturesListAndMap.vue +++ b/src/views/Project/FeaturesListAndMap.vue @@ -1,236 +1,44 @@ <template> <div id="project-features" - class="page" + class="page grid" > - <div - id="feature-list-container" - class="ui grid mobile-column" - > - <div class="mobile-fullwidth"> - <h1>Signalements</h1> - </div> - <div class="no-padding-mobile mobile-fullwidth"> - <div class="ui large text loader"> - Chargement - </div> - <div class="ui secondary menu no-margin"> - <a - :class="['item no-margin', { active: showMap }]" - data-tab="map" - data-tooltip="Carte" - data-position="bottom left" - @click="showMap = true" - > - <i - class="map fitted icon" - aria-hidden="true" - /> - </a> - <a - :class="['item no-margin', { active: !showMap }]" - data-tab="list" - data-tooltip="Liste" - data-position="bottom left" - @click="showMap = false" - > - <i - class="list fitted icon" - aria-hidden="true" - /> - </a> - <div class="item"> - <h4> - {{ featuresCount }} signalement{{ featuresCount > 1 ? "s" : "" }} - </h4> - </div> - - <div - v-if=" - project && - feature_types.length > 0 && - permissions.can_create_feature - " - id="button-dropdown" - class="item right" - > - <div - class="ui dropdown button compact button-hover-green" - data-tooltip="Ajouter un signalement" - data-position="bottom right" - @click="toggleAddFeature" - > - <i - class="plus fitted icon" - aria-hidden="true" - /> - <div - v-if="showAddFeature" - class="menu left transition visible" - style="z-index: 9999" - > - <div class="header"> - Ajouter un signalement du type - </div> - <div class="scrolling menu text-wrap"> - <router-link - v-for="(type, index) in feature_types" - :key="type.slug + index" - :to="{ - name: 'ajouter-signalement', - params: { slug_type_signal: type.slug }, - }" - class="item" - > - {{ type.title }} - </router-link> - </div> - </div> - </div> - - <div - v-if="checkedFeatures.length > 0 && massMode === 'modify'" - class="ui dropdown button compact button-hover-green margin-left-25" - data-tooltip="Modifier le statut des Signalements" - data-position="bottom right" - @click="toggleModifyStatus" - > - <i - class="pencil fitted icon" - aria-hidden="true" - /> - <div - v-if="showModifyStatus" - class="menu left transition visible" - style="z-index: 9999" - > - <div class="header"> - Modifier le statut des Signalements - </div> - <div class="scrolling menu text-wrap"> - <span - v-for="status in availableStatus" - :key="status.value" - class="item" - @click="modifyStatus(status.value)" - > - {{ status.name }} - </span> - </div> - </div> - </div> - - <div - v-if="checkedFeatures.length > 0 && massMode === 'delete'" - class="ui button compact button-hover-red margin-left-25" - data-tooltip="Supprimer tous les signalements sélectionnés" - data-position="bottom right" - @click="toggleDeleteModal" - > - <i - class="grey trash fitted icon" - aria-hidden="true" - /> - </div> - </div> - </div> - </div> - </div> + <div class="column"> + <FeaturesListAndMapFilters + :show-map="showMap" + :pagination="pagination" + @set-filter="setFilters" + @reset-pagination="resetPagination" + @fetch-features="fetchPagedFeatures" + @show-map="setShowMap" + /> - <section - id="form-filters" - class="ui form grid" - > - <div class="field wide four column no-margin-mobile"> - <label>Type</label> - <Dropdown - :options="featureTypeChoices" - :selected="form.type.selected" - :selection.sync="form.type.selected" - :search="true" - :clearable="true" - /> - </div> - <div class="field wide four column no-padding-mobile no-margin-mobile"> - <label>Statut</label> - <!-- //* giving an object mapped on key name --> - <Dropdown - :options="filteredStatusChoices" - :selected="form.status.selected.name" - :selection.sync="form.status.selected" - :search="true" - :clearable="true" + <div + v-if="showMap" + class="ui tab active map-container visible" + data-tab="map" + > + <div class="hider" /> + <div + id="map" + ref="map" /> + <SidebarLayers v-if="basemaps && map" /> </div> - <div class="field wide four column"> - <label>Nom</label> - <div class="ui icon input"> - <i - class="search icon" - aria-hidden="true" - /> - <div class="ui action input"> - <input - v-model="form.title" - type="text" - name="title" - @keyup.enter="resetPaginationNfetchFeatures" - > - <button - id="submit-search" - class="ui teal icon button" - @click="resetPaginationNfetchFeatures" - > - <i - class="search icon" - aria-hidden="true" - /> - </button> - </div> - </div> - </div> - <!-- map params, updated on map move --> - <input - v-model="zoom" - type="hidden" - name="zoom" - > - <input - v-model="lat" - type="hidden" - name="lat" - > - <input - v-model="lng" - type="hidden" - name="lng" - > - </section> - <div - v-if="showMap" - class="ui tab active map-container visible" - data-tab="map" - > - <div - id="map" - ref="map" + <FeatureListTable + v-else + :paginated-features="paginatedFeatures" + :page-numbers="pageNumbers" + :checked-features.sync="checkedFeatures" + :features-count="featuresCount" + :pagination="pagination" + :sort="sort" + @update:page="handlePageChange" + @update:sort="handleSortChange" /> - <SidebarLayers v-if="basemaps && map" /> </div> - <FeatureListTable - v-else - :paginated-features="paginatedFeatures" - :page-numbers="pageNumbers" - :checked-features.sync="checkedFeatures" - :features-count="featuresCount" - :pagination="pagination" - :sort="sort" - @update:page="handlePageChange" - @update:sort="handleSortChange" - /> - <!-- MODAL ALL DELETE FEATURE TYPE --> <div v-if="isDeleteModalOpen" @@ -273,15 +81,14 @@ <script> -import { mapState, mapGetters, mapActions, mapMutations } from 'vuex'; +import { mapState, mapActions, mapMutations } from 'vuex'; import { mapUtil } from '@/assets/js/map-util.js'; -import { allowedStatus2change } from '@/utils'; import featureAPI from '@/services/feature-api'; +import FeaturesListAndMapFilters from '@/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters'; import SidebarLayers from '@/components/SidebarLayers'; import FeatureListTable from '@/components/Project/FeaturesListAndMap/FeatureListTable'; -import Dropdown from '@/components/Dropdown.vue'; const initialPagination = { currentPage: 1, @@ -294,8 +101,8 @@ export default { name: 'FeaturesListAndMap', components: { + FeaturesListAndMapFilters, SidebarLayers, - Dropdown, FeatureListTable, }, @@ -331,21 +138,12 @@ export default { }, computed: { - ...mapState([ - 'user', - 'USER_LEVEL_PROJECTS' - ]), - ...mapGetters([ - 'permissions', - ]), ...mapState('projects', [ 'project', ]), ...mapState('feature', [ 'checkedFeatures', 'clickedFeatures', - 'statusChoices', - 'massMode', ]), ...mapState('feature-type', [ 'feature_types', @@ -358,38 +156,12 @@ export default { return this.$store.state.configuration.VUE_APP_DJANGO_API_BASE; }, - filteredStatusChoices() { - //* if project is not moderate, remove pending status - return this.statusChoices.filter((el) => - this.project && this.project.moderation ? true : el.value !== 'pending' - ); - }, - availableStatus() { - if (this.project && this.user) { - const isModerate = this.project.moderation; - const userStatus = this.USER_LEVEL_PROJECTS[this.project.slug]; - const isOwnFeature = true; //* dans ce cas le contributeur est toujours l'auteur des signalements qu'il peut modifier - return allowedStatus2change(this.statusChoices, isModerate, userStatus, isOwnFeature); - } - return []; - }, - - featureTypeChoices() { - return this.feature_types.map((el) => el.title); - }, - pageNumbers() { return this.createPagesArray(this.featuresCount, this.pagination.pagesize); }, }, watch: { - 'form.type.selected'() { - this.resetPaginationNfetchFeatures(); - }, - 'form.status.selected.value'() { - this.resetPaginationNfetchFeatures(); - }, map(newValue) { if (newValue && this.paginatedFeatures && this.paginatedFeatures.length) { if (this.currentLayer) { @@ -458,9 +230,20 @@ export default { 'UPDATE_CHECKED_FEATURES' ]), - toggleAddFeature() { - this.showAddFeature = !this.showAddFeature; - this.showModifyStatus = false; + setShowMap(e) { + this.showMap = e; + }, + resetPagination() { + this.pagination = { ...initialPagination }; + }, + setFilters(e) { + const filter = Object.keys(e)[0]; + const value = Object.values(e)[0]; + if (filter === 'title') { + this.form[filter] = value; + } else { + this.form[filter].selected = value; + } }, toggleModifyStatus() { @@ -667,11 +450,6 @@ export default { }); }, - resetPaginationNfetchFeatures() { - this.pagination = { ...initialPagination }; - this.fetchPagedFeatures(); - }, - //* Pagination for table *// createPagesArray(featuresCount, pagesize) { @@ -744,79 +522,44 @@ export default { margin: 2em auto 1em; } -#map { - width: 100%; - min-height: 300px; - height: calc(100vh - 300px); - border: 1px solid grey; - /* To not hide the filters */ - z-index: 1; -} - -#feature-list-container { - justify-content: flex-start; -} - -#feature-list-container .ui.menu:not(.vertical) .right.item { - padding-right: 0; -} - .map-container { - width: 80vw; - transform: translateX(-50%); - margin-left: 50%; visibility: hidden; - position: absolute; } .map-container.visible { visibility: visible; - position: initial; -} - -.margin-left-25 { - margin-left: 0.25em !important; -} - -.no-padding { - padding: 0 !important; -} + position: relative; + width: calc(100% - 1em); + + .hider { + position: absolute; + width: calc(1em - 1px); + height: 100%; + background-color: white; + z-index: 999999999; + } -.ui.dropdown .menu .left.menu, .ui.dropdown > .left.menu .menu { - margin-right: 0 !important; -} + .sidebar-container { + left: calc(-250px + 1em); + } -#button-dropdown { - z-index: 1; -} + .sidebar-container.expanded { + left: 1em; + } -@media screen and (min-width: 767px) { - .twelve-wide { - width: 75% !important; + #map { + width: 100%; + min-height: 300px; + height: calc(100vh - 300px); + border: 1px solid grey; + /* To not hide the filters */ + z-index: 1; } } @media screen and (max-width: 767px) { - #feature-list-container > .mobile-fullwidth { - width: 100% !important; - } - .no-margin-mobile { - margin: 0 !important; - } - .no-padding-mobile { - padding-top: 0 !important; - padding-bottom: 0 !important; - } - .mobile-column { - flex-direction: column !important; - } - #button-dropdown { - transform: translate(-50px, -60px); - } - #form-filters > .field.column { - width: 100% !important; - } .map-container { width: 100%; + position: relative; } } </style> diff --git a/src/views/Project/ProjectDetail.vue b/src/views/Project/ProjectDetail.vue index e90652ea..0ae1fa9e 100644 --- a/src/views/Project/ProjectDetail.vue +++ b/src/views/Project/ProjectDetail.vue @@ -51,7 +51,7 @@ @updateLocalStorage="updateLocalStorage" /> - <div class="ui grid"> + <div class="ui grid stackable"> <div class="row"> <div class="eight wide column"> <ProjectFeatureTypes diff --git a/src/views/Projects/ProjectsList.vue b/src/views/Projects/ProjectsList.vue index a888a0eb..3c63c147 100644 --- a/src/views/Projects/ProjectsList.vue +++ b/src/views/Projects/ProjectsList.vue @@ -74,14 +74,9 @@ <span v-if="!projects || projects.length === 0" - >Vous n'avez accès à aucun projet.</span> - - <div - :class="{ active: loading }" - class="ui inverted dimmer" > - <div class="ui loader" /> - </div> + Vous n'avez accès à aucun projet. + </span> <!-- PAGINATION --> <Pagination @@ -206,6 +201,16 @@ export default { #projects { margin: 0 auto; + + .dimmable { + + .dimmer { + + .loader { + top: 25%; + } + } + } } .flex { -- GitLab