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

added reportpage.js

This commit is contained in:
Rushabh Mehta 2018-03-26 17:48:07 +05:30
parent 3646e7d778
commit a91489cfbc
15 changed files with 168 additions and 57 deletions

View File

@ -38,7 +38,7 @@ module.exports = class HTTPClient extends Observable {
async getAll({ doctype, fields, filters, start, limit, sort_by, order }) {
let url = this.getURL('/api/resource', doctype);
url = url + "?" + this.getQueryString({
url = url + "?" + frappe.getQueryString({
fields: JSON.stringify(fields),
filters: JSON.stringify(filters),
start: start,
@ -107,13 +107,6 @@ module.exports = class HTTPClient extends Observable {
return this.protocol + '://' + this.server + parts.join('/');
}
getQueryString(params) {
return Object.keys(params)
.map(k => params[k] != null ? encodeURIComponent(k) + '=' + encodeURIComponent(params[k]) : null)
.filter(v => v)
.join('&');
}
getHeaders() {
return {
'Accept': 'application/json',

View File

@ -53,7 +53,7 @@ module.exports = class mysqlDatabase extends Database{
updateColumnDefinition(df, columns, indexes) {
columns.push(`${df.fieldname} ${this.typeMap[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}`);
columns.push(`${df.fieldname} ${this.typeMap[df.fieldtype]} ${df.required && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}`);
}
async getTableColumns(doctype) {

View File

@ -78,7 +78,7 @@ module.exports = class sqliteDatabase extends Database {
if (field.fieldname==='name') {
def += ' PRIMARY KEY NOT NULL';
}
else if (field.reqd) {
else if (field.required) {
def += ' NOT NULL';
}
if (field.default) {

View File

@ -131,7 +131,6 @@ module.exports = class Desk {
if (!this.pages[view]) this.pages[view] = {};
if (!this.pages[view][doctype]) this.pages[view][doctype] = new views[view](doctype);
const page = this.pages[view][doctype];
this.toggleCenter(page.fullPage ? false : true);
await page.show(params);
}

86
client/desk/reportpage.js Normal file
View File

@ -0,0 +1,86 @@
const Page = require('frappejs/client/view/page');
const controls = require('frappejs/client/view/controls');
const DataTable = require('frappe-datatable');
const frappe = require('frappejs');
const utils = require('frappejs/client/ui/utils');
// baseclass for report
// `url` url for report
// `getColumns` return columns
module.exports = class ReportPage extends Page {
constructor({title, }) {
super({title: title, hasRoute: true});
this.fullPage = true;
this.filterWrapper = frappe.ui.add('div', 'filter-toolbar form-inline', this.body);
this.tableWrapper = frappe.ui.add('div', 'table-page-wrapper', this.body);
this.btnNew = this.addButton(frappe._('Refresh'), 'btn-primary', async () => {
await this.run();
});
this.filters = {};
}
getColumns() {
// overrride
}
addFilter(field) {
if (field.fieldname) {
field.fieldname = frappe.slug(field.label);
}
field.placeholder = field.label;
field.inline = true;
this.filters[field.fieldname] = controls.makeControl({field: field, form: this, parent: this.filterWrapper});
return this.filters[field.fieldname];
}
getFilterValues() {
const values = {};
for (let fieldname in this.filters) {
let control = this.filters[fieldname];
values[fieldname] = control.getInputValue();
if (control.required && !values[fieldname]) {
frappe.ui.showAlert({message: frappe._('{0} is mandatory', control.label), color: 'red'});
return false;
}
}
return values;
}
async show(params) {
super.show();
await this.run();
}
async run() {
if (!this.datatable) {
this.makeDataTable();
}
const filterValues = this.getFilterValues();
if (filterValues === false) return;
let response = await fetch(this.url + '?' + frappe.getQueryString(filterValues), {
method: 'GET',
headers: {
Accept: 'application/json'
}
})
const data = await response.json();
this.datatable.refresh(data);
}
makeDataTable() {
this.datatable = new DataTable(this.tableWrapper, {
columns: utils.convertFieldsToDatatableColumns(this.getColumns(), this.layout),
data: [],
layout: this.layout || 'fluid',
});
}
}

View File

@ -68,7 +68,7 @@ html {
margin-bottom: $spacer-2;
.form-check-input {
margin-top: 0.25rem;
margin-top: $spacer-1;
}
.form-check-label {
margin-left: $spacer-1;
@ -84,6 +84,12 @@ html {
}
}
.form-inline {
.form-group {
margin-right: $spacer-2;
}
}
.list-search {
padding: $spacer-3 $spacer-4;
}

View File

@ -2,6 +2,7 @@ const frappe = require('frappejs');
const DataTable = require('frappe-datatable');
const controls = require('frappejs/client/view/controls');
const Modal = require('frappejs/client/ui/modal');
const utils = require('./utils');
module.exports = class ModelTable {
constructor({doctype, parent, layout, parentControl, getRowData,
@ -26,27 +27,7 @@ module.exports = class ModelTable {
}
getColumns() {
return this.getTableFields().map(field => {
if (!field.width) {
if (this.layout==='ratio') {
field.width = 1;
} else if (this.layout==='fixed') {
field.width = 120;
}
}
return {
id: field.fieldname,
field: field,
content: field.label,
editable: true,
sortable: false,
resizable: true,
dropdown: false,
width: field.width,
align: ['Int', 'Float', 'Currency'].includes(field.fieldtype) ? 'right' : 'left',
format: (value) => frappe.format(value, field)
}
});
return utils.convertFieldsToDatatableColumns(this.getTableFields(), this.layout);
}
getTableFields() {

28
client/ui/utils.js Normal file
View File

@ -0,0 +1,28 @@
const frappe = require('frappejs');
module.exports = {
convertFieldsToDatatableColumns(fields, layout = 'fixed') {
return fields.map(field => {
if (!field.width) {
if (layout==='ratio') {
field.width = 1;
} else if (layout==='fixed') {
field.width = 120;
}
}
return {
id: field.fieldname || frappe.slug(field.label),
field: field,
content: field.label,
editable: true,
sortable: false,
resizable: true,
dropdown: false,
width: field.width,
align: ['Int', 'Float', 'Currency'].includes(field.fieldtype) ? 'right' : 'left',
format: (value) => frappe.format(value, field)
}
});
}
}

View File

@ -70,6 +70,9 @@ class BaseControl {
makeLabel(labelClass = null) {
this.labelElement = frappe.ui.add('label', labelClass, this.inputContainer, this.label);
this.labelElement.setAttribute('for', this.id);
if (this.inline) {
this.labelElement.classList.add("sr-only");
}
}
makeInput(inputClass='form-control') {
@ -83,6 +86,9 @@ class BaseControl {
if (!this.onlyInput) {
this.makeDescription();
}
if (this.placeholder) {
this.input.setAttribute('placeholder', this.placeholder);
}
}

View File

@ -85,6 +85,8 @@ module.exports = class Page extends Observable {
}
this.parent.activePage = this;
frappe.desk.toggleCenter(this.fullPage ? false : true);
}
renderError(title, message) {

View File

@ -1,7 +1,7 @@
module.exports = {
input: './src/index.js',
input: './www/index.js',
output: {
file: './dist/js/bundle.js',
file: './www/dist/js/bundle.js',
format: 'iife',
name: 'desk',
globals: ['io', 'nunjucks'] // for socketio client, which is imported directly

View File

@ -1,7 +1,7 @@
module.exports = {
input: './node_modules/frappejs/client/style/style.scss',
output: {
file: './dist/css/style.css',
file: './www/dist/css/style.css',
format: 'cjs'
},
plugins: [

View File

@ -18,7 +18,7 @@ require.extensions['.html'] = function (module, filename) {
};
module.exports = {
async start({backend, connectionParams, models}) {
async start({backend, connectionParams, models, staticPath = './'}) {
await this.init();
if (models) {
@ -31,7 +31,7 @@ module.exports = {
// app
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static('./'));
app.use(express.static(staticPath));
// socketio
io.on('connection', function (socket) {

View File

@ -1,18 +1,9 @@
const frappe = require('frappejs');
module.exports = {
asyncHandler(fn) {
return (req, res, next) => Promise.resolve(fn(req, res, next))
.catch((err) => {
console.log(err);
// handle error
res.status(err.status_code || 500).send({error: err.message});
});
},
setup(app) {
// get list
app.get('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
app.get('/api/resource/:doctype', frappe.asyncHandler(async function(request, response) {
for (let key of ['fields', 'filters']) {
if (request.query[key]) {
request.query[key] = JSON.parse(request.query[key]);
@ -33,7 +24,7 @@ module.exports = {
}));
// create
app.post('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
app.post('/api/resource/:doctype', frappe.asyncHandler(async function(request, response) {
let data = request.body;
data.doctype = request.params.doctype;
let doc = frappe.newDoc(data);
@ -43,7 +34,7 @@ module.exports = {
}));
// update
app.put('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
app.put('/api/resource/:doctype/:name', frappe.asyncHandler(async function(request, response) {
let data = request.body;
let doc = await frappe.getDoc(request.params.doctype, request.params.name);
Object.assign(doc, data);
@ -54,26 +45,26 @@ module.exports = {
// get document
app.get('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
app.get('/api/resource/:doctype/:name', frappe.asyncHandler(async function(request, response) {
let doc = await frappe.getDoc(request.params.doctype, request.params.name);
return response.json(doc.getValidDict());
}));
// get value
app.get('/api/resource/:doctype/:name/:fieldname', this.asyncHandler(async function(request, response) {
app.get('/api/resource/:doctype/:name/:fieldname', frappe.asyncHandler(async function(request, response) {
let value = await frappe.db.getValue(request.params.doctype, request.params.name, request.params.fieldname);
return response.json({value: value});
}));
// delete
app.delete('/api/resource/:doctype/:name', this.asyncHandler(async function(request, response) {
app.delete('/api/resource/:doctype/:name', frappe.asyncHandler(async function(request, response) {
let doc = await frappe.getDoc(request.params.doctype, request.params.name)
await doc.delete();
return response.json({});
}));
// delete many
app.delete('/api/resource/:doctype', this.asyncHandler(async function(request, response) {
app.delete('/api/resource/:doctype', frappe.asyncHandler(async function(request, response) {
let names = request.body;
for (let name of names) {
let doc = await frappe.getDoc(request.params.doctype, name);

View File

@ -4,8 +4,10 @@ Array.prototype.equals = function( array ) {
}
module.exports = {
slug(text) {
return text.toLowerCase().replace(/ /g, '_');
slug(str) {
return str.replace(/(?:^\w|[A-Z]|\b\w)/g, function(letter, index) {
return index == 0 ? letter.toLowerCase() : letter.toUpperCase();
}).replace(/\s+/g, '');
},
getRandomString() {
@ -42,6 +44,23 @@ module.exports = {
: match;
}
});
}
},
getQueryString(params) {
if (!params) return '';
return Object.keys(params)
.map(k => params[k] != null ? encodeURIComponent(k) + '=' + encodeURIComponent(params[k]) : null)
.filter(v => v)
.join('&');
},
asyncHandler(fn) {
return (req, res, next) => Promise.resolve(fn(req, res, next))
.catch((err) => {
console.log(err);
// handle error
res.status(err.status_code || 500).send({error: err.message});
});
},
};