extra features, after publish, before upgrade
This commit is contained in:
parent
805c5e2299
commit
61d3b2089c
|
|
@ -765,6 +765,48 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
// };
|
||||
//}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Add a note to an order that will be displayed in the external application
|
||||
/// </summary>
|
||||
[HttpPost]
|
||||
public async Task<IActionResult> AddOrderNote(int orderId, string note)
|
||||
{
|
||||
if (!await _permissionService.AuthorizeAsync(StandardPermission.Orders.ORDERS_CREATE_EDIT_DELETE))
|
||||
return Json(new { success = false, message = "Access denied" });
|
||||
|
||||
if (orderId <= 0)
|
||||
{
|
||||
return Json(new { success = false, message = "Invalid order ID" });
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(note))
|
||||
{
|
||||
return Json(new { success = false, message = "Note text is required" });
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Create and insert the order note
|
||||
await InsertOrderNoteAsync(orderId, displayToCustomer: false, note);
|
||||
|
||||
return Json(new
|
||||
{
|
||||
success = true,
|
||||
message = "Order note added successfully"
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return Json(new
|
||||
{
|
||||
success = false,
|
||||
message = $"Error adding order note: {ex.Message}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static OrderNote CreateOrderNote(int orderId, bool displayToCustomer, string note)
|
||||
{
|
||||
return new OrderNote
|
||||
|
|
@ -878,7 +920,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
if (string.IsNullOrWhiteSpace(term) || term.Length < 2)
|
||||
return Json(new List<object>());
|
||||
|
||||
const int maxResults = 15;
|
||||
const int maxResults = 30;
|
||||
|
||||
// Search products by name or SKU
|
||||
var products = await _productService.SearchProductsAsync(
|
||||
|
|
@ -892,16 +934,21 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Areas.Admin.Controllers
|
|||
foreach (var product in products)
|
||||
{
|
||||
var productDto = productDtosById[product.Id];
|
||||
|
||||
result.Add(new
|
||||
if (productDto != null)
|
||||
{
|
||||
label = $"{product.Name} [RENDELHETŐ: {(product.StockQuantity + productDto.IncomingQuantity)}] [ÁR: {product.Price}]",
|
||||
value = product.Id,
|
||||
sku = product.Sku,
|
||||
price = product.Price,
|
||||
stockQuantity = product.StockQuantity,
|
||||
incomingQuantity = productDto.IncomingQuantity,
|
||||
});
|
||||
if(productDto.AvailableQuantity > 0)
|
||||
{
|
||||
result.Add(new
|
||||
{
|
||||
label = $"{product.Name} [RENDELHETŐ: {(product.StockQuantity + productDto.IncomingQuantity)}] [ÁR: {product.Price}]",
|
||||
value = product.Id,
|
||||
sku = product.Sku,
|
||||
price = product.Price,
|
||||
stockQuantity = product.StockQuantity,
|
||||
incomingQuantity = productDto.IncomingQuantity,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Json(result);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
@{
|
||||
@{
|
||||
Layout = "_ConfigurePlugin";
|
||||
}
|
||||
|
||||
|
|
@ -13,6 +13,21 @@
|
|||
<h4>Voice Recorder</h4>
|
||||
<p>Click the button below to start recording your voice message.</p>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle"></i> <strong>Important:</strong> When you click "Start Recording", your browser will ask for permission to use your microphone. Please click "Allow" to enable voice recording.
|
||||
</div>
|
||||
|
||||
<div id="permissionInstructions" class="alert alert-warning" style="display:none;">
|
||||
<strong><i class="fas fa-exclamation-triangle"></i> Microphone Permission Required</strong>
|
||||
<p class="mb-2">Your browser blocked microphone access. To enable it:</p>
|
||||
<ul class="mb-2">
|
||||
<li><strong>Chrome/Edge:</strong> Click the camera/microphone icon in the address bar (or the lock icon) → Site settings → Allow Microphone</li>
|
||||
<li><strong>Firefox:</strong> Click the microphone icon in the address bar → Click the X to remove the block → Refresh the page</li>
|
||||
<li><strong>Safari:</strong> Safari menu → Settings for This Website → Microphone → Allow</li>
|
||||
</ul>
|
||||
<p class="mb-0">After allowing permission, <strong>refresh this page</strong> and try again.</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<div class="col-md-9">
|
||||
<button type="button" id="recordButton" class="btn btn-primary">
|
||||
|
|
@ -70,9 +85,29 @@
|
|||
const responseMessage = document.getElementById('responseMessage');
|
||||
const transcriptionResult = document.getElementById('transcriptionResult');
|
||||
const transcriptionText = document.getElementById('transcriptionText');
|
||||
const permissionInstructions = document.getElementById('permissionInstructions');
|
||||
|
||||
recordButton.addEventListener('click', async () => {
|
||||
try {
|
||||
// Hide permission instructions if shown
|
||||
permissionInstructions.style.display = 'none';
|
||||
|
||||
// Check if we're on HTTPS or localhost
|
||||
const isSecure = window.location.protocol === 'https:' ||
|
||||
window.location.hostname === 'localhost' ||
|
||||
window.location.hostname === '127.0.0.1';
|
||||
|
||||
if (!isSecure) {
|
||||
showMessage('Error: Microphone access requires HTTPS connection. Please access this page via HTTPS.', 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if getUserMedia is supported
|
||||
if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
|
||||
showMessage('Error: Your browser does not support audio recording. Please use a modern browser like Chrome, Firefox, or Edge.', 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
|
||||
|
||||
mediaRecorder = new MediaRecorder(stream);
|
||||
|
|
@ -103,7 +138,35 @@
|
|||
|
||||
} catch (error) {
|
||||
console.error('Error accessing microphone:', error);
|
||||
showMessage('Error: Could not access microphone. Please check permissions.', 'danger');
|
||||
|
||||
let errorMessage = 'Error: Could not access microphone. ';
|
||||
let showInstructions = false;
|
||||
|
||||
if (error.name === 'NotAllowedError' || error.name === 'PermissionDeniedError') {
|
||||
errorMessage = 'Microphone access was denied. Please see the instructions below to enable microphone access.';
|
||||
showInstructions = true;
|
||||
} else if (error.name === 'NotFoundError' || error.name === 'DevicesNotFoundError') {
|
||||
errorMessage += 'No microphone found. Please connect a microphone and try again.';
|
||||
} else if (error.name === 'NotReadableError' || error.name === 'TrackStartError') {
|
||||
errorMessage += 'Microphone is already in use by another application. Please close other applications using the microphone.';
|
||||
} else if (error.name === 'OverconstrainedError' || error.name === 'ConstraintNotSatisfiedError') {
|
||||
errorMessage += 'Microphone does not meet the required constraints.';
|
||||
} else if (error.name === 'NotSupportedError') {
|
||||
errorMessage += 'This page must be accessed via HTTPS to use the microphone.';
|
||||
} else if (error.name === 'TypeError') {
|
||||
errorMessage += 'This page must be accessed via HTTPS to use the microphone.';
|
||||
} else {
|
||||
errorMessage += error.message || 'Unknown error occurred.';
|
||||
}
|
||||
|
||||
showMessage(errorMessage, 'danger');
|
||||
|
||||
// Show permission instructions if needed
|
||||
if (showInstructions) {
|
||||
permissionInstructions.style.display = 'block';
|
||||
// Scroll to instructions
|
||||
permissionInstructions.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -133,7 +196,7 @@
|
|||
// Get the antiforgery token
|
||||
const token = document.querySelector('input[name="__RequestVerificationToken"]').value;
|
||||
|
||||
const response = await fetch('@Url.Action("ReceiveVoiceRecording", "FruitBankAudio")', {
|
||||
const response = await fetch('@Url.Action("ReceiveVoiceRecording", "FruitBankAdmin")', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'RequestVerificationToken': token
|
||||
|
|
|
|||
|
|
@ -355,7 +355,9 @@
|
|||
<td style="width: 80px;" class="text-center">
|
||||
|
||||
|
||||
<span class="">Eltérés: @item.AverageWeightDifference (KG), Mért átlag: @item.AverageWeight (KG/rekesz)</span>
|
||||
<span class="">Eltérés: @item.AverageWeightDifference (KG)</span>
|
||||
<span class="">Mért átlag: @item.AverageWeight (KG/rekesz)</span>
|
||||
<span class="">Elvárt: @item.ProductAverageWeight</span>
|
||||
|
||||
<input type="hidden" name="pvAverageWeightDifference@(item.Id)" id="pvAverageWeightDifference@(item.Id)" value="@(item.AverageWeightDifference.ToString())" disabled />
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -228,6 +228,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Factories
|
|||
orderItemModelExtended.ProductStockQuantity = orderItemDto.ProductDto!.StockQuantity;
|
||||
orderItemModelExtended.ProductIncomingQuantity = orderItemDto.ProductDto.IncomingQuantity;
|
||||
orderItemModelExtended.ProductAvailableQuantity = orderItemDto.ProductDto.AvailableQuantity;
|
||||
orderItemModelExtended.ProductAverageWeight = orderItemDto.ProductDto.AverageWeight;
|
||||
orderItemModelExtended.AverageWeight = orderItemDto.AverageWeight;
|
||||
orderItemModelExtended.AverageWeightIsValid = orderItemDto.AverageWeightIsValid;
|
||||
orderItemModelExtended.AverageWeightDifference = orderItemDto.AverageWeightDifference;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ namespace Nop.Plugin.Misc.FruitBankPlugin.Models.Orders
|
|||
public int ProductStockQuantity { get; set; }
|
||||
public int ProductIncomingQuantity { get; set; }
|
||||
public int ProductAvailableQuantity { get; set; }
|
||||
public double ProductAverageWeight { get; set; }
|
||||
public double AverageWeight { get; set; }
|
||||
public bool AverageWeightIsValid { get; set; }
|
||||
public double AverageWeightDifference { get; set; }
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@
|
|||
</div>
|
||||
<div class="col-md-6">
|
||||
<nop-editor asp-for="DateOfReceipt" asp-template="" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 text-right">
|
||||
<button type="button" id="saveAttributesBtn" class="btn btn-primary">
|
||||
<i class="fa fa-save"></i> Mentés
|
||||
|
|
@ -38,16 +38,21 @@
|
|||
</div>
|
||||
<hr />
|
||||
<div class="form-group row">
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-4">
|
||||
<button type="button" class="btn btn-warning btn-block" data-toggle="modal" data-target="#allowRevisionModal">
|
||||
<i class="fa fa-redo"></i> Újramérés engedélyezése
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="col-md-4">
|
||||
<button type="button" class="btn btn-primary btn-block" data-toggle="modal" data-target="#sendMessageModal">
|
||||
<i class="fas fa-paper-plane"></i> Üzenet küldése
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<button type="button" class="btn btn-info btn-block" data-toggle="modal" data-target="#addOrderNoteModal">
|
||||
<i class="fas fa-sticky-note"></i> Jegyzet hozzáadása
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -60,7 +65,7 @@
|
|||
</div>
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
|
||||
|
||||
<div id="orderStatus" class="alert alert-info" style="display: none;">
|
||||
<i class="fas fa-info-circle"></i> <span id="orderStatusMessage"></span>
|
||||
</div>
|
||||
|
|
@ -161,6 +166,40 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Add Order Note Modal -->
|
||||
<div class="modal fade" id="addOrderNoteModal" tabindex="-1" role="dialog" aria-labelledby="addOrderNoteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addOrderNoteModalLabel">
|
||||
<i class="fas fa-sticky-note"></i> Jegyzet hozzáadása
|
||||
</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="orderNoteText"><strong>Jegyzet szövege:</strong></label>
|
||||
<textarea id="orderNoteText" class="form-control" rows="4" placeholder="Írja be a jegyzetet a kollégák számára..."></textarea>
|
||||
<small class="form-text text-muted">Ez a jegyzet megjelenik az external alkalmazásban a kollégák számára.</small>
|
||||
</div>
|
||||
<div id="orderNoteStatus" class="alert" style="display: none; margin-top: 15px;">
|
||||
<i class="fas fa-info-circle"></i> <span id="orderNoteStatusMessage"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">
|
||||
<i class="fa fa-times"></i> Mégse
|
||||
</button>
|
||||
<button type="button" id="addOrderNoteBtn" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Jegyzet mentése
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
|
|
@ -614,5 +653,90 @@
|
|||
$("#notificationMessage").val("");
|
||||
});
|
||||
|
||||
// ========== ORDER NOTE MANAGEMENT ==========
|
||||
|
||||
var addOrderNoteUrl = '@Url.Action("AddOrderNote", "CustomOrder")';
|
||||
|
||||
// Add Order Note (in modal)
|
||||
$("#addOrderNoteBtn").click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
var noteText = $("#orderNoteText").val().trim();
|
||||
|
||||
if (!noteText) {
|
||||
showOrderNoteStatus("Kérjük, adjon meg egy jegyzetet!", "warning");
|
||||
return false;
|
||||
}
|
||||
|
||||
var btn = $(this);
|
||||
btn.prop("disabled", true).html('<i class="fas fa-spinner fa-spin"></i> Mentés...');
|
||||
|
||||
showOrderNoteStatus("Jegyzet mentése folyamatban...", "info");
|
||||
|
||||
// Add asterisk (*) at the beginning of the message
|
||||
var noteWithAsterisk = "* " + noteText;
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: addOrderNoteUrl,
|
||||
data: {
|
||||
orderId: @Model.OrderId,
|
||||
note: noteWithAsterisk,
|
||||
__RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function (response) {
|
||||
console.log("Order Note Response:", response);
|
||||
btn.prop("disabled", false).html('<i class="fas fa-save"></i> Jegyzet mentése');
|
||||
|
||||
if (response.success) {
|
||||
showOrderNoteStatus("Jegyzet sikeresen mentve!", "success");
|
||||
|
||||
// Close modal after 1.5 seconds
|
||||
setTimeout(function() {
|
||||
$('#addOrderNoteModal').modal('hide');
|
||||
$("#orderNoteText").val(""); // Clear the textbox
|
||||
}, 1500);
|
||||
} else {
|
||||
showOrderNoteStatus("Hiba: " + (response.message || "Ismeretlen hiba"), "danger");
|
||||
}
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error("Order Note AJAX Error:", xhr, status, error);
|
||||
btn.prop("disabled", false).html('<i class="fas fa-save"></i> Jegyzet mentése');
|
||||
|
||||
var errorMessage = "Hiba a jegyzet mentése közben";
|
||||
if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
errorMessage = xhr.responseJSON.message;
|
||||
} else if (xhr.responseText) {
|
||||
try {
|
||||
var errorObj = JSON.parse(xhr.responseText);
|
||||
errorMessage = errorObj.message || errorMessage;
|
||||
} catch (e) {
|
||||
errorMessage = "Hiba: " + xhr.status + " - " + xhr.statusText;
|
||||
}
|
||||
}
|
||||
showOrderNoteStatus(errorMessage, "danger");
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
function showOrderNoteStatus(message, type) {
|
||||
var statusDiv = $("#orderNoteStatus");
|
||||
statusDiv.removeClass("alert-info alert-success alert-warning alert-danger")
|
||||
.addClass("alert-" + type);
|
||||
$("#orderNoteStatusMessage").text(message);
|
||||
statusDiv.show();
|
||||
}
|
||||
|
||||
// Clear order note status when modal is closed
|
||||
$('#addOrderNoteModal').on('hidden.bs.modal', function () {
|
||||
$("#orderNoteStatus").hide();
|
||||
$("#orderNoteText").val("");
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue