mirror of
https://github.com/grocy/grocy.git
synced 2025-04-29 17:45:39 +00:00
Finished first version of "pictures for products" (references #58)
This commit is contained in:
parent
fcdeb33426
commit
f1fc0ee549
@ -14,7 +14,7 @@ class FilesApiController extends BaseApiController
|
|||||||
|
|
||||||
protected $FilesService;
|
protected $FilesService;
|
||||||
|
|
||||||
public function Upload(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
public function UploadFile(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -69,4 +69,31 @@ class FilesApiController extends BaseApiController
|
|||||||
return $this->VoidApiActionResponse($response, false, 400, $ex->getMessage());
|
return $this->VoidApiActionResponse($response, false, 400, $ex->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function DeleteFile(\Slim\Http\Request $request, \Slim\Http\Response $response, array $args)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isset($request->getQueryParams()['file_name']) && !empty($request->getQueryParams()['file_name']) && IsValidFileName($request->getQueryParams()['file_name']))
|
||||||
|
{
|
||||||
|
$fileName = $request->getQueryParams()['file_name'];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new \Exception('file_name query parameter missing or contains an invalid filename');
|
||||||
|
}
|
||||||
|
|
||||||
|
$filePath = $this->FilesService->GetFilePath($args['group'], $fileName);
|
||||||
|
if (file_exists($filePath))
|
||||||
|
{
|
||||||
|
unlink($filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->ApiResponse(array('success' => true));
|
||||||
|
}
|
||||||
|
catch (\Exception $ex)
|
||||||
|
{
|
||||||
|
return $this->VoidApiActionResponse($response, false, 400, $ex->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,9 +431,58 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/files/upload/{group}": {
|
"/file/{group}": {
|
||||||
"post": {
|
"get": {
|
||||||
"description": "Uploads a single file to /data/storage/{group}/{file_name}",
|
"description": "Serves the given file (with proper Content-Type header)",
|
||||||
|
"tags": [
|
||||||
|
"Files"
|
||||||
|
],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "path",
|
||||||
|
"name": "group",
|
||||||
|
"required": true,
|
||||||
|
"description": "The file group",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "file_name",
|
||||||
|
"required": true,
|
||||||
|
"description": "The file name (including extension)",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The binary file contents (Content-Type header is automatically set based on the file type)",
|
||||||
|
"content": {
|
||||||
|
"application/octet-stream": {
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "binary"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "A VoidApiActionResponse object",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ErrorExampleVoidApiActionResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"put": {
|
||||||
|
"description": "Uploads a single file to /data/storage/{group}/{file_name} (you need to remember the group and file name to get or delete it again)",
|
||||||
"tags": [
|
"tags": [
|
||||||
"Files"
|
"Files"
|
||||||
],
|
],
|
||||||
@ -489,11 +538,9 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
"delete": {
|
||||||
"/files/get/{group}": {
|
"description": "Deletes the given file",
|
||||||
"get": {
|
|
||||||
"description": "Serves the given file",
|
|
||||||
"tags": [
|
"tags": [
|
||||||
"Files"
|
"Files"
|
||||||
],
|
],
|
||||||
@ -519,12 +566,11 @@
|
|||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
"200": {
|
"200": {
|
||||||
"description": "The binary file contents (mime type is set based on file extension)",
|
"description": "A VoidApiActionResponse object",
|
||||||
"content": {
|
"content": {
|
||||||
"application/octet-stream": {
|
"application/json": {
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "string",
|
"$ref": "#/components/schemas/VoidApiActionResponse"
|
||||||
"format": "binary"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1683,6 +1729,9 @@
|
|||||||
"minimum": 0,
|
"minimum": 0,
|
||||||
"default": 0
|
"default": 0
|
||||||
},
|
},
|
||||||
|
"picture_file_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"row_created_timestamp": {
|
"row_created_timestamp": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "date-time"
|
"format": "date-time"
|
||||||
|
@ -192,7 +192,7 @@ function Pluralize($number, $singularForm, $pluralForm)
|
|||||||
|
|
||||||
function IsValidFileName($fileName)
|
function IsValidFileName($fileName)
|
||||||
{
|
{
|
||||||
if(preg_match('#^[a-z0-9]+\.[a-z]+?$#i', $fileName))
|
if(preg_match('=^[^/?*;:{}\\\\]+\.[^/?*;:{}\\\\]+$=', $fileName))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -54,3 +54,13 @@ BoolVal = function(test)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GetFileNameFromPath = function(path)
|
||||||
|
{
|
||||||
|
return path.split("/").pop().split("\\").pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
GetFileExtension = function(pathOrFileName)
|
||||||
|
{
|
||||||
|
return pathOrFileName.split(".").pop();
|
||||||
|
}
|
||||||
|
@ -177,15 +177,10 @@ Grocy.Api.Post = function(apiFunction, jsonData, success, error)
|
|||||||
xhr.send(JSON.stringify(jsonData));
|
xhr.send(JSON.stringify(jsonData));
|
||||||
};
|
};
|
||||||
|
|
||||||
Grocy.Api.UploadFile = function(fileInput, group, success, error)
|
Grocy.Api.UploadFile = function(file, group, fileName, success, error)
|
||||||
{
|
{
|
||||||
if (fileInput[0].files.length === 0)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
var url = U('/api/files/upload/' + group + '?file_name=' + encodeURIComponent(fileInput[0].files[0].name));
|
var url = U('/api/file/' + group + '?file_name=' + encodeURIComponent(fileName));
|
||||||
|
|
||||||
xhr.onreadystatechange = function()
|
xhr.onreadystatechange = function()
|
||||||
{
|
{
|
||||||
@ -208,9 +203,40 @@ Grocy.Api.UploadFile = function(fileInput, group, success, error)
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
xhr.open('POST', url, true);
|
xhr.open('PUT', url, true);
|
||||||
xhr.setRequestHeader('Content-type', 'application/octet-stream');
|
xhr.setRequestHeader('Content-type', 'application/octet-stream');
|
||||||
xhr.send(fileInput[0].files[0]);
|
xhr.send(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
Grocy.Api.DeleteFile = function(fileName, group, success, error)
|
||||||
|
{
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
var url = U('/api/file/' + group + '?file_name=' + encodeURIComponent(fileName));
|
||||||
|
|
||||||
|
xhr.onreadystatechange = function()
|
||||||
|
{
|
||||||
|
if (xhr.readyState === XMLHttpRequest.DONE)
|
||||||
|
{
|
||||||
|
if (xhr.status === 200)
|
||||||
|
{
|
||||||
|
if (success)
|
||||||
|
{
|
||||||
|
success(JSON.parse(xhr.responseText));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
error(xhr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
xhr.open('DELETE', url, true);
|
||||||
|
xhr.setRequestHeader('Content-type', 'application/json');
|
||||||
|
xhr.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
Grocy.FrontendHelpers = { };
|
Grocy.FrontendHelpers = { };
|
||||||
@ -284,3 +310,15 @@ $(".user-setting-control").on("change", function()
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Show file name Bootstrap custom file input
|
||||||
|
$('input.custom-file-input').on('change', function()
|
||||||
|
{
|
||||||
|
$(this).next('.custom-file-label').html(GetFileNameFromPath($(this).val()));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Translation of "Browse"-button of Bootstrap custom file input
|
||||||
|
if ($(".custom-file-label").length > 0)
|
||||||
|
{
|
||||||
|
$("<style>").html('.custom-file-label::after { content: "' + L("Select file") + '"; }').appendTo("head");
|
||||||
|
}
|
||||||
|
@ -24,6 +24,18 @@ Grocy.Components.ProductCard.Refresh = function(productId)
|
|||||||
$('#productcard-product-last-price').text(L('Unknown'));
|
$('#productcard-product-last-price').text(L('Unknown'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (productDetails.product.picture_file_name !== null && !productDetails.product.picture_file_name.isEmpty())
|
||||||
|
{
|
||||||
|
$("#productcard-no-product-picture").addClass("d-none");
|
||||||
|
$("#productcard-product-picture").removeClass("d-none");
|
||||||
|
$("#productcard-product-picture").attr("src", U('/api/file/productpictures?file_name=' + productDetails.product.picture_file_name));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$("#productcard-no-product-picture").removeClass("d-none");
|
||||||
|
$("#productcard-product-picture").addClass("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
EmptyElementWhenMatches('#productcard-product-last-purchased-timeago', L('timeago_nan'));
|
EmptyElementWhenMatches('#productcard-product-last-purchased-timeago', L('timeago_nan'));
|
||||||
EmptyElementWhenMatches('#productcard-product-last-used-timeago', L('timeago_nan'));
|
EmptyElementWhenMatches('#productcard-product-last-used-timeago', L('timeago_nan'));
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
$('#save-product-button').on('click', function(e)
|
$('#save-product-button').on('click', function (e)
|
||||||
{
|
{
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
@ -12,22 +12,28 @@
|
|||||||
var jsonData = $('#product-form').serializeJSON();
|
var jsonData = $('#product-form').serializeJSON();
|
||||||
if ($("#product-picture")[0].files.length > 0)
|
if ($("#product-picture")[0].files.length > 0)
|
||||||
{
|
{
|
||||||
jsonData.picture_file_name = $("#product-picture")[0].files[0].name;
|
var someRandomStuff = Math.random().toString(36).substring(2, 100) + Math.random().toString(36).substring(2, 100);
|
||||||
|
jsonData.picture_file_name = someRandomStuff + $("#product-picture")[0].files[0].name;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Grocy.DeleteProductPictureOnSave)
|
||||||
|
{
|
||||||
|
jsonData.picture_file_name = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Grocy.EditMode === 'create')
|
if (Grocy.EditMode === 'create')
|
||||||
{
|
{
|
||||||
Grocy.Api.Post('add-object/products', jsonData,
|
Grocy.Api.Post('add-object/products', jsonData,
|
||||||
function(result)
|
function (result)
|
||||||
{
|
{
|
||||||
if (jsonData.hasOwnProperty("picture_file_name"))
|
if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteProductPictureOnSave)
|
||||||
{
|
{
|
||||||
Grocy.Api.UploadFile($("#product-picture"), 'productpictures',
|
Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
|
||||||
function(result)
|
function (result)
|
||||||
{
|
{
|
||||||
window.location.href = redirectDestination;
|
window.location.href = redirectDestination;
|
||||||
},
|
},
|
||||||
function(xhr)
|
function (xhr)
|
||||||
{
|
{
|
||||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||||
}
|
}
|
||||||
@ -38,7 +44,7 @@
|
|||||||
window.location.href = redirectDestination;
|
window.location.href = redirectDestination;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
function(xhr)
|
function (xhr)
|
||||||
{
|
{
|
||||||
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||||
}
|
}
|
||||||
@ -46,12 +52,26 @@
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (Grocy.DeleteProductPictureOnSave)
|
||||||
|
{
|
||||||
|
Grocy.Api.DeleteFile(Grocy.ProductPictureFileName, 'productpictures',
|
||||||
|
function(result)
|
||||||
|
{
|
||||||
|
// Nothing to do
|
||||||
|
},
|
||||||
|
function(xhr)
|
||||||
|
{
|
||||||
|
Grocy.FrontendHelpers.ShowGenericError('Error while saving, probably this item already exists', xhr.response)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
Grocy.Api.Post('edit-object/products/' + Grocy.EditObjectId, jsonData,
|
Grocy.Api.Post('edit-object/products/' + Grocy.EditObjectId, jsonData,
|
||||||
function(result)
|
function(result)
|
||||||
{
|
{
|
||||||
if (jsonData.hasOwnProperty("picture_file_name"))
|
if (jsonData.hasOwnProperty("picture_file_name") && !Grocy.DeleteProductPictureOnSave)
|
||||||
{
|
{
|
||||||
Grocy.Api.UploadFile($("#product-picture"), 'productpictures',
|
Grocy.Api.UploadFile($("#product-picture")[0].files[0], 'productpictures', jsonData.picture_file_name,
|
||||||
function(result)
|
function(result)
|
||||||
{
|
{
|
||||||
window.location.href = redirectDestination;
|
window.location.href = redirectDestination;
|
||||||
@ -150,6 +170,15 @@ $('#product-form input').keydown(function(event)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Grocy.DeleteProductPictureOnSave = false;
|
||||||
|
$('#delete-current-product-picture-button').on('click', function (e)
|
||||||
|
{
|
||||||
|
Grocy.DeleteProductPictureOnSave = true;
|
||||||
|
$("#current-product-picture").addClass("d-none");
|
||||||
|
$("#delete-current-product-picture-on-save-hint").removeClass("d-none");
|
||||||
|
$("#delete-current-product-picture-button").addClass("disabled");
|
||||||
|
});
|
||||||
|
|
||||||
$('#name').focus();
|
$('#name').focus();
|
||||||
$('.input-group-qu').trigger('change');
|
$('.input-group-qu').trigger('change');
|
||||||
Grocy.FrontendHelpers.ValidateForm('product-form');
|
Grocy.FrontendHelpers.ValidateForm('product-form');
|
||||||
|
@ -141,15 +141,20 @@ $(document).on('click', '.product-consume-button', function(e)
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
$(document).on("click", ".show-product-picture-button", function(e)
|
$(document).on("click", ".product-name-cell", function(e)
|
||||||
{
|
{
|
||||||
var pictureUrl = $(e.currentTarget).attr("data-picture-url");
|
var productHasPicture = BoolVal($(e.currentTarget).attr("data-product-has-picture"));
|
||||||
var productName = $(e.currentTarget).attr("data-product-name");
|
|
||||||
|
|
||||||
bootbox.alert({
|
if (productHasPicture)
|
||||||
title: L("Image of product #1", productName),
|
{
|
||||||
message: "<img src='" + pictureUrl + "' class='img-fluid'>"
|
var pictureUrl = $(e.currentTarget).attr("data-picture-url");
|
||||||
});
|
var productName = $(e.currentTarget).attr("data-product-name");
|
||||||
|
|
||||||
|
bootbox.alert({
|
||||||
|
title: L("Image of product #1", productName),
|
||||||
|
message: "<img src='" + pictureUrl + "' class='img-fluid'>"
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
function RefreshStatistics()
|
function RefreshStatistics()
|
||||||
|
@ -84,8 +84,9 @@ $app->group('/api', function()
|
|||||||
$this->post('/system/log-missing-localization', '\Grocy\Controllers\SystemApiController:LogMissingLocalization');
|
$this->post('/system/log-missing-localization', '\Grocy\Controllers\SystemApiController:LogMissingLocalization');
|
||||||
|
|
||||||
// Files
|
// Files
|
||||||
$this->post('/files/upload/{group}', '\Grocy\Controllers\FilesApiController:Upload');
|
$this->put('/file/{group}', '\Grocy\Controllers\FilesApiController:UploadFile');
|
||||||
$this->get('/files/get/{group}', '\Grocy\Controllers\FilesApiController:ServeFile');
|
$this->get('/file/{group}', '\Grocy\Controllers\FilesApiController:ServeFile');
|
||||||
|
$this->delete('/file/{group}', '\Grocy\Controllers\FilesApiController:DeleteFile');
|
||||||
|
|
||||||
// Users
|
// Users
|
||||||
$this->get('/users/get', '\Grocy\Controllers\UsersApiController:GetUsers');
|
$this->get('/users/get', '\Grocy\Controllers\UsersApiController:GetUsers');
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
<strong>{{ $L('Last used') }}:</strong> <span id="productcard-product-last-used"></span> <time id="productcard-product-last-used-timeago" class="timeago timeago-contextual"></time><br>
|
<strong>{{ $L('Last used') }}:</strong> <span id="productcard-product-last-used"></span> <time id="productcard-product-last-used-timeago" class="timeago timeago-contextual"></time><br>
|
||||||
<strong>{{ $L('Last price') }}:</strong> <span id="productcard-product-last-price"></span>
|
<strong>{{ $L('Last price') }}:</strong> <span id="productcard-product-last-price"></span>
|
||||||
|
|
||||||
|
<h5 class="mt-3">{{ $L('Product picture') }}</h5>
|
||||||
|
<img id="productcard-product-picture" src="" class="img-fluid img-thumbnail d-none">
|
||||||
|
<span id="productcard-no-product-picture" class="font-italic d-none">{{ $L('No picture') }}</span>
|
||||||
|
|
||||||
<h5 class="mt-3">{{ $L('Price history') }}</h5>
|
<h5 class="mt-3">{{ $L('Price history') }}</h5>
|
||||||
<canvas id="productcard-product-price-history-chart" class="w-100 d-none"></canvas>
|
<canvas id="productcard-product-price-history-chart" class="w-100 d-none"></canvas>
|
||||||
<span id="productcard-no-price-data-hint" class="font-italic d-none">{{ $L('No price history available') }}</span>
|
<span id="productcard-no-price-data-hint" class="font-italic d-none">{{ $L('No price history available') }}</span>
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
<div class="col-lg-6 col-xs-12">
|
<div class="col-lg-6 col-xs-12">
|
||||||
<h1>@yield('title')</h1>
|
<h1>@yield('title')</h1>
|
||||||
|
|
||||||
@ -17,6 +18,10 @@
|
|||||||
|
|
||||||
@if($mode == 'edit')
|
@if($mode == 'edit')
|
||||||
<script>Grocy.EditObjectId = {{ $product->id }};</script>
|
<script>Grocy.EditObjectId = {{ $product->id }};</script>
|
||||||
|
|
||||||
|
@if(!empty($product->picture_file_name))
|
||||||
|
<script>Grocy.ProductPictureFileName = '{{ $product->picture_file_name }}';</script>
|
||||||
|
@endif
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
<form id="product-form" novalidate>
|
<form id="product-form" novalidate>
|
||||||
@ -109,17 +114,27 @@
|
|||||||
))
|
))
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="product-picture">{{ $L('Product picture') }}</label>
|
<label for="product-pictur">{{ $L('Product picture') }}</label>
|
||||||
<input type="file" class="form-control-file" id="product-picture" accept="image/*">
|
<div class="custom-file">
|
||||||
|
<input type="file" class="custom-file-input" id="product-picture">
|
||||||
@if(!empty($product->picture_file_name))
|
<label class="custom-file-label" for="product-picture">{{ $L('No file selected') }}</label>
|
||||||
<label class="mt-2">{{ $L('Current picture') }}</label>
|
</div>
|
||||||
<img src="{{ $U('/api/files/get/productpictures?file_name=' . $product->picture_file_name) }}" class="img-fluid">
|
<p class="form-text text-muted small">{{ $L('If you don\'t select a file, the current picture will not be altered') }}</p>
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="save-product-button" class="btn btn-success">{{ $L('Save') }}</button>
|
<button id="save-product-button" class="btn btn-success">{{ $L('Save') }}</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-lg-6 col-xs-12">
|
||||||
|
<label class="mt-2">{{ $L('Current picture') }}</label>
|
||||||
|
<button id="delete-current-product-picture-button" class="btn btn-sm btn-danger @if(empty($product->picture_file_name)) disabled @endif"><i class="fas fa-trash"></i> {{ $L('Delete') }}</button>
|
||||||
|
@if(!empty($product->picture_file_name))
|
||||||
|
<img id="current-product-picture" src="{{ $U('/api/file/productpictures?file_name=' . $product->picture_file_name) }}" class="img-fluid img-thumbnail mt-2">
|
||||||
|
<p id="delete-current-product-picture-on-save-hint" class="form-text text-muted font-italic d-none">{{ $L('The current picture will be deleted when you save the product') }}</p>
|
||||||
|
@else
|
||||||
|
<p id="no-current-product-picture-hint" class="form-text text-muted font-italic">{{ $L('No picture') }}</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@stop
|
@stop
|
||||||
|
@ -8,6 +8,14 @@
|
|||||||
<script src="{{ $U('/node_modules/jquery-ui-dist/jquery-ui.min.js?v=', true) }}{{ $version }}"></script>
|
<script src="{{ $U('/node_modules/jquery-ui-dist/jquery-ui.min.js?v=', true) }}{{ $version }}"></script>
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
|
@push('pageStyles')
|
||||||
|
<style>
|
||||||
|
.product-name-cell[data-product-has-picture='true'] {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@endpush
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
@ -74,14 +82,12 @@
|
|||||||
data-consume-amount="{{ $currentStockEntry->amount }}">
|
data-consume-amount="{{ $currentStockEntry->amount }}">
|
||||||
<i class="fas fa-utensils"></i> {{ $L('All') }}
|
<i class="fas fa-utensils"></i> {{ $L('All') }}
|
||||||
</a>
|
</a>
|
||||||
<a id="product-{{ $currentStockEntry->product_id }}-show-product-picture-button" class="btn btn-info btn-sm show-product-picture-button @if(empty(FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->picture_file_name)) disabled @endif" href="#"
|
|
||||||
data-picture-url="{{ $U('/api/files/get/productpictures?file_name=' . FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->picture_file_name) }}"
|
|
||||||
data-product-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }}">
|
|
||||||
<i class="fas fa-image"></i>
|
|
||||||
</a>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td class="product-name-cell"
|
||||||
{{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }}
|
data-picture-url="{{ $U('/api/file/productpictures?file_name=' . FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->picture_file_name) }}"
|
||||||
|
data-product-name="{{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }}"
|
||||||
|
data-product-has-picture="{{ BoolToString(!empty(FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->picture_file_name)) }}">
|
||||||
|
{{ FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->name }}@if(!empty(FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->picture_file_name)) <i class="fas fa-image text-muted"></i>@endif
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<span id="product-{{ $currentStockEntry->product_id }}-amount">{{ $currentStockEntry->amount }}</span> {{ Pluralize($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }}
|
<span id="product-{{ $currentStockEntry->product_id }}-amount">{{ $currentStockEntry->amount }}</span> {{ Pluralize($currentStockEntry->amount, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name, FindObjectInArrayByPropertyValue($quantityunits, 'id', FindObjectInArrayByPropertyValue($products, 'id', $currentStockEntry->product_id)->qu_id_stock)->name_plural) }}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user