Added step-control component and basic data step controllers

This commit is contained in:
Joan Sanchez 2018-04-18 09:47:05 +02:00
parent 91a7f5a45c
commit 228239b027
21 changed files with 347 additions and 158 deletions

View File

@ -29,4 +29,5 @@ import './combo/combo';
import './card/card';
import './switch/switch';
import './float-button/float-button';
import './step-control/step-control';
import './label-value/label-value';

View File

@ -0,0 +1,33 @@
<section class="step-control">
<section class="steps">
<section class="step" ng-repeat="step in $ctrl.steps track by $index">
<section class="circle"
vn-tooltip="{{::step.name}}"
tooltip-position="down"
ng-click="$ctrl.currentState = step.state"
ng-class="{active: step.state == $ctrl.currentState}">
</section>
</section>
</section>
<section class="buttons">
<section class="step">
<vn-button
ng-if="$ctrl.canMovePrevious()"
label="Previous"
ng-click="$ctrl.onPreviousClick()">
</vn-button>
</section>
<section class="step">
<vn-button
ng-if="$ctrl.canMoveNext()"
label="Next"
ng-click="$ctrl.onNextClick()">
</vn-button>
<vn-submit
ng-if="$ctrl.canFinalize()"
label="Finalize"
ng-click="$ctrl.onStepEnd()">
</vn-submit>
</section>
</section>
</section>

View File

@ -0,0 +1,71 @@
import ngModule from '../../module';
import './style.scss';
export default class StepControl {
constructor($state) {
this.$state = $state;
}
set currentState(state) {
let isAllowed = true;
if (this.onStepChange)
isAllowed = this.onStepChange({state});
if (isAllowed)
this.$state.go(state);
}
get totalSteps() {
return this.steps.length;
}
get currentState() {
return this.$state.current.name;
}
get currentStepIndex() {
for (let i = 0; i < this.steps.length; i++) {
if (this.steps[i].state == this.$state.current.name)
return i;
}
}
onPreviousClick() {
let state = this.steps[this.currentStepIndex - 1].state;
this.currentState = state;
}
onNextClick() {
let state = this.steps[this.currentStepIndex + 1].state;
this.currentState = state;
}
canMovePrevious() {
return this.steps[0].state != this.currentState;
}
canFinalize() {
let lastStep = this.steps[this.totalSteps - 1];
return lastStep.state == this.currentState;
}
canMoveNext() {
let lastStep = this.steps[this.totalSteps - 1];
return lastStep.state != this.currentState;
}
}
StepControl.$inject = ['$state'];
ngModule.component('vnStepControl', {
template: require('./step-control.html'),
controller: StepControl,
bindings: {
steps: '<',
onStepChange: '&?',
onStepEnd: '&?'
}
});

View File

@ -0,0 +1,49 @@
import './step-control.js';
describe('Component vnStepControl', () => {
let $componentController;
let controller;
let $state;
beforeEach(() => {
angular.mock.module('ticket');
});
beforeEach(angular.mock.inject((_$componentController_, _$state_) => {
$componentController = _$componentController_;
$state = _$state_;
controller = $componentController('vnStepControl', {$state: $state});
}));
describe('currentState()', () => {
it(`should call the onStepChange method then return false and never call the go method`, () => {
controller.onStepChange = jasmine.createSpy(controller, 'onStepChange').and.returnValue(false);
spyOn(controller.$state, 'go');
controller.currentState = "something";
expect(controller.onStepChange).toHaveBeenCalledWith({state: 'something'});
expect(controller.$state.go).not.toHaveBeenCalledWith("something");
});
it(`should call the onStepChange method then return true and finally call the go method`, () => {
controller.onStepChange = jasmine.createSpy(controller, 'onStepChange').and.returnValue(true);
spyOn(controller.$state, 'go');
controller.currentState = "something";
expect(controller.onStepChange).toHaveBeenCalledWith({state: 'something'});
expect(controller.$state.go).toHaveBeenCalledWith("something");
});
});
describe('currentStepIndex()', () => {
it('should get the current state index from an Array of states', () => {
controller.$state.current.name = 'iam_a_current_state';
controller.steps = [{state: 'iam_not_current_state'}, {state: 'iam_a_current_state'}];
let result = controller.currentStepIndex;
expect(result).toEqual(1);
});
});
});

View File

@ -0,0 +1,58 @@
vn-step-control {
display: flex;
justify-content: center;
.step-control {
border-top: 2px solid rgb(255,152,0);
margin-bottom: 15px;
& > .steps {
display: flex;
flex-direction: row
}
& > .steps > .step {
justify-content: center;
min-width: 125px;
display: flex;
flex: auto
}
& > .steps > .step .circle {
border: 2px solid rgb(255,152,0);
background-color: #FFF;
align-content: center;
margin-top: -9.5px;
-moz-border-radius: 50%;
-webkit-border-radius: 50%;
border-radius: 50%;
cursor: pointer;
height: 15px;
width: 15px
}
& > .steps > .step .circle.active {
background-color: rgb(255,152,0);
}
& > .buttons {
display: flex;
flex: auto;
flex-direction: row;
justify-content: space-between;
margin-top: 10px
}
& > .buttons > .step {
display: flex
}
& > .buttons > .step > .mdl-button {
line-height: 32px;
font-size: 12px;
padding: 0 12px;
height: 32px
}
}
}

View File

@ -7,4 +7,7 @@ Add: Add
Search: Search
Show More: Show More
No more results: No more results
Hide: Hide
Hide: Hide
Next: Next
Finalize: Finalize
Previous: Back

View File

@ -7,4 +7,7 @@ Add: Añadir
Search: Buscar
Show More: Ver más
No more results: No hay más resultados
Hide: Ocultar
Hide: Ocultar
Next: Siguiente
Finalize: Finalizar
Previous: Anterior

View File

@ -1,4 +1,4 @@
<form name="form" ng-submit="$ctrl.onSubmit()" ng-cloak>
<form name="form">
<vn-card pad-large>
<vn-title>Basic data</vn-title>
<vn-horizontal>
@ -19,7 +19,7 @@
initial-data="$ctrl.ticket.addressFk">
</vn-autocomplete>
<vn-autocomplete vn-one
url="/api/AgencyModes/"
url="/api/AgencyModes"
label="Agency"
show-field="name"
value-field="id"
@ -48,7 +48,4 @@
</vn-autocomplete>
</vn-horizontal>
</vn-card>
<vn-button-bar>
<vn-submit label="Next"></vn-submit>
</vn-button-bar>
</form>

View File

@ -1,22 +1,17 @@
import ngModule from '../../module';
class Controller {
constructor($scope, $timeout, $state) {
constructor($scope) {
this.$scope = $scope;
this.$timeout = $timeout;
this.$state = $state;
}
onSubmit() {
this.$state.go('ticket.card.data.stepTwo');
}
}
Controller.$inject = ['$scope', '$timeout', '$state'];
Controller.$inject = ['$scope'];
ngModule.component('vnTicketDataStepOne', {
template: require('./step-one.html'),
controller: Controller,
bindings: {
ticket: '<'
},
controller: Controller
}
});

View File

@ -0,0 +1,8 @@
<form name="form">
<vn-card pad-large>
<vn-title>Step tree</vn-title>
<vn-horizontal>
Ticket id {{$ctrl.ticket.id}}
</vn-horizontal>
</vn-card>
</form>

View File

@ -0,0 +1,15 @@
import ngModule from '../../module';
class Controller {
constructor($scope) {
this.$scope = $scope;
}
}
ngModule.component('vnTicketDataStepThree', {
template: require('./step-three.html'),
controller: Controller,
bindings: {
ticket: '<'
}
});

View File

@ -0,0 +1,29 @@
<form name="form">
<vn-card pad-large>
<vn-title>Price gap</vn-title>
<vn-horizontal>
<table class="vn-grid">
<thead>
<tr>
<th number translate>Item</th>
<th translate style="text-align:center">Description</th>
<th number translate>Quantity</th>
<th number translate>Price</th>
<th number translate>New price</th>
<th number translate>Price gap</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="sale in $ctrl.ticket.sales track by sale.id">
<td number>{{("000000"+sale.itemFk).slice(-6)}}</td>
<td><vn-fetched-tags sale="sale"/></td>
<td number>{{::sale.quantity}}</td>
<td number>{{::sale.price | currency:'€':2}}</td>
<td number>-</td>
<td number>-</td>
</tr>
</tbody>
</table>
</vn-horizontal>
</vn-card>
</form>

View File

@ -0,0 +1,28 @@
import ngModule from '../../module';
class Controller {
constructor($http, $scope) {
this.$http = $http;
this.$scope = $scope;
}
$onChanges(data) {
if (!this.ticket || !this.ticket.id) return;
let query = `/ticket/api/sales/${this.ticket.id}/priceGap`;
this.$http.get(query).then(res => {
if (res.data)
this.ticket.sales = res.data;
});
}
}
Controller.$inject = ['$http', '$scope'];
ngModule.component('vnTicketDataStepTwo', {
template: require('./step-two.html'),
controller: Controller,
bindings: {
ticket: '<'
}
});

View File

@ -1,4 +1,5 @@
module.exports = function(Self) {
require('../methods/sale/filter')(Self);
require('../methods/sale/priceGap')(Self);
require('../methods/sale/saleComponentFilter.js')(Self);
require('../methods/sale/price-gap')(Self);
};

View File

@ -1,38 +1,38 @@
{
"name": "Sale",
"base": "VnModel",
"options": {
"mysql": {
"table": "sale"
}
"name": "Sale",
"base": "VnModel",
"options": {
"mysql": {
"table": "sale"
}
},
"properties": {
"id": {
"id": true,
"type": "Number",
"description": "Identifier"
},
"properties": {
"id": {
"id": true,
"type": "Number",
"description": "Identifier"
},
"concept": {
"type": "String"
},
"quantity": {
"type": "Number"
},
"price": {
"type": "Number"
},
"discount": {
"type": "Number"
},
"reserved": {
"type": "Number"
},
"isPicked": {
"type": "Number"
},
"created": {
"type": "date"
}
"concept": {
"type": "String"
},
"quantity": {
"type": "Number"
},
"price": {
"type": "Number"
},
"discount": {
"type": "Number"
},
"reserved": {
"type": "Number"
},
"isPicked": {
"type": "Number"
},
"created": {
"type": "date"
}
},
"relations": {
"item": {
@ -51,6 +51,11 @@
"type": "hasOne",
"model": "SaleChecked",
"foreignKey": "saleFk"
}
},
"components": {
"type": "hasMany",
"model": "SaleComponent",
"foreignKey": "saleFk"
}
}
}

View File

@ -1,39 +0,0 @@
module.exports = Self => {
Self.installMethod('filter', filterParams);
function filterParams(params) {
return {
where: {
ticketFk: params.ticketFk
},
skip: (params.page - 1) * params.size,
limit: params.size,
order: params.order || 'concept ASC',
include: [{
relation: "item",
scope: {
include: {
relation: "itemTag",
scope: {
fields: ["tagFk", "value"],
include: {
relation: "tag",
scope: {
fields: ["name"]
}
},
limit: 6
}
},
fields: ["itemFk", "name"]
}
},
{
relation: "isChecked",
scope: {
fields: ["isChecked"]
}
}]
};
}
};

View File

@ -1,4 +0,0 @@
module.exports = function(Self) {
require('../methods/sale/filter.js')(Self);
require('../methods/sale/saleComponentFilter.js')(Self);
};

View File

@ -1,61 +0,0 @@
{
"name": "Sale",
"base": "VnModel",
"options": {
"mysql": {
"table": "sale"
}
},
"properties": {
"id": {
"id": true,
"type": "Number",
"description": "Identifier"
},
"concept": {
"type": "String"
},
"quantity": {
"type": "Number"
},
"price": {
"type": "Number"
},
"discount": {
"type": "Number"
},
"reserved": {
"type": "Number"
},
"isPicked": {
"type": "Number"
},
"created": {
"type": "date"
}
},
"relations": {
"item": {
"type": "belongsTo",
"model": "Item",
"foreignKey": "itemFk",
"required": true
},
"ticket": {
"type": "belongsTo",
"model": "Ticket",
"foreignKey": "ticketFk",
"required": true
},
"isChecked": {
"type": "hasOne",
"model": "SaleChecked",
"foreignKey": "saleFk"
},
"components": {
"type": "hasMany",
"model": "SaleComponent",
"foreignKey": "saleFk"
}
}
}

View File

@ -5,9 +5,6 @@
"ObservationType": {
"dataSource": "vn"
},
"Sale": {
"dataSource": "vn"
},
"TicketTracking": {
"dataSource": "vn"
},