Browse Source

:recycle: making single profile view route | cleaning up more of the survey form

tags/0.0.1
toj 4 years ago
parent
commit
8560427fdc

+ 3
- 3
backend/db/data-generator/mock.js View File

@@ -85,7 +85,7 @@ module.exports = {
85 85
         },
86 86
         {
87 87
             response_key_id: 7,
88
-            response_key_category: 'locationPref',
88
+            response_key_category: 'profile',
89 89
             response_key_prompt: 'what is your zip code',
90 90
             response_key_description: null,
91 91
         },
@@ -142,6 +142,6 @@ module.exports = {
142 142
             profile_id: 45,
143 143
             target_id: 3,
144 144
             is_deleted: false,
145
-        }
145
+        },
146 146
     ],
147
-}
147
+}

+ 2
- 0
backend/lib/plugins/profile.js View File

@@ -19,6 +19,7 @@ const ProfileUpdateRoute = require('../routes/profile/update')
19 19
 const ProfileRespondRoute = require('../routes/profile/respond')
20 20
 const ProfileMatchRoute = require('../routes/profile/match')
21 21
 const ProfileQueueRoute = require('../routes/profile/queue')
22
+const ProfileGetRoute = require('../routes/profile/get')
22 23
 const ProfilePatchQueueRoute = require('../routes/profile/patch-queue')
23 24
 
24 25
 module.exports = {
@@ -50,6 +51,7 @@ module.exports = {
50 51
         await server.route(ProfileUpdateRoute)
51 52
         await server.route(ProfileMatchRoute)
52 53
         await server.route(ProfileQueueRoute)
54
+        await server.route(ProfileGetRoute)
53 55
         await server.route(ProfilePatchQueueRoute)
54 56
     },
55 57
 }

+ 84
- 0
backend/lib/routes/profile/get.js View File

@@ -0,0 +1,84 @@
1
+'use strict'
2
+
3
+const Joi = require('joi')
4
+const apiSchema = require('../../schemas/api')
5
+const errorSchema = require('../../schemas/errors')
6
+
7
+const pluginConfig = {
8
+    handlerType: 'profile',
9
+    docs: {
10
+        description: 'Returns a single profile with tags',
11
+        notes: 'returns from the Profiles Table',
12
+    },
13
+}
14
+
15
+const responseSchemas = {
16
+    profile: Joi.object({
17
+        profile_id: Joi.number(),
18
+        user_id: Joi.number(),
19
+        user_name: Joi.string(),
20
+        responses: Joi.array().items(),
21
+        tags: Joi.array().items(),
22
+        user_media: Joi.string(),
23
+        user_type: Joi.any(),
24
+        user: Joi.object(),
25
+    }),
26
+    error: errorSchema.single,
27
+}
28
+
29
+const validators = {
30
+    params: Joi.object({ profile_id: Joi.number() }),
31
+}
32
+
33
+module.exports = {
34
+    method: 'GET',
35
+    path: '/{profile_id}',
36
+    options: {
37
+        ...pluginConfig.docs,
38
+        tags: ['api'],
39
+        /** Protect this route with authentication? */
40
+        auth: false,
41
+        cors: true,
42
+        handler: async function (request, h) {
43
+            const { profile_id } = request.params
44
+            const { profileService } = request.server.services()
45
+
46
+            const res = {
47
+                ok: true,
48
+                handler: pluginConfig.handlerType,
49
+                data: null,
50
+            }
51
+
52
+            res.data = await profileService.getProfile(profile_id)
53
+
54
+            try {
55
+                return h.response(res).code(200)
56
+            } catch (err) {
57
+                return h
58
+                    .response({
59
+                        ok: false,
60
+                        handler: pluginConfig.handlerType,
61
+                        data: { error: `${err}` },
62
+                    })
63
+                    .code(409)
64
+            }
65
+        },
66
+        /** Validate based on validators object */
67
+        validate: {
68
+            ...validators,
69
+            failAction: 'log',
70
+        },
71
+
72
+        /** Validate the server response */
73
+        response: {
74
+            status: {
75
+                200: apiSchema.single.append({
76
+                    data: responseSchemas.profile,
77
+                }),
78
+                409: apiSchema.single.append({
79
+                    data: responseSchemas.error,
80
+                }),
81
+            },
82
+        },
83
+    },
84
+}

+ 66
- 33
backend/lib/services/profile.js View File

@@ -7,12 +7,12 @@ const scoreResponses = (seeker, potentialMatch, prescoreLookup) => {
7 7
         return {
8 8
             error: `complete responses for profile: ${seeker.profile_id} unqeual to profile: ${potentialMatch.profile_id} | ${seeker.responses.length}:${potentialMatch.responses.length}`,
9 9
         }
10
-    
10
+
11 11
     const aRes = [...seeker.responses]
12 12
     const bRes = [...potentialMatch.responses]
13 13
 
14 14
     const composite = []
15
-    while(aRes.length + bRes.length > 0) {
15
+    while (aRes.length + bRes.length > 0) {
16 16
         const mKey = resList => {
17 17
             let el = resList.shift()
18 18
             let pair = el.val
@@ -21,7 +21,10 @@ const scoreResponses = (seeker, potentialMatch, prescoreLookup) => {
21 21
         }
22 22
         composite.push(prescoreLookup[mKey(aRes)][mKey(bRes)])
23 23
     }
24
-    return { total: Math.round(composite.reduce((a, b) => a + b) / composite.length), aspects: composite }
24
+    return {
25
+        total: Math.round(composite.reduce((a, b) => a + b) / composite.length),
26
+        aspects: composite,
27
+    }
25 28
 }
26 29
 const filterByDistance = (profileList, max) => {
27 30
     return profileList.filter(profile => {
@@ -60,19 +63,18 @@ const getZipCodeFromProfile = profile => {
60 63
 
61 64
 const makeScoreLookup = (aspects, labels) => {
62 65
     const labelLookup = {}
63
-    labels.forEach(label => labelLookup[label.aspect_id] = label)
66
+    labels.forEach(label => (labelLookup[label.aspect_id] = label))
64 67
 
65 68
     const scoreLookup = {}
66 69
     aspects.forEach(aspect => {
67 70
         const key = labelLookup[aspect.aspect_id]
68 71
         scoreLookup[`${key.a}:${key.b}`] = {}
69 72
         Object.keys(aspect).forEach(aspect_id => {
70
-            if(!labelLookup[aspect_id]) return
73
+            if (!labelLookup[aspect_id]) return
71 74
             const comp = labelLookup[aspect_id]
72 75
             const score = aspect[aspect_id]
73 76
             scoreLookup[`${key.a}:${key.b}`][`${comp.a}:${comp.b}`] = score
74 77
         })
75
-        
76 78
     })
77 79
     return scoreLookup
78 80
 }
@@ -101,7 +103,7 @@ module.exports = class ProfileService extends Schmervice.Service {
101 103
         this.tagLookup = {}
102 104
     }
103 105
     async _setScoreLookup() {
104
-        if(!Object.keys(this.scoreLookup).length) {
106
+        if (!Object.keys(this.scoreLookup).length) {
105 107
             const { Aspect, AspectLabel } = this.server.models()
106 108
             const aspects = await Aspect.query()
107 109
             const labels = await AspectLabel.query()
@@ -109,13 +111,16 @@ module.exports = class ProfileService extends Schmervice.Service {
109 111
         }
110 112
     }
111 113
     async _setTagLookup() {
112
-        if(!Object.keys(this.tagLookup).length) {
114
+        if (!Object.keys(this.tagLookup).length) {
113 115
             const { Tag } = this.server.models()
114 116
             const allTagDescriptions = await Tag.query()
115
-            allTagDescriptions.forEach(desc => this.tagLookup[desc.tag_id] = {
116
-                description: desc.tag_description,
117
-                category: desc.tag_category,
118
-            })
117
+            allTagDescriptions.forEach(
118
+                desc =>
119
+                    (this.tagLookup[desc.tag_id] = {
120
+                        description: desc.tag_description,
121
+                        category: desc.tag_category,
122
+                    }),
123
+            )
119 124
         }
120 125
     }
121 126
     /**
@@ -136,12 +141,30 @@ module.exports = class ProfileService extends Schmervice.Service {
136 141
         return [...new Set(profileIdsToGrab)]
137 142
     }
138 143
 
144
+    async getProfile(profileId) {
145
+        const { Profile } = this.server.models()
146
+        await this._setTagLookup()
147
+
148
+        const matchingProfile = await Profile.query()
149
+            .where('profile_id', profileId)
150
+            .first()
151
+            .withGraphFetched('tags')
152
+            .withGraphFetched('responses')
153
+            .withGraphFetched('user')
154
+
155
+        matchingProfile.tags = matchingProfile.tags.map(
156
+            tag => this.tagLookup[tag.tag_id],
157
+        )
158
+
159
+        return new CompleteProfile(matchingProfile)
160
+    }
161
+
139 162
     async getCompleteProfilesFor(userId, type) {
140 163
         const { Profile } = this.server.models()
141 164
         await this._setTagLookup()
142 165
 
143 166
         const dedupedProfileIds = await this._getProfileIdsForUserId(userId)
144
-        
167
+
145 168
         const profilesEntries = await Profile.query()
146 169
             .whereIn('profile_id', dedupedProfileIds)
147 170
             .withGraphFetched('tags')
@@ -153,7 +176,7 @@ module.exports = class ProfileService extends Schmervice.Service {
153 176
         profilesEntries.forEach(profile => {
154 177
             profile.tags = profile.tags.map(tag => this.tagLookup[tag.tag_id])
155 178
         })
156
-        
179
+
157 180
         //** Get responses asociated with each profile_id */
158 181
         return profilesEntries.map(profile => {
159 182
             return new CompleteProfile(profile, type)
@@ -164,17 +187,18 @@ module.exports = class ProfileService extends Schmervice.Service {
164 187
         const { Profile } = this.server.models()
165 188
         await this._setScoreLookup()
166 189
         await this._setTagLookup()
167
-        
168 190
 
169
-        // profilesEntries is profiles in dataaspect_labelsbase row order 
170
-        const profilesEntries = includeResponses ? await Profile.query()
171
-            .whereIn('profile_id', profileIdArray)
172
-            .withGraphFetched('tags')
173
-            .withGraphFetched('responses')
174
-            .withGraphFetched('user') : await Profile.query()
175
-            .whereIn('profile_id', profileIdArray)
176
-            .withGraphFetched('tags')
177
-            .withGraphFetched('user') 
191
+        // profilesEntries is profiles in dataaspect_labelsbase row order
192
+        const profilesEntries = includeResponses
193
+            ? await Profile.query()
194
+                  .whereIn('profile_id', profileIdArray)
195
+                  .withGraphFetched('tags')
196
+                  .withGraphFetched('responses')
197
+                  .withGraphFetched('user')
198
+            : await Profile.query()
199
+                  .whereIn('profile_id', profileIdArray)
200
+                  .withGraphFetched('tags')
201
+                  .withGraphFetched('user')
178 202
 
179 203
         // taking the info from profilesEntries
180 204
         // to repack into completeProfiles
@@ -184,11 +208,13 @@ module.exports = class ProfileService extends Schmervice.Service {
184 208
             profilesEntries.forEach(entry => {
185 209
                 if (entry.profile_id == pid) {
186 210
                     const complete = new CompleteProfile(entry, type)
187
-                    if(!includeResponses) {
211
+                    if (!includeResponses) {
188 212
                         delete complete['responses']
189 213
                     }
190
-                    if(entry?.tags?.length){
191
-                        complete.tags = entry.tags.map(tag => this.tagLookup[tag.tag_id])
214
+                    if (entry?.tags?.length) {
215
+                        complete.tags = entry.tags.map(
216
+                            tag => this.tagLookup[tag.tag_id],
217
+                        )
192 218
                     }
193 219
                     completeProfiles.push(complete)
194 220
                 }
@@ -266,7 +292,9 @@ module.exports = class ProfileService extends Schmervice.Service {
266 292
         // Delete matches
267 293
         // ?:Maybe bad idea
268 294
         if (matchingResponses.length > 0) {
269
-            const alreadyAnswered = matchingResponses.map(matchingRes => matchingRes.response_key_id)
295
+            const alreadyAnswered = matchingResponses.map(
296
+                matchingRes => matchingRes.response_key_id,
297
+            )
270 298
             await Response.query()
271 299
                 .where({ profile_id: profileId })
272 300
                 .delete()
@@ -301,7 +329,7 @@ module.exports = class ProfileService extends Schmervice.Service {
301 329
      */
302 330
     async scoreProfilesFor(profileId, maxDistance, distanceUnit) {
303 331
         const { Profile } = this.server.models()
304
-        
332
+
305 333
         await this._setScoreLookup()
306 334
 
307 335
         // Our User Profile to score for
@@ -327,7 +355,9 @@ module.exports = class ProfileService extends Schmervice.Service {
327 355
 
328 356
         // Only include profiles that included zipcode response
329 357
         profileIdsOfOppositeType = profileIdsOfOppositeType.filter(profile => {
330
-            const zipcodeResponses = profile.responses.filter(res => res.response_key_id == _ZIPCODEKEY)
358
+            const zipcodeResponses = profile.responses.filter(
359
+                res => res.response_key_id == _ZIPCODEKEY,
360
+            )
331 361
             return zipcodeResponses.length > 0
332 362
         })
333 363
 
@@ -335,7 +365,8 @@ module.exports = class ProfileService extends Schmervice.Service {
335 365
             profileIdsOfOppositeType.map(async profile => {
336 366
                 const targetZip = getZipCodeFromProfile(profile)
337 367
 
338
-                if(!userZip || !targetZip) return { ...profile, distance: [9999, distanceUnit] }
368
+                if (!userZip || !targetZip)
369
+                    return { ...profile, distance: [9999, distanceUnit] }
339 370
 
340 371
                 const distance = await this._compareDistance(
341 372
                     userZip,
@@ -357,11 +388,13 @@ module.exports = class ProfileService extends Schmervice.Service {
357 388
         const scoredProfilesWithDistance = scoreAll(
358 389
             distanceFilteredProfiles,
359 390
             userProfile,
360
-            this.scoreLookup
391
+            this.scoreLookup,
361 392
         )
362 393
 
363 394
         // Order by score
364
-        return scoredProfilesWithDistance.sort((a, b) => b.score.total - a.score.total)
395
+        return scoredProfilesWithDistance.sort(
396
+            (a, b) => b.score.total - a.score.total,
397
+        )
365 398
     }
366 399
 
367 400
     /**

+ 0
- 60
frontend/src/components/Archive/card.vue View File

@@ -1,60 +0,0 @@
1
-<template lang="pug">
2
-.card.f-col.start.b-solid.rounded.bg-primary.mr-1
3
-    .front(v-if='!state.flipped')
4
-        header.t-right.dark.ph-1.pt-1
5
-            h3 hidden
6
-
7
-        main.ph-1
8
-            section
9
-                img.w-full(alt='Vue logo' src='@/assets/logo.png')
10
-
11
-        footer.ph-1.b-1
12
-            button.b-solid.rounded.p-0(@click='state.count++') {{ state.count }}
13
-            button.b-solid.rounded.p-0(@click='flip') flip
14
-            button.b-solid.rounded.p-0(@click='close') close
15
-    .back(v-else)
16
-        header.t-right.dark.ph-1.pt-1
17
-            h3 revealed: {{ card.name }}
18
-
19
-        main.ph-1
20
-            section
21
-                img.w-full(alt='Vue logo' src='@/assets/logo.png')
22
-
23
-        footer.ph-1.b-1
24
-            button.b-solid.rounded.p-0(@click='flip') unflip
25
-            button.b-solid.rounded.p-0(@click='close') close
26
-</template>
27
-
28
-<script setup>
29
-import { defineProps, reactive, getCurrentInstance } from 'vue'
30
-
31
-const instance = getCurrentInstance()
32
-
33
-const props = defineProps({
34
-    msg: String,
35
-    card: Object,
36
-})
37
-
38
-const state = reactive({
39
-    count: 0,
40
-    loaded: false,
41
-    profile: null,
42
-    flipped: false,
43
-})
44
-
45
-const close = e => {
46
-    instance.emit('on-remove', props.card)
47
-}
48
-const flip = e => {
49
-    state.flipped = !state.flipped
50
-}
51
-</script>
52
-
53
-<style lang="postcss">
54
-// prettier-ignore
55
-@import '@/sss/theme.sss'
56
-
57
-.card
58
-    height: $ms-8
59
-    width: $ms-7
60
-</style>

+ 0
- 19
frontend/src/components/Archive/icon.vue View File

@@ -1,19 +0,0 @@
1
-<template lang="pug">
2
-.icon
3
-</template>
4
-
5
-<script>
6
-export default {}
7
-</script>
8
-
9
-<style lang="postcss">
10
-.icon
11
-    display: block
12
-    border-radius: 50%
13
-    width: 48px
14
-    height: 48px
15
-    background-image: url('https://i.imgur.com/WFqSBgc.png')
16
-    background-color: rgba(20,20,20,0.3)
17
-    background-position: 50% 0
18
-    background-size: auto 125%
19
-</style>

+ 0
- 23
frontend/src/components/Archive/navbar.vue View File

@@ -1,23 +0,0 @@
1
-<template lang="pug">
2
-nav#nav 
3
-    router-link(to='/') Browse
4
-    router-link(to='/match') Match
5
-    router-link(to='/account') Account
6
-</template>
7
-
8
-<script>
9
-export default {}
10
-</script>
11
-
12
-<style lang="postcss">
13
-nav
14
-    display: flex
15
-    justify-content: space-evenly
16
-    background-color: #000
17
-    color: #fff
18
-    padding: 40px 0
19
-    margin: 0
20
-    position: absolute
21
-    bottom: 0
22
-    width: 100%
23
-</style>

+ 0
- 7
frontend/src/components/MainNav.vue View File

@@ -3,19 +3,12 @@ nav#main-header.w-full.f-row.around.p-2
3 3
     button(@click="$emit('show-sidebar')") show sidebar
4 4
     router-link.header__icon.mobile--only(:to="`/matches`") matches
5 5
     router-link.header__icon(to='/') home
6
-    router-link.header__icon.mobile--only(:to="`/profile`") profile
7 6
     router-link.header__icon.mobile--only(:to="`/survey`") survey
8 7
 </template>
9 8
 
10 9
 <script>
11 10
 export default {
12 11
     name: 'MainNav',
13
-    props: {
14
-        pid: {
15
-            type: Number,
16
-            required: true
17
-        }
18
-    }
19 12
 }
20 13
 </script>
21 14
 

+ 40
- 41
frontend/src/components/ProfileCardList.vue View File

@@ -3,42 +3,47 @@ section.profile_card_list.w-full
3 3
     .profile_card_list_container.w-full
4 4
         .swipe(
5 5
             v-if="!isGrid"
6
-            :config='config'
7
-            :key='profile.pid'
8
-            @throwout='swipped(profile)'
9
-            v-for='(profile, i) in profiles'
10
-            :style='{"z-index": 1000-i}'
6
+            :config="config"
7
+            :key="profile.pid"
8
+            @throwout="swipped(profile)"
9
+            v-for="(profile, i) in loadedProfiles"
10
+            :style="{ 'z-index': 1000-i }"
11 11
         )
12
-            .card.b-solid.rounded.p-0.bg-cover(
13
-                :style='{ "background-image": `url(${profile.avatar})` }'
12
+            .card.b-solid.rounded.p-0.bg-cover.randomize(
13
+                :style="{ 'background-image': `url(${profile.avatar})`, 'top': `${randomize(10)}px`, 'right': `${randomize(20)}px`, 'transform': `rotate(${randomize(7)}deg)` }"
14 14
             )
15 15
                 .card__content
16 16
                     h3.p-1.mv-0.b-solid.rounded {{ profile.pid  }} {{ profile.name }}
17 17
                 nav.swipe_icons.w-full.f-row.between
18 18
                     //- Accept
19
-                    button.p-1(@click='accept') Accept
19
+                    button.p-1(@click="accept") Accept
20 20
                     //- Hold
21
-                    button.p-1(@click='hold') H
21
+                    button.p-1(@click="view(profile.pid)") view
22 22
                     //- Pass
23
-                    button.p-1(@click='pass') Pass
23
+                    button.p-1(@click="pass") Pass
24 24
         
25 25
         .match-layout(
26 26
             v-else
27
-            :key='profile.pid'
28
-            v-for='(profile, i) in profiles'
27
+            :key="profile.pid"
28
+            v-for="(profile, i) in loadedProfiles"
29 29
         )
30 30
                 .card.b-solid.rounded.p-0.bg-cover(
31
-                    :style='{ "background-image": `url(${profile.avatar})` }'
31
+                    :style="{ 'background-image': `url(${profile.avatar})` }"
32 32
                 )
33 33
                 .card__content
34 34
                     h3.p-1.mv-0.b-solid.rounded {{ profile.pid  }} {{ profile.name }}
35 35
 </template>
36 36
 
37 37
 <script setup>
38
-import { onMounted } from '@vue/runtime-core';
39
-import { computed, defineProps, defineEmits } from 'vue'
40
-import { updateQueueByProfileId, fetchMembershipsByProfileId, postMembershipByProfileId } from '../services'
38
+import { useRouter } from 'vue-router'
39
+import { defineProps, defineEmits } from 'vue'
40
+import {
41
+    updateQueueByProfileId,
42
+    fetchMembershipsByProfileId,
43
+    postMembershipByProfileId,
44
+} from '../services'
41 45
 
46
+const router = useRouter()
42 47
 const emit = defineEmits(['reload-queue'])
43 48
 // TODO: Please review this conversion from script to script setup
44 49
 // converted from the props section
@@ -59,31 +64,21 @@ const props = defineProps({
59 64
         default: 9999,
60 65
     },
61 66
     isGrid: {
62
-        type: Boolean
63
-    }
67
+        type: Boolean,
68
+    },
64 69
 })
65
-// from the computed section
66
-const currentCard = computed(() => props.profiles[props.profiles.length - 1]);
67
-// from the method section
68
-// const logOut = () => {};
69
-const reject = () => {
70
-    swipped(currentCard)
71
-}
72
-
73
-
74 70
 const swipped = profile => {
75
-    const index = props.profiles.findIndex(u => u.pid == profile.pid)
76
-    // TODO: bug, fix this
77
-    props.profiles.splice(index, 1)
71
+    const index = loadedProfiles.findIndex(u => u.pid == profile.pid)
72
+    loadedProfiles.splice(index, 1)
78 73
     profile.id = Date.now() + (Math.random() * 100000).toFixed()
79
-    // TODO: bug, fix this
80
-    props.profiles.unshift({ ...profile })
74
+    loadedProfiles.unshift({ ...profile })
81 75
 }
82
-const getUser = () => {
83
-}
84
-const onRequest = () => {
85
-    const data = { ...currentCard }
76
+
77
+const randomize = max => {
78
+    const pos = Math.random() > 0.5 ? -1 : 1
79
+    return Math.floor(Math.random() * max) * pos
86 80
 }
81
+
87 82
 // AHP Button behavior
88 83
 
89 84
 const accept = async () => {
@@ -103,8 +98,11 @@ const accept = async () => {
103 98
     }
104 99
     emit('reload-queue')
105 100
 }
106
-const hold = () => {
107
-    console.log('held? do we need this?')
101
+const view = pid => {
102
+    router.push({
103
+        name: 'ProfileView',
104
+        params: { pid },
105
+    })
108 106
 }
109 107
 const pass = () => {
110 108
     console.log('passed aka do reinsert')
@@ -121,10 +119,9 @@ const config = {
121 119
     minThrowOutDistance: 250,
122 120
     maxThrowOutDistance: 300,
123 121
 }
124
-const favorites = []
125
-const requests = []
126
-
127 122
 
123
+// Copy profiles so we don't mutate the original
124
+const loadedProfiles = [...props.profiles]
128 125
 </script>
129 126
 
130 127
 <style lang="postcss">
@@ -134,6 +131,8 @@ const requests = []
134 131
 
135 132
 .swipe
136 133
     position: absolute
134
+.randomize
135
+    position: relative
137 136
 
138 137
 .card
139 138
     position: relative

+ 64
- 42
frontend/src/components/form.vue View File

@@ -1,63 +1,70 @@
1 1
 <template lang="pug">
2
-.form.f-col.start.mr-1.w-full(v-if='form.length')
2
+.form.f-col.start.mr-1.w-full(v-if="form.length")
3 3
     header
4 4
         p answers to save: {{ answers }}
5 5
 
6
-    ul.w-full(v-for='(step, i) in form')
6
+    ul.w-full(v-for="(step, i) in form")
7 7
         li.f-row.start.b-solid.p-0(
8
-            v-for='prompt in step'
9
-            v-if='step && step.length'
10
-            v-show='(i + 1) == state.step'
8
+            v-for="prompt in step"
9
+            v-if="step && step.length"
10
+            v-show="(i + 1) == state.step"
11 11
         )
12 12
             h3 {{ prompt.question }}?
13
-            .response-wrapper(v-if='prompt.type === "input-string"')
13
+            .response-wrapper(v-if="prompt.type === 'input-string'")
14 14
                 label {{ prompt.type }}
15
-                input(v-model='answers[makeKey(prompt)]')
16
-            .response-wrapper(v-else-if='prompt.type === "tag-cloud"')
15
+                input(v-model="answers[makeKey(prompt)]")
16
+            .response-wrapper(v-else-if="prompt.type === 'tag-cloud'")
17 17
                 label {{ prompt.type }}
18 18
                 button.p-0(
19
-                    :disabled='response == answers[makeKey(prompt)]'
20
-                    :prompt-question='makeKey(prompt)'
21
-                    @click='respondFromTag'
22
-                    v-for='response in prompt.responses'
19
+                    :disabled="response == answers[makeKey(prompt)]"
20
+                    :prompt-question="makeKey(prompt)"
21
+                    @click="respondFromTag"
22
+                    v-for="response in prompt.responses"
23 23
                 ) {{ response }}
24 24
             //- Checklist
25
-            .response-wrapper(v-if='prompt.type === "checklist"')
25
+            .response-wrapper(v-if="prompt.type === 'checklist'")
26 26
                 .checklist(
27
-                    v-for='(response, index) in prompt.responses'
28
-                    v-bind:key='response')
27
+                    v-for="(response, index) in prompt.responses"
28
+                    v-bind:key="response")
29 29
                     input(
30
-                        type='checkbox'
31
-                        :id='response'
32
-                        :value='response'
33
-                        :name='response'
34
-                        :true-value='response'
35
-                        false-value=''
36
-                        v-model='answers[makeKey(prompt)]')
37
-                    label(:for='response') {{ response }}
30
+                        type="checkbox"
31
+                        :id="response"
32
+                        :value="response"
33
+                        :name="response"
34
+                        :true-value="response"
35
+                        false-value=""
36
+                        v-model="answers[makeKey(prompt)]")
37
+                    label(:for="response") {{ response }}
38 38
             //- Slider from -3 to 0 to +3 (increments of 1)
39
-            .response-wrapper(v-else-if='prompt.type === "input-slide"')
39
+            .response-wrapper(v-else-if="prompt.type === 'input-slide'")
40 40
                 label {{ prompt.type }}
41 41
                 input(
42
-                    type='range'
43
-                    min='-3'
44
-                    max='3'
45
-                    v-model='answers[makeKey(prompt)]')
42
+                    type="range"
43
+                    min="-3"
44
+                    max="3"
45
+                    :prompt="makeKey(prompt)"
46
+                    :value="answers[makeKey(prompt)]"
47
+                    @input="setAnswer"
48
+                )
46 49
                 span {{ answers[makeKey(prompt)] }}
47 50
 
48 51
     footer.f-row.w-full
49
-        button.p-1(:disabled='state.step == 1' @click='back') back
50
-        button.p-1(:disabled='state.step == form.length' @click='next') next
51
-        button.p-1(:disabled='state.step != form.length' @click='next') save
52
+        button.p-1(:disabled="state.step == 1" @click="back") back
53
+        button.p-1(:disabled="state.step == form.length" @click="next") next
54
+        button.p-1(:disabled="state.step != form.length" @click="next") save
52 55
         .f-grow
53 56
         p step: {{ state.step }} of {{ form.length }} for {{ pid }}
54 57
 </template>
55 58
 
56 59
 <script setup>
57 60
 import Joi from 'joi'
58
-import { validatorMapping, makeKebob } from '@/utils'
59 61
 import { defineProps, reactive } from 'vue'
60
-import { saveSurveyByProfileId, scoreSurveyByProfileId } from '../services/survey.service'
62
+import { useRouter } from 'vue-router'
63
+import { validatorMapping, makeKebob } from '@/utils'
64
+import {
65
+    saveSurveyByProfileId,
66
+    scoreSurveyByProfileId,
67
+} from '../services/survey.service'
61 68
 import { scoreVals } from '../../../backend/db/data-generator/config.json'
62 69
 
63 70
 const props = defineProps({
@@ -67,8 +74,8 @@ const props = defineProps({
67 74
     },
68 75
     pid: {
69 76
         required: true,
70
-        type: Number
71
-    }
77
+        type: Number,
78
+    },
72 79
 })
73 80
 const makeKey = prompt => {
74 81
     return `${prompt.id}-${prompt.type}-${makeKebob(prompt.question)}`
@@ -83,10 +90,23 @@ const state = reactive({ step: 1 })
83 90
  * Create state object to store answers based on the questions in formSteps
84 91
  */
85 92
 const answers = reactive({})
93
+
94
+const setAnswer = e => {
95
+    const prompt = e.target.attributes.prompt.nodeValue
96
+    answers[prompt] = e.target.value
97
+}
86 98
 const resetAnswers = () => {
87 99
     Object.keys(answers).forEach(key => (answers[key] = null))
88 100
 }
89 101
 resetAnswers()
102
+const setupAnswers = () => {
103
+    props.form.forEach(step => {
104
+        step.forEach(prompt => {
105
+            answers[makeKey(prompt)] = '0'
106
+        })
107
+    })
108
+}
109
+setupAnswers()
90 110
 /**
91 111
  * Callback for clicking a tag to respond
92 112
  */
@@ -116,7 +136,7 @@ const isValid = step => {
116 136
  */
117 137
 const formatAnswer = (inputType, answer) => {
118 138
     let formattedAnswer = answer
119
-    if(inputType == 'slide')  {
139
+    if (inputType == 'slide') {
120 140
         const offset = (scoreVals.length - 1) / 2
121 141
         formattedAnswer = scoreVals[offset + parseInt(answer)].toString()
122 142
     }
@@ -148,22 +168,24 @@ const next = async e => {
148 168
                 response_key_id: questiontoResponseKeyId[answerKey],
149 169
                 val: formatAnswer(
150 170
                     getInputTypeFromKey(answerKey),
151
-                    answers[answerKey]
152
-                )
171
+                    answers[answerKey],
172
+                ),
153 173
             }
154 174
         })
155 175
 
156 176
         const maxDistance = 100
157
-        
158
-        if(!props.pid) return console.error(`no pid: ${props.pid}`)
177
+
178
+        if (!props.pid) return console.error(`no pid: ${props.pid}`)
159 179
 
160 180
         saveSurveyByProfileId(idWithResponseVal, props.pid)
161
-        alert('Responses submitted!')
162
-        
163 181
         resetAnswers()
182
+
164 183
         scoreSurveyByProfileId(props.pid, maxDistance)
165 184
 
166 185
         state.step = 1
186
+
187
+        const router = useRouter()
188
+        router.push({ name: 'HomeView' })
167 189
     } else if (state.step < props.form.length) {
168 190
         state.step++
169 191
     } else {

+ 5
- 5
frontend/src/router/index.js View File

@@ -1,7 +1,7 @@
1 1
 import { createRouter, createWebHistory } from 'vue-router'
2 2
 
3 3
 import HomeView from '../views/HomeView.vue'
4
-import Profile from '../views/Profile.vue'
4
+import ProfileView from '../views/ProfileView.vue'
5 5
 import Matches from '../views/Matches.vue'
6 6
 import Chats from '../views/Chats.vue'
7 7
 import Login from '../views/Login.vue'
@@ -13,12 +13,12 @@ const routes = [
13 13
         path: '/',
14 14
         component: HomeView,
15 15
         name: 'HomeView',
16
-        meta: { requiresAuth: true, requiresProfile: true }
16
+        meta: { requiresAuth: true, requiresProfile: true },
17 17
     },
18 18
     {
19
-        path: '/profile',
20
-        component: Profile,
21
-        name: 'Profile',
19
+        path: '/profile/:pid',
20
+        component: ProfileView,
21
+        name: 'ProfileView',
22 22
         meta: { requiresAuth: true },
23 23
     },
24 24
     {

+ 5
- 1
frontend/src/services/profile.service.js View File

@@ -20,4 +20,8 @@ const fetchProfilesByUserId = async userId => {
20 20
     return validProfileInstances
21 21
 }
22 22
 
23
-export { fetchProfilesByUserId }
23
+const fetchProfileByProfileId = async profileId => {
24
+    const profile = await db.get(`/profile/${profileId}`)
25
+    return profile
26
+}
27
+export { fetchProfilesByUserId, fetchProfileByProfileId }

+ 20
- 16
frontend/src/services/survey.service.js View File

@@ -24,8 +24,11 @@ const fetchSurveyByProfileId = async profileId => {
24 24
         const question = myquestions[i]
25 25
 
26 26
         // Set the input type based on category
27
-        const type = question.response_key_category == 'locationPref' ? 'input-string' : 'input-slide'
28
-        
27
+        const type =
28
+            question.response_key_category != 'profile'
29
+                ? 'input-slide'
30
+                : 'input-string'
31
+
29 32
         // Reformats myquestions into the format we want
30 33
         const reformatted = {
31 34
             id: question.response_key_id,
@@ -42,7 +45,6 @@ const fetchSurveyByProfileId = async profileId => {
42 45
         }
43 46
     }
44 47
     const mysurvey = new Survey(allsteps)
45
-    console.log('mysurvey :>> ', mysurvey);
46 48
     return mysurvey
47 49
 }
48 50
 
@@ -50,9 +52,11 @@ const saveSurveyByProfileId = async (surveyResponses, profileId) => {
50 52
     surveyResponses.forEach(responseKeyIdwithVal => {
51 53
         const keyId = responseKeyIdwithVal.response_key_id
52 54
         const val = responseKeyIdwithVal.val
53
-        
55
+
54 56
         // POST
55
-        const myresponses = db.post(`/profile/${profileId}/respond?response_key_id=${keyId}&val=${val}`)
57
+        const myresponses = db.post(
58
+            `/profile/${profileId}/respond?response_key_id=${keyId}&val=${val}`,
59
+        )
56 60
         return myresponses
57 61
     })
58 62
 }
@@ -62,21 +66,21 @@ const updateSurveyByProfileId = async (surveyResponses, profileId) => {
62 66
         const keyId = responseKeyIdwithVal.response_key_id
63 67
         const val = responseKeyIdwithVal.val
64 68
         // PATCH
65
-        const myresponses = db.patch(`/profile/${profileId}/update/${keyId}`,
66
-            [
67
-                {
68
-                    response_id: 2,
69
-                    profile_id: profileId,
70
-                    response_key_id: keyId,
71
-                    val: val,
72
-                },
73
-            ]
74
-        )
69
+        const myresponses = db.patch(`/profile/${profileId}/update/${keyId}`, [
70
+            {
71
+                response_id: 2,
72
+                profile_id: profileId,
73
+                response_key_id: keyId,
74
+                val: val,
75
+            },
76
+        ])
75 77
     })
76 78
 }
77 79
 
78 80
 const scoreSurveyByProfileId = async (profileId, maxDistance) => {
79
-    const scoreSurvey = await db.get(`/profile/${profileId}/score?max_distance=${maxDistance}`)
81
+    const scoreSurvey = await db.get(
82
+        `/profile/${profileId}/score?max_distance=${maxDistance}`,
83
+    )
80 84
     return scoreSurvey
81 85
 }
82 86
 

+ 6
- 6
frontend/src/views/HomeView.vue View File

@@ -4,7 +4,7 @@ main.f-col.start.w-full
4 4
         h1 Queue Page
5 5
         ProfileCardList(:profiles="cards" :pid="pid" @reload-queue="getQueue")
6 6
     p(v-else) Loading...
7
-    MainNav(:pid="pid" @show-sidebar="$emit('show-sidebar')")
7
+    MainNav(@show-sidebar="$emit('show-sidebar')")
8 8
 </template>
9 9
 
10 10
 <script>
@@ -18,8 +18,8 @@ export default {
18 18
     props: {
19 19
         pid: {
20 20
             type: Number,
21
-            required: true
22
-        }
21
+            required: true,
22
+        },
23 23
     },
24 24
     data: () => ({
25 25
         cards: [],
@@ -28,13 +28,13 @@ export default {
28 28
     watch: {
29 29
         pid() {
30 30
             this.getQueue()
31
-        }
31
+        },
32 32
     },
33 33
     async created() {
34 34
         await this.getQueue()
35 35
     },
36 36
     methods: {
37
-         _reformatProfiles(profiles) {
37
+        _reformatProfiles(profiles) {
38 38
             return profiles.map(profile => {
39 39
                 return {
40 40
                     pid: profile.profile_id,
@@ -53,7 +53,7 @@ export default {
53 53
             }
54 54
             this.loading = false
55 55
         },
56
-        
56
+
57 57
         // For Batch Data Parsing & Processing
58 58
         _parseBatch(allBatches) {
59 59
             const finished = { profiles: [], users: [], responses: [] }

+ 15
- 10
frontend/src/views/Matches.vue View File

@@ -1,14 +1,14 @@
1 1
 <template lang="pug">
2 2
 main.f-col.start.w-full
3
-    article.match(v-if='!loading')
3
+    article#match(v-if="!loading")
4 4
         h1 Match Page
5
-        ProfileCardList(:profiles='profiles' :pid='parseInt(pid)' :is-grid="true")
6
-    MainNav(:pid="pid" @show-sidebar="$emit('show-sidebar')")
5
+        ProfileCardList(:profiles="profiles" :pid="pid" :is-grid="true")
6
+    p(v-else) Loading...
7
+    MainNav(@show-sidebar="$emit('show-sidebar')")
7 8
 </template>
8 9
 
9 10
 <script>
10 11
 import ProfileCardList from '../components/ProfileCardList.vue'
11
-import { loginHandler } from '../utils'
12 12
 import { fetchMembershipsByProfileId } from '../services'
13 13
 
14 14
 export default {
@@ -16,16 +16,16 @@ export default {
16 16
     props: {
17 17
         pid: {
18 18
             type: Number,
19
-            required: true
20
-        }
19
+            required: true,
20
+        },
21 21
     },
22 22
     data: () => ({
23 23
         matches: null,
24
-        loading: true
24
+        loading: true,
25 25
     }),
26 26
     computed: {
27 27
         profiles() {
28
-            if(this.loading || this.matches.length < 1) return []
28
+            if (this.loading || this.matches.length < 1) return []
29 29
             return this.matches.map(m => {
30 30
                 return {
31 31
                     pid: m.profile.profile_id,
@@ -33,7 +33,12 @@ export default {
33 33
                     name: m.profile.user_name,
34 34
                 }
35 35
             })
36
-        }
36
+        },
37
+    },
38
+    watch: {
39
+        pid() {
40
+            this.getMatches()
41
+        },
37 42
     },
38 43
     methods: {
39 44
         async getMatches() {
@@ -48,7 +53,7 @@ export default {
48 53
     },
49 54
     mounted() {
50 55
         this.getMatches()
51
-    }
56
+    },
52 57
 }
53 58
 </script>
54 59
 

+ 0
- 54
frontend/src/views/Profile.vue View File

@@ -1,54 +0,0 @@
1
-<template lang="pug">
2
-main.f-col.start.w-full
3
-    article#profile
4
-        h1 Profile Page
5
-    main-nav(:pid="pid")
6
-</template>
7
-
8
-<script>
9
-export default {
10
-    name: 'Profile',
11
-    props: {
12
-        pid: {
13
-            type: Number,
14
-            required: true
15
-        }
16
-    },
17
-    data() {
18
-        return {
19
-            requesting: false,
20
-            form: {
21
-                fullname: '',
22
-                avatar: '',
23
-                age: '',
24
-                metadata: '',
25
-            },
26
-        }
27
-    },
28
-    created() {
29
-        this.getUser()
30
-    },
31
-    methods: {
32
-        onSubmit() {
33
-            this.requesting = true
34
-            // this.getUser()
35
-            // this.setUser()
36
-        },
37
-        getUser() {
38
-            // const uid = auth.currentUser.uid
39
-            return
40
-        },
41
-        setUser() {
42
-            // CometChat.updateUser(user, apiKey)
43
-            // .then(() => this.$router.push({ name: "home" }))
44
-            // .catch((error) => console.log(error))
45
-            // .finally(() => this.requesting = false);
46
-        },
47
-    },
48
-}
49
-</script>
50
-
51
-<style lang="postcss">
52
-#profile
53
-
54
-</style>

+ 39
- 0
frontend/src/views/ProfileView.vue View File

@@ -0,0 +1,39 @@
1
+<template lang="pug">
2
+main.f-col.start.w-full
3
+    article#home(v-if="!loading")
4
+        h1 Profile Page
5
+        h2 {{ profile }}
6
+        RouterLink(:to="{ name: 'HomeView' }") back
7
+    p(v-else) Loading...
8
+    MainNav(@show-sidebar="$emit('show-sidebar')")
9
+</template>
10
+
11
+<script>
12
+import { fetchProfileByProfileId } from '../services'
13
+
14
+export default {
15
+    name: 'ProfileView',
16
+    data: () => ({
17
+        loading: true,
18
+        profile: {},
19
+    }),
20
+    created() {
21
+        this.getProfile()
22
+    },
23
+    methods: {
24
+        async getProfile() {
25
+            this.loading = true
26
+            try {
27
+                this.profile = await fetchProfileByProfileId(
28
+                    this.$route.params.pid,
29
+                )
30
+            } catch (err) {
31
+                console.error(err)
32
+            }
33
+            this.loading = false
34
+        },
35
+    },
36
+}
37
+</script>
38
+
39
+<style lang="postcss"></style>

+ 4
- 4
frontend/src/views/Survey.vue View File

@@ -15,17 +15,17 @@ export default {
15 15
     props: {
16 16
         pid: {
17 17
             type: Number,
18
-            required: true
19
-        }
18
+            required: true,
19
+        },
20 20
     },
21 21
     data() {
22
-        return { 
22
+        return {
23 23
             validSurvey: null,
24 24
         }
25 25
     },
26 26
     async created() {
27
-        console.log('survey for:', this.pid)
28 27
         this.validSurvey = await fetchSurveyByProfileId(this.pid)
28
+        console.log('survey for:', this.validSurvey)
29 29
     },
30 30
 }
31 31
 </script>

Loading…
Cancel
Save