ildar-shaymukhametov_grandnode-refactorings/Grand.Web/Views/Product/_ProductAttributes.cshtml
@model IList<ProductDetailsModel.ProductAttributeModel>
@using System.Text;
@using Grand.Services.Media;
@using Grand.Domain.Media
@inject IDownloadService downloadService
@inject CatalogSettings catalogSettings
<div class="attributes">
    <table class="table">
        @foreach (var attribute in Model)
        {
            string controlId = string.Format("product_attribute_{0}", attribute.Id);
            string textPrompt = !string.IsNullOrEmpty(attribute.TextPrompt) ? attribute.TextPrompt : attribute.Name;
            <tr>
                <td class="d-inline-flex w-100" id="@string.Format("product_attribute_label_{0}", attribute.Id)">
                    <span class="text-prompt">
                        @textPrompt
                    </span>
                    @if (attribute.IsRequired)
                    {
                        <span class="pl-1 required">*</span>
                    }
                    @if (!string.IsNullOrEmpty(attribute.Description))
                    {
                        <span class="attribute-description">
                            @Html.Raw(attribute.Description)
                        </span>
                    }
                </td>
                <td id="@string.Format("product_attribute_input_{0}", attribute.Id)">
                    @switch (attribute.AttributeControlType)
                    {
                        case AttributeControlType.DropdownList:
                            {
                                <label for="@(controlId)" class="sr-only">@textPrompt</label>
                                <select class="custom-select form-control" name="@(controlId)" id="@(controlId)">
                                    @if (!attribute.IsRequired)
                                    {
                                        <option value="0">---</option>
                                    }
                                    @foreach (var attributeValue in attribute.Values)
                                    {
                                        var attributeName = String.IsNullOrEmpty(attributeValue.PriceAdjustment) ?
                                            attributeValue.Name :
                                            T("Products.ProductAttributes.PriceAdjustment", attributeValue.Name, attributeValue.PriceAdjustment).Text;
                                        <option selected="@attributeValue.IsPreSelected" value="@attributeValue.Id">@attributeName</option>
                                    }
                                </select>
                            }
                            break;
                        case AttributeControlType.RadioList:
                            {
                                <ul>
                                    @foreach (var attributeValue in attribute.Values)
                                    {
                                        var attributeName = String.IsNullOrEmpty(attributeValue.PriceAdjustment) ?
                                            attributeValue.Name :
                                            T("Products.ProductAttributes.PriceAdjustment", attributeValue.Name, attributeValue.PriceAdjustment).Text;
                                        <li>
                                            <label class="custom-control custom-radio">
                                                <input class="custom-control-input" id="@(controlId)_@(attributeValue.Id)" type="radio" name="@(controlId)" value="@attributeValue.Id" checked="@attributeValue.IsPreSelected" />
                                                <span class="custom-control-label"></span>
                                                <span class="custom-control-description">@attributeName</span>
                                            </label>
                                        </li>
                                    }
                                </ul>
                            }
                            break;
                        case AttributeControlType.Checkboxes:
                        case AttributeControlType.ReadonlyCheckboxes:
                            {
                                <ul>
                                    @foreach (var attributeValue in attribute.Values)
                                    {
                                        var attributeName = String.IsNullOrEmpty(attributeValue.PriceAdjustment) ?
                                            attributeValue.Name :
                                            T("Products.ProductAttributes.PriceAdjustment", attributeValue.Name, attributeValue.PriceAdjustment).Text;
                                        <li>
                                            <label class="custom-control custom-checkbox">
                                                <input class="custom-control-input" id="@(controlId)_@(attributeValue.Id)" type="checkbox" name="@(controlId)" value="@attributeValue.Id" checked="@attributeValue.IsPreSelected" @(attribute.AttributeControlType == AttributeControlType.ReadonlyCheckboxes ? Html.Raw(" disabled=\"disabled\"") : null) />
                                                <span class="custom-control-label"></span>
                                                <span class="sr-only">@attributeName</span>
                                                <span class="custom-control-description">@attributeName</span>
                                            </label>
                                        </li>
                                    }
                                </ul>
                            }
                            break;
                        case AttributeControlType.TextBox:
                            {
                                <input name="@(controlId)" type="text" class="form-control textbox" id="@(controlId)" value="@(attribute.DefaultValue) " />
                            }
                            break;
                        case AttributeControlType.MultilineTextbox:
                            {
                                <textarea class="form-control" cols="20" id="@(controlId)" name="@(controlId)">@(attribute.DefaultValue)</textarea>
                            }
                            break;
                        case AttributeControlType.Datepicker:
                            {
                                <date-picker-dropdown control-day="@(controlId + "_day")"
                                                      control-month="@(controlId + "_month")"
                                                      control-year="@(controlId + "_year")"
                                                      begin-year="@(DateTime.Now.Year)"
                                                      end-year="@(DateTime.Now.Year + 1)"
                                                      selected-day="(attribute.SelectedDay.HasValue ? attribute.SelectedDay.Value : 0)"
                                                      selected-month="(attribute.SelectedMonth.HasValue ? attribute.SelectedMonth.Value : 0)"
                                                      selected-year="(attribute.SelectedYear.HasValue ? attribute.SelectedYear.Value : 0)"
                                                      class="col-12 input-group" />
                            }
                            break;
                        case AttributeControlType.FileUpload:
                            {
                                Download download = null;
                                if (!String.IsNullOrEmpty(attribute.DefaultValue))
                                {
                                    download = await downloadService.GetDownloadByGuid(new Guid(attribute.DefaultValue));
                                }

                                //register CSS and JS
                                @inject IPageHeadBuilder pagebuilder
                                pagebuilder.AddCssFileParts(ResourceLocation.Head, "~/scripts/fineuploader/fineuploader-4.2.2.min.css");
                                pagebuilder.AddScriptParts(ResourceLocation.Footer, "~/scripts/fineuploader/jquery.fineuploader-4.2.2.min.js");

                                //ex. ['jpg', 'jpeg', 'png', 'gif'] or []
                                var allowedFileExtensions = string.Join(", ", attribute.AllowedFileExtensions.Select(x => "'" + x.Trim() + "'").ToList());

                                if (download != null)
                                {
                                    <input id="@(controlId)" name="@(controlId)" type="hidden" value="@download.DownloadGuid" />
                                }
                                else
                                {
                                    <input id="@(controlId)" name="@(controlId)" type="hidden" />
                                }

                                @*fine uploader container*@
                                <div id="@(controlId)uploader"></div>
                                @*fine uploader template (keep it synchronized to \content\fineuploader\templates\default.html)*@
                                <script type="text/template" id="@(controlId)-qq-template">
                                    <div class="qq-uploader-selector qq-uploader">
                                        <div class="qq-upload-drop-area-selector qq-upload-drop-area" qq-hide-dropzone>
                                            <span>@T("Common.FileUploader.DropFiles")</span>
                                        </div>
                                        <div class="qq-upload-button-selector qq-upload-button">
                                            <div>@T("Common.FileUploader.Upload")</div>
                                        </div>
                                        <span class="qq-drop-processing-selector qq-drop-processing">
                                            <span>@T("Common.FileUploader.Processing")</span>
                                            <span class="qq-drop-processing-spinner-selector qq-drop-processing-spinner"></span>
                                        </span>
                                        <ul class="qq-upload-list-selector qq-upload-list">
                                            <li>
                                                <div class="qq-progress-bar-container-selector">
                                                    <div class="qq-progress-bar-selector qq-progress-bar"></div>
                                                </div>
                                                <span class="qq-upload-spinner-selector qq-upload-spinner"></span>
                                                <span class="qq-edit-filename-icon-selector qq-edit-filename-icon"></span>
                                                <span class="qq-upload-file-selector qq-upload-file"></span>
                                                <input class="qq-edit-filename-selector qq-edit-filename" tabindex="0" type="text" />
                                                <span class="qq-upload-size-selector qq-upload-size"></span>
                                                <a class="qq-upload-cancel-selector qq-upload-cancel" href="#">@T("Common.FileUploader.Cancel")</a>
                                                <a class="qq-upload-retry-selector qq-upload-retry" href="#">@T("Common.FileUploader.Retry")</a>
                                                <a class="qq-upload-delete-selector qq-upload-delete" href="#">@T("Common.FileUploader.Delete")</a>
                                                <span class="qq-upload-status-text-selector qq-upload-status-text"></span>
                                            </li>
                                        </ul>
                                    </div>
                                </script>
                                <script asp-location="Footer">
                                        $(document).ready(function() {
                                            $("#@(controlId)uploader").fineUploader({
                                                request: {
                                                    endpoint: '@(Url.RouteUrl("UploadFileProductAttribute", new { attributeId = attribute.Id, productId = attribute.ProductId }))'
                                                },
                                                template: "@(controlId)-qq-template",
                                                multiple: false,
                                                validation: {
                                                    allowedExtensions: [@Html.Raw(allowedFileExtensions)]
                                                }
                                            }).on("complete", function(event, id, name, responseJSON, xhr) {
                                                $("#@(controlId)").val(responseJSON.downloadGuid);
                                                if (responseJSON.success) {
                                                    $("#@(controlId + "downloadurl")").html("<a href='" + responseJSON.downloadUrl + "'>@T("Common.FileUploader.DownloadUploadedFile")</a>");
                                                    $("#@(controlId + "remove")").show();
                                                }
                                                if (responseJSON.message) {
                                                    alert(responseJSON.message);
                                                }
                                            });

                                            $("#@(controlId + "remove")").click(function(e) {
                                                $("#@(controlId + "downloadurl")").html("");
                                                $("#@(controlId)").val('');
                                                $(this).hide();
                                            });
                                        });
                                </script>
                                <div id="@(controlId + "downloadurl")">
                                    @if (download != null)
                                    {
                                        <a href="@(Url.Action("GetFileUpload", "Download", new { downloadId = download.DownloadGuid }))" class="download-uploaded-file">@T("Common.FileUploader.DownloadUploadedFile")</a>
                                    }
                                </div>
                                <div>
                                    @if (download != null)
                                    {
                                        <a id="@(controlId + "remove")" class="remove-download-button">@T("Common.FileUploader.RemoveDownload")</a>
                                    }
                                    else
                                    {
                                        <a id="@(controlId + "remove")" class="remove-download-buttonn" style="display: none;">@T("Common.FileUploader.RemoveDownload")</a>
                                    }
                                </div>

                            }
                            break;
                        case AttributeControlType.ColorSquares:
                            {
                                <ul class="list-inline option-list color-squares pl-0" id="color-squares-@(attribute.Id)">
                                    @foreach (var attributeValue in attribute.Values)
                                    {
                                        var attributeName = String.IsNullOrEmpty(attributeValue.PriceAdjustment) ?
                                            attributeValue.Name :
                                            T("Products.ProductAttributes.PriceAdjustment", attributeValue.Name, attributeValue.PriceAdjustment).Text;
                                        <li @(attributeValue.IsPreSelected ? @Html.Raw(" class=\"selected-value list-inline-item\"") : @Html.Raw(" class=\"list-inline-item\""))>
                                            <label>
                                                <span class="color-container" title="@attributeName">
                                                    <span class="color" style="background-color:@(attributeValue.ColorSquaresRgb);">&nbsp;</span>
                                                </span>
                                                <input id="@(controlId)_@(attributeValue.Id)" type="radio" name="@(controlId)" value="@attributeValue.Id" checked="@attributeValue.IsPreSelected" />
                                            </label>
                                        </li>
                                    }
                                </ul>
                                <script asp-location="Footer">
                                        $(document).ready(function() {
                                            $('.attributes #color-squares-@(attribute.Id)').delegate('input', 'click', function(event) {
                                                $('.attributes #color-squares-@(attribute.Id)').find('li').removeClass('selected-value');
                                                $(this).closest('li').addClass('selected-value');
                                            });
                                        });
                                </script>
                            }
                            break;


                        case AttributeControlType.ImageSquares:
                            {
                                <ul class="option-list color-squares d-inline-flex align-items-center pl-0" id="color-squares-@(attribute.Id)">
                                    @foreach (var attributeValue in attribute.Values)
                                    {
                                        var attributeName = String.IsNullOrEmpty(attributeValue.PriceAdjustment) ?
                                            attributeValue.Name :
                                            T("Products.ProductAttributes.PriceAdjustment", attributeValue.Name, attributeValue.PriceAdjustment).Text;
                                        <li class="mr-2" @(attributeValue.IsPreSelected ? @Html.Raw(" class=\"selected-value mr-2\"") : null)>
                                            <label class="mb-0" data-toggle="tooltip" title="<img src='@(attributeValue.ImageSquaresPictureModel.FullSizeImageUrl)' alt='@attributeName' />">
                                                <span class="color-container">
                                                    <span class="color" style="background: url('@(attributeValue.ImageSquaresPictureModel.ImageUrl)') 50% 50% no-repeat;">&nbsp;</span>
                                                </span>
                                                <input id="@(controlId)_@(attributeValue.Id)" type="radio" name="@(controlId)" value="@attributeValue.Id" checked="@attributeValue.IsPreSelected" />
                                            </label>
                                            <script asp-location="Footer">
                                                $('[data-toggle="tooltip"]').tooltip({
                                                    animated: 'fade',
                                                    placement: 'right',
                                                    html: true
                                                });
                                            </script>
                                        </li>
                                    }
                                </ul>
                                <script asp-location="Footer">
	                                    $(document).ready(function() {
	                                        $('.attributes #color-squares-@(attribute.Id)').delegate('input', 'click', function(event) {
	                                            $('.attributes #color-squares-@(attribute.Id)').find('li').removeClass('selected-value');
	                                            $(this).closest('li').addClass('selected-value');
	                                        });
	                                    });
                                </script>
                            }
                            break;
                    }
                </td>
            </tr>
        }
    </table>
</div>

@{
    //dynamic update support
    var attributesHaveConditions = Model.Any(x => x.HasCondition);
    var attributesHaveAssociatedPictures = true;
    var dynamicPriceUpdate = catalogSettings.AjaxProcessAttributeChange;
    var attributeChangeScriptsBuilder = new StringBuilder();
    var productId = Model.First().ProductId;
    var attributeChangeHandlerFuncName = string.Format("attribute_change_handler_{0}", productId);
    if (dynamicPriceUpdate)
    {
        //generate change event script
        foreach (var attribute in Model)
        {
            string controlId = string.Format("product_attribute_{0}", attribute.Id);
            switch (attribute.AttributeControlType)
            {
                case AttributeControlType.DropdownList:
                    {
                        attributeChangeScriptsBuilder.AppendFormat("$('#{0}').change(function(){{{1}();}});\n", controlId, attributeChangeHandlerFuncName);
                    }
                    break;
                case AttributeControlType.RadioList:
                case AttributeControlType.ColorSquares:
                case AttributeControlType.ImageSquares:
                    {
                        foreach (var attributeValue in attribute.Values)
                        {
                            attributeChangeScriptsBuilder.AppendFormat("$('#{0}_{1}').click(function(){{{2}();}});\n", controlId, attributeValue.Id, attributeChangeHandlerFuncName);
                        }
                    }
                    break;
                case AttributeControlType.Checkboxes:
                case AttributeControlType.ReadonlyCheckboxes:
                    {
                        foreach (var attributeValue in attribute.Values)
                        {
                            attributeChangeScriptsBuilder.AppendFormat("$('#{0}_{1}').click(function(){{{2}();}});\n", controlId, attributeValue.Id, attributeChangeHandlerFuncName);
                        }
                    }
                    break;
                default:
                    break;
            }
        }
        //render scripts
        <script asp-location="Footer">
            function @(attributeChangeHandlerFuncName)() {
                $.ajax({
                    cache: false,
                    url: '@Html.Raw(Url.Action("productdetails_attributechange", "product", new { productId = productId, validateAttributeConditions = attributesHaveConditions, loadPicture = attributesHaveAssociatedPictures }))',
                    data: $('#product-details-form').serialize(),
                    type: 'post',
                    context: { product: '@productId' },
                    success: function (data, url) {
                        if (data.price) {
                            $('.price-value-@productId').text(data.price);
                        }
                        if (data.sku) {
                            $('#sku-@productId').text(data.sku);
                        }
                        if (data.mpn) {
                            $('#mpn-@productId').text(data.mpn);
                        }
                        if (data.gtin) {
                            $('#gtin-@productId').text(data.gtin);
                        }
                        if (data.stockAvailability) {
                            $('#stock-availability-value-@productId').text(data.stockAvailability);
                        }
                        if (data.backInStockSubscription) {
                            $('#back-in-stock-subscribe-@productId').show();
                        }
                        else {
                            $('#back-in-stock-subscribe-@productId').hide();
                        }
                        if (data.buttonTextBackInStockSubscription) {
                            $('#back-in-stock-subscribe-@productId').val(data.buttonTextBackInStockSubscription);
                        }
                        if (data.enabledattributemappingids) {
                            for (var i = 0; i < data.enabledattributemappingids.length; i++) {
                                $('#product_attribute_label_' + data.enabledattributemappingids[i]).show();
                                $('#product_attribute_input_' + data.enabledattributemappingids[i]).show();
                            }
                        }
                        if (data.disabledattributemappingids) {
                            for (var i = 0; i < data.disabledattributemappingids.length; i++) {
                                $('#product_attribute_label_' + data.disabledattributemappingids[i]).hide();
                                $('#product_attribute_input_' + data.disabledattributemappingids[i]).hide();
                            }
                        }
                        var product = this.product;
                        setTimeout(function () {
                            if (data.pictureDefaultSizeUrl) {
                                if ($(".product-grouped").length == 0) {
                                    $('#main-product-img-@productId').attr("src", data.pictureDefaultSizeUrl);
                                    if ($(".zoomImg").length) {
                                        $(".zoomImg").attr("src", data.pictureFullSizeUrl)
                                    }
                                }
                                else {
                                    $('#main-product-img-'+product).attr("src", data.pictureDefaultSizeUrl);
                                }

                            }}, 100);
                    }
                });
            }
            $(document).ready(function() {
                @(attributeChangeHandlerFuncName)();
                @Html.Raw(attributeChangeScriptsBuilder.ToString())
            });
        </script>
        }
    }