Vincent 4 月之前
父节点
当前提交
50098ba697

+ 48 - 6
components/Form/Freemium/Event.vue

@@ -60,13 +60,13 @@
         <h4 class="mb-8">{{ $t('place_event') }}</h4>
 
         <UiInputAutocompleteApiResources
-          v-model="entity.place"
+          :model-value="entity.place"
           field="place"
           :model="PlaceSearchItem"
           listValue="id"
           listLabel="name"
           v-if="!newPlace"
-          @update:model-value="getPlace(entity)"
+          @update:model-value="getPlace($event, entity)"
         />
 
         <div class="d-flex justify-center"
@@ -74,15 +74,16 @@
         >
           <v-btn
             prepend-icon="fa-solid fa-plus"
-            class="my-5"
+            class="mb-6"
             @click="onAddPlaceClick(entity)"
           >
             {{ $t('add_place') }}
           </v-btn>
 
           <v-btn
+            v-if="entity.place && !editPlace"
             prepend-icon="fa-solid fa-plus"
-            class="my-5"
+            class="mb-6 ml-6"
             @click="onEditPlaceClick(entity)"
           >
             {{ $t('edit_place') }}
@@ -191,11 +192,24 @@ const props = defineProps<{
 const {em} = useEntityManager()
 const getAsserts = (key) => getAssertUtils(Event.getAsserts(), key)
 
+/**
+ * Si la date de début est mise à jour, on s'assure que la date de fin est
+ * après elle, sinon elle devient égale
+ * @param entity
+ * @param dateTime
+ */
 const onUpdateDateTimeStart = (entity, dateTime) =>{
   if(DateUtils.isBefore(props.entity.datetimeEnd, dateTime, false)){
     entity.datetimeEnd = dateTime
   }
 }
+
+/**
+ * Si la date de fin est mise à jour, on s'assure que la date de début est
+ * avant elle, sinon elle devient égale
+ * @param entity
+ * @param dateTime
+ */
 const onUpdateDateTimeEnd = (entity, dateTime) =>{
   if(DateUtils.isBefore(dateTime, props.entity.datetimeStart, false)){
     entity.datetimeStart = dateTime
@@ -205,27 +219,50 @@ const onUpdateDateTimeEnd = (entity, dateTime) =>{
 const showAlert: Ref<boolean> = ref(false)
 const newPlace: Ref<boolean> = ref(false)
 const editPlace: Ref<boolean> = ref(false)
+
+/**
+ * Si on clic sur le bouton "Ajouter un lieu", le choix dans la liste déroulante
+ * est mise à nulle, et les champs input de l'adresse sont vidées
+ * @param entity
+ */
 const onAddPlaceClick = function(entity: Event){
   newPlace.value = true
   entity.place = null
   resetPlace(entity)
 }
 
+/**
+ * Quand on clic sur le bouton "Editer le lieu", une alerte s'affiche
+ */
 const onEditPlaceClick = function(){
   showAlert.value = true
 }
 
+/**
+ * Quand on ferme la boite de dialogue
+ */
 const closeDialog = function(){
   showAlert.value = false
 }
 
+/**
+ * Si on décide d'éditer le lieu
+ */
 const onEditPlaceConfirm = function(){
   showAlert.value = false
   editPlace.value = true
 }
 
-const getPlace = async (entity: Event)=>{
-  if(entity.place){
+const emit = defineEmits(['update:model-value'])
+/**
+ * Lorsque l'on choisit un lieu dans la liste déroulante, on mets a jour les champs
+ * input de l'adresse
+ * @param placeId
+ * @param entity
+ */
+const getPlace = async (placeId: number, entity: Event)=>{
+  if(placeId){
+    entity.place = placeId
     const placeInstance = await em.fetch(Place, entity.place as number)
     entity.placeName = placeInstance.name
     entity.streetAddress = placeInstance.streetAddress
@@ -238,10 +275,15 @@ const getPlace = async (entity: Event)=>{
     entity.longitude = placeInstance.longitude
     editPlace.value = false
   }else{
+    //Dans le cas où l'on ne récupère aucune place on remet a null le formulaire de l'adresse
     resetPlace(entity)
   }
 }
 
+/**
+ * fonction permettant de remettre à vide tous les champs input de l'adresse
+ * @param entity
+ */
 const resetPlace = (entity: Event)=>{
   entity.placeName = null
   entity.streetAddress = null

+ 0 - 107
components/Ui/Input/Autocomplete.vue

@@ -42,9 +42,6 @@ Liste déroulante avec autocompletion, à placer dans un composant `UiForm`
       @update:menu="emit('update:menu', $event)"
       @update:focused="emit('update:focused', $event)"
     >
-      <template v-if="slotText" #item="{ item }">
-        <div>{{ item.slotTextDisplay }}</div>
-      </template>
     </v-autocomplete>
   </main>
 </template>
@@ -52,8 +49,6 @@ Liste déroulante avec autocompletion, à placer dans un composant `UiForm`
 <script setup lang="ts">
 import type { Ref, PropType } from 'vue'
 import { useFieldViolation } from '~/composables/form/useFieldViolation'
-import ObjectUtils from '~/services/utils/objectUtils'
-import type { AnyJson } from '~/types/data'
 
 const props = defineProps({
   /**
@@ -182,12 +177,6 @@ const props = defineProps({
     required: false,
     default: false,
   },
-  // TODO: c'est quoi?
-  slotText: {
-    type: Array,
-    required: false,
-    default: null,
-  },
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-no-filter
    */
@@ -202,11 +191,6 @@ const props = defineProps({
     type: Boolean,
     default: true,
   },
-  // TODO: c'est quoi?
-  translate: {
-    type: Boolean,
-    default: false,
-  },
   /**
    * Règles de validation
    * @see https://vuetify.cn/en/components/forms/#validation-with-submit-clear
@@ -258,8 +242,6 @@ const props = defineProps({
   },
 })
 
-const i18n = useI18n()
-
 const search: Ref<string | null> = ref(null)
 
 const fieldLabel: string = props.label ?? props.field
@@ -278,95 +260,6 @@ const onUpdate = (event: string) => {
   emit('update:model-value', event)
 }
 
-/**
- * On construit l'Array à double entrée contenant les groups (headers) et les items
- * TODO: à revoir
- *
- * @param items
- */
-const groupItems = (items: Array<AnyJson>): Array<Array<string>> => {
-  const group = props.group as string | null
-  if (group === null) {
-    return items
-  }
-
-  const itemsByGroup: Array<Array<string>> = []
-  let groupValue = null
-
-  for (const item of items) {
-    if (item) {
-      groupValue = item[group]
-
-      if (!itemsByGroup[groupValue]) {
-        itemsByGroup[groupValue] = []
-      }
-
-      itemsByGroup[groupValue].push(item)
-    }
-  }
-
-  return itemsByGroup
-}
-
-/**
- * Construction de l'Array JSON contenant toutes les propositions à afficher dans le select
- * TODO: à revoir
- *
- * @param groupedItems
- */
-const prepareGroups = (groupedItems: Array<Array<string>>): Array<AnyJson> => {
-  let finalItems: Array<AnyJson> = []
-
-  for (const group in groupedItems) {
-    // Si un groupe est présent, alors on créé le groupe options header
-    if (group !== 'undefined') {
-      finalItems.push({ header: i18n.t(group as string) })
-    }
-
-    // On parcourt les items pour préparer les texts / slotTexts à afficher
-    finalItems = finalItems.concat(
-      groupedItems[group].map((item: AnyJson) => {
-        return prepareItem(item)
-      }),
-    )
-  }
-  return finalItems
-}
-
-/**
- * Construction d'un item
- * TODO: à revoir
- *
- * @param item
- */
-const prepareItem = (item: object): AnyJson => {
-  const slotTextDisplay: Array<string> = []
-  const itemTextDisplay: Array<string> = []
-
-  item = ObjectUtils.cloneAndFlatten(item)
-
-  // Si on souhaite avoir un texte différent dans les propositions que dans la sélection finale de select
-  if (props.slotText) {
-    for (const text of props.slotText) {
-      slotTextDisplay.push(
-        props.translate ? i18n.t(item[text as string]) : item[text as string],
-      )
-    }
-  }
-
-  for (const text of props.itemTitle) {
-    itemTextDisplay.push(
-      props.translate ? i18n.t(item[text as string]) : item[text as string],
-    )
-  }
-
-  // On reconstruit l'objet
-  return Object.assign({}, item, {
-    itemTextDisplay: itemTextDisplay.join(' '),
-    slotTextDisplay: slotTextDisplay.join(' '),
-  })
-}
-
 onUnmounted(() => {
   updateViolationState()
   search.value = null

+ 46 - 30
components/Ui/Input/Autocomplete/ApiResources.vue

@@ -23,7 +23,6 @@ Champs autocomplete dédié à la recherche des Accesses d'une structure
       :variant="variant"
       :readonly="readonly"
       :clearable="true"
-      :class="pending || pageStore.loading ? 'hide-selection' : ''"
       @update:model-value="onUpdateModelValue"
       @update:search="onUpdateSearch"
     />
@@ -44,11 +43,17 @@ import SearchFilter from '~/services/data/Filters/SearchFilter'
 import type ApiModel from "~/models/ApiModel";
 
 const props = defineProps({
+  /**
+   * Valeur actives (tableau si multiple ou ID seule si choix unique)
+   */
   modelValue: {
     type: [Array, Number],
     required: false,
     default: null,
   },
+  /**
+   * API Resource qui sera fetch
+   */
   model: {
     type: Function as PropType<() => typeof ApiModel>,
     required: true,
@@ -155,32 +160,36 @@ interface ListItem {
 
 const { fetchCollection } = useEntityFetch()
 const i18n = useI18n()
-const pageStore = usePageStore()
+
+
+const activeIds = computed(() => {
+  if (Array.isArray(props.modelValue)) {
+    return props.modelValue
+  }
+  if (props.modelValue !== null && typeof props.modelValue === 'object') {
+    return [props.modelValue]
+  }
+  return []
+})
+
 
 /**
- * Génère un AccessListItem à partir d'un Access
- * @param searchItem
+ * Query transmise à l'API lors de l'initialisation afin de récupérer les items actifs
  */
-const item = (searchItem: any): ListItem => {
-  return {
-    id: searchItem[props.listValue],
-    title: searchItem[props.listLabel]
-      ? searchItem[props.listLabel]
-      : `(${i18n.t('missing_value')})`,
-  }
-}
 const queryActive = new Query(
-  new OrderBy(props.listLabel, ORDER_BY_DIRECTION.ASC),
-  new PageFilter(ref(1), ref(20)),
-  new InArrayFilter(props.listValue, [props.modelValue]),
-)
+    new OrderBy(props.listLabel, ORDER_BY_DIRECTION.ASC),
+    new PageFilter(ref(1), ref(20)),
+    new InArrayFilter(props.listValue, activeIds),
+  )
 
+/**
+ * On commence par fetcher les models déjà actifs, pour affichage des labels correspondant
+ */
 const {
   data: collectionActive,
   status: statusActive
 } = fetchCollection(props.model, null, queryActive)
 
-
 /**
  * Saisie de l'utilisateur utilisée pour filtrer la recherche
  */
@@ -194,9 +203,8 @@ const querySearch = new Query(
   new PageFilter(ref(1), ref(20)),
   new SearchFilter(props.listLabel, searchFilter, SEARCH_STRATEGY.IPARTIAL),
 )
-
 /**
- * On commence par fetcher les accesses déjà actifs, pour affichage des noms
+ * On fetch les résultats correspondants à la recherche faite par l'utilisateur
  */
 const {
   data: collectionSearch,
@@ -204,10 +212,24 @@ const {
   refresh: refreshSearch,
 } = fetchCollection(props.model, null, querySearch)
 
+//Le pending global dépend des deux recherche (actif, et globale)
 const pending = computed(() => statusSearch.value == FETCHING_STATUS.PENDING || statusActive.value == FETCHING_STATUS.PENDING)
 
 /**
- * Contenu de la liste autocomplete
+ * Génère un ListItem à partir des props
+ * @param searchItem
+ */
+const item = (searchItem: any): ListItem => {
+  return {
+    id: searchItem[props.listValue],
+    title: searchItem[props.listLabel]
+      ? searchItem[props.listLabel]
+      : `(${i18n.t('missing_value')})`,
+  }
+}
+
+/**
+ * Contenu de la liste autocomplete : Les items actifs + les items correspondants à la recherche
  */
 const items: ComputedRef<Array<ListItem>> = computed(() => {
   if (pending.value || !(collectionActive.value && collectionSearch.value)) {
@@ -257,6 +279,10 @@ const onUpdateSearch = (event: string) => {
   }
 }
 
+/**
+ * Quand un item est sélectionné
+ * @param event
+ */
 const onUpdateModelValue = (event: Array<number>) => {
   if (props.clearSearchAfterUpdate) {
     searchFilter.value = ''
@@ -278,14 +304,4 @@ onBeforeUnmount(() => {
 .v-autocomplete {
   min-width: 350px;
 }
-
-.hide-selection {
-  /**
-      On cache le contenu au chargement en attendant de résoudre le bug qui fait
-      que ce sont les ids ou les IRIs qui s'affichent le temps du chargement
-   */
-  :deep(.v-chip__content) {
-    color: transparent !important;
-  }
-}
 </style>

+ 12 - 2
components/Ui/MapLeaflet.client.vue

@@ -102,13 +102,23 @@ const props = defineProps({
   },
 })
 
+const FRANCE_LATTITUDE = 46.603354
+const FRANCE_LONGITUDE = 1.888334
 const {apiRequestService, pending} = useAp2iRequestService()
 const { em } = useEntityManager()
 
 const position:ComputedRef<PointTuple> = computed(()=>{
-  return [props.latitude || 12, props.longitude || 12]
+  return [props.latitude || FRANCE_LATTITUDE, props.longitude || FRANCE_LONGITUDE]
+})
+
+const zoom = computed({
+  get() {
+    return props.latitude && props.latitude != FRANCE_LATTITUDE ? 12 : 5
+  },
+  set(newValue: string) {
+    zoom.value = newValue
+  }
 })
-const zoom = ref(12)
 
 const emit = defineEmits(['update:latitude', 'update:longitude'])