mirror of
https://github.com/frappe/books.git
synced 2025-02-08 15:08:29 +00:00
list view updates
This commit is contained in:
parent
34131c86d7
commit
2769423510
36
dist/css/style.css
vendored
36
dist/css/style.css
vendored
@ -7129,6 +7129,42 @@ div.CodeMirror-dragcursors {
|
||||
/* Help users use markselection to safely style text background */
|
||||
span.CodeMirror-selectedtext {
|
||||
background: none; }
|
||||
.indicator, .indicator-right {
|
||||
background: none;
|
||||
vertical-align: middle; }
|
||||
.indicator::before, .indicator-right::after {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
border-radius: 8px;
|
||||
background: #dee2e6; }
|
||||
.indicator::before {
|
||||
margin: 0 0.5rem 0 0; }
|
||||
.indicator-right::after {
|
||||
margin: 0 0 0 0.5rem; }
|
||||
.indicator.grey::before, .indicator-right.grey::after {
|
||||
background: #dee2e6; }
|
||||
.indicator.blue::before, .indicator-right.blue::after {
|
||||
background: #007bff; }
|
||||
.indicator.red::before, .indicator-right.red::after {
|
||||
background: #dc3545; }
|
||||
.indicator.green::before, .indicator-right.green::after {
|
||||
background: #28a745; }
|
||||
.indicator.orange::before, .indicator-right.orange::after {
|
||||
background: #fd7e14; }
|
||||
.indicator.purple::before, .indicator-right.purple::after {
|
||||
background: #6f42c1; }
|
||||
.indicator.darkgrey::before, .indicator-right.darkgrey::after {
|
||||
background: #6c757d; }
|
||||
.indicator.black::before, .indicator-right.black::after {
|
||||
background: #343a40; }
|
||||
.indicator.yellow::before, .indicator-right.yellow::after {
|
||||
background: #ffc107; }
|
||||
.modal-header .indicator {
|
||||
float: left;
|
||||
margin-top: 7.5px;
|
||||
margin-right: 3px; }
|
||||
html {
|
||||
font-size: 12px; }
|
||||
.desk-body {
|
||||
|
225
dist/js/bundle.js
vendored
225
dist/js/bundle.js
vendored
@ -9105,10 +9105,7 @@ var frappejs = {
|
||||
this.models = {};
|
||||
this.forms = {};
|
||||
this.views = {};
|
||||
this.docs = {};
|
||||
this.flags = {
|
||||
cacheDocs: false
|
||||
};
|
||||
this.flags = {};
|
||||
},
|
||||
|
||||
registerLibs(common) {
|
||||
@ -9126,7 +9123,7 @@ var frappejs = {
|
||||
},
|
||||
|
||||
addToCache(doc) {
|
||||
if (!this.flags.cacheDocs) return;
|
||||
if (!this.docs) return;
|
||||
|
||||
// add to `docs` cache
|
||||
if (doc.doctype && doc.name) {
|
||||
@ -9139,11 +9136,21 @@ var frappejs = {
|
||||
if (doc.doctype === doc.name) {
|
||||
this[doc.name] = doc;
|
||||
}
|
||||
|
||||
// propogate change to `docs`
|
||||
doc.on('change', params => {
|
||||
this.docs.trigger('change', params);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
isDirty(doctype, name) {
|
||||
return (this.docs && this.docs[doctype] && this.docs[doctype][name]
|
||||
&& this.docs[doctype][name]._dirty) || false;
|
||||
},
|
||||
|
||||
getDocFromCache(doctype, name) {
|
||||
if (this.docs[doctype] && this.docs[doctype][name]) {
|
||||
if (this.docs && this.docs[doctype] && this.docs[doctype][name]) {
|
||||
return this.docs[doctype][name];
|
||||
}
|
||||
},
|
||||
@ -9293,6 +9300,11 @@ var errors = {
|
||||
};
|
||||
|
||||
var observable = class Observable {
|
||||
constructor() {
|
||||
this._isHot = {};
|
||||
this._eventQueue = {};
|
||||
}
|
||||
|
||||
on(event, listener) {
|
||||
this._addListener('_listeners', event, listener);
|
||||
if (this._socketClient) {
|
||||
@ -9300,6 +9312,16 @@ var observable = class Observable {
|
||||
}
|
||||
}
|
||||
|
||||
// remove listener
|
||||
off(event, listener) {
|
||||
for (let type of ['_listeners', '_onceListeners']) {
|
||||
let index = this[type] && this[type][event] && this[type][event].indexOf(listener);
|
||||
if (index) {
|
||||
this[type][event].splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
once(event, listener) {
|
||||
this._addListener('_onceListeners', event, listener);
|
||||
}
|
||||
@ -9314,7 +9336,12 @@ var observable = class Observable {
|
||||
this._socketServer = socket;
|
||||
}
|
||||
|
||||
async trigger(event, params) {
|
||||
async trigger(event, params, throttle=false) {
|
||||
if (this._throttled(event, params, throttle)) return;
|
||||
|
||||
// listify if throttled
|
||||
if (throttle) params = [params];
|
||||
|
||||
await this._triggerEvent('_listeners', event, params);
|
||||
await this._triggerEvent('_onceListeners', event, params);
|
||||
|
||||
@ -9330,6 +9357,39 @@ var observable = class Observable {
|
||||
|
||||
}
|
||||
|
||||
_throttled(event, params, throttle) {
|
||||
if (throttle) {
|
||||
if (this._isHot[event]) {
|
||||
|
||||
// hot, add to queue
|
||||
if (this._eventQueue[event]) {
|
||||
|
||||
// queue exists, just add
|
||||
this._eventQueue[event].push(params);
|
||||
} else {
|
||||
|
||||
// create a new queue to be called after cool-off
|
||||
this._eventQueue[event] = [params];
|
||||
|
||||
// call after cool-off
|
||||
setTimeout(() => {
|
||||
let _queuedParams = this._eventQueue[event];
|
||||
|
||||
// reset queues
|
||||
this._isHot[event] = false;
|
||||
this._eventQueue[event] = null;
|
||||
|
||||
this.trigger(event, _queuedParams, true);
|
||||
}, throttle);
|
||||
|
||||
}
|
||||
return true;
|
||||
}
|
||||
this._isHot[event] = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_addListener(name, event, listener) {
|
||||
if (!this[name]) {
|
||||
this[name] = {};
|
||||
@ -9442,9 +9502,12 @@ var document$1 = class BaseDocument extends observable {
|
||||
|
||||
// set value and trigger change
|
||||
async set(fieldname, value) {
|
||||
if (this[fieldname] !== value) {
|
||||
this._dirty = true;
|
||||
this[fieldname] = await this.validateField(fieldname, value);
|
||||
await this.applyChange(fieldname);
|
||||
}
|
||||
}
|
||||
|
||||
async applyChange(fieldname) {
|
||||
if (await this.applyFormula()) {
|
||||
@ -9560,6 +9623,8 @@ var document$1 = class BaseDocument extends observable {
|
||||
syncValues(data) {
|
||||
this.clearValues();
|
||||
Object.assign(this, data);
|
||||
this._dirty = false;
|
||||
this.trigger('change', {doc: this});
|
||||
}
|
||||
|
||||
clearValues() {
|
||||
@ -9671,12 +9736,13 @@ var document$1 = class BaseDocument extends observable {
|
||||
var meta = class BaseMeta extends document$1 {
|
||||
constructor(data) {
|
||||
super(data);
|
||||
this.list_options = {
|
||||
fields: ['name', 'modified']
|
||||
};
|
||||
this.setDefaultIndicators();
|
||||
if (this.setupMeta) {
|
||||
this.setupMeta();
|
||||
}
|
||||
if (!this.titleField) {
|
||||
this.titleField = 'name';
|
||||
}
|
||||
}
|
||||
|
||||
hasField(fieldname) {
|
||||
@ -9824,6 +9890,32 @@ var meta = class BaseMeta extends document$1 {
|
||||
|
||||
await super.trigger(event, params);
|
||||
}
|
||||
|
||||
setDefaultIndicators() {
|
||||
if (!this.indicators) {
|
||||
this.indicators = {
|
||||
key: 'docstatus',
|
||||
colors: {
|
||||
0: 'gray',
|
||||
1: 'blue',
|
||||
2: 'red'
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
getIndicatorColor(doc) {
|
||||
if (frappejs.isDirty(this.name, doc.name)) {
|
||||
return 'orange';
|
||||
} else {
|
||||
let value = doc[this.indicators.key];
|
||||
if (value) {
|
||||
return this.indicators.colors[value] || 'gray';
|
||||
} else {
|
||||
return 'gray';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var common = {
|
||||
@ -26926,7 +27018,7 @@ var router = class Router extends observable {
|
||||
};
|
||||
|
||||
var page = class Page extends observable {
|
||||
constructor({title, parent, hasRoute=true}) {
|
||||
constructor({title, parent, hasRoute=true} = {}) {
|
||||
super();
|
||||
Object.assign(this, arguments[0]);
|
||||
if (!this.parent) {
|
||||
@ -26943,7 +27035,7 @@ var page = class Page extends observable {
|
||||
make() {
|
||||
this.wrapper = frappejs.ui.add('div', 'page hide', this.parent);
|
||||
this.wrapper.innerHTML = `<div class="page-head clearfix hide">
|
||||
<span class="page-title"></span>
|
||||
<span class="page-title font-weight-bold"></span>
|
||||
</div>
|
||||
<div class="page-body"></div>`;
|
||||
this.head = this.wrapper.querySelector('.page-head');
|
||||
@ -27071,12 +27163,8 @@ var list = class BaseList {
|
||||
this.data = [];
|
||||
|
||||
frappejs.db.on(`change:${this.doctype}`, (params) => {
|
||||
this.dirty = true;
|
||||
this.refresh();
|
||||
});
|
||||
|
||||
setInterval(() => {
|
||||
if (this.dirty) this.refresh();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
makeBody() {
|
||||
@ -27101,7 +27189,8 @@ var list = class BaseList {
|
||||
let data = await this.getData();
|
||||
|
||||
for (let i=0; i< Math.min(this.pageLength, data.length); i++) {
|
||||
this.renderRow(this.start + i, data[i]);
|
||||
let row = this.getRow(this.start + i);
|
||||
this.renderRow(row, data[i]);
|
||||
}
|
||||
|
||||
if (this.start > 0) {
|
||||
@ -27143,15 +27232,10 @@ var list = class BaseList {
|
||||
return filters;
|
||||
}
|
||||
|
||||
renderRow(i, data) {
|
||||
let row = this.getRow(i);
|
||||
renderRow(row, data) {
|
||||
row.innerHTML = this.getRowBodyHTML(data);
|
||||
row.docName = data.name;
|
||||
row.setAttribute('data-name', data.name);
|
||||
row.style.display = 'flex';
|
||||
|
||||
// make element focusable
|
||||
row.setAttribute('tabindex', -1);
|
||||
}
|
||||
|
||||
getRowBodyHTML(data) {
|
||||
@ -27161,24 +27245,42 @@ var list = class BaseList {
|
||||
}
|
||||
|
||||
getRowHTML(data) {
|
||||
return `<div class="col-11">${data.name}</div>`;
|
||||
return `<div class="col-11">
|
||||
${this.getNameHTML(data)}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
getNameHTML(data) {
|
||||
return `<span class="indicator ${this.meta.getIndicatorColor(data)}">${data[this.meta.titleField]}</span>`;
|
||||
}
|
||||
|
||||
getRow(i) {
|
||||
if (!this.rows[i]) {
|
||||
this.rows[i] = frappejs.ui.add('div', 'list-row row no-gutters', this.body);
|
||||
let row = frappejs.ui.add('div', 'list-row row no-gutters', this.body);
|
||||
|
||||
// open on click
|
||||
let me = this;
|
||||
this.rows[i].addEventListener('click', async function(e) {
|
||||
row.addEventListener('click', async function(e) {
|
||||
if (!e.target.tagName !== 'input') {
|
||||
await me.showItem(this.docName);
|
||||
}
|
||||
});
|
||||
row.style.display = 'flex';
|
||||
|
||||
// make element focusable
|
||||
row.setAttribute('tabindex', -1);
|
||||
this.rows[i] = row;
|
||||
}
|
||||
return this.rows[i];
|
||||
}
|
||||
|
||||
refreshRow(doc) {
|
||||
let row = this.getRowByName(doc.name);
|
||||
if (row) {
|
||||
this.renderRow(row, doc);
|
||||
}
|
||||
}
|
||||
|
||||
async showItem(name) {
|
||||
if (this.meta.print) {
|
||||
await frappejs.router.setRoute('print', this.doctype, name);
|
||||
@ -27311,14 +27413,18 @@ var list = class BaseList {
|
||||
}
|
||||
|
||||
if (name) {
|
||||
let myListRow = this.body.querySelector(`.list-row[data-name="${name}"]`);
|
||||
if (myListRow) {
|
||||
myListRow.classList.add('active');
|
||||
myListRow.focus();
|
||||
let row = this.getRowByName(name);
|
||||
if (row) {
|
||||
row.classList.add('active');
|
||||
row.focus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getRowByName(name) {
|
||||
return this.body.querySelector(`.list-row[data-name="${name}"]`);
|
||||
}
|
||||
|
||||
getActiveListRow() {
|
||||
return this.body.querySelector('.list-row.active');
|
||||
}
|
||||
@ -27411,7 +27517,7 @@ class BaseControl {
|
||||
}
|
||||
|
||||
setDisabled() {
|
||||
if (this.readonly || this.disabled) {
|
||||
if (this.disabled) {
|
||||
this.input.disabled = true;
|
||||
}
|
||||
}
|
||||
@ -48470,19 +48576,19 @@ var form = class BaseForm extends observable {
|
||||
setTitle() {
|
||||
const doctypeLabel = this.doc.meta.label || this.doc.meta.name;
|
||||
|
||||
if (this.doc.meta.isSingle || !this.doc.meta.showTitle) {
|
||||
if (this.doc.meta.isSingle || this.doc.meta.naming == 'random') {
|
||||
this.container.setTitle(doctypeLabel);
|
||||
} else if (this.doc._notInserted) {
|
||||
this.container.setTitle(frappejs._('New {0}', doctypeLabel));
|
||||
} else {
|
||||
this.container.setTitle(`${doctypeLabel} ${this.doc.name}`);
|
||||
this.container.setTitle(this.doc.name);
|
||||
}
|
||||
}
|
||||
|
||||
async bindEvents(doc) {
|
||||
if (this.doc) {
|
||||
// clear listeners of outgoing doc
|
||||
this.doc.clearListeners();
|
||||
if (this.doc && this.docListener) {
|
||||
// stop listening to the old doc
|
||||
this.doc.off(this.docListener);
|
||||
}
|
||||
this.clearAlert();
|
||||
this.doc = doc;
|
||||
@ -48496,7 +48602,7 @@ var form = class BaseForm extends observable {
|
||||
|
||||
setupChangeListener() {
|
||||
// refresh value in control
|
||||
this.doc.on('change', (params) => {
|
||||
this.docListener = (params) => {
|
||||
if (params.fieldname) {
|
||||
// only single value changed
|
||||
let control = this.controls[params.fieldname];
|
||||
@ -48508,7 +48614,9 @@ var form = class BaseForm extends observable {
|
||||
this.refresh();
|
||||
}
|
||||
this.form.classList.remove('was-validated');
|
||||
});
|
||||
};
|
||||
|
||||
this.doc.on('change', this.docListener);
|
||||
}
|
||||
|
||||
checkValidity() {
|
||||
@ -48538,7 +48646,6 @@ var form = class BaseForm extends observable {
|
||||
} else {
|
||||
await this.doc.update();
|
||||
}
|
||||
await this.refresh();
|
||||
this.showAlert('Saved', 'success');
|
||||
} catch (e) {
|
||||
this.showAlert('Failed', 'danger');
|
||||
@ -48593,7 +48700,7 @@ var view = {
|
||||
var formpage = class FormPage extends page {
|
||||
constructor(doctype) {
|
||||
let meta = frappejs.getMeta(doctype);
|
||||
super({title: `Edit ${meta.name}`});
|
||||
super({title: `Edit ${meta.name}`, hasRoute: true});
|
||||
this.meta = meta;
|
||||
this.doctype = doctype;
|
||||
|
||||
@ -48653,7 +48760,13 @@ var listpage = class ListPage extends page {
|
||||
});
|
||||
|
||||
this.on('show', async () => {
|
||||
await this.list.run();
|
||||
await this.list.refresh();
|
||||
});
|
||||
|
||||
frappejs.docs.on('change', (params) => {
|
||||
if (params.doc.doctype === doctype) {
|
||||
this.list.refreshRow(params.doc);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -56393,7 +56506,7 @@ nunjucks.configure({ autoescape: false });
|
||||
var printpage = class PrintPage extends page {
|
||||
constructor(doctype) {
|
||||
let meta = frappejs.getMeta(doctype);
|
||||
super({title: `View ${meta.name}`});
|
||||
super({title: `${meta.name}`, hasRoute: true});
|
||||
this.meta = meta;
|
||||
this.doctype = doctype;
|
||||
|
||||
@ -56421,6 +56534,7 @@ var printpage = class PrintPage extends page {
|
||||
|
||||
try {
|
||||
this.body.innerHTML = `<div class="print-page">${nunjucks.renderString(this.printFormat.template, context)}</div>`;
|
||||
this.setTitle(doc.name);
|
||||
} catch (e) {
|
||||
this.renderError('Template Error', e);
|
||||
throw e;
|
||||
@ -56683,7 +56797,7 @@ var NumberSeries = {
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "name",
|
||||
"label": "Name",
|
||||
"label": "Prefix",
|
||||
"fieldtype": "Data",
|
||||
"required": 1
|
||||
},
|
||||
@ -56820,7 +56934,7 @@ var SystemSettings = {
|
||||
};
|
||||
|
||||
var ToDo = {
|
||||
"autoname": "random",
|
||||
"naming": "random",
|
||||
"name": "ToDo",
|
||||
"doctype": "DocType",
|
||||
"isSingle": 0,
|
||||
@ -56828,6 +56942,14 @@ var ToDo = {
|
||||
"subject",
|
||||
"description"
|
||||
],
|
||||
titleField: 'subject',
|
||||
indicators: {
|
||||
key: 'status',
|
||||
colors: {
|
||||
Open: 'gray',
|
||||
Closed: 'green'
|
||||
}
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "subject",
|
||||
@ -56916,6 +57038,7 @@ var models = {
|
||||
frappejs.ui = ui;
|
||||
|
||||
|
||||
|
||||
var client = {
|
||||
async start({server, columns = 2}) {
|
||||
window.frappe = frappejs;
|
||||
@ -56929,7 +57052,7 @@ var client = {
|
||||
this.socket = io.connect('http://localhost:8000'); // eslint-disable-line
|
||||
frappejs.db.bindSocketClient(this.socket);
|
||||
|
||||
frappejs.flags.cacheDocs = true;
|
||||
frappejs.docs = new observable();
|
||||
|
||||
await frappejs.getSingle('SystemSettings');
|
||||
|
||||
@ -57357,10 +57480,6 @@ var ToDoList_1 = class ToDoList extends list {
|
||||
getFields() {
|
||||
return ['name', 'subject', 'status'];
|
||||
}
|
||||
getRowHTML(data) {
|
||||
let symbol = data.status=="Closed" ? "✔" : "";
|
||||
return `<a href="#edit/ToDo/${data.name}">${symbol} ${data.subject}</a>`;
|
||||
}
|
||||
};
|
||||
|
||||
var AccountList_1 = class AccountList extends list {
|
||||
@ -57368,7 +57487,7 @@ var AccountList_1 = class AccountList extends list {
|
||||
return ['name', 'account_type'];
|
||||
}
|
||||
getRowHTML(data) {
|
||||
return `${data.name} (${data.account_type})`;
|
||||
return `<div class="col-11">${this.getNameHTML(data)} (${data.account_type})</div>`;
|
||||
}
|
||||
};
|
||||
|
||||
@ -57391,8 +57510,8 @@ var InvoiceList_1 = class InvoiceList extends list {
|
||||
return ['name', 'customer', 'grandTotal'];
|
||||
}
|
||||
getRowHTML(data) {
|
||||
return `<div class="col-2">${data.name}</div>
|
||||
<div class="col-5 text-muted">${data.customer}</div>
|
||||
return `<div class="col-3">${this.getNameHTML(data)}</div>
|
||||
<div class="col-4 text-muted">${data.customer}</div>
|
||||
<div class="col-4 text-muted text-right">${frappejs.format(data.grandTotal, "Currency")}</div>`;
|
||||
}
|
||||
};
|
||||
|
@ -12,7 +12,7 @@
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th style='width: 30px'></th>
|
||||
<th>{{ frappe._("Item") }}</th>
|
||||
<th class='text-right'>{{ frappe._("Qty") }}</th>
|
||||
<th class='text-right'>{{ frappe._("Rate") }}</th>
|
||||
|
@ -5,6 +5,6 @@ module.exports = class AccountList extends BaseList {
|
||||
return ['name', 'account_type'];
|
||||
}
|
||||
getRowHTML(data) {
|
||||
return `${data.name} (${data.account_type})`;
|
||||
return `<div class="col-11">${this.getNameHTML(data)} (${data.account_type})</div>`;
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ module.exports = class InvoiceList extends BaseList {
|
||||
return ['name', 'customer', 'grandTotal'];
|
||||
}
|
||||
getRowHTML(data) {
|
||||
return `<div class="col-2">${data.name}</div>
|
||||
<div class="col-5 text-muted">${data.customer}</div>
|
||||
return `<div class="col-3">${this.getNameHTML(data)}</div>
|
||||
<div class="col-4 text-muted">${data.customer}</div>
|
||||
<div class="col-4 text-muted text-right">${frappe.format(data.grandTotal, "Currency")}</div>`;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user