0
1
Fork 0

refs #3971 import() forms, refactor, async, retrocompatibility

This commit is contained in:
Juan Ferrer 2022-11-17 23:31:48 +01:00
parent 0c7476a37c
commit 6ecce8fec6
21 changed files with 3667 additions and 264 deletions

29
app.js
View File

@ -1,6 +1,7 @@
__webpack_public_path__ = _PUBLIC_PATH; __webpack_public_path__ = _PUBLIC_PATH;
require('hedera/hedera'); import 'promise-polyfill/src/polyfill';
import 'hedera/hedera';
const locales = require('./import').locales; const locales = require('./import').locales;
const packageJson = require('./package.json'); const packageJson = require('./package.json');
@ -8,31 +9,33 @@ window.onload = function() {
loadLocale(main); loadLocale(main);
} }
function main(req) { function main() {
if (req) onLocaleLoad(req);
Vn.setVersion(packageJson.version); Vn.setVersion(packageJson.version);
const hederaWeb = new Hedera.App();
hederaWeb = new Hedera.App(); window.hederaWeb = hederaWeb;
hederaWeb.run(); hederaWeb.run();
} }
function loadLocale(cb) { function loadLocale(callback) {
Vn.Locale.init(); Vn.Locale.init();
var lang = Vn.Locale.language; var lang = Vn.Locale.language;
var req = require.context('js', true, /locale\/en.yml$/); var req = require.context('js', true, /locale\/en.yml$/);
onLocaleLoad(req); onLocaleLoad(Vn.Locale.fallbackLang, req);
const fn = locales[lang]; const loadFn = locales[lang];
if (fn) if (loadFn)
fn(cb); loadFn(function(req) {
onLocaleLoad(lang, req);
callback();
});
else else
cb(); callback();
} }
function onLocaleLoad(req) { function onLocaleLoad(lang, req) {
var keys = req.keys(); var keys = req.keys();
for (var i = 0; i < keys.length; i++) for (var i = 0; i < keys.length; i++)
Vn.Locale.add(req(keys[i])); Vn.Locale.add(req(keys[i]), lang);
} }

View File

@ -27,8 +27,7 @@ export default new Class({
this.$.oldPassword.focus(); this.$.oldPassword.focus();
} }
,onPassModifyClick() { ,async onPassModifyClick() {
try {
var oldPassword = this.$.oldPassword.value; var oldPassword = this.$.oldPassword.value;
var newPassword = this.$.newPassword.value; var newPassword = this.$.newPassword.value;
var repeatedPassword = this.$.repeatPassword.value; var repeatedPassword = this.$.repeatPassword.value;
@ -46,15 +45,16 @@ export default new Class({
this.conn.send('user/restore-password', params, this.conn.send('user/restore-password', params,
this._onPassChange.bind(this)); this._onPassChange.bind(this));
} else { } else {
try {
let userId = this.gui.user.id; let userId = this.gui.user.id;
params.oldPassword = oldPassword; params.oldPassword = oldPassword;
this.conn.lbSend('PATCH', await this.conn.lbSend('PATCH',
`Accounts/${userId}/changePassword`, params, `Accounts/${userId}/changePassword`, params,
this._onPassChange.bind(this)
); );
this._onPassChange();
} catch(err) {
this._onPassChange(null, err);
} }
} catch (e) {
Htk.Toast.showError(e.message);
} }
} }

View File

@ -22,6 +22,7 @@ export default new Class({
}, },
onConfigureClick() { onConfigureClick() {
test();
Htk.Toast.showWarning(_('RememberReconfiguringImpact')); Htk.Toast.showWarning(_('RememberReconfiguringImpact'));
this.hash.setAll({form: 'ecomerce/checkout'}); this.hash.setAll({form: 'ecomerce/checkout'});
}, },

View File

@ -1,5 +1,6 @@
import './style.scss';
export default new Class({ const Catalog = new Class({
Extends: Hedera.Form, Extends: Hedera.Form,
Template: require('./ui.xml') Template: require('./ui.xml')
@ -30,7 +31,7 @@ export default new Class({
if (localStorage.getItem('hederaView')) if (localStorage.getItem('hederaView'))
this.setView(parseInt(localStorage.getItem('hederaView'))); this.setView(parseInt(localStorage.getItem('hederaView')));
else else
this.setView(Hedera.Catalog.View.GRID); this.setView(Catalog.View.GRID);
this.onFilterChange(); this.onFilterChange();
} }
@ -144,19 +145,19 @@ export default new Class({
} }
,setView(view) { ,setView(view) {
if (view === Hedera.Catalog.View.GRID) { if (view === Catalog.View.GRID) {
this.$.viewButton.setProperties({ this.$.viewButton.setProperties({
icon: 'view_list', icon: 'view_list',
tip: _('List view') tip: _('List view')
}); });
this.view = Hedera.Catalog.View.GRID; this.view = Catalog.View.GRID;
var className = 'grid-view'; var className = 'grid-view';
} else { } else {
this.$.viewButton.setProperties({ this.$.viewButton.setProperties({
icon: 'grid_on', icon: 'grid_on',
tip: _('Grid view') tip: _('Grid view')
}); });
this.view = Hedera.Catalog.View.LIST; this.view = Catalog.View.LIST;
var className = 'list-view'; var className = 'list-view';
} }
@ -166,8 +167,8 @@ export default new Class({
} }
,onSwitchViewClick() { ,onSwitchViewClick() {
this.setView(this.view === Hedera.Catalog.View.LIST ? this.setView(this.view === Catalog.View.LIST ?
Hedera.Catalog.View.GRID : Hedera.Catalog.View.LIST); Catalog.View.GRID : Catalog.View.LIST);
} }
,onItemsChange(model, status) { ,onItemsChange(model, status) {
@ -321,9 +322,11 @@ export default new Class({
} }
}); });
Hedera.Catalog.extend({ Catalog.extend({
View: { View: {
LIST: 0, LIST: 0,
GRID: 1 GRID: 1
} }
}); });
export default Catalog;

View File

@ -152,7 +152,7 @@
on-click="this.onAddItemClick($event, $iter)"> on-click="this.onAddItemClick($event, $iter)">
<htk-image <htk-image
directory="catalog" directory="catalog"
subdir="500x500" subdir="200x200"
form="$iter" form="$iter"
column="image" column="image"
stamp-column="updated" stamp-column="updated"

View File

@ -64,6 +64,7 @@ export default new Class({
} }
var methods = []; var methods = [];
let selectedMethod;
if (totalDebt <= 0) { if (totalDebt <= 0) {
methods = ['balance']; methods = ['balance'];

View File

@ -1,6 +1,8 @@
import './style.scss';
export default new Class({ export default new Class({
Extends: Hedera.Form Extends: Hedera.Form,
Template: require('./ui.xml')
,activate() { ,activate() {
this.$.lot.assign({ this.$.lot.assign({

View File

@ -1,6 +1,8 @@
import './style.scss';
export default new Class({ export default new Class({
Extends: Hedera.Form Extends: Hedera.Form,
Template: require('./ui.xml')
,activate() { ,activate() {
this.$.lot.assign({ this.$.lot.assign({

View File

@ -1,65 +1,86 @@
export const locales = { export const locales = {
'ca': cb => require([], () => cb(require.context('js', true, /locale\/ca.yml$/))), ca: cb => require([],
'es': cb => require([], () => cb(require.context('js', true, /locale\/es.yml$/))), () => cb(require.context('js', true, /locale\/ca.yml$/))),
'fr': cb => require([], () => cb(require.context('js', true, /locale\/fr.yml$/))), es: cb => require([],
'mn': cb => require([], () => cb(require.context('js', true, /locale\/mn.yml$/))), () => cb(require.context('js', true, /locale\/es.yml$/))),
'pt': cb => require([], () => cb(require.context('js', true, /locale\/pt.yml$/))) fr: cb => require([],
() => cb(require.context('js', true, /locale\/fr.yml$/))),
mn: cb => require([],
() => cb(require.context('js', true, /locale\/mn.yml$/))),
pt: cb => require([],
() => cb(require.context('js', true, /locale\/pt.yml$/)))
}; };
export const formMap = { export const routes = {
'account/address': account: {
address:
() => import('account/address'), () => import('account/address'),
'account/address-list': addressList:
() => import('account/address-list'), () => import('account/address-list'),
'account/conf': conf:
() => import('account/conf'), () => import('account/conf')
'admin/access-log': },
admin: {
accessLog:
() => import('admin/access-log'), () => import('admin/access-log'),
'admin/connections': connections:
() => import('admin/connections'), () => import('admin/connections'),
'admin/items': items:
() => import('admin/items'), () => import('admin/items'),
'admin/links': links:
() => import('admin/links'), () => import('admin/links'),
'admin/photos': photos:
() => import('admin/photos'), () => import('admin/photos'),
'admin/queries': queries:
() => import('admin/queries'), () => import('admin/queries'),
'admin/users': users:
() => import('admin/users'), () => import('admin/users'),
'admin/visits': visits:
() => import('admin/visits'), () => import('admin/visits')
'agencies/packages': },
agencies: {
packages:
() => import('agencies/packages'), () => import('agencies/packages'),
'agencies/provinces': provinces:
() => import('agencies/provinces'), () => import('agencies/provinces')
'cms/about': },
cms: {
about:
() => import('cms/about'), () => import('cms/about'),
'cms/contact': contact:
() => import('cms/contact'), () => import('cms/contact'),
'cms/home': home:
() => import('cms/home'), () => import('cms/home'),
'cms/location': location:
() => import('cms/location'), () => import('cms/location'),
'cms/why': why:
() => import('cms/why'), () => import('cms/why')
'ecomerce/basket': },
ecomerce: {
basket:
() => import('ecomerce/basket'), () => import('ecomerce/basket'),
'ecomerce/catalog': catalog:
() => import('ecomerce/catalog'), () => import('ecomerce/catalog'),
'ecomerce/checkout': checkout:
() => import('ecomerce/checkout'), () => import('ecomerce/checkout'),
'ecomerce/confirm': confirm:
() => import('ecomerce/confirm'), () => import('ecomerce/confirm'),
'ecomerce/invoices': invoices:
() => import('ecomerce/invoices'), () => import('ecomerce/invoices'),
'ecomerce/orders': orders:
() => import('ecomerce/orders'), () => import('ecomerce/orders'),
'ecomerce/ticket': ticket:
() => import('ecomerce/ticket'), () => import('ecomerce/ticket')
'news/new': },
news: {
new:
() => import('news/new'), () => import('news/new'),
'news/news': news:
() => import('news/news') () => import('news/news')
},
reports: {
shelves:
() => import('reports/shelves')
}
}; };

View File

@ -15,7 +15,9 @@ module.exports = new Class({
,initialize() { ,initialize() {
window.onerror = this._onWindowError.bind(this); window.onerror = this._onWindowError.bind(this);
window.onunhandledrejection = e => this._onWindowRejection(e);
window.onunload = this._onWindowUnload.bind(this); window.onunload = this._onWindowUnload.bind(this);
this._hash = new Vn.Hash({window: window}); this._hash = new Vn.Hash({window: window});
var conn = new Db.Connection(); var conn = new Db.Connection();
@ -67,6 +69,12 @@ module.exports = new Class({
this._notifyError(error); this._notifyError(error);
} }
,_onWindowRejection(event) {
const err = event.reason;
this._onConnError(null, err);
event.preventDefault();
}
,_onConnError(conn, error) { ,_onConnError(conn, error) {
if (error instanceof Vn.JsonException) { if (error instanceof Vn.JsonException) {
if (error.message) if (error.message)

View File

@ -1,7 +1,8 @@
const Module = require('./module'); const Module = require('./module');
const Tpl = require('./gui.xml').default; const Tpl = require('./gui.xml').default;
const formMap = require('../../import').formMap; const kebabToCamel = require('vn/string-util').kebabToCamel;
const routes = require('../../import').routes;
require('./gui.scss'); require('./gui.scss');
module.exports = new Class({ module.exports = new Class({
@ -98,13 +99,13 @@ module.exports = new Class({
this.onLogoutClick(); this.onLogoutClick();
} }
,onLogoutClick() { ,async onLogoutClick() {
this._conn.close(this._onConnClose.bind(this)); try {
} await this._conn.close();
} finally {
,_onConnClose() {
this.emit('logout'); this.emit('logout');
} }
}
,_onConnLoadChange(conn, isLoading) { ,_onConnLoadChange(conn, isLoading) {
if (isLoading) if (isLoading)
@ -369,13 +370,16 @@ module.exports = new Class({
this.choosedOption = newChoosedOption; this.choosedOption = newChoosedOption;
} }
await Vn.Locale.load(`forms/${formPath}`);
let FormKlass; let FormKlass;
try { try {
FormKlass = (await this.importForm(formPath)).default; FormKlass = (await this.importForm(formPath)).default;
} catch (err) { } catch (err) {
this.loaderPop(); this.loaderPop();
return Htk.Toast.showError(_('Error loading form')); console.log(err);
return Htk.Toast.showError(_('Error loading form') +`: ${err.message}`);
} }
this.loaderPop(); this.loaderPop();
@ -388,8 +392,13 @@ module.exports = new Class({
} }
,async importForm(path) { ,async importForm(path) {
const fn = formMap[path]; const states = path.split('/');
return fn ? fn() : null; let importFn = routes;
for (let i = 0; i < states.length && importFn; i++)
importFn = importFn[kebabToCamel(states[i])];
if (!importFn)
throw new Error(`Route '${path}' does not exist`);
return importFn();
} }
,setForm(form) { ,setForm(form) {

View File

@ -1,4 +1,4 @@
var Tpl = require('./login.xml'); var Tpl = require('./login.xml').default;
require('./login.scss'); require('./login.scss');
module.exports = new Class({ module.exports = new Class({
@ -48,30 +48,24 @@ module.exports = new Class({
this._focusUserInput(); this._focusUserInput();
} }
,_onSubmit() { ,async _onSubmit() {
this._conn.open( this._disableUi(true);
try {
await this._conn.open(
this.$.user.value, this.$.user.value,
this.$.pass.value, this.$.pass.value,
this.$.remember.checked, this.$.remember.checked
this._onConnOpen.bind(this)
); );
this._disableUi(true);
}
,_onConnOpen(conn, success, error) { const user = this.$.user.value;
if (user) localStorage.setItem('hederaLastUser', user);
this.emit('login');
} catch (err) {
this._focusUserInput();
throw err;
} finally {
this.$.pass.value = ''; this.$.pass.value = '';
this._disableUi(false); this._disableUi(false);
if (success) {
var user = this.$.user.value;
if (user)
localStorage.setItem('hederaLastUser', user);
this.emit('login');
} else {
this._focusUserInput();
throw error;
} }
} }

View File

@ -43,62 +43,47 @@ module.exports = new Class({
* @param {String} user The user name * @param {String} user The user name
* @param {String} password The user password * @param {String} password The user password
* @param {Boolean} remember Specifies if the user should be remembered * @param {Boolean} remember Specifies if the user should be remembered
* @param {Function} openCallback The function to call when operation is done * @return {Promise} Resolved when operation is done
*/ */
,open(user, pass, remember, callback) { ,async open(user, pass, remember) {
let params;
if (user !== null && user !== undefined) { if (user !== null && user !== undefined) {
var params = { params = {
user: user user: user
,password: pass ,password: pass
,remember: remember ,remember: remember
}; };
} else } else
var params = null; params = null;
this.lbSend('POST', 'Accounts/login', params, try {
this._onOpen.bind(this, callback, remember)); const json = await this.lbSend('POST', 'Accounts/login', params);
}
/*
* Called when open operation is done.
*/
,_onOpen(callback, remember, json, error) {
if (json) {
this._connected = true;
this.token = json.token;
var storage = remember ? localStorage : sessionStorage; var storage = remember ? localStorage : sessionStorage;
storage.setItem('vnToken', this.token); storage.setItem('vnToken', json.token);
this.token = json.token;
this._connected = true;
this.emit('openned'); this.emit('openned');
} else } catch(err) {
this._closeClient(); this._closeClient();
throw err;
if (callback) }
callback(this, this._connected, error);
} }
/** /**
* Closes the connection to the REST service. * Closes the connection to the REST service.
* *
* @param {Function} callback The function to call when operation is done * @return {Promise} Resolved when operation is done
*/ */
,close(callback) { ,async close() {
this.lbSend('POST', 'Accounts/logout', null, await this.lbSend('POST', 'Accounts/logout');
this._onClose.bind(this, callback));
this._closeClient(); this._closeClient();
}
/*
* Called when close operation is done.
*/
,_onClose(callback, json, error) {
this.emit('closed'); this.emit('closed');
if (callback)
callback(this, null, error);
} }
,_closeClient() { ,_closeClient() {
this._connected = false; this._connected = false;
this.clearToken(); this.clearToken();
@ -203,18 +188,25 @@ module.exports = new Class({
this._addRequest(); this._addRequest();
} }
,lbSend(method, url, params, callback) { ,async lbSend(method, url, params) {
var request = new XMLHttpRequest(); var request = new XMLHttpRequest();
request.open(method, `api/${url}`, true); request.open(method, `api/${url}`, true);
request.setRequestHeader('Content-Type', request.setRequestHeader('Content-Type',
'application/json;charset=utf-8'); 'application/json;charset=utf-8');
if (this.token) if (this.token)
request.setRequestHeader('Authorization', this.token); request.setRequestHeader('Authorization', this.token);
this._addRequest();
return await new Promise((resolve, reject) => {
function callback(data, err) {
if (err) return reject(err);
resolve(data);
}
request.onreadystatechange = request.onreadystatechange =
this._onStateChange.bind(this, request, callback); this._onStateChange.bind(this, request, callback);
request.send(params && JSON.stringify(params)); request.send(params && JSON.stringify(params));
});
this._addRequest();
} }
,_addRequest() { ,_addRequest() {

View File

@ -1,101 +1,97 @@
var yaml = require('js-yaml'); const yaml = require('js-yaml');
vnLocaleStrings = {};
const locales = {};
let fallbackLang = 'en';
let lang = null;
/** /**
* Class to manage the internationalization. * Class to manage the internationalization.
*/ */
module.exports = module.exports =
{ {
language: null init() {
,defaultLang: 'en' if (lang) return;
this.loads = {};
this.locales = locales;
this.fallbackLang = fallbackLang;
,init() { const languages = navigator.languages;
if (this.language)
return;
var language = this.defaultLang;
var languages = navigator.languages;
if (languages && languages.length > 0) if (languages && languages.length > 0)
language = languages[0]; lang = languages[0];
else if (navigator.language) else if (navigator.language)
language = navigator.language; lang = navigator.language;
this.language = language.substr(0, 2); lang = lang ? lang.substr(0, 2) : fallbackLang;
} this.language = lang;
},
,load(path, callback) { async load(path) {
this.init(); this.init();
await Promise.all([
this.loadLang(path, fallbackLang),
this.loadLang(path, lang)
]);
},
var data = { /**
path: path, * @returns {Promise}
callback: callback, */
defOk: false, loadLang(path, lang) {
orgOk: this.defaultLang === this.language let langLoad = this.loads[lang];
}; if (!langLoad)
langLoad = this.loads[lang] = {};
data.def = this.createRequest(data, true, this.defaultLang); if (langLoad[path])
return Promise.resolve();
langLoad[path] = true;
if (!data.orgOk) const langFile = `${path}/locale/${lang}.yml${Vn.getVersion()}`;
data.org = this.createRequest(data, false, this.language); const request = new XMLHttpRequest();
}
,createRequest(data, isDef, lang) {
var langFile = data.path +'/locale/'+ lang +'.yml'+ Vn.getVersion();
var request = new XMLHttpRequest();
request.open('get', langFile, true); request.open('get', langFile, true);
request.onreadystatechange =
this.onRequestReady.bind(this, request, data, isDef);
request.send();
return request;
}
,onRequestReady(request, data, isDef) { return new Promise(
(resolve, reject) => {
request.onreadystatechange =
() => this.onRequestReady(request, resolve, reject);
request.send();
})
.then(translations => {
this.add(translations, lang);
})
.catch(err => {
langLoad[path] = false;
console.warn(err);
});
},
onRequestReady(request, resolve, reject) {
if (request.readyState != 4) if (request.readyState != 4)
return; return;
if (request.status < 200 || request.status >= 400)
return reject(new Error(`HTTP ${request.status}: ${request.statusText}`));
if (isDef) { resolve(yaml.safeLoad(request.responseText));
this.loadFromRequest(request); },
data.defOk = true;
} else
data.orgOk = true;
if (data.orgOk && data.defOk) { add(translations, myLang) {
if (data.org != null) if (!translations) return;
this.loadFromRequest(data.org) myLang = myLang || lang;
if (data.callback) let langLocales = locales[myLang];
data.callback(); if (!langLocales)
langLocales = locales[myLang] = {};
for (const str in translations)
langLocales[str] = translations[str];
} }
} }
,loadFromRequest(request) { function getString(stringId, lang) {
if (request.status !== 200) return locales[lang] ? locales[lang][stringId] : null;
return false;
try {
this.add(yaml.safeLoad(request.responseText));
return true;
} catch (e) {
console.error(e);
}
return false
}
,loadScript(path, callback) {
this.init();
Vn.includeJs(path +'/locale/'+ this.language, callback);
}
,add(translations) {
for (var str in translations)
vnLocaleStrings[str] = translations[str];
}
} }
window._ = function(stringId) { window._ = function(stringId) {
var string = vnLocaleStrings[stringId]; let string = getString(stringId, lang);
return string ? string : stringId; if (!string)
string = getString(stringId, fallbackLang);
return string || stringId;
} }

View File

@ -238,19 +238,6 @@ Vn = module.exports = {
this.currentCallback = callback; this.currentCallback = callback;
} }
/**
* Includes an entire Javascript library including it's localized file.
*
* @param {string} libName The folder of the library
* @param {Array<string>} files Array with every library file name
*/
,includeLib(libName, files) {
Vn.Locale.loadScript('js/'+ libName +'.js');
for (var i = 0; i < files.length; i++)
this.include('js/'+ libName +'/'+ files[i]);
}
/** /**
* Includes a new Javascript in the current document, if the script * Includes a new Javascript in the current document, if the script
* is already included, does nothing and calls the callback. * is already included, does nothing and calls the callback.

3376
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -8,8 +8,10 @@
"url": "https://git.verdnatura.es/hedera-web" "url": "https://git.verdnatura.es/hedera-web"
}, },
"devDependencies": { "devDependencies": {
"@babel/preset-env": "^7.20.2",
"archiver": "^5.3.1", "archiver": "^5.3.1",
"assets-webpack-plugin": "^7.1.1", "assets-webpack-plugin": "^7.1.1",
"babel-loader": "^9.1.0",
"bundle-loader": "^0.5.6", "bundle-loader": "^0.5.6",
"css-loader": "^6.7.1", "css-loader": "^6.7.1",
"eslint": "^8.15.0", "eslint": "^8.15.0",
@ -32,6 +34,7 @@
"dependencies": { "dependencies": {
"js-yaml": "^3.12.1", "js-yaml": "^3.12.1",
"mootools": "^1.5.2", "mootools": "^1.5.2",
"promise-polyfill": "^8.2.3",
"require-yaml": "0.0.1", "require-yaml": "0.0.1",
"tinymce": "^5.0.0" "tinymce": "^5.0.0"
}, },

View File

@ -19,6 +19,15 @@ const baseConfig = {
module: { module: {
rules: [ rules: [
{ {
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}, {
test: /\.css$/, test: /\.css$/,
use: ['style-loader', 'css-loader'], use: ['style-loader', 'css-loader'],
}, { }, {