Mango.Nop.Plugins/Nop.Plugin.Misc.AIPlugin/Views/Preorder/Index.cshtml

461 lines
21 KiB
Plaintext

@using System.Text.Encodings.Web
@{
Layout = "_Root";
ViewBag.Title = T("Plugins.Misc.FruitBankPlugin.Preorder.PageTitle").Text;
}
<link rel="stylesheet" href="~/Plugins/Misc.FruitBankPlugin/css/quick-order.css" />
<link rel="stylesheet" href="~/Plugins/Misc.FruitBankPlugin/css/preorder.css" />
<div class="quick-order-page">
<!-- ── STEP 1: Delivery date + time ─────────────────────────────────── -->
<div id="deliveryStep" class="qo-delivery-step">
<div class="ds-header">
<i class="fa fa-calendar"></i>
<div>
<div class="ds-title">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Title")</div>
<div class="ds-subtitle">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Subtitle")</div>
</div>
</div>
<div class="ds-body">
<div class="ds-section-label">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.DayLabel")</div>
<div class="ds-day-buttons" id="dayButtons"></div>
<div class="ds-section-label" style="margin-top:20px;">
@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeLabel")
</div>
<div class="ds-time-wrapper">
<input type="time" id="deliveryTimePicker" class="ds-time-input" value="08:00" min="05:00" max="22:00" />
<span class="ds-time-hint">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.TimeHint")</span>
</div>
</div>
<div class="ds-footer">
<button type="button" class="ds-confirm-btn" id="deliveryConfirmBtn" disabled>
<i class="fa fa-arrow-right"></i> @T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ConfirmButton")
</button>
</div>
</div>
<!-- ── Delivery chip ────────────────────────────────────────────────── -->
<div id="deliveryChip" class="qo-delivery-chip" style="display:none;">
<i class="fa fa-calendar-check-o"></i>
<span class="dc-label">@T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeLabel")</span>
<strong id="deliveryChipText"></strong>
<button type="button" class="dc-change-btn" id="deliveryChangeBtn">
<i class="fa fa-pencil"></i> @T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ChangeButton")
</button>
</div>
<!-- ── STEP 2: Product selection + submit ───────────────────────────── -->
<div id="mainContent" style="display:none;">
<!-- Info banner -->
<div class="po-info-banner">
<i class="fa fa-info-circle"></i>
@T("Plugins.Misc.FruitBankPlugin.Preorder.InfoBanner")
</div>
<div class="qo-layout">
<!-- LEFT: product list + note + submit -->
<div class="qo-products-panel">
<!-- Loading -->
<div id="productsLoadingState" class="products-empty-state">
<i class="fa fa-spinner fa-spin"></i>
<p>@T("Plugins.Misc.FruitBankPlugin.Preorder.LoadingProducts")</p>
</div>
<!-- No products -->
<div id="noProductsCard" class="no-results-card" style="display:none;">
<i class="fa fa-calendar-times-o"></i>
<p>@T("Plugins.Misc.FruitBankPlugin.Preorder.NoProductsAvailable")</p>
</div>
<!-- Product grid -->
<div id="productSection" style="display:none;">
<div class="matches-label">
<i class="fa fa-cubes"></i>
<span>@T("Plugins.Misc.FruitBankPlugin.Preorder.ProductsLabel")</span>
</div>
<div id="productGrid" class="product-grid"></div>
<!-- Customer note -->
<div class="po-note-section">
<label class="po-note-label" for="customerNote">
<i class="fa fa-comment-o"></i>
@T("Plugins.Misc.FruitBankPlugin.Preorder.NoteLabel")
</label>
<textarea id="customerNote" class="po-note-input"
placeholder="@T("Plugins.Misc.FruitBankPlugin.Preorder.NotePlaceholder")"
rows="3" maxlength="1000"></textarea>
</div>
<!-- Submit -->
<div class="po-submit-row">
<div id="selectionSummary" class="po-selection-summary"></div>
<button type="button" id="submitPreorderBtn" class="po-submit-btn" disabled>
<i class="fa fa-paper-plane"></i>
@T("Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton")
</button>
</div>
</div>
</div>
<!-- RIGHT: summary panel -->
<div class="qo-cart-panel">
<div class="qo-section-title">
<i class="fa fa-list-ul"></i>
@T("Plugins.Misc.FruitBankPlugin.Preorder.SummaryTitle")
<span id="itemCountBadge" class="cart-count-badge">0</span>
</div>
<div id="summaryEmpty" class="cart-empty">
<i class="fa fa-list-ul"></i>
<p>@T("Plugins.Misc.FruitBankPlugin.Preorder.SummaryEmpty")</p>
</div>
<div id="summaryList" class="cart-items-list" style="display:none;"></div>
<div id="summaryNote" class="cart-total-row" style="display:none;">
<div class="cart-total-note">
<i class="fa fa-info-circle"></i>
<small>@T("Plugins.Misc.FruitBankPlugin.Preorder.SummaryNote")</small>
</div>
</div>
</div>
</div>
</div>
<!-- ── SUCCESS STATE ─────────────────────────────────────────────────── -->
<div id="successState" style="display:none;" class="po-success-state">
<div class="po-success-icon"><i class="fa fa-check-circle"></i></div>
<h2>@T("Plugins.Misc.FruitBankPlugin.Preorder.SuccessTitle")</h2>
<p id="successMessage"></p>
<a href="@Url.RouteUrl("Homepage")" class="po-back-btn">
<i class="fa fa-home"></i> @T("Plugins.Misc.FruitBankPlugin.Preorder.BackToHome")
</a>
</div>
</div>
@Html.AntiForgeryToken()
<script asp-location="Footer">
var poStr = {
dsToday : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Today").Text))',
dsTomorrow : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Tomorrow").Text))',
dsSaving : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.Saving").Text))',
dsConfirm : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.DeliveryStep.ConfirmButton").Text))',
measurable : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.MeasurableBadge").Text))',
pricePerPc : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.PricePerPiece").Text))',
pieceUnit : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.PieceUnit").Text))',
stockLabel : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.StockLabel").Text))',
selNone : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SelectionNone").Text))',
selItems : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SelectionItems").Text))',
submitting : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.Submitting").Text))',
successMsg : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SuccessMessage").Text))',
errorPfx : '@Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.ErrorPrefix").Text))',
huDayNames : ['vas\u00e1rnap','h\u00e9tf\u0151','kedd','szerda','cs\u00fct\u00f6rt\u00f6k','p\u00e9ntek','szombat']
};
</script>
<script asp-location="Footer">
var selectedDeliveryDate = null;
var selectedDeliveryTime = null;
var selectedDayLabel = null;
var products = []; // loaded from server
var quantities = {}; // productId → quantity (0 = not selected)
// ── Init ──────────────────────────────────────────────────────────────────
$(document).ready(function () {
renderDayButtons();
$(document).on('click', '.ds-day-btn', function () {
$('.ds-day-btn').removeClass('selected');
$(this).addClass('selected');
selectedDeliveryDate = $(this).data('date');
selectedDayLabel = $(this).data('label');
checkDeliveryReady();
});
$('#deliveryTimePicker').on('input change', function () {
selectedDeliveryTime = $(this).val() || null;
checkDeliveryReady();
});
selectedDeliveryTime = $('#deliveryTimePicker').val() || null;
$('#deliveryConfirmBtn').click(confirmDelivery);
$('#deliveryChangeBtn').click(function () {
$('#deliveryChip').hide();
$('#mainContent').hide();
$('#deliveryStep').show();
});
$('#submitPreorderBtn').click(submitPreorder);
// Restore saved delivery datetime if revisiting
$.ajax({
url: '@Url.Action("GetDeliveryDateTime", "Preorder")',
type: 'GET',
success: function (result) {
if (!result.success || !result.hasValue) return;
selectedDeliveryDate = result.date;
selectedDeliveryTime = result.time;
var $btn = $('.ds-day-btn[data-date="' + result.date + '"]');
if ($btn.length) {
$btn.addClass('selected');
selectedDayLabel = $btn.data('label');
} else {
selectedDayLabel = result.date;
}
$('#deliveryTimePicker').val(result.time);
showMainContent();
}
});
});
// ── Delivery step ─────────────────────────────────────────────────────────
function renderDayButtons() {
var container = $('#dayButtons').empty();
var today = new Date();
for (var i = 0; i < 14; i++) { // 2-week window for preorders
var d = new Date(today);
d.setDate(today.getDate() + i);
var iso = d.toISOString().split('T')[0];
var dayName;
if (i === 0) dayName = poStr.dsToday;
else if (i === 1) dayName = poStr.dsTomorrow;
else dayName = poStr.huDayNames[d.getDay()];
var dateStr = (d.getMonth() + 1) + '. ' + d.getDate() + '.';
var btn = $('<button type="button" class="ds-day-btn">')
.attr('data-date', iso)
.attr('data-label', dayName + ' ' + dateStr)
.html('<span class="ds-day-name">' + dayName + '</span><span class="ds-day-date">' + dateStr + '</span>');
container.append(btn);
}
}
function checkDeliveryReady() {
$('#deliveryConfirmBtn').prop('disabled', !(selectedDeliveryDate && selectedDeliveryTime));
}
function confirmDelivery() {
var btn = $('#deliveryConfirmBtn');
btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> ' + poStr.dsSaving);
var deliveryDateTime = selectedDeliveryDate + 'T' + selectedDeliveryTime;
$.ajax({
url : '@Url.Action("SetDeliveryDateTime", "Preorder")',
type: 'POST',
data: {
deliveryDateTime: deliveryDateTime,
__RequestVerificationToken: $('input[name="__RequestVerificationToken"]').val()
},
success: function (result) {
if (!result.success) {
alert(poStr.errorPfx + (result.message || ''));
btn.prop('disabled', false).html('<i class="fa fa-arrow-right"></i> ' + poStr.dsConfirm);
return;
}
showMainContent();
},
error: function () {
btn.prop('disabled', false).html('<i class="fa fa-arrow-right"></i> ' + poStr.dsConfirm);
}
});
}
function showMainContent() {
var chipText = selectedDayLabel + ' \u2014 ' + selectedDeliveryTime;
$('#deliveryChipText').text(chipText);
$('#deliveryStep').hide();
$('#deliveryChip').show();
$('#mainContent').show();
loadProducts();
}
// ── Products ──────────────────────────────────────────────────────────────
function loadProducts() {
$('#productsLoadingState').show();
$('#noProductsCard').hide();
$('#productSection').hide();
$.ajax({
url : '@Url.Action("GetAvailableProducts", "Preorder")',
type: 'GET',
success: function (result) {
$('#productsLoadingState').hide();
if (!result.success || !result.products || result.products.length === 0) {
$('#noProductsCard').show();
return;
}
products = result.products;
quantities = {};
renderProducts();
$('#productSection').show();
},
error: function () {
$('#productsLoadingState').hide();
$('#noProductsCard').show();
}
});
}
function renderProducts() {
var grid = $('#productGrid').empty();
$.each(products, function (_, p) {
quantities[p.id] = quantities[p.id] || 0;
var priceHtml = p.isMeasurable
? '<span class="measurable-badge"><i class="fa fa-balance-scale"></i> ' + poStr.measurable + '</span>'
: (p.unitPrice > 0 ? '<span class="pm-price">' + fmt(p.unitPrice) + ' ' + poStr.pricePerPc + '</span>' : '');
var card = $('<div>').addClass('product-card po-product-card').attr('data-id', p.id);
card.html(
'<div class="pc-body">' +
'<div class="pc-name"><i class="fa fa-cube"></i> ' + p.name + '</div>' +
'<div class="pc-meta">' +
'<span class="pc-stock">' + poStr.stockLabel + ' ' + p.stockQuantity + ' ' + poStr.pieceUnit + '</span>' +
priceHtml +
'</div>' +
'</div>' +
'<div class="pc-actions">' +
'<div class="qty-stepper">' +
'<button type="button" class="qty-btn qty-minus" tabindex="-1"><i class="fa fa-minus"></i></button>' +
'<input type="number" class="qty-input po-qty" value="0" min="0" max="' + p.stockQuantity + '">' +
'<button type="button" class="qty-btn qty-plus" tabindex="-1"><i class="fa fa-plus"></i></button>' +
'</div>' +
'</div>'
);
card.find('.qty-minus').click(function () {
var inp = $(this).siblings('.qty-input');
var val = parseInt(inp.val()) || 0;
if (val > 0) { inp.val(val - 1); onQtyChange(p.id, val - 1, card); }
});
card.find('.qty-plus').click(function () {
var inp = $(this).siblings('.qty-input');
var val = parseInt(inp.val()) || 0;
if (val < p.stockQuantity) { inp.val(val + 1); onQtyChange(p.id, val + 1, card); }
});
card.find('.qty-input').on('input change blur', function () {
var val = parseInt($(this).val());
if (isNaN(val) || val < 0) val = 0;
if (val > p.stockQuantity) val = p.stockQuantity;
$(this).val(val);
onQtyChange(p.id, val, card);
});
grid.append(card);
});
}
function onQtyChange(productId, qty, $card) {
quantities[productId] = qty;
// Highlight selected cards
$card.toggleClass('po-selected', qty > 0);
updateSummary();
}
function updateSummary() {
var selectedItems = products.filter(function (p) { return (quantities[p.id] || 0) > 0; });
var count = selectedItems.length;
$('#itemCountBadge').text(count);
$('#submitPreorderBtn').prop('disabled', count === 0);
// Selection summary text
if (count === 0) {
$('#selectionSummary').text(poStr.selNone);
} else {
$('#selectionSummary').text(count + ' ' + poStr.selItems);
}
// Right panel
if (count === 0) {
$('#summaryEmpty').show();
$('#summaryList, #summaryNote').hide();
return;
}
$('#summaryEmpty').hide();
$('#summaryList, #summaryNote').show();
var list = $('#summaryList').empty();
var hasMeasurable = false;
$.each(selectedItems, function (_, p) {
var qty = quantities[p.id];
var priceHtml = p.isMeasurable
? '<span class="measurable-badge-sm"><i class="fa fa-balance-scale"></i></span>'
: '<strong class="line-total">' + fmt(p.unitPrice * qty) + ' Ft</strong>';
if (p.isMeasurable) hasMeasurable = true;
list.append(
'<div class="cart-item">' +
'<div class="ci-name">' + p.name + '</div>' +
'<div class="ci-details">' +
'<span class="ci-qty">' + qty + ' ' + poStr.pieceUnit + '</span>' +
priceHtml +
'</div>' +
'</div>'
);
});
if (hasMeasurable) $('#summaryNote').show();
else $('#summaryNote').hide();
}
// ── Submit ────────────────────────────────────────────────────────────────
function submitPreorder() {
var selectedItems = products.filter(function (p) { return (quantities[p.id] || 0) > 0; });
if (!selectedItems.length) return;
var btn = $('#submitPreorderBtn');
btn.prop('disabled', true).html('<i class="fa fa-spinner fa-spin"></i> ' + poStr.submitting);
var payload = {
deliveryDateTime : selectedDeliveryDate + 'T' + selectedDeliveryTime,
customerNote : $('#customerNote').val().trim(),
items : selectedItems.map(function (p) {
return { productId: p.id, quantity: quantities[p.id] };
})
};
$.ajax({
url : '@Url.Action("PlacePreorder", "Preorder")',
type : 'POST',
contentType: 'application/json',
data : JSON.stringify(payload),
headers : { 'RequestVerificationToken': $('input[name="__RequestVerificationToken"]').val() },
success : function (result) {
if (result.success) {
$('#mainContent, #deliveryChip').hide();
$('#successMessage').text(poStr.successMsg.replace('{0}', result.preorderId));
$('#successState').show();
} else {
alert(poStr.errorPfx + (result.message || ''));
btn.prop('disabled', false)
.html('<i class="fa fa-paper-plane"></i> @Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton").Text))');
}
},
error: function () {
alert(poStr.errorPfx);
btn.prop('disabled', false)
.html('<i class="fa fa-paper-plane"></i> @Html.Raw(JavaScriptEncoder.Default.Encode(T("Plugins.Misc.FruitBankPlugin.Preorder.SubmitButton").Text))');
}
});
}
function fmt(val) {
if (!val) return '—';
return Math.round(val).toLocaleString('hu-HU');
}
</script>