Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
e83ad87
fix(map): preserve layer params and prevent default filters from over…
shubham-gupta-nagarro Mar 17, 2026
f00dcdf
Merge pull request #191 from unicef/fix/disputed-geography-labels
niteshbhardwaj02 Mar 25, 2026
e83cebe
Merge pull request #188 from unicef/fix/tech-6770-url-filters-and-lay…
niteshbhardwaj02 Mar 25, 2026
f55761e
Merge branch 'staging' of https://github.com/unicef/giga-maps-fronten…
niteshbhardwaj02 Apr 3, 2026
679b7ea
Merge pull request #182 from unicef/feature/TECH-9046-Added-percentag…
niteshbhardwaj02 Apr 3, 2026
393269e
Merge branch 'staging' of https://github.com/unicef/giga-maps-fronten…
niteshbhardwaj02 Apr 3, 2026
bd2efb9
Revert "fix file upload size"
niteshbhardwaj02 Apr 3, 2026
36d5429
Merge pull request #196 from unicef/feature/layers-avg-function
niteshbhardwaj02 Apr 3, 2026
7fda209
fix aggregate static view and failed logic handling
niteshbhardwaj02 May 11, 2026
ac75e0b
Merge pull request #211 from unicef/bugfix/aggregate-static
niteshbhardwaj02 May 11, 2026
febd6b4
adding territories to countries
reanbrenda Mar 26, 2026
622c797
adding the change to other 3 places
reanbrenda Apr 8, 2026
8d7bc2b
removing countries from the footer sidebar fo spanish and portuguese
reanbrenda Apr 9, 2026
b044234
changing translations
reanbrenda Apr 15, 2026
07c466c
fix(i18n): use natural Spanish country count copy
reanbrenda Apr 27, 2026
2f5ddb9
correct the yarn run build error
reanbrenda Apr 22, 2026
1520dbd
fix(docker): match staging Dockerfile, drop .dockerignore
reanbrenda Jun 3, 2026
365acde
changing school
reanbrenda Jun 18, 2026
0877a8d
fix for yarn install
reanbrenda Jun 18, 2026
519300e
adding translation
reanbrenda Jun 18, 2026
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Stage 1
FROM node:22.2.0 AS s1
FROM node:22.18.0 AS s1
WORKDIR /proco
COPY package.json ./
RUN yarn install
Expand Down
3 changes: 2 additions & 1 deletion src/@/admin/constants/giga-layer.constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export const defaultGigaLayerForm = {
tooltip: ""
}
},
globalBenchmark: null
globalBenchmark: null,
supportedFunctions: null,
} as GigaLayerFormType;

export const DataSourceStatusChoices = {
Expand Down
5 changes: 4 additions & 1 deletion src/@/admin/models/giga-layer.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ $formData.on(onUdpateGigaLayerForm, (state, payload: [string, GigaLayerAllValueT
sourceType: [],
dataSource: [],
dataSourceColumn: null,
supportedFunctions: null,
globalBenchmark: null
}
}
Expand Down Expand Up @@ -107,6 +108,7 @@ sample({
clock: $currentGigaLayerItem,
fn: (layer) => {
if (!layer) return defaultGigaLayerForm;
const dataSourceColumn = Object.values(layer?.data_source_column ?? [])[0];
return ({
code: layer?.code,
name: layer?.name,
Expand All @@ -119,7 +121,8 @@ sample({
})),
isReverse: layer?.is_reverse,
dataSource: layer?.data_sources_list?.map(source => source.id),
dataSourceColumn: Object.values(layer?.data_source_column ?? [])[0],
dataSourceColumn,
supportedFunctions: layer?.data_source_column_function,
applicableCountries: layer.applicable_countries,
globalBenchmark: layer?.global_benchmark,
benchmarkConvertUnit: layer?.global_benchmark?.convert_unit,
Expand Down
15 changes: 13 additions & 2 deletions src/@/admin/types/giga-layer.type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export interface DataLayer {
category: string
applicable_countries: number[]
legend_configs: LegendConfigType
data_source_column_function: SupportedFunctionType
global_benchmark: GlobalBenchmark
status: LayerStatusType
published_by: null | CreatedBy
Expand Down Expand Up @@ -77,9 +78,18 @@ export interface ColumnConfig {
type: string
on?: string
unit?: string
is_parameters: boolean
is_parameters: boolean;
base_benchmark: number;
display_unit: string;
supported_functions: SupportedFunctionType[]
}

export interface SupportedFunctionType {
description: string
name: string
sql: string
verbose: string
}
export interface DataSourceColumn {
"1": N1
}
Expand Down Expand Up @@ -188,10 +198,11 @@ export type GigaLayerFormType = {
description: DataLayer['description'],
type: DataLayer['type'] | undefined,
dataSource: number[],
dataSourceColumn: DataLayer['data_source'] | null,
dataSourceColumn: ColumnConfig | null,
applicableCountries: DataLayer['applicable_countries'],
globalBenchmark: DataLayer['global_benchmark'] | null,
benchmarkConvertUnit: string,
supportedFunctions: SupportedFunctionType | null,
}

export type GigaLayerAllValueType = ValuesType<GigaLayerFormType>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { DataLayerFieldContainer, DataLayerNameField, InputLabel, SelectLayerCon
import { Div } from "~/@/common/style/styled-component-style";
import { speedConverterUtil } from "~/lib/utils";

const benchmarkUnitValues = ['bps', 'ms', 'mbps'];
const benchmarkUnitValues = ['bps', 'ms', 'mbps', '%'];
export default function GigaBenchmarkForm({ isDefaultLayer }: { readonly isDefaultLayer: boolean }) {
const formData = useStore($formData);

Expand Down Expand Up @@ -96,4 +96,4 @@ export default function GigaBenchmarkForm({ isDefaultLayer }: { readonly isDefau
</DataLayerNameField>

</>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { useMemo } from "react";
import { DataSourceName, LayerDataSource, LayerTypeNames } from "~/@/admin/constants/giga-layer.constant";
import { $appConfigValues } from "~/@/admin/models/admin-model";
import { $apiSourceValues, $formData, onUdpateGigaLayerForm } from "~/@/admin/models/giga-layer.model";
import { DataSource } from "~/@/admin/types/giga-layer.type";
import { DataSource, LayerTypeChoices } from "~/@/admin/types/giga-layer.type";
import { $countryList } from "~/@/api-docs/models/explore-api.model";
import { CountryListType } from "~/@/api-docs/types/country-list.type";

Expand All @@ -25,6 +25,11 @@ export default function GigaFields({ isEditMode, isDefaultLayer }: { readonly is
return Array.from(
list.reduce((map, obj) => map.set(obj.name, obj), new Map()).values());
}, [apiSourceSelected])

const supportedFunctions = useMemo(() => {
return parameters.find(item => item.name === formData?.dataSourceColumn?.name)?.supported_functions ?? [];
}, [parameters, formData?.dataSourceColumn])

const selectedCountries = useMemo(() => {
if (!countryList || !formData?.applicableCountries) return [];
return countryList?.filter(item => formData?.applicableCountries.includes(item.id))
Expand All @@ -39,6 +44,8 @@ export default function GigaFields({ isEditMode, isDefaultLayer }: { readonly is
name: DataSourceName[sourceName]
}))
}, [formData.type])
const isLive = String(formData.type) === String(LayerTypeChoices.LIVE);

return <>
<DataLayerFieldContainer>
<InputLabel>
Expand Down Expand Up @@ -161,6 +168,18 @@ export default function GigaFields({ isEditMode, isDefaultLayer }: { readonly is
<SelectItem value="" text="Select parameter" />
{parameters?.map((parameter) => <SelectItem key={parameter?.name} value={parameter?.name} text={parameter?.alias} />)}
</SelectLayerConfig>
{isLive && <SelectLayerConfig
name="supportedFunctions"
required
labelText="Parameter Aggregator Function"
id={`convert-function-select`}
value={formData.supportedFunctions?.name}
placeholder="Select Parameter Aggregator Function"
onChange={(e) => onUdpateGigaLayerForm([e.target.name, supportedFunctions?.find(item => e.target.value === item.name)])}
>
<SelectItem value="" text="Select Parameter Aggregator Function" />
{supportedFunctions?.map((item) => <SelectItem key={item?.name} value={item?.name} text={`${item?.verbose} ${item.description ? `(${item.description})` : ''}`} />)}
</SelectLayerConfig>}
<MultiSelectLayerConfig
required
direction='top'
Expand Down
7 changes: 5 additions & 2 deletions src/@/admin/ui/giga-layer/common/giga-layer-form/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ const GigaLayerForm = ({ isEditMode }: { isEditMode: boolean }) => {
const countryList = useStore($countryList)
const layerItem = useStore($currentGigaLayerItem);
const isDefaultLayer = isEditMode && !layerItem?.created_by;
let dataSourceColumn = formData.dataSourceColumn;
const isLive = formData.type === LayerTypeChoices.LIVE;

const updateOrCreateLayer = async () => {
try {
Expand All @@ -32,11 +34,12 @@ const GigaLayerForm = ({ isEditMode }: { isEditMode: boolean }) => {
description: formData.description,
type: formData.type,
data_sources_list: formData.dataSource,
data_source_column: formData.dataSourceColumn,
data_source_column: dataSourceColumn,
is_reverse: formData.isReverse,
data_source_column_function: isLive ? formData.supportedFunctions : null,
applicable_countries: countryList.filter((country) => formData.applicableCountries.includes(country.id)).map((item) => ({ name: item.code })),
legend_configs: { ...defaultGigaLayerForm.legendConfigs, ...formData.legendConfigs },
global_benchmark: { ...(formData.type === LayerTypeChoices.LIVE ? { ...formData.globalBenchmark, convert_unit: formData.benchmarkConvertUnit } : { benchmark_name: formData?.globalBenchmark?.benchmark_name ?? 'Global' }) },
global_benchmark: { ...(isLive ? { ...formData.globalBenchmark, convert_unit: formData.benchmarkConvertUnit } : { benchmark_name: formData?.globalBenchmark?.benchmark_name ?? 'Global' }) },
...(!layerItem?.status ? { status: LayerStatusType.DRAFT } : {}),
}

Expand Down
4 changes: 4 additions & 0 deletions src/@/admin/ui/giga-layer/view-giga-layer.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ const AdminViewLayer = () => {
<LayerLabel>Parameter</LayerLabel>
<LayerDetail>{parameterName}</LayerDetail>
</LayerContentWrapper>
{isLive && <LayerContentWrapper>
<LayerLabel>Parameter Aggregator Function</LayerLabel>
<LayerDetail>{`${layerItem?.data_source_column_function.verbose} ${layerItem.data_source_column_function.description ? `(${layerItem.data_source_column_function.description})` : ''}`}</LayerDetail>
</LayerContentWrapper>}
<LayerContentWrapper>
<LayerLabel>Countries</LayerLabel>
<LayerDetail>{countriesNames}</LayerDetail>
Expand Down
21 changes: 18 additions & 3 deletions src/@/map/map.init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,18 +136,33 @@ sample({
target: $reloadStyle
});

const hasFilterParams = () => {
const params = new URLSearchParams(window.location.search);
return Array.from(params.keys()).some(key => key.startsWith('filter__'));
};

const $derivedCountryActiveFilterList = combine({
countryActiveFiltersList: $countryActiveFiltersList,
activeFiltersList: $advanceFilterList,
schoolFocusLatLng: $schoolFocusLatLng,
});

// guard: only run when both filters loaded AND no school is focused
// guard: apply default country filters only when:
// - filter data is loaded
// - no school is focused
// - URL has no existing filter params (to avoid overriding shared URLs)
const activeFiltersListClock = guard({
source: $derivedCountryActiveFilterList,
clock: merge([fetchCountryFx.doneData, fetchAdvanceFilterFx.doneData]),
filter: ({ countryActiveFiltersList, activeFiltersList, schoolFocusLatLng }) =>
countryActiveFiltersList != null && activeFiltersList != null && schoolFocusLatLng === null,
filter: ({ countryActiveFiltersList, activeFiltersList, schoolFocusLatLng }) => {
if (hasFilterParams()) return false; // 🚨 IMPORTANT FIX

return (
countryActiveFiltersList != null &&
activeFiltersList != null &&
schoolFocusLatLng === null
);
},
});

sample({
Expand Down
4 changes: 3 additions & 1 deletion src/@/map/popup/popup.util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ export const createAndSetPopupTemplate = ({ popupElement, feature, stylePaintDat

const schoolCoords = JSON.parse(JSON.stringify((feature?.geopoint?.coordinates ?? [])));
const isLiveNotUnknown = isLive && feature?.connectivityType !== UNKNOWN;
const connectivityValue = isLiveNotUnknown ? `${feature?.liveAvg ?? 0} ${unit}` : t.getState()('unknown');
const connectivityValue = isLiveNotUnknown
? `${feature?.liveAvg ?? 0}${unit === '%' ? unit : ` ${unit}`}`
: t.getState()('unknown');

setContentHTML(popupTemplate, '.map-school-name', feature?.name);
setContentHTML(popupTemplate, '.map-school-id', `${feature?.externalId}`);
Expand Down
6 changes: 3 additions & 3 deletions src/@/map/tests/__snapshots__/map-page.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1865,7 +1865,7 @@ exports[`Render Map-page MapPage snapshot 1`] = `
<p>
0 /6M
<span>
schools location mapped
Schools mapped
</span>
</p>
<span
Expand Down Expand Up @@ -1949,7 +1949,7 @@ exports[`Render Map-page MapPage snapshot 1`] = `
<p>
0
<span>
schools connectivity status mapped
Schools with connectivity status
</span>
</p>
<span
Expand Down Expand Up @@ -2047,7 +2047,7 @@ exports[`Render Map-page MapPage snapshot 1`] = `
<p>
0
<span>
schools with real-time data mapped
Schools sending real-time connectivity measurements
</span>
</p>
<span
Expand Down
32 changes: 27 additions & 5 deletions src/@/map/ui/advanced-filter/filter-popup-content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,22 @@ const FilterPopupContent = ({ setOpen }: PropsWithChildren<{ setOpen: (open: boo
const onApply = async (e: MouseEvent) => {
e.preventDefault();
const prefix = 'filter__';
const params = new URLSearchParams();
const params = new URLSearchParams(window.location.search);
for (const key of Array.from(params.keys())) {
if (key.startsWith(prefix)) {
params.delete(key);
}
}

for (const [key, value] of Object.entries(selectedFields)) {
if (value) {
if (typeof value === 'object') {
const { none_range, value: rangeValue } = value;
if (none_range) {
params.set(`${prefix}${key.replace('__range', '__none_range')}`, String(rangeValue) || "null,null");
params.set(
`${prefix}${key.replace('__range', '__none_range')}`,
String(rangeValue) || "null,null"
);
} else if (rangeValue) {
params.set(`${prefix}${key}`, String(rangeValue));
}
Expand All @@ -92,6 +101,20 @@ const FilterPopupContent = ({ setOpen }: PropsWithChildren<{ setOpen: (open: boo
router.navigate(`${window.location.pathname}?${params.toString()}`);
setOpen(false);
}

const onReset = async (e: MouseEvent) => {
e.preventDefault();
const params = new URLSearchParams(window.location.search);
for (const key of Array.from(params.keys())) {
if (key.startsWith('filter__')) {
params.delete(key);
}
}

router.navigate(`${window.location.pathname}?${params.toString()}`);
setOpen(false)
}

// const items = ['All data layers']
if (!isReady) return null;
return (
Expand Down Expand Up @@ -128,9 +151,8 @@ const FilterPopupContent = ({ setOpen }: PropsWithChildren<{ setOpen: (open: boo
<Button
type="reset"
kind="secondary"
onClick={() => {
router.navigate(`${window.location.pathname}`);
setOpen(false)
onClick={(event: MouseEvent<Element, globalThis.MouseEvent>) => {
void onReset(event);
}}>
{t('reset')}
</Button>
Expand Down
6 changes: 5 additions & 1 deletion src/@/map/ui/map-school-popup/school-popup-hook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ const useSchoolPopupData = () => {
intervalUnit,
false
);
const formatConnectivityValue = (value: number, unit?: string) => {
if (!unit) return String(value);
return unit === '%' ? `${value}${unit}` : `${value} ${unit}`;
};
const getFeatureInfo = (feature: any) => {
const unit = global_benchmark?.convert_unit;
const connectivityStatusValue = feature?.connectivityStatus;
Expand All @@ -41,7 +45,7 @@ const useSchoolPopupData = () => {
const connecitivityColor = stylePaintData[feature?.connectivityType ?? UNKNOWN];
const schoolCoords = JSON.parse(JSON.stringify((feature?.geopoint?.coordinates ?? [])));
const isLiveNotUnknown = isLive && feature?.connectivityType !== UNKNOWN;
const connectivityValue = isLiveNotUnknown ? `${feature?.liveAvg ?? 0} ${unit}` : t('unknown');
const connectivityValue = isLiveNotUnknown ? formatConnectivityValue(feature?.liveAvg ?? 0, unit) : t('unknown');
const benchmarkTitle = connectivityBenchMarks === ConnectivityBenchMarks.global ? benchmarkNamesAllLayers[selectedLayerData?.id ?? ""] : countryConnectivityNames[selectedLayerData?.id ?? ""]
let staticValue = feature?.staticValue as boolean | undefined | string;
const staticColor = stylePaintData[feature?.staticType ?? UNKNOWN]
Expand Down
3 changes: 1 addition & 2 deletions src/@/sidebar/ui/breadcrumb/global-bdb.view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ const GlobalBDB = () => {

return (<div className="sidebar-worldview-global-indication">
<div className="sidebar-worldview-global-indication-text-and-contries">
<span className="sidebar-worldview-globalText">{t('worldwide')}</span>
<span className="sidebar-worldview-contriesText">{`${noOfCounties} ${t('countries')}`}</span>
<span className="sidebar-worldview-contriesText">{t('countries-and-territories', { count: noOfCounties })}</span>
</div>
{/* <Tooltip align="bottom" label="worldwide information for register countries" className='side-info-panel-infomartion-of-worldwideview'>
<button>
Expand Down
6 changes: 3 additions & 3 deletions src/@/sidebar/ui/landing-page-side-bar/landing-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ const LandingPage = () => {
</SchoolNumberWrapper>
<MappedInfoWrapper>
<p>
{t('across-no-countries', { count: globalstats?.no_of_countries ?? 0 })}
{t('across-no-countries-and-territories', { count: globalstats?.no_of_countries ?? 0 })}
</p>
</MappedInfoWrapper>
</>}
Expand All @@ -100,7 +100,7 @@ const LandingPage = () => {
</SchoolNumberWrapper>
<MappedInfoWrapper>
<p data-title={`${globalstats?.countries_with_connectivity_status_mapped}`}>
{t('across-no-countries', { count: globalstats?.countries_with_connectivity_status_mapped ?? 0 })}
{t('across-no-countries-and-territories', { count: globalstats?.countries_with_connectivity_status_mapped ?? 0 })}
</p>
<span data-title={t('int', { val: globalstats?.schools_with_connectivity_status_mapped ?? 0 })}>
{formatNumber(globalstats?.schools_with_connectivity_status_mapped ?? 0, lng)}
Expand Down Expand Up @@ -134,7 +134,7 @@ const LandingPage = () => {
</SchoolNumberWrapper>
<MappedInfoWrapper>
<p data-title={t('int', { val: connectivityStats?.countries_with_realtime_data ?? 0 })}>
{t('across-no-countries', { count: connectivityStats?.countries_with_realtime_data ?? 0 })}
{t('across-no-countries-and-territories', { count: connectivityStats?.countries_with_realtime_data ?? 0 })}
</p>
<span data-title={t('int', { val: connectivityStats?.no_of_schools_measure ?? 0 })}>
{formatNumber(connectivityStats?.no_of_schools_measure, lng)}
Expand Down
Loading