fix(merchant): wire up merchant registration in onboarding and settings
All checks were successful
Build & Deploy to K8s / build-and-deploy (push) Successful in 9m29s

OnboardingBusiness.razor was only navigating to the next step without
calling the merchant registration API, so no merchant record was ever
created in the database. This caused settings page updates to fail with
"Merchant not found" and the SuperAdmin panel to show zero merchants.

- Inject MerchantApiService and call RegisterMerchantAsync on "Tiếp tục"
- Remove hardcoded demo data from onboarding form fields
- Add fallback in AdminSettings SaveMerchant to auto-register if PUT fails
- Add BFF POST /api/bff/account/register-merchant endpoint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ho Ngoc Hai
2026-04-12 20:46:27 +07:00
parent 54fbaeaffe
commit 5e2f20967d
3 changed files with 118 additions and 11 deletions

View File

@@ -764,10 +764,33 @@
_saving = true; _merchantMsg = null;
try
{
// EN: Try to update existing merchant first
// VI: Thử cập nhật merchant hiện tại trước
var ok = await DataService.PutAsync("api/bff/account/merchant", new { businessName = _businessName, taxId = _taxId });
_merchantMsg = ok ? "Cập nhật doanh nghiệp thành công!" : "Không thể cập nhật";
_merchantMsgOk = ok;
if (ok) _editingMerchant = false;
if (ok)
{
_merchantMsg = "Cập nhật doanh nghiệp thành công!";
_merchantMsgOk = true;
_editingMerchant = false;
}
else
{
// EN: Merchant might not exist yet — try to register first, then update
// VI: Merchant có thể chưa tồn tại — thử đăng ký trước, rồi cập nhật
var registerOk = await DataService.PostAsync("api/bff/account/register-merchant",
new { businessName = _businessName, type = "Individual", taxId = _taxId });
if (registerOk)
{
_merchantMsg = "Đăng ký doanh nghiệp thành công!";
_merchantMsgOk = true;
_editingMerchant = false;
}
else
{
_merchantMsg = "Không thể cập nhật hoặc đăng ký doanh nghiệp";
_merchantMsgOk = false;
}
}
}
catch (Exception ex) { _merchantMsg = ex.Message; _merchantMsgOk = false; }
finally { _saving = false; }

View File

@@ -1,6 +1,7 @@
@page "/admin/onboarding/business"
@layout AdminLayout
@inherits AdminBase
@inject WebClientTpos.Client.Services.MerchantApiService MerchantApi
@*
EN: Onboarding Step 1 — Business info (name, type, tax ID, address, logo upload)
@@ -109,10 +110,25 @@
</div>
</div>
@if (!string.IsNullOrEmpty(_errorMsg))
{
<div style="padding:10px 14px;border-radius:8px;font-size:13px;background:rgba(239,68,68,0.12);color:#EF4444;">
@_errorMsg
</div>
}
<div style="display:flex;justify-content:flex-end;">
<button class="admin-btn-primary" @onclick='() => NavigateTo("onboarding/store")' style="display:flex;align-items:center;gap:8px;">
<span>Tiếp tục</span>
<i data-lucide="arrow-right"></i>
<button class="admin-btn-primary" @onclick="SaveAndContinue" disabled="@_saving" style="display:flex;align-items:center;gap:8px;">
@if (_saving)
{
<MudProgressCircular Indeterminate="true" Size="Size.Small" Color="Color.Surface" Style="width:16px;height:16px;" />
<span>Đang lưu...</span>
}
else
{
<span>Tiếp tục</span>
<i data-lucide="arrow-right"></i>
}
</button>
</div>
</section>
@@ -121,13 +137,15 @@
@code {
private int currentStep = 1;
private string _businessName = "aPOS Bistro";
private string _taxId = "0312345678";
private string _address = "123 Nguyễn Huệ, Quận 1, TP.HCM";
private string _phone = "028 7300 1234";
private string _email = "contact@goodgo.vn";
private string _businessName = "";
private string _taxId = "";
private string _address = "";
private string _phone = "";
private string _email = "";
private string _businessType = "fnb";
private string _logoPlaceholder = "Chưa chọn tệp";
private bool _saving;
private string? _errorMsg;
private record StepInfo(int Index, string Label);
private readonly StepInfo[] _steps =
@@ -150,4 +168,62 @@
private string GetBusinessTypeLabel()
=> Array.Find(_businessTypes, t => t.Key == _businessType)?.Label ?? "F&B";
/// <summary>
/// EN: Register merchant via API, then navigate to next step.
/// VI: Đăng ký merchant qua API, sau đó chuyển sang bước tiếp theo.
/// </summary>
private async Task SaveAndContinue()
{
_errorMsg = null;
// EN: Validate required fields
// VI: Kiểm tra trường bắt buộc
if (string.IsNullOrWhiteSpace(_businessName))
{
_errorMsg = "Vui lòng nhập tên doanh nghiệp";
return;
}
_saving = true;
StateHasChanged();
try
{
var dto = new WebClientTpos.Shared.DTOs.MerchantRegisterDto
{
BusinessName = _businessName.Trim(),
Type = "Individual",
TaxId = string.IsNullOrWhiteSpace(_taxId) ? null : _taxId.Trim()
};
var (result, error) = await MerchantApi.RegisterMerchantAsync(dto);
if (result != null)
{
// EN: Registration successful, proceed to next step
// VI: Đăng ký thành công, chuyển sang bước tiếp theo
NavigateTo("onboarding/store");
}
else if (error != null && error.Contains("already has", StringComparison.OrdinalIgnoreCase))
{
// EN: User already has a merchant account, just continue to next step
// VI: User đã có tài khoản merchant, tiếp tục bước tiếp theo
NavigateTo("onboarding/store");
}
else
{
_errorMsg = error ?? "Không thể đăng ký doanh nghiệp. Vui lòng thử lại.";
}
}
catch (Exception ex)
{
_errorMsg = $"Lỗi: {ex.Message}";
}
finally
{
_saving = false;
StateHasChanged();
}
}
}

View File

@@ -89,6 +89,14 @@ public class AccountController : ControllerBase
public Task<IActionResult> UpdateMerchant([FromBody] JsonElement body) =>
_merchant.PutAsJsonAsync("/api/v1/merchants/me", body).ProxyAsync();
/// <summary>
/// EN: Register current user as a new merchant.
/// VI: Đăng ký user hiện tại làm merchant mới.
/// </summary>
[HttpPost("register-merchant")]
public Task<IActionResult> RegisterMerchant([FromBody] JsonElement body) =>
_merchant.PostAsJsonAsync("/api/v1/merchants/register", body).ProxyAsync();
// ═══ SECURITY ═══
/// <summary>