1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-19 05:09:42 +02:00

Allow sorting of search results and space contents

Closes #187

Sort search results and space contents by Name, Created or Revised.
This commit is contained in:
McMatts 2019-03-13 11:40:36 +00:00
parent 0985dbf5b6
commit 1d00f8ac6e
19 changed files with 1182 additions and 807 deletions

View file

@ -283,7 +283,7 @@ func (s Store) matchFullText(ctx domain.RequestContext, keywords, itemType strin
s.id, s.c_orgid AS orgid, s.c_docid AS documentid, s.c_itemid AS itemid, s.c_itemtype AS itemtype, s.id, s.c_orgid AS orgid, s.c_docid AS documentid, s.c_itemid AS itemid, s.c_itemtype AS itemtype,
d.c_spaceid as spaceid, COALESCE(d.c_name,'Unknown') AS document, d.c_tags AS tags, d.c_spaceid as spaceid, COALESCE(d.c_name,'Unknown') AS document, d.c_tags AS tags,
d.c_desc AS excerpt, d.c_template AS template, d.c_versionid AS versionid, d.c_desc AS excerpt, d.c_template AS template, d.c_versionid AS versionid,
COALESCE(l.c_name,'Unknown') AS space COALESCE(l.c_name,'Unknown') AS space, d.c_created AS created, d.c_revised AS revised
FROM FROM
dmz_search s, dmz_search s,
dmz_doc d dmz_doc d
@ -337,7 +337,7 @@ func (s Store) matchLike(ctx domain.RequestContext, keywords, itemType string) (
sql1 := s.Bind(`SELECT sql1 := s.Bind(`SELECT
s.id, s.c_orgid AS orgid, s.c_docid AS documentid, s.c_itemid AS itemid, s.c_itemtype AS itemtype, s.id, s.c_orgid AS orgid, s.c_docid AS documentid, s.c_itemid AS itemid, s.c_itemtype AS itemtype,
d.c_spaceid as spaceid, COALESCE(d.c_name,'Unknown') AS document, d.c_tags AS tags, d.c_desc AS excerpt, d.c_spaceid as spaceid, COALESCE(d.c_name,'Unknown') AS document, d.c_tags AS tags, d.c_desc AS excerpt,
COALESCE(l.c_name,'Unknown') AS space COALESCE(l.c_name,'Unknown') AS space, d.c_created AS created, d.c_revised AS revised
FROM FROM
dmz_search s, dmz_search s,
dmz_doc d dmz_doc d

File diff suppressed because one or more lines are too long

View file

@ -63,5 +63,6 @@ module.exports = {
"slug": true, "slug": true,
"iziToast": true, "iziToast": true,
"Papa": true, "Papa": true,
"Popper": true,
} }
}; };

View file

@ -11,6 +11,7 @@ module.exports = {
'no-invalid-interactive': false, 'no-invalid-interactive': false,
'no-nested-interactive': false, 'no-nested-interactive': false,
'no-triple-curlies': false, 'no-triple-curlies': false,
'no-global-jquery': false,
'style-concatenation': false, 'style-concatenation': false,
'simple-unless': false, 'simple-unless': false,
} }

View file

@ -9,11 +9,13 @@
// //
// https://documize.com // https://documize.com
import { inject as service } from '@ember/service';
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { A } from '@ember/array'; import { A } from '@ember/array';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
localStorage: service(),
showDeleteDialog: false, showDeleteDialog: false,
showMoveDialog: false, showMoveDialog: false,
selectedDocuments: A([]), selectedDocuments: A([]),
@ -36,9 +38,53 @@ export default Component.extend({
let targets = _.reject(this.get('spaces'), {id: space.get('id')}); let targets = _.reject(this.get('spaces'), {id: space.get('id')});
this.set('moveOptions', A(targets)); this.set('moveOptions', A(targets));
this.set('selectedDocuments', A([])); this.set('selectedDocuments', A([]));
let sortBy = this.get('localStorage').getSessionItem('space.sortBy');
if (!_.isNull(sortBy) && !_.isUndefined(sortBy)) {
this.send('onSetSort', sortBy);
}
let sortOrder = this.get('localStorage').getSessionItem('space.sortOrder');
if (!_.isNull(sortOrder) && !_.isUndefined(sortOrder)) {
this.send('onSetSort', sortOrder);
}
}, },
actions: { actions: {
onSetSort(val) {
switch (val) {
case 'name':
this.set('sortBy.name', true);
this.set('sortBy.created', false);
this.set('sortBy.updated', false);
break;
case 'created':
this.set('sortBy.name', false);
this.set('sortBy.created', true);
this.set('sortBy.updated', false);
break;
case 'updated':
this.set('sortBy.name', false);
this.set('sortBy.created', false);
this.set('sortBy.updated', true);
break;
case 'asc':
this.set('sortBy.asc', true);
this.set('sortBy.desc', false);
break;
case 'desc':
this.set('sortBy.asc', false);
this.set('sortBy.desc', true);
break;
}
},
// eslint-disable-next-line no-unused-vars
onSortBy(attacher) {
// attacher.hide();
this.get('onFiltered')(this.get('documents'));
},
onShowDeleteDocuments() { onShowDeleteDocuments() {
this.set('showDeleteDialog', true); this.set('showDeleteDialog', true);
}, },

View file

@ -10,13 +10,23 @@
// https://documize.com // https://documize.com
import { computed } from '@ember/object'; import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@ember/component'; import Component from '@ember/component';
export default Component.extend({ export default Component.extend({
localStorage: service('localStorage'),
resultPhrase: '', resultPhrase: '',
searchQuery: computed('keywords', function() { searchQuery: computed('keywords', function() {
return encodeURIComponent(this.get('keywords')); return encodeURIComponent(this.get('keywords'));
}), }),
// eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects
sortBy: {
name: true,
created: false,
updated: false,
asc: true,
desc: false,
},
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
@ -34,10 +44,84 @@ export default Component.extend({
let docLabel = duped.length === 1 ? "document" : "documents"; let docLabel = duped.length === 1 ? "document" : "documents";
let i = docs.length; let i = docs.length;
let j = duped.length; let j = duped.length;
phrase = `${i} ${references} across ${j} ${docLabel}`; phrase = `${i} ${references} in ${j} ${docLabel}`;
} }
this.set('resultPhrase', phrase); this.set('resultPhrase', phrase);
this.set('documents', duped);
let sortBy = this.get('localStorage').getSessionItem('search.sortBy');
if (!_.isNull(sortBy) && !_.isUndefined(sortBy)) {
this.send('onSetSort', sortBy);
}
let sortOrder = this.get('localStorage').getSessionItem('search.sortOrder');
if (!_.isNull(sortOrder) && !_.isUndefined(sortOrder)) {
this.send('onSetSort', sortOrder);
}
this.sortResults(duped);
},
sortResults(docs) {
let ls = this.get('localStorage');
let sortBy = this.get('sortBy');
if (_.isNull(docs)) return;
if (sortBy.name) {
docs = docs.sortBy('document');
ls.storeSessionItem('search.sortBy', 'name');
}
if (sortBy.created) {
docs = docs.sortBy('created');
ls.storeSessionItem('search.sortBy', 'created');
}
if (sortBy.updated) {
docs = docs.sortBy('revised');
ls.storeSessionItem('search.sortBy', 'updated');
}
if (sortBy.desc) {
docs = docs.reverseObjects();
ls.storeSessionItem('search.sortOrder', 'desc');
} else {
ls.storeSessionItem('search.sortOrder', 'asc');
}
this.set('documents', docs);
},
actions: {
onSetSort(val) {
switch (val) {
case 'name':
this.set('sortBy.name', true);
this.set('sortBy.created', false);
this.set('sortBy.updated', false);
break;
case 'created':
this.set('sortBy.name', false);
this.set('sortBy.created', true);
this.set('sortBy.updated', false);
break;
case 'updated':
this.set('sortBy.name', false);
this.set('sortBy.created', false);
this.set('sortBy.updated', true);
break;
case 'asc':
this.set('sortBy.asc', true);
this.set('sortBy.desc', false);
break;
case 'desc':
this.set('sortBy.asc', false);
this.set('sortBy.desc', true);
break;
}
},
// eslint-disable-next-line no-unused-vars
onSortBy(attacher) {
this.sortResults(this.get('documents'));
},
} }
}); });

View file

@ -20,10 +20,6 @@ export default Component.extend({
keywords: '' , keywords: '' ,
matchFilter: null, matchFilter: null,
// init() {
// this._super(...arguments);
// },
didReceiveAttrs() { didReceiveAttrs() {
this._super(...arguments); this._super(...arguments);
this.set('keywords', this.get('filter')); this.set('keywords', this.get('filter'));

View file

@ -21,6 +21,7 @@ export default Component.extend({
icon: '', icon: '',
color: '', color: '',
light: false, light: false,
outline: false,
themed: false, themed: false,
dismiss: false, dismiss: false,
truncate: false, truncate: false,
@ -48,6 +49,10 @@ export default Component.extend({
bc += '-light'; bc += '-light';
} }
if (this.outline) {
bc += '-outline';
}
if (!this.uppercase) { if (!this.uppercase) {
bc += ' text-case-normal'; bc += ' text-case-normal';
} }

View file

@ -353,6 +353,7 @@ let constants = EmberObject.extend({
Send: 'Send', Send: 'Send',
Share: 'Share', Share: 'Share',
SignIn: 'Sign In', SignIn: 'Sign In',
Sort: 'Sort',
Unassigned: 'Unassigned', Unassigned: 'Unassigned',
Update: 'Update', Update: 'Update',
Upload: 'Upload', Upload: 'Upload',

View file

@ -23,6 +23,14 @@ export default Controller.extend(NotifierMixin, {
queryParams: ['category'], queryParams: ['category'],
category: '', category: '',
filteredDocs: null, filteredDocs: null,
// eslint-disable-next-line ember/avoid-leaking-state-in-ember-objects
sortBy: {
name: true,
created: false,
updated: false,
asc: true,
desc: false,
},
actions: { actions: {
onRefresh() { onRefresh() {
@ -80,6 +88,30 @@ export default Controller.extend(NotifierMixin, {
}, },
onFiltered(docs) { onFiltered(docs) {
let ls = this.get('localStorage');
let sortBy = this.get('sortBy');
if (_.isNull(docs)) return;
if (sortBy.name) {
docs = docs.sortBy('name');
ls.storeSessionItem('space.sortBy', 'name');
}
if (sortBy.created) {
docs = docs.sortBy('created');
ls.storeSessionItem('space.sortBy', 'created');
}
if (sortBy.updated) {
docs = docs.sortBy('revised');
ls.storeSessionItem('space.sortBy', 'updated');
}
if (sortBy.desc) {
docs = docs.reverseObjects();
ls.storeSessionItem('space.sortOrder', 'desc');
} else {
ls.storeSessionItem('space.sortOrder', 'asc');
}
this.set('filteredDocs', docs); this.set('filteredDocs', docs);
} }
} }

View file

@ -51,6 +51,8 @@
space=model.folder space=model.folder
templates=model.templates templates=model.templates
permissions=model.permissions permissions=model.permissions
sortBy=sortBy
onFiltered=(action "onFiltered")
onExportDocument=(action "onExportDocument") onExportDocument=(action "onExportDocument")
onDeleteDocument=(action "onDeleteDocument") onDeleteDocument=(action "onDeleteDocument")
onMoveDocument=(action "onMoveDocument")}} onMoveDocument=(action "onMoveDocument")}}

View file

@ -30,11 +30,6 @@ body {
ul { ul {
margin: 0; margin: 0;
padding: 0; padding: 0;
li {
// list-style: none;
// list-style-type: none;
}
} }
input:-webkit-autofill { input:-webkit-autofill {

View file

@ -3,3 +3,4 @@
@import "ui-button"; @import "ui-button";
@import "ui-toolbar"; @import "ui-toolbar";
@import "ui-icon-picker"; @import "ui-icon-picker";
@import "ui-option";

View file

@ -4,6 +4,9 @@
@mixin button-shadow() { @mixin button-shadow() {
box-shadow: 1px 1px 3px 0px map-get($gray-shades, 500); box-shadow: 1px 1px 3px 0px map-get($gray-shades, 500);
} }
@mixin button-shadow-none() {
box-shadow: none;
}
%dmz-button { %dmz-button {
display: inline-block; display: inline-block;
@ -153,6 +156,26 @@
background-color: map-get($gray-shades, 300); background-color: map-get($gray-shades, 300);
} }
} }
.dmz-button-gray-outline {
@extend %dmz-button;
background-color: transparent;
color: map-get($gray-shades, 700);
border: 1px solid map-get($gray-shades, 300);
@extend .no-select;
outline: none;
@include button-shadow-none();
&:active,
&:focus {
outline: none;
@include button-shadow-none();
}
&:hover {
color: map-get($gray-shades, 800);
border-color: map-get($gray-shades, 500);
}
}
.dmz-button-theme { .dmz-button-theme {
@extend %dmz-button; @extend %dmz-button;

View file

@ -0,0 +1,47 @@
.ui-option-picker {
margin: 0;
padding: 0;
list-style: none;
font-size: 0;
> .option {
margin: 0;
padding: 5px 11px;
color: map-get($gray-shades, 600);
cursor: pointer;
position: relative;
list-style: none;
&:hover {
> .text {
color: map-get($yellow-shades, 800);
}
}
> .text {
font-size: 1rem;
font-weight: 400;
text-transform: uppercase;
letter-spacing: 0.00375rem;
}
}
> .selected {
> .text {
color: map-get($yellow-shades, 700) !important;
font-weight: 400;
}
}
}
.ui-option-picker-horiz {
> .option {
display: inline-block;
@media only screen and (max-width: 1200px) {
display: block;
width: 100%;
}
}
}

View file

@ -3,8 +3,12 @@
.ember-attacher-popper { .ember-attacher-popper {
background-color: $color-white; background-color: $color-white;
font-size: 1rem; font-size: 1rem;
box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.16), 0 1px 5px 0 rgba(0, 0, 0, 0.12); -webkit-box-shadow: 3px 3px 33px 0 rgba(0, 0, 0, 0.16), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
@include border-radius(3px); box-shadow: 3px 3px 33px 0 rgba(0, 0, 0, 0.16), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
// -webkit-box-shadow: 0 20px 66px 0 rgba(34,48,73,0.2);
// box-shadow: 0 20px 66px 0 rgba(34,48,73,0.2);
@include border-radius(5px);
> p { > p {
margin: 4px; margin: 4px;
@ -31,7 +35,7 @@
&:hover { &:hover {
color: $color-black; color: $color-black;
background-color: map-get($gray-shades, 100); background-color: map-get($yellow-shades, 100);
} }
} }
@ -84,6 +88,24 @@
} }
} }
> .closer {
color: map-get($gray-shades, 500);
font-weight: 300;
font-size: 1.7rem;
cursor: pointer;
padding: 5px 5px 0 0;
display: block;
text-align: right;
&:hover {
color: map-get($gray-shades, 700);
}
}
> .container {
padding: 0 20px 20px 20px;
}
> .form { > .form {
padding: 20px; padding: 20px;
width: 300px; width: 300px;

View file

@ -1,4 +1,59 @@
<div class="view-space"> <div class="view-space">
<div class="text-right">
{{#ui/ui-button
light=false
outline=true
uppercase=false
color=constants.Color.Gray
label=constants.Label.Sort}}
{{#attach-popover class="ember-attacher-popper" hideOn="click" showOn="click" isShown=false placement="bottom-end" as |attacher|}}
<i class="dicon {{constants.Icon.Cross}} closer" {{action attacher.hide}}/>
<div class="container">
{{ui/ui-spacer size=100}}
<div class="text-center">
<ul class="ui-option-picker ui-option-picker-horiz">
<li class="option {{if sortBy.name "selected"}}" {{action "onSetSort" "name"}}>
<div class="text">Name</div>
</li>
<li class="option {{if sortBy.created "selected"}}" {{action "onSetSort" "created"}}>
<div class="text">Created date</div>
</li>
<li class="option {{if sortBy.updated "selected"}}" {{action "onSetSort" "updated"}}>
<div class="text">Last updated</div>
</li>
</ul>
</div>
{{ui/ui-spacer size=100}}
<div class="text-center">
<ul class="ui-option-picker ui-option-picker-horiz">
<li class="option {{if sortBy.asc "selected"}}" {{action "onSetSort" "asc"}}>
<div class="text">Ascending</div>
</li>
<li class="option {{if sortBy.desc "selected"}}" {{action "onSetSort" "desc"}}>
<div class="text">Descending</div>
</li>
</ul>
</div>
{{ui/ui-spacer size=300}}
{{ui/ui-button
light=true
color=constants.Color.Yellow
label=constants.Label.Sort
onClick=(action "onSortBy" attacher)}}
</div>
{{/attach-popover}}
{{/ui/ui-button}}
</div>
{{ui/ui-spacer size=200}}
<ul class="documents"> <ul class="documents">
{{#each documents key="id" as |document|}} {{#each documents key="id" as |document|}}
<li class="document {{if document.selected "selected"}}" id="document-{{document.id}}"> <li class="document {{if document.selected "selected"}}" id="document-{{document.id}}">

View file

@ -1,4 +1,61 @@
<div class="view-search"> <div class="view-search">
{{#if documents}}
<div class="text-right">
{{#ui/ui-button
light=false
outline=true
uppercase=false
color=constants.Color.Gray
label=constants.Label.Sort}}
{{#attach-popover class="ember-attacher-popper" hideOn="click" showOn="click" isShown=false placement="bottom-end" as |attacher|}}
<i class="dicon {{constants.Icon.Cross}} closer" {{action attacher.hide}}/>
<div class="container">
{{ui/ui-spacer size=100}}
<div class="text-center">
<ul class="ui-option-picker ui-option-picker-horiz">
<li class="option {{if sortBy.name "selected"}}" {{action "onSetSort" "name"}}>
<div class="text">Name</div>
</li>
<li class="option {{if sortBy.created "selected"}}" {{action "onSetSort" "created"}}>
<div class="text">Created date</div>
</li>
<li class="option {{if sortBy.updated "selected"}}" {{action "onSetSort" "updated"}}>
<div class="text">Last updated</div>
</li>
</ul>
</div>
{{ui/ui-spacer size=100}}
<div class="text-center">
<ul class="ui-option-picker ui-option-picker-horiz">
<li class="option {{if sortBy.asc "selected"}}" {{action "onSetSort" "asc"}}>
<div class="text">Ascending</div>
</li>
<li class="option {{if sortBy.desc "selected"}}" {{action "onSetSort" "desc"}}>
<div class="text">Descending</div>
</li>
</ul>
</div>
{{ui/ui-spacer size=300}}
{{ui/ui-button
light=true
color=constants.Color.Yellow
label=constants.Label.Sort
onClick=(action "onSortBy" attacher)}}
</div>
{{/attach-popover}}
{{/ui/ui-button}}
</div>
{{ui/ui-spacer size=200}}
{{/if}}
<div class="result-summary">{{resultPhrase}}</div> <div class="result-summary">{{resultPhrase}}</div>
<ul class="documents"> <ul class="documents">
{{#each documents key="id" as |result|}} {{#each documents key="id" as |result|}}
@ -17,4 +74,5 @@
</li> </li>
{{/each}} {{/each}}
</ul> </ul>
</div> </div>

View file

@ -11,6 +11,10 @@
package search package search
import (
"time"
)
// QueryOptions defines how we search. // QueryOptions defines how we search.
type QueryOptions struct { type QueryOptions struct {
Keywords string `json:"keywords"` Keywords string `json:"keywords"`
@ -37,4 +41,6 @@ type QueryResult struct {
SpaceSlug string `json:"spaceSlug"` SpaceSlug string `json:"spaceSlug"`
Template bool `json:"template"` Template bool `json:"template"`
VersionID string `json:"versionId"` VersionID string `json:"versionId"`
Created time.Time `json:"created"`
Revised time.Time `json:"revised"`
} }