diff --git a/src/App.tsx b/src/App.tsx index 27229a1..c4f92ac 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,583 +1,137 @@ -/* eslint-disable react/jsx-key */ -/* eslint-disable no-irregular-whitespace */ -/* eslint-disable react/no-unescaped-entities */ -/* eslint-disable no-plusplus */ -import React, { useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { csv } from 'd3-fetch'; -import { Segmented, Radio } from 'antd'; -import styled from 'styled-components'; -import { Leaf, School, BriefcaseBusiness, Flag, Shell } from 'lucide-react'; import { ChoroplethMap } from './Components/Graphs/Maps/ChoroplethMap'; -import World from './Components/Graphs/Maps/MapData/worldMap.json'; +import Header from './Components/Header'; +import FilterCountryGroup from './Components/Filter'; +import { PROGRAMMES } from './Components/Constants'; +import { ProgrammeProvider, useProgramme } from './Components/ProgrammeContext'; +import CheckboxGroup from './Components/CheckboxGroup'; import { ChoroplethMapDataType } from './Types'; -import { FIELDS } from './Utils/constants'; -import Table from './Components/Graphs/Table'; - -const StyledSpan = styled.span` - position: relative; - line-height: 1.5; - border: 1px solid var(--gray-300); - background-color: var(--white); - padding: 1px 12px 2px 12px; - - &::before { - content: ''; - position: relative; - display: inline-block; - vertical-align: middle; // Vertically aligns the circle with the text - margin-right: 2px; // Space between the circle and the text - width: 10px; - height: 10px; - background-color: var(--blue-600); - } -`; - -const programmeOptions = [ - { - label: 'All Sustainable Financial Programmes', - value: 'public_finance_budget', - color: '#006EB5', - icon: Leaf, - }, - { - label: 'Public finance for the SDGs', - value: 'public_finance_tax', - color: '#5DD4F0', - icon: School, - }, - { - label: 'Unlocking private capital and aligning for the SDGs', - value: 'public_finance_debt', - color: '#02A38A', - icon: BriefcaseBusiness, - }, - { - label: 'Integrated National Financing Frameworks', - value: 'insurance_and_risk_finance', - color: '#E78625', - icon: Flag, - }, - { - label: 'Biofin', - value: 'private_capital', - color: '#E0529E', - icon: Shell, - }, -]; - -const StyledSegmented = styled(Segmented)<{ selectedValue: string }>` - background-color: white !important; - display: flex; - width: 100%; - - .ant-segmented-item { - border-radius: 0px !important; - width: 20%; - background-color: var(--gray-200) !important; - flex: 1; - color: var(--gray-700); - display: flex; - align-items: center; - justify-content: flex-start; - height: 68px; - &:hover { - background-color: var( - --gray-300 - ) !important; // Change hover background color - } - &:not(:last-child) { - margin-right: 8px; // Add gap between items except the last one - } - .ant-segmented-item-label { - white-space: normal; - display: flex; - gap: 16px; - padding: 0 16px; - align-items: center; - } - &.ant-segmented-item-selected { - background-color: ${({ selectedValue }) => { - const selectedOption = programmeOptions.find( - option => option.value === selectedValue, - ); - return selectedOption ? selectedOption.color : 'var(--blue-600)'; - }} !important; // Change selected item background color - color: white !important; // Change text color for selected item - } - } -`; - -const IconWrapper = styled.span<{ color: string }>` - display: inline-flex; - align-items: center; - justify-content: center; - width: 32px; // Adjust the size as needed - height: 32px; // Adjust the size as needed - border-radius: 100px; - background-color: ${({ color }) => `${color}33`}; - color: ${({ color }) => color}; - margin-right: 8px; // Space between the icon and the label -`; - -interface ProgrammeDetail { - column: string; - note: string; - key: string; -} - -interface Counts { - countriesTotal: number; - countriesPublicTotal: number; - countriesPublicBudget: number; - countriesPublicTax: number; - countriesPublicDebt: number; - countriesPublicRisk: number; - countriesPrivate: number; - fragileTotal: number; -} - -const initialState: Counts = { - countriesTotal: 0, - countriesPublicTotal: 0, - countriesPublicBudget: 0, - countriesPublicTax: 0, - countriesPublicDebt: 0, - countriesPublicRisk: 0, - countriesPrivate: 0, - fragileTotal: 0, -}; - -interface ColumnDescription { - [key: string]: { - text: string; - count: number; - }; -} - -type SegmentedValue = string | number; - -function App() { - const [data, setData] = useState([]); - const [selectedColumn, setSelectedColumn] = useState( - 'public_finance_budget', - ); - const [filter, setFilter] = useState('All'); - const [counts, setCounts] = useState(initialState); +function AppContent() { + const [data, setData] = useState([]); + const [selectedRadio, setSelectedRadio] = useState('allCountries'); + const [selectedCheckboxes, setSelectedCheckboxes] = useState([]); + const { currentProgramme, setCurrentProgramme } = useProgramme(); useEffect(() => { csv( `https://raw.githubusercontent.com/UNDP-Data/dv-sustainable-finance-hub-data-repo/main/SFH_data.csv`, ) - .then((loadedData: any[]) => { - const countryFlagsAny = new Set(); - const countryFlagsFinance = new Set(); - const fragileCountries = new Set(); - const newCounts = { ...initialState }; - - const transformedData = loadedData.map(d => { - const budget = +d.public_finance_budget; - const tax = +d.public_finance_tax; - const debt = +d.public_finance_debt; - const insurance = +d.insurance_and_risk_finance; - const capital = +d.private_capital; - const programmes: ProgrammeDetail[] = []; - - FIELDS.forEach(field => { - if (+d[field.key] === 1) { - // Ensure the value is treated as a number - programmes.push({ - key: field.key, - column: field.label, - note: d[field.noteKey], - }); - } - }); - - const isFragile = - !!d.WB_Fragile_and_Crisis_Affected_Countries || - !!d.crisis_level || - !!d.OECD_Multidimensional_Fragility_Framework; - - if (isFragile) { - fragileCountries.add(d['Country ISO code']); - } - - if (budget === 1) { - newCounts.countriesPublicBudget++; - countryFlagsFinance.add(d['Country ISO code']); - } - if (tax === 1) { - newCounts.countriesPublicTax++; - countryFlagsFinance.add(d['Country ISO code']); - } - if (debt === 1) { - newCounts.countriesPublicDebt++; - countryFlagsFinance.add(d['Country ISO code']); - } - if (insurance === 1) { - newCounts.countriesPublicRisk++; - countryFlagsFinance.add(d['Country ISO code']); - } - if (capital === 1) { - newCounts.countriesPrivate++; - countryFlagsAny.add(d['Country ISO code']); - } - - if ( - budget === 1 || - tax === 1 || - debt === 1 || - insurance === 1 || - capital === 1 - ) { - countryFlagsAny.add(d['Country ISO code']); - } - - const countryInfo = World.features.find( - feature => feature.properties.ISO3 === d['Country ISO code'], - ); - - return { - ...d, - countryCode: d['Country ISO code'], - countryName: countryInfo ? countryInfo.properties.NAME : 'Unknown', // Fallback to 'Unknown' if not found - x: +d[selectedColumn], - programmes, - isFragile, - }; - }); - - setData(transformedData); - newCounts.countriesTotal = countryFlagsAny.size; - newCounts.fragileTotal = fragileCountries.size; - newCounts.countriesPublicTotal = countryFlagsFinance.size; - setCounts(newCounts); + .then((loadedData: any) => { + setData(loadedData); + console.log(loadedData); }) .catch((err: any) => { - // eslint-disable-next-line no-console console.error('Error loading the CSV file:', err); }); - }, [selectedColumn]); + }, []); - const handleChange = (value: SegmentedValue) => { - setSelectedColumn(value); + useEffect(() => { + if (currentProgramme.subcategories) { + setSelectedCheckboxes( + currentProgramme.subcategories.map(sub => sub.value), + ); + } + }, [currentProgramme]); + + const handleSegmentChange = (value: string | number) => { + const programme = PROGRAMMES.find(p => p.value === value); + if (programme) { + setCurrentProgramme(programme); + if (programme.subcategories) { + setSelectedCheckboxes(programme.subcategories.map(sub => sub.value)); + } else { + setSelectedCheckboxes([]); + } + } }; - const fragilityOptions = [ - { label: 'All Countries', value: 'All' }, - { label: 'Fragile and Crisis Affected', value: 'Fragile' }, - { label: 'LDC', value: 'LDC' }, - { label: 'SIDS', value: 'SIDS' }, - ]; + const handleRadioChange = (value: string) => { + setSelectedRadio(value); + }; - const tooltip = (d: any) => { - return ( -
- {d.programmes.length > 0 && ( - <> -
-
- {d.countryName} -
-
-
-
-
-
-

- Available programmes -

- {d.programmes.map((p: any) => ( -

- • {p.column}{' '} - {p.key === selectedColumn && p.note ? '*' : ''} -

- ))} -
- {d.programmes.some( - (p: any) => p.key === selectedColumn && p.note, - ) && ( -
- {d.programmes - .filter((p: any) => p.key === selectedColumn && p.note) - .map((p: any) => ( -

- * {p.note} -

- ))} -
- )} - {/* Access Fragility Columns Directly */} - {(d.WB_Fragile_and_Crisis_Affected_Countries || - d.crisis_level || - d.OECD_Multidimensional_Fragility_Framework) && ( -
-

- Fragility Information: -

- {d.OECD_Multidimensional_Fragility_Framework && ( -

- • OECD Multidimensional Fragility Framework :{' '} - {d.OECD_Multidimensional_Fragility_Framework} -

- )} - {d.WB_Fragile_and_Crisis_Affected_Countries && ( -

- • World Bank Fragile and Crisis Affected Countries :{' '} - {d.WB_Fragile_and_Crisis_Affected_Countries} -

- )} - {d.crisis_level && ( -

- • Crisis Level: {d.crisis_level} -

- )} -
- )} -
-
-
- - )} -
- ); + const handleCheckboxChange = (checkedValues: string[]) => { + setSelectedCheckboxes(checkedValues); }; - const columnDescriptions: ColumnDescription = { - public_finance_budget: { - text: 'budgeting', - count: counts.countriesPublicBudget, - }, - public_finance_tax: { - text: 'taxation', - count: counts.countriesPublicTax, - }, - public_finance_debt: { - text: 'debt', - count: counts.countriesPublicDebt, - }, - insurance_and_risk_finance: { - text: 'insurance and risk', - count: counts.countriesPublicRisk, - }, - private_capital: { - text: 'private capital', - count: counts.countriesPrivate, - }, + const transformData = ( + rawData: any[], + programme: string, + countryGroup: string, + ): ChoroplethMapDataType[] => { + const filteredData = rawData.filter(item => { + if (countryGroup === 'allCountries') return true; + return item[countryGroup] === '1'; + }); + + return filteredData.map(item => ({ + x: parseFloat(item[programme]), // Assuming the data in the programme column can be parsed as a number + countryCode: item.iso, + data: { + country: item.country, + programmeValue: item[programme], + }, + })); }; - const filteredData = - filter === 'Fragile' ? data.filter(d => d.isFragile) : data; - return ( -
-
-
-

- Sustainable Finance Hub{' '} -

-
-
- ({ - label: ( - <> - - - -
- {option.label} -
- - ), - value: option.value, - }))} - value={selectedColumn} - onChange={handleChange} - selectedValue={selectedColumn as string} - /> -
-
-
-
+ const transformedData = transformData( + data, + currentProgramme.value, + selectedRadio, + ); -
+ return ( +
+
+
-
-
-

Filter by country gropup

- setFilter(e.target.value)} - value={filter} - className='undp-radio' - > - {fragilityOptions.map(option => ( -
- - {option.label} - -
- ))} -
-
-
-
-
-

{counts.countriesTotal}

-

- countries with sustainable -
- finance programming in total -

-
-
-

- {columnDescriptions[selectedColumn].count} -

-

- countries with programmes -
- related to{' '} - - {' '} - {columnDescriptions[selectedColumn].text} - -

-
-
-

XX

-

- any additional number -
- can be placed here -

-
-
+ + {currentProgramme?.subcategories && ( + ({ + label: sub.label, + value: sub.value, + }), + )} + onChange={handleCheckboxChange} + value={selectedCheckboxes} + /> + )}
({ - ...d, - x: d[selectedColumn], - }))} - backgroundColor='var(--white)' - centerPoint={[430, 360]} - scale={250} - height={600} - // domain={[0, 1, 2, 3, 4]} - tooltip={tooltip} + data={transformedData} + width={1000} + height={550} + scale={270} + centerPoint={[470, 380]} />
- ); } +function App() { + return ( + + + + ); +} + export default App; diff --git a/src/Components/CheckboxGroup.tsx b/src/Components/CheckboxGroup.tsx new file mode 100644 index 0000000..7de8286 --- /dev/null +++ b/src/Components/CheckboxGroup.tsx @@ -0,0 +1,28 @@ +import { Checkbox, CheckboxOptionType } from 'antd'; + +interface CheckboxGroupProps { + options: CheckboxOptionType[]; + value: string[]; + onChange: (checkedValues: string[]) => void; +} + +function CheckboxGroup(props: CheckboxGroupProps): JSX.Element { + const { options, value, onChange } = props; + + return ( +
+

Filter by subprogrammes

+ onChange(checkedValues as string[])} + /> +
+ ); +} + +export default CheckboxGroup; diff --git a/src/Components/Constants.tsx b/src/Components/Constants.tsx new file mode 100644 index 0000000..5d263d3 --- /dev/null +++ b/src/Components/Constants.tsx @@ -0,0 +1,63 @@ +import { Leaf, School, BriefcaseBusiness, Flag, Shell } from 'lucide-react'; + +// constants.ts +export interface Programme { + label: string; + value: string; + color: string; + icon: any; + subcategories?: Subcategory[]; +} + +export interface Subcategory { + label: string; + value: string; +} + +export const PROGRAMMES: Programme[] = [ + { + label: 'All Sustainable Financial Programmes', + value: 'all_programmes', + color: '#006EB5', + icon: Leaf, + }, + { + label: 'Public finance for the SDGs', + value: 'public', + color: '#5DD4F0', + icon: School, + subcategories: [ + { label: 'Sub Programme 1.1', value: 'subProgramme1.1' }, + { label: 'Sub Programme 1.2', value: 'subProgramme1.2' }, + ], + }, + { + label: 'Unlocking private capital and aligning for the SDGs', + value: 'private', + color: '#02A38A', + icon: BriefcaseBusiness, + subcategories: [ + { label: 'Sub Programme 2.1', value: 'subProgramme2.1' }, + { label: 'Sub Programme 2.2', value: 'subProgramme2.2' }, + ], + }, + { + label: 'Integrated National Financing Frameworks', + value: 'frameworks', + color: '#E78625', + icon: Flag, + }, + { + label: 'Biofin', + value: 'biofin', + color: '#E0529E', + icon: Shell, + }, +]; + +export const GROUPS = [ + { label: 'All Countries', value: 'allCountries' }, + { label: 'Fragile and Affected', value: 'fragile_affected' }, + { label: 'LDC', value: 'ldc' }, + { label: 'SIDS', value: 'sids' }, +]; diff --git a/src/Components/Filter.tsx b/src/Components/Filter.tsx new file mode 100644 index 0000000..4841ba1 --- /dev/null +++ b/src/Components/Filter.tsx @@ -0,0 +1,34 @@ +import { Radio } from 'antd'; +import styled from 'styled-components'; +import { GROUPS } from './Constants'; + +const StyledRadioGroup = styled(Radio.Group)` + display: flex; + flex-direction: column; // Display items in one column +`; + +interface CountryGroupProps { + onRadioChange: (value: string) => void; // Callback function prop for Radio Group + selectedRadio: string; // Selected radio value +} + +function FilterCountryGroup(props: CountryGroupProps): JSX.Element { + const { onRadioChange, selectedRadio } = props; + + return ( +
+

Filter by country group

+ onRadioChange(e.target.value)} + className='undp-radio' + /> +
+ ); +} + +export default FilterCountryGroup; diff --git a/src/Components/Graphs/Maps/ChoroplethMap/Graph.tsx b/src/Components/Graphs/Maps/ChoroplethMap/Graph.tsx index fd46f72..5a761de 100644 --- a/src/Components/Graphs/Maps/ChoroplethMap/Graph.tsx +++ b/src/Components/Graphs/Maps/ChoroplethMap/Graph.tsx @@ -2,20 +2,15 @@ import { useEffect, useRef, useState } from 'react'; import { geoEqualEarth } from 'd3-geo'; import { zoom } from 'd3-zoom'; import { select } from 'd3-selection'; -// import { scaleThreshold, scaleOrdinal } from 'd3-scale'; import UNDPColorModule from 'undp-viz-colors'; import World from '../MapData/worldMap.json'; import { ChoroplethMapDataType } from '../../../../Types'; -// import { numberFormattingFunction } from '../../../../Utils/numberFormattingFunction'; import { Tooltip } from '../../../Elements/Tooltip'; interface Props { - // domain: number[]; width: number; height: number; - // colors: string[]; - // colorLegendTitle?: string; - // categorical?: boolean; + colors: string[]; data: ChoroplethMapDataType[]; scale: number; centerPoint: [number, number]; @@ -26,10 +21,7 @@ interface Props { export function Graph(props: Props) { const { data, - // domain, - // colors, - // colorLegendTitle, - // categorical, + colors, height, width, scale, @@ -37,9 +29,6 @@ export function Graph(props: Props) { tooltip, onSeriesMouseOver, } = props; - // const [selectedColor, setSelectedColor] = useState( - // undefined, - // ); const [mouseOverData, setMouseOverData] = useState(undefined); const [eventX, setEventX] = useState(undefined); const [eventY, setEventY] = useState(undefined); @@ -51,9 +40,6 @@ export function Graph(props: Props) { .rotate([0, 0]) .scale(scale) .translate(centerPoint); - // const colorScale = categorical - // ? scaleOrdinal().domain(domain).range(colors) - // : scaleThreshold().domain(domain).range(colors); useEffect(() => { const mapGSelect = select(mapG.current); @@ -70,6 +56,9 @@ export function Graph(props: Props) { // eslint-disable-next-line @typescript-eslint/no-explicit-any mapSvgSelect.call(zoomBehaviour as any); }, [svgHeight, svgWidth]); + + const programmeColor = colors[0]; + return ( <> ); }) @@ -130,10 +119,10 @@ export function Graph(props: Props) { key={j} d={path} style={{ - stroke: 'var(--gray-300)', + stroke: 'var(--gray-500)', }} - strokeWidth={0.4} - fill='var(--gray-200)' + strokeWidth={0.25} + fill={UNDPColorModule.graphNoData} /> ); })} @@ -144,17 +133,17 @@ export function Graph(props: Props) { const index = (World as any).features.findIndex( (el: any) => d.countryCode === el.properties.ISO3, ); - const color = - // d.x !== undefined ? colorScale(d.x) : UNDPColorModule.graphNoData; - d.x !== undefined - ? d.x === '1' - ? UNDPColorModule.categoricalColors.colors[0] - : 'var(--gray-200)' - : 'var(--gray-200)'; + let color = 'var(--gray-200)'; + // eslint-disable-next-line no-restricted-globals + if (index !== -1 && !isNaN(d.x)) { + if (d.x === 1) { + color = programmeColor; + } + } return ( { + onMouseEnter={event => { setMouseOverData(d); setEventY(event.clientY); setEventX(event.clientX); @@ -162,7 +151,7 @@ export function Graph(props: Props) { onSeriesMouseOver(d); } }} - onMouseMove={(event: any) => { + onMouseMove={event => { setMouseOverData(d); setEventY(event.clientY); setEventX(event.clientX); @@ -201,9 +190,9 @@ export function Graph(props: Props) { key={j} d={masterPath} style={{ - stroke: '#C6C6C6', + stroke: 'var(--gray-500)', }} - strokeWidth={0.4} + strokeWidth={0.25} fill={color} /> ); @@ -226,9 +215,9 @@ export function Graph(props: Props) { key={j} d={path} style={{ - stroke: '#C6C6C6', + stroke: 'var(--gray-500)', }} - strokeWidth={0.4} + strokeWidth={0.25} fill={color} /> ); @@ -267,10 +256,10 @@ export function Graph(props: Props) { key={j} d={masterPath} style={{ - stroke: '#C6C6C6', + stroke: 'var(--gray-700)', fill: 'none', fillOpacity: 0, - strokeWidth: '0.4', + strokeWidth: '0.5', }} /> ); @@ -291,10 +280,10 @@ export function Graph(props: Props) { key={j} d={path} style={{ - stroke: '#C6C6C6', + stroke: 'var(--gray-700)', fill: 'none', fillOpacity: 0, - strokeWidth: '0.4', + strokeWidth: '0.5', }} /> ); @@ -305,100 +294,6 @@ export function Graph(props: Props) { : null} - {/*
-
-
-
- {colorLegendTitle ? ( -
- {colorLegendTitle} -
- ) : null} - - - {domain.map((d, i) => ( - { - setSelectedColor(colors[i]); - }} - onMouseLeave={() => { - setSelectedColor(undefined); - }} - style={{ cursor: 'pointer' }} - > - - - {numberFormattingFunction(d)} - - - ))} - {categorical ? null : ( - - { - setSelectedColor(colors[domain.length]); - }} - onMouseLeave={() => { - setSelectedColor(undefined); - }} - x={(domain.length * 320) / colors.length + 1} - y={1} - width={320 / colors.length - 2} - height={8} - fill={colors[domain.length]} - stroke={ - selectedColor === colors[domain.length] - ? '#212121' - : colors[domain.length] - } - strokeWidth={1} - style={{ cursor: 'pointer' }} - /> - - )} - - -
-
-
-
*/} {mouseOverData && tooltip && eventX && eventY ? ( ) : null} diff --git a/src/Components/Graphs/Maps/ChoroplethMap/index.tsx b/src/Components/Graphs/Maps/ChoroplethMap/index.tsx index 9cfa3fd..952530f 100644 --- a/src/Components/Graphs/Maps/ChoroplethMap/index.tsx +++ b/src/Components/Graphs/Maps/ChoroplethMap/index.tsx @@ -1,10 +1,10 @@ -/* eslint-disable react/button-has-type */ -// import UNDPColorModule from 'undp-viz-colors'; import { useState, useRef, useEffect } from 'react'; import { Graph } from './Graph'; import { ChoroplethMapDataType } from '../../../../Types'; import { GraphFooter } from '../../../Elements/GraphFooter'; import { GraphHeader } from '../../../Elements/GraphHeader'; +import { PROGRAMMES } from '../../../Constants'; +import { useProgramme } from '../../../ProgrammeContext'; interface Props { graphTitle?: string; @@ -14,15 +14,12 @@ interface Props { width?: number; height?: number; source?: string; - // domain: number[]; - // colors?: string[]; - // colorLegendTitle?: string; - // categorical?: boolean; data: ChoroplethMapDataType[]; scale?: number; centerPoint?: [number, number]; backgroundColor?: string | boolean; - tooltip?: (_d: any) => JSX.Element | any; + padding?: string; + tooltip?: (_d: any) => JSX.Element; onSeriesMouseOver?: (_d: any) => void; } @@ -30,23 +27,25 @@ export function ChoroplethMap(props: Props) { const { data, graphTitle, - // colors, source, graphDescription, sourceLink, height, width, footNote, - // domain, - // colorLegendTitle, - // categorical, scale, centerPoint, + padding, backgroundColor, tooltip, onSeriesMouseOver, } = props; + const { currentProgramme } = useProgramme(); + const currentProgrammeColor = + PROGRAMMES.find(prog => prog.value === currentProgramme.value)?.color || + '#000000'; + const [svgWidth, setSvgWidth] = useState(0); const [svgHeight, setSvgHeight] = useState(0); @@ -62,21 +61,26 @@ export function ChoroplethMap(props: Props) {
@@ -99,25 +103,11 @@ export function ChoroplethMap(props: Props) { {(width || svgWidth) && (height || svgHeight) ? ( diff --git a/src/Components/Header.tsx b/src/Components/Header.tsx new file mode 100644 index 0000000..3f44806 --- /dev/null +++ b/src/Components/Header.tsx @@ -0,0 +1,105 @@ +import { Segmented } from 'antd'; +import styled from 'styled-components'; +import { useProgramme } from './ProgrammeContext'; +import { PROGRAMMES } from './Constants'; + +const StyledSegmented = styled(Segmented)<{ selectedColor: string }>` + background-color: white !important; + display: flex; + width: 100%; + + .ant-segmented-item { + border: 0.06rem solid var(--gray-400); + width: 20%; + flex: 1; + color: var(--gray-700); + display: flex; + align-items: center; + justify-content: flex-start; + height: 68px; + + &:not(:last-child) { + margin-right: 8px; // Add gap between items except the last one + } + .ant-segmented-item-label { + white-space: normal; + display: flex; + gap: 16px; + padding: 0 16px; + align-items: center; + } + } + + .ant-segmented-item-selected { + background-color: ${({ selectedColor }) => selectedColor} !important; + border: 0.07rem solid ${({ selectedColor }) => selectedColor}; + color: white !important; + + .ant-segmented-item-label { + .icon-wrapper { + color: white !important; + background-color: #ffffff33; + ) !important; + } + } + } +`; + +const IconWrapper = styled.span<{ color: string }>` + display: inline-flex; + align-items: center; + justify-content: center; + padding: 6px; + border-radius: 100px; + background-color: ${({ color }) => `${color}33`}; + color: ${({ color }) => color}; + margin-right: 8px; // Space between the icon and the label + &.ant-segmented-item-label { + color: white !important; // Change icon color for selected item + background-color: white !important; + } +`; + +interface HeaderProps { + onSegmentChange: (value: any) => void; // Callback function prop +} + +function Header(props: HeaderProps): JSX.Element { + const { onSegmentChange } = props; + const { currentProgramme } = useProgramme(); + + return ( +
+
+ Sustainable Financial Hub +
+ ({ + label: ( + <> + + + +
+ {programme.label} +
+ + ), + value: programme.value, + }))} + onChange={onSegmentChange} + /> +
+ ); +} + +export default Header; diff --git a/src/Components/ProgrammeContext.tsx b/src/Components/ProgrammeContext.tsx new file mode 100644 index 0000000..d197fb8 --- /dev/null +++ b/src/Components/ProgrammeContext.tsx @@ -0,0 +1,34 @@ +import React, { createContext, useContext, useState, ReactNode } from 'react'; +import { Programme, PROGRAMMES } from './Constants'; + +interface ProgrammeContextProps { + currentProgramme: Programme; + setCurrentProgramme: (programme: Programme) => void; +} + +const ProgrammeContext = createContext( + undefined, +); + +export function ProgrammeProvider({ children }: { children: ReactNode }) { + const [currentProgramme, setCurrentProgramme] = useState( + PROGRAMMES[0], + ); + + return ( + + {children} + + ); +} + +export const useProgramme = () => { + const context = useContext(ProgrammeContext); + if (!context) { + throw new Error('useProgramme must be used within a ProgrammeProvider'); + } + return context; +}; diff --git a/src/Components/Summary.tsx b/src/Components/Summary.tsx new file mode 100644 index 0000000..e69de29