Skip to content

Commit 6c68366

Browse files
Merge branch 'dev'
2 parents c495b30 + 38d19cd commit 6c68366

5 files changed

Lines changed: 233 additions & 6 deletions

File tree

components/infiniteload/Annotations.vue

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,16 @@
1515
</template>
1616

1717
<script>
18+
import uniqBy from 'lodash/uniqBy'
19+
import Fuse from 'fuse.js'
20+
1821
export default {
22+
data () {
23+
return {
24+
items: []
25+
}
26+
},
27+
1928
props: {
2029
value: {
2130
type: Array,
@@ -32,6 +41,14 @@ export default {
3241
noMoreResults: {
3342
type: String,
3443
default: 'No more results'
44+
},
45+
searchString: {
46+
type: String,
47+
default: ''
48+
},
49+
searchKeys: {
50+
type: Array,
51+
default: () => ([])
3552
}
3653
},
3754
@@ -43,26 +60,50 @@ export default {
4360
*/
4461
async infiniteLoadAnnotations ($state) {
4562
let response = null
63+
const limit = 20
4664
try {
4765
response = await this.$explicates.search({
4866
collection: this.containerIri,
49-
limit: 20,
50-
offset: this.value.length
67+
limit: limit,
68+
offset: this.items.length
5169
})
5270
} catch (err) {
5371
this.$nuxt.error(err)
5472
}
5573
56-
if (!response.data.hasOwnProperty('first')) {
74+
if (response.data.hasOwnProperty('first')) {
75+
this.items = uniqBy(this.items.concat(response.data.first.items), 'id')
76+
}
77+
78+
if (
79+
!response.data.hasOwnProperty('first') ||
80+
response.data.first.items.length < limit
81+
) {
5782
$state.complete()
5883
this.$emit('complete')
5984
return
6085
}
6186
62-
this.$emit('input', this.value.concat(response.data.first.items))
6387
$state.loaded()
6488
},
6589
90+
/**
91+
* Apply fuzzy search.
92+
* @param {Array} items
93+
* The items to search.
94+
*/
95+
search (items) {
96+
if (
97+
this.searchKeys &&
98+
this.searchString &&
99+
this.searchKeys.length &&
100+
this.searchString.length
101+
) {
102+
return this.fuse.search(this.searchString)
103+
}
104+
return items
105+
},
106+
66107
/**
67108
* Reset the loaded items.
68109
*/
@@ -83,6 +124,43 @@ export default {
83124
this.$refs.infiniteload.attemptLoad()
84125
}
85126
})
127+
},
128+
129+
/**
130+
* Emit the loaded and possibly filtered items.
131+
*/
132+
emitItems () {
133+
const filteredItems = this.search(this.items)
134+
this.$emit('input', filteredItems)
135+
}
136+
},
137+
138+
computed: {
139+
fuse () {
140+
return new Fuse(this.items, {
141+
shouldSort: true,
142+
tokenize: true,
143+
matchAllTokens: true,
144+
findAllMatches: true,
145+
threshold: 0,
146+
location: 0,
147+
distance: 100,
148+
maxPatternLength: 32,
149+
minMatchCharLength: 1,
150+
keys: this.searchKeys
151+
})
152+
}
153+
},
154+
155+
watch: {
156+
searchKeys () {
157+
this.emitItems()
158+
},
159+
searchString () {
160+
this.emitItems()
161+
},
162+
items () {
163+
this.emitItems()
86164
}
87165
}
88166
}

layouts/admin-collection-dashboard.vue

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,15 @@ export default {
100100
}
101101
}
102102
},
103+
{
104+
label: 'Item Tags',
105+
link: {
106+
name: 'admin-collection-short_name-tags',
107+
params: {
108+
short_name: this.currentCollection.short_name
109+
}
110+
}
111+
},
103112
{
104113
label: 'Project Filters',
105114
link: {

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "libcrowds",
3-
"version": "1.0.0",
3+
"version": "1.1.0",
44
"description": "A Vue.js frontend for PYBOSSA.",
55
"author": "Alex Mendes <alexanderhmendes@gmail.com>",
66
"private": true,
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<template>
2+
<card-base
3+
:title="title"
4+
:description="description"
5+
docs="/collections/tags/">
6+
7+
<p slot="guidance">
8+
Use the table below to delete tags. Note that the same word may be used
9+
to tag multiple items and therefore could appear multiple times in the
10+
table below, you can use the search button above to filter the
11+
table.
12+
</p>
13+
14+
<b-form slot="controls" :class="darkMode ? 'form-dark' : null">
15+
<b-form-input
16+
v-model="searchString"
17+
class="search-control"
18+
size="sm"
19+
placeholder="Type to search">
20+
</b-form-input>
21+
</b-form>
22+
23+
<b-table
24+
responsive
25+
striped
26+
hover
27+
show-empty
28+
:dark="darkMode"
29+
:items="tagAnnotations"
30+
:fields="tableFields">
31+
<template slot="action" slot-scope="row">
32+
<b-btn
33+
variant="warning"
34+
size="sm"
35+
@click="deleteTag(row.item)">
36+
Delete
37+
</b-btn>
38+
</template>
39+
</b-table>
40+
41+
<infinite-load-annotations
42+
ref="infiniteload"
43+
v-if="hasTagAnnotations"
44+
v-model="tagAnnotations"
45+
:search-keys="searchKeys"
46+
:search-string="searchString"
47+
:container-iri="currentCollection.info.annotations.tags"
48+
no-results="It looks like nothing has been tagged yet!"
49+
no-more-results="">
50+
</infinite-load-annotations>
51+
52+
</card-base>
53+
</template>
54+
55+
<script>
56+
import { fetchCollectionByName } from '@/mixins/fetchCollectionByName'
57+
import { metaTags } from '@/mixins/metaTags'
58+
import InfiniteLoadAnnotations from '@/components/infiniteload/Annotations'
59+
import CardBase from '@/components/cards/Base'
60+
61+
export default {
62+
layout: 'admin-collection-dashboard',
63+
64+
mixins: [ fetchCollectionByName, metaTags ],
65+
66+
data () {
67+
return {
68+
title: 'Item Tags',
69+
description: 'Manage item tags for the collection microsite.',
70+
tagAnnotations: [],
71+
tableFields: {
72+
'body.value': {
73+
label: 'Name'
74+
},
75+
action: {
76+
label: 'Action',
77+
class: 'text-center'
78+
}
79+
},
80+
searchKeys: [
81+
'body.value'
82+
],
83+
searchString: ''
84+
}
85+
},
86+
87+
components: {
88+
CardBase,
89+
InfiniteLoadAnnotations
90+
},
91+
92+
computed: {
93+
currentCollection () {
94+
return this.$store.state.currentCollection
95+
},
96+
97+
hasTagAnnotations () {
98+
return this.currentCollection.info.annotations.hasOwnProperty('tags')
99+
}
100+
},
101+
102+
methods: {
103+
/**
104+
* Delete a tag.
105+
* @param {Object}
106+
* The tag.
107+
*/
108+
deleteTag (tag) {
109+
this.$swal({
110+
title: `Confirm`,
111+
html: `This tag will be removed permanently.<br><br>
112+
Are you sure you want to continue?`,
113+
type: 'question',
114+
showCancelButton: true,
115+
reverseButtons: true,
116+
showLoaderOnConfirm: true,
117+
preConfirm: () => {
118+
return this.$axios.$delete(tag.id)
119+
}
120+
}).then(r => {
121+
this.reset()
122+
this.$notifications.success({
123+
message: 'Tag deleted'
124+
})
125+
}).catch(err => {
126+
if (typeof err === 'object' && err.hasOwnProperty('dismiss')) {
127+
this.$notifications.error({ message: err.message })
128+
}
129+
})
130+
},
131+
132+
/**
133+
* Reset the loaded items.
134+
*/
135+
reset () {
136+
this.$refs.infiniteload.reset()
137+
}
138+
}
139+
}
140+
</script>

0 commit comments

Comments
 (0)