add user route
This commit is contained in:
parent
dc7ed4c71a
commit
b7b8431975
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -15,3 +15,5 @@ export * from "./usePermissionQueries";
|
|||
|
||||
// Role Queries
|
||||
export * from "./useRoleQueries";
|
||||
// User Queries
|
||||
export * from "./useUserQueries";
|
||||
|
|
|
|||
20
src/hooks/queries/useUserQueries.ts
Normal file
20
src/hooks/queries/useUserQueries.ts
Normal file
|
|
@ -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<UserProfile[]>({
|
||||
queryKey: USER_QUERY_KEYS.list(),
|
||||
queryFn: () => userService.getUsersInfo(),
|
||||
enabled,
|
||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
||||
});
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ function CreateUserComponent() {
|
|||
</div>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => navigate({ to: "/dashboard" })}
|
||||
onClick={() => navigate({ to: "/user" })}
|
||||
>
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Quay lại
|
||||
|
|
|
|||
|
|
@ -1,9 +1,165 @@
|
|||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
import { useMemo, useState } from "react";
|
||||
import { UserListTemplate } from "@/template/user-list-template";
|
||||
import { useGetUsersInfo } from "@/hooks/queries";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipTrigger,
|
||||
TooltipContent,
|
||||
} from "@/components/ui/tooltip";
|
||||
import type { ColumnDef } from "@tanstack/react-table";
|
||||
import { VersionTable } from "@/components/tables/version-table";
|
||||
import { Edit2, Trash2, Shield } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
export const Route = createFileRoute('/_auth/user/')({
|
||||
export const Route = createFileRoute("/_auth/user/")({
|
||||
component: RouteComponent,
|
||||
})
|
||||
loader: async ({ context }) => {
|
||||
context.breadcrumbs = [
|
||||
{ title: "Quản lý người dùng", path: "#" },
|
||||
];
|
||||
},
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
return <div>Hello "/_auth/user/"!</div>
|
||||
const navigate = useNavigate();
|
||||
const { data: users = [], isLoading } = useGetUsersInfo();
|
||||
|
||||
const [table, setTable] = useState<any>();
|
||||
|
||||
const columns = useMemo<ColumnDef<any>[]>(
|
||||
() => [
|
||||
{
|
||||
accessorKey: "userName",
|
||||
header: () => <div className="text-center whitespace-normal max-w-xs">Tên đăng nhập</div>,
|
||||
cell: ({ getValue }) => (
|
||||
<div className="flex justify-center"><span className="font-medium">{getValue() as string}</span></div>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "name",
|
||||
header: () => <div className="text-center whitespace-normal max-w-xs">Họ và tên</div>,
|
||||
cell: ({ getValue }) => (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="truncate block max-w-[240px]">
|
||||
{getValue() as string}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="bottom">
|
||||
{getValue() as string}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
),
|
||||
},
|
||||
{
|
||||
accessorKey: "role",
|
||||
header: () => <div className="text-center whitespace-normal max-w-xs">Vai trò</div>,
|
||||
cell: ({ getValue }) => <div className="flex justify-center">{getValue() as string}</div>,
|
||||
},
|
||||
{
|
||||
accessorKey: "accessRooms",
|
||||
header: () => <div className="text-center whitespace-normal max-w-xs">Phòng</div>,
|
||||
cell: ({ getValue }) => (
|
||||
<div className="flex justify-center">{Array.isArray(getValue()) ? (getValue() as number[]).join(", ") : "-"}</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "select",
|
||||
header: () => <div className="text-center whitespace-normal max-w-xs">Chọn</div>,
|
||||
cell: ({ row }) => (
|
||||
<div className="flex justify-center">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={row.getIsSelected?.() ?? false}
|
||||
onChange={row.getToggleSelectedHandler?.()}
|
||||
/>
|
||||
</div>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
header: () => (
|
||||
<div className="text-center whitespace-normal max-w-xs">Hành động</div>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<div className="flex gap-2 justify-center items-center">
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate({
|
||||
to: "/user/change-password/$userName",
|
||||
params: { userName: row.original.userName },
|
||||
} as any);
|
||||
}}
|
||||
>
|
||||
<Edit2 className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
navigate({ to: "/user/role/$roleId", params: { roleId: String(row.original.roleId) } } as any);
|
||||
}}
|
||||
>
|
||||
<Shield className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
if (!confirm("Bạn có chắc muốn xóa người dùng này?")) return;
|
||||
// Placeholder delete - implement API call as needed
|
||||
toast.success("Xóa người dùng (chưa thực thi API)");
|
||||
if (table) table.setRowSelection({});
|
||||
}}
|
||||
>
|
||||
<Trash2 className="h-4 w-4 text-red-500" />
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
],
|
||||
[navigate],
|
||||
);
|
||||
|
||||
const tableData = users.map((u) => ({ ...u, id: u.userName }));
|
||||
|
||||
return (
|
||||
<UserListTemplate
|
||||
title="Người dùng"
|
||||
description="Danh sách tài khoản hệ thống"
|
||||
headerAction={
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
onClick={() => navigate({ to: "/user/create" } as any)}
|
||||
className="gap-2"
|
||||
>
|
||||
Thêm người dùng
|
||||
</Button>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="p-4">
|
||||
<VersionTable
|
||||
data={tableData}
|
||||
isLoading={isLoading}
|
||||
columns={columns as ColumnDef<any, any>[]}
|
||||
scrollable={true}
|
||||
maxHeight="400px"
|
||||
enablePagination={false}
|
||||
onTableInit={(t) => setTable(t)}
|
||||
/>
|
||||
</div>
|
||||
</UserListTemplate>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="w-full px-6 space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button variant="ghost" size="sm" onClick={() => navigate({ to: "/dashboard" })}>
|
||||
<Button variant="ghost" size="sm" onClick={() => navigate({ to: "/user" })}>
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Quay lại
|
||||
</Button>
|
||||
|
|
@ -88,11 +101,11 @@ function ViewRolePermissionsComponent() {
|
|||
<div key={parent?.permisionId} className="border rounded-lg p-4">
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-semibold text-lg">{parent?.permissionName || "Unknown"}</span>
|
||||
<span className="font-semibold text-lg">{parent?.permissionName || "Allow all"}</span>
|
||||
<Badge variant="secondary" className="text-xs">{parent?.permissionCode}</Badge>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
{parent?.isChecked === 1 ? (
|
||||
{(parent?.isChecked === 1 || parent === null) ? (
|
||||
<Badge variant="default" className="bg-green-600">
|
||||
<Check className="h-3 w-3 mr-1" />Đã bật
|
||||
</Badge>
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
<div className="w-full px-6 flex items-center justify-center py-12">
|
||||
<Loader2 className="h-8 w-8 animate-spin text-primary" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<div className="w-full px-6 space-y-4">
|
||||
<Card className="max-w-2xl mx-auto">
|
||||
<CardContent className="pt-6">
|
||||
<div className="text-center text-destructive">
|
||||
Không thể tải danh sách vai trò
|
||||
</div>
|
||||
<div className="flex justify-center mt-4">
|
||||
<Button variant="outline" onClick={() => navigate({ to: "/dashboard" })}>
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Quay lại
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="w-full px-6 space-y-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<Button variant="ghost" size="sm" onClick={() => navigate({ to: "/dashboard" })}>
|
||||
<ArrowLeft className="h-4 w-4 mr-2" />
|
||||
Quay lại
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Card className="max-w-4xl mx-auto">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Shield className="h-5 w-5" />
|
||||
Danh sách vai trò
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Quản lý các vai trò và quyền hạn trong hệ thống
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{roles && roles.length > 0 ? (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>ID</TableHead>
|
||||
<TableHead>Tên vai trò</TableHead>
|
||||
<TableHead>Độ ưu tiên</TableHead>
|
||||
<TableHead>Ngày tạo</TableHead>
|
||||
<TableHead className="text-right">Thao tác</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{roles.map((role) => (
|
||||
<TableRow key={role.id}>
|
||||
<TableCell>
|
||||
<Badge variant="outline">{role.id}</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="font-medium">{role.roleName}</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant="secondary">{role.priority}</Badge>
|
||||
</TableCell>
|
||||
<TableCell className="text-muted-foreground">
|
||||
{role.createdAt ? new Date(role.createdAt).toLocaleDateString("vi-VN") : "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
navigate({ to: "/user/role/$roleId", params: { roleId: String(role.id) } } as any)
|
||||
}
|
||||
>
|
||||
<Eye className="h-4 w-4 mr-1" />
|
||||
Xem quyền
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
) : (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
Không có vai trò nào trong hệ thống
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
34
src/services/user.service.ts
Normal file
34
src/services/user.service.ts
Normal file
|
|
@ -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<UserProfile[]> {
|
||||
try {
|
||||
const response = await axios.get<any[]>(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 };
|
||||
38
src/template/user-list-template.tsx
Normal file
38
src/template/user-list-template.tsx
Normal file
|
|
@ -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 (
|
||||
<div className="w-full px-6 space-y-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold">{title}</h1>
|
||||
{description && <div className="text-muted-foreground mt-2">{description}</div>}
|
||||
</div>
|
||||
<div>{headerAction}</div>
|
||||
</div>
|
||||
|
||||
<Card className="w-full">
|
||||
<CardHeader>
|
||||
<div>
|
||||
<CardTitle className="flex items-center gap-2">Danh sách người dùng</CardTitle>
|
||||
{description && <CardDescription>{description}</CardDescription>}
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent>{children}</CardContent>
|
||||
{footer && <CardFooter>{footer}</CardFooter>}
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default UserListTemplate;
|
||||
|
|
@ -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],
|
||||
}
|
||||
],
|
||||
|
|
|
|||
11
src/types/user-profile.ts
Normal file
11
src/types/user-profile.ts
Normal file
|
|
@ -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;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user