fix: From device to vehicle table
This commit is contained in:
		| @@ -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> | ||||
|     </> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user