From 420acdd2999e4261214a9475c9a6d37b1c85ef28 Mon Sep 17 00:00:00 2001 From: bchu1 <151689101+bchu1@users.noreply.github.com> Date: Fri, 9 Aug 2024 16:15:27 -0700 Subject: [PATCH] feat: Add Tomograms table (#988) closes #995 Barebones initial table (UX not finalized yet, waiting until then to do fit & finish). Metadata drawer is empty. Download modal does not target the selected tomogram yet. Query changes: - Added an `is_canonical` check to the existing `tomogram_voxel_spacings` field (which we can delete after launch) so that if we need to turn off the flag, the UI will still only display the canonical `tomogram`. - Replaced `tomogram_stats` count with top-level `tomograms_aggregate` count that doesn't require FE summing. BE pieces not supported yet: - Distinguish between Portal Standard and Author Submitted tomograms. - Deposition date. - Alignment ID. ![image](https://github.com/user-attachments/assets/db706cf9-09d7-46bc-ae0a-b0f591c2d529) --- .../data-portal/app/components/PageHeader.tsx | 2 +- .../app/components/Run/RunHeader.tsx | 8 +- .../app/components/Run/RunMetadataDrawer.tsx | 4 +- .../components/Run/TomogramMetadataDrawer.tsx | 23 ++ .../app/components/Run/TomogramTable.tsx | 199 ++++++++++++++++++ ...Table.tsx => TomogramsMetadataSection.tsx} | 2 +- .../app/components/ViewTomogramButton.tsx | 5 +- .../data-portal/app/constants/table.ts | 12 ++ .../app/graphql/getRunById.server.ts | 54 ++++- .../app/hooks/useMetadataDrawer.ts | 1 + .../data-portal/app/hooks/useRunById.ts | 6 + .../data-portal/app/routes/runs.$id.tsx | 12 +- .../app/state/metadataDrawerTomogram.ts | 8 + .../packages/data-portal/app/utils/url.ts | 6 + .../public/locales/en/translation.json | 4 + 15 files changed, 321 insertions(+), 25 deletions(-) create mode 100644 frontend/packages/data-portal/app/components/Run/TomogramMetadataDrawer.tsx create mode 100644 frontend/packages/data-portal/app/components/Run/TomogramTable.tsx rename frontend/packages/data-portal/app/components/Run/{TomogramsTable.tsx => TomogramsMetadataSection.tsx} (97%) create mode 100644 frontend/packages/data-portal/app/state/metadataDrawerTomogram.ts diff --git a/frontend/packages/data-portal/app/components/PageHeader.tsx b/frontend/packages/data-portal/app/components/PageHeader.tsx index 286412689..2272f7bcf 100644 --- a/frontend/packages/data-portal/app/components/PageHeader.tsx +++ b/frontend/packages/data-portal/app/components/PageHeader.tsx @@ -27,7 +27,7 @@ export function PageHeader({ const { t } = useI18n() return ( -
+
stats.tomograms_aggregate.aggregate?.count ?? 0, - ), - ) const annotationsCount = annotationFilesAggregates.totalCount return ( diff --git a/frontend/packages/data-portal/app/components/Run/RunMetadataDrawer.tsx b/frontend/packages/data-portal/app/components/Run/RunMetadataDrawer.tsx index 310ae86c7..846f82bd9 100644 --- a/frontend/packages/data-portal/app/components/Run/RunMetadataDrawer.tsx +++ b/frontend/packages/data-portal/app/components/Run/RunMetadataDrawer.tsx @@ -6,7 +6,7 @@ import { useRunById } from 'app/hooks/useRunById' import { i18n } from 'app/i18n' import { RunTiltSeriesTable } from './RunTiltSeriesTable' -import { TomogramsTable } from './TomogramsTable' +import { TomogramsMetadataSection } from './TomogramsMetadataSection' export function RunMetadataDrawer() { const { run } = useRunById() @@ -27,7 +27,7 @@ export function RunMetadataDrawer() { initialOpen={false} /> - + ) } diff --git a/frontend/packages/data-portal/app/components/Run/TomogramMetadataDrawer.tsx b/frontend/packages/data-portal/app/components/Run/TomogramMetadataDrawer.tsx new file mode 100644 index 000000000..6d5967d72 --- /dev/null +++ b/frontend/packages/data-portal/app/components/Run/TomogramMetadataDrawer.tsx @@ -0,0 +1,23 @@ +import { useAtom } from 'jotai' + +import { useI18n } from 'app/hooks/useI18n' +import { MetadataDrawerId } from 'app/hooks/useMetadataDrawer' +import { metadataDrawerTomogramAtom } from 'app/state/metadataDrawerTomogram' + +import { MetadataDrawer } from '../MetadataDrawer' + +export function TomogramMetadataDrawer() { + const { t } = useI18n() + const [metadataDrawerTomogram] = useAtom(metadataDrawerTomogramAtom) + + return ( + + <> + + ) +} diff --git a/frontend/packages/data-portal/app/components/Run/TomogramTable.tsx b/frontend/packages/data-portal/app/components/Run/TomogramTable.tsx new file mode 100644 index 000000000..865061d9c --- /dev/null +++ b/frontend/packages/data-portal/app/components/Run/TomogramTable.tsx @@ -0,0 +1,199 @@ +/* eslint-disable react/no-unstable-nested-components */ + +import { Button, Icon } from '@czi-sds/components' +import { ColumnDef, createColumnHelper } from '@tanstack/react-table' +import { useAtom } from 'jotai' +import { startCase } from 'lodash-es' +import { useCallback, useMemo } from 'react' + +import { CellHeader, PageTable, TableCell } from 'app/components/Table' +import { TomogramTableWidths } from 'app/constants/table' +import { useDownloadModalQueryParamState } from 'app/hooks/useDownloadModalQueryParamState' +import { useI18n } from 'app/hooks/useI18n' +import { + MetadataDrawerId, + useMetadataDrawer, +} from 'app/hooks/useMetadataDrawer' +import { useRunById } from 'app/hooks/useRunById' +import { + metadataDrawerTomogramAtom, + Tomogram, +} from 'app/state/metadataDrawerTomogram' +import { getNeuroglancerUrl } from 'app/utils/url' + +import { AuthorList } from '../AuthorList' +import { KeyPhoto } from '../KeyPhoto' + +export function TomogramsTable() { + const { t } = useI18n() + const { tomograms } = useRunById() + + const { toggleDrawer } = useMetadataDrawer() + const [, setMetadataDrawerTomogram] = useAtom(metadataDrawerTomogramAtom) + + const { openTomogramDownloadModal } = useDownloadModalQueryParamState() + + const openMetadataDrawer = useCallback( + (tomogram: Tomogram) => { + setMetadataDrawerTomogram(tomogram) + toggleDrawer(MetadataDrawerId.Tomogram) + }, + [setMetadataDrawerTomogram, toggleDrawer], + ) + + const columns = useMemo(() => { + const columnHelper = createColumnHelper() + return [ + columnHelper.accessor('key_photo_url', { + header: () => , + cell: ({ row: { original } }) => ( + + + + ), + }), + columnHelper.accessor('id', { + header: () => ( + + {t('tomogramId')} + + ), + cell: ({ row: { original } }) => ( + +
+

+ {original.id} +

+
+
+ +
+
+ ), + }), + // TODO(bchu): Switch to deposition_date when available. + columnHelper.accessor('name', { + id: 'deposition_date', + header: () => ( + + {t('depositionDate')} + + ), + cell: ({ getValue }) => ( + +
{getValue()}
+
+ ), + }), + // TODO(bchu): Switch to alignment_id when available. + columnHelper.accessor('name', { + header: () => ( + + {t('alignmentId')} + + ), + cell: ({ getValue }) => ( + +
{getValue()}
+
+ ), + }), + columnHelper.accessor('voxel_spacing', { + header: () => ( + + {t('voxelSpacing')} + + ), + cell: ({ getValue, row: { original } }) => ( + + {t('unitAngstrom', { value: getValue() })} +
+ ({original.size_x}, {original.size_y}, {original.size_z})px +
+
+ ), + }), + columnHelper.accessor('reconstruction_method', { + header: () => ( + + {t('reconstructionMethod')} + + ), + cell: ({ getValue }) => ( + +
{getValue()}
+
+ ), + }), + columnHelper.accessor('processing', { + header: () => ( + + {t('postProcessing')} + + ), + cell: ({ getValue }) => ( + +
{startCase(getValue())}
+
+ ), + }), + columnHelper.display({ + id: 'tomogram-actions', + header: () => , + cell: ({ row: { original } }) => ( + +
+ {original.is_canonical && + original.neuroglancer_config != null && ( + + )} + + +
+
+ ), + }), + ] as ColumnDef[] // https://github.com/TanStack/table/issues/4382 + }, [openMetadataDrawer, openTomogramDownloadModal, t]) + + return +} diff --git a/frontend/packages/data-portal/app/components/Run/TomogramsTable.tsx b/frontend/packages/data-portal/app/components/Run/TomogramsMetadataSection.tsx similarity index 97% rename from frontend/packages/data-portal/app/components/Run/TomogramsTable.tsx rename to frontend/packages/data-portal/app/components/Run/TomogramsMetadataSection.tsx index 4ad5b22e2..e6d1ccf26 100644 --- a/frontend/packages/data-portal/app/components/Run/TomogramsTable.tsx +++ b/frontend/packages/data-portal/app/components/Run/TomogramsMetadataSection.tsx @@ -6,7 +6,7 @@ import { isFiducial } from 'app/utils/tomograms' import { Matrix4x4 } from './Matrix4x4' -export function TomogramsTable() { +export function TomogramsMetadataSection() { const { run } = useRunById() const tomo = run.tomogram_voxel_spacings.at(0)?.tomograms.at(0) diff --git a/frontend/packages/data-portal/app/components/ViewTomogramButton.tsx b/frontend/packages/data-portal/app/components/ViewTomogramButton.tsx index 46e706c7e..a64f7baf1 100644 --- a/frontend/packages/data-portal/app/components/ViewTomogramButton.tsx +++ b/frontend/packages/data-portal/app/components/ViewTomogramButton.tsx @@ -2,6 +2,7 @@ import { Button, ButtonProps } from '@czi-sds/components' import { useI18n } from 'app/hooks/useI18n' import { EventPayloads, Events, usePlausible } from 'app/hooks/usePlausible' +import { getNeuroglancerUrl } from 'app/utils/url' import { Link } from './Link' @@ -45,9 +46,7 @@ export function ViewTomogramButton({ onMouseLeave={() => setIsHoveringOver?.(false)} >