250 lines
5.6 KiB
Markdown
250 lines
5.6 KiB
Markdown
|
|
# System Admin Priority Logic - Hướng dẫn
|
||
|
|
|
||
|
|
## Tổng quan
|
||
|
|
|
||
|
|
Đã cập nhật logic để **System Admin** (Priority = 0) trở thành quyền cao nhất trong hệ thống.
|
||
|
|
|
||
|
|
### Quy tắc Priority
|
||
|
|
|
||
|
|
```
|
||
|
|
Priority càng thấp = Quyền càng cao
|
||
|
|
Priority = 0 (System Admin) = Quyền cao nhất
|
||
|
|
```
|
||
|
|
|
||
|
|
## Các thay đổi đã thực hiện
|
||
|
|
|
||
|
|
### 1. Constants mới (`src/config/constants.ts`)
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
export const SYSTEM_ADMIN_PRIORITY = 0;
|
||
|
|
|
||
|
|
export const RolePriority = {
|
||
|
|
SYSTEM_ADMIN: 0,
|
||
|
|
} as const;
|
||
|
|
```
|
||
|
|
|
||
|
|
**Mục đích**: Định nghĩa giá trị priority của System Admin, tránh hardcode số 0 trong code.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 2. Helper Functions (`src/helpers/roleHelpers.ts`)
|
||
|
|
|
||
|
|
#### `isSystemAdminPriority(priority: number): boolean`
|
||
|
|
Kiểm tra xem priority có phải là System Admin không.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { isSystemAdminPriority } from '@/helpers/roleHelpers';
|
||
|
|
|
||
|
|
if (isSystemAdminPriority(userPriority)) {
|
||
|
|
// User là System Admin
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `hasHigherOrEqualPriority(priority1, priority2): boolean`
|
||
|
|
So sánh 2 priority (nhỏ hơn = cao hơn).
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
if (hasHigherOrEqualPriority(userPriority, requiredPriority)) {
|
||
|
|
// User có đủ quyền
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
#### `getPriorityLabel(priority: number): string`
|
||
|
|
Lấy nhãn mô tả cho priority.
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
getPriorityLabel(0) // "System Admin (Highest)"
|
||
|
|
getPriorityLabel(5) // "Priority 5"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 3. useAuth Hook (`src/hooks/useAuth.tsx`)
|
||
|
|
|
||
|
|
Thêm method mới: `isSystemAdmin()`
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
const { isSystemAdmin } = useAuth();
|
||
|
|
|
||
|
|
if (isSystemAdmin()) {
|
||
|
|
// User là System Admin (priority = 0)
|
||
|
|
console.log('You have highest permission!');
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Interface cập nhật:**
|
||
|
|
```typescript
|
||
|
|
export interface IAuthContext {
|
||
|
|
// ... các field cũ
|
||
|
|
isSystemAdmin: () => boolean; // ← Mới
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### 4. Sidebar Logic (`src/components/sidebars/app-sidebar.tsx`)
|
||
|
|
|
||
|
|
Cập nhật logic kiểm tra admin:
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
// TRƯỚC
|
||
|
|
const isAdmin = acs.includes(PermissionEnum.ALLOW_ALL);
|
||
|
|
|
||
|
|
// SAU
|
||
|
|
const isAdmin = acs.includes(PermissionEnum.ALLOW_ALL) || isSystemAdmin();
|
||
|
|
```
|
||
|
|
|
||
|
|
**Lợi ích:**
|
||
|
|
- System Admin (Priority = 0) thấy tất cả menu items
|
||
|
|
- Không cần phải có permission `ALLOW_ALL` trong database
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Cách sử dụng
|
||
|
|
|
||
|
|
### Kiểm tra System Admin trong component
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { useAuth } from '@/hooks/useAuth';
|
||
|
|
|
||
|
|
function MyComponent() {
|
||
|
|
const { isSystemAdmin, role } = useAuth();
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div>
|
||
|
|
{isSystemAdmin() && (
|
||
|
|
<AdminOnlyFeature />
|
||
|
|
)}
|
||
|
|
|
||
|
|
<p>Role: {role.roleName}</p>
|
||
|
|
<p>Priority: {role.priority}</p>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Kiểm tra priority trong logic nghiệp vụ
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { isSystemAdminPriority, hasHigherOrEqualPriority } from '@/helpers/roleHelpers';
|
||
|
|
|
||
|
|
function canDeleteUser(currentUserPriority: number, targetUserPriority: number): boolean {
|
||
|
|
// System Admin có thể xóa bất kỳ ai
|
||
|
|
if (isSystemAdminPriority(currentUserPriority)) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
// User chỉ có thể xóa user có priority thấp hơn (số lớn hơn)
|
||
|
|
return hasHigherOrEqualPriority(currentUserPriority, targetUserPriority);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Hiển thị label priority
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { getPriorityLabel } from '@/helpers/roleHelpers';
|
||
|
|
|
||
|
|
<Badge>{getPriorityLabel(role.priority)}</Badge>
|
||
|
|
// System Admin sẽ hiển thị: "System Admin (Highest)"
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Luồng kiểm tra quyền
|
||
|
|
|
||
|
|
```
|
||
|
|
User đăng nhập
|
||
|
|
↓
|
||
|
|
Priority được lưu vào localStorage
|
||
|
|
↓
|
||
|
|
useAuth hook load priority
|
||
|
|
↓
|
||
|
|
isSystemAdmin() kiểm tra priority === 0
|
||
|
|
↓
|
||
|
|
Sidebar check: ALLOW_ALL || isSystemAdmin()
|
||
|
|
↓
|
||
|
|
Hiển thị menu items phù hợp
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Ví dụ thực tế
|
||
|
|
|
||
|
|
### Ví dụ 1: Ẩn/hiện nút Delete dựa trên priority
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
function UserManagement() {
|
||
|
|
const { role, isSystemAdmin } = useAuth();
|
||
|
|
const currentUserPriority = role.priority;
|
||
|
|
|
||
|
|
function canDelete(targetUserPriority: number): boolean {
|
||
|
|
// System Admin xóa được tất cả
|
||
|
|
if (isSystemAdmin()) return true;
|
||
|
|
|
||
|
|
// Priority thấp hơn (số nhỏ hơn) mới xóa được
|
||
|
|
return currentUserPriority < targetUserPriority;
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Table>
|
||
|
|
{users.map(user => (
|
||
|
|
<TableRow key={user.id}>
|
||
|
|
<TableCell>{user.name}</TableCell>
|
||
|
|
<TableCell>
|
||
|
|
{canDelete(user.role.priority) && (
|
||
|
|
<DeleteButton userId={user.id} />
|
||
|
|
)}
|
||
|
|
</TableCell>
|
||
|
|
</TableRow>
|
||
|
|
))}
|
||
|
|
</Table>
|
||
|
|
);
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Ví dụ 2: Route protection
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { useAuth } from '@/hooks/useAuth';
|
||
|
|
import { redirect } from '@tanstack/react-router';
|
||
|
|
|
||
|
|
export const Route = createFileRoute('/_auth/admin-panel')({
|
||
|
|
beforeLoad: ({ context }) => {
|
||
|
|
const { isSystemAdmin } = context.auth;
|
||
|
|
|
||
|
|
if (!isSystemAdmin()) {
|
||
|
|
throw redirect({
|
||
|
|
to: '/unauthorized',
|
||
|
|
});
|
||
|
|
}
|
||
|
|
},
|
||
|
|
component: AdminPanel,
|
||
|
|
});
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Tóm tắt
|
||
|
|
|
||
|
|
✅ **Priority = 0** là System Admin (quyền cao nhất)
|
||
|
|
✅ **Priority thấp hơn** = Quyền cao hơn
|
||
|
|
✅ Có constants và helpers để tái sử dụng
|
||
|
|
✅ `isSystemAdmin()` method trong useAuth hook
|
||
|
|
✅ Sidebar tự động nhận biết System Admin
|
||
|
|
✅ Không cần hardcode giá trị priority nữa
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Files đã thay đổi
|
||
|
|
|
||
|
|
1. ✅ `src/config/constants.ts` - Constants mới
|
||
|
|
2. ✅ `src/helpers/roleHelpers.ts` - Helper functions
|
||
|
|
3. ✅ `src/hooks/useAuth.tsx` - Thêm isSystemAdmin()
|
||
|
|
4. ✅ `src/types/auth.ts` - Cập nhật interface
|
||
|
|
5. ✅ `src/components/sidebars/app-sidebar.tsx` - Logic admin check
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
**Lưu ý quan trọng:**
|
||
|
|
Backend cũng cần implement logic tương tự để đảm bảo consistency giữa frontend và backend!
|