diff --git a/next.config.js b/next.config.js index 1e801f0b..fbffc9ab 100644 --- a/next.config.js +++ b/next.config.js @@ -1,15 +1,16 @@ /** @type {import('next').NextConfig} */ const nextConfig = { - webpack(config, { isServer }) { - // Add rule to handle SVG imports - config.module.rules.push({ - test: /\.svg$/, - use: ['@svgr/webpack', 'url-loader'], - }); - config.resolve.alias.canvas = false; - - return config; - }, - }; - - module.exports = nextConfig; + webpack(config, { isServer }) { + // Add rule to handle SVG imports + config.module.rules.push({ + test: /\.svg$/, + use: ['@svgr/webpack', 'url-loader'], + }); + config.resolve.alias.canvas = false; + + return config; + }, + cacheHandler: require.resolve('next/dist/server/lib/incremental-cache/file-system-cache.js'), +}; + +module.exports = nextConfig; diff --git a/src/app/error.tsx b/src/app/error.tsx index 6847fc17..64f60e16 100644 --- a/src/app/error.tsx +++ b/src/app/error.tsx @@ -2,7 +2,10 @@ import { useEffect } from 'react'; -export default function Error({ error, reset }: { error: Error; reset: () => void }) { +import { CustomButton } from '@/components/Buttons/CustomButton'; +import { Topbar } from '@/components/Topbar/Topbar'; + +export default function Error({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) { useEffect(() => { // Log the error to an error reporting service /* eslint-disable no-console */ @@ -10,17 +13,24 @@ export default function Error({ error, reset }: { error: Error; reset: () => voi }, [error]); return ( -
-

Something went wrong!

- +
+ +
+
+

An Error Occurred

+

Please try again or go back to the Home page.

+
+
+ reset()}> + Try again + + + + Go to Home page + + +
+
); } diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx new file mode 100644 index 00000000..c3655215 --- /dev/null +++ b/src/app/not-found.tsx @@ -0,0 +1,25 @@ +'use client'; + +import { CustomButton } from '@/components/Buttons/CustomButton'; +import { Topbar } from '@/components/Topbar/Topbar'; + +export default function NotFound() { + return ( +
+ +
+
+

Ooops

+

The requested page could not be found.

+
+
+ + + Go to Home page + + +
+
+
+ ); +} diff --git a/src/components/Legend/GradientLegend.tsx b/src/components/Legend/GradientLegend.tsx index 79a673ce..b7f9e7d5 100644 --- a/src/components/Legend/GradientLegend.tsx +++ b/src/components/Legend/GradientLegend.tsx @@ -1,13 +1,20 @@ +import clsx from 'clsx'; + +import { ColorsData } from '@/domain/props/ColorsData'; import GradientLegendProps from '@/domain/props/GradientLegendProps'; -export default function GradientLegend({ colors, startLabel, endLabel, hasNotAnalyzedPoint }: GradientLegendProps) { - const gradients: string = colors - .map((color: string, index: number) => { - const percentage = (index / (colors.length - 1)) * 100; - return `hsl(var(--nextui-${color})) ${percentage}%`; +import { Tooltip } from '../Tooltip/Tooltip'; + +export default function GradientLegend({ colorsData, startLabel, endLabel, hasNotAnalyzedPoint }: GradientLegendProps) { + const gradients: string = colorsData + .map((colorData: ColorsData, index: number) => { + const percentage = (index / (colorsData.length - 1)) * 100; + return `hsl(var(--nextui-${colorData.color})) ${percentage}%`; }) .join(', '); + const segmentWidth: number = 100 / colorsData.length; + return (
{hasNotAnalyzedPoint && ( @@ -20,12 +27,31 @@ export default function GradientLegend({ colors, startLabel, endLabel, hasNotAna
)} -
+
+ {colorsData.map((colorData, index) => ( +
+ +
+ +
+ ))} +
+
{startLabel} {endLabel} diff --git a/src/components/Map/Map.tsx b/src/components/Map/Map.tsx index 195ebbed..77567b5a 100644 --- a/src/components/Map/Map.tsx +++ b/src/components/Map/Map.tsx @@ -84,6 +84,9 @@ export default function Map({ countries, disputedAreas, fcsData, alertData }: Ma setRegionNutritionData, setIpcRegionData ); + window.gtag('event', `${selectedCountryData.properties.iso3}_country_selected`, { + selectedMap: selectedMapType, + }); setSelectedCountryName(selectedCountryData.properties.adm0_name); mapRef.current?.fitBounds(L.geoJSON(selectedCountryData as GeoJSON).getBounds(), { animate: true }); } diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx index b1b532a0..231b9ac6 100644 --- a/src/components/Tooltip/Tooltip.tsx +++ b/src/components/Tooltip/Tooltip.tsx @@ -1,4 +1,5 @@ import { Tooltip as NextUITooltip } from '@nextui-org/tooltip'; +import clsx from 'clsx'; import TooltipProps from '@/domain/props/TooltipProps'; @@ -11,9 +12,11 @@ import TooltipProps from '@/domain/props/TooltipProps'; * @param text textual content of the tooltip * @param delay delay with which tooltip appears on hover in milliseconds; default is 0 * @param warning selected if the tooltip should be highlighted (optional) + * @param titleStyle tailwind classes to style the title (optional) + * @param textStyle tailwind classes to style the text (optional) * @constructor */ -export function Tooltip({ children, title, text, delay, warning }: TooltipProps) { +export function Tooltip({ children, title, text, delay, warning, titleStyle, textStyle }: TooltipProps) { const OFFSET: number = 10; const RADIUS = 'sm'; const SHADOW = 'md'; @@ -23,11 +26,11 @@ export function Tooltip({ children, title, text, delay, warning }: TooltipProps) const tooltipContent = title ? (
-

{title}

-

{text}

+

{title}

+

{text}

) : ( -

{text}

+

{text}

); return ( diff --git a/src/domain/constant/legend/mapLegendData.tsx b/src/domain/constant/legend/mapLegendData.tsx index 17cdb644..6dff0678 100644 --- a/src/domain/constant/legend/mapLegendData.tsx +++ b/src/domain/constant/legend/mapLegendData.tsx @@ -106,7 +106,15 @@ export function mapLegendData( case GlobalInsight.FOOD: legendData.push({ title: 'Prevalence of insufficient food consumption', - colors: ['fcsGradient1', 'fcsGradient2', 'fcsGradient3', 'fcsGradient4', 'fcsGradient5', 'fcsGradient6'], + colorsData: [ + { color: 'fcsGradient1', title: 'Very Low', value: '0-5%' }, + { color: 'fcsGradient2', title: 'Low', value: '5-10%' }, + { color: 'fcsGradient3', title: 'Moderately Low', value: '10-20%' }, + { color: 'fcsGradient4', title: 'Moderately High', value: '20-30%' }, + { color: 'fcsGradient5', title: 'High', value: '30-40%' }, + { color: 'fcsGradient6', title: 'Very High', value: 'Above 40%' }, + ], + startLabel: '0%', endLabel: 'above 40%', popoverInfo: ( @@ -158,17 +166,18 @@ export function mapLegendData( case GlobalInsight.RAINFALL: legendData.push({ title: 'Rainfall', - colors: [ - 'vegetationGradient1', - 'vegetationGradient2', - 'vegetationGradient3', - 'vegetationGradient4', - 'vegetationGradient5', - 'rainfallGradient6', - 'rainfallGradient7', - 'rainfallGradient8', - 'rainfallGradient9', + colorsData: [ + { color: 'vegetationGradient1', value: '<40%' }, + { color: 'vegetationGradient2', value: '40-60%' }, + { color: 'vegetationGradient3', value: '60-80%' }, + { color: 'vegetationGradient4', value: '80-90%' }, + { color: 'vegetationGradient5', value: '90-110%' }, + { color: 'rainfallGradient6', value: '110-120%' }, + { color: 'rainfallGradient7', value: '120-140%' }, + { color: 'rainfallGradient8', value: '140-180%' }, + { color: 'rainfallGradient9', value: '>180%' }, ], + startLabel: '<40%', endLabel: '>180%', popoverInfo: ( @@ -196,17 +205,18 @@ export function mapLegendData( case GlobalInsight.VEGETATION: legendData.push({ title: 'Vegetation', - colors: [ - 'vegetationGradient1', - 'vegetationGradient2', - 'vegetationGradient3', - 'vegetationGradient4', - 'vegetationGradient5', - 'vegetationGradient6', - 'vegetationGradient7', - 'vegetationGradient8', - 'vegetationGradient9', + colorsData: [ + { color: 'vegetationGradient1', value: '<50%' }, + { color: 'vegetationGradient2', value: '50-70%' }, + { color: 'vegetationGradient3', value: '70-80%' }, + { color: 'vegetationGradient4', value: '80-90%' }, + { color: 'vegetationGradient5', value: '90-110%' }, + { color: 'vegetationGradient6', value: '110-120%' }, + { color: 'vegetationGradient7', value: '120-130%' }, + { color: 'vegetationGradient8', value: '130-150%' }, + { color: 'vegetationGradient9', value: '>150%' }, ], + startLabel: '<50%', endLabel: '>150%', popoverInfo: ( @@ -236,14 +246,14 @@ export function mapLegendData( legendData.push({ title: 'Number of people in IPC/CH Phase 3 or above (millions)', hasNotAnalyzedPoint: true, - colors: [ - 'ipcGradient1', - 'ipcGradient2', - 'ipcGradient3', - 'ipcGradient4', - 'ipcGradient5', - 'ipcGradient6', - 'ipcGradient7', + colorsData: [ + { color: 'ipcGradient1', value: '0-0.099' }, + { color: 'ipcGradient2', value: '0.1-0.49' }, + { color: 'ipcGradient3', value: '0.5-0.99' }, + { color: 'ipcGradient4', value: '1.0-2.99' }, + { color: 'ipcGradient5', value: '3.0-4.99' }, + { color: 'ipcGradient6', value: '5.0-9.99' }, + { color: 'ipcGradient7', value: '>10' }, ], startLabel: '0', endLabel: '>10', @@ -356,7 +366,13 @@ export function mapLegendData( } else { legendData.push({ title: 'Risk of Inadequate Micronutrient Intake', - colors: ['ipcGradient1', 'ipcGradient2', 'ipcGradient3', 'ipcGradient4', 'ipcGradient5'], + colorsData: [ + { color: 'ipcGradient1', title: 'Lowest', value: '0-19%' }, + { color: 'ipcGradient2', title: 'Low', value: '20-39%' }, + { color: 'ipcGradient3', title: 'Moderate', value: '40-59%' }, + { color: 'ipcGradient4', title: 'High', value: '60-79%' }, + { color: 'ipcGradient5', title: 'Highest', value: '80-100%' }, + ], startLabel: '0%', endLabel: '100%', popoverInfo: ( diff --git a/src/domain/props/ColorsData.tsx b/src/domain/props/ColorsData.tsx new file mode 100644 index 00000000..786f491b --- /dev/null +++ b/src/domain/props/ColorsData.tsx @@ -0,0 +1,5 @@ +export interface ColorsData { + color: string; + title?: string; + value: string; +} diff --git a/src/domain/props/GradientLegendContainerItem.ts b/src/domain/props/GradientLegendContainerItem.ts index cc301b29..c8df3110 100644 --- a/src/domain/props/GradientLegendContainerItem.ts +++ b/src/domain/props/GradientLegendContainerItem.ts @@ -1,7 +1,9 @@ import { ReactNode } from 'react'; +import { ColorsData } from './ColorsData'; + export interface GradientLegendContainerItem { - colors: string[]; + colorsData: ColorsData[]; title: string; startLabel: string; endLabel: string; diff --git a/src/domain/props/GradientLegendProps.ts b/src/domain/props/GradientLegendProps.ts index c10256fd..bf76f913 100644 --- a/src/domain/props/GradientLegendProps.ts +++ b/src/domain/props/GradientLegendProps.ts @@ -1,5 +1,7 @@ +import { ColorsData } from './ColorsData'; + export default interface GradientLegendProps { - colors: string[]; + colorsData: ColorsData[]; startLabel: string; endLabel: string; hasNotAnalyzedPoint?: boolean; diff --git a/src/domain/props/TooltipProps.tsx b/src/domain/props/TooltipProps.tsx index 325d7473..9879bae0 100644 --- a/src/domain/props/TooltipProps.tsx +++ b/src/domain/props/TooltipProps.tsx @@ -6,4 +6,6 @@ export default interface TooltipProps { text: string; delay?: number; warning?: boolean; + titleStyle?: string; + textStyle?: string; } diff --git a/src/operations/legends/LegendOperations.ts b/src/operations/legends/LegendOperations.ts index bbe17c2a..b025e051 100644 --- a/src/operations/legends/LegendOperations.ts +++ b/src/operations/legends/LegendOperations.ts @@ -6,8 +6,8 @@ export class LegendOperations { return false; } return ( - Array.isArray((value as GradientLegendContainerItem).colors) && - (value as GradientLegendContainerItem).colors.every((color) => typeof color === 'string') + Array.isArray((value as GradientLegendContainerItem).colorsData) && + (value as GradientLegendContainerItem).colorsData.every((colorsData) => typeof colorsData.color === 'string') ); } }