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

Feature/f 52 current food consumption country view #48

Merged
merged 23 commits into from
Nov 22, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
48af61e
feat: basic choropleth
bohdangarchu Nov 15, 2024
e2dde54
Merge branch 'main' into feature/f-52-current-food-consumption-countr…
bohdangarchu Nov 17, 2024
fda1527
feat: choropleth accordion
bohdangarchu Nov 18, 2024
b7ca36d
fix: accordion and region split
bohdangarchu Nov 19, 2024
fd5a5cc
Merge branch 'main' into feature/f-52-current-food-consumption-countr…
bohdangarchu Nov 19, 2024
8fcec93
Merge branch 'main' into feature/f-52-current-food-consumption-countr…
bohdangarchu Nov 19, 2024
0eee3c9
feat: basic region tooltip
bohdangarchu Nov 19, 2024
5250a34
feat: fcs region tooltip
bohdangarchu Nov 20, 2024
56e9f9b
Merge branch 'main' into feature/f-52-current-food-consumption-countr…
bohdangarchu Nov 20, 2024
6f7dcb7
fix: merge
bohdangarchu Nov 20, 2024
48d6183
feat: refactoring
bohdangarchu Nov 20, 2024
0abee6d
feat: deactivate map layer & refactor
bohdangarchu Nov 21, 2024
4d2ac94
fix: add provider
bohdangarchu Nov 21, 2024
d40894d
feat: disable alert on country zoom
bohdangarchu Nov 21, 2024
e5f5032
feat: show text when no data
bohdangarchu Nov 22, 2024
3c2bcbe
feat: add zoom control
bohdangarchu Nov 22, 2024
37d2b2f
Merge branch 'main' into feature/f-52-current-food-consumption-countr…
bohdangarchu Nov 22, 2024
f6d3b4f
fix: selectedMapVisibility
bohdangarchu Nov 22, 2024
d688355
fix: tooltip
bohdangarchu Nov 22, 2024
d82b8f7
fix: tooltip bug & style on hover
bohdangarchu Nov 22, 2024
f4dd66a
fix: selectable countries with fcs
bohdangarchu Nov 22, 2024
d5ea005
Merge branch 'main' into feature/f-52-current-food-consumption-countr…
bohdangarchu Nov 22, 2024
4d1acf0
fix: chart types
bohdangarchu Nov 22, 2024
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
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@
"editor.tabSize": 2,
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
"files.eol": "\n",
"cSpell.words": ["nextui"]
"cSpell.words": [
"Choropleth",
"nextui"
]
}
9 changes: 6 additions & 3 deletions src/app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as React from 'react';

import { SelectedAlertProvider } from '@/domain/contexts/SelectedAlertContext';
import { SelectedMapProvider } from '@/domain/contexts/SelectedMapContext';
import { SelectedMapVisibilityProvider } from '@/domain/contexts/SelectedMapVisibilityContext';
import { SidebarProvider } from '@/domain/contexts/SidebarContext';

export interface ProvidersProps {
Expand All @@ -22,9 +23,11 @@ export function Providers({ children, themeProps }: ProvidersProps) {
<NextUIProvider navigate={router.push}>
<NextThemesProvider defaultTheme="system" {...themeProps}>
<SidebarProvider>
<SelectedMapProvider>
<SelectedAlertProvider>{children}</SelectedAlertProvider>
</SelectedMapProvider>
<SelectedMapVisibilityProvider>
<SelectedMapProvider>
<SelectedAlertProvider>{children}</SelectedAlertProvider>
</SelectedMapProvider>
</SelectedMapVisibilityProvider>
</SidebarProvider>
</NextThemesProvider>
</NextUIProvider>
Expand Down
3 changes: 2 additions & 1 deletion src/components/Cards/Card.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { Card, CardBody, CardHeader, Image } from '@nextui-org/react';
import { v4 as uuid } from 'uuid';

import { CardProps } from '@/domain/props/CardProps';

Expand All @@ -13,7 +14,7 @@ export default function CustomCard({ title, content }: CardProps) {
<CardBody className="flex flex-col items-center justify-center overflow-visible overflow-y-auto max-h-[300px] py-2">
<div className="flex flex-row gap-6 overflow-y-auto">
{content.map((item) => (
<div key={item.text} className="flex flex-col gap-[3px] items-center">
<div key={uuid()} className="flex flex-col gap-[3px] items-center">
<div className="imageWrapper">
<Image alt={item.altText} className="w-[102px] h-[75px]" src={item.imageSrc} />
</div>
Expand Down
158 changes: 158 additions & 0 deletions src/components/Map/FcsAccordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { Spacer } from '@nextui-org/react';

import FcsAccordionProps from '@/domain/props/FcsAccordionProps';
import { FcsAccordionOperations } from '@/operations/map/FcsAccordionOperations';
import { cardsWrapperClass } from '@/utils/primitives';

import CustomAccordion from '../Accordions/Accordion';
import CustomCard from '../Cards/Card';
import { LineChart } from '../Charts/LineChart';

export default function FcsAccordion({ countryData, loading, countryIso3Data }: FcsAccordionProps) {
const deltaOneMonth = countryData?.fcsMinus1 ? countryData.fcs - countryData.fcsMinus1 : null;
const deltaThreeMonth = countryData?.fcsMinus3 ? countryData.fcs - countryData.fcsMinus3 : null;
const fcsChartData = FcsAccordionOperations.getFcsChartData(countryData);
const rcsiChartData = FcsAccordionOperations.getRcsiChartData(countryData);
const currencyExchangeChartData = FcsAccordionOperations.getCurrencyExchangeChartData(countryIso3Data);
const balanceOfTradeChartData = FcsAccordionOperations.getBalanceOfTradeChartData(countryIso3Data);
const headlineAndFoodInflationChartData =
FcsAccordionOperations.getHeadlineAndFoodInflationChartData(countryIso3Data);
return (
<div className="absolute w-[350px] left-[108px] top-4 z-9999">
<CustomAccordion
loading={loading}
items={[
{
title: 'Food Security',
iconSrc: '/Images/InfoIcon.svg',
content: (
<div className={cardsWrapperClass}>
<CustomCard
title="Population"
content={[
{
imageSrc: '/Images/Population.svg',
text: countryData?.population ? `${countryData.population.toFixed(2)} M` : 'N/A',
altText: 'Population Icon',
},
]}
/>
<CustomCard
title="People with insufficient food consumption"
content={[
{
imageSrc: '/Images/FoodConsumption.svg',
text: countryData?.fcs ? `${countryData.fcs.toFixed(2)} M` : 'N/A',
altText: 'Population Icon',
},
{
imageSrc: deltaOneMonth && deltaOneMonth > 0 ? '/Images/ArrowGreen.svg' : '/Images/ArrowRed.svg',
text: deltaOneMonth ? `${deltaOneMonth.toFixed(2)} M` : 'N/A',
timeText: '1 Months ago',
altText: 'Icon',
},
{
imageSrc:
deltaThreeMonth && deltaThreeMonth > 0 ? '/Images/ArrowGreen.svg' : '/Images/ArrowRed.svg',
text: deltaThreeMonth ? `${deltaThreeMonth.toFixed(2)} M` : 'N/A',
timeText: '3 Month ago',
altText: 'Other Icon',
},
]}
/>
</div>
),
},
{
title: 'Food Security Trends',
iconSrc: '/Images/InfoIcon.svg',
content: (
<div>
{fcsChartData ? (
<LineChart
title="Trend of the number of people with insufficient food consumption"
data={fcsChartData}
expandable
small
/>
) : (
<p>No data about insufficient food consumption</p>
)}
<Spacer y={1} />
{rcsiChartData ? (
<LineChart
title="Trend of the number of people using crisis or above crisis food-based coping"
data={rcsiChartData}
expandable
small
/>
) : (
<p>No data about crisis or above crisis food-based coping</p>
)}
</div>
),
},
{
title: 'Macro-economic',
iconSrc: '/Images/InfoIcon.svg',
content: (
<div className={cardsWrapperClass}>
<CustomCard
title="Import Dependency"
content={[
{
imageSrc: '/Images/Import.svg',
text: countryData?.importDependency
? `${countryData.importDependency.toFixed(1)}% of Cereals`
: 'N/A',
altText: 'Icon',
},
]}
/>
</div>
),
},
{
title: 'Currency Exchange',
iconSrc: '/Images/InfoIcon.svg',
content: (
<div>
{currencyExchangeChartData ? (
<LineChart data={currencyExchangeChartData} expandable small />
) : (
<p>No data about currency exchange</p>
)}
</div>
),
},
{
title: 'Balance of Trade',
iconSrc: '/Images/InfoIcon.svg',
content: (
<div>
{balanceOfTradeChartData ? (
<LineChart data={balanceOfTradeChartData} expandable small />
) : (
<p>No data about balance of trade</p>
)}
</div>
),
},
{
title: 'Headline and food inflation',
iconSrc: '/Images/InfoIcon.svg',
content: (
<div>
{headlineAndFoodInflationChartData ? (
<LineChart data={headlineAndFoodInflationChartData} expandable small />
) : (
<p>No data about headline and food inflation</p>
)}
</div>
),
},
]}
/>
</div>
);
}
66 changes: 66 additions & 0 deletions src/components/Map/FcsChoropleth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';
import L from 'leaflet';
import { useTheme } from 'next-themes';
import React, { useRef, useState } from 'react';
import { GeoJSON, useMap } from 'react-leaflet';

import { CountryData } from '@/domain/entities/country/CountryData';
import { CountryIso3Data } from '@/domain/entities/country/CountryIso3Data';
import FcsChoroplethProps from '@/domain/props/FcsChoroplethProps';
import FcsChoroplethOperations from '@/operations/map/FcsChoroplethOperations';

import FscCountryChoropleth from './FcsCountryChoropleth';

export default function FcsChoropleth({
data,
countryId,
selectedCountryId,
selectedAlert,
setSelectedCountryId,
setSelectedMapVisibility,
toggleAlert,
}: FcsChoroplethProps) {
const geoJsonRef = useRef<L.GeoJSON | null>(null);
const map = useMap();
const { theme } = useTheme();
const [countryData, setCountryData] = useState<CountryData | undefined>();
const [countryIso3Data, setCountryIso3Data] = useState<CountryIso3Data | undefined>();
const [regionData, setRegionData] = useState<FeatureCollection<Geometry, GeoJsonProperties> | undefined>();
const [loading, setLoading] = useState<boolean>(false);

return (
<div>
<GeoJSON
ref={(instance) => {
geoJsonRef.current = instance;
}}
data={data}
style={FcsChoroplethOperations.countryStyle}
onEachFeature={(feature, layer) =>
FcsChoroplethOperations.onEachFeature(
feature,
layer,
map,
selectedAlert,
setSelectedCountryId,
setLoading,
setRegionData,
setCountryData,
setCountryIso3Data,
setSelectedMapVisibility,
toggleAlert,
theme === 'dark'
)
}
/>
{regionData && countryId === selectedCountryId && (
<FscCountryChoropleth
regionData={regionData}
countryData={countryData}
countryIso3Data={countryIso3Data}
loading={loading}
/>
)}
</div>
);
}
25 changes: 25 additions & 0 deletions src/components/Map/FcsCountryChoropleth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { GeoJSON } from 'react-leaflet';

import FscCountryChoroplethProps from '@/domain/props/FcsCountryChoroplethProps';
import { FcsCountryChoroplethOperations } from '@/operations/map/FcsCountryChoroplethOperations';

import FcsAccordion from './FcsAccordion';

export default function FscCountryChoropleth({
regionData,
countryData,
countryIso3Data,
loading,
}: FscCountryChoroplethProps) {
return (
<div>
<FcsAccordion countryData={countryData} countryIso3Data={countryIso3Data} loading={loading} />
<GeoJSON
data={regionData}
style={FcsCountryChoroplethOperations.styleFunction}
onEachFeature={(feature, layer) => FcsCountryChoroplethOperations.onEachFeature(feature, layer, regionData)}
/>
</div>
);
}
61 changes: 61 additions & 0 deletions src/components/Map/FcsRegionTooltip.tsx
bohdangarchu marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { Feature, GeoJsonProperties, Geometry } from 'geojson';

import { FcsRegionTooltipOperations } from '@/operations/map/FcsRegionTooltipOperations';
import { formatToMillion } from '@/utils/formatting';

import { LineChart } from '../Charts/LineChart';

interface FcsRegionTooltipProps {
feature: Feature<Geometry, GeoJsonProperties>;
}

export default function FcsRegionTooltip({ feature }: FcsRegionTooltipProps) {
const fcsPeople = feature.properties?.fcs?.people;
const fcsMillion = fcsPeople ? formatToMillion(fcsPeople) : null;
const fcsRatio = feature?.properties?.fcs?.ratio;
const rcsiPeople = feature.properties?.rcsi?.people;
const rcsiMillion = rcsiPeople ? formatToMillion(rcsiPeople) : null;
const rcsiRatio = feature?.properties?.rcsi?.ratio || null;
return (
<div className="bg-background text-foreground rounded-md shadow-md max-w-sm z-9999">
<div className="px-4 pt-4">
<h3 className="text-lg text-foreground font-bold">{feature.properties?.Name}</h3>
<div className="mt-2 text-foreground">
<p>
{fcsMillion && fcsRatio ? (
<>
<span className="text-clusterOrange font-bold">
{fcsRatio}% ({fcsMillion}M){' '}
</span>
with insufficient food consumption
</>
) : (
'No data about insufficient food consumption'
)}
</p>
<p>
{rcsiRatio && rcsiMillion ? (
<>
<span className="text-clusterOrange font-bold">
{rcsiRatio}% ({rcsiMillion}M){' '}
</span>
with crisis or above crisis food-based coping
</>
) : (
'No data about crisis or above crisis food-based coping'
)}
</p>
</div>
</div>
<div className="px-1">
{feature.properties?.fcsGraph && (
<LineChart
title="Number of people with insufficient food consumption"
data={FcsRegionTooltipOperations.getFcsChartData(feature.properties.fcsGraph)}
small
/>
)}
</div>
</div>
);
}
Loading