Add 'sge-frontend/' from commit '5fa787e054b25ac53edc7ff0275ea7960a709401'

git-subtree-dir: sge-frontend
git-subtree-mainline: 876c278ac4
git-subtree-split: 5fa787e054
This commit is contained in:
2025-08-04 00:27:23 +03:00
337 changed files with 854877 additions and 0 deletions

View File

@@ -0,0 +1,729 @@
import React, { useEffect } from "react";
import {
MapContainer,
Marker,
Polygon,
Popup,
TileLayer,
Tooltip,
useMap,
LayerGroup,
} from "react-leaflet";
import "../components/leaflet.css";
import { useState, useRef } from "react";
import {
Button,
Label,
Modal,
ModalHeader,
ModalBody,
Row,
Col,
Input,
Card,
CardHeader,
CardTitle,
} from "reactstrap";
import Select from "react-select";
import { useTranslation } from "react-i18next";
import DataInputGroup from "../components/data-input/index.js";
import { useSelector, useDispatch } from "react-redux";
import { getCities } from "../redux/actions/cities";
import { v4 as uuidv4 } from "uuid";
import { getCity } from "../redux/actions/city";
import { getDistrict } from "../redux/actions/district";
import {
getOrganisations,
getOrganisationById,
} from "../redux/actions/organisations";
import { getAreasWithCriteria } from "../redux/actions/areas";
import { ChromePicker } from "react-color";
import { customFilterForSelect } from "../utility/Utils";
import { permissionCheck } from "../components/permission-check";
import { getDataCenters } from "../redux/actions/dataCenter";
const ColorPicker = ({ selectedColors, setSelectedColors, index }) => {
const [showColorPicker, setShowColorPicker] = useState(false);
useEffect(() => {
const storedColors = localStorage.getItem("selectedMapColors");
if (storedColors) {
setSelectedColors(JSON.parse(storedColors));
}
}, []);
useEffect(() => {
localStorage.setItem("selectedMapColors", JSON.stringify(selectedColors));
}, [selectedColors]);
const handleColorChange = (color) => {
const rgbaColor = `rgba(${color.rgb.r}, ${color.rgb.g}, ${color.rgb.b}, 0.3)`;
const updatedColors = [...selectedColors];
updatedColors[index] = rgbaColor;
setSelectedColors(updatedColors);
};
const handlePickerOutsideClick = (event) => {
// Renk seçici dışına tıklama kontrolü
if (
event.target.closest(".color-picker-container") === null &&
event.target.closest(".color-box") === null
) {
setShowColorPicker(false);
}
};
useEffect(() => {
// Tüm belgeye tıklama olayını dinle
document.addEventListener("click", handlePickerOutsideClick);
return () => {
// Bileşen kaldırıldığında tıklama olayının dinlemesini kaldır
document.removeEventListener("click", handlePickerOutsideClick);
};
}, []);
return (
<div className="color-picker-container">
<div
key={index}
className="color-box"
style={{
backgroundColor: selectedColors[index],
width: "30px",
height: "15px",
display: "inline-block",
marginRight: "10px",
cursor: "pointer",
}}
onClick={() => setShowColorPicker(true)}
></div>
{showColorPicker && (
<div style={{ position: "absolute", zIndex: 1000 }}>
<ChromePicker
color={selectedColors[index]}
onChange={handleColorChange}
/>
</div>
)}
</div>
);
};
const Map = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const citiesStore = useSelector((state) => state.cities);
const cityStore = useSelector((state) => state.city);
const districtStore = useSelector((state) => state.district);
const OrganisationsStore = useSelector((state) => state.organizations);
const areasStore = useSelector((state) => state.areas);
const dataCenterStore = useSelector((state) => state.dataCenter);
const [cities, setCities] = useState([]);
const [districts, setDistricts] = useState([]);
const [neighborhoods, setNeighborhoods] = useState([]);
const [organizationOptions, setOrganizationOptions] = useState([]);
const organizationId = localStorage.getItem("organizationId");
const roleTag = localStorage.getItem("roleTag");
const [selectedOrganization, setSelectedOrganization] = useState({
label: localStorage.getItem("organizationName"),
value: organizationId,
});
const [areasOptions, setAreasOptions] = useState([]);
const [done, setDone] = useState(false);
const [selectedCity, setSelectedCity] = useState(null);
const [selectedDistrict, setSelectedDistrict] = useState(null);
const [districtView, setDistrictView] = useState(false);
const [neighbourhoodView, setNeighbourhoodView] = useState(false);
const cityRefs = useRef({});
const districtRefs = useRef({});
const neighbourhoodRefs = useRef({});
const [zoom, setZoom] = useState(6.7);
const [center, setCenter] = useState({ lat: 38.5, lng: 35.27 });
const [showDataInputModal, setShowDataInputModal] = useState(false);
const [inputData, setInputData] = useState({
organization: selectedOrganization,
});
const [referance, setReferance] = useState(
Number(localStorage.getItem("referance")) || 1000
);
const [referance2, setReferance2] = useState(
Number(localStorage.getItem("referance2")) || 2000
);
const initialColors = [
"rgba(44,232,44,0.3)",
"rgba(234,234,17,0.3)",
"rgba(228,19,19,0.3)",
];
const [selectedColors, setSelectedColors] = useState(initialColors);
const currentYear = new Date().getFullYear();
const [year, setYear] = useState({ value: currentYear, label: currentYear });
const renderYearOptions = () => {
const years = [];
for (let year = currentYear; year >= 1990; year--) {
years.push({ label: year, value: year });
}
return years;
};
useEffect(() => {
dispatch(getCities());
dispatch(getDataCenters());
}, []);
useEffect(() => {
if (selectedOrganization?.value != "undefined") {
dispatch(getAreasWithCriteria(selectedOrganization.value));
}
}, [selectedOrganization]);
useEffect(() => {
setAreasOptions([]);
const citiesOptions =
areasStore?.areasWithCriteria
?.map((area) =>
Array.isArray(area?.cities) // cities'in bir dizi olup olmadığını kontrol edin
? area.cities.map((city) => ({
value: city?.id,
label: city?.name,
type: "city",
}))
: []
)
.flat() || []; // Eğer map boş dönerse, `flat` sonrası boş bir dizi kullan
const districtsOptions = areasStore?.areasWithCriteria
?.map((area) => {
return area?.districts?.map((district) => {
return {
value: district?.id,
label: district?.name + " (" + district?.city?.name + ")",
type: "district",
};
});
})
.flat();
const neighborhoodsOptions = areasStore?.areasWithCriteria
?.map((area) => {
return area?.neighborhoods?.map((neighborhood) => {
return {
value: neighborhood?.id,
label:
neighborhood?.name +
" (" +
neighborhood?.district?.name +
" / " +
neighborhood?.district?.city?.name +
")",
type: "neighborhood",
};
});
})
.flat();
setAreasOptions([
...(citiesOptions || []),
...(districtsOptions || []),
...(neighborhoodsOptions || []),
]);
}, [areasStore]);
useEffect(() => {
if (roleTag === "SUPER_ADMIN") {
dispatch(getOrganisations());
} else {
dispatch(getOrganisationById(organizationId));
}
}, []);
const handleDataInputButtonPressed = ({ area, type }) => {
const areaName =
type === "neighborhood"
? `${area?.name} / ${area?.district?.name} / ${area?.district?.city?.name}`
: type === "district"
? `${area?.name} / ${area?.city?.name}`
: area?.name;
const areaType =
type === "district"
? "district"
: type === "city"
? "city"
: type === "neighborhood"
? "neighborhood"
: "";
setShowDataInputModal(true);
setInputData({
...inputData,
area: { value: area?.id, label: areaName, type: areaType },
});
};
useEffect(() => {
if (!citiesStore?.cities || !year?.value) {
console.error("Cities or year is not defined.");
return;
}
setCities(() => {
const updatedCities = citiesStore.cities.map((city) => {
const mainDataTables = city.mainDataTables
? city.mainDataTables.filter(
(data) => data.year === year.value.toString()
)
: [];
const total = mainDataTables.reduce(
(acc, data) => acc + (data?.totalEmission || 0),
0
);
return { ...city, mainDataTables, total };
});
return updatedCities;
});
}, [citiesStore?.cities, year?.value]);
useEffect(() => {
if (selectedCity != undefined) {
dispatch(getCity(selectedCity?.id));
}
}, [selectedCity]);
useEffect(() => {
if (selectedDistrict != undefined && selectedDistrict.length != 0) {
dispatch(getDistrict(selectedDistrict?.id));
}
}, [selectedDistrict]);
useEffect(() => {
if (!cityStore?.city?.districts || !year.value) {
return;
}
setDistricts(() => {
const updatedDistricts = cityStore.city.districts?.map((district) => {
const mainDataTables = district.mainDataTables.filter(
(data) => data.year === year.value.toString()
);
const total = mainDataTables.reduce(
(acc, data) => acc + data.totalEmission,
0
);
return { ...district, mainDataTables, total };
});
return updatedDistricts;
});
}, [selectedCity, cityStore?.city, year.value]);
useEffect(() => {
if (!districtStore?.district?.neighborhoods || !year.value) {
return;
}
setNeighborhoods(() => {
const updatedNeighborhoods = districtStore.district?.neighborhoods?.map(
(neighborhood) => {
const mainDataTables = neighborhood.mainDataTables.filter(
(data) => data.year === year.value.toString()
);
const total = mainDataTables.reduce(
(acc, data) => acc + data.totalEmission,
0
);
return { ...neighborhood, mainDataTables, total };
}
);
return updatedNeighborhoods;
});
}, [selectedDistrict, districtStore?.district, year.value]);
useEffect(() => {
let organizationOptions = [];
if (
OrganisationsStore.organization &&
OrganisationsStore.organization.length !== 0
) {
organizationOptions.push({
value: OrganisationsStore.organization.id,
label: OrganisationsStore.organization.tag,
});
if (OrganisationsStore.organization.children) {
organizationOptions = [
...organizationOptions,
...OrganisationsStore.organization.children.map((organization) => ({
value: organization.child.id,
label: organization.child.tag,
})),
];
}
} else {
organizationOptions = OrganisationsStore.dataOrganization.map(
(organization) => ({
value: organization.id,
label: organization.tag,
})
);
}
setOrganizationOptions(organizationOptions);
}, [OrganisationsStore]);
const renderDataInputModal = () => {
return (
<Modal
isOpen={showDataInputModal}
toggle={() => setShowDataInputModal(!showDataInputModal)}
className="modal-dialog-centered"
size="lg"
>
<ModalHeader toggle={() => setShowDataInputModal(!showDataInputModal)}>
{t("DataInput.dataInput")} - {inputData?.area?.label}
</ModalHeader>
<ModalBody>
<DataInputGroup inputData={inputData} setInputData={setInputData} />
</ModalBody>
</Modal>
);
};
const grayOptions = {
color: "gray",
fillColor: "transparent",
opacity: 1,
};
const greenOptions = {
color: "gray",
fillColor: selectedColors[0],
opacity: 1,
};
const yellowOptions = {
color: "gray",
fillColor: selectedColors[1],
opacity: 1,
};
const redOptions = {
color: "gray",
fillColor: selectedColors[2],
opacity: 1,
};
const pathOptions = (total) => {
return total > referance2
? redOptions
: referance < total && total < referance2
? yellowOptions
: total != 0 && total < referance
? greenOptions
: grayOptions;
};
const turkeyBounds = [
[34.42, 44.83],
[43.45, 25.62],
];
const RenderDistrictView = () => {
return districts?.map((district, index) => {
return (
<Polygon
key={uuidv4()}
ref={(c) => {
districtRefs.current[index] = c;
if (index === district.length - 1 && !done) {
setDone(true);
}
}}
fillOpacity={1}
pathOptions={pathOptions(district.total)}
positions={convertCoordinates(district.coordinates)}
eventHandlers={{
mouseover: () => {
const district = districtRefs.current[index];
district.openPopup();
},
}}
>
<Tooltip permanent direction="center" className="tooltip">
{district.name}
</Tooltip>
{
<Popup closeButton={true} autoPan={false}>
<h5 className="w-100 text-center">{district.name}</h5>
<Button
className="w-100 mb-1"
color="primary"
onClick={() => {
setSelectedDistrict(district);
setNeighbourhoodView(true);
setDistrictView(false);
setSelectedCity(null);
setZoom(11.0);
let convertCordinates = convertCoordinates(
district.coordinates
);
let length = convertCordinates[0][0][0].length;
let mlength = ((length + 1) / 2).toFixed(0);
let lat1 = convertCordinates[0][0][0][0];
let lng1 = convertCordinates[0][0][0][1];
let lat2 = convertCordinates[0][0][mlength][0];
let lng2 = convertCordinates[0][0][mlength][1];
setCenter({
lat: ((lat1 + lat2) / 2).toFixed(2),
lng: ((lng1 + lng2) / 2).toFixed(2),
});
}}
>
{t("Areas.neighborhoods")}
</Button>
<br></br>
{areaCheck({ areaId: district.id }) &&
permissionCheck("dataset_create") && (
<Button
className="w-100 mb-1"
color="primary"
onClick={() =>
handleDataInputButtonPressed({
area: district,
type: "district",
})
}
>
{t("DataInput.dataInput")}
</Button>
)}
<br></br>
<Button
className="w-100"
color="primary"
onClick={() => {
setDistrictView(false);
setCenter({ lat: 38, lng: 37 });
setZoom(6.7);
}}
>
{t("Map.goBack")}
</Button>
</Popup>
}
</Polygon>
);
});
};
const neighborhoodTooltipBackground = (total) => {
return total > referance2
? selectedColors[2]
: referance < total && total < referance2
? selectedColors[1]
: total != 0 && total < referance
? selectedColors[0]
: "white";
};
const renderNeighbourhoodView = () => {
return neighborhoods?.map((neighbourhood, index) => {
return (
<Marker
opacity={0}
zoom={10.75}
key={uuidv4()}
ref={(c) => {
// <--- add city refs to ref object here
neighbourhoodRefs.current[index] = c;
if (index === neighbourhood.length - 1 && !done) {
setDone(true);
}
}}
fillOpacity={1}
pathOptions={pathOptions(neighbourhood.total)}
position={[
Number(neighbourhood?.minLat + neighbourhood?.maxLat) / 2,
Number(neighbourhood?.minLong + neighbourhood?.maxLong) / 2,
]}
eventHandlers={{
mouseover: () => {
const neighbourhood = neighbourhoodRefs.current[index];
neighbourhood.openPopup();
},
}}
>
<Tooltip permanent direction="center" className="tooltip">
{neighbourhood.name}
</Tooltip>
<Popup closeButton={true} autoPan={false}>
{areaCheck({ areaId: neighbourhood.id }) &&
permissionCheck("dataset_create") && (
<Button
className="w-100 mb-1"
color="primary"
onClick={() =>
handleDataInputButtonPressed({
area: neighbourhood,
type: "neighborhood",
})
}
>
{t("DataInput.dataInput")}
</Button>
)}
<br></br>
<Button
className="w-100"
color="primary"
onClick={() => {
setNeighbourhoodView(false);
setCenter({ lat: 38, lng: 37 });
setZoom(6.7);
}}
>
{t("Map.goBack")}
</Button>
</Popup>
</Marker>
);
});
};
function ChangeZoom({ center, zoom }) {
const map = useMap();
map.setView(center, zoom);
return null;
}
function convertCoordinates(coords) {
const editedCoordinates = JSON.parse(coords);
return editedCoordinates;
}
function areaCheck({ areaId }) {
return areasOptions?.some((area) => areaId === area.value);
}
const renderDataCenterMarkers = () => {
if (!dataCenterStore?.dataCenters) return null;
return (
<LayerGroup>
{dataCenterStore.dataCenters.map((dc) => {
if (!dc.latitude || !dc.longitude) return null;
return (
<Marker
key={dc.id}
position={[dc.latitude, dc.longitude]}
>
<Popup>
<div className="data-center-popup">
<h5 className="mb-2">{dc.dataCenter}</h5>
<p className="mb-1"><strong>{t('DataCenter.number')}:</strong> {dc.number}</p>
<p className="mb-1"><strong>{t('DataCenter.externalId')}:</strong> {dc.externalId}</p>
<p className="mb-1"><strong>{t('DataCenter.city')}:</strong> {dc.area?.cityNames || "-"}</p>
{dc.area && <p className="mb-1"><strong>{t('Area')}:</strong> {dc.area.tag}</p>}
{dc.projects?.length > 0 && (
<p className="mb-1">
<strong>{t('Projects')}:</strong> {dc.projects.length}
</p>
)}
{dc.ayposURL && (
<Button
className="w-100 mb-1"
color="primary"
onClick={() => window.open(dc.ayposURL, '_blank')}
>
Dashboard
</Button>
)}
</div>
</Popup>
<Tooltip>{dc.dataCenter}</Tooltip>
</Marker>
);
})}
</LayerGroup>
);
};
return (
<Card>
<CardHeader className="border-bottom">
<CardTitle tag="h4">{t("Map.title")}</CardTitle>
</CardHeader>
<div style={{ height: "700px", width: "100%" }}>
<MapContainer
center={center}
zoom={zoom}
style={{ height: "100%", width: "100%" }}
>
<TileLayer
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
/>
{renderDataCenterMarkers()}
{neighbourhoodView
? renderNeighbourhoodView(selectedDistrict)
: districtView
? RenderDistrictView(selectedCity)
: cities.map((city, index) => {
return (
<Polygon
key={uuidv4()}
ref={(c) => {
cityRefs.current[index] = c;
if (index === cities.length - 1 && !done) {
setDone(true);
}
}}
fillOpacity={1}
pathOptions={pathOptions(city.total)}
positions={convertCoordinates(city.coordinates)}
eventHandlers={{
click: () => {
setSelectedCity(city);
setDistrictView(true);
setZoom(8.0);
let convertCordinates = convertCoordinates(city.coordinates);
let length = convertCordinates[0][0][0].length;
let mlength = ((length + 1) / 2).toFixed(0);
let lat1 = convertCordinates[0][0][0][0];
let lng1 = convertCordinates[0][0][0][1];
let lat2 = convertCordinates[0][0][mlength][0];
let lng2 = convertCordinates[0][0][mlength][1];
setCenter({
lat: ((lat1 + lat2) / 2).toFixed(2),
lng: ((lng1 + lng2) / 2).toFixed(2),
});
}
}}
>
<Tooltip permanent direction="center">
{city.name}
</Tooltip>
</Polygon>
);
})}
{renderDataInputModal()}
</MapContainer>
</div>
</Card>
);
};
export default Map;