diff --git a/src/config/api.ts b/src/config/api.ts index 519f5f0..6af1583 100644 --- a/src/config/api.ts +++ b/src/config/api.ts @@ -13,6 +13,7 @@ export const API_ENDPOINTS = { PING: `${BASE_URL}/ping`, CSRF_TOKEN: `${BASE_URL}/csrf-token`, CREATE_ACCOUNT: `${BASE_URL}/auth/create-account`, + GET_USERS_LIST: `${BASE_URL}/users-info`, }, APP_VERSION: { //agent and app api diff --git a/src/hooks/queries/index.ts b/src/hooks/queries/index.ts index 490d480..3804d03 100644 --- a/src/hooks/queries/index.ts +++ b/src/hooks/queries/index.ts @@ -15,3 +15,5 @@ export * from "./usePermissionQueries"; // Role Queries export * from "./useRoleQueries"; +// User Queries +export * from "./useUserQueries"; diff --git a/src/hooks/queries/useUserQueries.ts b/src/hooks/queries/useUserQueries.ts new file mode 100644 index 0000000..4592649 --- /dev/null +++ b/src/hooks/queries/useUserQueries.ts @@ -0,0 +1,20 @@ +import { useQuery } from "@tanstack/react-query"; +import * as userService from "@/services/user.service"; +import type { UserProfile } from "@/types/user-profile"; + +const USER_QUERY_KEYS = { + all: ["users"] as const, + list: () => [...USER_QUERY_KEYS.all, "list"] as const, +}; + +/** + * Hook để lấy danh sách thông tin người dùng + */ +export function useGetUsersInfo(enabled = true) { + return useQuery({ + queryKey: USER_QUERY_KEYS.list(), + queryFn: () => userService.getUsersInfo(), + enabled, + staleTime: 5 * 60 * 1000, // 5 minutes + }); +} diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 1af80a4..1cdba4b 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -21,7 +21,6 @@ import { Route as AuthBlacklistsIndexRouteImport } from './routes/_auth/blacklis import { Route as AuthAppsIndexRouteImport } from './routes/_auth/apps/index' import { Route as AuthAgentIndexRouteImport } from './routes/_auth/agent/index' import { Route as authLoginIndexRouteImport } from './routes/(auth)/login/index' -import { Route as AuthUserRoleIndexRouteImport } from './routes/_auth/user/role/index' import { Route as AuthUserCreateIndexRouteImport } from './routes/_auth/user/create/index' import { Route as AuthRoomsRoomNameIndexRouteImport } from './routes/_auth/rooms/$roomName/index' import { Route as AuthRoleCreateIndexRouteImport } from './routes/_auth/role/create/index' @@ -91,11 +90,6 @@ const authLoginIndexRoute = authLoginIndexRouteImport.update({ path: '/login/', getParentRoute: () => rootRouteImport, } as any) -const AuthUserRoleIndexRoute = AuthUserRoleIndexRouteImport.update({ - id: '/user/role/', - path: '/user/role/', - getParentRoute: () => AuthRoute, -} as any) const AuthUserCreateIndexRoute = AuthUserCreateIndexRouteImport.update({ id: '/user/create/', path: '/user/create/', @@ -163,7 +157,6 @@ export interface FileRoutesByFullPath { '/role/create': typeof AuthRoleCreateIndexRoute '/rooms/$roomName': typeof AuthRoomsRoomNameIndexRoute '/user/create': typeof AuthUserCreateIndexRoute - '/user/role': typeof AuthUserRoleIndexRoute '/role/$id/edit': typeof AuthRoleIdEditIndexRoute '/rooms/$roomName/folder-status': typeof AuthRoomsRoomNameFolderStatusIndexRoute '/user/change-password/$userName': typeof AuthUserChangePasswordUserNameIndexRoute @@ -186,7 +179,6 @@ export interface FileRoutesByTo { '/role/create': typeof AuthRoleCreateIndexRoute '/rooms/$roomName': typeof AuthRoomsRoomNameIndexRoute '/user/create': typeof AuthUserCreateIndexRoute - '/user/role': typeof AuthUserRoleIndexRoute '/role/$id/edit': typeof AuthRoleIdEditIndexRoute '/rooms/$roomName/folder-status': typeof AuthRoomsRoomNameFolderStatusIndexRoute '/user/change-password/$userName': typeof AuthUserChangePasswordUserNameIndexRoute @@ -211,7 +203,6 @@ export interface FileRoutesById { '/_auth/role/create/': typeof AuthRoleCreateIndexRoute '/_auth/rooms/$roomName/': typeof AuthRoomsRoomNameIndexRoute '/_auth/user/create/': typeof AuthUserCreateIndexRoute - '/_auth/user/role/': typeof AuthUserRoleIndexRoute '/_auth/role/$id/edit/': typeof AuthRoleIdEditIndexRoute '/_auth/rooms/$roomName/folder-status/': typeof AuthRoomsRoomNameFolderStatusIndexRoute '/_auth/user/change-password/$userName/': typeof AuthUserChangePasswordUserNameIndexRoute @@ -236,7 +227,6 @@ export interface FileRouteTypes { | '/role/create' | '/rooms/$roomName' | '/user/create' - | '/user/role' | '/role/$id/edit' | '/rooms/$roomName/folder-status' | '/user/change-password/$userName' @@ -259,7 +249,6 @@ export interface FileRouteTypes { | '/role/create' | '/rooms/$roomName' | '/user/create' - | '/user/role' | '/role/$id/edit' | '/rooms/$roomName/folder-status' | '/user/change-password/$userName' @@ -283,7 +272,6 @@ export interface FileRouteTypes { | '/_auth/role/create/' | '/_auth/rooms/$roomName/' | '/_auth/user/create/' - | '/_auth/user/role/' | '/_auth/role/$id/edit/' | '/_auth/rooms/$roomName/folder-status/' | '/_auth/user/change-password/$userName/' @@ -382,13 +370,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof authLoginIndexRouteImport parentRoute: typeof rootRouteImport } - '/_auth/user/role/': { - id: '/_auth/user/role/' - path: '/user/role' - fullPath: '/user/role' - preLoaderRoute: typeof AuthUserRoleIndexRouteImport - parentRoute: typeof AuthRoute - } '/_auth/user/create/': { id: '/_auth/user/create/' path: '/user/create' @@ -470,7 +451,6 @@ interface AuthRouteChildren { AuthRoleCreateIndexRoute: typeof AuthRoleCreateIndexRoute AuthRoomsRoomNameIndexRoute: typeof AuthRoomsRoomNameIndexRoute AuthUserCreateIndexRoute: typeof AuthUserCreateIndexRoute - AuthUserRoleIndexRoute: typeof AuthUserRoleIndexRoute AuthRoleIdEditIndexRoute: typeof AuthRoleIdEditIndexRoute AuthRoomsRoomNameFolderStatusIndexRoute: typeof AuthRoomsRoomNameFolderStatusIndexRoute AuthUserChangePasswordUserNameIndexRoute: typeof AuthUserChangePasswordUserNameIndexRoute @@ -492,7 +472,6 @@ const AuthRouteChildren: AuthRouteChildren = { AuthRoleCreateIndexRoute: AuthRoleCreateIndexRoute, AuthRoomsRoomNameIndexRoute: AuthRoomsRoomNameIndexRoute, AuthUserCreateIndexRoute: AuthUserCreateIndexRoute, - AuthUserRoleIndexRoute: AuthUserRoleIndexRoute, AuthRoleIdEditIndexRoute: AuthRoleIdEditIndexRoute, AuthRoomsRoomNameFolderStatusIndexRoute: AuthRoomsRoomNameFolderStatusIndexRoute, diff --git a/src/routes/_auth/user/create/index.tsx b/src/routes/_auth/user/create/index.tsx index 7cdf745..2470107 100644 --- a/src/routes/_auth/user/create/index.tsx +++ b/src/routes/_auth/user/create/index.tsx @@ -135,7 +135,7 @@ function CreateUserComponent() { + + + + ), + enableSorting: false, + enableHiding: false, + }, + ], + [navigate], + ); + + const tableData = users.map((u) => ({ ...u, id: u.userName })); + + return ( + + + + } + > +
+ []} + scrollable={true} + maxHeight="400px" + enablePagination={false} + onTableInit={(t) => setTable(t)} + /> +
+
+ ); } diff --git a/src/routes/_auth/user/role/$roleId/index.tsx b/src/routes/_auth/user/role/$roleId/index.tsx index 0e2e0a1..b7521e6 100644 --- a/src/routes/_auth/user/role/$roleId/index.tsx +++ b/src/routes/_auth/user/role/$roleId/index.tsx @@ -13,11 +13,24 @@ import { Shield, ArrowLeft, Check, X } from "lucide-react"; import type { PermissionOnRole } from "@/types/permission"; export const Route = createFileRoute("/_auth/user/role/$roleId/")({ + head: () => ({ + meta: [{ title: "Quyền của người dùng | AccessControl" }] + }), component: ViewRolePermissionsComponent, loader: async ({ context, params }) => { context.breadcrumbs = [ - { title: "Quản lý người dùng", path: "#" }, - { title: `Quyền của Role #${params.roleId}`, path: `/user/role/${params.roleId}` }, + { + title: "Quản lý tài khoản", + path: "#" + }, + { + title: "Danh sách người dùng", + path: "/user" + }, + { + title: "Quyền của người dùng", + path: `/user/role/${params.roleId}` + } ]; }, }); @@ -60,7 +73,7 @@ function ViewRolePermissionsComponent() { return (
- @@ -88,11 +101,11 @@ function ViewRolePermissionsComponent() {
- {parent?.permissionName || "Unknown"} + {parent?.permissionName || "Allow all"} {parent?.permissionCode}
- {parent?.isChecked === 1 ? ( + {(parent?.isChecked === 1 || parent === null) ? ( Đã bật diff --git a/src/routes/_auth/user/role/index.tsx b/src/routes/_auth/user/role/index.tsx deleted file mode 100644 index f720d09..0000000 --- a/src/routes/_auth/user/role/index.tsx +++ /dev/null @@ -1,133 +0,0 @@ -import { createFileRoute, useNavigate } from "@tanstack/react-router"; -import { useGetRoleList } from "@/hooks/queries"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Badge } from "@/components/ui/badge"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { Shield, Eye, ArrowLeft, Loader2 } from "lucide-react"; - -export const Route = createFileRoute("/_auth/user/role/")({ - component: RoleListComponent, - loader: async ({ context }) => { - context.breadcrumbs = [ - { title: "Quản lý người dùng", path: "#" }, - { title: "Danh sách vai trò", path: "/user/role" }, - ]; - }, -}); - -function RoleListComponent() { - const navigate = useNavigate(); - const { data: roles, isLoading, isError } = useGetRoleList(); - - if (isLoading) { - return ( -
- -
- ); - } - - if (isError) { - return ( -
- - -
- Không thể tải danh sách vai trò -
-
- -
-
-
-
- ); - } - - return ( -
-
- -
- - - - - - Danh sách vai trò - - - Quản lý các vai trò và quyền hạn trong hệ thống - - - - {roles && roles.length > 0 ? ( - - - - ID - Tên vai trò - Độ ưu tiên - Ngày tạo - Thao tác - - - - {roles.map((role) => ( - - - {role.id} - - {role.roleName} - - {role.priority} - - - {role.createdAt ? new Date(role.createdAt).toLocaleDateString("vi-VN") : "—"} - - - - - - ))} - -
- ) : ( -
- Không có vai trò nào trong hệ thống -
- )} -
-
-
- ); -} diff --git a/src/services/user.service.ts b/src/services/user.service.ts new file mode 100644 index 0000000..e196400 --- /dev/null +++ b/src/services/user.service.ts @@ -0,0 +1,34 @@ +import axios from "@/config/axios"; +import { API_ENDPOINTS } from "@/config/api"; +import type { UserProfile } from "@/types/user-profile"; + +/** + * Lấy danh sách thông tin người dùng và chuyển sang camelCase keys + */ +export async function getUsersInfo(): Promise { + try { + const response = await axios.get(API_ENDPOINTS.AUTH.GET_USERS_LIST); + const list = Array.isArray(response.data) ? response.data : []; + + return list.map((u: any) => ({ + userName: u.userName ?? u.UserName ?? "", + name: u.name ?? u.Name ?? "", + role: u.role ?? u.Role ?? "", + roleId: u.roleId !== undefined ? Number(u.roleId) : u.RoleId !== undefined ? Number(u.RoleId) : 0, + accessRooms: Array.isArray(u.accessRooms) + ? u.accessRooms.map((v: any) => Number(v)) + : Array.isArray(u.AccessRooms) + ? u.AccessRooms.map((v: any) => Number(v)) + : [], + createdAt: u.createdAt ?? u.CreatedAt ?? null, + createdBy: u.createdBy ?? u.CreatedBy ?? null, + updatedAt: u.updatedAt ?? u.UpdatedAt ?? null, + updatedBy: u.updatedBy ?? u.UpdatedBy ?? null, + } as UserProfile)); + } catch (error) { + console.error("getUsersInfo error:", error); + throw error; + } +} + +export default { getUsersInfo }; diff --git a/src/template/user-list-template.tsx b/src/template/user-list-template.tsx new file mode 100644 index 0000000..1912e4f --- /dev/null +++ b/src/template/user-list-template.tsx @@ -0,0 +1,38 @@ +import React from "react"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription, CardFooter} from "@/components/ui/card"; + +type Props = { + title?: string; + description?: string; + children?: React.ReactNode; + headerAction?: React.ReactNode; + footer?: React.ReactNode; +}; + +export function UserListTemplate({ title, description, children, headerAction, footer }: Props) { + return ( +
+
+
+

{title}

+ {description &&
{description}
} +
+
{headerAction}
+
+ + + +
+ Danh sách người dùng + {description && {description}} +
+
+ + {children} + {footer && {footer}} +
+
+ ); +} + +export default UserListTemplate; diff --git a/src/types/app-sidebar.ts b/src/types/app-sidebar.ts index 02951ee..8adc1eb 100644 --- a/src/types/app-sidebar.ts +++ b/src/types/app-sidebar.ts @@ -1,4 +1,4 @@ -import { AppWindow, Building, CircleX, Home, ShieldCheck, Terminal, UserPlus} from "lucide-react"; +import { AppWindow, Building, CircleX, Folder, Home, ShieldCheck, Terminal, UserPlus} from "lucide-react"; import { PermissionEnum } from "./permission"; enum AppSidebarSectionCode { @@ -54,7 +54,7 @@ export const appSidebarSection = { { title: "Thư mục Setup", url: "/apps", - icon: AppWindow, + icon: Folder, permissions: [PermissionEnum.VIEW_APPS], } ], diff --git a/src/types/user-profile.ts b/src/types/user-profile.ts new file mode 100644 index 0000000..e561b28 --- /dev/null +++ b/src/types/user-profile.ts @@ -0,0 +1,11 @@ +export type UserProfile = { + userName: string; + name: string; + role: string; + roleId: number; + accessRooms: number[]; + createdAt?: string | null; + createdBy?: string | null; + updatedAt?: string | null; + updatedBy?: string | null; +}; \ No newline at end of file