|
| 1 | +<template> |
| 2 | + <!-- eslint-disable max-len --> |
| 3 | + <section class="mb-4 sm:mb-16 lg:mb-32"> |
| 4 | + <div v-if="recommendationsBackup.length" v-bind:class="{ |
| 5 | + 'flex': true, |
| 6 | + 'w-full': true, |
| 7 | + 'items-stretch': true, |
| 8 | + 'sm:items-center': true, |
| 9 | + 'justify-center': true, |
| 10 | + 'md:justify-start': true, |
| 11 | + 'mb-12': recommendationsBackup.length > 1, |
| 12 | + 'mb-6': recommendationsBackup.length === 1, |
| 13 | + 'relative': true}"> |
| 14 | + <Sort |
| 15 | + v-if="recommendationsBackup.length > 1" |
| 16 | + v-bind:position="'left'" |
| 17 | + v-bind:startingOption="'Closest'" |
| 18 | + v-bind:options="['Closest', 'Furthest', 'Youngest', |
| 19 | + 'Oldest', 'Most popular', 'Least popular', 'Most common interests', 'Least common interests']" |
| 20 | + v-on:save-sort="saveSort"></Sort> |
| 21 | + <FilterSliderDropdown |
| 22 | + v-if="recommendationsBackup.length > 1 && recommendationsAnalysis.age.min !== recommendationsAnalysis.age.max" |
| 23 | + v-bind:min="recommendationsAnalysis.age.min" |
| 24 | + v-bind:max="recommendationsAnalysis.age.max" |
| 25 | + v-bind:name="'age'" |
| 26 | + v-on:save-filter="saveFilter"></FilterSliderDropdown> |
| 27 | + <FilterSliderDropdown |
| 28 | + v-if="recommendationsBackup.length > 1 && recommendationsAnalysis.distance.min !== recommendationsAnalysis.distance.max" |
| 29 | + v-bind:min="recommendationsAnalysis.distance.min" |
| 30 | + v-bind:max="recommendationsAnalysis.distance.max" |
| 31 | + v-bind:unit="'km'" |
| 32 | + v-bind:name="'distance'" |
| 33 | + v-on:save-filter="saveFilter"></FilterSliderDropdown> |
| 34 | + <FilterSliderDropdown |
| 35 | + v-if="recommendationsBackup.length > 1 && recommendationsAnalysis.popularity.min !== recommendationsAnalysis.popularity.max" |
| 36 | + v-bind:min="recommendationsAnalysis.popularity.min" |
| 37 | + v-bind:max="recommendationsAnalysis.popularity.max" |
| 38 | + v-bind:unit="'pts'" |
| 39 | + v-bind:name="'popularity'" |
| 40 | + v-on:save-filter="saveFilter"></FilterSliderDropdown> |
| 41 | + <MultipleFiltersDropdown |
| 42 | + v-if="recommendationsBackup.length > 1" |
| 43 | + v-bind:position="'right'" |
| 44 | + v-bind:options="recommendationsAnalysis.interests" |
| 45 | + v-bind:name="'interests'" |
| 46 | + v-on:save-filter-multiple="saveFilterMultiple"></MultipleFiltersDropdown> |
| 47 | + </div> |
| 48 | + <div v-if="recommendations.length" ref="recommendationCards" class="grid grid-cols-1 md:grid-cols-2 gap-2"> |
| 49 | + <RecommendationCard |
| 50 | + v-for="(recommendation, index) in recommendations" :key="index" |
| 51 | + v-bind:recommendation="recommendation"></RecommendationCard> |
| 52 | + </div> |
| 53 | + <div class="text-center md:text-left px-4 md:px-0" v-if="!recommendations.length"> |
| 54 | + <h1 style="opacity: 0.085; font-size: 10vh;" class="uppercase leading-none">"Some things</h1> |
| 55 | + <h1 style="opacity: 0.085; font-size: 10vh;" class="uppercase leading-none">take time"</h1> |
| 56 | + </div> |
| 57 | + </section> |
| 58 | +</template> |
| 59 | + |
| 60 | +<script> |
| 61 | +/* eslint-disable */ |
| 62 | +import Sort from '@/components/shared/Sort.vue'; |
| 63 | +import FilterSliderDropdown from '@/components/shared/FilterSliderDropdown.vue'; |
| 64 | +import RecommendationCard from '@/components/app/recommendations/RecommendationCard.vue'; |
| 65 | +import MultipleFiltersDropdown from '@/components/shared/MultipleFiltersDropdown.vue'; |
| 66 | +
|
| 67 | +export default { |
| 68 | + props: ['title', 'recommendationsReceived', 'recommendationsAnalysis'], |
| 69 | + components: { |
| 70 | + Sort, |
| 71 | + RecommendationCard, |
| 72 | + FilterSliderDropdown, |
| 73 | + MultipleFiltersDropdown, |
| 74 | + }, |
| 75 | + data: () => ({ |
| 76 | + recommendations: [], |
| 77 | + recommendationsBackup: [], |
| 78 | + sorting: 'Closest', |
| 79 | + filters: { |
| 80 | + age: { |
| 81 | + min: null, |
| 82 | + max: null, |
| 83 | + }, |
| 84 | + distance: { |
| 85 | + min: null, |
| 86 | + max: null, |
| 87 | + }, |
| 88 | + popularity: { |
| 89 | + min: null, |
| 90 | + max: null, |
| 91 | + }, |
| 92 | + interests: [], |
| 93 | + }, |
| 94 | + }), |
| 95 | + methods: { |
| 96 | + saveSort(...args) { |
| 97 | + const [by] = args; |
| 98 | + this.sorting = by; |
| 99 | + }, |
| 100 | + saveFilter(...range) { |
| 101 | + const [name, min, max] = range; |
| 102 | + this.filters[name].min = min; |
| 103 | + this.filters[name].max = max; |
| 104 | + }, |
| 105 | + saveFilterMultiple(...multiple) { |
| 106 | + const [name, filters] = multiple; |
| 107 | + this.filters[name] = filters; |
| 108 | + }, |
| 109 | + sort(recommendations, by) { |
| 110 | + if (by === 'Closest') { |
| 111 | + recommendations.sort((a, b) => a.distance - b.distance); |
| 112 | + } else if (by === 'Furthest') { |
| 113 | + recommendations.sort((a, b) => b.distance - a.distance); |
| 114 | + } else if (by === 'Youngest') { |
| 115 | + recommendations.sort((a, b) => a.age - b.age); |
| 116 | + } else if (by === 'Oldest') { |
| 117 | + recommendations.sort((a, b) => b.age - a.age); |
| 118 | + } else if (by === 'Most popular') { |
| 119 | + recommendations.sort((a, b) => b.heat_score - a.heat_score); |
| 120 | + } else if (by === 'Least popular') { |
| 121 | + recommendations.sort((a, b) => a.heat_score - b.heat_score); |
| 122 | + } else if (by === 'Most common interests') { |
| 123 | + recommendations.sort((a, b) => b.common_tags.length - a.common_tags.length); |
| 124 | + } else if (by === 'Least common interests') { |
| 125 | + recommendations.sort((a, b) => a.common_tags.length - b.common_tags.length); |
| 126 | + } |
| 127 | + }, |
| 128 | + filter(recommendations) { |
| 129 | + let i = recommendations.length; |
| 130 | + while (i--) { |
| 131 | + if (!this.meetsFilters(recommendations[i])) { |
| 132 | + recommendations.splice(i, 1); |
| 133 | + } |
| 134 | + } |
| 135 | + return recommendations; |
| 136 | + }, |
| 137 | + meetsFilters(recommendation) { |
| 138 | + return this.meetsAge(recommendation) |
| 139 | + && this.meetsPopularity(recommendation) |
| 140 | + && this.meetsDistance(recommendation) |
| 141 | + && this.meetsInterests(recommendation); |
| 142 | + }, |
| 143 | + meetsAge(recommendation) { |
| 144 | + return recommendation.age >= this.filters.age.min |
| 145 | + && recommendation.age <= this.filters.age.max; |
| 146 | + }, |
| 147 | + meetsPopularity(recommendation) { |
| 148 | + return recommendation.heat_score >= this.filters.popularity.min |
| 149 | + && recommendation.heat_score <= this.filters.popularity.max; |
| 150 | + }, |
| 151 | + meetsDistance(recommendation) { |
| 152 | + return recommendation.distance >= this.filters.distance.min |
| 153 | + && recommendation.distance <= this.filters.distance.max; |
| 154 | + }, |
| 155 | + meetsInterests(recommendation) { |
| 156 | + const recommendationInterests = recommendation.interests; |
| 157 | + const filterInterests = this.filters.interests; |
| 158 | + for (let i = 0; i < filterInterests.length; i += 1) { |
| 159 | + if (recommendationInterests.indexOf(filterInterests[i]) === -1) { |
| 160 | + return false; |
| 161 | + } |
| 162 | + } |
| 163 | + return true; |
| 164 | + }, |
| 165 | + saveSingleChoice(...args) { |
| 166 | + const [key, value] = args; |
| 167 | + if (key === 'history') { |
| 168 | + this.$emit('update-history', value); |
| 169 | + } |
| 170 | + }, |
| 171 | + setupRecommendations() { |
| 172 | + this.recommendations = this.recommendationsReceived; |
| 173 | + this.recommendationsBackup = this.recommendations; |
| 174 | + this.filters.age.min = this.recommendationsAnalysis.age.min; |
| 175 | + this.filters.age.max = this.recommendationsAnalysis.age.max; |
| 176 | + this.filters.distance.min = this.recommendationsAnalysis.distance.min; |
| 177 | + this.filters.distance.max = this.recommendationsAnalysis.distance.max; |
| 178 | + this.filters.popularity.min = this.recommendationsAnalysis.popularity.min; |
| 179 | + this.filters.popularity.max = this.recommendationsAnalysis.popularity.max; |
| 180 | + }, |
| 181 | + }, |
| 182 | + watch: { |
| 183 | + sorting: { |
| 184 | + handler() { |
| 185 | + this.sort(this.recommendations, this.sorting); |
| 186 | + }, |
| 187 | + }, |
| 188 | + filters: { |
| 189 | + handler() { |
| 190 | + let base = this.recommendationsBackup.slice(); |
| 191 | + base = this.filter(base); |
| 192 | + this.sort(base, this.sorting); |
| 193 | + this.recommendations = base; |
| 194 | + this.$emit('filtered-count', this.recommendations.length); |
| 195 | + }, |
| 196 | + deep: true, |
| 197 | + }, |
| 198 | + }, |
| 199 | + beforeMount() { |
| 200 | + this.setupRecommendations(); |
| 201 | + }, |
| 202 | +}; |
| 203 | +</script> |
0 commit comments