Merge pull request #12365 from frappe/rebrand-ui-fixes
This commit is contained in:
commit
1c2365728d
8 changed files with 250 additions and 47 deletions
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,11 @@
|
|||
|
||||
.page-container {
|
||||
background-color: var(--bg-color);
|
||||
|
||||
.page-body.full-width {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-actions {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue