1
0
Fork 0
mirror of https://github.com/documize/community.git synced 2025-07-18 20:59:43 +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,
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,
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
dmz_search s,
dmz_doc d
@ -337,7 +337,7 @@ func (s Store) matchLike(ctx domain.RequestContext, keywords, itemType string) (
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,
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
dmz_search s,
dmz_doc d

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

@ -9,11 +9,13 @@
//
// https://documize.com
import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import { A } from '@ember/array';
import Component from '@ember/component';
export default Component.extend({
localStorage: service(),
showDeleteDialog: false,
showMoveDialog: false,
selectedDocuments: A([]),
@ -36,9 +38,53 @@ export default Component.extend({
let targets = _.reject(this.get('spaces'), {id: space.get('id')});
this.set('moveOptions', A(targets));
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() {
this.set('showDeleteDialog', true);
},

View file

@ -10,13 +10,23 @@
// https://documize.com
import { computed } from '@ember/object';
import { inject as service } from '@ember/service';
import Component from '@ember/component';
export default Component.extend({
localStorage: service('localStorage'),
resultPhrase: '',
searchQuery: computed('keywords', function() {
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() {
this._super(...arguments);
@ -34,10 +44,84 @@ export default Component.extend({
let docLabel = duped.length === 1 ? "document" : "documents";
let i = docs.length;
let j = duped.length;
phrase = `${i} ${references} across ${j} ${docLabel}`;
phrase = `${i} ${references} in ${j} ${docLabel}`;
}
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: '' ,
matchFilter: null,
// init() {
// this._super(...arguments);
// },
didReceiveAttrs() {
this._super(...arguments);
this.set('keywords', this.get('filter'));

View file

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

View file

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

View file

@ -23,6 +23,14 @@ export default Controller.extend(NotifierMixin, {
queryParams: ['category'],
category: '',
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: {
onRefresh() {
@ -80,6 +88,30 @@ export default Controller.extend(NotifierMixin, {
},
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);
}
}

View file

@ -44,13 +44,15 @@
onRefresh=(action "onRefresh")}}
</div>
</div>
{{folder/documents-list
documents=filteredDocs
spaces=model.folders
space=model.folder
templates=model.templates
permissions=model.permissions
sortBy=sortBy
onFiltered=(action "onFiltered")
onExportDocument=(action "onExportDocument")
onDeleteDocument=(action "onDeleteDocument")
onMoveDocument=(action "onMoveDocument")}}

View file

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

View file

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

View file

@ -4,6 +4,9 @@
@mixin button-shadow() {
box-shadow: 1px 1px 3px 0px map-get($gray-shades, 500);
}
@mixin button-shadow-none() {
box-shadow: none;
}
%dmz-button {
display: inline-block;
@ -153,6 +156,26 @@
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 {
@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 {
background-color: $color-white;
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);
@include border-radius(3px);
-webkit-box-shadow: 3px 3px 33px 0 rgba(0, 0, 0, 0.16), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
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 {
margin: 4px;
@ -31,7 +35,7 @@
&:hover {
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 {
padding: 20px;
width: 300px;

View file

@ -1,4 +1,59 @@
<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">
{{#each documents key="id" as |document|}}
<li class="document {{if document.selected "selected"}}" id="document-{{document.id}}">

View file

@ -1,4 +1,61 @@
<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>
<ul class="documents">
{{#each documents key="id" as |result|}}
@ -17,4 +74,5 @@
</li>
{{/each}}
</ul>
</div>

View file

@ -11,6 +11,10 @@
package search
import (
"time"
)
// QueryOptions defines how we search.
type QueryOptions struct {
Keywords string `json:"keywords"`
@ -23,18 +27,20 @@ type QueryOptions struct {
// QueryResult represents 'presentable' search results.
type QueryResult struct {
ID string `json:"id"`
OrgID string `json:"orgId"`
ItemID string `json:"itemId"`
ItemType string `json:"itemType"`
DocumentID string `json:"documentId"`
DocumentSlug string `json:"documentSlug"`
Document string `json:"document"`
Excerpt string `json:"excerpt"`
Tags string `json:"tags"`
SpaceID string `json:"spaceId"`
Space string `json:"space"`
SpaceSlug string `json:"spaceSlug"`
Template bool `json:"template"`
VersionID string `json:"versionId"`
ID string `json:"id"`
OrgID string `json:"orgId"`
ItemID string `json:"itemId"`
ItemType string `json:"itemType"`
DocumentID string `json:"documentId"`
DocumentSlug string `json:"documentSlug"`
Document string `json:"document"`
Excerpt string `json:"excerpt"`
Tags string `json:"tags"`
SpaceID string `json:"spaceId"`
Space string `json:"space"`
SpaceSlug string `json:"spaceSlug"`
Template bool `json:"template"`
VersionID string `json:"versionId"`
Created time.Time `json:"created"`
Revised time.Time `json:"revised"`
}