Files
universal_is/client/src/pages/fuel/Fuel/Boilers.tsx
2025-12-23 09:53:04 +09:00

239 lines
8.5 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 { useEffect, useState } from 'react'
import { Dropdown, Option, Spinner } from '@fluentui/react-components'
import useSWR from 'swr'
import axios from 'axios'
import { AgGridReact, CustomCellRendererProps } from 'ag-grid-react'
import { AllCommunityModule, ModuleRegistry } from 'ag-grid-community'
import FuelRenderer from './FuelRenderer'
import { useSearchParams } from 'react-router-dom'
import { ICity, IRegion } from '../../../interfaces/fuel'
import BoilersCard from './BoilersCard'
ModuleRegistry.registerModules([AllCommunityModule])
function Boilers() {
const [searchParams, setSearchParams] = useSearchParams();
const [regionId, setRegionId] = useState<number | undefined>(
searchParams.get("region_id") ? Number(searchParams.get("region_id")) : undefined
);
const [cityId, setCityId] = useState<number | undefined>(
searchParams.get("city_id") ? Number(searchParams.get("city_id")) : undefined
)
// Load regions
const { data: regions, isLoading: regionsLoading } = useSWR("/general/regions", () =>
axios
.get("/general/regions", { baseURL: import.meta.env.VITE_API_NEST_URL })
.then((res) => res.data)
);
// Load cities when regionId exists
const { data: cities, isLoading: citiesLoading } = useSWR(
regionId ? `/general/cities?region_id=${regionId}` : null,
() =>
axios
.get(`/general/cities?region_id=${regionId}`, {
baseURL: import.meta.env.VITE_API_NEST_URL,
})
.then((res) => res.data)
);
// Load boilers when cityId exists
const { data: boilers, isLoading: boilersLoading } = useSWR(
cityId ? `/fuel/boilers?city_id=${cityId}&offset=0&limit=100` : null,
() =>
axios
.get(`/fuel/boilers?city_id=${cityId}&offset=0&limit=100`, {
baseURL: import.meta.env.VITE_API_NEST_URL,
})
.then((res) => res.data)
);
//
// Sync regionId → URL
//
useEffect(() => {
if (!regionId) return;
setSearchParams((prev) => {
const params = new URLSearchParams(prev);
params.set("region_id", regionId.toString());
params.delete("city_id"); // reset city when region changes
return params;
});
}, [regionId]);
//
// Sync cityId → URL
//
useEffect(() => {
if (!cityId) return;
setSearchParams((prev) => {
const params = new URLSearchParams(prev);
params.set("city_id", cityId.toString());
return params;
});
}, [cityId]);
//
// Utility: get display values
//
const selectedRegion = regions?.find((r: IRegion) => r.id === regionId);
const selectedCity = cities?.find((c: ICity) => c.id === cityId);
useEffect(() => {
const paramCityId = searchParams.get("city_id");
if (!paramCityId) return;
const id = Number(paramCityId);
// If cityId not yet set OR mismatched with URL
if (cities && !cityId && cities.some((c: ICity) => c.id === id)) {
setCityId(id);
}
}, [cities])
useEffect(() => {
const paramRegionId = searchParams.get("region_id");
if (!paramRegionId) return;
const id = Number(paramRegionId);
if (regions && !regionId && regions.some((r: IRegion) => r.id === id)) {
setRegionId(id);
}
}, [regions]);
useEffect(() => {
setCityId(undefined); // clear stale value before SWR runs
}, [regionId]);
return (
<div style={{
display: 'flex',
flexDirection: 'column',
padding: '1rem',
width: '100%',
gap: '1rem'
}}>
{/* <Portal mountNode={document.querySelector('#header-portal')}>
</Portal> */}
<div style={{ display: 'flex', gap: '1rem' }}>
{regionsLoading ?
<Spinner />
:
<Dropdown
placeholder='Выберите район'
value={selectedRegion?.name ?? ""}
selectedOptions={regionId ? [regionId.toString()] : []}
onOptionSelect={(_, data) => {
if (!data.optionValue) {
setRegionId(undefined);
return;
}
setRegionId(Number(data.optionValue));
}}
>
{regions && Array.isArray(regions) && regions.map((option) => (
<Option key={`region-${option.id}`} text={option.name} value={option.id}>
{option.name}
</Option>
))}
</Dropdown>
}
{citiesLoading ?
<Spinner />
:
<Dropdown
placeholder='Выберите населенный пункт'
value={selectedCity?.name ?? ""}
selectedOptions={cityId ? [cityId.toString()] : []}
onOptionSelect={(_, data) => {
if (!data.optionValue) {
setCityId(undefined);
return;
}
setCityId(Number(data.optionValue));
}}
>
{cities && Array.isArray(cities) && cities.map((option) => (
<Option key={`region-${option.id}`} text={option.name} value={option.id}>
{option.name}
</Option>
))}
</Dropdown>
}
</div>
<div style={{
display: 'flex',
width: '100%',
flexDirection: 'column',
gap: '1rem'
}}>
{cityId &&
<div style={{ display: 'flex', width: '100%', gap: '1rem', justifyContent: 'space-between' }}>
<BoilersCard title='Всего объектов' value={boilers && Array.isArray(boilers) ? boilers.length.toString() : ''} subtitle='' />
<BoilersCard title='Общий остаток' value={''} subtitle='' />
<BoilersCard title='Лимит на сезон' value={''} subtitle='' />
<BoilersCard title='Требуют внимания' value={''} subtitle='' />
</div>
}
</div>
{boilersLoading ?
<Spinner />
:
<AgGridReact
key={`boilers-${cityId}`}
loading={boilersLoading}
overlayLoadingTemplate='Загрузка...'
overlayNoRowsTemplate='Нет данных'
rowData={boilers}
columnDefs={[
{
field: 'boiler_name',
headerName: 'Наименование'
},
{
field: 'boiler_code',
headerName: 'Идент. код'
},
{
field: 'id_fuels',
headerName: 'Вид топлива',
//editable: true,
//cellEditor: FuelTypeEditor,
autoHeight: true,
cellRenderer: FuelRenderer,
cellStyle: (_) => {
return { padding: '1px' }
}
//enableCellChangeFlash: true
},
{
field: 'activity',
headerName: 'Активный',
cellRenderer: (params: CustomCellRendererProps) => (<span>{params.value === true ? 'Да' : 'Нет'}</span>)
}
]}
defaultColDef={{
flex: 1,
}}
/>}
</div>
)
}
export default Boilers