From 278b1afb3b0fd62ceb8c484ff44ac42adbffd4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Gonz=C3=A1lez=20Mu=C3=B1oz?= Date: Wed, 8 Nov 2023 18:34:55 +0100 Subject: [PATCH] fixes many crashes due to disabling pagination --- app/hooks/features/index.ts | 155 +++++------- app/hooks/gap-analysis/index.ts | 234 ++++++------------ app/hooks/projects/index.ts | 146 +++++------ app/hooks/published-projects/index.ts | 96 ++----- app/hooks/scenarios/index.ts | 158 ++++-------- app/hooks/solutions/index.ts | 65 ++--- .../list/table/item/component.tsx | 2 +- .../features/add/add-modal/list/component.tsx | 37 +-- .../features/add/intersect/list/component.tsx | 21 -- .../grid-setup/gap-analysis/list/index.tsx | 25 +- .../overview/table/table-form/component.tsx | 15 +- .../target-achievement/list/index.tsx | 21 -- .../projects/show/scenarios/component.tsx | 23 +- 13 files changed, 302 insertions(+), 696 deletions(-) diff --git a/app/hooks/features/index.ts b/app/hooks/features/index.ts index e93a7c13e5..6f8232d9b6 100644 --- a/app/hooks/features/index.ts +++ b/app/hooks/features/index.ts @@ -1,12 +1,6 @@ import { useMemo } from 'react'; -import { - useQuery, - useInfiniteQuery, - useMutation, - useQueryClient, - QueryObserverOptions, -} from 'react-query'; +import { useQuery, useMutation, useQueryClient, QueryObserverOptions } from 'react-query'; import { AxiosRequestConfig } from 'axios'; import Fuse from 'fuse.js'; @@ -48,7 +42,7 @@ export function useAllPaginatedFeatures(projectId, options: UseFeaturesOptionsPr }; }, {}); - const fetchFeatures = ({ pageParam = 1 }) => + const fetchFeatures = () => PROJECTS.request({ method: 'GET', url: `/${projectId}/features`, @@ -56,8 +50,6 @@ export function useAllPaginatedFeatures(projectId, options: UseFeaturesOptionsPr Authorization: `Bearer ${session.accessToken}`, }, params: { - 'page[number]': pageParam, - // omitFields: 'properties', ...parsedFilters, ...(search && { q: search, @@ -65,102 +57,71 @@ export function useAllPaginatedFeatures(projectId, options: UseFeaturesOptionsPr ...(sort && { sort, }), + disablePagination: true, }, - }); + }).then((response) => response.data); - const query = useInfiniteQuery( - ['all-paginated-features', projectId, JSON.stringify(options)], - fetchFeatures, - { - keepPreviousData: true, - getNextPageParam: (lastPage) => { + return useQuery(['all-paginated-features', projectId, JSON.stringify(options)], fetchFeatures, { + keepPreviousData: true, + select: ({ data }) => { + const parsedData = data.map((d): AllItemProps => { const { - data: { meta }, - } = lastPage; - const { page, totalPages } = meta; - - const nextPage = page + 1 > totalPages ? null : page + 1; - return nextPage; - }, - } - ); - - const { data } = query; - const { pages } = data || {}; + id, + alias, + featureClassName, + description, + properties = {}, + splitSelected, + splitFeaturesSelected, + } = d; - return useMemo(() => { - const parsedData = Array.isArray(pages) - ? flatten( - pages.map((p) => { - const { - data: { data: pageData }, - } = p; - - return pageData.map((d): AllItemProps => { - const { - id, - alias, - featureClassName, - description, - properties = {}, - splitSelected, - splitFeaturesSelected, - } = d; - - let splitOptions = []; - let splitFeaturesOptions = []; - - /** - * @todo Checking whether `properties` is defined here is just a - * workaround to avoid an error when processing `bioregional` - * features, which would prevent progressing through the stop of - * configuring features for a scenario until this code is reviewed. - * Without much knowledge of the flow for feature data, I see that - * short-circuiting the `map()` below and therefore setting - * `splitOptions = []` still results in properties being shown in the - * dropdowns used for splitting features, but since `properties` is - * always undefined (from what I can see), we may need to adapt the - * API payload or how we process it here. - */ - splitOptions = properties - ? Object.keys(properties).map((k) => { - return { - key: k, - label: k, - values: properties[k].map((v) => ({ id: v, name: v })), - }; - }) - : []; - - splitFeaturesOptions = splitSelected - ? splitOptions - .find((s) => s.key === splitSelected) - .values.map((v) => ({ label: v.name, value: v.id })) - : []; + let splitOptions = []; + let splitFeaturesOptions = []; + /** + * @todo Checking whether `properties` is defined here is just a + * workaround to avoid an error when processing `bioregional` + * features, which would prevent progressing through the stop of + * configuring features for a scenario until this code is reviewed. + * Without much knowledge of the flow for feature data, I see that + * short-circuiting the `map()` below and therefore setting + * `splitOptions = []` still results in properties being shown in the + * dropdowns used for splitting features, but since `properties` is + * always undefined (from what I can see), we may need to adapt the + * API payload or how we process it here. + */ + splitOptions = properties + ? Object.keys(properties).map((k) => { return { - id, - name: alias || featureClassName, - description, - - splitSelected, - splitOptions, - splitFeaturesSelected, - splitFeaturesOptions, + key: k, + label: k, + values: properties[k].map((v) => ({ id: v, name: v })), }; - }); - }) - ) - : []; + }) + : []; - // We want to return custom features first, but preserve the overall sorting - const sortedByCustomFeature = flatten(partition(parsedData, (feature) => feature.isCustom)); + splitFeaturesOptions = splitSelected + ? splitOptions + .find((s) => s.key === splitSelected) + .values.map((v) => ({ label: v.name, value: v.id })) + : []; - return { - ...query, - data: sortedByCustomFeature, - }; - }, [query, pages]); + return { + id, + name: alias || featureClassName, + description, + + splitSelected, + splitOptions, + splitFeaturesSelected, + splitFeaturesOptions, + }; + }); + + // We want to return custom features first, but preserve the overall sorting + return flatten(partition(parsedData, (feature) => feature.isCustom)); + }, + }); } export function useAllFeatures( diff --git a/app/hooks/gap-analysis/index.ts b/app/hooks/gap-analysis/index.ts index 22c28c0783..a032992411 100644 --- a/app/hooks/gap-analysis/index.ts +++ b/app/hooks/gap-analysis/index.ts @@ -1,10 +1,7 @@ -import { useMemo, useRef } from 'react'; - -import { useInfiniteQuery, useQuery } from 'react-query'; +import { useQuery } from 'react-query'; import { format } from 'd3'; import { sortBy } from 'lodash'; -import flatten from 'lodash/flatten'; import { useSession } from 'next-auth/react'; import { ItemProps as RawItemProps } from 'components/gap-analysis/item/component'; @@ -41,11 +38,7 @@ export function useAllGapAnalysis(sId: Scenario['id'], queryOptions) { ); } -export function usePreGapAnalysis(sId, options: UseFeaturesOptionsProps = {}) { - const placeholderDataRef = useRef({ - pages: [], - pageParams: [], - }); +export function usePreGapAnalysis(sId: Scenario['id'], options: UseFeaturesOptionsProps = {}) { const { data: session } = useSession(); const { filters = {}, search, sort } = options; @@ -57,7 +50,7 @@ export function usePreGapAnalysis(sId, options: UseFeaturesOptionsProps = {}) { }; }, {}); - const fetchFeatures = ({ pageParam = 1 }) => + const fetchFeatures = () => SCENARIOS.request({ method: 'GET', url: `/${sId}/features/gap-data`, @@ -74,87 +67,45 @@ export function usePreGapAnalysis(sId, options: UseFeaturesOptionsProps = {}) { }), disablePagination: true, }, - }); - - const query = useInfiniteQuery( - ['pre-gap-analysis', sId, JSON.stringify(options)], - fetchFeatures, - { - placeholderData: placeholderDataRef.current, - getNextPageParam: (lastPage) => { - const { - data: { meta }, - } = lastPage; - const { page, totalPages } = meta; - - const nextPage = page + 1 > totalPages ? null : page + 1; - return nextPage; - }, - } - ); - - const { data } = query; - const { pages } = data || {}; - - if (data) { - placeholderDataRef.current = data; - } - - return useMemo(() => { - const parsedData = Array.isArray(pages) - ? flatten( - pages.map((p) => { - const { - data: { data: pageData }, - } = p; - - return sortBy( - pageData.map((d): AllItemProps => { - const { - id, - name, - featureClassName, - met, - metArea, - coverageTarget, - coverageTargetArea, - onTarget, - } = d; - - return { - id, - name: name || featureClassName || 'Metadata name', - onTarget, - current: { - percent: met / 100, - value: format('.3s')(metArea / 1000000), - unit: 'km2', - }, - target: { - percent: coverageTarget / 100, - value: format('.3s')(coverageTargetArea / 1000000), - unit: 'km2', - }, - }; - }), - ['name'] - ); - }) - ) - : []; - - return { - ...query, - data: parsedData, - }; - }, [query, pages]); + }).then((response) => response.data); + + return useQuery(['pre-gap-analysis', sId, JSON.stringify(options)], fetchFeatures, { + select: ({ data }) => + sortBy( + data.map((d): AllItemProps => { + const { + id, + name, + featureClassName, + met, + metArea, + coverageTarget, + coverageTargetArea, + onTarget, + } = d; + + return { + id, + name: name || featureClassName || 'Metadata name', + onTarget, + current: { + percent: met / 100, + value: format('.3s')(metArea / 1000000), + unit: 'km2', + }, + target: { + percent: coverageTarget / 100, + value: format('.3s')(coverageTargetArea / 1000000), + unit: 'km2', + }, + }; + }), + ['name'] + ), + }); } export function usePostGapAnalysis(sId, options: UseFeaturesOptionsProps = {}) { - const placeholderDataRef = useRef({ - pages: [], - pageParams: [], - }); const { data: session } = useSession(); const { filters = {}, search, sort } = options; @@ -166,7 +117,7 @@ export function usePostGapAnalysis(sId, options: UseFeaturesOptionsProps = {}) { }; }, {}); - const fetchFeatures = ({ pageParam = 1 }) => + const fetchFeatures = () => SCENARIOS.request({ method: 'GET', url: `/${sId}/marxan/solutions/gap-data`, @@ -174,7 +125,6 @@ export function usePostGapAnalysis(sId, options: UseFeaturesOptionsProps = {}) { Authorization: `Bearer ${session.accessToken}`, }, params: { - 'page[number]': pageParam, ...parsedFilters, ...(search && { q: search, @@ -182,77 +132,41 @@ export function usePostGapAnalysis(sId, options: UseFeaturesOptionsProps = {}) { ...(sort && { sort, }), + disablePagination: true, }, - }); + }).then((response) => response.data); - const query = useInfiniteQuery( - ['post-gap-analysis', sId, JSON.stringify(options)], - fetchFeatures, - { - enabled: !!filters.runId, - placeholderData: placeholderDataRef.current, - getNextPageParam: (lastPage) => { + return useQuery(['post-gap-analysis', sId, JSON.stringify(options)], fetchFeatures, { + enabled: !!filters.runId, + placeholderData: { data: [] }, + select: ({ data }) => + data.map((d): AllItemProps => { const { - data: { meta }, - } = lastPage; - const { page, totalPages } = meta; - - const nextPage = page + 1 > totalPages ? null : page + 1; - return nextPage; - }, - } - ); - - const { data } = query; - const { pages } = data || {}; - - if (data) { - placeholderDataRef.current = data; - } - - return useMemo(() => { - const parsedData = Array.isArray(pages) - ? flatten( - pages.map((p) => { - const { - data: { data: pageData }, - } = p; - - return pageData.map((d): AllItemProps => { - const { - id, - name, - met, - featureClassName, - metArea, - coverageTarget, - coverageTargetArea, - onTarget, - } = d; - - return { - id, - name: name || featureClassName || 'Metadata name', - onTarget, - current: { - percent: met / 100, - value: format('.3s')(metArea / 1000000), - unit: 'km2', - }, - target: { - percent: coverageTarget / 100, - value: format('.3s')(coverageTargetArea / 1000000), - unit: 'km2', - }, - }; - }); - }) - ) - : []; - - return { - ...query, - data: parsedData, - }; - }, [query, pages]); + id, + name, + met, + featureClassName, + metArea, + coverageTarget, + coverageTargetArea, + onTarget, + } = d; + + return { + id, + name: name || featureClassName || 'Metadata name', + onTarget, + current: { + percent: met / 100, + value: format('.3s')(metArea / 1000000), + unit: 'km2', + }, + target: { + percent: coverageTarget / 100, + value: format('.3s')(coverageTargetArea / 1000000), + unit: 'km2', + }, + }; + }), + }); } diff --git a/app/hooks/projects/index.ts b/app/hooks/projects/index.ts index 34530b0f25..30038dea1a 100644 --- a/app/hooks/projects/index.ts +++ b/app/hooks/projects/index.ts @@ -1,13 +1,12 @@ import { useMemo } from 'react'; -import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from 'react-query'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; import { useRouter } from 'next/router'; import { AxiosRequestConfig } from 'axios'; import axios from 'axios'; import { formatDistance } from 'date-fns'; -import flatten from 'lodash/flatten'; import orderBy from 'lodash/orderBy'; import { useSession } from 'next-auth/react'; @@ -69,7 +68,7 @@ export function useProjects(options: UseProjectsOptionsProps) { }; }, {}); - const fetchProjects = ({ pageParam = 1 }) => + const fetchProjects = () => PROJECTS.request({ method: 'GET', url: '/', @@ -77,7 +76,6 @@ export function useProjects(options: UseProjectsOptionsProps) { Authorization: `Bearer ${session.accessToken}`, }, params: { - 'page[number]': pageParam, include: 'scenarios,users', ...parsedFilters, ...(search && { @@ -86,96 +84,68 @@ export function useProjects(options: UseProjectsOptionsProps) { ...(sort && { sort, }), + disablePagination: true, }, - }); + }).then((response) => response.data); - const query = useInfiniteQuery(['projects', JSON.stringify(options)], fetchProjects, { + return useQuery(['projects', JSON.stringify(options)], fetchProjects, { retry: false, keepPreviousData: true, - getNextPageParam: (lastPage) => { - const { - data: { meta }, - } = lastPage; - const { page, totalPages } = meta; - - const nextPage = page + 1 > totalPages ? null : page + 1; - return nextPage; + placeholderData: { data: [] }, + select: ({ data }) => { + const parsedData = data.map((d): ItemProps => { + const { + id, + name, + description, + lastModifiedAt, + scenarios, + planningAreaName, + isPublic, + publicMetadata, + } = d; + + const lastUpdate = scenarios.reduce((acc, s) => { + const { lastModifiedAt: slastModifiedAt } = s; + + return slastModifiedAt > acc ? slastModifiedAt : acc; + }, lastModifiedAt); + + const lastUpdateDistance = () => { + return formatDistance(new Date(lastUpdate || null), new Date(), { + addSuffix: true, + }); + }; + + return { + id, + area: planningAreaName || 'Custom area name', + name, + description, + lastUpdate, + lastUpdateDistance: lastUpdateDistance(), + contributors: [], + isPublic, + scenarios, + underModeration: publicMetadata?.underModeration, + onClick: () => { + push(`/projects/${id}`); + }, + onDownload: (e) => { + console.info('onDownload', e); + }, + onDuplicate: (e) => { + console.info('onDuplicate', e); + }, + onDelete: (e) => { + console.info('onDelete', e); + }, + }; + }); + + return orderBy(parsedData, ['lastUpdate'], ['desc']); }, }); - - const { data } = query; - const { pages } = data || {}; - - return useMemo(() => { - let parsedData = Array.isArray(pages) - ? flatten( - pages.map((p) => { - const { - data: { data: pageData }, - } = p; - - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return pageData.map((d): ItemProps => { - const { - id, - name, - description, - lastModifiedAt, - scenarios, - planningAreaName, - isPublic, - publicMetadata, - } = d; - - const lastUpdate = scenarios.reduce((acc, s) => { - const { lastModifiedAt: slastModifiedAt } = s; - - return slastModifiedAt > acc ? slastModifiedAt : acc; - }, lastModifiedAt); - - const lastUpdateDistance = () => { - return formatDistance(new Date(lastUpdate || null), new Date(), { - addSuffix: true, - }); - }; - - return { - id, - area: planningAreaName || 'Custom area name', - name, - description, - lastUpdate, - lastUpdateDistance: lastUpdateDistance(), - contributors: [], - isPublic, - scenarios, - underModeration: publicMetadata?.underModeration, - onClick: () => { - push(`/projects/${id}`); - }, - onDownload: (e) => { - console.info('onDownload', e); - }, - onDuplicate: (e) => { - console.info('onDuplicate', e); - }, - onDelete: (e) => { - console.info('onDelete', e); - }, - }; - }); - }) - ) - : []; - - // Sort - parsedData = orderBy(parsedData, ['lastUpdate'], ['desc']); - - return { - ...query, - data: parsedData, - }; - }, [query, pages, push]); } export function useProject(id: Project['id']) { diff --git a/app/hooks/published-projects/index.ts b/app/hooks/published-projects/index.ts index 4c0cc6a1bf..d360d79c21 100644 --- a/app/hooks/published-projects/index.ts +++ b/app/hooks/published-projects/index.ts @@ -1,8 +1,7 @@ import { useMemo } from 'react'; -import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from 'react-query'; +import { useMutation, useQuery, useQueryClient } from 'react-query'; -import flatten from 'lodash/flatten'; import { useSession } from 'next-auth/react'; import { PublishedItemProps } from 'layout/community/published-projects/list/table/item/component'; @@ -26,82 +25,41 @@ export function usePublishedProjects(options: UsePublishedProjectsProps = {}) { }; }, {}); - const fetchPublishedProjects = ({ pageParam = 1 }) => + const fetchPublishedProjects = () => PUBLISHED_PROJECTS.request({ method: 'GET', url: '/', params: { - 'page[number]': pageParam, ...parsedFilters, ...(search && { q: search, }), }, - }); - - const query = useInfiniteQuery( - ['published-projects', JSON.stringify(options)], - fetchPublishedProjects, - { - retry: false, - keepPreviousData: true, - getNextPageParam: (lastPage) => { - const { - data: { meta }, - } = lastPage; - const { page, totalPages } = meta; - - const nextPage = page + 1 > totalPages ? null : page + 1; - return nextPage; - }, - } - ); - - const { data } = query; - const { pages } = data || {}; - - return useMemo(() => { - const parsedData = Array.isArray(pages) - ? flatten( - pages.map((p) => { - const { - data: { data: pageData }, - } = p; - - return pageData.map((d): PublishedItemProps => { - const { - id, - name, - description, - location, - creators, - resources, - company, - pngData, - exportId, - } = d; - - return { - id, - name, - description, - location, - creators, - resources, - company, - pngData, - exportId, - }; - }); - }) - ) - : []; - - return { - ...query, - data: parsedData, - }; - }, [query, pages]); + }).then((response) => response.data); + + return useQuery(['published-projects', JSON.stringify(options)], fetchPublishedProjects, { + retry: false, + keepPreviousData: true, + placeholderData: { data: [] }, + + select: ({ data }) => + data.map((d): PublishedItemProps => { + const { id, name, description, location, creators, resources, company, pngData, exportId } = + d; + + return { + id, + name, + description, + location, + creators, + resources, + company, + pngData, + exportId, + }; + }), + }); } export function usePublishedProject(id) { diff --git a/app/hooks/scenarios/index.ts b/app/hooks/scenarios/index.ts index 8ed08797eb..db4140bc5c 100644 --- a/app/hooks/scenarios/index.ts +++ b/app/hooks/scenarios/index.ts @@ -1,19 +1,11 @@ import { useMemo } from 'react'; -import { - useQuery, - useInfiniteQuery, - useMutation, - useQueryClient, - useQueries, - UseQueryOptions, -} from 'react-query'; +import { useQuery, useMutation, useQueryClient, useQueries, UseQueryOptions } from 'react-query'; import { useRouter } from 'next/router'; import axios, { AxiosError, AxiosRequestConfig } from 'axios'; import { formatDistanceToNow } from 'date-fns'; -import flatten from 'lodash/flatten'; import { useSession } from 'next-auth/react'; import { useMe } from 'hooks/me'; @@ -368,22 +360,7 @@ export function useScenarios(pId, options: UseScenariosOptionsProps = {}) { }), disablePagination: true, }, - }); - - const query = useInfiniteQuery(['scenarios', pId, JSON.stringify(options)], fetchScenarios, { - retry: false, - keepPreviousData: true, - refetchOnWindowFocus: true, - getNextPageParam: (lastPage) => { - const { - data: { meta }, - } = lastPage; - const { page, totalPages } = meta; - - const nextPage = page + 1 > totalPages ? null : page + 1; - return nextPage; - }, - }); + }).then((response) => response.data); const { data: user } = useMe(); @@ -393,86 +370,61 @@ export function useScenarios(pId, options: UseScenariosOptionsProps = {}) { const { data: scenariosLocksData = [] } = useProjectScenariosLocks(pId); const { data: projectUsersData = [] } = useProjectUsers(pId); - const { data } = query; - const { pages } = data || {}; - - return useMemo(() => { - const parsedData = Array.isArray(pages) - ? flatten( - pages.map((p) => { - const { - data: { data: pageData }, - } = p; - - return pageData.map((d): ItemProps => { - const { id, projectId, name, lastModifiedAt, status, ranAtLeastOnce, numberOfRuns } = - d; - - const jobs = statusScenarios.find((s) => s.id === id)?.jobs || []; - const runStatus = - status || jobs.find((job) => job.kind === 'run')?.status || 'created'; - - let lock = scenariosLocksData.find( - (sl) => sl.scenarioId === id && sl.userId !== user?.id - ); - if (lock) { - const userLock = projectUsersData.find((pu) => pu?.user?.id === lock.userId); - - lock = { - ...lock, - ...userLock?.user, - roleName: userLock?.roleName, - }; - } - - const lastUpdateDistance = () => { - return formatDistanceToNow(new Date(lastModifiedAt), { addSuffix: true }); - }; - - return { - id, - name, - lastUpdate: lastModifiedAt, - lastUpdateDistance: lastUpdateDistance(), - warnings: false, - runStatus, - jobs, - lock, - ranAtLeastOnce, - numberOfRuns, - onEdit: () => { - push(`/projects/${projectId}/scenarios/${id}/edit?tab=protected-areas`); - }, - }; - }); - }) - ) - : []; + return useQuery(['scenarios', pId, JSON.stringify(options)], fetchScenarios, { + retry: false, + keepPreviousData: true, + refetchOnWindowFocus: true, + placeholderData: { data: [] }, + select: (data) => { + const parsedData = data?.data.map((d): ItemProps => { + const { id, projectId, name, lastModifiedAt, status, ranAtLeastOnce, numberOfRuns } = d; + + const jobs = statusScenarios.find((s) => s.id === id)?.jobs || []; + const runStatus = status || jobs.find((job) => job.kind === 'run')?.status || 'created'; + + let lock = scenariosLocksData.find((sl) => sl.scenarioId === id && sl.userId !== user?.id); + if (lock) { + const userLock = projectUsersData.find((pu) => pu?.user?.id === lock.userId); + + lock = { + ...lock, + ...userLock?.user, + roleName: userLock?.roleName, + }; + } - // Backend can't deal with the `status` filter just yet, so we'll just manually - // filter it out manually in the frontend. It'll allow us to make the feature work - // in the frontend for now, albeit in a non-ideal way. - const filteredData = parsedData.filter((parsedDataItem) => { - const statusFiltersArr = (filters?.status as Array) || []; - // No filters to apply, return everything - if (!statusFiltersArr.length) return true; - return statusFiltersArr.includes(parsedDataItem.runStatus); - }); + const lastUpdateDistance = () => { + return formatDistanceToNow(new Date(lastModifiedAt), { addSuffix: true }); + }; - return { - ...query, - data: filteredData, - }; - }, [ - query, - pages, - filters, - push, - user?.id, - statusScenarios, - scenariosLocksData, - projectUsersData, - ]); + return { + id, + name, + lastUpdate: lastModifiedAt, + lastUpdateDistance: lastUpdateDistance(), + warnings: false, + runStatus, + jobs, + lock, + ranAtLeastOnce, + numberOfRuns, + onEdit: () => { + push(`/projects/${projectId}/scenarios/${id}/edit?tab=protected-areas`); + }, + }; + }); + + // Backend can't deal with the `status` filter just yet, so we'll just manually + // filter it out manually in the frontend. It'll allow us to make the feature work + // in the frontend for now, albeit in a non-ideal way. + return parsedData.filter((parsedDataItem) => { + const statusFiltersArr = (filters?.status as Array) || []; + // No filters to apply, return everything + if (!statusFiltersArr.length) return true; + return statusFiltersArr.includes(parsedDataItem.runStatus); + }); + }, + }); } export function useScenario(id: Scenario['id'], params = {}) { diff --git a/app/hooks/solutions/index.ts b/app/hooks/solutions/index.ts index 66bd1bb032..19aed51a0a 100644 --- a/app/hooks/solutions/index.ts +++ b/app/hooks/solutions/index.ts @@ -2,7 +2,6 @@ import { useMemo } from 'react'; import { useInfiniteQuery, useMutation, useQuery } from 'react-query'; -import flatten from 'lodash/flatten'; import { useSession } from 'next-auth/react'; import { Scenario } from 'types/api/scenario'; @@ -25,7 +24,7 @@ export function useSolutions(sid, options: UseSolutionsOptionsProps = {}) { }; }, {}); - const fetchSolutions = ({ pageParam = 1 }) => + const fetchSolutions = () => SCENARIOS.request({ method: 'GET', url: `/${sid}/marxan/solutions`, @@ -33,59 +32,31 @@ export function useSolutions(sid, options: UseSolutionsOptionsProps = {}) { Authorization: `Bearer ${session.accessToken}`, }, params: { - 'page[number]': pageParam, ...parsedFilters, ...(sort && { sort, }), + disablePagination: true, }, - }); + }).then((response) => response.data); - const query = useInfiniteQuery(['solutions', sid, JSON.stringify(options)], fetchSolutions, { + return useQuery(['solutions', sid, JSON.stringify(options)], fetchSolutions, { keepPreviousData: true, - getNextPageParam: (lastPage) => { - const { - data: { meta }, - } = lastPage; - const { page, totalPages } = meta; - - const nextPage = page + 1 > totalPages ? null : page + 1; - return nextPage; - }, + placeholderData: { data: [] }, + select: ({ data }) => + data.map((d) => { + const { id, runId, scoreValue, costValue, planningUnits, missingValues } = d; + + return { + id, + runId, + score: scoreValue, + cost: costValue, + planningUnits, + missingValues, + }; + }), }); - - const { data } = query; - const { pages } = data || {}; - - return useMemo(() => { - const parsedData = Array.isArray(pages) - ? flatten( - pages.map((p) => { - const { - data: { data: pageData }, - } = p; - - return pageData.map((d) => { - const { id, runId, scoreValue, costValue, planningUnits, missingValues } = d; - - return { - id, - runId, - score: scoreValue, - cost: costValue, - planningUnits, - missingValues, - }; - }); - }) - ) - : []; - - return { - ...query, - data: parsedData, - }; - }, [query, pages]); } export function useAllSolutions(sid: Scenario['id'], options: UseSolutionsOptionsProps = {}) { diff --git a/app/layout/community/published-projects/list/table/item/component.tsx b/app/layout/community/published-projects/list/table/item/component.tsx index 2beaa4cd37..44633c9fe5 100644 --- a/app/layout/community/published-projects/list/table/item/component.tsx +++ b/app/layout/community/published-projects/list/table/item/component.tsx @@ -36,7 +36,7 @@ export const PublishedItem: React.FC = ({ {name} -

{description}

+

{description}

diff --git a/app/layout/project/sidebar/scenario/grid-setup/features/add/add-modal/list/component.tsx b/app/layout/project/sidebar/scenario/grid-setup/features/add/add-modal/list/component.tsx index f4af457588..6768c98403 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/features/add/add-modal/list/component.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/features/add/add-modal/list/component.tsx @@ -3,7 +3,6 @@ import React, { useCallback } from 'react'; import { useRouter } from 'next/router'; import { useAllPaginatedFeatures } from 'hooks/features'; -import useBottomScrollListener from 'hooks/scroll'; import Item from 'components/features/raw-item'; import Loading from 'components/loading'; @@ -28,21 +27,14 @@ export const ScenariosFeaturesAddList: React.FC = const { query } = useRouter(); const { pid } = query as { pid: string }; - const { - data: allFeaturesData, - fetchNextPage: allFeaturesfetchNextPage, - hasNextPage, - isFetching: allFeaturesIsFetching, - isFetchingNextPage: allFeaturesIsFetchingNextPage, - } = useAllPaginatedFeatures(pid, { - search, - filters, - sort, - }); - - const scrollRef = useBottomScrollListener(() => { - if (hasNextPage) allFeaturesfetchNextPage(); - }); + const { data: allFeaturesData, isFetching: allFeaturesIsFetching } = useAllPaginatedFeatures( + pid, + { + search, + filters, + sort, + } + ); // Callbacks const handleToggleSelected = useCallback( @@ -57,7 +49,6 @@ export const ScenariosFeaturesAddList: React.FC =
=
{ handleToggleSelected(item.id); @@ -98,17 +88,6 @@ export const ScenariosFeaturesAddList: React.FC =
- -
-
Loading more...
-
-
); }; diff --git a/app/layout/project/sidebar/scenario/grid-setup/features/add/intersect/list/component.tsx b/app/layout/project/sidebar/scenario/grid-setup/features/add/intersect/list/component.tsx index 7908fed6cd..7329d02bf8 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/features/add/intersect/list/component.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/features/add/intersect/list/component.tsx @@ -3,7 +3,6 @@ import React, { useCallback } from 'react'; import { useRouter } from 'next/router'; import { useAllPaginatedFeatures } from 'hooks/features'; -import useBottomScrollListener from 'hooks/scroll'; import Item from 'components/features/intersect-item'; import Loading from 'components/loading'; @@ -30,19 +29,12 @@ export const ScenariosFeaturesIntersectList: React.FC { - if (hasNextPage) allFeaturesfetchNextPage(); - }); - // Callbacks const handleSelected = useCallback( (feature) => { @@ -70,7 +62,6 @@ export const ScenariosFeaturesIntersectList: React.FC
{ handleSelected(item); @@ -124,17 +114,6 @@ export const ScenariosFeaturesIntersectList: React.FC
- -
-
Loading more...
-
-
); }; diff --git a/app/layout/project/sidebar/scenario/grid-setup/gap-analysis/list/index.tsx b/app/layout/project/sidebar/scenario/grid-setup/gap-analysis/list/index.tsx index 3fc3801ca3..c9dc3001c8 100644 --- a/app/layout/project/sidebar/scenario/grid-setup/gap-analysis/list/index.tsx +++ b/app/layout/project/sidebar/scenario/grid-setup/gap-analysis/list/index.tsx @@ -7,7 +7,6 @@ import { useRouter } from 'next/router'; import { getScenarioEditSlice } from 'store/slices/scenarios/edit'; import { usePreGapAnalysis } from 'hooks/gap-analysis'; -import useBottomScrollListener from 'hooks/scroll'; import Item from 'components/gap-analysis/item'; import Loading from 'components/loading'; @@ -20,7 +19,7 @@ export interface ScenariosPreGapAnalysisListProps { export const ScenariosPreGapAnalysisList = ({ search }: { search?: string }) => { const { query } = useRouter(); - const { sid } = query; + const { sid } = query as { sid: string }; const scenarioSlice = getScenarioEditSlice(sid); const { setPreHighlightFeatures, setLayerSettings } = scenarioSlice.actions; @@ -31,19 +30,12 @@ export const ScenariosPreGapAnalysisList = ({ search }: { search?: string }) => const { data: allFeaturesData, - fetchNextPage: allFeaturesfetchNextPage, - hasNextPage, isFetching: allFeaturesIsFetching, - isFetchingNextPage: allFeaturesIsFetchingNextPage, isFetched: allFeaturesIsFetched, } = usePreGapAnalysis(sid, { search, }); - const scrollRef = useBottomScrollListener(() => { - if (hasNextPage) allFeaturesfetchNextPage(); - }); - const toggleHighlight = useCallback( (id) => { const newHighlightFeatures = [...preHighlightFeatures]; @@ -89,7 +81,6 @@ export const ScenariosPreGapAnalysisList = ({ search }: { search?: string }) =>
> toggleHighlight(item.id)} /> @@ -128,19 +118,6 @@ export const ScenariosPreGapAnalysisList = ({ search }: { search?: string }) => })}
- -
- -
-
Loading more...
-
-
); }; diff --git a/app/layout/project/sidebar/scenario/solutions/overview/table/table-form/component.tsx b/app/layout/project/sidebar/scenario/solutions/overview/table/table-form/component.tsx index 54478afd89..7b5754f54e 100644 --- a/app/layout/project/sidebar/scenario/solutions/overview/table/table-form/component.tsx +++ b/app/layout/project/sidebar/scenario/solutions/overview/table/table-form/component.tsx @@ -7,7 +7,6 @@ import { useRouter } from 'next/router'; import { getScenarioEditSlice } from 'store/slices/scenarios/edit'; import { useScenario } from 'hooks/scenarios'; -import useBottomScrollListener from 'hooks/scroll'; import { useSolutions, useBestSolution, @@ -22,7 +21,6 @@ import Label from 'components/forms/label'; import Icon from 'components/icon'; import InfoButton from 'components/info-button'; import Loading from 'components/loading'; -import LoadingMore from 'components/loading-more/component'; import NoResults from 'layout/project/sidebar/project/inventory-panel/components/no-results'; import FIVE_DIFF_SOLUTIONS_IMG from 'images/info-buttons/img_5_different_solutions.png'; @@ -60,10 +58,7 @@ export const SolutionsTableForm: React.FC = ({ const { data: solutionsData, - fetchNextPage, - hasNextPage, isFetching: solutionsAreFetching, - isFetchingNextPage, isFetched: solutionsAreFetched, } = useSolutions(sid); @@ -81,10 +76,6 @@ export const SolutionsTableForm: React.FC = ({ const solutionsAreLoading = solutionsAreFetching || mostDifSolutionsAreFetching; - const scrollRef = useBottomScrollListener(() => { - if (hasNextPage) fetchNextPage(); - }); - const downloadSolutionsMutation = useDownloadSolutions({}); const onSave = useCallback(() => { @@ -115,10 +106,7 @@ export const SolutionsTableForm: React.FC = ({ return (
-
+

Solutions Table:

@@ -232,7 +220,6 @@ export const SolutionsTableForm: React.FC = ({ onSelectSolution={(solution) => setSelectSolution(solution)} /> )} -
diff --git a/app/layout/project/sidebar/scenario/solutions/target-achievement/list/index.tsx b/app/layout/project/sidebar/scenario/solutions/target-achievement/list/index.tsx index 6cbb43ca63..7614d0889b 100644 --- a/app/layout/project/sidebar/scenario/solutions/target-achievement/list/index.tsx +++ b/app/layout/project/sidebar/scenario/solutions/target-achievement/list/index.tsx @@ -7,7 +7,6 @@ import { getScenarioEditSlice } from 'store/slices/scenarios/edit'; import { usePostGapAnalysis } from 'hooks/gap-analysis'; import { useScenario } from 'hooks/scenarios'; -import useBottomScrollListener from 'hooks/scroll'; import { useBestSolution } from 'hooks/solutions'; import Item from 'components/gap-analysis/item'; @@ -35,10 +34,7 @@ export const TargetAchievementList = ({ search }: { search?: string }): JSX.Elem const { data: allFeaturesData, - fetchNextPage: allFeaturesfetchNextPage, - hasNextPage, isFetching: allFeaturesIsFetching, - isFetchingNextPage: allFeaturesIsFetchingNextPage, isFetched: allFeaturesIsFetched, } = usePostGapAnalysis(sid, { search, @@ -47,10 +43,6 @@ export const TargetAchievementList = ({ search }: { search?: string }): JSX.Elem }, }); - const scrollRef = useBottomScrollListener(() => { - if (hasNextPage) allFeaturesfetchNextPage(); - }); - const toggleHighlight = useCallback( (id) => { const newHighlightFeatures = [...postHighlightFeatures]; @@ -96,7 +88,6 @@ export const TargetAchievementList = ({ search }: { search?: string }): JSX.Elem
toggleHighlight(item.id)} /> @@ -138,17 +128,6 @@ export const TargetAchievementList = ({ search }: { search?: string }): JSX.Elem
- -
-
Loading more...
-
-
); }; diff --git a/app/layout/projects/show/scenarios/component.tsx b/app/layout/projects/show/scenarios/component.tsx index dd2115522f..344edcfe96 100644 --- a/app/layout/projects/show/scenarios/component.tsx +++ b/app/layout/projects/show/scenarios/component.tsx @@ -58,10 +58,7 @@ export const ProjectScenarios: React.FC = () => { const { data: scenariosData, - fetchNextPage: scenariosFetchNextPage, - hasNextPage, isFetching: scenariosIsFetching, - isFetchingNextPage: scenariosIsFetchingNextPage, isFetched: scenariosIsFetched, } = useScenarios(pid, { search, @@ -72,10 +69,6 @@ export const ProjectScenarios: React.FC = () => { sort, }); - const scrollRef = useBottomScrollListener(() => { - if (hasNextPage) scenariosFetchNextPage(); - }); - const projectLoading = projectIsFetching && !projectIsFetched; const scenariosLoading = scenariosIsFetching && !scenariosIsFetched; const hasScenarios = !!scenariosData.length; @@ -229,10 +222,7 @@ export const ProjectScenarios: React.FC = () => { )} {hasScenarios && ( -
+
{scenariosData.map((s, i) => { const TAG = i === 0 ? HelpBeacon : Fragment; @@ -275,17 +265,6 @@ export const ProjectScenarios: React.FC = () => { )}
- -
-
Loading more...
-
-
{!hasScenarios && !search && !hasFilters && scenariosIsFetched && (