Skip to content

Commit b393913

Browse files
feat: single deposition table updates setup (#1851)
* extract columns from annotations and tomograms table * extract data contents query to fragment * add format for table counts * vertical tabs * add title + divider to table page layout * increase shape type column width * conditionally render <span /> * destructure data contents
1 parent 33e5836 commit b393913

23 files changed

+802
-472
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { I18n } from 'app/components/I18n'
2+
import { Tooltip } from 'app/components/Tooltip'
3+
import { IdPrefix } from 'app/constants/idPrefixes'
4+
import { TestIds } from 'app/constants/testIds'
5+
import { useI18n } from 'app/hooks/useI18n'
6+
import { cns, cnsNoMerge } from 'app/utils/cns'
7+
8+
export function AnnotationNameTableCell({
9+
annotationId,
10+
groundTruthStatus,
11+
objectName,
12+
s3Path,
13+
}: {
14+
annotationId?: number
15+
groundTruthStatus?: boolean | null
16+
objectName?: string
17+
s3Path?: string
18+
}) {
19+
const { t } = useI18n()
20+
21+
return (
22+
<div>
23+
<p
24+
className={cns(
25+
'text-sds-body-m-400-wide leading-sds-body-m font-semibold',
26+
'text-ellipsis line-clamp-2 break-all',
27+
)}
28+
>
29+
{s3Path && <span className="pr-sds-xs">{parseFilePath(s3Path)}</span>}
30+
<span>{objectName}</span>
31+
</p>
32+
33+
<div className="flex items-center gap-sds-xxs">
34+
<p className="text-sds-body-xxs-400-wide leading-sds-body-xxs">
35+
<span>
36+
{t('annotationId')}: {IdPrefix.Annotation}-
37+
</span>
38+
<span data-testid={TestIds.AnnotationId}>{annotationId}</span>
39+
</p>
40+
41+
{groundTruthStatus && (
42+
<Tooltip
43+
tooltip={<I18n i18nKey="groundTruthTooltip" />}
44+
placement="top"
45+
>
46+
<div
47+
className={cnsNoMerge(
48+
'px-sds-xs py-sds-xxxs',
49+
'flex items-center justify-center',
50+
'rounded-sds-m bg-light-sds-color-primitive-blue-200',
51+
'text-sds-body-xxxs-400-wide leading-sds-body-xxxs text-light-sds-color-primitive-blue-600 whitespace-nowrap',
52+
)}
53+
>
54+
{t('groundTruth')}
55+
</div>
56+
</Tooltip>
57+
)}
58+
</div>
59+
</div>
60+
)
61+
}
62+
63+
function parseFilePath(filePath: string) {
64+
const path = filePath.split('/')
65+
return path.at(-2)
66+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import Skeleton from '@mui/material/Skeleton'
2+
import { createColumnHelper } from '@tanstack/react-table'
3+
4+
import type { AnnotationShape } from 'app/__generated_v2__/graphql'
5+
import { AuthorList } from 'app/components/AuthorList'
6+
import { CellHeader, TableCell } from 'app/components/Table'
7+
import type { TableColumnWidth } from 'app/constants/table'
8+
import { useI18n } from 'app/hooks/useI18n'
9+
10+
import { AnnotationNameTableCell } from './AnnotationNameTableCell'
11+
12+
const columnHelper = createColumnHelper<AnnotationShape>()
13+
14+
export function useAnnotationNameColumn({
15+
width,
16+
showAuthors = false,
17+
}: {
18+
width: TableColumnWidth
19+
showAuthors?: boolean
20+
}) {
21+
const { t } = useI18n()
22+
23+
return columnHelper.accessor(
24+
(annotationShape) => annotationShape.annotation,
25+
{
26+
id: 'annotationName',
27+
28+
header: () => (
29+
<CellHeader width={width}>{t('annotationName')}</CellHeader>
30+
),
31+
32+
cell: ({ row: { original: annotationShape } }) => (
33+
<TableCell
34+
className="flex flex-col gap-sds-xxxs !items-start"
35+
renderLoadingSkeleton={() => (
36+
<div>
37+
<Skeleton className="w-[200px]" variant="text" />
38+
<Skeleton className="w-[200px]" variant="text" />
39+
<Skeleton className="w-[100px]" variant="text" />
40+
</div>
41+
)}
42+
width={width}
43+
>
44+
<AnnotationNameTableCell
45+
annotationId={annotationShape.annotation?.id}
46+
groundTruthStatus={annotationShape.annotation?.groundTruthStatus}
47+
objectName={annotationShape.annotation?.objectName}
48+
s3Path={annotationShape.annotationFiles.edges[0]?.node.s3Path}
49+
/>
50+
51+
{showAuthors && (
52+
<div className="text-light-sds-color-semantic-base-text-secondary text-sds-body-xxs-400-wide leading-sds-header-xxs mt-sds-s">
53+
<AuthorList
54+
authors={
55+
annotationShape.annotation?.authors.edges.map(
56+
(author) => author.node,
57+
) ?? []
58+
}
59+
compact
60+
/>
61+
</div>
62+
)}
63+
</TableCell>
64+
),
65+
},
66+
)
67+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import Skeleton from '@mui/material/Skeleton'
2+
import { createColumnHelper } from '@tanstack/react-table'
3+
import { match, P } from 'ts-pattern'
4+
5+
import type { AnnotationShape } from 'app/__generated_v2__/graphql'
6+
import { CellHeader, TableCell } from 'app/components/Table'
7+
import {
8+
getMethodTypeLabelI18nKey,
9+
getMethodTypeTooltipI18nKey,
10+
} from 'app/constants/methodTypes'
11+
import type { TableColumnWidth } from 'app/constants/table'
12+
import { useI18n } from 'app/hooks/useI18n'
13+
import { DASHED_BORDERED_CLASSES } from 'app/utils/classNames'
14+
import { cnsNoMerge } from 'app/utils/cns'
15+
16+
import { I18n } from '../I18n'
17+
18+
const columnHelper = createColumnHelper<AnnotationShape>()
19+
20+
const ROOT_CLASS_NAME = cnsNoMerge(
21+
'text-sds-header-s-600-wide leading-sds-header-s',
22+
DASHED_BORDERED_CLASSES,
23+
)
24+
25+
export function useMethodTypeColumn({
26+
onClick,
27+
width,
28+
}: {
29+
onClick?(shape: AnnotationShape): void
30+
width: TableColumnWidth
31+
}) {
32+
const { t } = useI18n()
33+
34+
return columnHelper.accessor('annotation.methodType', {
35+
header: () => <CellHeader width={width}>{t('methodType')}</CellHeader>,
36+
37+
cell: ({ row: { original: annotationShape } }) => {
38+
const methodType = annotationShape.annotation?.methodType
39+
40+
return (
41+
<TableCell
42+
tooltip={
43+
methodType ? (
44+
<I18n i18nKey={getMethodTypeTooltipI18nKey(methodType)} />
45+
) : undefined
46+
}
47+
tooltipProps={{ placement: 'top' }}
48+
renderLoadingSkeleton={() => (
49+
<Skeleton className="w-[200px]" variant="text" />
50+
)}
51+
width={width}
52+
>
53+
{match({ methodType, onClick })
54+
.with(
55+
{ methodType: P.string, onClick: P.not(P.nullish) },
56+
({ methodType: type, onClick: handler }) => (
57+
<button
58+
className={ROOT_CLASS_NAME}
59+
onClick={() => handler(annotationShape)}
60+
type="button"
61+
>
62+
{t(getMethodTypeLabelI18nKey(type))}
63+
</button>
64+
),
65+
)
66+
.with({ methodType: P.string }, ({ methodType: type }) => (
67+
<span className={ROOT_CLASS_NAME}>
68+
{t(getMethodTypeLabelI18nKey(type))}
69+
</span>
70+
))
71+
.otherwise(() => '--')}
72+
</TableCell>
73+
)
74+
},
75+
})
76+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import Skeleton from '@mui/material/Skeleton'
2+
import { createColumnHelper } from '@tanstack/react-table'
3+
4+
import type { AnnotationShape } from 'app/__generated_v2__/graphql'
5+
import { CellHeader, TableCell } from 'app/components/Table'
6+
import type { TableColumnWidth } from 'app/constants/table'
7+
import { useI18n } from 'app/hooks/useI18n'
8+
9+
const columnHelper = createColumnHelper<AnnotationShape>()
10+
11+
export function useShapeTypeColumn(width: TableColumnWidth) {
12+
const { t } = useI18n()
13+
14+
return columnHelper.accessor('shapeType', {
15+
header: () => <CellHeader width={width}>{t('objectShapeType')}</CellHeader>,
16+
17+
cell: ({ row: { original: annotationShape } }) => (
18+
<TableCell
19+
renderLoadingSkeleton={() => (
20+
<Skeleton className="w-[200px]" variant="text" />
21+
)}
22+
width={width}
23+
>
24+
{annotationShape.shapeType ?? '--'}
25+
</TableCell>
26+
),
27+
})
28+
}

frontend/packages/data-portal/app/components/Dataset/utils.ts

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { UnfilteredRun } from 'app/types/gql/datasetPageTypes'
2+
import { getDataContents } from 'app/utils/deposition'
23

34
export interface SummaryData {
45
annotations: number
@@ -10,40 +11,14 @@ export interface SummaryData {
1011
}
1112

1213
export const getContentSummaryCounts = (runs: UnfilteredRun[]): SummaryData => {
13-
let annotations = 0
14-
let tomograms = 0
15-
let framesAvailable = false
16-
let tiltSeriesAvailable = false
17-
let ctfAvailable = false
18-
let alignmentAvailable = false
19-
20-
for (const run of runs) {
21-
annotations +=
22-
run.annotationsAggregate?.aggregate?.reduce(
23-
(sum, agg) => sum + (agg?.count || 0),
24-
0,
25-
) || 0
26-
tomograms +=
27-
run.tomogramsAggregate?.aggregate?.reduce(
28-
(sum, agg) => sum + (agg?.count || 0),
29-
0,
30-
) || 0
31-
framesAvailable ||=
32-
run.framesAggregate?.aggregate?.some((agg) => (agg?.count || 0) > 0) ||
33-
false
34-
tiltSeriesAvailable ||=
35-
run.tiltseriesAggregate?.aggregate?.some(
36-
(agg) => (agg?.count || 0) > 0,
37-
) || false
38-
ctfAvailable ||=
39-
run.perSectionParametersAggregate?.aggregate?.some(
40-
(agg) => (agg?.count || 0) > 0,
41-
) || false
42-
alignmentAvailable ||=
43-
run.alignmentsAggregate?.aggregate?.some(
44-
(agg) => (agg?.count || 0) > 0,
45-
) || false
46-
}
14+
const {
15+
annotations,
16+
tomograms,
17+
framesAvailable,
18+
tiltSeriesAvailable,
19+
ctfAvailable,
20+
alignmentAvailable,
21+
} = getDataContents(runs)
4722

4823
return {
4924
annotations,

0 commit comments

Comments
 (0)