Skip to content

Commit 9d90b65

Browse files
committed
feat: initial port of member search view, still a wip
1 parent 8e35a16 commit 9d90b65

File tree

4 files changed

+154
-8
lines changed

4 files changed

+154
-8
lines changed

src/api/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export const authApi = {
107107

108108
export const usersApi = {
109109
search: username => $http('/api/users/search', { params: { username } }),
110+
memberSearch: params => $http('/api/search/users', { params }),
110111
lookup: (username, params) => $http(`/api/users/lookup/${username}`, { params }),
111112
update: (userId, data) => $http(`/api/users/${userId}`, { method: 'PUT', data }),
112113
find: username => $http(`/api/users/${username}`),

src/components/layout/Pagination.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export default {
3030
}
3131
3232
const updatePageDisplay = (e, value) => {
33+
if (!e) return
3334
if (v.pageCount < 2) return
3435
const range = e.target || e // account for passing in ref in nextTick
3536
value = value || range.value // account for passing in custom page

src/router/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ const routes = [
8686
path: '/search/users',
8787
name: 'MemberSearch',
8888
component: MemberSearch,
89-
meta: { requiresAuth: true, bodyClass: 'search-users' }
89+
meta: { requiresAuth: true, bodyClass: 'member-search' }
9090
},
9191
{
9292
path: '/about',

src/views/MemberSearch.vue

Lines changed: 151 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,167 @@
11
<template>
2-
Member Search
2+
<div class="user-search">
3+
<div class="member-search-field">
4+
<div class="nested-input-container">
5+
<a v-if="search" @click="clearSearch()" class="nested-clear-btn fa fa-times"></a>
6+
<a @click="searchUsers()" class="nested-btn">Search</a>
7+
<input class="input-text nested-input" type="search" v-model="searchStr" id="search-users" placeholder="Search users by username" @keydown="$event.which === 13 && searchUsers()" @keyup="$event.which === 27 && clearSearch()" />
8+
</div>
9+
</div>
10+
11+
<div class="member-search-results" v-if="searchData && (searchData.count > 0 || search)">
12+
<div v-if="search">
13+
Displaying {{searchData.count}} search result(s) for "<strong>{{search}}</strong>":<br /><br />
14+
</div>
15+
<table width="100%">
16+
<thead class="members-header">
17+
<tr>
18+
<th class="pointer" ><span :class="getSortClass('username')"></span>&nbsp;Username</th>
19+
<th class="pointer" ><span :class="getSortClass('role')"></span>&nbsp;Role</th>
20+
<th class="pointer"><span :class="getSortClass('post_count')"></span>&nbsp;Post Count</th>
21+
<th class="pointer hide-mobile" ><span :class="getSortClass('created_at')"></span>&nbsp;Registered Date</th>
22+
</tr>
23+
</thead>
24+
<tbody class="members">
25+
<tr v-if="searchData?.count <= 0">
26+
<td class="centered-text" colspan="4"><h5>No users to display</h5></td>
27+
</tr>
28+
<tr v-for="user in searchData?.users" :key="user.username">
29+
<td class="username">
30+
<a class="search-users user-avatar hide-mobile" href="">
31+
<img :src="user.avatar || defaultAvatar">
32+
</a>
33+
<a href="" v-html="user.username"></a>
34+
</td>
35+
<td v-html="user.role"></td>
36+
<td v-html="user.post_count"></td>
37+
<td class="hide-mobile">{{ user.created_at }}</td>
38+
</tr>
39+
</tbody>
40+
</table>
41+
</div>
42+
</div>
43+
<div class="sidebar">
44+
<div class="sidebar-block" v-if="searchData?.page_count > 1">
45+
<pagination :page="searchData.page" :limit="searchData.limit" :count="searchData.count"></pagination>
46+
</div>
47+
</div>
348
</template>
449

550
<script>
6-
import { reactive, toRefs } from 'vue'
51+
import { reactive, toRefs, computed } from 'vue'
52+
import { usersApi } from '@/api'
53+
import Pagination from '@/components/layout/Pagination.vue'
54+
import { useRoute, useRouter } from 'vue-router'
755
856
export default {
957
name: 'About',
58+
components: { Pagination },
59+
beforeRouteEnter(to, from, next) {
60+
const params = {
61+
limit: 15,
62+
page: to.query.page || 1,
63+
field: to.query.field,
64+
desc: to.query.desc
65+
}
66+
next(vm => usersApi.memberSearch(params).then(d => vm.searchData = d).catch(() => {}))
67+
},
68+
beforeRouteUpdate(to, from, next) {
69+
const params = {
70+
limit: 15,
71+
page: to.query.page || 1,
72+
field: to.query.field,
73+
desc: to.query.desc
74+
}
75+
usersApi.memberSearch(params).then(d => this.searchData = d).catch(() => {})
76+
next()
77+
},
1078
setup() {
79+
const pageUsers = inc => {
80+
const params = { ...$route.params, saveScrollPos: true }
81+
// Handle different pagination controls (threads) prev/next vs (posts) actual pagination
82+
const newPage = v.threads && inc ? v.postData.page + inc : v.currentPage
83+
let query = { ...$route.query, page: newPage }
84+
if (query.page === 1 || !query.page) delete query.page
85+
if ($route.query.page !== v.currentPage)
86+
$router.replace({ name: $route.name, params: params, query: query })
87+
}
88+
89+
const searchUsers = () => console.log('search users')
90+
const clearSearch = () => console.log('clear search')
91+
92+
const setSortField = () => {
93+
// // Get/Set new sort field
94+
// if (newField) v.sortField = newField
95+
// else newField = v.sortField
96+
// // Convert desc query param to boolean
97+
// let desc = $route.query.desc === 'false' ? false : true
98+
// // Sort Field hasn't changed just toggle desc
99+
// const defaultField = newField === 'updated_at' && !$route.query.field
100+
// if (defaultField || newField === $route.query.field) desc = !desc
101+
// else desc = true // Sort field changed, default to desc true
102+
// // Update router to have new query params, watch on query params will update data
103+
// let query = { field: newField, page: $route.query.page }
104+
// if (!query.page) delete query.page // don't include page if undefined
105+
// if (newField === 'updated_at') delete query.field // do not display default field in qs
106+
// if (!desc) query.desc = false // only display desc in query string when false
107+
// const params = { ...$route.params, saveScrollPos: true } // save scroll pos when sorting table
108+
// $router.replace({ name: $route.name, params: params, query: query })
109+
}
110+
111+
const getSortClass = field => {
112+
let sortClass = 'fa '
113+
const desc = v.searchData.desc
114+
const curField = v.searchData.field
115+
const defaultField = field === 'updated_at' && !curField
116+
if ((defaultField || curField === field) && desc) sortClass += 'fa-sort-down'
117+
else if ((defaultField || curField === field) && !desc) sortClass += 'fa-sort-up'
118+
else sortClass += 'fa-sort'
119+
return sortClass
120+
}
121+
122+
const $route = useRoute()
123+
const $router = useRouter()
124+
11125
const v = reactive({
12-
title: window.title,
13-
description: window.description,
14-
currentYear: window.currentYear
126+
currentPage: Number($route.query.page) || 1,
127+
pages: computed(() => v.searchData?.page_count),
128+
search: computed(() => v.searchData?.search),
129+
searchData: null,
130+
searchStr: null,
131+
defaultAvatar: window.default_avatar,
132+
defaultAvatarShape: window.default_avatar_shape,
15133
})
16-
return { ...toRefs(v) }
134+
return { ...toRefs(v), searchUsers, clearSearch, setSortField, getSortClass, pageUsers }
17135
}
18136
}
19137
</script>
20138
21-
<style scoped lang="scss">
139+
<style lang="scss">
140+
.member-search main #public-content { grid-template-areas: 'header header' 'main sidebar' 'main sidebar'; }
141+
.user-search { grid-area: main; }
142+
.sidebar {
143+
grid-area: sidebar;
22144
145+
.sidebar-block {
146+
display: block;
147+
position: sticky;
148+
top: $header-offset;
149+
}
150+
}
151+
152+
.members td {
153+
height: 6.25rem;
154+
padding-left: 0;
155+
padding-right: 0;
156+
}
157+
158+
.user-avatar img {
159+
object-fit: cover;
160+
height: 6.25rem;
161+
width: 6.25rem;
162+
display: inline-block;
163+
vertical-align: middle;
164+
margin-right: .5rem
165+
}
166+
.members-header th { text-align: left; }
23167
</style>

0 commit comments

Comments
 (0)