diff --git a/libs/BackOffice.BFF.Common.Protobuf.dll b/libs/BackOffice.BFF.Common.Protobuf.dll
index c58dbc9..9be5fc3 100644
Binary files a/libs/BackOffice.BFF.Common.Protobuf.dll and b/libs/BackOffice.BFF.Common.Protobuf.dll differ
diff --git a/libs/BackOffice.BFF.DiscountProduct.Protobuf.dll b/libs/BackOffice.BFF.DiscountProduct.Protobuf.dll
index e967bd9..717621d 100644
Binary files a/libs/BackOffice.BFF.DiscountProduct.Protobuf.dll and b/libs/BackOffice.BFF.DiscountProduct.Protobuf.dll differ
diff --git a/src/BackOffice/Pages/DiscountShop/Components/ProductFormDialog.razor b/src/BackOffice/Pages/DiscountShop/Components/ProductFormDialog.razor
index c2e0b54..6dc8770 100644
--- a/src/BackOffice/Pages/DiscountShop/Components/ProductFormDialog.razor
+++ b/src/BackOffice/Pages/DiscountShop/Components/ProductFormDialog.razor
@@ -1,121 +1,155 @@
@using BackOffice.Services.DiscountProduct
@using BackOffice.Services.DiscountCategory
+@using Microsoft.AspNetCore.Components.Forms
+@using Tizzani.MudBlazor.HtmlEditor
+@using BackOffice.Common.BaseComponents
+@using BackOffice.Pages.AutoComplete
@inject ISnackbar Snackbar
-
-
-
-
+
+
+ @if (!string.IsNullOrWhiteSpace(_mainImagePreview))
+ {
+
+ }
+ else if (!string.IsNullOrEmpty(Model.ImagePath))
+ {
+
+ }
+ else
+ {
+
+ تصویری انتخاب نشده است
+
+ }
-
-
-
+
+
+
+ انتخاب تصویر اصلی
+
+
+
+ @if (context != null)
+ {
+
+
+ @context.Name
+
+
+ }
+ else
+ {
+ فایلی انتخاب نشده
+ }
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- @foreach (var category in _categories)
- {
- @GetCategoryPath(category)
- }
-
-
-
-
-
-
-
-
-
-
-
- @if (!string.IsNullOrEmpty(Model.ThumbnailPath))
- {
-
-
+
+
+
- }
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ دستهبندیها
+
+ @if (_selectedCategoryIds?.Count > 0)
+ {
+
+ تعداد دستهبندیهای انتخابشده: @_selectedCategoryIds.Count
+
+ }
+
+
+
+ تصویر بندانگشتی
+
+
+
+ انتخاب تصویر بندانگشتی (اختیاری)
+
+
+
+
+
+
+
- انصراف
+ انصراف
- @if (_loading)
- {
-
- }
- else
- {
- @(IsEditMode ? "ذخیره تغییرات" : "ایجاد محصول")
- }
-
+ Disabled="@(!_isValid || _loading)">ثبت
@@ -128,56 +162,35 @@
private MudForm? _form;
private bool _isValid;
private bool _loading;
- private List _categories = new();
- private IEnumerable _selectedCategoryIds = new List();
+ private IBrowserFile? _mainImageFile;
+ private IBrowserFile? _thumbnailImageFile;
+ private readonly long _maxAllowedSize = (1024 * 1024) * 5;
+ private string? _mainImagePreview;
+ private List _selectedCategoryIds = new();
protected override async Task OnInitializedAsync()
{
- await LoadCategories();
-
if (Model.CategoryIds?.Any() == true)
{
_selectedCategoryIds = Model.CategoryIds;
}
}
- private async Task LoadCategories()
+ private async Task OnMainImageSelected(IBrowserFile? file)
{
- try
+ _mainImageFile = file;
+ if (file != null)
{
- var tree = await CategoryService.GetCategoriesAsync(isActive: true);
- _categories = FlattenCategories(tree);
- }
- catch (Exception ex)
- {
- Snackbar.Add($"خطا در بارگذاری دستهبندیها: {ex.Message}", Severity.Error);
+ var buffer = new byte[file.Size];
+ await file.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
+ _mainImagePreview = $"data:{file.ContentType};base64," + Convert.ToBase64String(buffer);
+ StateHasChanged();
}
}
- private List FlattenCategories(List categories, string prefix = "")
+ private async Task OnThumbnailSelected(IBrowserFile? file)
{
- var result = new List();
- foreach (var category in categories)
- {
- var catCopy = new DiscountCategoryDto
- {
- Id = category.Id,
- Title = prefix + category.Title,
- ParentCategoryId = category.ParentCategoryId
- };
- result.Add(catCopy);
-
- if (category.Children.Any())
- {
- result.AddRange(FlattenCategories(category.Children.ToList(), prefix + " "));
- }
- }
- return result;
- }
-
- private string GetCategoryPath(DiscountCategoryDto category)
- {
- return category.Title;
+ _thumbnailImageFile = file;
}
private async Task Submit()
@@ -193,6 +206,26 @@
// Update CategoryIds from selected values
Model.CategoryIds = _selectedCategoryIds.ToList();
+ // Handle main image upload
+ if (_mainImageFile != null)
+ {
+ var buffer = new byte[_mainImageFile.Size];
+ await _mainImageFile.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
+ Model.ImageFile = buffer;
+ Model.ImageMime = _mainImageFile.ContentType;
+ Model.ImageFileName = Path.GetFileNameWithoutExtension(_mainImageFile.Name);
+ }
+
+ // Handle thumbnail upload
+ if (_thumbnailImageFile != null)
+ {
+ var buffer = new byte[_thumbnailImageFile.Size];
+ await _thumbnailImageFile.OpenReadStream(_maxAllowedSize).ReadAsync(buffer);
+ Model.ThumbnailFile = buffer;
+ Model.ThumbnailMime = _thumbnailImageFile.ContentType;
+ Model.ThumbnailFileName = Path.GetFileNameWithoutExtension(_thumbnailImageFile.Name);
+ }
+
MudDialog.Close(DialogResult.Ok(Model));
_loading = false;
}
@@ -211,9 +244,16 @@
public string? ThumbnailPath { get; set; }
public long Price { get; set; }
public int MaxDiscountPercent { get; set; }
- public int InitialCount { get; set; }
public int SortOrder { get; set; }
public bool IsActive { get; set; } = true;
public List? CategoryIds { get; set; }
+
+ // Image upload properties
+ public byte[]? ImageFile { get; set; }
+ public string? ImageMime { get; set; }
+ public string? ImageFileName { get; set; }
+ public byte[]? ThumbnailFile { get; set; }
+ public string? ThumbnailMime { get; set; }
+ public string? ThumbnailFileName { get; set; }
}
}
diff --git a/src/BackOffice/Pages/DiscountShop/DiscountProductsMainPage.razor b/src/BackOffice/Pages/DiscountShop/DiscountProductsMainPage.razor
index 9ab4344..f723dcd 100644
--- a/src/BackOffice/Pages/DiscountShop/DiscountProductsMainPage.razor
+++ b/src/BackOffice/Pages/DiscountShop/DiscountProductsMainPage.razor
@@ -232,10 +232,16 @@
ThumbnailPath = formData.ThumbnailPath,
Price = formData.Price,
MaxDiscountPercent = formData.MaxDiscountPercent,
- InitialCount = formData.InitialCount,
+ InitialCount = 0, // Always 0 - must use inventory management
SortOrder = formData.SortOrder,
IsActive = formData.IsActive,
- CategoryIds = formData.CategoryIds
+ CategoryIds = formData.CategoryIds,
+ ImageFile = formData.ImageFile,
+ ImageMime = formData.ImageMime,
+ ImageFileName = formData.ImageFileName,
+ ThumbnailFile = formData.ThumbnailFile,
+ ThumbnailMime = formData.ThumbnailMime,
+ ThumbnailFileName = formData.ThumbnailFileName
};
await DiscountProductService.CreateAsync(dto);
@@ -269,7 +275,6 @@
ThumbnailPath = product.ThumbnailPath,
Price = product.Price,
MaxDiscountPercent = product.MaxDiscountPercent,
- InitialCount = product.RemainingCount,
SortOrder = product.SortOrder,
IsActive = product.IsActive,
CategoryIds = product.Categories?.Select(c => c.Id).ToList() ?? new()
@@ -298,7 +303,13 @@
MaxDiscountPercent = formData.MaxDiscountPercent,
SortOrder = formData.SortOrder,
IsActive = formData.IsActive,
- CategoryIds = formData.CategoryIds
+ CategoryIds = formData.CategoryIds,
+ ImageFile = formData.ImageFile,
+ ImageMime = formData.ImageMime,
+ ImageFileName = formData.ImageFileName,
+ ThumbnailFile = formData.ThumbnailFile,
+ ThumbnailMime = formData.ThumbnailMime,
+ ThumbnailFileName = formData.ThumbnailFileName
};
await DiscountProductService.UpdateAsync(productId, dto);
diff --git a/src/BackOffice/Services/DiscountProduct/DiscountProductService.cs b/src/BackOffice/Services/DiscountProduct/DiscountProductService.cs
index 06b6a86..7f2b711 100644
--- a/src/BackOffice/Services/DiscountProduct/DiscountProductService.cs
+++ b/src/BackOffice/Services/DiscountProduct/DiscountProductService.cs
@@ -1,4 +1,5 @@
using BackOffice.BFF.DiscountProduct.Protobuf.Protos.DiscountProduct;
+using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
namespace BackOffice.Services.DiscountProduct;
@@ -115,6 +116,28 @@ public class DiscountProductService : IDiscountProductService
request.CategoryIds.AddRange(dto.CategoryIds);
}
+ // Add image file if provided
+ if (dto.ImageFile != null && !string.IsNullOrEmpty(dto.ImageMime) && !string.IsNullOrEmpty(dto.ImageFileName))
+ {
+ request.ImageFile = new ImageFileModel
+ {
+ File = ByteString.CopyFrom(dto.ImageFile),
+ Mime = dto.ImageMime,
+ FileName = dto.ImageFileName
+ };
+ }
+
+ // Add thumbnail file if provided
+ if (dto.ThumbnailFile != null && !string.IsNullOrEmpty(dto.ThumbnailMime) && !string.IsNullOrEmpty(dto.ThumbnailFileName))
+ {
+ request.ThumbnailFile = new ImageFileModel
+ {
+ File = ByteString.CopyFrom(dto.ThumbnailFile),
+ Mime = dto.ThumbnailMime,
+ FileName = dto.ThumbnailFileName
+ };
+ }
+
var response = await _client.CreateDiscountProductAsync(request);
return response.ProductId;
}
@@ -140,6 +163,50 @@ public class DiscountProductService : IDiscountProductService
request.CategoryIds.AddRange(dto.CategoryIds);
}
+ // Handle image file upload
+ if (dto.ImageFile != null)
+ {
+ request.ImageFile = new ImageFileModel
+ {
+ File = ByteString.CopyFrom(dto.ImageFile),
+ Mime = dto.ImageMime ?? string.Empty,
+ FileName = dto.ImageFileName ?? string.Empty
+ };
+ }
+
+ // Handle thumbnail file upload
+ if (dto.ThumbnailFile != null)
+ {
+ request.ThumbnailFile = new ImageFileModel
+ {
+ File = ByteString.CopyFrom(dto.ThumbnailFile),
+ Mime = dto.ThumbnailMime ?? string.Empty,
+ FileName = dto.ThumbnailFileName ?? string.Empty
+ };
+ }
+
+ // Add image file if provided
+ if (dto.ImageFile != null && !string.IsNullOrEmpty(dto.ImageMime) && !string.IsNullOrEmpty(dto.ImageFileName))
+ {
+ request.ImageFile = new ImageFileModel
+ {
+ File = ByteString.CopyFrom(dto.ImageFile),
+ Mime = dto.ImageMime,
+ FileName = dto.ImageFileName
+ };
+ }
+
+ // Add thumbnail file if provided
+ if (dto.ThumbnailFile != null && !string.IsNullOrEmpty(dto.ThumbnailMime) && !string.IsNullOrEmpty(dto.ThumbnailFileName))
+ {
+ request.ThumbnailFile = new ImageFileModel
+ {
+ File = ByteString.CopyFrom(dto.ThumbnailFile),
+ Mime = dto.ThumbnailMime,
+ FileName = dto.ThumbnailFileName
+ };
+ }
+
await _client.UpdateDiscountProductAsync(request);
}
diff --git a/src/BackOffice/Services/DiscountProduct/IDiscountProductService.cs b/src/BackOffice/Services/DiscountProduct/IDiscountProductService.cs
index 6600a2b..c9a3b23 100644
--- a/src/BackOffice/Services/DiscountProduct/IDiscountProductService.cs
+++ b/src/BackOffice/Services/DiscountProduct/IDiscountProductService.cs
@@ -74,6 +74,14 @@ public class CreateDiscountProductDto
public int SortOrder { get; set; }
public bool IsActive { get; set; } = true;
public List? CategoryIds { get; set; }
+
+ // Image upload support
+ public byte[]? ImageFile { get; set; }
+ public string? ImageMime { get; set; }
+ public string? ImageFileName { get; set; }
+ public byte[]? ThumbnailFile { get; set; }
+ public string? ThumbnailMime { get; set; }
+ public string? ThumbnailFileName { get; set; }
}
public class UpdateDiscountProductDto
@@ -88,4 +96,13 @@ public class UpdateDiscountProductDto
public int SortOrder { get; set; }
public bool IsActive { get; set; }
public List? CategoryIds { get; set; }
+
+ // Image upload support
+ public byte[]? ImageFile { get; set; }
+ public string? ImageMime { get; set; }
+ public string? ImageFileName { get; set; }
+ public byte[]? ThumbnailFile { get; set; }
+ public string? ThumbnailMime { get; set; }
+ public string? ThumbnailFileName { get; set; }
}
+