Super Admin Device Creation Flow Design

Date: 2026-05-02
Status: Approved (conversation-level), pending file review gate

1) Goal

Tối ưu luồng Thêm thiết bị mới để Super Admin tạo thiết bị nhanh hơn bằng cách:

  • Đưa Loại thiết bị lên đầu form
  • Tự sinh sẵn Tên thiết bịMã serial ngay sau khi chọn loại
  • Vẫn cho phép người dùng sửa tay hai field tự sinh trước khi lưu
  • Biến Mô hình thành optional ở frontend

Thiết kế phải giữ backend là nguồn sự thật cho rule đánh số và uniqueness của serialNumber, nhưng không làm nặng quy trình bằng reservation hoặc locking.

2) Current State

  • Form Thêm thiết bị mới hiện nằm trong frontend/src/pages/devices/DeviceList.tsx.
  • Thứ tự field hiện tại là:
    • Tên thiết bị
    • Mã serial
    • Loại thiết bị
    • Mô hình
  • Frontend đang coi model là required trong frontend/src/schemas/device.schema.ts.
  • Backend CreateDeviceDto đang coi model là optional.
  • Backend hiện chỉ tạo thiết bị từ payload gửi lên; chưa có API generate identity preview.
  • serialNumber đang được backend kiểm tra unique tuyệt đối.
  • mqttTopic đang được backend sinh từ serialNumber lúc tạo thiết bị.

3) Options Considered

Option A: Frontend-only suggestion

Frontend tự generate Tên thiết bịMã serial từ Loại thiết bị, không gọi backend trước khi submit.

Pros

  • Triển khai nhanh
  • UX mượt, không thêm round-trip

Cons

  • Dễ đụng sequence khi nhiều người tạo cùng lúc
  • Logic generate bị đẩy lên frontend, khó giữ backend làm nguồn sự thật

Option B: Backend-only generation on create

Frontend chỉ cho chọn Loại thiết bị, backend tự generate identity lúc lưu.

Pros

  • Rule tập trung hoàn toàn ở backend
  • Không có preview “ảo” trước submit

Cons

  • Không đáp ứng yêu cầu preview sẵn để Super Admin chỉnh trước khi lưu
  • UX kém hơn cho mục tiêu nhập liệu nhanh

Option C (selected): Hybrid preview with backend-generated suggestion

Frontend gọi backend để lấy cặp identity gợi ý sau khi chọn Loại thiết bị, sau đó điền sẵn vào form và cho phép sửa tay trước khi submit.

Pros

  • Khớp mục tiêu Super Admin tạo nhanh
  • Backend vẫn nắm rule generate
  • Frontend có preview sẵn để người dùng chỉnh nếu cần

Cons

  • Cần thêm endpoint/service mới
  • Preview không thể được coi là “reserved”

4) Selected Design

Chọn Option C: backend cấp identity preview theo loại thiết bị, frontend dùng preview này để autofill form.

4.1 Form flow

Luồng form mới:

  1. Mở form tạo mới:
    • Loại thiết bị đứng đầu form
    • Tên thiết bị, Mã serial để trống
    • Mô hình hiển thị nhưng không bắt buộc
  2. Người dùng chọn Loại thiết bị
  3. Frontend gọi backend để lấy identity preview
  4. Frontend điền sẵn:
    • Tên thiết bị
    • Mã serial
  5. Người dùng có thể:
    • giữ nguyên giá trị gợi ý và lưu
    • sửa một hoặc cả hai field trước khi lưu

4.2 Generate rules

Mapping mặc định theo loại thiết bị:

  • sensor -> name prefix Sensor, serial prefix SNS
  • gateway -> name prefix Gateway, serial prefix GTW
  • inverter -> name prefix Inverter, serial prefix INV
  • feeder -> name prefix Feeder, serial prefix FDR

Format mặc định:

  • Tên thiết bị: <NamePrefix> <NNN>
  • Mã serial: <SerialPrefix>-<NNN>

Ví dụ:

  • Sensor 001 / SNS-001
  • Gateway 001 / GTW-001

Rule đánh số:

  • Sequence chạy toàn hệ thống theo từng loại thiết bị
  • Sequence mặc định dùng 3 chữ số: 001, 002, 003
  • “Số tiếp theo” được suy ra từ sequence lớn nhất hiện có của cùng loại rồi cộng 1

4.3 Manual override behavior

Frontend phải theo dõi trạng thái “đã chỉnh tay” riêng cho:

  • name
  • serialNumber

Hành vi mong muốn:

  • Nếu người dùng chưa chỉnh tay field nào và đổi Loại thiết bị, frontend được phép regenerate field đó
  • Nếu người dùng đã chỉnh tay một field, frontend không được silently overwrite field đó nữa
  • Nếu người dùng đã sửa cả hai field, đổi loại thiết bị không được đè lại hai field đó

Thiết kế này tối ưu cho nhập liệu nhanh nhưng vẫn tôn trọng chủ đích người dùng.

4.4 Backend preview contract

Backend thêm API preview identity cho tạo thiết bị mới, ví dụ:

  • GET /api/v1/devices/generate-identity?type=sensor

Hoặc endpoint tương đương nếu team muốn dùng POST.

Response tối thiểu nên trả:

{
  "type": "sensor",
  "name": "Sensor 001",
  "serialNumber": "SNS-001",
  "sequence": 1
}

Endpoint này:

  • Không tạo thiết bị
  • Không giữ chỗ sequence
  • Chỉ trả về gợi ý tốt nhất tại thời điểm gọi

4.5 Create-time validation and race conditions

Preview identity không phải reservation.

Do đó ở bước create device:

  • Backend tiếp tục check serialNumber unique tuyệt đối
  • Nếu serial preview đã bị chiếm trong khoảng thời gian giữa preview và submit, backend trả lỗi conflict rõ ràng
  • Frontend phải hiển thị lỗi dễ hiểu và cho phép người dùng:
    • sửa tay serial
    • hoặc regenerate preview mới rồi lưu lại

Thiết kế này giữ tính đúng đắn dữ liệu mà không đưa thêm cơ chế lock phức tạp vào scope hiện tại.

4.6 Sequence parsing policy

Backend cần một helper thống nhất để:

  • map type -> name prefix
  • map type -> serial prefix
  • đọc sequence từ các serial đúng pattern mặc định

Chỉ các serial khớp pattern mặc định của loại tương ứng mới được dùng để tính next sequence, ví dụ:

  • SNS-001
  • GTW-014

Serial custom không theo pattern mặc định:

  • vẫn hợp lệ nếu unique
  • không được dùng để làm hỏng hoặc nhảy sai bộ đếm mặc định

4.7 Model field behavior

Mô hình phải được đổi thành optional ở frontend để đồng bộ với backend hiện tại.

Điều này bao gồm:

  • bỏ required marker trong UI
  • cập nhật validation schema tương ứng
  • không chặn submit nếu field này để trống

5) File-Level Scope

Expected scope ở mức thiết kế:

  • frontend/src/pages/devices/DeviceList.tsx
    • đổi thứ tự field
    • thêm autofill flow
    • theo dõi manual override state
  • frontend/src/schemas/device.schema.ts
    • đổi model thành optional trong create schema
  • frontend/src/services/device.service.ts
    • thêm method gọi API generate identity
  • frontend/src/types/device.types.ts
    • thêm type cho generate identity request/response nếu cần
  • backend/src/devices/controllers/devices.controller.ts
    • thêm route generate identity
  • backend/src/devices/devices.service.ts
    • thêm logic generate preview
  • backend/src/devices/dto/
    • thêm DTO/query DTO/response DTO cho generate identity nếu cần

6) Acceptance Criteria

  1. Trong form tạo thiết bị mới, Loại thiết bị là field đầu tiên.
  2. Khi chọn Loại thiết bị, hệ thống tự điền Tên thiết bịMã serial theo rule mặc định.
  3. Tên thiết bịMã serial vẫn chỉnh tay được trước khi lưu.
  4. Nếu người dùng chưa chỉnh tay một field, field đó được phép regenerate khi đổi Loại thiết bị.
  5. Nếu người dùng đã chỉnh tay một field, frontend không tự đè field đó âm thầm.
  6. Mô hình không còn là field bắt buộc ở frontend.
  7. Backend vẫn enforce uniqueness của serialNumber ở bước create.
  8. Nếu serialNumber bị trùng ở lúc submit, hệ thống trả lỗi rõ ràng để người dùng regenerate hoặc sửa tay.
  9. Luồng mới giúp Super Admin tạo thiết bị với ít thao tác nhập tay hơn luồng hiện tại.

7) Out of Scope

  • Reservation hoặc locking sequence
  • Thay đổi rule unique tuyệt đối của serialNumber
  • Migrate dữ liệu cũ sang pattern serial mới
  • Mở rộng form hiện tại để thêm pondId, installationDate, warrantyMonths, ownershipType
  • Thiết kế lại toàn bộ trang danh sách thiết bị

8) Risks and Mitigations

  • Risk: Hai người cùng lấy preview giống nhau rồi submit gần thời điểm nhau
    Mitigation: Backend giữ unique validation ở bước create; frontend xử lý conflict rõ ràng.

  • Risk: Dữ liệu cũ có serial custom khiến việc tính next sequence sai
    Mitigation: Chỉ parse sequence từ serial khớp pattern mặc định theo từng loại.

  • Risk: Frontend overwrite giá trị user đã sửa khi đổi loại thiết bị
    Mitigation: Theo dõi cờ manual override riêng cho từng field.

  • Risk: model optional ở frontend nhưng code submit cũ vẫn giả định luôn có string
    Mitigation: Đồng bộ type/schema/service payload để chấp nhận undefined hoặc empty value hợp lệ.