From 38ec9687f46a7975a907045816f55c385fb60968 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Wed, 1 Jul 2026 12:58:17 +0200 Subject: [PATCH 01/16] Fix(indicators): Fix taxon abundances --- .../features/indicators/biodiversity/format-data.ts | 1 + webapp/src/features/indicators/soil/format-data.ts | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/webapp/src/features/indicators/biodiversity/format-data.ts b/webapp/src/features/indicators/biodiversity/format-data.ts index 86430208..784ddb73 100644 --- a/webapp/src/features/indicators/biodiversity/format-data.ts +++ b/webapp/src/features/indicators/biodiversity/format-data.ts @@ -11,6 +11,7 @@ import type { ForestInventoryData } from "@features/popup/forest-inventory/types import type { NumericKeys } from "@shared/types"; import type { BiodiversityData } from "./types"; +import type { ForestInventoryData } from "@features/popup/forest-inventory/types"; const indicatorKeys: NumericKeys[] = [ "biomass_volume", diff --git a/webapp/src/features/indicators/soil/format-data.ts b/webapp/src/features/indicators/soil/format-data.ts index eb60ef17..8d094ed7 100644 --- a/webapp/src/features/indicators/soil/format-data.ts +++ b/webapp/src/features/indicators/soil/format-data.ts @@ -60,6 +60,16 @@ export const useFormatSoilData = (data: ForestInventoryData) => { indicatorsToPreciseWithFallBack, t("dataManagement.noData"), ); + + safeData.soil_surface_fauna_abundance = convertDictToPercentage(safeData.soil_surface_fauna_abundance, Object.values(safeData.soil_surface_fauna_abundance || {}).reduce((a, b) => a + b, 0), "0") /*formatTaxonAbundance( + safeData.soil_surface_fauna_abundance_pop, + safeData.soil_surface_fauna_total_pop + )*/ + + safeData.soil_fauna_abundance = formatTaxonAbundance( + safeData.soil_fauna_abundance_pop, + safeData.soil_fauna_total_pop + ) safeData.soil_surface_fauna_abundance = formatTaxonAbundance( safeData.soil_surface_fauna_abundance_pop, From 7dad1481213a05d1b9d2299bb90c4622d9640b7a Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Wed, 1 Jul 2026 13:00:21 +0200 Subject: [PATCH 02/16] Fix lint --- .../indicators/biodiversity/format-data.ts | 1 - .../src/features/indicators/soil/format-data.ts | 15 +++++++++++---- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/webapp/src/features/indicators/biodiversity/format-data.ts b/webapp/src/features/indicators/biodiversity/format-data.ts index 784ddb73..86430208 100644 --- a/webapp/src/features/indicators/biodiversity/format-data.ts +++ b/webapp/src/features/indicators/biodiversity/format-data.ts @@ -11,7 +11,6 @@ import type { ForestInventoryData } from "@features/popup/forest-inventory/types import type { NumericKeys } from "@shared/types"; import type { BiodiversityData } from "./types"; -import type { ForestInventoryData } from "@features/popup/forest-inventory/types"; const indicatorKeys: NumericKeys[] = [ "biomass_volume", diff --git a/webapp/src/features/indicators/soil/format-data.ts b/webapp/src/features/indicators/soil/format-data.ts index 8d094ed7..02625a6e 100644 --- a/webapp/src/features/indicators/soil/format-data.ts +++ b/webapp/src/features/indicators/soil/format-data.ts @@ -60,16 +60,23 @@ export const useFormatSoilData = (data: ForestInventoryData) => { indicatorsToPreciseWithFallBack, t("dataManagement.noData"), ); - - safeData.soil_surface_fauna_abundance = convertDictToPercentage(safeData.soil_surface_fauna_abundance, Object.values(safeData.soil_surface_fauna_abundance || {}).reduce((a, b) => a + b, 0), "0") /*formatTaxonAbundance( + + safeData.soil_surface_fauna_abundance = convertDictToPercentage( + safeData.soil_surface_fauna_abundance, + Object.values(safeData.soil_surface_fauna_abundance || {}).reduce( + (a, b) => a + b, + 0, + ), + "0", + ); /*formatTaxonAbundance( safeData.soil_surface_fauna_abundance_pop, safeData.soil_surface_fauna_total_pop )*/ safeData.soil_fauna_abundance = formatTaxonAbundance( safeData.soil_fauna_abundance_pop, - safeData.soil_fauna_total_pop - ) + safeData.soil_fauna_total_pop, + ); safeData.soil_surface_fauna_abundance = formatTaxonAbundance( safeData.soil_surface_fauna_abundance_pop, From f477e7e25d80251b4f588db93a5fb913c3fae0dd Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Thu, 2 Jul 2026 12:59:05 +0200 Subject: [PATCH 03/16] Feat(Bio Inventory): Add Bio Inventory Layer --- backend/configs/all4trees_config.json | 36 ++++++++++ .../app/providers/map-provider-all4trees.tsx | 12 ---- .../use-categories-config.tsx | 8 +-- .../use-categories-filters.ts | 3 +- .../chart-taxon-abundance.tsx | 6 +- .../{soil => components}/lib/sunburst.ts | 2 +- .../charts/{soil => components}/lib/taxon.ts | 4 ++ .../src/features/controls/layer-control.tsx | 4 +- .../indicators/bio-inventory/format-data.ts | 49 ++++++++++++++ .../indicators/bio-inventory/index.ts | 1 + .../use-bioinventory-indicator-elements.tsx | 60 +++++++++++++++++ .../features/indicators/soil/format-data.ts | 18 +---- .../soil/use-soil-indicator-elements.tsx | 2 +- webapp/src/features/indicators/utils.ts | 24 +++++++ .../src/features/popup/bio-inventory/index.ts | 2 + .../bio-inventory/popup-bio-inventory.tsx | 53 +++++++++++++++ .../src/features/popup/bio-inventory/types.ts | 19 ++++++ webapp/src/shared/api/categories-filters.ts | 5 +- webapp/src/shared/api/layers.ts | 3 +- .../shared/contexts/map-context-all4trees.ts | 5 -- .../i18n/translations/en/all4trees.json | 8 ++- .../shared/i18n/translations/en/common.json | 3 +- .../i18n/translations/fr/all4trees.json | 15 ++++- .../shared/i18n/translations/fr/common.json | 3 +- webapp/src/widgets/map/map-all4trees.tsx | 66 ++++++++----------- 25 files changed, 315 insertions(+), 96 deletions(-) rename webapp/src/features/charts/{soil/ui => components}/chart-taxon-abundance.tsx (95%) rename webapp/src/features/charts/{soil => components}/lib/sunburst.ts (97%) rename webapp/src/features/charts/{soil => components}/lib/taxon.ts (90%) create mode 100644 webapp/src/features/indicators/bio-inventory/format-data.ts create mode 100644 webapp/src/features/indicators/bio-inventory/index.ts create mode 100644 webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx create mode 100644 webapp/src/features/popup/bio-inventory/index.ts create mode 100644 webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx create mode 100644 webapp/src/features/popup/bio-inventory/types.ts diff --git a/backend/configs/all4trees_config.json b/backend/configs/all4trees_config.json index 77be4689..59a2fe40 100644 --- a/backend/configs/all4trees_config.json +++ b/backend/configs/all4trees_config.json @@ -91,6 +91,42 @@ "trigger": "click" } }, + { + "id": "inventaire_bio", + "type": "datapackage", + "path": "../catalog/inventaire_bio", + "resource": "inv_bio", + "layerType": "symbol", + "cluster": { + "radius": 50, + "maxZoom": 50, + "steps": [10, 20], + "colors": ["#99D6C2", "#009966", "#006B47"] + }, + "columns": { + "geom": "start_point.merge().centroid()", + "id": "_id", + "conf": "conf", + "proj": "proj", + "ecos": "ecos", + "region": "loc1", + "forest": "loc2", + "start_date": "coh", + "taxon": "'N/A'", + "type": "meth", + "samp_area": "inv_samp", + "samp_unit": "inv_unit", + "density": "int(sum(inv_001.no)) / inv_samp", + "dens_unit": "dens_unit", + "richness": "list_unique(list(nullif(inv_001.concat_ws('-', tax1, tax2, tax3), '')))", + "taxons_relative_abundance": "list_aggregate(list(nullif(inv_001.concat_ws('-', tax1, tax2, tax3), '')), 'histogram')", + "pop_by_taxon": "list_concat(list(inv_001.concat_ws(':', concat_ws('-',tax1, tax2, tax3), no) if inv_001.no > 0))", + "total_pop": "int(sum(inv_001.no))" + }, + "popup": { + "trigger": "click" + } + }, { "id": "enquete", "type": "datapackage", diff --git a/webapp/src/app/providers/map-provider-all4trees.tsx b/webapp/src/app/providers/map-provider-all4trees.tsx index 51771624..f6634378 100644 --- a/webapp/src/app/providers/map-provider-all4trees.tsx +++ b/webapp/src/app/providers/map-provider-all4trees.tsx @@ -6,7 +6,6 @@ import { renderAnchor, renderLayerRow } from "@features/controls/layer-control"; import { API_URL } from "@shared/api/client"; import { - type Category, MapContext, } from "@shared/contexts/map-context-all4trees"; import { useLocalStorage } from "@shared/hooks/use-local-storage"; @@ -33,7 +32,6 @@ export function MapProviderAll4Trees({ children }: MapProviderAll4TreesProps) { const { isAuthenticated, token } = useAuth(); const mapApiRef = useRef | null>(null); - const [forests, setForests] = useState([]); const [mapSettings, setMapSettings] = useLocalStorage( "d4g:map-settings:all4trees", DEFAULT_MAP_SETTINGS, @@ -54,14 +52,6 @@ export function MapProviderAll4Trees({ children }: MapProviderAll4TreesProps) { const handleReady = () => { setIsReady(true); - // biome-ignore lint/suspicious/noExplicitAny : - const metadata: any = mapApiRef.current?.getLayerMetadata("inventaire"); - const forestField = metadata?.schema?.fields?.find( - (f: { name: string }) => f.name === "for", - ); - if (forestField?.categories) { - setForests(forestField.categories); - } // On first mount, sync the map state with the local storage state of "categories-filters" syncInitialCategoriesFilters({ @@ -104,12 +94,10 @@ export function MapProviderAll4Trees({ children }: MapProviderAll4TreesProps) { diff --git a/webapp/src/features/categories-filters/use-categories-config.tsx b/webapp/src/features/categories-filters/use-categories-config.tsx index 672b92b8..c078d653 100644 --- a/webapp/src/features/categories-filters/use-categories-config.tsx +++ b/webapp/src/features/categories-filters/use-categories-config.tsx @@ -20,15 +20,15 @@ export const useCategoriesConfig = (): { // ----- Actions ----- - const actionInventary: CategoryGroupItem = { + const actionInventory: CategoryGroupItem = { icon: ( ), - identifier: CATEGORY_IDENTIFIERS.ACTION_INVENTARY, - label: t("filters.categories.actions.forestInventary"), + identifier: CATEGORY_IDENTIFIERS.ACTION_INVENTORY, + label: t("filters.categories.actions.forestInventory"), }; const actionDiversity: CategoryGroupItem = { @@ -111,7 +111,7 @@ export const useCategoriesConfig = (): { }; return { - actions: [actionInventary, actionDiversity, actionSocioEco], + actions: [actionInventory, actionDiversity, actionSocioEco], data: [dataGround, dataSatellite, dataModel], system: [ systemMangroveHigh, diff --git a/webapp/src/features/categories-filters/use-categories-filters.ts b/webapp/src/features/categories-filters/use-categories-filters.ts index f396f000..2ddd7313 100644 --- a/webapp/src/features/categories-filters/use-categories-filters.ts +++ b/webapp/src/features/categories-filters/use-categories-filters.ts @@ -38,7 +38,8 @@ export const useCategoriesFilters = () => { const [categoriesFilters, setCategoriesFilters] = useLocalStorage("categories-filters", { [CATEGORY_IDENTIFIERS.ACTION_DIVERSITY]: true, - [CATEGORY_IDENTIFIERS.ACTION_INVENTARY]: true, + [CATEGORY_IDENTIFIERS.ACTION_INVENTORY]: true, + [CATEGORY_IDENTIFIERS.ACTION_BIO]: true, [CATEGORY_IDENTIFIERS.ACTION_SOCIO]: true, [CATEGORY_IDENTIFIERS.DATA_GROUND]: true, [CATEGORY_IDENTIFIERS.DATA_MODEL]: true, diff --git a/webapp/src/features/charts/soil/ui/chart-taxon-abundance.tsx b/webapp/src/features/charts/components/chart-taxon-abundance.tsx similarity index 95% rename from webapp/src/features/charts/soil/ui/chart-taxon-abundance.tsx rename to webapp/src/features/charts/components/chart-taxon-abundance.tsx index 96c817d7..213e8002 100644 --- a/webapp/src/features/charts/soil/ui/chart-taxon-abundance.tsx +++ b/webapp/src/features/charts/components/chart-taxon-abundance.tsx @@ -9,9 +9,9 @@ import { import { useTranslation } from "@shared/i18n"; -import { SUNBURST_LAYOUT } from "../config"; -import { buildNodeColors, buildSunburstNodes } from "../lib/sunburst"; -import type { PieChartProps, SunburstTrace } from "../types"; +import { SUNBURST_LAYOUT } from "../soil/config"; +import { buildNodeColors, buildSunburstNodes } from "./lib/sunburst"; +import type { PieChartProps, SunburstTrace } from "../soil/types"; export const ChartTaxonAbundance: ChartComponentType = ({ data, diff --git a/webapp/src/features/charts/soil/lib/sunburst.ts b/webapp/src/features/charts/components/lib/sunburst.ts similarity index 97% rename from webapp/src/features/charts/soil/lib/sunburst.ts rename to webapp/src/features/charts/components/lib/sunburst.ts index 04684fbf..11f8c98a 100644 --- a/webapp/src/features/charts/soil/lib/sunburst.ts +++ b/webapp/src/features/charts/components/lib/sunburst.ts @@ -4,7 +4,7 @@ import type { ExternalData } from "@features/popup/forest-inventory/types"; import { getChartPalette } from "@shared/lib/palette"; -import type { SunburstNode } from "../types"; +import type { SunburstNode } from "../../soil/types"; import { formatTaxonLevelLabel } from "./taxon"; export function buildSunburstNodes( diff --git a/webapp/src/features/charts/soil/lib/taxon.ts b/webapp/src/features/charts/components/lib/taxon.ts similarity index 90% rename from webapp/src/features/charts/soil/lib/taxon.ts rename to webapp/src/features/charts/components/lib/taxon.ts index b6de8068..06a6f658 100644 --- a/webapp/src/features/charts/soil/lib/taxon.ts +++ b/webapp/src/features/charts/components/lib/taxon.ts @@ -1,6 +1,10 @@ import { findLabelInExternalData } from "@features/indicators/utils"; import type { ExternalData } from "@features/popup/forest-inventory/types"; +export function getDataTypePrefix(dataType: "tsbf" | "barbA" | null): string { + return dataType ? `${dataType}_` : ''; +} + export function getTaxonLabels( element: string, metadata: ExternalData, diff --git a/webapp/src/features/controls/layer-control.tsx b/webapp/src/features/controls/layer-control.tsx index 6ced8f24..4789ac60 100644 --- a/webapp/src/features/controls/layer-control.tsx +++ b/webapp/src/features/controls/layer-control.tsx @@ -54,9 +54,9 @@ function useLayerConfig(layerId: string) { /> ), }; - case LAYERS.INVENTARY: + case LAYERS.INVENTORY_FOR: return { - label: t("filters.categories.actions.forestInventary") as string, + label: t("filters.categories.actions.forestInventory") as string, renderIcon: (checked?: boolean) => ( [] = [ + "samp_area", + "density", + "richness", + "total_pop", +]; + +/** + * Return data in a convenient way for UI rendering, handling units and fixing + */ +export const useFormatBioInventoryData = (data: BioInventoryData, metadata: LayerMetadata) => { + const { t } = useTranslation("common"); + + const safeData = preciseNumericIndicators( + data, + indicatorKeys, + t("dataManagement.noData"), + ); + + safeData.taxons_relative_abundance = formatTaxonAbundance( + safeData.pop_by_taxon, + safeData.total_pop, + ); + + return { + taxon: 'Lémuriens', // findCategoricalLabel(metadata, "tax", safeData.taxon) || safeData.taxon + type: findCategoricalLabel(metadata, "meth", safeData.type.toString()) || safeData.type, + area: `${safeData.samp_area} ${findCategoricalLabel(metadata, "inv_unit", safeData.samp_unit) || t("dataManagement.noUnit")}`, + density: `${safeData.density} ${safeData.dens_unit || t("dataManagement.noUnit")}`, + richness: safeData.richness, + relative_abundance: safeData.taxons_relative_abundance, + total_population: safeData.total_pop, + }; +}; + +export type FormattedData = ReturnType; diff --git a/webapp/src/features/indicators/bio-inventory/index.ts b/webapp/src/features/indicators/bio-inventory/index.ts new file mode 100644 index 00000000..04683400 --- /dev/null +++ b/webapp/src/features/indicators/bio-inventory/index.ts @@ -0,0 +1 @@ +export { useBioInventoryIndicatorElements as useBiodiversityIndicatorElements } from "./use-bioinventory-indicator-elements"; diff --git a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx new file mode 100644 index 00000000..5bb48601 --- /dev/null +++ b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx @@ -0,0 +1,60 @@ +import { PawPrint, SearchAlert, Gem } from "lucide-react"; + +import type { LayerMetadata } from "@shared/lib/coordo"; +import { useTranslation } from "@i18n"; + +import { ICON_SIZE } from "../components/constants"; +import { IndicatorRawValue } from "../components/indicator-raw-value"; +import type { UseIndicatorReturnType } from "../components/types"; +import { useFormatBioInventoryData } from "./format-data"; +import type { BioInventoryData } from "@features/popup/bio-inventory/types"; +import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; + +export const useBioInventoryIndicatorElements = ( + rawData: BioInventoryData, + metadata: LayerMetadata, +): UseIndicatorReturnType => { + const { t } = useTranslation("all4trees"); + const data = useFormatBioInventoryData(rawData, metadata); + + return [ + { + children: ( + <> + } + value={data.taxon} + /> + } + value={data.type} + /> + } + value={data.area} + /> + } + value={data.density} + /> + } + value={data.richness} + /> + + + ), + identifier: "bio", + title: t("indicators.bioinventory.title"), + type: "section", + }, + ]; +}; diff --git a/webapp/src/features/indicators/soil/format-data.ts b/webapp/src/features/indicators/soil/format-data.ts index 02625a6e..79f46491 100644 --- a/webapp/src/features/indicators/soil/format-data.ts +++ b/webapp/src/features/indicators/soil/format-data.ts @@ -3,6 +3,7 @@ import { useTranslation } from "react-i18next"; import { computeScore, convertDictToPercentage, + formatTaxonAbundance, preciseNumericIndicators, UNITS, useFormatterWithUnit, @@ -21,23 +22,6 @@ const indicatorsToPreciseWithFallBack: NumericKeys[] = [ "soil_eros_stability", ] as const; -function formatTaxonAbundance(abundancePop: string[], abundanceTotal: number) { - if (!abundanceTotal) { - return {}; - } - - const abundancePopRecord: Record = {}; - abundancePop.forEach((value) => { - if (value) { - const [taxon, count] = value.split(":"); - const currentCount = abundancePopRecord[taxon] || 0; - abundancePopRecord[taxon] = currentCount + parseInt(count, 10); - } - }); - - return convertDictToPercentage(abundancePopRecord, abundanceTotal, "0"); -} - /** * Return data in a convenient way for UI rendering, handling units and fixing */ diff --git a/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx b/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx index 142f04bb..2c24539f 100644 --- a/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx +++ b/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx @@ -1,7 +1,7 @@ import { Bug, Gem, Sprout } from "lucide-react"; import { ChartAquaticErosion } from "@features/charts/soil/ui/chart-aquatic-erosion"; -import { ChartTaxonAbundance } from "@features/charts/soil/ui/chart-taxon-abundance"; +import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; import { ChartWindErosion } from "@features/charts/soil/ui/chart-wind-erosion"; import type { UseIndicatorReturnType } from "@features/indicators//components/types"; import { IndicatorRawValue } from "@features/indicators/components/indicator-raw-value"; diff --git a/webapp/src/features/indicators/utils.ts b/webapp/src/features/indicators/utils.ts index e1151c12..ed453889 100644 --- a/webapp/src/features/indicators/utils.ts +++ b/webapp/src/features/indicators/utils.ts @@ -121,6 +121,30 @@ export function convertDictToPercentage( ); } +/* + Format taxon relative abundance by converting the sring array containing data like 'taxon:pop' by: + 1. Sum all pop corresponding to same taxon + 2. Convert the pop to percentage of total population + 3. Return a record with taxon as key and percentage as value + Example: + Input: ['taxon1:10', 'taxon2:20', 'taxon1:30'], totalPopulation = 60 + Output: { taxon1: 66.67, taxon2: 33.33 } +*/ +export function formatTaxonAbundance(abundancePop: string[], abundanceTotal: number) { + if (!abundanceTotal) { + return {}; + } + + const abundancePopRecord: Record = {}; + abundancePop.forEach((value) => { + const [taxon, count] = value.split(":"); + const currentCount = abundancePopRecord[taxon] || 0; + abundancePopRecord[taxon] = currentCount + parseInt(count, 10); + }); + + return convertDictToPercentage(abundancePopRecord, abundanceTotal, "0"); +} + /* Compute score based on the scale define in for_score external data (see backed/catalog/inventaire_for) Ideally should be conmputed in the config.json but for now we can't do it. diff --git a/webapp/src/features/popup/bio-inventory/index.ts b/webapp/src/features/popup/bio-inventory/index.ts new file mode 100644 index 00000000..56a09ba3 --- /dev/null +++ b/webapp/src/features/popup/bio-inventory/index.ts @@ -0,0 +1,2 @@ +export { BioInventoryPopupContent } from "./popup-bio-inventory"; +export type { BioInventoryData } from "./types"; diff --git a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx new file mode 100644 index 00000000..83ea5e62 --- /dev/null +++ b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx @@ -0,0 +1,53 @@ +import { cx } from "class-variance-authority"; +import { Leaf } from "lucide-react"; +import { type FC } from "react"; + +import { ICON_SIZE_HEADER } from "@features/indicators/components/constants"; +import { IndicatorElements } from "@features/indicators/components/indicator-elements"; +import { IndicatorScrollContainer } from "@features/indicators/components/indicator-scroll-container"; +import { IndicatorPopupHeader } from "@features/popup/components/indicator-popup-header"; + +import { findCategoricalLabel, formatDate } from "@shared/lib/utils"; +import { useTranslation } from "@i18n"; + +import type { RenderPopupProps } from "../renderPopup"; +import type { BioInventoryData } from "./types"; +import { useBioInventoryIndicatorElements } from "@features/indicators/bio-inventory/use-bioinventory-indicator-elements"; + +type BioInventoryPopupContentProps = RenderPopupProps; + +export const BioInventoryPopupContent: FC< + BioInventoryPopupContentProps +> = ({ data, metadata, className, ...headerProps }) => { + const { t } = useTranslation(["common", "all4trees"]); + + const biodiversityElements = useBioInventoryIndicatorElements(data, metadata); + + console.log("BioInventoryPopupContent data", data, "metadata", metadata, "biodiversityElements", biodiversityElements); + const title = t("popup.bioInventory.title", { + id: data.id, + ns: "all4trees", + }); + + return ( +
+ } + subtitle={ + findCategoricalLabel(metadata, "loc2", data.forest) || + t("dataManagement.undefined", { ns: "common" }) + } + title={title} + {...headerProps} + /> + + + + +
+ ); +}; diff --git a/webapp/src/features/popup/bio-inventory/types.ts b/webapp/src/features/popup/bio-inventory/types.ts new file mode 100644 index 00000000..833706c0 --- /dev/null +++ b/webapp/src/features/popup/bio-inventory/types.ts @@ -0,0 +1,19 @@ +export type BioInventoryData = { + id: number; + conf: boolean; + proj: string; + ecos: number; + region: number; + forest: number; + start_date: Date; + taxon: string; + type: number; + samp_area: number; + samp_unit: number; + density: number; + dens_unit: string; + richness: number; + taxons_relative_abundance: Record; + pop_by_taxon: string[]; + total_pop: number; +} diff --git a/webapp/src/shared/api/categories-filters.ts b/webapp/src/shared/api/categories-filters.ts index 660533cf..3f57bce7 100644 --- a/webapp/src/shared/api/categories-filters.ts +++ b/webapp/src/shared/api/categories-filters.ts @@ -7,8 +7,9 @@ const SEPARATOR = ":::"; */ export const CATEGORY_IDENTIFIERS = { ACTION_DIVERSITY: "action-tree-diversity", - ACTION_INVENTARY: `action-forest${SEPARATOR}${LAYERS.INVENTARY}`, - ACTION_SOCIO: `action-socio-eco${SEPARATOR}${LAYERS.ENQUETE}`, + ACTION_INVENTORY: `action${SEPARATOR}${LAYERS.INVENTORY_FOR}`, + ACTION_SOCIO: `action${SEPARATOR}${LAYERS.ENQUETE}`, + ACTION_BIO: `action${SEPARATOR}${LAYERS.INVENTORY_BIO}`, DATA_GROUND: "data-ground", DATA_MODEL: "data-model", DATA_SATELLITE: `data${SEPARATOR}${LAYERS.SATELLITE}`, diff --git a/webapp/src/shared/api/layers.ts b/webapp/src/shared/api/layers.ts index 5ed1eb49..c5d73334 100644 --- a/webapp/src/shared/api/layers.ts +++ b/webapp/src/shared/api/layers.ts @@ -1,7 +1,8 @@ export const LAYERS = { BOUNDARIES: "boundaries", ENQUETE: "enquete", - INVENTARY: "inventaire_for", + INVENTORY_FOR: "inventaire_for", + INVENTORY_BIO: "inventaire_bio", SATELLITE: "satellite", SEED: "seed", SEED_POINT: "seed_point", diff --git a/webapp/src/shared/contexts/map-context-all4trees.ts b/webapp/src/shared/contexts/map-context-all4trees.ts index 5c16b5dd..f248e46a 100644 --- a/webapp/src/shared/contexts/map-context-all4trees.ts +++ b/webapp/src/shared/contexts/map-context-all4trees.ts @@ -3,17 +3,12 @@ import { createContext, type RefCallback, type RefObject } from "react"; import type { CategoriesFiltersState } from "@shared/api/categories-filters"; import type { SetValue } from "@shared/hooks/use-local-storage"; import type { createMap } from "@shared/lib/coordo"; - -export type Category = { value: string; label: string }; - export interface MapContextType { categoriesFilters: CategoriesFiltersState; - forests: Category[]; isReady: boolean; mapApiRef: RefObject | null>; mapContainerRef: RefCallback; setCategoriesFilters: (setter: SetValue) => void; - setForests: (forests: Category[]) => void; setIsReady: (isReady: boolean) => void; } diff --git a/webapp/src/shared/i18n/translations/en/all4trees.json b/webapp/src/shared/i18n/translations/en/all4trees.json index a91856ce..d0b9565c 100644 --- a/webapp/src/shared/i18n/translations/en/all4trees.json +++ b/webapp/src/shared/i18n/translations/en/all4trees.json @@ -7,7 +7,7 @@ "filters": { "categories": { "actions": { - "forestInventary": "Forest inventory", + "forestInventory": "Forest inventory", "socioEco": "Socio-economic", "title": "Actions taken", "treeDiversity": "Tree diversity" @@ -224,6 +224,10 @@ "subtitleCount_one": "{{ count }} household surveyed", "subtitleCount_other": "{{ count }} households surveyed", "title": "{{ village }}" + }, + "bioInventory": { + "date": "Survey date: {{ date }}", + "title": "Plot n°{{ id }}" } } -} +} \ No newline at end of file diff --git a/webapp/src/shared/i18n/translations/en/common.json b/webapp/src/shared/i18n/translations/en/common.json index 00ddf154..0c336c74 100644 --- a/webapp/src/shared/i18n/translations/en/common.json +++ b/webapp/src/shared/i18n/translations/en/common.json @@ -19,7 +19,8 @@ }, "dataManagement": { "noData": "No data available", - "undefined": "not found" + "undefined": "not found", + "noUnit": "Unit undefined" }, "error": { "pleaseLogin": "Please sign in", diff --git a/webapp/src/shared/i18n/translations/fr/all4trees.json b/webapp/src/shared/i18n/translations/fr/all4trees.json index 80dc30d6..d7b43f42 100644 --- a/webapp/src/shared/i18n/translations/fr/all4trees.json +++ b/webapp/src/shared/i18n/translations/fr/all4trees.json @@ -7,7 +7,7 @@ "filters": { "categories": { "actions": { - "forestInventary": "Inventaire forestier", + "forestInventory": "Inventaire forestier", "socioEco": "Socio-éco", "title": "Actions menées", "treeDiversity": "Diversité arborée" @@ -48,7 +48,6 @@ "other": "Autre", "title": "Abondance relative par essence" }, - "speciesRichness": "Richesse spécifique", "title": "Diversité arborée" }, "tropicalBiodivIndex": { @@ -66,6 +65,12 @@ }, "title": "Biodiversité" }, + "bioinventory": { + "title": "Inventaire de faune", + "taxon": "Taxon cible", + "type": "Type de dispositif", + "sampArea": "Effort d'échantillonnage" + }, "common": { "abundance": "Abondance relative", "beneficiary": "Bénef.", @@ -228,6 +233,10 @@ "subtitleCount_one": "{{ count }} ménage enquêté", "subtitleCount_other": "{{ count }} ménages enquêtés", "title": "{{ village }}" + }, + "bioInventory": { + "date": "Date relevé: {{ date }}", + "title": "Placette n°{{ id }}" } } -} +} \ No newline at end of file diff --git a/webapp/src/shared/i18n/translations/fr/common.json b/webapp/src/shared/i18n/translations/fr/common.json index 57013943..116d9bfa 100644 --- a/webapp/src/shared/i18n/translations/fr/common.json +++ b/webapp/src/shared/i18n/translations/fr/common.json @@ -19,7 +19,8 @@ }, "dataManagement": { "noData": "Données non disponibles", - "undefined": "non trouvée" + "undefined": "non trouvée", + "noUnit": "Unité non définie" }, "error": { "pleaseLogin": "Veuillez vous connecter", diff --git a/webapp/src/widgets/map/map-all4trees.tsx b/webapp/src/widgets/map/map-all4trees.tsx index ce7544b3..af3037a7 100644 --- a/webapp/src/widgets/map/map-all4trees.tsx +++ b/webapp/src/widgets/map/map-all4trees.tsx @@ -22,9 +22,10 @@ import pictoInventaire from "./assets/inventaire-icon.svg"; import pictoSocioEco from "./assets/socio-eco-icon.svg"; import { MapBase } from "./map-base"; import { getExternalDataPromiseByLayer, getIconSize } from "./utils"; +import { BioInventoryPopupContent, type BioInventoryData } from "@features/popup/bio-inventory"; export const MapAll4Trees: FC = () => { - const { isReady, mapApiRef, forests, mapContainerRef } = useMap(); + const { isReady, mapApiRef, mapContainerRef } = useMap(); // We need to pass the api as prop to the popup content, so we can fetch data from the catalog. // We can't call useApi() from inside the popup as it is created dynamically and not part of the React tree. const api = useApi(); @@ -36,7 +37,7 @@ export const MapAll4Trees: FC = () => { mapApiRef.current.setLayerSymbol({ iconSize: getIconSize({}), - layerId: LAYERS.INVENTARY, + layerId: LAYERS.INVENTORY_FOR, svg: pictoInventaire, }); @@ -46,14 +47,20 @@ export const MapAll4Trees: FC = () => { svg: pictoSocioEco, }); - // Set the popup for the "inventaire" layer + mapApiRef.current.setLayerSymbol({ + iconSize: getIconSize({}), + layerId: LAYERS.INVENTORY_BIO, + svg: pictoInventaire, + }); + + // Set the popup for the "inventaire_for" layer mapApiRef.current.setLayerPopup({ centerOnClick: true, - layerId: LAYERS.INVENTARY, + layerId: LAYERS.INVENTORY_FOR, popupConfig: DEFAULT_POPUP_CONFIG, renderCallback: getRenderPopupLayer({ Element: ForestInventoryPopupContent, - getExternalData: getExternalDataPromiseByLayer(LAYERS.INVENTARY, api), + getExternalData: getExternalDataPromiseByLayer(LAYERS.INVENTORY_FOR, api), toggleShiftSize, }), trigger: "click", @@ -72,6 +79,19 @@ export const MapAll4Trees: FC = () => { trigger: "click", }); + // Set the popup for the "inventaire_bio" layer + mapApiRef.current.setLayerPopup({ + centerOnClick: true, + layerId: LAYERS.INVENTORY_BIO, + popupConfig: DEFAULT_POPUP_CONFIG, + renderCallback: getRenderPopupLayer({ + Element: BioInventoryPopupContent, + getExternalData: getExternalDataPromiseByLayer(LAYERS.INVENTORY_BIO, api), + toggleShiftSize, + }), + trigger: "click", + }); + // Set the popup for the Seed data layer mapApiRef.current.setLayerPopup({ centerOnClick: true, @@ -86,45 +106,11 @@ export const MapAll4Trees: FC = () => { }); }, [isReady, mapApiRef, api]); - const filterByForest = (forestId: string) => { - mapApiRef.current?.setLayerFilters({ - filters: { args: [{ property: "for" }, forestId], op: "=" }, - layerId: "inventaire", - }); - }; - - const resetFilter = () => { - mapApiRef.current?.setLayerFilters({ - filters: null, - layerId: "inventaire", - }); - }; - return ( - {forests.length > 0 && ( -
- {forests.map((forest) => ( - - ))} - -
- )} -
+ /> ); }; From dc9afb825e73f8ae69b118839243336b587ea55b Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Thu, 9 Jul 2026 23:10:18 +0200 Subject: [PATCH 04/16] Lint --- .../app/providers/map-provider-all4trees.tsx | 4 +--- .../components/chart-taxon-abundance.tsx | 2 +- .../features/charts/components/lib/taxon.ts | 2 +- .../indicators/bio-inventory/format-data.ts | 20 +++++++++------- .../use-bioinventory-indicator-elements.tsx | 7 +++--- .../soil/use-soil-indicator-elements.tsx | 2 +- webapp/src/features/indicators/utils.ts | 5 +++- .../bio-inventory/popup-bio-inventory.tsx | 24 +++++++++++++------ .../src/features/popup/bio-inventory/types.ts | 2 +- webapp/src/shared/api/categories-filters.ts | 2 +- webapp/src/shared/api/layers.ts | 2 +- .../i18n/translations/en/all4trees.json | 10 ++++---- .../shared/i18n/translations/en/common.json | 4 ++-- .../i18n/translations/fr/all4trees.json | 16 ++++++------- .../shared/i18n/translations/fr/common.json | 4 ++-- webapp/src/widgets/map/map-all4trees.tsx | 15 +++++++++--- 16 files changed, 73 insertions(+), 48 deletions(-) diff --git a/webapp/src/app/providers/map-provider-all4trees.tsx b/webapp/src/app/providers/map-provider-all4trees.tsx index f6634378..b28f1db2 100644 --- a/webapp/src/app/providers/map-provider-all4trees.tsx +++ b/webapp/src/app/providers/map-provider-all4trees.tsx @@ -5,9 +5,7 @@ import { useCategoriesFilters } from "@features/categories-filters/use-categorie import { renderAnchor, renderLayerRow } from "@features/controls/layer-control"; import { API_URL } from "@shared/api/client"; -import { - MapContext, -} from "@shared/contexts/map-context-all4trees"; +import { MapContext } from "@shared/contexts/map-context-all4trees"; import { useLocalStorage } from "@shared/hooks/use-local-storage"; import { createMap, EVENTS } from "@shared/lib/coordo"; diff --git a/webapp/src/features/charts/components/chart-taxon-abundance.tsx b/webapp/src/features/charts/components/chart-taxon-abundance.tsx index 213e8002..77065be8 100644 --- a/webapp/src/features/charts/components/chart-taxon-abundance.tsx +++ b/webapp/src/features/charts/components/chart-taxon-abundance.tsx @@ -10,8 +10,8 @@ import { import { useTranslation } from "@shared/i18n"; import { SUNBURST_LAYOUT } from "../soil/config"; -import { buildNodeColors, buildSunburstNodes } from "./lib/sunburst"; import type { PieChartProps, SunburstTrace } from "../soil/types"; +import { buildNodeColors, buildSunburstNodes } from "./lib/sunburst"; export const ChartTaxonAbundance: ChartComponentType = ({ data, diff --git a/webapp/src/features/charts/components/lib/taxon.ts b/webapp/src/features/charts/components/lib/taxon.ts index 06a6f658..b419ce1a 100644 --- a/webapp/src/features/charts/components/lib/taxon.ts +++ b/webapp/src/features/charts/components/lib/taxon.ts @@ -2,7 +2,7 @@ import { findLabelInExternalData } from "@features/indicators/utils"; import type { ExternalData } from "@features/popup/forest-inventory/types"; export function getDataTypePrefix(dataType: "tsbf" | "barbA" | null): string { - return dataType ? `${dataType}_` : ''; + return dataType ? `${dataType}_` : ""; } export function getTaxonLabels( diff --git a/webapp/src/features/indicators/bio-inventory/format-data.ts b/webapp/src/features/indicators/bio-inventory/format-data.ts index 31f8c999..72e3cfdd 100644 --- a/webapp/src/features/indicators/bio-inventory/format-data.ts +++ b/webapp/src/features/indicators/bio-inventory/format-data.ts @@ -4,12 +4,11 @@ import { formatTaxonAbundance, preciseNumericIndicators, } from "@features/indicators/utils"; - -import type { NumericKeys } from "@shared/types"; - import type { BioInventoryData } from "@features/popup/bio-inventory"; -import { findCategoricalLabel } from "@shared/lib/utils"; + import type { LayerMetadata } from "@shared/lib/coordo"; +import { findCategoricalLabel } from "@shared/lib/utils"; +import type { NumericKeys } from "@shared/types"; const indicatorKeys: NumericKeys[] = [ "samp_area", @@ -21,7 +20,10 @@ const indicatorKeys: NumericKeys[] = [ /** * Return data in a convenient way for UI rendering, handling units and fixing */ -export const useFormatBioInventoryData = (data: BioInventoryData, metadata: LayerMetadata) => { +export const useFormatBioInventoryData = ( + data: BioInventoryData, + metadata: LayerMetadata, +) => { const { t } = useTranslation("common"); const safeData = preciseNumericIndicators( @@ -36,13 +38,15 @@ export const useFormatBioInventoryData = (data: BioInventoryData, metadata: Laye ); return { - taxon: 'Lémuriens', // findCategoricalLabel(metadata, "tax", safeData.taxon) || safeData.taxon - type: findCategoricalLabel(metadata, "meth", safeData.type.toString()) || safeData.type, area: `${safeData.samp_area} ${findCategoricalLabel(metadata, "inv_unit", safeData.samp_unit) || t("dataManagement.noUnit")}`, density: `${safeData.density} ${safeData.dens_unit || t("dataManagement.noUnit")}`, - richness: safeData.richness, relative_abundance: safeData.taxons_relative_abundance, + richness: safeData.richness, + taxon: "Lémuriens", // findCategoricalLabel(metadata, "tax", safeData.taxon) || safeData.taxon total_population: safeData.total_pop, + type: + findCategoricalLabel(metadata, "meth", safeData.type.toString()) || + safeData.type, }; }; diff --git a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx index 5bb48601..0e16105b 100644 --- a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx +++ b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx @@ -1,4 +1,7 @@ -import { PawPrint, SearchAlert, Gem } from "lucide-react"; +import { Gem, PawPrint, SearchAlert } from "lucide-react"; + +import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; +import type { BioInventoryData } from "@features/popup/bio-inventory/types"; import type { LayerMetadata } from "@shared/lib/coordo"; import { useTranslation } from "@i18n"; @@ -7,8 +10,6 @@ import { ICON_SIZE } from "../components/constants"; import { IndicatorRawValue } from "../components/indicator-raw-value"; import type { UseIndicatorReturnType } from "../components/types"; import { useFormatBioInventoryData } from "./format-data"; -import type { BioInventoryData } from "@features/popup/bio-inventory/types"; -import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; export const useBioInventoryIndicatorElements = ( rawData: BioInventoryData, diff --git a/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx b/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx index 2c24539f..fc1b0386 100644 --- a/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx +++ b/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx @@ -1,7 +1,7 @@ import { Bug, Gem, Sprout } from "lucide-react"; -import { ChartAquaticErosion } from "@features/charts/soil/ui/chart-aquatic-erosion"; import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; +import { ChartAquaticErosion } from "@features/charts/soil/ui/chart-aquatic-erosion"; import { ChartWindErosion } from "@features/charts/soil/ui/chart-wind-erosion"; import type { UseIndicatorReturnType } from "@features/indicators//components/types"; import { IndicatorRawValue } from "@features/indicators/components/indicator-raw-value"; diff --git a/webapp/src/features/indicators/utils.ts b/webapp/src/features/indicators/utils.ts index ed453889..0b4f7846 100644 --- a/webapp/src/features/indicators/utils.ts +++ b/webapp/src/features/indicators/utils.ts @@ -130,7 +130,10 @@ export function convertDictToPercentage( Input: ['taxon1:10', 'taxon2:20', 'taxon1:30'], totalPopulation = 60 Output: { taxon1: 66.67, taxon2: 33.33 } */ -export function formatTaxonAbundance(abundancePop: string[], abundanceTotal: number) { +export function formatTaxonAbundance( + abundancePop: string[], + abundanceTotal: number, +) { if (!abundanceTotal) { return {}; } diff --git a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx index 83ea5e62..b2a72a13 100644 --- a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx +++ b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx @@ -1,7 +1,8 @@ import { cx } from "class-variance-authority"; import { Leaf } from "lucide-react"; -import { type FC } from "react"; +import type { FC } from "react"; +import { useBioInventoryIndicatorElements } from "@features/indicators/bio-inventory/use-bioinventory-indicator-elements"; import { ICON_SIZE_HEADER } from "@features/indicators/components/constants"; import { IndicatorElements } from "@features/indicators/components/indicator-elements"; import { IndicatorScrollContainer } from "@features/indicators/components/indicator-scroll-container"; @@ -12,18 +13,27 @@ import { useTranslation } from "@i18n"; import type { RenderPopupProps } from "../renderPopup"; import type { BioInventoryData } from "./types"; -import { useBioInventoryIndicatorElements } from "@features/indicators/bio-inventory/use-bioinventory-indicator-elements"; type BioInventoryPopupContentProps = RenderPopupProps; -export const BioInventoryPopupContent: FC< - BioInventoryPopupContentProps -> = ({ data, metadata, className, ...headerProps }) => { +export const BioInventoryPopupContent: FC = ({ + data, + metadata, + className, + ...headerProps +}) => { const { t } = useTranslation(["common", "all4trees"]); const biodiversityElements = useBioInventoryIndicatorElements(data, metadata); - console.log("BioInventoryPopupContent data", data, "metadata", metadata, "biodiversityElements", biodiversityElements); + console.log( + "BioInventoryPopupContent data", + data, + "metadata", + metadata, + "biodiversityElements", + biodiversityElements, + ); const title = t("popup.bioInventory.title", { id: data.id, ns: "all4trees", @@ -46,7 +56,7 @@ export const BioInventoryPopupContent: FC< /> - + ); diff --git a/webapp/src/features/popup/bio-inventory/types.ts b/webapp/src/features/popup/bio-inventory/types.ts index 833706c0..0b7cda65 100644 --- a/webapp/src/features/popup/bio-inventory/types.ts +++ b/webapp/src/features/popup/bio-inventory/types.ts @@ -16,4 +16,4 @@ export type BioInventoryData = { taxons_relative_abundance: Record; pop_by_taxon: string[]; total_pop: number; -} +}; diff --git a/webapp/src/shared/api/categories-filters.ts b/webapp/src/shared/api/categories-filters.ts index 3f57bce7..44b195d4 100644 --- a/webapp/src/shared/api/categories-filters.ts +++ b/webapp/src/shared/api/categories-filters.ts @@ -6,10 +6,10 @@ const SEPARATOR = ":::"; * Format of a layer: GROUP_BACKEND-NAME */ export const CATEGORY_IDENTIFIERS = { + ACTION_BIO: `action${SEPARATOR}${LAYERS.INVENTORY_BIO}`, ACTION_DIVERSITY: "action-tree-diversity", ACTION_INVENTORY: `action${SEPARATOR}${LAYERS.INVENTORY_FOR}`, ACTION_SOCIO: `action${SEPARATOR}${LAYERS.ENQUETE}`, - ACTION_BIO: `action${SEPARATOR}${LAYERS.INVENTORY_BIO}`, DATA_GROUND: "data-ground", DATA_MODEL: "data-model", DATA_SATELLITE: `data${SEPARATOR}${LAYERS.SATELLITE}`, diff --git a/webapp/src/shared/api/layers.ts b/webapp/src/shared/api/layers.ts index c5d73334..3ae1434d 100644 --- a/webapp/src/shared/api/layers.ts +++ b/webapp/src/shared/api/layers.ts @@ -1,8 +1,8 @@ export const LAYERS = { BOUNDARIES: "boundaries", ENQUETE: "enquete", - INVENTORY_FOR: "inventaire_for", INVENTORY_BIO: "inventaire_bio", + INVENTORY_FOR: "inventaire_for", SATELLITE: "satellite", SEED: "seed", SEED_POINT: "seed_point", diff --git a/webapp/src/shared/i18n/translations/en/all4trees.json b/webapp/src/shared/i18n/translations/en/all4trees.json index d0b9565c..e5e0a698 100644 --- a/webapp/src/shared/i18n/translations/en/all4trees.json +++ b/webapp/src/shared/i18n/translations/en/all4trees.json @@ -213,6 +213,10 @@ "villages": "Villages" }, "popup": { + "bioInventory": { + "date": "Survey date: {{ date }}", + "title": "Plot n°{{ id }}" + }, "forestInventory": { "date": "Site survey date : {{ date }}", "ecos": "Ecosystem", @@ -224,10 +228,6 @@ "subtitleCount_one": "{{ count }} household surveyed", "subtitleCount_other": "{{ count }} households surveyed", "title": "{{ village }}" - }, - "bioInventory": { - "date": "Survey date: {{ date }}", - "title": "Plot n°{{ id }}" } } -} \ No newline at end of file +} diff --git a/webapp/src/shared/i18n/translations/en/common.json b/webapp/src/shared/i18n/translations/en/common.json index 0c336c74..3ffc6c18 100644 --- a/webapp/src/shared/i18n/translations/en/common.json +++ b/webapp/src/shared/i18n/translations/en/common.json @@ -19,8 +19,8 @@ }, "dataManagement": { "noData": "No data available", - "undefined": "not found", - "noUnit": "Unit undefined" + "noUnit": "Unit undefined", + "undefined": "not found" }, "error": { "pleaseLogin": "Please sign in", diff --git a/webapp/src/shared/i18n/translations/fr/all4trees.json b/webapp/src/shared/i18n/translations/fr/all4trees.json index d7b43f42..b46b0bf3 100644 --- a/webapp/src/shared/i18n/translations/fr/all4trees.json +++ b/webapp/src/shared/i18n/translations/fr/all4trees.json @@ -66,10 +66,10 @@ "title": "Biodiversité" }, "bioinventory": { - "title": "Inventaire de faune", + "sampArea": "Effort d'échantillonnage", "taxon": "Taxon cible", - "type": "Type de dispositif", - "sampArea": "Effort d'échantillonnage" + "title": "Inventaire de faune", + "type": "Type de dispositif" }, "common": { "abundance": "Abondance relative", @@ -222,6 +222,10 @@ "villages": "Villages" }, "popup": { + "bioInventory": { + "date": "Date relevé: {{ date }}", + "title": "Placette n°{{ id }}" + }, "forestInventory": { "date": "Date relevé: {{ date }}", "ecos": "Ecosystème", @@ -233,10 +237,6 @@ "subtitleCount_one": "{{ count }} ménage enquêté", "subtitleCount_other": "{{ count }} ménages enquêtés", "title": "{{ village }}" - }, - "bioInventory": { - "date": "Date relevé: {{ date }}", - "title": "Placette n°{{ id }}" } } -} \ No newline at end of file +} diff --git a/webapp/src/shared/i18n/translations/fr/common.json b/webapp/src/shared/i18n/translations/fr/common.json index 116d9bfa..79c3f67c 100644 --- a/webapp/src/shared/i18n/translations/fr/common.json +++ b/webapp/src/shared/i18n/translations/fr/common.json @@ -19,8 +19,8 @@ }, "dataManagement": { "noData": "Données non disponibles", - "undefined": "non trouvée", - "noUnit": "Unité non définie" + "noUnit": "Unité non définie", + "undefined": "non trouvée" }, "error": { "pleaseLogin": "Veuillez vous connecter", diff --git a/webapp/src/widgets/map/map-all4trees.tsx b/webapp/src/widgets/map/map-all4trees.tsx index af3037a7..384b614e 100644 --- a/webapp/src/widgets/map/map-all4trees.tsx +++ b/webapp/src/widgets/map/map-all4trees.tsx @@ -1,6 +1,10 @@ import { type FC, useEffect, useState } from "react"; import { type SeedData, SeedIndicator } from "@features/indicators/seed"; +import { + type BioInventoryData, + BioInventoryPopupContent, +} from "@features/popup/bio-inventory"; import { type ForestInventoryData, ForestInventoryPopupContent, @@ -22,7 +26,6 @@ import pictoInventaire from "./assets/inventaire-icon.svg"; import pictoSocioEco from "./assets/socio-eco-icon.svg"; import { MapBase } from "./map-base"; import { getExternalDataPromiseByLayer, getIconSize } from "./utils"; -import { BioInventoryPopupContent, type BioInventoryData } from "@features/popup/bio-inventory"; export const MapAll4Trees: FC = () => { const { isReady, mapApiRef, mapContainerRef } = useMap(); @@ -60,7 +63,10 @@ export const MapAll4Trees: FC = () => { popupConfig: DEFAULT_POPUP_CONFIG, renderCallback: getRenderPopupLayer({ Element: ForestInventoryPopupContent, - getExternalData: getExternalDataPromiseByLayer(LAYERS.INVENTORY_FOR, api), + getExternalData: getExternalDataPromiseByLayer( + LAYERS.INVENTORY_FOR, + api, + ), toggleShiftSize, }), trigger: "click", @@ -86,7 +92,10 @@ export const MapAll4Trees: FC = () => { popupConfig: DEFAULT_POPUP_CONFIG, renderCallback: getRenderPopupLayer({ Element: BioInventoryPopupContent, - getExternalData: getExternalDataPromiseByLayer(LAYERS.INVENTORY_BIO, api), + getExternalData: getExternalDataPromiseByLayer( + LAYERS.INVENTORY_BIO, + api, + ), toggleShiftSize, }), trigger: "click", From 224f9e0caa0d708a7a4bd7254355a1187d457ec0 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Fri, 10 Jul 2026 10:00:02 +0200 Subject: [PATCH 05/16] Use external Data --- backend/Makefile | 5 +- backend/configs/all4trees_config.json | 8 +-- webapp/src/{shared/api => entities}/layers.ts | 0 webapp/src/entities/resources.ts | 16 +++++ .../biodiversity/chart-relative-abundance.tsx | 4 +- .../components/chart-taxon-abundance.tsx | 11 ++- .../charts/components/lib/sunburst.ts | 9 +-- .../features/charts/components/lib/taxon.ts | 23 +++--- webapp/src/features/charts/soil/types.ts | 4 +- .../src/features/controls/layer-control.tsx | 2 +- .../indicators/bio-inventory/format-data.ts | 43 ++++++++--- .../use-bioinventory-indicator-elements.tsx | 11 +-- .../use-biodiversity-indicator-elements.tsx | 4 +- .../features/indicators/soil/format-data.ts | 4 +- webapp/src/features/indicators/utils.ts | 72 ++++++++++++++++--- .../bio-inventory/popup-bio-inventory.tsx | 32 ++++++--- .../popup-forest-inventory.tsx | 4 ++ webapp/src/shared/api/categories-filters.ts | 2 +- .../i18n/translations/en/all4trees.json | 13 +++- .../i18n/translations/fr/all4trees.json | 9 ++- webapp/src/widgets/dashboard/dashboard.tsx | 2 +- webapp/src/widgets/map/map-all4trees.tsx | 2 +- webapp/src/widgets/map/map-seed.tsx | 2 +- webapp/src/widgets/map/utils.ts | 11 +-- 24 files changed, 208 insertions(+), 85 deletions(-) rename webapp/src/{shared/api => entities}/layers.ts (100%) create mode 100644 webapp/src/entities/resources.ts diff --git a/backend/Makefile b/backend/Makefile index fe499476..546ee976 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -27,12 +27,11 @@ catalog: coordo add foreignkey barbd_001.barbD_tax3 for_mf_tax3.tax3 --package catalog/inventaire_for # All 4 Trees - Inventaire Biologique - coordo add kobotoolbox data/all4trees/inventaire_bio/20260422_InventaireBiologique_DonneesK.xlsx --form data/all4trees/inventaire_bio/20260519_InventaireBiologique_QuestionnaireK.xlsx --package catalog/inventaire_bio - coordo add file data/all4trees/inventaire_bio/20260422_InventaireBiologique_DonneesExternes.xlsx --package catalog/inventaire_bio + coordo add kobotoolbox data/all4trees/inventaire_bio/20260422_InventaireBiologique_DonneesK.xlsx --form data/all4trees/inventaire_bio/20260709_InventaireBiologique_QuestionnaireK.xlsx --package catalog/inventaire_bio + coordo add file data/all4trees/inventaire_bio/20260709_InventaireBiologique_DonneesExternes.xlsx --package catalog/inventaire_bio coordo add foreignkey inv_bio.proj bio_samp.proj --package catalog/inventaire_bio coordo add foreignkey inv_bio.year bio_pop.year --package catalog/inventaire_bio - coordo add foreignkey inv_001.tax3 bio_sp.tax3 --package catalog/inventaire_bio # All 4 Trees - Enquête ménage coordo add kobotoolbox data/all4trees/enquete/20260422_EnqueteMenage_DonneesK.csv --form data/all4trees/enquete/20260519_EnqueteMenage_QuestionnaireK.xlsx --package catalog/enquete diff --git a/backend/configs/all4trees_config.json b/backend/configs/all4trees_config.json index 59a2fe40..d0d7d5db 100644 --- a/backend/configs/all4trees_config.json +++ b/backend/configs/all4trees_config.json @@ -97,12 +97,6 @@ "path": "../catalog/inventaire_bio", "resource": "inv_bio", "layerType": "symbol", - "cluster": { - "radius": 50, - "maxZoom": 50, - "steps": [10, 20], - "colors": ["#99D6C2", "#009966", "#006B47"] - }, "columns": { "geom": "start_point.merge().centroid()", "id": "_id", @@ -112,7 +106,7 @@ "region": "loc1", "forest": "loc2", "start_date": "coh", - "taxon": "'N/A'", + "taxon": "tax", "type": "meth", "samp_area": "inv_samp", "samp_unit": "inv_unit", diff --git a/webapp/src/shared/api/layers.ts b/webapp/src/entities/layers.ts similarity index 100% rename from webapp/src/shared/api/layers.ts rename to webapp/src/entities/layers.ts diff --git a/webapp/src/entities/resources.ts b/webapp/src/entities/resources.ts new file mode 100644 index 00000000..ae63d03e --- /dev/null +++ b/webapp/src/entities/resources.ts @@ -0,0 +1,16 @@ +import { LAYERS } from "./layers"; + +export const EXTERNAL_RESOURCES_BY_LAYER = new Map([ + [ + LAYERS.INVENTORY_FOR, + ["for_label", "for_mf_tax1", "for_mf_tax2", "for_mf_tax3", "for_score"], + ], + [LAYERS.INVENTORY_BIO, ["bio_label"]], + [LAYERS.ENQUETE, [""]], + [LAYERS.SEED_POINT, [""]], +]); + +export const LABEL_DATA = new Map([ + [LAYERS.INVENTORY_FOR, "for_label"], + [LAYERS.INVENTORY_BIO, "bio_label"] +]); \ No newline at end of file diff --git a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx index 5d4e56ad..2b631a37 100644 --- a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx +++ b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx @@ -1,7 +1,7 @@ import { findLabelInExternalData } from "@features/indicators/utils"; import type { ExternalData } from "@features/popup/forest-inventory/types"; -import { useTranslation } from "@shared/i18n"; +import { i18nInstance, useTranslation } from "@shared/i18n"; import type { ChartConfig } from "@shared/ui/chart"; import type { ChartComponentType } from "../components/chart-component"; @@ -19,6 +19,7 @@ export const ChartRelativeAbundance: ChartComponentType = ({ externalData, }) => { const { t } = useTranslation("all4trees"); + const lang = i18nInstance.language; const smallCategoriesSum = Object.values(data) .filter((value) => value < 5) .reduce((acc, value) => acc + value, 0); @@ -43,6 +44,7 @@ export const ChartRelativeAbundance: ChartComponentType = ({ externalData, "for_label", project, + lang, "ess", Number(element.name), ) || element.name, diff --git a/webapp/src/features/charts/components/chart-taxon-abundance.tsx b/webapp/src/features/charts/components/chart-taxon-abundance.tsx index 77065be8..cd8b7daf 100644 --- a/webapp/src/features/charts/components/chart-taxon-abundance.tsx +++ b/webapp/src/features/charts/components/chart-taxon-abundance.tsx @@ -7,7 +7,7 @@ import { type ChartComponentType, } from "@features/charts/components/chart-component"; -import { useTranslation } from "@shared/i18n"; +import { i18nInstance, useTranslation } from "@shared/i18n"; import { SUNBURST_LAYOUT } from "../soil/config"; import type { PieChartProps, SunburstTrace } from "../soil/types"; @@ -19,7 +19,7 @@ export const ChartTaxonAbundance: ChartComponentType = ({ project, }) => { const { t } = useTranslation(["common", "all4trees"]); - + const lang = i18nInstance.language; const dataEntries = Object.entries(data); const hasTaxonData = dataEntries.some(([key]) => key.trim() !== ""); let sunburstData: PlotlyData[] = []; @@ -30,7 +30,12 @@ export const ChartTaxonAbundance: ChartComponentType = ({ const filteredDataEntries = dataEntries.filter( ([key]) => key.trim() !== "0", ); - const nodes = buildSunburstNodes(filteredDataEntries, metadata, project); + const nodes = buildSunburstNodes( + filteredDataEntries, + metadata, + project, + lang, + ); const nodeColors = buildNodeColors(nodes); const hoverText = nodes.map( (node) => `${node.label}
${node.value.toFixed(2)} %`, diff --git a/webapp/src/features/charts/components/lib/sunburst.ts b/webapp/src/features/charts/components/lib/sunburst.ts index 11f8c98a..c0c0edd3 100644 --- a/webapp/src/features/charts/components/lib/sunburst.ts +++ b/webapp/src/features/charts/components/lib/sunburst.ts @@ -1,6 +1,6 @@ import { darken, lighten } from "color2k"; -import type { ExternalData } from "@features/popup/forest-inventory/types"; +import type { LabelData } from "@features/popup/forest-inventory/types"; import { getChartPalette } from "@shared/lib/palette"; @@ -9,8 +9,9 @@ import { formatTaxonLevelLabel } from "./taxon"; export function buildSunburstNodes( dataEntries: [string, number][], - metadata: ExternalData, + metadata: LabelData[], project: string, + lang: string, ): SunburstNode[] { const nodes = new Map(); for (const [key, value] of dataEntries) { @@ -18,7 +19,7 @@ export function buildSunburstNodes( nodes.set(key, { depth: parts.length - 1, id: key, - label: formatTaxonLevelLabel(key, metadata, project), + label: formatTaxonLevelLabel(key, metadata, project, lang), parent: parts.slice(0, -1).join("-") || "", value, }); @@ -33,7 +34,7 @@ export function buildSunburstNodes( nodes.set(parentPath, { depth: parentPath.split("-").length - 1, id: parentPath, - label: formatTaxonLevelLabel(parentPath, metadata, project), + label: formatTaxonLevelLabel(parentPath, metadata, project, lang), parent: parts.slice(0, i - 1).join("-") || "", value: value, }); diff --git a/webapp/src/features/charts/components/lib/taxon.ts b/webapp/src/features/charts/components/lib/taxon.ts index b419ce1a..e6e323e4 100644 --- a/webapp/src/features/charts/components/lib/taxon.ts +++ b/webapp/src/features/charts/components/lib/taxon.ts @@ -1,5 +1,5 @@ -import { findLabelInExternalData } from "@features/indicators/utils"; -import type { ExternalData } from "@features/popup/forest-inventory/types"; +import { findLabelInExternalData2 } from "@features/indicators/utils"; +import type { LabelData } from "@features/popup/forest-inventory/types"; export function getDataTypePrefix(dataType: "tsbf" | "barbA" | null): string { return dataType ? `${dataType}_` : ""; @@ -7,31 +7,32 @@ export function getDataTypePrefix(dataType: "tsbf" | "barbA" | null): string { export function getTaxonLabels( element: string, - metadata: ExternalData, + metadata: LabelData[], project: string, + lang: string, ): [string, string, string] { const [taxon1, taxon2, taxon3] = element.split("-"); const taxon1Label = - findLabelInExternalData( + findLabelInExternalData2( metadata, - "for_label", project, + lang, "tax1", Number(taxon1), ) || taxon1; const taxon2Label = - findLabelInExternalData( + findLabelInExternalData2( metadata, - "for_label", project, + lang, "tax2", Number(taxon2), ) || taxon2; const taxon3Label = - findLabelInExternalData( + findLabelInExternalData2( metadata, - "for_label", project, + lang, "tax3", Number(taxon3), ) || taxon3; @@ -40,13 +41,15 @@ export function getTaxonLabels( export function formatTaxonLevelLabel( element: string, - metadata: ExternalData, + metadata: LabelData[], project: string, + lang: string, ): string { const [taxon1Label, taxon2Label, taxon3Label] = getTaxonLabels( element, metadata, project, + lang, ); const parts = element.split("-"); return parts.length === 1 diff --git a/webapp/src/features/charts/soil/types.ts b/webapp/src/features/charts/soil/types.ts index c9b68d3d..e4705762 100644 --- a/webapp/src/features/charts/soil/types.ts +++ b/webapp/src/features/charts/soil/types.ts @@ -1,8 +1,8 @@ -import type { ExternalData } from "@features/popup/forest-inventory/types"; +import type { LabelData } from "@features/popup/forest-inventory/types"; export type PieChartProps = { data: Record; - metadata: ExternalData; + metadata: LabelData[]; project: string; }; diff --git a/webapp/src/features/controls/layer-control.tsx b/webapp/src/features/controls/layer-control.tsx index 4789ac60..3ed7e80d 100644 --- a/webapp/src/features/controls/layer-control.tsx +++ b/webapp/src/features/controls/layer-control.tsx @@ -10,7 +10,7 @@ import { import { Activity, useEffect, useRef, useState } from "react"; import { createRoot } from "react-dom/client"; -import { LAYERS } from "@shared/api/layers"; +import { LAYERS } from "@entities/layers"; import { useTranslation } from "@shared/i18n"; import { LAYER_CONTROL_ELEMENTS, diff --git a/webapp/src/features/indicators/bio-inventory/format-data.ts b/webapp/src/features/indicators/bio-inventory/format-data.ts index 72e3cfdd..bdaee0fb 100644 --- a/webapp/src/features/indicators/bio-inventory/format-data.ts +++ b/webapp/src/features/indicators/bio-inventory/format-data.ts @@ -1,13 +1,16 @@ import { useTranslation } from "react-i18next"; import { + findLabelInExternalData2, formatTaxonAbundance, preciseNumericIndicators, + UNITS, + useFormatterWithUnit, } from "@features/indicators/utils"; import type { BioInventoryData } from "@features/popup/bio-inventory"; +import type { LabelData } from "@features/popup/forest-inventory/types"; -import type { LayerMetadata } from "@shared/lib/coordo"; -import { findCategoricalLabel } from "@shared/lib/utils"; +import { i18nInstance } from "@shared/i18n"; import type { NumericKeys } from "@shared/types"; const indicatorKeys: NumericKeys[] = [ @@ -22,9 +25,11 @@ const indicatorKeys: NumericKeys[] = [ */ export const useFormatBioInventoryData = ( data: BioInventoryData, - metadata: LayerMetadata, + metadata: LabelData[], ) => { const { t } = useTranslation("common"); + const lang = i18nInstance.language; + const { formatWithUnit } = useFormatterWithUnit(); const safeData = preciseNumericIndicators( data, @@ -38,15 +43,37 @@ export const useFormatBioInventoryData = ( ); return { - area: `${safeData.samp_area} ${findCategoricalLabel(metadata, "inv_unit", safeData.samp_unit) || t("dataManagement.noUnit")}`, + area: `${safeData.samp_area} ${ + findLabelInExternalData2( + metadata, + data.proj, + lang, + "unit", + Number(safeData.samp_unit), + ) || t("dataManagement.noUnit") + }`, density: `${safeData.density} ${safeData.dens_unit || t("dataManagement.noUnit")}`, relative_abundance: safeData.taxons_relative_abundance, - richness: safeData.richness, - taxon: "Lémuriens", // findCategoricalLabel(metadata, "tax", safeData.taxon) || safeData.taxon + richness: formatWithUnit(safeData.richness, UNITS.speciesInventoried), + taxon: + findLabelInExternalData2( + metadata, + data.proj, + lang, + "tax", + Number(safeData.taxon), + ) || + t("dataManagement.noUnit") || + safeData.taxon, total_population: safeData.total_pop, type: - findCategoricalLabel(metadata, "meth", safeData.type.toString()) || - safeData.type, + findLabelInExternalData2( + metadata, + data.proj, + lang, + "meth", + Number(safeData.type), + ) || safeData.type, }; }; diff --git a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx index 0e16105b..6ee20214 100644 --- a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx +++ b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx @@ -1,9 +1,9 @@ -import { Gem, PawPrint, SearchAlert } from "lucide-react"; +import { Gem, PawPrint, Ruler, SearchAlert, VectorSquare } from "lucide-react"; import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; import type { BioInventoryData } from "@features/popup/bio-inventory/types"; +import type { ExternalData, LabelData } from "@features/popup/forest-inventory/types"; -import type { LayerMetadata } from "@shared/lib/coordo"; import { useTranslation } from "@i18n"; import { ICON_SIZE } from "../components/constants"; @@ -13,7 +13,7 @@ import { useFormatBioInventoryData } from "./format-data"; export const useBioInventoryIndicatorElements = ( rawData: BioInventoryData, - metadata: LayerMetadata, + metadata: LabelData[], ): UseIndicatorReturnType => { const { t } = useTranslation("all4trees"); const data = useFormatBioInventoryData(rawData, metadata); @@ -29,12 +29,12 @@ export const useBioInventoryIndicatorElements = ( /> } + iconStart={} value={data.type} /> } + iconStart={} value={data.area} /> ), diff --git a/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx b/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx index 2a4c5114..784f43a2 100644 --- a/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx +++ b/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx @@ -46,9 +46,7 @@ export const useBiodiversityIndicatorElements = ( children: ( <> } value={data.treeDiversity.speciesRichness} /> diff --git a/webapp/src/features/indicators/soil/format-data.ts b/webapp/src/features/indicators/soil/format-data.ts index 79f46491..d4e3ba5f 100644 --- a/webapp/src/features/indicators/soil/format-data.ts +++ b/webapp/src/features/indicators/soil/format-data.ts @@ -88,7 +88,7 @@ export const useFormatSoilData = (data: ForestInventoryData) => { ), soil_fauna_diversity: formatWithUnit( soil_fauna_diversity, - UNITS.speciesCount, + UNITS.speciesPerTrap, ), soil_structure_idx: String(soil_structure_idx) === t("dataManagement.noData") @@ -101,7 +101,7 @@ export const useFormatSoilData = (data: ForestInventoryData) => { ), surface_fauna_diversity: formatWithUnit( soil_surface_fauna_diversity, - UNITS.speciesCount, + UNITS.speciesPerTrap, ), }; }; diff --git a/webapp/src/features/indicators/utils.ts b/webapp/src/features/indicators/utils.ts index 0b4f7846..241425ca 100644 --- a/webapp/src/features/indicators/utils.ts +++ b/webapp/src/features/indicators/utils.ts @@ -10,6 +10,7 @@ import type { NumericKeys } from "@shared/types"; export const UNITS = { essenceCount: "essenceCount", + speciesInventoried: "speciesInventoried", individualPerCubicMeter: "individualPerCubicMeter", individualPerHectare: "individualPerHectare", individualPerTrap: "individualPerTrap", @@ -17,7 +18,7 @@ export const UNITS = { minPerHouseholdPerDay: "minPerHouseholdPerDay", monthPerYear: "monthPerYear", percentFoodRequirements: "percentFoodRequirements", - speciesCount: "speciesCount", + speciesPerTrap: "speciesPerTrap", tonPerHectare: "tonPerHectare", } as const; @@ -56,8 +57,8 @@ export const useFormatterWithUnit = () => { ns: "all4trees", value, }); - case UNITS.speciesCount: - return t("indicators.units.speciesCount", { + case UNITS.speciesPerTrap: + return t("indicators.units.speciesPerTrap", { count: parseInt(formattedValue, 10), ns: "all4trees", }); @@ -66,6 +67,11 @@ export const useFormatterWithUnit = () => { count: parseInt(formattedValue, 10), ns: "all4trees", }); + case UNITS.speciesInventoried: + return t("indicators.units.speciesInventoried", { + count: parseInt(formattedValue, 10), + ns: "all4trees", + }); case UNITS.tonPerHectare: return t("indicators.units.tonPerHectare", { ns: "all4trees", value }); case UNITS.m3PerHabPerYear: @@ -99,7 +105,7 @@ export function preciseNumericIndicators>( Object.entries(data).map(([key, value]) => [ key, indicatorKeys.includes(key as (typeof indicatorKeys)[number]) - ? precise(value, defaultValue) + ? precise(Number(value), defaultValue) : value, // Keep the original value if it's not in the list of indicator keys ]), ) as T; @@ -140,9 +146,11 @@ export function formatTaxonAbundance( const abundancePopRecord: Record = {}; abundancePop.forEach((value) => { - const [taxon, count] = value.split(":"); - const currentCount = abundancePopRecord[taxon] || 0; - abundancePopRecord[taxon] = currentCount + parseInt(count, 10); + if (value) { + const [taxon, count] = value.split(":"); + const currentCount = abundancePopRecord[taxon] || 0; + abundancePopRecord[taxon] = currentCount + parseInt(count, 10); + } }); return convertDictToPercentage(abundancePopRecord, abundanceTotal, "0"); @@ -195,9 +203,20 @@ export function findLabelInExternalData( externalData: ExternalData, resourceName: string, project: string, + lang: string, fieldName: string, fieldValue: any, ): string | undefined { + return findMatchingRecord(externalData, resourceName, project, fieldName, fieldValue)?.[`label::${lang}`]; +} + +export function findMatchingRecord( + externalData: ExternalData, + resourceName: string, + project: string, + fieldName: string, + fieldValue: any, +): any { // Get the data array for the resource (e.g., for_label, for_mf_tax1, etc.) const resourceData = externalData[resourceName]; @@ -219,5 +238,42 @@ export function findLabelInExternalData( ); }); - return record?.label; + return record; +} + +export function findLabelInExternalData2( + resourceData: LabelData[], + project: string, + lang: string, + fieldName: string, + fieldValue: any, +): string | undefined { + return findMatchingRecord2(resourceData, project, fieldName, fieldValue)?.[`label::${lang}`]; +} + +export function findMatchingRecord2( + resourceData: LabelData[], + project: string, + fieldName: string, + fieldValue: any, +): any { + if (!resourceData || !Array.isArray(resourceData)) { + return undefined; + } + + // Find the record matching all criteria: project, list_name, and name + const record = resourceData.find((item: LabelData) => { + if (typeof item.name !== typeof fieldValue) { + console.warn( + `Checking field values with different types ! fieldName=${fieldName} fieldValue type=${typeof fieldValue}; item.name type= ${typeof item.name}`, + ); + } + return ( + item.proj?.trim() === project.trim() && + item.list_name?.trim() === fieldName.trim() && + item.name === fieldValue + ); + }); + + return record; } diff --git a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx index b2a72a13..e4d09cc9 100644 --- a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx +++ b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx @@ -1,36 +1,45 @@ import { cx } from "class-variance-authority"; import { Leaf } from "lucide-react"; -import type { FC } from "react"; +import { type FC, use } from "react"; import { useBioInventoryIndicatorElements } from "@features/indicators/bio-inventory/use-bioinventory-indicator-elements"; import { ICON_SIZE_HEADER } from "@features/indicators/components/constants"; import { IndicatorElements } from "@features/indicators/components/indicator-elements"; import { IndicatorScrollContainer } from "@features/indicators/components/indicator-scroll-container"; +import { findLabelInExternalData2 } from "@features/indicators/utils"; import { IndicatorPopupHeader } from "@features/popup/components/indicator-popup-header"; -import { findCategoricalLabel, formatDate } from "@shared/lib/utils"; -import { useTranslation } from "@i18n"; +import { formatDate } from "@shared/lib/utils"; +import { i18nInstance, useTranslation } from "@i18n"; import type { RenderPopupProps } from "../renderPopup"; import type { BioInventoryData } from "./types"; +import { LABEL_DATA } from "@entities/resources"; +import { LAYERS } from "@entities/layers"; +import type { LabelData } from "../forest-inventory/types"; type BioInventoryPopupContentProps = RenderPopupProps; export const BioInventoryPopupContent: FC = ({ data, - metadata, + externalDataPromise, className, ...headerProps }) => { const { t } = useTranslation(["common", "all4trees"]); - - const biodiversityElements = useBioInventoryIndicatorElements(data, metadata); + const lang = i18nInstance.language; + const externalData = use(externalDataPromise); + const labelData = externalData[LABEL_DATA.get(LAYERS.INVENTORY_BIO) || ""] || [] as LabelData[]; + const biodiversityElements = useBioInventoryIndicatorElements( + data, + labelData, + ); console.log( "BioInventoryPopupContent data", data, "metadata", - metadata, + externalData, "biodiversityElements", biodiversityElements, ); @@ -48,8 +57,13 @@ export const BioInventoryPopupContent: FC = ({ })} icon={} subtitle={ - findCategoricalLabel(metadata, "loc2", data.forest) || - t("dataManagement.undefined", { ns: "common" }) + findLabelInExternalData2( + labelData, + data.proj, + lang, + "loc2", + Number(data.forest), + ) || t("dataManagement.undefined", { ns: "common" }) } title={title} {...headerProps} diff --git a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx index 58e3704d..c1c264a1 100644 --- a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx +++ b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx @@ -10,6 +10,7 @@ import { IndicatorScrollContainer } from "@features/indicators/components/indica import { useSoilIndicatorElements } from "@features/indicators/soil"; import { findLabelInExternalData } from "@features/indicators/utils"; +import { i18nInstance } from "@shared/i18n"; import { formatDate } from "@shared/lib/utils"; import { GridSelector } from "@shared/ui/grid-selector"; @@ -30,6 +31,7 @@ export const ForestInventoryPopupContent: FC< ForestInventoryPopupContentProps > = ({ data, metadata, externalDataPromise, className, ...headerProps }) => { const { t } = useTranslation(["common", "all4trees"]); + const lang = i18nInstance.language; const [selectedTab, setSelectedTab] = useState(TABS.BIODIVERSITY); const externalData = use(externalDataPromise); @@ -52,6 +54,7 @@ export const ForestInventoryPopupContent: FC< externalData, "for_label", data.project, + lang, "loc2", data.for, ) || t("common:dataManagement.undefined"); @@ -61,6 +64,7 @@ export const ForestInventoryPopupContent: FC< externalData, "for_label", data.project, + lang, "ecos", data.ecos, ) || t("dataManagement.undefined", { ns: "common" }) diff --git a/webapp/src/shared/api/categories-filters.ts b/webapp/src/shared/api/categories-filters.ts index 44b195d4..6b4c4307 100644 --- a/webapp/src/shared/api/categories-filters.ts +++ b/webapp/src/shared/api/categories-filters.ts @@ -1,4 +1,4 @@ -import { LAYERS } from "./layers"; +import { LAYERS } from "@entities/layers"; const SEPARATOR = ":::"; diff --git a/webapp/src/shared/i18n/translations/en/all4trees.json b/webapp/src/shared/i18n/translations/en/all4trees.json index e5e0a698..06cea695 100644 --- a/webapp/src/shared/i18n/translations/en/all4trees.json +++ b/webapp/src/shared/i18n/translations/en/all4trees.json @@ -66,6 +66,12 @@ }, "title": "Biodiversity" }, + "bioinventory": { + "sampArea": "Sampling area", + "taxon": "Target taxon", + "title": "Fauna inventory", + "type": "Measures type" + }, "common": { "abundance": "Relative abundance", "beneficiary": "Beneficiary", @@ -195,7 +201,10 @@ "title": "Soil" }, "units": { - "essenceCount": "{{ count }} species inventoried", + "essenceCount": "", + "essenceCount_one": "{{ count }} essence inventoried", + "essenceCount_other": "{{ count }} essences inventoried", + "speciesInventoried": "{{ count }} species inventoried", "individualPerCubicMeter": "{{ value }} ind/m³", "individualPerHectare": "{{ value }} ind/ha", "individualPerTrap": "{{ value }} ind/trap", @@ -203,7 +212,7 @@ "minPerHhPerDay": "{{ value }} min/household/day", "monthPerYear": "{{ value }} month/year", "percentFoodRequirements": "{{ value }} % of food needs", - "speciesCount": "{{ count }} species per trap", + "speciesPerTrap": "{{ count }} species per trap", "tonPerHectare": "{{ value }} t/ha" } }, diff --git a/webapp/src/shared/i18n/translations/fr/all4trees.json b/webapp/src/shared/i18n/translations/fr/all4trees.json index b46b0bf3..a75ab9ad 100644 --- a/webapp/src/shared/i18n/translations/fr/all4trees.json +++ b/webapp/src/shared/i18n/translations/fr/all4trees.json @@ -203,6 +203,9 @@ "essenceCount": "", "essenceCount_one": "{{ count }} essence inventoriée", "essenceCount_other": "{{ count }} essences inventoriées", + "speciesInventoried": "", + "speciesInventoried_one": "{{ count }} espèce inventoriée", + "speciesInventoried_other": "{{ count }} espèces inventoriées", "individualPerCubicMeter": "{{ value }} ind/m³", "individualPerHectare": "{{ value }} ind/ha", "individualPerTrap": "{{ value }} ind/piège", @@ -210,9 +213,9 @@ "minPerHhPerDay": "{{ value }} min/ménage/jour", "monthPerYear": "{{ value }} mois/an", "percentFoodRequirements": "{{ value }} % des besoins alimentaires", - "speciesCount": "", - "speciesCount_one": "{{ count }} espèce par piège", - "speciesCount_other": "{{ count }} espèces par piège", + "speciesPerTrap": "", + "speciesPerTrap_one": "{{ count }} espèce par piège", + "speciesPerTrap_other": "{{ count }} espèces par piège", "tonPerHectare": "{{ value }} t/ha" } }, diff --git a/webapp/src/widgets/dashboard/dashboard.tsx b/webapp/src/widgets/dashboard/dashboard.tsx index 771a497e..f1e80e52 100644 --- a/webapp/src/widgets/dashboard/dashboard.tsx +++ b/webapp/src/widgets/dashboard/dashboard.tsx @@ -4,8 +4,8 @@ import LoadedDashboard from "@widgets/dashboard/loaded-dashboard"; import { SuspenseBoundary } from "@features/fallback/suspense-boundary"; -import { LAYERS } from "@shared/api/layers"; import { useSuspenseData } from "@shared/api/suspense-fetch"; +import { LAYERS } from "@entities/layers"; import { useApi } from "@shared/hooks/useApi"; export default function Dashboard() { diff --git a/webapp/src/widgets/map/map-all4trees.tsx b/webapp/src/widgets/map/map-all4trees.tsx index 384b614e..8be77b13 100644 --- a/webapp/src/widgets/map/map-all4trees.tsx +++ b/webapp/src/widgets/map/map-all4trees.tsx @@ -18,7 +18,7 @@ import { SocioEcoIndicator, } from "@features/popup/socio-eco"; -import { LAYERS } from "@shared/api/layers"; +import { LAYERS } from "@entities/layers"; import { useMap } from "@shared/hooks/use-map-all4trees"; import { useApi } from "@shared/hooks/useApi"; diff --git a/webapp/src/widgets/map/map-seed.tsx b/webapp/src/widgets/map/map-seed.tsx index c7e6e810..cfd10bb0 100644 --- a/webapp/src/widgets/map/map-seed.tsx +++ b/webapp/src/widgets/map/map-seed.tsx @@ -6,7 +6,7 @@ import { getRenderPopupLayer, } from "@features/popup/renderPopup"; -import { LAYERS } from "@shared/api/layers"; +import { LAYERS } from "@entities/layers"; import { useMap } from "@shared/hooks/use-map-seed"; import { useApi } from "@shared/hooks/useApi"; diff --git a/webapp/src/widgets/map/utils.ts b/webapp/src/widgets/map/utils.ts index 089258f5..a7494093 100644 --- a/webapp/src/widgets/map/utils.ts +++ b/webapp/src/widgets/map/utils.ts @@ -1,5 +1,5 @@ +import { EXTERNAL_RESOURCES_BY_LAYER } from "@entities/resources"; import type { ApiClient } from "@shared/api/client"; -import { LAYERS } from "@shared/api/layers"; export const SVG_SIZE_DEFAULT = 72; export const TARGET_SIZE_DEFAULT = 48; @@ -17,15 +17,6 @@ export function getIconSize({ return targetSize / assetSize; } -export const EXTERNAL_RESOURCES_BY_LAYER = new Map([ - [ - LAYERS.INVENTARY, - ["for_label", "for_mf_tax1", "for_mf_tax2", "for_mf_tax3", "for_score"], - ], - [LAYERS.ENQUETE, [""]], - [LAYERS.SEED_POINT, [""]], -]); - export const getExternalDataPromiseByLayer = ( layerId: string, client: ApiClient, From 1fea1ab4ca077eace0f42a1c1c6bc898cea6e00f Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Fri, 10 Jul 2026 11:39:44 +0200 Subject: [PATCH 06/16] Fix errors and improve clustering --- backend/configs/all4trees_config.json | 12 ++++++--- webapp/src/entities/resources.ts | 4 +-- .../biodiversity/chart-relative-abundance.tsx | 13 +++++---- .../features/charts/components/lib/taxon.ts | 27 +++++-------------- .../src/features/controls/layer-control.tsx | 2 +- .../use-bioinventory-indicator-elements.tsx | 2 +- .../use-biodiversity-indicator-elements.tsx | 5 ++-- .../soil/use-soil-indicator-elements.tsx | 4 +-- webapp/src/features/indicators/utils.ts | 14 +++++++--- .../bio-inventory/popup-bio-inventory.tsx | 10 ++++--- .../popup-forest-inventory.tsx | 18 ++++++++----- .../i18n/translations/en/all4trees.json | 2 +- .../i18n/translations/fr/all4trees.json | 6 ++--- webapp/src/widgets/map/map-all4trees.tsx | 2 +- webapp/src/widgets/map/map-seed.tsx | 2 +- webapp/src/widgets/map/utils.ts | 1 + 16 files changed, 66 insertions(+), 58 deletions(-) diff --git a/backend/configs/all4trees_config.json b/backend/configs/all4trees_config.json index d0d7d5db..a07ed3c7 100644 --- a/backend/configs/all4trees_config.json +++ b/backend/configs/all4trees_config.json @@ -41,8 +41,8 @@ "resource": "inv_for", "layerType": "symbol", "cluster": { - "radius": 50, - "maxZoom": 12.5, + "radius": 40, + "maxZoom": 12, "steps": [10, 20], "colors": ["#99D6C2", "#009966", "#006B47"] }, @@ -97,6 +97,12 @@ "path": "../catalog/inventaire_bio", "resource": "inv_bio", "layerType": "symbol", + "cluster": { + "radius": 2, + "maxZoom": 12, + "steps": [10, 20], + "colors": ["#99D6C2", "#009966", "#006B47"] + }, "columns": { "geom": "start_point.merge().centroid()", "id": "_id", @@ -130,7 +136,7 @@ "groupby": ["loc2"], "cluster": { "radius": 50, - "maxZoom": 12.5, + "maxZoom": 12, "steps": [10, 20], "colors": ["#A1B5F5", "#1447E6", "#0E32A1"] }, diff --git a/webapp/src/entities/resources.ts b/webapp/src/entities/resources.ts index ae63d03e..f24734c9 100644 --- a/webapp/src/entities/resources.ts +++ b/webapp/src/entities/resources.ts @@ -12,5 +12,5 @@ export const EXTERNAL_RESOURCES_BY_LAYER = new Map([ export const LABEL_DATA = new Map([ [LAYERS.INVENTORY_FOR, "for_label"], - [LAYERS.INVENTORY_BIO, "bio_label"] -]); \ No newline at end of file + [LAYERS.INVENTORY_BIO, "bio_label"], +]); diff --git a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx index 2b631a37..177dbc67 100644 --- a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx +++ b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx @@ -1,5 +1,5 @@ -import { findLabelInExternalData } from "@features/indicators/utils"; -import type { ExternalData } from "@features/popup/forest-inventory/types"; +import { findLabelInExternalData2 } from "@features/indicators/utils"; +import type { LabelData } from "@features/popup/forest-inventory/types"; import { i18nInstance, useTranslation } from "@shared/i18n"; import type { ChartConfig } from "@shared/ui/chart"; @@ -10,13 +10,13 @@ import { PieChartCategorical } from "../components/pie-chart-categorical"; type PieChartProps = { project: string; data: Record; - externalData: ExternalData; + metadata: LabelData[]; }; export const ChartRelativeAbundance: ChartComponentType = ({ project, data, - externalData, + metadata, }) => { const { t } = useTranslation("all4trees"); const lang = i18nInstance.language; @@ -40,9 +40,8 @@ export const ChartRelativeAbundance: ChartComponentType = ({ ...chartConfig, [element.name]: { label: - findLabelInExternalData( - externalData, - "for_label", + findLabelInExternalData2( + metadata, project, lang, "ess", diff --git a/webapp/src/features/charts/components/lib/taxon.ts b/webapp/src/features/charts/components/lib/taxon.ts index e6e323e4..99d47610 100644 --- a/webapp/src/features/charts/components/lib/taxon.ts +++ b/webapp/src/features/charts/components/lib/taxon.ts @@ -13,29 +13,14 @@ export function getTaxonLabels( ): [string, string, string] { const [taxon1, taxon2, taxon3] = element.split("-"); const taxon1Label = - findLabelInExternalData2( - metadata, - project, - lang, - "tax1", - Number(taxon1), - ) || taxon1; + findLabelInExternalData2(metadata, project, lang, "tax1", Number(taxon1)) || + taxon1; const taxon2Label = - findLabelInExternalData2( - metadata, - project, - lang, - "tax2", - Number(taxon2), - ) || taxon2; + findLabelInExternalData2(metadata, project, lang, "tax2", Number(taxon2)) || + taxon2; const taxon3Label = - findLabelInExternalData2( - metadata, - project, - lang, - "tax3", - Number(taxon3), - ) || taxon3; + findLabelInExternalData2(metadata, project, lang, "tax3", Number(taxon3)) || + taxon3; return [taxon1Label, taxon2Label, taxon3Label]; } diff --git a/webapp/src/features/controls/layer-control.tsx b/webapp/src/features/controls/layer-control.tsx index 3ed7e80d..0cc6be62 100644 --- a/webapp/src/features/controls/layer-control.tsx +++ b/webapp/src/features/controls/layer-control.tsx @@ -1,3 +1,4 @@ +import { LAYERS } from "@entities/layers"; import { HouseIcon, LayersIcon, @@ -10,7 +11,6 @@ import { import { Activity, useEffect, useRef, useState } from "react"; import { createRoot } from "react-dom/client"; -import { LAYERS } from "@entities/layers"; import { useTranslation } from "@shared/i18n"; import { LAYER_CONTROL_ELEMENTS, diff --git a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx index 6ee20214..f468a306 100644 --- a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx +++ b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx @@ -2,7 +2,7 @@ import { Gem, PawPrint, Ruler, SearchAlert, VectorSquare } from "lucide-react"; import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; import type { BioInventoryData } from "@features/popup/bio-inventory/types"; -import type { ExternalData, LabelData } from "@features/popup/forest-inventory/types"; +import type { LabelData } from "@features/popup/forest-inventory/types"; import { useTranslation } from "@i18n"; diff --git a/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx b/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx index 784f43a2..3e7bcbb9 100644 --- a/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx +++ b/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx @@ -5,6 +5,7 @@ import { ChartRelativeAbundance } from "@features/charts/biodiversity/chart-rela import type { ExternalData, ForestInventoryData, + LabelData, } from "@features/popup/forest-inventory/types"; import { useTranslation } from "@i18n"; @@ -16,7 +17,7 @@ import { useFormatBiodiversityData } from "./format-data"; export const useBiodiversityIndicatorElements = ( rawData: ForestInventoryData, - metadata: ExternalData, + metadata: LabelData[], ): UseIndicatorReturnType => { const { t } = useTranslation("all4trees"); const data = useFormatBiodiversityData(rawData); @@ -52,7 +53,7 @@ export const useBiodiversityIndicatorElements = ( /> diff --git a/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx b/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx index fc1b0386..de11199d 100644 --- a/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx +++ b/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx @@ -6,7 +6,7 @@ import { ChartWindErosion } from "@features/charts/soil/ui/chart-wind-erosion"; import type { UseIndicatorReturnType } from "@features/indicators//components/types"; import { IndicatorRawValue } from "@features/indicators/components/indicator-raw-value"; import type { ForestInventoryData } from "@features/popup/forest-inventory"; -import type { ExternalData } from "@features/popup/forest-inventory/types"; +import type { LabelData } from "@features/popup/forest-inventory/types"; import { useTranslation } from "@i18n"; @@ -15,7 +15,7 @@ import { useFormatSoilData } from "./format-data"; export const useSoilIndicatorElements = ( rawData: ForestInventoryData, - metadata: ExternalData, + metadata: LabelData[], ): UseIndicatorReturnType => { const { t } = useTranslation("all4trees"); diff --git a/webapp/src/features/indicators/utils.ts b/webapp/src/features/indicators/utils.ts index 241425ca..778f6e3f 100644 --- a/webapp/src/features/indicators/utils.ts +++ b/webapp/src/features/indicators/utils.ts @@ -10,7 +10,6 @@ import type { NumericKeys } from "@shared/types"; export const UNITS = { essenceCount: "essenceCount", - speciesInventoried: "speciesInventoried", individualPerCubicMeter: "individualPerCubicMeter", individualPerHectare: "individualPerHectare", individualPerTrap: "individualPerTrap", @@ -18,6 +17,7 @@ export const UNITS = { minPerHouseholdPerDay: "minPerHouseholdPerDay", monthPerYear: "monthPerYear", percentFoodRequirements: "percentFoodRequirements", + speciesInventoried: "speciesInventoried", speciesPerTrap: "speciesPerTrap", tonPerHectare: "tonPerHectare", } as const; @@ -207,7 +207,13 @@ export function findLabelInExternalData( fieldName: string, fieldValue: any, ): string | undefined { - return findMatchingRecord(externalData, resourceName, project, fieldName, fieldValue)?.[`label::${lang}`]; + return findMatchingRecord( + externalData, + resourceName, + project, + fieldName, + fieldValue, + )?.[`label::${lang}`]; } export function findMatchingRecord( @@ -248,7 +254,9 @@ export function findLabelInExternalData2( fieldName: string, fieldValue: any, ): string | undefined { - return findMatchingRecord2(resourceData, project, fieldName, fieldValue)?.[`label::${lang}`]; + return findMatchingRecord2(resourceData, project, fieldName, fieldValue)?.[ + `label::${lang}` + ]; } export function findMatchingRecord2( diff --git a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx index e4d09cc9..f20b824f 100644 --- a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx +++ b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx @@ -1,3 +1,5 @@ +import { LAYERS } from "@entities/layers"; +import { LABEL_DATA } from "@entities/resources"; import { cx } from "class-variance-authority"; import { Leaf } from "lucide-react"; import { type FC, use } from "react"; @@ -12,11 +14,9 @@ import { IndicatorPopupHeader } from "@features/popup/components/indicator-popup import { formatDate } from "@shared/lib/utils"; import { i18nInstance, useTranslation } from "@i18n"; +import type { LabelData } from "../forest-inventory/types"; import type { RenderPopupProps } from "../renderPopup"; import type { BioInventoryData } from "./types"; -import { LABEL_DATA } from "@entities/resources"; -import { LAYERS } from "@entities/layers"; -import type { LabelData } from "../forest-inventory/types"; type BioInventoryPopupContentProps = RenderPopupProps; @@ -29,7 +29,9 @@ export const BioInventoryPopupContent: FC = ({ const { t } = useTranslation(["common", "all4trees"]); const lang = i18nInstance.language; const externalData = use(externalDataPromise); - const labelData = externalData[LABEL_DATA.get(LAYERS.INVENTORY_BIO) || ""] || [] as LabelData[]; + const labelData = + externalData[LABEL_DATA.get(LAYERS.INVENTORY_BIO) || ""] || + ([] as LabelData[]); const biodiversityElements = useBioInventoryIndicatorElements( data, labelData, diff --git a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx index c1c264a1..230ad4f2 100644 --- a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx +++ b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx @@ -16,7 +16,9 @@ import { GridSelector } from "@shared/ui/grid-selector"; import { IndicatorPopupHeader } from "../components/indicator-popup-header"; import type { RenderPopupProps } from "../renderPopup"; -import type { ForestInventoryData } from "./types"; +import type { ForestInventoryData, LabelData } from "./types"; +import { LABEL_DATA } from "@entities/resources"; +import { LAYERS } from "@entities/layers"; type ForestInventoryPopupContentProps = RenderPopupProps; @@ -34,6 +36,15 @@ export const ForestInventoryPopupContent: FC< const lang = i18nInstance.language; const [selectedTab, setSelectedTab] = useState(TABS.BIODIVERSITY); const externalData = use(externalDataPromise); + const labelData = + externalData[LABEL_DATA.get(LAYERS.INVENTORY_BIO) || ""] || + ([] as LabelData[]); + + const biodiversityElements = useBiodiversityIndicatorElements( + data, + labelData, + ); + const soilElements = useSoilIndicatorElements(data, labelData); const tabs = { [TABS.BIODIVERSITY]: t("indicators.biodiversity.title", { @@ -70,11 +81,6 @@ export const ForestInventoryPopupContent: FC< ) || t("dataManagement.undefined", { ns: "common" }) }`; - const biodiversityElements = useBiodiversityIndicatorElements( - data, - externalData, - ); - const soilElements = useSoilIndicatorElements(data, externalData); return (
Date: Fri, 10 Jul 2026 11:40:19 +0200 Subject: [PATCH 07/16] Remove unused import --- .../use-biodiversity-indicator-elements.tsx | 1 - .../popup/forest-inventory/popup-forest-inventory.tsx | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx b/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx index 3e7bcbb9..ec7285c8 100644 --- a/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx +++ b/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx @@ -3,7 +3,6 @@ import { Gem, TreePine, Trees } from "lucide-react"; import { ChartForestPotential } from "@features/charts/biodiversity/chart-forest-potential"; import { ChartRelativeAbundance } from "@features/charts/biodiversity/chart-relative-abundance"; import type { - ExternalData, ForestInventoryData, LabelData, } from "@features/popup/forest-inventory/types"; diff --git a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx index 230ad4f2..c8ccdf3e 100644 --- a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx +++ b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx @@ -1,3 +1,5 @@ +import { LAYERS } from "@entities/layers"; +import { LABEL_DATA } from "@entities/resources"; import { cx } from "class-variance-authority"; import { TreesIcon } from "lucide-react"; import { Activity, type FC, use, useState } from "react"; @@ -17,8 +19,6 @@ import { GridSelector } from "@shared/ui/grid-selector"; import { IndicatorPopupHeader } from "../components/indicator-popup-header"; import type { RenderPopupProps } from "../renderPopup"; import type { ForestInventoryData, LabelData } from "./types"; -import { LABEL_DATA } from "@entities/resources"; -import { LAYERS } from "@entities/layers"; type ForestInventoryPopupContentProps = RenderPopupProps; @@ -36,9 +36,9 @@ export const ForestInventoryPopupContent: FC< const lang = i18nInstance.language; const [selectedTab, setSelectedTab] = useState(TABS.BIODIVERSITY); const externalData = use(externalDataPromise); - const labelData = - externalData[LABEL_DATA.get(LAYERS.INVENTORY_BIO) || ""] || - ([] as LabelData[]); + const labelData = + externalData[LABEL_DATA.get(LAYERS.INVENTORY_BIO) || ""] || + ([] as LabelData[]); const biodiversityElements = useBiodiversityIndicatorElements( data, From 088cd9be170ba17ffa6c024406d85fbcdb8e40bc Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Fri, 10 Jul 2026 16:57:22 +0200 Subject: [PATCH 08/16] Add Endangered Species chart --- backend/Makefile | 2 +- backend/configs/all4trees_config.json | 7 +- webapp/src/app/styles/all4trees.css | 2 +- .../biodiversity/chart-relative-abundance.tsx | 11 +-- .../bioinventory/chart-species-status.tsx | 77 +++++++++++++++++++ .../features/charts/components/lib/taxon.ts | 11 +-- .../charts/socio-eco/chart-firewood-needs.tsx | 6 +- .../socio-eco/chart-living-condition.tsx | 6 +- .../charts/socio-eco/chart-timber-needs.tsx | 6 +- .../indicators/bio-inventory/format-data.ts | 33 +++----- .../use-bioinventory-indicator-elements.tsx | 6 ++ webapp/src/features/indicators/utils.ts | 58 +++----------- .../bio-inventory/popup-bio-inventory.tsx | 11 +-- .../src/features/popup/bio-inventory/types.ts | 5 +- .../popup-forest-inventory.tsx | 24 ++---- .../i18n/translations/en/all4trees.json | 1 + .../shared/i18n/translations/en/common.json | 1 + .../i18n/translations/fr/all4trees.json | 1 + .../shared/i18n/translations/fr/common.json | 1 + 19 files changed, 140 insertions(+), 129 deletions(-) create mode 100644 webapp/src/features/charts/bioinventory/chart-species-status.tsx diff --git a/backend/Makefile b/backend/Makefile index 546ee976..88d204b5 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -27,7 +27,7 @@ catalog: coordo add foreignkey barbd_001.barbD_tax3 for_mf_tax3.tax3 --package catalog/inventaire_for # All 4 Trees - Inventaire Biologique - coordo add kobotoolbox data/all4trees/inventaire_bio/20260422_InventaireBiologique_DonneesK.xlsx --form data/all4trees/inventaire_bio/20260709_InventaireBiologique_QuestionnaireK.xlsx --package catalog/inventaire_bio + coordo add kobotoolbox data/all4trees/inventaire_bio/20260710_InventaireBiologique_DonneesK.xlsx --form data/all4trees/inventaire_bio/20260709_InventaireBiologique_QuestionnaireK.xlsx --package catalog/inventaire_bio coordo add file data/all4trees/inventaire_bio/20260709_InventaireBiologique_DonneesExternes.xlsx --package catalog/inventaire_bio coordo add foreignkey inv_bio.proj bio_samp.proj --package catalog/inventaire_bio diff --git a/backend/configs/all4trees_config.json b/backend/configs/all4trees_config.json index a07ed3c7..e8eb279b 100644 --- a/backend/configs/all4trees_config.json +++ b/backend/configs/all4trees_config.json @@ -118,10 +118,9 @@ "samp_unit": "inv_unit", "density": "int(sum(inv_001.no)) / inv_samp", "dens_unit": "dens_unit", - "richness": "list_unique(list(nullif(inv_001.concat_ws('-', tax1, tax2, tax3), '')))", - "taxons_relative_abundance": "list_aggregate(list(nullif(inv_001.concat_ws('-', tax1, tax2, tax3), '')), 'histogram')", - "pop_by_taxon": "list_concat(list(inv_001.concat_ws(':', concat_ws('-',tax1, tax2, tax3), no) if inv_001.no > 0))", - "total_pop": "int(sum(inv_001.no))" + "richness": "inv_001.count(no)", + "taxons_abundance_pop": "list_concat(list(inv_001.concat_ws(':', concat_ws('-',tax1, tax2, tax3), no) if inv_001.no > 0))", + "taxons_total_pop": "int(sum(inv_001.no))" }, "popup": { "trigger": "click" diff --git a/webapp/src/app/styles/all4trees.css b/webapp/src/app/styles/all4trees.css index 993e44a2..cf57a531 100644 --- a/webapp/src/app/styles/all4trees.css +++ b/webapp/src/app/styles/all4trees.css @@ -101,7 +101,7 @@ --chart-3: var(--a4t-color-citrouille); --chart-4: var(--a4t-color-vert-de-gris); --chart-5: #895bf5; - --chart-6: var(--a4t-color-onyx); + --chart-6: #da4238f3; --sidebar: var(--a4t-color-alabaster); --sidebar-foreground: var(--a4t-color-alabaster); diff --git a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx index 177dbc67..c01cfe39 100644 --- a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx +++ b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx @@ -1,4 +1,4 @@ -import { findLabelInExternalData2 } from "@features/indicators/utils"; +import { findLabel } from "@features/indicators/utils"; import type { LabelData } from "@features/popup/forest-inventory/types"; import { i18nInstance, useTranslation } from "@shared/i18n"; @@ -40,13 +40,8 @@ export const ChartRelativeAbundance: ChartComponentType = ({ ...chartConfig, [element.name]: { label: - findLabelInExternalData2( - metadata, - project, - lang, - "ess", - Number(element.name), - ) || element.name, + findLabel(metadata, project, lang, "ess", Number(element.name)) || + element.name, }, other: { label: t( diff --git a/webapp/src/features/charts/bioinventory/chart-species-status.tsx b/webapp/src/features/charts/bioinventory/chart-species-status.tsx new file mode 100644 index 00000000..542f7406 --- /dev/null +++ b/webapp/src/features/charts/bioinventory/chart-species-status.tsx @@ -0,0 +1,77 @@ +import { findStatus } from "@features/indicators/utils"; +import type { LabelData } from "@features/popup/forest-inventory/types"; + +import { i18nInstance, useTranslation } from "@shared/i18n"; +import type { ChartConfig } from "@shared/ui/chart"; + +import type { ChartComponentType } from "../components/chart-component"; +import { PieChartCategorical } from "../components/pie-chart-categorical"; + +type PieChartProps = { + data: Record; + metadata: LabelData[]; + project: string; +}; + +export const ChartSpeciesStatus: ChartComponentType = ({ + data, + metadata, + project, +}) => { + const { t } = useTranslation(["common", "all4trees"]); + const lang = i18nInstance.language; + + // Replace taxon string like 1-2-3 by corresponding status label in external data + const labeledData = Object.entries(data).map<[string, number]>( + ([name, value]) => { + const tax3 = name.split("-")[2]; + const tax3status = ( + tax3 + ? findStatus(metadata, project, lang, "tax3", Number(tax3)) + : t("common:dataManagement.other") + ) as string; + return [tax3status, value]; + }, + ); + + // Sum pop values grouped by labels then map into chartData + const chartData = Object.entries( + labeledData.reduce( + (acc, [label, value]) => { + if (!acc[label]) { + acc[label] = 0; + } + acc[label] += value; + return acc; + }, + {} as { [key: string]: number }, + ), + ).map(([label, value], index) => ({ + fill: `var(--chart-${(index % 5) + 1})`, + name: label, + value, + })); + + // Generate chart config from chart Data + let chartConfig: ChartConfig = {}; + chartData.forEach((element) => { + chartConfig = { + ...chartConfig, + [element.name]: { + label: element.name, + }, + }; + }); + + return ( + + ); +}; + +ChartSpeciesStatus.isChartComponent = true; diff --git a/webapp/src/features/charts/components/lib/taxon.ts b/webapp/src/features/charts/components/lib/taxon.ts index 99d47610..dfa9f098 100644 --- a/webapp/src/features/charts/components/lib/taxon.ts +++ b/webapp/src/features/charts/components/lib/taxon.ts @@ -1,4 +1,4 @@ -import { findLabelInExternalData2 } from "@features/indicators/utils"; +import { findLabel } from "@features/indicators/utils"; import type { LabelData } from "@features/popup/forest-inventory/types"; export function getDataTypePrefix(dataType: "tsbf" | "barbA" | null): string { @@ -13,14 +13,11 @@ export function getTaxonLabels( ): [string, string, string] { const [taxon1, taxon2, taxon3] = element.split("-"); const taxon1Label = - findLabelInExternalData2(metadata, project, lang, "tax1", Number(taxon1)) || - taxon1; + findLabel(metadata, project, lang, "tax1", Number(taxon1)) || taxon1; const taxon2Label = - findLabelInExternalData2(metadata, project, lang, "tax2", Number(taxon2)) || - taxon2; + findLabel(metadata, project, lang, "tax2", Number(taxon2)) || taxon2; const taxon3Label = - findLabelInExternalData2(metadata, project, lang, "tax3", Number(taxon3)) || - taxon3; + findLabel(metadata, project, lang, "tax3", Number(taxon3)) || taxon3; return [taxon1Label, taxon2Label, taxon3Label]; } diff --git a/webapp/src/features/charts/socio-eco/chart-firewood-needs.tsx b/webapp/src/features/charts/socio-eco/chart-firewood-needs.tsx index 3eaa5d62..a6303378 100644 --- a/webapp/src/features/charts/socio-eco/chart-firewood-needs.tsx +++ b/webapp/src/features/charts/socio-eco/chart-firewood-needs.tsx @@ -27,17 +27,17 @@ export const ChartFireWoodNeeds: ChartComponentType = ({ value: data.easyToMeet, }, { - fill: "var(--chart-2)", + fill: "var(--chart-3)", name: "moderateToMeet", value: data.moderateToMeet, }, { - fill: "var(--chart-5)", + fill: "var(--chart-6)", name: "difficultToMeet", value: data.difficultToMeet, }, { - fill: "var(--chart-6)", + fill: "var(--chart-2)", name: "dontKnow", value: data.dontKnow, }, diff --git a/webapp/src/features/charts/socio-eco/chart-living-condition.tsx b/webapp/src/features/charts/socio-eco/chart-living-condition.tsx index ff01cd74..ce8203d3 100644 --- a/webapp/src/features/charts/socio-eco/chart-living-condition.tsx +++ b/webapp/src/features/charts/socio-eco/chart-living-condition.tsx @@ -25,17 +25,17 @@ export const ChartLivingCondition: ChartComponentType = ({ value: data.improvement, }, { - fill: "var(--chart-3)", + fill: "var(--chart-5)", name: "stable", value: data.stable, }, { - fill: "var(--chart-5)", + fill: "var(--chart-6)", name: "regression", value: data.regression, }, { - fill: "var(--chart-6)", + fill: "var(--chart-3)", name: "refuse", value: data.refuse, }, diff --git a/webapp/src/features/charts/socio-eco/chart-timber-needs.tsx b/webapp/src/features/charts/socio-eco/chart-timber-needs.tsx index 29a0fc57..47d26a2c 100644 --- a/webapp/src/features/charts/socio-eco/chart-timber-needs.tsx +++ b/webapp/src/features/charts/socio-eco/chart-timber-needs.tsx @@ -27,17 +27,17 @@ export const ChartTimberNeeds: ChartComponentType = ({ value: data.easyToMeet, }, { - fill: "var(--chart-2)", + fill: "var(--chart-3)", name: "moderateToMeet", value: data.moderateToMeet, }, { - fill: "var(--chart-5)", + fill: "var(--chart-6)", name: "difficultToMeet", value: data.difficultToMeet, }, { - fill: "var(--chart-6)", + fill: "var(--chart-2)", name: "dontKnow", value: data.dontKnow, }, diff --git a/webapp/src/features/indicators/bio-inventory/format-data.ts b/webapp/src/features/indicators/bio-inventory/format-data.ts index bdaee0fb..f6cf93c4 100644 --- a/webapp/src/features/indicators/bio-inventory/format-data.ts +++ b/webapp/src/features/indicators/bio-inventory/format-data.ts @@ -1,7 +1,7 @@ import { useTranslation } from "react-i18next"; import { - findLabelInExternalData2, + findLabel, formatTaxonAbundance, preciseNumericIndicators, UNITS, @@ -17,7 +17,7 @@ const indicatorKeys: NumericKeys[] = [ "samp_area", "density", "richness", - "total_pop", + "taxons_total_pop", ]; /** @@ -37,14 +37,14 @@ export const useFormatBioInventoryData = ( t("dataManagement.noData"), ); - safeData.taxons_relative_abundance = formatTaxonAbundance( - safeData.pop_by_taxon, - safeData.total_pop, + const taxons_relative_abundance = formatTaxonAbundance( + safeData.taxons_abundance_pop, + safeData.taxons_total_pop, ); return { area: `${safeData.samp_area} ${ - findLabelInExternalData2( + findLabel( metadata, data.proj, lang, @@ -53,27 +53,16 @@ export const useFormatBioInventoryData = ( ) || t("dataManagement.noUnit") }`, density: `${safeData.density} ${safeData.dens_unit || t("dataManagement.noUnit")}`, - relative_abundance: safeData.taxons_relative_abundance, + relative_abundance: taxons_relative_abundance, richness: formatWithUnit(safeData.richness, UNITS.speciesInventoried), taxon: - findLabelInExternalData2( - metadata, - data.proj, - lang, - "tax", - Number(safeData.taxon), - ) || + findLabel(metadata, data.proj, lang, "tax", Number(safeData.taxon)) || t("dataManagement.noUnit") || safeData.taxon, - total_population: safeData.total_pop, + total_population: safeData.taxons_total_pop, type: - findLabelInExternalData2( - metadata, - data.proj, - lang, - "meth", - Number(safeData.type), - ) || safeData.type, + findLabel(metadata, data.proj, lang, "meth", Number(safeData.type)) || + safeData.type, }; }; diff --git a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx index f468a306..fcbaf7e9 100644 --- a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx +++ b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx @@ -1,5 +1,6 @@ import { Gem, PawPrint, Ruler, SearchAlert, VectorSquare } from "lucide-react"; +import { ChartSpeciesStatus } from "@features/charts/bioinventory/chart-species-status"; import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; import type { BioInventoryData } from "@features/popup/bio-inventory/types"; import type { LabelData } from "@features/popup/forest-inventory/types"; @@ -52,6 +53,11 @@ export const useBioInventoryIndicatorElements = ( metadata={metadata} project={rawData.proj} /> + ), identifier: "bio", diff --git a/webapp/src/features/indicators/utils.ts b/webapp/src/features/indicators/utils.ts index 778f6e3f..de923ba8 100644 --- a/webapp/src/features/indicators/utils.ts +++ b/webapp/src/features/indicators/utils.ts @@ -1,7 +1,4 @@ -import type { - ExternalData, - LabelData, -} from "@features/popup/forest-inventory/types"; +import type { LabelData } from "@features/popup/forest-inventory/types"; import { useTranslation } from "@shared/i18n"; import type { LayerMetadata } from "@shared/lib/coordo"; @@ -199,67 +196,32 @@ export function findCategoricalLabel( ?.categories?.find((c) => c.value === fieldValue)?.label; } -export function findLabelInExternalData( - externalData: ExternalData, - resourceName: string, +// Find status of corresponding taxon value. +export function findStatus( + resourceData: LabelData[], project: string, lang: string, fieldName: string, fieldValue: any, ): string | undefined { - return findMatchingRecord( - externalData, - resourceName, - project, - fieldName, - fieldValue, - )?.[`label::${lang}`]; -} - -export function findMatchingRecord( - externalData: ExternalData, - resourceName: string, - project: string, - fieldName: string, - fieldValue: any, -): any { - // Get the data array for the resource (e.g., for_label, for_mf_tax1, etc.) - const resourceData = externalData[resourceName]; - - if (!resourceData || !Array.isArray(resourceData)) { - return undefined; - } - - // Find the record matching all criteria: project, list_name, and name - const record = resourceData.find((item: LabelData) => { - if (typeof item.name !== typeof fieldValue) { - console.warn( - `Checking field values with different types ! resourceName=${resourceName} fieldName=${fieldName} fieldValue type=${typeof fieldValue}; item.name type= ${typeof item.name}`, - ); - } - return ( - item.proj?.trim() === project.trim() && - item.list_name?.trim() === fieldName.trim() && - item.name === fieldValue - ); - }); - - return record; + return findMatchingRecord(resourceData, project, fieldName, fieldValue)?.[ + `stat::${lang}` + ]; } -export function findLabelInExternalData2( +export function findLabel( resourceData: LabelData[], project: string, lang: string, fieldName: string, fieldValue: any, ): string | undefined { - return findMatchingRecord2(resourceData, project, fieldName, fieldValue)?.[ + return findMatchingRecord(resourceData, project, fieldName, fieldValue)?.[ `label::${lang}` ]; } -export function findMatchingRecord2( +export function findMatchingRecord( resourceData: LabelData[], project: string, fieldName: string, diff --git a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx index f20b824f..773e089d 100644 --- a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx +++ b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx @@ -8,7 +8,7 @@ import { useBioInventoryIndicatorElements } from "@features/indicators/bio-inven import { ICON_SIZE_HEADER } from "@features/indicators/components/constants"; import { IndicatorElements } from "@features/indicators/components/indicator-elements"; import { IndicatorScrollContainer } from "@features/indicators/components/indicator-scroll-container"; -import { findLabelInExternalData2 } from "@features/indicators/utils"; +import { findLabel } from "@features/indicators/utils"; import { IndicatorPopupHeader } from "@features/popup/components/indicator-popup-header"; import { formatDate } from "@shared/lib/utils"; @@ -59,13 +59,8 @@ export const BioInventoryPopupContent: FC = ({ })} icon={} subtitle={ - findLabelInExternalData2( - labelData, - data.proj, - lang, - "loc2", - Number(data.forest), - ) || t("dataManagement.undefined", { ns: "common" }) + findLabel(labelData, data.proj, lang, "loc2", Number(data.forest)) || + t("dataManagement.undefined", { ns: "common" }) } title={title} {...headerProps} diff --git a/webapp/src/features/popup/bio-inventory/types.ts b/webapp/src/features/popup/bio-inventory/types.ts index 0b7cda65..a43b0ce2 100644 --- a/webapp/src/features/popup/bio-inventory/types.ts +++ b/webapp/src/features/popup/bio-inventory/types.ts @@ -13,7 +13,6 @@ export type BioInventoryData = { density: number; dens_unit: string; richness: number; - taxons_relative_abundance: Record; - pop_by_taxon: string[]; - total_pop: number; + taxons_abundance_pop: string[]; + taxons_total_pop: number; }; diff --git a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx index c8ccdf3e..a575c7ea 100644 --- a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx +++ b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx @@ -10,7 +10,7 @@ import { ICON_SIZE_HEADER } from "@features/indicators/components/constants"; import { IndicatorElements } from "@features/indicators/components/indicator-elements"; import { IndicatorScrollContainer } from "@features/indicators/components/indicator-scroll-container"; import { useSoilIndicatorElements } from "@features/indicators/soil"; -import { findLabelInExternalData } from "@features/indicators/utils"; +import { findLabel } from "@features/indicators/utils"; import { i18nInstance } from "@shared/i18n"; import { formatDate } from "@shared/lib/utils"; @@ -37,7 +37,7 @@ export const ForestInventoryPopupContent: FC< const [selectedTab, setSelectedTab] = useState(TABS.BIODIVERSITY); const externalData = use(externalDataPromise); const labelData = - externalData[LABEL_DATA.get(LAYERS.INVENTORY_BIO) || ""] || + externalData[LABEL_DATA.get(LAYERS.INVENTORY_FOR) || ""] || ([] as LabelData[]); const biodiversityElements = useBiodiversityIndicatorElements( @@ -61,24 +61,12 @@ export const ForestInventoryPopupContent: FC< }); const subtitle = - findLabelInExternalData( - externalData, - "for_label", - data.project, - lang, - "loc2", - data.for, - ) || t("common:dataManagement.undefined"); + findLabel(labelData, data.project, lang, "loc2", data.for) || + t("common:dataManagement.undefined"); const ecos = `${t("all4trees:popup.forestInventory.ecos")}: ${ - findLabelInExternalData( - externalData, - "for_label", - data.project, - lang, - "ecos", - data.ecos, - ) || t("dataManagement.undefined", { ns: "common" }) + findLabel(labelData, data.project, lang, "ecos", data.ecos) || + t("dataManagement.undefined", { ns: "common" }) }`; return ( diff --git a/webapp/src/shared/i18n/translations/en/all4trees.json b/webapp/src/shared/i18n/translations/en/all4trees.json index d0dc08c1..c33a3dd1 100644 --- a/webapp/src/shared/i18n/translations/en/all4trees.json +++ b/webapp/src/shared/i18n/translations/en/all4trees.json @@ -68,6 +68,7 @@ }, "bioinventory": { "sampArea": "Sampling area", + "status_abundance": "Relative abundance by extinction threat", "taxon": "Target taxon", "title": "Fauna inventory", "type": "Measures type" diff --git a/webapp/src/shared/i18n/translations/en/common.json b/webapp/src/shared/i18n/translations/en/common.json index 3ffc6c18..b519af44 100644 --- a/webapp/src/shared/i18n/translations/en/common.json +++ b/webapp/src/shared/i18n/translations/en/common.json @@ -20,6 +20,7 @@ "dataManagement": { "noData": "No data available", "noUnit": "Unit undefined", + "other": "Autre", "undefined": "not found" }, "error": { diff --git a/webapp/src/shared/i18n/translations/fr/all4trees.json b/webapp/src/shared/i18n/translations/fr/all4trees.json index f3d01d0f..762165d8 100644 --- a/webapp/src/shared/i18n/translations/fr/all4trees.json +++ b/webapp/src/shared/i18n/translations/fr/all4trees.json @@ -67,6 +67,7 @@ }, "bioinventory": { "sampArea": "Effort d'échantillonnage", + "status_abundance": "Abondance relative par menace d'extinction", "taxon": "Taxon cible", "title": "Inventaire de faune", "type": "Type de dispositif" diff --git a/webapp/src/shared/i18n/translations/fr/common.json b/webapp/src/shared/i18n/translations/fr/common.json index 79c3f67c..facf510a 100644 --- a/webapp/src/shared/i18n/translations/fr/common.json +++ b/webapp/src/shared/i18n/translations/fr/common.json @@ -20,6 +20,7 @@ "dataManagement": { "noData": "Données non disponibles", "noUnit": "Unité non définie", + "other": "Autre", "undefined": "non trouvée" }, "error": { From 6bbd181d60c15bec64783f34ba6b5afc19545f7e Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Fri, 10 Jul 2026 17:00:08 +0200 Subject: [PATCH 09/16] Fix FSD Arch --- webapp/src/entities/resources.ts | 2 +- webapp/src/features/controls/layer-control.tsx | 2 +- .../popup/bio-inventory/popup-bio-inventory.tsx | 2 +- .../popup/forest-inventory/popup-forest-inventory.tsx | 2 +- webapp/src/shared/api/categories-filters.ts | 2 +- webapp/src/{entities => shared/api}/layers.ts | 0 webapp/src/widgets/dashboard/dashboard.tsx | 10 +++++----- webapp/src/widgets/map/map-all4trees.tsx | 2 +- webapp/src/widgets/map/map-seed.tsx | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) rename webapp/src/{entities => shared/api}/layers.ts (100%) diff --git a/webapp/src/entities/resources.ts b/webapp/src/entities/resources.ts index f24734c9..0ade50e8 100644 --- a/webapp/src/entities/resources.ts +++ b/webapp/src/entities/resources.ts @@ -1,4 +1,4 @@ -import { LAYERS } from "./layers"; +import { LAYERS } from "../shared/api/layers"; export const EXTERNAL_RESOURCES_BY_LAYER = new Map([ [ diff --git a/webapp/src/features/controls/layer-control.tsx b/webapp/src/features/controls/layer-control.tsx index 0cc6be62..4789ac60 100644 --- a/webapp/src/features/controls/layer-control.tsx +++ b/webapp/src/features/controls/layer-control.tsx @@ -1,4 +1,3 @@ -import { LAYERS } from "@entities/layers"; import { HouseIcon, LayersIcon, @@ -11,6 +10,7 @@ import { import { Activity, useEffect, useRef, useState } from "react"; import { createRoot } from "react-dom/client"; +import { LAYERS } from "@shared/api/layers"; import { useTranslation } from "@shared/i18n"; import { LAYER_CONTROL_ELEMENTS, diff --git a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx index 773e089d..8e1103bc 100644 --- a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx +++ b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx @@ -1,4 +1,3 @@ -import { LAYERS } from "@entities/layers"; import { LABEL_DATA } from "@entities/resources"; import { cx } from "class-variance-authority"; import { Leaf } from "lucide-react"; @@ -11,6 +10,7 @@ import { IndicatorScrollContainer } from "@features/indicators/components/indica import { findLabel } from "@features/indicators/utils"; import { IndicatorPopupHeader } from "@features/popup/components/indicator-popup-header"; +import { LAYERS } from "@shared/api/layers"; import { formatDate } from "@shared/lib/utils"; import { i18nInstance, useTranslation } from "@i18n"; diff --git a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx index a575c7ea..4fe29152 100644 --- a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx +++ b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx @@ -1,4 +1,3 @@ -import { LAYERS } from "@entities/layers"; import { LABEL_DATA } from "@entities/resources"; import { cx } from "class-variance-authority"; import { TreesIcon } from "lucide-react"; @@ -12,6 +11,7 @@ import { IndicatorScrollContainer } from "@features/indicators/components/indica import { useSoilIndicatorElements } from "@features/indicators/soil"; import { findLabel } from "@features/indicators/utils"; +import { LAYERS } from "@shared/api/layers"; import { i18nInstance } from "@shared/i18n"; import { formatDate } from "@shared/lib/utils"; import { GridSelector } from "@shared/ui/grid-selector"; diff --git a/webapp/src/shared/api/categories-filters.ts b/webapp/src/shared/api/categories-filters.ts index 6b4c4307..99e08852 100644 --- a/webapp/src/shared/api/categories-filters.ts +++ b/webapp/src/shared/api/categories-filters.ts @@ -1,4 +1,4 @@ -import { LAYERS } from "@entities/layers"; +import { LAYERS } from "@shared/api/layers"; const SEPARATOR = ":::"; diff --git a/webapp/src/entities/layers.ts b/webapp/src/shared/api/layers.ts similarity index 100% rename from webapp/src/entities/layers.ts rename to webapp/src/shared/api/layers.ts diff --git a/webapp/src/widgets/dashboard/dashboard.tsx b/webapp/src/widgets/dashboard/dashboard.tsx index f1e80e52..0a8c9965 100644 --- a/webapp/src/widgets/dashboard/dashboard.tsx +++ b/webapp/src/widgets/dashboard/dashboard.tsx @@ -1,12 +1,12 @@ +import { useSuspenseData } from "@shared/api/suspense-fetch"; import { useCallback } from "react"; -import LoadedDashboard from "@widgets/dashboard/loaded-dashboard"; - -import { SuspenseBoundary } from "@features/fallback/suspense-boundary"; +import LoadedDashboard, { +} from "@widgets/dashboard/loaded-dashboard"; -import { useSuspenseData } from "@shared/api/suspense-fetch"; -import { LAYERS } from "@entities/layers"; +import { LAYERS } from "@shared/api/layers"; import { useApi } from "@shared/hooks/useApi"; +import { SuspenseBoundary } from "@features/fallback/suspense-boundary"; export default function Dashboard() { const { getDashboardData } = useApi(); diff --git a/webapp/src/widgets/map/map-all4trees.tsx b/webapp/src/widgets/map/map-all4trees.tsx index fe160454..384b614e 100644 --- a/webapp/src/widgets/map/map-all4trees.tsx +++ b/webapp/src/widgets/map/map-all4trees.tsx @@ -1,4 +1,3 @@ -import { LAYERS } from "@entities/layers"; import { type FC, useEffect, useState } from "react"; import { type SeedData, SeedIndicator } from "@features/indicators/seed"; @@ -19,6 +18,7 @@ import { SocioEcoIndicator, } from "@features/popup/socio-eco"; +import { LAYERS } from "@shared/api/layers"; import { useMap } from "@shared/hooks/use-map-all4trees"; import { useApi } from "@shared/hooks/useApi"; diff --git a/webapp/src/widgets/map/map-seed.tsx b/webapp/src/widgets/map/map-seed.tsx index f88e59d9..c7e6e810 100644 --- a/webapp/src/widgets/map/map-seed.tsx +++ b/webapp/src/widgets/map/map-seed.tsx @@ -1,4 +1,3 @@ -import { LAYERS } from "@entities/layers"; import { type FC, useEffect, useState } from "react"; import { type SeedData, SeedIndicator } from "@features/indicators/seed"; @@ -7,6 +6,7 @@ import { getRenderPopupLayer, } from "@features/popup/renderPopup"; +import { LAYERS } from "@shared/api/layers"; import { useMap } from "@shared/hooks/use-map-seed"; import { useApi } from "@shared/hooks/useApi"; From 9781996ea65f6b28ac1101655edb270e696beb21 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Wed, 15 Jul 2026 18:42:25 +0200 Subject: [PATCH 10/16] Add new external data --- backend/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/Makefile b/backend/Makefile index 88d204b5..82520d75 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -28,7 +28,7 @@ catalog: # All 4 Trees - Inventaire Biologique coordo add kobotoolbox data/all4trees/inventaire_bio/20260710_InventaireBiologique_DonneesK.xlsx --form data/all4trees/inventaire_bio/20260709_InventaireBiologique_QuestionnaireK.xlsx --package catalog/inventaire_bio - coordo add file data/all4trees/inventaire_bio/20260709_InventaireBiologique_DonneesExternes.xlsx --package catalog/inventaire_bio + coordo add file data/all4trees/inventaire_bio/20260713_InventaireBiologique_DonneesExternes.xlsx --package catalog/inventaire_bio coordo add foreignkey inv_bio.proj bio_samp.proj --package catalog/inventaire_bio coordo add foreignkey inv_bio.year bio_pop.year --package catalog/inventaire_bio From 69b97a38f6fd58cc95394ac906a3418f5a158f13 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Fri, 17 Jul 2026 16:09:30 +0200 Subject: [PATCH 11/16] Sync with last external data update --- backend/Makefile | 2 +- webapp/src/entities/data.ts | 25 +++++++++++++++++++ .../biodiversity/chart-relative-abundance.tsx | 3 ++- .../bioinventory/chart-species-status.tsx | 7 +++--- .../charts/components/lib/sunburst.ts | 3 +-- .../features/charts/components/lib/taxon.ts | 3 ++- webapp/src/features/charts/soil/types.ts | 2 +- .../indicators/bio-inventory/format-data.ts | 2 +- .../use-bioinventory-indicator-elements.tsx | 10 ++++---- .../use-biodiversity-indicator-elements.tsx | 6 ++--- .../soil/use-soil-indicator-elements.tsx | 2 +- webapp/src/features/indicators/utils.ts | 20 +++++++++------ .../bio-inventory/popup-bio-inventory.tsx | 18 ++++++------- .../popup-forest-inventory.tsx | 8 ++---- .../features/popup/forest-inventory/types.ts | 17 ------------- webapp/src/features/popup/renderPopup.tsx | 2 +- webapp/src/widgets/dashboard/dashboard.tsx | 10 ++++---- 17 files changed, 75 insertions(+), 65 deletions(-) create mode 100644 webapp/src/entities/data.ts diff --git a/backend/Makefile b/backend/Makefile index 82520d75..faa8336e 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -2,7 +2,7 @@ catalog: # All 4 Trees - Inventaire forestier coordo add kobotoolbox data/all4trees/inventaire_for/20260422_InventaireForestier_DonneesK.xlsx --form data/all4trees/inventaire_for/20260519_InventaireForestier_QuestionnaireK.xlsx --package catalog/inventaire_for - coordo add file data/all4trees/inventaire_for/20260703_InventaireForestier_DonneesExternes.xlsx --package catalog/inventaire_for + coordo add file data/all4trees/inventaire_for/20260713_InventaireForestier_DonneesExternes.xlsx --package catalog/inventaire_for coordo add foreignkey adu.decay for_dw.decay --package catalog/inventaire_for coordo add foreignkey inv_for.proj for_samp.proj --package catalog/inventaire_for diff --git a/webapp/src/entities/data.ts b/webapp/src/entities/data.ts new file mode 100644 index 00000000..157d78b4 --- /dev/null +++ b/webapp/src/entities/data.ts @@ -0,0 +1,25 @@ +export type ExternalData = { + for_label: LabelData[]; + bio_label: LabelData[]; + bio_sp: BioSpeciesData[]; + for_mf_tax1: any[]; + for_mf_tax2: any[]; + for_mf_tax3: any[]; + // Index signature pour accepter d'autres clés dynamiques si besoin + [key: string]: any[]; +}; + +export type LabelData = { + proj: string; + list_name: string; + name: number; + "label::fr": string; + "label::en": string; +}; + +export type BioSpeciesData = { + proj: string; + tax3: number; + "stat::fr": string; + "stat::en": string; +}; diff --git a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx index c01cfe39..2c618c3f 100644 --- a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx +++ b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx @@ -1,5 +1,6 @@ +import type { LabelData } from "@entities/data"; + import { findLabel } from "@features/indicators/utils"; -import type { LabelData } from "@features/popup/forest-inventory/types"; import { i18nInstance, useTranslation } from "@shared/i18n"; import type { ChartConfig } from "@shared/ui/chart"; diff --git a/webapp/src/features/charts/bioinventory/chart-species-status.tsx b/webapp/src/features/charts/bioinventory/chart-species-status.tsx index 542f7406..d63d7122 100644 --- a/webapp/src/features/charts/bioinventory/chart-species-status.tsx +++ b/webapp/src/features/charts/bioinventory/chart-species-status.tsx @@ -1,5 +1,6 @@ +import type { BioSpeciesData } from "@entities/data"; + import { findStatus } from "@features/indicators/utils"; -import type { LabelData } from "@features/popup/forest-inventory/types"; import { i18nInstance, useTranslation } from "@shared/i18n"; import type { ChartConfig } from "@shared/ui/chart"; @@ -9,7 +10,7 @@ import { PieChartCategorical } from "../components/pie-chart-categorical"; type PieChartProps = { data: Record; - metadata: LabelData[]; + metadata: BioSpeciesData[]; project: string; }; @@ -27,7 +28,7 @@ export const ChartSpeciesStatus: ChartComponentType = ({ const tax3 = name.split("-")[2]; const tax3status = ( tax3 - ? findStatus(metadata, project, lang, "tax3", Number(tax3)) + ? findStatus(metadata, project, lang, Number(tax3)) : t("common:dataManagement.other") ) as string; return [tax3status, value]; diff --git a/webapp/src/features/charts/components/lib/sunburst.ts b/webapp/src/features/charts/components/lib/sunburst.ts index c0c0edd3..554eb8bf 100644 --- a/webapp/src/features/charts/components/lib/sunburst.ts +++ b/webapp/src/features/charts/components/lib/sunburst.ts @@ -1,7 +1,6 @@ +import type { LabelData } from "@entities/data"; import { darken, lighten } from "color2k"; -import type { LabelData } from "@features/popup/forest-inventory/types"; - import { getChartPalette } from "@shared/lib/palette"; import type { SunburstNode } from "../../soil/types"; diff --git a/webapp/src/features/charts/components/lib/taxon.ts b/webapp/src/features/charts/components/lib/taxon.ts index dfa9f098..d03858a9 100644 --- a/webapp/src/features/charts/components/lib/taxon.ts +++ b/webapp/src/features/charts/components/lib/taxon.ts @@ -1,5 +1,6 @@ +import type { LabelData } from "@entities/data"; + import { findLabel } from "@features/indicators/utils"; -import type { LabelData } from "@features/popup/forest-inventory/types"; export function getDataTypePrefix(dataType: "tsbf" | "barbA" | null): string { return dataType ? `${dataType}_` : ""; diff --git a/webapp/src/features/charts/soil/types.ts b/webapp/src/features/charts/soil/types.ts index e4705762..96d26c78 100644 --- a/webapp/src/features/charts/soil/types.ts +++ b/webapp/src/features/charts/soil/types.ts @@ -1,4 +1,4 @@ -import type { LabelData } from "@features/popup/forest-inventory/types"; +import type { LabelData } from "@entities/data"; export type PieChartProps = { data: Record; diff --git a/webapp/src/features/indicators/bio-inventory/format-data.ts b/webapp/src/features/indicators/bio-inventory/format-data.ts index f6cf93c4..4dee16c1 100644 --- a/webapp/src/features/indicators/bio-inventory/format-data.ts +++ b/webapp/src/features/indicators/bio-inventory/format-data.ts @@ -1,3 +1,4 @@ +import type { LabelData } from "@entities/data"; import { useTranslation } from "react-i18next"; import { @@ -8,7 +9,6 @@ import { useFormatterWithUnit, } from "@features/indicators/utils"; import type { BioInventoryData } from "@features/popup/bio-inventory"; -import type { LabelData } from "@features/popup/forest-inventory/types"; import { i18nInstance } from "@shared/i18n"; import type { NumericKeys } from "@shared/types"; diff --git a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx index fcbaf7e9..841a99be 100644 --- a/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx +++ b/webapp/src/features/indicators/bio-inventory/use-bioinventory-indicator-elements.tsx @@ -1,9 +1,9 @@ +import type { ExternalData } from "@entities/data"; import { Gem, PawPrint, Ruler, SearchAlert, VectorSquare } from "lucide-react"; import { ChartSpeciesStatus } from "@features/charts/bioinventory/chart-species-status"; import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; import type { BioInventoryData } from "@features/popup/bio-inventory/types"; -import type { LabelData } from "@features/popup/forest-inventory/types"; import { useTranslation } from "@i18n"; @@ -14,10 +14,10 @@ import { useFormatBioInventoryData } from "./format-data"; export const useBioInventoryIndicatorElements = ( rawData: BioInventoryData, - metadata: LabelData[], + externalData: ExternalData, ): UseIndicatorReturnType => { const { t } = useTranslation("all4trees"); - const data = useFormatBioInventoryData(rawData, metadata); + const data = useFormatBioInventoryData(rawData, externalData.bio_label); return [ { @@ -50,12 +50,12 @@ export const useBioInventoryIndicatorElements = ( /> diff --git a/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx b/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx index ec7285c8..8f0650dc 100644 --- a/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx +++ b/webapp/src/features/indicators/biodiversity/use-biodiversity-indicator-elements.tsx @@ -1,11 +1,9 @@ +import type { LabelData } from "@entities/data"; import { Gem, TreePine, Trees } from "lucide-react"; import { ChartForestPotential } from "@features/charts/biodiversity/chart-forest-potential"; import { ChartRelativeAbundance } from "@features/charts/biodiversity/chart-relative-abundance"; -import type { - ForestInventoryData, - LabelData, -} from "@features/popup/forest-inventory/types"; +import type { ForestInventoryData } from "@features/popup/forest-inventory/types"; import { useTranslation } from "@i18n"; diff --git a/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx b/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx index de11199d..5a798374 100644 --- a/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx +++ b/webapp/src/features/indicators/soil/use-soil-indicator-elements.tsx @@ -1,3 +1,4 @@ +import type { LabelData } from "@entities/data"; import { Bug, Gem, Sprout } from "lucide-react"; import { ChartTaxonAbundance } from "@features/charts/components/chart-taxon-abundance"; @@ -6,7 +7,6 @@ import { ChartWindErosion } from "@features/charts/soil/ui/chart-wind-erosion"; import type { UseIndicatorReturnType } from "@features/indicators//components/types"; import { IndicatorRawValue } from "@features/indicators/components/indicator-raw-value"; import type { ForestInventoryData } from "@features/popup/forest-inventory"; -import type { LabelData } from "@features/popup/forest-inventory/types"; import { useTranslation } from "@i18n"; diff --git a/webapp/src/features/indicators/utils.ts b/webapp/src/features/indicators/utils.ts index de923ba8..fc8d9236 100644 --- a/webapp/src/features/indicators/utils.ts +++ b/webapp/src/features/indicators/utils.ts @@ -1,4 +1,4 @@ -import type { LabelData } from "@features/popup/forest-inventory/types"; +import type { BioSpeciesData, LabelData } from "@entities/data"; import { useTranslation } from "@shared/i18n"; import type { LayerMetadata } from "@shared/lib/coordo"; @@ -198,15 +198,21 @@ export function findCategoricalLabel( // Find status of corresponding taxon value. export function findStatus( - resourceData: LabelData[], + resourceData: BioSpeciesData[], project: string, lang: string, - fieldName: string, - fieldValue: any, + taxon: number, ): string | undefined { - return findMatchingRecord(resourceData, project, fieldName, fieldValue)?.[ - `stat::${lang}` - ]; + if (!resourceData || !Array.isArray(resourceData)) { + return undefined; + } + + // Find the record matching all criteria: project, list_name, and name + const record = resourceData.find((item: BioSpeciesData) => { + return item.proj?.trim() === project.trim() && item.tax3 === taxon; + }); + + return record?.[`stat::${lang}` as keyof BioSpeciesData] as string; } export function findLabel( diff --git a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx index 8e1103bc..527d6574 100644 --- a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx +++ b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx @@ -1,4 +1,3 @@ -import { LABEL_DATA } from "@entities/resources"; import { cx } from "class-variance-authority"; import { Leaf } from "lucide-react"; import { type FC, use } from "react"; @@ -10,11 +9,9 @@ import { IndicatorScrollContainer } from "@features/indicators/components/indica import { findLabel } from "@features/indicators/utils"; import { IndicatorPopupHeader } from "@features/popup/components/indicator-popup-header"; -import { LAYERS } from "@shared/api/layers"; import { formatDate } from "@shared/lib/utils"; import { i18nInstance, useTranslation } from "@i18n"; -import type { LabelData } from "../forest-inventory/types"; import type { RenderPopupProps } from "../renderPopup"; import type { BioInventoryData } from "./types"; @@ -29,12 +26,10 @@ export const BioInventoryPopupContent: FC = ({ const { t } = useTranslation(["common", "all4trees"]); const lang = i18nInstance.language; const externalData = use(externalDataPromise); - const labelData = - externalData[LABEL_DATA.get(LAYERS.INVENTORY_BIO) || ""] || - ([] as LabelData[]); + const biodiversityElements = useBioInventoryIndicatorElements( data, - labelData, + externalData, ); console.log( @@ -59,8 +54,13 @@ export const BioInventoryPopupContent: FC = ({ })} icon={} subtitle={ - findLabel(labelData, data.proj, lang, "loc2", Number(data.forest)) || - t("dataManagement.undefined", { ns: "common" }) + findLabel( + externalData.bio_label, + data.proj, + lang, + "loc2", + Number(data.forest), + ) || t("dataManagement.undefined", { ns: "common" }) } title={title} {...headerProps} diff --git a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx index 4fe29152..f8b96abe 100644 --- a/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx +++ b/webapp/src/features/popup/forest-inventory/popup-forest-inventory.tsx @@ -1,4 +1,3 @@ -import { LABEL_DATA } from "@entities/resources"; import { cx } from "class-variance-authority"; import { TreesIcon } from "lucide-react"; import { Activity, type FC, use, useState } from "react"; @@ -11,14 +10,13 @@ import { IndicatorScrollContainer } from "@features/indicators/components/indica import { useSoilIndicatorElements } from "@features/indicators/soil"; import { findLabel } from "@features/indicators/utils"; -import { LAYERS } from "@shared/api/layers"; import { i18nInstance } from "@shared/i18n"; import { formatDate } from "@shared/lib/utils"; import { GridSelector } from "@shared/ui/grid-selector"; import { IndicatorPopupHeader } from "../components/indicator-popup-header"; import type { RenderPopupProps } from "../renderPopup"; -import type { ForestInventoryData, LabelData } from "./types"; +import type { ForestInventoryData } from "./types"; type ForestInventoryPopupContentProps = RenderPopupProps; @@ -36,9 +34,7 @@ export const ForestInventoryPopupContent: FC< const lang = i18nInstance.language; const [selectedTab, setSelectedTab] = useState(TABS.BIODIVERSITY); const externalData = use(externalDataPromise); - const labelData = - externalData[LABEL_DATA.get(LAYERS.INVENTORY_FOR) || ""] || - ([] as LabelData[]); + const labelData = externalData.for_label; const biodiversityElements = useBiodiversityIndicatorElements( data, diff --git a/webapp/src/features/popup/forest-inventory/types.ts b/webapp/src/features/popup/forest-inventory/types.ts index 8441591b..3073b3e9 100644 --- a/webapp/src/features/popup/forest-inventory/types.ts +++ b/webapp/src/features/popup/forest-inventory/types.ts @@ -11,20 +11,3 @@ export type ForestInventoryData = { plot_size: number; } & SoilData & BiodiversityData; - -export type ExternalData = { - for_label: LabelData[]; - for_mf_tax1: any[]; - for_mf_tax2: any[]; - for_mf_tax3: any[]; - for_score: any[]; - // Index signature pour accepter d'autres clés dynamiques si besoin - [key: string]: any[]; -}; - -export type LabelData = { - proj: string; - list_name: string; - name: number; - label: string; -}; diff --git a/webapp/src/features/popup/renderPopup.tsx b/webapp/src/features/popup/renderPopup.tsx index f65a7ecf..665782d7 100644 --- a/webapp/src/features/popup/renderPopup.tsx +++ b/webapp/src/features/popup/renderPopup.tsx @@ -1,3 +1,4 @@ +import type { ExternalData } from "@entities/data"; import type { FC } from "react"; import { createRoot } from "react-dom/client"; @@ -5,7 +6,6 @@ import type { LayerMetadata, PopupOptions } from "@shared/lib/coordo"; import type { IndicatorPopupHeaderProps } from "./components/indicator-popup-header"; import { Popup } from "./components/popup"; -import type { ExternalData } from "./forest-inventory/types"; export const getPopupSizeCustomVariables = (isMaximizedPopupSize: boolean) => { return { diff --git a/webapp/src/widgets/dashboard/dashboard.tsx b/webapp/src/widgets/dashboard/dashboard.tsx index 0a8c9965..a224170e 100644 --- a/webapp/src/widgets/dashboard/dashboard.tsx +++ b/webapp/src/widgets/dashboard/dashboard.tsx @@ -1,12 +1,12 @@ -import { useSuspenseData } from "@shared/api/suspense-fetch"; import { useCallback } from "react"; -import LoadedDashboard, { -} from "@widgets/dashboard/loaded-dashboard"; +import LoadedDashboard from "@widgets/dashboard/loaded-dashboard"; + +import { SuspenseBoundary } from "@features/fallback/suspense-boundary"; import { LAYERS } from "@shared/api/layers"; +import { useSuspenseData } from "@shared/api/suspense-fetch"; import { useApi } from "@shared/hooks/useApi"; -import { SuspenseBoundary } from "@features/fallback/suspense-boundary"; export default function Dashboard() { const { getDashboardData } = useApi(); @@ -14,7 +14,7 @@ export default function Dashboard() { // Stable per API client (auth token) so the promise cache stays scoped to // the current session — see suspense-fetch.ts. const fetcher = useCallback( - () => getDashboardData(LAYERS.INVENTARY), + () => getDashboardData(LAYERS.INVENTORY_FOR), [getDashboardData], ); const { dataPromise, retry } = useSuspenseData({ fetcher }); From 9299136873a4cfc9d795db911f895efc2d50cf34 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Fri, 17 Jul 2026 16:10:42 +0200 Subject: [PATCH 12/16] Remove console.log --- .../features/popup/bio-inventory/popup-bio-inventory.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx index 527d6574..9b4f0567 100644 --- a/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx +++ b/webapp/src/features/popup/bio-inventory/popup-bio-inventory.tsx @@ -31,15 +31,6 @@ export const BioInventoryPopupContent: FC = ({ data, externalData, ); - - console.log( - "BioInventoryPopupContent data", - data, - "metadata", - externalData, - "biodiversityElements", - biodiversityElements, - ); const title = t("popup.bioInventory.title", { id: data.id, ns: "all4trees", From 0f987e18795edd3780fc0fae1315729788118d47 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Sat, 18 Jul 2026 15:04:55 +0200 Subject: [PATCH 13/16] Fix review --- backend/configs/all4trees_config.json | 2 +- webapp/biome.json | 5 ++++- webapp/src/entities/resources.ts | 2 +- .../biodiversity/chart-relative-abundance.tsx | 4 ++-- .../bioinventory/chart-species-status.tsx | 17 +++++++---------- .../features/charts/components/lib/sunburst.ts | 3 ++- .../src/features/charts/components/lib/taxon.ts | 4 ++-- .../indicators/bio-inventory/format-data.ts | 3 ++- .../features/indicators/bio-inventory/index.ts | 2 +- .../use-bioinventory-indicator-elements.tsx | 3 ++- .../use-biodiversity-indicator-elements.tsx | 3 ++- .../soil/use-soil-indicator-elements.tsx | 3 ++- .../popup/bio-inventory/popup-bio-inventory.tsx | 2 +- webapp/src/features/popup/renderPopup.tsx | 3 ++- .../widgets/map/assets/bio-inventory-icon.svg | 12 ++++++++++++ ...taire-icon.svg => forest-inventory-icon.svg} | 0 webapp/src/widgets/map/map-all4trees.tsx | 7 ++++--- 17 files changed, 47 insertions(+), 28 deletions(-) create mode 100644 webapp/src/widgets/map/assets/bio-inventory-icon.svg rename webapp/src/widgets/map/assets/{inventaire-icon.svg => forest-inventory-icon.svg} (100%) diff --git a/backend/configs/all4trees_config.json b/backend/configs/all4trees_config.json index e8eb279b..7264166c 100644 --- a/backend/configs/all4trees_config.json +++ b/backend/configs/all4trees_config.json @@ -101,7 +101,7 @@ "radius": 2, "maxZoom": 12, "steps": [10, 20], - "colors": ["#99D6C2", "#009966", "#006B47"] + "colors": ["#f4a236", "#e58800", "#b76c00"] }, "columns": { "geom": "start_point.merge().centroid()", diff --git a/webapp/biome.json b/webapp/biome.json index ea96cdd8..319ff7d3 100644 --- a/webapp/biome.json +++ b/webapp/biome.json @@ -3,7 +3,7 @@ "assist": { "actions": { "source": { - "organizeImports": { +"organizeImports": { "level": "on", "options": { "groups": [ @@ -16,6 +16,7 @@ "!@pages/**", "!@widgets/**", "!@features/**", + "!@entities/**", "!@shared/**", "!@lib/**", "!@i18n", @@ -31,6 +32,8 @@ ":BLANK_LINE:", "@features/**", ":BLANK_LINE:", + "@entities/**", + ":BLANK_LINE:", "@shared/**", "@lib/**", "@i18n", diff --git a/webapp/src/entities/resources.ts b/webapp/src/entities/resources.ts index 0ade50e8..31301d8d 100644 --- a/webapp/src/entities/resources.ts +++ b/webapp/src/entities/resources.ts @@ -1,4 +1,4 @@ -import { LAYERS } from "../shared/api/layers"; +import { LAYERS } from "@shared/api/layers"; export const EXTERNAL_RESOURCES_BY_LAYER = new Map([ [ diff --git a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx index 2c618c3f..20bb4b55 100644 --- a/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx +++ b/webapp/src/features/charts/biodiversity/chart-relative-abundance.tsx @@ -1,7 +1,7 @@ -import type { LabelData } from "@entities/data"; - import { findLabel } from "@features/indicators/utils"; +import type { LabelData } from "@entities/data"; + import { i18nInstance, useTranslation } from "@shared/i18n"; import type { ChartConfig } from "@shared/ui/chart"; diff --git a/webapp/src/features/charts/bioinventory/chart-species-status.tsx b/webapp/src/features/charts/bioinventory/chart-species-status.tsx index d63d7122..0c1935d4 100644 --- a/webapp/src/features/charts/bioinventory/chart-species-status.tsx +++ b/webapp/src/features/charts/bioinventory/chart-species-status.tsx @@ -1,7 +1,7 @@ -import type { BioSpeciesData } from "@entities/data"; - import { findStatus } from "@features/indicators/utils"; +import type { BioSpeciesData } from "@entities/data"; + import { i18nInstance, useTranslation } from "@shared/i18n"; import type { ChartConfig } from "@shared/ui/chart"; @@ -54,15 +54,12 @@ export const ChartSpeciesStatus: ChartComponentType = ({ })); // Generate chart config from chart Data - let chartConfig: ChartConfig = {}; - chartData.forEach((element) => { - chartConfig = { - ...chartConfig, - [element.name]: { - label: element.name, - }, + const chartConfig = chartData.reduce((acc, element) => { + return { + ...acc, + [element.name]: { label: element.name }, }; - }); + }, {} as ChartConfig); return ( + + + + + + + + + + + diff --git a/webapp/src/widgets/map/assets/inventaire-icon.svg b/webapp/src/widgets/map/assets/forest-inventory-icon.svg similarity index 100% rename from webapp/src/widgets/map/assets/inventaire-icon.svg rename to webapp/src/widgets/map/assets/forest-inventory-icon.svg diff --git a/webapp/src/widgets/map/map-all4trees.tsx b/webapp/src/widgets/map/map-all4trees.tsx index 384b614e..e2f9ca5f 100644 --- a/webapp/src/widgets/map/map-all4trees.tsx +++ b/webapp/src/widgets/map/map-all4trees.tsx @@ -22,7 +22,8 @@ import { LAYERS } from "@shared/api/layers"; import { useMap } from "@shared/hooks/use-map-all4trees"; import { useApi } from "@shared/hooks/useApi"; -import pictoInventaire from "./assets/inventaire-icon.svg"; +import pictoBioInventory from "./assets/bio-inventory-icon.svg"; +import pictoForestInventory from "./assets/forest-inventory-icon.svg"; import pictoSocioEco from "./assets/socio-eco-icon.svg"; import { MapBase } from "./map-base"; import { getExternalDataPromiseByLayer, getIconSize } from "./utils"; @@ -41,7 +42,7 @@ export const MapAll4Trees: FC = () => { mapApiRef.current.setLayerSymbol({ iconSize: getIconSize({}), layerId: LAYERS.INVENTORY_FOR, - svg: pictoInventaire, + svg: pictoForestInventory, }); mapApiRef.current.setLayerSymbol({ @@ -53,7 +54,7 @@ export const MapAll4Trees: FC = () => { mapApiRef.current.setLayerSymbol({ iconSize: getIconSize({}), layerId: LAYERS.INVENTORY_BIO, - svg: pictoInventaire, + svg: pictoBioInventory, }); // Set the popup for the "inventaire_for" layer From e7e5607f407b506e242cda1b3697af2ce8b65760 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Sun, 19 Jul 2026 08:56:40 +0200 Subject: [PATCH 14/16] Improve cluster visibility and sync layer color with all4trees design chart --- backend/configs/all4trees_config.json | 4 ++-- backend/requirements.txt | 2 +- webapp/package-lock.json | 4 ++-- webapp/package.json | 2 +- webapp/src/widgets/map/assets/bio-inventory-icon.svg | 6 +++--- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/backend/configs/all4trees_config.json b/backend/configs/all4trees_config.json index 7264166c..b075dc3f 100644 --- a/backend/configs/all4trees_config.json +++ b/backend/configs/all4trees_config.json @@ -98,10 +98,10 @@ "resource": "inv_bio", "layerType": "symbol", "cluster": { - "radius": 2, + "radius": 40, "maxZoom": 12, "steps": [10, 20], - "colors": ["#f4a236", "#e58800", "#b76c00"] + "colors": ["#f98038", "#c7662c", "#954c21"] }, "columns": { "geom": "start_point.merge().centroid()", diff --git a/backend/requirements.txt b/backend/requirements.txt index 68fbf144..5fb46631 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,6 @@ asgiref==3.8.1 chardet==7.4.3 -coordo @ git+https://github.com/dataforgoodfr/Coordonnees.git@0.8.0#subdirectory=coordo-py +coordo @ git+https://github.com/dataforgoodfr/Coordonnees.git@0.9.0#subdirectory=coordo-py Django>=4.2.27 djangorestframework==3.16.0 djangorestframework_simplejwt==5.5.1 diff --git a/webapp/package-lock.json b/webapp/package-lock.json index d434a1ec..a822f6ce 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -24,7 +24,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "color2k": "^2.0.4", - "coordo": "github:dataforgoodfr/Coordonnees#0.8.1", + "coordo": "github:dataforgoodfr/Coordonnees#test-0.9.2", "i18next": "^25.8.0", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", @@ -4605,7 +4605,7 @@ }, "node_modules/coordo": { "version": "0.7.0", - "resolved": "git+ssh://git@github.com/dataforgoodfr/Coordonnees.git#c6a65de3db142e9b1a7505224cce794c4d1ef7a2", + "resolved": "git+ssh://git@github.com/dataforgoodfr/Coordonnees.git#bfa8913d6a94b6d9cde4950b9cf6b4304547a2ba", "license": "ISC", "dependencies": { "maplibre-gl": "^5.16.0" diff --git a/webapp/package.json b/webapp/package.json index e4fb02a3..1c11b23f 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -41,7 +41,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "color2k": "^2.0.4", - "coordo": "github:dataforgoodfr/Coordonnees#0.8.1", + "coordo": "github:dataforgoodfr/Coordonnees#test-0.9.2", "i18next": "^25.8.0", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", diff --git a/webapp/src/widgets/map/assets/bio-inventory-icon.svg b/webapp/src/widgets/map/assets/bio-inventory-icon.svg index 5b00adef..a430b787 100644 --- a/webapp/src/widgets/map/assets/bio-inventory-icon.svg +++ b/webapp/src/widgets/map/assets/bio-inventory-icon.svg @@ -1,12 +1,12 @@ - + - + - + From ac0756d8208af2ead10a8f93988902b7d4e0e7b2 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Sun, 19 Jul 2026 17:03:08 +0200 Subject: [PATCH 15/16] Use rgba colors for clusters and fix public data bug --- backend/configs/all4trees_config.json | 6 +++--- backend/maps/views.py | 3 ++- backend/requirements.txt | 2 +- webapp/package-lock.json | 4 ++-- webapp/package.json | 2 +- 5 files changed, 9 insertions(+), 8 deletions(-) diff --git a/backend/configs/all4trees_config.json b/backend/configs/all4trees_config.json index b075dc3f..035acbab 100644 --- a/backend/configs/all4trees_config.json +++ b/backend/configs/all4trees_config.json @@ -44,7 +44,7 @@ "radius": 40, "maxZoom": 12, "steps": [10, 20], - "colors": ["#99D6C2", "#009966", "#006B47"] + "colors": ["rgba(153,214,194,0.7)", "rgba(0,153,102,0.7)", "rgba(0,107,71,0.7)"] }, "columns": { "id": "_id", @@ -101,7 +101,7 @@ "radius": 40, "maxZoom": 12, "steps": [10, 20], - "colors": ["#f98038", "#c7662c", "#954c21"] + "colors": ["rgba(249,128,56,0.7)", "rgba(199,102,44,0.7)", "rgba(149,76,33,0.7)"] }, "columns": { "geom": "start_point.merge().centroid()", @@ -137,7 +137,7 @@ "radius": 50, "maxZoom": 12, "steps": [10, 20], - "colors": ["#A1B5F5", "#1447E6", "#0E32A1"] + "colors": ["rgba(161,181,245,0.7)", "rgba(20,71,230,0.7)", "rgba(14,50,161,0.7)"] }, "columns": { "geom": "centroid(merge(point(enq_gps.long, enq_gps.latit)))", diff --git a/backend/maps/views.py b/backend/maps/views.py index e7e53617..d0a4bfe2 100644 --- a/backend/maps/views.py +++ b/backend/maps/views.py @@ -13,7 +13,7 @@ from . import stats from .datapackage_manager import DatapackageManager -ALL4TREES_LAYERS = ['inventaire_for', 'enquete'] +ALL4TREES_LAYERS = ['inventaire_for', 'enquete', 'inventaire_bio'] config_path = settings.BASE_DIR / "configs" / "all4trees_config.json" map = Map.from_file(config_path) @@ -108,6 +108,7 @@ def remove_foreign_key_view(request): def get_map(user): user_map = copy(map) + filter = '' if user.is_authenticated: project = user.project if (project.lower() != ADMIN_PROJECT): diff --git a/backend/requirements.txt b/backend/requirements.txt index 5fb46631..04617858 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,6 +1,6 @@ asgiref==3.8.1 chardet==7.4.3 -coordo @ git+https://github.com/dataforgoodfr/Coordonnees.git@0.9.0#subdirectory=coordo-py +coordo @ git+https://github.com/dataforgoodfr/Coordonnees.git@0.8.1#subdirectory=coordo-py Django>=4.2.27 djangorestframework==3.16.0 djangorestframework_simplejwt==5.5.1 diff --git a/webapp/package-lock.json b/webapp/package-lock.json index a822f6ce..d434a1ec 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -24,7 +24,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "color2k": "^2.0.4", - "coordo": "github:dataforgoodfr/Coordonnees#test-0.9.2", + "coordo": "github:dataforgoodfr/Coordonnees#0.8.1", "i18next": "^25.8.0", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", @@ -4605,7 +4605,7 @@ }, "node_modules/coordo": { "version": "0.7.0", - "resolved": "git+ssh://git@github.com/dataforgoodfr/Coordonnees.git#bfa8913d6a94b6d9cde4950b9cf6b4304547a2ba", + "resolved": "git+ssh://git@github.com/dataforgoodfr/Coordonnees.git#c6a65de3db142e9b1a7505224cce794c4d1ef7a2", "license": "ISC", "dependencies": { "maplibre-gl": "^5.16.0" diff --git a/webapp/package.json b/webapp/package.json index 1c11b23f..e4fb02a3 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -41,7 +41,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "color2k": "^2.0.4", - "coordo": "github:dataforgoodfr/Coordonnees#test-0.9.2", + "coordo": "github:dataforgoodfr/Coordonnees#0.8.1", "i18next": "^25.8.0", "i18next-browser-languagedetector": "^8.2.0", "i18next-http-backend": "^3.0.2", From a06a690b157566964b2baaccbd41c74512528154 Mon Sep 17 00:00:00 2001 From: Arnaud Fournier Date: Sun, 19 Jul 2026 17:10:56 +0200 Subject: [PATCH 16/16] Fix lint & bug --- webapp/src/entities/resources.ts | 2 +- webapp/src/shared/api/layers.ts | 3 ++- webapp/src/shared/api/suspense-fetch.ts | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/webapp/src/entities/resources.ts b/webapp/src/entities/resources.ts index 31301d8d..cae9ca9a 100644 --- a/webapp/src/entities/resources.ts +++ b/webapp/src/entities/resources.ts @@ -5,7 +5,7 @@ export const EXTERNAL_RESOURCES_BY_LAYER = new Map([ LAYERS.INVENTORY_FOR, ["for_label", "for_mf_tax1", "for_mf_tax2", "for_mf_tax3", "for_score"], ], - [LAYERS.INVENTORY_BIO, ["bio_label"]], + [LAYERS.INVENTORY_BIO, ["bio_label", "bio_sp"]], [LAYERS.ENQUETE, [""]], [LAYERS.SEED_POINT, [""]], ]); diff --git a/webapp/src/shared/api/layers.ts b/webapp/src/shared/api/layers.ts index 3ae1434d..e9169f96 100644 --- a/webapp/src/shared/api/layers.ts +++ b/webapp/src/shared/api/layers.ts @@ -12,5 +12,6 @@ export const LAYERS = { export const LAYERS_WITH_CLUSTERS: string[] = [ LAYERS.ENQUETE, - LAYERS.INVENTARY, + LAYERS.INVENTORY_FOR, + LAYERS.INVENTORY_BIO, ]; diff --git a/webapp/src/shared/api/suspense-fetch.ts b/webapp/src/shared/api/suspense-fetch.ts index feda300a..0568d4a2 100644 --- a/webapp/src/shared/api/suspense-fetch.ts +++ b/webapp/src/shared/api/suspense-fetch.ts @@ -18,7 +18,7 @@ import { useCallback, useMemo, useState } from "react"; * ```tsx * const { getDashboardData } = useApi(); * const fetcher = useCallback( - * () => getDashboardData(LAYERS.INVENTARY), + * () => getDashboardData(LAYERS.INVENTORY_FOR), * [getDashboardData], * ); * const { dataPromise, retry } = useSuspenseData({ fetcher });