mirror of
https://github.com/grocy/grocy.git
synced 2026-04-05 12:26:15 +02:00
Bootstrap Dropdown with detached dropdown definition
This commit is contained in:
parent
a8153a2cf2
commit
5be9faed43
|
|
@ -57,6 +57,7 @@ var eslint_config = {
|
|||
"moment",
|
||||
"toastr",
|
||||
"bootbox",
|
||||
"Popper",
|
||||
],
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
|
|
|
|||
239
js/helpers/dropdown.js
Normal file
239
js/helpers/dropdown.js
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
import Popper from "popper.js";
|
||||
|
||||
/* this is basically a 1 on 1 port of Bootstraps'
|
||||
DropdownMenu plug-in, but has its Elements detached.
|
||||
And pobably triggers an event or two less.
|
||||
|
||||
HTML-wise it uses standard bootstrap 4 dropdown syntax,
|
||||
however the button is out of the <div class="dropdown">
|
||||
wrapper, and needs to reference the dropdown menu
|
||||
with a data-detached-element="#someSelector" attribute.
|
||||
|
||||
Also this class is way less generic than Bootstraps,
|
||||
but that's okay.
|
||||
|
||||
Parts of this code are taken from https://github.com/twbs/bootstrap/blob/v4-dev/js/src/dropdown.js
|
||||
which is available under the MIT License.
|
||||
*/
|
||||
|
||||
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
|
||||
const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key
|
||||
const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key
|
||||
const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
|
||||
const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
|
||||
const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)
|
||||
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)
|
||||
const SELECTOR_VISIBLE_ITEMS = '.dropdown-item:not(.disabled):not(:disabled)'
|
||||
|
||||
class DetachedDropdown
|
||||
{
|
||||
constructor(target, menuElement = null, scope = null)
|
||||
{
|
||||
this.scopeSelector = scope;
|
||||
if (scope != null)
|
||||
{
|
||||
this.scope = $(scope);
|
||||
var jScope = this.scope;
|
||||
this.$scope = (selector) => jScope.find(selector);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.$scope = $;
|
||||
this.scope = $(document);
|
||||
}
|
||||
|
||||
this.$target = this.$scope(target);
|
||||
this.target = this.$target[0];
|
||||
this.menu = menuElement != null ? this.$scope(menuElement) : this.$scope(this.$target.data('target'));
|
||||
this._popper = null;
|
||||
var self = this;
|
||||
|
||||
$(document).on('keydown', (event) => self.keydownHandler(event));
|
||||
this.menu.on("click", "form", e =>
|
||||
{
|
||||
e.stopPropagation()
|
||||
})
|
||||
|
||||
this.scope.on("click keyup", (event) => self.clear(event));
|
||||
}
|
||||
|
||||
toggle()
|
||||
{
|
||||
if (this.menu.parent().hasClass('show'))
|
||||
this.hide();
|
||||
|
||||
else
|
||||
this.show();
|
||||
}
|
||||
|
||||
show()
|
||||
{
|
||||
// show always re-shows.
|
||||
this.hide()
|
||||
|
||||
|
||||
this._popper = new Popper(this.target, this.menu, this._getPopperConfig())
|
||||
|
||||
// If this is a touch-enabled device we add extra
|
||||
// empty mouseover listeners to the body's immediate children;
|
||||
// only needed because of broken event delegation on iOS
|
||||
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
|
||||
if ('ontouchstart' in document.documentElement)
|
||||
{
|
||||
$(document.body).children().on('mouseover', null, $.noop)
|
||||
}
|
||||
|
||||
this.menu.trigger('focus');
|
||||
this.menu.attr('aria-expanded', true)
|
||||
|
||||
this.menu.toggleClass('show')
|
||||
this.menu.parent().toggleClass('show')
|
||||
}
|
||||
|
||||
hide()
|
||||
{
|
||||
if (this.isDisabled() || !this.menu.parent().hasClass('show'))
|
||||
return;
|
||||
|
||||
// If this is a touch-enabled device we remove the extra
|
||||
// empty mouseover listeners we added for iOS support
|
||||
if ('ontouchstart' in document.documentElement)
|
||||
{
|
||||
$(document.body).children().off('mouseover', null, $.noop)
|
||||
}
|
||||
|
||||
if (this._popper)
|
||||
{
|
||||
this._popper.destroy()
|
||||
}
|
||||
|
||||
|
||||
this.menu.removeClass('show');
|
||||
this.menu.parent().removeClass('show');
|
||||
this.menu.attr('aria-expanded', false)
|
||||
}
|
||||
|
||||
isDisabled()
|
||||
{
|
||||
return this.target.disabled || this.$target.hasClass("disabled");
|
||||
}
|
||||
|
||||
_getPopperConfig()
|
||||
{
|
||||
return {
|
||||
placement: 'right',
|
||||
modifiers: {
|
||||
offset: '50px',
|
||||
flip: {
|
||||
enabled: true,
|
||||
},
|
||||
preventOverflow: {
|
||||
boundariesElement: 'viewport'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keydownHandler(event)
|
||||
{
|
||||
if (!this.isActive() && event.target.id != this.target.id)
|
||||
return;
|
||||
// If not input/textarea:
|
||||
// - And not a key in REGEXP_KEYDOWN => not a dropdown command
|
||||
// If input/textarea:
|
||||
// - If space key => not a dropdown command
|
||||
// - If key is other than escape
|
||||
// - If key is not up or down => not a dropdown command
|
||||
// - If trigger inside the menu => not a dropdown command
|
||||
if (/input|textarea/i.test(event.target.tagName) ?
|
||||
event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&
|
||||
(event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||
|
||||
this.menu.length) : !REGEXP_KEYDOWN.test(event.which))
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
if (this.isDisabled())
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.isActive() && event.which === ESCAPE_KEYCODE)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
|
||||
if (!this.isActive() || (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE))
|
||||
{
|
||||
if (event.which === ESCAPE_KEYCODE)
|
||||
{
|
||||
this.menu.trigger('focus')
|
||||
}
|
||||
|
||||
this.$target.trigger('click')
|
||||
return
|
||||
}
|
||||
|
||||
const items = [].slice.call(this.menu[0].querySelectorAll(SELECTOR_VISIBLE_ITEMS))
|
||||
.filter(item => $(item).is(':visible'))
|
||||
|
||||
if (items.length === 0)
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
let index = items.indexOf(event.target)
|
||||
|
||||
if (event.which === ARROW_UP_KEYCODE && index > 0)
|
||||
{ // Up
|
||||
index--
|
||||
}
|
||||
|
||||
if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1)
|
||||
{ // Down
|
||||
index++
|
||||
}
|
||||
|
||||
if (index < 0)
|
||||
{
|
||||
index = 0
|
||||
}
|
||||
|
||||
items[index].focus()
|
||||
|
||||
}
|
||||
|
||||
isActive()
|
||||
{
|
||||
return this.menu.parent().hasClass('show');
|
||||
}
|
||||
|
||||
clear(event)
|
||||
{
|
||||
if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH ||
|
||||
(event.type === 'keyup' && event.which !== TAB_KEYCODE)))
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
if (!this.menu.parent().hasClass('show'))
|
||||
{
|
||||
return
|
||||
}
|
||||
let parent = this.menu.parent()[0];
|
||||
|
||||
if (event && (event.type === 'click' &&
|
||||
/input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) &&
|
||||
$.contains(parent, event.target))
|
||||
{
|
||||
return
|
||||
}
|
||||
|
||||
this.hide();
|
||||
|
||||
}
|
||||
}
|
||||
export { DetachedDropdown }
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { DetachedDropdown } from './dropdown';
|
||||
|
||||
class GrocyFrontendHelpers
|
||||
{
|
||||
|
|
@ -5,18 +6,112 @@ class GrocyFrontendHelpers
|
|||
{
|
||||
this.Grocy = Grocy;
|
||||
this.Api = Api;
|
||||
this.scopeSelector = scope;
|
||||
if (scope != null)
|
||||
{
|
||||
this.scope = $(scope);
|
||||
var jScope = this.scope;
|
||||
this.$scope = (selector) => jScope.find(selector);
|
||||
this.scopeSelector = scope;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.$scope = $;
|
||||
this.scope = $(document);
|
||||
}
|
||||
|
||||
this.dropdowns = {}
|
||||
|
||||
this.InitDropdowns();
|
||||
}
|
||||
|
||||
_ApplyTemplate(data, template)
|
||||
{
|
||||
for (let key in data)
|
||||
{
|
||||
// transforms data-product-id to PRODUCT_ID
|
||||
let param = key.replace('data-', '').toUpperCase().replaceAll('-', '_');
|
||||
template = template.replaceAll(param, data[key]);
|
||||
}
|
||||
|
||||
return template.replace('RETURNTO', '?returnto=' + encodeURIComponent(window.location.pathname));
|
||||
}
|
||||
|
||||
InitDropdowns()
|
||||
{
|
||||
var self = this;
|
||||
this.$scope('[data-toggle="dropdown-detached"]').on('click', function(event)
|
||||
{
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
var button = this;
|
||||
var selector = button.getAttribute('data-target');
|
||||
var $dropdown = self.$scope(selector);
|
||||
|
||||
let dropper = self.dropdowns[button.id];
|
||||
if (dropper !== undefined)
|
||||
{
|
||||
if (dropper.isActive())
|
||||
{
|
||||
dropper.hide();
|
||||
if (dropper.target.id == button.id)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var elements = $dropdown.find('*');
|
||||
var source_data = {};
|
||||
|
||||
for (let i = button.attributes.length - 1; i >= 0; i--)
|
||||
{
|
||||
let attr = button.attributes[i];
|
||||
if (attr.name.startsWith("data-"))
|
||||
{
|
||||
source_data[attr.name] = attr.value;
|
||||
}
|
||||
}
|
||||
|
||||
for (let elem of elements)
|
||||
{
|
||||
for (let i = elem.attributes.length - 1; i >= 0; i--)
|
||||
{
|
||||
// copy over data-* attributes
|
||||
let attr = elem.attributes[i];
|
||||
if (source_data[attr.name] !== undefined)
|
||||
{
|
||||
elem.setAttribute(attr.name, source_data[attr.name]);
|
||||
}
|
||||
}
|
||||
|
||||
if (elem.hasAttribute('data-href'))
|
||||
{
|
||||
elem.setAttribute("href", self._ApplyTemplate(source_data, elem.getAttribute('data-href')))
|
||||
}
|
||||
|
||||
if (elem.hasAttribute("data-compute"))
|
||||
{
|
||||
let tArgs = JSON.parse(self._ApplyTemplate(source_data, elem.getAttribute("data-compute")))
|
||||
elem.innerText = self.Grocy.translate(...tArgs);
|
||||
}
|
||||
|
||||
if (elem.hasAttribute("data-disable") &&
|
||||
source_data["data-" + elem.getAttribute("data-disable")] !== undefined &&
|
||||
source_data["data-" + elem.getAttribute("data-disable")] === "1")
|
||||
{
|
||||
elem.classList.add("disabled");
|
||||
}
|
||||
else
|
||||
{
|
||||
elem.classList.remove("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
if (dropper === undefined)
|
||||
{
|
||||
dropper = new DetachedDropdown(button, null, this.scopeSelector);
|
||||
self.dropdowns[button.id] = dropper;
|
||||
}
|
||||
dropper.toggle();
|
||||
});
|
||||
}
|
||||
|
||||
Delay(callable, delayMilliseconds)
|
||||
|
|
|
|||
|
|
@ -43,4 +43,8 @@
|
|||
/* Always show modals over everything else */
|
||||
.modal {
|
||||
z-index: 99998;
|
||||
}
|
||||
|
||||
.detached-dropdown-menu {
|
||||
z-index: 99999;
|
||||
}
|
||||
82
views/components/stockentrydropdowncommon.blade.php
Normal file
82
views/components/stockentrydropdowncommon.blade.php
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
<a class="dropdown-item show-as-dialog-link permission-SHOPPINGLIST_ITEMS_ADD"
|
||||
type="button"
|
||||
data-href="{{ $U('/shoppinglistitem/new?embedded&updateexistingproduct&product=PRODUCT_ID') }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-shopping-cart"></i></span> <span class="dropdown-item-text">{{ $__t('Add to shopping list') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_PURCHASE"
|
||||
type="button"
|
||||
data-href="{{ $U('/purchase?embedded&product=PRODUCT_ID' ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-cart-plus"></i></span> <span class="dropdown-item-text">{{ $__t('Purchase') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_CONSUME"
|
||||
type="button"
|
||||
data-disable="consume"
|
||||
data-href="{{ $U('/consume?embedded&product=PRODUCT_ID&locationId=LOCATON_ID&stockId=STOCK_ID') }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-utensils"></i></span> <span class="dropdown-item-text">{{ $__t('Consume') }}</span>
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_TRANSFER"
|
||||
data-disable="transfer"
|
||||
type="button"
|
||||
data-href="{{ $U('/transfer?embedded&product=PRODUCT_ID&locationId=LOCATON_ID&stockId=STOCK_ID') }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-exchange-alt"></i></span> <span class="dropdown-item-text">{{ $__t('Transfer') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_INVENTORY"
|
||||
type="button"
|
||||
data-href="{{ $U('/inventory?embedded&product=PRODUCT_ID') }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-list"></i></span> <span class="dropdown-item-text">{{ $__t('Inventory') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item product-consume-button product-consume-button-spoiled permission-STOCK_CONSUME"
|
||||
type="button"
|
||||
href="#"
|
||||
data-product-id=""
|
||||
data-product-name=""
|
||||
data-product-qu-name=""
|
||||
data-consume-amount="1"
|
||||
data-stock-id=""
|
||||
data-stockrow-id=""
|
||||
data-location-id=""
|
||||
data-disable="consume">
|
||||
<span class="dropdown-item-text"
|
||||
data-compute='["Consume %1$s of %2$s as spoiled", "1 PRODUCT_QU_NAME" , "PRODUCT_NAME"]'></span>
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_RECIPES)
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
data-href="{{ $U('/recipes?search=PRODUCT_NAME') }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Search for recipes containing this product') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item product-name-cell"
|
||||
data-product-id="xxx"
|
||||
type="button"
|
||||
data-href="#">
|
||||
<span class="dropdown-item-text">{{ $__t('Product overview') }}</span>
|
||||
</a>
|
||||
@if(!isset($skipStockEntries))
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
data-href="{{ $U('/stockentries?embedded&product=PRODUCT_ID') }}"
|
||||
data-product-id="xxx">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock entries') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
data-href="{{ $U('/stockjournal?embedded&product=PRODUCT_ID') }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock journal') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
data-href="{{ $U('/stockjournal/summary?embedded&product_id=PRODUCT_ID') }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock journal summary') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item permission-MASTER_DATA_EDIT"
|
||||
type="button"
|
||||
data-href="{{ $U('/product/PRODUCT_ID') }}RETURNTO">
|
||||
<span class="dropdown-item-text">{{ $__t('Edit product') }}</span>
|
||||
</a>
|
||||
|
|
@ -45,8 +45,37 @@ $collapsed_flex = $embedded ? '' : 'd-md-flex';
|
|||
</div>
|
||||
</div>
|
||||
|
||||
@php
|
||||
$dt_uniq = uniqid();
|
||||
@endphp
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="dropdown">
|
||||
<div class="table-inline-menu dropdown-menu detached-dropdown-menu dropdown-menu-right" id="datatable-dropdown{{ $dt_uniq }}">
|
||||
@include('components.stockentrydropdowncommon', [ 'skipStockEntries' => true])
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item stockentry-grocycode-link"
|
||||
type="button"
|
||||
data-href="{{ $U('/stockentry/STOCK_ENTRY_ID/grocycode?download=true') }}">
|
||||
{{ $__t('Download stock entry grocycode') }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
<a class="dropdown-item stockentry-grocycode-stockentry-label-print"
|
||||
data-stock-id=""
|
||||
type="button"
|
||||
href="#">
|
||||
{{ $__t('Print stock entry grocycode on label printer') }}
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item stockentry-label-link"
|
||||
type="button"
|
||||
target="_blank"
|
||||
data-href="{{ $U('/stockentry/STOCK_ENTRY_ID/label') }}">
|
||||
{{ $__t('Open stock entry print label in new window') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<table id="stockentries-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
|
|
@ -119,106 +148,20 @@ $collapsed_flex = $embedded ? '' : 'd-md-flex';
|
|||
title="{{ $__t('Edit stock entry') }}">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
data-toggle="dropdown"
|
||||
data-boundary="viewport">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="dropdown-menu">
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/shoppinglistitem/new?embedded&updateexistingproduct&product=' . $stockEntry->product_id ) }}">
|
||||
<i class="fas fa-shopping-cart"></i> {{ $__t('Add to shopping list') }}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/purchase?embedded&product=' . $stockEntry->product_id ) }}">
|
||||
<i class="fas fa-cart-plus"></i> {{ $__t('Purchase') }}
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/consume?embedded&product=' . $stockEntry->product_id . '&locationId=' . $stockEntry->location_id . '&stockId=' . $stockEntry->stock_id) }}">
|
||||
<i class="fas fa-utensils"></i> {{ $__t('Consume') }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/transfer?embedded&product=' . $stockEntry->product_id . '&locationId=' . $stockEntry->location_id . '&stockId=' . $stockEntry->stock_id) }}">
|
||||
<i class="fas fa-exchange-alt"></i> {{ $__t('Transfer') }}
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/inventory?embedded&product=' . $stockEntry->product_id ) }}">
|
||||
<i class="fas fa-list"></i> {{ $__t('Inventory') }}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item stock-consume-button stock-consume-button-spoiled @if($stockEntry->amount < 1) disabled @endif"
|
||||
type="button"
|
||||
href="#"
|
||||
data-product-id="{{ $stockEntry->product_id }}"
|
||||
data-product-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name }}"
|
||||
data-product-qu-name="{{ FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name }}"
|
||||
data-stock-id="{{ $stockEntry->stock_id }}"
|
||||
data-stockrow-id="{{ $stockEntry->id }}"
|
||||
data-location-id="{{ $stockEntry->location_id }}"
|
||||
data-consume-amount="1">
|
||||
{{ $__t('Consume this stock entry as spoiled', '1 ' . FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name) }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_RECIPES)
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
href="{{ $U('/recipes?search=') }}{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name }}">
|
||||
{{ $__t('Search for recipes containing this product') }}
|
||||
</a>
|
||||
@endif
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item product-name-cell"
|
||||
data-product-id="{{ $stockEntry->product_id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
{{ $__t('Product overview') }}
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/stockjournal?embedded&product=') }}{{ $stockEntry->product_id }}">
|
||||
{{ $__t('Stock journal') }}
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/stockjournal/summary?embedded&product=') }}{{ $stockEntry->product_id }}">
|
||||
{{ $__t('Stock journal summary') }}
|
||||
</a>
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
href="{{ $U('/product/') }}{{ $stockEntry->product_id . '?returnto=/stockentries' }}">
|
||||
{{ $__t('Edit product') }}
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item stockentry-grocycode-link"
|
||||
type="button"
|
||||
href="{{ $U('/stockentry/' . $stockEntry->id . '/grocycode?download=true') }}">
|
||||
{{ $__t('Download stock entry grocycode') }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
<a class="dropdown-item stockentry-grocycode-stockentry-label-print"
|
||||
data-stock-id="{{ $stockEntry->id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
{{ $__t('Print stock entry grocycode on label printer') }}
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item stockentry-label-link"
|
||||
type="button"
|
||||
target="_blank"
|
||||
href="{{ $U('/stockentry/' . $stockEntry->id . '/label') }}">
|
||||
{{ $__t('Open stock entry print label in new window') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
id="detached-dropdown-{!! uniqid() !!}"
|
||||
data-toggle="dropdown-detached"
|
||||
data-target="#datatable-dropdown{{ $dt_uniq }}"
|
||||
data-product-id="{{ $stockEntry->product_id }}"
|
||||
data-product-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->name }}"
|
||||
data-product-qu-name="{{ $__n($stockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $stockEntry->product_id)->qu_id_stock)->name_plural) }}"
|
||||
data-transfer="{{ ($currentStockEntry->amount < 1 ? 1 : 0) }}"
|
||||
data-consume="{{ ($currentStockEntry->amount < 1 ? 1 : 0) }}"
|
||||
data-location-id="{{ $stockEntry->location_id }}"
|
||||
data-stock-id="{{ $stockEntry->stock_id }}">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td class="d-none"
|
||||
data-product-id="{{ $stockEntry->product_id }}">
|
||||
|
|
|
|||
|
|
@ -4,25 +4,30 @@
|
|||
@section('activeNav', 'stockjournal')
|
||||
@section('viewJsName', 'stockjournal')
|
||||
|
||||
@php
|
||||
$collapsed_none = $embedded ? '' : 'd-md-none';
|
||||
$collapsed_flex = $embedded ? '' : 'd-md-flex';
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
<div class="title-related-links">
|
||||
<h2 class="title">@yield('title')</h2>
|
||||
<div class="float-right">
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
<button class="btn btn-outline-dark {{ $collapsed_none }} mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#table-filter-row">
|
||||
data-target="#stockjournal-table-filter-row">
|
||||
<i class="fas fa-filter"></i>
|
||||
</button>
|
||||
<button class="btn btn-outline-dark d-md-none mt-2 order-1 order-md-3"
|
||||
<button class="btn btn-outline-dark {{ $collapsed_none }} mt-2 order-1 order-md-3"
|
||||
type="button"
|
||||
data-toggle="collapse"
|
||||
data-target="#related-links">
|
||||
data-target="#stockjournal-related-links">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</div>
|
||||
<div class="related-links collapse d-md-flex order-2 width-xs-sm-100"
|
||||
id="related-links">
|
||||
<div class="related-links collapse {{ $collapsed_flex }} order-2 width-xs-sm-100"
|
||||
id="stockjournal-related-links">
|
||||
<a class="btn btn-outline-dark responsive-button m-1 mt-md-0 mb-md-0 float-right"
|
||||
href="{{ $U('/stockjournal/summary') }}">
|
||||
{{ $__t('Journal summary') }}
|
||||
|
|
@ -32,8 +37,8 @@
|
|||
|
||||
<hr class="my-2">
|
||||
|
||||
<div class="row collapse d-md-flex"
|
||||
id="table-filter-row">
|
||||
<div class="row collapse {{ $collapsed_flex }}"
|
||||
id="stockjournal-table-filter-row">
|
||||
<div class="col-12 col-md-6 col-xl-2">
|
||||
<div class="input-group">
|
||||
<div class="input-group-prepend">
|
||||
|
|
|
|||
|
|
@ -136,6 +136,27 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="dropdown">
|
||||
<div class="table-inline-menu dropdown-menu detached-dropdown-menu dropdown-menu-right" id="datatable-dropdown">
|
||||
@include('components.stockentrydropdowncommon')
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item stockentry-grocycode-link"
|
||||
type="button"
|
||||
data-href="{{ $U('/product/PRODUCT_ID/grocycode?download=true') }}">
|
||||
{{ $__t('Download product grocycode') }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
<a class="dropdown-item stockentry-grocycode-product-label-print"
|
||||
data-product-id="xxx"
|
||||
type="button"
|
||||
href="#">
|
||||
{{ $__t('Print product grocycode on label printer') }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<table id="stock-overview-table"
|
||||
class="table table-sm table-striped nowrap w-100">
|
||||
<thead>
|
||||
|
|
@ -209,102 +230,19 @@
|
|||
<i class="fas fa-box-open"></i> <span class="locale-number locale-number-quantity-amount">{{ $currentStockEntry->quick_consume_amount }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
data-toggle="dropdown">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
<div class="table-inline-menu dropdown-menu dropdown-menu-right">
|
||||
<a class="dropdown-item show-as-dialog-link permission-SHOPPINGLIST_ITEMS_ADD"
|
||||
type="button"
|
||||
href="{{ $U('/shoppinglistitem/new?embedded&updateexistingproduct&product=' . $currentStockEntry->product_id ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-shopping-cart"></i></span> <span class="dropdown-item-text">{{ $__t('Add to shopping list') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_PURCHASE"
|
||||
type="button"
|
||||
href="{{ $U('/purchase?embedded&product=' . $currentStockEntry->product_id ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-cart-plus"></i></span> <span class="dropdown-item-text">{{ $__t('Purchase') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_CONSUME"
|
||||
type="button"
|
||||
href="{{ $U('/consume?embedded&product=' . $currentStockEntry->product_id ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-utensils"></i></span> <span class="dropdown-item-text">{{ $__t('Consume') }}</span>
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_STOCK_LOCATION_TRACKING)
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_TRANSFER @if($currentStockEntry->amount < 1) disabled @endif"
|
||||
type="button"
|
||||
href="{{ $U('/transfer?embedded&product=' . $currentStockEntry->product_id) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-exchange-alt"></i></span> <span class="dropdown-item-text">{{ $__t('Transfer') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<a class="dropdown-item show-as-dialog-link permission-STOCK_INVENTORY"
|
||||
type="button"
|
||||
href="{{ $U('/inventory?embedded&product=' . $currentStockEntry->product_id ) }}">
|
||||
<span class="dropdown-item-icon"><i class="fas fa-list"></i></span> <span class="dropdown-item-text">{{ $__t('Inventory') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item product-consume-button product-consume-button-spoiled permission-STOCK_CONSUME @if($currentStockEntry->amount_aggregated < 1) disabled @endif"
|
||||
type="button"
|
||||
href="#"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
data-product-name="{{ $currentStockEntry->product_name }}"
|
||||
data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}"
|
||||
data-consume-amount="1">
|
||||
<span class="dropdown-item-text">{{ $__t('Consume %1$s of %2$s as spoiled', '1 ' . $currentStockEntry->qu_unit_name, $currentStockEntry->product_name) }}</span>
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_RECIPES)
|
||||
<a class="dropdown-item"
|
||||
type="button"
|
||||
href="{{ $U('/recipes?search=') }}{{ $currentStockEntry->product_name }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Search for recipes containing this product') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item product-name-cell"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
<span class="dropdown-item-text">{{ $__t('Product overview') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/stockentries?embedded&product=') }}{{ $currentStockEntry->product_id }}"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock entries') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/stockjournal?embedded&product=') }}{{ $currentStockEntry->product_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock journal') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item show-as-dialog-link"
|
||||
type="button"
|
||||
href="{{ $U('/stockjournal/summary?embedded&product_id=') }}{{ $currentStockEntry->product_id }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Stock journal summary') }}</span>
|
||||
</a>
|
||||
<a class="dropdown-item permission-MASTER_DATA_EDIT"
|
||||
type="button"
|
||||
href="{{ $U('/product/') }}{{ $currentStockEntry->product_id . '?returnto=%2Fstockoverview' }}">
|
||||
<span class="dropdown-item-text">{{ $__t('Edit product') }}</span>
|
||||
</a>
|
||||
<div class="dropdown-divider"></div>
|
||||
<a class="dropdown-item stockentry-grocycode-link"
|
||||
type="button"
|
||||
href="{{ $U('/product/' . $currentStockEntry->product_id . '/grocycode?download=true') }}">
|
||||
{{ $__t('Download product grocycode') }}
|
||||
</a>
|
||||
@if(GROCY_FEATURE_FLAG_LABELPRINTER)
|
||||
<a class="dropdown-item stockentry-grocycode-product-label-print"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
type="button"
|
||||
href="#">
|
||||
{{ $__t('Print product grocycode on label printer') }}
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-sm btn-light text-secondary"
|
||||
type="button"
|
||||
id="detached-dropdown-{!! uniqid() !!}"
|
||||
data-toggle="dropdown-detached"
|
||||
data-target="#datatable-dropdown"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}"
|
||||
data-product-name="{{ $currentStockEntry->product_name }}"
|
||||
data-product-qu-name="{{ $currentStockEntry->qu_unit_name }}"
|
||||
data-transfer="{{ ($currentStockEntry->amount < 1 ? 1 : 0) }}"
|
||||
data-consume="{{ ($currentStockEntry->amount < 1 ? 1 : 0) }}"
|
||||
data-location-id="">
|
||||
<i class="fas fa-ellipsis-v"></i>
|
||||
</button>
|
||||
</td>
|
||||
<td class="product-name-cell cursor-link"
|
||||
data-product-id="{{ $currentStockEntry->product_id }}">
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user