Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/projects #108

Merged
merged 3 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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'}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so this placeholder.png can be removed from code

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);
};
Loading