Merge pull request #12365 from frappe/rebrand-ui-fixes

This commit is contained in:
Prssanna Desai 2021-02-16 13:34:24 +05:30 committed by GitHub
commit 1c2365728d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 250 additions and 47 deletions

View file

@ -17,6 +17,10 @@ class KanbanBoard(Document):
def on_update(self):
frappe.clear_cache(doctype=self.reference_doctype)
def before_insert(self):
for column in self.columns:
column.order = get_order_for_column(self, column.column_name)
def validate_column_name(self):
for column in self.columns:
if not column.column_name:
@ -125,6 +129,53 @@ def update_order(board_name, order):
board.save()
return board, updated_cards
@frappe.whitelist()
def update_order_for_single_card(board_name, docname, from_colname, to_colname, old_index, new_index):
'''Save the order of cards in columns'''
board = frappe.get_doc('Kanban Board', board_name)
doctype = board.reference_doctype
fieldname = board.field_name
old_index = frappe.parse_json(old_index)
new_index = frappe.parse_json(new_index)
# save current order and index of columns to be updated
from_col_order, from_col_idx = get_kanban_column_order_and_index(board, from_colname)
to_col_order, to_col_idx = get_kanban_column_order_and_index(board, to_colname)
if from_colname == to_colname:
from_col_order = to_col_order
to_col_order.insert(new_index, from_col_order.pop((old_index)))
# save updated order
board.columns[from_col_idx].order = frappe.as_json(from_col_order)
board.columns[to_col_idx].order = frappe.as_json(to_col_order)
board.save()
# update changed value in doc
frappe.set_value(doctype, docname, fieldname, to_colname)
return board
def get_kanban_column_order_and_index(board, colname):
for i, col in enumerate(board.columns):
if col.column_name == colname:
col_order = frappe.parse_json(col.order)
col_idx = i
return col_order, col_idx
@frappe.whitelist()
def add_card(board_name, docname, colname):
board = frappe.get_doc('Kanban Board', board_name)
col_order, col_idx = get_kanban_column_order_and_index(board, colname)
col_order.insert(0, docname)
board.columns[col_idx].order = frappe.as_json(col_order)
board.save()
return board
@frappe.whitelist()
def quick_kanban_board(doctype, board_name, field_name, project=None):
@ -133,6 +184,13 @@ def quick_kanban_board(doctype, board_name, field_name, project=None):
doc = frappe.new_doc('Kanban Board')
meta = frappe.get_meta(doctype)
doc.kanban_board_name = board_name
doc.reference_doctype = doctype
doc.field_name = field_name
if project:
doc.filters = '[["Task","project","=","{0}"]]'.format(project)
options = ''
for field in meta.fields:
if field.fieldname == field_name:
@ -149,12 +207,6 @@ def quick_kanban_board(doctype, board_name, field_name, project=None):
column_name=column
))
doc.kanban_board_name = board_name
doc.reference_doctype = doctype
doc.field_name = field_name
if project:
doc.filters = '[["Task","project","=","{0}"]]'.format(project)
if doctype in ['Note', 'ToDo']:
doc.private = 1
@ -162,6 +214,12 @@ def quick_kanban_board(doctype, board_name, field_name, project=None):
doc.save()
return doc
def get_order_for_column(board, colname):
filters = [[board.reference_doctype, board.field_name, '=', colname]]
if board.filters:
filters.append(frappe.parse_json(board.filters)[0])
return frappe.as_json(frappe.get_list(board.reference_doctype, filters=filters, pluck='name'))
@frappe.whitelist()
def update_column_order(board_name, order):

View file

@ -164,7 +164,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
const match_rules_list = frappe.perm.get_match_rules(this.doctype);
if (match_rules_list.length) {
this.restricted_list = $(
`<button class="btn btn-default btn-xs restricted-button flex align-center">
`<button class="btn btn-xs restricted-button flex align-center">
${frappe.utils.icon('restriction', 'xs')}
</button>`
).click(() => this.show_restrictions(match_rules_list)).appendTo(this.page.page_form);
@ -676,7 +676,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
if (col.type === "Tag") {
const tags_display_class = !this.tags_shown ? 'hide' : '';
let tags_html = doc._user_tags ? this.get_tags_html(doc._user_tags) : '<div class="tags-empty">-</div>';
let tags_html = doc._user_tags ? this.get_tags_html(doc._user_tags, 2) : '<div class="tags-empty">-</div>';
return `
<div class="list-row-col tag-col ${tags_display_class} hidden-xs ellipsis">
${tags_html}
@ -790,13 +790,19 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
`;
}
get_tags_html(user_tags) {
get_tags_html(user_tags, limit, colored=false) {
let get_tag_html = tag => {
let color = '', style = '';
if (tag) {
return `<div class="tag-pill ellipsis" title="${tag}">${tag}</div>`;
if (colored) {
color = frappe.get_palette(tag);
style = `background-color: var(${color[0]}); color: var(${color[1]})`;
}
return `<div class="tag-pill ellipsis" title="${tag}" style="${style}">${tag}</div>`;
}
};
return user_tags.split(',').slice(1, 3).map(get_tag_html).join('');
return user_tags.split(',').slice(1, limit + 1).map(get_tag_html).join('');
}
get_meta_html(doc) {

View file

@ -114,6 +114,7 @@ frappe.ui.Page = Class.extend({
this.get_main_icon(this.icon);
this.body = this.main = this.wrapper.find(".layout-main-section");
this.container = this.wrapper.find(".page-body");
this.sidebar = this.wrapper.find(".layout-side-section");
this.footer = this.wrapper.find(".layout-footer");
this.indicator = this.wrapper.find(".indicator-pill");

View file

@ -124,7 +124,12 @@ frappe.provide("frappe.views");
const new_cards = state.cards.slice();
new_cards[index] = card;
updater.set({ cards: new_cards });
fluxify.doAction('update_order');
const args = {
new: 1,
name: card.name,
colname: updated_doc[state.board.field_name],
};
fluxify.doAction('update_order_for_single_card', args);
});
} else {
frappe.new_doc(this.doctype, doc);
@ -155,6 +160,53 @@ frappe.provide("frappe.views");
fluxify.doAction('update_card', updated_card);
});
},
update_order_for_single_card: function(updater, card) {
// cache original order
const _cards = this.cards.slice();
const _columns = this.columns.slice();
let args = {};
let method_name = "";
if (card.new) {
method_name = "add_card";
args = {
board_name: this.board.name,
docname: card.name,
colname: card.colname,
};
} else {
method_name = "update_order_for_single_card";
args = {
board_name: this.board.name,
docname: card.name,
from_colname: card.from_colname,
to_colname: card.to_colname,
old_index: card.old_index,
new_index: card.new_index,
};
}
frappe.call({
method: method_prefix + method_name,
args: args,
callback: (r) => {
let board = r.message;
let updated_cards = [{'name': card.name, 'column': card.to_colname || card.colname}];
let cards = update_cards_column(updated_cards);
let columns = prepare_columns(board.columns);
updater.set({
cards: cards,
columns: columns
});
}
}).fail(function() {
// revert original order
updater.set({
cards: _cards,
columns: _columns
});
});
},
update_order: function(updater) {
// cache original order
const _cards = this.cards.slice();
@ -446,16 +498,24 @@ frappe.provide("frappe.views");
group: "cards",
animation: 150,
dataIdAttr: 'data-name',
forceFallback: true,
onStart: function() {
wrapper.find('.kanban-card.add-card').fadeOut(200, function() {
wrapper.find('.kanban-cards').height('100vh');
});
},
onEnd: function() {
onEnd: function(e) {
wrapper.find('.kanban-card.add-card').fadeIn(100);
wrapper.find('.kanban-cards').height('auto');
// update order
fluxify.doAction('update_order');
const args = {
name: $(e.item).attr('data-name'),
from_colname: $(e.from).parents('.kanban-column').attr('data-column-value'),
to_colname: $(e.to).parents('.kanban-column').attr('data-column-value'),
old_index: e.oldIndex,
new_index: e.newIndex,
};
fluxify.doAction('update_order_for_single_card', args);
},
onAdd: function() {
},
@ -546,14 +606,24 @@ frappe.provide("frappe.views");
var opts = {
name: card.name,
title: remove_img_tags(card.title),
disable_click: card._disable_click ? 'disable-click' : ''
disable_click: card._disable_click ? 'disable-click' : '',
creation: card.creation,
};
self.$card = $(frappe.render_template('kanban_card', opts))
.appendTo(wrapper);
}
function get_tags_html(card) {
return card.tags
? `<div class="kanban-tags">
${cur_list.get_tags_html(card.tags, 3, true)}
</div>`
: '';
}
function render_card_meta() {
var html = "";
let html = get_tags_html(card);
if (card.comment_count > 0)
html +=
`<span class="list-comment-count small text-muted ">
@ -563,7 +633,10 @@ frappe.provide("frappe.views");
const $assignees_group = get_assignees_group();
html += `<span class="kanban-assignments"></span>`;
html += `
<span class="kanban-assignments"></span>
${cur_list.get_like_html(card)}
`;
if (card.color && frappe.ui.color.validate_hex(card.color)) {
const $div = $('<div>');
@ -630,6 +703,8 @@ frappe.provide("frappe.views");
doctype: state.doctype,
name: card.name,
title: card[state.card_meta.title_field.fieldname],
creation: moment(card.creation).format('MMM DD, YYYY'),
tags: card._user_tags,
column: card[state.board.field_name],
assigned_list: card.assigned_list || assigned_list,
comment_count: card.comment_count || comment_count,

View file

@ -2,9 +2,12 @@
<a class="kanban-card-redirect" href="#">
<div class="kanban-card content">
<div class="kanban-title-area">
<div class="kanban-card-title ellipsis" title="{{title}}">
<div class="kanban-card-title" title="{{title}}">
{{ title }}
</div>
<div class="kanban-card-creation">
{{ creation }}
</div>
</div>
<div class="kanban-card-meta">
</div>

View file

@ -73,7 +73,11 @@ frappe.views.KanbanView = class KanbanView extends frappe.views.ListView {
}
setup_view() {
if (this.board.columns.length > 5) {
this.page.container.addClass('full-width');
}
this.setup_realtime_updates();
this.setup_like();
}
set_fields() {

View file

@ -15,7 +15,7 @@
.kanban {
display: flex;
overflow: auto;
overflow-y: hidden;
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
@ -32,19 +32,7 @@
border-radius: var(--border-radius);
padding: var(--padding-md);
min-height: calc(100vh - 250px);
&.add-new-column {
order: 1;
}
&:hover {
.add-card {
background-color: var(--kanban-new-card-hover-bg);
box-shadow: var(--shadow-xs);
}
background-color: var(--kanban-new-card-bg);
}
max-height: calc(75vh - 10px);
.add-card {
@include flex(flex, center, center, null);
@ -70,6 +58,17 @@
}
}
.kanban-column:not(.add-new-column) {
&:hover {
.add-card {
background-color: var(--kanban-new-card-hover-bg);
box-shadow: var(--shadow-xs);
}
background-color: var(--kanban-new-card-bg);
}
}
.kanban-column-header {
@include flex(flex, space-between, null, null);
margin-top: 0;
@ -138,7 +137,6 @@
}
.kanban-cards {
min-height: 100px;
max-height: calc(100vh - 250px);
margin: -5px;
padding: 5px;
@ -191,10 +189,20 @@
}
}
.kanban-card-title {
max-width: 90%;
font-size: $font-size-base;
font-weight: 500;
.kanban-title-area {
margin-bottom: 12px;
.kanban-card-title {
max-width: 90%;
font-size: var(--text-md);
font-weight: 500;
}
.kanban-card-creation {
font-size: var(--text-md);
color: var(--text-muted);
margin-top: var(--margin-xs);
}
}
.kanban-card-edit {
@ -232,28 +240,71 @@
}
}
.add-new-column {
display: flex;
justify-content: center;
align-items: center;
min-height: 65px;
.kanban-column.add-new-column {
color: var(--text-muted);
border: 1px dashed var(--gray-400);
max-height: 80px;
background-color: transparent;
}
order: 1;
.add-new-column:hover {
background-color: var(--kanban-column-bg);
&:hover {
background-color: none;
}
.kanban-column-title.compose-column {
@include flex(flex, center, center, null);
min-height: 65px;
border-radius: var(--border-radius);
border: 1px dashed var(--gray-400);
font-size: var(--text-base);
&:hover {
background-color: var(--kanban-column-bg);
}
}
}
.kanban-card-meta {
.list-comment-count {
width: 30px;
}
.like-action:not(.liked) {
.icon use {
stroke: var(--text-muted);
}
}
.kanban-tags {
font-size: var(--text-sm);
margin-bottom: 8px;
.tag-pill {
border-radius: 100px;
height: 22px;
width: auto;
padding: 2px 8px;
margin-right: var(--margin-xs);
}
}
.kanban-assignments {
display: flex;
float: right;
.avatar {
cursor: default;
width: 22px;
height: 22px;
}
.avatar-action {
width: 22px;
height: 22px;
.icon {
width: 12px;
height: 12px;
}
}
}
}

View file

@ -44,6 +44,11 @@
.page-container {
background-color: var(--bg-color);
.page-body.full-width {
width: 100%;
max-width: 100%;
}
}
.custom-actions {