From 67ea288de6e3a71ba908c8600e28c812fc127a3b Mon Sep 17 00:00:00 2001 From: phuongdm Date: Tue, 12 May 2026 14:53:23 +0700 Subject: [PATCH] dynamic device grid render --- .gitignore | 1 + src/components/grids/device-grid.tsx | 117 ++++++++++++++------- src/hooks/queries/useDeviceCommQueries.ts | 3 +- src/routes/_auth/rooms/$roomName/index.tsx | 7 +- src/services/device-comm.service.ts | 5 +- 5 files changed, 90 insertions(+), 43 deletions(-) diff --git a/.gitignore b/.gitignore index d125dd8..120c8e6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ count.txt .nitro .tanstack .vscode/ +plans/ \ No newline at end of file diff --git a/src/components/grids/device-grid.tsx b/src/components/grids/device-grid.tsx index b0bcd89..01ccfcd 100644 --- a/src/components/grids/device-grid.tsx +++ b/src/components/grids/device-grid.tsx @@ -7,68 +7,107 @@ export function DeviceGrid({ devices, folderStatuses, isCheckingFolder, + totalSeats, }: { devices: any[]; folderStatuses?: Map; isCheckingFolder?: boolean; + totalSeats?: number; }) { const getMachineNumber = useMachineNumber(); const deviceMap = new Map(); + let maxMachineNumber = 0; devices.forEach((device) => { 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; + + devices.forEach((device) => { + const number = getMachineNumber(device.id || ""); + if (number > 0 && number <= seatCount) deviceMap.set(number, device); + }); + + const columnsPerSide = 4; + const rightCount = Math.ceil(seatCount / 2); + const leftCount = seatCount - rightCount; + const totalRows = Math.max( + Math.ceil(rightCount / columnsPerSide), + 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(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
; + } + + const device = deviceMap.get(position); + const macAddress = device?.networkInfos?.[0]?.macAddress || device?.id; + const folderStatus = folderStatuses?.get(macAddress); + + return ( + + ); + }; const renderRow = (rowIndex: number) => { - // Đảo ngược: 21-40 sang trái, 1-20 sang phải - const leftStart = 21 + (totalRows - 1 - rowIndex) * 4; - const rightStart = (totalRows - 1 - rowIndex) * 4 + 1; + const rowIndexFromBottom = totalRows - 1 - rowIndex; + const leftPositions = buildRowPositions("left", rowIndexFromBottom); + const rightPositions = buildRowPositions("right", rowIndexFromBottom); return (
- {/* Bên trái (21–40) */} - {Array.from({ length: 4 }).map((_, i) => { - const pos = leftStart + (3 - i); - const device = deviceMap.get(pos); - const macAddress = device?.networkInfos?.[0]?.macAddress || device?.id; - const folderStatus = folderStatuses?.get(macAddress); - - return ( - - ); - })} + {leftPositions.map((position, i) => + renderSeat(position, `left-${rowIndex}-${i}`) + )} {/* Đường chia giữa */}
- {/* Bên phải (1–20) */} - {Array.from({ length: 4 }).map((_, 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 ( - - ); - })} + {rightPositions.map((position, i) => + renderSeat(position, `right-${rowIndex}-${i}`) + )}
); }; diff --git a/src/hooks/queries/useDeviceCommQueries.ts b/src/hooks/queries/useDeviceCommQueries.ts index d4086d7..7227ea1 100644 --- a/src/hooks/queries/useDeviceCommQueries.ts +++ b/src/hooks/queries/useDeviceCommQueries.ts @@ -2,6 +2,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import * as deviceCommService from "@/services/device-comm.service"; import type { DeviceHealthCheck } from "@/types/device"; import type { ClientFolderStatus } from "@/types/folder"; +import type { Room } from "@/types/room"; const DEVICE_COMM_QUERY_KEYS = { all: ["device-comm"] as const, @@ -29,7 +30,7 @@ export function useGetAllDevices(enabled = true) { * Hook để lấy danh sách phòng */ export function useGetRoomList(enabled = true) { - return useQuery({ + return useQuery({ queryKey: DEVICE_COMM_QUERY_KEYS.roomList(), queryFn: () => deviceCommService.getRoomList(), enabled, diff --git a/src/routes/_auth/rooms/$roomName/index.tsx b/src/routes/_auth/rooms/$roomName/index.tsx index 738a90c..1c4fb99 100644 --- a/src/routes/_auth/rooms/$roomName/index.tsx +++ b/src/routes/_auth/rooms/$roomName/index.tsx @@ -3,7 +3,7 @@ import { useState } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { LayoutGrid, TableIcon, Monitor, FolderCheck } from "lucide-react"; import { Button } from "@/components/ui/button"; -import { useGetDeviceFromRoom } from "@/hooks/queries"; +import { useGetDeviceFromRoom, useGetRoomList } from "@/hooks/queries"; import { useDeviceEvents } from "@/hooks/useDeviceEvents"; import { DeviceGrid } from "@/components/grids/device-grid"; import { DeviceTable } from "@/components/tables/device-table"; @@ -32,6 +32,7 @@ function RoomDetailPage() { // Folder status from SS const { data: devices = [] } = useGetDeviceFromRoom(roomName); + const { data: roomData = [] } = useGetRoomList(); const parseMachineNumber = useMachineNumber(); @@ -41,6 +42,9 @@ function RoomDetailPage() { return parseMachineNumber(a.id) - parseMachineNumber(b.id); }); + const currentRoom = roomData.find((room) => room.name === roomName); + const totalSeats = currentRoom?.numberOfDevices; + return (
@@ -120,6 +124,7 @@ function RoomDetailPage() { ) : viewMode === "grid" ? ( ) : ( { /** * Lấy danh sách phòng */ -export async function getRoomList(): Promise { - const response = await axios.get(API_ENDPOINTS.DEVICE_COMM.GET_ROOM_LIST); +export async function getRoomList(): Promise { + const response = await axios.get(API_ENDPOINTS.DEVICE_COMM.GET_ROOM_LIST); return response.data; }