From 33850f1afc5998e7673588f9dfd987f6dd750f91 Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Mon, 13 Nov 2023 12:31:55 +0000 Subject: [PATCH 1/9] Update FE orval config to include mpa-protection-coverage-stat --- frontend/orval.config.ts | 1 + .../generated/mpa-protection-coverage-stat.ts | 182 ++++++++++++++++++ .../src/types/generated/strapi.schemas.ts | 50 +++++ 3 files changed, 233 insertions(+) create mode 100644 frontend/src/types/generated/mpa-protection-coverage-stat.ts diff --git a/frontend/orval.config.ts b/frontend/orval.config.ts index 37c6da0e..c248b423 100644 --- a/frontend/orval.config.ts +++ b/frontend/orval.config.ts @@ -32,6 +32,7 @@ module.exports = { 'Mpaa-protection-level', 'Mpaa-protection-level-stat', 'Mpaa-establishment-stage', + 'Mpa-protection-coverage-stat', 'Mpaa-establishment-stage-status', 'Protection-coverage-stat', 'Protection-status', diff --git a/frontend/src/types/generated/mpa-protection-coverage-stat.ts b/frontend/src/types/generated/mpa-protection-coverage-stat.ts new file mode 100644 index 00000000..e067b5a5 --- /dev/null +++ b/frontend/src/types/generated/mpa-protection-coverage-stat.ts @@ -0,0 +1,182 @@ +/** + * Generated by orval v6.18.1 🍺 + * Do not edit manually. + * DOCUMENTATION + * OpenAPI spec version: 1.0.0 + */ +import { useQuery } from '@tanstack/react-query'; +import type { + UseQueryOptions, + QueryFunction, + UseQueryResult, + QueryKey, +} from '@tanstack/react-query'; +import type { + MpaProtectionCoverageStatListResponse, + Error, + GetMpaProtectionCoverageStatsParams, + MpaProtectionCoverageStatResponse, + GetMpaProtectionCoverageStatsIdParams, +} from './strapi.schemas'; +import { API } from '../../services/api/index'; +import type { ErrorType } from '../../services/api/index'; + +// eslint-disable-next-line +type SecondParameter any> = T extends ( + config: any, + args: infer P +) => any + ? P + : never; + +export const getMpaProtectionCoverageStats = ( + params?: GetMpaProtectionCoverageStatsParams, + options?: SecondParameter, + signal?: AbortSignal +) => { + return API( + { url: `/mpa-protection-coverage-stats`, method: 'get', params, signal }, + options + ); +}; + +export const getGetMpaProtectionCoverageStatsQueryKey = ( + params?: GetMpaProtectionCoverageStatsParams +) => { + return [`/mpa-protection-coverage-stats`, ...(params ? [params] : [])] as const; +}; + +export const getGetMpaProtectionCoverageStatsQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + params?: GetMpaProtectionCoverageStatsParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + request?: SecondParameter; + } +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetMpaProtectionCoverageStatsQueryKey(params); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getMpaProtectionCoverageStats(params, requestOptions, signal); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetMpaProtectionCoverageStatsQueryResult = NonNullable< + Awaited> +>; +export type GetMpaProtectionCoverageStatsQueryError = ErrorType; + +export const useGetMpaProtectionCoverageStats = < + TData = Awaited>, + TError = ErrorType +>( + params?: GetMpaProtectionCoverageStatsParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + request?: SecondParameter; + } +): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetMpaProtectionCoverageStatsQueryOptions(params, options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; + +export const getMpaProtectionCoverageStatsId = ( + id: number, + params?: GetMpaProtectionCoverageStatsIdParams, + options?: SecondParameter, + signal?: AbortSignal +) => { + return API( + { url: `/mpa-protection-coverage-stats/${id}`, method: 'get', params, signal }, + options + ); +}; + +export const getGetMpaProtectionCoverageStatsIdQueryKey = ( + id: number, + params?: GetMpaProtectionCoverageStatsIdParams +) => { + return [`/mpa-protection-coverage-stats/${id}`, ...(params ? [params] : [])] as const; +}; + +export const getGetMpaProtectionCoverageStatsIdQueryOptions = < + TData = Awaited>, + TError = ErrorType +>( + id: number, + params?: GetMpaProtectionCoverageStatsIdParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + request?: SecondParameter; + } +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetMpaProtectionCoverageStatsIdQueryKey(id, params); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getMpaProtectionCoverageStatsId(id, params, requestOptions, signal); + + return { queryKey, queryFn, enabled: !!id, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: QueryKey }; +}; + +export type GetMpaProtectionCoverageStatsIdQueryResult = NonNullable< + Awaited> +>; +export type GetMpaProtectionCoverageStatsIdQueryError = ErrorType; + +export const useGetMpaProtectionCoverageStatsId = < + TData = Awaited>, + TError = ErrorType +>( + id: number, + params?: GetMpaProtectionCoverageStatsIdParams, + options?: { + query?: UseQueryOptions< + Awaited>, + TError, + TData + >; + request?: SecondParameter; + } +): UseQueryResult & { queryKey: QueryKey } => { + const queryOptions = getGetMpaProtectionCoverageStatsIdQueryOptions(id, params, options); + + const query = useQuery(queryOptions) as UseQueryResult & { queryKey: QueryKey }; + + query.queryKey = queryOptions.queryKey; + + return query; +}; diff --git a/frontend/src/types/generated/strapi.schemas.ts b/frontend/src/types/generated/strapi.schemas.ts index 6579283b..3bda3a5d 100644 --- a/frontend/src/types/generated/strapi.schemas.ts +++ b/frontend/src/types/generated/strapi.schemas.ts @@ -254,6 +254,56 @@ export type GetMpaaEstablishmentStagesParams = { locale?: string; }; +export type GetMpaProtectionCoverageStatsIdParams = { + /** + * Relations to return + */ + populate?: string; +}; + +export type GetMpaProtectionCoverageStatsParams = { + /** + * Sort by attributes ascending (asc) or descending (desc) + */ + sort?: string; + /** + * Return page/pageSize (default: true) + */ + 'pagination[withCount]'?: boolean; + /** + * Page number (default: 0) + */ + 'pagination[page]'?: number; + /** + * Page size (default: 25) + */ + 'pagination[pageSize]'?: number; + /** + * Offset value (default: 0) + */ + 'pagination[start]'?: number; + /** + * Number of entities to return (default: 25) + */ + 'pagination[limit]'?: number; + /** + * Fields to return (ex: title,author) + */ + fields?: string; + /** + * Relations to return + */ + populate?: string; + /** + * Filters to apply + */ + filters?: { [key: string]: any }; + /** + * Locale to apply + */ + locale?: string; +}; + export type GetMpasIdParams = { /** * Relations to return From 4dce7eaccf5b514f5131e82c6c84984499dfd234 Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Mon, 13 Nov 2023 12:37:52 +0000 Subject: [PATCH 2/9] Add the National/highseas data-tool table; actual data, table columns, sorting and filtering --- .../details/table/sorting-button/index.tsx | 5 +- .../tables/national-highseas/index.tsx | 113 +++++++- .../tables/national-highseas/mocked-data.ts | 22 -- .../tables/national-highseas/useColumns.tsx | 250 ++++++++++++++++++ 4 files changed, 363 insertions(+), 27 deletions(-) delete mode 100644 frontend/src/containers/data-tool/content/details/tables/national-highseas/mocked-data.ts create mode 100644 frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx diff --git a/frontend/src/containers/data-tool/content/details/table/sorting-button/index.tsx b/frontend/src/containers/data-tool/content/details/table/sorting-button/index.tsx index f593088d..38401ca9 100644 --- a/frontend/src/containers/data-tool/content/details/table/sorting-button/index.tsx +++ b/frontend/src/containers/data-tool/content/details/table/sorting-button/index.tsx @@ -3,12 +3,15 @@ import { ArrowDownNarrowWide, ArrowUpNarrowWide, ArrowUpDown } from 'lucide-reac import { Button } from '@/components/ui/button'; import { GlobalRegionalTableColumns } from '@/containers/data-tool/content/details/tables/global-regional/useColumns'; +import { NationalHighseasTableColumns } from '@/containers/data-tool/content/details/tables/national-highseas/useColumns'; const BUTTON_CLASSNAMES = '-ml-4'; const ICON_CLASSNAMES = 'h-4 w-4'; type SortingButtonProps = { - column: Column; + column: + | Column + | Column; }; const SortingButton: React.FC = ({ column }) => { diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx index d1a599e6..130e215b 100644 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx @@ -1,11 +1,116 @@ +import { useMemo, useState } from 'react'; + +import { useAtomValue } from 'jotai'; + import Table from '@/containers/data-tool/content/details/table'; -import columns from '@/containers/data-tool/content/details/tables/national-highseas/columns'; -import mockedData from '@/containers/data-tool/content/details/tables/national-highseas/mocked-data'; +import useColumns from '@/containers/data-tool/content/details/tables/national-highseas/useColumns'; +import { locationAtom } from '@/store/location'; +import { useGetMpaProtectionCoverageStats } from '@/types/generated/mpa-protection-coverage-stat'; +import { MpaProtectionCoverageStatListResponseDataItem } from '@/types/generated/strapi.schemas'; const NationalHighseasTable: React.FC = () => { - const data = mockedData; + const location = useAtomValue(locationAtom); + + const [filters, setFilters] = useState({ + protectedAreaType: ['mpa', 'oecm'], + establishmentStage: [ + 'designated-implemented', + 'designated-unimplemented', + 'proposed-committed', + ], + protectionLevel: ['fully-highly-protected', 'less-protected-unknown'], + fishingProtectionLevel: ['highly', 'moderately', 'less'], + }); + + const handleOnFiltersChange = (field, values) => { + setFilters({ ...filters, [field]: values }); + }; + + const columns = useColumns({ filters, onFiltersChange: handleOnFiltersChange }); + + const { data: coverageData }: { data: MpaProtectionCoverageStatListResponseDataItem[] } = + useGetMpaProtectionCoverageStats( + { + filters: { + location: { + code: { + $eq: location.code, + }, + }, + }, + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + fields: ['area'], + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + populate: { + mpa: { + fields: ['name', 'wdpaid', 'area'], + populate: { + mpaa_establishment_stage: { + fields: ['slug', 'name'], + }, + protection_status: { + fields: ['slug', 'name'], + }, + }, + }, + location: { + fields: ['code', 'total_marine_area'], + }, + fishing_protection_level: { + fields: ['slug', 'name'], + }, + mpaa_protection_level: { + fields: ['slug', 'name'], + }, + }, + }, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const parsedData = useMemo(() => { + return coverageData.map(({ attributes: coverageStats }) => { + const mpa = coverageStats?.mpa?.data?.attributes; + const protectionStatus = mpa?.protection_status?.data?.attributes; + const establishmentStage = mpa?.mpaa_establishment_stage?.data?.attributes; + const mpaaProtectionLevel = coverageStats?.mpaa_protection_level?.data?.attributes; + const fishingProtectionLevel = coverageStats?.fishing_protection_level?.data?.attributes; + const coverageArea = coverageStats?.area; + const location = coverageStats?.location?.data?.attributes; + + // Calculate coverage percentage + const coveragePercentage = (coverageArea / location?.totalMarineArea) * 100; + + return { + protectedArea: mpa.name, + coverage: coveragePercentage, + protectedAreaType: protectionStatus?.slug, + establishmentStage: establishmentStage?.slug, + protectionLevel: mpaaProtectionLevel?.slug, + fishingProtectionLevel: fishingProtectionLevel?.slug, + area: coverageArea, + }; + }); + }, [coverageData]); + + const tableData = useMemo(() => { + const filteredData = parsedData.filter((item) => { + for (const key in filters) { + return filters[key].includes(item[key]); + } + return true; + }); + + return filteredData; + }, [filters, parsedData]); - return ; + return
; }; export default NationalHighseasTable; diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/mocked-data.ts b/frontend/src/containers/data-tool/content/details/tables/national-highseas/mocked-data.ts deleted file mode 100644 index c66ff113..00000000 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/mocked-data.ts +++ /dev/null @@ -1,22 +0,0 @@ -const mockedData = [ - { - protectedArea: 'Protected Area 1', - coverage: 4, - protectedAreaType: 'OECM', - establishmentStage: 'Designated / Implemented', - protectionLevel: 'Fully or highly protected', - fishingProtectionLevel: null, - area: 15335, - }, - { - protectedArea: 'Protected Area 2', - coverage: 3, - protectedAreaType: 'WPA', - establishmentStage: 'Proposed / Committed', - protectionLevel: 'Fully or highly protected', - fishingProtectionLevel: 'Less', - area: 321681, - }, -]; - -export default mockedData; diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx new file mode 100644 index 00000000..ea963c53 --- /dev/null +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx @@ -0,0 +1,250 @@ +import { useMemo } from 'react'; + +import { ColumnDef } from '@tanstack/react-table'; + +import FiltersButton from '@/containers/data-tool/content/details/table/filters-button'; +import HeaderItem from '@/containers/data-tool/content/details/table/header-item'; +import { cellFormatter } from '@/containers/data-tool/content/details/table/helpers'; +import SortingButton from '@/containers/data-tool/content/details/table/sorting-button'; +import { useGetFishingProtectionLevels } from '@/types/generated/fishing-protection-level'; +import { useGetMpaaEstablishmentStages } from '@/types/generated/mpaa-establishment-stage'; +import { useGetMpaaProtectionLevels } from '@/types/generated/mpaa-protection-level'; +import { useGetProtectionStatuses } from '@/types/generated/protection-status'; + +export type NationalHighseasTableColumns = { + protectedArea: string; + coverage: number; + protectedAreaType: string; + establishmentStage: string; + protectionLevel: string; + fishingProtectionLevel: string; + area: number; +}; + +type UseColumnsProps = { + filters: { [key: string]: string[] }; + onFiltersChange: (field: string, values: string[]) => void; +}; + +const useColumns = ({ filters, onFiltersChange }: UseColumnsProps) => { + // Fetch protection statuses and build options for the filter + const { data: protectionStatuses } = useGetProtectionStatuses( + {}, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const protectionStatusOptions = useMemo(() => { + return protectionStatuses.map(({ attributes }) => ({ + name: attributes?.name, + value: attributes?.slug, + })); + }, [protectionStatuses]); + + // Fetch establishment stages and build options for the filter + const { data: establishmentStages } = useGetMpaaEstablishmentStages( + {}, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const establishmentStageOptions = useMemo(() => { + return establishmentStages.map(({ attributes }) => ({ + name: attributes?.name, + value: attributes?.slug, + })); + }, [establishmentStages]); + + // Fetch protection levels and build options for the filter + const { data: protectionLevels } = useGetMpaaProtectionLevels( + {}, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const protectionLevelOptions = useMemo(() => { + return protectionLevels.map(({ attributes }) => ({ + name: attributes?.name, + value: attributes?.slug, + })); + }, [protectionLevels]); + + // Fetch fishing protection levels and build options for the filter + const { data: fishingProtectionLevels } = useGetFishingProtectionLevels( + {}, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const fishingProtectionLevelOptions = useMemo(() => { + return fishingProtectionLevels.map(({ attributes }) => ({ + name: attributes?.name, + value: attributes?.slug, + })); + }, [fishingProtectionLevels]); + + // Define columns + const columns: ColumnDef[] = useMemo(() => { + return [ + { + accessorKey: 'protectedArea', + header: 'Protected Area', + cell: ({ row }) => { + const { protectedArea } = row.original; + return {protectedArea}; + }, + }, + { + accessorKey: 'coverage', + header: ({ column }) => ( + + + Coverage + + ), + cell: ({ row }) => { + const { coverage: value } = row.original; + if (!value) return <>—; + + const formattedCoverage = cellFormatter.percentage(value); + + return ( + + {formattedCoverage} + % + + ); + }, + }, + { + accessorKey: 'protectedAreaType', + header: ({ column }) => ( + + + Protected Area Type + + ), + cell: ({ row }) => { + const { protectedAreaType: value } = row.original; + const formattedValue = protectionStatusOptions.find( + (entry) => value === entry?.value + )?.name; + return <>{formattedValue}; + }, + }, + { + accessorKey: 'establishmentStage', + header: ({ column }) => ( + + + Establishment Stage + + ), + cell: ({ row }) => { + const { establishmentStage: value } = row.original; + const formattedValue = establishmentStageOptions.find( + (entry) => value === entry?.value + )?.name; + return <>{formattedValue}; + }, + }, + { + accessorKey: 'protectionLevel', + header: ({ column }) => ( + + + Protection Level + + ), + cell: ({ row }) => { + const { protectionLevel: value } = row.original; + const formattedValue = protectionLevelOptions.find( + (entry) => value === entry?.value + )?.name; + return <>{formattedValue}; + }, + }, + { + accessorKey: 'fishingProtectionLevel', + header: ({ column }) => ( + + + Fishing Protection Level + + ), + cell: ({ row }) => { + const { fishingProtectionLevel: value } = row.original; + const formattedValue = fishingProtectionLevelOptions.find( + (entry) => value === entry?.value + )?.name; + return <>{formattedValue}; + }, + }, + { + accessorKey: 'area', + header: ({ column }) => ( + + + Area + + ), + cell: ({ row }) => { + const { area: value } = row.original; + const formattedValue = cellFormatter.area(value); + return ( + + {formattedValue} km2 + + ); + }, + }, + ]; + // ! If we add the filters dependency, the columns will re-render and the popovers will close on updates / act funny + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + protectionStatusOptions, + establishmentStageOptions, + protectionLevelOptions, + fishingProtectionLevelOptions, + ]); + + return columns; +}; + +export default useColumns; From 13274744cf621c2cb39fa05b200de2a818c3801a Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Tue, 14 Nov 2023 11:46:19 +0000 Subject: [PATCH 3/9] Fix filtering functions for the data-tool - were not working with multiple filtered columns --- .../data-tool/content/details/tables/global-regional/index.tsx | 2 +- .../content/details/tables/national-highseas/index.tsx | 2 +- .../content/details/tables/national-highseas/useColumns.tsx | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/frontend/src/containers/data-tool/content/details/tables/global-regional/index.tsx b/frontend/src/containers/data-tool/content/details/tables/global-regional/index.tsx index 420adc32..3ce27787 100644 --- a/frontend/src/containers/data-tool/content/details/tables/global-regional/index.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/global-regional/index.tsx @@ -200,7 +200,7 @@ const GlobalRegionalTable: React.FC = () => { const tableData = useMemo(() => { const filteredData = parsedData.filter((item) => { for (const key in filters) { - return filters[key].includes(item[key]); + if (!filters[key].includes(item[key])) return false; } return true; }); diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx index 130e215b..013c8ca1 100644 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx @@ -102,7 +102,7 @@ const NationalHighseasTable: React.FC = () => { const tableData = useMemo(() => { const filteredData = parsedData.filter((item) => { for (const key in filters) { - return filters[key].includes(item[key]); + if (!filters[key].includes(item[key])) return false; } return true; }); diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx index ea963c53..f8dbcd04 100644 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx @@ -99,6 +99,8 @@ const useColumns = ({ filters, onFiltersChange }: UseColumnsProps) => { })); }, [fishingProtectionLevels]); + // TODO DEBUG FILTERS + // Define columns const columns: ColumnDef[] = useMemo(() => { return [ From 06a627d45e067ff6d47c1ea9b2666c7268c82bc4 Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Tue, 14 Nov 2023 12:11:07 +0000 Subject: [PATCH 4/9] Remove unnecessary TODO comment --- .../content/details/tables/national-highseas/useColumns.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx index f8dbcd04..ea963c53 100644 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx @@ -99,8 +99,6 @@ const useColumns = ({ filters, onFiltersChange }: UseColumnsProps) => { })); }, [fishingProtectionLevels]); - // TODO DEBUG FILTERS - // Define columns const columns: ColumnDef[] = useMemo(() => { return [ From 2d6bed1d257d778d3eaaa7d6cac76f51695029da Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Tue, 14 Nov 2023 12:48:48 +0000 Subject: [PATCH 5/9] Remove no longer used national-highseas columns definitions --- .../tables/national-highseas/columns.tsx | 60 ------------------- 1 file changed, 60 deletions(-) delete mode 100644 frontend/src/containers/data-tool/content/details/tables/national-highseas/columns.tsx diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/columns.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/columns.tsx deleted file mode 100644 index 8e87372e..00000000 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/columns.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { format } from 'd3-format'; - -// ! type me -const columns = [ - { - accessorKey: 'protectedArea', - header: 'Protected Area', - cell: ({ row }) => {row.original.protectedArea}, - }, - { - accessorKey: 'coverage', - header: 'Coverage', - cell: ({ row }) => { - const { coverage: value } = row.original; - if (!value) return <>—; - return ( - - {value} - % - - ); - }, - }, - { - accessorKey: 'protectedAreaType', - header: 'Protected Area Type', - }, - { - accessorKey: 'establishmentStage', - header: 'Establishment Stage', - }, - { - accessorKey: 'protectionLevel', - header: 'Protection Level', - }, - { - accessorKey: 'fishingProtectionLevel', - header: 'Fishing Protection Level', - cell: ({ row }) => { - const { fishingProtectionLevel: value } = row.original; - if (!value) return <>No data; - return value; - }, - }, - { - accessorKey: 'area', - header: 'Area', - cell: ({ row }) => { - const { area: value } = row.original; - const formattedValue = format(',.2r')(value); - return ( - - {formattedValue} km2 - - ); - }, - }, -]; - -export default columns; From fc2c69df7f7429a0f90b8fb61e6b63216e66b5a2 Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Tue, 14 Nov 2023 13:25:28 +0000 Subject: [PATCH 6/9] Split fetching data for the national-highseas table's filters into a separate hook for readability --- .../tables/national-highseas/useColumns.tsx | 82 ++--------------- .../national-highseas/useFiltersOptions.ts | 89 +++++++++++++++++++ 2 files changed, 96 insertions(+), 75 deletions(-) create mode 100644 frontend/src/containers/data-tool/content/details/tables/national-highseas/useFiltersOptions.ts diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx index ea963c53..21e7e6a4 100644 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useColumns.tsx @@ -6,10 +6,7 @@ import FiltersButton from '@/containers/data-tool/content/details/table/filters- import HeaderItem from '@/containers/data-tool/content/details/table/header-item'; import { cellFormatter } from '@/containers/data-tool/content/details/table/helpers'; import SortingButton from '@/containers/data-tool/content/details/table/sorting-button'; -import { useGetFishingProtectionLevels } from '@/types/generated/fishing-protection-level'; -import { useGetMpaaEstablishmentStages } from '@/types/generated/mpaa-establishment-stage'; -import { useGetMpaaProtectionLevels } from '@/types/generated/mpaa-protection-level'; -import { useGetProtectionStatuses } from '@/types/generated/protection-status'; +import useFiltersOptions from '@/containers/data-tool/content/details/tables/national-highseas/useFiltersOptions'; export type NationalHighseasTableColumns = { protectedArea: string; @@ -27,77 +24,12 @@ type UseColumnsProps = { }; const useColumns = ({ filters, onFiltersChange }: UseColumnsProps) => { - // Fetch protection statuses and build options for the filter - const { data: protectionStatuses } = useGetProtectionStatuses( - {}, - { - query: { - select: ({ data }) => data, - placeholderData: { data: [] }, - }, - } - ); - - const protectionStatusOptions = useMemo(() => { - return protectionStatuses.map(({ attributes }) => ({ - name: attributes?.name, - value: attributes?.slug, - })); - }, [protectionStatuses]); - - // Fetch establishment stages and build options for the filter - const { data: establishmentStages } = useGetMpaaEstablishmentStages( - {}, - { - query: { - select: ({ data }) => data, - placeholderData: { data: [] }, - }, - } - ); - - const establishmentStageOptions = useMemo(() => { - return establishmentStages.map(({ attributes }) => ({ - name: attributes?.name, - value: attributes?.slug, - })); - }, [establishmentStages]); - - // Fetch protection levels and build options for the filter - const { data: protectionLevels } = useGetMpaaProtectionLevels( - {}, - { - query: { - select: ({ data }) => data, - placeholderData: { data: [] }, - }, - } - ); - - const protectionLevelOptions = useMemo(() => { - return protectionLevels.map(({ attributes }) => ({ - name: attributes?.name, - value: attributes?.slug, - })); - }, [protectionLevels]); - - // Fetch fishing protection levels and build options for the filter - const { data: fishingProtectionLevels } = useGetFishingProtectionLevels( - {}, - { - query: { - select: ({ data }) => data, - placeholderData: { data: [] }, - }, - } - ); - - const fishingProtectionLevelOptions = useMemo(() => { - return fishingProtectionLevels.map(({ attributes }) => ({ - name: attributes?.name, - value: attributes?.slug, - })); - }, [fishingProtectionLevels]); + const { + protectionStatus: protectionStatusOptions, + establishmentStage: establishmentStageOptions, + protectionLevel: protectionLevelOptions, + fishingProtectionLevel: fishingProtectionLevelOptions, + } = useFiltersOptions(); // Define columns const columns: ColumnDef[] = useMemo(() => { diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/useFiltersOptions.ts b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useFiltersOptions.ts new file mode 100644 index 00000000..fbcb7b0e --- /dev/null +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/useFiltersOptions.ts @@ -0,0 +1,89 @@ +import { useMemo } from 'react'; + +import { useGetFishingProtectionLevels } from '@/types/generated/fishing-protection-level'; +import { useGetMpaaEstablishmentStages } from '@/types/generated/mpaa-establishment-stage'; +import { useGetMpaaProtectionLevels } from '@/types/generated/mpaa-protection-level'; +import { useGetProtectionStatuses } from '@/types/generated/protection-status'; + +const useFiltersOptions = () => { + // Fetch protection statuses and build options for the filter + const { data: protectionStatuses } = useGetProtectionStatuses( + {}, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const protectionStatusOptions = useMemo(() => { + return protectionStatuses.map(({ attributes }) => ({ + name: attributes?.name, + value: attributes?.slug, + })); + }, [protectionStatuses]); + + // Fetch establishment stages and build options for the filter + const { data: establishmentStages } = useGetMpaaEstablishmentStages( + {}, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const establishmentStageOptions = useMemo(() => { + return establishmentStages.map(({ attributes }) => ({ + name: attributes?.name, + value: attributes?.slug, + })); + }, [establishmentStages]); + + // Fetch protection levels and build options for the filter + const { data: protectionLevels } = useGetMpaaProtectionLevels( + {}, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const protectionLevelOptions = useMemo(() => { + return protectionLevels.map(({ attributes }) => ({ + name: attributes?.name, + value: attributes?.slug, + })); + }, [protectionLevels]); + + // Fetch fishing protection levels and build options for the filter + const { data: fishingProtectionLevels } = useGetFishingProtectionLevels( + {}, + { + query: { + select: ({ data }) => data, + placeholderData: { data: [] }, + }, + } + ); + + const fishingProtectionLevelOptions = useMemo(() => { + return fishingProtectionLevels.map(({ attributes }) => ({ + name: attributes?.name, + value: attributes?.slug, + })); + }, [fishingProtectionLevels]); + + return { + protectionStatus: protectionStatusOptions, + establishmentStage: establishmentStageOptions, + protectionLevel: protectionLevelOptions, + fishingProtectionLevel: fishingProtectionLevelOptions, + }; +}; + +export default useFiltersOptions; From 87b3829a5b0f901a16b843f72a11fa3c5178ec03 Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Tue, 14 Nov 2023 13:32:53 +0000 Subject: [PATCH 7/9] Add note for improvements on data-tables --- .../data-tool/content/details/tables/national-highseas/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx index 013c8ca1..a3b3756c 100644 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx @@ -12,6 +12,7 @@ const NationalHighseasTable: React.FC = () => { const location = useAtomValue(locationAtom); const [filters, setFilters] = useState({ + // ! This shouldn't be hardcoded. The setup needs to be able to work the same without any default filters here. protectedAreaType: ['mpa', 'oecm'], establishmentStage: [ 'designated-implemented', From 9d021a21ae5149a838827a9df71187f811dc7178 Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Tue, 14 Nov 2023 13:41:05 +0000 Subject: [PATCH 8/9] Refactor global/regional data-tool tables filtering to be consistent with the national/highseas ones --- .../details/tables/global-regional/useColumns.tsx | 6 ++++-- .../details/tables/global-regional/useFiltersOptions.ts | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 frontend/src/containers/data-tool/content/details/tables/global-regional/useFiltersOptions.ts diff --git a/frontend/src/containers/data-tool/content/details/tables/global-regional/useColumns.tsx b/frontend/src/containers/data-tool/content/details/tables/global-regional/useColumns.tsx index f0b3a404..78ae00cc 100644 --- a/frontend/src/containers/data-tool/content/details/tables/global-regional/useColumns.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/global-regional/useColumns.tsx @@ -2,11 +2,11 @@ import { useMemo } from 'react'; import { ColumnDef } from '@tanstack/react-table'; -import { LOCATION_TYPES_FILTER_OPTIONS } from '@/containers/data-tool/constants'; import FiltersButton from '@/containers/data-tool/content/details/table/filters-button'; import HeaderItem from '@/containers/data-tool/content/details/table/header-item'; import { cellFormatter } from '@/containers/data-tool/content/details/table/helpers'; import SortingButton from '@/containers/data-tool/content/details/table/sorting-button'; +import useFiltersOptions from '@/containers/data-tool/content/details/tables/global-regional/useFiltersOptions'; export type GlobalRegionalTableColumns = { location: string; @@ -26,6 +26,8 @@ type UseColumnsProps = { }; const useColumns = ({ filters, onFiltersChange }: UseColumnsProps) => { + const { locationTypes: locationTypesOptions } = useFiltersOptions(); + const columns: ColumnDef[] = useMemo(() => { return [ { @@ -64,7 +66,7 @@ const useColumns = ({ filters, onFiltersChange }: UseColumnsProps) => { diff --git a/frontend/src/containers/data-tool/content/details/tables/global-regional/useFiltersOptions.ts b/frontend/src/containers/data-tool/content/details/tables/global-regional/useFiltersOptions.ts new file mode 100644 index 00000000..ec8b7e3d --- /dev/null +++ b/frontend/src/containers/data-tool/content/details/tables/global-regional/useFiltersOptions.ts @@ -0,0 +1,9 @@ +import { LOCATION_TYPES_FILTER_OPTIONS } from '@/containers/data-tool/constants'; + +const useFiltersOptions = () => { + return { + locationTypes: LOCATION_TYPES_FILTER_OPTIONS, + }; +}; + +export default useFiltersOptions; From 9fc3d7101839be4f704d8182f81952b0a47bab5f Mon Sep 17 00:00:00 2001 From: Simao Rodrigues Date: Tue, 14 Nov 2023 13:59:23 +0000 Subject: [PATCH 9/9] Extract data tables filtering function into a helper file for reusability purposes --- .../containers/data-tool/content/details/helpers.ts | 9 +++++++++ .../content/details/tables/global-regional/index.tsx | 10 +++------- .../content/details/tables/national-highseas/index.tsx | 10 ++-------- 3 files changed, 14 insertions(+), 15 deletions(-) create mode 100644 frontend/src/containers/data-tool/content/details/helpers.ts diff --git a/frontend/src/containers/data-tool/content/details/helpers.ts b/frontend/src/containers/data-tool/content/details/helpers.ts new file mode 100644 index 00000000..f892d33c --- /dev/null +++ b/frontend/src/containers/data-tool/content/details/helpers.ts @@ -0,0 +1,9 @@ +export const applyFilters = (data, filters) => { + const filteredData = data.filter((item) => { + for (const key in filters) { + if (!filters[key].includes(item[key])) return false; + } + return true; + }); + return filteredData; +}; diff --git a/frontend/src/containers/data-tool/content/details/tables/global-regional/index.tsx b/frontend/src/containers/data-tool/content/details/tables/global-regional/index.tsx index 3ce27787..d016caca 100644 --- a/frontend/src/containers/data-tool/content/details/tables/global-regional/index.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/global-regional/index.tsx @@ -8,6 +8,8 @@ import { locationAtom } from '@/store/location'; import { useGetLocations } from '@/types/generated/location'; import type { LocationListResponseDataItem } from '@/types/generated/strapi.schemas'; +import { applyFilters } from '@/containers/data-tool/content/details/helpers'; + const DATA_YEAR = 2023; const GlobalRegionalTable: React.FC = () => { @@ -198,13 +200,7 @@ const GlobalRegionalTable: React.FC = () => { }, [globalStats, locationsData]); const tableData = useMemo(() => { - const filteredData = parsedData.filter((item) => { - for (const key in filters) { - if (!filters[key].includes(item[key])) return false; - } - return true; - }); - return filteredData; + return applyFilters(parsedData, filters); }, [filters, parsedData]); return
; diff --git a/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx b/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx index a3b3756c..e4a4d93d 100644 --- a/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx +++ b/frontend/src/containers/data-tool/content/details/tables/national-highseas/index.tsx @@ -7,6 +7,7 @@ import useColumns from '@/containers/data-tool/content/details/tables/national-h import { locationAtom } from '@/store/location'; import { useGetMpaProtectionCoverageStats } from '@/types/generated/mpa-protection-coverage-stat'; import { MpaProtectionCoverageStatListResponseDataItem } from '@/types/generated/strapi.schemas'; +import { applyFilters } from '@/containers/data-tool/content/details/helpers'; const NationalHighseasTable: React.FC = () => { const location = useAtomValue(locationAtom); @@ -101,14 +102,7 @@ const NationalHighseasTable: React.FC = () => { }, [coverageData]); const tableData = useMemo(() => { - const filteredData = parsedData.filter((item) => { - for (const key in filters) { - if (!filters[key].includes(item[key])) return false; - } - return true; - }); - - return filteredData; + return applyFilters(parsedData, filters); }, [filters, parsedData]); return
;