* first cut * Code refactoring, styling * Added Sorting * Revert query_report to use slickgrid * cleanup * Edit cell working * Add regrid, remove datatable * Add clusterize * Update lib, fix get_checked_items * New ReportView * wip * Enable editing, fix styles * update lib * wip * fix refresh rows and editable cells * Refresh list_view every 3s, decouple refreshing logic * Report editing fixes * Cleanup loading fields, add column then refresh list * [wip] New List View * [working] Render results * ListView is now BaseList, add new ListView and GanttView * Create new page for each ListView * GanttView working * CalendarView working * KanbanView working * Cache list_view based on page_name * Gantt view buttons on mobile * Add ReportView * Refresh datatable on render * Setup like * [start][filters] clean up FilterList * [filters] refactor FilterList * [filters] minor fix * [filters] fix remove filter * filter utils * more utils, remove apply * rewrite as class, remove 'me' references * [filter] implement on_change to decouple parent functions * Integrate new filters with new BaseList * Setup freeze area for ListView * Set breadcrumbs on setup_page * Trigger list update from events * Setup footnote area * Fix Kanban Board filters * Add filters to standard filters, then filter_list * Remove old files * Fix ImageView * Some more fixes for BaseList.init * Fix order_by on load * Report View: remember columns * Fix for hidden filters * Fix for delete items * InboxView * Shift select checkboxes * Fix ESLint errors * More refactoring - Move ListMenu to Listview - New FileView - Ability to add custom breadcrumbs * FileManager working * Tags, set filters from route options * Custom Reports Working * List Sidebar reports * Report Name as title * Fix ESLint errors * Fix UI tests * Fix Kanban test * Format ID column * [fix] Kanban cards title * Checkbox fix * Fix Activity Page * Update rows in Report in place * Child Table columns in Report View
948 lines
24 KiB
JavaScript
948 lines
24 KiB
JavaScript
frappe.provide("frappe.views");
|
|
|
|
(function () {
|
|
|
|
var method_prefix = 'frappe.desk.doctype.kanban_board.kanban_board.';
|
|
var saving_filters = false;
|
|
|
|
var store = fluxify.createStore({
|
|
id: 'store',
|
|
initialState: {
|
|
doctype: '',
|
|
board: {},
|
|
card_meta: {},
|
|
cards: [],
|
|
columns: [],
|
|
filters_modified: false,
|
|
cur_list: {},
|
|
empty_state: true
|
|
},
|
|
actionCallbacks: {
|
|
init: function (updater, opts) {
|
|
updater.set({
|
|
empty_state: true
|
|
});
|
|
|
|
get_board(opts.board_name)
|
|
.then(function (board) {
|
|
var card_meta = get_card_meta(opts);
|
|
opts.card_meta = card_meta;
|
|
opts.board = board;
|
|
var cards = opts.cards.map(function (card) {
|
|
return prepare_card(card, opts);
|
|
});
|
|
var columns = prepare_columns(board.columns);
|
|
|
|
updater.set({
|
|
doctype: opts.doctype,
|
|
board: board,
|
|
card_meta: card_meta,
|
|
cards: cards,
|
|
columns: columns,
|
|
cur_list: opts.cur_list,
|
|
empty_state: false
|
|
});
|
|
})
|
|
.fail(function() {
|
|
// redirect back to List
|
|
setTimeout(() => {
|
|
frappe.set_route('List', opts.doctype, 'List');
|
|
}, 2000);
|
|
});
|
|
},
|
|
update_cards: function (updater, cards) {
|
|
var state = this;
|
|
var _cards = cards
|
|
.map(card => prepare_card(card, state))
|
|
.concat(this.cards)
|
|
.uniqBy(card => card.name);
|
|
|
|
updater.set({
|
|
cards: _cards
|
|
});
|
|
},
|
|
add_column: function (updater, col) {
|
|
if(frappe.model.can_create('Custom Field')) {
|
|
fluxify.doAction('update_column', col, 'add');
|
|
} else {
|
|
frappe.msgprint({
|
|
title: __('Not permitted'),
|
|
message: __('You are not allowed to create columns'),
|
|
indicator: 'red'
|
|
});
|
|
}
|
|
},
|
|
archive_column: function (updater, col) {
|
|
fluxify.doAction('update_column', col, 'archive');
|
|
},
|
|
restore_column: function (updater, col) {
|
|
fluxify.doAction('update_column', col, 'restore');
|
|
},
|
|
update_column: function (updater, col, action) {
|
|
var doctype = this.doctype;
|
|
var board = this.board;
|
|
fetch_customization(doctype)
|
|
.then(function (doc) {
|
|
return modify_column_field_in_c11n(doc, board, col.title, action);
|
|
})
|
|
.then(save_customization)
|
|
.then(function () {
|
|
return update_kanban_board(board.name, col.title, action);
|
|
}).then(function (r) {
|
|
var cols = r.message;
|
|
updater.set({
|
|
columns: prepare_columns(cols)
|
|
});
|
|
}, function (err) {
|
|
console.error(err); // eslint-disable-line
|
|
});
|
|
},
|
|
set_filter_state: function (updater) {
|
|
is_filters_modified(this.board, this.cur_list)
|
|
.then(function(flag) {
|
|
updater.set({
|
|
filters_modified: flag
|
|
});
|
|
});
|
|
},
|
|
save_filters: function (updater) {
|
|
if(saving_filters) return;
|
|
saving_filters = true;
|
|
var filters = JSON.stringify(this.cur_list.filter_area.get());
|
|
frappe.call({
|
|
method: method_prefix + 'save_filters',
|
|
args: {
|
|
board_name: this.board.name,
|
|
filters: filters
|
|
}
|
|
}).then(function() {
|
|
saving_filters = false;
|
|
updater.set({ filters_modified: false });
|
|
frappe.show_alert({
|
|
message: __('Filters saved'),
|
|
indicator: 'green'
|
|
}, 0.5);
|
|
});
|
|
},
|
|
add_card: function (updater, card_title, column_title) {
|
|
var doc = frappe.model.get_new_doc(this.doctype);
|
|
var field = this.card_meta.title_field;
|
|
var quick_entry = this.card_meta.quick_entry;
|
|
var state = this;
|
|
|
|
var doc_fields = {};
|
|
doc_fields[field.fieldname] = card_title;
|
|
doc_fields[this.board.field_name] = column_title;
|
|
this.board.filters_array.forEach(function (f) {
|
|
if (f[2] !== "=") return;
|
|
doc_fields[f[1]] = f[3];
|
|
});
|
|
|
|
$.extend(doc, doc_fields);
|
|
|
|
if (field && !quick_entry) {
|
|
return insert_doc(doc)
|
|
.then(function (r) {
|
|
var updated_doc = r.message;
|
|
var card = prepare_card(doc, state, updated_doc);
|
|
var cards = state.cards.slice();
|
|
cards.push(card);
|
|
updater.set({ cards: cards });
|
|
});
|
|
} else {
|
|
frappe.new_doc(this.doctype, doc);
|
|
}
|
|
},
|
|
update_card: function (updater, card) {
|
|
var index = -1;
|
|
this.cards.forEach(function (c, i) {
|
|
if (c.name === card.name) {
|
|
index = i;
|
|
}
|
|
});
|
|
var cards = this.cards.slice();
|
|
if (index !== -1) {
|
|
cards.splice(index, 1, card);
|
|
}
|
|
updater.set({ cards: cards });
|
|
},
|
|
update_doc: function (updater, doc, card) {
|
|
var state = this;
|
|
return frappe.call({
|
|
method: method_prefix + "update_doc",
|
|
args: { doc: doc },
|
|
freeze: true
|
|
}).then(function (r) {
|
|
var updated_doc = r.message;
|
|
var updated_card = prepare_card(card, state, updated_doc);
|
|
fluxify.doAction('update_card', updated_card);
|
|
});
|
|
},
|
|
update_order: function(updater, order) {
|
|
// cache original order
|
|
const _cards = this.cards.slice();
|
|
const _columns = this.columns.slice();
|
|
|
|
frappe.call({
|
|
method: method_prefix + "update_order",
|
|
args: {
|
|
board_name: this.board.name,
|
|
order: order
|
|
},
|
|
callback: (r) => {
|
|
var board = r.message[0];
|
|
var updated_cards = r.message[1];
|
|
var cards = update_cards_column(updated_cards);
|
|
var columns = prepare_columns(board.columns);
|
|
updater.set({
|
|
cards: cards,
|
|
columns: columns
|
|
});
|
|
}
|
|
}).fail(function() {
|
|
// revert original order
|
|
updater.set({
|
|
cards: _cards,
|
|
columns: _columns
|
|
});
|
|
});
|
|
},
|
|
update_column_order: function(updater, order) {
|
|
return frappe.call({
|
|
method: method_prefix + "update_column_order",
|
|
args: {
|
|
board_name: this.board.name,
|
|
order: order
|
|
}
|
|
}).then(function(r) {
|
|
var board = r.message;
|
|
var columns = prepare_columns(board.columns);
|
|
updater.set({
|
|
columns: columns
|
|
});
|
|
});
|
|
},
|
|
set_indicator: function(updater, column, color) {
|
|
return frappe.call({
|
|
method: method_prefix + "set_indicator",
|
|
args: {
|
|
board_name: this.board.name,
|
|
column_name: column.title,
|
|
indicator: color
|
|
}
|
|
}).then(function(r) {
|
|
var board = r.message;
|
|
var columns = prepare_columns(board.columns);
|
|
updater.set({
|
|
columns: columns
|
|
});
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
frappe.views.KanbanBoard = function (opts) {
|
|
|
|
var self = {};
|
|
self.wrapper = opts.wrapper;
|
|
self.cur_list = opts.cur_list;
|
|
self.board_name = opts.board_name;
|
|
|
|
self.update = function(cards) {
|
|
// update cards internally
|
|
opts.cards = cards;
|
|
|
|
if(self.wrapper.find('.kanban').length > 0 && self.cur_list.start !== 0) {
|
|
fluxify.doAction('update_cards', cards);
|
|
} else {
|
|
init();
|
|
}
|
|
};
|
|
|
|
function init() {
|
|
fluxify.doAction('init', opts);
|
|
store.on('change:columns', make_columns);
|
|
prepare();
|
|
store.on('change:cur_list', setup_restore_columns);
|
|
store.on('change:columns', setup_restore_columns);
|
|
store.on('change:empty_state', show_empty_state);
|
|
}
|
|
|
|
function prepare() {
|
|
self.$kanban_board = self.wrapper.find('.kanban');
|
|
|
|
if(self.$kanban_board.length === 0) {
|
|
self.$kanban_board = $(frappe.render_template("kanban_board"));
|
|
self.$kanban_board.appendTo(self.wrapper);
|
|
}
|
|
|
|
self.$filter_area = self.cur_list.$page.find('.active-tag-filters');
|
|
bind_events();
|
|
setup_sortable();
|
|
}
|
|
|
|
function make_columns() {
|
|
self.$kanban_board.find(".kanban-column").not(".add-new-column").remove();
|
|
var columns = store.getState().columns;
|
|
|
|
columns.filter(is_active_column).map(function (col) {
|
|
frappe.views.KanbanBoardColumn(col, self.$kanban_board);
|
|
});
|
|
}
|
|
|
|
function bind_events() {
|
|
bind_add_column();
|
|
bind_save_filter();
|
|
}
|
|
|
|
function setup_sortable() {
|
|
var sortable = new Sortable(self.$kanban_board.get(0), {
|
|
group: 'columns',
|
|
animation: 150,
|
|
dataIdAttr: 'data-column-value',
|
|
filter: '.add-new-column',
|
|
handle: '.kanban-column-title',
|
|
onEnd: function() {
|
|
var order = sortable.toArray();
|
|
order = order.slice(1);
|
|
fluxify.doAction('update_column_order', order);
|
|
}
|
|
});
|
|
}
|
|
|
|
function bind_add_column() {
|
|
|
|
var $add_new_column = self.$kanban_board.find(".add-new-column"),
|
|
$compose_column = $add_new_column.find(".compose-column"),
|
|
$compose_column_form = $add_new_column.find(".compose-column-form").hide();
|
|
|
|
$compose_column.on('click', function () {
|
|
$(this).hide();
|
|
$compose_column_form.show();
|
|
$compose_column_form.find('input').focus();
|
|
});
|
|
|
|
//save on enter
|
|
$compose_column_form.keydown(function (e) {
|
|
if (e.which == 13) {
|
|
e.preventDefault();
|
|
if (!frappe.request.ajax_count) {
|
|
// not already working -- double entry
|
|
var title = $compose_column_form.serializeArray()[0].value;
|
|
var col = {
|
|
title: title.trim()
|
|
};
|
|
fluxify.doAction('add_column', col);
|
|
$compose_column_form.find('input').val('');
|
|
$compose_column.show();
|
|
$compose_column_form.hide();
|
|
}
|
|
}
|
|
});
|
|
|
|
// on form blur
|
|
$compose_column_form.find('input').on("blur", function () {
|
|
$(this).val('');
|
|
$compose_column.show();
|
|
$compose_column_form.hide();
|
|
});
|
|
}
|
|
|
|
function bind_save_filter() {
|
|
var set_filter_state = function () {
|
|
fluxify.doAction('set_filter_state');
|
|
};
|
|
|
|
if(isBound(self.$kanban_board, 'after-refresh', set_filter_state)) return;
|
|
|
|
store.on('change:filters_modified', function (modified) {
|
|
if(modified) fluxify.doAction('save_filters');
|
|
});
|
|
self.$kanban_board.on('after-refresh', set_filter_state);
|
|
}
|
|
|
|
function setup_restore_columns() {
|
|
var cur_list = store.getState().cur_list;
|
|
var columns = store.getState().columns;
|
|
var list_row_right = cur_list.$page
|
|
.find(`[data-list-renderer='Kanban'] .list-row-right`)
|
|
.css('margin-right', '15px');
|
|
list_row_right.empty();
|
|
|
|
var archived_columns = columns.filter(function (col) {
|
|
return col.status === 'Archived';
|
|
});
|
|
|
|
if (!archived_columns.length) return;
|
|
|
|
var options = archived_columns.reduce(function (a, b) {
|
|
return a + "<li><a class='option'>" +
|
|
"<span class='ellipsis' style='max-width: 100px; display: inline-block'>" +
|
|
__(b.title) + "</span>" +
|
|
"<button style='float:right;' data-column='" + b.title +
|
|
"' class='btn btn-default btn-xs restore-column text-muted'>"
|
|
+ __('Restore') + "</button></a></li>";
|
|
}, "");
|
|
var $dropdown = $("<div class='dropdown pull-right'>" +
|
|
"<a class='text-muted dropdown-toggle' data-toggle='dropdown'>" +
|
|
"<span class='dropdown-text'>" + __('Archived Columns') + "</span><i class='caret'></i></a>" +
|
|
"<ul class='dropdown-menu'>" + options + "</ul>" +
|
|
"</div>");
|
|
|
|
list_row_right.html($dropdown);
|
|
|
|
$dropdown.find(".dropdown-menu").on("click", "button.restore-column", function () {
|
|
var column_title = $(this).data().column;
|
|
var col = {
|
|
title: column_title,
|
|
status: 'Archived'
|
|
};
|
|
fluxify.doAction('restore_column', col);
|
|
});
|
|
}
|
|
|
|
function show_empty_state() {
|
|
var empty_state = store.getState().empty_state;
|
|
|
|
if(empty_state) {
|
|
self.$kanban_board.find('.kanban-column').hide();
|
|
self.$kanban_board.find('.kanban-empty-state').show();
|
|
} else {
|
|
self.$kanban_board.find('.kanban-column').show();
|
|
self.$kanban_board.find('.kanban-empty-state').hide();
|
|
}
|
|
}
|
|
|
|
init();
|
|
|
|
return self;
|
|
};
|
|
|
|
frappe.views.KanbanBoardColumn = function (column, wrapper) {
|
|
var self = {};
|
|
var filtered_cards = [];
|
|
|
|
function init() {
|
|
make_dom();
|
|
setup_sortable();
|
|
make_cards();
|
|
store.on('change:cards', make_cards);
|
|
bind_add_card();
|
|
bind_options();
|
|
}
|
|
|
|
function make_dom() {
|
|
self.$kanban_column = $(frappe.render_template(
|
|
'kanban_column', {
|
|
title: column.title,
|
|
doctype: store.getState().doctype,
|
|
indicator: column.indicator
|
|
})).appendTo(wrapper);
|
|
self.$kanban_cards = self.$kanban_column.find('.kanban-cards');
|
|
}
|
|
|
|
function make_cards() {
|
|
self.$kanban_cards.empty();
|
|
var cards = store.getState().cards;
|
|
filtered_cards = get_cards_for_column(cards, column);
|
|
var filtered_cards_names = filtered_cards.map(card => card.name);
|
|
|
|
var order = column.order;
|
|
if(order) {
|
|
order = JSON.parse(order);
|
|
order.forEach(function(name) {
|
|
if (!filtered_cards_names.includes(name)) return;
|
|
frappe.views.KanbanBoardCard(get_card(name), self.$kanban_cards);
|
|
});
|
|
// new cards
|
|
filtered_cards.forEach(function(card) {
|
|
if(order.indexOf(card.name) === -1) {
|
|
frappe.views.KanbanBoardCard(card, self.$kanban_cards);
|
|
}
|
|
});
|
|
} else {
|
|
filtered_cards.map(function (card) {
|
|
frappe.views.KanbanBoardCard(card, self.$kanban_cards);
|
|
});
|
|
}
|
|
}
|
|
|
|
function setup_sortable() {
|
|
Sortable.create(self.$kanban_cards.get(0), {
|
|
group: "cards",
|
|
animation: 150,
|
|
dataIdAttr: 'data-name',
|
|
onStart: function () {
|
|
wrapper.find('.kanban-card.add-card').fadeOut(200, function () {
|
|
wrapper.find('.kanban-cards').height('100vh');
|
|
});
|
|
},
|
|
onEnd: function () {
|
|
wrapper.find('.kanban-card.add-card').fadeIn(100);
|
|
wrapper.find('.kanban-cards').height('auto');
|
|
// update order
|
|
var order = {};
|
|
wrapper.find('.kanban-column[data-column-value]')
|
|
.each(function() {
|
|
var col_name = $(this).data().columnValue;
|
|
order[col_name] = [];
|
|
$(this).find('.kanban-card-wrapper').each(function() {
|
|
var card_name = $(this).data().name;
|
|
order[col_name].push(card_name);
|
|
});
|
|
});
|
|
fluxify.doAction('update_order', order);
|
|
},
|
|
onAdd: function () {
|
|
},
|
|
});
|
|
}
|
|
|
|
function bind_add_card() {
|
|
var $wrapper = self.$kanban_column;
|
|
var $btn_add = $wrapper.find('.add-card');
|
|
var $new_card_area = $wrapper.find('.new-card-area');
|
|
var $textarea = $new_card_area.find('textarea');
|
|
|
|
//Add card button
|
|
$new_card_area.hide();
|
|
$btn_add.on('click', function () {
|
|
$btn_add.hide();
|
|
$new_card_area.show();
|
|
$textarea.focus();
|
|
});
|
|
|
|
//save on enter
|
|
$new_card_area.keydown(function (e) {
|
|
if (e.which == 13) {
|
|
e.preventDefault();
|
|
if (!frappe.request.ajax_count) {
|
|
// not already working -- double entry
|
|
e.preventDefault();
|
|
var card_title = $textarea.val();
|
|
fluxify.doAction('add_card', card_title, column.title)
|
|
.then(() => {
|
|
$btn_add.show();
|
|
$new_card_area.hide();
|
|
$textarea.val('');
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
// on textarea blur
|
|
$textarea.on("blur", function () {
|
|
$(this).val('');
|
|
$btn_add.show();
|
|
$new_card_area.hide();
|
|
});
|
|
}
|
|
|
|
function bind_options() {
|
|
self.$kanban_column.find(".column-options .dropdown-menu")
|
|
.on("click", "[data-action]", function () {
|
|
var $btn = $(this);
|
|
var action = $btn.data().action;
|
|
|
|
if (action === "archive") {
|
|
fluxify.doAction('archive_column', column);
|
|
} else if (action === "indicator") {
|
|
var color = $btn.data().indicator;
|
|
fluxify.doAction('set_indicator', column, color);
|
|
}
|
|
});
|
|
get_column_indicators(function(indicators) {
|
|
var html = '<li class="button-group">';
|
|
html += indicators.reduce(function(prev, curr) {
|
|
return prev + '<div \
|
|
data-action="indicator" data-indicator="'+curr+'"\
|
|
class="btn btn-default btn-xs indicator ' + curr + '"></div>';
|
|
}, "");
|
|
html += '</li>';
|
|
self.$kanban_column.find(".column-options .dropdown-menu")
|
|
.append(html);
|
|
});
|
|
}
|
|
|
|
init();
|
|
};
|
|
|
|
frappe.views.KanbanBoardCard = function (card, wrapper) {
|
|
var self = {};
|
|
|
|
function init() {
|
|
if(!card) return;
|
|
make_dom();
|
|
render_card_meta();
|
|
bind_edit_card();
|
|
// edit_card_title();
|
|
}
|
|
|
|
function make_dom() {
|
|
var opts = {
|
|
name: card.name,
|
|
title: remove_img_tags(card.title)
|
|
};
|
|
self.$card = $(frappe.render_template('kanban_card', opts))
|
|
.appendTo(wrapper);
|
|
}
|
|
|
|
function render_card_meta() {
|
|
var html = "";
|
|
if (card.comment_count > 0)
|
|
html += '<span class="list-comment-count small text-muted ">' +
|
|
'<i class="octicon octicon-comment"></i> ' + card.comment_count +
|
|
'</span>';
|
|
html += get_assignees_html();
|
|
|
|
if (card.color && frappe.ui.color.validate_hex(card.color)) {
|
|
const $div = $('<div>');
|
|
$('<div></div>').css({
|
|
width: '20px',
|
|
height: '5px',
|
|
borderRadius: '2px',
|
|
marginBottom: '4px',
|
|
backgroundColor: card.color
|
|
}).appendTo($div);
|
|
|
|
self.$card.find('.kanban-card.content').prepend($div);
|
|
}
|
|
|
|
self.$card.find(".kanban-card-meta").empty().append(html);
|
|
}
|
|
|
|
function bind_edit_card() {
|
|
self.$card.find('.kanban-card.content').on('click', function () {
|
|
frappe.set_route('Form', card.doctype, card.name);
|
|
// setup_edit_card();
|
|
});
|
|
}
|
|
|
|
function refresh_dialog() {
|
|
set_dialog_fields();
|
|
make_assignees();
|
|
}
|
|
|
|
function set_dialog_fields() {
|
|
self.edit_dialog.fields.forEach(function (df) {
|
|
var value = card.doc[df.fieldname];
|
|
if (value) {
|
|
self.edit_dialog.set_value(df.fieldname, value);
|
|
}
|
|
});
|
|
}
|
|
|
|
function make_assignees() {
|
|
var d = self.edit_dialog;
|
|
var html = get_assignees_html() + '<a class="add-assignment avatar avatar-small avatar-empty">\
|
|
<i class="octicon octicon-plus text-muted" style="margin: 3px 0 0 5px;"></i></a>';
|
|
|
|
d.$wrapper.find("[data-fieldname='assignees'] .control-input-wrapper").empty().append(html);
|
|
d.$wrapper.find(".add-assignment").on("click", function () {
|
|
if (self.assign_to_dialog) {
|
|
self.assign_to_dialog.show();
|
|
return;
|
|
}
|
|
show_assign_to_dialog();
|
|
});
|
|
}
|
|
|
|
function get_assignees_html() {
|
|
return card.assigned_list.reduce(function (a, b) {
|
|
return a + frappe.avatar(b);
|
|
}, "");
|
|
}
|
|
|
|
function show_assign_to_dialog() {
|
|
self.dialog = new frappe.ui.form.AssignToDialog({
|
|
obj: self,
|
|
method: 'frappe.desk.form.assign_to.add',
|
|
doctype: card.doctype,
|
|
docname: card.name,
|
|
callback: function() {
|
|
var user = self.assign_to_dialog.get_values().assign_to;
|
|
card.assigned_list.push(user);
|
|
fluxify.doAction('update_card', card);
|
|
refresh_dialog();
|
|
}
|
|
});
|
|
self.assign_to_dialog = self.dialog;
|
|
self.assign_to_dialog.show();
|
|
}
|
|
|
|
init();
|
|
};
|
|
|
|
// Helpers
|
|
function get_board(board_name) {
|
|
return frappe.call({
|
|
type: 'GET',
|
|
method: "frappe.client.get",
|
|
args: {
|
|
doctype: 'Kanban Board',
|
|
name: board_name
|
|
}
|
|
}).then(function(r) {
|
|
var board = r.message;
|
|
if (!board) {
|
|
frappe.msgprint(__('Kanban Board {0} does not exist.',
|
|
['<b>' + self.board_name + '</b>']));
|
|
}
|
|
return prepare_board(board);
|
|
}, function(e) {
|
|
console.log(e); // eslint-disable-line
|
|
});
|
|
}
|
|
|
|
function prepare_board(board) {
|
|
board.filters_array = board.filters ?
|
|
JSON.parse(board.filters) : [];
|
|
return board;
|
|
}
|
|
|
|
function get_card_meta(opts) {
|
|
var meta = frappe.get_meta(opts.doctype);
|
|
var doc = frappe.model.get_new_doc(opts.doctype);
|
|
var title_field = null;
|
|
var quick_entry = false;
|
|
var description_field = null;
|
|
var due_date_field = null;
|
|
|
|
if(meta.title_field) {
|
|
title_field = frappe.meta.get_field(opts.doctype, meta.title_field);
|
|
}
|
|
|
|
meta.fields.forEach(function (df) {
|
|
const is_valid_field =
|
|
in_list(['Data', 'Text', 'Small Text', 'Text Editor'], df.fieldtype)
|
|
&& !df.hidden;
|
|
|
|
if (is_valid_field && !title_field) {
|
|
// can be mapped to textarea
|
|
title_field = df;
|
|
}
|
|
if (df.fieldtype === "Text Editor" && !description_field) {
|
|
description_field = df;
|
|
}
|
|
if (!due_date_field) {
|
|
due_date_field = get_date_field(meta.fields);
|
|
}
|
|
});
|
|
|
|
// quick entry
|
|
var mandatory = meta.fields.filter(function(df) {
|
|
return df.reqd && !doc[df.fieldname];
|
|
});
|
|
if(mandatory.length > 1) {
|
|
quick_entry = true;
|
|
}
|
|
|
|
if(!title_field) {
|
|
title_field = frappe.meta.get_field(opts.doctype, 'name');
|
|
}
|
|
|
|
return {
|
|
quick_entry: quick_entry,
|
|
title_field: title_field,
|
|
description_field: description_field,
|
|
due_date_field: due_date_field,
|
|
};
|
|
}
|
|
|
|
function get_date_field(fields) {
|
|
var filtered = fields.filter(function(df) {
|
|
return df.fieldtype === 'Date' &&
|
|
df.fieldname.indexOf('date') !== -1;
|
|
});
|
|
var field = filtered.find(function(df) {
|
|
return df.fieldname.indexOf('end') !== -1;
|
|
});
|
|
return field || filtered[0];
|
|
}
|
|
|
|
function prepare_card(card, state, doc) {
|
|
var assigned_list = card._assign ?
|
|
JSON.parse(card._assign) : [];
|
|
var comment_count = card._comment_count || 0;
|
|
|
|
if (doc) {
|
|
card = Object.assign({}, card, doc);
|
|
}
|
|
|
|
return {
|
|
doctype: state.doctype,
|
|
name: card.name,
|
|
title: card[state.card_meta.title_field.fieldname],
|
|
column: card[state.board.field_name],
|
|
assigned_list: card.assigned_list || assigned_list,
|
|
comment_count: card.comment_count || comment_count,
|
|
color: card.color || null,
|
|
doc: doc
|
|
};
|
|
}
|
|
|
|
function prepare_columns(columns) {
|
|
return columns.map(function (col) {
|
|
return {
|
|
title: col.column_name,
|
|
status: col.status,
|
|
order: col.order,
|
|
indicator: col.indicator || 'darkgrey'
|
|
};
|
|
});
|
|
}
|
|
|
|
function modify_column_field_in_c11n(doc, board, title, action) {
|
|
doc.fields.forEach(function (df) {
|
|
if (df.fieldname === board.field_name && df.fieldtype === "Select") {
|
|
if(!df.options) df.options = "";
|
|
|
|
if (action === "add") {
|
|
//add column_name to Select field's option field
|
|
if(!df.options.includes(title))
|
|
df.options += "\n" + title;
|
|
} else if (action === "delete") {
|
|
var options = df.options.split("\n");
|
|
var index = options.indexOf(title);
|
|
if (index !== -1) options.splice(index, 1);
|
|
df.options = options.join("\n");
|
|
}
|
|
}
|
|
});
|
|
return doc;
|
|
}
|
|
|
|
function fetch_customization(doctype) {
|
|
return new Promise(function (resolve) {
|
|
frappe.model.with_doc("Customize Form", "Customize Form", function () {
|
|
var doc = frappe.get_doc("Customize Form");
|
|
doc.doc_type = doctype;
|
|
frappe.call({
|
|
doc: doc,
|
|
method: "fetch_to_customize",
|
|
callback: function (r) {
|
|
resolve(r.docs[0]);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
function save_customization(doc) {
|
|
if (!doc) return;
|
|
doc.hide_success = true;
|
|
return frappe.call({
|
|
doc: doc,
|
|
method: "save_customization"
|
|
});
|
|
}
|
|
|
|
function insert_doc(doc) {
|
|
return frappe.call({
|
|
method: "frappe.client.insert",
|
|
args: {
|
|
doc: doc
|
|
},
|
|
callback: function () {
|
|
frappe.model.clear_doc(doc.doctype, doc.name);
|
|
frappe.show_alert({ message: __("Saved"), indicator: 'green' }, 1);
|
|
}
|
|
});
|
|
}
|
|
|
|
function update_kanban_board(board_name, column_title, action) {
|
|
var method;
|
|
var args = {
|
|
board_name: board_name,
|
|
column_title: column_title
|
|
};
|
|
if (action === 'add') {
|
|
method = 'add_column';
|
|
} else if (action === 'archive' || action === 'restore') {
|
|
method = 'archive_restore_column';
|
|
args.status = action === 'archive' ? 'Archived' : 'Active';
|
|
}
|
|
return frappe.call({
|
|
method: method_prefix + method,
|
|
args: args
|
|
});
|
|
}
|
|
|
|
function is_filters_modified(board, cur_list) {
|
|
return new Promise(function(resolve) {
|
|
setTimeout(function() {
|
|
try {
|
|
var list_filters = JSON.stringify(cur_list.filter_area.get());
|
|
resolve(list_filters !== board.filters);
|
|
} catch(e) {
|
|
// sometimes the filter_list is not initiated
|
|
resolve(false);
|
|
}
|
|
|
|
}, 2000);
|
|
});
|
|
}
|
|
|
|
function is_active_column(col) {
|
|
return col.status !== 'Archived';
|
|
}
|
|
|
|
function get_cards_for_column(cards, column) {
|
|
return cards.filter(function (card) {
|
|
return card.column === column.title;
|
|
});
|
|
}
|
|
|
|
function get_card(name) {
|
|
return store.getState().cards.find(function (c) {
|
|
return c.name === name;
|
|
});
|
|
}
|
|
|
|
function update_cards_column(updated_cards) {
|
|
var cards = store.getState().cards;
|
|
cards.forEach(function(c) {
|
|
updated_cards.forEach(function(uc) {
|
|
if(uc.name === c.name) {
|
|
c.column = uc.column;
|
|
}
|
|
});
|
|
});
|
|
return cards;
|
|
}
|
|
|
|
function get_column_indicators(callback) {
|
|
frappe.model.with_doctype('Kanban Board Column', function() {
|
|
var meta = frappe.get_meta('Kanban Board Column');
|
|
var indicators;
|
|
meta.fields.forEach(function(df) {
|
|
if(df.fieldname==='indicator') {
|
|
indicators = df.options.split("\n");
|
|
}
|
|
});
|
|
if(!indicators) {
|
|
//
|
|
indicators = ['green', 'blue', 'orange', 'grey'];
|
|
}
|
|
callback(indicators);
|
|
});
|
|
}
|
|
|
|
function isBound(el, event, fn) {
|
|
var events = $._data(el[0], 'events');
|
|
if(!events) return false;
|
|
var handlers = events[event];
|
|
var flag = false;
|
|
handlers.forEach(function(h) {
|
|
if(h.handler.name === fn.name)
|
|
flag = true;
|
|
});
|
|
return flag;
|
|
}
|
|
|
|
function remove_img_tags(html) {
|
|
const $temp = $(`<div>${html}</div>`);
|
|
$temp.find('img').remove();
|
|
return $temp.html();
|
|
}
|
|
})();
|