• 1405/06/13

طراحی جدید صفحه افزودن صفحه محصول :

سلام خدمت استاد مدائنی عزیز و هم دوره ای های گل 

من با کمک از هوش مصنوعی  و دانش کمی که از کد نویسی فرانت داشتم یه صفحه افزودن محصول طراحی کردم خوشحال میشم که بکار دوستان بیاد و ذره ایی کمکشون بکنه

موردی که من داشتم اینه که برای طراحی سرویس افزودن محصول کمی راهنمایی میخواستم تا بتونم تصاویر رو همزمان با افزودن محصول به پایگاه داده اضافه کنم این کار رو در فراند کامل انحام شده البته از نظر بنده کم سواد.

خوشحال میشم بنده رو راهنمایی کنید 

با تشکر

این کد صفحه اصلیه

@using ERP_Eshop.Domain.Models.Categories
@using ERP_Eshop.Domain.Models.Products
@model ERP_Eshop.Domain.ViewModels.Products.AdminCreateProductViewModel
@{
    ViewData["Title"] = "افزودن محصول جدید  ";
    ViewBag.Url = "/Admin/Product/Create";

    var categories = ViewBag.MainCategories as List<Category>;
    var brands = ViewBag.Brands as List<Brand>;
    var supplier = ViewBag.Suppliers as List<Supplier>;
}
@section Style
{
    <link rel="stylesheet" href="/Product/Css/Custome.css">

}
<div class="container mx-auto">

    <h3 class="mb-4">افزودن محصول جدید</h3>
    <div class="card adaptable-card">
        <div class="card-body">
            <div class="grid grid-cols-1 xl:grid-cols-5 gap-8">
                <div class="xl:col-span-5">
                    <div id="GridColumnStartEnd">

                        <form asp-area="Admin" asp-controller="Product" asp-action="Create" id="productForm" class="card mb-9 card-border" novalidate>

                            <div class="card-body">
                                <div class="grid grid-cols-3 gap-4">

                                    <!-- ===== Left Column ===== -->
                                    <div class="col-span-2 ...">

                                        <!-- ===== Basic Info ===== -->
                                        <div class="form-card">
                                            <div class="card-header">
                                                <div>
                                                    <h5>اطلاعات پایه</h5>
                                                    <span class="subtitle">اطلاعات اصلی محصول را وارد کنید</span>
                                                </div>
                                            </div>

                                            <div class="row g-3">
                                                <div class="col-12">
                                                    <label class="form-label">
                                                        <span class="required">*</span> عنوان محصول
                                                    </label>
                                                    <input type="text" class="input" id="productTitle"
                                                           placeholder="مثال: گوشی موبایل اپل iPhone 13" required>
                                                    <div class="invalid-feedback">لطفاً عنوان محصول را وارد کنید</div>
                                                </div>

                                                <div class="col-12">
                                                    <label class="form-label">عنوان انگلیسی</label>
                                                    <input type="text" class="input" id="productTitleEn"
                                                           placeholder="Apple iPhone 13">
                                                </div>

                                                <div class="form-container inline" style="margin-top:10px;">
                                                    <div class="form-item inline">
                                                        <label class="form-label"><span class="required">*</span> کد محصول (SKU)</label>
                                                        <input type="text" class="input" id="productSku"
                                                               placeholder="مثال: APP-13-128" required>
                                                        <div class="invalid-feedback">لطفاً کد محصول را وارد کنید</div>
                                                    </div>
                                                    <div class="form-item inline">
                                                        <label class="form-label"><span class="required">*</span> بارکد</label>
                                                        <input type="text" class="input" id="barcode"
                                                               placeholder="مثال: 11101110" required>
                                                        <div class="invalid-feedback">لطفاً بارکد محصول را وارد کنید</div>
                                                    </div>

                                                    <div class="form-item inline">
                                                        <label class="form-label"><span class="required">*</span> وضعیت</label>
                                                        <select class="input" id="productStatus" required>
                                                            <option value="true">فعال</option>
                                                            <option value="false">غیرفعال</option>
                                                        </select>
                                                    </div>
                                                </div>

                                                <div class="col-12">
                                                    <label class="form-label">توضیحات کوتاه</label>
                                                    <textarea class="input" id="productShortDesc" rows="2"
                                                              placeholder="توضیحات مختصر محصول"></textarea>
                                                </div>

                                                <div class="col-12">
                                                    <label class="form-label">توضیحات کامل (نقد و بررسی)</label>
                                                    <div id="fullDescription"></div>
                                                </div>

                                                <div class="col-12">
                                                    <label class="form-label">بررسی تخصصی</label>
                                                    <div id="expertReview"></div>
                                                </div>
                                            </div>
                                        </div>

                                        <!-- ===== Pricing & Stock ===== -->
                                        <div class="form-card">
                                            <div class="card-header">
                                                <div>
                                                    <h5>قیمت‌گذاری و موجودی</h5>
                                                    <span class="subtitle">اطلاعات قیمت و موجودی محصول</span>
                                                </div>
                                            </div>

                                            <div class="row g-3">
                                                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                                                    <div class="col-span-1">
                                                        <div class="form-item vertical">
                                                            <label class="form-label mb-2"><span class="required">*</span> قیمت (تومان)</label>
                                                            <div class="input-group">

                                                                <input type="number" class="input" id="productPrice"
                                                                       placeholder="۰" required min="0" step="1000">
                                                                <span class="input-group-text">تومان</span>
                                                            </div>
                                                            <div class="invalid-feedback">لطفاً قیمت را وارد کنید</div>
                                                        </div>
                                                    </div>

                                                    <div class="col-span-1">
                                                        <div class="form-item vertical">
                                                            <label class="form-label">قیمت قبلی (تخفیف)</label>
                                                            <div class="input-group">
                                                                <input type="number" class="input" id="productOldPrice"
                                                                       placeholder="۰" min="0" step="1000">
                                                                <span class="input-group-text">تومان</span>

                                                            </div>
                                                            <div class="form-text">در صورت تخفیف، قیمت قبلی را وارد کنید</div>
                                                        </div>
                                                    </div>
                                                </div>
                                                <div class="grid grid-cols-1 md:grid-cols-2 gap-4">

                                                    <div class="col-span-1">
                                                        <div class="form-item vertical">
                                                            <label class="form-label">حداقل موجودی هشدار</label>
                                                            <input type="number" class="input" id="productMinStock"
                                                                   placeholder="۵" value="5" min="1">
                                                            <div class="form-text">زمانی که موجودی به این عدد رسید، هشدار داده می‌شود</div>
                                                        </div>
                                                    </div>

                                                </div>
                                            </div>
                                        </div>

                                        <!-- ===== Organization ===== -->
                                        <div class="form-card">
                                            <div class="card-header">
                                                <div>
                                                    <h5>دسته‌بندی و برند</h5>
                                                    <span class="subtitle">دسته‌بندی و برند محصول را مشخص کنید</span>
                                                </div>
                                            </div>

                                            <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
                                                <div class="col-span-1">
                                                    <div class="form-item vertical">
                                                        <label class="form-label"><span class="required">*</span> برند</label>
                                                        <select class="form-select" id="productBrand" required>
                                                            <option value="">انتخاب کنید...</option>
                                                            @if (brands != null && brands.Any())
                                                            {
                                                                @foreach (var brand in brands)
                                                                {
                                                                    <option value="@brand.Id">@brand.Name (@brand.NameEn)</option>
                                                                }
                                                            }
                                                        </select>
                                                        <div class="invalid-feedback">لطفاً برند را انتخاب کنید</div>
                                                    </div>
                                                </div>

                                            </div>
                                            <div class="form-card">
                                                <div class="card-header">
                                                    <h4>
                                                        <span class="required">*</span> دسته‌بندی
                                                    </h4>
                                                    <div class="mt-1 demo-card-description">
                                                        لطفا گروه هایی که مایلید کالا در آن نمایش داده شود را انتخاب نمایید
                                                    </div>
                                                </div>
                                            </div>
                                            <div class="card mb-9 card-border">
                                                @if (categories != null && categories.Any())
                                                {
                                                    @foreach (var category in categories.Where(c => c.ParentId == null))
                                                    {
                                                        <ul>
                                                            <li>
                                                                <label class="checkbox-label">
                                                                    <input class="checkbox text-green-500" name="@nameof(Model.SelectedCategories)" type="checkbox" main_category_checkbox="@category.Id" value="@category.Id">
                                                                    <span>@category.Title</span>
                                                                </label>
                                                                @if (categories.Any(c => c.ParentId == category.Id))
                                                                {
                                                                    <div id="sub_categories_@category.Id" style="display: none">
                                                                        @foreach (var subcategory in categories.Where(c => c.ParentId == category.Id))
                                                                        {
                                                                            <label class="checkbox-label">
                                                                                <input class="checkbox text-blue-600" type="checkbox" main_category_checkbox="@subcategory.Id" value="@subcategory.Id" parent-category-id="@category.Id">
                                                                                <span>@subcategory.Title</span>
                                                                            </label>
                                                                            @if (categories.Any(c => c.ParentId == subcategory.Id))
                                                                            {
                                                                                <div id="sub_categories_@subcategory.Id" style="display: none">
                                                                                    @foreach (var secendsubcategory in categories.Where(c => c.ParentId == subcategory.Id))
                                                                                    {
                                                                                        <label class="checkbox-label">
                                                                                            <input class="checkbox text-yellow-500" type="checkbox" value="@secendsubcategory.Id" parent-category-id="@subcategory.Id">
                                                                                            <span>@secendsubcategory.Title</span>
                                                                                        </label>
                                                                                    }
                                                                                </div>
                                                                            }
                                                                        }
                                                                    </div>
                                                                }
                                                            </li>
                                                        </ul>
                                                        <hr />
                                                    }
                                                }
                                            </div>
                                        </div>
                                        <!-- ===== ProductSpecifications ===== -->
                                        <div class="form-card">
                                            <div class="card-header">
                                                <div>
                                                    <h5>مشخصات فنی</h5>
                                                    <span class="subtitle">مشخصات و ویژگی‌های محصول</span>
                                                </div>
                                                <button type="button" class="btn btn-sm btn-primary" id="addSpecGroup">
                                                    <i class="bi bi-plus-lg"></i> افزودن گروه
                                                </button>
                                            </div>
                                            <div id="ProductSpecificationsContainer">
                                                <!-- گروه مشخصات به صورت داینامیک اضافه می‌شود -->

                                            </div>
                                        </div>

                                    </div>

                                    <!-- ===== Right Column ===== -->
                                    <div class="...">

                                        <!-- ===== Images ===== -->
                                        <div class="form-card">
                                            <div class="card-header">
                                                <div>
                                                    <h5>تصاویر محصول</h5>
                                                    <span class="subtitle">حداقل ۱ تصویر برای محصول آپلود کنید</span>
                                                </div>
                                                <span class="badge" style="background: var(--bg-elevated); color: var(--text-secondary);" id="imageCount">
                                                    <i class="bi bi-images"></i> ۰ / ۱۰
                                                </span>
                                            </div>

                                            <div style="padding-right: 10px; position: relative;">
                                                <div class="image-upload-grid" id="imageGrid">
                                                    <!-- تصاویر توسط JavaScript ساخته می‌شوند -->
                                                    <!-- باکس آپلود -->
                                                    <div class="upload-box" id="uploadBox">
                                                        <div class="upload-content">
                                                            <i class="bi bi-cloud-arrow-up"></i>
                                                            <span>آپلود تصویر</span>
                                                            <input type="file" id="imageInput" accept="image/*" multiple>
                                                        </div>
                                                    </div>
                                                </div>
                                            </div>
                                            <div class="form-text mt-2">
                                                <i class="bi bi-info-circle"></i>
                                                تصویر اول به عنوان تصویر اصلی نمایش داده می‌شود
                                            </div>
                                        </div>
                                        <!-- ===== Variants ===== -->
                                        <div class="form-card">
                                            <div class="card-header">
                                                <div>
                                                    <h5>تنوع‌ رنگ محصول</h5>
                                                </div>
                                            </div>

                                            <div id="variantsContainer">
                                                <div class="variant-row">
                                                    <div class="form-group">
                                                        <label class="form-label mb-2" style="font-size:12px;">عنوان </label>
                                                        <input type="text" id="color_name_input" class="input w-25" placeholder="آبی">
                                                    </div>
                                                    <div class="form-group">
                                                        <label class="form-label" style="font-size:12px;">کد رنگ</label>
                                                        <div class="color-picker">
                                                            <input type="color" id="color_hex_input" class="input w-10" value="">
                                                        </div>
                                                    </div>
                                                    <div class="form-group" style="scroll-margin-right:20px;">
                                                        <label class="form-label" style="font-size:12px;">تفاوت قیمت</label>
                                                        <input type="number" id="color_price_input" class="input w-20" value="10" min="0">
                                                    </div>
                                                    <div class="form-group">
                                                        <button type="button" class="btn btn-sm btn-primary" id="add_color">
                                                            +
                                                        </button>
                                                    </div>
                                                </div>
                                                <div class="variant-row">
                                                    <table id="product-list-data-table" class="table-default table-hover data-table dataTable no-footer" role="grid" aria-describedby="product-list-data-table_info">
                                                        <thead>
                                                            <tr role="row">
                                                                <th class="sorting_asc" style="width: 470.188px;">عنوان </th>
                                                                <th class="sorting_asc" style="width: 470.188px;">رنگ </th>
                                                                <th class="sorting_asc" style="width: 470.188px;">تفاوت قیمت</th>
                                                                <th class="sorting_asc" style="width: 470.188px;">عملیات</th>

                                                            </tr>
                                                        </thead>
                                                        <tbody id="list_of_product_Color">
                                                        </tbody>
                                                    </table>
                                                </div>
                                            </div>
                                        </div>

                                        <!-- ===== SEO ===== -->
                                        <div class="form-card">
                                            <div class="card-header">
                                                <div>
                                                    <h5>سئو (SEO)</h5>
                                                    <span class="subtitle">بهینه‌سازی برای موتورهای جستجو</span>
                                                </div>
                                            </div>

                                            <div class="row g-3">
                                                <div class="col-12">
                                                    <label class="form-label">عنوان SEO</label>
                                                    <input type="text" class="input" id="seoTitle"
                                                           placeholder="عنوان برای موتورهای جستجو">
                                                </div>
                                                <div class="col-12">
                                                    <label class="form-label">توضیحات SEO</label>
                                                    <textarea class="input" id="seoDescription" rows="3"
                                                              placeholder="توضیحات متا برای سئو"></textarea>
                                                </div>
                                                <div class="col-12">
                                                    <label class="form-label">کلمات کلیدی</label>
                                                    <input type="text" class="input" id="seoKeywords"
                                                           placeholder="کلمات کلیدی، جدا شده با کاما">
                                                </div>
                                                <div class="col-12">
                                                    <label class="form-label">اسلاگ (Slug)</label>
                                                    <input type="text" class="input" id="seoSlug"
                                                           placeholder="آدرس صفحه محصول">
                                                    <div class="form-text">به صورت خودکار از عنوان تولید می‌شود</div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                            <div class="form-actions">

                                <div class="d-flex gap-2">
                                    <a type="reset" class="btn btn-outline-secondary" asp-action="Index">بازگشت</a>

                                    <button type="submit" class="btn btn-primary" id="btnSubmit">
                                        <i class="bi bi-check-lg"></i> ذخیره محصول
                                    </button>
                                </div>
                            </div>
                        </form>

                  
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<div class="toast-container" id="toastContainer"></div>

@section Scripts
{
    <script src="~/js/addproductcolor.js"></script>

    <script>

        // ==================== product-images.js (نسخه نهایی) ====================
class ProductImagesManager {
    constructor(options = {}) {
        this.containerId = options.containerId || 'imageGrid';
        this.container = document.getElementById(this.containerId);
        this.uploadBoxId = options.uploadBoxId || 'uploadBox';
        this.imageInputId = options.imageInputId || 'imageInput';
        this.maxImages = options.maxImages || 10;
        this.allowedTypes = options.allowedTypes || ['image/jpeg', 'image/png', 'image/webp', 'image/gif'];
        this.maxSize = options.maxSize || 5 * 1024 * 1024;
        this.imageCounter = 0;
        this.currentModalIndex = 0;
        this.modalInstance = null;
        this.keyboardHandler = null;
        
        this.initializeEvents();
        this.loadInitialData();
    }

    loadInitialData() {
        const existingImages = window.initialProductImages || [];
        existingImages.forEach((image, index) => {
            this.addImageToGrid(image, index);
        });
        this.updateImageCount();
        this.updateMainImage();
    }

    initializeEvents() {
        const uploadBox = document.getElementById(this.uploadBoxId);
        const fileInput = document.getElementById(this.imageInputId);

        if (uploadBox) {
            uploadBox.addEventListener('click', () => {
                fileInput?.click();
            });

            uploadBox.addEventListener('dragover', (e) => {
                e.preventDefault();
                uploadBox.classList.add('dragover');
            });

            uploadBox.addEventListener('dragleave', () => {
                uploadBox.classList.remove('dragover');
            });

            uploadBox.addEventListener('drop', (e) => {
                e.preventDefault();
                uploadBox.classList.remove('dragover');
                const files = e.dataTransfer.files;
                this.handleFiles(files);
            });
        }

        if (fileInput) {
            fileInput.addEventListener('change', (e) => {
                this.handleFiles(e.target.files);
                fileInput.value = '';
            });
        }

        // ===== رویدادهای دکمه‌های روی تصاویر با jQuery =====
        $(document).on('click', '.remove-image', (e) => {
            e.stopPropagation();
            const imageItem = $(e.target).closest('.image-item');
            const imageId = imageItem.data('image-id');
            
            if (confirm('آیا از حذف این تصویر اطمینان دارید؟')) {
                if (imageId && !imageId.toString().startsWith('new_image_')) {
                    imageItem.find('input[name$=".IsDeleted"]').val('true');
                    imageItem.css('opacity', '0.4');
                    imageItem.find('.image-actions-always').hide();
                    imageItem.find('button').prop('disabled', true);
                } else {
                    imageItem.remove();
                    this.updateDisplayOrders();
                    this.updateMainImage();
                }
                
                this.updateImageCount();
                this.showToast('info', 'حذف', 'تصویر با موفقیت حذف شد.');
            }
        });

        $(document).on('click', '.set-main-image', (e) => {
            e.stopPropagation();
            const imageItem = $(e.target).closest('.image-item');
            this.setImageAsMain(imageItem);
        });

        $(document).on('click', '.view-image', (e) => {
            e.stopPropagation();
            const imageItem = $(e.target).closest('.image-item');
            const imageId = imageItem.data('image-id');
            this.showImageModal(imageId);
        });

        $(document).on('click', '.image-item', function(e) {
            if (!$(e.target).closest('button').length) {
                const imageId = $(this).data('image-id');
                const manager = $(this).closest('#imageGrid').data('manager');
                if (manager) {
                    manager.showImageModal(imageId);
                }
            }
        });

        // ===== رویدادهای مودال با jQuery (Event Delegation) =====
        $(document).on('click', '#imageModal .nav-prev', (e) => {
            e.stopPropagation();
            this.navigateModal(-1);
        });

        $(document).on('click', '#imageModal .nav-next', (e) => {
            e.stopPropagation();
            this.navigateModal(1);
        });

        $(document).on('click', '#imageModal .action-set-main', (e) => {
            e.stopPropagation();
            const modalEl = document.getElementById('imageModal');
            if (!modalEl) return;
            
            const imgId = modalEl.dataset.imageId;
            this.setImageAsMainById(imgId);
            
            // بروزرسانی وضعیت دکمه
            const allImgs = this.getAllImages();
            const idx = allImgs.findIndex(img => img.id == imgId);
            const btn = e.currentTarget;
            
            if (idx !== -1 && allImgs[idx].isMain) {
                btn.classList.add('active');
                btn.querySelector('span').textContent = 'اصلی ✓';
            } else {
                btn.classList.remove('active');
                btn.querySelector('span').textContent = 'تصویر اصلی';
            }
            
            setTimeout(() => {
                if (this.modalInstance) {
                    this.modalInstance.hide();
                }
            }, 500);
        });

        $(document).on('click', '#imageModal .action-delete', (e) => {
            e.stopPropagation();
            const modalEl = document.getElementById('imageModal');
            if (!modalEl) return;
            
            const imgId = modalEl.dataset.imageId;
            
            if (confirm('آیا از حذف این تصویر اطمینان دارید؟')) {
                this.deleteImageById(imgId);
                if (this.modalInstance) {
                    this.modalInstance.hide();
                }
            }
        });
    }

    handleFiles(files) {
        const currentCount = this.container.querySelectorAll('.image-item:not(.upload-box)').length;
        const totalFiles = files.length;
        
        if (currentCount + totalFiles > this.maxImages) {
            this.showToast('error', 'خطا', `حداکثر ${this.maxImages} تصویر مجاز است.`);
            return;
        }

        Array.from(files).forEach((file, index) => {
            if (!this.allowedTypes.includes(file.type)) {
                this.showToast('error', 'خطا', `نوع فایل ${file.name} پشتیبانی نمی‌شود.`);
                return;
            }

            if (file.size > this.maxSize) {
                this.showToast('error', 'خطا', `حجم فایل ${file.name} بیشتر از حد مجاز است.`);
                return;
            }

            const reader = new FileReader();
            reader.onload = (e) => {
                const imageUrl = e.target.result;
                const isMain = currentCount === 0 && index === 0;
                this.addImageToGrid({
                    id: `new_image_${++this.imageCounter}`,
                    imageUrl: imageUrl,
                    isMain: isMain,
                    displayOrder: currentCount + index + 1,
                    altText: file.name,
                    file: file,
                    isNew: true
                }, currentCount + index);
                
                this.updateDisplayOrders();
                this.updateMainImage();
                this.updateImageCount();
            };
            reader.readAsDataURL(file);
        });
    }

// ===== تابع addImageToGrid با آیکون‌ها =====
addImageToGrid(imageData, index) {
    const isMain = imageData.isMain || false;
    const imageId = imageData.id || `new_image_${++this.imageCounter}`;
    const imgSrc = imageData.imageUrl || 'https://via.placeholder.com/120x120/2d3748/8b949e?text=بدون+تصویر';
    const altText = imageData.altText || 'تصویر محصول';
    const displayOrder = imageData.displayOrder || index + 1;
    const isNew = imageData.isNew || false;
    const isDeleted = imageData.isDeleted || false;

    const uploadBox = document.getElementById(this.uploadBoxId);
    
    const imageHtml = `
        <div class="image-item ${isMain ? 'is-main' : ''}" 
             data-image-id="${imageId}" 
             data-display-order="${displayOrder}"
             style="${isDeleted ? 'opacity: 0.4;' : ''}">
            
            <div class="image-wrapper">
                <img src="${imgSrc}" alt="${altText}" loading="lazy">
                
                <div class="image-badges">
                    ${isMain ? '<span class="badge-main">اصلی</span>' : ''}
                    <span class="badge-order">#${displayOrder}</span>
                </div>
            </div>
            
            <div class="image-actions-always" style="${isDeleted ? 'display: none;' : ''}">
                <button type="button" class="btn-star set-main-image ${isMain ? 'active' : ''}" title="انتخاب به عنوان تصویر اصلی">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
                    </svg>
                </button>
                <button type="button" class="btn-eye view-image" title="مشاهده بزرگ">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
                        <circle cx="12" cy="12" r="3"/>
                    </svg>
                </button>
                <button type="button" class="btn-trash remove-image" title="حذف">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                        <polyline points="3 6 5 6 21 6"/>
                        <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
                    </svg>
                </button>
            </div>
            
            <input type="hidden" name="Images[${index}].Id" value="${imageId}">
            <input type="hidden" name="Images[${index}].ImageUrl" value="${imageData.imageUrl || ''}">
            <input type="hidden" name="Images[${index}].IsMain" value="${isMain}">
            <input type="hidden" name="Images[${index}].DisplayOrder" value="${displayOrder}">
            <input type="hidden" name="Images[${index}].AltText" value="${altText}">
            <input type="hidden" name="Images[${index}].IsDeleted" value="${isDeleted}">
            
            ${isNew && imageData.file ? 
                `<input type="file" name="Images[${index}].File" style="display:none;" 
                       data-file-name="${imageData.file.name}" data-file-size="${imageData.file.size}">` : ''
            }
        </div>
    `;

    uploadBox.insertAdjacentHTML('beforebegin', imageHtml);
    
    if (isNew && imageData.file) {
        const fileInput = uploadBox.parentElement.querySelector(`input[data-file-name="${imageData.file.name}"]`);
        if (fileInput) {
            const dataTransfer = new DataTransfer();
            dataTransfer.items.add(imageData.file);
            fileInput.files = dataTransfer.files;
        }
    }
    
    this.container.dataset.manager = this;
}

    updateDisplayOrders() {
        const imageItems = this.container.querySelectorAll('.image-item:not(.upload-box)');
        imageItems.forEach((item, index) => {
            const orderInput = item.querySelector('input[name$=".DisplayOrder"]');
            if (orderInput) {
                const name = orderInput.getAttribute('name');
                const newName = name.replace(/Images\[\d+\]/, `Images[${index}]`);
                orderInput.setAttribute('name', newName);
                orderInput.value = index + 1;
            }
            item.dataset.displayOrder = index + 1;
            
            const badgeOrder = item.querySelector('.badge-order');
            if (badgeOrder) {
                badgeOrder.textContent = `#${index + 1}`;
            }
        });
    }

// ===== بروزرسانی تصویر اصلی با آیکون‌ها =====
updateMainImage() {
    const items = this.container.querySelectorAll('.image-item:not(.upload-box):not([style*="opacity: 0.4"])');
    let hasMain = false;
    
    items.forEach((item, index) => {
        const isMainInput = item.querySelector('input[name$=".IsMain"]');
        const badgeMain = item.querySelector('.badge-main');
        const btnStar = item.querySelector('.btn-star');
        
        if (isMainInput) {
            if (!hasMain && index === 0) {
                isMainInput.value = 'true';
                item.classList.add('is-main');
                if (!badgeMain) {
                    const badges = item.querySelector('.image-badges');
                    badges.prepend('<span class="badge-main">اصلی</span>');
                }
                if (btnStar) {
                    btnStar.classList.add('active');
                }
                hasMain = true;
            } else {
                isMainInput.value = 'false';
                item.classList.remove('is-main');
                if (badgeMain) badgeMain.remove();
                if (btnStar) {
                    btnStar.classList.remove('active');
                }
            }
        }
    });
}

    updateImageCount() {
        const count = this.container.querySelectorAll('.image-item:not(.upload-box)').length;
        const badge = document.getElementById('imageCount');
        if (badge) {
            badge.innerHTML = `
                <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: inline-block; vertical-align: middle; margin-left: 4px;">
                    <rect x="3" y="3" width="18" height="18" rx="2" ry="2"/>
                    <circle cx="8.5" cy="8.5" r="1.5"/>
                    <polyline points="21 15 16 10 5 21"/>
                </svg>
                ${count} / ${this.maxImages}
            `;
        }
    }

// ===== انتخاب تصویر به عنوان اصلی با آیکون‌ها =====
setImageAsMain(imageItem) {
    if (!imageItem || !imageItem.length) return;
    
    this.container.querySelectorAll('.image-item').forEach(item => {
        item.classList.remove('is-main');
        const mainInput = item.querySelector('input[name$=".IsMain"]');
        if (mainInput) mainInput.value = 'false';
        const badgeMain = item.querySelector('.badge-main');
        if (badgeMain) badgeMain.remove();
        const btnStar = item.querySelector('.btn-star');
        if (btnStar) {
            btnStar.classList.remove('active');
        }
    });
    
    imageItem.addClass('is-main');
    const mainInput = imageItem.find('input[name$=".IsMain"]');
    if (mainInput) mainInput.val('true');
    
    const badges = imageItem.find('.image-badges');
    badges.prepend('<span class="badge-main">اصلی</span>');
    
    const btnStar = imageItem.find('.btn-star');
    if (btnStar) {
        btnStar.addClass('active');
    }
    
    this.showToast('success', 'موفق', 'تصویر اصلی با موفقیت انتخاب شد.');
}

    setImageAsMainById(imageId) {
        const imageItem = $(this.container).find(`.image-item[data-image-id="${imageId}"]`);
        if (imageItem.length) {
            this.setImageAsMain(imageItem);
        }
    }

    deleteImageById(imageId) {
        const imageItem = this.container.querySelector(`.image-item[data-image-id="${imageId}"]`);
        if (!imageItem) return;
        
        const isNew = imageId.toString().startsWith('new_image_');
        
        if (!isNew) {
            imageItem.querySelector('input[name$=".IsDeleted"]').value = 'true';
            imageItem.style.opacity = '0.4';
            const actions = imageItem.querySelector('.image-actions-always');
            if (actions) actions.style.display = 'none';
            imageItem.querySelectorAll('button').forEach(btn => btn.disabled = true);
        } else {
            imageItem.remove();
        }
        
        this.updateImageCount();
        this.updateDisplayOrders();
        this.updateMainImage();
        this.showToast('info', 'حذف', 'تصویر با موفقیت حذف شد.');
    }

    getAllImages() {
        const images = [];
        const imageItems = this.container.querySelectorAll('.image-item:not(.upload-box)');
        
        imageItems.forEach(item => {
            const isDeleted = item.querySelector('input[name$=".IsDeleted"]')?.value === 'true';
            if (!isDeleted) {
                const img = item.querySelector('img');
                images.push({
                    id: item.dataset.imageId,
                    src: img?.src || '',
                    alt: img?.alt || 'تصویر محصول',
                    isMain: item.querySelector('input[name$=".IsMain"]')?.value === 'true'
                });
            }
        });
        
        return images;
    }

// ===== نمایش مودال با آیکون‌ها =====
// ===== نمایش مودال مینیمال =====
showImageModal(imageId) {
    const existingModal = document.getElementById('imageModal');
    if (existingModal) existingModal.remove();

    const allImages = this.getAllImages();
    const currentIndex = allImages.findIndex(img => img.id == imageId);
    
    if (currentIndex === -1) return;
    
    this.currentModalIndex = currentIndex;
    const imageData = allImages[currentIndex];
    const totalImages = allImages.length;

    const modalHtml = `
        <div class="modal fade" id="imageModal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static">
            <div class="modal-dialog modal-dialog-centered modal-xl">
                <div class="modal-content">
                    <!-- هدر مینیمال -->
                    <div class="modal-header">
                        <div class="modal-header-info">
                            <span class="image-counter">${currentIndex + 1} / ${totalImages}</span>
                            <span class="image-name">${imageData.alt || 'تصویر محصول'}</span>
                        </div>
                        <button type="button" class="btn-close-modal" data-bs-dismiss="modal">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                <line x1="18" y1="6" x2="6" y2="18"/>
                                <line x1="6" y1="6" x2="18" y2="18"/>
                            </svg>
                        </button>
                    </div>

                    <!-- بدنه مودال -->
                    <div class="modal-body">
                        <div class="image-viewer">
                            <img id="modalImage" src="${imageData.src}" alt="${imageData.alt}" loading="lazy">
                        </div>
                        
                        ${totalImages > 1 ? `
                            <button type="button" class="nav-btn nav-prev" data-nav="prev" title="تصویر قبلی">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                    <polyline points="15 18 9 12 15 6"/>
                                </svg>
                            </button>
                            <button type="button" class="nav-btn nav-next" data-nav="next" title="تصویر بعدی">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                    <polyline points="9 18 15 12 9 6"/>
                                </svg>
                            </button>
                        ` : ''}
                    </div>

                    <!-- فوتر مینیمال -->
                    <div class="modal-footer">
                        <div class="footer-actions">
                            <button type="button" class="action-btn action-set-main ${imageData.isMain ? 'active' : ''}" data-action="setmain">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                    <polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>
                                </svg>
                                <span>${imageData.isMain ? 'اصلی' : 'تصویر اصلی'}</span>
                            </button>
                            
                            <span class="divider"></span>
                            
                            <button type="button" class="action-btn action-delete" data-action="delete">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                    <polyline points="3 6 5 6 21 6"/>
                                    <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>
                                </svg>
                                <span>حذف</span>
                            </button>
                            
                            <span class="divider"></span>
                            
                            <button type="button" class="action-btn action-close" data-bs-dismiss="modal">
                                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
                                    <line x1="18" y1="6" x2="6" y2="18"/>
                                    <line x1="6" y1="6" x2="18" y2="18"/>
                                </svg>
                                <span>بستن</span>
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    `;

    document.body.insertAdjacentHTML('beforeend', modalHtml);
    
    const modalElement = document.getElementById('imageModal');
    this.modalInstance = new bootstrap.Modal(modalElement, {
        backdrop: 'static',
        keyboard: true
    });
    
    this.modalInstance.show();

    modalElement.dataset.currentIndex = currentIndex;
    modalElement.dataset.totalImages = totalImages;
    modalElement.dataset.imageId = imageId;

    // ===== کلیدهای کیبورد =====
    this.keyboardHandler = (e) => this.handleKeyboard(e);
    document.addEventListener('keydown', this.keyboardHandler);

    // ===== رویدادهای مودال =====
    modalElement.addEventListener('hidden.bs.modal', () => {
        document.removeEventListener('keydown', this.keyboardHandler);
        this.updateDisplayOrders();
        this.updateMainImage();
        this.updateImageCount();
        modalElement.remove();
        this.modalInstance = null;
    });
}

    // ===== ناوبری در مودال =====
    navigateModal(direction) {
        const modalElement = document.getElementById('imageModal');
        if (!modalElement) return;
        
        let currentIndex = parseInt(modalElement.dataset.currentIndex);
        const totalImages = parseInt(modalElement.dataset.totalImages);
        
        currentIndex = (currentIndex + direction + totalImages) % totalImages;
        
        const allImages = this.getAllImages();
        const imageData = allImages[currentIndex];
        
        if (imageData) {
            const modalImage = document.getElementById('modalImage');
            if (modalImage) {
                modalImage.src = imageData.src;
                modalImage.alt = imageData.alt;
            }
            
            const counter = modalElement.querySelector('.image-counter');
            if (counter) {
                counter.textContent = `${currentIndex + 1} / ${totalImages}`;
            }
            
            const name = modalElement.querySelector('.image-name');
            if (name) {
                name.textContent = imageData.alt || 'تصویر محصول';
            }
            
            const setMainBtn = modalElement.querySelector('[data-action="setmain"]');
            if (setMainBtn) {
                if (imageData.isMain) {
                    setMainBtn.classList.add('active');
                    setMainBtn.querySelector('span').textContent = 'اصلی ✓';
                } else {
                    setMainBtn.classList.remove('active');
                    setMainBtn.querySelector('span').textContent = 'تصویر اصلی';
                }
            }
            
            modalElement.dataset.currentIndex = currentIndex;
            modalElement.dataset.imageId = imageData.id;
        }
    }

    // ===== مدیریت کلیدهای کیبورد =====
    handleKeyboard(e) {
        const modalElement = document.getElementById('imageModal');
        if (!modalElement || !modalElement.classList.contains('show')) return;
        
        if (e.key === 'ArrowRight' || e.key === 'ArrowDown') {
            e.preventDefault();
            this.navigateModal(1);
        } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp') {
            e.preventDefault();
            this.navigateModal(-1);
        }
    }

    collectFormData() {
        const images = [];
        const imageItems = this.container.querySelectorAll('.image-item:not(.upload-box)');
        
        imageItems.forEach((item, index) => {
            const isDeleted = item.querySelector('input[name$=".IsDeleted"]')?.value === 'true';
            if (isDeleted) return;

            const img = item.querySelector('img');
            images.push({
                id: item.dataset.imageId,
                imageUrl: img?.src || '',
                isMain: item.querySelector('input[name$=".IsMain"]')?.value === 'true',
                displayOrder: index + 1,
                altText: img?.alt || '',
                isDeleted: false
            });
        });

        return images;
    }

    validate() {
        const imageItems = this.container.querySelectorAll('.image-item:not(.upload-box)');
        const validImages = Array.from(imageItems).filter(item => {
            const isDeleted = item.querySelector('input[name$=".IsDeleted"]')?.value === 'true';
            return !isDeleted;
        });

        if (validImages.length === 0) {
            this.showToast('error', 'خطا', 'حداقل یک تصویر برای محصول باید آپلود شود.');
            return false;
        }

        const hasMain = Array.from(validImages).some(item => {
            return item.querySelector('input[name$=".IsMain"]')?.value === 'true';
        });

        if (!hasMain) {
            this.showToast('error', 'خطا', 'لطفاً یک تصویر را به عنوان تصویر اصلی انتخاب کنید.');
            return false;
        }

        return true;
    }

    showToast(type, title, message) {
        const toast = $(`
            <div class="toast-custom ${type}">
                <div class="toast-body">
                    <strong>${title}</strong>
                    <span>${message}</span>
                </div>
                <button class="btn-close-toast">✕</button>
            </div>
        `);

        $('#toastContainer').append(toast);

        toast.find('.btn-close-toast').on('click', function() {
            toast.remove();
        });

        setTimeout(function() {
            toast.fadeOut(300, function() {
                $(this).remove();
            });
        }, 5000);
    }
}


        // ==================== product-ProductSpecifications.js ====================
        class ProductProductSpecificationsManager {
        constructor(containerId) {
        this.container = document.getElementById(containerId);
        this.groupCounter = 0;
        this.specCounter = 0;
        this.initializeEvents();
        this.loadInitialData();
        }

        // ===== بارگذاری داده‌های اولیه از سرور =====
        loadInitialData() {
        // اگر داده‌ای از سرور وجود داشت، بارگذاری کن
        // این تابع توسط سرور با داده‌های موجود فراخوانی می‌شود
        }

        // ===== تنظیم رویدادها =====
        initializeEvents() {
        // افزودن گروه جدید
        $(document).on('click', '#addSpecGroup', () => {
        this.addGroup();
        });

        // حذف گروه
        $(document).on('click', '.remove-spec-group', (e) => {
        const group = $(e.target).closest('.spec-group');
        if (this.container.querySelectorAll('.spec-group').length > 1) {
        const groupId = group.data('group-id');
        if (groupId && !groupId.toString().startsWith('new_group_')) {
        // اگر گروه در دیتابیس وجود دارد، برای حذف علامت بزن
        group.find('input[name$=".IsDeleted"]').val('true');
        group.css('opacity', '0.5');
        group.find('.remove-spec-group').prop('disabled', true);
        } else {
        group.remove();
        }
        this.updateDisplayOrders();
        this.updateAllInputNames();
        } else {
        this.showToast('error', 'خطا', 'حداقل یک گروه مشخصات باید وجود داشته باشد.');
        }
        });

        // افزودن مشخصه جدید
        $(document).on('click', '.add-spec-row', (e) => {
        const group = $(e.target).closest('.spec-group');
        this.addSpecification(group);
        });

        // حذف مشخصه
        $(document).on('click', '.remove-spec-row', (e) => {
        const row = $(e.target).closest('.spec-row');
        const group = row.closest('.spec-group');
        if (group.find('.spec-row').length > 1) {
        const specId = row.data('spec-id');
        if (specId && !specId.toString().startsWith('new_spec_')) {
        row.find('input[name$=".IsDeleted"]').val('true');
        row.css('opacity', '0.5');
        row.find('.remove-spec-row').prop('disabled', true);
        } else {
        row.remove();
        }
        this.updateDisplayOrders(group);
        this.updateAllInputNames();
        } else {
        this.showToast('error', 'خطا', 'حداقل یک مشخصه در هر گروه باید وجود داشته باشد.');
        }
        });

        // تغییر نام گروه
        $(document).on('input', '.spec-group-name', (e) => {
        const group = $(e.target).closest('.spec-group');
        const name = $(e.target).val();
        const groupIndex = group.data('group-index');
        group.find(`input[name="Specifications[${groupIndex}].GroupName"]`).val(name);
        });
        // تغییر کلید مشخصه
        $(document).on('input', '.spec-key', (e) => {
        const row = $(e.target).closest('.spec-row');
        const group = row.closest('.spec-group');
        const groupIndex = group.data('group-index');
        const specIndex = row.data('spec-index');
        const value = $(e.target).val();
        row.find(`input[name="Specifications[${groupIndex}].ProductSpecifications[${specIndex}].Key"]`).val(value);
        });
        // تغییر مقدار مشخصه
        $(document).on('input', '.spec-value', (e) => {
        const row = $(e.target).closest('.spec-row');
        const group = row.closest('.spec-group');
        const groupIndex = group.data('group-index');
        const specIndex = row.data('spec-index');
        const value = $(e.target).val();
        row.find(`input[name="Specifications[${groupIndex}].ProductSpecifications[${specIndex}].Value"]`).val(value);
        });
        }

        // ===== افزودن گروه جدید =====
        addGroup(groupData = null) {
        this.groupCounter++;
        const groupIndex = this.container.querySelectorAll('.spec-group').length;
        const groupId = groupData?.id || `new_group_${this.groupCounter}`;
        const groupName = groupData?.groupName || 'گروه جدید';
        const ProductSpecifications = groupData?.ProductSpecifications || [];

        const groupHtml = `
        <div class="spec-group" data-group-id="${groupId}" data-group-index="${groupIndex}">
        <div class="spec-header">
        <h6>
        <input type="text" class="form-control form-control-sm spec-group-name"
        style="display:inline-block;width:auto;min-width:150px;background:transparent;border:1px solid var(--border-color);color:var(--text-primary);"
        value="${groupName}" placeholder="نام گروه">
        </h6>

        <button type="button" class="btn bg-orange-500 hover:bg-orange-400 active:bg-orange-600 text-white btn-xs remove-spec-group">
        X
        </button>
        </div>
        <div class="spec-group-items">
        ${this.renderProductSpecifications(ProductSpecifications, groupIndex)}
        </div>
        <button type="button" class="btn btn-sm btn-outline-primary add-spec-row">
        <i class="bi bi-plus-lg"></i> افزودن مشخصه
        </button>

        <!-- فیلدهای مخفی برای ارسال به سرور -->
        <input type="hidden" name="Specifications[${groupIndex}].Id" value="${groupId}">
        <input type="hidden" name="Specifications[${groupIndex}].GroupName" value="${groupName}">
        <input type="hidden" name="Specifications[${groupIndex}].DisplayOrder" value="${groupIndex + 1}">
        </div>
        `;

        this.container.insertAdjacentHTML('beforeend', groupHtml);
        this.updateDisplayOrders();
        this.updateAllInputNames();
        }

        // ===== رندر مشخصات یک گروه =====
        renderProductSpecifications(ProductSpecifications, groupIndex) {
        if (!ProductSpecifications || ProductSpecifications.length === 0) {
        return this.getDefaultSpecRow(groupIndex, 0);
        }

        let html = '';
        ProductSpecifications.forEach((spec, index) => {
        html += this.getSpecRowHtml(spec, groupIndex, index);
        });
        return html;
        }

        // ===== دریافت HTML یک ردیف مشخصه =====
        getSpecRowHtml(spec = null, groupIndex, specIndex) {
        const specId = spec?.id || `new_spec_${++this.specCounter}`;
        const key = spec?.key || '';
        const value = spec?.value || '';
        const displayOrder = spec?.displayOrder || specIndex + 1;
        const isDeleted = spec?.isDeleted || false;

        return `
        <div class="spec-row" data-spec-id="${specId}" data-spec-index="${specIndex}" data-group-index="${groupIndex}">
        <div class="form-group">
        <input type="text" class="form-control form-control-sm spec-key"
        placeholder="عنوان (مثال: ابعاد)" value="${key}">
        </div>
        <div class="form-group">
        <input type="text" class="form-control form-control-sm spec-value"
        placeholder="مقدار (مثال: 159x75mm)" value="${value}">
        </div>
        <button type="button" class="btn bg-rose-600 hover:bg-rose-500 active:bg-rose-700 text-white btn-xs remove-spec-row">
        X
        </button>

        <!-- فیلدهای مخفی با نام‌های صحیح -->
        <input type="hidden" name="Specifications[${groupIndex}].ProductSpecifications[${specIndex}].Id" value="${specId}">
        <input type="hidden" name="Specifications[${groupIndex}].ProductSpecifications[${specIndex}].Key" value="${key}">
        <input type="hidden" name="Specifications[${groupIndex}].ProductSpecifications[${specIndex}].Value" value="${value}">
        <input type="hidden" name="Specifications[${groupIndex}].ProductSpecifications[${specIndex}].DisplayOrder" value="${displayOrder}">
        </div>
        `;
        }


        // ===== دریافت یک ردیف مشخصه پیش‌فرض =====
        getDefaultSpecRow(groupIndex, specIndex) {
        return this.getSpecRowHtml(null, groupIndex, specIndex);
        }

        // ===== افزودن مشخصه به گروه =====
        addSpecification(groupElement) {
        const groupItems = groupElement.find('.spec-group-items');
        const group = groupElement.closest('.spec-group');
        const groupIndex = group.data('group-index');
        const specCount = groupItems.find('.spec-row').length;

        const specHtml = this.getSpecRowHtml(null, groupIndex, specCount);
        groupItems.append(specHtml);
        this.updateDisplayOrders(group);
        this.updateAllInputNames();
        }
        // ===== بروزرسانی همه نام‌های اینپوت‌ها =====
        updateAllInputNames() {
        const groups = this.container.querySelectorAll('.spec-group');

        groups.forEach((group, groupIndex) => {
        // بروزرسانی ایندکس گروه در data attribute
        group.dataset.groupIndex = groupIndex;

        // بروزرسانی نام‌های فیلدهای گروه
        const groupInputs = group.querySelectorAll('input[name^="Specifications["]');
        groupInputs.forEach(input => {
        const name = input.getAttribute('name');
        // بروزرسانی ایندکس گروه
        const newName = name.replace(/Specifications\[\d+\]/, `Specifications[${groupIndex}]`);
        if (name !== newName) {
        input.setAttribute('name', newName);
        }
        });

        // بروزرسانی ایندکس مشخصات
        const specs = group.querySelectorAll('.spec-row');
        specs.forEach((spec, specIndex) => {
        spec.dataset.specIndex = specIndex;

        const specInputs = spec.querySelectorAll('input[name^="Specifications["]');
        specInputs.forEach(input => {
        let name = input.getAttribute('name');
        // بروزرسانی ایندکس گروه و مشخصه
        name = name.replace(/Specifications\[\d+\]/, `Specifications[${groupIndex}]`);
        name = name.replace(/ProductSpecifications\[\d+\]/, `ProductSpecifications[${specIndex}]`);
        input.setAttribute('name', name);
        });
        });
        });
        }
        // ===== بروزرسانی ترتیب نمایش =====
        updateDisplayOrders(groupElement = null) {
        const groups = groupElement
        ? [groupElement[0]]
        : this.container.querySelectorAll('.spec-group');

        groups.forEach((group, groupIndex) => {
        // بروزرسانی ترتیب گروه
        const orderInput = group.querySelector('input[name$=".DisplayOrder"]');
        if (orderInput) {
        const name = orderInput.getAttribute('name');
        const newName = name.replace(/Specifications\[\d+\]/, `Specifications[${groupIndex}]`);
        orderInput.setAttribute('name', newName);
        orderInput.value = groupIndex + 1;
        }

        // بروزرسانی ترتیب مشخصات
        const specs = group.querySelectorAll('.spec-row');
        specs.forEach((spec, specIndex) => {
        const orderInputSpec = spec.querySelector('input[name$=".DisplayOrder"]');
        if (orderInputSpec) {
        const name = orderInputSpec.getAttribute('name');
        const newName = name.replace(/Specifications\[\d+\]/, `Specifications[${groupIndex}]`)
        .replace(/ProductSpecifications\[\d+\]/, `ProductSpecifications[${specIndex}]`);
        orderInputSpec.setAttribute('name', newName);
        orderInputSpec.value = specIndex + 1;
        }
        });
        });
        }

        // ===== جمع‌آوری داده‌های فرم =====
        collectFormData() {
        const groups = [];
        this.container.querySelectorAll('.spec-group').forEach((group) => {
        const groupId = group.dataset.groupId;
        const groupIndex = group.dataset.groupIndex;
        const groupName = group.querySelector('.spec-group-name')?.value || '';
        const isDeleted = group.querySelector(`input[name="Specifications[${groupIndex}].IsDeleted"]`)?.value === 'true';

        if (isDeleted) return;

        const ProductSpecifications = [];
        group.querySelectorAll('.spec-row').forEach((spec) => {
        const specId = spec.dataset.specId;
        const specIndex = spec.dataset.specIndex;
        const key = spec.querySelector('.spec-key')?.value || '';
        const value = spec.querySelector('.spec-value')?.value || '';
        const isSpecDeleted = spec.querySelector(`input[name="Specifications[${groupIndex}].ProductSpecifications[${specIndex}].IsDeleted"]`)?.value === 'true';

        if (!isSpecDeleted && (key || value)) {
        ProductSpecifications.push({
        id: specId,
        key: key,
        value: value,
        displayOrder: parseInt(spec.dataset.specIndex) + 1
        });
        }
        });

        if (ProductSpecifications.length > 0) {
        groups.push({
        id: groupId,
        groupName: groupName,
        displayOrder: parseInt(groupIndex) + 1,
        ProductSpecifications: ProductSpecifications
        });
        }
        });

        return groups;
        }

        // ===== اعتبارسنجی =====
        validate() {
        const groups = this.collectFormData();
        if (groups.length === 0) {
        this.showToast('error', 'خطا', 'حداقل یک گروه مشخصات با حداقل یک مشخصه باید وجود داشته باشد.');
        return false;
        }

        for (const group of groups) {
        if (!group.groupName || group.groupName.trim() === '') {
        this.showToast('error', 'خطا', 'نام همه گروه‌ها باید پر شود.');
        return false;
        }

        for (const spec of group.ProductSpecifications) {
        if (!spec.key || spec.key.trim() === '' || !spec.value || spec.value.trim() === '') {
        this.showToast('error', 'خطا', 'عنوان و مقدار همه مشخصات باید پر شود.');
        return false;
        }
        }
        }

        return true;
        }

        // ===== نمایش Toast =====
        showToast(type, title, message) {
        const icons = {
        success: 'bi bi-check-circle-fill',
        error: 'bi bi-x-circle-fill',
        info: 'bi bi-info-circle-fill'
        };

        const toast = $(`
        <div class="toast-custom ${type}">
        <i class="${icons[type]}"></i>
        <div class="toast-body">
        <strong>${title}</strong>
        <span>${message}</span>
        </div>
        <button class="btn-close-toast"><i class="bi bi-x-lg"></i></button>
        </div>
        `);

        $('#toastContainer').append(toast);

        toast.find('.btn-close-toast').on('click', function() {
        toast.remove();
        });

        setTimeout(function() {
        toast.fadeOut(300, function() {
        $(this).remove();
        });
        }, 5000);
        }
        }

        // ===== راه‌اندازی =====
        $(document).ready(function() {
        const specManager = new ProductProductSpecificationsManager('ProductSpecificationsContainer');

        // ===== ذخیره‌سازی (ارسال به سرور) =====
        $('#productForm').on('submit', function(e) {
        e.preventDefault();

        // اعتبارسنجی
        if (!specManager.validate()) {
        return;
        }

        // جمع‌آوری داده‌ها
        const specificationData = specManager.collectFormData();

        // افزودن به داده‌های فرم
        const formData = new FormData(this);
        formData.append('Specifications', JSON.stringify(specificationData));

        // ارسال به سرور
        const btn = $('#btnSubmit');
        btn.prop('disabled', true).html(
        '<span class="spinner-border spinner-border-sm me-2"></span> در حال ذخیره...');

        $.ajax({
        url: '/api/products/save', // آدرس API شما
        method: 'POST',
        data: formData,
        processData: false,
        contentType: false,
        success: function(response) {
        specManager.showToast('success', 'موفق!', 'محصول با موفقیت ذخیره شد.');
        btn.prop('disabled', false).html('<i class="bi bi-check-lg"></i> ذخیره محصول');
        },
        error: function(xhr) {
        specManager.showToast('error', 'خطا', 'مشکلی در ذخیره‌سازی رخ داد.');
        btn.prop('disabled', false).html('<i class="bi bi-check-lg"></i> ذخیره محصول');
        }
        });
        });
        // ===== Initialize Summernote =====
        $('#fullDescription').summernote({
        placeholder: 'توضیحات کامل محصول را وارد کنید...',
        tabsize: 2,
        height: 200,
        lang: 'fa-IR',
        toolbar: [
        ['style', ['style']],
        ['font', ['bold', 'underline', 'clear']],
        ['color', ['color']],
        ['para', ['ul', 'ol', 'paragraph']],
        ['table', ['table']],
        ['insert', ['link', 'picture']],
        ['view', ['fullscreen', 'codeview', 'help']]
        ]
        });

        $('#expertReview').summernote({
        placeholder: 'بررسی تخصصی محصول را وارد کنید...',
        tabsize: 2,
        height: 200,
        lang: 'fa-IR',
        toolbar: [
        ['style', ['style']],
        ['font', ['bold', 'underline', 'clear']],
        ['color', ['color']],
        ['para', ['ul', 'ol', 'paragraph']],
        ['table', ['table']],
        ['insert', ['link', 'picture']],
        ['view', ['fullscreen', 'codeview', 'help']]
        ]
        });
        // Close sidebar when clicking outside on mobile
        $(document).on('click', function(e) {
        if ($(window).width() <= 992) {
        if (!$(e.target).closest('#sidebar').length && !$(e.target).closest('#toggleSidebar').length) {
        $('#sidebar').removeClass('open');
        }
        }
        });
    const imageManager = new ProductImagesManager({
        containerId: 'imageGrid',
        uploadBoxId: 'uploadBox',
        imageInputId: 'imageInput',
        maxImages: 10,
        maxSize: 5 * 1024 * 1024
    });

    window.imageManager = imageManager;

    $('#productForm').on('submit', function(e) {
        e.preventDefault();

        if (!imageManager.validate()) {
            return;
        }

        const imageData = imageManager.collectFormData();
        const formData = new FormData(this);
        formData.append('Images', JSON.stringify(imageData));

        const btn = $('#btnSubmit');
        btn.prop('disabled', true).html('در حال ذخیره...');

        $.ajax({
            url: '/api/products/save',
            method: 'POST',
            data: formData,
            processData: false,
            contentType: false,
            success: function(response) {
                imageManager.showToast('success', 'موفق!', 'محصول با موفقیت ذخیره شد.');
                btn.prop('disabled', false).html('ذخیره محصول');
            },
            error: function(xhr) {
                imageManager.showToast('error', 'خطا', 'مشکلی در ذخیره‌سازی رخ داد.');
                btn.prop('disabled', false).html('ذخیره محصول');
            }
        });
    });
        });

    </script>



    

}

و اینم کد جاوا بخش افزودن رنگ به محصول

 // ==add color==
$("[main_category_checkbox]").on('change', function (e) {
    var isChecked = $(this).is(':checked');
    var selectedCategoryId = $(this).attr('main_category_checkbox');
    if (isChecked) {
        $('#sub_categories_' + selectedCategoryId).slideDown(500);

    } else {
        $('#sub_categories_' + selectedCategoryId).slideUp(500);
        $('[parent-category-id="' + selectedCategoryId + '"]').prop('checked', false);
    }
});
$('#add_color').on('click', function (e) {
    e.preventDefault;
    var colorName = $('#color_name_input').val();
    var colorHex = $('#color_hex_input').val();
    var colorPrice = $('#color_price_input').val();

    if (colorName !== '' && colorPrice !== '') {
        var curentColorCunt = $('#list_of_product_Color tr');
        var items = curentColorCunt.length;
        //add to from
        var colorNameNode = `<input type="hidden" value="${colorName}" name="ProductVariant[${items}].VariantName" color-name-hidden-input="${colorName}-${colorHex}-${colorPrice}">`;
        var colorHexNode = `<input type="hidden" value="${colorHex}" name="ProductVariant[${items}].ColorCode" color-hex-hidden-input="${colorName}-${colorHex}-${colorPrice}" >`;
        var colorPriceNode = `<input type="hidden" value="${colorPrice}" name="ProductVariant[${items}].PriceAdjustment" color-price-hidden-input="${colorName}-${colorHex}-${colorPrice}">`;
        $('#productForm').append(colorNameNode);
        $('#productForm').append(colorHexNode);
        $('#productForm').append(colorPriceNode);
        //add to table
        var colorTableNode = `<tr color-table-item="${colorName}-${colorHex}-${colorPrice}"  role="row" class="odd"> <td> ${colorName} </td>  <td>  <input type="color"  class="input w-10" value="${colorHex}">     </td>     <td> ${colorPrice} </td>    <td>   <div class="flex justify-end text-lg"> <a  onclick="hazfradif('${colorName}-${colorHex}-${colorPrice}')" class="cursor-pointer p-2 hover:text-red-500" > <svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"> <path stroke-linecap="round" stroke-linejoin="round" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"> </path> </svg> </a> </div> </td>    </tr>`;
        $('#list_of_product_Color').append(colorTableNode);
        $('#color_name_input').val('');
        $('#color_hex_input').val('#000000');
        $('#color_price_input').val('');
    }
    else {
        showToast('error', 'خطا', 'لطفا نام رنگ و تفاوت قیمت را وارد کنید.');
    }
});
function hazfradif(index) {
    $('[color-name-hidden-input="' + index + '"]').remove();
    $('[color-hex-hidden-input="' + index + '"]').remove();
    $('[color-price-hidden-input="' + index + '"]').remove();
    $('[color-table-item="' + index + '"]').remove();

    }

و اینم کد بخش استایل ها هستش


:root {
    --primary-color: #6c8cff;
    --primary-dark: #4a6cf7;
    --primary-light: #1e2a4a;
    --bg-body: #0d1117;
    --bg-surface: #161b22;
    --bg-elevated: #1c2333;
    --border-color: #2d3748;
    --text-primary: #f0f6fc;
    --text-secondary: #8b949e;
    --text-muted: #6e7681;
    --sidebar-width: 280px;
    --header-height: 70px;
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'IRANSans', 'Vazir', Tahoma, Arial, sans-serif;
    background: var(--bg-body);
    color: var(--text-primary);
}

.app-wrapper {
    display: flex;
    min-height: 100vh;
}

/* ===== Page Content ===== */
.page-content {
    padding: 24px 28px;
    flex: 1;
}

/* ===== Form Cards ===== */
.form-card {
    background: var(--bg-surface);
    border-radius: 16px;
    padding: 24px;
    margin-bottom: 24px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
    border: 1px solid var(--border-color);
}

    .form-card .card-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 20px;
        padding-bottom: 16px;
        border-bottom: 1px solid var(--border-color);
    }

        .form-card .card-header h5 {
            font-size: 16px;
            font-weight: 600;
            margin: 0;
            color: var(--text-primary);
        }

        .form-card .card-header .subtitle {
            font-size: 13px;
            color: var(--text-muted);
            font-weight: 400;
        }

.form-label {
    font-size: 13px;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 6px;
}

    .form-label .required {
        color: #f85149;
    }

.form-control,
.form-select {
    border-radius: 10px;
    border: 1px solid var(--border-color);
    padding: 10px 14px;
    font-size: 14px;
    transition: all 0.2s;
    background: var(--bg-elevated);
    color: var(--text-primary);
}

    .form-control::placeholder {
        color: var(--text-muted);
    }

    .form-control:focus,
    .form-select:focus {
        border-color: var(--primary-color);
        box-shadow: 0 0 0 3px rgba(108, 140, 255, 0.15);
        background: var(--bg-elevated);
        color: var(--text-primary);
    }

    .form-control.is-invalid,
    .form-select.is-invalid {
        border-color: #f85149;
    }

.form-text {
    font-size: 12px;
    color: var(--text-muted);
}

.input-group-text {
    background: var(--bg-elevated);
    border: 1px solid var(--border-color);
    border-radius: 10px 0 0 10px;
    font-size: 13px;
    color: var(--text-secondary);
}

.input-group .form-control {
    border-radius: 0 10px 10px 0;
}

    .input-group .form-control:first-child {
        border-radius: 10px 0 0 10px;
    }
/* ===== تصاویر - ۲ عدد در هر ردیف ===== */

.image-upload-grid {
    display: grid;
    grid-template-columns: repeat(2, 1fr); /* ۲ ستون با عرض مساوی */
    gap: 16px;
}

.image-item {
    position: relative;
    border-radius: 10px;
    overflow: hidden;
    border: 2px solid var(--border-color);
    aspect-ratio: 1;
    background: var(--bg-elevated);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    min-height: 150px; /* افزایش ارتفاع */
}

    .image-item .image-wrapper {
        position: relative;
        width: 100%;
        height: 100%;
        border-radius: 8px;
        overflow: hidden;
    }

    .image-item img {
        width: 100%;
        height: 100%;
        object-fit: cover;
    }

    .image-item:hover {
        border-color: var(--primary-color);
        transform: scale(1.02);
        z-index: 5;
        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
    }

    /* ===== برچسب‌ها ===== */
    .image-item .image-badges {
        position: absolute;
        top: 6px;
        right: 6px;
        display: flex;
        flex-direction: column;
        gap: 3px;
        z-index: 3;
    }

        .image-item .image-badges .badge-main {
            background: var(--primary-color);
            color: #fff;
            font-size: 9px;
            padding: 2px 12px;
            border-radius: 12px;
            font-weight: 700;
            letter-spacing: 0.3px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
        }

        .image-item .image-badges .badge-order {
            background: rgba(0, 0, 0, 0.7);
            color: #fff;
            font-size: 9px;
            padding: 2px 10px;
            border-radius: 12px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
        }

    /* ===== دکمه‌های آیکون داخل کادر ===== */
    .image-item .image-actions-always {
        position: absolute;
        left: 8px; /* سمت راست */
        top: 50%;
        transform: translateY(-50%);
        display: flex;
        flex-direction: column;
        gap: 6px;
        z-index: 10;
    }

        .image-item .image-actions-always button {
            width: 32px;
            height: 32px;
            border: none;
            border-radius: 50%;
            cursor: pointer;
            transition: all 0.2s ease;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0;
            background: rgba(0, 0, 0, 0.65);
            color: rgba(255, 255, 255, 0.8);
            backdrop-filter: blur(4px);
            border: 1px solid rgba(255, 255, 255, 0.08);
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
        }

            .image-item .image-actions-always button:hover {
                transform: scale(1.12);
                color: #fff;
                background: rgba(0, 0, 0, 0.85);
            }

            .image-item .image-actions-always button svg {
                width: 16px;
                height: 16px;
                stroke: currentColor;
                fill: none;
                stroke-width: 2;
                stroke-linecap: round;
                stroke-linejoin: round;
            }

        /* ===== آیکون ستاره (تصویر اصلی) ===== */
        .image-item .image-actions-always .btn-star {
            color: rgba(255, 215, 0, 0.7);
        }

            .image-item .image-actions-always .btn-star:hover {
                color: #ffd700;
                background: rgba(255, 215, 0, 0.2);
            }

            .image-item .image-actions-always .btn-star.active {
                color: #ffd700;
                background: rgba(255, 215, 0, 0.25);
                box-shadow: 0 0 20px rgba(255, 215, 0, 0.3);
            }

        /* ===== آیکون چشم (مشاهده) ===== */
        .image-item .image-actions-always .btn-eye {
            color: rgba(255, 255, 255, 0.6);
        }

            .image-item .image-actions-always .btn-eye:hover {
                color: #fff;
                background: rgba(255, 255, 255, 0.15);
            }

        /* ===== آیکون حذف ===== */
        .image-item .image-actions-always .btn-trash {
            color: rgba(248, 81, 73, 0.7);
        }

            .image-item .image-actions-always .btn-trash:hover {
                color: #f85149;
                background: rgba(248, 81, 73, 0.2);
            }

    /* ===== تصویر اصلی ===== */
    .image-item.is-main {
        border-color: var(--primary-color);
        box-shadow: 0 0 25px rgba(108, 140, 255, 0.2);
    }

/* ===== باکس آپلود ===== */
.upload-box {
    border: 2px dashed var(--border-color);
    border-radius: 10px;
    aspect-ratio: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all 0.2s ease;
    background: var(--bg-elevated);
    min-height: 150px;
}

    .upload-box:hover {
        border-color: var(--primary-color);
        background: var(--primary-light);
        transform: scale(1.02);
    }

    .upload-box .upload-content {
        display: flex;
        flex-direction: column;
        align-items: center;
        gap: 6px;
        padding: 12px;
    }

        .upload-box .upload-content svg {
            width: 40px;
            height: 40px;
            color: var(--text-muted);
        }

        .upload-box .upload-content span {
            font-size: 13px;
            color: var(--text-muted);
            font-weight: 500;
        }

        .upload-box .upload-content input {
            display: none;
        }

    .upload-box.dragover {
        border-color: var(--primary-color);
        background: var(--primary-light);
        transform: scale(1.02);
    }

/* ===== پاسخگو برای موبایل ===== */
@media (max-width: 768px) {
    .image-upload-grid {
        grid-template-columns: repeat(2, 1fr);
        gap: 12px;
    }

    .image-item {
        min-height: 120px;
    }

        .image-item .image-actions-always button {
            width: 28px;
            height: 28px;
        }

            .image-item .image-actions-always button svg {
                width: 14px;
                height: 14px;
            }

        .image-item .image-actions-always {
            left: 6px;
            gap: 4px;
        }

    .upload-box {
        min-height: 120px;
    }

        .upload-box .upload-content svg {
            width: 32px;
            height: 32px;
        }

        .upload-box .upload-content span {
            font-size: 11px;
        }
}

@media (max-width: 480px) {
    .image-upload-grid {
        gap: 8px;
    }

    .image-item {
        min-height: 100px;
    }

        .image-item .image-actions-always button {
            width: 24px;
            height: 24px;
        }

            .image-item .image-actions-always button svg {
                width: 12px;
                height: 12px;
            }

        .image-item .image-actions-always {
            left: 4px;
            gap: 3px;
        }

        .image-item .image-badges .badge-main {
            font-size: 7px;
            padding: 1px 8px;
        }

        .image-item .image-badges .badge-order {
            font-size: 7px;
            padding: 1px 6px;
        }

    .upload-box {
        min-height: 100px;
    }

        .upload-box .upload-content svg {
            width: 28px;
            height: 28px;
        }

        .upload-box .upload-content span {
            font-size: 10px;
        }
}
/*  Modal  */
/* ===== مودال مینیمال ===== */

#imageModal .modal-content {
    background: var(--bg-surface);
    border: none;
    border-radius: 16px;
    overflow: hidden;
    box-shadow: 0 25px 80px rgba(0, 0, 0, 0.8);
}

/* ===== هدر مینیمال ===== */
#imageModal .modal-header {
    padding: 16px 24px;
    border-bottom: 1px solid rgba(255, 255, 255, 0.06);
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: var(--bg-elevated);
}

#imageModal .modal-header-info {
    display: flex;
    align-items: center;
    gap: 14px;
}

#imageModal .image-counter {
    background: rgba(255, 255, 255, 0.08);
    color: var(--text-secondary);
    padding: 2px 14px;
    border-radius: 20px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.3px;
}

#imageModal .image-name {
    color: var(--text-primary);
    font-size: 14px;
    font-weight: 400;
    max-width: 300px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    opacity: 0.8;
}

#imageModal .btn-close-modal {
    background: none;
    border: none;
    color: var(--text-secondary);
    padding: 4px;
    cursor: pointer;
    transition: all 0.3s ease;
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
}

    #imageModal .btn-close-modal:hover {
        background: rgba(255, 255, 255, 0.06);
        color: var(--text-primary);
        transform: rotate(90deg);
    }

    #imageModal .btn-close-modal svg {
        width: 20px;
        height: 20px;
        stroke: currentColor;
        stroke-width: 1.5;
    }

/* ===== بدنه مودال ===== */
#imageModal .modal-body {
    padding: 0;
    position: relative;
    background: var(--bg-body);
    min-height: 450px;
    display: flex;
    align-items: center;
    justify-content: center;
}

#imageModal .image-viewer {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 30px;
}

    #imageModal .image-viewer img {
        max-width: 100%;
        max-height: 75vh;
        object-fit: contain;
        border-radius: 8px;
        box-shadow: 0 4px 40px rgba(0, 0, 0, 0.2);
        transition: all 0.3s ease;
    }

/* ===== دکمه‌های ناوبری ===== */
#imageModal .nav-btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    width: 44px;
    height: 44px;
    border: none;
    border-radius: 50%;
    background: rgba(0, 0, 0, 0.5);
    color: #fff;
    cursor: pointer;
    transition: all 0.3s ease;
    backdrop-filter: blur(12px);
    border: 1px solid rgba(255, 255, 255, 0.06);
    display: flex;
    align-items: center;
    justify-content: center;
    z-index: 10;
    opacity: 0.6;
}

    #imageModal .nav-btn:hover {
        background: rgba(0, 0, 0, 0.8);
        transform: translateY(-50%) scale(1.05);
        opacity: 1;
    }

    #imageModal .nav-btn:active {
        transform: translateY(-50%) scale(0.95);
    }

#imageModal .nav-prev {
    left: 20px;
}

#imageModal .nav-next {
    right: 20px;
}

#imageModal .nav-btn svg {
    width: 20px;
    height: 20px;
    stroke: currentColor;
    stroke-width: 2;
}

/* ===== فوتر مینیمال ===== */
#imageModal .modal-footer {
    padding: 12px 24px;
    border-top: 1px solid rgba(255, 255, 255, 0.06);
    background: var(--bg-elevated);
    justify-content: center;
}

#imageModal .footer-actions {
    display: flex;
    align-items: center;
    gap: 6px;
}

    #imageModal .footer-actions .divider {
        width: 1px;
        height: 24px;
        background: rgba(255, 255, 255, 0.06);
    }

#imageModal .action-btn {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 16px;
    border: none;
    border-radius: 10px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s ease;
    background: transparent;
    color: var(--text-secondary);
}

    #imageModal .action-btn:hover {
        background: rgba(255, 255, 255, 0.04);
        color: var(--text-primary);
    }

    #imageModal .action-btn svg {
        width: 16px;
        height: 16px;
        stroke: currentColor;
        fill: none;
        stroke-width: 1.8;
        stroke-linecap: round;
        stroke-linejoin: round;
    }

    /* ===== دکمه تصویر اصلی ===== */
    #imageModal .action-btn.action-set-main {
        color: rgba(255, 215, 0, 0.6);
    }

        #imageModal .action-btn.action-set-main:hover {
            background: rgba(255, 215, 0, 0.08);
            color: #ffd700;
        }

        #imageModal .action-btn.action-set-main.active {
            color: #ffd700;
            background: rgba(255, 215, 0, 0.12);
        }

            #imageModal .action-btn.action-set-main.active:hover {
                background: rgba(255, 215, 0, 0.18);
            }

    /* ===== دکمه حذف ===== */
    #imageModal .action-btn.action-delete {
        color: rgba(248, 81, 73, 0.6);
    }

        #imageModal .action-btn.action-delete:hover {
            background: rgba(248, 81, 73, 0.08);
            color: #f85149;
        }

    /* ===== دکمه بستن ===== */
    #imageModal .action-btn.action-close {
        color: var(--text-muted);
    }

        #imageModal .action-btn.action-close:hover {
            background: rgba(255, 255, 255, 0.04);
            color: var(--text-primary);
        }

/* ===== انیمیشن ===== */
#imageModal .modal-content {
    animation: modalFadeIn 0.25s ease;
}

@keyframes modalFadeIn {
    from {
        opacity: 0;
        transform: scale(0.96) translateY(16px);
    }

    to {
        opacity: 1;
        transform: scale(1) translateY(0);
    }
}

/* ===== پیشرفت تصویر (لودینگ) ===== */
#imageModal .image-viewer img {
    opacity: 0;
    animation: imageFadeIn 0.4s ease 0.1s forwards;
}

@keyframes imageFadeIn {
    from {
        opacity: 0;
        transform: scale(0.98);
    }

    to {
        opacity: 1;
        transform: scale(1);
    }
}

/* ===== پاسخگو ===== */
@media (max-width: 768px) {
    #imageModal .modal-dialog {
        margin: 10px;
    }

    #imageModal .modal-content {
        border-radius: 12px;
    }

    #imageModal .modal-header {
        padding: 12px 16px;
    }

    #imageModal .image-name {
        font-size: 12px;
        max-width: 120px;
    }

    #imageModal .image-counter {
        font-size: 11px;
        padding: 1px 12px;
    }

    #imageModal .image-viewer {
        padding: 16px;
    }

        #imageModal .image-viewer img {
            max-height: 55vh;
        }

    #imageModal .nav-btn {
        width: 36px;
        height: 36px;
    }

    #imageModal .nav-prev {
        left: 10px;
    }

    #imageModal .nav-next {
        right: 10px;
    }

    #imageModal .nav-btn svg {
        width: 16px;
        height: 16px;
    }

    #imageModal .modal-footer {
        padding: 10px 16px;
    }

    #imageModal .action-btn {
        padding: 6px 12px;
        font-size: 11px;
    }

        #imageModal .action-btn span {
            display: none;
        }

        #imageModal .action-btn svg {
            width: 18px;
            height: 18px;
        }

    #imageModal .footer-actions .divider {
        display: none;
    }
}

@media (max-width: 480px) {
    #imageModal .modal-header {
        padding: 10px 12px;
    }

    #imageModal .image-name {
        font-size: 11px;
        max-width: 80px;
    }

    #imageModal .image-viewer {
        padding: 12px;
    }

        #imageModal .image-viewer img {
            max-height: 45vh;
        }

    #imageModal .nav-btn {
        width: 30px;
        height: 30px;
    }

    #imageModal .nav-prev {
        left: 6px;
    }

    #imageModal .nav-next {
        right: 6px;
    }

    #imageModal .nav-btn svg {
        width: 14px;
        height: 14px;
    }

    #imageModal .modal-footer {
        padding: 8px 12px;
    }
}
/* ===== Variants ===== */
.variant-row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr auto auto;
    gap: 12px;
    align-items: end;
    padding: 16px;
    background: var(--bg-elevated);
    border-radius: 12px;
    margin-bottom: 12px;
}

    .variant-row .form-group {
        margin-bottom: 0;
    }

    .variant-row .remove-variant {
        background: none;
        border: none;
        color: #f85149;
        font-size: 20px;
        padding: 8px;
        cursor: pointer;
        margin-bottom: 4px;
    }

        .variant-row .remove-variant:hover {
            transform: scale(1.2);
        }

.color-picker {
    display: flex;
    align-items: center;
    gap: 8px;
}

    .color-picker input[type="color"] {
        width: 40px;
        height: 40px;
        border: 2px solid var(--border-color);
        border-radius: 8px;
        padding: 2px;
        cursor: pointer;
        background: var(--bg-elevated);
    }

/* ===== Specifications ===== */
.spec-group {
    background: var(--bg-elevated);
    border-radius: 12px;
    padding: 16px;
    margin-bottom: 16px;
}

    .spec-group .spec-header {
        display: flex;
        align-items: center;
        justify-content: space-between;
        margin-bottom: 12px;
    }

        .spec-group .spec-header h6 {
            margin: 0;
            font-size: 14px;
            font-weight: 600;
            color: var(--text-primary);
        }

.spec-row {
    display: grid;
    grid-template-columns: 1fr 2fr auto;
    gap: 12px;
    align-items: center;
    margin-bottom: 8px;
    padding: 8px;
    background: var(--bg-surface);
    border-radius: 8px;
    border: 1px solid var(--border-color);
}

    .spec-row .form-group {
        margin-bottom: 0;
    }

/* ===== Suppliers ===== */
.supplier-row {
    display: grid;
    grid-template-columns: 1fr 1fr 1fr 1fr auto;
    gap: 12px;
    align-items: end;
    padding: 16px;
    background: var(--bg-elevated);
    border-radius: 12px;
    margin-bottom: 12px;
}

    .supplier-row .form-group {
        margin-bottom: 0;
    }

/* ===== Footer Actions ===== */
.form-actions {
    background: var(--bg-surface);
    border-top: 1px solid var(--border-color);
    padding: 16px 28px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: sticky;
    bottom: 0;
    z-index: 50;
    margin: 0 -28px;
    padding-left: 28px;
    padding-right: 28px;
}

    .form-actions .btn {
        padding: 10px 24px;
        border-radius: 10px;
        font-weight: 600;
        font-size: 14px;
    }

    .form-actions .btn-outline-danger {
        color: #f85149;
        border-color: #f85149;
    }

        .form-actions .btn-outline-danger:hover {
            background: #f85149;
            color: #fff;
        }

    .form-actions .btn-outline-secondary {
        color: var(--text-secondary);
        border-color: var(--border-color);
    }

        .form-actions .btn-outline-secondary:hover {
            background: var(--bg-elevated);
            color: var(--text-primary);
            border-color: var(--text-secondary);
        }

    .form-actions .btn-outline-primary {
        color: var(--primary-color);
        border-color: var(--primary-color);
    }

        .form-actions .btn-outline-primary:hover {
            background: var(--primary-color);
            color: #fff;
        }

    .form-actions .btn-primary {
        background: var(--primary-color);
        border-color: var(--primary-color);
        color: #fff;
    }

        .form-actions .btn-primary:hover {
            background: var(--primary-dark);
            border-color: var(--primary-dark);
        }

/* ===== Toast ===== */
.toast-container {
    position: fixed;
    top: 20px;
    left: 20px;
    z-index: 9999;
}

.toast-custom {
    background: var(--bg-surface);
    border-radius: 12px;
    padding: 16px 20px;
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.5);
    border-right: 4px solid var(--primary-color);
    display: flex;
    align-items: center;
    gap: 12px;
    min-width: 300px;
    margin-bottom: 12px;
    animation: slideIn 0.4s ease;
    border: 1px solid var(--border-color);
}

    .toast-custom.success {
        border-right-color: #3fb950;
    }

    .toast-custom.error {
        border-right-color: #f85149;
    }

    .toast-custom i {
        font-size: 24px;
    }

    .toast-custom.success i {
        color: #3fb950;
    }

    .toast-custom.error i {
        color: #f85149;
    }

    .toast-custom .toast-body {
        flex: 1;
    }

        .toast-custom .toast-body strong {
            display: block;
            font-size: 14px;
            color: var(--text-primary);
        }

        .toast-custom .toast-body span {
            font-size: 13px;
            color: var(--text-secondary);
        }

    .toast-custom .btn-close-toast {
        background: none;
        border: none;
        font-size: 18px;
        color: var(--text-muted);
        cursor: pointer;
        padding: 4px;
    }

        .toast-custom .btn-close-toast:hover {
            color: var(--text-primary);
        }


/* Scrollbar */
.sidebar::-webkit-scrollbar {
    width: 4px;
}

.sidebar::-webkit-scrollbar-track {
    background: var(--bg-body);
}

.sidebar::-webkit-scrollbar-thumb {
    background: var(--border-color);
    border-radius: 10px;
}

/* Summernote customization */
.note-editor {
    border-radius: 10px !important;
    border-color: var(--border-color) !important;
    background: var(--bg-elevated) !important;
}

    .note-editor .note-toolbar {
        background: var(--bg-surface) !important;
        border-radius: 10px 10px 0 0 !important;
        border-bottom-color: var(--border-color) !important;
    }

        .note-editor .note-toolbar .btn {
            background: transparent !important;
            color: var(--text-secondary) !important;
        }

            .note-editor .note-toolbar .btn:hover {
                background: var(--bg-elevated) !important;
                color: var(--text-primary) !important;
            }

        .note-editor .note-toolbar .dropdown-menu {
            background: var(--bg-surface) !important;
            border-color: var(--border-color) !important;
        }

        .note-editor .note-toolbar .dropdown-item {
            color: var(--text-secondary) !important;
        }

            .note-editor .note-toolbar .dropdown-item:hover {
                background: var(--bg-elevated) !important;
                color: var(--text-primary) !important;
            }

    .note-editor .note-editable {
        min-height: 200px !important;
        color: var(--text-primary) !important;
        background: var(--bg-elevated) !important;
    }

        .note-editor .note-editable.note-placeholder {
            color: var(--text-muted) !important;
        }

    .note-editor .note-statusbar {
        background: var(--bg-surface) !important;
        border-top-color: var(--border-color) !important;
    }

        .note-editor .note-statusbar .note-resizebar {
            background: var(--border-color) !important;
        }

/* Select2 Dark */
.select2-container--bootstrap-5 .select2-selection {
    border-radius: 10px !important;
    border-color: var(--border-color) !important;
    min-height: 46px !important;
    background: var(--bg-elevated) !important;
}

.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered {
    padding: 6px 12px !important;
}

.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice {
    background: var(--primary-light) !important;
    border: none !important;
    color: var(--primary-color) !important;
    font-size: 12px !important;
    padding: 2px 10px !important;
    border-radius: 20px !important;
}

.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__choice__remove {
    color: var(--primary-color) !important;
}

.select2-container--bootstrap-5 .select2-search__field {
    color: var(--text-primary) !important;
}

.select2-container--bootstrap-5 .select2-dropdown {
    background: var(--bg-surface) !important;
    border-color: var(--border-color) !important;
}

.select2-container--bootstrap-5 .select2-results__option {
    color: var(--text-secondary) !important;
}

.select2-container--bootstrap-5 .select2-results__option--highlighted {
    background: var(--primary-light) !important;
    color: var(--primary-color) !important;
}

/* Footer */
.footer {
    padding: 12px 28px;
    border-top: 1px solid var(--border-color);
    background: var(--bg-surface);
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-size: 13px;
    color: var(--text-muted);
}

    .footer a {
        color: var(--text-muted);
        text-decoration: none;
    }

        .footer a:hover {
            color: var(--primary-color);
        }

/* Additional dark overrides */
.btn-sm {
    border-radius: 8px;
}

    .btn-sm.btn-primary {
        background: var(--primary-color);
        border-color: var(--primary-color);
        color: #fff;
    }

        .btn-sm.btn-primary:hover {
            background: var(--primary-dark);
            border-color: var(--primary-dark);
        }

    .btn-sm.btn-outline-primary {
        color: var(--primary-color);
        border-color: var(--primary-color);
    }

        .btn-sm.btn-outline-primary:hover {
            background: var(--primary-color);
            color: #fff;
        }

    .btn-sm.btn-outline-danger {
        color: #f85149;
        border-color: #f85149;
    }

        .btn-sm.btn-outline-danger:hover {
            background: #f85149;
            color: #fff;
        }

.btn-outline-secondary {
    color: var(--text-secondary);
    border-color: var(--border-color);
}

    .btn-outline-secondary:hover {
        background: var(--bg-elevated);
        color: var(--text-primary);
        border-color: var(--text-secondary);
    }

.form-select option {
    background: var(--bg-elevated);
    color: var(--text-primary);
}

.form-select optgroup {
    background: var(--bg-surface);
    color: var(--text-secondary);
}

/* Override Bootstrap table-like elements if any */
.table {
    color: var(--text-primary);
    border-color: var(--border-color);
}

    .table th,
    .table td {
        border-color: var(--border-color);
    }

.invalid-feedback {
    color: #f85149;
}

/* Summernote modal fix */
.modal-content {
    background: var(--bg-surface) !important;
    border-color: var(--border-color) !important;
    color: var(--text-primary) !important;
}

.modal-header,
.modal-footer {
    border-color: var(--border-color) !important;
}

    .modal-header .btn-close {
        filter: invert(1);
    }

.note-modal .note-modal-content {
    background: var(--bg-surface) !important;
}

.note-modal .note-modal-header {
    background: var(--bg-elevated) !important;
    border-bottom-color: var(--border-color) !important;
}

.note-modal .note-modal-footer {
    background: var(--bg-elevated) !important;
    border-top-color: var(--border-color) !important;
}

.note-modal .note-modal-title {
    color: var(--text-primary) !important;
}

.note-modal .note-btn {
    background: var(--bg-elevated) !important;
    color: var(--text-secondary) !important;
    border-color: var(--border-color) !important;
}

.note-modal .note-btn-primary {
    background: var(--primary-color) !important;
    color: #fff !important;
}


  • 1405/06/13
  • ساعت 12:52

بسیار عالی 

بهتر بود تصویر خروجی هم قرار میدادید