mobile/admin-settings-inventory.md

Admin Settings Inventory — Mobile Planning

Mục đích: liệt kê toàn bộ cấu hình / dữ liệu mà salon owner đang quản lý trên booking-web/admin để lên plan mobile app (owner + staff). Mỗi mục ghi rõ: nguồn dữ liệu, API endpoint, đang/không enforce ở đâu, mức ưu tiên khi đưa lên mobile.

Đọc cùng:

Quy ước ưu tiên:

  • P0 — MVP mobile (phải có trong bản đầu tiên)
  • P1 — V1 (có sau 1–2 sprint, sau khi MVP chạy)
  • P2 — V2+ (để sau, desktop admin vẫn phục vụ được)
  • ❌ Desktop-only (không nên đưa lên mobile, quá phức tạp hoặc ít dùng)

0. Tổng quan tầng cài đặt

Hệ thống có 4 tầng cấu hình, tất cả thuộc booking-web/admin:

Tầng Scope Model DB Ai quản lý
Tenant-level settings Toàn salon Tenant.settings (JSON), Tenant.branding (JSON), Tenant.address (JSON) Owner
Catalog Dịch vụ, thuế, kế toán Service, ServiceCategory, Tax, AccountingAccount Owner
Resources Staff + lịch làm việc + time-off Resource, ResourceSchedule, ResourceScheduleOverride, TimeOff, ResourceSkill Owner
Payment config Gateway integration TenantPaymentConfig Owner (cần trợ giúp kỹ thuật)
Loyalty Chương trình khách hàng trung thành LoyaltyCard Owner

UI entry: /admin/settings (8 tabs), /admin/staff, /admin/services, /admin/loyalty, /admin/work-schedule.


1. Settings — General tab

Route: /admin/settings?tab=general Component: booking-web/src/components/settings/GeneralSection.tsx API: GET /api/tenants/me, PATCH /api/tenants/:id

Field Type Mặc định Enforce Mobile
name string P0
slug string (kebab-case) unique, chỉ đổi được qua superadmin ❌ Read-only
industryType enum (beauty|nail|barbershop) beauty quyết định default settings + labels P1 (read-only trước, edit có confirm)
settings.organizationName string? null P1
settings.currency ISO 4217 (3 chữ) NOK toàn bộ money format P0 (read-only)
settings.timezone IANA tz Europe/Oslo mọi so sánh giờ làm việc P0 (read-only)

💡 Lưu ý mobile: slug, currency, timezonecritical data — không nên cho đổi trên mobile vì breaking change với bookings cũ. Chỉ cho xem.


2. Settings — Booking tab (Booking Policy)

Route: /admin/settings?tab=booking Component: booking-web/src/components/settings/BookingSection.tsx + BookingPolicyEditor API: PATCH /api/tenants/:id (nested trong settings)

Field Type Mặc định Ý nghĩa Mobile
walkInEnabled boolean true Cho phép tạo booking walk-in trên admin P0
autoConfirm boolean false (beauty/nail), true (barbershop) Booking online tự CONFIRMED thay vì PENDING P0
bookingMode assigned_only | allow_unassigned allow_unassigned Khách book không chọn staff được không P0
allowDoubleBooking boolean false Cho phép 2 booking trùng giờ cùng 1 staff P1
cancellationHours int 0–168 24 (beauty), 2 (barbershop) Số giờ trước giờ hẹn được cancel miễn phí P0
maxBookingDaysInAdvance int 1–365 30 Khách book trước tối đa bao nhiêu ngày P1
posEnabled boolean false Bật POS mode (chưa dùng) ❌ Không triển khai MVP
depositEnabled boolean false Yêu cầu đặt cọc khi book P1
depositType percentage | fixed percentage Cọc theo % tổng hay số cố định P1
depositValue int 0 Giá trị cọc (0–100 nếu %, øre nếu fixed) P1

Enforce matrix (đã document chi tiết trong docs/flows/booking-flow.md):

  • autoConfirm → public booking API set status
  • bookingMode → public booking validate resourceId
  • cancellationHours → customer cancel endpoint
  • maxBookingDaysInAdvance → availability API + create booking
  • depositEnabled/type/value → payment flow (dispatch deposit command)

⚠️ Cảnh báo chéo: Nếu depositEnabled=true nhưng chưa có TenantPaymentConfig active → onboarding đã cảnh báo. Mobile cũng phải show cảnh báo này.


3. Settings — Business Hours tab

Route: /admin/settings?tab=businessHours Component: booking-web/src/components/settings/BusinessHoursSection.tsx + shared business-hours editor API: PATCH /api/tenants/:id (settings.businessHours)

Schema (lưu trong Tenant.settings.businessHours):

[
  {
    "dayOfWeek": 1,       // 0=Sunday, 6=Saturday
    "isOpen": true,
    "slots": [
      { "startTime": "09:00", "endTime": "12:00" },
      { "startTime": "13:00", "endTime": "17:00" }  // multi-slot, break giữa trưa
    ]
  }
]
Feature Desktop đã có Mobile
7 ngày × toggle mở/đóng P0
Multi-slot / ngày P0
Copy 1 ngày → ngày khác P1
Copy all → staff schedules (applyToStaff: true in UpdateTenantDto) P1 (cần confirm dialog)

💡 Editor pattern: đã có shared editor ở booking-web/src/components/business-hours/. Mobile nên thiết kế native picker tối ưu cho ngón tay (tap & hold range slider).


4. Settings — Branding tab

Route: /admin/settings?tab=branding Component: booking-web/src/components/settings/BrandingSection.tsx API: PATCH /api/tenants/:id (branding)

Field Type Mobile
branding.primaryColor hex (#RRGGBB) P1
branding.secondaryColor hex P1
branding.logoUrl URL (MinIO upload) P1 (cần expo-image-picker + upload)
branding.coverUrl URL (MinIO upload) P1

💡 Upload: API đã có upload endpoint qua MinIO. Mobile cần expo-image-picker + resize trước khi POST.


5. Settings — About tab

Route: /admin/settings?tab=about Component: booking-web/src/components/settings/AboutSection.tsx API: PATCH /api/tenants/:id (description, HTML)

  • description (HTML, rich text via Tiptap, sanitize-html ở backend).
  • Mobile: P2. Rich text editor trên mobile phức tạp — tạm thời cho nhập plain text + markdown hoặc defer.

6. Settings — Location tab

Route: /admin/settings?tab=location Component: booking-web/src/components/settings/LocationSection.tsx + AddressSearch, LocationMap API: PATCH /api/tenants/:id (address)

Field Type Mobile
address.street string P0 (read-only trước, edit sau)
address.city string P0
address.postalCode string P0
address.country string P0
address.latitude number P1
address.longitude number P1
  • Desktop dùng Nominatim (OpenStreetMap) geocoding + pigeon-maps interactive map.
  • Mobile: có thể tận dụng native map (react-native-maps) + location picker. P1 cho edit, P0 cho hiển thị.

7. Settings — Tax & Accounting tab

Route: /admin/settings?tab=tax Components: TaxSettings.tsx, AccountingSettings.tsx, TaxFormModal.tsx, AccountFormModal.tsx API: GET/POST/PATCH/DELETE /api/taxes, /api/accounting-accounts

7.1 Tax rates

Field Type Mobile
name string ("25%") P2
rate float (percent) P2
isDefault boolean P2
isActive boolean P2

Seed mặc định (Na Uy): 25% (default), 15%, 0% — xem booking-api/src/core/tenant/tenant-defaults.ts.

7.2 Accounting accounts

Field Type Mobile
code string ("3000") P2
name string P2
taxId FK → Tax P2
isDefault boolean P2

Đánh giá: Tax + Accounting là accountant territory, owner thường setup 1 lần rồi quên. Mobile không cần trừ khi đưa vào flow "tạo service" (cần chọn accounting account).


8. Settings — Payment tab

Route: /admin/settings?tab=payment Components: PaymentSettings.tsx, ProviderCard, ProviderConfigDrawer, BamboraConfigForm, HealthCheckBadge API: GET/POST/PATCH/DELETE /api/admin/payment-configs, POST .../:id/rotate, POST .../:id/activate, POST .../:id/health-check

Feature Desktop Mobile
List provider configs (Bambora, Worldline, Stripe, Vipps, Nets, Adyen) P1 (list + toggle active)
Create/edit credentials (encrypted) Desktop-only (nhập API key phức tạp, an toàn)
Rotate encryption key
Activate / deactivate provider P1
Health check P1 (quan trọng khi on-site đi xem provider có chạy không)
Display name, test/prod mode P1

⚠️ Security: credentials chỉ nhập từ desktop (đã có khuyến nghị trong memory "Payment Implementation"). Mobile chỉ nên: xem status, toggle active, health check.


9. Services & Categories (Catalog)

Route: /admin/services Components: ServicesContent.tsx, ServiceList.tsx, ServiceFormModal.tsx, CategoryManager.tsx, AddCategoryModal.tsx API: /api/services, /api/service-categories

9.1 Service

Field Type Mobile
name string P0
description string? P0
duration int (phút) P0
price int (øre, minor unit) P0
currency ISO 4217 P0 (auto từ tenant)
categoryId FK? P0
accountingAccountId FK? P1
isActive boolean P0
sortOrder int P1
metadata JSON (industry-specific) P1

9.2 ServiceCategory

Field Mobile
name P0
sortOrder P1

💡 Mobile nên có quick-create service ngay từ booking drawer (khi staff/owner tạo booking và chưa có dịch vụ trong catalog).


10. Staff / Resources

Route: /admin/staff Components: StaffContent.tsx, StaffList.tsx, StaffFormModal.tsx, MetadataFields.tsx API: /api/resources, /api/resources/:id/skills

Field Type Mobile
type string (staff/room/equipment) P0 (default staff)
name string P0
color hex? P0
imageUrl string? P1 (camera upload)
description string? P1
isActive boolean P0
isBookableOnline boolean P0
sortOrder int P1
metadata.jobTitle, metadata.phone, metadata.email, metadata.commission JSON P1
userId FK User? P1 (link staff → login user)
Skills (assigned services) ResourceSkill[] P0

💡 Industry-aware label: hiển thị "Barbers" / "Therapists" / "Staff" theo industryType. Dùng useResourceLabel() hook ở web — mobile cần port logic tương đương.


11. Work Schedule (Staff lịch làm việc)

Route: /admin/work-schedule Components: WorkScheduleContent.tsx, ScheduleGrid.tsx, ScheduleCellEditor.tsx, RecurringScheduleModal.tsx, TimeOffModal.tsx API: /api/resources/:id/schedules, /api/resources/:id/schedule-overrides, /api/time-offs

11.1 ResourceSchedule (weekly recurring)

  • 1 staff × 7 ngày × multi-slot ({ dayOfWeek, startTime, endTime, isActive }).
  • Mobile P0 — staff cần xem & owner cần sửa nhanh.

11.2 ResourceScheduleOverride (override 1 ngày cụ thể)

  • { resourceId, date, startTime?, endTime?, reason? }null = ngày nghỉ đặc biệt.
  • Mobile P0 — ví dụ owner duyệt nghỉ cho 1 staff trong ngày.

11.3 TimeOff (vacation / sick)

Field Type Mobile
type vacation/sick/personal/other P0
startDate / endDate date P0
startTime / endTime string? (null = full day) P0
description string? (max 100) P1
isApproved boolean P1 (owner approval flow)

💡 Staff request → Owner approve là flow rất tự nhiên trên mobile. Nên làm sớm.


12. Loyalty Programs

Route: /admin/loyalty Components: LoyaltyContent.tsx, LoyaltyList.tsx, LoyaltyFormModal.tsx API: /api/loyalty/cards, /api/loyalty/stamps, /api/loyalty/redemptions, /api/loyalty/points

Field Type Mobile
name string P2
type VISIT_BASED | POINTS_BASED P2
requiredVisits, rewardType, rewardValue (visit-based) int/enum P2
earnRate, redeemRate, expiryMonths, minRedemption (points) int P2
serviceIds string[] (empty = all) P2
isActive boolean P2

Setup loyalty chỉ cần làm 1 lần — defer mobile. Nhưng xem tình trạng stamps/points của khách khi tra profile → P0.


13. Customers

Route: /admin/customers Components: CustomersContent.tsx, CustomerList.tsx, CustomerFormModal.tsx API: /api/customers, /api/tenant-customers

Field Mobile
name, email, phone, notes P0
TenantCustomer.tags[], notes, visitCount, lastVisit, totalSpent P0
Booking history per customer P0
Loyalty stamps / points balance P0

💡 Customer profile trên mobile = màn hình quan trọng cho staff (check lịch sử trước cuộc hẹn, ghi note sau).


14. Bookings

Route: /admin/bookings Components: Calendar (BookingCalendar.tsx, StaffColumn.tsx, BookingBlock.tsx, useDragBooking.ts), List (BookingList.tsx), Drawer (BookingDrawer.tsx, ServicePicker.tsx, AddedServiceCard.tsx) API: /api/bookings, /api/availability, /api/bookings/:id/status/:status

Tính năng đã có (desktop)

  • Day/Week calendar view, drag-drop reschedule, staff column layout.
  • Multi-service booking (1 booking nhiều items với staff riêng, thời gian riêng).
  • Status machine: PENDING → CONFIRMED → ARRIVED → IN_PROGRESS → COMPLETED / CANCELLED / NO_SHOW.
  • Conflict detection (409 on overlap).
  • Audit log (ai làm gì khi nào).
  • Payment summary card (deposit, remaining, refund).
  • Loyalty redemption apply.

📱 Chi tiết phân rã cho mobile ở feature-plan.md.


15. Payments (Transactions)

Route: /admin/payments Components: PaymentsContent.tsx, PaymentList.tsx, PaymentDetailDrawer.tsx, CaptureDialog.tsx, CollectRemainingModal.tsx, RefundDialog.tsx, VoidDialog.tsx API: /api/admin/payments/*

  • List + filter by status (INITIATED, AUTHORIZED, CAPTURED, ...).
  • Detail drawer: events, state machine, provider refs.
  • Actions: capture, void, refund (partial), collect remaining.
  • Mobile: P1 (xem list + capture/refund common cases), action chi tiết ưu tiên desktop.

16. Onboarding Wizard

Route: /admin/onboarding (full-page, guards OWNER nếu tenant.onboardedAt == null) Spec: 7 bước — Welcome → Salon info → Business hours → Services → Staff → Booking policy → Review & Launch.

💡 Mobile: mobile app cũng cần support onboarding cho owner lần đầu đăng ký qua app (ví dụ download app trên store, tạo tài khoản). P1 — MVP có thể force onboard qua web.


17. Dashboard

Route: /admin (default) Components: DashboardContent.tsx, DashboardMetrics.tsx, MetricCard.tsx, TodayTimeline.tsx, UpcomingBookings.tsx, QuickActions.tsx.

Widget Mobile
Metrics (booking today, revenue today, ...) P0
Today timeline (gantt-like timeline booking hôm nay) P0
Upcoming bookings (next 5) P0
Quick actions (new booking, walk-in, ...) P0

18. Profile (Admin user settings)

Route: /admin/profile API: /api/auth/me, /api/auth/change-password

  • Email, phone, password. P0 cho mobile.

19. Ma trận ưu tiên — cheatsheet

Area P0 (MVP) P1 (V1) P2+ / Desktop-only
General info name, currency/tz read-only organizationName slug, industryType edit
Booking policy walkIn, autoConfirm, bookingMode, cancellationHours deposit*, maxBookingDays, allowDoubleBooking posEnabled
Business hours 7-day × multi-slot copy day, apply to staff
Branding colors, logo, cover upload
About rich text editor
Location read-only display edit address + map
Tax/Accounting full CRUD
Payment config view + activate + health-check edit credentials
Services CRUD + category + active toggle accounting account, sortOrder, image, metadata
Staff CRUD + skills + active + color avatar upload, metadata, userId link bulk ops
Work schedule weekly + overrides + TimeOff approve TimeOff flow, copy week bulk grid editor
Customers CRUD + history + notes + tags advanced filter bulk import
Bookings list + calendar day + create + status + walk-in drag-drop, multi-service, refund analytics
Payments list + basic capture/refund drawer detail, event log admin overrides
Loyalty view customer balance card CRUD
Dashboard metrics + timeline + upcoming
Profile email/phone/password 2FA
Onboarding full 7-step wizard

20. Gaps / câu hỏi cần trả lời trước khi build mobile

  1. Owner vs Staff UI split — cùng app hay 2 app? Hiện tại README nói "role-based UI switching within a single app" → đi theo hướng này.
  2. Offline-first scope — WatermelonDB sync cho những entity nào? Đề xuất: Booking, Resource, Service, Customer, TenantSettings snapshot. Payment KHÔNG offline (phải online để gọi provider).
  3. Push notification — Expo push hay FCM trực tiếp? PRD nêu Expo push (US-7.3). Cần chuẩn bị expo-notifications + device token endpoint ở API.
  4. Subdomain routing — web dùng {slug}.app.no, mobile dùng API trực tiếp → không lệ thuộc, ok.
  5. Deep link từ QR code/b/:slug/bookings/:id đã gen sẵn. Mobile staff cần scan QR → open booking detail. Cần expo-linking + deep link scheme.
  6. Payment credential — confirm dứt khoát: nhập credentials chỉ qua web admin, mobile chỉ view + toggle.
  7. Rich text (description, service description) — dùng markdown thay vì HTML trên mobile để tránh Tiptap phức tạp.
  8. Language — Norwegian Bokmål (nb-NO) primary, chuẩn bị i18n từ đầu (expo-localization + i18n-js hoặc react-intl).
  9. Admin "login as tenant" (impersonate) — chưa có, cả web cũng chưa. Mobile có cần không? → Không cho MVP, dùng desktop.

21. Sync với existing docs

Khi thêm/sửa setting mới trên mobile, phải update đồng thời:

Quy tắc "KHÔNG fallback settings" áp dụng cho mobile như backend — data phải complete trong DB, UI không được silent default.