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

print formats, momentjs, markdown, nunjucks

This commit is contained in:
Rushabh Mehta 2018-02-21 15:08:56 +05:30
parent eac8bb3051
commit f25c4ab81b
20 changed files with 289 additions and 78 deletions

View File

@ -19,7 +19,8 @@
"describe": true,
"before": true,
"after": true,
"it": true
"it": true,
"io": true
},
"rules": {

View File

@ -8,6 +8,7 @@ module.exports = class FormModal extends Modal {
}
async showWith(doctype, name) {
if (!name) name = doctype;
this.show();
await this.setDoc(doctype, name);
}
@ -17,7 +18,8 @@ module.exports = class FormModal extends Modal {
this.makeForm();
}
await this.form.setDoc(doctype, name);
this.modal.querySelector('input').focus();
let input = this.modal.querySelector('input') || this.modal.querySelector('select');
input && input.focus();
}
makeForm() {

View File

@ -13,14 +13,11 @@ module.exports = class FormPage extends Page {
doctype: doctype,
parent: this.body,
container: this,
actions: ['submit', 'delete', 'duplicate', 'settings']
actions: ['submit', 'delete', 'duplicate', 'settings', 'print']
});
this.on('show', async (params) => {
await this.showDoc(params.doctype, params.name);
if (frappe.desk.center && !frappe.desk.center.activePage) {
frappe.desk.showListPage(doctype);
}
});
// if name is different after saving, change the route
@ -41,15 +38,9 @@ module.exports = class FormPage extends Page {
async showDoc(doctype, name) {
try {
await this.form.setDoc(doctype, name);
this.setActiveListRow(name);
frappe.desk.setActiveDoc(this.form.doc);
} catch (e) {
this.renderError(e.status_code, e.message);
}
}
setActiveListRow(name) {
if (frappe.desk.pages.lists[this.doctype]) {
frappe.desk.pages.lists[this.doctype].list.setActiveListRow(name);
}
}
}

View File

@ -2,11 +2,13 @@ const frappe = require('frappejs');
// const Search = require('./search');
const Router = require('frappejs/common/router');
const Page = require('frappejs/client/view/page');
const FormPage = require('frappejs/client/desk/formpage');
const ListPage = require('frappejs/client/desk/listpage');
// const Navbar = require('./navbar');
const views = {};
views.Form = require('frappejs/client/desk/formpage');
views.List = require('frappejs/client/desk/listpage');
views.Print = require('frappejs/client/desk/printpage');
views.FormModal = require('frappejs/client/desk/formmodal');
const DeskMenu = require('./menu');
const FormModal = require('frappejs/client/desk/formmodal');
module.exports = class Desk {
constructor(columns=2) {
@ -20,9 +22,8 @@ module.exports = class Desk {
this.makeColumns(columns);
this.pages = {
lists: {},
forms: {},
formModals: {}
formModals: {},
List: {}
};
this.routeItems = {};
@ -69,12 +70,15 @@ module.exports = class Desk {
})
frappe.router.add('list/:doctype', async (params) => {
await this.showListPage(params.doctype);
await this.showViewPage('List', params.doctype);
});
frappe.router.add('edit/:doctype/:name', async (params) => {
let page = this.getFormPage(params.doctype);
await page.show(params);
await this.showViewPage('Form', params.doctype, params);
})
frappe.router.add('print/:doctype/:name', async (params) => {
await this.showViewPage('Print', params.doctype, params);
})
frappe.router.add('new/:doctype', async (params) => {
@ -94,29 +98,31 @@ module.exports = class Desk {
}
async showListPage(doctype) {
if (!this.pages.lists[doctype]) {
this.pages.lists[doctype] = new ListPage(doctype);
}
await this.pages.lists[doctype].show(doctype);
return this.pages.lists[doctype];
}
getFormPage(doctype) {
if (!this.pages.forms[doctype]) {
this.pages.forms[doctype] = new FormPage(doctype);
}
return this.pages.forms[doctype];
async showViewPage(view, doctype, params) {
if (!params) params = doctype;
if (!this.pages[view]) this.pages[view] = {};
if (!this.pages[view][doctype]) this.pages[view][doctype] = new views[view](doctype);
await this.pages[view][doctype].show(params);
}
async showFormModal(doctype, name) {
if (!this.pages.formModals[doctype]) {
this.pages.formModals[doctype] = new FormModal(doctype);
this.pages.formModals[doctype] = new views.FormModal(doctype);
}
await this.pages.formModals[doctype].showWith(doctype, name);
return this.pages.formModals[doctype];
}
async setActiveDoc(doc) {
this.activeDoc = doc;
if (frappe.desk.center && !frappe.desk.center.activePage) {
await frappe.desk.showViewPage('List', doc.doctype);
}
if (frappe.desk.pages.List[doc.doctype]) {
frappe.desk.pages.List[doc.doctype].list.setActiveListRow(doc.name);
}
}
setActive(item) {
let className = 'list-group-item-secondary';
let activeItem = this.sidebarList.querySelector('.' + className);

View File

@ -0,0 +1,43 @@
const Page = require('frappejs/client/view/page');
const frappe = require('frappejs');
const nunjucks = require('nunjucks/browser/nunjucks');
nunjucks.configure({ autoescape: false });
module.exports = class PrintPage extends Page {
constructor(doctype) {
let meta = frappe.getMeta(doctype);
super({title: `View ${meta.name}`});
this.meta = meta;
this.doctype = doctype;
this.on('show', async (params) => {
this.name = params.name;
if (this.meta.print) {
// render
this.renderTemplate();
} else {
this.renderError('No Print Settings');
}
});
this.addButton(frappe._('Edit'), 'primary', () => {
frappe.router.setRoute('edit', this.doctype, this.name)
});
}
async renderTemplate() {
this.printFormat = await frappe.getDoc('PrintFormat', this.meta.print.printFormat);
let doc = await frappe.getDoc(this.doctype, this.name);
let context = {doc: doc, frappe: frappe};
frappe.desk.setActiveDoc(doc);
try {
this.body.innerHTML = `<div class="print-page">${nunjucks.renderString(this.printFormat.template, context)}</div>`;
} catch (e) {
this.renderError('Template Error', e);
throw e;
}
}
}

View File

@ -17,7 +17,9 @@ module.exports = {
this.socket = io.connect('http://localhost:8000'); // eslint-disable-line
frappe.db.bindSocketClient(this.socket);
frappe.flags.cache_docs = true;
frappe.flags.cacheDocs = true;
await frappe.getSingle('SystemSettings');
frappe.desk = new Desk(columns);
await frappe.login();

View File

@ -9,6 +9,7 @@ $spacer-1: 0.25rem;
$spacer-2: 0.5rem;
$spacer-3: 1rem;
$spacer-4: 2rem;
$spacer-5: 3rem;
$page-width: 500px;
@ -192,3 +193,12 @@ mark {
}
}
.print-page {
padding: $spacer-5;
line-height: 1.8;
td, th {
padding: $spacer-2;
}
}

View File

@ -1,5 +1,5 @@
const $ = require('jquery');
const bootstrap = require('bootstrap');
const bootstrap = require('bootstrap'); // eslint-disable-line
const Observable = require('frappejs/utils/observable');
module.exports = class Modal extends Observable {

View File

@ -1,14 +1,31 @@
const flatpickr = require('flatpickr');
const BaseControl = require('./base');
const frappe = require('frappejs');
class DateControl extends BaseControl {
make() {
let dateFormat = {
'yyyy-mm-dd': 'Y-m-d',
'dd/mm/yyyy': 'd/m/Y',
'dd-mm-yyyy': 'd-m-Y',
'mm/dd/yyyy': 'm/d/Y',
'mm-dd-yyyy': 'm-d-Y'
}
let altFormat = dateFormat[frappe.SystemSettings.dateFormat];
super.make();
this.input.setAttribute('type', 'text');
flatpickr.default(this.input, {
dateFormat: "Y-m-d"
this.flatpickr = flatpickr.default(this.input, {
altInput: true,
altFormat: altFormat,
dateFormat:'Y-m-d'
});
}
setInputValue(value) {
super.setInputValue(value);
this.flatpickr.setDate(value);
}
};
module.exports = DateControl;

View File

@ -89,7 +89,7 @@ class TableControl extends BaseControl {
control.parentControl = this;
control.doc = this.doc[this.fieldname][rowIndex];
control.set_focus();
return control.setInputValue(value);
return control.setInputValue(control.doc[column.id]);
},
setValue: async (value, rowIndex, column) => {
control.handleChange();

View File

@ -49,6 +49,13 @@ module.exports = class BaseForm extends Observable {
})
}
if (this.meta.print && this.actions.includes('print')) {
let menu = this.container.getDropdown(frappe._('Menu'));
menu.addItem(frappe._("Print"), async (e) => {
await frappe.router.setRoute('print', this.doctype, this.doc.name);
});
}
if (!this.meta.isSingle && this.actions.includes('delete')) {
let menu = this.container.getDropdown(frappe._('Menu'));
menu.addItem(frappe._("Delete"), async (e) => {

View File

@ -124,7 +124,11 @@ module.exports = class BaseList {
}
async showItem(name) {
await frappe.router.setRoute('edit', this.doctype, name);
if (this.meta.print) {
await frappe.router.setRoute('print', this.doctype, name);
} else {
await frappe.router.setRoute('edit', this.doctype, name);
}
}
getCheckedRowNames() {
@ -188,14 +192,29 @@ module.exports = class BaseList {
}
bindKeys() {
keyboard.bindKey(this.body, 'up', async (e) => await this.move('up'));
keyboard.bindKey(this.body, 'down', async (e) => await this.move('down'))
keyboard.bindKey(this.body, 'up', () => this.move('up'));
keyboard.bindKey(this.body, 'down', () => this.move('down'))
keyboard.bindKey(this.body, 'right', () => {
if (frappe.desk.body.activePage) {
frappe.desk.body.activePage.body.querySelector('input').focus();
}
})
});
keyboard.bindKey(this.body, 'n', (e) => {
frappe.router.setRoute('new', this.doctype);
e.preventDefault();
});
keyboard.bindKey(this.body, 'x', async (e) => {
let activeListRow = this.getActiveListRow();
if (activeListRow && activeListRow.docName) {
e.preventDefault();
await frappe.db.delete(this.doctype, activeListRow.docName);
frappe.desk.body.activePage.hide();
}
});
}
async move(direction) {
@ -225,15 +244,14 @@ module.exports = class BaseList {
}
setActiveListRow(name) {
let activeListRow = this.body.querySelector('.list-row.active');
let activeListRow = this.getActiveListRow();
if (activeListRow) {
activeListRow.classList.remove('active');
}
if (!name) {
// get name from active page
name = frappe.desk.body.activePage && frappe.desk.body.activePage.form.doc
&& frappe.desk.body.activePage.form.doc.name;
name = frappe.desk.activeDoc && frappe.desk.activeDoc.name;
}
if (name) {
@ -245,5 +263,7 @@ module.exports = class BaseList {
}
}
getActiveListRow() {
return this.body.querySelector('.list-row.active');
}
};

View File

@ -4,7 +4,7 @@ module.exports = {
file: './dist/js/bundle.js',
format: 'iife',
name: 'desk',
globals: ['io'] // for socketio client, which is imported directly
globals: ['io', 'nunjucks'] // for socketio client, which is imported directly
},
plugins: [
require('rollup-plugin-commonjs')(),

View File

@ -21,7 +21,7 @@ module.exports = {
this.views = {};
this.docs = {};
this.flags = {
cache_docs: false
cacheDocs: false
}
},
@ -40,7 +40,7 @@ module.exports = {
},
addToCache(doc) {
if (!this.flags.cache_docs) return;
if (!this.flags.cacheDocs) return;
// add to `docs` cache
if (doc.doctype && doc.name) {
@ -48,6 +48,11 @@ module.exports = {
this.docs[doc.doctype] = {};
}
this.docs[doc.doctype][doc.name] = doc;
// singles available as first level objects too
if (doc.doctype === doc.name) {
this[doc.name] = doc;
}
}
},

View File

@ -75,10 +75,10 @@ module.exports = class BaseDocument extends Observable {
setDefaults() {
for (let field of this.meta.fields) {
if (field.fieldtype === 'Date') {
this[field.fieldname] = (new Date()).toISOString().substr(0, 10);
} else if (!this[field.fieldname]) {
if(field.default) {
if (this[field.fieldname]===null || this[field.fieldname]===undefined) {
if (field.fieldtype === 'Date') {
this[field.fieldname] = (new Date()).toISOString().substr(0, 10);
} else if(field.default) {
this[field.fieldname] = field.default;
}
}
@ -141,6 +141,9 @@ module.exports = class BaseDocument extends Observable {
let data = await frappe.db.get(this.doctype, this.name);
if (data.name) {
this.syncValues(data);
if (this.meta.isSingle) {
this.setDefaults();
}
} else {
throw new frappe.errors.NotFound(`Not Found: ${this.doctype} ${this.name}`);
}

View File

@ -1,5 +1,6 @@
module.exports = {
name: "PrintFormat",
label: "Print Format",
doctype: "DocType",
isSingle: 0,
isChild: 0,

View File

@ -1,21 +1,24 @@
module.exports = {
"name": "SystemSettings",
"doctype": "DocType",
"isSingle": 1,
"isChild": 0,
"keywordFields": [],
"fields": [
name: "SystemSettings",
label: "System Settings",
doctype: "DocType",
isSingle: 1,
isChild: 0,
keywordFields: [],
fields: [
{
"fieldname": "dateFormat",
"label": "Date Format",
"fieldtype": "Select",
"options": [
fieldname: "dateFormat",
label: "Date Format",
fieldtype: "Select",
options: [
"dd/mm/yyyy",
"mm/dd/yyyy",
"dd-mm-yyyy",
"mm-dd-yyyy"
"mm-dd-yyyy",
"yyyy-mm-dd"
],
"required": 1
default: "yyyy-mm-dd",
required: 1
}
]
}

View File

@ -12,23 +12,21 @@
"awesomplete": "^1.1.2",
"body-parser": "^1.18.2",
"bootstrap": "^4.0.0",
"bufferutil": "^3.0.3",
"clusterize.js": "^0.18.0",
"codemirror": "^5.35.0",
"commander": "^2.13.0",
"express": "^4.16.2",
"flatpickr": "^4.3.2",
"jquery": "^3.3.1",
"moment": "^2.20.1",
"mysql": "^2.15.0",
"node-fetch": "^1.7.3",
"nunjucks": "^3.1.0",
"popper.js": "^1.12.9",
"rollup-plugin-ignore": "^1.0.3",
"showdown": "^1.8.6",
"socket.io": "^2.0.4",
"socket.io-client": "^2.0.4",
"sortablejs": "^1.7.0",
"sqlite3": "^3.1.13",
"utf-8-validate": "^4.0.0",
"walk": "^2.3.9"
},
"repository": {

View File

@ -1,13 +1,23 @@
const number_format = require('./number_format');
const markdown = new (require('showdown').Converter)();
const moment = require('moment');
const frappe = require('frappejs');
module.exports = {
format(value, field) {
if (typeof field === 'string') {
field = {fieldtype: field};
}
if (field.fieldtype==='Currency') {
value = number_format.format_number(value);
} else if (field.fieldtype==='Date') {
if (value instanceof Date) {
value = value.toISOString().substr(0, 10);
}
} else if (field.fieldtype === 'Text') {
value = markdown.makeHtml(value);
} else if (field.fieldtype === 'Date') {
value = moment(value).format(frappe.SystemSettings.dateFormat.toUpperCase());
} else {
if (value===null || value===undefined) {
value = '';

View File

@ -533,7 +533,7 @@ camelcase@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
camelcase@^4.0.0:
camelcase@^4.0.0, camelcase@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
@ -646,6 +646,14 @@ cliui@^3.0.3, cliui@^3.2.0:
strip-ansi "^3.0.1"
wrap-ansi "^2.0.0"
cliui@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/cliui/-/cliui-4.0.0.tgz#743d4650e05f36d1ed2575b59638d87322bfbbcc"
dependencies:
string-width "^2.1.1"
strip-ansi "^4.0.0"
wrap-ansi "^2.0.0"
clone@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f"
@ -1478,6 +1486,12 @@ find-up@^1.0.0:
path-exists "^2.0.0"
pinkie-promise "^2.0.0"
find-up@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
dependencies:
locate-path "^2.0.0"
flat-cache@^1.2.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481"
@ -2436,6 +2450,13 @@ loader-utils@^0.2.16:
json5 "^0.5.0"
object-assign "^4.0.1"
locate-path@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
dependencies:
p-locate "^2.0.0"
path-exists "^3.0.0"
lodash.assign@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7"
@ -2527,6 +2548,12 @@ media-typer@0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
mem@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76"
dependencies:
mimic-fn "^1.0.0"
meow@^3.7.0:
version "3.7.0"
resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb"
@ -2636,6 +2663,10 @@ mocha@^4.1.0:
mkdirp "0.5.1"
supports-color "4.4.0"
moment@^2.20.1:
version "2.20.1"
resolved "https://registry.yarnpkg.com/moment/-/moment-2.20.1.tgz#d6eb1a46cbcc14a2b2f9434112c1ff8907f313fd"
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
@ -2912,6 +2943,14 @@ os-locale@^1.4.0:
dependencies:
lcid "^1.0.0"
os-locale@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2"
dependencies:
execa "^0.7.0"
lcid "^1.0.0"
mem "^1.1.0"
os-tmpdir@^1.0.0, os-tmpdir@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
@ -2927,6 +2966,22 @@ p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
p-limit@^1.1.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c"
dependencies:
p-try "^1.0.0"
p-locate@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
dependencies:
p-limit "^1.1.0"
p-try@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
package-json@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/package-json/-/package-json-4.0.1.tgz#8869a0401253661c4c4ca3da6c2121ed555f5eed"
@ -2983,6 +3038,10 @@ path-exists@^2.0.0:
dependencies:
pinkie-promise "^2.0.0"
path-exists@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
@ -4190,6 +4249,12 @@ shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
showdown@^1.8.6:
version "1.8.6"
resolved "https://registry.yarnpkg.com/showdown/-/showdown-1.8.6.tgz#91ea4ee3b7a5448aaca6820a4e27e690c6ad771c"
dependencies:
yargs "^10.0.3"
signal-exit@^3.0.0, signal-exit@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
@ -4749,6 +4814,10 @@ which-module@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
which-module@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a"
which@1, which@^1.2.9:
version "1.3.0"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a"
@ -4855,6 +4924,29 @@ yargs-parser@^5.0.0:
dependencies:
camelcase "^3.0.0"
yargs-parser@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950"
dependencies:
camelcase "^4.1.0"
yargs@^10.0.3:
version "10.1.2"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5"
dependencies:
cliui "^4.0.0"
decamelize "^1.1.1"
find-up "^2.1.0"
get-caller-file "^1.0.1"
os-locale "^2.0.0"
require-directory "^2.1.1"
require-main-filename "^1.0.1"
set-blocking "^2.0.0"
string-width "^2.0.0"
which-module "^2.0.0"
y18n "^3.2.1"
yargs-parser "^8.1.0"
yargs@^3.32.0:
version "3.32.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995"