2
0
mirror of https://github.com/frappe/books.git synced 2024-12-23 03:19:01 +00:00

added modal

This commit is contained in:
Rushabh Mehta 2018-02-07 18:53:52 +05:30
parent 78799cb8d9
commit 2a2f1fb6f3
11 changed files with 224 additions and 27 deletions

View File

@ -1,6 +1,6 @@
const frappe = require('frappejs');
const sqlite3 = require('sqlite3').verbose();
const debug = true;
const debug = false;
class sqliteDatabase {
constructor({ db_path }) {
@ -164,10 +164,11 @@ class sqliteDatabase {
// delete other children
// `delete from doctype where parent = ? and name not in (?, ?, ?)}`
await this.run(`delete from ${frappe.slug(field.childtype)}
where
parent = ? and
name not in (${added_children.map(d => '?').join(', ')})`, added_children);
name not in (${added_children.slice(1).map(d => '?').join(', ')})`, added_children);
}
return doc;
}
@ -185,6 +186,9 @@ class sqliteDatabase {
}
prepare_child(parenttype, parent, child, field, idx) {
if (!child.name) {
child.name = frappe.get_random_name();
}
child.parent = parent;
child.parenttype = parenttype;
child.parentfield = field.fieldname;

View File

@ -26,7 +26,6 @@ module.exports = class Desk {
};
this.init_routes();
// this.search = new Search(this.nav);
}

View File

@ -1,6 +1,11 @@
@import "node_modules/bootstrap/scss/bootstrap";
@import "node_modules/awesomplete/awesomplete";
$spacer-1: 0.25rem;
$spacer-2: 0.5rem;
$spacer-3: 1rem;
$spacer-4: 2rem;
html {
font-size: 14px;
}
@ -18,12 +23,12 @@ html {
max-width: 600px;
.form-toolbar {
height: 2rem;
margin-bottom: 1rem;
height: $spacer-4;
margin-bottom: $spacer-3;
}
.alert {
margin-top: 1rem;
margin-top: $spacer-3;
}
}
@ -66,13 +71,21 @@ mark {
padding: 0px;
}
.table-wrapper {
margin-bottom: $spacer-2;
}
.table-toolbar {
margin-top: $spacer-2;
}
.data-table-col .edit-cell {
padding: 0px !important;
input, textarea {
border-radius: none;
margin: none;
padding: 0.25rem;
padding: $spacer-1;
}
.awesomplete > ul {

59
client/ui/modal.js Normal file
View File

@ -0,0 +1,59 @@
const $ = require('jquery');
const bootstrap = require('bootstrap');
module.exports = class Modal {
constructor({ title, body, primary_label, primary_action, secondary_label, secondary_action }) {
Object.assign(this, arguments[0]);
this.$modal = $(`<div class="modal" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">${title}</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
${body}
</div>
<div class="modal-footer">
</div>
</div>
</div>
</div>`).appendTo(document.body);
if (this.primary_label) {
this.add_primary(this.primary_label, this.primary_action);
}
if (this.secondary_label) {
this.add_secondary(this.secondary_label, this.secondary_action);
}
this.show();
}
add_primary(label, action) {
this.$primary = $(`<button type="button" class="btn btn-primary">
${label}</button>`)
.appendTo(this.$modal.find('.modal-footer'))
.on('click', () => action(this));
}
add_secondary(label, action) {
this.$primary = $(`<button type="button" class="btn btn-secondary">
${label}</button>`)
.appendTo(this.$modal.find('.modal-footer'))
.on('click', () => action(this));
}
show() {
this.$modal.modal('show');
}
hide() {
this.$modal.modal('hide');
}
get_body() {
return this.$modal.find('.modal-body').get(0);
}
}

View File

@ -4,7 +4,12 @@ class FloatControl extends BaseControl {
make() {
super.make();
this.input.setAttribute('type', 'text');
this.input.classList.add('text-right');
this.input.classList.add('text-right');
this.input.addEventListener('focus', () => {
setTimeout(() => {
this.input.select();
}, 100);
})
}
};

View File

@ -2,20 +2,41 @@ const frappe = require('frappejs');
const BaseControl = require('./base');
const DataTable = require('frappe-datatable');
const controls = require('./index');
const Modal = require('frappejs/client/ui/modal');
class TableControl extends BaseControl {
make() {
if (!this.datatable) {
this.wrapper = frappe.ui.add('div', 'datatable-wrapper', this.get_input_parent());
this.datatable = new DataTable(this.wrapper, {
this.wrapper = frappe.ui.add('div', 'table-wrapper', this.get_input_parent());
this.wrapper.innerHTML =
`<div class="datatable-wrapper"></div>
<div class="table-toolbar">
<button class="btn btn-sm btn-outline-secondary btn-add">${frappe._("Add")}</button>
<button class="btn btn-sm btn-outline-danger btn-remove">${frappe._("Remove")}</button>
</div>`;
this.datatable = new DataTable(this.wrapper.querySelector('.datatable-wrapper'), {
columns: this.get_columns(),
data: this.get_table_data(),
takeAvailableSpace: true,
enableClusterize: true,
addCheckboxColumn: true,
editing: this.get_table_input.bind(this),
});
this.datatable.datatableWrapper.style = 'height: 300px';
this.wrapper.querySelector('.btn-add').addEventListener('click', async (event) => {
this.doc[this.fieldname].push({});
await this.doc.commit();
this.refresh();
});
this.wrapper.querySelector('.btn-remove').addEventListener('click', async (event) => {
let checked = this.datatable.rowmanager.getCheckedRows();
this.doc[this.fieldname] = this.doc[this.fieldname].filter(d => !checked.includes(d.idx));
await this.doc.commit();
this.refresh();
this.datatable.rowmanager.checkAll(false);
});
}
}
@ -33,6 +54,15 @@ class TableControl extends BaseControl {
get_table_input(colIndex, rowIndex, value, parent) {
let field = this.datatable.getColumn(colIndex).field;
if (field.fieldtype==='Text') {
return this.get_text_control(field);
} else {
return this.get_control(field, parent);
}
}
get_control(field, parent) {
field.only_input = true;
const control = controls.make_control({field: field, parent: parent});
@ -50,6 +80,23 @@ class TableControl extends BaseControl {
return control.get_input_value();
}
}
}
get_text_control(field, parent) {
this.text_modal = new Modal({
title: frappe._('Edit {0}', field.label),
body: '',
primary_label: frappe._('Submit'),
primary_action: (modal) => {
this.datatable.cellmanager.submitEditing();
this.datatable.cellmanager.deactivateEditing();
modal.hide();
}
});
return this.get_control(field, this.text_modal.get_body());
}
get_columns() {

View File

@ -27,7 +27,7 @@ module.exports = class BaseForm extends Observable {
this.body = frappe.ui.add('div', 'form-body', this.parent);
this.make_toolbar();
this.form = frappe.ui.add('form', null, this.body);
this.form = frappe.ui.add('div', 'form-container', this.body);
for(let field of this.meta.fields) {
if (controls.get_control_class(field.fieldtype)) {
let control = controls.make_control({field: field, form: this});

View File

@ -1,4 +1,5 @@
const utils = require('../utils');
const number_format = require('../utils/number_format');
const model = require('../model');
const BaseDocument = require('../model/document');
const BaseMeta = require('../model/meta');
@ -9,6 +10,7 @@ const errors = require('./errors');
module.exports = {
init_libs(frappe) {
Object.assign(frappe, utils);
Object.assign(frappe, number_format);
frappe.model = model;
frappe.BaseDocument = BaseDocument;
frappe.BaseMeta = BaseMeta;

View File

@ -121,11 +121,29 @@ module.exports = class BaseDocument {
}
}
async insert() {
set_child_idx() {
// renumber children
for (let field of this.meta.get_valid_fields()) {
if (field.fieldtype==='Table') {
for(let i=0; i < (this[field.fieldname] || []).length; i++) {
this[field.fieldname][i].idx = i;
}
}
}
}
async commit() {
// re-run triggers
this.set_name();
this.set_standard_values();
this.set_keywords();
this.set_child_idx();
await this.trigger('validate');
await this.trigger('commit');
}
async insert() {
await this.commit();
await this.trigger('before_insert');
this.sync_values(await frappe.db.insert(this.doctype, this.get_valid_dict()));
await this.trigger('after_insert');
@ -134,10 +152,8 @@ module.exports = class BaseDocument {
return this;
}
async update() {
this.set_standard_values();
this.set_keywords();
await this.trigger('validate');
async update() {
await this.commit();
await this.trigger('before_update');
this.sync_values(await frappe.db.update(this.doctype, this.get_valid_dict()));
await this.trigger('after_update');

View File

@ -17,6 +17,10 @@ describe('Document', () => {
let doc2 = await frappe.get_doc(doc1.doctype, doc1.name);
assert.equal(doc1.subject, doc2.subject);
assert.equal(doc1.description, doc2.description);
// test frappe.db.exists
assert.ok(await frappe.db.exists(doc1.doctype, doc1.name));
assert.equal(await frappe.db.exists(doc1.doctype, doc1.name + '---'), false);
});
it('should update a doc', async () => {
@ -80,6 +84,7 @@ describe('Document', () => {
await user.insert();
assert.equal(user.roles.length, 1);
assert.equal(user.roles[0].role, 'Test Role');
assert.equal(user.roles[0].parent, user.name);
assert.equal(user.roles[0].parenttype, user.doctype);
assert.equal(user.roles[0].parentfield, 'roles');
@ -87,6 +92,27 @@ describe('Document', () => {
// add another role
user.roles.push({role: 'Test Role 1'});
await user.update();
assert.equal(user.roles.length, 2);
assert.equal(user.roles[1].role, 'Test Role 1');
assert.equal(user.roles[1].parent, user.name);
assert.equal(user.roles[1].parenttype, user.doctype);
assert.equal(user.roles[1].parentfield, 'roles');
// remove the first row
user.roles = user.roles.filter((d, i) => i === 1);
await user.update();
user = await frappe.get_doc('User', user.name);
assert.equal(user.roles.length, 1);
assert.equal(user.roles[0].role, 'Test Role 1');
assert.equal(user.roles[0].idx, 0);
assert.equal(user.roles[0].parent, user.name);
assert.equal(user.roles[0].parenttype, user.doctype);
assert.equal(user.roles[0].parentfield, 'roles');
});
});

View File

@ -1,15 +1,16 @@
let utils = {};
Object.assign(utils, require('./number_format'));
Object.assign(utils, {
module.exports = {
format(value, field) {
if (field.fieldtype==='Currency') {
return frappe.format_number(value);
} else {
return value + '';
if (value===null || value===undefined) {
return '';
} else {
return value + '';
}
}
},
slug(text) {
return text.toLowerCase().replace(/ /g, '_');
},
@ -31,7 +32,32 @@ Object.assign(utils, {
return new Promise(resolve => {
setTimeout(resolve, seconds * 1000);
});
}
});
},
module.exports = utils;
_(text, args) {
// should return translated text
return this.string_replace(text, args);
},
string_replace(str, args) {
if (!Array.isArray(args)) {
args = [args];
}
if(str==undefined) return str;
let unkeyed_index = 0;
return str.replace(/\{(\w*)\}/g, (match, key) => {
if (key === '') {
key = unkeyed_index;
unkeyed_index++
}
if (key == +key) {
return args[key] !== undefined
? args[key]
: match;
}
});
}
};