Sfoglia il codice sorgente

fiix: add search to dropdowns and move tags to dropdown

version-14
Prssanna Desai 6 anni fa
parent
commit
20e5e6c151
8 ha cambiato i file con 167 aggiunte e 162 eliminazioni
  1. +1
    -1
      cypress/integration/list_view_settings.js
  2. +30
    -40
      frappe/desk/listview.py
  3. +20
    -14
      frappe/public/js/frappe/list/list_sidebar.html
  4. +76
    -64
      frappe/public/js/frappe/list/list_sidebar.js
  5. +14
    -8
      frappe/public/js/frappe/list/list_sidebar_group_by.html
  6. +16
    -22
      frappe/public/js/frappe/list/list_sidebar_stat.html
  7. +7
    -10
      frappe/public/less/sidebar.less
  8. +3
    -3
      frappe/tests/test_assign.py

+ 1
- 1
cypress/integration/list_view_settings.js Vedi File

@@ -6,7 +6,7 @@ context('List View Settings', () => {
it('Default settings', () => {
cy.visit('/desk#List/DocType/List');
cy.get('.list-count').should('contain', "20 of");
cy.get('.sidebar-stat').should('contain', "No Tags");
cy.get('.sidebar-stat').should('contain', "Tags");
});
it('disable count and sidebar stats then verify', () => {
cy.visit('/desk#List/DocType/List');


+ 30
- 40
frappe/desk/listview.py Vedi File

@@ -25,49 +25,39 @@ def set_list_settings(doctype, values):
doc.update(json.loads(values))
doc.save()

@frappe.whitelist()
def get_user_assignments_and_count(doctype, current_filters):

subquery_condition = ''
if current_filters:
# get the subquery
subquery = frappe.get_all(doctype,
filters=current_filters, return_query = True)
subquery_condition = ' and `tabToDo`.reference_name in ({subquery})'.format(subquery = subquery)

todo_list = frappe.db.sql("""select `tabToDo`.owner as name, count(*) as count
from
`tabToDo`, `tabUser`
where
`tabToDo`.status='Open' and
`tabToDo`.owner = `tabUser`.name and
`tabUser`.user_type = 'System User'
{subquery_condition}
group by
`tabToDo`.owner
order by
count desc
limit 50""".format(subquery_condition = subquery_condition), as_dict=True)

return todo_list

@frappe.whitelist()
def get_group_by_count(doctype, current_filters, field):
current_filters = json.loads(current_filters)
subquery= ''
if current_filters:
subquery = frappe.get_all(doctype,
filters=current_filters, return_query = True)
subquery = subquery[subquery.index('where'):subquery.index('order')]

group_by_list = frappe.db.sql("""select `tab{doctype}`.{field} as name, count(*) as count
from
`tab{doctype}`
{subquery}
group by
`tab{doctype}`.{field}
order by
count desc
limit 50""".format(subquery= subquery, doctype = doctype, field = field), as_dict=True)
subquery_condition = ''
subquery = frappe.get_all(doctype,
filters=current_filters, return_query = True)
if field == 'assigned_to':
subquery_condition = ' and `tabToDo`.reference_name in ({subquery})'.format(subquery = subquery)
group_by_list = frappe.db.sql("""select `tabToDo`.owner as name, count(*) as count
from
`tabToDo`, `tabUser`
where
`tabToDo`.status='Open' and
`tabToDo`.owner = `tabUser`.name and
`tabUser`.user_type = 'System User'
{subquery_condition}
group by
`tabToDo`.owner
order by
count desc
limit 50""".format(subquery_condition = subquery_condition), as_dict=True)
else :
if current_filters:
subquery_condition = subquery[subquery.index('where'):subquery.index('order')]
group_by_list = frappe.db.sql("""select `tab{doctype}`.{field} as name, count(*) as count
from
`tab{doctype}`
{subquery_condition}
group by
`tab{doctype}`.{field}
order by
count desc
limit 50""".format(subquery_condition = subquery_condition, doctype = doctype, field = field), as_dict=True)

return group_by_list

+ 20
- 14
frappe/public/js/frappe/list/list_sidebar.html Vedi File

@@ -57,25 +57,31 @@
{% } %}
</ul>
<ul class="list-unstyled sidebar-menu list-group-by">
<!-- <li class="h6 group-by-title">{%= __("Group By") %}</li> -->
<label class="control-label" style="margin-bottom: 0px;font-size: 10px;">FILTER BY</label>
<li class="assigned-to list-link" style="display: none">
<a class = "dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{%= __("Assigned To") %} <span class="caret"></span>
</a>
<ul class="dropdown-menu assigned-dropdown" style="max-height: 300px; overflow-y: auto;" role="menu">
<li><div class="list-loading text-center assigned-loading text-muted">
{%= (__("Loading") + "..." ) %}
</div>
</li>
</ul>
</li>
<label class="control-label list-group-by-label" style="margin-bottom:0px; font-size: 10px;">FILTER BY</label>
<li class="add-list-group-by list-link" style="margin-top: 10px">
<a class="add-group-by hidden-xs text-muted">{{ __("Add Fields") }}
<!-- <span class="small"><i class="fa fa-plus"></i></span> -->
</a>
</li>
</ul>

<ul class="list-unstyled sidebar-menu sidebar-stat">
<label class="control-label stat-label" style="margin-bottom:0px;font-size: 10px;">TAGS</label>
<li class="list-stats list-link">
<div class="btn-group">
<a class = "dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" href="#" onclick="return false;">
{{ __("Tags") }}<span class="caret"></span>
</a>
<ul class="dropdown-menu list-stats-dropdown" role="menu">
<div class="dropdown-search">
<input type="text" placeholder="Search" class="form-control dropdown-search-input input-xs">
</div>
</ul>
</div>
</li>
<li class="list-link" style="margin-top: 10px;">
<a class="list-tag-preview hidden-xs text-muted">{{ __("Show tags") }}</a>
</li>
</ul>
<ul class="list-unstyled sidebar-menu charts-menu hide">
<li class="h6">{%= __("Charts") %}</li>
<li class="list-link">


+ 76
- 64
frappe/public/js/frappe/list/list_sidebar.js Vedi File

@@ -13,7 +13,6 @@ frappe.views.ListSidebar = class ListSidebar {
constructor(opts) {
$.extend(this, opts);
this.make();
this.get_stats();
this.cat_tags = [];
}

@@ -45,13 +44,14 @@ frappe.views.ListSidebar = class ListSidebar {
this.setup_upgrade_box();
}

if(this.doctype !== 'ToDo') {
$('.assigned-to').show();
if (this.list_view.list_view_settings && this.list_view.list_view_settings.disable_sidebar_stats) {
this.sidebar.find('.sidebar-stat').remove();
} else {
this.sidebar.find('.list-stats').on('click', (e) => {
$(e.currentTarget).find('.stat-link').remove();
this.get_stats();
});
}
$('.assigned-to').on('click', () => {
$('.assigned').remove();
this.setup_assigned_to();
});

}

@@ -232,31 +232,6 @@ frappe.views.ListSidebar = class ListSidebar {
});
}

setup_assigned_to() {
$('.assigned-loading').show();
let dropdown = this.page.sidebar.find('.assigned-dropdown');
let current_filters = this.list_view.get_filters_for_args();

frappe.call('frappe.desk.listview.get_user_assignments_and_count', {doctype: this.doctype, current_filters: current_filters}).then((data) => {
$('.assigned-loading').hide();
let current_user = data.message.find(user => user.name === frappe.session.user);
if(current_user) {
let current_user_count = current_user.count;
this.get_html_for_assigned(frappe.session.user, current_user_count).appendTo(dropdown);
}
let user_list = data.message.filter(user => !['Guest', frappe.session.user, 'Administrator'].includes(user.name) && user.count!==0 );
user_list.forEach((user) => {
this.get_html_for_assigned(user.name, user.count).appendTo(dropdown);
});
$(".assigned-dropdown li a").on("click", (e) => {
let assigned_user = $(e.currentTarget).find($('.assigned-user')).text();
if(assigned_user === 'Me') assigned_user = frappe.session.user;
this.list_view.filter_area.remove('_assign');
this.list_view.filter_area.add(this.list_view.doctype, "_assign", "like", `%${assigned_user}%`);
});
});
}

setup_keyboard_shortcuts() {
this.sidebar.find('.list-link > a, .list-link > .btn-group > a').each((i, el) => {
frappe.ui.keys
@@ -265,21 +240,14 @@ frappe.views.ListSidebar = class ListSidebar {
});
}

get_html_for_assigned(name, count) {
if (name === frappe.session.user) name='Me';
if (count > 99) count='99+';
let html = $('<li class="assigned"><a class="badge-hover" href="#" onclick="return false;" role="assigned-item"><span class="assigned-user">'
+ name + '</span><span class="badge pull-right" style="position:relative">' + count + '</span></a></li>');
return html;
}

setup_list_group_by() {
this.add_group_by_dropdown_fields(['assigned_to']);
let d = new frappe.ui.Dialog ({
title: __("Add Filter By"),
fields: this.get_group_by_dropdown_fields()
});
d.set_primary_action("Add", (values) => {
this.page.sidebar.find('.group-by-field').remove();
this.page.sidebar.find('.group-by-field a').not("[data-fieldname='assigned_to']").remove();
let fields = values[this.doctype];
delete values[this.doctype];
if(!fields) {
@@ -290,59 +258,104 @@ frappe.views.ListSidebar = class ListSidebar {
}
d.hide();
});

this.page.sidebar.find(".add-list-group-by a ").on("click", () => {
d.show();
});
}

get_group_by_count(field, dropdown) {
dropdown.find('.group-by-loading').show();
dropdown.find('.group-by-item').remove();
let current_filters = this.list_view.get_filters_for_args();
let current_filters = this.list_view.get_filters_for_args(), field_list;
frappe.call('frappe.desk.listview.get_group_by_count',
{doctype: this.doctype, current_filters: current_filters, field: field}).then((data) => {
dropdown.find('.group-by-loading').hide();
let field_list = data.message.filter(field => field.count!==0 );
if(field === 'assigned_to') {
let current_user = data.message.find(user => user.name === frappe.session.user);
if(current_user) {
let current_user_count = current_user.count;
this.get_html_for_group_by('Me', current_user_count).appendTo(dropdown);
}
field_list = data.message.filter(user => !['Guest', frappe.session.user, 'Administrator'].includes(user.name) && user.count!==0 );
} else {
field_list = data.message.filter(field => field.count!==0 );
}
field_list.forEach((f) => {
if(f.name === null) {
f.name = 'Not Specified'
f.name = 'Not Specified';
}
this.get_html_for_group_by(f.name, f.count).appendTo(dropdown);
});
if(field_list.length) {
this.setup_dropdown_search(dropdown, '.group-by-value');
} else {
dropdown.find('.dropdown-search').hide();
}
this.setup_group_by_filter(dropdown, field);
});
}

dropdown.find("li a").on("click", (e) => {;
let value = $(e.currentTarget).find($('.group-by-value')).text().trim();
let fieldname = $(e.currentTarget).parents('.group-by-field').children('a').attr('data-fieldname');
setup_group_by_filter(dropdown, field) {
dropdown.find("li a").on("click", (e) => {
let value = $(e.currentTarget).find($('.group-by-value')).text().trim();
let fieldname = field === 'assigned_to'? '_assign': field;
this.list_view.filter_area.remove(field);
if(value === 'Not Specified') {
this.list_view.filter_area.add(this.doctype, fieldname, "like", '');
} else {
if(value === 'Me') value = frappe.session.user;
this.list_view.filter_area.add(this.doctype, fieldname, "like", `%${value}%`);
}
});
}

this.list_view.filter_area.remove(fieldname);
if(value === 'Not Specified') {
this.list_view.filter_area.add(this.doctype, fieldname, "like", '');
setup_dropdown_search(dropdown, text_class) {
let $dropdown_search = dropdown.find('.dropdown-search').show();
let $search_input = $dropdown_search.find('.dropdown-search-input');
$search_input.focus();
$dropdown_search.on('click',(e)=>{
e.stopPropagation();
});
let $elements = dropdown.find('li');
$dropdown_search.on('keyup',()=> {
let text_filter = $search_input.find('.dropdown-search-input').val().toLowerCase();
let text;
for (var i = 0; i < $elements.length; i++) {
text = $elements.eq(i).find(text_class).text();
if (text.toLowerCase().indexOf(text_filter) > -1) {
$elements.eq(i).css('display','');
} else {
$elements.eq(i).css('display','none');
}
else this.list_view.filter_area.add(this.doctype, fieldname, "like", `%${value}%`);
});
}
});
dropdown.parent().on('hide.bs.dropdown',()=> {
$dropdown_search.val('');
});
}

add_group_by_dropdown_fields(fields) {
if(fields) {
fields.forEach((field)=> {
let field_label = frappe.meta.get_label(this.doctype, field);
let field_label = field === 'assigned_to'? 'Assigned To': frappe.meta.get_label(this.doctype, field);
this.list_group_by_dropdown = $(frappe.render_template("list_sidebar_group_by", {
field_label: field_label,
group_by_field: field,
}));

this.list_group_by_dropdown.on('click', (e)=> {
this.get_group_by_count(field, $(e.currentTarget).find('.group-by-dropdown'));
})
this.list_group_by_dropdown.insertBefore(this.page.sidebar.find('.add-list-group-by'));
})
let dropdown = $(e.currentTarget).find('.group-by-dropdown');
dropdown.find('.group-by-loading').show();
dropdown.find('.group-by-item').remove();
this.get_group_by_count(field, dropdown);
});
this.list_group_by_dropdown.insertAfter(this.page.sidebar.find('.list-group-by-label'));
});
}
}

get_html_for_group_by(name, count) {
if (count > 99) count='99+';
let html = $('<li class="group-by-item"><a class="badge-hover"><span class="group-by-value">'
let html = $('<li class="group-by-item"><a class="badge-hover" href="#" onclick="return false;"><span class="group-by-value">'
+ name + '</span><span class="badge pull-right" style="position:relative">' + count + '</span></a></li>');
return html;
}
@@ -401,9 +414,6 @@ frappe.views.ListSidebar = class ListSidebar {

get_stats() {
var me = this;
if (this.list_view.list_view_settings && this.list_view.list_view_settings.disable_sidebar_stats) {
return;
}
frappe.call({
method: 'frappe.desk.reportview.get_sidebar_stats',
type: 'GET',
@@ -436,6 +446,8 @@ frappe.views.ListSidebar = class ListSidebar {
//render normal stats
me.render_stat("_user_tags", (r.message.stats || {})["_user_tags"]);
}
let stats_dropdown = me.sidebar.find('.list-stats-dropdown');
me.setup_dropdown_search(stats_dropdown,'.stat-label');
}
});
}
@@ -498,7 +510,7 @@ frappe.views.ListSidebar = class ListSidebar {
me.list_view.refresh();
});
})
.insertBefore(this.sidebar.find(".close-sidebar-button"));
.appendTo(this.sidebar.find(".list-stats-dropdown"));
}

set_fieldtype(df) {


+ 14
- 8
frappe/public/js/frappe/list/list_sidebar_group_by.html Vedi File

@@ -1,15 +1,21 @@

{% if group_by_field %}
<li class="group-by-field list-link">
<a class = "dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" data-label="{{ field_label %}" data-fieldname="{{ group_by_field %}">
{{field_label}} <span class="caret"></span>
</a>
<ul class="dropdown-menu group-by-dropdown" style="max-height: 300px; overflow-y: auto;">
<li><div class="list-loading text-center group-by-loading text-muted">
{%= (__("Loading") + "..." ) %}
<div class="btn-group">
<a class = "dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"
data-label="{{ field_label %}" data-fieldname="{{ group_by_field %}" href="#" onclick="return false;">
{{field_label}} <span class="caret"></span>
</a>
<ul class="dropdown-menu group-by-dropdown" role="menu">
<div class="dropdown-search">
<input type="text" placeholder="Search" class="form-control dropdown-search-input input-xs">
</div>
</li>
</ul>
<li><div class="list-loading text-center group-by-loading text-muted">
{%= (__("Loading") + "..." ) %}
</div>
</li>
</ul>
</div>
</li>

{% endif %}

+ 16
- 22
frappe/public/js/frappe/list/list_sidebar_stat.html Vedi File

@@ -1,22 +1,16 @@
<ul class="list-unstyled sidebar-menu sidebar-stat">
<li class="divider"></li>
<li class="h6 stat-label">{{ label }}</li>
{% if(!stat.length) { %}
<li class="stat-no-records text-muted">{{ __("No records tagged.") }}</li>
{% } else {
for (var i=0, l=stat.length; i < l; i++) {
var stat_label = stat[i][0];
var stat_count = stat[i][1];
%}
<li>
<a class="stat-link badge-hover" data-label="{{ stat_label %}" data-field="{{ field %}">
<span class="badge">{{ stat_count }}</span>
<span>{{ __(stat_label) }}</span>
</a>
</li>
{% }
} %}
</ul>
<div style="margin-top: 10px;">
<a class="list-tag-preview hidden-xs text-muted">{{ __("Show tags") }}</a>
</div>

{% if(!stat.length) { %}
<li class="stat-no-records text-muted">{{ __("No records tagged.") }}</li>
{% } else {
for (var i=0, l=stat.length; i < l; i++) {
var stat_label = stat[i][0];
var stat_count = stat[i][1];
%}
<li>
<a class="stat-link badge-hover" data-label="{{ stat_label %}" data-field="{{ field %}" href="#" onclick="return false;">
<span class="badge pull-right" style="position: relative">{{ stat_count }}</span>
<span class="stat-label">{{ __(stat_label) }}</span>
</a>
</li>
{% }
} %}

+ 7
- 10
frappe/public/less/sidebar.less Vedi File

@@ -126,9 +126,6 @@ body[data-route^="Module"] .main-menu {
}
}

.stat-link {
margin-bottom: 0.5em;
}

a.close {
position: absolute;
@@ -374,14 +371,14 @@ body[data-route^="Module"] .main-menu {
}


.sidebar-left .list-sidebar {
.stat-label,
.stat-no-records {
.sidebar-padding;
.list-sidebar {
.group-by-dropdown, .list-stats-dropdown {
max-height: 300px;
overflow-y: auto;
max-width: 200px;
}

.stat-label {
margin-bottom: -10px;
.dropdown-search {
padding: 8px;
}
}



+ 3
- 3
frappe/tests/test_assign.py Vedi File

@@ -4,7 +4,7 @@ from __future__ import unicode_literals

import frappe, unittest
import frappe.desk.form.assign_to
from frappe.desk.listview import get_user_assignments_and_count
from frappe.desk.listview import get_group_by_count
from frappe.automation.doctype.assignment_rule.test_assignment_rule import make_note

class TestAssign(unittest.TestCase):
@@ -44,13 +44,13 @@ class TestAssign(unittest.TestCase):
note = make_note()
assign(note, "test_assign2@example.com")

data = {d.name: d.count for d in get_user_assignments_and_count('Note', [])}
data = {d.name: d.count for d in get_group_by_count('Note', '[]', 'assigned_to')}

self.assertTrue('test_assign1@example.com' in data)
self.assertEqual(data['test_assign1@example.com'], 1)
self.assertEqual(data['test_assign2@example.com'], 3)

data = {d.name: d.count for d in get_user_assignments_and_count('Note', [{'public': 1}])}
data = {d.name: d.count for d in get_group_by_count('Note', '[{"public": 1}]', 'assigned_to')}

self.assertFalse('test_assign1@example.com' in data)
self.assertEqual(data['test_assign2@example.com'], 2)


Caricamento…
Annulla
Salva