Compare commits

..

66 Commits

Author SHA1 Message Date
Juan Ferrer f7d58c8546 gitignore 2020-01-17 13:09:16 +01:00
Juan Ferrer Toribio 85bd397b83 Merge 2018-02-19 18:24:49 +01:00
Juan Ferrer Toribio 5d26b6cc3f Merge 2018-02-19 18:24:40 +01:00
Juan Ferrer Toribio 2ca8a09acd CSS class inherithed in components, bugs solved 2018-01-16 09:10:44 +01:00
Juan Ferrer Toribio 6666304332 Merge 2018-01-15 09:38:13 +01:00
Juan Ferrer Toribio 479ad20fa9 Webpack updated 2018-01-04 10:23:08 +01:00
Juan Ferrer Toribio 17d3a4457e Merge with master 2018-01-03 16:52:45 +01:00
Juan Ferrer Toribio e458987f67 Merge with master 2018-01-03 16:51:59 +01:00
Juan Ferrer Toribio cae37d5d6a Merge 2018-01-02 16:54:47 +01:00
Juan Ferrer Toribio a341f17ee5 Merge 2018-01-02 16:54:40 +01:00
Juan Ferrer Toribio 6829bb5a83 Merge with master 2017-11-29 11:29:08 +01:00
Juan Ferrer Toribio 2b9c28bc76 CSS bug 2017-11-27 16:51:40 +01:00
Juan Ferrer Toribio a5225fbb5c Merge with master 2017-11-27 16:41:08 +01:00
Juan Ferrer Toribio 42b5662b23 Merge with master 2017-11-24 12:38:07 +01:00
Juan Ferrer Toribio b9c14883c4 Backup 2017-11-23 17:06:28 +01:00
Juan Ferrer Toribio 7b54145da6 Refactor, Component improved 2017-11-22 13:25:19 +01:00
Juan Ferrer Toribio 23715816ce Tags Alpha v2 2017-11-21 12:50:55 +01:00
Juan Ferrer Toribio 857e46ce4d Tags alpha, bugs combo 2017-11-20 19:01:14 +01:00
Juan Ferrer Toribio ce167066f4 Refactor 2017-11-20 13:15:01 +01:00
Juan Ferrer Toribio 7658a5680d Tags, errores solucionados 2017-11-16 15:53:20 +01:00
Juan Ferrer Toribio f9c002f08a backup 2017-11-13 17:36:30 +01:00
Juan Ferrer Toribio 1cec1b4cd6 Backup 2017-11-06 16:00:14 +01:00
Juan Ferrer Toribio c3c7c77b65 Push test 2017-11-02 10:45:04 +01:00
Juan Ferrer Toribio 3aaf1298ee Various fixes 2017-11-02 09:23:55 +01:00
Juan Ferrer Toribio 37236b6bb0 Various fixes 2017-10-28 17:13:00 +02:00
Juan Ferrer Toribio fe68eb5ce0 Vn.Model v1 2017-10-25 09:47:32 +02:00
Juan Ferrer Toribio 4d8bd4ad95 Interpolate v2 2017-10-22 02:25:57 +02:00
Juan Ferrer Toribio f0dcd47baf Bugs solved 2017-10-20 19:45:11 +02:00
Juan Ferrer Toribio c1cf88e44d Bugs solved, refactor 2017-10-20 19:22:24 +02:00
Juan Ferrer Toribio 60a1d7cf9e interpolate v1, camelCase htmlId 2017-10-20 19:09:06 +02:00
Juan Ferrer Toribio 63158d335e New mark for translatable TextNode, bugs in builder solved, 4 spaces replaced by tabs 2017-10-20 16:24:49 +02:00
Juan Ferrer Toribio 2e9ea7ac33 Refactorización Vn.Builder 2017-10-20 14:06:16 +02:00
Juan Ferrer Toribio f6603ccc13 Backup 2017-10-18 18:01:21 +02:00
Juan Ferrer Toribio e178c74f25 Backup 2017-10-16 09:58:12 +02:00
Juan Ferrer Toribio 125686307f Backup 2017-10-10 13:58:25 +02:00
Juan Ferrer Toribio a8b6dd4c1a Backup 2017-09-12 13:31:15 +02:00
Juan Ferrer Toribio b8c83df08f Backup 2017-08-21 12:20:36 +02:00
Juan Ferrer Toribio fab5253406 Merge 2017-07-14 07:37:10 +02:00
Juan Ferrer Toribio c1175122d0 Backup 2017-07-05 11:50:42 +02:00
Juan Ferrer Toribio 942772ec03 Errores merge solucionados 2017-07-04 09:13:06 +02:00
Juan Ferrer Toribio 13e45afc77 Merge with master 2017-07-04 08:51:41 +02:00
Juan Ferrer Toribio d327be4780 Backup 2017-05-22 09:08:21 +02:00
Juan Ferrer Toribio ff0b899fa2 Errores solucionados 2017-05-11 17:38:31 +02:00
Juan Ferrer Toribio 06bd47bf91 Merge with master 2017-05-10 08:38:26 +02:00
Juan Ferrer Toribio 45f5cc3deb Merge with master 2017-05-09 15:33:02 +02:00
Juan Ferrer Toribio 25e9b37b75 Merge con master 2017-05-02 15:48:47 +02:00
Juan Ferrer Toribio b7bdef4bf9 Merge with master fixes 2017-04-27 10:39:37 +02:00
Juan Ferrer Toribio 28b2302d78 Backup 2017-04-25 19:56:39 +02:00
Juan Ferrer Toribio 1e43aead46 backup 2017-04-24 09:47:56 +02:00
Juan Ferrer Toribio adb651fe69 Backup 2017-04-21 12:53:15 +02:00
Juan Ferrer Toribio 234566b6af Backup 2017-04-19 08:16:37 +02:00
Juan Ferrer Toribio 73b2fed910 Backup 2017-04-10 18:23:40 +02:00
Juan Ferrer Toribio 22ee7e5020 Backup 2017-04-10 17:17:56 +02:00
Juan Ferrer Toribio 403845bf2b Backup 2017-04-08 13:42:27 +02:00
Juan Ferrer Toribio 0be33631ca Backup 2017-04-07 13:00:33 +02:00
Juan Ferrer Toribio 944a955068 Backup 2017-04-05 16:06:07 +02:00
Juan Ferrer Toribio efdde01e19 Backup 2017-03-30 13:44:53 +02:00
Juan Ferrer Toribio a5854290e9 Backup 2017-03-23 17:20:51 +01:00
Juan Ferrer Toribio 5640951c80 Backup 2017-03-22 17:57:21 +01:00
Juan Ferrer Toribio 86a19925d8 Backup 2017-03-17 13:42:10 +01:00
Juan Ferrer Toribio 043c11a4ed backup 2017-03-09 13:30:39 +01:00
Juan Ferrer Toribio b7ec06ffdc Webpack caching 2017-03-02 11:01:29 +01:00
Juan Ferrer Toribio 1a5bcf2bd2 Deteccion idioma del navedor mejorada 2017-01-18 13:11:23 +01:00
Juan Ferrer Toribio 969f58981e Estilo beta 2017-01-18 12:50:07 +01:00
Juan Ferrer Toribio 2c6c51865f Estilos CSS depurados 2016-12-23 09:57:49 +01:00
Juan Ferrer Toribio 63d75a7ff6 Js doc asteriscos 2016-12-20 10:32:17 +01:00
671 changed files with 22007 additions and 36966 deletions

View File

@ -1,2 +0,0 @@
debian
node_modules

View File

@ -9,7 +9,3 @@ rules:
no-console: 0 no-console: 0
no-cond-assign: 0 no-cond-assign: 0
no-unexpected-multiline: 0 no-unexpected-multiline: 0
brace-style: [error, 1tbs]
space-before-function-paren: [error, never]
padded-blocks: [error, never]
func-call-spacing: [error, never]

2
.gitignore vendored
View File

@ -1,6 +1,4 @@
node_modules node_modules
build/ build/
dist/
config.my.php config.my.php
.vscode/ .vscode/
.quasar

View File

@ -1,52 +0,0 @@
# Not using buster because of bug: https://bugs.php.net/bug.php?id=78870
FROM debian:bookworm-slim
ARG DEBIAN_FRONTEND=noninteractive
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
ca-certificates \
gnupg2
# Apache
RUN apt-get install -y --no-install-recommends \
apache2 \
libapache2-mod-php \
&& . /etc/apache2/envvars \
&& ln -sfT /dev/stderr "$APACHE_LOG_DIR/error.log" \
&& ln -sfT /dev/stdout "$APACHE_LOG_DIR/access.log" \
&& ln -sfT /dev/stdout "$APACHE_LOG_DIR/other_vhosts_access.log"
RUN a2dissite 000-default
# NodeJs
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
&& apt-get install -y --no-install-recommends nodejs
# Hedera
RUN curl -sL https://apt.verdnatura.es/conf/verdnatura.gpg | tee /etc/apt/trusted.gpg.d/verdnatura.gpg \
&& echo "deb http://apt.verdnatura.es/ bookworm main" \
> /etc/apt/sources.list.d/vn.list \
&& apt-get update \
&& apt-get install -y --no-install-recommends \
php-apcu \
php-image-text \
php-text-captcha \
php-zip \
hedera-web \
cron
ARG BUILD_ID=unknown
ARG VERSION
ENV VERSION $VERSION
RUN echo $VERSION
RUN apt-get update \
&& apt-get install -y php-vn-lib hedera-web=$VERSION \
&& rm -rf /var/lib/apt/lists/*
CMD ["apachectl", "-D", "FOREGROUND"]

102
Jenkinsfile vendored
View File

@ -1,102 +0,0 @@
#!/usr/bin/env groovy
def BRANCH_ENV = [
test: 'test',
master: 'production'
]
def remote = [:]
node {
stage('Setup') {
env.NODE_ENV = BRANCH_ENV[env.BRANCH_NAME] ?: 'dev'
echo "NODE_NAME: ${env.NODE_NAME}"
echo "WORKSPACE: ${env.WORKSPACE}"
}
}
pipeline {
agent any
environment {
PROJECT_NAME = 'hedera-web'
}
stages {
stage('Debuild') {
when {
anyOf {
branch 'master'
branch 'test'
}
}
agent {
docker {
image 'registry.verdnatura.es/verdnatura/debuild:2.23.4-vn7'
registryUrl 'https://registry.verdnatura.es/'
registryCredentialsId 'docker-registry'
}
}
steps {
sh 'debuild -us -uc -b'
sh 'mkdir -p debuild'
sh 'mv ../hedera-web_* debuild'
script {
def files = findFiles(glob: 'debuild/*.changes')
files.each { file -> env.CHANGES_FILE = file.name }
}
configFileProvider([
configFile(fileId: "dput.cf", variable: 'DPUT_CONFIG')
]) {
sshagent(credentials: ['jenkins-agent']) {
sh 'dput --config "$DPUT_CONFIG" verdnatura "debuild/$CHANGES_FILE"'
}
}
}
}
stage('Deploy') {
when {
anyOf {
branch 'master'
branch 'test'
}
}
environment {
CREDS = credentials('docker-registry')
IMAGE = "$REGISTRY/verdnatura/hedera-web"
}
steps {
script {
def packageJson = readJSON file: 'package.json'
env.VERSION = "${packageJson.version}"
env.TAG = "${packageJson.version}-build${env.BUILD_ID}"
}
sh 'docker-compose build --build-arg BUILD_ID=$BUILD_ID --parallel'
sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY'
sh 'docker push $IMAGE:$TAG'
script {
if (env.BRANCH_NAME == 'master') {
sh 'docker tag $IMAGE:$TAG $IMAGE:latest'
sh 'docker push $IMAGE:latest'
}
}
withKubeConfig([
serverUrl: "$KUBERNETES_API",
credentialsId: 'kubernetes',
namespace: 'salix'
]) {
sh 'kubectl set image deployment/hedera-web-$BRANCH_NAME hedera-web-$BRANCH_NAME=$IMAGE:$TAG'
sh 'kubectl set image deployment/hedera-web-cron-$BRANCH_NAME hedera-web-cron-$BRANCH_NAME=$IMAGE:$TAG'
}
}
}
}
post {
unsuccessful {
setEnv()
sendEmail()
}
}
}

View File

@ -1,58 +1,3 @@
# Hedera # Hedera
Hedera is the main web shop page for Verdnatura. Hedera is the main page for Verdnatura.
## Prerequisites
Required applications.
* PHP >= 8.4
* Node.js >= 20.0
Take a look to *debian/control* file to see additional dependencies.
Copy config.php to *config.my.php* and place your DB config there.
### Installing dependencies and launching
Pull from repository.
Run this commands on project root directory to install Node dependencies.
```
$ npm install
```
Install project dependences (debian/control).
Pull from repository [php-vn-lib](https://gitea.verdnatura.es/verdnatura/php-vn-lib) and install [dependences](https://gitea.verdnatura.es/verdnatura/php-vn-lib/src/branch/master/debian/control) of this project.
Configure config.php file.
Launch project fronted.
```
$ npm run front
```
Launch salix backend.
```
$ npm run db
$ npm run back
```
Launch project backend.
```
$ php -S 127.0.0.1:3001 -t . index.php
```
### Command line
Run server side method from command line.
```
$ php hedera-web.php -m method_path
```
## Built with
* [nodejs](https://nodejs.org/)
* [php](https://www.php.net/)
* [Webpack](https://webpack.js.org/)
* [MooTools](https://mootools.net/)
* [TinyMCE](https://www.tinymce.com/)

74
app.js
View File

@ -1,41 +1,71 @@
__webpack_public_path__ = _PUBLIC_PATH;
import 'promise-polyfill/src/polyfill'; var assetsPath;
import 'hedera/hedera';
const locales = require('./import').locales;
const packageJson = require('./package.json');
window.onload = function() { if (_DEV_MODE)
{
var host = window.location.host.split(':')[0];
assetsPath = 'http://'+ host +':'+ _DEV_SERVER_PORT +'/'+ _PUBLIC_PATH;
}
else
assetsPath = _PUBLIC_PATH;
__webpack_public_path__ = assetsPath;
require ('hedera/hedera');
window.onload = function ()
{
loadLocale (main); loadLocale (main);
} }
function main() { function main (req)
Vn.setVersion(packageJson.version); {
const hederaWeb = new Hedera.App(); if (req)
window.hederaWeb = hederaWeb; onLocaleLoad (req);
hederaWeb = new Hedera.App ();
hederaWeb.run (); hederaWeb.run ();
} }
function loadLocale(callback) { function loadLocale (cb)
{
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(Vn.Locale.fallbackLang, req); onLocaleLoad (req);
const loadFn = locales[lang]; switch (lang)
if (loadFn) {
loadFn(function(req) { case 'ca':
onLocaleLoad(lang, req); require ([], function () {
callback(); cb (require.context ('js', true, /locale\/ca.yml$/)); });
}); break;
else case 'es':
callback(); require ([], function () {
cb (require.context ('js', true, /locale\/es.yml$/)); });
break;
case 'fr':
require ([], function () {
cb (require.context ('js', true, /locale\/fr.yml$/)); });
break;
case 'mn':
require ([], function () {
cb (require.context ('js', true, /locale\/mn.yml$/)); });
break;
case 'pt':
require ([], function () {
cb (require.context ('js', true, /locale\/pt.yml$/)); });
break;
default:
cb ();
}
} }
function onLocaleLoad(lang, req) { function onLocaleLoad (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]), lang); Vn.Locale.add (req (keys[i]));
} }

View File

@ -1,22 +1,14 @@
# Alias /hedera-web /usr/share/hedera-web/
# Alias /image-db /var/lib/hedera-web/image-db/
<IfModule mod_mime.c> <IfModule mod_mime.c>
AddType text/x-yaml .yml AddType text/x-yaml .yml
</IfModule> </IfModule>
<IfModule mpm_prefork_module>
ServerLimit 30
MaxRequestWorkers 30
MaxRequestsPerChild 1000
</IfModule>
<VirtualHost *:80>
DocumentRoot /usr/share/hedera-web/
</VirtualHost>
<Directory /usr/share/hedera-web/> <Directory /usr/share/hedera-web/>
Options -Indexes -FollowSymLinks Options -Indexes -FollowSymLinks
AllowOverride None AllowOverride None
Require all granted Require all granted
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
<FilesMatch "\.(css|js|json|yml|php|xml|html|svg)$"> <FilesMatch "\.(css|js|json|yml|php|xml|html|svg)$">
SetOutputFilter DEFLATE SetOutputFilter DEFLATE
@ -28,3 +20,9 @@
</IfModule> </IfModule>
</FilesMatch> </FilesMatch>
</Directory> </Directory>
<Directory /var/lib/hedera-web/image-db/>
Options Indexes FollowSymLinks MultiViews
AllowOverride FileInfo Options
Require all granted
</Directory>

View File

@ -12,19 +12,18 @@
* *
* - http://www.mydomain.org -> config.www.php * - http://www.mydomain.org -> config.www.php
* - http://test.mydomain.org -> config.test.php * - http://test.mydomain.org -> config.test.php
*
* Put the password in base64.
*/ */
return [ return [
/** /**
* Database parameters. * Database parameters.
*/ */
'db' => [ 'db' => [
'host' => 'localhost' 'host' => 'localhost'
,'port' => 3306 ,'port' => 3306
,'schema' => 'hedera' ,'schema' => 'hedera-web'
,'user' => 'hedera-web' ,'user' => 'hedera-web'
,'pass' => '' // base64 encoded ,'pass' => ''
,'tz' => 'Europe/madrid'
] ]
]; ];

2
debian/changelog vendored
View File

@ -1,4 +1,4 @@
hedera-web (25.4.4) stable; urgency=low hedera-web (2.0.4) stable; urgency=low
* Initial Release. * Initial Release.

2
debian/compat vendored
View File

@ -1 +1 @@
10 9

6
debian/control vendored
View File

@ -5,12 +5,12 @@ Build-Depends: build-essential, debhelper, nodejs
Standards-Version: 3.9.3 Standards-Version: 3.9.3
Section: misc Section: misc
Homepage: https://verdnatura.es Homepage: https://verdnatura.es
Vcs-Git: https://gitea.verdnatura.es/verdnatura/hedera-web Vcs-Git: https://git.verdnatura.es/hedera-web
Package: hedera-web Package: hedera-web
Architecture: all Architecture: all
Depends: apache2 | httpd, nodejs, php-cli, php-vn-lib, php-apcu, php-imap, php-soap, libphp-phpmailer, php-gd, php-pear Depends: apache2 | httpd, nodejs, php5-cli, php5-mysql, php5-mcrypt, php5-ldap, php5-ssh2, php5-apcu, php-vn-lib
Suggests: php-image-text, php-text-captcha, php-zip, cron Suggests: php-text-captcha, php5-imap
Section: misc Section: misc
Priority: optional Priority: optional
Description: Verdnatura's web page Description: Verdnatura's web page

2
debian/copyright vendored
View File

@ -1,6 +1,6 @@
Format: http://dep.debian.net/deps/dep5 Format: http://dep.debian.net/deps/dep5
Name: hedera-web Name: hedera-web
Source: https://gitea.verdnatura.es/verdnatura/hedera-web Source: git://www.verdnatura.es/var/git/hedera-web
Files: * Files: *
Copyright: 2011-2015 Juan Ferrer Toribio <juan@verdnatura.es> Copyright: 2011-2015 Juan Ferrer Toribio <juan@verdnatura.es>

7
debian/cron.d vendored
View File

@ -1,8 +1,7 @@
MAILTO=webmaster MAILTO=webmaster
*/1 * * * * root hedera-web.php -m misc/mail */4 * * * * root hedera-web.php -m tpv/confirm-mail
*/2 * * * * root hedera-web.php -m edi/load */2 * * * * root hedera-web.php -m edi/load
0 23 * * * root hedera-web.php -m edi/clean 0 23 * * * root hedera-web.php -m edi/clean
0 5 * * * root hedera-web.php -m edi/update 0 5 * * * root hedera-web.php -m edi/update
0 0 * * * root hedera-web.php -m image/sync */1 * * * * root hedera-web.php -m misc/mail
0 1 * * * root /usr/share/hedera-web/utils/image-clean.sh > /dev/null 0 5 * * * root hedera-web.php -m misc/exchange-rate
0 */1 * * * root /usr/share/hedera-web/utils/update-browscap.sh > /dev/null

7
debian/install vendored
View File

@ -1,6 +1,4 @@
apache.conf etc/hedera-web conf/* etc/hedera-web
config.php etc/hedera-web
php.ini etc/hedera-web
web usr/share/php/vn web usr/share/php/vn
doc/* usr/share/doc/hedera-web doc/* usr/share/doc/hedera-web
hedera-web.php usr/share/hedera-web hedera-web.php usr/share/hedera-web
@ -10,10 +8,9 @@ js usr/share/hedera-web
pages usr/share/hedera-web pages usr/share/hedera-web
reports usr/share/hedera-web reports usr/share/hedera-web
rest usr/share/hedera-web rest usr/share/hedera-web
utils usr/share/hedera-web
index.php usr/share/hedera-web index.php usr/share/hedera-web
package.json usr/share/hedera-web package.json usr/share/hedera-web
build usr/share/hedera-web build usr/share/hedera-web
README.md usr/share/hedera-web README.md usr/share/hedera-web
webpack.config.json usr/share/hedera-web webpack.config.json usr/share/hedera-web
build-deps/node_modules usr/share/hedera-web build usr/share/hedera-web

3
debian/links vendored
View File

@ -1,3 +1,2 @@
usr/share/hedera-web/hedera-web.php usr/bin/hedera-web.php
etc/hedera-web/apache.conf etc/apache2/conf-available/hedera-web.conf etc/hedera-web/apache.conf etc/apache2/conf-available/hedera-web.conf
etc/hedera-web/php.ini etc/php/8.2/apache2/conf.d/99-hedera-web.ini usr/share/hedera-web/hedera-web.php usr/bin/hedera-web.php

9
debian/postinst vendored
View File

@ -2,15 +2,14 @@
set -e set -e
/usr/share/hedera-web/utils/update-browscap.sh > /dev/null (cd /usr/share/hedera-web && npm install --production)
if [ -e /usr/share/apache2/apache2-maintscript-helper ] if [ -e /usr/share/apache2/apache2-maintscript-helper ]
then then
. /usr/share/apache2/apache2-maintscript-helper . /usr/share/apache2/apache2-maintscript-helper
apache2_invoke enmod rewrite
apache2_invoke enmod headers
apache2_invoke enconf hedera-web.conf apache2_invoke enconf hedera-web.conf
fi fi
#service php7.3-fpm restart service php5-fpm restart
#service cron restart service cron restart

4
debian/postrm vendored
View File

@ -8,5 +8,5 @@ then
apache2_invoke disconf hedera-web.conf apache2_invoke disconf hedera-web.conf
fi fi
#rm -rf /usr/share/hedera-web/node_modules rm -rf /usr/share/hedera-web/node_modules
#service cron restart service cron restart

6
debian/rules vendored
View File

@ -10,9 +10,5 @@ clean:
dh_clean $@ dh_clean $@
build: build:
npm install --no-audit --prefer-offline npm --production run build
npm --omit=dev run build
mkdir -p build-deps
cp package.json package-lock.json build-deps
(cd build-deps && npm install --omit=dev --no-audit --prefer-offline)

View File

@ -1 +0,0 @@
hedera-web: package-contains-npm-ignore-file

View File

@ -1,9 +0,0 @@
version: '3.7'
services:
main:
image: registry.verdnatura.es/verdnatura/hedera-web:${TAG:?}
build:
context: .
dockerfile: Dockerfile
args:
- VERSION=${VERSION:?}

View File

@ -8,5 +8,6 @@ $vnAutoloadMap['vn/web'] = __DIR__.'/web';
const _ENABLE_DEBUG = TRUE; const _ENABLE_DEBUG = TRUE;
const _DEV_MODE = TRUE; const _DEV_MODE = TRUE;
const _CONFIG_DIR = __DIR__.'/../../.config';
const _LOG_DIR = '/tmp'; const _LOG_DIR = '/tmp';
const _DATA_DIR = '/tmp'; const _DATA_DIR = '/tmp';

View File

@ -0,0 +1,34 @@
Hedera.AddressList = new Class
({
Extends: Hedera.Form
,activate: function ()
{
this.$.userModel.setInfo ('c', 'myClient', 'hedera');
this.$.addresses.setInfo ('a', 'myAddress', 'hedera');
}
,onAddAddressClick: function ()
{
this.hash.setAll ({
form: 'account/address',
address: 0
});
}
,onReturnClick: function ()
{
window.history.back();
}
,onRemoveAddressClick: function (button)
{
if (confirm (_('AreYouSureDeleteAddress')))
{
button.lot.set ('isActive', false);
button.lot.refresh ();
}
}
});

View File

@ -1,43 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
,activate() {
this.$.userModel.setInfo('c', 'myClient', 'hedera');
this.$.addresses.setInfo('a', 'myAddress', 'hedera');
}
,onAddAddressClick() {
this.hash.setAll({
form: 'account/address',
address: 0
});
}
,onReturnClick() {
window.history.back();
}
,onSetDefaultClick(event, addressId) {
if (event.defaultPrevented) return;
this.$.defaultAddress.value = addressId;
Htk.Toast.showMessage(_('DefaultAddressModified'));
}
,async onRemoveAddressClick(form) {
if (confirm(_('AreYouSureDeleteAddress'))) {
await form.set('isActive', false);
await form.refresh();
}
}
,onEditAddressClick(address) {
this.hash.setAll({
form: 'account/address',
address
});
}
});

View File

@ -1,8 +1,7 @@
Addresses: Adreces Addresses: Direccions
Return: Tornar Return: Tornar
AddAddress: Afegir adreça AddAddress: Afegir adreça
SetAsDefault: Establir com per defecte SetAsDefault: Establir com per defecte
RemoveAddress: Esborrar direcció RemoveAddress: Esborrar direcció
EditAddress: Modificar direcció EditAddress: Modificar direcció
AreYouSureDeleteAddress: Estàs segur de que vols eliminar la direcció? AreYouSureDeleteAddress: Estàs segur de que vols eliminar la direcció?
DefaultAddressModified: Adreça per defecte modificada

View File

@ -5,4 +5,3 @@ SetAsDefault: Set as default
RemoveAddress: Remove address RemoveAddress: Remove address
EditAddress: Edit address EditAddress: Edit address
AreYouSureDeleteAddress: Are you sure you want to delete the address? AreYouSureDeleteAddress: Are you sure you want to delete the address?
DefaultAddressModified: Default address modified

View File

@ -5,4 +5,3 @@ SetAsDefault: Establecer como predeterminada
RemoveAddress: Borrar dirección RemoveAddress: Borrar dirección
EditAddress: Modificar dirección EditAddress: Modificar dirección
AreYouSureDeleteAddress: ¿Estás seguro de que quieres borrar la dirección? AreYouSureDeleteAddress: ¿Estás seguro de que quieres borrar la dirección?
DefaultAddressModified: Dirección por defecto modificada

View File

@ -5,4 +5,3 @@ SetAsDefault: Définir par défaut
RemoveAddress: Supprimer l'adresse RemoveAddress: Supprimer l'adresse
EditAddress: Changement d'adresse EditAddress: Changement d'adresse
AreYouSureDeleteAddress: Souhaitez-vous vraiment supprier l'adresse? AreYouSureDeleteAddress: Souhaitez-vous vraiment supprier l'adresse?
DefaultAddressModified: Adresse par défaut modifiée

View File

@ -5,4 +5,3 @@ SetAsDefault: Selecionar como pre-determinado
RemoveAddress: Eliminar Morada RemoveAddress: Eliminar Morada
EditAddress: Modificar Morada EditAddress: Modificar Morada
AreYouSureDeleteAddress: Tens certeza que queres eliminar esta morada? AreYouSureDeleteAddress: Tens certeza que queres eliminar esta morada?
DefaultAddressModified: Endereço padrão modificado

View File

@ -0,0 +1 @@

View File

@ -1,6 +0,0 @@
hedera-address-list {
.htk-list .side {
padding-right: 16px;
}
}

View File

@ -3,66 +3,62 @@
<db-form id="user-form"> <db-form id="user-form">
<db-model property="model" id="user-model" updatable="true"> <db-model property="model" id="user-model" updatable="true">
SELECT id, defaultAddressFk SELECT id, defaultAddressFk
FROM myClient c FROM myClient
</db-model> </db-model>
</db-form> </db-form>
<db-model id="addresses" updatable="true"> <db-model id="addresses" updatable="true">
SELECT a.id, a.nickname, p.name province, a.postalCode, SELECT a.id, a.nickname, p.name province, a.postalCode,
a.city, a.street, a.isActive a.city, a.street, a.isActive, c.country
FROM myAddress a FROM myAddress a
LEFT JOIN vn.province p ON p.id = a.provinceFk LEFT JOIN vn.province p ON p.id = a.provinceFk
JOIN vn.country c ON c.id = p.countryFk
WHERE a.isActive WHERE a.isActive
</db-model> </db-model>
</vn-group> </vn-group>
<div id="title"> <h1 id="title">
<h1><t>Addresses</t></h1> _Addresses
</div> </h1>
<div id="actions"> <div id="actions">
<htk-bar-button <htk-bar-button
icon="add" icon="add"
tip="_AddAddress" tip="_AddAddress"
on-click="this.onAddAddressClick()"/> on-click="onAddAddressClick"/>
</div> </div>
<div id="form" class="hedera-address-list"> <div id="main" class="address-list">
<div class="box vn-w-sm"> <div class="card list">
<htk-radio-group <htk-radio-group
id="default-address" id="default-address"
column="defaultAddressFk" lot="user-form"
form="user-form"/> name="defaultAddressFk"/>
<htk-repeater model="addresses" form-id="address" class="htk-list"> <htk-repeater model="addresses">
<custom> <custom>
<div class="item clickable" on-click="this.onSetDefaultClick($event, address.id)"> <a
<div class="side"> class="list-row"
href="#!form=account/address&amp;address={{id}}"
title="_EditAddress">
<div class="actions">
<htk-radio <htk-radio
lot="iter"
name="id"
radio-group="default-address" radio-group="default-address"
val="{{address.id}}" tip="_SetAsDefault"/>
tip="_SetAsDefault"
name="test"/>
</div>
<div class="content">
<p class="important">
{{address.nickname}}
</p>
<p>
{{address.street}}
</p>
<p>
{{address.postalCode}}, {{address.city}}
</p>
</div>
<div
class="actions"
on-click="$event.preventDefault()">
<htk-button <htk-button
icon="delete" lot="iter"
name="id"
tip="_RemoveAddress" tip="_RemoveAddress"
on-click="this.onRemoveAddressClick($iter)"/> icon="delete"
<htk-button on-click="onRemoveAddressClick"/>
icon="edit"
tip="_EditAddress"
on-click="this.onEditAddressClick(address.id)"/>
</div>
</div> </div>
<p class="important">
{{nickname}}
</p>
<p>
{{street}}
</p>
<p>
{{postalCode}}, {{city}}
</p>
</a>
</custom> </custom>
</htk-repeater> </htk-repeater>
</div> </div>

View File

@ -0,0 +1,35 @@
Hedera.Address = new Class
({
Extends: Hedera.Form
,activate: function ()
{
this.$.model.setInfo ('a', 'myAddress', 'hedera', ['id'], 'id');
this.$.model.setDefault ('clientFk', 'a',
new Sql.Function ({schema: 'account', name: 'userGetId'}));
}
,onStatusChange: function (form)
{
if (form.ready && this.$.params.$.address == 0)
form.insertRow ();
}
,onOperationsDone: function ()
{
Htk.Toast.showMessage (_('AddressChangedSuccessfully'));
this.onReturnClick ();
}
,onAcceptClick: function ()
{
this.$.iter.performOperations ();
}
,onReturnClick: function ()
{
window.history.back();
}
});

View File

@ -1,20 +0,0 @@
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml'),
activate() {
this.$.model.setInfo('a', 'myAddress', 'hedera', ['id'], 'id');
this.$.model.setDefault('clientFk', 'a',
new Sql.Function({schema: 'account', name: 'myUser_getId'}));
},
onStatusChange() {
if (this.$.iter.ready && this.hash.$.address == 0)
this.$.iter.insertRow();
},
onOperationsDone() {
Htk.Toast.showMessage(_('AddressChangedSuccessfully'));
window.history.back()
}
});

View File

@ -0,0 +1 @@

View File

@ -3,81 +3,75 @@
<vn-lot-query id="params"> <vn-lot-query id="params">
<vn-spec name="address" type="Number"/> <vn-spec name="address" type="Number"/>
</vn-lot-query> </vn-lot-query>
<db-form id="iter" on-status-changed="this.onStatusChange()"> <db-form id="iter" on-status-changed="onStatusChange">
<db-model <db-model
id="model" id="model"
property="model" property="model"
updatable="true" updatable="true"
mode="ON_DEMAND" mode="ON_DEMAND"
lot="params" lot="params"
on-operations-done="this.onOperationsDone()"> on-operations-done="onOperationsDone">
SELECT a.id, a.street, a.nickname, a.city, SELECT a.id, a.street, a.nickname, a.city,
a.postalCode, a.provinceFk, p.countryFk a.postalCode, a.provinceFk, c.id countryFk
FROM myAddress a FROM myAddress a
LEFT JOIN vn.province p ON p.id = a.provinceFk LEFT JOIN vn.province p ON p.id = a.provinceFk
JOIN vn.country c ON c.id = p.countryFk
WHERE a.id = #address WHERE a.id = #address
</db-model> </db-model>
</db-form> </db-form>
</vn-group> </vn-group>
<div id="title"> <h1 id="title">
<h1><t>Configuration</t></h1> _AddEditAddress
</div> </h1>
<div id="actions"> <div id="actions">
<htk-bar-button
icon="ok"
tip="_Accept"
on-click="onAcceptClick"/>
<htk-bar-button <htk-bar-button
icon="close" icon="close"
tip="_Return" tip="_Return"
on-click="window.history.back()"/> on-click="onReturnClick"/>
<htk-bar-button
icon="check"
tip="_Accept"
on-click="iter.performOperations()"/>
</div> </div>
<div id="form" class="hedera-address"> <div id="main" class="address">
<div class="form box vn-w-sm vn-pa-lg"> <div class="card form">
<h5 class="vn-mb-md"> <div>
<t>AddEditAddress</t> <label>_Name</label>
</h5> <htk-entry lot="iter" name="nickname"/>
<div class="form-group">
<htk-entry
placeholder="_Name"
form="iter" column="nickname"/>
</div> </div>
<div class="form-group"> <div>
<htk-entry <label>_Address</label>
placeholder="_Address" <htk-entry lot="iter" name="street"/>
form="iter" column="street"/>
</div> </div>
<div class="form-group"> <div>
<htk-entry <label>_City</label>
placeholder="_City" <htk-entry lot="iter" name="city"/>
form="iter" column="city"/>
</div> </div>
<div class="form-group"> <div>
<htk-entry <label>_ZipCode</label>
placeholder="_ZipCode" <htk-entry lot="iter" name="postalCode"/>
form="iter" column="postalCode"/>
</div> </div>
<div class="form-group"> <div>
<htk-combo <label>_Country</label>
placeholder="_Country" <htk-combo>
form="iter" column="countryFk" <vn-param
id="country" id="country"
one-way="true" property="param"
one-time="true"> lot="iter"
name="country"
one-way="true"/>
<db-model property="model"> <db-model property="model">
SELECT id, name FROM vn.country SELECT id, country FROM vn.country
ORDER BY name ORDER BY country
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>
<div class="form-group"> <div>
<htk-combo <label>_Province</label>
placeholder="_Province" <htk-combo lot="iter" name="province_id">
column="provinceFk" <db-model property="model" lot="iter">
form="iter">
<db-model property="model" lot="country">
SELECT id, name FROM vn.province SELECT id, name FROM vn.province
WHERE countryFk = #id WHERE countryFk = #country
ORDER BY name ORDER BY name
</db-model> </db-model>
</htk-combo> </htk-combo>

View File

@ -0,0 +1,95 @@
Hedera.Conf = new Class
({
Extends: Hedera.Form
,activate: function ()
{
this.$.userModel.setInfo ('c', 'myClient', 'hedera');
if (this.hash.$.changePass)
this.onPassChangeClick ();
}
,onChangePassOpen: function ()
{
this.hash.assign ({changePass: true});
}
,onChangePassClose: function ()
{
this.hash.assign ({changePass: undefined});
}
,onPassChangeClick: function ()
{
this.$.oldPassword.value = '';
this.$.newPassword.value = '';
this.$.repeatPassword.value = '';
var recoverPass = this.$.user.get ('recoverPass');
this.$.oldPassword.style.display = recoverPass ? 'none' : 'block';
this.$.changePassword.show ();
var focusInput;
if (recoverPass)
focusInput = this.$.newPassword;
else
focusInput = this.$.oldPassword;
focusInput.focus ();
focusInput.select ();
}
,onPassModifyClick: function ()
{
try {
var oldPassword = this.$.oldPassword.value;
var newPassword = this.$.newPassword.value;
var repeatedPassword = this.$.repeatPassword.value;
if (newPassword == '' && repeatedPassword == '')
throw new Error (_('Passwords empty'));
if (newPassword !== repeatedPassword)
throw new Error (_('Passwords doesn\'t match'));
var params = {
oldPassword: oldPassword,
newPassword: newPassword
};
this.conn.send ('core/change-password', params,
this._onPassChange.bind (this));
}
catch (e)
{
Htk.Toast.showError (e.message);
}
}
,_onPassChange: function (json, error)
{
if (json)
{
this.$.changePassword.hide ();
Htk.Toast.showMessage (_('Password changed!'));
this.$.user.refresh ();
}
else
{
Htk.Toast.showError (error.message);
this.$.oldPassword.select ();
}
}
,onPassInfoClick: function ()
{
this.$.passwordInfo.show ();
}
,onAddressesClick: function ()
{
this.hash.setAll ({form: 'account/address-list'});
}
});

View File

@ -1,13 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml'),
activate() {
this.$.userModel.setInfo('c', 'myClient', 'hedera');
this.$.userModel.setInfo('u', 'myUser', 'account');
this.$.changePassword.conn = this.conn
this.$.changePassword.user = this.gui.user
}
});

View File

@ -1,5 +1,4 @@
Configuration: Configuració Configuration: Configuració
Personal information: Dades personals
Username: Nom d'usuari Username: Nom d'usuari
Password: Contrasenya Password: Contrasenya
Email: Correu electrònic Email: Correu electrònic
@ -10,7 +9,7 @@ Receive invoices by email: Rebre factures per correu electrònic
Old password: Contrasenya antiga Old password: Contrasenya antiga
New password: Nova contrasenya New password: Nova contrasenya
Repeat password: Repetir contrasenya Repeat password: Repetir contrasenya
Requirements: Requisits Info: Info
Modify: Modificar Modify: Modificar
Password requirements: Requisits de contrasenya Password requirements: Requisits de contrasenya
characters long: caràcters de longitud characters long: caràcters de longitud

View File

@ -1,5 +1,4 @@
Configuration: Configuration Configuration: Configuration
Personal information: Personal information
Username: Username Username: Username
Password: Password Password: Password
Email: Email Email: Email
@ -10,7 +9,7 @@ Receive invoices by email: Receive invoices by email
Old password: Old password Old password: Old password
New password: New password New password: New password
Repeat password: Repeat password Repeat password: Repeat password
Requirements: Requirements Info: Info
Modify: Modify Modify: Modify
Password requirements: Password requirements Password requirements: Password requirements
characters long: characters long characters long: characters long

View File

@ -1,5 +1,4 @@
Configuration: Configuración Configuration: Configuración
Personal information: Datos personales
Username: Nombre de usuario Username: Nombre de usuario
Password: Contraseña Password: Contraseña
Email: Correo electrónico Email: Correo electrónico
@ -10,7 +9,7 @@ Receive invoices by email: Recibir facturas por correo electrónico
Old password: Contaseña antigua Old password: Contaseña antigua
New password: Nueva contraseña New password: Nueva contraseña
Repeat password: Repetir contraseña Repeat password: Repetir contraseña
Requirements: Requisitos Info: Info
Modify: Modificar Modify: Modificar
Password requirements: Requisitos de constraseña Password requirements: Requisitos de constraseña
characters long: carácteres de longitud characters long: carácteres de longitud

View File

@ -1,5 +1,4 @@
Configuration: Configuration Configuration: Configuration
Personal information: Informations personnelles
Username: Utilisateur Username: Utilisateur
Password: Mot de passe Password: Mot de passe
Email: Courriel Email: Courriel
@ -10,7 +9,7 @@ Receive invoices by email: Recevoir des factures par e-mail
Old password: Ancien mot de passe Old password: Ancien mot de passe
New password: Nouveau mot de passe New password: Nouveau mot de passe
Repeat password: Répéter le mot de passe Repeat password: Répéter le mot de passe
Requirements: Exigences Info: Info
Modify: Modifier Modify: Modifier
Password requirements: Mot de passe exigences Password requirements: Mot de passe exigences
characters long: Longs caractères characters long: Longs caractères

View File

@ -1,26 +1,25 @@
Configuration: Configuração Configuration: Configuração
Personal information: Dados pessoais
Username: Nome de usuario Username: Nome de usuario
Password: Palavra-passe Password: Palavra-Passe
Email: E-Mail Email: E-Mail
Display name: Nome para mostrar Display name: Nome para mostrar
Language: Idioma Language: Idioma
Billing: Facturação Billing: Facturação
Receive invoices by email: Receber facturas por e-mail Receive invoices by email: Receber facturas por e-mail
Old password: Palavra-passe antiga Old password: Palavra-Passe antiga
New password: Nova Palavra-passe New password: Nova Palavra-Passe
Repeat password: Repetir Palavra-passe Repeat password: Repetir Palavra-Passe
Requirements: Requisitos Info: Info
Modify: Modificar Modify: Modificar
Password requirements: Requisitos de Palavra-passe Password requirements: Requisitos de Palavra-Passe
characters long: caracteres characters long: caracteres
alphabetic characters: caracteres alfabéticos alphabetic characters: caracteres alfabéticos
capital letters: letras maiúsculas capital letters: letras maiúsculas
digits: dígitos digits: dígitos
symbols: 'símbolos. Ej: $%&.' symbols: 'símbolos. Ej: $%&.'
Password changed!: Palavra-passe Modificada! Password changed!: Palavra-Passe Modificada!
Password doesn't meet the requirements: Palavra-passe não atende aos requisitos Password doesn't meet the requirements: Palavra-Passe não atende aos requisitos
Passwords doesn't match: As Palavras-Passe não coincidem! Passwords doesn't match: As Palavras-Passe não coincidem!
Passwords empty: Palavra-passe vazia Passwords empty: Palavra-Passe vazia
Addresses: Moradas Addresses: Moradas
Change password: Mudar Palavra-passe Change password: Mudar Palavra-Passe

View File

@ -0,0 +1,20 @@
.conf .form-group input[type=password]
{
margin-bottom: 0.5em;
}
.pass-change
{
max-width: 15em;
}
.pass-info
{
max-width: 17em;
}
.pass-info ul
{
list-style-type: none;
padding-left: 1.5em;
}

View File

@ -1,4 +0,0 @@
.pass-info ul {
list-style-type: none;
}

View File

@ -1,72 +1,118 @@
<vn> <vn>
<vn-group> <vn-group>
<db-form id="user-form"> <db-lot id="password-form">
SELECT length, nAlpha, nUpper, nDigits, nPunct
FROM account.userPassword
</db-lot>
<db-form id="user">
<db-model property="model" id="user-model" updatable="true"> <db-model property="model" id="user-model" updatable="true">
SELECT u.id, u.name, u.email, u.nickname, SELECT u.id, u.name, u.email, u.recoverPass,
u.lang, c.isToBeMailed, c.id clientFk u.nickname, u.lang, c.isToBeMailed, c.id clientFk
FROM account.myUser u FROM account.userView u
LEFT JOIN myClient c LEFT JOIN myClient c
ON u.id = c.id ON u.id = c.id
</db-model> </db-model>
</db-form> </db-form>
</vn-group> </vn-group>
<div id="title"> <h1 id="title">
<h1><t>Configuration</t></h1> _Configuration
</div> </h1>
<div id="actions"> <div id="actions">
<htk-bar-button <htk-bar-button
icon="place" icon="place"
tip="_Addresses" tip="_Addresses"
on-click="hash.setAll({form: 'account/address-list'})"/> on-click="onAddressesClick"/>
<htk-bar-button <htk-bar-button
icon="lock_reset" icon="preferences"
tip="_Change password" tip="_Change password"
on-click="this.$.changePassword.open()"/> on-click="onPassChangeClick"/>
</div> </div>
<div id="form" class="conf"> <div id="main" class="conf">
<div class="form box vn-w-sm vn-pa-lg"> <div class="card form">
<h5 class="vn-mb-md"> <div>
<t>Personal information</t> <label for="user-name">_Username</label>
</h5> <htk-text lot="user" name="name"/>
<div class="form-group">
<htk-entry
placeholder="_Username"
disabled="true"
form="user-form"
column="name"/>
</div> </div>
<div class="form-group"> <div>
<htk-entry <label for="email">_Email</label>
placeholder="_Email" <htk-entry lot="user" name="email"></htk-entry>
form="user-form"
column="email">
</htk-entry>
</div> </div>
<div class="form-group"> <div>
<htk-entry <label for="nickname">_Display name</label>
placeholder="_Display name" <htk-entry lot="user" name="nickname"/>
form="user-form"
column="nickname"/>
</div> </div>
<div class="form-group"> <div>
<htk-combo <label for="lang">_Language</label>
placeholder="_Language" <htk-combo lot="user" name="lang"
form="user-form" value-field="code">
column="lang">
<db-model property="model"> <db-model property="model">
<custom> SELECT code, name FROM language WHERE active
SELECT code, name FROM language WHERE isActive
</custom>
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>
<div class="form-group"> <div>
<label> <span>
<htk-check form="user-form" column="isToBeMailed"/> <htk-check id="mail" lot="user" name="isToBeMailed"/>
<t>Receive invoices by email</t> <label for="mail">_Receive invoices by email</label>
</label> </span>
</div> </div>
</div> </div>
</div> </div>
<htk-change-password id="change-password"/> <htk-popup
id="change-password"
on-open="onChangePassOpen"
on-closed="onChangePassClose"
modal="true">
<div property="child-node" class="htk-dialog pass-change">
<div>
<input
id="old-password"
type="password"
placeholder="_Old password"/>
<input
id="new-password"
type="password"
placeholder="_New password"/>
<input
id="repeat-password"
type="password"
placeholder="_Repeat password"/>
</div>
<div class="button-bar">
<button class="thin" on-click="onPassModifyClick">
_Modify
</button>
<button class="thin" on-click="onPassInfoClick">
_Info
</button>
<div class="clear"/>
</div>
</div>
</htk-popup>
<htk-popup
id="password-info"
modal="true">
<div property="child-node" class="htk-dialog pass-info">
<h3>
_Password requirements
</h3>
<ul>
<li>
{{passwordForm.length}} <span>_characters long</span>
</li>
<li>
{{passwordForm.nAlpha}} <span>_alphabetic characters</span>
</li>
<li>
{{passwordForm.nUpper}} <span>_capital letters</span>
</li>
<li>
{{passwordForm.nDigits}} <span>_digits</span>
</li>
<li>
{{passwordForm.nPunct}} <span>_symbols</span>
</li>
</ul>
</div>
</htk-popup>
</vn> </vn>

View File

@ -0,0 +1,6 @@
Hedera.AccessLog = new Class
({
Extends: Hedera.Form
});

View File

@ -1,7 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
});

View File

@ -1,6 +1,6 @@
AccessLog: Registro de acessos AccessLog: Registro de acessos
'UserNumber:': 'Nº utilizador:' 'UserNumber:': 'Nº usuario:'
'User:': 'Utilizador:' 'User:': 'Usuario:'
'Phone:': 'Telefone:' 'Phone:': 'Telefone:'
'Mobile:': 'Telemóvel:' 'Mobile:': 'Telemóvel:'
Access: Acceso Access: Acceso

View File

@ -0,0 +1,12 @@
.access-log .card
{
max-width: 28em;
}
.access-log .form > p
{
font-size: 1.2em;
margin: .1em 0;
}

View File

@ -1,12 +0,0 @@
.access-log .form > p {
font-size: 1.2rem;
margin: .1em 0;
}
/* List */
.access-log .htk-list {
margin-top: 16px;
}

View File

@ -1,50 +1,54 @@
<vn> <vn>
<vn-group> <vn-group>
<db-form v-model="user"> <db-form id="client">
<db-model property="model" lot="hash"> <db-model property="model" lot="hash">
SELECT u.id, u.name user, u.nickname, u.email, c.phone, r.name role SELECT Id_Cliente, Cliente, Telefono, movil
FROM account.user u FROM vn2008.Clientes WHERE Id_Cliente = #user
JOIN account.role r ON r.id = u.role
LEFT JOIN vn.client c ON c.id = u.id
WHERE u.id = #user
</db-model> </db-model>
</db-form> </db-form>
</vn-group> </vn-group>
<div id="title"> <h1 id="title">
<h1><t>AccessLog</t></h1> _AccessLog
</h1>
<div id="main" class="access-log">
<div class="card form">
<p>
{{client.Id_Cliente}}
</p>
<p>
{{client.Cliente}}
</p>
<p>
{{client.Telefono}}
</p>
<p>
{{client.movil}}
</p>
</div> </div>
<div id="form" class="access-log"> <div class="card">
<div class="box vn-w-xs vn-pa-lg"> <div>
<div class="form"> <htk-repeater>
<h4>{{user.nickname}}</h4>
<p>#{{user.id}} - {{user.user}}</p>
<p>{{user.role}}</p>
<p>{{user.email}}</p>
<p>{{user.phone}}</p>
</div>
</div>
<htk-repeater form-id="iter" class="box vn-w-xs htk-list vn-mt-md">
<db-model property="model" lot="hash"> <db-model property="model" lot="hash">
SELECT u.stamp, a.platform, a.browser, a.version, a.javascript, a.cookies SELECT u.stamp, a.platform, a.browser, a.version, a.javascript, a.cookies
FROM visitUser u FROM visitUser u
JOIN visitAccess c ON c.id = u.accessFk JOIN visitAccess c ON c.id = u.access
JOIN visitAgent a ON a.id = c.agentFk JOIN visitAgent a ON a.id = c.agent
WHERE u.userFk = #user WHERE u.user = #user
ORDER BY u.stamp DESC ORDER BY u.stamp DESC
LIMIT 8 LIMIT 8
</db-model> </db-model>
<custom> <custom>
<div class="item"> <div class="list-row">
<div class="content">
<p> <p>
{{Vn.Value.format(iter.stamp, _('%a, %e %b %Y at %T'))}} <htk-text lot="iter" name="stamp" format="_%a, %e %b %Y at %T"/>
</p> </p>
<p> <p>
{{iter.platform}} - {{iter.browser}} {{iter.version}} {{platform}} - {{browser}} {{version}}
</p> </p>
</div> </div>
</div>
</custom> </custom>
</htk-repeater> </htk-repeater>
</div> </div>
</div>
</div>
</vn> </vn>

View File

@ -0,0 +1,45 @@
Hedera.Connections = new Class
({
Extends: Hedera.Form
,_timeoutId: null
,onModelStatusChange: function (model)
{
if (!model.ready)
return;
if (this._timeoutId)
clearTimeout (this._timeoutId);
this._timeoutId = setTimeout (this.onRefreshClick.bind (this), 60000);
}
,deactivate: function ()
{
clearTimeout (this._timeoutId);
}
,onRefreshClick: function ()
{
this.$.sessions.refresh ();
}
,onChangeUserClick: function (button, form)
{
this.gui.supplantUser (form.get ('user'),
this._onUserSupplant.bind (this));
}
,_onUserSupplant: function ()
{
this.hash.setAll ({form: 'ecomerce/orders'});
}
,sessionsFunc: function ()
{
return 1;
}
});

View File

@ -1,33 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
,_timeoutId: null
,onModelStatusChange() {
if (!this.$.sessions.ready)
return;
if (this._timeoutId)
clearTimeout(this._timeoutId);
this._timeoutId = setTimeout(
() => this.$.sessions.refresh(), 60000);
}
,deactivate() {
clearTimeout(this._timeoutId);
}
,async onChangeUserClick(userName) {
await this.gui.supplantUser(userName);
this.hash.setAll({form: 'ecomerce/orders'});
}
,sessionsFunc() {
return 1;
}
});

View File

@ -0,0 +1,10 @@
.action-bar .connections-sum
{
padding: .4em;
margin-top: .9em;
margin-right: .5em;
background-color: #1e88e5;
border-radius: 0.1em;
box-shadow: 0 0 0.4em #666;
}

View File

@ -1,9 +0,0 @@
.action-bar .connections-sum {
padding: .4em;
background-color: #1e88e5;
border-radius: .1em;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}

View File

@ -1,12 +1,12 @@
<vn> <vn>
<div id="title"> <h1 id="title">
<h1><t>Connections</t></h1> _Connections
</div> </h1>
<div id="actions"> <div id="actions">
<htk-bar-button <htk-bar-button
icon="refresh" icon="refresh"
tip="_Refresh" tip="_Refresh"
on-click="sessions.refresh()"/> on-click="onRefreshClick"/>
<div class="connections-sum"> <div class="connections-sum">
<htk-text> <htk-text>
<db-calc-sum <db-calc-sum
@ -14,50 +14,48 @@
model="sessions" model="sessions"
func="sessionsFunc"/> func="sessionsFunc"/>
</htk-text> </htk-text>
<t>connections</t> _connections
</div> </div>
</div> </div>
<div id="form" class="connections"> <div id="main" class="connections">
<htk-repeater form-id="iter" class="box htk-list vn-w-xs"> <div class="card list">
<db-model <htk-repeater>
property="model" <db-model property="model" id="sessions" on-status-changed="onModelStatusChange">
id="sessions" SELECT vu.user userId, vu.stamp, u.nickname, s.lastUpdate,
on-status-changed="this.onModelStatusChange()">
SELECT vu.userFk userId, vu.stamp, u.nickname, s.lastUpdate,
a.platform, a.browser, a.version, u.name user a.platform, a.browser, a.version, u.name user
FROM userSession s FROM userSession s
JOIN visitUser vu ON vu.id = s.userVisitFk JOIN visitUser vu ON vu.id = s.userVisit
JOIN visitAccess ac ON ac.id = vu.accessFk JOIN visitAccess ac ON ac.id = vu.access
JOIN visitAgent a ON a.id = ac.agentFk JOIN visitAgent a ON a.id = ac.agent
JOIN visit v ON v.id = a.visitFk JOIN visit v ON v.id = a.visit
JOIN account.user u ON u.id = vu.userFk JOIN account.user u ON u.id = vu.user
ORDER BY lastUpdate DESC ORDER BY lastUpdate DESC
</db-model> </db-model>
<custom> <custom>
<a class="item" <a href="#!form=admin/access-log&amp;user={{userId}}"
href="{{`#!form=admin/access-log&amp;user=${iter.userId}`}}" class="list-row"
title="_Access log"> title="_Access log">
<div class="content"> <div class="actions">
<p class="important">
{{iter.nickname}}
</p>
<p>
{{Vn.Value.format(iter.stamp, '%a, %T')}} -
{{Vn.Value.format(iter.lastUpdate, '%T')}}
</p>
<p>
{{iter.platform}} - {{iter.browser}} {{iter.version}}
</p>
</div>
<div class="actions"
on-click="$event.preventDefault()">
<htk-button <htk-button
lot="iter"
name="id"
tip="_Supplant user" tip="_Supplant user"
icon="supervisor_account" icon="incognito"
on-click="this.onChangeUserClick(iter.user)"/> on-click="onChangeUserClick"/>
</div> </div>
<p class="important">
{{nickname}}
</p>
<p>
<htk-text lot="iter" name="stamp" format="%a, %T"/> -
<htk-text lot="iter" name="lastUpdate" format="%T"/>
</p>
<p>
{{platform}} - {{browser}} {{version}}
</p>
</a> </a>
</custom> </custom>
</htk-repeater> </htk-repeater>
</div> </div>
</div>
</vn> </vn>

View File

@ -1,10 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml'),
activate() {
this.$.items.setInfo('i', 'item', 'vn', ['id']);
}
});

View File

@ -0,0 +1,6 @@
Hedera.Items = new Class
({
Extends: Hedera.Form
});

View File

@ -0,0 +1,23 @@
/* Row */
.items .list-row > .photo
{
margin-right: 1em;
float: left;
border-radius: 50%;
height: 3.2em;
width: 3.2em;
}
.items .list-row > p
{
margin: .1em 0;
margin-left: 5em;
}
/* Topbar */
.action-bar .htk-search-entry
{
margin: .8em .6em;
}

View File

@ -1,6 +0,0 @@
.items .item .photo {
border-radius: 10px;
height: 80px;
width: 80px;
}

View File

@ -1,57 +1,44 @@
<vn> <vn>
<div id="title"> <h1 id="title">
<h1><t>Items</t></h1> _Items
</div> </h1>
<div id="actions"> <div id="actions">
<htk-search-entry form="hash" column="search"/> <htk-search-entry lot="hash" name="search"/>
</div> </div>
<div id="form" class="items"> <div id="main" class="items">
<htk-repeater <div class="card list">
class="htk-list rows box vn-w-xs" <htk-repeater empty-message="_Enter a search term">
form-id="iter" <db-model property="model" lot="hash">
empty-message="_Enter a search term"> SELECT Id_Article, Article, Medida, Categoria, Foto
<db-model property="model" id="items" lot="hash"> FROM vn2008.Articles
SELECT i.id, i.longName, i.size, i.category, WHERE Article LIKE CONCAT('%', #search, '%')
i.value5, i.value6, i.value7, OR Id_Article = #search
i.image, im.updated ORDER BY Article LIMIT 50
FROM vn.item i
LEFT JOIN image im
ON im.collectionFk = 'catalog'
AND im.name = i.image
WHERE i.isActive
AND (i.longName LIKE CONCAT('%', #search, '%') OR i.id = #search)
ORDER BY i.longName LIMIT 50
</db-model> </db-model>
<custom> <custom>
<div class="item"> <div class="list-row">
<div class="side vn-mr-md">
<htk-image <htk-image
form="$iter" lot="iter"
column="image" name="Foto"
stamp-column="updated"
class="photo" class="photo"
directory="catalog" directory="catalog"
subdir="200x200" subdir="200x200"
full-dir="1600x900" full-dir="900x900"
editable="true" editable="true"
conn="conn"/> conn="conn"/>
</div> <p class="concept">
<div class="content"> {{Article}} {{Medida}} {{Categoria}}
<p class="important">
{{iter.longName}}
</p>
<p class="tags">
{{iter.value5}} {{iter.value6}} {{iter.value7}}
</p> </p>
<p> <p>
{{iter.id}} @{{Id_Article}}
</p> </p>
<p> <p>
{{iter.image}} {{Foto}}
</p> </p>
</div> <div class="clear"/>
</div> </div>
</custom> </custom>
</htk-repeater> </htk-repeater>
</div> </div>
</div>
</vn> </vn>

View File

@ -1,7 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
});

View File

@ -0,0 +1,6 @@
Hedera.Links = new Class
({
Extends: Hedera.Form
});

View File

@ -0,0 +1,13 @@
.cpanel .list-row:hover
{
background-color: rgba(1, 1, 1, 0.05);
}
.cpanel .list-row > .htk-image
{
margin: 0;
margin-right: 1em;
float: left;
height: 2.8em;
width: 2.8em;
}

View File

@ -1,50 +0,0 @@
.cpanel .items > div {
max-width: 900px;
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 16px;
margin: 0 auto;
}
.cpanel .item {
display: flex;
flex-direction: column;
align-items: center;
margin: 0;
width: 140px;
padding: 15px;
text-align: center;
transition: background-color 250ms ease-out;
}
.cpanel .item:hover {
background-color: rgba(1, 1, 1, 0.05);
}
.cpanel .item > .htk-image {
display: flex;
justify-content: center;
align-items: center;
margin: 0;
float: left;
height: 80px;
}
.cpanel .item > .htk-image > img {
max-height: 60px;
max-width: 60px;
padding: 0;
}
.cpanel .item > h6 {
flex: none;
margin: .1em 0;
font-size: .9rem;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.cpanel .item > .text-secondary {
flex: none;
margin: 0;
font-size: .8rem;
height: 40px;
overflow: hidden;
}

View File

@ -1,31 +1,29 @@
<vn> <vn>
<div id="title"> <h1 id="title">
<h1><t>ControlPanel</t></h1> _ControlPanel
</div> </h1>
<div id="form" class="cpanel"> <div id="main" class="cpanel">
<htk-repeater form-id="iter" class="items"> <div class="card list">
<htk-repeater>
<db-model property="model"> <db-model property="model">
<custom>
SELECT image, name, description, link FROM link SELECT image, name, description, link FROM link
ORDER BY name ORDER BY name
</custom>
</db-model> </db-model>
<custom> <custom>
<a class="item box" <a class="list-row" href="{{link}}" target="_blank">
href="{{iter.link}}"
target="_blank">
<htk-image <htk-image
value="{{iter.image}}" value="{{image}}"
directory="link" directory="link"
subdir="full"/> subdir="full"/>
<h6> <p class="important">
{{iter.name}} {{name}}
</h6> </p>
<p class="text-secondary"> <p>
{{iter.description}} {{description}}
</p> </p>
</a> </a>
</custom> </custom>
</htk-repeater> </htk-repeater>
</div> </div>
</div>
</vn> </vn>

View File

@ -1,201 +0,0 @@
import './style.scss';
var Status = {
NONE : 0
,WAITING : 1
,UPLOADING : 2
,UPLOADED : 3
};
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
,filesData: []
,uploadCount: 0
,isUploading: false
,activate() {
this.$.schema.value = 'catalog';
}
,addFiles(files) {
if (!files)
return;
for (var i = 0; i < files.length; i++)
this.addFile(files[i]);
}
,addFile(file) {
var doc = document;
var li = doc.createElement('div');
var div = doc.createElement('div');
div.className = 'thumb';
li.appendChild(div);
var thumb = doc.createElement('img');
thumb.file = file;
div.appendChild(thumb);
var reader = new FileReader();
reader.onload = function(e) {
thumb.src = e.target.result;
};
reader.readAsDataURL(file);
var name = doc.createElement('input');
name.type = 'text';
name.value = getFileName(file.name);
li.appendChild(name);
var statusNode = doc.createElement('div');
statusNode.className = 'status';
li.appendChild(statusNode);
var fileData = {
li: li,
file: file,
name: name,
statusNode: statusNode
};
var button = new Htk.Button({
tip: 'Remove',
icon: 'delete'
});
button.node.addEventListener('click',
() => this.onFileRemove(fileData));
li.appendChild(button.node);
this.filesData.push(fileData);
this.$.fileList.appendChild(li);
this.setImageStatus(fileData, Status.NONE, 'add', _('Pending upload'));
}
,async onUploadClick() {
if (this.isUploading) return;
const uploadQueue = [];
let hasFiles = false;
for (const fileData of this.filesData) {
if (fileData.status !== Status.NONE) continue;
this.setImageStatus(
fileData, Status.WAITING, 'cloud_upload', _('Waiting for upload'));
fileData.name.disabled = true;
uploadQueue.push(fileData);
hasFiles = true;
}
if (!hasFiles) {
Htk.Toast.showWarning(_('There are no files to upload'));
return;
}
this.isUploading = true;
let hasErrors = false;
for (const fileData of uploadQueue) {
this.setImageStatus(
fileData, Status.UPLOADING, 'upload', _('Uploading file'));
const formData = new FormData();
formData.append('updateMatching', this.$.updateMatching.value);
formData.append('image', fileData.file);
formData.append('name', fileData.name.value);
formData.append('schema', this.$.schema.value);
formData.append('srv', 'json:image/upload');
try {
await this.conn.sendFormData(formData);
this.setImageStatus(
fileData, Status.UPLOADED, 'cloud_done', _('Image uploaded'));
} catch(err) {
this.setImageStatus(
fileData, Status.NONE, 'error', err.message);
fileData.name.disabled = false;
hasErrors = true;
}
}
this.isUploading = false;
if (hasErrors)
Htk.Toast.showError(_('Some errors happened on upload'));
else
Htk.Toast.showMessage(_('Upload finished successfully'));
}
,setImageStatus(fileData, status, icon, title) {
fileData.status = status;
var statusNode = fileData.statusNode;
Vn.Node.removeChilds(statusNode);
var iconNode = new Htk.Icon({name: icon});
statusNode.appendChild(iconNode.node);
statusNode.title = title ? title : '';
}
,onFileRemove(fileData) {
this.$.fileList.removeChild(fileData.li);
for (var i = 0; i < this.filesData.length; i++)
if (this.filesData[i] === fileData) {
this.filesData.splice(i, 1);
break;
}
}
,onClearClick() {
this.filesData = [];
Vn.Node.removeChilds(this.$.fileList);
}
,onDropzoneClick() {
this.$.file.click();
}
,onFileChange() {
this.addFiles(this.$.file.files);
}
,onDragEnter() {
this.$.dropzone.classList.add('dragover');
}
,onDragLeave() {
this.$.dropzone.classList.remove('dragover');
}
,onDragOver(event) {
event.preventDefault();
}
,onDragEnd(event) {
this.$.dropzone.classList.remove('dragover');
event.dataTransfer.clearData();
}
,onDrop(event) {
event.preventDefault();
this.addFiles(event.dataTransfer.files);
}
});
function getFileName(path) {
var barIndex = path.lastIndexOf('/');
if (barIndex === -1)
barIndex = path.lastIndexOf('\\');
if (barIndex === -1)
barIndex = 0;
var dotIndex = path.lastIndexOf('.');
if (dotIndex === -1)
dotIndex = 0;
return path.substr(barIndex, dotIndex);
}

View File

@ -1,8 +1,6 @@
Images: Imatges Images: Imatges
Collection: Col·lecció Collection: Col·lecció
Click or drop files here: Prem o deixa anar els arxius aquí Click or drop files here: Prem o deixa anar els arxius aquí
Pending upload: Pujada pendent
Update items with matching id: Actualitzar els elements amb id coincident
Clear all: Netejar tot Clear all: Netejar tot
Upload files: Pujar arxius Upload files: Pujar arxius
Waiting for upload: Esperant per pujar Waiting for upload: Esperant per pujar

View File

@ -1,8 +1,6 @@
Images: Images Images: Images
Collection: Collection Collection: Collection
Click or drop files here: Click or drop files here Click or drop files here: Click or drop files here
Pending upload: Pending upload
Update items with matching id: Update items with matching id
Clear all: Clear all Clear all: Clear all
Upload files: Upload files Upload files: Upload files
Waiting for upload: Waiting for upload Waiting for upload: Waiting for upload

View File

@ -1,8 +1,6 @@
Images: Imágenes Images: Imágenes
Collection: Colección Collection: Colección
Click or drop files here: Pulsa o suelta los archivos aquí Click or drop files here: Pulsa o suelta los archivos aquí
Pending upload: Subida pendiente
Update items with matching id: Actualizar ítems con id coincidente
Clear all: Limpiar todo Clear all: Limpiar todo
Upload files: Subir archivos Upload files: Subir archivos
Waiting for upload: Esperando para subir Waiting for upload: Esperando para subir

View File

@ -1,8 +1,6 @@
Images: Images Images: Images
Collection: Collection Collection: Collection
Click or drop files here: Cliquez ici ou déposer des fichiers Click or drop files here: Cliquez ici ou déposer des fichiers
Pending upload: Hausse en attente
Update items with matching id: Mettre à jour les éléments avec l'identifiant correspondant
Clear all: Tout effacer Clear all: Tout effacer
Upload files: Upload Files Upload files: Upload Files
Waiting for upload: En attente de télécharger Waiting for upload: En attente de télécharger

View File

@ -1,8 +1,6 @@
Images: Imagens Images: Imagens
Collection: Coleção Collection: Coleção
Click or drop files here: Clique ou solte arquivos aqui Click or drop files here: Clique ou solte arquivos aqui
Pending upload: Ascensão pendente
Update items with matching id: Atualizar itens com id correspondente
Clear all: Limpar tudo Clear all: Limpar tudo
Upload files: Fazer upload de arquivos Upload files: Fazer upload de arquivos
Waiting for upload: Esperando para enviar Waiting for upload: Esperando para enviar

View File

@ -0,0 +1,235 @@
(function() {
var Status = {
NONE : 0
,WAITING : 1
,UPLOADING : 2
,UPLOADED : 3
};
Hedera.Photos = new Class
({
Extends: Hedera.Form
,filesData: []
,uploadCount: 0
,errors: false
,uploadQueue: []
,isUploading: false
,activate: function ()
{
this.$.schema.value = 'catalog';
}
,addFiles: function (files)
{
if (!files)
return;
for (var i = 0; i < files.length; i++)
this.addFile (files[i]);
}
,addFile: function (file)
{
var doc = document;
var div = doc.createElement ('div');
var button = new Htk.Button ({
tip: 'Remove',
icon: 'delete'
});
button.on ('click', this.onFileRemove, this);
div.appendChild (button.node);
var thumb = doc.createElement ('img');
thumb.file = file;
thumb.className = 'thumb';
div.appendChild (thumb);
var reader = new FileReader ();
reader.onload = function (e) { thumb.src = e.target.result; };
reader.readAsDataURL(file);
var name = doc.createElement ('input');
name.type = 'text';
name.value = getFileName (file.name);
div.appendChild (name);
var statusNode = doc.createElement ('span');
statusNode.className = 'status';
div.appendChild (statusNode);
var fileData = {
div: div,
file: file,
name: name,
statusNode: statusNode,
status: Status.NONE
};
this.filesData.push (fileData);
button.value = fileData;
this.$.fileList.appendChild (div);
}
,onUploadClick: function ()
{
var filesData = this.filesData;
var count = 0;
for (var i = 0; i < filesData.length; i++)
{
var fileData = filesData[i];
if (fileData.status === Status.NONE)
{
this.setImageStatus (
fileData, Status.WAITING, 'cloud-upload', _('Waiting for upload'));
fileData.name.disabled = true;
this.uploadQueue.push (fileData);
count++;
}
}
if (count === 0)
Htk.Toast.showWarning (_('There are no files to upload'));
else
this.uploadNextFile ();
}
,uploadNextFile: function ()
{
if (this.isUploading)
return;
this.isUploading = true;
var fileData = this.uploadQueue.shift ();
this.setImageStatus (
fileData, Status.UPLOADING, 'upload', _('Uploading file'));
var formData = new FormData();
formData.append ('image', fileData.file);
formData.append ('name', fileData.name.value);
formData.append ('schema', this.$.schema.value);
formData.append ('srv', 'json:image/upload');
this.conn.sendFormData (formData,
this.onFileUpload.bind (this, fileData));
}
,onFileUpload: function (fileData, data, error)
{
this.isUploading = false;
if (data)
{
this.setImageStatus (
fileData, Status.UPLOADED, 'ok', _('Image uploaded'));
}
else
{
this.setImageStatus (
fileData, Status.NONE, 'error', error.message);
fileData.name.disabled = false;
this.errors = true;
}
if (this.uploadQueue.length === 0)
{
if (this.errors)
Htk.Toast.showError (_('Some errors happened on upload'));
else
Htk.Toast.showMessage (_('Upload finished successfully'));
this.errors = false;
}
else
this.uploadNextFile ();
}
,setImageStatus: function (fileData, status, icon, title)
{
fileData.status = status;
var statusNode = fileData.statusNode;
Vn.Node.removeChilds (statusNode);
var iconNode = new Htk.Icon ({icon: icon});
statusNode.appendChild (iconNode.node);
statusNode.title = title ? title : '';
}
,onFileRemove: function (button)
{
var fileData = button.value;
this.$.fileList.removeChild (fileData.div);
for (var i = 0; i < this.filesData.length; i++)
if (this.filesData[i] === fileData)
{
this.filesData.splice (i, 1);
break;
}
}
,onClearClick: function ()
{
this.filesData = [];
Vn.Node.removeChilds (this.$.fileList);
}
,onDropzoneClick: function ()
{
this.$.file.click ();
}
,onFileChange: function ()
{
this.addFiles (this.$.file.files);
}
,onDragEnter: function ()
{
Vn.Node.addClass (this.$.dropzone, 'dragover');
}
,onDragLeave: function ()
{
Vn.Node.removeClass (this.$.dropzone, 'dragover');
}
,onDragOver: function (event)
{
event.preventDefault ();
}
,onDragEnd: function (event)
{
Vn.Node.removeClass (this.$.dropzone, 'dragover');
event.dataTransfer.clearData ();
}
,onDrop: function (event)
{
event.preventDefault ();
this.addFiles (event.dataTransfer.files);
}
});
function getFileName (path)
{
var barIndex = path.lastIndexOf ('/');
if (barIndex === -1)
barIndex = path.lastIndexOf ('\\');
if (barIndex === -1)
barIndex = 0;
var dotIndex = path.lastIndexOf ('.');
if (dotIndex === -1)
dotIndex = 0;
return path.substr (barIndex, dotIndex);
}
})();

View File

@ -0,0 +1,63 @@
/* Dropzone */
.photos .dropzone
{
background-color: white;
border-style: dashed;
border-radius: .4em;
border-color: #2196F3;
padding: 2em 1em;
text-align: center;
color: #666;
cursor: pointer;
}
.photos .dropzone.dragover
{
color: #CCC;
border-style: solid;
}
.photos input[type=file]
{
display: none;
}
/* File list */
.photos .file-list
{
margin-top: 1em;
}
.photos .file-list > div
{
height: 2.5em;
}
.photos .file-list .thumb
{
max-height: 2em;
max-width: 2em;
vertical-align: middle;
margin: 0 1em;
}
.photos .file-list input
{
max-width: 10em;
}
.photos .file-list .status
{
margin-left: .5em;
cursor: pointer;
}
/* Footer */
.photos .footer
{
margin-top: 1.5em;
text-align: center;
}
.photos .footer > button
{
font-size: 1.2em;
margin-left: 1em;
}

View File

@ -1,81 +0,0 @@
/* Dropzone */
.photos .dropzone {
background-color: white;
border-style: dashed;
border-radius: .4em;
border-color: #2196F3;
padding: 2em 1em;
text-align: center;
color: #666;
cursor: pointer;
}
.photos .dropzone.dragover {
color: #CCC;
border-style: solid;
}
.photos input[type=file] {
display: none;
}
/* File list */
.photos .file-list {
margin-top: 1em;
}
.photos .file-list > div {
height: 2.5em;
display: flex;
align-items: center;
}
.photos .file-list > div > * {
overflow: hidden;
}
.photos .file-list .thumb {
width: 2em;
padding-right: .5em;
text-align: center;
}
.photos .file-list .thumb > img {
max-height: 2em;
max-width: 2em;
vertical-align: middle;
}
.photos .file-list input {
flex: 1;
min-width: 0;
}
.photos .file-list .status {
cursor: pointer;
width: 1.2em;
padding-left: .5em;
padding-right: .5em;
}
.photos .file-list .status > .htk-icon {
display: block;
}
.photos .file-list .htk-button {
opacity: .2;
}
.photos .file-list .htk-button:hover {
background-color: transparent;
opacity: 1;
}
.photos .file-list .htk-button > img {
display: block;
}
/* Footer */
.photos .update-matching {
margin-top: 1.5em;
}
.photos .footer {
margin-top: 1.5em;
text-align: center;
}
.photos .footer > button {
font-size: 1.2rem;
margin-left: 1em;
}

View File

@ -1,16 +1,14 @@
<vn> <vn>
<div id="title"> <h1 id="title">
<h1><t>Images</t></h1> _Images
</div> </h1>
<div id="form" class="photos"> <div id="main" class="photos">
<div class="box form vn-w-sm vn-pa-lg"> <div class="card form">
<div class="form-group"> <div class="form-group">
<label><t>Collection</t></label> <label>_Collection</label>
<htk-combo id="schema"> <htk-combo id="schema">
<db-model property="model"> <db-model property="model">
<custom>
SELECT name, `desc` FROM imageCollection ORDER BY `desc` SELECT name, `desc` FROM imageCollection ORDER BY `desc`
</custom>
</db-model> </db-model>
</htk-combo> </htk-combo>
</div> </div>
@ -22,7 +20,7 @@
on-drop="onDrop" on-drop="onDrop"
on-dragend="onDragEnd" on-dragend="onDragEnd"
on-click="onDropzoneClick"> on-click="onDropzoneClick">
<t>Click or drop files here</t> _Click or drop files here
</div> </div>
<input <input
id="file" id="file"
@ -31,18 +29,12 @@
name="image" name="image"
on-change="onFileChange"/> on-change="onFileChange"/>
<div id="file-list" class="file-list"/> <div id="file-list" class="file-list"/>
<div class="update-matching">
<label>
<htk-check id="update-matching" value="true"/>
<t>Update items with matching id</t>
</label>
</div>
<div class="footer"> <div class="footer">
<button class="thin" on-click="onClearClick"> <button class="thin" on-click="onClearClick">
<t>Clear all</t> _Clear all
</button> </button>
<button class="thin" on-click="onUploadClick"> <button class="thin" on-click="onUploadClick">
<t>Upload files</t> _Upload files
</button> </button>
</div> </div>
</div> </div>

View File

@ -1,93 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
,activate() {
this.$.resultIndex.value = 0;
}
,clean() {
if (this._grid) {
this.$.gridHolder.removeChild(this._grid.node);
this._grid.unref();
this._grid = null;
}
}
,onExecuteClick() {
this.clean();
var model = new Db.Model({
conn: this.conn,
query: this.$.sql.value,
resultIndex: this.$.resultIndex.value,
updatable: this.$.updatable.value
});
model.on('status-changed', this.onModelChange, this);
}
,onCleanClick() {
this.clean();
}
,onModelChange(model, status) {
if (status !== Db.Model.Status.LOADING) {
model.disconnect('status-changed', this.onModelChange, this);
model.unref();
}
if (status !== Db.Model.Status.READY)
return;
Htk.Toast.showMessage(_('Query executed!'));
var gridHolder = this.$.gridHolder;
if (gridHolder.firstChild)
gridHolder.removeChilds(gridHolder.firstChild);
var grid = new Htk.Grid();
var columns = model.columns;
for (var i = 0; i < columns.length; i++) {
var c = columns[i];
switch (c.type) {
case Db.Conn.Type.BOOLEAN:
var column = new Htk.ColumnCheck();
break;
case Db.Conn.Type.INTEGER:
var column = new Htk.ColumnSpin();
break;
case Db.Conn.Type.DOUBLE:
var column = new Htk.ColumnSpin({digits: 2});
break;
case Db.Conn.Type.DATE:
var column = new Htk.ColumnDate({format: '%a, %e %b %Y'});
break;
case Db.Conn.Type.DATE_TIME:
var column = new Htk.ColumnDate({format: '%a, %e %b %Y, %T'});
break;
case Db.Conn.Type.STRING:
default:
var column = new Htk.ColumnText();
}
column.setProperties({
title: c.name,
editable: this.$.updatable.value,
columnIndex: i
});
grid.appendColumn(column);
}
grid.model = model;
gridHolder.appendChild(grid.node);
this._grid = grid;
}
});

View File

@ -0,0 +1,102 @@
Hedera.Queries = new Class
({
Extends: Hedera.Form
,activate: function ()
{
this.$.resultIndex.value = 0;
}
,clean: function ()
{
if (this._grid)
{
this.$.gridHolder.removeChild (this._grid.node);
this._grid.unref ();
this._grid = null;
}
}
,_onExecuteClick: function ()
{
this.clean ();
var model = new Db.Model ({
conn: this.conn,
query: this.$.sql.value,
resultIndex: this.$.resultIndex.value,
updatable: this.$.updatable.value
});
model.on ('status-changed', this._onModelChange, this);
}
,_onCleanClick: function ()
{
this.clean ();
}
,_onModelChange: function (model, status)
{
if (status !== Db.Model.Status.LOADING)
{
model.disconnect ('status-changed', this._onModelChange, this);
model.unref ();
}
if (status !== Db.Model.Status.READY)
return;
Htk.Toast.showMessage (_('Query executed!'));
var gridHolder = this.$.gridHolder;
if (gridHolder.firstChild)
gridHolder.removeChilds (gridHolder.firstChild);
var grid = new Htk.Grid ();
var columns = model.columns;
for (var i = 0; i < columns.length; i++)
{
var c = columns[i];
switch (c.type)
{
case Db.Conn.Type.BOOLEAN:
var column = new Htk.ColumnCheck ();
break;
case Db.Conn.Type.INTEGER:
var column = new Htk.ColumnSpin ();
break;
case Db.Conn.Type.DOUBLE:
var column = new Htk.ColumnSpin ({digits: 2});
break;
case Db.Conn.Type.DATE:
var column = new Htk.ColumnDate ({format: '%a, %e %b %Y'});
break;
case Db.Conn.Type.DATE_TIME:
var column = new Htk.ColumnDate ({format: '%a, %e %b %Y, %T'});
break;
case Db.Conn.Type.STRING:
default:
var column = new Htk.ColumnText ();
}
column.setProperties ({
title: c.name,
editable: this.$.updatable.value,
columnIndex: i
});
grid.appendColumn (column);
}
grid.model = model;
gridHolder.appendChild (grid.node);
this._grid = grid;
}
});

View File

@ -1,10 +1,16 @@
.queries textarea { .queries .card
{
max-width: 40em;
}
.queries textarea
{
display: block; display: block;
width: 100%; width: 100%;
height: 8em; height: 8em;
} }
.queries .result { .queries .result
{
margin-top: 1em; margin-top: 1em;
overflow: auto; overflow: auto;
} }

View File

@ -1,37 +1,37 @@
<vn> <vn>
<div id="title"> <h1 id="title">
<h1><t>Queries</t></h1> _Queries
</div> </h1>
<div id="actions"> <div id="actions">
<htk-bar-button <htk-bar-button
icon="ok" icon="ok"
tip="_Execute" tip="_Execute"
on-click="this.onExecuteClick()"/> on-click="_onExecuteClick"/>
<htk-bar-button <htk-bar-button
icon="delete" icon="delete"
tip="_Clean" tip="_Clean"
on-click="this.onCleanClick()"/> on-click="_onCleanClick"/>
</div> </div>
<div id="form" class="queries"> <div id="main" class="queries">
<div class="box form vn-w-sm vn-pa-lg"> <div class="card form">
<div class="form-group"> <div>
<label><t>SQL query</t></label> <label>_SQL query</label>
<textarea <textarea
id="sql" id="sql"
autocorrect="off" autocorrect="off"
autocapitalize="off" autocapitalize="off"
spellcheck="false"/> spellcheck="false"/>
</div> </div>
<div class="form-group"> <div>
<label><t>Result index</t></label> <label>_Result index</label>
<htk-spin id="result-index"/> <htk-spin id="result-index"/>
</div> </div>
<div class="form-group"> <div>
<label><t>Updatable</t></label> <label>_Updatable</label>
<htk-check id="updatable"/> <htk-check id="updatable"/>
</div> </div>
</div> </div>
<div class="box result"> <div class="card result">
<div id="grid-holder"/> <div id="grid-holder"/>
</div> </div>
</div> </div>

View File

@ -1,20 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
,rendererFunc(scope, form) {
var isEnabled = form.$.active
scope.$.disabled.style.display = isEnabled ?
'none' : 'block';
scope.$.impersonate.node.style.display = isEnabled ?
'block' : 'none';
}
,async onChangeUserClick(userName) {
await this.gui.supplantUser(userName);
this.hash.setAll({form: 'ecomerce/orders'});
}
});

View File

@ -1,11 +1,14 @@
.users-box.item > .actions { .users ._disabled
display: flex; {
}
.users-box .disabled {
color: white; color: white;
background-color: #F66; background-color: #F66;
border-radius: .2em; border-radius: .2em;
padding: .3em; padding: .3em;
font-size: .8em; font-size: .8em;
} }
.action-bar .htk-search-entry
{
margin: .8em;
}

View File

@ -1,15 +1,13 @@
<vn> <vn>
<div id="title"> <h1 id="title">
<h1><t>User management</t></h1> _User management
</div> </h1>
<div id="actions"> <div id="actions">
<htk-search-entry form="hash" column="user"/> <htk-search-entry lot="hash" name="user"/>
</div> </div>
<div id="form" class="users"> <div id="main" class="users">
<htk-repeater <div class="card list">
form-id="iter" <htk-repeater renderer="rendererFunc">
renderer="rendererFunc"
class="htk-list box vn-w-xs">
<db-model property="model" lot="hash"> <db-model property="model" lot="hash">
SELECT u.id, u.name, u.nickname, u.active SELECT u.id, u.name, u.nickname, u.active
FROM account.user u FROM account.user u
@ -19,30 +17,30 @@
ORDER BY u.name LIMIT 200 ORDER BY u.name LIMIT 200
</db-model> </db-model>
<custom> <custom>
<a class="users-box item" <a
href="{{`#!form=admin/access-log&amp;user=${iter.id}`}}" class="list-row"
title="_Access log"> href="#!form=admin/access-log&amp;user={{id}}"
<div class="content"> title="_AccessLog">
<p class="important"> <div class="actions">
{{iter.nickname}} <span id="disabled">
</p> _Disabled
<p>
#{{iter.id}} - {{iter.name}}
</p>
</div>
<div class="actions"
on-click="$event.preventDefault()">
<span id="disabled" class="disabled">
<t>Disabled</t>
</span> </span>
<htk-button <htk-button
id="impersonate" id="impersonate"
icon="supervisor_account" value="{{id}}"
tip="_Impersonate user" tip="_AccessAsUser"
on-click="this.onChangeUserClick(iter.name)"/> icon="incognito"
on-click="onChangeUserClick"/>
</div> </div>
<p class="important">
{{nickname}}
</p>
<p>
@{{id}} - {{name}}
</p>
</a> </a>
</custom> </custom>
</htk-repeater> </htk-repeater>
</div> </div>
</div>
</vn> </vn>

View File

@ -0,0 +1,26 @@
Hedera.Users = new Class
({
Extends: Hedera.Form
,rendererFunc: function (scope, lot)
{
var isEnabled = lot.$.active;
scope.$.disabled.style.display = isEnabled ?
'none' : 'block';
scope.$.impersonate.node.style.display = isEnabled ?
'block' : 'none';
}
,onChangeUserClick: function (button)
{
this.gui.supplantUser (button.value,
this.onUserSupplant.bind (this));
}
,onUserSupplant: function ()
{
this.hash.setAll ({form: 'ecomerce/orders'});
}
});

View File

@ -1,15 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml')
,activate() {
if (!this.hash.$.to)
this.hash.assign({
from: Date.vnNew(),
to: Date.vnNew()
});
}
});

View File

@ -0,0 +1,13 @@
.visits .card
{
max-width: 28em;
}
.visits .summary p
{
margin: 0;
font-size: 1.4em;
text-align: right;
}

View File

@ -1,12 +0,0 @@
.visits .box {
margin-bottom: 16px;
}
.visits .box:last-child {
margin-bottom: 0;
}
.visits .summary p {
font-size: 1.4rem;
margin: 0;
text-align: right;
}

View File

@ -3,36 +3,31 @@
<vn-spec name="from" type="Date"/> <vn-spec name="from" type="Date"/>
<vn-spec name="to" type="Date"/> <vn-spec name="to" type="Date"/>
</vn-lot-query> </vn-lot-query>
<div id="title"> <h1 id="title">
<h1><t>Visits</t></h1> _Visits
</div> </h1>
<div id="actions"> <div id="actions">
<htk-bar-button <htk-bar-button
icon="refresh" icon="refresh"
tip="_Refresh" tip="_Refresh"
on-click="visits.refresh()"/> on-click="onRefreshClick"/>
<htk-bar-button <htk-bar-button
icon="visibility" icon="user-info"
tip="_Connections" tip="_Connections"
on-click="this.hash.setAll({form: 'admin/connections'})"/> on-click="onSessionsClick"/>
</div> </div>
<div id="form" class="visits"> <div id="main" class="visits">
<div class="vn-w-xs"> <div class="card form">
<div class="form vn-pa-lg box"> <div>
<div class="form-group"> <label>_From</label>
<label><t>From</t></label> <htk-date-chooser lot="params" name="from"/>
<htk-date-chooser
form="params"
column="from"/>
</div> </div>
<div class="form-group"> <div>
<label><t>To</t></label> <label>_To</label>
<htk-date-chooser <htk-date-chooser lot="params" name="to"/>
form="params"
column="to"/>
</div> </div>
</div> </div>
<div class="summary vn-pa-lg box"> <div class="summary card form">
<p> <p>
<htk-text> <htk-text>
<db-calc-sum <db-calc-sum
@ -40,49 +35,45 @@
model="visits" model="visits"
column-name="visits"/> column-name="visits"/>
</htk-text> </htk-text>
<t>visits</t>, <span>_visits</span>,
<htk-text> <htk-text>
<db-calc-sum <db-calc-sum
property="param" property="param"
model="visits" model="visits"
column-name="newVisits"/> column-name="newVisits"/>
</htk-text> </htk-text>
<t>news</t> <span>_news</span>
</p> </p>
</div> </div>
<htk-repeater <div class="card list ">
class="box htk-list" <htk-repeater empty-message="_Select date interval">
form-id="iter"
empty-message="_Select date interval">
<db-model property="model" id="visits" lot="params"> <db-model property="model" id="visits" lot="params">
SELECT browser, SELECT browser
MIN(CAST(version AS DECIMAL(4,1))) minVersion, ,MIN(CAST(version AS DECIMAL(4,1))) minVersion
MAX(CAST(version AS DECIMAL(4,1))) maxVersion, ,MAX(CAST(version AS DECIMAL(4,1))) maxVersion
MAX(c.stamp) lastVisit, ,MAX(c.stamp) lastVisit
COUNT(DISTINCT c.id) visits, ,COUNT(DISTINCT c.id) visits
SUM(a.firstAccessFk = c.id AND v.firstAgentFk = a.id) newVisits ,SUM(a.firstAccess = c.id AND v.firstAgent = a.id) newVisits
FROM visitUser e FROM visitUser e
JOIN visitAccess c ON c.id = e.accessFk JOIN visitAccess c ON e.access = c.id
JOIN visitAgent a ON a.id = c.agentFk JOIN visitAgent a ON c.agent = a.id
JOIN visit v ON v.id = a.visitFk JOIN visit v ON a.visit = v.id
WHERE c.stamp BETWEEN TIMESTAMP(#from,'00:00:00') AND TIMESTAMP(#to,'23:59:59') WHERE c.stamp BETWEEN TIMESTAMP(#from,'00:00:00') AND TIMESTAMP(#to,'23:59:59')
GROUP BY browser ORDER BY visits DESC GROUP BY browser
ORDER BY visits DESC
</db-model> </db-model>
<custom> <custom>
<div class="item"> <div class="list-row">
<div class="content">
<p class="important"> <p class="important">
{{iter.browser}} {{iter.minVersion}} - {{iter.maxVersion}} {{browser}} {{minVersion}} - {{maxVersion}}
</p> </p>
<p> <p>
{{iter.visits}} <t>visits</t>, {{visits}} <span>_visits</span>, {{newVisits}} <span>_news</span>
{{iter.newVisits}} <t>news</t>
</p> </p>
<p> <p>
{{Vn.Value.format(iter.lastVisit, _('%a, %e %b %Y at %T'))}} <htk-text lot="iter" name="lastVisit" format="_%a, %e %b %Y at %T"/>
</p> </p>
</div> </div>
</div>
</custom> </custom>
</htk-repeater> </htk-repeater>
</div> </div>

View File

@ -0,0 +1,25 @@
Hedera.Visits = new Class
({
Extends: Hedera.Form
,activate: function ()
{
if (!this.hash.$.to)
this.hash.assign ({
from: new Date (),
to: new Date ()
});
}
,onRefreshClick: function ()
{
this.$.visits.refresh ();
}
,onSessionsClick: function ()
{
this.hash.setAll ({form: 'admin/connections'});
}
});

View File

@ -1,7 +0,0 @@
import './style.scss';
export default new Class({
Extends: Hedera.Form,
Template: require('./ui.xml'),
});

View File

@ -1,4 +1,5 @@
ListByAgency: Paquets per agència ListByAgency: Paquets per agència
ShowByProvince: Mostrar desglose per província
Agency: Agència Agency: Agència
Exps: Exps. Exps: Exps.
Bundles: Paquets Bundles: Paquets

View File

@ -1,4 +1,5 @@
ListByAgency: Bundles by agency ListByAgency: Bundles by agency
ShowByProvince: Show breakdown by province
Agency: Agency Agency: Agency
Exps: Exps. Exps: Exps.
Bundles: Bundles Bundles: Bundles

View File

@ -1,4 +1,5 @@
ListByAgency: Bultos por agencia ListByAgency: Bultos por agencia
ShowByProvince: Mostrar desglose por provincia
Agency: Agencia Agency: Agencia
Exps: Exps. Exps: Exps.
Bundles: Bultos Bundles: Bultos

View File

@ -1,4 +1,5 @@
ListByAgency: Liste par agence ListByAgency: Liste par agence
ShowByProvince: Montrer par province
Agency: Agence Agency: Agence
Exps: Expéditeur Exps: Expéditeur
Bundles: Cartons Bundles: Cartons

View File

@ -1,4 +1,5 @@
ListByAgency: Bultos por agencia ListByAgency: Bultos por agencia
ShowByProvince: Mostrar desglosse por Distrito
Agency: Agencia Agency: Agencia
Exps: Exps. Exps: Exps.
Bundles: Bultos Bundles: Bultos

View File

@ -0,0 +1,14 @@
Hedera.Packages = new Class
({
Extends: Hedera.Form
,onShowClick: function (column, agencyId)
{
this.hash.setAll ({
form: 'agencies/provinces',
agency: agencyId
});
}
});

View File

@ -0,0 +1,4 @@
.packages .card
{
max-width: 35em;
}

View File

@ -1,15 +1,18 @@
<vn> <vn>
<div id="title"> <h1 id="title">
<h1><t>ListByAgency</t></h1> _ListByAgency
</div> </h1>
<div id="form" class="packages"> <div id="main" class="packages">
<div class="box vn-w-sm"> <div class="card">
<htk-grid> <htk-grid>
<db-model property="model"> <db-model property="model">
<custom> CALL vn2008.agencia_volume ()
CALL vn.agencyVolume()
</custom>
</db-model> </db-model>
<htk-column-button
column="agency_id"
icon="show"
tip="_ShowByProvince"
on-clicked="onShowClick"/>
<htk-column-text title="_Agency" column="Agencia"/> <htk-column-text title="_Agency" column="Agencia"/>
<htk-column-spin title="_Exps" column="expediciones"/> <htk-column-spin title="_Exps" column="expediciones"/>
<htk-column-spin title="_Bundles" column="Bultos"/> <htk-column-spin title="_Bundles" column="Bultos"/>

View File

@ -0,0 +1,6 @@
ByProvince: Desglose per província
Return: Tornar
SelectAgency: Selecciona una agència al llistat de l'esquerra
Province: Província
Expeditions: Exps.
Left: Falten

Some files were not shown because too many files have changed in this diff Show More