Skip to content

Commit

Permalink
Merge pull request #108 from Vizzuality/fix/projects
Browse files Browse the repository at this point in the history
Fix/projects
  • Loading branch information
mluena authored Apr 10, 2024
2 parents 5f5d424 + 00132bc commit 03bf9eb
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 16 deletions.
120 changes: 120 additions & 0 deletions client/src/containers/datasets/layers/projects/hooks.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,134 @@
import { useState } from 'react';

import type { LayerProps } from 'react-map-gl';

import { useAtomValue } from 'jotai';
import { useSetAtom } from 'jotai';

import { hoveredProjectMapAtom } from '@/store';

import { useGetProjects } from '@/types/generated/project';
import type { LayerSettings } from '@/types/layers';

import { useSyncFilters } from '@/hooks/datasets/sync-query';

export function useLayers({
settings: { opacity = 1, visibility = 'visible' },
}: {
settings: { opacity: LayerSettings['opacity']; visibility: LayerSettings['visibility'] };
}): LayerProps[] {
const hoveredProject = useAtomValue(hoveredProjectMapAtom);

// To - Do add search to global state here and in filters component
const [searchValue] = useState<string | null>(null);
const [filtersSettings] = useSyncFilters();
const { data } = useGetProjects(
{
populate: '*',
filters: {
name: {
$containsi: searchValue,
},
countries: {
name: {
$in: filtersSettings?.country,
},
},
intervention_types: {
name: {
$containsi: Array.isArray(filtersSettings?.intervention)
? filtersSettings?.intervention.map((i: string) => i.replace(/-/g, ' '))
: [],
},
},
project_indicator_fields: {
$or: [
{
...(filtersSettings.area_restored?.includes('>500') && {
indicator_name: 'area_reforested_total',
filter_tag: {
$gt: 500,
},
}),
},
{
...(filtersSettings.area_restored?.includes('<200') && {
indicator_name: 'area_reforested_total',
filter_tag: {
$lt: 200,
},
}),
},
{
...(filtersSettings.area_restored?.includes('200-500') && {
indicator_name: 'area_reforested_total',
filter_tag: {
$between: [200, 500],
},
}),
},
{
...(filtersSettings.area_protected?.includes('>500') && {
indicator_name: 'area_protected_total',
filter_tag: {
$gt: 500,
},
}),
},
{
...(filtersSettings.area_protected?.includes('<200') && {
indicator_name: 'area_protected_total',
filter_tag: {
$lt: 200,
},
}),
},
{
...(filtersSettings.area_protected?.includes('200-500') && {
indicator_name: 'area_protected_total',
filter_tag: {
$between: [200, 500],
},
}),
},
{
...(filtersSettings.area_plantation?.includes('>500') && {
indicator_name: 'area_plantation_total',
filter_tag: {
$gt: 500,
},
}),
},
{
...(filtersSettings.area_plantation?.includes('<200') && {
indicator_name: 'area_plantation_total',
filter_tag: {
$lt: 200,
},
}),
},
{
...(filtersSettings.area_plantation?.includes('200-500') && {
indicator_name: 'area_plantation_total',
filter_tag: {
$between: [200, 500],
},
}),
},
],
},
},
},
{
query: {
select: (response) =>
response?.data?.filter((project) => project.attributes?.project_code !== 'AFoCO_global'),
},
}
);

const filteredProjects = data?.map((project) => project.attributes?.project_code);

// The layer is designed to react both to hover events directly on the map and to hover events over a specific project listed in a sidebar.

// Reactivity to Hover Events on the Map:
Expand All @@ -23,6 +140,7 @@ export function useLayers({
{
id: 'projects_points_shadow',
type: 'circle',
filter: ['in', ['get', 'project_code'], ['literal', filteredProjects]],
source: 'projects',
'source-layer': 'areas_centroids_c',
paint: {
Expand All @@ -39,6 +157,7 @@ export function useLayers({
{
id: 'projects_circle',
type: 'circle',
filter: ['in', ['get', 'project_code'], ['literal', filteredProjects]],
source: 'projects',
'source-layer': 'areas_centroids_c',
paint: {
Expand Down Expand Up @@ -93,6 +212,7 @@ export function useLayers({
{
id: 'projects_fill',
type: 'fill',
filter: ['in', ['get', 'project_code'], ['literal', filteredProjects]],
source: 'projects',
'source-layer': 'areas_centroids_l',
paint: {
Expand Down
4 changes: 3 additions & 1 deletion client/src/containers/map/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,10 @@ export default function MapContainer() {

const handleMapClick = useCallback(
(e: MapLayerMouseEvent) => {
const ProjectData = e.features && e.features.find(({ layer }) => layer.id === 'projects');
const ProjectData =
e.features && e.features.find(({ layer }) => layer.id === 'projects_circle');
const ProjectsDataGeometry = e.features?.find(({ layer }) => layer.id === 'projects_fill');

if (e.features && e.features.length && map) {
if (ProjectData || ProjectsDataGeometry) {
push(
Expand Down
27 changes: 16 additions & 11 deletions client/src/containers/projects/detail/panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,17 @@ export default function ProjectDetailPanel() {
<div className="absolute left-0 top-0 w-full">
{data && indicators && (
<div className="relative">
<Image
src={data.main_image?.data?.attributes?.url ?? '/images/projects/placeholder.png'}
alt="AFOCO"
width={300}
height={300}
className="h-52 w-full rounded-t-[24px] object-cover"
/>
{data.main_image?.data?.attributes?.url ? (
<Image
src={data.main_image?.data?.attributes?.url}
alt="AFOCO"
width={300}
height={300}
className="h-52 w-full rounded-t-[24px] object-cover"
/>
) : (
<div className="h-[208px] w-full rounded-t-[24px] bg-gray-200" />
)}

<h2 className="absolute bottom-6 mx-6 text-lg font-semibold leading-6 text-white">
{data?.name}
Expand Down Expand Up @@ -236,10 +240,10 @@ export default function ProjectDetailPanel() {
{!!data?.gallery?.data?.length &&
data.gallery.data.map((img, index) => (
<div key={index}>
{index >= 0 && index % 5 === 0 && (
{index >= 0 && index % 5 === 0 && img?.attributes?.url && (
<div className="flex h-[132px] space-x-1">
<Image
src={img?.attributes?.url ?? '/images/projects/placeholder.png'}
src={img?.attributes?.url}
alt="AFOCO"
width={165}
height={170}
Expand All @@ -256,10 +260,11 @@ export default function ProjectDetailPanel() {
data.gallery.data.map(
(img, index) =>
index > 0 &&
index % 5 != 0 && (
index % 5 != 0 &&
img?.attributes?.url && (
<div key={index} className="flex h-16 w-full space-x-1">
<Image
src={img?.attributes?.url ?? '/images/projects/placeholder.png'}
src={img?.attributes?.url}
alt="AFOCO"
width={165}
height={165}
Expand Down
4 changes: 2 additions & 2 deletions client/src/containers/sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ export default function Sidebar() {
return (
<div className="rounded-8xl absolute bottom-0 left-2 top-0 z-20 my-2 w-16 bg-yellow-700 py-10 text-xs text-yellow-50 xl:left-3 xl:w-20">
<div className="h-[88%]">
<div className="mx-2 flex hidden items-center pb-12 xl:block">
<div className="mx-2 hidden items-center pb-12 xl:block">
<Image src="/images/logo.svg" alt="logo" width={60} height={29} />
</div>

<div className="mx-2 block flex items-center pb-12 xl:hidden">
<div className="mx-2 flex items-center pb-12 xl:hidden">
<Image src="/images/logo.svg" alt="logo" width={48} height={19} />
</div>

Expand Down
31 changes: 29 additions & 2 deletions client/src/hooks/datasets/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,37 @@
import { serialize } from './query-parsers';
import { useSyncFilters, useSyncLayers, useSyncBasemap } from './sync-query';

export const useSyncQueryParams = () => {
// Define a type for the data structure
type QueryParamsData = {
filters: any;
layers: any;
settings: any;
};

// Define a type for the exclusion parameters
type ExcludeParams = {
filters?: boolean;
layers?: boolean;
settings?: boolean;
};

export const useSyncQueryParams = (exclude: ExcludeParams = {}) => {
const [filters] = useSyncFilters();
const [layers] = useSyncLayers();
const [settings] = useSyncBasemap();

return serialize({ filters, layers, settings });
// Construct the data object with correct typing
const data: QueryParamsData = { filters, layers, settings };

// Filter out excluded keys
const result: Partial<QueryParamsData> = {};
Object.keys(data).forEach((key) => {
if (!(key in exclude && exclude[key as keyof ExcludeParams])) {
// Use type assertion here to ensure keys are recognized as valid
result[key as keyof QueryParamsData] = data[key as keyof QueryParamsData];
}
});

// Return the serialized object
return serialize(result);
};

0 comments on commit 03bf9eb

Please sign in to comment.