dynamic device grid render
This commit is contained in:
parent
5e53c17de9
commit
67ea288de6
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -8,3 +8,4 @@ count.txt
|
||||||
.nitro
|
.nitro
|
||||||
.tanstack
|
.tanstack
|
||||||
.vscode/
|
.vscode/
|
||||||
|
plans/
|
||||||
|
|
@ -7,68 +7,107 @@ export function DeviceGrid({
|
||||||
devices,
|
devices,
|
||||||
folderStatuses,
|
folderStatuses,
|
||||||
isCheckingFolder,
|
isCheckingFolder,
|
||||||
|
totalSeats,
|
||||||
}: {
|
}: {
|
||||||
devices: any[];
|
devices: any[];
|
||||||
folderStatuses?: Map<string, ClientFolderStatus>;
|
folderStatuses?: Map<string, ClientFolderStatus>;
|
||||||
isCheckingFolder?: boolean;
|
isCheckingFolder?: boolean;
|
||||||
|
totalSeats?: number;
|
||||||
}) {
|
}) {
|
||||||
const getMachineNumber = useMachineNumber();
|
const getMachineNumber = useMachineNumber();
|
||||||
const deviceMap = new Map<number, any>();
|
const deviceMap = new Map<number, any>();
|
||||||
|
|
||||||
|
let maxMachineNumber = 0;
|
||||||
devices.forEach((device) => {
|
devices.forEach((device) => {
|
||||||
const number = getMachineNumber(device.id || "");
|
const number = getMachineNumber(device.id || "");
|
||||||
if (number > 0 && number <= 40) deviceMap.set(number, device);
|
if (number > maxMachineNumber) maxMachineNumber = number;
|
||||||
});
|
});
|
||||||
|
|
||||||
const totalRows = 5;
|
const seatCount =
|
||||||
|
typeof totalSeats === "number" && totalSeats > 0 ? totalSeats : maxMachineNumber;
|
||||||
|
|
||||||
const renderRow = (rowIndex: number) => {
|
devices.forEach((device) => {
|
||||||
// Đảo ngược: 21-40 sang trái, 1-20 sang phải
|
const number = getMachineNumber(device.id || "");
|
||||||
const leftStart = 21 + (totalRows - 1 - rowIndex) * 4;
|
if (number > 0 && number <= seatCount) deviceMap.set(number, device);
|
||||||
const rightStart = (totalRows - 1 - rowIndex) * 4 + 1;
|
});
|
||||||
|
|
||||||
return (
|
const columnsPerSide = 4;
|
||||||
<div key={rowIndex} className="flex items-center justify-center gap-3">
|
const rightCount = Math.ceil(seatCount / 2);
|
||||||
{/* Bên trái (21–40) */}
|
const leftCount = seatCount - rightCount;
|
||||||
{Array.from({ length: 4 }).map((_, i) => {
|
const totalRows = Math.max(
|
||||||
const pos = leftStart + (3 - i);
|
Math.ceil(rightCount / columnsPerSide),
|
||||||
const device = deviceMap.get(pos);
|
Math.ceil(leftCount / columnsPerSide)
|
||||||
|
);
|
||||||
|
|
||||||
|
const buildRowPositions = (side: "left" | "right", rowIndexFromBottom: number) => {
|
||||||
|
const start =
|
||||||
|
side === "right"
|
||||||
|
? rowIndexFromBottom * columnsPerSide + 1
|
||||||
|
: rightCount + 1 + rowIndexFromBottom * columnsPerSide;
|
||||||
|
const end =
|
||||||
|
side === "right"
|
||||||
|
? Math.min(start + columnsPerSide - 1, rightCount)
|
||||||
|
: Math.min(start + columnsPerSide - 1, seatCount);
|
||||||
|
|
||||||
|
const count = Math.max(0, end - start + 1);
|
||||||
|
const positions = Array<number | null>(columnsPerSide).fill(null);
|
||||||
|
|
||||||
|
if (count === 0) {
|
||||||
|
return positions;
|
||||||
|
}
|
||||||
|
|
||||||
|
const numbers: number[] = [];
|
||||||
|
for (let n = end; n >= start; n -= 1) {
|
||||||
|
numbers.push(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
const offset = side === "right" ? 0 : columnsPerSide - count;
|
||||||
|
numbers.forEach((n, index) => {
|
||||||
|
positions[offset + index] = n;
|
||||||
|
});
|
||||||
|
|
||||||
|
return positions;
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderSeat = (position: number | null, key: string) => {
|
||||||
|
if (!position) {
|
||||||
|
return <div key={key} className="w-24 h-24 shrink-0" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = deviceMap.get(position);
|
||||||
const macAddress = device?.networkInfos?.[0]?.macAddress || device?.id;
|
const macAddress = device?.networkInfos?.[0]?.macAddress || device?.id;
|
||||||
const folderStatus = folderStatuses?.get(macAddress);
|
const folderStatus = folderStatuses?.get(macAddress);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ComputerCard
|
<ComputerCard
|
||||||
key={pos}
|
key={key}
|
||||||
device={device}
|
device={device}
|
||||||
position={pos}
|
position={position}
|
||||||
folderStatus={folderStatus}
|
folderStatus={folderStatus}
|
||||||
isCheckingFolder={isCheckingFolder}
|
isCheckingFolder={isCheckingFolder}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
})}
|
};
|
||||||
|
|
||||||
|
const renderRow = (rowIndex: number) => {
|
||||||
|
const rowIndexFromBottom = totalRows - 1 - rowIndex;
|
||||||
|
const leftPositions = buildRowPositions("left", rowIndexFromBottom);
|
||||||
|
const rightPositions = buildRowPositions("right", rowIndexFromBottom);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div key={rowIndex} className="flex items-center justify-center gap-3">
|
||||||
|
{leftPositions.map((position, i) =>
|
||||||
|
renderSeat(position, `left-${rowIndex}-${i}`)
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Đường chia giữa */}
|
{/* Đường chia giữa */}
|
||||||
<div className="w-32 flex items-center justify-center">
|
<div className="w-32 flex items-center justify-center">
|
||||||
<div className="h-px w-full bg-border border-t-2 border-dashed" />
|
<div className="h-px w-full bg-border border-t-2 border-dashed" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Bên phải (1–20) */}
|
{rightPositions.map((position, i) =>
|
||||||
{Array.from({ length: 4 }).map((_, i) => {
|
renderSeat(position, `right-${rowIndex}-${i}`)
|
||||||
const pos = rightStart + (3 - i);
|
)}
|
||||||
const device = deviceMap.get(pos);
|
|
||||||
const macAddress = device?.networkInfos?.[0]?.macAddress || device?.id;
|
|
||||||
const folderStatus = folderStatuses?.get(macAddress);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ComputerCard
|
|
||||||
key={pos}
|
|
||||||
device={device}
|
|
||||||
position={pos}
|
|
||||||
folderStatus={folderStatus}
|
|
||||||
isCheckingFolder={isCheckingFolder}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import * as deviceCommService from "@/services/device-comm.service";
|
import * as deviceCommService from "@/services/device-comm.service";
|
||||||
import type { DeviceHealthCheck } from "@/types/device";
|
import type { DeviceHealthCheck } from "@/types/device";
|
||||||
import type { ClientFolderStatus } from "@/types/folder";
|
import type { ClientFolderStatus } from "@/types/folder";
|
||||||
|
import type { Room } from "@/types/room";
|
||||||
|
|
||||||
const DEVICE_COMM_QUERY_KEYS = {
|
const DEVICE_COMM_QUERY_KEYS = {
|
||||||
all: ["device-comm"] as const,
|
all: ["device-comm"] as const,
|
||||||
|
|
@ -29,7 +30,7 @@ export function useGetAllDevices(enabled = true) {
|
||||||
* Hook để lấy danh sách phòng
|
* Hook để lấy danh sách phòng
|
||||||
*/
|
*/
|
||||||
export function useGetRoomList(enabled = true) {
|
export function useGetRoomList(enabled = true) {
|
||||||
return useQuery({
|
return useQuery<Room[]>({
|
||||||
queryKey: DEVICE_COMM_QUERY_KEYS.roomList(),
|
queryKey: DEVICE_COMM_QUERY_KEYS.roomList(),
|
||||||
queryFn: () => deviceCommService.getRoomList(),
|
queryFn: () => deviceCommService.getRoomList(),
|
||||||
enabled,
|
enabled,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ import { useState } from "react";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { LayoutGrid, TableIcon, Monitor, FolderCheck } from "lucide-react";
|
import { LayoutGrid, TableIcon, Monitor, FolderCheck } from "lucide-react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { useGetDeviceFromRoom } from "@/hooks/queries";
|
import { useGetDeviceFromRoom, useGetRoomList } from "@/hooks/queries";
|
||||||
import { useDeviceEvents } from "@/hooks/useDeviceEvents";
|
import { useDeviceEvents } from "@/hooks/useDeviceEvents";
|
||||||
import { DeviceGrid } from "@/components/grids/device-grid";
|
import { DeviceGrid } from "@/components/grids/device-grid";
|
||||||
import { DeviceTable } from "@/components/tables/device-table";
|
import { DeviceTable } from "@/components/tables/device-table";
|
||||||
|
|
@ -32,6 +32,7 @@ function RoomDetailPage() {
|
||||||
|
|
||||||
// Folder status from SS
|
// Folder status from SS
|
||||||
const { data: devices = [] } = useGetDeviceFromRoom(roomName);
|
const { data: devices = [] } = useGetDeviceFromRoom(roomName);
|
||||||
|
const { data: roomData = [] } = useGetRoomList();
|
||||||
|
|
||||||
const parseMachineNumber = useMachineNumber();
|
const parseMachineNumber = useMachineNumber();
|
||||||
|
|
||||||
|
|
@ -41,6 +42,9 @@ function RoomDetailPage() {
|
||||||
return parseMachineNumber(a.id) - parseMachineNumber(b.id);
|
return parseMachineNumber(a.id) - parseMachineNumber(b.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const currentRoom = roomData.find((room) => room.name === roomName);
|
||||||
|
const totalSeats = currentRoom?.numberOfDevices;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full px-6 space-y-6">
|
<div className="w-full px-6 space-y-6">
|
||||||
<Card className="shadow-sm">
|
<Card className="shadow-sm">
|
||||||
|
|
@ -120,6 +124,7 @@ function RoomDetailPage() {
|
||||||
) : viewMode === "grid" ? (
|
) : viewMode === "grid" ? (
|
||||||
<DeviceGrid
|
<DeviceGrid
|
||||||
devices={sortedDevices}
|
devices={sortedDevices}
|
||||||
|
totalSeats={totalSeats}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<DeviceTable
|
<DeviceTable
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import { API_ENDPOINTS } from "@/config/api";
|
import { API_ENDPOINTS } from "@/config/api";
|
||||||
import type { DeviceHealthCheck } from "@/types/device";
|
import type { DeviceHealthCheck } from "@/types/device";
|
||||||
|
import type { Room } from "@/types/room";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lấy tất cả thiết bị trong hệ thống
|
* Lấy tất cả thiết bị trong hệ thống
|
||||||
|
|
@ -13,8 +14,8 @@ export async function getAllDevices(): Promise<any[]> {
|
||||||
/**
|
/**
|
||||||
* Lấy danh sách phòng
|
* Lấy danh sách phòng
|
||||||
*/
|
*/
|
||||||
export async function getRoomList(): Promise<any[]> {
|
export async function getRoomList(): Promise<Room[]> {
|
||||||
const response = await axios.get<any[]>(API_ENDPOINTS.DEVICE_COMM.GET_ROOM_LIST);
|
const response = await axios.get<Room[]>(API_ENDPOINTS.DEVICE_COMM.GET_ROOM_LIST);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user