Dashboard Role Architecture Plan
Cập nhật triển khai (2026-04-25)
Các phần của kiến trúc trong tài liệu này đã được dựng foundation:
- Đã có
backend/src/dashboard/*với module/controller/services/dto riêng, tách khỏireports. - Đã có 5 endpoint role-specific:
GET /api/v1/dashboard/super-adminGET /api/v1/dashboard/adminGET /api/v1/dashboard/ownerGET /api/v1/dashboard/staffGET /api/v1/dashboard/technical
- JWT role đã chuẩn hóa enum active role; frontend decode role fail-closed.
- Frontend đã có
DashboardRoute, lazy route theo role, và redirect/->/dashboard. - Navigation đã tách cấu hình theo role trong
frontend/src/navigation/*,SidebarMenuchỉ còn resolver/render. - Service/hook/type boundary cho dashboard role đã tách riêng trong
frontend/src/services/dashboard,frontend/src/hooks/dashboard,frontend/src/types/dashboard.
Phần chưa hoàn tất:
- Aggregate query/presenter và UI section business thật cho từng role (các phase tiếp theo).
1. Mục tiêu tài liệu
Tài liệu này chuyển hóa docs/dashboard_design.md thành plan kiến trúc code để triển khai dashboard theo từng vai trò trong HMP IoT Platform.
Mục tiêu chính:
- Tách dashboard theo mục tiêu công việc của từng role, không dùng một dashboard chung rồi ẩn/hiện UI.
- Tổ chức file structure để dễ mở rộng, dễ test, dễ lazy-load và tránh bundle phình không cần thiết.
- Đảm bảo dữ liệu được scope từ backend theo multi-tenant/RBAC, không dựa vào frontend để che dữ liệu nhạy cảm.
- Tạo nền tảng cho các phase: Owner/Staff, Admin, Super Admin, Technical, Ticket & Maintenance.
Phạm vi tài liệu:
- Chỉ plan kiến trúc và tổ chức code.
- Không mô tả visual design chi tiết từng component.
- Không thay thế
docs/dashboard_design.md; file này là implementation architecture companion cho tài liệu design đó.
Tài liệu triển khai theo task:
docs/dashboard-role-implementation-tasks.md: task breakdown theo phase, dependencies và Acceptance Criteria để giao dev.
2. Bối cảnh codebase hiện tại
Frontend hiện có:
frontend/src/App.tsx: khai báo route tập trung.frontend/src/components/layouts/DashboardLayout.tsx: layout dashboard dùng chung.frontend/src/components/layouts/SidebarMenu.tsx: menu sidebar tập trung theo permission/role.frontend/src/pages/dashboard/DashboardOverview.tsx: dashboard tổng quan hiện tại.frontend/src/pages/farm/FarmDashboard.tsx: dashboard farm đơn giản hiện tại.frontend/src/hooks/useReports.ts: hooks dashboard/report hiện tại.frontend/src/services/report.service.ts: service gọi/dashboard/*và/reports/*.frontend/src/stores/auth.store.ts: lưurole,permissions,user,accessToken.frontend/src/lib/permissions.ts: decode JWT role/permission và check permission.
Backend hiện có:
backend/src/reports/dashboard.controller.ts: endpoint dashboard hiện tại.backend/src/reports/dashboard.service.ts: service tổng hợp dashboard/report hiện tại.- Các module domain:
farm,aquaculture,devices,financial,inventory,organizations,users,notifications,telemetry. - RBAC và multi-tenancy qua permission guard và
OrganizationFilterInterceptor.
Các quyết định kiến trúc đã chốt cho dashboard:
- Backend role chuẩn hóa bằng enum:
SUPER_ADMIN,ADMIN,OWNER,STAFF,TECHNICAL. - Mỗi user chỉ có một role active tại một thời điểm; không hỗ trợ multi-role active trong dashboard.
- Owner quản lý đúng một farm.
- Technical được tạo ticket nội bộ, nhưng customer ticket vẫn đi qua luồng Owner/Admin/Staff/Super Admin.
- Dashboard endpoint chưa dùng Redis ở phase đầu; chỉ thêm Redis sau khi có số đo hiệu năng.
- Route
/redirect về/dashboard.
Rủi ro nếu tiếp tục mở rộng theo cấu trúc hiện tại:
App.tsxsẽ thành route registry quá lớn.SidebarMenu.tsxsẽ chứa quá nhiều logic role/menu/permission.DashboardOverview.tsxdễ biến thành file conditional theo 5 role.report.service.tssẽ trộn report analytics với role dashboard.- Frontend có nguy cơ nhận dư dữ liệu rồi chỉ ẩn bằng UI, không an toàn cho Technical/Staff.
3. Nguyên tắc kiến trúc
3.1. Role-specific dashboard entry
Mỗi role có dashboard entry riêng:
- Super Admin: SaaS Control Center.
- Admin: Organization Command Center.
- Owner: Farm Operation Dashboard.
- Staff: Field Work Dashboard.
- Technical: Maintenance Dashboard.
Không tạo một dashboard chung kiểu:
if (role === 'Admin') return <AdminBlocks />;
if (role === 'Owner') return <OwnerBlocks />;
Cách này làm file lớn, khó lazy-load, khó test, và dễ lộ dependency không liên quan.
3.2. Backend aggregate theo role
Dashboard là màn hình tổng hợp nhiều domain. Frontend không nên gọi 8-12 API nhỏ cho một dashboard nếu các dữ liệu đó luôn được dùng cùng nhau.
Nên có aggregate endpoint theo role:
GET /api/v1/dashboard/super-admin
GET /api/v1/dashboard/admin
GET /api/v1/dashboard/owner
GET /api/v1/dashboard/staff
GET /api/v1/dashboard/technical
Mỗi endpoint:
- Enforce role/permission ở backend.
- Query đúng scope dữ liệu.
- Trả DTO đã tối ưu cho UI.
- Không trả field nhạy cảm ngoài phạm vi role.
- Có thể cache từng phần theo tính chất dữ liệu.
3.3. Shared UI, khác composition
Dùng chung primitive component:
- KPI card.
- Section card.
- Table card.
- Status badge.
- Empty state.
- Loading skeleton.
- Filter toolbar.
Không dùng chung toàn bộ dashboard layout giữa các role. Mỗi role khác:
- KPI.
- Section order.
- Table columns.
- Quick actions.
- Refresh interval.
- Mobile behavior.
- Data visibility.
3.4. Permission là guard, role là UX composition
Permission quyết định user có được truy cập resource/action không. Role quyết định trải nghiệm dashboard mặc định.
Ví dụ:
financial:read:allquyết định có vào financial report không.- Role
Ownerquyết định dashboard ưu tiên pond/task/feed/staff. - Role
Technicalquyết định chỉ thấy ticket/device liên quan bảo trì.
Không dùng role string thay thế permission guard cho action nhạy cảm.
3.5. Data security nằm ở backend
Frontend chỉ là lớp hiển thị. Các rule sau phải được backend đảm bảo:
- Staff chỉ nhận pond/task/log/incident của chính mình hoặc pond được giao.
- Owner chỉ nhận farm/staff/device/ticket thuộc farm của mình.
- Admin chỉ nhận dữ liệu trong organization.
- Super Admin nhận dữ liệu cấp SaaS, nhưng không cần nhận sâu dữ liệu kinh doanh farm nếu không phục vụ support/debug.
- Technical chỉ nhận ticket được giao, device liên quan ticket, vị trí và contact đủ để xử lý, không nhận chi phí/FCR/sản lượng/nhật ký nuôi chi tiết.
4. Frontend file structure đề xuất
4.1. Dashboard pages
frontend/src/pages/dashboard/
DashboardRoute.tsx
dashboard-role.ts
dashboard.routes.tsx
index.ts
shared/
components/
DashboardPageShell.tsx
DashboardSection.tsx
DashboardTableCard.tsx
EmptyDashboardState.tsx
KpiCard.tsx
KpiGrid.tsx
RealtimeStatusBadge.tsx
hooks/
useDashboardFilters.ts
useDashboardRefresh.ts
utils/
dashboard-formatters.ts
dashboard-query-keys.ts
types.ts
super-admin/
SuperAdminDashboard.tsx
components/
AuditSecurityFeed.tsx
DeviceInventorySummary.tsx
DeviceIssueTable.tsx
OrganizationHealthTable.tsx
SaaSOverview.tsx
SystemHealthPanel.tsx
TechnicalWorkloadTable.tsx
TicketMaintenanceTable.tsx
hooks/
useSuperAdminDashboard.ts
types.ts
admin/
AdminDashboard.tsx
components/
AdminAlertCenter.tsx
DataInterventionQueue.tsx
FarmPerformanceTable.tsx
FarmRanking.tsx
OrganizationDeviceSummary.tsx
OrganizationOverview.tsx
OwnerStaffActivity.tsx
ReportsSnapshot.tsx
hooks/
useAdminDashboard.ts
types.ts
owner/
OwnerDashboard.tsx
components/
EnvironmentSummary.tsx
FarmDeviceStatus.tsx
FarmOverview.tsx
FeedGrowthCharts.tsx
IncidentReports.tsx
PondAttentionList.tsx
PondStatusMap.tsx
StaffPerformance.tsx
TodayTasks.tsx
hooks/
useOwnerDashboard.ts
types.ts
staff/
StaffDashboard.tsx
components/
IncidentReportPanel.tsx
MyPonds.tsx
QuickLogEntry.tsx
TodayChecklist.tsx
UrgentAlerts.tsx
WorkHistory.tsx
hooks/
useStaffDashboard.ts
types.ts
technical/
TechnicalDashboard.tsx
components/
DeviceDiagnostics.tsx
MaintenanceHistory.tsx
MyTicketQueue.tsx
PartsNeeded.tsx
ServiceReportPanel.tsx
TodayRoute.tsx
hooks/
useTechnicalDashboard.ts
types.ts
4.2. Trách nhiệm từng file lõi
DashboardRoute.tsx
- Đọc role từ
useAuthStore. - Normalize role qua
dashboard-role.ts. - Chọn đúng lazy dashboard từ
dashboard.routes.tsx. - Render fallback nếu role không hợp lệ hoặc thiếu permission.
- Không chứa UI block nghiệp vụ.
dashboard-role.ts
- Định nghĩa enum frontend đồng bộ với backend:
SUPER_ADMIN,ADMIN,OWNER,STAFF,TECHNICAL. - Validate role nhận từ JWT/backend; role không thuộc enum phải bị coi là unsupported.
- Không xử lý multi-role priority vì user chỉ có một role active.
dashboard.routes.tsx
- Khai báo
React.lazycho từng dashboard. - Là nơi duy nhất import entry dashboard theo role.
- Giúp code splitting rõ ràng.
shared/components/*
- Chỉ chứa UI primitive có thể dùng cho nhiều dashboard.
- Không import service/hook domain cụ thể.
- Không biết role hiện tại là gì.
role/hooks/use<Role>Dashboard.ts
- Dùng TanStack Query để gọi service tương ứng.
- Định nghĩa query key, stale time, refetch interval.
- Mapping error/loading state cho dashboard entry.
role/types.ts
- Type frontend cho payload đã được backend DTO trả về.
- Chỉ chứa type dùng trong role dashboard đó.
- Shared type dùng nhiều role thì đưa vào
shared/types.ts.
4.3. Services, hooks, types tách khỏi report
Không nên tiếp tục mở rộng frontend/src/services/report.service.ts cho dashboard theo role. Report analytics và dashboard role là hai use case khác nhau.
Đề xuất:
frontend/src/services/dashboard/
admin-dashboard.service.ts
dashboard.service.ts
owner-dashboard.service.ts
staff-dashboard.service.ts
super-admin-dashboard.service.ts
technical-dashboard.service.ts
frontend/src/hooks/dashboard/
useAdminDashboard.ts
useDashboardRole.ts
useOwnerDashboard.ts
useStaffDashboard.ts
useSuperAdminDashboard.ts
useTechnicalDashboard.ts
frontend/src/types/dashboard/
admin-dashboard.types.ts
owner-dashboard.types.ts
shared.types.ts
staff-dashboard.types.ts
super-admin-dashboard.types.ts
technical-dashboard.types.ts
Quy ước:
- Service chỉ gọi API và return typed response.
- Hook quản lý query options, polling, enabled condition.
- Page/component chỉ consume hook và render UI.
- Không đặt API response type trong component file.
5. Routing plan
5.1. Route chính
Route /dashboard nên đổi từ render trực tiếp DashboardOverview sang render DashboardRoute.
/dashboard -> DashboardRoute -> lazy dashboard theo role
Route / hiện đang render FarmDashboard. Quyết định đã chốt: / phải redirect về /dashboard.
/ -> Navigate /dashboard
/dashboard -> DashboardRoute
Lý do:
- Mọi user có entry nhất quán.
- Không mặc định mọi role vào FarmDashboard.
- Technical và Super Admin không bị rơi vào màn farm không phù hợp.
5.2. Lazy loading
Mỗi dashboard role phải lazy-load:
SuperAdminDashboard chunk
AdminDashboard chunk
OwnerDashboard chunk
StaffDashboard chunk
TechnicalDashboard chunk
Lợi ích:
- Staff mobile không tải bảng/biểu đồ Super Admin.
- Technical không tải financial/farm analytics không liên quan.
- Giảm initial bundle cho dashboard.
- Dễ phát triển độc lập từng role.
5.3. Fallback behavior
Fallback cần rõ:
- Chưa auth: ProtectedRoute xử lý.
- Đã auth nhưng chưa có role: hiển thị empty state “Không xác định vai trò”.
- Role không được hỗ trợ: redirect
/403hoặcForbiddenPage. - Nếu JWT/backend trả nhiều role, đây là dữ liệu không hợp lệ cho dashboard; frontend nên chọn fail-closed thay vì tự quyết định priority.
- Role hợp lệ phải là một trong
SUPER_ADMIN,ADMIN,OWNER,STAFF,TECHNICAL.
6. Navigation/menu plan
6.1. Tách menu khỏi SidebarMenu
SidebarMenu.tsx hiện vừa chứa config menu vừa render menu. Khi thêm 5 role, file này sẽ phình lớn.
Đề xuất:
frontend/src/navigation/
nav-resolver.ts
nav.types.ts
permission-visibility.ts
menus/
admin.menu.tsx
owner.menu.tsx
staff.menu.tsx
super-admin.menu.tsx
technical.menu.tsx
SidebarMenu.tsx chỉ nên:
- Lấy role/permissions từ auth store.
- Gọi
resolveMenuForRole(role, permissions). - Render Ant Design
Menu. - Xử lý selected/open keys.
6.2. Menu theo role
Super Admin:
- Dashboard.
- Organizations.
- Admin Accounts.
- Device Inventory.
- Device Assignment.
- Ticket & Maintenance.
- Technical Team.
- Subscription & Billing.
- System Health.
- Audit Logs.
- System Settings.
Admin:
- Dashboard.
- Farms.
- Owners & Staff.
- Ponds.
- Crops / Vụ nuôi.
- Device Management.
- Device Assignment.
- Alerts.
- Logs & Data Correction.
- Reports & Analytics.
- Cost & Profit.
- Organization Settings.
Owner:
- Dashboard.
- Ao nuôi.
- Staff.
- Phân công công việc.
- Nhật ký vận hành.
- Cảnh báo.
- Thiết bị Farm.
- Vụ nuôi.
- Kho thức ăn.
- Chi phí & lợi nhuận.
- Báo cáo.
Staff:
- Dashboard.
- Ao của tôi.
- Việc hôm nay.
- Nhập nhật ký.
- Cảnh báo.
- Báo cáo sự cố.
- Lịch sử công việc.
Technical:
- Dashboard.
- My Tickets.
- Devices.
- Maintenance Schedule.
- Map.
- Parts / Inventory.
- Service Reports.
- Profile.
6.3. Visibility rule
Menu visibility cần qua hai lớp:
- Role menu: role quyết định nhóm menu nền.
- Permission filter: permission quyết định item cụ thể có hiển thị không.
Không hardcode logic phức tạp trực tiếp trong JSX.
7. Backend architecture plan
7.1. Module dashboard riêng
Dashboard theo role nên có module riêng, thay vì mở rộng vô hạn reports.
Đề xuất:
backend/src/dashboard/
dashboard.module.ts
dashboard.controller.ts
dashboard-query.service.ts
dashboard-cache.service.ts
dto/
admin-dashboard-response.dto.ts
owner-dashboard-response.dto.ts
shared-dashboard.dto.ts
staff-dashboard-response.dto.ts
super-admin-dashboard-response.dto.ts
technical-dashboard-response.dto.ts
services/
admin-dashboard.service.ts
owner-dashboard.service.ts
staff-dashboard.service.ts
super-admin-dashboard.service.ts
technical-dashboard.service.ts
presenters/
admin-dashboard.presenter.ts
owner-dashboard.presenter.ts
staff-dashboard.presenter.ts
super-admin-dashboard.presenter.ts
technical-dashboard.presenter.ts
7.2. Controller contract
Controller nên expose endpoint theo role:
GET /dashboard/super-admin
GET /dashboard/admin
GET /dashboard/owner
GET /dashboard/staff
GET /dashboard/technical
Mỗi endpoint cần:
@UseGuards(JwtAuthGuard, PermissionsGuard).@RequirePermission(...)phù hợp.- Lấy user hiện tại từ
@CurrentUser. - Không nhận
organizationIdtùy tiện từ query nếu user không có quyền cross-tenant. - Gọi service đúng role.
7.3. Service responsibility
super-admin-dashboard.service.ts
- Tổng hợp SaaS KPI.
- Organization health.
- Device inventory toàn hệ thống.
- Device issues.
- Ticket & maintenance toàn hệ thống.
- Technical workload.
- System health.
- Audit/security feed.
admin-dashboard.service.ts
- Tổng hợp organization KPI.
- Farm performance.
- Farm ranking.
- Alert center.
- Device trong organization.
- Data intervention queue.
- Owner/staff activity.
- Reports snapshot.
owner-dashboard.service.ts
- Farm overview.
- Pond status.
- Today tasks.
- Staff performance trong farm.
- Pond attention list.
- Environment summary.
- Feed/growth/FCR summary.
- Device trong farm.
- Incident reports.
staff-dashboard.service.ts
- Today checklist của staff.
- My ponds.
- Quick log options.
- Urgent alerts liên quan pond được giao.
- Incident report state.
- Work history.
technical-dashboard.service.ts
- My ticket queue.
- Today route.
- Device diagnostics cho ticket được giao.
- Parts needed.
- Maintenance history của device liên quan.
- Service report draft/status.
7.4. Query service và presenter
dashboard-query.service.ts nên chứa query helper dùng lại:
- Count active ponds.
- Count online/offline devices.
- Load active alerts.
- Load task completion.
- Load ticket SLA.
- Load organization/farm summary.
Presenter chịu trách nhiệm map entity/raw query thành DTO:
- Format status.
- Tính severity.
- Tính SLA label.
- Giới hạn field trả về theo role.
- Không để component frontend phải suy luận quá nhiều.
7.5. Cache strategy
Không phải mọi block dashboard cần refresh cùng tần suất.
Gợi ý cache:
| Dữ liệu | TTL gợi ý | Ghi chú |
|---|---|---|
| SaaS KPI tổng | 60-120 giây | Không cần realtime tuyệt đối |
| Organization health | 60 giây | Phụ thuộc alert/device/ticket |
| Device online/offline | 15-30 giây | Có thể polling hoặc WebSocket |
| Ticket queue | 15-30 giây | Cần gần realtime |
| Staff today tasks | 30-60 giây | Có mutation invalidate |
| System health | 10-30 giây | Có thể dùng health endpoint riêng |
| Reports snapshot | 5-15 phút | Có thể cache lâu hơn |
| Audit feed | 30-60 giây | Hoặc cursor pagination |
Nếu số đo hiệu năng cho thấy cần Redis, key cần bao gồm role/scope:
dashboard:super-admin
dashboard:admin:{organizationId}
dashboard:owner:{farmId}
dashboard:staff:{userId}:{date}
dashboard:technical:{userId}:{date}
Redis không được đưa vào phase đầu theo mặc định. Phase đầu chỉ dùng TanStack Query staleTime, backend aggregate query, và đo thời gian response. Chỉ thêm dashboard-cache.service.ts/Redis khi số đo cho thấy endpoint chậm, request volume cao, hoặc block dữ liệu có chi phí query lớn.
8. Data contract đề xuất
8.1. Shared DTO pattern
Các dashboard nên dùng structure nhất quán:
type DashboardResponse = {
generatedAt: string;
scope: DashboardScope;
kpis: DashboardKpi[];
sections: RoleSpecificSections;
};
Tuy nhiên không nên ép mọi role dùng chung một mega type. Nên có shared primitive và role-specific sections.
Shared primitive:
type DashboardKpi = {
key: string;
label: string;
value: number | string;
unit?: string;
status?: 'good' | 'warning' | 'critical' | 'neutral';
trend?: {
direction: 'up' | 'down' | 'flat';
value: number;
label: string;
};
};
Role-specific response:
type OwnerDashboardResponse = {
generatedAt: string;
farmId: string;
farmName: string;
kpis: DashboardKpi[];
pondStatus: PondStatusSummary[];
todayTasks: TodayTask[];
staffPerformance: StaffPerformanceRow[];
pondAttention: PondAttentionRow[];
environmentSummary: EnvironmentMetric[];
feedGrowthCharts: FeedGrowthChartData;
farmDevices: FarmDeviceRow[];
incidents: IncidentRow[];
};
8.2. Không trả dữ liệu dư
Ví dụ Technical dashboard không được nhận:
farmProfit.fcr.survivalRate.feedCost.staffPerformance.dailyLogDetails.
Technical chỉ cần:
- Ticket.
- Device.
- Issue.
- Location.
- Contact.
- Device logs kỹ thuật.
- Maintenance history.
- Parts.
- Service report state.
9. Backend implementation plan chi tiết
Phần này là plan để sửa backend cho phù hợp với kiến trúc dashboard theo role.
9.1. Hiện trạng backend dashboard
Backend hiện có backend/src/reports/dashboard.controller.ts với các endpoint:
GET /dashboard/overview
GET /dashboard/farm-stats
GET /dashboard/device-status
Service hiện tại nằm ở backend/src/reports/dashboard.service.ts và đang aggregate dữ liệu farm/report chung theo report:read. Cách này phù hợp cho dashboard/report tổng quan ban đầu, nhưng chưa đủ cho dashboard theo role vì:
- Chưa có endpoint riêng cho Super Admin/Admin/Owner/Staff/Technical.
- Chưa có DTO response tách theo data visibility từng role.
- Chưa tách service theo boundary nghiệp vụ.
getDeviceStatus()đang load toàn bộ devices rồi count ở memory; khi device lớn nên chuyển sang aggregate query.- Technical dashboard cần scope theo ticket được giao, không theo
organizationId. - Staff dashboard cần scope theo pond/task được giao, không theo toàn organization.
Không nên xóa ngay controller/service cũ. Giai đoạn chuyển đổi nên giữ backward compatibility cho các màn report hiện tại, sau đó migrate frontend dashboard mới sang module mới.
9.2. Backend target structure
Tạo module mới:
backend/src/dashboard/
dashboard.module.ts
dashboard.controller.ts
dashboard-permission.service.ts
dashboard-query.service.ts
dashboard-cache.service.ts
dto/
dashboard-kpi.dto.ts
dashboard-scope.dto.ts
super-admin-dashboard-response.dto.ts
admin-dashboard-response.dto.ts
owner-dashboard-response.dto.ts
staff-dashboard-response.dto.ts
technical-dashboard-response.dto.ts
services/
super-admin-dashboard.service.ts
admin-dashboard.service.ts
owner-dashboard.service.ts
staff-dashboard.service.ts
technical-dashboard.service.ts
presenters/
super-admin-dashboard.presenter.ts
admin-dashboard.presenter.ts
owner-dashboard.presenter.ts
staff-dashboard.presenter.ts
technical-dashboard.presenter.ts
dashboard.controller.spec.ts
services/
super-admin-dashboard.service.spec.ts
admin-dashboard.service.spec.ts
owner-dashboard.service.spec.ts
staff-dashboard.service.spec.ts
technical-dashboard.service.spec.ts
Lý do tách khỏi reports:
- Dashboard là operational command center, không chỉ là report analytics.
- Ticket, technical workload, device lifecycle, staff checklist không nên sống trong reports module.
- Có thể giữ
reportscho report xuất file, chart phân tích dài hạn, comparison, FCR, biomass.
9.3. App module integration
Khi implement, cần:
- Import
DashboardModulevàobackend/src/app.module.ts. DashboardModuleimport các module hoặc repository cần thiết:TypeOrmModule.forFeature([...])cho entities cần query.RbacModulenếu cầnAuthorizationPolicyService.HealthModulenếu Super Admin dashboard dùng system health.
- Không import
RedisModuleở phase đầu; chỉ thêm sau khi có số đo hiệu năng chứng minh cần cache backend. - Không tạo circular dependency với
reports.
Entities dự kiến cần đăng ký trong DashboardModule:
Organization
User
Area
Pond
FarmingCycle
DailyLog
GrowthRecord
Device
DeviceAlert
Notification
InventoryItem hoặc InventoryTransaction nếu dashboard cần feed/parts
Sau khi có Ticket & Maintenance module, bổ sung:
MaintenanceTicket
TicketComment hoặc TicketEvent
ServiceReport
MaintenancePartUsage
9.4. Resource/permission plan
backend/src/common/enums/resource.enum.ts hiện chưa có dashboard, ticket, audit, system-health.
Khuyến nghị thêm resource:
export enum Resource {
// existing resources...
DASHBOARD = 'dashboard',
TICKET = 'ticket',
MAINTENANCE = 'maintenance',
AUDIT = 'audit',
SYSTEM_HEALTH = 'system-health',
}
Permission gợi ý:
dashboard:read:system # Super Admin dashboard
dashboard:read:organization # Admin dashboard
dashboard:read:own # Owner/Staff/Technical dashboard tùy scope
ticket:read:system
ticket:read:organization
ticket:read:own
ticket:update:own
maintenance:read:system
maintenance:update:own
audit:read:system
audit:read:organization
system-health:read:system
Mapping role gợi ý:
| Role | Dashboard permission |
|---|---|
SUPER_ADMIN |
dashboard:read:system |
ADMIN |
dashboard:read:organization |
OWNER |
dashboard:read:own |
STAFF |
dashboard:read:own |
TECHNICAL |
dashboard:read:own |
Vì Owner, Staff, Technical cùng có dashboard:read:own, controller không được chỉ dựa vào permission này. Cần kiểm tra role/scope cụ thể cho endpoint tương ứng.
Backend role claim phải chuẩn hóa enum. JWT không nên trả role dạng display label như Super Admin; display label chỉ nên xử lý ở frontend i18n/menu.
9.5. Controller endpoint contract
dashboard.controller.ts nên expose:
GET /dashboard/super-admin
GET /dashboard/admin
GET /dashboard/owner
GET /dashboard/staff
GET /dashboard/technical
Contract chi tiết:
| Endpoint | Guard | Permission | Role check | Scope |
|---|---|---|---|---|
/dashboard/super-admin |
JWT + permission | dashboard:read:system |
SUPER_ADMIN |
System |
/dashboard/admin |
JWT + permission | dashboard:read:organization |
ADMIN |
currentUser.organizationId |
/dashboard/owner |
JWT + permission | dashboard:read:own |
OWNER |
một farm owner quản lý |
/dashboard/staff |
JWT + permission | dashboard:read:own |
STAFF |
pond/task được giao |
/dashboard/technical |
JWT + permission | dashboard:read:own |
TECHNICAL |
ticket được giao hoặc internal ticket do technical tạo |
Controller không nên nhận organizationId, farmId, staffId, technicalId tự do từ query trong dashboard mặc định. Nếu cần filter:
- Admin có thể filter
farmId, nhưng backend phải verify farm thuộc organization. - Owner có thể filter
pondId, nhưng backend phải verify pond thuộc farm của owner. - Staff có thể filter
pondId, nhưng backend phải verify pond được assign. - Technical có thể filter
ticketId, nhưng backend phải verify ticket assigned to current technical.
9.6. Dashboard permission service
Tạo dashboard-permission.service.ts để gom các kiểm tra role/scope, tránh lặp trong 5 service.
Trách nhiệm:
assertRole(currentUser, allowedRoles).assertOrganizationScope(currentUser).getOrganizationIdOrThrow(currentUser).getOwnerFarmId(currentUser).getStaffAssignedPondIds(currentUser).getTechnicalAssignedTicketIds(currentUser).assertFarmInOrganization(farmId, organizationId).assertPondAssignedToStaff(pondId, userId).assertTicketAssignedToTechnical(ticketId, userId).assertInternalTicketCreatedByTechnical(ticketId, userId).
Không nên để frontend truyền scope quyết định. Scope phải được derive từ currentUser và database.
9.7. Query service plan
dashboard-query.service.ts chứa các query helper dùng lại, không chứa role orchestration.
Nhóm query nên có:
Organization/system:
countActiveOrganizations().countTotalFarmsSystem().countTotalPondsSystem().countTotalUsersSystem().countUsersByRole(role).getOrganizationHealthRows(limit, offset).
Farm/pond:
countFarmsByOrganization(organizationId).countPondsByOrganization(organizationId).countActivePondsByFarmId(farmId).getFarmPerformanceRows(organizationId, filters).getPondAttentionRows(scope).
Aquaculture/log:
countActiveCycles(scope).sumTodayFeed(scope, date).getDataCompletion(scope, date).getFcrSummary(scope).getGrowthTrend(scope, range).
Device:
countDevicesByStatus(scope).countDevicesByLifecycle(scope).getOfflineDeviceRows(scope, limit).getDeviceIssueRows(scope, limit).getDeviceDiagnosticsForTickets(ticketIds).
Alert:
countActiveAlerts(scope).getAlertCenterRows(scope, limit).getCriticalAlerts(scope, limit).
Task/staff:
getTodayTasksForOwner(farmId, date).getTodayTasksForStaff(userId, date).getStaffPerformanceRows(farmId, date).getWorkHistory(userId, range).
Ticket/maintenance:
countTicketsByStatus(scope).countOverdueTickets(scope).getTicketQueue(scope, limit).getInternalTicketQueueForTechnical(userId, limit).getTechnicalWorkloadRows().getTodayRouteForTechnical(userId, date).getMaintenanceHistory(deviceIds, limit).getPartsNeeded(ticketIds).
9.8. Query efficiency requirements
Các query count/status không nên load entity list rồi filter bằng JavaScript nếu dữ liệu có thể lớn.
Nên dùng:
repository.count({ where })cho count đơn giản.- QueryBuilder
COUNT(*) FILTER (...)hoặcSUM(CASE WHEN ...)cho nhiều status. GROUP BYcho status/lifecycle/type.- Pagination cho table rows.
- Select field cụ thể thay vì
find()full entity nếu chỉ cần row summary.
Ví dụ device status nên đi theo hướng:
SELECT
COUNT(*) AS total,
SUM(CASE WHEN status = 'online' THEN 1 ELSE 0 END) AS online,
SUM(CASE WHEN status = 'offline' THEN 1 ELSE 0 END) AS offline,
SUM(CASE WHEN status = 'error' THEN 1 ELSE 0 END) AS error
FROM devices
WHERE organization_id = $1;
Trong TypeORM có thể dùng QueryBuilder, không cần raw SQL nếu query vẫn rõ.
9.9. Service orchestration theo role
Super Admin service:
validate Super Admin
load SaaS overview
load organization health
load device lifecycle counts
load device issue rows
load ticket SLA summary
load technical workload
load system health snapshot
load audit/security feed
present response
Admin service:
validate Admin
derive organizationId
load organization overview
load farm performance rows
load farm ranking
load alert center
load organization device summary
load data intervention queue
load owner/staff activity
load reports snapshot
present response
Owner service:
validate Owner
derive single farmId owned by current user
load farm overview
load pond status
load today tasks
load staff performance
load pond attention list
load environment summary
load feed/growth charts
load farm device status
load incident reports
present response
Staff service:
validate Staff
derive assigned pondIds
load today checklist
load my ponds
load quick log entry options
load urgent alerts
load incident report state
load work history
present response
Technical service:
validate Technical
derive assigned ticketIds and internal ticketIds created by current technical
load assigned ticket queue
load internal ticket queue
load today route
load device diagnostics for ticket devices
load parts needed
load maintenance history
load service report state
present response
9.10. DTO response plan
DTO class phải dùng explicit type, không dùng any.
Shared DTO:
DashboardKpiDto
DashboardTrendDto
DashboardScopeDto
DashboardStatusDto
DashboardTablePaginationDto
Role DTO:
SuperAdminDashboardResponseDto
AdminDashboardResponseDto
OwnerDashboardResponseDto
StaffDashboardResponseDto
TechnicalDashboardResponseDto
Mỗi DTO nên có:
generatedAt.scope.kpis.- Section data.
- Optional
warningsnếu có partial data.
Không nên tạo mega DTO chứa toàn bộ field của 5 role.
9.11. Ticket & Maintenance dependency plan
Technical dashboard cần Ticket & Maintenance module. Nếu module này chưa có, triển khai theo 2 bước:
Step 1: Dashboard foundation không phụ thuộc ticket entity thật.
- Tạo Technical dashboard endpoint trả empty state có typed response.
- DTO đã định nghĩa đúng shape.
- Frontend Technical dashboard render empty state.
Step 2: Khi Ticket module có entity/service.
- Thêm
MaintenanceTicketentity. - Thêm ticket repository vào
DashboardModule. - Implement
technical-dashboard.service.ts. - Implement Super Admin ticket/technical workload section.
- Implement Admin/Owner ticket status section nếu cần.
- Implement luồng Technical tạo internal ticket để ghi nhận sự cố kỹ thuật nội bộ, bảo trì chủ động, kiểm tra lại, hoặc nhu cầu linh kiện không xuất phát từ khách hàng.
Không nên fake ticket bằng device alert lâu dài vì lifecycle ticket có SLA, assignee, verification, parts, service report khác alert.
Technical được tạo internal ticket, nhưng không được tự tạo customer ticket thay mặt Organization/Farm nếu không có luồng triage rõ. Internal ticket cần có loại riêng, ví dụ INTERNAL_MAINTENANCE, và không được mặc định hiển thị cho Owner/Admin như ticket khách hàng.
9.12. Migration và seed plan
Foundation dashboard không nhất thiết cần migration nếu chỉ aggregate dữ liệu hiện có.
Cần migration khi thêm:
- Ticket & Maintenance tables.
- Audit log tables nếu chưa có.
- Task assignment tables nếu chưa có.
- Device lifecycle fields nếu entity hiện tại chưa đủ.
- Staff/Pond assignment nếu chưa đủ quan hệ.
Seed nên bổ sung:
- Demo Super Admin có
dashboard:read:system. - Demo Admin có
dashboard:read:organization. - Demo Owner/Staff/Technical có
dashboard:read:own. - Demo device online/offline.
- Demo alerts.
- Demo ticket nếu Ticket module đã có.
9.13. Backend rollout tasks
Task 1: Thêm resource và permission seed.
- Add
DASHBOARD,TICKET,MAINTENANCE,AUDIT,SYSTEM_HEALTHvàoResource. - Update role permission seed/migration.
- Test decode JWT vẫn trả permissions đúng.
Task 2: Tạo DashboardModule skeleton.
- Create module/controller/service placeholders.
- Import vào
AppModule. - Add endpoint typed empty response cho 5 role.
- Add controller tests cho auth/role rejection cơ bản.
Task 3: Implement permission/scope service.
- Derive organization scope.
- Derive owner single-farm scope.
- Derive staff pond scope.
- Derive technical ticket scope sau khi có ticket module.
- Derive technical internal ticket scope theo
createdByTechnicalId. - Test không cho cross-scope.
Task 4: Implement Admin/Owner/Staff trước.
- Reuse existing farm/aquaculture/device/alert data.
- Chưa phụ thuộc Ticket module.
- Optimize count/group queries.
- Add DTO tests/service tests.
Task 5: Implement Super Admin.
- Organization health.
- Device inventory lifecycle.
- System-level counts.
- Audit/system health section có thể trả partial nếu module chưa đủ.
Task 6: Implement Ticket & Maintenance dependency.
- Add Ticket module nếu chưa có.
- Add Technical dashboard.
- Add ticket summary vào Super Admin/Admin/Owner.
Task 7: Deprecate old dashboard endpoints.
- Frontend mới chuyển sang
/dashboard/{role}. - Giữ
/dashboard/overview,/dashboard/farm-stats,/dashboard/device-statuscho report legacy nếu còn dùng. - Sau khi không còn consumer, đánh dấu deprecated hoặc migrate sang reports.
9.14. Backend test checklist
Bắt buộc test:
- Super Admin gọi
/dashboard/super-adminthành công. - Admin gọi
/dashboard/super-adminbị forbidden. - Admin gọi
/dashboard/adminchỉ thấy organization của mình. - Owner gọi
/dashboard/ownerchỉ thấy farm mình quản lý. - Staff gọi
/dashboard/staffchỉ thấy pond/task được giao. - Technical gọi
/dashboard/technicalchỉ thấy ticket/device được giao và internal ticket do chính technical tạo. - Technical response không chứa FCR, cost, revenue, staff performance, daily log detail.
- Staff response không chứa cost/revenue/admin report.
- Query service count device status không load full list khi chỉ cần count.
- Empty scope trả empty response hợp lệ, không throw 500.
9.15. Backend acceptance criteria
Backend phần dashboard đạt yêu cầu khi:
- Có 5 endpoint role-specific.
- Có DTO riêng cho từng role.
- Có service riêng cho từng role.
- Có permission/scope guard rõ.
- Không endpoint nào trả dữ liệu ngoài phạm vi role.
- Query dashboard chính dùng aggregate query hợp lý.
- Tests cover role access và data visibility.
bun run checkpass từ root.
10. Performance plan
9.1. Frontend performance
Áp dụng:
- Lazy-load từng dashboard role.
- Lazy-load chart nặng nếu chart nằm dưới fold.
- Dùng TanStack Query query key ổn định theo role/scope/date/filter.
- Dùng
staleTimetheo loại dữ liệu. - Dùng pagination/cursor cho bảng lớn như audit/ticket/device.
- Dùng virtual table nếu bảng Super Admin có nhiều organization/device.
- Staff dashboard mobile-first, ít chart, ưu tiên checklist và quick actions.
Không nên:
- Fetch toàn bộ device rồi count ở frontend.
- Fetch toàn bộ farm/pond rồi tự tính ranking nếu backend có thể aggregate.
- Dùng một query lớn cho mọi role.
- Để mọi section polling cùng interval.
9.2. Backend performance
Áp dụng:
- Aggregate bằng SQL count/group by ở backend.
- Index các field dùng nhiều trong dashboard:
organization_id,farm_id,pond_id,status,created_at,updated_at,last_signal_at,assigned_technical_id. - Cache block ít thay đổi.
- Không join sâu không cần thiết.
- Với telemetry/time-series, query theo window ngắn và aggregate sẵn.
- Với reports snapshot, dùng precomputed summary nếu cần.
9.3. Realtime strategy
Không cần realtime toàn bộ dashboard ngay phase đầu.
Ưu tiên realtime/polling ngắn cho:
- Device online/offline.
- Critical alerts.
- Ticket SLA/status.
- Staff task status hôm nay.
Các block khác có thể refresh thủ công hoặc stale time dài hơn.
11. Security và access plan
11.1. Role access matrix cho dashboard
| Endpoint | Role chính | Scope dữ liệu |
|---|---|---|
/dashboard/super-admin |
SUPER_ADMIN |
Toàn hệ thống SaaS |
/dashboard/admin |
ADMIN |
Organization hiện tại |
/dashboard/owner |
OWNER |
Một farm được quản lý |
/dashboard/staff |
STAFF |
Pond/task được giao |
/dashboard/technical |
TECHNICAL |
Ticket/device được giao và internal ticket do technical tạo |
11.2. Audit bắt buộc
Dashboard không chỉ hiển thị dữ liệu; nhiều quick action có thể kích hoạt thay đổi. Những action sau phải có audit khi triển khai mutation:
- Assign device cho Organization/Farm/Pond.
- Correct data hoặc mark data reviewed.
- Tạo ticket từ alert/device issue.
- Technical tạo internal ticket.
- Assign technical.
- Technical cập nhật ticket.
- Owner/Admin verify ticket.
- Thay đổi role/scope user.
11.3. Technical isolation
Technical là role đặc biệt:
- Không thuộc tenant.
- Không xem business dashboard.
- Không xem chi phí/lợi nhuận/FCR/sản lượng.
- Không xem staff performance.
- Không xem daily log chi tiết.
- Chỉ xem Organization/Farm/Location ở mức đủ để xử lý ticket.
Do đó Technical dashboard phải là module riêng cả frontend và backend, không tái dùng Admin/Owner data response.
12. Phase triển khai đề xuất
Phase 0: Foundation
Mục tiêu: chuẩn bị route/menu/service boundary trước khi build UI thật.
Tasks:
- Tạo
DashboardRoute. - Tạo lazy dashboard placeholders cho 5 role.
- Tách dashboard services/hooks/types khỏi report service.
- Tách navigation config khỏi
SidebarMenu. - Redirect
/về/dashboard. - Thêm fallback cho unknown role.
Kết quả:
- Role nào cũng vào đúng dashboard shell.
- Chưa cần đủ KPI/table thật.
- Bundle đã có boundary rõ.
Phase 1: Core Farm Operation
Mục tiêu: Owner và Staff vận hành farm thật.
Ưu tiên:
- Owner dashboard.
- Staff dashboard.
- Pond/task/log/alert cơ bản.
- Feed/FCR/growth summary.
- Staff performance cơ bản.
Lý do:
- Đây là nhóm người dùng vận hành hằng ngày.
- Staff cần mobile-first và thao tác nhanh.
- Owner cần nắm farm, pond, staff, task, device, incident.
Phase 2: Organization Management
Mục tiêu: Admin quản lý nhiều farm trong một organization.
Ưu tiên:
- Admin dashboard.
- Farm performance table.
- Farm ranking.
- Alert center cấp organization.
- Data completion tracking.
- Data correction queue.
- Organization device summary.
- Owner/staff activity.
Lý do:
- Admin không phải Owner mở rộng; Admin là cấp organization.
- Dashboard phải giúp phát hiện farm yếu, dữ liệu thiếu, device lỗi, staff/owner chưa hoàn thành việc.
Phase 3: SaaS Platform Management
Mục tiêu: Super Admin vận hành SaaS multi-tenant.
Ưu tiên:
- Super Admin dashboard.
- Organization health.
- Device inventory lifecycle.
- Device issues.
- Ticket & maintenance overview.
- Technical workload.
- System health.
- Audit/security feed.
Lý do:
- Super Admin cần control center toàn hệ thống.
- Không nên dùng Farm dashboard hoặc Admin dashboard cho Super Admin.
Phase 4: IoT & Maintenance
Mục tiêu: hoàn thiện luồng thiết bị thật và đội Technical.
Ưu tiên:
- Ticket & Maintenance module.
- Technical dashboard.
- Device diagnostics.
- SLA.
- Maintenance history.
- Parts/inventory.
- Service report.
Lý do:
- Technical dashboard phụ thuộc ticket lifecycle.
- Nếu chưa có ticket module, Technical dashboard chỉ là UI rỗng.
13. Testing plan
13.1. Frontend tests
Nên có test cho:
DashboardRoutemap role đúng dashboard.- Unknown role hiển thị forbidden/empty state.
- JWT/backend trả nhiều role thì dashboard fail-closed hoặc hiển thị invalid role state.
- Role enum
SUPER_ADMIN,ADMIN,OWNER,STAFF,TECHNICALmap đúng dashboard. - Navigation resolver trả đúng menu theo role/permission.
- Staff dashboard không render section nhạy cảm.
- Technical dashboard không render business metric.
Test dạng unit/component là đủ cho foundation.
13.2. Backend tests
Nên có test cho:
- Mỗi endpoint reject role không đúng.
- Role claim dùng enum chuẩn, không dùng display label.
- Admin endpoint chỉ trả organization hiện tại.
- Owner endpoint chỉ trả đúng một farm được quản lý.
- Staff endpoint chỉ trả pond/task được giao.
- Technical endpoint chỉ trả ticket/device được giao và internal ticket do chính technical tạo.
- Super Admin endpoint không bị filter sai bởi organization scope.
- DTO không chứa field nhạy cảm ở Technical/Staff response.
13.3. Performance verification
Nên đo:
- Dashboard initial load theo role.
- Số request khi vào
/dashboard. - Bundle chunk cho từng role.
- Query duration cho endpoint aggregate.
- Polling interval không gây request storm.
14. Coding convention áp dụng
Theo AGENTS.md của repo:
- Dùng Bun, không dùng
npm/yarn. - Dùng Biome, không chạy ESLint/Prettier trực tiếp.
- Không dùng
any; dùngunknownhoặc type rõ. - Frontend import bằng alias
@/, không dùng../. - Sau mỗi edit chạy
bun run checktừ root.
Quy ước bổ sung cho dashboard:
- Component role dashboard chỉ import từ cùng role folder hoặc
shared. - Không import chéo
adminsangowner,ownersangstaff, hoặc role khác. - Shared component không biết role hiện tại.
- Hook/service role không nằm trong component file.
- Type API response không đặt inline trong JSX.
- Không để frontend tự filter dữ liệu nhạy cảm từ response dư.
15. Quyết định kiến trúc đã chốt
Các quyết định sau là baseline khi implement:
- Backend role chuẩn hóa enum:
SUPER_ADMIN,ADMIN,OWNER,STAFF,TECHNICAL. - User không thể có nhiều role active trong dashboard.
- Owner quản lý đúng một farm.
- Technical được tạo ticket nội bộ.
- Dashboard endpoint chỉ thêm Redis sau khi có số đo hiệu năng.
- Route
/redirect về/dashboard.
Hệ quả triển khai:
- JWT/backend phải trả một role enum duy nhất cho dashboard. Nếu payload trả nhiều role, frontend/backend nên fail-closed thay vì tự chọn priority.
dashboard-role.tschỉ validate/normalize enum chuẩn, không cần logic priority hoặcactiveRole.- Owner dashboard/service dùng
farmId, không dùngfarmIds. - Technical dashboard/service có hai nhóm ticket: ticket được giao và internal ticket do technical tạo.
- Customer ticket vẫn cần luồng triage/assignment riêng; internal ticket của Technical không mặc định thay mặt khách hàng.
- Redis không nằm trong foundation phase; phase đầu cần đo query duration, request count và load thực tế trước.
/không còn render legacyFarmDashboard; mọi role vào/dashboardrồi được route tới dashboard tương ứng.
16. Definition of done
Một phase dashboard được xem là hoàn thành khi:
- Route
/dashboardđưa đúng role vào đúng dashboard. - Menu sidebar hiển thị đúng theo role và permission.
- Endpoint backend enforce scope đúng role.
- Response không chứa dữ liệu ngoài phạm vi role.
- Loading/error/empty states có xử lý.
- Các bảng lớn có pagination hoặc giới hạn rõ.
- Query key và stale/refetch interval được định nghĩa rõ.
- Unit/backend tests cover role routing và data visibility chính.
bun run checkpass từ root.
17. Tóm tắt quyết định
Triển khai dashboard nên đi theo hướng:
- Frontend: role-specific page modules, lazy-load, shared primitive components.
- Backend: dashboard module riêng, aggregate endpoint theo role, DTO theo scope.
- Navigation: menu config tách theo role,
SidebarMenuchỉ render. - Security: backend quyết định data visibility, frontend không nhận dữ liệu dư.
- Performance: aggregate API, cache/polling theo block, không fetch toàn bộ rồi tính ở frontend.
- Rollout: Foundation -> Owner/Staff -> Admin -> Super Admin -> Technical/Ticket.
Đây là hướng phù hợp nhất với yêu cầu SaaS multi-tenant, RBAC theo resource:action:scope, và đặc thù Technical không thuộc tenant nhưng cần xử lý thiết bị thật.