1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-09 20:52:56 +01:00

Use typeahead for product key on invoice page

This commit is contained in:
Hillel Coren 2016-02-29 11:40:52 +02:00
parent 6a9b2130c5
commit eef80d94d2
7 changed files with 116 additions and 98 deletions

View File

@ -30967,6 +30967,29 @@ function prettyJson(json) {
return '<span class="' + cls + '">' + match + '</span>';
});
}
function searchData(data, key, fuzzy) {
return function findMatches(q, cb) {
var matches, substringRegex;
if (fuzzy) {
var options = {
keys: [key],
}
var fuse = new Fuse(data, options);
matches = fuse.search(q);
} else {
matches = [];
substrRegex = new RegExp(q, 'i');
$.each(data, function(i, obj) {
if (substrRegex.test(obj[key])) {
matches.push(obj);
}
});
}
cb(matches);
}
};
var NINJA = NINJA || {};
NINJA.TEMPLATES = {

View File

@ -2060,7 +2060,7 @@ See http://bgrins.github.io/spectrum/themes/ for instructions.
/*root typeahead class*/
.twitter-typeahead {
display: inherit !important;
/*display: inherit !important;*/
width: 100%;
}

View File

@ -4,7 +4,7 @@
/*root typeahead class*/
.twitter-typeahead {
display: inherit !important;
/*display: inherit !important;*/
width: 100%;
}

View File

@ -1074,4 +1074,26 @@ function prettyJson(json) {
match = snakeToCamel(match);
return '<span class="' + cls + '">' + match + '</span>';
});
}
}
function searchData(data, key, fuzzy) {
return function findMatches(q, cb) {
var matches, substringRegex;
if (fuzzy) {
var options = {
keys: [key],
}
var fuse = new Fuse(data, options);
matches = fuse.search(q);
} else {
matches = [];
substrRegex = new RegExp(q, 'i');
$.each(data, function(i, obj) {
if (substrRegex.test(obj[key])) {
matches.push(obj);
}
});
}
cb(matches);
}
};

View File

@ -259,13 +259,11 @@
}
function showSearch() {
console.log('showSearch..');
//$('#search').typeahead('setQuery', '');
$('#search').typeahead('val', '');
$('#navbar-options').hide();
$('#search-form').show();
if (window.hasOwnProperty('loadedSearchData')) {
console.log('has data');
$('#search').focus();
} else {
trackEvent('/activity', '/search');
@ -275,68 +273,24 @@
$('#search').typeahead({
hint: true,
highlight: true,
},
{
}
@foreach (['clients', 'contacts', 'invoices', 'quotes', 'navigation'] as $type)
,{
name: 'data',
display: 'value',
source: searchData(data['clients']),
source: searchData(data['{{ $type }}'], 'value', true),
templates: {
header: '&nbsp;<b>{{ trans('texts.clients') }}</b>'
header: '&nbsp;<span style="font-weight:600;font-size:16px">{{ trans("texts.{$type}") }}</span>'
}
},
{
name: 'data',
display: 'value',
source: searchData(data['contacts']),
templates: {
header: '&nbsp;<b>{{ trans('texts.contacts') }}</b>'
}
},
{
name: 'data',
display: 'value',
source: searchData(data['invoices']),
templates: {
header: '&nbsp;<b>{{ trans('texts.contacts') }}</b>'
}
},
{
name: 'data',
display: 'value',
source: searchData(data['quotes']),
templates: {
header: '&nbsp;<b>{{ trans('texts.quotes') }}</b>'
}
},
{
name: 'data',
display: 'value',
source: searchData(data['navigation']),
templates: {
header: '&nbsp;<b>{{ trans('texts.navigation') }}</b>'
}
}).on('typeahead:selected', function(element, datum, name) {
}
@endforeach
).on('typeahead:selected', function(element, datum, name) {
window.location = datum.url;
}).focus();
//.typeahead('setQuery', $('#search').val());
});
}
}
function searchData(data) {
return function findMatches(q, cb) {
var options = {
keys: ['value'],
}
var fuse = new Fuse(data, options);
var matches = fuse.search(q);
cb(matches);
}
};
function hideSearch() {
$('#search-form').hide();
$('#navbar-options').show();

View File

@ -209,11 +209,7 @@
$parent.invoice_items().length > 1" class="fa fa-sort"></i>
</td>
<td>
{!! Former::text('product_key')->useDatalist($products->toArray(), 'product_key')
->data_bind("value: product_key, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + \$index() + '][product_key]'}")
->addClass('datalist')
->raw()
!!}
<input type="text" data-bind="typeahead: product_key, items: $root.products, key: 'product_key', valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][product_key]'}" class="form-control invoice-item handled"/>
</td>
<td>
<textarea data-bind="value: wrapped_notes, valueUpdate: 'afterkeydown', attr: {name: 'invoice_items[' + $index() + '][notes]'}"
@ -915,6 +911,9 @@
function applyComboboxListeners() {
var selectorStr = '.invoice-table input, .invoice-table textarea';
$(selectorStr).off('change').on('change', function(event) {
if ($(event.target).hasClass('handled')) {
return;
}
onItemChange();
refreshPDF(true);
});
@ -930,38 +929,6 @@
$(this).height($(this).height()+1);
};
});
@if (Auth::user()->account->fill_products)
$('.datalist').off('input').on('input', function() {
var key = $(this).val();
for (var i=0; i<products.length; i++) {
var product = products[i];
if (product.product_key == key) {
var model = ko.dataFor(this);
if (model.expense_public_id()) {
return;
}
if (product.notes) {
model.notes(product.notes);
}
if (product.cost) {
model.cost(accounting.toFixed(product.cost, 2));
}
if (!model.qty()) {
model.qty(1);
}
@if ($account->invoice_item_taxes)
if (product.default_tax_rate) {
model.tax(self.model.getTaxRateById(product.default_tax_rate.public_id));
}
@endif
model.product_key(key);
onItemChange();
break;
}
}
});
@endif
}
function createInvoiceModel() {

View File

@ -9,7 +9,7 @@ function ViewModel(data) {
self.expense_currency_id = ko.observable();
self.tax_rates = ko.observableArray();
self.tax_rates.push(new TaxRateModel()); // add blank row
self.products = {!! $products !!};
self.loadClient = function(client) {
ko.mapping.fromJS(client, model.invoice().client().mapping, model.invoice().client);
@ -807,4 +807,56 @@ function ItemModel(data) {
this.onSelect = function() {}
}
</script>
/* Custom binding for product key typeahead */
ko.bindingHandlers.typeahead = {
init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var $element = $(element);
var allBindings = allBindingsAccessor();
$element.typeahead({
highlight: true,
minLength: 0,
},
{
name: 'data',
display: allBindings.key,
source: searchData(allBindings.items, allBindings.key)
}).on('typeahead:select', function(element, datum, name) {
@if (Auth::user()->account->fill_products)
var model = ko.dataFor(this);
if (model.expense_public_id()) {
return;
}
if (datum.notes) {
model.notes(datum.notes);
}
if (datum.cost) {
model.cost(accounting.toFixed(datum.cost, 2));
}
if (!model.qty()) {
model.qty(1);
}
@if ($account->invoice_item_taxes)
if (datum.default_tax_rate) {
model.tax(self.model.getTaxRateById(datum.default_tax_rate.public_id));
}
@endif
@endif
onItemChange();
}).on('typeahead:change', function(element, datum, name) {
var value = valueAccessor();
value(datum);
onItemChange();
refreshPDF(true);
});
},
update: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
if (value) {
$(element).typeahead('val', value);
}
}
};
</script>