2
0
mirror of https://github.com/frappe/books.git synced 2024-11-09 23:30:56 +00:00

added text, select controls and fixes to form.js

This commit is contained in:
Rushabh Mehta 2018-01-10 16:21:35 +05:30
parent d9e306b641
commit 234eb9dea1
14 changed files with 231 additions and 130 deletions

View File

@ -5,6 +5,9 @@ class RESTClient {
constructor({server, protocol='http', fetch}) {
this.server = server;
this.protocol = protocol;
this.init_type_map();
frappe.fetch = fetch;
this.json_headers = {
'Accept': 'application/json',
@ -78,6 +81,36 @@ class RESTClient {
return await response.json();
}
init_type_map() {
this.type_map = {
'Currency': true
,'Int': true
,'Float': true
,'Percent': true
,'Check': true
,'Small Text': true
,'Long Text': true
,'Code': true
,'Text Editor': true
,'Date': true
,'Datetime': true
,'Time': true
,'Text': true
,'Data': true
,'Link': true
,'Dynamic Link':true
,'Password': true
,'Select': true
,'Read Only': true
,'Attach': true
,'Attach Image':true
,'Signature': true
,'Color': true
,'Barcode': true
,'Geolocation': true
}
}
close() {
}

View File

@ -13,6 +13,8 @@ class sqliteDatabase {
}
return new Promise(resolve => {
this.conn = new sqlite3.Database(this.db_path, () => {
// debug
// this.conn.on('trace', (trace) => console.log(trace));
resolve();
});
});
@ -32,10 +34,14 @@ class sqliteDatabase {
async create_table(doctype) {
let meta = frappe.get_meta(doctype);
let columns = [];
let values = [];
for (let df of this.get_fields(meta)) {
if (this.type_map[df.fieldtype]) {
columns.push(`${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd ? "not null" : ""} ${df.default ? ("default " + frappe.sqlescape(df.default)) : ""}`);
columns.push(`${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd ? "not null" : ""} ${df.default ? "default ?" : ""}`);
if (df.default) {
values.push(df.default);
}
}
}
@ -70,18 +76,29 @@ class sqliteDatabase {
}
async insert(doctype, doc) {
let placeholders = Object.keys(doc).map(d => '?').join(', ');
return await this.run(`insert into ${frappe.slug(doctype)}
(${Object.keys(doc).join(", ")})
values (${Object.values(doc).map(d => frappe.db.escape(d)).join(", ")})`);
values (${placeholders})`, this.get_formatted_values(doc));
}
async update(doctype, doc) {
let assigns = [];
for (let key in doc) {
assigns.push(`${key} = ${this.escape(doc[key])}`);
}
let assigns = Object.keys(doc).map(key => `${key} = ?`);
let values = this.get_formatted_values(doc);
values.push(doc.name);
return await this.run(`update ${frappe.slug(doctype)}
set ${assigns.join(", ")}`);
set ${assigns.join(", ")} where name=?`, values);
}
get_formatted_values(doc) {
return Object.values(doc).map(value => {
if (value instanceof Date) {
return value.toISOString();
} else {
return value;
}
})
}
async delete(doctype, name) {
@ -89,14 +106,20 @@ class sqliteDatabase {
}
get_all({doctype, fields=['name'], filters, start, limit, order_by='modified', order='desc'} = {}) {
return new Promise(resolve => {
return new Promise((resolve, reject) => {
let conditions = this.get_filter_conditions(filters);
this.conn.all(`select ${fields.join(", ")}
from ${frappe.slug(doctype)}
${filters ? "where" : ""} ${this.get_filter_conditions(filters)}
${conditions.conditions ? "where" : ""} ${conditions.conditions}
${order_by ? ("order by " + order_by) : ""} ${order_by ? (order || "asc") : ""}
${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`,
${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`, conditions.values,
(err, rows) => {
resolve(rows);
if (err) {
reject(err);
} else {
resolve(rows);
}
});
});
}
@ -106,22 +129,27 @@ class sqliteDatabase {
// {"status": "Open", "name": ["like", "apple%"]}
// => `status="Open" and name like "apple%"
let conditions = [];
let values = [];
for (let key in filters) {
const value = filters[key];
if (value instanceof Array) {
conditions.push(`${key} ${value[0]} ${this.escape(value)}`);
conditions.push(`${key} ${value[0]} ?`);
} else {
conditions.push(`${key} = ${this.escape(value)}`);
conditions.push(`${key} = ?`);
}
values.push(value);
}
return conditions.join(" and ");
return {
conditions: conditions.length ? conditions.join(" and ") : "",
values: values
};
}
run(query, params) {
//console.log(query);
return new Promise((resolve, reject) => {
this.conn.run(query, params, (err) => {
if (err) {
console.log(err);
reject(err);
} else {
resolve();
@ -131,7 +159,6 @@ class sqliteDatabase {
}
sql(query, params) {
//console.log(query);
return new Promise((resolve) => {
this.conn.all(query, params, (err, rows) => {
resolve(rows);
@ -163,10 +190,6 @@ class sqliteDatabase {
return row.length ? row[0][fieldname] : null;
}
escape(value) {
return frappe.sqlescape(value);
}
get_fields(meta) {
// add standard fields
let fields = frappe.model.standard_fields.slice();

View File

@ -18,6 +18,7 @@ module.exports = {
frappe.view.init({container: container});
frappe.router = new Router();
await frappe.login();
}
};

View File

@ -14,6 +14,10 @@ module.exports = {
return element;
},
remove(element) {
element.parentNode.removeChild(element);
},
add_class(element, className) {
if (element.classList) {
element.classList.add(className);

View File

@ -38,6 +38,7 @@ class BaseControl {
this.make_form_group();
this.make_label();
this.make_input();
this.set_input_name();
this.make_description();
this.bind_change_event();
}
@ -54,7 +55,10 @@ class BaseControl {
make_input() {
this.input = frappe.ui.add('input', 'form-control', this.form_group);
this.input.setAttribute('type', this.fieldname);
}
set_input_name() {
this.input.setAttribute('name', this.fieldname);
}
make_description() {
@ -65,6 +69,9 @@ class BaseControl {
}
set_input_value(value) {
if (value === undefined || value === null) {
value = '';
}
this.input.value = value;
}
@ -76,8 +83,12 @@ class BaseControl {
return value;
}
async validate(value) {
return value;
}
bind_change_event() {
this.input.addEventListener('change', () => this.handle_change(e));
this.input.addEventListener('change', (e) => this.handle_change(e));
}
async handle_change(e) {

View File

@ -1,5 +1,7 @@
const control_classes = {
Data: require('./data')
Data: require('./data'),
Text: require('./text'),
Select: require('./select')
}

View File

@ -0,0 +1,24 @@
const BaseControl = require('./base');
class SelectControl extends BaseControl {
make_input() {
this.input = frappe.ui.add('select', 'form-control', this.form_group);
let options = this.options;
if (typeof options==='string') {
options = options.split('\n');
}
for (let value of options) {
let option = frappe.ui.add('option', null, this.input);
option.textContent = value;
option.setAttribute('value', value);
}
}
make() {
super.make();
this.input.setAttribute('row', '3');
}
};
module.exports = SelectControl;

View File

@ -0,0 +1,13 @@
const BaseControl = require('./base');
class TextControl extends BaseControl {
make_input() {
this.input = frappe.ui.add('textarea', 'form-control', this.form_group);
}
make() {
super.make();
this.input.setAttribute('rows', '8');
}
};
module.exports = TextControl;

View File

@ -26,19 +26,33 @@ class Form {
}
make_submit() {
this.submit_btn = frappe.ui.add('button', 'btn btn-primary', this.body);
this.submit_btn = frappe.ui.add('button', 'btn btn-outline-primary', this.body);
this.submit_btn.setAttribute('type', 'submit');
this.submit_btn.textContent = this.submit_label;
this.submit_btn.addEventListener('click', (event) => {
this.submit();
event.preventDefault();
})
}
show_alert(message, type) {
this.alert = frappe.ui.add('div', `alert alert-${type}`, this.body);
this.alert.textContent = message;
}
clear_alert() {
if (this.alert) {
frappe.ui.remove(this.alert);
this.alert = null;
}
}
async use(doc, is_new = false) {
if (this.doc) {
// clear handlers of outgoing doc
this.doc.clear_handlers();
}
this.clear_alert();
this.doc = doc;
this.is_new = is_new;
for (let control of this.controls_list) {
@ -47,12 +61,17 @@ class Form {
}
async submit() {
if (this.is_new) {
await this.doc.insert();
} else {
await this.doc.update();
try {
if (this.is_new) {
await this.doc.insert();
} else {
await this.doc.update();
}
await this.refresh();
this.show_alert('Saved', 'success');
} catch (e) {
this.show_alert('Failed', 'danger');
}
await this.refresh();
}
refresh() {

View File

@ -8,7 +8,7 @@ class todo_meta extends frappe.meta.Meta {
}
get_row_html(data) {
return `<a href="#todo/${data.name}">${data.subject}</a>`;
return `<a href="#edit/todo/${data.name}">${data.subject}</a>`;
}
}

View File

@ -4,7 +4,7 @@ backends.sqllite = require('frappe-core/frappe/backends/sqlite');
const express = require('express');
const app = express();
const frappe = require('frappe-core');
const rest_server = require('frappe-core/frappe/server/rest_server')
const rest_api = require('./rest_api')
const models = require('frappe-core/frappe/server/models');
const common = require('frappe-core/frappe/common');
const bodyParser = require('body-parser');
@ -33,8 +33,12 @@ module.exports = {
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('./'));
app.use(function (err, req, res, next) {
console.error(err.stack);
res.status(500).send('Something broke!');
})
// routes
rest_server.setup(app);
rest_api.setup(app);
// listen
frappe.app = app;

61
frappe/server/rest_api.js Normal file
View File

@ -0,0 +1,61 @@
const frappe = require('frappe-core');
module.exports = {
setup(app) {
// get list
app.get('/api/resource/:doctype', frappe.async_handler(async function(request, response) {
let fields, filters;
for (key of ['fields', 'filters']) {
if (request.params[key]) {
request.params[key] = JSON.parse(request.params[key]);
}
}
let data = await frappe.db.get_all({
doctype: request.params.doctype,
fields: request.params.fields || ['name', 'subject'],
filters: request.params.filters,
start: request.params.start || 0,
limit: request.params.limit || 20,
order_by: request.params.order_by,
order: request.params.order
});
return response.json(data);
}));
// create
app.post('/api/resource/:doctype', frappe.async_handler(async function(request, response) {
data = request.body;
data.doctype = request.params.doctype;
let doc = await frappe.get_doc(data);
await doc.insert();
await frappe.db.commit();
return response.json(doc.get_valid_dict());
}));
// update
app.put('/api/resource/:doctype/:name', frappe.async_handler(async function(request, response) {
data = request.body;
let doc = await frappe.get_doc(request.params.doctype, request.params.name);
Object.assign(doc, data);
await doc.update();
await frappe.db.commit();
return response.json(doc.get_valid_dict());
}));
// get document
app.get('/api/resource/:doctype/:name', frappe.async_handler(async function(request, response) {
let doc = await frappe.get_doc(request.params.doctype, request.params.name);
return response.json(doc.get_valid_dict());
}));
// delete
app.delete('/api/resource/:doctype/:name', frappe.async_handler(async function(request, response) {
let doc = await frappe.get_doc(request.params.doctype, request.params.name)
await doc.delete();
return response.json({});
}));
}
};

View File

@ -1,82 +0,0 @@
const frappe = require('frappe-core');
module.exports = {
setup(app) {
// get list
app.get('/api/resource/:doctype', async function(request, response) {
try {
let fields, filters;
for (key of ['fields', 'filters']) {
if (request.params[key]) {
request.params[key] = JSON.parse(request.params[key]);
}
}
let data = await frappe.db.get_all({
doctype: request.params.doctype,
fields: request.params.fields || ['name', 'subject'],
filters: request.params.filters,
start: request.params.start || 0,
limit: request.params.limit || 20,
order_by: request.params.order_by,
order: request.params.order
});
return response.json(data);
} catch (e) {
throw e;
}
});
// create
app.post('/api/resource/:doctype', async function(request, response) {
try {
data = request.body;
data.doctype = request.params.doctype;
let doc = await frappe.get_doc(data);
await doc.insert();
await frappe.db.commit();
return response.json(doc.get_valid_dict());
} catch (e) {
throw e;
}
});
// update
app.put('/api/resource/:doctype/:name', async function(request, response) {
try {
data = request.body;
let doc = await frappe.get_doc(request.params.doctype, request.params.name);
Object.assign(doc, data);
await doc.update();
await frappe.db.commit();
return response.json(doc.get_valid_dict());
} catch (e) {
throw e;
}
});
// get document
app.get('/api/resource/:doctype/:name', async function(request, response) {
try {
let doc = await frappe.get_doc(request.params.doctype, request.params.name);
return response.json(doc.get_valid_dict());
} catch (e) {
throw e;
}
});
// delete
app.delete('/api/resource/:doctype/:name', async function(request, response) {
try {
let doc = await frappe.get_doc(request.params.doctype, request.params.name)
await doc.delete();
return response.json({});
} catch (e) {
throw e;
}
});
}
};

View File

@ -2,24 +2,12 @@ module.exports = {
slug(text) {
return text.toLowerCase().replace(/ /g, '_');
},
sqlescape(value) {
if (value===null || value===undefined) {
// null
return 'null';
} else if (value instanceof Date) {
// date
return `'${value.toISOString()}'`;
} else if (typeof value==='string') {
// text
return "'" + value.replace(/'/g, '\'').replace(/"/g, '\"') + "'";
} else {
// number
return value + '';
}
async_handler(fn) {
return (req, res, next) => Promise.resolve(fn(req, res, next))
.catch(next);
},
async sleep(seconds) {
return new Promise(resolve => {
setTimeout(resolve, seconds * 1000);