mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 17:45:39 +00:00
Added the ability to directly add products to the meal plan (closes #450)
This commit is contained in:
parent
061f4da041
commit
e111d07f4e
@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
### Meal plan improvements/fixes
|
### Meal plan improvements/fixes
|
||||||
- It's now possible to add notes per day (in the dropdown of the add button in the header of each day column)
|
- It's now possible to add notes per day (in the dropdown of the add button in the header of each day column)
|
||||||
|
- It's now possible to products directly (also in the dropdown of the add button in the header of each day column, maybe useful in combination with the new "Self produced products" feature)
|
||||||
- Added that the calories per serving are now also shown
|
- Added that the calories per serving are now also shown
|
||||||
- Added that the total costs and calories per day are displayed in the header of each day column
|
- Added that the total costs and calories per day are displayed in the header of each day column
|
||||||
- Fixed that when `FEATURE_FLAG_STOCK_PRICE_TRACKING` was set to `false`, prices were still shown (thanks @kriddles)
|
- Fixed that when `FEATURE_FLAG_STOCK_PRICE_TRACKING` was set to `false`, prices were still shown (thanks @kriddles)
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace Grocy\Controllers;
|
namespace Grocy\Controllers;
|
||||||
|
|
||||||
use \Grocy\Services\RecipesService;
|
use \Grocy\Services\RecipesService;
|
||||||
|
use \Grocy\Services\StockService;
|
||||||
use \Grocy\Services\UserfieldsService;
|
use \Grocy\Services\UserfieldsService;
|
||||||
|
|
||||||
class RecipesController extends BaseController
|
class RecipesController extends BaseController
|
||||||
@ -11,10 +12,12 @@ class RecipesController extends BaseController
|
|||||||
{
|
{
|
||||||
parent::__construct($container);
|
parent::__construct($container);
|
||||||
$this->RecipesService = new RecipesService();
|
$this->RecipesService = new RecipesService();
|
||||||
|
$this->StockService = new StockService();
|
||||||
$this->UserfieldsService = new UserfieldsService();
|
$this->UserfieldsService = new UserfieldsService();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $RecipesService;
|
protected $RecipesService;
|
||||||
|
protected $StockService;
|
||||||
protected $UserfieldsService;
|
protected $UserfieldsService;
|
||||||
|
|
||||||
public function Overview(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
public function Overview(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||||
@ -143,6 +146,12 @@ class RecipesController extends BaseController
|
|||||||
$title = $recipe->name;
|
$title = $recipe->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$productDetails = null;
|
||||||
|
if ($mealPlanEntry['product_id'] !== null)
|
||||||
|
{
|
||||||
|
$productDetails = $this->StockService->GetProductDetails($mealPlanEntry['product_id']);
|
||||||
|
}
|
||||||
|
|
||||||
$events[] = array(
|
$events[] = array(
|
||||||
'id' => $mealPlanEntry['id'],
|
'id' => $mealPlanEntry['id'],
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
@ -150,7 +159,8 @@ class RecipesController extends BaseController
|
|||||||
'date_format' => 'date',
|
'date_format' => 'date',
|
||||||
'recipe' => json_encode($recipe),
|
'recipe' => json_encode($recipe),
|
||||||
'mealPlanEntry' => json_encode($mealPlanEntry),
|
'mealPlanEntry' => json_encode($mealPlanEntry),
|
||||||
'type' => $mealPlanEntry['type']
|
'type' => $mealPlanEntry['type'],
|
||||||
|
'productDetails' => json_encode($productDetails)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +168,10 @@ class RecipesController extends BaseController
|
|||||||
'fullcalendarEventSources' => $events,
|
'fullcalendarEventSources' => $events,
|
||||||
'recipes' => $recipes,
|
'recipes' => $recipes,
|
||||||
'internalRecipes' => $this->Database->recipes()->whereNot('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll(),
|
'internalRecipes' => $this->Database->recipes()->whereNot('type', RecipesService::RECIPE_TYPE_NORMAL)->fetchAll(),
|
||||||
'recipesResolved' => $this->RecipesService->GetRecipesResolved()
|
'recipesResolved' => $this->RecipesService->GetRecipesResolved(),
|
||||||
|
'products' => $this->Database->products()->orderBy('name'),
|
||||||
|
'quantityUnits' => $this->Database->quantity_units()->orderBy('name'),
|
||||||
|
'quantityUnitConversionsResolved' => $this->Database->quantity_unit_conversions_resolved()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1651,3 +1651,9 @@ msgstr ""
|
|||||||
|
|
||||||
msgid "Only undone items"
|
msgid "Only undone items"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Add product"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
msgid "Add product to %s"
|
||||||
|
msgstr ""
|
||||||
|
@ -9,6 +9,9 @@ CREATE TABLE meal_plan (
|
|||||||
recipe_id INTEGER,
|
recipe_id INTEGER,
|
||||||
recipe_servings INTEGER DEFAULT 1,
|
recipe_servings INTEGER DEFAULT 1,
|
||||||
note TEXT,
|
note TEXT,
|
||||||
|
product_id INTEGER,
|
||||||
|
product_amount REAL DEFAULT 0,
|
||||||
|
product_qu_id INTEGER,
|
||||||
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
row_created_timestamp DATETIME DEFAULT (datetime('now', 'localtime'))
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -67,6 +70,26 @@ BEGIN
|
|||||||
AND type = 'recipe'
|
AND type = 'recipe'
|
||||||
AND recipe_id IS NOT NULL
|
AND recipe_id IS NOT NULL
|
||||||
GROUP BY recipe_id;
|
GROUP BY recipe_id;
|
||||||
|
|
||||||
|
-- Add all products for this day as ingredients in the day-recipe
|
||||||
|
INSERT INTO recipes_pos
|
||||||
|
(recipe_id, product_id, amount, qu_id)
|
||||||
|
SELECT (SELECT id FROM recipes WHERE name = NEW.day AND type = 'mealplan-day'), product_id, SUM(product_amount), product_qu_id
|
||||||
|
FROM meal_plan
|
||||||
|
WHERE day = NEW.day
|
||||||
|
AND type = 'product'
|
||||||
|
AND product_id IS NOT NULL
|
||||||
|
GROUP BY product_id, product_qu_id;
|
||||||
|
|
||||||
|
-- Add all products for this week as ingredients recipes in the week-recipe
|
||||||
|
INSERT INTO recipes_pos
|
||||||
|
(recipe_id, product_id, amount, qu_id)
|
||||||
|
SELECT (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', NEW.day), '0') AND type = 'mealplan-week'), product_id, SUM(product_amount), product_qu_id
|
||||||
|
FROM meal_plan
|
||||||
|
WHERE STRFTIME('%Y-%W', day) = STRFTIME('%Y-%W', NEW.day)
|
||||||
|
AND type = 'product'
|
||||||
|
AND product_id IS NOT NULL
|
||||||
|
GROUP BY product_id, product_qu_id;
|
||||||
END;
|
END;
|
||||||
|
|
||||||
CREATE TRIGGER remove_internal_recipe AFTER DELETE ON meal_plan
|
CREATE TRIGGER remove_internal_recipe AFTER DELETE ON meal_plan
|
||||||
@ -117,4 +140,24 @@ BEGIN
|
|||||||
AND type = 'recipe'
|
AND type = 'recipe'
|
||||||
AND recipe_id IS NOT NULL
|
AND recipe_id IS NOT NULL
|
||||||
GROUP BY recipe_id;
|
GROUP BY recipe_id;
|
||||||
|
|
||||||
|
-- Add all products for this day as ingredients in the day-recipe
|
||||||
|
INSERT INTO recipes_pos
|
||||||
|
(recipe_id, product_id, amount, qu_id)
|
||||||
|
SELECT (SELECT id FROM recipes WHERE name = OLD.day AND type = 'mealplan-day'), product_id, SUM(product_amount), product_qu_id
|
||||||
|
FROM meal_plan
|
||||||
|
WHERE day = OLD.day
|
||||||
|
AND type = 'product'
|
||||||
|
AND product_id IS NOT NULL
|
||||||
|
GROUP BY product_id, product_qu_id;
|
||||||
|
|
||||||
|
-- Add all products for this week as ingredients recipes in the week-recipe
|
||||||
|
INSERT INTO recipes_pos
|
||||||
|
(recipe_id, product_id, amount, qu_id)
|
||||||
|
SELECT (SELECT id FROM recipes WHERE name = LTRIM(STRFTIME('%Y-%W', OLD.day), '0') AND type = 'mealplan-week'), product_id, SUM(product_amount), product_qu_id
|
||||||
|
FROM meal_plan
|
||||||
|
WHERE STRFTIME('%Y-%W', day) = STRFTIME('%Y-%W', OLD.day)
|
||||||
|
AND type = 'product'
|
||||||
|
AND product_id IS NOT NULL
|
||||||
|
GROUP BY product_id, product_qu_id;
|
||||||
END;
|
END;
|
||||||
|
@ -35,6 +35,7 @@ var calendar = $("#calendar").fullCalendar({
|
|||||||
<button type="button" class="btn btn-outline-dark btn-xs dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button> \
|
<button type="button" class="btn btn-outline-dark btn-xs dropdown-toggle dropdown-toggle-split" data-toggle="dropdown"></button> \
|
||||||
<div class="dropdown-menu"> \
|
<div class="dropdown-menu"> \
|
||||||
<a class="dropdown-item add-note-button" href="#">' + __t('Add note') + '</a> \
|
<a class="dropdown-item add-note-button" href="#">' + __t('Add note') + '</a> \
|
||||||
|
<a class="dropdown-item add-product-button" href="#">' + __t('Add product') + '</a> \
|
||||||
</div> \
|
</div> \
|
||||||
</div>');
|
</div>');
|
||||||
|
|
||||||
@ -157,6 +158,90 @@ var calendar = $("#calendar").fullCalendar({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (event.type == "product")
|
||||||
|
{
|
||||||
|
var productDetails = JSON.parse(event.productDetails);
|
||||||
|
if (productDetails === null || productDetails === undefined)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (productDetails.last_price === null)
|
||||||
|
{
|
||||||
|
productDetails.last_price = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
element.attr("data-product-details", event.productDetails);
|
||||||
|
|
||||||
|
var productOrderMissingButtonDisabledClasses = "disabled";
|
||||||
|
if (productDetails.stock_amount_aggregated < mealPlanEntry.product_amount)
|
||||||
|
{
|
||||||
|
productOrderMissingButtonDisabledClasses = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var productConsumeButtonDisabledClasses = "disabled";
|
||||||
|
if (productDetails.stock_amount_aggregated >= mealPlanEntry.product_amount)
|
||||||
|
{
|
||||||
|
productConsumeButtonDisabledClasses = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
var fulfillmentInfoHtml = __t('Enough in stock');
|
||||||
|
var fulfillmentIconHtml = '<i class="fas fa-check text-success"></i>';
|
||||||
|
if (productDetails.stock_amount_aggregated < mealPlanEntry.product_amount)
|
||||||
|
{
|
||||||
|
fulfillmentInfoHtml = __t('Not enough in stock');
|
||||||
|
var fulfillmentIconHtml = '<i class="fas fa-times text-danger"></i>';
|
||||||
|
}
|
||||||
|
|
||||||
|
var costsAndCaloriesPerServing = ""
|
||||||
|
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||||
|
{
|
||||||
|
costsAndCaloriesPerServing = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + productDetails.last_price * mealPlanEntry.product_amount + '</span> / <span class="locale-number locale-number-generic">' + productDetails.product.calories * mealPlanEntry.product_amount + '</span> kcal ' + '<h5>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
costsAndCaloriesPerServing = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + productDetails.product.calories * mealPlanEntry.product_amount + '</span> kcal ' + '<h5>';
|
||||||
|
}
|
||||||
|
|
||||||
|
element.html('\
|
||||||
|
<div> \
|
||||||
|
<h5 class="text-truncate">' + productDetails.product.name + '<h5> \
|
||||||
|
<h5 class="small text-truncate"><span class="locale-number locale-number-quantity"' + jsonData.product_amount + "</span>" + __n(mealPlanEntry.product_amount, productDetails.quantity_unit_purchase.name, productDetails.quantity_unit_purchase.name_plural) + '</h5> \
|
||||||
|
<h5 class="small timeago-contextual text-truncate">' + fulfillmentIconHtml + " " + fulfillmentInfoHtml + '</h5> \
|
||||||
|
' + costsAndCaloriesPerServing + ' \
|
||||||
|
<h5> \
|
||||||
|
<a class="ml-1 btn btn-outline-danger btn-xs remove-product-button" href="#"><i class="fas fa-trash"></i></a> \
|
||||||
|
<!--TODO<a class="ml-1 btn btn-outline-primary btn-xs product-order-missing-button ' + productOrderMissingButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Put missing products on shopping list") + '" data-product-id="' + productDetails.product.id.toString() + '" data-product-name="' + productDetails.product.name + '" data-product-amount="' + mealPlanEntry.product_amount + '"><i class="fas fa-cart-plus"></i></a> \
|
||||||
|
<a class="ml-1 btn btn-outline-success btn-xs product-consume-button ' + productConsumeButtonDisabledClasses + '" href="#" data-toggle="tooltip" title="' + __t("Consume all ingredients needed by this recipe") + '" data-product-id="' + productDetails.product.id.toString() + '" data-product-name="' + productDetails.product.name + '" data-product-amount="' + mealPlanEntry.product_amount + '"><i class="fas fa-utensils"></i></a> \
|
||||||
|
--></h5> \
|
||||||
|
</div>');
|
||||||
|
|
||||||
|
if (productDetails.product.picture_file_name && !productDetails.product.picture_file_name.isEmpty())
|
||||||
|
{
|
||||||
|
element.html(element.html() + '<div class="mx-auto"><img data-src="' + U("/api/files/productpictures/") + btoa(productDetails.product.picture_file_name) + '?force_serve_as=picture&best_fit_width=400" class="img-fluid lazy"></div>')
|
||||||
|
}
|
||||||
|
|
||||||
|
var dayRecipeName = event.start.format("YYYY-MM-DD");
|
||||||
|
if (!$("#day-summary-" + dayRecipeName).length) // This runs for every event/recipe, so maybe multiple times per day, so only add the day summary once
|
||||||
|
{
|
||||||
|
var dayRecipe = FindObjectInArrayByPropertyValue(internalRecipes, "name", dayRecipeName);
|
||||||
|
if (dayRecipe != null)
|
||||||
|
{
|
||||||
|
var dayRecipeResolved = FindObjectInArrayByPropertyValue(recipesResolved, "recipe_id", dayRecipe.id);
|
||||||
|
|
||||||
|
var costsAndCaloriesPerDay = ""
|
||||||
|
if (Grocy.FeatureFlags.GROCY_FEATURE_FLAG_STOCK_PRICE_TRACKING)
|
||||||
|
{
|
||||||
|
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-currency">' + dayRecipeResolved.costs + '</span> / <span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '<h5>';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
costsAndCaloriesPerDay = '<h5 class="small text-truncate"><span class="locale-number locale-number-generic">' + dayRecipeResolved.calories + '</span> kcal ' + __t('per day') + '<h5>';
|
||||||
|
}
|
||||||
|
$(".fc-day-header[data-date='" + dayRecipeName + "']").append('<h5 id="day-summary-' + dayRecipeName + '" class="small text-truncate border-top pt-1 pb-0">' + costsAndCaloriesPerDay + '</h5>');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (event.type == "note")
|
else if (event.type == "note")
|
||||||
{
|
{
|
||||||
element.html('\
|
element.html('\
|
||||||
@ -203,6 +288,17 @@ $(document).on("click", ".add-note-button", function(e)
|
|||||||
Grocy.FrontendHelpers.ValidateForm("add-note-form");
|
Grocy.FrontendHelpers.ValidateForm("add-note-form");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$(document).on("click", ".add-product-button", function(e)
|
||||||
|
{
|
||||||
|
var day = $(this).parent().parent().parent().data("date");
|
||||||
|
|
||||||
|
$("#add-product-modal-title").text(__t("Add product to %s", day.toString()));
|
||||||
|
$("#day").val(day.toString());
|
||||||
|
Grocy.Components.ProductPicker.Clear();
|
||||||
|
$("#add-product-modal").modal("show");
|
||||||
|
Grocy.FrontendHelpers.ValidateForm("add-product-form");
|
||||||
|
});
|
||||||
|
|
||||||
$("#add-recipe-modal").on("shown.bs.modal", function(e)
|
$("#add-recipe-modal").on("shown.bs.modal", function(e)
|
||||||
{
|
{
|
||||||
Grocy.Components.RecipePicker.GetInputElement().focus();
|
Grocy.Components.RecipePicker.GetInputElement().focus();
|
||||||
@ -213,23 +309,12 @@ $("#add-note-modal").on("shown.bs.modal", function (e)
|
|||||||
$("#note").focus();
|
$("#note").focus();
|
||||||
})
|
})
|
||||||
|
|
||||||
$(document).on("click", ".remove-recipe-button", function(e)
|
$("#add-product-modal").on("shown.bs.modal", function (e)
|
||||||
{
|
{
|
||||||
var mealPlanEntry = JSON.parse($(this).parents(".fc-h-event:first").attr("data-meal-plan-entry"));
|
Grocy.Components.ProductPicker.GetInputElement().focus();
|
||||||
|
})
|
||||||
|
|
||||||
Grocy.Api.Delete('objects/meal_plan/' + mealPlanEntry.id.toString(), { },
|
$(document).on("click", ".remove-recipe-button, .remove-note-button, .remove-product-button", function(e)
|
||||||
function(result)
|
|
||||||
{
|
|
||||||
window.location.reload();
|
|
||||||
},
|
|
||||||
function(xhr)
|
|
||||||
{
|
|
||||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on("click", ".remove-note-button", function(e)
|
|
||||||
{
|
{
|
||||||
var mealPlanEntry = JSON.parse($(this).parents(".fc-h-event:first").attr("data-meal-plan-entry"));
|
var mealPlanEntry = JSON.parse($(this).parents(".fc-h-event:first").attr("data-meal-plan-entry"));
|
||||||
|
|
||||||
@ -289,6 +374,34 @@ $('#save-add-note-button').on('click', function(e)
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#save-add-product-button').on('click', function(e)
|
||||||
|
{
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (document.getElementById("add-product-form").checkValidity() === false) //There is at least one validation error
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonData = $('#add-product-form').serializeJSON();
|
||||||
|
jsonData.day = $("#day").val();
|
||||||
|
delete jsonData.display_amount;
|
||||||
|
jsonData.product_amount = jsonData.amount;
|
||||||
|
delete jsonData.amount;
|
||||||
|
jsonData.product_qu_id = jsonData.qu_id;
|
||||||
|
delete jsonData.qu_id;
|
||||||
|
Grocy.Api.Post('objects/meal_plan', jsonData,
|
||||||
|
function(result)
|
||||||
|
{
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
function(xhr)
|
||||||
|
{
|
||||||
|
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
Grocy.Components.RecipePicker.GetInputElement().keydown(function(event)
|
Grocy.Components.RecipePicker.GetInputElement().keydown(function(event)
|
||||||
{
|
{
|
||||||
if (event.keyCode === 13) //Enter
|
if (event.keyCode === 13) //Enter
|
||||||
@ -461,3 +574,27 @@ $(window).on("resize", function()
|
|||||||
calendar.fullCalendar("changeView", "basicWeek");
|
calendar.fullCalendar("changeView", "basicWeek");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Grocy.Components.ProductPicker.GetPicker().on('change', function(e)
|
||||||
|
{
|
||||||
|
var productId = $(e.target).val();
|
||||||
|
|
||||||
|
if (productId)
|
||||||
|
{
|
||||||
|
Grocy.Api.Get('stock/products/' + productId,
|
||||||
|
function(productDetails)
|
||||||
|
{
|
||||||
|
Grocy.Components.ProductAmountPicker.Reload(productDetails.product.id, productDetails.quantity_unit_stock.id, true);
|
||||||
|
|
||||||
|
$('#display_amount').val(1);
|
||||||
|
$('#display_amount').focus();
|
||||||
|
$(".input-group-productamountpicker").trigger("change");
|
||||||
|
Grocy.FrontendHelpers.ValidateForm('add-product-form');
|
||||||
|
},
|
||||||
|
function(xhr)
|
||||||
|
{
|
||||||
|
console.error(xhr);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
@ -18,6 +18,9 @@
|
|||||||
var fullcalendarEventSources = {!! json_encode(array($fullcalendarEventSources)) !!}
|
var fullcalendarEventSources = {!! json_encode(array($fullcalendarEventSources)) !!}
|
||||||
var internalRecipes = {!! json_encode($internalRecipes) !!}
|
var internalRecipes = {!! json_encode($internalRecipes) !!}
|
||||||
var recipesResolved = {!! json_encode($recipesResolved) !!}
|
var recipesResolved = {!! json_encode($recipesResolved) !!}
|
||||||
|
|
||||||
|
Grocy.QuantityUnits = {!! json_encode($quantityUnits) !!};
|
||||||
|
Grocy.QuantityUnitConversionsResolved = {!! json_encode($quantityUnitConversionsResolved) !!};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
@ -95,4 +98,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="add-product-modal" tabindex="-1">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 id="add-product-modal-title" class="modal-title w-100"></h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form id="add-product-form" novalidate>
|
||||||
|
|
||||||
|
@include('components.productpicker', array(
|
||||||
|
'products' => $products,
|
||||||
|
'nextInputSelector' => '#amount'
|
||||||
|
))
|
||||||
|
|
||||||
|
@include('components.productamountpicker', array(
|
||||||
|
'value' => 1,
|
||||||
|
'additionalGroupCssClasses' => 'mb-0'
|
||||||
|
))
|
||||||
|
|
||||||
|
<input type="hidden" name="type" value="product">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">{{ $__t('Cancel') }}</button>
|
||||||
|
<button id="save-add-product-button" data-dismiss="modal" class="btn btn-success">{{ $__t('Save') }}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@stop
|
@stop
|
||||||
|
Loading…
x
Reference in New Issue
Block a user