Jelajahi Sumber

finalize data provider and filters for public events

Olivier Massot 3 tahun lalu
induk
melakukan
3c73e90b16

+ 0 - 162
components/Ui/Search/DateRangePickerCustom.vue

@@ -1,162 +0,0 @@
-<template >
-  <div v-click-outside="onOutsideClick">
-    <v-text-field
-      v-model="dateIntervalText"
-      type="text"
-      class="text-field"
-      outlined
-      readonly
-      clearable
-      hide-details
-      :label="$t('when') + ' ?'"
-      append-icon="mdi-calendar"
-      @click="onClick"
-      @change="$emit('change', $event ? $event.value : '')"
-      @click:append="onClick"
-    />
-
-    <v-card v-show="show" class="date-picker pa-2">
-      <div class="d-flex flex-row">
-        <div class="presets">
-          <a
-            v-for="preset in presets"
-            class="preset-link"
-            @click="presetClicked(preset.range)"
-          >
-            {{ preset.label }}
-          </a>
-        </div>
-
-        <v-date-picker
-          ref="datePicker1"
-          :value="isoDateStart"
-          range
-          :min="today"
-          scrollable
-          no-title
-          locale="fr"
-          @change="dateRangeChanged"
-        />
-      </div>
-    </v-card>
-  </div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue'
-import { format, parse, addDays, addMonths, nextSunday } from 'date-fns'
-
-interface DateRangePreset {
-  label: string,
-  range: DateRange
-}
-
-export default Vue.extend({
-  props: {
-    value: {
-      type: Object
-    }
-  },
-  data () {
-    return {
-      model: null as DateRange | null,
-      dateStart: null as Date | null,
-      dateEnd: null as Date | null,
-      show: false
-    }
-  },
-  methods: {
-    onClick() {
-      this.show = !this.show
-    },
-    onOutsideClick() {
-      this.show = false
-    },
-    presetClicked(range: DateRange) {
-      this.dateStart = range.start
-      this.dateEnd = range.end
-    },
-    clear () {
-      this.dateStart = null
-      this.dateEnd = null
-    },
-    dateToString(date: Date | null) {
-      return date !== null ? format(date, 'yyyy-MM-dd') : ''
-    },
-    stringToDate(date: string) {
-      return new Date(date)
-    }
-  },
-  computed: {
-    today() {
-      return format(new Date(), 'yyyy-MM-dd')
-    },
-    presets (): Array<DateRangePreset> {
-      const today = new Date()
-
-      // Today
-      const today_preset: DateRangePreset = {
-        label: this.$t('today').toString(),
-        range: {start: today, end: today}
-      }
-
-      // Cette semaine
-      const week_preset: DateRangePreset = {
-        label: this.$t('next_week').toString(),
-        range: {start: today, end: addDays(today, 7)}
-      }
-
-      // Ce week-end
-      const sunday: Date = nextSunday(today)
-      const weekend_preset = {
-        label: this.$t('next_weekend').toString(),
-        range: {start: addDays(sunday, -1), end: sunday}
-      }
-
-      // Ce mois
-      const month_preset: DateRangePreset = {
-        label: this.$t('next_month').toString(),
-        range: {start: today, end: addMonths(today, 1)}
-      }
-
-      return [today_preset, week_preset, weekend_preset, month_preset]
-    },
-    dateIntervalText (): string {
-      if (this.dateStart !== null && this.dateEnd !== null) {
-        return format(this.dateStart, 'dd/MM/yyyy') + ' ~ ' + format(this.dateEnd, 'dd/MM/yyyy')
-      } else if (this.dateStart !== null) {
-        return format(this.dateStart, 'dd/MM/yyyy') + ' ~ ?'
-      } else if (this.dateEnd !== null) {
-        return '? ~ ' + format(this.dateEnd, 'dd/MM/yyyy')
-      } else {
-        return ''
-      }
-    }
-  }
-})
-</script>
-
-<style scoped>
-
-.date-picker {
-  z-index: 1;
-  position: absolute;
-}
-
-.presets {
-  display: flex;
-  flex-direction: column;
-  padding: 12px;
-  min-width: 120px;
-  font-size: 14px;
-}
-
-.preset-link {
-  padding: 12px 0;
-}
-
-.preset-link:hover {
-  text-decoration: underline;
-}
-
-</style>

+ 29 - 58
pages/events/index.vue

@@ -66,7 +66,7 @@
               </v-btn>
             </v-col>
             <v-col cols="2" class="py-2 px-1 d-flex justify-end">
-              <v-btn class="h100" @click="test()">
+              <v-btn class="h100" @click="search()">
                 {{ $t('search') }}
               </v-btn>
             </v-col>
@@ -88,7 +88,7 @@
       <!-- Results -->
       <v-data-iterator
         v-else
-        :items="filteredEvents"
+        :items="events"
         :page.sync="page"
         :items-per-page="itemsPerPage"
         sort-by="name"
@@ -155,9 +155,10 @@
         <template #footer>
           <v-pagination
             v-model="page"
-            :length="pageCount"
+            :length="pagesCount"
             total-visible="9"
             color="primary"
+            @input="pageUpdated"
           />
         </template>
       </v-data-iterator>
@@ -167,7 +168,6 @@
 
 <script lang="ts">
 import Vue from 'vue'
-import sphericDistance from '@/services/utils/geo'
 import EventsProvider from "~/services/data/EventsProvider"
 import { today, todayIso, formatIso } from '@/services/utils/date'
 import { addDays, nextSunday, addMonths } from "date-fns";
@@ -180,29 +180,36 @@ export default Vue.extend({
       theme: this.$route.query.theme ?? 'orange',
       hideTitle: this.$route.query.hideTitle === 'true',
       events: [] as Array<PublicEvent>,
-      filteredEvents: [] as Array<PublicEvent>,
       loading: true,
       page: 1,
-      itemsPerPage: 8,
+      itemsPerPage: 16,
       textFilter: null as string | null,
       locationFilter: null as Coordinates | null,
-      dateRangeFilter: defaultDateRange
+      dateRangeFilter: defaultDateRange,
+      totalRecords: 0 as number,
+      pagesCount: 1 as number | null,
     }
   },
   async fetch () {
-    await new EventsProvider(this.$axios).getBy().then(
-      (res) => {
-        this.events = res
-        this.filteredEvents = res
+    this.loading = true
+    await new EventsProvider(this.$axios).getBy(
+      this.textFilter,
+      null,
+      this.dateRangeFilter.start,
+      this.dateRangeFilter.end,
+      null,
+      this.page,
+      this.itemsPerPage
+    ).then(
+      (collection: HydraCollection<PublicEvent>) => {
+        this.events = collection.items
+        this.loading = false
+        this.totalRecords = collection.totalItems
+        this.page = collection.page ?? 1
+        this.pagesCount = collection.lastPage ?? 1
       })
   },
   computed: {
-    totalRecords (): number {
-      return this.filteredEvents.length
-    },
-    pageCount (): number {
-      return Math.floor(this.totalRecords / this.itemsPerPage) + 1
-    },
     dateRangeMin(): string {
       return todayIso()
     },
@@ -236,9 +243,6 @@ export default Vue.extend({
     }
   },
   methods: {
-    test () {
-      console.log(this.dateRangeFilter)
-    },
     textFilterChanged (newVal: string) {
       this.textFilter = newVal
     },
@@ -256,50 +260,17 @@ export default Vue.extend({
       this.dateRangeFilter = defaultDateRange
       const addressSearch = this.$refs.addressSearch as any
       addressSearch.clear()
-      this.filteredEvents = this.events
-    },
-    /**
-     * Does the event match the current textFilter
-     * @param event
-     * @returns {boolean}
-     */
-    matchTextFilter (event: PublicEvent): boolean {
-      if (!this.textFilter) { return true }
-
-      return normalize(event.name).includes(normalize(this.textFilter))
-    },
-    /**
-     * Does the event match the current locationFilter
-     * @param event
-     * @returns {boolean}
-     */
-    matchLocationFilter (event: PublicEvent): boolean {
-      if (!this.locationFilter) { return true }
-      if (event.address === null || !event.address.latitude || !event.address.longitude) { return false }
-
-      const radius = 30
-
-      return sphericDistance(
-        this.locationFilter.latitude,
-        this.locationFilter.longitude,
-        event.address.latitude,
-        event.address.longitude
-      ) <= radius
+      this.search()
     },
-    /**
-     * Does the event match each of the page filters
-     * @param event
-     * @returns {boolean}
-     */
-    matchFilters (event: PublicEvent): boolean {
-      return this.matchTextFilter(event) &&
-        this.matchLocationFilter(event)
+    pageUpdated (page: number): void {
+      this.page = page
+      this.search()
     },
     /**
      * Update the filteredEvents array
      */
     search (): void {
-      this.filteredEvents = this.events.filter((e) => { return this.matchFilters(e) })
+      this.$fetch()
     },
     /**
      * Enhanced filter for v-autocomplete components

+ 25 - 9
services/data/EventsProvider.ts

@@ -1,5 +1,6 @@
 import BaseProvider from '~/services/data/BaseProvider'
 import Address from "~/components/Ui/Search/Address.vue";
+import HydraParser from "~/services/data/HydraParser";
 
 class PublicEventsProvider extends BaseProvider {
   protected normalize (e: any) : PublicEvent {
@@ -15,6 +16,14 @@ class PublicEventsProvider extends BaseProvider {
     } as Address
     // s.categories = s.categories.split()
 
+    delete e['@id']
+    delete e['@type']
+    delete e.latitude
+    delete e.longitude
+    delete e.streetAddress
+    delete e.postalCode
+    delete e.addressCity
+
     return e
   }
 
@@ -23,8 +32,10 @@ class PublicEventsProvider extends BaseProvider {
     organizationId: number | null = null,
     dateMin: string | null = null,
     dateMax: string | null = null,
-    city: string | null = null
-  ): Promise<Array<PublicEvent>> {
+    city: string | null = null,
+    page: number = 1,
+    itemsPerPage = 20
+  ): Promise<HydraCollection<PublicEvent>> {
 
     const query = new URLSearchParams();
     if (name !== null) {
@@ -33,24 +44,29 @@ class PublicEventsProvider extends BaseProvider {
     if (organizationId !== null) {
       query.append('organizationId', String(organizationId))
     }
-    if (dateMin !== null) {
-      query.append('dateStart[after]', dateMin)
+    if (dateMin !== null && dateMin !== '') {
+      query.append('datetimeStart[after]', dateMin)
     }
-    if (dateMax !== null) {
-      query.append('dateEnd[before]', dateMax)
+    if (dateMax !== null && dateMax !== '') {
+      query.append('datetimeEnd[before]', dateMax)
     }
     if (city !== null) {
       query.append('city', city)
     }
+    if (page !== null) {
+      query.append('page', `${page}`)
+    }
+    if (itemsPerPage !== null) {
+      query.append('itemsPerPage', `${itemsPerPage}`)
+    }
 
     let url = `/api/public/events`
     if (query) {
-      url += `?{query}`
+      url += `?${query}`
     }
 
     return await this.get(url).then((res) => {
-      console.log(res)
-      return res['hydra:member'].map((s: any) => { return this.normalize(s) })
+      return HydraParser.parseCollection(res, this.normalize)
     })
   }
 

+ 28 - 0
services/data/HydraParser.ts

@@ -0,0 +1,28 @@
+
+
+
+
+class HydraParser {
+  static parseCollection<T>(data: any, normalize: ((s: object) => T)): HydraCollection<T> {
+
+    const view = data['hydra:view'] as any
+
+    return {
+      iri: data['@id'] as string,
+      totalItems: parseInt(data['hydra:totalItems']) ?? 0,
+      page: this.getPageFromIri(view['@id'] ?? ''),
+      previousPage: this.getPageFromIri(view['hydra:previous'] ?? ''),
+      nextPage: this.getPageFromIri(view['hydra:next'] ?? ''),
+      firstPage: this.getPageFromIri(view['hydra:first'] ?? ''),
+      lastPage: this.getPageFromIri(view['hydra:last'] ?? ''),
+      items: data['hydra:member'].map((s: any) => { return normalize(s) }),
+    }
+  }
+
+  private static getPageFromIri(iri: string): number | null {
+    const match = iri.match(/page=(\d+)/)
+    return match ? parseInt(match[1]) : null;
+  }
+}
+
+export default HydraParser

+ 11 - 0
types/interfaces.d.ts

@@ -87,3 +87,14 @@ interface DateRangePreset {
   label: string,
   range: DateRange
 }
+
+interface HydraCollection<T> {
+  iri: string,
+  totalItems: number,
+  page: number | null,
+  previousPage: number | null,
+  nextPage: number | null,
+  firstPage: number | null,
+  lastPage: number | null
+  items: Array<T>,
+}