Browse Source

Merge pull request #179 from pinry/feature/search-boards-and-pins-by-tag

close #50 #115 search boards and pins by tag
pull/180/head
Ji Qu 4 years ago
committed by GitHub
parent
commit
d5975d04a3
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 224 additions and 10 deletions
  1. +2
    -1
      core/views.py
  2. +17
    -4
      pinry-spa/src/components/Boards.vue
  3. +10
    -1
      pinry-spa/src/components/PHeader.vue
  4. +9
    -0
      pinry-spa/src/components/api.js
  5. +129
    -0
      pinry-spa/src/components/search/SearchPanel.vue
  6. +6
    -0
      pinry-spa/src/router/index.js
  7. +4
    -4
      pinry-spa/src/views/Boards4User.vue
  8. +47
    -0
      pinry-spa/src/views/Search.vue

+ 2
- 1
core/views.py View File

@@ -36,7 +36,8 @@ class PinViewSet(viewsets.ModelViewSet):

class BoardViewSet(viewsets.ModelViewSet):
serializer_class = api.BoardSerializer
filter_backends = (DjangoFilterBackend, OrderingFilter)
filter_backends = (DjangoFilterBackend, OrderingFilter, SearchFilter)
search_fields = ("name", )
filter_fields = ("submitter__username", )
ordering_fields = ('-id', )
ordering = ('-id', )


+ 17
- 4
pinry-spa/src/components/Boards.vue View File

@@ -118,14 +118,15 @@ export default {
BoardEditorUI,
},
data: initialData,
props: ['boardUsername'],
props: ['filters'],
watch: {
boardUsername() {
filters() {
this.reset();
},
},
methods: {
initialize() {
this.initializeMeta();
this.fetchMore(true);
},
initializeMeta() {
@@ -205,8 +206,21 @@ export default {
if (!this.shouldFetchMore(created)) {
return;
}
let promise;
if (this.filters.boardUsername) {
promise = API.fetchBoardForUser(
this.filters.boardUsername,
this.status.offset,
);
} else if (this.filters.boardNameContains) {
promise = API.Board.fetchListWhichContains(
this.filters.boardNameContains,
this.status.offset,
);
} else {
return;
}
this.status.loading = true;
const promise = API.fetchBoardForUser(this.boardUsername, this.status.offset);
promise.then(
(resp) => {
const { results, next } = resp.data;
@@ -227,7 +241,6 @@ export default {
created() {
bus.bus.$on(bus.events.refreshBoards, this.reset);
this.registerScrollEvent();
this.initializeMeta();
this.initialize();
},
};


+ 10
- 1
pinry-spa/src/components/PHeader.vue View File

@@ -43,7 +43,7 @@
v-if="user.loggedIn"
class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
My Collections
My
</a>
<div class="navbar-dropdown">
<router-link
@@ -73,6 +73,15 @@
</div>
</div>
<div class="navbar-end">
<router-link
:to="{ name: 'search' }"
class="navbar-item">
<b-icon
type="is-dark"
icon="magnify"
custom-size="mdi-24px">
</b-icon>
</router-link>
<div class="navbar-item">
<div class="buttons">
<a


+ 9
- 0
pinry-spa/src/components/api.js View File

@@ -31,6 +31,15 @@ const Board = {
const url = `${API_PREFIX}boards-auto-complete/?submitter__username=${username}`;
return axios.get(url);
},
fetchSiteFullList() {
const url = `${API_PREFIX}boards-auto-complete/`;
return axios.get(url);
},
fetchListWhichContains(text, offset = 0, limit = 50) {
const prefix = `${API_PREFIX}boards/?search=${text}`;
const url = `${prefix}&offset=${offset}&limit=${limit}`;
return axios.get(url);
},
saveChanges(boardId, fieldsForm) {
const url = `${API_PREFIX}boards/${boardId}/`;
return axios.patch(


+ 129
- 0
pinry-spa/src/components/search/SearchPanel.vue View File

@@ -0,0 +1,129 @@
<template>
<div class="search-panel">
<div class="filter-selector">
<div class="card-content">
<b-field>
<b-select placeholder="Choose Filter" v-model="filterType">
<option>Tag</option>
<option>Board</option>
</b-select>
<b-autocomplete
v-show="filterType === 'Tag'"
class="search-input"
v-model="name"
:data="filteredDataArray"
:keep-first="true"
:open-on-focus="true"
placeholder="select a filter then type to filter"
icon="magnify"
@select="option => selected = option">
<template slot="empty">No results found</template>
</b-autocomplete>
<template v-if="filterType === 'Board'">
<b-input
class="search-input"
type="search"
v-model="boardText"
placeholder="type to search board"
icon="magnify"
>
</b-input>
<p class="control">
<b-button @click="searchBoard" class="button is-primary">Search</b-button>
</p>
</template>
</b-field>
</div>
</div>
</div>
</template>

<script>
import api from '../api';

export default {
name: 'FilterSelector',
data() {
return {
filterType: null,
selectedOption: [],
options: {
Tag: [],
},
name: '',
boardText: '',
selected: null,
};
},
methods: {
selectOption(filterName) {
this.name = '';
this.boardText = '';
if (filterName === 'Tag') {
this.selectedOption = this.options.Tag;
}
},
searchBoard() {
if (this.boardText === '') {
return;
}
this.$emit(
'selected',
{ filterType: this.filterType, selected: this.boardText },
);
},
},
watch: {
filterType(newVal) {
this.selectOption(newVal);
},
selected(newVal) {
this.$emit(
'selected',
{ filterType: this.filterType, selected: newVal },
);
},
},
computed: {
filteredDataArray() {
return this.selectedOption.filter(
(option) => {
const ret = option
.toString()
.toLowerCase()
.indexOf(this.name.toLowerCase()) >= 0;
return ret;
},
);
},
},
created() {
api.Tag.fetchList().then(
(resp) => {
const options = [];
resp.data.forEach(
(tag) => {
options.push(tag.name);
},
);
this.options.Tag = options;
},
);
},
};
</script>

<style scoped="scoped" lang="scss">
.search-panel {
padding-top: 3rem;
padding-left: 2rem;
padding-right: 2rem;
}
.filter-selector {
background-color: white;
border-radius: 3px;
.search-input {
width: 100%;
}
}
</style>

+ 6
- 0
pinry-spa/src/router/index.js View File

@@ -7,6 +7,7 @@ import Pins4Board from '../views/Pins4Board.vue';
import Pins4Id from '../views/Pins4Id.vue';
import Boards4User from '../views/Boards4User.vue';
import PinCreate from '../views/PinCreate.vue';
import Search from '../views/Search.vue';

Vue.use(VueRouter);

@@ -46,6 +47,11 @@ const routes = [
name: 'pin-creation-from-url',
component: PinCreate,
},
{
path: '/search',
name: 'search',
component: Search,
},
];

const router = new VueRouter({


+ 4
- 4
pinry-spa/src/views/Boards4User.vue View File

@@ -1,7 +1,7 @@
<template>
<div class="boards-for-user">
<PHeader></PHeader>
<Boards :boardUsername="username"></Boards>
<Boards :filters="filters"></Boards>
</div>
</template>

@@ -13,7 +13,7 @@ export default {
name: 'Boards4User',
data() {
return {
username: '',
filters: { boardUsername: null },
};
},
components: {
@@ -24,12 +24,12 @@ export default {
this.initialize();
},
beforeRouteUpdate(to, from, next) {
this.username = to.params.username;
this.filters = { boardUsername: to.params.username };
next();
},
methods: {
initialize() {
this.username = this.$route.params.username;
this.filters = { boardUsername: this.$route.params.username };
},
},
};


+ 47
- 0
pinry-spa/src/views/Search.vue View File

@@ -0,0 +1,47 @@
<template>
<div class="pins-for-tag">
<PHeader></PHeader>
<SearchPanel v-on:selected="doSearch"></SearchPanel>
<Pins v-if="pinFilters" :pin-filters="pinFilters"></Pins>
<Boards v-if="boardFilters" :filters="boardFilters"></Boards>
</div>
</template>

<script>
import PHeader from '../components/PHeader.vue';
import Pins from '../components/Pins.vue';
import Boards from '../components/Boards.vue';
import SearchPanel from '../components/search/SearchPanel.vue';

export default {
name: 'Search',
data() {
return {
pinFilters: null,
boardFilters: null,
};
},
components: {
PHeader,
Pins,
Boards,
SearchPanel,
},
created() {},
methods: {
doSearch(args) {
this.pinFilters = null;
this.boardFilters = null;
if (args.filterType === 'Tag') {
this.pinFilters = { tagFilter: args.selected };
} else if (args.filterType === 'Board') {
this.boardFilters = { boardNameContains: args.selected };
}
},
},
};
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped lang="scss">
</style>

Loading…
Cancel
Save