461 lines
21 KiB
Plaintext
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>
|