Merge branch 'feature/popup_points' into 'dev'

Feature/popup points

See merge request spatial/postamates_frontend!34
dev
Timofey Malinin 3 years ago
commit 9a83f686eb

@ -23,7 +23,7 @@ if (import.meta.env.MODE === "development") {
mountStoreDevtool("PointSelection", usePointSelection); mountStoreDevtool("PointSelection", usePointSelection);
} }
const version = '0.0.6'; const version = '0.0.7';
function App() { function App() {

@ -1,8 +1,5 @@
import { Layer } from "react-map-gl"; import { Layer } from "react-map-gl";
import { import { workingPointSymbolLayer } from "./layers-config";
workingPointBackgroundLayer,
workingPointSymbolLayer,
} from "./layers-config";
import { useLayersVisibility } from "../../stores/useLayersVisibility"; import { useLayersVisibility } from "../../stores/useLayersVisibility";
import { STATUSES } from "../../config"; import { STATUSES } from "../../config";
import { useRegionFilterExpression } from "./useRegionFilterExpression"; import { useRegionFilterExpression } from "./useRegionFilterExpression";
@ -52,16 +49,6 @@ export const FilteredWorkingPoints = () => {
return ( return (
<> <>
<Layer
{...workingPointBackgroundLayer}
id={LAYER_IDS.filteredWorkingBackground}
source={"points"}
source-layer={"public.points_with_dist"}
layout={{
visibility: isVisible[LAYER_IDS.filteredWorking] ? "visible" : "none",
}}
filter={filter}
/>
<Layer <Layer
{...workingPointSymbolLayer} {...workingPointSymbolLayer}
id={LAYER_IDS.filteredWorking} id={LAYER_IDS.filteredWorking}

@ -1,8 +1,5 @@
import { Layer } from "react-map-gl"; import { Layer } from "react-map-gl";
import { import { workingPointSymbolLayer } from "./layers-config";
workingPointBackgroundLayer,
workingPointSymbolLayer,
} from "./layers-config";
import { useLayersVisibility } from "../../stores/useLayersVisibility"; import { useLayersVisibility } from "../../stores/useLayersVisibility";
import { MODES, STATUSES } from "../../config"; import { MODES, STATUSES } from "../../config";
import { useRegionFilterExpression } from "./useRegionFilterExpression"; import { useRegionFilterExpression } from "./useRegionFilterExpression";
@ -33,16 +30,6 @@ export const WorkingPoints = () => {
return ( return (
<> <>
<Layer
{...workingPointBackgroundLayer}
id={LAYER_IDS.workingBackground}
source={"points"}
source-layer={"public.points_with_dist"}
layout={{
visibility: isVisible[LAYER_IDS.working] ? "visible" : "none",
}}
filter={getFilter()}
/>
<Layer <Layer
{...workingPointSymbolLayer} {...workingPointSymbolLayer}
id={LAYER_IDS.working} id={LAYER_IDS.working}

@ -1,4 +1,4 @@
import { Button } from "antd"; import {Button, Spin} from "antd";
import { CATEGORIES, MODES, STATUSES } from "../../config"; import { CATEGORIES, MODES, STATUSES } from "../../config";
import { useMode } from "../../stores/useMode"; import { useMode } from "../../stores/useMode";
import { LAYER_IDS } from "../Layers/constants"; import { LAYER_IDS } from "../Layers/constants";
@ -9,57 +9,13 @@ import { WorkingPointPopup } from "./mode-popup/WorkingPointPopup";
import { FeatureProperties } from "./mode-popup/FeatureProperties"; import { FeatureProperties } from "./mode-popup/FeatureProperties";
import { usePopup } from "../../stores/usePopup.js"; import { usePopup } from "../../stores/usePopup.js";
import { PanoramaIcon } from "../../icons/PanoramaIcon"; import { PanoramaIcon } from "../../icons/PanoramaIcon";
import {useOtherGroups, usePostamatesAndPvzGroups} from "../../api.js"; import { useGetPopupPoints } from "../../api.js";
import {useMemo} from "react"; import { doesMatchFilter } from "../../utils.js";
import {doesMatchFilter, getFilteredGroups} from "../../utils.js";
import Checkbox from "antd/es/checkbox/Checkbox"; import Checkbox from "antd/es/checkbox/Checkbox";
import {usePointSelection} from "../../stores/usePointSelection.js"; import { usePointSelection } from "../../stores/usePointSelection.js";
import {usePendingPointsFilters} from "../../stores/usePendingPointsFilters.js"; import { usePendingPointsFilters } from "../../stores/usePendingPointsFilters.js";
const getRivalsName = (feature) => { const SingleFeaturePopup = ({ feature, point }) => {
const { data: postamatesAndPvzGroups } = usePostamatesAndPvzGroups();
const { data: otherGroups } = useOtherGroups();
const filteredPostamatesCategories = useMemo(() => {
return getFilteredGroups(postamatesAndPvzGroups);
}, [postamatesAndPvzGroups]);
const filteredOtherCategories = useMemo(() => {
return getFilteredGroups(otherGroups);
}, [otherGroups]);
const filteredPostamatesGroups = useMemo(() => {
if (!filteredPostamatesCategories) return [];
return filteredPostamatesCategories
.map((category) => {
return [...category.groups]
}).flat();
}, [filteredPostamatesCategories]);
const filteredOtherGroups = useMemo(() => {
if (!filteredOtherCategories) return [];
return filteredOtherCategories
.map((category) => {
return [...category.groups]
}).flat();
}, [filteredOtherCategories]);
const isOther = feature.layer?.id.includes(LAYER_IDS.other);
const name = isOther ?
filteredOtherCategories.find(c => c.id === feature.properties.category_id)?.name :
filteredPostamatesCategories.find(c => c.id === feature.properties.category_id)?.name;
const groupName = isOther ?
filteredOtherGroups.find(c => c.id === feature.properties.group_id)?.name :
filteredPostamatesGroups.find(c => c.id === feature.properties.group_id)?.name;
return {
name,
groupName
}
}
const SingleFeaturePopup = ({ feature }) => {
const { mode } = useMode(); const { mode } = useMode();
const isRivals = const isRivals =
feature.layer?.id.includes(LAYER_IDS.pvz) || feature.layer?.id.includes(LAYER_IDS.pvz) ||
@ -68,25 +24,24 @@ const SingleFeaturePopup = ({ feature }) => {
const isWorkingPoint = feature.properties.status === STATUSES.working; const isWorkingPoint = feature.properties.status === STATUSES.working;
if (isRivals) { if (isRivals) {
const { name, groupName } = getRivalsName(feature); return <FeatureProperties feature={feature} point={point} />;
return <FeatureProperties feature={feature} name={name} groupName={groupName} />;
} }
if (mode === MODES.ON_APPROVAL && !isPendingPoint) { if (mode === MODES.ON_APPROVAL && !isPendingPoint) {
return <OnApprovalPointPopup feature={feature} />; return <OnApprovalPointPopup feature={feature} point={point} />;
} }
if (mode === MODES.WORKING && isWorkingPoint) { if (mode === MODES.WORKING && isWorkingPoint) {
return <WorkingPointPopup feature={feature} />; return <WorkingPointPopup feature={feature} point={point} />;
} }
if (mode === MODES.PENDING && isPendingPoint) if (mode === MODES.PENDING && isPendingPoint)
return <PendingPointPopup feature={feature} />; return <PendingPointPopup feature={feature} point={point} />;
return <FeatureProperties feature={feature} />; return <FeatureProperties feature={feature} point={point} />;
}; };
const MultipleFeaturesPopup = ({ features }) => { const MultipleFeaturesPopup = ({ features, points }) => {
const { setPopup } = usePopup(); const { setPopup } = usePopup();
const { selection, include, exclude } = usePointSelection(); const { selection, include, exclude } = usePointSelection();
const { filters, ranges } = usePendingPointsFilters(); const { filters, ranges } = usePendingPointsFilters();
@ -95,8 +50,10 @@ const MultipleFeaturesPopup = ({ features }) => {
<div className="space-y-2 p-1"> <div className="space-y-2 p-1">
{features.map((feature) => { {features.map((feature) => {
const featureId = feature.properties.id; const featureId = feature.properties.id;
const point = points.find(p => p.id = featureId);
const isSelected = (doesMatchFilter(filters, ranges, feature) && !selection.excluded.has(featureId)) || const isSelected = (doesMatchFilter(filters, ranges, feature) && !selection.excluded.has(featureId)) ||
selection.included.has(featureId); selection.included.has(featureId);
console.log(isSelected, doesMatchFilter(filters, ranges, feature));
const handleSelect = () => { const handleSelect = () => {
if (isSelected) { if (isSelected) {
exclude(featureId); exclude(featureId);
@ -127,15 +84,15 @@ const MultipleFeaturesPopup = ({ features }) => {
{feature.properties.category === CATEGORIES.residential ? ( {feature.properties.category === CATEGORIES.residential ? (
<div className="space-x-2 flex items-center w-full"> <div className="space-x-2 flex items-center w-full">
<span className="flex-1 truncate inline-block"> <span className="flex-1 truncate inline-block">
{feature.properties.address} {point.address}
</span> </span>
<span>{feature.properties.name}</span> <span>{point.name}</span>
</div> </div>
) : ( ) : (
<div className="flex w-full"> <div className="flex w-full">
<span className="truncate"> <span className="truncate">
{feature.properties.name ?? feature.properties.category} {point.name ?? point.category}
{feature.properties.category_id && getRivalsName(feature).name} {point.category_id && getRivalsName(feature).name}
</span> </span>
</div> </div>
)} )}
@ -159,19 +116,20 @@ const YandexPanoramaLink = ({ lat, lng }) => {
} }
export const MapPopup = ({ features, lat, lng, onClose }) => { export const MapPopup = ({ features, lat, lng, onClose }) => {
const {data: points, isLoading} = useGetPopupPoints(features);
const getContent = () => { const getContent = () => {
if (features.length === 1) { if (features.length === 1) {
return <SingleFeaturePopup feature={features[0]} />; return <SingleFeaturePopup feature={features[0]} point={points[0]}/>;
} }
return <MultipleFeaturesPopup features={features} />; return <MultipleFeaturesPopup features={features} points={points} />;
}; };
return ( return (
<PopupWrapper lat={lat} lng={lng} onClose={onClose}> <PopupWrapper lat={lat} lng={lng} onClose={onClose}>
<YandexPanoramaLink lat={lat} lng={lng}/> <YandexPanoramaLink lat={lat} lng={lng}/>
{getContent()} {isLoading ? <Spin /> : getContent()}
</PopupWrapper> </PopupWrapper>
); );
}; };

@ -8,13 +8,59 @@ import {
import { Col, Row } from "antd"; import { Col, Row } from "antd";
import { twMerge } from "tailwind-merge"; import { twMerge } from "tailwind-merge";
import { LAYER_IDS } from "../../Layers/constants"; import { LAYER_IDS } from "../../Layers/constants";
import { isNil } from "../../../utils.js"; import {getFilteredGroups, isNil} from "../../../utils.js";
import { useGetRegions } from "../../../components/RegionSelect.jsx"; import { useGetRegions } from "../../../components/RegionSelect.jsx";
import {useOtherGroups, usePostamatesAndPvzGroups} from "../../../api.js";
import {useMemo} from "react";
export const FeatureProperties = ({ feature, dynamicStatus, postamatId, name, groupName }) => { const getRivalsName = (feature) => {
const { data: postamatesAndPvzGroups } = usePostamatesAndPvzGroups();
const { data: otherGroups } = useOtherGroups();
const filteredPostamatesCategories = useMemo(() => {
return getFilteredGroups(postamatesAndPvzGroups);
}, [postamatesAndPvzGroups]);
const filteredOtherCategories = useMemo(() => {
return getFilteredGroups(otherGroups);
}, [otherGroups]);
const filteredPostamatesGroups = useMemo(() => {
if (!filteredPostamatesCategories) return [];
return filteredPostamatesCategories
.map((category) => {
return [...category.groups]
}).flat();
}, [filteredPostamatesCategories]);
const filteredOtherGroups = useMemo(() => {
if (!filteredOtherCategories) return [];
return filteredOtherCategories
.map((category) => {
return [...category.groups]
}).flat();
}, [filteredOtherCategories]);
const isOther = feature.layer?.id.includes(LAYER_IDS.other);
const name = isOther ?
filteredOtherCategories.find(c => c.id === feature.properties.category_id)?.name :
filteredPostamatesCategories.find(c => c.id === feature.properties.category_id)?.name;
const groupName = isOther ?
filteredOtherGroups.find(c => c.id === feature.properties.group_id)?.name :
filteredPostamatesGroups.find(c => c.id === feature.properties.group_id)?.name;
return {
name,
groupName
}
}
export const FeatureProperties = ({ feature, dynamicStatus, postamatId, point }) => {
const { data } = useGetRegions(); const { data } = useGetRegions();
const isResidential = feature.properties.category === CATEGORIES.residential; const isResidential = feature.properties.category === CATEGORIES.residential;
const isWorking = feature.properties.status === STATUSES.working; const isWorking = feature.properties.status === STATUSES.working;
const { name, groupName } = getRivalsName(feature);
const isRivals = const isRivals =
feature.layer?.id.includes(LAYER_IDS.pvz) || feature.layer?.id.includes(LAYER_IDS.pvz) ||
@ -30,7 +76,7 @@ export const FeatureProperties = ({ feature, dynamicStatus, postamatId, name, gr
}; };
const getValue = ({ field, render, empty, type, fallbackField }) => { const getValue = ({ field, render, empty, type, fallbackField }) => {
let value = feature.properties[field]; let value = point ? point[field] : feature.properties[field];
if (field === "category_id") { if (field === "category_id") {
value = name; value = name;
@ -49,7 +95,7 @@ export const FeatureProperties = ({ feature, dynamicStatus, postamatId, name, gr
} }
if (type === "region") { if (type === "region") {
value = value ? value : feature.properties[fallbackField]; value = value ? value : point[fallbackField];
value = render(value, data?.normalized); value = render(value, data?.normalized);
} else { } else {
value = render ? render(value) : value; value = render ? render(value) : value;

@ -10,12 +10,12 @@ import { Button, InputNumber } from "antd";
import { STATUSES } from "../../../config"; import { STATUSES } from "../../../config";
import { isNil } from "../../../utils.js"; import { isNil } from "../../../utils.js";
export const OnApprovalPointPopup = ({ feature }) => { export const OnApprovalPointPopup = ({ feature, point }) => {
const featureId = feature.properties.id; const featureId = feature.properties.id;
const { setClickedPointConfig } = useClickedPointConfig(); const { setClickedPointConfig } = useClickedPointConfig();
const { status: initialStatus, postamat_id: initialPostamatId } = const { status: initialStatus, postamat_id: initialPostamatId } =
feature.properties; point;
const [status, setStatus] = useState(initialStatus); const [status, setStatus] = useState(initialStatus);
const [postamatId, setPostamatId] = useState(initialPostamatId); const [postamatId, setPostamatId] = useState(initialPostamatId);
@ -92,6 +92,7 @@ export const OnApprovalPointPopup = ({ feature }) => {
<> <>
<FeatureProperties <FeatureProperties
feature={feature} feature={feature}
point={point}
dynamicStatus={status} dynamicStatus={status}
postamatId={postamatId} postamatId={postamatId}
/> />

@ -7,7 +7,7 @@ import { useCanEdit } from "../../../api";
import { usePendingPointsFilters } from "../../../stores/usePendingPointsFilters"; import { usePendingPointsFilters } from "../../../stores/usePendingPointsFilters";
import { doesMatchFilter } from "../../../utils.js"; import { doesMatchFilter } from "../../../utils.js";
export const PendingPointPopup = ({ feature }) => { export const PendingPointPopup = ({ feature, point }) => {
const { include, selection, exclude } = usePointSelection(); const { include, selection, exclude } = usePointSelection();
const { setClickedPointConfig } = useClickedPointConfig(); const { setClickedPointConfig } = useClickedPointConfig();
const { filters, ranges } = usePendingPointsFilters(); const { filters, ranges } = usePendingPointsFilters();
@ -35,7 +35,7 @@ export const PendingPointPopup = ({ feature }) => {
return ( return (
<> <>
<FeatureProperties feature={feature} /> <FeatureProperties feature={feature} point={point} />
{canEdit && ( {canEdit && (
<Button <Button
type="primary" type="primary"

@ -2,11 +2,11 @@ import { useClickedPointConfig } from "../../../stores/useClickedPointConfig";
import { useEffect } from "react"; import { useEffect } from "react";
import { FeatureProperties } from "./FeatureProperties"; import { FeatureProperties } from "./FeatureProperties";
export const WorkingPointPopup = ({ feature }) => { export const WorkingPointPopup = ({ feature, point }) => {
const featureId = feature.properties.id; const featureId = feature.properties.id;
const { setClickedPointConfig } = useClickedPointConfig(); const { setClickedPointConfig } = useClickedPointConfig();
useEffect(() => setClickedPointConfig(featureId), [feature]); useEffect(() => setClickedPointConfig(featureId), [feature]);
return <FeatureProperties feature={feature} />; return <FeatureProperties feature={feature} point={point} />;
}; };

@ -218,14 +218,14 @@ export const useGetPendingPointsRange = () => {
select: (data) => { select: (data) => {
const distToGroupsArr = data.dist_to_groups.map((groupRange) => { const distToGroupsArr = data.dist_to_groups.map((groupRange) => {
return { return {
[`d${groupRange.group_id}`]: [Math.floor(groupRange.dist[0]), Math.ceil(groupRange.dist[1])], [`d${groupRange.group_id}`]: [Math.floor(groupRange.dist[0]), Math.min(Math.ceil(groupRange.dist[1]), 4000)],
} }
}); });
const distToGroups = Object.assign({}, ...distToGroupsArr); const distToGroups = Object.assign({}, ...distToGroupsArr);
const rangesArr = RANGE_FILTERS_KEYS.map((key) => { const rangesArr = RANGE_FILTERS_KEYS.map((key) => {
if ((/d[0-9]/.test(key))) return; if ((/d[0-9]/.test(key))) return;
return { return {
[key]: [Math.floor(data[key][0]), Math.ceil(data[key][1])] [key]: [Math.floor(data[key][0]), Math.min(Math.ceil(data[key][1]), 4000)]
} }
}).filter(item => !!item); }).filter(item => !!item);
const ranges = Object.assign({}, ...rangesArr); const ranges = Object.assign({}, ...rangesArr);
@ -238,3 +238,24 @@ export const useGetPendingPointsRange = () => {
} }
); );
}; };
export const useGetPopupPoints = (features) => {
const pointsIds = features.map(f => f.properties.id);
const { data, isInitialLoading, isFetching } = useQuery(
["popup_data", features],
async () => {
const params = new URLSearchParams({
"location_ids[]": pointsIds,
});
const { data } = await api.get(
`/api/placement_points/?${params.toString()}`
);
return data.results;
},
);
return { data, isLoading: isInitialLoading || isFetching };
};

@ -70,11 +70,12 @@ export const doesMatchFilter = (filters, ranges, feature) => {
const doesMatchOtherFilters = () => { const doesMatchOtherFilters = () => {
let res = true; let res = true;
RANGE_FILTERS_KEYS.map((filterKey) => { RANGE_FILTERS_KEYS.map((filterKey) => {
if (fieldHasChanged(filters, ranges, filterKey) && res) { if (fieldHasChanged(filters, ranges, filterKey).result && res) {
res = res =
feature.properties[filterKey] >= filters[`${filterKey}__gt`] && feature.properties[filterKey] >= filters[`${filterKey}__gt`] &&
feature.properties[filterKey] <= filters[`${filterKey}__lt`]; feature.properties[filterKey] <= filters[`${filterKey}__lt`];
} }
}); });
return res; return res;

Loading…
Cancel
Save