295 lines
8.5 KiB
TypeScript
295 lines
8.5 KiB
TypeScript
import { createFileRoute } from "@tanstack/react-router";
|
|
import { AppManagerTemplate } from "@/template/app-manager-template";
|
|
import {
|
|
useGetSoftwareList,
|
|
useGetRoomList,
|
|
useUploadSoftware,
|
|
useDeleteFile,
|
|
useAddRequiredFile,
|
|
useDeleteRequiredFile,
|
|
useInstallMsi,
|
|
useDownloadFiles,
|
|
} from "@/hooks/queries";
|
|
import { toast } from "sonner";
|
|
import type { ColumnDef } from "@tanstack/react-table";
|
|
import type { AxiosProgressEvent } from "axios";
|
|
import type { Version } from "@/types/file";
|
|
import { Check, X } from "lucide-react";
|
|
import { useState } from "react";
|
|
|
|
export const Route = createFileRoute("/_auth/apps/")({
|
|
head: () => ({ meta: [{ title: "Quản lý phần mềm" }] }),
|
|
component: AppsComponent,
|
|
});
|
|
|
|
function AppsComponent() {
|
|
const { data, isLoading } = useGetSoftwareList();
|
|
const { data: roomData } = useGetRoomList();
|
|
|
|
const versionList: Version[] = Array.isArray(data)
|
|
? data
|
|
: data
|
|
? [data]
|
|
: [];
|
|
|
|
const [table, setTable] = useState<any>();
|
|
|
|
const uploadMutation = useUploadSoftware();
|
|
|
|
const installMutation = useInstallMsi();
|
|
|
|
const downloadMutation = useDownloadFiles();
|
|
|
|
const deleteMutation = useDeleteFile();
|
|
|
|
const addRequiredFileMutation = useAddRequiredFile();
|
|
|
|
const deleteRequiredFileMutation = useDeleteRequiredFile();
|
|
|
|
// Cột bảng
|
|
const columns: ColumnDef<Version>[] = [
|
|
{ accessorKey: "version", header: "Phiên bản" },
|
|
{ accessorKey: "fileName", header: "Tên file" },
|
|
{ accessorKey: "folderPath", header: "Đường dẫn" },
|
|
{
|
|
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
|
|
const handleUpload = async (
|
|
fd: FormData,
|
|
config?: { onUploadProgress?: (e: AxiosProgressEvent) => void }
|
|
) => {
|
|
try {
|
|
await uploadMutation.mutateAsync({
|
|
formData: fd,
|
|
onUploadProgress: config?.onUploadProgress,
|
|
});
|
|
toast.success("Upload thành công!");
|
|
} catch (error: any) {
|
|
console.error("Upload error:", error);
|
|
toast.error("Upload thất bại!");
|
|
}
|
|
};
|
|
|
|
// Callback khi chọn phòng
|
|
const handleInstall = async (roomNames: string[]) => {
|
|
if (!table) {
|
|
toast.error("Không thể lấy thông tin bảng!");
|
|
return;
|
|
}
|
|
|
|
const selectedRows = table.getSelectedRowModel().rows;
|
|
if (selectedRows.length === 0) {
|
|
toast.error("Vui lòng chọn ít nhất một file để cài đặt!");
|
|
return;
|
|
}
|
|
|
|
const MsiFileIds = selectedRows.map((row: any) => row.original.id);
|
|
|
|
try {
|
|
for (const roomName of roomNames) {
|
|
await installMutation.mutateAsync({
|
|
roomName,
|
|
data: { MsiFileIds },
|
|
});
|
|
}
|
|
toast.success("Đã gửi yêu cầu cài đặt phần mềm cho các phòng đã chọn!");
|
|
} catch (e) {
|
|
toast.error("Có lỗi xảy ra khi cài đặt!");
|
|
}
|
|
};
|
|
|
|
const handleDonwload = async (roomNames: string[]) => {
|
|
if (!table) {
|
|
toast.error("Không thể lấy thông tin bảng!");
|
|
return;
|
|
}
|
|
|
|
const selectedRows = table.getSelectedRowModel().rows;
|
|
if (selectedRows.length === 0) {
|
|
toast.error("Vui lòng chọn ít nhất một file để tải!");
|
|
return;
|
|
}
|
|
|
|
const MsiFileIds = selectedRows.map((row: any) => row.original.id);
|
|
|
|
try {
|
|
for (const roomName of roomNames) {
|
|
await downloadMutation.mutateAsync({
|
|
roomName,
|
|
data: { MsiFileIds },
|
|
});
|
|
}
|
|
toast.success("Đã gửi yêu cầu tải file cho các phòng đã chọn!");
|
|
} catch (e) {
|
|
toast.error("Có lỗi xảy ra khi tải!");
|
|
}
|
|
};
|
|
|
|
const handleDelete = async () => {
|
|
if (!table) {
|
|
toast.error("Không thể lấy thông tin bảng!");
|
|
return;
|
|
}
|
|
|
|
const selectedRows = table.getSelectedRowModel().rows;
|
|
if (selectedRows.length === 0) {
|
|
toast.error("Vui lòng chọn ít nhất một file để xóa!");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
for (const row of selectedRows) {
|
|
const { id } = row.original;
|
|
await deleteMutation.mutateAsync(id);
|
|
}
|
|
toast.success("Xóa phần mềm thành công!");
|
|
} catch (e) {
|
|
toast.error("Xóa phần mềm thất bại!");
|
|
}
|
|
};
|
|
|
|
const handleDeleteFromRequiredList = async () => {
|
|
if (!table) return;
|
|
|
|
const selectedRows = table.getSelectedRowModel().rows;
|
|
|
|
try {
|
|
for (const row of selectedRows) {
|
|
const { id } = row.original;
|
|
await deleteRequiredFileMutation.mutateAsync(id);
|
|
}
|
|
toast.success("Xóa file khỏi danh sách thành công!");
|
|
if (table) {
|
|
table.setRowSelection({});
|
|
}
|
|
} catch (e) {
|
|
console.error("Delete from required list error:", e);
|
|
toast.error("Có lỗi xảy ra khi xóa!");
|
|
}
|
|
};
|
|
|
|
const handleDeleteFromServer = async () => {
|
|
if (!table) return;
|
|
|
|
const selectedRows = table.getSelectedRowModel().rows;
|
|
|
|
try {
|
|
for (const row of selectedRows) {
|
|
const { id } = row.original;
|
|
await deleteMutation.mutateAsync(id);
|
|
}
|
|
toast.success("Xóa phần mềm từ server thành công!");
|
|
if (table) {
|
|
table.setRowSelection({});
|
|
}
|
|
} catch (e) {
|
|
console.error("Delete error:", e);
|
|
toast.error("Có lỗi xảy ra khi xóa!");
|
|
}
|
|
};
|
|
|
|
const handleAddToRequired = async () => {
|
|
if (!table) {
|
|
toast.error("Không thể lấy thông tin bảng!");
|
|
return;
|
|
}
|
|
|
|
const selectedRows = table.getSelectedRowModel().rows;
|
|
if (selectedRows.length === 0) {
|
|
toast.error("Vui lòng chọn ít nhất một file!");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
for (const row of selectedRows) {
|
|
const { fileName, version } = row.original;
|
|
await addRequiredFileMutation.mutateAsync({
|
|
fileName,
|
|
version,
|
|
});
|
|
}
|
|
toast.success("Thêm file vào danh sách thành công!");
|
|
table.setRowSelection({});
|
|
} catch (e) {
|
|
console.error("Add required file error:", e);
|
|
toast.error("Có lỗi xảy ra!");
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<AppManagerTemplate<Version>
|
|
title="Quản lý phần mềm"
|
|
uploadFormTitle="Tải lên || Cập nhật file phần mềm"
|
|
description="Quản lý và gửi yêu cầu cài đặt phần mềm hoặc file cấu hình"
|
|
data={versionList}
|
|
isLoading={isLoading}
|
|
columns={columns}
|
|
onUpload={handleUpload}
|
|
onUpdate={handleInstall}
|
|
onDownload={handleDonwload}
|
|
onDelete={handleDelete}
|
|
onDeleteFromServer={handleDeleteFromServer}
|
|
onDeleteFromRequired={handleDeleteFromRequiredList}
|
|
onAddToRequired={handleAddToRequired}
|
|
updateLoading={installMutation.isPending}
|
|
downloadLoading={downloadMutation.isPending}
|
|
deleteLoading={deleteMutation.isPending || deleteRequiredFileMutation.isPending}
|
|
addToRequiredLoading={addRequiredFileMutation.isPending}
|
|
onTableInit={setTable}
|
|
rooms={roomData}
|
|
/>
|
|
</>
|
|
);
|
|
}
|