Compare commits

...

46 Commits

Author SHA1 Message Date
masoodafar-web 58037f0b49 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 5m9s
2026-08-24 00:10:52 +03:30
masoodafar-web aae4b2693d fix(checkout): add missing JSInterop import for enhanced interactivity
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 3m47s
- Included the Microsoft.JSInterop namespace in the Checkout.razor.cs file to enable JavaScript interop functionality.
- This addition prepares the Checkout component for future enhancements that may require interaction with JavaScript libraries or functions.

These changes improve the potential for interactive features in the checkout process, enhancing user experience.
2026-08-23 23:46:35 +03:30
masoodafar-web 61fd805c75 feat(profile): enhance withdrawal request processing with IBAN normalization
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 4m8s
- Implemented a new method to normalize Iranian IBANs, ensuring they are formatted correctly before processing withdrawal requests.
- Added validation to provide user feedback if the IBAN format is invalid, improving the user experience during withdrawal submissions.
- Updated the withdrawal request logic to utilize the normalized IBAN, enhancing the reliability of the withdrawal process.

These changes improve the accuracy and usability of the withdrawal request feature, ensuring users can submit valid IBANs effectively.
2026-08-23 23:01:39 +03:30
masoodafar-web ef6d233bbc refactor(checkout): simplify address management and improve package selection logic
Build and Deploy to Kubernetes / build-and-deploy (push) Failing after 2m45s
- Removed the address selection UI and related logic from the Checkout page to streamline the user experience.
- Updated the payment validation to only require package selection, enhancing clarity for users.
- Adjusted the loading of addresses in related components based on user authentication status, ensuring addresses are only loaded when available.

These changes improve the overall usability of the checkout process by focusing on essential selections and reducing unnecessary complexity.
2026-08-23 22:32:47 +03:30
masoodafar-web 3d84e726db Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 6m27s
2026-08-09 02:33:27 +03:30
masoodafar-web 13091849cf feat(wallet): update ChargeCreditWallet component for improved magic ceiling consent handling
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m1s
- Enhanced the user interface to provide clearer instructions and options for charging without a magic multiplier when the magic wallet ceiling is full.
- Removed the previous consent prompting logic and replaced it with direct buttons for user actions.
- Updated methods to streamline the consent process and improve navigation based on user choices.

These changes enhance user experience by simplifying the consent flow and providing immediate options for wallet charging.
2026-08-09 00:11:49 +03:30
masoodafar-web 8e98c50bdf feat(wallet): enhance ChargeCreditWallet and Wallet components with magic ceiling consent logic
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 5m29s
- Added new logic to handle user consent for charging without a magic multiplier when the magic wallet ceiling is full.
- Updated the ChargeCreditWallet component to display alerts and prompts based on the user's magic wallet status and consent.
- Enhanced the Wallet component to conditionally render buttons and alerts related to the magic wallet ceiling status.
- Improved user feedback in the InsufficientCreditDialog to guide users on charging options based on their wallet status.

These changes enhance the user experience by providing clear instructions and options for charging wallets under specific conditions.
2026-08-08 23:59:36 +03:30
masoodafar-web 6123e47968 feat(wallet): enhance ChargeCreditWallet and Wallet components with magic wallet functionality
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 5m28s
- Added logic to handle redirection and alerts for users with a magic wallet status in the ChargeCreditWallet component.
- Updated the Wallet component to conditionally render buttons and alerts based on the user's magic wallet status.
- Implemented a method to check the magic wallet status and provide user feedback regarding wallet charging options.

These changes improve user experience by clearly guiding users on the appropriate wallet charging methods based on their current wallet status.
2026-08-08 22:22:03 +03:30
masoodafar-web 9203dc7924 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 6m24s
2026-08-07 14:38:22 +03:30
masoodafar-web 4989cd00e0 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Failing after 16m3s
2026-07-26 00:52:30 +03:30
masoodafar-web bed35935ad Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Failing after 17m17s
2026-07-15 22:02:31 +03:30
masoodafar-web ceaaac3e23 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Failing after 18m42s
2026-07-03 06:04:56 +03:30
masoodafar-web db35814ce3 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2026-07-03 05:43:14 +03:30
masoodafar-web 7a5a2110c6 feat: add SEO configuration for production environment
Build and Deploy to Production / build-and-deploy (push) Failing after 19m58s
- Introduced SEO settings in appsettings.Production.json, including SiteUrl, SiteName, and DefaultDescription.
- These additions enhance the application's search engine optimization capabilities, improving visibility and user engagement.
2026-07-01 21:48:49 +03:30
masoodafar-web 8dbc64cd86 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2026-07-01 21:45:43 +03:30
masoodafar-web deadd197e6 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2026-07-01 21:31:49 +03:30
masoodafar-web 671c8407e5 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2026-06-30 02:50:03 +03:30
masoodafar-web 1956dcc5f4 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2026-06-30 02:28:13 +03:30
masoodafar-web d59d829d5f Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Failing after 17m21s
2026-06-26 19:30:51 +03:30
masoodafar-web 3c216feb32 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Failing after 15m3s
2026-06-19 02:38:15 +03:30
masoodafar-web 032b8eb448 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 8m14s
2026-06-08 01:55:28 +03:30
masoodafar-web 088ea8dc7f Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 6m36s
2026-05-29 19:59:19 +03:30
masoodafar-web cd92a2f7b0 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 4m24s
2026-05-14 20:12:20 +03:30
masoodafar-web 0c9eb5f9ef Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 4m25s
2026-05-14 02:07:56 +03:30
masoodafar-web 94fbf49fb9 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 4m51s
2026-05-14 01:56:18 +03:30
masoodafar-web 4330a5eaa5 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 4m16s
2026-05-14 01:12:35 +03:30
masoodafar-web 1e5e6bcf3d Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 5m50s
2026-05-04 00:04:13 +03:30
masoodafar-web d751c06a67 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 4m5s
2026-05-01 00:16:24 +03:30
masoodafar-web 1c5aa0ce15 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 14m28s
- Proto version: 0.0.192
- Remove local nupkg source from NuGet.config (breaks Docker builds)
2026-03-15 03:26:17 +03:30
masoodafar-web caaf28b08e fix: update CMS protobuf package version to 0.0.191 and comment out local package reference
Build and Deploy to Production / build-and-deploy (push) Successful in 6m22s
2026-02-28 05:07:54 +03:30
masoodafar-web 1914f703db Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Failing after 2m40s
2026-02-28 04:47:34 +03:30
masoodafar-web a606cd93cd fix: rename wallet labels — اصلی، اعتباری، پاداش تیمی
Build and Deploy to Production / build-and-deploy (push) Successful in 6m19s
- عادی → اصلی (کیف پول اصلی)
- شبکه → پاداش تیمی
- پورسانت → پاداش تیمی
- موجودی کیف پول → موجودی کیف پول اصلی

Files: Wallet.razor, WalletService.cs, MagicWallet.razor,
       Profile/Index.razor, PaymentCallback.razor
2026-02-23 23:26:52 +03:30
masoodafar-web 8b78235ef5 chore: prevent appsettings.Production.json from merging — keep ours strategy
Build and Deploy to Production / build-and-deploy (push) Successful in 6m27s
2026-02-23 00:03:00 +03:30
masoodafar-web f02d082d7b fix: correct GwUrl in Production config to cms.kbs2.ir
Build and Deploy to Production / build-and-deploy (push) Successful in 6m23s
2026-02-22 22:29:17 +03:30
masoodafar-web a11a3765b8 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2026-02-22 22:22:43 +03:30
admin 3cf5ad429d Update src/FrontOffice.Main/Shared/Footer.razor
Build and Deploy to Production / build-and-deploy (push) Successful in 6m39s
2026-02-22 10:25:35 +00:00
masoodafar-web 710ddbe261 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 6m2s
2026-02-21 20:38:23 +03:30
masoodafar-web e2ca65ed98 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 6m42s
2026-02-18 01:19:34 +03:30
masoodafar-web ae9b448a64 merge: kub-stage → production (1:1 square images + site pages + landing animation fix)
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2026-02-18 01:01:44 +03:30
Masoud 1ea928118d fix: update references from تخفیفی to اعتباری across multiple files
Build and Deploy to Production / build-and-deploy (push) Successful in 5m46s
2026-02-17 15:26:09 +03:30
Masoud 8f2d739d1f fix: update references from تخفیفی to اعتباری across multiple files
Build and Deploy to Production / build-and-deploy (push) Has been cancelled
2026-02-17 15:24:13 +03:30
masoodafar-web 8e89d5e064 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 5m43s
2026-02-17 01:59:40 +03:30
masoodafar-web 8bd961ade1 Fix production workflow: use SERVER_PASSWORD, add docker login, use set image for deploy
Build and Deploy to Production / build-and-deploy (push) Successful in 5m39s
2026-02-17 01:24:06 +03:30
masoodafar-web 750d4c6b97 Remove staging workflow files from production branch to prevent cross-deployment 2026-02-17 01:15:59 +03:30
masoodafar-web 276a4c20f2 Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 1m12s
Features merged:
- Discount store UI improvements (100% auto-discount, VAT breakdown)
- PaymentStatus display on orders (Pending/Success/Failed/Refunded)
- Failed payment UX with re-order link
- Product discount badges show actual percent
- Various UI and bug fixes

Production workflow preserved.
2026-02-17 00:55:52 +03:30
masoodafar-web 98a68074bc chore: Update production workflow to use Nexus registry (no proxy needed)
Build and Deploy to Production / build-and-deploy (push) Failing after 0s
- Changed Dockerfile base images to use 194.5.195.53:32082 (Nexus)
- Updated workflow to use docker-sshpass image from Nexus
- Removed HTTP_PROXY/HTTPS_PROXY dependencies
- Changed deploy method to SSH instead of kubeconfig
- Registry: 194.5.195.53:30080 (Gitea internal)
- K8S Server: 45.149.79.127 (Production)
2026-01-29 22:04:31 +03:30
22 changed files with 349 additions and 235 deletions
-46
View File
@@ -1,46 +0,0 @@
name: Push nuget and docker image Actions Workflow
on:
push:
branches:
- stage-new
jobs:
Deploy:
runs-on: windows
steps:
- name: Checkout
uses: https://git.afrino.co/actions/checkout@v3
- name: Setup dotnet
uses: https://git.afrino.co/actions/setup-dotnet@v3
with:
dotnet-version: 7.0.x
- name: Remove Package Source
run: dotnet nuget remove source FourSat
continue-on-error: true
- name: Add Package Source
run: dotnet nuget add source --name FourSat --username systemuser --password sZSA7PTiv3pUSQZ https://git.afrino.co/api/packages/FourSat/nuget/index.json --store-password-in-clear-text
- name: Install dependencies
run: dotnet restore ".\src\FrontOffice.Main\FrontOffice.Main.csproj"
- name: Build
run: dotnet build ".\src\FrontOffice.Main\FrontOffice.Main.csproj" --configuration Release --no-restore
- name: Test
run: dotnet test ".\src\FrontOffice.Main\FrontOffice.Main.csproj" --no-restore --verbosity normal
- name: Recycle Apppool
run: |
& "C:\Windows\System32\inetsrv\appcmd.exe" recycle apppool /apppool.name:kbs1.ir
shell: powershell
- name: Stop Website
run: |
& "C:\Windows\System32\inetsrv\appcmd.exe" stop site /site.name:kbs1.ir
shell: powershell
- name: Publish
run: dotnet publish ".\src\FrontOffice.Main\FrontOffice.Main.csproj" -c Release -o publish
- name: Copy Publish To IIS Directory
run: Get-ChildItem -Path "publish\*" | Copy-Item -Destination "E:\kbs1.ir\kbs1.ir\" -Recurse -Force
- name: Start Website
run: |
& "C:\Windows\System32\inetsrv\appcmd.exe" start site /site.name:kbs1.ir
shell: powershell
+5 -4
View File
@@ -8,7 +8,7 @@ on:
env:
REGISTRY: 194.5.195.53:30080
IMAGE_NAME: admin/frontoffice
K8S_SERVER: 194.5.195.53
K8S_SERVER: 45.149.79.127
jobs:
build-and-deploy:
@@ -40,7 +40,7 @@ jobs:
done
if ! docker info >/dev/null 2>&1; then
echo "❌ Docker daemon failed to start after 3 minutes"
echo "❌ Docker daemon failed to start"
exit 1
fi
@@ -56,9 +56,10 @@ jobs:
- name: Build Docker Image
run: |
cd src
DOCKER_BUILDKIT=0 docker build --network host -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
DOCKER_BUILDKIT=0 docker build --network host -f FrontOffice.Main/Dockerfile \
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} \
-t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:prod \
-f FrontOffice.Main/Dockerfile .
.
- name: Push to Registry
run: |
+1
View File
@@ -1,3 +1,4 @@
# Docs moved to totalDoc
See [totalDoc/INDEX.md](../../totalDoc/INDEX.md) for all documentation.
+1 -75
View File
@@ -71,80 +71,6 @@
</MudStack>
}
</MudPaper>
<!-- Address Selection -->
<MudPaper Elevation="2" Class="pa-4 rounded-lg">
<MudText Typo="Typo.h5" Class="mb-4">انتخاب آدرس</MudText>
@if (_isLoadingAddresses)
{
<MudStack AlignItems="AlignItems.Center" Class="py-4">
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Medium" />
<MudText Typo="Typo.body2" Class="mud-text-secondary mt-2">بارگذاری آدرس‌ها...</MudText>
</MudStack>
}
else if (_addresses.Any())
{
<MudStack Spacing="2">
@foreach (var address in _addresses)
{
<MudPaper Outlined="@(_selectedAddress?.Id != address.Id)"
Elevation="@(_selectedAddress?.Id == address.Id ? 4 : 0)"
Class="pa-3 rounded-xl cursor-pointer"
Style="@(_selectedAddress?.Id == address.Id ? "border: 2px solid var(--mud-palette-primary);" : "")"
@onclick="() => SetAddressAsDefault(address.Id)">
<MudStack Spacing="1">
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
<MudCheckBox @bind-Value="address.IsDefault"
Disabled="true"
Color="Color.Primary" />
<MudText Typo="Typo.subtitle2">@(address.Title)</MudText>
@if (address.IsDefault)
{
<MudChip T="string" Color="Color.Success" Variant="Variant.Filled" Size="Size.Small">پیش‌فرض</MudChip>
}
@if (!address.IsDefault)
{
@if (_isSettingDefaultAddress && _settingDefaultAddressId == address.Id)
{
<MudProgressCircular Size="Size.Small" Color="Color.Primary" Indeterminate="true" />
}
else
{
<MudButton Variant="Variant.Text"
Color="Color.Primary"
Size="Size.Small"
OnClick="() => SetAddressAsDefault(address.Id)">
تنظیم به عنوان پیش‌فرض
</MudButton>
}
}
</MudStack>
<MudStack Row="true">
<MudText Typo="Typo.body2" Class="mud-text-secondary">@(address.Address)</MudText>
<MudSpacer />
<MudText Typo="Typo.caption" Class="mud-text-secondary">کد پستی: @(address.PostalCode)</MudText>
</MudStack>
</MudStack>
</MudPaper>
}
</MudStack>
}
else
{
<MudStack AlignItems="AlignItems.Center" Class="py-4">
<MudIcon Icon="@Icons.Material.Filled.LocationOff" Size="Size.Large" Color="Color.Default" />
<MudText Typo="Typo.body2" Class="mud-text-secondary mt-2">آدرسی ثبت نشده است.</MudText>
<MudButton Variant="Variant.Outlined"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Add"
OnClick="() => Navigation.NavigateTo(RouteConstants.Profile.Index)"
Class="mt-2">
افزودن آدرس
</MudButton>
</MudStack>
}
</MudPaper>
</MudStack>
</MudItem>
@@ -241,7 +167,7 @@
@if (!CanProceedToPayment)
{
<MudText Typo="Typo.caption" Color="Color.Error" Align="Align.Center">
لطفاً پکیج و آدرس را انتخاب کنید.
لطفاً پکیج را انتخاب کنید.
</MudText>
}
</MudStack>
+6 -71
View File
@@ -1,6 +1,4 @@
using CMSMicroservice.Protobuf.Protos.Package;
using CMSMicroservice.Protobuf.Protos.UserAddress;
using FrontOffice.Main.Utilities;
using Microsoft.AspNetCore.Components;
using Microsoft.JSInterop;
@@ -13,14 +11,10 @@ public partial class Checkout
{
[Inject] private PackageService PackageService { get; set; } = default!;
[Inject] private PackageContract.PackageContractClient PackageClient { get; set; } = default!;
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
[Parameter] public long? PackageId { get; set; }
private Pack? _selectedPackage;
private List<CustomerAddressModel> _addresses = new();
private CustomerAddressModel? _selectedAddress;
private bool _isLoadingAddresses;
private bool _isProcessingPayment;
// Discount code
@@ -31,16 +25,11 @@ public partial class Checkout
private long _discountAmount;
private long _finalPrice;
// Address management
private bool _isSettingDefaultAddress;
private long? _settingDefaultAddressId;
private bool CanProceedToPayment => _selectedPackage != null && _selectedAddress != null;
private bool CanProceedToPayment => _selectedPackage != null;
protected override async Task OnInitializedAsync()
{
await LoadPackageDetails();
await LoadAddresses();
}
private async Task LoadPackageDetails()
@@ -72,60 +61,6 @@ public partial class Checkout
}
}
private async Task LoadAddresses()
{
_isLoadingAddresses = true;
try
{
var response = await UserAddressContract.GetCustomerAddressesAsync(request: new());
if (response?.Models?.Any() == true)
{
_addresses = response.Models.ToList();
// Select default address if available
_selectedAddress = _addresses.FirstOrDefault(a => a.IsDefault) ?? _addresses.First();
}
else
{
_addresses = new List<CustomerAddressModel>();
}
}
catch (Exception ex)
{
Snackbar.Add($"خطا در بارگذاری آدرس‌ها: {ex.Message}", Severity.Error);
_addresses = new List<CustomerAddressModel>();
}
finally
{
_isLoadingAddresses = false;
await InvokeAsync(StateHasChanged);
}
}
private async Task SetAddressAsDefault(long addressId)
{
if (_isSettingDefaultAddress) return;
_isSettingDefaultAddress = true;
_settingDefaultAddressId = addressId;
try
{
await UserAddressContract.SetAddressAsDefaultAsync(new() { Id = addressId });
await LoadAddresses(); // Reload addresses to reflect the change
Snackbar.Add("آدرس پیش‌فرض با موفقیت تغییر یافت.", Severity.Success);
}
catch (Exception ex)
{
Snackbar.Add($"خطا در تغییر آدرس پیش‌فرض: {ex.Message}", Severity.Error);
}
finally
{
_isSettingDefaultAddress = false;
_settingDefaultAddressId = null;
await InvokeAsync(StateHasChanged);
}
}
private async Task ApplyDiscountCode()
{
if (string.IsNullOrWhiteSpace(_discountCode))
@@ -157,7 +92,7 @@ public partial class Checkout
_finalPrice = _selectedPackage!.Price;
}
}
catch (Exception ex)
catch (Exception)
{
_discountMessage = "خطا در اعمال کد تخفیف.";
_discountApplied = false;
@@ -175,9 +110,9 @@ public partial class Checkout
{
if (_isProcessingPayment) return;
if (!CanProceedToPayment || _selectedPackage == null || _selectedAddress == null)
if (!CanProceedToPayment || _selectedPackage == null)
{
Snackbar.Add("لطفاً پکیج و آدرس را انتخاب کنید.", Severity.Warning);
Snackbar.Add("لطفاً پکیج را انتخاب کنید.", Severity.Warning);
return;
}
@@ -219,9 +154,9 @@ public partial class Checkout
private async Task DayaLoanPayment()
{
if (_selectedAddress == null)
if (_selectedPackage == null)
{
Snackbar.Add("لطفاً آدرس را انتخاب کنید.", Severity.Warning);
Snackbar.Add("لطفاً پکیج را انتخاب کنید.", Severity.Warning);
return;
}
@@ -40,7 +40,11 @@ public partial class Checkout
}
await VAT.LoadAsync();
await DiscountCart.EnsureInitializedAsync();
await LoadAddresses();
var userInfo = await AuthService.GetUserAuthInfo();
if (userInfo.HasAddress)
await LoadAddresses();
else
_addresses = new();
}
private async Task LoadAddresses()
@@ -1,4 +1,4 @@
using CMSMicroservice.Protobuf.Protos.UserAddress;
using FrontOffice.Main.Utilities;
using CMSMicroservice.Protobuf.Protos.UserAddress;
using FrontOffice.Main.Pages.Profile.Components;
using Microsoft.AspNetCore.Components;
@@ -9,6 +9,7 @@ namespace FrontOffice.Main.Pages.Profile;
public partial class Addresses : ComponentBase
{
[Inject] private UserAddressContract.UserAddressContractClient UserAddressContract { get; set; } = default!;
[Inject] private AuthService AuthService { get; set; } = default!;
private List<CustomerAddressModel> _addresses = new();
private bool _isLoadingAddresses;
@@ -42,7 +43,10 @@ public partial class Addresses : ComponentBase
var dialog = await DialogService.ShowAsync<AddAddressDialog>("افزودن آدرس جدید");
var result = await dialog.Result;
if (result is not null && !result.Canceled)
{
await LoadAddresses();
await AuthService.RefreshTokenAsync();
}
}
private async Task OpenEditAddressDialog(CustomerAddressModel address)
@@ -80,6 +84,7 @@ public partial class Addresses : ComponentBase
await UserAddressContract.DeleteCustomerAddressAsync(new() { Id = id });
Snackbar.Add("آدرس حذف شد.", Severity.Success);
await LoadAddresses();
await AuthService.RefreshTokenAsync();
}
catch (Exception ex)
{
@@ -35,7 +35,23 @@
</MudAlert>
}
@if (_authLoaded && !_isClubMemberActive)
@if (_authLoaded && _redirectingToMagic)
{
<MudAlert Severity="MudBlazor.Severity.Info" Class="rounded-lg" Variant="Variant.Outlined">
<MudStack Spacing="2" AlignItems="AlignItems.Center">
<MudProgressCircular Size="Size.Small" Indeterminate="true" Color="Color.Primary" />
<MudText>
کیف پول شما در حالت جادویی است. در حال انتقال به صفحه شارژ کیف پول جادویی...
</MudText>
<MudButton Size="Size.Small" Variant="Variant.Filled" Color="Color.Primary"
Href="@RouteConstants.Profile.MagicWallet"
StartIcon="@Icons.Material.Filled.AutoAwesome">
رفتن به شارژ کیف جادویی
</MudButton>
</MudStack>
</MudAlert>
}
else if (_authLoaded && !_isClubMemberActive)
{
<MudAlert Severity="MudBlazor.Severity.Warning" Class="rounded-lg" Variant="Variant.Outlined">
<MudStack Spacing="2">
@@ -67,7 +83,35 @@
</MudStack>
</MudAlert>
}
else if (_authLoaded)
else if (_authLoaded && _needsMagicCeilingConsent && !_allowNormalDespiteMagic && !_redirectingToMagic)
{
<MudAlert Severity="MudBlazor.Severity.Warning" Class="rounded-lg" Variant="Variant.Outlined">
<MudStack Spacing="3">
<MudText Typo="Typo.subtitle1">
سقف شارژ کیف پول جادویی در این دور پر شده است.
</MudText>
<MudText Typo="Typo.body2">
شارژ از این صفحه بدون ضریب (۱:۱) به موجودی اضافه می‌شود و ضریب جادویی اعمال نمی‌شود.
برای ادامه، تأیید کنید.
</MudText>
<MudStack Row="true" Spacing="1" Class="flex-wrap">
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Check"
OnClick="AcceptMagicCeilingConsent">
ادامه شارژ بدون ضریب
</MudButton>
<MudButton Variant="Variant.Outlined"
Color="Color.Secondary"
StartIcon="@Icons.Material.Filled.AutoAwesome"
OnClick="DeclineMagicCeilingConsent">
بازگشت به کیف جادویی
</MudButton>
</MudStack>
</MudStack>
</MudAlert>
}
else if (ShowChargeForm)
{
<MudPaper Elevation="2" Class="pa-5 rounded-lg">
<MudText Typo="Typo.h6" Class="mb-3">
@@ -75,6 +119,13 @@
شارژ موجودی اصلی
</MudText>
@if (_allowNormalDespiteMagic)
{
<MudAlert Severity="MudBlazor.Severity.Warning" Dense="true" Class="mb-3" Icon="@Icons.Material.Filled.Warning">
این شارژ بدون ضریب جادویی است (۱:۱). سقف واریز جادویی شما در این دور پر شده است.
</MudAlert>
}
<MudAlert Severity="MudBlazor.Severity.Info" Dense="true" Class="mb-3" Icon="@Icons.Material.Filled.Info">
مبلغ وارد شده (به تومان) از طریق درگاه پرداخت به موجودی اصلی شما اضافه می‌شود.
از موجودی اصلی برای خرید از فروشگاه عادی و پرداخت سفارش استفاده می‌شود.
@@ -16,6 +16,9 @@ public partial class ChargeCreditWallet : ComponentBase
private bool _isClubMemberActive;
private bool _hasPurchasedPackage;
private bool _authLoaded;
private bool _redirectingToMagic;
private bool _allowNormalDespiteMagic;
private bool _needsMagicCeilingConsent;
private readonly long[] _presetAmounts = { 500_000, 1_000_000, 5_000_000, 10_000_000, 20_000_000, 50_000_000 };
@@ -28,6 +31,12 @@ public partial class ChargeCreditWallet : ComponentBase
[SupplyParameterFromQuery(Name = "returnUrl")]
public string? ReturnUrlQueryParam { get; set; }
private bool ShowChargeForm =>
_authLoaded
&& _isClubMemberActive
&& !_redirectingToMagic
&& (!_needsMagicCeilingConsent || _allowNormalDespiteMagic);
protected override async Task OnInitializedAsync()
{
_paymentResult = PaymentQueryParam;
@@ -53,18 +62,31 @@ public partial class ChargeCreditWallet : ComponentBase
{
_authLoaded = true;
}
if (_isClubMemberActive)
await EvaluateMagicWalletGateAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && !string.IsNullOrEmpty(_pendingReturnUrl) && _isClubMemberActive)
{
if (firstRender && !string.IsNullOrEmpty(_pendingReturnUrl) && _isClubMemberActive && !_redirectingToMagic)
await CreditChargeNavigation.SaveReturnUrlAsync(JSRuntime, _pendingReturnUrl);
}
await base.OnAfterRenderAsync(firstRender);
}
private void AcceptMagicCeilingConsent()
{
_allowNormalDespiteMagic = true;
_redirectingToMagic = false;
}
private void DeclineMagicCeilingConsent()
{
_redirectingToMagic = true;
Navigation.NavigateTo(RouteConstants.Profile.MagicWallet);
}
private async Task StartCreditCharge()
{
if (!_isClubMemberActive)
@@ -75,6 +97,12 @@ public partial class ChargeCreditWallet : ComponentBase
return;
}
if (await EvaluateMagicWalletGateAsync())
return;
if (_needsMagicCeilingConsent && !_allowNormalDespiteMagic)
return;
if (_chargeAmount <= 0 || _isProcessing) return;
_isProcessing = true;
@@ -113,6 +141,50 @@ public partial class ChargeCreditWallet : ComponentBase
}
}
/// <summary>
/// Magic با ظرفیت باقی‌مانده → ریدایرکت به MagicWallet.
/// Magic با سقف پر → نمایش تأیید روی صفحه برای شارژ ۱:۱.
/// true یعنی مسیر شارژ فعلی باید متوقف شود.
/// </summary>
private async Task<bool> EvaluateMagicWalletGateAsync()
{
if (_allowNormalDespiteMagic)
return false;
try
{
var status = await WalletService.GetMagicWalletStatusAsync();
if (status.WalletMode != 1)
{
_needsMagicCeilingConsent = false;
return false;
}
if (status.MagicRemainingDeposit > 0)
{
var multiplier = status.MagicMultiplier > 0 ? status.MagicMultiplier : 2.5;
var multiplierText = multiplier.ToString("0.#");
_redirectingToMagic = true;
_needsMagicCeilingConsent = false;
Snackbar.Add(
$"کیف پول شما در حالت جادویی است. شارژ از این صفحه موجودی را چندبرابر نمی‌کند. برای دریافت ضریب جادویی (×{multiplierText}) به صفحه شارژ کیف پول جادویی هدایت می‌شوید.",
Severity.Info);
Navigation.NavigateTo(RouteConstants.Profile.MagicWallet);
return true;
}
_needsMagicCeilingConsent = true;
return !_allowNormalDespiteMagic;
}
catch
{
// در صورت خطا در خواندن وضعیت، مانع شارژ عادی نشو
return false;
}
}
private async Task ContinueShoppingAsync()
{
var returnUrl = await CreditChargeNavigation.GetReturnUrlAsync(JSRuntime);
@@ -333,6 +333,7 @@ public partial class Index
if (!result.Canceled)
{
await LoadAddresses();
await AuthService.RefreshTokenAsync();
}
}
@@ -385,6 +386,7 @@ public partial class Index
});
Snackbar.Add("آدرس با موفقیت حذف شد.", Severity.Success);
await LoadAddresses();
await AuthService.RefreshTokenAsync();
}
catch (Exception ex)
{
@@ -491,11 +493,6 @@ public partial class Index
Snackbar.Add("لطفا اطلاعات شخصی خود را تکمیل کنید. (تاریخ تولد وارد نشده)", Severity.Error);
return;
}
if (!_addresses.Any())
{
Snackbar.Add("آدرس محل سکونت شما الزامی است!", Severity.Error);
return;
}
var url = "https://dayadiamond.ir/profile/creditpurchase/?merchantcode=56146364";
await JSRuntime.InvokeVoidAsync("open", url, "_blank");
@@ -166,8 +166,23 @@
else
{
<MudAlert Severity="MudBlazor.Severity.Warning" Class="rounded-lg">
سقف شارژ جادویی در این دور پر شده است.
با خرج کردن موجودی و خرید مجدد پکیج، سقف ریست می‌شود.
<MudStack Spacing="2">
<MudText>
سقف شارژ جادویی در این دور پر شده است.
با خرج کردن موجودی و خرید مجدد پکیج، سقف ریست می‌شود.
</MudText>
<MudText Typo="Typo.body2">
اگر برای تکمیل خرید به موجودی بیشتری نیاز دارید، می‌توانید به‌صورت استثنایی کیف اصلی را بدون ضریب (۱:۱) شارژ کنید.
</MudText>
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Small"
StartIcon="@Icons.Material.Filled.AccountBalanceWallet"
Href="@RouteConstants.Profile.ChargeCreditWallet"
Class="rounded-lg align-self-start">
شارژ عادی بدون ضریب
</MudButton>
</MudStack>
</MudAlert>
}
@@ -31,7 +31,7 @@
<MudDivider Class="my-4" />
<MudText Typo="Typo.body2" Color="Color.Secondary">
موجودی کیف پول: @FormatPrice(_walletBalance)
موجودی کیف پول اصلی: @FormatPrice(_walletBalance)
</MudText>
<MudButton Color="Color.Primary" Variant="Variant.Filled" Class="mt-6"
+46 -10
View File
@@ -38,18 +38,54 @@
درخواست‌های برداشت
</MudButton>
<!-- دکمه شارژ کیف‌پول اصلی -->
<!-- دکمه شارژ کیف‌پول اصلی / جادویی -->
@if (_isClubMemberActive)
{
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Large"
FullWidth="true"
StartIcon="@Icons.Material.Filled.AccountBalanceWallet"
Href="@RouteConstants.Profile.ChargeCreditWallet"
Class="rounded-lg">
شارژ کیف‌پول اصلی
</MudButton>
@if (_isMagicWallet)
{
<MudButton Variant="Variant.Filled"
Color="Color.Secondary"
Size="Size.Large"
FullWidth="true"
StartIcon="@Icons.Material.Filled.AutoAwesome"
Href="@RouteConstants.Profile.MagicWallet"
Class="rounded-lg">
شارژ کیف‌پول جادویی
</MudButton>
@if (_magicCeilingFull)
{
<MudAlert Severity="Severity.Warning" Dense="true" Variant="Variant.Outlined">
سقف شارژ جادویی این دور پر است. در صورت نیاز می‌توانید به‌صورت استثنایی بدون ضریب شارژ کنید.
</MudAlert>
<MudButton Variant="Variant.Outlined"
Color="Color.Primary"
Size="Size.Large"
FullWidth="true"
StartIcon="@Icons.Material.Filled.AccountBalanceWallet"
Href="@RouteConstants.Profile.ChargeCreditWallet"
Class="rounded-lg">
شارژ عادی بدون ضریب
</MudButton>
}
else
{
<MudAlert Severity="Severity.Info" Dense="true" Variant="Variant.Outlined">
کیف پول شما در حالت جادویی است؛ شارژ از مسیر جادویی با ضریب چندبرابر اعمال می‌شود.
</MudAlert>
}
}
else
{
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
Size="Size.Large"
FullWidth="true"
StartIcon="@Icons.Material.Filled.AccountBalanceWallet"
Href="@RouteConstants.Profile.ChargeCreditWallet"
Class="rounded-lg">
شارژ کیف‌پول اصلی
</MudButton>
}
}
else
{
@@ -14,6 +14,8 @@ public partial class Wallet : ComponentBase
private string _filterType = "all";
private bool _isClubMemberActive;
private bool _hasPurchasedPackage;
private bool _isMagicWallet;
private bool _magicCeilingFull;
protected override async Task OnInitializedAsync()
{
@@ -29,6 +31,18 @@ public partial class Wallet : ComponentBase
_hasPurchasedPackage = false;
}
try
{
var magicStatus = await WalletService.GetMagicWalletStatusAsync();
_isMagicWallet = magicStatus.WalletMode == 1;
_magicCeilingFull = _isMagicWallet && magicStatus.MagicRemainingDeposit <= 0;
}
catch
{
_isMagicWallet = false;
_magicCeilingFull = false;
}
var b = await WalletService.GetBalancesAsync();
_balances = (FormatPrice(b.CreditBalance), FormatPrice(b.DiscountBalance), FormatPrice(b.NetworkBalance));
_txs = await WalletService.GetTransactionsAsync();
@@ -58,8 +58,19 @@ public partial class WithdrawalRequests : ComponentBase
try
{
_isSubmittingWithdrawal = true;
_withdrawIban=_withdrawIban!.Trim().ToUpper().Replace("IR", "").Replace(" ", "");
await WalletService.RequestWithdrawalAsync(_selectedPayout.Id, _withdrawMethod, "IR"+_withdrawIban);
string? normalizedIban = null;
if (_withdrawMethod == WithdrawalMethodClient.Cash)
{
normalizedIban = NormalizeIranianIban(_withdrawIban);
if (normalizedIban is null)
{
Snackbar.Add("فرمت شماره شبا معتبر نیست. باید IR و ۲۴ رقم باشد (خط تیره اختیاری است).", Severity.Warning);
return;
}
}
await WalletService.RequestWithdrawalAsync(_selectedPayout.Id, _withdrawMethod, normalizedIban);
Snackbar.Add("درخواست برداشت ثبت شد.", Severity.Success);
// بروزرسانی لیست
@@ -125,4 +136,26 @@ public partial class WithdrawalRequests : ComponentBase
1 => "الماس",
_ => "-"
};
/// <summary>
/// فاصله و خط تیره را حذف می‌کند و شبا را به شکل IR + ۲۴ رقم برمی‌گرداند.
/// </summary>
private static string? NormalizeIranianIban(string? iban)
{
if (string.IsNullOrWhiteSpace(iban))
return null;
var normalized = iban.Trim().ToUpperInvariant()
.Replace(" ", "", StringComparison.Ordinal)
.Replace("-", "", StringComparison.Ordinal);
if (normalized.StartsWith("IR", StringComparison.Ordinal))
normalized = normalized[2..];
normalized = "IR" + normalized;
return System.Text.RegularExpressions.Regex.IsMatch(normalized, @"^IR\d{24}$")
? normalized
: null;
}
}
@@ -35,7 +35,11 @@ public partial class CheckoutSummary : ComponentBase
await AuthDialogService.ShowAuthDialogAsync();
}
await Cart.EnsureInitializedAsync();
await LoadAddresses();
var userInfo = await AuthService.GetUserAuthInfo();
if (userInfo.HasAddress)
await LoadAddresses();
else
_addresses = new();
await LoadWalletBalance();
}
@@ -127,6 +131,8 @@ public partial class CheckoutSummary : ComponentBase
var shortfall = totalRequired - walletBalance;
var allowCharge = false;
var hasPurchasedPackage = false;
var isMagicWallet = false;
var magicCeilingFull = false;
try
{
var userInfo = await AuthService.GetUserAuthInfo();
@@ -138,13 +144,27 @@ public partial class CheckoutSummary : ComponentBase
allowCharge = false;
}
try
{
var magicStatus = await WalletService.GetMagicWalletStatusAsync();
isMagicWallet = magicStatus.WalletMode == 1;
magicCeilingFull = isMagicWallet && magicStatus.MagicRemainingDeposit <= 0;
}
catch
{
isMagicWallet = false;
magicCeilingFull = false;
}
var parameters = new DialogParameters<InsufficientCreditDialog>
{
{ x => x.CurrentBalance, walletBalance },
{ x => x.RequiredAmount, totalRequired },
{ x => x.ShortfallAmount, shortfall },
{ x => x.AllowChargeCredit, allowCharge },
{ x => x.HasPurchasedPackage, hasPurchasedPackage }
{ x => x.HasPurchasedPackage, hasPurchasedPackage },
{ x => x.IsMagicWallet, isMagicWallet },
{ x => x.MagicCeilingFull, magicCeilingFull }
};
var dialog = await DialogService.ShowAsync<InsufficientCreditDialog>(
+3
View File
@@ -45,6 +45,9 @@
Href="https://wa.me/989123456789"
Target="_blank" />
</MudStack>
<MudStack Row="true" Spacing="2" Class="enamad">
<a referrerpolicy='origin' target='_blank' href='https://trustseal.enamad.ir/?id=688255&Code=uoMRGkyUYHAtMxkpBLB0KyPL5Ac66ACX'><img referrerpolicy='origin' src='https://trustseal.enamad.ir/logo.aspx?id=688255&Code=uoMRGkyUYHAtMxkpBLB0KyPL5Ac66ACX' alt='' style='cursor:pointer' code='uoMRGkyUYHAtMxkpBLB0KyPL5Ac66ACX'></a>
</MudStack>
</MudStack>
</MudItem>
@@ -11,9 +11,24 @@
<MudStack Spacing="2">
@if (AllowChargeCredit)
{
<MudText Typo="Typo.body2" Class="mud-text-secondary">
برای تکمیل این سفارش، موجودی اصلی شما کافی نیست. می‌توانید کیف پول را شارژ کنید و سپس سفارش را ثبت کنید.
</MudText>
@if (IsMagicWallet && !MagicCeilingFull)
{
<MudText Typo="Typo.body2" Class="mud-text-secondary">
برای تکمیل این سفارش موجودی شما کافی نیست. کیف پول شما در حالت جادویی است؛ برای دریافت ضریب، از مسیر شارژ جادویی اقدام کنید.
</MudText>
}
else if (IsMagicWallet && MagicCeilingFull)
{
<MudAlert Severity="Severity.Warning" Dense="true" Variant="Variant.Outlined">
سقف شارژ جادویی شما در این دور پر است. می‌توانید به‌صورت استثنایی کیف اصلی را بدون ضریب (۱:۱) شارژ کنید.
</MudAlert>
}
else
{
<MudText Typo="Typo.body2" Class="mud-text-secondary">
برای تکمیل این سفارش، موجودی اصلی شما کافی نیست. می‌توانید کیف پول را شارژ کنید و سپس سفارش را ثبت کنید.
</MudText>
}
}
else
{
@@ -46,10 +61,10 @@
@if (AllowChargeCredit)
{
<MudButton Variant="Variant.Filled"
Color="Color.Primary"
StartIcon="@Icons.Material.Filled.Payment"
Color="@(IsMagicWallet && !MagicCeilingFull ? Color.Secondary : Color.Primary)"
StartIcon="@(IsMagicWallet && !MagicCeilingFull ? Icons.Material.Filled.AutoAwesome : Icons.Material.Filled.Payment)"
OnClick="GoToCharge">
شارژ حساب اصلی
@ChargeButtonLabel
</MudButton>
}
else if (!HasPurchasedPackage)
@@ -93,9 +108,33 @@
[Parameter]
public bool HasPurchasedPackage { get; set; }
[Parameter]
public bool IsMagicWallet { get; set; }
/// <summary>سقف واریز جادویی این دور پر شده است.</summary>
[Parameter]
public bool MagicCeilingFull { get; set; }
private string ChargeButtonLabel =>
IsMagicWallet && !MagicCeilingFull
? "شارژ کیف جادویی"
: IsMagicWallet && MagicCeilingFull
? "شارژ عادی بدون ضریب (سقف جادویی پر است)"
: "شارژ حساب اصلی";
private void Cancel() => MudDialog.Cancel();
private void GoToCharge() => MudDialog.Close(DialogResult.Ok(ShortfallAmount));
private void GoToCharge()
{
if (IsMagicWallet && !MagicCeilingFull)
{
MudDialog.Cancel();
Navigation.NavigateTo(RouteConstants.Profile.MagicWallet);
return;
}
MudDialog.Close(DialogResult.Ok(ShortfallAmount));
}
private void GoToPackages()
{
@@ -79,6 +79,7 @@ public class AuthService
var isSignMainContractStr = claims.FirstOrDefault(c => c.key == "IsSignMainContract").value;
var hasPurchasedPackageStr = claims.FirstOrDefault(c => c.key == "HasPurchasedPackage").value;
var isClubMemberActiveStr = claims.FirstOrDefault(c => c.key == "IsClubMemberActive").value;
var hasAddressStr = claims.FirstOrDefault(c => c.key == "HasAddress").value;
_userAuthInfo.UserId = long.TryParse(userIdStr, out var userId) ? userId : 0;
_userAuthInfo.FirstName = firstName ?? string.Empty;
@@ -88,6 +89,7 @@ public class AuthService
_userAuthInfo.IsSignMainContract = bool.TryParse(isSignMainContractStr, out var isSignMainContract) && isSignMainContract;
_userAuthInfo.HasPurchasedPackage = bool.TryParse(hasPurchasedPackageStr, out var hasPurchased) && hasPurchased;
_userAuthInfo.IsClubMemberActive = bool.TryParse(isClubMemberActiveStr, out var isClubActive) && isClubActive;
_userAuthInfo.HasAddress = bool.TryParse(hasAddressStr, out var hasAddress) && hasAddress;
}
private async Task<List<(string key, string value)>?> GetTokenClaimsAsync()
@@ -11,4 +11,5 @@ public class UserAuthInfo
public bool IsSignMainContract { get; set; }
public bool HasPurchasedPackage { get; set; }
public bool IsClubMemberActive { get; set; }
public bool HasAddress { get; set; }
}
@@ -1,5 +1,5 @@
{
"GwUrl": "https://cms.kbs1.ir",
"GwUrl": "https://cms.kbs2.ir",
"DownloadUrl": "",
"EncryptionSettings": {
"Key": "kmcQ3XTmH4mrdh8VHziuscyf8LLYjG//Kyni81nH/0E=",
@@ -14,5 +14,10 @@
"Microsoft.AspNetCore": "Warning"
}
},
"Seo": {
"SiteUrl": "https://kbs2.ir",
"SiteName": "کارابازار سلامت",
"DefaultDescription": "باشگاه مشتریان کارابازار سلامت (KBS) — فروشگاه محصولات سلامت، پاداش شفاف، کیف پول و شبکه فروش."
},
"AllowedHosts": "*"
}