mirror of
https://github.com/frappe/books.git
synced 2025-03-19 17:42:23 +00:00
Merge branch 'pratu16x7-dev' into electronify
This commit is contained in:
commit
62718a55db
@ -444,7 +444,7 @@ module.exports = class Database extends Observable {
|
|||||||
, 'Text': 'text'
|
, 'Text': 'text'
|
||||||
, 'Data': 'text'
|
, 'Data': 'text'
|
||||||
, 'Link': 'text'
|
, 'Link': 'text'
|
||||||
, 'Dynamic Link': 'text'
|
, 'DynamicLink': 'text'
|
||||||
, 'Password': 'text'
|
, 'Password': 'text'
|
||||||
, 'Select': 'text'
|
, 'Select': 'text'
|
||||||
, 'Read Only': 'text'
|
, 'Read Only': 'text'
|
||||||
|
@ -131,7 +131,7 @@ module.exports = class HTTPClient extends Observable {
|
|||||||
, 'Text': true
|
, 'Text': true
|
||||||
, 'Data': true
|
, 'Data': true
|
||||||
, 'Link': true
|
, 'Link': true
|
||||||
, 'Dynamic Link': true
|
, 'DynamicLink': true
|
||||||
, 'Password': true
|
, 'Password': true
|
||||||
, 'Select': true
|
, 'Select': true
|
||||||
, 'Read Only': true
|
, 'Read Only': true
|
||||||
|
@ -193,7 +193,7 @@ module.exports = class mysqlDatabase extends Database{
|
|||||||
, 'Text': 'text'
|
, 'Text': 'text'
|
||||||
, 'Data': 'VARCHAR(140)'
|
, 'Data': 'VARCHAR(140)'
|
||||||
, 'Link': ' varchar(140)'
|
, 'Link': ' varchar(140)'
|
||||||
, 'Dynamic Link': 'text'
|
, 'DynamicLink': 'text'
|
||||||
, 'Password': 'varchar(140)'
|
, 'Password': 'varchar(140)'
|
||||||
, 'Select': 'VARCHAR(140)'
|
, 'Select': 'VARCHAR(140)'
|
||||||
, 'Read Only': 'varchar(140)'
|
, 'Read Only': 'varchar(140)'
|
||||||
|
@ -74,16 +74,14 @@ module.exports = class sqliteDatabase extends Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getColumnDefinition(field) {
|
getColumnDefinition(field) {
|
||||||
let def = `${field.fieldname} ${this.typeMap[field.fieldtype]}`;
|
let def = [
|
||||||
if (field.fieldname==='name') {
|
field.fieldname,
|
||||||
def += ' PRIMARY KEY NOT NULL';
|
this.typeMap[field.fieldtype],
|
||||||
}
|
field.fieldname === 'name' ? 'PRIMARY KEY NOT NULL' : '',
|
||||||
else if (field.required) {
|
field.required ? 'NOT NULL' : '',
|
||||||
def += ' NOT NULL';
|
field.default ? `DEFAULT ${field.default}` : ''
|
||||||
}
|
].join(' ');
|
||||||
if (field.default) {
|
|
||||||
def += `DEFAULT ${field.default}`;
|
|
||||||
}
|
|
||||||
return def;
|
return def;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,7 +230,7 @@ module.exports = class sqliteDatabase extends Database {
|
|||||||
, 'Text': 'text'
|
, 'Text': 'text'
|
||||||
, 'Data': 'text'
|
, 'Data': 'text'
|
||||||
, 'Link': 'text'
|
, 'Link': 'text'
|
||||||
, 'Dynamic Link': 'text'
|
, 'DynamicLink': 'text'
|
||||||
, 'Password': 'text'
|
, 'Password': 'text'
|
||||||
, 'Select': 'text'
|
, 'Select': 'text'
|
||||||
, 'Read Only': 'text'
|
, 'Read Only': 'text'
|
||||||
|
@ -18,6 +18,7 @@ module.exports = class Desk {
|
|||||||
|
|
||||||
let body = document.querySelector('body');
|
let body = document.querySelector('body');
|
||||||
//this.navbar = new Navbar();
|
//this.navbar = new Navbar();
|
||||||
|
frappe.ui.empty(body);
|
||||||
this.container = frappe.ui.add('div', '', body);
|
this.container = frappe.ui.add('div', '', body);
|
||||||
this.containerRow = frappe.ui.add('div', 'row no-gutters', this.container)
|
this.containerRow = frappe.ui.add('div', 'row no-gutters', this.container)
|
||||||
this.makeColumns(columns);
|
this.makeColumns(columns);
|
||||||
@ -100,7 +101,6 @@ module.exports = class Desk {
|
|||||||
this.menu.setActive();
|
this.menu.setActive();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleCenter(show) {
|
toggleCenter(show) {
|
||||||
@ -173,5 +173,4 @@ module.exports = class Desk {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,51 +1,50 @@
|
|||||||
const Page = require('frappejs/client/view/page');
|
const Page = require('frappejs/client/view/page');
|
||||||
const controls = require('frappejs/client/view/controls');
|
const Form = require('frappejs/client/view/form');
|
||||||
const DataTable = require('frappe-datatable');
|
const DataTable = require('frappe-datatable');
|
||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
const utils = require('frappejs/client/ui/utils');
|
const utils = require('frappejs/client/ui/utils');
|
||||||
|
const Observable = require('frappejs/utils/observable');
|
||||||
|
|
||||||
// baseclass for report
|
// baseclass for report
|
||||||
// `url` url for report
|
// `url` url for report
|
||||||
// `getColumns` return columns
|
// `getColumns` return columns
|
||||||
|
|
||||||
module.exports = class ReportPage extends Page {
|
module.exports = class ReportPage extends Page {
|
||||||
constructor({title, }) {
|
constructor({title, filterFields}) {
|
||||||
super({title: title, hasRoute: true});
|
super({title: title, hasRoute: true});
|
||||||
|
|
||||||
this.fullPage = true;
|
this.fullPage = true;
|
||||||
|
this.filterFields = filterFields;
|
||||||
|
|
||||||
this.filterWrapper = frappe.ui.add('div', 'filter-toolbar form-inline', this.body);
|
this.filterWrapper = frappe.ui.add('div', 'filter-toolbar', this.body);
|
||||||
this.tableWrapper = frappe.ui.add('div', 'table-page-wrapper', this.body);
|
this.tableWrapper = frappe.ui.add('div', 'table-page-wrapper', this.body);
|
||||||
|
|
||||||
this.btnNew = this.addButton(frappe._('Refresh'), 'btn-primary', async () => {
|
this.btnNew = this.addButton(frappe._('Refresh'), 'btn-primary', async () => {
|
||||||
await this.run();
|
await this.run();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filters = {};
|
this.makeFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
getColumns() {
|
getColumns() {
|
||||||
// overrride
|
// overrride
|
||||||
}
|
}
|
||||||
|
|
||||||
addFilter(field) {
|
makeFilters() {
|
||||||
if (field.fieldname) {
|
this.form = new Form({
|
||||||
field.fieldname = frappe.slug(field.label);
|
parent: this.filterWrapper,
|
||||||
}
|
meta: { fields: this.filterFields },
|
||||||
|
doc: new Observable(),
|
||||||
field.placeholder = field.label;
|
inline: true,
|
||||||
field.inline = true;
|
container: this
|
||||||
|
});
|
||||||
this.filters[field.fieldname] = controls.makeControl({field: field, form: this, parent: this.filterWrapper});
|
|
||||||
return this.filters[field.fieldname];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilterValues() {
|
getFilterValues() {
|
||||||
const values = {};
|
const values = {};
|
||||||
for (let fieldname in this.filters) {
|
for (let control of this.form.controlList) {
|
||||||
let control = this.filters[fieldname];
|
values[control.fieldname] = control.getInputValue();
|
||||||
values[fieldname] = control.getInputValue();
|
if (control.required && !values[control.fieldname]) {
|
||||||
if (control.required && !values[fieldname]) {
|
|
||||||
frappe.ui.showAlert({message: frappe._('{0} is mandatory', control.label), color: 'red'});
|
frappe.ui.showAlert({message: frappe._('{0} is mandatory', control.label), color: 'red'});
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -59,6 +58,16 @@ module.exports = class ReportPage extends Page {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run() {
|
||||||
|
if (frappe.params && frappe.params.filters) {
|
||||||
|
for (let key in frappe.params.filters) {
|
||||||
|
if (this.form.controls[key]) {
|
||||||
|
this.form.controls[key].setInputValue(frappe.params.filters[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
frappe.params = null;
|
||||||
|
|
||||||
|
|
||||||
if (!this.datatable) {
|
if (!this.datatable) {
|
||||||
this.makeDataTable();
|
this.makeDataTable();
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ module.exports = class TablePage extends Page {
|
|||||||
this.filterSelector.reset(this.doctype);
|
this.filterSelector.reset(this.doctype);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frappe.params.filters) {
|
if (frappe.params && frappe.params.filters) {
|
||||||
this.filterSelector.setFilters(frappe.params.filters);
|
this.filterSelector.setFilters(frappe.params.filters);
|
||||||
}
|
}
|
||||||
frappe.params = null;
|
frappe.params = null;
|
||||||
|
@ -6,7 +6,7 @@ const Desk = require('./desk');
|
|||||||
const Observable = require('frappejs/utils/observable');
|
const Observable = require('frappejs/utils/observable');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
async start({server, columns = 2}) {
|
async start({server, columns = 2, makeDesk = 1}) {
|
||||||
window.frappe = frappe;
|
window.frappe = frappe;
|
||||||
frappe.init();
|
frappe.init();
|
||||||
frappe.registerLibs(common);
|
frappe.registerLibs(common);
|
||||||
@ -24,6 +24,12 @@ module.exports = {
|
|||||||
|
|
||||||
await frappe.getSingle('SystemSettings');
|
await frappe.getSingle('SystemSettings');
|
||||||
|
|
||||||
|
if(makeDesk) {
|
||||||
|
this.makeDesk(columns);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async makeDesk(columns) {
|
||||||
frappe.desk = new Desk(columns);
|
frappe.desk = new Desk(columns);
|
||||||
await frappe.login();
|
await frappe.login();
|
||||||
},
|
},
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
@import "node_modules/flatpickr/dist/themes/airbnb";
|
@import "node_modules/flatpickr/dist/themes/airbnb";
|
||||||
@import "node_modules/codemirror/lib/codemirror";
|
@import "node_modules/codemirror/lib/codemirror";
|
||||||
@import "node_modules/frappe-datatable/dist/frappe-datatable";
|
@import "node_modules/frappe-datatable/dist/frappe-datatable";
|
||||||
|
// @import "node_modules/octicons/build/build.css";
|
||||||
|
|
||||||
$spacer-1: 0.25rem;
|
$spacer-1: 0.25rem;
|
||||||
$spacer-2: 0.5rem;
|
$spacer-2: 0.5rem;
|
||||||
@ -86,7 +87,8 @@ html {
|
|||||||
|
|
||||||
.form-inline {
|
.form-inline {
|
||||||
.form-group {
|
.form-group {
|
||||||
margin-right: $spacer-2;
|
margin-right: $spacer-3;
|
||||||
|
margin-bottom: $spacer-3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,3 +253,157 @@ mark {
|
|||||||
background: inherit;
|
background: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.align-right {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.align-center {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-sm {
|
||||||
|
margin: $spacer-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vertical-margin {
|
||||||
|
margin: $spacer-3 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree li {
|
||||||
|
list-style: none;
|
||||||
|
margin: 2px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul.tree-children {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link {
|
||||||
|
cursor: pointer;
|
||||||
|
display: inline-block;
|
||||||
|
padding: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link .node-parent {
|
||||||
|
color: $gray-600;
|
||||||
|
font-size: 14px;
|
||||||
|
width: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link .node-leaf {
|
||||||
|
color: $gray-400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link .node-parent, .tree-link .node-leaf {
|
||||||
|
margin-right: 5px;
|
||||||
|
margin-left: 2px;
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link.active {
|
||||||
|
svg {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $gray-600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-hover {
|
||||||
|
background-color: $gray-200;
|
||||||
|
min-height: 20px;
|
||||||
|
border: 1px solid $gray-600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-node-toolbar {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0px 5px;
|
||||||
|
margin-left: 15px;
|
||||||
|
margin-bottom: -4px;
|
||||||
|
margin-top: -8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// @media (max-width: @screen-xs) {
|
||||||
|
// ul.tree-children {
|
||||||
|
// padding-left: 10px;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// decoration
|
||||||
|
// .tree, .tree-node {
|
||||||
|
.tree.with-skeleton, .tree.with-skeleton .tree-node {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&.opened::before, &:last-child::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 12px;
|
||||||
|
left: 7px;
|
||||||
|
height: calc(100% - 23px);
|
||||||
|
width: 1px;
|
||||||
|
background: $gray-400;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child::after {
|
||||||
|
top: 11px;
|
||||||
|
left: -13px;
|
||||||
|
height: calc(100% - 15px);
|
||||||
|
width: 3px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.opened > .tree-children > .tree-node > .tree-link::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
width: 18px;
|
||||||
|
height: 1px;
|
||||||
|
top: 10px;
|
||||||
|
left: -12px;
|
||||||
|
z-index: -1;
|
||||||
|
background: $gray-400;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree.with-skeleton.opened::before {
|
||||||
|
left: 22px;
|
||||||
|
top: 33px;
|
||||||
|
height: calc(100% - 67px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tree-link.active ~ .balance-area {
|
||||||
|
color: $gray-600 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
// just for accounting
|
||||||
|
|
||||||
|
.setup-container {
|
||||||
|
margin: 40px auto;
|
||||||
|
padding: 20px 0px;
|
||||||
|
width: 450px;
|
||||||
|
|
||||||
|
border: 1px solid $gray-300;
|
||||||
|
border-radius: 4px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-body {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-body.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setup-link-area {
|
||||||
|
margin: $spacer-1 $spacer-4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,6 +2,48 @@ const frappe = require('frappejs');
|
|||||||
const Dropdown = require('./dropdown');
|
const Dropdown = require('./dropdown');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
create(tag, obj) {
|
||||||
|
if(!obj) {
|
||||||
|
let div = document.createElement('div');
|
||||||
|
div.innerHTML = tag.trim();
|
||||||
|
return div.firstChild;
|
||||||
|
}
|
||||||
|
let element = document.createElement(tag);
|
||||||
|
|
||||||
|
let $ = (expr, con) => {
|
||||||
|
return typeof expr === "string"
|
||||||
|
? (con || document).querySelector(expr)
|
||||||
|
: expr || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i in obj) {
|
||||||
|
let val = obj[i];
|
||||||
|
|
||||||
|
if (i === "inside") {
|
||||||
|
$(val).appendChild(element);
|
||||||
|
}
|
||||||
|
else if (i === "around") {
|
||||||
|
let ref = $(val);
|
||||||
|
ref.parentNode.insertBefore(element, ref);
|
||||||
|
element.appendChild(ref);
|
||||||
|
|
||||||
|
} else if (i === "styles") {
|
||||||
|
if(typeof val === "object") {
|
||||||
|
Object.keys(val).map(prop => {
|
||||||
|
element.style[prop] = val[prop];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (i in element ) {
|
||||||
|
element[i] = val;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
element.setAttribute(i, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return element;
|
||||||
|
},
|
||||||
|
|
||||||
add(tag, className, parent, textContent) {
|
add(tag, className, parent, textContent) {
|
||||||
let element = document.createElement(tag);
|
let element = document.createElement(tag);
|
||||||
if (className) {
|
if (className) {
|
||||||
@ -36,7 +78,7 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
remove_class(element, className) {
|
removeClass(element, className) {
|
||||||
if (element.classList) {
|
if (element.classList) {
|
||||||
element.classList.remove(className);
|
element.classList.remove(className);
|
||||||
} else {
|
} else {
|
||||||
|
171
client/ui/tree.js
Normal file
171
client/ui/tree.js
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
const frappe = require('frappejs');
|
||||||
|
const octicons = require('octicons');
|
||||||
|
const utils = require('frappejs/client/ui/utils');
|
||||||
|
|
||||||
|
class Tree {
|
||||||
|
constructor({parent, label, iconSet, withSkeleton, method}) {
|
||||||
|
Object.assign(this, arguments[0]);
|
||||||
|
this.nodes = {};
|
||||||
|
if(!iconSet) {
|
||||||
|
this.iconSet = {
|
||||||
|
open: octicons["triangle-down"].toSVG({ "width": 10, "class": "node-parent"}),
|
||||||
|
closed: octicons["triangle-right"].toSVG({ "width": 5, "class": "node-parent"}),
|
||||||
|
leaf: octicons["primitive-dot"].toSVG({ "width": 7, "class": "node-leaf"})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
this.make();
|
||||||
|
}
|
||||||
|
|
||||||
|
make() {
|
||||||
|
this.tree = frappe.ui.create('div', {
|
||||||
|
inside: this.parent,
|
||||||
|
className: 'tree ' + (this.withSkeleton ? 'with-skeleton' : '')
|
||||||
|
});
|
||||||
|
|
||||||
|
this.rootNode = this.makeNode(this.label, this.label, true, null, this.tree);
|
||||||
|
this.expandNode(this.rootNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
// this.selectedNode.parentNode &&
|
||||||
|
// this.loadChildren(this.selectedNode.parentNode, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
loadChildren(node, deep=false) {
|
||||||
|
if(!deep) {
|
||||||
|
this.renderNodeChildren(node, this.method(node.value));
|
||||||
|
} else {
|
||||||
|
this.renderChildrenDeep(node, this.getAllNodes(node.value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderChildrenDeep(dataList) {
|
||||||
|
dataList.map(d => { this.renderNodeChildren(this.nodes[d.parent], d.data); });
|
||||||
|
}
|
||||||
|
|
||||||
|
renderNodeChildren(node, dataSet=[]) {
|
||||||
|
frappe.ui.empty(node.childrenList);
|
||||||
|
|
||||||
|
dataSet.forEach(data => {
|
||||||
|
let parentNode = this.nodes[node.value];
|
||||||
|
let childNode = this.makeNode(data.label || data.value, data.value,
|
||||||
|
data.expandable, parentNode);
|
||||||
|
childNode.treeLink.dataset.nodeData = data;
|
||||||
|
});
|
||||||
|
node.expanded = false;
|
||||||
|
|
||||||
|
// As children loaded
|
||||||
|
node.loaded = true;
|
||||||
|
this.onNodeClick(node, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllNodes() { }
|
||||||
|
|
||||||
|
makeNode(label, value, expandable, parentNode, parentEl) {
|
||||||
|
let node = {
|
||||||
|
parent: parent,
|
||||||
|
label: label,
|
||||||
|
value: value,
|
||||||
|
loaded: 0,
|
||||||
|
expanded: 0,
|
||||||
|
expandable: expandable,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(parentNode){
|
||||||
|
node.parentNode = parentNode;
|
||||||
|
node.parent = parentNode.childrenList;
|
||||||
|
node.isRoot = 0;
|
||||||
|
} else {
|
||||||
|
node.isRoot = 1;
|
||||||
|
node.parent = parentEl;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nodes[value] = node;
|
||||||
|
this.buildNodeElement(node);
|
||||||
|
this.onRender && this.onRender(node);
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
buildNodeElement(node) {
|
||||||
|
node.parentLi = frappe.ui.create('li', {
|
||||||
|
inside: node.parent,
|
||||||
|
className: 'tree-node'
|
||||||
|
});
|
||||||
|
|
||||||
|
let iconHtml = '';
|
||||||
|
if(this.iconSet) {
|
||||||
|
iconHtml = node.expandable ? this.iconSet.closed : this.iconSet.leaf;
|
||||||
|
}
|
||||||
|
let labelEl = `<a class="tree-label"> ${node.label}</a>`;
|
||||||
|
|
||||||
|
node.treeLink = frappe.ui.create('span', {
|
||||||
|
inside: node.parentLi,
|
||||||
|
className: 'tree-link',
|
||||||
|
'data-label': node.label,
|
||||||
|
innerHTML: iconHtml + labelEl
|
||||||
|
});
|
||||||
|
node.treeLink.dataset.node = node;
|
||||||
|
node.treeLink.addEventListener('click', () => {
|
||||||
|
this.onNodeClick(node);
|
||||||
|
});
|
||||||
|
|
||||||
|
node.childrenList = frappe.ui.create('ul', {
|
||||||
|
inside: node.parentLi,
|
||||||
|
className: 'tree-children hide'
|
||||||
|
});
|
||||||
|
|
||||||
|
// if(this.toolbar) {
|
||||||
|
// node.toolbar = this.getToolbar(node).insertAfter(node.treeLink);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
onNodeClick(node, click = true) {
|
||||||
|
this.setSelectedNode(node);
|
||||||
|
if(click) {
|
||||||
|
this.onClick && this.onClick(node);
|
||||||
|
}
|
||||||
|
this.expandNode(node);
|
||||||
|
// select link
|
||||||
|
utils.activate(this.tree, node.treeLink, 'tree-link', 'active');
|
||||||
|
if(node.toolbar) this.showToolbar(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
expandNode(node) {
|
||||||
|
if(node.expandable) {
|
||||||
|
this.toggleNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.expanded = !node.expanded;
|
||||||
|
// node.parent.classList.toggle('opened', node.expanded);
|
||||||
|
node.parent.classList.add('opened');
|
||||||
|
node.parentLi.classList.add('opened');
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleNode(node) {
|
||||||
|
if(!node.loaded) this.loadChildren(node);
|
||||||
|
|
||||||
|
// expand children
|
||||||
|
if(node.childrenList) {
|
||||||
|
if(node.childrenList.innerHTML.length) {
|
||||||
|
node.childrenList.classList.toggle('hide', !node.expanded);
|
||||||
|
}
|
||||||
|
|
||||||
|
// open close icon
|
||||||
|
if(this.iconSet) {
|
||||||
|
const oldIcon = node.treeLink.querySelector('svg');
|
||||||
|
const newIconKey = !node.expanded ? 'closed' : 'open';
|
||||||
|
const newIcon = frappe.ui.create(this.iconSet[newIconKey]);
|
||||||
|
node.treeLink.replaceChild(newIcon, oldIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getSelectedNode() { return this.selectedNode; }
|
||||||
|
|
||||||
|
setSelectedNode(node) { this.selectedNode = node; }
|
||||||
|
|
||||||
|
showToolbar() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = Tree;
|
@ -24,5 +24,33 @@ module.exports = {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
},
|
||||||
|
|
||||||
|
addLink(label, parent, action, unhide = true) {
|
||||||
|
const link = frappe.ui.add('button', 'btn btn-sm btn-outline-secondary', parent, label);
|
||||||
|
link.addEventListener('click', action);
|
||||||
|
if (unhide) {
|
||||||
|
parent.classList.remove('hide');
|
||||||
|
}
|
||||||
|
return link;
|
||||||
|
},
|
||||||
|
|
||||||
|
// https://css-tricks.com/snippets/javascript/loop-queryselectorall-matches/
|
||||||
|
forEachNode(nodeList, callback, scope) {
|
||||||
|
if(!nodeList) return;
|
||||||
|
for (var i = 0; i < nodeList.length; i++) {
|
||||||
|
callback.call(scope, nodeList[i], i);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
activate($parent, $child, commonClass, activeClass='active', index = -1) {
|
||||||
|
let $children = $parent.querySelectorAll(`.${commonClass}.${activeClass}`);
|
||||||
|
|
||||||
|
this.forEachNode($children, (node, i) => {
|
||||||
|
if(index >= 0 && i <= index) return;
|
||||||
|
node.classList.remove(activeClass);
|
||||||
|
})
|
||||||
|
|
||||||
|
$child.classList.add(activeClass);
|
||||||
|
}
|
||||||
}
|
}
|
@ -41,7 +41,7 @@ class BaseControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderTemplate() {
|
renderTemplate() {
|
||||||
if (this.form.doc) {
|
if (this.form && this.form.doc) {
|
||||||
this.wrapper.innerHTML = this.template(this.form.doc, this.doc);
|
this.wrapper.innerHTML = this.template(this.form.doc, this.doc);
|
||||||
} else {
|
} else {
|
||||||
this.wrapper.innerHTML = '';
|
this.wrapper.innerHTML = '';
|
||||||
@ -86,8 +86,8 @@ class BaseControl {
|
|||||||
if (!this.onlyInput) {
|
if (!this.onlyInput) {
|
||||||
this.makeDescription();
|
this.makeDescription();
|
||||||
}
|
}
|
||||||
if (this.placeholder) {
|
if (this.placeholder || this.inline) {
|
||||||
this.input.setAttribute('placeholder', this.placeholder);
|
this.input.setAttribute('placeholder', this.placeholder || this.label);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -173,6 +173,7 @@ class BaseControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateDocValue(value) {
|
async updateDocValue(value) {
|
||||||
|
if (!this.doc) return;
|
||||||
if (this.doc[this.fieldname] !== value) {
|
if (this.doc[this.fieldname] !== value) {
|
||||||
if (this.parentControl) {
|
if (this.parentControl) {
|
||||||
// its a child
|
// its a child
|
||||||
|
9
client/view/controls/dynamicLink.js
Normal file
9
client/view/controls/dynamicLink.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
const LinkControl = require('./link');
|
||||||
|
|
||||||
|
class DynamicLinkControl extends LinkControl {
|
||||||
|
getTarget() {
|
||||||
|
return this.doc[this.references];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = DynamicLinkControl;
|
@ -3,6 +3,7 @@ const controlClasses = {
|
|||||||
Code: require('./code'),
|
Code: require('./code'),
|
||||||
Data: require('./data'),
|
Data: require('./data'),
|
||||||
Date: require('./date'),
|
Date: require('./date'),
|
||||||
|
DynamicLink: require('./dynamicLink'),
|
||||||
Currency: require('./currency'),
|
Currency: require('./currency'),
|
||||||
Float: require('./float'),
|
Float: require('./float'),
|
||||||
Int: require('./int'),
|
Int: require('./int'),
|
||||||
|
@ -33,8 +33,8 @@ class LinkControl extends BaseControl {
|
|||||||
this.input.addEventListener('awesomplete-select', async (e) => {
|
this.input.addEventListener('awesomplete-select', async (e) => {
|
||||||
if (e.text && e.text.value === '__newItem') {
|
if (e.text && e.text.value === '__newItem') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const newDoc = await frappe.getNewDoc(this.target);
|
const newDoc = await frappe.getNewDoc(this.getTarget());
|
||||||
const formModal = await frappe.desk.showFormModal(this.target, newDoc.name);
|
const formModal = await frappe.desk.showFormModal(this.getTarget(), newDoc.name);
|
||||||
if (formModal.form.doc.meta.hasField('name')) {
|
if (formModal.form.doc.meta.hasField('name')) {
|
||||||
formModal.form.doc.set('name', this.input.value);
|
formModal.form.doc.set('name', this.input.value);
|
||||||
}
|
}
|
||||||
@ -49,7 +49,7 @@ class LinkControl extends BaseControl {
|
|||||||
|
|
||||||
async getList(query) {
|
async getList(query) {
|
||||||
return (await frappe.db.getAll({
|
return (await frappe.db.getAll({
|
||||||
doctype: this.target,
|
doctype: this.getTarget(),
|
||||||
filters: this.getFilters(query, this),
|
filters: this.getFilters(query, this),
|
||||||
limit: 50
|
limit: 50
|
||||||
})).map(d => d.name);
|
})).map(d => d.name);
|
||||||
@ -58,6 +58,10 @@ class LinkControl extends BaseControl {
|
|||||||
getFilters(query) {
|
getFilters(query) {
|
||||||
return { keywords: ["like", query] }
|
return { keywords: ["like", query] }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTarget() {
|
||||||
|
return this.target;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = LinkControl;
|
module.exports = LinkControl;
|
@ -1,23 +1,29 @@
|
|||||||
const frappe = require('frappejs');
|
const frappe = require('frappejs');
|
||||||
const controls = require('./controls');
|
const controls = require('./controls');
|
||||||
|
const FormLayout = require('./formLayout');
|
||||||
const Observable = require('frappejs/utils/observable');
|
const Observable = require('frappejs/utils/observable');
|
||||||
const keyboard = require('frappejs/client/ui/keyboard');
|
const keyboard = require('frappejs/client/ui/keyboard');
|
||||||
|
const utils = require('frappejs/client/ui/utils');
|
||||||
|
|
||||||
module.exports = class BaseForm extends Observable {
|
module.exports = class BaseForm extends Observable {
|
||||||
constructor({doctype, parent, submit_label='Submit', container}) {
|
constructor({doctype, parent, submit_label='Submit', container, meta, inline=false}) {
|
||||||
super();
|
super();
|
||||||
Object.assign(this, arguments[0]);
|
Object.assign(this, arguments[0]);
|
||||||
this.controls = {};
|
|
||||||
this.controlList = [];
|
|
||||||
this.sections = [];
|
|
||||||
this.links = [];
|
this.links = [];
|
||||||
|
|
||||||
this.meta = frappe.getMeta(this.doctype);
|
this.meta = frappe.getMeta(this.doctype);
|
||||||
|
|
||||||
if (this.setup) {
|
if (this.setup) {
|
||||||
this.setup();
|
this.setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.make();
|
this.make();
|
||||||
this.bindFormEvents();
|
this.bindFormEvents();
|
||||||
|
|
||||||
|
if (this.doc) {
|
||||||
|
// bootstrapped with a doc
|
||||||
|
this.bindEvents(this.doc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
make() {
|
make() {
|
||||||
@ -25,13 +31,31 @@ module.exports = class BaseForm extends Observable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.body = frappe.ui.add('div', 'form-body', this.parent);
|
if (this.inline) {
|
||||||
this.makeToolbar();
|
this.body = this.parent
|
||||||
|
} else {
|
||||||
|
this.body = frappe.ui.add('div', 'form-body', this.parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.actions) {
|
||||||
|
this.makeToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
this.form = frappe.ui.add('form', 'form-container', this.body);
|
this.form = frappe.ui.add('form', 'form-container', this.body);
|
||||||
|
|
||||||
|
if (this.inline) {
|
||||||
|
this.form.classList.add('form-inline');
|
||||||
|
}
|
||||||
|
|
||||||
this.form.onValidate = true;
|
this.form.onValidate = true;
|
||||||
|
|
||||||
this.makeLayout();
|
this.formLayout = new FormLayout({
|
||||||
|
fields: this.meta.fields,
|
||||||
|
layout: this.meta.layout
|
||||||
|
});
|
||||||
|
|
||||||
|
this.form.appendChild(this.formLayout.form);
|
||||||
|
|
||||||
this.bindKeyboard();
|
this.bindKeyboard();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,44 +67,6 @@ module.exports = class BaseForm extends Observable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
makeLayout() {
|
|
||||||
if (this.meta.layout) {
|
|
||||||
for (let section of this.meta.layout) {
|
|
||||||
this.makeSection(section);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.makeControls(this.meta.fields);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
makeSection(section) {
|
|
||||||
const sectionElement = frappe.ui.add('div', 'form-section', this.form);
|
|
||||||
if (section.columns) {
|
|
||||||
sectionElement.classList.add('row');
|
|
||||||
for (let column of section.columns) {
|
|
||||||
let columnElement = frappe.ui.add('div', 'col', sectionElement);
|
|
||||||
this.makeControls(this.getFieldsFromLayoutElement(column.fields), columnElement);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.makeControls(this.getFieldsFromLayoutElement(section.fields), sectionElement);
|
|
||||||
}
|
|
||||||
this.sections.push(sectionElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFieldsFromLayoutElement(fields) {
|
|
||||||
return this.meta.fields.filter(d => fields.includes(d.fieldname));
|
|
||||||
}
|
|
||||||
|
|
||||||
makeControls(fields, parent) {
|
|
||||||
for(let field of fields) {
|
|
||||||
if (!field.hidden && controls.getControlClass(field.fieldtype)) {
|
|
||||||
let control = controls.makeControl({field: field, form: this, parent: parent});
|
|
||||||
this.controlList.push(control);
|
|
||||||
this.controls[field.fieldname] = control;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
makeToolbar() {
|
makeToolbar() {
|
||||||
if (this.actions.includes('save')) {
|
if (this.actions.includes('save')) {
|
||||||
this.makeSaveButton();
|
this.makeSaveButton();
|
||||||
@ -180,6 +166,8 @@ module.exports = class BaseForm extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setTitle() {
|
setTitle() {
|
||||||
|
if (!this.container) return;
|
||||||
|
|
||||||
const doctypeLabel = this.doc.meta.label || this.doc.meta.name;
|
const doctypeLabel = this.doc.meta.label || this.doc.meta.name;
|
||||||
|
|
||||||
if (this.doc.meta.isSingle || this.doc.meta.naming === 'random') {
|
if (this.doc.meta.isSingle || this.doc.meta.naming === 'random') {
|
||||||
@ -216,10 +204,12 @@ module.exports = class BaseForm extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refreshLinks(links) {
|
refreshLinks(links) {
|
||||||
|
if (!this.container) return;
|
||||||
|
|
||||||
this.container.clearLinks();
|
this.container.clearLinks();
|
||||||
for(let link of links) {
|
for(let link of links) {
|
||||||
// make the link
|
// make the link
|
||||||
this.container.addLink(link.label, () => {
|
utils.addLink(link.label, this.container.linksElement, () => {
|
||||||
let options = link.action(this);
|
let options = link.action(this);
|
||||||
|
|
||||||
if (options) {
|
if (options) {
|
||||||
@ -243,7 +233,7 @@ module.exports = class BaseForm extends Observable {
|
|||||||
this.doc.off(this.docListener);
|
this.doc.off(this.docListener);
|
||||||
}
|
}
|
||||||
this.doc = doc;
|
this.doc = doc;
|
||||||
for (let control of this.controlList) {
|
for (let control of this.formLayout.controlList) {
|
||||||
control.bind(this.doc);
|
control.bind(this.doc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -257,7 +247,7 @@ module.exports = class BaseForm extends Observable {
|
|||||||
this.docListener = (params) => {
|
this.docListener = (params) => {
|
||||||
if (params.fieldname) {
|
if (params.fieldname) {
|
||||||
// only single value changed
|
// only single value changed
|
||||||
let control = this.controls[params.fieldname];
|
let control = this.formLayout.controls[params.fieldname];
|
||||||
if (control && control.getInputValue() !== control.format(params.fieldname)) {
|
if (control && control.getInputValue() !== control.format(params.fieldname)) {
|
||||||
control.refresh();
|
control.refresh();
|
||||||
}
|
}
|
||||||
@ -276,7 +266,7 @@ module.exports = class BaseForm extends Observable {
|
|||||||
checkValidity() {
|
checkValidity() {
|
||||||
let validity = this.form.checkValidity();
|
let validity = this.form.checkValidity();
|
||||||
if (validity) {
|
if (validity) {
|
||||||
for (let control of this.controlList) {
|
for (let control of this.formLayout.controlList) {
|
||||||
// check validity in table
|
// check validity in table
|
||||||
if (control.fieldtype==='Table') {
|
if (control.fieldtype==='Table') {
|
||||||
validity = control.checkValidity();
|
validity = control.checkValidity();
|
||||||
@ -290,9 +280,7 @@ module.exports = class BaseForm extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
for(let control of this.controlList) {
|
this.formLayout.refresh();
|
||||||
control.refresh();
|
|
||||||
}
|
|
||||||
this.trigger('refresh', this);
|
this.trigger('refresh', this);
|
||||||
this.setLinks();
|
this.setLinks();
|
||||||
}
|
}
|
||||||
|
89
client/view/formLayout.js
Normal file
89
client/view/formLayout.js
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
const controls = require('./controls');
|
||||||
|
const Observable = require('frappejs/utils/observable');
|
||||||
|
|
||||||
|
module.exports = class FormLayout extends Observable {
|
||||||
|
constructor({fields, layout, events = []}) {
|
||||||
|
super();
|
||||||
|
Object.assign(this, arguments[0]);
|
||||||
|
this.controls = {};
|
||||||
|
this.controlList = [];
|
||||||
|
this.sections = [];
|
||||||
|
this.links = [];
|
||||||
|
|
||||||
|
this.doc = {
|
||||||
|
get(fieldname) {
|
||||||
|
return this[fieldname]
|
||||||
|
},
|
||||||
|
|
||||||
|
set(fieldname, value) {
|
||||||
|
this[fieldname] = value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this.form = document.createElement('div');
|
||||||
|
this.form.classList.add('form-body');
|
||||||
|
|
||||||
|
this.makeLayout();
|
||||||
|
this.bindEvents(this.doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
makeLayout() {
|
||||||
|
if (this.layout) {
|
||||||
|
for (let section of this.layout) {
|
||||||
|
this.makeSection(section);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.makeControls(this.fields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSection(section) {
|
||||||
|
const sectionElement = frappe.ui.add('div', 'form-section', this.form);
|
||||||
|
if (section.columns) {
|
||||||
|
sectionElement.classList.add('row');
|
||||||
|
for (let column of section.columns) {
|
||||||
|
let columnElement = frappe.ui.add('div', 'col', sectionElement);
|
||||||
|
this.makeControls(this.getFieldsFromLayoutElement(column.fields), columnElement);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.makeControls(this.getFieldsFromLayoutElement(section.fields), sectionElement);
|
||||||
|
}
|
||||||
|
this.sections.push(sectionElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFieldsFromLayoutElement(fields) {
|
||||||
|
return this.fields.filter(d => fields.includes(d.fieldname));
|
||||||
|
}
|
||||||
|
|
||||||
|
makeControls(fields, parent) {
|
||||||
|
for(let field of fields) {
|
||||||
|
if (!field.hidden && controls.getControlClass(field.fieldtype)) {
|
||||||
|
let control = controls.makeControl({field: field, form: this, parent: parent});
|
||||||
|
this.controlList.push(control);
|
||||||
|
this.controls[field.fieldname] = control;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async bindEvents(doc) {
|
||||||
|
this.doc = doc;
|
||||||
|
this.controlList.forEach(control => {
|
||||||
|
control.bind(this.doc);
|
||||||
|
});
|
||||||
|
this.refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.controlList.forEach(control => {
|
||||||
|
control.refresh();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bindFormEvents() {
|
||||||
|
if (this.events) {
|
||||||
|
for (let key in this.events) {
|
||||||
|
this.on(key, this.events[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@ const keyboard = require('frappejs/client/ui/keyboard');
|
|||||||
const Observable = require('frappejs/utils/observable');
|
const Observable = require('frappejs/utils/observable');
|
||||||
|
|
||||||
module.exports = class BaseList extends Observable {
|
module.exports = class BaseList extends Observable {
|
||||||
constructor({doctype, parent, fields, page}) {
|
constructor({doctype, parent, fields=[], page}) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
Object.assign(this, arguments[0]);
|
Object.assign(this, arguments[0]);
|
||||||
@ -17,11 +17,29 @@ module.exports = class BaseList extends Observable {
|
|||||||
this.rows = [];
|
this.rows = [];
|
||||||
this.data = [];
|
this.data = [];
|
||||||
|
|
||||||
|
this.setupListSettings();
|
||||||
|
|
||||||
frappe.db.on(`change:${this.doctype}`, (params) => {
|
frappe.db.on(`change:${this.doctype}`, (params) => {
|
||||||
this.refresh();
|
this.refresh();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupListSettings() {
|
||||||
|
// list settings that can be overridden by meta
|
||||||
|
this.listSettings = {
|
||||||
|
getFields: list => list.fields,
|
||||||
|
getRowHTML: (list, data) => {
|
||||||
|
return `<div class="col-11">
|
||||||
|
${list.getNameHTML(data)}
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.meta.listSettings) {
|
||||||
|
Object.assign(this.listSettings, this.meta.listSettings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
makeBody() {
|
makeBody() {
|
||||||
if (!this.body) {
|
if (!this.body) {
|
||||||
this.makeToolbar();
|
this.makeToolbar();
|
||||||
@ -62,7 +80,7 @@ module.exports = class BaseList extends Observable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getData() {
|
async getData() {
|
||||||
let fields = this.getFields();
|
let fields = this.listSettings.getFields(this) || [];
|
||||||
this.updateStandardFields(fields);
|
this.updateStandardFields(fields);
|
||||||
return await frappe.db.getAll({
|
return await frappe.db.getAll({
|
||||||
doctype: this.doctype,
|
doctype: this.doctype,
|
||||||
@ -73,10 +91,6 @@ module.exports = class BaseList extends Observable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getFields() {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
updateStandardFields(fields) {
|
updateStandardFields(fields) {
|
||||||
if (!fields.includes('name')) fields.push('name');
|
if (!fields.includes('name')) fields.push('name');
|
||||||
if (!fields.includes('modified')) fields.push('modified');
|
if (!fields.includes('modified')) fields.push('modified');
|
||||||
@ -105,14 +119,9 @@ module.exports = class BaseList extends Observable {
|
|||||||
getRowBodyHTML(data) {
|
getRowBodyHTML(data) {
|
||||||
return `<div class="col-1">
|
return `<div class="col-1">
|
||||||
<input class="checkbox" type="checkbox" data-name="${data.name}">
|
<input class="checkbox" type="checkbox" data-name="${data.name}">
|
||||||
</div>` + this.getRowHTML(data);
|
</div>` + this.listSettings.getRowHTML(this, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
getRowHTML(data) {
|
|
||||||
return `<div class="col-11">
|
|
||||||
${this.getNameHTML(data)}
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getNameHTML(data) {
|
getNameHTML(data) {
|
||||||
return `<span class="indicator ${this.meta.getIndicatorColor(data)}">${data[this.meta.titleField]}</span>`;
|
return `<span class="indicator ${this.meta.getIndicatorColor(data)}">${data[this.meta.titleField]}</span>`;
|
||||||
|
@ -11,8 +11,8 @@ module.exports = class Page extends Observable {
|
|||||||
}
|
}
|
||||||
this.make();
|
this.make();
|
||||||
this.dropdowns = {};
|
this.dropdowns = {};
|
||||||
|
|
||||||
if(this.title) {
|
if(this.title) {
|
||||||
|
this.wrapper.setAttribute('title', this.title);
|
||||||
this.setTitle(this.title);
|
this.setTitle(this.title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -37,15 +37,6 @@ module.exports = class Page extends Observable {
|
|||||||
${message}</span>`;
|
${message}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
addLink(label, action, unhide = true) {
|
|
||||||
const link = frappe.ui.add('button', 'btn btn-sm btn-outline-secondary', this.linksElement, label);
|
|
||||||
link.addEventListener('click', action);
|
|
||||||
if (unhide) {
|
|
||||||
this.linksElement.classList.remove('hide');
|
|
||||||
}
|
|
||||||
return link;
|
|
||||||
}
|
|
||||||
|
|
||||||
clearLinks() {
|
clearLinks() {
|
||||||
frappe.ui.empty(this.linksElement);
|
frappe.ui.empty(this.linksElement);
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ module.exports = {
|
|||||||
naming: "name", // {random|autoincrement}
|
naming: "name", // {random|autoincrement}
|
||||||
isSingle: 0,
|
isSingle: 0,
|
||||||
isChild: 0,
|
isChild: 0,
|
||||||
|
isSubmittable: 0,
|
||||||
|
settings: null,
|
||||||
keywordFields: [],
|
keywordFields: [],
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
@ -5,11 +5,13 @@ const naming = require('./naming');
|
|||||||
module.exports = class BaseDocument extends Observable {
|
module.exports = class BaseDocument extends Observable {
|
||||||
constructor(data) {
|
constructor(data) {
|
||||||
super();
|
super();
|
||||||
this.fetchValues = {};
|
this.fetchValuesCache = {};
|
||||||
this.flags = {};
|
this.flags = {};
|
||||||
this.setup();
|
this.setup();
|
||||||
Object.assign(this, data);
|
Object.assign(this, data);
|
||||||
frappe.db.on('change', (params) => this.fetchValues[`${params.doctype}:${params.name}`] = {});
|
|
||||||
|
// clear fetch-values cache
|
||||||
|
frappe.db.on('change', (params) => this.fetchValuesCache[`${params.doctype}:${params.name}`] = {});
|
||||||
}
|
}
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
@ -30,10 +32,6 @@ module.exports = class BaseDocument extends Observable {
|
|||||||
return this._settings;
|
return this._settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(fieldname) {
|
|
||||||
return this[fieldname];
|
|
||||||
}
|
|
||||||
|
|
||||||
// set value and trigger change
|
// set value and trigger change
|
||||||
async set(fieldname, value) {
|
async set(fieldname, value) {
|
||||||
if (this[fieldname] !== value) {
|
if (this[fieldname] !== value) {
|
||||||
@ -118,7 +116,7 @@ module.exports = class BaseDocument extends Observable {
|
|||||||
this.owner = frappe.session.user;
|
this.owner = frappe.session.user;
|
||||||
this.creation = now;
|
this.creation = now;
|
||||||
}
|
}
|
||||||
this.modifieldBy = frappe.session.user;
|
this.modifiedBy = frappe.session.user;
|
||||||
this.modified = now;
|
this.modified = now;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,7 +296,7 @@ module.exports = class BaseDocument extends Observable {
|
|||||||
|
|
||||||
async getFrom(doctype, name, fieldname) {
|
async getFrom(doctype, name, fieldname) {
|
||||||
if (!name) return '';
|
if (!name) return '';
|
||||||
let _values = this.fetchValues[`${doctype}:${name}`] || (this.fetchValues[`${doctype}:${name}`] = {});
|
let _values = this.fetchValuesCache[`${doctype}:${name}`] || (this.fetchValuesCache[`${doctype}:${name}`] = {});
|
||||||
if (!_values[fieldname]) {
|
if (!_values[fieldname]) {
|
||||||
_values[fieldname] = await frappe.db.getValue(doctype, name, fieldname);
|
_values[fieldname] = await frappe.db.getValue(doctype, name, fieldname);
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ module.exports = {
|
|||||||
fieldname: 'owner', fieldtype: 'Data', required: 1
|
fieldname: 'owner', fieldtype: 'Data', required: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'modifieldBy', fieldtype: 'Data', required: 1
|
fieldname: 'modifiedBy', fieldtype: 'Data', required: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fieldname: 'creation', fieldtype: 'Datetime', required: 1
|
fieldname: 'creation', fieldtype: 'Datetime', required: 1
|
||||||
|
@ -65,10 +65,22 @@ module.exports = {
|
|||||||
if (!e.status_code || e.status_code !== 404) {
|
if (!e.status_code || e.status_code !== 404) {
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
series = frappe.newDoc({doctype: 'NumberSeries', name: prefix, current: 0});
|
await this.createNumberSeries(prefix);
|
||||||
await series.insert();
|
|
||||||
}
|
}
|
||||||
let next = await series.next()
|
let next = await series.next()
|
||||||
return prefix + next;
|
return prefix + next;
|
||||||
|
},
|
||||||
|
|
||||||
|
async createNumberSeries(prefix, setting, start=1000) {
|
||||||
|
if (!(await frappe.db.exists('NumberSeries', prefix))) {
|
||||||
|
const series = frappe.newDoc({doctype: 'NumberSeries', name: prefix, current: start});
|
||||||
|
await series.insert();
|
||||||
|
|
||||||
|
if (setting) {
|
||||||
|
const settingDoc = await frappe.getSingle(setting);
|
||||||
|
settingDoc.numberSeries = series.name;
|
||||||
|
await settingDoc.update();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const BaseList = require('frappejs/client/view/list');
|
const BaseList = require('frappejs/client/view/list');
|
||||||
|
|
||||||
module.exports = class ToDoList extends BaseList {
|
module.exports = class ToDoList extends BaseList {
|
||||||
getFields() {
|
getFields(list) {
|
||||||
return ['name', 'subject', 'status'];
|
return ['name', 'subject', 'status'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
"node-sass": "^4.7.2",
|
"node-sass": "^4.7.2",
|
||||||
"nodemon": "^1.14.7",
|
"nodemon": "^1.14.7",
|
||||||
"nunjucks": "^3.1.0",
|
"nunjucks": "^3.1.0",
|
||||||
|
"octicons": "^7.2.0",
|
||||||
"popper.js": "^1.12.9",
|
"popper.js": "^1.12.9",
|
||||||
"precss": "^2.0.0",
|
"precss": "^2.0.0",
|
||||||
"puppeteer": "^1.2.0",
|
"puppeteer": "^1.2.0",
|
||||||
|
@ -8,6 +8,16 @@ module.exports = class Observable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getter, setter stubs, so Observable can be used as a simple Document
|
||||||
|
get(key) {
|
||||||
|
return this[key];
|
||||||
|
}
|
||||||
|
|
||||||
|
set(key, value) {
|
||||||
|
this[key] = value;
|
||||||
|
this.trigger('change', {doc: this, fieldname: key});
|
||||||
|
}
|
||||||
|
|
||||||
on(event, listener) {
|
on(event, listener) {
|
||||||
this._addListener('listeners', event, listener);
|
this._addListener('listeners', event, listener);
|
||||||
if (this._observable.socketClient) {
|
if (this._observable.socketClient) {
|
||||||
|
@ -2544,7 +2544,7 @@ oauth-sign@~0.8.1, oauth-sign@~0.8.2:
|
|||||||
version "0.8.2"
|
version "0.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43"
|
||||||
|
|
||||||
object-assign@^4.0.1, object-assign@^4.1.0:
|
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
|
||||||
version "4.1.1"
|
version "4.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
|
|
||||||
@ -2559,6 +2559,12 @@ object.omit@^2.0.0:
|
|||||||
for-own "^0.1.4"
|
for-own "^0.1.4"
|
||||||
is-extendable "^0.1.1"
|
is-extendable "^0.1.1"
|
||||||
|
|
||||||
|
octicons@^7.2.0:
|
||||||
|
version "7.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/octicons/-/octicons-7.2.0.tgz#a721635f73c774d7ffda56a83a29dbde03fc4b53"
|
||||||
|
dependencies:
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
on-finished@~2.3.0:
|
on-finished@~2.3.0:
|
||||||
version "2.3.0"
|
version "2.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user