diff --git a/src/widgets/DevicesTable/index.tsx b/src/widgets/DevicesTable/index.tsx index 386b513..bbc40c9 100644 --- a/src/widgets/DevicesTable/index.tsx +++ b/src/widgets/DevicesTable/index.tsx @@ -15,7 +15,7 @@ import { } from "@shared"; import { useEffect, useState } from "react"; import { observer } from "mobx-react-lite"; -import { Button, Checkbox } from "@mui/material"; +import { Button, Checkbox, Typography } from "@mui/material"; // Import Typography for the modal message const formatDate = (dateString: string | undefined) => { if (!dateString) return "Нет данных"; @@ -48,21 +48,16 @@ function createData( return { uuid, online, lastUpdate, gps, media, connection }; } +// Keep the rows function as you provided it, without additional filters const rows = (devices: any[], vehicles: any[]) => { - return devices.map((device) => { - const { device_status } = vehicles.find( - (v) => v?.device_status?.device_uuid === device - ); - const findVehicle = vehicles.find((v) => v?.vehicle?.uuid === device); - - console.log(findVehicle); + return vehicles.map((vehicle) => { return createData( - findVehicle?.vehicle?.tail_number ?? "1243000", - device_status?.online, - device_status?.last_update, - device_status?.gps_ok, - device_status?.media_service_ok, - device_status?.is_connected + vehicle?.vehicle?.tail_number ?? "1243000", // Using tail_number as UUID, as in your original code + vehicle?.device_status?.online ?? false, + vehicle?.device_status?.last_update, + vehicle?.device_status?.gps_ok, + vehicle?.device_status?.media_service_ok, + vehicle?.device_status?.is_connected ); }); }; @@ -71,14 +66,18 @@ export const DevicesTable = observer(() => { const { devices, getDevices, - uuid, - setSelectedDevice, + // uuid, // This 'uuid' from devicesStore refers to a *single* selected device, not for batch actions. + setSelectedDevice, // Useful for individual device actions like 'Reload Status' sendSnapshotModalOpen, toggleSendSnapshotModal, } = devicesStore; const { snapshots, getSnapshots } = snapshotStore; const { vehicles, getVehicles } = vehicleStore; const [selectedDevices, setSelectedDevices] = useState<string[]>([]); + + // Get the current list of rows displayed in the table + const currentRows = rows(devices, vehicles); + useEffect(() => { const fetchData = async () => { await getVehicles(); @@ -88,55 +87,98 @@ export const DevicesTable = observer(() => { fetchData(); }, []); - const handleSendSnapshot = (uuid: string) => { - setSelectedDevice(uuid); - toggleSendSnapshotModal(); - }; + // Determine if all visible devices are selected + const isAllSelected = + currentRows.length > 0 && selectedDevices.length === currentRows.length; - const handleReloadStatus = async (uuid: string) => { - setSelectedDevice(uuid); - await authInstance.post(`/devices/${uuid}/request-status`); - await getDevices(); + const handleSelectAllDevices = () => { + if (isAllSelected) { + // If all are currently selected, deselect all + setSelectedDevices([]); + } else { + // Otherwise, select all device UUIDs from the current rows + setSelectedDevices(currentRows.map((row) => row.uuid)); + } }; const handleSelectDevice = (event: React.ChangeEvent<HTMLInputElement>) => { + const deviceUuid = event.target.value; if (event.target.checked) { - setSelectedDevices([...selectedDevices, event.target.value]); + setSelectedDevices((prevSelected) => [...prevSelected, deviceUuid]); } else { - setSelectedDevices( - selectedDevices.filter((device) => device !== event.target.value) + setSelectedDevices((prevSelected) => + prevSelected.filter((uuid) => uuid !== deviceUuid) ); } }; - const handleSendSnapshotAction = async (uuid: string, snapshotId: string) => { - await authInstance.post(`/devices/${uuid}/force-snapshot`, { - snapshot_id: snapshotId, - }); - await getDevices(); + // This function now opens the modal for selected devices + const handleOpenSendSnapshotModal = () => { + if (selectedDevices.length > 0) { + toggleSendSnapshotModal(); + } + }; + + const handleReloadStatus = async (uuid: string) => { + setSelectedDevice(uuid); // Set the active device in store for context if needed + await authInstance.post(`/devices/${uuid}/request-status`); + await getDevices(); // Refresh devices after status request + }; + + // This function now handles sending snapshots to ALL selected devices + const handleSendSnapshotAction = async (snapshotId: string) => { + try { + for (const deviceUuid of selectedDevices) { + console.log(`Sending snapshot ${snapshotId} to device ${deviceUuid}`); + // Ensure you are using the correct API endpoint for force-snapshot + await authInstance.post(`/devices/${deviceUuid}/force-snapshot`, { + snapshot_id: snapshotId, + }); + } + // After all requests are sent + await getDevices(); // Refresh the device list to show updated status + setSelectedDevices([]); // Clear the selection + toggleSendSnapshotModal(); // Close the modal + } catch (error) { + console.error("Error sending snapshots:", error); + // You might want to show an error notification to the user here + } }; return ( <> <TableContainer component={Paper}> - <div className="flex justify-end p-3 gap-5"> - <Button variant="contained" color="primary"> - Выбрать все + <div className="flex justify-end p-3 gap-3"> + {" "} + {/* Changed gap to 3 for slightly less space */} + <Button + variant="contained" + color="primary" + onClick={handleSelectAllDevices} + > + {isAllSelected ? "Снять выбор со всех" : "Выбрать все"} </Button> <Button variant="contained" color="primary" disabled={selectedDevices.length === 0} - className="ml-auto" - onClick={() => handleSendSnapshot(uuid ?? "")} + onClick={handleOpenSendSnapshotModal} // Call the new handler > - Отправить снапшот + Отправить снапшот ({selectedDevices.length}) </Button> </div> <Table sx={{ minWidth: 650 }} aria-label="simple table"> <TableHead> <TableRow> - <TableCell align="center"></TableCell> + <TableCell align="center" padding="checkbox"> + {" "} + {/* Added padding="checkbox" */} + <Checkbox + checked={isAllSelected} + onChange={handleSelectAllDevices} + inputProps={{ "aria-label": "select all devices" }} + /> + </TableCell> <TableCell align="center">Бортовой номер</TableCell> <TableCell align="center">Онлайн</TableCell> <TableCell align="center">Последнее обновление</TableCell> @@ -147,56 +189,58 @@ export const DevicesTable = observer(() => { </TableRow> </TableHead> <TableBody> - {rows(devices, vehicles).map((row) => ( + {currentRows.map((row) => ( <TableRow - key={row?.uuid} + key={row.uuid} // Use row.uuid as key for consistent rendering sx={{ "&:last-child td, &:last-child th": { border: 0 } }} - className="flex items-center" > - <TableCell align="center"> + <TableCell align="center" padding="checkbox"> + {" "} + {/* Added padding="checkbox" */} <Checkbox className="h-full" onChange={handleSelectDevice} - value={row?.uuid} + value={row.uuid} + checked={selectedDevices.includes(row.uuid)} // THIS IS THE KEY CHANGE /> </TableCell> <TableCell align="center" component="th" scope="row"> - {row?.uuid} + {row.uuid} </TableCell> <TableCell align="center"> - {row?.online ? ( + {row.online ? ( <Check className="m-auto text-green-500" /> ) : ( <X className="m-auto text-red-500" /> )} </TableCell> <TableCell align="center"> - {formatDate(row?.lastUpdate)} + {formatDate(row.lastUpdate)} </TableCell> <TableCell align="center"> - {row?.gps ? ( + {row.gps ? ( <Check className="m-auto text-green-500" /> ) : ( <X className="m-auto text-red-500" /> )} </TableCell> <TableCell align="center"> - {row?.media ? ( + {row.media ? ( <Check className="m-auto text-green-500" /> ) : ( <X className="m-auto text-red-500" /> )} </TableCell> <TableCell align="center"> - {row?.connection ? ( + {row.connection ? ( <Check className="m-auto text-green-500" /> ) : ( <X className="m-auto text-red-500" /> )} </TableCell> - <TableCell align="center" className="flex justify-center"> - <button onClick={() => handleReloadStatus(row?.uuid ?? "")}> + <TableCell align="center"> + <button onClick={() => handleReloadStatus(row.uuid)}> <RotateCcw className="m-auto" /> </button> </TableCell> @@ -206,18 +250,35 @@ export const DevicesTable = observer(() => { </Table> </TableContainer> <Modal open={sendSnapshotModalOpen} onClose={toggleSendSnapshotModal}> - <p>Выбрать снапшот</p> + <Typography variant="h6" component="p" sx={{ mb: 2 }}> + Выбрать снапшот для{" "} + <strong className="text-blue-600">{selectedDevices.length}</strong>{" "} + устройств + </Typography> <div className="mt-5 flex flex-col gap-2 max-h-[300px] overflow-y-auto"> - {snapshots && + {snapshots && snapshots.length > 0 ? ( snapshots.map((snapshot) => ( - <button - onClick={() => handleSendSnapshotAction(uuid!, snapshot.ID)} - className="p-2 rounded-xl bg-slate-100" + <Button + variant="outlined" + onClick={() => handleSendSnapshotAction(snapshot.ID)} + sx={{ + p: 1.5, // Adjust padding + borderRadius: 2, // Adjust border radius + backgroundColor: "white", // Ensure background is white + "&:hover": { + backgroundColor: "grey.100", // Light hover effect + }, + }} key={snapshot.ID} > {snapshot.Name} - </button> - ))} + </Button> + )) + ) : ( + <Typography variant="body2" color="textSecondary"> + Нет доступных снапшотов. + </Typography> + )} </div> </Modal> </>