Fix: Date filtering works properly in Tokenizer (requires moment.js)
Feature: Selecting Start/Finish Date in Recipe list updates the search string.
This commit is contained in:
parent
3c4f24989c
commit
3df2146ba2
@ -1,10 +1,10 @@
|
||||
<h2>Products</h2>
|
||||
<div layout-gt-sm="row">
|
||||
<md-input-container flex-gt-sm>
|
||||
<input type="text" ng-model="search"
|
||||
<div layout="row">
|
||||
<md-input-container flex="grow">
|
||||
<input type="text" ng-model="search" md-autofocus
|
||||
placeholder="Search: n:Name, a:Active, u:Units, t:Type and e:true for extended properties">
|
||||
</md-input-container>
|
||||
<md-input-container flex-gt-sm>
|
||||
<md-input-container flex>
|
||||
<md-button ng-href="/Product">Add <i
|
||||
class="glyphicon glyphicon-plus"></i></md-button>
|
||||
</md-input-container>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<form class="form-horizontal" role=form>
|
||||
<h1>Recipe Detail</h1>
|
||||
|
||||
<div layout-gt-sm="row">
|
||||
<div layout="row">
|
||||
<md-autocomplete md-selected-item="recipe.Product" md-search-text="searchProd"
|
||||
md-autofocus="recipe.RecipeItems.length === 0"
|
||||
md-items="item in products(searchProd, {p: false})" md-item-text="item.Name" on-return="add()"
|
||||
@ -30,11 +30,11 @@
|
||||
value="{{costPrice | currency}} / {{costPrice * 100 / recipe.SalePrice | number:2}}%"/>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<div layout-gt-sm="row">
|
||||
<div layout="row" class="md-inline-form">
|
||||
<md-datepicker md-placeholder="Valid From" ng-model="recipe.ValidFrom"></md-datepicker>
|
||||
<md-datepicker md-placeholder="Valid To" ng-model="recipe.ValidTo"></md-datepicker>
|
||||
</div>
|
||||
<div layout-gt-sm="row">
|
||||
<div layout="row">
|
||||
<md-autocomplete md-selected-item="product" md-search-text="searchIngredient" focus-on="foIngredient"
|
||||
md-autofocus="recipe.RecipeItems.length > 0" md-items="item in products(searchIngredient, {})"
|
||||
md-item-text="item.Name" on-return="add()"
|
||||
|
@ -1,35 +1,44 @@
|
||||
<form class="form-horizontal" role=form>
|
||||
<h2>Recipes
|
||||
<div class="form-group col-md-9 pull-right">
|
||||
<div class="col-md-10">
|
||||
<input type="text" class="form-control" placeholder="Search" ng-model="search">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<a href="/Recipe" class="btn btn-success btn-block">Add <i
|
||||
class="glyphicon glyphicon-plus"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</h2>
|
||||
<table class="table table-condensed table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Valid From</th>
|
||||
<th>Valid To</th >
|
||||
<th class="text-right">Cost Price</th>
|
||||
<th class="text-right">Sale Price</th>
|
||||
<th class="text-right">Costing</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-repeat="items in info">
|
||||
<tr ng-repeat="item in items.Prices">
|
||||
<td ng-if="$first" rowspan="{{items.Prices.length}}">{{items.Name}}</td>
|
||||
<td><a href="{{item.Url}}">{{item.ValidFrom}}</a></td>
|
||||
<td>{{item.ValidTo}}</td>
|
||||
<td class="text-right">{{item.CostPrice | currency}}</td>
|
||||
<td class="text-right">{{item.SalePrice | currency}}</td>
|
||||
<td class="text-right">{{item.Costing | percent}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</form>
|
||||
<h1>Recipes</h1>
|
||||
<div layout="row">
|
||||
<md-input-container flex="grow">
|
||||
<input type="text" ng-model="search" md-autofocus placeholder="Search: n:Name, s:Start Date, f:Finish Date">
|
||||
</md-input-container>
|
||||
<md-input-container flex>
|
||||
<md-button ng-href="/Recipe">Add <i
|
||||
class="glyphicon glyphicon-plus"></i></md-button>
|
||||
</md-input-container>
|
||||
</div>
|
||||
<div layout="row">
|
||||
<div layout="column" flex>
|
||||
<label class="md-caption">Start Date</label>
|
||||
<md-datepicker md-placeholder="Start Date" ng-model="startDate"
|
||||
ng-model-options="{ getterSetter: true }"></md-datepicker>
|
||||
</div>
|
||||
<div layout="column" flex>
|
||||
<label class="md-caption">Finish Date</label>
|
||||
<md-datepicker md-placeholder="Finish Date" ng-model="finishDate"
|
||||
ng-model-options="{ getterSetter: true }"></md-datepicker>
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-condensed table-bordered table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Valid From</th>
|
||||
<th>Valid To</th>
|
||||
<th class="text-right">Cost Price</th>
|
||||
<th class="text-right">Sale Price</th>
|
||||
<th class="text-right">Costing</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody ng-repeat="items in recipes">
|
||||
<tr ng-repeat="item in items.Prices">
|
||||
<td ng-if="$first" rowspan="{{items.Prices.length}}">{{items.Name}}</td>
|
||||
<td><a href="{{item.Url}}">{{item.ValidFrom}}</a></td>
|
||||
<td>{{item.ValidTo}}</td>
|
||||
<td class="text-right">{{item.CostPrice | currency}}</td>
|
||||
<td class="text-right">{{item.SalePrice | currency}}</td>
|
||||
<td class="text-right">{{item.Costing | percent}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
30
brewman/static/scripts/angular_service.js
vendored
30
brewman/static/scripts/angular_service.js
vendored
@ -270,33 +270,17 @@ overlordService.factory('Tokenizer', ['$filter', function ($filter) {
|
||||
}
|
||||
},
|
||||
'date': function (obj, text) {
|
||||
var reGoodDate = /^((0?[1-9]|[12][0-9]|3[01])[- /.](0?[1-9]|1[012]|Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[- /.](19|20)?[0-9]{2})*$/i,
|
||||
asDateObject = function (input) {
|
||||
if (angular.isDate(input)) {
|
||||
return input;
|
||||
}
|
||||
|
||||
},
|
||||
op = opLength(text),
|
||||
ob = op ? asDateObject(obj) : '',
|
||||
tex = op ? asDateObject(text.substr(op).trim()) : '';
|
||||
if (op) {
|
||||
return operators[text.substr(0, op)](ob, tex);
|
||||
} else {
|
||||
return obj.toString().indexOf(text) > -1;
|
||||
}
|
||||
|
||||
if (text.indexOf('!') === 0) {
|
||||
return obj.toLowerCase().indexOf(text.substr(1)) <= -1;
|
||||
} else {
|
||||
return obj.toLowerCase().indexOf(text) > -1;
|
||||
}
|
||||
var obDate = moment(obj, 'DD-MMM-YYYY'),
|
||||
op = operatorLength(text),
|
||||
operator = op ? text.substr(0, op) : '==',
|
||||
textDate = op ? moment(text.substr(op).trim(), 'DD-MMM-YYYY') : moment(text, 'DD-MMM-YYYY');
|
||||
return !obDate.isValid() || !textDate.isValid() || operators[operator](obDate.valueOf(), textDate.valueOf());
|
||||
},
|
||||
'boolean': function (obj, text) {
|
||||
return obj === isTrue(text);
|
||||
},
|
||||
'numeric': function (obj, text) {
|
||||
var op = opLength(text);
|
||||
var op = operatorLength(text);
|
||||
if (op) {
|
||||
return operators[text.substr(0, op)](obj, Number(text.substr(op).trim()));
|
||||
} else {
|
||||
@ -323,7 +307,7 @@ overlordService.factory('Tokenizer', ['$filter', function ($filter) {
|
||||
return {'Col': comparator.Col, 'Comparator': comparators[comparator.Comparator], 'Value': value};
|
||||
}
|
||||
|
||||
function opLength(operator) {
|
||||
function operatorLength(operator) {
|
||||
var i,
|
||||
ops = ['<=', '<', '>=', '>', '==', '!='];
|
||||
for (i = 0; i < ops.length; i++) {
|
||||
|
@ -417,7 +417,7 @@ var overlord = angular.module('overlord', ['overlord.directive', 'overlord.filte
|
||||
}])
|
||||
.config(['$mdDateLocaleProvider', function ($mdDateLocaleProvider) {
|
||||
$mdDateLocaleProvider.formatDate = function (date) {
|
||||
return moment(date).format('DD-MMM-YYYY');
|
||||
return !angular.isUndefined(date) ? moment(date).format('DD-MMM-YYYY') : "";
|
||||
};
|
||||
$mdDateLocaleProvider.parseDate = function (date) {
|
||||
return moment(date, 'DD-MMM-YYYY').toDate();
|
||||
|
@ -1,8 +1,80 @@
|
||||
'use strict';
|
||||
|
||||
var RecipeListController = ['$scope', 'recipes', function ($scope, recipes) {
|
||||
$scope.info = recipes;
|
||||
}];
|
||||
var RecipeListController = ['$scope', 'recipes', '$location', '$routeParams', 'Tokenizer', function ($scope, recipes, $location, $routeParams, Tokenizer) {
|
||||
$scope.info = recipes;
|
||||
$scope.search = $routeParams.q || '';
|
||||
|
||||
$scope.startDate = function (startDate) {
|
||||
if (arguments.length === 0) {
|
||||
return $scope._startDate;
|
||||
}
|
||||
$scope._startDate = startDate;
|
||||
var re = /(('[s]'|"[s]"|[s]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
||||
matches = $scope.search.match(re),
|
||||
st = startDate === null ? '' : 's:>=' + moment(startDate).format('DD-MMM-YYYY');
|
||||
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
|
||||
};
|
||||
|
||||
$scope.finishDate = function (finishDate) {
|
||||
if (arguments.length === 0) {
|
||||
return $scope._finishDate;
|
||||
}
|
||||
$scope._finishDate = finishDate;
|
||||
var re = /(('[f]'|"[f]"|[f]+)\s*:\s*('[^']+'|"[^"]+"|[^\s]+))/gi,
|
||||
matches = $scope.search.match(re),
|
||||
st = finishDate === null ? '' : 'f:<=' + moment(finishDate).format('DD-MMM-YYYY');
|
||||
$scope.search = (matches === null) ? $scope.search.trim() + ' ' + st : $scope.search.replace(re, st);
|
||||
};
|
||||
|
||||
$scope.$watch('search', function (value) {
|
||||
$scope.filterRecipes(value);
|
||||
}, true);
|
||||
|
||||
$scope.$on('$destroy', function () {
|
||||
Tokenizer.doFilter.cache = {};
|
||||
});
|
||||
|
||||
$scope.searchInfo = {
|
||||
comparator: {
|
||||
'n': {'Col': 'Name', 'Comparator': 'text'},
|
||||
's': {'Col': 'ValidFrom', 'Comparator': 'date'},
|
||||
'f': {'Col': 'ValidTo', 'Comparator': 'date'}
|
||||
},
|
||||
def: 'n',
|
||||
sorter: {
|
||||
'n': 'Name',
|
||||
's': 'ValidFrom',
|
||||
'f': 'ValidTo',
|
||||
'c': 'Costing',
|
||||
'cp': 'CostPrice',
|
||||
'sp': 'SalePrice'
|
||||
},
|
||||
flags: {}
|
||||
};
|
||||
|
||||
$scope.filterRecipes = _.debounce(function (q) {
|
||||
if (q !== $scope._search) {
|
||||
$scope._search = q;
|
||||
if (angular.isUndefined(q) || q === '') {
|
||||
$location.path('/Recipes').search('q', null).replace();
|
||||
} else {
|
||||
$location.path('/Recipes').search({'q': q}).replace();
|
||||
}
|
||||
$scope.$apply(function () {
|
||||
var matches = Tokenizer.parseFilterString(q, $scope.searchInfo);
|
||||
$scope.recipes = _.map($scope.info, function (prod) {
|
||||
var item = angular.copy(prod);
|
||||
item.Prices = Tokenizer.doFilter(prod.ProductID + ' ' + q, prod.Prices, matches);
|
||||
return item;
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
}, 350);
|
||||
|
||||
}]
|
||||
;
|
||||
|
||||
RecipeListController.resolve = {
|
||||
recipes: ['Recipe', function (Recipe) {
|
||||
|
Loading…
Reference in New Issue
Block a user