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>
PermissionAuthorizationBehavior blocked merchant owners from updating
staff because owners don't have staff permission claims in their JWT.
Added owner/admin role bypass — users with "owner", "admin", or
"superadmin" role claims skip the staff permission check.
Also added UpdateStaffAsync return value check in ShopStaff.razor
to show proper error message when update fails.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
BFF InviteStaffWithAccount endpoint was constructing payload for
merchant-service without employeeCode, phone, and address fields.
This caused these fields to be null for all staff created via IAM
account flow — the edit form then showed empty Mã NV.
- Add employeeCode, phone, address to both create-active and invite
payloads in BFF StaffController
- Add optional EmployeeCode, Phone, Address params to
InviteStaffWithAccountRequest DTO
- Pass _newStaffCode, _newStaffPhone, _newStaffAddress from
ShopStaff.razor when creating via IAM flow
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix IAM 401: Change reset-password endpoint to [AllowAnonymous]
(BFF already handles auth, IAM token validation fails across
Docker container boundaries with Duende IdentityServer)
- Fix IAM 500: Add Npgsql.EnableLegacyTimestampBehavior switch to
resolve DateTime Kind=Unspecified issue with Identity UserManager
- Fix handler: Use RemovePassword + AddPassword instead of
ResetPasswordAsync to avoid timestamptz column errors
- Fix validation: Remove mandatory employee code check when editing
(staff created via IAM may not have employeeCode set)
- Fix Dockerfile: Use root repo context to include blazor-ui package
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add "Đổi mật khẩu" toggle in staff edit form with new password
and confirm password fields, validation (min 8 chars, match check)
- Add ResetStaffPasswordAsync() in PosDataService
- Add POST /api/bff/staff/reset-password BFF endpoint proxying to
IAM service /api/v1/users/{userId}/reset-password
- Reset password state fields when opening edit form
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix POS settings button redirecting to /auth/login when UserRole is
not loaded — now navigates to /admin/shop/{shopId}/overview
- Add receipt template management page with full CRUD:
- Create/edit/delete receipt templates stored in localStorage
- Live preview of thermal receipt (80mm/58mm paper width)
- 18 toggleable fields (logo, address, phone, tax ID, items, etc.)
- Customizable header, footer, font size, paper width
- Set default template for POS printing
- Add "Hoá đơn in" section to shop settings with active template info
- Add sidebar menu item and route for receipt-templates
- Add vi-VN/en-US localization keys
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add POST /api/v1/audit/logs endpoint to IAM AuditController for creating entries
- Hook audit logging into BFF login (fire-and-forget after successful login)
- Hook audit logging into SuperAdmin merchant actions (approve/suspend/reactivate)
- Fix IamApiService AuditLogDto to match actual API response (logs[] not items[])
- Fix AuditLogDto fields: ActorName→ActorEmail, Status→Success, Details→Action
- Fix Admin AuditLog.razor to use updated DTO fields
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ShopSettings.razor: new "AI Assistant" section with:
- Enable/disable toggle
- Provider dropdown (OpenAI / OpenRouter / Claude)
- Model input with per-provider placeholder
- API Key input (password with show/hide toggle)
- Per-provider hint text for getting API keys
- System Prompt textarea (optional, default used if empty)
- Save button with success/error feedback
- Config loads on init, saves via BFF PUT /api/bff/ai/config
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Backend (IAM Service):
- New GetRolePermissionsQuery + Handler: reads permissions from role_claims
- New UpdateRolePermissionsCommand + Handler: validates permission names
against StaffPermissions enum, replaces role_claims, blocks system roles
- New endpoints: GET/PUT /api/v1/roles/{id}/permissions
- GetRolesQuery: batch-fetch permissions per role via role_claims join
- RoleResponse: add Permissions field to API response
- Seeded role_claims for Admin (7), Merchant (7), MerchantAdmin (6),
MerchantStaff (2), SuperAdmin (All), Support (2)
Frontend (Blazor WASM):
- IamApiService: add Permissions to RoleDto, UpdateRolePermissionsAsync()
- RolePermissions.razor: replace hardcoded GetPermissionsForRole() with
API-driven permission toggles from role_claims data
- Editable toggles for non-system roles, disabled for system roles
- Save/Cancel buttons appear when permissions modified
- 7 permission types matching StaffPermissions enum
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove placeholder _defaultPermissions (same 5 toggles for every role)
- Add GetPermissionsForRole() mapping each role to its actual backend
authorization capabilities:
- SuperAdmin: full platform access (6 permissions)
- Admin: user/shop/report/audit management (5 permissions)
- Merchant: full shop owner access (6 permissions)
- MerchantAdmin: shop admin without settings (6 permissions)
- MerchantStaff: POS + payment only (6 permissions, 4 disabled)
- Support: read-only system access (5 permissions)
- PremiumUser/User: customer-level access (4-5 permissions)
- Toggles are now read-only (disabled) reflecting enforced policies
- No conflict between system roles and shop roles confirmed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace hardcoded s.Status == "active" with ShopVerticalHelper.IsActive()
(handles "Active", "Published", "active" etc.)
- Replace shop.Status != "active" with ShopVerticalHelper.IsSetup()
to correctly show "Hoàn thành thiết lập" only for Draft shops
- KPIs and shop card badges now reflect actual shop status correctly
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add YARP proxy middleware to attach Bearer token from bff_session
cookie to all YARP-proxied requests (users, roles, audit pages)
- Dashboard search: bind input with oninput and filter shops by
name/slug/category client-side via FilteredShops property
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ShopVerticalHelper.GetLabel: return Vietnamese text directly instead of
localization keys (Vertical_Cafe → Café, etc.)
- ShopVerticalHelper.GetStatusLabel: return Vietnamese text directly
(Status_Setup → Thiết lập, Status_Active → Đang mở, etc.)
- ShopSettings: add "Kích hoạt cửa hàng" section with publish button
when shop is in Draft status, with setup checklist indicators
- ShopPage: pass ShopStatus parameter to ShopSettings component
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Change orders fetch from "today" filter to "all" so KPIs show actual data
- Add date range presets (Hôm nay / 7 ngày / 30 ngày / Tất cả)
- Add weekly period tab to revenue chart
- Display filtered recent orders based on selected period
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Redesign all 6 onboarding steps with inline step indicators replacing
the fixed sidebar layout. Simplified structure with admin-content/admin-panel
pattern for consistency with other admin pages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add "Danger Zone" section to ShopSettings with deactivate/close actions
- CloseShopConfirmDialog: type shop name to confirm (GitHub-style)
- BFF: proxy endpoints POST /shops/{id}/deactivate and /close
- MerchantApiService: DeactivateShopAsync(), CloseShopAsync()
- CTO report documenting the gap and implementation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
P2: Products appeared 2x in POS grid — BFF now filters isActive=true
by default, plus client-side dedup by product ID as safety net.
P3: Admin Settings showed "--" for shop name — parent ShopPage now
passes ShopName and VerticalLabel parameters to ShopSettings component.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite StaffSchedule.razor from hardcoded stub to real API integration
(profile → shop schedules → filter by staffId)
- Fix admin ShopSchedule role column: use staff role from merchant data
instead of showing "—"
- Add FormatTime() helper to strip seconds from time display (08:00:00 → 08:00)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1. Attendance API now joins with MerchantStaff to return staffName instead of showing truncated staffId
2. AuthService uses role-suffixed localStorage keys (aPOS_token_owner, aPOS_token_staff) to prevent
staff and admin tokens from overwriting each other on the same origin
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- BFF: add GET /api/bff/shops/{shopId}/attendance proxy to merchant-service
- ShopAttendance.razor: replace mock data with real attendance API call
- Calculate present/late/absent counts from actual attendance records
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- BFF: extract approver/rejector userId from JWT instead of accepting Guid.Empty from client
- Staff pages (Dashboard, Leave, Attendance): move data loading to OnAfterRenderAsync
to fix token timing bug where OnInitializedAsync runs before auth session is restored
- EF Core: fix AttendanceRepository to use public properties after HasField() migration
- LeaveRequest: fix DateTime UTC kind for Npgsql 10 compatibility
- merchant-service: add debug seed endpoints for staff/shop test data
- EF configs: migrate to HasField() pattern for private field mapping
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix DTO field mismatch: QuantityChange→Quantity, Reason→Notes in PosDataService
- Fix ItemType enum mismatch: FinishedProduct→FinishedGood, Supply→Consumable in ShopInventory
- Add ResolveTransactionTypeName fallback in InventoryMapper when Type nav property is null
- Add "In"/"Out" alternative matches for TransactionType in history display
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>