Skip to content

Commit 9031e8b

Browse files
authored
Merge pull request #350 from Seluj78/348-history-page
348 history page
2 parents 06ec567 + c65d8a3 commit 9031e8b

File tree

11 files changed

+433
-28
lines changed

11 files changed

+433
-28
lines changed

frontend/src/App.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div id="app" class="antialiased" v-bind:style="{background: getBackground}">
3-
<keep-alive include="Browse,Search">
3+
<keep-alive include="Browse,Search,History">
44
<router-view/>
55
</keep-alive>
66
</div>

frontend/src/assets/css/tailwind.css

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
@apply block;
88
@apply px-2;
99
@apply py-2;
10-
@apply font-semibold;
10+
@apply my-1;
1111
@apply text-purple-matcha;
1212
@apply rounded-md;
1313
}
@@ -21,8 +21,17 @@
2121
@apply text-white-matcha;
2222
}
2323
@screen sm {
24+
.navigation-button-logged-in {
25+
@apply mx-1;
26+
@apply my-0;
27+
}
28+
}
29+
30+
@screen md {
2431
.navigation-button-logged-in {
2532
@apply px-5;
33+
@apply mx-1;
34+
@apply my-0;
2635
}
2736
}
2837

@@ -408,6 +417,7 @@
408417
@apply absolute;
409418
@apply rounded-md;
410419
@apply bg-white;
420+
@apply z-10;
411421
}
412422

413423
.sort-dropdown-option {
@@ -470,6 +480,7 @@
470480
@apply rounded-md;
471481
@apply bg-white;
472482
@apply outline-none;
483+
@apply z-10;
473484
}
474485

475486
@screen md {
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
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>

frontend/src/components/shared/DropdownDisplayChoice.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<!-- eslint-disable max-len -->
33
<div class="focus:outline-none sm:flex-none" @focusout="close" tabindex="0">
44
<div v-bind:class="{'settings-button-choice': true, 'border-gray-matcha': !closed}" @click="toggle">
5-
<h1 v-bind:class="{ 'opacity-50': closed, 'noSelect': true, 'capitalize': true }">{{ currentOption }}<span class="ml-2">▼</span></h1>
5+
<h1 v-bind:class="{ 'opacity-50': closed, 'noSelect': true, 'capitalize': true }">{{ currentOption }}<span v-if="closed" class="ml-2">▼</span><span v-if="!closed" class="ml-2">▲</span></h1>
66
</div>
77
<div v-bind:class="{'sort-dropdown': true, 'max-w-xs': true, 'mx-auto': true, 'left-0': position === 'left' || position === 'center', 'right-0': position === 'right' || position === 'center', 'z-10': true, 'hidden': closed, 'h-auto': options.length < 5}">
88
<h1 v-for="(option, index) in options" :key="option + index + option"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<template>
2+
<!-- eslint-disable max-len -->
3+
<div class="focus:outline-none sm:flex-none cursor-pointer" @focusout="close" tabindex="0">
4+
<div v-bind:class="{'onboarding-sub-container-content-heading': true, 'border': true, 'px-4': true, 'py-2': true, 'rounded-md': true, 'text-center': true, 'text-2xl': true, 'sm:text-5xl': true, 'border-gray-matcha': !closed}" @click="toggle">
5+
<h1 v-bind:class="{ 'opacity-100': closed, 'noSelect': true, 'capitalize': true }">{{ currentOption }}<span v-if="closed" class="ml-2">▼</span><span v-if="!closed" class="ml-2">▲</span></h1>
6+
</div>
7+
<div v-bind:class="{'sort-dropdown': true, 'max-w-xs': true, 'mx-auto': true, 'left-0': position === 'left' || position === 'center', 'right-0': position === 'right' || position === 'center', 'z-10': true, 'hidden': closed, 'h-auto': options.length < 5}">
8+
<h1 v-for="(option, index) in options" :key="option + index + option"
9+
v-bind:class="{'capitalize': true, 'sort-dropdown-option': true, 'border-b': index !== options.length - 1, 'font-extrabold': option === currentOption, 'text-gray-matcha': option === currentOption}"
10+
v-on:click="select(option)">
11+
{{option}}
12+
</h1>
13+
</div>
14+
</div>
15+
</template>
16+
17+
<script>
18+
export default {
19+
props: ['options', 'name', 'position', 'startingOption'],
20+
data: () => ({
21+
closed: true,
22+
currentOption: '',
23+
}),
24+
methods: {
25+
select(option) {
26+
this.close();
27+
this.currentOption = option;
28+
this.$emit('save-single-choice', this.name, option);
29+
},
30+
toggle() {
31+
this.closed = !this.closed;
32+
},
33+
close() {
34+
this.closed = true;
35+
},
36+
},
37+
beforeMount() {
38+
this.currentOption = this.startingOption;
39+
},
40+
deactivated() {
41+
if (!this.$route.path.startsWith('/users')) {
42+
this.currentOption = this.startingOption;
43+
}
44+
},
45+
};
46+
</script>

frontend/src/components/shared/DropdownDisplayChoices.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<div class="focus:outline-none ml-2 sm:flex-none" @focusout="close" tabindex="0">
44
<div v-bind:class="{'settings-button-choice': true, 'rounded-lg': true , 'border-gray-matcha': !closed}" @click="toggle">
55
<h1 v-bind:class="{ 'opacity-50': closed, 'noSelect': true, 'capitalize': true }">
6-
{{ getName }}<span class="ml-2">▼</span></h1>
6+
{{ getName }}<span v-if="closed" class="ml-2">▼</span><span v-if="!closed" class="ml-2">▲</span></h1>
77
</div>
88
<div v-bind:class="{'sort-dropdown': true, 'z-10': true, 'h-auto': options.length < 5 , 'hidden': closed, 'right-0': position === 'right', 'md:right-auto': position === 'right'}">
99
<h1 v-for="(option, index) in options" :key="option + index + option"

frontend/src/components/shared/NavBar.vue

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
<div id="logo">
77
<router-link to="/">
88
<div class="flex justify-center items-center">
9-
<img src="../../assets/logo.png" class="h-10">
10-
<h1 class="text-purple-matcha text-2xl font-bold ml-2">Matcha</h1>
9+
<img src="../../assets/logo.png" class="h-6">
10+
<h1 v-if="!loggedIn" class="text-purple-matcha text-xl ml-2">Matcha</h1>
1111
</div>
1212
</router-link>
1313
</div>
@@ -22,13 +22,14 @@
2222
</div>
2323
<div id="links" v-on:click="isOpen = !isOpen" v-bind:class="isOpen ? 'block' : 'hidden'" class="px-2 pb-5 text-center sm:p-0 sm:block sm:flex sm:items-center">
2424
<div v-if="!loggedIn" class="sm:flex sm:bg-purple-matcha sm:border-2 sm:border-purple-matcha sm:rounded-lg">
25-
<router-link to="/accounts/signin" class="navigation-button-logged-in sm:hover:bg-white-matcha sm:hover:text-purple-matcha sm:text-purple-matcha sm:bg-white-matcha sm:py-2 sm:px-8 sm:rounded-md">Sign In</router-link>
25+
<router-link to="/accounts/signin" class="navigation-button-logged-in sm:hover:bg-white-matcha sm:hover:text-purple-matcha sm:text-purple-matcha sm:bg-white-matcha sm:py-2 sm:px-8 sm:rounded-md mx-0">Sign In</router-link>
2626
<router-link to="/accounts/signup" class="navigation-button-logged-in sm:hover:bg-purple-matcha sm:hover:text-white-matcha sm:text-white-matcha sm:py-2 sm:px-8">Get Started</router-link>
2727
</div>
28-
<router-link v-if="loggedIn && currentRoute !== 'Browse'" to="/browse" class="navigation-button-logged-in">Browse</router-link>
29-
<router-link v-if="loggedIn && currentRoute !== 'Search'" to="/search" class="navigation-button-logged-in">Search</router-link>
30-
<router-link v-if="loggedIn" to="/" class="navigation-button-logged-in">Matches</router-link>
31-
<router-link v-if="loggedIn && currentRoute !== 'Settings'" to="/settings" class="navigation-button-logged-in">Settings</router-link>
28+
<router-link v-if="loggedIn" to="/browse" v-bind:class="{'navigation-button-logged-in': true, 'bg-purple-matcha': currentRoute === 'Browse', 'text-white-matcha': currentRoute === 'Browse'}">Browse</router-link>
29+
<router-link v-if="loggedIn" to="/search" v-bind:class="{'navigation-button-logged-in': true, 'bg-purple-matcha': currentRoute === 'Search', 'text-white-matcha': currentRoute === 'Search'}">Search</router-link>
30+
<router-link v-if="loggedIn" to="/matches" v-bind:class="{'navigation-button-logged-in': true, 'bg-purple-matcha': currentRoute === 'Matches', 'text-white-matcha': currentRoute === 'Matches'}" class="navigation-button-logged-in">Matches</router-link>
31+
<router-link v-if="loggedIn" to="/settings" v-bind:class="{'navigation-button-logged-in': true, 'bg-purple-matcha': currentRoute === 'Settings', 'text-white-matcha': currentRoute === 'Settings'}">Settings</router-link>
32+
<router-link v-if="loggedIn" to="/history" v-bind:class="{'navigation-button-logged-in': true, 'bg-purple-matcha': currentRoute === 'History', 'text-white-matcha': currentRoute === 'History'}">History</router-link>
3233
<router-link v-if="loggedIn" v-on:click.native="logout()" to="/" class="navigation-button-logged-in">Exit</router-link>
3334
</div>
3435
</nav>

frontend/src/main.js

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import Vue from 'vue';
22
import { ValidationProvider, ValidationObserver, extend } from 'vee-validate/dist/vee-validate.full.esm';
3-
import * as Sentry from '@sentry/browser';
4-
import { Vue as VueIntegration } from '@sentry/integrations';
5-
import { Integrations } from '@sentry/tracing';
63
import http from './plugins/http';
74
import errorMessenger from './plugins/errorMessenger';
85
import App from './App.vue';
@@ -11,18 +8,6 @@ import store from './store';
118
import validPassword from './validators/validPassword';
129
import './assets/css/tailwind.css';
1310

14-
Sentry.init({
15-
dsn: 'https://fc31e918801742e2b1e691067496e257@o450203.ingest.sentry.io/5434440',
16-
integrations: [
17-
new VueIntegration({
18-
Vue,
19-
tracing: true,
20-
}),
21-
new Integrations.BrowserTracing(),
22-
],
23-
tracesSampleRate: 1,
24-
});
25-
2611
Vue.component('ValidationProvider', ValidationProvider);
2712
Vue.component('ValidationObserver', ValidationObserver);
2813
extend('validPassword', validPassword);

frontend/src/router/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import Browse from '../views/app/Browse.vue';
1313
import Search from '../views/app/Search.vue';
1414
import Settings from '../views/app/Settings.vue';
1515
import User from '../components/app/users/User.vue';
16+
import History from '../views/app/History.vue';
1617
import store from '../store/index';
1718

1819
Vue.use(VueRouter);
@@ -120,6 +121,11 @@ const routes = [
120121
component: User,
121122
beforeEnter: notLoggedInRedirectLogin,
122123
},
124+
{
125+
path: '/history',
126+
component: History,
127+
beforeEnter: notLoggedInRedirectLogin,
128+
},
123129
];
124130

125131
const router = new VueRouter({

0 commit comments

Comments
 (0)