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:
parent
3646e7d778
commit
a91489cfbc
@ -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',
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
86
client/desk/reportpage.js
Normal 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',
|
||||
});
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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
28
client/ui/utils.js
Normal 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)
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,8 @@ module.exports = class Page extends Observable {
|
||||
}
|
||||
|
||||
this.parent.activePage = this;
|
||||
|
||||
frappe.desk.toggleCenter(this.fullPage ? false : true);
|
||||
}
|
||||
|
||||
renderError(title, message) {
|
||||
|
@ -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
|
||||
|
@ -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: [
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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});
|
||||
});
|
||||
},
|
||||
|
||||
};
|
Loading…
Reference in New Issue
Block a user