add audit page
This commit is contained in:
parent
0d9a1ec002
commit
9df4401c47
|
|
@ -109,7 +109,7 @@ export function CommandActionButtons({ roomName, selectedDevices = [] }: Command
|
||||||
// All rendered commands are sourced from sensitiveCommands — send via sensitive mutation
|
// All rendered commands are sourced from sensitiveCommands — send via sensitive mutation
|
||||||
await executeSensitiveMutation.mutateAsync({
|
await executeSensitiveMutation.mutateAsync({
|
||||||
roomName,
|
roomName,
|
||||||
command: confirmDialog.command.commandContent,
|
command: confirmDialog.command.commandName,
|
||||||
password: sensitivePassword,
|
password: sensitivePassword,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
23
src/components/columns/agent-column.tsx
Normal file
23
src/components/columns/agent-column.tsx
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
import type { Version } from "@/types/file";
|
||||||
|
import type { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
||||||
|
export const agentColumns: ColumnDef<Version>[] = [
|
||||||
|
{ accessorKey: "version", header: "Phiên bản" },
|
||||||
|
{ accessorKey: "fileName", header: "Tên file" },
|
||||||
|
{
|
||||||
|
accessorKey: "updatedAt",
|
||||||
|
header: "Thời gian cập nhật",
|
||||||
|
cell: ({ getValue }) =>
|
||||||
|
getValue()
|
||||||
|
? new Date(getValue() as string).toLocaleString("vi-VN")
|
||||||
|
: "N/A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "requestUpdateAt",
|
||||||
|
header: "Thời gian yêu cầu cập nhật",
|
||||||
|
cell: ({ getValue }) =>
|
||||||
|
getValue()
|
||||||
|
? new Date(getValue() as string).toLocaleString("vi-VN")
|
||||||
|
: "N/A",
|
||||||
|
},
|
||||||
|
];
|
||||||
70
src/components/columns/apps-column.tsx
Normal file
70
src/components/columns/apps-column.tsx
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
// components/columns/apps-column.tsx
|
||||||
|
import type { Version } from "@/types/file";
|
||||||
|
import type { ColumnDef } from "@tanstack/react-table";
|
||||||
|
import { Check, X } from "lucide-react";
|
||||||
|
|
||||||
|
// Không gọi hook ở đây — nhận isPending từ ngoài truyền vào
|
||||||
|
export function createAppsColumns(isPending: boolean): ColumnDef<Version>[] {
|
||||||
|
return [
|
||||||
|
{ accessorKey: "version", header: "Phiên bản" },
|
||||||
|
{ accessorKey: "fileName", header: "Tên file" },
|
||||||
|
{
|
||||||
|
accessorKey: "updatedAt",
|
||||||
|
header: () => (
|
||||||
|
<div className="whitespace-normal max-w-xs">Thời gian cập nhật</div>
|
||||||
|
),
|
||||||
|
cell: ({ getValue }) =>
|
||||||
|
getValue()
|
||||||
|
? new Date(getValue() as string).toLocaleString("vi-VN")
|
||||||
|
: "N/A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "requestUpdateAt",
|
||||||
|
header: () => (
|
||||||
|
<div className="whitespace-normal max-w-xs">
|
||||||
|
Thời gian yêu cầu cài đặt/tải xuống
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
cell: ({ getValue }) =>
|
||||||
|
getValue()
|
||||||
|
? new Date(getValue() as string).toLocaleString("vi-VN")
|
||||||
|
: "N/A",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "required",
|
||||||
|
header: () => (
|
||||||
|
<div className="whitespace-normal max-w-xs">Đã thêm vào danh sách</div>
|
||||||
|
),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const isRequired = row.original.isRequired;
|
||||||
|
return isRequired ? (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<Check className="h-4 w-4 text-green-600" />
|
||||||
|
<span className="text-sm text-green-600">Có</span>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<X className="h-4 w-4 text-gray-400" />
|
||||||
|
<span className="text-sm text-gray-400">Không</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "select",
|
||||||
|
header: () => <div className="whitespace-normal max-w-xs">Chọn</div>,
|
||||||
|
cell: ({ row }) => (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={row.getIsSelected?.() ?? false}
|
||||||
|
onChange={row.getToggleSelectedHandler?.()}
|
||||||
|
disabled={isPending} // ← nhận từ tham số, không gọi hook
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
enableSorting: false,
|
||||||
|
enableHiding: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
98
src/components/columns/audit-column.tsx
Normal file
98
src/components/columns/audit-column.tsx
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { type ColumnDef } from "@tanstack/react-table";
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import type { Audits } from "@/types/audit";
|
||||||
|
|
||||||
|
export const auditColumns: ColumnDef<Audits>[] = [
|
||||||
|
{
|
||||||
|
header: "Thời gian",
|
||||||
|
accessorKey: "dateTime",
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
const v = getValue() as string;
|
||||||
|
const d = v ? new Date(v) : null;
|
||||||
|
return d ? (
|
||||||
|
<div className="text-sm whitespace-nowrap">
|
||||||
|
<div className="font-medium">{d.toLocaleDateString("vi-VN")}</div>
|
||||||
|
<div className="text-muted-foreground text-xs">
|
||||||
|
{d.toLocaleTimeString("vi-VN")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<span className="text-muted-foreground">—</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "User",
|
||||||
|
accessorKey: "username",
|
||||||
|
cell: ({ getValue }) => (
|
||||||
|
<span className="font-medium text-sm whitespace-nowrap">
|
||||||
|
{getValue() as string}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Loại",
|
||||||
|
accessorKey: "apiCall",
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
const v = (getValue() as string) ?? "";
|
||||||
|
if (!v) return <span className="text-muted-foreground">—</span>;
|
||||||
|
return (
|
||||||
|
<code className="text-xs bg-muted px-1.5 py-0.5 rounded whitespace-nowrap">
|
||||||
|
{v}
|
||||||
|
</code>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Hành động",
|
||||||
|
accessorKey: "action",
|
||||||
|
cell: ({ getValue }) => (
|
||||||
|
<code className="text-xs bg-muted px-1.5 py-0.5 rounded whitespace-nowrap">
|
||||||
|
{getValue() as string}
|
||||||
|
</code>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "URL",
|
||||||
|
accessorKey: "url",
|
||||||
|
cell: ({ getValue }) => (
|
||||||
|
<code className="text-xs text-muted-foreground max-w-[180px] truncate block">
|
||||||
|
{(getValue() as string) ?? "—"}
|
||||||
|
</code>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Kết quả",
|
||||||
|
accessorKey: "isSuccess",
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
const v = getValue();
|
||||||
|
if (v == null) return <span className="text-muted-foreground">—</span>;
|
||||||
|
return v ? (
|
||||||
|
<Badge variant="outline" className="text-green-600 border-green-600 whitespace-nowrap">
|
||||||
|
Thành công
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge variant="outline" className="text-red-600 border-red-600 whitespace-nowrap">
|
||||||
|
Thất bại
|
||||||
|
</Badge>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
header: "Nội dung request",
|
||||||
|
accessorKey: "requestPayload",
|
||||||
|
cell: ({ getValue }) => {
|
||||||
|
const v = getValue() as string;
|
||||||
|
if (!v) return <span className="text-muted-foreground">—</span>;
|
||||||
|
let preview = v;
|
||||||
|
try {
|
||||||
|
preview = JSON.stringify(JSON.parse(v));
|
||||||
|
} catch {}
|
||||||
|
return (
|
||||||
|
<span className="text-xs text-muted-foreground max-w-[200px] truncate block">
|
||||||
|
{preview}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
180
src/components/dialogs/audit-detail-dialog.tsx
Normal file
180
src/components/dialogs/audit-detail-dialog.tsx
Normal file
|
|
@ -0,0 +1,180 @@
|
||||||
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
import type { Audits } from "@/types/audit";
|
||||||
|
|
||||||
|
function JsonDisplay({ value }: { value: string | null | undefined }) {
|
||||||
|
if (!value) return <span className="text-muted-foreground">—</span>;
|
||||||
|
try {
|
||||||
|
return (
|
||||||
|
<pre className="text-xs bg-muted/60 p-2.5 rounded-md overflow-auto whitespace-pre-wrap break-all leading-relaxed max-h-48 font-mono">
|
||||||
|
{JSON.stringify(JSON.parse(value), null, 2)}
|
||||||
|
</pre>
|
||||||
|
);
|
||||||
|
} catch {
|
||||||
|
return <span className="text-xs break-all font-mono">{value}</span>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AuditDetailDialogProps {
|
||||||
|
audit: Audits | null;
|
||||||
|
open: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AuditDetailDialog({
|
||||||
|
audit,
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
}: AuditDetailDialogProps) {
|
||||||
|
if (!audit) return null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onOpenChange={(o) => !o && onClose()}>
|
||||||
|
<DialogContent className="max-w-2xl w-full max-h-[85vh] overflow-y-auto">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="flex items-center gap-2">
|
||||||
|
Chi tiết audit
|
||||||
|
<span className="text-muted-foreground font-normal text-sm">
|
||||||
|
#{audit.id}
|
||||||
|
</span>
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<div className="grid grid-cols-2 gap-x-6 gap-y-3 pt-1">
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Thời gian
|
||||||
|
</p>
|
||||||
|
<p className="text-sm font-medium">
|
||||||
|
{audit.dateTime
|
||||||
|
? new Date(audit.dateTime).toLocaleString("vi-VN")
|
||||||
|
: "—"}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
User
|
||||||
|
</p>
|
||||||
|
<p className="text-sm font-medium">{audit.username}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
API Call
|
||||||
|
</p>
|
||||||
|
<code className="text-xs bg-muted px-1.5 py-0.5 rounded">
|
||||||
|
{audit.apiCall ?? "—"}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Kết quả
|
||||||
|
</p>
|
||||||
|
<div>
|
||||||
|
{audit.isSuccess == null ? (
|
||||||
|
<span className="text-muted-foreground text-sm">—</span>
|
||||||
|
) : audit.isSuccess ? (
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="text-green-600 border-green-600"
|
||||||
|
>
|
||||||
|
Thành công
|
||||||
|
</Badge>
|
||||||
|
) : (
|
||||||
|
<Badge
|
||||||
|
variant="outline"
|
||||||
|
className="text-red-600 border-red-600"
|
||||||
|
>
|
||||||
|
Thất bại
|
||||||
|
</Badge>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Hành động
|
||||||
|
</p>
|
||||||
|
<code className="text-xs bg-muted px-1.5 py-0.5 rounded">
|
||||||
|
{audit.action}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
URL
|
||||||
|
</p>
|
||||||
|
<code className="text-xs text-muted-foreground break-all">
|
||||||
|
{audit.url ?? "—"}
|
||||||
|
</code>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Bảng
|
||||||
|
</p>
|
||||||
|
<p className="text-sm">{audit.tableName ?? "—"}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Entity ID
|
||||||
|
</p>
|
||||||
|
<p className="text-sm">{audit.entityId ?? "—"}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-span-2 space-y-0.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Lỗi
|
||||||
|
</p>
|
||||||
|
<p className="text-sm text-red-600">{audit.errorMessage ?? "—"}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Separator />
|
||||||
|
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Nội dung request
|
||||||
|
</p>
|
||||||
|
<JsonDisplay value={audit.requestPayload} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Giá trị cũ
|
||||||
|
</p>
|
||||||
|
<JsonDisplay value={audit.oldValues} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Giá trị mới
|
||||||
|
</p>
|
||||||
|
<JsonDisplay value={audit.newValues} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<p className="text-xs text-muted-foreground uppercase tracking-wide">
|
||||||
|
Kết quả
|
||||||
|
</p>
|
||||||
|
<p className="text-sm">{audit.isSuccess == null ? "—" : audit.isSuccess ? "Thành công" : "Thất bại"}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
73
src/components/filters/audit-filter-bar.tsx
Normal file
73
src/components/filters/audit-filter-bar.tsx
Normal file
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
|
||||||
|
interface AuditFilterBarProps {
|
||||||
|
username: string | null;
|
||||||
|
action: string | null;
|
||||||
|
from: string | null;
|
||||||
|
to: string | null;
|
||||||
|
isLoading: boolean;
|
||||||
|
isFetching: boolean;
|
||||||
|
onUsernameChange: (v: string | null) => void;
|
||||||
|
onActionChange: (v: string | null) => void;
|
||||||
|
onFromChange: (v: string | null) => void;
|
||||||
|
onToChange: (v: string | null) => void;
|
||||||
|
onSearch: () => void;
|
||||||
|
onReset: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AuditFilterBar({
|
||||||
|
username,
|
||||||
|
action,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
isLoading,
|
||||||
|
isFetching,
|
||||||
|
onUsernameChange,
|
||||||
|
onActionChange,
|
||||||
|
onFromChange,
|
||||||
|
onToChange,
|
||||||
|
onSearch,
|
||||||
|
onReset,
|
||||||
|
}: AuditFilterBarProps) {
|
||||||
|
return (
|
||||||
|
<div className="flex gap-2 mb-4 flex-wrap items-end">
|
||||||
|
<Input
|
||||||
|
className="w-36"
|
||||||
|
placeholder="Username"
|
||||||
|
value={username ?? ""}
|
||||||
|
onChange={(e) => onUsernameChange(e.target.value || null)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
className="w-44"
|
||||||
|
placeholder="Hành động..."
|
||||||
|
value={action ?? ""}
|
||||||
|
onChange={(e) => onActionChange(e.target.value || null)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
className="w-36"
|
||||||
|
type="date"
|
||||||
|
value={from ?? ""}
|
||||||
|
onChange={(e) => onFromChange(e.target.value || null)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
className="w-36"
|
||||||
|
type="date"
|
||||||
|
value={to ?? ""}
|
||||||
|
onChange={(e) => onToChange(e.target.value || null)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button onClick={onSearch} disabled={isFetching || isLoading} size="sm">
|
||||||
|
Tìm
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" onClick={onReset} size="sm">
|
||||||
|
Reset
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -82,4 +82,12 @@ export const API_ENDPOINTS = {
|
||||||
TOGGLE_PERMISSION: (roleId: number, permissionId: number) =>
|
TOGGLE_PERMISSION: (roleId: number, permissionId: number) =>
|
||||||
`${BASE_URL}/Role/${roleId}/permissions/${permissionId}/toggle`,
|
`${BASE_URL}/Role/${roleId}/permissions/${permissionId}/toggle`,
|
||||||
},
|
},
|
||||||
|
DASHBOARD:
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
,
|
||||||
|
AUDIT: {
|
||||||
|
GET_AUDITS: `${BASE_URL}/Audit/audits`,
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,9 @@ export * from "./useDeviceCommQueries";
|
||||||
// Command Queries
|
// Command Queries
|
||||||
export * from "./useCommandQueries";
|
export * from "./useCommandQueries";
|
||||||
|
|
||||||
|
// Audit Queries
|
||||||
|
export * from "./useAuditQueries";
|
||||||
|
|
||||||
// Permission Queries
|
// Permission Queries
|
||||||
export * from "./usePermissionQueries";
|
export * from "./usePermissionQueries";
|
||||||
|
|
||||||
|
|
|
||||||
37
src/hooks/queries/useAuditQueries.ts
Normal file
37
src/hooks/queries/useAuditQueries.ts
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
import * as auditService from "@/services/audit.service";
|
||||||
|
import type { PageResult, Audits } from "@/types/audit";
|
||||||
|
|
||||||
|
const AUDIT_QUERY_KEYS = {
|
||||||
|
all: ["audit"] as const,
|
||||||
|
list: () => [...AUDIT_QUERY_KEYS.all, "list"] as const,
|
||||||
|
audits: (params: any) => [...AUDIT_QUERY_KEYS.all, "audits", params] as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function useGetAudits(
|
||||||
|
params: {
|
||||||
|
pageNumber?: number;
|
||||||
|
pageSize?: number;
|
||||||
|
username?: string | null;
|
||||||
|
action?: string | null;
|
||||||
|
from?: string | null;
|
||||||
|
to?: string | null;
|
||||||
|
} = { pageNumber: 1, pageSize: 20 },
|
||||||
|
enabled = true
|
||||||
|
) {
|
||||||
|
const { pageNumber = 1, pageSize = 20, username, action, from, to } = params;
|
||||||
|
|
||||||
|
return useQuery<PageResult<Audits>>({
|
||||||
|
queryKey: AUDIT_QUERY_KEYS.audits({ pageNumber, pageSize, username, action, from, to }),
|
||||||
|
queryFn: () =>
|
||||||
|
auditService.getAudits(
|
||||||
|
pageNumber,
|
||||||
|
pageSize,
|
||||||
|
username ?? null,
|
||||||
|
action ?? null,
|
||||||
|
from ?? null,
|
||||||
|
to ?? null
|
||||||
|
),
|
||||||
|
enabled,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ import { Route as AuthDeviceIndexRouteImport } from './routes/_auth/device/index
|
||||||
import { Route as AuthDashboardIndexRouteImport } from './routes/_auth/dashboard/index'
|
import { Route as AuthDashboardIndexRouteImport } from './routes/_auth/dashboard/index'
|
||||||
import { Route as AuthCommandsIndexRouteImport } from './routes/_auth/commands/index'
|
import { Route as AuthCommandsIndexRouteImport } from './routes/_auth/commands/index'
|
||||||
import { Route as AuthBlacklistsIndexRouteImport } from './routes/_auth/blacklists/index'
|
import { Route as AuthBlacklistsIndexRouteImport } from './routes/_auth/blacklists/index'
|
||||||
|
import { Route as AuthAuditsIndexRouteImport } from './routes/_auth/audits/index'
|
||||||
import { Route as AuthAppsIndexRouteImport } from './routes/_auth/apps/index'
|
import { Route as AuthAppsIndexRouteImport } from './routes/_auth/apps/index'
|
||||||
import { Route as AuthAgentIndexRouteImport } from './routes/_auth/agent/index'
|
import { Route as AuthAgentIndexRouteImport } from './routes/_auth/agent/index'
|
||||||
import { Route as authLoginIndexRouteImport } from './routes/(auth)/login/index'
|
import { Route as authLoginIndexRouteImport } from './routes/(auth)/login/index'
|
||||||
|
|
@ -29,6 +30,7 @@ import { Route as AuthProfileUserNameIndexRouteImport } from './routes/_auth/pro
|
||||||
import { Route as AuthUserRoleRoleIdIndexRouteImport } from './routes/_auth/user/role/$roleId/index'
|
import { Route as AuthUserRoleRoleIdIndexRouteImport } from './routes/_auth/user/role/$roleId/index'
|
||||||
import { Route as AuthUserChangePasswordUserNameIndexRouteImport } from './routes/_auth/user/change-password/$userName/index'
|
import { Route as AuthUserChangePasswordUserNameIndexRouteImport } from './routes/_auth/user/change-password/$userName/index'
|
||||||
import { Route as AuthRoomsRoomNameFolderStatusIndexRouteImport } from './routes/_auth/rooms/$roomName/folder-status/index'
|
import { Route as AuthRoomsRoomNameFolderStatusIndexRouteImport } from './routes/_auth/rooms/$roomName/folder-status/index'
|
||||||
|
import { Route as AuthRoomsRoomNameConnectIndexRouteImport } from './routes/_auth/rooms/$roomName/connect/index'
|
||||||
import { Route as AuthRoleIdEditIndexRouteImport } from './routes/_auth/role/$id/edit/index'
|
import { Route as AuthRoleIdEditIndexRouteImport } from './routes/_auth/role/$id/edit/index'
|
||||||
|
|
||||||
const AuthRoute = AuthRouteImport.update({
|
const AuthRoute = AuthRouteImport.update({
|
||||||
|
|
@ -75,6 +77,11 @@ const AuthBlacklistsIndexRoute = AuthBlacklistsIndexRouteImport.update({
|
||||||
path: '/blacklists/',
|
path: '/blacklists/',
|
||||||
getParentRoute: () => AuthRoute,
|
getParentRoute: () => AuthRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const AuthAuditsIndexRoute = AuthAuditsIndexRouteImport.update({
|
||||||
|
id: '/audits/',
|
||||||
|
path: '/audits/',
|
||||||
|
getParentRoute: () => AuthRoute,
|
||||||
|
} as any)
|
||||||
const AuthAppsIndexRoute = AuthAppsIndexRouteImport.update({
|
const AuthAppsIndexRoute = AuthAppsIndexRouteImport.update({
|
||||||
id: '/apps/',
|
id: '/apps/',
|
||||||
path: '/apps/',
|
path: '/apps/',
|
||||||
|
|
@ -134,6 +141,12 @@ const AuthRoomsRoomNameFolderStatusIndexRoute =
|
||||||
path: '/rooms/$roomName/folder-status/',
|
path: '/rooms/$roomName/folder-status/',
|
||||||
getParentRoute: () => AuthRoute,
|
getParentRoute: () => AuthRoute,
|
||||||
} as any)
|
} as any)
|
||||||
|
const AuthRoomsRoomNameConnectIndexRoute =
|
||||||
|
AuthRoomsRoomNameConnectIndexRouteImport.update({
|
||||||
|
id: '/rooms/$roomName/connect/',
|
||||||
|
path: '/rooms/$roomName/connect/',
|
||||||
|
getParentRoute: () => AuthRoute,
|
||||||
|
} as any)
|
||||||
const AuthRoleIdEditIndexRoute = AuthRoleIdEditIndexRouteImport.update({
|
const AuthRoleIdEditIndexRoute = AuthRoleIdEditIndexRouteImport.update({
|
||||||
id: '/role/$id/edit/',
|
id: '/role/$id/edit/',
|
||||||
path: '/role/$id/edit/',
|
path: '/role/$id/edit/',
|
||||||
|
|
@ -145,6 +158,7 @@ export interface FileRoutesByFullPath {
|
||||||
'/login': typeof authLoginIndexRoute
|
'/login': typeof authLoginIndexRoute
|
||||||
'/agent': typeof AuthAgentIndexRoute
|
'/agent': typeof AuthAgentIndexRoute
|
||||||
'/apps': typeof AuthAppsIndexRoute
|
'/apps': typeof AuthAppsIndexRoute
|
||||||
|
'/audits': typeof AuthAuditsIndexRoute
|
||||||
'/blacklists': typeof AuthBlacklistsIndexRoute
|
'/blacklists': typeof AuthBlacklistsIndexRoute
|
||||||
'/commands': typeof AuthCommandsIndexRoute
|
'/commands': typeof AuthCommandsIndexRoute
|
||||||
'/dashboard': typeof AuthDashboardIndexRoute
|
'/dashboard': typeof AuthDashboardIndexRoute
|
||||||
|
|
@ -158,6 +172,7 @@ export interface FileRoutesByFullPath {
|
||||||
'/rooms/$roomName': typeof AuthRoomsRoomNameIndexRoute
|
'/rooms/$roomName': typeof AuthRoomsRoomNameIndexRoute
|
||||||
'/user/create': typeof AuthUserCreateIndexRoute
|
'/user/create': typeof AuthUserCreateIndexRoute
|
||||||
'/role/$id/edit': typeof AuthRoleIdEditIndexRoute
|
'/role/$id/edit': typeof AuthRoleIdEditIndexRoute
|
||||||
|
'/rooms/$roomName/connect': typeof AuthRoomsRoomNameConnectIndexRoute
|
||||||
'/rooms/$roomName/folder-status': typeof AuthRoomsRoomNameFolderStatusIndexRoute
|
'/rooms/$roomName/folder-status': typeof AuthRoomsRoomNameFolderStatusIndexRoute
|
||||||
'/user/change-password/$userName': typeof AuthUserChangePasswordUserNameIndexRoute
|
'/user/change-password/$userName': typeof AuthUserChangePasswordUserNameIndexRoute
|
||||||
'/user/role/$roleId': typeof AuthUserRoleRoleIdIndexRoute
|
'/user/role/$roleId': typeof AuthUserRoleRoleIdIndexRoute
|
||||||
|
|
@ -167,6 +182,7 @@ export interface FileRoutesByTo {
|
||||||
'/login': typeof authLoginIndexRoute
|
'/login': typeof authLoginIndexRoute
|
||||||
'/agent': typeof AuthAgentIndexRoute
|
'/agent': typeof AuthAgentIndexRoute
|
||||||
'/apps': typeof AuthAppsIndexRoute
|
'/apps': typeof AuthAppsIndexRoute
|
||||||
|
'/audits': typeof AuthAuditsIndexRoute
|
||||||
'/blacklists': typeof AuthBlacklistsIndexRoute
|
'/blacklists': typeof AuthBlacklistsIndexRoute
|
||||||
'/commands': typeof AuthCommandsIndexRoute
|
'/commands': typeof AuthCommandsIndexRoute
|
||||||
'/dashboard': typeof AuthDashboardIndexRoute
|
'/dashboard': typeof AuthDashboardIndexRoute
|
||||||
|
|
@ -180,6 +196,7 @@ export interface FileRoutesByTo {
|
||||||
'/rooms/$roomName': typeof AuthRoomsRoomNameIndexRoute
|
'/rooms/$roomName': typeof AuthRoomsRoomNameIndexRoute
|
||||||
'/user/create': typeof AuthUserCreateIndexRoute
|
'/user/create': typeof AuthUserCreateIndexRoute
|
||||||
'/role/$id/edit': typeof AuthRoleIdEditIndexRoute
|
'/role/$id/edit': typeof AuthRoleIdEditIndexRoute
|
||||||
|
'/rooms/$roomName/connect': typeof AuthRoomsRoomNameConnectIndexRoute
|
||||||
'/rooms/$roomName/folder-status': typeof AuthRoomsRoomNameFolderStatusIndexRoute
|
'/rooms/$roomName/folder-status': typeof AuthRoomsRoomNameFolderStatusIndexRoute
|
||||||
'/user/change-password/$userName': typeof AuthUserChangePasswordUserNameIndexRoute
|
'/user/change-password/$userName': typeof AuthUserChangePasswordUserNameIndexRoute
|
||||||
'/user/role/$roleId': typeof AuthUserRoleRoleIdIndexRoute
|
'/user/role/$roleId': typeof AuthUserRoleRoleIdIndexRoute
|
||||||
|
|
@ -191,6 +208,7 @@ export interface FileRoutesById {
|
||||||
'/(auth)/login/': typeof authLoginIndexRoute
|
'/(auth)/login/': typeof authLoginIndexRoute
|
||||||
'/_auth/agent/': typeof AuthAgentIndexRoute
|
'/_auth/agent/': typeof AuthAgentIndexRoute
|
||||||
'/_auth/apps/': typeof AuthAppsIndexRoute
|
'/_auth/apps/': typeof AuthAppsIndexRoute
|
||||||
|
'/_auth/audits/': typeof AuthAuditsIndexRoute
|
||||||
'/_auth/blacklists/': typeof AuthBlacklistsIndexRoute
|
'/_auth/blacklists/': typeof AuthBlacklistsIndexRoute
|
||||||
'/_auth/commands/': typeof AuthCommandsIndexRoute
|
'/_auth/commands/': typeof AuthCommandsIndexRoute
|
||||||
'/_auth/dashboard/': typeof AuthDashboardIndexRoute
|
'/_auth/dashboard/': typeof AuthDashboardIndexRoute
|
||||||
|
|
@ -204,6 +222,7 @@ export interface FileRoutesById {
|
||||||
'/_auth/rooms/$roomName/': typeof AuthRoomsRoomNameIndexRoute
|
'/_auth/rooms/$roomName/': typeof AuthRoomsRoomNameIndexRoute
|
||||||
'/_auth/user/create/': typeof AuthUserCreateIndexRoute
|
'/_auth/user/create/': typeof AuthUserCreateIndexRoute
|
||||||
'/_auth/role/$id/edit/': typeof AuthRoleIdEditIndexRoute
|
'/_auth/role/$id/edit/': typeof AuthRoleIdEditIndexRoute
|
||||||
|
'/_auth/rooms/$roomName/connect/': typeof AuthRoomsRoomNameConnectIndexRoute
|
||||||
'/_auth/rooms/$roomName/folder-status/': typeof AuthRoomsRoomNameFolderStatusIndexRoute
|
'/_auth/rooms/$roomName/folder-status/': typeof AuthRoomsRoomNameFolderStatusIndexRoute
|
||||||
'/_auth/user/change-password/$userName/': typeof AuthUserChangePasswordUserNameIndexRoute
|
'/_auth/user/change-password/$userName/': typeof AuthUserChangePasswordUserNameIndexRoute
|
||||||
'/_auth/user/role/$roleId/': typeof AuthUserRoleRoleIdIndexRoute
|
'/_auth/user/role/$roleId/': typeof AuthUserRoleRoleIdIndexRoute
|
||||||
|
|
@ -215,6 +234,7 @@ export interface FileRouteTypes {
|
||||||
| '/login'
|
| '/login'
|
||||||
| '/agent'
|
| '/agent'
|
||||||
| '/apps'
|
| '/apps'
|
||||||
|
| '/audits'
|
||||||
| '/blacklists'
|
| '/blacklists'
|
||||||
| '/commands'
|
| '/commands'
|
||||||
| '/dashboard'
|
| '/dashboard'
|
||||||
|
|
@ -228,6 +248,7 @@ export interface FileRouteTypes {
|
||||||
| '/rooms/$roomName'
|
| '/rooms/$roomName'
|
||||||
| '/user/create'
|
| '/user/create'
|
||||||
| '/role/$id/edit'
|
| '/role/$id/edit'
|
||||||
|
| '/rooms/$roomName/connect'
|
||||||
| '/rooms/$roomName/folder-status'
|
| '/rooms/$roomName/folder-status'
|
||||||
| '/user/change-password/$userName'
|
| '/user/change-password/$userName'
|
||||||
| '/user/role/$roleId'
|
| '/user/role/$roleId'
|
||||||
|
|
@ -237,6 +258,7 @@ export interface FileRouteTypes {
|
||||||
| '/login'
|
| '/login'
|
||||||
| '/agent'
|
| '/agent'
|
||||||
| '/apps'
|
| '/apps'
|
||||||
|
| '/audits'
|
||||||
| '/blacklists'
|
| '/blacklists'
|
||||||
| '/commands'
|
| '/commands'
|
||||||
| '/dashboard'
|
| '/dashboard'
|
||||||
|
|
@ -250,6 +272,7 @@ export interface FileRouteTypes {
|
||||||
| '/rooms/$roomName'
|
| '/rooms/$roomName'
|
||||||
| '/user/create'
|
| '/user/create'
|
||||||
| '/role/$id/edit'
|
| '/role/$id/edit'
|
||||||
|
| '/rooms/$roomName/connect'
|
||||||
| '/rooms/$roomName/folder-status'
|
| '/rooms/$roomName/folder-status'
|
||||||
| '/user/change-password/$userName'
|
| '/user/change-password/$userName'
|
||||||
| '/user/role/$roleId'
|
| '/user/role/$roleId'
|
||||||
|
|
@ -260,6 +283,7 @@ export interface FileRouteTypes {
|
||||||
| '/(auth)/login/'
|
| '/(auth)/login/'
|
||||||
| '/_auth/agent/'
|
| '/_auth/agent/'
|
||||||
| '/_auth/apps/'
|
| '/_auth/apps/'
|
||||||
|
| '/_auth/audits/'
|
||||||
| '/_auth/blacklists/'
|
| '/_auth/blacklists/'
|
||||||
| '/_auth/commands/'
|
| '/_auth/commands/'
|
||||||
| '/_auth/dashboard/'
|
| '/_auth/dashboard/'
|
||||||
|
|
@ -273,6 +297,7 @@ export interface FileRouteTypes {
|
||||||
| '/_auth/rooms/$roomName/'
|
| '/_auth/rooms/$roomName/'
|
||||||
| '/_auth/user/create/'
|
| '/_auth/user/create/'
|
||||||
| '/_auth/role/$id/edit/'
|
| '/_auth/role/$id/edit/'
|
||||||
|
| '/_auth/rooms/$roomName/connect/'
|
||||||
| '/_auth/rooms/$roomName/folder-status/'
|
| '/_auth/rooms/$roomName/folder-status/'
|
||||||
| '/_auth/user/change-password/$userName/'
|
| '/_auth/user/change-password/$userName/'
|
||||||
| '/_auth/user/role/$roleId/'
|
| '/_auth/user/role/$roleId/'
|
||||||
|
|
@ -349,6 +374,13 @@ declare module '@tanstack/react-router' {
|
||||||
preLoaderRoute: typeof AuthBlacklistsIndexRouteImport
|
preLoaderRoute: typeof AuthBlacklistsIndexRouteImport
|
||||||
parentRoute: typeof AuthRoute
|
parentRoute: typeof AuthRoute
|
||||||
}
|
}
|
||||||
|
'/_auth/audits/': {
|
||||||
|
id: '/_auth/audits/'
|
||||||
|
path: '/audits'
|
||||||
|
fullPath: '/audits'
|
||||||
|
preLoaderRoute: typeof AuthAuditsIndexRouteImport
|
||||||
|
parentRoute: typeof AuthRoute
|
||||||
|
}
|
||||||
'/_auth/apps/': {
|
'/_auth/apps/': {
|
||||||
id: '/_auth/apps/'
|
id: '/_auth/apps/'
|
||||||
path: '/apps'
|
path: '/apps'
|
||||||
|
|
@ -426,6 +458,13 @@ declare module '@tanstack/react-router' {
|
||||||
preLoaderRoute: typeof AuthRoomsRoomNameFolderStatusIndexRouteImport
|
preLoaderRoute: typeof AuthRoomsRoomNameFolderStatusIndexRouteImport
|
||||||
parentRoute: typeof AuthRoute
|
parentRoute: typeof AuthRoute
|
||||||
}
|
}
|
||||||
|
'/_auth/rooms/$roomName/connect/': {
|
||||||
|
id: '/_auth/rooms/$roomName/connect/'
|
||||||
|
path: '/rooms/$roomName/connect'
|
||||||
|
fullPath: '/rooms/$roomName/connect'
|
||||||
|
preLoaderRoute: typeof AuthRoomsRoomNameConnectIndexRouteImport
|
||||||
|
parentRoute: typeof AuthRoute
|
||||||
|
}
|
||||||
'/_auth/role/$id/edit/': {
|
'/_auth/role/$id/edit/': {
|
||||||
id: '/_auth/role/$id/edit/'
|
id: '/_auth/role/$id/edit/'
|
||||||
path: '/role/$id/edit'
|
path: '/role/$id/edit'
|
||||||
|
|
@ -439,6 +478,7 @@ declare module '@tanstack/react-router' {
|
||||||
interface AuthRouteChildren {
|
interface AuthRouteChildren {
|
||||||
AuthAgentIndexRoute: typeof AuthAgentIndexRoute
|
AuthAgentIndexRoute: typeof AuthAgentIndexRoute
|
||||||
AuthAppsIndexRoute: typeof AuthAppsIndexRoute
|
AuthAppsIndexRoute: typeof AuthAppsIndexRoute
|
||||||
|
AuthAuditsIndexRoute: typeof AuthAuditsIndexRoute
|
||||||
AuthBlacklistsIndexRoute: typeof AuthBlacklistsIndexRoute
|
AuthBlacklistsIndexRoute: typeof AuthBlacklistsIndexRoute
|
||||||
AuthCommandsIndexRoute: typeof AuthCommandsIndexRoute
|
AuthCommandsIndexRoute: typeof AuthCommandsIndexRoute
|
||||||
AuthDashboardIndexRoute: typeof AuthDashboardIndexRoute
|
AuthDashboardIndexRoute: typeof AuthDashboardIndexRoute
|
||||||
|
|
@ -452,6 +492,7 @@ interface AuthRouteChildren {
|
||||||
AuthRoomsRoomNameIndexRoute: typeof AuthRoomsRoomNameIndexRoute
|
AuthRoomsRoomNameIndexRoute: typeof AuthRoomsRoomNameIndexRoute
|
||||||
AuthUserCreateIndexRoute: typeof AuthUserCreateIndexRoute
|
AuthUserCreateIndexRoute: typeof AuthUserCreateIndexRoute
|
||||||
AuthRoleIdEditIndexRoute: typeof AuthRoleIdEditIndexRoute
|
AuthRoleIdEditIndexRoute: typeof AuthRoleIdEditIndexRoute
|
||||||
|
AuthRoomsRoomNameConnectIndexRoute: typeof AuthRoomsRoomNameConnectIndexRoute
|
||||||
AuthRoomsRoomNameFolderStatusIndexRoute: typeof AuthRoomsRoomNameFolderStatusIndexRoute
|
AuthRoomsRoomNameFolderStatusIndexRoute: typeof AuthRoomsRoomNameFolderStatusIndexRoute
|
||||||
AuthUserChangePasswordUserNameIndexRoute: typeof AuthUserChangePasswordUserNameIndexRoute
|
AuthUserChangePasswordUserNameIndexRoute: typeof AuthUserChangePasswordUserNameIndexRoute
|
||||||
AuthUserRoleRoleIdIndexRoute: typeof AuthUserRoleRoleIdIndexRoute
|
AuthUserRoleRoleIdIndexRoute: typeof AuthUserRoleRoleIdIndexRoute
|
||||||
|
|
@ -460,6 +501,7 @@ interface AuthRouteChildren {
|
||||||
const AuthRouteChildren: AuthRouteChildren = {
|
const AuthRouteChildren: AuthRouteChildren = {
|
||||||
AuthAgentIndexRoute: AuthAgentIndexRoute,
|
AuthAgentIndexRoute: AuthAgentIndexRoute,
|
||||||
AuthAppsIndexRoute: AuthAppsIndexRoute,
|
AuthAppsIndexRoute: AuthAppsIndexRoute,
|
||||||
|
AuthAuditsIndexRoute: AuthAuditsIndexRoute,
|
||||||
AuthBlacklistsIndexRoute: AuthBlacklistsIndexRoute,
|
AuthBlacklistsIndexRoute: AuthBlacklistsIndexRoute,
|
||||||
AuthCommandsIndexRoute: AuthCommandsIndexRoute,
|
AuthCommandsIndexRoute: AuthCommandsIndexRoute,
|
||||||
AuthDashboardIndexRoute: AuthDashboardIndexRoute,
|
AuthDashboardIndexRoute: AuthDashboardIndexRoute,
|
||||||
|
|
@ -473,6 +515,7 @@ const AuthRouteChildren: AuthRouteChildren = {
|
||||||
AuthRoomsRoomNameIndexRoute: AuthRoomsRoomNameIndexRoute,
|
AuthRoomsRoomNameIndexRoute: AuthRoomsRoomNameIndexRoute,
|
||||||
AuthUserCreateIndexRoute: AuthUserCreateIndexRoute,
|
AuthUserCreateIndexRoute: AuthUserCreateIndexRoute,
|
||||||
AuthRoleIdEditIndexRoute: AuthRoleIdEditIndexRoute,
|
AuthRoleIdEditIndexRoute: AuthRoleIdEditIndexRoute,
|
||||||
|
AuthRoomsRoomNameConnectIndexRoute: AuthRoomsRoomNameConnectIndexRoute,
|
||||||
AuthRoomsRoomNameFolderStatusIndexRoute:
|
AuthRoomsRoomNameFolderStatusIndexRoute:
|
||||||
AuthRoomsRoomNameFolderStatusIndexRoute,
|
AuthRoomsRoomNameFolderStatusIndexRoute,
|
||||||
AuthUserChangePasswordUserNameIndexRoute:
|
AuthUserChangePasswordUserNameIndexRoute:
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,10 @@ import {
|
||||||
useUpdateAgent,
|
useUpdateAgent,
|
||||||
} from "@/hooks/queries";
|
} from "@/hooks/queries";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import type { ColumnDef } from "@tanstack/react-table";
|
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
import type { Version } from "@/types/file";
|
import type { Version } from "@/types/file";
|
||||||
import { ErrorFetchingPage } from "@/components/pages/error-fetching-page";
|
import { ErrorFetchingPage } from "@/components/pages/error-fetching-page";
|
||||||
|
import { agentColumns } from "@/components/columns/agent-column";
|
||||||
export const Route = createFileRoute("/_auth/agent/")({
|
export const Route = createFileRoute("/_auth/agent/")({
|
||||||
head: () => ({ meta: [{ title: "Quản lý Agent" }] }),
|
head: () => ({ meta: [{ title: "Quản lý Agent" }] }),
|
||||||
component: AgentsPage,
|
component: AgentsPage,
|
||||||
|
|
@ -71,26 +70,7 @@ function AgentsPage() {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Cột bảng
|
// Cột bảng
|
||||||
const columns: ColumnDef<Version>[] = [
|
|
||||||
{ accessorKey: "version", header: "Phiên bản" },
|
|
||||||
{ accessorKey: "fileName", header: "Tên file" },
|
|
||||||
{
|
|
||||||
accessorKey: "updatedAt",
|
|
||||||
header: "Thời gian cập nhật",
|
|
||||||
cell: ({ getValue }) =>
|
|
||||||
getValue()
|
|
||||||
? new Date(getValue() as string).toLocaleString("vi-VN")
|
|
||||||
: "N/A",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "requestUpdateAt",
|
|
||||||
header: "Thời gian yêu cầu cập nhật",
|
|
||||||
cell: ({ getValue }) =>
|
|
||||||
getValue()
|
|
||||||
? new Date(getValue() as string).toLocaleString("vi-VN")
|
|
||||||
: "N/A",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppManagerTemplate<Version>
|
<AppManagerTemplate<Version>
|
||||||
|
|
@ -98,7 +78,7 @@ function AgentsPage() {
|
||||||
description="Quản lý và theo dõi các phiên bản Agent"
|
description="Quản lý và theo dõi các phiên bản Agent"
|
||||||
data={versionList}
|
data={versionList}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
columns={columns}
|
columns={agentColumns}
|
||||||
onUpload={handleUpload}
|
onUpload={handleUpload}
|
||||||
onUpdate={handleUpdate}
|
onUpdate={handleUpdate}
|
||||||
updateLoading={updateMutation.isPending}
|
updateLoading={updateMutation.isPending}
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,10 @@ import {
|
||||||
useDownloadFiles,
|
useDownloadFiles,
|
||||||
} from "@/hooks/queries";
|
} from "@/hooks/queries";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import type { ColumnDef } from "@tanstack/react-table";
|
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
import type { Version } from "@/types/file";
|
import type { Version } from "@/types/file";
|
||||||
import { Check, X } from "lucide-react";
|
import { useMemo, useState } from "react";
|
||||||
import { useState } from "react";
|
import { createAppsColumns } from "@/components/columns/apps-column";
|
||||||
|
|
||||||
export const Route = createFileRoute("/_auth/apps/")({
|
export const Route = createFileRoute("/_auth/apps/")({
|
||||||
head: () => ({ meta: [{ title: "Quản lý phần mềm" }] }),
|
head: () => ({ meta: [{ title: "Quản lý phần mềm" }] }),
|
||||||
component: AppsComponent,
|
component: AppsComponent,
|
||||||
|
|
@ -51,62 +49,10 @@ function AppsComponent() {
|
||||||
|
|
||||||
const deleteRequiredFileMutation = useDeleteRequiredFile();
|
const deleteRequiredFileMutation = useDeleteRequiredFile();
|
||||||
|
|
||||||
// Cột bảng
|
const columns = useMemo(
|
||||||
const columns: ColumnDef<Version>[] = [
|
() => createAppsColumns(installMutation.isPending),
|
||||||
{ accessorKey: "version", header: "Phiên bản" },
|
[installMutation.isPending]
|
||||||
{ accessorKey: "fileName", header: "Tên file" },
|
|
||||||
{
|
|
||||||
accessorKey: "updatedAt",
|
|
||||||
header: () => <div className="whitespace-normal max-w-xs">Thời gian cập nhật</div>,
|
|
||||||
cell: ({ getValue }) =>
|
|
||||||
getValue()
|
|
||||||
? new Date(getValue() as string).toLocaleString("vi-VN")
|
|
||||||
: "N/A",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
accessorKey: "requestUpdateAt",
|
|
||||||
header: () => <div className="whitespace-normal max-w-xs">Thời gian yêu cầu cài đặt/tải xuống</div>,
|
|
||||||
cell: ({ getValue }) =>
|
|
||||||
getValue()
|
|
||||||
? new Date(getValue() as string).toLocaleString("vi-VN")
|
|
||||||
: "N/A",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "required",
|
|
||||||
header: () => <div className="whitespace-normal max-w-xs">Đã thêm vào danh sách</div>,
|
|
||||||
cell: ({ row }) => {
|
|
||||||
const isRequired = row.original.isRequired;
|
|
||||||
return isRequired ? (
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<Check className="h-4 w-4 text-green-600" />
|
|
||||||
<span className="text-sm text-green-600">Có</span>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="flex items-center gap-1">
|
|
||||||
<X className="h-4 w-4 text-gray-400" />
|
|
||||||
<span className="text-sm text-gray-400">Không</span>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
},
|
|
||||||
enableSorting: false,
|
|
||||||
enableHiding: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "select",
|
|
||||||
header: () => <div className="whitespace-normal max-w-xs">Chọn</div>,
|
|
||||||
cell: ({ row }) => (
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
checked={row.getIsSelected?.() ?? false}
|
|
||||||
onChange={row.getToggleSelectedHandler?.()}
|
|
||||||
disabled={installMutation.isPending}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
enableSorting: false,
|
|
||||||
enableHiding: false,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// Upload file MSI
|
// Upload file MSI
|
||||||
const handleUpload = async (
|
const handleUpload = async (
|
||||||
fd: FormData,
|
fd: FormData,
|
||||||
|
|
|
||||||
92
src/routes/_auth/audits/index.tsx
Normal file
92
src/routes/_auth/audits/index.tsx
Normal file
|
|
@ -0,0 +1,92 @@
|
||||||
|
import { createFileRoute } from "@tanstack/react-router";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { useGetAudits } from "@/hooks/queries";
|
||||||
|
import type { Audits } from "@/types/audit";
|
||||||
|
import { AuditListTemplate } from "@/template/audit-list-template";
|
||||||
|
import { auditColumns } from "@/components/columns/audit-column";
|
||||||
|
|
||||||
|
export const Route = createFileRoute("/_auth/audits/")({
|
||||||
|
head: () => ({ meta: [{ title: "Audit Logs" }] }),
|
||||||
|
loader: async ({ context }) => {
|
||||||
|
context.breadcrumbs = [{ title: "Audit logs", path: "#" }];
|
||||||
|
},
|
||||||
|
component: AuditsPage,
|
||||||
|
});
|
||||||
|
|
||||||
|
function AuditsPage() {
|
||||||
|
const [pageNumber, setPageNumber] = useState(1);
|
||||||
|
const [pageSize] = useState(20);
|
||||||
|
const [username, setUsername] = useState<string | null>(null);
|
||||||
|
const [action, setAction] = useState<string | null>(null);
|
||||||
|
const [from, setFrom] = useState<string | null>(null);
|
||||||
|
const [to, setTo] = useState<string | null>(null);
|
||||||
|
const [selectedAudit, setSelectedAudit] = useState<Audits | null>(null);
|
||||||
|
|
||||||
|
const { data, isLoading, refetch, isFetching } = useGetAudits(
|
||||||
|
{
|
||||||
|
pageNumber,
|
||||||
|
pageSize,
|
||||||
|
username,
|
||||||
|
action,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
},
|
||||||
|
true
|
||||||
|
) as any;
|
||||||
|
|
||||||
|
const items: Audits[] = data?.items ?? [];
|
||||||
|
const total: number = data?.totalCount ?? 0;
|
||||||
|
const pageCount = Math.max(1, Math.ceil(total / pageSize));
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
refetch();
|
||||||
|
}, [pageNumber, pageSize]);
|
||||||
|
|
||||||
|
const handleSearch = () => {
|
||||||
|
setPageNumber(1);
|
||||||
|
refetch();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleReset = () => {
|
||||||
|
setUsername(null);
|
||||||
|
setAction(null);
|
||||||
|
setFrom(null);
|
||||||
|
setTo(null);
|
||||||
|
setPageNumber(1);
|
||||||
|
refetch();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuditListTemplate
|
||||||
|
// data
|
||||||
|
items={items}
|
||||||
|
total={total}
|
||||||
|
columns={auditColumns}
|
||||||
|
isLoading={isLoading}
|
||||||
|
isFetching={isFetching}
|
||||||
|
// pagination
|
||||||
|
pageNumber={pageNumber}
|
||||||
|
pageSize={pageSize}
|
||||||
|
pageCount={pageCount}
|
||||||
|
canPreviousPage={pageNumber > 1}
|
||||||
|
canNextPage={pageNumber < pageCount}
|
||||||
|
onPreviousPage={() => setPageNumber((p) => Math.max(1, p - 1))}
|
||||||
|
onNextPage={() => setPageNumber((p) => Math.min(pageCount, p + 1))}
|
||||||
|
// filter
|
||||||
|
username={username}
|
||||||
|
action={action}
|
||||||
|
from={from}
|
||||||
|
to={to}
|
||||||
|
onUsernameChange={setUsername}
|
||||||
|
onActionChange={setAction}
|
||||||
|
onFromChange={setFrom}
|
||||||
|
onToChange={setTo}
|
||||||
|
onSearch={handleSearch}
|
||||||
|
onReset={handleReset}
|
||||||
|
// detail dialog
|
||||||
|
selectedAudit={selectedAudit}
|
||||||
|
onRowClick={setSelectedAudit}
|
||||||
|
onDialogClose={() => setSelectedAudit(null)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -11,7 +11,6 @@ import {
|
||||||
useSendCommand,
|
useSendCommand,
|
||||||
} from "@/hooks/queries";
|
} from "@/hooks/queries";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
|
|
||||||
import { Check, X, Edit2, Trash2 } from "lucide-react";
|
import { Check, X, Edit2, Trash2 } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import type { ColumnDef } from "@tanstack/react-table";
|
import type { ColumnDef } from "@tanstack/react-table";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
import { createFileRoute } from '@tanstack/react-router'
|
||||||
|
|
||||||
|
export const Route = createFileRoute('/_auth/rooms/$roomName/connect/')({
|
||||||
|
component: RouteComponent,
|
||||||
|
})
|
||||||
|
|
||||||
|
function RouteComponent() {
|
||||||
|
return <div>Hello "/_auth/rooms/$roomName/connect/"!</div>
|
||||||
|
}
|
||||||
19
src/services/audit.service.ts
Normal file
19
src/services/audit.service.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import axios from "@/config/axios";
|
||||||
|
import { API_ENDPOINTS } from "@/config/api";
|
||||||
|
import type { PageResult, Audits } from "@/types/audit";
|
||||||
|
|
||||||
|
export async function getAudits(
|
||||||
|
pageNumber = 1,
|
||||||
|
pageSize = 20,
|
||||||
|
username?: string | null,
|
||||||
|
action?: string | null,
|
||||||
|
from?: string | null,
|
||||||
|
to?: string | null
|
||||||
|
): Promise<PageResult<Audits>> {
|
||||||
|
const response = await axios.get<PageResult<Audits>>(API_ENDPOINTS.AUDIT.GET_AUDITS, {
|
||||||
|
params: { pageNumber, pageSize, username, action, from, to },
|
||||||
|
});
|
||||||
|
|
||||||
|
// API trả về camelCase khớp với PageResult<Audits> — dùng trực tiếp, không cần map
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
242
src/template/audit-list-template.tsx
Normal file
242
src/template/audit-list-template.tsx
Normal file
|
|
@ -0,0 +1,242 @@
|
||||||
|
import {
|
||||||
|
type ColumnDef,
|
||||||
|
flexRender,
|
||||||
|
getCoreRowModel,
|
||||||
|
useReactTable,
|
||||||
|
} from "@tanstack/react-table";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableBody,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
TableCell,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
CardDescription,
|
||||||
|
CardContent,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { type Audits } from "@/types/audit";
|
||||||
|
import { AuditFilterBar } from "@/components/filters/audit-filter-bar";
|
||||||
|
import { AuditDetailDialog } from "@/components/dialogs/audit-detail-dialog";
|
||||||
|
|
||||||
|
interface AuditListTemplateProps {
|
||||||
|
// data
|
||||||
|
items: Audits[];
|
||||||
|
total: number;
|
||||||
|
columns: ColumnDef<Audits>[];
|
||||||
|
isLoading: boolean;
|
||||||
|
isFetching: boolean;
|
||||||
|
|
||||||
|
// pagination
|
||||||
|
pageNumber: number;
|
||||||
|
pageSize: number;
|
||||||
|
pageCount: number;
|
||||||
|
onPreviousPage: () => void;
|
||||||
|
onNextPage: () => void;
|
||||||
|
canPreviousPage: boolean;
|
||||||
|
canNextPage: boolean;
|
||||||
|
|
||||||
|
// filter
|
||||||
|
username: string | null;
|
||||||
|
action: string | null;
|
||||||
|
from: string | null;
|
||||||
|
to: string | null;
|
||||||
|
onUsernameChange: (v: string | null) => void;
|
||||||
|
onActionChange: (v: string | null) => void;
|
||||||
|
onFromChange: (v: string | null) => void;
|
||||||
|
onToChange: (v: string | null) => void;
|
||||||
|
onSearch: () => void;
|
||||||
|
onReset: () => void;
|
||||||
|
|
||||||
|
// detail dialog
|
||||||
|
selectedAudit: Audits | null;
|
||||||
|
onRowClick: (audit: Audits) => void;
|
||||||
|
onDialogClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AuditListTemplate({
|
||||||
|
items,
|
||||||
|
total,
|
||||||
|
columns,
|
||||||
|
isLoading,
|
||||||
|
isFetching,
|
||||||
|
pageNumber,
|
||||||
|
pageSize,
|
||||||
|
pageCount,
|
||||||
|
onPreviousPage,
|
||||||
|
onNextPage,
|
||||||
|
canPreviousPage,
|
||||||
|
canNextPage,
|
||||||
|
username,
|
||||||
|
action,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
onUsernameChange,
|
||||||
|
onActionChange,
|
||||||
|
onFromChange,
|
||||||
|
onToChange,
|
||||||
|
onSearch,
|
||||||
|
onReset,
|
||||||
|
selectedAudit,
|
||||||
|
onRowClick,
|
||||||
|
onDialogClose,
|
||||||
|
}: AuditListTemplateProps) {
|
||||||
|
const table = useReactTable({
|
||||||
|
data: items,
|
||||||
|
columns,
|
||||||
|
state: {
|
||||||
|
pagination: { pageIndex: Math.max(0, pageNumber - 1), pageSize },
|
||||||
|
},
|
||||||
|
pageCount,
|
||||||
|
manualPagination: true,
|
||||||
|
onPaginationChange: (updater) => {
|
||||||
|
const next =
|
||||||
|
typeof updater === "function"
|
||||||
|
? updater({ pageIndex: Math.max(0, pageNumber - 1), pageSize })
|
||||||
|
: updater;
|
||||||
|
const newPage = (next.pageIndex ?? 0) + 1;
|
||||||
|
if (newPage > pageNumber) onNextPage();
|
||||||
|
else if (newPage < pageNumber) onPreviousPage();
|
||||||
|
},
|
||||||
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full px-4 md:px-6 space-y-4">
|
||||||
|
<div>
|
||||||
|
<h1 className="text-2xl md:text-3xl font-bold">Nhật ký hoạt động</h1>
|
||||||
|
<p className="text-muted-foreground mt-1 text-sm">
|
||||||
|
Xem nhật ký audit hệ thống
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="pb-3">
|
||||||
|
<CardTitle className="text-base">Danh sách audit</CardTitle>
|
||||||
|
<CardDescription className="text-xs">
|
||||||
|
Lọc theo người dùng, loại, hành động và khoảng thời gian. Nhấn vào
|
||||||
|
dòng để xem chi tiết.
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent>
|
||||||
|
<AuditFilterBar
|
||||||
|
username={username}
|
||||||
|
action={action}
|
||||||
|
from={from}
|
||||||
|
to={to}
|
||||||
|
isLoading={isLoading}
|
||||||
|
isFetching={isFetching}
|
||||||
|
onUsernameChange={onUsernameChange}
|
||||||
|
onActionChange={onActionChange}
|
||||||
|
onFromChange={onFromChange}
|
||||||
|
onToChange={onToChange}
|
||||||
|
onSearch={onSearch}
|
||||||
|
onReset={onReset}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="rounded-md border overflow-x-auto">
|
||||||
|
<Table className="min-w-[640px] w-full">
|
||||||
|
<TableHeader className="sticky top-0 bg-background z-10">
|
||||||
|
{table.getHeaderGroups().map((hg) => (
|
||||||
|
<TableRow key={hg.id}>
|
||||||
|
{hg.headers.map((header) => (
|
||||||
|
<TableHead
|
||||||
|
key={header.id}
|
||||||
|
className="text-xs font-semibold whitespace-nowrap"
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
header.column.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
|
||||||
|
<TableBody>
|
||||||
|
{isLoading || isFetching ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="text-center py-10 text-muted-foreground text-sm"
|
||||||
|
>
|
||||||
|
Đang tải...
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : table.getRowModel().rows.length === 0 ? (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="text-center py-10 text-muted-foreground text-sm"
|
||||||
|
>
|
||||||
|
Không có dữ liệu
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
) : (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
className="hover:bg-muted/40 cursor-pointer"
|
||||||
|
onClick={() => onRowClick(row.original)}
|
||||||
|
>
|
||||||
|
{row.getVisibleCells().map((cell) => (
|
||||||
|
<TableCell key={cell.id} className="py-2.5 align-middle">
|
||||||
|
{cell.column.columnDef.cell
|
||||||
|
? flexRender(
|
||||||
|
cell.column.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
|
)
|
||||||
|
: String(cell.getValue() ?? "")}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between mt-4">
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
Hiển thị {items.length} / {total} mục
|
||||||
|
</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
disabled={!canPreviousPage || isFetching}
|
||||||
|
onClick={onPreviousPage}
|
||||||
|
>
|
||||||
|
Trước
|
||||||
|
</Button>
|
||||||
|
<span className="text-sm tabular-nums">
|
||||||
|
{pageNumber} / {pageCount}
|
||||||
|
</span>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
disabled={!canNextPage || isFetching}
|
||||||
|
onClick={onNextPage}
|
||||||
|
>
|
||||||
|
Sau
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<AuditDetailDialog
|
||||||
|
audit={selectedAudit}
|
||||||
|
open={!!selectedAudit}
|
||||||
|
onClose={onDialogClose}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { AppWindow, Building, CircleX, Folder, Home, ShieldCheck, Terminal, UserPlus} from "lucide-react";
|
import { AppWindow, Building, CircleX, ClipboardList, Folder, Home, ShieldCheck, Terminal, UserPlus} from "lucide-react";
|
||||||
import { PermissionEnum } from "./permission";
|
import { PermissionEnum } from "./permission";
|
||||||
|
|
||||||
enum AppSidebarSectionCode {
|
enum AppSidebarSectionCode {
|
||||||
|
|
@ -93,6 +93,17 @@ export const appSidebarSection = {
|
||||||
permissions: [PermissionEnum.VIEW_USER],
|
permissions: [PermissionEnum.VIEW_USER],
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Audits",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: "Lịch sử hoạt động",
|
||||||
|
url: "/audits",
|
||||||
|
icon: ClipboardList,
|
||||||
|
permissions: [PermissionEnum.VIEW_AUDIT_LOGS],
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
||||||
29
src/types/audit.ts
Normal file
29
src/types/audit.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
export interface Audits {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
dateTime: string; // ISO string
|
||||||
|
|
||||||
|
// request identity
|
||||||
|
apiCall?: string; // Controller.ActionName
|
||||||
|
url?: string;
|
||||||
|
requestPayload?: string; // request body (redacted)
|
||||||
|
|
||||||
|
// DB fields — null if request didn't touch DB
|
||||||
|
action?: string;
|
||||||
|
tableName?: string;
|
||||||
|
entityId?: string;
|
||||||
|
oldValues?: string;
|
||||||
|
newValues?: string;
|
||||||
|
|
||||||
|
// result
|
||||||
|
isSuccess?: boolean;
|
||||||
|
errorMessage?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PageResult<T> {
|
||||||
|
items: T[];
|
||||||
|
totalCount: number;
|
||||||
|
pageNumber: number;
|
||||||
|
pageSize: number;
|
||||||
|
totalPages: number;
|
||||||
|
}
|
||||||
|
|
@ -44,6 +44,7 @@ export enum PermissionEnum {
|
||||||
EDIT_COMMAND = 53,
|
EDIT_COMMAND = 53,
|
||||||
DEL_COMMAND = 54,
|
DEL_COMMAND = 54,
|
||||||
SEND_COMMAND = 55,
|
SEND_COMMAND = 55,
|
||||||
|
SEND_SENSITIVE_COMMAND = 56,
|
||||||
|
|
||||||
//DEVICE_OPERATION
|
//DEVICE_OPERATION
|
||||||
DEVICE_OPERATION = 70,
|
DEVICE_OPERATION = 70,
|
||||||
|
|
@ -59,10 +60,12 @@ export enum PermissionEnum {
|
||||||
VIEW_ACCOUNT_ROOM = 115,
|
VIEW_ACCOUNT_ROOM = 115,
|
||||||
EDIT_ACCOUNT_ROOM = 116,
|
EDIT_ACCOUNT_ROOM = 116,
|
||||||
|
|
||||||
|
|
||||||
//WARNING_OPERATION
|
//WARNING_OPERATION
|
||||||
WARNING_OPERATION = 140,
|
WARNING_OPERATION = 140,
|
||||||
VIEW_WARNING = 141,
|
VIEW_WARNING = 141,
|
||||||
|
|
||||||
|
|
||||||
//USER_OPERATION
|
//USER_OPERATION
|
||||||
USER_OPERATION = 150,
|
USER_OPERATION = 150,
|
||||||
VIEW_USER_ROLE = 151,
|
VIEW_USER_ROLE = 151,
|
||||||
|
|
@ -80,7 +83,7 @@ export enum PermissionEnum {
|
||||||
DEL_ROLE = 164,
|
DEL_ROLE = 164,
|
||||||
|
|
||||||
// AGENT
|
// AGENT
|
||||||
APP_OPERATION = 170,
|
AGENT_OPERATION = 170,
|
||||||
VIEW_AGENT = 171,
|
VIEW_AGENT = 171,
|
||||||
UPDATE_AGENT = 173,
|
UPDATE_AGENT = 173,
|
||||||
SEND_UPDATE_COMMAND = 174,
|
SEND_UPDATE_COMMAND = 174,
|
||||||
|
|
@ -94,9 +97,13 @@ export enum PermissionEnum {
|
||||||
ADD_APP_TO_SELECTED = 185,
|
ADD_APP_TO_SELECTED = 185,
|
||||||
DEL_APP_FROM_SELECTED = 186,
|
DEL_APP_FROM_SELECTED = 186,
|
||||||
|
|
||||||
|
// AUDIT
|
||||||
|
AUDIT_OPERATION = 190,
|
||||||
|
VIEW_AUDIT_LOGS = 191,
|
||||||
|
|
||||||
//Undefined
|
//Undefined
|
||||||
UNDEFINED = 9999,
|
UNDEFINED = 9999,
|
||||||
|
|
||||||
//Allow All
|
//Allow All
|
||||||
ALLOW_ALL = 0,
|
ALLOW_ALL = 0
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user