From 6a369ddab113357a6aa02cf1c950b291c771e557 Mon Sep 17 00:00:00 2001 From: David Lyon Date: Mon, 15 Apr 2024 16:06:12 -0700 Subject: [PATCH 1/4] add biolog context tabs --- .../collections/data_products/Biolog.tsx | 64 +++++++++++++++---- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/src/features/collections/data_products/Biolog.tsx b/src/features/collections/data_products/Biolog.tsx index 6ce5840d..9d2c0eab 100644 --- a/src/features/collections/data_products/Biolog.tsx +++ b/src/features/collections/data_products/Biolog.tsx @@ -20,6 +20,7 @@ import { formatNumber } from '../../../common/utils/stringUtils'; import { useAppParam } from '../../params/hooks'; import classes from '../Collections.module.scss'; import { useGenerateSelectionId } from '../collectionsSlice'; +import { useFilterContexts } from '../Filters'; import { useProcessStatePolling } from '../hooks'; import { HeatMap, HeatMapCallback, MAX_HEATMAP_PAGE } from './HeatMap'; @@ -119,25 +120,30 @@ const useBiolog = (collection_id: string | undefined) => { () => ({ collection_id: collection_id ?? '', limit: pagination.pageSize, + match_mark: false, + selection_mark: false, ...(pagination.pageIndex !== 0 ? { start_after: pageLastIdCache[pagination.pageIndex - 1] } : {}), - ...(matchId ? { match_id: matchId, match_mark: matchMark } : {}), - ...(selId ? { selection_id: selId, selection_mark: selMark } : {}), + ...(matchId ? { match_id: matchId } : {}), + ...(selId ? { selection_id: selId } : {}), }), [ collection_id, matchId, - matchMark, pageLastIdCache, pagination.pageIndex, pagination.pageSize, selId, - selMark, ] ); - const countParams = useMemo( - () => ({ ...heatMapParams, count: true }), + const allCountParams = useMemo( + () => ({ + ...heatMapParams, + count: true, + match_mark: true, + selection_mark: true, + }), [heatMapParams] ); const metaParams = useMemo( @@ -156,10 +162,10 @@ const useBiolog = (collection_id: string | undefined) => { //cache last row of each page, we should implement better backend pagination this is silly useEffect(() => { if (!biologQuery.isFetching && biologQuery.data) { - pageLastIdCache[pagination.pageIndex] = - biologQuery.data.data[ - biologQuery.data.data.length - 1 - ].kbase_display_name; + const name = + biologQuery.data.data[biologQuery.data.data.length - 1] + ?.kbase_display_name; + if (name) pageLastIdCache[pagination.pageIndex] = name; } }, [ biologQuery.data, @@ -168,14 +174,48 @@ const useBiolog = (collection_id: string | undefined) => { pageLastIdCache, ]); - const { data: count, ...countQuery } = getBiolog.useQuery(countParams, { + const { data: meta, ...metaQuery } = getBiologMeta.useQuery(metaParams, { skip: !collection_id, }); - const { data: meta, ...metaQuery } = getBiologMeta.useQuery(metaParams, { + const { data: count, ...countQuery } = getBiolog.useQuery(allCountParams, { skip: !collection_id, }); + const matchCount = getBiolog.useQuery( + { ...allCountParams, match_mark: false }, + { + skip: !collection_id, + } + ); + + const selCount = getBiolog.useQuery( + { ...allCountParams, selection_mark: false }, + { + skip: !collection_id, + } + ); + + useFilterContexts(collection_id || '', [ + { + label: 'All', + value: 'biolog.all', + count: count?.count, + }, + { + label: 'Matched', + value: 'biolog.matched', + count: heatMapParams.match_id ? matchCount?.data?.count : undefined, + disabled: !heatMapParams.match_id || matchCount?.data?.count === 0, + }, + { + label: 'Selected', + value: 'biolog.selected', + count: heatMapParams.selection_id ? selCount?.data?.count : undefined, + disabled: !heatMapParams.selection_id || selCount?.data?.count === 0, + }, + ]); + type RowDatum = NonNullable['data'][number]; const cols = createColumnHelper(); From d929c006e03cce9596a1ff20bb77b318d1459b7c Mon Sep 17 00:00:00 2001 From: David Lyon Date: Thu, 25 Apr 2024 17:05:22 -0700 Subject: [PATCH 2/4] Make biolog context tabs function with loading and counts --- .../collections/data_products/Biolog.tsx | 59 ++++++++++++++----- src/features/collections/hooks.ts | 7 ++- 2 files changed, 49 insertions(+), 17 deletions(-) diff --git a/src/features/collections/data_products/Biolog.tsx b/src/features/collections/data_products/Biolog.tsx index 9d2c0eab..c48d0f36 100644 --- a/src/features/collections/data_products/Biolog.tsx +++ b/src/features/collections/data_products/Biolog.tsx @@ -19,7 +19,10 @@ import { useAppDispatch } from '../../../common/hooks'; import { formatNumber } from '../../../common/utils/stringUtils'; import { useAppParam } from '../../params/hooks'; import classes from '../Collections.module.scss'; -import { useGenerateSelectionId } from '../collectionsSlice'; +import { + useFilterContextState, + useGenerateSelectionId, +} from '../collectionsSlice'; import { useFilterContexts } from '../Filters'; import { useProcessStatePolling } from '../hooks'; import { HeatMap, HeatMapCallback, MAX_HEATMAP_PAGE } from './HeatMap'; @@ -103,25 +106,24 @@ const useBiolog = (collection_id: string | undefined) => { const selId = useGenerateSelectionId(collection_id || '', { skip: !collection_id, }); - const [matchMark, setMatchMark] = useState(true); - const [selMark, setSelMark] = useState(true); const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 56, }); + const context = useFilterContextState(collection_id); const pageLastIdCache: Record = useMemo( () => ({}), // eslint-disable-next-line react-hooks/exhaustive-deps - [collection_id, selMark, matchMark, pagination.pageSize] + [collection_id, context, pagination.pageSize] ); const heatMapParams = useMemo( () => ({ collection_id: collection_id ?? '', limit: pagination.pageSize, - match_mark: false, - selection_mark: false, + match_mark: !context.endsWith('.matched'), + selection_mark: !context.endsWith('.selected'), ...(pagination.pageIndex !== 0 ? { start_after: pageLastIdCache[pagination.pageIndex - 1] } : {}), @@ -135,6 +137,7 @@ const useBiolog = (collection_id: string | undefined) => { pagination.pageIndex, pagination.pageSize, selId, + context, ] ); const allCountParams = useMemo( @@ -182,17 +185,37 @@ const useBiolog = (collection_id: string | undefined) => { skip: !collection_id, }); - const matchCount = getBiolog.useQuery( - { ...allCountParams, match_mark: false }, - { - skip: !collection_id, - } + const [matchCountParams, selCountParams] = useMemo( + () => [ + { + ...allCountParams, + match_mark: false, + }, + { + ...allCountParams, + selection_mark: false, + }, + ], + [allCountParams] ); - const selCount = getBiolog.useQuery( - { ...allCountParams, selection_mark: false }, + const matchCount = getBiolog.useQuery(matchCountParams, { + skip: !collection_id || !matchId, + }); + + const { match_state } = useProcessStatePolling(matchCount, ['match_state'], { + skipPoll: !collection_id || !matchId, + }); + + const selCount = getBiolog.useQuery(selCountParams, { + skip: !collection_id || !selId, + }); + + const { selection_state } = useProcessStatePolling( + selCount, + ['selection_state'], { - skip: !collection_id, + skipPoll: !collection_id || !selId, } ); @@ -207,12 +230,18 @@ const useBiolog = (collection_id: string | undefined) => { value: 'biolog.matched', count: heatMapParams.match_id ? matchCount?.data?.count : undefined, disabled: !heatMapParams.match_id || matchCount?.data?.count === 0, + loading: + (heatMapParams.match_id && !match_state) || + match_state === 'processing', }, { label: 'Selected', value: 'biolog.selected', count: heatMapParams.selection_id ? selCount?.data?.count : undefined, disabled: !heatMapParams.selection_id || selCount?.data?.count === 0, + loading: + (heatMapParams.selection_id && !selection_state) || + selection_state === 'processing', }, ]); @@ -295,8 +324,6 @@ const useBiolog = (collection_id: string | undefined) => { }); return { - setMatchMark, - setSelMark, setPagination, biolog, biologQuery, diff --git a/src/features/collections/hooks.ts b/src/features/collections/hooks.ts index 186db295..1e8847eb 100644 --- a/src/features/collections/hooks.ts +++ b/src/features/collections/hooks.ts @@ -104,7 +104,7 @@ export const useProcessStatePolling = < result: R, processStateKeys: StateKey[], options?: { baseInterval?: number; rate?: number; skipPoll?: boolean } -) => { +): Partial<{ [processStateKey in StateKey]: ProcessState }> => { useBackoffPolling( result, (result) => { @@ -128,4 +128,9 @@ export const useProcessStatePolling = < }, options ); + const results: Partial<{ [processStateKey in StateKey]: ProcessState }> = {}; + processStateKeys.forEach((key) => { + results[key] = result.data?.[key]; + }); + return results; }; From 550d21eb4781ec057ee5d6069739bdd28239e55d Mon Sep 17 00:00:00 2001 From: David Lyon Date: Fri, 26 Apr 2024 10:45:48 -0700 Subject: [PATCH 3/4] Add microtrait context tabs --- .../collections/data_products/Biolog.tsx | 5 + .../collections/data_products/Microtrait.tsx | 115 +++++++++++++++--- 2 files changed, 101 insertions(+), 19 deletions(-) diff --git a/src/features/collections/data_products/Biolog.tsx b/src/features/collections/data_products/Biolog.tsx index c48d0f36..0ffb50a0 100644 --- a/src/features/collections/data_products/Biolog.tsx +++ b/src/features/collections/data_products/Biolog.tsx @@ -162,6 +162,11 @@ const useBiolog = (collection_id: string | undefined) => { skipPoll: !collection_id || !(matchId || selId), }); + // Reload on context change + useEffect(() => { + biologQuery.refetch(); + }, [biologQuery, context]); + //cache last row of each page, we should implement better backend pagination this is silly useEffect(() => { if (!biologQuery.isFetching && biologQuery.data) { diff --git a/src/features/collections/data_products/Microtrait.tsx b/src/features/collections/data_products/Microtrait.tsx index 85fab350..2d37cfa5 100644 --- a/src/features/collections/data_products/Microtrait.tsx +++ b/src/features/collections/data_products/Microtrait.tsx @@ -18,7 +18,12 @@ import { Pagination, usePageBounds } from '../../../common/components/Table'; import { useAppDispatch } from '../../../common/hooks'; import { formatNumber } from '../../../common/utils/stringUtils'; import classes from '../Collections.module.scss'; -import { useMatchId, useGenerateSelectionId } from '../collectionsSlice'; +import { + useMatchId, + useGenerateSelectionId, + useFilterContextState, +} from '../collectionsSlice'; +import { useFilterContexts } from '../Filters'; import { useProcessStatePolling } from '../hooks'; import { HeatMap, HeatMapCallback, MAX_HEATMAP_PAGE } from './HeatMap'; @@ -104,8 +109,8 @@ const useMicrotrait = (collection_id: string | undefined) => { const selId = useGenerateSelectionId(collection_id || '', { skip: !collection_id, }); - const [matchMark, setMatchMark] = useState(true); - const [selMark, setSelMark] = useState(true); + const context = useFilterContextState(collection_id); + const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 50, @@ -114,32 +119,38 @@ const useMicrotrait = (collection_id: string | undefined) => { const pageLastIdCache: Record = useMemo( () => ({}), // eslint-disable-next-line react-hooks/exhaustive-deps - [collection_id, selMark, matchMark, pagination.pageSize] + [collection_id, context, pagination.pageSize] ); const heatMapParams = useMemo( () => ({ collection_id: collection_id ?? '', limit: pagination.pageSize, + match_mark: !context.endsWith('.matched'), + selection_mark: !context.endsWith('.selected'), ...(pagination.pageIndex !== 0 ? { start_after: pageLastIdCache[pagination.pageIndex - 1] } : {}), - ...(matchId ? { match_id: matchId, match_mark: matchMark } : {}), - ...(selId ? { selection_id: selId, selection_mark: selMark } : {}), + ...(matchId ? { match_id: matchId } : {}), + ...(selId ? { selection_id: selId } : {}), }), [ collection_id, matchId, - matchMark, pageLastIdCache, pagination.pageIndex, pagination.pageSize, selId, - selMark, + context, ] ); - const countParams = useMemo( - () => ({ ...heatMapParams, count: true }), + const allCountParams = useMemo( + () => ({ + ...heatMapParams, + count: true, + match_mark: true, + selection_mark: true, + }), [heatMapParams] ); const metaParams = useMemo( @@ -156,13 +167,18 @@ const useMicrotrait = (collection_id: string | undefined) => { skipPoll: !collection_id || !(matchId || selId), }); + // Reload on context change + useEffect(() => { + microtraitQuery.refetch(); + }, [microtraitQuery, context]); + //cache last row of each page, we should implement better backend pagination this is silly useEffect(() => { if (!microtraitQuery.isFetching && microtraitQuery.data) { - pageLastIdCache[pagination.pageIndex] = - microtraitQuery.data.data[ - microtraitQuery.data.data.length - 1 - ].kbase_display_name; + const name = + microtraitQuery.data.data[microtraitQuery.data.data.length - 1] + ?.kbase_display_name; + if (name) pageLastIdCache[pagination.pageIndex] = name; } }, [ microtraitQuery.data, @@ -171,14 +187,77 @@ const useMicrotrait = (collection_id: string | undefined) => { pageLastIdCache, ]); - const { data: count, ...countQuery } = getMicroTrait.useQuery(countParams, { + const { data: meta, ...metaQuery } = getMicroTraitMeta.useQuery(metaParams, { skip: !collection_id, }); - const { data: meta, ...metaQuery } = getMicroTraitMeta.useQuery(metaParams, { - skip: !collection_id, + const { data: count, ...countQuery } = getMicroTrait.useQuery( + allCountParams, + { + skip: !collection_id, + } + ); + + const [matchCountParams, selCountParams] = useMemo( + () => [ + { + ...allCountParams, + match_mark: false, + }, + { + ...allCountParams, + selection_mark: false, + }, + ], + [allCountParams] + ); + + const matchCount = getMicroTrait.useQuery(matchCountParams, { + skip: !collection_id || !matchId, + }); + + const { match_state } = useProcessStatePolling(matchCount, ['match_state'], { + skipPoll: !collection_id || !matchId, }); + const selCount = getMicroTrait.useQuery(selCountParams, { + skip: !collection_id || !selId, + }); + + const { selection_state } = useProcessStatePolling( + selCount, + ['selection_state'], + { + skipPoll: !collection_id || !selId, + } + ); + + useFilterContexts(collection_id || '', [ + { + label: 'All', + value: 'microtrait.all', + count: count?.count, + }, + { + label: 'Matched', + value: 'microtrait.matched', + count: heatMapParams.match_id ? matchCount?.data?.count : undefined, + disabled: !heatMapParams.match_id || matchCount?.data?.count === 0, + loading: + (heatMapParams.match_id && !match_state) || + match_state === 'processing', + }, + { + label: 'Selected', + value: 'microtrait.selected', + count: heatMapParams.selection_id ? selCount?.data?.count : undefined, + disabled: !heatMapParams.selection_id || selCount?.data?.count === 0, + loading: + (heatMapParams.selection_id && !selection_state) || + selection_state === 'processing', + }, + ]); + type RowDatum = NonNullable['data'][number]; const cols = createColumnHelper(); @@ -260,8 +339,6 @@ const useMicrotrait = (collection_id: string | undefined) => { }); return { - setMatchMark, - setSelMark, setPagination, microtrait, microtraitQuery, From 090911a750004d04579188cc9700eee58e2f3998 Mon Sep 17 00:00:00 2001 From: David Lyon Date: Fri, 26 Apr 2024 12:27:25 -0700 Subject: [PATCH 4/4] fix effect deps issue --- src/features/collections/data_products/Biolog.tsx | 3 ++- src/features/collections/data_products/Microtrait.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/features/collections/data_products/Biolog.tsx b/src/features/collections/data_products/Biolog.tsx index 0ffb50a0..2866946e 100644 --- a/src/features/collections/data_products/Biolog.tsx +++ b/src/features/collections/data_products/Biolog.tsx @@ -165,7 +165,8 @@ const useBiolog = (collection_id: string | undefined) => { // Reload on context change useEffect(() => { biologQuery.refetch(); - }, [biologQuery, context]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [context]); //cache last row of each page, we should implement better backend pagination this is silly useEffect(() => { diff --git a/src/features/collections/data_products/Microtrait.tsx b/src/features/collections/data_products/Microtrait.tsx index 2d37cfa5..dbbb1e23 100644 --- a/src/features/collections/data_products/Microtrait.tsx +++ b/src/features/collections/data_products/Microtrait.tsx @@ -170,7 +170,8 @@ const useMicrotrait = (collection_id: string | undefined) => { // Reload on context change useEffect(() => { microtraitQuery.refetch(); - }, [microtraitQuery, context]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [context]); //cache last row of each page, we should implement better backend pagination this is silly useEffect(() => {