Browse Source

Shop basket progress

Vova Tkach 5 years ago
parent
commit
1d22e196a0

+ 14 - 3
assets/template/header_html_file.go

@@ -38,6 +38,17 @@ var VarHeaderHtmlFile = []byte(`<!doctype html>
 			var ShopOrderLabelEmailAddress = 'Email Address';
 			var ShopOrderLabelDelivery = 'Delivery';
 			var ShopOrderLabelComment = 'Comment';
+
+			var ShopOrderRequiredLastName = true;
+			var ShopOrderRequiredFirstName = true;
+			var ShopOrderRequiredSecondName = false;
+			var ShopOrderRequiredMobilePhone = false;
+			var ShopOrderRequiredEmailAddress = true;
+			var ShopOrderRequiredDelivery = false;
+			var ShopOrderRequiredComment = false;
+
+			var ShopOrderError = 'Error!';
+			var ShopOrderErrorBasketEmpty = 'Your basket is empty';
 			var ShopOrderEmptyLastName = 'Please enter your last name';
 			var ShopOrderEmptyFirstName = 'Please enter your first name';
 			var ShopOrderEmptySecondName = 'Please enter your second name';
@@ -45,9 +56,9 @@ var VarHeaderHtmlFile = []byte(`<!doctype html>
 			var ShopOrderEmptyEmailAddress = 'Please enter your email address';
 			var ShopOrderEmptyDelivery = 'Please enter delivery comment';
 			var ShopOrderEmptyComment = 'Please enter order comment';
-			var ShopOrderSuccess = 'Thank you for your order! We will call you shortly';
-			var ShopOrderErrorMobilePhone = 'Mobile phone number is invalid';
-			var ShopOrderErrorEmailAddress = 'Email address is invalid';
+			var ShopOrderSuccess = '<b>Thank you for your order! We will call you shortly!</b>';
+			// var ShopOrderErrorMobilePhone = 'Mobile phone number is invalid';
+			// var ShopOrderErrorEmailAddress = 'Email address is invalid';
 		</script>
 	</head>
 	<body id="body" class="fixed-top-bar">

+ 184 - 128
assets/template/scripts_js_file.go

@@ -136,6 +136,15 @@ var VarScriptsJsFile = []byte(`(function(window, $) {
 			$('#sys-modal-shop-basket button.btn-order').prop('disabled', total <= 0);
 		};
 
+		function ClearOrderFormErrorMessage() {
+			$('#sys-modal-shop-basket .modal-body .order-form .sys-messages').html('');
+			$('#sys-modal-shop-basket .modal-body .order-form .input-error-msg').css('display', 'none');
+		};
+
+		function ShowOrderFormErrorMessage(title, message) {
+			$('#sys-modal-shop-basket .modal-body .order-form .sys-messages').html('<div class="alert alert-danger alert-dismissible fade show" role="alert"><strong>' + title + '</strong> ' + message + '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>');
+		};
+
 		function Initialize() {
 			// Check if jQuery was loaded
 			if(typeof $ == 'function') {
@@ -180,80 +189,117 @@ var VarScriptsJsFile = []byte(`(function(window, $) {
 									<div class="blocker" style="position:absolute;left:0px;top:0px;width:100%;height:100%;background:#fff;opacity:0.5;display:none;"></div> \
 									<div class="data"></div> \
 									<div class="order-form mt-3" style="display:none;"> \
-									<hr class="mb-4"> \
-									<form class="data-form" action="/" method="post" autocomplete="off"> \
-										<div class="hidden"><input type="hidden" name="action" value="shop-order"></div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_last_name">' + ShopOrderLabelLastName + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_last_name" name="client_last_name" value="" minlength="1" maxlength="64" autocomplete="off" required> \
+										<hr class="mb-4"> \
+										<form class="data-form" action="/" method="post" autocomplete="off"> \
+											<div class="hidden"><input type="hidden" name="action" value="shop-order"></div> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_last_name">' + ShopOrderLabelLastName + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_last_name" name="client_last_name" minlength="1" maxlength="64" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyLastName + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_first_name">' + ShopOrderLabelFirstName + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_first_name" name="client_first_name" value="" minlength="1" maxlength="64" autocomplete="off" required> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_first_name">' + ShopOrderLabelFirstName + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_first_name" name="client_first_name" minlength="1" maxlength="64" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyFirstName + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_second_name">' + ShopOrderLabelSecondName + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_second_name" name="client_second_name" value="" minlength="1" maxlength="64" autocomplete="off" required> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_second_name">' + ShopOrderLabelSecondName + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_second_name" name="client_second_name" minlength="1" maxlength="64" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptySecondName + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_phone">' + ShopOrderLabelMobilePhone + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_phone" name="client_phone" value="" minlength="1" maxlength="20" autocomplete="off" required> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_phone">' + ShopOrderLabelMobilePhone + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_phone" name="client_phone" minlength="1" maxlength="20" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyMobilePhone + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_email">' + ShopOrderLabelEmailAddress + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_email" name="client_email" value="" minlength="1" maxlength="64" autocomplete="off" required> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_email">' + ShopOrderLabelEmailAddress + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_email" name="client_email" minlength="1" maxlength="64" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyEmailAddress + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_delivery_comment">' + ShopOrderLabelDelivery + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_delivery_comment" name="client_delivery_comment" value="" minlength="1" maxlength="255" autocomplete="off"> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_delivery_comment">' + ShopOrderLabelDelivery + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_delivery_comment" name="client_delivery_comment" minlength="1" maxlength="255" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyDelivery + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_order_comment">' + ShopOrderLabelComment + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<textarea class="form-control" id="lbl_client_order_comment" name="client_order_comment" autocomplete="off"></textarea> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_order_comment">' + ShopOrderLabelComment + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<textarea class="form-control" id="lbl_client_order_comment" name="client_order_comment" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"></textarea> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyComment + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-									</form> \
+											<button type="submit" style="display:none;"></button> \ \
+										</form> \
+										<div class="sys-messages"></div>\
 									</div> \
 								</div> \
 								<div class="modal-footer"> \
@@ -264,61 +310,10 @@ var VarScriptsJsFile = []byte(`(function(window, $) {
 						</div> \
 					</div>';
 					$('#sys-modal-shop-basket-placeholder').html(html);
-
-					// ---
-					// var OrderForm = $('#sys-modal-shop-basket .modal-body .order-form form');
-					// OrderForm.submit(function(e) {
-					// 	if(OrderForm.hasClass('loading')) {
-					// 		e.preventDefault();
-					// 		return;
-					// 	}
-
-					// 	// Block send button
-					// 	OrderForm.addClass('loading').addClass('alert-here');
-					// 	var button = OrderForm.find('button[type=submit]');
-					// 	button.addClass('progress-bar-striped')
-					// 		.addClass('progress-bar-animated');
-
-					// 	// Another button
-					// 	if(button.attr('data-target') != '') {
-					// 		$('#' + button.attr('data-target')).addClass('progress-bar-striped')
-					// 			.addClass('progress-bar-animated');
-					// 	}
-
-					// 	// Clear form messages
-					// 	form.find('.sys-messages').html('');
-
-					// 	$.ajax({
-					// 		type: "POST",
-					// 		url: form.attr('action'),
-					// 		data: form.serialize()
-					// 	}).done(function(data) {
-					// 		FormDataWasChanged = false;
-					// 		if(IsDebugMode()) console.log('done', data);
-					// 		AjaxDone(data)
-					// 	}).fail(function(xhr, status, error) {
-					// 		if(IsDebugMode()) console.log('fail', xhr, status, error);
-					// 		AjaxFail(xhr.responseText, status, error);
-					// 	}).always(function() {
-					// 		// Add delay for one second
-					// 		setTimeout(function() {
-					// 			form.removeClass('loading').removeClass('alert-here');
-					// 			button.removeClass('progress-bar-striped')
-					// 				.removeClass('progress-bar-animated');
-					// 			// Another button
-					// 			if(button.attr('data-target') != '') {
-					// 				$('#' + button.attr('data-target'))
-					// 					.removeClass('progress-bar-striped')
-					// 					.removeClass('progress-bar-animated');
-					// 			}
-					// 		}, 100);
-					// 	});
-
-					// 	// Prevent submit action
-					// 	e.preventDefault();
-					// });
-					// ---
-
+					$('#sys-modal-shop-basket .order-form form').submit(function(e) {
+						$('#sys-modal-shop-basket .modal-footer button.btn-success').click();
+						e.preventDefault();
+					});
 					$("#sys-modal-shop-basket").modal({
 						backdrop: 'static',
 						keyboard: true,
@@ -428,30 +423,91 @@ var VarScriptsJsFile = []byte(`(function(window, $) {
 					var OrderFormBlock = $('#sys-modal-shop-basket .modal-body .order-form');
 					if(OrderFormBlock.css('display') == 'none') {
 						OrderFormBlock.css('display', 'block');
-						setTimeout(function() { OrderFormBlock.find('input.form-control').first().focus(); }, 500);
+						setTimeout(function() { OrderFormBlock.find('input.form-control').first().focus(); }, 200);
 						return;
 					}
+					ClearOrderFormErrorMessage();
+					var OrderForm = $('#sys-modal-shop-basket .modal-body .order-form form');
 					// Validate
+					var ValidateError = false;
+					if(ShopOrderRequiredLastName && $.trim(OrderForm.find('input[name=client_last_name]').val()) == '') {
+						OrderForm.find('input[name=client_last_name]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_last_name]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredFirstName && $.trim(OrderForm.find('input[name=client_first_name]').val()) == '') {
+						OrderForm.find('input[name=client_first_name]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_first_name]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredSecondName && $.trim(OrderForm.find('input[name=client_second_name]').val()) == '') {
+						OrderForm.find('input[name=client_second_name]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_second_name]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredMobilePhone && $.trim(OrderForm.find('input[name=client_phone]').val()) == '') {
+						OrderForm.find('input[name=client_phone]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_phone]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredEmailAddress && $.trim(OrderForm.find('input[name=client_email]').val()) == '') {
+						OrderForm.find('input[name=client_email]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_email]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredDelivery && $.trim(OrderForm.find('input[name=client_delivery_comment]').val()) == '') {
+						OrderForm.find('input[name=client_delivery_comment]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_delivery_comment]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredComment && $.trim(OrderForm.find('textarea[name=client_order_comment]').val()) == '') {
+						OrderForm.find('textarea[name=client_order_comment]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('textarea[name=client_order_comment]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ValidateError) {
+						return;
+					}
 					// Send form
 					ShopBasketBlockObject(object);
-					var OrderForm = $('#sys-modal-shop-basket .modal-body .order-form form');
 					$.ajax({
 						type: "POST",
 						url: OrderForm.attr('action'),
 						data: OrderForm.serialize()
 					}).done(function(data) {
-						// FormDataWasChanged = false;
-						// if(IsDebugMode()) console.log('done', data);
-						// AjaxDone(data);
-						// ---
-						// ShopOrderSuccess
-						// ShopOrderErrorMobilePhone
-						// ShopOrderErrorEmailAddress
-						console.log('Order action done', data);
+						try {
+							jdata = JSON.parse(data);
+							if(jdata.error === true) {
+								ShowOrderFormErrorMessage(ShopOrderError, window[jdata.variable]);
+								if(jdata.field) {
+									setTimeout(function() { OrderForm.find('[name=' + jdata.field + ']').first().focus(); }, 200);
+									OrderForm.find('[name=' + jdata.field + ']').parent().parent().find('.input-error-msg').css('display', 'inline');
+								}
+							} else {
+								ShopBasketSetNavBtnProductsCount(0);
+								OrderFormBlock.css('display', 'none');
+								$('#sys-modal-shop-basket .modal-body .data').html(window[jdata.variable]);
+								$('#sys-modal-shop-basket button.btn-order').prop('disabled', true);
+							}
+						} catch(e) {
+							ShowOrderFormErrorMessage(ShopOrderError, e.message);
+						}
 					}).fail(function(xhr, status, error) {
-						// if(IsDebugMode()) console.log('fail', xhr, status, error);
-						// AjaxFail(xhr.responseText, status, error);
-						console.log('Order action fail', xhr, status, error);
+						ShowOrderFormErrorMessage(ShopOrderError, error);
 					}).always(function() {
 						ShopBasketUnBlockObject(object);
 					});

+ 4 - 0
assets/template/styles_css_file.go

@@ -356,6 +356,10 @@ footer {
 	margin-bottom: .45rem;
 }
 
+#sys-modal-shop-basket .order-form .form-group .input-error-msg small {
+	color: #721c24;
+}
+
 @media (max-width: 768px) {
 	#sys-modal-shop-basket .data .table td {
 		display: block;

+ 2 - 0
engine/basket/session.go

@@ -351,6 +351,8 @@ func (this *session) Remove(p *SBParam, product_id int) {
 
 func (this *session) ClearBasket(p *SBParam) {
 	this.Products = map[int]*product{}
+	this.updateProducts(p.DB)
+	this.updateTotals(p)
 }
 
 func (this *session) ProductsCount() int {

+ 14 - 3
hosts/localhost/template/header.html

@@ -36,6 +36,17 @@
 			var ShopOrderLabelEmailAddress = 'Email Address';
 			var ShopOrderLabelDelivery = 'Delivery';
 			var ShopOrderLabelComment = 'Comment';
+
+			var ShopOrderRequiredLastName = true;
+			var ShopOrderRequiredFirstName = true;
+			var ShopOrderRequiredSecondName = false;
+			var ShopOrderRequiredMobilePhone = false;
+			var ShopOrderRequiredEmailAddress = true;
+			var ShopOrderRequiredDelivery = false;
+			var ShopOrderRequiredComment = false;
+
+			var ShopOrderError = 'Error!';
+			var ShopOrderErrorBasketEmpty = 'Your basket is empty';
 			var ShopOrderEmptyLastName = 'Please enter your last name';
 			var ShopOrderEmptyFirstName = 'Please enter your first name';
 			var ShopOrderEmptySecondName = 'Please enter your second name';
@@ -43,9 +54,9 @@
 			var ShopOrderEmptyEmailAddress = 'Please enter your email address';
 			var ShopOrderEmptyDelivery = 'Please enter delivery comment';
 			var ShopOrderEmptyComment = 'Please enter order comment';
-			var ShopOrderSuccess = 'Thank you for your order! We will call you shortly';
-			var ShopOrderErrorMobilePhone = 'Mobile phone number is invalid';
-			var ShopOrderErrorEmailAddress = 'Email address is invalid';
+			var ShopOrderSuccess = '<b>Thank you for your order! We will call you shortly!</b>';
+			// var ShopOrderErrorMobilePhone = 'Mobile phone number is invalid';
+			// var ShopOrderErrorEmailAddress = 'Email address is invalid';
 		</script>
 	</head>
 	<body id="body" class="fixed-top-bar">

+ 184 - 73
hosts/localhost/template/scripts.js

@@ -134,6 +134,15 @@
 			$('#sys-modal-shop-basket button.btn-order').prop('disabled', total <= 0);
 		};
 
+		function ClearOrderFormErrorMessage() {
+			$('#sys-modal-shop-basket .modal-body .order-form .sys-messages').html('');
+			$('#sys-modal-shop-basket .modal-body .order-form .input-error-msg').css('display', 'none');
+		};
+
+		function ShowOrderFormErrorMessage(title, message) {
+			$('#sys-modal-shop-basket .modal-body .order-form .sys-messages').html('<div class="alert alert-danger alert-dismissible fade show" role="alert"><strong>' + title + '</strong> ' + message + '<button type="button" class="close" data-dismiss="alert" aria-label="Close"><span aria-hidden="true">&times;</span></button></div>');
+		};
+
 		function Initialize() {
 			// Check if jQuery was loaded
 			if(typeof $ == 'function') {
@@ -178,80 +187,117 @@
 									<div class="blocker" style="position:absolute;left:0px;top:0px;width:100%;height:100%;background:#fff;opacity:0.5;display:none;"></div> \
 									<div class="data"></div> \
 									<div class="order-form mt-3" style="display:none;"> \
-									<hr class="mb-4"> \
-									<form class="data-form" action="/" method="post" autocomplete="off"> \
-										<div class="hidden"><input type="hidden" name="action" value="shop-order"></div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_last_name">' + ShopOrderLabelLastName + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_last_name" name="client_last_name" value="" minlength="1" maxlength="64" autocomplete="off" required> \
+										<hr class="mb-4"> \
+										<form class="data-form" action="/" method="post" autocomplete="off"> \
+											<div class="hidden"><input type="hidden" name="action" value="shop-order"></div> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_last_name">' + ShopOrderLabelLastName + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_last_name" name="client_last_name" minlength="1" maxlength="64" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyLastName + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_first_name">' + ShopOrderLabelFirstName + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_first_name" name="client_first_name" value="" minlength="1" maxlength="64" autocomplete="off" required> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_first_name">' + ShopOrderLabelFirstName + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_first_name" name="client_first_name" minlength="1" maxlength="64" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyFirstName + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_second_name">' + ShopOrderLabelSecondName + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_second_name" name="client_second_name" value="" minlength="1" maxlength="64" autocomplete="off" required> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_second_name">' + ShopOrderLabelSecondName + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_second_name" name="client_second_name" minlength="1" maxlength="64" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptySecondName + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_phone">' + ShopOrderLabelMobilePhone + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_phone" name="client_phone" value="" minlength="1" maxlength="20" autocomplete="off" required> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_phone">' + ShopOrderLabelMobilePhone + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_phone" name="client_phone" minlength="1" maxlength="20" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyMobilePhone + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_email">' + ShopOrderLabelEmailAddress + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_email" name="client_email" value="" minlength="1" maxlength="64" autocomplete="off" required> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_email">' + ShopOrderLabelEmailAddress + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_email" name="client_email" minlength="1" maxlength="64" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyEmailAddress + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_delivery_comment">' + ShopOrderLabelDelivery + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<input class="form-control" type="text" id="lbl_client_delivery_comment" name="client_delivery_comment" value="" minlength="1" maxlength="255" autocomplete="off"> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_delivery_comment">' + ShopOrderLabelDelivery + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<input class="form-control" type="text" id="lbl_client_delivery_comment" name="client_delivery_comment" minlength="1" maxlength="255" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyDelivery + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-										<div class="form-group"> \
-											<div class="row"> \
-												<div class="col-md-3"> \
-													<label for="lbl_client_order_comment">' + ShopOrderLabelComment + '</label> \
-												</div> \
-												<div class="col-md-9"> \
-													<textarea class="form-control" id="lbl_client_order_comment" name="client_order_comment" autocomplete="off"></textarea> \
+											<div class="form-group"> \
+												<div class="row"> \
+													<div class="col-md-3"> \
+														<label for="lbl_client_order_comment">' + ShopOrderLabelComment + '</label> \
+													</div> \
+													<div class="col-md-9"> \
+														<div> \
+															<textarea class="form-control" id="lbl_client_order_comment" name="client_order_comment" autocomplete="off" onkeydown="$(this).parent().parent().find(\'.input-error-msg\').css(\'display\', \'none\');"></textarea> \
+														</div> \
+														<div class="input-error-msg" style="display:none;"> \
+															<small>' + ShopOrderEmptyComment + '</small> \
+														</div> \
+													</div> \
 												</div> \
 											</div> \
-										</div> \
-									</form> \
+											<button type="submit" style="display:none;"></button> \ \
+										</form> \
+										<div class="sys-messages"></div>\
 									</div> \
 								</div> \
 								<div class="modal-footer"> \
@@ -262,6 +308,10 @@
 						</div> \
 					</div>';
 					$('#sys-modal-shop-basket-placeholder').html(html);
+					$('#sys-modal-shop-basket .order-form form').submit(function(e) {
+						$('#sys-modal-shop-basket .modal-footer button.btn-success').click();
+						e.preventDefault();
+					});
 					$("#sys-modal-shop-basket").modal({
 						backdrop: 'static',
 						keyboard: true,
@@ -371,30 +421,91 @@
 					var OrderFormBlock = $('#sys-modal-shop-basket .modal-body .order-form');
 					if(OrderFormBlock.css('display') == 'none') {
 						OrderFormBlock.css('display', 'block');
-						setTimeout(function() { OrderFormBlock.find('input.form-control').first().focus(); }, 500);
+						setTimeout(function() { OrderFormBlock.find('input.form-control').first().focus(); }, 200);
 						return;
 					}
+					ClearOrderFormErrorMessage();
+					var OrderForm = $('#sys-modal-shop-basket .modal-body .order-form form');
 					// Validate
+					var ValidateError = false;
+					if(ShopOrderRequiredLastName && $.trim(OrderForm.find('input[name=client_last_name]').val()) == '') {
+						OrderForm.find('input[name=client_last_name]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_last_name]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredFirstName && $.trim(OrderForm.find('input[name=client_first_name]').val()) == '') {
+						OrderForm.find('input[name=client_first_name]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_first_name]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredSecondName && $.trim(OrderForm.find('input[name=client_second_name]').val()) == '') {
+						OrderForm.find('input[name=client_second_name]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_second_name]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredMobilePhone && $.trim(OrderForm.find('input[name=client_phone]').val()) == '') {
+						OrderForm.find('input[name=client_phone]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_phone]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredEmailAddress && $.trim(OrderForm.find('input[name=client_email]').val()) == '') {
+						OrderForm.find('input[name=client_email]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_email]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredDelivery && $.trim(OrderForm.find('input[name=client_delivery_comment]').val()) == '') {
+						OrderForm.find('input[name=client_delivery_comment]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('input[name=client_delivery_comment]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ShopOrderRequiredComment && $.trim(OrderForm.find('textarea[name=client_order_comment]').val()) == '') {
+						OrderForm.find('textarea[name=client_order_comment]').parent().parent().find('.input-error-msg').css('display', 'inline');
+						if(!ValidateError) {
+							setTimeout(function() { OrderForm.find('textarea[name=client_order_comment]').first().focus(); }, 200);
+						}
+						ValidateError = true;
+					}
+					if(ValidateError) {
+						return;
+					}
 					// Send form
 					ShopBasketBlockObject(object);
-					var OrderForm = $('#sys-modal-shop-basket .modal-body .order-form form');
 					$.ajax({
 						type: "POST",
 						url: OrderForm.attr('action'),
 						data: OrderForm.serialize()
 					}).done(function(data) {
-						// FormDataWasChanged = false;
-						// if(IsDebugMode()) console.log('done', data);
-						// AjaxDone(data);
-						// ---
-						// ShopOrderSuccess
-						// ShopOrderErrorMobilePhone
-						// ShopOrderErrorEmailAddress
-						console.log('Order action done', data);
+						try {
+							jdata = JSON.parse(data);
+							if(jdata.error === true) {
+								ShowOrderFormErrorMessage(ShopOrderError, window[jdata.variable]);
+								if(jdata.field) {
+									setTimeout(function() { OrderForm.find('[name=' + jdata.field + ']').first().focus(); }, 200);
+									OrderForm.find('[name=' + jdata.field + ']').parent().parent().find('.input-error-msg').css('display', 'inline');
+								}
+							} else {
+								ShopBasketSetNavBtnProductsCount(0);
+								OrderFormBlock.css('display', 'none');
+								$('#sys-modal-shop-basket .modal-body .data').html(window[jdata.variable]);
+								$('#sys-modal-shop-basket button.btn-order').prop('disabled', true);
+							}
+						} catch(e) {
+							ShowOrderFormErrorMessage(ShopOrderError, e.message);
+						}
 					}).fail(function(xhr, status, error) {
-						// if(IsDebugMode()) console.log('fail', xhr, status, error);
-						// AjaxFail(xhr.responseText, status, error);
-						console.log('Order action fail', xhr, status, error);
+						ShowOrderFormErrorMessage(ShopOrderError, error);
 					}).always(function() {
 						ShopBasketUnBlockObject(object);
 					});

+ 4 - 0
hosts/localhost/template/styles.css

@@ -354,6 +354,10 @@ footer {
 	margin-bottom: .45rem;
 }
 
+#sys-modal-shop-basket .order-form .form-group .input-error-msg small {
+	color: #721c24;
+}
+
 @media (max-width: 768px) {
 	#sys-modal-shop-basket .data .table td {
 		display: block;

+ 66 - 10
modules/module_shop_order.go

@@ -1,8 +1,10 @@
 package modules
 
 import (
+	"strings"
+
+	"golang-fave/engine/basket"
 	"golang-fave/engine/wrapper"
-	// "golang-fave/utils"
 )
 
 func (this *Modules) RegisterAction_ShopOrder() *Action {
@@ -11,17 +13,71 @@ func (this *Modules) RegisterAction_ShopOrder() *Action {
 		Mount:     "shop-order",
 		WantAdmin: false,
 	}, func(wrap *wrapper.Wrapper) {
-		// pf_id := wrap.R.FormValue("id")
+		// SBParam := basket.SBParam{
+		// 	R:         wrap.R,
+		// 	DB:        wrap.DB,
+		// 	Host:      wrap.CurrHost,
+		// 	Config:    wrap.Config,
+		// 	SessionId: wrap.GetSessionId(),
+		// }
+		if wrap.ShopBasket.ProductsCount(&basket.SBParam{
+			R:         wrap.R,
+			DB:        wrap.DB,
+			Host:      wrap.CurrHost,
+			Config:    wrap.Config,
+			SessionId: wrap.GetSessionId(),
+		}) <= 0 {
+			wrap.Write(`{"error": true, "variable": "ShopOrderErrorBasketEmpty"}`)
+			return
+		}
+
+		pf_client_last_name := wrap.R.FormValue("client_last_name")
+		pf_client_first_name := wrap.R.FormValue("client_first_name")
+		pf_client_second_name := wrap.R.FormValue("client_second_name")
+		pf_client_phone := wrap.R.FormValue("client_phone")
+		pf_client_email := wrap.R.FormValue("client_email")
+		pf_client_delivery_comment := wrap.R.FormValue("client_delivery_comment")
+		pf_client_order_comment := wrap.R.FormValue("client_order_comment")
+
+		if strings.TrimSpace(pf_client_last_name) == "" {
+			wrap.Write(`{"error": true, "field": "client_last_name", "variable": "ShopOrderEmptyLastName"}`)
+			return
+		}
+		if strings.TrimSpace(pf_client_first_name) == "" {
+			wrap.Write(`{"error": true, "field": "client_first_name", "variable": "ShopOrderEmptyFirstName"}`)
+			return
+		}
+		if strings.TrimSpace(pf_client_second_name) == "" {
+			wrap.Write(`{"error": true, "field": "client_second_name", "variable": "ShopOrderEmptySecondName"}`)
+			return
+		}
+		if strings.TrimSpace(pf_client_phone) == "" {
+			wrap.Write(`{"error": true, "field": "client_phone", "variable": "ShopOrderEmptyMobilePhone"}`)
+			return
+		}
+		if strings.TrimSpace(pf_client_email) == "" {
+			wrap.Write(`{"error": true, "field": "client_email", "variable": "ShopOrderEmptyEmailAddress"}`)
+			return
+		}
+		if strings.TrimSpace(pf_client_delivery_comment) == "" {
+			wrap.Write(`{"error": true, "field": "client_delivery_comment", "variable": "ShopOrderEmptyDelivery"}`)
+			return
+		}
+		if strings.TrimSpace(pf_client_order_comment) == "" {
+			wrap.Write(`{"error": true, "field": "client_order_comment", "variable": "ShopOrderEmptyComment"}`)
+			return
+		}
 
-		// pf_client_last_name := wrap.R.FormValue("client_last_name")
-		// pf_client_first_name := wrap.R.FormValue("client_first_name")
-		// pf_client_second_name := wrap.R.FormValue("client_second_name")
-		// pf_client_phone := wrap.R.FormValue("client_phone")
-		// pf_client_email := wrap.R.FormValue("client_email")
-		// pf_client_delivery_comment := wrap.R.FormValue("client_delivery_comment")
-		// pf_client_order_comment := wrap.R.FormValue("client_order_comment")
+		// Clear user basket
+		wrap.ShopBasket.ClearBasket(&basket.SBParam{
+			R:         wrap.R,
+			DB:        wrap.DB,
+			Host:      wrap.CurrHost,
+			Config:    wrap.Config,
+			SessionId: wrap.GetSessionId(),
+		})
 
-		wrap.MsgError(`OK!`)
+		wrap.Write(`{"error": false, "field": "", "variable": "ShopOrderSuccess"}`)
 		return
 
 		// if !utils.IsNumeric(pf_id) {