219 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			219 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
 | ||
| import { ruRU } from "@mui/x-data-grid/locales";
 | ||
| import { carrierStore, languageStore, vehicleStore } from "@shared";
 | ||
| import { useEffect, useState } from "react";
 | ||
| import { observer } from "mobx-react-lite";
 | ||
| import { Eye, Pencil, Trash2, Minus } from "lucide-react";
 | ||
| import { useNavigate } from "react-router-dom";
 | ||
| import { CreateButton, DeleteModal } from "@widgets";
 | ||
| import { VEHICLE_TYPES } from "@shared";
 | ||
| import { Box, CircularProgress } from "@mui/material";
 | ||
| 
 | ||
| export const VehicleListPage = observer(() => {
 | ||
|   const { vehicles, getVehicles, deleteVehicle } = vehicleStore;
 | ||
|   const { carriers, getCarriers } = carrierStore;
 | ||
|   const navigate = useNavigate();
 | ||
|   const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
 | ||
|   const [isBulkDeleteModalOpen, setIsBulkDeleteModalOpen] = useState(false);
 | ||
|   const [rowId, setRowId] = useState<number | null>(null);
 | ||
|   const [ids, setIds] = useState<number[]>([]);
 | ||
|   const [isLoading, setIsLoading] = useState(false);
 | ||
|   const { language } = languageStore;
 | ||
| 
 | ||
|   useEffect(() => {
 | ||
|     const fetchData = async () => {
 | ||
|       setIsLoading(true);
 | ||
|       await getVehicles();
 | ||
|       await getCarriers(language);
 | ||
|       setIsLoading(false);
 | ||
|     };
 | ||
|     fetchData();
 | ||
|   }, [language]);
 | ||
| 
 | ||
|   const columns: GridColDef[] = [
 | ||
|     {
 | ||
|       field: "tail_number",
 | ||
|       headerName: "Бортовой номер",
 | ||
|       flex: 1,
 | ||
|       renderCell: (params: GridRenderCellParams) => {
 | ||
|         return (
 | ||
|           <div className="w-full h-full flex items-center">
 | ||
|             {params.value ? (
 | ||
|               params.value
 | ||
|             ) : (
 | ||
|               <Minus size={20} className="text-red-500" />
 | ||
|             )}
 | ||
|           </div>
 | ||
|         );
 | ||
|       },
 | ||
|     },
 | ||
|     {
 | ||
|       field: "type",
 | ||
|       headerName: "Тип",
 | ||
|       flex: 1,
 | ||
|       renderCell: (params: GridRenderCellParams) => {
 | ||
|         return (
 | ||
|           <div className="w-full h-full flex items-center">
 | ||
|             {params.value ? (
 | ||
|               VEHICLE_TYPES.find((type) => type.value === params.row.type)
 | ||
|                 ?.label || params.row.type
 | ||
|             ) : (
 | ||
|               <Minus size={20} className="text-red-500" />
 | ||
|             )}
 | ||
|           </div>
 | ||
|         );
 | ||
|       },
 | ||
|     },
 | ||
|     {
 | ||
|       field: "carrier",
 | ||
|       headerName: "Перевозчик",
 | ||
|       flex: 1,
 | ||
|       renderCell: (params: GridRenderCellParams) => {
 | ||
|         return (
 | ||
|           <div className="w-full h-full flex items-center">
 | ||
|             {params.value ? (
 | ||
|               params.value
 | ||
|             ) : (
 | ||
|               <Minus size={20} className="text-red-500" />
 | ||
|             )}
 | ||
|           </div>
 | ||
|         );
 | ||
|       },
 | ||
|     },
 | ||
|     {
 | ||
|       field: "city",
 | ||
|       headerName: "Город",
 | ||
|       flex: 1,
 | ||
|       renderCell: (params: GridRenderCellParams) => {
 | ||
|         return (
 | ||
|           <div className="w-full h-full flex items-center">
 | ||
|             {params.value ? (
 | ||
|               params.value
 | ||
|             ) : (
 | ||
|               <Minus size={20} className="text-red-500" />
 | ||
|             )}
 | ||
|           </div>
 | ||
|         );
 | ||
|       },
 | ||
|     },
 | ||
|     {
 | ||
|       field: "actions",
 | ||
|       headerName: "Действия",
 | ||
|       width: 200,
 | ||
|       align: "center",
 | ||
|       headerAlign: "center",
 | ||
|       sortable: false,
 | ||
| 
 | ||
|       renderCell: (params: GridRenderCellParams) => {
 | ||
|         return (
 | ||
|           <div className="flex h-full gap-7 justify-center items-center">
 | ||
|             <button onClick={() => navigate(`/vehicle/${params.row.id}/edit`)}>
 | ||
|               <Pencil size={20} className="text-blue-500" />
 | ||
|             </button>
 | ||
|             <button onClick={() => navigate(`/vehicle/${params.row.id}`)}>
 | ||
|               <Eye size={20} className="text-green-500" />
 | ||
|             </button>
 | ||
|             <button
 | ||
|               onClick={() => {
 | ||
|                 setIsDeleteModalOpen(true);
 | ||
|                 setRowId(params.row.id);
 | ||
|               }}
 | ||
|             >
 | ||
|               <Trash2 size={20} className="text-red-500" />
 | ||
|             </button>
 | ||
|           </div>
 | ||
|         );
 | ||
|       },
 | ||
|     },
 | ||
|   ];
 | ||
| 
 | ||
|   const rows = vehicles.data?.map((vehicle) => ({
 | ||
|     id: vehicle.vehicle.id,
 | ||
|     tail_number: vehicle.vehicle.tail_number,
 | ||
|     type: vehicle.vehicle.type,
 | ||
|     carrier: vehicle.vehicle.carrier,
 | ||
|     city: carriers[language].data?.find(
 | ||
|       (carrier) => carrier.id === vehicle.vehicle.carrier_id
 | ||
|     )?.city,
 | ||
|   }));
 | ||
| 
 | ||
|   return (
 | ||
|     <>
 | ||
|       <div style={{ width: "100%" }}>
 | ||
|         <div className="flex justify-between items-center mb-10">
 | ||
|           <h1 className="text-2xl">Транспортные средства</h1>
 | ||
|           <CreateButton
 | ||
|             label="Создать транспортное средство"
 | ||
|             path="/vehicle/create"
 | ||
|           />
 | ||
|         </div>
 | ||
| 
 | ||
|         <div
 | ||
|           className="flex justify-end mb-5 duration-300"
 | ||
|           style={{ opacity: ids.length > 0 ? 1 : 0 }}
 | ||
|         >
 | ||
|           <button
 | ||
|             className="px-4 py-2 bg-red-500 text-white rounded flex gap-2 items-center"
 | ||
|             onClick={() => setIsBulkDeleteModalOpen(true)}
 | ||
|           >
 | ||
|             <Trash2 size={20} className="text-white" /> Удалить выбранные (
 | ||
|             {ids.length})
 | ||
|           </button>
 | ||
|         </div>
 | ||
| 
 | ||
|         <DataGrid
 | ||
|           rows={rows}
 | ||
|           columns={columns}
 | ||
|           hideFooterPagination
 | ||
|           checkboxSelection
 | ||
|           loading={isLoading}
 | ||
|           onRowSelectionModelChange={(newSelection) => {
 | ||
|             setIds(Array.from(newSelection.ids) as number[]);
 | ||
|           }}
 | ||
|           hideFooter
 | ||
|           localeText={ruRU.components.MuiDataGrid.defaultProps.localeText}
 | ||
|           slots={{
 | ||
|             noRowsOverlay: () => (
 | ||
|               <Box sx={{ mt: 5, textAlign: "center", color: "text.secondary" }}>
 | ||
|                 {isLoading ? (
 | ||
|                   <CircularProgress size={20} />
 | ||
|                 ) : (
 | ||
|                   "Нет транспортных средств"
 | ||
|                 )}
 | ||
|               </Box>
 | ||
|             ),
 | ||
|           }}
 | ||
|         />
 | ||
|       </div>
 | ||
| 
 | ||
|       <DeleteModal
 | ||
|         open={isDeleteModalOpen}
 | ||
|         onDelete={async () => {
 | ||
|           if (rowId) {
 | ||
|             await deleteVehicle(rowId);
 | ||
|           }
 | ||
|           setIsDeleteModalOpen(false);
 | ||
|           setRowId(null);
 | ||
|         }}
 | ||
|         onCancel={() => {
 | ||
|           setIsDeleteModalOpen(false);
 | ||
|           setRowId(null);
 | ||
|         }}
 | ||
|       />
 | ||
| 
 | ||
|       <DeleteModal
 | ||
|         open={isBulkDeleteModalOpen}
 | ||
|         onDelete={async () => {
 | ||
|           await Promise.all(ids.map((id) => deleteVehicle(id)));
 | ||
|           getVehicles();
 | ||
|           setIsBulkDeleteModalOpen(false);
 | ||
|           setIds([]);
 | ||
|         }}
 | ||
|         onCancel={() => {
 | ||
|           setIsBulkDeleteModalOpen(false);
 | ||
|         }}
 | ||
|       />
 | ||
|     </>
 | ||
|   );
 | ||
| });
 |