Files
WhiteNightsAdminPanel/src/pages/Carrier/CarrierListPage/index.tsx

249 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { DataGrid, GridColDef, GridRenderCellParams } from "@mui/x-data-grid";
import { ruRU } from "@mui/x-data-grid/locales";
import { authStore, carrierStore, cityStore, languageStore, selectedCityStore, SearchInput } from "@shared";
import { useEffect, useState, useMemo } from "react";
import { observer } from "mobx-react-lite";
import { Pencil, Trash2, Minus } from "lucide-react";
import { useNavigate } from "react-router-dom";
import { CreateButton, DeleteModal, LanguageSwitcher } from "@widgets";
import { Box, CircularProgress } from "@mui/material";
export const CarrierListPage = observer(() => {
const { carriers, getCarriers, deleteCarrier } = 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 [paginationModel, setPaginationModel] = useState({
page: 0,
pageSize: 50,
});
const { language } = languageStore;
const canReadCities = authStore.canRead("cities");
const [searchQuery, setSearchQuery] = useState("");
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
if (!authStore.me) {
await authStore.getMeAction().catch(() => undefined);
}
if (authStore.canRead("cities")) {
await cityStore.getCities(language);
} else {
await authStore.fetchMeCities().catch(() => undefined);
}
await getCarriers(language);
setIsLoading(false);
};
fetchData();
}, [language, selectedCityStore.cityVersion]);
const columns: GridColDef[] = [
{
field: "full_name",
headerName: "Полное имя",
width: 300,
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: "short_name",
headerName: "Короткое имя",
width: 200,
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_id",
headerName: "Город",
flex: 1,
renderCell: (params: GridRenderCellParams) => {
const lang = language as "ru" | "en" | "zh";
const cityName = canReadCities
? cityStore.cities[lang]?.data.find((c) => c.id === params.value)?.name
: authStore.meCities[lang]?.find((c) => c.city_id === params.value)?.name;
return (
<div className="w-full h-full flex items-center">
{cityName ?? <Minus size={20} className="text-red-500" />}
</div>
);
},
},
...(authStore.canWrite("carriers") ? [{
field: "actions",
headerName: "Действия",
headerAlign: "center" as const,
width: 200,
sortable: false,
renderCell: (params: GridRenderCellParams) => (
<div className="flex h-full gap-7 justify-center items-center">
<button title="Редактировать" onClick={() => navigate(`/carrier/${params.row.id}/edit`)}>
<Pencil size={20} className="text-blue-500" />
</button>
<button
title="Удалить"
onClick={() => {
setIsDeleteModalOpen(true);
setRowId(params.row.id);
}}
>
<Trash2 size={20} className="text-red-500" />
</button>
</div>
),
}] : []),
];
const allowedCityIds = canReadCities
? null
: authStore.meCities["ru"].map((c) => c.city_id);
const canWriteCarriers = authStore.canWrite("carriers");
const rows = useMemo(() => {
const query = searchQuery.trim().toLowerCase();
return (carriers[language].data ?? [])
.filter((carrier) => !allowedCityIds || allowedCityIds.includes(carrier.city_id))
.filter(
(carrier) =>
!query ||
(carrier.full_name ?? "").toLowerCase().includes(query) ||
(carrier.short_name ?? "").toLowerCase().includes(query)
)
.map((carrier) => ({
id: carrier.id,
full_name: carrier.full_name,
short_name: carrier.short_name,
city_id: carrier.city_id,
}));
}, [carriers[language].data, searchQuery, allowedCityIds]);
return (
<>
<LanguageSwitcher />
<div className="w-full">
<div className="flex justify-between items-center mb-10">
<h1 className="text-2xl">Перевозчики</h1>
{canWriteCarriers && (
<CreateButton label="Создать перевозчика" path="/carrier/create" />
)}
</div>
{canWriteCarriers && ids.length > 0 && (
<div className="flex justify-end mb-5 duration-300">
<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>
)}
{(rows.length > 0 || searchQuery) && (
<SearchInput value={searchQuery} onChange={setSearchQuery} />
)}
<DataGrid
rows={rows}
columns={columns}
checkboxSelection={canWriteCarriers}
disableRowSelectionExcludeModel
disableRowSelectionOnClick
onRowDoubleClick={(params) => {
if (canWriteCarriers) {
navigate(`/carrier/${params.id}/edit`);
}
}}
loading={isLoading}
paginationModel={paginationModel}
onPaginationModelChange={setPaginationModel}
pageSizeOptions={[50]}
localeText={ruRU.components.MuiDataGrid.defaultProps.localeText}
onRowSelectionModelChange={
canWriteCarriers
? (newSelection: any) => {
if (Array.isArray(newSelection)) {
const selectedIds = newSelection.map(Number);
setIds(selectedIds);
} else if (
newSelection &&
typeof newSelection === "object" &&
"ids" in newSelection
) {
const idsSet = newSelection.ids as Set<string | number>;
const selectedIds = Array.from(idsSet).map(Number);
setIds(selectedIds);
} else {
setIds([]);
}
}
: undefined
}
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 deleteCarrier(rowId);
}
setIsDeleteModalOpen(false);
setRowId(null);
}}
onCancel={() => {
setIsDeleteModalOpen(false);
setRowId(null);
}}
/>
<DeleteModal
open={isBulkDeleteModalOpen}
onDelete={async () => {
await Promise.all(ids.map((id) => deleteCarrier(id)));
await getCarriers(language);
setIsBulkDeleteModalOpen(false);
setIds([]);
}}
onCancel={() => {
setIsBulkDeleteModalOpen(false);
}}
/>
</>
);
});