feat: add club membership contract dialog and city autocomplete

This commit is contained in:
masoodafar-web
2025-12-17 22:46:07 +03:30
parent 3af564be73
commit 27c2c0259b
10 changed files with 462 additions and 23 deletions
@@ -29,13 +29,34 @@
Required="true"
RequiredError="کد پستی الزامی است." />
<MudTextField @bind-Value="_request.CityId"
For="@(() => _request.CityId)"
Label="شناسه شهر"
Variant="Variant.Outlined"
InputType="InputType.Number"
Required="true"
RequiredError="شهر الزامی است." />
<MudAutocomplete T="CityProto.GetAllCitiesByFilterResponseModel"
@bind-Value="_selectedCity"
Label="شهر"
Variant="Variant.Outlined"
SearchFunc="SearchCities"
ToStringFunc="@(city => city != null ? $"{city.Native} ({city.StateName})" : string.Empty)"
Required="true"
RequiredError="شهر الزامی است."
Clearable="true"
ResetValueOnEmptyText="true"
CoerceText="false"
CoerceValue="false"
Dense="true"
DebounceInterval="300"
MaxItems="20"
MinCharacters="2"
ProgressIndicatorColor="Color.Primary">
<ItemTemplate Context="city">
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
<MudIcon Icon="@Icons.Material.Filled.LocationCity" Size="Size.Small" />
<MudText>@city.Native</MudText>
<MudText Typo="Typo.caption" Class="mud-text-secondary">(@city.StateName)</MudText>
</MudStack>
</ItemTemplate>
<NoItemsTemplate>
<MudText Typo="Typo.body2" Class="mud-text-secondary pa-2">شهری یافت نشد</MudText>
</NoItemsTemplate>
</MudAutocomplete>
</MudStack>
</MudForm>
</DialogContent>
@@ -3,6 +3,7 @@ using FrontOffice.BFF.UserAddress.Protobuf.Validator;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using Severity = MudBlazor.Severity;
using CityProto = FrontOffice.BFF.City.Protobuf;
namespace FrontOffice.Main.Pages.Profile.Components;
@@ -10,19 +11,48 @@ public partial class AddAddressDialog : ComponentBase
{
[CascadingParameter] private IDialogReference MudDialog { get; set; } = default!;
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
[Inject] private CityProto.CityContract.CityContractClient CityContract { get; set; } = default!;
private MudForm? _form;
private readonly CreateNewUserAddressRequestValidator _validator = new();
private bool _isSaving;
private CreateNewUserAddressRequest _request = new();
private CityProto.GetAllCitiesByFilterResponseModel? _selectedCity;
private async Task<IEnumerable<CityProto.GetAllCitiesByFilterResponseModel>> SearchCities(string value, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(value) || value.Length < 2)
return Enumerable.Empty<CityProto.GetAllCitiesByFilterResponseModel>();
try
{
var response = await CityContract.GetAllCitiesByFilterAsync(new CityProto.GetAllCitiesByFilterRequest
{
PaginationState = new CityProto.PaginationState { PageNumber = 1, PageSize = 20 },
Filter = new CityProto.GetAllCitiesByFilterFilter { Native = value }
}, cancellationToken: ct);
return response?.Models?.ToList() ?? new List<CityProto.GetAllCitiesByFilterResponseModel>();
}
catch
{
return Enumerable.Empty<CityProto.GetAllCitiesByFilterResponseModel>();
}
}
private async Task SaveAddress()
{
if (_form == null) return;
await _form.Validate();
if (!_form.IsValid) return;
if (!_form.IsValid || _selectedCity == null)
{
if (_selectedCity == null)
Snackbar.Add("لطفاً شهر را انتخاب کنید.", Severity.Warning);
return;
}
_request.CityId = _selectedCity.Id;
_isSaving = true;
try
{
@@ -28,13 +28,34 @@
Required="true"
RequiredError="کد پستی الزامی است." />
<MudTextField @bind-Value="_request.CityId"
For="@(() => _request.CityId)"
Label="شناسه شهر"
Variant="Variant.Outlined"
InputType="InputType.Number"
Required="true"
RequiredError="شهر الزامی است." />
<MudAutocomplete T="CityProto.GetAllCitiesByFilterResponseModel"
@bind-Value="_selectedCity"
Label="شهر"
Variant="Variant.Outlined"
SearchFunc="SearchCities"
ToStringFunc="@(city => city != null ? $"{city.Native} ({city.StateName})" : string.Empty)"
Required="true"
RequiredError="شهر الزامی است."
Clearable="true"
ResetValueOnEmptyText="true"
CoerceText="false"
CoerceValue="false"
Dense="true"
DebounceInterval="300"
MaxItems="20"
MinCharacters="2"
ProgressIndicatorColor="Color.Primary">
<ItemTemplate Context="city">
<MudStack Row="true" Spacing="2" AlignItems="AlignItems.Center">
<MudIcon Icon="@Icons.Material.Filled.LocationCity" Size="Size.Small" />
<MudText>@city.Native</MudText>
<MudText Typo="Typo.caption" Class="mud-text-secondary">(@city.StateName)</MudText>
</MudStack>
</ItemTemplate>
<NoItemsTemplate>
<MudText Typo="Typo.body2" Class="mud-text-secondary pa-2">شهری یافت نشد</MudText>
</NoItemsTemplate>
</MudAutocomplete>
</MudStack>
</MudForm>
</DialogContent>
@@ -4,14 +4,15 @@ using Mapster;
using Microsoft.AspNetCore.Components;
using MudBlazor;
using Severity = MudBlazor.Severity;
using CityProto = FrontOffice.BFF.City.Protobuf;
namespace FrontOffice.Main.Pages.Profile.Components;
public partial class EditAddressDialog : ComponentBase
{
[CascadingParameter] private IDialogReference MudDialog { get; set; } = default!; // updated type
[CascadingParameter] private IDialogReference MudDialog { get; set; } = default!;
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
// removed duplicate Snackbar injection; provided by Razor partial via _Imports
[Inject] private CityProto.CityContract.CityContractClient CityContract { get; set; } = default!;
[Parameter] public GetAllUserAddressByFilterResponseModel? Model { get; set; }
@@ -19,12 +20,60 @@ public partial class EditAddressDialog : ComponentBase
private readonly UpdateUserAddressRequestValidator _validator = new();
private bool _isSaving;
private UpdateUserAddressRequest _request = new();
private CityProto.GetAllCitiesByFilterResponseModel? _selectedCity;
protected override void OnInitialized()
protected override async Task OnInitializedAsync()
{
base.OnInitialized();
await base.OnInitializedAsync();
if (Model != null)
{
_request = Model.Adapt<UpdateUserAddressRequest>();
// Load selected city if CityId exists
if (Model.CityId > 0)
{
await LoadCurrentCity(Model.CityId);
}
}
}
private async Task LoadCurrentCity(long cityId)
{
try
{
var response = await CityContract.GetAllCitiesByFilterAsync(new CityProto.GetAllCitiesByFilterRequest
{
PaginationState = new CityProto.PaginationState { PageNumber = 1, PageSize = 1 },
Filter = new CityProto.GetAllCitiesByFilterFilter { Id = cityId }
});
_selectedCity = response?.Models?.FirstOrDefault();
}
catch
{
// Ignore error loading city
}
}
private async Task<IEnumerable<CityProto.GetAllCitiesByFilterResponseModel>> SearchCities(string value, CancellationToken ct)
{
if (string.IsNullOrWhiteSpace(value) || value.Length < 2)
return Enumerable.Empty<CityProto.GetAllCitiesByFilterResponseModel>();
try
{
var response = await CityContract.GetAllCitiesByFilterAsync(new CityProto.GetAllCitiesByFilterRequest
{
PaginationState = new CityProto.PaginationState { PageNumber = 1, PageSize = 20 },
Filter = new CityProto.GetAllCitiesByFilterFilter { Native = value }
}, cancellationToken: ct);
return response?.Models?.ToList() ?? new List<CityProto.GetAllCitiesByFilterResponseModel>();
}
catch
{
return Enumerable.Empty<CityProto.GetAllCitiesByFilterResponseModel>();
}
}
private async Task SaveAddress()
@@ -32,8 +81,14 @@ public partial class EditAddressDialog : ComponentBase
if (_form == null) return;
await _form.Validate();
if (!_form.IsValid) return;
if (!_form.IsValid || _selectedCity == null)
{
if (_selectedCity == null)
Snackbar.Add("لطفاً شهر را انتخاب کنید.", Severity.Warning);
return;
}
_request.CityId = _selectedCity.Id;
_isSaving = true;
try
{