зеркало из https://github.com/DeGsoft/meanjs.git
angular express
This commit is contained in:
Родитель
c1cef837b7
Коммит
4dd94aa5d5
|
@ -0,0 +1,24 @@
|
|||
angular.module('controlCajaApp', ['ui.router']);
|
||||
|
||||
angular.module('controlCajaApp').config(function ($stateProvider,$locationProvider) {
|
||||
$stateProvider
|
||||
.state('total', {
|
||||
url: '/',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'total.html'
|
||||
})
|
||||
.state('nuevo', {
|
||||
url: '/nuevo',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'nuevo.html'
|
||||
})
|
||||
.state('lista', {
|
||||
url: '/lista',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'lista.html'
|
||||
}).state('not-found', {
|
||||
url: '*path',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'total.html'
|
||||
});
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
(function () {
|
||||
var cajaCtrl = function (movimientosFactory) {
|
||||
var vm = this;
|
||||
|
||||
vm.titulo = "Controla tu Cash Flow";
|
||||
vm.maestros = {
|
||||
categoriasIngresos: ['Nómina', 'Ventas', 'Intereses Depósitos'],
|
||||
categoriasGastos: ['Hipotéca', 'Compras', 'Impuestos']
|
||||
};
|
||||
vm.nuevoMovimiento = {
|
||||
esIngreso: 1,
|
||||
esGasto: 0,
|
||||
importe: 0,
|
||||
fecha: new Date()
|
||||
};
|
||||
vm.total={ingresos:0, gastos:0};
|
||||
// Como los datos están en un servidor
|
||||
// las factorias me deovlerán promesas
|
||||
// Todo funcionará de manera asíncrona
|
||||
|
||||
//vm.movimientos = movimientosFactory.getMovimientos();
|
||||
movimientosFactory.gettingMovimientos()
|
||||
.success(function (movimientos) {
|
||||
vm.movimientos = movimientos;
|
||||
});
|
||||
|
||||
//vm.total = movimientosFactory.getTotal();
|
||||
movimientosFactory.gettingTotal()
|
||||
.success(function (total) {
|
||||
vm.total = total;
|
||||
});
|
||||
|
||||
vm.guardarMovimiento = function () {
|
||||
// No necesitamos hacer una copia
|
||||
// porque el array ya no es local
|
||||
//var auxCopyMov = angular.copy(vm.nuevoMovimiento);
|
||||
console.log(JSON.stringify(vm.nuevoMovimiento));
|
||||
vm.nuevoMovimiento.tipo = vm.tipo(vm.nuevoMovimiento);
|
||||
movimientosFactory.postingMovimiento(vm.nuevoMovimiento)
|
||||
.success(function (postedData) {
|
||||
// cuando ha terminado el guardado del movimiento
|
||||
// es momento de pedir una actualización de datos
|
||||
movimientosFactory.gettingMovimientos()
|
||||
.success(function (movimientos) {
|
||||
vm.movimientos = movimientos;
|
||||
});
|
||||
movimientosFactory.gettingTotal()
|
||||
.success(function (total) {
|
||||
vm.total = total;
|
||||
});
|
||||
// puedo limpiar movimiento sin problema
|
||||
vm.nuevoMovimiento.importe = 0;
|
||||
});
|
||||
}
|
||||
vm.balance = function () {
|
||||
return vm.total.ingresos - vm.total.gastos
|
||||
}
|
||||
vm.tipo = function (movimiento) {
|
||||
return movimiento.esIngreso && 'Ingreso' || 'Gasto'
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('CajaCtrl', cajaCtrl);
|
||||
}());
|
|
@ -0,0 +1,44 @@
|
|||
<html lang="es" ng-app="controlCajaApp">
|
||||
<head>
|
||||
<title>Control de Caja</title>
|
||||
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<meta charset="utf-8" />
|
||||
<meta lang="es" />
|
||||
<meta name="description" content="Ejemplo Control de Caja en AngularJS por Alberto Basalo" />
|
||||
<meta name="author" content="Alberto Basalo @ Ágora Binaria" />
|
||||
<meta name="application-name" content="ControlAngularJS" />
|
||||
<meta name="Keywords" content="AngularJS, ejemplo, tutorial, curso" />
|
||||
<link rel="author" href="https://plus.google.com/+AlbertoBasalo71" />
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation" ng-controller="MenuCtrl as menu">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-class="{ active: menu.isActive('total') }">
|
||||
<a ui-sref="total">Totales</a>
|
||||
</li>
|
||||
<li ng-class="{ active: menu.isActive('nuevo') }">
|
||||
<a ui-sref="nuevo">Nuevo</a>
|
||||
</li>
|
||||
<li ng-class="{ active: menu.isActive('lista') }">
|
||||
<a ui-sref="lista">Lista</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container text-center" style="padding-top:50px;" ui-view >
|
||||
|
||||
</div>
|
||||
<!-- JavaScript References -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
|
||||
<script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script src="menuCtrl.js"></script>
|
||||
<script src="cajaCtrl.js"></script>
|
||||
<script src="movimientosFactory.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<section name="Lista">
|
||||
<p class="lead">Estos son tus movimientos recientes.</p>
|
||||
<br>
|
||||
<label class="control-label" for="importe">Filtrar:</label>
|
||||
<input type="text" name="filtro" placeholder="qué buscas?" class="input" ng-model="valorBuscado">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="" ng-click="campo = 'fecha'; sentido = campo == 'fecha' && !sentido">Fecha</a>
|
||||
</th>
|
||||
<th>Tipo</th>
|
||||
<th>Categoría</th>
|
||||
<th><a href="" ng-click="campo = 'importe'; sentido = campo == 'importe' && !sentido">Importe</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="movimiento in caja.movimientos | filter:valorBuscado | orderBy:campo:sentido">
|
||||
<td class="text-left">{{movimiento.fecha | date}}</td>
|
||||
<td class="text-left">{{movimiento.tipo}}</td>
|
||||
<td class="text-left">{{movimiento.categoria}}</td>
|
||||
<td class="text-left" ng-class="{'text-success': movimiento.tipo=='Ingreso', 'text-danger' : movimiento.tipo=='Gasto'}">{{movimiento.importe | number:2}} €</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
|
@ -0,0 +1,8 @@
|
|||
(function () {
|
||||
var menuCtrl = function ($state) {
|
||||
this.isActive = function (estado) {
|
||||
return $state.is(estado);
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('MenuCtrl',menuCtrl);
|
||||
}());
|
|
@ -0,0 +1,46 @@
|
|||
(function () {
|
||||
// La factoria ya no almacenrá sus propios datos
|
||||
// Mediante el servicio $http hará las llamadas al servidor
|
||||
// Es un servicio core y no hay que referenciar ningún módulo extra
|
||||
var movimientosFactory = function ($http) {
|
||||
// var movimientos = [];
|
||||
// var total = {
|
||||
// ingresos: 0,
|
||||
// gastos: 0
|
||||
// };
|
||||
var urlBase = "http://localhost:3000/api/";
|
||||
|
||||
|
||||
var factory = {};
|
||||
// factory.getMovimientos = function () {
|
||||
// return movimientos;
|
||||
// };
|
||||
|
||||
// se produce un cambio en la nomenclatura
|
||||
// al usar el gerundio indicamos un proceso no terminado
|
||||
// el controlador que lo consuma debe manejar la promesa
|
||||
factory.gettingMovimientos = function () {
|
||||
// Estamos devolviendo promesas, no objetos
|
||||
return $http.get(urlBase + 'priv/movimientos');
|
||||
};
|
||||
|
||||
factory.gettingTotal = function () {
|
||||
return $http.get(urlBase + 'priv/total');
|
||||
};
|
||||
factory.postingMovimiento = function (movimiento) {
|
||||
return $http.post(urlBase + 'priv/movimientos', movimiento);
|
||||
};
|
||||
|
||||
|
||||
//La poca lógica de negocio se irá al lado del servidor
|
||||
// factory.postMovimiento = function (movimiento) {
|
||||
// movimientos.push(movimiento);
|
||||
// total.ingresos += movimiento.esIngreso * movimiento.importe;
|
||||
// total.gastos += movimiento.esGasto * movimiento.importe;
|
||||
// };
|
||||
|
||||
return factory;
|
||||
};
|
||||
|
||||
angular.module('controlCajaApp').factory('movimientosFactory', movimientosFactory);
|
||||
}());
|
|
@ -0,0 +1,3 @@
|
|||
<section name="total">
|
||||
<h2>No se ha encontrado lo que buscabas</h2>
|
||||
</section>
|
|
@ -0,0 +1,53 @@
|
|||
<section name="nuevoMovimiento" class="row-fluid">
|
||||
<form class="form-horizontal text-left">
|
||||
<fieldset>
|
||||
<div id="legend">
|
||||
<legend class="">Introduce tus movimientos</legend>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="tipo">Tipo</label>
|
||||
<div class="controls">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success" ng-class="{'active':caja.nuevoMovimiento.esIngreso==1}" ng-click="caja.nuevoMovimiento.esIngreso=1; caja.nuevoMovimiento.esGasto=0">
|
||||
<span ng-class="{'small':caja.nuevoMovimiento.esIngreso==0}">+ Ingreso</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" ng-class="{'active':caja.nuevoMovimiento.esGasto==1}" ng-click="caja.nuevoMovimiento.esIngreso=0; caja.nuevoMovimiento.esGasto=1">
|
||||
<span ng-class="{'small':caja.nuevoMovimiento.esGasto==0}">- Gasto</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="categ">Categoría</label>
|
||||
<div class="controls">
|
||||
<select ng-show="caja.nuevoMovimiento.esIngreso" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasIngresos"></select>
|
||||
<select ng-show="caja.nuevoMovimiento.esGasto" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasGastos"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="fecha">Fecha</label>
|
||||
<div class="controls">
|
||||
<input type="date" name="fecha" placeholder="" class="input" ng-model="caja.nuevoMovimiento.fecha">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="importe">Importe</label>
|
||||
<div class="controls">
|
||||
<input type="number" name="importe" placeholder="" class="input" ng-model="caja.nuevoMovimiento.importe">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button style="margin-top: 20px" type="button" class="btn btn-lg btn-primary" ng-click="caja.guardarMovimiento()">
|
||||
<span>Guardar {{ caja.tipo(caja.nuevoMovimiento) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
{{caja.movimientos}}
|
||||
</section>
|
|
@ -0,0 +1,38 @@
|
|||
<!-- Cada sección tendrá su propia plantilla -->
|
||||
<!-- No se especifíca el controlador, que será asignado por el enrutador -->
|
||||
<section name="total">
|
||||
<!-- Pero se supone un viweModel llamado caja-->
|
||||
<h1>{{ caja.titulo }}</h1>
|
||||
<p class="lead">Comprueba de dónde viene y a dónde va tu dinero.</p>
|
||||
<div class="row-fluid">
|
||||
<div class="row placeholders">
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label label-success">
|
||||
{{ caja.total.ingresos | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Total ingresos</h4>
|
||||
<span class="text-muted">Acumulado</span>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label label-danger">
|
||||
{{ caja.total.gastos | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Total gastos</h4>
|
||||
<span class="text-muted">Acumulado</span>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label " ng-class="{'label-success': caja.balance()>=0 , 'label-danger' : caja.balance()<0}">
|
||||
{{ caja.balance() | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Balance</h4>
|
||||
<span class="text-muted">Ingresos-Gastos</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "rest",
|
||||
"version": "0.0.0",
|
||||
"description": "rest nodejs angularjs",
|
||||
"main": "server.js",
|
||||
"dependencies": {
|
||||
"body-parser": "~1.0.2",
|
||||
"express": "~4.1.2"
|
||||
},
|
||||
"author": "@albertobasalo",
|
||||
"license": "BSD-2-Clause"
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
// Configuración de la aplicación express
|
||||
var app = express();
|
||||
app.use(bodyParser());
|
||||
app.use(express.static(__dirname + '/client'));
|
||||
|
||||
console.log('ready');
|
||||
|
||||
// Variables locales en las que almacenar la información
|
||||
// En el futuro estrán en MongoDB
|
||||
var movimientos = [];
|
||||
var total = {
|
||||
ingresos: 0,
|
||||
gastos: 0
|
||||
}
|
||||
|
||||
// API - REST
|
||||
// verbos en protocolo, nombres de recurso en la ruta, parametros en segmentos o body
|
||||
|
||||
// ruta en el API que devolverá la lista de maestros
|
||||
app.get('/api/pub/maestros', function (req, res, next) {
|
||||
var maestros = {
|
||||
categoriasIngresos: ['Nómina', 'Ventas', 'Intereses Depósitos'],
|
||||
categoriasGastos: ['Hipotéca', 'Compras', 'Impuestos']
|
||||
};
|
||||
// la comunicación será siempre vía JSON
|
||||
res.json(maestros);
|
||||
});
|
||||
|
||||
|
||||
// Si para una misma ruta hay más de un verbo
|
||||
// Podemos tener rutas para enviar y recibir datos con los verbos htttp get y post
|
||||
app.route('/api/priv/movimientos')
|
||||
.get(function (req, res, next) {
|
||||
res.json(movimientos);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
var movimiento = req.body;
|
||||
console.log(JSON.stringify(movimiento));
|
||||
movimientos.push(movimiento);
|
||||
// La lógica de negocio se viene al lado del servidor
|
||||
if (movimiento.tipo == 'Ingreso')
|
||||
total.ingresos += movimiento.importe;
|
||||
else
|
||||
total.gastos += movimiento.importe;
|
||||
res.status(200).send();
|
||||
});
|
||||
|
||||
// En esta ruta devolveremos un objeto con los totales agrupados
|
||||
app.get('/api/priv/total', function (req, res, next) {
|
||||
res.json(total);
|
||||
});
|
||||
|
||||
|
||||
console.log('steady');
|
||||
app.listen(3000);
|
||||
console.log('go');
|
|
@ -0,0 +1,31 @@
|
|||
// Apuntamos la nueva dependencia a ngCookies
|
||||
angular.module('controlCajaApp', ['ui.router','ngCookies']);
|
||||
|
||||
angular.module('controlCajaApp').config(function ($stateProvider,$locationProvider) {
|
||||
$stateProvider
|
||||
.state('total', {
|
||||
url: '/',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'total.html'
|
||||
})
|
||||
.state('nuevo', {
|
||||
url: '/nuevo',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'nuevo.html'
|
||||
})
|
||||
.state('lista', {
|
||||
url: '/lista',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'lista.html'
|
||||
})
|
||||
.state('registro', {
|
||||
url: '/registro',
|
||||
controller: 'RegistroCtrl as registro',
|
||||
templateUrl: 'registro.html'
|
||||
})
|
||||
.state('not-found', {
|
||||
url: '*path',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'total.html'
|
||||
});
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
(function () {
|
||||
// Los interceptores de http se configuran con funciones asignadas en el 'config'
|
||||
// Las configuraciones afectan a servicios, como el $http
|
||||
// Cada servicio configurable ofrece un 'provider' al que 'configurar'
|
||||
// En este caso es el $httpProvider
|
||||
function configuradorInterceptores($httpProvider) {
|
||||
// La configuración admite multiples funciones interceptoras
|
||||
// usaremos una función para controlar la seguridad
|
||||
$httpProvider.interceptors.push(funcionInterceptoraSeguridad);
|
||||
}
|
||||
|
||||
// Esta funión será usada en todas las llamadas http
|
||||
// Y controlará el tema de la seguridad
|
||||
function funcionInterceptoraSeguridad($injector,$q, $cookieStore, $rootScope) {
|
||||
// la función interceptora devuelve un objeto que configura el interceptor
|
||||
var interceptor = {}; // Este objeto almacena funciones a llamar en ciertos momentos
|
||||
|
||||
// Función que se ejecutarán antes de cada petición
|
||||
interceptor.request = function (request) {
|
||||
// Enviar en la cabecera el token de sesión previamente guardado en una cookie
|
||||
// El acceso las cookies se realiza con el servicio $cookieStore
|
||||
request.headers["sessionId"] = $cookieStore.get("sessionId");
|
||||
return request || $q.when(request);
|
||||
};
|
||||
// Función que se ejecutarán despues de cada respuesta con error
|
||||
interceptor.responseError = function (response) {
|
||||
var state = $injector.get('$state');
|
||||
if (response.status === 401) {
|
||||
// Si no tenemos cookie o es inválida, recibiremos un 401
|
||||
$rootScope.mensaje = "No hay derecho!!!";
|
||||
// Redirigimos al usuario a la página de registro
|
||||
state.go('registro');
|
||||
} else if (response.status === 419) {
|
||||
$rootScope.mensaje = "Estoy caduco!!!";
|
||||
// Similar al 401, pero con sesión caducada, implica borrar el código actual
|
||||
$cookieStore.remove("sessionId")
|
||||
state.go('registro');
|
||||
};
|
||||
return $q.reject(response);
|
||||
}
|
||||
|
||||
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
angular.module('controlCajaApp').config(configuradorInterceptores);
|
||||
}());
|
|
@ -0,0 +1,59 @@
|
|||
(function () {
|
||||
var cajaCtrl = function (maestrosFactory, movimientosFactory) {
|
||||
var vm = this;
|
||||
|
||||
vm.titulo = "Controla tu Cash Flow";
|
||||
|
||||
maestrosFactory.gettingMaestros()
|
||||
.success(function (maestros) {
|
||||
vm.maestros = maestros;
|
||||
});
|
||||
|
||||
|
||||
vm.nuevoMovimiento = {
|
||||
esIngreso: 1,
|
||||
esGasto: 0,
|
||||
importe: 0,
|
||||
fecha: new Date()
|
||||
};
|
||||
vm.total = {
|
||||
ingresos: 0,
|
||||
gastos: 0
|
||||
};
|
||||
|
||||
movimientosFactory.gettingMovimientos()
|
||||
.success(function (movimientos) {
|
||||
vm.movimientos = movimientos;
|
||||
});
|
||||
|
||||
|
||||
movimientosFactory.gettingTotal()
|
||||
.success(function (total) {
|
||||
vm.total = total;
|
||||
});
|
||||
|
||||
vm.guardarMovimiento = function () {
|
||||
vm.nuevoMovimiento.tipo = vm.tipo(vm.nuevoMovimiento);
|
||||
movimientosFactory.postingMovimiento(vm.nuevoMovimiento)
|
||||
.success(function (postedData) {
|
||||
movimientosFactory.gettingMovimientos()
|
||||
.success(function (movimientos) {
|
||||
vm.movimientos = movimientos;
|
||||
});
|
||||
movimientosFactory.gettingTotal()
|
||||
.success(function (total) {
|
||||
vm.total = total;
|
||||
});
|
||||
vm.nuevoMovimiento.importe = 0;
|
||||
});
|
||||
|
||||
}
|
||||
vm.balance = function () {
|
||||
return vm.total.ingresos - vm.total.gastos
|
||||
}
|
||||
vm.tipo = function (movimiento) {
|
||||
return movimiento.esIngreso && 'Ingreso' || 'Gasto'
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('CajaCtrl', cajaCtrl);
|
||||
}());
|
|
@ -0,0 +1,52 @@
|
|||
<html lang="es" ng-app="controlCajaApp">
|
||||
<head>
|
||||
<title>Control de Caja</title>
|
||||
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<meta charset="utf-8" />
|
||||
<meta lang="es" />
|
||||
<meta name="description" content="Ejemplo Control de Caja en AngularJS por Alberto Basalo" />
|
||||
<meta name="author" content="Alberto Basalo @ Ágora Binaria" />
|
||||
<meta name="application-name" content="ControlAngularJS" />
|
||||
<meta name="Keywords" content="AngularJS, ejemplo, tutorial, curso" />
|
||||
<link rel="author" href="https://plus.google.com/+AlbertoBasalo71" />
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation" ng-controller="MenuCtrl as menu">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-class="{ active: menu.isActive('total') }">
|
||||
<a ui-sref="total">Totales</a>
|
||||
</li>
|
||||
<li ng-class="{ active: menu.isActive('nuevo') }">
|
||||
<a ui-sref="nuevo">Nuevo</a>
|
||||
</li>
|
||||
<li ng-class="{ active: menu.isActive('lista') }">
|
||||
<a ui-sref="lista">Lista</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container text-center" style="padding-top:50px;" ui-view >
|
||||
|
||||
</div>
|
||||
<!-- JavaScript References -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
|
||||
<!-- Referencia para usar el módulo ngCookies, el nombre viene en forma de angular-cookies -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-cookies.min.js"></script>
|
||||
<script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<!-- Inluímos referencia al fichero que configura la seguridad y al Registro controller-->
|
||||
<script src="appSecurity.js"></script>
|
||||
<script src="registroCtrl.js"></script>
|
||||
<script src="menuCtrl.js"></script>
|
||||
<script src="cajaCtrl.js"></script>
|
||||
|
||||
<script src="movimientosFactory.js"></script>
|
||||
<!-- Inluímos referencia al fichero para la carga de maestros -->
|
||||
<script src="maestrosFactory.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<section name="Lista">
|
||||
<p class="lead">Estos son tus movimientos recientes.</p>
|
||||
<br>
|
||||
<label class="control-label" for="importe">Filtrar:</label>
|
||||
<input type="text" name="filtro" placeholder="qué buscas?" class="input" ng-model="valorBuscado">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="" ng-click="campo = 'fecha'; sentido = campo == 'fecha' && !sentido">Fecha</a>
|
||||
</th>
|
||||
<th>Tipo</th>
|
||||
<th>Categoría</th>
|
||||
<th><a href="" ng-click="campo = 'importe'; sentido = campo == 'importe' && !sentido">Importe</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="movimiento in caja.movimientos | filter:valorBuscado | orderBy:campo:sentido">
|
||||
<td class="text-left">{{movimiento.fecha | date}}</td>
|
||||
<td class="text-left">{{movimiento.tipo}}</td>
|
||||
<td class="text-left">{{movimiento.categoria}}</td>
|
||||
<td class="text-left" ng-class="{'text-success': movimiento.tipo=='Ingreso', 'text-danger' : movimiento.tipo=='Gasto'}">{{movimiento.importe | number:2}} €</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
|
@ -0,0 +1,19 @@
|
|||
(function () {
|
||||
// La factoría que me comunica con maestros es similar
|
||||
// Es bueno crear una factoría para cada recuros rest
|
||||
var maestrosFactory = function ($http) {
|
||||
// en este caso la ruta es pública, pero a este nivel no influye
|
||||
var urlBase = "http://localhost:3000/api/";
|
||||
|
||||
var factory = {};
|
||||
|
||||
factory.gettingMaestros = function () {
|
||||
return $http.get(urlBase+'pub/maestros');
|
||||
};
|
||||
|
||||
return factory;
|
||||
|
||||
};
|
||||
|
||||
angular.module('controlCajaApp').factory('maestrosFactory', maestrosFactory);
|
||||
}());
|
|
@ -0,0 +1,8 @@
|
|||
(function () {
|
||||
var menuCtrl = function ($state) {
|
||||
this.isActive = function (estado) {
|
||||
return $state.is(estado);
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('MenuCtrl',menuCtrl);
|
||||
}());
|
|
@ -0,0 +1,40 @@
|
|||
(function () {
|
||||
// La factoria ya no almacenrá sus propios datos
|
||||
// Mediante el servicio $http hará las llamadas al servidor
|
||||
// Es un servicio core y no hay que referenciar ningún módulo extra
|
||||
var movimientosFactory = function ($http) {
|
||||
|
||||
var urlBase = "http://localhost:3000/api/";
|
||||
|
||||
|
||||
var factory = {};
|
||||
|
||||
|
||||
// se produce un cambio en la nomenclatura
|
||||
// al usar el gerundio indicamos un proceso no terminado
|
||||
// el controlador que lo consuma debe manejar la promesa
|
||||
factory.gettingMovimientos = function () {
|
||||
// Estamos devolviendo promesas, no objetos
|
||||
return $http.get(urlBase + 'priv/movimientos');
|
||||
};
|
||||
|
||||
factory.gettingTotal = function () {
|
||||
return $http.get(urlBase + 'priv/total');
|
||||
};
|
||||
factory.postingMovimiento = function (movimiento) {
|
||||
return $http.post(urlBase + 'priv/movimientos', movimiento);
|
||||
};
|
||||
|
||||
|
||||
//La poca lógica de negocio se irá al lado del servidor
|
||||
// factory.postMovimiento = function (movimiento) {
|
||||
// movimientos.push(movimiento);
|
||||
// total.ingresos += movimiento.esIngreso * movimiento.importe;
|
||||
// total.gastos += movimiento.esGasto * movimiento.importe;
|
||||
// };
|
||||
|
||||
return factory;
|
||||
};
|
||||
|
||||
angular.module('controlCajaApp').factory('movimientosFactory', movimientosFactory);
|
||||
}());
|
|
@ -0,0 +1,3 @@
|
|||
<section name="total">
|
||||
<h2>No se ha encontrado lo que buscabas</h2>
|
||||
</section>
|
|
@ -0,0 +1,53 @@
|
|||
<section name="nuevoMovimiento" class="row-fluid">
|
||||
<form class="form-horizontal text-left">
|
||||
<fieldset>
|
||||
<div id="legend">
|
||||
<legend class="">Introduce tus movimientos</legend>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="tipo">Tipo</label>
|
||||
<div class="controls">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success" ng-class="{'active':caja.nuevoMovimiento.esIngreso==1}" ng-click="caja.nuevoMovimiento.esIngreso=1; caja.nuevoMovimiento.esGasto=0">
|
||||
<span ng-class="{'small':caja.nuevoMovimiento.esIngreso==0}">+ Ingreso</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" ng-class="{'active':caja.nuevoMovimiento.esGasto==1}" ng-click="caja.nuevoMovimiento.esIngreso=0; caja.nuevoMovimiento.esGasto=1">
|
||||
<span ng-class="{'small':caja.nuevoMovimiento.esGasto==0}">- Gasto</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="categ">Categoría</label>
|
||||
<div class="controls">
|
||||
<select ng-show="caja.nuevoMovimiento.esIngreso" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasIngresos"></select>
|
||||
<select ng-show="caja.nuevoMovimiento.esGasto" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasGastos"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="fecha">Fecha</label>
|
||||
<div class="controls">
|
||||
<input type="date" name="fecha" placeholder="" class="input" ng-model="caja.nuevoMovimiento.fecha">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="importe">Importe</label>
|
||||
<div class="controls">
|
||||
<input type="number" name="importe" placeholder="" class="input" ng-model="caja.nuevoMovimiento.importe">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button style="margin-top: 20px" type="button" class="btn btn-lg btn-primary" ng-click="caja.guardarMovimiento()">
|
||||
<span>Guardar {{ caja.tipo(caja.nuevoMovimiento) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
{{caja.movimientos}}
|
||||
</section>
|
|
@ -0,0 +1,37 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<form class="form-horizontal">
|
||||
<fieldset>
|
||||
<div id="legend">
|
||||
<legend class="">Login</legend>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- email -->
|
||||
<label class="control-label" for="email">Email</label>
|
||||
<div class="controls">
|
||||
<input type="email" ng-model="registro.usuario.email" name="email" class="input-xlarge">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- Password-->
|
||||
<label class="control-label" for="password">Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" ng-model="registro.usuario.password" name="password" class="input-xlarge">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- Button -->
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" ng-click="registro.entrar()">Login</button>
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div class="controls">
|
||||
<button class="btn btn-success" ng-click="registro.registrar()">Registro</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,31 @@
|
|||
(function () {
|
||||
// El rootScope mantiene un ViewModel al que se puede 'bindear' cualquier vista
|
||||
// Es cómodo usarlo para compartir datos de infraestructura
|
||||
// No conviene abusar, para reducir la posibilidad de conflictos y polución de la memoria
|
||||
// El servicio $cookieStore, viene en el módulo ngCookies
|
||||
var registroCtrl = function ( $state, $http, $cookieStore) {
|
||||
var urlBase = "http://localhost:3000/api/";
|
||||
var vm = this;
|
||||
vm.usuario = {};
|
||||
vm.registrar = function () {
|
||||
$http.post(urlBase + 'usuarios/', vm.usuario)
|
||||
.success(function (data) {
|
||||
//$rootScope.nombre = vm.usuario.email;
|
||||
//$rootScope.mensaje = 'recién creado';
|
||||
$cookieStore.put("sessionId", data);
|
||||
$state.go("total");
|
||||
});
|
||||
}
|
||||
vm.entrar = function () {
|
||||
$http.post(urlBase + 'sesiones/', vm.usuario)
|
||||
.success(function (data) {
|
||||
//$rootScope.nombre = vm.usuario.email;
|
||||
//$rootScope.mensaje = 'recién entrado';
|
||||
$cookieStore.put("sessionId", data);
|
||||
$state.go("total");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
angular.module('controlCajaApp').controller('RegistroCtrl', registroCtrl);
|
||||
}());
|
|
@ -0,0 +1,38 @@
|
|||
<!-- Cada sección tendrá su propia plantilla -->
|
||||
<!-- No se especifíca el controlador, que será asignado por el enrutador -->
|
||||
<section name="total">
|
||||
<!-- Pero se supone un viweModel llamado caja-->
|
||||
<h1>{{ caja.titulo }}</h1>
|
||||
<p class="lead">Comprueba de dónde viene y a dónde va tu dinero.</p>
|
||||
<div class="row-fluid">
|
||||
<div class="row placeholders">
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label label-success">
|
||||
{{ caja.total.ingresos | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Total ingresos</h4>
|
||||
<span class="text-muted">Acumulado</span>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label label-danger">
|
||||
{{ caja.total.gastos | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Total gastos</h4>
|
||||
<span class="text-muted">Acumulado</span>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label " ng-class="{'label-success': caja.balance()>=0 , 'label-danger' : caja.balance()<0}">
|
||||
{{ caja.balance() | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Balance</h4>
|
||||
<span class="text-muted">Ingresos-Gastos</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "ControlCashFlow",
|
||||
"version": "0.0.0",
|
||||
"description": "Controla tu flujo de caja",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Alberto Basalo",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"express": "~4.1.1",
|
||||
"body-parser": "~1.0.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,187 @@
|
|||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
var app = express();
|
||||
app.use(bodyParser());
|
||||
app.use(express.static(__dirname + '/client'));
|
||||
|
||||
console.log('ready');
|
||||
|
||||
var movimientos = [];
|
||||
// Ahora necesitaremos un array, para los totales de los disitintos usuarios
|
||||
var totales = [];
|
||||
|
||||
// Arrays para guardar los datos de seguridad y sesiones
|
||||
// Por ahora van en memoria
|
||||
var usuarios = [];
|
||||
var sesiones = [];
|
||||
|
||||
// Middleware de validación de sesiones
|
||||
// Esta función se ejecuta para todas las rutas que empiecen por '/api/priv'
|
||||
app.use('/api/priv/', function (req, res, next) {
|
||||
// Obtener el token de sesión desde una de las cabeceras que nos enviará el navegador
|
||||
var sessionId = req.get('sessionId');
|
||||
var sesion = getSesion(sessionId);
|
||||
if (sesion) {
|
||||
// Controlar si está caducada
|
||||
if (esSesionValida(sesion)) {
|
||||
// La sesión es válida
|
||||
// Ampliar margen de caducidad
|
||||
sesion.timeStamp = new Date();
|
||||
// Para que el resto de la pila sepa quien es el usuario actual
|
||||
req.usuario = sesion.email;
|
||||
} else {
|
||||
// La sesión ya está caducada
|
||||
console.log('Sesión caducada:' + JSON.stringify(sesion));
|
||||
res.send(419, 'Sesión caducada');
|
||||
}
|
||||
} else {
|
||||
// No se encuentra una sesión con ese sessionId
|
||||
res.send(401, 'Credencial inválida');
|
||||
}
|
||||
// Si llegó hasta aquí entonces es una sesión válida
|
||||
// Se ejecuta la siguente función de la pila
|
||||
// En este caso la función de negocio que corresponda a la ruta
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// API - REST
|
||||
// SECURITY
|
||||
// Gestión de usuarios: Lista y registro
|
||||
app.route('/api/usuarios')
|
||||
.get(function (req, res, next) {
|
||||
// Esto devuelve la lista completa de usuarios y contraseñas
|
||||
// PELIGRO: Usar sólo a modo de debug mientras desarrollamos
|
||||
res.json(usuarios);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
// registro de un nuevo usuario
|
||||
var usuario = req.body;
|
||||
// Comprobar si ya existe
|
||||
if (existeUsuario(usuario)) {
|
||||
console.log('email ya registrado:' + usuario.email);
|
||||
res.send(409, 'email ' + usuario.email + ' ya registrado');
|
||||
} else {
|
||||
console.log('registrado:' + usuario.email);
|
||||
usuarios.push(usuario);
|
||||
res.json(newSession(usuario.email));
|
||||
}
|
||||
});
|
||||
|
||||
// Gestión de sesiones: listado y login
|
||||
app.route('/api/sesiones')
|
||||
.get(function (req, res, next) {
|
||||
// Esto devuelve la lista completa de sesiones
|
||||
// PELIGRO: Usar sólo a modo de debug mientras desarrollamos
|
||||
res.json(sesiones);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
// login
|
||||
var usuario = req.body;
|
||||
if (esUsuarioValido(usuario)) {
|
||||
console.log('aceptado:' + usuario.email);
|
||||
res.json(newSession(usuario.email));
|
||||
} else {
|
||||
console.log('Credencial inválida:' + usuario.email);
|
||||
res.send(401, 'Credencial inválida');
|
||||
}
|
||||
});
|
||||
|
||||
function existeUsuario(usuario) {
|
||||
return usuarios.some(function (u) {
|
||||
return u.email == usuario.email;
|
||||
});
|
||||
}
|
||||
|
||||
function esUsuarioValido(usuario) {
|
||||
return usuarios.filter(function (u) {
|
||||
return u.email == usuario.email && u.password == usuario.password;
|
||||
})[0];
|
||||
}
|
||||
|
||||
function getSesion(sessionId) {
|
||||
return sesiones.filter(function (s) {
|
||||
return s.sessionId == sessionId;
|
||||
})[0]
|
||||
}
|
||||
|
||||
function esSesionValida(sesion) {
|
||||
return (new Date() - sesion.timeStamp) < 20 * 60 * 1000;
|
||||
}
|
||||
|
||||
// función auxiliar para crear una nueva sesión
|
||||
function newSession(email) {
|
||||
var sessionId = Math.random() * (88888) + 11111;
|
||||
var timeStamp = new Date();
|
||||
sesiones.push({
|
||||
sessionId: sessionId,
|
||||
email: email,
|
||||
timeStamp: timeStamp
|
||||
});
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
|
||||
// BUSINESS
|
||||
app.get('/api/pub/maestros', function (req, res, next) {
|
||||
var maestros = {
|
||||
categoriasIngresos: ['Nómina', 'Ventas', 'Intereses Depósitos'],
|
||||
categoriasGastos: ['Hipotéca', 'Compras', 'Impuestos']
|
||||
};
|
||||
res.json(maestros);
|
||||
});
|
||||
|
||||
app.route('/api/priv/movimientos')
|
||||
.get(function (req, res, next) {
|
||||
// Ahora debemos filtrar los movimientos por usuario
|
||||
console.log(JSON.stringify(movimientos));
|
||||
var movimientosUsuario = movimientos.filter(function (m) {
|
||||
return m.usuario == req.usuario;
|
||||
});
|
||||
res.json(movimientosUsuario);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
var movimiento = req.body;
|
||||
// Las funciones de negocio trabajan con la info de seguridad
|
||||
// esto es posible gracias que los parametrs se pssan de unas funiones a otras
|
||||
movimiento.usuario = req.usuario;
|
||||
movimientos.push(movimiento);
|
||||
// Tambien tenemos que totalizar adecuadamente
|
||||
var totalUsuario = getTotalUsuario(req.usuario);
|
||||
if (movimiento.tipo == 'Ingreso')
|
||||
totalUsuario.ingresos += movimiento.importe;
|
||||
else
|
||||
totalUsuario.gastos += movimiento.importe;
|
||||
res.status(200).send();
|
||||
});
|
||||
|
||||
function getTotalUsuario(usuario) {
|
||||
if(usuario===undefined) return {};
|
||||
var totalUsuario = totales.filter(function (t) {
|
||||
return t.usuario == usuario;
|
||||
})[0];
|
||||
if (totalUsuario===undefined) {
|
||||
totalUsuario = {
|
||||
usuario : usuario,
|
||||
ingresos: 0,
|
||||
gastos: 0
|
||||
};
|
||||
totales.push(totalUsuario);
|
||||
}
|
||||
return totalUsuario;
|
||||
}
|
||||
|
||||
app.get('/api/priv/total', function (req, res, next) {
|
||||
// Obtenemos los totales para el usuario actual
|
||||
var totalUsuario = getTotalUsuario(req.usuario);
|
||||
res.json(totalUsuario);
|
||||
});
|
||||
|
||||
|
||||
console.log('steady');
|
||||
app.listen(3000);
|
||||
console.log('go');
|
|
@ -0,0 +1,31 @@
|
|||
// Apuntamos la nueva dependencia a ngResource
|
||||
angular.module('controlCajaApp', ['ui.router','ngCookies', 'ngResource']);
|
||||
|
||||
angular.module('controlCajaApp').config(function ($stateProvider,$locationProvider) {
|
||||
$stateProvider
|
||||
.state('total', {
|
||||
url: '/',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'total.html'
|
||||
})
|
||||
.state('nuevo', {
|
||||
url: '/nuevo',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'nuevo.html'
|
||||
})
|
||||
.state('lista', {
|
||||
url: '/lista',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'lista.html'
|
||||
})
|
||||
.state('registro', {
|
||||
url: '/registro',
|
||||
controller: 'RegistroCtrl as registro',
|
||||
templateUrl: 'registro.html'
|
||||
})
|
||||
.state('not-found', {
|
||||
url: '*path',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'total.html'
|
||||
});
|
||||
});
|
|
@ -0,0 +1,30 @@
|
|||
(function () {
|
||||
// Como ngResources usa a $http, no hay que cambiar nada a nivel de interceptores
|
||||
function configuradorInterceptores($httpProvider) {
|
||||
$httpProvider.interceptors.push(funcionInterceptoraSeguridad);
|
||||
}
|
||||
|
||||
function funcionInterceptoraSeguridad($q, $injector, $cookieStore, $rootScope) {
|
||||
|
||||
var interceptor = {};
|
||||
interceptor.request = function (request) {
|
||||
request.headers["sessionId"] = $cookieStore.get("sessionId");
|
||||
return request || $q.when(request);
|
||||
};
|
||||
interceptor.responseError = function (response) {
|
||||
var state = $injector.get('$state');
|
||||
if (response.status === 401) {
|
||||
$rootScope.mensaje = "No hay derecho!!!";
|
||||
state.go('registro');
|
||||
} else if (response.status === 419) {
|
||||
$rootScope.mensaje = "Estoy caduco!!!";
|
||||
$cookieStore.remove("sessionId")
|
||||
state.go('registro');
|
||||
};
|
||||
return $q.reject(response);
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
angular.module('controlCajaApp').config(configuradorInterceptores);
|
||||
}());
|
|
@ -0,0 +1,78 @@
|
|||
(function () {
|
||||
var cajaCtrl = function (maestrosFactory, movimientosFactory) {
|
||||
var vm = this;
|
||||
|
||||
vm.titulo = "Controla tu Cash Flow";
|
||||
|
||||
// Al usar recursos, la sintaxis se asemaja de nuevo al modelo síncrono
|
||||
// eso es posible porque AngularJS maneja internamente las promesas
|
||||
|
||||
maestrosFactory.gettingMaestros()
|
||||
.success(function (maestros) {
|
||||
vm.maestros = maestros;
|
||||
});
|
||||
//vm.maestros = maestrosFactory.get();
|
||||
|
||||
vm.nuevoMovimiento = {
|
||||
esIngreso: 1,
|
||||
esGasto: 0,
|
||||
importe: 0,
|
||||
fecha: new Date()
|
||||
};
|
||||
|
||||
// vm.nuevoMovimiento = new movimientosFactory.movimientos();
|
||||
// vm.nuevoMovimiento.esIngreso =1;
|
||||
// vm.nuevoMovimiento.fecha = new Date();
|
||||
|
||||
|
||||
movimientosFactory.gettingMovimientos()
|
||||
.success(function (movimientos) {
|
||||
vm.movimientos = movimientos;
|
||||
});
|
||||
// Si el recurso devuelve un array, tenemos usar el método query en lugar de get que es para un elemnto
|
||||
// vm.movimientos = movimientosFactory.movimientos.query();
|
||||
|
||||
|
||||
// movimientosFactory.gettingTotal()
|
||||
// .success(function (total) {
|
||||
// vm.total = total;
|
||||
// });
|
||||
vm.total = movimientosFactory.total.get();
|
||||
|
||||
vm.guardarMovimiento = function () {
|
||||
vm.nuevoMovimiento.tipo = vm.tipo(vm.nuevoMovimiento);
|
||||
movimientosFactory.postingMovimiento(vm.nuevoMovimiento)
|
||||
.success(function (postedData) {
|
||||
// cuando ha terminado el guardado del movimiento
|
||||
// es momento de pedir una actualización de datos
|
||||
movimientosFactory.gettingMovimientos()
|
||||
.success(function (movimientos) {
|
||||
vm.movimientos = movimientos;
|
||||
});
|
||||
movimientosFactory.gettingTotal()
|
||||
.success(function (total) {
|
||||
vm.total = total;
|
||||
});
|
||||
});
|
||||
// La sintaxis ahora es totalmente distinta
|
||||
// Se basa en el patrón ActiveRecord
|
||||
// Tenemos que imaginarnos los recursos como objetos 'dopados' con superpoderes
|
||||
// vm.nuevoMovimiento.$save()
|
||||
// .then(function(postedData){
|
||||
// // Igualmente, tenemos que recargar los datos tras enviar un nuevo movimiento
|
||||
// vm.movimientos = movimientosFactory.movimientos.query();
|
||||
// vm.total = movimientosFactory.total.get();
|
||||
// vm.nuevoMovimiento.importe = 0;
|
||||
// });
|
||||
|
||||
|
||||
}
|
||||
vm.balance = function () {
|
||||
return vm.total.ingresos - vm.total.gastos
|
||||
}
|
||||
vm.tipo = function (movimiento) {
|
||||
return movimiento.esIngreso && 'Ingreso' || 'Gasto'
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('CajaCtrl', cajaCtrl);
|
||||
}());
|
|
@ -0,0 +1,50 @@
|
|||
<html lang="es" ng-app="controlCajaApp">
|
||||
<head>
|
||||
<title>Control de Caja</title>
|
||||
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<meta charset="utf-8" />
|
||||
<meta lang="es" />
|
||||
<meta name="description" content="Ejemplo Control de Caja en AngularJS por Alberto Basalo" />
|
||||
<meta name="author" content="Alberto Basalo @ Ágora Binaria" />
|
||||
<meta name="application-name" content="ControlAngularJS" />
|
||||
<meta name="Keywords" content="AngularJS, ejemplo, tutorial, curso" />
|
||||
<link rel="author" href="https://plus.google.com/+AlbertoBasalo71" />
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation" ng-controller="MenuCtrl as menu">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-class="{ active: menu.isActive('total') }">
|
||||
<a ui-sref="total">Totales</a>
|
||||
</li>
|
||||
<li ng-class="{ active: menu.isActive('nuevo') }">
|
||||
<a ui-sref="nuevo">Nuevo</a>
|
||||
</li>
|
||||
<li ng-class="{ active: menu.isActive('lista') }">
|
||||
<a ui-sref="lista">Lista</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container text-center" style="padding-top:50px;" ui-view >
|
||||
|
||||
</div>
|
||||
<!-- JavaScript References -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-cookies.min.js"></script>
|
||||
<!-- El uso de recursos es opcional y por tanto viene en su propio módulo -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>
|
||||
<script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script src="appSecurity.js"></script>
|
||||
<script src="registroCtrl.js"></script>
|
||||
<script src="menuCtrl.js"></script>
|
||||
<script src="cajaCtrl.js"></script>
|
||||
<script src="movimientosFactory.js"></script>
|
||||
<script src="maestrosFactory.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,26 @@
|
|||
<section name="Lista">
|
||||
<p class="lead">Estos son tus movimientos recientes.</p>
|
||||
<br>
|
||||
<label class="control-label" for="importe">Filtrar:</label>
|
||||
<input type="text" name="filtro" placeholder="qué buscas?" class="input" ng-model="valorBuscado">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><a href="" ng-click="campo = 'fecha'; sentido = campo == 'fecha' && !sentido">Fecha</a>
|
||||
</th>
|
||||
<th>Tipo</th>
|
||||
<th>Categoría</th>
|
||||
<th><a href="" ng-click="campo = 'importe'; sentido = campo == 'importe' && !sentido">Importe</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="movimiento in caja.movimientos | filter:valorBuscado | orderBy:campo:sentido">
|
||||
<td class="text-left">{{movimiento.fecha | date}}</td>
|
||||
<td class="text-left">{{movimiento.tipo}}</td>
|
||||
<td class="text-left">{{movimiento.categoria}}</td>
|
||||
<td class="text-left" ng-class="{'text-success': movimiento.tipo=='Ingreso', 'text-danger' : movimiento.tipo=='Gasto'}">{{movimiento.importe | number:2}} €</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
|
@ -0,0 +1,20 @@
|
|||
(function () {
|
||||
var maestrosFactory = function ($resource) {
|
||||
|
||||
// var urlBase = "http://localhost:3000/api/";
|
||||
//
|
||||
// var factory = {};
|
||||
//
|
||||
// factory.gettingMaestros = function () {
|
||||
// return $http.get(urlBase+'pub/maestros');
|
||||
// };
|
||||
//
|
||||
// return factory;
|
||||
|
||||
|
||||
// El uso de recursos simplifica mucho la sintaxis
|
||||
return $resource("/api/pub/maestros/");
|
||||
};
|
||||
|
||||
angular.module('controlCajaApp').factory('maestrosFactory', maestrosFactory);
|
||||
}());
|
|
@ -0,0 +1,8 @@
|
|||
(function () {
|
||||
var menuCtrl = function ($state) {
|
||||
this.isActive = function (estado) {
|
||||
return $state.is(estado);
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('MenuCtrl',menuCtrl);
|
||||
}());
|
|
@ -0,0 +1,27 @@
|
|||
(function () {
|
||||
|
||||
var movimientosFactory = function ($resource) {
|
||||
// Las factorias terminan siendo una agrupación de recursos
|
||||
// En situaciones complejas pueden usarse para configurar promesas, cachés...
|
||||
var factory = {};
|
||||
|
||||
// Estamos devolviendo recursos, que internamente son promesas
|
||||
factory.movimientos = $resource("/api/priv/movimientos/");
|
||||
|
||||
// Realmente los métodos crud desaparecen
|
||||
factory.postingMovimiento = function (movimiento) {
|
||||
return $http.post(urlBase + 'priv/movimientos', movimiento);
|
||||
};
|
||||
|
||||
|
||||
factory.total = $resource("/api/priv/total/");
|
||||
// una alternativa es devolver las promesas y ocultar las llamadas
|
||||
// pero genera más código del necesario
|
||||
factory.total.gettingTotal = $resource("/api/priv/total/").get().$promise;
|
||||
|
||||
|
||||
return factory;
|
||||
};
|
||||
|
||||
angular.module('controlCajaApp').factory('movimientosFactory', movimientosFactory);
|
||||
}());
|
|
@ -0,0 +1,3 @@
|
|||
<section name="total">
|
||||
<h2>No se ha encontrado lo que buscabas</h2>
|
||||
</section>
|
|
@ -0,0 +1,53 @@
|
|||
<section name="nuevoMovimiento" class="row-fluid">
|
||||
<form class="form-horizontal text-left">
|
||||
<fieldset>
|
||||
<div id="legend">
|
||||
<legend class="">Introduce tus movimientos</legend>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="tipo">Tipo</label>
|
||||
<div class="controls">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success" ng-class="{'active':caja.nuevoMovimiento.esIngreso==1}" ng-click="caja.nuevoMovimiento.esIngreso=1; caja.nuevoMovimiento.esGasto=0">
|
||||
<span ng-class="{'small':caja.nuevoMovimiento.esIngreso==0}">+ Ingreso</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" ng-class="{'active':caja.nuevoMovimiento.esGasto==1}" ng-click="caja.nuevoMovimiento.esIngreso=0; caja.nuevoMovimiento.esGasto=1">
|
||||
<span ng-class="{'small':caja.nuevoMovimiento.esGasto==0}">- Gasto</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="categ">Categoría</label>
|
||||
<div class="controls">
|
||||
<select ng-show="caja.nuevoMovimiento.esIngreso" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasIngresos"></select>
|
||||
<select ng-show="caja.nuevoMovimiento.esGasto" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasGastos"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="fecha">Fecha</label>
|
||||
<div class="controls">
|
||||
<input type="date" name="fecha" placeholder="" class="input" ng-model="caja.nuevoMovimiento.fecha">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="importe">Importe</label>
|
||||
<div class="controls">
|
||||
<input type="number" name="importe" placeholder="" class="input" ng-model="caja.nuevoMovimiento.importe">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button style="margin-top: 20px" type="button" class="btn btn-lg btn-primary" ng-click="caja.guardarMovimiento()">
|
||||
<span>Guardar {{ caja.tipo(caja.nuevoMovimiento) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
{{caja.movimientos}}
|
||||
</section>
|
|
@ -0,0 +1,37 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<form class="form-horizontal">
|
||||
<fieldset>
|
||||
<div id="legend">
|
||||
<legend class="">Login</legend>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- email -->
|
||||
<label class="control-label" for="email">Email</label>
|
||||
<div class="controls">
|
||||
<input type="email" ng-model="registro.usuario.email" name="email" class="input-xlarge">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- Password-->
|
||||
<label class="control-label" for="password">Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" ng-model="registro.usuario.password" name="password" class="input-xlarge">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- Button -->
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" ng-click="registro.entrar()">Login</button>
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div class="controls">
|
||||
<button class="btn btn-success" ng-click="registro.registrar()">Registro</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,26 @@
|
|||
(function () {
|
||||
var registroCtrl = function ($rootScope, $state, $http, $cookieStore) {
|
||||
var urlBase = "http://localhost:3000/api/";
|
||||
var vm = this;
|
||||
vm.usuario = {};
|
||||
vm.entrar = function () {
|
||||
$http.post(urlBase + 'sesiones/', vm.usuario)
|
||||
.success(function (data) {
|
||||
$rootScope.nombre = vm.usuario.email;
|
||||
$rootScope.mensaje = 'recién entrado';
|
||||
$cookieStore.put("sessionId", data);
|
||||
$state.go("total");
|
||||
});
|
||||
}
|
||||
vm.registrar = function () {
|
||||
$http.post(urlBase + 'usuarios/', vm.usuario)
|
||||
.success(function (data) {
|
||||
$rootScope.nombre = vm.usuario.email;
|
||||
$rootScope.mensaje = 'recién creado';
|
||||
$cookieStore.put("sessionId", data);
|
||||
$state.go("total");
|
||||
});
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('RegistroCtrl', registroCtrl);
|
||||
}());
|
|
@ -0,0 +1,38 @@
|
|||
<!-- Cada sección tendrá su propia plantilla -->
|
||||
<!-- No se especifíca el controlador, que será asignado por el enrutador -->
|
||||
<section name="total">
|
||||
<!-- Pero se supone un viweModel llamado caja-->
|
||||
<h1>{{ caja.titulo }}</h1>
|
||||
<p class="lead">Comprueba de dónde viene y a dónde va tu dinero.</p>
|
||||
<div class="row-fluid">
|
||||
<div class="row placeholders">
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label label-success">
|
||||
{{ caja.total.ingresos | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Total ingresos</h4>
|
||||
<span class="text-muted">Acumulado</span>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label label-danger">
|
||||
{{ caja.total.gastos | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Total gastos</h4>
|
||||
<span class="text-muted">Acumulado</span>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label " ng-class="{'label-success': caja.balance()>=0 , 'label-danger' : caja.balance()<0}">
|
||||
{{ caja.balance() | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Balance</h4>
|
||||
<span class="text-muted">Ingresos-Gastos</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "ControlCashFlow",
|
||||
"version": "0.0.0",
|
||||
"description": "Controla tu flujo de caja",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Alberto Basalo",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"express": "~4.1.1",
|
||||
"body-parser": "~1.0.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
var app = express();
|
||||
app.use(bodyParser());
|
||||
app.use(express.static(__dirname + '/client'));
|
||||
|
||||
console.log('ready');
|
||||
|
||||
var movimientos = [];
|
||||
|
||||
var totales = [];
|
||||
|
||||
var usuarios = [];
|
||||
var sesiones = [];
|
||||
|
||||
app.use('/api/priv/', function (req, res, next) {
|
||||
var sessionId = req.get('sessionId');
|
||||
var sesion = getSesion(sessionId);
|
||||
if (sesion) {
|
||||
if (esSesionValida(sesion)) {
|
||||
sesion.timeStamp = new Date();
|
||||
req.usuario = sesion.email;
|
||||
} else {
|
||||
console.log('Sesión caducada:' + JSON.stringify(sesion));
|
||||
res.send(419, 'Sesión caducada');
|
||||
}
|
||||
} else {
|
||||
res.send(401, 'Credencial inválida');
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// API - REST
|
||||
// SECURITY
|
||||
app.route('/api/usuarios')
|
||||
.get(function (req, res, next) {
|
||||
// Esto devuelve la lista completa de usuarios y contraseñas
|
||||
// PELIGRO: Usar sólo a modo de debug mientras desarrollamos
|
||||
res.json(usuarios);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
var usuario = req.body;
|
||||
if (existeUsuario(usuario)) {
|
||||
console.log('email ya registrado:' + usuario.email);
|
||||
res.send(409, 'email ' + usuario.email + ' ya registrado');
|
||||
} else {
|
||||
console.log('registrado:' + usuario.email);
|
||||
usuarios.push(usuario);
|
||||
res.json(newSession(usuario.email));
|
||||
}
|
||||
});
|
||||
|
||||
app.route('/api/sesiones')
|
||||
.get(function (req, res, next) {
|
||||
// Esto devuelve la lista completa de sesiones
|
||||
// PELIGRO: Usar sólo a modo de debug mientras desarrollamos
|
||||
res.json(sesiones);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
var usuario = req.body;
|
||||
if (esUsuarioValido(usuario)) {
|
||||
console.log('aceptado:' + usuario.email);
|
||||
res.json(newSession(usuario.email));
|
||||
} else {
|
||||
console.log('Credencial inválida:' + usuario.email);
|
||||
res.send(401, 'Credencial inválida');
|
||||
}
|
||||
});
|
||||
|
||||
function existeUsuario(usuario) {
|
||||
return usuarios.some(function (u) {
|
||||
return u.email == usuario.email;
|
||||
});
|
||||
}
|
||||
|
||||
function esUsuarioValido(usuario) {
|
||||
return usuarios.filter(function (u) {
|
||||
return u.email == usuario.email && u.password == usuario.password;
|
||||
})[0];
|
||||
}
|
||||
|
||||
function getSesion(sessionId) {
|
||||
return sesiones.filter(function (s) {
|
||||
return s.sessionId == sessionId;
|
||||
})[0]
|
||||
}
|
||||
|
||||
function esSesionValida(sesion) {
|
||||
return (new Date() - sesion.timeStamp) < 20 * 60 * 1000;
|
||||
}
|
||||
|
||||
function newSession(email) {
|
||||
var sessionId = Math.random() * (88888) + 11111;
|
||||
var timeStamp = new Date();
|
||||
sesiones.push({
|
||||
sessionId: sessionId,
|
||||
email: email,
|
||||
timeStamp: timeStamp
|
||||
});
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
|
||||
// BUSINESS
|
||||
app.get('/api/pub/maestros', function (req, res, next) {
|
||||
var maestros = {
|
||||
categoriasIngresos: ['Nómina', 'Ventas', 'Intereses Depósitos'],
|
||||
categoriasGastos: ['Hipotéca', 'Compras', 'Impuestos']
|
||||
};
|
||||
res.json(maestros);
|
||||
});
|
||||
|
||||
app.route('/api/priv/movimientos')
|
||||
.get(function (req, res, next) {
|
||||
console.log(JSON.stringify(movimientos));
|
||||
var movimientosUsuario = movimientos.filter(function (m) {
|
||||
return m.usuario == req.usuario;
|
||||
});
|
||||
res.json(movimientosUsuario);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
var movimiento = req.body;
|
||||
movimiento.usuario = req.usuario;
|
||||
movimientos.push(movimiento);
|
||||
var totalUsuario = getTotalUsuario(req.usuario);
|
||||
if (movimiento.tipo == 'Ingreso')
|
||||
totalUsuario.ingresos += movimiento.importe;
|
||||
else
|
||||
totalUsuario.gastos += movimiento.importe;
|
||||
res.status(200).send();
|
||||
});
|
||||
|
||||
function getTotalUsuario(usuario) {
|
||||
if(usuario===undefined) return {};
|
||||
var totalUsuario = totales.filter(function (t) {
|
||||
return t.usuario == usuario;
|
||||
})[0];
|
||||
if (totalUsuario===undefined) {
|
||||
totalUsuario = {
|
||||
usuario : usuario,
|
||||
ingresos: 0,
|
||||
gastos: 0
|
||||
};
|
||||
totales.push(totalUsuario);
|
||||
}
|
||||
return totalUsuario;
|
||||
}
|
||||
|
||||
app.get('/api/priv/total', function (req, res, next) {
|
||||
var totalUsuario = getTotalUsuario(req.usuario);
|
||||
res.json(totalUsuario);
|
||||
});
|
||||
|
||||
|
||||
console.log('steady');
|
||||
app.listen(3000);
|
||||
console.log('go');
|
|
@ -0,0 +1,35 @@
|
|||
angular.module('controlCajaApp', ['ui.router','ngCookies', 'ngResource']);
|
||||
|
||||
angular.module('controlCajaApp').config(function ($stateProvider,$locationProvider) {
|
||||
$stateProvider
|
||||
.state('total', {
|
||||
url: '/',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'total.html'
|
||||
})
|
||||
.state('nuevo', {
|
||||
url: '/nuevo',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'nuevo.html'
|
||||
})
|
||||
.state('lista', {
|
||||
url: '/lista',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'lista.html'
|
||||
})
|
||||
.state('movimiento', {
|
||||
url: '/movimiento/:id', // declaracion de parametros en rutas
|
||||
controller: 'MovimientoCtrl as vm',
|
||||
templateUrl: 'movimiento.html'
|
||||
})
|
||||
.state('registro', {
|
||||
url: '/registro',
|
||||
controller: 'RegistroCtrl as registro',
|
||||
templateUrl: 'registro.html'
|
||||
})
|
||||
.state('not-found', {
|
||||
url: '*path',
|
||||
controller: 'CajaCtrl as caja',
|
||||
templateUrl: 'total.html'
|
||||
});
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
(function () {
|
||||
|
||||
function configuradorInterceptores($httpProvider) {
|
||||
$httpProvider.interceptors.push(funcionInterceptoraLog);
|
||||
}
|
||||
|
||||
// Esta función se especializa en escribir en la consola
|
||||
// información sobre llamadas http
|
||||
function funcionInterceptoraLog($log) {
|
||||
|
||||
var interceptor = {};
|
||||
interceptor.request = function (request) {
|
||||
$log.info('request:' + request.url);
|
||||
return request ;
|
||||
};
|
||||
interceptor.responseError = function (response) {
|
||||
$log.error("excepción: " + response.status + " de :" + response.config.url);
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
angular.module('controlCajaApp').config(configuradorInterceptores);
|
||||
}());
|
|
@ -0,0 +1,29 @@
|
|||
(function () {
|
||||
function configuradorInterceptores($httpProvider) {
|
||||
$httpProvider.interceptors.push(funcionInterceptoraSeguridad);
|
||||
}
|
||||
|
||||
function funcionInterceptoraSeguridad($q, $injector, $cookieStore, $rootScope) {
|
||||
|
||||
var interceptor = {};
|
||||
interceptor.request = function (request) {
|
||||
request.headers["sessionId"] = $cookieStore.get("sessionId");
|
||||
return request || $q.when(request);
|
||||
};
|
||||
interceptor.responseError = function (response) {
|
||||
var state = $injector.get('$state');
|
||||
if (response.status === 401) {
|
||||
$rootScope.mensaje = "No hay derecho!!!";
|
||||
state.go('registro');
|
||||
} else if (response.status === 419) {
|
||||
$rootScope.mensaje = "Estoy caduco!!!";
|
||||
$cookieStore.remove("sessionId")
|
||||
state.go('registro');
|
||||
};
|
||||
return $q.reject(response);
|
||||
}
|
||||
return interceptor;
|
||||
}
|
||||
|
||||
angular.module('controlCajaApp').config(configuradorInterceptores);
|
||||
}());
|
|
@ -0,0 +1,34 @@
|
|||
(function () {
|
||||
var cajaCtrl = function (maestrosFactory, movimientosFactory) {
|
||||
var vm = this;
|
||||
|
||||
vm.titulo = "Controla tu Cash Flow";
|
||||
|
||||
vm.maestros = maestrosFactory.get();
|
||||
|
||||
vm.nuevoMovimiento = new movimientosFactory.movimientos();
|
||||
vm.nuevoMovimiento.esIngreso =1;
|
||||
vm.nuevoMovimiento.fecha = new Date();
|
||||
|
||||
vm.movimientos = movimientosFactory.movimientos.query();
|
||||
|
||||
vm.total = movimientosFactory.total.get();
|
||||
|
||||
vm.guardarMovimiento = function () {
|
||||
vm.nuevoMovimiento.tipo = vm.tipo(vm.nuevoMovimiento);
|
||||
vm.nuevoMovimiento.$save()
|
||||
.then(function(postedData){
|
||||
vm.movimientos = movimientosFactory.movimientos.query();
|
||||
vm.total = movimientosFactory.total.get();
|
||||
vm.nuevoMovimiento.importe = 0;
|
||||
});
|
||||
}
|
||||
vm.balance = function () {
|
||||
return vm.total.ingresos - vm.total.gastos
|
||||
}
|
||||
vm.tipo = function (movimiento) {
|
||||
return movimiento.esIngreso && 'Ingreso' || 'Gasto'
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('CajaCtrl', cajaCtrl);
|
||||
}());
|
|
@ -0,0 +1,53 @@
|
|||
<html lang="es" ng-app="controlCajaApp">
|
||||
<head>
|
||||
<title>Control de Caja</title>
|
||||
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<meta charset="utf-8" />
|
||||
<meta lang="es" />
|
||||
<meta name="description" content="Ejemplo Control de Caja en AngularJS por Alberto Basalo" />
|
||||
<meta name="author" content="Alberto Basalo @ Ágora Binaria" />
|
||||
<meta name="application-name" content="ControlAngularJS" />
|
||||
<meta name="Keywords" content="AngularJS, ejemplo, tutorial, curso" />
|
||||
<link rel="author" href="https://plus.google.com/+AlbertoBasalo71" />
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-default navbar-fixed-top" role="navigation" ng-controller="MenuCtrl as menu">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<ul class="nav navbar-nav">
|
||||
<li ng-class="{ active: menu.isActive('total') }">
|
||||
<a ui-sref="total">Totales</a>
|
||||
</li>
|
||||
<li ng-class="{ active: menu.isActive('nuevo') }">
|
||||
<a ui-sref="nuevo">Nuevo</a>
|
||||
</li>
|
||||
<li ng-class="{ active: menu.isActive('lista') }">
|
||||
<a ui-sref="lista">Lista</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container text-center" style="padding-top:50px;" ui-view >
|
||||
|
||||
</div>
|
||||
<!-- JavaScript References -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-cookies.min.js"></script>
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular-resource.min.js"></script>
|
||||
<script src="http://angular-ui.github.io/ui-router/release/angular-ui-router.min.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<!-- Necesitamos colocar delante el configurador del Log, para saber que ocurre pase lo que pase -->
|
||||
<script src="appHttpLog.js"></script>
|
||||
<script src="appSecurity.js"></script>
|
||||
<script src="registroCtrl.js"></script>
|
||||
<script src="menuCtrl.js"></script>
|
||||
<script src="cajaCtrl.js"></script>
|
||||
<!-- El controlador del estado movimiento -->
|
||||
<script src="movimientoCtrl.js"></script>
|
||||
<script src="movimientosFactory.js"></script>
|
||||
<script src="maestrosFactory.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,31 @@
|
|||
<section name="Lista">
|
||||
<p class="lead">Estos son tus movimientos recientes.</p>
|
||||
<br>
|
||||
<label class="control-label" for="importe">Filtrar:</label>
|
||||
<input type="text" name="filtro" placeholder="qué buscas?" class="input" ng-model="valorBuscado">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<!-- Nueva columna para el Id-->
|
||||
<th>Id</th>
|
||||
<th><a href="" ng-click="campo = 'fecha'; sentido = campo == 'fecha' && !sentido">Fecha</a>
|
||||
</th>
|
||||
<th>Tipo</th>
|
||||
<th>Categoría</th>
|
||||
<th><a href="" ng-click="campo = 'importe'; sentido = campo == 'importe' && !sentido">Importe</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="movimiento in caja.movimientos | filter:valorBuscado | orderBy:campo:sentido">
|
||||
<!-- Nueva columna para el Id, con enlace para ver ese movimiento en otra página-->
|
||||
<!-- El enlace se formula como un cambio de estado con parámetros-->
|
||||
<td class="text-left"><a ui-sref="movimiento({id: movimiento.id})">{{movimiento.id }}</a> </td>
|
||||
<td class="text-left">{{movimiento.fecha | date}}</td>
|
||||
<td class="text-left">{{movimiento.tipo}}</td>
|
||||
<td class="text-left">{{movimiento.categoria}}</td>
|
||||
<td class="text-left" ng-class="{'text-success': movimiento.tipo=='Ingreso', 'text-danger' : movimiento.tipo=='Gasto'}">{{movimiento.importe | number:2}} €</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
|
@ -0,0 +1,9 @@
|
|||
(function () {
|
||||
var maestrosFactory = function ($resource) {
|
||||
// Los resursos son totalmente configurables
|
||||
// Una de sus utilidades de 'fábrica' de una caché simple pero potente
|
||||
return $resource("/api/pub/maestros/",{},{get: {cache: true}});
|
||||
};
|
||||
|
||||
angular.module('controlCajaApp').factory('maestrosFactory', maestrosFactory);
|
||||
}());
|
|
@ -0,0 +1,8 @@
|
|||
(function () {
|
||||
var menuCtrl = function ($state) {
|
||||
this.isActive = function (estado) {
|
||||
return $state.is(estado);
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('MenuCtrl',menuCtrl);
|
||||
}());
|
|
@ -0,0 +1,41 @@
|
|||
<section name="verMovimiento" class="row-fluid">
|
||||
<form class="form-horizontal text-left">
|
||||
<fieldset>
|
||||
<div id="legend">
|
||||
<legend class="">Ver el movimiento {{ vm.movimiento.id}} </legend>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="tipo">Tipo</label>
|
||||
<div class="controls">
|
||||
<div class="btn-group">
|
||||
{{ vm.movimiento.tipo}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="categ">Categoría</label>
|
||||
<div class="controls">
|
||||
{{ vm.movimiento.categoria }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="fecha">Fecha</label>
|
||||
<div class="controls">
|
||||
{{ vm.movimiento.fecha || date }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="importe">Importe</label>
|
||||
<div class="controls">
|
||||
{{ vm.movimiento.importe }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</section>
|
|
@ -0,0 +1,12 @@
|
|||
(function () {
|
||||
// $stateParams es el servicio de ui-router para acceder a los parámetros de la ruta
|
||||
var movimientoCtrl = function ($stateParams, movimientosFactory) {
|
||||
var vm = this;
|
||||
// El acceso es por nombre de parámetro
|
||||
var movId = $stateParams.id;
|
||||
// Se llama al recurso expuesto por la factoría de recursos
|
||||
// enviándole el objeto que sirve de consulta al método get
|
||||
vm.movimiento = movimientosFactory.movimientos.get({id:movId});
|
||||
}
|
||||
angular.module('controlCajaApp').controller('MovimientoCtrl', movimientoCtrl);
|
||||
}());
|
|
@ -0,0 +1,25 @@
|
|||
(function () {
|
||||
|
||||
var movimientosFactory = function ($resource) {
|
||||
|
||||
var factory = {};
|
||||
|
||||
// $resource("/api/priv/movimientos/");
|
||||
// El uso de parametros en los recursos debe especificarse
|
||||
// Indicando el nombre local y el remoto
|
||||
// En la url va el nombre remoto
|
||||
// En el objeto se repute ese nombre como clave y se dice que propiedad será la que lo lleve
|
||||
// La propiedad empieza por @
|
||||
factory.movimientos = $resource("/api/priv/movimientos/:id", {
|
||||
id: "@id"
|
||||
})
|
||||
|
||||
|
||||
|
||||
factory.total = $resource("/api/priv/total/");
|
||||
|
||||
return factory;
|
||||
};
|
||||
|
||||
angular.module('controlCajaApp').factory('movimientosFactory', movimientosFactory);
|
||||
}());
|
|
@ -0,0 +1,3 @@
|
|||
<section name="total">
|
||||
<h2>No se ha encontrado lo que buscabas</h2>
|
||||
</section>
|
|
@ -0,0 +1,53 @@
|
|||
<section name="nuevoMovimiento" class="row-fluid">
|
||||
<form class="form-horizontal text-left">
|
||||
<fieldset>
|
||||
<div id="legend">
|
||||
<legend class="">Introduce tus movimientos</legend>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="tipo">Tipo</label>
|
||||
<div class="controls">
|
||||
<div class="btn-group">
|
||||
<button type="button" class="btn btn-success" ng-class="{'active':caja.nuevoMovimiento.esIngreso==1}" ng-click="caja.nuevoMovimiento.esIngreso=1; caja.nuevoMovimiento.esGasto=0">
|
||||
<span ng-class="{'small':caja.nuevoMovimiento.esIngreso==0}">+ Ingreso</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" ng-class="{'active':caja.nuevoMovimiento.esGasto==1}" ng-click="caja.nuevoMovimiento.esIngreso=0; caja.nuevoMovimiento.esGasto=1">
|
||||
<span ng-class="{'small':caja.nuevoMovimiento.esGasto==0}">- Gasto</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="categ">Categoría</label>
|
||||
<div class="controls">
|
||||
<select ng-show="caja.nuevoMovimiento.esIngreso" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasIngresos"></select>
|
||||
<select ng-show="caja.nuevoMovimiento.esGasto" name="categoria" ng-model="caja.nuevoMovimiento.categoria" ng-options="categoria for categoria in caja.maestros.categoriasGastos"></select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6">
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="fecha">Fecha</label>
|
||||
<div class="controls">
|
||||
<input type="date" name="fecha" placeholder="" class="input" ng-model="caja.nuevoMovimiento.fecha">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="importe">Importe</label>
|
||||
<div class="controls">
|
||||
<input type="number" name="importe" placeholder="" class="input" ng-model="caja.nuevoMovimiento.importe">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<button style="margin-top: 20px" type="button" class="btn btn-lg btn-primary" ng-click="caja.guardarMovimiento()">
|
||||
<span>Guardar {{ caja.tipo(caja.nuevoMovimiento) }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
{{caja.movimientos}}
|
||||
</section>
|
|
@ -0,0 +1,37 @@
|
|||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="span12">
|
||||
<form class="form-horizontal">
|
||||
<fieldset>
|
||||
<div id="legend">
|
||||
<legend class="">Login</legend>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- email -->
|
||||
<label class="control-label" for="email">Email</label>
|
||||
<div class="controls">
|
||||
<input type="email" ng-model="registro.usuario.email" name="email" class="input-xlarge">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- Password-->
|
||||
<label class="control-label" for="password">Password</label>
|
||||
<div class="controls">
|
||||
<input type="password" ng-model="registro.usuario.password" name="password" class="input-xlarge">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<!-- Button -->
|
||||
<div class="controls">
|
||||
<button class="btn btn-primary" ng-click="registro.entrar()">Login</button>
|
||||
</div>
|
||||
<!-- Button -->
|
||||
<div class="controls">
|
||||
<button class="btn btn-success" ng-click="registro.registrar()">Registro</button>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,26 @@
|
|||
(function () {
|
||||
var registroCtrl = function ($rootScope, $state, $http, $cookieStore) {
|
||||
var urlBase = "http://localhost:3000/api/";
|
||||
var vm = this;
|
||||
vm.usuario = {};
|
||||
vm.entrar = function () {
|
||||
$http.post(urlBase + 'sesiones/', vm.usuario)
|
||||
.success(function (data) {
|
||||
$rootScope.nombre = vm.usuario.email;
|
||||
$rootScope.mensaje = 'recién entrado';
|
||||
$cookieStore.put("sessionId", data);
|
||||
$state.go("total");
|
||||
});
|
||||
}
|
||||
vm.registrar = function () {
|
||||
$http.post(urlBase + 'usuarios/', vm.usuario)
|
||||
.success(function (data) {
|
||||
$rootScope.nombre = vm.usuario.email;
|
||||
$rootScope.mensaje = 'recién creado';
|
||||
$cookieStore.put("sessionId", data);
|
||||
$state.go("total");
|
||||
});
|
||||
}
|
||||
}
|
||||
angular.module('controlCajaApp').controller('RegistroCtrl', registroCtrl);
|
||||
}());
|
|
@ -0,0 +1,38 @@
|
|||
<!-- Cada sección tendrá su propia plantilla -->
|
||||
<!-- No se especifíca el controlador, que será asignado por el enrutador -->
|
||||
<section name="total">
|
||||
<!-- Pero se supone un viweModel llamado caja-->
|
||||
<h1>{{ caja.titulo }}</h1>
|
||||
<p class="lead">Comprueba de dónde viene y a dónde va tu dinero.</p>
|
||||
<div class="row-fluid">
|
||||
<div class="row placeholders">
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label label-success">
|
||||
{{ caja.total.ingresos | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Total ingresos</h4>
|
||||
<span class="text-muted">Acumulado</span>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label label-danger">
|
||||
{{ caja.total.gastos | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Total gastos</h4>
|
||||
<span class="text-muted">Acumulado</span>
|
||||
</div>
|
||||
<div class="col-xs-8 col-sm-4 placeholder">
|
||||
<h1>
|
||||
<span class="label " ng-class="{'label-success': caja.balance()>=0 , 'label-danger' : caja.balance()<0}">
|
||||
{{ caja.balance() | number:2 }} €
|
||||
</span>
|
||||
</h1>
|
||||
<h4>Balance</h4>
|
||||
<span class="text-muted">Ingresos-Gastos</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"name": "ControlCashFlow",
|
||||
"version": "0.0.0",
|
||||
"description": "Controla tu flujo de caja",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "Alberto Basalo",
|
||||
"license": "BSD-2-Clause",
|
||||
"dependencies": {
|
||||
"express": "~4.12.1",
|
||||
"body-parser": "~1.0.2"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
var express = require('express');
|
||||
var bodyParser = require('body-parser');
|
||||
|
||||
var app = express();
|
||||
app.use(bodyParser());
|
||||
app.use(express.static(__dirname + '/client'));
|
||||
|
||||
// Podemos incrustar middelware propio para mostrar por consola todas las llamadas
|
||||
// Por supuesto hay cientos de paquetes especializados en esta labor
|
||||
//app.use(function (peticion, respuesta, siguiente) {
|
||||
// console.log("recibida petición: " + peticion.url);
|
||||
// if (peticion.body && Object.keys(peticion.body).length>0) {
|
||||
// console.log("body: " + JSON.stringify(peticion.body));
|
||||
// }
|
||||
// siguiente();
|
||||
// });
|
||||
|
||||
console.log('ready');
|
||||
|
||||
// Mientras no tengamos base de datos usaremos un contador para ir generando Ids
|
||||
var maxId = 0;
|
||||
var movimientos = [];
|
||||
var totales = [];
|
||||
|
||||
var usuarios = [];
|
||||
var sesiones = [];
|
||||
|
||||
|
||||
app.use('/api/priv/', function (req, res, next) {
|
||||
var sessionId = req.get('sessionId');
|
||||
var sesion = getSesion(sessionId);
|
||||
if (sesion) {
|
||||
if (esSesionValida(sesion)) {
|
||||
sesion.timeStamp = new Date();
|
||||
req.usuario = sesion.email;
|
||||
next();
|
||||
} else {
|
||||
console.log('Sesión caducada:' + JSON.stringify(sesion));
|
||||
// Sintaxis mejorada de envío de códigos de estado http
|
||||
res.status(419).send('Sesión caducada');
|
||||
}
|
||||
} else {
|
||||
res.status(401).send('Credencial inválida');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// API - REST
|
||||
// SECURITY
|
||||
app.route('/api/usuarios')
|
||||
.get(function (req, res, next) {
|
||||
// Esto devuelve la lista completa de usuarios y contraseñas
|
||||
// PELIGRO: Usar sólo a modo de debug mientras desarrollamos
|
||||
res.json(usuarios);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
var usuario = req.body;
|
||||
if (existeUsuario(usuario)) {
|
||||
console.log('email ya registrado:' + usuario.email);
|
||||
res.status(409).send('email ' + usuario.email + ' ya registrado');
|
||||
} else {
|
||||
console.log('registrado:' + usuario.email);
|
||||
usuarios.push(usuario);
|
||||
res.json(newSession(usuario.email));
|
||||
}
|
||||
});
|
||||
|
||||
// Gestión de sesiones: listado y login
|
||||
app.route('/api/sesiones')
|
||||
.get(function (req, res, next) {
|
||||
// Esto devuelve la lista completa de sesiones
|
||||
// PELIGRO: Usar sólo a modo de debug mientras desarrollamos
|
||||
res.json(sesiones);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
var usuario = req.body;
|
||||
if (esUsuarioValido(usuario)) {
|
||||
console.log('aceptado:' + usuario.email);
|
||||
res.json(newSession(usuario.email));
|
||||
} else {
|
||||
console.log('Credencial inválida:' + usuario.email);
|
||||
res.status(401).send('Credencial inválida');
|
||||
}
|
||||
});
|
||||
|
||||
function existeUsuario(usuario) {
|
||||
return usuarios.some(function (u) {
|
||||
return u.email == usuario.email;
|
||||
});
|
||||
}
|
||||
|
||||
function esUsuarioValido(usuario) {
|
||||
return usuarios.filter(function (u) {
|
||||
return u.email == usuario.email && u.password == usuario.password;
|
||||
})[0];
|
||||
}
|
||||
|
||||
function getSesion(sessionId) {
|
||||
return sesiones.filter(function (s) {
|
||||
return s.sessionId == sessionId;
|
||||
})[0]
|
||||
}
|
||||
|
||||
function esSesionValida(sesion) {
|
||||
return (new Date() - sesion.timeStamp) < 20 * 60 * 1000;
|
||||
}
|
||||
|
||||
function newSession(email) {
|
||||
var sessionId = Math.random() * (88888) + 11111;
|
||||
var timeStamp = new Date();
|
||||
sesiones.push({
|
||||
sessionId: sessionId,
|
||||
email: email,
|
||||
timeStamp: timeStamp
|
||||
});
|
||||
return sessionId;
|
||||
}
|
||||
|
||||
|
||||
// BUSINESS
|
||||
app.get('/api/pub/maestros', function (req, res, next) {
|
||||
var maestros = {
|
||||
categoriasIngresos: ['Nómina', 'Ventas', 'Intereses Depósitos'],
|
||||
categoriasGastos: ['Hipotéca', 'Compras', 'Impuestos']
|
||||
};
|
||||
res.json(maestros);
|
||||
});
|
||||
|
||||
app.route('/api/priv/movimientos')
|
||||
.get(function (req, res, next) {
|
||||
var movimientosUsuario = movimientos.filter(function (m) {
|
||||
return m.usuario = req.usuario;
|
||||
});
|
||||
res.json(movimientosUsuario);
|
||||
})
|
||||
.post(function (req, res, next) {
|
||||
var movimiento = req.body;
|
||||
movimiento.usuario = req.usuario;
|
||||
// Avanzamos el contador para asignar un nuevo id al movimiento
|
||||
maxId++;
|
||||
movimiento.id = maxId;
|
||||
movimientos.push(movimiento);
|
||||
var totalUsuario = getTotalUsuario(req.usuario);
|
||||
if (movimiento.tipo == 'Ingreso')
|
||||
totalUsuario.ingresos += movimiento.importe;
|
||||
else
|
||||
totalUsuario.gastos += movimiento.importe;
|
||||
res.status(200).send();
|
||||
});
|
||||
|
||||
// Para las rutas paramétircas creamos otro patrón
|
||||
// El recurso sigue siendo movimientos, y el parámetro se declara con :id
|
||||
// Obtención a partir de parámetros
|
||||
app.get('/api/priv/movimientos/:id', function (req, res, next) {
|
||||
var movId = req.params.id;
|
||||
// Buscar en el array el movimiento con este id
|
||||
movimientoBuscado = getMovimientoById(movId, req.usuario);
|
||||
res.json(movimientoBuscado);
|
||||
});
|
||||
|
||||
function getMovimientoById(id,usuario) {
|
||||
var movimientoBuscado = movimientos.filter(function (movimiento) {
|
||||
return movimiento.id == id && movimiento.usuario == usuario;
|
||||
})[0];
|
||||
return movimientoBuscado;
|
||||
}
|
||||
|
||||
app.get('/api/priv/total', function (req, res, next) {
|
||||
var totalUsuario = getTotalUsuario(req.usuario);
|
||||
res.json(totalUsuario);
|
||||
});
|
||||
|
||||
function getTotalUsuario(usuario) {
|
||||
if(usuario===undefined) return {};
|
||||
var totalUsuario = totales.filter(function (t) {
|
||||
return t.usuario == usuario;
|
||||
})[0];
|
||||
if (totalUsuario===undefined) {
|
||||
totalUsuario = {
|
||||
usuario : usuario,
|
||||
ingresos: 0,
|
||||
gastos: 0
|
||||
};
|
||||
totales.push(totalUsuario);
|
||||
}
|
||||
return totalUsuario;
|
||||
}
|
||||
|
||||
|
||||
console.log('steady');
|
||||
app.listen(3000);
|
||||
console.log('go');
|
Загрузка…
Ссылка в новой задаче